linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/3] pch_udc: Detecting VBUS through GPIO
@ 2012-02-03  7:14 Tomoya MORINAGA
  2012-02-03  7:14 ` [PATCH 2/3] pch_udc: Detecting VBUS through GPIO with interrupt Tomoya MORINAGA
  2012-02-03  7:14 ` [PATCH 3/3] pch_udc: Specifies GPI port number at configuration Tomoya MORINAGA
  0 siblings, 2 replies; 8+ messages in thread
From: Tomoya MORINAGA @ 2012-02-03  7:14 UTC (permalink / raw)
  To: Felipe Balbi, Greg Kroah-Hartman, linux-usb, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, Tomoya MORINAGA


Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
---
 drivers/usb/gadget/pch_udc.c |  145 +++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 142 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c
index 01ff48d..d77211c 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/pch_udc.c
@@ -15,6 +15,13 @@
 #include <linux/interrupt.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
+#include <linux/gpio.h>
+
+/* GPIO port for VBUS detecting */
+static int vbus_gpio_port = -1;		/* GPIO port number (-1:Not used) */
+
+#define PCH_VBUS_PERIOD		3000	/* VBUS polling period (msec) */
+#define PCH_VBUS_INTERVAL	10	/* VBUS polling interval (msec) */
 
 /* Address offset of Registers */
 #define UDC_EP_REG_SHIFT	0x20	/* Offset to next EP */
