Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v8 08/22] mfd: omap-usb-tll: Fix error message
From: Roger Quadros @ 2013-01-18 12:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358511445-26656-1-git-send-email-rogerq@ti.com>

omap_enable/disable_tll() can fail if TLL device is not
initialized. It could be due to multiple reasons and not only
due to missing platform data.

Also make local variables static and use 'struct device *'
instead of 'struct platform_device *' for global reference.

Signed-off-by: Roger Quadros <rogerq@ti.com>
Reviewed-by: Felipe Balbi <balbi@ti.com>
---
 drivers/mfd/omap-usb-tll.c |   21 ++++++++++++---------
 1 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index 0e68c8a..81d2cac 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -109,8 +109,8 @@ struct usbtll_omap {
 
 /*-------------------------------------------------------------------------*/
 
-const char usbtll_driver_name[] = USBTLL_DRIVER_NAME;
-struct platform_device	*tll_pdev;
+static const char usbtll_driver_name[] = USBTLL_DRIVER_NAME;
+static struct device	*tll_dev;
 
 /*-------------------------------------------------------------------------*/
 
@@ -337,7 +337,8 @@ static int usbtll_omap_probe(struct platform_device *pdev)
 
 	spin_unlock_irqrestore(&tll->lock, flags);
 	pm_runtime_put_sync(dev);
-	tll_pdev = pdev;
+	/* only after this can omap_tll_enable/disable work */
+	tll_dev = dev;
 
 	return 0;
 
@@ -359,6 +360,8 @@ static int usbtll_omap_remove(struct platform_device *pdev)
 	struct usbtll_omap *tll = platform_get_drvdata(pdev);
 	int i;
 
+	tll_dev = NULL;
+
 	for (i = 0; i < tll->nch; i++)
 		clk_put(tll->ch_clk[i]);
 
@@ -438,21 +441,21 @@ static struct platform_driver usbtll_omap_driver = {
 
 int omap_tll_enable(void)
 {
-	if (!tll_pdev) {
-		pr_err("missing omap usbhs tll platform_data\n");
+	if (!tll_dev) {
+		pr_err("%s: OMAP USB TLL not initialized\n", __func__);
 		return  -ENODEV;
 	}
-	return pm_runtime_get_sync(&tll_pdev->dev);
+	return pm_runtime_get_sync(tll_dev);
 }
 EXPORT_SYMBOL_GPL(omap_tll_enable);
 
 int omap_tll_disable(void)
 {
-	if (!tll_pdev) {
-		pr_err("missing omap usbhs tll platform_data\n");
+	if (!tll_dev) {
+		pr_err("%s: OMAP USB TLL not initialized\n", __func__);
 		return  -ENODEV;
 	}
-	return pm_runtime_put_sync(&tll_pdev->dev);
+	return pm_runtime_put_sync(tll_dev);
 }
 EXPORT_SYMBOL_GPL(omap_tll_disable);
 
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH v8 07/22] mfd: omap-usb-tll: Check for missing platform data in probe
From: Roger Quadros @ 2013-01-18 12:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358511445-26656-1-git-send-email-rogerq@ti.com>

No need to check for missing platform data in runtime_suspend/resume
as it makes more sense to do it in the probe function.

Signed-off-by: Roger Quadros <rogerq@ti.com>
Reviewed-by: Felipe Balbi <balbi@ti.com>
---
 drivers/mfd/omap-usb-tll.c |   15 +++++----------
 1 files changed, 5 insertions(+), 10 deletions(-)

diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index 6a43391..0e68c8a 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -225,6 +225,11 @@ static int usbtll_omap_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	}
 
+	if (!pdata) {
+		dev_err(dev, "Platform data missing\n");
+		return -ENODEV;
+	}
+
 	spin_lock_init(&tll->lock);
 
 	tll->pdata = pdata;
@@ -370,11 +375,6 @@ static int usbtll_runtime_resume(struct device *dev)
 
 	dev_dbg(dev, "usbtll_runtime_resume\n");
 
-	if (!pdata) {
-		dev_dbg(dev, "missing platform_data\n");
-		return  -ENODEV;
-	}
-
 	spin_lock_irqsave(&tll->lock, flags);
 
 	for (i = 0; i < tll->nch; i++) {
@@ -406,11 +406,6 @@ static int usbtll_runtime_suspend(struct device *dev)
 
 	dev_dbg(dev, "usbtll_runtime_suspend\n");
 
-	if (!pdata) {
-		dev_dbg(dev, "missing platform_data\n");
-		return  -ENODEV;
-	}
-
 	spin_lock_irqsave(&tll->lock, flags);
 
 	for (i = 0; i < tll->nch; i++) {
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH v8 06/22] mfd: omap-usb-tll: introduce and use mode_needs_tll()
From: Roger Quadros @ 2013-01-18 12:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358511445-26656-1-git-send-email-rogerq@ti.com>

This is a handy macro to check if the port requires the
USB TLL module or not. Use it to Enable the TLL module and manage
the clocks.

Signed-off-by: Roger Quadros <rogerq@ti.com>
Reviewed-by: Felipe Balbi <balbi@ti.com>
---
 drivers/mfd/omap-usb-tll.c |   20 ++++++++++++--------
 1 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index 4ce134b..6a43391 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -95,6 +95,10 @@
 
 #define is_ehci_tll_mode(x)	(x == OMAP_EHCI_PORT_MODE_TLL)
 
+/* only PHY and UNUSED modes don't need TLL */
+#define omap_usb_mode_needs_tll(x)	((x != OMAP_USBHS_PORT_MODE_UNUSED) &&\
+					 (x != OMAP_EHCI_PORT_MODE_PHY))
+
 struct usbtll_omap {
 	int					nch;	/* num. of channels */
 	struct usbhs_omap_platform_data		*pdata;
@@ -211,6 +215,7 @@ static int usbtll_omap_probe(struct platform_device *pdev)
 	unsigned long				flags;
 	int					ret = 0;
 	int					i, ver;
+	bool needs_tll;
 
 	dev_dbg(dev, "starting TI HSUSB TLL Controller\n");
 
@@ -281,12 +286,11 @@ static int usbtll_omap_probe(struct platform_device *pdev)
 
 	spin_lock_irqsave(&tll->lock, flags);
 
-	if (is_ehci_tll_mode(pdata->port_mode[0]) ||
-	    is_ehci_tll_mode(pdata->port_mode[1]) ||
-	    is_ehci_tll_mode(pdata->port_mode[2]) ||
-	    is_ohci_port(pdata->port_mode[0]) ||
-	    is_ohci_port(pdata->port_mode[1]) ||
-	    is_ohci_port(pdata->port_mode[2])) {
+	needs_tll = false;
+	for (i = 0; i < tll->nch; i++)
+		needs_tll |= omap_usb_mode_needs_tll(pdata->port_mode[i]);
+
+	if (needs_tll) {
 
 		/* Program Common TLL register */
 		reg = usbtll_read(base, OMAP_TLL_SHARED_CONF);
@@ -374,7 +378,7 @@ static int usbtll_runtime_resume(struct device *dev)
 	spin_lock_irqsave(&tll->lock, flags);
 
 	for (i = 0; i < tll->nch; i++) {
-		if (is_ehci_tll_mode(pdata->port_mode[i])) {
+		if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {
 			int r;
 
 			if (!tll->ch_clk[i])
@@ -410,7 +414,7 @@ static int usbtll_runtime_suspend(struct device *dev)
 	spin_lock_irqsave(&tll->lock, flags);
 
 	for (i = 0; i < tll->nch; i++) {
-		if (is_ehci_tll_mode(pdata->port_mode[i])) {
+		if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {
 			if (tll->ch_clk[i])
 				clk_disable(tll->ch_clk[i]);
 		}
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH v8 05/22] mfd: omap-usb-tll: Clean up clock handling
From: Roger Quadros @ 2013-01-18 12:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358511445-26656-1-git-send-email-rogerq@ti.com>

Every channel has a functional clock that is similarly named.
It makes sense to use a for loop to manage these clocks as OMAPs
can come with up to 3 channels.

Dynamically allocate and get channel clocks depending on the
number of clocks avaiable on the platform.

Signed-off-by: Roger Quadros <rogerq@ti.com>
Reviewed-by: Felipe Balbi <balbi@ti.com>
---
 drivers/mfd/omap-usb-tll.c |   89 +++++++++++++++++++++++++++----------------
 1 files changed, 56 insertions(+), 33 deletions(-)

diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index 9a19cc7..4ce134b 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -96,10 +96,9 @@
 #define is_ehci_tll_mode(x)	(x == OMAP_EHCI_PORT_MODE_TLL)
 
 struct usbtll_omap {
-	struct clk				*usbtll_p1_fck;
-	struct clk				*usbtll_p2_fck;
 	int					nch;	/* num. of channels */
 	struct usbhs_omap_platform_data		*pdata;
+	struct clk				**ch_clk;
 	/* secure the register updates */
 	spinlock_t				lock;
 };
@@ -225,26 +224,12 @@ static int usbtll_omap_probe(struct platform_device *pdev)
 
 	tll->pdata = pdata;
 
-	tll->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk");
-	if (IS_ERR(tll->usbtll_p1_fck)) {
-		ret = PTR_ERR(tll->usbtll_p1_fck);
-		dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret);
-		return ret;
-	}
-
-	tll->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk");
-	if (IS_ERR(tll->usbtll_p2_fck)) {
-		ret = PTR_ERR(tll->usbtll_p2_fck);
-		dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret);
-		goto err_p2_fck;
-	}
-
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	base = devm_request_and_ioremap(dev, res);
 	if (!base) {
 		ret = -EADDRNOTAVAIL;
 		dev_err(dev, "Resource request/ioremap failed:%d\n", ret);
-		goto err_res;
+		return ret;
 	}
 
 	platform_set_drvdata(pdev, tll);
@@ -270,6 +255,32 @@ static int usbtll_omap_probe(struct platform_device *pdev)
 		break;
 	}
 
+	spin_unlock_irqrestore(&tll->lock, flags);
+
+	tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk * [tll->nch]),
+						GFP_KERNEL);
+	if (!tll->ch_clk) {
+		ret = -ENOMEM;
+		dev_err(dev, "Couldn't allocate memory for channel clocks\n");
+		goto err_clk_alloc;
+	}
+
+	for (i = 0; i < tll->nch; i++) {
+		char clkname[] = "usb_tll_hs_usb_chx_clk";
+		struct clk *fck;
+
+		snprintf(clkname, sizeof(clkname),
+					"usb_tll_hs_usb_ch%d_clk", i);
+		fck = clk_get(dev, clkname);
+
+		if (IS_ERR(fck))
+			dev_dbg(dev, "can't get clock : %s\n", clkname);
+		else
+			tll->ch_clk[i] = fck;
+	}
+
+	spin_lock_irqsave(&tll->lock, flags);
+
 	if (is_ehci_tll_mode(pdata->port_mode[0]) ||
 	    is_ehci_tll_mode(pdata->port_mode[1]) ||
 	    is_ehci_tll_mode(pdata->port_mode[2]) ||
@@ -321,11 +332,9 @@ static int usbtll_omap_probe(struct platform_device *pdev)
 
 	return 0;
 
-err_res:
-	clk_put(tll->usbtll_p2_fck);
-
-err_p2_fck:
-	clk_put(tll->usbtll_p1_fck);
+err_clk_alloc:
+	pm_runtime_put_sync(dev);
+	pm_runtime_disable(dev);
 
 	return ret;
 }
@@ -339,9 +348,11 @@ err_p2_fck:
 static int usbtll_omap_remove(struct platform_device *pdev)
 {
 	struct usbtll_omap *tll = platform_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < tll->nch; i++)
+		clk_put(tll->ch_clk[i]);
 
-	clk_put(tll->usbtll_p2_fck);
-	clk_put(tll->usbtll_p1_fck);
 	pm_runtime_disable(&pdev->dev);
 	return 0;
 }
@@ -351,6 +362,7 @@ static int usbtll_runtime_resume(struct device *dev)
 	struct usbtll_omap			*tll = dev_get_drvdata(dev);
 	struct usbhs_omap_platform_data		*pdata = tll->pdata;
 	unsigned long				flags;
+	int i;
 
 	dev_dbg(dev, "usbtll_runtime_resume\n");
 
@@ -361,11 +373,20 @@ static int usbtll_runtime_resume(struct device *dev)
 
 	spin_lock_irqsave(&tll->lock, flags);
 
-	if (is_ehci_tll_mode(pdata->port_mode[0]))
-		clk_enable(tll->usbtll_p1_fck);
+	for (i = 0; i < tll->nch; i++) {
+		if (is_ehci_tll_mode(pdata->port_mode[i])) {
+			int r;
 
-	if (is_ehci_tll_mode(pdata->port_mode[1]))
-		clk_enable(tll->usbtll_p2_fck);
+			if (!tll->ch_clk[i])
+				continue;
+
+			r = clk_enable(tll->ch_clk[i]);
+			if (r) {
+				dev_err(dev,
+				 "Error enabling ch %d clock: %d\n", i, r);
+			}
+		}
+	}
 
 	spin_unlock_irqrestore(&tll->lock, flags);
 
@@ -377,6 +398,7 @@ static int usbtll_runtime_suspend(struct device *dev)
 	struct usbtll_omap			*tll = dev_get_drvdata(dev);
 	struct usbhs_omap_platform_data		*pdata = tll->pdata;
 	unsigned long				flags;
+	int i;
 
 	dev_dbg(dev, "usbtll_runtime_suspend\n");
 
@@ -387,11 +409,12 @@ static int usbtll_runtime_suspend(struct device *dev)
 
 	spin_lock_irqsave(&tll->lock, flags);
 
-	if (is_ehci_tll_mode(pdata->port_mode[0]))
-		clk_disable(tll->usbtll_p1_fck);
-
-	if (is_ehci_tll_mode(pdata->port_mode[1]))
-		clk_disable(tll->usbtll_p2_fck);
+	for (i = 0; i < tll->nch; i++) {
+		if (is_ehci_tll_mode(pdata->port_mode[i])) {
+			if (tll->ch_clk[i])
+				clk_disable(tll->ch_clk[i]);
+		}
+	}
 
 	spin_unlock_irqrestore(&tll->lock, flags);
 
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH v8 04/22] mfd: omap-usb-tll: Use devm_kzalloc/ioremap and clean up error path
From: Roger Quadros @ 2013-01-18 12:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358511445-26656-1-git-send-email-rogerq@ti.com>

Use devm_ variants of kzalloc() and ioremap(). Simplify the error path.

Signed-off-by: Roger Quadros <rogerq@ti.com>
Reviewed-by: Felipe Balbi <balbi@ti.com>
---
 drivers/mfd/omap-usb-tll.c |   38 ++++++++++++--------------------------
 1 files changed, 12 insertions(+), 26 deletions(-)

diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index 9658e18..9a19cc7 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -215,11 +215,10 @@ static int usbtll_omap_probe(struct platform_device *pdev)
 
 	dev_dbg(dev, "starting TI HSUSB TLL Controller\n");
 
-	tll = kzalloc(sizeof(struct usbtll_omap), GFP_KERNEL);
+	tll = devm_kzalloc(dev, sizeof(struct usbtll_omap), GFP_KERNEL);
 	if (!tll) {
 		dev_err(dev, "Memory allocation failed\n");
-		ret = -ENOMEM;
-		goto end;
+		return -ENOMEM;
 	}
 
 	spin_lock_init(&tll->lock);
@@ -230,28 +229,22 @@ static int usbtll_omap_probe(struct platform_device *pdev)
 	if (IS_ERR(tll->usbtll_p1_fck)) {
 		ret = PTR_ERR(tll->usbtll_p1_fck);
 		dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret);
-		goto err_tll;
+		return ret;
 	}
 
 	tll->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk");
 	if (IS_ERR(tll->usbtll_p2_fck)) {
 		ret = PTR_ERR(tll->usbtll_p2_fck);
 		dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret);
-		goto err_usbtll_p1_fck;
+		goto err_p2_fck;
 	}
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res) {
-		dev_err(dev, "usb tll get resource failed\n");
-		ret = -ENODEV;
-		goto err_usbtll_p2_fck;
-	}
-
-	base = ioremap(res->start, resource_size(res));
+	base = devm_request_and_ioremap(dev, res);
 	if (!base) {
-		dev_err(dev, "TLL ioremap failed\n");
-		ret = -ENOMEM;
-		goto err_usbtll_p2_fck;
+		ret = -EADDRNOTAVAIL;
+		dev_err(dev, "Resource request/ioremap failed:%d\n", ret);
+		goto err_res;
 	}
 
 	platform_set_drvdata(pdev, tll);
@@ -323,23 +316,17 @@ static int usbtll_omap_probe(struct platform_device *pdev)
 	}
 
 	spin_unlock_irqrestore(&tll->lock, flags);
-	iounmap(base);
 	pm_runtime_put_sync(dev);
 	tll_pdev = pdev;
-	if (!ret)
-		goto end;
-	pm_runtime_disable(dev);
 
-err_usbtll_p2_fck:
+	return 0;
+
+err_res:
 	clk_put(tll->usbtll_p2_fck);
 
-err_usbtll_p1_fck:
+err_p2_fck:
 	clk_put(tll->usbtll_p1_fck);
 
-err_tll:
-	kfree(tll);
-
-end:
 	return ret;
 }
 
@@ -356,7 +343,6 @@ static int usbtll_omap_remove(struct platform_device *pdev)
 	clk_put(tll->usbtll_p2_fck);
 	clk_put(tll->usbtll_p1_fck);
 	pm_runtime_disable(&pdev->dev);
-	kfree(tll);
 	return 0;
 }
 
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH v8 03/22] mfd: omap-usb-tll: Fix channel count detection
From: Roger Quadros @ 2013-01-18 12:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358511445-26656-1-git-send-email-rogerq@ti.com>

Fix channel count detecion for REV2. Also, don't give up
if we don't recognize the IP Revision. We assume the default
number of channels (i.e. 3) for unrecognized IPs.

Signed-off-by: Roger Quadros <rogerq@ti.com>
Reviewed-by: Felipe Balbi <balbi@ti.com>
---
 drivers/mfd/omap-usb-tll.c |   20 +++++++++++---------
 1 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index e459489..9658e18 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -98,6 +98,7 @@
 struct usbtll_omap {
 	struct clk				*usbtll_p1_fck;
 	struct clk				*usbtll_p2_fck;
+	int					nch;	/* num. of channels */
 	struct usbhs_omap_platform_data		*pdata;
 	/* secure the register updates */
 	spinlock_t				lock;
@@ -210,7 +211,7 @@ static int usbtll_omap_probe(struct platform_device *pdev)
 	unsigned				reg;
 	unsigned long				flags;
 	int					ret = 0;
-	int					i, ver, count;
+	int					i, ver;
 
 	dev_dbg(dev, "starting TI HSUSB TLL Controller\n");
 
@@ -262,16 +263,18 @@ static int usbtll_omap_probe(struct platform_device *pdev)
 	ver =  usbtll_read(base, OMAP_USBTLL_REVISION);
 	switch (ver) {
 	case OMAP_USBTLL_REV1:
-	case OMAP_USBTLL_REV2:
-		count = OMAP_TLL_CHANNEL_COUNT;
+		tll->nch = OMAP_TLL_CHANNEL_COUNT;
 		break;
+	case OMAP_USBTLL_REV2:
 	case OMAP_USBTLL_REV3:
-		count = OMAP_REV2_TLL_CHANNEL_COUNT;
+		tll->nch = OMAP_REV2_TLL_CHANNEL_COUNT;
 		break;
 	default:
-		dev_err(dev, "TLL version failed\n");
-		ret = -ENODEV;
-		goto err_ioremap;
+		tll->nch = OMAP_TLL_CHANNEL_COUNT;
+		dev_dbg(dev,
+		 "USB TLL Rev : 0x%x not recognized, assuming %d channels\n",
+			ver, tll->nch);
+		break;
 	}
 
 	if (is_ehci_tll_mode(pdata->port_mode[0]) ||
@@ -291,7 +294,7 @@ static int usbtll_omap_probe(struct platform_device *pdev)
 		usbtll_write(base, OMAP_TLL_SHARED_CONF, reg);
 
 		/* Enable channels now */
-		for (i = 0; i < count; i++) {
+		for (i = 0; i < tll->nch; i++) {
 			reg = usbtll_read(base,	OMAP_TLL_CHANNEL_CONF(i));
 
 			if (is_ohci_port(pdata->port_mode[i])) {
@@ -319,7 +322,6 @@ static int usbtll_omap_probe(struct platform_device *pdev)
 		}
 	}
 
-err_ioremap:
 	spin_unlock_irqrestore(&tll->lock, flags);
 	iounmap(base);
 	pm_runtime_put_sync(dev);
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH v8 02/22] mfd: omap-usb-host: Consolidate OMAP USB-HS platform data
From: Roger Quadros @ 2013-01-18 12:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358511445-26656-1-git-send-email-rogerq@ti.com>

Let's have a single platform data structure for the OMAP's High-Speed
USB host subsystem instead of having 3 separate ones i.e. one for
board data, one for USB Host (UHH) module and one for USB-TLL module.

This makes the code much simpler and avoids creating multiple copies of
platform data.

CC: Alan Stern <stern@rowland.harvard.edu>

Signed-off-by: Roger Quadros <rogerq@ti.com>
Reviewed-by: Felipe Balbi <balbi@ti.com>
For the ehci-omap.c part:
Acked-by: Alan Stern <stern@rowland.harvard.edu>
---
 arch/arm/mach-omap2/board-3430sdp.c        |    2 +-
 arch/arm/mach-omap2/board-3630sdp.c        |    2 +-
 arch/arm/mach-omap2/board-am3517crane.c    |    2 +-
 arch/arm/mach-omap2/board-am3517evm.c      |    2 +-
 arch/arm/mach-omap2/board-cm-t35.c         |    2 +-
 arch/arm/mach-omap2/board-cm-t3517.c       |    2 +-
 arch/arm/mach-omap2/board-devkit8000.c     |    2 +-
 arch/arm/mach-omap2/board-igep0020.c       |    4 +-
 arch/arm/mach-omap2/board-omap3beagle.c    |    2 +-
 arch/arm/mach-omap2/board-omap3evm.c       |    2 +-
 arch/arm/mach-omap2/board-omap3pandora.c   |    2 +-
 arch/arm/mach-omap2/board-omap3stalker.c   |    2 +-
 arch/arm/mach-omap2/board-omap3touchbook.c |    2 +-
 arch/arm/mach-omap2/board-omap4panda.c     |    2 +-
 arch/arm/mach-omap2/board-overo.c          |    2 +-
 arch/arm/mach-omap2/board-zoom.c           |    2 +-
 arch/arm/mach-omap2/usb-host.c             |   29 ++-----------
 arch/arm/mach-omap2/usb.h                  |   20 +--------
 drivers/mfd/omap-usb-host.c                |   63 +++++++++++----------------
 drivers/mfd/omap-usb-tll.c                 |   11 ++---
 drivers/usb/host/ehci-omap.c               |    6 +-
 include/linux/platform_data/usb-omap.h     |   23 ++--------
 22 files changed, 61 insertions(+), 125 deletions(-)

diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c
index bb73afc..46147c8 100644
--- a/arch/arm/mach-omap2/board-3430sdp.c
+++ b/arch/arm/mach-omap2/board-3430sdp.c
@@ -424,7 +424,7 @@ static void enable_board_wakeup_source(void)
 		OMAP_WAKEUP_EN | OMAP_PIN_INPUT_PULLUP);
 }
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 
 	.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
 	.port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
diff --git a/arch/arm/mach-omap2/board-3630sdp.c b/arch/arm/mach-omap2/board-3630sdp.c
index 050aaa7..78b1724 100644
--- a/arch/arm/mach-omap2/board-3630sdp.c
+++ b/arch/arm/mach-omap2/board-3630sdp.c
@@ -53,7 +53,7 @@ static void enable_board_wakeup_source(void)
 		OMAP_WAKEUP_EN | OMAP_PIN_INPUT_PULLUP);
 }
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 
 	.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
 	.port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
