diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index cd4026c..34c44ff 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -1,7 +1,8 @@ obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed.o clk-divider.o clk-fixed-factor.o \ clk-mux.o clk-gate.o clk-composite.o \ clk-fractional-divider.o clk-conf.o \ - clk-gate-shared.o clk-gpio.o + clk-gate-shared.o clk-gpio.o \ + clk-bulk.o obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_ARCH_MVEBU) += mvebu/ diff --git a/drivers/clk/clk-bulk.c b/drivers/clk/clk-bulk.c new file mode 100644 index 0000000..ddbe32f --- /dev/null +++ b/drivers/clk/clk-bulk.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2017 NXP + * + * Dong Aisheng + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +void clk_bulk_put(int num_clks, struct clk_bulk_data *clks) +{ + while (--num_clks >= 0) { + clk_put(clks[num_clks].clk); + clks[num_clks].clk = NULL; + } +} +EXPORT_SYMBOL_GPL(clk_bulk_put); + +int __must_check clk_bulk_get(struct device_d *dev, int num_clks, + struct clk_bulk_data *clks) +{ + int ret; + int i; + + for (i = 0; i < num_clks; i++) + clks[i].clk = NULL; + + for (i = 0; i < num_clks; i++) { + clks[i].clk = clk_get(dev, clks[i].id); + if (IS_ERR(clks[i].clk)) { + ret = PTR_ERR(clks[i].clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get clk '%s': %d\n", + clks[i].id, ret); + clks[i].clk = NULL; + goto err; + } + } + + return 0; + +err: + clk_bulk_put(i, clks); + + return ret; +} +EXPORT_SYMBOL(clk_bulk_get); + +/** + * clk_bulk_disable - gate a set of clocks + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table being gated + * + * clk_bulk_disable must not sleep, which differentiates it from + * clk_bulk_unprepare. clk_bulk_disable must be called before + * clk_bulk_unprepare. + */ +void clk_bulk_disable(int num_clks, const struct clk_bulk_data *clks) +{ + + while (--num_clks >= 0) + clk_disable(clks[num_clks].clk); +} +EXPORT_SYMBOL_GPL(clk_bulk_disable); + +/** + * clk_bulk_enable - ungate a set of clocks + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table being ungated + * + * clk_bulk_enable must not sleep + * Returns 0 on success, -EERROR otherwise. + */ +int __must_check clk_bulk_enable(int num_clks, const struct clk_bulk_data *clks) +{ + int ret; + int i; + + for (i = 0; i < num_clks; i++) { + ret = clk_enable(clks[i].clk); + if (ret) { + pr_err("Failed to enable clk '%s': %d\n", + clks[i].id, ret); + goto err; + } + } + + return 0; + +err: + clk_bulk_disable(i, clks); + + return ret; +} +EXPORT_SYMBOL_GPL(clk_bulk_enable); diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index abdc415..f67a5c4 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -189,11 +189,6 @@ } EXPORT_SYMBOL(clk_get); -void clk_put(struct clk *clk) -{ -} -EXPORT_SYMBOL(clk_put); - void clkdev_add(struct clk_lookup *cl) { if (cl->dev_id) diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index b0c8b9b..b5cefb2 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -22,4 +22,6 @@ built-in with usb ip or which are autonomous and doesn't require any phy programming such as ISP1x04 etc. +source "drivers/phy/freescale/Kconfig" + endif diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 8fc8595..179c55e 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_GENERIC_PHY) += phy-core.o obj-$(CONFIG_USB_NOP_XCEIV) += usb-nop-xceiv.o +obj-y += freescale/ diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig new file mode 100644 index 0000000..8e56dd7 --- /dev/null +++ b/drivers/phy/freescale/Kconfig @@ -0,0 +1,4 @@ +config PHY_FSL_IMX8MQ_USB + bool "Freescale i.MX8M USB3 PHY" + default SOC_IMX8MQ + diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile new file mode 100644 index 0000000..dc2b3f1 --- /dev/null +++ b/drivers/phy/freescale/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c new file mode 100644 index 0000000..1aef2b3 --- /dev/null +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2017 NXP. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define PHY_CTRL0 0x0 +#define PHY_CTRL0_REF_SSP_EN BIT(2) + +#define PHY_CTRL1 0x4 +#define PHY_CTRL1_RESET BIT(0) +#define PHY_CTRL1_COMMONONN BIT(1) +#define PHY_CTRL1_ATERESET BIT(3) +#define PHY_CTRL1_VDATSRCENB0 BIT(19) +#define PHY_CTRL1_VDATDETENB0 BIT(20) + +#define PHY_CTRL2 0x8 +#define PHY_CTRL2_TXENABLEN0 BIT(8) + +struct imx8mq_usb_phy { + struct phy *phy; + struct clk *clk; + void __iomem *base; +}; + +static int imx8mq_usb_phy_init(struct phy *phy) +{ + struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); + u32 value; + + value = readl(imx_phy->base + PHY_CTRL1); + value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0 | + PHY_CTRL1_COMMONONN); + value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET; + writel(value, imx_phy->base + PHY_CTRL1); + + value = readl(imx_phy->base + PHY_CTRL0); + value |= PHY_CTRL0_REF_SSP_EN; + writel(value, imx_phy->base + PHY_CTRL0); + + value = readl(imx_phy->base + PHY_CTRL2); + value |= PHY_CTRL2_TXENABLEN0; + writel(value, imx_phy->base + PHY_CTRL2); + + value = readl(imx_phy->base + PHY_CTRL1); + value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET); + writel(value, imx_phy->base + PHY_CTRL1); + + return 0; +} + +static int imx8mq_phy_power_on(struct phy *phy) +{ + struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); + + return clk_enable(imx_phy->clk); +} + +static int imx8mq_phy_power_off(struct phy *phy) +{ + struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); + + clk_disable(imx_phy->clk); + + return 0; +} + +static struct phy_ops imx8mq_usb_phy_ops = { + .init = imx8mq_usb_phy_init, + .power_on = imx8mq_phy_power_on, + .power_off = imx8mq_phy_power_off, +}; + +static struct phy *imx8mq_usb_phy_xlate(struct device_d *dev, + struct of_phandle_args *args) +{ + struct imx8mq_usb_phy *imx_phy = dev->priv; + + return imx_phy->phy; +} + +static int imx8mq_usb_phy_probe(struct device_d *dev) +{ + struct phy_provider *phy_provider; + struct imx8mq_usb_phy *imx_phy; + + imx_phy = xzalloc(sizeof(*imx_phy)); + + dev->priv = imx_phy; + + imx_phy->clk = clk_get(dev, "phy"); + if (IS_ERR(imx_phy->clk)) + return PTR_ERR(imx_phy->clk); + + imx_phy->base = dev_get_mem_region(dev, 0); + if (IS_ERR(imx_phy->base)) + return PTR_ERR(imx_phy->base); + + imx_phy->phy = phy_create(dev, NULL, &imx8mq_usb_phy_ops, NULL); + if (IS_ERR(imx_phy->phy)) + return PTR_ERR(imx_phy->phy); + + phy_set_drvdata(imx_phy->phy, imx_phy); + + phy_provider = of_phy_provider_register(dev, imx8mq_usb_phy_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id imx8mq_usb_phy_of_match[] = { + {.compatible = "fsl,imx8mq-usb-phy",}, + { }, +}; + +static struct driver_d imx8mq_usb_phy_driver = { + .name = "imx8mq-usb-phy", + .probe = imx8mq_usb_phy_probe, + .of_compatible = DRV_OF_COMPAT(imx8mq_usb_phy_of_match), +}; +device_platform_driver(imx8mq_usb_phy_driver); diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 9d6288f..066a887 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -227,7 +227,7 @@ return phy_provider; } - return ERR_PTR(-ENODEV); + return ERR_PTR(-EPROBE_DEFER); } /** @@ -254,7 +254,7 @@ phy_provider = of_phy_provider_lookup(args.np); if (IS_ERR(phy_provider)) { - return ERR_PTR(-ENODEV); + return ERR_CAST(phy_provider); } return phy_provider->of_xlate(phy_provider->dev, &args); diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 8520a2f..8ff3d18 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -9,12 +9,16 @@ source drivers/usb/imx/Kconfig +source "drivers/usb/dwc3/Kconfig" + source drivers/usb/host/Kconfig source drivers/usb/otg/Kconfig source drivers/usb/storage/Kconfig +source "drivers/usb/misc/Kconfig" + endif source drivers/usb/gadget/Kconfig diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 047f184..9e98099 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -1,8 +1,10 @@ obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB_IMX_CHIPIDEA) += imx/ +obj-$(CONFIG_USB_DWC3) += dwc3/ obj-$(CONFIG_USB_MUSB) += musb/ obj-$(CONFIG_USB_GADGET) += gadget/ obj-$(CONFIG_USB_STORAGE) += storage/ obj-y += host/ obj-y += otg/ +obj-$(CONFIG_USB) += misc/ diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig new file mode 100644 index 0000000..c91fd36 --- /dev/null +++ b/drivers/usb/dwc3/Kconfig @@ -0,0 +1,22 @@ +config USB_DWC3 + tristate "DesignWare USB3 DRD Core Support" + depends on USB && HAS_DMA + select USB_XHCI + select USB_DWC3_HOST # Remove this once we support more + # than USB host + help + Say Y or M here if your system has a Dual Role SuperSpeed + USB controller based on the DesignWare USB3 IP Core. + + If you choose to build this driver is a dynamically linked + module, the module will be called dwc3.ko. + +if USB_DWC3 + +config USB_DWC3_HOST + bool "Host only mode" + help + Select this when you want to use DWC3 in host mode only, + thereby the gadget feature will be regressed. + +endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile new file mode 100644 index 0000000..d43b23e --- /dev/null +++ b/drivers/usb/dwc3/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_USB_DWC3) += dwc3.o + +dwc3-y := core.o + +ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST)),) + dwc3-y += host.o +endif + diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c new file mode 100644 index 0000000..2e7031a --- /dev/null +++ b/drivers/usb/dwc3/core.c @@ -0,0 +1,740 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * core.c - DesignWare USB3 DRD Controller Core file + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + */ + +#include +#include +#include +#include +#include + +#include "core.h" +#include "io.h" + + +#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */ + +void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); + reg |= DWC3_GCTL_PRTCAPDIR(mode); + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + dwc->current_dr_role = mode; +} + +/** + * dwc3_core_soft_reset - Issues core soft reset and PHY reset + * @dwc: pointer to our context structure + */ +static int dwc3_core_soft_reset(struct dwc3 *dwc) +{ + u32 reg; + int retries = 1000; + int ret; + + ret = phy_init(dwc->usb2_generic_phy); + if (ret < 0) + return ret; + + ret = phy_init(dwc->usb3_generic_phy); + if (ret < 0) { + phy_exit(dwc->usb2_generic_phy); + return ret; + } + + /* + * We're resetting only the device side because, if we're in host mode, + * XHCI driver will reset the host block. If dwc3 was configured for + * host-only mode, then we can return early. + */ + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) + return 0; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg |= DWC3_DCTL_CSFTRST; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + do { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + goto done; + + udelay(1); + } while (--retries); + + phy_exit(dwc->usb3_generic_phy); + phy_exit(dwc->usb2_generic_phy); + + return -ETIMEDOUT; + +done: + /* + * For DWC_usb31 controller, once DWC3_DCTL_CSFTRST bit is cleared, + * we must wait at least 50ms before accessing the PHY domain + * (synchronization delay). DWC_usb31 programming guide section 1.3.2. + */ + if (dwc3_is_usb31(dwc)) + mdelay(50); + + return 0; +} + +static const struct clk_bulk_data dwc3_core_clks[] = { + { .id = "ref" }, + { .id = "bus_early" }, + { .id = "suspend" }, +}; + +/* + * dwc3_frame_length_adjustment - Adjusts frame length if required + * @dwc3: Pointer to our controller context structure + */ +static void dwc3_frame_length_adjustment(struct dwc3 *dwc) +{ + u32 reg; + u32 dft; + + if (dwc->revision < DWC3_REVISION_250A) + return; + + if (dwc->fladj == 0) + return; + + reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); + dft = reg & DWC3_GFLADJ_30MHZ_MASK; + if (!WARN(dft == dwc->fladj, + "request value same as default, ignoring\n")) { + reg &= ~DWC3_GFLADJ_30MHZ_MASK; + reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj; + dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); + } +} + +static void dwc3_core_num_eps(struct dwc3 *dwc) +{ + struct dwc3_hwparams *parms = &dwc->hwparams; + + dwc->num_eps = DWC3_NUM_EPS(parms); +} + +static void dwc3_cache_hwparams(struct dwc3 *dwc) +{ + struct dwc3_hwparams *parms = &dwc->hwparams; + + parms->hwparams0 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS0); + parms->hwparams1 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS1); + parms->hwparams2 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS2); + parms->hwparams3 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS3); + parms->hwparams4 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS4); + parms->hwparams5 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS5); + parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); + parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7); + parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8); +} + +/** + * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core + * @dwc: Pointer to our controller context structure + * + * Returns 0 on success. The USB PHY interfaces are configured but not + * initialized. The PHY interfaces and the PHYs get initialized together with + * the core in dwc3_core_init. + */ +static int dwc3_phy_setup(struct dwc3 *dwc) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + + /* + * Make sure UX_EXIT_PX is cleared as that causes issues with some + * PHYs. Also, this bit is not supposed to be used in normal operation. + */ + reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX; + + /* + * Above 1.94a, it is recommended to set DWC3_GUSB3PIPECTL_SUSPHY + * to '0' during coreConsultant configuration. So default value + * will be '0' when the core is reset. Application needs to set it + * to '1' after the core initialization is completed. + */ + if (dwc->revision > DWC3_REVISION_194A) + reg |= DWC3_GUSB3PIPECTL_SUSPHY; + + if (dwc->u2ss_inp3_quirk) + reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK; + + if (dwc->dis_rxdet_inp3_quirk) + reg |= DWC3_GUSB3PIPECTL_DISRXDETINP3; + + if (dwc->req_p1p2p3_quirk) + reg |= DWC3_GUSB3PIPECTL_REQP1P2P3; + + if (dwc->del_p1p2p3_quirk) + reg |= DWC3_GUSB3PIPECTL_DEP1P2P3_EN; + + if (dwc->del_phy_power_chg_quirk) + reg |= DWC3_GUSB3PIPECTL_DEPOCHANGE; + + if (dwc->lfps_filter_quirk) + reg |= DWC3_GUSB3PIPECTL_LFPSFILT; + + if (dwc->rx_detect_poll_quirk) + reg |= DWC3_GUSB3PIPECTL_RX_DETOPOLL; + + if (dwc->tx_de_emphasis_quirk) + reg |= DWC3_GUSB3PIPECTL_TX_DEEPH(dwc->tx_de_emphasis); + + if (dwc->dis_u3_susphy_quirk) + reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; + + if (dwc->dis_del_phy_power_chg_quirk) + reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE; + + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + + /* Select the HS PHY interface */ + switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) { + case DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI: + if (dwc->hsphy_interface && + !strncmp(dwc->hsphy_interface, "utmi", 4)) { + reg &= ~DWC3_GUSB2PHYCFG_ULPI_UTMI; + break; + } else if (dwc->hsphy_interface && + !strncmp(dwc->hsphy_interface, "ulpi", 4)) { + reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } else { + /* Relying on default value. */ + if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI)) + break; + } + /* FALLTHROUGH */ + case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI: + /* FALLTHROUGH */ + default: + break; + } + + /* + * Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to + * '0' during coreConsultant configuration. So default value will + * be '0' when the core is reset. Application needs to set it to + * '1' after the core initialization is completed. + */ + if (dwc->revision > DWC3_REVISION_194A) + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + + if (dwc->dis_u2_susphy_quirk) + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + + if (dwc->dis_enblslpm_quirk) + reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; + else + reg |= DWC3_GUSB2PHYCFG_ENBLSLPM; + + if (dwc->dis_u2_freeclk_exists_quirk) + reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS; + + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + + return 0; +} + +static void dwc3_core_exit(struct dwc3 *dwc) +{ + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); + + phy_power_off(dwc->usb2_generic_phy); + phy_power_off(dwc->usb3_generic_phy); + clk_bulk_disable(dwc->num_clks, dwc->clks); +} + +static bool dwc3_core_is_valid(struct dwc3 *dwc) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); + + /* This should read as U3 followed by revision number */ + if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) { + /* Detected DWC_usb3 IP */ + dwc->revision = reg; + } else if ((reg & DWC3_GSNPSID_MASK) == 0x33310000) { + /* Detected DWC_usb31 IP */ + dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER); + dwc->revision |= DWC3_REVISION_IS_DWC31; + dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE); + } else { + return false; + } + + return true; +} + +static void dwc3_core_setup_global_control(struct dwc3 *dwc) +{ + u32 hwparams4 = dwc->hwparams.hwparams4; + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~DWC3_GCTL_SCALEDOWN_MASK; + + switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) { + case DWC3_GHWPARAMS1_EN_PWROPT_CLK: + /** + * WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an + * issue which would cause xHCI compliance tests to fail. + * + * Because of that we cannot enable clock gating on such + * configurations. + * + * Refers to: + * + * STAR#9000588375: Clock Gating, SOF Issues when ref_clk-Based + * SOF/ITP Mode Used + */ + if ((dwc->dr_mode == USB_DR_MODE_HOST || + dwc->dr_mode == USB_DR_MODE_OTG) && + (dwc->revision >= DWC3_REVISION_210A && + dwc->revision <= DWC3_REVISION_250A)) + reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC; + else + reg &= ~DWC3_GCTL_DSBLCLKGTNG; + break; + case DWC3_GHWPARAMS1_EN_PWROPT_HIB: + /* enable hibernation here */ + dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4); + + /* + * REVISIT Enabling this bit so that host-mode hibernation + * will work. Device-mode hibernation is not yet implemented. + */ + reg |= DWC3_GCTL_GBLHIBERNATIONEN; + break; + default: + /* nothing */ + break; + } + + /* check if current dwc3 is on simulation board */ + if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) { + dev_info(dwc->dev, "Running with FPGA optimizations\n"); + dwc->is_fpga = true; + } + + WARN(dwc->disable_scramble_quirk && !dwc->is_fpga, + "disable_scramble cannot be used on non-FPGA builds\n"); + + if (dwc->disable_scramble_quirk && dwc->is_fpga) + reg |= DWC3_GCTL_DISSCRAMBLE; + else + reg &= ~DWC3_GCTL_DISSCRAMBLE; + + if (dwc->u2exit_lfps_quirk) + reg |= DWC3_GCTL_U2EXIT_LFPS; + + /* + * WORKAROUND: DWC3 revisions <1.90a have a bug + * where the device can fail to connect at SuperSpeed + * and falls back to high-speed mode which causes + * the device to enter a Connect/Disconnect loop + */ + if (dwc->revision < DWC3_REVISION_190A) + reg |= DWC3_GCTL_U2RSTECN; + + dwc3_writel(dwc->regs, DWC3_GCTL, reg); +} + +static int dwc3_core_get_phy(struct dwc3 *dwc); + +/** + * dwc3_core_init - Low-level initialization of DWC3 Core + * @dwc: Pointer to our controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +static int dwc3_core_init(struct dwc3 *dwc) +{ + u32 reg; + int ret; + + if (!dwc3_core_is_valid(dwc)) { + dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); + ret = -ENODEV; + goto err0; + } + + /* + * Write Linux Version Code to our GUID register so it's easy to figure + * out which kernel version a bug was found. + */ + dwc3_writel(dwc->regs, DWC3_GUID, 0xdeadbeef); + + /* Handle USB2.0-only core configuration */ + if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == + DWC3_GHWPARAMS3_SSPHY_IFC_DIS) { + if (dwc->maximum_speed == USB_SPEED_SUPER) + dwc->maximum_speed = USB_SPEED_HIGH; + } + + ret = dwc3_phy_setup(dwc); + if (ret) + goto err0; + + if (!dwc->phys_ready) { + ret = dwc3_core_get_phy(dwc); + if (ret) + goto err0a; + dwc->phys_ready = true; + } + + ret = dwc3_core_soft_reset(dwc); + if (ret) + goto err0a; + + dwc3_core_setup_global_control(dwc); + dwc3_core_num_eps(dwc); + + /* Adjust Frame Length */ + dwc3_frame_length_adjustment(dwc); + + ret = phy_power_on(dwc->usb2_generic_phy); + if (ret < 0) + goto err2; + + ret = phy_power_on(dwc->usb3_generic_phy); + if (ret < 0) + goto err3; + /* + * ENDXFER polling is available on version 3.10a and later of + * the DWC_usb3 controller. It is NOT available in the + * DWC_usb31 controller. + */ + if (!dwc3_is_usb31(dwc) && dwc->revision >= DWC3_REVISION_310A) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL2); + reg |= DWC3_GUCTL2_RST_ACTBITLATER; + dwc3_writel(dwc->regs, DWC3_GUCTL2, reg); + } + + if (dwc->revision >= DWC3_REVISION_250A) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL1); + + /* + * Enable hardware control of sending remote wakeup + * in HS when the device is in the L1 state. + */ + if (dwc->revision >= DWC3_REVISION_290A) + reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW; + + if (dwc->dis_tx_ipgap_linecheck_quirk) + reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS; + + dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); + } + + if (dwc->dr_mode == USB_DR_MODE_HOST || + dwc->dr_mode == USB_DR_MODE_OTG) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL); + + /* + * Enable Auto retry Feature to make the controller operating in + * Host mode on seeing transaction errors(CRC errors or internal + * overrun scenerios) on IN transfers to reply to the device + * with a non-terminating retry ACK (i.e, an ACK transcation + * packet with Retry=1 & Nump != 0) + */ + reg |= DWC3_GUCTL_HSTINAUTORETRY; + + dwc3_writel(dwc->regs, DWC3_GUCTL, reg); + } + + /* + * Must config both number of packets and max burst settings to enable + * RX and/or TX threshold. + */ + if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) { + u8 rx_thr_num = dwc->rx_thr_num_pkt_prd; + u8 rx_maxburst = dwc->rx_max_burst_prd; + u8 tx_thr_num = dwc->tx_thr_num_pkt_prd; + u8 tx_maxburst = dwc->tx_max_burst_prd; + + if (rx_thr_num && rx_maxburst) { + reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG); + reg |= DWC31_RXTHRNUMPKTSEL_PRD; + + reg &= ~DWC31_RXTHRNUMPKT_PRD(~0); + reg |= DWC31_RXTHRNUMPKT_PRD(rx_thr_num); + + reg &= ~DWC31_MAXRXBURSTSIZE_PRD(~0); + reg |= DWC31_MAXRXBURSTSIZE_PRD(rx_maxburst); + + dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg); + } + + if (tx_thr_num && tx_maxburst) { + reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG); + reg |= DWC31_TXTHRNUMPKTSEL_PRD; + + reg &= ~DWC31_TXTHRNUMPKT_PRD(~0); + reg |= DWC31_TXTHRNUMPKT_PRD(tx_thr_num); + + reg &= ~DWC31_MAXTXBURSTSIZE_PRD(~0); + reg |= DWC31_MAXTXBURSTSIZE_PRD(tx_maxburst); + + dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg); + } + } + + return 0; + +/* err4: */ + /* phy_power_off(dwc->usb3_generic_phy); */ +err3: + phy_power_off(dwc->usb2_generic_phy); +err2: +/* err1: */ + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); +err0a: +err0: + return ret; +} + +static int dwc3_core_get_phy(struct dwc3 *dwc) +{ + struct device_d *dev = dwc->dev; + int ret; + + dwc->usb2_generic_phy = phy_get(dev, "usb2-phy"); + if (IS_ERR(dwc->usb2_generic_phy)) { + ret = PTR_ERR(dwc->usb2_generic_phy); + if (ret == -ENOSYS || ret == -ENODEV) { + dev_err(dev, "no usb2 phy configured\n"); + dwc->usb2_generic_phy = NULL; + } else if (ret == -EPROBE_DEFER) { + return ret; + } else { + dev_err(dev, "no usb2 phy configured\n"); + return ret; + } + } + + dwc->usb3_generic_phy = phy_get(dev, "usb3-phy"); + if (IS_ERR(dwc->usb3_generic_phy)) { + ret = PTR_ERR(dwc->usb3_generic_phy); + if (ret == -ENOSYS || ret == -ENODEV) { + dev_err(dev, "no usb2 phy configured\n"); + dwc->usb3_generic_phy = NULL; + } else if (ret == -EPROBE_DEFER) { + return ret; + } else { + dev_err(dev, "no usb3 phy configured\n"); + return ret; + } + } + + return 0; +} + +static int dwc3_core_init_mode(struct dwc3 *dwc) +{ + struct device_d *dev = dwc->dev; + int ret; + + switch (dwc->dr_mode) { + case USB_DR_MODE_HOST: + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); + + ret = dwc3_host_init(dwc); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to initialize host\n"); + return ret; + } + break; + default: + dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode); + return -EINVAL; + } + + return 0; +} + +static void dwc3_get_properties(struct dwc3 *dwc) +{ + struct device_d *dev = dwc->dev; + u8 lpm_nyet_threshold; + u8 tx_de_emphasis; + u8 hird_threshold; + + /* default to highest possible threshold */ + lpm_nyet_threshold = 0xff; + + /* default to -3.5dB de-emphasis */ + tx_de_emphasis = 1; + + /* + * default to assert utmi_sleep_n and use maximum allowed HIRD + * threshold value of 0b1100 + */ + hird_threshold = 12; + + dwc->maximum_speed = of_usb_get_maximum_speed(dev->device_node, NULL); + dwc->dr_mode = of_usb_get_dr_mode(dev->device_node, NULL); + + dwc->lpm_nyet_threshold = lpm_nyet_threshold; + dwc->tx_de_emphasis = tx_de_emphasis; + + dwc->hird_threshold = hird_threshold + | (dwc->is_utmi_l1_suspend << 4); + + dwc->imod_interval = 0; +} + +/* check whether the core supports IMOD */ +bool dwc3_has_imod(struct dwc3 *dwc) +{ + return ((dwc3_is_usb3(dwc) && + dwc->revision >= DWC3_REVISION_300A) || + (dwc3_is_usb31(dwc) && + dwc->revision >= DWC3_USB31_REVISION_120A)); +} + +static void dwc3_check_params(struct dwc3 *dwc) +{ + struct device_d *dev = dwc->dev; + + /* Check for proper value of imod_interval */ + if (dwc->imod_interval && !dwc3_has_imod(dwc)) { + dev_warn(dwc->dev, "Interrupt moderation not supported\n"); + dwc->imod_interval = 0; + } + + /* + * Workaround for STAR 9000961433 which affects only version + * 3.00a of the DWC_usb3 core. This prevents the controller + * interrupt from being masked while handling events. IMOD + * allows us to work around this issue. Enable it for the + * affected version. + */ + if (!dwc->imod_interval && + (dwc->revision == DWC3_REVISION_300A)) + dwc->imod_interval = 1; + + /* Check the maximum_speed parameter */ + switch (dwc->maximum_speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: + break; + default: + dev_err(dev, "invalid maximum_speed parameter %d\n", + dwc->maximum_speed); + /* fall through */ + case USB_SPEED_UNKNOWN: + /* default to superspeed */ + dwc->maximum_speed = USB_SPEED_SUPER; + + /* + * default to superspeed plus if we are capable. + */ + if (dwc3_is_usb31(dwc) && + (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == + DWC3_GHWPARAMS3_SSPHY_IFC_GEN2)) + dwc->maximum_speed = USB_SPEED_SUPER_PLUS; + + break; + } +} + +static int dwc3_probe(struct device_d *dev) +{ + struct dwc3 *dwc; + int ret; + + dwc = xzalloc(sizeof(*dwc)); + dev->priv = dwc; + + dwc->clks = xmemdup(dwc3_core_clks, sizeof(dwc3_core_clks)); + dwc->dev = dev; + dwc->regs = dev_get_mem_region(dwc->dev, 0) + DWC3_GLOBALS_REGS_START; + + dwc3_get_properties(dwc); + + if (dev->device_node) { + dwc->num_clks = ARRAY_SIZE(dwc3_core_clks); + + ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks); + if (ret == -EPROBE_DEFER) + return ret; + /* + * Clocks are optional, but new DT platforms should support all + * clocks as required by the DT-binding. + */ + if (ret) + dwc->num_clks = 0; + } + + ret = clk_bulk_enable(dwc->num_clks, dwc->clks); + if (ret) + return ret; + + dwc3_cache_hwparams(dwc); + + ret = dwc3_core_init(dwc); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to initialize core: %d\n", ret); + return ret; + } + + dwc3_check_params(dwc); + + ret = dwc3_core_init_mode(dwc); + if (ret) + return ret; + + return 0; +} + +static void dwc3_remove(struct device_d *dev) +{ + struct dwc3 *dwc = dev->priv; + + dwc3_core_exit(dwc); + clk_bulk_put(dwc->num_clks, dwc->clks); +} + +static const struct of_device_id of_dwc3_match[] = { + { + .compatible = "snps,dwc3" + }, + { + .compatible = "synopsys,dwc3" + }, + { }, +}; + +static struct driver_d dwc3_driver = { + .probe = dwc3_probe, + .remove = dwc3_remove, + .name = "dwc3", + .of_compatible = DRV_OF_COMPAT(of_dwc3_match), +}; +device_platform_driver(dwc3_driver); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h new file mode 100644 index 0000000..a404e4c --- /dev/null +++ b/drivers/usb/dwc3/core.h @@ -0,0 +1,1267 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * core.h - DesignWare USB3 DRD Core Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + */ + +#ifndef __DRIVERS_USB_DWC3_CORE_H +#define __DRIVERS_USB_DWC3_CORE_H + +#include + +#define DWC3_MSG_MAX 500 + +/* Global constants */ +#define DWC3_PULL_UP_TIMEOUT 500 /* ms */ +#define DWC3_BOUNCE_SIZE 1024 /* size of a superspeed bulk */ +#define DWC3_EP0_SETUP_SIZE 512 +#define DWC3_ENDPOINTS_NUM 32 +#define DWC3_XHCI_RESOURCES_NUM 2 +#define DWC3_ISOC_MAX_RETRIES 5 + +#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */ +#define DWC3_EVENT_BUFFERS_SIZE 4096 +#define DWC3_EVENT_TYPE_MASK 0xfe + +#define DWC3_EVENT_TYPE_DEV 0 +#define DWC3_EVENT_TYPE_CARKIT 3 +#define DWC3_EVENT_TYPE_I2C 4 + +#define DWC3_DEVICE_EVENT_DISCONNECT 0 +#define DWC3_DEVICE_EVENT_RESET 1 +#define DWC3_DEVICE_EVENT_CONNECT_DONE 2 +#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3 +#define DWC3_DEVICE_EVENT_WAKEUP 4 +#define DWC3_DEVICE_EVENT_HIBER_REQ 5 +#define DWC3_DEVICE_EVENT_EOPF 6 +#define DWC3_DEVICE_EVENT_SOF 7 +#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9 +#define DWC3_DEVICE_EVENT_CMD_CMPL 10 +#define DWC3_DEVICE_EVENT_OVERFLOW 11 + +/* Controller's role while using the OTG block */ +#define DWC3_OTG_ROLE_IDLE 0 +#define DWC3_OTG_ROLE_HOST 1 +#define DWC3_OTG_ROLE_DEVICE 2 + +#define DWC3_GEVNTCOUNT_MASK 0xfffc +#define DWC3_GEVNTCOUNT_EHB BIT(31) +#define DWC3_GSNPSID_MASK 0xffff0000 +#define DWC3_GSNPSREV_MASK 0xffff + +/* DWC3 registers memory space boundries */ +#define DWC3_XHCI_REGS_START 0x0 +#define DWC3_XHCI_REGS_END 0x7fff +#define DWC3_GLOBALS_REGS_START 0xc100 +#define DWC3_GLOBALS_REGS_END 0xc6ff +#define DWC3_DEVICE_REGS_START 0xc700 +#define DWC3_DEVICE_REGS_END 0xcbff +#define DWC3_OTG_REGS_START 0xcc00 +#define DWC3_OTG_REGS_END 0xccff + +/* Global Registers */ +#define DWC3_GSBUSCFG0 0xc100 +#define DWC3_GSBUSCFG1 0xc104 +#define DWC3_GTXTHRCFG 0xc108 +#define DWC3_GRXTHRCFG 0xc10c +#define DWC3_GCTL 0xc110 +#define DWC3_GEVTEN 0xc114 +#define DWC3_GSTS 0xc118 +#define DWC3_GUCTL1 0xc11c +#define DWC3_GSNPSID 0xc120 +#define DWC3_GGPIO 0xc124 +#define DWC3_GUID 0xc128 +#define DWC3_GUCTL 0xc12c +#define DWC3_GBUSERRADDR0 0xc130 +#define DWC3_GBUSERRADDR1 0xc134 +#define DWC3_GPRTBIMAP0 0xc138 +#define DWC3_GPRTBIMAP1 0xc13c +#define DWC3_GHWPARAMS0 0xc140 +#define DWC3_GHWPARAMS1 0xc144 +#define DWC3_GHWPARAMS2 0xc148 +#define DWC3_GHWPARAMS3 0xc14c +#define DWC3_GHWPARAMS4 0xc150 +#define DWC3_GHWPARAMS5 0xc154 +#define DWC3_GHWPARAMS6 0xc158 +#define DWC3_GHWPARAMS7 0xc15c +#define DWC3_GDBGFIFOSPACE 0xc160 +#define DWC3_GDBGLTSSM 0xc164 +#define DWC3_GDBGBMU 0xc16c +#define DWC3_GDBGLSPMUX 0xc170 +#define DWC3_GDBGLSP 0xc174 +#define DWC3_GDBGEPINFO0 0xc178 +#define DWC3_GDBGEPINFO1 0xc17c +#define DWC3_GPRTBIMAP_HS0 0xc180 +#define DWC3_GPRTBIMAP_HS1 0xc184 +#define DWC3_GPRTBIMAP_FS0 0xc188 +#define DWC3_GPRTBIMAP_FS1 0xc18c +#define DWC3_GUCTL2 0xc19c + +#define DWC3_VER_NUMBER 0xc1a0 +#define DWC3_VER_TYPE 0xc1a4 + +#define DWC3_GUSB2PHYCFG(n) (0xc200 + ((n) * 0x04)) +#define DWC3_GUSB2I2CCTL(n) (0xc240 + ((n) * 0x04)) + +#define DWC3_GUSB2PHYACC(n) (0xc280 + ((n) * 0x04)) + +#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + ((n) * 0x04)) + +#define DWC3_GTXFIFOSIZ(n) (0xc300 + ((n) * 0x04)) +#define DWC3_GRXFIFOSIZ(n) (0xc380 + ((n) * 0x04)) + +#define DWC3_GEVNTADRLO(n) (0xc400 + ((n) * 0x10)) +#define DWC3_GEVNTADRHI(n) (0xc404 + ((n) * 0x10)) +#define DWC3_GEVNTSIZ(n) (0xc408 + ((n) * 0x10)) +#define DWC3_GEVNTCOUNT(n) (0xc40c + ((n) * 0x10)) + +#define DWC3_GHWPARAMS8 0xc600 +#define DWC3_GFLADJ 0xc630 + +/* Device Registers */ +#define DWC3_DCFG 0xc700 +#define DWC3_DCTL 0xc704 +#define DWC3_DEVTEN 0xc708 +#define DWC3_DSTS 0xc70c +#define DWC3_DGCMDPAR 0xc710 +#define DWC3_DGCMD 0xc714 +#define DWC3_DALEPENA 0xc720 + +#define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10)) +#define DWC3_DEPCMDPAR2 0x00 +#define DWC3_DEPCMDPAR1 0x04 +#define DWC3_DEPCMDPAR0 0x08 +#define DWC3_DEPCMD 0x0c + +#define DWC3_DEV_IMOD(n) (0xca00 + ((n) * 0x4)) + +/* OTG Registers */ +#define DWC3_OCFG 0xcc00 +#define DWC3_OCTL 0xcc04 +#define DWC3_OEVT 0xcc08 +#define DWC3_OEVTEN 0xcc0C +#define DWC3_OSTS 0xcc10 + +/* Bit fields */ + +/* Global SoC Bus Configuration INCRx Register 0 */ +#define DWC3_GSBUSCFG0_INCR256BRSTENA (1 << 7) /* INCR256 burst */ +#define DWC3_GSBUSCFG0_INCR128BRSTENA (1 << 6) /* INCR128 burst */ +#define DWC3_GSBUSCFG0_INCR64BRSTENA (1 << 5) /* INCR64 burst */ +#define DWC3_GSBUSCFG0_INCR32BRSTENA (1 << 4) /* INCR32 burst */ +#define DWC3_GSBUSCFG0_INCR16BRSTENA (1 << 3) /* INCR16 burst */ +#define DWC3_GSBUSCFG0_INCR8BRSTENA (1 << 2) /* INCR8 burst */ +#define DWC3_GSBUSCFG0_INCR4BRSTENA (1 << 1) /* INCR4 burst */ +#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */ +#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff + +/* Global Debug LSP MUX Select */ +#define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */ +#define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff) +#define DWC3_GDBGLSPMUX_DEVSELECT(n) (((n) & 0xf) << 4) +#define DWC3_GDBGLSPMUX_EPSELECT(n) ((n) & 0xf) + +/* Global Debug Queue/FIFO Space Available Register */ +#define DWC3_GDBGFIFOSPACE_NUM(n) ((n) & 0x1f) +#define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0) +#define DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(n) (((n) >> 16) & 0xffff) + +#define DWC3_TXFIFO 0 +#define DWC3_RXFIFO 1 +#define DWC3_TXREQQ 2 +#define DWC3_RXREQQ 3 +#define DWC3_RXINFOQ 4 +#define DWC3_PSTATQ 5 +#define DWC3_DESCFETCHQ 6 +#define DWC3_EVENTQ 7 +#define DWC3_AUXEVENTQ 8 + +/* Global RX Threshold Configuration Register */ +#define DWC3_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 19) +#define DWC3_GRXTHRCFG_RXPKTCNT(n) (((n) & 0xf) << 24) +#define DWC3_GRXTHRCFG_PKTCNTSEL BIT(29) + +/* Global RX Threshold Configuration Register for DWC_usb31 only */ +#define DWC31_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 16) +#define DWC31_GRXTHRCFG_RXPKTCNT(n) (((n) & 0x1f) << 21) +#define DWC31_GRXTHRCFG_PKTCNTSEL BIT(26) +#define DWC31_RXTHRNUMPKTSEL_HS_PRD BIT(15) +#define DWC31_RXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13) +#define DWC31_RXTHRNUMPKTSEL_PRD BIT(10) +#define DWC31_RXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5) +#define DWC31_MAXRXBURSTSIZE_PRD(n) ((n) & 0x1f) + +/* Global TX Threshold Configuration Register for DWC_usb31 only */ +#define DWC31_GTXTHRCFG_MAXTXBURSTSIZE(n) (((n) & 0x1f) << 16) +#define DWC31_GTXTHRCFG_TXPKTCNT(n) (((n) & 0x1f) << 21) +#define DWC31_GTXTHRCFG_PKTCNTSEL BIT(26) +#define DWC31_TXTHRNUMPKTSEL_HS_PRD BIT(15) +#define DWC31_TXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13) +#define DWC31_TXTHRNUMPKTSEL_PRD BIT(10) +#define DWC31_TXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5) +#define DWC31_MAXTXBURSTSIZE_PRD(n) ((n) & 0x1f) + +/* Global Configuration Register */ +#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19) +#define DWC3_GCTL_U2RSTECN BIT(16) +#define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6) +#define DWC3_GCTL_CLK_BUS (0) +#define DWC3_GCTL_CLK_PIPE (1) +#define DWC3_GCTL_CLK_PIPEHALF (2) +#define DWC3_GCTL_CLK_MASK (3) + +#define DWC3_GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12) +#define DWC3_GCTL_PRTCAPDIR(n) ((n) << 12) +#define DWC3_GCTL_PRTCAP_HOST 1 +#define DWC3_GCTL_PRTCAP_DEVICE 2 +#define DWC3_GCTL_PRTCAP_OTG 3 + +#define DWC3_GCTL_CORESOFTRESET BIT(11) +#define DWC3_GCTL_SOFITPSYNC BIT(10) +#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4) +#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3) +#define DWC3_GCTL_DISSCRAMBLE BIT(3) +#define DWC3_GCTL_U2EXIT_LFPS BIT(2) +#define DWC3_GCTL_GBLHIBERNATIONEN BIT(1) +#define DWC3_GCTL_DSBLCLKGTNG BIT(0) + +/* Global User Control Register */ +#define DWC3_GUCTL_HSTINAUTORETRY BIT(14) + +/* Global User Control 1 Register */ +#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28) +#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) + +/* Global Status Register */ +#define DWC3_GSTS_OTG_IP BIT(10) +#define DWC3_GSTS_BC_IP BIT(9) +#define DWC3_GSTS_ADP_IP BIT(8) +#define DWC3_GSTS_HOST_IP BIT(7) +#define DWC3_GSTS_DEVICE_IP BIT(6) +#define DWC3_GSTS_CSR_TIMEOUT BIT(5) +#define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4) +#define DWC3_GSTS_CURMOD(n) ((n) & 0x3) +#define DWC3_GSTS_CURMOD_DEVICE 0 +#define DWC3_GSTS_CURMOD_HOST 1 + +/* Global USB2 PHY Configuration Register */ +#define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31) +#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS BIT(30) +#define DWC3_GUSB2PHYCFG_SUSPHY BIT(6) +#define DWC3_GUSB2PHYCFG_ULPI_UTMI BIT(4) +#define DWC3_GUSB2PHYCFG_ENBLSLPM BIT(8) +#define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3) +#define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1) +#define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10) +#define DWC3_GUSB2PHYCFG_USBTRDTIM_MASK DWC3_GUSB2PHYCFG_USBTRDTIM(0xf) +#define USBTRDTIM_UTMI_8_BIT 9 +#define USBTRDTIM_UTMI_16_BIT 5 +#define UTMI_PHYIF_16_BIT 1 +#define UTMI_PHYIF_8_BIT 0 + +/* Global USB2 PHY Vendor Control Register */ +#define DWC3_GUSB2PHYACC_NEWREGREQ BIT(25) +#define DWC3_GUSB2PHYACC_BUSY BIT(23) +#define DWC3_GUSB2PHYACC_WRITE BIT(22) +#define DWC3_GUSB2PHYACC_ADDR(n) (n << 16) +#define DWC3_GUSB2PHYACC_EXTEND_ADDR(n) (n << 8) +#define DWC3_GUSB2PHYACC_DATA(n) (n & 0xff) + +/* Global USB3 PIPE Control Register */ +#define DWC3_GUSB3PIPECTL_PHYSOFTRST BIT(31) +#define DWC3_GUSB3PIPECTL_U2SSINP3OK BIT(29) +#define DWC3_GUSB3PIPECTL_DISRXDETINP3 BIT(28) +#define DWC3_GUSB3PIPECTL_UX_EXIT_PX BIT(27) +#define DWC3_GUSB3PIPECTL_REQP1P2P3 BIT(24) +#define DWC3_GUSB3PIPECTL_DEP1P2P3(n) ((n) << 19) +#define DWC3_GUSB3PIPECTL_DEP1P2P3_MASK DWC3_GUSB3PIPECTL_DEP1P2P3(7) +#define DWC3_GUSB3PIPECTL_DEP1P2P3_EN DWC3_GUSB3PIPECTL_DEP1P2P3(1) +#define DWC3_GUSB3PIPECTL_DEPOCHANGE BIT(18) +#define DWC3_GUSB3PIPECTL_SUSPHY BIT(17) +#define DWC3_GUSB3PIPECTL_LFPSFILT BIT(9) +#define DWC3_GUSB3PIPECTL_RX_DETOPOLL BIT(8) +#define DWC3_GUSB3PIPECTL_TX_DEEPH_MASK DWC3_GUSB3PIPECTL_TX_DEEPH(3) +#define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1) + +/* Global TX Fifo Size Register */ +#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */ +#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */ +#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff) +#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000) + +/* Global Event Size Registers */ +#define DWC3_GEVNTSIZ_INTMASK BIT(31) +#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff) + +/* Global HWPARAMS0 Register */ +#define DWC3_GHWPARAMS0_MODE(n) ((n) & 0x3) +#define DWC3_GHWPARAMS0_MODE_GADGET 0 +#define DWC3_GHWPARAMS0_MODE_HOST 1 +#define DWC3_GHWPARAMS0_MODE_DRD 2 +#define DWC3_GHWPARAMS0_MBUS_TYPE(n) (((n) >> 3) & 0x7) +#define DWC3_GHWPARAMS0_SBUS_TYPE(n) (((n) >> 6) & 0x3) +#define DWC3_GHWPARAMS0_MDWIDTH(n) (((n) >> 8) & 0xff) +#define DWC3_GHWPARAMS0_SDWIDTH(n) (((n) >> 16) & 0xff) +#define DWC3_GHWPARAMS0_AWIDTH(n) (((n) >> 24) & 0xff) + +/* Global HWPARAMS1 Register */ +#define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24) +#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0 +#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1 +#define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2 +#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24) +#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3) +#define DWC3_GHWPARAMS1_ENDBC BIT(31) + +/* Global HWPARAMS3 Register */ +#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3) +#define DWC3_GHWPARAMS3_SSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN1 1 +#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN2 2 /* DWC_usb31 only */ +#define DWC3_GHWPARAMS3_HSPHY_IFC(n) (((n) & (3 << 2)) >> 2) +#define DWC3_GHWPARAMS3_HSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI 1 +#define DWC3_GHWPARAMS3_HSPHY_IFC_ULPI 2 +#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI 3 +#define DWC3_GHWPARAMS3_FSPHY_IFC(n) (((n) & (3 << 4)) >> 4) +#define DWC3_GHWPARAMS3_FSPHY_IFC_DIS 0 +#define DWC3_GHWPARAMS3_FSPHY_IFC_ENA 1 + +/* Global HWPARAMS4 Register */ +#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13) +#define DWC3_MAX_HIBER_SCRATCHBUFS 15 + +/* Global HWPARAMS6 Register */ +#define DWC3_GHWPARAMS6_BCSUPPORT BIT(14) +#define DWC3_GHWPARAMS6_OTG3SUPPORT BIT(13) +#define DWC3_GHWPARAMS6_ADPSUPPORT BIT(12) +#define DWC3_GHWPARAMS6_HNPSUPPORT BIT(11) +#define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10) +#define DWC3_GHWPARAMS6_EN_FPGA BIT(7) + +/* Global HWPARAMS7 Register */ +#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff) +#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff) + +/* Global Frame Length Adjustment Register */ +#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7) +#define DWC3_GFLADJ_30MHZ_MASK 0x3f + +/* Global User Control Register 2 */ +#define DWC3_GUCTL2_RST_ACTBITLATER BIT(14) + +/* Device Configuration Register */ +#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) +#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) + +#define DWC3_DCFG_SPEED_MASK (7 << 0) +#define DWC3_DCFG_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */ +#define DWC3_DCFG_SUPERSPEED (4 << 0) +#define DWC3_DCFG_HIGHSPEED (0 << 0) +#define DWC3_DCFG_FULLSPEED BIT(0) +#define DWC3_DCFG_LOWSPEED (2 << 0) + +#define DWC3_DCFG_NUMP_SHIFT 17 +#define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f) +#define DWC3_DCFG_NUMP_MASK (0x1f << DWC3_DCFG_NUMP_SHIFT) +#define DWC3_DCFG_LPM_CAP BIT(22) + +/* Device Control Register */ +#define DWC3_DCTL_RUN_STOP BIT(31) +#define DWC3_DCTL_CSFTRST BIT(30) +#define DWC3_DCTL_LSFTRST BIT(29) + +#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24) +#define DWC3_DCTL_HIRD_THRES(n) ((n) << 24) + +#define DWC3_DCTL_APPL1RES BIT(23) + +/* These apply for core versions 1.87a and earlier */ +#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17) +#define DWC3_DCTL_TRGTULST(n) ((n) << 17) +#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2)) +#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3)) +#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4)) +#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5)) +#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6)) + +/* These apply for core versions 1.94a and later */ +#define DWC3_DCTL_LPM_ERRATA_MASK DWC3_DCTL_LPM_ERRATA(0xf) +#define DWC3_DCTL_LPM_ERRATA(n) ((n) << 20) + +#define DWC3_DCTL_KEEP_CONNECT BIT(19) +#define DWC3_DCTL_L1_HIBER_EN BIT(18) +#define DWC3_DCTL_CRS BIT(17) +#define DWC3_DCTL_CSS BIT(16) + +#define DWC3_DCTL_INITU2ENA BIT(12) +#define DWC3_DCTL_ACCEPTU2ENA BIT(11) +#define DWC3_DCTL_INITU1ENA BIT(10) +#define DWC3_DCTL_ACCEPTU1ENA BIT(9) +#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1) + +#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5) +#define DWC3_DCTL_ULSTCHNGREQ(n) (((n) << 5) & DWC3_DCTL_ULSTCHNGREQ_MASK) + +#define DWC3_DCTL_ULSTCHNG_NO_ACTION (DWC3_DCTL_ULSTCHNGREQ(0)) +#define DWC3_DCTL_ULSTCHNG_SS_DISABLED (DWC3_DCTL_ULSTCHNGREQ(4)) +#define DWC3_DCTL_ULSTCHNG_RX_DETECT (DWC3_DCTL_ULSTCHNGREQ(5)) +#define DWC3_DCTL_ULSTCHNG_SS_INACTIVE (DWC3_DCTL_ULSTCHNGREQ(6)) +#define DWC3_DCTL_ULSTCHNG_RECOVERY (DWC3_DCTL_ULSTCHNGREQ(8)) +#define DWC3_DCTL_ULSTCHNG_COMPLIANCE (DWC3_DCTL_ULSTCHNGREQ(10)) +#define DWC3_DCTL_ULSTCHNG_LOOPBACK (DWC3_DCTL_ULSTCHNGREQ(11)) + +/* Device Event Enable Register */ +#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN BIT(12) +#define DWC3_DEVTEN_EVNTOVERFLOWEN BIT(11) +#define DWC3_DEVTEN_CMDCMPLTEN BIT(10) +#define DWC3_DEVTEN_ERRTICERREN BIT(9) +#define DWC3_DEVTEN_SOFEN BIT(7) +#define DWC3_DEVTEN_EOPFEN BIT(6) +#define DWC3_DEVTEN_HIBERNATIONREQEVTEN BIT(5) +#define DWC3_DEVTEN_WKUPEVTEN BIT(4) +#define DWC3_DEVTEN_ULSTCNGEN BIT(3) +#define DWC3_DEVTEN_CONNECTDONEEN BIT(2) +#define DWC3_DEVTEN_USBRSTEN BIT(1) +#define DWC3_DEVTEN_DISCONNEVTEN BIT(0) + +/* Device Status Register */ +#define DWC3_DSTS_DCNRD BIT(29) + +/* This applies for core versions 1.87a and earlier */ +#define DWC3_DSTS_PWRUPREQ BIT(24) + +/* These apply for core versions 1.94a and later */ +#define DWC3_DSTS_RSS BIT(25) +#define DWC3_DSTS_SSS BIT(24) + +#define DWC3_DSTS_COREIDLE BIT(23) +#define DWC3_DSTS_DEVCTRLHLT BIT(22) + +#define DWC3_DSTS_USBLNKST_MASK (0x0f << 18) +#define DWC3_DSTS_USBLNKST(n) (((n) & DWC3_DSTS_USBLNKST_MASK) >> 18) + +#define DWC3_DSTS_RXFIFOEMPTY BIT(17) + +#define DWC3_DSTS_SOFFN_MASK (0x3fff << 3) +#define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3) + +#define DWC3_DSTS_CONNECTSPD (7 << 0) + +#define DWC3_DSTS_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */ +#define DWC3_DSTS_SUPERSPEED (4 << 0) +#define DWC3_DSTS_HIGHSPEED (0 << 0) +#define DWC3_DSTS_FULLSPEED BIT(0) +#define DWC3_DSTS_LOWSPEED (2 << 0) + +/* Device Generic Command Register */ +#define DWC3_DGCMD_SET_LMP 0x01 +#define DWC3_DGCMD_SET_PERIODIC_PAR 0x02 +#define DWC3_DGCMD_XMIT_FUNCTION 0x03 + +/* These apply for core versions 1.94a and later */ +#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO 0x04 +#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI 0x05 + +#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09 +#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a +#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c +#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10 + +#define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F) +#define DWC3_DGCMD_CMDACT BIT(10) +#define DWC3_DGCMD_CMDIOC BIT(8) + +/* Device Generic Command Parameter Register */ +#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT BIT(0) +#define DWC3_DGCMDPAR_FIFO_NUM(n) ((n) << 0) +#define DWC3_DGCMDPAR_RX_FIFO (0 << 5) +#define DWC3_DGCMDPAR_TX_FIFO BIT(5) +#define DWC3_DGCMDPAR_LOOPBACK_DIS (0 << 0) +#define DWC3_DGCMDPAR_LOOPBACK_ENA BIT(0) + +/* Device Endpoint Command Register */ +#define DWC3_DEPCMD_PARAM_SHIFT 16 +#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT) +#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f) +#define DWC3_DEPCMD_STATUS(x) (((x) >> 12) & 0x0F) +#define DWC3_DEPCMD_HIPRI_FORCERM BIT(11) +#define DWC3_DEPCMD_CLEARPENDIN BIT(11) +#define DWC3_DEPCMD_CMDACT BIT(10) +#define DWC3_DEPCMD_CMDIOC BIT(8) + +#define DWC3_DEPCMD_DEPSTARTCFG (0x09 << 0) +#define DWC3_DEPCMD_ENDTRANSFER (0x08 << 0) +#define DWC3_DEPCMD_UPDATETRANSFER (0x07 << 0) +#define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0) +#define DWC3_DEPCMD_CLEARSTALL (0x05 << 0) +#define DWC3_DEPCMD_SETSTALL (0x04 << 0) +/* This applies for core versions 1.90a and earlier */ +#define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0) +/* This applies for core versions 1.94a and later */ +#define DWC3_DEPCMD_GETEPSTATE (0x03 << 0) +#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0) +#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0) + +#define DWC3_DEPCMD_CMD(x) ((x) & 0xf) + +/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */ +#define DWC3_DALEPENA_EP(n) BIT(n) + +#define DWC3_DEPCMD_TYPE_CONTROL 0 +#define DWC3_DEPCMD_TYPE_ISOC 1 +#define DWC3_DEPCMD_TYPE_BULK 2 +#define DWC3_DEPCMD_TYPE_INTR 3 + +#define DWC3_DEV_IMOD_COUNT_SHIFT 16 +#define DWC3_DEV_IMOD_COUNT_MASK (0xffff << 16) +#define DWC3_DEV_IMOD_INTERVAL_SHIFT 0 +#define DWC3_DEV_IMOD_INTERVAL_MASK (0xffff << 0) + +/* OTG Configuration Register */ +#define DWC3_OCFG_DISPWRCUTTOFF BIT(5) +#define DWC3_OCFG_HIBDISMASK BIT(4) +#define DWC3_OCFG_SFTRSTMASK BIT(3) +#define DWC3_OCFG_OTGVERSION BIT(2) +#define DWC3_OCFG_HNPCAP BIT(1) +#define DWC3_OCFG_SRPCAP BIT(0) + +/* OTG CTL Register */ +#define DWC3_OCTL_OTG3GOERR BIT(7) +#define DWC3_OCTL_PERIMODE BIT(6) +#define DWC3_OCTL_PRTPWRCTL BIT(5) +#define DWC3_OCTL_HNPREQ BIT(4) +#define DWC3_OCTL_SESREQ BIT(3) +#define DWC3_OCTL_TERMSELIDPULSE BIT(2) +#define DWC3_OCTL_DEVSETHNPEN BIT(1) +#define DWC3_OCTL_HSTSETHNPEN BIT(0) + +/* OTG Event Register */ +#define DWC3_OEVT_DEVICEMODE BIT(31) +#define DWC3_OEVT_XHCIRUNSTPSET BIT(27) +#define DWC3_OEVT_DEVRUNSTPSET BIT(26) +#define DWC3_OEVT_HIBENTRY BIT(25) +#define DWC3_OEVT_CONIDSTSCHNG BIT(24) +#define DWC3_OEVT_HRRCONFNOTIF BIT(23) +#define DWC3_OEVT_HRRINITNOTIF BIT(22) +#define DWC3_OEVT_ADEVIDLE BIT(21) +#define DWC3_OEVT_ADEVBHOSTEND BIT(20) +#define DWC3_OEVT_ADEVHOST BIT(19) +#define DWC3_OEVT_ADEVHNPCHNG BIT(18) +#define DWC3_OEVT_ADEVSRPDET BIT(17) +#define DWC3_OEVT_ADEVSESSENDDET BIT(16) +#define DWC3_OEVT_BDEVBHOSTEND BIT(11) +#define DWC3_OEVT_BDEVHNPCHNG BIT(10) +#define DWC3_OEVT_BDEVSESSVLDDET BIT(9) +#define DWC3_OEVT_BDEVVBUSCHNG BIT(8) +#define DWC3_OEVT_BSESSVLD BIT(3) +#define DWC3_OEVT_HSTNEGSTS BIT(2) +#define DWC3_OEVT_SESREQSTS BIT(1) +#define DWC3_OEVT_ERROR BIT(0) + +/* OTG Event Enable Register */ +#define DWC3_OEVTEN_XHCIRUNSTPSETEN BIT(27) +#define DWC3_OEVTEN_DEVRUNSTPSETEN BIT(26) +#define DWC3_OEVTEN_HIBENTRYEN BIT(25) +#define DWC3_OEVTEN_CONIDSTSCHNGEN BIT(24) +#define DWC3_OEVTEN_HRRCONFNOTIFEN BIT(23) +#define DWC3_OEVTEN_HRRINITNOTIFEN BIT(22) +#define DWC3_OEVTEN_ADEVIDLEEN BIT(21) +#define DWC3_OEVTEN_ADEVBHOSTENDEN BIT(20) +#define DWC3_OEVTEN_ADEVHOSTEN BIT(19) +#define DWC3_OEVTEN_ADEVHNPCHNGEN BIT(18) +#define DWC3_OEVTEN_ADEVSRPDETEN BIT(17) +#define DWC3_OEVTEN_ADEVSESSENDDETEN BIT(16) +#define DWC3_OEVTEN_BDEVBHOSTENDEN BIT(11) +#define DWC3_OEVTEN_BDEVHNPCHNGEN BIT(10) +#define DWC3_OEVTEN_BDEVSESSVLDDETEN BIT(9) +#define DWC3_OEVTEN_BDEVVBUSCHNGEN BIT(8) + +/* OTG Status Register */ +#define DWC3_OSTS_DEVRUNSTP BIT(13) +#define DWC3_OSTS_XHCIRUNSTP BIT(12) +#define DWC3_OSTS_PERIPHERALSTATE BIT(4) +#define DWC3_OSTS_XHCIPRTPOWER BIT(3) +#define DWC3_OSTS_BSESVLD BIT(2) +#define DWC3_OSTS_VBUSVLD BIT(1) +#define DWC3_OSTS_CONIDSTS BIT(0) + +/* Structures */ + +struct dwc3_trb; + +#define DWC3_EP_FLAG_STALLED BIT(0) +#define DWC3_EP_FLAG_WEDGED BIT(1) + +#define DWC3_EP_DIRECTION_TX true +#define DWC3_EP_DIRECTION_RX false + +#define DWC3_TRB_NUM 256 + +enum dwc3_phy { + DWC3_PHY_UNKNOWN = 0, + DWC3_PHY_USB3, + DWC3_PHY_USB2, +}; + +enum dwc3_ep0_next { + DWC3_EP0_UNKNOWN = 0, + DWC3_EP0_COMPLETE, + DWC3_EP0_NRDY_DATA, + DWC3_EP0_NRDY_STATUS, +}; + +enum dwc3_ep0_state { + EP0_UNCONNECTED = 0, + EP0_SETUP_PHASE, + EP0_DATA_PHASE, + EP0_STATUS_PHASE, +}; + +enum dwc3_link_state { + /* In SuperSpeed */ + DWC3_LINK_STATE_U0 = 0x00, /* in HS, means ON */ + DWC3_LINK_STATE_U1 = 0x01, + DWC3_LINK_STATE_U2 = 0x02, /* in HS, means SLEEP */ + DWC3_LINK_STATE_U3 = 0x03, /* in HS, means SUSPEND */ + DWC3_LINK_STATE_SS_DIS = 0x04, + DWC3_LINK_STATE_RX_DET = 0x05, /* in HS, means Early Suspend */ + DWC3_LINK_STATE_SS_INACT = 0x06, + DWC3_LINK_STATE_POLL = 0x07, + DWC3_LINK_STATE_RECOV = 0x08, + DWC3_LINK_STATE_HRESET = 0x09, + DWC3_LINK_STATE_CMPLY = 0x0a, + DWC3_LINK_STATE_LPBK = 0x0b, + DWC3_LINK_STATE_RESET = 0x0e, + DWC3_LINK_STATE_RESUME = 0x0f, + DWC3_LINK_STATE_MASK = 0x0f, +}; + +/* TRB Length, PCM and Status */ +#define DWC3_TRB_SIZE_MASK (0x00ffffff) +#define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK) +#define DWC3_TRB_SIZE_PCM1(n) (((n) & 0x03) << 24) +#define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28)) >> 28) + +#define DWC3_TRBSTS_OK 0 +#define DWC3_TRBSTS_MISSED_ISOC 1 +#define DWC3_TRBSTS_SETUP_PENDING 2 +#define DWC3_TRB_STS_XFER_IN_PROG 4 + +/* TRB Control */ +#define DWC3_TRB_CTRL_HWO BIT(0) +#define DWC3_TRB_CTRL_LST BIT(1) +#define DWC3_TRB_CTRL_CHN BIT(2) +#define DWC3_TRB_CTRL_CSP BIT(3) +#define DWC3_TRB_CTRL_TRBCTL(n) (((n) & 0x3f) << 4) +#define DWC3_TRB_CTRL_ISP_IMI BIT(10) +#define DWC3_TRB_CTRL_IOC BIT(11) +#define DWC3_TRB_CTRL_SID_SOFN(n) (((n) & 0xffff) << 14) +#define DWC3_TRB_CTRL_GET_SID_SOFN(n) (((n) & (0xffff << 14)) >> 14) + +#define DWC3_TRBCTL_TYPE(n) ((n) & (0x3f << 4)) +#define DWC3_TRBCTL_NORMAL DWC3_TRB_CTRL_TRBCTL(1) +#define DWC3_TRBCTL_CONTROL_SETUP DWC3_TRB_CTRL_TRBCTL(2) +#define DWC3_TRBCTL_CONTROL_STATUS2 DWC3_TRB_CTRL_TRBCTL(3) +#define DWC3_TRBCTL_CONTROL_STATUS3 DWC3_TRB_CTRL_TRBCTL(4) +#define DWC3_TRBCTL_CONTROL_DATA DWC3_TRB_CTRL_TRBCTL(5) +#define DWC3_TRBCTL_ISOCHRONOUS_FIRST DWC3_TRB_CTRL_TRBCTL(6) +#define DWC3_TRBCTL_ISOCHRONOUS DWC3_TRB_CTRL_TRBCTL(7) +#define DWC3_TRBCTL_LINK_TRB DWC3_TRB_CTRL_TRBCTL(8) + +/** + * struct dwc3_trb - transfer request block (hw format) + * @bpl: DW0-3 + * @bph: DW4-7 + * @size: DW8-B + * @ctrl: DWC-F + */ +struct dwc3_trb { + u32 bpl; + u32 bph; + u32 size; + u32 ctrl; +} __packed; + +/** + * struct dwc3_hwparams - copy of HWPARAMS registers + * @hwparams0: GHWPARAMS0 + * @hwparams1: GHWPARAMS1 + * @hwparams2: GHWPARAMS2 + * @hwparams3: GHWPARAMS3 + * @hwparams4: GHWPARAMS4 + * @hwparams5: GHWPARAMS5 + * @hwparams6: GHWPARAMS6 + * @hwparams7: GHWPARAMS7 + * @hwparams8: GHWPARAMS8 + */ +struct dwc3_hwparams { + u32 hwparams0; + u32 hwparams1; + u32 hwparams2; + u32 hwparams3; + u32 hwparams4; + u32 hwparams5; + u32 hwparams6; + u32 hwparams7; + u32 hwparams8; +}; + +/* HWPARAMS0 */ +#define DWC3_MODE(n) ((n) & 0x7) + +#define DWC3_MDWIDTH(n) (((n) & 0xff00) >> 8) + +/* HWPARAMS1 */ +#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) + +/* HWPARAMS3 */ +#define DWC3_NUM_IN_EPS_MASK (0x1f << 18) +#define DWC3_NUM_EPS_MASK (0x3f << 12) +#define DWC3_NUM_EPS(p) (((p)->hwparams3 & \ + (DWC3_NUM_EPS_MASK)) >> 12) +#define DWC3_NUM_IN_EPS(p) (((p)->hwparams3 & \ + (DWC3_NUM_IN_EPS_MASK)) >> 18) + +/* HWPARAMS7 */ +#define DWC3_RAM1_DEPTH(n) ((n) & 0xffff) + +/** + * struct dwc3 - representation of our controller + * @drd_work: workqueue used for role swapping + * @ep0_trb: trb which is used for the ctrl_req + * @bounce: address of bounce buffer + * @scratchbuf: address of scratch buffer + * @setup_buf: used while precessing STD USB requests + * @ep0_trb_addr: dma address of @ep0_trb + * @bounce_addr: dma address of @bounce + * @ep0_usb_req: dummy req used while handling STD USB requests + * @scratch_addr: dma address of scratchbuf + * @ep0_in_setup: one control transfer is completed and enter setup phase + * @lock: for synchronizing + * @dev: pointer to our struct device + * @sysdev: pointer to the DMA-capable device + * @xhci: pointer to our xHCI child + * @xhci_resources: struct resources for our @xhci child + * @ev_buf: struct dwc3_event_buffer pointer + * @eps: endpoint array + * @gadget: device side representation of the peripheral controller + * @gadget_driver: pointer to the gadget driver + * @clks: array of clocks + * @num_clks: number of clocks + * @reset: reset control + * @regs: base address for our registers + * @regs_size: address space size + * @fladj: frame length adjustment + * @irq_gadget: peripheral controller's IRQ number + * @otg_irq: IRQ number for OTG IRQs + * @current_otg_role: current role of operation while using the OTG block + * @desired_otg_role: desired role of operation while using the OTG block + * @otg_restart_host: flag that OTG controller needs to restart host + * @nr_scratch: number of scratch buffers + * @u1u2: only used on revisions <1.83a for workaround + * @maximum_speed: maximum speed requested (mainly for testing purposes) + * @revision: revision register contents + * @version_type: VERSIONTYPE register contents, a sub release of a revision + * @dr_mode: requested mode of operation + * @current_dr_role: current role of operation when in dual-role mode + * @desired_dr_role: desired role of operation when in dual-role mode + * @edev: extcon handle + * @edev_nb: extcon notifier + * @hsphy_mode: UTMI phy mode, one of following: + * - USBPHY_INTERFACE_MODE_UTMI + * - USBPHY_INTERFACE_MODE_UTMIW + * @usb2_phy: pointer to USB2 PHY + * @usb3_phy: pointer to USB3 PHY + * @usb2_generic_phy: pointer to USB2 PHY + * @usb3_generic_phy: pointer to USB3 PHY + * @phys_ready: flag to indicate that PHYs are ready + * @ulpi: pointer to ulpi interface + * @ulpi_ready: flag to indicate that ULPI is initialized + * @u2sel: parameter from Set SEL request. + * @u2pel: parameter from Set SEL request. + * @u1sel: parameter from Set SEL request. + * @u1pel: parameter from Set SEL request. + * @num_eps: number of endpoints + * @ep0_next_event: hold the next expected event + * @ep0state: state of endpoint zero + * @link_state: link state + * @speed: device speed (super, high, full, low) + * @hwparams: copy of hwparams registers + * @root: debugfs root folder pointer + * @regset: debugfs pointer to regdump file + * @dbg_lsp_select: current debug lsp mux register selection + * @test_mode: true when we're entering a USB test mode + * @test_mode_nr: test feature selector + * @lpm_nyet_threshold: LPM NYET response threshold + * @hird_threshold: HIRD threshold + * @rx_thr_num_pkt_prd: periodic ESS receive packet count + * @rx_max_burst_prd: max periodic ESS receive burst size + * @tx_thr_num_pkt_prd: periodic ESS transmit packet count + * @tx_max_burst_prd: max periodic ESS transmit burst size + * @hsphy_interface: "utmi" or "ulpi" + * @connected: true when we're connected to a host, false otherwise + * @delayed_status: true when gadget driver asks for delayed status + * @ep0_bounced: true when we used bounce buffer + * @ep0_expect_in: true when we expect a DATA IN transfer + * @has_hibernation: true when dwc3 was configured with Hibernation + * @sysdev_is_parent: true when dwc3 device has a parent driver + * @has_lpm_erratum: true when core was configured with LPM Erratum. Note that + * there's now way for software to detect this in runtime. + * @is_utmi_l1_suspend: the core asserts output signal + * 0 - utmi_sleep_n + * 1 - utmi_l1_suspend_n + * @is_fpga: true when we are using the FPGA board + * @pending_events: true when we have pending IRQs to be handled + * @pullups_connected: true when Run/Stop bit is set + * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround + * @three_stage_setup: set if we perform a three phase setup + * @dis_start_transfer_quirk: set if start_transfer failure SW workaround is + * not needed for DWC_usb31 version 1.70a-ea06 and below + * @usb3_lpm_capable: set if hadrware supports Link Power Management + * @usb2_lpm_disable: set to disable usb2 lpm + * @disable_scramble_quirk: set if we enable the disable scramble quirk + * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk + * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk + * @req_p1p2p3_quirk: set if we enable request p1p2p3 quirk + * @del_p1p2p3_quirk: set if we enable delay p1p2p3 quirk + * @del_phy_power_chg_quirk: set if we enable delay phy power change quirk + * @lfps_filter_quirk: set if we enable LFPS filter quirk + * @rx_detect_poll_quirk: set if we enable rx_detect to polling lfps quirk + * @dis_u3_susphy_quirk: set if we disable usb3 suspend phy + * @dis_u2_susphy_quirk: set if we disable usb2 suspend phy + * @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG, + * disabling the suspend signal to the PHY. + * @dis_rxdet_inp3_quirk: set if we disable Rx.Detect in P3 + * @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists + * in GUSB2PHYCFG, specify that USB2 PHY doesn't + * provide a free-running PHY clock. + * @dis_del_phy_power_chg_quirk: set if we disable delay phy power + * change quirk. + * @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate + * check during HS transmit. + * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk + * @tx_de_emphasis: Tx de-emphasis value + * 0 - -6dB de-emphasis + * 1 - -3.5dB de-emphasis + * 2 - No de-emphasis + * 3 - Reserved + * @dis_metastability_quirk: set to disable metastability quirk. + * @imod_interval: set the interrupt moderation interval in 250ns + * increments or 0 to disable. + */ +struct dwc3 { + struct device_d *dev; + struct device_d *xhci; + + struct clk_bulk_data *clks; + int num_clks; + + struct phy *usb2_generic_phy; + struct phy *usb3_generic_phy; + + bool phys_ready; + + void __iomem *regs; + + enum usb_dr_mode dr_mode; + u32 current_dr_role; + u32 desired_dr_role; + + u32 fladj; + u32 irq_gadget; + u32 otg_irq; + u32 current_otg_role; + u32 desired_otg_role; + bool otg_restart_host; + u32 nr_scratch; + u32 u1u2; + u32 maximum_speed; + + /* + * All 3.1 IP version constants are greater than the 3.0 IP + * version constants. This works for most version checks in + * dwc3. However, in the future, this may not apply as + * features may be developed on newer versions of the 3.0 IP + * that are not in the 3.1 IP. + */ + u32 revision; + +#define DWC3_REVISION_173A 0x5533173a +#define DWC3_REVISION_175A 0x5533175a +#define DWC3_REVISION_180A 0x5533180a +#define DWC3_REVISION_183A 0x5533183a +#define DWC3_REVISION_185A 0x5533185a +#define DWC3_REVISION_187A 0x5533187a +#define DWC3_REVISION_188A 0x5533188a +#define DWC3_REVISION_190A 0x5533190a +#define DWC3_REVISION_194A 0x5533194a +#define DWC3_REVISION_200A 0x5533200a +#define DWC3_REVISION_202A 0x5533202a +#define DWC3_REVISION_210A 0x5533210a +#define DWC3_REVISION_220A 0x5533220a +#define DWC3_REVISION_230A 0x5533230a +#define DWC3_REVISION_240A 0x5533240a +#define DWC3_REVISION_250A 0x5533250a +#define DWC3_REVISION_260A 0x5533260a +#define DWC3_REVISION_270A 0x5533270a +#define DWC3_REVISION_280A 0x5533280a +#define DWC3_REVISION_290A 0x5533290a +#define DWC3_REVISION_300A 0x5533300a +#define DWC3_REVISION_310A 0x5533310a +#define DWC3_REVISION_330A 0x5533330a + +/* + * NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really + * just so dwc31 revisions are always larger than dwc3. + */ +#define DWC3_REVISION_IS_DWC31 0x80000000 +#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31) +#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31) +#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31) +#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31) + + u32 version_type; + +#define DWC31_VERSIONTYPE_EA01 0x65613031 +#define DWC31_VERSIONTYPE_EA02 0x65613032 +#define DWC31_VERSIONTYPE_EA03 0x65613033 +#define DWC31_VERSIONTYPE_EA04 0x65613034 +#define DWC31_VERSIONTYPE_EA05 0x65613035 +#define DWC31_VERSIONTYPE_EA06 0x65613036 + + u16 u2sel; + u16 u2pel; + u8 u1sel; + u8 u1pel; + + u8 speed; + + u8 num_eps; + + struct dwc3_hwparams hwparams; + + u32 dbg_lsp_select; + + u8 test_mode; + u8 test_mode_nr; + u8 lpm_nyet_threshold; + u8 hird_threshold; + u8 rx_thr_num_pkt_prd; + u8 rx_max_burst_prd; + u8 tx_thr_num_pkt_prd; + u8 tx_max_burst_prd; + + const char *hsphy_interface; + + unsigned connected:1; + unsigned delayed_status:1; + unsigned ep0_bounced:1; + unsigned ep0_expect_in:1; + unsigned has_hibernation:1; + unsigned sysdev_is_parent:1; + unsigned has_lpm_erratum:1; + unsigned is_utmi_l1_suspend:1; + unsigned is_fpga:1; + unsigned pending_events:1; + unsigned pullups_connected:1; + unsigned setup_packet_pending:1; + unsigned three_stage_setup:1; + unsigned dis_start_transfer_quirk:1; + unsigned usb3_lpm_capable:1; + unsigned usb2_lpm_disable:1; + + unsigned disable_scramble_quirk:1; + unsigned u2exit_lfps_quirk:1; + unsigned u2ss_inp3_quirk:1; + unsigned req_p1p2p3_quirk:1; + unsigned del_p1p2p3_quirk:1; + unsigned del_phy_power_chg_quirk:1; + unsigned lfps_filter_quirk:1; + unsigned rx_detect_poll_quirk:1; + unsigned dis_u3_susphy_quirk:1; + unsigned dis_u2_susphy_quirk:1; + unsigned dis_enblslpm_quirk:1; + unsigned dis_rxdet_inp3_quirk:1; + unsigned dis_u2_freeclk_exists_quirk:1; + unsigned dis_del_phy_power_chg_quirk:1; + unsigned dis_tx_ipgap_linecheck_quirk:1; + + unsigned tx_de_emphasis_quirk:1; + unsigned tx_de_emphasis:2; + + unsigned dis_metastability_quirk:1; + + u16 imod_interval; +}; + +#define INCRX_BURST_MODE 0 +#define INCRX_UNDEF_LENGTH_BURST_MODE 1 + +#define work_to_dwc(w) (container_of((w), struct dwc3, drd_work)) + +/* -------------------------------------------------------------------------- */ + +struct dwc3_event_type { + u32 is_devspec:1; + u32 type:7; + u32 reserved8_31:24; +} __packed; + +#define DWC3_DEPEVT_XFERCOMPLETE 0x01 +#define DWC3_DEPEVT_XFERINPROGRESS 0x02 +#define DWC3_DEPEVT_XFERNOTREADY 0x03 +#define DWC3_DEPEVT_RXTXFIFOEVT 0x04 +#define DWC3_DEPEVT_STREAMEVT 0x06 +#define DWC3_DEPEVT_EPCMDCMPLT 0x07 + +/** + * struct dwc3_event_depvt - Device Endpoint Events + * @one_bit: indicates this is an endpoint event (not used) + * @endpoint_number: number of the endpoint + * @endpoint_event: The event we have: + * 0x00 - Reserved + * 0x01 - XferComplete + * 0x02 - XferInProgress + * 0x03 - XferNotReady + * 0x04 - RxTxFifoEvt (IN->Underrun, OUT->Overrun) + * 0x05 - Reserved + * 0x06 - StreamEvt + * 0x07 - EPCmdCmplt + * @reserved11_10: Reserved, don't use. + * @status: Indicates the status of the event. Refer to databook for + * more information. + * @parameters: Parameters of the current event. Refer to databook for + * more information. + */ +struct dwc3_event_depevt { + u32 one_bit:1; + u32 endpoint_number:5; + u32 endpoint_event:4; + u32 reserved11_10:2; + u32 status:4; + +/* Within XferNotReady */ +#define DEPEVT_STATUS_TRANSFER_ACTIVE BIT(3) + +/* Within XferComplete or XferInProgress */ +#define DEPEVT_STATUS_BUSERR BIT(0) +#define DEPEVT_STATUS_SHORT BIT(1) +#define DEPEVT_STATUS_IOC BIT(2) +#define DEPEVT_STATUS_LST BIT(3) /* XferComplete */ +#define DEPEVT_STATUS_MISSED_ISOC BIT(3) /* XferInProgress */ + +/* Stream event only */ +#define DEPEVT_STREAMEVT_FOUND 1 +#define DEPEVT_STREAMEVT_NOTFOUND 2 + +/* Control-only Status */ +#define DEPEVT_STATUS_CONTROL_DATA 1 +#define DEPEVT_STATUS_CONTROL_STATUS 2 +#define DEPEVT_STATUS_CONTROL_PHASE(n) ((n) & 3) + +/* In response to Start Transfer */ +#define DEPEVT_TRANSFER_NO_RESOURCE 1 +#define DEPEVT_TRANSFER_BUS_EXPIRY 2 + + u32 parameters:16; + +/* For Command Complete Events */ +#define DEPEVT_PARAMETER_CMD(n) (((n) & (0xf << 8)) >> 8) +} __packed; + +/** + * struct dwc3_event_devt - Device Events + * @one_bit: indicates this is a non-endpoint event (not used) + * @device_event: indicates it's a device event. Should read as 0x00 + * @type: indicates the type of device event. + * 0 - DisconnEvt + * 1 - USBRst + * 2 - ConnectDone + * 3 - ULStChng + * 4 - WkUpEvt + * 5 - Reserved + * 6 - EOPF + * 7 - SOF + * 8 - Reserved + * 9 - ErrticErr + * 10 - CmdCmplt + * 11 - EvntOverflow + * 12 - VndrDevTstRcved + * @reserved15_12: Reserved, not used + * @event_info: Information about this event + * @reserved31_25: Reserved, not used + */ +struct dwc3_event_devt { + u32 one_bit:1; + u32 device_event:7; + u32 type:4; + u32 reserved15_12:4; + u32 event_info:9; + u32 reserved31_25:7; +} __packed; + +/** + * struct dwc3_event_gevt - Other Core Events + * @one_bit: indicates this is a non-endpoint event (not used) + * @device_event: indicates it's (0x03) Carkit or (0x04) I2C event. + * @phy_port_number: self-explanatory + * @reserved31_12: Reserved, not used. + */ +struct dwc3_event_gevt { + u32 one_bit:1; + u32 device_event:7; + u32 phy_port_number:4; + u32 reserved31_12:20; +} __packed; + +/** + * union dwc3_event - representation of Event Buffer contents + * @raw: raw 32-bit event + * @type: the type of the event + * @depevt: Device Endpoint Event + * @devt: Device Event + * @gevt: Global Event + */ +union dwc3_event { + u32 raw; + struct dwc3_event_type type; + struct dwc3_event_depevt depevt; + struct dwc3_event_devt devt; + struct dwc3_event_gevt gevt; +}; + +/** + * struct dwc3_gadget_ep_cmd_params - representation of endpoint command + * parameters + * @param2: third parameter + * @param1: second parameter + * @param0: first parameter + */ +struct dwc3_gadget_ep_cmd_params { + u32 param2; + u32 param1; + u32 param0; +}; + +/* + * DWC3 Features to be used as Driver Data + */ + +#define DWC3_HAS_PERIPHERAL BIT(0) +#define DWC3_HAS_XHCI BIT(1) +#define DWC3_HAS_OTG BIT(3) + +/* prototypes */ +void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode); +void dwc3_set_mode(struct dwc3 *dwc, u32 mode); + +/* check whether we are on the DWC_usb3 core */ +static inline bool dwc3_is_usb3(struct dwc3 *dwc) +{ + return !(dwc->revision & DWC3_REVISION_IS_DWC31); +} + +/* check whether we are on the DWC_usb31 core */ +static inline bool dwc3_is_usb31(struct dwc3 *dwc) +{ + return !!(dwc->revision & DWC3_REVISION_IS_DWC31); +} + +bool dwc3_has_imod(struct dwc3 *dwc); + +int dwc3_event_buffers_setup(struct dwc3 *dwc); +void dwc3_event_buffers_cleanup(struct dwc3 *dwc); + +#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) +int dwc3_host_init(struct dwc3 *dwc); +#else +static inline int dwc3_host_init(struct dwc3 *dwc) +{ return 0; } +#endif + +#if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) +int dwc3_gadget_init(struct dwc3 *dwc); +void dwc3_gadget_exit(struct dwc3 *dwc); +int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode); +int dwc3_gadget_get_link_state(struct dwc3 *dwc); +int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); +int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, + struct dwc3_gadget_ep_cmd_params *params); +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param); +#else +static inline int dwc3_gadget_init(struct dwc3 *dwc) +{ return 0; } +static inline void dwc3_gadget_exit(struct dwc3 *dwc) +{ } +static inline int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) +{ return 0; } +static inline int dwc3_gadget_get_link_state(struct dwc3 *dwc) +{ return 0; } +static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc, + enum dwc3_link_state state) +{ return 0; } + +static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, + int cmd, u32 param) +{ return 0; } +#endif + +#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) +int dwc3_drd_init(struct dwc3 *dwc); +void dwc3_drd_exit(struct dwc3 *dwc); +void dwc3_otg_init(struct dwc3 *dwc); +void dwc3_otg_exit(struct dwc3 *dwc); +void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus); +void dwc3_otg_host_init(struct dwc3 *dwc); +#else +static inline int dwc3_drd_init(struct dwc3 *dwc) +{ return 0; } +static inline void dwc3_drd_exit(struct dwc3 *dwc) +{ } +static inline void dwc3_otg_init(struct dwc3 *dwc) +{ } +static inline void dwc3_otg_exit(struct dwc3 *dwc) +{ } +static inline void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) +{ } +static inline void dwc3_otg_host_init(struct dwc3 *dwc) +{ } +#endif + +/* power management interface */ +#if !IS_ENABLED(CONFIG_USB_DWC3_HOST) +int dwc3_gadget_suspend(struct dwc3 *dwc); +int dwc3_gadget_resume(struct dwc3 *dwc); +void dwc3_gadget_process_pending_events(struct dwc3 *dwc); +#else +static inline int dwc3_gadget_suspend(struct dwc3 *dwc) +{ + return 0; +} + +static inline int dwc3_gadget_resume(struct dwc3 *dwc) +{ + return 0; +} + +static inline void dwc3_gadget_process_pending_events(struct dwc3 *dwc) +{ +} +#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */ + +#if IS_ENABLED(CONFIG_USB_DWC3_ULPI) +int dwc3_ulpi_init(struct dwc3 *dwc); +void dwc3_ulpi_exit(struct dwc3 *dwc); +#else +static inline int dwc3_ulpi_init(struct dwc3 *dwc) +{ return 0; } +static inline void dwc3_ulpi_exit(struct dwc3 *dwc) +{ } +#endif + +#endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h new file mode 100644 index 0000000..4f75ab3 --- /dev/null +++ b/drivers/usb/dwc3/debug.h @@ -0,0 +1,664 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * debug.h - DesignWare USB3 DRD Controller Debug Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + */ + +#ifndef __DWC3_DEBUG_H +#define __DWC3_DEBUG_H + +#include "core.h" + +/** + * dwc3_gadget_ep_cmd_string - returns endpoint command string + * @cmd: command code + */ +static inline const char * +dwc3_gadget_ep_cmd_string(u8 cmd) +{ + switch (cmd) { + case DWC3_DEPCMD_DEPSTARTCFG: + return "Start New Configuration"; + case DWC3_DEPCMD_ENDTRANSFER: + return "End Transfer"; + case DWC3_DEPCMD_UPDATETRANSFER: + return "Update Transfer"; + case DWC3_DEPCMD_STARTTRANSFER: + return "Start Transfer"; + case DWC3_DEPCMD_CLEARSTALL: + return "Clear Stall"; + case DWC3_DEPCMD_SETSTALL: + return "Set Stall"; + case DWC3_DEPCMD_GETEPSTATE: + return "Get Endpoint State"; + case DWC3_DEPCMD_SETTRANSFRESOURCE: + return "Set Endpoint Transfer Resource"; + case DWC3_DEPCMD_SETEPCONFIG: + return "Set Endpoint Configuration"; + default: + return "UNKNOWN command"; + } +} + +/** + * dwc3_gadget_generic_cmd_string - returns generic command string + * @cmd: command code + */ +static inline const char * +dwc3_gadget_generic_cmd_string(u8 cmd) +{ + switch (cmd) { + case DWC3_DGCMD_SET_LMP: + return "Set LMP"; + case DWC3_DGCMD_SET_PERIODIC_PAR: + return "Set Periodic Parameters"; + case DWC3_DGCMD_XMIT_FUNCTION: + return "Transmit Function Wake Device Notification"; + case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO: + return "Set Scratchpad Buffer Array Address Lo"; + case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI: + return "Set Scratchpad Buffer Array Address Hi"; + case DWC3_DGCMD_SELECTED_FIFO_FLUSH: + return "Selected FIFO Flush"; + case DWC3_DGCMD_ALL_FIFO_FLUSH: + return "All FIFO Flush"; + case DWC3_DGCMD_SET_ENDPOINT_NRDY: + return "Set Endpoint NRDY"; + case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK: + return "Run SoC Bus Loopback Test"; + default: + return "UNKNOWN"; + } +} + +/** + * dwc3_gadget_link_string - returns link name + * @link_state: link state code + */ +static inline const char * +dwc3_gadget_link_string(enum dwc3_link_state link_state) +{ + switch (link_state) { + case DWC3_LINK_STATE_U0: + return "U0"; + case DWC3_LINK_STATE_U1: + return "U1"; + case DWC3_LINK_STATE_U2: + return "U2"; + case DWC3_LINK_STATE_U3: + return "U3"; + case DWC3_LINK_STATE_SS_DIS: + return "SS.Disabled"; + case DWC3_LINK_STATE_RX_DET: + return "RX.Detect"; + case DWC3_LINK_STATE_SS_INACT: + return "SS.Inactive"; + case DWC3_LINK_STATE_POLL: + return "Polling"; + case DWC3_LINK_STATE_RECOV: + return "Recovery"; + case DWC3_LINK_STATE_HRESET: + return "Hot Reset"; + case DWC3_LINK_STATE_CMPLY: + return "Compliance"; + case DWC3_LINK_STATE_LPBK: + return "Loopback"; + case DWC3_LINK_STATE_RESET: + return "Reset"; + case DWC3_LINK_STATE_RESUME: + return "Resume"; + default: + return "UNKNOWN link state\n"; + } +} + +/** + * dwc3_gadget_hs_link_string - returns highspeed and below link name + * @link_state: link state code + */ +static inline const char * +dwc3_gadget_hs_link_string(enum dwc3_link_state link_state) +{ + switch (link_state) { + case DWC3_LINK_STATE_U0: + return "On"; + case DWC3_LINK_STATE_U2: + return "Sleep"; + case DWC3_LINK_STATE_U3: + return "Suspend"; + case DWC3_LINK_STATE_SS_DIS: + return "Disconnected"; + case DWC3_LINK_STATE_RX_DET: + return "Early Suspend"; + case DWC3_LINK_STATE_RECOV: + return "Recovery"; + case DWC3_LINK_STATE_RESET: + return "Reset"; + case DWC3_LINK_STATE_RESUME: + return "Resume"; + default: + return "UNKNOWN link state\n"; + } +} + +/** + * dwc3_trb_type_string - returns TRB type as a string + * @type: the type of the TRB + */ +static inline const char *dwc3_trb_type_string(unsigned int type) +{ + switch (type) { + case DWC3_TRBCTL_NORMAL: + return "normal"; + case DWC3_TRBCTL_CONTROL_SETUP: + return "setup"; + case DWC3_TRBCTL_CONTROL_STATUS2: + return "status2"; + case DWC3_TRBCTL_CONTROL_STATUS3: + return "status3"; + case DWC3_TRBCTL_CONTROL_DATA: + return "data"; + case DWC3_TRBCTL_ISOCHRONOUS_FIRST: + return "isoc-first"; + case DWC3_TRBCTL_ISOCHRONOUS: + return "isoc"; + case DWC3_TRBCTL_LINK_TRB: + return "link"; + default: + return "UNKNOWN"; + } +} + +static inline const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) +{ + switch (state) { + case EP0_UNCONNECTED: + return "Unconnected"; + case EP0_SETUP_PHASE: + return "Setup Phase"; + case EP0_DATA_PHASE: + return "Data Phase"; + case EP0_STATUS_PHASE: + return "Status Phase"; + default: + return "UNKNOWN"; + } +} + +/** + * dwc3_gadget_event_string - returns event name + * @event: the event code + */ +static inline const char * +dwc3_gadget_event_string(char *str, const struct dwc3_event_devt *event) +{ + enum dwc3_link_state state = event->event_info & DWC3_LINK_STATE_MASK; + + switch (event->type) { + case DWC3_DEVICE_EVENT_DISCONNECT: + sprintf(str, "Disconnect: [%s]", + dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_RESET: + sprintf(str, "Reset [%s]", dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + sprintf(str, "Connection Done [%s]", + dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + sprintf(str, "Link Change [%s]", + dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_WAKEUP: + sprintf(str, "WakeUp [%s]", dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_EOPF: + sprintf(str, "End-Of-Frame [%s]", + dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_SOF: + sprintf(str, "Start-Of-Frame [%s]", + dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + sprintf(str, "Erratic Error [%s]", + dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_CMD_CMPL: + sprintf(str, "Command Complete [%s]", + dwc3_gadget_link_string(state)); + break; + case DWC3_DEVICE_EVENT_OVERFLOW: + sprintf(str, "Overflow [%s]", dwc3_gadget_link_string(state)); + break; + default: + sprintf(str, "UNKNOWN"); + } + + return str; +} + +static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str) +{ + switch (t & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: + sprintf(str, "Get Interface Status(Intf = %d, Length = %d)", + i, l); + break; + case USB_RECIP_ENDPOINT: + sprintf(str, "Get Endpoint Status(ep%d%s)", + i & ~USB_DIR_IN, + i & USB_DIR_IN ? "in" : "out"); + break; + } +} + +static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v, + __u16 i, char *str) +{ + switch (t & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + sprintf(str, "%s Device Feature(%s%s)", + b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", + ({char *s; + switch (v) { + case USB_DEVICE_SELF_POWERED: + s = "Self Powered"; + break; + case USB_DEVICE_REMOTE_WAKEUP: + s = "Remote Wakeup"; + break; + case USB_DEVICE_TEST_MODE: + s = "Test Mode"; + break; + case USB_DEVICE_U1_ENABLE: + s = "U1 Enable"; + break; + case USB_DEVICE_U2_ENABLE: + s = "U2 Enable"; + break; + case USB_DEVICE_LTM_ENABLE: + s = "LTM Enable"; + break; + default: + s = "UNKNOWN"; + } s; }), + v == USB_DEVICE_TEST_MODE ? + ({ char *s; + switch (i) { + case TEST_J: + s = ": TEST_J"; + break; + case TEST_K: + s = ": TEST_K"; + break; + case TEST_SE0_NAK: + s = ": TEST_SE0_NAK"; + break; + case TEST_PACKET: + s = ": TEST_PACKET"; + break; + case TEST_FORCE_EN: + s = ": TEST_FORCE_EN"; + break; + default: + s = ": UNKNOWN"; + } s; }) : ""); + break; + case USB_RECIP_INTERFACE: + sprintf(str, "%s Interface Feature(%s)", + b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", + v == USB_INTRF_FUNC_SUSPEND ? + "Function Suspend" : "UNKNOWN"); + break; + case USB_RECIP_ENDPOINT: + sprintf(str, "%s Endpoint Feature(%s ep%d%s)", + b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", + v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN", + i & ~USB_DIR_IN, + i & USB_DIR_IN ? "in" : "out"); + break; + } +} + +static inline void dwc3_decode_set_address(__u16 v, char *str) +{ + sprintf(str, "Set Address(Addr = %02x)", v); +} + +static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v, + __u16 i, __u16 l, char *str) +{ + sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)", + b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set", + ({ char *s; + switch (v >> 8) { + case USB_DT_DEVICE: + s = "Device"; + break; + case USB_DT_CONFIG: + s = "Configuration"; + break; + case USB_DT_STRING: + s = "String"; + break; + case USB_DT_INTERFACE: + s = "Interface"; + break; + case USB_DT_ENDPOINT: + s = "Endpoint"; + break; + case USB_DT_DEVICE_QUALIFIER: + s = "Device Qualifier"; + break; + case USB_DT_OTHER_SPEED_CONFIG: + s = "Other Speed Config"; + break; + case USB_DT_INTERFACE_POWER: + s = "Interface Power"; + break; + case USB_DT_OTG: + s = "OTG"; + break; + case USB_DT_DEBUG: + s = "Debug"; + break; + case USB_DT_INTERFACE_ASSOCIATION: + s = "Interface Association"; + break; + case USB_DT_BOS: + s = "BOS"; + break; + case USB_DT_DEVICE_CAPABILITY: + s = "Device Capability"; + break; + case USB_DT_PIPE_USAGE: + s = "Pipe Usage"; + break; + case USB_DT_SS_ENDPOINT_COMP: + s = "SS Endpoint Companion"; + break; + case USB_DT_SSP_ISOC_ENDPOINT_COMP: + s = "SSP Isochronous Endpoint Companion"; + break; + default: + s = "UNKNOWN"; + break; + } s; }), v & 0xff, l); +} + + +static inline void dwc3_decode_get_configuration(__u16 l, char *str) +{ + sprintf(str, "Get Configuration(Length = %d)", l); +} + +static inline void dwc3_decode_set_configuration(__u8 v, char *str) +{ + sprintf(str, "Set Configuration(Config = %d)", v); +} + +static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str) +{ + sprintf(str, "Get Interface(Intf = %d, Length = %d)", i, l); +} + +static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str) +{ + sprintf(str, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v); +} + +static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str) +{ + sprintf(str, "Synch Frame(Endpoint = %d, Length = %d)", i, l); +} + +static inline void dwc3_decode_set_sel(__u16 l, char *str) +{ + sprintf(str, "Set SEL(Length = %d)", l); +} + +static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str) +{ + sprintf(str, "Set Isochronous Delay(Delay = %d ns)", v); +} + +/** + * dwc3_decode_ctrl - returns a string represetion of ctrl request + */ +static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType, + __u8 bRequest, __u16 wValue, __u16 wIndex, __u16 wLength) +{ + switch (bRequest) { + case USB_REQ_GET_STATUS: + dwc3_decode_get_status(bRequestType, wIndex, wLength, str); + break; + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue, + wIndex, str); + break; + case USB_REQ_SET_ADDRESS: + dwc3_decode_set_address(wValue, str); + break; + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_SET_DESCRIPTOR: + dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue, + wIndex, wLength, str); + break; + case USB_REQ_GET_CONFIGURATION: + dwc3_decode_get_configuration(wLength, str); + break; + case USB_REQ_SET_CONFIGURATION: + dwc3_decode_set_configuration(wValue, str); + break; + case USB_REQ_GET_INTERFACE: + dwc3_decode_get_intf(wIndex, wLength, str); + break; + case USB_REQ_SET_INTERFACE: + dwc3_decode_set_intf(wValue, wIndex, str); + break; + case USB_REQ_SYNCH_FRAME: + dwc3_decode_synch_frame(wIndex, wLength, str); + break; + case USB_REQ_SET_SEL: + dwc3_decode_set_sel(wLength, str); + break; + case USB_REQ_SET_ISOCH_DELAY: + dwc3_decode_set_isoch_delay(wValue, str); + break; + default: + sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x", + bRequestType, bRequest, + cpu_to_le16(wValue) & 0xff, + cpu_to_le16(wValue) >> 8, + cpu_to_le16(wIndex) & 0xff, + cpu_to_le16(wIndex) >> 8, + cpu_to_le16(wLength) & 0xff, + cpu_to_le16(wLength) >> 8); + } + + return str; +} + +/** + * dwc3_ep_event_string - returns event name + * @event: then event code + */ +static inline const char * +dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event, + u32 ep0state) +{ + u8 epnum = event->endpoint_number; + size_t len; + int status; + int ret; + + ret = sprintf(str, "ep%d%s: ", epnum >> 1, + (epnum & 1) ? "in" : "out"); + if (ret < 0) + return "UNKNOWN"; + + status = event->status; + + switch (event->endpoint_event) { + case DWC3_DEPEVT_XFERCOMPLETE: + len = strlen(str); + sprintf(str + len, "Transfer Complete (%c%c%c)", + status & DEPEVT_STATUS_SHORT ? 'S' : 's', + status & DEPEVT_STATUS_IOC ? 'I' : 'i', + status & DEPEVT_STATUS_LST ? 'L' : 'l'); + + len = strlen(str); + + if (epnum <= 1) + sprintf(str + len, " [%s]", dwc3_ep0_state_string(ep0state)); + break; + case DWC3_DEPEVT_XFERINPROGRESS: + len = strlen(str); + + sprintf(str + len, "Transfer In Progress [%d] (%c%c%c)", + event->parameters, + status & DEPEVT_STATUS_SHORT ? 'S' : 's', + status & DEPEVT_STATUS_IOC ? 'I' : 'i', + status & DEPEVT_STATUS_LST ? 'M' : 'm'); + break; + case DWC3_DEPEVT_XFERNOTREADY: + len = strlen(str); + + sprintf(str + len, "Transfer Not Ready [%d]%s", + event->parameters, + status & DEPEVT_STATUS_TRANSFER_ACTIVE ? + " (Active)" : " (Not Active)"); + + /* Control Endpoints */ + if (epnum <= 1) { + int phase = DEPEVT_STATUS_CONTROL_PHASE(event->status); + + switch (phase) { + case DEPEVT_STATUS_CONTROL_DATA: + strcat(str, " [Data Phase]"); + break; + case DEPEVT_STATUS_CONTROL_STATUS: + strcat(str, " [Status Phase]"); + } + } + break; + case DWC3_DEPEVT_RXTXFIFOEVT: + strcat(str, "FIFO"); + break; + case DWC3_DEPEVT_STREAMEVT: + status = event->status; + + switch (status) { + case DEPEVT_STREAMEVT_FOUND: + sprintf(str + ret, " Stream %d Found", + event->parameters); + break; + case DEPEVT_STREAMEVT_NOTFOUND: + default: + strcat(str, " Stream Not Found"); + break; + } + + break; + case DWC3_DEPEVT_EPCMDCMPLT: + strcat(str, "Endpoint Command Complete"); + break; + default: + sprintf(str, "UNKNOWN"); + } + + return str; +} + +/** + * dwc3_gadget_event_type_string - return event name + * @event: the event code + */ +static inline const char *dwc3_gadget_event_type_string(u8 event) +{ + switch (event) { + case DWC3_DEVICE_EVENT_DISCONNECT: + return "Disconnect"; + case DWC3_DEVICE_EVENT_RESET: + return "Reset"; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + return "Connect Done"; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + return "Link Status Change"; + case DWC3_DEVICE_EVENT_WAKEUP: + return "Wake-Up"; + case DWC3_DEVICE_EVENT_HIBER_REQ: + return "Hibernation"; + case DWC3_DEVICE_EVENT_EOPF: + return "End of Periodic Frame"; + case DWC3_DEVICE_EVENT_SOF: + return "Start of Frame"; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + return "Erratic Error"; + case DWC3_DEVICE_EVENT_CMD_CMPL: + return "Command Complete"; + case DWC3_DEVICE_EVENT_OVERFLOW: + return "Overflow"; + default: + return "UNKNOWN"; + } +} + +static inline const char *dwc3_decode_event(char *str, u32 event, u32 ep0state) +{ + const union dwc3_event evt = (union dwc3_event) event; + + if (evt.type.is_devspec) + return dwc3_gadget_event_string(str, &evt.devt); + else + return dwc3_ep_event_string(str, &evt.depevt, ep0state); +} + +static inline const char *dwc3_ep_cmd_status_string(int status) +{ + switch (status) { + case -ETIMEDOUT: + return "Timed Out"; + case 0: + return "Successful"; + case DEPEVT_TRANSFER_NO_RESOURCE: + return "No Resource"; + case DEPEVT_TRANSFER_BUS_EXPIRY: + return "Bus Expiry"; + default: + return "UNKNOWN"; + } +} + +static inline const char *dwc3_gadget_generic_cmd_status_string(int status) +{ + switch (status) { + case -ETIMEDOUT: + return "Timed Out"; + case 0: + return "Successful"; + case 1: + return "Error"; + default: + return "UNKNOWN"; + } +} + + +#ifdef CONFIG_DEBUG_FS +extern void dwc3_debugfs_init(struct dwc3 *); +extern void dwc3_debugfs_exit(struct dwc3 *); +#else +static inline void dwc3_debugfs_init(struct dwc3 *d) +{ } +static inline void dwc3_debugfs_exit(struct dwc3 *d) +{ } +#endif +#endif /* __DWC3_DEBUG_H */ diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c new file mode 100644 index 0000000..d5daa7f --- /dev/null +++ b/drivers/usb/dwc3/host.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * host.c - DesignWare USB3 DRD Controller Host Glue + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi , + */ + +#include +#include +#include + +#include "core.h" + +int dwc3_host_init(struct dwc3 *dwc) +{ + struct resource *io; + struct device_d *dev = dwc->dev; + + io = dev_get_resource(dev, IORESOURCE_MEM, 0); + if (IS_ERR(io)) { + dev_err(dev, "Failed to get IORESOURCE_MEM\n"); + return PTR_ERR(io); + } + + dwc->xhci = add_generic_device("xHCI", DEVICE_ID_DYNAMIC, NULL, + io->start, resource_size(io), + IORESOURCE_MEM, NULL); + if (!dwc->xhci) { + dev_err(dev, "Failed to register xHCI device\n"); + return -ENODEV; + } + + return 0; +} diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h new file mode 100644 index 0000000..f87b173 --- /dev/null +++ b/drivers/usb/dwc3/io.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * io.h - DesignWare USB3 DRD IO Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + */ + +#ifndef __DRIVERS_USB_DWC3_IO_H +#define __DRIVERS_USB_DWC3_IO_H + +#include +#include "core.h" + +static inline u32 dwc3_readl(void __iomem *base, u32 offset) +{ + u32 value; + + /* + * We requested the mem region starting from the Globals address + * space, see dwc3_probe in core.c. + * However, the offsets are given starting from xHCI address space. + */ + value = readl(base + offset - DWC3_GLOBALS_REGS_START); + + return value; +} + +static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value) +{ + /* + * We requested the mem region starting from the Globals address + * space, see dwc3_probe in core.c. + * However, the offsets are given starting from xHCI address space. + */ + writel(value, base + offset - DWC3_GLOBALS_REGS_START); +} + +#endif /* __DRIVERS_USB_DWC3_IO_H */ diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 2b808cc..32a6ccd 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -23,16 +23,49 @@ #include "xhci.h" + +static struct xhci_input_control_ctx * +xhci_get_input_control_ctx(struct xhci_container_ctx *ctx) +{ + if (ctx->type != XHCI_CTX_TYPE_INPUT) + return NULL; + + return (struct xhci_input_control_ctx *)ctx->bytes; +} + +static struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx) +{ + if (ctx->type == XHCI_CTX_TYPE_DEVICE) + return (struct xhci_slot_ctx *)ctx->bytes; + + return (struct xhci_slot_ctx *) + (ctx->bytes + HCC_CTX_SIZE(xhci->hcc_params)); +} + +static struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx, + unsigned int ep_index) +{ + /* increment ep index by offset of start of ep ctx array */ + ep_index++; + if (ctx->type == XHCI_CTX_TYPE_INPUT) + ep_index++; + + return (struct xhci_ep_ctx *) + (ctx->bytes + (ep_index * HCC_CTX_SIZE(xhci->hcc_params))); +} + /* * xHCI ring handling */ static int xhci_ring_is_last_trb(struct xhci_ring *ring, union xhci_trb *trb) { - if (ring->type == TYPE_EVENT) - return trb == &ring->trbs[NUM_EVENT_TRBS]; - else - return TRB_TYPE_LINK(le32_to_cpu(trb->link.control)); + if (ring->type == TYPE_EVENT) + return trb == &ring->trbs[NUM_EVENT_TRBS]; + else + return TRB_TYPE_LINK(le32_to_cpu(trb->link.control)); } static int xhci_ring_increment(struct xhci_ring *ring, bool enqueue) @@ -41,7 +74,7 @@ (*queue)++; - if (!xhci_ring_is_last_trb(ring, *queue)) + if (!xhci_ring_is_last_trb(ring, *queue)) return 0; if (ring->type == TYPE_EVENT) { @@ -139,15 +172,15 @@ { u8 epnum = epaddress & USB_ENDPOINT_NUMBER_MASK; u8 xfer = epattributes & USB_ENDPOINT_XFERTYPE_MASK; - unsigned int index; + unsigned int index; - if (xfer == USB_ENDPOINT_XFER_CONTROL) - index = (unsigned int)(epnum * 2); - else - index = (unsigned int)(epnum * 2) + - ((epaddress & USB_DIR_IN) ? 1 : 0) - 1; + if (xfer == USB_ENDPOINT_XFER_CONTROL) + index = (unsigned int)(epnum * 2); + else + index = (unsigned int)(epnum * 2) + + ((epaddress & USB_DIR_IN) ? 1 : 0) - 1; - return index; + return index; } static u8 xhci_get_endpoint_type(u8 epaddress, u8 epattributes) @@ -341,15 +374,15 @@ static void xhci_set_event_dequeue(struct xhci_hcd *xhci) { - u64 reg64; + u64 reg64; - reg64 = xhci_read_64(&xhci->ir_set->erst_dequeue); - reg64 &= ERST_PTR_MASK; - /* + reg64 = xhci_read_64(&xhci->ir_set->erst_dequeue); + reg64 &= ERST_PTR_MASK; + /* * Don't clear the EHB bit (which is RW1C) because - * there might be more events to service. - */ - reg64 &= ~ERST_EHB; + * there might be more events to service. + */ + reg64 &= ~ERST_EHB; reg64 |= (dma_addr_t)xhci->event_ring.dequeue & ~(dma_addr_t)ERST_PTR_MASK; @@ -426,29 +459,48 @@ return NULL; } +static struct xhci_container_ctx * +xhci_alloc_container_ctx(struct xhci_hcd *xhci, int type) +{ + struct xhci_container_ctx *ctx; + + if ((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT)) + return NULL; + + ctx = xzalloc(sizeof(*ctx)); + ctx->type = type; + ctx->size = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024; + if (type == XHCI_CTX_TYPE_INPUT) + ctx->size += HCC_CTX_SIZE(xhci->hcc_params); + + ctx->bytes = dma_alloc_coherent(ctx->size, &ctx->dma); + if (WARN_ON(!ctx->bytes)) { + kfree(ctx); + return NULL; + } + return ctx; +} + +static void xhci_free_container_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx) +{ + if (!ctx) + return; + dma_free_coherent(ctx->bytes, ctx->dma, ctx->size); + kfree(ctx); +} + static struct xhci_virtual_device *xhci_alloc_virtdev(struct xhci_hcd *xhci, struct usb_device *udev) { struct xhci_virtual_device *vdev; - size_t sz_ctx, sz_ictx, sz_dctx; - void *p; vdev = xzalloc(sizeof(*vdev)); vdev->udev = udev; list_add_tail(&vdev->list, &xhci->vdev_list); - sz_ctx = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024; - /* Device Context: 64B aligned */ - sz_dctx = ALIGN(sz_ctx, 64); - /* Input Control Context: 64B aligned */ - sz_ictx = ALIGN(sz_ctx + HCC_CTX_SIZE(xhci->hcc_params), 64); - - vdev->dma_size = sz_ictx + sz_dctx; - p = vdev->dma = dma_alloc_coherent(vdev->dma_size, DMA_ADDRESS_BROKEN); - memset(vdev->dma, 0, vdev->dma_size); - - vdev->out_ctx = p; p += sz_dctx; - vdev->in_ctx = p; p += sz_ictx; + vdev->out_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_DEVICE); + vdev->in_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT); return vdev; } @@ -463,7 +515,8 @@ xhci_put_endpoint_ring(xhci, vdev->ep[i]); list_del(&vdev->list); - dma_free_coherent(vdev->dma, 0, vdev->dma_size); + xhci_free_container_ctx(xhci, vdev->out_ctx); + xhci_free_container_ctx(xhci, vdev->in_ctx); free(vdev); } @@ -487,26 +540,43 @@ static void xhci_virtdev_zero_in_ctx(struct xhci_virtual_device *vdev) { + struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + struct xhci_input_control_ctx *in_icc; + struct xhci_slot_ctx *in_slot; + struct xhci_ep_ctx *in_ep; int i; - /* When a device's add flag and drop flag are zero, any subsequent - * configure endpoint command will leave that endpoint's state - * untouched. Make sure we don't leave any old state in the input - * endpoint contexts. - */ - vdev->in_ctx->icc.drop_flags = 0; - vdev->in_ctx->icc.add_flags = 0; - vdev->in_ctx->slot.dev_info &= cpu_to_le32(~LAST_CTX_MASK); - /* Endpoint 0 is always valid */ - vdev->in_ctx->slot.dev_info |= cpu_to_le32(LAST_CTX(1)); + in_icc = xhci_get_input_control_ctx(vdev->in_ctx); + in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); + + /* When a device's add flag and drop flag are zero, any subsequent + * configure endpoint command will leave that endpoint's state + * untouched. Make sure we don't leave any old state in the input + * endpoint contexts. + */ + in_icc->drop_flags = 0; + in_icc->add_flags = 0; + in_slot->dev_info &= cpu_to_le32(~LAST_CTX_MASK); + /* Endpoint 0 is always valid */ + in_slot->dev_info |= cpu_to_le32(LAST_CTX(1)); for (i = 1; i < 31; i++) { - vdev->in_ctx->ep[i].ep_info = 0; - vdev->in_ctx->ep[i].ep_info2 = 0; - vdev->in_ctx->ep[i].deq = 0; - vdev->in_ctx->ep[i].tx_info = 0; + in_ep = xhci_get_ep_ctx(xhci, vdev->in_ctx, i); + + in_ep->ep_info = 0; + in_ep->ep_info2 = 0; + in_ep->deq = 0; + in_ep->tx_info = 0; } } +static void xhci_init_event_cmd_trb(union xhci_trb *trb, + u64 cmd_trb, u32 status, u32 flags) +{ + xhci_write_64(cmd_trb, &trb->event_cmd.cmd_trb); + writel(status, &trb->event_cmd.status); + writel(flags, &trb->event_cmd.flags); +} + static int xhci_virtdev_disable_slot(struct xhci_virtual_device *vdev) { struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); @@ -514,15 +584,17 @@ int ret; /* Issue Disable Slot Command */ - memset(&trb, 0, sizeof(union xhci_trb)); - trb.event_cmd.flags = TRB_TYPE(TRB_DISABLE_SLOT) | - SLOT_ID_FOR_TRB(vdev->slot_id); + xhci_init_event_cmd_trb(&trb, + 0, + 0, + TRB_TYPE(TRB_DISABLE_SLOT) | + SLOT_ID_FOR_TRB(vdev->slot_id)); xhci_print_trb(xhci, &trb, "Request DisableSlot"); xhci_issue_command(xhci, &trb); ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); xhci_print_trb(xhci, &trb, "Response DisableSlot"); - /* Clear Device Context Base Address Array */ + /* Clear Device Context Base Address Array */ xhci->dcbaa[vdev->slot_id] = 0; return ret; @@ -536,8 +608,10 @@ int ret; /* Issue Enable Slot Command */ - memset(&trb, 0, sizeof(union xhci_trb)); - trb.event_cmd.flags = TRB_TYPE(TRB_ENABLE_SLOT); + xhci_init_event_cmd_trb(&trb, + 0, + 0, + TRB_TYPE(TRB_ENABLE_SLOT)); xhci_print_trb(xhci, &trb, "Request EnableSlot"); xhci_issue_command(xhci, &trb); ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); @@ -559,42 +633,47 @@ int xhci_virtdev_reset(struct xhci_virtual_device *vdev) { struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + struct xhci_slot_ctx *out_slot; union xhci_trb trb; int ret; - /* If device is not setup, there is no point in resetting it */ - if (GET_SLOT_STATE(le32_to_cpu(vdev->out_ctx->slot.dev_state)) == - SLOT_STATE_DISABLED) - return 0; + out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx); - memset(&trb, 0, sizeof(union xhci_trb)); - trb.event_cmd.flags = TRB_TYPE(TRB_RESET_DEV) | - SLOT_ID_FOR_TRB(vdev->slot_id); + /* If device is not setup, there is no point in resetting it */ + if (GET_SLOT_STATE(le32_to_cpu(out_slot->dev_state)) == + SLOT_STATE_DISABLED) + return 0; + + xhci_init_event_cmd_trb(&trb, + 0, + 0, + TRB_TYPE(TRB_RESET_DEV) | + SLOT_ID_FOR_TRB(vdev->slot_id)); xhci_print_trb(xhci, &trb, "Request Reset"); xhci_issue_command(xhci, &trb); ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); xhci_print_trb(xhci, &trb, "Response Reset"); - /* + /* * The Reset Device command can't fail, according to the 0.95/0.96 spec, - * unless we tried to reset a slot ID that wasn't enabled, - * or the device wasn't in the addressed or configured state. - */ - switch (GET_COMP_CODE(trb.event_cmd.status)) { - case COMP_CMD_ABORT: - case COMP_CMD_STOP: - dev_warn(xhci->dev, "Timeout waiting for reset device command\n"); - ret = -ETIMEDOUT; + * unless we tried to reset a slot ID that wasn't enabled, + * or the device wasn't in the addressed or configured state. + */ + switch (GET_COMP_CODE(trb.event_cmd.status)) { + case COMP_CMD_ABORT: + case COMP_CMD_STOP: + dev_warn(xhci->dev, "Timeout waiting for reset device command\n"); + ret = -ETIMEDOUT; break; - case COMP_EBADSLT: /* 0.95 completion code for bad slot ID */ - case COMP_CTX_STATE: /* 0.96 completion code for same thing */ - /* Don't treat this as an error. May change my mind later. */ - ret = 0; - case COMP_SUCCESS: - break; - default: - ret = -EINVAL; - } + case COMP_EBADSLT: /* 0.95 completion code for bad slot ID */ + case COMP_CTX_STATE: /* 0.96 completion code for same thing */ + /* Don't treat this as an error. May change my mind later. */ + ret = 0; + case COMP_SUCCESS: + break; + default: + ret = -EINVAL; + } return ret; } @@ -608,31 +687,38 @@ { struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); struct usb_hub_descriptor *desc = buffer; + struct xhci_input_control_ctx *in_icc; + struct xhci_slot_ctx *in_slot, *out_slot; union xhci_trb trb; u32 dev_info, dev_info2, tt_info; u8 maxchild; u16 hubchar; + u32 flags; int ret; + out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx); + /* Need at least first byte of wHubCharacteristics */ if (length < 4) return 0; /* Skip already configured hub device */ - if (vdev->out_ctx->slot.dev_info & DEV_HUB) + if (out_slot->dev_info & DEV_HUB) return 0; maxchild = desc->bNbrPorts; hubchar = le16_to_cpu(desc->wHubCharacteristics); + in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); + in_icc = xhci_get_input_control_ctx(vdev->in_ctx); + /* update slot context */ - memcpy(&vdev->in_ctx->slot, &vdev->out_ctx->slot, - sizeof(struct xhci_slot_ctx)); - vdev->in_ctx->icc.add_flags |= cpu_to_le32(SLOT_FLAG); - vdev->in_ctx->icc.drop_flags = 0; - vdev->in_ctx->slot.dev_state = 0; - dev_info = le32_to_cpu(vdev->in_ctx->slot.dev_info); - dev_info2 = le32_to_cpu(vdev->in_ctx->slot.dev_info2); - tt_info = le32_to_cpu(vdev->in_ctx->slot.tt_info); + memcpy(in_slot, out_slot, sizeof(struct xhci_slot_ctx)); + in_icc->add_flags |= cpu_to_le32(SLOT_FLAG); + in_icc->drop_flags = 0; + in_slot->dev_state = 0; + dev_info = le32_to_cpu(in_slot->dev_info); + dev_info2 = le32_to_cpu(in_slot->dev_info2); + tt_info = le32_to_cpu(in_slot->tt_info); dev_info |= DEV_HUB; /* HS Multi-TT in bDeviceProtocol */ @@ -651,21 +737,23 @@ if (xhci->hci_version < 0x100 || vdev->udev->speed == USB_SPEED_HIGH) { u32 think_time = (hubchar & HUB_CHAR_TTTT) >> 5; - tt_info |= TT_THINK_TIME(think_time); + tt_info |= TT_THINK_TIME(think_time); } - } - vdev->in_ctx->slot.dev_info = cpu_to_le32(dev_info); - vdev->in_ctx->slot.dev_info2 = cpu_to_le32(dev_info2); - vdev->in_ctx->slot.tt_info = cpu_to_le32(tt_info); + } + in_slot->dev_info = cpu_to_le32(dev_info); + in_slot->dev_info2 = cpu_to_le32(dev_info2); + in_slot->tt_info = cpu_to_le32(tt_info); - /* Issue Configure Endpoint or Evaluate Context Command */ - memset(&trb, 0, sizeof(union xhci_trb)); - xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); - trb.event_cmd.flags = SLOT_ID_FOR_TRB(vdev->slot_id); + /* Issue Configure Endpoint or Evaluate Context Command */ + flags = SLOT_ID_FOR_TRB(vdev->slot_id); if (xhci->hci_version > 0x95) - trb.event_cmd.flags |= TRB_TYPE(TRB_CONFIG_EP); + flags |= TRB_TYPE(TRB_CONFIG_EP); else - trb.event_cmd.flags |= TRB_TYPE(TRB_EVAL_CONTEXT); + flags |= TRB_TYPE(TRB_EVAL_CONTEXT); + xhci_init_event_cmd_trb(&trb, + vdev->in_ctx->dma, + 0, + flags); xhci_print_trb(xhci, &trb, "Request ConfigureEndpoint"); xhci_issue_command(xhci, &trb); ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); @@ -696,6 +784,8 @@ static int xhci_virtdev_configure(struct xhci_virtual_device *vdev, int config) { struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + struct xhci_input_control_ctx *in_icc; + struct xhci_slot_ctx *in_slot; struct usb_device *udev = vdev->udev; union xhci_trb trb; u32 add_flags = 0, last_ctx; @@ -712,10 +802,12 @@ ep->bmAttributes); u8 epi = xhci_get_endpoint_index(ep->bEndpointAddress, ep->bmAttributes); - struct xhci_ep_ctx *ctx = &vdev->in_ctx->ep[epi]; + struct xhci_ep_ctx *ctx; u32 mps, interval, mult, esit, max_packet, max_burst; u32 ep_info, ep_info2, tx_info; + ctx = xhci_get_ep_ctx(xhci, vdev->in_ctx, epi); + vdev->ep[epi] = xhci_get_endpoint_ring(xhci); if (!vdev->ep[epi]) return -ENOMEM; @@ -782,25 +874,29 @@ last_ctx = fls(add_flags) - 1; - /* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */ - vdev->in_ctx->icc.add_flags = cpu_to_le32(add_flags); - vdev->in_ctx->icc.add_flags |= cpu_to_le32(SLOT_FLAG); - vdev->in_ctx->icc.add_flags &= cpu_to_le32(~EP0_FLAG); - vdev->in_ctx->icc.drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG)); + in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); + in_icc = xhci_get_input_control_ctx(vdev->in_ctx); + + /* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */ + in_icc->add_flags = cpu_to_le32(add_flags); + in_icc->add_flags |= cpu_to_le32(SLOT_FLAG); + in_icc->add_flags &= cpu_to_le32(~EP0_FLAG); + in_icc->drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG)); /* Don't issue the command if there's no endpoints to update. */ - if (vdev->in_ctx->icc.add_flags == cpu_to_le32(SLOT_FLAG) && - vdev->in_ctx->icc.drop_flags == 0) + if (in_icc->add_flags == cpu_to_le32(SLOT_FLAG) && + in_icc->drop_flags == 0) return 0; - vdev->in_ctx->slot.dev_info &= cpu_to_le32(~LAST_CTX_MASK); - vdev->in_ctx->slot.dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); + in_slot->dev_info &= cpu_to_le32(~LAST_CTX_MASK); + in_slot->dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); - /* Issue Configure Endpoint Command */ - memset(&trb, 0, sizeof(union xhci_trb)); - xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); - trb.event_cmd.flags = TRB_TYPE(TRB_CONFIG_EP) | - SLOT_ID_FOR_TRB(vdev->slot_id); + /* Issue Configure Endpoint Command */ + xhci_init_event_cmd_trb(&trb, + vdev->in_ctx->dma, + 0, + TRB_TYPE(TRB_CONFIG_EP) | + SLOT_ID_FOR_TRB(vdev->slot_id)); xhci_print_trb(xhci, &trb, "Request ConfigureEndpoint"); xhci_issue_command(xhci, &trb); ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); @@ -816,11 +912,12 @@ union xhci_trb trb; int ret; - /* Issue Deconfigure Endpoint Command */ - memset(&trb, 0, sizeof(union xhci_trb)); - xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); - trb.event_cmd.flags = TRB_TYPE(TRB_CONFIG_EP) | TRB_DC | - SLOT_ID_FOR_TRB(vdev->slot_id); + /* Issue Deconfigure Endpoint Command */ + xhci_init_event_cmd_trb(&trb, + vdev->in_ctx->dma, + 0, + TRB_TYPE(TRB_CONFIG_EP) | TRB_DC | + SLOT_ID_FOR_TRB(vdev->slot_id)); xhci_print_trb(xhci, &trb, "Request DeconfigureEndpoint"); xhci_issue_command(xhci, &trb); ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); @@ -834,23 +931,30 @@ { struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); struct usb_device *top_dev; + struct xhci_slot_ctx *in_slot; + struct xhci_ep_ctx *in_ep; int max_packets; u32 route = 0, dev_info, dev_info2, tt_info, ep_info2, tx_info; bool on_hs_hub = false; int hs_slot_id = 0; + in_ep = xhci_get_ep_ctx(xhci, vdev->in_ctx, 0); + in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); + /* * Find the root hub port this device is under, also determine SlotID * of possible external HS hub a LS/FS device could be connected to. */ for (top_dev = vdev->udev; top_dev->parent && top_dev->parent->parent; top_dev = top_dev->parent) { - if (top_dev->parent->descriptor->bDeviceClass == USB_CLASS_HUB) - route = (route << 4) | (top_dev->portnr & 0xf); - if (top_dev->parent->descriptor->bDeviceClass == USB_CLASS_HUB && - top_dev->parent->speed != USB_SPEED_LOW && + if (top_dev->parent->descriptor->bDeviceClass != USB_CLASS_HUB) + continue; + + route = (route << 4) | (top_dev->portnr & 0xf); + + if (top_dev->parent->speed != USB_SPEED_LOW && top_dev->parent->speed != USB_SPEED_FULL) { - on_hs_hub |= true; + on_hs_hub = true; if (!hs_slot_id) { struct xhci_virtual_device *vhub = xhci_find_virtdev(xhci, top_dev->parent); @@ -893,9 +997,9 @@ tt_info |= (top_dev->portnr << 8) | hs_slot_id; } - vdev->in_ctx->slot.dev_info = cpu_to_le32(dev_info); - vdev->in_ctx->slot.dev_info2 = cpu_to_le32(dev_info2); - vdev->in_ctx->slot.tt_info = cpu_to_le32(tt_info); + in_slot->dev_info = cpu_to_le32(dev_info); + in_slot->dev_info2 = cpu_to_le32(dev_info2); + in_slot->tt_info = cpu_to_le32(tt_info); /* 4.3.3 4) Initalize Transfer Ring */ vdev->ep[0] = xhci_get_endpoint_ring(xhci); @@ -907,13 +1011,13 @@ ep_info2 = EP_TYPE(CTRL_EP) | MAX_BURST(0) | ERROR_COUNT(3); ep_info2 |= MAX_PACKET(max_packets); tx_info = AVG_TRB_LENGTH_FOR_EP(8); - vdev->in_ctx->ep[0].ep_info2 = cpu_to_le32(ep_info2); - vdev->in_ctx->ep[0].tx_info = cpu_to_le32(tx_info); - vdev->in_ctx->ep[0].deq = cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue | - vdev->ep[0]->cycle_state); + in_ep->ep_info2 = cpu_to_le32(ep_info2); + in_ep->tx_info = cpu_to_le32(tx_info); + in_ep->deq = cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue | + vdev->ep[0]->cycle_state); - /* 4.3.3 6+7) Initalize and Set Device Context Base Address Array */ - xhci->dcbaa[vdev->slot_id] = cpu_to_le64((dma_addr_t)vdev->out_ctx); + /* 4.3.3 6+7) Initalize and Set Device Context Base Address Array */ + xhci->dcbaa[vdev->slot_id] = cpu_to_le64(vdev->out_ctx->dma); return 0; } @@ -922,43 +1026,53 @@ enum xhci_setup_dev setup) { struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + static struct xhci_input_control_ctx *in_icc; + struct xhci_slot_ctx *in_slot; + struct xhci_ep_ctx *in_ep; union xhci_trb trb; + u32 flags; int ret; + in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx); + in_icc = xhci_get_input_control_ctx(vdev->in_ctx); + /* * If this is the first Set Address since device * plug-in then initialize Slot Context */ - if (!vdev->in_ctx->slot.dev_info) + if (!in_slot->dev_info) xhci_virtdev_init(vdev); else { + in_ep = xhci_get_ep_ctx(xhci, vdev->in_ctx, 0); + /* Otherwise, update Control Ring Dequeue pointer */ - vdev->in_ctx->ep[0].deq = - cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue | - vdev->ep[0]->cycle_state); + in_ep->deq = cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue | + vdev->ep[0]->cycle_state); /* * FS devices have MaxPacketSize0 of 8 or 64, we start * with 64. If assumtion was wrong, fix it up here. */ if (vdev->udev->speed == USB_SPEED_FULL && vdev->udev->maxpacketsize == PACKET_SIZE_8) { - u32 info = le32_to_cpu(vdev->in_ctx->ep[0].ep_info2); + u32 info = le32_to_cpu(in_ep->ep_info2); info &= ~MAX_PACKET_MASK; info |= MAX_PACKET(8); - vdev->in_ctx->ep[0].ep_info2 = cpu_to_le32(info); + in_ep->ep_info2 = cpu_to_le32(info); } } - vdev->in_ctx->icc.add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); - vdev->in_ctx->icc.drop_flags = 0; + in_icc->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); + in_icc->drop_flags = 0; - /* Issue Address Device Command */ - memset(&trb, 0, sizeof(union xhci_trb)); - xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); - trb.event_cmd.flags = TRB_TYPE(TRB_ADDR_DEV) | + /* Issue Address Device Command */ + flags = TRB_TYPE(TRB_ADDR_DEV) | SLOT_ID_FOR_TRB(vdev->slot_id); if (setup == SETUP_CONTEXT_ONLY) - trb.event_cmd.flags |= TRB_BSR; + flags |= TRB_BSR; + xhci_init_event_cmd_trb(&trb, + vdev->in_ctx->dma, + 0, + flags); xhci_print_trb(xhci, &trb, "Request AddressDevice"); xhci_issue_command(xhci, &trb); ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); @@ -970,12 +1084,12 @@ static int xhci_virtdev_set_address(struct xhci_virtual_device *vdev) { - return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ADDRESS); + return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ADDRESS); } static int xhci_virtdev_enable(struct xhci_virtual_device *vdev) { - return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ONLY); + return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ONLY); } static int xhci_virtdev_attach(struct xhci_hcd *xhci, struct usb_device *udev) @@ -1008,45 +1122,57 @@ { struct usb_host *host = udev->host; struct xhci_hcd *xhci = to_xhci_hcd(host); + enum dma_data_direction dma_direction; struct xhci_virtual_device *vdev; + struct xhci_slot_ctx *out_slot; + dma_addr_t buffer_dma; union xhci_trb trb; - u8 epaddr = (usb_pipein(pipe) ? USB_DIR_IN : USB_DIR_OUT) | - usb_pipeendpoint(pipe); - u8 epi = xhci_get_endpoint_index(epaddr, usb_pipetype(pipe)); + u8 epaddr = usb_pipeendpoint(pipe); + u8 epi; + u32 flags = TRB_TYPE(TRB_NORMAL) | TRB_IOC; int ret; + if (usb_pipein(pipe)) { + epaddr |= USB_DIR_IN; + flags |= TRB_ISP; + dma_direction = DMA_FROM_DEVICE; + } else { + epaddr |= USB_DIR_OUT; + dma_direction = DMA_TO_DEVICE; + } + + epi = xhci_get_endpoint_index(epaddr, usb_pipetype(pipe)); vdev = xhci_find_virtdev(xhci, udev); if (!vdev) return -ENODEV; + out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx); + dev_dbg(xhci->dev, "%s udev %p vdev %p slot %u state %u epi %u in_ctx %p out_ctx %p\n", __func__, udev, vdev, vdev->slot_id, - GET_SLOT_STATE(le32_to_cpu(vdev->out_ctx->slot.dev_state)), epi, - vdev->in_ctx, vdev->out_ctx); + GET_SLOT_STATE(le32_to_cpu(out_slot->dev_state)), epi, + vdev->in_ctx->bytes, vdev->out_ctx->bytes); /* pass ownership of data buffer to device */ - dma_sync_single_for_device((unsigned long)buffer, length, - usb_pipein(pipe) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); + buffer_dma = dma_map_single(xhci->dev, buffer, length, + dma_direction); + if (dma_mapping_error(xhci->dev, buffer_dma)) + return -EFAULT; /* Normal TRB */ - memset(&trb, 0, sizeof(union xhci_trb)); - trb.event_cmd.cmd_trb = cpu_to_le64((dma_addr_t)buffer); /* FIXME: TD remainder */ - trb.event_cmd.status = TRB_LEN(length) | TRB_INTR_TARGET(0); - trb.event_cmd.flags = TRB_TYPE(TRB_NORMAL) | TRB_IOC; - if (usb_pipein(pipe)) - trb.event_cmd.flags |= TRB_ISP; + xhci_init_event_cmd_trb(&trb, + buffer_dma, + TRB_LEN(length) | TRB_INTR_TARGET(0), + flags); xhci_print_trb(xhci, &trb, "Request Normal"); xhci_virtdev_issue_transfer(vdev, epi, &trb, true); ret = xhci_wait_for_event(xhci, TRB_TRANSFER, &trb); xhci_print_trb(xhci, &trb, "Response Normal"); /* Regain ownership of data buffer from device */ - dma_sync_single_for_cpu((unsigned long)buffer, length, - usb_pipein(pipe) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); - + dma_unmap_single(xhci->dev, buffer_dma, length, + dma_direction); switch (ret) { case -COMP_SHORT_TX: udev->status = 0; @@ -1070,8 +1196,12 @@ struct usb_host *host = udev->host; struct xhci_hcd *xhci = to_xhci_hcd(host); struct xhci_virtual_device *vdev; + struct xhci_slot_ctx *out_slot; + dma_addr_t buffer_dma = 0; union xhci_trb trb; u16 typeReq = (req->requesttype << 8) | req->request; + u64 field[2]; + u32 flags; int ret; dev_dbg(xhci->dev, "%s req %u (%#x), type %u (%#x), value %u (%#x), index %u (%#x), length %u (%#x)\n", @@ -1091,10 +1221,12 @@ if (!vdev) return -ENODEV; + out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx); + dev_dbg(xhci->dev, "%s udev %p vdev %p slot %u state %u epi %u in_ctx %p out_ctx %p\n", __func__, udev, vdev, vdev->slot_id, - GET_SLOT_STATE(le32_to_cpu(vdev->out_ctx->slot.dev_state)), 0, - vdev->in_ctx, vdev->out_ctx); + GET_SLOT_STATE(le32_to_cpu(out_slot->dev_state)), 0, + vdev->in_ctx->bytes, vdev->out_ctx->bytes); if (req->request == USB_REQ_SET_ADDRESS) return xhci_virtdev_set_address(vdev); @@ -1104,53 +1236,59 @@ return ret; } - /* Pass ownership of data buffer to device */ - dma_sync_single_for_device((unsigned long)buffer, length, - (req->requesttype & USB_DIR_IN) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); - - /* Setup TRB */ - memset(&trb, 0, sizeof(union xhci_trb)); - trb.generic.field[0] = le16_to_cpu(req->value) << 16 | - req->request << 8 | req->requesttype; - trb.generic.field[1] = le16_to_cpu(req->length) << 16 | - le16_to_cpu(req->index); - trb.event_cmd.status = TRB_LEN(8) | TRB_INTR_TARGET(0); - trb.event_cmd.flags = TRB_TYPE(TRB_SETUP) | TRB_IDT; - if (xhci->hci_version == 0x100 && length > 0) { - if (req->requesttype & USB_DIR_IN) - trb.event_cmd.flags |= TRB_TX_TYPE(TRB_DATA_IN); - else - trb.event_cmd.flags |= TRB_TX_TYPE(TRB_DATA_OUT); + if (length > 0) { + /* Pass ownership of data buffer to device */ + buffer_dma = dma_map_single(xhci->dev, buffer, length, + (req->requesttype & USB_DIR_IN) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (dma_mapping_error(xhci->dev, buffer_dma)) + return -EFAULT; } + /* Setup TRB */ + field[0] = le16_to_cpu(req->value) << 16 | + req->request << 8 | req->requesttype; + field[1] = le16_to_cpu(req->length) << 16 | + le16_to_cpu(req->index); + flags = TRB_TYPE(TRB_SETUP) | TRB_IDT; + if (xhci->hci_version >= 0x100 && length > 0) { + if (req->requesttype & USB_DIR_IN) + flags |= TRB_TX_TYPE(TRB_DATA_IN); + else + flags |= TRB_TX_TYPE(TRB_DATA_OUT); + } + xhci_init_event_cmd_trb(&trb, + field[1] << 32 | field[0], + TRB_LEN(8) | TRB_INTR_TARGET(0), + flags); xhci_print_trb(xhci, &trb, "Request Setup "); xhci_virtdev_issue_transfer(vdev, 0, &trb, false); /* Data TRB */ if (length > 0) { - memset(&trb, 0, sizeof(union xhci_trb)); - trb.event_cmd.cmd_trb = cpu_to_le64((dma_addr_t)buffer); /* FIXME: TD remainder */ - trb.event_cmd.status = TRB_LEN(length) | TRB_INTR_TARGET(0); - trb.event_cmd.flags = TRB_TYPE(TRB_DATA) | TRB_IOC; + flags = TRB_TYPE(TRB_DATA) | TRB_IOC; if (req->requesttype & USB_DIR_IN) - trb.event_cmd.flags |= TRB_ISP | TRB_DIR_IN; + flags |= TRB_ISP | TRB_DIR_IN; + xhci_init_event_cmd_trb(&trb, + buffer_dma, + TRB_LEN(length) | TRB_INTR_TARGET(0), + flags); xhci_print_trb(xhci, &trb, "Request Data "); xhci_virtdev_issue_transfer(vdev, 0, &trb, false); } /* Status TRB */ - memset(&trb, 0, sizeof(union xhci_trb)); - trb.event_cmd.status = TRB_INTR_TARGET(0); - if (length > 0 && req->requesttype & USB_DIR_IN) - trb.event_cmd.flags = 0; - else - trb.event_cmd.flags = TRB_DIR_IN; - trb.event_cmd.flags |= TRB_TYPE(TRB_STATUS) | TRB_IOC; + flags = TRB_TYPE(TRB_STATUS) | TRB_IOC; + if (!(length > 0 && req->requesttype & USB_DIR_IN)) + flags |= TRB_DIR_IN; + xhci_init_event_cmd_trb(&trb, + 0, + TRB_INTR_TARGET(0), + flags); xhci_print_trb(xhci, &trb, "Request Status"); xhci_virtdev_issue_transfer(vdev, 0, &trb, true); - if (length > 0 && req->requesttype & USB_DIR_IN) { + if (length > 0) { ret = xhci_wait_for_event(xhci, TRB_TRANSFER, &trb); xhci_print_trb(xhci, &trb, "Response Data "); if (ret == -COMP_SHORT_TX) @@ -1163,10 +1301,13 @@ xhci_print_trb(xhci, &trb, "Response Status"); dma_regain: - /* Regain ownership of data buffer from device */ - dma_sync_single_for_cpu((unsigned long)buffer, length, - (req->requesttype & USB_DIR_IN) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (length > 0) { + /* Regain ownership of data buffer from device */ + dma_unmap_single(xhci->dev, buffer_dma, length, + (req->requesttype & USB_DIR_IN) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + } + if (ret < 0) return ret; @@ -1224,8 +1365,7 @@ num_ep = max(MAX_EP_RINGS, MIN_EP_RINGS + num_ep); xhci->dma_size += num_ep * sz_ep; - p = xhci->dma = dma_alloc_coherent(xhci->dma_size, DMA_ADDRESS_BROKEN); - memset(xhci->dma, 0, xhci->dma_size); + p = dma_alloc_coherent(xhci->dma_size, DMA_ADDRESS_BROKEN); xhci->sp = p; p += sz_sp; xhci->dcbaa = p; p += sz_dca; @@ -1261,7 +1401,7 @@ static int xhci_halt(struct xhci_hcd *xhci) { u32 reg = readl(&xhci->op_regs->status); - u32 mask = ~XHCI_IRQS; + u32 mask = (u32)~XHCI_IRQS; if (!(reg & STS_HALT)) mask &= ~CMD_RUN; @@ -1291,7 +1431,7 @@ return ret; } - return 0; + return 0; } static int xhci_start(struct xhci_hcd *xhci) @@ -1305,8 +1445,8 @@ ret = xhci_handshake(&xhci->op_regs->status, STS_HALT, 0, XHCI_MAX_HALT_USEC); - if (ret) { - dev_err(xhci->dev, "failed to start\n"); + if (ret) { + dev_err(xhci->dev, "failed to start\n"); return ret; } @@ -1314,7 +1454,7 @@ for (i = 0; i < xhci->num_usb_ports; i++) xhci_hub_port_power(xhci, i, false); - return 0; + return 0; } static int xhci_init(struct usb_host *host) @@ -1472,6 +1612,7 @@ xhci = xzalloc(sizeof(*xhci)); host = &xhci->host; INIT_LIST_HEAD(&xhci->vdev_list); + INIT_LIST_HEAD(&xhci->rings_list); xhci->dev = dev; xhci->cap_regs = data->regs; xhci->op_regs = (void __iomem *)xhci->cap_regs + diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 078f881..84a14dd 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -24,6 +24,8 @@ #ifndef __XHCI_H #define __XHCI_H +#include + #define NUM_COMMAND_TRBS 8 #define NUM_TRANSFER_TRBS 8 #define NUM_EVENT_SEGM 1 /* only one supported */ @@ -504,6 +506,27 @@ #define DB_VALUE_HOST 0x00000000 /** + * struct xhci_container_ctx + * @type: Type of context. Used to calculated offsets to contained contexts. + * @size: Size of the context data + * @bytes: The raw context data given to HW + * @dma: dma address of the bytes + * + * Represents either a Device or Input context. Holds a pointer to the raw + * memory used for the context (bytes) and dma address of it (dma). + */ +struct xhci_container_ctx { + unsigned type; +#define XHCI_CTX_TYPE_DEVICE 0x1 +#define XHCI_CTX_TYPE_INPUT 0x2 + + int size; + + u8 *bytes; + dma_addr_t dma; +}; + +/** * struct xhci_slot_ctx * @dev_info: Route string, device speed, hub info, and last valid endpoint * @dev_info2: Max exit latency for device number, root hub port number @@ -1144,19 +1167,11 @@ */ static inline u64 xhci_read_64(__le64 __iomem *regs) { - __u32 __iomem *ptr = (__u32 __iomem *)regs; - u64 val_lo = readl(ptr); - u64 val_hi = readl(ptr + 1); - return val_lo + (val_hi << 32); + return lo_hi_readq(regs); } static inline void xhci_write_64(const u64 val, __le64 __iomem *regs) { - __u32 __iomem *ptr = (__u32 __iomem *)regs; - u32 val_lo = lower_32_bits(val); - u32 val_hi = upper_32_bits(val); - - writel(val_lo, ptr); - writel(val_hi, ptr + 1); + lo_hi_writeq(val, regs); } /* @@ -1183,17 +1198,6 @@ int cycle_state; }; -struct xhci_device_context { - struct xhci_slot_ctx slot; - struct xhci_ep_ctx ep[31]; -}; - -struct xhci_input_context { - struct xhci_input_control_ctx icc; - struct xhci_slot_ctx slot; - struct xhci_ep_ctx ep[31]; -}; - struct xhci_virtual_device { struct list_head list; struct usb_device *udev; @@ -1201,8 +1205,8 @@ size_t dma_size; int slot_id; struct xhci_ring *ep[USB_MAXENDPOINTS]; - struct xhci_input_context *in_ctx; - struct xhci_device_context *out_ctx; + struct xhci_container_ctx *in_ctx; + struct xhci_container_ctx *out_ctx; }; struct usb_root_hub_info { @@ -1232,7 +1236,6 @@ int num_sp; int page_size; int page_shift; - void *dma; size_t dma_size; __le64 *dcbaa; void *sp; diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig new file mode 100644 index 0000000..7d6c9da --- /dev/null +++ b/drivers/usb/misc/Kconfig @@ -0,0 +1,14 @@ +# +# USB Miscellaneous driver configuration +# +comment "USB Miscellaneous drivers" + +config USB_HUB_USB251XB + bool "USB251XB Hub Controller Configuration Driver" + depends on I2C + select NLS + help + This option enables support for configuration via SMBus of the + Microchip USB251x/xBi USB 2.0 Hub Controller series. Configuration + parameters may be set in devicetree or platform data. + Say Y or M here if you need to configure such a device via SMBus. diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile new file mode 100644 index 0000000..fb69c45 --- /dev/null +++ b/drivers/usb/misc/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the rest of the USB drivers +# (the ones that don't fit into any other categories) +# +obj-$(CONFIG_USB_HUB_USB251XB) += usb251xb.o diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c new file mode 100644 index 0000000..97f55ef --- /dev/null +++ b/drivers/usb/misc/usb251xb.c @@ -0,0 +1,683 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Microchip USB251xB USB 2.0 Hi-Speed Hub Controller + * Configuration via SMBus. + * + * Copyright (c) 2017 SKIDATA AG + * + * This work is based on the USB3503 driver by Dongjin Kim and + * a not-accepted patch by Fabien Lahoudere, see: + * https://patchwork.kernel.org/patch/9257715/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Internal Register Set Addresses & Default Values acc. to DS00001692C */ +#define USB251XB_ADDR_VENDOR_ID_LSB 0x00 +#define USB251XB_ADDR_VENDOR_ID_MSB 0x01 +#define USB251XB_DEF_VENDOR_ID 0x0424 + +#define USB251XB_ADDR_PRODUCT_ID_LSB 0x02 +#define USB251XB_ADDR_PRODUCT_ID_MSB 0x03 +#define USB251XB_DEF_PRODUCT_ID_12 0x2512 /* USB2512B/12Bi */ +#define USB251XB_DEF_PRODUCT_ID_13 0x2513 /* USB2513B/13Bi */ +#define USB251XB_DEF_PRODUCT_ID_14 0x2514 /* USB2514B/14Bi */ +#define USB251XB_DEF_PRODUCT_ID_17 0x2517 /* USB2517/17i */ + +#define USB251XB_ADDR_DEVICE_ID_LSB 0x04 +#define USB251XB_ADDR_DEVICE_ID_MSB 0x05 +#define USB251XB_DEF_DEVICE_ID 0x0BB3 + +#define USB251XB_ADDR_CONFIG_DATA_1 0x06 +#define USB251XB_DEF_CONFIG_DATA_1 0x9B +#define USB251XB_ADDR_CONFIG_DATA_2 0x07 +#define USB251XB_DEF_CONFIG_DATA_2 0x20 +#define USB251XB_ADDR_CONFIG_DATA_3 0x08 +#define USB251XB_DEF_CONFIG_DATA_3 0x02 + +#define USB251XB_ADDR_NON_REMOVABLE_DEVICES 0x09 +#define USB251XB_DEF_NON_REMOVABLE_DEVICES 0x00 + +#define USB251XB_ADDR_PORT_DISABLE_SELF 0x0A +#define USB251XB_DEF_PORT_DISABLE_SELF 0x00 +#define USB251XB_ADDR_PORT_DISABLE_BUS 0x0B +#define USB251XB_DEF_PORT_DISABLE_BUS 0x00 + +#define USB251XB_ADDR_MAX_POWER_SELF 0x0C +#define USB251XB_DEF_MAX_POWER_SELF 0x01 +#define USB251XB_ADDR_MAX_POWER_BUS 0x0D +#define USB251XB_DEF_MAX_POWER_BUS 0x32 + +#define USB251XB_ADDR_MAX_CURRENT_SELF 0x0E +#define USB251XB_DEF_MAX_CURRENT_SELF 0x01 +#define USB251XB_ADDR_MAX_CURRENT_BUS 0x0F +#define USB251XB_DEF_MAX_CURRENT_BUS 0x32 + +#define USB251XB_ADDR_POWER_ON_TIME 0x10 +#define USB251XB_DEF_POWER_ON_TIME 0x32 + +#define USB251XB_ADDR_LANGUAGE_ID_HIGH 0x11 +#define USB251XB_ADDR_LANGUAGE_ID_LOW 0x12 +#define USB251XB_DEF_LANGUAGE_ID 0x0000 + +#define USB251XB_STRING_BUFSIZE 62 +#define USB251XB_ADDR_MANUFACTURER_STRING_LEN 0x13 +#define USB251XB_ADDR_MANUFACTURER_STRING 0x16 +#define USB251XB_DEF_MANUFACTURER_STRING "Microchip" + +#define USB251XB_ADDR_PRODUCT_STRING_LEN 0x14 +#define USB251XB_ADDR_PRODUCT_STRING 0x54 +#define USB251XB_DEF_PRODUCT_STRING "USB251xB/xBi/7i" + +#define USB251XB_ADDR_SERIAL_STRING_LEN 0x15 +#define USB251XB_ADDR_SERIAL_STRING 0x92 +#define USB251XB_DEF_SERIAL_STRING "" + +#define USB251XB_ADDR_BATTERY_CHARGING_ENABLE 0xD0 +#define USB251XB_DEF_BATTERY_CHARGING_ENABLE 0x00 + +#define USB251XB_ADDR_BOOST_UP 0xF6 +#define USB251XB_DEF_BOOST_UP 0x00 +#define USB251XB_ADDR_BOOST_57 0xF7 +#define USB251XB_DEF_BOOST_57 0x00 +#define USB251XB_ADDR_BOOST_14 0xF8 +#define USB251XB_DEF_BOOST_14 0x00 + +#define USB251XB_ADDR_PORT_SWAP 0xFA +#define USB251XB_DEF_PORT_SWAP 0x00 + +#define USB251XB_ADDR_PORT_MAP_12 0xFB +#define USB251XB_DEF_PORT_MAP_12 0x00 +#define USB251XB_ADDR_PORT_MAP_34 0xFC +#define USB251XB_DEF_PORT_MAP_34 0x00 /* USB251{3B/i,4B/i,7/i} only */ +#define USB251XB_ADDR_PORT_MAP_56 0xFD +#define USB251XB_DEF_PORT_MAP_56 0x00 /* USB2517/i only */ +#define USB251XB_ADDR_PORT_MAP_7 0xFE +#define USB251XB_DEF_PORT_MAP_7 0x00 /* USB2517/i only */ + +#define USB251XB_ADDR_STATUS_COMMAND 0xFF +#define USB251XB_STATUS_COMMAND_SMBUS_DOWN 0x04 +#define USB251XB_STATUS_COMMAND_RESET 0x02 +#define USB251XB_STATUS_COMMAND_ATTACH 0x01 + +#define USB251XB_I2C_REG_SZ 0x100 +#define USB251XB_I2C_WRITE_SZ 0x10 + +#define DRIVER_NAME "usb251xb" +#define DRIVER_DESC "Microchip USB 2.0 Hi-Speed Hub Controller" + +struct usb251xb { + struct device_d *dev; + struct i2c_client *i2c; + u8 skip_config; + int gpio_reset; + u16 vendor_id; + u16 product_id; + u16 device_id; + u8 conf_data1; + u8 conf_data2; + u8 conf_data3; + u8 non_rem_dev; + u8 port_disable_sp; + u8 port_disable_bp; + u8 max_power_sp; + u8 max_power_bp; + u8 max_current_sp; + u8 max_current_bp; + u8 power_on_time; + u16 lang_id; + u8 manufacturer_len; + u8 product_len; + u8 serial_len; + char manufacturer[USB251XB_STRING_BUFSIZE]; + char product[USB251XB_STRING_BUFSIZE]; + char serial[USB251XB_STRING_BUFSIZE]; + u8 bat_charge_en; + u8 boost_up; + u8 boost_57; + u8 boost_14; + u8 port_swap; + u8 port_map12; + u8 port_map34; + u8 port_map56; + u8 port_map7; + u8 status; +}; + +struct usb251xb_data { + u16 product_id; + u8 port_cnt; + bool led_support; + bool bat_support; + char product_str[USB251XB_STRING_BUFSIZE / 2]; /* ASCII string */ +}; + +static const struct usb251xb_data usb2512b_data = { + .product_id = 0x2512, + .port_cnt = 2, + .led_support = false, + .bat_support = true, + .product_str = "USB2512B", +}; + +static const struct usb251xb_data usb2512bi_data = { + .product_id = 0x2512, + .port_cnt = 2, + .led_support = false, + .bat_support = true, + .product_str = "USB2512Bi", +}; + +static const struct usb251xb_data usb2513b_data = { + .product_id = 0x2513, + .port_cnt = 3, + .led_support = false, + .bat_support = true, + .product_str = "USB2513B", +}; + +static const struct usb251xb_data usb2513bi_data = { + .product_id = 0x2513, + .port_cnt = 3, + .led_support = false, + .bat_support = true, + .product_str = "USB2513Bi", +}; + +static const struct usb251xb_data usb2514b_data = { + .product_id = 0x2514, + .port_cnt = 4, + .led_support = false, + .bat_support = true, + .product_str = "USB2514B", +}; + +static const struct usb251xb_data usb2514bi_data = { + .product_id = 0x2514, + .port_cnt = 4, + .led_support = false, + .bat_support = true, + .product_str = "USB2514Bi", +}; + +static const struct usb251xb_data usb2517_data = { + .product_id = 0x2517, + .port_cnt = 7, + .led_support = true, + .bat_support = false, + .product_str = "USB2517", +}; + +static const struct usb251xb_data usb2517i_data = { + .product_id = 0x2517, + .port_cnt = 7, + .led_support = true, + .bat_support = false, + .product_str = "USB2517i", +}; + +static void usb251xb_reset(struct usb251xb *hub, int state) +{ + if (!gpio_is_valid(hub->gpio_reset)) + return; + + gpio_set_active(hub->gpio_reset, state); + + /* wait for hub recovery/stabilization */ + if (!state) + udelay(750); /* >=500us at power on */ + else + udelay(10); /* >=1us at power down */ +} + +static int usb251xb_connect(struct usb251xb *hub) +{ + struct device_d *dev = hub->dev; + int err, i; + char i2c_wb[USB251XB_I2C_REG_SZ]; + + memset(i2c_wb, 0, USB251XB_I2C_REG_SZ); + + if (hub->skip_config) { + dev_info(dev, "Skip hub configuration, only attach.\n"); + i2c_wb[0] = 0x01; + i2c_wb[1] = USB251XB_STATUS_COMMAND_ATTACH; + + usb251xb_reset(hub, 0); + + err = i2c_smbus_write_i2c_block_data(hub->i2c, + USB251XB_ADDR_STATUS_COMMAND, 2, i2c_wb); + if (err) { + dev_err(dev, "attaching hub failed: %d\n", err); + return err; + } + return 0; + } + + i2c_wb[USB251XB_ADDR_VENDOR_ID_MSB] = (hub->vendor_id >> 8) & 0xFF; + i2c_wb[USB251XB_ADDR_VENDOR_ID_LSB] = hub->vendor_id & 0xFF; + i2c_wb[USB251XB_ADDR_PRODUCT_ID_MSB] = (hub->product_id >> 8) & 0xFF; + i2c_wb[USB251XB_ADDR_PRODUCT_ID_LSB] = hub->product_id & 0xFF; + i2c_wb[USB251XB_ADDR_DEVICE_ID_MSB] = (hub->device_id >> 8) & 0xFF; + i2c_wb[USB251XB_ADDR_DEVICE_ID_LSB] = hub->device_id & 0xFF; + i2c_wb[USB251XB_ADDR_CONFIG_DATA_1] = hub->conf_data1; + i2c_wb[USB251XB_ADDR_CONFIG_DATA_2] = hub->conf_data2; + i2c_wb[USB251XB_ADDR_CONFIG_DATA_3] = hub->conf_data3; + i2c_wb[USB251XB_ADDR_NON_REMOVABLE_DEVICES] = hub->non_rem_dev; + i2c_wb[USB251XB_ADDR_PORT_DISABLE_SELF] = hub->port_disable_sp; + i2c_wb[USB251XB_ADDR_PORT_DISABLE_BUS] = hub->port_disable_bp; + i2c_wb[USB251XB_ADDR_MAX_POWER_SELF] = hub->max_power_sp; + i2c_wb[USB251XB_ADDR_MAX_POWER_BUS] = hub->max_power_bp; + i2c_wb[USB251XB_ADDR_MAX_CURRENT_SELF] = hub->max_current_sp; + i2c_wb[USB251XB_ADDR_MAX_CURRENT_BUS] = hub->max_current_bp; + i2c_wb[USB251XB_ADDR_POWER_ON_TIME] = hub->power_on_time; + i2c_wb[USB251XB_ADDR_LANGUAGE_ID_HIGH] = (hub->lang_id >> 8) & 0xFF; + i2c_wb[USB251XB_ADDR_LANGUAGE_ID_LOW] = hub->lang_id & 0xFF; + i2c_wb[USB251XB_ADDR_MANUFACTURER_STRING_LEN] = hub->manufacturer_len; + i2c_wb[USB251XB_ADDR_PRODUCT_STRING_LEN] = hub->product_len; + i2c_wb[USB251XB_ADDR_SERIAL_STRING_LEN] = hub->serial_len; + memcpy(&i2c_wb[USB251XB_ADDR_MANUFACTURER_STRING], hub->manufacturer, + USB251XB_STRING_BUFSIZE); + memcpy(&i2c_wb[USB251XB_ADDR_SERIAL_STRING], hub->serial, + USB251XB_STRING_BUFSIZE); + memcpy(&i2c_wb[USB251XB_ADDR_PRODUCT_STRING], hub->product, + USB251XB_STRING_BUFSIZE); + i2c_wb[USB251XB_ADDR_BATTERY_CHARGING_ENABLE] = hub->bat_charge_en; + i2c_wb[USB251XB_ADDR_BOOST_UP] = hub->boost_up; + i2c_wb[USB251XB_ADDR_BOOST_57] = hub->boost_57; + i2c_wb[USB251XB_ADDR_BOOST_14] = hub->boost_14; + i2c_wb[USB251XB_ADDR_PORT_SWAP] = hub->port_swap; + i2c_wb[USB251XB_ADDR_PORT_MAP_12] = hub->port_map12; + i2c_wb[USB251XB_ADDR_PORT_MAP_34] = hub->port_map34; + i2c_wb[USB251XB_ADDR_PORT_MAP_56] = hub->port_map56; + i2c_wb[USB251XB_ADDR_PORT_MAP_7] = hub->port_map7; + i2c_wb[USB251XB_ADDR_STATUS_COMMAND] = USB251XB_STATUS_COMMAND_ATTACH; + + usb251xb_reset(hub, 0); + + /* write registers */ + for (i = 0; i < (USB251XB_I2C_REG_SZ / USB251XB_I2C_WRITE_SZ); i++) { + int offset = i * USB251XB_I2C_WRITE_SZ; + char wbuf[USB251XB_I2C_WRITE_SZ + 1]; + + /* The first data byte transferred tells the hub how many data + * bytes will follow (byte count). + */ + wbuf[0] = USB251XB_I2C_WRITE_SZ; + memcpy(&wbuf[1], &i2c_wb[offset], USB251XB_I2C_WRITE_SZ); + + dev_dbg(dev, "writing %d byte block %d to 0x%02X\n", + USB251XB_I2C_WRITE_SZ, i, offset); + + err = i2c_smbus_write_i2c_block_data(hub->i2c, offset, + USB251XB_I2C_WRITE_SZ + 1, + wbuf); + if (err) + goto out_err; + } + + dev_info(dev, "Hub configuration was successful.\n"); + return 0; + +out_err: + dev_err(dev, "configuring block %d failed: %d\n", i, err); + return err; +} + +#ifdef CONFIG_OFDEVICE +static int usb251xb_get_ofdata(struct usb251xb *hub, + struct usb251xb_data *data) +{ + struct device_d *dev = hub->dev; + struct device_node *np = dev->device_node; + int len, err, i; + u32 port, property_u32 = 0; + const u32 *cproperty_u32; + const char *cproperty_char; + enum of_gpio_flags of_flags; + unsigned long flags = GPIOF_OUT_INIT_ACTIVE; + char str[USB251XB_STRING_BUFSIZE / 2]; + struct property *prop; + const __be32 *p; + + if (!np) { + dev_err(dev, "failed to get ofdata\n"); + return -ENODEV; + } + + if (of_get_property(np, "skip-config", NULL)) + hub->skip_config = 1; + else + hub->skip_config = 0; + + hub->gpio_reset = of_get_named_gpio_flags(np, "reset-gpios", 0, + &of_flags); + if (gpio_is_valid(hub->gpio_reset)) { + char *name; + int ret; + + if (of_flags & OF_GPIO_ACTIVE_LOW) + flags |= GPIOF_ACTIVE_LOW; + + name = basprintf("%s reset", dev_name(dev)); + ret = gpio_request_one(hub->gpio_reset, flags, name); + if (ret < 0) + return ret; + } else if (hub->gpio_reset == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else { + err = hub->gpio_reset; + dev_err(dev, "unable to request GPIO reset pin (%d)\n", err); + return err; + } + + if (of_property_read_u16_array(np, "vendor-id", &hub->vendor_id, 1)) + hub->vendor_id = USB251XB_DEF_VENDOR_ID; + + if (of_property_read_u16_array(np, "product-id", + &hub->product_id, 1)) + hub->product_id = data->product_id; + + if (of_property_read_u16_array(np, "device-id", &hub->device_id, 1)) + hub->device_id = USB251XB_DEF_DEVICE_ID; + + hub->conf_data1 = USB251XB_DEF_CONFIG_DATA_1; + if (of_get_property(np, "self-powered", NULL)) { + hub->conf_data1 |= BIT(7); + + /* Configure Over-Current sens when self-powered */ + hub->conf_data1 &= ~BIT(2); + if (of_get_property(np, "ganged-sensing", NULL)) + hub->conf_data1 &= ~BIT(1); + else if (of_get_property(np, "individual-sensing", NULL)) + hub->conf_data1 |= BIT(1); + } else if (of_get_property(np, "bus-powered", NULL)) { + hub->conf_data1 &= ~BIT(7); + + /* Disable Over-Current sense when bus-powered */ + hub->conf_data1 |= BIT(2); + } + + if (of_get_property(np, "disable-hi-speed", NULL)) + hub->conf_data1 |= BIT(5); + + if (of_get_property(np, "multi-tt", NULL)) + hub->conf_data1 |= BIT(4); + else if (of_get_property(np, "single-tt", NULL)) + hub->conf_data1 &= ~BIT(4); + + if (of_get_property(np, "disable-eop", NULL)) + hub->conf_data1 |= BIT(3); + + if (of_get_property(np, "individual-port-switching", NULL)) + hub->conf_data1 |= BIT(0); + else if (of_get_property(np, "ganged-port-switching", NULL)) + hub->conf_data1 &= ~BIT(0); + + hub->conf_data2 = USB251XB_DEF_CONFIG_DATA_2; + if (of_get_property(np, "dynamic-power-switching", NULL)) + hub->conf_data2 |= BIT(7); + + if (!of_property_read_u32(np, "oc-delay-us", &property_u32)) { + if (property_u32 == 100) { + /* 100 us*/ + hub->conf_data2 &= ~BIT(5); + hub->conf_data2 &= ~BIT(4); + } else if (property_u32 == 4000) { + /* 4 ms */ + hub->conf_data2 &= ~BIT(5); + hub->conf_data2 |= BIT(4); + } else if (property_u32 == 16000) { + /* 16 ms */ + hub->conf_data2 |= BIT(5); + hub->conf_data2 |= BIT(4); + } else { + /* 8 ms (DEFAULT) */ + hub->conf_data2 |= BIT(5); + hub->conf_data2 &= ~BIT(4); + } + } + + if (of_get_property(np, "compound-device", NULL)) + hub->conf_data2 |= BIT(3); + + hub->conf_data3 = USB251XB_DEF_CONFIG_DATA_3; + if (of_get_property(np, "port-mapping-mode", NULL)) + hub->conf_data3 |= BIT(3); + + if (data->led_support && of_get_property(np, "led-usb-mode", NULL)) + hub->conf_data3 &= ~BIT(1); + + if (of_get_property(np, "string-support", NULL)) + hub->conf_data3 |= BIT(0); + + hub->non_rem_dev = USB251XB_DEF_NON_REMOVABLE_DEVICES; + cproperty_u32 = of_get_property(np, "non-removable-ports", &len); + if (cproperty_u32 && (len / sizeof(u32)) > 0) { + for (i = 0; i < len / sizeof(u32); i++) { + u32 port = be32_to_cpu(cproperty_u32[i]); + + if ((port >= 1) && (port <= data->port_cnt)) + hub->non_rem_dev |= BIT(port); + else + dev_warn(dev, "NRD port %u doesn't exist\n", + port); + } + } + + hub->port_disable_sp = USB251XB_DEF_PORT_DISABLE_SELF; + cproperty_u32 = of_get_property(np, "sp-disabled-ports", &len); + if (cproperty_u32 && (len / sizeof(u32)) > 0) { + for (i = 0; i < len / sizeof(u32); i++) { + u32 port = be32_to_cpu(cproperty_u32[i]); + + if ((port >= 1) && (port <= data->port_cnt)) + hub->port_disable_sp |= BIT(port); + else + dev_warn(dev, "PDS port %u doesn't exist\n", + port); + } + } + + hub->port_disable_bp = USB251XB_DEF_PORT_DISABLE_BUS; + cproperty_u32 = of_get_property(np, "bp-disabled-ports", &len); + if (cproperty_u32 && (len / sizeof(u32)) > 0) { + for (i = 0; i < len / sizeof(u32); i++) { + u32 port = be32_to_cpu(cproperty_u32[i]); + + if ((port >= 1) && (port <= data->port_cnt)) + hub->port_disable_bp |= BIT(port); + else + dev_warn(dev, "PDB port %u doesn't exist\n", + port); + } + } + + hub->max_power_sp = USB251XB_DEF_MAX_POWER_SELF; + if (!of_property_read_u32(np, "sp-max-total-current-microamp", + &property_u32)) + hub->max_power_sp = min_t(u8, property_u32 / 2000, 50); + + hub->max_power_bp = USB251XB_DEF_MAX_POWER_BUS; + if (!of_property_read_u32(np, "bp-max-total-current-microamp", + &property_u32)) + hub->max_power_bp = min_t(u8, property_u32 / 2000, 255); + + hub->max_current_sp = USB251XB_DEF_MAX_CURRENT_SELF; + if (!of_property_read_u32(np, "sp-max-removable-current-microamp", + &property_u32)) + hub->max_current_sp = min_t(u8, property_u32 / 2000, 50); + + hub->max_current_bp = USB251XB_DEF_MAX_CURRENT_BUS; + if (!of_property_read_u32(np, "bp-max-removable-current-microamp", + &property_u32)) + hub->max_current_bp = min_t(u8, property_u32 / 2000, 255); + + hub->power_on_time = USB251XB_DEF_POWER_ON_TIME; + if (!of_property_read_u32(np, "power-on-time-ms", &property_u32)) + hub->power_on_time = min_t(u8, property_u32 / 2, 255); + + if (of_property_read_u16_array(np, "language-id", &hub->lang_id, 1)) + hub->lang_id = USB251XB_DEF_LANGUAGE_ID; + + cproperty_char = of_get_property(np, "manufacturer", NULL); + strlcpy(str, cproperty_char ? : USB251XB_DEF_MANUFACTURER_STRING, + sizeof(str)); + hub->manufacturer_len = strlen(str) & 0xFF; + memset(hub->manufacturer, 0, USB251XB_STRING_BUFSIZE); + len = min_t(size_t, USB251XB_STRING_BUFSIZE / 2, strlen(str)); + len = utf8s_to_utf16s(str, len, UTF16_LITTLE_ENDIAN, + (wchar_t *)hub->manufacturer, + USB251XB_STRING_BUFSIZE); + + cproperty_char = of_get_property(np, "product", NULL); + strlcpy(str, cproperty_char ? : data->product_str, sizeof(str)); + hub->product_len = strlen(str) & 0xFF; + memset(hub->product, 0, USB251XB_STRING_BUFSIZE); + len = min_t(size_t, USB251XB_STRING_BUFSIZE / 2, strlen(str)); + len = utf8s_to_utf16s(str, len, UTF16_LITTLE_ENDIAN, + (wchar_t *)hub->product, + USB251XB_STRING_BUFSIZE); + + cproperty_char = of_get_property(np, "serial", NULL); + strlcpy(str, cproperty_char ? : USB251XB_DEF_SERIAL_STRING, + sizeof(str)); + hub->serial_len = strlen(str) & 0xFF; + memset(hub->serial, 0, USB251XB_STRING_BUFSIZE); + len = min_t(size_t, USB251XB_STRING_BUFSIZE / 2, strlen(str)); + len = utf8s_to_utf16s(str, len, UTF16_LITTLE_ENDIAN, + (wchar_t *)hub->serial, + USB251XB_STRING_BUFSIZE); + + /* + * The datasheet documents the register as 'Port Swap' but the register + * controls the USB DP/DM signal swapping + */ + hub->port_swap = USB251XB_DEF_PORT_SWAP; + of_property_for_each_u32(np, "swap-dx-lanes", prop, p, port) { + if ((port >= 0) && (port <= data->port_cnt)) + hub->port_swap |= BIT(port); + } + + /* The following parameters are currently not exposed to devicetree, but + * may be as soon as needed. + */ + hub->bat_charge_en = USB251XB_DEF_BATTERY_CHARGING_ENABLE; + hub->boost_up = USB251XB_DEF_BOOST_UP; + hub->boost_57 = USB251XB_DEF_BOOST_57; + hub->boost_14 = USB251XB_DEF_BOOST_14; + hub->port_map12 = USB251XB_DEF_PORT_MAP_12; + hub->port_map34 = USB251XB_DEF_PORT_MAP_34; + hub->port_map56 = USB251XB_DEF_PORT_MAP_56; + hub->port_map7 = USB251XB_DEF_PORT_MAP_7; + + return 0; +} + +static const struct of_device_id usb251xb_of_match[] = { + { + .compatible = "microchip,usb2512b", + .data = &usb2512b_data, + }, { + .compatible = "microchip,usb2512bi", + .data = &usb2512bi_data, + }, { + .compatible = "microchip,usb2513b", + .data = &usb2513b_data, + }, { + .compatible = "microchip,usb2513bi", + .data = &usb2513bi_data, + }, { + .compatible = "microchip,usb2514b", + .data = &usb2514b_data, + }, { + .compatible = "microchip,usb2514bi", + .data = &usb2514bi_data, + }, { + .compatible = "microchip,usb2517", + .data = &usb2517_data, + }, { + .compatible = "microchip,usb2517i", + .data = &usb2517i_data, + }, { + /* sentinel */ + } +}; +#else /* CONFIG_OF */ +static int usb251xb_get_ofdata(struct usb251xb *hub, + struct usb251xb_data *data) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int usb251xb_probe(struct usb251xb *hub) +{ + struct device_d *dev = hub->dev; + struct device_node *np = dev->device_node; + const struct of_device_id *of_id = of_match_device(usb251xb_of_match, + dev); + int err; + + if (np) { + err = usb251xb_get_ofdata(hub, + (struct usb251xb_data *)of_id->data); + if (err) { + dev_err(dev, "failed to get ofdata: %d\n", err); + return err; + } + } + + err = usb251xb_connect(hub); + if (err) { + dev_err(dev, "Failed to connect hub (%d)\n", err); + return err; + } + + dev_info(dev, "Hub probed successfully\n"); + + return 0; +} + +static int usb251xb_i2c_probe(struct device_d *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct usb251xb *hub; + + hub = xzalloc(sizeof(struct usb251xb)); + + i2c_set_clientdata(i2c, hub); + hub->dev = &i2c->dev; + hub->i2c = i2c; + + return usb251xb_probe(hub); +} + +static const struct platform_device_id usb251xb_id[] = { + { "usb2512b", 0 }, + { "usb2512bi", 0 }, + { "usb2513b", 0 }, + { "usb2513bi", 0 }, + { "usb2514b", 0 }, + { "usb2514bi", 0 }, + { "usb2517", 0 }, + { "usb2517i", 0 }, + { /* sentinel */ } +}; + +static struct driver_d usb251xb_i2c_driver = { + .name = DRIVER_NAME, + .probe = usb251xb_i2c_probe, + .id_table = usb251xb_id, + .of_compatible = DRV_OF_COMPAT(usb251xb_of_match), +}; +device_i2c_driver(usb251xb_i2c_driver); diff --git a/include/linux/clk.h b/include/linux/clk.h index 2049857..ee888dc 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -25,6 +25,22 @@ */ struct clk; +/** + * struct clk_bulk_data - Data used for bulk clk operations. + * + * @id: clock consumer ID + * @clk: struct clk * to store the associated clock + * + * The CLK APIs provide a series of clk_bulk_() API calls as + * a convenience to consumers which require multiple clks. This + * structure is used to manage data for these calls. + */ +struct clk_bulk_data { + const char *id; + struct clk *clk; +}; + + #ifdef CONFIG_HAVE_CLK /** @@ -45,6 +61,29 @@ struct clk *clk_get(struct device_d *dev, const char *id); /** + * clk_bulk_get - lookup and obtain a number of references to clock producer. + * @dev: device for clock "consumer" + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table of consumer + * + * This helper function allows drivers to get several clk consumers in one + * operation. If any of the clk cannot be acquired then any clks + * that were obtained will be freed before returning to the caller. + * + * Returns 0 if all clocks specified in clk_bulk_data table are obtained + * successfully, or valid IS_ERR() condition containing errno. + * The implementation uses @dev and @clk_bulk_data.id to determine the + * clock consumer, and thereby the clock producer. + * The clock returned is stored in each @clk_bulk_data.clk field. + * + * Drivers must assume that the clock source is not enabled. + * + * clk_bulk_get should not be called from within interrupt context. + */ +int __must_check clk_bulk_get(struct device_d *dev, int num_clks, + struct clk_bulk_data *clks); + +/** * clk_enable - inform the system when the clock source should be running. * @clk: clock source * @@ -55,6 +94,18 @@ int clk_enable(struct clk *clk); /** + * clk_bulk_enable - inform the system when the set of clks should be running. + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table of consumer + * + * May be called from atomic contexts. + * + * Returns success (0) or negative errno. + */ +int __must_check clk_bulk_enable(int num_clks, + const struct clk_bulk_data *clks); + +/** * clk_disable - inform the system when the clock source is no longer required. * @clk: clock source * @@ -69,6 +120,24 @@ void clk_disable(struct clk *clk); /** + * clk_bulk_disable - inform the system when the set of clks is no + * longer required. + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table of consumer + * + * Inform the system that a set of clks is no longer required by + * a driver and may be shut down. + * + * May be called from atomic contexts. + * + * Implementation detail: if the set of clks is shared between + * multiple drivers, clk_bulk_enable() calls must be balanced by the + * same number of clk_bulk_disable() calls for the clock source to be + * disabled. + */ +void clk_bulk_disable(int num_clks, const struct clk_bulk_data *clks); + +/** * clk_get_rate - obtain the current clock rate (in Hz) for a clock source. * This is only valid once the clock source has been enabled. * @clk: clock source @@ -76,17 +145,17 @@ unsigned long clk_get_rate(struct clk *clk); /** - * clk_put - "free" the clock source - * @clk: clock source + * clk_bulk_put - "free" the clock source + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table of consumer * - * Note: drivers must ensure that all clk_enable calls made on this - * clock source are balanced by clk_disable calls prior to calling + * Note: drivers must ensure that all clk_bulk_enable calls made on this + * clock source are balanced by clk_bulk_disable calls prior to calling * this function. * - * clk_put should not be called from within interrupt context. + * clk_bulk_put should not be called from within interrupt context. */ -void clk_put(struct clk *clk); - +void clk_bulk_put(int num_clks, struct clk_bulk_data *clks); /* * The remaining APIs are optional for machine class support. @@ -166,24 +235,36 @@ return NULL; } +static inline int __must_check clk_bulk_get(struct device_d *dev, int num_clks, + struct clk_bulk_data *clks) +{ + return 0; +} + +static inline void clk_bulk_put(int num_clks, struct clk_bulk_data *clks) {} + static inline int clk_enable(struct clk *clk) { return 0; } +static inline int __must_check clk_bulk_enable(int num_clks, struct clk_bulk_data *clks) +{ + return 0; +} + static inline void clk_disable(struct clk *clk) { } +static inline void clk_bulk_disable(int num_clks, + struct clk_bulk_data *clks) {} + static inline unsigned long clk_get_rate(struct clk *clk) { return 0; } -static inline void clk_put(struct clk *clk) -{ -} - static inline long clk_round_rate(struct clk *clk, unsigned long rate) { return 0; @@ -195,6 +276,10 @@ } #endif +static inline void clk_put(struct clk *clk) +{ +} + #ifdef CONFIG_COMMON_CLK #include diff --git a/include/linux/nls.h b/include/linux/nls.h new file mode 100644 index 0000000..62fb7b5 --- /dev/null +++ b/include/linux/nls.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_NLS_H +#define _LINUX_NLS_H + +/* Unicode has changed over the years. Unicode code points no longer + * fit into 16 bits; as of Unicode 5 valid code points range from 0 + * to 0x10ffff (17 planes, where each plane holds 65536 code points). + * + * The original decision to represent Unicode characters as 16-bit + * wchar_t values is now outdated. But plane 0 still includes the + * most commonly used characters, so we will retain it. The newer + * 32-bit unicode_t type can be used when it is necessary to + * represent the full Unicode character set. + */ + +/* Plane-0 Unicode character */ +typedef u16 wchar_t; +#define MAX_WCHAR_T 0xffff + +/* Arbitrary Unicode character */ +typedef u32 unicode_t; + +/* this value hold the maximum octet of charset */ +#define NLS_MAX_CHARSET_SIZE 6 /* for UTF-8 */ + +/* Byte order for UTF-16 strings */ +enum utf16_endian { + UTF16_HOST_ENDIAN, + UTF16_LITTLE_ENDIAN, + UTF16_BIG_ENDIAN +}; + +/* nls_base.c */ + +extern int utf8_to_utf32(const u8 *s, int len, unicode_t *pu); +extern int utf8s_to_utf16s(const u8 *s, int len, + enum utf16_endian endian, wchar_t *pwcs, int maxlen); + +#endif /* _LINUX_NLS_H */ + diff --git a/include/usb/ch9.h b/include/usb/ch9.h index b44d41e..89d83e0 100644 --- a/include/usb/ch9.h +++ b/include/usb/ch9.h @@ -913,6 +913,7 @@ USB_SPEED_HIGH, /* usb 2.0 */ USB_SPEED_WIRELESS, /* wireless (usb 2.5) */ USB_SPEED_SUPER, /* usb 3.0 */ + USB_SPEED_SUPER_PLUS, /* usb 3.1 */ }; diff --git a/lib/Kconfig b/lib/Kconfig index e048ade..44f30d8 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -148,4 +148,7 @@ config GENERIC_LIB_MULDI3 bool +config NLS + bool "Native language support" + endmenu diff --git a/lib/Makefile b/lib/Makefile index e72aa66..763516d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -67,6 +67,7 @@ obj-y += clz_ctz.o obj-$(CONFIG_CRC_CCITT) += crc-ccitt.o obj-$(CONFIG_CRC8) += crc8.o +obj-$(CONFIG_NLS) += nls_base.o # GCC library routines obj-$(CONFIG_GENERIC_LIB_ASHLDI3) += ashldi3.o diff --git a/lib/nls_base.c b/lib/nls_base.c new file mode 100644 index 0000000..cd6a5ff --- /dev/null +++ b/lib/nls_base.c @@ -0,0 +1,131 @@ +/* + * linux/fs/nls/nls_base.c + * + * Native language support--charsets and unicode translations. + * By Gordon Chaffee 1996, 1997 + * + * Unicode based case conversion 1999 by Wolfram Pienkoss + * + */ +#include +#include + +/* + * Sample implementation from Unicode home page. + * http://www.stonehand.com/unicode/standard/fss-utf.html + */ +struct utf8_table { + int cmask; + int cval; + int shift; + long lmask; + long lval; +}; + +static const struct utf8_table utf8_table[] = +{ + {0x80, 0x00, 0*6, 0x7F, 0, /* 1 byte sequence */}, + {0xE0, 0xC0, 1*6, 0x7FF, 0x80, /* 2 byte sequence */}, + {0xF0, 0xE0, 2*6, 0xFFFF, 0x800, /* 3 byte sequence */}, + {0xF8, 0xF0, 3*6, 0x1FFFFF, 0x10000, /* 4 byte sequence */}, + {0xFC, 0xF8, 4*6, 0x3FFFFFF, 0x200000, /* 5 byte sequence */}, + {0xFE, 0xFC, 5*6, 0x7FFFFFFF, 0x4000000, /* 6 byte sequence */}, + {0, /* end of table */} +}; + +#define UNICODE_MAX 0x0010ffff +#define PLANE_SIZE 0x00010000 + +#define SURROGATE_MASK 0xfffff800 +#define SURROGATE_PAIR 0x0000d800 +#define SURROGATE_LOW 0x00000400 +#define SURROGATE_BITS 0x000003ff + +int utf8_to_utf32(const u8 *s, int inlen, unicode_t *pu) +{ + unsigned long l; + int c0, c, nc; + const struct utf8_table *t; + + nc = 0; + c0 = *s; + l = c0; + for (t = utf8_table; t->cmask; t++) { + nc++; + if ((c0 & t->cmask) == t->cval) { + l &= t->lmask; + if (l < t->lval || l > UNICODE_MAX || + (l & SURROGATE_MASK) == SURROGATE_PAIR) + return -1; + *pu = (unicode_t) l; + return nc; + } + if (inlen <= nc) + return -1; + s++; + c = (*s ^ 0x80) & 0xFF; + if (c & 0xC0) + return -1; + l = (l << 6) | c; + } + return -1; +} +EXPORT_SYMBOL(utf8_to_utf32); + +static inline void put_utf16(wchar_t *s, unsigned c, enum utf16_endian endian) +{ + switch (endian) { + default: + *s = (wchar_t) c; + break; + case UTF16_LITTLE_ENDIAN: + *s = __cpu_to_le16(c); + break; + case UTF16_BIG_ENDIAN: + *s = __cpu_to_be16(c); + break; + } +} + +int utf8s_to_utf16s(const u8 *s, int inlen, enum utf16_endian endian, + wchar_t *pwcs, int maxout) +{ + u16 *op; + int size; + unicode_t u; + + op = pwcs; + while (inlen > 0 && maxout > 0 && *s) { + if (*s & 0x80) { + size = utf8_to_utf32(s, inlen, &u); + if (size < 0) + return -EINVAL; + s += size; + inlen -= size; + + if (u >= PLANE_SIZE) { + if (maxout < 2) + break; + u -= PLANE_SIZE; + put_utf16(op++, SURROGATE_PAIR | + ((u >> 10) & SURROGATE_BITS), + endian); + put_utf16(op++, SURROGATE_PAIR | + SURROGATE_LOW | + (u & SURROGATE_BITS), + endian); + maxout -= 2; + } else { + put_utf16(op++, u, endian); + maxout--; + } + } else { + put_utf16(op++, *s++, endian); + inlen--; + maxout--; + } + } + return op - pwcs; +} +EXPORT_SYMBOL(utf8s_to_utf16s); +