xref: /linux/drivers/clk/renesas/clk-mstp.c (revision 9ab27b018649c9504e894496cb4d7d8afcffd897)
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>
1309c32427SSimon Horman #include <linux/clk/renesas.h>
14b3a33077SSimon Horman #include <linux/device.h>
15b3a33077SSimon Horman #include <linux/io.h>
16d0414e76SGeert Uytterhoeven #include <linux/iopoll.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>
214ae2c995SGeert Uytterhoeven #include <linux/slab.h>
22b3a33077SSimon Horman #include <linux/spinlock.h>
23b3a33077SSimon Horman 
24b3a33077SSimon Horman /*
25b3a33077SSimon Horman  * MSTP clocks. We can't use standard gate clocks as we need to poll on the
26b3a33077SSimon Horman  * status register when enabling the clock.
27b3a33077SSimon Horman  */
28b3a33077SSimon Horman 
29b3a33077SSimon Horman #define MSTP_MAX_CLOCKS		32
30b3a33077SSimon Horman 
31b3a33077SSimon Horman /**
32b3a33077SSimon Horman  * struct mstp_clock_group - MSTP gating clocks group
33b3a33077SSimon Horman  *
34a79f5836SGeert Uytterhoeven  * @data: clock specifier translation for clocks in this group
35b3a33077SSimon Horman  * @smstpcr: module stop control register
36b3a33077SSimon Horman  * @mstpsr: module stop status register (optional)
37b3a33077SSimon Horman  * @lock: protects writes to SMSTPCR
38e2a33c34SChris Brandt  * @width_8bit: registers are 8-bit, not 32-bit
39a79f5836SGeert Uytterhoeven  * @clks: clocks in this group
40b3a33077SSimon Horman  */
41b3a33077SSimon Horman struct mstp_clock_group {
42b3a33077SSimon Horman 	struct clk_onecell_data data;
43b3a33077SSimon Horman 	void __iomem *smstpcr;
44b3a33077SSimon Horman 	void __iomem *mstpsr;
45b3a33077SSimon Horman 	spinlock_t lock;
46e2a33c34SChris Brandt 	bool width_8bit;
47a79f5836SGeert Uytterhoeven 	struct clk *clks[];
48b3a33077SSimon Horman };
49b3a33077SSimon Horman 
50b3a33077SSimon Horman /**
51b3a33077SSimon Horman  * struct mstp_clock - MSTP gating clock
52b3a33077SSimon Horman  * @hw: handle between common and hardware-specific interfaces
53b3a33077SSimon Horman  * @bit_index: control bit index
54b3a33077SSimon Horman  * @group: MSTP clocks group
55b3a33077SSimon Horman  */
56b3a33077SSimon Horman struct mstp_clock {
57b3a33077SSimon Horman 	struct clk_hw hw;
58b3a33077SSimon Horman 	u32 bit_index;
59b3a33077SSimon Horman 	struct mstp_clock_group *group;
60b3a33077SSimon Horman };
61b3a33077SSimon Horman 
62b3a33077SSimon Horman #define to_mstp_clock(_hw) container_of(_hw, struct mstp_clock, hw)
63b3a33077SSimon Horman 
64e2a33c34SChris Brandt static inline u32 cpg_mstp_read(struct mstp_clock_group *group,
65e2a33c34SChris Brandt 				u32 __iomem *reg)
66e2a33c34SChris Brandt {
6780275198SGeert Uytterhoeven 	return group->width_8bit ? readb(reg) : readl(reg);
68e2a33c34SChris Brandt }
69e2a33c34SChris Brandt 
70e2a33c34SChris Brandt static inline void cpg_mstp_write(struct mstp_clock_group *group, u32 val,
71e2a33c34SChris Brandt 				  u32 __iomem *reg)
72e2a33c34SChris Brandt {
7380275198SGeert Uytterhoeven 	group->width_8bit ? writeb(val, reg) : writel(val, reg);
74e2a33c34SChris Brandt }
75e2a33c34SChris Brandt 
76b3a33077SSimon Horman static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
77b3a33077SSimon Horman {
78b3a33077SSimon Horman 	struct mstp_clock *clock = to_mstp_clock(hw);
79b3a33077SSimon Horman 	struct mstp_clock_group *group = clock->group;
80b3a33077SSimon Horman 	u32 bitmask = BIT(clock->bit_index);
81b3a33077SSimon Horman 	unsigned long flags;
82b3a33077SSimon Horman 	u32 value;
83d0414e76SGeert Uytterhoeven 	int ret;
84b3a33077SSimon Horman 
85b3a33077SSimon Horman 	spin_lock_irqsave(&group->lock, flags);
86b3a33077SSimon Horman 
87e2a33c34SChris Brandt 	value = cpg_mstp_read(group, group->smstpcr);
88b3a33077SSimon Horman 	if (enable)
89b3a33077SSimon Horman 		value &= ~bitmask;
90b3a33077SSimon Horman 	else
91b3a33077SSimon Horman 		value |= bitmask;
92e2a33c34SChris Brandt 	cpg_mstp_write(group, value, group->smstpcr);
93b3a33077SSimon Horman 
94f59de563SChris Brandt 	if (!group->mstpsr) {
95f59de563SChris Brandt 		/* dummy read to ensure write has completed */
96f59de563SChris Brandt 		cpg_mstp_read(group, group->smstpcr);
97f59de563SChris Brandt 		barrier_data(group->smstpcr);
98f59de563SChris Brandt 	}
99f59de563SChris Brandt 
100b3a33077SSimon Horman 	spin_unlock_irqrestore(&group->lock, flags);
101b3a33077SSimon Horman 
102b3a33077SSimon Horman 	if (!enable || !group->mstpsr)
103b3a33077SSimon Horman 		return 0;
104b3a33077SSimon Horman 
105d0414e76SGeert Uytterhoeven 	/* group->width_8bit is always false if group->mstpsr is present */
106d0414e76SGeert Uytterhoeven 	ret = readl_poll_timeout_atomic(group->mstpsr, value,
107d0414e76SGeert Uytterhoeven 					!(value & bitmask), 0, 10);
108d0414e76SGeert Uytterhoeven 	if (ret)
109b3a33077SSimon Horman 		pr_err("%s: failed to enable %p[%d]\n", __func__,
110b3a33077SSimon Horman 		       group->smstpcr, clock->bit_index);
111b3a33077SSimon Horman 
112d0414e76SGeert Uytterhoeven 	return ret;
113b3a33077SSimon Horman }
114b3a33077SSimon Horman 
115b3a33077SSimon Horman static int cpg_mstp_clock_enable(struct clk_hw *hw)
116b3a33077SSimon Horman {
117b3a33077SSimon Horman 	return cpg_mstp_clock_endisable(hw, true);
118b3a33077SSimon Horman }
119b3a33077SSimon Horman 
120b3a33077SSimon Horman static void cpg_mstp_clock_disable(struct clk_hw *hw)
121b3a33077SSimon Horman {
122b3a33077SSimon Horman 	cpg_mstp_clock_endisable(hw, false);
123b3a33077SSimon Horman }
124b3a33077SSimon Horman 
125b3a33077SSimon Horman static int cpg_mstp_clock_is_enabled(struct clk_hw *hw)
126b3a33077SSimon Horman {
127b3a33077SSimon Horman 	struct mstp_clock *clock = to_mstp_clock(hw);
128b3a33077SSimon Horman 	struct mstp_clock_group *group = clock->group;
129b3a33077SSimon Horman 	u32 value;
130b3a33077SSimon Horman 
131b3a33077SSimon Horman 	if (group->mstpsr)
132e2a33c34SChris Brandt 		value = cpg_mstp_read(group, group->mstpsr);
133b3a33077SSimon Horman 	else
134e2a33c34SChris Brandt 		value = cpg_mstp_read(group, group->smstpcr);
135b3a33077SSimon Horman 
136b3a33077SSimon Horman 	return !(value & BIT(clock->bit_index));
137b3a33077SSimon Horman }
138b3a33077SSimon Horman 
139b3a33077SSimon Horman static const struct clk_ops cpg_mstp_clock_ops = {
140b3a33077SSimon Horman 	.enable = cpg_mstp_clock_enable,
141b3a33077SSimon Horman 	.disable = cpg_mstp_clock_disable,
142b3a33077SSimon Horman 	.is_enabled = cpg_mstp_clock_is_enabled,
143b3a33077SSimon Horman };
144b3a33077SSimon Horman 
1451ce87dd2SGeert Uytterhoeven static struct clk * __init cpg_mstp_clock_register(const char *name,
1461ce87dd2SGeert Uytterhoeven 	const char *parent_name, unsigned int index,
1471ce87dd2SGeert Uytterhoeven 	struct mstp_clock_group *group)
148b3a33077SSimon Horman {
149f2fb4fe6SGeert Uytterhoeven 	struct clk_init_data init = {};
150b3a33077SSimon Horman 	struct mstp_clock *clock;
151b3a33077SSimon Horman 	struct clk *clk;
152b3a33077SSimon Horman 
153b3a33077SSimon Horman 	clock = kzalloc(sizeof(*clock), GFP_KERNEL);
154168bcbcbSMarkus Elfring 	if (!clock)
155b3a33077SSimon Horman 		return ERR_PTR(-ENOMEM);
156b3a33077SSimon Horman 
157b3a33077SSimon Horman 	init.name = name;
158b3a33077SSimon Horman 	init.ops = &cpg_mstp_clock_ops;
159ddbae665SStephen Boyd 	init.flags = CLK_SET_RATE_PARENT;
160e34084fbSGeert Uytterhoeven 	/* INTC-SYS is the module clock of the GIC, and must not be disabled */
161e34084fbSGeert Uytterhoeven 	if (!strcmp(name, "intc-sys")) {
162e34084fbSGeert Uytterhoeven 		pr_debug("MSTP %s setting CLK_IS_CRITICAL\n", name);
163e34084fbSGeert Uytterhoeven 		init.flags |= CLK_IS_CRITICAL;
164e34084fbSGeert Uytterhoeven 	}
165b3a33077SSimon Horman 	init.parent_names = &parent_name;
166b3a33077SSimon Horman 	init.num_parents = 1;
167b3a33077SSimon Horman 
168b3a33077SSimon Horman 	clock->bit_index = index;
169b3a33077SSimon Horman 	clock->group = group;
170b3a33077SSimon Horman 	clock->hw.init = &init;
171b3a33077SSimon Horman 
172b3a33077SSimon Horman 	clk = clk_register(NULL, &clock->hw);
173b3a33077SSimon Horman 
174b3a33077SSimon Horman 	if (IS_ERR(clk))
175b3a33077SSimon Horman 		kfree(clock);
176b3a33077SSimon Horman 
177b3a33077SSimon Horman 	return clk;
178b3a33077SSimon Horman }
179b3a33077SSimon Horman 
180b3a33077SSimon Horman static void __init cpg_mstp_clocks_init(struct device_node *np)
181b3a33077SSimon Horman {
182b3a33077SSimon Horman 	struct mstp_clock_group *group;
183b3a33077SSimon Horman 	const char *idxname;
184b3a33077SSimon Horman 	struct clk **clks;
185b3a33077SSimon Horman 	unsigned int i;
186b3a33077SSimon Horman 
187a79f5836SGeert Uytterhoeven 	group = kzalloc(struct_size(group, clks, MSTP_MAX_CLOCKS), GFP_KERNEL);
188ba28236eSMarkus Elfring 	if (!group)
189b3a33077SSimon Horman 		return;
190b3a33077SSimon Horman 
191a79f5836SGeert Uytterhoeven 	clks = group->clks;
192b3a33077SSimon Horman 	spin_lock_init(&group->lock);
193b3a33077SSimon Horman 	group->data.clks = clks;
194b3a33077SSimon Horman 
195b3a33077SSimon Horman 	group->smstpcr = of_iomap(np, 0);
196b3a33077SSimon Horman 	group->mstpsr = of_iomap(np, 1);
197b3a33077SSimon Horman 
198b3a33077SSimon Horman 	if (group->smstpcr == NULL) {
199b3a33077SSimon Horman 		pr_err("%s: failed to remap SMSTPCR\n", __func__);
200b3a33077SSimon Horman 		kfree(group);
201b3a33077SSimon Horman 		return;
202b3a33077SSimon Horman 	}
203b3a33077SSimon Horman 
204e2a33c34SChris Brandt 	if (of_device_is_compatible(np, "renesas,r7s72100-mstp-clocks"))
205e2a33c34SChris Brandt 		group->width_8bit = true;
206e2a33c34SChris Brandt 
207b3a33077SSimon Horman 	for (i = 0; i < MSTP_MAX_CLOCKS; ++i)
208b3a33077SSimon Horman 		clks[i] = ERR_PTR(-ENOENT);
209b3a33077SSimon Horman 
210*66b06523SRob Herring (Arm) 	if (of_property_present(np, "clock-indices"))
211b3a33077SSimon Horman 		idxname = "clock-indices";
212b3a33077SSimon Horman 	else
213b3a33077SSimon Horman 		idxname = "renesas,clock-indices";
214b3a33077SSimon Horman 
215b3a33077SSimon Horman 	for (i = 0; i < MSTP_MAX_CLOCKS; ++i) {
216b3a33077SSimon Horman 		const char *parent_name;
217b3a33077SSimon Horman 		const char *name;
218b3a33077SSimon Horman 		u32 clkidx;
219b3a33077SSimon Horman 		int ret;
220b3a33077SSimon Horman 
221b3a33077SSimon Horman 		/* Skip clocks with no name. */
222b3a33077SSimon Horman 		ret = of_property_read_string_index(np, "clock-output-names",
223b3a33077SSimon Horman 						    i, &name);
224b3a33077SSimon Horman 		if (ret < 0 || strlen(name) == 0)
225b3a33077SSimon Horman 			continue;
226b3a33077SSimon Horman 
227b3a33077SSimon Horman 		parent_name = of_clk_get_parent_name(np, i);
228b3a33077SSimon Horman 		ret = of_property_read_u32_index(np, idxname, i, &clkidx);
229b3a33077SSimon Horman 		if (parent_name == NULL || ret < 0)
230b3a33077SSimon Horman 			break;
231b3a33077SSimon Horman 
232b3a33077SSimon Horman 		if (clkidx >= MSTP_MAX_CLOCKS) {
233e665f029SRob Herring 			pr_err("%s: invalid clock %pOFn %s index %u\n",
234e665f029SRob Herring 			       __func__, np, name, clkidx);
235b3a33077SSimon Horman 			continue;
236b3a33077SSimon Horman 		}
237b3a33077SSimon Horman 
238b3a33077SSimon Horman 		clks[clkidx] = cpg_mstp_clock_register(name, parent_name,
239b3a33077SSimon Horman 						       clkidx, group);
2404ae2c995SGeert Uytterhoeven 		if (!IS_ERR(clks[clkidx]))
241b3a33077SSimon Horman 			group->data.clk_num = max(group->data.clk_num,
242b3a33077SSimon Horman 						  clkidx + 1);
2434ae2c995SGeert Uytterhoeven 		else
244e665f029SRob Herring 			pr_err("%s: failed to register %pOFn %s clock (%ld)\n",
245e665f029SRob Herring 			       __func__, np, name, PTR_ERR(clks[clkidx]));
246b3a33077SSimon Horman 	}
247b3a33077SSimon Horman 
248b3a33077SSimon Horman 	of_clk_add_provider(np, of_clk_src_onecell_get, &group->data);
249b3a33077SSimon Horman }
250b3a33077SSimon Horman CLK_OF_DECLARE(cpg_mstp_clks, "renesas,cpg-mstp-clocks", cpg_mstp_clocks_init);
251b3a33077SSimon Horman 
25212a56817SGeert Uytterhoeven int cpg_mstp_attach_dev(struct generic_pm_domain *unused, struct device *dev)
253b3a33077SSimon Horman {
254b3a33077SSimon Horman 	struct device_node *np = dev->of_node;
255b3a33077SSimon Horman 	struct of_phandle_args clkspec;
256b3a33077SSimon Horman 	struct clk *clk;
257b3a33077SSimon Horman 	int i = 0;
258b3a33077SSimon Horman 	int error;
259b3a33077SSimon Horman 
260b3a33077SSimon Horman 	while (!of_parse_phandle_with_args(np, "clocks", "#clock-cells", i,
261b3a33077SSimon Horman 					   &clkspec)) {
262b3a33077SSimon Horman 		if (of_device_is_compatible(clkspec.np,
263b3a33077SSimon Horman 					    "renesas,cpg-mstp-clocks"))
264b3a33077SSimon Horman 			goto found;
265b3a33077SSimon Horman 
266b3a33077SSimon Horman 		/* BSC on r8a73a4/sh73a0 uses zb_clk instead of an mstp clock */
26787ab1151SRob Herring 		if (of_node_name_eq(clkspec.np, "zb_clk"))
268b3a33077SSimon Horman 			goto found;
269b3a33077SSimon Horman 
270b3a33077SSimon Horman 		of_node_put(clkspec.np);
271b3a33077SSimon Horman 		i++;
272b3a33077SSimon Horman 	}
273b3a33077SSimon Horman 
274b3a33077SSimon Horman 	return 0;
275b3a33077SSimon Horman 
276b3a33077SSimon Horman found:
277b3a33077SSimon Horman 	clk = of_clk_get_from_provider(&clkspec);
278b3a33077SSimon Horman 	of_node_put(clkspec.np);
279b3a33077SSimon Horman 
280b3a33077SSimon Horman 	if (IS_ERR(clk))
281b3a33077SSimon Horman 		return PTR_ERR(clk);
282b3a33077SSimon Horman 
283b3a33077SSimon Horman 	error = pm_clk_create(dev);
2840f7ece0dSGeert Uytterhoeven 	if (error)
285b3a33077SSimon Horman 		goto fail_put;
286b3a33077SSimon Horman 
287b3a33077SSimon Horman 	error = pm_clk_add_clk(dev, clk);
2880f7ece0dSGeert Uytterhoeven 	if (error)
289b3a33077SSimon Horman 		goto fail_destroy;
290b3a33077SSimon Horman 
291b3a33077SSimon Horman 	return 0;
292b3a33077SSimon Horman 
293b3a33077SSimon Horman fail_destroy:
294b3a33077SSimon Horman 	pm_clk_destroy(dev);
295b3a33077SSimon Horman fail_put:
296b3a33077SSimon Horman 	clk_put(clk);
297b3a33077SSimon Horman 	return error;
298b3a33077SSimon Horman }
299b3a33077SSimon Horman 
30012a56817SGeert Uytterhoeven void cpg_mstp_detach_dev(struct generic_pm_domain *unused, struct device *dev)
301b3a33077SSimon Horman {
302e05e853eSGeert Uytterhoeven 	if (!pm_clk_no_clocks(dev))
303b3a33077SSimon Horman 		pm_clk_destroy(dev);
304b3a33077SSimon Horman }
305b3a33077SSimon Horman 
306b3a33077SSimon Horman void __init cpg_mstp_add_clk_domain(struct device_node *np)
307b3a33077SSimon Horman {
308b3a33077SSimon Horman 	struct generic_pm_domain *pd;
309b3a33077SSimon Horman 	u32 ncells;
310b3a33077SSimon Horman 
311b3a33077SSimon Horman 	if (of_property_read_u32(np, "#power-domain-cells", &ncells)) {
31216673931SRob Herring 		pr_warn("%pOF lacks #power-domain-cells\n", np);
313b3a33077SSimon Horman 		return;
314b3a33077SSimon Horman 	}
315b3a33077SSimon Horman 
316b3a33077SSimon Horman 	pd = kzalloc(sizeof(*pd), GFP_KERNEL);
317b3a33077SSimon Horman 	if (!pd)
318b3a33077SSimon Horman 		return;
319b3a33077SSimon Horman 
320b3a33077SSimon Horman 	pd->name = np->name;
321a459a184SGeert Uytterhoeven 	pd->flags = GENPD_FLAG_PM_CLK | GENPD_FLAG_ALWAYS_ON |
322a459a184SGeert Uytterhoeven 		    GENPD_FLAG_ACTIVE_WAKEUP;
323b3a33077SSimon Horman 	pd->attach_dev = cpg_mstp_attach_dev;
324b3a33077SSimon Horman 	pd->detach_dev = cpg_mstp_detach_dev;
32520729300SGeert Uytterhoeven 	pm_genpd_init(pd, &pm_domain_always_on_gov, false);
326b3a33077SSimon Horman 
327b3a33077SSimon Horman 	of_genpd_add_provider_simple(np, pd);
328b3a33077SSimon Horman }
329