diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 93707e05fb..62aa65bf0c 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 6e3e024e97..0dd1ba87cd 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/dwc3-layerscape.c b/drivers/usb/dwc3/dwc3-layerscape.c new file mode 100644 index 0000000000..79cf71f7a8 --- /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 + * + * Based on dwc3-generic.c. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "gadget.h" +#include + +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, +};