xref: /openbmc/linux/drivers/clk/davinci/psc.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1c6ed4d73SDavid Lechner // SPDX-License-Identifier: GPL-2.0
2c6ed4d73SDavid Lechner /*
3c6ed4d73SDavid Lechner  * Clock driver for TI Davinci PSC controllers
4c6ed4d73SDavid Lechner  *
5c6ed4d73SDavid Lechner  * Copyright (C) 2017 David Lechner <david@lechnology.com>
6c6ed4d73SDavid Lechner  *
7c6ed4d73SDavid Lechner  * Based on: drivers/clk/keystone/gate.c
8c6ed4d73SDavid Lechner  * Copyright (C) 2013 Texas Instruments.
9c6ed4d73SDavid Lechner  *	Murali Karicheri <m-karicheri2@ti.com>
10c6ed4d73SDavid Lechner  *	Santosh Shilimkar <santosh.shilimkar@ti.com>
11c6ed4d73SDavid Lechner  *
12c6ed4d73SDavid Lechner  * And: arch/arm/mach-davinci/psc.c
13c6ed4d73SDavid Lechner  * Copyright (C) 2006 Texas Instruments.
14c6ed4d73SDavid Lechner  */
15c6ed4d73SDavid Lechner 
16c6ed4d73SDavid Lechner #include <linux/clk-provider.h>
17c6ed4d73SDavid Lechner #include <linux/clk.h>
18043eaa70SDavid Lechner #include <linux/clk/davinci.h>
19c6ed4d73SDavid Lechner #include <linux/clkdev.h>
20c6ed4d73SDavid Lechner #include <linux/err.h>
21c6ed4d73SDavid Lechner #include <linux/of_address.h>
22c6ed4d73SDavid Lechner #include <linux/of_device.h>
23c6ed4d73SDavid Lechner #include <linux/of.h>
24c6ed4d73SDavid Lechner #include <linux/platform_device.h>
25c6ed4d73SDavid Lechner #include <linux/pm_clock.h>
26c6ed4d73SDavid Lechner #include <linux/pm_domain.h>
27c6ed4d73SDavid Lechner #include <linux/regmap.h>
28c6ed4d73SDavid Lechner #include <linux/reset-controller.h>
29c6ed4d73SDavid Lechner #include <linux/slab.h>
30c6ed4d73SDavid Lechner #include <linux/types.h>
31c6ed4d73SDavid Lechner 
32c6ed4d73SDavid Lechner #include "psc.h"
33c6ed4d73SDavid Lechner 
34c6ed4d73SDavid Lechner /* PSC register offsets */
35c6ed4d73SDavid Lechner #define EPCPR			0x070
36c6ed4d73SDavid Lechner #define PTCMD			0x120
37c6ed4d73SDavid Lechner #define PTSTAT			0x128
38c6ed4d73SDavid Lechner #define PDSTAT(n)		(0x200 + 4 * (n))
39c6ed4d73SDavid Lechner #define PDCTL(n)		(0x300 + 4 * (n))
40c6ed4d73SDavid Lechner #define MDSTAT(n)		(0x800 + 4 * (n))
41c6ed4d73SDavid Lechner #define MDCTL(n)		(0xa00 + 4 * (n))
42c6ed4d73SDavid Lechner 
43c6ed4d73SDavid Lechner /* PSC module states */
44c6ed4d73SDavid Lechner enum davinci_lpsc_state {
45c6ed4d73SDavid Lechner 	LPSC_STATE_SWRSTDISABLE	= 0,
46c6ed4d73SDavid Lechner 	LPSC_STATE_SYNCRST	= 1,
47c6ed4d73SDavid Lechner 	LPSC_STATE_DISABLE	= 2,
48c6ed4d73SDavid Lechner 	LPSC_STATE_ENABLE	= 3,
49c6ed4d73SDavid Lechner };
50c6ed4d73SDavid Lechner 
51c6ed4d73SDavid Lechner #define MDSTAT_STATE_MASK	GENMASK(5, 0)
52c6ed4d73SDavid Lechner #define MDSTAT_MCKOUT		BIT(12)
53c6ed4d73SDavid Lechner #define PDSTAT_STATE_MASK	GENMASK(4, 0)
54c6ed4d73SDavid Lechner #define MDCTL_FORCE		BIT(31)
55c6ed4d73SDavid Lechner #define MDCTL_LRESET		BIT(8)
56c6ed4d73SDavid Lechner #define PDCTL_EPCGOOD		BIT(8)
57c6ed4d73SDavid Lechner #define PDCTL_NEXT		BIT(0)
58c6ed4d73SDavid Lechner 
59c6ed4d73SDavid Lechner struct davinci_psc_data {
60c6ed4d73SDavid Lechner 	struct clk_onecell_data clk_data;
61c6ed4d73SDavid Lechner 	struct genpd_onecell_data pm_data;
62c6ed4d73SDavid Lechner 	struct reset_controller_dev rcdev;
63c6ed4d73SDavid Lechner };
64c6ed4d73SDavid Lechner 
65c6ed4d73SDavid Lechner /**
66c6ed4d73SDavid Lechner  * struct davinci_lpsc_clk - LPSC clock structure
67043eaa70SDavid Lechner  * @dev: the device that provides this LPSC or NULL
68c6ed4d73SDavid Lechner  * @hw: clk_hw for the LPSC
69c6ed4d73SDavid Lechner  * @pm_domain: power domain for the LPSC
70c6ed4d73SDavid Lechner  * @genpd_clk: clock reference owned by @pm_domain
71c6ed4d73SDavid Lechner  * @regmap: PSC MMIO region
72c6ed4d73SDavid Lechner  * @md: Module domain (LPSC module id)
73c6ed4d73SDavid Lechner  * @pd: Power domain
74c6ed4d73SDavid Lechner  * @flags: LPSC_* quirk flags
75c6ed4d73SDavid Lechner  */
76c6ed4d73SDavid Lechner struct davinci_lpsc_clk {
77c6ed4d73SDavid Lechner 	struct device *dev;
78c6ed4d73SDavid Lechner 	struct clk_hw hw;
79c6ed4d73SDavid Lechner 	struct generic_pm_domain pm_domain;
80c6ed4d73SDavid Lechner 	struct clk *genpd_clk;
81c6ed4d73SDavid Lechner 	struct regmap *regmap;
82c6ed4d73SDavid Lechner 	u32 md;
83c6ed4d73SDavid Lechner 	u32 pd;
84c6ed4d73SDavid Lechner 	u32 flags;
85c6ed4d73SDavid Lechner };
86c6ed4d73SDavid Lechner 
87c6ed4d73SDavid Lechner #define to_davinci_psc_data(x) container_of(x, struct davinci_psc_data, x)
88c6ed4d73SDavid Lechner #define to_davinci_lpsc_clk(x) container_of(x, struct davinci_lpsc_clk, x)
89c6ed4d73SDavid Lechner 
90c6ed4d73SDavid Lechner /**
91c6ed4d73SDavid Lechner  * best_dev_name - get the "best" device name.
92c6ed4d73SDavid Lechner  * @dev: the device
93c6ed4d73SDavid Lechner  *
94c6ed4d73SDavid Lechner  * Returns the device tree compatible name if the device has a DT node,
95c6ed4d73SDavid Lechner  * otherwise return the device name. This is mainly needed because clkdev
96c6ed4d73SDavid Lechner  * lookups are limited to 20 chars for dev_id and when using device tree,
97c6ed4d73SDavid Lechner  * dev_name(dev) is much longer than that.
98c6ed4d73SDavid Lechner  */
best_dev_name(struct device * dev)99c6ed4d73SDavid Lechner static inline const char *best_dev_name(struct device *dev)
100c6ed4d73SDavid Lechner {
101c6ed4d73SDavid Lechner 	const char *compatible;
102c6ed4d73SDavid Lechner 
103c6ed4d73SDavid Lechner 	if (!of_property_read_string(dev->of_node, "compatible", &compatible))
104c6ed4d73SDavid Lechner 		return compatible;
105c6ed4d73SDavid Lechner 
106c6ed4d73SDavid Lechner 	return dev_name(dev);
107c6ed4d73SDavid Lechner }
108c6ed4d73SDavid Lechner 
davinci_lpsc_config(struct davinci_lpsc_clk * lpsc,enum davinci_lpsc_state next_state)109c6ed4d73SDavid Lechner static void davinci_lpsc_config(struct davinci_lpsc_clk *lpsc,
110c6ed4d73SDavid Lechner 				enum davinci_lpsc_state next_state)
111c6ed4d73SDavid Lechner {
112c6ed4d73SDavid Lechner 	u32 epcpr, pdstat, mdstat, ptstat;
113c6ed4d73SDavid Lechner 
114c6ed4d73SDavid Lechner 	regmap_write_bits(lpsc->regmap, MDCTL(lpsc->md), MDSTAT_STATE_MASK,
115c6ed4d73SDavid Lechner 			  next_state);
116c6ed4d73SDavid Lechner 
117c6ed4d73SDavid Lechner 	if (lpsc->flags & LPSC_FORCE)
118c6ed4d73SDavid Lechner 		regmap_write_bits(lpsc->regmap, MDCTL(lpsc->md), MDCTL_FORCE,
119c6ed4d73SDavid Lechner 				  MDCTL_FORCE);
120c6ed4d73SDavid Lechner 
121c6ed4d73SDavid Lechner 	regmap_read(lpsc->regmap, PDSTAT(lpsc->pd), &pdstat);
122c6ed4d73SDavid Lechner 	if ((pdstat & PDSTAT_STATE_MASK) == 0) {
123c6ed4d73SDavid Lechner 		regmap_write_bits(lpsc->regmap, PDCTL(lpsc->pd), PDCTL_NEXT,
124c6ed4d73SDavid Lechner 				  PDCTL_NEXT);
125c6ed4d73SDavid Lechner 
126c6ed4d73SDavid Lechner 		regmap_write(lpsc->regmap, PTCMD, BIT(lpsc->pd));
127c6ed4d73SDavid Lechner 
128c6ed4d73SDavid Lechner 		regmap_read_poll_timeout(lpsc->regmap, EPCPR, epcpr,
129c6ed4d73SDavid Lechner 					 epcpr & BIT(lpsc->pd), 0, 0);
130c6ed4d73SDavid Lechner 
131c6ed4d73SDavid Lechner 		regmap_write_bits(lpsc->regmap, PDCTL(lpsc->pd), PDCTL_EPCGOOD,
132c6ed4d73SDavid Lechner 				  PDCTL_EPCGOOD);
133c6ed4d73SDavid Lechner 	} else {
134c6ed4d73SDavid Lechner 		regmap_write(lpsc->regmap, PTCMD, BIT(lpsc->pd));
135c6ed4d73SDavid Lechner 	}
136c6ed4d73SDavid Lechner 
137c6ed4d73SDavid Lechner 	regmap_read_poll_timeout(lpsc->regmap, PTSTAT, ptstat,
138c6ed4d73SDavid Lechner 				 !(ptstat & BIT(lpsc->pd)), 0, 0);
139c6ed4d73SDavid Lechner 
140c6ed4d73SDavid Lechner 	regmap_read_poll_timeout(lpsc->regmap, MDSTAT(lpsc->md), mdstat,
141c6ed4d73SDavid Lechner 				 (mdstat & MDSTAT_STATE_MASK) == next_state,
142c6ed4d73SDavid Lechner 				 0, 0);
143c6ed4d73SDavid Lechner }
144c6ed4d73SDavid Lechner 
davinci_lpsc_clk_enable(struct clk_hw * hw)145c6ed4d73SDavid Lechner static int davinci_lpsc_clk_enable(struct clk_hw *hw)
146c6ed4d73SDavid Lechner {
147c6ed4d73SDavid Lechner 	struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw);
148c6ed4d73SDavid Lechner 
149c6ed4d73SDavid Lechner 	davinci_lpsc_config(lpsc, LPSC_STATE_ENABLE);
150c6ed4d73SDavid Lechner 
151c6ed4d73SDavid Lechner 	return 0;
152c6ed4d73SDavid Lechner }
153c6ed4d73SDavid Lechner 
davinci_lpsc_clk_disable(struct clk_hw * hw)154c6ed4d73SDavid Lechner static void davinci_lpsc_clk_disable(struct clk_hw *hw)
155c6ed4d73SDavid Lechner {
156c6ed4d73SDavid Lechner 	struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw);
157c6ed4d73SDavid Lechner 
158c6ed4d73SDavid Lechner 	davinci_lpsc_config(lpsc, LPSC_STATE_DISABLE);
159c6ed4d73SDavid Lechner }
160c6ed4d73SDavid Lechner 
davinci_lpsc_clk_is_enabled(struct clk_hw * hw)161c6ed4d73SDavid Lechner static int davinci_lpsc_clk_is_enabled(struct clk_hw *hw)
162c6ed4d73SDavid Lechner {
163c6ed4d73SDavid Lechner 	struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw);
164c6ed4d73SDavid Lechner 	u32 mdstat;
165c6ed4d73SDavid Lechner 
166c6ed4d73SDavid Lechner 	regmap_read(lpsc->regmap, MDSTAT(lpsc->md), &mdstat);
167c6ed4d73SDavid Lechner 
168c6ed4d73SDavid Lechner 	return (mdstat & MDSTAT_MCKOUT) ? 1 : 0;
169c6ed4d73SDavid Lechner }
170c6ed4d73SDavid Lechner 
171c6ed4d73SDavid Lechner static const struct clk_ops davinci_lpsc_clk_ops = {
172c6ed4d73SDavid Lechner 	.enable		= davinci_lpsc_clk_enable,
173c6ed4d73SDavid Lechner 	.disable	= davinci_lpsc_clk_disable,
174c6ed4d73SDavid Lechner 	.is_enabled	= davinci_lpsc_clk_is_enabled,
175c6ed4d73SDavid Lechner };
176c6ed4d73SDavid Lechner 
davinci_psc_genpd_attach_dev(struct generic_pm_domain * pm_domain,struct device * dev)177c6ed4d73SDavid Lechner static int davinci_psc_genpd_attach_dev(struct generic_pm_domain *pm_domain,
178c6ed4d73SDavid Lechner 					struct device *dev)
179c6ed4d73SDavid Lechner {
180c6ed4d73SDavid Lechner 	struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(pm_domain);
181c6ed4d73SDavid Lechner 	struct clk *clk;
182c6ed4d73SDavid Lechner 	int ret;
183c6ed4d73SDavid Lechner 
184c6ed4d73SDavid Lechner 	/*
185c6ed4d73SDavid Lechner 	 * pm_clk_remove_clk() will call clk_put(), so we have to use clk_get()
186c6ed4d73SDavid Lechner 	 * to get the clock instead of using lpsc->hw.clk directly.
187c6ed4d73SDavid Lechner 	 */
188c6ed4d73SDavid Lechner 	clk = clk_get_sys(best_dev_name(lpsc->dev), clk_hw_get_name(&lpsc->hw));
189c6ed4d73SDavid Lechner 	if (IS_ERR(clk))
190c6ed4d73SDavid Lechner 		return (PTR_ERR(clk));
191c6ed4d73SDavid Lechner 
192c6ed4d73SDavid Lechner 	ret = pm_clk_create(dev);
193c6ed4d73SDavid Lechner 	if (ret < 0)
194c6ed4d73SDavid Lechner 		goto fail_clk_put;
195c6ed4d73SDavid Lechner 
196c6ed4d73SDavid Lechner 	ret = pm_clk_add_clk(dev, clk);
197c6ed4d73SDavid Lechner 	if (ret < 0)
198c6ed4d73SDavid Lechner 		goto fail_pm_clk_destroy;
199c6ed4d73SDavid Lechner 
200c6ed4d73SDavid Lechner 	lpsc->genpd_clk = clk;
201c6ed4d73SDavid Lechner 
202c6ed4d73SDavid Lechner 	return 0;
203c6ed4d73SDavid Lechner 
204c6ed4d73SDavid Lechner fail_pm_clk_destroy:
205c6ed4d73SDavid Lechner 	pm_clk_destroy(dev);
206c6ed4d73SDavid Lechner fail_clk_put:
207c6ed4d73SDavid Lechner 	clk_put(clk);
208c6ed4d73SDavid Lechner 
209c6ed4d73SDavid Lechner 	return ret;
210c6ed4d73SDavid Lechner }
211c6ed4d73SDavid Lechner 
davinci_psc_genpd_detach_dev(struct generic_pm_domain * pm_domain,struct device * dev)212c6ed4d73SDavid Lechner static void davinci_psc_genpd_detach_dev(struct generic_pm_domain *pm_domain,
213c6ed4d73SDavid Lechner 					 struct device *dev)
214c6ed4d73SDavid Lechner {
215c6ed4d73SDavid Lechner 	struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(pm_domain);
216c6ed4d73SDavid Lechner 
217c6ed4d73SDavid Lechner 	pm_clk_remove_clk(dev, lpsc->genpd_clk);
218c6ed4d73SDavid Lechner 	pm_clk_destroy(dev);
219c6ed4d73SDavid Lechner 
220c6ed4d73SDavid Lechner 	lpsc->genpd_clk = NULL;
221c6ed4d73SDavid Lechner }
222c6ed4d73SDavid Lechner 
223c6ed4d73SDavid Lechner /**
224c6ed4d73SDavid Lechner  * davinci_lpsc_clk_register - register LPSC clock
225043eaa70SDavid Lechner  * @dev: the clocks's device or NULL
226c6ed4d73SDavid Lechner  * @name: name of this clock
227c6ed4d73SDavid Lechner  * @parent_name: name of clock's parent
228c6ed4d73SDavid Lechner  * @regmap: PSC MMIO region
229c6ed4d73SDavid Lechner  * @md: local PSC number
230c6ed4d73SDavid Lechner  * @pd: power domain
231c6ed4d73SDavid Lechner  * @flags: LPSC_* flags
232c6ed4d73SDavid Lechner  */
233c6ed4d73SDavid Lechner static struct davinci_lpsc_clk *
davinci_lpsc_clk_register(struct device * dev,const char * name,const char * parent_name,struct regmap * regmap,u32 md,u32 pd,u32 flags)234c6ed4d73SDavid Lechner davinci_lpsc_clk_register(struct device *dev, const char *name,
235c6ed4d73SDavid Lechner 			  const char *parent_name, struct regmap *regmap,
236c6ed4d73SDavid Lechner 			  u32 md, u32 pd, u32 flags)
237c6ed4d73SDavid Lechner {
238c6ed4d73SDavid Lechner 	struct clk_init_data init;
239c6ed4d73SDavid Lechner 	struct davinci_lpsc_clk *lpsc;
240c6ed4d73SDavid Lechner 	int ret;
241c6ed4d73SDavid Lechner 	bool is_on;
242c6ed4d73SDavid Lechner 
243043eaa70SDavid Lechner 	lpsc = kzalloc(sizeof(*lpsc), GFP_KERNEL);
244c6ed4d73SDavid Lechner 	if (!lpsc)
245c6ed4d73SDavid Lechner 		return ERR_PTR(-ENOMEM);
246c6ed4d73SDavid Lechner 
247c6ed4d73SDavid Lechner 	init.name = name;
248c6ed4d73SDavid Lechner 	init.ops = &davinci_lpsc_clk_ops;
249c6ed4d73SDavid Lechner 	init.parent_names = (parent_name ? &parent_name : NULL);
250c6ed4d73SDavid Lechner 	init.num_parents = (parent_name ? 1 : 0);
251c6ed4d73SDavid Lechner 	init.flags = 0;
252c6ed4d73SDavid Lechner 
253c6ed4d73SDavid Lechner 	if (flags & LPSC_ALWAYS_ENABLED)
254c6ed4d73SDavid Lechner 		init.flags |= CLK_IS_CRITICAL;
255c6ed4d73SDavid Lechner 
256c6ed4d73SDavid Lechner 	if (flags & LPSC_SET_RATE_PARENT)
257c6ed4d73SDavid Lechner 		init.flags |= CLK_SET_RATE_PARENT;
258c6ed4d73SDavid Lechner 
259c6ed4d73SDavid Lechner 	lpsc->dev = dev;
260c6ed4d73SDavid Lechner 	lpsc->regmap = regmap;
261c6ed4d73SDavid Lechner 	lpsc->hw.init = &init;
262c6ed4d73SDavid Lechner 	lpsc->md = md;
263c6ed4d73SDavid Lechner 	lpsc->pd = pd;
264c6ed4d73SDavid Lechner 	lpsc->flags = flags;
265c6ed4d73SDavid Lechner 
266043eaa70SDavid Lechner 	ret = clk_hw_register(dev, &lpsc->hw);
267043eaa70SDavid Lechner 	if (ret < 0) {
268043eaa70SDavid Lechner 		kfree(lpsc);
269c6ed4d73SDavid Lechner 		return ERR_PTR(ret);
270043eaa70SDavid Lechner 	}
271043eaa70SDavid Lechner 
272043eaa70SDavid Lechner 	/* for now, genpd is only registered when using device-tree */
273043eaa70SDavid Lechner 	if (!dev || !dev->of_node)
274043eaa70SDavid Lechner 		return lpsc;
275c6ed4d73SDavid Lechner 
276c6ed4d73SDavid Lechner 	/* genpd attach needs a way to look up this clock */
277c6ed4d73SDavid Lechner 	ret = clk_hw_register_clkdev(&lpsc->hw, name, best_dev_name(dev));
278c6ed4d73SDavid Lechner 
279c6ed4d73SDavid Lechner 	lpsc->pm_domain.name = devm_kasprintf(dev, GFP_KERNEL, "%s: %s",
280c6ed4d73SDavid Lechner 					      best_dev_name(dev), name);
281c6ed4d73SDavid Lechner 	lpsc->pm_domain.attach_dev = davinci_psc_genpd_attach_dev;
282c6ed4d73SDavid Lechner 	lpsc->pm_domain.detach_dev = davinci_psc_genpd_detach_dev;
283c6ed4d73SDavid Lechner 	lpsc->pm_domain.flags = GENPD_FLAG_PM_CLK;
284c6ed4d73SDavid Lechner 
285c6ed4d73SDavid Lechner 	is_on = davinci_lpsc_clk_is_enabled(&lpsc->hw);
286c6ed4d73SDavid Lechner 	pm_genpd_init(&lpsc->pm_domain, NULL, is_on);
287c6ed4d73SDavid Lechner 
288c6ed4d73SDavid Lechner 	return lpsc;
289c6ed4d73SDavid Lechner }
290c6ed4d73SDavid Lechner 
davinci_lpsc_clk_reset(struct clk * clk,bool reset)291c6ed4d73SDavid Lechner static int davinci_lpsc_clk_reset(struct clk *clk, bool reset)
292c6ed4d73SDavid Lechner {
293c6ed4d73SDavid Lechner 	struct clk_hw *hw = __clk_get_hw(clk);
294c6ed4d73SDavid Lechner 	struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw);
295c6ed4d73SDavid Lechner 	u32 mdctl;
296c6ed4d73SDavid Lechner 
297c6ed4d73SDavid Lechner 	if (IS_ERR_OR_NULL(lpsc))
298c6ed4d73SDavid Lechner 		return -EINVAL;
299c6ed4d73SDavid Lechner 
300c6ed4d73SDavid Lechner 	mdctl = reset ? 0 : MDCTL_LRESET;
301c6ed4d73SDavid Lechner 	regmap_write_bits(lpsc->regmap, MDCTL(lpsc->md), MDCTL_LRESET, mdctl);
302c6ed4d73SDavid Lechner 
303c6ed4d73SDavid Lechner 	return 0;
304c6ed4d73SDavid Lechner }
305c6ed4d73SDavid Lechner 
davinci_psc_reset_assert(struct reset_controller_dev * rcdev,unsigned long id)306c6ed4d73SDavid Lechner static int davinci_psc_reset_assert(struct reset_controller_dev *rcdev,
307c6ed4d73SDavid Lechner 				    unsigned long id)
308c6ed4d73SDavid Lechner {
309c6ed4d73SDavid Lechner 	struct davinci_psc_data *psc = to_davinci_psc_data(rcdev);
310c6ed4d73SDavid Lechner 	struct clk *clk = psc->clk_data.clks[id];
311c6ed4d73SDavid Lechner 
312c6ed4d73SDavid Lechner 	return davinci_lpsc_clk_reset(clk, true);
313c6ed4d73SDavid Lechner }
314c6ed4d73SDavid Lechner 
davinci_psc_reset_deassert(struct reset_controller_dev * rcdev,unsigned long id)315c6ed4d73SDavid Lechner static int davinci_psc_reset_deassert(struct reset_controller_dev *rcdev,
316c6ed4d73SDavid Lechner 				      unsigned long id)
317c6ed4d73SDavid Lechner {
318c6ed4d73SDavid Lechner 	struct davinci_psc_data *psc = to_davinci_psc_data(rcdev);
319c6ed4d73SDavid Lechner 	struct clk *clk = psc->clk_data.clks[id];
320c6ed4d73SDavid Lechner 
321c6ed4d73SDavid Lechner 	return davinci_lpsc_clk_reset(clk, false);
322c6ed4d73SDavid Lechner }
323c6ed4d73SDavid Lechner 
324c6ed4d73SDavid Lechner static const struct reset_control_ops davinci_psc_reset_ops = {
325c6ed4d73SDavid Lechner 	.assert		= davinci_psc_reset_assert,
326c6ed4d73SDavid Lechner 	.deassert	= davinci_psc_reset_deassert,
327c6ed4d73SDavid Lechner };
328c6ed4d73SDavid Lechner 
davinci_psc_reset_of_xlate(struct reset_controller_dev * rcdev,const struct of_phandle_args * reset_spec)329c6ed4d73SDavid Lechner static int davinci_psc_reset_of_xlate(struct reset_controller_dev *rcdev,
330c6ed4d73SDavid Lechner 				      const struct of_phandle_args *reset_spec)
331c6ed4d73SDavid Lechner {
332c6ed4d73SDavid Lechner 	struct of_phandle_args clkspec = *reset_spec; /* discard const qualifier */
333c6ed4d73SDavid Lechner 	struct clk *clk;
334c6ed4d73SDavid Lechner 	struct clk_hw *hw;
335c6ed4d73SDavid Lechner 	struct davinci_lpsc_clk *lpsc;
336c6ed4d73SDavid Lechner 
337c6ed4d73SDavid Lechner 	/* the clock node is the same as the reset node */
338c6ed4d73SDavid Lechner 	clk = of_clk_get_from_provider(&clkspec);
339c6ed4d73SDavid Lechner 	if (IS_ERR(clk))
340c6ed4d73SDavid Lechner 		return PTR_ERR(clk);
341c6ed4d73SDavid Lechner 
342c6ed4d73SDavid Lechner 	hw = __clk_get_hw(clk);
343c6ed4d73SDavid Lechner 	lpsc = to_davinci_lpsc_clk(hw);
344c6ed4d73SDavid Lechner 	clk_put(clk);
345c6ed4d73SDavid Lechner 
346c6ed4d73SDavid Lechner 	/* not all modules support local reset */
347c6ed4d73SDavid Lechner 	if (!(lpsc->flags & LPSC_LOCAL_RESET))
348c6ed4d73SDavid Lechner 		return -EINVAL;
349c6ed4d73SDavid Lechner 
350c6ed4d73SDavid Lechner 	return lpsc->md;
351c6ed4d73SDavid Lechner }
352c6ed4d73SDavid Lechner 
353c6ed4d73SDavid Lechner static const struct regmap_config davinci_psc_regmap_config = {
354c6ed4d73SDavid Lechner 	.reg_bits	= 32,
355c6ed4d73SDavid Lechner 	.reg_stride	= 4,
356c6ed4d73SDavid Lechner 	.val_bits	= 32,
357c6ed4d73SDavid Lechner };
358c6ed4d73SDavid Lechner 
359c6ed4d73SDavid Lechner static struct davinci_psc_data *
__davinci_psc_register_clocks(struct device * dev,const struct davinci_lpsc_clk_info * info,int num_clks,void __iomem * base)360c6ed4d73SDavid Lechner __davinci_psc_register_clocks(struct device *dev,
361c6ed4d73SDavid Lechner 			      const struct davinci_lpsc_clk_info *info,
362c6ed4d73SDavid Lechner 			      int num_clks,
363c6ed4d73SDavid Lechner 			      void __iomem *base)
364c6ed4d73SDavid Lechner {
365c6ed4d73SDavid Lechner 	struct davinci_psc_data *psc;
366c6ed4d73SDavid Lechner 	struct clk **clks;
367c6ed4d73SDavid Lechner 	struct generic_pm_domain **pm_domains;
368c6ed4d73SDavid Lechner 	struct regmap *regmap;
369c6ed4d73SDavid Lechner 	int i, ret;
370c6ed4d73SDavid Lechner 
371043eaa70SDavid Lechner 	psc = kzalloc(sizeof(*psc), GFP_KERNEL);
372c6ed4d73SDavid Lechner 	if (!psc)
373c6ed4d73SDavid Lechner 		return ERR_PTR(-ENOMEM);
374c6ed4d73SDavid Lechner 
375043eaa70SDavid Lechner 	clks = kmalloc_array(num_clks, sizeof(*clks), GFP_KERNEL);
376043eaa70SDavid Lechner 	if (!clks) {
377043eaa70SDavid Lechner 		ret = -ENOMEM;
378043eaa70SDavid Lechner 		goto err_free_psc;
379043eaa70SDavid Lechner 	}
380c6ed4d73SDavid Lechner 
381c6ed4d73SDavid Lechner 	psc->clk_data.clks = clks;
382c6ed4d73SDavid Lechner 	psc->clk_data.clk_num = num_clks;
383c6ed4d73SDavid Lechner 
384c6ed4d73SDavid Lechner 	/*
385c6ed4d73SDavid Lechner 	 * init array with error so that of_clk_src_onecell_get() doesn't
386c6ed4d73SDavid Lechner 	 * return NULL for gaps in the sparse array
387c6ed4d73SDavid Lechner 	 */
388c6ed4d73SDavid Lechner 	for (i = 0; i < num_clks; i++)
389c6ed4d73SDavid Lechner 		clks[i] = ERR_PTR(-ENOENT);
390c6ed4d73SDavid Lechner 
391043eaa70SDavid Lechner 	pm_domains = kcalloc(num_clks, sizeof(*pm_domains), GFP_KERNEL);
392043eaa70SDavid Lechner 	if (!pm_domains) {
393043eaa70SDavid Lechner 		ret = -ENOMEM;
394043eaa70SDavid Lechner 		goto err_free_clks;
395043eaa70SDavid Lechner 	}
396c6ed4d73SDavid Lechner 
397c6ed4d73SDavid Lechner 	psc->pm_data.domains = pm_domains;
398c6ed4d73SDavid Lechner 	psc->pm_data.num_domains = num_clks;
399c6ed4d73SDavid Lechner 
400043eaa70SDavid Lechner 	regmap = regmap_init_mmio(dev, base, &davinci_psc_regmap_config);
401043eaa70SDavid Lechner 	if (IS_ERR(regmap)) {
402043eaa70SDavid Lechner 		ret = PTR_ERR(regmap);
403043eaa70SDavid Lechner 		goto err_free_pm_domains;
404043eaa70SDavid Lechner 	}
405c6ed4d73SDavid Lechner 
406c6ed4d73SDavid Lechner 	for (; info->name; info++) {
407c6ed4d73SDavid Lechner 		struct davinci_lpsc_clk *lpsc;
408c6ed4d73SDavid Lechner 
409c6ed4d73SDavid Lechner 		lpsc = davinci_lpsc_clk_register(dev, info->name, info->parent,
410c6ed4d73SDavid Lechner 						 regmap, info->md, info->pd,
411c6ed4d73SDavid Lechner 						 info->flags);
412c6ed4d73SDavid Lechner 		if (IS_ERR(lpsc)) {
413c6ed4d73SDavid Lechner 			dev_warn(dev, "Failed to register %s (%ld)\n",
414c6ed4d73SDavid Lechner 				 info->name, PTR_ERR(lpsc));
415c6ed4d73SDavid Lechner 			continue;
416c6ed4d73SDavid Lechner 		}
417c6ed4d73SDavid Lechner 
418c6ed4d73SDavid Lechner 		clks[info->md] = lpsc->hw.clk;
419c6ed4d73SDavid Lechner 		pm_domains[info->md] = &lpsc->pm_domain;
420c6ed4d73SDavid Lechner 	}
421c6ed4d73SDavid Lechner 
422043eaa70SDavid Lechner 	/*
423043eaa70SDavid Lechner 	 * for now, a reset controller is only registered when there is a device
424043eaa70SDavid Lechner 	 * to associate it with.
425043eaa70SDavid Lechner 	 */
426043eaa70SDavid Lechner 	if (!dev)
427043eaa70SDavid Lechner 		return psc;
428043eaa70SDavid Lechner 
429c6ed4d73SDavid Lechner 	psc->rcdev.ops = &davinci_psc_reset_ops;
430c6ed4d73SDavid Lechner 	psc->rcdev.owner = THIS_MODULE;
4315ced1923SBartosz Golaszewski 	psc->rcdev.dev = dev;
432c6ed4d73SDavid Lechner 	psc->rcdev.of_node = dev->of_node;
433c6ed4d73SDavid Lechner 	psc->rcdev.of_reset_n_cells = 1;
434c6ed4d73SDavid Lechner 	psc->rcdev.of_xlate = davinci_psc_reset_of_xlate;
435c6ed4d73SDavid Lechner 	psc->rcdev.nr_resets = num_clks;
436c6ed4d73SDavid Lechner 
437c6ed4d73SDavid Lechner 	ret = devm_reset_controller_register(dev, &psc->rcdev);
438c6ed4d73SDavid Lechner 	if (ret < 0)
439c6ed4d73SDavid Lechner 		dev_warn(dev, "Failed to register reset controller (%d)\n", ret);
440c6ed4d73SDavid Lechner 
441c6ed4d73SDavid Lechner 	return psc;
442043eaa70SDavid Lechner 
443043eaa70SDavid Lechner err_free_pm_domains:
444043eaa70SDavid Lechner 	kfree(pm_domains);
445043eaa70SDavid Lechner err_free_clks:
446043eaa70SDavid Lechner 	kfree(clks);
447043eaa70SDavid Lechner err_free_psc:
448043eaa70SDavid Lechner 	kfree(psc);
449043eaa70SDavid Lechner 
450043eaa70SDavid Lechner 	return ERR_PTR(ret);
451c6ed4d73SDavid Lechner }
452c6ed4d73SDavid Lechner 
davinci_psc_register_clocks(struct device * dev,const struct davinci_lpsc_clk_info * info,u8 num_clks,void __iomem * base)453c6ed4d73SDavid Lechner int davinci_psc_register_clocks(struct device *dev,
454c6ed4d73SDavid Lechner 				const struct davinci_lpsc_clk_info *info,
455c6ed4d73SDavid Lechner 				u8 num_clks,
456c6ed4d73SDavid Lechner 				void __iomem *base)
457c6ed4d73SDavid Lechner {
458c6ed4d73SDavid Lechner 	struct davinci_psc_data *psc;
459c6ed4d73SDavid Lechner 
460c6ed4d73SDavid Lechner 	psc = __davinci_psc_register_clocks(dev, info, num_clks, base);
461c6ed4d73SDavid Lechner 	if (IS_ERR(psc))
462c6ed4d73SDavid Lechner 		return PTR_ERR(psc);
463c6ed4d73SDavid Lechner 
464c6ed4d73SDavid Lechner 	for (; info->name; info++) {
465c6ed4d73SDavid Lechner 		const struct davinci_lpsc_clkdev_info *cdevs = info->cdevs;
466c6ed4d73SDavid Lechner 		struct clk *clk = psc->clk_data.clks[info->md];
467c6ed4d73SDavid Lechner 
468c6ed4d73SDavid Lechner 		if (!cdevs || IS_ERR_OR_NULL(clk))
469c6ed4d73SDavid Lechner 			continue;
470c6ed4d73SDavid Lechner 
471c6ed4d73SDavid Lechner 		for (; cdevs->con_id || cdevs->dev_id; cdevs++)
472c6ed4d73SDavid Lechner 			clk_register_clkdev(clk, cdevs->con_id, cdevs->dev_id);
473c6ed4d73SDavid Lechner 	}
474c6ed4d73SDavid Lechner 
475c6ed4d73SDavid Lechner 	return 0;
476c6ed4d73SDavid Lechner }
477c6ed4d73SDavid Lechner 
of_davinci_psc_clk_init(struct device * dev,const struct davinci_lpsc_clk_info * info,u8 num_clks,void __iomem * base)478c6ed4d73SDavid Lechner int of_davinci_psc_clk_init(struct device *dev,
479c6ed4d73SDavid Lechner 			    const struct davinci_lpsc_clk_info *info,
480c6ed4d73SDavid Lechner 			    u8 num_clks,
481c6ed4d73SDavid Lechner 			    void __iomem *base)
482c6ed4d73SDavid Lechner {
483c6ed4d73SDavid Lechner 	struct device_node *node = dev->of_node;
484c6ed4d73SDavid Lechner 	struct davinci_psc_data *psc;
485c6ed4d73SDavid Lechner 
486c6ed4d73SDavid Lechner 	psc = __davinci_psc_register_clocks(dev, info, num_clks, base);
487c6ed4d73SDavid Lechner 	if (IS_ERR(psc))
488c6ed4d73SDavid Lechner 		return PTR_ERR(psc);
489c6ed4d73SDavid Lechner 
490c6ed4d73SDavid Lechner 	of_genpd_add_provider_onecell(node, &psc->pm_data);
491c6ed4d73SDavid Lechner 
492c6ed4d73SDavid Lechner 	of_clk_add_provider(node, of_clk_src_onecell_get, &psc->clk_data);
493c6ed4d73SDavid Lechner 
494c6ed4d73SDavid Lechner 	return 0;
495c6ed4d73SDavid Lechner }
496c6ed4d73SDavid Lechner 
497c6ed4d73SDavid Lechner static const struct of_device_id davinci_psc_of_match[] = {
4984eff0bebSDavid Lechner #ifdef CONFIG_ARCH_DAVINCI_DA850
499a47d6040SDavid Lechner 	{ .compatible = "ti,da850-psc0", .data = &of_da850_psc0_init_data },
500a47d6040SDavid Lechner 	{ .compatible = "ti,da850-psc1", .data = &of_da850_psc1_init_data },
5014eff0bebSDavid Lechner #endif
502c6ed4d73SDavid Lechner 	{ }
503c6ed4d73SDavid Lechner };
504c6ed4d73SDavid Lechner 
505c6ed4d73SDavid Lechner static const struct platform_device_id davinci_psc_id_table[] = {
5064eff0bebSDavid Lechner #ifdef CONFIG_ARCH_DAVINCI_DA830
507c2952e27SDavid Lechner 	{ .name = "da830-psc0", .driver_data = (kernel_ulong_t)&da830_psc0_init_data },
508c2952e27SDavid Lechner 	{ .name = "da830-psc1", .driver_data = (kernel_ulong_t)&da830_psc1_init_data },
5094eff0bebSDavid Lechner #endif
5104eff0bebSDavid Lechner #ifdef CONFIG_ARCH_DAVINCI_DA850
511a47d6040SDavid Lechner 	{ .name = "da850-psc0", .driver_data = (kernel_ulong_t)&da850_psc0_init_data },
512a47d6040SDavid Lechner 	{ .name = "da850-psc1", .driver_data = (kernel_ulong_t)&da850_psc1_init_data },
5134eff0bebSDavid Lechner #endif
514c6ed4d73SDavid Lechner 	{ }
515c6ed4d73SDavid Lechner };
516c6ed4d73SDavid Lechner 
davinci_psc_probe(struct platform_device * pdev)517c6ed4d73SDavid Lechner static int davinci_psc_probe(struct platform_device *pdev)
518c6ed4d73SDavid Lechner {
519c6ed4d73SDavid Lechner 	struct device *dev = &pdev->dev;
520c6ed4d73SDavid Lechner 	const struct of_device_id *of_id;
521c6ed4d73SDavid Lechner 	const struct davinci_psc_init_data *init_data = NULL;
522c6ed4d73SDavid Lechner 	void __iomem *base;
523c6ed4d73SDavid Lechner 	int ret;
524c6ed4d73SDavid Lechner 
525c6ed4d73SDavid Lechner 	of_id = of_match_device(davinci_psc_of_match, dev);
526c6ed4d73SDavid Lechner 	if (of_id)
527c6ed4d73SDavid Lechner 		init_data = of_id->data;
528c6ed4d73SDavid Lechner 	else if (pdev->id_entry)
529c6ed4d73SDavid Lechner 		init_data = (void *)pdev->id_entry->driver_data;
530c6ed4d73SDavid Lechner 
531c6ed4d73SDavid Lechner 	if (!init_data) {
532c6ed4d73SDavid Lechner 		dev_err(dev, "unable to find driver init data\n");
533c6ed4d73SDavid Lechner 		return -EINVAL;
534c6ed4d73SDavid Lechner 	}
535c6ed4d73SDavid Lechner 
536*1f8f3c6bSYueHaibing 	base = devm_platform_ioremap_resource(pdev, 0);
537fc3fcb4fSWei Yongjun 	if (IS_ERR(base))
538c6ed4d73SDavid Lechner 		return PTR_ERR(base);
539c6ed4d73SDavid Lechner 
540c6ed4d73SDavid Lechner 	ret = devm_clk_bulk_get(dev, init_data->num_parent_clks,
541c6ed4d73SDavid Lechner 				init_data->parent_clks);
542c6ed4d73SDavid Lechner 	if (ret < 0)
543c6ed4d73SDavid Lechner 		return ret;
544c6ed4d73SDavid Lechner 
545c6ed4d73SDavid Lechner 	return init_data->psc_init(dev, base);
546c6ed4d73SDavid Lechner }
547c6ed4d73SDavid Lechner 
548c6ed4d73SDavid Lechner static struct platform_driver davinci_psc_driver = {
549c6ed4d73SDavid Lechner 	.probe		= davinci_psc_probe,
550c6ed4d73SDavid Lechner 	.driver		= {
551c6ed4d73SDavid Lechner 		.name		= "davinci-psc-clk",
552c6ed4d73SDavid Lechner 		.of_match_table	= davinci_psc_of_match,
553c6ed4d73SDavid Lechner 	},
554c6ed4d73SDavid Lechner 	.id_table	= davinci_psc_id_table,
555c6ed4d73SDavid Lechner };
556c6ed4d73SDavid Lechner 
davinci_psc_driver_init(void)557c6ed4d73SDavid Lechner static int __init davinci_psc_driver_init(void)
558c6ed4d73SDavid Lechner {
559c6ed4d73SDavid Lechner 	return platform_driver_register(&davinci_psc_driver);
560c6ed4d73SDavid Lechner }
561c6ed4d73SDavid Lechner 
562c6ed4d73SDavid Lechner /* has to be postcore_initcall because davinci_gpio depend on PSC clocks */
563c6ed4d73SDavid Lechner postcore_initcall(davinci_psc_driver_init);
564