diff options
Diffstat (limited to 'drivers/usb/chipidea')
-rw-r--r-- | drivers/usb/chipidea/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci.h | 10 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_imx.c | 45 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_msm.c | 9 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_usb2.c | 115 | ||||
-rw-r--r-- | drivers/usb/chipidea/core.c | 225 | ||||
-rw-r--r-- | drivers/usb/chipidea/debug.c | 2 | ||||
-rw-r--r-- | drivers/usb/chipidea/host.c | 78 | ||||
-rw-r--r-- | drivers/usb/chipidea/otg_fsm.c | 41 | ||||
-rw-r--r-- | drivers/usb/chipidea/udc.c | 22 | ||||
-rw-r--r-- | drivers/usb/chipidea/usbmisc_imx.c | 6 |
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); |