2013-06-11 07:17:59

by B, Ravi

[permalink] [raw]
Subject: [PATCH v2 0/7] adding dual instance and usb-phy support for am335x platform

This patch set series
- adds dual musb instances support for am335x platform
- adds phy-dsps-usb driver based on TI's gs70 driver
- adds DT bindings for am33xx usb-phy
- removed references to usb-nop-xceiv from musb

has been verified on tree [1]

[1] git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git

changes from v1:
- includes sergei's & felipe comments
- retain the nop-phy driver API usage for non-DT davinci &
other musb platform

Ravi Babu (7):
usb: musb: dsps: enable dual instance support for am33xx platform
usb: phy: dsps: adding usbphy driver for am33xx platform
usb: musb: dsps: remove nop_xceiv_(un)register APIs from dsps glue
usb: musb: dsps: use usb-phy driver API for phy power on/off
usb: musb: dsps: use get-usb-phy by phandle for multi instance
usb: phy: dts: Adding usbphy DT bindings for am33xx
usb: musb: dsp: remove the usb-phy control acess from platform glue

arch/arm/boot/dts/am33xx.dtsi | 17 +++
drivers/usb/musb/musb_dsps.c | 85 +++-----------
drivers/usb/phy/Kconfig | 9 ++
drivers/usb/phy/Makefile | 1 +
drivers/usb/phy/phy-dsps-usb.c | 236 ++++++++++++++++++++++++++++++++++++++++
5 files changed, 282 insertions(+), 66 deletions(-)
create mode 100644 drivers/usb/phy/phy-dsps-usb.c


2013-06-11 07:18:04

by B, Ravi

[permalink] [raw]
Subject: [PATCH v2 1/7] usb: musb: dsps: enable dual instance support for am33xx platform

The dsps am33xx platform has two instances of musb controller,
enable the support for dual musb instances

Signed-off-by: Ravi Babu <[email protected]>
---
drivers/usb/musb/musb_dsps.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 3a18e44..590dd0b 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -746,7 +746,7 @@ static const struct dsps_musb_wrapper ti81xx_driver_data = {
.rxep_bitmap = (0xfffe << 16),
.musb_core_offset = 0x400,
.poll_seconds = 2,
- .instances = 1,
+ .instances = 2,
};

static const struct platform_device_id musb_dsps_id_table[] = {
--
1.7.0.4

2013-06-11 07:18:13

by B, Ravi

[permalink] [raw]
Subject: [PATCH v2 6/7] usb: phy: dts: Adding usbphy DT bindings for am33xx

The am33xx platforms suppors dual musb instance which need two instances
of usb-phy. Add dual instance usb-phy DT bindings for am333x platform.

Signed-off-by: Ravi Babu <[email protected]>
---
arch/arm/boot/dts/am33xx.dtsi | 17 +++++++++++++++++
1 files changed, 17 insertions(+), 0 deletions(-)

diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 0957645..b0b4deb 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -322,6 +322,22 @@
status = "disabled";
};

+ phy1: usbphy-gs70@44e10620 {
+ compatible = "ti,dsps-usbphy";
+ reg = <0x44e10620 0x8
+ 0x44e10648 0x4>;
+ reg-names = "phy_ctrl","phy_wkup";
+ id = <0>;
+ };
+
+ phy2: usbphy-gs70@44e10628 {
+ compatible = "ti,dsps-usbphy";
+ reg = <0x44e10628 0x8
+ 0x44e10648 0x4>;
+ reg-names = "phy_ctrl","phy_wkup";
+ id = <1>;
+ };
+
usb@47400000 {
compatible = "ti,musb-am33xx";
reg = <0x47400000 0x1000 /* usbss */
@@ -337,6 +353,7 @@
port1-mode = <3>;
power = <250>;
ti,hwmods = "usb_otg_hs";
+ usb-phy = <&phy1>, <&phy2>;
};

