summaryrefslogtreecommitdiff
path: root/drivers/usb/chipidea
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/chipidea')
-rw-r--r--drivers/usb/chipidea/Kconfig8
-rw-r--r--drivers/usb/chipidea/Makefile1
-rw-r--r--drivers/usb/chipidea/ci.h22
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c4
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.h1
-rw-r--r--drivers/usb/chipidea/ci_hdrc_msm.c280
-rw-r--r--drivers/usb/chipidea/ci_hdrc_usb2.c4
-rw-r--r--drivers/usb/chipidea/core.c173
-rw-r--r--drivers/usb/chipidea/host.c10
-rw-r--r--drivers/usb/chipidea/otg.c99
-rw-r--r--drivers/usb/chipidea/otg_fsm.c14
-rw-r--r--drivers/usb/chipidea/udc.c17
-rw-r--r--drivers/usb/chipidea/udc.h12
-rw-r--r--drivers/usb/chipidea/ulpi.c113
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c86
15 files changed, 643 insertions, 201 deletions
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig
index 5e5b9eb7ebf6..fc96f5cdcb5c 100644
--- a/drivers/usb/chipidea/Kconfig
+++ b/drivers/usb/chipidea/Kconfig
@@ -2,6 +2,7 @@ config USB_CHIPIDEA
tristate "ChipIdea Highspeed Dual Role Controller"
depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA
select EXTCON
+ select RESET_CONTROLLER
help
Say Y here if your system has a dual role high speed USB
controller based on ChipIdea silicon IP. It supports:
@@ -38,4 +39,11 @@ config USB_CHIPIDEA_HOST
Say Y here to enable host controller functionality of the
ChipIdea driver.
+config USB_CHIPIDEA_ULPI
+ bool "ChipIdea ULPI PHY support"
+ depends on USB_ULPI_BUS=y || USB_ULPI_BUS=USB_CHIPIDEA
+ help
+ Say Y here if you have a ULPI PHY attached to your ChipIdea
+ controller.
+
endif
diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile
index 518e445476c3..39fca5715ed3 100644
--- a/drivers/usb/chipidea/Makefile
+++ b/drivers/usb/chipidea/Makefile
@@ -4,6 +4,7 @@ ci_hdrc-y := core.o otg.o debug.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o
ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
+ci_hdrc-$(CONFIG_USB_CHIPIDEA_ULPI) += ulpi.o
# Glue/Bridge layers go here
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index cd414559040f..59e22389c10b 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -18,6 +18,8 @@
#include <linux/usb.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg-fsm.h>
+#include <linux/usb/otg.h>
+#include <linux/ulpi/interface.h>
/******************************************************************************
* DEFINE
@@ -52,6 +54,7 @@ enum ci_hw_regs {
OP_ENDPTLISTADDR,
OP_TTCTRL,
OP_BURSTSIZE,
+ OP_ULPI_VIEWPORT,
OP_PORTSC,
OP_DEVLC,
OP_OTGSC,
@@ -187,6 +190,8 @@ struct hw_bank {
* @test_mode: the selected test mode
* @platdata: platform specific information supplied by parent device
* @vbus_active: is VBUS active
+ * @ulpi: pointer to ULPI device, if any
+ * @ulpi_ops: ULPI read/write ops for this device
* @phy: pointer to PHY, if any
* @usb_phy: pointer to USB PHY, if any and if using the USB PHY framework
* @hcd: pointer to usb_hcd for ehci host driver
@@ -236,6 +241,10 @@ struct ci_hdrc {
struct ci_hdrc_platform_data *platdata;
int vbus_active;
+#ifdef CONFIG_USB_CHIPIDEA_ULPI
+ struct ulpi *ulpi;
+ struct ulpi_ops ulpi_ops;
+#endif
struct phy *phy;
/* old usb_phy interface */
struct usb_phy *usb_phy;
@@ -418,6 +427,16 @@ static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci)
#endif
}
+#if IS_ENABLED(CONFIG_USB_CHIPIDEA_ULPI)
+int ci_ulpi_init(struct ci_hdrc *ci);
+void ci_ulpi_exit(struct ci_hdrc *ci);
+int ci_ulpi_resume(struct ci_hdrc *ci);
+#else
+static inline int ci_ulpi_init(struct ci_hdrc *ci) { return 0; }
+static inline void ci_ulpi_exit(struct ci_hdrc *ci) { }
+static inline int ci_ulpi_resume(struct ci_hdrc *ci) { return 0; }
+#endif
+
u32 hw_read_intr_enable(struct ci_hdrc *ci);
u32 hw_read_intr_status(struct ci_hdrc *ci);
@@ -428,8 +447,7 @@ int hw_port_test_set(struct ci_hdrc *ci, u8 mode);
u8 hw_port_test_get(struct ci_hdrc *ci);
-int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask,
- u32 value, unsigned int timeout_ms);
+void hw_phymode_configure(struct ci_hdrc *ci);
void ci_platform_configure(struct ci_hdrc *ci);
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index 099179457f60..5f4a8157fad8 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -18,6 +18,7 @@
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/usb/chipidea.h>
+#include <linux/usb/of.h>
#include <linux/clk.h>
#include "ci.h"
@@ -146,6 +147,9 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
if (of_find_property(np, "external-vbus-divider", NULL))
data->evdo = 1;
+ if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI)
+ data->ulpi = 1;
+
return data;
}
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h
index 409aa5ca8dda..d666c9f036ba 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.h
+++ b/drivers/usb/chipidea/ci_hdrc_imx.h
@@ -19,6 +19,7 @@ struct imx_usbmisc_data {
unsigned int disable_oc:1; /* over current detect disabled */
unsigned int oc_polarity:1; /* over current polarity if oc enabled */
unsigned int evdo:1; /* set external vbus divider option */
+ unsigned int ulpi:1; /* connected to an ULPI phy */
};
int imx_usbmisc_init(struct imx_usbmisc_data *);
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index 3889809fd0c4..0bdfcdcbf7a5 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -8,90 +8,292 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/usb/msm_hsusb_hw.h>
-#include <linux/usb/ulpi.h>
-#include <linux/usb/gadget.h>
#include <linux/usb/chipidea.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/io.h>
+#include <linux/reset-controller.h>
+#include <linux/extcon.h>
+#include <linux/of.h>
#include "ci.h"
-#define MSM_USB_BASE (ci->hw_bank.abs)
+#define HS_PHY_AHB_MODE 0x0098
-static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
+#define HS_PHY_GENCONFIG 0x009c
+#define HS_PHY_TXFIFO_IDLE_FORCE_DIS BIT(4)
+
+#define HS_PHY_GENCONFIG_2 0x00a0
+#define HS_PHY_SESS_VLD_CTRL_EN BIT(7)
+#define HS_PHY_ULPI_TX_PKT_EN_CLR_FIX BIT(19)
+
+#define HSPHY_SESS_VLD_CTRL BIT(25)
+
+/* Vendor base starts at 0x200 beyond CI base */
+#define HS_PHY_CTRL 0x0040
+#define HS_PHY_SEC_CTRL 0x0078
+#define HS_PHY_DIG_CLAMP_N BIT(16)
+#define HS_PHY_POR_ASSERT BIT(0)
+
+struct ci_hdrc_msm {
+ struct platform_device *ci;
+ struct clk *core_clk;
+ struct clk *iface_clk;
+ struct clk *fs_clk;
+ struct ci_hdrc_platform_data pdata;
+ struct reset_controller_dev rcdev;
+ bool secondary_phy;
+ bool hsic;
+ void __iomem *base;
+};
+
+static int
+ci_hdrc_msm_por_reset(struct reset_controller_dev *r, unsigned long id)
{
- struct device *dev = ci->gadget.dev.parent;
+ struct ci_hdrc_msm *ci_msm = container_of(r, struct ci_hdrc_msm, rcdev);
+ void __iomem *addr = ci_msm->base;
+ u32 val;
+
+ if (id)
+ addr += HS_PHY_SEC_CTRL;
+ else
+ addr += HS_PHY_CTRL;
+
+ val = readl_relaxed(addr);
+ val |= HS_PHY_POR_ASSERT;
+ writel(val, addr);
+ /*
+ * wait for minimum 10 microseconds as suggested by manual.
+ * Use a slightly larger value since the exact value didn't
+ * work 100% of the time.
+ */
+ udelay(12);
+ val &= ~HS_PHY_POR_ASSERT;
+ writel(val, addr);
+
+ return 0;
+}
+
+static const struct reset_control_ops ci_hdrc_msm_reset_ops = {
+ .reset = ci_hdrc_msm_por_reset,
+};
+
+static int ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
+{
+ struct device *dev = ci->dev->parent;
+ struct ci_hdrc_msm *msm_ci = dev_get_drvdata(dev);
+ int ret;
switch (event) {
case CI_HDRC_CONTROLLER_RESET_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
- writel(0, USB_AHBBURST);
+
+ hw_phymode_configure(ci);
+ if (msm_ci->secondary_phy) {
+ u32 val = readl_relaxed(msm_ci->base + HS_PHY_SEC_CTRL);
+ val |= HS_PHY_DIG_CLAMP_N;
+ writel_relaxed(val, msm_ci->base + HS_PHY_SEC_CTRL);
+ }
+
+ ret = phy_init(ci->phy);
+ if (ret)
+ return ret;
+
+ ret = phy_power_on(ci->phy);
+ if (ret) {
+ phy_exit(ci->phy);
+ return ret;
+ }
+
/* use AHB transactor, allow posted data writes */
- writel(0x8, USB_AHBMODE);
- usb_phy_init(ci->usb_phy);
+ hw_write_id_reg(ci, HS_PHY_AHB_MODE, 0xffffffff, 0x8);
+
+ /* workaround for rx buffer collision issue */
+ hw_write_id_reg(ci, HS_PHY_GENCONFIG,
+ HS_PHY_TXFIFO_IDLE_FORCE_DIS, 0);
+
+ if (!msm_ci->hsic)
+ hw_write_id_reg(ci, HS_PHY_GENCONFIG_2,
+ HS_PHY_ULPI_TX_PKT_EN_CLR_FIX, 0);
+
+ if (!IS_ERR(ci->platdata->vbus_extcon.edev)) {
+ hw_write_id_reg(ci, HS_PHY_GENCONFIG_2,
+ HS_PHY_SESS_VLD_CTRL_EN,
+ HS_PHY_SESS_VLD_CTRL_EN);
+ hw_write(ci, OP_USBCMD, HSPHY_SESS_VLD_CTRL,
+ HSPHY_SESS_VLD_CTRL);
+
+ }
break;
case CI_HDRC_CONTROLLER_STOPPED_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n");
- /*
- * Put the phy in non-driving mode. Otherwise host
- * may not detect soft-disconnection.
- */
- usb_phy_notify_disconnect(ci->usb_phy, USB_SPEED_UNKNOWN);
+ phy_power_off(ci->phy);
+ phy_exit(ci->phy);
break;
default:
dev_dbg(dev, "unknown ci_hdrc event\n");
break;
}
+
+ return 0;
}
-static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = {
- .name = "ci_hdrc_msm",
- .capoffset = DEF_CAPOFFSET,
- .flags = CI_HDRC_REGS_SHARED |
- CI_HDRC_DISABLE_STREAMING,
+static int ci_hdrc_msm_mux_phy(struct ci_hdrc_msm *ci,
+ struct platform_device *pdev)
+{
+ struct regmap *regmap;
+ struct device *dev = &pdev->dev;
+ struct of_phandle_args args;
+ u32 val;
+ int ret;
- .notify_event = ci_hdrc_msm_notify_event,
-};
+ ret = of_parse_phandle_with_fixed_args(dev->of_node, "phy-select", 2, 0,
+ &args);
+ if (ret)
+ return 0;
+
+ regmap = syscon_node_to_regmap(args.np);
+ of_node_put(args.np);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ret = regmap_write(regmap, args.args[0], args.args[1]);
+ if (ret)
+ return ret;
+
+ ci->secondary_phy = !!args.args[1];
+ if (ci->secondary_phy) {
+ val = readl_relaxed(ci->base + HS_PHY_SEC_CTRL);
+ val |= HS_PHY_DIG_CLAMP_N;
+ writel_relaxed(val, ci->base + HS_PHY_SEC_CTRL);
+ }
+
+ return 0;
+}
static int ci_hdrc_msm_probe(struct platform_device *pdev)
{
+ struct ci_hdrc_msm *ci;
struct platform_device *plat_ci;
- struct usb_phy *phy;
+ struct clk *clk;
+ struct reset_control *reset;
+ struct resource *res;
+ int ret;
+ struct device_node *ulpi_node, *phy_node;
dev_dbg(&pdev->dev, "ci_hdrc_msm_probe\n");
- /*
- * OTG(PHY) driver takes care of PHY initialization, clock management,
- * powering up VBUS, mapping of registers address space and power
- * management.
- */
- phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
- if (IS_ERR(phy))
- return PTR_ERR(phy);
+ ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
+ if (!ci)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, ci);
+
+ ci->pdata.name = "ci_hdrc_msm";
+ ci->pdata.capoffset = DEF_CAPOFFSET;
+ ci->pdata.flags = CI_HDRC_REGS_SHARED | CI_HDRC_DISABLE_STREAMING |
+ CI_HDRC_OVERRIDE_AHB_BURST |
+ CI_HDRC_OVERRIDE_PHY_CONTROL;
+ ci->pdata.notify_event = ci_hdrc_msm_notify_event;
+
+ reset = devm_reset_control_get(&pdev->dev, "core");
+ if (IS_ERR(reset))
+ return PTR_ERR(reset);
+
+ ci->core_clk = clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
- ci_hdrc_msm_platdata.usb_phy = phy;
+ ci->iface_clk = clk = devm_clk_get(&pdev->dev, "iface");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
- plat_ci = ci_hdrc_add_device(&pdev->dev,
- pdev->resource, pdev->num_resources,
- &ci_hdrc_msm_platdata);
+ ci->fs_clk = clk = devm_clk_get(&pdev->dev, "fs");
+ if (IS_ERR(clk)) {
+ if (PTR_ERR(clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ ci->fs_clk = NULL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ ci->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ci->base))
+ return PTR_ERR(ci->base);
+
+ ci->rcdev.owner = THIS_MODULE;
+ ci->rcdev.ops = &ci_hdrc_msm_reset_ops;
+ ci->rcdev.of_node = pdev->dev.of_node;
+ ci->rcdev.nr_resets = 2;
+ ret = reset_controller_register(&ci->rcdev);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(ci->fs_clk);
+ if (ret)
+ goto err_fs;
+
+ reset_control_assert(reset);
+ usleep_range(10000, 12000);
+ reset_control_deassert(reset);
+
+ clk_disable_unprepare(ci->fs_clk);
+
+ ret = clk_prepare_enable(ci->core_clk);
+ if (ret)
+ goto err_fs;
+
+ ret = clk_prepare_enable(ci->iface_clk);
+ if (ret)
+ goto err_iface;
+
+ ret = ci_hdrc_msm_mux_phy(ci, pdev);
+ if (ret)
+ goto err_mux;
+
+ ulpi_node = of_find_node_by_name(pdev->dev.of_node, "ulpi");
+ if (ulpi_node) {
+ phy_node = of_get_next_available_child(ulpi_node, NULL);
+ ci->hsic = of_device_is_compatible(phy_node, "qcom,usb-hsic-phy");
+ of_node_put(phy_node);
+ }
+ of_node_put(ulpi_node);
+
+ plat_ci = ci_hdrc_add_device(&pdev->dev, pdev->resource,
+ pdev->num_resources, &ci->pdata);
if (IS_ERR(plat_ci)) {
- dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
- return PTR_ERR(plat_ci);
+ ret = PTR_ERR(plat_ci);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
+ goto err_mux;
}
- platform_set_drvdata(pdev, plat_ci);
+ ci->ci = plat_ci;
+ pm_runtime_set_active(&pdev->dev);
pm_runtime_no_callbacks(&pdev->dev);
pm_runtime_enable(&pdev->dev);
return 0;
+
+err_mux:
+ clk_disable_unprepare(ci->iface_clk);
+err_iface:
+ clk_disable_unprepare(ci->core_clk);
+err_fs:
+ reset_controller_unregister(&ci->rcdev);
+ return ret;
}
static int ci_hdrc_msm_remove(struct platform_device *pdev)
{
- struct platform_device *plat_ci = platform_get_drvdata(pdev);
+ struct ci_hdrc_msm *ci = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
- ci_hdrc_remove_device(plat_ci);
+ ci_hdrc_remove_device(ci->ci);
+ clk_disable_unprepare(ci->iface_clk);
+ clk_disable_unprepare(ci->core_clk);
+ reset_controller_unregister(&ci->rcdev);
return 0;
}
diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c
index 4456d2cf80ff..d162cc0bb8ce 100644
--- a/drivers/usb/chipidea/ci_hdrc_usb2.c
+++ b/drivers/usb/chipidea/ci_hdrc_usb2.c
@@ -74,10 +74,6 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev)
}
}
- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
- if (ret)
- goto clk_err;
-
ci_pdata->name = dev_name(dev);
priv->ci_pdev = ci_hdrc_add_device(dev, pdev->resource,
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 3dbb4a21ab44..79ad8e91632e 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -62,7 +62,6 @@
#include <linux/usb/chipidea.h>
#include <linux/usb/of.h>
#include <linux/of.h>
-#include <linux/phy.h>
#include <linux/regulator/consumer.h>
#include <linux/usb/ehci_def.h>
@@ -86,6 +85,7 @@ static const u8 ci_regs_nolpm[] = {
[OP_ENDPTLISTADDR] = 0x18U,
[OP_TTCTRL] = 0x1CU,
[OP_BURSTSIZE] = 0x20U,
+ [OP_ULPI_VIEWPORT] = 0x30U,
[OP_PORTSC] = 0x44U,
[OP_DEVLC] = 0x84U,
[OP_OTGSC] = 0x64U,
@@ -110,6 +110,7 @@ static const u8 ci_regs_lpm[] = {
[OP_ENDPTLISTADDR] = 0x18U,
[OP_TTCTRL] = 0x1CU,
[OP_BURSTSIZE] = 0x20U,
+ [OP_ULPI_VIEWPORT] = 0x30U,
[OP_PORTSC] = 0x44U,
[OP_DEVLC] = 0x84U,
[OP_OTGSC] = 0xC4U,
@@ -285,7 +286,7 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
return 0;
}
-static void hw_phymode_configure(struct ci_hdrc *ci)
+void hw_phymode_configure(struct ci_hdrc *ci)
{
u32 portsc, lpm, sts = 0;
@@ -325,6 +326,7 @@ static void hw_phymode_configure(struct ci_hdrc *ci)
hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS);
}
}
+EXPORT_SYMBOL_GPL(hw_phymode_configure);
/**
* _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy
@@ -361,6 +363,9 @@ static int _ci_usb_phy_init(struct ci_hdrc *ci)
*/
static void ci_usb_phy_exit(struct ci_hdrc *ci)
{
+ if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL)
+ return;
+
if (ci->phy) {
phy_power_off(ci->phy);
phy_exit(ci->phy);
@@ -379,6 +384,9 @@ static int ci_usb_phy_init(struct ci_hdrc *ci)
{
int ret;
+ if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL)
+ return 0;
+
switch (ci->platdata->phy_mode) {
case USBPHY_INTERFACE_MODE_UTMI:
case USBPHY_INTERFACE_MODE_UTMIW:
@@ -419,13 +427,21 @@ void ci_platform_configure(struct ci_hdrc *ci)
is_device_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_DC;
is_host_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_HC;
- if (is_device_mode &&
- (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING))
- hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
+ if (is_device_mode) {
+ phy_set_mode(ci->phy, PHY_MODE_USB_DEVICE);
+
+ if (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING)
+ hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS,
+ USBMODE_CI_SDIS);
+ }
+
+ if (is_host_mode) {
+ phy_set_mode(ci->phy, PHY_MODE_USB_HOST);
- if (is_host_mode &&
- (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING))
- hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
+ if (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING)
+ hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS,
+ USBMODE_CI_SDIS);
+ }
if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) {
if (ci->hw_bank.lpm)
@@ -495,9 +511,12 @@ int hw_device_reset(struct ci_hdrc *ci)
return ret;
}
- if (ci->platdata->notify_event)
- ci->platdata->notify_event(ci,
+ if (ci->platdata->notify_event) {
+ ret = ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_RESET_EVENT);
+ if (ret)
+ return ret;
+ }
/* USBMODE should be configured step by step */
hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
@@ -516,38 +535,6 @@ int hw_device_reset(struct ci_hdrc *ci)
return 0;
}
-/**
- * hw_wait_reg: wait the register value
- *
- * Sometimes, it needs to wait register value before going on.
- * Eg, when switch to device mode, the vbus value should be lower
- * than OTGSC_BSV before connects to host.
- *
- * @ci: the controller
- * @reg: register index
- * @mask: mast bit
- * @value: the bit value to wait
- * @timeout_ms: timeout in millisecond
- *
- * This function returns an error code if timeout
- */
-int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask,
- u32 value, unsigned int timeout_ms)
-{
- unsigned long elapse = jiffies + msecs_to_jiffies(timeout_ms);
-
- while (hw_read(ci, reg, mask) != value) {
- if (time_after(jiffies, elapse)) {
- dev_err(ci->dev, "timeout waiting for %08x in %d\n",
- mask, reg);
- return -ETIMEDOUT;
- }
- msleep(20);
- }
-
- return 0;
-}
-
static irqreturn_t ci_irq(int irq, void *data)
{
struct ci_hdrc *ci = data;
@@ -601,35 +588,14 @@ static irqreturn_t ci_irq(int irq, void *data)
return ret;
}
-static int ci_vbus_notifier(struct notifier_block *nb, unsigned long event,
- void *ptr)
-{
- struct ci_hdrc_cable *vbus = container_of(nb, struct ci_hdrc_cable, nb);
- struct ci_hdrc *ci = vbus->ci;
-
- if (event)
- vbus->state = true;
- else
- vbus->state = false;
-
- vbus->changed = true;
-
- ci_irq(ci->irq, ci);
- return NOTIFY_DONE;
-}
-
-static int ci_id_notifier(struct notifier_block *nb, unsigned long event,
- void *ptr)
+static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
+ void *ptr)
{
- struct ci_hdrc_cable *id = container_of(nb, struct ci_hdrc_cable, nb);
- struct ci_hdrc *ci = id->ci;
+ struct ci_hdrc_cable *cbl = container_of(nb, struct ci_hdrc_cable, nb);
+ struct ci_hdrc *ci = cbl->ci;
- if (event)
- id->state = false;
- else
- id->state = true;
-
- id->changed = true;
+ cbl->connected = event;
+ cbl->changed = true;
ci_irq(ci->irq, ci);
return NOTIFY_DONE;
@@ -738,27 +704,27 @@ static int ci_get_platdata(struct device *dev,
}
cable = &platdata->vbus_extcon;
- cable->nb.notifier_call = ci_vbus_notifier;
+ cable->nb.notifier_call = ci_cable_notifier;
cable->edev = ext_vbus;
if (!IS_ERR(ext_vbus)) {
- ret = extcon_get_cable_state_(cable->edev, EXTCON_USB);
+ ret = extcon_get_state(cable->edev, EXTCON_USB);
if (ret)
- cable->state = true;
+ cable->connected = true;
else
- cable->state = false;
+ cable->connected = false;
}
cable = &platdata->id_extcon;
- cable->nb.notifier_call = ci_id_notifier;
+ cable->nb.notifier_call = ci_cable_notifier;
cable->edev = ext_id;
if (!IS_ERR(ext_id)) {
- ret = extcon_get_cable_state_(cable->edev, EXTCON_USB_HOST);
+ ret = extcon_get_state(cable->edev, EXTCON_USB_HOST);
if (ret)
- cable->state = false;
+ cable->connected = true;
else
- cable->state = true;
+ cable->connected = false;
}
return 0;
}
@@ -771,8 +737,8 @@ static int ci_extcon_register(struct ci_hdrc *ci)
id = &ci->platdata->id_extcon;
id->ci = ci;
if (!IS_ERR(id->edev)) {
- ret = extcon_register_notifier(id->edev, EXTCON_USB_HOST,
- &id->nb);
+ ret = devm_extcon_register_notifier(ci->dev, id->edev,
+ EXTCON_USB_HOST, &id->nb);
if (ret < 0) {
dev_err(ci->dev, "register ID failed\n");
return ret;
@@ -782,11 +748,9 @@ static int ci_extcon_register(struct ci_hdrc *ci)
vbus = &ci->platdata->vbus_extcon;
vbus->ci = ci;
if (!IS_ERR(vbus->edev)) {
- ret = extcon_register_notifier(vbus->edev, EXTCON_USB,
- &vbus->nb);
+ ret = devm_extcon_register_notifier(ci->dev, vbus->edev,
+ EXTCON_USB, &vbus->nb);
if (ret < 0) {
- extcon_unregister_notifier(id->edev, EXTCON_USB_HOST,
- &id->nb);
dev_err(ci->dev, "register VBUS failed\n");
return ret;
}
@@ -795,20 +759,6 @@ static int ci_extcon_register(struct ci_hdrc *ci)
return 0;
}
-static void ci_extcon_unregister(struct ci_hdrc *ci)
-{
- struct ci_hdrc_cable *cable;
-
- cable = &ci->platdata->id_extcon;
- if (!IS_ERR(cable->edev))
- extcon_unregister_notifier(cable->edev, EXTCON_USB_HOST,
- &cable->nb);
-
- cable = &ci->platdata->vbus_extcon;
- if (!IS_ERR(cable->edev))
- extcon_unregister_notifier(cable->edev, EXTCON_USB, &cable->nb);
-}
-
static DEFINE_IDA(ci_ida);
struct platform_device *ci_hdrc_add_device(struct device *dev,
@@ -921,6 +871,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
CI_HDRC_IMX28_WRITE_FIX);
ci->supports_runtime_pm = !!(ci->platdata->flags &
CI_HDRC_SUPPORTS_RUNTIME_PM);
+ platform_set_drvdata(pdev, ci);
ret = hw_device_init(ci, base);
if (ret < 0) {
@@ -928,6 +879,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return -ENODEV;
}
+ ret = ci_ulpi_init(ci);
+ if (ret)
+ return ret;
+
if (ci->platdata->phy) {
ci->phy = ci->platdata->phy;
} else if (ci->platdata->usb_phy) {
@@ -938,11 +893,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
/* if both generic PHY and USB PHY layers aren't enabled */
if (PTR_ERR(ci->phy) == -ENOSYS &&
- PTR_ERR(ci->usb_phy) == -ENXIO)
- return -ENXIO;
+ PTR_ERR(ci->usb_phy) == -ENXIO) {
+ ret = -ENXIO;
+ goto ulpi_exit;
+ }
- if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy))
- return -EPROBE_DEFER;
+ if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) {
+ ret = -EPROBE_DEFER;
+ goto ulpi_exit;
+ }
if (IS_ERR(ci->phy))
ci->phy = NULL;
@@ -1027,7 +986,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
}
}
- platform_set_drvdata(pdev, ci);
ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED,
ci->platdata->name, ci);
if (ret)
@@ -1054,11 +1012,12 @@ static int ci_hdrc_probe(struct platform_device *pdev)
if (!ret)
return 0;
- ci_extcon_unregister(ci);
stop:
ci_role_destroy(ci);
deinit_phy:
ci_usb_phy_exit(ci);
+ulpi_exit:
+ ci_ulpi_exit(ci);
return ret;
}
@@ -1074,10 +1033,10 @@ static int ci_hdrc_remove(struct platform_device *pdev)
}
dbg_remove_files(ci);
- ci_extcon_unregister(ci);
ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true);
ci_usb_phy_exit(ci);
+ ci_ulpi_exit(ci);
return 0;
}
@@ -1125,6 +1084,7 @@ static void ci_controller_suspend(struct ci_hdrc *ci)
static int ci_controller_resume(struct device *dev)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
+ int ret;
dev_dbg(dev, "at %s\n", __func__);
@@ -1134,6 +1094,11 @@ static int ci_controller_resume(struct device *dev)
}
ci_hdrc_enter_lpm(ci, false);
+
+ ret = ci_ulpi_resume(ci);
+ if (ret)
+ return ret;
+
if (ci->usb_phy) {
usb_phy_set_suspend(ci->usb_phy, 0);
usb_phy_set_wakeup(ci->usb_phy, false);
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
index 111b0e0b8698..915f3e91586e 100644
--- a/drivers/usb/chipidea/host.c
+++ b/drivers/usb/chipidea/host.c
@@ -90,6 +90,13 @@ static int ehci_ci_reset(struct usb_hcd *hcd)
ehci->need_io_watchdog = 0;
+ if (ci->platdata->notify_event) {
+ ret = ci->platdata->notify_event(ci,
+ CI_HDRC_CONTROLLER_RESET_EVENT);
+ if (ret)
+ return ret;
+ }
+
ci_platform_configure(ci);
return ret;
@@ -187,6 +194,9 @@ static void host_stop(struct ci_hdrc *ci)
struct usb_hcd *hcd = ci->hcd;
if (hcd) {
+ if (ci->platdata->notify_event)
+ ci->platdata->notify_event(ci,
+ CI_HDRC_CONTROLLER_STOPPED_EVENT);
usb_remove_hcd(hcd);
ci->role = CI_ROLE_END;
synchronize_irq(ci->irq);
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index 03b6743461d1..10236fe71522 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -44,12 +44,15 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
else
val &= ~OTGSC_BSVIS;
- cable->changed = false;
-
- if (cable->state)
+ if (cable->connected)
val |= OTGSC_BSV;
else
val &= ~OTGSC_BSV;
+
+ if (cable->enabled)
+ val |= OTGSC_BSVIE;
+ else
+ val &= ~OTGSC_BSVIE;
}
cable = &ci->platdata->id_extcon;
@@ -59,15 +62,18 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
else
val &= ~OTGSC_IDIS;
- cable->changed = false;
+ if (cable->connected)
+ val &= ~OTGSC_ID; /* host */
+ else
+ val |= OTGSC_ID; /* device */
- if (cable->state)
- val |= OTGSC_ID;
+ if (cable->enabled)
+ val |= OTGSC_IDIE;
else
- val &= ~OTGSC_ID;
+ val &= ~OTGSC_IDIE;
}
- return val;
+ return val & mask;
}
/**
@@ -77,6 +83,36 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
*/
void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
{
+ struct ci_hdrc_cable *cable;
+
+ cable = &ci->platdata->vbus_extcon;
+ if (!IS_ERR(cable->edev)) {
+ if (data & mask & OTGSC_BSVIS)
+ cable->changed = false;
+
+ /* Don't enable vbus interrupt if using external notifier */
+ if (data & mask & OTGSC_BSVIE) {
+ cable->enabled = true;
+ data &= ~OTGSC_BSVIE;
+ } else if (mask & OTGSC_BSVIE) {
+ cable->enabled = false;
+ }
+ }
+
+ cable = &ci->platdata->id_extcon;
+ if (!IS_ERR(cable->edev)) {
+ if (data & mask & OTGSC_IDIS)
+ cable->changed = false;
+
+ /* Don't enable id interrupt if using external notifier */
+ if (data & mask & OTGSC_IDIE) {
+ cable->enabled = true;
+ data &= ~OTGSC_IDIE;
+ } else if (mask & OTGSC_IDIE) {
+ cable->enabled = false;
+ }
+ }
+
hw_write(ci, OP_OTGSC, mask | OTGSC_INT_STATUS_BITS, data);
}
@@ -98,13 +134,37 @@ void ci_handle_vbus_change(struct ci_hdrc *ci)
if (!ci->is_otg)
return;
- if (hw_read_otgsc(ci, OTGSC_BSV))
+ if (hw_read_otgsc(ci, OTGSC_BSV) && !ci->vbus_active)
usb_gadget_vbus_connect(&ci->gadget);
- else
+ else if (!hw_read_otgsc(ci, OTGSC_BSV) && ci->vbus_active)
usb_gadget_vbus_disconnect(&ci->gadget);
}
-#define CI_VBUS_STABLE_TIMEOUT_MS 5000
+/**
+ * When we switch to device mode, the vbus value should be lower
+ * than OTGSC_BSV before connecting to host.
+ *
+ * @ci: the controller
+ *
+ * This function returns an error code if timeout
+ */
+static int hw_wait_vbus_lower_bsv(struct ci_hdrc *ci)
+{
+ unsigned long elapse = jiffies + msecs_to_jiffies(5000);
+ u32 mask = OTGSC_BSV;
+
+ while (hw_read_otgsc(ci, mask)) {
+ if (time_after(jiffies, elapse)) {
+ dev_err(ci->dev, "timeout waiting for %08x in OTGSC\n",
+ mask);
+ return -ETIMEDOUT;
+ }
+ msleep(20);
+ }
+
+ return 0;
+}
+
static void ci_handle_id_switch(struct ci_hdrc *ci)
{
enum ci_role role = ci_otg_role(ci);
@@ -115,12 +175,21 @@ static void ci_handle_id_switch(struct ci_hdrc *ci)
ci_role_stop(ci);
- if (role == CI_ROLE_GADGET)
- /* wait vbus lower than OTGSC_BSV */
- hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0,
- CI_VBUS_STABLE_TIMEOUT_MS);
+ if (role == CI_ROLE_GADGET &&
+ IS_ERR(ci->platdata->vbus_extcon.edev))
+ /*
+ * Wait vbus lower than OTGSC_BSV before connecting
+ * to host. If connecting status is from an external
+ * connector instead of register, we don't need to
+ * care vbus on the board, since it will not affect
+ * external connector status.
+ */
+ hw_wait_vbus_lower_bsv(ci);
ci_role_start(ci, role);
+ /* vbus change may have already occurred */
+ if (role == CI_ROLE_GADGET)
+ ci_handle_vbus_change(ci);
}
}
/**
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
index de8e22ec3902..93e24ce61a3a 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -234,8 +234,8 @@ static void ci_otg_add_timer(struct ci_hdrc *ci, enum otg_fsm_timer t)
ktime_set(timer_sec, timer_nsec));
ci->enabled_otg_timer_bits |= (1 << t);
if ((ci->next_otg_timer == NUM_OTG_FSM_TIMERS) ||
- (ci->hr_timeouts[ci->next_otg_timer].tv64 >
- ci->hr_timeouts[t].tv64)) {
+ (ci->hr_timeouts[ci->next_otg_timer] >
+ ci->hr_timeouts[t])) {
ci->next_otg_timer = t;
hrtimer_start_range_ns(&ci->otg_fsm_hrtimer,
ci->hr_timeouts[t], NSEC_PER_MSEC,
@@ -269,8 +269,8 @@ static void ci_otg_del_timer(struct ci_hdrc *ci, enum otg_fsm_timer t)
for_each_set_bit(cur_timer, &enabled_timer_bits,
NUM_OTG_FSM_TIMERS) {
if ((next_timer == NUM_OTG_FSM_TIMERS) ||
- (ci->hr_timeouts[next_timer].tv64 <
- ci->hr_timeouts[cur_timer].tv64))
+ (ci->hr_timeouts[next_timer] <
+ ci->hr_timeouts[cur_timer]))
next_timer = cur_timer;
}
}
@@ -397,14 +397,14 @@ static enum hrtimer_restart ci_otg_hrtimer_func(struct hrtimer *t)
now = ktime_get();
for_each_set_bit(cur_timer, &enabled_timer_bits, NUM_OTG_FSM_TIMERS) {
- if (now.tv64 >= ci->hr_timeouts[cur_timer].tv64) {
+ if (now >= ci->hr_timeouts[cur_timer]) {
ci->enabled_otg_timer_bits &= ~(1 << cur_timer);
if (otg_timer_handlers[cur_timer])
ret = otg_timer_handlers[cur_timer](ci);
} else {
if ((next_timer == NUM_OTG_FSM_TIMERS) ||
- (ci->hr_timeouts[cur_timer].tv64 <
- ci->hr_timeouts[next_timer].tv64))
+ (ci->hr_timeouts[cur_timer] <
+ ci->hr_timeouts[next_timer]))
next_timer = cur_timer;
}
}
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index c9e80ad48fdc..f88e9157fad0 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -365,7 +365,7 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
if (hwreq->req.length == 0
|| hwreq->req.length % hwep->ep.maxpacket)
mul++;
- node->ptr->token |= mul << __ffs(TD_MULTO);
+ node->ptr->token |= cpu_to_le32(mul << __ffs(TD_MULTO));
}
temp = (u32) (hwreq->req.dma + hwreq->req.actual);
@@ -504,7 +504,7 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
if (hwreq->req.length == 0
|| hwreq->req.length % hwep->ep.maxpacket)
mul++;
- hwep->qh.ptr->cap |= mul << __ffs(QH_MULT);
+ hwep->qh.ptr->cap |= cpu_to_le32(mul << __ffs(QH_MULT));
}
ret = hw_ep_prime(ci, hwep->num, hwep->dir,
@@ -529,7 +529,7 @@ static void free_pending_td(struct ci_hw_ep *hwep)
static int reprime_dtd(struct ci_hdrc *ci, struct ci_hw_ep *hwep,
struct td_node *node)
{
- hwep->qh.ptr->td.next = node->dma;
+ hwep->qh.ptr->td.next = cpu_to_le32(node->dma);
hwep->qh.ptr->td.token &=
cpu_to_le32(~(TD_STATUS_HALTED | TD_STATUS_ACTIVE));
@@ -821,7 +821,7 @@ static int _ep_queue(struct usb_ep *ep, struct usb_request *req,
}
if (usb_endpoint_xfer_isoc(hwep->ep.desc) &&
- hwreq->req.length > (1 + hwep->ep.mult) * hwep->ep.maxpacket) {
+ hwreq->req.length > hwep->ep.mult * hwep->ep.maxpacket) {
dev_err(hwep->ci->dev, "request length too big for isochronous\n");
return -EMSGSIZE;
}
@@ -1253,8 +1253,8 @@ static int ep_enable(struct usb_ep *ep,
hwep->num = usb_endpoint_num(desc);
hwep->type = usb_endpoint_type(desc);
- hwep->ep.maxpacket = usb_endpoint_maxp(desc) & 0x07ff;
- hwep->ep.mult = QH_ISO_MULT(usb_endpoint_maxp(desc));
+ hwep->ep.maxpacket = usb_endpoint_maxp(desc);
+ hwep->ep.mult = usb_endpoint_maxp_mult(desc);
if (hwep->type == USB_ENDPOINT_XFER_CONTROL)
cap |= QH_IOS;
@@ -1725,7 +1725,6 @@ static int ci_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
- unsigned long flags;
int retval = -ENOMEM;
if (driver->disconnect == NULL)
@@ -1752,7 +1751,6 @@ static int ci_udc_start(struct usb_gadget *gadget,
pm_runtime_get_sync(&ci->gadget.dev);
if (ci->vbus_active) {
- spin_lock_irqsave(&ci->lock, flags);
hw_device_reset(ci);
} else {
usb_udc_vbus_handler(&ci->gadget, false);
@@ -1761,7 +1759,6 @@ static int ci_udc_start(struct usb_gadget *gadget,
}
retval = hw_device_state(ci, ci->ep0out->qh.dma);
- spin_unlock_irqrestore(&ci->lock, flags);
if (retval)
pm_runtime_put_sync(&ci->gadget.dev);
@@ -1796,10 +1793,10 @@ static int ci_udc_stop(struct usb_gadget *gadget)
if (ci->vbus_active) {
hw_device_state(ci, 0);
+ spin_unlock_irqrestore(&ci->lock, flags);
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_STOPPED_EVENT);
- spin_unlock_irqrestore(&ci->lock, flags);
_gadget_stop_activity(&ci->gadget);
spin_lock_irqsave(&ci->lock, flags);
pm_runtime_put(&ci->gadget.dev);
diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h
index e66df0020bd4..2ecd1174d66c 100644
--- a/drivers/usb/chipidea/udc.h
+++ b/drivers/usb/chipidea/udc.h
@@ -22,11 +22,11 @@
/* DMA layout of transfer descriptors */
struct ci_hw_td {
/* 0 */
- u32 next;
+ __le32 next;
#define TD_TERMINATE BIT(0)
#define TD_ADDR_MASK (0xFFFFFFEUL << 5)
/* 1 */
- u32 token;
+ __le32 token;
#define TD_STATUS (0x00FFUL << 0)
#define TD_STATUS_TR_ERR BIT(3)
#define TD_STATUS_DT_ERR BIT(5)
@@ -36,7 +36,7 @@ struct ci_hw_td {
#define TD_IOC BIT(15)
#define TD_TOTAL_BYTES (0x7FFFUL << 16)
/* 2 */
- u32 page[5];
+ __le32 page[5];
#define TD_CURR_OFFSET (0x0FFFUL << 0)
#define TD_FRAME_NUM (0x07FFUL << 0)
#define TD_RESERVED_MASK (0x0FFFUL << 0)
@@ -45,18 +45,18 @@ struct ci_hw_td {
/* DMA layout of queue heads */
struct ci_hw_qh {
/* 0 */
- u32 cap;
+ __le32 cap;
#define QH_IOS BIT(15)
#define QH_MAX_PKT (0x07FFUL << 16)
#define QH_ZLT BIT(29)
#define QH_MULT (0x0003UL << 30)
#define QH_ISO_MULT(x) ((x >> 11) & 0x03)
/* 1 */
- u32 curr;
+ __le32 curr;
/* 2 - 8 */
struct ci_hw_td td;
/* 9 */
- u32 RESERVED;
+ __le32 RESERVED;
struct usb_ctrlrequest setup;
} __attribute__ ((packed, aligned(4)));
diff --git a/drivers/usb/chipidea/ulpi.c b/drivers/usb/chipidea/ulpi.c
new file mode 100644
index 000000000000..1219583dc1b2
--- /dev/null
+++ b/drivers/usb/chipidea/ulpi.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2016 Linaro Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/device.h>
+#include <linux/usb/chipidea.h>
+#include <linux/ulpi/interface.h>
+
+#include "ci.h"
+
+#define ULPI_WAKEUP BIT(31)
+#define ULPI_RUN BIT(30)
+#define ULPI_WRITE BIT(29)
+#define ULPI_SYNC_STATE BIT(27)
+#define ULPI_ADDR(n) ((n) << 16)
+#define ULPI_DATA(n) (n)
+
+static int ci_ulpi_wait(struct ci_hdrc *ci, u32 mask)
+{
+ unsigned long usec = 10000;
+
+ while (usec--) {
+ if (!hw_read(ci, OP_ULPI_VIEWPORT, mask))
+ return 0;
+
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int ci_ulpi_read(struct device *dev, u8 addr)
+{
+ struct ci_hdrc *ci = dev_get_drvdata(dev);
+ int ret;
+
+ hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP);
+ ret = ci_ulpi_wait(ci, ULPI_WAKEUP);
+ if (ret)
+ return ret;
+
+ hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_RUN | ULPI_ADDR(addr));
+ ret = ci_ulpi_wait(ci, ULPI_RUN);
+ if (ret)
+ return ret;
+
+ return hw_read(ci, OP_ULPI_VIEWPORT, GENMASK(15, 8)) >> 8;
+}
+
+static int ci_ulpi_write(struct device *dev, u8 addr, u8 val)
+{
+ struct ci_hdrc *ci = dev_get_drvdata(dev);
+ int ret;
+
+ hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP);
+ ret = ci_ulpi_wait(ci, ULPI_WAKEUP);
+ if (ret)
+ return ret;
+
+ hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff,
+ ULPI_RUN | ULPI_WRITE | ULPI_ADDR(addr) | val);
+ return ci_ulpi_wait(ci, ULPI_RUN);
+}
+
+int ci_ulpi_init(struct ci_hdrc *ci)
+{
+ if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI)
+ return 0;
+
+ /*
+ * Set PORTSC correctly so we can read/write ULPI registers for
+ * identification purposes
+ */
+ hw_phymode_configure(ci);
+
+ ci->ulpi_ops.read = ci_ulpi_read;
+ ci->ulpi_ops.write = ci_ulpi_write;
+ ci->ulpi = ulpi_register_interface(ci->dev, &ci->ulpi_ops);
+ if (IS_ERR(ci->ulpi))
+ dev_err(ci->dev, "failed to register ULPI interface");
+
+ return PTR_ERR_OR_ZERO(ci->ulpi);
+}
+
+void ci_ulpi_exit(struct ci_hdrc *ci)
+{
+ if (ci->ulpi) {
+ ulpi_unregister_interface(ci->ulpi);
+ ci->ulpi = NULL;
+ }
+}
+
+int ci_ulpi_resume(struct ci_hdrc *ci)
+{
+ int cnt = 100000;
+
+ while (cnt-- > 0) {
+ if (hw_read(ci, OP_ULPI_VIEWPORT, ULPI_SYNC_STATE))
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index 20d02a5e418d..e77a4ed4f021 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -46,11 +46,23 @@
#define MX53_USB_OTG_PHY_CTRL_0_OFFSET 0x08
#define MX53_USB_OTG_PHY_CTRL_1_OFFSET 0x0c
+#define MX53_USB_CTRL_1_OFFSET 0x10
+#define MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_MASK (0x11 << 2)
+#define MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_ULPI BIT(2)
+#define MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_MASK (0x11 << 6)
+#define MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_ULPI BIT(6)
#define MX53_USB_UH2_CTRL_OFFSET 0x14
#define MX53_USB_UH3_CTRL_OFFSET 0x18
+#define MX53_USB_CLKONOFF_CTRL_OFFSET 0x24
+#define MX53_USB_CLKONOFF_CTRL_H2_INT60CKOFF BIT(21)
+#define MX53_USB_CLKONOFF_CTRL_H3_INT60CKOFF BIT(22)
#define MX53_BM_OVER_CUR_DIS_H1 BIT(5)
#define MX53_BM_OVER_CUR_DIS_OTG BIT(8)
#define MX53_BM_OVER_CUR_DIS_UHx BIT(30)
+#define MX53_USB_CTRL_1_UH2_ULPI_EN BIT(26)
+#define MX53_USB_CTRL_1_UH3_ULPI_EN BIT(27)
+#define MX53_USB_UHx_CTRL_WAKE_UP_EN BIT(7)
+#define MX53_USB_UHx_CTRL_ULPI_INT_EN BIT(8)
#define MX53_USB_PHYCTRL1_PLLDIV_MASK 0x3
#define MX53_USB_PLL_DIV_24_MHZ 0x01
@@ -199,31 +211,77 @@ static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
val |= MX53_USB_PLL_DIV_24_MHZ;
writel(val, usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
- if (data->disable_oc) {
- spin_lock_irqsave(&usbmisc->lock, flags);
- switch (data->index) {
- case 0:
+ spin_lock_irqsave(&usbmisc->lock, flags);
+
+ switch (data->index) {
+ case 0:
+ if (data->disable_oc) {
reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG;
- break;
- case 1:
+ writel(val, reg);
+ }
+ break;
+ case 1:
+ if (data->disable_oc) {
reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
val = readl(reg) | MX53_BM_OVER_CUR_DIS_H1;
- break;
- case 2:
+ writel(val, reg);
+ }
+ break;
+ case 2:
+ if (data->ulpi) {
+ /* set USBH2 into ULPI-mode. */
+ reg = usbmisc->base + MX53_USB_CTRL_1_OFFSET;
+ val = readl(reg) | MX53_USB_CTRL_1_UH2_ULPI_EN;
+ /* select ULPI clock */
+ val &= ~MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_MASK;
+ val |= MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_ULPI;
+ writel(val, reg);
+ /* Set interrupt wake up enable */
+ reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET;
+ val = readl(reg) | MX53_USB_UHx_CTRL_WAKE_UP_EN
+ | MX53_USB_UHx_CTRL_ULPI_INT_EN;
+ writel(val, reg);
+ /* Disable internal 60Mhz clock */
+ reg = usbmisc->base + MX53_USB_CLKONOFF_CTRL_OFFSET;
+ val = readl(reg) | MX53_USB_CLKONOFF_CTRL_H2_INT60CKOFF;
+ writel(val, reg);
+ }
+ if (data->disable_oc) {
reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET;
val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
- break;
- case 3:
+ writel(val, reg);
+ }
+ break;
+ case 3:
+ if (data->ulpi) {
+ /* set USBH3 into ULPI-mode. */
+ reg = usbmisc->base + MX53_USB_CTRL_1_OFFSET;
+ val = readl(reg) | MX53_USB_CTRL_1_UH3_ULPI_EN;
+ /* select ULPI clock */
+ val &= ~MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_MASK;
+ val |= MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_ULPI;
+ writel(val, reg);
+ /* Set interrupt wake up enable */
reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET;
- val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
- break;
+ val = readl(reg) | MX53_USB_UHx_CTRL_WAKE_UP_EN
+ | MX53_USB_UHx_CTRL_ULPI_INT_EN;
+ writel(val, reg);
+ /* Disable internal 60Mhz clock */
+ reg = usbmisc->base + MX53_USB_CLKONOFF_CTRL_OFFSET;
+ val = readl(reg) | MX53_USB_CLKONOFF_CTRL_H3_INT60CKOFF;
+ writel(val, reg);
}
- if (reg && val)
+ if (data->disable_oc) {
+ reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET;
+ val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
writel(val, reg);
- spin_unlock_irqrestore(&usbmisc->lock, flags);
+ }
+ break;
}
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
return 0;
}