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