@@ -296,6 +303,17 @@ struct pch_udc_ep {
 };
 
 /**
+ * struct pch_vbus_gpio_data - Structure holding GPIO informaton
+ *					for detecting VBUS
+ * @port:		gpio port number
+ * @irq_work_fall	Structure for WorkQueue
+ */
+struct pch_vbus_gpio_data {
+	int			port;
+	struct work_struct	irq_work_fall;
+};
+
+/**
  * struct pch_udc_dev - Structure holding complete information
  *			of the PCH USB device
  * @gadget:		gadget driver data
@@ -323,6 +341,7 @@ struct pch_udc_ep {
  * @base_addr:		for mapped device memory
  * @irq:		IRQ line for the device
  * @cfg_data:		current cfg, intf, and alt in use
+ * @vbus_gpio:		GPIO informaton for detecting VBUS
  */
 struct pch_udc_dev {
 	struct usb_gadget		gadget;
@@ -349,7 +368,8 @@ struct pch_udc_dev {
 	unsigned long			phys_addr;
 	void __iomem			*base_addr;
 	unsigned			irq;
-	struct pch_udc_cfg_data	cfg_data;
+	struct pch_udc_cfg_data		cfg_data;
+	struct pch_vbus_gpio_data	vbus_gpio;
 };
 
 #define PCH_UDC_PCI_BAR			1
@@ -1226,6 +1246,115 @@ static const struct usb_gadget_ops pch_udc_ops = {
 };
 
 /**
+ * pch_vbus_gpio_get_value() - This API gets value of GPIO port as VBUS status.
+ * @dev:	Reference to the driver structure
+ *
+ * Return value:
+ *	1: VBUS is high
+ *	0: VBUS is low
+ *     -1: It is not enable to detect VBUS using GPIO
+ */
+static int pch_vbus_gpio_get_value(struct pch_udc_dev *dev)
+{
+	int vbus = 0;
+
+	if (dev->vbus_gpio.port)
+		vbus = gpio_get_value(dev->vbus_gpio.port) ? 1 : 0;
+	else
+		vbus = -1;
+
+	return vbus;
+}
+
+/**
+ * pch_vbus_gpio_work_fall() - This API keeps watch on VBUS becoming Low.
+ *                             If VBUS is Low, disconnect is processed
+ * @irq_work:	Structure for WorkQueue
+ *
+ */
+static void pch_vbus_gpio_work_fall(struct work_struct *irq_work)
+{
+	struct pch_vbus_gpio_data *vbus_gpio = container_of(irq_work,
+		struct pch_vbus_gpio_data, irq_work_fall);
+	struct pch_udc_dev *dev =
+		container_of(vbus_gpio, struct pch_udc_dev, vbus_gpio);
+	int vbus_saved = -1;
+	int vbus;
+	int count;
+
+	if (!dev->vbus_gpio.port)
+		return;
+
+	for (count = 0; count < (PCH_VBUS_PERIOD / PCH_VBUS_INTERVAL);
+		count++) {
+		vbus = pch_vbus_gpio_get_value(dev);
+
+		if ((vbus_saved == vbus) && (vbus == 0)) {
+			dev_dbg(&dev->pdev->dev, "VBUS fell");
+			if (dev->driver
+				&& dev->driver->disconnect) {
+				dev->driver->disconnect(
+					&dev->gadget);
+			}
+			pch_udc_reconnect(dev);
+			dev_dbg(&dev->pdev->dev, "VBUS fell");
+			return;
+		}
+		vbus_saved = vbus;
+		mdelay(PCH_VBUS_INTERVAL);
+	}
+}
+
+/**
+ * pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS.
+ * @dev:	Reference to the driver structure
+ * @vbus_gpio	Number of GPIO port to detect gpio
+ *
+ * Return codes:
+ *	0: Success
+ *	-EINVAL: GPIO port is invalid or can't be initialized.
+ */
+static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port)
+{
+	int err;
+
+	dev->vbus_gpio.port = 0;
+
+	if (vbus_gpio_port <= -1)
+		return -EINVAL;
+
+	err = gpio_is_valid(vbus_gpio_port);
+	if (!err) {
+		pr_err("%s: gpio port %d is invalid\n",
+			__func__, vbus_gpio_port);
+		return -EINVAL;
+	}
+
+	err = gpio_request(vbus_gpio_port, "pch_vbus");
+	if (err) {
+		pr_err("%s: can't request gpio port %d, err: %d\n",
+			__func__, vbus_gpio_port, err);
+		return -EINVAL;
+	}
+
+	dev->vbus_gpio.port = vbus_gpio_port;
+	gpio_direction_input(vbus_gpio_port);
+	INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall);
+
+	return 0;
+}
+
+/**
+ * pch_vbus_gpio_free() - This API frees resources of GPIO port
+ * @dev:	Reference to the driver structure
+ */
+static void pch_vbus_gpio_free(struct pch_udc_dev *dev)
+{
+	if (dev->vbus_gpio.port)
+		gpio_free(dev->vbus_gpio.port);
+}
+
+/**
  * complete_req() - This API is invoked from the driver when processing
  *			of a request is complete
  * @ep:		Reference to the endpoint structure
@@ -2510,6 +2639,8 @@ static void pch_udc_svc_cfg_interrupt(struct pch_udc_dev *dev)
  */
 static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr)
 {
+	int vbus;
+
 	/* USB Reset Interrupt */
 	if (dev_intr & UDC_DEVINT_UR) {
 		pch_udc_svc_ur_interrupt(dev);
@@ -2535,14 +2666,19 @@ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr)
 			spin_lock(&dev->lock);
 		}
 
-		if (dev->vbus_session == 0) {
+		vbus = pch_vbus_gpio_get_value(dev);
+		if ((dev->vbus_session == 0)
+			&& (vbus != 1)) {
 			if (dev->driver && dev->driver->disconnect) {
 				spin_unlock(&dev->lock);
 				dev->driver->disconnect(&dev->gadget);
 				spin_lock(&dev->lock);
 			}
 			pch_udc_reconnect(dev);
-		}
+		} else if ((dev->vbus_session == 0)
+			&& (vbus == 1))
+			schedule_work(&dev->vbus_gpio.irq_work_fall);
+
 		dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n");
 	}
 	/* Clear the SOF interrupt, if enabled */
@@ -2704,6 +2840,7 @@ static int pch_udc_pcd_init(struct pch_udc_dev *dev)
 {
 	pch_udc_init(dev);
 	pch_udc_pcd_reinit(dev);
+	pch_vbus_gpio_init(dev, vbus_gpio_port);
 	return 0;
 }
 
