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