diff options
author | Chen-Yu Tsai <wens@csie.org> | 2015-01-27 14:54:07 -0500 |
---|---|---|
committer | Maxime Ripard <maxime.ripard@free-electrons.com> | 2015-02-23 03:25:54 -0500 |
commit | 71f32f56cb54303a1b6ce6811373f57d87de40d3 (patch) | |
tree | a558860eda063760e331be9389c77f34dc7cde94 | |
parent | 6089ef19c9dadaf0e3378f75eca65af861cd3974 (diff) |
clk: sunxi: Add support for sun9i A80 USB clocks and resets
The USB controller/phy clocks and reset controls are in a separate
address block, unlike previous SoCs where they were in the clock
controller. Also, access to the address block is controlled by a
clock gate to AHB.
Add support for resets requiring a clock to be enabled when
asserting/deasserting the reset controls, and add the sun9i USB
clocks.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
-rw-r--r-- | Documentation/devicetree/bindings/clock/sunxi.txt | 2 | ||||
-rw-r--r-- | drivers/clk/sunxi/clk-usb.c | 43 |
2 files changed, 45 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 60b44285250d..3f1dcd879af7 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt | |||
@@ -66,6 +66,8 @@ Required properties: | |||
66 | "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20 | 66 | "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20 |
67 | "allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13 | 67 | "allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13 |
68 | "allwinner,sun6i-a31-usb-clk" - for usb gates + resets on A31 | 68 | "allwinner,sun6i-a31-usb-clk" - for usb gates + resets on A31 |
69 | "allwinner,sun9i-a80-usb-mod-clk" - for usb gates + resets on A80 | ||
70 | "allwinner,sun9i-a80-usb-phy-clk" - for usb phy gates + resets on A80 | ||
69 | 71 | ||
70 | Required properties for all clocks: | 72 | Required properties for all clocks: |
71 | - reg : shall be the control register address for the clock. | 73 | - reg : shall be the control register address for the clock. |
diff --git a/drivers/clk/sunxi/clk-usb.c b/drivers/clk/sunxi/clk-usb.c index f1dcc8fb5a7d..a86ed2f8d7af 100644 --- a/drivers/clk/sunxi/clk-usb.c +++ b/drivers/clk/sunxi/clk-usb.c | |||
@@ -29,6 +29,7 @@ | |||
29 | struct usb_reset_data { | 29 | struct usb_reset_data { |
30 | void __iomem *reg; | 30 | void __iomem *reg; |
31 | spinlock_t *lock; | 31 | spinlock_t *lock; |
32 | struct clk *clk; | ||
32 | struct reset_controller_dev rcdev; | 33 | struct reset_controller_dev rcdev; |
33 | }; | 34 | }; |
34 | 35 | ||
@@ -41,12 +42,14 @@ static int sunxi_usb_reset_assert(struct reset_controller_dev *rcdev, | |||
41 | unsigned long flags; | 42 | unsigned long flags; |
42 | u32 reg; | 43 | u32 reg; |
43 | 44 | ||
45 | clk_prepare_enable(data->clk); | ||
44 | spin_lock_irqsave(data->lock, flags); | 46 | spin_lock_irqsave(data->lock, flags); |
45 | 47 | ||
46 | reg = readl(data->reg); | 48 | reg = readl(data->reg); |
47 | writel(reg & ~BIT(id), data->reg); | 49 | writel(reg & ~BIT(id), data->reg); |
48 | 50 | ||
49 | spin_unlock_irqrestore(data->lock, flags); | 51 | spin_unlock_irqrestore(data->lock, flags); |
52 | clk_disable_unprepare(data->clk); | ||
50 | 53 | ||
51 | return 0; | 54 | return 0; |
52 | } | 55 | } |
@@ -60,12 +63,14 @@ static int sunxi_usb_reset_deassert(struct reset_controller_dev *rcdev, | |||
60 | unsigned long flags; | 63 | unsigned long flags; |
61 | u32 reg; | 64 | u32 reg; |
62 | 65 | ||
66 | clk_prepare_enable(data->clk); | ||
63 | spin_lock_irqsave(data->lock, flags); | 67 | spin_lock_irqsave(data->lock, flags); |
64 | 68 | ||
65 | reg = readl(data->reg); | 69 | reg = readl(data->reg); |
66 | writel(reg | BIT(id), data->reg); | 70 | writel(reg | BIT(id), data->reg); |
67 | 71 | ||
68 | spin_unlock_irqrestore(data->lock, flags); | 72 | spin_unlock_irqrestore(data->lock, flags); |
73 | clk_disable_unprepare(data->clk); | ||
69 | 74 | ||
70 | return 0; | 75 | return 0; |
71 | } | 76 | } |
@@ -84,6 +89,7 @@ static struct reset_control_ops sunxi_usb_reset_ops = { | |||
84 | struct usb_clk_data { | 89 | struct usb_clk_data { |
85 | u32 clk_mask; | 90 | u32 clk_mask; |
86 | u32 reset_mask; | 91 | u32 reset_mask; |
92 | bool reset_needs_clk; | ||
87 | }; | 93 | }; |
88 | 94 | ||
89 | static void __init sunxi_usb_clk_setup(struct device_node *node, | 95 | static void __init sunxi_usb_clk_setup(struct device_node *node, |
@@ -146,6 +152,15 @@ static void __init sunxi_usb_clk_setup(struct device_node *node, | |||
146 | if (!reset_data) | 152 | if (!reset_data) |
147 | return; | 153 | return; |
148 | 154 | ||
155 | if (data->reset_needs_clk) { | ||
156 | reset_data->clk = of_clk_get(node, 0); | ||
157 | if (IS_ERR(reset_data->clk)) { | ||
158 | pr_err("Could not get clock for reset controls\n"); | ||
159 | kfree(reset_data); | ||
160 | return; | ||
161 | } | ||
162 | } | ||
163 | |||
149 | reset_data->reg = reg; | 164 | reset_data->reg = reg; |
150 | reset_data->lock = lock; | 165 | reset_data->lock = lock; |
151 | reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1; | 166 | reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1; |
@@ -188,3 +203,31 @@ static void __init sun6i_a31_usb_setup(struct device_node *node) | |||
188 | sunxi_usb_clk_setup(node, &sun6i_a31_usb_clk_data, &sun4i_a10_usb_lock); | 203 | sunxi_usb_clk_setup(node, &sun6i_a31_usb_clk_data, &sun4i_a10_usb_lock); |
189 | } | 204 | } |
190 | CLK_OF_DECLARE(sun6i_a31_usb, "allwinner,sun6i-a31-usb-clk", sun6i_a31_usb_setup); | 205 | CLK_OF_DECLARE(sun6i_a31_usb, "allwinner,sun6i-a31-usb-clk", sun6i_a31_usb_setup); |
206 | |||
207 | static const struct usb_clk_data sun9i_a80_usb_mod_data __initconst = { | ||
208 | .clk_mask = BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1), | ||
209 | .reset_mask = BIT(19) | BIT(18) | BIT(17), | ||
210 | .reset_needs_clk = 1, | ||
211 | }; | ||
212 | |||
213 | static DEFINE_SPINLOCK(a80_usb_mod_lock); | ||
214 | |||
215 | static void __init sun9i_a80_usb_mod_setup(struct device_node *node) | ||
216 | { | ||
217 | sunxi_usb_clk_setup(node, &sun9i_a80_usb_mod_data, &a80_usb_mod_lock); | ||
218 | } | ||
219 | CLK_OF_DECLARE(sun9i_a80_usb_mod, "allwinner,sun9i-a80-usb-mod-clk", sun9i_a80_usb_mod_setup); | ||
220 | |||
221 | static const struct usb_clk_data sun9i_a80_usb_phy_data __initconst = { | ||
222 | .clk_mask = BIT(10) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1), | ||
223 | .reset_mask = BIT(21) | BIT(20) | BIT(19) | BIT(18) | BIT(17), | ||
224 | .reset_needs_clk = 1, | ||
225 | }; | ||
226 | |||
227 | static DEFINE_SPINLOCK(a80_usb_phy_lock); | ||
228 | |||
229 | static void __init sun9i_a80_usb_phy_setup(struct device_node *node) | ||
230 | { | ||
231 | sunxi_usb_clk_setup(node, &sun9i_a80_usb_phy_data, &a80_usb_phy_lock); | ||
232 | } | ||
233 | CLK_OF_DECLARE(sun9i_a80_usb_phy, "allwinner,sun9i-a80-usb-phy-clk", sun9i_a80_usb_phy_setup); | ||