|
26 | 26 |
|
27 | 27 | static DEFINE_IDR(icc_idr); |
28 | 28 | static LIST_HEAD(icc_providers); |
| 29 | +static int providers_count; |
| 30 | +static bool synced_state; |
29 | 31 | static DEFINE_MUTEX(icc_lock); |
30 | 32 | static struct dentry *icc_debugfs_dir; |
31 | 33 |
|
@@ -267,6 +269,12 @@ static int aggregate_requests(struct icc_node *node) |
267 | 269 | } |
268 | 270 | p->aggregate(node, r->tag, avg_bw, peak_bw, |
269 | 271 | &node->avg_bw, &node->peak_bw); |
| 272 | + |
| 273 | + /* during boot use the initial bandwidth as a floor value */ |
| 274 | + if (!synced_state) { |
| 275 | + node->avg_bw = max(node->avg_bw, node->init_avg); |
| 276 | + node->peak_bw = max(node->peak_bw, node->init_peak); |
| 277 | + } |
270 | 278 | } |
271 | 279 |
|
272 | 280 | return 0; |
@@ -958,6 +966,19 @@ void icc_node_add(struct icc_node *node, struct icc_provider *provider) |
958 | 966 | node->provider = provider; |
959 | 967 | list_add_tail(&node->node_list, &provider->nodes); |
960 | 968 |
|
| 969 | + /* get the initial bandwidth values and sync them with hardware */ |
| 970 | + if (provider->get_bw) { |
| 971 | + provider->get_bw(node, &node->init_avg, &node->init_peak); |
| 972 | + } else { |
| 973 | + node->init_avg = INT_MAX; |
| 974 | + node->init_peak = INT_MAX; |
| 975 | + } |
| 976 | + node->avg_bw = node->init_avg; |
| 977 | + node->peak_bw = node->init_peak; |
| 978 | + provider->set(node, node); |
| 979 | + node->avg_bw = 0; |
| 980 | + node->peak_bw = 0; |
| 981 | + |
961 | 982 | mutex_unlock(&icc_lock); |
962 | 983 | } |
963 | 984 | EXPORT_SYMBOL_GPL(icc_node_add); |
@@ -1053,8 +1074,54 @@ int icc_provider_del(struct icc_provider *provider) |
1053 | 1074 | } |
1054 | 1075 | EXPORT_SYMBOL_GPL(icc_provider_del); |
1055 | 1076 |
|
| 1077 | +static int of_count_icc_providers(struct device_node *np) |
| 1078 | +{ |
| 1079 | + struct device_node *child; |
| 1080 | + int count = 0; |
| 1081 | + |
| 1082 | + for_each_available_child_of_node(np, child) { |
| 1083 | + if (of_property_read_bool(child, "#interconnect-cells")) |
| 1084 | + count++; |
| 1085 | + count += of_count_icc_providers(child); |
| 1086 | + } |
| 1087 | + of_node_put(np); |
| 1088 | + |
| 1089 | + return count; |
| 1090 | +} |
| 1091 | + |
| 1092 | +void icc_sync_state(struct device *dev) |
| 1093 | +{ |
| 1094 | + struct icc_provider *p; |
| 1095 | + struct icc_node *n; |
| 1096 | + static int count; |
| 1097 | + |
| 1098 | + count++; |
| 1099 | + |
| 1100 | + if (count < providers_count) |
| 1101 | + return; |
| 1102 | + |
| 1103 | + mutex_lock(&icc_lock); |
| 1104 | + synced_state = true; |
| 1105 | + list_for_each_entry(p, &icc_providers, provider_list) { |
| 1106 | + dev_dbg(p->dev, "interconnect provider is in synced state\n"); |
| 1107 | + list_for_each_entry(n, &p->nodes, node_list) { |
| 1108 | + if (n->init_avg || n->init_peak) { |
| 1109 | + aggregate_requests(n); |
| 1110 | + p->set(n, n); |
| 1111 | + } |
| 1112 | + } |
| 1113 | + } |
| 1114 | + mutex_unlock(&icc_lock); |
| 1115 | +} |
| 1116 | +EXPORT_SYMBOL_GPL(icc_sync_state); |
| 1117 | + |
1056 | 1118 | static int __init icc_init(void) |
1057 | 1119 | { |
| 1120 | + struct device_node *root = of_find_node_by_path("/"); |
| 1121 | + |
| 1122 | + providers_count = of_count_icc_providers(root); |
| 1123 | + of_node_put(root); |
| 1124 | + |
1058 | 1125 | icc_debugfs_dir = debugfs_create_dir("interconnect", NULL); |
1059 | 1126 | debugfs_create_file("interconnect_summary", 0444, |
1060 | 1127 | icc_debugfs_dir, NULL, &icc_summary_fops); |
|
0 commit comments