diff options
author | Emilio López <emilio@elopez.com.ar> | 2013-02-25 09:44:26 -0500 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2013-03-27 11:35:34 -0400 |
commit | e874a6697710f52fa8ab29487a99034d5d96fdcc (patch) | |
tree | 1743abc90ee2d604fe5f8a0f4b69f171179ced31 | |
parent | b54891685162e09c4292cd38f067ca118604353c (diff) |
clk: arm: sunxi: Add a new clock driver for sunxi SOCs
This commit implements the base CPU clocks for sunxi devices. It has
been tested using a slightly modified cpufreq driver from the
linux-sunxi 3.0 tree.
Additionally, document the new bindings introduced by this patch.
Idling:
/ # cat /sys/kernel/debug/clk/clk_summary
clock enable_cnt prepare_cnt rate
---------------------------------------------------------------------
osc32k 0 0 32768
osc24M_fixed 0 0 24000000
osc24M 0 0 24000000
apb1_mux 0 0 24000000
apb1 0 0 24000000
pll1 0 0 60000000
cpu 0 0 60000000
axi 0 0 60000000
ahb 0 0 60000000
apb0 0 0 30000000
dummy 0 0 0
After "yes >/dev/null &":
/ # cat /sys/kernel/debug/clk/clk_summary
clock enable_cnt prepare_cnt rate
---------------------------------------------------------------------
osc32k 0 0 32768
osc24M_fixed 0 0 24000000
osc24M 0 0 24000000
apb1_mux 0 0 24000000
apb1 0 0 24000000
pll1 0 0 1008000000
cpu 0 0 1008000000
axi 0 0 336000000
ahb 0 0 168000000
apb0 0 0 84000000
dummy 0 0 0
Signed-off-by: Emilio López <emilio@elopez.com.ar>
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
-rw-r--r-- | Documentation/devicetree/bindings/clock/sunxi.txt | 44 | ||||
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/sunxi/Makefile | 5 | ||||
-rw-r--r-- | drivers/clk/sunxi/clk-factors.c | 180 | ||||
-rw-r--r-- | drivers/clk/sunxi/clk-factors.h | 27 | ||||
-rw-r--r-- | drivers/clk/sunxi/clk-sunxi.c | 362 | ||||
-rw-r--r-- | drivers/clocksource/sunxi_timer.c | 4 | ||||
-rw-r--r-- | include/linux/clk/sunxi.h | 22 |
8 files changed, 643 insertions, 2 deletions
diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt new file mode 100644 index 000000000000..b23cfbdbcd6d --- /dev/null +++ b/Documentation/devicetree/bindings/clock/sunxi.txt | |||
@@ -0,0 +1,44 @@ | |||
1 | Device Tree Clock bindings for arch-sunxi | ||
2 | |||
3 | This binding uses the common clock binding[1]. | ||
4 | |||
5 | [1] Documentation/devicetree/bindings/clock/clock-bindings.txt | ||
6 | |||
7 | Required properties: | ||
8 | - compatible : shall be one of the following: | ||
9 | "allwinner,sunxi-osc-clk" - for a gatable oscillator | ||
10 | "allwinner,sunxi-pll1-clk" - for the main PLL clock | ||
11 | "allwinner,sunxi-cpu-clk" - for the CPU multiplexer clock | ||
12 | "allwinner,sunxi-axi-clk" - for the sunxi AXI clock | ||
13 | "allwinner,sunxi-ahb-clk" - for the sunxi AHB clock | ||
14 | "allwinner,sunxi-apb0-clk" - for the sunxi APB0 clock | ||
15 | "allwinner,sunxi-apb1-clk" - for the sunxi APB1 clock | ||
16 | "allwinner,sunxi-apb1-mux-clk" - for the sunxi APB1 clock muxing | ||
17 | |||
18 | Required properties for all clocks: | ||
19 | - reg : shall be the control register address for the clock. | ||
20 | - clocks : shall be the input parent clock(s) phandle for the clock | ||
21 | - #clock-cells : from common clock binding; shall be set to 0. | ||
22 | |||
23 | For example: | ||
24 | |||
25 | osc24M: osc24M@01c20050 { | ||
26 | #clock-cells = <0>; | ||
27 | compatible = "allwinner,sunxi-osc-clk"; | ||
28 | reg = <0x01c20050 0x4>; | ||
29 | clocks = <&osc24M_fixed>; | ||
30 | }; | ||
31 | |||
32 | pll1: pll1@01c20000 { | ||
33 | #clock-cells = <0>; | ||
34 | compatible = "allwinner,sunxi-pll1-clk"; | ||
35 | reg = <0x01c20000 0x4>; | ||
36 | clocks = <&osc24M>; | ||
37 | }; | ||
38 | |||
39 | cpu: cpu@01c20054 { | ||
40 | #clock-cells = <0>; | ||
41 | compatible = "allwinner,sunxi-cpu-clk"; | ||
42 | reg = <0x01c20054 0x4>; | ||
43 | clocks = <&osc32k>, <&osc24M>, <&pll1>; | ||
44 | }; | ||
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 41cb123a2d02..79e98e416724 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile | |||
@@ -24,6 +24,7 @@ ifeq ($(CONFIG_COMMON_CLK), y) | |||
24 | obj-$(CONFIG_ARCH_MMP) += mmp/ | 24 | obj-$(CONFIG_ARCH_MMP) += mmp/ |
25 | endif | 25 | endif |
26 | obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o | 26 | obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o |
27 | obj-$(CONFIG_ARCH_SUNXI) += sunxi/ | ||
27 | obj-$(CONFIG_ARCH_U8500) += ux500/ | 28 | obj-$(CONFIG_ARCH_U8500) += ux500/ |
28 | obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o | 29 | obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o |
29 | obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o | 30 | obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o |
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile new file mode 100644 index 000000000000..b5bac917612c --- /dev/null +++ b/drivers/clk/sunxi/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | # | ||
2 | # Makefile for sunxi specific clk | ||
3 | # | ||
4 | |||
5 | obj-y += clk-sunxi.o clk-factors.o | ||
diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c new file mode 100644 index 000000000000..88523f91d9b7 --- /dev/null +++ b/drivers/clk/sunxi/clk-factors.c | |||
@@ -0,0 +1,180 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 Emilio López <emilio@elopez.com.ar> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * Adjustable factor-based clock implementation | ||
9 | */ | ||
10 | |||
11 | #include <linux/clk-provider.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/string.h> | ||
17 | |||
18 | #include <linux/delay.h> | ||
19 | |||
20 | #include "clk-factors.h" | ||
21 | |||
22 | /* | ||
23 | * DOC: basic adjustable factor-based clock that cannot gate | ||
24 | * | ||
25 | * Traits of this clock: | ||
26 | * prepare - clk_prepare only ensures that parents are prepared | ||
27 | * enable - clk_enable only ensures that parents are enabled | ||
28 | * rate - rate is adjustable. | ||
29 | * clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1) | ||
30 | * parent - fixed parent. No clk_set_parent support | ||
31 | */ | ||
32 | |||
33 | struct clk_factors { | ||
34 | struct clk_hw hw; | ||
35 | void __iomem *reg; | ||
36 | struct clk_factors_config *config; | ||
37 | void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p); | ||
38 | spinlock_t *lock; | ||
39 | }; | ||
40 | |||
41 | #define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) | ||
42 | |||
43 | #define SETMASK(len, pos) (((-1U) >> (31-len)) << (pos)) | ||
44 | #define CLRMASK(len, pos) (~(SETMASK(len, pos))) | ||
45 | #define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit)) | ||
46 | |||
47 | #define FACTOR_SET(bit, len, reg, val) \ | ||
48 | (((reg) & CLRMASK(len, bit)) | (val << (bit))) | ||
49 | |||
50 | static unsigned long clk_factors_recalc_rate(struct clk_hw *hw, | ||
51 | unsigned long parent_rate) | ||
52 | { | ||
53 | u8 n = 1, k = 0, p = 0, m = 0; | ||
54 | u32 reg; | ||
55 | unsigned long rate; | ||
56 | struct clk_factors *factors = to_clk_factors(hw); | ||
57 | struct clk_factors_config *config = factors->config; | ||
58 | |||
59 | /* Fetch the register value */ | ||
60 | reg = readl(factors->reg); | ||
61 | |||
62 | /* Get each individual factor if applicable */ | ||
63 | if (config->nwidth != SUNXI_FACTORS_NOT_APPLICABLE) | ||
64 | n = FACTOR_GET(config->nshift, config->nwidth, reg); | ||
65 | if (config->kwidth != SUNXI_FACTORS_NOT_APPLICABLE) | ||
66 | k = FACTOR_GET(config->kshift, config->kwidth, reg); | ||
67 | if (config->mwidth != SUNXI_FACTORS_NOT_APPLICABLE) | ||
68 | m = FACTOR_GET(config->mshift, config->mwidth, reg); | ||
69 | if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE) | ||
70 | p = FACTOR_GET(config->pshift, config->pwidth, reg); | ||
71 | |||
72 | /* Calculate the rate */ | ||
73 | rate = (parent_rate * n * (k + 1) >> p) / (m + 1); | ||
74 | |||
75 | return rate; | ||
76 | } | ||
77 | |||
78 | static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate, | ||
79 | unsigned long *parent_rate) | ||
80 | { | ||
81 | struct clk_factors *factors = to_clk_factors(hw); | ||
82 | factors->get_factors((u32 *)&rate, (u32)*parent_rate, | ||
83 | NULL, NULL, NULL, NULL); | ||
84 | |||
85 | return rate; | ||
86 | } | ||
87 | |||
88 | static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, | ||
89 | unsigned long parent_rate) | ||
90 | { | ||
91 | u8 n, k, m, p; | ||
92 | u32 reg; | ||
93 | struct clk_factors *factors = to_clk_factors(hw); | ||
94 | struct clk_factors_config *config = factors->config; | ||
95 | unsigned long flags = 0; | ||
96 | |||
97 | factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p); | ||
98 | |||
99 | if (factors->lock) | ||
100 | spin_lock_irqsave(factors->lock, flags); | ||
101 | |||
102 | /* Fetch the register value */ | ||
103 | reg = readl(factors->reg); | ||
104 | |||
105 | /* Set up the new factors - macros do not do anything if width is 0 */ | ||
106 | reg = FACTOR_SET(config->nshift, config->nwidth, reg, n); | ||
107 | reg = FACTOR_SET(config->kshift, config->kwidth, reg, k); | ||
108 | reg = FACTOR_SET(config->mshift, config->mwidth, reg, m); | ||
109 | reg = FACTOR_SET(config->pshift, config->pwidth, reg, p); | ||
110 | |||
111 | /* Apply them now */ | ||
112 | writel(reg, factors->reg); | ||
113 | |||
114 | /* delay 500us so pll stabilizes */ | ||
115 | __delay((rate >> 20) * 500 / 2); | ||
116 | |||
117 | if (factors->lock) | ||
118 | spin_unlock_irqrestore(factors->lock, flags); | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static const struct clk_ops clk_factors_ops = { | ||
124 | .recalc_rate = clk_factors_recalc_rate, | ||
125 | .round_rate = clk_factors_round_rate, | ||
126 | .set_rate = clk_factors_set_rate, | ||
127 | }; | ||
128 | |||
129 | /** | ||
130 | * clk_register_factors - register a factors clock with | ||
131 | * the clock framework | ||
132 | * @dev: device registering this clock | ||
133 | * @name: name of this clock | ||
134 | * @parent_name: name of clock's parent | ||
135 | * @flags: framework-specific flags | ||
136 | * @reg: register address to adjust factors | ||
137 | * @config: shift and width of factors n, k, m and p | ||
138 | * @get_factors: function to calculate the factors for a given frequency | ||
139 | * @lock: shared register lock for this clock | ||
140 | */ | ||
141 | struct clk *clk_register_factors(struct device *dev, const char *name, | ||
142 | const char *parent_name, | ||
143 | unsigned long flags, void __iomem *reg, | ||
144 | struct clk_factors_config *config, | ||
145 | void (*get_factors)(u32 *rate, u32 parent, | ||
146 | u8 *n, u8 *k, u8 *m, u8 *p), | ||
147 | spinlock_t *lock) | ||
148 | { | ||
149 | struct clk_factors *factors; | ||
150 | struct clk *clk; | ||
151 | struct clk_init_data init; | ||
152 | |||
153 | /* allocate the factors */ | ||
154 | factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); | ||
155 | if (!factors) { | ||
156 | pr_err("%s: could not allocate factors clk\n", __func__); | ||
157 | return ERR_PTR(-ENOMEM); | ||
158 | } | ||
159 | |||
160 | init.name = name; | ||
161 | init.ops = &clk_factors_ops; | ||
162 | init.flags = flags; | ||
163 | init.parent_names = (parent_name ? &parent_name : NULL); | ||
164 | init.num_parents = (parent_name ? 1 : 0); | ||
165 | |||
166 | /* struct clk_factors assignments */ | ||
167 | factors->reg = reg; | ||
168 | factors->config = config; | ||
169 | factors->lock = lock; | ||
170 | factors->hw.init = &init; | ||
171 | factors->get_factors = get_factors; | ||
172 | |||
173 | /* register the clock */ | ||
174 | clk = clk_register(dev, &factors->hw); | ||
175 | |||
176 | if (IS_ERR(clk)) | ||
177 | kfree(factors); | ||
178 | |||
179 | return clk; | ||
180 | } | ||
diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h new file mode 100644 index 000000000000..f49851cc4380 --- /dev/null +++ b/drivers/clk/sunxi/clk-factors.h | |||
@@ -0,0 +1,27 @@ | |||
1 | #ifndef __MACH_SUNXI_CLK_FACTORS_H | ||
2 | #define __MACH_SUNXI_CLK_FACTORS_H | ||
3 | |||
4 | #include <linux/clk-provider.h> | ||
5 | #include <linux/clkdev.h> | ||
6 | |||
7 | #define SUNXI_FACTORS_NOT_APPLICABLE (0) | ||
8 | |||
9 | struct clk_factors_config { | ||
10 | u8 nshift; | ||
11 | u8 nwidth; | ||
12 | u8 kshift; | ||
13 | u8 kwidth; | ||
14 | u8 mshift; | ||
15 | u8 mwidth; | ||
16 | u8 pshift; | ||
17 | u8 pwidth; | ||
18 | }; | ||
19 | |||
20 | struct clk *clk_register_factors(struct device *dev, const char *name, | ||
21 | const char *parent_name, | ||
22 | unsigned long flags, void __iomem *reg, | ||
23 | struct clk_factors_config *config, | ||
24 | void (*get_factors) (u32 *rate, u32 parent_rate, | ||
25 | u8 *n, u8 *k, u8 *m, u8 *p), | ||
26 | spinlock_t *lock); | ||
27 | #endif | ||
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c new file mode 100644 index 000000000000..d4ad1c22859e --- /dev/null +++ b/drivers/clk/sunxi/clk-sunxi.c | |||
@@ -0,0 +1,362 @@ | |||
1 | /* | ||
2 | * Copyright 2013 Emilio López | ||
3 | * | ||
4 | * Emilio López <emilio@elopez.com.ar> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | */ | ||
16 | |||
17 | #include <linux/clk-provider.h> | ||
18 | #include <linux/clkdev.h> | ||
19 | #include <linux/clk/sunxi.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <linux/of_address.h> | ||
22 | |||
23 | #include "clk-factors.h" | ||
24 | |||
25 | static DEFINE_SPINLOCK(clk_lock); | ||
26 | |||
27 | /** | ||
28 | * sunxi_osc_clk_setup() - Setup function for gatable oscillator | ||
29 | */ | ||
30 | |||
31 | #define SUNXI_OSC24M_GATE 0 | ||
32 | |||
33 | static void __init sunxi_osc_clk_setup(struct device_node *node) | ||
34 | { | ||
35 | struct clk *clk; | ||
36 | const char *clk_name = node->name; | ||
37 | const char *parent; | ||
38 | void *reg; | ||
39 | |||
40 | reg = of_iomap(node, 0); | ||
41 | |||
42 | parent = of_clk_get_parent_name(node, 0); | ||
43 | |||
44 | clk = clk_register_gate(NULL, clk_name, parent, CLK_IGNORE_UNUSED, | ||
45 | reg, SUNXI_OSC24M_GATE, 0, &clk_lock); | ||
46 | |||
47 | if (clk) { | ||
48 | of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
49 | clk_register_clkdev(clk, clk_name, NULL); | ||
50 | } | ||
51 | } | ||
52 | |||
53 | |||
54 | |||
55 | /** | ||
56 | * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1 | ||
57 | * PLL1 rate is calculated as follows | ||
58 | * rate = (parent_rate * n * (k + 1) >> p) / (m + 1); | ||
59 | * parent_rate is always 24Mhz | ||
60 | */ | ||
61 | |||
62 | static void sunxi_get_pll1_factors(u32 *freq, u32 parent_rate, | ||
63 | u8 *n, u8 *k, u8 *m, u8 *p) | ||
64 | { | ||
65 | u8 div; | ||
66 | |||
67 | /* Normalize value to a 6M multiple */ | ||
68 | div = *freq / 6000000; | ||
69 | *freq = 6000000 * div; | ||
70 | |||
71 | /* we were called to round the frequency, we can now return */ | ||
72 | if (n == NULL) | ||
73 | return; | ||
74 | |||
75 | /* m is always zero for pll1 */ | ||
76 | *m = 0; | ||
77 | |||
78 | /* k is 1 only on these cases */ | ||
79 | if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000) | ||
80 | *k = 1; | ||
81 | else | ||
82 | *k = 0; | ||
83 | |||
84 | /* p will be 3 for divs under 10 */ | ||
85 | if (div < 10) | ||
86 | *p = 3; | ||
87 | |||
88 | /* p will be 2 for divs between 10 - 20 and odd divs under 32 */ | ||
89 | else if (div < 20 || (div < 32 && (div & 1))) | ||
90 | *p = 2; | ||
91 | |||
92 | /* p will be 1 for even divs under 32, divs under 40 and odd pairs | ||
93 | * of divs between 40-62 */ | ||
94 | else if (div < 40 || (div < 64 && (div & 2))) | ||
95 | *p = 1; | ||
96 | |||
97 | /* any other entries have p = 0 */ | ||
98 | else | ||
99 | *p = 0; | ||
100 | |||
101 | /* calculate a suitable n based on k and p */ | ||
102 | div <<= *p; | ||
103 | div /= (*k + 1); | ||
104 | *n = div / 4; | ||
105 | } | ||
106 | |||
107 | |||
108 | |||
109 | /** | ||
110 | * sunxi_get_apb1_factors() - calculates m, p factors for APB1 | ||
111 | * APB1 rate is calculated as follows | ||
112 | * rate = (parent_rate >> p) / (m + 1); | ||
113 | */ | ||
114 | |||
115 | static void sunxi_get_apb1_factors(u32 *freq, u32 parent_rate, | ||
116 | u8 *n, u8 *k, u8 *m, u8 *p) | ||
117 | { | ||
118 | u8 calcm, calcp; | ||
119 | |||
120 | if (parent_rate < *freq) | ||
121 | *freq = parent_rate; | ||
122 | |||
123 | parent_rate = (parent_rate + (*freq - 1)) / *freq; | ||
124 | |||
125 | /* Invalid rate! */ | ||
126 | if (parent_rate > 32) | ||
127 | return; | ||
128 | |||
129 | if (parent_rate <= 4) | ||
130 | calcp = 0; | ||
131 | else if (parent_rate <= 8) | ||
132 | calcp = 1; | ||
133 | else if (parent_rate <= 16) | ||
134 | calcp = 2; | ||
135 | else | ||
136 | calcp = 3; | ||
137 | |||
138 | calcm = (parent_rate >> calcp) - 1; | ||
139 | |||
140 | *freq = (parent_rate >> calcp) / (calcm + 1); | ||
141 | |||
142 | /* we were called to round the frequency, we can now return */ | ||
143 | if (n == NULL) | ||
144 | return; | ||
145 | |||
146 | *m = calcm; | ||
147 | *p = calcp; | ||
148 | } | ||
149 | |||
150 | |||
151 | |||
152 | /** | ||
153 | * sunxi_factors_clk_setup() - Setup function for factor clocks | ||
154 | */ | ||
155 | |||
156 | struct factors_data { | ||
157 | struct clk_factors_config *table; | ||
158 | void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); | ||
159 | }; | ||
160 | |||
161 | static struct clk_factors_config pll1_config = { | ||
162 | .nshift = 8, | ||
163 | .nwidth = 5, | ||
164 | .kshift = 4, | ||
165 | .kwidth = 2, | ||
166 | .mshift = 0, | ||
167 | .mwidth = 2, | ||
168 | .pshift = 16, | ||
169 | .pwidth = 2, | ||
170 | }; | ||
171 | |||
172 | static struct clk_factors_config apb1_config = { | ||
173 | .mshift = 0, | ||
174 | .mwidth = 5, | ||
175 | .pshift = 16, | ||
176 | .pwidth = 2, | ||
177 | }; | ||
178 | |||
179 | static const __initconst struct factors_data pll1_data = { | ||
180 | .table = &pll1_config, | ||
181 | .getter = sunxi_get_pll1_factors, | ||
182 | }; | ||
183 | |||
184 | static const __initconst struct factors_data apb1_data = { | ||
185 | .table = &apb1_config, | ||
186 | .getter = sunxi_get_apb1_factors, | ||
187 | }; | ||
188 | |||
189 | static void __init sunxi_factors_clk_setup(struct device_node *node, | ||
190 | struct factors_data *data) | ||
191 | { | ||
192 | struct clk *clk; | ||
193 | const char *clk_name = node->name; | ||
194 | const char *parent; | ||
195 | void *reg; | ||
196 | |||
197 | reg = of_iomap(node, 0); | ||
198 | |||
199 | parent = of_clk_get_parent_name(node, 0); | ||
200 | |||
201 | clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED, | ||
202 | reg, data->table, data->getter, &clk_lock); | ||
203 | |||
204 | if (clk) { | ||
205 | of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
206 | clk_register_clkdev(clk, clk_name, NULL); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | |||
211 | |||
212 | /** | ||
213 | * sunxi_mux_clk_setup() - Setup function for muxes | ||
214 | */ | ||
215 | |||
216 | #define SUNXI_MUX_GATE_WIDTH 2 | ||
217 | |||
218 | struct mux_data { | ||
219 | u8 shift; | ||
220 | }; | ||
221 | |||
222 | static const __initconst struct mux_data cpu_data = { | ||
223 | .shift = 16, | ||
224 | }; | ||
225 | |||
226 | static const __initconst struct mux_data apb1_mux_data = { | ||
227 | .shift = 24, | ||
228 | }; | ||
229 | |||
230 | static void __init sunxi_mux_clk_setup(struct device_node *node, | ||
231 | struct mux_data *data) | ||
232 | { | ||
233 | struct clk *clk; | ||
234 | const char *clk_name = node->name; | ||
235 | const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL); | ||
236 | void *reg; | ||
237 | int i = 0; | ||
238 | |||
239 | reg = of_iomap(node, 0); | ||
240 | |||
241 | while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL) | ||
242 | i++; | ||
243 | |||
244 | clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg, | ||
245 | data->shift, SUNXI_MUX_GATE_WIDTH, | ||
246 | 0, &clk_lock); | ||
247 | |||
248 | if (clk) { | ||
249 | of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
250 | clk_register_clkdev(clk, clk_name, NULL); | ||
251 | } | ||
252 | } | ||
253 | |||
254 | |||
255 | |||
256 | /** | ||
257 | * sunxi_divider_clk_setup() - Setup function for simple divider clocks | ||
258 | */ | ||
259 | |||
260 | #define SUNXI_DIVISOR_WIDTH 2 | ||
261 | |||
262 | struct div_data { | ||
263 | u8 shift; | ||
264 | u8 pow; | ||
265 | }; | ||
266 | |||
267 | static const __initconst struct div_data axi_data = { | ||
268 | .shift = 0, | ||
269 | .pow = 0, | ||
270 | }; | ||
271 | |||
272 | static const __initconst struct div_data ahb_data = { | ||
273 | .shift = 4, | ||
274 | .pow = 1, | ||
275 | }; | ||
276 | |||
277 | static const __initconst struct div_data apb0_data = { | ||
278 | .shift = 8, | ||
279 | .pow = 1, | ||
280 | }; | ||
281 | |||
282 | static void __init sunxi_divider_clk_setup(struct device_node *node, | ||
283 | struct div_data *data) | ||
284 | { | ||
285 | struct clk *clk; | ||
286 | const char *clk_name = node->name; | ||
287 | const char *clk_parent; | ||
288 | void *reg; | ||
289 | |||
290 | reg = of_iomap(node, 0); | ||
291 | |||
292 | clk_parent = of_clk_get_parent_name(node, 0); | ||
293 | |||
294 | clk = clk_register_divider(NULL, clk_name, clk_parent, 0, | ||
295 | reg, data->shift, SUNXI_DIVISOR_WIDTH, | ||
296 | data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0, | ||
297 | &clk_lock); | ||
298 | if (clk) { | ||
299 | of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
300 | clk_register_clkdev(clk, clk_name, NULL); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | |||
305 | /* Matches for of_clk_init */ | ||
306 | static const __initconst struct of_device_id clk_match[] = { | ||
307 | {.compatible = "fixed-clock", .data = of_fixed_clk_setup,}, | ||
308 | {.compatible = "allwinner,sunxi-osc-clk", .data = sunxi_osc_clk_setup,}, | ||
309 | {} | ||
310 | }; | ||
311 | |||
312 | /* Matches for factors clocks */ | ||
313 | static const __initconst struct of_device_id clk_factors_match[] = { | ||
314 | {.compatible = "allwinner,sunxi-pll1-clk", .data = &pll1_data,}, | ||
315 | {.compatible = "allwinner,sunxi-apb1-clk", .data = &apb1_data,}, | ||
316 | {} | ||
317 | }; | ||
318 | |||
319 | /* Matches for divider clocks */ | ||
320 | static const __initconst struct of_device_id clk_div_match[] = { | ||
321 | {.compatible = "allwinner,sunxi-axi-clk", .data = &axi_data,}, | ||
322 | {.compatible = "allwinner,sunxi-ahb-clk", .data = &ahb_data,}, | ||
323 | {.compatible = "allwinner,sunxi-apb0-clk", .data = &apb0_data,}, | ||
324 | {} | ||
325 | }; | ||
326 | |||
327 | /* Matches for mux clocks */ | ||
328 | static const __initconst struct of_device_id clk_mux_match[] = { | ||
329 | {.compatible = "allwinner,sunxi-cpu-clk", .data = &cpu_data,}, | ||
330 | {.compatible = "allwinner,sunxi-apb1-mux-clk", .data = &apb1_mux_data,}, | ||
331 | {} | ||
332 | }; | ||
333 | |||
334 | static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match, | ||
335 | void *function) | ||
336 | { | ||
337 | struct device_node *np; | ||
338 | const struct div_data *data; | ||
339 | const struct of_device_id *match; | ||
340 | void (*setup_function)(struct device_node *, const void *) = function; | ||
341 | |||
342 | for_each_matching_node(np, clk_match) { | ||
343 | match = of_match_node(clk_match, np); | ||
344 | data = match->data; | ||
345 | setup_function(np, data); | ||
346 | } | ||
347 | } | ||
348 | |||
349 | void __init sunxi_init_clocks(void) | ||
350 | { | ||
351 | /* Register all the simple sunxi clocks on DT */ | ||
352 | of_clk_init(clk_match); | ||
353 | |||
354 | /* Register factor clocks */ | ||
355 | of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup); | ||
356 | |||
357 | /* Register divider clocks */ | ||
358 | of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup); | ||
359 | |||
360 | /* Register mux clocks */ | ||
361 | of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup); | ||
362 | } | ||
diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sunxi_timer.c index 4086b9167159..0ce85e29769b 100644 --- a/drivers/clocksource/sunxi_timer.c +++ b/drivers/clocksource/sunxi_timer.c | |||
@@ -23,7 +23,7 @@ | |||
23 | #include <linux/of_address.h> | 23 | #include <linux/of_address.h> |
24 | #include <linux/of_irq.h> | 24 | #include <linux/of_irq.h> |
25 | #include <linux/sunxi_timer.h> | 25 | #include <linux/sunxi_timer.h> |
26 | #include <linux/clk-provider.h> | 26 | #include <linux/clk/sunxi.h> |
27 | 27 | ||
28 | #define TIMER_CTL_REG 0x00 | 28 | #define TIMER_CTL_REG 0x00 |
29 | #define TIMER_CTL_ENABLE (1 << 0) | 29 | #define TIMER_CTL_ENABLE (1 << 0) |
@@ -123,7 +123,7 @@ void __init sunxi_timer_init(void) | |||
123 | if (irq <= 0) | 123 | if (irq <= 0) |
124 | panic("Can't parse IRQ"); | 124 | panic("Can't parse IRQ"); |
125 | 125 | ||
126 | of_clk_init(NULL); | 126 | sunxi_init_clocks(); |
127 | 127 | ||
128 | clk = of_clk_get(node, 0); | 128 | clk = of_clk_get(node, 0); |
129 | if (IS_ERR(clk)) | 129 | if (IS_ERR(clk)) |
diff --git a/include/linux/clk/sunxi.h b/include/linux/clk/sunxi.h new file mode 100644 index 000000000000..e074fdd5a236 --- /dev/null +++ b/include/linux/clk/sunxi.h | |||
@@ -0,0 +1,22 @@ | |||
1 | /* | ||
2 | * Copyright 2012 Maxime Ripard | ||
3 | * | ||
4 | * Maxime Ripard <maxime.ripard@free-electrons.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | */ | ||
16 | |||
17 | #ifndef __LINUX_CLK_SUNXI_H_ | ||
18 | #define __LINUX_CLK_SUNXI_H_ | ||
19 | |||
20 | void __init sunxi_init_clocks(void); | ||
21 | |||
22 | #endif | ||