diff options
Diffstat (limited to 'drivers/usb/chipidea')
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_imx.c | 81 | ||||
-rw-r--r-- | drivers/usb/chipidea/host.c | 13 | ||||
-rw-r--r-- | drivers/usb/chipidea/otg_fsm.c | 3 | ||||
-rw-r--r-- | drivers/usb/chipidea/udc.c | 7 | ||||
-rw-r--r-- | drivers/usb/chipidea/usbmisc_imx.c | 87 |
5 files changed, 168 insertions, 23 deletions
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 1a7fc638213e..780f4d151345 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -6,6 +6,7 @@ */ #include <linux/module.h> +#include <linux/irq.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> @@ -98,6 +99,7 @@ struct ci_hdrc_imx_data { struct clk *clk; struct clk *clk_wakeup; struct imx_usbmisc_data *usbmisc_data; + int wakeup_irq; bool supports_runtime_pm; bool override_phy_control; bool in_lpm; @@ -336,6 +338,23 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event) return ret; } +static irqreturn_t ci_wakeup_irq_handler(int irq, void *data) +{ + struct ci_hdrc_imx_data *imx_data = data; + + disable_irq_nosync(irq); + pm_runtime_resume(&imx_data->ci_pdev->dev); + + return IRQ_HANDLED; +} + +static void ci_hdrc_imx_disable_regulator(void *arg) +{ + struct ci_hdrc_imx_data *data = arg; + + regulator_disable(data->hsic_pad_regulator); +} + static int ci_hdrc_imx_probe(struct platform_device *pdev) { struct ci_hdrc_imx_data *data; @@ -394,6 +413,13 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) "Failed to enable HSIC pad regulator\n"); goto err_put; } + ret = devm_add_action_or_reset(dev, + ci_hdrc_imx_disable_regulator, data); + if (ret) { + dev_err(dev, + "Failed to add regulator devm action\n"); + goto err_put; + } } } @@ -432,11 +458,11 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) ret = imx_get_clks(dev); if (ret) - goto disable_hsic_regulator; + goto qos_remove_request; ret = imx_prepare_enable_clks(dev); if (ret) - goto disable_hsic_regulator; + goto qos_remove_request; ret = clk_prepare_enable(data->clk_wakeup); if (ret) @@ -470,16 +496,30 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) { pdata.flags |= CI_HDRC_OVERRIDE_PHY_CONTROL; data->override_phy_control = true; - usb_phy_init(pdata.usb_phy); + ret = usb_phy_init(pdata.usb_phy); + if (ret) { + dev_err(dev, "Failed to init phy\n"); + goto err_clk; + } } if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM) data->supports_runtime_pm = true; + data->wakeup_irq = platform_get_irq_optional(pdev, 1); + if (data->wakeup_irq > 0) { + ret = devm_request_threaded_irq(dev, data->wakeup_irq, + NULL, ci_wakeup_irq_handler, + IRQF_ONESHOT | IRQF_NO_AUTOEN, + pdata.name, data); + if (ret) + goto err_clk; + } + ret = imx_usbmisc_init(data->usbmisc_data); if (ret) { dev_err(dev, "usbmisc init failed, ret=%d\n", ret); - goto err_clk; + goto phy_shutdown; } data->ci_pdev = ci_hdrc_add_device(dev, @@ -488,7 +528,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) if (IS_ERR(data->ci_pdev)) { ret = PTR_ERR(data->ci_pdev); dev_err_probe(dev, ret, "ci_hdrc_add_device failed\n"); - goto err_clk; + goto phy_shutdown; } if (data->usbmisc_data) { @@ -522,19 +562,20 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) disable_device: ci_hdrc_remove_device(data->ci_pdev); +phy_shutdown: + if (data->override_phy_control) + usb_phy_shutdown(data->phy); err_clk: clk_disable_unprepare(data->clk_wakeup); err_wakeup_clk: imx_disable_unprepare_clks(dev); -disable_hsic_regulator: - if (data->hsic_pad_regulator) - /* don't overwrite original ret (cf. EPROBE_DEFER) */ - regulator_disable(data->hsic_pad_regulator); +qos_remove_request: if (pdata.flags & CI_HDRC_PMQOS) cpu_latency_qos_remove_request(&data->pm_qos_req); data->ci_pdev = NULL; err_put: - put_device(data->usbmisc_data->dev); + if (data->usbmisc_data) + put_device(data->usbmisc_data->dev); return ret; } @@ -556,10 +597,9 @@ static void ci_hdrc_imx_remove(struct platform_device *pdev) clk_disable_unprepare(data->clk_wakeup); if (data->plat_data->flags & CI_HDRC_PMQOS) cpu_latency_qos_remove_request(&data->pm_qos_req); - if (data->hsic_pad_regulator) - regulator_disable(data->hsic_pad_regulator); } - put_device(data->usbmisc_data->dev); + if (data->usbmisc_data) + put_device(data->usbmisc_data->dev); } static void ci_hdrc_imx_shutdown(struct platform_device *pdev) @@ -584,6 +624,10 @@ static int imx_controller_suspend(struct device *dev, } imx_disable_unprepare_clks(dev); + + if (data->wakeup_irq > 0) + enable_irq(data->wakeup_irq); + if (data->plat_data->flags & CI_HDRC_PMQOS) cpu_latency_qos_remove_request(&data->pm_qos_req); @@ -608,6 +652,10 @@ static int imx_controller_resume(struct device *dev, if (data->plat_data->flags & CI_HDRC_PMQOS) cpu_latency_qos_add_request(&data->pm_qos_req, 0); + if (data->wakeup_irq > 0 && + !irqd_irq_disabled(irq_get_irq_data(data->wakeup_irq))) + disable_irq_nosync(data->wakeup_irq); + ret = imx_prepare_enable_clks(dev); if (ret) return ret; @@ -643,6 +691,10 @@ static int ci_hdrc_imx_suspend(struct device *dev) return ret; pinctrl_pm_select_sleep_state(dev); + + if (data->wakeup_irq > 0 && device_may_wakeup(dev)) + enable_irq_wake(data->wakeup_irq); + return ret; } @@ -651,6 +703,9 @@ static int ci_hdrc_imx_resume(struct device *dev) struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); int ret; + if (data->wakeup_irq > 0 && device_may_wakeup(dev)) + disable_irq_wake(data->wakeup_irq); + pinctrl_pm_select_default_state(dev); ret = imx_controller_resume(dev, PMSG_RESUME); if (!ret && data->supports_runtime_pm) { diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 0cce19208370..ced6076a8248 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -13,6 +13,7 @@ #include <linux/usb/hcd.h> #include <linux/usb/chipidea.h> #include <linux/regulator/consumer.h> +#include <linux/string_choices.h> #include <linux/pinctrl/consumer.h> #include "../host/ehci.h" @@ -56,7 +57,7 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) if (ret) { dev_err(dev, "Failed to %s vbus regulator, ret=%d\n", - enable ? "enable" : "disable", ret); + str_enable_disable(enable), ret); return ret; } priv->enabled = enable; @@ -256,8 +257,14 @@ static int ci_ehci_hub_control( struct device *dev = hcd->self.controller; struct ci_hdrc *ci = dev_get_drvdata(dev); - port_index = wIndex & 0xff; - port_index -= (port_index > 0); + /* + * Avoid out-of-bounds values while calculating the port index + * from wIndex. The compiler doesn't like pointers to invalid + * addresses, even if they are never used. + */ + port_index = (wIndex - 1) & 0xff; + if (port_index >= HCS_N_PORTS_MAX) + port_index = 0; status_reg = &ehci->regs->port_status[port_index]; spin_lock_irqsave(&ehci->lock, flags); diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index c17516c29b63..a093544482d5 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -424,8 +424,7 @@ static enum hrtimer_restart ci_otg_hrtimer_func(struct hrtimer *t) /* Initialize timers */ static int ci_otg_init_timers(struct ci_hdrc *ci) { - hrtimer_init(&ci->otg_fsm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); - ci->otg_fsm_hrtimer.function = ci_otg_hrtimer_func; + hrtimer_setup(&ci->otg_fsm_hrtimer, ci_otg_hrtimer_func, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); return 0; } diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 8a9b31fd5c89..1a48e6440e6c 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -2374,6 +2374,10 @@ static void udc_suspend(struct ci_hdrc *ci) */ if (hw_read(ci, OP_ENDPTLISTADDR, ~0) == 0) hw_write(ci, OP_ENDPTLISTADDR, ~0, ~0); + + if (ci->gadget.connected && + (!ci->suspended || !device_may_wakeup(ci->dev))) + usb_gadget_disconnect(&ci->gadget); } static void udc_resume(struct ci_hdrc *ci, bool power_lost) @@ -2384,6 +2388,9 @@ static void udc_resume(struct ci_hdrc *ci, bool power_lost) OTGSC_BSVIS | OTGSC_BSVIE); if (ci->vbus_active) usb_gadget_vbus_disconnect(&ci->gadget); + } else if (ci->vbus_active && ci->driver && + !ci->gadget.connected) { + usb_gadget_connect(&ci->gadget); } /* Restore value 0 if it was set for power lost check */ diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 1394881fde5f..118b9a68496b 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -139,6 +139,22 @@ #define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \ MX6_BM_ID_WAKEUP | MX6SX_BM_DPDM_WAKEUP_EN) +/* + * HSIO Block Control Register + */ + +#define BLKCTL_USB_WAKEUP_CTRL 0x0 +#define BLKCTL_OTG_WAKE_ENABLE BIT(31) +#define BLKCTL_OTG_VBUS_SESSVALID BIT(4) +#define BLKCTL_OTG_ID_WAKEUP_EN BIT(2) +#define BLKCTL_OTG_VBUS_WAKEUP_EN BIT(1) +#define BLKCTL_OTG_DPDM_WAKEUP_EN BIT(0) + +#define BLKCTL_WAKEUP_SOURCE (BLKCTL_OTG_WAKE_ENABLE | \ + BLKCTL_OTG_ID_WAKEUP_EN | \ + BLKCTL_OTG_VBUS_WAKEUP_EN | \ + BLKCTL_OTG_DPDM_WAKEUP_EN) + struct usbmisc_ops { /* It's called once when probe a usb device */ int (*init)(struct imx_usbmisc_data *data); @@ -159,6 +175,7 @@ struct usbmisc_ops { struct imx_usbmisc { void __iomem *base; + void __iomem *blkctl; spinlock_t lock; const struct usbmisc_ops *ops; }; @@ -440,7 +457,7 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) else if (data->oc_pol_configured) reg &= ~MX6_BM_OVER_CUR_POLARITY; } - /* If the polarity is not set keep it as setup by the bootlader */ + /* If the polarity is not set keep it as setup by the bootloader */ if (data->pwr_pol == 1) reg |= MX6_BM_PWR_POLARITY; writel(reg, usbmisc->base + data->index * 4); @@ -645,7 +662,7 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) else if (data->oc_pol_configured) reg &= ~MX6_BM_OVER_CUR_POLARITY; } - /* If the polarity is not set keep it as setup by the bootlader */ + /* If the polarity is not set keep it as setup by the bootloader */ if (data->pwr_pol == 1) reg |= MX6_BM_PWR_POLARITY; writel(reg, usbmisc->base); @@ -939,7 +956,7 @@ static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data) else if (data->oc_pol_configured) reg &= ~MX6_BM_OVER_CUR_POLARITY; } - /* If the polarity is not set keep it as setup by the bootlader */ + /* If the polarity is not set keep it as setup by the bootloader */ if (data->pwr_pol == 1) reg |= MX6_BM_PWR_POLARITY; @@ -1016,6 +1033,44 @@ static int usbmisc_imx6sx_power_lost_check(struct imx_usbmisc_data *data) return 0; } +static u32 usbmisc_blkctl_wakeup_setting(struct imx_usbmisc_data *data) +{ + u32 wakeup_setting = BLKCTL_WAKEUP_SOURCE; + + if (data->ext_id || data->available_role != USB_DR_MODE_OTG) + wakeup_setting &= ~BLKCTL_OTG_ID_WAKEUP_EN; + + if (data->ext_vbus || data->available_role == USB_DR_MODE_HOST) + wakeup_setting &= ~BLKCTL_OTG_VBUS_WAKEUP_EN; + + /* Select session valid as VBUS wakeup source */ + wakeup_setting |= BLKCTL_OTG_VBUS_SESSVALID; + + return wakeup_setting; +} + +static int usbmisc_imx95_set_wakeup(struct imx_usbmisc_data *data, bool enabled) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 val; + + if (!usbmisc->blkctl) + return 0; + + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->blkctl + BLKCTL_USB_WAKEUP_CTRL); + val &= ~BLKCTL_WAKEUP_SOURCE; + + if (enabled) + val |= usbmisc_blkctl_wakeup_setting(data); + + writel(val, usbmisc->blkctl + BLKCTL_USB_WAKEUP_CTRL); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + return 0; +} + static const struct usbmisc_ops imx25_usbmisc_ops = { .init = usbmisc_imx25_init, .post = usbmisc_imx25_post, @@ -1068,6 +1123,14 @@ static const struct usbmisc_ops imx7ulp_usbmisc_ops = { .power_lost_check = usbmisc_imx7d_power_lost_check, }; +static const struct usbmisc_ops imx95_usbmisc_ops = { + .init = usbmisc_imx7d_init, + .set_wakeup = usbmisc_imx95_set_wakeup, + .charger_detection = imx7d_charger_detection, + .power_lost_check = usbmisc_imx7d_power_lost_check, + .vbus_comparator_on = usbmisc_imx7d_vbus_comparator_on, +}; + static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); @@ -1185,7 +1248,7 @@ int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup) if (usbmisc->ops->hsic_set_clk && data->hsic) ret = usbmisc->ops->hsic_set_clk(data, false); if (ret) { - dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret); + dev_err(data->dev, "hsic_set_clk failed, ret=%d\n", ret); return ret; } @@ -1224,7 +1287,7 @@ int imx_usbmisc_resume(struct imx_usbmisc_data *data, bool wakeup) if (usbmisc->ops->hsic_set_clk && data->hsic) ret = usbmisc->ops->hsic_set_clk(data, true); if (ret) { - dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret); + dev_err(data->dev, "hsic_set_clk failed, ret=%d\n", ret); goto hsic_set_clk_fail; } @@ -1289,6 +1352,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = { .compatible = "fsl,imx8ulp-usbmisc", .data = &imx7ulp_usbmisc_ops, }, + { + .compatible = "fsl,imx95-usbmisc", + .data = &imx95_usbmisc_ops, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids); @@ -1296,6 +1363,7 @@ MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids); static int usbmisc_imx_probe(struct platform_device *pdev) { struct imx_usbmisc *data; + struct resource *res; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -1307,6 +1375,15 @@ static int usbmisc_imx_probe(struct platform_device *pdev) if (IS_ERR(data->base)) return PTR_ERR(data->base); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + data->blkctl = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->blkctl)) + return PTR_ERR(data->blkctl); + } else if (device_is_compatible(&pdev->dev, "fsl,imx95-usbmisc")) { + dev_warn(&pdev->dev, "wakeup setting is missing\n"); + } + data->ops = of_device_get_match_data(&pdev->dev); platform_set_drvdata(pdev, data); |