Loading drivers/usb/cdns3/cdns3-imx.c +186 −5 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ #include <linux/io.h> #include <linux/of_platform.h> #include <linux/iopoll.h> #include <linux/pm_runtime.h> #include "core.h" #define USB3_CORE_CTRL1 0x00 #define USB3_CORE_CTRL2 0x04 Loading @@ -32,7 +34,7 @@ /* Register bits definition */ /* USB3_CORE_CTRL1 */ #define SW_RESET_MASK (0x3f << 26) #define SW_RESET_MASK GENMASK(31, 26) #define PWR_SW_RESET BIT(31) #define APB_SW_RESET BIT(30) #define AXI_SW_RESET BIT(29) Loading Loading @@ -66,11 +68,30 @@ #define CLK_VALID_COMPARE_BITS (0xf << 28) #define PHY_REFCLK_REQ (1 << 0) /* OTG registers definition */ #define OTGSTS 0x4 /* OTGSTS */ #define OTG_NRDY BIT(11) /* xHCI registers definition */ #define XECP_PM_PMCSR 0x8018 #define XECP_AUX_CTRL_REG1 0x8120 /* Register bits definition */ /* XECP_AUX_CTRL_REG1 */ #define CFG_RXDET_P3_EN BIT(15) /* XECP_PM_PMCSR */ #define PS_MASK GENMASK(1, 0) #define PS_D0 0 #define PS_D1 1 struct cdns_imx { struct device *dev; void __iomem *noncore; struct clk_bulk_data *clks; int num_clks; struct platform_device *cdns3_pdev; }; static inline u32 cdns_imx_readl(struct cdns_imx *data, u32 offset) Loading Loading @@ -126,6 +147,20 @@ static int cdns_imx_noncore_init(struct cdns_imx *data) return ret; } static int cdns_imx_platform_suspend(struct device *dev, bool suspend, bool wakeup); static struct cdns3_platform_data cdns_imx_pdata = { .platform_suspend = cdns_imx_platform_suspend, }; static const struct of_dev_auxdata cdns_imx_auxdata[] = { { .compatible = "cdns,usb3", .platform_data = &cdns_imx_pdata, }, {}, }; static int cdns_imx_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; Loading Loading @@ -162,14 +197,18 @@ static int cdns_imx_probe(struct platform_device *pdev) if (ret) goto err; ret = of_platform_populate(node, NULL, NULL, dev); ret = of_platform_populate(node, NULL, cdns_imx_auxdata, dev); if (ret) { dev_err(dev, "failed to create children: %d\n", ret); goto err; } return ret; device_set_wakeup_capable(dev, true); pm_runtime_set_active(dev); pm_runtime_enable(dev); pm_runtime_forbid(dev); return ret; err: clk_bulk_disable_unprepare(data->num_clks, data->clks); return ret; Loading @@ -194,6 +233,147 @@ static int cdns_imx_remove(struct platform_device *pdev) return 0; } #ifdef CONFIG_PM static void cdns3_set_wakeup(struct cdns_imx *data, bool enable) { u32 value; value = cdns_imx_readl(data, USB3_INT_REG); if (enable) value |= OTG_WAKEUP_EN | DEVU3_WAEKUP_EN; else value &= ~(OTG_WAKEUP_EN | DEVU3_WAEKUP_EN); cdns_imx_writel(data, USB3_INT_REG, value); } static int cdns_imx_platform_suspend(struct device *dev, bool suspend, bool wakeup) { struct cdns3 *cdns = dev_get_drvdata(dev); struct device *parent = dev->parent; struct cdns_imx *data = dev_get_drvdata(parent); void __iomem *otg_regs = (void __iomem *)(cdns->otg_regs); void __iomem *xhci_regs = cdns->xhci_regs; u32 value; int ret = 0; if (cdns->role != USB_ROLE_HOST) return 0; if (suspend) { /* SW request low power when all usb ports allow to it ??? */ value = readl(xhci_regs + XECP_PM_PMCSR); value &= ~PS_MASK; value |= PS_D1; writel(value, xhci_regs + XECP_PM_PMCSR); /* mdctrl_clk_sel */ value = cdns_imx_readl(data, USB3_CORE_CTRL1); value |= MDCTRL_CLK_SEL; cdns_imx_writel(data, USB3_CORE_CTRL1, value); /* wait for mdctrl_clk_status */ value = cdns_imx_readl(data, USB3_CORE_STATUS); ret = readl_poll_timeout(data->noncore + USB3_CORE_STATUS, value, (value & MDCTRL_CLK_STATUS) == MDCTRL_CLK_STATUS, 10, 100000); if (ret) dev_warn(parent, "wait mdctrl_clk_status timeout\n"); /* wait lpm_clk_req to be 0 */ value = cdns_imx_readl(data, USB3_INT_REG); ret = readl_poll_timeout(data->noncore + USB3_INT_REG, value, (value & LPM_CLK_REQ) != LPM_CLK_REQ, 10, 100000); if (ret) dev_warn(parent, "wait lpm_clk_req timeout\n"); /* wait phy_refclk_req to be 0 */ value = cdns_imx_readl(data, USB3_SSPHY_STATUS); ret = readl_poll_timeout(data->noncore + USB3_SSPHY_STATUS, value, (value & PHY_REFCLK_REQ) != PHY_REFCLK_REQ, 10, 100000); if (ret) dev_warn(parent, "wait phy_refclk_req timeout\n"); cdns3_set_wakeup(data, wakeup); } else { cdns3_set_wakeup(data, false); /* SW request D0 */ value = readl(xhci_regs + XECP_PM_PMCSR); value &= ~PS_MASK; value |= PS_D0; writel(value, xhci_regs + XECP_PM_PMCSR); /* clr CFG_RXDET_P3_EN */ value = readl(xhci_regs + XECP_AUX_CTRL_REG1); value &= ~CFG_RXDET_P3_EN; writel(value, xhci_regs + XECP_AUX_CTRL_REG1); /* clear mdctrl_clk_sel */ value = cdns_imx_readl(data, USB3_CORE_CTRL1); value &= ~MDCTRL_CLK_SEL; cdns_imx_writel(data, USB3_CORE_CTRL1, value); /* wait CLK_125_REQ to be 1 */ value = cdns_imx_readl(data, USB3_INT_REG); ret = readl_poll_timeout(data->noncore + USB3_INT_REG, value, (value & CLK_125_REQ) == CLK_125_REQ, 10, 100000); if (ret) dev_warn(parent, "wait CLK_125_REQ timeout\n"); /* wait for mdctrl_clk_status is cleared */ value = cdns_imx_readl(data, USB3_CORE_STATUS); ret = readl_poll_timeout(data->noncore + USB3_CORE_STATUS, value, (value & MDCTRL_CLK_STATUS) != MDCTRL_CLK_STATUS, 10, 100000); if (ret) dev_warn(parent, "wait mdctrl_clk_status cleared timeout\n"); /* Wait until OTG_NRDY is 0 */ value = readl(otg_regs + OTGSTS); ret = readl_poll_timeout(otg_regs + OTGSTS, value, (value & OTG_NRDY) != OTG_NRDY, 10, 100000); if (ret) dev_warn(parent, "wait OTG ready timeout\n"); } return ret; } static int cdns_imx_resume(struct device *dev) { struct cdns_imx *data = dev_get_drvdata(dev); return clk_bulk_prepare_enable(data->num_clks, data->clks); } static int cdns_imx_suspend(struct device *dev) { struct cdns_imx *data = dev_get_drvdata(dev); clk_bulk_disable_unprepare(data->num_clks, data->clks); return 0; } #else static int cdns_imx_platform_suspend(struct device *dev, bool suspend, bool wakeup) { return 0; } #endif /* CONFIG_PM */ static const struct dev_pm_ops cdns_imx_pm_ops = { SET_RUNTIME_PM_OPS(cdns_imx_suspend, cdns_imx_resume, NULL) }; static const struct of_device_id cdns_imx_of_match[] = { { .compatible = "fsl,imx8qm-usb3", }, {}, Loading @@ -206,6 +386,7 @@ static struct platform_driver cdns_imx_driver = { .driver = { .name = "cdns3-imx", .of_match_table = cdns_imx_of_match, .pm = &cdns_imx_pm_ops, }, }; module_platform_driver(cdns_imx_driver); Loading Loading
drivers/usb/cdns3/cdns3-imx.c +186 −5 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ #include <linux/io.h> #include <linux/of_platform.h> #include <linux/iopoll.h> #include <linux/pm_runtime.h> #include "core.h" #define USB3_CORE_CTRL1 0x00 #define USB3_CORE_CTRL2 0x04 Loading @@ -32,7 +34,7 @@ /* Register bits definition */ /* USB3_CORE_CTRL1 */ #define SW_RESET_MASK (0x3f << 26) #define SW_RESET_MASK GENMASK(31, 26) #define PWR_SW_RESET BIT(31) #define APB_SW_RESET BIT(30) #define AXI_SW_RESET BIT(29) Loading Loading @@ -66,11 +68,30 @@ #define CLK_VALID_COMPARE_BITS (0xf << 28) #define PHY_REFCLK_REQ (1 << 0) /* OTG registers definition */ #define OTGSTS 0x4 /* OTGSTS */ #define OTG_NRDY BIT(11) /* xHCI registers definition */ #define XECP_PM_PMCSR 0x8018 #define XECP_AUX_CTRL_REG1 0x8120 /* Register bits definition */ /* XECP_AUX_CTRL_REG1 */ #define CFG_RXDET_P3_EN BIT(15) /* XECP_PM_PMCSR */ #define PS_MASK GENMASK(1, 0) #define PS_D0 0 #define PS_D1 1 struct cdns_imx { struct device *dev; void __iomem *noncore; struct clk_bulk_data *clks; int num_clks; struct platform_device *cdns3_pdev; }; static inline u32 cdns_imx_readl(struct cdns_imx *data, u32 offset) Loading Loading @@ -126,6 +147,20 @@ static int cdns_imx_noncore_init(struct cdns_imx *data) return ret; } static int cdns_imx_platform_suspend(struct device *dev, bool suspend, bool wakeup); static struct cdns3_platform_data cdns_imx_pdata = { .platform_suspend = cdns_imx_platform_suspend, }; static const struct of_dev_auxdata cdns_imx_auxdata[] = { { .compatible = "cdns,usb3", .platform_data = &cdns_imx_pdata, }, {}, }; static int cdns_imx_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; Loading Loading @@ -162,14 +197,18 @@ static int cdns_imx_probe(struct platform_device *pdev) if (ret) goto err; ret = of_platform_populate(node, NULL, NULL, dev); ret = of_platform_populate(node, NULL, cdns_imx_auxdata, dev); if (ret) { dev_err(dev, "failed to create children: %d\n", ret); goto err; } return ret; device_set_wakeup_capable(dev, true); pm_runtime_set_active(dev); pm_runtime_enable(dev); pm_runtime_forbid(dev); return ret; err: clk_bulk_disable_unprepare(data->num_clks, data->clks); return ret; Loading @@ -194,6 +233,147 @@ static int cdns_imx_remove(struct platform_device *pdev) return 0; } #ifdef CONFIG_PM static void cdns3_set_wakeup(struct cdns_imx *data, bool enable) { u32 value; value = cdns_imx_readl(data, USB3_INT_REG); if (enable) value |= OTG_WAKEUP_EN | DEVU3_WAEKUP_EN; else value &= ~(OTG_WAKEUP_EN | DEVU3_WAEKUP_EN); cdns_imx_writel(data, USB3_INT_REG, value); } static int cdns_imx_platform_suspend(struct device *dev, bool suspend, bool wakeup) { struct cdns3 *cdns = dev_get_drvdata(dev); struct device *parent = dev->parent; struct cdns_imx *data = dev_get_drvdata(parent); void __iomem *otg_regs = (void __iomem *)(cdns->otg_regs); void __iomem *xhci_regs = cdns->xhci_regs; u32 value; int ret = 0; if (cdns->role != USB_ROLE_HOST) return 0; if (suspend) { /* SW request low power when all usb ports allow to it ??? */ value = readl(xhci_regs + XECP_PM_PMCSR); value &= ~PS_MASK; value |= PS_D1; writel(value, xhci_regs + XECP_PM_PMCSR); /* mdctrl_clk_sel */ value = cdns_imx_readl(data, USB3_CORE_CTRL1); value |= MDCTRL_CLK_SEL; cdns_imx_writel(data, USB3_CORE_CTRL1, value); /* wait for mdctrl_clk_status */ value = cdns_imx_readl(data, USB3_CORE_STATUS); ret = readl_poll_timeout(data->noncore + USB3_CORE_STATUS, value, (value & MDCTRL_CLK_STATUS) == MDCTRL_CLK_STATUS, 10, 100000); if (ret) dev_warn(parent, "wait mdctrl_clk_status timeout\n"); /* wait lpm_clk_req to be 0 */ value = cdns_imx_readl(data, USB3_INT_REG); ret = readl_poll_timeout(data->noncore + USB3_INT_REG, value, (value & LPM_CLK_REQ) != LPM_CLK_REQ, 10, 100000); if (ret) dev_warn(parent, "wait lpm_clk_req timeout\n"); /* wait phy_refclk_req to be 0 */ value = cdns_imx_readl(data, USB3_SSPHY_STATUS); ret = readl_poll_timeout(data->noncore + USB3_SSPHY_STATUS, value, (value & PHY_REFCLK_REQ) != PHY_REFCLK_REQ, 10, 100000); if (ret) dev_warn(parent, "wait phy_refclk_req timeout\n"); cdns3_set_wakeup(data, wakeup); } else { cdns3_set_wakeup(data, false); /* SW request D0 */ value = readl(xhci_regs + XECP_PM_PMCSR); value &= ~PS_MASK; value |= PS_D0; writel(value, xhci_regs + XECP_PM_PMCSR); /* clr CFG_RXDET_P3_EN */ value = readl(xhci_regs + XECP_AUX_CTRL_REG1); value &= ~CFG_RXDET_P3_EN; writel(value, xhci_regs + XECP_AUX_CTRL_REG1); /* clear mdctrl_clk_sel */ value = cdns_imx_readl(data, USB3_CORE_CTRL1); value &= ~MDCTRL_CLK_SEL; cdns_imx_writel(data, USB3_CORE_CTRL1, value); /* wait CLK_125_REQ to be 1 */ value = cdns_imx_readl(data, USB3_INT_REG); ret = readl_poll_timeout(data->noncore + USB3_INT_REG, value, (value & CLK_125_REQ) == CLK_125_REQ, 10, 100000); if (ret) dev_warn(parent, "wait CLK_125_REQ timeout\n"); /* wait for mdctrl_clk_status is cleared */ value = cdns_imx_readl(data, USB3_CORE_STATUS); ret = readl_poll_timeout(data->noncore + USB3_CORE_STATUS, value, (value & MDCTRL_CLK_STATUS) != MDCTRL_CLK_STATUS, 10, 100000); if (ret) dev_warn(parent, "wait mdctrl_clk_status cleared timeout\n"); /* Wait until OTG_NRDY is 0 */ value = readl(otg_regs + OTGSTS); ret = readl_poll_timeout(otg_regs + OTGSTS, value, (value & OTG_NRDY) != OTG_NRDY, 10, 100000); if (ret) dev_warn(parent, "wait OTG ready timeout\n"); } return ret; } static int cdns_imx_resume(struct device *dev) { struct cdns_imx *data = dev_get_drvdata(dev); return clk_bulk_prepare_enable(data->num_clks, data->clks); } static int cdns_imx_suspend(struct device *dev) { struct cdns_imx *data = dev_get_drvdata(dev); clk_bulk_disable_unprepare(data->num_clks, data->clks); return 0; } #else static int cdns_imx_platform_suspend(struct device *dev, bool suspend, bool wakeup) { return 0; } #endif /* CONFIG_PM */ static const struct dev_pm_ops cdns_imx_pm_ops = { SET_RUNTIME_PM_OPS(cdns_imx_suspend, cdns_imx_resume, NULL) }; static const struct of_device_id cdns_imx_of_match[] = { { .compatible = "fsl,imx8qm-usb3", }, {}, Loading @@ -206,6 +386,7 @@ static struct platform_driver cdns_imx_driver = { .driver = { .name = "cdns3-imx", .of_match_table = cdns_imx_of_match, .pm = &cdns_imx_pm_ops, }, }; module_platform_driver(cdns_imx_driver); Loading