diff --git a/arch/arm/mach-omap2/board-am3517crane.c b/arch/arm/mach-omap2/board-am3517crane.c
index 51b96a1..26f1916 100644
--- a/arch/arm/mach-omap2/board-am3517crane.c
+++ b/arch/arm/mach-omap2/board-am3517crane.c
@@ -40,7 +40,7 @@ static struct omap_board_mux board_mux[] __initdata = {
 };
 #endif
 
-static struct usbhs_omap_board_data usbhs_bdata __initdata = {
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 	.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
 	.port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED,
 	.port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
diff --git a/arch/arm/mach-omap2/board-am3517evm.c b/arch/arm/mach-omap2/board-am3517evm.c
index f81a303..c76725d 100644
--- a/arch/arm/mach-omap2/board-am3517evm.c
+++ b/arch/arm/mach-omap2/board-am3517evm.c
@@ -274,7 +274,7 @@ static __init void am3517_evm_mcbsp1_init(void)
 	omap_ctrl_writel(devconf0, OMAP2_CONTROL_DEVCONF0);
 }
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 	.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
 #if defined(CONFIG_PANEL_SHARP_LQ043T1DG01) || \
 		defined(CONFIG_PANEL_SHARP_LQ043T1DG01_MODULE)
diff --git a/arch/arm/mach-omap2/board-cm-t35.c b/arch/arm/mach-omap2/board-cm-t35.c
index b3102c2..cdf1d6e 100644
--- a/arch/arm/mach-omap2/board-cm-t35.c
+++ b/arch/arm/mach-omap2/board-cm-t35.c
@@ -418,7 +418,7 @@ static struct omap2_hsmmc_info mmc[] = {
 	{}	/* Terminator */
 };
 
