diff options
-rw-r--r-- | drivers/phy/phy-stm32-usbphyc.c | 9 | ||||
-rw-r--r-- | drivers/regulator/stm32-pwr.c | 19 | ||||
-rw-r--r-- | drivers/usb/dwc2/core.c | 26 | ||||
-rw-r--r-- | drivers/usb/dwc2/core.h | 3 | ||||
-rw-r--r-- | drivers/usb/dwc2/dwc2.c | 57 | ||||
-rw-r--r-- | drivers/usb/dwc2/dwc2.h | 7 | ||||
-rw-r--r-- | drivers/usb/dwc2/gadget.c | 144 |
7 files changed, 235 insertions, 30 deletions
diff --git a/drivers/phy/phy-stm32-usbphyc.c b/drivers/phy/phy-stm32-usbphyc.c index 093842fe14..d9eaa8a754 100644 --- a/drivers/phy/phy-stm32-usbphyc.c +++ b/drivers/phy/phy-stm32-usbphyc.c @@ -328,13 +328,13 @@ static int stm32_usbphyc_probe(struct device_d *dev) if (IS_ERR(usbphyc->clk)) { ret = PTR_ERR(usbphyc->clk); dev_err(dev, "clk get failed: %d\n", ret); - return ret; + goto release_region; } ret = clk_enable(usbphyc->clk); if (ret) { dev_err(dev, "clk enable failed: %d\n", ret); - return ret; + goto release_region; } device_reset_us(dev, 2); @@ -405,6 +405,11 @@ static int stm32_usbphyc_probe(struct device_d *dev) clk_disable: clk_disable(usbphyc->clk); +release_region: + release_region(iores); + + free(usbphyc->phys); + free(usbphyc); return ret; } diff --git a/drivers/regulator/stm32-pwr.c b/drivers/regulator/stm32-pwr.c index 54ba716a8f..4cb46b081a 100644 --- a/drivers/regulator/stm32-pwr.c +++ b/drivers/regulator/stm32-pwr.c @@ -86,8 +86,6 @@ static int stm32_pwr_reg_enable(struct regulator_dev *rdev) int ret; u32 val; - regulator_enable(priv->supply); - val = readl(priv->base + REG_PWR_CR3); val |= rdev->desc->enable_mask; writel(val, priv->base + REG_PWR_CR3); @@ -120,8 +118,6 @@ static int stm32_pwr_reg_disable(struct regulator_dev *rdev) dev_err(rdev->dev, "%s: regulator disable timed out!\n", desc->name); - regulator_disable(priv->supply); - return ret; } @@ -183,18 +179,27 @@ static int stm32_pwr_regulator_probe(struct device_d *dev) priv->rdev.dev = dev; priv->supply = regulator_get(dev, desc->supply_name); - if (IS_ERR(priv->supply)) - return PTR_ERR(priv->supply); + if (IS_ERR(priv->supply)) { + ret = PTR_ERR(priv->supply); + goto release_region; + } ret = of_regulator_register(&priv->rdev, child); if (ret) { dev_err(dev, "%s: Failed to register regulator: %d\n", desc->name, ret); - return ret; + goto release_region; } + + regulator_enable(priv->supply); } return 0; + +release_region: + release_region(iores); + + return ret; } static const struct of_device_id stm32_pwr_of_match[] = { diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index c4a3cc789b..cc5729ed92 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -28,7 +28,7 @@ static bool dwc2_hw_is_device(struct dwc2 *dwc2) (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE); } -void dwc2_set_param_otg_cap(struct dwc2 *dwc2) +static void dwc2_set_param_otg_cap(struct dwc2 *dwc2) { u8 val; @@ -49,7 +49,7 @@ void dwc2_set_param_otg_cap(struct dwc2 *dwc2) dwc2->params.otg_cap = val; } -void dwc2_set_param_phy_type(struct dwc2 *dwc2) +static void dwc2_set_param_phy_type(struct dwc2 *dwc2) { u8 val; @@ -69,7 +69,7 @@ void dwc2_set_param_phy_type(struct dwc2 *dwc2) dwc2->params.phy_type = val; } -void dwc2_set_param_speed(struct dwc2 *dwc2) +static void dwc2_set_param_speed(struct dwc2 *dwc2) { if (dwc2->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) dwc2->params.speed = DWC2_SPEED_PARAM_FULL; @@ -77,7 +77,7 @@ void dwc2_set_param_speed(struct dwc2 *dwc2) dwc2->params.speed = DWC2_SPEED_PARAM_HIGH; } -void dwc2_set_param_phy_utmi_width(struct dwc2 *dwc2) +static void dwc2_set_param_phy_utmi_width(struct dwc2 *dwc2) { int val; @@ -185,12 +185,10 @@ void dwc2_set_default_params(struct dwc2 *dwc2) } } -int dwc2_core_snpsid(struct dwc2 *dwc2) +int dwc2_check_core_version(struct dwc2 *dwc2) { struct dwc2_hw_params *hw = &dwc2->hw_params; - hw->snpsid = dwc2_readl(dwc2, GSNPSID); - /* * Attempt to ensure this device is really a DWC2 Controller. * Read and verify the GSNPSID register contents. The value should be @@ -649,18 +647,16 @@ int dwc2_get_dr_mode(struct dwc2 *dwc2) if (dwc2_hw_is_device(dwc2)) { dwc2_dbg(dwc2, "Controller is device only\n"); - if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) { - dwc2_err(dwc2, - "Controller does not support host mode.\n"); - return -EINVAL; + if (!IS_ENABLED(CONFIG_USB_DWC2_GADGET)) { + dwc2_err(dwc2, "gadget mode support not compiled in!\n"); + return -ENOTSUPP; } mode = USB_DR_MODE_PERIPHERAL; } else if (dwc2_hw_is_host(dwc2)) { dwc2_dbg(dwc2, "Controller is host only\n"); - if (IS_ENABLED(CONFIG_USB_DWC2_GADGET)) { - dwc2_err(dwc2, - "Controller does not support device mode.\n"); - return -EINVAL; + if (!IS_ENABLED(CONFIG_USB_DWC2_HOST)) { + dwc2_err(dwc2, "host mode support not compiled in!\n"); + return -ENOTSUPP; } mode = USB_DR_MODE_HOST; } else { diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 090ca15fec..780efb262c 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -466,6 +466,9 @@ struct dwc2 { struct dwc2_hw_params hw_params; struct dwc2_core_params params; + struct phy *phy; /* optional */ + struct clk *clk; + #ifdef CONFIG_USB_DWC2_HOST struct usb_host host; u8 in_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; diff --git a/drivers/usb/dwc2/dwc2.c b/drivers/usb/dwc2/dwc2.c index 282e6754b0..9893977b8e 100644 --- a/drivers/usb/dwc2/dwc2.c +++ b/drivers/usb/dwc2/dwc2.c @@ -12,6 +12,7 @@ #include <errno.h> #include <driver.h> #include <linux/clk.h> +#include <linux/reset.h> #include "dwc2.h" @@ -19,6 +20,9 @@ static int dwc2_set_mode(void *ctx, enum usb_dr_mode mode) { struct dwc2 *dwc2 = ctx; int ret = -ENODEV; + int oldmode = dwc2->dr_mode; + + dwc2->dr_mode = mode; if (mode == USB_DR_MODE_HOST || mode == USB_DR_MODE_OTG) { if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) @@ -33,6 +37,9 @@ static int dwc2_set_mode(void *ctx, enum usb_dr_mode mode) dwc2_err(dwc2, "Peripheral support not available\n"); } + if (ret) + dwc2->dr_mode = oldmode; + return ret; } @@ -50,7 +57,36 @@ static int dwc2_probe(struct device_d *dev) dwc2->regs = IOMEM(iores->start); dwc2->dev = dev; - ret = dwc2_core_snpsid(dwc2); + dwc2->clk = clk_get(dev, "otg"); + if (IS_ERR(dwc2->clk)) { + ret = PTR_ERR(dwc2->clk); + dev_err(dev, "Failed to get USB clock %d\n", ret); + goto release_region; + } + + ret = clk_enable(dwc2->clk); + if (ret) + goto clk_put; + + ret = device_reset_us(dev, 2); + if (ret) + goto clk_disable; + + dwc2->phy = phy_optional_get(dev, "usb2-phy"); + if (IS_ERR(dwc2->phy)) { + ret = PTR_ERR(dwc2->phy); + goto clk_disable; + } + + ret = phy_power_on(dwc2->phy); + if (ret) + goto clk_disable; + + ret = phy_init(dwc2->phy); + if (ret) + goto phy_power_off; + + ret = dwc2_check_core_version(dwc2); if (ret) goto error; @@ -66,6 +102,8 @@ static int dwc2_probe(struct device_d *dev) dwc2_get_hwparams(dwc2); ret = dwc2_get_dr_mode(dwc2); + if (ret) + goto error; dwc2_set_default_params(dwc2); @@ -77,7 +115,21 @@ static int dwc2_probe(struct device_d *dev) else ret = dwc2_set_mode(dwc2, dwc2->dr_mode); + if (ret) + goto error; + + return 0; error: + phy_exit(dwc2->phy); +phy_power_off: + phy_power_off(dwc2->phy); +clk_disable: + clk_disable(dwc2->clk); +clk_put: + clk_put(dwc2->clk); +release_region: + release_region(iores); + return ret; } @@ -87,6 +139,9 @@ static void dwc2_remove(struct device_d *dev) dwc2_host_uninit(dwc2); dwc2_gadget_uninit(dwc2); + + phy_exit(dwc2->phy); + phy_power_off(dwc2->phy); } static const struct of_device_id dwc2_platform_dt_ids[] = { diff --git a/drivers/usb/dwc2/dwc2.h b/drivers/usb/dwc2/dwc2.h index 30ad906656..b9fdda850d 100644 --- a/drivers/usb/dwc2/dwc2.h +++ b/drivers/usb/dwc2/dwc2.h @@ -2,17 +2,14 @@ #include <usb/usb.h> #include <usb/usb_defs.h> #include <usb/gadget.h> +#include <linux/phy/phy.h> #include "regs.h" #include "core.h" /* Core functions */ -void dwc2_set_param_otg_cap(struct dwc2 *dwc2); -void dwc2_set_param_phy_type(struct dwc2 *dwc2); -void dwc2_set_param_speed(struct dwc2 *dwc2); -void dwc2_set_param_phy_utmi_width(struct dwc2 *dwc2); void dwc2_set_default_params(struct dwc2 *dwc2); -int dwc2_core_snpsid(struct dwc2 *dwc2); +int dwc2_check_core_version(struct dwc2 *dwc2); void dwc2_get_hwparams(struct dwc2 *dwc2); void dwc2_init_fs_ls_pclk_sel(struct dwc2 *dwc2); diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index aa7447c9b4..b8ec37be78 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ #include <dma.h> #include <usb/gadget.h> +#include <linux/iopoll.h> #include "dwc2.h" #define to_dwc2 gadget_to_dwc2 @@ -2660,6 +2661,144 @@ static int dwc2_eps_alloc(struct dwc2 *dwc2) return 0; } +/** + * dwc2_wait_for_mode() - Waits for the controller mode. + * @dwc2: Programming view of the DWC_otg controller. + * @host_mode: If true, waits for host mode, otherwise device mode. + */ +static void dwc2_wait_for_mode(struct dwc2 *dwc2, bool host_mode) +{ + int val, ret; + + dev_vdbg(dwc2->dev, "Waiting for %s mode\n", + host_mode ? "host" : "device"); + + ret = readx_poll_timeout(dwc2_is_host_mode, dwc2, val, + val == host_mode, 110 * USEC_PER_MSEC); + if (ret) + dev_err(dwc2->dev, "%s: Couldn't set %s mode\n", + __func__, host_mode ? "host" : "device"); + + dev_vdbg(dwc2->dev, "%s mode set\n", + host_mode ? "Host" : "Device"); +} + +/** + * dwc2_iddig_filter_enabled() - Returns true if the IDDIG debounce + * filter is enabled. + * + * @hsotg: Programming view of DWC_otg controller + */ +static bool dwc2_iddig_filter_enabled(struct dwc2 *dwc2) +{ + u32 gsnpsid; + u32 ghwcfg4; + + /* Check if core configuration includes the IDDIG filter. */ + ghwcfg4 = dwc2_readl(dwc2, GHWCFG4); + if (!(ghwcfg4 & GHWCFG4_IDDIG_FILT_EN)) + return false; + + /* + * Check if the IDDIG debounce filter is bypassed. Available + * in core version >= 3.10a. + */ + gsnpsid = dwc2_readl(dwc2, GSNPSID); + if (gsnpsid >= DWC2_CORE_REV_3_10a) { + u32 gotgctl = dwc2_readl(dwc2, GOTGCTL); + + if (gotgctl & GOTGCTL_DBNCE_FLTR_BYPASS) + return false; + } + + return true; +} + +/** + * dwc2_force_mode() - Force the mode of the controller. + * + * Forcing the mode is needed for two cases: + * + * 1) If the dr_mode is set to either HOST or PERIPHERAL we force the + * controller to stay in a particular mode regardless of ID pin + * changes. We do this once during probe. + * + * 2) During probe we want to read reset values of the hw + * configuration registers that are only available in either host or + * device mode. We may need to force the mode if the current mode does + * not allow us to access the register in the mode that we want. + * + * In either case it only makes sense to force the mode if the + * controller hardware is OTG capable. + * + * Checks are done in this function to determine whether doing a force + * would be valid or not. + * + * If a force is done, it requires a IDDIG debounce filter delay if + * the filter is configured and enabled. We poll the current mode of + * the controller to account for this delay. + * + * @dwc2: Programming view of DWC_otg controller + * @host: Host mode flag + */ +static void dwc2_force_mode(struct dwc2 *dwc2, bool host) +{ + u32 gusbcfg; + u32 set; + u32 clear; + + dev_dbg(dwc2->dev, "Forcing mode to %s\n", host ? "host" : "device"); + + /* + * If dr_mode is either peripheral or host only, there is no + * need to ever force the mode to the opposite mode. + */ + if (WARN_ON(host && dwc2->dr_mode == USB_DR_MODE_PERIPHERAL)) + return; + + if (WARN_ON(!host && dwc2->dr_mode == USB_DR_MODE_HOST)) + return; + + gusbcfg = dwc2_readl(dwc2, GUSBCFG); + + set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE; + clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE; + + gusbcfg &= ~clear; + gusbcfg |= set; + dwc2_writel(dwc2, gusbcfg, GUSBCFG); + + dwc2_wait_for_mode(dwc2, host); + + return; +} + +/** + * dwc2_clear_force_mode() - Clears the force mode bits. + * + * After clearing the bits, wait up to 100 ms to account for any + * potential IDDIG filter delay. We can't know if we expect this delay + * or not because the value of the connector ID status is affected by + * the force mode. We only need to call this once during probe if + * dr_mode == OTG. + * + * @dwc2: Programming view of DWC_otg controller + */ +static void dwc2_clear_force_mode(struct dwc2 *dwc2) +{ + u32 gusbcfg; + + dev_dbg(dwc2->dev, "Clearing force mode bits\n"); + + gusbcfg = dwc2_readl(dwc2, GUSBCFG); + gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; + gusbcfg &= ~GUSBCFG_FORCEDEVMODE; + dwc2_writel(dwc2, gusbcfg, GUSBCFG); + + if (dwc2_iddig_filter_enabled(dwc2)) + mdelay(100); +} + int dwc2_gadget_init(struct dwc2 *dwc2) { u32 dctl; @@ -2684,6 +2823,11 @@ int dwc2_gadget_init(struct dwc2 *dwc2) dwc2->gadget.is_otg = (dwc2->dr_mode == USB_DR_MODE_OTG) ? 1 : 0; + if (dwc2->gadget.is_otg) + dwc2_clear_force_mode(dwc2); + else + dwc2_force_mode(dwc2, false); + ret = dwc2_eps_alloc(dwc2); if (ret) { dwc2_err(dwc2, "Endpoints allocation failed: %d\n", ret); |