cpufreq: rockchip: optimize rockchip_cpufreq_driver_init()
[firefly-linux-kernel-4.4.55.git] / drivers / cpufreq / rockchip-cpufreq.c
1 /*
2  * Rockchip CPUFreq Driver
3  *
4  * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
11  * kind, whether express or implied; without even the implied warranty
12  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  */
15
16 #include <linux/clk.h>
17 #include <linux/cpu.h>
18 #include <linux/err.h>
19 #include <linux/init.h>
20 #include <linux/kernel.h>
21 #include <linux/module.h>
22 #include <linux/nvmem-consumer.h>
23 #include <linux/of.h>
24 #include <linux/platform_device.h>
25 #include <linux/pm_opp.h>
26 #include <linux/slab.h>
27
28 #include "../clk/rockchip/clk.h"
29
30 #define MAX_PROP_NAME_LEN       3
31 #define LEAKAGE_TABLE_END       ~1
32 #define INVALID_VALUE           0xff
33
34 struct leakage_table {
35         int min;
36         int max;
37         int value;
38 };
39
40 struct cluster_info {
41         struct list_head list_head;
42         cpumask_t cpus;
43         int leakage;
44         int lkg_volt_sel;
45         int soc_version;
46         bool set_opp;
47 };
48
49 static LIST_HEAD(cluster_info_list);
50
51 static struct cluster_info *rockchip_cluster_info_lookup(int cpu)
52 {
53         struct cluster_info *cluster;
54
55         list_for_each_entry(cluster, &cluster_info_list, list_head) {
56                 if (cpumask_test_cpu(cpu, &cluster->cpus))
57                         return cluster;
58         }
59
60         return NULL;
61 }
62
63 static int rockchip_efuse_get_one_byte(struct device_node *np, char *porp_name,
64                                        int *value)
65 {
66         struct nvmem_cell *cell;
67         unsigned char *buf;
68         size_t len;
69
70         cell = of_nvmem_cell_get(np, porp_name);
71         if (IS_ERR(cell))
72                 return PTR_ERR(cell);
73
74         buf = (unsigned char *)nvmem_cell_read(cell, &len);
75
76         nvmem_cell_put(cell);
77
78         if (IS_ERR(buf))
79                 return PTR_ERR(buf);
80
81         if (buf[0] == INVALID_VALUE)
82                 return -EINVAL;
83
84         *value = buf[0];
85
86         kfree(buf);
87
88         return 0;
89 }
90
91 static int rk3399_get_soc_version(struct device_node *np, int *soc_version)
92 {
93         int ret, version;
94
95         if (of_property_match_string(np, "nvmem-cell-names",
96                                      "soc_version") < 0)
97                 return 0;
98
99         ret = rockchip_efuse_get_one_byte(np, "soc_version",
100                                           &version);
101         if (ret)
102                 return ret;
103
104         *soc_version = (version & 0xf0) >> 4;
105
106         return 0;
107 }
108
109 static const struct of_device_id rockchip_cpufreq_of_match[] = {
110         {
111                 .compatible = "rockchip,rk3399",
112                 .data = (void *)&rk3399_get_soc_version,
113         },
114         {},
115 };
116
117 static int rockchip_get_leakage_table(struct device_node *np, char *porp_name,
118                                       struct leakage_table **table)
119 {
120         struct leakage_table *lkg_table;
121         const struct property *prop;
122         int count, i;
123
124         prop = of_find_property(np, porp_name, NULL);
125         if (!prop)
126                 return -EINVAL;
127
128         if (!prop->value)
129                 return -ENODATA;
130
131         count = of_property_count_u32_elems(np, porp_name);
132         if (count < 0)
133                 return -EINVAL;
134
135         if (count % 3)
136                 return -EINVAL;
137
138         lkg_table = kzalloc(sizeof(*lkg_table) * (count / 3 + 1), GFP_KERNEL);
139         if (!lkg_table)
140                 return -ENOMEM;
141
142         for (i = 0; i < count / 3; i++) {
143                 of_property_read_u32_index(np, porp_name, 3 * i,
144                                            &lkg_table[i].min);
145                 of_property_read_u32_index(np, porp_name, 3 * i + 1,
146                                            &lkg_table[i].max);
147                 of_property_read_u32_index(np, porp_name, 3 * i + 2,
148                                            &lkg_table[i].value);
149         }
150         lkg_table[i].min = 0;
151         lkg_table[i].max = 0;
152         lkg_table[i].value = LEAKAGE_TABLE_END;
153
154         *table = lkg_table;
155
156         return 0;
157 }
158
159 static int rockchip_get_leakage_sel(struct device_node *np, char *name,
160                                     int leakage, int *value)
161 {
162         struct leakage_table *table;
163         struct property *prop;
164         int i, j = -1, ret;
165
166         if (of_property_match_string(np, "nvmem-cell-names", "cpu_leakage") < 0)
167                 return 0;
168
169         prop = of_find_property(np, name, NULL);
170         if (!prop)
171                 return 0;
172
173         ret = rockchip_get_leakage_table(np, name, &table);
174         if (ret)
175                 return -EINVAL;
176
177         for (i = 0; table[i].value != LEAKAGE_TABLE_END; i++) {
178                 if (leakage >= table[i].min)
179                         j = i;
180         }
181         if (j != -1)
182                 *value = table[j].value;
183         else
184                 ret = -EINVAL;
185
186         kfree(table);
187
188         return ret;
189 }
190
191 static int rockchip_cpufreq_of_parse_dt(int cpu, struct cluster_info *cluster)
192 {
193         int (*get_soc_version)(struct device_node *np, int *soc_version);
194         const struct of_device_id *match;
195         struct device_node *node, *np;
196         struct clk *clk;
197         struct device *dev;
198         int ret, lkg_scaling_sel = -1;
199
200         dev = get_cpu_device(cpu);
201         if (!dev)
202                 return -ENODEV;
203
204         ret = dev_pm_opp_of_get_sharing_cpus(dev, &cluster->cpus);
205         if (ret)
206                 return ret;
207
208         np = of_parse_phandle(dev->of_node, "operating-points-v2", 0);
209         if (!np) {
210                 dev_info(dev, "OPP-v2 not supported\n");
211                 return -EINVAL;
212         }
213
214         cluster->soc_version = -1;
215         node = of_find_node_by_path("/");
216         match = of_match_node(rockchip_cpufreq_of_match, node);
217         if (match && match->data) {
218                 get_soc_version = match->data;
219                 ret = get_soc_version(np, &cluster->soc_version);
220                 if (ret) {
221                         dev_err(dev, "Failed to get chip_version\n");
222                         return ret;
223                 }
224         }
225
226         ret = rockchip_efuse_get_one_byte(np, "cpu_leakage", &cluster->leakage);
227         if (ret)
228                 dev_err(dev, "Failed to get cpu_leakage\n");
229         else
230                 dev_info(dev, "leakage=%d\n", cluster->leakage);
231
232         cluster->lkg_volt_sel = -1;
233         ret = rockchip_get_leakage_sel(np, "leakage-voltage-sel",
234                                        cluster->leakage,
235                                        &cluster->lkg_volt_sel);
236         if (ret) {
237                 dev_err(dev, "Failed to get voltage-sel\n");
238                 return ret;
239         }
240
241         ret = rockchip_get_leakage_sel(np, "leakage-scaling-sel",
242                                        cluster->leakage,
243                                        &lkg_scaling_sel);
244         if (ret) {
245                 dev_err(dev, "Failed to get scaling-sel\n");
246                 return ret;
247         } else if (lkg_scaling_sel >= 0) {
248                 clk = of_clk_get_by_name(np, NULL);
249                 if (IS_ERR(clk)) {
250                         dev_err(dev, "Failed to get opp clk");
251                         return PTR_ERR(clk);
252                 }
253                 ret = rockchip_pll_clk_adaptive_scaling(clk, lkg_scaling_sel);
254                 if (ret) {
255                         dev_err(dev, "Failed to adaptive scaling\n");
256                         return ret;
257                 }
258         }
259
260         return 0;
261 }
262
263 static int rockchip_cpufreq_set_opp_info(int cpu, struct cluster_info *cluster)
264 {
265         struct device *dev;
266         char name[MAX_PROP_NAME_LEN];
267         int ret, version;
268
269         dev = get_cpu_device(cpu);
270         if (!dev)
271                 return -ENODEV;
272
273         if (cluster->soc_version != -1 && cluster->lkg_volt_sel != -1)
274                 snprintf(name, MAX_PROP_NAME_LEN, "S%d-L%d",
275                          cluster->soc_version,
276                          cluster->lkg_volt_sel);
277         else if (cluster->soc_version != -1 && cluster->lkg_volt_sel == -1)
278                 snprintf(name, MAX_PROP_NAME_LEN, "S%d", cluster->soc_version);
279         else if (cluster->soc_version == -1 && cluster->lkg_volt_sel != -1)
280                 snprintf(name, MAX_PROP_NAME_LEN, "L%d", cluster->lkg_volt_sel);
281         else
282                 return 0;
283
284         ret = dev_pm_opp_set_prop_name(dev, name);
285         if (ret) {
286                 dev_err(dev, "Failed to set prop name\n");
287                 return ret;
288         }
289
290         if (cluster->soc_version != -1) {
291                 version = BIT(cluster->soc_version);
292                 ret = dev_pm_opp_set_supported_hw(dev, &version, 1);
293                 if (ret) {
294                         dev_err(dev, "Failed to set supported hardware\n");
295                         return ret;
296                 }
297         }
298
299         return 0;
300 }
301
302 static int rockchip_hotcpu_notifier(struct notifier_block *nb,
303                                     unsigned long action, void *hcpu)
304 {
305         unsigned int cpu = (unsigned long)hcpu;
306         struct cluster_info *cluster;
307         cpumask_t cpus;
308         int number, ret;
309
310         cluster = rockchip_cluster_info_lookup(cpu);
311         if (!cluster)
312                 return NOTIFY_OK;
313
314         switch (action & ~CPU_TASKS_FROZEN) {
315         case CPU_ONLINE:
316                 if (cluster->set_opp) {
317                         ret = rockchip_cpufreq_set_opp_info(cpu, cluster);
318                         if (ret)
319                                 pr_err("Failed to set cpu%d opp_info\n", cpu);
320                         cluster->set_opp = false;
321                 }
322                 break;
323
324         case CPU_POST_DEAD:
325                 cpumask_and(&cpus, &cluster->cpus, cpu_online_mask);
326                 number = cpumask_weight(&cpus);
327                 if (!number)
328                         cluster->set_opp = true;
329                 break;
330         }
331
332         return NOTIFY_OK;
333 }
334
335 static struct notifier_block rockchip_hotcpu_nb = {
336         .notifier_call = rockchip_hotcpu_notifier,
337 };
338
339 static int __init rockchip_cpufreq_driver_init(void)
340 {
341         struct platform_device *pdev;
342         struct cluster_info *cluster, *pos;
343         int cpu, first_cpu, ret, i = 0;
344
345         for_each_possible_cpu(cpu) {
346                 cluster = rockchip_cluster_info_lookup(cpu);
347                 if (cluster)
348                         continue;
349
350                 cluster = kzalloc(sizeof(*cluster), GFP_KERNEL);
351                 if (!cluster)
352                         return -ENOMEM;
353
354                 ret = rockchip_cpufreq_of_parse_dt(cpu, cluster);
355                 if (ret) {
356                         if (ret != -ENOENT) {
357                                 pr_err("Failed to cpu%d parse_dt\n", cpu);
358                                 return ret;
359                         }
360
361                         /*
362                          * As the OPP document said, only one OPP binding
363                          * should be used per device.
364                          * And if there are multiple clusters on rockchip
365                          * platforms, we should use operating-points-v2.
366                          * So if don't support operating-points-v2, there must
367                          * be only one cluster, the list shuold be null.
368                          */
369                         list_for_each_entry(pos, &cluster_info_list, list_head)
370                                 i++;
371                         if (i)
372                                 return ret;
373                         /*
374                          * If don't support operating-points-v2, there is no
375                          * need to register notifiers.
376                          */
377                         goto next;
378                 }
379
380                 first_cpu = cpumask_first_and(&cluster->cpus, cpu_online_mask);
381                 ret = rockchip_cpufreq_set_opp_info(first_cpu, cluster);
382                 if (ret) {
383                         pr_err("Failed to set cpu%d opp_info\n", first_cpu);
384                         return ret;
385                 }
386
387                 list_add(&cluster->list_head, &cluster_info_list);
388         }
389
390         register_hotcpu_notifier(&rockchip_hotcpu_nb);
391
392 next:
393         pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
394
395         return PTR_ERR_OR_ZERO(pdev);
396 }
397 module_init(rockchip_cpufreq_driver_init);
398
399 MODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>");
400 MODULE_DESCRIPTION("Rockchip cpufreq driver");
401 MODULE_LICENSE("GPL v2");