1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC |
4 | * |
5 | * Authors: |
6 | * Serge Semin <Sergey.Semin@baikalelectronics.ru> |
7 | * Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru> |
8 | * |
9 | * Baikal-T1 CCU PLL clocks driver |
10 | */ |
11 | |
12 | #define pr_fmt(fmt) "bt1-ccu-pll: " fmt |
13 | |
14 | #include <linux/kernel.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/printk.h> |
17 | #include <linux/slab.h> |
18 | #include <linux/clk-provider.h> |
19 | #include <linux/mfd/syscon.h> |
20 | #include <linux/of.h> |
21 | #include <linux/of_address.h> |
22 | #include <linux/ioport.h> |
23 | #include <linux/regmap.h> |
24 | |
25 | #include <dt-bindings/clock/bt1-ccu.h> |
26 | |
27 | #include "ccu-pll.h" |
28 | |
29 | #define CCU_CPU_PLL_BASE 0x000 |
30 | #define CCU_SATA_PLL_BASE 0x008 |
31 | #define CCU_DDR_PLL_BASE 0x010 |
32 | #define CCU_PCIE_PLL_BASE 0x018 |
33 | #define CCU_ETH_PLL_BASE 0x020 |
34 | |
35 | #define CCU_PLL_INFO(_id, _name, _pname, _base, _flags, _features) \ |
36 | { \ |
37 | .id = _id, \ |
38 | .name = _name, \ |
39 | .parent_name = _pname, \ |
40 | .base = _base, \ |
41 | .flags = _flags, \ |
42 | .features = _features, \ |
43 | } |
44 | |
45 | #define CCU_PLL_NUM ARRAY_SIZE(pll_info) |
46 | |
47 | struct ccu_pll_info { |
48 | unsigned int id; |
49 | const char *name; |
50 | const char *parent_name; |
51 | unsigned int base; |
52 | unsigned long flags; |
53 | unsigned long features; |
54 | }; |
55 | |
56 | /* |
57 | * Alas we have to mark all PLLs as critical. CPU and DDR PLLs are sources of |
58 | * CPU cores and DDR controller reference clocks, due to which they obviously |
59 | * shouldn't be ever gated. SATA and PCIe PLLs are the parents of APB-bus and |
60 | * DDR controller AXI-bus clocks. If they are gated the system will be |
61 | * unusable. Moreover disabling SATA and Ethernet PLLs causes automatic reset |
62 | * of the corresponding subsystems. So until we aren't ready to re-initialize |
63 | * all the devices consuming those PLLs, they will be marked as critical too. |
64 | */ |
65 | static const struct ccu_pll_info pll_info[] = { |
66 | CCU_PLL_INFO(CCU_CPU_PLL, "cpu_pll" , "ref_clk" , CCU_CPU_PLL_BASE, |
67 | CLK_IS_CRITICAL, CCU_PLL_BASIC), |
68 | CCU_PLL_INFO(CCU_SATA_PLL, "sata_pll" , "ref_clk" , CCU_SATA_PLL_BASE, |
69 | CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0), |
70 | CCU_PLL_INFO(CCU_DDR_PLL, "ddr_pll" , "ref_clk" , CCU_DDR_PLL_BASE, |
71 | CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0), |
72 | CCU_PLL_INFO(CCU_PCIE_PLL, "pcie_pll" , "ref_clk" , CCU_PCIE_PLL_BASE, |
73 | CLK_IS_CRITICAL, CCU_PLL_BASIC), |
74 | CCU_PLL_INFO(CCU_ETH_PLL, "eth_pll" , "ref_clk" , CCU_ETH_PLL_BASE, |
75 | CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0) |
76 | }; |
77 | |
78 | struct ccu_pll_data { |
79 | struct device_node *np; |
80 | struct regmap *sys_regs; |
81 | struct ccu_pll *plls[CCU_PLL_NUM]; |
82 | }; |
83 | |
84 | static struct ccu_pll_data *pll_data; |
85 | |
86 | static struct ccu_pll *ccu_pll_find_desc(struct ccu_pll_data *data, |
87 | unsigned int clk_id) |
88 | { |
89 | int idx; |
90 | |
91 | for (idx = 0; idx < CCU_PLL_NUM; ++idx) { |
92 | if (pll_info[idx].id == clk_id) |
93 | return data->plls[idx]; |
94 | } |
95 | |
96 | return ERR_PTR(error: -EINVAL); |
97 | } |
98 | |
99 | static struct ccu_pll_data *ccu_pll_create_data(struct device_node *np) |
100 | { |
101 | struct ccu_pll_data *data; |
102 | |
103 | data = kzalloc(size: sizeof(*data), GFP_KERNEL); |
104 | if (!data) |
105 | return ERR_PTR(error: -ENOMEM); |
106 | |
107 | data->np = np; |
108 | |
109 | return data; |
110 | } |
111 | |
112 | static void ccu_pll_free_data(struct ccu_pll_data *data) |
113 | { |
114 | kfree(objp: data); |
115 | } |
116 | |
117 | static int ccu_pll_find_sys_regs(struct ccu_pll_data *data) |
118 | { |
119 | data->sys_regs = syscon_node_to_regmap(np: data->np->parent); |
120 | if (IS_ERR(ptr: data->sys_regs)) { |
121 | pr_err("Failed to find syscon regs for '%s'\n" , |
122 | of_node_full_name(data->np)); |
123 | return PTR_ERR(ptr: data->sys_regs); |
124 | } |
125 | |
126 | return 0; |
127 | } |
128 | |
129 | static struct clk_hw *ccu_pll_of_clk_hw_get(struct of_phandle_args *clkspec, |
130 | void *priv) |
131 | { |
132 | struct ccu_pll_data *data = priv; |
133 | struct ccu_pll *pll; |
134 | unsigned int clk_id; |
135 | |
136 | clk_id = clkspec->args[0]; |
137 | pll = ccu_pll_find_desc(data, clk_id); |
138 | if (IS_ERR(ptr: pll)) { |
139 | if (pll != ERR_PTR(error: -EPROBE_DEFER)) |
140 | pr_info("Invalid PLL clock ID %d specified\n" , clk_id); |
141 | |
142 | return ERR_CAST(ptr: pll); |
143 | } |
144 | |
145 | return ccu_pll_get_clk_hw(pll); |
146 | } |
147 | |
148 | static int ccu_pll_clk_register(struct ccu_pll_data *data, bool defer) |
149 | { |
150 | int idx, ret; |
151 | |
152 | for (idx = 0; idx < CCU_PLL_NUM; ++idx) { |
153 | const struct ccu_pll_info *info = &pll_info[idx]; |
154 | struct ccu_pll_init_data init = {0}; |
155 | |
156 | /* Defer non-basic PLLs allocation for the probe stage */ |
157 | if (!!(info->features & CCU_PLL_BASIC) ^ defer) { |
158 | if (!data->plls[idx]) |
159 | data->plls[idx] = ERR_PTR(error: -EPROBE_DEFER); |
160 | |
161 | continue; |
162 | } |
163 | |
164 | init.id = info->id; |
165 | init.name = info->name; |
166 | init.parent_name = info->parent_name; |
167 | init.base = info->base; |
168 | init.sys_regs = data->sys_regs; |
169 | init.np = data->np; |
170 | init.flags = info->flags; |
171 | init.features = info->features; |
172 | |
173 | data->plls[idx] = ccu_pll_hw_register(init: &init); |
174 | if (IS_ERR(ptr: data->plls[idx])) { |
175 | ret = PTR_ERR(ptr: data->plls[idx]); |
176 | pr_err("Couldn't register PLL hw '%s'\n" , |
177 | init.name); |
178 | goto err_hw_unregister; |
179 | } |
180 | } |
181 | |
182 | return 0; |
183 | |
184 | err_hw_unregister: |
185 | for (--idx; idx >= 0; --idx) { |
186 | if (!!(pll_info[idx].features & CCU_PLL_BASIC) ^ defer) |
187 | continue; |
188 | |
189 | ccu_pll_hw_unregister(pll: data->plls[idx]); |
190 | } |
191 | |
192 | return ret; |
193 | } |
194 | |
195 | static void ccu_pll_clk_unregister(struct ccu_pll_data *data, bool defer) |
196 | { |
197 | int idx; |
198 | |
199 | /* Uninstall only the clocks registered on the specfied stage */ |
200 | for (idx = 0; idx < CCU_PLL_NUM; ++idx) { |
201 | if (!!(pll_info[idx].features & CCU_PLL_BASIC) ^ defer) |
202 | continue; |
203 | |
204 | ccu_pll_hw_unregister(pll: data->plls[idx]); |
205 | } |
206 | } |
207 | |
208 | static int ccu_pll_of_register(struct ccu_pll_data *data) |
209 | { |
210 | int ret; |
211 | |
212 | ret = of_clk_add_hw_provider(np: data->np, get: ccu_pll_of_clk_hw_get, data); |
213 | if (ret) { |
214 | pr_err("Couldn't register PLL provider of '%s'\n" , |
215 | of_node_full_name(data->np)); |
216 | } |
217 | |
218 | return ret; |
219 | } |
220 | |
221 | static int ccu_pll_probe(struct platform_device *pdev) |
222 | { |
223 | struct ccu_pll_data *data = pll_data; |
224 | |
225 | if (!data) |
226 | return -EINVAL; |
227 | |
228 | return ccu_pll_clk_register(data, defer: false); |
229 | } |
230 | |
231 | static const struct of_device_id ccu_pll_of_match[] = { |
232 | { .compatible = "baikal,bt1-ccu-pll" }, |
233 | { } |
234 | }; |
235 | |
236 | static struct platform_driver ccu_pll_driver = { |
237 | .probe = ccu_pll_probe, |
238 | .driver = { |
239 | .name = "clk-ccu-pll" , |
240 | .of_match_table = ccu_pll_of_match, |
241 | .suppress_bind_attrs = true, |
242 | }, |
243 | }; |
244 | builtin_platform_driver(ccu_pll_driver); |
245 | |
246 | static __init void ccu_pll_init(struct device_node *np) |
247 | { |
248 | struct ccu_pll_data *data; |
249 | int ret; |
250 | |
251 | data = ccu_pll_create_data(np); |
252 | if (IS_ERR(ptr: data)) |
253 | return; |
254 | |
255 | ret = ccu_pll_find_sys_regs(data); |
256 | if (ret) |
257 | goto err_free_data; |
258 | |
259 | ret = ccu_pll_clk_register(data, defer: true); |
260 | if (ret) |
261 | goto err_free_data; |
262 | |
263 | ret = ccu_pll_of_register(data); |
264 | if (ret) |
265 | goto err_clk_unregister; |
266 | |
267 | pll_data = data; |
268 | |
269 | return; |
270 | |
271 | err_clk_unregister: |
272 | ccu_pll_clk_unregister(data, defer: true); |
273 | |
274 | err_free_data: |
275 | ccu_pll_free_data(data); |
276 | } |
277 | CLK_OF_DECLARE_DRIVER(ccu_pll, "baikal,bt1-ccu-pll" , ccu_pll_init); |
278 | |