diff options
Diffstat (limited to 'drivers/usb/dwc2/core.c')
-rw-r--r-- | drivers/usb/dwc2/core.c | 414 |
1 files changed, 413 insertions, 1 deletions
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index d5197d492e21..c3cc1a78d1e2 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c | |||
@@ -56,6 +56,364 @@ | |||
56 | #include "core.h" | 56 | #include "core.h" |
57 | #include "hcd.h" | 57 | #include "hcd.h" |
58 | 58 | ||
59 | #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) | ||
60 | /** | ||
61 | * dwc2_backup_host_registers() - Backup controller host registers. | ||
62 | * When suspending usb bus, registers needs to be backuped | ||
63 | * if controller power is disabled once suspended. | ||
64 | * | ||
65 | * @hsotg: Programming view of the DWC_otg controller | ||
66 | */ | ||
67 | static int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) | ||
68 | { | ||
69 | struct dwc2_hregs_backup *hr; | ||
70 | int i; | ||
71 | |||
72 | dev_dbg(hsotg->dev, "%s\n", __func__); | ||
73 | |||
74 | /* Backup Host regs */ | ||
75 | hr = &hsotg->hr_backup; | ||
76 | hr->hcfg = readl(hsotg->regs + HCFG); | ||
77 | hr->haintmsk = readl(hsotg->regs + HAINTMSK); | ||
78 | for (i = 0; i < hsotg->core_params->host_channels; ++i) | ||
79 | hr->hcintmsk[i] = readl(hsotg->regs + HCINTMSK(i)); | ||
80 | |||
81 | hr->hprt0 = readl(hsotg->regs + HPRT0); | ||
82 | hr->hfir = readl(hsotg->regs + HFIR); | ||
83 | hr->valid = true; | ||
84 | |||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * dwc2_restore_host_registers() - Restore controller host registers. | ||
90 | * When resuming usb bus, device registers needs to be restored | ||
91 | * if controller power were disabled. | ||
92 | * | ||
93 | * @hsotg: Programming view of the DWC_otg controller | ||
94 | */ | ||
95 | static int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) | ||
96 | { | ||
97 | struct dwc2_hregs_backup *hr; | ||
98 | int i; | ||
99 | |||
100 | dev_dbg(hsotg->dev, "%s\n", __func__); | ||
101 | |||
102 | /* Restore host regs */ | ||
103 | hr = &hsotg->hr_backup; | ||
104 | if (!hr->valid) { | ||
105 | dev_err(hsotg->dev, "%s: no host registers to restore\n", | ||
106 | __func__); | ||
107 | return -EINVAL; | ||
108 | } | ||
109 | hr->valid = false; | ||
110 | |||
111 | writel(hr->hcfg, hsotg->regs + HCFG); | ||
112 | writel(hr->haintmsk, hsotg->regs + HAINTMSK); | ||
113 | |||
114 | for (i = 0; i < hsotg->core_params->host_channels; ++i) | ||
115 | writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i)); | ||
116 | |||
117 | writel(hr->hprt0, hsotg->regs + HPRT0); | ||
118 | writel(hr->hfir, hsotg->regs + HFIR); | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | #else | ||
123 | static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) | ||
124 | { return 0; } | ||
125 | |||
126 | static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) | ||
127 | { return 0; } | ||
128 | #endif | ||
129 | |||
130 | #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ | ||
131 | IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) | ||
132 | /** | ||
133 | * dwc2_backup_device_registers() - Backup controller device registers. | ||
134 | * When suspending usb bus, registers needs to be backuped | ||
135 | * if controller power is disabled once suspended. | ||
136 | * | ||
137 | * @hsotg: Programming view of the DWC_otg controller | ||
138 | */ | ||
139 | static int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) | ||
140 | { | ||
141 | struct dwc2_dregs_backup *dr; | ||
142 | int i; | ||
143 | |||
144 | dev_dbg(hsotg->dev, "%s\n", __func__); | ||
145 | |||
146 | /* Backup dev regs */ | ||
147 | dr = &hsotg->dr_backup; | ||
148 | |||
149 | dr->dcfg = readl(hsotg->regs + DCFG); | ||
150 | dr->dctl = readl(hsotg->regs + DCTL); | ||
151 | dr->daintmsk = readl(hsotg->regs + DAINTMSK); | ||
152 | dr->diepmsk = readl(hsotg->regs + DIEPMSK); | ||
153 | dr->doepmsk = readl(hsotg->regs + DOEPMSK); | ||
154 | |||
155 | for (i = 0; i < hsotg->num_of_eps; i++) { | ||
156 | /* Backup IN EPs */ | ||
157 | dr->diepctl[i] = readl(hsotg->regs + DIEPCTL(i)); | ||
158 | |||
159 | /* Ensure DATA PID is correctly configured */ | ||
160 | if (dr->diepctl[i] & DXEPCTL_DPID) | ||
161 | dr->diepctl[i] |= DXEPCTL_SETD1PID; | ||
162 | else | ||
163 | dr->diepctl[i] |= DXEPCTL_SETD0PID; | ||
164 | |||
165 | dr->dieptsiz[i] = readl(hsotg->regs + DIEPTSIZ(i)); | ||
166 | dr->diepdma[i] = readl(hsotg->regs + DIEPDMA(i)); | ||
167 | |||
168 | /* Backup OUT EPs */ | ||
169 | dr->doepctl[i] = readl(hsotg->regs + DOEPCTL(i)); | ||
170 | |||
171 | /* Ensure DATA PID is correctly configured */ | ||
172 | if (dr->doepctl[i] & DXEPCTL_DPID) | ||
173 | dr->doepctl[i] |= DXEPCTL_SETD1PID; | ||
174 | else | ||
175 | dr->doepctl[i] |= DXEPCTL_SETD0PID; | ||
176 | |||
177 | dr->doeptsiz[i] = readl(hsotg->regs + DOEPTSIZ(i)); | ||
178 | dr->doepdma[i] = readl(hsotg->regs + DOEPDMA(i)); | ||
179 | } | ||
180 | dr->valid = true; | ||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | /** | ||
185 | * dwc2_restore_device_registers() - Restore controller device registers. | ||
186 | * When resuming usb bus, device registers needs to be restored | ||
187 | * if controller power were disabled. | ||
188 | * | ||
189 | * @hsotg: Programming view of the DWC_otg controller | ||
190 | */ | ||
191 | static int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) | ||
192 | { | ||
193 | struct dwc2_dregs_backup *dr; | ||
194 | u32 dctl; | ||
195 | int i; | ||
196 | |||
197 | dev_dbg(hsotg->dev, "%s\n", __func__); | ||
198 | |||
199 | /* Restore dev regs */ | ||
200 | dr = &hsotg->dr_backup; | ||
201 | if (!dr->valid) { | ||
202 | dev_err(hsotg->dev, "%s: no device registers to restore\n", | ||
203 | __func__); | ||
204 | return -EINVAL; | ||
205 | } | ||
206 | dr->valid = false; | ||
207 | |||
208 | writel(dr->dcfg, hsotg->regs + DCFG); | ||
209 | writel(dr->dctl, hsotg->regs + DCTL); | ||
210 | writel(dr->daintmsk, hsotg->regs + DAINTMSK); | ||
211 | writel(dr->diepmsk, hsotg->regs + DIEPMSK); | ||
212 | writel(dr->doepmsk, hsotg->regs + DOEPMSK); | ||
213 | |||
214 | for (i = 0; i < hsotg->num_of_eps; i++) { | ||
215 | /* Restore IN EPs */ | ||
216 | writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i)); | ||
217 | writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i)); | ||
218 | writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i)); | ||
219 | |||
220 | /* Restore OUT EPs */ | ||
221 | writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i)); | ||
222 | writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i)); | ||
223 | writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i)); | ||
224 | } | ||
225 | |||
226 | /* Set the Power-On Programming done bit */ | ||
227 | dctl = readl(hsotg->regs + DCTL); | ||
228 | dctl |= DCTL_PWRONPRGDONE; | ||
229 | writel(dctl, hsotg->regs + DCTL); | ||
230 | |||
231 | return 0; | ||
232 | } | ||
233 | #else | ||
234 | static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) | ||
235 | { return 0; } | ||
236 | |||
237 | static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) | ||
238 | { return 0; } | ||
239 | #endif | ||
240 | |||
241 | /** | ||
242 | * dwc2_backup_global_registers() - Backup global controller registers. | ||
243 | * When suspending usb bus, registers needs to be backuped | ||
244 | * if controller power is disabled once suspended. | ||
245 | * | ||
246 | * @hsotg: Programming view of the DWC_otg controller | ||
247 | */ | ||
248 | static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg) | ||
249 | { | ||
250 | struct dwc2_gregs_backup *gr; | ||
251 | int i; | ||
252 | |||
253 | /* Backup global regs */ | ||
254 | gr = &hsotg->gr_backup; | ||
255 | |||
256 | gr->gotgctl = readl(hsotg->regs + GOTGCTL); | ||
257 | gr->gintmsk = readl(hsotg->regs + GINTMSK); | ||
258 | gr->gahbcfg = readl(hsotg->regs + GAHBCFG); | ||
259 | gr->gusbcfg = readl(hsotg->regs + GUSBCFG); | ||
260 | gr->grxfsiz = readl(hsotg->regs + GRXFSIZ); | ||
261 | gr->gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ); | ||
262 | gr->hptxfsiz = readl(hsotg->regs + HPTXFSIZ); | ||
263 | gr->gdfifocfg = readl(hsotg->regs + GDFIFOCFG); | ||
264 | for (i = 0; i < MAX_EPS_CHANNELS; i++) | ||
265 | gr->dtxfsiz[i] = readl(hsotg->regs + DPTXFSIZN(i)); | ||
266 | |||
267 | gr->valid = true; | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | /** | ||
272 | * dwc2_restore_global_registers() - Restore controller global registers. | ||
273 | * When resuming usb bus, device registers needs to be restored | ||
274 | * if controller power were disabled. | ||
275 | * | ||
276 | * @hsotg: Programming view of the DWC_otg controller | ||
277 | */ | ||
278 | static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) | ||
279 | { | ||
280 | struct dwc2_gregs_backup *gr; | ||
281 | int i; | ||
282 | |||
283 | dev_dbg(hsotg->dev, "%s\n", __func__); | ||
284 | |||
285 | /* Restore global regs */ | ||
286 | gr = &hsotg->gr_backup; | ||
287 | if (!gr->valid) { | ||
288 | dev_err(hsotg->dev, "%s: no global registers to restore\n", | ||
289 | __func__); | ||
290 | return -EINVAL; | ||
291 | } | ||
292 | gr->valid = false; | ||
293 | |||
294 | writel(0xffffffff, hsotg->regs + GINTSTS); | ||
295 | writel(gr->gotgctl, hsotg->regs + GOTGCTL); | ||
296 | writel(gr->gintmsk, hsotg->regs + GINTMSK); | ||
297 | writel(gr->gusbcfg, hsotg->regs + GUSBCFG); | ||
298 | writel(gr->gahbcfg, hsotg->regs + GAHBCFG); | ||
299 | writel(gr->grxfsiz, hsotg->regs + GRXFSIZ); | ||
300 | writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ); | ||
301 | writel(gr->hptxfsiz, hsotg->regs + HPTXFSIZ); | ||
302 | writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG); | ||
303 | for (i = 0; i < MAX_EPS_CHANNELS; i++) | ||
304 | writel(gr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i)); | ||
305 | |||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | /** | ||
310 | * dwc2_exit_hibernation() - Exit controller from Partial Power Down. | ||
311 | * | ||
312 | * @hsotg: Programming view of the DWC_otg controller | ||
313 | * @restore: Controller registers need to be restored | ||
314 | */ | ||
315 | int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore) | ||
316 | { | ||
317 | u32 pcgcctl; | ||
318 | int ret = 0; | ||
319 | |||
320 | if (!hsotg->core_params->hibernation) | ||
321 | return -ENOTSUPP; | ||
322 | |||
323 | pcgcctl = readl(hsotg->regs + PCGCTL); | ||
324 | pcgcctl &= ~PCGCTL_STOPPCLK; | ||
325 | writel(pcgcctl, hsotg->regs + PCGCTL); | ||
326 | |||
327 | pcgcctl = readl(hsotg->regs + PCGCTL); | ||
328 | pcgcctl &= ~PCGCTL_PWRCLMP; | ||
329 | writel(pcgcctl, hsotg->regs + PCGCTL); | ||
330 | |||
331 | pcgcctl = readl(hsotg->regs + PCGCTL); | ||
332 | pcgcctl &= ~PCGCTL_RSTPDWNMODULE; | ||
333 | writel(pcgcctl, hsotg->regs + PCGCTL); | ||
334 | |||
335 | udelay(100); | ||
336 | if (restore) { | ||
337 | ret = dwc2_restore_global_registers(hsotg); | ||
338 | if (ret) { | ||
339 | dev_err(hsotg->dev, "%s: failed to restore registers\n", | ||
340 | __func__); | ||
341 | return ret; | ||
342 | } | ||
343 | if (dwc2_is_host_mode(hsotg)) { | ||
344 | ret = dwc2_restore_host_registers(hsotg); | ||
345 | if (ret) { | ||
346 | dev_err(hsotg->dev, "%s: failed to restore host registers\n", | ||
347 | __func__); | ||
348 | return ret; | ||
349 | } | ||
350 | } else { | ||
351 | ret = dwc2_restore_device_registers(hsotg); | ||
352 | if (ret) { | ||
353 | dev_err(hsotg->dev, "%s: failed to restore device registers\n", | ||
354 | __func__); | ||
355 | return ret; | ||
356 | } | ||
357 | } | ||
358 | } | ||
359 | |||
360 | return ret; | ||
361 | } | ||
362 | |||
363 | /** | ||
364 | * dwc2_enter_hibernation() - Put controller in Partial Power Down. | ||
365 | * | ||
366 | * @hsotg: Programming view of the DWC_otg controller | ||
367 | */ | ||
368 | int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg) | ||
369 | { | ||
370 | u32 pcgcctl; | ||
371 | int ret = 0; | ||
372 | |||
373 | if (!hsotg->core_params->hibernation) | ||
374 | return -ENOTSUPP; | ||
375 | |||
376 | /* Backup all registers */ | ||
377 | ret = dwc2_backup_global_registers(hsotg); | ||
378 | if (ret) { | ||
379 | dev_err(hsotg->dev, "%s: failed to backup global registers\n", | ||
380 | __func__); | ||
381 | return ret; | ||
382 | } | ||
383 | |||
384 | if (dwc2_is_host_mode(hsotg)) { | ||
385 | ret = dwc2_backup_host_registers(hsotg); | ||
386 | if (ret) { | ||
387 | dev_err(hsotg->dev, "%s: failed to backup host registers\n", | ||
388 | __func__); | ||
389 | return ret; | ||
390 | } | ||
391 | } else { | ||
392 | ret = dwc2_backup_device_registers(hsotg); | ||
393 | if (ret) { | ||
394 | dev_err(hsotg->dev, "%s: failed to backup device registers\n", | ||
395 | __func__); | ||
396 | return ret; | ||
397 | } | ||
398 | } | ||
399 | |||
400 | /* Put the controller in low power state */ | ||
401 | pcgcctl = readl(hsotg->regs + PCGCTL); | ||
402 | |||
403 | pcgcctl |= PCGCTL_PWRCLMP; | ||
404 | writel(pcgcctl, hsotg->regs + PCGCTL); | ||
405 | ndelay(20); | ||
406 | |||
407 | pcgcctl |= PCGCTL_RSTPDWNMODULE; | ||
408 | writel(pcgcctl, hsotg->regs + PCGCTL); | ||
409 | ndelay(20); | ||
410 | |||
411 | pcgcctl |= PCGCTL_STOPPCLK; | ||
412 | writel(pcgcctl, hsotg->regs + PCGCTL); | ||
413 | |||
414 | return ret; | ||
415 | } | ||
416 | |||
59 | /** | 417 | /** |
60 | * dwc2_enable_common_interrupts() - Initializes the commmon interrupts, | 418 | * dwc2_enable_common_interrupts() - Initializes the commmon interrupts, |
61 | * used in both device and host modes | 419 | * used in both device and host modes |
@@ -77,8 +435,10 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg) | |||
77 | 435 | ||
78 | if (hsotg->core_params->dma_enable <= 0) | 436 | if (hsotg->core_params->dma_enable <= 0) |
79 | intmsk |= GINTSTS_RXFLVL; | 437 | intmsk |= GINTSTS_RXFLVL; |
438 | if (hsotg->core_params->external_id_pin_ctl <= 0) | ||
439 | intmsk |= GINTSTS_CONIDSTSCHNG; | ||
80 | 440 | ||
81 | intmsk |= GINTSTS_CONIDSTSCHNG | GINTSTS_WKUPINT | GINTSTS_USBSUSP | | 441 | intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP | |
82 | GINTSTS_SESSREQINT; | 442 | GINTSTS_SESSREQINT; |
83 | 443 | ||
84 | writel(intmsk, hsotg->regs + GINTMSK); | 444 | writel(intmsk, hsotg->regs + GINTMSK); |
@@ -2602,6 +2962,40 @@ static void dwc2_set_param_uframe_sched(struct dwc2_hsotg *hsotg, int val) | |||
2602 | hsotg->core_params->uframe_sched = val; | 2962 | hsotg->core_params->uframe_sched = val; |
2603 | } | 2963 | } |
2604 | 2964 | ||
2965 | static void dwc2_set_param_external_id_pin_ctl(struct dwc2_hsotg *hsotg, | ||
2966 | int val) | ||
2967 | { | ||
2968 | if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { | ||
2969 | if (val >= 0) { | ||
2970 | dev_err(hsotg->dev, | ||
2971 | "'%d' invalid for parameter external_id_pin_ctl\n", | ||
2972 | val); | ||
2973 | dev_err(hsotg->dev, "external_id_pin_ctl must be 0 or 1\n"); | ||
2974 | } | ||
2975 | val = 0; | ||
2976 | dev_dbg(hsotg->dev, "Setting external_id_pin_ctl to %d\n", val); | ||
2977 | } | ||
2978 | |||
2979 | hsotg->core_params->external_id_pin_ctl = val; | ||
2980 | } | ||
2981 | |||
2982 | static void dwc2_set_param_hibernation(struct dwc2_hsotg *hsotg, | ||
2983 | int val) | ||
2984 | { | ||
2985 | if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { | ||
2986 | if (val >= 0) { | ||
2987 | dev_err(hsotg->dev, | ||
2988 | "'%d' invalid for parameter hibernation\n", | ||
2989 | val); | ||
2990 | dev_err(hsotg->dev, "hibernation must be 0 or 1\n"); | ||
2991 | } | ||
2992 | val = 0; | ||
2993 | dev_dbg(hsotg->dev, "Setting hibernation to %d\n", val); | ||
2994 | } | ||
2995 | |||
2996 | hsotg->core_params->hibernation = val; | ||
2997 | } | ||
2998 | |||
2605 | /* | 2999 | /* |
2606 | * This function is called during module intialization to pass module parameters | 3000 | * This function is called during module intialization to pass module parameters |
2607 | * for the DWC_otg core. | 3001 | * for the DWC_otg core. |
@@ -2646,6 +3040,8 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg, | |||
2646 | dwc2_set_param_ahbcfg(hsotg, params->ahbcfg); | 3040 | dwc2_set_param_ahbcfg(hsotg, params->ahbcfg); |
2647 | dwc2_set_param_otg_ver(hsotg, params->otg_ver); | 3041 | dwc2_set_param_otg_ver(hsotg, params->otg_ver); |
2648 | dwc2_set_param_uframe_sched(hsotg, params->uframe_sched); | 3042 | dwc2_set_param_uframe_sched(hsotg, params->uframe_sched); |
3043 | dwc2_set_param_external_id_pin_ctl(hsotg, params->external_id_pin_ctl); | ||
3044 | dwc2_set_param_hibernation(hsotg, params->hibernation); | ||
2649 | } | 3045 | } |
2650 | 3046 | ||
2651 | /** | 3047 | /** |
@@ -2814,6 +3210,22 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) | |||
2814 | return 0; | 3210 | return 0; |
2815 | } | 3211 | } |
2816 | 3212 | ||
3213 | /* | ||
3214 | * Sets all parameters to the given value. | ||
3215 | * | ||
3216 | * Assumes that the dwc2_core_params struct contains only integers. | ||
3217 | */ | ||
3218 | void dwc2_set_all_params(struct dwc2_core_params *params, int value) | ||
3219 | { | ||
3220 | int *p = (int *)params; | ||
3221 | size_t size = sizeof(*params) / sizeof(*p); | ||
3222 | int i; | ||
3223 | |||
3224 | for (i = 0; i < size; i++) | ||
3225 | p[i] = value; | ||
3226 | } | ||
3227 | |||
3228 | |||
2817 | u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg) | 3229 | u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg) |
2818 | { | 3230 | { |
2819 | return hsotg->core_params->otg_ver == 1 ? 0x0200 : 0x0103; | 3231 | return hsotg->core_params->otg_ver == 1 ? 0x0200 : 0x0103; |