linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/5] musb-ux500 and ab8500-usb updates
@ 2013-02-28 10:38 Fabio Baltieri
  2013-02-28 10:38 ` [PATCH 1/5] usb: musb: ux500_dma: add missing MEM resource check Fabio Baltieri
                   ` (4 more replies)
  0 siblings, 5 replies; 13+ messages in thread
From: Fabio Baltieri @ 2013-02-28 10:38 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Linus Walleij, linux-kernel, linux-usb, Fabio Baltieri

Hello Felipe,

This series is an initial effort to update current UX500 specific
MUSB/OTG drivers with the various improvements and fixes developed
internally by ST-Ericsson since the driver was initially merged.

The driver currently deployed by STE includes many updates such as
runtime PM support, new AB85xx variants, integration with modern
frameworks (common clock, pin control, regulator, devm_ variants
etc...), and of course some fixes as well.

In this initial set I only included some essential patches so that I get
an early feedback from you if I'm moving in the right direction.

Thanks,
Fabio

Fabio Baltieri (4):
  usb: musb: ux500: implement musb_set_vbus
  usb: musb: ux500: add otg notifier support
  usb: otg: ab8500-usb: drop support for ab8500 pre v2.0
  usb: otg: ab8500-usb: update irq handling code

Virupax Sadashivpetimath (1):
  usb: musb: ux500_dma: add missing MEM resource check

 drivers/usb/musb/ux500.c       | 106 ++++++++
 drivers/usb/musb/ux500_dma.c   |  12 +-
 drivers/usb/otg/ab8500-usb.c   | 531 ++++++++++++++++++++++++++---------------
 include/linux/usb/musb-ux500.h |  31 +++
 4 files changed, 482 insertions(+), 198 deletions(-)
 create mode 100644 include/linux/usb/musb-ux500.h

-- 
1.8.1.3


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

* [PATCH 1/5] usb: musb: ux500_dma: add missing MEM resource check
  2013-02-28 10:38 [PATCH 0/5] musb-ux500 and ab8500-usb updates Fabio Baltieri
@ 2013-02-28 10:38 ` Fabio Baltieri
  2013-02-28 10:38 ` [PATCH 2/5] usb: musb: ux500: implement musb_set_vbus Fabio Baltieri
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 13+ messages in thread
From: Fabio Baltieri @ 2013-02-28 10:38 UTC (permalink / raw)
  To: Felipe Balbi
  Cc: Linus Walleij, linux-kernel, linux-usb, Virupax Sadashivpetimath,
	Fabio Baltieri

From: Virupax Sadashivpetimath <virupax.sadashivpetimath@stericsson.com>

Fix dma_controller_create() fail path in case memory resource is
missing.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Virupax Sadashivpetimath <virupax.sadashivpetimath@stericsson.com>
Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
---
 drivers/usb/musb/ux500_dma.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c
index 360c99c..6e3db71 100644
--- a/drivers/usb/musb/ux500_dma.c
+++ b/drivers/usb/musb/ux500_dma.c
@@ -372,12 +372,17 @@ struct dma_controller *dma_controller_create(struct musb *musb,
 
 	controller = kzalloc(sizeof(*controller), GFP_KERNEL);
 	if (!controller)
-		return NULL;
+		goto kzalloc_fail;
 
 	controller->private_data = musb;
 
 	/* Save physical address for DMA controller. */
 	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!iomem) {
+		dev_err(musb->controller, "no memory resource defined\n");
+		goto plat_get_fail;
+	}
+
 	controller->phy_base = (dma_addr_t) iomem->start;
 
 	controller->controller.start = ux500_dma_controller_start;
@@ -389,4 +394,9 @@ struct dma_controller *dma_controller_create(struct musb *musb,
 	controller->controller.is_compatible = ux500_dma_is_compatible;
 
 	return &controller->controller;
+
+plat_get_fail:
+	kfree(controller);
+kzalloc_fail:
+	return NULL;
 }
-- 
1.8.1.3


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