@@ -2882,6 +3019,8 @@ static void pch_udc_remove(struct pci_dev *pdev)
 				 UDC_EP0OUT_BUFF_SIZE * 4, DMA_FROM_DEVICE);
 	kfree(dev->ep0out_buf);
 
+	pch_vbus_gpio_free(dev);
+
 	pch_udc_exit(dev);
 
 	if (dev->irq_registered)
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/3] pch_udc: Detecting VBUS through GPIO with interrupt
  2012-02-03  7:14 [PATCH 1/3] pch_udc: Detecting VBUS through GPIO Tomoya MORINAGA
@ 2012-02-03  7:14 ` Tomoya MORINAGA
  2012-02-09  7:58   ` Felipe Balbi
  2012-02-09  7:59   ` Felipe Balbi
  2012-02-03  7:14 ` [PATCH 3/3] pch_udc: Specifies GPI port number at configuration Tomoya MORINAGA
  1 sibling, 2 replies; 8+ messages in thread
From: Tomoya MORINAGA @ 2012-02-03  7:14 UTC (permalink / raw)
  To: Felipe Balbi, Greg Kroah-Hartman, linux-usb, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, Tomoya MORINAGA

Problem:
 pch_udc continues operation even if VBUS becomes Low.
 pch_udc performs D+ pulling up before VBUS becomes High.
 USB device should be controlled according to VBUS state.

Root cause:
 The current pch_udc is not always monitoring VBUS.

Solution:
 The change of VBUS is detected using an interrupt of GPIO.
 If VBUS became Low, pch_udc handles 'disconnect'.
 After VBUS became High, a pull improves D+, and pch_udc
 handles 'connect'.

Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
---
 drivers/usb/gadget/pch_udc.c |   85 ++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 82 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c
