1 // SPDX-License-Identifier: GPL-2.0-only
3 * ARTPEC-6 clock initialization
5 * Copyright 2015-2016 Axis Comunications AB.
8 #include <linux/clk-provider.h>
9 #include <linux/device.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>
17 #define NUM_I2S_CLOCKS 2
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
;
26 static struct artpec6_clkctrl_drvdata
*clkdata
;
28 static const char *const i2s_clk_names
[NUM_I2S_CLOCKS
] = {
33 static const int i2s_clk_indexes
[NUM_I2S_CLOCKS
] = {
38 static void of_artpec6_clkctrl_setup(struct device_node
*np
)
41 const char *sys_refclk_name
;
42 u32 pll_mode
, pll_m
, pll_n
;
45 /* Mandatory parent clock. */
46 i
= of_property_match_string(np
, "clock-names", "sys_refclk");
50 sys_refclk_name
= of_clk_get_parent_name(np
, i
);
52 clkdata
= kzalloc(sizeof(*clkdata
), GFP_KERNEL
);
56 clks
= clkdata
->clk_table
;
58 for (i
= 0; i
< ARTPEC6_CLK_NUMCLOCKS
; ++i
)
59 clks
[i
] = ERR_PTR(-EPROBE_DEFER
);
61 clkdata
->syscon_base
= of_iomap(np
, 0);
62 BUG_ON(clkdata
->syscon_base
== NULL
);
64 /* Read PLL1 factors configured by boot strap pins. */
65 pll_mode
= (readl(clkdata
->syscon_base
) >> 6) & 3;
67 case 0: /* DDR3-2133 mode */
71 case 1: /* DDR3-1866 mode */
75 case 2: /* DDR3-1600 mode */
79 case 3: /* DDR3-1333 mode */
85 clks
[ARTPEC6_CLK_CPU
] =
86 clk_register_fixed_factor(NULL
, "cpu", sys_refclk_name
, 0, pll_n
,
88 clks
[ARTPEC6_CLK_CPU_PERIPH
] =
89 clk_register_fixed_factor(NULL
, "cpu_periph", "cpu", 0, 1, 2);
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,
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,
104 clks
[ARTPEC6_CLK_DBG_PCLK
] =
105 clk_register_fixed_factor(NULL
, "dbg_pclk", "cpu", 0, 1, 8);
107 clkdata
->clk_data
.clks
= clkdata
->clk_table
;
108 clkdata
->clk_data
.clk_num
= ARTPEC6_CLK_NUMCLOCKS
;
110 of_clk_add_provider(np
, of_clk_src_onecell_get
, &clkdata
->clk_data
);
113 CLK_OF_DECLARE_DRIVER(artpec6_clkctrl
, "axis,artpec6-clkctrl",
114 of_artpec6_clkctrl_setup
);
116 static int artpec6_clkctrl_probe(struct platform_device
*pdev
)
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];
130 /* Mandatory parent clock. */
131 propidx
= of_property_match_string(np
, "clock-names", "sys_refclk");
135 sys_refclk_name
= of_clk_get_parent_name(np
, propidx
);
137 /* Find clock names of optional parent clocks. */
138 propidx
= of_property_match_string(np
, "clock-names", "i2s_refclk");
140 i2s_refclk_name
= of_clk_get_parent_name(np
, propidx
);
142 propidx
= of_property_match_string(np
, "clock-names", "frac_clk0");
144 frac_clk_name
[0] = of_clk_get_parent_name(np
, propidx
);
145 propidx
= of_property_match_string(np
, "clock-names", "frac_clk1");
147 frac_clk_name
[1] = of_clk_get_parent_name(np
, propidx
);
149 spin_lock_init(&clkdata
->i2scfg_lock
);
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,
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,
163 clks
[ARTPEC6_CLK_SD_PCLK
] =
164 clk_register_fixed_rate(dev
, "sd_pclk", sys_refclk_name
, 0,
166 clks
[ARTPEC6_CLK_SD_IMCLK
] =
167 clk_register_fixed_rate(dev
, "sd_imclk", sys_refclk_name
, 0,
169 clks
[ARTPEC6_CLK_I2S_HST
] =
170 clk_register_fixed_factor(dev
, "i2s_hst", "cpu", 0, 1, 8);
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
;
177 clks
[i2s_clk_indexes
[i
]] =
178 clk_register_mux(dev
, i2s_clk_names
[i
],
180 CLK_SET_RATE_NO_REPARENT
|
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);
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,
193 } else if (i2s_refclk_name
) {
194 /* Lock the mux for external clock reference. */
195 muxreg
= readl(clkdata
->syscon_base
+ 0x14);
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);
204 clks
[ARTPEC6_CLK_I2C
] =
205 clk_register_fixed_rate(dev
, "i2c", sys_refclk_name
, 0, 100000000);
207 clks
[ARTPEC6_CLK_SYS_TIMER
] =
208 clk_register_fixed_rate(dev
, "timer", sys_refclk_name
, 0,
210 clks
[ARTPEC6_CLK_FRACDIV_IN
] =
211 clk_register_fixed_rate(dev
, "fracdiv_in", sys_refclk_name
, 0,
214 for (i
= 0; i
< ARTPEC6_CLK_NUMCLOCKS
; ++i
) {
215 if (IS_ERR(clks
[i
]) && PTR_ERR(clks
[i
]) != -EPROBE_DEFER
) {
217 "Failed to register clock at index %d err=%ld\n",
218 i
, PTR_ERR(clks
[i
]));
219 err
= PTR_ERR(clks
[i
]);
226 static const struct of_device_id artpec_clkctrl_of_match
[] = {
227 { .compatible
= "axis,artpec6-clkctrl" },
231 static struct platform_driver artpec6_clkctrl_driver
= {
232 .probe
= artpec6_clkctrl_probe
,
234 .name
= "artpec6_clkctrl",
235 .of_match_table
= artpec_clkctrl_of_match
,
239 builtin_platform_driver(artpec6_clkctrl_driver
);