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 | * sunxi_usb_clk_setup() - Setup function for usb gate clocks |
78 | */ |
79 | |
80 | #define SUNXI_USB_MAX_SIZE 32 |
81 | |
82 | struct usb_clk_data { |
83 | u32 clk_mask; |
84 | u32 reset_mask; |
85 | bool reset_needs_clk; |
86 | }; |
87 | |
88 | static void __init sunxi_usb_clk_setup(struct device_node *node, |
89 | const struct usb_clk_data *data, |
90 | spinlock_t *lock) |
91 | { |
92 | struct clk_onecell_data *clk_data; |
93 | struct usb_reset_data *reset_data; |
94 | const char *clk_parent; |
95 | const char *clk_name; |
96 | void __iomem *reg; |
97 | int qty; |
98 | int i = 0; |
99 | int j = 0; |
100 | |
101 | reg = of_io_request_and_map(device: node, index: 0, name: of_node_full_name(np: node)); |
102 | if (IS_ERR(ptr: reg)) |
103 | return; |
104 | |
105 | clk_parent = of_clk_get_parent_name(np: node, index: 0); |
106 | if (!clk_parent) |
107 | return; |
108 | |
109 | /* Worst-case size approximation and memory allocation */ |
110 | qty = find_last_bit(addr: (unsigned long *)&data->clk_mask, |
111 | SUNXI_USB_MAX_SIZE); |
112 | |
113 | clk_data = kmalloc(size: sizeof(struct clk_onecell_data), GFP_KERNEL); |
114 | if (!clk_data) |
115 | return; |
116 | |
117 | clk_data->clks = kcalloc(n: qty + 1, size: sizeof(struct clk *), GFP_KERNEL); |
118 | if (!clk_data->clks) { |
119 | kfree(objp: clk_data); |
120 | return; |
121 | } |
122 | |
123 | for_each_set_bit(i, (unsigned long *)&data->clk_mask, |
124 | SUNXI_USB_MAX_SIZE) { |
125 | of_property_read_string_index(np: node, propname: "clock-output-names" , |
126 | index: j, output: &clk_name); |
127 | clk_data->clks[i] = clk_register_gate(NULL, name: clk_name, |
128 | parent_name: clk_parent, flags: 0, |
129 | reg, bit_idx: i, clk_gate_flags: 0, lock); |
130 | WARN_ON(IS_ERR(clk_data->clks[i])); |
131 | |
132 | j++; |
133 | } |
134 | |
135 | /* Adjust to the real max */ |
136 | clk_data->clk_num = i; |
137 | |
138 | of_clk_add_provider(np: node, clk_src_get: of_clk_src_onecell_get, data: clk_data); |
139 | |
140 | /* Register a reset controller for usb with reset bits */ |
141 | if (data->reset_mask == 0) |
142 | return; |
143 | |
144 | reset_data = kzalloc(size: sizeof(*reset_data), GFP_KERNEL); |
145 | if (!reset_data) |
146 | return; |
147 | |
148 | if (data->reset_needs_clk) { |
149 | reset_data->clk = of_clk_get(np: node, index: 0); |
150 | if (IS_ERR(ptr: reset_data->clk)) { |
151 | pr_err("Could not get clock for reset controls\n" ); |
152 | kfree(objp: reset_data); |
153 | return; |
154 | } |
155 | } |
156 | |
157 | reset_data->reg = reg; |
158 | reset_data->lock = lock; |
159 | reset_data->rcdev.nr_resets = __fls(word: data->reset_mask) + 1; |
160 | reset_data->rcdev.ops = &sunxi_usb_reset_ops; |
161 | reset_data->rcdev.of_node = node; |
162 | reset_controller_register(rcdev: &reset_data->rcdev); |
163 | } |
164 | |
165 | static const struct usb_clk_data sun4i_a10_usb_clk_data __initconst = { |
166 | .clk_mask = BIT(8) | BIT(7) | BIT(6), |
167 | .reset_mask = BIT(2) | BIT(1) | BIT(0), |
168 | }; |
169 | |
170 | static DEFINE_SPINLOCK(sun4i_a10_usb_lock); |
171 | |
172 | static void __init sun4i_a10_usb_setup(struct device_node *node) |
173 | { |
174 | sunxi_usb_clk_setup(node, data: &sun4i_a10_usb_clk_data, lock: &sun4i_a10_usb_lock); |
175 | } |
176 | CLK_OF_DECLARE(sun4i_a10_usb, "allwinner,sun4i-a10-usb-clk" , sun4i_a10_usb_setup); |
177 | |
178 | static const struct usb_clk_data sun5i_a13_usb_clk_data __initconst = { |
179 | .clk_mask = BIT(8) | BIT(6), |
180 | .reset_mask = BIT(1) | BIT(0), |
181 | }; |
182 | |
183 | static void __init sun5i_a13_usb_setup(struct device_node *node) |
184 | { |
185 | sunxi_usb_clk_setup(node, data: &sun5i_a13_usb_clk_data, lock: &sun4i_a10_usb_lock); |
186 | } |
187 | CLK_OF_DECLARE(sun5i_a13_usb, "allwinner,sun5i-a13-usb-clk" , sun5i_a13_usb_setup); |
188 | |
189 | static const struct usb_clk_data sun6i_a31_usb_clk_data __initconst = { |
190 | .clk_mask = BIT(18) | BIT(17) | BIT(16) | BIT(10) | BIT(9) | BIT(8), |
191 | .reset_mask = BIT(2) | BIT(1) | BIT(0), |
192 | }; |
193 | |
194 | static void __init sun6i_a31_usb_setup(struct device_node *node) |
195 | { |
196 | sunxi_usb_clk_setup(node, data: &sun6i_a31_usb_clk_data, lock: &sun4i_a10_usb_lock); |
197 | } |
198 | CLK_OF_DECLARE(sun6i_a31_usb, "allwinner,sun6i-a31-usb-clk" , sun6i_a31_usb_setup); |
199 | |
200 | static const struct usb_clk_data sun8i_a23_usb_clk_data __initconst = { |
201 | .clk_mask = BIT(16) | BIT(11) | BIT(10) | BIT(9) | BIT(8), |
202 | .reset_mask = BIT(2) | BIT(1) | BIT(0), |
203 | }; |
204 | |
205 | static void __init sun8i_a23_usb_setup(struct device_node *node) |
206 | { |
207 | sunxi_usb_clk_setup(node, data: &sun8i_a23_usb_clk_data, lock: &sun4i_a10_usb_lock); |
208 | } |
209 | CLK_OF_DECLARE(sun8i_a23_usb, "allwinner,sun8i-a23-usb-clk" , sun8i_a23_usb_setup); |
210 | |
211 | static const struct usb_clk_data sun8i_h3_usb_clk_data __initconst = { |
212 | .clk_mask = BIT(19) | BIT(18) | BIT(17) | BIT(16) | |
213 | BIT(11) | BIT(10) | BIT(9) | BIT(8), |
214 | .reset_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0), |
215 | }; |
216 | |
217 | static void __init sun8i_h3_usb_setup(struct device_node *node) |
218 | { |
219 | sunxi_usb_clk_setup(node, data: &sun8i_h3_usb_clk_data, lock: &sun4i_a10_usb_lock); |
220 | } |
221 | CLK_OF_DECLARE(sun8i_h3_usb, "allwinner,sun8i-h3-usb-clk" , sun8i_h3_usb_setup); |
222 | |
223 | static const struct usb_clk_data sun9i_a80_usb_mod_data __initconst = { |
224 | .clk_mask = BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1), |
225 | .reset_mask = BIT(19) | BIT(18) | BIT(17), |
226 | .reset_needs_clk = 1, |
227 | }; |
228 | |
229 | static DEFINE_SPINLOCK(a80_usb_mod_lock); |
230 | |
231 | static void __init sun9i_a80_usb_mod_setup(struct device_node *node) |
232 | { |
233 | sunxi_usb_clk_setup(node, data: &sun9i_a80_usb_mod_data, lock: &a80_usb_mod_lock); |
234 | } |
235 | CLK_OF_DECLARE(sun9i_a80_usb_mod, "allwinner,sun9i-a80-usb-mod-clk" , sun9i_a80_usb_mod_setup); |
236 | |
237 | static const struct usb_clk_data sun9i_a80_usb_phy_data __initconst = { |
238 | .clk_mask = BIT(10) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1), |
239 | .reset_mask = BIT(21) | BIT(20) | BIT(19) | BIT(18) | BIT(17), |
240 | .reset_needs_clk = 1, |
241 | }; |
242 | |
243 | static DEFINE_SPINLOCK(a80_usb_phy_lock); |
244 | |
245 | static void __init sun9i_a80_usb_phy_setup(struct device_node *node) |
246 | { |
247 | sunxi_usb_clk_setup(node, data: &sun9i_a80_usb_phy_data, lock: &a80_usb_phy_lock); |
248 | } |
249 | CLK_OF_DECLARE(sun9i_a80_usb_phy, "allwinner,sun9i-a80-usb-phy-clk" , sun9i_a80_usb_phy_setup); |
250 | |