mac: ethernet@4a100000 {
--
1.7.0.4

2013-06-11 07:18:25

by B, Ravi

[permalink] [raw]
Subject: [PATCH v2 7/7] usb: musb: dsp: remove the usb-phy control acess from platform glue

Remove usb-phy control access from platform glue, after moving
usb-phy controls to saperate phy-dsps-usb driver.

Signed-off-by: Ravi Babu <[email protected]>
---
drivers/usb/musb/musb_dsps.c | 51 ------------------------------------------
1 files changed, 0 insertions(+), 51 deletions(-)

diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 0d8581b..958c6b6 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -123,49 +123,8 @@ struct dsps_glue {
const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */
struct timer_list timer[2]; /* otg_workaround timer */
unsigned long last_timer[2]; /* last timer data for each instance */
- u32 __iomem *usb_ctrl[2];
};

-#define DSPS_AM33XX_CONTROL_MODULE_PHYS_0 0x44e10620
-#define DSPS_AM33XX_CONTROL_MODULE_PHYS_1 0x44e10628
-
-static const resource_size_t dsps_control_module_phys[] = {
- DSPS_AM33XX_CONTROL_MODULE_PHYS_0,
- DSPS_AM33XX_CONTROL_MODULE_PHYS_1,
-};
-
-#define USBPHY_CM_PWRDN (1 << 0)
-#define USBPHY_OTG_PWRDN (1 << 1)
-#define USBPHY_OTGVDET_EN (1 << 19)
-#define USBPHY_OTGSESSEND_EN (1 << 20)
-
-/**
- * musb_dsps_phy_control - phy on/off
- * @glue: struct dsps_glue *
- * @id: musb instance
- * @on: flag for phy to be switched on or off
- *
- * This is to enable the PHY using usb_ctrl register in system control
- * module space.
- *
- * XXX: This function will be removed once we have a seperate driver for
- * control module
- */
-static void musb_dsps_phy_control(struct dsps_glue *glue, u8 id, u8 on)
-{
- u32 usbphycfg;
-
- usbphycfg = readl(glue->usb_ctrl[id]);
-
- if (on) {
- usbphycfg &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN);
- usbphycfg |= USBPHY_OTGVDET_EN | USBPHY_OTGSESSEND_EN;
- } else {
- usbphycfg |= USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN;
- }
-
- writel(usbphycfg, glue->usb_ctrl[id]);
-}
/**
* dsps_musb_enable - enable interrupts
*/
@@ -494,16 +453,6 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, u8 id)
char res_name[11];
int ret;

- resources[0].start = dsps_control_module_phys[id];
- resources[0].end = resources[0].start + SZ_4 - 1;
- resources[0].flags = IORESOURCE_MEM;
-
- glue->usb_ctrl[id] = devm_ioremap_resource(&pdev->dev, resources);
- if (IS_ERR(glue->usb_ctrl[id])) {
- ret = PTR_ERR(glue->usb_ctrl[id]);
- goto err0;
- }
-
/* first resource is for usbss, so start index from 1 */
res = platform_get_resource(pdev, IORESOURCE_MEM, id + 1);
if (!res) {
--
1.7.0.4

2013-06-11 07:18:10

by B, Ravi

[permalink] [raw]
Subject: [PATCH v2 4/7] usb: musb: dsps: use usb-phy driver API for phy power on/off

use usb-phy driver API for powering on/off phy and removed
usage of the phy control access in platform glue driver.

Signed-off-by: Ravi Babu <[email protected]>
---
drivers/usb/musb/musb_dsps.c | 22 +++++++++++++---------
1 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 0ecedb3..0096aad 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -36,6 +36,7 @@
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/module.h>
+#include <linux/usb/phy.h>
#include <linux/platform_data/usb-omap.h>
#include <linux/sizes.h>

@@ -432,7 +433,7 @@ static int dsps_musb_init(struct musb *musb)
dsps_writel(reg_base, wrp->control, (1 << wrp->reset));

/* Start the on-chip PHY and its PLL. */
- musb_dsps_phy_control(glue, pdev->id, 1);
+ usb_phy_init(musb->xceiv);

musb->isr = dsps_interrupt;

@@ -459,10 +460,7 @@ static int dsps_musb_exit(struct musb *musb)
del_timer_sync(&glue->timer[pdev->id]);

/* Shutdown the on-chip PHY and its PLL. */
- musb_dsps_phy_control(glue, pdev->id, 0);
-
- /* NOP driver needs change if supporting dual instance */
- usb_put_phy(musb->xceiv);
+ usb_phy_shutdown(musb->xceiv);

return 0;
}
@@ -690,10 +688,13 @@ static int dsps_suspend(struct device *dev)
struct platform_device *pdev = to_platform_device(dev->parent);
struct dsps_glue *glue = platform_get_drvdata(pdev);
const struct dsps_musb_wrapper *wrp = glue->wrp;
+ struct musb *musb;
int i;

