summaryrefslogtreecommitdiffstats
path: root/drivers/usb/phy/phy-msm-usb.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-06-08 11:31:16 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2014-06-08 11:31:16 -0700
commit3f17ea6dea8ba5668873afa54628a91aaa3fb1c0 (patch)
treeafbeb2accd4c2199ddd705ae943995b143a0af02 /drivers/usb/phy/phy-msm-usb.c
parent1860e379875dfe7271c649058aeddffe5afd9d0d (diff)
parent1a5700bc2d10cd379a795fd2bb377a190af5acd4 (diff)
Merge branch 'next' (accumulated 3.16 merge window patches) into master
Now that 3.15 is released, this merges the 'next' branch into 'master', bringing us to the normal situation where my 'master' branch is the merge window. * accumulated work in next: (6809 commits) ufs: sb mutex merge + mutex_destroy powerpc: update comments for generic idle conversion cris: update comments for generic idle conversion idle: remove cpu_idle() forward declarations nbd: zero from and len fields in NBD_CMD_DISCONNECT. mm: convert some level-less printks to pr_* MAINTAINERS: adi-buildroot-devel is moderated MAINTAINERS: add linux-api for review of API/ABI changes mm/kmemleak-test.c: use pr_fmt for logging fs/dlm/debug_fs.c: replace seq_printf by seq_puts fs/dlm/lockspace.c: convert simple_str to kstr fs/dlm/config.c: convert simple_str to kstr mm: mark remap_file_pages() syscall as deprecated mm: memcontrol: remove unnecessary memcg argument from soft limit functions mm: memcontrol: clean up memcg zoneinfo lookup mm/memblock.c: call kmemleak directly from memblock_(alloc|free) mm/mempool.c: update the kmemleak stack trace for mempool allocations lib/radix-tree.c: update the kmemleak stack trace for radix tree allocations mm: introduce kmemleak_update_trace() mm/kmemleak.c: use %u to print ->checksum ...
Diffstat (limited to 'drivers/usb/phy/phy-msm-usb.c')
-rw-r--r--drivers/usb/phy/phy-msm-usb.c702
1 files changed, 398 insertions, 304 deletions
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index 5b37b81f2ef..ced34f39bdd 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -30,9 +30,13 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/reset.h>
#include <linux/usb.h>
#include <linux/usb/otg.h>
+#include <linux/usb/of.h>
#include <linux/usb/ulpi.h>
#include <linux/usb/gadget.h>
#include <linux/usb/hcd.h>
@@ -44,6 +48,7 @@
#define DRIVER_NAME "msm_otg"
#define ULPI_IO_TIMEOUT_USEC (10 * 1000)
+#define LINK_RESET_TIMEOUT_USEC (250 * 1000)
#define USB_PHY_3P3_VOL_MIN 3050000 /* uV */
#define USB_PHY_3P3_VOL_MAX 3300000 /* uV */
@@ -57,48 +62,38 @@
#define USB_PHY_VDD_DIG_VOL_MIN 1000000 /* uV */
#define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */
+#define USB_PHY_SUSP_DIG_VOL 500000 /* uV */
-static struct regulator *hsusb_3p3;
-static struct regulator *hsusb_1p8;
-static struct regulator *hsusb_vddcx;
+enum vdd_levels {
+ VDD_LEVEL_NONE = 0,
+ VDD_LEVEL_MIN,
+ VDD_LEVEL_MAX,
+};
static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init)
{
int ret = 0;
if (init) {
- hsusb_vddcx = regulator_get(motg->phy.dev, "HSUSB_VDDCX");
- if (IS_ERR(hsusb_vddcx)) {
- dev_err(motg->phy.dev, "unable to get hsusb vddcx\n");
- return PTR_ERR(hsusb_vddcx);
- }
-
- ret = regulator_set_voltage(hsusb_vddcx,
- USB_PHY_VDD_DIG_VOL_MIN,
- USB_PHY_VDD_DIG_VOL_MAX);
+ ret = regulator_set_voltage(motg->vddcx,
+ motg->vdd_levels[VDD_LEVEL_MIN],
+ motg->vdd_levels[VDD_LEVEL_MAX]);
if (ret) {
- dev_err(motg->phy.dev, "unable to set the voltage "
- "for hsusb vddcx\n");
- regulator_put(hsusb_vddcx);
+ dev_err(motg->phy.dev, "Cannot set vddcx voltage\n");
return ret;
}
- ret = regulator_enable(hsusb_vddcx);
- if (ret) {
+ ret = regulator_enable(motg->vddcx);
+ if (ret)
dev_err(motg->phy.dev, "unable to enable hsusb vddcx\n");
- regulator_put(hsusb_vddcx);
- }
} else {
- ret = regulator_set_voltage(hsusb_vddcx, 0,
- USB_PHY_VDD_DIG_VOL_MAX);
+ ret = regulator_set_voltage(motg->vddcx, 0,
+ motg->vdd_levels[VDD_LEVEL_MAX]);
if (ret)
- dev_err(motg->phy.dev, "unable to set the voltage "
- "for hsusb vddcx\n");
- ret = regulator_disable(hsusb_vddcx);
+ dev_err(motg->phy.dev, "Cannot set vddcx voltage\n");
+ ret = regulator_disable(motg->vddcx);
if (ret)
dev_err(motg->phy.dev, "unable to disable hsusb vddcx\n");
-
- regulator_put(hsusb_vddcx);
}
return ret;
@@ -109,98 +104,67 @@ static int msm_hsusb_ldo_init(struct msm_otg *motg, int init)
int rc = 0;
if (init) {
- hsusb_3p3 = regulator_get(motg->phy.dev, "HSUSB_3p3");
- if (IS_ERR(hsusb_3p3)) {
- dev_err(motg->phy.dev, "unable to get hsusb 3p3\n");
- return PTR_ERR(hsusb_3p3);
- }
-
- rc = regulator_set_voltage(hsusb_3p3, USB_PHY_3P3_VOL_MIN,
+ rc = regulator_set_voltage(motg->v3p3, USB_PHY_3P3_VOL_MIN,
USB_PHY_3P3_VOL_MAX);
if (rc) {
- dev_err(motg->phy.dev, "unable to set voltage level "
- "for hsusb 3p3\n");
- goto put_3p3;
+ dev_err(motg->phy.dev, "Cannot set v3p3 voltage\n");
+ goto exit;
}
- rc = regulator_enable(hsusb_3p3);
+ rc = regulator_enable(motg->v3p3);
if (rc) {
dev_err(motg->phy.dev, "unable to enable the hsusb 3p3\n");
- goto put_3p3;
+ goto exit;
}
- hsusb_1p8 = regulator_get(motg->phy.dev, "HSUSB_1p8");
- if (IS_ERR(hsusb_1p8)) {
- dev_err(motg->phy.dev, "unable to get hsusb 1p8\n");
- rc = PTR_ERR(hsusb_1p8);
- goto disable_3p3;
- }
- rc = regulator_set_voltage(hsusb_1p8, USB_PHY_1P8_VOL_MIN,
+ rc = regulator_set_voltage(motg->v1p8, USB_PHY_1P8_VOL_MIN,
USB_PHY_1P8_VOL_MAX);
if (rc) {
- dev_err(motg->phy.dev, "unable to set voltage level "
- "for hsusb 1p8\n");
- goto put_1p8;
+ dev_err(motg->phy.dev, "Cannot set v1p8 voltage\n");
+ goto disable_3p3;
}
- rc = regulator_enable(hsusb_1p8);
+ rc = regulator_enable(motg->v1p8);
if (rc) {
dev_err(motg->phy.dev, "unable to enable the hsusb 1p8\n");
- goto put_1p8;
+ goto disable_3p3;
}
return 0;
}
- regulator_disable(hsusb_1p8);
-put_1p8:
- regulator_put(hsusb_1p8);
+ regulator_disable(motg->v1p8);
disable_3p3:
- regulator_disable(hsusb_3p3);
-put_3p3:
- regulator_put(hsusb_3p3);
+ regulator_disable(motg->v3p3);
+exit:
return rc;
}
-static int msm_hsusb_ldo_set_mode(int on)
+static int msm_hsusb_ldo_set_mode(struct msm_otg *motg, int on)
{
int ret = 0;
- if (!hsusb_1p8 || IS_ERR(hsusb_1p8)) {
- pr_err("%s: HSUSB_1p8 is not initialized\n", __func__);
- return -ENODEV;
- }
-
- if (!hsusb_3p3 || IS_ERR(hsusb_3p3)) {
- pr_err("%s: HSUSB_3p3 is not initialized\n", __func__);
- return -ENODEV;
- }
-
if (on) {
- ret = regulator_set_optimum_mode(hsusb_1p8,
+ ret = regulator_set_optimum_mode(motg->v1p8,
USB_PHY_1P8_HPM_LOAD);
if (ret < 0) {
- pr_err("%s: Unable to set HPM of the regulator "
- "HSUSB_1p8\n", __func__);
+ pr_err("Could not set HPM for v1p8\n");
return ret;
}
- ret = regulator_set_optimum_mode(hsusb_3p3,
+ ret = regulator_set_optimum_mode(motg->v3p3,
USB_PHY_3P3_HPM_LOAD);
if (ret < 0) {
- pr_err("%s: Unable to set HPM of the regulator "
- "HSUSB_3p3\n", __func__);
- regulator_set_optimum_mode(hsusb_1p8,
+ pr_err("Could not set HPM for v3p3\n");
+ regulator_set_optimum_mode(motg->v1p8,
USB_PHY_1P8_LPM_LOAD);
return ret;
}
} else {
- ret = regulator_set_optimum_mode(hsusb_1p8,
+ ret = regulator_set_optimum_mode(motg->v1p8,
USB_PHY_1P8_LPM_LOAD);
if (ret < 0)
- pr_err("%s: Unable to set LPM of the regulator "
- "HSUSB_1p8\n", __func__);
- ret = regulator_set_optimum_mode(hsusb_3p3,
+ pr_err("Could not set LPM for v1p8\n");
+ ret = regulator_set_optimum_mode(motg->v3p3,
USB_PHY_3P3_LPM_LOAD);
if (ret < 0)
- pr_err("%s: Unable to set LPM of the regulator "
- "HSUSB_3p3\n", __func__);
+ pr_err("Could not set LPM for v3p3\n");
}
pr_debug("reg (%s)\n", on ? "HPM" : "LPM");
@@ -265,27 +229,47 @@ static struct usb_phy_io_ops msm_otg_io_ops = {
static void ulpi_init(struct msm_otg *motg)
{
struct msm_otg_platform_data *pdata = motg->pdata;
- int *seq = pdata->phy_init_seq;
+ int *seq = pdata->phy_init_seq, idx;
+ u32 addr = ULPI_EXT_VENDOR_SPECIFIC;
- if (!seq)
- return;
+ for (idx = 0; idx < pdata->phy_init_sz; idx++) {
+ if (seq[idx] == -1)
+ continue;
- while (seq[0] >= 0) {
dev_vdbg(motg->phy.dev, "ulpi: write 0x%02x to 0x%02x\n",
- seq[0], seq[1]);
- ulpi_write(&motg->phy, seq[0], seq[1]);
- seq += 2;
+ seq[idx], addr + idx);
+ ulpi_write(&motg->phy, seq[idx], addr + idx);
}
}
+static int msm_phy_notify_disconnect(struct usb_phy *phy,
+ enum usb_device_speed speed)
+{
+ int val;
+
+ /*
+ * Put the transceiver in non-driving mode. Otherwise host
+ * may not detect soft-disconnection.
+ */
+ val = ulpi_read(phy, ULPI_FUNC_CTRL);
+ val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
+ ulpi_write(phy, val, ULPI_FUNC_CTRL);
+
+ return 0;
+}
+
static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert)
{
- int ret = 0;
+ int ret;
- if (!motg->pdata->link_clk_reset)
- return ret;
+ if (motg->pdata->link_clk_reset)
+ ret = motg->pdata->link_clk_reset(motg->clk, assert);
+ else if (assert)
+ ret = reset_control_assert(motg->link_rst);
+ else
+ ret = reset_control_deassert(motg->link_rst);
- ret = motg->pdata->link_clk_reset(motg->clk, assert);
if (ret)
dev_err(motg->phy.dev, "usb link clk reset %s failed\n",
assert ? "assert" : "deassert");
@@ -295,111 +279,150 @@ static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert)
static int msm_otg_phy_clk_reset(struct msm_otg *motg)
{
- int ret = 0;
+ int ret;
- if (!motg->pdata->phy_clk_reset)
- return ret;
+ if (motg->pdata->phy_clk_reset)
+ ret = motg->pdata->phy_clk_reset(motg->phy_reset_clk);
+ else
+ ret = reset_control_reset(motg->phy_rst);
- ret = motg->pdata->phy_clk_reset(motg->phy_reset_clk);
if (ret)
dev_err(motg->phy.dev, "usb phy clk reset failed\n");
return ret;
}
-static int msm_otg_phy_reset(struct msm_otg *motg)
+static int msm_link_reset(struct msm_otg *motg)
{
u32 val;
int ret;
- int retries;
ret = msm_otg_link_clk_reset(motg, 1);
if (ret)
return ret;
- ret = msm_otg_phy_clk_reset(motg);
- if (ret)
- return ret;
+
+ /* wait for 1ms delay as suggested in HPG. */
+ usleep_range(1000, 1200);
+
ret = msm_otg_link_clk_reset(motg, 0);
if (ret)
return ret;
+ if (motg->phy_number)
+ writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2);
+
+ /* put transceiver in serial mode as part of reset */
val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK;
- writel(val | PORTSC_PTS_ULPI, USB_PORTSC);
+ writel(val | PORTSC_PTS_SERIAL, USB_PORTSC);
- for (retries = 3; retries > 0; retries--) {
- ret = ulpi_write(&motg->phy, ULPI_FUNC_CTRL_SUSPENDM,
- ULPI_CLR(ULPI_FUNC_CTRL));
- if (!ret)
- break;
- ret = msm_otg_phy_clk_reset(motg);
- if (ret)
- return ret;
- }
- if (!retries)
- return -ETIMEDOUT;
+ return 0;
+}
- /* This reset calibrates the phy, if the above write succeeded */
- ret = msm_otg_phy_clk_reset(motg);
- if (ret)
- return ret;
+static int msm_otg_reset(struct usb_phy *phy)
+{
+ struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+ int cnt = 0;
- for (retries = 3; retries > 0; retries--) {
- ret = ulpi_read(&motg->phy, ULPI_DEBUG);
- if (ret != -ETIMEDOUT)
+ writel(USBCMD_RESET, USB_USBCMD);
+ while (cnt < LINK_RESET_TIMEOUT_USEC) {
+ if (!(readl(USB_USBCMD) & USBCMD_RESET))
break;
- ret = msm_otg_phy_clk_reset(motg);
- if (ret)
- return ret;
+ udelay(1);
+ cnt++;
}
- if (!retries)
+ if (cnt >= LINK_RESET_TIMEOUT_USEC)
return -ETIMEDOUT;
- dev_info(motg->phy.dev, "phy_reset: success\n");
+ /* select ULPI phy and clear other status/control bits in PORTSC */
+ writel(PORTSC_PTS_ULPI, USB_PORTSC);
+
+ writel(0x0, USB_AHBBURST);
+ writel(0x08, USB_AHBMODE);
+
+ if (motg->phy_number)
+ writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2);
return 0;
}
-#define LINK_RESET_TIMEOUT_USEC (250 * 1000)
-static int msm_otg_reset(struct usb_phy *phy)
+static void msm_phy_reset(struct msm_otg *motg)
+{
+ void __iomem *addr;
+
+ if (motg->pdata->phy_type != SNPS_28NM_INTEGRATED_PHY) {
+ msm_otg_phy_clk_reset(motg);
+ return;
+ }
+
+ addr = USB_PHY_CTRL;
+ if (motg->phy_number)
+ addr = USB_PHY_CTRL2;
+
+ /* Assert USB PHY_POR */
+ writel(readl(addr) | PHY_POR_ASSERT, addr);
+
+ /*
+ * wait for minimum 10 microseconds as suggested in HPG.
+ * Use a slightly larger value since the exact value didn't
+ * work 100% of the time.
+ */
+ udelay(12);
+
+ /* Deassert USB PHY_POR */
+ writel(readl(addr) & ~PHY_POR_ASSERT, addr);
+}
+
+static int msm_usb_reset(struct usb_phy *phy)
{
struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
- struct msm_otg_platform_data *pdata = motg->pdata;
- int cnt = 0;
int ret;
- u32 val = 0;
- u32 ulpi_val = 0;
- ret = msm_otg_phy_reset(motg);
+ if (!IS_ERR(motg->core_clk))
+ clk_prepare_enable(motg->core_clk);
+
+ ret = msm_link_reset(motg);
if (ret) {
dev_err(phy->dev, "phy_reset failed\n");
return ret;
}
- ulpi_init(motg);
-
- writel(USBCMD_RESET, USB_USBCMD);
- while (cnt < LINK_RESET_TIMEOUT_USEC) {
- if (!(readl(USB_USBCMD) & USBCMD_RESET))
- break;
- udelay(1);
- cnt++;
+ ret = msm_otg_reset(&motg->phy);
+ if (ret) {
+ dev_err(phy->dev, "link reset failed\n");
+ return ret;
}
- if (cnt >= LINK_RESET_TIMEOUT_USEC)
- return -ETIMEDOUT;
-
- /* select ULPI phy */
- writel(0x80000000, USB_PORTSC);
msleep(100);
- writel(0x0, USB_AHBBURST);
- writel(0x00, USB_AHBMODE);
+ /* Reset USB PHY after performing USB Link RESET */
+ msm_phy_reset(motg);
+
+ if (!IS_ERR(motg->core_clk))
+ clk_disable_unprepare(motg->core_clk);
+
+ return 0;
+}
+
+static int msm_phy_init(struct usb_phy *phy)
+{
+ struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+ struct msm_otg_platform_data *pdata = motg->pdata;
+ u32 val, ulpi_val = 0;
+
+ /* Program USB PHY Override registers. */
+ ulpi_init(motg);
+
+ /*
+ * It is recommended in HPG to reset USB PHY after programming
+ * USB PHY Override registers.
+ */
+ msm_phy_reset(motg);
if (pdata->otg_control == OTG_PHY_CONTROL) {
val = readl(USB_OTGSC);
- if (pdata->mode == USB_OTG) {
+ if (pdata->mode == USB_DR_MODE_OTG) {
ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID;
val |= OTGSC_IDIE | OTGSC_BSVIE;
- } else if (pdata->mode == USB_PERIPHERAL) {
+ } else if (pdata->mode == USB_DR_MODE_PERIPHERAL) {
ulpi_val = ULPI_INT_SESS_VALID;
val |= OTGSC_BSVIE;
}
@@ -408,6 +431,9 @@ static int msm_otg_reset(struct usb_phy *phy)
ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL);
}
+ if (motg->phy_number)
+ writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2);
+
return 0;
}
@@ -416,22 +442,20 @@ static int msm_otg_reset(struct usb_phy *phy)
#ifdef CONFIG_PM
-#define USB_PHY_SUSP_DIG_VOL 500000
-static int msm_hsusb_config_vddcx(int high)
+static int msm_hsusb_config_vddcx(struct msm_otg *motg, int high)
{
- int max_vol = USB_PHY_VDD_DIG_VOL_MAX;
+ int max_vol = motg->vdd_levels[VDD_LEVEL_MAX];
int min_vol;
int ret;
if (high)
- min_vol = USB_PHY_VDD_DIG_VOL_MIN;
+ min_vol = motg->vdd_levels[VDD_LEVEL_MIN];
else
- min_vol = USB_PHY_SUSP_DIG_VOL;
+ min_vol = motg->vdd_levels[VDD_LEVEL_NONE];
- ret = regulator_set_voltage(hsusb_vddcx, min_vol, max_vol);
+ ret = regulator_set_voltage(motg->vddcx, min_vol, max_vol);
if (ret) {
- pr_err("%s: unable to set the voltage for regulator "
- "HSUSB_VDDCX\n", __func__);
+ pr_err("Cannot set vddcx voltage\n");
return ret;
}
@@ -445,6 +469,7 @@ static int msm_otg_suspend(struct msm_otg *motg)
struct usb_phy *phy = &motg->phy;
struct usb_bus *bus = phy->otg->host;
struct msm_otg_platform_data *pdata = motg->pdata;
+ void __iomem *addr;
int cnt = 0;
if (atomic_read(&motg->in_lpm))
@@ -504,22 +529,23 @@ static int msm_otg_suspend(struct msm_otg *motg)
*/
writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD);
+ addr = USB_PHY_CTRL;
+ if (motg->phy_number)
+ addr = USB_PHY_CTRL2;
+
if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY &&
motg->pdata->otg_control == OTG_PMIC_CONTROL)
- writel(readl(USB_PHY_CTRL) | PHY_RETEN, USB_PHY_CTRL);
+ writel(readl(addr) | PHY_RETEN, addr);
clk_disable_unprepare(motg->pclk);
clk_disable_unprepare(motg->clk);
- if (motg->core_clk)
+ if (!IS_ERR(motg->core_clk))
clk_disable_unprepare(motg->core_clk);
- if (!IS_ERR(motg->pclk_src))
- clk_disable_unprepare(motg->pclk_src);
-
if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY &&
motg->pdata->otg_control == OTG_PMIC_CONTROL) {
- msm_hsusb_ldo_set_mode(0);
- msm_hsusb_config_vddcx(0);
+ msm_hsusb_ldo_set_mode(motg, 0);
+ msm_hsusb_config_vddcx(motg, 0);
}
if (device_may_wakeup(phy->dev))
@@ -539,25 +565,28 @@ static int msm_otg_resume(struct msm_otg *motg)
{
struct usb_phy *phy = &motg->phy;
struct usb_bus *bus = phy->otg->host;
+ void __iomem *addr;
int cnt = 0;
unsigned temp;
if (!atomic_read(&motg->in_lpm))
return 0;
- if (!IS_ERR(motg->pclk_src))
- clk_prepare_enable(motg->pclk_src);
-
clk_prepare_enable(motg->pclk);
clk_prepare_enable(motg->clk);
- if (motg->core_clk)
+ if (!IS_ERR(motg->core_clk))
clk_prepare_enable(motg->core_clk);
if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY &&
motg->pdata->otg_control == OTG_PMIC_CONTROL) {
- msm_hsusb_ldo_set_mode(1);
- msm_hsusb_config_vddcx(1);
- writel(readl(USB_PHY_CTRL) & ~PHY_RETEN, USB_PHY_CTRL);
+
+ addr = USB_PHY_CTRL;
+ if (motg->phy_number)
+ addr = USB_PHY_CTRL2;
+
+ msm_hsusb_ldo_set_mode(motg, 1);
+ msm_hsusb_config_vddcx(motg, 1);
+ writel(readl(addr) & ~PHY_RETEN, addr);
}
temp = readl(USB_USBCMD);
@@ -586,8 +615,7 @@ static int msm_otg_resume(struct msm_otg *motg)
* PHY. USB state can not be restored. Re-insertion
* of USB cable is the only way to get USB working.
*/
- dev_err(phy->dev, "Unable to resume USB."
- "Re-plugin the cable\n");
+ dev_err(phy->dev, "Unable to resume USB. Re-plugin the cable\n");
msm_otg_reset(phy);
}
@@ -687,7 +715,7 @@ static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
* Fail host registration if this board can support
* only peripheral configuration.
*/
- if (motg->pdata->mode == USB_PERIPHERAL) {
+ if (motg->pdata->mode == USB_DR_MODE_PERIPHERAL) {
dev_info(otg->phy->dev, "Host mode is not supported\n");
return -ENODEV;
}
@@ -716,7 +744,7 @@ static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
* Kick the state machine work, if peripheral is not supported
* or peripheral is already registered with us.
*/
- if (motg->pdata->mode == USB_HOST || otg->gadget) {
+ if (motg->pdata->mode == USB_DR_MODE_HOST || otg->gadget) {
pm_runtime_get_sync(otg->phy->dev);
schedule_work(&motg->sm_work);
}
@@ -760,7 +788,7 @@ static int msm_otg_set_peripheral(struct usb_otg *otg,
* Fail peripheral registration if this board can support
* only host configuration.
*/
- if (motg->pdata->mode == USB_HOST) {
+ if (motg->pdata->mode == USB_DR_MODE_HOST) {
dev_info(otg->phy->dev, "Peripheral mode is not supported\n");
return -ENODEV;
}
@@ -785,7 +813,7 @@ static int msm_otg_set_peripheral(struct usb_otg *otg,
* Kick the state machine work, if host is not supported
* or host is already registered with us.
*/
- if (motg->pdata->mode == USB_PERIPHERAL || otg->host) {
+ if (motg->pdata->mode == USB_DR_MODE_PERIPHERAL || otg->host) {
pm_runtime_get_sync(otg->phy->dev);
schedule_work(&motg->sm_work);
}
@@ -1106,7 +1134,7 @@ static void msm_otg_init_sm(struct msm_otg *motg)
u32 otgsc = readl(USB_OTGSC);
switch (pdata->mode) {
- case USB_OTG:
+ case USB_DR_MODE_OTG:
if (pdata->otg_control == OTG_PHY_CONTROL) {
if (otgsc & OTGSC_ID)
set_bit(ID, &motg->inputs);
@@ -1118,21 +1146,14 @@ static void msm_otg_init_sm(struct msm_otg *motg)
else
clear_bit(B_SESS_VLD, &motg->inputs);
} else if (pdata->otg_control == OTG_USER_CONTROL) {
- if (pdata->default_mode == USB_HOST) {
- clear_bit(ID, &motg->inputs);
- } else if (pdata->default_mode == USB_PERIPHERAL) {
- set_bit(ID, &motg->inputs);
- set_bit(B_SESS_VLD, &motg->inputs);
- } else {
set_bit(ID, &motg->inputs);
clear_bit(B_SESS_VLD, &motg->inputs);
- }
}
break;
- case USB_HOST:
+ case USB_DR_MODE_HOST:
clear_bit(ID, &motg->inputs);
break;
- case USB_PERIPHERAL:
+ case USB_DR_MODE_PERIPHERAL:
set_bit(ID, &motg->inputs);
if (otgsc & OTGSC_BSV)
set_bit(B_SESS_VLD, &motg->inputs);
@@ -1282,13 +1303,13 @@ static int msm_otg_mode_show(struct seq_file *s, void *unused)
switch (otg->phy->state) {
case OTG_STATE_A_HOST:
- seq_printf(s, "host\n");
+ seq_puts(s, "host\n");
break;
case OTG_STATE_B_PERIPHERAL:
- seq_printf(s, "peripheral\n");
+ seq_puts(s, "peripheral\n");
break;
default:
- seq_printf(s, "none\n");
+ seq_puts(s, "none\n");
break;
}
@@ -1308,7 +1329,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf,
char buf[16];
struct usb_otg *otg = motg->phy.otg;
int status = count;
- enum usb_mode_type req_mode;
+ enum usb_dr_mode req_mode;
memset(buf, 0x00, sizeof(buf));
@@ -1318,18 +1339,18 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf,
}
if (!strncmp(buf, "host", 4)) {
- req_mode = USB_HOST;
+ req_mode = USB_DR_MODE_HOST;
} else if (!strncmp(buf, "peripheral", 10)) {
- req_mode = USB_PERIPHERAL;
+ req_mode = USB_DR_MODE_PERIPHERAL;
} else if (!strncmp(buf, "none", 4)) {
- req_mode = USB_NONE;
+ req_mode = USB_DR_MODE_UNKNOWN;
} else {
status = -EINVAL;
goto out;
}
switch (req_mode) {
- case USB_NONE:
+ case USB_DR_MODE_UNKNOWN:
switch (otg->phy->state) {
case OTG_STATE_A_HOST:
case OTG_STATE_B_PERIPHERAL:
@@ -1340,7 +1361,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf,
goto out;
}
break;
- case USB_PERIPHERAL:
+ case USB_DR_MODE_PERIPHERAL:
switch (otg->phy->state) {
case OTG_STATE_B_IDLE:
case OTG_STATE_A_HOST:
@@ -1351,7 +1372,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf,
goto out;
}
break;
- case USB_HOST:
+ case USB_DR_MODE_HOST:
switch (otg->phy->state) {
case OTG_STATE_B_IDLE:
case OTG_STATE_B_PERIPHERAL:
@@ -1406,74 +1427,154 @@ static void msm_otg_debugfs_cleanup(void)
debugfs_remove(msm_otg_dbg_root);
}
-static int __init msm_otg_probe(struct platform_device *pdev)
+static struct of_device_id msm_otg_dt_match[] = {
+ {
+ .compatible = "qcom,usb-otg-ci",
+ .data = (void *) CI_45NM_INTEGRATED_PHY
+ },
+ {
+ .compatible = "qcom,usb-otg-snps",
+ .data = (void *) SNPS_28NM_INTEGRATED_PHY
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, msm_otg_dt_match);
+
+static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
+{
+ struct msm_otg_platform_data *pdata;
+ const struct of_device_id *id;
+ struct device_node *node = pdev->dev.of_node;
+ struct property *prop;
+ int len, ret, words;
+ u32 val, tmp[3];
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ motg->pdata = pdata;
+
+ id = of_match_device(msm_otg_dt_match, &pdev->dev);
+ pdata->phy_type = (enum msm_usb_phy_type) id->data;
+
+ motg->link_rst = devm_reset_control_get(&pdev->dev, "link");
+ if (IS_ERR(motg->link_rst))
+ return PTR_ERR(motg->link_rst);
+
+ motg->phy_rst = devm_reset_control_get(&pdev->dev, "phy");
+ if (IS_ERR(motg->phy_rst))
+ return PTR_ERR(motg->phy_rst);
+
+ pdata->mode = of_usb_get_dr_mode(node);
+ if (pdata->mode == USB_DR_MODE_UNKNOWN)
+ pdata->mode = USB_DR_MODE_OTG;
+
+ pdata->otg_control = OTG_PHY_CONTROL;
+ if (!of_property_read_u32(node, "qcom,otg-control", &val))
+ if (val == OTG_PMIC_CONTROL)
+ pdata->otg_control = val;
+
+ if (!of_property_read_u32(node, "qcom,phy-num", &val) && val < 2)
+ motg->phy_number = val;
+
+ motg->vdd_levels[VDD_LEVEL_NONE] = USB_PHY_SUSP_DIG_VOL;
+ motg->vdd_levels[VDD_LEVEL_MIN] = USB_PHY_VDD_DIG_VOL_MIN;
+ motg->vdd_levels[VDD_LEVEL_MAX] = USB_PHY_VDD_DIG_VOL_MAX;
+
+ if (of_get_property(node, "qcom,vdd-levels", &len) &&
+ len == sizeof(tmp)) {
+ of_property_read_u32_array(node, "qcom,vdd-levels",
+ tmp, len / sizeof(*tmp));
+ motg->vdd_levels[VDD_LEVEL_NONE] = tmp[VDD_LEVEL_NONE];
+ motg->vdd_levels[VDD_LEVEL_MIN] = tmp[VDD_LEVEL_MIN];
+ motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX];
+ }
+
+ prop = of_find_property(node, "qcom,phy-init-sequence", &len);
+ if (!prop || !len)
+ return 0;
+
+ words = len / sizeof(u32);
+
+ if (words >= ULPI_EXT_VENDOR_SPECIFIC) {
+ dev_warn(&pdev->dev, "Too big PHY init sequence %d\n", words);
+ return 0;
+ }
+
+ pdata->phy_init_seq = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+ if (!pdata->phy_init_seq) {
+ dev_warn(&pdev->dev, "No space for PHY init sequence\n");
+ return 0;
+ }
+
+ ret = of_property_read_u32_array(node, "qcom,phy-init-sequence",
+ pdata->phy_init_seq, words);
+ if (!ret)
+ pdata->phy_init_sz = words;
+
+ return 0;
+}
+
+static int msm_otg_probe(struct platform_device *pdev)
{
+ struct regulator_bulk_data regs[3];
int ret = 0;
+ struct device_node *np = pdev->dev.of_node;
+ struct msm_otg_platform_data *pdata;
struct resource *res;
struct msm_otg *motg;
struct usb_phy *phy;
+ void __iomem *phy_select;
- dev_info(&pdev->dev, "msm_otg probe\n");
- if (!dev_get_platdata(&pdev->dev)) {
- dev_err(&pdev->dev, "No platform data given. Bailing out\n");
- return -ENODEV;
- }
-
- motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL);
+ motg = devm_kzalloc(&pdev->dev, sizeof(struct msm_otg), GFP_KERNEL);
if (!motg) {
dev_err(&pdev->dev, "unable to allocate msm_otg\n");
return -ENOMEM;
}
- motg->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ if (!np)
+ return -ENXIO;
+ ret = msm_otg_read_dt(pdev, motg);
+ if (ret)
+ return ret;
+ }
+
+ motg->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg),
+ GFP_KERNEL);
if (!motg->phy.otg) {
dev_err(&pdev->dev, "unable to allocate msm_otg\n");
- ret = -ENOMEM;
- goto free_motg;
+ return -ENOMEM;
}
- motg->pdata = dev_get_platdata(&pdev->dev);
phy = &motg->phy;
phy->dev = &pdev->dev;
- motg->phy_reset_clk = clk_get(&pdev->dev, "usb_phy_clk");
+ motg->phy_reset_clk = devm_clk_get(&pdev->dev,
+ np ? "phy" : "usb_phy_clk");
if (IS_ERR(motg->phy_reset_clk)) {
dev_err(&pdev->dev, "failed to get usb_phy_clk\n");
- ret = PTR_ERR(motg->phy_reset_clk);
- goto free_motg;
+ return PTR_ERR(motg->phy_reset_clk);
}
- motg->clk = clk_get(&pdev->dev, "usb_hs_clk");
+ motg->clk = devm_clk_get(&pdev->dev, np ? "core" : "usb_hs_clk");
if (IS_ERR(motg->clk)) {
dev_err(&pdev->dev, "failed to get usb_hs_clk\n");
- ret = PTR_ERR(motg->clk);
- goto put_phy_reset_clk;
+ return PTR_ERR(motg->clk);
}
- clk_set_rate(motg->clk, 60000000);
/*
* If USB Core is running its protocol engine based on CORE CLK,
* CORE CLK must be running at >55Mhz for correct HSUSB
* operation and USB core cannot tolerate frequency changes on
- * CORE CLK. For such USB cores, vote for maximum clk frequency
- * on pclk source
+ * CORE CLK.
*/
- if (motg->pdata->pclk_src_name) {
- motg->pclk_src = clk_get(&pdev->dev,
- motg->pdata->pclk_src_name);
- if (IS_ERR(motg->pclk_src))
- goto put_clk;
- clk_set_rate(motg->pclk_src, INT_MAX);
- clk_prepare_enable(motg->pclk_src);
- } else
- motg->pclk_src = ERR_PTR(-ENOENT);
-
-
- motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk");
+ motg->pclk = devm_clk_get(&pdev->dev, np ? "iface" : "usb_hs_pclk");
if (IS_ERR(motg->pclk)) {
dev_err(&pdev->dev, "failed to get usb_hs_pclk\n");
- ret = PTR_ERR(motg->pclk);
- goto put_pclk_src;
+ return PTR_ERR(motg->pclk);
}
/*
@@ -1481,69 +1582,90 @@ static int __init msm_otg_probe(struct platform_device *pdev)
* clock is introduced to remove the dependency on AXI
* bus frequency.
*/
- motg->core_clk = clk_get(&pdev->dev, "usb_hs_core_clk");
- if (IS_ERR(motg->core_clk))
- motg->core_clk = NULL;
+ motg->core_clk = devm_clk_get(&pdev->dev,
+ np ? "alt_core" : "usb_hs_core_clk");
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "failed to get platform resource mem\n");
- ret = -ENODEV;
- goto put_core_clk;
- }
+ if (!res)
+ return -EINVAL;
+ motg->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!motg->regs)
+ return -ENOMEM;
- motg->regs = ioremap(res->start, resource_size(res));
- if (!motg->regs) {
- dev_err(&pdev->dev, "ioremap failed\n");
- ret = -ENOMEM;
- goto put_core_clk;
+ /*
+ * NOTE: The PHYs can be multiplexed between the chipidea controller
+ * and the dwc3 controller, using a single bit. It is important that
+ * the dwc3 driver does not set this bit in an incompatible way.
+ */
+ if (motg->phy_number) {
+ phy_select = devm_ioremap_nocache(&pdev->dev, USB2_PHY_SEL, 4);
+ if (IS_ERR(phy_select))
+ return PTR_ERR(phy_select);
+ /* Enable second PHY with the OTG port */
+ writel(0x1, phy_select);
}
+
dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs);
motg->irq = platform_get_irq(pdev, 0);
- if (!motg->irq) {
+ if (motg->irq < 0) {
dev_err(&pdev->dev, "platform_get_irq failed\n");
- ret = -ENODEV;
- goto free_regs;
+ return motg->irq;
}
+ regs[0].supply = "vddcx";
+ regs[1].supply = "v3p3";
+ regs[2].supply = "v1p8";
+
+ ret = devm_regulator_bulk_get(motg->phy.dev, ARRAY_SIZE(regs), regs);
+ if (ret)
+ return ret;
+
+ motg->vddcx = regs[0].consumer;
+ motg->v3p3 = regs[1].consumer;
+ motg->v1p8 = regs[2].consumer;
+
+ clk_set_rate(motg->clk, 60000000);
+
clk_prepare_enable(motg->clk);
clk_prepare_enable(motg->pclk);
+ if (!IS_ERR(motg->core_clk))
+ clk_prepare_enable(motg->core_clk);
+
ret = msm_hsusb_init_vddcx(motg, 1);
if (ret) {
dev_err(&pdev->dev, "hsusb vddcx configuration failed\n");
- goto free_regs;
+ goto disable_clks;
}
ret = msm_hsusb_ldo_init(motg, 1);
if (ret) {
dev_err(&pdev->dev, "hsusb vreg configuration failed\n");
- goto vddcx_exit;
+ goto disable_vddcx;
}
- ret = msm_hsusb_ldo_set_mode(1);
+ ret = msm_hsusb_ldo_set_mode(motg, 1);
if (ret) {
dev_err(&pdev->dev, "hsusb vreg enable failed\n");
- goto ldo_exit;
+ goto disable_ldo;
}
- if (motg->core_clk)
- clk_prepare_enable(motg->core_clk);
-
writel(0, USB_USBINTR);
writel(0, USB_OTGSC);
INIT_WORK(&motg->sm_work, msm_otg_sm_work);
INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work);
- ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED,
+ ret = devm_request_irq(&pdev->dev, motg->irq, msm_otg_irq, IRQF_SHARED,
"msm_otg", motg);
if (ret) {
dev_err(&pdev->dev, "request irq failed\n");
- goto disable_clks;
+ goto disable_ldo;
}
- phy->init = msm_otg_reset;
+ phy->init = msm_phy_init;
phy->set_power = msm_otg_set_power;
+ phy->notify_disconnect = msm_phy_notify_disconnect;
+ phy->type = USB_PHY_TYPE_USB2;
phy->io_ops = &msm_otg_io_ops;
@@ -1551,54 +1673,38 @@ static int __init msm_otg_probe(struct platform_device *pdev)
phy->otg->set_host = msm_otg_set_host;
phy->otg->set_peripheral = msm_otg_set_peripheral;
- ret = usb_add_phy(&motg->phy, USB_PHY_TYPE_USB2);
+ msm_usb_reset(phy);
+
+ ret = usb_add_phy_dev(&motg->phy);
if (ret) {
dev_err(&pdev->dev, "usb_add_phy failed\n");
- goto free_irq;
+ goto disable_ldo;
}
platform_set_drvdata(pdev, motg);
device_init_wakeup(&pdev->dev, 1);
- if (motg->pdata->mode == USB_OTG &&
- motg->pdata->otg_control == OTG_USER_CONTROL) {
+ if (motg->pdata->mode == USB_DR_MODE_OTG &&
+ motg->pdata->otg_control == OTG_USER_CONTROL) {
ret = msm_otg_debugfs_init(motg);
if (ret)
- dev_dbg(&pdev->dev, "mode debugfs file is"
- "not available\n");
+ dev_dbg(&pdev->dev, "Can not create mode change file\n");
}
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
return 0;
-free_irq:
- free_irq(motg->irq, motg);
+
+disable_ldo:
+ msm_hsusb_ldo_init(motg, 0);
+disable_vddcx:
+ msm_hsusb_init_vddcx(motg, 0);
disable_clks:
clk_disable_unprepare(motg->pclk);
clk_disable_unprepare(motg->clk);
-ldo_exit:
- msm_hsusb_ldo_init(motg, 0);
-vddcx_exit:
- msm_hsusb_init_vddcx(motg, 0);
-free_regs:
- iounmap(motg->regs);
-put_core_clk:
- if (motg->core_clk)
- clk_put(motg->core_clk);
- clk_put(motg->pclk);
-put_pclk_src:
- if (!IS_ERR(motg->pclk_src)) {
- clk_disable_unprepare(motg->pclk_src);
- clk_put(motg->pclk_src);
- }
-put_clk:
- clk_put(motg->clk);
-put_phy_reset_clk:
- clk_put(motg->phy_reset_clk);
-free_motg:
- kfree(motg->phy.otg);
- kfree(motg);
+ if (!IS_ERR(motg->core_clk))
+ clk_disable_unprepare(motg->core_clk);
return ret;
}
@@ -1621,7 +1727,7 @@ static int msm_otg_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
usb_remove_phy(phy);
- free_irq(motg->irq, motg);
+ disable_irq(motg->irq);
/*
* Put PHY in low power mode.
@@ -1641,26 +1747,12 @@ static int msm_otg_remove(struct platform_device *pdev)
clk_disable_unprepare(motg->pclk);
clk_disable_unprepare(motg->clk);
- if (motg->core_clk)
+ if (!IS_ERR(motg->core_clk))
clk_disable_unprepare(motg->core_clk);
- if (!IS_ERR(motg->pclk_src)) {
- clk_disable_unprepare(motg->pclk_src);
- clk_put(motg->pclk_src);
- }
msm_hsusb_ldo_init(motg, 0);
- iounmap(motg->regs);
pm_runtime_set_suspended(&pdev->dev);
- clk_put(motg->phy_reset_clk);
- clk_put(motg->pclk);
- clk_put(motg->clk);
- if (motg->core_clk)
- clk_put(motg->core_clk);
-
- kfree(motg->phy.otg);
- kfree(motg);
-
return 0;
}
@@ -1740,15 +1832,17 @@ static const struct dev_pm_ops msm_otg_dev_pm_ops = {
};
static struct platform_driver msm_otg_driver = {
+ .probe = msm_otg_probe,
.remove = msm_otg_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.pm = &msm_otg_dev_pm_ops,
+ .of_match_table = msm_otg_dt_match,
},
};
-module_platform_driver_probe(msm_otg_driver, msm_otg_probe);
+module_platform_driver(msm_otg_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MSM USB transceiver driver");