diff options
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r-- | drivers/usb/dwc3/Kconfig | 9 | ||||
-rw-r--r-- | drivers/usb/dwc3/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.c | 76 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.h | 2 | ||||
-rw-r--r-- | drivers/usb/dwc3/drd.c | 50 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-am62.c | 332 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-pci.c | 3 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-xilinx.c | 17 | ||||
-rw-r--r-- | drivers/usb/dwc3/ep0.c | 14 | ||||
-rw-r--r-- | drivers/usb/dwc3/gadget.c | 216 | ||||
-rw-r--r-- | drivers/usb/dwc3/gadget.h | 2 | ||||
-rw-r--r-- | drivers/usb/dwc3/host.c | 2 |
12 files changed, 568 insertions, 156 deletions
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index c483f28b695d..cd9a734522a7 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -159,4 +159,13 @@ config USB_DWC3_XILINX This driver handles both ZynqMP and Versal SoC operations. Say 'Y' or 'M' if you have one such device. +config USB_DWC3_AM62 + tristate "Texas Instruments AM62 Platforms" + depends on ARCH_K3 || COMPILE_TEST + default USB_DWC3 + help + Support TI's AM62 platforms with DesignWare Core USB3 IP. + The Designware Core USB3 IP is progammed to operate in + in USB 2.0 mode only. + Say 'Y' or 'M' here if you have one such device endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 2d499de6f66a..9f66bd82b639 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -42,6 +42,7 @@ endif # and allyesconfig builds. ## +obj-$(CONFIG_USB_DWC3_AM62) += dwc3-am62.o obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index d28cd1a6709b..573421984948 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -23,6 +23,7 @@ #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/of.h> +#include <linux/of_graph.h> #include <linux/acpi.h> #include <linux/pinctrl/consumer.h> #include <linux/reset.h> @@ -85,7 +86,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc) * mode. If the controller supports DRD but the dr_mode is not * specified or set to OTG, then set the mode to peripheral. */ - if (mode == USB_DR_MODE_OTG && + if (mode == USB_DR_MODE_OTG && !dwc->edev && (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) || !device_property_read_bool(dwc->dev, "usb-role-switch")) && !DWC3_VER_IS_PRIOR(DWC3, 330A)) @@ -297,6 +298,7 @@ int dwc3_core_soft_reset(struct dwc3 *dwc) udelay(1); } while (--retries); + dev_warn(dwc->dev, "DWC3 controller soft reset failed.\n"); return -ETIMEDOUT; done: @@ -342,7 +344,6 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc) * from the default, this will set clock period in DWC3_GUCTL * register. * @dwc: Pointer to our controller context structure - * @ref_clk_per: reference clock period in ns */ static void dwc3_ref_clk_period(struct dwc3 *dwc) { @@ -964,10 +965,8 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) return; vals = kcalloc(ntype, sizeof(u32), GFP_KERNEL); - if (!vals) { - dev_err(dev, "Error to get memory\n"); + if (!vals) return; - } /* Get INCR burst type, and parse it */ ret = device_property_read_u32_array(dev, @@ -1268,40 +1267,36 @@ static int dwc3_core_get_phy(struct dwc3 *dwc) if (IS_ERR(dwc->usb2_phy)) { ret = PTR_ERR(dwc->usb2_phy); - if (ret == -ENXIO || ret == -ENODEV) { + if (ret == -ENXIO || ret == -ENODEV) dwc->usb2_phy = NULL; - } else { + else return dev_err_probe(dev, ret, "no usb2 phy configured\n"); - } } if (IS_ERR(dwc->usb3_phy)) { ret = PTR_ERR(dwc->usb3_phy); - if (ret == -ENXIO || ret == -ENODEV) { + if (ret == -ENXIO || ret == -ENODEV) dwc->usb3_phy = NULL; - } else { + else return dev_err_probe(dev, ret, "no usb3 phy configured\n"); - } } dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy"); if (IS_ERR(dwc->usb2_generic_phy)) { ret = PTR_ERR(dwc->usb2_generic_phy); - if (ret == -ENOSYS || ret == -ENODEV) { + if (ret == -ENOSYS || ret == -ENODEV) dwc->usb2_generic_phy = NULL; - } else { + else return dev_err_probe(dev, ret, "no usb2 phy configured\n"); - } } dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy"); if (IS_ERR(dwc->usb3_generic_phy)) { ret = PTR_ERR(dwc->usb3_generic_phy); - if (ret == -ENOSYS || ret == -ENODEV) { + if (ret == -ENOSYS || ret == -ENODEV) dwc->usb3_generic_phy = NULL; - } else { + else return dev_err_probe(dev, ret, "no usb3 phy configured\n"); - } } return 0; @@ -1633,6 +1628,46 @@ static void dwc3_check_params(struct dwc3 *dwc) } } +static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) +{ + struct device *dev = dwc->dev; + struct device_node *np_phy; + struct extcon_dev *edev = NULL; + const char *name; + + if (device_property_read_bool(dev, "extcon")) + return extcon_get_edev_by_phandle(dev, 0); + + /* + * Device tree platforms should get extcon via phandle. + * On ACPI platforms, we get the name from a device property. + * This device property is for kernel internal use only and + * is expected to be set by the glue code. + */ + if (device_property_read_string(dev, "linux,extcon-name", &name) == 0) + return extcon_get_extcon_dev(name); + + /* + * Try to get an extcon device from the USB PHY controller's "port" + * node. Check if it has the "port" node first, to avoid printing the + * error message from underlying code, as it's a valid case: extcon + * device (and "port" node) may be missing in case of "usb-role-switch" + * or OTG mode. + */ + np_phy = of_parse_phandle(dev->of_node, "phys", 0); + if (of_graph_is_present(np_phy)) { + struct device_node *np_conn; + + np_conn = of_graph_get_remote_node(np_phy, -1, -1); + if (np_conn) + edev = extcon_find_edev_by_node(np_conn); + of_node_put(np_conn); + } + of_node_put(np_phy); + + return edev; +} + static int dwc3_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1768,6 +1803,13 @@ static int dwc3_probe(struct platform_device *pdev) goto err2; } + dwc->edev = dwc3_get_extcon(dwc); + if (IS_ERR(dwc->edev)) { + ret = PTR_ERR(dwc->edev); + dev_err_probe(dwc->dev, ret, "failed to get extcon\n"); + goto err3; + } + ret = dwc3_get_dr_mode(dwc); if (ret) goto err3; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 5c9d467195a6..81c486b3941c 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1046,6 +1046,7 @@ struct dwc3_scratchpad_array { * @tx_thr_num_pkt_prd: periodic ESS transmit packet count * @tx_max_burst_prd: max periodic ESS transmit burst size * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize + * @clear_stall_protocol: endpoint number that requires a delayed status phase * @hsphy_interface: "utmi" or "ulpi" * @connected: true when we're connected to a host, false otherwise * @softconnect: true when gadget connect is called, false when disconnect runs @@ -1266,6 +1267,7 @@ struct dwc3 { u8 tx_thr_num_pkt_prd; u8 tx_max_burst_prd; u8 tx_fifo_resize_max_num; + u8 clear_stall_protocol; const char *hsphy_interface; diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 8cad9e7d3368..039bf241769a 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -8,7 +8,6 @@ */ #include <linux/extcon.h> -#include <linux/of_graph.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/property.h> @@ -439,51 +438,6 @@ static int dwc3_drd_notifier(struct notifier_block *nb, return NOTIFY_DONE; } -static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) -{ - struct device *dev = dwc->dev; - struct device_node *np_phy; - struct extcon_dev *edev = NULL; - const char *name; - - if (device_property_read_bool(dev, "extcon")) - return extcon_get_edev_by_phandle(dev, 0); - - /* - * Device tree platforms should get extcon via phandle. - * On ACPI platforms, we get the name from a device property. - * This device property is for kernel internal use only and - * is expected to be set by the glue code. - */ - if (device_property_read_string(dev, "linux,extcon-name", &name) == 0) { - edev = extcon_get_extcon_dev(name); - if (!edev) - return ERR_PTR(-EPROBE_DEFER); - - return edev; - } - - /* - * Try to get an extcon device from the USB PHY controller's "port" - * node. Check if it has the "port" node first, to avoid printing the - * error message from underlying code, as it's a valid case: extcon - * device (and "port" node) may be missing in case of "usb-role-switch" - * or OTG mode. - */ - np_phy = of_parse_phandle(dev->of_node, "phys", 0); - if (of_graph_is_present(np_phy)) { - struct device_node *np_conn; - - np_conn = of_graph_get_remote_node(np_phy, -1, -1); - if (np_conn) - edev = extcon_find_edev_by_node(np_conn); - of_node_put(np_conn); - } - of_node_put(np_phy); - - return edev; -} - #if IS_ENABLED(CONFIG_USB_ROLE_SWITCH) #define ROLE_SWITCH 1 static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, @@ -588,10 +542,6 @@ int dwc3_drd_init(struct dwc3 *dwc) device_property_read_bool(dwc->dev, "usb-role-switch")) return dwc3_setup_role_switch(dwc); - dwc->edev = dwc3_get_extcon(dwc); - if (IS_ERR(dwc->edev)) - return PTR_ERR(dwc->edev); - if (dwc->edev) { dwc->edev_nb.notifier_call = dwc3_drd_notifier; ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST, diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c new file mode 100644 index 000000000000..fea7aca35dc8 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-am62.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dwc3-am62.c - TI specific Glue layer for AM62 DWC3 USB Controller + * + * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pm_runtime.h> +#include <linux/clk.h> +#include <linux/regmap.h> +#include <linux/pinctrl/consumer.h> + +/* USB WRAPPER register offsets */ +#define USBSS_PID 0x0 +#define USBSS_OVERCURRENT_CTRL 0x4 +#define USBSS_PHY_CONFIG 0x8 +#define USBSS_PHY_TEST 0xc +#define USBSS_CORE_STAT 0x14 +#define USBSS_HOST_VBUS_CTRL 0x18 +#define USBSS_MODE_CONTROL 0x1c +#define USBSS_WAKEUP_CONFIG 0x30 +#define USBSS_WAKEUP_STAT 0x34 +#define USBSS_OVERRIDE_CONFIG 0x38 +#define USBSS_IRQ_MISC_STATUS_RAW 0x430 +#define USBSS_IRQ_MISC_STATUS 0x434 +#define USBSS_IRQ_MISC_ENABLE_SET 0x438 +#define USBSS_IRQ_MISC_ENABLE_CLR 0x43c +#define USBSS_IRQ_MISC_EOI 0x440 +#define USBSS_INTR_TEST 0x490 +#define USBSS_VBUS_FILTER 0x614 +#define USBSS_VBUS_STAT 0x618 +#define USBSS_DEBUG_CFG 0x708 +#define USBSS_DEBUG_DATA 0x70c +#define USBSS_HOST_HUB_CTRL 0x714 + +/* PHY CONFIG register bits */ +#define USBSS_PHY_VBUS_SEL_MASK GENMASK(2, 1) +#define USBSS_PHY_VBUS_SEL_SHIFT 1 +#define USBSS_PHY_LANE_REVERSE BIT(0) + +/* MODE CONTROL register bits */ +#define USBSS_MODE_VALID BIT(0) + +/* WAKEUP CONFIG register bits */ +#define USBSS_WAKEUP_CFG_OVERCURRENT_EN BIT(3) +#define USBSS_WAKEUP_CFG_LINESTATE_EN BIT(2) +#define USBSS_WAKEUP_CFG_SESSVALID_EN BIT(1) +#define USBSS_WAKEUP_CFG_VBUSVALID_EN BIT(0) + +/* WAKEUP STAT register bits */ +#define USBSS_WAKEUP_STAT_OVERCURRENT BIT(4) +#define USBSS_WAKEUP_STAT_LINESTATE BIT(3) +#define USBSS_WAKEUP_STAT_SESSVALID BIT(2) +#define USBSS_WAKEUP_STAT_VBUSVALID BIT(1) +#define USBSS_WAKEUP_STAT_CLR BIT(0) + +/* IRQ_MISC_STATUS_RAW register bits */ +#define USBSS_IRQ_MISC_RAW_VBUSVALID BIT(22) +#define USBSS_IRQ_MISC_RAW_SESSVALID BIT(20) + +/* IRQ_MISC_STATUS register bits */ +#define USBSS_IRQ_MISC_VBUSVALID BIT(22) +#define USBSS_IRQ_MISC_SESSVALID BIT(20) + +/* IRQ_MISC_ENABLE_SET register bits */ +#define USBSS_IRQ_MISC_ENABLE_SET_VBUSVALID BIT(22) +#define USBSS_IRQ_MISC_ENABLE_SET_SESSVALID BIT(20) + +/* IRQ_MISC_ENABLE_CLR register bits */ +#define USBSS_IRQ_MISC_ENABLE_CLR_VBUSVALID BIT(22) +#define USBSS_IRQ_MISC_ENABLE_CLR_SESSVALID BIT(20) + +/* IRQ_MISC_EOI register bits */ +#define USBSS_IRQ_MISC_EOI_VECTOR BIT(0) + +/* VBUS_STAT register bits */ +#define USBSS_VBUS_STAT_SESSVALID BIT(2) +#define USBSS_VBUS_STAT_VBUSVALID BIT(0) + +/* Mask for PHY PLL REFCLK */ +#define PHY_PLL_REFCLK_MASK GENMASK(3, 0) + +#define DWC3_AM62_AUTOSUSPEND_DELAY 100 + +struct dwc3_data { + struct device *dev; + void __iomem *usbss; + struct clk *usb2_refclk; + int rate_code; + struct regmap *syscon; + unsigned int offset; + unsigned int vbus_divider; +}; + +static const int dwc3_ti_rate_table[] = { /* in KHZ */ + 9600, + 10000, + 12000, + 19200, + 20000, + 24000, + 25000, + 26000, + 38400, + 40000, + 58000, + 50000, + 52000, +}; + +static inline u32 dwc3_ti_readl(struct dwc3_data *data, u32 offset) +{ + return readl((data->usbss) + offset); +} + +static inline void dwc3_ti_writel(struct dwc3_data *data, u32 offset, u32 value) +{ + writel(value, (data->usbss) + offset); +} + +static int phy_syscon_pll_refclk(struct dwc3_data *data) +{ + struct device *dev = data->dev; + struct device_node *node = dev->of_node; + struct of_phandle_args args; + struct regmap *syscon; + int ret; + + syscon = syscon_regmap_lookup_by_phandle(node, "ti,syscon-phy-pll-refclk"); + if (IS_ERR(syscon)) { + dev_err(dev, "unable to get ti,syscon-phy-pll-refclk regmap\n"); + return PTR_ERR(syscon); + } + + data->syscon = syscon; + + ret = of_parse_phandle_with_fixed_args(node, "ti,syscon-phy-pll-refclk", 1, + 0, &args); + if (ret) + return ret; + + data->offset = args.args[0]; + + ret = regmap_update_bits(data->syscon, data->offset, PHY_PLL_REFCLK_MASK, data->rate_code); + if (ret) { + dev_err(dev, "failed to set phy pll reference clock rate\n"); + return ret; + } + + return 0; +} + +static int dwc3_ti_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = pdev->dev.of_node; + struct dwc3_data *data; + int i, ret; + unsigned long rate; + u32 reg; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = dev; + platform_set_drvdata(pdev, data); + + data->usbss = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->usbss)) { + dev_err(dev, "can't map IOMEM resource\n"); + return PTR_ERR(data->usbss); + } + + data->usb2_refclk = devm_clk_get(dev, "ref"); + if (IS_ERR(data->usb2_refclk)) { + dev_err(dev, "can't get usb2_refclk\n"); + return PTR_ERR(data->usb2_refclk); + } + + /* Calculate the rate code */ + rate = clk_get_rate(data->usb2_refclk); + rate /= 1000; // To KHz + for (i = 0; i < ARRAY_SIZE(dwc3_ti_rate_table); i++) { + if (dwc3_ti_rate_table[i] == rate) + break; + } + + if (i == ARRAY_SIZE(dwc3_ti_rate_table)) { + dev_err(dev, "unsupported usb2_refclk rate: %lu KHz\n", rate); + ret = -EINVAL; + goto err_clk_disable; + } + + data->rate_code = i; + + /* Read the syscon property and set the rate code */ + ret = phy_syscon_pll_refclk(data); + if (ret) + goto err_clk_disable; + + /* VBUS divider select */ + data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider"); + reg = dwc3_ti_readl(data, USBSS_PHY_CONFIG); + if (data->vbus_divider) + reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT; + + dwc3_ti_writel(data, USBSS_PHY_CONFIG, reg); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + /* + * Don't ignore its dependencies with its children + */ + pm_suspend_ignore_children(dev, false); + clk_prepare_enable(data->usb2_refclk); + pm_runtime_get_noresume(dev); + + ret = of_platform_populate(node, NULL, NULL, dev); + if (ret) { + dev_err(dev, "failed to create dwc3 core: %d\n", ret); + goto err_pm_disable; + } + + /* Set mode valid bit to indicate role is valid */ + reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL); + reg |= USBSS_MODE_VALID; + dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg); + + /* Setting up autosuspend */ + pm_runtime_set_autosuspend_delay(dev, DWC3_AM62_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return 0; + +err_pm_disable: + clk_disable_unprepare(data->usb2_refclk); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); +err_clk_disable: + clk_put(data->usb2_refclk); + return ret; +} + +static int dwc3_ti_remove_core(struct device *dev, void *c) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + return 0; +} + +static int dwc3_ti_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dwc3_data *data = platform_get_drvdata(pdev); + u32 reg; + + device_for_each_child(dev, NULL, dwc3_ti_remove_core); + + /* Clear mode valid bit */ + reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL); + reg &= ~USBSS_MODE_VALID; + dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg); + + pm_runtime_put_sync(dev); + clk_disable_unprepare(data->usb2_refclk); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + + clk_put(data->usb2_refclk); + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int dwc3_ti_suspend_common(struct device *dev) +{ + struct dwc3_data *data = dev_get_drvdata(dev); + + clk_disable_unprepare(data->usb2_refclk); + + return 0; +} + +static int dwc3_ti_resume_common(struct device *dev) +{ + struct dwc3_data *data = dev_get_drvdata(dev); + + clk_prepare_enable(data->usb2_refclk); + + return 0; +} + +static UNIVERSAL_DEV_PM_OPS(dwc3_ti_pm_ops, dwc3_ti_suspend_common, + dwc3_ti_resume_common, NULL); + +#define DEV_PM_OPS (&dwc3_ti_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + +static const struct of_device_id dwc3_ti_of_match[] = { + { .compatible = "ti,am62-usb"}, + {}, +}; +MODULE_DEVICE_TABLE(of, dwc3_ti_of_match); + +static struct platform_driver dwc3_ti_driver = { + .probe = dwc3_ti_probe, + .remove = dwc3_ti_remove, + .driver = { + .name = "dwc3-am62", + .pm = DEV_PM_OPS, + .of_match_table = dwc3_ti_of_match, + }, +}; + +module_platform_driver(dwc3_ti_driver); + +MODULE_ALIAS("platform:dwc3-am62"); +MODULE_AUTHOR("Aswath Govindraju <a-govindraju@ti.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DesignWare USB3 TI Glue Layer"); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 2e19e0e4ea53..6b018048fe2e 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -127,6 +127,7 @@ static const struct property_entry dwc3_pci_intel_phy_charger_detect_properties[ PROPERTY_ENTRY_STRING("dr_mode", "peripheral"), PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"), PROPERTY_ENTRY_BOOL("linux,phy_charger_detect"), + PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"), {} }; @@ -288,7 +289,7 @@ static void dwc3_pci_resume_work(struct work_struct *work) int ret; ret = pm_runtime_get_sync(&dwc3->dev); - if (ret) { + if (ret < 0) { pm_runtime_put_sync_autosuspend(&dwc3->dev); return; } diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index a6f3a9b38789..67b237c7a76a 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -13,6 +13,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> +#include <linux/of_gpio.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> #include <linux/reset.h> @@ -98,6 +99,7 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) { struct device *dev = priv_data->dev; struct reset_control *crst, *hibrst, *apbrst; + struct gpio_desc *reset_gpio; struct phy *usb3_phy; int ret = 0; u32 reg; @@ -201,6 +203,21 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) } skip_usb3_phy: + /* ulpi reset via gpio-modepin or gpio-framework driver */ + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(reset_gpio)) { + return dev_err_probe(dev, PTR_ERR(reset_gpio), + "Failed to request reset GPIO\n"); + } + + if (reset_gpio) { + /* Toggle ulpi to reset the phy. */ + gpiod_set_value_cansleep(reset_gpio, 1); + usleep_range(5000, 10000); + gpiod_set_value_cansleep(reset_gpio, 0); + usleep_range(5000, 10000); + } + /* * This routes the USB DMA traffic to go through FPD path instead * of reaching DDR directly. This traffic routing is needed to diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 1064be5518f6..5d642660fd15 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -218,7 +218,7 @@ out: return ret; } -static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) +void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) { struct dwc3_ep *dep; @@ -813,7 +813,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, int ret = -EINVAL; u32 len; - if (!dwc->gadget_driver) + if (!dwc->gadget_driver || !dwc->connected) goto out; trace_dwc3_ctrl_req(ctrl); @@ -1080,6 +1080,7 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc) unsigned int direction = !dwc->ep0_expect_in; dwc->delayed_status = false; + dwc->clear_stall_protocol = 0; if (dwc->ep0state != EP0_STATUS_PHASE) return; @@ -1087,13 +1088,18 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc) __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); } -static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) +void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_gadget_ep_cmd_params params; u32 cmd; int ret; - if (!dep->resource_index) + /* + * For status/DATA OUT stage, TRB will be queued on ep0 out + * endpoint for which resource index is zero. Hence allow + * queuing ENDXFER command for ep0 out endpoint. + */ + if (!dep->resource_index && dep->number) return; cmd = DWC3_DEPCMD_ENDTRANSFER; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0b9c2493844a..8716bece1072 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -657,7 +657,6 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) /** * dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value * @dwc: pointer to the DWC3 context - * @nfifos: number of fifos to calculate for * * Calculates the size value based on the equation below: * @@ -690,7 +689,7 @@ static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult) } /** - * dwc3_gadget_clear_tx_fifo_size - Clears txfifo allocation + * dwc3_gadget_clear_tx_fifos - Clears txfifo allocation * @dwc: pointer to the DWC3 context * * Iterates through all the endpoint registers and clears the previous txfifo @@ -783,7 +782,8 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) num_fifos = 3; if (dep->endpoint.maxburst > 6 && - usb_endpoint_xfer_bulk(dep->endpoint.desc) && DWC3_IP_IS(DWC31)) + (usb_endpoint_xfer_bulk(dep->endpoint.desc) || + usb_endpoint_xfer_isoc(dep->endpoint.desc)) && DWC3_IP_IS(DWC31)) num_fifos = dwc->tx_fifo_resize_max_num; /* FIFO size for a single buffer */ @@ -882,12 +882,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) reg |= DWC3_DALEPENA_EP(dep->number); dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + dep->trb_dequeue = 0; + dep->trb_enqueue = 0; + if (usb_endpoint_xfer_control(desc)) goto out; /* Initialize the TRB ring */ - dep->trb_dequeue = 0; - dep->trb_enqueue = 0; memset(dep->trb_pool, 0, sizeof(struct dwc3_trb) * DWC3_TRB_NUM); @@ -2001,10 +2002,10 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep) { struct dwc3_request *req; - struct dwc3_request *tmp; struct dwc3 *dwc = dep->dwc; - list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) { + while (!list_empty(&dep->cancelled_list)) { + req = next_request(&dep->cancelled_list); dwc3_gadget_ep_skip_trbs(dep, req); switch (req->status) { case DWC3_REQUEST_STATUS_DISCONNECTED: @@ -2021,6 +2022,12 @@ static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep) dwc3_gadget_giveback(dep, req, -ECONNRESET); break; } + /* + * The endpoint is disabled, let the dwc3_remove_requests() + * handle the cleanup. + */ + if (!dep->endpoint.desc) + break; } } @@ -2056,16 +2063,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, if (r == req) { struct dwc3_request *t; - /* - * If a Setup packet is received but yet to DMA out, the controller will - * not process the End Transfer command of any endpoint. Polling of its - * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a - * timeout. Delay issuing the End Transfer command until the Setup TRB is - * prepared. - */ - if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) - dep->flags |= DWC3_EP_DELAY_STOP; - /* wait until it is processed */ dwc3_stop_active_transfer(dep, true, true); @@ -2152,6 +2149,9 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) if (dep->flags & DWC3_EP_END_TRANSFER_PENDING || (dep->flags & DWC3_EP_DELAY_STOP)) { dep->flags |= DWC3_EP_PENDING_CLEAR_STALL; + if (protocol) + dwc->clear_stall_protocol = dep->number; + return 0; } @@ -2498,28 +2498,64 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc); static void __dwc3_gadget_stop(struct dwc3 *dwc); static int __dwc3_gadget_start(struct dwc3 *dwc); -static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) +static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc) { - struct dwc3 *dwc = gadget_to_dwc(g); - unsigned long flags; - int ret; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + dwc->connected = false; - is_on = !!is_on; - dwc->softconnect = is_on; /* * Per databook, when we want to stop the gadget, if a control transfer * is still in process, complete it and get the core into setup phase. */ - if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) { + if (dwc->ep0state != EP0_SETUP_PHASE) { + int ret; + reinit_completion(&dwc->ep0_in_setup); + spin_unlock_irqrestore(&dwc->lock, flags); ret = wait_for_completion_timeout(&dwc->ep0_in_setup, msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT)); + spin_lock_irqsave(&dwc->lock, flags); if (ret == 0) dev_warn(dwc->dev, "timed out waiting for SETUP phase\n"); } /* + * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a + * Section 4.1.8 Table 4-7, it states that for a device-initiated + * disconnect, the SW needs to ensure that it sends "a DEPENDXFER + * command for any active transfers" before clearing the RunStop + * bit. + */ + dwc3_stop_active_transfers(dwc); + __dwc3_gadget_stop(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + + /* + * Note: if the GEVNTCOUNT indicates events in the event buffer, the + * driver needs to acknowledge them before the controller can halt. + * Simply let the interrupt handler acknowledges and handle the + * remaining event generated by the controller while polling for + * DSTS.DEVCTLHLT. + */ + return dwc3_gadget_run_stop(dwc, false, false); +} + +static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + int ret; + + is_on = !!is_on; + + if (dwc->pullups_connected == is_on) + return 0; + + dwc->softconnect = is_on; + + /* * Avoid issuing a runtime resume if the device is already in the * suspended state during gadget disconnect. DWC3 gadget was already * halted/stopped during runtime suspend. @@ -2541,42 +2577,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) return 0; } - /* - * Synchronize and disable any further event handling while controller - * is being enabled/disabled. - */ - disable_irq(dwc->irq_gadget); - - spin_lock_irqsave(&dwc->lock, flags); - if (!is_on) { - u32 count; - - dwc->connected = false; - /* - * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a - * Section 4.1.8 Table 4-7, it states that for a device-initiated - * disconnect, the SW needs to ensure that it sends "a DEPENDXFER - * command for any active transfers" before clearing the RunStop - * bit. - */ - dwc3_stop_active_transfers(dwc); - __dwc3_gadget_stop(dwc); - - /* - * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a - * Section 1.3.4, it mentions that for the DEVCTRLHLT bit, the - * "software needs to acknowledge the events that are generated - * (by writing to GEVNTCOUNTn) while it is waiting for this bit - * to be set to '1'." - */ - count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); - count &= DWC3_GEVNTCOUNT_MASK; - if (count > 0) { - dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count); - dwc->ev_buf->lpos = (dwc->ev_buf->lpos + count) % - dwc->ev_buf->length; - } + ret = dwc3_gadget_soft_disconnect(dwc); } else { /* * In the Synopsys DWC_usb31 1.90a programming guide section @@ -2584,18 +2586,13 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) * device-initiated disconnect requires a core soft reset * (DCTL.CSftRst) before enabling the run/stop bit. */ - spin_unlock_irqrestore(&dwc->lock, flags); dwc3_core_soft_reset(dwc); - spin_lock_irqsave(&dwc->lock, flags); dwc3_event_buffers_setup(dwc); __dwc3_gadget_start(dwc); + ret = dwc3_gadget_run_stop(dwc, true, false); } - ret = dwc3_gadget_run_stop(dwc, is_on, false); - spin_unlock_irqrestore(&dwc->lock, flags); - enable_irq(dwc->irq_gadget); - pm_runtime_put(dwc->dev); return ret; @@ -2745,6 +2742,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) /* begin to receive SETUP packets */ dwc->ep0state = EP0_SETUP_PHASE; + dwc->ep0_bounced = false; dwc->link_state = DWC3_LINK_STATE_SS_DIS; dwc->delayed_status = false; dwc3_ep0_out_start(dwc); @@ -2978,6 +2976,7 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep) struct dwc3 *dwc = dep->dwc; u32 mdwidth; int size; + int maxpacket; mdwidth = dwc3_mdwidth(dwc); @@ -2990,21 +2989,24 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep) else size = DWC31_GTXFIFOSIZ_TXFDEP(size); - /* FIFO Depth is in MDWDITH bytes. Multiply */ - size *= mdwidth; - /* - * To meet performance requirement, a minimum TxFIFO size of 3x - * MaxPacketSize is recommended for endpoints that support burst and a - * minimum TxFIFO size of 2x MaxPacketSize for endpoints that don't - * support burst. Use those numbers and we can calculate the max packet - * limit as below. + * maxpacket size is determined as part of the following, after assuming + * a mult value of one maxpacket: + * DWC3 revision 280A and prior: + * fifo_size = mult * (max_packet / mdwidth) + 1; + * maxpacket = mdwidth * (fifo_size - 1); + * + * DWC3 revision 290A and onwards: + * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1 + * maxpacket = mdwidth * ((fifo_size - 1) - 1) - mdwidth; */ - if (dwc->maximum_speed >= USB_SPEED_SUPER) - size /= 3; + if (DWC3_VER_IS_PRIOR(DWC3, 290A)) + maxpacket = mdwidth * (size - 1); else - size /= 2; + maxpacket = mdwidth * ((size - 1) - 1) - mdwidth; + /* Functionally, space for one max packet is sufficient */ + size = min_t(int, maxpacket, 1024); usb_ep_set_maxpacket_limit(&dep->endpoint, size); dep->endpoint.max_streams = 16; @@ -3333,15 +3335,21 @@ static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep, const struct dwc3_event_depevt *event, int status) { struct dwc3_request *req; - struct dwc3_request *tmp; - list_for_each_entry_safe(req, tmp, &dep->started_list, list) { + while (!list_empty(&dep->started_list)) { int ret; + req = next_request(&dep->started_list); ret = dwc3_gadget_ep_cleanup_completed_request(dep, event, req, status); if (ret) break; + /* + * The endpoint is disabled, let the dwc3_remove_requests() + * handle the cleanup. + */ + if (!dep->endpoint.desc) + break; } } @@ -3380,14 +3388,14 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep, struct dwc3 *dwc = dep->dwc; bool no_started_trb = true; - if (!dep->endpoint.desc) - return no_started_trb; - dwc3_gadget_ep_cleanup_completed_requests(dep, event, status); if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) goto out; + if (!dep->endpoint.desc) + return no_started_trb; + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && list_empty(&dep->started_list) && (list_empty(&dep->pending_list) || status == -EXDEV)) @@ -3512,7 +3520,7 @@ static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep, } dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); - if (dwc->delayed_status) + if (dwc->clear_stall_protocol == dep->number) dwc3_ep0_send_delayed_status(dwc); } @@ -3673,12 +3681,35 @@ static void dwc3_reset_gadget(struct dwc3 *dwc) void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) { + struct dwc3 *dwc = dep->dwc; + + /* + * Only issue End Transfer command to the control endpoint of a started + * Data Phase. Typically we should only do so in error cases such as + * invalid/unexpected direction as described in the control transfer + * flow of the programming guide. + */ + if (dep->number <= 1 && dwc->ep0state != EP0_DATA_PHASE) + return; + if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) || (dep->flags & DWC3_EP_DELAY_STOP) || (dep->flags & DWC3_EP_END_TRANSFER_PENDING)) return; /* + * If a Setup packet is received but yet to DMA out, the controller will + * not process the End Transfer command of any endpoint. Polling of its + * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a + * timeout. Delay issuing the End Transfer command until the Setup TRB is + * prepared. + */ + if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) { + dep->flags |= DWC3_EP_DELAY_STOP; + return; + } + + /* * NOTICE: We are violating what the Databook says about the * EndTransfer command. Ideally we would _always_ wait for the * EndTransfer Command Completion IRQ, but that's causing too @@ -3795,6 +3826,27 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) } dwc3_reset_gadget(dwc); + + /* + * From SNPS databook section 8.1.2, the EP0 should be in setup + * phase. So ensure that EP0 is in setup phase by issuing a stall + * and restart if EP0 is not in setup phase. + */ + if (dwc->ep0state != EP0_SETUP_PHASE) { + unsigned int dir; + + dir = !!dwc->ep0_expect_in; + if (dwc->ep0state == EP0_DATA_PHASE) + dwc3_ep0_end_control_data(dwc, dwc->eps[dir]); + else + dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]); + + dwc->eps[0]->trb_enqueue = 0; + dwc->eps[1]->trb_enqueue = 0; + + dwc3_ep0_stall_and_restart(dwc); + } + /* * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a * Section 4.1.2 Table 4-2, it states that during a USB reset, the SW diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index f763380e672e..55a56cf67d73 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -110,6 +110,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event); void dwc3_ep0_out_start(struct dwc3 *dwc); +void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep); +void dwc3_ep0_stall_and_restart(struct dwc3 *dwc); int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index eda871973d6c..f56c30cf151e 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -7,7 +7,6 @@ * Authors: Felipe Balbi <balbi@ti.com>, */ -#include <linux/acpi.h> #include <linux/irq.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -83,7 +82,6 @@ int dwc3_host_init(struct dwc3 *dwc) } xhci->dev.parent = dwc->dev; - ACPI_COMPANION_SET(&xhci->dev, ACPI_COMPANION(dwc->dev)); dwc->xhci = xhci; |