- for (i = 0; i < wrp->instances; i++)
- musb_dsps_phy_control(glue, i, 0);
+ for (i = 0; i < wrp->instances; i++) {
+ musb = dev_get_drvdata(&glue->musb[i]->dev);
+ usb_phy_set_suspend(musb->xceiv, 1);
+ }

return 0;
}
@@ -703,10 +704,13 @@ static int dsps_resume(struct device *dev)
struct platform_device *pdev = to_platform_device(dev->parent);
struct dsps_glue *glue = platform_get_drvdata(pdev);
const struct dsps_musb_wrapper *wrp = glue->wrp;
+ struct musb *musb;
int i;

- for (i = 0; i < wrp->instances; i++)
- musb_dsps_phy_control(glue, i, 1);
+ for (i = 0; i < wrp->instances; i++) {
+ musb = dev_get_drvdata(&glue->musb[i]->dev);
+ usb_phy_set_suspend(musb->xceiv, 0);
+ }

return 0;
}
--
1.7.0.4

2013-06-11 07:18:08

by B, Ravi

[permalink] [raw]
Subject: [PATCH v2 3/7] usb: musb: dsps: remove nop_xceiv_(un)register APIs from dsps glue

removed nop xceiv (un_)register API's references from musb dsps
platform, as it uses saperate phy driver.

Signed-off-by: Ravi Babu <[email protected]>
---
drivers/usb/musb/musb_dsps.c | 4 ----
1 files changed, 0 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 590dd0b..0ecedb3 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -36,7 +36,6 @@
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/module.h>
-#include <linux/usb/nop-usb-xceiv.h>
#include <linux/platform_data/usb-omap.h>
#include <linux/sizes.h>

@@ -416,7 +415,6 @@ static int dsps_musb_init(struct musb *musb)
musb->mregs += wrp->musb_core_offset;

/* NOP driver needs change if supporting dual instance */
- usb_nop_xceiv_register();
musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
if (IS_ERR_OR_NULL(musb->xceiv))
return -EPROBE_DEFER;
@@ -449,7 +447,6 @@ static int dsps_musb_init(struct musb *musb)
return 0;
err0:
usb_put_phy(musb->xceiv);
- usb_nop_xceiv_unregister();
return status;
}

@@ -466,7 +463,6 @@ static int dsps_musb_exit(struct musb *musb)

/* NOP driver needs change if supporting dual instance */
usb_put_phy(musb->xceiv);
- usb_nop_xceiv_unregister();

return 0;
}
--
1.7.0.4

2013-06-11 07:19:03

by B, Ravi

[permalink] [raw]
Subject: [PATCH v2 5/7] usb: musb: dsps: use get-usb-phy by phandle for multi instance

In case of mutli instance support, use get-phy object using phandle
to return to repsective phy xceiv object for each instance

Signed-off-by: Ravi Babu <[email protected]>
---
drivers/usb/musb/musb_dsps.c | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 0096aad..0d8581b 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -416,7 +416,11 @@ static int dsps_musb_init(struct musb *musb)
musb->mregs += wrp->musb_core_offset;

/* NOP driver needs change if supporting dual instance */
- musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
+ if (dev->parent->of_node)
+ musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent,
+ "usb-phy", pdev->id);
+ else
+ musb->xceiv = devm_usb_get_phy_dev(dev, pdev->id);
if (IS_ERR_OR_NULL(musb->xceiv))
return -EPROBE_DEFER;

--
1.7.0.4

2013-06-11 07:19:50

by B, Ravi

[permalink] [raw]
Subject: [PATCH v2 2/7] usb: phy: dsps: adding usbphy driver for am33xx platform

Adds usb-phy driver support for am33xx platform, the host/device
peripheral controller shall get this phy object to control the phy
operations.

Signed-off-by: Ravi Babu <[email protected]>
---
drivers/usb/phy/Kconfig | 9 ++
drivers/usb/phy/Makefile | 1 +
drivers/usb/phy/phy-dsps-usb.c | 236 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 246 insertions(+), 0 deletions(-)
create mode 100644 drivers/usb/phy/phy-dsps-usb.c

diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 372db48..b55c265 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -85,6 +85,15 @@ config OMAP_USB3
This driver interacts with the "OMAP Control USB Driver" to power
on/off the PHY.

