1 | // SPDX-License-Identifier: GPL-1.0+ |
2 | /* |
3 | * Renesas USB driver |
4 | * |
5 | * Copyright (C) 2011 Renesas Solutions Corp. |
6 | * Copyright (C) 2019 Renesas Electronics Corporation |
7 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
8 | */ |
9 | #include <linux/interrupt.h> |
10 | |
11 | #include "common.h" |
12 | #include "mod.h" |
13 | |
14 | /* |
15 | * autonomy |
16 | * |
17 | * these functions are used if platform doesn't have external phy. |
18 | * -> there is no "notify_hotplug" callback from platform |
19 | * -> call "notify_hotplug" by itself |
20 | * -> use own interrupt to connect/disconnect |
21 | * -> it mean module clock is always ON |
22 | * ~~~~~~~~~~~~~~~~~~~~~~~~~ |
23 | */ |
24 | static int usbhsm_autonomy_get_vbus(struct platform_device *pdev) |
25 | { |
26 | struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); |
27 | |
28 | return VBSTS & usbhs_read(priv, INTSTS0); |
29 | } |
30 | |
31 | static int usbhsm_autonomy_irq_vbus(struct usbhs_priv *priv, |
32 | struct usbhs_irq_state *irq_state) |
33 | { |
34 | struct platform_device *pdev = usbhs_priv_to_pdev(priv); |
35 | |
36 | usbhsc_schedule_notify_hotplug(pdev); |
37 | |
38 | return 0; |
39 | } |
40 | |
41 | void usbhs_mod_autonomy_mode(struct usbhs_priv *priv) |
42 | { |
43 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); |
44 | |
45 | info->irq_vbus = usbhsm_autonomy_irq_vbus; |
46 | info->get_vbus = usbhsm_autonomy_get_vbus; |
47 | |
48 | usbhs_irq_callback_update(priv, NULL); |
49 | } |
50 | |
51 | void usbhs_mod_non_autonomy_mode(struct usbhs_priv *priv) |
52 | { |
53 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); |
54 | |
55 | info->get_vbus = priv->pfunc->get_vbus; |
56 | } |
57 | |
58 | /* |
59 | * host / gadget functions |
60 | * |
61 | * renesas_usbhs host/gadget can register itself by below functions. |
62 | * these functions are called when probe |
63 | * |
64 | */ |
65 | void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *mod, int id) |
66 | { |
67 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); |
68 | |
69 | info->mod[id] = mod; |
70 | mod->priv = priv; |
71 | } |
72 | |
73 | struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id) |
74 | { |
75 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); |
76 | struct usbhs_mod *ret = NULL; |
77 | |
78 | switch (id) { |
79 | case USBHS_HOST: |
80 | case USBHS_GADGET: |
81 | ret = info->mod[id]; |
82 | break; |
83 | } |
84 | |
85 | return ret; |
86 | } |
87 | |
88 | int usbhs_mod_is_host(struct usbhs_priv *priv) |
89 | { |
90 | struct usbhs_mod *mod = usbhs_mod_get_current(priv); |
91 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); |
92 | |
93 | if (!mod) |
94 | return -EINVAL; |
95 | |
96 | return info->mod[USBHS_HOST] == mod; |
97 | } |
98 | |
99 | struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv) |
100 | { |
101 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); |
102 | |
103 | return info->curt; |
104 | } |
105 | |
106 | int usbhs_mod_change(struct usbhs_priv *priv, int id) |
107 | { |
108 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); |
109 | struct usbhs_mod *mod = NULL; |
110 | int ret = 0; |
111 | |
112 | /* id < 0 mean no current */ |
113 | switch (id) { |
114 | case USBHS_HOST: |
115 | case USBHS_GADGET: |
116 | mod = info->mod[id]; |
117 | break; |
118 | default: |
119 | ret = -EINVAL; |
120 | } |
121 | info->curt = mod; |
122 | |
123 | return ret; |
124 | } |
125 | |
126 | static irqreturn_t usbhs_interrupt(int irq, void *data); |
127 | int usbhs_mod_probe(struct usbhs_priv *priv) |
128 | { |
129 | struct device *dev = usbhs_priv_to_dev(priv); |
130 | int ret; |
131 | |
132 | /* |
133 | * install host/gadget driver |
134 | */ |
135 | ret = usbhs_mod_host_probe(priv); |
136 | if (ret < 0) |
137 | return ret; |
138 | |
139 | ret = usbhs_mod_gadget_probe(priv); |
140 | if (ret < 0) |
141 | goto mod_init_host_err; |
142 | |
143 | /* irq settings */ |
144 | ret = devm_request_irq(dev, irq: priv->irq, handler: usbhs_interrupt, |
145 | irqflags: 0, devname: dev_name(dev), dev_id: priv); |
146 | if (ret) { |
147 | dev_err(dev, "irq request err\n" ); |
148 | goto mod_init_gadget_err; |
149 | } |
150 | |
151 | return ret; |
152 | |
153 | mod_init_gadget_err: |
154 | usbhs_mod_gadget_remove(priv); |
155 | mod_init_host_err: |
156 | usbhs_mod_host_remove(priv); |
157 | |
158 | return ret; |
159 | } |
160 | |
161 | void usbhs_mod_remove(struct usbhs_priv *priv) |
162 | { |
163 | usbhs_mod_host_remove(priv); |
164 | usbhs_mod_gadget_remove(priv); |
165 | } |
166 | |
167 | /* |
168 | * status functions |
169 | */ |
170 | int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state) |
171 | { |
172 | return (int)irq_state->intsts0 & DVSQ_MASK; |
173 | } |
174 | |
175 | int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state) |
176 | { |
177 | /* |
178 | * return value |
179 | * |
180 | * IDLE_SETUP_STAGE |
181 | * READ_DATA_STAGE |
182 | * READ_STATUS_STAGE |
183 | * WRITE_DATA_STAGE |
184 | * WRITE_STATUS_STAGE |
185 | * NODATA_STATUS_STAGE |
186 | * SEQUENCE_ERROR |
187 | */ |
188 | return (int)irq_state->intsts0 & CTSQ_MASK; |
189 | } |
190 | |
191 | static int usbhs_status_get_each_irq(struct usbhs_priv *priv, |
192 | struct usbhs_irq_state *state) |
193 | { |
194 | struct usbhs_mod *mod = usbhs_mod_get_current(priv); |
195 | u16 intenb0, intenb1; |
196 | unsigned long flags; |
197 | |
198 | /******************** spin lock ********************/ |
199 | usbhs_lock(priv, flags); |
200 | state->intsts0 = usbhs_read(priv, INTSTS0); |
201 | intenb0 = usbhs_read(priv, INTENB0); |
202 | |
203 | if (usbhs_mod_is_host(priv)) { |
204 | state->intsts1 = usbhs_read(priv, INTSTS1); |
205 | intenb1 = usbhs_read(priv, INTENB1); |
206 | } else { |
207 | state->intsts1 = intenb1 = 0; |
208 | } |
209 | |
210 | /* mask */ |
211 | if (mod) { |
212 | state->brdysts = usbhs_read(priv, BRDYSTS); |
213 | state->nrdysts = usbhs_read(priv, NRDYSTS); |
214 | state->bempsts = usbhs_read(priv, BEMPSTS); |
215 | |
216 | state->bempsts &= mod->irq_bempsts; |
217 | state->brdysts &= mod->irq_brdysts; |
218 | } |
219 | usbhs_unlock(priv, flags); |
220 | /******************** spin unlock ******************/ |
221 | |
222 | return 0; |
223 | } |
224 | |
225 | /* |
226 | * interrupt |
227 | */ |
228 | #define INTSTS0_MAGIC 0xF800 /* acknowledge magical interrupt sources */ |
229 | #define INTSTS1_MAGIC 0xA870 /* acknowledge magical interrupt sources */ |
230 | static irqreturn_t usbhs_interrupt(int irq, void *data) |
231 | { |
232 | struct usbhs_priv *priv = data; |
233 | struct usbhs_irq_state irq_state; |
234 | |
235 | if (usbhs_status_get_each_irq(priv, state: &irq_state) < 0) |
236 | return IRQ_NONE; |
237 | |
238 | /* |
239 | * clear interrupt |
240 | * |
241 | * The hardware is _very_ picky to clear interrupt bit. |
242 | * Especially INTSTS0_MAGIC, INTSTS1_MAGIC value. |
243 | * |
244 | * see |
245 | * "Operation" |
246 | * - "Control Transfer (DCP)" |
247 | * - Function :: VALID bit should 0 |
248 | */ |
249 | usbhs_write(priv, INTSTS0, data: ~irq_state.intsts0 & INTSTS0_MAGIC); |
250 | if (usbhs_mod_is_host(priv)) |
251 | usbhs_write(priv, INTSTS1, data: ~irq_state.intsts1 & INTSTS1_MAGIC); |
252 | |
253 | /* |
254 | * The driver should not clear the xxxSTS after the line of |
255 | * "call irq callback functions" because each "if" statement is |
256 | * possible to call the callback function for avoiding any side effects. |
257 | */ |
258 | if (irq_state.intsts0 & BRDY) |
259 | usbhs_write(priv, BRDYSTS, data: ~irq_state.brdysts); |
260 | usbhs_write(priv, NRDYSTS, data: ~irq_state.nrdysts); |
261 | if (irq_state.intsts0 & BEMP) |
262 | usbhs_write(priv, BEMPSTS, data: ~irq_state.bempsts); |
263 | |
264 | /* |
265 | * call irq callback functions |
266 | * see also |
267 | * usbhs_irq_setting_update |
268 | */ |
269 | |
270 | /* INTSTS0 */ |
271 | if (irq_state.intsts0 & VBINT) |
272 | usbhs_mod_info_call(priv, irq_vbus, priv, &irq_state); |
273 | |
274 | if (irq_state.intsts0 & DVST) |
275 | usbhs_mod_call(priv, irq_dev_state, priv, &irq_state); |
276 | |
277 | if (irq_state.intsts0 & CTRT) |
278 | usbhs_mod_call(priv, irq_ctrl_stage, priv, &irq_state); |
279 | |
280 | if (irq_state.intsts0 & BEMP) |
281 | usbhs_mod_call(priv, irq_empty, priv, &irq_state); |
282 | |
283 | if (irq_state.intsts0 & BRDY) |
284 | usbhs_mod_call(priv, irq_ready, priv, &irq_state); |
285 | |
286 | if (usbhs_mod_is_host(priv)) { |
287 | /* INTSTS1 */ |
288 | if (irq_state.intsts1 & ATTCH) |
289 | usbhs_mod_call(priv, irq_attch, priv, &irq_state); |
290 | |
291 | if (irq_state.intsts1 & DTCH) |
292 | usbhs_mod_call(priv, irq_dtch, priv, &irq_state); |
293 | |
294 | if (irq_state.intsts1 & SIGN) |
295 | usbhs_mod_call(priv, irq_sign, priv, &irq_state); |
296 | |
297 | if (irq_state.intsts1 & SACK) |
298 | usbhs_mod_call(priv, irq_sack, priv, &irq_state); |
299 | } |
300 | return IRQ_HANDLED; |
301 | } |
302 | |
303 | void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) |
304 | { |
305 | u16 intenb0 = 0; |
306 | u16 intenb1 = 0; |
307 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); |
308 | |
309 | /* |
310 | * BEMPENB/BRDYENB are picky. |
311 | * below method is required |
312 | * |
313 | * - clear INTSTS0 |
314 | * - update BEMPENB/BRDYENB |
315 | * - update INTSTS0 |
316 | */ |
317 | usbhs_write(priv, INTENB0, data: 0); |
318 | if (usbhs_mod_is_host(priv)) |
319 | usbhs_write(priv, INTENB1, data: 0); |
320 | |
321 | usbhs_write(priv, BEMPENB, data: 0); |
322 | usbhs_write(priv, BRDYENB, data: 0); |
323 | |
324 | /* |
325 | * see also |
326 | * usbhs_interrupt |
327 | */ |
328 | |
329 | if (info->irq_vbus) |
330 | intenb0 |= VBSE; |
331 | |
332 | if (mod) { |
333 | /* |
334 | * INTSTS0 |
335 | */ |
336 | if (mod->irq_ctrl_stage) |
337 | intenb0 |= CTRE; |
338 | |
339 | if (mod->irq_dev_state) |
340 | intenb0 |= DVSE; |
341 | |
342 | if (mod->irq_empty && mod->irq_bempsts) { |
343 | usbhs_write(priv, BEMPENB, data: mod->irq_bempsts); |
344 | intenb0 |= BEMPE; |
345 | } |
346 | |
347 | if (mod->irq_ready && mod->irq_brdysts) { |
348 | usbhs_write(priv, BRDYENB, data: mod->irq_brdysts); |
349 | intenb0 |= BRDYE; |
350 | } |
351 | |
352 | if (usbhs_mod_is_host(priv)) { |
353 | /* |
354 | * INTSTS1 |
355 | */ |
356 | if (mod->irq_attch) |
357 | intenb1 |= ATTCHE; |
358 | |
359 | if (mod->irq_dtch) |
360 | intenb1 |= DTCHE; |
361 | |
362 | if (mod->irq_sign) |
363 | intenb1 |= SIGNE; |
364 | |
365 | if (mod->irq_sack) |
366 | intenb1 |= SACKE; |
367 | } |
368 | } |
369 | |
370 | if (intenb0) |
371 | usbhs_write(priv, INTENB0, data: intenb0); |
372 | |
373 | if (usbhs_mod_is_host(priv) && intenb1) |
374 | usbhs_write(priv, INTENB1, data: intenb1); |
375 | } |
376 | |