2 * Rockchip PVTM support.
4 * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
5 * Author: Finley Xiao <finley.xiao@rock-chips.com>
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.
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
17 #include <linux/clk.h>
18 #include <linux/delay.h>
19 #include <linux/device.h>
20 #include <linux/debugfs.h>
22 #include <linux/mfd/syscon.h>
23 #include <linux/module.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>
31 #define RK3366_PVTM_CORE 0
32 #define RK3366_PVTM_GPU 1
33 #define RK3366_PVTM_PMU 2
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
41 #define wr_mask_bit(v, off, mask) ((v) << (off) | (mask) << (16 + off))
43 #define PVTM(_ch, _name, _num_sub, _start, _en, _cal, _done, _freq) \
47 .num_sub = _num_sub, \
48 .bit_start = _start, \
51 .bit_freq_done = _done, \
57 struct rockchip_pvtm_channel {
61 unsigned char *clk_name;
63 unsigned int bit_start;
65 unsigned int bit_freq_done;
68 struct rockchip_pvtm_info {
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);
78 struct rockchip_pvtm {
81 struct list_head node;
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);
91 static LIST_HEAD(pvtm_list);
93 #ifdef CONFIG_DEBUG_FS
95 static struct dentry *rootdir;
97 static int pvtm_value_show(struct seq_file *s, void *data)
99 struct rockchip_pvtm *pvtm = (struct rockchip_pvtm *)s->private;
104 pr_err("pvtm struct NULL\n");
108 for (i = 0; i < pvtm->channel->num_sub; i++) {
109 value = pvtm->get_value(pvtm, i, 1000);
110 seq_printf(s, "%d ", value);
117 static int pvtm_value_open(struct inode *inode, struct file *file)
119 return single_open(file, pvtm_value_show, inode->i_private);
122 static const struct file_operations pvtm_value_fops = {
123 .open = pvtm_value_open,
126 .release = single_release,
129 static int __init pvtm_debug_init(void)
131 struct dentry *dentry, *d;
132 struct rockchip_pvtm *pvtm;
134 rootdir = debugfs_create_dir("pvtm", NULL);
136 pr_err("Failed to create pvtm debug directory\n");
140 if (list_empty(&pvtm_list)) {
141 pr_info("pvtm list NULL\n");
145 list_for_each_entry(pvtm, &pvtm_list, node) {
146 dentry = debugfs_create_dir(pvtm->channel->clk_name, rootdir);
148 dev_err(pvtm->dev, "failed to creat pvtm %s debug dir\n",
149 pvtm->channel->clk_name);
153 d = debugfs_create_file("value", 0444, dentry,
154 (void *)pvtm, &pvtm_value_fops);
156 dev_err(pvtm->dev, "failed to pvtm %s value node\n",
157 pvtm->channel->clk_name);
165 late_initcall(pvtm_debug_init);
169 u32 rockchip_get_pvtm_value(unsigned int ch, unsigned int sub_ch,
170 unsigned int time_us)
172 struct rockchip_pvtm *pvtm;
174 if (list_empty(&pvtm_list)) {
175 pr_err("pvtm list NULL\n");
179 list_for_each_entry(pvtm, &pvtm_list, node) {
180 if (pvtm->channel->ch == ch)
184 if (sub_ch >= pvtm->channel->num_sub) {
185 pr_err("invalid pvtm sub_ch %d\n", sub_ch);
189 return pvtm->get_value(pvtm, sub_ch, time_us);
192 static void rockchip_pvtm_delay(unsigned int delay)
194 unsigned int ms = delay / 1000;
195 unsigned int us = delay % 1000;
205 usleep_range(us, us + 100);
210 static void rk3399_pvtm_set_ring_sel(struct rockchip_pvtm *pvtm,
213 unsigned int ch = pvtm->channel->ch;
215 if (ch == RK3399_PVTM_CORE_B) {
216 regmap_write(pvtm->grf, pvtm->con + 0x14,
217 wr_mask_bit(sub_ch >> 0x3, 0, 0x1));
220 if (ch != RK3399_PVTM_PMU)
221 regmap_write(pvtm->grf, pvtm->con,
222 wr_mask_bit(sub_ch, (ch * 0x4 + 0x2), 0x3));
225 static u32 rockchip_pvtm_get_value(struct rockchip_pvtm *pvtm,
227 unsigned int time_us)
229 const struct rockchip_pvtm_channel *channel = pvtm->channel;
230 unsigned int ch = pvtm->channel->ch;
231 unsigned int clk_cnt, check_cnt = 100;
235 ret = clk_prepare_enable(pvtm->clk);
237 dev_err(pvtm->dev, "failed to enable %d\n", ch);
241 regmap_write(pvtm->grf, pvtm->con,
242 wr_mask_bit(0x1, channel->bit_en, 0x1));
244 if (pvtm->set_ring_sel)
245 pvtm->set_ring_sel(pvtm, sub_ch);
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);
251 regmap_write(pvtm->grf, pvtm->con,
252 wr_mask_bit(0x1, channel->bit_start, 0x1));
254 rockchip_pvtm_delay(time_us);
256 while (check_cnt--) {
257 regmap_read(pvtm->grf, pvtm->sta, &sta);
258 if (sta & channel->bit_freq_done)
264 regmap_read(pvtm->grf, pvtm->sta + channel->reg_freq, &val);
266 dev_err(pvtm->dev, "wait pvtm_done timeout!\n");
270 regmap_write(pvtm->grf, pvtm->con,
271 wr_mask_bit(0, channel->bit_start, 0x1));
273 regmap_write(pvtm->grf, pvtm->con,
274 wr_mask_bit(0, channel->bit_en, 0x1));
276 clk_disable_unprepare(pvtm->clk);
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),
286 static const struct rockchip_pvtm_info rk3366_pvtm = {
289 .num_channels = ARRAY_SIZE(rk3366_pvtm_channels),
290 .channels = rk3366_pvtm_channels,
291 .get_value = rockchip_pvtm_get_value,
294 static const struct rockchip_pvtm_channel rk3366_pmupvtm_channels[] = {
295 PVTM(RK3366_PVTM_PMU, "pmu", 1, 0, 1, 0x4, 0, 0x4),
298 static const struct rockchip_pvtm_info rk3366_pmupvtm = {
301 .num_channels = ARRAY_SIZE(rk3366_pmupvtm_channels),
302 .channels = rk3366_pmupvtm_channels,
303 .get_value = rockchip_pvtm_get_value,
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),
313 static const struct rockchip_pvtm_info rk3399_pvtm = {
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,
322 static const struct rockchip_pvtm_channel rk3399_pmupvtm_channels[] = {
323 PVTM(RK3399_PVTM_PMU, "pmu", 1, 0, 1, 0x4, 0, 0x4),
326 static const struct rockchip_pvtm_info rk3399_pmupvtm = {
329 .num_channels = ARRAY_SIZE(rk3399_pmupvtm_channels),
330 .channels = rk3399_pmupvtm_channels,
331 .get_value = rockchip_pvtm_get_value,
334 static const struct of_device_id rockchip_pvtm_match[] = {
336 .compatible = "rockchip,rk3366-pvtm",
337 .data = (void *)&rk3366_pvtm,
340 .compatible = "rockchip,rk3366-pmu-pvtm",
341 .data = (void *)&rk3366_pmupvtm,
344 .compatible = "rockchip,rk3399-pvtm",
345 .data = (void *)&rk3399_pvtm,
348 .compatible = "rockchip,rk3399-pmu-pvtm",
349 .data = (void *)&rk3399_pmupvtm,
353 MODULE_DEVICE_TABLE(of, rockchip_pvtm_match);
355 static int rockchip_pvtm_probe(struct platform_device *pdev)
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;
364 match = of_match_device(dev->driver->of_match_table, dev);
365 if (!match || !match->data) {
366 dev_err(dev, "missing pvtm data\n");
372 if (!dev->parent || !dev->parent->of_node)
375 grf = syscon_node_to_regmap(dev->parent->of_node);
379 pvtm = devm_kzalloc(dev, sizeof(*pvtm) * info->num_channels,
384 for (i = 0; i < info->num_channels; i++) {
385 pvtm[i].dev = &pdev->dev;
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;
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);
401 list_add(&pvtm[i].node, &pvtm_list);
407 static struct platform_driver rockchip_pvtm_driver = {
408 .probe = rockchip_pvtm_probe,
410 .name = "rockchip-pvtm",
411 .of_match_table = rockchip_pvtm_match,
415 module_platform_driver(rockchip_pvtm_driver);
417 MODULE_DESCRIPTION("Rockchip PVTM driver");
418 MODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>");
419 MODULE_LICENSE("GPL v2");