diff options
Diffstat (limited to 'drivers/usb/gadget/pch_udc.c')
-rw-r--r-- | drivers/usb/gadget/pch_udc.c | 70 |
1 files changed, 66 insertions, 4 deletions
diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 842ca6349a90..c2f19752c074 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c | |||
@@ -311,6 +311,7 @@ struct pch_udc_ep { | |||
311 | * @registered: driver regsitered with system | 311 | * @registered: driver regsitered with system |
312 | * @suspended: driver in suspended state | 312 | * @suspended: driver in suspended state |
313 | * @connected: gadget driver associated | 313 | * @connected: gadget driver associated |
314 | * @vbus_session: required vbus_session state | ||
314 | * @set_cfg_not_acked: pending acknowledgement 4 setup | 315 | * @set_cfg_not_acked: pending acknowledgement 4 setup |
315 | * @waiting_zlp_ack: pending acknowledgement 4 ZLP | 316 | * @waiting_zlp_ack: pending acknowledgement 4 ZLP |
316 | * @data_requests: DMA pool for data requests | 317 | * @data_requests: DMA pool for data requests |
@@ -337,6 +338,7 @@ struct pch_udc_dev { | |||
337 | registered:1, | 338 | registered:1, |
338 | suspended:1, | 339 | suspended:1, |
339 | connected:1, | 340 | connected:1, |
341 | vbus_session:1, | ||
340 | set_cfg_not_acked:1, | 342 | set_cfg_not_acked:1, |
341 | waiting_zlp_ack:1; | 343 | waiting_zlp_ack:1; |
342 | struct pci_pool *data_requests; | 344 | struct pci_pool *data_requests; |
@@ -554,6 +556,31 @@ static void pch_udc_clear_disconnect(struct pch_udc_dev *dev) | |||
554 | } | 556 | } |
555 | 557 | ||
556 | /** | 558 | /** |
559 | * pch_udc_reconnect() - This API initializes usb device controller, | ||
560 | * and clear the disconnect status. | ||
561 | * @dev: Reference to pch_udc_regs structure | ||
562 | */ | ||
563 | static void pch_udc_init(struct pch_udc_dev *dev); | ||
564 | static void pch_udc_reconnect(struct pch_udc_dev *dev) | ||
565 | { | ||
566 | pch_udc_init(dev); | ||
567 | |||
568 | /* enable device interrupts */ | ||
569 | /* pch_udc_enable_interrupts() */ | ||
570 | pch_udc_bit_clr(dev, UDC_DEVIRQMSK_ADDR, | ||
571 | UDC_DEVINT_UR | UDC_DEVINT_US | | ||
572 | UDC_DEVINT_ENUM | | ||
573 | UDC_DEVINT_SI | UDC_DEVINT_SC); | ||
574 | |||
575 | /* Clear the disconnect */ | ||
576 | pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES); | ||
577 | pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_SD); | ||
578 | mdelay(1); | ||
579 | /* Resume USB signalling */ | ||
580 | pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES); | ||
581 | } | ||
582 | |||
583 | /** | ||
557 | * pch_udc_vbus_session() - set or clearr the disconnect status. | 584 | * pch_udc_vbus_session() - set or clearr the disconnect status. |
558 | * @dev: Reference to pch_udc_regs structure | 585 | * @dev: Reference to pch_udc_regs structure |
559 | * @is_active: Parameter specifying the action | 586 | * @is_active: Parameter specifying the action |
@@ -563,10 +590,18 @@ static void pch_udc_clear_disconnect(struct pch_udc_dev *dev) | |||
563 | static inline void pch_udc_vbus_session(struct pch_udc_dev *dev, | 590 | static inline void pch_udc_vbus_session(struct pch_udc_dev *dev, |
564 | int is_active) | 591 | int is_active) |
565 | { | 592 | { |
566 | if (is_active) | 593 | if (is_active) { |
567 | pch_udc_clear_disconnect(dev); | 594 | pch_udc_reconnect(dev); |
568 | else | 595 | dev->vbus_session = 1; |
596 | } else { | ||
597 | if (dev->driver && dev->driver->disconnect) { | ||
598 | spin_unlock(&dev->lock); | ||
599 | dev->driver->disconnect(&dev->gadget); | ||
600 | spin_lock(&dev->lock); | ||
601 | } | ||
569 | pch_udc_set_disconnect(dev); | 602 | pch_udc_set_disconnect(dev); |
603 | dev->vbus_session = 0; | ||
604 | } | ||
570 | } | 605 | } |
571 | 606 | ||
572 | /** | 607 | /** |
@@ -1126,7 +1161,17 @@ static int pch_udc_pcd_pullup(struct usb_gadget *gadget, int is_on) | |||
1126 | if (!gadget) | 1161 | if (!gadget) |
1127 | return -EINVAL; | 1162 | return -EINVAL; |
1128 | dev = container_of(gadget, struct pch_udc_dev, gadget); | 1163 | dev = container_of(gadget, struct pch_udc_dev, gadget); |
1129 | pch_udc_vbus_session(dev, is_on); | 1164 | if (is_on) { |
1165 | pch_udc_reconnect(dev); | ||
1166 | } else { | ||
1167 | if (dev->driver && dev->driver->disconnect) { | ||
1168 | spin_unlock(&dev->lock); | ||
1169 | dev->driver->disconnect(&dev->gadget); | ||
1170 | spin_lock(&dev->lock); | ||
1171 | } | ||
1172 | pch_udc_set_disconnect(dev); | ||
1173 | } | ||
1174 | |||
1130 | return 0; | 1175 | return 0; |
1131 | } | 1176 | } |
1132 | 1177 | ||
@@ -2482,6 +2527,15 @@ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr) | |||
2482 | dev->driver->suspend(&dev->gadget); | 2527 | dev->driver->suspend(&dev->gadget); |
2483 | spin_lock(&dev->lock); | 2528 | spin_lock(&dev->lock); |
2484 | } | 2529 | } |
2530 | |||
2531 | if (dev->vbus_session == 0) { | ||
2532 | if (dev->driver && dev->driver->disconnect) { | ||
2533 | spin_unlock(&dev->lock); | ||
2534 | dev->driver->disconnect(&dev->gadget); | ||
2535 | spin_lock(&dev->lock); | ||
2536 | } | ||
2537 | pch_udc_reconnect(dev); | ||
2538 | } | ||
2485 | dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n"); | 2539 | dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n"); |
2486 | } | 2540 | } |
2487 | /* Clear the SOF interrupt, if enabled */ | 2541 | /* Clear the SOF interrupt, if enabled */ |
@@ -2509,6 +2563,14 @@ static irqreturn_t pch_udc_isr(int irq, void *pdev) | |||
2509 | dev_intr = pch_udc_read_device_interrupts(dev); | 2563 | dev_intr = pch_udc_read_device_interrupts(dev); |
2510 | ep_intr = pch_udc_read_ep_interrupts(dev); | 2564 | ep_intr = pch_udc_read_ep_interrupts(dev); |
2511 | 2565 | ||
2566 | /* For a hot plug, this find that the controller is hung up. */ | ||
2567 | if (dev_intr == ep_intr) | ||
2568 | if (dev_intr == pch_udc_readl(dev, UDC_DEVCFG_ADDR)) { | ||
2569 | dev_dbg(&dev->pdev->dev, "UDC: Hung up\n"); | ||
2570 | /* The controller is reset */ | ||
2571 | pch_udc_writel(dev, UDC_SRST, UDC_SRST_ADDR); | ||
2572 | return IRQ_HANDLED; | ||
2573 | } | ||
2512 | if (dev_intr) | 2574 | if (dev_intr) |
2513 | /* Clear device interrupts */ | 2575 | /* Clear device interrupts */ |
2514 | pch_udc_write_device_interrupts(dev, dev_intr); | 2576 | pch_udc_write_device_interrupts(dev, dev_intr); |