summaryrefslogtreecommitdiffstats
path: root/drivers/usb/chipidea
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/chipidea')
-rw-r--r--drivers/usb/chipidea/Makefile1
-rw-r--r--drivers/usb/chipidea/ci.h10
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c45
-rw-r--r--drivers/usb/chipidea/ci_hdrc_msm.c9
-rw-r--r--drivers/usb/chipidea/ci_hdrc_usb2.c115
-rw-r--r--drivers/usb/chipidea/core.c225
-rw-r--r--drivers/usb/chipidea/debug.c2
-rw-r--r--drivers/usb/chipidea/host.c78
-rw-r--r--drivers/usb/chipidea/otg_fsm.c41
-rw-r--r--drivers/usb/chipidea/udc.c22
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c6
11 files changed, 417 insertions, 137 deletions
diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile
index 2f099c7df7b5..1fc86a2ca22d 100644
--- a/drivers/usb/chipidea/Makefile
+++ b/drivers/usb/chipidea/Makefile
@@ -10,6 +10,7 @@ ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
# Glue/Bridge layers go here
+obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_usb2.o
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_msm.o
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_zevio.o
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index ea40626e0246..65913d48f0c8 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -161,7 +161,8 @@ struct hw_bank {
* @test_mode: the selected test mode
* @platdata: platform specific information supplied by parent device
* @vbus_active: is VBUS active
- * @transceiver: pointer to USB PHY, if any
+ * @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
* @debugfs: root dentry for this controller in debugfs
* @id_event: indicates there is an id event, and handled at ci_otg_work
@@ -177,6 +178,7 @@ struct ci_hdrc {
struct ci_role_driver *roles[CI_ROLE_END];
enum ci_role role;
bool is_otg;
+ struct usb_otg otg;
struct otg_fsm fsm;
struct ci_otg_fsm_timer_list *fsm_timer;
struct work_struct work;
@@ -201,7 +203,9 @@ struct ci_hdrc {
struct ci_hdrc_platform_data *platdata;
int vbus_active;
- struct usb_phy *transceiver;
+ struct phy *phy;
+ /* old usb_phy interface */
+ struct usb_phy *usb_phy;
struct usb_hcd *hcd;
struct dentry *debugfs;
bool id_event;
@@ -348,7 +352,7 @@ u32 hw_read_intr_enable(struct ci_hdrc *ci);
u32 hw_read_intr_status(struct ci_hdrc *ci);
-int hw_device_reset(struct ci_hdrc *ci, u32 mode);
+int hw_device_reset(struct ci_hdrc *ci);
int hw_port_test_set(struct ci_hdrc *ci, u8 mode);
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index e913792e4df5..0f05de7c6b6c 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -106,8 +106,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
struct ci_hdrc_platform_data pdata = {
.name = dev_name(&pdev->dev),
.capoffset = DEF_CAPOFFSET,
- .flags = CI_HDRC_REQUIRE_TRANSCEIVER |
- CI_HDRC_DISABLE_STREAMING,
+ .flags = CI_HDRC_DISABLE_STREAMING,
};
int ret;
const struct of_device_id *of_id =
@@ -115,10 +114,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
const struct ci_hdrc_imx_platform_flag *imx_platform_flag = of_id->data;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
- if (!data) {
- dev_err(&pdev->dev, "Failed to allocate ci_hdrc-imx data!\n");
+ if (!data)
return -ENOMEM;
- }
data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
if (IS_ERR(data->usbmisc_data))
@@ -147,7 +144,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
goto err_clk;
}
- pdata.phy = data->phy;
+ pdata.usb_phy = data->phy;
if (imx_platform_flag->flags & CI_HDRC_IMX_IMX28_WRITE_FIX)
pdata.flags |= CI_HDRC_IMX28_WRITE_FIX;
@@ -210,12 +207,48 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int imx_controller_suspend(struct device *dev)
+{
+ struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "at %s\n", __func__);
+
+ clk_disable_unprepare(data->clk);
+
+ return 0;
+}
+
+static int imx_controller_resume(struct device *dev)
+{
+ struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "at %s\n", __func__);
+
+ return clk_prepare_enable(data->clk);
+}
+
+static int ci_hdrc_imx_suspend(struct device *dev)
+{
+ return imx_controller_suspend(dev);
+}
+
+static int ci_hdrc_imx_resume(struct device *dev)
+{
+ return imx_controller_resume(dev);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
+};
static struct platform_driver ci_hdrc_imx_driver = {
.probe = ci_hdrc_imx_probe,
.remove = ci_hdrc_imx_remove,
.driver = {
.name = "imx_usb",
.of_match_table = ci_hdrc_imx_dt_ids,
+ .pm = &ci_hdrc_imx_pm_ops,
},
};
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index 4935ac38fd00..d79ecc08a1be 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -26,15 +26,15 @@ static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
writel(0, USB_AHBBURST);
writel(0, USB_AHBMODE);
- usb_phy_init(ci->transceiver);
+ usb_phy_init(ci->usb_phy);
break;
case CI_HDRC_CONTROLLER_STOPPED_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n");
/*
- * Put the transceiver in non-driving mode. Otherwise host
+ * Put the phy in non-driving mode. Otherwise host
* may not detect soft-disconnection.
*/
- usb_phy_notify_disconnect(ci->transceiver, USB_SPEED_UNKNOWN);
+ usb_phy_notify_disconnect(ci->usb_phy, USB_SPEED_UNKNOWN);
break;
default:
dev_dbg(dev, "unknown ci_hdrc event\n");
@@ -46,7 +46,6 @@ static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = {
.name = "ci_hdrc_msm",
.capoffset = DEF_CAPOFFSET,
.flags = CI_HDRC_REGS_SHARED |
- CI_HDRC_REQUIRE_TRANSCEIVER |
CI_HDRC_DISABLE_STREAMING,
.notify_event = ci_hdrc_msm_notify_event,
@@ -68,7 +67,7 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
if (IS_ERR(phy))
return PTR_ERR(phy);
- ci_hdrc_msm_platdata.phy = phy;
+ ci_hdrc_msm_platdata.usb_phy = phy;
plat_ci = ci_hdrc_add_device(&pdev->dev,
pdev->resource, pdev->num_resources,
diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c
new file mode 100644
index 000000000000..45844c951788
--- /dev/null
+++ b/drivers/usb/chipidea/ci_hdrc_usb2.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2014 Marvell Technology Group Ltd.
+ *
+ * Antoine Tenart <antoine.tenart@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/usb/chipidea.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/ulpi.h>
+
+#include "ci.h"
+
+struct ci_hdrc_usb2_priv {
+ struct platform_device *ci_pdev;
+ struct clk *clk;
+};
+
+static struct ci_hdrc_platform_data ci_default_pdata = {
+ .capoffset = DEF_CAPOFFSET,
+ .flags = CI_HDRC_DISABLE_STREAMING,
+};
+
+static int ci_hdrc_usb2_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ci_hdrc_usb2_priv *priv;
+ struct ci_hdrc_platform_data *ci_pdata = dev_get_platdata(dev);
+ int ret;
+
+ if (!ci_pdata)
+ ci_pdata = &ci_default_pdata;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (!IS_ERR(priv->clk)) {
+ ret = clk_prepare_enable(priv->clk);
+ if (ret) {
+ dev_err(dev, "failed to enable the clock: %d\n", ret);
+ return ret;
+ }
+ }
+
+ 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,
+ pdev->num_resources, ci_pdata);
+ if (IS_ERR(priv->ci_pdev)) {
+ ret = PTR_ERR(priv->ci_pdev);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev,
+ "failed to register ci_hdrc platform device: %d\n",
+ ret);
+ goto clk_err;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ pm_runtime_no_callbacks(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+
+clk_err:
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+ return ret;
+}
+
+static int ci_hdrc_usb2_remove(struct platform_device *pdev)
+{
+ struct ci_hdrc_usb2_priv *priv = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+ ci_hdrc_remove_device(priv->ci_pdev);
+ clk_disable_unprepare(priv->clk);
+
+ return 0;
+}
+
+static const struct of_device_id ci_hdrc_usb2_of_match[] = {
+ { .compatible = "chipidea,usb2" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ci_hdrc_usb2_of_match);
+
+static struct platform_driver ci_hdrc_usb2_driver = {
+ .probe = ci_hdrc_usb2_probe,
+ .remove = ci_hdrc_usb2_remove,
+ .driver = {
+ .name = "chipidea-usb2",
+ .of_match_table = of_match_ptr(ci_hdrc_usb2_of_match),
+ },
+};
+module_platform_driver(ci_hdrc_usb2_driver);
+
+MODULE_DESCRIPTION("ChipIdea HDRC USB2 binding for ci13xxx");
+MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 947ed6e78be2..5b9825a4538a 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -47,6 +47,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
+#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/idr.h>
@@ -189,24 +190,29 @@ u8 hw_port_test_get(struct ci_hdrc *ci)
return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC);
}
+static void hw_wait_phy_stable(void)
+{
+ /*
+ * The phy needs some delay to output the stable status from low
+ * power mode. And for OTGSC, the status inputs are debounced
+ * using a 1 ms time constant, so, delay 2ms for controller to get
+ * the stable status, like vbus and id when the phy leaves low power.
+ */
+ usleep_range(2000, 2500);
+}
+
/* The PHY enters/leaves low power mode */
static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
{
enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC;
bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm)));
- if (enable && !lpm) {
+ if (enable && !lpm)
hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
PORTSC_PHCD(ci->hw_bank.lpm));
- } else if (!enable && lpm) {
+ else if (!enable && lpm)
hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
0);
- /*
- * the PHY needs some time (less
- * than 1ms) to leave low power mode.
- */
- usleep_range(1000, 1100);
- }
}
static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
@@ -299,6 +305,49 @@ static void hw_phymode_configure(struct ci_hdrc *ci)
}
/**
+ * _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy
+ * interfaces
+ * @ci: the controller
+ *
+ * This function returns an error code if the phy failed to init
+ */
+static int _ci_usb_phy_init(struct ci_hdrc *ci)
+{
+ int ret;
+
+ if (ci->phy) {
+ ret = phy_init(ci->phy);
+ if (ret)
+ return ret;
+
+ ret = phy_power_on(ci->phy);
+ if (ret) {
+ phy_exit(ci->phy);
+ return ret;
+ }
+ } else {
+ ret = usb_phy_init(ci->usb_phy);
+ }
+
+ return ret;
+}
+
+/**
+ * _ci_usb_phy_exit: deinitialize phy taking in account both phy and usb_phy
+ * interfaces
+ * @ci: the controller
+ */
+static void ci_usb_phy_exit(struct ci_hdrc *ci)
+{
+ if (ci->phy) {
+ phy_power_off(ci->phy);
+ phy_exit(ci->phy);
+ } else {
+ usb_phy_shutdown(ci->usb_phy);
+ }
+}
+
+/**
* ci_usb_phy_init: initialize phy according to different phy type
* @ci: the controller
*
@@ -312,40 +361,68 @@ static int ci_usb_phy_init(struct ci_hdrc *ci)
case USBPHY_INTERFACE_MODE_UTMI:
case USBPHY_INTERFACE_MODE_UTMIW:
case USBPHY_INTERFACE_MODE_HSIC:
- ret = usb_phy_init(ci->transceiver);
- if (ret)
+ ret = _ci_usb_phy_init(ci);
+ if (!ret)
+ hw_wait_phy_stable();
+ else
return ret;
hw_phymode_configure(ci);
break;
case USBPHY_INTERFACE_MODE_ULPI:
case USBPHY_INTERFACE_MODE_SERIAL:
hw_phymode_configure(ci);
- ret = usb_phy_init(ci->transceiver);
+ ret = _ci_usb_phy_init(ci);
if (ret)
return ret;
break;
default:
- ret = usb_phy_init(ci->transceiver);
+ ret = _ci_usb_phy_init(ci);
+ if (!ret)
+ hw_wait_phy_stable();
}
return ret;
}
/**
- * hw_device_reset: resets chip (execute without interruption)
+ * hw_controller_reset: do controller reset
* @ci: the controller
*
* This function returns an error code
*/
-int hw_device_reset(struct ci_hdrc *ci, u32 mode)
+static int hw_controller_reset(struct ci_hdrc *ci)
{
+ int count = 0;
+
+ hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST);
+ while (hw_read(ci, OP_USBCMD, USBCMD_RST)) {
+ udelay(10);
+ if (count++ > 1000)
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+/**
+ * hw_device_reset: resets chip (execute without interruption)
+ * @ci: the controller
+ *
+ * This function returns an error code
+ */
+int hw_device_reset(struct ci_hdrc *ci)
+{
+ int ret;
+
/* should flush & stop before reset */
hw_write(ci, OP_ENDPTFLUSH, ~0, ~0);
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
- hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST);
- while (hw_read(ci, OP_USBCMD, USBCMD_RST))
- udelay(10); /* not RTOS friendly */
+ ret = hw_controller_reset(ci);
+ if (ret) {
+ dev_err(ci->dev, "error resetting controller, ret=%d\n", ret);
+ return ret;
+ }
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
@@ -363,12 +440,12 @@ int hw_device_reset(struct ci_hdrc *ci, u32 mode)
/* USBMODE should be configured step by step */
hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
- hw_write(ci, OP_USBMODE, USBMODE_CM, mode);
+ hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_DC);
/* HW >= 2.3 */
hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM);
- if (hw_read(ci, OP_USBMODE, USBMODE_CM) != mode) {
- pr_err("cannot enter in %s mode", ci_role(ci)->name);
+ if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) {
+ pr_err("cannot enter in %s device mode", ci_role(ci)->name);
pr_err("lpm = %i", ci->hw_bank.lpm);
return -ENODEV;
}
@@ -472,7 +549,7 @@ static int ci_get_platdata(struct device *dev,
if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) {
return -EPROBE_DEFER;
} else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) {
- /* no vbus regualator is needed */
+ /* no vbus regulator is needed */
platdata->reg_vbus = NULL;
} else if (IS_ERR(platdata->reg_vbus)) {
dev_err(dev, "Getting regulator error: %ld\n",
@@ -589,11 +666,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return PTR_ERR(base);
ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL);
- if (!ci) {
- dev_err(dev, "can't allocate device\n");
+ if (!ci)
return -ENOMEM;
- }
+ platform_set_drvdata(pdev, ci);
ci->dev = dev;
ci->platdata = dev_get_platdata(dev);
ci->imx28_write_fix = !!(ci->platdata->flags &
@@ -605,36 +681,32 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return -ENODEV;
}
- if (ci->platdata->phy)
- ci->transceiver = ci->platdata->phy;
- else
- ci->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
-
- if (IS_ERR(ci->transceiver)) {
- ret = PTR_ERR(ci->transceiver);
- /*
- * if -ENXIO is returned, it means PHY layer wasn't
- * enabled, so it makes no sense to return -EPROBE_DEFER
- * in that case, since no PHY driver will ever probe.
- */
- if (ret == -ENXIO)
- return ret;
+ if (ci->platdata->phy) {
+ ci->phy = ci->platdata->phy;
+ } else if (ci->platdata->usb_phy) {
+ ci->usb_phy = ci->platdata->usb_phy;
+ } else {
+ ci->phy = devm_phy_get(dev->parent, "usb-phy");
+ ci->usb_phy = devm_usb_get_phy(dev->parent, USB_PHY_TYPE_USB2);
+
+ /* 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;
+
+ if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy))
+ return -EPROBE_DEFER;
- dev_err(dev, "no usb2 phy configured\n");
- return -EPROBE_DEFER;
+ if (IS_ERR(ci->phy))
+ ci->phy = NULL;
+ else if (IS_ERR(ci->usb_phy))
+ ci->usb_phy = NULL;
}
ret = ci_usb_phy_init(ci);
if (ret) {
dev_err(dev, "unable to init phy: %d\n", ret);
return ret;
- } else {
- /*
- * The delay to sync PHY's status, the maximum delay is
- * 2ms since the otgsc uses 1ms timer to debounce the
- * PHY's input
- */
- usleep_range(2000, 2500);
}
ci->hw_bank.phys = res->start;
@@ -711,9 +783,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
}
}
- platform_set_drvdata(pdev, ci);
- ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->platdata->name,
- ci);
+ ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED,
+ ci->platdata->name, ci);
if (ret)
goto stop;
@@ -724,11 +795,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
if (!ret)
return 0;
- free_irq(ci->irq, ci);
stop:
ci_role_destroy(ci);
deinit_phy:
- usb_phy_shutdown(ci->transceiver);
+ ci_usb_phy_exit(ci);
return ret;
}
@@ -738,19 +808,66 @@ static int ci_hdrc_remove(struct platform_device *pdev)
struct ci_hdrc *ci = platform_get_drvdata(pdev);
dbg_remove_files(ci);
- free_irq(ci->irq, ci);
ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true);
- usb_phy_shutdown(ci->transceiver);
+ ci_usb_phy_exit(ci);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void ci_controller_suspend(struct ci_hdrc *ci)
+{
+ ci_hdrc_enter_lpm(ci, true);
+
+ if (ci->usb_phy)
+ usb_phy_set_suspend(ci->usb_phy, 1);
+}
+
+static int ci_controller_resume(struct device *dev)
+{
+ struct ci_hdrc *ci = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "at %s\n", __func__);
+
+ ci_hdrc_enter_lpm(ci, false);
+
+ if (ci->usb_phy) {
+ usb_phy_set_suspend(ci->usb_phy, 0);
+ usb_phy_set_wakeup(ci->usb_phy, false);
+ hw_wait_phy_stable();
+ }
return 0;
}
+static int ci_suspend(struct device *dev)
+{
+ struct ci_hdrc *ci = dev_get_drvdata(dev);
+
+ if (ci->wq)
+ flush_workqueue(ci->wq);
+
+ ci_controller_suspend(ci);
+
+ return 0;
+}
+
+static int ci_resume(struct device *dev)
+{
+ return ci_controller_resume(dev);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops ci_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume)
+};
static struct platform_driver ci_hdrc_driver = {
.probe = ci_hdrc_probe,
.remove = ci_hdrc_remove,
.driver = {
.name = "ci_hdrc",
+ .pm = &ci_pm_ops,
},
};
diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c
index 795d6538d630..268e4236e84c 100644
--- a/drivers/usb/chipidea/debug.c
+++ b/drivers/usb/chipidea/debug.c
@@ -220,7 +220,7 @@ static int ci_otg_show(struct seq_file *s, void *unused)
/* ------ State ----- */
seq_printf(s, "OTG state: %s\n\n",
- usb_otg_state_string(ci->transceiver->state));
+ usb_otg_state_string(ci->otg.state));
/* ------ State Machine Variables ----- */
seq_printf(s, "a_bus_drop: %d\n", fsm->a_bus_drop);
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
index ebde7b6ce687..c1694cff1eaf 100644
--- a/drivers/usb/chipidea/host.c
+++ b/drivers/usb/chipidea/host.c
@@ -34,6 +34,44 @@
static struct hc_driver __read_mostly ci_ehci_hc_driver;
+struct ehci_ci_priv {
+ struct regulator *reg_vbus;
+};
+
+static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv;
+ struct device *dev = hcd->self.controller;
+ struct ci_hdrc *ci = dev_get_drvdata(dev);
+ int ret = 0;
+ int port = HCS_N_PORTS(ehci->hcs_params);
+
+ if (priv->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
+ if (port > 1) {
+ dev_warn(dev,
+ "Not support multi-port regulator control\n");
+ return 0;
+ }
+ if (enable)
+ ret = regulator_enable(priv->reg_vbus);
+ else
+ ret = regulator_disable(priv->reg_vbus);
+ if (ret) {
+ dev_err(dev,
+ "Failed to %s vbus regulator, ret=%d\n",
+ enable ? "enable" : "disable", ret);
+ return ret;
+ }
+ }
+ return 0;
+};
+
+static const struct ehci_driver_overrides ehci_ci_overrides = {
+ .extra_priv_size = sizeof(struct ehci_ci_priv),
+ .port_power = ehci_ci_portpower,
+};
+
static irqreturn_t host_irq(struct ci_hdrc *ci)
{
return usb_hcd_irq(ci->irq, ci->hcd);
@@ -43,6 +81,7 @@ static int host_start(struct ci_hdrc *ci)
{
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
+ struct ehci_ci_priv *priv;
int ret;
if (usb_disabled())
@@ -52,15 +91,17 @@ static int host_start(struct ci_hdrc *ci)
if (!hcd)
return -ENOMEM;
- dev_set_drvdata(ci->dev, ci);
hcd->rsrc_start = ci->hw_bank.phys;
hcd->rsrc_len = ci->hw_bank.size;
hcd->regs = ci->hw_bank.abs;
hcd->has_tt = 1;
hcd->power_budget = ci->platdata->power_budget;
- hcd->usb_phy = ci->transceiver;
hcd->tpl_support = ci->platdata->tpl_support;
+ if (ci->phy)
+ hcd->phy = ci->phy;
+ else
+ hcd->usb_phy = ci->usb_phy;
ehci = hcd_to_ehci(hcd);
ehci->caps = ci->hw_bank.cap;
@@ -68,28 +109,21 @@ static int host_start(struct ci_hdrc *ci)
ehci->has_tdi_phy_lpm = ci->hw_bank.lpm;
ehci->imx28_write_fix = ci->imx28_write_fix;
- /*
- * vbus is always on if host is not in OTG FSM mode,
- * otherwise should be controlled by OTG FSM
- */
- if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
- ret = regulator_enable(ci->platdata->reg_vbus);
- if (ret) {
- dev_err(ci->dev,
- "Failed to enable vbus regulator, ret=%d\n",
- ret);
- goto put_hcd;
- }
- }
+ priv = (struct ehci_ci_priv *)ehci->priv;
+ priv->reg_vbus = NULL;
+
+ if (ci->platdata->reg_vbus)
+ priv->reg_vbus = ci->platdata->reg_vbus;
ret = usb_add_hcd(hcd, 0, 0);
if (ret) {
- goto disable_reg;
+ goto put_hcd;
} else {
- struct usb_otg *otg = ci->transceiver->otg;
+ struct usb_otg *otg = &ci->otg;
ci->hcd = hcd;
- if (otg) {
+
+ if (ci_otg_is_fsm_mode(ci)) {
otg->host = &hcd->self;
hcd->self.otg_port = 1;
}
@@ -100,10 +134,6 @@ static int host_start(struct ci_hdrc *ci)
return ret;
-disable_reg:
- if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci))
- regulator_disable(ci->platdata->reg_vbus);
-
put_hcd:
usb_put_hcd(hcd);
@@ -117,8 +147,6 @@ static void host_stop(struct ci_hdrc *ci)
if (hcd) {
usb_remove_hcd(hcd);
usb_put_hcd(hcd);
- if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci))
- regulator_disable(ci->platdata->reg_vbus);
}
}
@@ -146,7 +174,7 @@ int ci_hdrc_host_init(struct ci_hdrc *ci)
rdrv->name = "host";
ci->roles[CI_ROLE_HOST] = rdrv;
- ehci_init_driver(&ci_ehci_hc_driver, NULL);
+ ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides);
return 0;
}
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
index caaabc58021e..562e581f6765 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -303,7 +303,7 @@ static void a_wait_vfall_tmout_func(void *ptr, unsigned long indicator)
set_tmout(ci, indicator);
/* Disable port power */
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, 0);
- /* Clear exsiting DP irq */
+ /* Clear existing DP irq */
hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
/* Enable data pulse irq */
hw_write_otgsc(ci, OTGSC_DPIE, OTGSC_DPIE);
@@ -328,7 +328,7 @@ static void b_ssend_srp_tmout_func(void *ptr, unsigned long indicator)
set_tmout(ci, indicator);
/* only vbus fall below B_sess_vld in b_idle state */
- if (ci->transceiver->state == OTG_STATE_B_IDLE)
+ if (ci->fsm.otg->state == OTG_STATE_B_IDLE)
ci_otg_queue_work(ci);
}
@@ -543,7 +543,7 @@ static int ci_otg_start_host(struct otg_fsm *fsm, int on)
ci_role_start(ci, CI_ROLE_HOST);
} else {
ci_role_stop(ci);
- hw_device_reset(ci, USBMODE_CM_DC);
+ hw_device_reset(ci);
ci_role_start(ci, CI_ROLE_GADGET);
}
mutex_lock(&fsm->lock);
@@ -582,11 +582,11 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
* when there is no gadget class driver
*/
if (ci->fsm.id && !(ci->driver) &&
- ci->transceiver->state < OTG_STATE_A_IDLE)
+ ci->fsm.otg->state < OTG_STATE_A_IDLE)
return 0;
if (otg_statemachine(&ci->fsm)) {
- if (ci->transceiver->state == OTG_STATE_A_IDLE) {
+ if (ci->fsm.otg->state == OTG_STATE_A_IDLE) {
/*
* Further state change for cases:
* a_idle to b_idle; or
@@ -600,7 +600,7 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
ci_otg_queue_work(ci);
if (ci->id_event)
ci->id_event = false;
- } else if (ci->transceiver->state == OTG_STATE_B_IDLE) {
+ } else if (ci->fsm.otg->state == OTG_STATE_B_IDLE) {
if (ci->fsm.b_sess_vld) {
ci->fsm.power_up = 0;
/*
@@ -627,7 +627,7 @@ static void ci_otg_fsm_event(struct ci_hdrc *ci)
otg_bsess_vld = hw_read_otgsc(ci, OTGSC_BSV);
port_conn = hw_read(ci, OP_PORTSC, PORTSC_CCS);
- switch (ci->transceiver->state) {
+ switch (ci->fsm.otg->state) {
case OTG_STATE_A_WAIT_BCON:
if (port_conn) {
fsm->b_conn = 1;
@@ -663,7 +663,7 @@ static void ci_otg_fsm_event(struct ci_hdrc *ci)
fsm->b_bus_suspend = 1;
/*
* Init a timer to know how long this suspend
- * will contine, if time out, indicates B no longer
+ * will continue, if time out, indicates B no longer
* wants to be host role
*/
ci_otg_add_timer(ci, A_BIDL_ADIS);
@@ -778,34 +778,25 @@ void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci)
int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
{
int retval = 0;
- struct usb_otg *otg;
- otg = devm_kzalloc(ci->dev,
- sizeof(struct usb_otg), GFP_KERNEL);
- if (!otg) {
- dev_err(ci->dev,
- "Failed to allocate usb_otg structure for ci hdrc otg!\n");
- return -ENOMEM;
- }
+ if (ci->phy)
+ ci->otg.phy = ci->phy;
+ else
+ ci->otg.usb_phy = ci->usb_phy;
- otg->phy = ci->transceiver;
- otg->gadget = &ci->gadget;
- ci->fsm.otg = otg;
- ci->transceiver->otg = ci->fsm.otg;
+ ci->otg.gadget = &ci->gadget;
+ ci->fsm.otg = &ci->otg;
ci->fsm.power_up = 1;
ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0;
- ci->transceiver->state = OTG_STATE_UNDEFINED;
+ ci->fsm.otg->state = OTG_STATE_UNDEFINED;
ci->fsm.ops = &ci_otg_ops;
mutex_init(&ci->fsm.lock);
ci->fsm_timer = devm_kzalloc(ci->dev,
sizeof(struct ci_otg_fsm_timer_list), GFP_KERNEL);
- if (!ci->fsm_timer) {
- dev_err(ci->dev,
- "Failed to allocate timer structure for ci hdrc otg!\n");
+ if (!ci->fsm_timer)
return -ENOMEM;
- }
INIT_LIST_HEAD(&ci->fsm_timer->active_timers);
retval = ci_otg_init_timers(ci);
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 0444d3f8971a..4fe18ce3bd5a 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -692,10 +692,8 @@ __acquires(ci->lock)
int retval;
spin_unlock(&ci->lock);
- if (ci->gadget.speed != USB_SPEED_UNKNOWN) {
- if (ci->driver)
- ci->driver->disconnect(&ci->gadget);
- }
+ if (ci->gadget.speed != USB_SPEED_UNKNOWN)
+ usb_gadget_udc_reset(&ci->gadget, ci->driver);
retval = _gadget_stop_activity(&ci->gadget);
if (retval)
@@ -709,8 +707,6 @@ __acquires(ci->lock)
if (ci->status == NULL)
retval = -ENOMEM;
- usb_gadget_set_state(&ci->gadget, USB_STATE_DEFAULT);
-
done:
spin_lock(&ci->lock);
@@ -1475,7 +1471,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
if (gadget_ready) {
if (is_active) {
pm_runtime_get_sync(&_gadget->dev);
- hw_device_reset(ci, USBMODE_CM_DC);
+ hw_device_reset(ci);
hw_device_state(ci, ci->ep0out->qh.dma);
usb_gadget_set_state(_gadget, USB_STATE_POWERED);
} else {
@@ -1519,8 +1515,8 @@ static int ci_udc_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
{
struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
- if (ci->transceiver)
- return usb_phy_set_power(ci->transceiver, ma);
+ if (ci->usb_phy)
+ return usb_phy_set_power(ci->usb_phy, ma);
return -ENOTSUPP;
}
@@ -1544,8 +1540,7 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)
static int ci_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver);
-static int ci_udc_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver);
+static int ci_udc_stop(struct usb_gadget *gadget);
/**
* Device operations part of the API to the USB controller hardware,
* which don't involve endpoints (or i/o)
@@ -1665,7 +1660,7 @@ 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, USBMODE_CM_DC);
+ hw_device_reset(ci);
} else {
pm_runtime_put_sync(&ci->gadget.dev);
return retval;
@@ -1682,8 +1677,7 @@ static int ci_udc_start(struct usb_gadget *gadget,
/**
* ci_udc_stop: unregister a gadget driver
*/
-static int ci_udc_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static int ci_udc_stop(struct usb_gadget *gadget)
{
struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
unsigned long flags;
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index 7776ec676014..c3c6225b8acf 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -117,10 +117,9 @@ static int usbmisc_imx25_post(struct imx_usbmisc_data *data)
if (data->index > 2)
return -EINVAL;
- reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET;
-
if (data->evdo) {
spin_lock_irqsave(&usbmisc->lock, flags);
+ reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET;
val = readl(reg);
writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg);
spin_unlock_irqrestore(&usbmisc->lock, flags);
@@ -172,8 +171,7 @@ static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
return -EINVAL;
/* Select a 24 MHz reference clock for the PHY */
- reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET;
- val = readl(reg);
+ val = readl(usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
val &= ~MX53_USB_PHYCTRL1_PLLDIV_MASK;
val |= MX53_USB_PLL_DIV_24_MHZ;
writel(val, usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
OpenPOWER on IntegriCloud