diff options
author | Chen-Yu Tsai <wens@csie.org> | 2015-12-05 08:16:43 -0500 |
---|---|---|
committer | Maxime Ripard <maxime.ripard@free-electrons.com> | 2015-12-08 05:05:27 -0500 |
commit | 3cdd9f5c4953465abb87ec757159cc0576ae6b0a (patch) | |
tree | 9304b707c35d6f0e650ecf7e97cfbaa7098382a9 | |
parent | ab6e23a4e388f5f2696b8e92c350f845142da118 (diff) |
clk: sunxi: Add VE (Video Engine) module clock driver for sun[457]i
The video engine has its own special module clock, consisting of a clock
gate, configurable dividers, and a reset control.
On later (sun[68]i) families, the reset control is moved out of this
piece of hardware and grouped with reset controls of other peripherals.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Tested-by: Jens Kuske <jenskuske@gmail.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
-rw-r--r-- | Documentation/devicetree/bindings/clock/sunxi.txt | 4 | ||||
-rw-r--r-- | drivers/clk/sunxi/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/sunxi/clk-a10-ve.c | 171 |
3 files changed, 176 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 014eab8673f5..e59f57b24777 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt | |||
@@ -76,6 +76,7 @@ Required properties: | |||
76 | "allwinner,sun8i-h3-usb-clk" - for usb gates + resets on H3 | 76 | "allwinner,sun8i-h3-usb-clk" - for usb gates + resets on H3 |
77 | "allwinner,sun9i-a80-usb-mod-clk" - for usb gates + resets on A80 | 77 | "allwinner,sun9i-a80-usb-mod-clk" - for usb gates + resets on A80 |
78 | "allwinner,sun9i-a80-usb-phy-clk" - for usb phy gates + resets on A80 | 78 | "allwinner,sun9i-a80-usb-phy-clk" - for usb phy gates + resets on A80 |
79 | "allwinner,sun4i-a10-ve-clk" - for the Video Engine clock | ||
79 | 80 | ||
80 | Required properties for all clocks: | 81 | Required properties for all clocks: |
81 | - reg : shall be the control register address for the clock. | 82 | - reg : shall be the control register address for the clock. |
@@ -95,6 +96,9 @@ Required properties for all clocks: | |||
95 | And "allwinner,*-usb-clk" clocks also require: | 96 | And "allwinner,*-usb-clk" clocks also require: |
96 | - reset-cells : shall be set to 1 | 97 | - reset-cells : shall be set to 1 |
97 | 98 | ||
99 | The "allwinner,sun4i-a10-ve-clk" clock also requires: | ||
100 | - reset-cells : shall be set to 0 | ||
101 | |||
98 | The "allwinner,sun9i-a80-mmc-config-clk" clock also requires: | 102 | The "allwinner,sun9i-a80-mmc-config-clk" clock also requires: |
99 | - #reset-cells : shall be set to 1 | 103 | - #reset-cells : shall be set to 1 |
100 | - resets : shall be the reset control phandle for the mmc block. | 104 | - resets : shall be the reset control phandle for the mmc block. |
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index abf4916f1f97..1a909f9024eb 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile | |||
@@ -7,6 +7,7 @@ obj-y += clk-a10-codec.o | |||
7 | obj-y += clk-a10-hosc.o | 7 | obj-y += clk-a10-hosc.o |
8 | obj-y += clk-a10-mod1.o | 8 | obj-y += clk-a10-mod1.o |
9 | obj-y += clk-a10-pll2.o | 9 | obj-y += clk-a10-pll2.o |
10 | obj-y += clk-a10-ve.o | ||
10 | obj-y += clk-a20-gmac.o | 11 | obj-y += clk-a20-gmac.o |
11 | obj-y += clk-mod0.o | 12 | obj-y += clk-mod0.o |
12 | obj-y += clk-simple-gates.o | 13 | obj-y += clk-simple-gates.o |
diff --git a/drivers/clk/sunxi/clk-a10-ve.c b/drivers/clk/sunxi/clk-a10-ve.c new file mode 100644 index 000000000000..044c1717b762 --- /dev/null +++ b/drivers/clk/sunxi/clk-a10-ve.c | |||
@@ -0,0 +1,171 @@ | |||
1 | /* | ||
2 | * Copyright 2015 Chen-Yu Tsai | ||
3 | * | ||
4 | * Chen-Yu Tsai <wens@csie.org> | ||
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/of.h> | ||
19 | #include <linux/of_address.h> | ||
20 | #include <linux/reset-controller.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/spinlock.h> | ||
23 | |||
24 | static DEFINE_SPINLOCK(ve_lock); | ||
25 | |||
26 | #define SUN4I_VE_ENABLE 31 | ||
27 | #define SUN4I_VE_DIVIDER_SHIFT 16 | ||
28 | #define SUN4I_VE_DIVIDER_WIDTH 3 | ||
29 | #define SUN4I_VE_RESET 0 | ||
30 | |||
31 | /** | ||
32 | * sunxi_ve_reset... - reset bit in ve clk registers handling | ||
33 | */ | ||
34 | |||
35 | struct ve_reset_data { | ||
36 | void __iomem *reg; | ||
37 | spinlock_t *lock; | ||
38 | struct reset_controller_dev rcdev; | ||
39 | }; | ||
40 | |||
41 | static int sunxi_ve_reset_assert(struct reset_controller_dev *rcdev, | ||
42 | unsigned long id) | ||
43 | { | ||
44 | struct ve_reset_data *data = container_of(rcdev, | ||
45 | struct ve_reset_data, | ||
46 | rcdev); | ||
47 | unsigned long flags; | ||
48 | u32 reg; | ||
49 | |||
50 | spin_lock_irqsave(data->lock, flags); | ||
51 | |||
52 | reg = readl(data->reg); | ||
53 | writel(reg & ~BIT(SUN4I_VE_RESET), data->reg); | ||
54 | |||
55 | spin_unlock_irqrestore(data->lock, flags); | ||
56 | |||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | static int sunxi_ve_reset_deassert(struct reset_controller_dev *rcdev, | ||
61 | unsigned long id) | ||
62 | { | ||
63 | struct ve_reset_data *data = container_of(rcdev, | ||
64 | struct ve_reset_data, | ||
65 | rcdev); | ||
66 | unsigned long flags; | ||
67 | u32 reg; | ||
68 | |||
69 | spin_lock_irqsave(data->lock, flags); | ||
70 | |||
71 | reg = readl(data->reg); | ||
72 | writel(reg | BIT(SUN4I_VE_RESET), data->reg); | ||
73 | |||
74 | spin_unlock_irqrestore(data->lock, flags); | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static int sunxi_ve_of_xlate(struct reset_controller_dev *rcdev, | ||
80 | const struct of_phandle_args *reset_spec) | ||
81 | { | ||
82 | if (WARN_ON(reset_spec->args_count != 0)) | ||
83 | return -EINVAL; | ||
84 | |||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static struct reset_control_ops sunxi_ve_reset_ops = { | ||
89 | .assert = sunxi_ve_reset_assert, | ||
90 | .deassert = sunxi_ve_reset_deassert, | ||
91 | }; | ||
92 | |||
93 | static void __init sun4i_ve_clk_setup(struct device_node *node) | ||
94 | { | ||
95 | struct clk *clk; | ||
96 | struct clk_divider *div; | ||
97 | struct clk_gate *gate; | ||
98 | struct ve_reset_data *reset_data; | ||
99 | const char *parent; | ||
100 | const char *clk_name = node->name; | ||
101 | void __iomem *reg; | ||
102 | int err; | ||
103 | |||
104 | reg = of_io_request_and_map(node, 0, of_node_full_name(node)); | ||
105 | if (IS_ERR(reg)) | ||
106 | return; | ||
107 | |||
108 | div = kzalloc(sizeof(*div), GFP_KERNEL); | ||
109 | if (!div) | ||
110 | goto err_unmap; | ||
111 | |||
112 | gate = kzalloc(sizeof(*gate), GFP_KERNEL); | ||
113 | if (!gate) | ||
114 | goto err_free_div; | ||
115 | |||
116 | of_property_read_string(node, "clock-output-names", &clk_name); | ||
117 | parent = of_clk_get_parent_name(node, 0); | ||
118 | |||
119 | gate->reg = reg; | ||
120 | gate->bit_idx = SUN4I_VE_ENABLE; | ||
121 | gate->lock = &ve_lock; | ||
122 | |||
123 | div->reg = reg; | ||
124 | div->shift = SUN4I_VE_DIVIDER_SHIFT; | ||
125 | div->width = SUN4I_VE_DIVIDER_WIDTH; | ||
126 | div->lock = &ve_lock; | ||
127 | |||
128 | clk = clk_register_composite(NULL, clk_name, &parent, 1, | ||
129 | NULL, NULL, | ||
130 | &div->hw, &clk_divider_ops, | ||
131 | &gate->hw, &clk_gate_ops, | ||
132 | CLK_SET_RATE_PARENT); | ||
133 | if (IS_ERR(clk)) | ||
134 | goto err_free_gate; | ||
135 | |||
136 | err = of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
137 | if (err) | ||
138 | goto err_unregister_clk; | ||
139 | |||
140 | reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); | ||
141 | if (!reset_data) | ||
142 | goto err_del_provider; | ||
143 | |||
144 | reset_data->reg = reg; | ||
145 | reset_data->lock = &ve_lock; | ||
146 | reset_data->rcdev.nr_resets = 1; | ||
147 | reset_data->rcdev.ops = &sunxi_ve_reset_ops; | ||
148 | reset_data->rcdev.of_node = node; | ||
149 | reset_data->rcdev.of_xlate = sunxi_ve_of_xlate; | ||
150 | reset_data->rcdev.of_reset_n_cells = 0; | ||
151 | err = reset_controller_register(&reset_data->rcdev); | ||
152 | if (err) | ||
153 | goto err_free_reset; | ||
154 | |||
155 | return; | ||
156 | |||
157 | err_free_reset: | ||
158 | kfree(reset_data); | ||
159 | err_del_provider: | ||
160 | of_clk_del_provider(node); | ||
161 | err_unregister_clk: | ||
162 | clk_unregister(clk); | ||
163 | err_free_gate: | ||
164 | kfree(gate); | ||
165 | err_free_div: | ||
166 | kfree(div); | ||
167 | err_unmap: | ||
168 | iounmap(reg); | ||
169 | } | ||
170 | CLK_OF_DECLARE(sun4i_ve, "allwinner,sun4i-a10-ve-clk", | ||
171 | sun4i_ve_clk_setup); | ||