xref: /linux/drivers/clk/x86/clk-pmc-atom.c (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Intel Atom platform clocks driver for BayTrail and CherryTrail SoCs
4  *
5  * Copyright (C) 2016, Intel Corporation
6  * Author: Irina Tirdea <irina.tirdea@intel.com>
7  */
8 
9 #include <linux/clk-provider.h>
10 #include <linux/clkdev.h>
11 #include <linux/err.h>
12 #include <linux/io.h>
13 #include <linux/platform_data/x86/clk-pmc-atom.h>
14 #include <linux/platform_data/x86/pmc_atom.h>
15 #include <linux/platform_device.h>
16 #include <linux/slab.h>
17 
18 #define PLT_CLK_NAME_BASE	"pmc_plt_clk"
19 
20 struct clk_plt_fixed {
21 	struct clk_hw *clk;
22 	struct clk_lookup *lookup;
23 };
24 
25 struct clk_plt {
26 	struct clk_hw hw;
27 	void __iomem *reg;
28 	struct clk_lookup *lookup;
29 	/* protect access to PMC registers */
30 	spinlock_t lock;
31 };
32 
33 #define to_clk_plt(_hw) container_of(_hw, struct clk_plt, hw)
34 
35 struct clk_plt_data {
36 	struct clk_plt_fixed **parents;
37 	u8 nparents;
38 	struct clk_plt *clks[PMC_CLK_NUM];
39 	struct clk_lookup *mclk_lookup;
40 	struct clk_lookup *ether_clk_lookup;
41 };
42 
43 /* Return an index in parent table */
44 static inline int plt_reg_to_parent(int reg)
45 {
46 	switch (reg & PMC_MASK_CLK_FREQ) {
47 	default:
48 	case PMC_CLK_FREQ_XTAL:
49 		return 0;
50 	case PMC_CLK_FREQ_PLL:
51 		return 1;
52 	}
53 }
54 
55 /* Return clk index of parent */
56 static inline int plt_parent_to_reg(int index)
57 {
58 	switch (index) {
59 	default:
60 	case 0:
61 		return PMC_CLK_FREQ_XTAL;
62 	case 1:
63 		return PMC_CLK_FREQ_PLL;
64 	}
65 }
66 
67 /* Abstract status in simpler enabled/disabled value */
68 static inline int plt_reg_to_enabled(int reg)
69 {
70 	switch (reg & PMC_MASK_CLK_CTL) {
71 	case PMC_CLK_CTL_GATED_ON_D3:
72 	case PMC_CLK_CTL_FORCE_ON:
73 		return 1;	/* enabled */
74 	case PMC_CLK_CTL_FORCE_OFF:
75 	case PMC_CLK_CTL_RESERVED:
76 	default:
77 		return 0;	/* disabled */
78 	}
79 }
80 
81 static void plt_clk_reg_update(struct clk_plt *clk, u32 mask, u32 val)
82 {
83 	u32 tmp;
84 	unsigned long flags;
85 
86 	spin_lock_irqsave(&clk->lock, flags);
87 
88 	tmp = readl(clk->reg);
89 	tmp = (tmp & ~mask) | (val & mask);
90 	writel(tmp, clk->reg);
91 
92 	spin_unlock_irqrestore(&clk->lock, flags);
93 }
94 
95 static int plt_clk_set_parent(struct clk_hw *hw, u8 index)
96 {
97 	struct clk_plt *clk = to_clk_plt(hw);
98 
99 	plt_clk_reg_update(clk, PMC_MASK_CLK_FREQ, plt_parent_to_reg(index));
100 
101 	return 0;
102 }
103 
104 static u8 plt_clk_get_parent(struct clk_hw *hw)
105 {
106 	struct clk_plt *clk = to_clk_plt(hw);
107 	u32 value;
108 
109 	value = readl(clk->reg);
110 
111 	return plt_reg_to_parent(value);
112 }
113 
114 static int plt_clk_enable(struct clk_hw *hw)
115 {
116 	struct clk_plt *clk = to_clk_plt(hw);
117 
118 	plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_ON);
119 
120 	return 0;
121 }
122 
123 static void plt_clk_disable(struct clk_hw *hw)
124 {
125 	struct clk_plt *clk = to_clk_plt(hw);
126 
127 	plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_OFF);
128 }
129 
130 static int plt_clk_is_enabled(struct clk_hw *hw)
131 {
132 	struct clk_plt *clk = to_clk_plt(hw);
133 	u32 value;
134 
135 	value = readl(clk->reg);
136 
137 	return plt_reg_to_enabled(value);
138 }
139 
140 static const struct clk_ops plt_clk_ops = {
141 	.enable = plt_clk_enable,
142 	.disable = plt_clk_disable,
143 	.is_enabled = plt_clk_is_enabled,
144 	.get_parent = plt_clk_get_parent,
145 	.set_parent = plt_clk_set_parent,
146 	.determine_rate = __clk_mux_determine_rate,
147 };
148 
149 static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id,
150 					const struct pmc_clk_data *pmc_data,
151 					const char **parent_names,
152 					int num_parents)
153 {
154 	struct clk_plt *pclk;
155 	struct clk_init_data init;
156 	int ret;
157 
158 	pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
159 	if (!pclk)
160 		return ERR_PTR(-ENOMEM);
161 
162 	init.name =  kasprintf(GFP_KERNEL, "%s_%d", PLT_CLK_NAME_BASE, id);
163 	if (!init.name)
164 		return ERR_PTR(-ENOMEM);
165 
166 	init.ops = &plt_clk_ops;
167 	init.flags = 0;
168 	init.parent_names = parent_names;
169 	init.num_parents = num_parents;
170 
171 	pclk->hw.init = &init;
172 	pclk->reg = pmc_data->base + PMC_CLK_CTL_OFFSET + id * PMC_CLK_CTL_SIZE;
173 	spin_lock_init(&pclk->lock);
174 
175 	/*
176 	 * On some systems, the pmc_plt_clocks already enabled by the
177 	 * firmware are being marked as critical to avoid them being
178 	 * gated by the clock framework.
179 	 */
180 	if (pmc_data->critical && plt_clk_is_enabled(&pclk->hw))
181 		init.flags |= CLK_IS_CRITICAL;
182 
183 	ret = devm_clk_hw_register(&pdev->dev, &pclk->hw);
184 	if (ret) {
185 		pclk = ERR_PTR(ret);
186 		goto err_free_init;
187 	}
188 
189 	pclk->lookup = clkdev_hw_create(&pclk->hw, init.name, NULL);
190 	if (!pclk->lookup) {
191 		pclk = ERR_PTR(-ENOMEM);
192 		goto err_free_init;
193 	}
194 
195 err_free_init:
196 	kfree(init.name);
197 	return pclk;
198 }
199 
200 static void plt_clk_unregister(struct clk_plt *pclk)
201 {
202 	clkdev_drop(pclk->lookup);
203 }
204 
205 static struct clk_plt_fixed *plt_clk_register_fixed_rate(struct platform_device *pdev,
206 						 const char *name,
207 						 const char *parent_name,
208 						 unsigned long fixed_rate)
209 {
210 	struct clk_plt_fixed *pclk;
211 
212 	pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
213 	if (!pclk)
214 		return ERR_PTR(-ENOMEM);
215 
216 	pclk->clk = clk_hw_register_fixed_rate(&pdev->dev, name, parent_name,
217 					       0, fixed_rate);
218 	if (IS_ERR(pclk->clk))
219 		return ERR_CAST(pclk->clk);
220 
221 	pclk->lookup = clkdev_hw_create(pclk->clk, name, NULL);
222 	if (!pclk->lookup) {
223 		clk_hw_unregister_fixed_rate(pclk->clk);
224 		return ERR_PTR(-ENOMEM);
225 	}
226 
227 	return pclk;
228 }
229 
230 static void plt_clk_unregister_fixed_rate(struct clk_plt_fixed *pclk)
231 {
232 	clkdev_drop(pclk->lookup);
233 	clk_hw_unregister_fixed_rate(pclk->clk);
234 }
235 
236 static void plt_clk_unregister_fixed_rate_loop(struct clk_plt_data *data,
237 					       unsigned int i)
238 {
239 	while (i--)
240 		plt_clk_unregister_fixed_rate(data->parents[i]);
241 }
242 
243 static void plt_clk_free_parent_names_loop(const char **parent_names,
244 					   unsigned int i)
245 {
246 	while (i--)
247 		kfree_const(parent_names[i]);
248 	kfree(parent_names);
249 }
250 
251 static void plt_clk_unregister_loop(struct clk_plt_data *data,
252 				    unsigned int i)
253 {
254 	while (i--)
255 		plt_clk_unregister(data->clks[i]);
256 }
257 
258 static const char **plt_clk_register_parents(struct platform_device *pdev,
259 					     struct clk_plt_data *data,
260 					     const struct pmc_clk *clks)
261 {
262 	const char **parent_names;
263 	unsigned int i;
264 	int err;
265 	int nparents = 0;
266 
267 	data->nparents = 0;
268 	while (clks[nparents].name)
269 		nparents++;
270 
271 	data->parents = devm_kcalloc(&pdev->dev, nparents,
272 				     sizeof(*data->parents), GFP_KERNEL);
273 	if (!data->parents)
274 		return ERR_PTR(-ENOMEM);
275 
276 	parent_names = kcalloc(nparents, sizeof(*parent_names),
277 			       GFP_KERNEL);
278 	if (!parent_names)
279 		return ERR_PTR(-ENOMEM);
280 
281 	for (i = 0; i < nparents; i++) {
282 		data->parents[i] =
283 			plt_clk_register_fixed_rate(pdev, clks[i].name,
284 						    clks[i].parent_name,
285 						    clks[i].freq);
286 		if (IS_ERR(data->parents[i])) {
287 			err = PTR_ERR(data->parents[i]);
288 			goto err_unreg;
289 		}
290 		parent_names[i] = kstrdup_const(clks[i].name, GFP_KERNEL);
291 	}
292 
293 	data->nparents = nparents;
294 	return parent_names;
295 
296 err_unreg:
297 	plt_clk_unregister_fixed_rate_loop(data, i);
298 	plt_clk_free_parent_names_loop(parent_names, i);
299 	return ERR_PTR(err);
300 }
301 
302 static void plt_clk_unregister_parents(struct clk_plt_data *data)
303 {
304 	plt_clk_unregister_fixed_rate_loop(data, data->nparents);
305 }
306 
307 static int plt_clk_probe(struct platform_device *pdev)
308 {
309 	const struct pmc_clk_data *pmc_data;
310 	const char **parent_names;
311 	struct clk_plt_data *data;
312 	unsigned int i;
313 	int err;
314 
315 	pmc_data = dev_get_platdata(&pdev->dev);
316 	if (!pmc_data || !pmc_data->clks)
317 		return -EINVAL;
318 
319 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
320 	if (!data)
321 		return -ENOMEM;
322 
323 	parent_names = plt_clk_register_parents(pdev, data, pmc_data->clks);
324 	if (IS_ERR(parent_names))
325 		return PTR_ERR(parent_names);
326 
327 	for (i = 0; i < PMC_CLK_NUM; i++) {
328 		data->clks[i] = plt_clk_register(pdev, i, pmc_data,
329 						 parent_names, data->nparents);
330 		if (IS_ERR(data->clks[i])) {
331 			err = PTR_ERR(data->clks[i]);
332 			goto err_unreg_clk_plt;
333 		}
334 	}
335 	data->mclk_lookup = clkdev_hw_create(&data->clks[3]->hw, "mclk", NULL);
336 	if (!data->mclk_lookup) {
337 		err = -ENOMEM;
338 		goto err_unreg_clk_plt;
339 	}
340 
341 	data->ether_clk_lookup = clkdev_hw_create(&data->clks[4]->hw,
342 						  "ether_clk", NULL);
343 	if (!data->ether_clk_lookup) {
344 		err = -ENOMEM;
345 		goto err_drop_mclk;
346 	}
347 
348 	plt_clk_free_parent_names_loop(parent_names, data->nparents);
349 
350 	platform_set_drvdata(pdev, data);
351 	return 0;
352 
353 err_drop_mclk:
354 	clkdev_drop(data->mclk_lookup);
355 err_unreg_clk_plt:
356 	plt_clk_unregister_loop(data, i);
357 	plt_clk_unregister_parents(data);
358 	plt_clk_free_parent_names_loop(parent_names, data->nparents);
359 	return err;
360 }
361 
362 static void plt_clk_remove(struct platform_device *pdev)
363 {
364 	struct clk_plt_data *data;
365 
366 	data = platform_get_drvdata(pdev);
367 
368 	clkdev_drop(data->ether_clk_lookup);
369 	clkdev_drop(data->mclk_lookup);
370 	plt_clk_unregister_loop(data, PMC_CLK_NUM);
371 	plt_clk_unregister_parents(data);
372 }
373 
374 static struct platform_driver plt_clk_driver = {
375 	.driver = {
376 		.name = "clk-pmc-atom",
377 	},
378 	.probe = plt_clk_probe,
379 	.remove = plt_clk_remove,
380 };
381 builtin_platform_driver(plt_clk_driver);
382