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,
37 unsigned int time_us);
38 int (*init_freq)(struct rockchip_clock_pvtm *pvtm);
39 int (*sel_enable)(struct rockchip_clock_pvtm *pvtm);
40 };
41
42 struct rockchip_clock_pvtm {
43 const struct rockchip_clock_pvtm_info *info;
44 struct regmap *grf;
45 struct clk *pvtm_clk;
46 struct clk *clk;
47 unsigned long rate;
48 };
49
xin32k_pvtm_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)50 static unsigned long xin32k_pvtm_recalc_rate(struct clk_hw *hw,
51 unsigned long parent_rate)
52 {
53 return 32768;
54 }
55
56 static const struct clk_ops xin32k_pvtm = {
57 .recalc_rate = xin32k_pvtm_recalc_rate,
58 };
59
rockchip_clock_pvtm_delay(unsigned int delay)60 static void rockchip_clock_pvtm_delay(unsigned int delay)
61 {
62 unsigned int ms = delay / 1000;
63 unsigned int us = delay % 1000;
64
65 if (ms > 0) {
66 if (ms < 20)
67 us += ms * 1000;
68 else
69 msleep(ms);
70 }
71
72 if (us >= 10)
73 usleep_range(us, us + 100);
74 else
75 udelay(us);
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,
84 pvtm->info->sel_shift,
85 pvtm->info->sel_mask));
86 if (ret != 0)
87 pr_err("%s: fail to write register\n", __func__);
88
89 return ret;
90 }
91
92 /* get pmu pvtm value */
rockchip_clock_pvtm_get_value(struct rockchip_clock_pvtm * pvtm,u32 time_us)93 static u32 rockchip_clock_pvtm_get_value(struct rockchip_clock_pvtm *pvtm,
94 u32 time_us)
95 {
96 const struct rockchip_clock_pvtm_info *info = pvtm->info;
97 u32 val = 0, sta = 0;
98 u32 clk_cnt, check_cnt;
99
100 /* 24m clk ,24cnt=1us */
101 clk_cnt = time_us * 24;
102
103 regmap_write(pvtm->grf, info->con + 0x4, clk_cnt);
104 regmap_write(pvtm->grf, info->con, wr_msk_bit(3, 0, 0x3));
105
106 rockchip_clock_pvtm_delay(time_us);
107
108 check_cnt = 100;
109 while (check_cnt--) {
110 regmap_read(pvtm->grf, info->sta, &sta);
111 if (sta & 0x1)
112 break;
113 udelay(4);
114 }
115
116 if (check_cnt) {
117 regmap_read(pvtm->grf, info->sta + 0x4, &val);
118 } else {
119 pr_err("%s: wait pvtm_done timeout!\n", __func__);
120 val = 0;
121 }
122
123 regmap_write(pvtm->grf, info->con, wr_msk_bit(0, 0, 0x3));
124
125 return val;
126 }
127
rockchip_clock_pvtm_init_freq(struct rockchip_clock_pvtm * pvtm)128 static int rockchip_clock_pvtm_init_freq(struct rockchip_clock_pvtm *pvtm)
129 {
130 u32 pvtm_cnt = 0;
131 u32 div, time_us;
132 int ret = 0;
133
134 time_us = 1000;
135 pvtm_cnt = pvtm->info->get_value(pvtm, time_us);
136 pr_debug("get pvtm_cnt = %d\n", pvtm_cnt);
137
138 /* set pvtm_div to get rate */
139 div = DIV_ROUND_UP(1000 * pvtm_cnt, pvtm->rate);
140 if (div > pvtm->info->div_mask) {
141 pr_err("pvtm_div out of bounary! set max instead\n");
142 div = pvtm->info->div_mask;
143 }
144
145 pr_debug("set div %d, rate %luKHZ\n", div, pvtm->rate);
146 ret = regmap_write(pvtm->grf, pvtm->info->con,
147 wr_msk_bit(div, pvtm->info->div_shift,
148 pvtm->info->div_mask));
149 if (ret != 0)
150 goto out;
151
152 /* pmu pvtm oscilator enable */
153 ret = regmap_write(pvtm->grf, pvtm->info->con,
154 wr_msk_bit(1, 1, 0x1));
155 if (ret != 0)
156 goto out;
157
158 ret = pvtm->info->sel_enable(pvtm);
159 out:
160 if (ret != 0)
161 pr_err("%s: fail to write register\n", __func__);
162
163 return ret;
164 }
165
clock_pvtm_regitstor(struct device * dev,struct rockchip_clock_pvtm * pvtm)166 static int clock_pvtm_regitstor(struct device *dev,
167 struct rockchip_clock_pvtm *pvtm)
168 {
169 struct clk_init_data init = {};
170 struct clk_hw *clk_hw;
171
172 /* Init the xin32k_pvtm */
173 pvtm->info->init_freq(pvtm);
174
175 init.parent_names = NULL;
176 init.num_parents = 0;
177 init.name = "xin32k_pvtm";
178 init.ops = &xin32k_pvtm;
179
180 clk_hw = devm_kzalloc(dev, sizeof(*clk_hw), GFP_KERNEL);
181 if (!clk_hw)
182 return -ENOMEM;
183 clk_hw->init = &init;
184
185 /* optional override of the clockname */
186 of_property_read_string_index(dev->of_node, "clock-output-names",
187 0, &init.name);
188 pvtm->clk = devm_clk_register(dev, clk_hw);
189 if (IS_ERR(pvtm->clk))
190 return PTR_ERR(pvtm->clk);
191
192 return of_clk_add_provider(dev->of_node, of_clk_src_simple_get,
193 pvtm->clk);
194 }
195
196 static const struct rockchip_clock_pvtm_info rk3368_pvtm_data = {
197 .con = 0x180,
198 .sta = 0x190,
199 .sel_con = 0x100,
200 .sel_shift = 6,
201 .sel_value = CLK_SEL_INTERNAL_PVTM,
202 .sel_mask = 0x1,
203 .div_shift = 2,
204 .div_mask = 0x3f,
205
206 .sel_enable = rockchip_clock_sel_internal_pvtm,
207 .get_value = rockchip_clock_pvtm_get_value,
208 .init_freq = rockchip_clock_pvtm_init_freq,
209 };
210
211 static const struct of_device_id rockchip_clock_pvtm_match[] = {
212 {
213 .compatible = "rockchip,rk3368-pvtm-clock",
214 .data = (void *)&rk3368_pvtm_data,
215 },
216 {}
217 };
218 MODULE_DEVICE_TABLE(of, rockchip_clock_pvtm_match);
219
rockchip_clock_pvtm_probe(struct platform_device * pdev)220 static int rockchip_clock_pvtm_probe(struct platform_device *pdev)
221 {
222 struct device *dev = &pdev->dev;
223 struct device_node *np = pdev->dev.of_node;
224 const struct of_device_id *match;
225 struct rockchip_clock_pvtm *pvtm;
226 int error;
227 u32 rate;
228
229 pvtm = devm_kzalloc(dev, sizeof(*pvtm), GFP_KERNEL);
230 if (!pvtm)
231 return -ENOMEM;
232
233 match = of_match_node(rockchip_clock_pvtm_match, np);
234 if (!match)
235 return -ENXIO;
236
237 pvtm->info = (const struct rockchip_clock_pvtm_info *)match->data;
238 if (!pvtm->info)
239 return -EINVAL;
240
241 if (!dev->parent || !dev->parent->of_node)
242 return -EINVAL;
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 if (!of_property_read_u32(np, "pvtm-rate", &rate))
249 pvtm->rate = rate;
250 else
251 pvtm->rate = 32768;
252
253 pvtm->pvtm_clk = devm_clk_get(&pdev->dev, "pvtm_pmu_clk");
254 if (IS_ERR(pvtm->pvtm_clk)) {
255 error = PTR_ERR(pvtm->pvtm_clk);
256 if (error != -EPROBE_DEFER)
257 dev_err(&pdev->dev,
258 "failed to get pvtm core clock: %d\n",
259 error);
260 goto out_probe;
261 }
262
263 error = clk_prepare_enable(pvtm->pvtm_clk);
264 if (error) {
265 dev_err(&pdev->dev, "failed to enable the clock: %d\n",
266 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",
275 error);
276 goto out_clk_put;
277 }
278
279 return error;
280
281 out_clk_put:
282 clk_disable_unprepare(pvtm->pvtm_clk);
283 out_probe:
284 return error;
285 }
286
rockchip_clock_pvtm_remove(struct platform_device * pdev)287 static int rockchip_clock_pvtm_remove(struct platform_device *pdev)
288 {
289 struct rockchip_clock_pvtm *pvtm = platform_get_drvdata(pdev);
290 struct device_node *np = pdev->dev.of_node;
291
292 of_clk_del_provider(np);
293 clk_disable_unprepare(pvtm->pvtm_clk);
294
295 return 0;
296 }
297
298 static struct platform_driver rockchip_clock_pvtm_driver = {
299 .driver = {
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