aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorTomoya MORINAGA <tomoya.rohm@gmail.com>2012-01-11 21:27:08 -0500
committerFelipe Balbi <balbi@ti.com>2012-01-24 04:35:13 -0500
commit1c575d2d2e3ff2a7cb3c2e2165064199cfd8ad32 (patch)
tree77966879bb96d56153ca810995af821acd54549c /drivers/usb
parent84566abba058b2aae8d603dfa90b5a3778a6714f (diff)
usb: gadget: pch_udc: Fix usb/gadget/pch_udc: Fix ether gadget connect/disconnect issue
ISSUE: After a USB cable is connect/disconnected, the system rarely freezes. CAUSE: Since the USB device controller cannot know to disconnect the USB cable, when it is used without detecting VBUS by GPIO, the UDC driver does not notify to USB Gadget. Since USB Gadget cannot know to disconnect, a false setting occurred when the USB cable is connected/disconnect repeatedly. Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/gadget/pch_udc.c70
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 */
563static void pch_udc_init(struct pch_udc_dev *dev);
564static 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)
563static inline void pch_udc_vbus_session(struct pch_udc_dev *dev, 590static 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);