soc: rockchip: add cpuinfo support
[firefly-linux-kernel-4.4.55.git] / drivers / soc / rockchip / rockchip_pvtm.c
1 /*
2  * Rockchip PVTM support.
3  *
4  * Copyright (c) 2016 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 #include <linux/clk.h>
18 #include <linux/delay.h>
19 #include <linux/device.h>
20 #include <linux/debugfs.h>
21 #include <linux/io.h>
22 #include <linux/mfd/syscon.h>
23 #include <linux/module.h>
24 #include <linux/of.h>
25 #include <linux/of_platform.h>
26 #include <linux/platform_device.h>
27 #include <linux/regmap.h>
28 #include <linux/slab.h>
29 #include <linux/soc/rockchip/pvtm.h>
30
31 #define RK3366_PVTM_CORE        0
32 #define RK3366_PVTM_GPU         1
33 #define RK3366_PVTM_PMU         2
34
35 #define RK3399_PVTM_CORE_L      0
36 #define RK3399_PVTM_CORE_B      1
37 #define RK3399_PVTM_DDR         2
38 #define RK3399_PVTM_GPU         3
39 #define RK3399_PVTM_PMU         4
40
41 #define wr_mask_bit(v, off, mask)       ((v) << (off) | (mask) << (16 + off))
42
43 #define PVTM(_ch, _name, _num_sub, _start, _en, _cal, _done, _freq)     \
44 {                                       \
45         .ch = _ch,                      \
46         .clk_name = _name,              \
47         .num_sub = _num_sub,            \
48         .bit_start = _start,            \
49         .bit_en = _en,                  \
50         .reg_cal = _cal,                \
51         .bit_freq_done = _done,         \
52         .reg_freq = _freq,              \
53 }
54
55 struct rockchip_pvtm;
56
57 struct rockchip_pvtm_channel {
58         u32 reg_cal;
59         u32 reg_freq;
60         unsigned char ch;
61         unsigned char *clk_name;
62         unsigned int num_sub;
63         unsigned int bit_start;
64         unsigned int bit_en;
65         unsigned int bit_freq_done;
66 };
67
68 struct rockchip_pvtm_info {
69         u32 con;
70         u32 sta;
71         unsigned int num_channels;
72         const struct rockchip_pvtm_channel *channels;
73         u32 (*get_value)(struct rockchip_pvtm *pvtm, unsigned int sub_ch,
74                          unsigned int time_us);
75         void (*set_ring_sel)(struct rockchip_pvtm *pvtm, unsigned int sub_ch);
76 };
77
78 struct rockchip_pvtm {
79         u32 con;
80         u32 sta;
81         struct list_head node;
82         struct device *dev;
83         struct regmap *grf;
84         struct clk *clk;
85         const struct rockchip_pvtm_channel *channel;
86         u32 (*get_value)(struct rockchip_pvtm *pvtm, unsigned int sub_ch,
87                          unsigned int time_us);
88         void (*set_ring_sel)(struct rockchip_pvtm *pvtm, unsigned int sub_ch);
89 };
90
91 static LIST_HEAD(pvtm_list);
92
93 #ifdef CONFIG_DEBUG_FS
94
95 static struct dentry *rootdir;
96
97 static int pvtm_value_show(struct seq_file *s, void *data)
98 {
99         struct rockchip_pvtm *pvtm = (struct rockchip_pvtm *)s->private;
100         u32 value;
101         int i;
102
103         if (!pvtm) {
104                 pr_err("pvtm struct NULL\n");
105                 return -EINVAL;
106         }
107
108         for (i = 0; i < pvtm->channel->num_sub; i++) {
109                 value = pvtm->get_value(pvtm, i, 1000);
110                 seq_printf(s, "%d ", value);
111         }
112         seq_puts(s, "\n");
113
114         return 0;
115 }
116
117 static int pvtm_value_open(struct inode *inode, struct file *file)
118 {
119         return single_open(file, pvtm_value_show, inode->i_private);
120 }
121
122 static const struct file_operations pvtm_value_fops = {
123         .open           = pvtm_value_open,
124         .read           = seq_read,
125         .llseek         = seq_lseek,
126         .release        = single_release,
127 };
128
129 static int __init pvtm_debug_init(void)
130 {
131         struct dentry *dentry, *d;
132         struct rockchip_pvtm *pvtm;
133
134         rootdir = debugfs_create_dir("pvtm", NULL);
135         if (!rootdir) {
136                 pr_err("Failed to create pvtm debug directory\n");
137                 return -ENOMEM;
138         }
139
140         if (list_empty(&pvtm_list)) {
141                 pr_info("pvtm list NULL\n");
142                 return 0;
143         }
144
145         list_for_each_entry(pvtm, &pvtm_list, node) {
146                 dentry = debugfs_create_dir(pvtm->channel->clk_name, rootdir);
147                 if (!dentry) {
148                         dev_err(pvtm->dev, "failed to creat pvtm %s debug dir\n",
149                                 pvtm->channel->clk_name);
150                         return -ENOMEM;
151                 }
152
153                 d = debugfs_create_file("value", 0444, dentry,
154                                         (void *)pvtm, &pvtm_value_fops);
155                 if (!d) {
156                         dev_err(pvtm->dev, "failed to pvtm %s value node\n",
157                                 pvtm->channel->clk_name);
158                         return -ENOMEM;
159                 }
160         }
161
162         return 0;
163 }
164
165 late_initcall(pvtm_debug_init);
166
167 #endif
168
169 u32 rockchip_get_pvtm_value(unsigned int ch, unsigned int sub_ch,
170                             unsigned int time_us)
171 {
172         struct rockchip_pvtm *pvtm;
173
174         if (list_empty(&pvtm_list)) {
175                 pr_err("pvtm list NULL\n");
176                 return -EINVAL;
177         }
178
179         list_for_each_entry(pvtm, &pvtm_list, node) {
180                 if (pvtm->channel->ch == ch)
181                         break;
182         }
183
184         if (sub_ch >= pvtm->channel->num_sub) {
185                 pr_err("invalid pvtm sub_ch %d\n", sub_ch);
186                 return -EINVAL;
187         }
188
189         return pvtm->get_value(pvtm, sub_ch, time_us);
190 }
191
192 static void rockchip_pvtm_delay(unsigned int delay)
193 {
194         unsigned int ms = delay / 1000;
195         unsigned int us = delay % 1000;
196
197         if (ms > 0) {
198                 if (ms < 20)
199                         us += ms * 1000;
200                 else
201                         msleep(ms);
202         }
203
204         if (us >= 10)
205                 usleep_range(us, us + 100);
206         else
207                 udelay(us);
208 }
209
210 static void rk3399_pvtm_set_ring_sel(struct rockchip_pvtm *pvtm,
211                                      unsigned int sub_ch)
212 {
213         unsigned int ch = pvtm->channel->ch;
214
215         if (ch == RK3399_PVTM_CORE_B) {
216                 regmap_write(pvtm->grf, pvtm->con + 0x14,
217                              wr_mask_bit(sub_ch >> 0x3, 0, 0x1));
218                 sub_ch &= 0x3;
219         }
220         if (ch != RK3399_PVTM_PMU)
221                 regmap_write(pvtm->grf, pvtm->con,
222                              wr_mask_bit(sub_ch, (ch * 0x4 + 0x2), 0x3));
223 }
224
225 static u32 rockchip_pvtm_get_value(struct rockchip_pvtm *pvtm,
226                                    unsigned int sub_ch,
227                                    unsigned int time_us)
228 {
229         const struct rockchip_pvtm_channel *channel = pvtm->channel;
230         unsigned int ch = pvtm->channel->ch;
231         unsigned int clk_cnt, check_cnt = 100;
232         u32 sta, val = 0;
233         int ret;
234
235         ret = clk_prepare_enable(pvtm->clk);
236         if (ret < 0) {
237                 dev_err(pvtm->dev, "failed to enable %d\n", ch);
238                 return 0;
239         }
240
241         regmap_write(pvtm->grf, pvtm->con,
242                      wr_mask_bit(0x1, channel->bit_en, 0x1));
243
244         if (pvtm->set_ring_sel)
245                 pvtm->set_ring_sel(pvtm, sub_ch);
246
247         /* clk = 24 Mhz, T = 1 / 24 us */
248         clk_cnt = time_us * 24;
249         regmap_write(pvtm->grf, pvtm->con + channel->reg_cal, clk_cnt);
250
251         regmap_write(pvtm->grf, pvtm->con,
252                      wr_mask_bit(0x1, channel->bit_start, 0x1));
253
254         rockchip_pvtm_delay(time_us);
255
256         while (check_cnt--) {
257                 regmap_read(pvtm->grf, pvtm->sta, &sta);
258                 if (sta & channel->bit_freq_done)
259                         break;
260                 udelay(4);
261         }
262
263         if (check_cnt) {
264                 regmap_read(pvtm->grf, pvtm->sta + channel->reg_freq, &val);
265         } else {
266                 dev_err(pvtm->dev, "wait pvtm_done timeout!\n");
267                 val = 0;
268         }
269
270         regmap_write(pvtm->grf, pvtm->con,
271                      wr_mask_bit(0, channel->bit_start, 0x1));
272
273         regmap_write(pvtm->grf, pvtm->con,
274                      wr_mask_bit(0, channel->bit_en, 0x1));
275
276         clk_disable_unprepare(pvtm->clk);
277
278         return val;
279 }
280
281 static const struct rockchip_pvtm_channel rk3366_pvtm_channels[] = {
282         PVTM(RK3366_PVTM_CORE, "core", 1, 0, 1, 0x4, 0, 0x4),
283         PVTM(RK3366_PVTM_GPU, "gpu", 1, 8, 9, 0x8, 1, 0x8),
284 };
285
286 static const struct rockchip_pvtm_info rk3366_pvtm = {
287         .con = 0x800,
288         .sta = 0x80c,
289         .num_channels = ARRAY_SIZE(rk3366_pvtm_channels),
290         .channels = rk3366_pvtm_channels,
291         .get_value = rockchip_pvtm_get_value,
292 };
293
294 static const struct rockchip_pvtm_channel rk3366_pmupvtm_channels[] = {
295         PVTM(RK3366_PVTM_PMU, "pmu", 1, 0, 1, 0x4, 0, 0x4),
296 };
297
298 static const struct rockchip_pvtm_info rk3366_pmupvtm = {
299         .con = 0x180,
300         .sta = 0x190,
301         .num_channels = ARRAY_SIZE(rk3366_pmupvtm_channels),
302         .channels = rk3366_pmupvtm_channels,
303         .get_value = rockchip_pvtm_get_value,
304 };
305
306 static const struct rockchip_pvtm_channel rk3399_pvtm_channels[] = {
307         PVTM(RK3399_PVTM_CORE_L, "core_l", 4, 0, 1, 0x4, 0, 0x4),
308         PVTM(RK3399_PVTM_CORE_B, "core_b", 6, 4, 5, 0x8, 1, 0x8),
309         PVTM(RK3399_PVTM_DDR, "ddr", 4, 8, 9, 0xc, 3, 0x10),
310         PVTM(RK3399_PVTM_GPU, "gpu", 4, 12, 13, 0x10, 2, 0xc),
311 };
312
313 static const struct rockchip_pvtm_info rk3399_pvtm = {
314         .con = 0xe600,
315         .sta = 0xe620,
316         .num_channels = ARRAY_SIZE(rk3399_pvtm_channels),
317         .channels = rk3399_pvtm_channels,
318         .get_value = rockchip_pvtm_get_value,
319         .set_ring_sel = rk3399_pvtm_set_ring_sel,
320 };
321
322 static const struct rockchip_pvtm_channel rk3399_pmupvtm_channels[] = {
323         PVTM(RK3399_PVTM_PMU, "pmu", 1, 0, 1, 0x4, 0, 0x4),
324 };
325
326 static const struct rockchip_pvtm_info rk3399_pmupvtm = {
327         .con = 0x240,
328         .sta = 0x248,
329         .num_channels = ARRAY_SIZE(rk3399_pmupvtm_channels),
330         .channels = rk3399_pmupvtm_channels,
331         .get_value = rockchip_pvtm_get_value,
332 };
333
334 static const struct of_device_id rockchip_pvtm_match[] = {
335         {
336                 .compatible = "rockchip,rk3366-pvtm",
337                 .data = (void *)&rk3366_pvtm,
338         },
339         {
340                 .compatible = "rockchip,rk3366-pmu-pvtm",
341                 .data = (void *)&rk3366_pmupvtm,
342         },
343         {
344                 .compatible = "rockchip,rk3399-pvtm",
345                 .data = (void *)&rk3399_pvtm,
346         },
347         {
348                 .compatible = "rockchip,rk3399-pmu-pvtm",
349                 .data = (void *)&rk3399_pmupvtm,
350         },
351         { /* sentinel */ },
352 };
353 MODULE_DEVICE_TABLE(of, rockchip_pvtm_match);
354
355 static int rockchip_pvtm_probe(struct platform_device *pdev)
356 {
357         struct device *dev = &pdev->dev;
358         const struct of_device_id *match;
359         const struct rockchip_pvtm_info *info;
360         struct rockchip_pvtm *pvtm;
361         struct regmap *grf;
362         int i;
363
364         match = of_match_device(dev->driver->of_match_table, dev);
365         if (!match || !match->data) {
366                 dev_err(dev, "missing pvtm data\n");
367                 return -EINVAL;
368         }
369
370         info = match->data;
371
372         if (!dev->parent || !dev->parent->of_node)
373                 return -EINVAL;
374
375         grf = syscon_node_to_regmap(dev->parent->of_node);
376         if (IS_ERR(grf))
377                 return PTR_ERR(grf);
378
379         pvtm = devm_kzalloc(dev, sizeof(*pvtm) * info->num_channels,
380                             GFP_KERNEL);
381         if (!pvtm)
382                 return -ENOMEM;
383
384         for (i = 0; i < info->num_channels; i++) {
385                 pvtm[i].dev = &pdev->dev;
386                 pvtm[i].grf = grf;
387                 pvtm[i].con = info->con;
388                 pvtm[i].sta = info->sta;
389                 pvtm[i].get_value = info->get_value;
390                 pvtm[i].channel = &info->channels[i];
391                 if (info->set_ring_sel)
392                         pvtm[i].set_ring_sel = info->set_ring_sel;
393
394                 pvtm[i].clk = devm_clk_get(dev, info->channels[i].clk_name);
395                 if (IS_ERR_OR_NULL(pvtm[i].clk)) {
396                         dev_err(dev, "failed to get clk %d %s\n", i,
397                                 info->channels[i].clk_name);
398                         return PTR_ERR(pvtm[i].clk);
399                 }
400
401                 list_add(&pvtm[i].node, &pvtm_list);
402         }
403
404         return 0;
405 }
406
407 static struct platform_driver rockchip_pvtm_driver = {
408         .probe = rockchip_pvtm_probe,
409         .driver = {
410                 .name  = "rockchip-pvtm",
411                 .of_match_table = rockchip_pvtm_match,
412         },
413 };
414
415 module_platform_driver(rockchip_pvtm_driver);
416
417 MODULE_DESCRIPTION("Rockchip PVTM driver");
418 MODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>");
419 MODULE_LICENSE("GPL v2");