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