* [PATCH 2/5] usb: musb: ux500: implement musb_set_vbus
  2013-02-28 10:38 [PATCH 0/5] musb-ux500 and ab8500-usb updates Fabio Baltieri
  2013-02-28 10:38 ` [PATCH 1/5] usb: musb: ux500_dma: add missing MEM resource check Fabio Baltieri
@ 2013-02-28 10:38 ` Fabio Baltieri
  2013-02-28 10:38 ` [PATCH 3/5] usb: musb: ux500: add otg notifier support Fabio Baltieri
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 13+ messages in thread
From: Fabio Baltieri @ 2013-02-28 10:38 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Linus Walleij, linux-kernel, linux-usb, Fabio Baltieri

Add ux500_musb_set_vbus() implementation for ux500.

This is based on the version originally developed inside ST-Ericsson.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
---
 drivers/usb/musb/ux500.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
index 13a3929..5b742ba 100644
--- a/drivers/usb/musb/ux500.c
+++ b/drivers/usb/musb/ux500.c
@@ -36,6 +36,68 @@ struct ux500_glue {
 };
 #define glue_to_musb(g)	platform_get_drvdata(g->musb)
 
+static void ux500_musb_set_vbus(struct musb *musb, int is_on)
+{
+	u8            devctl;
+	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+	/* HDRC controls CPEN, but beware current surges during device
+	 * connect.  They can trigger transient overcurrent conditions
+	 * that must be ignored.
+	 */
+
+	devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+	if (is_on) {
+		if (musb->xceiv->state == OTG_STATE_A_IDLE) {
+			/* start the session */
+			devctl |= MUSB_DEVCTL_SESSION;
+			musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+			/*
+			 * Wait for the musb to set as A device to enable the
+			 * VBUS
+			 */
+			while (musb_readb(musb->mregs, MUSB_DEVCTL) & 0x80) {
+
+				if (time_after(jiffies, timeout)) {
+					dev_err(musb->controller,
+					"configured as A device timeout");
+					break;
+				}
+			}
+
+		} else {
+			musb->is_active = 1;
+			musb->xceiv->otg->default_a = 1;
+			musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+			devctl |= MUSB_DEVCTL_SESSION;
+			MUSB_HST_MODE(musb);
+		}
+	} else {
+		musb->is_active = 0;
+
+		/* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and jumping
+		 * right to B_IDLE...
+		 */
+		musb->xceiv->otg->default_a = 0;
+		devctl &= ~MUSB_DEVCTL_SESSION;
+		MUSB_DEV_MODE(musb);
+	}
+	musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+	/*
+	 * Devctl values will be updated after vbus goes below
+	 * session_valid. The time taken depends on the capacitance
+	 * on VBUS line. The max discharge time can be upto 1 sec
+	 * as per the spec. Typically on our platform, it is 200ms
+	 */
+	if (!is_on)
+		mdelay(200);
+
+	dev_dbg(musb->controller, "VBUS %s, devctl %02x\n",
+		otg_state_string(musb->xceiv->state),
+		musb_readb(musb->mregs, MUSB_DEVCTL));
+}
+
 static irqreturn_t ux500_musb_interrupt(int irq, void *__hci)
 {
 	unsigned long   flags;
@@ -79,6 +141,8 @@ static int ux500_musb_exit(struct musb *musb)
 static const struct musb_platform_ops ux500_ops = {
 	.init		= ux500_musb_init,
 	.exit		= ux500_musb_exit,
+
+	.set_vbus	= ux500_musb_set_vbus,
 };
 
 static int ux500_probe(struct platform_device *pdev)
-- 
1.8.1.3


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

* [PATCH 3/5] usb: musb: ux500: add otg notifier support
  2013-02-28 10:38 [PATCH 0/5] musb-ux500 and ab8500-usb updates Fabio Baltieri
  2013-02-28 10:38 ` [PATCH 1/5] usb: musb: ux500_dma: add missing MEM resource check Fabio Baltieri
  2013-02-28 10:38 ` [PATCH 2/5] usb: musb: ux500: implement musb_set_vbus Fabio Baltieri
@ 2013-02-28 10:38 ` Fabio Baltieri
  2013-03-04 14:38   ` Felipe Balbi
  2013-02-28 10:38 ` [PATCH 4/5] usb: otg: ab8500-usb: drop support for ab8500 pre v2.0 Fabio Baltieri
  2013-02-28 10:38 ` [PATCH 5/5] usb: otg: ab8500-usb: update irq handling code Fabio Baltieri
  4 siblings, 1 reply; 13+ messages in thread
From: Fabio Baltieri @ 2013-02-28 10:38 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Linus Walleij, linux-kernel, linux-usb, Fabio Baltieri

Add transceiver notifier event handling to the ux500 driver to set vbus
on specific transceiver events.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
---
 drivers/usb/musb/ux500.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
index 5b742ba..b20326bb 100644
--- a/drivers/usb/musb/ux500.c
+++ b/drivers/usb/musb/ux500.c
@@ -98,6 +98,36 @@ static void ux500_musb_set_vbus(struct musb *musb, int is_on)
 		musb_readb(musb->mregs, MUSB_DEVCTL));
 }
 
+static int musb_otg_notifications(struct notifier_block *nb,
+		unsigned long event, void *unused)
+{
+	struct musb *musb = container_of(nb, struct musb, nb);
+
+	dev_dbg(musb->controller, "musb_otg_notifications %ld %s\n",
+			event, otg_state_string(musb->xceiv->state));
+
+	switch (event) {
+	case USB_EVENT_ID:
+		dev_dbg(musb->controller, "ID GND\n");
+		ux500_musb_set_vbus(musb, 1);
+		break;
+	case USB_EVENT_VBUS:
+		dev_dbg(musb->controller, "VBUS Connect\n");
+		break;
+	case USB_EVENT_NONE:
+		dev_dbg(musb->controller, "VBUS Disconnect\n");
+		if (is_host_active(musb))
+			ux500_musb_set_vbus(musb, 0);
+		else
+			musb->xceiv->state = OTG_STATE_B_IDLE;
+		break;
+	default:
+		dev_dbg(musb->controller, "ID float\n");
+		return NOTIFY_DONE;
+	}
+	return NOTIFY_OK;
+}
+
 static irqreturn_t ux500_musb_interrupt(int irq, void *__hci)
 {
 	unsigned long   flags;
@@ -120,12 +150,21 @@ static irqreturn_t ux500_musb_interrupt(int irq, void *__hci)
 
 static int ux500_musb_init(struct musb *musb)
 {
+	int status;
+
 	musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
 	if (IS_ERR_OR_NULL(musb->xceiv)) {
 		pr_err("HS USB OTG: no transceiver configured\n");
 		return -EPROBE_DEFER;
 	}
 
+	musb->nb.notifier_call = musb_otg_notifications;
+	status = usb_register_notifier(musb->xceiv, &musb->nb);
+	if (status < 0) {
+		dev_dbg(musb->controller, "notification register failed\n");
+		return status;
+	}
+
 	musb->isr = ux500_musb_interrupt;
 
 	return 0;
@@ -133,6 +172,8 @@ static int ux500_musb_init(struct musb *musb)
 
 static int ux500_musb_exit(struct musb *musb)
 {
+	usb_unregister_notifier(musb->xceiv, &musb->nb);
+
 	usb_put_phy(musb->xceiv);
 
 	return 0;
-- 
1.8.1.3


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

* [PATCH 4/5] usb: otg: ab8500-usb: drop support for ab8500 pre v2.0
  2013-02-28 10:38 [PATCH 0/5] musb-ux500 and ab8500-usb updates Fabio Baltieri
                   ` (2 preceding siblings ...)
  2013-02-28 10:38 ` [PATCH 3/5] usb: musb: ux500: add otg notifier support Fabio Baltieri
@ 2013-02-28 10:38 ` Fabio Baltieri
  2013-02-28 10:38 ` [PATCH 5/5] usb: otg: ab8500-usb: update irq handling code Fabio Baltieri
  4 siblings, 0 replies; 13+ messages in thread
From: Fabio Baltieri @ 2013-02-28 10:38 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Linus Walleij, linux-kernel, linux-usb, Fabio Baltieri

AB8500 versions preceding 2.0 were only used internally by ST-Ericsson
and are not supported anymore.  This patch drops all v1.0 and v1.1
support code.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
---
 drivers/usb/otg/ab8500-usb.c | 139 ++++---------------------------------------
 1 file changed, 11 insertions(+), 128 deletions(-)

diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c
index 2d86f26..9f5e0e4 100644
--- a/drivers/usb/otg/ab8500-usb.c
+++ b/drivers/usb/otg/ab8500-usb.c
@@ -42,10 +42,8 @@
 #define AB8500_BIT_WD_CTRL_ENABLE (1 << 0)
 #define AB8500_BIT_WD_CTRL_KICK (1 << 1)
 
-#define AB8500_V1x_LINK_STAT_WAIT (HZ/10)
 #define AB8500_WD_KICK_DELAY_US 100 /* usec */
 #define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */
-#define AB8500_WD_V10_DISABLE_DELAY_MS 100 /* ms */
 
 /* Usb line status register */
 enum ab8500_usb_link_status {
@@ -70,16 +68,12 @@ enum ab8500_usb_link_status {
 struct ab8500_usb {
 	struct usb_phy phy;
 	struct device *dev;
-	int irq_num_id_rise;
-	int irq_num_id_fall;
-	int irq_num_vbus_rise;
-	int irq_num_vbus_fall;
+	struct ab8500 *ab8500;
 	int irq_num_link_status;
 	unsigned vbus_draw;
 	struct delayed_work dwork;
 	struct work_struct phy_dis_work;
 	unsigned long link_status_wait;
-	int rev;
 };
 
 static inline struct ab8500_usb *phy_to_ab(struct usb_phy *x)
@@ -102,10 +96,7 @@ static void ab8500_usb_wd_workaround(struct ab8500_usb *ab)
 		(AB8500_BIT_WD_CTRL_ENABLE
 		| AB8500_BIT_WD_CTRL_KICK));
 
-	if (ab->rev > 0x10) /* v1.1 v2.0 */
-		udelay(AB8500_WD_V11_DISABLE_DELAY_US);
-	else /* v1.0 */
-		msleep(AB8500_WD_V10_DISABLE_DELAY_MS);
+	udelay(AB8500_WD_V11_DISABLE_DELAY_US);
 
 	abx500_set_register_interruptible(ab->dev,
 		AB8500_SYS_CTRL2_BLOCK,
@@ -225,29 +216,6 @@ static void ab8500_usb_delayed_work(struct work_struct *work)
 	ab8500_usb_link_status_update(ab);
 }
 
-static irqreturn_t ab8500_usb_v1x_common_irq(int irq, void *data)
-{
-	struct ab8500_usb *ab = (struct ab8500_usb *) data;
-
-	/* Wait for link status to become stable. */
-	schedule_delayed_work(&ab->dwork, ab->link_status_wait);
-
-	return IRQ_HANDLED;
-}
-
-static irqreturn_t ab8500_usb_v1x_vbus_fall_irq(int irq, void *data)
-{
-	struct ab8500_usb *ab = (struct ab8500_usb *) data;
-
-	/* Link status will not be updated till phy is disabled. */
-	ab8500_usb_peri_phy_dis(ab);
-
-	/* Wait for link status to become stable. */
-	schedule_delayed_work(&ab->dwork, ab->link_status_wait);
-
-	return IRQ_HANDLED;
-}
-
 static irqreturn_t ab8500_usb_v20_irq(int irq, void *data)
 {
 	struct ab8500_usb *ab = (struct ab8500_usb *) data;
@@ -361,86 +329,7 @@ static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
 
 static void ab8500_usb_irq_free(struct ab8500_usb *ab)
 {
-	if (ab->rev < 0x20) {
-		free_irq(ab->irq_num_id_rise, ab);
-		free_irq(ab->irq_num_id_fall, ab);
-		free_irq(ab->irq_num_vbus_rise, ab);
-		free_irq(ab->irq_num_vbus_fall, ab);
-	} else {
-		free_irq(ab->irq_num_link_status, ab);
-	}
-}
-
-static int ab8500_usb_v1x_res_setup(struct platform_device *pdev,
-				struct ab8500_usb *ab)
-{
-	int err;
-
-	ab->irq_num_id_rise = platform_get_irq_byname(pdev, "ID_WAKEUP_R");
-	if (ab->irq_num_id_rise < 0) {
-		dev_err(&pdev->dev, "ID rise irq not found\n");
-		return ab->irq_num_id_rise;
-	}
-	err = request_threaded_irq(ab->irq_num_id_rise, NULL,
-		ab8500_usb_v1x_common_irq,
-		IRQF_NO_SUSPEND | IRQF_SHARED,
-		"usb-id-rise", ab);
-	if (err < 0) {
-		dev_err(ab->dev, "request_irq failed for ID rise irq\n");
-		goto fail0;
-	}
-
-	ab->irq_num_id_fall = platform_get_irq_byname(pdev, "ID_WAKEUP_F");
-	if (ab->irq_num_id_fall < 0) {
-		dev_err(&pdev->dev, "ID fall irq not found\n");
-		return ab->irq_num_id_fall;
-	}
-	err = request_threaded_irq(ab->irq_num_id_fall, NULL,
-		ab8500_usb_v1x_common_irq,
-		IRQF_NO_SUSPEND | IRQF_SHARED,
-		"usb-id-fall", ab);
-	if (err < 0) {
-		dev_err(ab->dev, "request_irq failed for ID fall irq\n");
-		goto fail1;
-	}
-
-	ab->irq_num_vbus_rise = platform_get_irq_byname(pdev, "VBUS_DET_R");
-	if (ab->irq_num_vbus_rise < 0) {
-		dev_err(&pdev->dev, "VBUS rise irq not found\n");
-		return ab->irq_num_vbus_rise;
-	}
-	err = request_threaded_irq(ab->irq_num_vbus_rise, NULL,
-		ab8500_usb_v1x_common_irq,
-		IRQF_NO_SUSPEND | IRQF_SHARED,
-		"usb-vbus-rise", ab);
-	if (err < 0) {
-		dev_err(ab->dev, "request_irq failed for Vbus rise irq\n");
-		goto fail2;
-	}
-
-	ab->irq_num_vbus_fall = platform_get_irq_byname(pdev, "VBUS_DET_F");
-	if (ab->irq_num_vbus_fall < 0) {
-		dev_err(&pdev->dev, "VBUS fall irq not found\n");
-		return ab->irq_num_vbus_fall;
-	}
-	err = request_threaded_irq(ab->irq_num_vbus_fall, NULL,
-		ab8500_usb_v1x_vbus_fall_irq,
-		IRQF_NO_SUSPEND | IRQF_SHARED,
-		"usb-vbus-fall", ab);
-	if (err < 0) {
-		dev_err(ab->dev, "request_irq failed for Vbus fall irq\n");
-		goto fail3;
-	}
-
-	return 0;
-fail3:
-	free_irq(ab->irq_num_vbus_rise, ab);
-fail2:
-	free_irq(ab->irq_num_id_fall, ab);
-fail1:
-	free_irq(ab->irq_num_id_rise, ab);
-fail0:
-	return err;
+	free_irq(ab->irq_num_link_status, ab);
 }
 
 static int ab8500_usb_v2_res_setup(struct platform_device *pdev,
@@ -471,16 +360,16 @@ static int ab8500_usb_v2_res_setup(struct platform_device *pdev,
 static int ab8500_usb_probe(struct platform_device *pdev)
 {
 	struct ab8500_usb	*ab;
+	struct ab8500		*ab8500;
 	struct usb_otg		*otg;
 	int err;
 	int rev;
 
+	ab8500 = dev_get_drvdata(pdev->dev.parent);
 	rev = abx500_get_chip_id(&pdev->dev);
-	if (rev < 0) {
-		dev_err(&pdev->dev, "Chip id read failed\n");
-		return rev;
-	} else if (rev < 0x10) {
-		dev_err(&pdev->dev, "Unsupported AB8500 chip\n");
+
+	if (is_ab8500_1p1_or_earlier(ab8500)) {
+		dev_err(&pdev->dev, "Unsupported AB8500 chip rev=%d\n", rev);
 		return -ENODEV;
 	}
 
@@ -495,7 +384,7 @@ static int ab8500_usb_probe(struct platform_device *pdev)
 	}
 
 	ab->dev			= &pdev->dev;
-	ab->rev			= rev;
+	ab->ab8500		= ab8500;
 	ab->phy.dev		= ab->dev;
 	ab->phy.otg		= otg;
 	ab->phy.label		= "ab8500";
@@ -519,13 +408,7 @@ static int ab8500_usb_probe(struct platform_device *pdev)
 	/* all: Disable phy when called from set_host and set_peripheral */
 	INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work);
 
-	if (ab->rev < 0x20) {
-		err = ab8500_usb_v1x_res_setup(pdev, ab);
-		ab->link_status_wait = AB8500_V1x_LINK_STAT_WAIT;
-	} else {
-		err = ab8500_usb_v2_res_setup(pdev, ab);
-	}
-
+	err = ab8500_usb_v2_res_setup(pdev, ab);
 	if (err < 0)
 		goto fail0;
 
@@ -535,7 +418,7 @@ static int ab8500_usb_probe(struct platform_device *pdev)
 		goto fail1;
 	}
 
-	dev_info(&pdev->dev, "AB8500 usb driver initialized\n");
+	dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", rev);
 
 	return 0;
 fail1:
-- 
1.8.1.3


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

* [PATCH 5/5] usb: otg: ab8500-usb: update irq handling code
  2013-02-28 10:38 [PATCH 0/5] musb-ux500 and ab8500-usb updates Fabio Baltieri
                   ` (3 preceding siblings ...)
  2013-02-28 10:38 ` [PATCH 4/5] usb: otg: ab8500-usb: drop support for ab8500 pre v2.0 Fabio Baltieri
@ 2013-02-28 10:38 ` Fabio Baltieri
  4 siblings, 0 replies; 13+ messages in thread