index d77211c..942fe92 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/pch_udc.c
@@ -306,11 +306,15 @@ struct pch_udc_ep {
  * struct pch_vbus_gpio_data - Structure holding GPIO informaton
  *					for detecting VBUS
  * @port:		gpio port number
+ * @intr:		gpio interrupt number
  * @irq_work_fall	Structure for WorkQueue
+ * @irq_work_rise	Structure for WorkQueue
  */
 struct pch_vbus_gpio_data {
 	int			port;
+	int			intr;
 	struct work_struct	irq_work_fall;
+	struct work_struct	irq_work_rise;
 };
 
 /**
@@ -1296,8 +1300,10 @@ static void pch_vbus_gpio_work_fall(struct work_struct *irq_work)
 				dev->driver->disconnect(
 					&dev->gadget);
 			}
-			pch_udc_reconnect(dev);
-			dev_dbg(&dev->pdev->dev, "VBUS fell");
+			if (dev->vbus_gpio.intr)
+				pch_udc_init(dev);
+			else
+				pch_udc_reconnect(dev);
 			return;
 		}
 		vbus_saved = vbus;
@@ -1306,6 +1312,57 @@ static void pch_vbus_gpio_work_fall(struct work_struct *irq_work)
 }
 
 /**
+ * pch_vbus_gpio_work_rise() - This API checks VBUS is High.
+ *                             If VBUS is High, connect is processed
+ * @irq_work:	Structure for WorkQueue
+ *
+ */
+static void pch_vbus_gpio_work_rise(struct work_struct *irq_work)
+{
+	struct pch_vbus_gpio_data *vbus_gpio = container_of(irq_work,
+		struct pch_vbus_gpio_data, irq_work_rise);
+	struct pch_udc_dev *dev =
+		container_of(vbus_gpio, struct pch_udc_dev, vbus_gpio);
+	int vbus;
+
+	if (!dev->vbus_gpio.port)
+		return;
+
+	mdelay(PCH_VBUS_INTERVAL);
+	vbus = pch_vbus_gpio_get_value(dev);
+
+	if (vbus == 1) {
+		dev_dbg(&dev->pdev->dev, "VBUS rose");
+		pch_udc_reconnect(dev);
+		return;
+	}
+}
+
+/**
+ * pch_vbus_gpio_irq() - IRQ handler for GPIO intrerrupt for changing VBUS
+ * @irq:	Interrupt request number
+ * @dev:	Reference to the device structure
+ *
+ * Return codes:
+ *	0: Success
+ *	-EINVAL: GPIO port is invalid or can't be initialized.
+ */
+static irqreturn_t pch_vbus_gpio_irq(int irq, void *data)
+{
+	struct pch_udc_dev *dev = (struct pch_udc_dev *)data;
+
+	if (!dev->vbus_gpio.port || !dev->vbus_gpio.intr)
+		return IRQ_NONE;
+
+	if (pch_vbus_gpio_get_value(dev))
+		schedule_work(&dev->vbus_gpio.irq_work_rise);
+	else
+		schedule_work(&dev->vbus_gpio.irq_work_fall);
+
+	return IRQ_HANDLED;
+}
+
+/**
  * pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS.
  * @dev:	Reference to the driver structure
  * @vbus_gpio	Number of GPIO port to detect gpio
@@ -1317,8 +1374,10 @@ static void pch_vbus_gpio_work_fall(struct work_struct *irq_work)
 static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port)
 {
 	int err;
+	int irq_num = 0;
 
 	dev->vbus_gpio.port = 0;
+	dev->vbus_gpio.intr = 0;
 
 	if (vbus_gpio_port <= -1)
 		return -EINVAL;
@@ -1341,6 +1400,21 @@ static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port)
 	gpio_direction_input(vbus_gpio_port);
 	INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall);
 
+	irq_num = gpio_to_irq(vbus_gpio_port);
+	if (irq_num > 0) {
+		irq_set_irq_type(irq_num, IRQ_TYPE_EDGE_BOTH);
+		err = request_irq(irq_num, pch_vbus_gpio_irq, 0,
+			"vbus_detect", dev);
+		if (!err) {
+			dev->vbus_gpio.intr = irq_num;
+			INIT_WORK(&dev->vbus_gpio.irq_work_rise,
+				pch_vbus_gpio_work_rise);
+		} else {
+			pr_err("%s: can't request irq %d, err: %d\n",
+				__func__, irq_num, err);
+		}
+	}
+
 	return 0;
 }
 
@@ -1350,6 +1424,9 @@ static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port)
  */
 static void pch_vbus_gpio_free(struct pch_udc_dev *dev)
 {
+	if (dev->vbus_gpio.intr)
+		free_irq(dev->vbus_gpio.intr, dev);
+
 	if (dev->vbus_gpio.port)
 		gpio_free(dev->vbus_gpio.port);
 }
@@ -2677,6 +2754,7 @@ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr)
 			pch_udc_reconnect(dev);
 		} else if ((dev->vbus_session == 0)
 			&& (vbus == 1))
+			&&!dev->vbus_gpio.intr)
 			schedule_work(&dev->vbus_gpio.irq_work_fall);
 
 		dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n");