+config DSPS_USB2PHY
+ tristate "DSPS USB2 PHY Driver"
+ depends on SOC_AM33XX
+ help
+ Enable this to support the transceiver that is part of SOC. This
+ phy supports all LS/FS/HS speed and also supports OTG functionality.
+ The USB OTG controller communicates with this phy through stand UTMI
+ interface.
+
config SAMSUNG_USBPHY
tristate "Samsung USB PHY Driver"
help
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 33863c0..0b16fb3 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_NOP_USB_XCEIV) += phy-nop.o
obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o
+obj-$(CONFIG_DSPS_USB2PHY) += phy-dsps-usb.o
obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o
obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o
obj-$(CONFIG_SAMSUNG_USB3PHY) += phy-samsung-usb3.o
diff --git a/drivers/usb/phy/phy-dsps-usb.c b/drivers/usb/phy/phy-dsps-usb.c
new file mode 100644
index 0000000..aae97b3
--- /dev/null
+++ b/drivers/usb/phy/phy-dsps-usb.c
@@ -0,0 +1,236 @@
+/*
+ * phy-dsps-usb.c - TI gs70 based usb phy driver used by usb controller
+ *
+ * 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.
+ *
+ * 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/io.h>
+#include <linux/usb/omap_usb.h>
+#include <linux/usb/phy_companion.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <linux/usb/phy.h>
+
+struct dsps_phy {
+ struct usb_phy phy;
+ struct device *dev;
+ struct platform_device *pdev;
+ void __iomem *phy_ctrl;
+ void __iomem *phy_wkup;
+ u8 is_suspended:1;
+ int id;
+};
+
+#define DSPS_USBPHY_CM_PWRDN (1 << 0)
+#define DSPS_USBPHY_OTG_PWRDN (1 << 1)
+#define DSPS_USBPHY_OTGVDET_EN (1 << 19)
+#define DSPS_USBPHY_OTGSESSEND_EN (1 << 20)
+#define DSPS_USB0_WKUP_CTRL_ENABLE (1 << 0)
+#define DSPS_USB1_WKUP_CTRL_ENABLE (1 << 8)
+#define phy_to_dspsphy(x) container_of((x), struct dsps_phy, phy)
+
+static void dsps_usbphy_power(struct usb_phy *phy, bool is_on)
+{
+ struct dsps_phy *dsps_phy = phy_to_dspsphy(phy);
+ u32 val;
+
+ val = readl(dsps_phy->phy_ctrl);
+
+ if (is_on) {
+ val &= ~(DSPS_USBPHY_CM_PWRDN | DSPS_USBPHY_OTG_PWRDN);
+ val |= DSPS_USBPHY_OTGVDET_EN |
+ DSPS_USBPHY_OTGSESSEND_EN;
+ } else
+ val |= DSPS_USBPHY_CM_PWRDN | DSPS_USBPHY_OTG_PWRDN;
+
+ writel(val, dsps_phy->phy_ctrl);
+}
+
+static void dsps_usbphy_wakeup(struct usb_phy *phy, bool enable)
+{
+ struct dsps_phy *dsps_phy = phy_to_dspsphy(phy);
+ int id = dsps_phy->id;
+ u32 val, wkup_flag;
+
+ val = readl(dsps_phy->phy_wkup);
+ wkup_flag = id ? DSPS_USB1_WKUP_CTRL_ENABLE :
+ DSPS_USB0_WKUP_CTRL_ENABLE;
+
+ if (enable)
+ val |= wkup_flag;
+ else
+ val &= ~wkup_flag;
+
+ writel(val, dsps_phy->phy_wkup);
+}
+
+static int dsps_usbphy_suspend(struct usb_phy *x, int suspend)
+{
+ struct dsps_phy *dsps_phy = phy_to_dspsphy(x);
+
+ if (suspend) {
+ if (!pm_runtime_suspended(&dsps_phy->pdev->dev))
+ pm_runtime_put(&dsps_phy->pdev->dev);
+ } else if (!suspend)
+ pm_runtime_get(&dsps_phy->pdev->dev);
+
+ return 0;
+}
+
+static int dsps_usbphy_init(struct usb_phy *phy)
+{
+ /* enable the phy */
+ dsps_usbphy_power(phy, 1);
+
+ return 0;
+}
+
+static void dsps_usbphy_shutdown(struct usb_phy *phy)
+{
+ /* disable the phy */
+ dsps_usbphy_power(phy, 0);
+}
+
+static int dsps_usbphy_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct dsps_phy *phy;
+ struct usb_otg *otg;
+ struct resource *res;
+
+ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy) {
+ dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n");
+ return -ENOMEM;
+ }
+
+ otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
+ if (!otg) {
+ dev_err(&pdev->dev, "unable to allocate memory for USB OTG\n");
+ return -ENOMEM;
+ }
+
+ phy->dev = &pdev->dev;
+ phy->pdev = pdev;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
+ phy->phy_ctrl = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(phy->phy_ctrl))
+ return PTR_ERR(phy->phy_ctrl);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_wkup");
+ phy->phy_wkup = ioremap(res->start, resource_size(res));
+ if (IS_ERR(phy->phy_wkup))
+ return PTR_ERR(phy->phy_wkup);
+
+ if (np)
+ of_property_read_u32(np, "id", &phy->id);
+
+ phy->phy.dev = phy->dev;
+ phy->phy.label = "dsps-usbphy";
+ phy->phy.otg = otg;
+ phy->phy.type = USB_PHY_TYPE_USB2;
+ phy->phy.init = dsps_usbphy_init;
+ phy->phy.shutdown = dsps_usbphy_shutdown;
+ phy->phy.set_suspend = dsps_usbphy_suspend;
+ otg->phy = &phy->phy;
+
+ usb_add_phy_dev(&phy->phy);
+
+ platform_set_drvdata(pdev, phy);
+
+ pm_runtime_enable(phy->dev);
+
+ return 0;
+}
+
+static int dsps_usbphy_remove(struct platform_device *pdev)
+{
+ struct dsps_phy *phy = platform_get_drvdata(pdev);
+
+ dsps_usbphy_power(&phy->phy, 0);
+ iounmap(phy->phy_wkup);
+ usb_remove_phy(&phy->phy);
+
+ if (!pm_runtime_suspended(&pdev->dev))
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+
+static int dsps_usbphy_runtime_suspend(struct device *dev)
+{
+ struct dsps_phy *dsps_phy = dev_get_drvdata(dev);
+
+ /* suspend usb-phy */
+ dsps_usbphy_power(&dsps_phy->phy, 0);
+ dsps_phy->is_suspended = 1;
+
+ return 0;
+}
+
+static int dsps_usbphy_runtime_resume(struct device *dev)
+{
+ struct dsps_phy *dsps_phy = dev_get_drvdata(dev);
+
+ /* resume usb-phy */
+ dsps_usbphy_power(&dsps_phy->phy, 1);
+ dsps_phy->is_suspended = 0;
+
+ return 0;
+}
+
+static const struct dev_pm_ops dsps_usbphy_pm_ops = {
+ SET_RUNTIME_PM_OPS(dsps_usbphy_runtime_suspend,
+ dsps_usbphy_runtime_resume, NULL)
+};
+
+#define DEV_PM_OPS (&dsps_usbphy_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id dsps_usbphy_id_table[] = {
+ { .compatible = "ti,dsps-usbphy" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, dsps_usbphy_id_table);
+#endif
+
+static struct platform_driver dsps_usbphy_driver = {
+ .probe = dsps_usbphy_probe,
+ .remove = dsps_usbphy_remove,
+ .driver = {
+ .name = "dsps-usbphy",
+ .owner = THIS_MODULE,
+ .pm = DEV_PM_OPS,
+ .of_match_table = of_match_ptr(dsps_usbphy_id_table),
+ },
+};
+
+module_platform_driver(dsps_usbphy_driver);
+
+MODULE_ALIAS("platform: dsps_usbphy");
+MODULE_AUTHOR("Ravi Babu <[email protected]>");
+MODULE_DESCRIPTION("dsps USB2 phy driver");
+MODULE_LICENSE("GPL v2");
--
1.7.0.4

2013-06-11 08:41:23

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [PATCH v2 2/7] usb: phy: dsps: adding usbphy driver for am33xx platform

Hi,

On Tuesday 11 June 2013 12:47 PM, Ravi Babu wrote:
> Adds usb-phy driver support for am33xx platform, the host/device
> peripheral controller shall get this phy object to control the phy
> operations.
>
> Signed-off-by: Ravi Babu <[email protected]>
> ---
> drivers/usb/phy/Kconfig | 9 ++
> drivers/usb/phy/Makefile | 1 +
> drivers/usb/phy/phy-dsps-usb.c | 236 ++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 246 insertions(+), 0 deletions(-)
> create mode 100644 drivers/usb/phy/phy-dsps-usb.c
>
> diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
> index 372db48..b55c265 100644
> --- a/drivers/usb/phy/Kconfig
> +++ b/drivers/usb/phy/Kconfig
> @@ -85,6 +85,15 @@ config OMAP_USB3
> This driver interacts with the "OMAP Control USB Driver" to power
> on/off the PHY.
>
> +config DSPS_USB2PHY
> + tristate "DSPS USB2 PHY Driver"
> + depends on SOC_AM33XX
> + help
> + Enable this to support the transceiver that is part of SOC. This
> + phy supports all LS/FS/HS speed and also supports OTG functionality.
> + The USB OTG controller communicates with this phy through stand UTMI
> + interface.
> +
> config SAMSUNG_USBPHY
> tristate "Samsung USB PHY Driver"
> help
> diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
> index 33863c0..0b16fb3 100644
> --- a/drivers/usb/phy/Makefile
> +++ b/drivers/usb/phy/Makefile
> @@ -17,6 +17,7 @@ obj-$(CONFIG_NOP_USB_XCEIV) += phy-nop.o
> obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o
> obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
> obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o
> +obj-$(CONFIG_DSPS_USB2PHY) += phy-dsps-usb.o
> obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o
> obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o
> obj-$(CONFIG_SAMSUNG_USB3PHY) += phy-samsung-usb3.o
> diff --git a/drivers/usb/phy/phy-dsps-usb.c b/drivers/usb/phy/phy-dsps-usb.c
> new file mode 100644
> index 0000000..aae97b3
> --- /dev/null
> +++ b/drivers/usb/phy/phy-dsps-usb.c
> @@ -0,0 +1,236 @@
> +/*
> + * phy-dsps-usb.c - TI gs70 based usb phy driver used by usb controller
> + *
> + * 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.
> + *
> + * 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/io.h>
> +#include <linux/usb/omap_usb.h>
> +#include <linux/usb/phy_companion.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/delay.h>
> +#include <linux/usb/phy.h>
> +
> +struct dsps_phy {
> + struct usb_phy phy;
> + struct device *dev;
> + struct platform_device *pdev;
> + void __iomem *phy_ctrl;
> + void __iomem *phy_wkup;
> + u8 is_suspended:1;
> + int id;
> +};
> +
> +#define DSPS_USBPHY_CM_PWRDN (1 << 0)
> +#define DSPS_USBPHY_OTG_PWRDN (1 << 1)
> +#define DSPS_USBPHY_OTGVDET_EN (1 << 19)
> +#define DSPS_USBPHY_OTGSESSEND_EN (1 << 20)
> +#define DSPS_USB0_WKUP_CTRL_ENABLE (1 << 0)
> +#define DSPS_USB1_WKUP_CTRL_ENABLE (1 << 8)
> +#define phy_to_dspsphy(x) container_of((x), struct dsps_phy, phy)
> +
> +static void dsps_usbphy_power(struct usb_phy *phy, bool is_on)
> +{
> + struct dsps_phy *dsps_phy = phy_to_dspsphy(phy);
> + u32 val;
> +
> + val = readl(dsps_phy->phy_ctrl);
> +
> + if (is_on) {
> + val &= ~(DSPS_USBPHY_CM_PWRDN | DSPS_USBPHY_OTG_PWRDN);
> + val |= DSPS_USBPHY_OTGVDET_EN |
> + DSPS_USBPHY_OTGSESSEND_EN;
> + } else
> + val |= DSPS_USBPHY_CM_PWRDN | DSPS_USBPHY_OTG_PWRDN;
> +
> + writel(val, dsps_phy->phy_ctrl);
> +}
> +
> +static void dsps_usbphy_wakeup(struct usb_phy *phy, bool enable)
> +{
Where is this function called from?
> + struct dsps_phy *dsps_phy = phy_to_dspsphy(phy);
> + int id = dsps_phy->id;
> + u32 val, wkup_flag;
> +
> + val = readl(dsps_phy->phy_wkup);
> + wkup_flag = id ? DSPS_USB1_WKUP_CTRL_ENABLE :
> + DSPS_USB0_WKUP_CTRL_ENABLE;
> +
> + if (enable)
> + val |= wkup_flag;
> + else
> + val &= ~wkup_flag;
> +
> + writel(val, dsps_phy->phy_wkup);
> +}
> +
> +static int dsps_usbphy_suspend(struct usb_phy *x, int suspend)
> +{
> + struct dsps_phy *dsps_phy = phy_to_dspsphy(x);
> +
> + if (suspend) {
> + if (!pm_runtime_suspended(&dsps_phy->pdev->dev))
> + pm_runtime_put(&dsps_phy->pdev->dev);
> + } else if (!suspend)
> + pm_runtime_get(&dsps_phy->pdev->dev);
> +
> + return 0;
> +}
> +
> +static int dsps_usbphy_init(struct usb_phy *phy)
> +{
> + /* enable the phy */
> + dsps_usbphy_power(phy, 1);
> +
> + return 0;
> +}
> +
> +static void dsps_usbphy_shutdown(struct usb_phy *phy)
> +{
> + /* disable the phy */
> + dsps_usbphy_power(phy, 0);
> +}
> +
> +static int dsps_usbphy_probe(struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + struct dsps_phy *phy;
> + struct usb_otg *otg;
> + struct resource *res;
> +
> + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
> + if (!phy) {
> + dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n");
> + return -ENOMEM;
> + }
> +
> + otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
> + if (!otg) {
> + dev_err(&pdev->dev, "unable to allocate memory for USB OTG\n");
> + return -ENOMEM;
> + }
> +
> + phy->dev = &pdev->dev;
> + phy->pdev = pdev;
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
> + phy->phy_ctrl = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(phy->phy_ctrl))
> + return PTR_ERR(phy->phy_ctrl);
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_wkup");
> + phy->phy_wkup = ioremap(res->start, resource_size(res));

