1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * R-Car MSTP clocks |
4 | * |
5 | * Copyright (C) 2013 Ideas On Board SPRL |
6 | * Copyright (C) 2015 Glider bvba |
7 | * |
8 | * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> |
9 | */ |
10 | |
11 | #include <linux/clk.h> |
12 | #include <linux/clk-provider.h> |
13 | #include <linux/clkdev.h> |
14 | #include <linux/clk/renesas.h> |
15 | #include <linux/device.h> |
16 | #include <linux/io.h> |
17 | #include <linux/iopoll.h> |
18 | #include <linux/of.h> |
19 | #include <linux/of_address.h> |
20 | #include <linux/pm_clock.h> |
21 | #include <linux/pm_domain.h> |
22 | #include <linux/spinlock.h> |
23 | |
24 | /* |
25 | * MSTP clocks. We can't use standard gate clocks as we need to poll on the |
26 | * status register when enabling the clock. |
27 | */ |
28 | |
29 | #define MSTP_MAX_CLOCKS 32 |
30 | |
31 | /** |
32 | * struct mstp_clock_group - MSTP gating clocks group |
33 | * |
34 | * @data: clock specifier translation for clocks in this group |
35 | * @smstpcr: module stop control register |
36 | * @mstpsr: module stop status register (optional) |
37 | * @lock: protects writes to SMSTPCR |
38 | * @width_8bit: registers are 8-bit, not 32-bit |
39 | * @clks: clocks in this group |
40 | */ |
41 | struct mstp_clock_group { |
42 | struct clk_onecell_data data; |
43 | void __iomem *smstpcr; |
44 | void __iomem *mstpsr; |
45 | spinlock_t lock; |
46 | bool width_8bit; |
47 | struct clk *clks[]; |
48 | }; |
49 | |
50 | /** |
51 | * struct mstp_clock - MSTP gating clock |
52 | * @hw: handle between common and hardware-specific interfaces |
53 | * @bit_index: control bit index |
54 | * @group: MSTP clocks group |
55 | */ |
56 | struct mstp_clock { |
57 | struct clk_hw hw; |
58 | u32 bit_index; |
59 | struct mstp_clock_group *group; |
60 | }; |
61 | |
62 | #define to_mstp_clock(_hw) container_of(_hw, struct mstp_clock, hw) |
63 | |
64 | static inline u32 cpg_mstp_read(struct mstp_clock_group *group, |
65 | u32 __iomem *reg) |
66 | { |
67 | return group->width_8bit ? readb(addr: reg) : readl(addr: reg); |
68 | } |
69 | |
70 | static inline void cpg_mstp_write(struct mstp_clock_group *group, u32 val, |
71 | u32 __iomem *reg) |
72 | { |
73 | group->width_8bit ? writeb(val, addr: reg) : writel(val, addr: reg); |
74 | } |
75 | |
76 | static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) |
77 | { |
78 | struct mstp_clock *clock = to_mstp_clock(hw); |
79 | struct mstp_clock_group *group = clock->group; |
80 | u32 bitmask = BIT(clock->bit_index); |
81 | unsigned long flags; |
82 | u32 value; |
83 | int ret; |
84 | |
85 | spin_lock_irqsave(&group->lock, flags); |
86 | |
87 | value = cpg_mstp_read(group, reg: group->smstpcr); |
88 | if (enable) |
89 | value &= ~bitmask; |
90 | else |
91 | value |= bitmask; |
92 | cpg_mstp_write(group, val: value, reg: group->smstpcr); |
93 | |
94 | if (!group->mstpsr) { |
95 | /* dummy read to ensure write has completed */ |
96 | cpg_mstp_read(group, reg: group->smstpcr); |
97 | barrier_data(group->smstpcr); |
98 | } |
99 | |
100 | spin_unlock_irqrestore(lock: &group->lock, flags); |
101 | |
102 | if (!enable || !group->mstpsr) |
103 | return 0; |
104 | |
105 | /* group->width_8bit is always false if group->mstpsr is present */ |
106 | ret = readl_poll_timeout_atomic(group->mstpsr, value, |
107 | !(value & bitmask), 0, 10); |
108 | if (ret) |
109 | pr_err("%s: failed to enable %p[%d]\n" , __func__, |
110 | group->smstpcr, clock->bit_index); |
111 | |
112 | return ret; |
113 | } |
114 | |
115 | static int cpg_mstp_clock_enable(struct clk_hw *hw) |
116 | { |
117 | return cpg_mstp_clock_endisable(hw, enable: true); |
118 | } |
119 | |
120 | static void cpg_mstp_clock_disable(struct clk_hw *hw) |
121 | { |
122 | cpg_mstp_clock_endisable(hw, enable: false); |
123 | } |
124 | |
125 | static int cpg_mstp_clock_is_enabled(struct clk_hw *hw) |
126 | { |
127 | struct mstp_clock *clock = to_mstp_clock(hw); |
128 | struct mstp_clock_group *group = clock->group; |
129 | u32 value; |
130 | |
131 | if (group->mstpsr) |
132 | value = cpg_mstp_read(group, reg: group->mstpsr); |
133 | else |
134 | value = cpg_mstp_read(group, reg: group->smstpcr); |
135 | |
136 | return !(value & BIT(clock->bit_index)); |
137 | } |
138 | |
139 | static const struct clk_ops cpg_mstp_clock_ops = { |
140 | .enable = cpg_mstp_clock_enable, |
141 | .disable = cpg_mstp_clock_disable, |
142 | .is_enabled = cpg_mstp_clock_is_enabled, |
143 | }; |
144 | |
145 | static struct clk * __init cpg_mstp_clock_register(const char *name, |
146 | const char *parent_name, unsigned int index, |
147 | struct mstp_clock_group *group) |
148 | { |
149 | struct clk_init_data init = {}; |
150 | struct mstp_clock *clock; |
151 | struct clk *clk; |
152 | |
153 | clock = kzalloc(size: sizeof(*clock), GFP_KERNEL); |
154 | if (!clock) |
155 | return ERR_PTR(error: -ENOMEM); |
156 | |
157 | init.name = name; |
158 | init.ops = &cpg_mstp_clock_ops; |
159 | init.flags = CLK_SET_RATE_PARENT; |
160 | /* INTC-SYS is the module clock of the GIC, and must not be disabled */ |
161 | if (!strcmp(name, "intc-sys" )) { |
162 | pr_debug("MSTP %s setting CLK_IS_CRITICAL\n" , name); |
163 | init.flags |= CLK_IS_CRITICAL; |
164 | } |
165 | init.parent_names = &parent_name; |
166 | init.num_parents = 1; |
167 | |
168 | clock->bit_index = index; |
169 | clock->group = group; |
170 | clock->hw.init = &init; |
171 | |
172 | clk = clk_register(NULL, hw: &clock->hw); |
173 | |
174 | if (IS_ERR(ptr: clk)) |
175 | kfree(objp: clock); |
176 | |
177 | return clk; |
178 | } |
179 | |
180 | static void __init cpg_mstp_clocks_init(struct device_node *np) |
181 | { |
182 | struct mstp_clock_group *group; |
183 | const char *idxname; |
184 | struct clk **clks; |
185 | unsigned int i; |
186 | |
187 | group = kzalloc(struct_size(group, clks, MSTP_MAX_CLOCKS), GFP_KERNEL); |
188 | if (!group) |
189 | return; |
190 | |
191 | clks = group->clks; |
192 | spin_lock_init(&group->lock); |
193 | group->data.clks = clks; |
194 | |
195 | group->smstpcr = of_iomap(node: np, index: 0); |
196 | group->mstpsr = of_iomap(node: np, index: 1); |
197 | |
198 | if (group->smstpcr == NULL) { |
199 | pr_err("%s: failed to remap SMSTPCR\n" , __func__); |
200 | kfree(objp: group); |
201 | return; |
202 | } |
203 | |
204 | if (of_device_is_compatible(device: np, "renesas,r7s72100-mstp-clocks" )) |
205 | group->width_8bit = true; |
206 | |
207 | for (i = 0; i < MSTP_MAX_CLOCKS; ++i) |
208 | clks[i] = ERR_PTR(error: -ENOENT); |
209 | |
210 | if (of_find_property(np, name: "clock-indices" , lenp: &i)) |
211 | idxname = "clock-indices" ; |
212 | else |
213 | idxname = "renesas,clock-indices" ; |
214 | |
215 | for (i = 0; i < MSTP_MAX_CLOCKS; ++i) { |
216 | const char *parent_name; |
217 | const char *name; |
218 | u32 clkidx; |
219 | int ret; |
220 | |
221 | /* Skip clocks with no name. */ |
222 | ret = of_property_read_string_index(np, propname: "clock-output-names" , |
223 | index: i, output: &name); |
224 | if (ret < 0 || strlen(name) == 0) |
225 | continue; |
226 | |
227 | parent_name = of_clk_get_parent_name(np, index: i); |
228 | ret = of_property_read_u32_index(np, propname: idxname, index: i, out_value: &clkidx); |
229 | if (parent_name == NULL || ret < 0) |
230 | break; |
231 | |
232 | if (clkidx >= MSTP_MAX_CLOCKS) { |
233 | pr_err("%s: invalid clock %pOFn %s index %u\n" , |
234 | __func__, np, name, clkidx); |
235 | continue; |
236 | } |
237 | |
238 | clks[clkidx] = cpg_mstp_clock_register(name, parent_name, |
239 | index: clkidx, group); |
240 | if (!IS_ERR(ptr: clks[clkidx])) { |
241 | group->data.clk_num = max(group->data.clk_num, |
242 | clkidx + 1); |
243 | /* |
244 | * Register a clkdev to let board code retrieve the |
245 | * clock by name and register aliases for non-DT |
246 | * devices. |
247 | * |
248 | * FIXME: Remove this when all devices that require a |
249 | * clock will be instantiated from DT. |
250 | */ |
251 | clk_register_clkdev(clks[clkidx], name, NULL); |
252 | } else { |
253 | pr_err("%s: failed to register %pOFn %s clock (%ld)\n" , |
254 | __func__, np, name, PTR_ERR(clks[clkidx])); |
255 | } |
256 | } |
257 | |
258 | of_clk_add_provider(np, clk_src_get: of_clk_src_onecell_get, data: &group->data); |
259 | } |
260 | CLK_OF_DECLARE(cpg_mstp_clks, "renesas,cpg-mstp-clocks" , cpg_mstp_clocks_init); |
261 | |
262 | int cpg_mstp_attach_dev(struct generic_pm_domain *unused, struct device *dev) |
263 | { |
264 | struct device_node *np = dev->of_node; |
265 | struct of_phandle_args clkspec; |
266 | struct clk *clk; |
267 | int i = 0; |
268 | int error; |
269 | |
270 | while (!of_parse_phandle_with_args(np, list_name: "clocks" , cells_name: "#clock-cells" , index: i, |
271 | out_args: &clkspec)) { |
272 | if (of_device_is_compatible(device: clkspec.np, |
273 | "renesas,cpg-mstp-clocks" )) |
274 | goto found; |
275 | |
276 | /* BSC on r8a73a4/sh73a0 uses zb_clk instead of an mstp clock */ |
277 | if (of_node_name_eq(np: clkspec.np, name: "zb_clk" )) |
278 | goto found; |
279 | |
280 | of_node_put(node: clkspec.np); |
281 | i++; |
282 | } |
283 | |
284 | return 0; |
285 | |
286 | found: |
287 | clk = of_clk_get_from_provider(clkspec: &clkspec); |
288 | of_node_put(node: clkspec.np); |
289 | |
290 | if (IS_ERR(ptr: clk)) |
291 | return PTR_ERR(ptr: clk); |
292 | |
293 | error = pm_clk_create(dev); |
294 | if (error) |
295 | goto fail_put; |
296 | |
297 | error = pm_clk_add_clk(dev, clk); |
298 | if (error) |
299 | goto fail_destroy; |
300 | |
301 | return 0; |
302 | |
303 | fail_destroy: |
304 | pm_clk_destroy(dev); |
305 | fail_put: |
306 | clk_put(clk); |
307 | return error; |
308 | } |
309 | |
310 | void cpg_mstp_detach_dev(struct generic_pm_domain *unused, struct device *dev) |
311 | { |
312 | if (!pm_clk_no_clocks(dev)) |
313 | pm_clk_destroy(dev); |
314 | } |
315 | |
316 | void __init cpg_mstp_add_clk_domain(struct device_node *np) |
317 | { |
318 | struct generic_pm_domain *pd; |
319 | u32 ncells; |
320 | |
321 | if (of_property_read_u32(np, propname: "#power-domain-cells" , out_value: &ncells)) { |
322 | pr_warn("%pOF lacks #power-domain-cells\n" , np); |
323 | return; |
324 | } |
325 | |
326 | pd = kzalloc(size: sizeof(*pd), GFP_KERNEL); |
327 | if (!pd) |
328 | return; |
329 | |
330 | pd->name = np->name; |
331 | pd->flags = GENPD_FLAG_PM_CLK | GENPD_FLAG_ALWAYS_ON | |
332 | GENPD_FLAG_ACTIVE_WAKEUP; |
333 | pd->attach_dev = cpg_mstp_attach_dev; |
334 | pd->detach_dev = cpg_mstp_detach_dev; |
335 | pm_genpd_init(genpd: pd, gov: &pm_domain_always_on_gov, is_off: false); |
336 | |
337 | of_genpd_add_provider_simple(np, genpd: pd); |
338 | } |
339 | |