@@ -2941,7 +3019,8 @@ static int pch_udc_start(struct usb_gadget_driver *driver,
 	pch_udc_setup_ep0(dev);
 
 	/* clear SD */
-	pch_udc_clear_disconnect(dev);
+	if ((pch_vbus_gpio_get_value(dev) != 0) || !dev->vbus_gpio.intr)
+		pch_udc_clear_disconnect(dev);
 
 	dev->connected = 1;
 	return 0;
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/3] pch_udc: Specifies GPI port number at configuration.
  2012-02-03  7:14 [PATCH 1/3] pch_udc: Detecting VBUS through GPIO Tomoya MORINAGA
  2012-02-03  7:14 ` [PATCH 2/3] pch_udc: Detecting VBUS through GPIO with interrupt Tomoya MORINAGA
@ 2012-02-03  7:14 ` Tomoya MORINAGA
  2012-02-03 10:50   ` Sergei Shtylyov
  1 sibling, 1 reply; 8+ messages in thread
From: Tomoya MORINAGA @ 2012-02-03  7:14 UTC (permalink / raw)
  To: Felipe Balbi, Greg Kroah-Hartman, linux-usb, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, Tomoya MORINAGA

Problem:
 The GPIO ports used for detecting VBUS may be different
 on each platform.
 This should be easily modifiable.

Solution:
 In configuration, GPI port number is specified.

Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
---
 drivers/usb/gadget/Kconfig   |   12 ++++++++++++
 drivers/usb/gadget/pch_udc.c |    2 +-
 2 files changed, 13 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 23a4473..cd0ec15 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -491,6 +491,18 @@ config USB_EG20T
 	  ML7213/ML7831 is companion chip for Intel Atom E6xx series.
 	  ML7213/ML7831 is completely compatible for Intel EG20T PCH.
 
+config USB_VBUS_GPIO_PORT
+	int "GPIO pin used for detecting VBUS"
+	depends on USB_EG20T
+	default "-1"
+	help
+	  Enter the GPIO port number used for detecting VBUS state.
+	  If not used, specify -1(default).
+
+	  If not used, this driver does not support USB suspend state.
+	  If the GPIO port number that VBUS is not connected to is specified,
+	  the USB device may not work.
+
 config USB_CI13XXX_MSM
 	tristate "MIPS USB CI13xxx for MSM"
 	depends on ARCH_MSM
diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c
index 942fe92..44a86fc 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/pch_udc.c
@@ -18,7 +18,7 @@
 #include <linux/gpio.h>
 
 /* GPIO port for VBUS detecting */
-static int vbus_gpio_port = -1;		/* GPIO port number (-1:Not used) */
+static int vbus_gpio_port = CONFIG_USB_VBUS_GPIO_PORT;
 
 #define PCH_VBUS_PERIOD		3000	/* VBUS polling period (msec) */
 #define PCH_VBUS_INTERVAL	10	/* VBUS polling interval (msec) */
-- 
1.7.7.6


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/3] pch_udc: Specifies GPI port number at configuration.
  2012-02-03  7:14 ` [PATCH 3/3] pch_udc: Specifies GPI port number at configuration Tomoya MORINAGA
@ 2012-02-03 10:50   ` Sergei Shtylyov
  2012-02-09  7:56     ` Felipe Balbi
  2012-02-21  0:27     ` Tomoya MORINAGA
  0 siblings, 2 replies; 8+ messages in thread
From: Sergei Shtylyov @ 2012-02-03 10:50 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Felipe Balbi, Greg Kroah-Hartman, linux-usb, linux-kernel,
	qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

Hello.

On 03-02-2012 11:14, Tomoya MORINAGA wrote:

> Problem:
>   The GPIO ports used for detecting VBUS may be different
>   on each platform.
>   This should be easily modifiable.
> Solution:
>   In configuration, GPI port number is specified.

    Isn't it better to pass this with driver's platform data.

> Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>

WBR, Sergei

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/3] pch_udc: Specifies GPI port number at configuration.
  2012-02-03 10:50   ` Sergei Shtylyov
@ 2012-02-09  7:56     ` Felipe Balbi
  2012-02-21  0:27     ` Tomoya MORINAGA
  1 sibling, 0 replies; 8+ messages in thread
From: Felipe Balbi @ 2012-02-09  7:56 UTC (permalink / raw)
  To: Sergei Shtylyov
  Cc: Tomoya MORINAGA, Felipe Balbi, Greg Kroah-Hartman, linux-usb,
	linux-kernel, qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 500 bytes --]

On Fri, Feb 03, 2012 at 02:50:02PM +0400, Sergei Shtylyov wrote:
> Hello.
> 
> On 03-02-2012 11:14, Tomoya MORINAGA wrote:
> 
> >Problem:
> >  The GPIO ports used for detecting VBUS may be different
> >  on each platform.
> >  This should be easily modifiable.
> >Solution:
> >  In configuration, GPI port number is specified.
> 
>    Isn't it better to pass this with driver's platform data.