devm_ioremap?
> + if (IS_ERR(phy->phy_wkup))
> + return PTR_ERR(phy->phy_wkup);
> +
> + if (np)
> + of_property_read_u32(np, "id", &phy->id);

Is this property documented somewhere?
> +
> + phy->phy.dev = phy->dev;
> + phy->phy.label = "dsps-usbphy";
> + phy->phy.otg = otg;
> + phy->phy.type = USB_PHY_TYPE_USB2;
> + phy->phy.init = dsps_usbphy_init;
> + phy->phy.shutdown = dsps_usbphy_shutdown;
> + phy->phy.set_suspend = dsps_usbphy_suspend;
> + otg->phy = &phy->phy;
> +
> + usb_add_phy_dev(&phy->phy);
> +
> + platform_set_drvdata(pdev, phy);
> +
> + pm_runtime_enable(phy->dev);
> +
> + return 0;
> +}
> +
> +static int dsps_usbphy_remove(struct platform_device *pdev)
> +{
> + struct dsps_phy *phy = platform_get_drvdata(pdev);
> +
> + dsps_usbphy_power(&phy->phy, 0);
> + iounmap(phy->phy_wkup);
> + usb_remove_phy(&phy->phy);
> +
> + if (!pm_runtime_suspended(&pdev->dev))
> + pm_runtime_put(&pdev->dev);
> + pm_runtime_disable(&pdev->dev);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_RUNTIME
> +
> +static int dsps_usbphy_runtime_suspend(struct device *dev)
> +{
> + struct dsps_phy *dsps_phy = dev_get_drvdata(dev);
> +
> + /* suspend usb-phy */
> + dsps_usbphy_power(&dsps_phy->phy, 0);
> + dsps_phy->is_suspended = 1;

Are you using this "is_suspended" anywhere else?

Thanks
Kishon

2013-06-11 09:45:29

by B, Ravi

[permalink] [raw]
Subject: RE: [PATCH v2 2/7] usb: phy: dsps: adding usbphy driver for am33xx platform

Kishon

> Cc: [email protected]; [email protected]; Balbi, Felipe
> Subject: Re: [PATCH v2 2/7] usb: phy: dsps: adding usbphy driver for am33xx platform

>> +
>> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_wkup");
>> + phy->phy_wkup = ioremap(res->start, resource_size(res));

>devm_ioremap?

The phy_wakeup register is common across two instances of phy, devm_ioremap_resource() will fail to map for twice for same register space.

>> + if (IS_ERR(phy->phy_wkup))
>> + return PTR_ERR(phy->phy_wkup);
>> +
>> + if (np)
>> + of_property_read_u32(np, "id", &phy->id);

> Is this property documented somewhere?

Not it is not documented.

>> +
>> + phy->phy.dev = phy->dev;
>> + phy->phy.label = "dsps-usbphy";
>> + dsps_usbphy_power(&dsps_phy->phy, 0);
>> + dsps_phy->is_suspended = 1;

> Are you using this "is_suspended" anywhere else?

Currently not used.
--
Ravi B

2013-06-11 18:52:49

by Sergei Shtylyov

[permalink] [raw]
Subject: Re: [PATCH v2 2/7] usb: phy: dsps: adding usbphy driver for am33xx platform

Hello.

On 06/11/2013 01:45 PM, B, Ravi wrote:

>>> +
>>> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_wkup");
>>> + phy->phy_wkup = ioremap(res->start, resource_size(res));

>> devm_ioremap?

> The phy_wakeup register is common across two instances of phy, devm_ioremap_resource() will fail to map for twice for same register space.

I've already told you the register can't be shared between devices
like this. BTW, you haven't replied to my request concerning your
/proc/iomem contents...

WBR, Sergei

2013-06-12 07:20:54

by B, Ravi

[permalink] [raw]
Subject: RE: [PATCH v2 2/7] usb: phy: dsps: adding usbphy driver for am33xx platform

>>>> +
>>>> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_wkup");
>>>> + phy->phy_wkup = ioremap(res->start, resource_size(res));

>>> devm_ioremap?

>> The phy_wakeup register is common across two instances of phy, devm_ioremap_resource() will fail to map for twice for same register space.

> I've already told you the register can't be shared between devices like this.
> BTW, you haven't replied to my request concerning your /proc/iomem contents...

I have missed that specific mail. The /proc/iomem does show this common register. As you suggest to create a third device for this shared register? I agree it can be done.
Initially I thought this should be part of control module, but do we have control module frame work for every SoC(am335x)?
Every SoC has many such shared register to handle control/status logic for Soc specific IP(s)?
If we create separate device node for each such registers, this will end up in adding more files to kernel.

We should create single control module node for each SoC,

cm: control_module@44e10000 {
compatible = "ti,dsps-usbphy";
reg = 0x44e10648 0x4

0x44e10XXX 0x4
....>
reg-names = "phy_wkup", "mmc_control", <xxxreg_ctrl>, <xxxreg_status>;
id = <0>;
};
Have common control module APIs to control these registers, can be used by other driver modules.

--
Ravi B

2013-06-12 07:22:35

by B, Ravi

[permalink] [raw]
Subject: RE: [PATCH v2 2/7] usb: phy: dsps: adding usbphy driver for am33xx platform

Typo.. corrected
"The /proc/iomem does not show this common register"

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of B, Ravi
Sent: Wednesday, June 12, 2013 12:51 PM
To: Sergei Shtylyov
Cc: ABRAHAM, KISHON VIJAY; [email protected]; [email protected]; Balbi, Felipe
Subject: RE: [PATCH v2 2/7] usb: phy: dsps: adding usbphy driver for am33xx platform

>>>> +
>>>> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_wkup");
>>>> + phy->phy_wkup = ioremap(res->start, resource_size(res));

>>> devm_ioremap?

>> The phy_wakeup register is common across two instances of phy, devm_ioremap_resource() will fail to map for twice for same register space.

> I've already told you the register can't be shared between devices like this.
> BTW, you haven't replied to my request concerning your /proc/iomem contents...

I have missed that specific mail. The /proc/iomem does not show this common register. As you suggest to create a third device for this shared register? I agree it can be done.
Initially I thought this should be part of control module, but do we have control module frame work for every SoC(am335x)?
Every SoC has many such shared register to handle control/status logic for Soc specific IP(s)?
If we create separate device node for each such registers, this will end up in adding more files to kernel.

We should create single control module node for each SoC,

cm: control_module@44e10000 {
compatible = "ti,dsps-usbphy";
reg = 0x44e10648 0x4

0x44e10XXX 0x4
....>
reg-names = "phy_wkup", "mmc_control", <xxxreg_ctrl>, <xxxreg_status>;
id = <0>;
};
Have common control module APIs to control these registers, can be used by other driver modules.

--
Ravi B