Merge branch 'linux-linaro-lsk-v4.4-android' of git://git.linaro.org/kernel/linux...
[firefly-linux-kernel-4.4.55.git] / drivers / soc / rockchip / pm_test.c
1 /*
2  * Rockchip pm_test Driver
3  *
4  * Copyright (c) 2015 Rockchip Electronics Co. Ltd.
5  * Author: Finley Xiao <finley.xiao@rock-chips.com>
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of version 2 of the GNU General Public License as
9  * published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  */
16
17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18
19 #include <linux/clk.h>
20 #include <linux/clk-provider.h>
21 #include <linux/cpufreq.h>
22 #include <linux/fs.h>
23 #include <linux/module.h>
24 #include <linux/regulator/driver.h>
25 #include <linux/regulator/machine.h>
26 #include <linux/uaccess.h>
27 #include "../../../../drivers/regulator/internal.h"
28
29 #define CLK_NR_CLKS 550
30
31 static int cpu_usage_run;
32
33 static DEFINE_PER_CPU(struct work_struct, work_cpu_usage);
34 static DEFINE_PER_CPU(struct workqueue_struct *, workqueue_cpu_usage);
35
36 static const char pi_result[] = "3141592653589793238462643383279528841971693993751058209749445923078164062862089986280348253421170679821480865132823664709384469555822317253594081284811174502841270193852115559644622948954930381964428810975665933446128475648233786783165271201991456485669234634861045432664821339360726024914127372458706606315588174881520920962829254917153643678925903611330530548820466521384146951941511609433057273657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566438602139494639522473719070217986943702770539217176293176752384674818467669451320005681271452635608277857713427577896091736371787214684409012249534301465495853710579227968925892354201995611212902196864344181598136297747713099605187072113499999983729780499510597317328160963185";
37
38 struct pm_attribute {
39         struct attribute attr;
40         ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
41                         char *buf);
42         ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
43                          const char *buf, size_t n);
44 };
45
46 static ssize_t clk_rate_show(struct kobject *kobj, struct kobj_attribute *attr,
47                              char *buf)
48 {
49         char *str = buf;
50
51         str += sprintf(str, "get clk rate:\n");
52         str += sprintf(str,
53                 "         echo get [clk_name] > /sys/pm_tests/clk_rate\n");
54         str += sprintf(str, "set clk rate:\n");
55         str += sprintf(str,
56                 "         echo rawset [clk_name] [rate(Hz)] > /sys/pm_tests/clk_rate\n");
57         str += sprintf(str, "enable clk:\n");
58         str += sprintf(str,
59                 "         echo open [clk_name] > /sys/pm_tests/clk_rate\n");
60         str += sprintf(str, "disable clk:\n");
61         str += sprintf(str,
62                 "         echo close [clk_name] > /sys/pm_tests/clk_rate\n");
63         if (str != buf)
64                 *(str - 1) = '\n';
65
66         return (str - buf);
67 }
68
69 static struct clk *clk_get_by_name(const char *clk_name)
70 {
71         const char *name;
72         struct clk *clk;
73         struct device_node *np;
74         struct of_phandle_args clkspec;
75         int i;
76
77         np = of_find_node_by_name(NULL, "clock-controller");
78         clkspec.np = np;
79         clkspec.args_count = 1;
80         for (i = 1; i < CLK_NR_CLKS; i++) {
81                 clkspec.args[0] = i;
82                 clk = of_clk_get_from_provider(&clkspec);
83                 if (IS_ERR_OR_NULL(clk))
84                         continue;
85                 name = __clk_get_name(clk);
86                 if (strlen(name) != strlen(clk_name))
87                         continue;
88                 if (!strncmp(name, clk_name, strlen(clk_name)))
89                         break;
90         }
91
92         if (i == CLK_NR_CLKS)
93                 clk = NULL;
94
95         return clk;
96 }
97
98 static int clk_rate_get(const char *buf)
99 {
100         char cmd[20], clk_name[20];
101         unsigned long rate;
102         struct clk *clk;
103         int ret;
104
105         ret = sscanf(buf, "%s %s", cmd, clk_name);
106         if (ret != 2)
107                 return -EINVAL;
108
109         clk = clk_get_by_name(clk_name);
110         if (IS_ERR_OR_NULL(clk)) {
111                 pr_err("get clock error\n");
112                 return PTR_ERR(clk);
113         }
114
115         rate = clk_get_rate(clk);
116
117         pr_info("%s %lu Hz\n", clk_name, rate);
118
119         clk_put(clk);
120
121         return 0;
122 }
123
124 static int clk_rate_rawset(const char *buf)
125 {
126         char cmd[20], clk_name[20];
127         struct clk *clk;
128         unsigned long rate;
129         int ret;
130
131         ret = sscanf(buf, "%s %s %lu", cmd, clk_name, &rate);
132         if (ret != 3)
133                 return -EINVAL;
134
135         clk = clk_get_by_name(clk_name);
136         if (IS_ERR_OR_NULL(clk)) {
137                 pr_err("get %s error\n", clk_name);
138                 return PTR_ERR(clk);
139         }
140
141         ret = clk_set_rate(clk, rate);
142         if (ret) {
143                 pr_err("set %s rate %d error\n", clk_name, ret);
144                 return ret;
145         }
146
147         pr_debug("%s %s %lu\n", cmd, clk_name, rate);
148
149         clk_put(clk);
150
151         return ret;
152 }
153
154 static int clk_open(const char *buf)
155 {
156         char cmd[20], clk_name[20];
157         struct clk *clk;
158         int ret;
159
160         ret = sscanf(buf, "%s %s", cmd, clk_name);
161         if (ret != 2)
162                 return -EINVAL;
163
164         clk = clk_get_by_name(clk_name);
165         if (IS_ERR_OR_NULL(clk)) {
166                 pr_err("get clock %s error\n", clk_name);
167                 return PTR_ERR(clk);
168         }
169
170         clk_prepare_enable(clk);
171
172         pr_debug("%s %s\n", cmd, clk_name);
173
174         clk_put(clk);
175
176         return 0;
177 }
178
179 static int clk_close(const char *buf)
180 {
181         char cmd[20], clk_name[20];
182         struct clk *clk;
183         int ret;
184
185         ret = sscanf(buf, "%s %s", cmd, clk_name);
186         if (ret != 2)
187                 return -EINVAL;
188
189         clk = clk_get_by_name(clk_name);
190         if (IS_ERR_OR_NULL(clk)) {
191                 pr_err("get clock %s error\n", clk_name);
192                 return PTR_ERR(clk);
193         }
194
195         clk_disable_unprepare(clk);
196
197         pr_debug("%s %s\n", cmd, clk_name);
198
199         clk_put(clk);
200
201         return 0;
202 }
203
204 static ssize_t clk_rate_store(struct kobject *kobj, struct kobj_attribute *attr,
205                               const char *buf, size_t n)
206 {
207         char cmd[20];
208         int ret;
209
210         ret = sscanf(buf, "%s", cmd);
211         if (ret != 1)
212                 return -EINVAL;
213
214         if (!strncmp(cmd, "get", strlen("get"))) {
215                 ret = clk_rate_get(buf);
216                 if (ret)
217                         pr_err("get clk err\n");
218         } else if (!strncmp(cmd, "rawset", strlen("rawset"))) {
219                 ret = clk_rate_rawset(buf);
220                 if (ret)
221                         pr_err("rawset clk err\n");
222         } else if (!strncmp(cmd, "open", strlen("open"))) {
223                 ret = clk_open(buf);
224                 if (ret)
225                         pr_err("open clk err\n");
226         } else if (!strncmp(cmd, "close", strlen("close"))) {
227                 ret = clk_close(buf);
228                 if (ret)
229                         pr_err("close clk err\n");
230         } else {
231                 pr_info("unsupported cmd(%s)\n", cmd);
232         }
233
234         return n;
235 }
236
237 static ssize_t clk_volt_show(struct kobject *kobj, struct kobj_attribute *attr,
238                              char *buf)
239 {
240         char *str = buf;
241
242         str += sprintf(str, "get voltage:\n");
243         str += sprintf(str,
244                 "            echo get [regulaotr_name] > /sys/pm_tests/clk_volt\n");
245         str += sprintf(str, "set voltage:\n");
246         str += sprintf(str,
247                 "            echo set [regulaotr_name] [voltage(uV)] > /sys/pm_tests/clk_volt\n");
248         if (str != buf)
249                 *(str - 1) = '\n';
250
251         return (str - buf);
252 }
253
254 static int clk_volt_get(const char *buf)
255 {
256         char cmd[20], reg_name[20];
257         unsigned int ret;
258         struct regulator *regulator;
259
260         ret = sscanf(buf, "%s %s", cmd, reg_name);
261         if (ret != 2)
262                 return -EINVAL;
263
264         regulator = regulator_get(NULL, reg_name);
265         if (IS_ERR_OR_NULL(regulator)) {
266                 pr_err("get regulator %s error\n", reg_name);
267                 return PTR_ERR(regulator);
268         }
269
270         ret = regulator_get_voltage(regulator);
271
272         pr_info("%s %duV\n", reg_name, ret);
273
274         regulator_put(regulator);
275
276         return 0;
277 }
278
279 static void break_up_regulator_limit(struct regulator_dev *rdev, int min_uV,
280                                      int max_uV)
281 {
282         struct regulator *regulator;
283
284         list_for_each_entry(regulator, &rdev->consumer_list, list) {
285                 if (!regulator->min_uV && !regulator->max_uV)
286                         continue;
287                 pr_debug("%s min=%d, max=%d", dev_name(regulator->dev),
288                          regulator->min_uV, regulator->max_uV);
289                 regulator->min_uV = min_uV;
290                 regulator->max_uV = max_uV;
291         }
292 }
293
294 static int clk_volt_set(const char *buf)
295 {
296         char cmd[20], reg_name[20];
297         unsigned int ret;
298         struct regulator *regulator;
299         unsigned int volt;
300         struct regulator_dev *rdev;
301         int max_uV;
302
303         ret = sscanf(buf, "%s %s %u", cmd, reg_name, &volt);
304         if (ret != 3)
305                 return -EINVAL;
306
307         regulator = regulator_get(NULL, reg_name);
308         if (IS_ERR_OR_NULL(regulator)) {
309                 pr_info("get regulator %s error\n", reg_name);
310                 return PTR_ERR(regulator);
311         }
312
313         rdev = regulator->rdev;
314         max_uV = rdev->constraints->max_uV;
315
316         if (volt > max_uV) {
317                 pr_err("invalid volt, max %d uV\n", max_uV);
318                 return -EINVAL;
319         }
320
321         mutex_lock(&rdev->mutex);
322         break_up_regulator_limit(rdev, volt, max_uV);
323         mutex_unlock(&rdev->mutex);
324
325         ret = regulator_set_voltage(regulator, volt, max_uV);
326         if (ret) {
327                 pr_err("set voltage %d error\n", ret);
328                 regulator_put(regulator);
329                 return ret;
330         }
331
332         pr_debug("set %s %duV\n", reg_name, volt);
333
334         regulator_put(regulator);
335
336         return 0;
337 }
338
339 static ssize_t clk_volt_store(struct kobject *kobj, struct kobj_attribute *attr,
340                               const char *buf, size_t n)
341 {
342         unsigned int ret;
343         char cmd[20];
344
345         ret = sscanf(buf, "%s", cmd);
346         if (ret != 1)
347                 return -EINVAL;
348
349         if (!strncmp(cmd, "get", strlen("get"))) {
350                 ret = clk_volt_get(buf);
351                 if (ret)
352                         pr_err("get volt err\n");
353         } else if (!strncmp(cmd, "set", strlen("set"))) {
354                 ret = clk_volt_set(buf);
355                 if (ret)
356                         pr_err("set volt err\n");
357         } else {
358                 pr_err("unsupported cmd(%s)\n", cmd);
359         }
360
361         return n;
362 }
363
364 static ssize_t cpu_usage_show(struct kobject *kobj, struct kobj_attribute *attr,
365                               char *buf)
366 {
367         char *str = buf;
368
369         str += sprintf(str,
370                 "while(1) test:\n");
371         str += sprintf(str,
372                 "              cpu 0-3:  echo start 0 0 > /sys/pm_tests/cpu_usage\n");
373         str += sprintf(str,
374                 "              cpu 4-7:  echo start 0 1 > /sys/pm_tests/cpu_usage\n");
375         str += sprintf(str,
376                 "              cpu 0-7:  echo start 0 2 > /sys/pm_tests/cpu_usage\n");
377         str += sprintf(str,
378                 "calc_pi  test:\n");
379         str += sprintf(str,
380                 "              cpu 0-3:  echo start 1 0 > /sys/pm_tests/cpu_usage\n");
381         str += sprintf(str,
382                 "              cpu 4-7:  echo start 1 1 > /sys/pm_tests/cpu_usage\n");
383         str += sprintf(str,
384                 "              cpu 0-7:  echo start 1 2 > /sys/pm_tests/cpu_usage\n");
385         str += sprintf(str,
386                 "stop     test:\n");
387         str += sprintf(str,
388                 "              cpu 0-7:  echo stop -1 -1 > /sys/pm_tests/cpu_usage\n");
389         if (str != buf)
390                 *(str - 1) = '\n';
391
392         return (str - buf);
393 }
394
395 static inline int calc_pi(void)
396 {
397         int bit = 0, i = 0;
398         long a = 10000, b = 0, c = 2800, d = 0, e = 0, g = 0;
399         int *result;
400         long *f;
401         int len = 0;
402         char *pi_calc, *pi_temp;
403         char *pi_just = (char *)&pi_result[0];
404         size_t pi_just_size = sizeof(pi_result);
405
406         result = vmalloc(10000 * sizeof(int));
407         if (!result)
408                 return -ENOMEM;
409
410          f = vmalloc(2801 * sizeof(long));
411         if (!f)
412                 return -ENOMEM;
413
414          pi_calc = vmalloc(1000 * sizeof(char));
415         if (!pi_calc)
416                 return -ENOMEM;
417
418         for (; b - c; )
419                 f[b++] = a / 5;
420         for (; d = 0, g = c * 2; c -= 14, result[bit++] = e + d / a, e = d % a)
421                 for (b = c; d += f[b] * a, f[b] = d % --g, d /= g--, --b;
422                      d *= b)
423                         ;
424
425         pi_temp = pi_calc;
426         for (i = 0; i < bit; i++)
427                 len += sprintf(pi_temp + len, "%d", result[i]);
428
429         if (strncmp(pi_just, pi_calc, pi_just_size) == 0) {
430                 vfree(result);
431                 vfree(f);
432                 vfree(pi_calc);
433         } else {
434                 vfree(result);
435                 vfree(f);
436                 vfree(pi_calc);
437
438                 while (1)
439                         pr_err("calc_pi error\n");
440         }
441
442         return 0;
443 }
444
445 static void calc_pi2(void)
446 {
447         calc_pi();
448         calc_pi();
449 }
450
451 static void calc_pi4(void)
452 {
453         calc_pi2();
454         calc_pi2();
455 }
456
457 static void calc_pi8(void)
458 {
459         calc_pi4();
460         calc_pi4();
461 }
462
463 static void calc_pi16(void)
464 {
465         calc_pi8();
466         calc_pi8();
467 }
468
469 /* 0xffffffc000097468 - 0xffffffc0000949c4 =  10916 byte*/
470 static void calc_pi32(void)
471 {
472         calc_pi16();
473         calc_pi16();
474 }
475
476 /* 0xffffffc000099ee8 - 0xffffffc0000949c4 =  21796 byte*/
477 static void calc_pi64(void)
478 {
479         calc_pi32();
480         calc_pi32();
481 }
482
483 /* 0xffffffc00009f3e8 - 0xffffffc0000949c4 =  43556 byte*/
484 static void calc_pi128(void)
485 {
486         calc_pi64();
487         calc_pi64();
488 }
489
490 /* 0xffffffc0000a9de8 - 0xffffffc0000949c4 =  87076 byte*/
491 static void calc_pi256(void)
492 {
493         calc_pi128();
494         calc_pi128();
495 }
496
497 /* 0xffffffc0000bf1e8 - 0xffffffc0000949c4 =  174116 byte*/
498 static void calc_pi512(void)
499 {
500         calc_pi256();
501         calc_pi256();
502 }
503
504 /* 0xffffffc0000ea9e8 - 0xffffffc0000949c4 =  352295 byte*/
505 static void calc_pi1024(void)
506 {
507         calc_pi512();
508         calc_pi512();
509 }
510
511 static void calc_pi_large(void)
512 {
513         calc_pi1024();
514 }
515
516 static void handler_cpu_usage(struct work_struct *work)
517 {
518         while (cpu_usage_run == 0)
519                 barrier();
520
521         while (cpu_usage_run == 1)
522                 calc_pi_large();
523 }
524
525 static ssize_t cpu_usage_store(struct kobject *kobj,
526                                struct kobj_attribute *attr,
527                                const char *buf, size_t n)
528 {
529         struct workqueue_struct *workqueue;
530         struct work_struct *work;
531         char cmd[20];
532         int cpu;
533         int ret;
534         int usage = 0;
535         int cluster = 0;
536
537         ret = sscanf(buf, "%s %d %d", cmd, &usage, &cluster);
538         if (ret != 3)
539                 return -EINVAL;
540
541         if (!strncmp(cmd, "start", strlen("start"))) {
542                 if (usage == 0)
543                         pr_info("start while(1) test\n");
544                 else if (usage == 1)
545                         pr_info("start pi test\n");
546                 else
547                         return 0;
548
549                 cpu_usage_run = usage;
550                 for_each_online_cpu(cpu) {
551                         work = &per_cpu(work_cpu_usage, cpu);
552                         workqueue = per_cpu(workqueue_cpu_usage, cpu);
553                         if (!work || !workqueue) {
554                                 pr_err("work or workqueue NULL\n");
555                                 return n;
556                         }
557
558                         if (cluster == 0 && cpu < 4)
559                                 queue_work_on(cpu, workqueue, work);
560                         else if (cluster == 1 && cpu >= 4)
561                                 queue_work_on(cpu, workqueue, work);
562                         else if (cluster == 2)
563                                 queue_work_on(cpu, workqueue, work);
564                 }
565         } else if (!strncmp(cmd, "stop", strlen("stop"))) {
566                 if (cpu_usage_run == 0)
567                         pr_info("stop while(1) test\n");
568                 else if (cpu_usage_run == 1)
569                         pr_info("stop pi test\n");
570                 cpu_usage_run = usage;
571         }
572
573         return n;
574 }
575
576 static struct pm_attribute pm_attrs[] = {
577         __ATTR(clk_rate, 0644, clk_rate_show, clk_rate_store),
578         __ATTR(clk_volt, 0644, clk_volt_show, clk_volt_store),
579         __ATTR(cpu_usage, 0644, cpu_usage_show, cpu_usage_store),
580 };
581
582 static int __init pm_test_init(void)
583 {
584         struct kobject *kobject;
585         struct workqueue_struct *workqueue;
586         struct work_struct *work;
587         int i, cpu, ret = 0;
588
589         kobject = kobject_create_and_add("pm_tests", NULL);
590         if (!kobject)
591                 return -ENOMEM;
592
593         for (i = 0; i < ARRAY_SIZE(pm_attrs); i++) {
594                 ret = sysfs_create_file(kobject, &pm_attrs[i].attr);
595                 if (ret) {
596                         pr_err("create file index %d error\n", i);
597                         return ret;
598                 }
599         }
600
601         workqueue = create_workqueue("workqueue_cpu_usage");
602         if (!workqueue) {
603                 pr_err("workqueue NULL\n");
604                 return -EINVAL;
605         }
606
607         for_each_online_cpu(cpu) {
608                 work = &per_cpu(work_cpu_usage, cpu);
609                 if (!work) {
610                         pr_err("work NULL\n");
611                         return -EINVAL;
612                 }
613                 INIT_WORK(work, handler_cpu_usage);
614                 per_cpu(workqueue_cpu_usage, cpu) = workqueue;
615         }
616
617         return ret;
618 }
619
620 late_initcall(pm_test_init);
621
622 MODULE_DESCRIPTION("Rockchip pm_test driver");
623 MODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>");
624 MODULE_LICENSE("GPL v2");