Yes, please re-factor this port. This doesn't fit as a compile-time
choice.

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/3] pch_udc: Detecting VBUS through GPIO with interrupt
  2012-02-03  7:14 ` [PATCH 2/3] pch_udc: Detecting VBUS through GPIO with interrupt Tomoya MORINAGA
@ 2012-02-09  7:58   ` Felipe Balbi
  2012-02-09  7:59   ` Felipe Balbi
  1 sibling, 0 replies; 8+ messages in thread
From: Felipe Balbi @ 2012-02-09  7:58 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Felipe Balbi, Greg Kroah-Hartman, linux-usb, linux-kernel,
	qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 5811 bytes --]

On Fri, Feb 03, 2012 at 04:14:18PM +0900, Tomoya MORINAGA wrote:
> Problem:
>  pch_udc continues operation even if VBUS becomes Low.
>  pch_udc performs D+ pulling up before VBUS becomes High.
>  USB device should be controlled according to VBUS state.
> 
> Root cause:
>  The current pch_udc is not always monitoring VBUS.
> 
> Solution:
>  The change of VBUS is detected using an interrupt of GPIO.
>  If VBUS became Low, pch_udc handles 'disconnect'.
>  After VBUS became High, a pull improves D+, and pch_udc
>  handles 'connect'.
> 
> Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>
> ---
>  drivers/usb/gadget/pch_udc.c |   85 ++++++++++++++++++++++++++++++++++++++++-
>  1 files changed, 82 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c
> index d77211c..942fe92 100644
> --- a/drivers/usb/gadget/pch_udc.c
> +++ b/drivers/usb/gadget/pch_udc.c
> @@ -306,11 +306,15 @@ struct pch_udc_ep {
>   * struct pch_vbus_gpio_data - Structure holding GPIO informaton
>   *					for detecting VBUS
>   * @port:		gpio port number
> + * @intr:		gpio interrupt number
>   * @irq_work_fall	Structure for WorkQueue
> + * @irq_work_rise	Structure for WorkQueue
>   */
>  struct pch_vbus_gpio_data {
>  	int			port;
> +	int			intr;
>  	struct work_struct	irq_work_fall;
> +	struct work_struct	irq_work_rise;
>  };
>  
>  /**
> @@ -1296,8 +1300,10 @@ static void pch_vbus_gpio_work_fall(struct work_struct *irq_work)
>  				dev->driver->disconnect(
>  					&dev->gadget);
>  			}
> -			pch_udc_reconnect(dev);
> -			dev_dbg(&dev->pdev->dev, "VBUS fell");
> +			if (dev->vbus_gpio.intr)
> +				pch_udc_init(dev);
> +			else
> +				pch_udc_reconnect(dev);
>  			return;
>  		}
>  		vbus_saved = vbus;
> @@ -1306,6 +1312,57 @@ static void pch_vbus_gpio_work_fall(struct work_struct *irq_work)
>  }
>  
>  /**
> + * pch_vbus_gpio_work_rise() - This API checks VBUS is High.
> + *                             If VBUS is High, connect is processed
> + * @irq_work:	Structure for WorkQueue
> + *
> + */
> +static void pch_vbus_gpio_work_rise(struct work_struct *irq_work)
> +{
> +	struct pch_vbus_gpio_data *vbus_gpio = container_of(irq_work,
> +		struct pch_vbus_gpio_data, irq_work_rise);
> +	struct pch_udc_dev *dev =
> +		container_of(vbus_gpio, struct pch_udc_dev, vbus_gpio);
> +	int vbus;
> +
> +	if (!dev->vbus_gpio.port)
> +		return;
> +
> +	mdelay(PCH_VBUS_INTERVAL);
> +	vbus = pch_vbus_gpio_get_value(dev);
> +
> +	if (vbus == 1) {
> +		dev_dbg(&dev->pdev->dev, "VBUS rose");
> +		pch_udc_reconnect(dev);
> +		return;
> +	}
> +}
> +
> +/**
> + * pch_vbus_gpio_irq() - IRQ handler for GPIO intrerrupt for changing VBUS
> + * @irq:	Interrupt request number
> + * @dev:	Reference to the device structure
> + *
> + * Return codes:
> + *	0: Success
> + *	-EINVAL: GPIO port is invalid or can't be initialized.
> + */
> +static irqreturn_t pch_vbus_gpio_irq(int irq, void *data)
> +{
> +	struct pch_udc_dev *dev = (struct pch_udc_dev *)data;
> +
> +	if (!dev->vbus_gpio.port || !dev->vbus_gpio.intr)
> +		return IRQ_NONE;
> +
> +	if (pch_vbus_gpio_get_value(dev))
> +		schedule_work(&dev->vbus_gpio.irq_work_rise);
> +	else
> +		schedule_work(&dev->vbus_gpio.irq_work_fall);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/**
>   * pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS.
>   * @dev:	Reference to the driver structure
>   * @vbus_gpio	Number of GPIO port to detect gpio
> @@ -1317,8 +1374,10 @@ static void pch_vbus_gpio_work_fall(struct work_struct *irq_work)
>  static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port)
>  {
>  	int err;
> +	int irq_num = 0;
>  
>  	dev->vbus_gpio.port = 0;
> +	dev->vbus_gpio.intr = 0;
>  
>  	if (vbus_gpio_port <= -1)
>  		return -EINVAL;
> @@ -1341,6 +1400,21 @@ static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port)
>  	gpio_direction_input(vbus_gpio_port);
>  	INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall);
>  
> +	irq_num = gpio_to_irq(vbus_gpio_port);
> +	if (irq_num > 0) {
> +		irq_set_irq_type(irq_num, IRQ_TYPE_EDGE_BOTH);
> +		err = request_irq(irq_num, pch_vbus_gpio_irq, 0,
> +			"vbus_detect", dev);
> +		if (!err) {
> +			dev->vbus_gpio.intr = irq_num;
> +			INIT_WORK(&dev->vbus_gpio.irq_work_rise,
> +				pch_vbus_gpio_work_rise);
> +		} else {
> +			pr_err("%s: can't request irq %d, err: %d\n",
> +				__func__, irq_num, err);
> +		}
> +	}
> +
>  	return 0;
>  }
>  
> @@ -1350,6 +1424,9 @@ static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port)
>   */
>  static void pch_vbus_gpio_free(struct pch_udc_dev *dev)
>  {
> +	if (dev->vbus_gpio.intr)
> +		free_irq(dev->vbus_gpio.intr, dev);
> +
>  	if (dev->vbus_gpio.port)
>  		gpio_free(dev->vbus_gpio.port);
>  }
> @@ -2677,6 +2754,7 @@ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr)
>  			pch_udc_reconnect(dev);
>  		} else if ((dev->vbus_session == 0)
>  			&& (vbus == 1))
> +			&&!dev->vbus_gpio.intr)

This causes a compile error which I have fixed this time. Next time I
will silently drop the patch:

diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c
index fb74cc8..a992084 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/pch_udc.c
@@ -2753,8 +2753,8 @@ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr)
 			}
 			pch_udc_reconnect(dev);
 		} else if ((dev->vbus_session == 0)
-			&& (vbus == 1))
-			&&!dev->vbus_gpio.intr)
+			&& (vbus == 1)
+			&& !dev->vbus_gpio.intr)
 			schedule_work(&dev->vbus_gpio.irq_work_fall);
 
 		dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n");

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/3] pch_udc: Detecting VBUS through GPIO with interrupt
  2012-02-03  7:14 ` [PATCH 2/3] pch_udc: Detecting VBUS through GPIO with interrupt Tomoya MORINAGA
  2012-02-09  7:58   ` Felipe Balbi
@ 2012-02-09  7:59   ` Felipe Balbi
  1 sibling, 0 replies; 8+ messages in thread
