1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
4 */
5
6 #include <linux/clk.h>
7 #include <linux/delay.h>
8 #include <linux/err.h>
9 #include <linux/mfd/syscon.h>
10 #include <linux/module.h>
11 #include <linux/of.h>
12 #include <linux/of_platform.h>
13 #include <linux/platform_device.h>
14 #include <linux/regmap.h>
15 #include <linux/slab.h>
16 #include <linux/string.h>
17 #include <linux/clk-provider.h>
18
19 #define CLK_SEL_EXTERNAL_32K 0
20 #define CLK_SEL_INTERNAL_PVTM 1
21
22 #define wr_msk_bit(v, off, msk) ((v) << (off) | ((msk) << (16 + (off))))
23
24 struct rockchip_clock_pvtm;
25
26 struct rockchip_clock_pvtm_info {
27 u32 con;
28 u32 sta;
29 u32 sel_con;
30 u32 sel_shift;
31 u32 sel_value;
32 u32 sel_mask;
33 u32 div_shift;
34 u32 div_mask;
35
36 u32 (*get_value)(struct rockchip_clock_pvtm *pvtm, unsigned int time_us);
37 int (*init_freq)(struct rockchip_clock_pvtm *pvtm);
38 int (*sel_enable)(struct rockchip_clock_pvtm *pvtm);
39 };
40
41 struct rockchip_clock_pvtm {
42 const struct rockchip_clock_pvtm_info *info;
43 struct regmap *grf;
44 struct clk *pvtm_clk;
45 struct clk *clk;
46 unsigned long rate;
47 };
48
xin32k_pvtm_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)49 static unsigned long xin32k_pvtm_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
50 {
51 return 0x8000;
52 }
53
54 static const struct clk_ops xin32k_pvtm = {
55 .recalc_rate = xin32k_pvtm_recalc_rate,
56 };
57
rockchip_clock_pvtm_delay(unsigned int delay)58 static void rockchip_clock_pvtm_delay(unsigned int delay)
59 {
60 unsigned int ms = delay / 0x3e8;
61 unsigned int us = delay % 0x3e8;
62
63 if (ms > 0) {
64 if (ms < 0x14) {
65 us += ms * 0x3e8;
66 } else {
67 msleep(ms);
68 }
69 }
70
71 if (us >= 0xa) {
72 usleep_range(us, us + 0x64);
73 } else {
74 udelay(us);
75 }
76 }
77
rockchip_clock_sel_internal_pvtm(struct rockchip_clock_pvtm *pvtm)78 static int rockchip_clock_sel_internal_pvtm(struct rockchip_clock_pvtm *pvtm)
79 {
80 int ret = 0;
81
82 ret = regmap_write(pvtm->grf, pvtm->info->sel_con,
83 wr_msk_bit(pvtm->info->sel_value, pvtm->info->sel_shift, pvtm->info->sel_mask));
84 if (ret != 0) {
85 pr_err("%s: fail to write register\n", __func__);
86 }
87
88 return ret;
89 }
90
91 /* get pmu pvtm value */
rockchip_clock_pvtm_get_value(struct rockchip_clock_pvtm *pvtm, u32 time_us)92 static u32 rockchip_clock_pvtm_get_value(struct rockchip_clock_pvtm *pvtm, u32 time_us)
93 {
94 const struct rockchip_clock_pvtm_info *info = pvtm->info;
95 u32 val = 0, sta = 0;
96 u32 clk_cnt, check_cnt;
97
98 /* 24m clk ,24cnt=1us */
99 clk_cnt = time_us * 0x18;
100
101 regmap_write(pvtm->grf, info->con + 0x4, clk_cnt);
102 regmap_write(pvtm->grf, info->con, wr_msk_bit(0x3, 0, 0x3));
103
104 rockchip_clock_pvtm_delay(time_us);
105
106 check_cnt = 0x64;
107 while (check_cnt--) {
108 regmap_read(pvtm->grf, info->sta, &sta);
109 if (sta & 0x1) {
110 break;
111 }
112 udelay(0x4);
113 }
114
115 if (check_cnt) {
116 regmap_read(pvtm->grf, info->sta + 0x4, &val);
117 } else {
118 pr_err("%s: wait pvtm_done timeout!\n", __func__);
119 val = 0;
120 }
121
122 regmap_write(pvtm->grf, info->con, wr_msk_bit(0, 0, 0x3));
123
124 return val;
125 }
126
rockchip_clock_pvtm_init_freq(struct rockchip_clock_pvtm *pvtm)127 static int rockchip_clock_pvtm_init_freq(struct rockchip_clock_pvtm *pvtm)
128 {
129 u32 pvtm_cnt = 0;
130 u32 div, time_us;
131 int ret = 0;
132
133 time_us = 0x3e8;
134 pvtm_cnt = pvtm->info->get_value(pvtm, time_us);
135 pr_debug("get pvtm_cnt = %d\n", pvtm_cnt);
136
137 /* set pvtm_div to get rate */
138 div = DIV_ROUND_UP(0x3e8 * pvtm_cnt, pvtm->rate);
139 if (div > pvtm->info->div_mask) {
140 pr_err("pvtm_div out of bounary! set max instead\n");
141 div = pvtm->info->div_mask;
142 }
143
144 pr_debug("set div %d, rate %luKHZ\n", div, pvtm->rate);
145 ret = regmap_write(pvtm->grf, pvtm->info->con, wr_msk_bit(div, pvtm->info->div_shift, pvtm->info->div_mask));
146 if (ret != 0) {
147 goto out;
148 }
149
150 /* pmu pvtm oscilator enable */
151 ret = regmap_write(pvtm->grf, pvtm->info->con, wr_msk_bit(1, 1, 0x1));
152 if (ret != 0) {
153 goto out;
154 }
155
156 ret = pvtm->info->sel_enable(pvtm);
157 out:
158 if (ret != 0) {
159 pr_err("%s: fail to write register\n", __func__);
160 }
161
162 return ret;
163 }
164
clock_pvtm_regitstor(struct device *dev, struct rockchip_clock_pvtm *pvtm)165 static int clock_pvtm_regitstor(struct device *dev, struct rockchip_clock_pvtm *pvtm)
166 {
167 struct clk_init_data init = {};
168 struct clk_hw *clk_hw;
169
170 /* Init the xin32k_pvtm */
171 pvtm->info->init_freq(pvtm);
172
173 init.parent_names = NULL;
174 init.num_parents = 0;
175 init.name = "xin32k_pvtm";
176 init.ops = &xin32k_pvtm;
177
178 clk_hw = devm_kzalloc(dev, sizeof(*clk_hw), GFP_KERNEL);
179 if (!clk_hw) {
180 return -ENOMEM;
181 }
182 clk_hw->init = &init;
183
184 /* optional override of the clockname */
185 of_property_read_string_index(dev->of_node, "clock-output-names", 0, &init.name);
186 pvtm->clk = devm_clk_register(dev, clk_hw);
187 if (IS_ERR(pvtm->clk)) {
188 return PTR_ERR(pvtm->clk);
189 }
190
191 return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, pvtm->clk);
192 }
193
194 static const struct rockchip_clock_pvtm_info rk3368_pvtm_data = {
195 .con = 0x180,
196 .sta = 0x190,
197 .sel_con = 0x100,
198 .sel_shift = 6,
199 .sel_value = CLK_SEL_INTERNAL_PVTM,
200 .sel_mask = 0x1,
201 .div_shift = 2,
202 .div_mask = 0x3f,
203
204 .sel_enable = rockchip_clock_sel_internal_pvtm,
205 .get_value = rockchip_clock_pvtm_get_value,
206 .init_freq = rockchip_clock_pvtm_init_freq,
207 };
208
209 static const struct of_device_id rockchip_clock_pvtm_match[] = {{
210 .compatible = "rockchip,rk3368-pvtm-clock",
211 .data = (void *)&rk3368_pvtm_data,
212 },
213 {}};
214 MODULE_DEVICE_TABLE(of, rockchip_clock_pvtm_match);
215
rockchip_clock_pvtm_probe(struct platform_device *pdev)216 static int rockchip_clock_pvtm_probe(struct platform_device *pdev)
217 {
218 struct device *dev = &pdev->dev;
219 struct device_node *np = pdev->dev.of_node;
220 const struct of_device_id *match;
221 struct rockchip_clock_pvtm *pvtm;
222 int error;
223 u32 rate;
224
225 pvtm = devm_kzalloc(dev, sizeof(*pvtm), GFP_KERNEL);
226 if (!pvtm) {
227 return -ENOMEM;
228 }
229
230 match = of_match_node(rockchip_clock_pvtm_match, np);
231 if (!match) {
232 return -ENXIO;
233 }
234
235 pvtm->info = (const struct rockchip_clock_pvtm_info *)match->data;
236 if (!pvtm->info) {
237 return -EINVAL;
238 }
239
240 if (!dev->parent || !dev->parent->of_node) {
241 return -EINVAL;
242 }
243
244 pvtm->grf = syscon_node_to_regmap(dev->parent->of_node);
245 if (IS_ERR(pvtm->grf)) {
246 return PTR_ERR(pvtm->grf);
247 }
248
249 if (!of_property_read_u32(np, "pvtm-rate", &rate)) {
250 pvtm->rate = rate;
251 } else {
252 pvtm->rate = 0x8000;
253 }
254
255 pvtm->pvtm_clk = devm_clk_get(&pdev->dev, "pvtm_pmu_clk");
256 if (IS_ERR(pvtm->pvtm_clk)) {
257 error = PTR_ERR(pvtm->pvtm_clk);
258 if (error != -EPROBE_DEFER) {
259 dev_err(&pdev->dev, "failed to get pvtm core clock: %d\n", error);
260 }
261 goto out_probe;
262 }
263
264 error = clk_prepare_enable(pvtm->pvtm_clk);
265 if (error) {
266 dev_err(&pdev->dev, "failed to enable the clock: %d\n", error);
267 goto out_probe;
268 }
269
270 platform_set_drvdata(pdev, pvtm);
271
272 error = clock_pvtm_regitstor(&pdev->dev, pvtm);
273 if (error) {
274 dev_err(&pdev->dev, "failed to registor clock: %d\n", error);
275 goto out_clk_put;
276 }
277
278 return error;
279
280 out_clk_put:
281 clk_disable_unprepare(pvtm->pvtm_clk);
282 out_probe:
283 return error;
284 }
285
rockchip_clock_pvtm_remove(struct platform_device *pdev)286 static int rockchip_clock_pvtm_remove(struct platform_device *pdev)
287 {
288 struct rockchip_clock_pvtm *pvtm = platform_get_drvdata(pdev);
289 struct device_node *np = pdev->dev.of_node;
290
291 of_clk_del_provider(np);
292 clk_disable_unprepare(pvtm->pvtm_clk);
293
294 return 0;
295 }
296
297 static struct platform_driver rockchip_clock_pvtm_driver = {
298 .driver =
299 {
300 .name = "rockchip-clcok-pvtm",
301 .of_match_table = rockchip_clock_pvtm_match,
302 },
303 .probe = rockchip_clock_pvtm_probe,
304 .remove = rockchip_clock_pvtm_remove,
305 };
306
307 module_platform_driver(rockchip_clock_pvtm_driver);
308
309 MODULE_DESCRIPTION("Rockchip Clock Pvtm Driver");
310 MODULE_LICENSE("GPL v2");
311