From bc0f0d4a5853e32ba97a0318f774570428fc5634 Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Tue, 16 Jun 2020 16:07:15 +0200 Subject: [PATCH 01/38] usb: dwc2: override PHY input signals with usb role switch support This patch adds support for usb role switch to dwc2, by using overriding control of the PHY voltage valid and ID input signals. iddig signal (ID) can be overridden: - when setting GUSBCFG_FORCEHOSTMODE, iddig input pin is overridden with 1; - when setting GUSBCFG_FORCEDEVMODE, iddig input pin is overridden with 0. avalid/bvalid/vbusvalid signals can be overridden respectively with: - GOTGCTL_AVALOEN + GOTGCTL_AVALOVAL - GOTGCTL_BVALOEN + GOTGCTL_BVALOVAL - GOTGCTL_VBVALEN + GOTGCTL_VBVALOVAL It is possible to determine valid sessions thanks to usb role switch: - if USB_ROLE_NONE then !avalid && !bvalid && !vbusvalid - if USB_ROLE_DEVICE then !avalid && bvalid && vbusvalid - if USB_ROLE_HOST then avalid && !bvalid && vbusvalid Acked-by: Minas Harutyunyan Signed-off-by: Amelie Delaunay Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/Kconfig | 1 + drivers/usb/dwc2/Makefile | 2 +- drivers/usb/dwc2/core.h | 8 ++ drivers/usb/dwc2/drd.c | 190 ++++++++++++++++++++++++++++++++++++ drivers/usb/dwc2/gadget.c | 2 +- drivers/usb/dwc2/platform.c | 13 +++ 6 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/dwc2/drd.c diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index 16e1aa304edc40..dceb8f32414e77 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -47,6 +47,7 @@ config USB_DWC2_PERIPHERAL config USB_DWC2_DUAL_ROLE bool "Dual Role mode" depends on (USB=y && USB_GADGET=y) || (USB_DWC2=m && USB && USB_GADGET) + select USB_ROLE_SWITCH help Select this option if you want the driver to work in a dual-role mode. In this mode both host and gadget features are enabled, and diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile index 440320cc20a470..2bcd6945df461a 100644 --- a/drivers/usb/dwc2/Makefile +++ b/drivers/usb/dwc2/Makefile @@ -3,7 +3,7 @@ ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_DWC2) += dwc2.o -dwc2-y := core.o core_intr.o platform.o +dwc2-y := core.o core_intr.o platform.o drd.o dwc2-y += params.o ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 9deff0400a9228..33e790ccefb360 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -860,6 +860,7 @@ struct dwc2_hregs_backup { * - USB_DR_MODE_PERIPHERAL * - USB_DR_MODE_HOST * - USB_DR_MODE_OTG + * @role_sw: usb_role_switch handle * @hcd_enabled: Host mode sub-driver initialization indicator. * @gadget_enabled: Peripheral mode sub-driver initialization indicator. * @ll_hw_enabled: Status of low-level hardware resources. @@ -1054,6 +1055,7 @@ struct dwc2_hsotg { struct dwc2_core_params params; enum usb_otg_state op_state; enum usb_dr_mode dr_mode; + struct usb_role_switch *role_sw; unsigned int hcd_enabled:1; unsigned int gadget_enabled:1; unsigned int ll_hw_enabled:1; @@ -1376,6 +1378,11 @@ static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg) return (dwc2_readl(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) == 0; } +int dwc2_drd_init(struct dwc2_hsotg *hsotg); +void dwc2_drd_suspend(struct dwc2_hsotg *hsotg); +void dwc2_drd_resume(struct dwc2_hsotg *hsotg); +void dwc2_drd_exit(struct dwc2_hsotg *hsotg); + /* * Dump core registers and SPRAM */ @@ -1392,6 +1399,7 @@ int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2); int dwc2_gadget_init(struct dwc2_hsotg *hsotg); void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset); +void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg); void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg); void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2); int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode); diff --git a/drivers/usb/dwc2/drd.c b/drivers/usb/dwc2/drd.c new file mode 100644 index 00000000000000..032efffa37ab13 --- /dev/null +++ b/drivers/usb/dwc2/drd.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drd.c - DesignWare USB2 DRD Controller Dual-role support + * + * Copyright (C) 2020 STMicroelectronics + * + * Author(s): Amelie Delaunay + */ + +#include +#include +#include +#include "core.h" + +static void dwc2_ovr_init(struct dwc2_hsotg *hsotg) +{ + unsigned long flags; + u32 gotgctl; + + spin_lock_irqsave(&hsotg->lock, flags); + + gotgctl = dwc2_readl(hsotg, GOTGCTL); + gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | GOTGCTL_VBVALOEN; + gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS; + gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL); + dwc2_writel(hsotg, gotgctl, GOTGCTL); + + dwc2_force_mode(hsotg, false); + + spin_unlock_irqrestore(&hsotg->lock, flags); +} + +static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid) +{ + u32 gotgctl = dwc2_readl(hsotg, GOTGCTL); + + /* Check if A-Session is already in the right state */ + if ((valid && (gotgctl & GOTGCTL_ASESVLD)) || + (!valid && !(gotgctl & GOTGCTL_ASESVLD))) + return -EALREADY; + + if (valid) + gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL; + else + gotgctl &= ~(GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL); + dwc2_writel(hsotg, gotgctl, GOTGCTL); + + return 0; +} + +static int dwc2_ovr_bvalid(struct dwc2_hsotg *hsotg, bool valid) +{ + u32 gotgctl = dwc2_readl(hsotg, GOTGCTL); + + /* Check if B-Session is already in the right state */ + if ((valid && (gotgctl & GOTGCTL_BSESVLD)) || + (!valid && !(gotgctl & GOTGCTL_BSESVLD))) + return -EALREADY; + + if (valid) + gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL; + else + gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL); + dwc2_writel(hsotg, gotgctl, GOTGCTL); + + return 0; +} + +static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role) +{ + struct dwc2_hsotg *hsotg = usb_role_switch_get_drvdata(sw); + unsigned long flags; + + /* Skip session not in line with dr_mode */ + if ((role == USB_ROLE_DEVICE && hsotg->dr_mode == USB_DR_MODE_HOST) || + (role == USB_ROLE_HOST && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)) + return -EINVAL; + + /* Skip session if core is in test mode */ + if (role == USB_ROLE_NONE && hsotg->test_mode) { + dev_dbg(hsotg->dev, "Core is in test mode\n"); + return -EBUSY; + } + + spin_lock_irqsave(&hsotg->lock, flags); + + if (role == USB_ROLE_HOST) { + if (dwc2_ovr_avalid(hsotg, true)) + goto unlock; + + if (hsotg->dr_mode == USB_DR_MODE_OTG) + /* + * This will raise a Connector ID Status Change + * Interrupt - connID A + */ + dwc2_force_mode(hsotg, true); + } else if (role == USB_ROLE_DEVICE) { + if (dwc2_ovr_bvalid(hsotg, true)) + goto unlock; + + if (hsotg->dr_mode == USB_DR_MODE_OTG) + /* + * This will raise a Connector ID Status Change + * Interrupt - connID B + */ + dwc2_force_mode(hsotg, false); + + /* This clear DCTL.SFTDISCON bit */ + dwc2_hsotg_core_connect(hsotg); + } else { + if (dwc2_is_device_mode(hsotg)) { + if (!dwc2_ovr_bvalid(hsotg, false)) + /* This set DCTL.SFTDISCON bit */ + dwc2_hsotg_core_disconnect(hsotg); + } else { + dwc2_ovr_avalid(hsotg, false); + } + } + +unlock: + spin_unlock_irqrestore(&hsotg->lock, flags); + + dev_dbg(hsotg->dev, "%s-session valid\n", + role == USB_ROLE_NONE ? "No" : + role == USB_ROLE_HOST ? "A" : "B"); + + return 0; +} + +int dwc2_drd_init(struct dwc2_hsotg *hsotg) +{ + struct usb_role_switch_desc role_sw_desc = {0}; + struct usb_role_switch *role_sw; + int ret; + + if (!device_property_read_bool(hsotg->dev, "usb-role-switch")) + return 0; + + role_sw_desc.driver_data = hsotg; + role_sw_desc.fwnode = dev_fwnode(hsotg->dev); + role_sw_desc.set = dwc2_drd_role_sw_set; + role_sw_desc.allow_userspace_control = true; + + role_sw = usb_role_switch_register(hsotg->dev, &role_sw_desc); + if (IS_ERR(role_sw)) { + ret = PTR_ERR(role_sw); + dev_err(hsotg->dev, + "failed to register role switch: %d\n", ret); + return ret; + } + + hsotg->role_sw = role_sw; + + /* Enable override and initialize values */ + dwc2_ovr_init(hsotg); + + return 0; +} + +void dwc2_drd_suspend(struct dwc2_hsotg *hsotg) +{ + u32 gintsts, gintmsk; + + if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) { + gintmsk = dwc2_readl(hsotg, GINTMSK); + gintmsk &= ~GINTSTS_CONIDSTSCHNG; + dwc2_writel(hsotg, gintmsk, GINTMSK); + gintsts = dwc2_readl(hsotg, GINTSTS); + dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS); + } +} + +void dwc2_drd_resume(struct dwc2_hsotg *hsotg) +{ + u32 gintsts, gintmsk; + + if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) { + gintsts = dwc2_readl(hsotg, GINTSTS); + dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS); + gintmsk = dwc2_readl(hsotg, GINTMSK); + gintmsk |= GINTSTS_CONIDSTSCHNG; + dwc2_writel(hsotg, gintmsk, GINTMSK); + } +} + +void dwc2_drd_exit(struct dwc2_hsotg *hsotg) +{ + if (hsotg->role_sw) + usb_role_switch_unregister(hsotg->role_sw); +} diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 5b9d23991c99dd..16c5f976f1413a 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3530,7 +3530,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, dwc2_readl(hsotg, DOEPCTL0)); } -static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) +void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) { /* set the soft-disconnect bit */ dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON); diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index cb8ddbd537187d..8fea5f1f60abf6 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -317,6 +317,8 @@ static int dwc2_driver_remove(struct platform_device *dev) if (hsotg->params.activate_stm_id_vb_detection) regulator_disable(hsotg->usb33d); + dwc2_drd_exit(hsotg); + if (hsotg->ll_hw_enabled) dwc2_lowlevel_hw_disable(hsotg); @@ -533,6 +535,13 @@ static int dwc2_driver_probe(struct platform_device *dev) dwc2_writel(hsotg, ggpio, GGPIO); } + retval = dwc2_drd_init(hsotg); + if (retval) { + if (retval != -EPROBE_DEFER) + dev_err(hsotg->dev, "failed to initialize dual-role\n"); + goto error_init; + } + if (hsotg->dr_mode != USB_DR_MODE_HOST) { retval = dwc2_gadget_init(hsotg); if (retval) @@ -606,6 +615,8 @@ static int __maybe_unused dwc2_suspend(struct device *dev) if (is_device_mode) dwc2_hsotg_suspend(dwc2); + dwc2_drd_suspend(dwc2); + if (dwc2->params.activate_stm_id_vb_detection) { unsigned long flags; u32 ggpio, gotgctl; @@ -686,6 +697,8 @@ static int __maybe_unused dwc2_resume(struct device *dev) /* Need to restore FORCEDEVMODE/FORCEHOSTMODE */ dwc2_force_dr_mode(dwc2); + dwc2_drd_resume(dwc2); + if (dwc2_is_device_mode(dwc2)) ret = dwc2_hsotg_resume(dwc2); From 916f8b627288039d9e771a9b2ab1b3c79b303039 Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Tue, 16 Jun 2020 16:07:16 +0200 Subject: [PATCH 02/38] usb: dwc2: don't use ID/Vbus detection if usb-role-switch on STM32MP15 SoCs If usb-role-switch is present in the device tree, it means that ID and Vbus signals are not connected to the OTG controller but to an external component (GPIOs, Type-C controller). In this configuration, usb role switch is used to force valid sessions on STM32MP15 SoCs. Acked-by: Minas Harutyunyan Signed-off-by: Amelie Delaunay Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/params.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 8f9d061c4d5fa6..bd792c9cdef987 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -183,9 +183,11 @@ static void dwc2_set_stm32mp15_fsotg_params(struct dwc2_hsotg *hsotg) static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg) { struct dwc2_core_params *p = &hsotg->params; + struct device_node *np = hsotg->dev->of_node; p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; - p->activate_stm_id_vb_detection = true; + p->activate_stm_id_vb_detection = + !of_property_read_bool(np, "usb-role-switch"); p->host_rx_fifo_size = 440; p->host_nperio_tx_fifo_size = 256; p->host_perio_tx_fifo_size = 256; From 7a410953d1fb4dbe91ffcfdee9cbbf889d19b0d7 Mon Sep 17 00:00:00 2001 From: Dan Robertson Date: Mon, 13 Jul 2020 12:05:22 -0400 Subject: [PATCH 03/38] usb: dwc3: meson-g12a: fix shared reset control use The reset is a shared reset line, but reset_control_reset is still used and reset_control_deassert is not guaranteed to have been called before the first reset_control_assert call. When suspending the following warning may be seen: WARNING: CPU: 1 PID: 5530 at drivers/reset/core.c:355 reset_control_assert+0x184/0x19c Hardware name: Hardkernel ODROID-N2 (DT) [..] pc : reset_control_assert+0x184/0x19c lr : dwc3_meson_g12a_suspend+0x68/0x7c [..] Call trace: reset_control_assert+0x184/0x19c dwc3_meson_g12a_suspend+0x68/0x7c platform_pm_suspend+0x28/0x54 __device_suspend+0x590/0xabc dpm_suspend+0x104/0x404 dpm_suspend_start+0x84/0x1bc suspend_devices_and_enter+0xc4/0x4fc pm_suspend+0x198/0x2d4 Fixes: 6d9fa35a347a87 ("usb: dwc3: meson-g12a: get the reset as shared") Signed-off-by: Dan Robertson Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-meson-g12a.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 1f7f4d88ed9d8d..88b75b5a039c94 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -737,13 +737,13 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) goto err_disable_clks; } - ret = reset_control_reset(priv->reset); + ret = reset_control_deassert(priv->reset); if (ret) - goto err_disable_clks; + goto err_assert_reset; ret = dwc3_meson_g12a_get_phys(priv); if (ret) - goto err_disable_clks; + goto err_assert_reset; ret = priv->drvdata->setup_regmaps(priv, base); if (ret) @@ -752,7 +752,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) if (priv->vbus) { ret = regulator_enable(priv->vbus); if (ret) - goto err_disable_clks; + goto err_assert_reset; } /* Get dr_mode */ @@ -765,13 +765,13 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) ret = priv->drvdata->usb_init(priv); if (ret) - goto err_disable_clks; + goto err_assert_reset; /* Init PHYs */ for (i = 0 ; i < PHY_COUNT ; ++i) { ret = phy_init(priv->phys[i]); if (ret) - goto err_disable_clks; + goto err_assert_reset; } /* Set PHY Power */ @@ -809,6 +809,9 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) for (i = 0 ; i < PHY_COUNT ; ++i) phy_exit(priv->phys[i]); +err_assert_reset: + reset_control_assert(priv->reset); + err_disable_clks: clk_bulk_disable_unprepare(priv->drvdata->num_clks, priv->drvdata->clks); From f4cfe5ce607dd87873956453a7775c102a18fc62 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 17 Jul 2020 18:13:17 +0800 Subject: [PATCH 04/38] usb: cdns3: gadget: improve the set_configuration handling - Delete the duplicated EP_CMD_ERDY and EP_CMD_REQ_CMPL setting - Prepare the next setup before setting EP_CMD_ERDY and EP_CMD_REQ_CMPL, it could avoid a bug that DMA hang at EP0 OUT for DEV_VER_NXP_V1 - Delete the duplicated cdns3_set_hw_configuration calling at cdns3_req_ep0_set_configuration, the composite.c will handle this request, and call .ep0_queue back, and at .ep_queue it will call cdns3_set_hw_configuration. - Move cdns3_allow_enable_l1 into cdns3_set_hw_configuration since it is part of this function. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/ep0.c | 15 +++------------ drivers/usb/cdns3/gadget.c | 3 ++- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c index 88af81bc10a352..1530edd5838c8c 100644 --- a/drivers/usb/cdns3/ep0.c +++ b/drivers/usb/cdns3/ep0.c @@ -161,13 +161,12 @@ static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev, if (result) return result; - if (config) { - cdns3_set_hw_configuration(priv_dev); - } else { + if (!config) { cdns3_hw_reset_eps_config(priv_dev); usb_gadget_set_state(&priv_dev->gadget, USB_STATE_ADDRESS); } + break; case USB_STATE_CONFIGURED: result = cdns3_ep0_delegate_req(priv_dev, ctrl_req); @@ -707,7 +706,6 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep, struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep); struct cdns3_device *priv_dev = priv_ep->cdns3_dev; unsigned long flags; - int erdy_sent = 0; int ret = 0; u8 zlp = 0; @@ -723,15 +721,8 @@ 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) { cdns3_select_ep(priv_dev, 0x00); - - erdy_sent = !priv_dev->hw_configured_flag; cdns3_set_hw_configuration(priv_dev); - - if (!erdy_sent) - cdns3_ep0_complete_setup(priv_dev, 0, 1); - - cdns3_allow_enable_l1(priv_dev, 1); - + cdns3_ep0_complete_setup(priv_dev, 0, 1); request->actual = 0; priv_dev->status_completion_no_call = true; priv_dev->pending_status_request = request; diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index d9dde624636ba3..5b4b16bf416493 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -1315,7 +1315,6 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) return; writel(USB_CONF_CFGSET, &priv_dev->regs->usb_conf); - writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); cdns3_set_register_bit(&priv_dev->regs->usb_conf, USB_CONF_U1EN | USB_CONF_U2EN); @@ -1332,6 +1331,8 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev) cdns3_start_all_request(priv_dev, priv_ep); } } + + cdns3_allow_enable_l1(priv_dev, 1); } /** From 37d9453b052dec46f6439658536ca8f301566559 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 16 Jul 2020 13:55:25 +0200 Subject: [PATCH 05/38] usb: gadget: udc: Flush pending work also in error path When binding an UDC driver to the pending gadget fails in check_pending_gadget_drivers(), the usb_add_gadget_udc_release() function ends without waiting for the usb_gadget_state_work to finish, what in turn might cause the whole struct usb_gadget being freed by the caller before the usb_gadget_state_work being executed. This can be observed on some boards with USB Mass Storage gadget compiled-in and kernel booted without the needed module parameters: dwc2 12480000.hsotg: dwc2_check_params: Invalid parameter besl=1 dwc2 12480000.hsotg: dwc2_check_params: Invalid parameter g_np_tx_fifo_size=1024 dwc2 12480000.hsotg: EPs: 16, dedicated fifos, 7808 entries in SPRAM Mass Storage Function, version: 2009/09/11 LUN: removable file: (no medium) no file given for LUN0 g_mass_storage 12480000.hsotg: failed to start g_mass_storage: -22 dwc2: probe of 12480000.hsotg failed with error -22 8<--- cut here --- Unable to handle kernel NULL pointer dereference at virtual address 00000004 pgd = (ptrval) [00000004] *pgd=00000000 Internal error: Oops: 5 [#1] PREEMPT SMP ARM Modules linked in: CPU: 1 PID: 88 Comm: kworker/1:2 Not tainted 5.8.0-rc5-next-20200715-00062-gc5bb489ae825-dirty #8792 Hardware name: Samsung Exynos (Flattened Device Tree) Workqueue: 0x0 (rcu_gp) PC is at process_one_work+0x44/0x7dc ... Process kworker/1:2 (pid: 88, stack limit = 0x(ptrval)) Stack: (0xed9f1f00 to 0xed9f2000) ... [] (process_one_work) from [] (worker_thread+0x44/0x51c) [] (worker_thread) from [] (kthread+0x158/0x1a0) [] (kthread) from [] (ret_from_fork+0x14/0x20) Exception stack(0xed9f1fb0 to 0xed9f1ff8) ... ---[ end trace 5033c1326a62e5f3 ]--- note: kworker/1:2[88] exited with preempt_count 1 Fix this by flushing pending work in error path. Reviewed-by: Peter Chen Signed-off-by: Marek Szyprowski Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index c33ad8a333ad9a..4f82bcd31fd3f6 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1230,6 +1230,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, return 0; err_del_udc: + flush_work(&gadget->work); device_del(&udc->dev); err_unlist_udc: From abac8a85c8196c2a098048ad78552c8b1a236c4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Wed, 8 Jul 2020 20:04:10 +0200 Subject: [PATCH 06/38] usb: gadget: udc: atmel: implement .pullup callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement udc->pullup callback, so that udc_connect/disconnect work. This is needed for composite gadget, as it assumes udc_disconnect() actually works and calls its ->disconnect callback. Acked-by: Cristian Birsan Acked-by: Alexandre Belloni Signed-off-by: Michał Mirosław Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 5ca75e69e251b8..fa6793065c7c98 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -1028,6 +1028,7 @@ usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) return 0; } +static int atmel_usba_pullup(struct usb_gadget *gadget, int is_on); static int atmel_usba_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver); static int atmel_usba_stop(struct usb_gadget *gadget); @@ -1101,6 +1102,7 @@ static const struct usb_gadget_ops usba_udc_ops = { .get_frame = usba_udc_get_frame, .wakeup = usba_udc_wakeup, .set_selfpowered = usba_udc_set_selfpowered, + .pullup = atmel_usba_pullup, .udc_start = atmel_usba_start, .udc_stop = atmel_usba_stop, .match_ep = atmel_usba_match_ep, @@ -1957,6 +1959,24 @@ static irqreturn_t usba_vbus_irq_thread(int irq, void *devid) return IRQ_HANDLED; } +static int atmel_usba_pullup(struct usb_gadget *gadget, int is_on) +{ + struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget); + unsigned long flags; + u32 ctrl; + + spin_lock_irqsave(&udc->lock, flags); + ctrl = usba_readl(udc, CTRL); + if (is_on) + ctrl &= ~USBA_DETACH; + else + ctrl |= USBA_DETACH; + usba_writel(udc, CTRL, ctrl); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + static int atmel_usba_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { From 33a06f1300a79cfd461cea0268f05e969d4f34ec Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 16 Jul 2020 14:09:48 +0200 Subject: [PATCH 07/38] usb: dwc2: Fix error path in gadget registration When gadget registration fails, one should not call usb_del_gadget_udc(). Ensure this by setting gadget->udc to NULL. Also in case of a failure there is no need to disable low-level hardware, so return immiedetly instead of jumping to error_init label. This fixes the following kernel NULL ptr dereference on gadget failure (can be easily triggered with g_mass_storage without any module parameters): dwc2 12480000.hsotg: dwc2_check_params: Invalid parameter besl=1 dwc2 12480000.hsotg: dwc2_check_params: Invalid parameter g_np_tx_fifo_size=1024 dwc2 12480000.hsotg: EPs: 16, dedicated fifos, 7808 entries in SPRAM Mass Storage Function, version: 2009/09/11 LUN: removable file: (no medium) no file given for LUN0 g_mass_storage 12480000.hsotg: failed to start g_mass_storage: -22 8<--- cut here --- Unable to handle kernel NULL pointer dereference at virtual address 00000104 pgd = (ptrval) [00000104] *pgd=00000000 Internal error: Oops: 805 [#1] PREEMPT SMP ARM Modules linked in: CPU: 0 PID: 12 Comm: kworker/0:1 Not tainted 5.8.0-rc5 #3133 Hardware name: Samsung Exynos (Flattened Device Tree) Workqueue: events deferred_probe_work_func PC is at usb_del_gadget_udc+0x38/0xc4 LR is at __mutex_lock+0x31c/0xb18 ... Process kworker/0:1 (pid: 12, stack limit = 0x(ptrval)) Stack: (0xef121db0 to 0xef122000) ... [] (usb_del_gadget_udc) from [] (dwc2_hsotg_remove+0x10/0x20) [] (dwc2_hsotg_remove) from [] (dwc2_driver_probe+0x57c/0x69c) [] (dwc2_driver_probe) from [] (platform_drv_probe+0x6c/0xa4) [] (platform_drv_probe) from [] (really_probe+0x200/0x48c) [] (really_probe) from [] (driver_probe_device+0x78/0x1fc) [] (driver_probe_device) from [] (bus_for_each_drv+0x74/0xb8) [] (bus_for_each_drv) from [] (__device_attach+0xd4/0x16c) [] (__device_attach) from [] (bus_probe_device+0x88/0x90) [] (bus_probe_device) from [] (deferred_probe_work_func+0x3c/0xd0) [] (deferred_probe_work_func) from [] (process_one_work+0x234/0x7dc) [] (process_one_work) from [] (worker_thread+0x44/0x51c) [] (worker_thread) from [] (kthread+0x158/0x1a0) [] (kthread) from [] (ret_from_fork+0x14/0x20) Exception stack(0xef121fb0 to 0xef121ff8) ... ---[ end trace 9724c2fc7cc9c982 ]--- While fixing this also fix the double call to dwc2_lowlevel_hw_disable() if dr_mode is set to USB_DR_MODE_PERIPHERAL. In such case low-level hardware is already disabled before calling usb_add_gadget_udc(). That function correctly preserves low-level hardware state, there is no need for the second unconditional dwc2_lowlevel_hw_disable() call. Fixes: 207324a321a8 ("usb: dwc2: Postponed gadget registration to the udc class driver") Acked-by: Minas Harutyunyan Signed-off-by: Marek Szyprowski Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/platform.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 8fea5f1f60abf6..68b56b43a45e6c 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -591,6 +591,7 @@ static int dwc2_driver_probe(struct platform_device *dev) if (hsotg->gadget_enabled) { retval = usb_add_gadget_udc(hsotg->dev, &hsotg->gadget); if (retval) { + hsotg->gadget.udc = NULL; dwc2_hsotg_remove(hsotg); goto error_init; } @@ -602,7 +603,8 @@ static int dwc2_driver_probe(struct platform_device *dev) if (hsotg->params.activate_stm_id_vb_detection) regulator_disable(hsotg->usb33d); error: - dwc2_lowlevel_hw_disable(hsotg); + if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) + dwc2_lowlevel_hw_disable(hsotg); return retval; } From 75ae051efc9b3cceaed525e7ecb9cf19780e0af5 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Mon, 13 Jul 2020 14:48:01 +0800 Subject: [PATCH 08/38] usb: gadget: bdc: use readl_poll_timeout() to simplify code Use readl_poll_timeout() to poll register status Cc: Florian Fainelli Reviewed-by: Stephen Boyd Signed-off-by: Chunfeng Yun Reviewed-by: Florian Fainelli Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc_core.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 02a3a774670b16..d567e20234f0aa 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -29,24 +30,19 @@ #include "bdc_dbg.h" /* Poll till controller status is not OIP */ -static int poll_oip(struct bdc *bdc, int usec) +static int poll_oip(struct bdc *bdc, u32 usec) { u32 status; - /* Poll till STS!= OIP */ - while (usec) { - status = bdc_readl(bdc->regs, BDC_BDCSC); - if (BDC_CSTS(status) != BDC_OIP) { - dev_dbg(bdc->dev, - "poll_oip complete status=%d", - BDC_CSTS(status)); - return 0; - } - udelay(10); - usec -= 10; - } - dev_err(bdc->dev, "Err: operation timedout BDCSC: 0x%08x\n", status); + int ret; - return -ETIMEDOUT; + ret = readl_poll_timeout(bdc->regs + BDC_BDCSC, status, + (BDC_CSTS(status) != BDC_OIP), 10, usec); + if (ret) + dev_err(bdc->dev, "operation timedout BDCSC: 0x%08x\n", status); + else + dev_dbg(bdc->dev, "%s complete status=%d", __func__, BDC_CSTS(status)); + + return ret; } /* Stop the BDC controller */ From ae90cc8237bf4ee416a326c51052ddcc7918ee43 Mon Sep 17 00:00:00 2001 From: Evgeny Novikov Date: Wed, 15 Jul 2020 19:40:09 +0300 Subject: [PATCH 09/38] usb: gadget: net2272: skip BAR1 on error handling paths in probe net2272_rdk1_probe() skips "i == 1" (BAR1) during allocation of resources. The patch does this on error hanling paths as well. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Evgeny Novikov Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2272.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index fbbe6251354551..44d1ea2307bbe6 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -2370,6 +2370,8 @@ net2272_rdk1_probe(struct pci_dev *pdev, struct net2272 *dev) err: while (--i >= 0) { + if (i == 1) + continue; /* BAR1 unused */ iounmap(mem_mapped_addr[i]); release_mem_region(pci_resource_start(pdev, i), pci_resource_len(pdev, i)); From 33c4b00b85a9855de47e498a15e21515320cc442 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 22 Jul 2020 12:29:52 +0800 Subject: [PATCH 10/38] usb: cdns3: ep0: delete the unnecessary operation It doesn't need to enable/disable L1 on the fly for EP0 transfer, we only need to enable L1 after SET_CONFIGURATION. This code may be introduced by careless. Cc: Pawel Laszczak Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/ep0.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c index 1530edd5838c8c..d9779abc65b2b7 100644 --- a/drivers/usb/cdns3/ep0.c +++ b/drivers/usb/cdns3/ep0.c @@ -123,8 +123,6 @@ static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev, priv_dev->ep0_stage = CDNS3_SETUP_STAGE; writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd); - - cdns3_allow_enable_l1(priv_dev, 1); } /** @@ -639,7 +637,6 @@ void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir) if (priv_dev->wait_for_setup && ep_sts_reg & EP_STS_IOC) { priv_dev->wait_for_setup = 0; - cdns3_allow_enable_l1(priv_dev, 0); cdns3_ep0_setup_phase(priv_dev); } else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) { priv_dev->ep0_data_dir = dir; From 95f5acfc4f58f01a22b66d8c9c0ffb72aa96271c Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 22 Jul 2020 11:06:19 +0800 Subject: [PATCH 11/38] usb: cdns3: gadget: always zeroed TRB buffer when enable endpoint During the endpoint dequeue operation, it changes dequeued TRB as link TRB, when the endpoint is disabled and re-enabled, the DMA fetches the TRB before the link TRB, after it handles current TRB, the DMA pointer will advance to the TRB after link TRB, but enqueue and dequene variables don't know it due to no hardware interrupt at the time, when the next TRB is added to link TRB position, the DMA will not handle this TRB due to its pointer is already at the next TRB. See the trace log like below: file-storage-675 [001] d..1 86.585657: usb_ep_queue: ep0: req 00000000df9b3a4f length 0/0 sgs 0/0 stream 0 zsI status 0 --> 0 file-storage-675 [001] d..1 86.585663: cdns3_ep_queue: ep1out: req: 000000002ebce364, req buff 00000000f5bc96b4, length: 0/1024 zsi, status: -115, trb: [start:0, end:0: virt addr (null)], flags:0 SID: 0 file-storage-675 [001] d..1 86.585671: cdns3_prepare_trb: ep1out: trb 000000007f770303, dma buf: 0xbd195800, size: 1024, burst: 128 ctrl: 0x00000425 (C=1, T=0, ISP, IOC, Normal) SID:0 LAST_SID:0 file-storage-675 [001] d..1 86.585676: cdns3_ring: Ring contents for ep1out: Ring deq index: 0, trb: 000000007f770303 (virt), 0xc4003000 (dma) Ring enq index: 1, trb: 0000000049c1ba21 (virt), 0xc400300c (dma) free trbs: 38, CCS=1, PCS=1 @0x00000000c4003000 bd195800 80020400 00000425 @0x00000000c400300c c4003018 80020400 00001811 @0x00000000c4003018 bcfcc000 0000001f 00000426 @0x00000000c4003024 bcfce800 0000001f 00000426 ... irq/144-5b13000-698 [000] d... 87.619286: usb_gadget_giveback_request: ep1in: req 0000000031b832eb length 13/13 sgs 0/0 stream 0 zsI status 0 --> 0 file-storage-675 [001] d..1 87.619287: cdns3_ep_queue: ep1out: req: 000000002ebce364, req buff 00000000f5bc96b4, length: 0/1024 zsi, status: -115, trb: [start:0, end:0: virt addr 0x80020400c400300c], flags:0 SID: 0 file-storage-675 [001] d..1 87.619294: cdns3_prepare_trb: ep1out: trb 0000000049c1ba21, dma buf: 0xbd198000, size: 1024, burst: 128 ctrl: 0x00000425 (C=1, T=0, ISP, IOC, Normal) SID:0 LAST_SID:0 file-storage-675 [001] d..1 87.619297: cdns3_ring: Ring contents for ep1out: Ring deq index: 1, trb: 0000000049c1ba21 (virt), 0xc400300c (dma) Ring enq index: 2, trb: 0000000059b34b67 (virt), 0xc4003018 (dma) free trbs: 38, CCS=1, PCS=1 @0x00000000c4003000 bd195800 0000001f 00000427 @0x00000000c400300c bd198000 80020400 00000425 @0x00000000c4003018 bcfcc000 0000001f 00000426 @0x00000000c4003024 bcfce800 0000001f 00000426 ... file-storage-675 [001] d..1 87.619305: cdns3_doorbell_epx: ep1out, ep_trbaddr c4003018 file-storage-675 [001] .... 87.619308: usb_ep_queue: ep1out: req 000000002ebce364 length 0/1024 sgs 0/0 stream 0 zsI status -115 --> 0 irq/144-5b13000-698 [000] d..1 87.619315: cdns3_epx_irq: IRQ for ep1out: 01000c80 TRBERR , ep_traddr: c4003018 ep_last_sid: 00000000 use_streams: 0 irq/144-5b13000-698 [000] d..1 87.619395: cdns3_usb_irq: IRQ 00000008 = Hot Reset Fixes: f616c3bda47e ("usb: cdns3: Fix dequeue implementation") Cc: stable Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/gadget.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 5b4b16bf416493..65a154d47f8ead 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -242,9 +242,10 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep) return -ENOMEM; priv_ep->alloc_ring_size = ring_size; - memset(priv_ep->trb_pool, 0, ring_size); } + memset(priv_ep->trb_pool, 0, ring_size); + priv_ep->num_trbs = num_trbs; if (!priv_ep->num) From b20aecff99a2335731bb2ae69c8368dfc356aef7 Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 13 Jul 2020 12:05:46 +0200 Subject: [PATCH 12/38] usb: cdns3: core: removed cdns3_get_current_role_driver function Function is not used in driver so it can be removed. Signed-off-by: Pawel Laszczak Reviewed-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/core.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 59e5e213a99bbd..1f77fb5aefbf4a 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -27,13 +27,6 @@ static int cdns3_idle_init(struct cdns3 *cdns); -static inline -struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns) -{ - WARN_ON(!cdns->roles[cdns->role]); - return cdns->roles[cdns->role]; -} - static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role) { int ret; From 27afe1661275213adee3f8dc08b3610abcbca678 Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 13 Jul 2020 12:05:47 +0200 Subject: [PATCH 13/38] usb: cdns3: drd: removed not needed variables initialization Patch remove some variables initialization from core.c and drd.c file. Reviewed-by: Peter Chen Signed-off-by: Pawel Laszczak Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/core.c | 4 ++-- drivers/usb/cdns3/drd.c | 19 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 1f77fb5aefbf4a..c498b585eb1321 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -86,7 +86,7 @@ static int cdns3_core_init_role(struct cdns3 *cdns) struct device *dev = cdns->dev; enum usb_dr_mode best_dr_mode; enum usb_dr_mode dr_mode; - int ret = 0; + int ret; dr_mode = usb_get_dr_mode(dev); cdns->role = USB_ROLE_NONE; @@ -177,7 +177,7 @@ static int cdns3_core_init_role(struct cdns3 *cdns) goto err; } - return ret; + return 0; err: cdns3_exit_roles(cdns); return ret; diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index 58089841ed52d0..4939a568d8a25b 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -29,7 +29,6 @@ */ int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode) { - int ret = 0; u32 reg; switch (mode) { @@ -61,7 +60,7 @@ int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode) return -EINVAL; } - return ret; + return 0; } int cdns3_get_id(struct cdns3 *cdns) @@ -134,11 +133,11 @@ static void cdns3_otg_enable_irq(struct cdns3 *cdns) int cdns3_drd_switch_host(struct cdns3 *cdns, int on) { int ret, val; - u32 reg = OTGCMD_OTG_DIS; /* switch OTG core */ if (on) { - writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd); + writel(OTGCMD_HOST_BUS_REQ | OTGCMD_OTG_DIS, + &cdns->otg_regs->cmd); dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n"); ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val, @@ -212,7 +211,7 @@ int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on) */ static int cdns3_init_otg_mode(struct cdns3 *cdns) { - int ret = 0; + int ret; cdns3_otg_disable_irq(cdns); /* clear all interrupts */ @@ -223,7 +222,8 @@ static int cdns3_init_otg_mode(struct cdns3 *cdns) return ret; cdns3_otg_enable_irq(cdns); - return ret; + + return 0; } /** @@ -234,7 +234,7 @@ static int cdns3_init_otg_mode(struct cdns3 *cdns) */ int cdns3_drd_update_mode(struct cdns3 *cdns) { - int ret = 0; + int ret; switch (cdns->dr_mode) { case USB_DR_MODE_PERIPHERAL: @@ -307,8 +307,8 @@ static irqreturn_t cdns3_drd_irq(int irq, void *data) int cdns3_drd_init(struct cdns3 *cdns) { void __iomem *regs; - int ret = 0; u32 state; + int ret; regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res); if (IS_ERR(regs)) @@ -359,7 +359,6 @@ int cdns3_drd_init(struct cdns3 *cdns) cdns3_drd_thread_irq, IRQF_SHARED, dev_name(cdns->dev), cdns); - if (ret) { dev_err(cdns->dev, "couldn't get otg_irq\n"); return ret; @@ -371,7 +370,7 @@ int cdns3_drd_init(struct cdns3 *cdns) return -ENODEV; } - return ret; + return 0; } int cdns3_drd_exit(struct cdns3 *cdns) From ecf4f823fb7050e4f450bb56cd403cbb6d41ca7c Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 13 Jul 2020 12:05:48 +0200 Subject: [PATCH 14/38] usb: cnds3: drd: deleted != Patch deletes unnecessary != from condition statement in cdns3_drd_init function. Reviewed-by: Peter Chen Signed-off-by: Pawel Laszczak Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/drd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index 4939a568d8a25b..6d2da504ad4910 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -365,7 +365,7 @@ int cdns3_drd_init(struct cdns3 *cdns) } state = readl(&cdns->otg_regs->sts); - if (OTGSTS_OTG_NRDY(state) != 0) { + if (OTGSTS_OTG_NRDY(state)) { dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n"); return -ENODEV; } From 03cce68a828dba13b3715033e5133839820218c3 Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 13 Jul 2020 12:05:49 +0200 Subject: [PATCH 15/38] usb: cdns3: drd: return IRQ_NONE explicitly. IRQ_NONE can be returned indirect. Reviewed-by: Peter Chen Signed-off-by: Pawel Laszczak Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/drd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index 6d2da504ad4910..05a9f7d54c46dc 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -279,12 +279,12 @@ static irqreturn_t cdns3_drd_irq(int irq, void *data) u32 reg; if (cdns->dr_mode != USB_DR_MODE_OTG) - return ret; + return IRQ_NONE; reg = readl(&cdns->otg_regs->ivect); if (!reg) - return ret; + return IRQ_NONE; if (reg & OTGIEN_ID_CHANGE_INT) { dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n", From 245258495a5157e4428bd944189e4062d852dcdd Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 13 Jul 2020 12:05:50 +0200 Subject: [PATCH 16/38] usb: cdns3: drd: changed return type from int to bool Patch changes return type from int to bool for cdns3_is_host and cdns3_is_device functions. Reviewed-by: Peter Chen Signed-off-by: Pawel Laszczak Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/drd.c | 16 ++++++++-------- drivers/usb/cdns3/drd.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index 05a9f7d54c46dc..6fe092c828b3a2 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -83,25 +83,25 @@ int cdns3_get_vbus(struct cdns3 *cdns) return vbus; } -int cdns3_is_host(struct cdns3 *cdns) +bool cdns3_is_host(struct cdns3 *cdns) { if (cdns->dr_mode == USB_DR_MODE_HOST) - return 1; + return true; else if (!cdns3_get_id(cdns)) - return 1; + return true; - return 0; + return false; } -int cdns3_is_device(struct cdns3 *cdns) +bool cdns3_is_device(struct cdns3 *cdns) { if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL) - return 1; + return true; else if (cdns->dr_mode == USB_DR_MODE_OTG) if (cdns3_get_id(cdns)) - return 1; + return true; - return 0; + return false; } /** diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h index 04e01c4d237757..35b6d459ee58c1 100644 --- a/drivers/usb/cdns3/drd.h +++ b/drivers/usb/cdns3/drd.h @@ -153,8 +153,8 @@ struct cdns3_otg_common_regs { /* Only for CDNS3_CONTROLLER_V0 version */ #define OVERRIDE_IDPULLUP_V0 BIT(24) -int cdns3_is_host(struct cdns3 *cdns); -int cdns3_is_device(struct cdns3 *cdns); +bool cdns3_is_host(struct cdns3 *cdns); +bool cdns3_is_device(struct cdns3 *cdns); int cdns3_get_id(struct cdns3 *cdns); int cdns3_get_vbus(struct cdns3 *cdns); int cdns3_drd_init(struct cdns3 *cdns); From 08c35dd3cc2184dcc952fc845329dcae1ee302d3 Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 13 Jul 2020 12:05:51 +0200 Subject: [PATCH 17/38] usb: cdns3: Added CDNS3_ID_PERIPHERAL and CDNS3_ID_HOST Patch adds 2 definitions that make it easier to understand the code. Reviewed-by: Peter Chen Signed-off-by: Pawel Laszczak Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/drd.c | 4 ++-- drivers/usb/cdns3/drd.h | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index 6fe092c828b3a2..8e7673da905ed6 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -87,7 +87,7 @@ bool cdns3_is_host(struct cdns3 *cdns) { if (cdns->dr_mode == USB_DR_MODE_HOST) return true; - else if (!cdns3_get_id(cdns)) + else if (cdns3_get_id(cdns) == CDNS3_ID_HOST) return true; return false; @@ -98,7 +98,7 @@ bool cdns3_is_device(struct cdns3 *cdns) if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL) return true; else if (cdns->dr_mode == USB_DR_MODE_OTG) - if (cdns3_get_id(cdns)) + if (cdns3_get_id(cdns) == CDNS3_ID_PERIPHERAL) return true; return false; diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h index 35b6d459ee58c1..3889fead9df196 100644 --- a/drivers/usb/cdns3/drd.h +++ b/drivers/usb/cdns3/drd.h @@ -153,6 +153,9 @@ struct cdns3_otg_common_regs { /* Only for CDNS3_CONTROLLER_V0 version */ #define OVERRIDE_IDPULLUP_V0 BIT(24) +#define CDNS3_ID_PERIPHERAL 1 +#define CDNS3_ID_HOST 0 + bool cdns3_is_host(struct cdns3 *cdns); bool cdns3_is_device(struct cdns3 *cdns); int cdns3_get_id(struct cdns3 *cdns); From 5c2cf30f14cc3973f2d288e33b0153f5d9a357fb Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 13 Jul 2020 12:05:52 +0200 Subject: [PATCH 18/38] usb: cdns3: core: removed 'goto not_otg' Patch removes 'goto not_otg' instruction from cdns3_hw_role_state_machine function. Signed-off-by: Pawel Laszczak Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/core.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index c498b585eb1321..8e3996f211a851 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -191,11 +191,17 @@ static int cdns3_core_init_role(struct cdns3 *cdns) */ static enum usb_role cdns3_hw_role_state_machine(struct cdns3 *cdns) { - enum usb_role role; + enum usb_role role = USB_ROLE_NONE; int id, vbus; - if (cdns->dr_mode != USB_DR_MODE_OTG) - goto not_otg; + if (cdns->dr_mode != USB_DR_MODE_OTG) { + if (cdns3_is_host(cdns)) + role = USB_ROLE_HOST; + if (cdns3_is_device(cdns)) + role = USB_ROLE_DEVICE; + + return role; + } id = cdns3_get_id(cdns); vbus = cdns3_get_vbus(cdns); @@ -232,14 +238,6 @@ static enum usb_role cdns3_hw_role_state_machine(struct cdns3 *cdns) dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role); return role; - -not_otg: - if (cdns3_is_host(cdns)) - role = USB_ROLE_HOST; - if (cdns3_is_device(cdns)) - role = USB_ROLE_DEVICE; - - return role; } static int cdns3_idle_role_start(struct cdns3 *cdns) From f41ca26b8b178d0b7005c9bfeda88814179203b3 Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 13 Jul 2020 12:05:53 +0200 Subject: [PATCH 19/38] usb: cdns3: core: removed overwriting some error code Some error code can be preserved, so we can remove overwriting error code returned by some functions. Signed-off-by: Pawel Laszczak Reviewed-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/core.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 8e3996f211a851..72885c5edb09b1 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -347,7 +347,6 @@ static int cdns3_role_set(struct usb_role_switch *sw, enum usb_role role) case USB_ROLE_HOST: break; default: - ret = -EPERM; goto pm_put; } } @@ -358,17 +357,14 @@ static int cdns3_role_set(struct usb_role_switch *sw, enum usb_role role) case USB_ROLE_DEVICE: break; default: - ret = -EPERM; goto pm_put; } } cdns3_role_stop(cdns); ret = cdns3_role_start(cdns, role); - if (ret) { + if (ret) dev_err(cdns->dev, "set role %d has failed\n", role); - ret = -EPERM; - } pm_put: pm_runtime_put_sync(cdns->dev); @@ -393,7 +389,7 @@ static int cdns3_probe(struct platform_device *pdev) ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (ret) { dev_err(dev, "error setting dma mask: %d\n", ret); - return -ENODEV; + return ret; } cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL); From b2aeb6da3d6ee562e57ad3a7d73a7de0c36ce8eb Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 13 Jul 2020 12:05:54 +0200 Subject: [PATCH 20/38] usb: cdns3: drd: simplify *switch_gadet and *switch_host Patch split function cdns3_drd_switch_gadget and cdns3_drd_switch_host into: - cdns3_drd_host_on - cdns3_drd_host_off - cdns3_drd_gadget_on - cdns3_drd_gadgett_off These functions don't have any shared code so it's better to have smaller, faster and easier functions. Signed-off-by: Pawel Laszczak Reviewed-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/drd.c | 124 ++++++++++++++++++++----------------- drivers/usb/cdns3/drd.h | 6 +- drivers/usb/cdns3/gadget.c | 4 +- drivers/usb/cdns3/host.c | 4 +- 4 files changed, 76 insertions(+), 62 deletions(-) diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index 8e7673da905ed6..6234bcd6158aac 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -124,85 +124,97 @@ static void cdns3_otg_enable_irq(struct cdns3 *cdns) } /** - * cdns3_drd_switch_host - start/stop host - * @cdns: Pointer to controller context structure - * @on: 1 for start, 0 for stop + * cdns3_drd_host_on - start host. + * @cdns: Pointer to controller context structure. * - * Returns 0 on success otherwise negative errno + * Returns 0 on success otherwise negative errno. */ -int cdns3_drd_switch_host(struct cdns3 *cdns, int on) +int cdns3_drd_host_on(struct cdns3 *cdns) { - int ret, val; + u32 val; + int ret; - /* switch OTG core */ - if (on) { - writel(OTGCMD_HOST_BUS_REQ | OTGCMD_OTG_DIS, - &cdns->otg_regs->cmd); - - dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n"); - ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val, - val & OTGSTS_XHCI_READY, - 1, 100000); - if (ret) { - dev_err(cdns->dev, "timeout waiting for xhci_ready\n"); - return ret; - } - } else { - writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP | - OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF, - &cdns->otg_regs->cmd); - /* Waiting till H_IDLE state.*/ - readl_poll_timeout_atomic(&cdns->otg_regs->state, val, - !(val & OTGSTATE_HOST_STATE_MASK), - 1, 2000000); - } + /* Enable host mode. */ + writel(OTGCMD_HOST_BUS_REQ | OTGCMD_OTG_DIS, + &cdns->otg_regs->cmd); - return 0; + dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n"); + ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val, + val & OTGSTS_XHCI_READY, 1, 100000); + + if (ret) + dev_err(cdns->dev, "timeout waiting for xhci_ready\n"); + + return ret; } /** - * cdns3_drd_switch_gadget - start/stop gadget - * @cdns: Pointer to controller context structure - * @on: 1 for start, 0 for stop + * cdns3_drd_host_off - stop host. + * @cdns: Pointer to controller context structure. + */ +void cdns3_drd_host_off(struct cdns3 *cdns) +{ + u32 val; + + writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP | + OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF, + &cdns->otg_regs->cmd); + + /* Waiting till H_IDLE state.*/ + readl_poll_timeout_atomic(&cdns->otg_regs->state, val, + !(val & OTGSTATE_HOST_STATE_MASK), + 1, 2000000); +} + +/** + * cdns3_drd_gadget_on - start gadget. + * @cdns: Pointer to controller context structure. * * Returns 0 on success otherwise negative errno */ -int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on) +int cdns3_drd_gadget_on(struct cdns3 *cdns) { int ret, val; u32 reg = OTGCMD_OTG_DIS; /* switch OTG core */ - if (on) { - writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd); + writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd); - dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n"); + dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n"); - ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val, - val & OTGSTS_DEV_READY, - 1, 100000); - if (ret) { - dev_err(cdns->dev, "timeout waiting for dev_ready\n"); - return ret; - } - } else { - /* - * driver should wait at least 10us after disabling Device - * before turning-off Device (DEV_BUS_DROP) - */ - usleep_range(20, 30); - writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP | - OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF, - &cdns->otg_regs->cmd); - /* Waiting till DEV_IDLE state.*/ - readl_poll_timeout_atomic(&cdns->otg_regs->state, val, - !(val & OTGSTATE_DEV_STATE_MASK), - 1, 2000000); + ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val, + val & OTGSTS_DEV_READY, + 1, 100000); + if (ret) { + dev_err(cdns->dev, "timeout waiting for dev_ready\n"); + return ret; } return 0; } +/** + * cdns3_drd_gadget_off - stop gadget. + * @cdns: Pointer to controller context structure. + */ +void cdns3_drd_gadget_off(struct cdns3 *cdns) +{ + u32 val; + + /* + * Driver should wait at least 10us after disabling Device + * before turning-off Device (DEV_BUS_DROP). + */ + usleep_range(20, 30); + writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP | + OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF, + &cdns->otg_regs->cmd); + /* Waiting till DEV_IDLE state.*/ + readl_poll_timeout_atomic(&cdns->otg_regs->state, val, + !(val & OTGSTATE_DEV_STATE_MASK), + 1, 2000000); +} + /** * cdns3_init_otg_mode - initialize drd controller * @cdns: Pointer to controller context structure diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h index 3889fead9df196..7e7cf7fa2dd3d2 100644 --- a/drivers/usb/cdns3/drd.h +++ b/drivers/usb/cdns3/drd.h @@ -163,8 +163,10 @@ int cdns3_get_vbus(struct cdns3 *cdns); int cdns3_drd_init(struct cdns3 *cdns); int cdns3_drd_exit(struct cdns3 *cdns); int cdns3_drd_update_mode(struct cdns3 *cdns); -int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on); -int cdns3_drd_switch_host(struct cdns3 *cdns, int on); +int cdns3_drd_gadget_on(struct cdns3 *cdns); +void cdns3_drd_gadget_off(struct cdns3 *cdns); +int cdns3_drd_host_on(struct cdns3 *cdns); +void cdns3_drd_host_off(struct cdns3 *cdns); int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode); #endif /* __LINUX_CDNS3_DRD */ diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 65a154d47f8ead..dea649ee173bae 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -3017,7 +3017,7 @@ void cdns3_gadget_exit(struct cdns3 *cdns) kfree(priv_dev->zlp_buf); kfree(priv_dev); cdns->gadget_dev = NULL; - cdns3_drd_switch_gadget(cdns, 0); + cdns3_drd_gadget_off(cdns); } static int cdns3_gadget_start(struct cdns3 *cdns) @@ -3148,7 +3148,7 @@ static int __cdns3_gadget_init(struct cdns3 *cdns) return ret; } - cdns3_drd_switch_gadget(cdns, 1); + cdns3_drd_gadget_on(cdns); pm_runtime_get_sync(cdns->dev); ret = cdns3_gadget_start(cdns); diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index ad788bf3fe4f6f..36c63d9ecd37ff 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -19,7 +19,7 @@ static int __cdns3_host_init(struct cdns3 *cdns) struct platform_device *xhci; int ret; - cdns3_drd_switch_host(cdns, 1); + cdns3_drd_host_on(cdns); xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); if (!xhci) { @@ -53,7 +53,7 @@ static void cdns3_host_exit(struct cdns3 *cdns) { platform_device_unregister(cdns->host_dev); cdns->host_dev = NULL; - cdns3_drd_switch_host(cdns, 0); + cdns3_drd_host_off(cdns); } int cdns3_host_init(struct cdns3 *cdns) From 2468c877da428ebfd701142c4cdfefcfb7d4c00e Mon Sep 17 00:00:00 2001 From: Evgeny Novikov Date: Tue, 21 Jul 2020 23:15:58 +0300 Subject: [PATCH 21/38] usb: gadget: net2280: fix memory leak on probe error handling paths Driver does not release memory for device on error handling paths in net2280_probe() when gadget_release() is not registered yet. The patch fixes the bug like in other similar drivers. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Evgeny Novikov Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 5eff85eeaa5a09..7530bd9a08c433 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -3781,8 +3781,10 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; done: - if (dev) + if (dev) { net2280_remove(pdev); + kfree(dev); + } return retval; } From 4afd6fe4a3e331952e133a5fd01e9ad5377fdd1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=90=B0=E6=9D=B0=20=28Zhou=20Yanjie=29?= Date: Thu, 23 Jul 2020 14:12:58 +0800 Subject: [PATCH 22/38] dt-bindings: USB: Add bindings for new Ingenic SoCs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the USB PHY bindings for the JZ4780 SoC, the X1000 SoC and the X1830 SoC from Ingenic. Tested-by: 周正 (Zhou Zheng) Signed-off-by: 周琰杰 (Zhou Yanjie) Acked-by: Rob Herring Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/ingenic,jz4770-phy.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/ingenic,jz4770-phy.yaml b/Documentation/devicetree/bindings/usb/ingenic,jz4770-phy.yaml index a81b0b1a222694..2d61166ea5cf73 100644 --- a/Documentation/devicetree/bindings/usb/ingenic,jz4770-phy.yaml +++ b/Documentation/devicetree/bindings/usb/ingenic,jz4770-phy.yaml @@ -4,10 +4,11 @@ $id: http://devicetree.org/schemas/usb/ingenic,jz4770-phy.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Ingenic JZ4770 USB PHY devicetree bindings +title: Ingenic SoCs USB PHY devicetree bindings maintainers: - Paul Cercueil + - 周琰杰 (Zhou Yanjie) properties: $nodename: @@ -16,6 +17,9 @@ properties: compatible: enum: - ingenic,jz4770-phy + - ingenic,jz4780-phy + - ingenic,x1000-phy + - ingenic,x1830-phy reg: maxItems: 1 From 6e5478aeb3184cb37035550f82527b27c064eef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=90=B0=E6=9D=B0=20=28Zhou=20Yanjie=29?= Date: Thu, 23 Jul 2020 14:12:59 +0800 Subject: [PATCH 23/38] USB: PHY: JZ4770: Unify code style and simplify code. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.Modify the macro definition to unify "#define USBPCR_XXXX n" into the "#define USBPCR_XXXX (n << USBPCR_XXXX_LSB)" style, so as to unify the code style in the "jz4770_phy_init()" and simplify the code. 2.Remove unused macro definitions to simplify the code. Tested-by: 周正 (Zhou Zheng) Suggested-by: Paul Cercueil Signed-off-by: 周琰杰 (Zhou Yanjie) Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-jz4770.c | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/drivers/usb/phy/phy-jz4770.c b/drivers/usb/phy/phy-jz4770.c index 8f62dc2a90ff9e..00209d5469d378 100644 --- a/drivers/usb/phy/phy-jz4770.c +++ b/drivers/usb/phy/phy-jz4770.c @@ -20,8 +20,6 @@ /* USBPCR */ #define USBPCR_USB_MODE BIT(31) #define USBPCR_AVLD_REG BIT(30) -#define USBPCR_INCRM BIT(27) -#define USBPCR_CLK12_EN BIT(26) #define USBPCR_COMMONONN BIT(25) #define USBPCR_VBUSVLDEXT BIT(24) #define USBPCR_VBUSVLDEXTSEL BIT(23) @@ -32,45 +30,39 @@ #define USBPCR_IDPULLUP_LSB 28 #define USBPCR_IDPULLUP_MASK GENMASK(29, USBPCR_IDPULLUP_LSB) -#define USBPCR_IDPULLUP_ALWAYS (3 << USBPCR_IDPULLUP_LSB) -#define USBPCR_IDPULLUP_SUSPEND (1 << USBPCR_IDPULLUP_LSB) -#define USBPCR_IDPULLUP_OTG (0 << USBPCR_IDPULLUP_LSB) +#define USBPCR_IDPULLUP_ALWAYS (0x2 << USBPCR_IDPULLUP_LSB) +#define USBPCR_IDPULLUP_SUSPEND (0x1 << USBPCR_IDPULLUP_LSB) +#define USBPCR_IDPULLUP_OTG (0x0 << USBPCR_IDPULLUP_LSB) #define USBPCR_COMPDISTUNE_LSB 17 #define USBPCR_COMPDISTUNE_MASK GENMASK(19, USBPCR_COMPDISTUNE_LSB) -#define USBPCR_COMPDISTUNE_DFT 4 +#define USBPCR_COMPDISTUNE_DFT (0x4 << USBPCR_COMPDISTUNE_LSB) #define USBPCR_OTGTUNE_LSB 14 #define USBPCR_OTGTUNE_MASK GENMASK(16, USBPCR_OTGTUNE_LSB) -#define USBPCR_OTGTUNE_DFT 4 +#define USBPCR_OTGTUNE_DFT (0x4 << USBPCR_OTGTUNE_LSB) #define USBPCR_SQRXTUNE_LSB 11 #define USBPCR_SQRXTUNE_MASK GENMASK(13, USBPCR_SQRXTUNE_LSB) -#define USBPCR_SQRXTUNE_DFT 3 +#define USBPCR_SQRXTUNE_DFT (0x3 << USBPCR_SQRXTUNE_LSB) #define USBPCR_TXFSLSTUNE_LSB 7 #define USBPCR_TXFSLSTUNE_MASK GENMASK(10, USBPCR_TXFSLSTUNE_LSB) -#define USBPCR_TXFSLSTUNE_DFT 3 +#define USBPCR_TXFSLSTUNE_DFT (0x3 << USBPCR_TXFSLSTUNE_LSB) #define USBPCR_TXRISETUNE_LSB 4 #define USBPCR_TXRISETUNE_MASK GENMASK(5, USBPCR_TXRISETUNE_LSB) -#define USBPCR_TXRISETUNE_DFT 3 +#define USBPCR_TXRISETUNE_DFT (0x3 << USBPCR_TXRISETUNE_LSB) #define USBPCR_TXVREFTUNE_LSB 0 #define USBPCR_TXVREFTUNE_MASK GENMASK(3, USBPCR_TXVREFTUNE_LSB) -#define USBPCR_TXVREFTUNE_DFT 5 +#define USBPCR_TXVREFTUNE_DFT (0x5 << USBPCR_TXVREFTUNE_LSB) /* USBRDT */ #define USBRDT_VBFIL_LD_EN BIT(25) #define USBRDT_IDDIG_EN BIT(24) #define USBRDT_IDDIG_REG BIT(23) -#define USBRDT_USBRDT_LSB 0 -#define USBRDT_USBRDT_MASK GENMASK(22, USBRDT_USBRDT_LSB) - -/* USBPCR1 */ -#define USBPCR1_UHC_POWON BIT(5) - struct jz4770_phy { struct usb_phy phy; struct usb_otg otg; @@ -136,12 +128,8 @@ static int jz4770_phy_init(struct usb_phy *phy) } reg = USBPCR_AVLD_REG | USBPCR_COMMONONN | USBPCR_IDPULLUP_ALWAYS | - (USBPCR_COMPDISTUNE_DFT << USBPCR_COMPDISTUNE_LSB) | - (USBPCR_OTGTUNE_DFT << USBPCR_OTGTUNE_LSB) | - (USBPCR_SQRXTUNE_DFT << USBPCR_SQRXTUNE_LSB) | - (USBPCR_TXFSLSTUNE_DFT << USBPCR_TXFSLSTUNE_LSB) | - (USBPCR_TXRISETUNE_DFT << USBPCR_TXRISETUNE_LSB) | - (USBPCR_TXVREFTUNE_DFT << USBPCR_TXVREFTUNE_LSB) | + USBPCR_COMPDISTUNE_DFT | USBPCR_OTGTUNE_DFT | USBPCR_SQRXTUNE_DFT | + USBPCR_TXFSLSTUNE_DFT | USBPCR_TXRISETUNE_DFT | USBPCR_TXVREFTUNE_DFT | USBPCR_POR; writel(reg, priv->base + REG_USBPCR_OFFSET); From 2a6c0b82e65128c73b5102e00e031c5e58bb3504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=90=B0=E6=9D=B0=20=28Zhou=20Yanjie=29?= Date: Thu, 23 Jul 2020 14:13:00 +0800 Subject: [PATCH 24/38] USB: PHY: JZ4770: Add support for new Ingenic SoCs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for probing the phy-jz4770 driver on the JZ4780 SoC, the X1000 SoC and the X1830 SoC from Ingenic. Tested-by: 周正 (Zhou Zheng) Co-developed-by: 漆鹏振 (Qi Pengzhen) Signed-off-by: 漆鹏振 (Qi Pengzhen) Signed-off-by: 周琰杰 (Zhou Yanjie) Signed-off-by: Felipe Balbi --- drivers/usb/phy/Kconfig | 4 +- drivers/usb/phy/phy-jz4770.c | 192 +++++++++++++++++++++++++++++------ 2 files changed, 163 insertions(+), 33 deletions(-) diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 4b3fa78995cf2f..ef4787cd3d37b8 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -185,11 +185,11 @@ config USB_ULPI_VIEWPORT controllers with a viewport register (e.g. Chipidea/ARC controllers). config JZ4770_PHY - tristate "Ingenic JZ4770 Transceiver Driver" + tristate "Ingenic SoCs Transceiver Driver" depends on MIPS || COMPILE_TEST select USB_PHY help This driver provides PHY support for the USB controller found - on the JZ4770 SoC from Ingenic. + on the JZ-series and X-series SoCs from Ingenic. endmenu diff --git a/drivers/usb/phy/phy-jz4770.c b/drivers/usb/phy/phy-jz4770.c index 00209d5469d378..5ddf48f9085a19 100644 --- a/drivers/usb/phy/phy-jz4770.c +++ b/drivers/usb/phy/phy-jz4770.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Ingenic JZ4770 USB PHY driver + * Ingenic SoCs USB PHY driver * Copyright (c) Paul Cercueil + * Copyright (c) 漆鹏振 (Qi Pengzhen) + * Copyright (c) 周琰杰 (Zhou Yanjie) */ #include @@ -12,12 +14,13 @@ #include #include +/* OTGPHY register offsets */ #define REG_USBPCR_OFFSET 0x00 #define REG_USBRDT_OFFSET 0x04 #define REG_USBVBFIL_OFFSET 0x08 #define REG_USBPCR1_OFFSET 0x0c -/* USBPCR */ +/* bits within the USBPCR register */ #define USBPCR_USB_MODE BIT(31) #define USBPCR_AVLD_REG BIT(30) #define USBPCR_COMMONONN BIT(25) @@ -44,11 +47,21 @@ #define USBPCR_SQRXTUNE_LSB 11 #define USBPCR_SQRXTUNE_MASK GENMASK(13, USBPCR_SQRXTUNE_LSB) +#define USBPCR_SQRXTUNE_DCR_20PCT (0x7 << USBPCR_SQRXTUNE_LSB) #define USBPCR_SQRXTUNE_DFT (0x3 << USBPCR_SQRXTUNE_LSB) #define USBPCR_TXFSLSTUNE_LSB 7 #define USBPCR_TXFSLSTUNE_MASK GENMASK(10, USBPCR_TXFSLSTUNE_LSB) +#define USBPCR_TXFSLSTUNE_DCR_50PPT (0xf << USBPCR_TXFSLSTUNE_LSB) +#define USBPCR_TXFSLSTUNE_DCR_25PPT (0x7 << USBPCR_TXFSLSTUNE_LSB) #define USBPCR_TXFSLSTUNE_DFT (0x3 << USBPCR_TXFSLSTUNE_LSB) +#define USBPCR_TXFSLSTUNE_INC_25PPT (0x1 << USBPCR_TXFSLSTUNE_LSB) +#define USBPCR_TXFSLSTUNE_INC_50PPT (0x0 << USBPCR_TXFSLSTUNE_LSB) + +#define USBPCR_TXHSXVTUNE_LSB 4 +#define USBPCR_TXHSXVTUNE_MASK GENMASK(5, USBPCR_TXHSXVTUNE_LSB) +#define USBPCR_TXHSXVTUNE_DFT (0x3 << USBPCR_TXHSXVTUNE_LSB) +#define USBPCR_TXHSXVTUNE_DCR_15MV (0x1 << USBPCR_TXHSXVTUNE_LSB) #define USBPCR_TXRISETUNE_LSB 4 #define USBPCR_TXRISETUNE_MASK GENMASK(5, USBPCR_TXRISETUNE_LSB) @@ -56,14 +69,40 @@ #define USBPCR_TXVREFTUNE_LSB 0 #define USBPCR_TXVREFTUNE_MASK GENMASK(3, USBPCR_TXVREFTUNE_LSB) +#define USBPCR_TXVREFTUNE_INC_25PPT (0x7 << USBPCR_TXVREFTUNE_LSB) #define USBPCR_TXVREFTUNE_DFT (0x5 << USBPCR_TXVREFTUNE_LSB) -/* USBRDT */ +/* bits within the USBRDTR register */ +#define USBRDT_UTMI_RST BIT(27) +#define USBRDT_HB_MASK BIT(26) #define USBRDT_VBFIL_LD_EN BIT(25) #define USBRDT_IDDIG_EN BIT(24) #define USBRDT_IDDIG_REG BIT(23) +#define USBRDT_VBFIL_EN BIT(2) + +/* bits within the USBPCR1 register */ +#define USBPCR1_BVLD_REG BIT(31) +#define USBPCR1_DPPD BIT(29) +#define USBPCR1_DMPD BIT(28) +#define USBPCR1_USB_SEL BIT(28) +#define USBPCR1_WORD_IF_16BIT BIT(19) + +enum ingenic_usb_phy_version { + ID_JZ4770, + ID_JZ4780, + ID_X1000, + ID_X1830, +}; + +struct ingenic_soc_info { + enum ingenic_usb_phy_version version; + + void (*usb_phy_init)(struct usb_phy *phy); +}; struct jz4770_phy { + const struct ingenic_soc_info *soc_info; + struct usb_phy phy; struct usb_otg otg; struct device *dev; @@ -82,12 +121,18 @@ static inline struct jz4770_phy *phy_to_jz4770_phy(struct usb_phy *phy) return container_of(phy, struct jz4770_phy, phy); } -static int jz4770_phy_set_peripheral(struct usb_otg *otg, +static int ingenic_usb_phy_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) { struct jz4770_phy *priv = otg_to_jz4770_phy(otg); u32 reg; + if (priv->soc_info->version >= ID_X1000) { + reg = readl(priv->base + REG_USBPCR1_OFFSET); + reg |= USBPCR1_BVLD_REG; + writel(reg, priv->base + REG_USBPCR1_OFFSET); + } + reg = readl(priv->base + REG_USBPCR_OFFSET); reg &= ~USBPCR_USB_MODE; reg |= USBPCR_VBUSVLDEXT | USBPCR_VBUSVLDEXTSEL | USBPCR_OTG_DISABLE; @@ -96,7 +141,7 @@ static int jz4770_phy_set_peripheral(struct usb_otg *otg, return 0; } -static int jz4770_phy_set_host(struct usb_otg *otg, struct usb_bus *host) +static int ingenic_usb_phy_set_host(struct usb_otg *otg, struct usb_bus *host) { struct jz4770_phy *priv = otg_to_jz4770_phy(otg); u32 reg; @@ -109,7 +154,7 @@ static int jz4770_phy_set_host(struct usb_otg *otg, struct usb_bus *host) return 0; } -static int jz4770_phy_init(struct usb_phy *phy) +static int ingenic_usb_phy_init(struct usb_phy *phy) { struct jz4770_phy *priv = phy_to_jz4770_phy(phy); int err; @@ -127,11 +172,7 @@ static int jz4770_phy_init(struct usb_phy *phy) return err; } - reg = USBPCR_AVLD_REG | USBPCR_COMMONONN | USBPCR_IDPULLUP_ALWAYS | - USBPCR_COMPDISTUNE_DFT | USBPCR_OTGTUNE_DFT | USBPCR_SQRXTUNE_DFT | - USBPCR_TXFSLSTUNE_DFT | USBPCR_TXRISETUNE_DFT | USBPCR_TXVREFTUNE_DFT | - USBPCR_POR; - writel(reg, priv->base + REG_USBPCR_OFFSET); + priv->soc_info->usb_phy_init(phy); /* Wait for PHY to reset */ usleep_range(30, 300); @@ -141,7 +182,7 @@ static int jz4770_phy_init(struct usb_phy *phy) return 0; } -static void jz4770_phy_shutdown(struct usb_phy *phy) +static void ingenic_usb_phy_shutdown(struct usb_phy *phy) { struct jz4770_phy *priv = phy_to_jz4770_phy(phy); @@ -149,11 +190,100 @@ static void jz4770_phy_shutdown(struct usb_phy *phy) regulator_disable(priv->vcc_supply); } -static void jz4770_phy_remove(void *phy) +static void ingenic_usb_phy_remove(void *phy) { usb_remove_phy(phy); } +static void jz4770_usb_phy_init(struct usb_phy *phy) +{ + struct jz4770_phy *priv = phy_to_jz4770_phy(phy); + u32 reg; + + reg = USBPCR_AVLD_REG | USBPCR_COMMONONN | USBPCR_IDPULLUP_ALWAYS | + USBPCR_COMPDISTUNE_DFT | USBPCR_OTGTUNE_DFT | USBPCR_SQRXTUNE_DFT | + USBPCR_TXFSLSTUNE_DFT | USBPCR_TXRISETUNE_DFT | USBPCR_TXVREFTUNE_DFT | + USBPCR_POR; + writel(reg, priv->base + REG_USBPCR_OFFSET); +} + +static void jz4780_usb_phy_init(struct usb_phy *phy) +{ + struct jz4770_phy *priv = phy_to_jz4770_phy(phy); + u32 reg; + + reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_USB_SEL | + USBPCR1_WORD_IF_16BIT; + writel(reg, priv->base + REG_USBPCR1_OFFSET); + + reg = USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR; + writel(reg, priv->base + REG_USBPCR_OFFSET); +} + +static void x1000_usb_phy_init(struct usb_phy *phy) +{ + struct jz4770_phy *priv = phy_to_jz4770_phy(phy); + u32 reg; + + reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT; + writel(reg, priv->base + REG_USBPCR1_OFFSET); + + reg = USBPCR_SQRXTUNE_DCR_20PCT | USBPCR_TXPREEMPHTUNE | + USBPCR_TXHSXVTUNE_DCR_15MV | USBPCR_TXVREFTUNE_INC_25PPT | + USBPCR_COMMONONN | USBPCR_POR; + writel(reg, priv->base + REG_USBPCR_OFFSET); +} + +static void x1830_usb_phy_init(struct usb_phy *phy) +{ + struct jz4770_phy *priv = phy_to_jz4770_phy(phy); + u32 reg; + + /* rdt */ + writel(USBRDT_VBFIL_EN | USBRDT_UTMI_RST, priv->base + REG_USBRDT_OFFSET); + + reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT | + USBPCR1_DMPD | USBPCR1_DPPD; + writel(reg, priv->base + REG_USBPCR1_OFFSET); + + reg = USBPCR_IDPULLUP_OTG | USBPCR_VBUSVLDEXT | USBPCR_TXPREEMPHTUNE | + USBPCR_COMMONONN | USBPCR_POR; + writel(reg, priv->base + REG_USBPCR_OFFSET); +} + +static const struct ingenic_soc_info jz4770_soc_info = { + .version = ID_JZ4770, + + .usb_phy_init = jz4770_usb_phy_init, +}; + +static const struct ingenic_soc_info jz4780_soc_info = { + .version = ID_JZ4780, + + .usb_phy_init = jz4780_usb_phy_init, +}; + +static const struct ingenic_soc_info x1000_soc_info = { + .version = ID_X1000, + + .usb_phy_init = x1000_usb_phy_init, +}; + +static const struct ingenic_soc_info x1830_soc_info = { + .version = ID_X1830, + + .usb_phy_init = x1830_usb_phy_init, +}; + +static const struct of_device_id ingenic_usb_phy_of_matches[] = { + { .compatible = "ingenic,jz4770-phy", .data = &jz4770_soc_info }, + { .compatible = "ingenic,jz4780-phy", .data = &jz4780_soc_info }, + { .compatible = "ingenic,x1000-phy", .data = &x1000_soc_info }, + { .compatible = "ingenic,x1830-phy", .data = &x1830_soc_info }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ingenic_usb_phy_of_matches); + static int jz4770_phy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -164,18 +294,24 @@ static int jz4770_phy_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + priv->soc_info = device_get_match_data(&pdev->dev); + if (!priv->soc_info) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + platform_set_drvdata(pdev, priv); priv->dev = dev; priv->phy.dev = dev; priv->phy.otg = &priv->otg; - priv->phy.label = "jz4770-phy"; - priv->phy.init = jz4770_phy_init; - priv->phy.shutdown = jz4770_phy_shutdown; + priv->phy.label = "ingenic-usb-phy"; + priv->phy.init = ingenic_usb_phy_init; + priv->phy.shutdown = ingenic_usb_phy_shutdown; priv->otg.state = OTG_STATE_UNDEFINED; priv->otg.usb_phy = &priv->phy; - priv->otg.set_host = jz4770_phy_set_host; - priv->otg.set_peripheral = jz4770_phy_set_peripheral; + priv->otg.set_host = ingenic_usb_phy_set_host; + priv->otg.set_peripheral = ingenic_usb_phy_set_peripheral; priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) { @@ -206,26 +342,20 @@ static int jz4770_phy_probe(struct platform_device *pdev) return err; } - return devm_add_action_or_reset(dev, jz4770_phy_remove, &priv->phy); + return devm_add_action_or_reset(dev, ingenic_usb_phy_remove, &priv->phy); } -#ifdef CONFIG_OF -static const struct of_device_id jz4770_phy_of_matches[] = { - { .compatible = "ingenic,jz4770-phy" }, - { } -}; -MODULE_DEVICE_TABLE(of, jz4770_phy_of_matches); -#endif - -static struct platform_driver jz4770_phy_driver = { +static struct platform_driver ingenic_phy_driver = { .probe = jz4770_phy_probe, .driver = { .name = "jz4770-phy", - .of_match_table = of_match_ptr(jz4770_phy_of_matches), + .of_match_table = of_match_ptr(ingenic_usb_phy_of_matches), }, }; -module_platform_driver(jz4770_phy_driver); +module_platform_driver(ingenic_phy_driver); +MODULE_AUTHOR("周琰杰 (Zhou Yanjie) "); +MODULE_AUTHOR("漆鹏振 (Qi Pengzhen) "); MODULE_AUTHOR("Paul Cercueil "); -MODULE_DESCRIPTION("Ingenic JZ4770 USB PHY driver"); +MODULE_DESCRIPTION("Ingenic SoCs USB PHY driver"); MODULE_LICENSE("GPL"); From f7e764cba28e49fd22dbe106e819ae592780feb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=90=B0=E6=9D=B0=20=28Zhou=20Yanjie=29?= Date: Thu, 23 Jul 2020 14:13:01 +0800 Subject: [PATCH 25/38] USB: PHY: JZ4770: Reformat the code to align it. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reformat the code (add one level of indentation before the values), to align the code in the macro definition section. Tested-by: 周正 (Zhou Zheng) Co-developed-by: 漆鹏振 (Qi Pengzhen) Signed-off-by: 漆鹏振 (Qi Pengzhen) Signed-off-by: 周琰杰 (Zhou Yanjie) Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-jz4770.c | 74 ++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/drivers/usb/phy/phy-jz4770.c b/drivers/usb/phy/phy-jz4770.c index 5ddf48f9085a19..d4ee3cb721ea8c 100644 --- a/drivers/usb/phy/phy-jz4770.c +++ b/drivers/usb/phy/phy-jz4770.c @@ -15,46 +15,46 @@ #include /* OTGPHY register offsets */ -#define REG_USBPCR_OFFSET 0x00 -#define REG_USBRDT_OFFSET 0x04 -#define REG_USBVBFIL_OFFSET 0x08 -#define REG_USBPCR1_OFFSET 0x0c +#define REG_USBPCR_OFFSET 0x00 +#define REG_USBRDT_OFFSET 0x04 +#define REG_USBVBFIL_OFFSET 0x08 +#define REG_USBPCR1_OFFSET 0x0c /* bits within the USBPCR register */ -#define USBPCR_USB_MODE BIT(31) -#define USBPCR_AVLD_REG BIT(30) -#define USBPCR_COMMONONN BIT(25) -#define USBPCR_VBUSVLDEXT BIT(24) -#define USBPCR_VBUSVLDEXTSEL BIT(23) -#define USBPCR_POR BIT(22) -#define USBPCR_SIDDQ BIT(21) -#define USBPCR_OTG_DISABLE BIT(20) -#define USBPCR_TXPREEMPHTUNE BIT(6) +#define USBPCR_USB_MODE BIT(31) +#define USBPCR_AVLD_REG BIT(30) +#define USBPCR_COMMONONN BIT(25) +#define USBPCR_VBUSVLDEXT BIT(24) +#define USBPCR_VBUSVLDEXTSEL BIT(23) +#define USBPCR_POR BIT(22) +#define USBPCR_SIDDQ BIT(21) +#define USBPCR_OTG_DISABLE BIT(20) +#define USBPCR_TXPREEMPHTUNE BIT(6) #define USBPCR_IDPULLUP_LSB 28 -#define USBPCR_IDPULLUP_MASK GENMASK(29, USBPCR_IDPULLUP_LSB) -#define USBPCR_IDPULLUP_ALWAYS (0x2 << USBPCR_IDPULLUP_LSB) -#define USBPCR_IDPULLUP_SUSPEND (0x1 << USBPCR_IDPULLUP_LSB) -#define USBPCR_IDPULLUP_OTG (0x0 << USBPCR_IDPULLUP_LSB) +#define USBPCR_IDPULLUP_MASK GENMASK(29, USBPCR_IDPULLUP_LSB) +#define USBPCR_IDPULLUP_ALWAYS (0x2 << USBPCR_IDPULLUP_LSB) +#define USBPCR_IDPULLUP_SUSPEND (0x1 << USBPCR_IDPULLUP_LSB) +#define USBPCR_IDPULLUP_OTG (0x0 << USBPCR_IDPULLUP_LSB) -#define USBPCR_COMPDISTUNE_LSB 17 -#define USBPCR_COMPDISTUNE_MASK GENMASK(19, USBPCR_COMPDISTUNE_LSB) -#define USBPCR_COMPDISTUNE_DFT (0x4 << USBPCR_COMPDISTUNE_LSB) +#define USBPCR_COMPDISTUNE_LSB 17 +#define USBPCR_COMPDISTUNE_MASK GENMASK(19, USBPCR_COMPDISTUNE_LSB) +#define USBPCR_COMPDISTUNE_DFT (0x4 << USBPCR_COMPDISTUNE_LSB) -#define USBPCR_OTGTUNE_LSB 14 -#define USBPCR_OTGTUNE_MASK GENMASK(16, USBPCR_OTGTUNE_LSB) -#define USBPCR_OTGTUNE_DFT (0x4 << USBPCR_OTGTUNE_LSB) +#define USBPCR_OTGTUNE_LSB 14 +#define USBPCR_OTGTUNE_MASK GENMASK(16, USBPCR_OTGTUNE_LSB) +#define USBPCR_OTGTUNE_DFT (0x4 << USBPCR_OTGTUNE_LSB) #define USBPCR_SQRXTUNE_LSB 11 -#define USBPCR_SQRXTUNE_MASK GENMASK(13, USBPCR_SQRXTUNE_LSB) +#define USBPCR_SQRXTUNE_MASK GENMASK(13, USBPCR_SQRXTUNE_LSB) #define USBPCR_SQRXTUNE_DCR_20PCT (0x7 << USBPCR_SQRXTUNE_LSB) -#define USBPCR_SQRXTUNE_DFT (0x3 << USBPCR_SQRXTUNE_LSB) +#define USBPCR_SQRXTUNE_DFT (0x3 << USBPCR_SQRXTUNE_LSB) -#define USBPCR_TXFSLSTUNE_LSB 7 -#define USBPCR_TXFSLSTUNE_MASK GENMASK(10, USBPCR_TXFSLSTUNE_LSB) +#define USBPCR_TXFSLSTUNE_LSB 7 +#define USBPCR_TXFSLSTUNE_MASK GENMASK(10, USBPCR_TXFSLSTUNE_LSB) #define USBPCR_TXFSLSTUNE_DCR_50PPT (0xf << USBPCR_TXFSLSTUNE_LSB) #define USBPCR_TXFSLSTUNE_DCR_25PPT (0x7 << USBPCR_TXFSLSTUNE_LSB) -#define USBPCR_TXFSLSTUNE_DFT (0x3 << USBPCR_TXFSLSTUNE_LSB) +#define USBPCR_TXFSLSTUNE_DFT (0x3 << USBPCR_TXFSLSTUNE_LSB) #define USBPCR_TXFSLSTUNE_INC_25PPT (0x1 << USBPCR_TXFSLSTUNE_LSB) #define USBPCR_TXFSLSTUNE_INC_50PPT (0x0 << USBPCR_TXFSLSTUNE_LSB) @@ -63,21 +63,21 @@ #define USBPCR_TXHSXVTUNE_DFT (0x3 << USBPCR_TXHSXVTUNE_LSB) #define USBPCR_TXHSXVTUNE_DCR_15MV (0x1 << USBPCR_TXHSXVTUNE_LSB) -#define USBPCR_TXRISETUNE_LSB 4 -#define USBPCR_TXRISETUNE_MASK GENMASK(5, USBPCR_TXRISETUNE_LSB) -#define USBPCR_TXRISETUNE_DFT (0x3 << USBPCR_TXRISETUNE_LSB) +#define USBPCR_TXRISETUNE_LSB 4 +#define USBPCR_TXRISETUNE_MASK GENMASK(5, USBPCR_TXRISETUNE_LSB) +#define USBPCR_TXRISETUNE_DFT (0x3 << USBPCR_TXRISETUNE_LSB) -#define USBPCR_TXVREFTUNE_LSB 0 -#define USBPCR_TXVREFTUNE_MASK GENMASK(3, USBPCR_TXVREFTUNE_LSB) +#define USBPCR_TXVREFTUNE_LSB 0 +#define USBPCR_TXVREFTUNE_MASK GENMASK(3, USBPCR_TXVREFTUNE_LSB) #define USBPCR_TXVREFTUNE_INC_25PPT (0x7 << USBPCR_TXVREFTUNE_LSB) -#define USBPCR_TXVREFTUNE_DFT (0x5 << USBPCR_TXVREFTUNE_LSB) +#define USBPCR_TXVREFTUNE_DFT (0x5 << USBPCR_TXVREFTUNE_LSB) /* bits within the USBRDTR register */ #define USBRDT_UTMI_RST BIT(27) #define USBRDT_HB_MASK BIT(26) -#define USBRDT_VBFIL_LD_EN BIT(25) -#define USBRDT_IDDIG_EN BIT(24) -#define USBRDT_IDDIG_REG BIT(23) +#define USBRDT_VBFIL_LD_EN BIT(25) +#define USBRDT_IDDIG_EN BIT(24) +#define USBRDT_IDDIG_REG BIT(23) #define USBRDT_VBFIL_EN BIT(2) /* bits within the USBPCR1 register */ From 4e33ba7f82234041b677609203a784006c848e0e Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Wed, 22 Jul 2020 13:07:40 -0400 Subject: [PATCH 26/38] dt-bindings: usb: bdc: Update compatible strings Remove "brcm,bdc-v0.16" because it was never used on any system. Add "brcm,bdc-udc-v2" which exists for any STB system with BDC. Acked-by: Rob Herring Signed-off-by: Al Cooper Acked-by: Florian Fainelli Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/brcm,bdc.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/brcm,bdc.txt b/Documentation/devicetree/bindings/usb/brcm,bdc.txt index 63e63af3bf5939..c9f52b97cef12b 100644 --- a/Documentation/devicetree/bindings/usb/brcm,bdc.txt +++ b/Documentation/devicetree/bindings/usb/brcm,bdc.txt @@ -4,7 +4,7 @@ Broadcom USB Device Controller (BDC) Required properties: - compatible: must be one of: - "brcm,bdc-v0.16" + "brcm,bdc-udc-v2" "brcm,bdc" - reg: the base register address and length - interrupts: the interrupt line for this controller @@ -21,7 +21,7 @@ On Broadcom STB platforms, these properties are required: Example: bdc@f0b02000 { - compatible = "brcm,bdc-v0.16"; + compatible = "brcm,bdc-udc-v2"; reg = <0xf0b02000 0xfc4>; interrupts = <0x0 0x60 0x0>; phys = <&usbphy_0 0x0>; From 4e3a765ba03c065e5ee71ea309f1933102af7a33 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Wed, 22 Jul 2020 13:07:41 -0400 Subject: [PATCH 27/38] usb: bdc: Add compatible string for new style USB DT nodes Add compatible string for some newer boards that only have this as their match sting. Remove unused compatible string "brcm,bdc-v0.16". Signed-off-by: Al Cooper Acked-by: Florian Fainelli Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index d567e20234f0aa..27e8f50974bb89 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -625,7 +625,7 @@ static SIMPLE_DEV_PM_OPS(bdc_pm_ops, bdc_suspend, bdc_resume); static const struct of_device_id bdc_of_match[] = { - { .compatible = "brcm,bdc-v0.16" }, + { .compatible = "brcm,bdc-udc-v2" }, { .compatible = "brcm,bdc" }, { /* sentinel */ } }; From a95bdfd22076497288868c028619bc5995f5cc7f Mon Sep 17 00:00:00 2001 From: Sasi Kumar Date: Wed, 22 Jul 2020 13:07:42 -0400 Subject: [PATCH 28/38] bdc: Fix bug causing crash after multiple disconnects Multiple connects/disconnects can cause a crash on the second disconnect. The driver had a problem where it would try to send endpoint commands after it was disconnected which is not allowed by the hardware. The fix is to only allow the endpoint commands when the endpoint is connected. This will also fix issues that showed up when using configfs to create gadgets. Signed-off-by: Sasi Kumar Signed-off-by: Al Cooper Acked-by: Florian Fainelli Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc_core.c | 4 ++++ drivers/usb/gadget/udc/bdc/bdc_ep.c | 16 ++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 27e8f50974bb89..28909d4b81905d 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -278,6 +278,7 @@ static void bdc_mem_init(struct bdc *bdc, bool reinit) * in that case reinit is passed as 1 */ if (reinit) { + int i; /* Enable interrupts */ temp = bdc_readl(bdc->regs, BDC_BDCSC); temp |= BDC_GIE; @@ -287,6 +288,9 @@ static void bdc_mem_init(struct bdc *bdc, bool reinit) /* Initialize SRR to 0 */ memset(bdc->srr.sr_bds, 0, NUM_SR_ENTRIES * sizeof(struct bdc_bd)); + /* clear ep flags to avoid post disconnect stops/deconfigs */ + for (i = 1; i < bdc->num_eps; ++i) + bdc->bdc_ep_array[i]->flags = 0; } else { /* One time initiaization only */ /* Enable status report function pointers */ diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c index ba250cf75bef44..fafdc9fdb4a50a 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_ep.c +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c @@ -615,7 +615,6 @@ int bdc_ep_enable(struct bdc_ep *ep) } bdc_dbg_bd_list(bdc, ep); /* only for ep0: config ep is called for ep0 from connect event */ - ep->flags |= BDC_EP_ENABLED; if (ep->ep_num == 1) return ret; @@ -759,10 +758,13 @@ static int ep_dequeue(struct bdc_ep *ep, struct bdc_req *req) __func__, ep->name, start_bdi, end_bdi); dev_dbg(bdc->dev, "ep_dequeue ep=%p ep->desc=%p\n", ep, (void *)ep->usb_ep.desc); - /* Stop the ep to see where the HW is ? */ - ret = bdc_stop_ep(bdc, ep->ep_num); - /* if there is an issue with stopping ep, then no need to go further */ - if (ret) + /* if still connected, stop the ep to see where the HW is ? */ + if (!(bdc_readl(bdc->regs, BDC_USPC) & BDC_PST_MASK)) { + ret = bdc_stop_ep(bdc, ep->ep_num); + /* if there is an issue, then no need to go further */ + if (ret) + return 0; + } else return 0; /* @@ -1911,7 +1913,9 @@ static int bdc_gadget_ep_disable(struct usb_ep *_ep) __func__, ep->name, ep->flags); if (!(ep->flags & BDC_EP_ENABLED)) { - dev_warn(bdc->dev, "%s is already disabled\n", ep->name); + if (bdc->gadget.speed != USB_SPEED_UNKNOWN) + dev_warn(bdc->dev, "%s is already disabled\n", + ep->name); return 0; } spin_lock_irqsave(&bdc->lock, flags); From 33d1c71832b7b7e56ca82b2f19c5df31297657e6 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Wed, 22 Jul 2020 13:07:43 -0400 Subject: [PATCH 29/38] usb: bdc: Adb shows offline after resuming from S2 On Android systems, After temporarily putting device to S2 by short pressing the power button on the remote, the display turns off. Then press the power button to turn the display back up. Adb devices would show the devices is offline. It needs a physical disconnect of the usb cable or power cycle to bring the device back online. The device is operational otherwise. The problem is that during S2 resume, the ADB gadget driver could not link back with the BDC driver because the endpoint flags were cleared. The fix is to clear the endpoint flags for the disconnect case only and not for S2 exit. Signed-off-by: Al Cooper Acked-by: Florian Fainelli Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc_core.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 28909d4b81905d..d4ec1e37d50ab1 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -288,9 +288,13 @@ static void bdc_mem_init(struct bdc *bdc, bool reinit) /* Initialize SRR to 0 */ memset(bdc->srr.sr_bds, 0, NUM_SR_ENTRIES * sizeof(struct bdc_bd)); - /* clear ep flags to avoid post disconnect stops/deconfigs */ - for (i = 1; i < bdc->num_eps; ++i) - bdc->bdc_ep_array[i]->flags = 0; + /* + * clear ep flags to avoid post disconnect stops/deconfigs but + * not during S2 exit + */ + if (!bdc->gadget.speed) + for (i = 1; i < bdc->num_eps; ++i) + bdc->bdc_ep_array[i]->flags = 0; } else { /* One time initiaization only */ /* Enable status report function pointers */ From b10d33c4f068ebb70a607d644b7ace259127aa18 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Wed, 22 Jul 2020 13:07:44 -0400 Subject: [PATCH 30/38] usb: bdc: driver runs out of buffer descriptors on large ADB transfers Version v1.0.40 of the Android host ADB software increased maximum transfer sizes from 256K to 1M. Since the STB ADB gadget driver requests only 16K at a time, the BDC driver ran out of buffer descriptors (BDs) if the queuing happens faster than the incoming 16K transfers. This issue is fixed by doubling the number of BDs that can be queued so that the entire 1M request can be queued without running out of buffers. Signed-off-by: Al Cooper Acked-by: Florian Fainelli Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/bdc/bdc.h b/drivers/usb/gadget/udc/bdc/bdc.h index 6e1e881dc51e22..ac75e25c3b6ab4 100644 --- a/drivers/usb/gadget/udc/bdc/bdc.h +++ b/drivers/usb/gadget/udc/bdc/bdc.h @@ -44,7 +44,7 @@ #define NUM_SR_ENTRIES 64 /* Num of bds per table */ -#define NUM_BDS_PER_TABLE 32 +#define NUM_BDS_PER_TABLE 64 /* Num of tables in bd list for control,bulk and Int ep */ #define NUM_TABLES 2 From 5fc453d7de3d0c345812453823a3a56783c5f82c Mon Sep 17 00:00:00 2001 From: Danesh Petigara Date: Wed, 22 Jul 2020 13:07:45 -0400 Subject: [PATCH 31/38] usb: bdc: Halt controller on suspend GISB bus error kernel panics have been observed during S2 transition tests on the 7271t platform. The errors are a result of the BDC interrupt handler trying to access BDC register space after the system's suspend callbacks have completed. Adding a suspend hook to the BDC driver that halts the controller before S2 entry thus preventing unwanted access to the BDC register space during this transition. Signed-off-by: Danesh Petigara Signed-off-by: Al Cooper Acked-by: Florian Fainelli Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc_core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index d4ec1e37d50ab1..0f1617e34f38aa 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -603,9 +603,14 @@ static int bdc_remove(struct platform_device *pdev) static int bdc_suspend(struct device *dev) { struct bdc *bdc = dev_get_drvdata(dev); + int ret; - clk_disable_unprepare(bdc->clk); - return 0; + /* Halt the controller */ + ret = bdc_stop(bdc); + if (!ret) + clk_disable_unprepare(bdc->clk); + + return ret; } static int bdc_resume(struct device *dev) From 1fa645b1c92795b090ea8de314b5cecc22dbfb03 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 22 Jul 2020 13:07:46 -0400 Subject: [PATCH 32/38] usb: bdc: Use devm_clk_get_optional() The BDC clock is optional and we may get an -EPROBE_DEFER error code which would not be propagated correctly, fix this by using devm_clk_get_optional(). Signed-off-by: Florian Fainelli Signed-off-by: Al Cooper Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc_core.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 0f1617e34f38aa..5ff36525044ef0 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -493,11 +493,9 @@ static int bdc_probe(struct platform_device *pdev) dev_dbg(dev, "%s()\n", __func__); - clk = devm_clk_get(dev, "sw_usbd"); - if (IS_ERR(clk)) { - dev_info(dev, "Clock not found in Device Tree\n"); - clk = NULL; - } + clk = devm_clk_get_optional(dev, "sw_usbd"); + if (IS_ERR(clk)) + return PTR_ERR(clk); ret = clk_prepare_enable(clk); if (ret) { From ec3966268c67c4ff2fde2de5df2cb34e0ec57248 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 10 Jul 2020 14:33:37 +0300 Subject: [PATCH 33/38] dt-bindings: usb: ti,keystone-dwc3.yaml: Improve schema There were some review comments after the patch was integrated. Address those. Fixes: 1883a934e156 ("dt-bindings: usb: convert keystone-usb.txt to YAML") Reviewed-by: Rob Herring Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- .../bindings/usb/ti,keystone-dwc3.yaml | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/ti,keystone-dwc3.yaml b/Documentation/devicetree/bindings/usb/ti,keystone-dwc3.yaml index f127535feb0bb9..804b9b4f66540d 100644 --- a/Documentation/devicetree/bindings/usb/ti,keystone-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/ti,keystone-dwc3.yaml @@ -11,22 +11,36 @@ maintainers: properties: compatible: - oneOf: - - const: "ti,keystone-dwc3" - - const: "ti,am654-dwc3" + items: + - enum: + - ti,keystone-dwc3 + - ti,am654-dwc3 reg: maxItems: 1 - description: Address and length of the register set for the USB subsystem on - the SOC. + + '#address-cells': + const: 1 + + '#size-cells': + const: 1 + + ranges: true interrupts: maxItems: 1 - description: The irq number of this device that is used to interrupt the MPU. - clocks: - description: Clock ID for USB functional clock. + minItems: 1 + maxItems: 2 + + assigned-clocks: + minItems: 1 + maxItems: 2 + + assigned-clock-parents: + minItems: 1 + maxItems: 2 power-domains: description: Should contain a phandle to a PM domain provider node @@ -42,33 +56,42 @@ properties: phy-names: items: - - const: "usb3-phy" + - const: usb3-phy + + dma-coherent: true - dwc3: + dma-ranges: true + +patternProperties: + "usb@[a-f0-9]+$": + type: object description: This is the node representing the DWC3 controller instance Documentation/devicetree/bindings/usb/dwc3.txt required: - compatible - reg + - "#address-cells" + - "#size-cells" + - ranges - interrupts - - clocks + +additionalProperties: false examples: - | #include - usb: usb@2680000 { + dwc3@2680000 { compatible = "ti,keystone-dwc3"; #address-cells = <1>; #size-cells = <1>; reg = <0x2680000 0x10000>; clocks = <&clkusb>; - clock-names = "usb"; interrupts = ; ranges; - dwc3@2690000 { + usb@2690000 { compatible = "synopsys,dwc3"; reg = <0x2690000 0x70000>; interrupts = ; From a9cf8715180b18c62addbfe6f6267b8101903119 Mon Sep 17 00:00:00 2001 From: Ruslan Bilovol Date: Fri, 3 Jul 2020 16:49:03 +0300 Subject: [PATCH 34/38] usb: gadget: f_uac2: fix AC Interface Header Descriptor wTotalLength As per UAC2 spec (ch. 4.7.2), wTotalLength of AC Interface Header Descriptor "includes the combined length of this descriptor header and all Clock Source, Unit and Terminal descriptors." Thus add its size to its wTotalLength. Also after recent changes wTotalLength is calculated dynamically, update static definition of uac2_ac_header_descriptor accordingly Fixes: 132fcb460839 ("usb: gadget: Add Audio Class 2.0 Driver") Signed-off-by: Ruslan Bilovol Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index db2d4980cb3549..3633df6d7610f0 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -215,10 +215,7 @@ static struct uac2_ac_header_descriptor ac_hdr_desc = { .bDescriptorSubtype = UAC_MS_HEADER, .bcdADC = cpu_to_le16(0x200), .bCategory = UAC2_FUNCTION_IO_BOX, - .wTotalLength = cpu_to_le16(sizeof in_clk_src_desc - + sizeof out_clk_src_desc + sizeof usb_out_it_desc - + sizeof io_in_it_desc + sizeof usb_in_ot_desc - + sizeof io_out_ot_desc), + /* .wTotalLength = DYNAMIC */ .bmControls = 0, }; @@ -501,7 +498,7 @@ static void setup_descriptor(struct f_uac2_opts *opts) as_in_hdr_desc.bTerminalLink = usb_in_ot_desc.bTerminalID; iad_desc.bInterfaceCount = 1; - ac_hdr_desc.wTotalLength = 0; + ac_hdr_desc.wTotalLength = cpu_to_le16(sizeof(ac_hdr_desc)); if (EPIN_EN(opts)) { u16 len = le16_to_cpu(ac_hdr_desc.wTotalLength); From 7f2ca14d2f9b99e1579c8148dbabf92374ffc69a Mon Sep 17 00:00:00 2001 From: Zqiang Date: Tue, 30 Jun 2020 13:23:31 +0800 Subject: [PATCH 35/38] usb: gadget: function: printer: Interface is disabled and returns error After the device is disconnected from the host side, the interface of the device is reset. If the userspace operates the device again, an error code should be returned. Signed-off-by: Zqiang Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_printer.c | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index ec15f7637e405e..68697f596066c6 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -338,6 +338,11 @@ printer_open(struct inode *inode, struct file *fd) spin_lock_irqsave(&dev->lock, flags); + if (dev->interface < 0) { + spin_unlock_irqrestore(&dev->lock, flags); + return -ENODEV; + } + if (!dev->printer_cdev_open) { dev->printer_cdev_open = 1; fd->private_data = dev; @@ -430,6 +435,12 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) mutex_lock(&dev->lock_printer_io); spin_lock_irqsave(&dev->lock, flags); + if (dev->interface < 0) { + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return -ENODEV; + } + /* We will use this flag later to check if a printer reset happened * after we turn interrupts back on. */ @@ -561,6 +572,12 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) mutex_lock(&dev->lock_printer_io); spin_lock_irqsave(&dev->lock, flags); + if (dev->interface < 0) { + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return -ENODEV; + } + /* Check if a printer reset happens while we have interrupts on */ dev->reset_printer = 0; @@ -667,6 +684,13 @@ printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync) inode_lock(inode); spin_lock_irqsave(&dev->lock, flags); + + if (dev->interface < 0) { + spin_unlock_irqrestore(&dev->lock, flags); + inode_unlock(inode); + return -ENODEV; + } + tx_list_empty = (likely(list_empty(&dev->tx_reqs))); spin_unlock_irqrestore(&dev->lock, flags); @@ -689,6 +713,13 @@ printer_poll(struct file *fd, poll_table *wait) mutex_lock(&dev->lock_printer_io); spin_lock_irqsave(&dev->lock, flags); + + if (dev->interface < 0) { + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return EPOLLERR | EPOLLHUP; + } + setup_rx_reqs(dev); spin_unlock_irqrestore(&dev->lock, flags); mutex_unlock(&dev->lock_printer_io); @@ -722,6 +753,11 @@ printer_ioctl(struct file *fd, unsigned int code, unsigned long arg) spin_lock_irqsave(&dev->lock, flags); + if (dev->interface < 0) { + spin_unlock_irqrestore(&dev->lock, flags); + return -ENODEV; + } + switch (code) { case GADGET_GET_PRINTER_STATUS: status = (int)dev->printer_status; From ca14378560db5440893404a639d69846f46eb70e Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Wed, 1 Jul 2020 20:24:51 +0200 Subject: [PATCH 36/38] usb: dwc3: gadget: add frame number mask This patch adds a define DWC3_FRNUMBER_MASK for the commonly used 0x3fff mask and uses it. Signed-off-by: Michael Grzeschik Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 4 ++-- drivers/usb/dwc3/gadget.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 3ab6f118c508e3..ca27907667a36b 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1403,7 +1403,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) * Check if we can start isoc transfer on the next interval or * 4 uframes in the future with BIT[15:14] as dep->combo_num */ - test_frame_number = dep->frame_number & 0x3fff; + test_frame_number = dep->frame_number & DWC3_FRNUMBER_MASK; test_frame_number |= dep->combo_num << 14; test_frame_number += max_t(u32, 4, dep->interval); @@ -1450,7 +1450,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) else if (test0 && test1) dep->combo_num = 0; - dep->frame_number &= 0x3fff; + dep->frame_number &= DWC3_FRNUMBER_MASK; dep->frame_number |= dep->combo_num << 14; dep->frame_number += max_t(u32, 4, dep->interval); diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 42907dd8921264..bd85eb7fa9ef84 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -54,6 +54,8 @@ struct dwc3; /* U2 Device exit Latency */ #define DWC3_DEFAULT_U2_DEV_EXIT_LAT 0x1FF /* Less then 511 microsec */ +/* Frame/Microframe Number Mask */ +#define DWC3_FRNUMBER_MASK 0x3fff /* -------------------------------------------------------------------------- */ #define to_dwc3_request(r) (container_of(r, struct dwc3_request, request)) From c5a7092f401535d8a9159be963b0c318393c2a78 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Wed, 1 Jul 2020 20:24:52 +0200 Subject: [PATCH 37/38] usb: dwc3: gadget: make starting isoc transfers more robust Currently __dwc3_gadget_start_isoc must be called very shortly after XferNotReady. Otherwise the frame number is outdated and start transfer will fail, even with several retries. DSTS provides the lower 14 bit of the frame number. Use it in combination with the frame number provided by XferNotReady to guess the current frame number. This will succeed unless more than one 14 rollover has happened since XferNotReady. Start transfer might still fail if the frame number is increased immediately after DSTS is read. So retries are still needed. Don't drop the current request if this happens. This way it is not lost and can be used immediately to try again with the next frame number. With this change, __dwc3_gadget_start_isoc is still not successfully in all cases bit it increases the acceptable delay after XferNotReady significantly. Reviewed-by: Thinh Nguyen Signed-off-by: Michael Olbrich Signed-off-by: Michael Tretter Signed-off-by: Michael Grzeschik Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index ca27907667a36b..779b1737392942 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1463,6 +1463,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep) { + const struct usb_endpoint_descriptor *desc = dep->endpoint.desc; struct dwc3 *dwc = dep->dwc; int ret; int i; @@ -1480,6 +1481,27 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep) return dwc3_gadget_start_isoc_quirk(dep); } + if (desc->bInterval <= 14 && + dwc->gadget.speed >= USB_SPEED_HIGH) { + u32 frame = __dwc3_gadget_get_frame(dwc); + bool rollover = frame < + (dep->frame_number & DWC3_FRNUMBER_MASK); + + /* + * frame_number is set from XferNotReady and may be already + * out of date. DSTS only provides the lower 14 bit of the + * current frame number. So add the upper two bits of + * frame_number and handle a possible rollover. + * This will provide the correct frame_number unless more than + * rollover has happened since XferNotReady. + */ + + dep->frame_number = (dep->frame_number & ~DWC3_FRNUMBER_MASK) | + frame; + if (rollover) + dep->frame_number += BIT(14); + } + for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) { dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1); From f5e46aa4a124bfac34a770c4cfe5023de99380d4 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Wed, 1 Jul 2020 20:24:53 +0200 Subject: [PATCH 38/38] usb: dwc3: gadget: when the started list is empty stop the active xfer When we have nothing left to be queued after handling the last trb we have to stop the current transfer. This way we can ensure that the next request will be queued with a new and valid timestamp and will not directly run into an missed xfer. Reviewed-by: Thinh Nguyen Signed-off-by: Michael Grzeschik Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 779b1737392942..e44bfc3b5096d9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2738,7 +2738,9 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep, if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) goto out; - if (status == -EXDEV && list_empty(&dep->started_list)) + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && + list_empty(&dep->started_list) && + (list_empty(&dep->pending_list) || status == -EXDEV)) dwc3_stop_active_transfer(dep, true, true); else if (dwc3_gadget_ep_should_continue(dep)) if (__dwc3_gadget_kick_transfer(dep) == 0)