1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ARTPEC-6 clock initialization |
4 | * |
5 | * Copyright 2015-2016 Axis Communications AB. |
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, propname: "clock-names" , string: "sys_refclk" ); |
47 | if (i < 0) |
48 | return; |
49 | |
50 | sys_refclk_name = of_clk_get_parent_name(np, index: i); |
51 | |
52 | clkdata = kzalloc(size: 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(error: -EPROBE_DEFER); |
60 | |
61 | clkdata->syscon_base = of_iomap(node: np, index: 0); |
62 | BUG_ON(clkdata->syscon_base == NULL); |
63 | |
64 | /* Read PLL1 factors configured by boot strap pins. */ |
65 | pll_mode = (readl(addr: 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, name: "cpu" , parent_name: sys_refclk_name, flags: 0, mult: pll_n, |
87 | div: pll_m); |
88 | clks[ARTPEC6_CLK_CPU_PERIPH] = |
89 | clk_register_fixed_factor(NULL, name: "cpu_periph" , parent_name: "cpu" , flags: 0, mult: 1, div: 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, name: "uart_pclk" , parent_name: "cpu" , flags: 0, mult: 1, div: 8); |
94 | clks[ARTPEC6_CLK_UART_REFCLK] = |
95 | clk_register_fixed_rate(NULL, name: "uart_ref" , parent_name: sys_refclk_name, flags: 0, |
96 | fixed_rate: 50000000); |
97 | |
98 | clks[ARTPEC6_CLK_SPI_PCLK] = |
99 | clk_register_fixed_factor(NULL, name: "spi_pclk" , parent_name: "cpu" , flags: 0, mult: 1, div: 8); |
100 | clks[ARTPEC6_CLK_SPI_SSPCLK] = |
101 | clk_register_fixed_rate(NULL, name: "spi_sspclk" , parent_name: sys_refclk_name, flags: 0, |
102 | fixed_rate: 50000000); |
103 | |
104 | clks[ARTPEC6_CLK_DBG_PCLK] = |
105 | clk_register_fixed_factor(NULL, name: "dbg_pclk" , parent_name: "cpu" , flags: 0, mult: 1, div: 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, clk_src_get: of_clk_src_onecell_get, data: &clkdata->clk_data); |
111 | } |
112 | |
113 | CLK_OF_DECLARE_DRIVER(artpec6_clkctrl, "axis,artpec6-clkctrl" , |
114 | of_artpec6_clkctrl_setup); |
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, propname: "clock-names" , string: "sys_refclk" ); |
132 | if (propidx < 0) |
133 | return -EINVAL; |
134 | |
135 | sys_refclk_name = of_clk_get_parent_name(np, index: propidx); |
136 | |
137 | /* Find clock names of optional parent clocks. */ |
138 | propidx = of_property_match_string(np, propname: "clock-names" , string: "i2s_refclk" ); |
139 | if (propidx >= 0) |
140 | i2s_refclk_name = of_clk_get_parent_name(np, index: propidx); |
141 | |
142 | propidx = of_property_match_string(np, propname: "clock-names" , string: "frac_clk0" ); |
143 | if (propidx >= 0) |
144 | frac_clk_name[0] = of_clk_get_parent_name(np, index: propidx); |
145 | propidx = of_property_match_string(np, propname: "clock-names" , string: "frac_clk1" ); |
146 | if (propidx >= 0) |
147 | frac_clk_name[1] = of_clk_get_parent_name(np, index: propidx); |
148 | |
149 | spin_lock_init(&clkdata->i2scfg_lock); |
150 | |
151 | clks[ARTPEC6_CLK_NAND_CLKA] = |
152 | clk_register_fixed_factor(dev, name: "nand_clka" , parent_name: "cpu" , flags: 0, mult: 1, div: 8); |
153 | clks[ARTPEC6_CLK_NAND_CLKB] = |
154 | clk_register_fixed_rate(dev, name: "nand_clkb" , parent_name: sys_refclk_name, flags: 0, |
155 | fixed_rate: 100000000); |
156 | clks[ARTPEC6_CLK_ETH_ACLK] = |
157 | clk_register_fixed_factor(dev, name: "eth_aclk" , parent_name: "cpu" , flags: 0, mult: 1, div: 4); |
158 | clks[ARTPEC6_CLK_DMA_ACLK] = |
159 | clk_register_fixed_factor(dev, name: "dma_aclk" , parent_name: "cpu" , flags: 0, mult: 1, div: 4); |
160 | clks[ARTPEC6_CLK_PTP_REF] = |
161 | clk_register_fixed_rate(dev, name: "ptp_ref" , parent_name: sys_refclk_name, flags: 0, |
162 | fixed_rate: 100000000); |
163 | clks[ARTPEC6_CLK_SD_PCLK] = |
164 | clk_register_fixed_rate(dev, name: "sd_pclk" , parent_name: sys_refclk_name, flags: 0, |
165 | fixed_rate: 100000000); |
166 | clks[ARTPEC6_CLK_SD_IMCLK] = |
167 | clk_register_fixed_rate(dev, name: "sd_imclk" , parent_name: sys_refclk_name, flags: 0, |
168 | fixed_rate: 100000000); |
169 | clks[ARTPEC6_CLK_I2S_HST] = |
170 | clk_register_fixed_factor(dev, name: "i2s_hst" , parent_name: "cpu" , flags: 0, mult: 1, div: 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(addr: clkdata->syscon_base + 0x14); |
187 | muxreg &= ~BIT(i); |
188 | writel(val: muxreg, addr: clkdata->syscon_base + 0x14); |
189 | clks[i2s_clk_indexes[i]] = |
190 | clk_register_fixed_factor(dev, name: i2s_clk_names[i], |
191 | parent_name: frac_clk_name[i], flags: 0, mult: 1, |
192 | div: 1); |
193 | } else if (i2s_refclk_name) { |
194 | /* Lock the mux for external clock reference. */ |
195 | muxreg = readl(addr: clkdata->syscon_base + 0x14); |
196 | muxreg |= BIT(i); |
197 | writel(val: muxreg, addr: clkdata->syscon_base + 0x14); |
198 | clks[i2s_clk_indexes[i]] = |
199 | clk_register_fixed_factor(dev, name: i2s_clk_names[i], |
200 | parent_name: i2s_refclk_name, flags: 0, mult: 1, div: 1); |
201 | } |
202 | } |
203 | |
204 | clks[ARTPEC6_CLK_I2C] = |
205 | clk_register_fixed_rate(dev, name: "i2c" , parent_name: sys_refclk_name, flags: 0, fixed_rate: 100000000); |
206 | |
207 | clks[ARTPEC6_CLK_SYS_TIMER] = |
208 | clk_register_fixed_rate(dev, name: "timer" , parent_name: sys_refclk_name, flags: 0, |
209 | fixed_rate: 100000000); |
210 | clks[ARTPEC6_CLK_FRACDIV_IN] = |
211 | clk_register_fixed_rate(dev, name: "fracdiv_in" , parent_name: sys_refclk_name, flags: 0, |
212 | fixed_rate: 600000000); |
213 | |
214 | for (i = 0; i < ARTPEC6_CLK_NUMCLOCKS; ++i) { |
215 | if (IS_ERR(ptr: clks[i]) && PTR_ERR(ptr: 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(ptr: 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); |
240 | |