|
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; |
@@ -931,6 +939,19 @@ void icc_node_add(struct icc_node *node, struct icc_provider *provider) |
931 | 939 | node->provider = provider; |
932 | 940 | list_add_tail(&node->node_list, &provider->nodes); |
933 | 941 |
|
| 942 | + /* get the initial bandwidth values and sync them with hardware */ |
| 943 | + if (provider->get_bw) { |
| 944 | + provider->get_bw(node, &node->init_avg, &node->init_peak); |
| 945 | + } else { |
| 946 | + node->init_avg = INT_MAX; |
| 947 | + node->init_peak = INT_MAX; |
| 948 | + } |
| 949 | + node->avg_bw = node->init_avg; |
| 950 | + node->peak_bw = node->init_peak; |
| 951 | + provider->set(node, node); |
| 952 | + node->avg_bw = 0; |
| 953 | + node->peak_bw = 0; |
| 954 | + |
934 | 955 | mutex_unlock(&icc_lock); |
935 | 956 | } |
936 | 957 | EXPORT_SYMBOL_GPL(icc_node_add); |
@@ -1026,8 +1047,54 @@ int icc_provider_del(struct icc_provider *provider) |
1026 | 1047 | } |
1027 | 1048 | EXPORT_SYMBOL_GPL(icc_provider_del); |
1028 | 1049 |
|
| 1050 | +static int of_count_icc_providers(struct device_node *np) |
| 1051 | +{ |
| 1052 | + struct device_node *child; |
| 1053 | + int count = 0; |
| 1054 | + |
| 1055 | + for_each_available_child_of_node(np, child) { |
| 1056 | + if (of_property_read_bool(child, "#interconnect-cells")) |
| 1057 | + count++; |
| 1058 | + count += of_count_icc_providers(child); |
| 1059 | + } |
| 1060 | + of_node_put(np); |
| 1061 | + |
| 1062 | + return count; |
| 1063 | +} |
| 1064 | + |
| 1065 | +void icc_sync_state(struct device *dev) |
| 1066 | +{ |
| 1067 | + struct icc_provider *p; |
| 1068 | + struct icc_node *n; |
| 1069 | + static int count; |
| 1070 | + |
| 1071 | + count++; |
| 1072 | + |
| 1073 | + if (count < providers_count) |
| 1074 | + return; |
| 1075 | + |
| 1076 | + mutex_lock(&icc_lock); |
| 1077 | + synced_state = true; |
| 1078 | + list_for_each_entry(p, &icc_providers, provider_list) { |
| 1079 | + dev_dbg(p->dev, "interconnect provider is in synced state\n"); |
| 1080 | + list_for_each_entry(n, &p->nodes, node_list) { |
| 1081 | + if (n->init_avg || n->init_peak) { |
| 1082 | + aggregate_requests(n); |
| 1083 | + p->set(n, n); |
| 1084 | + } |
| 1085 | + } |
| 1086 | + } |
| 1087 | + mutex_unlock(&icc_lock); |
| 1088 | +} |
| 1089 | +EXPORT_SYMBOL_GPL(icc_sync_state); |
| 1090 | + |
1029 | 1091 | static int __init icc_init(void) |
1030 | 1092 | { |
| 1093 | + struct device_node *root = of_find_node_by_path("/"); |
| 1094 | + |
| 1095 | + providers_count = of_count_icc_providers(root); |
| 1096 | + of_node_put(root); |
| 1097 | + |
1031 | 1098 | icc_debugfs_dir = debugfs_create_dir("interconnect", NULL); |
1032 | 1099 | debugfs_create_file("interconnect_summary", 0444, |
1033 | 1100 | icc_debugfs_dir, NULL, &icc_summary_fops); |
|
0 commit comments