Commit | Line | Data |
---|---|---|
5fd54ace | 1 | // SPDX-License-Identifier: GPL-1.0+ |
f1407d5c KM |
2 | /* |
3 | * Renesas USB driver | |
4 | * | |
5 | * Copyright (C) 2011 Renesas Solutions Corp. | |
0966648d | 6 | * Copyright (C) 2019 Renesas Electronics Corporation |
f1407d5c | 7 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
f1407d5c KM |
8 | */ |
9 | #include <linux/interrupt.h> | |
10 | ||
cc502bb7 PB |
11 | #include "common.h" |
12 | #include "mod.h" | |
f1407d5c | 13 | |
b002ff6e KM |
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 | ||
0966648d | 36 | usbhsc_schedule_notify_hotplug(pdev); |
b4fcea2a KM |
37 | |
38 | return 0; | |
b002ff6e KM |
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 | ||
ccc3264c YS |
45 | info->irq_vbus = usbhsm_autonomy_irq_vbus; |
46 | info->get_vbus = usbhsm_autonomy_get_vbus; | |
b002ff6e KM |
47 | |
48 | usbhs_irq_callback_update(priv, NULL); | |
49 | } | |
f1407d5c | 50 | |
ccc3264c YS |
51 | void usbhs_mod_non_autonomy_mode(struct usbhs_priv *priv) |
52 | { | |
53 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); | |
54 | ||
426d3ff2 | 55 | info->get_vbus = priv->pfunc->get_vbus; |
ccc3264c YS |
56 | } |
57 | ||
f1407d5c KM |
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 | ||
0deb3e77 | 88 | int usbhs_mod_is_host(struct usbhs_priv *priv) |
f1407d5c | 89 | { |
0deb3e77 | 90 | struct usbhs_mod *mod = usbhs_mod_get_current(priv); |
f1407d5c KM |
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 | ||
2f98382d KM |
132 | /* |
133 | * install host/gadget driver | |
134 | */ | |
034d7c13 | 135 | ret = usbhs_mod_host_probe(priv); |
2f98382d KM |
136 | if (ret < 0) |
137 | return ret; | |
138 | ||
034d7c13 KM |
139 | ret = usbhs_mod_gadget_probe(priv); |
140 | if (ret < 0) | |
141 | goto mod_init_host_err; | |
142 | ||
f1407d5c | 143 | /* irq settings */ |
797b4e14 | 144 | ret = devm_request_irq(dev, priv->irq, usbhs_interrupt, |
22ae6415 | 145 | 0, dev_name(dev), priv); |
2f98382d | 146 | if (ret) { |
f1407d5c | 147 | dev_err(dev, "irq request err\n"); |
2f98382d KM |
148 | goto mod_init_gadget_err; |
149 | } | |
150 | ||
151 | return ret; | |
152 | ||
153 | mod_init_gadget_err: | |
154 | usbhs_mod_gadget_remove(priv); | |
034d7c13 KM |
155 | mod_init_host_err: |
156 | usbhs_mod_host_remove(priv); | |
f1407d5c KM |
157 | |
158 | return ret; | |
159 | } | |
160 | ||
161 | void usbhs_mod_remove(struct usbhs_priv *priv) | |
162 | { | |
034d7c13 | 163 | usbhs_mod_host_remove(priv); |
2f98382d | 164 | usbhs_mod_gadget_remove(priv); |
f1407d5c KM |
165 | } |
166 | ||
167 | /* | |
168 | * status functions | |
169 | */ | |
f1407d5c KM |
170 | int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state) |
171 | { | |
5022204a | 172 | return (int)irq_state->intsts0 & DVSQ_MASK; |
f1407d5c KM |
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 | ||
697d5c00 SY |
191 | static int usbhs_status_get_each_irq(struct usbhs_priv *priv, |
192 | struct usbhs_irq_state *state) | |
f1407d5c KM |
193 | { |
194 | struct usbhs_mod *mod = usbhs_mod_get_current(priv); | |
697d5c00 | 195 | u16 intenb0, intenb1; |
c4d8199b | 196 | unsigned long flags; |
f1407d5c | 197 | |
c4d8199b YS |
198 | /******************** spin lock ********************/ |
199 | usbhs_lock(priv, flags); | |
f1407d5c | 200 | state->intsts0 = usbhs_read(priv, INTSTS0); |
697d5c00 | 201 | intenb0 = usbhs_read(priv, INTENB0); |
88a25e02 NI |
202 | |
203 | if (usbhs_mod_is_host(priv)) { | |
204 | state->intsts1 = usbhs_read(priv, INTSTS1); | |
205 | intenb1 = usbhs_read(priv, INTENB1); | |
672bfdaa AB |
206 | } else { |
207 | state->intsts1 = intenb1 = 0; | |
88a25e02 | 208 | } |
697d5c00 | 209 | |
f1407d5c | 210 | /* mask */ |
5ea68d54 KM |
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 | } | |
c4d8199b YS |
219 | usbhs_unlock(priv, flags); |
220 | /******************** spin unlock ******************/ | |
697d5c00 | 221 | |
697d5c00 | 222 | return 0; |
f1407d5c KM |
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 | ||
697d5c00 SY |
235 | if (usbhs_status_get_each_irq(priv, &irq_state) < 0) |
236 | return IRQ_NONE; | |
f1407d5c KM |
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, ~irq_state.intsts0 & INTSTS0_MAGIC); | |
88a25e02 NI |
250 | if (usbhs_mod_is_host(priv)) |
251 | usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC); | |
f1407d5c | 252 | |
519d8bd4 YS |
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, ~irq_state.brdysts); | |
d5c6a1e0 | 260 | usbhs_write(priv, NRDYSTS, ~irq_state.nrdysts); |
519d8bd4 YS |
261 | if (irq_state.intsts0 & BEMP) |
262 | usbhs_write(priv, BEMPSTS, ~irq_state.bempsts); | |
f1407d5c KM |
263 | |
264 | /* | |
265 | * call irq callback functions | |
266 | * see also | |
267 | * usbhs_irq_setting_update | |
268 | */ | |
89c1d2e7 KM |
269 | |
270 | /* INTSTS0 */ | |
b002ff6e KM |
271 | if (irq_state.intsts0 & VBINT) |
272 | usbhs_mod_info_call(priv, irq_vbus, priv, &irq_state); | |
273 | ||
f1407d5c KM |
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 | ||
88a25e02 NI |
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); | |
89c1d2e7 | 290 | |
88a25e02 NI |
291 | if (irq_state.intsts1 & DTCH) |
292 | usbhs_mod_call(priv, irq_dtch, priv, &irq_state); | |
89c1d2e7 | 293 | |
88a25e02 NI |
294 | if (irq_state.intsts1 & SIGN) |
295 | usbhs_mod_call(priv, irq_sign, priv, &irq_state); | |
89c1d2e7 | 296 | |
88a25e02 NI |
297 | if (irq_state.intsts1 & SACK) |
298 | usbhs_mod_call(priv, irq_sack, priv, &irq_state); | |
299 | } | |
f1407d5c KM |
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; | |
89c1d2e7 | 306 | u16 intenb1 = 0; |
b002ff6e | 307 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); |
f1407d5c | 308 | |
651f5e49 KM |
309 | /* |
310 | * BEMPENB/BRDYENB are picky. | |
311 | * below method is required | |
312 | * | |
313 | * - clear INTSTS0 | |
314 | * - update BEMPENB/BRDYENB | |
315 | * - update INTSTS0 | |
316 | */ | |
f1407d5c | 317 | usbhs_write(priv, INTENB0, 0); |
88a25e02 NI |
318 | if (usbhs_mod_is_host(priv)) |
319 | usbhs_write(priv, INTENB1, 0); | |
f1407d5c KM |
320 | |
321 | usbhs_write(priv, BEMPENB, 0); | |
322 | usbhs_write(priv, BRDYENB, 0); | |
323 | ||
324 | /* | |
325 | * see also | |
326 | * usbhs_interrupt | |
327 | */ | |
328 | ||
b002ff6e KM |
329 | if (info->irq_vbus) |
330 | intenb0 |= VBSE; | |
f1407d5c | 331 | |
5ea68d54 | 332 | if (mod) { |
89c1d2e7 KM |
333 | /* |
334 | * INTSTS0 | |
335 | */ | |
5ea68d54 KM |
336 | if (mod->irq_ctrl_stage) |
337 | intenb0 |= CTRE; | |
f1407d5c | 338 | |
fef22636 ER |
339 | if (mod->irq_dev_state) |
340 | intenb0 |= DVSE; | |
341 | ||
5ea68d54 KM |
342 | if (mod->irq_empty && mod->irq_bempsts) { |
343 | usbhs_write(priv, BEMPENB, mod->irq_bempsts); | |
344 | intenb0 |= BEMPE; | |
345 | } | |
f1407d5c | 346 | |
5ea68d54 KM |
347 | if (mod->irq_ready && mod->irq_brdysts) { |
348 | usbhs_write(priv, BRDYENB, mod->irq_brdysts); | |
349 | intenb0 |= BRDYE; | |
350 | } | |
89c1d2e7 | 351 | |
88a25e02 NI |
352 | if (usbhs_mod_is_host(priv)) { |
353 | /* | |
354 | * INTSTS1 | |
355 | */ | |
356 | if (mod->irq_attch) | |
357 | intenb1 |= ATTCHE; | |
89c1d2e7 | 358 | |
88a25e02 NI |
359 | if (mod->irq_dtch) |
360 | intenb1 |= DTCHE; | |
89c1d2e7 | 361 | |
88a25e02 NI |
362 | if (mod->irq_sign) |
363 | intenb1 |= SIGNE; | |
89c1d2e7 | 364 | |
88a25e02 NI |
365 | if (mod->irq_sack) |
366 | intenb1 |= SACKE; | |
367 | } | |
f1407d5c KM |
368 | } |
369 | ||
651f5e49 KM |
370 | if (intenb0) |
371 | usbhs_write(priv, INTENB0, intenb0); | |
89c1d2e7 | 372 | |
88a25e02 | 373 | if (usbhs_mod_is_host(priv) && intenb1) |
89c1d2e7 | 374 | usbhs_write(priv, INTENB1, intenb1); |
f1407d5c | 375 | } |