-static struct usbhs_omap_board_data usbhs_bdata __initdata = {
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 	.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
 	.port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
 	.port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
diff --git a/arch/arm/mach-omap2/board-cm-t3517.c b/arch/arm/mach-omap2/board-cm-t3517.c
index ebbc2ad..cfa9098 100644
--- a/arch/arm/mach-omap2/board-cm-t3517.c
+++ b/arch/arm/mach-omap2/board-cm-t3517.c
@@ -166,7 +166,7 @@ static inline void cm_t3517_init_rtc(void) {}
 #define HSUSB2_RESET_GPIO	(147)
 #define USB_HUB_RESET_GPIO	(152)
 
-static struct usbhs_omap_board_data cm_t3517_ehci_pdata __initdata = {
+static struct usbhs_omap_platform_data cm_t3517_ehci_pdata __initdata = {
 	.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
 	.port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
 	.port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
diff --git a/arch/arm/mach-omap2/board-devkit8000.c b/arch/arm/mach-omap2/board-devkit8000.c
index 12865af..051ec0d 100644
--- a/arch/arm/mach-omap2/board-devkit8000.c
+++ b/arch/arm/mach-omap2/board-devkit8000.c
@@ -435,7 +435,7 @@ static struct platform_device *devkit8000_devices[] __initdata = {
 	&omap_dm9000_dev,
 };
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 
 	.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
 	.port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED,
diff --git a/arch/arm/mach-omap2/board-igep0020.c b/arch/arm/mach-omap2/board-igep0020.c
index 0f24cb8..cfba790 100644
--- a/arch/arm/mach-omap2/board-igep0020.c
+++ b/arch/arm/mach-omap2/board-igep0020.c
@@ -526,7 +526,7 @@ static void __init igep_i2c_init(void)
 	omap3_pmic_init("twl4030", &igep_twldata);
 }
 
-static const struct usbhs_omap_board_data igep2_usbhs_bdata __initconst = {
+static struct usbhs_omap_platform_data igep2_usbhs_bdata __initdata = {
 	.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
 	.port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED,
 	.port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
@@ -537,7 +537,7 @@ static const struct usbhs_omap_board_data igep2_usbhs_bdata __initconst = {
 	.reset_gpio_port[2] = -EINVAL,
 };
 
-static const struct usbhs_omap_board_data igep3_usbhs_bdata __initconst = {
+static struct usbhs_omap_platform_data igep3_usbhs_bdata __initdata = {
 	.port_mode[0] = OMAP_USBHS_PORT_MODE_UNUSED,
 	.port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
 	.port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c
index 22c483d..1cb114e 100644
--- a/arch/arm/mach-omap2/board-omap3beagle.c
+++ b/arch/arm/mach-omap2/board-omap3beagle.c
@@ -430,7 +430,7 @@ static struct platform_device *omap3_beagle_devices[] __initdata = {
 	&madc_hwmon,
 };
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 
 	.port_mode[0] = OMAP_USBHS_PORT_MODE_UNUSED,
 	.port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c
index 3985f35..7bdc8a4 100644
--- a/arch/arm/mach-omap2/board-omap3evm.c
+++ b/arch/arm/mach-omap2/board-omap3evm.c
@@ -538,7 +538,7 @@ static int __init omap3_evm_i2c_init(void)
 	return 0;
 }
 
-static struct usbhs_omap_board_data usbhs_bdata __initdata = {
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 
 	.port_mode[0] = OMAP_USBHS_PORT_MODE_UNUSED,
 	.port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c
index a53a668..145a6f8 100644
--- a/arch/arm/mach-omap2/board-omap3pandora.c
+++ b/arch/arm/mach-omap2/board-omap3pandora.c
@@ -567,7 +567,7 @@ static struct platform_device *omap3pandora_devices[] __initdata = {
 	&pandora_backlight,
 };
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 
 	.port_mode[0] = OMAP_USBHS_PORT_MODE_UNUSED,
 	.port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
diff --git a/arch/arm/mach-omap2/board-omap3stalker.c b/arch/arm/mach-omap2/board-omap3stalker.c
index 53a6cbc..278ae95 100644
--- a/arch/arm/mach-omap2/board-omap3stalker.c
+++ b/arch/arm/mach-omap2/board-omap3stalker.c
@@ -361,7 +361,7 @@ static struct platform_device *omap3_stalker_devices[] __initdata = {
 	&keys_gpio,
 };
 
-static struct usbhs_omap_board_data usbhs_bdata __initconst = {
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 	.port_mode[0] = OMAP_USBHS_PORT_MODE_UNUSED,
 	.port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
 	.port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
diff --git a/arch/arm/mach-omap2/board-omap3touchbook.c b/arch/arm/mach-omap2/board-omap3touchbook.c
index 263cb9c..65a285f 100644
--- a/arch/arm/mach-omap2/board-omap3touchbook.c
+++ b/arch/arm/mach-omap2/board-omap3touchbook.c
@@ -309,7 +309,7 @@ static struct platform_device *omap3_touchbook_devices[] __initdata = {
 	&keys_gpio,
 };
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 
 	.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
 	.port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c
index 5c8e9ce..ee76830 100644
--- a/arch/arm/mach-omap2/board-omap4panda.c
+++ b/arch/arm/mach-omap2/board-omap4panda.c
@@ -139,7 +139,7 @@ static struct platform_device *panda_devices[] __initdata = {
 	&btwilink_device,
 };
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 	.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
 	.port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED,
 	.port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c
index c8fde3e..b1b0f09 100644
--- a/arch/arm/mach-omap2/board-overo.c
+++ b/arch/arm/mach-omap2/board-overo.c
@@ -457,7 +457,7 @@ static int __init overo_spi_init(void)
 	return 0;
 }
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 	.port_mode[0] = OMAP_USBHS_PORT_MODE_UNUSED,
 	.port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
 	.port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
diff --git a/arch/arm/mach-omap2/board-zoom.c b/arch/arm/mach-omap2/board-zoom.c
index d7fa31e..2d7a457 100644
--- a/arch/arm/mach-omap2/board-zoom.c
+++ b/arch/arm/mach-omap2/board-zoom.c
@@ -92,7 +92,7 @@ static struct mtd_partition zoom_nand_partitions[] = {
 	},
 };
 
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+static struct usbhs_omap_platform_data usbhs_bdata __initdata = {
 	.port_mode[0]		= OMAP_USBHS_PORT_MODE_UNUSED,
 	.port_mode[1]		= OMAP_EHCI_PORT_MODE_PHY,
 	.port_mode[2]		= OMAP_USBHS_PORT_MODE_UNUSED,
diff --git a/arch/arm/mach-omap2/usb-host.c b/arch/arm/mach-omap2/usb-host.c
index 2e44e8a..940aad4 100644
--- a/arch/arm/mach-omap2/usb-host.c
+++ b/arch/arm/mach-omap2/usb-host.c
@@ -37,11 +37,6 @@
 #define	USBHS_UHH_HWMODNAME	"usb_host_hs"
 #define USBHS_TLL_HWMODNAME	"usb_tll_hs"
 
-static struct usbhs_omap_platform_data		usbhs_data;
-static struct usbtll_omap_platform_data		usbtll_data;
-static struct ehci_hcd_omap_platform_data	ehci_data;
-static struct ohci_hcd_omap_platform_data	ohci_data;
-
 static struct omap_device_pm_latency omap_uhhtll_latency[] = {
 	  {
 		.deactivate_func = omap_device_idle_hwmods,
@@ -485,32 +480,18 @@ void __init setup_4430ohci_io_mux(const enum usbhs_omap_port_mode *port_mode)
 	}
 }
 
-void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
+void __init usbhs_init(struct usbhs_omap_platform_data *pdata)
 {
 	struct omap_hwmod	*uhh_hwm, *tll_hwm;
 	struct platform_device	*pdev;
 	int			bus_id = -1;
-	int			i;
-
-	for (i = 0; i < OMAP3_HS_USB_PORTS; i++) {
-		usbhs_data.port_mode[i] = pdata->port_mode[i];
-		usbtll_data.port_mode[i] = pdata->port_mode[i];
-		ohci_data.port_mode[i] = pdata->port_mode[i];
-		ehci_data.port_mode[i] = pdata->port_mode[i];
-		ehci_data.reset_gpio_port[i] = pdata->reset_gpio_port[i];
-		ehci_data.regulator[i] = pdata->regulator[i];
-	}
-	ehci_data.phy_reset = pdata->phy_reset;
-	ohci_data.es2_compatibility = pdata->es2_compatibility;
-	usbhs_data.ehci_data = &ehci_data;
-	usbhs_data.ohci_data = &ohci_data;
 
 	if (cpu_is_omap34xx()) {
 		setup_ehci_io_mux(pdata->port_mode);
 		setup_ohci_io_mux(pdata->port_mode);
 
 		if (omap_rev() <= OMAP3430_REV_ES2_1)
-			usbhs_data.single_ulpi_bypass = true;
+			pdata->single_ulpi_bypass = true;
 
 	} else if (cpu_is_omap44xx()) {
 		setup_4430ehci_io_mux(pdata->port_mode);
@@ -530,7 +511,7 @@ void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
 	}
 
 	pdev = omap_device_build(OMAP_USBTLL_DEVICE, bus_id, tll_hwm,
-				&usbtll_data, sizeof(usbtll_data),
+				pdata, sizeof(*pdata),
 				omap_uhhtll_latency,
 				ARRAY_SIZE(omap_uhhtll_latency), false);
 	if (IS_ERR(pdev)) {
@@ -540,7 +521,7 @@ void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
 	}
 
 	pdev = omap_device_build(OMAP_USBHS_DEVICE, bus_id, uhh_hwm,
-				&usbhs_data, sizeof(usbhs_data),
+				pdata, sizeof(*pdata),
 				omap_uhhtll_latency,
 				ARRAY_SIZE(omap_uhhtll_latency), false);
 	if (IS_ERR(pdev)) {
@@ -552,7 +533,7 @@ void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
 
 #else
 
-void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
+void __init usbhs_init(struct usbhs_omap_platform_data *pdata)
 {
 }
 
diff --git a/arch/arm/mach-omap2/usb.h b/arch/arm/mach-omap2/usb.h
index 9b986ea..3319f5c 100644
--- a/arch/arm/mach-omap2/usb.h
+++ b/arch/arm/mach-omap2/usb.h
@@ -53,26 +53,8 @@
 #define USBPHY_OTGSESSEND_EN	(1 << 20)
 #define USBPHY_DATA_POLARITY	(1 << 23)
 
-struct usbhs_omap_board_data {
-	enum usbhs_omap_port_mode	port_mode[OMAP3_HS_USB_PORTS];
-
-	/* have to be valid if phy_reset is true and portx is in phy mode */
-	int	reset_gpio_port[OMAP3_HS_USB_PORTS];
-
-	/* Set this to true for ES2.x silicon */
-	unsigned			es2_compatibility:1;
-
-	unsigned			phy_reset:1;
-
-	/*
-	 * Regulators for USB PHYs.
-	 * Each PHY can have a separate regulator.
-	 */
-	struct regulator		*regulator[OMAP3_HS_USB_PORTS];
-};
-
 extern void usb_musb_init(struct omap_musb_board_data *board_data);
-extern void usbhs_init(const struct usbhs_omap_board_data *pdata);
+extern void usbhs_init(struct usbhs_omap_platform_data *pdata);
 
 extern void am35x_musb_reset(void);
 extern void am35x_musb_phy_power(u8 on);
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 05164d7..d6e6b8c 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -102,7 +102,7 @@ struct usbhs_hcd_omap {
 
 	void __iomem			*uhh_base;
 
-	struct usbhs_omap_platform_data	platdata;
+	struct usbhs_omap_platform_data	*pdata;
 
 	u32				usbhs_rev;
 	spinlock_t			lock;
@@ -184,19 +184,13 @@ err_end:
 static int omap_usbhs_alloc_children(struct platform_device *pdev)
 {
 	struct device				*dev = &pdev->dev;
-	struct usbhs_hcd_omap			*omap;
-	struct ehci_hcd_omap_platform_data	*ehci_data;
-	struct ohci_hcd_omap_platform_data	*ohci_data;
+	struct usbhs_omap_platform_data		*pdata = dev->platform_data;
 	struct platform_device			*ehci;
 	struct platform_device			*ohci;
 	struct resource				*res;
 	struct resource				resources[2];
 	int					ret;
 
-	omap = platform_get_drvdata(pdev);
-	ehci_data = omap->platdata.ehci_data;
-	ohci_data = omap->platdata.ohci_data;
-
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehci");
 	if (!res) {
 		dev_err(dev, "EHCI get resource IORESOURCE_MEM failed\n");
@@ -213,8 +207,8 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev)
 	}
 	resources[1] = *res;
 
-	ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, ehci_data,
-		sizeof(*ehci_data), dev);
+	ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, pdata,
+		sizeof(*pdata), dev);
 
 	if (!ehci) {
 		dev_err(dev, "omap_usbhs_alloc_child failed\n");
@@ -238,8 +232,8 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev)
 	}
 	resources[1] = *res;
 
-	ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, ohci_data,
-		sizeof(*ohci_data), dev);
+	ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, pdata,
+		sizeof(*pdata), dev);
 	if (!ohci) {
 		dev_err(dev, "omap_usbhs_alloc_child failed\n");
 		ret = -ENOMEM;
@@ -278,7 +272,7 @@ static bool is_ohci_port(enum usbhs_omap_port_mode pmode)
 static int usbhs_runtime_resume(struct device *dev)
 {
 	struct usbhs_hcd_omap		*omap = dev_get_drvdata(dev);
-	struct usbhs_omap_platform_data	*pdata = &omap->platdata;
+	struct usbhs_omap_platform_data	*pdata = omap->pdata;
 	unsigned long			flags;
 
 	dev_dbg(dev, "usbhs_runtime_resume\n");
@@ -310,7 +304,7 @@ static int usbhs_runtime_resume(struct device *dev)
 static int usbhs_runtime_suspend(struct device *dev)
 {
 	struct usbhs_hcd_omap		*omap = dev_get_drvdata(dev);
-	struct usbhs_omap_platform_data	*pdata = &omap->platdata;
+	struct usbhs_omap_platform_data	*pdata = omap->pdata;
 	unsigned long			flags;
 
 	dev_dbg(dev, "usbhs_runtime_suspend\n");
@@ -342,19 +336,19 @@ static int usbhs_runtime_suspend(struct device *dev)
 static void omap_usbhs_init(struct device *dev)
 {
 	struct usbhs_hcd_omap		*omap = dev_get_drvdata(dev);
-	struct usbhs_omap_platform_data	*pdata = &omap->platdata;
+	struct usbhs_omap_platform_data	*pdata = omap->pdata;
 	unsigned long			flags;
 	unsigned			reg;
 
 	dev_dbg(dev, "starting TI HSUSB Controller\n");
 
-	if (pdata->ehci_data->phy_reset) {
-		if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
-			gpio_request_one(pdata->ehci_data->reset_gpio_port[0],
+	if (pdata->phy_reset) {
+		if (gpio_is_valid(pdata->reset_gpio_port[0]))
+			gpio_request_one(pdata->reset_gpio_port[0],
 					 GPIOF_OUT_INIT_LOW, "USB1 PHY reset");
 
-		if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
-			gpio_request_one(pdata->ehci_data->reset_gpio_port[1],
+		if (gpio_is_valid(pdata->reset_gpio_port[1]))
+			gpio_request_one(pdata->reset_gpio_port[1],
 					 GPIOF_OUT_INIT_LOW, "USB2 PHY reset");
 
 		/* Hold the PHY in RESET for enough time till DIR is high */
@@ -430,33 +424,33 @@ static void omap_usbhs_init(struct device *dev)
 	spin_unlock_irqrestore(&omap->lock, flags);
 
 	pm_runtime_put_sync(dev);
-	if (pdata->ehci_data->phy_reset) {
+	if (pdata->phy_reset) {
 		/* Hold the PHY in RESET for enough time till
 		 * PHY is settled and ready
 		 */
 		udelay(10);
 
-		if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
+		if (gpio_is_valid(pdata->reset_gpio_port[0]))
 			gpio_set_value_cansleep
-				(pdata->ehci_data->reset_gpio_port[0], 1);
+				(pdata->reset_gpio_port[0], 1);
 
-		if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
+		if (gpio_is_valid(pdata->reset_gpio_port[1]))
 			gpio_set_value_cansleep
-				(pdata->ehci_data->reset_gpio_port[1], 1);
+				(pdata->reset_gpio_port[1], 1);
 	}
 }
 
 static void omap_usbhs_deinit(struct device *dev)
 {
 	struct usbhs_hcd_omap		*omap = dev_get_drvdata(dev);
-	struct usbhs_omap_platform_data	*pdata = &omap->platdata;
+	struct usbhs_omap_platform_data	*pdata = omap->pdata;
 
-	if (pdata->ehci_data->phy_reset) {
-		if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
-			gpio_free(pdata->ehci_data->reset_gpio_port[0]);
+	if (pdata->phy_reset) {
+		if (gpio_is_valid(pdata->reset_gpio_port[0]))
+			gpio_free(pdata->reset_gpio_port[0]);
 
-		if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
-			gpio_free(pdata->ehci_data->reset_gpio_port[1]);
+		if (gpio_is_valid(pdata->reset_gpio_port[1]))
+			gpio_free(pdata->reset_gpio_port[1]);
 	}
 }
 
@@ -490,15 +484,10 @@ static int usbhs_omap_probe(struct platform_device *pdev)
 
 	spin_lock_init(&omap->lock);
 
-	for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
-		omap->platdata.port_mode[i] = pdata->port_mode[i];
-
-	omap->platdata.ehci_data = pdata->ehci_data;
-	omap->platdata.ohci_data = pdata->ohci_data;
+	omap->pdata = pdata;
 
 	pm_runtime_enable(dev);
 
-
 	for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
 		if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) ||
 			is_ehci_hsic_mode(i)) {
diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index eb86915..e459489 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -98,7 +98,7 @@
 struct usbtll_omap {
 	struct clk				*usbtll_p1_fck;
 	struct clk				*usbtll_p2_fck;
-	struct usbtll_omap_platform_data	platdata;
+	struct usbhs_omap_platform_data		*pdata;
 	/* secure the register updates */
 	spinlock_t				lock;
 };
@@ -203,7 +203,7 @@ static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode)
 static int usbtll_omap_probe(struct platform_device *pdev)
 {
 	struct device				*dev =  &pdev->dev;
-	struct usbtll_omap_platform_data	*pdata = dev->platform_data;
+	struct usbhs_omap_platform_data		*pdata = dev->platform_data;
 	void __iomem				*base;
 	struct resource				*res;
 	struct usbtll_omap			*tll;
@@ -223,8 +223,7 @@ static int usbtll_omap_probe(struct platform_device *pdev)
 
 	spin_lock_init(&tll->lock);
 
-	for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
-		tll->platdata.port_mode[i] = pdata->port_mode[i];
+	tll->pdata = pdata;
 
 	tll->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk");
 	if (IS_ERR(tll->usbtll_p1_fck)) {
@@ -362,7 +361,7 @@ static int usbtll_omap_remove(struct platform_device *pdev)
 static int usbtll_runtime_resume(struct device *dev)
 {
 	struct usbtll_omap			*tll = dev_get_drvdata(dev);
-	struct usbtll_omap_platform_data	*pdata = &tll->platdata;
+	struct usbhs_omap_platform_data		*pdata = tll->pdata;
 	unsigned long				flags;
 
 	dev_dbg(dev, "usbtll_runtime_resume\n");
@@ -388,7 +387,7 @@ static int usbtll_runtime_resume(struct device *dev)
 static int usbtll_runtime_suspend(struct device *dev)
 {
 	struct usbtll_omap			*tll = dev_get_drvdata(dev);
-	struct usbtll_omap_platform_data	*pdata = &tll->platdata;
+	struct usbhs_omap_platform_data		*pdata = tll->pdata;
 	unsigned long				flags;
 
 	dev_dbg(dev, "usbtll_runtime_suspend\n");
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index e9d9b09..b96a4bf 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -107,7 +107,7 @@ static int omap_ehci_init(struct usb_hcd *hcd)
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);
 	int			rc;
-	struct ehci_hcd_omap_platform_data	*pdata;
+	struct usbhs_omap_platform_data	*pdata;
 
 	pdata = hcd->self.controller->platform_data;
 
@@ -151,7 +151,7 @@ static int omap_ehci_init(struct usb_hcd *hcd)
 }
 
 static void disable_put_regulator(
-		struct ehci_hcd_omap_platform_data *pdata)
+		struct usbhs_omap_platform_data *pdata)
 {
 	int i;
 
@@ -176,7 +176,7 @@ static void disable_put_regulator(
 static int ehci_hcd_omap_probe(struct platform_device *pdev)
 {
 	struct device				*dev = &pdev->dev;
-	struct ehci_hcd_omap_platform_data	*pdata = dev->platform_data;
+	struct usbhs_omap_platform_data		*pdata = dev->platform_data;
 	struct resource				*res;
 	struct usb_hcd				*hcd;
 	void __iomem				*regs;
diff --git a/include/linux/platform_data/usb-omap.h b/include/linux/platform_data/usb-omap.h
index ef65b67..04c7537 100644
--- a/include/linux/platform_data/usb-omap.h
+++ b/include/linux/platform_data/usb-omap.h
@@ -38,30 +38,15 @@ enum usbhs_omap_port_mode {
 	OMAP_OHCI_PORT_MODE_TLL_2PIN_DPDM
 };
 
-struct usbtll_omap_platform_data {
-	enum usbhs_omap_port_mode		port_mode[OMAP3_HS_USB_PORTS];
-};
-
-struct ehci_hcd_omap_platform_data {
+struct usbhs_omap_platform_data {
 	enum usbhs_omap_port_mode	port_mode[OMAP3_HS_USB_PORTS];
 	int				reset_gpio_port[OMAP3_HS_USB_PORTS];
 	struct regulator		*regulator[OMAP3_HS_USB_PORTS];
-	unsigned			phy_reset:1;
-};
-
-struct ohci_hcd_omap_platform_data {
-	enum usbhs_omap_port_mode	port_mode[OMAP3_HS_USB_PORTS];
-	unsigned			es2_compatibility:1;
-};
-
-struct usbhs_omap_platform_data {
-	enum usbhs_omap_port_mode		port_mode[OMAP3_HS_USB_PORTS];
-
-	struct ehci_hcd_omap_platform_data	*ehci_data;
-	struct ohci_hcd_omap_platform_data	*ohci_data;
 
 	/* OMAP3 <= ES2.1 have a single ulpi bypass control bit */
-	unsigned				single_ulpi_bypass:1;
+	unsigned			single_ulpi_bypass:1;
+	unsigned			es2_compatibility:1;
+	unsigned			phy_reset:1;
 };
 
 /*-------------------------------------------------------------------------*/
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH v8 01/22] USB: ehci-omap: Don't free gpios that we didn't request
From: Roger Quadros @ 2013-01-18 12:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358511445-26656-1-git-send-email-rogerq@ti.com>

This driver does not request any gpios so don't free them.
Fixes L3 bus error on multiple modprobe/rmmod of ehci_hcd
with ehci-omap in use.

Without this patch, EHCI will break on repeated insmod/rmmod
of ehci_hcd for all OMAP2+ platforms that use EHCI and
set 'phy_reset = true' in usbhs_omap_board_data.
i.e.

board-3430sdp.c:	.phy_reset  = true,
board-3630sdp.c:	.phy_reset  = true,
board-am3517crane.c:	.phy_reset  = true,
board-am3517evm.c:	.phy_reset  = true,
board-cm-t3517.c:	.phy_reset  = true,
board-cm-t35.c:	.phy_reset  = true,
board-devkit8000.c:	.phy_reset  = true,
board-igep0020.c:	.phy_reset = true,
board-igep0020.c:	.phy_reset = true,
board-omap3beagle.c:	.phy_reset  = true,
board-omap3evm.c:	.phy_reset  = true,
board-omap3pandora.c:	.phy_reset  = true,
board-omap3stalker.c:	.phy_reset = true,
board-omap3touchbook.c:	.phy_reset  = true,
board-omap4panda.c:	.phy_reset  = false,
board-overo.c:	.phy_reset  = true,
board-zoom.c:	.phy_reset		= true,

CC: Alan Stern <stern@rowland.harvard.edu>
Cc: stable at kernel.org

Signed-off-by: Roger Quadros <rogerq@ti.com>
Reviewed-by: Felipe Balbi <balbi@ti.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
---
 drivers/usb/host/ehci-omap.c |    8 --------
 1 files changed, 0 insertions(+), 8 deletions(-)

diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index ac17a7c..e9d9b09 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -288,7 +288,6 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev)
 {
 	struct device *dev				= &pdev->dev;
 	struct usb_hcd *hcd				= dev_get_drvdata(dev);
-	struct ehci_hcd_omap_platform_data *pdata	= dev->platform_data;
 
 	usb_remove_hcd(hcd);
 	disable_put_regulator(dev->platform_data);
@@ -298,13 +297,6 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev)
 	pm_runtime_put_sync(dev);
 	pm_runtime_disable(dev);
 
-	if (pdata->phy_reset) {
-		if (gpio_is_valid(pdata->reset_gpio_port[0]))
-			gpio_free(pdata->reset_gpio_port[0]);
-
-		if (gpio_is_valid(pdata->reset_gpio_port[1]))
-			gpio_free(pdata->reset_gpio_port[1]);
-	}
 	return 0;
 }
 
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH v8 00/22] OMAP USB Host cleanup
From: Roger Quadros @ 2013-01-18 12:17 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

This patchset addresses the following

- Consolidate USB Host platform data.
- Avoid addressing clocks one by one by name and use a for loop + bunch
  of cleanups.
- Get number of channels/ports dynamically either from revision register
  or from platform data. Avoids getting clocks that are not present.
- Add OMAP5 and HSIC mode (Not tested).

v8:
- Re-arranged patch "USB-ehci-omap-Don-t-free-gpios-that-we-didn-t-reques.patch"
  to be the first since it is a candidate for stable.
- Fixed issues raised by Felipe, i.e. indentation and use of switch ()

v7:
- Updated patch 4 to not hold a spinlock when using clk_get().
- Updated patches 3 and 11 to return -EADDRNOTAVAIL on failure of
  demv_request_and_ioremap().

v6:
- Added USB Host platform data consolidation patch as the first patch.
  based on request from Tony.
- Rebased on v3.8-rc3.

v5:
- Rebased on top of todays arm-soc/for-next.
- Removed the clock merging patch from the list.
- Updated patches 14, 19 and 20 to accomodate the above change.
- Added patch 22 to fix a build warning.

v4:
- Added appropriate maintainers in to/cc.
- minor print message fix in patch 23 to maintain consistency.

v3:
- Rebased on arm-soc/for-next commit f979306c4d38d213c6977aaf3b1115e8ded71e3a.
- Rearranged patch that get rids of cpu_is_omap..() macros.
- Coding style fixes.

v2:
- Clocks are allocated dynamically based on number of ports available
  on the platform.
- Reduced console spam if non critical clocks are not found on the platform.
- Get rid of cpu_is_.. macros from USB host driver.

cheers,
-roger

The following changes since commit 9931faca02c604c22335f5a935a501bb2ace6e20:

  Linux 3.8-rc3 (2013-01-09 18:59:55 -0800)

are available in the git repository at:
  git://github.com/rogerq/linux.git linux-usbhost13-part

Roger Quadros (22):
  USB: ehci-omap: Don't free gpios that we didn't request
  mfd: omap-usb-host: Consolidate OMAP USB-HS platform data
  mfd: omap-usb-tll: Fix channel count detection
  mfd: omap-usb-tll: Use devm_kzalloc/ioremap and clean up error path
  mfd: omap-usb-tll: Clean up clock handling
  mfd: omap-usb-tll: introduce and use mode_needs_tll()
  mfd: omap-usb-tll: Check for missing platform data in probe
  mfd: omap-usb-tll: Fix error message
  mfd: omap-usb-tll: serialize access to TLL device
  mfd: omap-usb-tll: Add OMAP5 revision and HSIC support
  mfd: omap_usb_host: Avoid missing platform data checks in
    suspend/resume
  mfd: omap-usb-host: Use devm_kzalloc() and devm_request_and_ioremap()
  mfd: omap-usb-host: know about number of ports from revision register
  mfd: omap-usb-host: override number of ports from platform data
  mfd: omap-usb-host: cleanup clock management code
  mfd: omap-usb-host: Manage HSIC clocks for HSIC mode
  mfd: omap-usb-host: Get rid of unnecessary spinlock
  mfd: omap-usb-host: clean up omap_usbhs_init()
  ARM: OMAP3: clock data: get rid of unused USB host clock aliases and
    dummies
  ARM: OMAP4: clock data: get rid of unused USB host clock aliases
  mfd: omap-usb-host: Don't spam console on clk_set_parent failure
  mdf: omap-usb-host: get rid of build warning

 arch/arm/mach-omap2/board-3430sdp.c        |    2 +-
 arch/arm/mach-omap2/board-3630sdp.c        |    2 +-
 arch/arm/mach-omap2/board-am3517crane.c    |    2 +-
 arch/arm/mach-omap2/board-am3517evm.c      |    2 +-
 arch/arm/mach-omap2/board-cm-t35.c         |    2 +-
 arch/arm/mach-omap2/board-cm-t3517.c       |    2 +-
 arch/arm/mach-omap2/board-devkit8000.c     |    2 +-
 arch/arm/mach-omap2/board-igep0020.c       |    4 +-
 arch/arm/mach-omap2/board-omap3beagle.c    |    2 +-
 arch/arm/mach-omap2/board-omap3evm.c       |    2 +-
 arch/arm/mach-omap2/board-omap3pandora.c   |    2 +-
 arch/arm/mach-omap2/board-omap3stalker.c   |    2 +-
 arch/arm/mach-omap2/board-omap3touchbook.c |    2 +-
 arch/arm/mach-omap2/board-omap4panda.c     |    2 +-
 arch/arm/mach-omap2/board-overo.c          |    2 +-
 arch/arm/mach-omap2/board-zoom.c           |    2 +-
 arch/arm/mach-omap2/cclock3xxx_data.c      |   11 -
 arch/arm/mach-omap2/cclock44xx_data.c      |    7 -
 arch/arm/mach-omap2/usb-host.c             |   29 +--
 arch/arm/mach-omap2/usb.h                  |   20 +-
 drivers/mfd/omap-usb-host.c                |  546 +++++++++++++++++-----------
 drivers/mfd/omap-usb-tll.c                 |  245 +++++++------
 drivers/usb/host/ehci-omap.c               |   14 +-
 include/linux/platform_data/usb-omap.h     |   24 +-
 24 files changed, 491 insertions(+), 439 deletions(-)

-- 
1.7.4.1

^ permalink raw reply

* [PATCH 1/4] drivers: usb: phy: add a new driver for usb part of control module
From: Felipe Balbi @ 2013-01-18 12:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <50F93B9C.108@ti.com>

Hi,

On Fri, Jan 18, 2013 at 05:40:04PM +0530, kishon wrote:
> >>+void omap_control_usb_host_mode(struct device *dev)
> >>+{
> >>+	u32 val;
> >>+	struct omap_control_usb	*control_usb = dev_get_drvdata(dev);
> >>+
> >>+	val = AVALID | VBUSVALID;
> >>+
> >>+	writel(val, control_usb->otghs_control);
> >
> >I would like to make this future proof too:
> >
> >val = readl(ctrl->otghs_control);
> >val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID;
> >writel(val, ctrl->otghs_control);
> 
> I think we might then have to add
> val &= ~(IDDIG | SESSEND) right?

right, my bad ;-)

> >Another idea is to try to get the extra resource below, if it fails you
> >try to continue without it ;-)
> 
> I prefer the way it is now because mailbox is needed for MUSB to be
> functional in OMAP4 and we should fail if we don't have it. No?

could be, that would mean an error on DT data though.

-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130118/4795ddc0/attachment.sig>

^ permalink raw reply

* [PATCH RESEND 1/6 v13] gpio: Add a block GPIO API to gpiolib
From: Stijn Devriendt @ 2013-01-18 12:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358250716-21986-2-git-send-email-stigge@antcom.de>

Hi Roland,

This mail has been long overdue due to issues with some internal
permission-tool.
Just to be clear, this is not a competing implementation, it's what we
currently use as-is. I'm just posting this as a reference to see if
perhaps more concepts could be reused. It's based on a 2.6.32 kernel.

It includes:
- labels in sysfs (to provide useful names to userspace)
- gpio group support
- exporting individual/groups of gpios dictated by platform-data or device-tree
- open-drain support (different from mainline)
- examplary support for multi-gpio to pcf8575 driver
- gpio_direction_output_keep() function that prevents toggling when
changing direction

Provided-as-is-by: Stijn Devriendt <sdevrien@cisco.com>
---
 drivers/gpio/Kconfig        |    6 +
 drivers/gpio/Makefile       |    1 +
 drivers/gpio/gpio-export.c  |  327 ++++++++++++++
 drivers/gpio/gpiolib.c      | 1021 ++++++++++++++++++++++++++++++++++++++++---
 drivers/gpio/pcf857x.c      |  102 ++++-
 include/asm-generic/gpio.h  |   67 +++
 include/linux/gpio-export.h |   64 +++
 include/linux/gpio.h        |   83 ++++
 8 files changed, 1602 insertions(+), 69 deletions(-)
 create mode 100644 drivers/gpio/gpio-export.c
 create mode 100644 include/linux/gpio-export.h

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 2ad0128..7daf6df 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -48,6 +48,12 @@ config DEBUG_GPIO
 	  slower.  The diagnostics help catch the type of setup errors
 	  that are most common when setting up new platforms or boards.

+config GPIO_EXPORT
+	bool "GPIO export driver"
+	depends on GPIO_SYSFS && GPIOLIB
+	help
+	  Say Y here to include the GPIO export driver.
+
 config GPIO_SYSFS
 	bool "/sys/class/gpio/... (sysfs interface)"
 	depends on SYSFS && EXPERIMENTAL
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 00a532c..40b96d7 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -4,6 +4,7 @@ ccflags-$(CONFIG_DEBUG_GPIO)	+= -DDEBUG

 obj-$(CONFIG_GPIOLIB)		+= gpiolib.o

+obj-$(CONFIG_GPIO_EXPORT)	+= gpio-export.o
 obj-$(CONFIG_GPIO_ADP5520)	+= adp5520-gpio.o
 obj-$(CONFIG_GPIO_LANGWELL)	+= langwell_gpio.o
 obj-$(CONFIG_GPIO_MAX7301)	+= max7301.o
diff --git a/drivers/gpio/gpio-export.c b/drivers/gpio/gpio-export.c
new file mode 100644
index 0000000..4ee4fe5
--- /dev/null
+++ b/drivers/gpio/gpio-export.c
@@ -0,0 +1,327 @@
+/* drivers/gpio/gpio-export.c
+ *
+ * Copyright (C) 2011 Stijn Devriendt, Cisco Systems Inc.
+ * Copyright (C) 2011 Eli Steenput, Cisco Systems Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/gpio-export.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_OF
+
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+
+#endif
+
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+struct gpio_export_priv
+{
+	int count;
+	int gpio_num;
+	struct gpio_group *group;
+	char desc[MAX_GPIO_LABEL_SIZE];
+};
+
+static __devinit int common_gpio_probe(struct mp_gpio_platform_data
*pdata, struct device *dev)
+{
+	int gpio_count = pdata->gpio_count;
+	struct mp_gpio_line *gpio_line;
+	int i;
+	int err = 0;
+	struct gpio_export_priv *priv;
+
+	if (gpio_count <= 0 || gpio_count > 32)
+		return -ENODEV;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->count = gpio_count;
+	strncpy(priv->desc, pdata->desc, MAX_GPIO_LABEL_SIZE);
+	if (gpio_count > 1)
+	{
+		u32 gpio[32];
+		u32 value = pdata->initialvalue;
+		u32 opendrain = 0;
+		for (i = 0; i < gpio_count; ++i)
+		{
+			gpio_line = &pdata->gpio_data[i];
+			gpio[i] = gpio_line->gpio_num;
+			if (gpio_line->active_low)
+				value ^= (1 << i);
+			if (gpio_line->open_drain)
+				opendrain |= (1 << i);
+		}
+
+		priv->group = gpio_group_request(gpio, gpio_count, priv->desc);
+		if (IS_ERR(priv->group))
+		{
+			dev_err(dev, "Could not request gpio-group: %ld\n", PTR_ERR(priv->group));
+			err = PTR_ERR(priv->group);
+			goto out_mem;
+		}
+
+		if (opendrain)
+		{
+			err = gpio_group_set_opendrain(priv->group,
gpio_group_value_to_raw(priv->group, opendrain));
+			if (err)
+			{
+				dev_err(dev, "Could not set gpio-group open-drain: %d\n", err);
+				goto out_free_group;
+			}
+		}
+
+		switch (pdata->direction)
+		{
+			case GPIO_INPUT:
+				err = gpio_group_direction_input(priv->group);
+				break;
+			case GPIO_OUTPUT:
+				err = gpio_group_direction_output(priv->group,
gpio_group_value_to_raw(priv->group, value));
+				break;
+			case GPIO_OUTPUT_KEEP:
+				err = gpio_group_direction_output_keep(priv->group);
+				break;
+			default:
+        break;
+		}
+
+		if (err)
+		{
+			dev_err(dev, "Could not set gpio-group direction: %d\n", err);
+			goto out_free_group;
+		}
+
+		err = gpio_group_export(priv->group, pdata->direction == GPIO_CHANGE);
+		if (err)
+		{
+			dev_err(dev, "Could not export gpio-group: %d\n", err);
+			goto out_free_group;
+		}
+	}
+	else
+	{
+		gpio_line = &pdata->gpio_data[0];
+		err = gpio_request(gpio_line->gpio_num, priv->desc);
+		if (err)
+		{
+			dev_err(dev, "Could not request gpio %d\n", gpio_line->gpio_num);
+			goto out_free;
+		}
+		if (gpio_line->open_drain)
+		{
+			err = gpio_set_opendrain(gpio_line->gpio_num, 1);
+			if (err)
+			{
+				dev_warn(dev, "Could not set open-drain on gpio %d\n",
gpio_line->gpio_num);
+				goto out_free;
+			}
+		}
+
+		if (pdata->direction == GPIO_INPUT)
+		{
+			err = gpio_direction_input(gpio_line->gpio_num);
+		}
+		else if (pdata->direction == GPIO_OUTPUT)
+		{
+			int value = 0;
+			value = pdata->initialvalue;
+			if (gpio_line->active_low)
+				value = !value;
+			dev_dbg(dev, "Setting output on gpio %d with value %d\n",
gpio_line->gpio_num, value);
+			err = gpio_direction_output(gpio_line->gpio_num, value);
+		}
+		else if (pdata->direction == GPIO_OUTPUT_KEEP)
+		{
+			err = gpio_direction_output_keep(gpio_line->gpio_num);
+		}
+
+		if (err)
+		{
+			dev_err(dev, "Could not set direction: %d\n", err);
+			goto out_free;
+		}
+
+		err = gpio_export(gpio_line->gpio_num, pdata->direction == GPIO_CHANGE);
+		if (err)
+		{
+			dev_warn(dev, "Could not export gpio %d\n", gpio_line->gpio_num);
+			goto out_free;
+		}
+		priv->gpio_num = gpio_line->gpio_num;
+	}
+	dev_set_drvdata(dev, priv);
+	dev_info(dev, "%s: Exported %d GPIO pins\n", pdata->desc, pdata->gpio_count);
+out:
+	return err;
+out_free:
+	gpio_free(gpio_line->gpio_num);
+	kfree(priv);
+	goto out;
+out_free_group:
+	gpio_group_free(priv->group);
+	kfree(priv);
+	goto out;
+out_mem:
+	kfree(priv);
+	goto out;
+}
+
+int common_gpio_remove(struct device *dev)
+{
+	struct gpio_export_priv *priv = dev_get_drvdata(dev);
+	BUG_ON(!priv);
+
+	if (priv->count == 1)
+		gpio_free(priv->gpio_num);
+	else
+		gpio_group_free(priv->group);
+
+	dev_set_drvdata(dev, NULL);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+
+static __devinit int of_gpio_probe(struct platform_device *of_dev,
+		const struct of_device_id *match)
+{
+	struct device_node *np = of_dev->dev.of_node;
+	int gpio_count = of_gpio_count(np);
+	const char* linuxname = of_get_property(np, "desc", NULL);
+
+	struct mp_gpio_platform_data pdata;
+	struct mp_gpio_line gpio_line[32];
+	int i;
+
+	pdata.gpio_data = gpio_line;
+	pdata.gpio_count = gpio_count;
+	strncpy(pdata.desc, linuxname, MAX_GPIO_LABEL_SIZE);
+	pdata.initialvalue = 0;
+
+	if (of_device_is_compatible(np, "gpio-input"))
+		pdata.direction = GPIO_INPUT;
+	else if (of_device_is_compatible(np, "gpio-output"))
+	{
+		const __be32 *value = of_get_property_u32(np, "initial");
+		if (!value)
+			pdata.direction = GPIO_OUTPUT_KEEP;
+		else
+		{
+			pdata.direction = GPIO_OUTPUT;
+			pdata.initialvalue = be32_to_cpup(value);
+			dev_dbg(&of_dev->dev, "initialvalue=%u, keep=%d\n",
pdata.initialvalue, pdata.direction == GPIO_OUTPUT_KEEP);
+		}
+	}
+	else
+	{
+		pdata.direction = GPIO_CHANGE;
+	}
+
+	for (i=0; i < gpio_count; ++i)
+	{
+		u32 flags;
+		gpio_line[i].gpio_num = of_get_gpio_flags(np, i, &flags);
+		gpio_line[i].active_low = flags & OF_GPIO_ACTIVE_LOW;
+		gpio_line[i].open_drain = flags & OF_GPIO_OPEN_DRAIN;
+	}
+	return common_gpio_probe(&pdata, &of_dev->dev);
+}
+
+static __devexit int of_gpio_remove(struct platform_device *of_dev)
+{
+	return common_gpio_remove(&of_dev->dev);
+}
+
+static const struct of_device_id __devinitconst of_gpio_match[] = {
+	{ .compatible = "gpio-input", },
+	{ .compatible = "gpio-output", },
+	{ .compatible = "gpio-user", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, of_gpio_match);
+
+static struct of_platform_driver of_gpio_driver = {
+	.probe = of_gpio_probe,
+	.remove = __devexit_p(of_gpio_remove),
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "of-gpio",
+		.of_match_table = of_gpio_match,
+	},
+};
+
+static int __init of_gpio_init(void)
+{
+	// Use i2c_bus_type to support I/O expanders?
+	return of_register_platform_driver(&of_gpio_driver);
+}
+
+static void __exit of_gpio_exit(void)
+{
+	of_unregister_driver(&of_gpio_driver);
+}
+
+late_initcall(of_gpio_init);
+module_exit(of_gpio_exit);
+
+#else
+
+static __devinit int mp_probe(struct platform_device *p_device)
+{
+	return common_gpio_probe(dev_get_platdata(&p_device->dev), &p_device->dev);
+}
+
+static __devexit int mp_remove(struct platform_device *p_device)
+{
+	return common_gpio_remove(&p_device->dev);
+}
+
+static struct platform_device_id mp_id_table[] = {
+	{
+		.name		= "gpio-export",
+	},
+};
+
+static struct platform_driver mp_gpio_driver = {
+	.probe = mp_probe,
+	.remove = mp_remove,
+	.id_table = mp_id_table,
+	.driver.name = "gpio-export",
+	.driver.bus = &platform_bus_type,
+	.driver.owner = THIS_MODULE,
+};
+
+static int __init mp_gpio_init(void)
+{
+	return platform_driver_register(&mp_gpio_driver);
+}
+
+static void __exit mp_gpio_exit(void)
+{
+	platform_driver_unregister(&mp_gpio_driver);
+}
+
+late_initcall(mp_gpio_init);
+module_exit(mp_gpio_exit);
+
+#endif
+
+MODULE_AUTHOR("Stijn Devriendt, Eli Steenput");
+MODULE_DESCRIPTION("Multi Purpose GPIO export driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 50de0f5..1c9c426 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -8,7 +8,9 @@
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/gpio.h>
+#include <linux/of_gpio.h>
 #include <linux/idr.h>
+#include <linux/ctype.h>


 /* Optional implementation infrastructure for GPIO interfaces.
@@ -53,15 +55,15 @@ struct gpio_desc {
 #define FLAG_SYSFS	4	/* exported via /sys/class/gpio/control */
 #define FLAG_TRIG_FALL	5	/* trigger on falling edge */
 #define FLAG_TRIG_RISE	6	/* trigger on rising edge */
+#define FLAG_OPEN_DRAIN 7       /* gpio is open drain */

 #define PDESC_ID_SHIFT	16	/* add new flags before this one */

 #define GPIO_FLAGS_MASK		((1 << PDESC_ID_SHIFT) - 1)
 #define GPIO_TRIGGER_MASK	(BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))

-#ifdef CONFIG_DEBUG_FS
 	const char		*label;
-#endif
+	struct gpio_group *group;
 };
 static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];

@@ -76,9 +78,7 @@ static struct idr pdesc_idr;

 static inline void desc_set_label(struct gpio_desc *d, const char *label)
 {
-#ifdef CONFIG_DEBUG_FS
 	d->label = label;
-#endif
 }

 /* Warn when drivers omit gpio_request() calls -- legal but ill-advised
@@ -119,6 +119,11 @@ static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
 	return gpio_desc[gpio].chip;
 }

+static inline struct gpio_group *gpio_to_group(unsigned gpio)
+{
+	return gpio_desc[gpio].group;
+}
+
 /* dynamic allocation of GPIOs, e.g. on a hotplugged device */
 static int gpiochip_find_base(int ngpio)
 {
@@ -189,6 +194,110 @@ err:
 	return ret;
 }

+/* gpio_group_raw_to_value() - reorder bits according to the gpio group
+ * request
+ *
+ * @group: gpio_group
+ * @raw: raw value
+ *
+ * Returns a compact value representing the gpio_group value.
+ * e.g. consider gpio pins [1,0,2,3] have been requested and their
+ * value is [1,0,0,1] respectively. The raw readout will be 0xA [1,0,1,0]
+ * while the return value of this function will be 0x9, considering
+ * the order of the GPIOs in the group.
+ */
+u32 gpio_group_raw_to_value(const struct gpio_group *group, u32 raw)
+{
+	int i = 0;
+	u32 ret = 0;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
+	{
+		unsigned offset = group->gpios[i] - base;
+		u32 rawbit = (1 << offset);
+
+		// if raw[offset] is set
+		// then set ret[i]
+		if (raw & rawbit)
+			ret |= (1 << i);
+
+		++i;
+	}
+	return ret;
+}
+
+/* gpio_group_value_to_raw() - Inverse of gpio_group_raw_to_value
+ *
+ * @group: gpio_group
+ * @raw: compact value
+ *
+ * Returns the raw value.
+ */
+u32 gpio_group_value_to_raw(const struct gpio_group *group, u32 value)
+{
+	int i = 0;
+	u32 raw = 0;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
+	{
+		unsigned offset = group->gpios[i] - base;
+		u32 rawbit = (1 << offset);
+
+		// if value[i] is set
+		// then set ret[offset]
+		if (value & (1 << i))
+			raw |= rawbit;
+
+		++i;
+	}
+	return raw;
+}
+
+int gpio_group_test_bit(unsigned long flag, const struct gpio_group *group)
+{
+	u32 set = 0;
+	int i;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+	for (i = 0; i < 32; ++i)
+	{
+		if (group->mask & (1 << i) && test_bit(flag, &gpio_desc[base + i].flags))
+			set |= (1 << i);
+	}
+	if (set == 0)
+		return 0;
+	else if (set == group->mask)
+		return 1;
+	else
+		return -EIO;
+}
+
+void gpio_group_set_bit(unsigned long flag, const struct gpio_group *group)
+{
+	int i;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	for (i = 0; i < 32; ++i)
+	{
+		if (group->mask & (1 << i))
+			set_bit(flag, &gpio_desc[base + i].flags);
+	}
+}
+
+void gpio_group_clear_bit(unsigned long flag, const struct gpio_group *group)
+{
+	int i;
+	unsigned base = gpio_to_chip(group->gpios[0])->base;
+
+	for (i = 0; i < 32; ++i)
+	{
+		if (group->mask & (1 << i))
+			clear_bit(flag, &gpio_desc[base + i].flags);
+	}
+}
+
+
 #ifdef CONFIG_GPIO_SYSFS

 /* lock protects against unexport_gpio() being called while
@@ -231,6 +340,30 @@ static ssize_t gpio_direction_show(struct device *dev,
 	return status;
 }

+static ssize_t gpio_group_direction_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status = 0;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+
+	status = gpio_group_test_bit(FLAG_IS_OUT, group);
+	if (status >= 0)
+		status = sprintf(buf, "%s\n", status == 0 ? "in" : "out");
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
 static ssize_t gpio_direction_store(struct device *dev,
 		struct device_attribute *attr, const char *buf, size_t size)
 {
@@ -255,9 +388,49 @@ static ssize_t gpio_direction_store(struct device *dev,
 	return status ? : size;
 }

-static const DEVICE_ATTR(direction, 0644,
+static ssize_t gpio_group_direction_store(struct device *dev,
+    struct device_attribute *attr, const char* buf, size_t size)
+{
+  const struct gpio_group *group = dev_get_drvdata(dev);
+  ssize_t status;
+  unsigned long input;
+
+  mutex_lock(&sysfs_lock);
+
+  if (gpio_desc[group->gpios[0]].group != group
+   || gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+  {
+    status = -EIO;
+    goto out;
+  }
+
+  if (sysfs_streq(buf, "in"))
+    status = gpio_group_direction_input(group);
+  else if (sysfs_streq(buf, "out"))
+    status = gpio_group_direction_output(group, 0);
+  else
+  {
+    status = strict_strtoul(buf, 0, &input);
+    if (!status)
+    {
+      if ((input & group->mask) == input)
+        status = gpio_group_direction_output(group, input);
+      else
+        status = -EINVAL;
+    }
+  }
+
+out:
+  mutex_unlock(&sysfs_lock);
+  return status ? : size;
+}
+
+static DEVICE_ATTR(direction, 0644,
 		gpio_direction_show, gpio_direction_store);

+struct device_attribute dev_attr_direction_group =
+	__ATTR(direction, 0644, gpio_group_direction_show,
gpio_group_direction_store);
+
 static ssize_t gpio_value_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
@@ -276,6 +449,50 @@ static ssize_t gpio_value_show(struct device *dev,
 	return status;
 }

+static ssize_t gpio_group_value_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = sprintf(buf, "%u\n", gpio_group_raw_to_value(group,
+				gpio_group_get_raw_cansleep(group)));
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+
+static ssize_t gpio_group_raw_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = sprintf(buf, "%u\n", gpio_group_get_raw_cansleep(group));
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
 static ssize_t gpio_value_store(struct device *dev,
 		struct device_attribute *attr, const char *buf, size_t size)
 {
@@ -303,9 +520,96 @@ static ssize_t gpio_value_store(struct device *dev,
 	return status;
 }

+static ssize_t gpio_group_value_store(struct device *dev,
+    struct device_attribute *attr, const char* buf, size_t size)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+	unsigned long value;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0
+			|| gpio_group_test_bit(FLAG_IS_OUT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = strict_strtoul(buf, 0, &value);
+	if (status == 0)
+	{
+		gpio_group_set_raw_cansleep(group,
+				gpio_group_value_to_raw(group, value));
+		status = size;
+	}
+
+out:
+	mutex_unlock(&sysfs_lock);
+
+	return status;
+}
+
+static ssize_t gpio_group_raw_store(struct device *dev,
+    struct device_attribute *attr, const char* buf, size_t size)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+	unsigned long value;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0
+			|| gpio_group_test_bit(FLAG_IS_OUT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = strict_strtoul(buf, 0, &value);
+	if (status == 0)
+	{
+		gpio_group_set_raw_cansleep(group, value);
+		status = size;
+	}
+
+out:
+	mutex_unlock(&sysfs_lock);
+
+	return status;
+}
+
+static ssize_t gpio_group_mask_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	ssize_t status;
+
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_desc[group->gpios[0]].group != group
+			|| gpio_group_test_bit(FLAG_EXPORT, group) <= 0)
+	{
+		status = -EIO;
+		goto out;
+	}
+	status = sprintf(buf, "%u\n", group->mask);
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
 static /*const*/ DEVICE_ATTR(value, 0644,
 		gpio_value_show, gpio_value_store);

+struct device_attribute dev_attr_value_group =
+	__ATTR(value, 0644, gpio_group_value_show, gpio_group_value_store);
+
+DEVICE_ATTR(raw, 0644, gpio_group_raw_show, gpio_group_raw_store);
+
+DEVICE_ATTR(mask, 0444, gpio_group_mask_show, NULL);
+
 static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
 {
 	struct work_struct	*work = priv;
@@ -381,7 +685,7 @@ static int gpio_setup_irq(struct gpio_desc *desc,
struct device *dev,
 			goto free_id;
 		}

-		pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, "value");
+		pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
 		if (!pdesc->value_sd) {
 			ret = -ENODEV;
 			goto free_id;
@@ -475,9 +779,55 @@ found:

 static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store);

+static ssize_t gpio_label_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	const struct gpio_desc	*desc = dev_get_drvdata(dev);
+	ssize_t			status = 0;
+
+	mutex_lock(&sysfs_lock);
+
+	if (!test_bit(FLAG_EXPORT, &desc->flags))
+		status = -EIO;
+	else if (desc->label)
+		status = sprintf(buf, "%s\n", desc->label);
+
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+static ssize_t gpio_group_label_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	const struct gpio_group *group = dev_get_drvdata(dev);
+	const struct gpio_desc	*desc = &gpio_desc[group->gpios[0]];
+	ssize_t			status = 0;
+
+	mutex_lock(&sysfs_lock);
+
+	if (!test_bit(FLAG_EXPORT, &desc->flags))
+		status = -EIO;
+	else if (desc->label)
+		status = sprintf(buf, "%s\n", desc->label);
+
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+static struct device_attribute dev_attr_gpio_label = __ATTR(label,
0444, gpio_label_show, NULL);
+static struct device_attribute dev_attr_gpio_group_label =
__ATTR(label, 0444, gpio_group_label_show, NULL);
+
 static const struct attribute *gpio_attrs[] = {
-	&dev_attr_direction.attr,
 	&dev_attr_value.attr,
+	&dev_attr_gpio_label.attr,
+	NULL,
+};
+
+static const struct attribute *gpio_group_attrs[] = {
+	&dev_attr_value_group.attr,
+	&dev_attr_raw.attr,
+	&dev_attr_mask.attr,
+	&dev_attr_gpio_group_label.attr,
 	NULL,
 };

@@ -485,6 +835,10 @@ static const struct attribute_group gpio_attr_group = {
 	.attrs = (struct attribute **) gpio_attrs,
 };

+static const struct attribute_group gpio_group_attr_group = {
+	.attrs = (struct attribute **) gpio_group_attrs,
+};
+
 /*
  * /sys/class/gpio/gpiochipN/
  *   /base ... matching gpio_chip.base (N)
@@ -530,6 +884,36 @@ static const struct attribute_group gpiochip_attr_group = {
 	.attrs = (struct attribute **) gpiochip_attrs,
 };

+int read_gpios(const char *buf, size_t len, unsigned *gpios)
+{
+	const char *startp = buf;
+	char *endp = (char*)buf;
+	int i;
+	for (i = 0; i < 32; ++i)
+	{
+		unsigned gpio;
+
+		gpio = simple_strtoul(startp, &endp, 0);
+
+		if (endp == startp)
+			return -EINVAL; // not a number, bail out
+
+		gpios[i] = gpio;
+
+		while (endp - buf < len && isspace(*endp)) // eat whitespace
+			endp++;
+
+
+		if (endp - buf > len) // buffer overrun, should never happen
+			return -EINVAL;
+		else if (endp - buf == len) // end of buffer, return number of read gpios
+			return i+1;
+
+		startp = endp;
+	}
+	return -EINVAL;
+}
+
 /*
  * /sys/class/gpio/export ... write-only
  *	integer N ... number of GPIO to export (full access)
@@ -538,10 +922,10 @@ static const struct attribute_group
gpiochip_attr_group = {
  */
 static ssize_t export_store(struct class *class, const char *buf, size_t len)
 {
-	long	gpio;
+	unsigned	gpio[32];
 	int	status;

-	status = strict_strtol(buf, 0, &gpio);
+	status = read_gpios(buf, len, gpio);
 	if (status < 0)
 		goto done;

@@ -550,16 +934,41 @@ static ssize_t export_store(struct class *class,
const char *buf, size_t len)
 	 * they may be undone on its behalf too.
 	 */

-	status = gpio_request(gpio, "sysfs");
-	if (status < 0)
-		goto done;
+	if (status == 1)
+	{
+		status = gpio_request(gpio[0], "sysfs");
+		if (status < 0)
+			goto done;

-	status = gpio_export(gpio, true);
-	if (status < 0)
-		gpio_free(gpio);
+		status = gpio_export(gpio[0], true);
+		if (status < 0)
+			gpio_free(gpio[0]);
+		else
+			set_bit(FLAG_SYSFS, &gpio_desc[gpio[0]].flags);
+	}
 	else
-		set_bit(FLAG_SYSFS, &gpio_desc[gpio].flags);
+	{
+		struct gpio_group *group = gpio_group_request(gpio, status, "sysfs");
+		if (IS_ERR(group))
+		{
+			status = PTR_ERR(group);
+			goto done;
+		}

+		status = gpio_group_export(group, true);
+		if (status < 0)
+			gpio_group_free(group);
+		else
+    {
+      // Lock required to protect against unexport being called
+      // against when only parts of the group have the flag set.
+      // The other cases: all have the flag or none have the flag
+      // are handled correctly.
+      mutex_lock(&sysfs_lock);
+			gpio_group_set_bit(FLAG_SYSFS, group);
+      mutex_unlock(&sysfs_lock);
+    }
+	}
 done:
 	if (status)
 		pr_debug("%s: status %d\n", __func__, status);
@@ -570,6 +979,7 @@ static ssize_t unexport_store(struct class *class,
const char *buf, size_t len)
 {
 	long	gpio;
 	int	status;
+	struct gpio_group *group;

 	status = strict_strtol(buf, 0, &gpio);
 	if (status < 0)
@@ -581,13 +991,29 @@ static ssize_t unexport_store(struct class
*class, const char *buf, size_t len)
 	if (!gpio_is_valid(gpio))
 		goto done;

-	/* No extra locking here; FLAG_SYSFS just signifies that the
-	 * request and export were done by on behalf of userspace, so
-	 * they may be undone on its behalf too.
-	 */
-	if (test_and_clear_bit(FLAG_SYSFS, &gpio_desc[gpio].flags)) {
-		status = 0;
-		gpio_free(gpio);
+	group = gpio_to_group(gpio);
+	if (group)
+	{
+		mutex_lock(&sysfs_lock);
+		if (gpio_group_test_bit(FLAG_SYSFS, group))
+		{
+			status = 0;
+			gpio_group_clear_bit(FLAG_SYSFS, group);
+		}
+		mutex_unlock(&sysfs_lock);
+		if (!status)
+			gpio_group_free(group);
+	}
+	else
+	{
+		/* No extra locking here; FLAG_SYSFS just signifies that the
+		 * request and export were done by on behalf of userspace, so
+		 * they may be undone on its behalf too.
+		 */
+		if (test_and_clear_bit(FLAG_SYSFS, &gpio_desc[gpio].flags)) {
+			status = 0;
+			gpio_free(gpio);
+		}
 	}
 done:
 	if (status)
@@ -662,12 +1088,11 @@ int gpio_export(unsigned gpio, bool direction_may_change)
 		dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
 				desc, ioname ? ioname : "gpio%d", gpio);
 		if (!IS_ERR(dev)) {
-			if (direction_may_change)
-				status = sysfs_create_group(&dev->kobj,
-						&gpio_attr_group);
-			else
-				status = device_create_file(dev,
-						&dev_attr_value);
+  		status = sysfs_create_group(&dev->kobj,
+  						&gpio_attr_group);
+
+			if (!status && direction_may_change)
+				status = device_create_file(dev, &dev_attr_direction);

 			if (!status && gpio_to_irq(gpio) >= 0
 					&& (direction_may_change
@@ -694,6 +1119,72 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_export);

+int gpio_group_export(struct gpio_group *group, bool direction_may_change)
+{
+	unsigned long flags;
+	int status = -EINVAL;
+	struct gpio_chip *chip;
+	unsigned lowest = group->gpios[0];
+	int i;
+
+	/* can't export until sysfs is available ... */
+	if (!gpio_class.p) {
+		pr_debug("%s: called too early!\n", __func__);
+		return -ENOENT;
+	}
+
+	mutex_lock(&sysfs_lock);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	chip = gpio_to_chip(group->gpios[0]);
+	if (gpio_group_test_bit(FLAG_REQUESTED, group) > 0
+			&& gpio_group_test_bit(FLAG_EXPORT, group) == 0)
+	{
+		status = 0;
+		if (!chip->direction_input_multi || !chip->direction_output_multi)
+		{
+			direction_may_change = false;
+		}
+	}
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	if (status == 0)
+	{
+		struct device *dev;
+
+		i=1;
+		while (i < 32 && group->gpios[i] != ARCH_NR_GPIOS)
+		{
+			if (group->gpios[i] < lowest)
+				lowest = group->gpios[i];
+			++i;
+		}
+
+		dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0),
+				group, "group%d", lowest);
+		if (!IS_ERR(dev)) {
+			status = sysfs_create_group(&dev->kobj, &gpio_group_attr_group);
+
+			if (status == 0 && direction_may_change)
+				status = device_create_file(dev, &dev_attr_direction_group);
+
+			if (status != 0)
+				device_unregister(dev);
+		}
+		else
+			status = PTR_ERR(dev);
+		if (status == 0)
+			gpio_group_set_bit(FLAG_EXPORT, group);
+	}
+	mutex_unlock(&sysfs_lock);
+
+	if (status)
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_group_export);
+
+
 static int match_export(struct device *dev, void *data)
 {
 	return dev_get_drvdata(dev) == data;
@@ -744,6 +1235,30 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_export_link);

+int gpio_group_export_link(struct device *dev, const char *name,
struct gpio_group *group)
+{
+	int status = -EINVAL;
+	mutex_lock(&sysfs_lock);
+	if (gpio_group_test_bit(FLAG_EXPORT, group) == 1)
+	{
+		struct device *tdev;
+
+		tdev = class_find_device(&gpio_class, NULL, group, match_export);
+		if (tdev != NULL) {
+			status = sysfs_create_link(&dev->kobj, &tdev->kobj, name);
+		}
+		else
+		{
+			status = -ENODEV;
+		}
+	}
+	mutex_unlock(&sysfs_lock);
+	if (status)
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_group_export_link);
+
 /**
  * gpio_unexport - reverse effect of gpio_export()
  * @gpio: gpio to make unavailable
@@ -783,6 +1298,31 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_unexport);

+void gpio_group_unexport(struct gpio_group *group)
+{
+	int status = -EINVAL;
+	mutex_lock(&sysfs_lock);
+
+	if (gpio_group_test_bit(FLAG_EXPORT, group) == 1)
+	{
+		struct device *dev = NULL;
+		dev = class_find_device(&gpio_class, NULL, group, match_export);
+		if (dev)
+		{
+			gpio_group_clear_bit(FLAG_EXPORT, group);
+			put_device(dev);
+			device_unregister(dev);
+			status = 0;
+		}
+		else
+			status = -ENODEV;
+	}
+	mutex_unlock(&sysfs_lock);
+	if (status)
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], status);
+}
+EXPORT_SYMBOL_GPL(gpio_group_unexport);
+
 static int gpiochip_export(struct gpio_chip *chip)
 {
 	int		status;
@@ -959,6 +1499,8 @@ int gpiochip_add(struct gpio_chip *chip)
 		}
 	}

+	of_gpiochip_add(chip);
+
 unlock:
 	spin_unlock_irqrestore(&gpio_lock, flags);
 	if (status == 0)
@@ -987,6 +1529,8 @@ int gpiochip_remove(struct gpio_chip *chip)

 	spin_lock_irqsave(&gpio_lock, flags);

+	of_gpiochip_remove(chip);
+
 	for (id = chip->base; id < chip->base + chip->ngpio; id++) {
 		if (test_bit(FLAG_REQUESTED, &gpio_desc[id].flags)) {
 			status = -EBUSY;
@@ -1007,6 +1551,38 @@ int gpiochip_remove(struct gpio_chip *chip)
 }
 EXPORT_SYMBOL_GPL(gpiochip_remove);

+/**
+ * gpiochip_find() - iterator for locating a specific gpio_chip
+ * @data: data to pass to match function
+ * @callback: Callback function to check gpio_chip
+ *
+ * Similar to bus_find_device.  It returns a reference to a gpio_chip as
+ * determined by a user supplied @match callback.  The callback should return
+ * 0 if the device doesn't match and non-zero if it does.  If the callback is
+ * non-zero, this function will return to the caller and not iterate over any
+ * more gpio_chips.
+ */
+struct gpio_chip *gpiochip_find(void *data,
+				int (*match)(struct gpio_chip *chip, void *data))
+{
+	struct gpio_chip *chip = NULL;
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	for (i = 0; i < ARCH_NR_GPIOS; i++) {
+		if (!gpio_desc[i].chip)
+			continue;
+
+		if (match(gpio_desc[i].chip, data)) {
+			chip = gpio_desc[i].chip;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	return chip;
+}

 /* These "optional" allocation calls help prevent drivers from stomping
  * on each other, and help provide better diagnostics in debugfs.
@@ -1066,6 +1642,111 @@ done:
 }
 EXPORT_SYMBOL_GPL(gpio_request);

+struct gpio_group* gpio_group_request(unsigned *gpio, int ngpios,
const char* label)
+{
+	int i;
+	unsigned long flags;
+	u32 mask = 0;
+	struct gpio_chip *chip;
+	int rc = 0;
+	struct gpio_group *group;
+
+	if (ngpios <= 0)
+		return ERR_PTR(-EINVAL);
+
+	group = kzalloc(sizeof(struct gpio_group), GFP_KERNEL);
+	if (!group)
+		return ERR_PTR(-ENOMEM);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	if (!gpio_is_valid(gpio[0]))
+	{
+		rc = -EINVAL;
+		goto out_free;
+	}
+	chip = gpio_to_chip(gpio[0]);
+	// if not (multi-input or multi-output capable)
+	if (!chip ||
+			!(   (chip->get_multi && chip->direction_input_multi)
+				|| (chip->set_multi && chip->direction_output_multi))
+	   )
+	{
+		rc = -ENODEV;
+		goto out_free;
+	}
+
+	mask |= (1 << (gpio[0] - chip->base));
+	for (i = 1; i < ngpios; ++i)
+	{
+		if (gpio[i] < chip->base || gpio[i] > chip->base + chip->ngpio)
+		{
+			rc = -EINVAL;
+			goto out_free;
+		}
+		mask |= (1 << (gpio[i] - chip->base));
+	}
+
+	if (!try_module_get(chip->owner))
+	{
+		rc = -ENOSYS;
+		goto out_free;
+	}
+
+	group->mask = mask;
+	for (i = 0; i < 32; ++i)
+	{
+		if (i < ngpios)
+			group->gpios[i] = gpio[i];
+		else
+			group->gpios[i] = ARCH_NR_GPIOS;
+	}
+
+	if (gpio_group_test_bit(FLAG_REQUESTED, group) != 0)
+	{
+		rc = -EBUSY;
+		goto out_put;
+	}
+
+	gpio_group_set_bit(FLAG_REQUESTED, group);
+	for (i = 0; i < ngpios; ++i)
+	{
+		gpio_desc[gpio[i]].group = group;
+		desc_set_label(&gpio_desc[gpio[i]], label ? : "?");
+	}
+
+	if (chip->request_multi) {
+		spin_unlock_irqrestore(&gpio_lock, flags);
+		rc = chip->request_multi(chip, mask);
+		spin_lock_irqsave(&gpio_lock, flags);
+		if (rc)
+			goto out_label;
+	}
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	return group;
+out_label:
+	for (i = 0; i < ngpios; ++i)
+	{
+		desc_set_label(&gpio_desc[gpio[i]], NULL);
+		gpio_desc[gpio[i]].group = NULL;
+	}
+out_put:
+	module_put(chip->owner);
+out_free:
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	kfree(group);
+	pr_debug("%s: group%d (%s) status %d\n", __func__, *gpio, label ? : "?", rc);
+	return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(gpio_group_request);
+
+struct gpio_group * gpio_get_group(unsigned gpio)
+{
+	if (!gpio_is_valid(gpio))
+		return NULL;
+	return gpio_desc[gpio].group;
+}
+
 void gpio_free(unsigned gpio)
 {
 	unsigned long		flags;
@@ -1102,6 +1783,45 @@ void gpio_free(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(gpio_free);

+void gpio_group_free(struct gpio_group *group)
+{
+	unsigned long flags;
+	struct gpio_chip *chip;
+
+	might_sleep();
+
+	gpio_group_unexport(group);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	chip = gpio_to_chip(group->gpios[0]);
+	if (chip && gpio_group_test_bit(FLAG_REQUESTED, group))
+	{
+		int i;
+		if (chip->free_multi) {
+			spin_unlock_irqrestore(&gpio_lock, flags);
+			might_sleep_if(extra_checks && chip->can_sleep);
+			chip->free_multi(chip, group->mask);
+			spin_lock_irqsave(&gpio_lock, flags);
+		}
+		for (i = 0; i < 32; ++i)
+		{
+			if (group->mask & (1 << i))
+			{
+				desc_set_label(&gpio_desc[chip->base + i], NULL);
+				gpio_desc[chip->base +i].group = NULL;
+			}
+			module_put(chip->owner);
+			gpio_group_clear_bit(FLAG_REQUESTED, group);
+		}
+	}
+	else
+		WARN_ON(extra_checks);
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	kfree(group);
+}
+EXPORT_SYMBOL_GPL(gpio_group_free);

 /**
  * gpiochip_is_requested - return string iff signal was requested
@@ -1124,15 +1844,10 @@ const char *gpiochip_is_requested(struct
gpio_chip *chip, unsigned offset)
 		return NULL;
 	if (test_bit(FLAG_REQUESTED, &gpio_desc[gpio].flags) == 0)
 		return NULL;
-#ifdef CONFIG_DEBUG_FS
 	return gpio_desc[gpio].label;
-#else
-	return "?";
-#endif
 }
 EXPORT_SYMBOL_GPL(gpiochip_is_requested);

-
 /* Drivers MUST set GPIO direction before making get/set calls.  In
  * some cases this is done in early boot, before IRQs are enabled.
  *
@@ -1195,6 +1910,25 @@ fail:
 }
 EXPORT_SYMBOL_GPL(gpio_direction_input);

+
+int gpio_group_direction_input(const struct gpio_group *group)
+{
+	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
+	int rc;
+
+	if (!chip->get_multi || !chip->direction_input_multi)
+		return -EINVAL;
+
+	rc = chip->direction_input_multi(chip, group->mask);
+	if (rc == 0)
+	{
+		gpio_group_clear_bit(FLAG_IS_OUT, group);
+	}
+	else
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], rc);
+	return rc;
+}
+
 int gpio_direction_output(unsigned gpio, int value)
 {
 	unsigned long		flags;
@@ -1248,6 +1982,124 @@ fail:
 }
 EXPORT_SYMBOL_GPL(gpio_direction_output);

+int gpio_group_direction_output(const struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
+	int rc;
+
+	if (!chip->set_multi || !chip->direction_output_multi)
+		return -EINVAL;
+
+	rc = chip->direction_output_multi(chip, group->mask, value & group->mask);
+	if (rc == 0)
+	{
+		gpio_group_set_bit(FLAG_IS_OUT, group);
+	}
+	else
+		pr_debug("%s: group%d status %d\n", __func__, group->gpios[0], rc);
+	return rc;
+}
+
+int gpio_set_opendrain(unsigned gpio, int value)
+{
+	unsigned long		flags;
+	struct gpio_chip	*chip;
+	struct gpio_desc	*desc = &gpio_desc[gpio];
+	int			status = -EINVAL;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	if (!gpio_is_valid(gpio))
+		goto fail;
+	chip = desc->chip;
+	if (!chip || !chip->set || !chip->direction_output)
+		goto fail;
+	gpio -= chip->base;
+	if (gpio >= chip->ngpio)
+		goto fail;
+	status = gpio_ensure_requested(desc, gpio);
+	if (status < 0)
+		goto fail;
+
+	/* now we know the gpio is valid and chip won't vanish */
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	might_sleep_if(extra_checks && chip->can_sleep);
+
+	if (!chip->set_opendrain)
+		return -ENOSYS;
+
+	if (status) {
+		status = chip->request(chip, gpio);
+		if (status < 0) {
+			pr_debug("GPIO-%d: chip request fail, %d\n",
+				chip->base + gpio, status);
+			/* and it's not available to anyone else ...
+			 * gpio_request() is the fully clean solution.
+			 */
+			goto lose;
+		}
+	}
+
+	status = chip->set_opendrain(chip, gpio, value);
+	if (status == 0)
+	{
+		if (value)
+			set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+		else
+			clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
+	}
+lose:
+	return status;
+fail:
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	if (status)
+		pr_debug("%s: gpio-%d status %d\n",
+			__func__, gpio, status);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_set_opendrain);
+
+int gpio_group_set_opendrain(struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip = gpio_to_chip(group->gpios[0]);
+	int err;
+	if (!chip)
+		return -EINVAL;
+	if (!chip->set_multi || !chip->direction_output_multi)
+		return -EINVAL;
+	if (!chip->set_opendrain_multi)
+		return -ENOSYS;
+
+	might_sleep_if(extra_checks && chip->can_sleep);
+
+	err = chip->set_opendrain_multi(chip, group->mask, value & group->mask);
+	if (err == 0)
+	{
+		int i;
+		unsigned base = chip->base;
+		for (i = 0; i < 32; ++i)
+		{
+			if (group->mask & (1 << i))
+			{
+				if (value & (1 << i))
+				{
+					set_bit(FLAG_OPEN_DRAIN, &gpio_desc[base+i].flags);
+				}
+				else
+				{
+					clear_bit(FLAG_OPEN_DRAIN, &gpio_desc[base+i].flags);
+				}
+			}
+		}
+	}
+	if (err)
+		pr_debug("%s: group%d status %d\n",
+				__func__, group->gpios[0], err);
+	return err;
+}
+EXPORT_SYMBOL_GPL(gpio_group_set_opendrain);

 /* I/O calls are only valid after configuration completed; the relevant
  * "is this a valid GPIO" error checks should already have been done.
@@ -1290,6 +2142,14 @@ int __gpio_get_value(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(__gpio_get_value);

+u32 gpio_group_get_raw(const struct gpio_group* group)
+{
+	struct gpio_chip *chip;
+	chip = gpio_to_chip(group->gpios[0]);
+	return chip->get_multi ? chip->get_multi(chip, group->mask) : 0;
+}
+EXPORT_SYMBOL_GPL(gpio_group_get_raw);
+
 /**
  * __gpio_set_value() - assign a gpio's value
  * @gpio: gpio whose value will be assigned
@@ -1309,6 +2169,13 @@ void __gpio_set_value(unsigned gpio, int value)
 }
 EXPORT_SYMBOL_GPL(__gpio_set_value);

+void gpio_group_set_raw(const struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip;
+	chip = gpio_to_chip(group->gpios[0]);
+	chip->set_multi(chip, group->mask, value | group->mask);
+}
+EXPORT_SYMBOL_GPL(gpio_group_set_raw);
 /**
  * __gpio_cansleep() - report whether gpio value access will sleep
  * @gpio: gpio in question
@@ -1328,6 +2195,11 @@ int __gpio_cansleep(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(__gpio_cansleep);

+int gpio_group_cansleep(const struct gpio_group *group)
+{
+	return __gpio_cansleep(group->gpios[0]);
+}
+
 /**
  * __gpio_to_irq() - return the IRQ corresponding to a GPIO
  * @gpio: gpio whose IRQ will be returned (already requested)
@@ -1362,6 +2234,15 @@ int gpio_get_value_cansleep(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(gpio_get_value_cansleep);

+u32 gpio_group_get_raw_cansleep(const struct gpio_group *group)
+{
+	struct gpio_chip *chip;
+	might_sleep_if(extra_checks);
+	chip = gpio_to_chip(group->gpios[0]);
+	return chip->get_multi ? chip->get_multi(chip, group->mask) : 0;
+}
+EXPORT_SYMBOL_GPL(gpio_group_get_raw_cansleep);
+
 void gpio_set_value_cansleep(unsigned gpio, int value)
 {
 	struct gpio_chip	*chip;
@@ -1372,6 +2253,14 @@ void gpio_set_value_cansleep(unsigned gpio, int value)
 }
 EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);

+void gpio_group_set_raw_cansleep(const struct gpio_group *group, u32 value)
+{
+	struct gpio_chip *chip;
+	might_sleep_if(extra_checks);
+	chip = gpio_to_chip(group->gpios[0]);
+	chip->set_multi(chip, group->mask, value & group->mask);
+}
+EXPORT_SYMBOL_GPL(gpio_group_set_raw_cansleep);

 #ifdef CONFIG_DEBUG_FS

@@ -1381,18 +2270,24 @@ static void gpiolib_dbg_show(struct seq_file
*s, struct gpio_chip *chip)
 	unsigned		gpio = chip->base;
 	struct gpio_desc	*gdesc = &gpio_desc[gpio];
 	int			is_out;
+	int			is_open;
+	int     is_group;

 	for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) {
 		if (!test_bit(FLAG_REQUESTED, &gdesc->flags))
 			continue;

 		is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);
-		seq_printf(s, " gpio-%-3d (%-20.20s) %s %s",
-			gpio, gdesc->label,
-			is_out ? "out" : "in ",
-			chip->get
+		is_open = test_bit(FLAG_OPEN_DRAIN, &gdesc->flags);
+		is_group = (gdesc->group != NULL);
+		seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s %s",
+				gpio, gdesc->label,
+				is_out ? "out" : "in ",
+				chip->get
 				? (chip->get(chip, i) ? "hi" : "lo")
-				: "?  ");
+				: "?  ",
+				is_open ? "open" : "act ",
+				is_group ? "grp" : "pin");

 		if (!is_out) {
 			int		irq = gpio_to_irq(gpio);
@@ -1408,32 +2303,32 @@ static void gpiolib_dbg_show(struct seq_file
*s, struct gpio_chip *chip)
 				char *trigger;

 				switch (desc->status & IRQ_TYPE_SENSE_MASK) {
-				case IRQ_TYPE_NONE:
-					trigger = "(default)";
-					break;
-				case IRQ_TYPE_EDGE_FALLING:
-					trigger = "edge-falling";
-					break;
-				case IRQ_TYPE_EDGE_RISING:
-					trigger = "edge-rising";
-					break;
-				case IRQ_TYPE_EDGE_BOTH:
-					trigger = "edge-both";
-					break;
-				case IRQ_TYPE_LEVEL_HIGH:
-					trigger = "level-high";
-					break;
-				case IRQ_TYPE_LEVEL_LOW:
-					trigger = "level-low";
-					break;
-				default:
-					trigger = "?trigger?";
-					break;
+					case IRQ_TYPE_NONE:
+						trigger = "(default)";
+						break;
+					case IRQ_TYPE_EDGE_FALLING:
+						trigger = "edge-falling";
+						break;
+					case IRQ_TYPE_EDGE_RISING:
+						trigger = "edge-rising";
+						break;
+					case IRQ_TYPE_EDGE_BOTH:
+						trigger = "edge-both";
+						break;
+					case IRQ_TYPE_LEVEL_HIGH:
+						trigger = "level-high";
+						break;
+					case IRQ_TYPE_LEVEL_LOW:
+						trigger = "level-low";
+						break;
+					default:
+						trigger = "?trigger?";
+						break;
 				}

 				seq_printf(s, " irq-%d %s%s",
-					irq, trigger,
-					(desc->status & IRQ_WAKEUP)
+						irq, trigger,
+						(desc->status & IRQ_WAKEUP)
 						? " wakeup" : "");
 			}
 		}
diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c
index 29f19ce..a51d9ad 100644
--- a/drivers/gpio/pcf857x.c
+++ b/drivers/gpio/pcf857x.c
@@ -79,6 +79,20 @@ static int pcf857x_input8(struct gpio_chip *chip,
unsigned offset)
 	return status;
 }

+static int pcf857x_input8_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out |= (u16)mask;
+	status = i2c_smbus_write_byte(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
+
 static int pcf857x_get8(struct gpio_chip *chip, unsigned offset)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -88,6 +102,15 @@ static int pcf857x_get8(struct gpio_chip *chip,
unsigned offset)
 	return (value < 0) ? 0 : (value & (1 << offset));
 }

+static u32 pcf857x_get8_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	u32		value;
+
+	value = i2c_smbus_read_byte(gpio->client);
+	return (value < 0) ? 0 : (value & mask);
+}
+
 static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -105,6 +128,25 @@ static int pcf857x_output8(struct gpio_chip
*chip, unsigned offset, int value)
 	return status;
 }

+static int pcf857x_output8_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out &= ~mask;
+	gpio->out |= value;
+	status = i2c_smbus_write_byte(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
+static void pcf857x_set8_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	pcf857x_output8_multi(chip, mask, value);
+}
+
 static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value)
 {
 	pcf857x_output8(chip, offset, value);
@@ -147,6 +189,19 @@ static int pcf857x_input16(struct gpio_chip
*chip, unsigned offset)
 	return status;
 }

+static int pcf857x_input16_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out |= mask;
+	status = i2c_write_le16(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
 static int pcf857x_get16(struct gpio_chip *chip, unsigned offset)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -156,6 +211,15 @@ static int pcf857x_get16(struct gpio_chip *chip,
unsigned offset)
 	return (value < 0) ? 0 : (value & (1 << offset));
 }

+static u32 pcf857x_get16_multi(struct gpio_chip *chip, u32 mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	u32		value;
+
+	value = i2c_read_le16(gpio->client);
+	return (value < 0) ? 0 : (value & mask);
+}
+
 static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value)
 {
 	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
@@ -173,11 +237,30 @@ static int pcf857x_output16(struct gpio_chip
*chip, unsigned offset, int value)
 	return status;
 }

+static int pcf857x_output16_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		status;
+
+	mutex_lock(&gpio->lock);
+	gpio->out &= ~mask;
+	gpio->out |= value;
+	status = i2c_write_le16(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+
+	return status;
+}
+
 static void pcf857x_set16(struct gpio_chip *chip, unsigned offset, int value)
 {
 	pcf857x_output16(chip, offset, value);
 }

+static void pcf857x_set16_multi(struct gpio_chip *chip, u32 mask, u32 value)
+{
+	pcf857x_output16_multi(chip, mask, value);
+}
+
 /*-------------------------------------------------------------------------*/

 static int pcf857x_probe(struct i2c_client *client,
@@ -190,7 +273,6 @@ static int pcf857x_probe(struct i2c_client *client,
 	pdata = client->dev.platform_data;
 	if (!pdata) {
 		dev_dbg(&client->dev, "no platform data\n");
-		return -EINVAL;
 	}

 	/* Allocate, initialize, and register this gpio_chip. */
@@ -200,7 +282,7 @@ static int pcf857x_probe(struct i2c_client *client,

 	mutex_init(&gpio->lock);

-	gpio->chip.base = pdata->gpio_base;
+	gpio->chip.base = pdata ? pdata->gpio_base : -1;
 	gpio->chip.can_sleep = 1;
 	gpio->chip.dev = &client->dev;
 	gpio->chip.owner = THIS_MODULE;
@@ -218,10 +300,14 @@ static int pcf857x_probe(struct i2c_client *client,
 	 */
 	gpio->chip.ngpio = id->driver_data;
 	if (gpio->chip.ngpio == 8) {
+		gpio->chip.direction_input_multi = pcf857x_input8_multi;
 		gpio->chip.direction_input = pcf857x_input8;
 		gpio->chip.get = pcf857x_get8;
+		gpio->chip.get_multi = pcf857x_get8_multi;
 		gpio->chip.direction_output = pcf857x_output8;
+		gpio->chip.direction_output_multi = pcf857x_output8_multi;
 		gpio->chip.set = pcf857x_set8;
+		gpio->chip.set_multi = pcf857x_set8_multi;

 		if (!i2c_check_functionality(client->adapter,
 				I2C_FUNC_SMBUS_BYTE))
@@ -239,9 +325,13 @@ static int pcf857x_probe(struct i2c_client *client,
 	 */
 	} else if (gpio->chip.ngpio == 16) {
 		gpio->chip.direction_input = pcf857x_input16;
+		gpio->chip.direction_input_multi = pcf857x_input16_multi;
 		gpio->chip.get = pcf857x_get16;
+		gpio->chip.get_multi = pcf857x_get16_multi;
 		gpio->chip.direction_output = pcf857x_output16;
+		gpio->chip.direction_output_multi = pcf857x_output16_multi;
 		gpio->chip.set = pcf857x_set16;
+		gpio->chip.set_multi = pcf857x_set16_multi;

 		if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
 			status = -EIO;
@@ -278,7 +368,7 @@ static int pcf857x_probe(struct i2c_client *client,
 	 * to zero, our software copy of the "latch" then matches the chip's
 	 * all-ones reset state.  Otherwise it flags pins to be driven low.
 	 */
-	gpio->out = ~pdata->n_latch;
+	gpio->out = pdata ? ~pdata->n_latch : ~0;

 	status = gpiochip_add(&gpio->chip);
 	if (status < 0)
@@ -299,7 +389,7 @@ static int pcf857x_probe(struct i2c_client *client,
 	/* Let platform code set up the GPIOs and their users.
 	 * Now is the first time anyone could use them.
 	 */
-	if (pdata->setup) {
+	if (pdata && pdata->setup) {
 		status = pdata->setup(client,
 				gpio->chip.base, gpio->chip.ngpio,
 				pdata->context);
@@ -310,7 +400,7 @@ static int pcf857x_probe(struct i2c_client *client,
 	return 0;

 fail:
-	dev_dbg(&client->dev, "probe error %d for '%s'\n",
+	dev_err(&client->dev, "probe error %d for '%s'\n",
 			status, client->name);
 	kfree(gpio);
 	return status;
@@ -322,7 +412,7 @@ static int pcf857x_remove(struct i2c_client *client)
 	struct pcf857x			*gpio = i2c_get_clientdata(client);
 	int				status = 0;

-	if (pdata->teardown) {
+	if (pdata && pdata->teardown) {
 		status = pdata->teardown(client,
 				gpio->chip.base, gpio->chip.ngpio,
 				pdata->context);
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 66d6106..ed2407f 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -30,6 +30,7 @@ static inline int gpio_is_valid(int number)

 struct seq_file;
 struct module;
+struct device_node;

 /**
  * struct gpio_chip - abstract a GPIO controller
@@ -89,6 +90,22 @@ struct gpio_chip {
 						unsigned offset, int value);
 	void			(*set)(struct gpio_chip *chip,
 						unsigned offset, int value);
+	int			(*set_opendrain)(struct gpio_chip *chip,
+						unsigned offset, int value);
+	int			(*set_opendrain_multi)(struct gpio_chip *chip,
+						u32 mask, u32 value);
+
+	int			(*request_multi)(struct gpio_chip *chip,
+						u32 mask);
+	void			(*free_multi)(struct gpio_chip *chip,
+						u32 mask);
+
+	int			(*direction_input_multi)(struct gpio_chip *chip,
+						u32 mask);
+	int			(*direction_output_multi)(struct gpio_chip *chip,
+						u32 mask, u32 value);
+	void			(*set_multi)(struct gpio_chip *chip, u32 mask, u32 value);
+	u32			(*get_multi)(struct gpio_chip *chip, u32 mask);

 	int			(*to_irq)(struct gpio_chip *chip,
 						unsigned offset);
@@ -100,6 +117,23 @@ struct gpio_chip {
 	char			**names;
 	unsigned		can_sleep:1;
 	unsigned		exported:1;
+
+#if defined(CONFIG_OF_GPIO)
+	/*
+	 * If CONFIG_OF is enabled, then all GPIO controllers described in the
+	 * device tree automatically may have an OF translation
+	 */
+	struct device_node *of_node;
+	int of_gpio_n_cells;
+	int (*of_xlate)(struct gpio_chip *gc, struct device_node *np,
+		        const void *gpio_spec, u32 *flags);
+#endif
+};
+
+struct gpio_group
+{
+	unsigned gpios[32];
+	u32 mask;
 };

 extern const char *gpiochip_is_requested(struct gpio_chip *chip,
@@ -109,6 +143,9 @@ extern int __must_check gpiochip_reserve(int
start, int ngpio);
 /* add/remove chips */
 extern int gpiochip_add(struct gpio_chip *chip);
 extern int __must_check gpiochip_remove(struct gpio_chip *chip);
+extern struct gpio_chip *gpiochip_find(void *data,
+					int (*match)(struct gpio_chip *chip,
+						     void *data));


 /* Always use the library code for GPIO management calls,
@@ -120,9 +157,28 @@ extern void gpio_free(unsigned gpio);
 extern int gpio_direction_input(unsigned gpio);
 extern int gpio_direction_output(unsigned gpio, int value);

+extern int gpio_set_opendrain(unsigned gpio, int value);
+
 extern int gpio_get_value_cansleep(unsigned gpio);
 extern void gpio_set_value_cansleep(unsigned gpio, int value);

+/*
+ * Handling of gpio groups
+ */
+extern struct gpio_group* gpio_group_request(unsigned *gpio, int ngpios,
+    const char *label);
+extern void gpio_group_free(struct gpio_group* group);
+
+extern int gpio_group_direction_input(const struct gpio_group *group);
+extern int gpio_group_direction_output(const struct gpio_group
*group, u32 value);
+
+extern u32 gpio_group_get_raw_cansleep(const struct gpio_group *group);
+extern void gpio_group_set_raw_cansleep(const struct gpio_group
*group, u32 value);
+
+extern int gpio_group_set_opendrain(struct gpio_group* group, u32 value);
+
+u32 gpio_group_raw_to_value(const struct gpio_group *group, u32 raw);
+u32 gpio_group_value_to_raw(const struct gpio_group *group, u32 value);

 /* A platform's <asm/gpio.h> code may want to inline the I/O calls when
  * the GPIO is constant and refers to some always-present controller,
@@ -135,6 +191,11 @@ extern int __gpio_cansleep(unsigned gpio);

 extern int __gpio_to_irq(unsigned gpio);

+extern u32 gpio_group_get_raw(const struct gpio_group *group);
+extern void gpio_group_set_raw(const struct gpio_group *group, u32 value);
+
+extern int gpio_group_cansleep(const struct gpio_group *group);
+
 #ifdef CONFIG_GPIO_SYSFS

 /*
@@ -146,6 +207,12 @@ extern int gpio_export_link(struct device *dev,
const char *name,
 			unsigned gpio);
 extern void gpio_unexport(unsigned gpio);

+extern int gpio_group_export(struct gpio_group *group,
+    bool direction_may_change);
+extern int gpio_group_export_link(struct device *dev, const char *name,
+    struct gpio_group *group);
+extern void gpio_group_unexport(struct gpio_group *group);
+
 #endif	/* CONFIG_GPIO_SYSFS */

 #else	/* !CONFIG_HAVE_GPIO_LIB */
diff --git a/include/linux/gpio-export.h b/include/linux/gpio-export.h
new file mode 100644
index 0000000..712e9ff
--- /dev/null
+++ b/include/linux/gpio-export.h
@@ -0,0 +1,64 @@
+/*    Structures for passing gpio settings to drivers/gpio/gpio-export.c
+ *    201103 steene99
+ */
+#ifndef __LINUX_GPIO_EXPORT_H
+#define __LINUX_GPIO_EXPORT_H
+
+enum gpio_direction
+{
+	GPIO_INPUT,
+	GPIO_OUTPUT,
+	GPIO_CHANGE,
+	GPIO_OUTPUT_KEEP,
+};
+
+struct mp_gpio_line
+{
+	int gpio_num;
+	int active_low;
+	int open_drain;
+};
+
+#define GPIO_PIN(_nr, _active_low, _open_drain) \
+{ \
+	.gpio_num = _nr, \
+	.active_low = _active_low, \
+	.open_drain = _open_drain, \
+}
+
+#define SIMPLE_GPIO_PIN(_name, _nr) \
+static struct mp_gpio_line _name[] = \
+{ \
+	GPIO_PIN(_nr, 0, 0), \
+}
+
+#define MAX_GPIO_LABEL_SIZE 32
+
+struct mp_gpio_platform_data
+{
+	int gpio_count;
+	enum gpio_direction direction;
+	u32 initialvalue;  // value
+	struct mp_gpio_line *gpio_data;
+	char desc[MAX_GPIO_LABEL_SIZE];
+};
+
+#define DEFINE_GPIO_GROUP(_name, _count, _direction, _initial, _pin, _desc) \
+	static struct mp_gpio_platform_data _name = { \
+		.gpio_count = _count, \
+		.direction = _direction, \
+		.initialvalue = _initial, \
+		.gpio_data = _pin, \
+		.desc=_desc, \
+	}
+
+#define GPIO_PDEV(_id, _pdata) \
+{ \
+	.name = "gpio-export", \
+	.id = _id, \
+	.dev = { \
+		.platform_data = &_pdata, \
+	}, \
+}
+
+#endif //__LINUX_GPIO_EXPORT_H
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 059bd18..f82edad 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -13,6 +13,7 @@
 #include <linux/errno.h>

 struct device;
+struct gpio_chip;

 /*
  * Some platforms don't support the GPIO programming interface.
@@ -33,6 +34,11 @@ static inline int gpio_request(unsigned gpio, const
char *label)
 	return -ENOSYS;
 }

+static inline int gpio_group_request(unsigned *gpios, int ngpios,
const char *label)
+{
+	return ERR_PTR(-ENOSYS);
+}
+
 static inline void gpio_free(unsigned gpio)
 {
 	might_sleep();
@@ -41,16 +47,32 @@ static inline void gpio_free(unsigned gpio)
 	WARN_ON(1);
 }

+static inline void gpio_group_free(struct gpio_group *group)
+{
+	might_sleep();
+	WARN_ON(1);
+}
+
 static inline int gpio_direction_input(unsigned gpio)
 {
 	return -ENOSYS;
 }

+static inline int gpio_group_direction_input(const struct gpio_group *group)
+{
+	return -ENOSYS;
+}
+
 static inline int gpio_direction_output(unsigned gpio, int value)
 {
 	return -ENOSYS;
 }

+static inline int gpio_group_direction_output(const struct gpio_group
*group, u32 value)
+{
+	return -ENOSYS;
+}
+
 static inline int gpio_get_value(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */
@@ -58,12 +80,29 @@ static inline int gpio_get_value(unsigned gpio)
 	return 0;
 }

+static inline int gpio_group_get_raw(const struct gpio_group *group)
+{
+	WARN_ON(1);
+	return 0;
+}
+
 static inline void gpio_set_value(unsigned gpio, int value)
 {
 	/* GPIO can never have been requested or set as output */
 	WARN_ON(1);
 }

+static inline void gpio_group_set_raw(const struct gpio_group *group,
u32 value)
+{
+	WARN_ON(1);
+}
+
+static inline int gpio_set_opendrain(unsigned gpio, int value)
+{
+	WARN_ON(1);
+	return -ENOSYS;
+}
+
 static inline int gpio_cansleep(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */
@@ -71,6 +110,12 @@ static inline int gpio_cansleep(unsigned gpio)
 	return 0;
 }

+static inline int gpio_group_cansleep(const struct gpio_group *group)
+{
+	WARN_ON(1);
+	return 0;
+}
+
 static inline int gpio_get_value_cansleep(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */
@@ -78,12 +123,23 @@ static inline int gpio_get_value_cansleep(unsigned gpio)
 	return 0;
 }

+static inline int gpio_group_get_raw_cansleep(const struct gpio_group *group)
+{
+	WARN_ON(1);
+	return 0;
+}
+
 static inline void gpio_set_value_cansleep(unsigned gpio, int value)
 {
 	/* GPIO can never have been requested or set as output */
 	WARN_ON(1);
 }

+static inline void gpio_group_set_raw_cansleep(const struct
gpio_group *group, u32 value)
+{
+	WARN_ON(1);
+}
+
 static inline int gpio_export(unsigned gpio, bool direction_may_change)
 {
 	/* GPIO can never have been requested or set as {in,out}put */
@@ -91,6 +147,12 @@ static inline int gpio_export(unsigned gpio, bool
direction_may_change)
 	return -EINVAL;
 }

+static inline int gpio_group_export(struct gpio_group *group, bool
direction_may_change)
+{
+	WARN_ON(1);
+	return -EINVAL;
+}
+
 static inline int gpio_export_link(struct device *dev, const char *name,
 				unsigned gpio)
 {
@@ -99,6 +161,12 @@ static inline int gpio_export_link(struct device
*dev, const char *name,
 	return -EINVAL;
 }

+static inline int gpio_group_export_link(struct device *dev, const char *name,
+    struct gpio_group *group)
+{
+	WARN_ON(1);
+	return -EINVAL;
+}

 static inline void gpio_unexport(unsigned gpio)
 {
@@ -106,6 +174,11 @@ static inline void gpio_unexport(unsigned gpio)
 	WARN_ON(1);
 }

+static inline void gpio_group_unexport(struct gpio_group *group)
+{
+	WARN_ON(1);
+}
+
 static inline int gpio_to_irq(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as input */
@@ -122,4 +195,14 @@ static inline int irq_to_gpio(unsigned irq)

 #endif

+static inline int gpio_direction_output_keep(int gpio)
+{
+	return gpio_direction_output(gpio, gpio_get_value_cansleep(gpio));
+}
+
+static inline int gpio_group_direction_output_keep(const struct
gpio_group *group)
+{
+	return gpio_group_direction_output(group, gpio_group_get_raw_cansleep(group));
+}
+
 #endif /* __LINUX_GPIO_H */
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 2/2] usb: chipidea: imx: Add system suspend/resume API
From: Chen Peter-B29397 @ 2013-01-18 12:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130118105901.GC23505@n2100.arm.linux.org.uk>

 
> 
> On Fri, Jan 18, 2013 at 10:50:28AM +0800, Peter Chen wrote:
> > +#ifdef CONFIG_PM
> > +static int ci13xxx_imx_suspend(struct device *dev)
> > +{
> > +	struct ci13xxx_imx_data *data =
> > +		platform_get_drvdata(to_platform_device(dev));
> 
> Is there a reason not to use dev_get_drvdata() here?  Hint:
> 

It is my careless, I will change. Thanks.

> #define to_platform_device(x) container_of((x), struct platform_device,
> dev)
> 
> static inline void *platform_get_drvdata(const struct platform_device
> *pdev)
> {
>         return dev_get_drvdata(&pdev->dev);
> }
> 
> So, you're going from a dev => platform device => the same dev again.
> 
> It's perfectly valid to use dev_get_drvdata() here because we're not
> going
> to separate these two (it makes absolutely no sense to.)

^ permalink raw reply

* [PATCH 4/4] drivers: usb: start using the control module driver
From: kishon @ 2013-01-18 12:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130118120256.GC4672@arwen.pp.htv.fi>

On Friday 18 January 2013 05:32 PM, Felipe Balbi wrote:
> Hi,
>
> On Fri, Jan 18, 2013 at 03:10:45PM +0530, Kishon Vijay Abraham I wrote:
>> Start using the control module driver for powering on the PHY and for
>> writing to the mailbox instead of writing to the control module
>> registers on their own.
>>
>> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
>> ---
>>   Documentation/devicetree/bindings/usb/omap-usb.txt |    4 ++
>>   Documentation/devicetree/bindings/usb/usb-phy.txt  |    7 +--
>>   arch/arm/mach-omap2/omap_hwmod_44xx_data.c         |   13 ----
>>   drivers/usb/musb/Kconfig                           |    1 +
>>   drivers/usb/musb/omap2430.c                        |   64 ++++++++------------
>>   drivers/usb/musb/omap2430.h                        |    9 ---
>>   drivers/usb/phy/Kconfig                            |    1 +
>>   drivers/usb/phy/omap-usb2.c                        |   38 +++---------
>>   include/linux/usb/omap_usb.h                       |    4 +-
>>   9 files changed, 42 insertions(+), 99 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt
>> index ead6ba9..8bedbba 100644
>> --- a/Documentation/devicetree/bindings/usb/omap-usb.txt
>> +++ b/Documentation/devicetree/bindings/usb/omap-usb.txt
>> @@ -3,6 +3,9 @@ OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS
>>   OMAP MUSB GLUE
>>    - compatible : Should be "ti,omap4-musb" or "ti,omap3-musb"
>>    - ti,hwmods : must be "usb_otg_hs"
>> + - ti,has_mailbox : to specify that omap uses an external mailbox
>> +   (in control module) to communicate with the musb core during device connect
>> +   and disconnect.
>>    - multipoint : Should be "1" indicating the musb controller supports
>>      multipoint. This is a MUSB configuration-specific setting.
>>    - num_eps : Specifies the number of endpoints. This is also a
>> @@ -24,6 +27,7 @@ SOC specific device node entry
>>   usb_otg_hs: usb_otg_hs at 4a0ab000 {
>>   	compatible = "ti,omap4-musb";
>>   	ti,hwmods = "usb_otg_hs";
>> +	ti,has_mailbox;
>>   	multipoint = <1>;
>>   	num_eps = <16>;
>>   	ram_bits = <12>;
>> diff --git a/Documentation/devicetree/bindings/usb/usb-phy.txt b/Documentation/devicetree/bindings/usb/usb-phy.txt
>> index 2466b6f..48761a2 100644
>> --- a/Documentation/devicetree/bindings/usb/usb-phy.txt
>> +++ b/Documentation/devicetree/bindings/usb/usb-phy.txt
>> @@ -4,9 +4,7 @@ OMAP USB2 PHY
>>
>>   Required properties:
>>    - compatible: Should be "ti,omap-usb2"
>> - - reg : Address and length of the register set for the device. Also
>> -add the address of control module dev conf register until a driver for
>> -control module is added
>> + - reg : Address and length of the register set for the device.
>>
>>   Optional properties:
>>    - ctrl_module : phandle of the control module used by PHY driver to power on
>> @@ -16,7 +14,6 @@ This is usually a subnode of ocp2scp to which it is connected.
>>
>>   usb2phy at 4a0ad080 {
>>   	compatible = "ti,omap-usb2";
>> -	reg = <0x4a0ad080 0x58>,
>> -	      <0x4a002300 0x4>;
>> +	reg = <0x4a0ad080 0x58>;
>>   	ctrl_module = <&omap_control_usb>;
>>   };
>> diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>> index 129d508..103f4ba 100644
>> --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>> +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
>> @@ -2698,13 +2698,6 @@ static struct resource omap44xx_usb_phy_and_pll_addrs[] = {
>>   		.end		= 0x4a0ae000,
>>   		.flags		= IORESOURCE_MEM,
>>   	},
>> -	{
>> -		/* XXX: Remove this once control module driver is in place */
>> -		.name		= "ctrl_dev",
>> -		.start		= 0x4a002300,
>> -		.end		= 0x4a002303,
>> -		.flags		= IORESOURCE_MEM,
>> -	},
>>   	{ }
>>   };
>>
>> @@ -6152,12 +6145,6 @@ static struct omap_hwmod_addr_space omap44xx_usb_otg_hs_addrs[] = {
>>   		.pa_end		= 0x4a0ab7ff,
>>   		.flags		= ADDR_TYPE_RT
>>   	},
>> -	{
>> -		/* XXX: Remove this once control module driver is in place */
>> -		.pa_start	= 0x4a00233c,
>> -		.pa_end		= 0x4a00233f,
>> -		.flags		= ADDR_TYPE_RT
>> -	},
>>   	{ }
>>   };
>>
>> diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
>> index 23a0b7f..de6e5ce 100644
>> --- a/drivers/usb/musb/Kconfig
>> +++ b/drivers/usb/musb/Kconfig
>> @@ -11,6 +11,7 @@ config USB_MUSB_HDRC
>>   	select NOP_USB_XCEIV if (SOC_TI81XX || SOC_AM33XX)
>>   	select TWL4030_USB if MACH_OMAP_3430SDP
>>   	select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
>> +	select OMAP_CONTROL_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
>>   	select USB_OTG_UTILS
>>   	help
>>   	  Say Y here if your system has a dual role high speed USB
>> diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
>> index da00af4..3e7ceef 100644
>> --- a/drivers/usb/musb/omap2430.c
>> +++ b/drivers/usb/musb/omap2430.c
>> @@ -37,6 +37,7 @@
>>   #include <linux/err.h>
>>   #include <linux/delay.h>
>>   #include <linux/usb/musb-omap.h>
>> +#include <linux/usb/omap_control_usb.h>
>>
>>   #include "musb_core.h"
>>   #include "omap2430.h"
>> @@ -46,7 +47,7 @@ struct omap2430_glue {
>>   	struct platform_device	*musb;
>>   	enum omap_musb_vbus_id_status status;
>>   	struct work_struct	omap_musb_mailbox_work;
>> -	u32 __iomem		*control_otghs;
>> +	struct device		*control_otghs;
>>   };
>>   #define glue_to_musb(g)		platform_get_drvdata(g->musb)
>>
>> @@ -54,26 +55,6 @@ struct omap2430_glue		*_glue;
>>
>>   static struct timer_list musb_idle_timer;
>>
>> -/**
>> - * omap4_usb_phy_mailbox - write to usb otg mailbox
>> - * @glue: struct omap2430_glue *
>> - * @val: the value to be written to the mailbox
>> - *
>> - * On detection of a device (ID pin is grounded), this API should be called
>> - * to set AVALID, VBUSVALID and ID pin is grounded.
>> - *
>> - * When OMAP is connected to a host (OMAP in device mode), this API
>> - * is called to set AVALID, VBUSVALID and ID pin in high impedance.
>> - *
>> - * XXX: This function will be removed once we have a seperate driver for
>> - * control module
>> - */
>> -static void omap4_usb_phy_mailbox(struct omap2430_glue *glue, u32 val)
>> -{
>> -	if (glue->control_otghs)
>> -		writel(val, glue->control_otghs);
>> -}
>> -
>>   static void musb_do_idle(unsigned long _musb)
>>   {
>>   	struct musb	*musb = (void *)_musb;
>> @@ -269,7 +250,6 @@ EXPORT_SYMBOL_GPL(omap_musb_mailbox);
>>
>>   static void omap_musb_set_mailbox(struct omap2430_glue *glue)
>>   {
>> -	u32 val;
>>   	struct musb *musb = glue_to_musb(glue);
>>   	struct device *dev = musb->controller;
>>   	struct musb_hdrc_platform_data *pdata = dev->platform_data;
>> @@ -285,8 +265,8 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
>>   		musb->xceiv->last_event = USB_EVENT_ID;
>>   		if (musb->gadget_driver) {
>>   			pm_runtime_get_sync(dev);
>> -			val = AVALID | VBUSVALID;
>> -			omap4_usb_phy_mailbox(glue, val);
>> +			if (!IS_ERR(glue->control_otghs))
>> +				omap_control_usb_host_mode(glue->control_otghs);
>
> you could introduce a helper to do the checks for you. something like:
>
> static inline void omap_control_set_mode(*ctrl, int mode)
> {
> 	if (IS_ERR(glue->control_otghs))
> 		return;
>
> 	switch (mode) {
> 	case HOST:
> 		omap_control_usb_host_mode(ctrl);
> 		break;
> 	case DEVICE:
> 		omap_control_usb_device(ctrl)
> 		break;
> 	default:
> 		meh();
> 	}
> }
>
> likewise for power() method

Ok.

Thanks
Kishon

^ permalink raw reply

* [PATCH 1/4] drivers: usb: phy: add a new driver for usb part of control module
From: kishon @ 2013-01-18 12:10 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130118115909.GA4672@arwen.pp.htv.fi>

Hi,

On Friday 18 January 2013 05:29 PM, Felipe Balbi wrote:
> Hi,
>
> On Fri, Jan 18, 2013 at 03:10:42PM +0530, Kishon Vijay Abraham I wrote:
>> Added a new driver for the usb part of control module. This has an API
>> to power on the USB2 phy and an API to write to the mailbox depending on
>> whether MUSB has to act in host mode or in device mode.
>>
>> Writing to control module registers for doing the above task which was
>> previously done in omap glue and in omap-usb2 phy will be removed.
>>
>> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
>> ---
>>   Documentation/devicetree/bindings/usb/omap-usb.txt |   26 ++-
>>   Documentation/devicetree/bindings/usb/usb-phy.txt  |    5 +
>>   drivers/usb/phy/Kconfig                            |    9 +
>>   drivers/usb/phy/Makefile                           |    1 +
>>   drivers/usb/phy/omap-control-usb.c                 |  204 ++++++++++++++++++++
>>   include/linux/usb/omap_control_usb.h               |   72 +++++++
>>   6 files changed, 316 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/usb/phy/omap-control-usb.c
>>   create mode 100644 include/linux/usb/omap_control_usb.h
>>
>> diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt
>> index 29a043e..ead6ba9 100644
>> --- a/Documentation/devicetree/bindings/usb/omap-usb.txt
>> +++ b/Documentation/devicetree/bindings/usb/omap-usb.txt
>> @@ -1,4 +1,4 @@
>> -OMAP GLUE
>> +OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS
>>
>>   OMAP MUSB GLUE
>>    - compatible : Should be "ti,omap4-musb" or "ti,omap3-musb"
>> @@ -16,6 +16,10 @@ OMAP MUSB GLUE
>>    - power : Should be "50". This signifies the controller can supply upto
>>      100mA when operating in host mode.
>>
>> +Optional properties:
>> + - ctrl_module : phandle of the control module this glue uses to write to
>> +   mailbox
>> +
>>   SOC specific device node entry
>>   usb_otg_hs: usb_otg_hs at 4a0ab000 {
>>   	compatible = "ti,omap4-musb";
>> @@ -23,6 +27,7 @@ usb_otg_hs: usb_otg_hs at 4a0ab000 {
>>   	multipoint = <1>;
>>   	num_eps = <16>;
>>   	ram_bits = <12>;
>> +	ctrl_module = <&omap_control_usb>;
>>   };
>>
>>   Board specific device node entry
>> @@ -31,3 +36,22 @@ Board specific device node entry
>>   	mode = <3>;
>>   	power = <50>;
>>   };
>> +
>> +OMAP CONTROL USB
>> +
>> +Required properties:
>> + - compatible: Should be "ti,omap-control-usb"
>> + - reg : Address and length of the register set for the device. It contains
>> +   the address of "control_dev_conf" and "otghs_control".
>> + - reg-names: The names of the register addresses corresponding to the registers
>> +   filled in "reg".
>> + - ti,has_mailbox: This is used to specify if the platform has mailbox in
>> +   control module.
>> +
>> +omap_control_usb: omap-control-usb at 4a002300 {
>> +	compatible = "ti,omap-control-usb";
>> +	reg = <0x4a002300 0x4>,
>> +	      <0x4a00233c 0x4>;
>> +	reg-names = "control_dev_conf", "otghs_control";
>> +	ti,has_mailbox;
>> +};
>> diff --git a/Documentation/devicetree/bindings/usb/usb-phy.txt b/Documentation/devicetree/bindings/usb/usb-phy.txt
>> index 80d4148..2466b6f 100644
>> --- a/Documentation/devicetree/bindings/usb/usb-phy.txt
>> +++ b/Documentation/devicetree/bindings/usb/usb-phy.txt
>> @@ -8,10 +8,15 @@ Required properties:
>>   add the address of control module dev conf register until a driver for
>>   control module is added
>>
>> +Optional properties:
>> + - ctrl_module : phandle of the control module used by PHY driver to power on
>> +   the PHY.
>> +
>>   This is usually a subnode of ocp2scp to which it is connected.
>>
>>   usb2phy at 4a0ad080 {
>>   	compatible = "ti,omap-usb2";
>>   	reg = <0x4a0ad080 0x58>,
>>   	      <0x4a002300 0x4>;
>> +	ctrl_module = <&omap_control_usb>;
>>   };
>> diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
>> index 5de6e7f..a7277ee 100644
>> --- a/drivers/usb/phy/Kconfig
>> +++ b/drivers/usb/phy/Kconfig
>> @@ -14,6 +14,15 @@ config OMAP_USB2
>>   	  The USB OTG controller communicates with the comparator using this
>>   	  driver.
>>
>> +config OMAP_CONTROL_USB
>> +	tristate "OMAP CONTROL USB Driver"
>> +	depends on ARCH_OMAP2PLUS
>> +	help
>> +	  Enable this to add support for the USB part present in the control
>> +	  module. This driver has API to power on the PHY and to write to the
>> +	  mailbox. The mailbox is present only in omap4 and the register to
>> +	  power on the PHY is present in omap4 and omap5.
>> +
>>   config USB_ISP1301
>>   	tristate "NXP ISP1301 USB transceiver support"
>>   	depends on USB || USB_GADGET
>> diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
>> index 1a579a8..0dea4d2 100644
>> --- a/drivers/usb/phy/Makefile
>> +++ b/drivers/usb/phy/Makefile
>> @@ -5,6 +5,7 @@
>>   ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG
>>
>>   obj-$(CONFIG_OMAP_USB2)			+= omap-usb2.o
>> +obj-$(CONFIG_OMAP_CONTROL_USB)		+= omap-control-usb.o
>>   obj-$(CONFIG_USB_ISP1301)		+= isp1301.o
>>   obj-$(CONFIG_MV_U3D_PHY)		+= mv_u3d_phy.o
>>   obj-$(CONFIG_USB_EHCI_TEGRA)	+= tegra_usb_phy.o
>> diff --git a/drivers/usb/phy/omap-control-usb.c b/drivers/usb/phy/omap-control-usb.c
>> new file mode 100644
>> index 0000000..7edeb41
>> --- /dev/null
>> +++ b/drivers/usb/phy/omap-control-usb.c
>> @@ -0,0 +1,204 @@
>> +/*
>> + * omap-control-usb.c - The USB part of control module.
>> + *
>> + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
>> + * 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.
>> + *
>> + * Author: Kishon Vijay Abraham I <kishon@ti.com>
>> + *
>> + * 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.
>> + *
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/of.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/usb/omap_control_usb.h>
>> +
>> +static struct omap_control_usb *control_usb;
>> +
>> +/**
>> + * get_omap_control_dev - returns the device pointer for this control device
>> + *
>> + * This API should be called to get the device pointer for this control
>> + * module device. This device pointer should be passed to all other API's
>> + * in this driver.
>> + *
>> + * To be used by PHY driver and glue driver
>> + */
>> +struct device *get_omap_control_dev(void)
>
> let's do a small rename here and call it:
>
> omap_get_control_dev(), just to keep consistency with rest of the file.

Ok.
>
>> +{
>> +	if (!control_usb)
>> +		return ERR_PTR(-ENODEV);
>> +
>> +	return control_usb->dev;
>> +}
>> +EXPORT_SYMBOL_GPL(get_omap_control_dev);
>> +
>> +/**
>> + * omap_control_usb_phy_power - power on/off the phy using control module reg
>> + * @dev: the control module device
>> + * @on: 0 or 1, based on powering on or off the PHY
>> + */
>> +void omap_control_usb_phy_power(struct device *dev, int on)
>> +{
>> +	u32 val;
>> +	struct omap_control_usb	*control_usb = dev_get_drvdata(dev);
>> +
>> +	if (on) {
>> +		val = readl(control_usb->dev_conf);
>> +		if (val & PHY_PD)
>> +			writel(~PHY_PD, control_usb->dev_conf);
>> +	} else {
>> +		writel(PHY_PD, control_usb->dev_conf);
>> +	}
>
> this can be better. I would rather have this:
>
> struct omap_control_usb *ctrl = dev_get_drvdata(dev);
> u32 val;
>
> val = readl(ctrl->dev_conf);
>
> if (on)
> 	val &= ~OMAP_CTRL_DEV_PHY_PD;
> else
> 	val |= OMAP_CTRL_DEV_PHY_PD;
>
> writel(val, ctrl->dev_conf);
>
>
>
>> +}
>> +EXPORT_SYMBOL_GPL(omap_control_usb_phy_power);
>> +
>> +/**
>> + * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded
>> + * @dev: struct device *
>> + *
>> + * This is an API to write to the mailbox register to notify the usb core that
>> + * a usb device has been connected.
>> + */
>> +void omap_control_usb_host_mode(struct device *dev)
>> +{
>> +	u32 val;
>> +	struct omap_control_usb	*control_usb = dev_get_drvdata(dev);
>> +
>> +	val = AVALID | VBUSVALID;
>> +
>> +	writel(val, control_usb->otghs_control);
>
> I would like to make this future proof too:
>
> val = readl(ctrl->otghs_control);
> val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID;
> writel(val, ctrl->otghs_control);

I think we might then have to add
val &= ~(IDDIG | SESSEND) right?

Similar things should be done for below API's also so that we don't 
inadvertently carry forward the settings.
>
>> +void omap_control_usb_device_mode(struct device *dev)
>> +{
>> +	u32 val;
>> +	struct omap_control_usb	*control_usb = dev_get_drvdata(dev);
>> +
>> +	val = IDDIG | AVALID | VBUSVALID;
>
> ditto. Read it before changing it. (the idea is that in future the other
> bits might be used for something else, so you risk breaking future
> functionality).
>
>> +
>> +	writel(val, control_usb->otghs_control);
>> +}
>> +EXPORT_SYMBOL_GPL(omap_control_usb_device_mode);
>> +
>> +/**
>> + * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high
>> + * impedance
>> + * @dev: struct device *
>> + *
>> + * This is an API to write to the mailbox register to notify the usb core
>> + * it's now in disconnected state.
>> + */
>> +void omap_control_usb_set_sessionend(struct device *dev)
>> +{
>> +	u32 val;
>> +	struct omap_control_usb	*control_usb = dev_get_drvdata(dev);
>> +
>> +	val = SESSEND | IDDIG;
>
> ditto
>
>> +
>> +	writel(val, control_usb->otghs_control);
>> +}
>> +EXPORT_SYMBOL_GPL(omap_control_usb_set_sessionend);
>> +
>> +static int omap_control_usb_probe(struct platform_device *pdev)
>> +{
>> +	struct resource	*res;
>> +	struct device_node *np = pdev->dev.of_node;
>> +	struct omap_control_usb_platform_data *pdata = pdev->dev.platform_data;
>> +
>> +	control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb),
>> +	    GFP_KERNEL);
>
> you're using space indentation here.
>
>> +	if (!control_usb) {
>> +		dev_err(&pdev->dev, "unable to alloc memory for control usb\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	if (np) {
>> +		control_usb->has_mailbox = of_property_read_bool(np,
>> +		    "ti,has_mailbox");
>> +	} else if (pdata) {
>> +		control_usb->has_mailbox = pdata->has_mailbox;
>> +	} else {
>> +		dev_err(&pdev->dev, "no pdata present\n");
>> +		return -EINVAL;
>
> does it make sense to default to has_mailbox as true ? I mean, seems
> like this is how it's going to be in future devices looking at the
> current trend.

No actually. omap5 doesn't have mailbox in control module ;-)
>
> Another idea is to try to get the extra resource below, if it fails you
> try to continue without it ;-)

I prefer the way it is now because mailbox is needed for MUSB to be 
functional in OMAP4 and we should fail if we don't have it. No?
>
>> +	}
>> +
>> +	control_usb->dev	= &pdev->dev;
>> +
>> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>> +	    "control_dev_conf");
>
> using space indentation here again.
>
>> +	control_usb->dev_conf = devm_request_and_ioremap(&pdev->dev, res);
>> +	if (control_usb->dev_conf == NULL) {
>> +		dev_err(&pdev->dev, "Failed to obtain io memory\n");
>> +		return -EADDRNOTAVAIL;
>> +	}
>> +
>> +	if (control_usb->has_mailbox) {
>> +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>> +		    "otghs_control");
>> +		control_usb->otghs_control = devm_request_and_ioremap(
>> +		    &pdev->dev, res);
>> +		if (control_usb->otghs_control == NULL) {
>> +			dev_err(&pdev->dev, "Failed to obtain io memory\n");
>> +			return -EADDRNOTAVAIL;
>> +		}
>> +	}
>> +
>> +	dev_set_drvdata(control_usb->dev, control_usb);
>> +
>> +	return 0;
>> +}
>> +
>> +#ifdef CONFIG_OF
>> +static const struct of_device_id omap_control_usb_id_table[] = {
>> +	{ .compatible = "ti,omap-control-usb" },
>> +	{}
>> +};
>> +MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
>> +#endif
>> +
>> +static struct platform_driver omap_control_usb_driver = {
>> +	.probe		= omap_control_usb_probe,
>> +	.driver		= {
>> +		.name	= "omap-control-usb",
>> +		.owner	= THIS_MODULE,
>
> no need to set owner here. When you register the platform_driver, it
> will be set for you.

Ok.
>
>> +		.of_match_table = of_match_ptr(omap_control_usb_id_table),
>> +	},
>> +};
>> +
>> +static int __init omap_control_usb_init(void)
>> +{
>> +	return platform_driver_register(&omap_control_usb_driver);
>> +}
>> +subsys_initcall(omap_control_usb_init);
>> +
>> +static void __exit omap_control_usb_exit(void)
>> +{
>> +	platform_driver_unregister(&omap_control_usb_driver);
>> +}
>> +module_exit(omap_control_usb_exit);
>> +
>> +MODULE_ALIAS("platform: omap_control_usb");
>> +MODULE_AUTHOR("Texas Instruments Inc.");
>> +MODULE_DESCRIPTION("OMAP CONTROL USB DRIVER");
>
> don't scream to your users like that ;-)
>
> MODULE_DESCRIPTION("OMAP Control Module USB Driver");

Ok.
>
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/usb/omap_control_usb.h b/include/linux/usb/omap_control_usb.h
>> new file mode 100644
>> index 0000000..a16f993
>> --- /dev/null
>> +++ b/include/linux/usb/omap_control_usb.h
>> @@ -0,0 +1,72 @@
>> +/*
>> + * omap_control_usb.h - Header file for the USB part of control module.
>> + *
>> + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
>> + * 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.
>> + *
>> + * Author: Kishon Vijay Abraham I <kishon@ti.com>
>> + *
>> + * 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 __OMAP_CONTROL_USB_H__
>> +#define __OMAP_CONTROL_USB_H__
>> +
>> +struct omap_control_usb {
>> +	struct device *dev;
>> +
>> +	u32 __iomem *dev_conf;
>> +	u32 __iomem *otghs_control;
>> +
>> +	u8 has_mailbox:1;
>> +};
>> +
>> +struct omap_control_usb_platform_data {
>> +	u8 has_mailbox:1;
>> +};
>> +
>> +#define	PHY_PD		BIT(0)
>> +
>> +#define	AVALID		BIT(0)
>> +#define	BVALID		BIT(1)
>> +#define	VBUSVALID	BIT(2)
>> +#define	SESSEND		BIT(3)
>> +#define	IDDIG		BIT(4)
>
> please prepend with OMAP_CTRL_ or something similar.

Sure.

Thanks
Kishon

^ permalink raw reply

* [PATCH 4/4] drivers: usb: start using the control module driver
From: Felipe Balbi @ 2013-01-18 12:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358502045-27008-5-git-send-email-kishon@ti.com>

Hi,

On Fri, Jan 18, 2013 at 03:10:45PM +0530, Kishon Vijay Abraham I wrote:
> Start using the control module driver for powering on the PHY and for
> writing to the mailbox instead of writing to the control module
> registers on their own.
> 
> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
> ---
>  Documentation/devicetree/bindings/usb/omap-usb.txt |    4 ++
>  Documentation/devicetree/bindings/usb/usb-phy.txt  |    7 +--
>  arch/arm/mach-omap2/omap_hwmod_44xx_data.c         |   13 ----
>  drivers/usb/musb/Kconfig                           |    1 +
>  drivers/usb/musb/omap2430.c                        |   64 ++++++++------------
>  drivers/usb/musb/omap2430.h                        |    9 ---
>  drivers/usb/phy/Kconfig                            |    1 +
>  drivers/usb/phy/omap-usb2.c                        |   38 +++---------
>  include/linux/usb/omap_usb.h                       |    4 +-
>  9 files changed, 42 insertions(+), 99 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt
> index ead6ba9..8bedbba 100644
> --- a/Documentation/devicetree/bindings/usb/omap-usb.txt
> +++ b/Documentation/devicetree/bindings/usb/omap-usb.txt
> @@ -3,6 +3,9 @@ OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS
>  OMAP MUSB GLUE
>   - compatible : Should be "ti,omap4-musb" or "ti,omap3-musb"
>   - ti,hwmods : must be "usb_otg_hs"
> + - ti,has_mailbox : to specify that omap uses an external mailbox
> +   (in control module) to communicate with the musb core during device connect
> +   and disconnect.
>   - multipoint : Should be "1" indicating the musb controller supports
>     multipoint. This is a MUSB configuration-specific setting.
>   - num_eps : Specifies the number of endpoints. This is also a
> @@ -24,6 +27,7 @@ SOC specific device node entry
>  usb_otg_hs: usb_otg_hs at 4a0ab000 {
>  	compatible = "ti,omap4-musb";
>  	ti,hwmods = "usb_otg_hs";
> +	ti,has_mailbox;
>  	multipoint = <1>;
>  	num_eps = <16>;
>  	ram_bits = <12>;
> diff --git a/Documentation/devicetree/bindings/usb/usb-phy.txt b/Documentation/devicetree/bindings/usb/usb-phy.txt
> index 2466b6f..48761a2 100644
> --- a/Documentation/devicetree/bindings/usb/usb-phy.txt
> +++ b/Documentation/devicetree/bindings/usb/usb-phy.txt
> @@ -4,9 +4,7 @@ OMAP USB2 PHY
>  
>  Required properties:
>   - compatible: Should be "ti,omap-usb2"
> - - reg : Address and length of the register set for the device. Also
> -add the address of control module dev conf register until a driver for
> -control module is added
> + - reg : Address and length of the register set for the device.
>  
>  Optional properties:
>   - ctrl_module : phandle of the control module used by PHY driver to power on
> @@ -16,7 +14,6 @@ This is usually a subnode of ocp2scp to which it is connected.
>  
>  usb2phy at 4a0ad080 {
>  	compatible = "ti,omap-usb2";
> -	reg = <0x4a0ad080 0x58>,
> -	      <0x4a002300 0x4>;
> +	reg = <0x4a0ad080 0x58>;
>  	ctrl_module = <&omap_control_usb>;
>  };
> diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
> index 129d508..103f4ba 100644
> --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
> +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
> @@ -2698,13 +2698,6 @@ static struct resource omap44xx_usb_phy_and_pll_addrs[] = {
>  		.end		= 0x4a0ae000,
>  		.flags		= IORESOURCE_MEM,
>  	},
> -	{
> -		/* XXX: Remove this once control module driver is in place */
> -		.name		= "ctrl_dev",
> -		.start		= 0x4a002300,
> -		.end		= 0x4a002303,
> -		.flags		= IORESOURCE_MEM,
> -	},
>  	{ }
>  };
>  
> @@ -6152,12 +6145,6 @@ static struct omap_hwmod_addr_space omap44xx_usb_otg_hs_addrs[] = {
>  		.pa_end		= 0x4a0ab7ff,
>  		.flags		= ADDR_TYPE_RT
>  	},
> -	{
> -		/* XXX: Remove this once control module driver is in place */
> -		.pa_start	= 0x4a00233c,
> -		.pa_end		= 0x4a00233f,
> -		.flags		= ADDR_TYPE_RT
> -	},
>  	{ }
>  };
>  
> diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
> index 23a0b7f..de6e5ce 100644
> --- a/drivers/usb/musb/Kconfig
> +++ b/drivers/usb/musb/Kconfig
> @@ -11,6 +11,7 @@ config USB_MUSB_HDRC
>  	select NOP_USB_XCEIV if (SOC_TI81XX || SOC_AM33XX)
>  	select TWL4030_USB if MACH_OMAP_3430SDP
>  	select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
> +	select OMAP_CONTROL_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
>  	select USB_OTG_UTILS
>  	help
>  	  Say Y here if your system has a dual role high speed USB
> diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
> index da00af4..3e7ceef 100644
> --- a/drivers/usb/musb/omap2430.c
> +++ b/drivers/usb/musb/omap2430.c
> @@ -37,6 +37,7 @@
>  #include <linux/err.h>
>  #include <linux/delay.h>
>  #include <linux/usb/musb-omap.h>
> +#include <linux/usb/omap_control_usb.h>
>  
>  #include "musb_core.h"
>  #include "omap2430.h"
> @@ -46,7 +47,7 @@ struct omap2430_glue {
>  	struct platform_device	*musb;
>  	enum omap_musb_vbus_id_status status;
>  	struct work_struct	omap_musb_mailbox_work;
> -	u32 __iomem		*control_otghs;
> +	struct device		*control_otghs;
>  };
>  #define glue_to_musb(g)		platform_get_drvdata(g->musb)
>  
> @@ -54,26 +55,6 @@ struct omap2430_glue		*_glue;
>  
>  static struct timer_list musb_idle_timer;
>  
> -/**
> - * omap4_usb_phy_mailbox - write to usb otg mailbox
> - * @glue: struct omap2430_glue *
> - * @val: the value to be written to the mailbox
> - *
> - * On detection of a device (ID pin is grounded), this API should be called
> - * to set AVALID, VBUSVALID and ID pin is grounded.
> - *
> - * When OMAP is connected to a host (OMAP in device mode), this API
> - * is called to set AVALID, VBUSVALID and ID pin in high impedance.
> - *
> - * XXX: This function will be removed once we have a seperate driver for
> - * control module
> - */
> -static void omap4_usb_phy_mailbox(struct omap2430_glue *glue, u32 val)
> -{
> -	if (glue->control_otghs)
> -		writel(val, glue->control_otghs);
> -}
> -
>  static void musb_do_idle(unsigned long _musb)
>  {
>  	struct musb	*musb = (void *)_musb;
> @@ -269,7 +250,6 @@ EXPORT_SYMBOL_GPL(omap_musb_mailbox);
>  
>  static void omap_musb_set_mailbox(struct omap2430_glue *glue)
>  {
> -	u32 val;
>  	struct musb *musb = glue_to_musb(glue);
>  	struct device *dev = musb->controller;
>  	struct musb_hdrc_platform_data *pdata = dev->platform_data;
> @@ -285,8 +265,8 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
>  		musb->xceiv->last_event = USB_EVENT_ID;
>  		if (musb->gadget_driver) {
>  			pm_runtime_get_sync(dev);
> -			val = AVALID | VBUSVALID;
> -			omap4_usb_phy_mailbox(glue, val);
> +			if (!IS_ERR(glue->control_otghs))
> +				omap_control_usb_host_mode(glue->control_otghs);

you could introduce a helper to do the checks for you. something like:

static inline void omap_control_set_mode(*ctrl, int mode)
{
	if (IS_ERR(glue->control_otghs))
		return;

	switch (mode) {
	case HOST:
		omap_control_usb_host_mode(ctrl);
		break;
	case DEVICE:
		omap_control_usb_device(ctrl)
		break;
	default:
		meh();
	}
}

likewise for power() method

-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130118/18b3d6d3/attachment.sig>

^ permalink raw reply

* [PATCH 2/4] ARM: OMAP: devices: create device for usb part of control module
From: Felipe Balbi @ 2013-01-18 12:00 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358502045-27008-3-git-send-email-kishon@ti.com>

On Fri, Jan 18, 2013 at 03:10:43PM +0530, Kishon Vijay Abraham I wrote:
> A seperate driver has been added to handle the usb part of control
> module. A device for the above driver is created here, using the register
> address information to be used by the driver for powering on the PHY and
> for writing to the mailbox.
> 
> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
> ---
>  arch/arm/mach-omap2/devices.c |   48 +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 48 insertions(+)
> 
> diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
> index 5e304d0..a58b0ce 100644
> --- a/arch/arm/mach-omap2/devices.c
> +++ b/arch/arm/mach-omap2/devices.c
> @@ -20,6 +20,7 @@
>  #include <linux/pinctrl/machine.h>
>  #include <linux/platform_data/omap4-keypad.h>
>  #include <linux/platform_data/omap_ocp2scp.h>
> +#include <linux/usb/omap_control_usb.h>
>  
>  #include <asm/mach-types.h>
>  #include <asm/mach/map.h>
> @@ -254,6 +255,52 @@ static inline void omap_init_camera(void)
>  #endif
>  }
>  
> +#if IS_ENABLED(CONFIG_OMAP_CONTROL_USB)
> +static struct omap_control_usb_platform_data omap4_control_usb_pdata = {
> +	.has_mailbox = true,
> +};
> +
> +struct resource omap4_control_usb_res[] = {
> +	{
> +		.name	= "control_dev_conf",
> +		.start	= 0x4a002300,
> +		.end	= 0x4a002303,
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	{
> +		.name	= "otghs_control",
> +		.start	= 0x4a00233c,
> +		.end	= 0x4a00233f,
> +		.flags	= IORESOURCE_MEM,
> +	},
> +};
> +
> +static struct platform_device omap4_control_usb = {
> +	.name		= "omap-control-usb",
> +	.id		= -1,
> +	.dev = {
> +		.platform_data = &omap4_control_usb_pdata,
> +	},
> +	.num_resources = 2,
> +	.resource = omap4_control_usb_res,
> +};
> +
> +static inline void __init omap_init_control_usb(void)
> +{
> +	int ret = 0;
> +
> +	if (cpu_is_omap44xx()) {
> +		ret = platform_device_register(&omap4_control_usb);
> +		if (ret)
> +			pr_err("Error registering omap_control_usb device:%d\n",
> +			    ret);
> +	}

you can decrease indentation if you invert the check:

if (!cpu_is_omap44xx())
	return;

if (platform_device_register(.....)
-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130118/5be607ef/attachment.sig>

^ permalink raw reply

* [PATCH 1/4] drivers: usb: phy: add a new driver for usb part of control module
From: Felipe Balbi @ 2013-01-18 11:59 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358502045-27008-2-git-send-email-kishon@ti.com>

Hi,

On Fri, Jan 18, 2013 at 03:10:42PM +0530, Kishon Vijay Abraham I wrote:
> Added a new driver for the usb part of control module. This has an API
> to power on the USB2 phy and an API to write to the mailbox depending on
> whether MUSB has to act in host mode or in device mode.
> 
> Writing to control module registers for doing the above task which was
> previously done in omap glue and in omap-usb2 phy will be removed.
> 
> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
> ---
>  Documentation/devicetree/bindings/usb/omap-usb.txt |   26 ++-
>  Documentation/devicetree/bindings/usb/usb-phy.txt  |    5 +
>  drivers/usb/phy/Kconfig                            |    9 +
>  drivers/usb/phy/Makefile                           |    1 +
>  drivers/usb/phy/omap-control-usb.c                 |  204 ++++++++++++++++++++
>  include/linux/usb/omap_control_usb.h               |   72 +++++++
>  6 files changed, 316 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/usb/phy/omap-control-usb.c
>  create mode 100644 include/linux/usb/omap_control_usb.h
> 
> diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt
> index 29a043e..ead6ba9 100644
> --- a/Documentation/devicetree/bindings/usb/omap-usb.txt
> +++ b/Documentation/devicetree/bindings/usb/omap-usb.txt
> @@ -1,4 +1,4 @@
> -OMAP GLUE
> +OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS
>  
>  OMAP MUSB GLUE
>   - compatible : Should be "ti,omap4-musb" or "ti,omap3-musb"
> @@ -16,6 +16,10 @@ OMAP MUSB GLUE
>   - power : Should be "50". This signifies the controller can supply upto
>     100mA when operating in host mode.
>  
> +Optional properties:
> + - ctrl_module : phandle of the control module this glue uses to write to
> +   mailbox
> +
>  SOC specific device node entry
>  usb_otg_hs: usb_otg_hs at 4a0ab000 {
>  	compatible = "ti,omap4-musb";
> @@ -23,6 +27,7 @@ usb_otg_hs: usb_otg_hs at 4a0ab000 {
>  	multipoint = <1>;
>  	num_eps = <16>;
>  	ram_bits = <12>;
> +	ctrl_module = <&omap_control_usb>;
>  };
>  
>  Board specific device node entry
> @@ -31,3 +36,22 @@ Board specific device node entry
>  	mode = <3>;
>  	power = <50>;
>  };
> +
> +OMAP CONTROL USB
> +
> +Required properties:
> + - compatible: Should be "ti,omap-control-usb"
> + - reg : Address and length of the register set for the device. It contains
> +   the address of "control_dev_conf" and "otghs_control".
> + - reg-names: The names of the register addresses corresponding to the registers
> +   filled in "reg".
> + - ti,has_mailbox: This is used to specify if the platform has mailbox in
> +   control module.
> +
> +omap_control_usb: omap-control-usb at 4a002300 {
> +	compatible = "ti,omap-control-usb";
> +	reg = <0x4a002300 0x4>,
> +	      <0x4a00233c 0x4>;
> +	reg-names = "control_dev_conf", "otghs_control";
> +	ti,has_mailbox;
> +};
> diff --git a/Documentation/devicetree/bindings/usb/usb-phy.txt b/Documentation/devicetree/bindings/usb/usb-phy.txt
> index 80d4148..2466b6f 100644
> --- a/Documentation/devicetree/bindings/usb/usb-phy.txt
> +++ b/Documentation/devicetree/bindings/usb/usb-phy.txt
> @@ -8,10 +8,15 @@ Required properties:
>  add the address of control module dev conf register until a driver for
>  control module is added
>  
> +Optional properties:
> + - ctrl_module : phandle of the control module used by PHY driver to power on
> +   the PHY.
> +
>  This is usually a subnode of ocp2scp to which it is connected.
>  
>  usb2phy at 4a0ad080 {
>  	compatible = "ti,omap-usb2";
>  	reg = <0x4a0ad080 0x58>,
>  	      <0x4a002300 0x4>;
> +	ctrl_module = <&omap_control_usb>;
>  };
> diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
> index 5de6e7f..a7277ee 100644
> --- a/drivers/usb/phy/Kconfig
> +++ b/drivers/usb/phy/Kconfig
> @@ -14,6 +14,15 @@ config OMAP_USB2
>  	  The USB OTG controller communicates with the comparator using this
>  	  driver.
>  
> +config OMAP_CONTROL_USB
> +	tristate "OMAP CONTROL USB Driver"
> +	depends on ARCH_OMAP2PLUS
> +	help
> +	  Enable this to add support for the USB part present in the control
> +	  module. This driver has API to power on the PHY and to write to the
> +	  mailbox. The mailbox is present only in omap4 and the register to
> +	  power on the PHY is present in omap4 and omap5.
> +
>  config USB_ISP1301
>  	tristate "NXP ISP1301 USB transceiver support"
>  	depends on USB || USB_GADGET
> diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
> index 1a579a8..0dea4d2 100644
> --- a/drivers/usb/phy/Makefile
> +++ b/drivers/usb/phy/Makefile
> @@ -5,6 +5,7 @@
>  ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG
>  
>  obj-$(CONFIG_OMAP_USB2)			+= omap-usb2.o
> +obj-$(CONFIG_OMAP_CONTROL_USB)		+= omap-control-usb.o
>  obj-$(CONFIG_USB_ISP1301)		+= isp1301.o
>  obj-$(CONFIG_MV_U3D_PHY)		+= mv_u3d_phy.o
>  obj-$(CONFIG_USB_EHCI_TEGRA)	+= tegra_usb_phy.o
> diff --git a/drivers/usb/phy/omap-control-usb.c b/drivers/usb/phy/omap-control-usb.c
> new file mode 100644
> index 0000000..7edeb41
> --- /dev/null
> +++ b/drivers/usb/phy/omap-control-usb.c
> @@ -0,0 +1,204 @@
> +/*
> + * omap-control-usb.c - The USB part of control module.
> + *
> + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
> + * 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.
> + *
> + * Author: Kishon Vijay Abraham I <kishon@ti.com>
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/usb/omap_control_usb.h>
> +
> +static struct omap_control_usb *control_usb;
> +
> +/**
> + * get_omap_control_dev - returns the device pointer for this control device
> + *
> + * This API should be called to get the device pointer for this control
> + * module device. This device pointer should be passed to all other API's
> + * in this driver.
> + *
> + * To be used by PHY driver and glue driver
> + */
> +struct device *get_omap_control_dev(void)

let's do a small rename here and call it:

omap_get_control_dev(), just to keep consistency with rest of the file.

> +{
> +	if (!control_usb)
> +		return ERR_PTR(-ENODEV);
> +
> +	return control_usb->dev;
> +}
> +EXPORT_SYMBOL_GPL(get_omap_control_dev);
> +
> +/**
> + * omap_control_usb_phy_power - power on/off the phy using control module reg
> + * @dev: the control module device
> + * @on: 0 or 1, based on powering on or off the PHY
> + */
> +void omap_control_usb_phy_power(struct device *dev, int on)
> +{
> +	u32 val;
> +	struct omap_control_usb	*control_usb = dev_get_drvdata(dev);
> +
> +	if (on) {
> +		val = readl(control_usb->dev_conf);
> +		if (val & PHY_PD)
> +			writel(~PHY_PD, control_usb->dev_conf);
> +	} else {
> +		writel(PHY_PD, control_usb->dev_conf);
> +	}

this can be better. I would rather have this:

struct omap_control_usb *ctrl = dev_get_drvdata(dev);
u32 val;

val = readl(ctrl->dev_conf);

if (on)
	val &= ~OMAP_CTRL_DEV_PHY_PD;
else
	val |= OMAP_CTRL_DEV_PHY_PD;

writel(val, ctrl->dev_conf);



> +}
> +EXPORT_SYMBOL_GPL(omap_control_usb_phy_power);
> +
> +/**
> + * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded
> + * @dev: struct device *
> + *
> + * This is an API to write to the mailbox register to notify the usb core that
> + * a usb device has been connected.
> + */
> +void omap_control_usb_host_mode(struct device *dev)
> +{
> +	u32 val;
> +	struct omap_control_usb	*control_usb = dev_get_drvdata(dev);
> +
> +	val = AVALID | VBUSVALID;
> +
> +	writel(val, control_usb->otghs_control);

I would like to make this future proof too:

val = readl(ctrl->otghs_control);
val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID;
writel(val, ctrl->otghs_control);

> +void omap_control_usb_device_mode(struct device *dev)
> +{
> +	u32 val;
> +	struct omap_control_usb	*control_usb = dev_get_drvdata(dev);
> +
> +	val = IDDIG | AVALID | VBUSVALID;

ditto. Read it before changing it. (the idea is that in future the other
bits might be used for something else, so you risk breaking future
functionality).

> +
> +	writel(val, control_usb->otghs_control);
> +}
> +EXPORT_SYMBOL_GPL(omap_control_usb_device_mode);
> +
> +/**
> + * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high
> + * impedance
> + * @dev: struct device *
> + *
> + * This is an API to write to the mailbox register to notify the usb core
> + * it's now in disconnected state.
> + */
> +void omap_control_usb_set_sessionend(struct device *dev)
> +{
> +	u32 val;
> +	struct omap_control_usb	*control_usb = dev_get_drvdata(dev);
> +
> +	val = SESSEND | IDDIG;

ditto

> +
> +	writel(val, control_usb->otghs_control);
> +}
> +EXPORT_SYMBOL_GPL(omap_control_usb_set_sessionend);
> +
> +static int omap_control_usb_probe(struct platform_device *pdev)
> +{
> +	struct resource	*res;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct omap_control_usb_platform_data *pdata = pdev->dev.platform_data;
> +
> +	control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb),
> +	    GFP_KERNEL);

you're using space indentation here.

> +	if (!control_usb) {
> +		dev_err(&pdev->dev, "unable to alloc memory for control usb\n");
> +		return -ENOMEM;
> +	}
> +
> +	if (np) {
> +		control_usb->has_mailbox = of_property_read_bool(np,
> +		    "ti,has_mailbox");
> +	} else if (pdata) {
> +		control_usb->has_mailbox = pdata->has_mailbox;
> +	} else {
> +		dev_err(&pdev->dev, "no pdata present\n");
> +		return -EINVAL;

does it make sense to default to has_mailbox as true ? I mean, seems
like this is how it's going to be in future devices looking at the
current trend.

Another idea is to try to get the extra resource below, if it fails you
try to continue without it ;-)

> +	}
> +
> +	control_usb->dev	= &pdev->dev;
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +	    "control_dev_conf");

using space indentation here again.

> +	control_usb->dev_conf = devm_request_and_ioremap(&pdev->dev, res);
> +	if (control_usb->dev_conf == NULL) {
> +		dev_err(&pdev->dev, "Failed to obtain io memory\n");
> +		return -EADDRNOTAVAIL;
> +	}
> +
> +	if (control_usb->has_mailbox) {
> +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +		    "otghs_control");
> +		control_usb->otghs_control = devm_request_and_ioremap(
> +		    &pdev->dev, res);
> +		if (control_usb->otghs_control == NULL) {
> +			dev_err(&pdev->dev, "Failed to obtain io memory\n");
> +			return -EADDRNOTAVAIL;
> +		}
> +	}
> +
> +	dev_set_drvdata(control_usb->dev, control_usb);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id omap_control_usb_id_table[] = {
> +	{ .compatible = "ti,omap-control-usb" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
> +#endif
> +
> +static struct platform_driver omap_control_usb_driver = {
> +	.probe		= omap_control_usb_probe,
> +	.driver		= {
> +		.name	= "omap-control-usb",
> +		.owner	= THIS_MODULE,

no need to set owner here. When you register the platform_driver, it
will be set for you.

> +		.of_match_table = of_match_ptr(omap_control_usb_id_table),
> +	},
> +};
> +
> +static int __init omap_control_usb_init(void)
> +{
> +	return platform_driver_register(&omap_control_usb_driver);
> +}
> +subsys_initcall(omap_control_usb_init);
> +
> +static void __exit omap_control_usb_exit(void)
> +{
> +	platform_driver_unregister(&omap_control_usb_driver);
> +}
> +module_exit(omap_control_usb_exit);
> +
> +MODULE_ALIAS("platform: omap_control_usb");
> +MODULE_AUTHOR("Texas Instruments Inc.");
> +MODULE_DESCRIPTION("OMAP CONTROL USB DRIVER");

don't scream to your users like that ;-)

MODULE_DESCRIPTION("OMAP Control Module USB Driver");

> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/usb/omap_control_usb.h b/include/linux/usb/omap_control_usb.h
> new file mode 100644
> index 0000000..a16f993
> --- /dev/null
> +++ b/include/linux/usb/omap_control_usb.h
> @@ -0,0 +1,72 @@
> +/*
> + * omap_control_usb.h - Header file for the USB part of control module.
> + *
> + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
> + * 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.
> + *
> + * Author: Kishon Vijay Abraham I <kishon@ti.com>
> + *
> + * 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 __OMAP_CONTROL_USB_H__
> +#define __OMAP_CONTROL_USB_H__
> +
> +struct omap_control_usb {
> +	struct device *dev;
> +
> +	u32 __iomem *dev_conf;
> +	u32 __iomem *otghs_control;
> +
> +	u8 has_mailbox:1;
> +};
> +
> +struct omap_control_usb_platform_data {
> +	u8 has_mailbox:1;
> +};
> +
> +#define	PHY_PD		BIT(0)
> +
> +#define	AVALID		BIT(0)
> +#define	BVALID		BIT(1)
> +#define	VBUSVALID	BIT(2)
> +#define	SESSEND		BIT(3)
> +#define	IDDIG		BIT(4)

please prepend with OMAP_CTRL_ or something similar.

-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130118/591f58b0/attachment-0001.sig>

^ permalink raw reply

* [RFC PATCH 0/6] USB: Add multiple PHYs of same type
From: kishon @ 2013-01-18 11:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130118114859.GC4379@arwen.pp.htv.fi>

On Friday 18 January 2013 05:18 PM, Felipe Balbi wrote:
> On Wed, Jan 16, 2013 at 08:30:56PM +0530, Kishon Vijay Abraham I wrote:
>> New platforms are being added which has multiple PHY's (of same type) and
>> which has multiple USB controllers. The binding information has to be
>> present in the PHY library (otg.c) in order for it to return the
>> appropriate PHY whenever the USB controller request for the PHY. So
>> added a new API to pass the binding information. This API should be
>> called by platform specific initialization code.
>>
>> So the binding should be done something like
>> usb_bind_phy("musb-hdrc.0.auto", 0, "omap-usb2.1.auto"); specifying the USB
>> controller device name, index, and the PHY device name.
>> I have done this binding for OMAP platforms, but it should be done for
>> all the platforms.
>>
>> After this design, the phy can be got by passing the USB controller device
>> pointer and the index.
>>
>> Developed this patch series on
>> git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git xceiv
>> after applying "usb: musb: add driver for control module" patch series.
>>
>> Did basic enumeration testing in omap4 panda, omap4 sdp and omap3 beagle.
>
> please resend without RFC so I can apply.

I'll resend after addressing Roger's comments in a while.

Thanks
Kishon

^ permalink raw reply

* [RFC PATCH 0/6] USB: Add multiple PHYs of same type
From: Felipe Balbi @ 2013-01-18 11:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358348462-27693-1-git-send-email-kishon@ti.com>

On Wed, Jan 16, 2013 at 08:30:56PM +0530, Kishon Vijay Abraham I wrote:
> New platforms are being added which has multiple PHY's (of same type) and
> which has multiple USB controllers. The binding information has to be
> present in the PHY library (otg.c) in order for it to return the
> appropriate PHY whenever the USB controller request for the PHY. So
> added a new API to pass the binding information. This API should be
> called by platform specific initialization code.
> 
> So the binding should be done something like
> usb_bind_phy("musb-hdrc.0.auto", 0, "omap-usb2.1.auto"); specifying the USB
> controller device name, index, and the PHY device name.
> I have done this binding for OMAP platforms, but it should be done for
> all the platforms.
> 
> After this design, the phy can be got by passing the USB controller device
> pointer and the index.
> 
> Developed this patch series on
> git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git xceiv
> after applying "usb: musb: add driver for control module" patch series.
> 
> Did basic enumeration testing in omap4 panda, omap4 sdp and omap3 beagle.

please resend without RFC so I can apply.

-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130118/b16f5fe2/attachment.sig>

^ permalink raw reply

* [RFC PATCH 1/6] usb: otg: Add an API to bind the USB controller and PHY
From: Felipe Balbi @ 2013-01-18 11:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <50F7DD2C.90500@ti.com>

On Thu, Jan 17, 2013 at 04:44:52PM +0530, kishon wrote:
> >>@@ -171,6 +188,11 @@ static inline void devm_usb_put_phy(struct device *dev, struct usb_phy *x)
> >>  {
> >>  }
> >>
> >>+static inline struct usb_phy_bind *usb_bind_phy(const char *dev_name, u8 index,
> >>+				const char *phy_dev_name)
> >>+{
> >>+	return NULL;
> >>+}
> >>  #endif
> >>
> >>  static inline int
> >>
> >
> >Controllers like ehci-omap which don't need OTG functionality would
> >benefit from this API. Can we make these PHY APIs not dependent on OTG /
> >OTG_UTILS?
> 
> Actually much of whatever is in otg.c can be used by controllers
> which don't have OTG functionality (except otg_state_string). I
> vaguely remember, there was a patch that renamed otg.c to phy.c etc..
> I'm not sure what happened to that.

right, that has to be done eventually ;-)

-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130118/b6f7337c/attachment.sig>

^ permalink raw reply

* [RFC PATCH 0/7] usb: musb: add driver for control module
From: Felipe Balbi @ 2013-01-18 11:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358239378-10030-1-git-send-email-kishon@ti.com>

Hi,

On Tue, Jan 15, 2013 at 02:12:51PM +0530, Kishon Vijay Abraham I wrote:
> Added a new driver for the usb part of control module. This has an API
> to power on the USB2 phy and an API to write to the mailbox depending on
> whether MUSB has to act in host mode or in device mode.
> 
> Writing to control module registers for doing the above task which was
> previously done in omap glue and in omap-usb2 phy is removed.
> 
> Also added the dt data to get MUSB working in OMAP platforms.
> This series has patches for both drivers and ARCH folders, so If it has to
> be split I'll do it.
> 
> This series was developed on
> git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git xceiv
> 
> Did basic enumeration testing in omap4 beagle, omap4 sdp and omap3 beagle.

can you resend without RFC tag so I can apply ?

Thank you

-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130118/353d7494/attachment-0001.sig>

^ permalink raw reply

* [PATCH RFC 2/2] Improve bios32 support for DT PCI host bridge controllers
From: Russell King - ARM Linux @ 2013-01-18 11:43 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130118114019.GA9034@arm.com>

On Fri, Jan 18, 2013 at 11:40:19AM +0000, Andrew Murray wrote:
>  static void __init pcibios_init_hw(struct hw_pci *hw, struct list_head *head)
>  {
>  	struct pci_sys_data *sys = NULL;
> +	static int busnr;
>  	int ret;
> -	int nr, busnr;
> -
> -	for (nr = busnr = 0; nr < hw->nr_controllers; nr++) {
> +	int nr;
> +	for (nr = 0; nr < hw->nr_controllers; nr++) {

Please try to retain the originally formatting.  There should be a blank
line between "int nr;" and "for...".

^ permalink raw reply

* [PATCH RFC 2/2] Improve bios32 support for DT PCI host bridge controllers
From: Andrew Murray @ 2013-01-18 11:40 UTC (permalink / raw)
  To: linux-arm-kernel

Continuing from discussion with Thierry (lkml.org/lkml/2013/1/18/107) perhaps
this will be useful to fold into your patchset - you may wish to remove the
overlap. 
---
This patch attempts to overcome two difficulities when providing DT PCI host
bridge controllers:

At present PCI controllers are registered via the pci_common_init call, this
results in callbacks (arch/arm/include/asm/mach/pci.h) which are used to setup
the controller. However there is no trivial way to pass a device_node to the
callbacks which is known at the time of calling pci_common_init. This is
required in order to add pci resources (pci_add_resource_offset) based on
information obtained from the device tree. This patch updates the hw_pci and
pci_sys_data structures such that drivers can provide a device_node to
pci_common_init and access it through the pci_sys_data argument of the
callbacks.

Additionally bios32 makes an assumption that all host controllers are
registered at the same time and handled by the same driver. This patch provides
support for calling pci_common_init multiple times to allow for one at a time
registration of PCI host controllers.

It also adds support for setting up of PCIe MPS and MRRS.

Signed-off-by: Andrew Murray <Andrew.Murray@arm.com>
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
---
 arch/arm/include/asm/mach/pci.h |    2 ++
 arch/arm/kernel/bios32.c        |   29 ++++++++++++++++++++++++-----
 2 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/arch/arm/include/asm/mach/pci.h b/arch/arm/include/asm/mach/pci.h
index 26c511f..845a6b7 100644
--- a/arch/arm/include/asm/mach/pci.h
+++ b/arch/arm/include/asm/mach/pci.h
@@ -27,6 +27,7 @@ struct hw_pci {
 	void		(*postinit)(void);
 	u8		(*swizzle)(struct pci_dev *dev, u8 *pin);
 	int		(*map_irq)(const struct pci_dev *dev, u8 slot, u8 pin);
+	struct device_node *of_node;
 };
 
 /*
@@ -47,6 +48,7 @@ struct pci_sys_data {
 					/* IRQ mapping				*/
 	int		(*map_irq)(const struct pci_dev *, u8, u8);
 	void		*private_data;	/* platform controller private data	*/
+	struct device_node *of_node;	/* device tree node			*/
 };
 
 /*
diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c
index 2b2f25e..bde4630 100644
--- a/arch/arm/kernel/bios32.c
+++ b/arch/arm/kernel/bios32.c
@@ -11,6 +11,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/io.h>
+#include <linux/of.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/pci.h>
@@ -426,10 +427,10 @@ static int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 static void __init pcibios_init_hw(struct hw_pci *hw, struct list_head *head)
 {
 	struct pci_sys_data *sys = NULL;
+	static int busnr;
 	int ret;
-	int nr, busnr;
-
-	for (nr = busnr = 0; nr < hw->nr_controllers; nr++) {
+	int nr;
+	for (nr = 0; nr < hw->nr_controllers; nr++) {
 		sys = kzalloc(sizeof(struct pci_sys_data), GFP_KERNEL);
 		if (!sys)
 			panic("PCI: unable to allocate sys data!");
@@ -440,6 +441,7 @@ static void __init pcibios_init_hw(struct hw_pci *hw, struct list_head *head)
 		sys->busnr   = busnr;
 		sys->swizzle = hw->swizzle;
 		sys->map_irq = hw->map_irq;
+		sys->of_node = hw->of_node;
 		INIT_LIST_HEAD(&sys->resources);
 
 		ret = hw->setup(nr, sys);
@@ -484,10 +486,11 @@ void __init pci_common_init(struct hw_pci *hw)
 	if (hw->postinit)
 		hw->postinit();
 
-	pci_fixup_irqs(pcibios_swizzle, pcibios_map_irq);
-
 	list_for_each_entry(sys, &head, node) {
 		struct pci_bus *bus = sys->bus;
+		struct pci_bus *child;
+
+		pci_bus_fixup_irqs(bus, pcibios_swizzle, pcibios_map_irq);
 
 		if (!pci_has_flag(PCI_PROBE_ONLY)) {
 			/*
@@ -504,6 +507,16 @@ void __init pci_common_init(struct hw_pci *hw)
 			 * Enable bridges
 			 */
 			pci_enable_bridges(bus);
+
+			/*
+			 * Configure children (MPS, MRRS)
+			 */
+			list_for_each_entry(child, &bus->children, node) {
+				struct pci_dev *self = child->self;
+				if (!self)
+					continue;
+				pcie_bus_configure_settings(child, self->pcie_mpss);
+			}
 		}
 
 		/*
@@ -627,3 +640,9 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
 
 	return 0;
 }
+
+struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus)
+{
+	struct pci_sys_data *sys = bus->sysdata;
+	return of_node_get(sys->of_node);
+}
-- 
1.7.0.4

^ permalink raw reply related

* [PATCH 4/4] mfd: db8500-prcmu: add ux500_wdt mfd_cell
From: Fabio Baltieri @ 2013-01-18 11:40 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358509214-22407-1-git-send-email-fabio.baltieri@linaro.org>

This patch adds the necessary structures to use the watchdog
functionality of PRCMU.

The watchdog driver is named ux500_wdt.

Acked-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
---
 drivers/mfd/db8500-prcmu.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index 7a63fa8..e42a417 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -32,6 +32,7 @@
 #include <linux/regulator/db8500-prcmu.h>
 #include <linux/regulator/machine.h>
 #include <linux/cpufreq.h>
+#include <linux/platform_data/ux500_wdt.h>
 #include <asm/hardware/gic.h>
 #include <mach/hardware.h>
 #include <mach/irqs.h>
@@ -3074,6 +3075,11 @@ static struct resource ab8500_resources[] = {
 	}
 };
 
+static struct ux500_wdt_data db8500_wdt_pdata = {
+	.timeout = 600, /* 10 minutes */
+	.has_28_bits_resolution = true,
+};
+
 static struct mfd_cell db8500_prcmu_devs[] = {
 	{
 		.name = "db8500-prcmu-regulators",
@@ -3088,6 +3094,12 @@ static struct mfd_cell db8500_prcmu_devs[] = {
 		.pdata_size = sizeof(db8500_cpufreq_table),
 	},
 	{
+		.name = "ux500_wdt",
+		.platform_data = &db8500_wdt_pdata,
+		.pdata_size = sizeof(db8500_wdt_pdata),
+		.id = -1,
+	},
+	{
 		.name = "ab8500-core",
 		.of_compatible = "stericsson,ab8500",
 		.num_resources = ARRAY_SIZE(ab8500_resources),
-- 
1.7.12.1

^ permalink raw reply related

* [PATCH 3/4] watchdog: add support for ux500_wdt watchdog
From: Fabio Baltieri @ 2013-01-18 11:40 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1358509214-22407-1-git-send-email-fabio.baltieri@linaro.org>

This patch adds support for the ux500_wdt watchdog that is found in
ST-Ericsson Ux500 platform.  The driver is based on PRCMU APIs.

Acked-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
---
 drivers/watchdog/Kconfig                |  12 +++
 drivers/watchdog/Makefile               |   1 +
 drivers/watchdog/ux500_wdt.c            | 171 ++++++++++++++++++++++++++++++++
 include/linux/platform_data/ux500_wdt.h |  19 ++++
 4 files changed, 203 insertions(+)
 create mode 100644 drivers/watchdog/ux500_wdt.c
 create mode 100644 include/linux/platform_data/ux500_wdt.h

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 7f809fd..26e1fdb 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -364,6 +364,18 @@ config IMX2_WDT
 	  To compile this driver as a module, choose M here: the
 	  module will be called imx2_wdt.
 
+config UX500_WATCHDOG
+	tristate "ST-Ericsson Ux500 watchdog"
+	depends on MFD_DB8500_PRCMU
+	select WATCHDOG_CORE
+	default y
+	help
+	  Say Y here to include Watchdog timer support for the watchdog
+	  existing in the prcmu of ST-Ericsson Ux500 series platforms.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ux500_wdt.
+
 # AVR32 Architecture
 
 config AT32AP700X_WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 97bbdb3a..bec86ee 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
 obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
 obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
 obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
+obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
 
 # AVR32 Architecture
 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/ux500_wdt.c
new file mode 100644
index 0000000..dd608db
--- /dev/null
+++ b/drivers/watchdog/ux500_wdt.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011-2013
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Mathieu Poirier <mathieu.poirier@linaro.org> for ST-Ericsson
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/miscdevice.h>
+#include <linux/err.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/ux500_wdt.h>
+
+#include <linux/mfd/dbx500-prcmu.h>
+
+#define WATCHDOG_TIMEOUT 600 /* 10 minutes */
+
+#define WATCHDOG_MIN	0
+#define WATCHDOG_MAX28	268435  /* 28 bit resolution in ms == 268435.455 s */
+#define WATCHDOG_MAX32	4294967 /* 32 bit resolution in ms == 4294967.295 s */
+
+static int timeout = WATCHDOG_TIMEOUT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+	"Watchdog timeout in seconds. default="
+				__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+	"Watchdog cannot be stopped once started (default="
+				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int ux500_wdt_start(struct watchdog_device *wdd)
+{
+	return prcmu_enable_a9wdog(PRCMU_WDOG_ALL);
+}
+
+static int ux500_wdt_stop(struct watchdog_device *wdd)
+{
+	return prcmu_disable_a9wdog(PRCMU_WDOG_ALL);
+}
+
+static int ux500_wdt_keepalive(struct watchdog_device *wdd)
+{
+	return prcmu_kick_a9wdog(PRCMU_WDOG_ALL);
+}
+
+static int ux500_wdt_set_timeout(struct watchdog_device *wdd,
+				 unsigned int timeout)
+{
+	ux500_wdt_stop(wdd);
+	prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
+	ux500_wdt_start(wdd);
+
+	return 0;
+}
+
+static const struct watchdog_info ux500_wdt_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "Ux500 WDT",
+	.firmware_version = 1,
+};
+
+static const struct watchdog_ops ux500_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = ux500_wdt_start,
+	.stop  = ux500_wdt_stop,
+	.ping  = ux500_wdt_keepalive,
+	.set_timeout = ux500_wdt_set_timeout,
+};
+
+static struct watchdog_device ux500_wdt = {
+	.info = &ux500_wdt_info,
+	.ops = &ux500_wdt_ops,
+	.min_timeout = WATCHDOG_MIN,
+	.max_timeout = WATCHDOG_MAX32,
+};
+
+static int ux500_wdt_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct ux500_wdt_data *pdata = pdev->dev.platform_data;
+
+	if (pdata) {
+		if (pdata->timeout > 0)
+			timeout = pdata->timeout;
+		if (pdata->has_28_bits_resolution)
+			ux500_wdt.max_timeout = WATCHDOG_MAX28;
+	}
+
+	watchdog_set_nowayout(&ux500_wdt, nowayout);
+
+	/* disable auto off on sleep */
+	prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false);
+
+	/* set HW initial value */
+	prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
+
+	ret = watchdog_register_device(&ux500_wdt);
+	if (ret)
+		return ret;
+
+	dev_info(&pdev->dev, "initialized\n");
+
+	return 0;
+}
+
+static int ux500_wdt_remove(struct platform_device *dev)
+{
+	watchdog_unregister_device(&ux500_wdt);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int ux500_wdt_suspend(struct platform_device *pdev,
+			     pm_message_t state)
+{
+	if (watchdog_active(&ux500_wdt)) {
+		ux500_wdt_stop(&ux500_wdt);
+		prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true);
+
+		prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
+		ux500_wdt_start(&ux500_wdt);
+	}
+	return 0;
+}
+
+static int ux500_wdt_resume(struct platform_device *pdev)
+{
+	if (watchdog_active(&ux500_wdt)) {
+		ux500_wdt_stop(&ux500_wdt);
+		prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false);
+
+		prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000);
+		ux500_wdt_start(&ux500_wdt);
+	}
+	return 0;
+}
+#else
+#define ux500_wdt_suspend NULL
+#define ux500_wdt_resume NULL
+#endif
+
+static struct platform_driver ux500_wdt_driver = {
+	.probe		= ux500_wdt_probe,
+	.remove		= ux500_wdt_remove,
+	.suspend	= ux500_wdt_suspend,
+	.resume		= ux500_wdt_resume,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "ux500_wdt",
+	},
+};
+
+module_platform_driver(ux500_wdt_driver);
+
+MODULE_AUTHOR("Jonas Aaberg <jonas.aberg@stericsson.com>");
+MODULE_DESCRIPTION("Ux500 Watchdog Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:ux500_wdt");
diff --git a/include/linux/platform_data/ux500_wdt.h b/include/linux/platform_data/ux500_wdt.h
new file mode 100644
index 0000000..aa2dfe7
--- /dev/null
+++ b/include/linux/platform_data/ux500_wdt.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) ST Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * STE Ux500 Watchdog platform data
+ */
+#ifndef __UX500_WDT_H
+#define __UX500_WDT_H
+
+/**
+ * struct ux500_wdt_data
+ */
+struct ux500_wdt_data {
+	int timeout;
+	bool has_28_bits_resolution;
+};
+
+#endif /* __UX500_WDT_H */
-- 
1.7.12.1

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox