|
@@ -311,6 +311,7 @@ struct pch_udc_ep {
|
|
|
* @registered: driver regsitered with system
|
|
|
* @suspended: driver in suspended state
|
|
|
* @connected: gadget driver associated
|
|
|
+ * @vbus_session: required vbus_session state
|
|
|
* @set_cfg_not_acked: pending acknowledgement 4 setup
|
|
|
* @waiting_zlp_ack: pending acknowledgement 4 ZLP
|
|
|
* @data_requests: DMA pool for data requests
|
|
@@ -337,6 +338,7 @@ struct pch_udc_dev {
|
|
|
registered:1,
|
|
|
suspended:1,
|
|
|
connected:1,
|
|
|
+ vbus_session:1,
|
|
|
set_cfg_not_acked:1,
|
|
|
waiting_zlp_ack:1;
|
|
|
struct pci_pool *data_requests;
|
|
@@ -553,6 +555,31 @@ static void pch_udc_clear_disconnect(struct pch_udc_dev *dev)
|
|
|
pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * pch_udc_reconnect() - This API initializes usb device controller,
|
|
|
+ * and clear the disconnect status.
|
|
|
+ * @dev: Reference to pch_udc_regs structure
|
|
|
+ */
|
|
|
+static void pch_udc_init(struct pch_udc_dev *dev);
|
|
|
+static void pch_udc_reconnect(struct pch_udc_dev *dev)
|
|
|
+{
|
|
|
+ pch_udc_init(dev);
|
|
|
+
|
|
|
+ /* enable device interrupts */
|
|
|
+ /* pch_udc_enable_interrupts() */
|
|
|
+ pch_udc_bit_clr(dev, UDC_DEVIRQMSK_ADDR,
|
|
|
+ UDC_DEVINT_UR | UDC_DEVINT_US |
|
|
|
+ UDC_DEVINT_ENUM |
|
|
|
+ UDC_DEVINT_SI | UDC_DEVINT_SC);
|
|
|
+
|
|
|
+ /* Clear the disconnect */
|
|
|
+ pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
|
|
|
+ pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_SD);
|
|
|
+ mdelay(1);
|
|
|
+ /* Resume USB signalling */
|
|
|
+ pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* pch_udc_vbus_session() - set or clearr the disconnect status.
|
|
|
* @dev: Reference to pch_udc_regs structure
|
|
@@ -563,10 +590,18 @@ static void pch_udc_clear_disconnect(struct pch_udc_dev *dev)
|
|
|
static inline void pch_udc_vbus_session(struct pch_udc_dev *dev,
|
|
|
int is_active)
|
|
|
{
|
|
|
- if (is_active)
|
|
|
- pch_udc_clear_disconnect(dev);
|
|
|
- else
|
|
|
+ if (is_active) {
|
|
|
+ pch_udc_reconnect(dev);
|
|
|
+ dev->vbus_session = 1;
|
|
|
+ } else {
|
|
|
+ if (dev->driver && dev->driver->disconnect) {
|
|
|
+ spin_unlock(&dev->lock);
|
|
|
+ dev->driver->disconnect(&dev->gadget);
|
|
|
+ spin_lock(&dev->lock);
|
|
|
+ }
|
|
|
pch_udc_set_disconnect(dev);
|
|
|
+ dev->vbus_session = 0;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1126,7 +1161,17 @@ static int pch_udc_pcd_pullup(struct usb_gadget *gadget, int is_on)
|
|
|
if (!gadget)
|
|
|
return -EINVAL;
|
|
|
dev = container_of(gadget, struct pch_udc_dev, gadget);
|
|
|
- pch_udc_vbus_session(dev, is_on);
|
|
|
+ if (is_on) {
|
|
|
+ pch_udc_reconnect(dev);
|
|
|
+ } else {
|
|
|
+ if (dev->driver && dev->driver->disconnect) {
|
|
|
+ spin_unlock(&dev->lock);
|
|
|
+ dev->driver->disconnect(&dev->gadget);
|
|
|
+ spin_lock(&dev->lock);
|
|
|
+ }
|
|
|
+ pch_udc_set_disconnect(dev);
|
|
|
+ }
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -2482,6 +2527,15 @@ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr)
|
|
|
dev->driver->suspend(&dev->gadget);
|
|
|
spin_lock(&dev->lock);
|
|
|
}
|
|
|
+
|
|
|
+ if (dev->vbus_session == 0) {
|
|
|
+ if (dev->driver && dev->driver->disconnect) {
|
|
|
+ spin_unlock(&dev->lock);
|
|
|
+ dev->driver->disconnect(&dev->gadget);
|
|
|
+ spin_lock(&dev->lock);
|
|
|
+ }
|
|
|
+ pch_udc_reconnect(dev);
|
|
|
+ }
|
|
|
dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n");
|
|
|
}
|
|
|
/* Clear the SOF interrupt, if enabled */
|
|
@@ -2509,6 +2563,14 @@ static irqreturn_t pch_udc_isr(int irq, void *pdev)
|
|
|
dev_intr = pch_udc_read_device_interrupts(dev);
|
|
|
ep_intr = pch_udc_read_ep_interrupts(dev);
|
|
|
|
|
|
+ /* For a hot plug, this find that the controller is hung up. */
|
|
|
+ if (dev_intr == ep_intr)
|
|
|
+ if (dev_intr == pch_udc_readl(dev, UDC_DEVCFG_ADDR)) {
|
|
|
+ dev_dbg(&dev->pdev->dev, "UDC: Hung up\n");
|
|
|
+ /* The controller is reset */
|
|
|
+ pch_udc_writel(dev, UDC_SRST, UDC_SRST_ADDR);
|
|
|
+ return IRQ_HANDLED;
|
|
|
+ }
|
|
|
if (dev_intr)
|
|
|
/* Clear device interrupts */
|
|
|
pch_udc_write_device_interrupts(dev, dev_intr);
|