From: Fabio Baltieri @ 2013-02-28 10:38 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Linus Walleij, linux-kernel, linux-usb, Fabio Baltieri

Update irq handling code to notify all possible link status changes of
AB8500 and AB8505 to the ux500-musb glue driver.  The additional event
codes will be used for pm-runtime implementation, and are defined in a
separate ux500-specific header.

This also modify the irq registration code to use devm_* helpers and
drop all non necessary fail path code.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
---
 drivers/usb/musb/ux500.c       |   7 +-
 drivers/usb/otg/ab8500-usb.c   | 440 ++++++++++++++++++++++++++++++++---------
 include/linux/usb/musb-ux500.h |  31 +++
 3 files changed, 382 insertions(+), 96 deletions(-)
 create mode 100644 include/linux/usb/musb-ux500.h

diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
index b20326bb..88ae475 100644
--- a/drivers/usb/musb/ux500.c
+++ b/drivers/usb/musb/ux500.c
@@ -26,6 +26,7 @@
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
+#include <linux/usb/musb-ux500.h>
 
 #include "musb_core.h"
 
@@ -107,14 +108,14 @@ static int musb_otg_notifications(struct notifier_block *nb,
 			event, otg_state_string(musb->xceiv->state));
 
 	switch (event) {
-	case USB_EVENT_ID:
+	case UX500_MUSB_ID:
 		dev_dbg(musb->controller, "ID GND\n");
 		ux500_musb_set_vbus(musb, 1);
 		break;
-	case USB_EVENT_VBUS:
+	case UX500_MUSB_VBUS:
 		dev_dbg(musb->controller, "VBUS Connect\n");
 		break;
-	case USB_EVENT_NONE:
+	case UX500_MUSB_NONE:
 		dev_dbg(musb->controller, "VBUS Disconnect\n");
 		if (is_host_active(musb))
 			ux500_musb_set_vbus(musb, 0);
diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c
index 9f5e0e4..351b036 100644
--- a/drivers/usb/otg/ab8500-usb.c
+++ b/drivers/usb/otg/ab8500-usb.c
@@ -31,9 +31,11 @@
 #include <linux/delay.h>
 #include <linux/mfd/abx500.h>
 #include <linux/mfd/abx500/ab8500.h>
+#include <linux/usb/musb-ux500.h>
 
 #define AB8500_MAIN_WD_CTRL_REG 0x01
 #define AB8500_USB_LINE_STAT_REG 0x80
+#define AB8505_USB_LINE_STAT_REG 0x94
 #define AB8500_USB_PHY_CTRL_REG 0x8A
 
 #define AB8500_BIT_OTG_STAT_ID (1 << 0)
@@ -44,36 +46,76 @@
 
 #define AB8500_WD_KICK_DELAY_US 100 /* usec */
 #define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */
+#define AB8500_V20_31952_DISABLE_DELAY_US 100 /* usec */
 
 /* Usb line status register */
 enum ab8500_usb_link_status {
-	USB_LINK_NOT_CONFIGURED = 0,
-	USB_LINK_STD_HOST_NC,
-	USB_LINK_STD_HOST_C_NS,
-	USB_LINK_STD_HOST_C_S,
-	USB_LINK_HOST_CHG_NM,
-	USB_LINK_HOST_CHG_HS,
-	USB_LINK_HOST_CHG_HS_CHIRP,
-	USB_LINK_DEDICATED_CHG,
-	USB_LINK_ACA_RID_A,
-	USB_LINK_ACA_RID_B,
-	USB_LINK_ACA_RID_C_NM,
-	USB_LINK_ACA_RID_C_HS,
-	USB_LINK_ACA_RID_C_HS_CHIRP,
-	USB_LINK_HM_IDGND,
-	USB_LINK_RESERVED,
-	USB_LINK_NOT_VALID_LINK
+	USB_LINK_NOT_CONFIGURED_8500 = 0,
+	USB_LINK_STD_HOST_NC_8500,
+	USB_LINK_STD_HOST_C_NS_8500,
+	USB_LINK_STD_HOST_C_S_8500,
+	USB_LINK_HOST_CHG_NM_8500,
+	USB_LINK_HOST_CHG_HS_8500,
+	USB_LINK_HOST_CHG_HS_CHIRP_8500,
+	USB_LINK_DEDICATED_CHG_8500,
+	USB_LINK_ACA_RID_A_8500,
+	USB_LINK_ACA_RID_B_8500,
+	USB_LINK_ACA_RID_C_NM_8500,
+	USB_LINK_ACA_RID_C_HS_8500,
+	USB_LINK_ACA_RID_C_HS_CHIRP_8500,
+	USB_LINK_HM_IDGND_8500,
+	USB_LINK_RESERVED_8500,
+	USB_LINK_NOT_VALID_LINK_8500,
+};
+
+enum ab8505_usb_link_status {
+	USB_LINK_NOT_CONFIGURED_8505 = 0,
+	USB_LINK_STD_HOST_NC_8505,
+	USB_LINK_STD_HOST_C_NS_8505,
+	USB_LINK_STD_HOST_C_S_8505,
+	USB_LINK_CDP_8505,
+	USB_LINK_RESERVED0_8505,
+	USB_LINK_RESERVED1_8505,
+	USB_LINK_DEDICATED_CHG_8505,
+	USB_LINK_ACA_RID_A_8505,
+	USB_LINK_ACA_RID_B_8505,
+	USB_LINK_ACA_RID_C_NM_8505,
+	USB_LINK_RESERVED2_8505,
+	USB_LINK_RESERVED3_8505,
+	USB_LINK_HM_IDGND_8505,
+	USB_LINK_CHARGERPORT_NOT_OK_8505,
+	USB_LINK_CHARGER_DM_HIGH_8505,
+	USB_LINK_PHYEN_NO_VBUS_NO_IDGND_8505,
+	USB_LINK_STD_UPSTREAM_NO_IDGNG_NO_VBUS_8505,
+	USB_LINK_STD_UPSTREAM_8505,
+	USB_LINK_CHARGER_SE1_8505,
+	USB_LINK_CARKIT_CHGR_1_8505,
+	USB_LINK_CARKIT_CHGR_2_8505,
+	USB_LINK_ACA_DOCK_CHGR_8505,
+	USB_LINK_SAMSUNG_BOOT_CBL_PHY_EN_8505,
+	USB_LINK_SAMSUNG_BOOT_CBL_PHY_DISB_8505,
+	USB_LINK_SAMSUNG_UART_CBL_PHY_EN_8505,
+	USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_8505,
+	USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_8505,
+};
+
+enum ab8500_usb_mode {
+	USB_IDLE = 0,
+	USB_PERIPHERAL,
+	USB_HOST,
+	USB_DEDICATED_CHG
 };
 
 struct ab8500_usb {
 	struct usb_phy phy;
 	struct device *dev;
 	struct ab8500 *ab8500;
-	int irq_num_link_status;
 	unsigned vbus_draw;
 	struct delayed_work dwork;
 	struct work_struct phy_dis_work;
 	unsigned long link_status_wait;
+	enum ab8500_usb_mode mode;
+	int previous_link_status_state;
 };
 
 static inline struct ab8500_usb *phy_to_ab(struct usb_phy *x)
@@ -104,6 +146,17 @@ static void ab8500_usb_wd_workaround(struct ab8500_usb *ab)
 		0);
 }
 
