diff options
-rw-r--r-- | Documentation/devicetree/bindings/usb/usbmisc-imx.txt | 1 | ||||
-rw-r--r-- | Documentation/usb/chipidea.txt | 21 | ||||
-rw-r--r-- | drivers/usb/chipidea/bits.h | 11 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci.h | 59 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_imx.c | 153 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_imx.h | 1 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_pci.c | 31 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_zevio.c | 2 | ||||
-rw-r--r-- | drivers/usb/chipidea/core.c | 185 | ||||
-rw-r--r-- | drivers/usb/chipidea/host.c | 74 | ||||
-rw-r--r-- | drivers/usb/chipidea/otg.c | 2 | ||||
-rw-r--r-- | drivers/usb/chipidea/otg_fsm.c | 22 | ||||
-rw-r--r-- | drivers/usb/chipidea/udc.c | 29 | ||||
-rw-r--r-- | drivers/usb/chipidea/usbmisc_imx.c | 130 | ||||
-rw-r--r-- | include/linux/usb/chipidea.h | 2 |
15 files changed, 641 insertions, 82 deletions
diff --git a/Documentation/devicetree/bindings/usb/usbmisc-imx.txt b/Documentation/devicetree/bindings/usb/usbmisc-imx.txt index c101a4b17131..3539d4e7d23e 100644 --- a/Documentation/devicetree/bindings/usb/usbmisc-imx.txt +++ b/Documentation/devicetree/bindings/usb/usbmisc-imx.txt @@ -5,6 +5,7 @@ Required properties: - compatible: Should be one of below: "fsl,imx6q-usbmisc" for imx6q "fsl,vf610-usbmisc" for Vybrid vf610 + "fsl,imx6sx-usbmisc" for imx6sx - reg: Should contain registers location and length Examples: diff --git a/Documentation/usb/chipidea.txt b/Documentation/usb/chipidea.txt index 995c8bca40e2..3f848c1f2940 100644 --- a/Documentation/usb/chipidea.txt +++ b/Documentation/usb/chipidea.txt @@ -69,3 +69,24 @@ cat /sys/kernel/debug/ci_hdrc.0/registers ---------------------- "On-The-Go and Embedded Host Supplement to the USB Revision 2.0 Specification July 27, 2012 Revision 2.0 version 1.1a" + +2. How to enable USB as system wakeup source +----------------------------------- +Below is the example for how to enable USB as system wakeup source +at imx6 platform. + +2.1 Enable core's wakeup +echo enabled > /sys/bus/platform/devices/ci_hdrc.0/power/wakeup +2.2 Enable glue layer's wakeup +echo enabled > /sys/bus/platform/devices/2184000.usb/power/wakeup +2.3 Enable PHY's wakeup (optional) +echo enabled > /sys/bus/platform/devices/20c9000.usbphy/power/wakeup +2.4 Enable roothub's wakeup +echo enabled > /sys/bus/usb/devices/usb1/power/wakeup +2.5 Enable related device's wakeup +echo enabled > /sys/bus/usb/devices/1-1/power/wakeup + +If the system has only one usb port, and you want usb wakeup at this port, you +can use below script to enable usb wakeup. +for i in $(find /sys -name wakeup | grep usb);do echo enabled > $i;done; + diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h index ca57e3dcd3d5..3cb9bda51ddf 100644 --- a/drivers/usb/chipidea/bits.h +++ b/drivers/usb/chipidea/bits.h @@ -15,6 +15,16 @@ #include <linux/usb/ehci_def.h> +/* + * ID + * For 1.x revision, bit24 - bit31 are reserved + * For 2.x revision, bit25 - bit28 are 0x2 + */ +#define TAG (0x1F << 16) +#define REVISION (0xF << 21) +#define VERSION (0xF << 25) +#define CIVERSION (0x7 << 29) + /* HCCPARAMS */ #define HCCPARAMS_LEN BIT(17) @@ -53,6 +63,7 @@ #define PORTSC_HSP BIT(9) #define PORTSC_PP BIT(12) #define PORTSC_PTC (0x0FUL << 16) +#define PORTSC_WKCN BIT(20) #define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23)) /* PTS and PTW for non lpm version only */ #define PORTSC_PFSC BIT(24) diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 65913d48f0c8..c09381d4ca77 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -29,6 +29,15 @@ /****************************************************************************** * REGISTERS *****************************************************************************/ +/* Identification Registers */ +#define ID_ID 0x0 +#define ID_HWGENERAL 0x4 +#define ID_HWHOST 0x8 +#define ID_HWDEVICE 0xc +#define ID_HWTXBUF 0x10 +#define ID_HWRXBUF 0x14 +#define ID_SBUSCFG 0x90 + /* register indices */ enum ci_hw_regs { CAP_CAPLENGTH, @@ -97,6 +106,18 @@ enum ci_role { CI_ROLE_END, }; +enum ci_revision { + CI_REVISION_1X = 10, /* Revision 1.x */ + CI_REVISION_20 = 20, /* Revision 2.0 */ + CI_REVISION_21, /* Revision 2.1 */ + CI_REVISION_22, /* Revision 2.2 */ + CI_REVISION_23, /* Revision 2.3 */ + CI_REVISION_24, /* Revision 2.4 */ + CI_REVISION_25, /* Revision 2.5 */ + CI_REVISION_25_PLUS, /* Revision above than 2.5 */ + CI_REVISION_UNKNOWN = 99, /* Unknown Revision */ +}; + /** * struct ci_role_driver - host/gadget role driver * @start: start this role @@ -169,6 +190,10 @@ struct hw_bank { * @b_sess_valid_event: indicates there is a vbus event, and handled * at ci_otg_work * @imx28_write_fix: Freescale imx28 needs swp instruction for writing + * @supports_runtime_pm: if runtime pm is supported + * @in_lpm: if the core in low power mode + * @wakeup_int: if wakeup interrupt occur + * @rev: The revision number for controller */ struct ci_hdrc { struct device *dev; @@ -211,6 +236,10 @@ struct ci_hdrc { bool id_event; bool b_sess_valid_event; bool imx28_write_fix; + bool supports_runtime_pm; + bool in_lpm; + bool wakeup_int; + enum ci_revision rev; }; static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci) @@ -248,6 +277,36 @@ static inline void ci_role_stop(struct ci_hdrc *ci) } /** + * hw_read_id_reg: reads from a identification register + * @ci: the controller + * @offset: offset from the beginning of identification registers region + * @mask: bitfield mask + * + * This function returns register contents + */ +static inline u32 hw_read_id_reg(struct ci_hdrc *ci, u32 offset, u32 mask) +{ + return ioread32(ci->hw_bank.abs + offset) & mask; +} + +/** + * hw_write_id_reg: writes to a identification register + * @ci: the controller + * @offset: offset from the beginning of identification registers region + * @mask: bitfield mask + * @data: new value + */ +static inline void hw_write_id_reg(struct ci_hdrc *ci, u32 offset, + u32 mask, u32 data) +{ + if (~mask) + data = (ioread32(ci->hw_bank.abs + offset) & ~mask) + | (data & mask); + + iowrite32(data, ci->hw_bank.abs + offset); +} + +/** * hw_read: reads from a hw register * @ci: the controller * @reg: register index diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 0f05de7c6b6c..389f0e034259 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -23,22 +23,40 @@ #include "ci.h" #include "ci_hdrc_imx.h" -#define CI_HDRC_IMX_IMX28_WRITE_FIX BIT(0) - struct ci_hdrc_imx_platform_flag { unsigned int flags; + bool runtime_pm; }; static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { }; static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { - .flags = CI_HDRC_IMX_IMX28_WRITE_FIX, + .flags = CI_HDRC_IMX28_WRITE_FIX | + CI_HDRC_TURN_VBUS_EARLY_ON, +}; + +static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = { + .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | + CI_HDRC_TURN_VBUS_EARLY_ON, +}; + +static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = { + .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | + CI_HDRC_TURN_VBUS_EARLY_ON, +}; + +static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = { + .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | + CI_HDRC_TURN_VBUS_EARLY_ON, }; static const struct of_device_id ci_hdrc_imx_dt_ids[] = { { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, + { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, + { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data}, + { .compatible = "fsl,imx6sx-usb", .data = &imx6sl_usb_data}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); @@ -48,6 +66,8 @@ struct ci_hdrc_imx_data { struct platform_device *ci_pdev; struct clk *clk; struct imx_usbmisc_data *usbmisc_data; + bool supports_runtime_pm; + bool in_lpm; }; /* Common functions shared by usbmisc drivers */ @@ -145,21 +165,18 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) } pdata.usb_phy = data->phy; - - if (imx_platform_flag->flags & CI_HDRC_IMX_IMX28_WRITE_FIX) - pdata.flags |= CI_HDRC_IMX28_WRITE_FIX; + pdata.flags |= imx_platform_flag->flags; + if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM) + data->supports_runtime_pm = true; ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (ret) goto err_clk; - if (data->usbmisc_data) { - ret = imx_usbmisc_init(data->usbmisc_data); - if (ret) { - dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", - ret); - goto err_clk; - } + ret = imx_usbmisc_init(data->usbmisc_data); + if (ret) { + dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret); + goto err_clk; } data->ci_pdev = ci_hdrc_add_device(&pdev->dev, @@ -173,19 +190,20 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) goto err_clk; } - if (data->usbmisc_data) { - ret = imx_usbmisc_init_post(data->usbmisc_data); - if (ret) { - dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", - ret); - goto disable_device; - } + ret = imx_usbmisc_init_post(data->usbmisc_data); + if (ret) { + dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret); + goto disable_device; } platform_set_drvdata(pdev, data); - pm_runtime_no_callbacks(&pdev->dev); - pm_runtime_enable(&pdev->dev); + if (data->supports_runtime_pm) { + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + } + + device_set_wakeup_capable(&pdev->dev, true); return 0; @@ -200,14 +218,18 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) { struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); - pm_runtime_disable(&pdev->dev); + if (data->supports_runtime_pm) { + pm_runtime_get_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + } ci_hdrc_remove_device(data->ci_pdev); clk_disable_unprepare(data->clk); return 0; } -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM static int imx_controller_suspend(struct device *dev) { struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); @@ -215,6 +237,7 @@ static int imx_controller_suspend(struct device *dev) dev_dbg(dev, "at %s\n", __func__); clk_disable_unprepare(data->clk); + data->in_lpm = true; return 0; } @@ -222,25 +245,103 @@ static int imx_controller_suspend(struct device *dev) static int imx_controller_resume(struct device *dev) { struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); + int ret = 0; dev_dbg(dev, "at %s\n", __func__); - return clk_prepare_enable(data->clk); + if (!data->in_lpm) { + WARN_ON(1); + return 0; + } + + ret = clk_prepare_enable(data->clk); + if (ret) + return ret; + + data->in_lpm = false; + + ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false); + if (ret) { + dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); + goto clk_disable; + } + + return 0; + +clk_disable: + clk_disable_unprepare(data->clk); + return ret; } +#ifdef CONFIG_PM_SLEEP static int ci_hdrc_imx_suspend(struct device *dev) { + int ret; + + struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); + + if (data->in_lpm) + /* The core's suspend doesn't run */ + return 0; + + if (device_may_wakeup(dev)) { + ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); + if (ret) { + dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", + ret); + return ret; + } + } + return imx_controller_suspend(dev); } static int ci_hdrc_imx_resume(struct device *dev) { - return imx_controller_resume(dev); + struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); + int ret; + + ret = imx_controller_resume(dev); + if (!ret && data->supports_runtime_pm) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + } + + return ret; } #endif /* CONFIG_PM_SLEEP */ +static int ci_hdrc_imx_runtime_suspend(struct device *dev) +{ + struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); + int ret; + + if (data->in_lpm) { + WARN_ON(1); + return 0; + } + + ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); + if (ret) { + dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); + return ret; + } + + return imx_controller_suspend(dev); +} + +static int ci_hdrc_imx_runtime_resume(struct device *dev) +{ + return imx_controller_resume(dev); +} + +#endif /* CONFIG_PM */ + static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume) + SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, + ci_hdrc_imx_runtime_resume, NULL) }; static struct platform_driver ci_hdrc_imx_driver = { .probe = ci_hdrc_imx_probe, diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index 4ed828f75a1e..635717e9354a 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -22,5 +22,6 @@ struct imx_usbmisc_data { int imx_usbmisc_init(struct imx_usbmisc_data *); int imx_usbmisc_init_post(struct imx_usbmisc_data *); +int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *, bool); #endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */ diff --git a/drivers/usb/chipidea/ci_hdrc_pci.c b/drivers/usb/chipidea/ci_hdrc_pci.c index 4df669437211..773d150512fa 100644 --- a/drivers/usb/chipidea/ci_hdrc_pci.c +++ b/drivers/usb/chipidea/ci_hdrc_pci.c @@ -16,10 +16,16 @@ #include <linux/interrupt.h> #include <linux/usb/gadget.h> #include <linux/usb/chipidea.h> +#include <linux/usb/usb_phy_generic.h> /* driver name */ #define UDC_DRIVER_NAME "ci_hdrc_pci" +struct ci_hdrc_pci { + struct platform_device *ci; + struct platform_device *phy; +}; + /****************************************************************************** * PCI block *****************************************************************************/ @@ -52,7 +58,7 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct ci_hdrc_platform_data *platdata = (void *)id->driver_data; - struct platform_device *plat_ci; + struct ci_hdrc_pci *ci; struct resource res[3]; int retval = 0, nres = 2; @@ -61,6 +67,10 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev, return -ENODEV; } + ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL); + if (!ci) + return -ENOMEM; + retval = pcim_enable_device(pdev); if (retval) return retval; @@ -73,6 +83,11 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev, pci_set_master(pdev); pci_try_set_mwi(pdev); + /* register a nop PHY */ + ci->phy = usb_phy_generic_register(); + if (!ci->phy) + return -ENOMEM; + memset(res, 0, sizeof(res)); res[0].start = pci_resource_start(pdev, 0); res[0].end = pci_resource_end(pdev, 0); @@ -80,13 +95,14 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev, res[1].start = pdev->irq; res[1].flags = IORESOURCE_IRQ; - plat_ci = ci_hdrc_add_device(&pdev->dev, res, nres, platdata); - if (IS_ERR(plat_ci)) { + ci->ci = ci_hdrc_add_device(&pdev->dev, res, nres, platdata); + if (IS_ERR(ci->ci)) { dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n"); - return PTR_ERR(plat_ci); + usb_phy_generic_unregister(ci->phy); + return PTR_ERR(ci->ci); } - pci_set_drvdata(pdev, plat_ci); + pci_set_drvdata(pdev, ci); return 0; } @@ -101,9 +117,10 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev, */ static void ci_hdrc_pci_remove(struct pci_dev *pdev) { - struct platform_device *plat_ci = pci_get_drvdata(pdev); + struct ci_hdrc_pci *ci = pci_get_drvdata(pdev); - ci_hdrc_remove_device(plat_ci); + ci_hdrc_remove_device(ci->ci); + usb_phy_generic_unregister(ci->phy); } /** diff --git a/drivers/usb/chipidea/ci_hdrc_zevio.c b/drivers/usb/chipidea/ci_hdrc_zevio.c index d976fc1db73a..1264de505527 100644 --- a/drivers/usb/chipidea/ci_hdrc_zevio.c +++ b/drivers/usb/chipidea/ci_hdrc_zevio.c @@ -18,7 +18,7 @@ static struct ci_hdrc_platform_data ci_hdrc_zevio_platdata = { .name = "ci_hdrc_zevio", - .flags = CI_HDRC_REGS_SHARED, + .flags = CI_HDRC_REGS_SHARED | CI_HDRC_FORCE_FULLSPEED, .capoffset = DEF_CAPOFFSET, }; diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index a57dc8866fc5..74fea4fa41b1 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -137,6 +137,22 @@ static int hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm) return 0; } +static enum ci_revision ci_get_revision(struct ci_hdrc *ci) +{ + int ver = hw_read_id_reg(ci, ID_ID, VERSION) >> __ffs(VERSION); + enum ci_revision rev = CI_REVISION_UNKNOWN; + + if (ver == 0x2) { + rev = hw_read_id_reg(ci, ID_ID, REVISION) + >> __ffs(REVISION); + rev += CI_REVISION_20; + } else if (ver == 0x0) { + rev = CI_REVISION_1X; + } + + return rev; +} + /** * hw_read_intr_enable: returns interrupt enable register * @@ -251,8 +267,11 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base) /* Clear all interrupts status bits*/ hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff); - dev_dbg(ci->dev, "ChipIdea HDRC found, lpm: %d; cap: %p op: %p\n", - ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op); + ci->rev = ci_get_revision(ci); + + dev_dbg(ci->dev, + "ChipIdea HDRC found, revision: %d, lpm: %d; cap: %p op: %p\n", + ci->rev, ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op); /* setup lock mode ? */ @@ -491,6 +510,13 @@ static irqreturn_t ci_irq(int irq, void *data) irqreturn_t ret = IRQ_NONE; u32 otgsc = 0; + if (ci->in_lpm) { + disable_irq_nosync(irq); + ci->wakeup_int = true; + pm_runtime_get(ci->dev); + return IRQ_HANDLED; + } + if (ci->is_otg) { otgsc = hw_read_otgsc(ci, ~0); if (ci_otg_is_fsm_mode(ci)) { @@ -642,8 +668,12 @@ static void ci_get_otg_capable(struct ci_hdrc *ci) ci->is_otg = (hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC | DCCPARAMS_HC) == (DCCPARAMS_DC | DCCPARAMS_HC)); - if (ci->is_otg) + if (ci->is_otg) { dev_dbg(ci->dev, "It is OTG capable controller\n"); + /* Disable and clear all OTG irq */ + hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, + OTGSC_INT_STATUS_BITS); + } } static int ci_hdrc_probe(struct platform_device *pdev) @@ -673,6 +703,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) ci->platdata = dev_get_platdata(dev); ci->imx28_write_fix = !!(ci->platdata->flags & CI_HDRC_IMX28_WRITE_FIX); + ci->supports_runtime_pm = !!(ci->platdata->flags & + CI_HDRC_SUPPORTS_RUNTIME_PM); ret = hw_device_init(ci, base); if (ret < 0) { @@ -740,9 +772,6 @@ static int ci_hdrc_probe(struct platform_device *pdev) } if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) { - /* Disable and clear all OTG irq */ - hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, - OTGSC_INT_STATUS_BITS); ret = ci_hdrc_otg_init(ci); if (ret) { dev_err(dev, "init otg fails, ret = %d\n", ret); @@ -769,11 +798,11 @@ static int ci_hdrc_probe(struct platform_device *pdev) : CI_ROLE_GADGET; } - /* only update vbus status for peripheral */ - if (ci->role == CI_ROLE_GADGET) - ci_handle_vbus_change(ci); - if (!ci_otg_is_fsm_mode(ci)) { + /* only update vbus status for peripheral */ + if (ci->role == CI_ROLE_GADGET) + ci_handle_vbus_change(ci); + ret = ci_role_start(ci, ci->role); if (ret) { dev_err(dev, "can't start %s role\n", @@ -788,9 +817,19 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (ret) goto stop; + if (ci->supports_runtime_pm) { + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); + pm_runtime_mark_last_busy(ci->dev); + pm_runtime_use_autosuspend(&pdev->dev); + } + if (ci_otg_is_fsm_mode(ci)) ci_hdrc_otg_fsm_start(ci); + device_set_wakeup_capable(&pdev->dev, true); + ret = dbg_create_files(ci); if (!ret) return 0; @@ -807,6 +846,12 @@ static int ci_hdrc_remove(struct platform_device *pdev) { struct ci_hdrc *ci = platform_get_drvdata(pdev); + if (ci->supports_runtime_pm) { + pm_runtime_get_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + } + dbg_remove_files(ci); ci_role_destroy(ci); ci_hdrc_enter_lpm(ci, true); @@ -815,13 +860,41 @@ static int ci_hdrc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM +/* Prepare wakeup by SRP before suspend */ +static void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci) +{ + if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) && + !hw_read_otgsc(ci, OTGSC_ID)) { + hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, + PORTSC_PP); + hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_WKCN, + PORTSC_WKCN); + } +} + +/* Handle SRP when wakeup by data pulse */ +static void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci) +{ + if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) && + (ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) { + if (!hw_read_otgsc(ci, OTGSC_ID)) { + ci->fsm.a_srp_det = 1; + ci->fsm.a_bus_drop = 0; + } else { + ci->fsm.id = 1; + } + ci_otg_queue_work(ci); + } +} + static void ci_controller_suspend(struct ci_hdrc *ci) { + disable_irq(ci->irq); ci_hdrc_enter_lpm(ci, true); - - if (ci->usb_phy) - usb_phy_set_suspend(ci->usb_phy, 1); + usb_phy_set_suspend(ci->usb_phy, 1); + ci->in_lpm = true; + enable_irq(ci->irq); } static int ci_controller_resume(struct device *dev) @@ -830,23 +903,59 @@ static int ci_controller_resume(struct device *dev) dev_dbg(dev, "at %s\n", __func__); - ci_hdrc_enter_lpm(ci, false); + if (!ci->in_lpm) { + WARN_ON(1); + return 0; + } + 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(); } + ci->in_lpm = false; + if (ci->wakeup_int) { + ci->wakeup_int = false; + pm_runtime_mark_last_busy(ci->dev); + pm_runtime_put_autosuspend(ci->dev); + enable_irq(ci->irq); + if (ci_otg_is_fsm_mode(ci)) + ci_otg_fsm_wakeup_by_srp(ci); + } + return 0; } +#ifdef CONFIG_PM_SLEEP static int ci_suspend(struct device *dev) { struct ci_hdrc *ci = dev_get_drvdata(dev); if (ci->wq) flush_workqueue(ci->wq); + /* + * Controller needs to be active during suspend, otherwise the core + * may run resume when the parent is at suspend if other driver's + * suspend fails, it occurs before parent's suspend has not started, + * but the core suspend has finished. + */ + if (ci->in_lpm) + pm_runtime_resume(dev); + + if (ci->in_lpm) { + WARN_ON(1); + return 0; + } + + if (device_may_wakeup(dev)) { + if (ci_otg_is_fsm_mode(ci)) + ci_otg_fsm_suspend_for_srp(ci); + + usb_phy_set_wakeup(ci->usb_phy, true); + enable_irq_wake(ci->irq); + } ci_controller_suspend(ci); @@ -855,13 +964,57 @@ static int ci_suspend(struct device *dev) static int ci_resume(struct device *dev) { - return ci_controller_resume(dev); + struct ci_hdrc *ci = dev_get_drvdata(dev); + int ret; + + if (device_may_wakeup(dev)) + disable_irq_wake(ci->irq); + + ret = ci_controller_resume(dev); + if (ret) + return ret; + + if (ci->supports_runtime_pm) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + } + + return ret; } #endif /* CONFIG_PM_SLEEP */ +static int ci_runtime_suspend(struct device *dev) +{ + struct ci_hdrc *ci = dev_get_drvdata(dev); + + dev_dbg(dev, "at %s\n", __func__); + + if (ci->in_lpm) { + WARN_ON(1); + return 0; + } + + if (ci_otg_is_fsm_mode(ci)) + ci_otg_fsm_suspend_for_srp(ci); + + usb_phy_set_wakeup(ci->usb_phy, true); + ci_controller_suspend(ci); + + return 0; +} + +static int ci_runtime_resume(struct device *dev) +{ + return ci_controller_resume(dev); +} + +#endif /* CONFIG_PM */ static const struct dev_pm_ops ci_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume) + SET_RUNTIME_PM_OPS(ci_runtime_suspend, ci_runtime_resume, NULL) }; + static struct platform_driver ci_hdrc_driver = { .probe = ci_hdrc_probe, .remove = ci_hdrc_remove, diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 48731d0bab35..21fe1a314313 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -33,6 +33,7 @@ #include "host.h" static struct hc_driver __read_mostly ci_ehci_hc_driver; +static int (*orig_bus_suspend)(struct usb_hcd *hcd); struct ehci_ci_priv { struct regulator *reg_vbus; @@ -43,11 +44,10 @@ 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 (priv->reg_vbus) { if (port > 1) { dev_warn(dev, "Not support multi-port regulator control\n"); @@ -113,12 +113,23 @@ static int host_start(struct ci_hdrc *ci) priv = (struct ehci_ci_priv *)ehci->priv; priv->reg_vbus = NULL; - if (ci->platdata->reg_vbus) - priv->reg_vbus = ci->platdata->reg_vbus; + if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) { + if (ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON) { + 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; + } + } else { + priv->reg_vbus = ci->platdata->reg_vbus; + } + } ret = usb_add_hcd(hcd, 0, 0); if (ret) { - goto put_hcd; + goto disable_reg; } else { struct usb_otg *otg = &ci->otg; @@ -133,8 +144,15 @@ static int host_start(struct ci_hdrc *ci) if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING) hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); + if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) + hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); + return ret; +disable_reg: + if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci) && + (ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON)) + regulator_disable(ci->platdata->reg_vbus); put_hcd: usb_put_hcd(hcd); @@ -148,6 +166,9 @@ 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) && + (ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON)) + regulator_disable(ci->platdata->reg_vbus); } } @@ -158,6 +179,47 @@ void ci_hdrc_host_destroy(struct ci_hdrc *ci) host_stop(ci); } +static int ci_ehci_bus_suspend(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int port; + u32 tmp; + + int ret = orig_bus_suspend(hcd); + + if (ret) + return ret; + + port = HCS_N_PORTS(ehci->hcs_params); + while (port--) { + u32 __iomem *reg = &ehci->regs->port_status[port]; + u32 portsc = ehci_readl(ehci, reg); + + if (portsc & PORT_CONNECT) { + /* + * For chipidea, the resume signal will be ended + * automatically, so for remote wakeup case, the + * usbcmd.rs may not be set before the resume has + * ended if other resume paths consumes too much + * time (~24ms), in that case, the SOF will not + * send out within 3ms after resume ends, then the + * high speed device will enter full speed mode. + */ + + tmp = ehci_readl(ehci, &ehci->regs->command); + tmp |= CMD_RUN; + ehci_writel(ehci, tmp, &ehci->regs->command); + /* + * It needs a short delay between set RS bit and PHCD. + */ + usleep_range(150, 200); + break; + } + } + + return 0; +} + int ci_hdrc_host_init(struct ci_hdrc *ci) { struct ci_role_driver *rdrv; @@ -176,6 +238,8 @@ int ci_hdrc_host_init(struct ci_hdrc *ci) ci->roles[CI_ROLE_HOST] = rdrv; ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides); + orig_bus_suspend = ci_ehci_hc_driver.bus_suspend; + ci_ehci_hc_driver.bus_suspend = ci_ehci_bus_suspend; return 0; } diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index a048b08b9d4d..ad6c87a4653c 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -96,6 +96,7 @@ static void ci_otg_work(struct work_struct *work) return; } + pm_runtime_get_sync(ci->dev); if (ci->id_event) { ci->id_event = false; ci_handle_id_switch(ci); @@ -104,6 +105,7 @@ static void ci_otg_work(struct work_struct *work) ci_handle_vbus_change(ci); } else dev_err(ci->dev, "unexpected event occurs at %s\n", __func__); + pm_runtime_put_sync(ci->dev); enable_irq(ci->irq); } diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index 562e581f6765..e3cf5be66d3d 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -225,6 +225,9 @@ static void ci_otg_add_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t) return; } + if (list_empty(active_timers)) + pm_runtime_get(ci->dev); + timer->count = timer->expires; list_add_tail(&timer->list, active_timers); @@ -241,17 +244,22 @@ static void ci_otg_del_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t) struct ci_otg_fsm_timer *tmp_timer, *del_tmp; struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t]; struct list_head *active_timers = &ci->fsm_timer->active_timers; + int flag = 0; if (t >= NUM_CI_OTG_FSM_TIMERS) return; list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list) - if (tmp_timer == timer) + if (tmp_timer == timer) { list_del(&timer->list); + flag = 1; + } /* Disable 1ms irq if there is no any active timer */ - if (list_empty(active_timers)) + if (list_empty(active_timers) && (flag == 1)) { hw_write_otgsc(ci, OTGSC_1MSIE, 0); + pm_runtime_put(ci->dev); + } } /* @@ -275,8 +283,10 @@ static inline int ci_otg_tick_timer(struct ci_hdrc *ci) } /* disable 1ms irq if there is no any timer active */ - if ((expired == 1) && list_empty(active_timers)) + if ((expired == 1) && list_empty(active_timers)) { hw_write_otgsc(ci, OTGSC_1MSIE, 0); + pm_runtime_put(ci->dev); + } return expired; } @@ -585,6 +595,7 @@ int ci_otg_fsm_work(struct ci_hdrc *ci) ci->fsm.otg->state < OTG_STATE_A_IDLE) return 0; + pm_runtime_get_sync(ci->dev); if (otg_statemachine(&ci->fsm)) { if (ci->fsm.otg->state == OTG_STATE_A_IDLE) { /* @@ -609,8 +620,13 @@ int ci_otg_fsm_work(struct ci_hdrc *ci) */ ci_otg_queue_work(ci); } + } else if (ci->fsm.otg->state == OTG_STATE_A_HOST) { + pm_runtime_mark_last_busy(ci->dev); + pm_runtime_put_autosuspend(ci->dev); + return 0; } } + pm_runtime_put_sync(ci->dev); return 0; } diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 4bfb7ac0239f..228654c94843 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -522,6 +522,20 @@ static void free_pending_td(struct ci_hw_ep *hwep) kfree(pending); } +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.token &= + cpu_to_le32(~(TD_STATUS_HALTED | TD_STATUS_ACTIVE)); + + /* Synchronize before ep prime */ + wmb(); + + return hw_ep_prime(ci, hwep->num, hwep->dir, + hwep->type == USB_ENDPOINT_XFER_CONTROL); +} + /** * _hardware_dequeue: handles a request at hardware level * @gadget: gadget @@ -535,6 +549,7 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) struct td_node *node, *tmpnode; unsigned remaining_length; unsigned actual = hwreq->req.length; + struct ci_hdrc *ci = hwep->ci; if (hwreq->req.status != -EALREADY) return -EINVAL; @@ -544,6 +559,11 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) { tmptoken = le32_to_cpu(node->ptr->token); if ((TD_STATUS_ACTIVE & tmptoken) != 0) { + int n = hw_ep_bit(hwep->num, hwep->dir); + + if (ci->rev == CI_REVISION_24) + if (!hw_read(ci, OP_ENDPTSTAT, BIT(n))) + reprime_dtd(ci, hwep, node); hwreq->req.status = -EALREADY; return -EBUSY; } @@ -1162,10 +1182,13 @@ static int ep_enable(struct usb_ep *ep, /* only internal SW should enable ctrl endpts */ - hwep->ep.desc = desc; - - if (!list_empty(&hwep->qh.queue)) + if (!list_empty(&hwep->qh.queue)) { dev_warn(hwep->ci->dev, "enabling a non-empty endpoint!\n"); + spin_unlock_irqrestore(hwep->lock, flags); + return -EBUSY; + } + + hwep->ep.desc = desc; hwep->dir = usb_endpoint_dir_in(desc) ? TX : RX; hwep->num = usb_endpoint_num(desc); diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index c3c6225b8acf..8af070f15d3e 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -11,7 +11,6 @@ #include <linux/module.h> #include <linux/of_platform.h> -#include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> #include <linux/delay.h> @@ -56,6 +55,19 @@ #define MX53_USB_PLL_DIV_24_MHZ 0x01 #define MX6_BM_OVER_CUR_DIS BIT(7) +#define MX6_BM_WAKEUP_ENABLE BIT(10) +#define MX6_BM_ID_WAKEUP BIT(16) +#define MX6_BM_VBUS_WAKEUP BIT(17) +#define MX6SX_BM_DPDM_WAKEUP_EN BIT(29) +#define MX6_BM_WAKEUP_INTR BIT(31) +#define MX6_USB_OTG1_PHY_CTRL 0x18 +/* For imx6dql, it is host-only controller, for later imx6, it is otg's */ +#define MX6_USB_OTG2_PHY_CTRL 0x1c +#define MX6SX_USB_VBUS_WAKEUP_SOURCE(v) (v << 8) +#define MX6SX_USB_VBUS_WAKEUP_SOURCE_VBUS MX6SX_USB_VBUS_WAKEUP_SOURCE(0) +#define MX6SX_USB_VBUS_WAKEUP_SOURCE_AVALID MX6SX_USB_VBUS_WAKEUP_SOURCE(1) +#define MX6SX_USB_VBUS_WAKEUP_SOURCE_BVALID MX6SX_USB_VBUS_WAKEUP_SOURCE(2) +#define MX6SX_USB_VBUS_WAKEUP_SOURCE_SESS_END MX6SX_USB_VBUS_WAKEUP_SOURCE(3) #define VF610_OVER_CUR_DIS BIT(7) @@ -64,12 +76,13 @@ struct usbmisc_ops { int (*init)(struct imx_usbmisc_data *data); /* It's called once after adding a usb device */ int (*post)(struct imx_usbmisc_data *data); + /* It's called when we need to enable/disable usb wakeup */ + int (*set_wakeup)(struct imx_usbmisc_data *data, bool enabled); }; struct imx_usbmisc { void __iomem *base; spinlock_t lock; - struct clk *clk; const struct usbmisc_ops *ops; }; @@ -204,6 +217,35 @@ static int usbmisc_imx53_init(struct imx_usbmisc_data *data) return 0; } +static int usbmisc_imx6q_set_wakeup + (struct imx_usbmisc_data *data, bool enabled) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 val; + u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE | + MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP); + int ret = 0; + + if (data->index > 3) + return -EINVAL; + + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + data->index * 4); + if (enabled) { + val |= wakeup_setting; + writel(val, usbmisc->base + data->index * 4); + } else { + if (val & MX6_BM_WAKEUP_INTR) + pr_debug("wakeup int at ci_hdrc.%d\n", data->index); + val &= ~wakeup_setting; + writel(val, usbmisc->base + data->index * 4); + } + spin_unlock_irqrestore(&usbmisc->lock, flags); + + return ret; +} + static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); @@ -221,9 +263,40 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) spin_unlock_irqrestore(&usbmisc->lock, flags); } + usbmisc_imx6q_set_wakeup(data, false); + return 0; } +static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data) +{ + void __iomem *reg = NULL; + unsigned long flags; + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + u32 val; + int ret = 0; + + usbmisc_imx6q_init(data); + + if (data->index == 0 || data->index == 1) { + reg = usbmisc->base + MX6_USB_OTG1_PHY_CTRL + data->index * 4; + spin_lock_irqsave(&usbmisc->lock, flags); + /* Set vbus wakeup source as bvalid */ + val = readl(reg); + writel(val | MX6SX_USB_VBUS_WAKEUP_SOURCE_BVALID, reg); + /* + * Disable dp/dm wakeup in device mode when vbus is + * not there. + */ + val = readl(usbmisc->base + data->index * 4); + writel(val & ~MX6SX_BM_DPDM_WAKEUP_EN, + usbmisc->base + data->index * 4); + spin_unlock_irqrestore(&usbmisc->lock, flags); + } + + return ret; +} + static int usbmisc_vf610_init(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); @@ -258,6 +331,7 @@ static const struct usbmisc_ops imx53_usbmisc_ops = { }; static const struct usbmisc_ops imx6q_usbmisc_ops = { + .set_wakeup = usbmisc_imx6q_set_wakeup, .init = usbmisc_imx6q_init, }; @@ -265,10 +339,19 @@ static const struct usbmisc_ops vf610_usbmisc_ops = { .init = usbmisc_vf610_init, }; +static const struct usbmisc_ops imx6sx_usbmisc_ops = { + .set_wakeup = usbmisc_imx6q_set_wakeup, + .init = usbmisc_imx6sx_init, +}; + int imx_usbmisc_init(struct imx_usbmisc_data *data) { - struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + struct imx_usbmisc *usbmisc; + + if (!data) + return 0; + usbmisc = dev_get_drvdata(data->dev); if (!usbmisc->ops->init) return 0; return usbmisc->ops->init(data); @@ -277,14 +360,32 @@ EXPORT_SYMBOL_GPL(imx_usbmisc_init); int imx_usbmisc_init_post(struct imx_usbmisc_data *data) { - struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + struct imx_usbmisc *usbmisc; + if (!data) + return 0; + + usbmisc = dev_get_drvdata(data->dev); if (!usbmisc->ops->post) return 0; return usbmisc->ops->post(data); } EXPORT_SYMBOL_GPL(imx_usbmisc_init_post); +int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled) +{ + struct imx_usbmisc *usbmisc; + + if (!data) + return 0; + + usbmisc = dev_get_drvdata(data->dev); + if (!usbmisc->ops->set_wakeup) + return 0; + return usbmisc->ops->set_wakeup(data, enabled); +} +EXPORT_SYMBOL_GPL(imx_usbmisc_set_wakeup); + static const struct of_device_id usbmisc_imx_dt_ids[] = { { .compatible = "fsl,imx25-usbmisc", @@ -314,6 +415,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = { .compatible = "fsl,vf610-usbmisc", .data = &vf610_usbmisc_ops, }, + { + .compatible = "fsl,imx6sx-usbmisc", + .data = &imx6sx_usbmisc_ops, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids); @@ -322,7 +427,6 @@ static int usbmisc_imx_probe(struct platform_device *pdev) { struct resource *res; struct imx_usbmisc *data; - int ret; struct of_device_id *tmp_dev; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); @@ -336,20 +440,6 @@ static int usbmisc_imx_probe(struct platform_device *pdev) if (IS_ERR(data->base)) return PTR_ERR(data->base); - data->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(data->clk)) { - dev_err(&pdev->dev, - "failed to get clock, err=%ld\n", PTR_ERR(data->clk)); - return PTR_ERR(data->clk); - } - - ret = clk_prepare_enable(data->clk); - if (ret) { - dev_err(&pdev->dev, - "clk_prepare_enable failed, err=%d\n", ret); - return ret; - } - tmp_dev = (struct of_device_id *) of_match_device(usbmisc_imx_dt_ids, &pdev->dev); data->ops = (const struct usbmisc_ops *)tmp_dev->data; @@ -360,8 +450,6 @@ static int usbmisc_imx_probe(struct platform_device *pdev) static int usbmisc_imx_remove(struct platform_device *pdev) { - struct imx_usbmisc *usbmisc = dev_get_drvdata(&pdev->dev); - clk_disable_unprepare(usbmisc->clk); return 0; } diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index 535997a6681b..ab94f78c4dd1 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -19,6 +19,7 @@ struct ci_hdrc_platform_data { enum usb_phy_interface phy_mode; unsigned long flags; #define CI_HDRC_REGS_SHARED BIT(0) +#define CI_HDRC_SUPPORTS_RUNTIME_PM BIT(2) #define CI_HDRC_DISABLE_STREAMING BIT(3) /* * Only set it when DCCPARAMS.DC==1 and DCCPARAMS.HC==1, @@ -27,6 +28,7 @@ struct ci_hdrc_platform_data { #define CI_HDRC_DUAL_ROLE_NOT_OTG BIT(4) #define CI_HDRC_IMX28_WRITE_FIX BIT(5) #define CI_HDRC_FORCE_FULLSPEED BIT(6) +#define CI_HDRC_TURN_VBUS_EARLY_ON BIT(7) enum usb_dr_mode dr_mode; #define CI_HDRC_CONTROLLER_RESET_EVENT 0 #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 |