diff options
-rw-r--r-- | drivers/clk/clk-aspeed.c | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c index 5adedda82d26..cf5ea63feb31 100644 --- a/drivers/clk/clk-aspeed.c +++ b/drivers/clk/clk-aspeed.c | |||
@@ -5,6 +5,8 @@ | |||
5 | #include <linux/clk-provider.h> | 5 | #include <linux/clk-provider.h> |
6 | #include <linux/mfd/syscon.h> | 6 | #include <linux/mfd/syscon.h> |
7 | #include <linux/of_address.h> | 7 | #include <linux/of_address.h> |
8 | #include <linux/of_device.h> | ||
9 | #include <linux/platform_device.h> | ||
8 | #include <linux/regmap.h> | 10 | #include <linux/regmap.h> |
9 | #include <linux/slab.h> | 11 | #include <linux/slab.h> |
10 | #include <linux/spinlock.h> | 12 | #include <linux/spinlock.h> |
@@ -107,6 +109,18 @@ static const struct aspeed_gate_data aspeed_gates[] = { | |||
107 | [ASPEED_CLK_GATE_LHCCLK] = { 28, -1, "lhclk-gate", "lhclk", 0 }, /* LPC master/LPC+ */ | 109 | [ASPEED_CLK_GATE_LHCCLK] = { 28, -1, "lhclk-gate", "lhclk", 0 }, /* LPC master/LPC+ */ |
108 | }; | 110 | }; |
109 | 111 | ||
112 | static const struct clk_div_table ast2500_mac_div_table[] = { | ||
113 | { 0x0, 4 }, /* Yep, really. Aspeed confirmed this is correct */ | ||
114 | { 0x1, 4 }, | ||
115 | { 0x2, 6 }, | ||
116 | { 0x3, 8 }, | ||
117 | { 0x4, 10 }, | ||
118 | { 0x5, 12 }, | ||
119 | { 0x6, 14 }, | ||
120 | { 0x7, 16 }, | ||
121 | { 0 } | ||
122 | }; | ||
123 | |||
110 | static const struct clk_div_table ast2400_div_table[] = { | 124 | static const struct clk_div_table ast2400_div_table[] = { |
111 | { 0x0, 2 }, | 125 | { 0x0, 2 }, |
112 | { 0x1, 4 }, | 126 | { 0x1, 4 }, |
@@ -172,6 +186,122 @@ static struct clk_hw *aspeed_ast2500_calc_pll(const char *name, u32 val) | |||
172 | mult, div); | 186 | mult, div); |
173 | } | 187 | } |
174 | 188 | ||
189 | struct aspeed_clk_soc_data { | ||
190 | const struct clk_div_table *div_table; | ||
191 | const struct clk_div_table *mac_div_table; | ||
192 | struct clk_hw *(*calc_pll)(const char *name, u32 val); | ||
193 | }; | ||
194 | |||
195 | static const struct aspeed_clk_soc_data ast2500_data = { | ||
196 | .div_table = ast2500_div_table, | ||
197 | .mac_div_table = ast2500_mac_div_table, | ||
198 | .calc_pll = aspeed_ast2500_calc_pll, | ||
199 | }; | ||
200 | |||
201 | static const struct aspeed_clk_soc_data ast2400_data = { | ||
202 | .div_table = ast2400_div_table, | ||
203 | .mac_div_table = ast2400_div_table, | ||
204 | .calc_pll = aspeed_ast2400_calc_pll, | ||
205 | }; | ||
206 | |||
207 | static int aspeed_clk_probe(struct platform_device *pdev) | ||
208 | { | ||
209 | const struct aspeed_clk_soc_data *soc_data; | ||
210 | struct device *dev = &pdev->dev; | ||
211 | struct regmap *map; | ||
212 | struct clk_hw *hw; | ||
213 | u32 val, rate; | ||
214 | |||
215 | map = syscon_node_to_regmap(dev->of_node); | ||
216 | if (IS_ERR(map)) { | ||
217 | dev_err(dev, "no syscon regmap\n"); | ||
218 | return PTR_ERR(map); | ||
219 | } | ||
220 | |||
221 | /* SoC generations share common layouts but have different divisors */ | ||
222 | soc_data = of_device_get_match_data(dev); | ||
223 | if (!soc_data) { | ||
224 | dev_err(dev, "no match data for platform\n"); | ||
225 | return -EINVAL; | ||
226 | } | ||
227 | |||
228 | /* UART clock div13 setting */ | ||
229 | regmap_read(map, ASPEED_MISC_CTRL, &val); | ||
230 | if (val & UART_DIV13_EN) | ||
231 | rate = 24000000 / 13; | ||
232 | else | ||
233 | rate = 24000000; | ||
234 | /* TODO: Find the parent data for the uart clock */ | ||
235 | hw = clk_hw_register_fixed_rate(dev, "uart", NULL, 0, rate); | ||
236 | if (IS_ERR(hw)) | ||
237 | return PTR_ERR(hw); | ||
238 | aspeed_clk_data->hws[ASPEED_CLK_UART] = hw; | ||
239 | |||
240 | /* | ||
241 | * Memory controller (M-PLL) PLL. This clock is configured by the | ||
242 | * bootloader, and is exposed to Linux as a read-only clock rate. | ||
243 | */ | ||
244 | regmap_read(map, ASPEED_MPLL_PARAM, &val); | ||
245 | hw = soc_data->calc_pll("mpll", val); | ||
246 | if (IS_ERR(hw)) | ||
247 | return PTR_ERR(hw); | ||
248 | aspeed_clk_data->hws[ASPEED_CLK_MPLL] = hw; | ||
249 | |||
250 | /* SD/SDIO clock divider (TODO: There's a gate too) */ | ||
251 | hw = clk_hw_register_divider_table(dev, "sdio", "hpll", 0, | ||
252 | scu_base + ASPEED_CLK_SELECTION, 12, 3, 0, | ||
253 | soc_data->div_table, | ||
254 | &aspeed_clk_lock); | ||
255 | if (IS_ERR(hw)) | ||
256 | return PTR_ERR(hw); | ||
257 | aspeed_clk_data->hws[ASPEED_CLK_SDIO] = hw; | ||
258 | |||
259 | /* MAC AHB bus clock divider */ | ||
260 | hw = clk_hw_register_divider_table(dev, "mac", "hpll", 0, | ||
261 | scu_base + ASPEED_CLK_SELECTION, 16, 3, 0, | ||
262 | soc_data->mac_div_table, | ||
263 | &aspeed_clk_lock); | ||
264 | if (IS_ERR(hw)) | ||
265 | return PTR_ERR(hw); | ||
266 | aspeed_clk_data->hws[ASPEED_CLK_MAC] = hw; | ||
267 | |||
268 | /* LPC Host (LHCLK) clock divider */ | ||
269 | hw = clk_hw_register_divider_table(dev, "lhclk", "hpll", 0, | ||
270 | scu_base + ASPEED_CLK_SELECTION, 20, 3, 0, | ||
271 | soc_data->div_table, | ||
272 | &aspeed_clk_lock); | ||
273 | if (IS_ERR(hw)) | ||
274 | return PTR_ERR(hw); | ||
275 | aspeed_clk_data->hws[ASPEED_CLK_LHCLK] = hw; | ||
276 | |||
277 | /* P-Bus (BCLK) clock divider */ | ||
278 | hw = clk_hw_register_divider_table(dev, "bclk", "hpll", 0, | ||
279 | scu_base + ASPEED_CLK_SELECTION_2, 0, 2, 0, | ||
280 | soc_data->div_table, | ||
281 | &aspeed_clk_lock); | ||
282 | if (IS_ERR(hw)) | ||
283 | return PTR_ERR(hw); | ||
284 | aspeed_clk_data->hws[ASPEED_CLK_BCLK] = hw; | ||
285 | |||
286 | return 0; | ||
287 | }; | ||
288 | |||
289 | static const struct of_device_id aspeed_clk_dt_ids[] = { | ||
290 | { .compatible = "aspeed,ast2400-scu", .data = &ast2400_data }, | ||
291 | { .compatible = "aspeed,ast2500-scu", .data = &ast2500_data }, | ||
292 | { } | ||
293 | }; | ||
294 | |||
295 | static struct platform_driver aspeed_clk_driver = { | ||
296 | .probe = aspeed_clk_probe, | ||
297 | .driver = { | ||
298 | .name = "aspeed-clk", | ||
299 | .of_match_table = aspeed_clk_dt_ids, | ||
300 | .suppress_bind_attrs = true, | ||
301 | }, | ||
302 | }; | ||
303 | builtin_platform_driver(aspeed_clk_driver); | ||
304 | |||
175 | static void __init aspeed_ast2400_cc(struct regmap *map) | 305 | static void __init aspeed_ast2400_cc(struct regmap *map) |
176 | { | 306 | { |
177 | struct clk_hw *hw; | 307 | struct clk_hw *hw; |