Commit | Line | Data |
---|---|---|
33b8ac91 LP |
1 | /* |
2 | * ARTPEC-6 clock initialization | |
3 | * | |
4 | * Copyright 2015-2016 Axis Comunications AB. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include <linux/clk-provider.h> | |
12 | #include <linux/device.h> | |
13 | #include <linux/io.h> | |
14 | #include <linux/of.h> | |
15 | #include <linux/of_address.h> | |
16 | #include <linux/platform_device.h> | |
17 | #include <linux/slab.h> | |
18 | #include <dt-bindings/clock/axis,artpec6-clkctrl.h> | |
19 | ||
20 | #define NUM_I2S_CLOCKS 2 | |
21 | ||
22 | struct artpec6_clkctrl_drvdata { | |
23 | struct clk *clk_table[ARTPEC6_CLK_NUMCLOCKS]; | |
24 | void __iomem *syscon_base; | |
25 | struct clk_onecell_data clk_data; | |
26 | spinlock_t i2scfg_lock; | |
27 | }; | |
28 | ||
29 | static struct artpec6_clkctrl_drvdata *clkdata; | |
30 | ||
31 | static const char *const i2s_clk_names[NUM_I2S_CLOCKS] = { | |
32 | "i2s0", | |
33 | "i2s1", | |
34 | }; | |
35 | ||
36 | static const int i2s_clk_indexes[NUM_I2S_CLOCKS] = { | |
37 | ARTPEC6_CLK_I2S0_CLK, | |
38 | ARTPEC6_CLK_I2S1_CLK, | |
39 | }; | |
40 | ||
41 | static void of_artpec6_clkctrl_setup(struct device_node *np) | |
42 | { | |
43 | int i; | |
44 | const char *sys_refclk_name; | |
45 | u32 pll_mode, pll_m, pll_n; | |
46 | struct clk **clks; | |
47 | ||
48 | /* Mandatory parent clock. */ | |
49 | i = of_property_match_string(np, "clock-names", "sys_refclk"); | |
50 | if (i < 0) | |
51 | return; | |
52 | ||
53 | sys_refclk_name = of_clk_get_parent_name(np, i); | |
54 | ||
55 | clkdata = kzalloc(sizeof(*clkdata), GFP_KERNEL); | |
56 | if (!clkdata) | |
57 | return; | |
58 | ||
59 | clks = clkdata->clk_table; | |
60 | ||
61 | for (i = 0; i < ARTPEC6_CLK_NUMCLOCKS; ++i) | |
62 | clks[i] = ERR_PTR(-EPROBE_DEFER); | |
63 | ||
64 | clkdata->syscon_base = of_iomap(np, 0); | |
65 | BUG_ON(clkdata->syscon_base == NULL); | |
66 | ||
67 | /* Read PLL1 factors configured by boot strap pins. */ | |
68 | pll_mode = (readl(clkdata->syscon_base) >> 6) & 3; | |
69 | switch (pll_mode) { | |
70 | case 0: /* DDR3-2133 mode */ | |
71 | pll_m = 4; | |
72 | pll_n = 85; | |
73 | break; | |
74 | case 1: /* DDR3-1866 mode */ | |
75 | pll_m = 6; | |
76 | pll_n = 112; | |
77 | break; | |
78 | case 2: /* DDR3-1600 mode */ | |
79 | pll_m = 4; | |
80 | pll_n = 64; | |
81 | break; | |
82 | case 3: /* DDR3-1333 mode */ | |
83 | pll_m = 8; | |
84 | pll_n = 106; | |
85 | break; | |
86 | } | |
87 | ||
88 | clks[ARTPEC6_CLK_CPU] = | |
89 | clk_register_fixed_factor(NULL, "cpu", sys_refclk_name, 0, pll_n, | |
90 | pll_m); | |
91 | clks[ARTPEC6_CLK_CPU_PERIPH] = | |
92 | clk_register_fixed_factor(NULL, "cpu_periph", "cpu", 0, 1, 2); | |
93 | ||
94 | /* EPROBE_DEFER on the apb_clock is not handled in amba devices. */ | |
95 | clks[ARTPEC6_CLK_UART_PCLK] = | |
96 | clk_register_fixed_factor(NULL, "uart_pclk", "cpu", 0, 1, 8); | |
97 | clks[ARTPEC6_CLK_UART_REFCLK] = | |
98 | clk_register_fixed_rate(NULL, "uart_ref", sys_refclk_name, 0, | |
99 | 50000000); | |
100 | ||
101 | clks[ARTPEC6_CLK_SPI_PCLK] = | |
102 | clk_register_fixed_factor(NULL, "spi_pclk", "cpu", 0, 1, 8); | |
103 | clks[ARTPEC6_CLK_SPI_SSPCLK] = | |
104 | clk_register_fixed_rate(NULL, "spi_sspclk", sys_refclk_name, 0, | |
105 | 50000000); | |
106 | ||
107 | clks[ARTPEC6_CLK_DBG_PCLK] = | |
108 | clk_register_fixed_factor(NULL, "dbg_pclk", "cpu", 0, 1, 8); | |
109 | ||
110 | clkdata->clk_data.clks = clkdata->clk_table; | |
111 | clkdata->clk_data.clk_num = ARTPEC6_CLK_NUMCLOCKS; | |
112 | ||
113 | of_clk_add_provider(np, of_clk_src_onecell_get, &clkdata->clk_data); | |
114 | } | |
115 | ||
aa8d7122 RRD |
116 | CLK_OF_DECLARE_DRIVER(artpec6_clkctrl, "axis,artpec6-clkctrl", |
117 | of_artpec6_clkctrl_setup); | |
33b8ac91 LP |
118 | |
119 | static int artpec6_clkctrl_probe(struct platform_device *pdev) | |
120 | { | |
121 | int propidx; | |
122 | struct device_node *np = pdev->dev.of_node; | |
123 | struct device *dev = &pdev->dev; | |
124 | struct clk **clks = clkdata->clk_table; | |
125 | const char *sys_refclk_name; | |
126 | const char *i2s_refclk_name = NULL; | |
127 | const char *frac_clk_name[2] = { NULL, NULL }; | |
128 | const char *i2s_mux_parents[2]; | |
129 | u32 muxreg; | |
130 | int i; | |
131 | int err = 0; | |
132 | ||
133 | /* Mandatory parent clock. */ | |
134 | propidx = of_property_match_string(np, "clock-names", "sys_refclk"); | |
135 | if (propidx < 0) | |
136 | return -EINVAL; | |
137 | ||
138 | sys_refclk_name = of_clk_get_parent_name(np, propidx); | |
139 | ||
140 | /* Find clock names of optional parent clocks. */ | |
141 | propidx = of_property_match_string(np, "clock-names", "i2s_refclk"); | |
142 | if (propidx >= 0) | |
143 | i2s_refclk_name = of_clk_get_parent_name(np, propidx); | |
144 | ||
145 | propidx = of_property_match_string(np, "clock-names", "frac_clk0"); | |
146 | if (propidx >= 0) | |
147 | frac_clk_name[0] = of_clk_get_parent_name(np, propidx); | |
148 | propidx = of_property_match_string(np, "clock-names", "frac_clk1"); | |
149 | if (propidx >= 0) | |
150 | frac_clk_name[1] = of_clk_get_parent_name(np, propidx); | |
151 | ||
152 | spin_lock_init(&clkdata->i2scfg_lock); | |
153 | ||
154 | clks[ARTPEC6_CLK_NAND_CLKA] = | |
155 | clk_register_fixed_factor(dev, "nand_clka", "cpu", 0, 1, 8); | |
156 | clks[ARTPEC6_CLK_NAND_CLKB] = | |
157 | clk_register_fixed_rate(dev, "nand_clkb", sys_refclk_name, 0, | |
158 | 100000000); | |
159 | clks[ARTPEC6_CLK_ETH_ACLK] = | |
160 | clk_register_fixed_factor(dev, "eth_aclk", "cpu", 0, 1, 4); | |
161 | clks[ARTPEC6_CLK_DMA_ACLK] = | |
162 | clk_register_fixed_factor(dev, "dma_aclk", "cpu", 0, 1, 4); | |
163 | clks[ARTPEC6_CLK_PTP_REF] = | |
164 | clk_register_fixed_rate(dev, "ptp_ref", sys_refclk_name, 0, | |
165 | 100000000); | |
166 | clks[ARTPEC6_CLK_SD_PCLK] = | |
167 | clk_register_fixed_rate(dev, "sd_pclk", sys_refclk_name, 0, | |
168 | 100000000); | |
169 | clks[ARTPEC6_CLK_SD_IMCLK] = | |
170 | clk_register_fixed_rate(dev, "sd_imclk", sys_refclk_name, 0, | |
171 | 100000000); | |
172 | clks[ARTPEC6_CLK_I2S_HST] = | |
173 | clk_register_fixed_factor(dev, "i2s_hst", "cpu", 0, 1, 8); | |
174 | ||
175 | for (i = 0; i < NUM_I2S_CLOCKS; ++i) { | |
176 | if (i2s_refclk_name && frac_clk_name[i]) { | |
177 | i2s_mux_parents[0] = frac_clk_name[i]; | |
178 | i2s_mux_parents[1] = i2s_refclk_name; | |
179 | ||
180 | clks[i2s_clk_indexes[i]] = | |
181 | clk_register_mux(dev, i2s_clk_names[i], | |
182 | i2s_mux_parents, 2, | |
183 | CLK_SET_RATE_NO_REPARENT | | |
184 | CLK_SET_RATE_PARENT, | |
185 | clkdata->syscon_base + 0x14, i, 1, | |
186 | 0, &clkdata->i2scfg_lock); | |
187 | } else if (frac_clk_name[i]) { | |
188 | /* Lock the mux for internal clock reference. */ | |
189 | muxreg = readl(clkdata->syscon_base + 0x14); | |
190 | muxreg &= ~BIT(i); | |
191 | writel(muxreg, clkdata->syscon_base + 0x14); | |
192 | clks[i2s_clk_indexes[i]] = | |
193 | clk_register_fixed_factor(dev, i2s_clk_names[i], | |
194 | frac_clk_name[i], 0, 1, | |
195 | 1); | |
196 | } else if (i2s_refclk_name) { | |
197 | /* Lock the mux for external clock reference. */ | |
198 | muxreg = readl(clkdata->syscon_base + 0x14); | |
199 | muxreg |= BIT(i); | |
200 | writel(muxreg, clkdata->syscon_base + 0x14); | |
201 | clks[i2s_clk_indexes[i]] = | |
202 | clk_register_fixed_factor(dev, i2s_clk_names[i], | |
203 | i2s_refclk_name, 0, 1, 1); | |
204 | } | |
205 | } | |
206 | ||
207 | clks[ARTPEC6_CLK_I2C] = | |
208 | clk_register_fixed_rate(dev, "i2c", sys_refclk_name, 0, 100000000); | |
209 | ||
210 | clks[ARTPEC6_CLK_SYS_TIMER] = | |
211 | clk_register_fixed_rate(dev, "timer", sys_refclk_name, 0, | |
212 | 100000000); | |
213 | clks[ARTPEC6_CLK_FRACDIV_IN] = | |
214 | clk_register_fixed_rate(dev, "fracdiv_in", sys_refclk_name, 0, | |
215 | 600000000); | |
216 | ||
217 | for (i = 0; i < ARTPEC6_CLK_NUMCLOCKS; ++i) { | |
218 | if (IS_ERR(clks[i]) && PTR_ERR(clks[i]) != -EPROBE_DEFER) { | |
219 | dev_err(dev, | |
220 | "Failed to register clock at index %d err=%ld\n", | |
221 | i, PTR_ERR(clks[i])); | |
222 | err = PTR_ERR(clks[i]); | |
223 | } | |
224 | } | |
225 | ||
226 | return err; | |
227 | } | |
228 | ||
229 | static const struct of_device_id artpec_clkctrl_of_match[] = { | |
230 | { .compatible = "axis,artpec6-clkctrl" }, | |
231 | {} | |
232 | }; | |
233 | ||
234 | static struct platform_driver artpec6_clkctrl_driver = { | |
235 | .probe = artpec6_clkctrl_probe, | |
236 | .driver = { | |
237 | .name = "artpec6_clkctrl", | |
238 | .of_match_table = artpec_clkctrl_of_match, | |
239 | }, | |
240 | }; | |
241 | ||
242 | builtin_platform_driver(artpec6_clkctrl_driver); |