diff options
Diffstat (limited to 'drivers/usb/cdns3')
-rw-r--r-- | drivers/usb/cdns3/cdns3-imx.c | 191 | ||||
-rw-r--r-- | drivers/usb/cdns3/core.c | 202 | ||||
-rw-r--r-- | drivers/usb/cdns3/core.h | 17 | ||||
-rw-r--r-- | drivers/usb/cdns3/drd.c | 20 | ||||
-rw-r--r-- | drivers/usb/cdns3/drd.h | 5 | ||||
-rw-r--r-- | drivers/usb/cdns3/ep0.c | 10 | ||||
-rw-r--r-- | drivers/usb/cdns3/gadget.c | 276 | ||||
-rw-r--r-- | drivers/usb/cdns3/gadget.h | 11 | ||||
-rw-r--r-- | drivers/usb/cdns3/host.c | 7 |
9 files changed, 606 insertions, 133 deletions
diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c index aba988e71958..54a2d70a9c73 100644 --- a/drivers/usb/cdns3/cdns3-imx.c +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -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 @@ -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) @@ -53,8 +55,8 @@ #define LPM_CLK_REQ BIT(28) #define DEVU3_WAEKUP_EN BIT(14) #define OTG_WAKEUP_EN BIT(12) -#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */ -#define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */ +#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */ +#define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */ /* USB3_CORE_STATUS */ #define MDCTRL_CLK_STATUS BIT(15) @@ -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) @@ -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; @@ -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; @@ -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", }, {}, @@ -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); diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 5c1586ec7824..a0f73d4711ae 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -280,6 +280,10 @@ int cdns3_hw_role_switch(struct cdns3 *cdns) enum usb_role real_role, current_role; int ret = 0; + /* Depends on role switch class */ + if (cdns->role_sw) + return 0; + pm_runtime_get_sync(cdns->dev); current_role = cdns->role; @@ -371,6 +375,50 @@ pm_put: return ret; } +static int set_phy_power_on(struct cdns3 *cdns) +{ + int ret; + + ret = phy_power_on(cdns->usb2_phy); + if (ret) + return ret; + + ret = phy_power_on(cdns->usb3_phy); + if (ret) + phy_power_off(cdns->usb2_phy); + + return ret; +} + +static void set_phy_power_off(struct cdns3 *cdns) +{ + phy_power_off(cdns->usb3_phy); + phy_power_off(cdns->usb2_phy); +} + +/** + * cdns3_wakeup_irq - interrupt handler for wakeup events + * @irq: irq number for cdns3 core device + * @data: structure of cdns3 + * + * Returns IRQ_HANDLED or IRQ_NONE + */ +static irqreturn_t cdns3_wakeup_irq(int irq, void *data) +{ + struct cdns3 *cdns = data; + + if (cdns->in_lpm) { + disable_irq_nosync(irq); + cdns->wakeup_pending = true; + if ((cdns->role == USB_ROLE_HOST) && cdns->host_dev) + pm_request_resume(&cdns->host_dev->dev); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + /** * cdns3_probe - probe for cdns3 core device * @pdev: Pointer to cdns3 core platform device @@ -397,6 +445,7 @@ static int cdns3_probe(struct platform_device *pdev) return -ENOMEM; cdns->dev = dev; + cdns->pdata = dev_get_platdata(dev); platform_set_drvdata(pdev, cdns); @@ -443,8 +492,21 @@ static int cdns3_probe(struct platform_device *pdev) return -ENXIO; } + cdns->phyrst_a_enable = device_property_read_bool(dev, "cdns,phyrst-a-enable"); + cdns->otg_res = *res; + cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup"); + if (cdns->wakeup_irq == -EPROBE_DEFER) + return cdns->wakeup_irq; + else if (cdns->wakeup_irq == 0) + return -EINVAL; + + if (cdns->wakeup_irq < 0) { + dev_dbg(dev, "couldn't get wakeup irq\n"); + cdns->wakeup_irq = 0x0; + } + mutex_init(&cdns->mutex); cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy"); @@ -463,14 +525,10 @@ static int cdns3_probe(struct platform_device *pdev) if (ret) goto err1; - ret = phy_power_on(cdns->usb2_phy); + ret = set_phy_power_on(cdns); if (ret) goto err2; - ret = phy_power_on(cdns->usb3_phy); - if (ret) - goto err3; - sw_desc.set = cdns3_role_set; sw_desc.get = cdns3_role_get; sw_desc.allow_userspace_control = true; @@ -482,20 +540,34 @@ static int cdns3_probe(struct platform_device *pdev) if (IS_ERR(cdns->role_sw)) { ret = PTR_ERR(cdns->role_sw); dev_warn(dev, "Unable to register Role Switch\n"); - goto err4; + goto err3; + } + + if (cdns->wakeup_irq) { + ret = devm_request_irq(cdns->dev, cdns->wakeup_irq, + cdns3_wakeup_irq, + IRQF_SHARED, + dev_name(cdns->dev), cdns); + + if (ret) { + dev_err(cdns->dev, "couldn't register wakeup irq handler\n"); + goto err3; + } } ret = cdns3_drd_init(cdns); if (ret) - goto err5; + goto err4; ret = cdns3_core_init_role(cdns); if (ret) - goto err5; + goto err4; + spin_lock_init(&cdns->lock); device_set_wakeup_capable(dev, true); pm_runtime_set_active(dev); pm_runtime_enable(dev); + pm_runtime_forbid(dev); /* * The controller needs less time between bus and controller suspend, @@ -508,14 +580,11 @@ static int cdns3_probe(struct platform_device *pdev) dev_dbg(dev, "Cadence USB3 core: probe succeed\n"); return 0; -err5: +err4: cdns3_drd_exit(cdns); usb_role_switch_unregister(cdns->role_sw); -err4: - phy_power_off(cdns->usb3_phy); - err3: - phy_power_off(cdns->usb2_phy); + set_phy_power_off(cdns); err2: phy_exit(cdns->usb3_phy); err1: @@ -539,59 +608,128 @@ static int cdns3_remove(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); cdns3_exit_roles(cdns); usb_role_switch_unregister(cdns->role_sw); - phy_power_off(cdns->usb2_phy); - phy_power_off(cdns->usb3_phy); + set_phy_power_off(cdns); phy_exit(cdns->usb2_phy); phy_exit(cdns->usb3_phy); return 0; } -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM -static int cdns3_suspend(struct device *dev) +static int cdns3_set_platform_suspend(struct device *dev, + bool suspend, bool wakeup) +{ + struct cdns3 *cdns = dev_get_drvdata(dev); + int ret = 0; + + if (cdns->pdata && cdns->pdata->platform_suspend) + ret = cdns->pdata->platform_suspend(dev, suspend, wakeup); + + return ret; +} + +static int cdns3_controller_suspend(struct device *dev, pm_message_t msg) { struct cdns3 *cdns = dev_get_drvdata(dev); + bool wakeup; unsigned long flags; - if (cdns->role == USB_ROLE_HOST) + if (cdns->in_lpm) return 0; - if (pm_runtime_status_suspended(dev)) - pm_runtime_resume(dev); + if (PMSG_IS_AUTO(msg)) + wakeup = true; + else + wakeup = device_may_wakeup(dev); - if (cdns->roles[cdns->role]->suspend) { - spin_lock_irqsave(&cdns->gadget_dev->lock, flags); - cdns->roles[cdns->role]->suspend(cdns, false); - spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags); - } + cdns3_set_platform_suspend(cdns->dev, true, wakeup); + set_phy_power_off(cdns); + spin_lock_irqsave(&cdns->lock, flags); + cdns->in_lpm = true; + spin_unlock_irqrestore(&cdns->lock, flags); + dev_dbg(cdns->dev, "%s ends\n", __func__); return 0; } -static int cdns3_resume(struct device *dev) +static int cdns3_controller_resume(struct device *dev, pm_message_t msg) { struct cdns3 *cdns = dev_get_drvdata(dev); + int ret; unsigned long flags; - if (cdns->role == USB_ROLE_HOST) + if (!cdns->in_lpm) return 0; - if (cdns->roles[cdns->role]->resume) { - spin_lock_irqsave(&cdns->gadget_dev->lock, flags); + ret = set_phy_power_on(cdns); + if (ret) + return ret; + + cdns3_set_platform_suspend(cdns->dev, false, false); + + spin_lock_irqsave(&cdns->lock, flags); + if (cdns->roles[cdns->role]->resume && !PMSG_IS_AUTO(msg)) cdns->roles[cdns->role]->resume(cdns, false); - spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags); + + cdns->in_lpm = false; + spin_unlock_irqrestore(&cdns->lock, flags); + if (cdns->wakeup_pending) { + cdns->wakeup_pending = false; + enable_irq(cdns->wakeup_irq); + } + dev_dbg(cdns->dev, "%s ends\n", __func__); + + return ret; +} + +static int cdns3_runtime_suspend(struct device *dev) +{ + return cdns3_controller_suspend(dev, PMSG_AUTO_SUSPEND); +} + +static int cdns3_runtime_resume(struct device *dev) +{ + return cdns3_controller_resume(dev, PMSG_AUTO_RESUME); +} +#ifdef CONFIG_PM_SLEEP + +static int cdns3_suspend(struct device *dev) +{ + struct cdns3 *cdns = dev_get_drvdata(dev); + unsigned long flags; + + if (pm_runtime_status_suspended(dev)) + pm_runtime_resume(dev); + + if (cdns->roles[cdns->role]->suspend) { + spin_lock_irqsave(&cdns->lock, flags); + cdns->roles[cdns->role]->suspend(cdns, false); + spin_unlock_irqrestore(&cdns->lock, flags); } + return cdns3_controller_suspend(dev, PMSG_SUSPEND); +} + +static int cdns3_resume(struct device *dev) +{ + int ret; + + ret = cdns3_controller_resume(dev, PMSG_RESUME); + if (ret) + return ret; + pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); - return 0; + return ret; } -#endif +#endif /* CONFIG_PM_SLEEP */ +#endif /* CONFIG_PM */ static const struct dev_pm_ops cdns3_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(cdns3_suspend, cdns3_resume) + SET_RUNTIME_PM_OPS(cdns3_runtime_suspend, cdns3_runtime_resume, NULL) }; #ifdef CONFIG_OF diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h index 1ad1f1fe61e9..8a40d53d5ede 100644 --- a/drivers/usb/cdns3/core.h +++ b/drivers/usb/cdns3/core.h @@ -38,6 +38,12 @@ struct cdns3_role_driver { }; #define CDNS3_XHCI_RESOURCES_NUM 2 + +struct cdns3_platform_data { + int (*platform_suspend)(struct device *dev, + bool suspend, bool wakeup); +}; + /** * struct cdns3 - Representation of Cadence USB3 DRD controller. * @dev: pointer to Cadence device struct @@ -50,6 +56,7 @@ struct cdns3_role_driver { * @otg_regs: pointer to base of otg registers * @otg_irq: irq number for otg controller * @dev_irq: irq number for device controller + * @wakeup_irq: irq number for wakeup event, it is optional * @roles: array of supported roles for this controller * @role: current role * @host_dev: the child host device pointer for cdns3 core @@ -62,6 +69,10 @@ struct cdns3_role_driver { * This field based on firmware setting, kernel configuration * and hardware configuration. * @role_sw: pointer to role switch object. + * @in_lpm: indicate the controller is in low power mode + * @wakeup_pending: wakeup interrupt pending + * @pdata: platform data from glue layer + * @lock: spinlock structure */ struct cdns3 { struct device *dev; @@ -76,9 +87,11 @@ struct cdns3 { #define CDNS3_CONTROLLER_V0 0 #define CDNS3_CONTROLLER_V1 1 u32 version; + bool phyrst_a_enable; int otg_irq; int dev_irq; + int wakeup_irq; struct cdns3_role_driver *roles[USB_ROLE_DEVICE + 1]; enum usb_role role; struct platform_device *host_dev; @@ -89,6 +102,10 @@ struct cdns3 { struct mutex mutex; enum usb_dr_mode dr_mode; struct usb_role_switch *role_sw; + bool in_lpm; + bool wakeup_pending; + struct cdns3_platform_data *pdata; + spinlock_t lock; }; int cdns3_hw_role_switch(struct cdns3 *cdns); diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index 6234bcd6158a..38ccd29e4cde 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -15,6 +15,7 @@ #include <linux/delay.h> #include <linux/iopoll.h> #include <linux/usb/otg.h> +#include <linux/phy/phy.h> #include "gadget.h" #include "drd.h" @@ -42,6 +43,18 @@ int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode) reg = readl(&cdns->otg_v1_regs->override); reg |= OVERRIDE_IDPULLUP; writel(reg, &cdns->otg_v1_regs->override); + + /* + * Enable work around feature built into the + * controller to address issue with RX Sensitivity + * est (EL_17) for USB2 PHY. The issue only occures + * for 0x0002450D controller version. + */ + if (cdns->phyrst_a_enable) { + reg = readl(&cdns->otg_v1_regs->phyrst_cfg); + reg |= PHYRST_CFG_PHYRST_A_ENABLE; + writel(reg, &cdns->otg_v1_regs->phyrst_cfg); + } } else { reg = readl(&cdns->otg_v0_regs->ctrl1); reg |= OVERRIDE_IDPULLUP_V0; @@ -145,6 +158,7 @@ int cdns3_drd_host_on(struct cdns3 *cdns) if (ret) dev_err(cdns->dev, "timeout waiting for xhci_ready\n"); + phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_HOST); return ret; } @@ -164,6 +178,7 @@ void cdns3_drd_host_off(struct cdns3 *cdns) readl_poll_timeout_atomic(&cdns->otg_regs->state, val, !(val & OTGSTATE_HOST_STATE_MASK), 1, 2000000); + phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID); } /** @@ -190,6 +205,7 @@ int cdns3_drd_gadget_on(struct cdns3 *cdns) return ret; } + phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE); return 0; } @@ -213,6 +229,7 @@ void cdns3_drd_gadget_off(struct cdns3 *cdns) readl_poll_timeout_atomic(&cdns->otg_regs->state, val, !(val & OTGSTATE_DEV_STATE_MASK), 1, 2000000); + phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID); } /** @@ -293,6 +310,9 @@ static irqreturn_t cdns3_drd_irq(int irq, void *data) if (cdns->dr_mode != USB_DR_MODE_OTG) return IRQ_NONE; + if (cdns->in_lpm) + return ret; + reg = readl(&cdns->otg_regs->ivect); if (!reg) diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h index 7e7cf7fa2dd3..f1ccae285a16 100644 --- a/drivers/usb/cdns3/drd.h +++ b/drivers/usb/cdns3/drd.h @@ -31,7 +31,7 @@ struct cdns3_otg_regs { __le32 simulate; __le32 override; __le32 susp_ctrl; - __le32 reserved4; + __le32 phyrst_cfg; __le32 anasts; __le32 adp_ramp_time; __le32 ctrl1; @@ -153,6 +153,9 @@ struct cdns3_otg_common_regs { /* Only for CDNS3_CONTROLLER_V0 version */ #define OVERRIDE_IDPULLUP_V0 BIT(24) +/* PHYRST_CFG - bitmasks */ +#define PHYRST_CFG_PHYRST_A_ENABLE BIT(0) + #define CDNS3_ID_PERIPHERAL 1 #define CDNS3_ID_HOST 0 diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c index d9779abc65b2..4761c852d9c4 100644 --- a/drivers/usb/cdns3/ep0.c +++ b/drivers/usb/cdns3/ep0.c @@ -717,9 +717,17 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep, /* send STATUS stage. Should be called only for SET_CONFIGURATION */ if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) { + u32 val; + cdns3_select_ep(priv_dev, 0x00); cdns3_set_hw_configuration(priv_dev); cdns3_ep0_complete_setup(priv_dev, 0, 1); + /* wait until configuration set */ + ret = readl_poll_timeout_atomic(&priv_dev->regs->usb_sts, val, + val & USB_STS_CFGSTS_MASK, 1, 100); + if (ret == -ETIMEDOUT) + dev_warn(priv_dev->dev, "timeout for waiting configuration set\n"); + request->actual = 0; priv_dev->status_completion_no_call = true; priv_dev->pending_status_request = request; @@ -731,7 +739,7 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep, * ep0_queue is back. */ queue_work(system_freezable_wq, &priv_dev->pending_status_wq); - return 0; + return ret; } if (!list_empty(&priv_ep->pending_req_list)) { diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index dea649ee173b..6e7b70a2e352 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -261,8 +261,8 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep) */ link_trb->control = 0; } else { - link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma); - link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE; + link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma)); + link_trb->control = cpu_to_le32(TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE); } return 0; } @@ -462,6 +462,36 @@ static int cdns3_start_all_request(struct cdns3_device *priv_dev, (reg) |= EP_STS_EN_DESCMISEN; \ } } while (0) +static void __cdns3_descmiss_copy_data(struct usb_request *request, + struct usb_request *descmiss_req) +{ + int length = request->actual + descmiss_req->actual; + struct scatterlist *s = request->sg; + + if (!s) { + if (length <= request->length) { + memcpy(&((u8 *)request->buf)[request->actual], + descmiss_req->buf, + descmiss_req->actual); + request->actual = length; + } else { + /* It should never occures */ + request->status = -ENOMEM; + } + } else { + if (length <= sg_dma_len(s)) { + void *p = phys_to_virt(sg_dma_address(s)); + + memcpy(&((u8 *)p)[request->actual], + descmiss_req->buf, + descmiss_req->actual); + request->actual = length; + } else { + request->status = -ENOMEM; + } + } +} + /** * cdns3_wa2_descmiss_copy_data copy data from internal requests to * request queued by class driver. @@ -488,21 +518,9 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep, chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH; length = request->actual + descmiss_req->actual; - request->status = descmiss_req->status; - - if (length <= request->length) { - memcpy(&((u8 *)request->buf)[request->actual], - descmiss_req->buf, - descmiss_req->actual); - request->actual = length; - } else { - /* It should never occures */ - request->status = -ENOMEM; - } - + __cdns3_descmiss_copy_data(request, descmiss_req); list_del_init(&descmiss_priv_req->list); - kfree(descmiss_req->buf); cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req); --priv_ep->wa2_counter; @@ -817,6 +835,8 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep, request->length); priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED); + /* All TRBs have finished, clear the counter */ + priv_req->finished_trb = 0; trace_cdns3_gadget_giveback(priv_req); if (priv_dev->dev_ver < DEV_VER_V2) { @@ -847,10 +867,10 @@ static void cdns3_wa1_restore_cycle_bit(struct cdns3_endpoint *priv_ep) priv_ep->wa1_trb_index = 0xFFFF; if (priv_ep->wa1_cycle_bit) { priv_ep->wa1_trb->control = - priv_ep->wa1_trb->control | 0x1; + priv_ep->wa1_trb->control | cpu_to_le32(0x1); } else { priv_ep->wa1_trb->control = - priv_ep->wa1_trb->control & ~0x1; + priv_ep->wa1_trb->control & cpu_to_le32(~0x1); } } } @@ -1008,17 +1028,16 @@ static int cdns3_ep_run_stream_transfer(struct cdns3_endpoint *priv_ep, TRB_STREAM_ID(priv_req->request.stream_id) | TRB_ISP; if (!request->num_sgs) { - trb->buffer = TRB_BUFFER(trb_dma); + trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma)); length = request->length; } else { - trb->buffer = TRB_BUFFER(request->sg[sg_idx].dma_address); + trb->buffer = cpu_to_le32(TRB_BUFFER(request->sg[sg_idx].dma_address)); length = request->sg[sg_idx].length; } tdl = DIV_ROUND_UP(length, priv_ep->endpoint.maxpacket); - trb->length = TRB_BURST_LEN(16 /*priv_ep->trb_burst_size*/) | - TRB_LEN(length); + trb->length = cpu_to_le32(TRB_BURST_LEN(16) | TRB_LEN(length)); /* * For DEV_VER_V2 controller version we have enabled @@ -1027,11 +1046,11 @@ static int cdns3_ep_run_stream_transfer(struct cdns3_endpoint *priv_ep, */ if (priv_dev->dev_ver >= DEV_VER_V2) { if (priv_dev->gadget.speed == USB_SPEED_SUPER) - trb->length |= TRB_TDL_SS_SIZE(tdl); + trb->length |= cpu_to_le32(TRB_TDL_SS_SIZE(tdl)); } priv_req->flags |= REQUEST_PENDING; - trb->control = control; + trb->control = cpu_to_le32(control); trace_cdns3_prepare_trb(priv_ep, priv_req->trb); @@ -1091,6 +1110,7 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, struct cdns3_device *priv_dev = priv_ep->cdns3_dev; struct cdns3_request *priv_req; struct cdns3_trb *trb; + struct cdns3_trb *link_trb; dma_addr_t trb_dma; u32 togle_pcs = 1; int sg_iter = 0; @@ -1099,11 +1119,13 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, u32 control; int pcs; u16 total_tdl = 0; + struct scatterlist *s = NULL; + bool sg_supported = !!(request->num_mapped_sgs); if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) num_trb = priv_ep->interval; else - num_trb = request->num_sgs ? request->num_sgs : 1; + num_trb = sg_supported ? request->num_mapped_sgs : 1; if (num_trb > priv_ep->free_trbs) { priv_ep->flags |= EP_RING_FULL; @@ -1129,7 +1151,6 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, /* prepare ring */ if ((priv_ep->enqueue + num_trb) >= (priv_ep->num_trbs - 1)) { - struct cdns3_trb *link_trb; int doorbell, dma_index; u32 ch_bit = 0; @@ -1156,13 +1177,16 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, TRBS_PER_SEGMENT > 2) ch_bit = TRB_CHAIN; - link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) | - TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit; + link_trb->control = cpu_to_le32(((priv_ep->pcs) ? TRB_CYCLE : 0) | + TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit); } if (priv_dev->dev_ver <= DEV_VER_V2) togle_pcs = cdns3_wa1_update_guard(priv_ep, trb); + if (sg_supported) + s = request->sg; + /* set incorrect Cycle Bit for first trb*/ control = priv_ep->pcs ? 0 : TRB_CYCLE; @@ -1172,13 +1196,13 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, /* fill TRB */ control |= TRB_TYPE(TRB_NORMAL); - trb->buffer = TRB_BUFFER(request->num_sgs == 0 - ? trb_dma : request->sg[sg_iter].dma_address); - - if (likely(!request->num_sgs)) + if (sg_supported) { + trb->buffer = cpu_to_le32(TRB_BUFFER(sg_dma_address(s))); + length = sg_dma_len(s); + } else { + trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma)); length = request->length; - else - length = request->sg[sg_iter].length; + } if (likely(priv_dev->dev_ver >= DEV_VER_V2)) td_size = DIV_ROUND_UP(length, @@ -1187,10 +1211,10 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, total_tdl += DIV_ROUND_UP(length, priv_ep->endpoint.maxpacket); - trb->length = TRB_BURST_LEN(priv_ep->trb_burst_size) | - TRB_LEN(length); + trb->length = cpu_to_le32(TRB_BURST_LEN(priv_ep->trb_burst_size) | + TRB_LEN(length)); if (priv_dev->gadget.speed == USB_SPEED_SUPER) - trb->length |= TRB_TDL_SS_SIZE(td_size); + trb->length |= cpu_to_le32(TRB_TDL_SS_SIZE(td_size)); else control |= TRB_TDL_HS_SIZE(td_size); @@ -1212,9 +1236,18 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, } if (sg_iter) - trb->control = control; + trb->control = cpu_to_le32(control); else - priv_req->trb->control = control; + priv_req->trb->control = cpu_to_le32(control); + + if (sg_supported) { + trb->control |= TRB_ISP; + /* Don't set chain bit for last TRB */ + if (sg_iter < num_trb - 1) + trb->control |= TRB_CHAIN; + + s = sg_next(s); + } control = 0; ++sg_iter; @@ -1226,9 +1259,10 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, trb = priv_req->trb; priv_req->flags |= REQUEST_PENDING; + priv_req->num_of_trb = num_trb; if (sg_iter == 1) - trb->control |= TRB_IOC | TRB_ISP; + trb->control |= cpu_to_le32(TRB_IOC | TRB_ISP); if (priv_dev->dev_ver < DEV_VER_V2 && (priv_ep->flags & EP_TDLCHK_EN)) { @@ -1254,12 +1288,27 @@ static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep, /* give the TD to the consumer*/ if (togle_pcs) - trb->control = trb->control ^ 1; + trb->control = trb->control ^ cpu_to_le32(1); if (priv_dev->dev_ver <= DEV_VER_V2) cdns3_wa1_tray_restore_cycle_bit(priv_dev, priv_ep); - trace_cdns3_prepare_trb(priv_ep, priv_req->trb); + if (num_trb > 1) { + int i = 0; + + while (i < num_trb) { + trace_cdns3_prepare_trb(priv_ep, trb + i); + if (trb + i == link_trb) { + trb = priv_ep->trb_pool; + num_trb = num_trb - i; + i = 0; + } else { + i++; + } + } + } else { + trace_cdns3_prepare_trb(priv_ep, priv_req->trb); + } /* * Memory barrier - Cycle Bit must be set before trb->length and @@ -1310,7 +1359,6 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) { struct cdns3_endpoint *priv_ep; struct usb_ep *ep; - int val; if (priv_dev->hw_configured_flag) return; @@ -1320,10 +1368,6 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) cdns3_set_register_bit(&priv_dev->regs->usb_conf, USB_CONF_U1EN | USB_CONF_U2EN); - /* wait until configuration set */ - readl_poll_timeout_atomic(&priv_dev->regs->usb_sts, val, - val & USB_STS_CFGSTS_MASK, 1, 100); - priv_dev->hw_configured_flag = 1; list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) { @@ -1337,7 +1381,7 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) } /** - * cdns3_request_handled - check whether request has been handled by DMA + * cdns3_trb_handled - check whether trb has been handled by DMA * * @priv_ep: extended endpoint object. * @priv_req: request object for checking @@ -1354,32 +1398,28 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) * ET = priv_req->end_trb - index of last TRB in transfer ring * CI = current_index - index of processed TRB by DMA. * - * As first step, function checks if cycle bit for priv_req->start_trb is - * correct. + * As first step, we check if the TRB between the ST and ET. + * Then, we check if cycle bit for index priv_ep->dequeue + * is correct. * * some rules: - * 1. priv_ep->dequeue never exceed current_index. + * 1. priv_ep->dequeue never equals to current_index. * 2 priv_ep->enqueue never exceed priv_ep->dequeue * 3. exception: priv_ep->enqueue == priv_ep->dequeue * and priv_ep->free_trbs is zero. * This case indicate that TR is full. * - * Then We can split recognition into two parts: + * At below two cases, the request have been handled. * Case 1 - priv_ep->dequeue < current_index * SR ... EQ ... DQ ... CI ... ER * SR ... DQ ... CI ... EQ ... ER * - * Request has been handled by DMA if ST and ET is between DQ and CI. - * * Case 2 - priv_ep->dequeue > current_index - * This situation take place when CI go through the LINK TRB at the end of + * This situation takes place when CI go through the LINK TRB at the end of * transfer ring. * SR ... CI ... EQ ... DQ ... ER - * - * Request has been handled by DMA if ET is less then CI or - * ET is greater or equal DQ. */ -static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep, +static bool cdns3_trb_handled(struct cdns3_endpoint *priv_ep, struct cdns3_request *priv_req) { struct cdns3_device *priv_dev = priv_ep->cdns3_dev; @@ -1391,9 +1431,27 @@ static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep, current_index = cdns3_get_dma_pos(priv_dev, priv_ep); doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY); - trb = &priv_ep->trb_pool[priv_req->start_trb]; + /* current trb doesn't belong to this request */ + if (priv_req->start_trb < priv_req->end_trb) { + if (priv_ep->dequeue > priv_req->end_trb) + goto finish; + + if (priv_ep->dequeue < priv_req->start_trb) + goto finish; + } + + if ((priv_req->start_trb > priv_req->end_trb) && + (priv_ep->dequeue > priv_req->end_trb) && + (priv_ep->dequeue < priv_req->start_trb)) + goto finish; - if ((trb->control & TRB_CYCLE) != priv_ep->ccs) + if ((priv_req->start_trb == priv_req->end_trb) && + (priv_ep->dequeue != priv_req->end_trb)) + goto finish; + + trb = &priv_ep->trb_pool[priv_ep->dequeue]; + + if ((le32_to_cpu(trb->control) & TRB_CYCLE) != priv_ep->ccs) goto finish; if (doorbell == 1 && current_index == priv_ep->dequeue) @@ -1413,12 +1471,8 @@ static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep, !priv_ep->dequeue) goto finish; - if (priv_req->end_trb >= priv_ep->dequeue && - priv_req->end_trb < current_index) - handled = 1; + handled = 1; } else if (priv_ep->dequeue > current_index) { - if (priv_req->end_trb < current_index || - priv_req->end_trb >= priv_ep->dequeue) handled = 1; } @@ -1434,6 +1488,8 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev, struct cdns3_request *priv_req; struct usb_request *request; struct cdns3_trb *trb; + bool request_handled = false; + bool transfer_end = false; while (!list_empty(&priv_ep->pending_req_list)) { request = cdns3_next_request(&priv_ep->pending_req_list); @@ -1442,7 +1498,7 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev, trb = priv_ep->trb_pool + priv_ep->dequeue; /* Request was dequeued and TRB was changed to TRB_LINK. */ - if (TRB_FIELD_TO_TYPE(trb->control) == TRB_LINK) { + if (TRB_FIELD_TO_TYPE(le32_to_cpu(trb->control)) == TRB_LINK) { trace_cdns3_complete_trb(priv_ep, trb); cdns3_move_deq_to_next_trb(priv_req); } @@ -1453,20 +1509,32 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev, */ cdns3_select_ep(priv_dev, priv_ep->endpoint.address); - if (!cdns3_request_handled(priv_ep, priv_req)) - goto prepare_next_td; + while (cdns3_trb_handled(priv_ep, priv_req)) { + priv_req->finished_trb++; + if (priv_req->finished_trb >= priv_req->num_of_trb) + request_handled = true; - trb = priv_ep->trb_pool + priv_ep->dequeue; - trace_cdns3_complete_trb(priv_ep, trb); + trb = priv_ep->trb_pool + priv_ep->dequeue; + trace_cdns3_complete_trb(priv_ep, trb); - if (trb != priv_req->trb) - dev_warn(priv_dev->dev, - "request_trb=0x%p, queue_trb=0x%p\n", - priv_req->trb, trb); + if (!transfer_end) + request->actual += + TRB_LEN(le32_to_cpu(trb->length)); - request->actual = TRB_LEN(le32_to_cpu(trb->length)); - cdns3_move_deq_to_next_trb(priv_req); - cdns3_gadget_giveback(priv_ep, priv_req, 0); + if (priv_req->num_of_trb > 1 && + le32_to_cpu(trb->control) & TRB_SMM) + transfer_end = true; + + cdns3_ep_inc_deq(priv_ep); + } + + if (request_handled) { + cdns3_gadget_giveback(priv_ep, priv_req, 0); + request_handled = false; + transfer_end = false; + } else { + goto prepare_next_td; + } if (priv_ep->type != USB_ENDPOINT_XFER_ISOC && TRBS_PER_SEGMENT == 2) @@ -1574,7 +1642,7 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep) * that host ignore the ERDY packet and driver has to send it * again. */ - if (tdl && (dbusy | !EP_STS_BUFFEMPTY(ep_sts_reg) | + if (tdl && (dbusy || !EP_STS_BUFFEMPTY(ep_sts_reg) || EP_STS_HOSTPP(ep_sts_reg))) { writel(EP_CMD_ERDY | EP_CMD_ERDY_SID(priv_ep->last_stream_id), @@ -1769,9 +1837,13 @@ static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev, static irqreturn_t cdns3_device_irq_handler(int irq, void *data) { struct cdns3_device *priv_dev = data; + struct cdns3 *cdns = dev_get_drvdata(priv_dev->dev); irqreturn_t ret = IRQ_NONE; u32 reg; + if (cdns->in_lpm) + return ret; + /* check USB device interrupt */ reg = readl(&priv_dev->regs->usb_ists); if (reg) { @@ -2552,10 +2624,10 @@ found: /* Update ring only if removed request is on pending_req_list list */ if (req_on_hw_ring && link_trb) { - link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma + - ((priv_req->end_trb + 1) * TRB_SIZE)); - link_trb->control = (link_trb->control & TRB_CYCLE) | - TRB_TYPE(TRB_LINK) | TRB_CHAIN; + link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma + + ((priv_req->end_trb + 1) * TRB_SIZE))); + link_trb->control = cpu_to_le32((le32_to_cpu(link_trb->control) & TRB_CYCLE) | + TRB_TYPE(TRB_LINK) | TRB_CHAIN); if (priv_ep->wa1_trb == priv_req->trb) cdns3_wa1_restore_cycle_bit(priv_ep); @@ -2610,7 +2682,7 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep) priv_req = to_cdns3_request(request); trb = priv_req->trb; if (trb) - trb->control = trb->control ^ TRB_CYCLE; + trb->control = trb->control ^ cpu_to_le32(TRB_CYCLE); } writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd); @@ -2625,7 +2697,8 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep) if (request) { if (trb) - trb->control = trb->control ^ TRB_CYCLE; + trb->control = trb->control ^ cpu_to_le32(TRB_CYCLE); + cdns3_rearm_transfer(priv_ep, 1); } @@ -2735,10 +2808,13 @@ static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on) { struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget); - if (is_on) + if (is_on) { writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf); - else + } else { + writel(~0, &priv_dev->regs->ep_ists); + writel(~0, &priv_dev->regs->usb_ists); writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf); + } return 0; } @@ -2779,6 +2855,8 @@ static void cdns3_gadget_config(struct cdns3_device *priv_dev) /* enable generic interrupt*/ writel(USB_IEN_INIT, ®s->usb_ien); writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, ®s->usb_conf); + /* keep Fast Access bit */ + writel(PUSB_PWR_FST_REG_ACCESS, &priv_dev->regs->usb_pwr); cdns3_configure_dmult(priv_dev, NULL); } @@ -2862,6 +2940,7 @@ static int cdns3_gadget_udc_stop(struct usb_gadget *gadget) /* disable interrupt for device */ writel(0, &priv_dev->regs->usb_ien); + writel(0, &priv_dev->regs->usb_pwr); writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf); return 0; @@ -2984,18 +3063,26 @@ err: return -ENOMEM; } +static void cdns3_gadget_release(struct device *dev) +{ + struct cdns3_device *priv_dev = container_of(dev, + struct cdns3_device, gadget.dev); + + kfree(priv_dev); +} + void cdns3_gadget_exit(struct cdns3 *cdns) { struct cdns3_device *priv_dev; priv_dev = cdns->gadget_dev; - devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev); pm_runtime_mark_last_busy(cdns->dev); pm_runtime_put_autosuspend(cdns->dev); - usb_del_gadget_udc(&priv_dev->gadget); + usb_del_gadget(&priv_dev->gadget); + devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev); cdns3_free_all_eps(priv_dev); @@ -3015,7 +3102,7 @@ void cdns3_gadget_exit(struct cdns3 *cdns) priv_dev->setup_dma); kfree(priv_dev->zlp_buf); - kfree(priv_dev); + usb_put_gadget(&priv_dev->gadget); cdns->gadget_dev = NULL; cdns3_drd_gadget_off(cdns); } @@ -3030,6 +3117,8 @@ static int cdns3_gadget_start(struct cdns3 *cdns) if (!priv_dev) return -ENOMEM; + usb_initialize_gadget(cdns->dev, &priv_dev->gadget, + cdns3_gadget_release); cdns->gadget_dev = priv_dev; priv_dev->sysdev = cdns->dev; priv_dev->dev = cdns->dev; @@ -3070,7 +3159,6 @@ static int cdns3_gadget_start(struct cdns3 *cdns) priv_dev->gadget.speed = USB_SPEED_UNKNOWN; priv_dev->gadget.ops = &cdns3_gadget_ops; priv_dev->gadget.name = "usb-ss-gadget"; - priv_dev->gadget.sg_supported = 1; priv_dev->gadget.quirk_avoids_skb_reserve = 1; priv_dev->gadget.irq = cdns->dev_irq; @@ -3109,6 +3197,8 @@ static int cdns3_gadget_start(struct cdns3 *cdns) readl(&priv_dev->regs->usb_cap2)); priv_dev->dev_ver = GET_DEV_BASE_VERSION(priv_dev->dev_ver); + if (priv_dev->dev_ver >= DEV_VER_V2) + priv_dev->gadget.sg_supported = 1; priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL); if (!priv_dev->zlp_buf) { @@ -3117,10 +3207,9 @@ static int cdns3_gadget_start(struct cdns3 *cdns) } /* add USB gadget device */ - ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget); + ret = usb_add_gadget(&priv_dev->gadget); if (ret < 0) { - dev_err(priv_dev->dev, - "Failed to register USB device controller\n"); + dev_err(priv_dev->dev, "Failed to add gadget\n"); goto err4; } @@ -3133,6 +3222,7 @@ err3: err2: cdns3_free_all_eps(priv_dev); err1: + usb_put_gadget(&priv_dev->gadget); cdns->gadget_dev = NULL; return ret; } diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h index 52765b098b9e..1ccecd237530 100644 --- a/drivers/usb/cdns3/gadget.h +++ b/drivers/usb/cdns3/gadget.h @@ -966,7 +966,7 @@ struct cdns3_usb_regs { /* * USBSS-DEV DMA interface. */ -#define TRBS_PER_SEGMENT 40 +#define TRBS_PER_SEGMENT 600 #define ISO_MAX_INTERVAL 10 @@ -1030,6 +1030,11 @@ struct cdns3_trb { * When set to '1', the device will toggle its interpretation of the Cycle bit */ #define TRB_TOGGLE BIT(1) +/* + * The controller will set it if OUTSMM (OUT size mismatch) is detected, + * this bit is for normal TRB + */ +#define TRB_SMM BIT(1) /* * Short Packet (SP). OUT EPs at DMULT=1 only. Indicates if the TRB was @@ -1215,6 +1220,8 @@ struct cdns3_aligned_buf { * this endpoint * @flags: flag specifying special usage of request * @list: used by internally allocated request to add to wa2_descmiss_req_list. + * @finished_trb: number of trb has already finished per request + * @num_of_trb: how many trbs in this request */ struct cdns3_request { struct usb_request request; @@ -1230,6 +1237,8 @@ struct cdns3_request { #define REQUEST_UNALIGNED BIT(4) u32 flags; struct list_head list; + int finished_trb; + int num_of_trb; }; #define to_cdns3_request(r) (container_of(r, struct cdns3_request, request)) diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index 36c63d9ecd37..b3e2cb69762c 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -13,11 +13,13 @@ #include "core.h" #include "drd.h" #include "host-export.h" +#include <linux/usb/hcd.h> static int __cdns3_host_init(struct cdns3 *cdns) { struct platform_device *xhci; int ret; + struct usb_hcd *hcd; cdns3_drd_host_on(cdns); @@ -43,6 +45,11 @@ static int __cdns3_host_init(struct cdns3 *cdns) goto err1; } + /* Glue needs to access xHCI region register for Power management */ + hcd = platform_get_drvdata(xhci); + if (hcd) + cdns->xhci_regs = hcd->regs; + return 0; err1: platform_device_put(xhci); |