+static void ab8500_usb_wd_linkstatus(struct ab8500_usb *ab, u8 bit)
+{
+	/* Workaround for v2.0 bug # 31952 */
+	if (is_ab8500_2p0(ab->ab8500)) {
+		abx500_mask_and_set_register_interruptible(ab->dev,
+				AB8500_USB, AB8500_USB_PHY_CTRL_REG,
+				bit, bit);
+		udelay(AB8500_V20_31952_DISABLE_DELAY_US);
+	}
+}
+
 static void ab8500_usb_phy_ctrl(struct ab8500_usb *ab, bool sel_host,
 					bool enable)
 {
@@ -139,92 +192,276 @@ static void ab8500_usb_phy_ctrl(struct ab8500_usb *ab, bool sel_host,
 #define ab8500_usb_peri_phy_en(ab)	ab8500_usb_phy_ctrl(ab, false, true)
 #define ab8500_usb_peri_phy_dis(ab)	ab8500_usb_phy_ctrl(ab, false, false)
 
-static int ab8500_usb_link_status_update(struct ab8500_usb *ab)
+static int ab8505_usb_link_status_update(struct ab8500_usb *ab,
+		enum ab8505_usb_link_status lsts)
 {
-	u8 reg;
-	enum ab8500_usb_link_status lsts;
-	void *v = NULL;
-	enum usb_phy_events event;
+	enum ux500_musb_vbus_id_status event = 0;
 
-	abx500_get_register_interruptible(ab->dev,
-			AB8500_USB,
-			AB8500_USB_LINE_STAT_REG,
-			&reg);
+	dev_dbg(ab->dev, "ab8505_usb_link_status_update %d\n", lsts);
 
-	lsts = (reg >> 3) & 0x0F;
+	/*
+	 * Spurious link_status interrupts are seen at the time of
+	 * disconnection of a device in RIDA state
+	 */
+	if (ab->previous_link_status_state == USB_LINK_ACA_RID_A_8505 &&
+			(lsts == USB_LINK_STD_HOST_NC_8505))
+		return 0;
+
+	ab->previous_link_status_state = lsts;
 
 	switch (lsts) {
-	case USB_LINK_NOT_CONFIGURED:
-	case USB_LINK_RESERVED:
-	case USB_LINK_NOT_VALID_LINK:
-		/* TODO: Disable regulators. */
-		ab8500_usb_host_phy_dis(ab);
-		ab8500_usb_peri_phy_dis(ab);
-		ab->phy.state = OTG_STATE_B_IDLE;
+	case USB_LINK_ACA_RID_B_8505:
+		event = UX500_MUSB_RIDB;
+	case USB_LINK_NOT_CONFIGURED_8505:
+	case USB_LINK_RESERVED0_8505:
+	case USB_LINK_RESERVED1_8505:
+	case USB_LINK_RESERVED2_8505:
+	case USB_LINK_RESERVED3_8505:
+		ab->mode = USB_IDLE;
 		ab->phy.otg->default_a = false;
 		ab->vbus_draw = 0;
-		event = USB_EVENT_NONE;
+		if (event != UX500_MUSB_RIDB)
+			event = UX500_MUSB_NONE;
+		/*
+		 * Fallback to default B_IDLE as nothing
+		 * is connected
+		 */
+		ab->phy.state = OTG_STATE_B_IDLE;
 		break;
 
-	case USB_LINK_STD_HOST_NC:
-	case USB_LINK_STD_HOST_C_NS:
-	case USB_LINK_STD_HOST_C_S:
-	case USB_LINK_HOST_CHG_NM:
-	case USB_LINK_HOST_CHG_HS:
-	case USB_LINK_HOST_CHG_HS_CHIRP:
-		if (ab->phy.otg->gadget) {
-			/* TODO: Enable regulators. */
+	case USB_LINK_ACA_RID_C_NM_8505:
+		event = UX500_MUSB_RIDC;
+	case USB_LINK_STD_HOST_NC_8505:
+	case USB_LINK_STD_HOST_C_NS_8505:
+	case USB_LINK_STD_HOST_C_S_8505:
+	case USB_LINK_CDP_8505:
+		if (ab->mode == USB_IDLE) {
+			ab->mode = USB_PERIPHERAL;
 			ab8500_usb_peri_phy_en(ab);
-			v = ab->phy.otg->gadget;
+			atomic_notifier_call_chain(&ab->phy.notifier,
+					UX500_MUSB_PREPARE, &ab->vbus_draw);
 		}
-		event = USB_EVENT_VBUS;
+		if (event != UX500_MUSB_RIDC)
+			event = UX500_MUSB_VBUS;
 		break;
 
-	case USB_LINK_HM_IDGND:
-		if (ab->phy.otg->host) {
-			/* TODO: Enable regulators. */
+	case USB_LINK_ACA_RID_A_8505:
+	case USB_LINK_ACA_DOCK_CHGR_8505:
+		event = UX500_MUSB_RIDA;
+	case USB_LINK_HM_IDGND_8505:
+		if (ab->mode == USB_IDLE) {
+			ab->mode = USB_HOST;
 			ab8500_usb_host_phy_en(ab);
-			v = ab->phy.otg->host;
+			atomic_notifier_call_chain(&ab->phy.notifier,
+					UX500_MUSB_PREPARE, &ab->vbus_draw);
 		}
-		ab->phy.state = OTG_STATE_A_IDLE;
 		ab->phy.otg->default_a = true;
-		event = USB_EVENT_ID;
+		if (event != UX500_MUSB_RIDA)
+			event = UX500_MUSB_ID;
+		atomic_notifier_call_chain(&ab->phy.notifier,
+				event, &ab->vbus_draw);
 		break;
 
-	case USB_LINK_ACA_RID_A:
-	case USB_LINK_ACA_RID_B:
-		/* TODO */
-	case USB_LINK_ACA_RID_C_NM:
-	case USB_LINK_ACA_RID_C_HS:
-	case USB_LINK_ACA_RID_C_HS_CHIRP:
-	case USB_LINK_DEDICATED_CHG:
-		/* TODO: vbus_draw */
-		event = USB_EVENT_CHARGER;
+	case USB_LINK_DEDICATED_CHG_8505:
+		ab->mode = USB_DEDICATED_CHG;
+		event = UX500_MUSB_CHARGER;
+		atomic_notifier_call_chain(&ab->phy.notifier,
+				event, &ab->vbus_draw);
+		break;
+
+	default:
 		break;
 	}
 
-	atomic_notifier_call_chain(&ab->phy.notifier, event, v);
+	return 0;
+}
+
+static int ab8500_usb_link_status_update(struct ab8500_usb *ab,
+		enum ab8500_usb_link_status lsts)
+{
+	enum ux500_musb_vbus_id_status event = 0;
+
+	dev_dbg(ab->dev, "ab8500_usb_link_status_update %d\n", lsts);
+
+	/*
+	 * Spurious link_status interrupts are seen in case of a
+	 * disconnection of a device in IDGND and RIDA stage
+	 */
+	if (ab->previous_link_status_state == USB_LINK_HM_IDGND_8500 &&
+			(lsts == USB_LINK_STD_HOST_C_NS_8500 ||
+			 lsts == USB_LINK_STD_HOST_NC_8500))
+		return 0;
+
+	if (ab->previous_link_status_state == USB_LINK_ACA_RID_A_8500 &&
+			lsts == USB_LINK_STD_HOST_NC_8500)
+		return 0;
+
+	ab->previous_link_status_state = lsts;
+
+	switch (lsts) {
+	case USB_LINK_ACA_RID_B_8500:
+		event = UX500_MUSB_RIDB;
+	case USB_LINK_NOT_CONFIGURED_8500:
+	case USB_LINK_NOT_VALID_LINK_8500:
+		ab->mode = USB_IDLE;
+		ab->phy.otg->default_a = false;
+		ab->vbus_draw = 0;
+		if (event != UX500_MUSB_RIDB)
+			event = UX500_MUSB_NONE;
+		/* Fallback to default B_IDLE as nothing is connected */
+		ab->phy.state = OTG_STATE_B_IDLE;
+		break;
+
+	case USB_LINK_ACA_RID_C_NM_8500:
+	case USB_LINK_ACA_RID_C_HS_8500:
+	case USB_LINK_ACA_RID_C_HS_CHIRP_8500:
+		event = UX500_MUSB_RIDC;
+	case USB_LINK_STD_HOST_NC_8500:
+	case USB_LINK_STD_HOST_C_NS_8500:
+	case USB_LINK_STD_HOST_C_S_8500:
+	case USB_LINK_HOST_CHG_NM_8500:
+	case USB_LINK_HOST_CHG_HS_8500:
+	case USB_LINK_HOST_CHG_HS_CHIRP_8500:
+		if (ab->mode == USB_IDLE) {
+			ab->mode = USB_PERIPHERAL;
+			ab8500_usb_peri_phy_en(ab);
+			atomic_notifier_call_chain(&ab->phy.notifier,
+					UX500_MUSB_PREPARE, &ab->vbus_draw);
+		}
+		if (event != UX500_MUSB_RIDC)
+			event = UX500_MUSB_VBUS;
+		break;
+
+	case USB_LINK_ACA_RID_A_8500:
+		event = UX500_MUSB_RIDA;
+	case USB_LINK_HM_IDGND_8500:
+		if (ab->mode == USB_IDLE) {
+			ab->mode = USB_HOST;
+			ab8500_usb_host_phy_en(ab);
+			atomic_notifier_call_chain(&ab->phy.notifier,
+					UX500_MUSB_PREPARE, &ab->vbus_draw);
+		}
+		ab->phy.otg->default_a = true;
+		if (event != UX500_MUSB_RIDA)
+			event = UX500_MUSB_ID;
+		atomic_notifier_call_chain(&ab->phy.notifier,
+				event, &ab->vbus_draw);
+		break;
+
+	case USB_LINK_DEDICATED_CHG_8500:
+		ab->mode = USB_DEDICATED_CHG;
+		event = UX500_MUSB_CHARGER;
+		atomic_notifier_call_chain(&ab->phy.notifier,
+				event, &ab->vbus_draw);
+		break;
+
+	case USB_LINK_RESERVED_8500:
+		break;
+	}
 
 	return 0;
 }
 
-static void ab8500_usb_delayed_work(struct work_struct *work)
+/*
+ * Connection Sequence:
+ *   1. Link Status Interrupt
+ *   2. Enable AB clock
+ *   3. Enable AB regulators
+ *   4. Enable USB phy
+ *   5. Reset the musb controller
+ *   6. Switch the ULPI GPIO pins to fucntion mode
+ *   7. Enable the musb Peripheral5 clock
+ *   8. Restore MUSB context
+ */
+static int abx500_usb_link_status_update(struct ab8500_usb *ab)
 {
-	struct ab8500_usb *ab = container_of(work, struct ab8500_usb,
-						dwork.work);
+	u8 reg;
+	int ret = 0;
+
+	if (is_ab8500(ab->ab8500)) {
+		enum ab8500_usb_link_status lsts;
+
+		abx500_get_register_interruptible(ab->dev,
+				AB8500_USB, AB8500_USB_LINE_STAT_REG, &reg);
+		lsts = (reg >> 3) & 0x0F;
+		ret = ab8500_usb_link_status_update(ab, lsts);
+	} else if (is_ab8505(ab->ab8500)) {
+		enum ab8505_usb_link_status lsts;
+
+		abx500_get_register_interruptible(ab->dev,
+				AB8500_USB, AB8505_USB_LINE_STAT_REG, &reg);
+		lsts = (reg >> 3) & 0x1F;
+		ret = ab8505_usb_link_status_update(ab, lsts);
+	}
+
+	return ret;
+}
+
+/*
+ * Disconnection Sequence:
+ *   1. Disconect Interrupt
+ *   2. Disable regulators
+ *   3. Disable AB clock
+ *   4. Disable the Phy
+ *   5. Link Status Interrupt
+ *   6. Disable Musb Clock
+ */
+static irqreturn_t ab8500_usb_disconnect_irq(int irq, void *data)
+{
+	struct ab8500_usb *ab = (struct ab8500_usb *) data;
+	enum usb_phy_events event = UX500_MUSB_NONE;
+
+	/* Link status will not be updated till phy is disabled. */
+	if (ab->mode == USB_HOST) {
+		ab->phy.otg->default_a = false;
+		ab->vbus_draw = 0;
+		atomic_notifier_call_chain(&ab->phy.notifier,
+				event, &ab->vbus_draw);
+		ab8500_usb_host_phy_dis(ab);
+		ab->mode = USB_IDLE;
+	}
+
+	if (ab->mode == USB_PERIPHERAL) {
+		atomic_notifier_call_chain(&ab->phy.notifier,
+				event, &ab->vbus_draw);
+		ab8500_usb_peri_phy_dis(ab);
+		atomic_notifier_call_chain(&ab->phy.notifier,
+				UX500_MUSB_CLEAN, &ab->vbus_draw);
+		ab->mode = USB_IDLE;
+		ab->phy.otg->default_a = false;
+		ab->vbus_draw = 0;
+	}
+
+	if (is_ab8500_2p0(ab->ab8500)) {
+		if (ab->mode == USB_DEDICATED_CHG) {
+			ab8500_usb_wd_linkstatus(ab,
+					AB8500_BIT_PHY_CTRL_DEVICE_EN);
+			abx500_mask_and_set_register_interruptible(ab->dev,
+					AB8500_USB, AB8500_USB_PHY_CTRL_REG,
+					AB8500_BIT_PHY_CTRL_DEVICE_EN, 0);
+		}
+	}
 
-	ab8500_usb_link_status_update(ab);
+	return IRQ_HANDLED;
 }
 
-static irqreturn_t ab8500_usb_v20_irq(int irq, void *data)
+static irqreturn_t ab8500_usb_link_status_irq(int irq, void *data)
 {
 	struct ab8500_usb *ab = (struct ab8500_usb *) data;
 
-	ab8500_usb_link_status_update(ab);
+	abx500_usb_link_status_update(ab);
 
 	return IRQ_HANDLED;
 }
 
+static void ab8500_usb_delayed_work(struct work_struct *work)
+{
+	struct ab8500_usb *ab = container_of(work, struct ab8500_usb,
+						dwork.work);
+
+	abx500_usb_link_status_update(ab);
+}
+
 static void ab8500_usb_phy_disable_work(struct work_struct *work)
 {
 	struct ab8500_usb *ab = container_of(work, struct ab8500_usb,
@@ -250,7 +487,7 @@ static int ab8500_usb_set_power(struct usb_phy *phy, unsigned mA)
 
 	if (mA)
 		atomic_notifier_call_chain(&ab->phy.notifier,
-				USB_EVENT_ENUMERATED, ab->phy.otg->gadget);
+				UX500_MUSB_ENUMERATED, ab->phy.otg->gadget);
 	return 0;
 }
 
@@ -327,30 +564,48 @@ static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
 	return 0;
 }
 
-static void ab8500_usb_irq_free(struct ab8500_usb *ab)
-{
-	free_irq(ab->irq_num_link_status, ab);
-}
-
-static int ab8500_usb_v2_res_setup(struct platform_device *pdev,
-				struct ab8500_usb *ab)
+static int ab8500_usb_irq_setup(struct platform_device *pdev,
+		struct ab8500_usb *ab)
 {
 	int err;
+	int irq;
 
-	ab->irq_num_link_status = platform_get_irq_byname(pdev,
-						"USB_LINK_STATUS");
-	if (ab->irq_num_link_status < 0) {
+	irq = platform_get_irq_byname(pdev, "USB_LINK_STATUS");
+	if (irq < 0) {
 		dev_err(&pdev->dev, "Link status irq not found\n");
-		return ab->irq_num_link_status;
+		return irq;
+	}
+	err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+			ab8500_usb_link_status_irq,
+			IRQF_NO_SUSPEND | IRQF_SHARED, "usb-link-status", ab);
+	if (err < 0) {
+		dev_err(ab->dev, "request_irq failed for link status irq\n");
+		return err;
 	}
 
-	err = request_threaded_irq(ab->irq_num_link_status, NULL,
-		ab8500_usb_v20_irq,
-		IRQF_NO_SUSPEND | IRQF_SHARED,
-		"usb-link-status", ab);
+	irq = platform_get_irq_byname(pdev, "ID_WAKEUP_F");
+	if (irq < 0) {
+		dev_err(&pdev->dev, "ID fall irq not found\n");
+		return irq;
+	}
+	err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+			ab8500_usb_disconnect_irq,
+			IRQF_NO_SUSPEND | IRQF_SHARED, "usb-id-fall", ab);
 	if (err < 0) {
-		dev_err(ab->dev,
-			"request_irq failed for link status irq\n");
+		dev_err(ab->dev, "request_irq failed for ID fall irq\n");
+		return err;
+	}
+
+	irq = platform_get_irq_byname(pdev, "VBUS_DET_F");
+	if (irq < 0) {
+		dev_err(&pdev->dev, "VBUS fall irq not found\n");
+		return irq;
+	}
+	err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+			ab8500_usb_disconnect_irq,
+			IRQF_NO_SUSPEND | IRQF_SHARED, "usb-vbus-fall", ab);
+	if (err < 0) {
+		dev_err(ab->dev, "request_irq failed for Vbus fall irq\n");
 		return err;
 	}
 
@@ -408,22 +663,23 @@ static int ab8500_usb_probe(struct platform_device *pdev)
 	/* all: Disable phy when called from set_host and set_peripheral */
 	INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work);
 
-	err = ab8500_usb_v2_res_setup(pdev, ab);
+	err = ab8500_usb_irq_setup(pdev, ab);
 	if (err < 0)
-		goto fail0;
+		goto fail;
 
 	err = usb_add_phy(&ab->phy, USB_PHY_TYPE_USB2);
 	if (err) {
 		dev_err(&pdev->dev, "Can't register transceiver\n");
-		goto fail1;
+		goto fail;
 	}
 
+	/* Needed to enable ID detection. */
+	ab8500_usb_wd_workaround(ab);
+
 	dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", rev);
 
 	return 0;
-fail1:
-	ab8500_usb_irq_free(ab);
-fail0:
+fail:
 	kfree(otg);
 	kfree(ab);
 	return err;
@@ -433,8 +689,6 @@ static int ab8500_usb_remove(struct platform_device *pdev)
 {
 	struct ab8500_usb *ab = platform_get_drvdata(pdev);
 
-	ab8500_usb_irq_free(ab);
-
 	cancel_delayed_work_sync(&ab->dwork);
 
 	cancel_work_sync(&ab->phy_dis_work);
diff --git a/include/linux/usb/musb-ux500.h b/include/linux/usb/musb-ux500.h
new file mode 100644
index 0000000..1e2c713
--- /dev/null
+++ b/include/linux/usb/musb-ux500.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 ST-Ericsson AB
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MUSB_UX500_H__
+#define __MUSB_UX500_H__
+
+enum ux500_musb_vbus_id_status {
+	UX500_MUSB_NONE = 0,
+	UX500_MUSB_VBUS,
+	UX500_MUSB_ID,
+	UX500_MUSB_CHARGER,
+	UX500_MUSB_ENUMERATED,
+	UX500_MUSB_RIDA,
+	UX500_MUSB_RIDB,
+	UX500_MUSB_RIDC,
+	UX500_MUSB_PREPARE,
+	UX500_MUSB_CLEAN,
+};
+
+#endif	/* __MUSB_UX500_H__ */
-- 
1.8.1.3


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

* Re: [PATCH 3/5] usb: musb: ux500: add otg notifier support
  2013-02-28 10:38 ` [PATCH 3/5] usb: musb: ux500: add otg notifier support Fabio Baltieri
@ 2013-03-04 14:38   ` Felipe Balbi
  2013-03-06  1:40     ` Fabio Baltieri
  0 siblings, 1 reply; 13+ messages in thread
From: Felipe Balbi @ 2013-03-04 14:38 UTC (permalink / raw)
  To: Fabio Baltieri; +Cc: Felipe Balbi, Linus Walleij, linux-kernel, linux-usb

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

On Thu, Feb 28, 2013 at 11:38:52AM +0100, Fabio Baltieri wrote:
> Add transceiver notifier event handling to the ux500 driver to set vbus
> on specific transceiver events.
> 
> Acked-by: Linus Walleij <linus.walleij@linaro.org>
> Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
> ---
>  drivers/usb/musb/ux500.c | 41 +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 41 insertions(+)
> 
> diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
> index 5b742ba..b20326bb 100644
> --- a/drivers/usb/musb/ux500.c
> +++ b/drivers/usb/musb/ux500.c
> @@ -98,6 +98,36 @@ static void ux500_musb_set_vbus(struct musb *musb, int is_on)
>  		musb_readb(musb->mregs, MUSB_DEVCTL));
>  }
>  
> +static int musb_otg_notifications(struct notifier_block *nb,
> +		unsigned long event, void *unused)
> +{
> +	struct musb *musb = container_of(nb, struct musb, nb);
> +
> +	dev_dbg(musb->controller, "musb_otg_notifications %ld %s\n",
> +			event, otg_state_string(musb->xceiv->state));
> +
> +	switch (event) {
> +	case USB_EVENT_ID:
> +		dev_dbg(musb->controller, "ID GND\n");
> +		ux500_musb_set_vbus(musb, 1);
> +		break;
> +	case USB_EVENT_VBUS:
> +		dev_dbg(musb->controller, "VBUS Connect\n");

are you sure this is correct ? you're not doing anything in case of
vbus event. Shouldn't you make sure your vbus is off ? What if your user
uses a non-standard cable which has id-pin grounded on both sides ?

-- 
balbi

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

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

* Re: [PATCH 3/5] usb: musb: ux500: add otg notifier support
  2013-03-04 14:38   ` Felipe Balbi
@ 2013-03-06  1:40     ` Fabio Baltieri
  2013-03-06  8:54       ` Felipe Balbi
  0 siblings, 1 reply; 13+ messages in thread
From: Fabio Baltieri @ 2013-03-06  1:40 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Linus Walleij, linux-kernel, linux-usb

Hello Felipe,

On Mon, Mar 04, 2013 at 04:38:10PM +0200, Felipe Balbi wrote:
> On Thu, Feb 28, 2013 at 11:38:52AM +0100, Fabio Baltieri wrote:
> > Add transceiver notifier event handling to the ux500 driver to set vbus
> > on specific transceiver events.
> > 
> > Acked-by: Linus Walleij <linus.walleij@linaro.org>
> > Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
> > ---
> >  drivers/usb/musb/ux500.c | 41 +++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 41 insertions(+)
> > 
> > diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
> > index 5b742ba..b20326bb 100644
> > --- a/drivers/usb/musb/ux500.c
> > +++ b/drivers/usb/musb/ux500.c
> > @@ -98,6 +98,36 @@ static void ux500_musb_set_vbus(struct musb *musb, int is_on)
> >  		musb_readb(musb->mregs, MUSB_DEVCTL));
> >  }
> >  
> > +static int musb_otg_notifications(struct notifier_block *nb,
> > +		unsigned long event, void *unused)
> > +{
> > +	struct musb *musb = container_of(nb, struct musb, nb);
> > +
> > +	dev_dbg(musb->controller, "musb_otg_notifications %ld %s\n",
> > +			event, otg_state_string(musb->xceiv->state));
> > +
> > +	switch (event) {
> > +	case USB_EVENT_ID:
> > +		dev_dbg(musb->controller, "ID GND\n");
> > +		ux500_musb_set_vbus(musb, 1);
> > +		break;
> > +	case USB_EVENT_VBUS:
> > +		dev_dbg(musb->controller, "VBUS Connect\n");
> 
> are you sure this is correct ? you're not doing anything in case of
> vbus event. Shouldn't you make sure your vbus is off ?

The implementation I'm taking as reference for this patches does not do
anything on VBUS event and as I can see from commmit history, it has
been based on the omap2430 one, that in turns used to just run phy_init
- and that's not implemented.  But...

> What if your user uses a non-standard cable which has id-pin grounded
> on both sides ?

A cable with two micro/mini connectors with grounded IDs?  Did you ever
encountered such a thing?  I guess a ux500_musb_set_vbus(musb, 0) would
not hurt in that case.

Fabio

-- 
Fabio Baltieri

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

* Re: [PATCH 3/5] usb: musb: ux500: add otg notifier support
  2013-03-06  1:40     ` Fabio Baltieri
@ 2013-03-06  8:54       ` Felipe Balbi
  2013-03-07  1:58         ` Fabio Baltieri
  2013-03-07  8:57         ` Fabio Baltieri
  0 siblings, 2 replies; 13+ messages in thread
From: Felipe Balbi @ 2013-03-06  8:54 UTC (permalink / raw)
  To: Fabio Baltieri; +Cc: Felipe Balbi, Linus Walleij, linux-kernel, linux-usb

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

Hi,

On Wed, Mar 06, 2013 at 09:40:28AM +0800, Fabio Baltieri wrote:
> > > Add transceiver notifier event handling to the ux500 driver to set vbus
> > > on specific transceiver events.
> > > 
> > > Acked-by: Linus Walleij <linus.walleij@linaro.org>
> > > Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
> > > ---
> > >  drivers/usb/musb/ux500.c | 41 +++++++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 41 insertions(+)
> > > 
> > > diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
> > > index 5b742ba..b20326bb 100644
> > > --- a/drivers/usb/musb/ux500.c
> > > +++ b/drivers/usb/musb/ux500.c
> > > @@ -98,6 +98,36 @@ static void ux500_musb_set_vbus(struct musb *musb, int is_on)
> > >  		musb_readb(musb->mregs, MUSB_DEVCTL));
> > >  }
> > >  
> > > +static int musb_otg_notifications(struct notifier_block *nb,
> > > +		unsigned long event, void *unused)
> > > +{
> > > +	struct musb *musb = container_of(nb, struct musb, nb);
> > > +
> > > +	dev_dbg(musb->controller, "musb_otg_notifications %ld %s\n",
> > > +			event, otg_state_string(musb->xceiv->state));
> > > +
> > > +	switch (event) {
> > > +	case USB_EVENT_ID:
> > > +		dev_dbg(musb->controller, "ID GND\n");
> > > +		ux500_musb_set_vbus(musb, 1);
> > > +		break;
> > > +	case USB_EVENT_VBUS:
> > > +		dev_dbg(musb->controller, "VBUS Connect\n");
> > 
> > are you sure this is correct ? you're not doing anything in case of
> > vbus event. Shouldn't you make sure your vbus is off ?
> 
> The implementation I'm taking as reference for this patches does not do
> anything on VBUS event and as I can see from commmit history, it has
> been based on the omap2430 one, that in turns used to just run phy_init
> - and that's not implemented.  But...
> 
> > What if your user uses a non-standard cable which has id-pin grounded
> > on both sides ?
> 
> A cable with two micro/mini connectors with grounded IDs?  Did you ever
> encountered such a thing?  I guess a ux500_musb_set_vbus(musb, 0) would

in fact, no...

> not hurt in that case.

right, just to be sure. Maybe I should fix omap2430 as well. I'll add it
to my TODO list.

-- 
balbi

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

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

* Re: [PATCH 3/5] usb: musb: ux500: add otg notifier support
  2013-03-06  8:54       ` Felipe Balbi
@ 2013-03-07  1:58         ` Fabio Baltieri
  2013-03-07  8:57         ` Fabio Baltieri
  1 sibling, 0 replies; 13+ messages in thread
From: Fabio Baltieri @ 2013-03-07  1:58 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Linus Walleij, linux-kernel, linux-usb

On Wed, Mar 06, 2013 at 10:54:46AM +0200, Felipe Balbi wrote:
> > > are you sure this is correct ? you're not doing anything in case of
> > > vbus event. Shouldn't you make sure your vbus is off ?
> > 
> > The implementation I'm taking as reference for this patches does not do
> > anything on VBUS event and as I can see from commmit history, it has
> > been based on the omap2430 one, that in turns used to just run phy_init
> > - and that's not implemented.  But...
> > 
> > > What if your user uses a non-standard cable which has id-pin grounded
> > > on both sides ?
> > 
> > A cable with two micro/mini connectors with grounded IDs?  Did you ever
> > encountered such a thing?  I guess a ux500_musb_set_vbus(musb, 0) would
> 
> in fact, no...
> 
> > not hurt in that case.
> 
> right, just to be sure. Maybe I should fix omap2430 as well. I'll add it
> to my TODO list.

Fair enough, I'll send a v2 of this patch with the added vbus-off as
soon as I manage to test it.

Thanks,
Fabio

-- 
Fabio Baltieri

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

* [PATCH 3/5] usb: musb: ux500: add otg notifier support
  2013-03-06  8:54       ` Felipe Balbi
  2013-03-07  1:58         ` Fabio Baltieri
@ 2013-03-07  8:57         ` Fabio Baltieri
  2013-03-07  9:10           ` Fabio Baltieri
  1 sibling, 1 reply; 13+ messages in thread
From: Fabio Baltieri @ 2013-03-07  8:57 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Linus Walleij, linux-kernel, linux-usb, Fabio Baltieri

Add transceiver notifier event handling to the ux500 driver to set vbus
on specific transceiver events.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
---
 drivers/usb/musb/ux500.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
index 5b742ba..55f24c6 100644
--- a/drivers/usb/musb/ux500.c
+++ b/drivers/usb/musb/ux500.c
@@ -98,6 +98,37 @@ static void ux500_musb_set_vbus(struct musb *musb, int is_on)
 		musb_readb(musb->mregs, MUSB_DEVCTL));
 }
 
+static int musb_otg_notifications(struct notifier_block *nb,
+		unsigned long event, void *unused)
+{
+	struct musb *musb = container_of(nb, struct musb, nb);
+
+	dev_dbg(musb->controller, "musb_otg_notifications %ld %s\n",
+			event, otg_state_string(musb->xceiv->state));
+
+	switch (event) {
+	case USB_EVENT_ID:
+		dev_dbg(musb->controller, "ID GND\n");
+		ux500_musb_set_vbus(musb, 1);
+		break;
+	case USB_EVENT_VBUS:
+		dev_dbg(musb->controller, "VBUS Connect\n");
+		ux500_musb_set_vbus(musb, 0);
+		break;
+	case USB_EVENT_NONE:
+		dev_dbg(musb->controller, "VBUS Disconnect\n");
+		if (is_host_active(musb))
+			ux500_musb_set_vbus(musb, 0);
+		else
+			musb->xceiv->state = OTG_STATE_B_IDLE;
+		break;
+	default:
+		dev_dbg(musb->controller, "ID float\n");
+		return NOTIFY_DONE;
+	}
+	return NOTIFY_OK;
+}
+
 static irqreturn_t ux500_musb_interrupt(int irq, void *__hci)
 {
 	unsigned long   flags;
@@ -120,12 +151,21 @@ static irqreturn_t ux500_musb_interrupt(int irq, void *__hci)
 
 static int ux500_musb_init(struct musb *musb)
 {
+	int status;
+
 	musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
 	if (IS_ERR_OR_NULL(musb->xceiv)) {
 		pr_err("HS USB OTG: no transceiver configured\n");
 		return -EPROBE_DEFER;
 	}
 
+	musb->nb.notifier_call = musb_otg_notifications;
+	status = usb_register_notifier(musb->xceiv, &musb->nb);
+	if (status < 0) {
+		dev_dbg(musb->controller, "notification register failed\n");
+		return status;
+	}
+
 	musb->isr = ux500_musb_interrupt;
 
 	return 0;
@@ -133,6 +173,8 @@ static int ux500_musb_init(struct musb *musb)
 
 static int ux500_musb_exit(struct musb *musb)
 {
+	usb_unregister_notifier(musb->xceiv, &musb->nb);
+
 	usb_put_phy(musb->xceiv);
 
 	return 0;
-- 
1.8.1.3


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

* Re: [PATCH 3/5] usb: musb: ux500: add otg notifier support
  2013-03-07  8:57         ` Fabio Baltieri
@ 2013-03-07  9:10           ` Fabio Baltieri
  2013-03-07 10:44             ` Felipe Balbi
  0 siblings, 1 reply; 13+ messages in thread
From: Fabio Baltieri @ 2013-03-07  9:10 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: Linus Walleij, linux-kernel, linux-usb

On Thu, Mar 07, 2013 at 04:57:40PM +0800, Fabio Baltieri wrote:
> Add transceiver notifier event handling to the ux500 driver to set vbus
> on specific transceiver events.
> 
> Acked-by: Linus Walleij <linus.walleij@linaro.org>
> Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
> ---

Sorry, I forgot about:

v2:
- turn off vbus on USB_EVENT_VBUS event

Note that this causes a trivial context conflict with patch 5, let me
know if you want me to resend the whole set.

Thanks,
Fabio

>  drivers/usb/musb/ux500.c | 42 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 42 insertions(+)
> 
> diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
> index 5b742ba..55f24c6 100644
> --- a/drivers/usb/musb/ux500.c
> +++ b/drivers/usb/musb/ux500.c
> @@ -98,6 +98,37 @@ static void ux500_musb_set_vbus(struct musb *musb, int is_on)
>  		musb_readb(musb->mregs, MUSB_DEVCTL));
>  }
>  
> +static int musb_otg_notifications(struct notifier_block *nb,
> +		unsigned long event, void *unused)
> +{
> +	struct musb *musb = container_of(nb, struct musb, nb);
> +
> +	dev_dbg(musb->controller, "musb_otg_notifications %ld %s\n",
> +			event, otg_state_string(musb->xceiv->state));
> +
> +	switch (event) {
> +	case USB_EVENT_ID:
> +		dev_dbg(musb->controller, "ID GND\n");
> +		ux500_musb_set_vbus(musb, 1);
> +		break;
> +	case USB_EVENT_VBUS:
> +		dev_dbg(musb->controller, "VBUS Connect\n");
> +		ux500_musb_set_vbus(musb, 0);
> +		break;
> +	case USB_EVENT_NONE:
> +		dev_dbg(musb->controller, "VBUS Disconnect\n");
> +		if (is_host_active(musb))
> +			ux500_musb_set_vbus(musb, 0);
> +		else
> +			musb->xceiv->state = OTG_STATE_B_IDLE;
> +		break;
> +	default:
> +		dev_dbg(musb->controller, "ID float\n");
> +		return NOTIFY_DONE;
> +	}
> +	return NOTIFY_OK;
> +}
> +
>  static irqreturn_t ux500_musb_interrupt(int irq, void *__hci)
>  {
>  	unsigned long   flags;
> @@ -120,12 +151,21 @@ static irqreturn_t ux500_musb_interrupt(int irq, void *__hci)
>  
>  static int ux500_musb_init(struct musb *musb)
>  {
> +	int status;
> +
>  	musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
>  	if (IS_ERR_OR_NULL(musb->xceiv)) {
>  		pr_err("HS USB OTG: no transceiver configured\n");
>  		return -EPROBE_DEFER;
>  	}
>  
> +	musb->nb.notifier_call = musb_otg_notifications;
> +	status = usb_register_notifier(musb->xceiv, &musb->nb);
> +	if (status < 0) {
> +		dev_dbg(musb->controller, "notification register failed\n");
> +		return status;
> +	}
> +
>  	musb->isr = ux500_musb_interrupt;
>  
>  	return 0;
> @@ -133,6 +173,8 @@ static int ux500_musb_init(struct musb *musb)
>  
>  static int ux500_musb_exit(struct musb *musb)
>  {
> +	usb_unregister_notifier(musb->xceiv, &musb->nb);
> +
>  	usb_put_phy(musb->xceiv);
>  
>  	return 0;
> -- 
> 1.8.1.3
> 

-- 
Fabio Baltieri

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

* Re: [PATCH 3/5] usb: musb: ux500: add otg notifier support
  2013-03-07  9:10           ` Fabio Baltieri
@ 2013-03-07 10:44             ` Felipe Balbi
  0 siblings, 0 replies; 13+ messages in thread
From: Felipe Balbi @ 2013-03-07 10:44 UTC (permalink / raw)
  To: Fabio Baltieri; +Cc: Felipe Balbi, Linus Walleij, linux-kernel, linux-usb

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

On Thu, Mar 07, 2013 at 05:10:57PM +0800, Fabio Baltieri wrote:
> On Thu, Mar 07, 2013 at 04:57:40PM +0800, Fabio Baltieri wrote:
> > Add transceiver notifier event handling to the ux500 driver to set vbus
> > on specific transceiver events.
> > 
> > Acked-by: Linus Walleij <linus.walleij@linaro.org>
> > Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
> > ---
> 
> Sorry, I forgot about:
> 
> v2:
> - turn off vbus on USB_EVENT_VBUS event
> 
> Note that this causes a trivial context conflict with patch 5, let me
> know if you want me to resend the whole set.

please do, I rather you resend otherwise I could mis-solve the conflict
:-p

-- 
balbi

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

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

end of thread, other threads:[~2013-03-07 10:45 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-02-28 10:38 [PATCH 0/5] musb-ux500 and ab8500-usb updates Fabio Baltieri
2013-02-28 10:38 ` [PATCH 1/5] usb: musb: ux500_dma: add missing MEM resource check Fabio Baltieri
2013-02-28 10:38 ` [PATCH 2/5] usb: musb: ux500: implement musb_set_vbus Fabio Baltieri
2013-02-28 10:38 ` [PATCH 3/5] usb: musb: ux500: add otg notifier support Fabio Baltieri
2013-03-04 14:38   ` Felipe Balbi
2013-03-06  1:40     ` Fabio Baltieri
2013-03-06  8:54       ` Felipe Balbi
2013-03-07  1:58         ` Fabio Baltieri
2013-03-07  8:57         ` Fabio Baltieri
2013-03-07  9:10           ` Fabio Baltieri
2013-03-07 10:44             ` Felipe Balbi
2013-02-28 10:38 ` [PATCH 4/5] usb: otg: ab8500-usb: drop support for ab8500 pre v2.0 Fabio Baltieri
2013-02-28 10:38 ` [PATCH 5/5] usb: otg: ab8500-usb: update irq handling code Fabio Baltieri

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).