diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/common/common.c | 4 | ||||
-rw-r--r-- | drivers/usb/dwc3/Kconfig | 10 | ||||
-rw-r--r-- | drivers/usb/dwc3/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.c | 95 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.h | 22 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-layerscape.c | 222 | ||||
-rw-r--r-- | drivers/usb/host/xhci-fsl.c | 1 |
7 files changed, 353 insertions, 2 deletions
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index 43564c9fbaf..ee0c064f1ff 100644 --- a/drivers/usb/common/common.c +++ b/drivers/usb/common/common.c @@ -29,7 +29,7 @@ enum usb_dr_mode usb_get_dr_mode(ofnode node) dr_mode = ofnode_read_string(node, "dr_mode"); if (!dr_mode) { - pr_err("usb dr_mode not found\n"); + pr_debug("usb dr_mode not found\n"); return USB_DR_MODE_UNKNOWN; } @@ -64,7 +64,7 @@ enum usb_device_speed usb_get_maximum_speed(ofnode node) max_speed = ofnode_read_string(node, "maximum-speed"); if (!max_speed) { - pr_err("usb maximum-speed not found\n"); + pr_debug("usb maximum-speed not found\n"); return USB_SPEED_UNKNOWN; } diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 93707e05fb1..62aa65bf0cd 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -53,6 +53,16 @@ config USB_DWC3_UNIPHIER Support of USB2/3 functionality in Socionext UniPhier platforms. Say 'Y' here if you have one such device. +config USB_DWC3_LAYERSCAPE + bool "Freescale Layerscape platform support" + depends on DM_USB && USB_DWC3 + depends on !USB_XHCI_FSL + help + Select this for Freescale Layerscape Platforms. + + Host and Peripheral operation modes are supported. OTG is not + supported. + menu "PHY Subsystem" config USB_DWC3_PHY_OMAP diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 6e3e024e97e..0dd1ba87cd9 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -11,5 +11,6 @@ obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o obj-$(CONFIG_USB_DWC3_MESON_GXL) += dwc3-meson-gxl.o obj-$(CONFIG_USB_DWC3_GENERIC) += dwc3-generic.o obj-$(CONFIG_USB_DWC3_UNIPHIER) += dwc3-uniphier.o +obj-$(CONFIG_USB_DWC3_LAYERSCAPE) += dwc3-layerscape.o obj-$(CONFIG_USB_DWC3_PHY_OMAP) += ti_usb_phy.o obj-$(CONFIG_USB_DWC3_PHY_SAMSUNG) += samsung_usb_phy.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index dfd7cf683f7..ce1c0e88c2a 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -93,6 +93,27 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) return 0; } +/* + * dwc3_frame_length_adjustment - Adjusts frame length if required + * @dwc3: Pointer to our controller context structure + * @fladj: Value of GFLADJ_30MHZ to adjust frame length + */ +static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj) +{ + u32 reg; + + if (dwc->revision < DWC3_REVISION_250A) + return; + + if (fladj == 0) + return; + + reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); + reg &= ~DWC3_GFLADJ_30MHZ_MASK; + reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | fladj; + dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); +} + /** * dwc3_free_one_event_buffer - Frees one event buffer * @dwc: Pointer to our controller context structure @@ -441,6 +462,53 @@ static void dwc3_phy_setup(struct dwc3 *dwc) mdelay(100); } +/* set global incr burst type configuration registers */ +static void dwc3_set_incr_burst_type(struct dwc3 *dwc) +{ + struct udevice *dev = dwc->dev; + u32 cfg; + + if (!dwc->incrx_size) + return; + + cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0); + + /* Enable Undefined Length INCR Burst and Enable INCRx Burst */ + cfg &= ~DWC3_GSBUSCFG0_INCRBRST_MASK; + if (dwc->incrx_mode) + cfg |= DWC3_GSBUSCFG0_INCRBRSTENA; + switch (dwc->incrx_size) { + case 256: + cfg |= DWC3_GSBUSCFG0_INCR256BRSTENA; + break; + case 128: + cfg |= DWC3_GSBUSCFG0_INCR128BRSTENA; + break; + case 64: + cfg |= DWC3_GSBUSCFG0_INCR64BRSTENA; + break; + case 32: + cfg |= DWC3_GSBUSCFG0_INCR32BRSTENA; + break; + case 16: + cfg |= DWC3_GSBUSCFG0_INCR16BRSTENA; + break; + case 8: + cfg |= DWC3_GSBUSCFG0_INCR8BRSTENA; + break; + case 4: + cfg |= DWC3_GSBUSCFG0_INCR4BRSTENA; + break; + case 1: + break; + default: + dev_err(dev, "Invalid property\n"); + break; + } + + dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg); +} + /** * dwc3_core_init - Low-level initialization of DWC3 Core * @dwc: Pointer to our controller context structure @@ -569,6 +637,11 @@ static int dwc3_core_init(struct dwc3 *dwc) if (ret) goto err1; + /* Adjust Frame Length */ + dwc3_frame_length_adjustment(dwc, dwc->fladj); + + dwc3_set_incr_burst_type(dwc); + return 0; err1: @@ -892,6 +965,8 @@ void dwc3_of_parse(struct dwc3 *dwc) u8 lpm_nyet_threshold; u8 tx_de_emphasis; u8 hird_threshold; + u32 val; + int i; /* default to highest possible threshold */ lpm_nyet_threshold = 0xff; @@ -958,6 +1033,26 @@ void dwc3_of_parse(struct dwc3 *dwc) dwc->hird_threshold = hird_threshold | (dwc->is_utmi_l1_suspend << 4); + + dev_read_u32(dev, "snps,quirk-frame-length-adjustment", &dwc->fladj); + + /* + * Handle property "snps,incr-burst-type-adjustment". + * Get the number of value from this property: + * result <= 0, means this property is not supported. + * result = 1, means INCRx burst mode supported. + * result > 1, means undefined length burst mode supported. + */ + dwc->incrx_mode = INCRX_BURST_MODE; + dwc->incrx_size = 0; + for (i = 0; i < 8; i++) { + if (dev_read_u32_index(dev, "snps,incr-burst-type-adjustment", + i, &val)) + break; + + dwc->incrx_mode = INCRX_UNDEF_LENGTH_BURST_MODE; + dwc->incrx_size = max(dwc->incrx_size, val); + } } int dwc3_init(struct dwc3 *dwc) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 1502cb859a5..d7cce3a861a 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -115,6 +115,7 @@ #define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10)) #define DWC3_GHWPARAMS8 0xc600 +#define DWC3_GFLADJ 0xc630 /* Device Registers */ #define DWC3_DCFG 0xc700 @@ -138,6 +139,17 @@ /* 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 Configuration Register */ #define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19) #define DWC3_GCTL_U2RSTECN (1 << 16) @@ -233,6 +245,10 @@ /* Global HWPARAMS6 Register */ #define DWC3_GHWPARAMS6_EN_FPGA (1 << 7) +/* Global Frame Length Adjustment Register */ +#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7) +#define DWC3_GFLADJ_30MHZ_MASK 0x3f + /* Device Configuration Register */ #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) @@ -812,6 +828,9 @@ struct dwc3 { u8 test_mode_nr; u8 lpm_nyet_threshold; u8 hird_threshold; + u32 fladj; + u8 incrx_mode; + u32 incrx_size; unsigned delayed_status:1; unsigned ep0_bounced:1; @@ -849,6 +868,9 @@ struct dwc3 { struct list_head list; }; +#define INCRX_BURST_MODE 0 +#define INCRX_UNDEF_LENGTH_BURST_MODE 1 + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/drivers/usb/dwc3/dwc3-layerscape.c b/drivers/usb/dwc3/dwc3-layerscape.c new file mode 100644 index 00000000000..79cf71f7a85 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-layerscape.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Layerscape DWC3 Glue layer + * + * Copyright (C) 2021 Michael Walle <michael@walle.cc> + * + * Based on dwc3-generic.c. + */ + +#include <common.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dwc3-uboot.h> +#include <linux/usb/gadget.h> +#include <usb.h> +#include "core.h" +#include "gadget.h" +#include <usb/xhci.h> + +struct dwc3_layerscape_plat { + fdt_addr_t base; + u32 maximum_speed; + enum usb_dr_mode dr_mode; +}; + +struct dwc3_layerscape_priv { + void *base; + struct dwc3 dwc3; + struct phy_bulk phys; +}; + +struct dwc3_layerscape_host_priv { + struct xhci_ctrl xhci_ctrl; + struct dwc3_layerscape_priv gen_priv; +}; + +static int dwc3_layerscape_probe(struct udevice *dev, + struct dwc3_layerscape_priv *priv) +{ + int rc; + struct dwc3_layerscape_plat *plat = dev_get_plat(dev); + struct dwc3 *dwc3 = &priv->dwc3; + + dwc3->dev = dev; + dwc3->maximum_speed = plat->maximum_speed; + dwc3->dr_mode = plat->dr_mode; + if (CONFIG_IS_ENABLED(OF_CONTROL)) + dwc3_of_parse(dwc3); + + rc = dwc3_setup_phy(dev, &priv->phys); + if (rc && rc != -ENOTSUPP) + return rc; + + priv->base = map_physmem(plat->base, DWC3_OTG_REGS_END, MAP_NOCACHE); + dwc3->regs = priv->base + DWC3_GLOBALS_REGS_START; + + rc = dwc3_init(dwc3); + if (rc) { + unmap_physmem(priv->base, MAP_NOCACHE); + return rc; + } + + return 0; +} + +static int dwc3_layerscape_remove(struct udevice *dev, + struct dwc3_layerscape_priv *priv) +{ + struct dwc3 *dwc3 = &priv->dwc3; + + dwc3_remove(dwc3); + dwc3_shutdown_phy(dev, &priv->phys); + unmap_physmem(dwc3->regs, MAP_NOCACHE); + + return 0; +} + +static int dwc3_layerscape_of_to_plat(struct udevice *dev) +{ + struct dwc3_layerscape_plat *plat = dev_get_plat(dev); + ofnode node = dev_ofnode(dev); + + plat->base = dev_read_addr(dev); + + plat->maximum_speed = usb_get_maximum_speed(node); + if (plat->maximum_speed == USB_SPEED_UNKNOWN) { + dev_dbg(dev, "No USB maximum speed specified. Using super speed\n"); + plat->maximum_speed = USB_SPEED_SUPER; + } + + plat->dr_mode = usb_get_dr_mode(node); + if (plat->dr_mode == USB_DR_MODE_UNKNOWN) { + dev_err(dev, "Invalid usb mode setup\n"); + return -ENODEV; + } + + return 0; +} + +#if CONFIG_IS_ENABLED(DM_USB_GADGET) +int dm_usb_gadget_handle_interrupts(struct udevice *dev) +{ + struct dwc3_layerscape_priv *priv = dev_get_priv(dev); + + dwc3_gadget_uboot_handle_interrupt(&priv->dwc3); + + return 0; +} + +static int dwc3_layerscape_peripheral_probe(struct udevice *dev) +{ + struct dwc3_layerscape_priv *priv = dev_get_priv(dev); + + return dwc3_layerscape_probe(dev, priv); +} + +static int dwc3_layerscape_peripheral_remove(struct udevice *dev) +{ + struct dwc3_layerscape_priv *priv = dev_get_priv(dev); + + return dwc3_layerscape_remove(dev, priv); +} + +U_BOOT_DRIVER(dwc3_layerscape_peripheral) = { + .name = "dwc3-layerscape-peripheral", + .id = UCLASS_USB_GADGET_GENERIC, + .of_to_plat = dwc3_layerscape_of_to_plat, + .probe = dwc3_layerscape_peripheral_probe, + .remove = dwc3_layerscape_peripheral_remove, + .priv_auto = sizeof(struct dwc3_layerscape_priv), + .plat_auto = sizeof(struct dwc3_layerscape_plat), +}; +#endif + +#if defined(CONFIG_SPL_USB_HOST_SUPPORT) || \ + !defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_HOST) +static int dwc3_layerscape_host_probe(struct udevice *dev) +{ + struct xhci_hcor *hcor; + struct xhci_hccr *hccr; + struct dwc3_layerscape_host_priv *priv = dev_get_priv(dev); + int rc; + + rc = dwc3_layerscape_probe(dev, &priv->gen_priv); + if (rc) + return rc; + + hccr = priv->gen_priv.base; + hcor = priv->gen_priv.base + HC_LENGTH(xhci_readl(&hccr->cr_capbase)); + + return xhci_register(dev, hccr, hcor); +} + +static int dwc3_layerscape_host_remove(struct udevice *dev) +{ + struct dwc3_layerscape_host_priv *priv = dev_get_priv(dev); + int rc; + + rc = xhci_deregister(dev); + if (rc) + return rc; + + return dwc3_layerscape_remove(dev, &priv->gen_priv); +} + +U_BOOT_DRIVER(dwc3_layerscape_host) = { + .name = "dwc3-layerscape-host", + .id = UCLASS_USB, + .of_to_plat = dwc3_layerscape_of_to_plat, + .probe = dwc3_layerscape_host_probe, + .remove = dwc3_layerscape_host_remove, + .priv_auto = sizeof(struct dwc3_layerscape_host_priv), + .plat_auto = sizeof(struct dwc3_layerscape_plat), + .ops = &xhci_usb_ops, + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; +#endif + +static int dwc3_layerscape_bind(struct udevice *dev) +{ + ofnode node = dev_ofnode(dev); + const char *name = ofnode_get_name(node); + enum usb_dr_mode dr_mode; + char *driver; + + dr_mode = usb_get_dr_mode(node); + + switch (dr_mode) { +#if CONFIG_IS_ENABLED(DM_USB_GADGET) + case USB_DR_MODE_PERIPHERAL: + dev_dbg(dev, "Using peripheral mode\n"); + driver = "dwc3-layerscape-peripheral"; + break; +#endif +#if defined(CONFIG_SPL_USB_HOST_SUPPORT) || !defined(CONFIG_SPL_BUILD) + case USB_DR_MODE_HOST: + dev_dbg(dev, "Using host mode\n"); + driver = "dwc3-layerscape-host"; + break; +#endif + default: + dev_dbg(dev, "Unsupported dr_mode\n"); + return -ENODEV; + }; + + return device_bind_driver_to_node(dev, driver, name, node, NULL); +} + +static const struct udevice_id dwc3_layerscape_ids[] = { + { .compatible = "fsl,layerscape-dwc3" }, + { .compatible = "fsl,ls1028a-dwc3" }, + { } +}; + +U_BOOT_DRIVER(dwc3_layerscape_wrapper) = { + .name = "dwc3-layerscape-wrapper", + .id = UCLASS_NOP, + .of_match = dwc3_layerscape_ids, + .bind = dwc3_layerscape_bind, +}; diff --git a/drivers/usb/host/xhci-fsl.c b/drivers/usb/host/xhci-fsl.c index f062f12ade6..80871908dc1 100644 --- a/drivers/usb/host/xhci-fsl.c +++ b/drivers/usb/host/xhci-fsl.c @@ -159,6 +159,7 @@ static int xhci_fsl_remove(struct udevice *dev) static const struct udevice_id xhci_usb_ids[] = { { .compatible = "fsl,layerscape-dwc3", }, + { .compatible = "fsl,ls1028a-dwc3", }, { } }; |