Blame view
drivers/usb/chipidea/core.c
20 KB
e443b3336 usb: chipidea: sp... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/* * core.c - ChipIdea USB IP core family device controller * * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. * * Author: David Lopo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ /* * Description: ChipIdea USB IP core family device controller * * This driver is composed of several blocks: * - HW: hardware interface * - DBG: debug facilities (optional) * - UTIL: utilities * - ISR: interrupts handling * - ENDPT: endpoint operations (Gadget API) * - GADGET: gadget operations (Gadget API) * - BUS: bus glue code, bus abstraction layer * * Compile Options |
58ce8499d usb: chipidea: up... |
26 |
* - CONFIG_USB_CHIPIDEA_DEBUG: enable debug facilities |
e443b3336 usb: chipidea: sp... |
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
* - STALL_IN: non-empty bulk-in pipes cannot be halted * if defined mass storage compliance succeeds but with warnings * => case 4: Hi > Dn * => case 5: Hi > Di * => case 8: Hi <> Do * if undefined usbtest 13 fails * - TRACE: enable function tracing (depends on DEBUG) * * Main Features * - Chapter 9 & Mass Storage Compliance with Gadget File Storage * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined) * - Normal & LPM support * * USBTEST Report * - OK: 0-12, 13 (STALL_IN defined) & 14 * - Not Supported: 15 & 16 (ISO) * * TODO List |
e443b3336 usb: chipidea: sp... |
45 46 47 48 |
* - Suspend & Remote Wakeup */ #include <linux/delay.h> #include <linux/device.h> |
e443b3336 usb: chipidea: sp... |
49 |
#include <linux/dma-mapping.h> |
1e5e2d3d0 usb: chipidea: ad... |
50 |
#include <linux/phy/phy.h> |
e443b3336 usb: chipidea: sp... |
51 52 |
#include <linux/platform_device.h> #include <linux/module.h> |
fe6e125e3 USB: Chipidea: ad... |
53 |
#include <linux/idr.h> |
e443b3336 usb: chipidea: sp... |
54 55 |
#include <linux/interrupt.h> #include <linux/io.h> |
e443b3336 usb: chipidea: sp... |
56 57 58 59 60 61 62 |
#include <linux/kernel.h> #include <linux/slab.h> #include <linux/pm_runtime.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/otg.h> #include <linux/usb/chipidea.h> |
40dcd0e80 usb: chipidea: ad... |
63 |
#include <linux/usb/of.h> |
4f6743d5c usb: chipidea: ud... |
64 |
#include <linux/of.h> |
40dcd0e80 usb: chipidea: ad... |
65 |
#include <linux/phy.h> |
1542d9c35 usb: chipidea: mo... |
66 |
#include <linux/regulator/consumer.h> |
e443b3336 usb: chipidea: sp... |
67 68 69 70 |
#include "ci.h" #include "udc.h" #include "bits.h" |
eb70e5ab8 usb: chipidea: ad... |
71 |
#include "host.h" |
e443b3336 usb: chipidea: sp... |
72 |
#include "debug.h" |
c10b4f033 usb: chipidea: ot... |
73 |
#include "otg.h" |
4dcf720c5 usb: chipidea: OT... |
74 |
#include "otg_fsm.h" |
e443b3336 usb: chipidea: sp... |
75 |
|
5f36e231e usb: chipidea: ad... |
76 |
/* Controller register map */ |
987e7bc34 usb: chipidea: ma... |
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
static const u8 ci_regs_nolpm[] = { [CAP_CAPLENGTH] = 0x00U, [CAP_HCCPARAMS] = 0x08U, [CAP_DCCPARAMS] = 0x24U, [CAP_TESTMODE] = 0x38U, [OP_USBCMD] = 0x00U, [OP_USBSTS] = 0x04U, [OP_USBINTR] = 0x08U, [OP_DEVICEADDR] = 0x14U, [OP_ENDPTLISTADDR] = 0x18U, [OP_PORTSC] = 0x44U, [OP_DEVLC] = 0x84U, [OP_OTGSC] = 0x64U, [OP_USBMODE] = 0x68U, [OP_ENDPTSETUPSTAT] = 0x6CU, [OP_ENDPTPRIME] = 0x70U, [OP_ENDPTFLUSH] = 0x74U, [OP_ENDPTSTAT] = 0x78U, [OP_ENDPTCOMPLETE] = 0x7CU, [OP_ENDPTCTRL] = 0x80U, |
e443b3336 usb: chipidea: sp... |
97 |
}; |
987e7bc34 usb: chipidea: ma... |
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
static const u8 ci_regs_lpm[] = { [CAP_CAPLENGTH] = 0x00U, [CAP_HCCPARAMS] = 0x08U, [CAP_DCCPARAMS] = 0x24U, [CAP_TESTMODE] = 0xFCU, [OP_USBCMD] = 0x00U, [OP_USBSTS] = 0x04U, [OP_USBINTR] = 0x08U, [OP_DEVICEADDR] = 0x14U, [OP_ENDPTLISTADDR] = 0x18U, [OP_PORTSC] = 0x44U, [OP_DEVLC] = 0x84U, [OP_OTGSC] = 0xC4U, [OP_USBMODE] = 0xC8U, [OP_ENDPTSETUPSTAT] = 0xD8U, [OP_ENDPTPRIME] = 0xDCU, [OP_ENDPTFLUSH] = 0xE0U, [OP_ENDPTSTAT] = 0xE4U, [OP_ENDPTCOMPLETE] = 0xE8U, [OP_ENDPTCTRL] = 0xECU, |
e443b3336 usb: chipidea: sp... |
118 |
}; |
8e22978c5 usb: chipidea: dr... |
119 |
static int hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm) |
e443b3336 usb: chipidea: sp... |
120 121 |
{ int i; |
e443b3336 usb: chipidea: sp... |
122 |
for (i = 0; i < OP_ENDPTCTRL; i++) |
5f36e231e usb: chipidea: ad... |
123 124 |
ci->hw_bank.regmap[i] = (i <= CAP_LAST ? ci->hw_bank.cap : ci->hw_bank.op) + |
e443b3336 usb: chipidea: sp... |
125 126 127 |
(is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]); for (; i <= OP_LAST; i++) |
5f36e231e usb: chipidea: ad... |
128 |
ci->hw_bank.regmap[i] = ci->hw_bank.op + |
e443b3336 usb: chipidea: sp... |
129 130 131 132 133 134 135 136 137 |
4 * (i - OP_ENDPTCTRL) + (is_lpm ? ci_regs_lpm[OP_ENDPTCTRL] : ci_regs_nolpm[OP_ENDPTCTRL]); return 0; } /** |
36304b061 usb: chipidea: ex... |
138 139 |
* hw_read_intr_enable: returns interrupt enable register * |
19353881b usb: chipidea: en... |
140 141 |
* @ci: the controller * |
36304b061 usb: chipidea: ex... |
142 143 144 145 146 147 148 149 150 151 |
* This function returns register data */ u32 hw_read_intr_enable(struct ci_hdrc *ci) { return hw_read(ci, OP_USBINTR, ~0); } /** * hw_read_intr_status: returns interrupt status register * |
19353881b usb: chipidea: en... |
152 153 |
* @ci: the controller * |
36304b061 usb: chipidea: ex... |
154 155 156 157 158 159 160 161 |
* This function returns register data */ u32 hw_read_intr_status(struct ci_hdrc *ci) { return hw_read(ci, OP_USBSTS, ~0); } /** |
e443b3336 usb: chipidea: sp... |
162 163 164 165 166 |
* hw_port_test_set: writes port test mode (execute without interruption) * @mode: new value * * This function returns an error code */ |
8e22978c5 usb: chipidea: dr... |
167 |
int hw_port_test_set(struct ci_hdrc *ci, u8 mode) |
e443b3336 usb: chipidea: sp... |
168 169 170 171 172 |
{ const u8 TEST_MODE_MAX = 7; if (mode > TEST_MODE_MAX) return -EINVAL; |
727b4ddb4 usb: chipidea: do... |
173 |
hw_write(ci, OP_PORTSC, PORTSC_PTC, mode << __ffs(PORTSC_PTC)); |
e443b3336 usb: chipidea: sp... |
174 175 176 177 178 179 |
return 0; } /** * hw_port_test_get: reads port test mode value * |
19353881b usb: chipidea: en... |
180 181 |
* @ci: the controller * |
e443b3336 usb: chipidea: sp... |
182 183 |
* This function returns port test mode value */ |
8e22978c5 usb: chipidea: dr... |
184 |
u8 hw_port_test_get(struct ci_hdrc *ci) |
e443b3336 usb: chipidea: sp... |
185 |
{ |
727b4ddb4 usb: chipidea: do... |
186 |
return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC); |
e443b3336 usb: chipidea: sp... |
187 |
} |
b82613cf0 usb: chipidea: ad... |
188 189 190 191 192 193 194 195 196 197 |
static void hw_wait_phy_stable(void) { /* * The phy needs some delay to output the stable status from low * power mode. And for OTGSC, the status inputs are debounced * using a 1 ms time constant, so, delay 2ms for controller to get * the stable status, like vbus and id when the phy leaves low power. */ usleep_range(2000, 2500); } |
864cf9499 usb: chipidea: ad... |
198 199 200 201 202 |
/* The PHY enters/leaves low power mode */ static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable) { enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC; bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm))); |
6d037db64 usb: chipidea: re... |
203 |
if (enable && !lpm) |
864cf9499 usb: chipidea: ad... |
204 205 |
hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm), PORTSC_PHCD(ci->hw_bank.lpm)); |
6d037db64 usb: chipidea: re... |
206 |
else if (!enable && lpm) |
864cf9499 usb: chipidea: ad... |
207 208 |
hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm), 0); |
864cf9499 usb: chipidea: ad... |
209 |
} |
8e22978c5 usb: chipidea: dr... |
210 |
static int hw_device_init(struct ci_hdrc *ci, void __iomem *base) |
e443b3336 usb: chipidea: sp... |
211 212 213 214 |
{ u32 reg; /* bank is a module variable */ |
5f36e231e usb: chipidea: ad... |
215 |
ci->hw_bank.abs = base; |
e443b3336 usb: chipidea: sp... |
216 |
|
5f36e231e usb: chipidea: ad... |
217 |
ci->hw_bank.cap = ci->hw_bank.abs; |
77c4400f2 USB: Chipidea: re... |
218 |
ci->hw_bank.cap += ci->platdata->capoffset; |
938d323f1 usb: chipidea: bi... |
219 |
ci->hw_bank.op = ci->hw_bank.cap + (ioread32(ci->hw_bank.cap) & 0xff); |
e443b3336 usb: chipidea: sp... |
220 |
|
5f36e231e usb: chipidea: ad... |
221 222 |
hw_alloc_regmap(ci, false); reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >> |
727b4ddb4 usb: chipidea: do... |
223 |
__ffs(HCCPARAMS_LEN); |
5f36e231e usb: chipidea: ad... |
224 |
ci->hw_bank.lpm = reg; |
aeb2c1210 usb: chipidea: Re... |
225 226 |
if (reg) hw_alloc_regmap(ci, !!reg); |
5f36e231e usb: chipidea: ad... |
227 228 229 |
ci->hw_bank.size = ci->hw_bank.op - ci->hw_bank.abs; ci->hw_bank.size += OP_LAST; ci->hw_bank.size /= sizeof(u32); |
e443b3336 usb: chipidea: sp... |
230 |
|
5f36e231e usb: chipidea: ad... |
231 |
reg = hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DEN) >> |
727b4ddb4 usb: chipidea: do... |
232 |
__ffs(DCCPARAMS_DEN); |
5f36e231e usb: chipidea: ad... |
233 |
ci->hw_ep_max = reg * 2; /* cache hw ENDPT_MAX */ |
e443b3336 usb: chipidea: sp... |
234 |
|
09c94e628 usb: chipidea: re... |
235 |
if (ci->hw_ep_max > ENDPT_MAX) |
e443b3336 usb: chipidea: sp... |
236 |
return -ENODEV; |
864cf9499 usb: chipidea: ad... |
237 |
ci_hdrc_enter_lpm(ci, false); |
c344b5180 usb: chipidea: di... |
238 239 240 241 242 |
/* Disable all interrupts bits */ hw_write(ci, OP_USBINTR, 0xffffffff, 0); /* Clear all interrupts status bits*/ hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff); |
5f36e231e usb: chipidea: ad... |
243 244 245 |
dev_dbg(ci->dev, "ChipIdea HDRC found, lpm: %d; cap: %p op: %p ", ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op); |
e443b3336 usb: chipidea: sp... |
246 247 248 249 250 251 252 253 254 |
/* setup lock mode ? */ /* ENDPTSETUPSTAT is '0' by default */ /* HCSPARAMS.bf.ppc SHOULD BE zero for device */ return 0; } |
8e22978c5 usb: chipidea: dr... |
255 |
static void hw_phymode_configure(struct ci_hdrc *ci) |
40dcd0e80 usb: chipidea: ad... |
256 |
{ |
3b5d3e684 usb: chipidea: Fi... |
257 |
u32 portsc, lpm, sts = 0; |
40dcd0e80 usb: chipidea: ad... |
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
switch (ci->platdata->phy_mode) { case USBPHY_INTERFACE_MODE_UTMI: portsc = PORTSC_PTS(PTS_UTMI); lpm = DEVLC_PTS(PTS_UTMI); break; case USBPHY_INTERFACE_MODE_UTMIW: portsc = PORTSC_PTS(PTS_UTMI) | PORTSC_PTW; lpm = DEVLC_PTS(PTS_UTMI) | DEVLC_PTW; break; case USBPHY_INTERFACE_MODE_ULPI: portsc = PORTSC_PTS(PTS_ULPI); lpm = DEVLC_PTS(PTS_ULPI); break; case USBPHY_INTERFACE_MODE_SERIAL: portsc = PORTSC_PTS(PTS_SERIAL); lpm = DEVLC_PTS(PTS_SERIAL); sts = 1; break; case USBPHY_INTERFACE_MODE_HSIC: portsc = PORTSC_PTS(PTS_HSIC); lpm = DEVLC_PTS(PTS_HSIC); break; default: return; } if (ci->hw_bank.lpm) { hw_write(ci, OP_DEVLC, DEVLC_PTS(7) | DEVLC_PTW, lpm); |
3b5d3e684 usb: chipidea: Fi... |
287 288 |
if (sts) hw_write(ci, OP_DEVLC, DEVLC_STS, DEVLC_STS); |
40dcd0e80 usb: chipidea: ad... |
289 290 |
} else { hw_write(ci, OP_PORTSC, PORTSC_PTS(7) | PORTSC_PTW, portsc); |
3b5d3e684 usb: chipidea: Fi... |
291 292 |
if (sts) hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS); |
40dcd0e80 usb: chipidea: ad... |
293 294 |
} } |
e443b3336 usb: chipidea: sp... |
295 |
/** |
1e5e2d3d0 usb: chipidea: ad... |
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
* _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy * interfaces * @ci: the controller * * This function returns an error code if the phy failed to init */ static int _ci_usb_phy_init(struct ci_hdrc *ci) { int ret; if (ci->phy) { ret = phy_init(ci->phy); if (ret) return ret; ret = phy_power_on(ci->phy); if (ret) { phy_exit(ci->phy); return ret; } } else { ret = usb_phy_init(ci->usb_phy); } return ret; } /** * _ci_usb_phy_exit: deinitialize phy taking in account both phy and usb_phy * interfaces * @ci: the controller */ static void ci_usb_phy_exit(struct ci_hdrc *ci) { if (ci->phy) { phy_power_off(ci->phy); phy_exit(ci->phy); } else { usb_phy_shutdown(ci->usb_phy); } } /** |
d03cccff9 usb: chipidea: co... |
339 340 |
* ci_usb_phy_init: initialize phy according to different phy type * @ci: the controller |
19353881b usb: chipidea: en... |
341 |
* |
d03cccff9 usb: chipidea: co... |
342 343 344 345 346 347 348 349 350 351 |
* This function returns an error code if usb_phy_init has failed */ static int ci_usb_phy_init(struct ci_hdrc *ci) { int ret; switch (ci->platdata->phy_mode) { case USBPHY_INTERFACE_MODE_UTMI: case USBPHY_INTERFACE_MODE_UTMIW: case USBPHY_INTERFACE_MODE_HSIC: |
1e5e2d3d0 usb: chipidea: ad... |
352 |
ret = _ci_usb_phy_init(ci); |
b82613cf0 usb: chipidea: ad... |
353 354 355 |
if (!ret) hw_wait_phy_stable(); else |
d03cccff9 usb: chipidea: co... |
356 357 358 359 360 361 |
return ret; hw_phymode_configure(ci); break; case USBPHY_INTERFACE_MODE_ULPI: case USBPHY_INTERFACE_MODE_SERIAL: hw_phymode_configure(ci); |
1e5e2d3d0 usb: chipidea: ad... |
362 |
ret = _ci_usb_phy_init(ci); |
d03cccff9 usb: chipidea: co... |
363 364 365 366 |
if (ret) return ret; break; default: |
1e5e2d3d0 usb: chipidea: ad... |
367 |
ret = _ci_usb_phy_init(ci); |
b82613cf0 usb: chipidea: ad... |
368 369 |
if (!ret) hw_wait_phy_stable(); |
d03cccff9 usb: chipidea: co... |
370 371 372 373 374 375 |
} return ret; } /** |
cdd278f21 usb: chipidea: ad... |
376 |
* hw_controller_reset: do controller reset |
e443b3336 usb: chipidea: sp... |
377 378 379 380 |
* @ci: the controller * * This function returns an error code */ |
cdd278f21 usb: chipidea: ad... |
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 |
static int hw_controller_reset(struct ci_hdrc *ci) { int count = 0; hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST); while (hw_read(ci, OP_USBCMD, USBCMD_RST)) { udelay(10); if (count++ > 1000) return -ETIMEDOUT; } return 0; } /** * hw_device_reset: resets chip (execute without interruption) * @ci: the controller * * This function returns an error code */ |
5b1573005 usb: chipidea: pa... |
401 |
int hw_device_reset(struct ci_hdrc *ci) |
e443b3336 usb: chipidea: sp... |
402 |
{ |
cdd278f21 usb: chipidea: ad... |
403 |
int ret; |
e443b3336 usb: chipidea: sp... |
404 405 406 |
/* should flush & stop before reset */ hw_write(ci, OP_ENDPTFLUSH, ~0, ~0); hw_write(ci, OP_USBCMD, USBCMD_RS, 0); |
cdd278f21 usb: chipidea: ad... |
407 408 409 410 411 412 |
ret = hw_controller_reset(ci); if (ret) { dev_err(ci->dev, "error resetting controller, ret=%d ", ret); return ret; } |
e443b3336 usb: chipidea: sp... |
413 |
|
77c4400f2 USB: Chipidea: re... |
414 415 |
if (ci->platdata->notify_event) ci->platdata->notify_event(ci, |
8e22978c5 usb: chipidea: dr... |
416 |
CI_HDRC_CONTROLLER_RESET_EVENT); |
e443b3336 usb: chipidea: sp... |
417 |
|
8e22978c5 usb: chipidea: dr... |
418 |
if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING) |
758fc9860 usb: chipidea: us... |
419 |
hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); |
e443b3336 usb: chipidea: sp... |
420 |
|
4f6743d5c usb: chipidea: ud... |
421 422 423 424 425 426 |
if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) { if (ci->hw_bank.lpm) hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC); else hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); } |
e443b3336 usb: chipidea: sp... |
427 428 |
/* USBMODE should be configured step by step */ hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); |
5b1573005 usb: chipidea: pa... |
429 |
hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_DC); |
e443b3336 usb: chipidea: sp... |
430 431 |
/* HW >= 2.3 */ hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); |
5b1573005 usb: chipidea: pa... |
432 433 |
if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { pr_err("cannot enter in %s device mode", ci_role(ci)->name); |
e443b3336 usb: chipidea: sp... |
434 435 436 437 438 439 |
pr_err("lpm = %i", ci->hw_bank.lpm); return -ENODEV; } return 0; } |
22fa84455 usb: chipidea: ad... |
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 |
/** * hw_wait_reg: wait the register value * * Sometimes, it needs to wait register value before going on. * Eg, when switch to device mode, the vbus value should be lower * than OTGSC_BSV before connects to host. * * @ci: the controller * @reg: register index * @mask: mast bit * @value: the bit value to wait * @timeout_ms: timeout in millisecond * * This function returns an error code if timeout */ int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask, u32 value, unsigned int timeout_ms) { unsigned long elapse = jiffies + msecs_to_jiffies(timeout_ms); while (hw_read(ci, reg, mask) != value) { if (time_after(jiffies, elapse)) { dev_err(ci->dev, "timeout waiting for %08x in %d ", mask, reg); return -ETIMEDOUT; } msleep(20); } return 0; } |
5f36e231e usb: chipidea: ad... |
472 473 |
static irqreturn_t ci_irq(int irq, void *data) { |
8e22978c5 usb: chipidea: dr... |
474 |
struct ci_hdrc *ci = data; |
5f36e231e usb: chipidea: ad... |
475 |
irqreturn_t ret = IRQ_NONE; |
b183c19f9 USB: chipidea: re... |
476 |
u32 otgsc = 0; |
5f36e231e usb: chipidea: ad... |
477 |
|
4dcf720c5 usb: chipidea: OT... |
478 |
if (ci->is_otg) { |
0c33bf781 usb: chipidea: op... |
479 |
otgsc = hw_read_otgsc(ci, ~0); |
4dcf720c5 usb: chipidea: OT... |
480 481 482 483 484 485 |
if (ci_otg_is_fsm_mode(ci)) { ret = ci_otg_fsm_irq(ci); if (ret == IRQ_HANDLED) return ret; } } |
5f36e231e usb: chipidea: ad... |
486 |
|
a107f8c50 usb: chipidea: ad... |
487 488 489 490 491 492 |
/* * Handle id change interrupt, it indicates device/host function * switch. */ if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { ci->id_event = true; |
0c33bf781 usb: chipidea: op... |
493 494 |
/* Clear ID change irq status */ hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); |
be6b0c1bd usb: chipidea: us... |
495 |
ci_otg_queue_work(ci); |
a107f8c50 usb: chipidea: ad... |
496 497 |
return IRQ_HANDLED; } |
b183c19f9 USB: chipidea: re... |
498 |
|
a107f8c50 usb: chipidea: ad... |
499 500 501 502 503 504 |
/* * Handle vbus change interrupt, it indicates device connection * and disconnection events. */ if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { ci->b_sess_valid_event = true; |
0c33bf781 usb: chipidea: op... |
505 506 |
/* Clear BSV irq */ hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); |
be6b0c1bd usb: chipidea: us... |
507 |
ci_otg_queue_work(ci); |
a107f8c50 usb: chipidea: ad... |
508 |
return IRQ_HANDLED; |
5f36e231e usb: chipidea: ad... |
509 |
} |
a107f8c50 usb: chipidea: ad... |
510 511 512 |
/* Handle device/host interrupt */ if (ci->role != CI_ROLE_END) ret = ci_role(ci)->irq(ci); |
b183c19f9 USB: chipidea: re... |
513 |
return ret; |
5f36e231e usb: chipidea: ad... |
514 |
} |
1542d9c35 usb: chipidea: mo... |
515 516 517 |
static int ci_get_platdata(struct device *dev, struct ci_hdrc_platform_data *platdata) { |
c22600c3e usb: chipidea: mo... |
518 519 520 521 522 523 524 525 |
if (!platdata->phy_mode) platdata->phy_mode = of_usb_get_phy_mode(dev->of_node); if (!platdata->dr_mode) platdata->dr_mode = of_usb_get_dr_mode(dev->of_node); if (platdata->dr_mode == USB_DR_MODE_UNKNOWN) platdata->dr_mode = USB_DR_MODE_OTG; |
c2ec3a732 usb: chipidea: on... |
526 527 528 529 530 531 |
if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) { /* Get the vbus regulator */ platdata->reg_vbus = devm_regulator_get(dev, "vbus"); if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) { return -EPROBE_DEFER; } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) { |
6629467ba usb: chipidea: Fi... |
532 |
/* no vbus regulator is needed */ |
c2ec3a732 usb: chipidea: on... |
533 534 535 536 537 538 539 |
platdata->reg_vbus = NULL; } else if (IS_ERR(platdata->reg_vbus)) { dev_err(dev, "Getting regulator error: %ld ", PTR_ERR(platdata->reg_vbus)); return PTR_ERR(platdata->reg_vbus); } |
f6a9ff078 usb: chipidea: ad... |
540 541 542 543 |
/* Get TPL support */ if (!platdata->tpl_support) platdata->tpl_support = of_usb_host_tpl_support(dev->of_node); |
c2ec3a732 usb: chipidea: on... |
544 |
} |
4f6743d5c usb: chipidea: ud... |
545 546 |
if (of_usb_get_maximum_speed(dev->of_node) == USB_SPEED_FULL) platdata->flags |= CI_HDRC_FORCE_FULLSPEED; |
1542d9c35 usb: chipidea: mo... |
547 548 |
return 0; } |
fe6e125e3 USB: Chipidea: ad... |
549 |
static DEFINE_IDA(ci_ida); |
8e22978c5 usb: chipidea: dr... |
550 |
struct platform_device *ci_hdrc_add_device(struct device *dev, |
cbc6dc2af USB: Chipidea: ad... |
551 |
struct resource *res, int nres, |
8e22978c5 usb: chipidea: dr... |
552 |
struct ci_hdrc_platform_data *platdata) |
cbc6dc2af USB: Chipidea: ad... |
553 554 |
{ struct platform_device *pdev; |
fe6e125e3 USB: Chipidea: ad... |
555 |
int id, ret; |
cbc6dc2af USB: Chipidea: ad... |
556 |
|
1542d9c35 usb: chipidea: mo... |
557 558 559 |
ret = ci_get_platdata(dev, platdata); if (ret) return ERR_PTR(ret); |
fe6e125e3 USB: Chipidea: ad... |
560 561 562 563 564 565 566 567 568 |
id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL); if (id < 0) return ERR_PTR(id); pdev = platform_device_alloc("ci_hdrc", id); if (!pdev) { ret = -ENOMEM; goto put_id; } |
cbc6dc2af USB: Chipidea: ad... |
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 |
pdev->dev.parent = dev; pdev->dev.dma_mask = dev->dma_mask; pdev->dev.dma_parms = dev->dma_parms; dma_set_coherent_mask(&pdev->dev, dev->coherent_dma_mask); ret = platform_device_add_resources(pdev, res, nres); if (ret) goto err; ret = platform_device_add_data(pdev, platdata, sizeof(*platdata)); if (ret) goto err; ret = platform_device_add(pdev); if (ret) goto err; return pdev; err: platform_device_put(pdev); |
fe6e125e3 USB: Chipidea: ad... |
591 592 |
put_id: ida_simple_remove(&ci_ida, id); |
cbc6dc2af USB: Chipidea: ad... |
593 594 |
return ERR_PTR(ret); } |
8e22978c5 usb: chipidea: dr... |
595 |
EXPORT_SYMBOL_GPL(ci_hdrc_add_device); |
cbc6dc2af USB: Chipidea: ad... |
596 |
|
8e22978c5 usb: chipidea: dr... |
597 |
void ci_hdrc_remove_device(struct platform_device *pdev) |
cbc6dc2af USB: Chipidea: ad... |
598 |
{ |
98c355344 USB: chipidea: fi... |
599 |
int id = pdev->id; |
cbc6dc2af USB: Chipidea: ad... |
600 |
platform_device_unregister(pdev); |
98c355344 USB: chipidea: fi... |
601 |
ida_simple_remove(&ci_ida, id); |
cbc6dc2af USB: Chipidea: ad... |
602 |
} |
8e22978c5 usb: chipidea: dr... |
603 |
EXPORT_SYMBOL_GPL(ci_hdrc_remove_device); |
cbc6dc2af USB: Chipidea: ad... |
604 |
|
3f124d233 usb: chipidea: ad... |
605 606 607 608 |
static inline void ci_role_destroy(struct ci_hdrc *ci) { ci_hdrc_gadget_destroy(ci); ci_hdrc_host_destroy(ci); |
cbec6bd55 usb: chipidea: mo... |
609 610 |
if (ci->is_otg) ci_hdrc_otg_destroy(ci); |
3f124d233 usb: chipidea: ad... |
611 |
} |
577b232fc usb: chipidea: ad... |
612 613 614 615 616 617 618 619 |
static void ci_get_otg_capable(struct ci_hdrc *ci) { if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG) ci->is_otg = false; else ci->is_otg = (hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC | DCCPARAMS_HC) == (DCCPARAMS_DC | DCCPARAMS_HC)); |
90893b90d usb: chipidea: ad... |
620 |
if (ci->is_otg) |
577b232fc usb: chipidea: ad... |
621 622 623 |
dev_dbg(ci->dev, "It is OTG capable controller "); } |
41ac7b3ab usb: remove use o... |
624 |
static int ci_hdrc_probe(struct platform_device *pdev) |
e443b3336 usb: chipidea: sp... |
625 626 |
{ struct device *dev = &pdev->dev; |
8e22978c5 usb: chipidea: dr... |
627 |
struct ci_hdrc *ci; |
e443b3336 usb: chipidea: sp... |
628 629 630 |
struct resource *res; void __iomem *base; int ret; |
691962d15 usb: chipidea: in... |
631 |
enum usb_dr_mode dr_mode; |
e443b3336 usb: chipidea: sp... |
632 |
|
fad56745a usb: chipidea: us... |
633 |
if (!dev_get_platdata(dev)) { |
e443b3336 usb: chipidea: sp... |
634 635 636 637 638 639 |
dev_err(dev, "platform data missing "); return -ENODEV; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
19290816c usb: chipidea: co... |
640 641 642 |
base = devm_ioremap_resource(dev, res); if (IS_ERR(base)) return PTR_ERR(base); |
e443b3336 usb: chipidea: sp... |
643 |
|
5f36e231e usb: chipidea: ad... |
644 |
ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL); |
d0f992498 usb: chipidea: co... |
645 |
if (!ci) |
5f36e231e usb: chipidea: ad... |
646 |
return -ENOMEM; |
5f36e231e usb: chipidea: ad... |
647 648 |
ci->dev = dev; |
fad56745a usb: chipidea: us... |
649 |
ci->platdata = dev_get_platdata(dev); |
ed8f8318d usb: chipidea: ad... |
650 651 |
ci->imx28_write_fix = !!(ci->platdata->flags & CI_HDRC_IMX28_WRITE_FIX); |
5f36e231e usb: chipidea: ad... |
652 653 654 655 656 657 658 |
ret = hw_device_init(ci, base); if (ret < 0) { dev_err(dev, "can't initialize hardware "); return -ENODEV; } |
e443b3336 usb: chipidea: sp... |
659 |
|
1e5e2d3d0 usb: chipidea: ad... |
660 661 662 |
if (ci->platdata->phy) { ci->phy = ci->platdata->phy; } else if (ci->platdata->usb_phy) { |
ef44cb422 usb: allow to sup... |
663 |
ci->usb_phy = ci->platdata->usb_phy; |
1e5e2d3d0 usb: chipidea: ad... |
664 |
} else { |
21a5b579c usb: chipidea: fi... |
665 666 |
ci->phy = devm_phy_get(dev->parent, "usb-phy"); ci->usb_phy = devm_usb_get_phy(dev->parent, USB_PHY_TYPE_USB2); |
c859aa65a usb: chipidea: re... |
667 |
|
1e5e2d3d0 usb: chipidea: ad... |
668 669 670 671 672 673 674 |
/* if both generic PHY and USB PHY layers aren't enabled */ if (PTR_ERR(ci->phy) == -ENOSYS && PTR_ERR(ci->usb_phy) == -ENXIO) return -ENXIO; if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) return -EPROBE_DEFER; |
c859aa65a usb: chipidea: re... |
675 |
|
1e5e2d3d0 usb: chipidea: ad... |
676 677 678 679 |
if (IS_ERR(ci->phy)) ci->phy = NULL; else if (IS_ERR(ci->usb_phy)) ci->usb_phy = NULL; |
c859aa65a usb: chipidea: re... |
680 |
} |
d03cccff9 usb: chipidea: co... |
681 |
ret = ci_usb_phy_init(ci); |
74475ede7 usb: chipidea: mo... |
682 683 684 685 686 |
if (ret) { dev_err(dev, "unable to init phy: %d ", ret); return ret; } |
eb70e5ab8 usb: chipidea: ad... |
687 |
ci->hw_bank.phys = res->start; |
5f36e231e usb: chipidea: ad... |
688 689 |
ci->irq = platform_get_irq(pdev, 0); if (ci->irq < 0) { |
e443b3336 usb: chipidea: sp... |
690 691 |
dev_err(dev, "missing IRQ "); |
42d182124 usb: chipidea: Pr... |
692 |
ret = ci->irq; |
c859aa65a usb: chipidea: re... |
693 |
goto deinit_phy; |
5f36e231e usb: chipidea: ad... |
694 |
} |
577b232fc usb: chipidea: ad... |
695 |
ci_get_otg_capable(ci); |
691962d15 usb: chipidea: in... |
696 |
dr_mode = ci->platdata->dr_mode; |
5f36e231e usb: chipidea: ad... |
697 |
/* initialize role(s) before the interrupt is requested */ |
691962d15 usb: chipidea: in... |
698 699 700 701 702 703 |
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) { ret = ci_hdrc_host_init(ci); if (ret) dev_info(dev, "doesn't support host "); } |
eb70e5ab8 usb: chipidea: ad... |
704 |
|
691962d15 usb: chipidea: in... |
705 706 707 708 709 710 |
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) { ret = ci_hdrc_gadget_init(ci); if (ret) dev_info(dev, "doesn't support gadget "); } |
5f36e231e usb: chipidea: ad... |
711 712 713 714 |
if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) { dev_err(dev, "no supported roles "); |
74475ede7 usb: chipidea: mo... |
715 |
ret = -ENODEV; |
c859aa65a usb: chipidea: re... |
716 |
goto deinit_phy; |
cbec6bd55 usb: chipidea: mo... |
717 |
} |
27c62c2da usb: chipidea: ot... |
718 |
if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) { |
90893b90d usb: chipidea: ad... |
719 720 721 |
/* Disable and clear all OTG irq */ hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, OTGSC_INT_STATUS_BITS); |
cbec6bd55 usb: chipidea: mo... |
722 723 724 725 726 727 |
ret = ci_hdrc_otg_init(ci); if (ret) { dev_err(dev, "init otg fails, ret = %d ", ret); goto stop; } |
5f36e231e usb: chipidea: ad... |
728 729 730 |
} if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { |
577b232fc usb: chipidea: ad... |
731 |
if (ci->is_otg) { |
577b232fc usb: chipidea: ad... |
732 |
ci->role = ci_otg_role(ci); |
0c33bf781 usb: chipidea: op... |
733 734 |
/* Enable ID change irq */ hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); |
577b232fc usb: chipidea: ad... |
735 736 737 738 739 740 741 742 |
} else { /* * If the controller is not OTG capable, but support * role switch, the defalt role is gadget, and the * user can switch it through debugfs. */ ci->role = CI_ROLE_GADGET; } |
5f36e231e usb: chipidea: ad... |
743 744 745 746 747 |
} else { ci->role = ci->roles[CI_ROLE_HOST] ? CI_ROLE_HOST : CI_ROLE_GADGET; } |
5a1e1456f usb: chipidea: fi... |
748 749 750 |
/* only update vbus status for peripheral */ if (ci->role == CI_ROLE_GADGET) ci_handle_vbus_change(ci); |
4dcf720c5 usb: chipidea: OT... |
751 752 753 754 755 756 757 758 |
if (!ci_otg_is_fsm_mode(ci)) { ret = ci_role_start(ci, ci->role); if (ret) { dev_err(dev, "can't start %s role ", ci_role(ci)->name); goto stop; } |
e443b3336 usb: chipidea: sp... |
759 |
} |
24c498df1 Revert "usb: chip... |
760 |
platform_set_drvdata(pdev, ci); |
4c503dd5f usb: chipidea: us... |
761 762 |
ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED, ci->platdata->name, ci); |
5f36e231e usb: chipidea: ad... |
763 764 |
if (ret) goto stop; |
e443b3336 usb: chipidea: sp... |
765 |
|
4dcf720c5 usb: chipidea: OT... |
766 767 |
if (ci_otg_is_fsm_mode(ci)) ci_hdrc_otg_fsm_start(ci); |
adf0f735e usb: chipidea: mo... |
768 769 770 |
ret = dbg_create_files(ci); if (!ret) return 0; |
5f36e231e usb: chipidea: ad... |
771 |
|
5f36e231e usb: chipidea: ad... |
772 |
stop: |
3f124d233 usb: chipidea: ad... |
773 |
ci_role_destroy(ci); |
c859aa65a usb: chipidea: re... |
774 |
deinit_phy: |
1e5e2d3d0 usb: chipidea: ad... |
775 |
ci_usb_phy_exit(ci); |
e443b3336 usb: chipidea: sp... |
776 777 778 |
return ret; } |
fb4e98ab6 usb: remove use o... |
779 |
static int ci_hdrc_remove(struct platform_device *pdev) |
e443b3336 usb: chipidea: sp... |
780 |
{ |
8e22978c5 usb: chipidea: dr... |
781 |
struct ci_hdrc *ci = platform_get_drvdata(pdev); |
e443b3336 usb: chipidea: sp... |
782 |
|
adf0f735e usb: chipidea: mo... |
783 |
dbg_remove_files(ci); |
3f124d233 usb: chipidea: ad... |
784 |
ci_role_destroy(ci); |
864cf9499 usb: chipidea: ad... |
785 |
ci_hdrc_enter_lpm(ci, true); |
1e5e2d3d0 usb: chipidea: ad... |
786 |
ci_usb_phy_exit(ci); |
e443b3336 usb: chipidea: sp... |
787 788 789 |
return 0; } |
8076932ff usb: chipidea: ad... |
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 |
#ifdef CONFIG_PM_SLEEP static void ci_controller_suspend(struct ci_hdrc *ci) { ci_hdrc_enter_lpm(ci, true); if (ci->usb_phy) usb_phy_set_suspend(ci->usb_phy, 1); } static int ci_controller_resume(struct device *dev) { struct ci_hdrc *ci = dev_get_drvdata(dev); dev_dbg(dev, "at %s ", __func__); ci_hdrc_enter_lpm(ci, false); if (ci->usb_phy) { usb_phy_set_suspend(ci->usb_phy, 0); usb_phy_set_wakeup(ci->usb_phy, false); hw_wait_phy_stable(); } return 0; } static int ci_suspend(struct device *dev) { struct ci_hdrc *ci = dev_get_drvdata(dev); if (ci->wq) flush_workqueue(ci->wq); ci_controller_suspend(ci); return 0; } static int ci_resume(struct device *dev) { return ci_controller_resume(dev); } #endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops ci_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume) }; |
5f36e231e usb: chipidea: ad... |
838 839 |
static struct platform_driver ci_hdrc_driver = { .probe = ci_hdrc_probe, |
7690417db usb: remove use o... |
840 |
.remove = ci_hdrc_remove, |
e443b3336 usb: chipidea: sp... |
841 |
.driver = { |
5f36e231e usb: chipidea: ad... |
842 |
.name = "ci_hdrc", |
8076932ff usb: chipidea: ad... |
843 |
.pm = &ci_pm_ops, |
e443b3336 usb: chipidea: sp... |
844 845 |
}, }; |
5f36e231e usb: chipidea: ad... |
846 |
module_platform_driver(ci_hdrc_driver); |
e443b3336 usb: chipidea: sp... |
847 |
|
5f36e231e usb: chipidea: ad... |
848 |
MODULE_ALIAS("platform:ci_hdrc"); |
e443b3336 usb: chipidea: sp... |
849 850 |
MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("David Lopo <dlopo@chipidea.mips.com>"); |
5f36e231e usb: chipidea: ad... |
851 |
MODULE_DESCRIPTION("ChipIdea HDRC Driver"); |