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