blob: ba94dcf091695eebb61b681f43eef75c924ddf1b [file] [log] [blame]
James Carrad3c02f2016-07-29 19:02:16 -07001/* drivers/misc/memory_state_time.c
2 *
3 * Copyright (C) 2016 Google, Inc.
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15
16#include <linux/device.h>
17#include <linux/err.h>
18#include <linux/errno.h>
19#include <linux/hashtable.h>
20#include <linux/kconfig.h>
21#include <linux/kernel.h>
22#include <linux/kobject.h>
23#include <linux/memory-state-time.h>
24#include <linux/module.h>
25#include <linux/mutex.h>
26#include <linux/of_platform.h>
27#include <linux/slab.h>
28#include <linux/sysfs.h>
29#include <linux/time.h>
30#include <linux/timekeeping.h>
31#include <linux/workqueue.h>
32
33#define KERNEL_ATTR_RO(_name) \
34static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
35
36#define KERNEL_ATTR_RW(_name) \
37static struct kobj_attribute _name##_attr = \
38 __ATTR(_name, 0644, _name##_show, _name##_store)
39
40#define FREQ_HASH_BITS 4
41DECLARE_HASHTABLE(freq_hash_table, FREQ_HASH_BITS);
42
43static DEFINE_MUTEX(mem_lock);
44
45#define TAG "memory_state_time"
46#define BW_NODE "/soc/memory-state-time"
47#define FREQ_TBL "freq-tbl"
48#define BW_TBL "bw-buckets"
49#define NUM_SOURCES "num-sources"
50
51#define LOWEST_FREQ 2
52
53static int curr_bw;
54static int curr_freq;
55static u32 *bw_buckets;
56static u32 *freq_buckets;
57static int num_freqs;
58static int num_buckets;
59static int registered_bw_sources;
60static u64 last_update;
61static bool init_success;
62static struct workqueue_struct *memory_wq;
63static u32 num_sources = 10;
64static int *bandwidths;
65
66struct freq_entry {
67 int freq;
68 u64 *buckets; /* Bandwidth buckets. */
69 struct hlist_node hash;
70};
71
72struct queue_container {
73 struct work_struct update_state;
74 int value;
75 u64 time_now;
76 int id;
77 struct mutex *lock;
78};
79
80static int find_bucket(int bw)
81{
82 int i;
83
84 if (bw_buckets != NULL) {
85 for (i = 0; i < num_buckets; i++) {
86 if (bw_buckets[i] > bw) {
87 pr_debug("Found bucket %d for bandwidth %d\n",
88 i, bw);
89 return i;
90 }
91 }
92 return num_buckets - 1;
93 }
94 return 0;
95}
96
97static u64 get_time_diff(u64 time_now)
98{
99 u64 ms;
100
101 ms = time_now - last_update;
102 last_update = time_now;
103 return ms;
104}
105
106static ssize_t show_stat_show(struct kobject *kobj,
107 struct kobj_attribute *attr, char *buf)
108{
109 int i, j;
110 int len = 0;
111 struct freq_entry *freq_entry;
112
113 for (i = 0; i < num_freqs; i++) {
114 hash_for_each_possible(freq_hash_table, freq_entry, hash,
115 freq_buckets[i]) {
116 if (freq_entry->freq == freq_buckets[i]) {
117 len += scnprintf(buf + len, PAGE_SIZE - len,
118 "%d ", freq_buckets[i]);
119 if (len >= PAGE_SIZE)
120 break;
121 for (j = 0; j < num_buckets; j++) {
122 len += scnprintf(buf + len,
123 PAGE_SIZE - len,
124 "%llu ",
125 freq_entry->buckets[j]);
126 }
127 len += scnprintf(buf + len, PAGE_SIZE - len,
128 "\n");
129 }
130 }
131 }
132 pr_debug("Current Time: %llu\n", ktime_get_boot_ns());
133 return len;
134}
135KERNEL_ATTR_RO(show_stat);
136
137static void update_table(u64 time_now)
138{
139 struct freq_entry *freq_entry;
140
141 pr_debug("Last known bw %d freq %d\n", curr_bw, curr_freq);
142 hash_for_each_possible(freq_hash_table, freq_entry, hash, curr_freq) {
143 if (curr_freq == freq_entry->freq) {
144 freq_entry->buckets[find_bucket(curr_bw)]
145 += get_time_diff(time_now);
146 break;
147 }
148 }
149}
150
151static bool freq_exists(int freq)
152{
153 int i;
154
155 for (i = 0; i < num_freqs; i++) {
156 if (freq == freq_buckets[i])
157 return true;
158 }
159 return false;
160}
161
162static int calculate_total_bw(int bw, int index)
163{
164 int i;
165 int total_bw = 0;
166
167 pr_debug("memory_state_time New bw %d for id %d\n", bw, index);
168 bandwidths[index] = bw;
169 for (i = 0; i < registered_bw_sources; i++)
170 total_bw += bandwidths[i];
171 return total_bw;
172}
173
174static void freq_update_do_work(struct work_struct *work)
175{
176 struct queue_container *freq_state_update
177 = container_of(work, struct queue_container,
178 update_state);
179 if (freq_state_update) {
180 mutex_lock(&mem_lock);
181 update_table(freq_state_update->time_now);
182 curr_freq = freq_state_update->value;
183 mutex_unlock(&mem_lock);
184 kfree(freq_state_update);
185 }
186}
187
188static void bw_update_do_work(struct work_struct *work)
189{
190 struct queue_container *bw_state_update
191 = container_of(work, struct queue_container,
192 update_state);
193 if (bw_state_update) {
194 mutex_lock(&mem_lock);
195 update_table(bw_state_update->time_now);
196 curr_bw = calculate_total_bw(bw_state_update->value,
197 bw_state_update->id);
198 mutex_unlock(&mem_lock);
199 kfree(bw_state_update);
200 }
201}
202
203static void memory_state_freq_update(struct memory_state_update_block *ub,
204 int value)
205{
206 if (IS_ENABLED(CONFIG_MEMORY_STATE_TIME)) {
207 if (freq_exists(value) && init_success) {
208 struct queue_container *freq_container
209 = kmalloc(sizeof(struct queue_container),
210 GFP_KERNEL);
211 if (!freq_container)
212 return;
213 INIT_WORK(&freq_container->update_state,
214 freq_update_do_work);
215 freq_container->time_now = ktime_get_boot_ns();
216 freq_container->value = value;
217 pr_debug("Scheduling freq update in work queue\n");
218 queue_work(memory_wq, &freq_container->update_state);
219 } else {
220 pr_debug("Freq does not exist.\n");
221 }
222 }
223}
224
225static void memory_state_bw_update(struct memory_state_update_block *ub,
226 int value)
227{
228 if (IS_ENABLED(CONFIG_MEMORY_STATE_TIME)) {
229 if (init_success) {
230 struct queue_container *bw_container
231 = kmalloc(sizeof(struct queue_container),
232 GFP_KERNEL);
233 if (!bw_container)
234 return;
235 INIT_WORK(&bw_container->update_state,
236 bw_update_do_work);
237 bw_container->time_now = ktime_get_boot_ns();
238 bw_container->value = value;
239 bw_container->id = ub->id;
240 pr_debug("Scheduling bandwidth update in work queue\n");
241 queue_work(memory_wq, &bw_container->update_state);
242 }
243 }
244}
245
246struct memory_state_update_block *memory_state_register_frequency_source(void)
247{
248 struct memory_state_update_block *block;
249
250 if (IS_ENABLED(CONFIG_MEMORY_STATE_TIME)) {
251 pr_debug("Allocating frequency source\n");
252 block = kmalloc(sizeof(struct memory_state_update_block),
253 GFP_KERNEL);
254 if (!block)
255 return NULL;
256 block->update_call = memory_state_freq_update;
257 return block;
258 }
259 pr_err("Config option disabled.\n");
260 return NULL;
261}
262EXPORT_SYMBOL_GPL(memory_state_register_frequency_source);
263
264struct memory_state_update_block *memory_state_register_bandwidth_source(void)
265{
266 struct memory_state_update_block *block;
267
268 if (IS_ENABLED(CONFIG_MEMORY_STATE_TIME)) {
269 pr_debug("Allocating bandwidth source %d\n",
270 registered_bw_sources);
271 block = kmalloc(sizeof(struct memory_state_update_block),
272 GFP_KERNEL);
273 if (!block)
274 return NULL;
275 block->update_call = memory_state_bw_update;
276 if (registered_bw_sources < num_sources) {
277 block->id = registered_bw_sources++;
278 } else {
279 pr_err("Unable to allocate source; max number reached\n");
280 kfree(block);
281 return NULL;
282 }
283 return block;
284 }
285 pr_err("Config option disabled.\n");
286 return NULL;
287}
288EXPORT_SYMBOL_GPL(memory_state_register_bandwidth_source);
289
290/* Buckets are designated by their maximum.
291 * Returns the buckets decided by the capability of the device.
292 */
293static int get_bw_buckets(struct device *dev)
294{
295 int ret, lenb;
296 struct device_node *node = dev->of_node;
297
298 of_property_read_u32(node, NUM_SOURCES, &num_sources);
Arnd Bergmann34827042017-05-10 11:21:27 +0200299 if (!of_find_property(node, BW_TBL, &lenb)) {
300 pr_err("Missing %s property\n", BW_TBL);
301 return -ENODATA;
James Carrad3c02f2016-07-29 19:02:16 -0700302 }
Arnd Bergmann34827042017-05-10 11:21:27 +0200303
304 bandwidths = devm_kzalloc(dev,
305 sizeof(*bandwidths) * num_sources, GFP_KERNEL);
306 if (!bandwidths)
307 return -ENOMEM;
308 lenb /= sizeof(*bw_buckets);
309 bw_buckets = devm_kzalloc(dev, lenb * sizeof(*bw_buckets),
310 GFP_KERNEL);
311 if (!bw_buckets) {
312 devm_kfree(dev, bandwidths);
313 return -ENOMEM;
314 }
315 ret = of_property_read_u32_array(node, BW_TBL, bw_buckets,
316 lenb);
317 if (ret < 0) {
318 devm_kfree(dev, bandwidths);
319 devm_kfree(dev, bw_buckets);
320 pr_err("Unable to read bandwidth table from device tree.\n");
321 return ret;
322 }
323
James Carrad3c02f2016-07-29 19:02:16 -0700324 curr_bw = 0;
325 num_buckets = lenb;
326 return 0;
327}
328
329/* Adds struct freq_entry nodes to the hashtable for each compatible frequency.
330 * Returns the supported number of frequencies.
331 */
332static int freq_buckets_init(struct device *dev)
333{
334 struct freq_entry *freq_entry;
335 int i;
336 int ret, lenf;
337 struct device_node *node = dev->of_node;
338
Arnd Bergmann34827042017-05-10 11:21:27 +0200339 if (!of_find_property(node, FREQ_TBL, &lenf)) {
340 pr_err("Missing %s property\n", FREQ_TBL);
341 return -ENODATA;
James Carrad3c02f2016-07-29 19:02:16 -0700342 }
Arnd Bergmann34827042017-05-10 11:21:27 +0200343
344 lenf /= sizeof(*freq_buckets);
345 freq_buckets = devm_kzalloc(dev, lenf * sizeof(*freq_buckets),
346 GFP_KERNEL);
347 if (!freq_buckets)
348 return -ENOMEM;
349 pr_debug("freqs found len %d\n", lenf);
350 ret = of_property_read_u32_array(node, FREQ_TBL, freq_buckets,
351 lenf);
352 if (ret < 0) {
353 devm_kfree(dev, freq_buckets);
354 pr_err("Unable to read frequency table from device tree.\n");
355 return ret;
356 }
357 pr_debug("ret freq %d\n", ret);
358
James Carrad3c02f2016-07-29 19:02:16 -0700359 num_freqs = lenf;
360 curr_freq = freq_buckets[LOWEST_FREQ];
361
362 for (i = 0; i < num_freqs; i++) {
363 freq_entry = devm_kzalloc(dev, sizeof(struct freq_entry),
364 GFP_KERNEL);
365 if (!freq_entry)
366 return -ENOMEM;
367 freq_entry->buckets = devm_kzalloc(dev, sizeof(u64)*num_buckets,
368 GFP_KERNEL);
369 if (!freq_entry->buckets) {
370 devm_kfree(dev, freq_entry);
371 return -ENOMEM;
372 }
373 pr_debug("memory_state_time Adding freq to ht %d\n",
374 freq_buckets[i]);
375 freq_entry->freq = freq_buckets[i];
376 hash_add(freq_hash_table, &freq_entry->hash, freq_buckets[i]);
377 }
378 return 0;
379}
380
381struct kobject *memory_kobj;
382EXPORT_SYMBOL_GPL(memory_kobj);
383
384static struct attribute *memory_attrs[] = {
385 &show_stat_attr.attr,
386 NULL
387};
388
389static struct attribute_group memory_attr_group = {
390 .attrs = memory_attrs,
391};
392
393static int memory_state_time_probe(struct platform_device *pdev)
394{
395 int error;
396
397 error = get_bw_buckets(&pdev->dev);
398 if (error)
399 return error;
400 error = freq_buckets_init(&pdev->dev);
401 if (error)
402 return error;
403 last_update = ktime_get_boot_ns();
404 init_success = true;
405
406 pr_debug("memory_state_time initialized with num_freqs %d\n",
407 num_freqs);
408 return 0;
409}
410
411static const struct of_device_id match_table[] = {
412 { .compatible = "memory-state-time" },
413 {}
414};
415
416static struct platform_driver memory_state_time_driver = {
417 .probe = memory_state_time_probe,
418 .driver = {
419 .name = "memory-state-time",
420 .of_match_table = match_table,
421 .owner = THIS_MODULE,
422 },
423};
424
425static int __init memory_state_time_init(void)
426{
427 int error;
428
429 hash_init(freq_hash_table);
430 memory_wq = create_singlethread_workqueue("memory_wq");
431 if (!memory_wq) {
432 pr_err("Unable to create workqueue.\n");
433 return -EINVAL;
434 }
435 /*
436 * Create sys/kernel directory for memory_state_time.
437 */
438 memory_kobj = kobject_create_and_add(TAG, kernel_kobj);
439 if (!memory_kobj) {
440 pr_err("Unable to allocate memory_kobj for sysfs directory.\n");
441 error = -ENOMEM;
442 goto wq;
443 }
444 error = sysfs_create_group(memory_kobj, &memory_attr_group);
445 if (error) {
446 pr_err("Unable to create sysfs folder.\n");
447 goto kobj;
448 }
449
450 error = platform_driver_register(&memory_state_time_driver);
451 if (error) {
452 pr_err("Unable to register memory_state_time platform driver.\n");
453 goto group;
454 }
455 return 0;
456
457group: sysfs_remove_group(memory_kobj, &memory_attr_group);
458kobj: kobject_put(memory_kobj);
459wq: destroy_workqueue(memory_wq);
460 return error;
461}
462module_init(memory_state_time_init);