1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2013-2015 Emilio López |
4 | * |
5 | * Emilio López <emilio@elopez.com.ar> |
6 | */ |
7 | |
8 | #include <linux/clk.h> |
9 | #include <linux/clk-provider.h> |
10 | #include <linux/io.h> |
11 | #include <linux/of.h> |
12 | #include <linux/of_address.h> |
13 | #include <linux/reset-controller.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/spinlock.h> |
16 | |
17 | |
18 | /* |
19 | * sunxi_usb_reset... - reset bits in usb clk registers handling |
20 | */ |
21 | |
22 | struct usb_reset_data { |
23 | void __iomem *reg; |
24 | spinlock_t *lock; |
25 | struct clk *clk; |
26 | struct reset_controller_dev rcdev; |
27 | }; |
28 | |
29 | static int sunxi_usb_reset_assert(struct reset_controller_dev *rcdev, |
30 | unsigned long id) |
31 | { |
32 | struct usb_reset_data *data = container_of(rcdev, |
33 | struct usb_reset_data, |
34 | rcdev); |
35 | unsigned long flags; |
36 | u32 reg; |
37 | |
38 | clk_prepare_enable(clk: data->clk); |
39 | spin_lock_irqsave(data->lock, flags); |
40 | |
41 | reg = readl(addr: data->reg); |
42 | writel(val: reg & ~BIT(id), addr: data->reg); |
43 | |
44 | spin_unlock_irqrestore(lock: data->lock, flags); |
45 | clk_disable_unprepare(clk: data->clk); |
46 | |
47 | return 0; |
48 | } |
49 | |
50 | static int sunxi_usb_reset_deassert(struct reset_controller_dev *rcdev, |
51 | unsigned long id) |
52 | { |
53 | struct usb_reset_data *data = container_of(rcdev, |
54 | struct usb_reset_data, |
55 | rcdev); |
56 | unsigned long flags; |
57 | u32 reg; |
58 | |
59 | clk_prepare_enable(clk: data->clk); |
60 | spin_lock_irqsave(data->lock, flags); |
61 | |
62 | reg = readl(addr: data->reg); |
63 | writel(val: reg | BIT(id), addr: data->reg); |
64 | |
65 | spin_unlock_irqrestore(lock: data->lock, flags); |
66 | clk_disable_unprepare(clk: data->clk); |
67 | |
68 | return 0; |
69 | } |
70 | |
71 | static const struct reset_control_ops sunxi_usb_reset_ops = { |
72 | .assert = sunxi_usb_reset_assert, |
73 | .deassert = sunxi_usb_reset_deassert, |
74 | }; |
75 | |
76 | |
77 | #define SUNXI_USB_MAX_SIZE 32 |
78 | |
79 | struct usb_clk_data { |
80 | u32 clk_mask; |
81 | u32 reset_mask; |
82 | bool reset_needs_clk; |
83 | }; |
84 | |
85 | /** |
86 | * sunxi_usb_clk_setup() - Setup function for usb gate clocks |
87 | * @node: &struct device_node for the clock |
88 | * @data: &struct usb_clk_data for the clock |
89 | * @lock: spinlock for the clock |
90 | */ |
91 | static void __init sunxi_usb_clk_setup(struct device_node *node, |
92 | const struct usb_clk_data *data, |
93 | spinlock_t *lock) |
94 | { |
95 | struct clk_onecell_data *clk_data; |
96 | struct usb_reset_data *reset_data; |
97 | const char *clk_parent; |
98 | const char *clk_name; |
99 | void __iomem *reg; |
100 | int qty; |
101 | int i = 0; |
102 | int j = 0; |
103 | |
104 | reg = of_io_request_and_map(device: node, index: 0, name: of_node_full_name(np: node)); |
105 | if (IS_ERR(ptr: reg)) |
106 | return; |
107 | |
108 | clk_parent = of_clk_get_parent_name(np: node, index: 0); |
109 | if (!clk_parent) |
110 | return; |
111 | |
112 | /* Worst-case size approximation and memory allocation */ |
113 | qty = find_last_bit(addr: (unsigned long *)&data->clk_mask, |
114 | SUNXI_USB_MAX_SIZE); |
115 | |
116 | clk_data = kmalloc(size: sizeof(struct clk_onecell_data), GFP_KERNEL); |
117 | if (!clk_data) |
118 | return; |
119 | |
120 | clk_data->clks = kcalloc(n: qty + 1, size: sizeof(struct clk *), GFP_KERNEL); |
121 | if (!clk_data->clks) { |
122 | kfree(objp: clk_data); |
123 | return; |
124 | } |
125 | |
126 | for_each_set_bit(i, (unsigned long *)&data->clk_mask, |
127 | SUNXI_USB_MAX_SIZE) { |
128 | of_property_read_string_index(np: node, propname: "clock-output-names" , |
129 | index: j, output: &clk_name); |
130 | clk_data->clks[i] = clk_register_gate(NULL, name: clk_name, |
131 | parent_name: clk_parent, flags: 0, |
132 | reg, bit_idx: i, clk_gate_flags: 0, lock); |
133 | WARN_ON(IS_ERR(clk_data->clks[i])); |
134 | |
135 | j++; |
136 | } |
137 | |
138 | /* Adjust to the real max */ |
139 | clk_data->clk_num = i; |
140 | |
141 | of_clk_add_provider(np: node, clk_src_get: of_clk_src_onecell_get, data: clk_data); |
142 | |
143 | /* Register a reset controller for usb with reset bits */ |
144 | if (data->reset_mask == 0) |
145 | return; |
146 | |
147 | reset_data = kzalloc(size: sizeof(*reset_data), GFP_KERNEL); |
148 | if (!reset_data) |
149 | return; |
150 | |
151 | if (data->reset_needs_clk) { |
152 | reset_data->clk = of_clk_get(np: node, index: 0); |
153 | if (IS_ERR(ptr: reset_data->clk)) { |
154 | pr_err("Could not get clock for reset controls\n" ); |
155 | kfree(objp: reset_data); |
156 | return; |
157 | } |
158 | } |
159 | |
160 | reset_data->reg = reg; |
161 | reset_data->lock = lock; |
162 | reset_data->rcdev.nr_resets = __fls(word: data->reset_mask) + 1; |
163 | reset_data->rcdev.ops = &sunxi_usb_reset_ops; |
164 | reset_data->rcdev.of_node = node; |
165 | reset_controller_register(rcdev: &reset_data->rcdev); |
166 | } |
167 | |
168 | static const struct usb_clk_data sun4i_a10_usb_clk_data __initconst = { |
169 | .clk_mask = BIT(8) | BIT(7) | BIT(6), |
170 | .reset_mask = BIT(2) | BIT(1) | BIT(0), |
171 | }; |
172 | |
173 | static DEFINE_SPINLOCK(sun4i_a10_usb_lock); |
174 | |
175 | static void __init sun4i_a10_usb_setup(struct device_node *node) |
176 | { |
177 | sunxi_usb_clk_setup(node, data: &sun4i_a10_usb_clk_data, lock: &sun4i_a10_usb_lock); |
178 | } |
179 | CLK_OF_DECLARE(sun4i_a10_usb, "allwinner,sun4i-a10-usb-clk" , sun4i_a10_usb_setup); |
180 | |
181 | static const struct usb_clk_data sun5i_a13_usb_clk_data __initconst = { |
182 | .clk_mask = BIT(8) | BIT(6), |
183 | .reset_mask = BIT(1) | BIT(0), |
184 | }; |
185 | |
186 | static void __init sun5i_a13_usb_setup(struct device_node *node) |
187 | { |
188 | sunxi_usb_clk_setup(node, data: &sun5i_a13_usb_clk_data, lock: &sun4i_a10_usb_lock); |
189 | } |
190 | CLK_OF_DECLARE(sun5i_a13_usb, "allwinner,sun5i-a13-usb-clk" , sun5i_a13_usb_setup); |
191 | |
192 | static const struct usb_clk_data sun6i_a31_usb_clk_data __initconst = { |
193 | .clk_mask = BIT(18) | BIT(17) | BIT(16) | BIT(10) | BIT(9) | BIT(8), |
194 | .reset_mask = BIT(2) | BIT(1) | BIT(0), |
195 | }; |
196 | |
197 | static void __init sun6i_a31_usb_setup(struct device_node *node) |
198 | { |
199 | sunxi_usb_clk_setup(node, data: &sun6i_a31_usb_clk_data, lock: &sun4i_a10_usb_lock); |
200 | } |
201 | CLK_OF_DECLARE(sun6i_a31_usb, "allwinner,sun6i-a31-usb-clk" , sun6i_a31_usb_setup); |
202 | |
203 | static const struct usb_clk_data sun8i_a23_usb_clk_data __initconst = { |
204 | .clk_mask = BIT(16) | BIT(11) | BIT(10) | BIT(9) | BIT(8), |
205 | .reset_mask = BIT(2) | BIT(1) | BIT(0), |
206 | }; |
207 | |
208 | static void __init sun8i_a23_usb_setup(struct device_node *node) |
209 | { |
210 | sunxi_usb_clk_setup(node, data: &sun8i_a23_usb_clk_data, lock: &sun4i_a10_usb_lock); |
211 | } |
212 | CLK_OF_DECLARE(sun8i_a23_usb, "allwinner,sun8i-a23-usb-clk" , sun8i_a23_usb_setup); |
213 | |
214 | static const struct usb_clk_data sun8i_h3_usb_clk_data __initconst = { |
215 | .clk_mask = BIT(19) | BIT(18) | BIT(17) | BIT(16) | |
216 | BIT(11) | BIT(10) | BIT(9) | BIT(8), |
217 | .reset_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0), |
218 | }; |
219 | |
220 | static void __init sun8i_h3_usb_setup(struct device_node *node) |
221 | { |
222 | sunxi_usb_clk_setup(node, data: &sun8i_h3_usb_clk_data, lock: &sun4i_a10_usb_lock); |
223 | } |
224 | CLK_OF_DECLARE(sun8i_h3_usb, "allwinner,sun8i-h3-usb-clk" , sun8i_h3_usb_setup); |
225 | |
226 | static const struct usb_clk_data sun9i_a80_usb_mod_data __initconst = { |
227 | .clk_mask = BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1), |
228 | .reset_mask = BIT(19) | BIT(18) | BIT(17), |
229 | .reset_needs_clk = 1, |
230 | }; |
231 | |
232 | static DEFINE_SPINLOCK(a80_usb_mod_lock); |
233 | |
234 | static void __init sun9i_a80_usb_mod_setup(struct device_node *node) |
235 | { |
236 | sunxi_usb_clk_setup(node, data: &sun9i_a80_usb_mod_data, lock: &a80_usb_mod_lock); |
237 | } |
238 | CLK_OF_DECLARE(sun9i_a80_usb_mod, "allwinner,sun9i-a80-usb-mod-clk" , sun9i_a80_usb_mod_setup); |
239 | |
240 | static const struct usb_clk_data sun9i_a80_usb_phy_data __initconst = { |
241 | .clk_mask = BIT(10) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1), |
242 | .reset_mask = BIT(21) | BIT(20) | BIT(19) | BIT(18) | BIT(17), |
243 | .reset_needs_clk = 1, |
244 | }; |
245 | |
246 | static DEFINE_SPINLOCK(a80_usb_phy_lock); |
247 | |
248 | static void __init sun9i_a80_usb_phy_setup(struct device_node *node) |
249 | { |
250 | sunxi_usb_clk_setup(node, data: &sun9i_a80_usb_phy_data, lock: &a80_usb_phy_lock); |
251 | } |
252 | CLK_OF_DECLARE(sun9i_a80_usb_phy, "allwinner,sun9i-a80-usb-phy-clk" , sun9i_a80_usb_phy_setup); |
253 | |