2 * ARTPEC-6 clock initialization
4 * Copyright 2015-2016 Axis Comunications AB.
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.
11 #include <linux/clk-provider.h>
12 #include <linux/device.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>
20 #define NUM_I2S_CLOCKS 2
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
;
29 static struct artpec6_clkctrl_drvdata
*clkdata
;
31 static const char *const i2s_clk_names
[NUM_I2S_CLOCKS
] = {
36 static const int i2s_clk_indexes
[NUM_I2S_CLOCKS
] = {
41 static void of_artpec6_clkctrl_setup(struct device_node
*np
)
44 const char *sys_refclk_name
;
45 u32 pll_mode
, pll_m
, pll_n
;
48 /* Mandatory parent clock. */
49 i
= of_property_match_string(np
, "clock-names", "sys_refclk");
53 sys_refclk_name
= of_clk_get_parent_name(np
, i
);
55 clkdata
= kzalloc(sizeof(*clkdata
), GFP_KERNEL
);
59 clks
= clkdata
->clk_table
;
61 for (i
= 0; i
< ARTPEC6_CLK_NUMCLOCKS
; ++i
)
62 clks
[i
] = ERR_PTR(-EPROBE_DEFER
);
64 clkdata
->syscon_base
= of_iomap(np
, 0);
65 BUG_ON(clkdata
->syscon_base
== NULL
);
67 /* Read PLL1 factors configured by boot strap pins. */
68 pll_mode
= (readl(clkdata
->syscon_base
) >> 6) & 3;
70 case 0: /* DDR3-2133 mode */
74 case 1: /* DDR3-1866 mode */
78 case 2: /* DDR3-1600 mode */
82 case 3: /* DDR3-1333 mode */
88 clks
[ARTPEC6_CLK_CPU
] =
89 clk_register_fixed_factor(NULL
, "cpu", sys_refclk_name
, 0, pll_n
,
91 clks
[ARTPEC6_CLK_CPU_PERIPH
] =
92 clk_register_fixed_factor(NULL
, "cpu_periph", "cpu", 0, 1, 2);
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,
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,
107 clks
[ARTPEC6_CLK_DBG_PCLK
] =
108 clk_register_fixed_factor(NULL
, "dbg_pclk", "cpu", 0, 1, 8);
110 clkdata
->clk_data
.clks
= clkdata
->clk_table
;
111 clkdata
->clk_data
.clk_num
= ARTPEC6_CLK_NUMCLOCKS
;
113 of_clk_add_provider(np
, of_clk_src_onecell_get
, &clkdata
->clk_data
);
116 CLK_OF_DECLARE_DRIVER(artpec6_clkctrl
, "axis,artpec6-clkctrl",
117 of_artpec6_clkctrl_setup
);
119 static int artpec6_clkctrl_probe(struct platform_device
*pdev
)
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];
133 /* Mandatory parent clock. */
134 propidx
= of_property_match_string(np
, "clock-names", "sys_refclk");
138 sys_refclk_name
= of_clk_get_parent_name(np
, propidx
);
140 /* Find clock names of optional parent clocks. */
141 propidx
= of_property_match_string(np
, "clock-names", "i2s_refclk");
143 i2s_refclk_name
= of_clk_get_parent_name(np
, propidx
);
145 propidx
= of_property_match_string(np
, "clock-names", "frac_clk0");
147 frac_clk_name
[0] = of_clk_get_parent_name(np
, propidx
);
148 propidx
= of_property_match_string(np
, "clock-names", "frac_clk1");
150 frac_clk_name
[1] = of_clk_get_parent_name(np
, propidx
);
152 spin_lock_init(&clkdata
->i2scfg_lock
);
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,
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,
166 clks
[ARTPEC6_CLK_SD_PCLK
] =
167 clk_register_fixed_rate(dev
, "sd_pclk", sys_refclk_name
, 0,
169 clks
[ARTPEC6_CLK_SD_IMCLK
] =
170 clk_register_fixed_rate(dev
, "sd_imclk", sys_refclk_name
, 0,
172 clks
[ARTPEC6_CLK_I2S_HST
] =
173 clk_register_fixed_factor(dev
, "i2s_hst", "cpu", 0, 1, 8);
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
;
180 clks
[i2s_clk_indexes
[i
]] =
181 clk_register_mux(dev
, i2s_clk_names
[i
],
183 CLK_SET_RATE_NO_REPARENT
|
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);
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,
196 } else if (i2s_refclk_name
) {
197 /* Lock the mux for external clock reference. */
198 muxreg
= readl(clkdata
->syscon_base
+ 0x14);
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);
207 clks
[ARTPEC6_CLK_I2C
] =
208 clk_register_fixed_rate(dev
, "i2c", sys_refclk_name
, 0, 100000000);
210 clks
[ARTPEC6_CLK_SYS_TIMER
] =
211 clk_register_fixed_rate(dev
, "timer", sys_refclk_name
, 0,
213 clks
[ARTPEC6_CLK_FRACDIV_IN
] =
214 clk_register_fixed_rate(dev
, "fracdiv_in", sys_refclk_name
, 0,
217 for (i
= 0; i
< ARTPEC6_CLK_NUMCLOCKS
; ++i
) {
218 if (IS_ERR(clks
[i
]) && PTR_ERR(clks
[i
]) != -EPROBE_DEFER
) {
220 "Failed to register clock at index %d err=%ld\n",
221 i
, PTR_ERR(clks
[i
]));
222 err
= PTR_ERR(clks
[i
]);
229 static const struct of_device_id artpec_clkctrl_of_match
[] = {
230 { .compatible
= "axis,artpec6-clkctrl" },
234 static struct platform_driver artpec6_clkctrl_driver
= {
235 .probe
= artpec6_clkctrl_probe
,
237 .name
= "artpec6_clkctrl",
238 .of_match_table
= artpec_clkctrl_of_match
,
242 builtin_platform_driver(artpec6_clkctrl_driver
);