diff options
-rw-r--r-- | Documentation/devicetree/bindings/clock/sunxi.txt | 13 | ||||
-rw-r--r-- | drivers/clk/sunxi/clk-mod0.c | 131 |
2 files changed, 77 insertions, 67 deletions
diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 9dc4f55a04ad..e4c42276c577 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt | |||
@@ -55,8 +55,7 @@ Required properties: | |||
55 | "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 | 55 | "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 |
56 | "allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23 | 56 | "allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23 |
57 | "allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13 | 57 | "allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13 |
58 | "allwinner,sun4i-a10-mmc-output-clk" - for the MMC output clock on A10 | 58 | "allwinner,sun4i-a10-mmc-clk" - for the MMC clock |
59 | "allwinner,sun4i-a10-mmc-sample-clk" - for the MMC sample clock on A10 | ||
60 | "allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks | 59 | "allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks |
61 | "allwinner,sun8i-a23-mbus-clk" - for the MBUS clock on A23 | 60 | "allwinner,sun8i-a23-mbus-clk" - for the MBUS clock on A23 |
62 | "allwinner,sun7i-a20-out-clk" - for the external output clocks | 61 | "allwinner,sun7i-a20-out-clk" - for the external output clocks |
@@ -95,6 +94,10 @@ For "allwinner,sun6i-a31-pll6-clk", there are 2 outputs. The first output | |||
95 | is the normal PLL6 output, or "pll6". The second output is rate doubled | 94 | is the normal PLL6 output, or "pll6". The second output is rate doubled |
96 | PLL6, or "pll6x2". | 95 | PLL6, or "pll6x2". |
97 | 96 | ||
97 | The "allwinner,sun4i-a10-mmc-clk" has three different outputs: the | ||
98 | main clock, with the ID 0, and the output and sample clocks, with the | ||
99 | IDs 1 and 2, respectively. | ||
100 | |||
98 | For example: | 101 | For example: |
99 | 102 | ||
100 | osc24M: clk@01c20050 { | 103 | osc24M: clk@01c20050 { |
@@ -138,11 +141,11 @@ cpu: cpu@01c20054 { | |||
138 | }; | 141 | }; |
139 | 142 | ||
140 | mmc0_clk: clk@01c20088 { | 143 | mmc0_clk: clk@01c20088 { |
141 | #clock-cells = <0>; | 144 | #clock-cells = <1>; |
142 | compatible = "allwinner,sun4i-mod0-clk"; | 145 | compatible = "allwinner,sun4i-a10-mmc-clk"; |
143 | reg = <0x01c20088 0x4>; | 146 | reg = <0x01c20088 0x4>; |
144 | clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; | 147 | clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; |
145 | clock-output-names = "mmc0"; | 148 | clock-output-names = "mmc0", "mmc0_output", "mmc0_sample"; |
146 | }; | 149 | }; |
147 | 150 | ||
148 | mii_phy_tx_clk: clk@2 { | 151 | mii_phy_tx_clk: clk@2 { |
diff --git a/drivers/clk/sunxi/clk-mod0.c b/drivers/clk/sunxi/clk-mod0.c index bf8fcd8c940e..e37eb6fe19e8 100644 --- a/drivers/clk/sunxi/clk-mod0.c +++ b/drivers/clk/sunxi/clk-mod0.c | |||
@@ -152,14 +152,10 @@ static void __init sun5i_a13_mbus_setup(struct device_node *node) | |||
152 | } | 152 | } |
153 | CLK_OF_DECLARE(sun5i_a13_mbus, "allwinner,sun5i-a13-mbus-clk", sun5i_a13_mbus_setup); | 153 | CLK_OF_DECLARE(sun5i_a13_mbus, "allwinner,sun5i-a13-mbus-clk", sun5i_a13_mbus_setup); |
154 | 154 | ||
155 | struct mmc_phase_data { | ||
156 | u8 offset; | ||
157 | }; | ||
158 | |||
159 | struct mmc_phase { | 155 | struct mmc_phase { |
160 | struct clk_hw hw; | 156 | struct clk_hw hw; |
157 | u8 offset; | ||
161 | void __iomem *reg; | 158 | void __iomem *reg; |
162 | struct mmc_phase_data *data; | ||
163 | spinlock_t *lock; | 159 | spinlock_t *lock; |
164 | }; | 160 | }; |
165 | 161 | ||
@@ -175,7 +171,7 @@ static int mmc_get_phase(struct clk_hw *hw) | |||
175 | u8 delay; | 171 | u8 delay; |
176 | 172 | ||
177 | value = readl(phase->reg); | 173 | value = readl(phase->reg); |
178 | delay = (value >> phase->data->offset) & 0x3; | 174 | delay = (value >> phase->offset) & 0x3; |
179 | 175 | ||
180 | if (!delay) | 176 | if (!delay) |
181 | return 180; | 177 | return 180; |
@@ -263,8 +259,8 @@ static int mmc_set_phase(struct clk_hw *hw, int degrees) | |||
263 | 259 | ||
264 | spin_lock_irqsave(phase->lock, flags); | 260 | spin_lock_irqsave(phase->lock, flags); |
265 | value = readl(phase->reg); | 261 | value = readl(phase->reg); |
266 | value &= ~GENMASK(phase->data->offset + 3, phase->data->offset); | 262 | value &= ~GENMASK(phase->offset + 3, phase->offset); |
267 | value |= delay << phase->data->offset; | 263 | value |= delay << phase->offset; |
268 | writel(value, phase->reg); | 264 | writel(value, phase->reg); |
269 | spin_unlock_irqrestore(phase->lock, flags); | 265 | spin_unlock_irqrestore(phase->lock, flags); |
270 | 266 | ||
@@ -276,66 +272,77 @@ static const struct clk_ops mmc_clk_ops = { | |||
276 | .set_phase = mmc_set_phase, | 272 | .set_phase = mmc_set_phase, |
277 | }; | 273 | }; |
278 | 274 | ||
279 | static void __init sun4i_a10_mmc_phase_setup(struct device_node *node, | 275 | static DEFINE_SPINLOCK(sun4i_a10_mmc_lock); |
280 | struct mmc_phase_data *data) | ||
281 | { | ||
282 | const char *parent_names[1] = { of_clk_get_parent_name(node, 0) }; | ||
283 | struct clk_init_data init = { | ||
284 | .num_parents = 1, | ||
285 | .parent_names = parent_names, | ||
286 | .ops = &mmc_clk_ops, | ||
287 | }; | ||
288 | |||
289 | struct mmc_phase *phase; | ||
290 | struct clk *clk; | ||
291 | |||
292 | phase = kmalloc(sizeof(*phase), GFP_KERNEL); | ||
293 | if (!phase) | ||
294 | return; | ||
295 | |||
296 | phase->hw.init = &init; | ||
297 | 276 | ||
298 | phase->reg = of_iomap(node, 0); | 277 | static void __init sun4i_a10_mmc_setup(struct device_node *node) |
299 | if (!phase->reg) | 278 | { |
300 | goto err_free; | 279 | struct clk_onecell_data *clk_data; |
280 | const char *parent; | ||
281 | void __iomem *reg; | ||
282 | int i; | ||
301 | 283 | ||
302 | phase->data = data; | 284 | reg = of_io_request_and_map(node, 0, of_node_full_name(node)); |
303 | phase->lock = &sun4i_a10_mod0_lock; | 285 | if (IS_ERR(reg)) { |
286 | pr_err("Couldn't map the %s clock registers\n", node->name); | ||
287 | return; | ||
288 | } | ||
304 | 289 | ||
305 | if (of_property_read_string(node, "clock-output-names", &init.name)) | 290 | clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL); |
306 | init.name = node->name; | 291 | if (!clk_data) |
292 | return; | ||
307 | 293 | ||
308 | clk = clk_register(NULL, &phase->hw); | 294 | clk_data->clks = kcalloc(3, sizeof(*clk_data->clks), GFP_KERNEL); |
309 | if (IS_ERR(clk)) | 295 | if (!clk_data->clks) |
310 | goto err_unmap; | 296 | goto err_free_data; |
297 | |||
298 | clk_data->clk_num = 3; | ||
299 | clk_data->clks[0] = sunxi_factors_register(node, | ||
300 | &sun4i_a10_mod0_data, | ||
301 | &sun4i_a10_mmc_lock, reg); | ||
302 | if (!clk_data->clks[0]) | ||
303 | goto err_free_clks; | ||
304 | |||
305 | parent = __clk_get_name(clk_data->clks[0]); | ||
306 | |||
307 | for (i = 1; i < 3; i++) { | ||
308 | struct clk_init_data init = { | ||
309 | .num_parents = 1, | ||
310 | .parent_names = &parent, | ||
311 | .ops = &mmc_clk_ops, | ||
312 | }; | ||
313 | struct mmc_phase *phase; | ||
314 | |||
315 | phase = kmalloc(sizeof(*phase), GFP_KERNEL); | ||
316 | if (!phase) | ||
317 | continue; | ||
318 | |||
319 | phase->hw.init = &init; | ||
320 | phase->reg = reg; | ||
321 | phase->lock = &sun4i_a10_mmc_lock; | ||
322 | |||
323 | if (i == 1) | ||
324 | phase->offset = 8; | ||
325 | else | ||
326 | phase->offset = 20; | ||
327 | |||
328 | if (of_property_read_string_index(node, "clock-output-names", | ||
329 | i, &init.name)) | ||
330 | init.name = node->name; | ||
331 | |||
332 | clk_data->clks[i] = clk_register(NULL, &phase->hw); | ||
333 | if (IS_ERR(clk_data->clks[i])) { | ||
334 | kfree(phase); | ||
335 | continue; | ||
336 | } | ||
337 | } | ||
311 | 338 | ||
312 | of_clk_add_provider(node, of_clk_src_simple_get, clk); | 339 | of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); |
313 | 340 | ||
314 | return; | 341 | return; |
315 | 342 | ||
316 | err_unmap: | 343 | err_free_clks: |
317 | iounmap(phase->reg); | 344 | kfree(clk_data->clks); |
318 | err_free: | 345 | err_free_data: |
319 | kfree(phase); | 346 | kfree(clk_data); |
320 | } | ||
321 | |||
322 | |||
323 | static struct mmc_phase_data mmc_output_clk = { | ||
324 | .offset = 8, | ||
325 | }; | ||
326 | |||
327 | static struct mmc_phase_data mmc_sample_clk = { | ||
328 | .offset = 20, | ||
329 | }; | ||
330 | |||
331 | static void __init sun4i_a10_mmc_output_setup(struct device_node *node) | ||
332 | { | ||
333 | sun4i_a10_mmc_phase_setup(node, &mmc_output_clk); | ||
334 | } | ||
335 | CLK_OF_DECLARE(sun4i_a10_mmc_output, "allwinner,sun4i-a10-mmc-output-clk", sun4i_a10_mmc_output_setup); | ||
336 | |||
337 | static void __init sun4i_a10_mmc_sample_setup(struct device_node *node) | ||
338 | { | ||
339 | sun4i_a10_mmc_phase_setup(node, &mmc_sample_clk); | ||
340 | } | 347 | } |
341 | CLK_OF_DECLARE(sun4i_a10_mmc_sample, "allwinner,sun4i-a10-mmc-sample-clk", sun4i_a10_mmc_sample_setup); | 348 | CLK_OF_DECLARE(sun4i_a10_mmc, "allwinner,sun4i-a10-mmc-clk", sun4i_a10_mmc_setup); |