diff options
Diffstat (limited to 'drivers/usb/imx')
-rw-r--r-- | drivers/usb/imx/Kconfig | 1 | ||||
-rw-r--r-- | drivers/usb/imx/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/imx/chipidea-imx.c | 96 | ||||
-rw-r--r-- | drivers/usb/imx/imx-usb-misc.c | 60 | ||||
-rw-r--r-- | drivers/usb/imx/imx-usb-phy.c | 61 |
5 files changed, 123 insertions, 96 deletions
diff --git a/drivers/usb/imx/Kconfig b/drivers/usb/imx/Kconfig index 2b9e63b21c..2b9ae607bc 100644 --- a/drivers/usb/imx/Kconfig +++ b/drivers/usb/imx/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config USB_IMX_CHIPIDEA bool "i.MX USB support (read help)" diff --git a/drivers/usb/imx/Makefile b/drivers/usb/imx/Makefile index ac17e91e99..8a70fceb78 100644 --- a/drivers/usb/imx/Makefile +++ b/drivers/usb/imx/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_USB_IMX_CHIPIDEA) += chipidea-imx.o obj-$(CONFIG_USB_IMX_CHIPIDEA_USBMISC) += imx-usb-misc.o obj-$(CONFIG_USB_IMX_PHY) += imx-usb-phy.o diff --git a/drivers/usb/imx/chipidea-imx.c b/drivers/usb/imx/chipidea-imx.c index 635be02929..c5e6ce61e9 100644 --- a/drivers/usb/imx/chipidea-imx.c +++ b/drivers/usb/imx/chipidea-imx.c @@ -1,15 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. */ #include <common.h> @@ -18,13 +9,13 @@ #include <of.h> #include <errno.h> #include <driver.h> -#include <usb/usb.h> -#include <usb/ehci.h> +#include <linux/usb/usb.h> +#include <linux/usb/ehci.h> #include <regulator.h> -#include <usb/chipidea-imx.h> -#include <usb/phy.h> -#include <usb/ulpi.h> -#include <usb/fsl_usb2.h> +#include <linux/usb/chipidea-imx.h> +#include <linux/usb/phy.h> +#include <linux/usb/ulpi.h> +#include <linux/usb/fsl_usb2.h> #include <linux/err.h> #include <linux/phy/phy.h> #include <linux/clk.h> @@ -36,13 +27,13 @@ struct imx_chipidea_data { }; struct imx_chipidea { - struct device_d *dev; + struct device *dev; void __iomem *base; struct ehci_data data; unsigned long flags; enum usb_dr_mode mode; int portno; - struct device_d *usbmisc; + struct device *usbmisc; enum usb_phy_interface phymode; struct param_d *param_mode; struct regulator *vbus; @@ -113,7 +104,7 @@ static int imx_chipidea_probe_dt(struct imx_chipidea *ci) struct of_phandle_args out_args; if (ci->have_usb_misc) { - if (of_parse_phandle_with_args(ci->dev->device_node, "fsl,usbmisc", + if (of_parse_phandle_with_args(ci->dev->of_node, "fsl,usbmisc", "#index-cells", 0, &out_args)) return -ENODEV; @@ -126,7 +117,7 @@ static int imx_chipidea_probe_dt(struct imx_chipidea *ci) ci->flags = MXC_EHCI_MODE_UTMI_8BIT; - ci->mode = of_usb_get_dr_mode(ci->dev->device_node, NULL); + ci->mode = of_usb_get_dr_mode(ci->dev->of_node, NULL); if (ci->mode == USB_DR_MODE_UNKNOWN) { /* @@ -139,7 +130,7 @@ static int imx_chipidea_probe_dt(struct imx_chipidea *ci) ci->mode = USB_DR_MODE_HOST; } - ci->phymode = of_usb_get_phy_mode(ci->dev->device_node, NULL); + ci->phymode = of_usb_get_phy_mode(ci->dev->of_node, NULL); switch (ci->phymode) { case USBPHY_INTERFACE_MODE_UTMI: ci->flags = MXC_EHCI_MODE_UTMI_8BIT; @@ -160,28 +151,24 @@ static int imx_chipidea_probe_dt(struct imx_chipidea *ci) dev_dbg(ci->dev, "no phy_type setting. Relying on reset default\n"); } - if (of_find_property(ci->dev->device_node, + if (of_find_property(ci->dev->of_node, "disable-over-current", NULL)) ci->flags |= MXC_EHCI_DISABLE_OVERCURRENT; - else if (!of_find_property(ci->dev->device_node, + else if (!of_find_property(ci->dev->of_node, "over-current-active-high", NULL)) ci->flags |= MXC_EHCI_OC_PIN_ACTIVE_LOW; - if (of_usb_get_maximum_speed(ci->dev->device_node, NULL) == + if (of_find_property(ci->dev->of_node, "power-active-high", NULL)) + ci->flags |= MXC_EHCI_PWR_PIN_ACTIVE_HIGH; + + if (of_usb_get_maximum_speed(ci->dev->of_node, NULL) == USB_SPEED_FULL) ci->flags |= MXC_EHCI_PFSC; return 0; } -static int ci_ehci_detect(struct device_d *dev) -{ - struct imx_chipidea *ci = dev->priv; - - return ehci_detect(ci->ehci); -} - static int ci_set_mode(void *ctx, enum usb_dr_mode mode) { struct imx_chipidea *ci = ctx; @@ -202,8 +189,6 @@ static int ci_set_mode(void *ctx, enum usb_dr_mode mode) } ci->ehci = ehci; - - ci->dev->detect = ci_ehci_detect; } else { dev_err(ci->dev, "Host support not available\n"); return -ENODEV; @@ -228,11 +213,12 @@ static int ci_set_mode(void *ctx, enum usb_dr_mode mode) return 0; } -static int imx_chipidea_probe(struct device_d *dev) +static int imx_chipidea_probe(struct device *dev) { struct resource *iores; struct imx_chipidea_data *imx_data; struct imxusb_platformdata *pdata = dev->platform_data; + char const *phynode_name; int ret; void __iomem *base; struct imx_chipidea *ci; @@ -246,7 +232,7 @@ static int imx_chipidea_probe(struct device_d *dev) if (!ret) ci->have_usb_misc = imx_data->have_usb_misc; - if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) { + if (IS_ENABLED(CONFIG_OFDEVICE) && dev->of_node) { ret = imx_chipidea_probe_dt(ci); if (ret) return ret; @@ -262,8 +248,11 @@ static int imx_chipidea_probe(struct device_d *dev) } ci->vbus = regulator_get(dev, "vbus"); - if (IS_ERR(ci->vbus)) + if (IS_ERR(ci->vbus)) { + dev_warn(dev, "Cannot get vbus regulator: %pe (ignoring)\n", + ci->vbus); ci->vbus = NULL; + } /* * Some devices have more than one clock, in this case they are enabled @@ -271,15 +260,23 @@ static int imx_chipidea_probe(struct device_d *dev) * devices which have only one. */ ci->clk = clk_get(dev, NULL); - if (!IS_ERR(ci->clk)) - clk_enable(ci->clk); - if (of_property_read_bool(dev->device_node, "fsl,usbphy")) { - ci->phy = of_phy_get_by_phandle(dev, "fsl,usbphy", 0); + /* Device trees are using both "phys" and "fsl,usbphy". Prefer the + * more modern former one but fall back to the old one. + * + * Code should be removed when all devicetrees are using "phys" */ + if (of_property_read_bool(dev->of_node, "phys")) + phynode_name = "phys"; + else if (of_property_read_bool(dev->of_node, "fsl,usbphy")) + phynode_name = "fsl,usbphy"; + else + phynode_name = NULL; + + if (phynode_name) { + ci->phy = of_phy_get_by_phandle(dev, phynode_name, 0); if (IS_ERR(ci->phy)) { - ret = PTR_ERR(ci->phy); - dev_err(dev, "Cannot get phy: %s\n", strerror(-ret)); - return ret; + dev_err(dev, "Cannot get phy: %pe\n", ci->phy); + return PTR_ERR(ci->phy); } else { ci->usbphy = phy_to_usbphy(ci->phy); if (IS_ERR(ci->usbphy)) @@ -303,6 +300,14 @@ static int imx_chipidea_probe(struct device_d *dev) ci->data.drvdata = ci; ci->data.usbphy = ci->usbphy; + /* + * Enable the clock after we ensured that all resources are available. + * This is crucial since the phy can be missing which and so the + * usb-controller <-> usb-phy communication is only partly initialized. + * This can trigger strange system hangs at least on i.MX8M SoCs. + */ + clk_enable(ci->clk); + if ((ci->flags & MXC_EHCI_PORTSC_MASK) == MXC_EHCI_MODE_HSIC) imx_chipidea_port_init(ci); @@ -325,7 +330,7 @@ static int imx_chipidea_probe(struct device_d *dev) return ret; }; -static void imx_chipidea_remove(struct device_d *dev) +static void imx_chipidea_remove(struct device *dev) { struct imx_chipidea *ci = dev->priv; @@ -358,8 +363,9 @@ static __maybe_unused struct of_device_id imx_chipidea_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, imx_chipidea_dt_ids); -static struct driver_d imx_chipidea_driver = { +static struct driver imx_chipidea_driver = { .name = "imx-usb", .probe = imx_chipidea_probe, .of_compatible = DRV_OF_COMPAT(imx_chipidea_dt_ids), diff --git a/drivers/usb/imx/imx-usb-misc.c b/drivers/usb/imx/imx-usb-misc.c index aa4485ccba..bf9583e626 100644 --- a/drivers/usb/imx/imx-usb-misc.c +++ b/drivers/usb/imx/imx-usb-misc.c @@ -1,16 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> * Copyright (C) 2010 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. */ #include <common.h> @@ -18,9 +9,9 @@ #include <io.h> #include <of.h> #include <errno.h> -#include <usb/chipidea-imx.h> -#include <mach/imx6-regs.h> -#include <mach/iomux-mx6.h> +#include <linux/usb/chipidea-imx.h> +#include <mach/imx/imx6-regs.h> +#include <mach/imx/iomux-mx6.h> #define MX25_OTG_SIC_SHIFT 29 #define MX25_OTG_SIC_MASK (0x3 << MX25_OTG_SIC_SHIFT) @@ -353,7 +344,8 @@ static __maybe_unused struct imx_usb_misc_data mx5_data = { #define MX6_USB_CTRL(n) ((n) * 4) #define MX6_USB_CTRL_OVER_CUR_DIS (1 << 7) -#define MX6_USB_CTRL_OVER_CUR_ACT_HIGH (1 << 8) +#define MX6_USB_CTRL_OVER_CUR_ACT_LOW (1 << 8) +#define MX6_USB_CTRL_PWR_POLARITY (1 << 9) static void mx6_hsic_pullup(unsigned long reg, int on) { @@ -378,8 +370,17 @@ static __maybe_unused int mx6_initialize_usb_hw(void __iomem *base, int port, case 0: case 1: val = readl(base + MX6_USB_CTRL(port)); - if (flags & MXC_EHCI_DISABLE_OVERCURRENT) + if (flags & MXC_EHCI_DISABLE_OVERCURRENT) { val |= MX6_USB_CTRL_OVER_CUR_DIS; + } else { + val &= ~MX6_USB_CTRL_OVER_CUR_DIS; + if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) + val |= MX6_USB_CTRL_OVER_CUR_ACT_LOW; + else + val &= ~MX6_USB_CTRL_OVER_CUR_ACT_LOW; + } + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + val |= MX6_USB_CTRL_PWR_POLARITY; writel(val, base + MX6_USB_CTRL(port)); break; case 2: /* HSIC port */ @@ -450,10 +451,12 @@ static int usbmisc_imx7d_init(void __iomem *base, int port, } else { reg &= ~MX6_USB_CTRL_OVER_CUR_DIS; if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) - reg &= ~MX6_USB_CTRL_OVER_CUR_ACT_HIGH; + reg |= MX6_USB_CTRL_OVER_CUR_ACT_LOW; else - reg |= MX6_USB_CTRL_OVER_CUR_ACT_HIGH; + reg &= ~MX6_USB_CTRL_OVER_CUR_ACT_LOW; } + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + reg |= MX6_USB_CTRL_PWR_POLARITY; writel(reg, base); reg = readl(base + MX7D_USBNC_USB_CTRL2); @@ -599,11 +602,15 @@ static __maybe_unused struct of_device_id imx_usbmisc_dt_ids[] = { .data = &mx7_data, }, #endif -#ifdef CONFIG_ARCH_IMX8M +#if defined CONFIG_ARCH_IMX8M || defined CONFIG_ARCH_IMX93 { .compatible = "fsl,imx8mm-usbmisc", .data = &mx7_data, }, + { + .compatible = "fsl,imx8mn-usbmisc", + .data = &mx7_data, + }, #endif #ifdef CONFIG_ARCH_VF610 { @@ -615,8 +622,9 @@ static __maybe_unused struct of_device_id imx_usbmisc_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, imx_usbmisc_dt_ids); -int imx_usbmisc_port_init(struct device_d *dev, int port, unsigned flags) +int imx_usbmisc_port_init(struct device *dev, int port, unsigned flags) { struct imx_usb_misc_priv *usbmisc = dev->priv; @@ -629,7 +637,7 @@ int imx_usbmisc_port_init(struct device_d *dev, int port, unsigned flags) return usbmisc->data->init(usbmisc->base, port, flags); } -int imx_usbmisc_port_post_init(struct device_d *dev, int port, unsigned flags) +int imx_usbmisc_port_post_init(struct device *dev, int port, unsigned flags) { struct imx_usb_misc_priv *usbmisc = dev->priv; @@ -642,7 +650,7 @@ int imx_usbmisc_port_post_init(struct device_d *dev, int port, unsigned flags) return usbmisc->data->post_init(usbmisc->base, port, flags); } -static int imx_usbmisc_probe(struct device_d *dev) +static int imx_usbmisc_probe(struct device *dev) { struct resource *iores; struct imx_usb_misc_data *devtype; @@ -666,17 +674,11 @@ static int imx_usbmisc_probe(struct device_d *dev) return 0; } -static struct driver_d imx_usbmisc_driver = { +static struct driver imx_usbmisc_driver = { .name = "imx-usbmisc", .probe = imx_usbmisc_probe, .id_table = imx_usbmisc_ids, .of_compatible = DRV_OF_COMPAT(imx_usbmisc_dt_ids), }; -static int imx_usbmisc_init(void) -{ - platform_driver_register(&imx_usbmisc_driver); - return 0; -} - -coredevice_initcall(imx_usbmisc_init); +coredevice_platform_driver(imx_usbmisc_driver); diff --git a/drivers/usb/imx/imx-usb-phy.c b/drivers/usb/imx/imx-usb-phy.c index c23a747d0b..70bf292f80 100644 --- a/drivers/usb/imx/imx-usb-phy.c +++ b/drivers/usb/imx/imx-usb-phy.c @@ -1,15 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2013 Sascha Hauer <s.hauer@pengutronix.de> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. */ #include <common.h> @@ -19,7 +10,7 @@ #include <errno.h> #include <driver.h> #include <malloc.h> -#include <usb/phy.h> +#include <linux/usb/phy.h> #include <linux/phy/phy.h> #include <linux/clk.h> #include <linux/err.h> @@ -37,9 +28,12 @@ #define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) #define ANADIG_USB1_CHRG_DETECT_SET 0x1b4 -#define ANADIG_USB2_CHRG_DETECT_SET 0x214 #define ANADIG_USB1_CHRG_DETECT_EN_B BIT(20) #define ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B BIT(19) +#define ANADIG_USB1_VBUS_DETECT_STAT 0x1c0 +#define ANADIG_USB1_VBUS_DETECT_STAT_VBUS_VALID BIT(3) +#define ANADIG_USB2_CHRG_DETECT_SET 0x214 +#define ANADIG_USB2_VBUS_DETECT_STAT 0x220 struct imx_usbphy { struct usb_phy usb_phy; @@ -49,6 +43,8 @@ struct imx_usbphy { struct clk *clk; struct phy_provider *provider; int port_id; + + unsigned int vbus_valid; }; static int imx_usbphy_phy_init(struct phy *phy) @@ -112,7 +108,7 @@ static int imx_usbphy_notify_disconnect(struct usb_phy *phy, return 0; } -static struct phy *imx_usbphy_xlate(struct device_d *dev, +static struct phy *imx_usbphy_xlate(struct device *dev, struct of_phandle_args *args) { struct imx_usbphy *imxphy = dev->priv; @@ -132,10 +128,25 @@ static const struct phy_ops imx_phy_ops = { .to_usbphy = imx_usbphy_to_usbphy, }; -static int imx_usbphy_probe(struct device_d *dev) +static int imx_usbphy_get_vbus_state(struct param_d *p, void *priv) +{ + struct imx_usbphy *imxphy = priv; + unsigned int reg, val; + + reg = imxphy->port_id ? + ANADIG_USB1_VBUS_DETECT_STAT : + ANADIG_USB2_VBUS_DETECT_STAT; + val = readl(imxphy->anatop + reg); + + imxphy->vbus_valid = !!(val & ANADIG_USB1_VBUS_DETECT_STAT_VBUS_VALID); + + return 0; +} + +static int imx_usbphy_probe(struct device *dev) { struct resource *iores; - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; int ret; struct imx_usbphy *imxphy; @@ -154,6 +165,15 @@ static int imx_usbphy_probe(struct device_d *dev) ret = PTR_ERR_OR_ZERO(imxphy->anatop); if (ret) goto err_free; + + /* + * This is useful in case of usb-otg = device. In host case + * it isn't that useful since we are the supplier of the vbus + * signal. + */ + dev_add_param_bool(dev, "vbus_valid", param_set_readonly, + imx_usbphy_get_vbus_state, + &imxphy->vbus_valid, imxphy); } iores = dev_request_mem_resource(dev, 0); @@ -165,7 +185,7 @@ static int imx_usbphy_probe(struct device_d *dev) imxphy->clk = clk_get(dev, NULL); if (IS_ERR(imxphy->clk)) { - dev_err(dev, "could not get clk: %s\n", strerrorp(imxphy->clk)); + dev_err(dev, "could not get clk: %pe\n", imxphy->clk); ret = PTR_ERR(imxphy->clk); goto err_clk; } @@ -209,15 +229,12 @@ static __maybe_unused struct of_device_id imx_usbphy_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, imx_usbphy_dt_ids); -static struct driver_d imx_usbphy_driver = { +static struct driver imx_usbphy_driver = { .name = "imx-usb-phy", .probe = imx_usbphy_probe, .of_compatible = DRV_OF_COMPAT(imx_usbphy_dt_ids), }; -static int imx_usbphy_init(void) -{ - return platform_driver_register(&imx_usbphy_driver); -} -fs_initcall(imx_usbphy_init); +fs_platform_driver(imx_usbphy_driver); |