From: Felipe Balbi @ 2012-02-09  7:59 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: Felipe Balbi, Greg Kroah-Hartman, linux-usb, linux-kernel,
	qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 661 bytes --]

On Fri, Feb 03, 2012 at 04:14:18PM +0900, Tomoya MORINAGA wrote:
> Problem:
>  pch_udc continues operation even if VBUS becomes Low.
>  pch_udc performs D+ pulling up before VBUS becomes High.
>  USB device should be controlled according to VBUS state.
> 
> Root cause:
>  The current pch_udc is not always monitoring VBUS.
> 
> Solution:
>  The change of VBUS is detected using an interrupt of GPIO.
>  If VBUS became Low, pch_udc handles 'disconnect'.
>  After VBUS became High, a pull improves D+, and pch_udc
>  handles 'connect'.
> 
> Signed-off-by: Tomoya MORINAGA <tomoya.rohm@gmail.com>

applied my fixed version
thanks

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/3] pch_udc: Specifies GPI port number at configuration.
  2012-02-03 10:50   ` Sergei Shtylyov
  2012-02-09  7:56     ` Felipe Balbi
@ 2012-02-21  0:27     ` Tomoya MORINAGA
  1 sibling, 0 replies; 8+ messages in thread
From: Tomoya MORINAGA @ 2012-02-21  0:27 UTC (permalink / raw)
  To: Sergei Shtylyov
  Cc: Felipe Balbi, Greg Kroah-Hartman, linux-usb, linux-kernel,
	qi.wang, yong.y.wang, joel.clark, kok.howg.ewe

[-- Attachment #1: Type: text/plain, Size: 472 bytes --]

2012年2月3日19:50 Sergei Shtylyov <sshtylyov@mvista.com>:
>   Isn't it better to pass this with driver's platform data.

To define GPIO port number, we must I'm supporting IntelCrown Bay
platform (Intel AtomE600 + PCH EG20T).
I made an initialization code(attached) for the Crown Bay platform.
Should I post this module to upstream? I need your advice.
If I should do, could you tell me which category we should put
it?

---
ROHM Co., Ltd.
tomoya

[-- Attachment #2: crownbay.c --]
[-- Type: text/x-csrc, Size: 1267 bytes --]

/*
 * Copyright (C) 2012 LAPIS Semiconductor Co., Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/usb/gpio_vbus.h>

#define PCH_GPIO_BASE		244

/* GPIO for USB VBUS detection */
#define CROWNBAY_GPIO_VBUS_PORT	5

static struct gpio_vbus_mach_info crownbay_gpio_vbus_data = {
	.gpio_vbus	= (PCH_GPIO_BASE + CROWNBAY_GPIO_VBUS_PORT),
};

static struct platform_device crownbay_gpio_vbus_device = {
	.name		= "pch-gpio-vbus",
	.id		= -1,
	.dev		= {
		.platform_data	= &crownbay_gpio_vbus_data,
	},
};

static int __init crownbay_platform_init(void)
{
	int retval;
	retval = platform_device_register(&crownbay_gpio_vbus_device);
	printk(KERN_INFO "Registered gpio-vbus device for Crown Bay\n");
	return retval;
}
module_init(crownbay_platform_init);

static void __exit crownbay_platform_exit(void)
{
	platform_device_unregister(&crownbay_gpio_vbus_device);
}
module_exit(crownbay_platform_exit);

MODULE_DESCRIPTION("Intel Crown Bay Platform Driver");
MODULE_LICENSE("GPL");

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2012-02-21  0:28 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-02-03  7:14 [PATCH 1/3] pch_udc: Detecting VBUS through GPIO Tomoya MORINAGA
2012-02-03  7:14 ` [PATCH 2/3] pch_udc: Detecting VBUS through GPIO with interrupt Tomoya MORINAGA
2012-02-09  7:58   ` Felipe Balbi
2012-02-09  7:59   ` Felipe Balbi
2012-02-03  7:14 ` [PATCH 3/3] pch_udc: Specifies GPI port number at configuration Tomoya MORINAGA
2012-02-03 10:50   ` Sergei Shtylyov
2012-02-09  7:56     ` Felipe Balbi
2012-02-21  0:27     ` Tomoya MORINAGA

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).