]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
70ee6577 LW |
2 | /* |
3 | * Clock driver for the ARM Integrator/IM-PD1 board | |
8e048b99 | 4 | * Copyright (C) 2012-2013 Linus Walleij |
70ee6577 LW |
5 | */ |
6 | #include <linux/clk-provider.h> | |
70ee6577 LW |
7 | #include <linux/clkdev.h> |
8 | #include <linux/err.h> | |
9 | #include <linux/io.h> | |
84655b76 | 10 | #include <linux/platform_device.h> |
70ee6577 | 11 | #include <linux/platform_data/clk-integrator.h> |
84655b76 LW |
12 | #include <linux/module.h> |
13 | #include <linux/mfd/syscon.h> | |
14 | #include <linux/regmap.h> | |
70ee6577 | 15 | |
ba3fae06 | 16 | #include "icst.h" |
70ee6577 LW |
17 | #include "clk-icst.h" |
18 | ||
cc0cc4ca LW |
19 | #define IMPD1_OSC1 0x00 |
20 | #define IMPD1_OSC2 0x04 | |
21 | #define IMPD1_LOCK 0x08 | |
22 | ||
70ee6577 | 23 | struct impd1_clk { |
222cb1bf LW |
24 | char *pclkname; |
25 | struct clk *pclk; | |
8e048b99 LW |
26 | char *vco1name; |
27 | struct clk *vco1clk; | |
28 | char *vco2name; | |
29 | struct clk *vco2clk; | |
30 | struct clk *mmciclk; | |
31 | char *uartname; | |
70ee6577 | 32 | struct clk *uartclk; |
8e048b99 LW |
33 | char *spiname; |
34 | struct clk *spiclk; | |
35 | char *scname; | |
36 | struct clk *scclk; | |
222cb1bf | 37 | struct clk_lookup *clks[15]; |
70ee6577 LW |
38 | }; |
39 | ||
8e048b99 | 40 | /* One entry for each connected IM-PD1 LM */ |
70ee6577 LW |
41 | static struct impd1_clk impd1_clks[4]; |
42 | ||
43 | /* | |
8e048b99 | 44 | * There are two VCO's on the IM-PD1 |
70ee6577 LW |
45 | */ |
46 | ||
8e048b99 | 47 | static const struct icst_params impd1_vco1_params = { |
70ee6577 LW |
48 | .ref = 24000000, /* 24 MHz */ |
49 | .vco_max = ICST525_VCO_MAX_3V, | |
50 | .vco_min = ICST525_VCO_MIN, | |
51 | .vd_min = 12, | |
52 | .vd_max = 519, | |
53 | .rd_min = 3, | |
54 | .rd_max = 120, | |
55 | .s2div = icst525_s2div, | |
56 | .idx2s = icst525_idx2s, | |
57 | }; | |
58 | ||
59 | static const struct clk_icst_desc impd1_icst1_desc = { | |
8e048b99 | 60 | .params = &impd1_vco1_params, |
70ee6577 LW |
61 | .vco_offset = IMPD1_OSC1, |
62 | .lock_offset = IMPD1_LOCK, | |
63 | }; | |
64 | ||
8e048b99 LW |
65 | static const struct icst_params impd1_vco2_params = { |
66 | .ref = 24000000, /* 24 MHz */ | |
67 | .vco_max = ICST525_VCO_MAX_3V, | |
68 | .vco_min = ICST525_VCO_MIN, | |
69 | .vd_min = 12, | |
70 | .vd_max = 519, | |
71 | .rd_min = 3, | |
72 | .rd_max = 120, | |
73 | .s2div = icst525_s2div, | |
74 | .idx2s = icst525_idx2s, | |
75 | }; | |
76 | ||
77 | static const struct clk_icst_desc impd1_icst2_desc = { | |
78 | .params = &impd1_vco2_params, | |
79 | .vco_offset = IMPD1_OSC2, | |
80 | .lock_offset = IMPD1_LOCK, | |
81 | }; | |
82 | ||
70ee6577 LW |
83 | /** |
84 | * integrator_impd1_clk_init() - set up the integrator clock tree | |
85 | * @base: base address of the logic module (LM) | |
86 | * @id: the ID of this LM | |
87 | */ | |
88 | void integrator_impd1_clk_init(void __iomem *base, unsigned int id) | |
89 | { | |
90 | struct impd1_clk *imc; | |
91 | struct clk *clk; | |
222cb1bf | 92 | struct clk *pclk; |
70ee6577 LW |
93 | int i; |
94 | ||
95 | if (id > 3) { | |
96 | pr_crit("no more than 4 LMs can be attached\n"); | |
97 | return; | |
98 | } | |
99 | imc = &impd1_clks[id]; | |
100 | ||
222cb1bf LW |
101 | /* Register the fixed rate PCLK */ |
102 | imc->pclkname = kasprintf(GFP_KERNEL, "lm%x-pclk", id); | |
ac82a8b5 | 103 | pclk = clk_register_fixed_rate(NULL, imc->pclkname, NULL, 0, 0); |
222cb1bf LW |
104 | imc->pclk = pclk; |
105 | ||
8e048b99 | 106 | imc->vco1name = kasprintf(GFP_KERNEL, "lm%x-vco1", id); |
bf6edb4b LW |
107 | clk = icst_clk_register(NULL, &impd1_icst1_desc, imc->vco1name, NULL, |
108 | base); | |
ae6e694e | 109 | imc->vco1clk = clk; |
222cb1bf LW |
110 | imc->clks[0] = clkdev_alloc(pclk, "apb_pclk", "lm%x:01000", id); |
111 | imc->clks[1] = clkdev_alloc(clk, NULL, "lm%x:01000", id); | |
70ee6577 | 112 | |
8e048b99 LW |
113 | /* VCO2 is also called "CLK2" */ |
114 | imc->vco2name = kasprintf(GFP_KERNEL, "lm%x-vco2", id); | |
bf6edb4b LW |
115 | clk = icst_clk_register(NULL, &impd1_icst2_desc, imc->vco2name, NULL, |
116 | base); | |
8e048b99 LW |
117 | imc->vco2clk = clk; |
118 | ||
119 | /* MMCI uses CLK2 right off */ | |
222cb1bf LW |
120 | imc->clks[2] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00700", id); |
121 | imc->clks[3] = clkdev_alloc(clk, NULL, "lm%x:00700", id); | |
8e048b99 LW |
122 | |
123 | /* UART reference clock divides CLK2 by a fixed factor 4 */ | |
124 | imc->uartname = kasprintf(GFP_KERNEL, "lm%x-uartclk", id); | |
125 | clk = clk_register_fixed_factor(NULL, imc->uartname, imc->vco2name, | |
126 | CLK_IGNORE_UNUSED, 1, 4); | |
70ee6577 | 127 | imc->uartclk = clk; |
222cb1bf LW |
128 | imc->clks[4] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00100", id); |
129 | imc->clks[5] = clkdev_alloc(clk, NULL, "lm%x:00100", id); | |
130 | imc->clks[6] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00200", id); | |
131 | imc->clks[7] = clkdev_alloc(clk, NULL, "lm%x:00200", id); | |
8e048b99 LW |
132 | |
133 | /* SPI PL022 clock divides CLK2 by a fixed factor 64 */ | |
134 | imc->spiname = kasprintf(GFP_KERNEL, "lm%x-spiclk", id); | |
135 | clk = clk_register_fixed_factor(NULL, imc->spiname, imc->vco2name, | |
136 | CLK_IGNORE_UNUSED, 1, 64); | |
222cb1bf LW |
137 | imc->clks[8] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00300", id); |
138 | imc->clks[9] = clkdev_alloc(clk, NULL, "lm%x:00300", id); | |
139 | ||
140 | /* The GPIO blocks and AACI have only PCLK */ | |
141 | imc->clks[10] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00400", id); | |
142 | imc->clks[11] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00500", id); | |
143 | imc->clks[12] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00800", id); | |
8e048b99 LW |
144 | |
145 | /* Smart Card clock divides CLK2 by a fixed factor 4 */ | |
146 | imc->scname = kasprintf(GFP_KERNEL, "lm%x-scclk", id); | |
147 | clk = clk_register_fixed_factor(NULL, imc->scname, imc->vco2name, | |
148 | CLK_IGNORE_UNUSED, 1, 4); | |
149 | imc->scclk = clk; | |
222cb1bf LW |
150 | imc->clks[13] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00600", id); |
151 | imc->clks[14] = clkdev_alloc(clk, NULL, "lm%x:00600", id); | |
70ee6577 LW |
152 | |
153 | for (i = 0; i < ARRAY_SIZE(imc->clks); i++) | |
154 | clkdev_add(imc->clks[i]); | |
155 | } | |
a218d7fa | 156 | EXPORT_SYMBOL_GPL(integrator_impd1_clk_init); |
70ee6577 LW |
157 | |
158 | void integrator_impd1_clk_exit(unsigned int id) | |
159 | { | |
160 | int i; | |
161 | struct impd1_clk *imc; | |
162 | ||
163 | if (id > 3) | |
164 | return; | |
165 | imc = &impd1_clks[id]; | |
166 | ||
167 | for (i = 0; i < ARRAY_SIZE(imc->clks); i++) | |
168 | clkdev_drop(imc->clks[i]); | |
8e048b99 | 169 | clk_unregister(imc->spiclk); |
70ee6577 | 170 | clk_unregister(imc->uartclk); |
8e048b99 LW |
171 | clk_unregister(imc->vco2clk); |
172 | clk_unregister(imc->vco1clk); | |
222cb1bf | 173 | clk_unregister(imc->pclk); |
8e048b99 LW |
174 | kfree(imc->scname); |
175 | kfree(imc->spiname); | |
176 | kfree(imc->uartname); | |
177 | kfree(imc->vco2name); | |
178 | kfree(imc->vco1name); | |
222cb1bf | 179 | kfree(imc->pclkname); |
70ee6577 | 180 | } |
a218d7fa | 181 | EXPORT_SYMBOL_GPL(integrator_impd1_clk_exit); |
84655b76 LW |
182 | |
183 | static int integrator_impd1_clk_spawn(struct device *dev, | |
184 | struct device_node *parent, | |
185 | struct device_node *np) | |
186 | { | |
187 | struct regmap *map; | |
188 | struct clk *clk = ERR_PTR(-EINVAL); | |
189 | const char *name = np->name; | |
190 | const char *parent_name; | |
191 | const struct clk_icst_desc *desc; | |
192 | int ret; | |
193 | ||
194 | map = syscon_node_to_regmap(parent); | |
195 | if (IS_ERR(map)) { | |
196 | pr_err("no regmap for syscon IM-PD1 ICST clock parent\n"); | |
197 | return PTR_ERR(map); | |
198 | } | |
199 | ||
200 | if (of_device_is_compatible(np, "arm,impd1-vco1")) { | |
201 | desc = &impd1_icst1_desc; | |
202 | } else if (of_device_is_compatible(np, "arm,impd1-vco2")) { | |
203 | desc = &impd1_icst2_desc; | |
204 | } else { | |
205 | dev_err(dev, "not a clock node %s\n", name); | |
206 | return -ENODEV; | |
207 | } | |
208 | ||
24661081 | 209 | of_property_read_string(np, "clock-output-names", &name); |
84655b76 LW |
210 | parent_name = of_clk_get_parent_name(np, 0); |
211 | clk = icst_clk_setup(NULL, desc, name, parent_name, map, | |
212 | ICST_INTEGRATOR_IM_PD1); | |
213 | if (!IS_ERR(clk)) { | |
214 | of_clk_add_provider(np, of_clk_src_simple_get, clk); | |
215 | ret = 0; | |
216 | } else { | |
217 | dev_err(dev, "error setting up IM-PD1 ICST clock\n"); | |
218 | ret = PTR_ERR(clk); | |
219 | } | |
220 | ||
221 | return ret; | |
222 | } | |
223 | ||
224 | static int integrator_impd1_clk_probe(struct platform_device *pdev) | |
225 | { | |
226 | struct device *dev = &pdev->dev; | |
227 | struct device_node *np = dev->of_node; | |
228 | struct device_node *child; | |
229 | int ret = 0; | |
230 | ||
231 | for_each_available_child_of_node(np, child) { | |
232 | ret = integrator_impd1_clk_spawn(dev, np, child); | |
233 | if (ret) | |
234 | break; | |
235 | } | |
236 | ||
237 | return ret; | |
238 | } | |
239 | ||
240 | static const struct of_device_id impd1_syscon_match[] = { | |
241 | { .compatible = "arm,im-pd1-syscon", }, | |
242 | {} | |
243 | }; | |
244 | MODULE_DEVICE_TABLE(of, impd1_syscon_match); | |
245 | ||
246 | static struct platform_driver impd1_clk_driver = { | |
247 | .driver = { | |
248 | .name = "impd1-clk", | |
249 | .of_match_table = impd1_syscon_match, | |
250 | }, | |
251 | .probe = integrator_impd1_clk_probe, | |
252 | }; | |
253 | builtin_platform_driver(impd1_clk_driver); | |
254 | ||
255 | MODULE_AUTHOR("Linus Walleij <linusw@kernel.org>"); | |
256 | MODULE_DESCRIPTION("Arm IM-PD1 module clock driver"); | |
257 | MODULE_LICENSE("GPL v2"); |