diff options
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r-- | drivers/usb/dwc3/Kconfig | 7 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.c | 89 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-generic.c | 293 | ||||
-rw-r--r-- | drivers/usb/dwc3/ep0.c | 2 |
4 files changed, 338 insertions, 53 deletions
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 943b7630eb..bbd8105c06 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -38,10 +38,11 @@ config USB_DWC3_OMAP Say 'Y' here if you have one such device config USB_DWC3_GENERIC - bool "Xilinx ZynqMP and similar Platforms" - depends on DM_USB && USB_DWC3 + bool "Generic implementation of a DWC3 wrapper (aka dwc3 glue)" + depends on DM_USB && USB_DWC3 && MISC help - Some platforms can reuse this DWC3 generic implementation. + Select this for Xilinx ZynqMP and similar Platforms. + This wrapper supports Host and Peripheral operation modes. config USB_DWC3_UNIPHIER bool "DesignWare USB3 Host Support on UniPhier Platforms" diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index f1ca6191ce..56e2a046bf 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -19,7 +19,7 @@ #include <asm/dma-mapping.h> #include <linux/ioport.h> #include <dm.h> - +#include <generic-phy.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> @@ -789,8 +789,92 @@ MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver"); -#if CONFIG_IS_ENABLED(DM_USB) +#if CONFIG_IS_ENABLED(PHY) && CONFIG_IS_ENABLED(DM_USB) +int dwc3_setup_phy(struct udevice *dev, struct phy **array, int *num_phys) +{ + int i, ret, count; + struct phy *usb_phys; + + /* Return if no phy declared */ + if (!dev_read_prop(dev, "phys", NULL)) + return 0; + count = dev_count_phandle_with_args(dev, "phys", "#phy-cells"); + if (count <= 0) + return count; + + usb_phys = devm_kcalloc(dev, count, sizeof(struct phy), + GFP_KERNEL); + if (!usb_phys) + return -ENOMEM; + + for (i = 0; i < count; i++) { + ret = generic_phy_get_by_index(dev, i, &usb_phys[i]); + if (ret && ret != -ENOENT) { + pr_err("Failed to get USB PHY%d for %s\n", + i, dev->name); + return ret; + } + } + + for (i = 0; i < count; i++) { + ret = generic_phy_init(&usb_phys[i]); + if (ret) { + pr_err("Can't init USB PHY%d for %s\n", + i, dev->name); + goto phys_init_err; + } + } + + for (i = 0; i < count; i++) { + ret = generic_phy_power_on(&usb_phys[i]); + if (ret) { + pr_err("Can't power USB PHY%d for %s\n", + i, dev->name); + goto phys_poweron_err; + } + } + + *array = usb_phys; + *num_phys = count; + return 0; + +phys_poweron_err: + for (i = count - 1; i >= 0; i--) + generic_phy_power_off(&usb_phys[i]); + for (i = 0; i < count; i++) + generic_phy_exit(&usb_phys[i]); + + return ret; + +phys_init_err: + for (; i >= 0; i--) + generic_phy_exit(&usb_phys[i]); + + return ret; +} + +int dwc3_shutdown_phy(struct udevice *dev, struct phy *usb_phys, int num_phys) +{ + int i, ret; + + for (i = 0; i < num_phys; i++) { + if (!generic_phy_valid(&usb_phys[i])) + continue; + + ret = generic_phy_power_off(&usb_phys[i]); + ret |= generic_phy_exit(&usb_phys[i]); + if (ret) { + pr_err("Can't shutdown USB PHY%d for %s\n", + i, dev->name); + } + } + + return 0; +} +#endif + +#if CONFIG_IS_ENABLED(DM_USB_GADGET) int dwc3_init(struct dwc3 *dwc) { int ret; @@ -841,5 +925,4 @@ void dwc3_remove(struct dwc3 *dwc) dwc3_core_exit(dwc); kfree(dwc->mem); } - #endif diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c index 56c9fd657f..bc6bba198e 100644 --- a/drivers/usb/dwc3/dwc3-generic.c +++ b/drivers/usb/dwc3/dwc3-generic.c @@ -8,72 +8,89 @@ */ #include <common.h> +#include <asm-generic/io.h> #include <dm.h> #include <dm/device-internal.h> #include <dm/lists.h> -#include <linux/usb/otg.h> -#include <linux/compat.h> +#include <dwc3-uboot.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <malloc.h> #include <usb.h> #include "core.h" #include "gadget.h" -#include "linux-compat.h" +#include <reset.h> +#include <clk.h> -DECLARE_GLOBAL_DATA_PTR; +#if CONFIG_IS_ENABLED(DM_USB_GADGET) +struct dwc3_generic_peripheral { + struct dwc3 dwc3; + struct phy *phys; + int num_phys; + fdt_addr_t base; +}; -int usb_gadget_handle_interrupts(int index) +int dm_usb_gadget_handle_interrupts(struct udevice *dev) { - struct dwc3 *priv; - struct udevice *dev; - int ret; - - ret = uclass_first_device(UCLASS_USB_DEV_GENERIC, &dev); - if (!dev || ret) { - pr_err("No USB device found\n"); - return -ENODEV; - } - - priv = dev_get_priv(dev); + struct dwc3_generic_peripheral *priv = dev_get_priv(dev); + struct dwc3 *dwc3 = &priv->dwc3; - dwc3_gadget_uboot_handle_interrupt(priv); + dwc3_gadget_uboot_handle_interrupt(dwc3); return 0; } static int dwc3_generic_peripheral_probe(struct udevice *dev) { - struct dwc3 *priv = dev_get_priv(dev); + int rc; + struct dwc3_generic_peripheral *priv = dev_get_priv(dev); + struct dwc3 *dwc3 = &priv->dwc3; - return dwc3_init(priv); + rc = dwc3_setup_phy(dev, &priv->phys, &priv->num_phys); + if (rc) + return rc; + + dwc3->regs = map_physmem(priv->base, DWC3_OTG_REGS_END, MAP_NOCACHE); + dwc3->regs += DWC3_GLOBALS_REGS_START; + dwc3->dev = dev; + + rc = dwc3_init(dwc3); + if (rc) { + unmap_physmem(dwc3->regs, MAP_NOCACHE); + return rc; + } + + return 0; } static int dwc3_generic_peripheral_remove(struct udevice *dev) { - struct dwc3 *priv = dev_get_priv(dev); + struct dwc3_generic_peripheral *priv = dev_get_priv(dev); + struct dwc3 *dwc3 = &priv->dwc3; - dwc3_remove(priv); + dwc3_remove(dwc3); + dwc3_shutdown_phy(dev, priv->phys, priv->num_phys); + unmap_physmem(dwc3->regs, MAP_NOCACHE); return 0; } static int dwc3_generic_peripheral_ofdata_to_platdata(struct udevice *dev) { - struct dwc3 *priv = dev_get_priv(dev); + struct dwc3_generic_peripheral *priv = dev_get_priv(dev); + struct dwc3 *dwc3 = &priv->dwc3; int node = dev_of_offset(dev); - priv->regs = (void *)devfdt_get_addr(dev); - priv->regs += DWC3_GLOBALS_REGS_START; + priv->base = devfdt_get_addr(dev); - priv->maximum_speed = usb_get_maximum_speed(node); - if (priv->maximum_speed == USB_SPEED_UNKNOWN) { + dwc3->maximum_speed = usb_get_maximum_speed(node); + if (dwc3->maximum_speed == USB_SPEED_UNKNOWN) { pr_err("Invalid usb maximum speed\n"); return -ENODEV; } - priv->dr_mode = usb_get_dr_mode(node); - if (priv->dr_mode == USB_DR_MODE_UNKNOWN) { + dwc3->dr_mode = usb_get_dr_mode(node); + if (dwc3->dr_mode == USB_DR_MODE_UNKNOWN) { pr_err("Invalid usb mode setup\n"); return -ENODEV; } @@ -81,24 +98,112 @@ static int dwc3_generic_peripheral_ofdata_to_platdata(struct udevice *dev) return 0; } -static int dwc3_generic_peripheral_bind(struct udevice *dev) -{ - return device_probe(dev); -} - U_BOOT_DRIVER(dwc3_generic_peripheral) = { .name = "dwc3-generic-peripheral", - .id = UCLASS_USB_DEV_GENERIC, + .id = UCLASS_USB_GADGET_GENERIC, .ofdata_to_platdata = dwc3_generic_peripheral_ofdata_to_platdata, .probe = dwc3_generic_peripheral_probe, .remove = dwc3_generic_peripheral_remove, - .bind = dwc3_generic_peripheral_bind, - .platdata_auto_alloc_size = sizeof(struct usb_platdata), - .priv_auto_alloc_size = sizeof(struct dwc3), - .flags = DM_FLAG_ALLOC_PRIV_DMA, + .priv_auto_alloc_size = sizeof(struct dwc3_generic_peripheral), +}; +#endif + +struct dwc3_glue_data { + struct clk_bulk clks; + struct reset_ctl_bulk resets; + fdt_addr_t regs; }; -static int dwc3_generic_bind(struct udevice *parent) +struct dwc3_glue_ops { + void (*select_dr_mode)(struct udevice *dev, int index, + enum usb_dr_mode mode); +}; + +void dwc3_ti_select_dr_mode(struct udevice *dev, int index, + enum usb_dr_mode mode) +{ +#define USBOTGSS_UTMI_OTG_STATUS 0x0084 +#define USBOTGSS_UTMI_OTG_OFFSET 0x0480 + +/* UTMI_OTG_STATUS REGISTER */ +#define USBOTGSS_UTMI_OTG_STATUS_SW_MODE BIT(31) +#define USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT BIT(9) +#define USBOTGSS_UTMI_OTG_STATUS_TXBITSTUFFENABLE BIT(8) +#define USBOTGSS_UTMI_OTG_STATUS_IDDIG BIT(4) +#define USBOTGSS_UTMI_OTG_STATUS_SESSEND BIT(3) +#define USBOTGSS_UTMI_OTG_STATUS_SESSVALID BIT(2) +#define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID BIT(1) +enum dwc3_omap_utmi_mode { + DWC3_OMAP_UTMI_MODE_UNKNOWN = 0, + DWC3_OMAP_UTMI_MODE_HW, + DWC3_OMAP_UTMI_MODE_SW, +}; + + u32 use_id_pin; + u32 host_mode; + u32 reg; + u32 utmi_mode; + u32 utmi_status_offset = USBOTGSS_UTMI_OTG_STATUS; + + struct dwc3_glue_data *glue = dev_get_platdata(dev); + void *base = map_physmem(glue->regs, 0x10000, MAP_NOCACHE); + + if (device_is_compatible(dev, "ti,am437x-dwc3")) + utmi_status_offset += USBOTGSS_UTMI_OTG_OFFSET; + + utmi_mode = dev_read_u32_default(dev, "utmi-mode", + DWC3_OMAP_UTMI_MODE_UNKNOWN); + if (utmi_mode != DWC3_OMAP_UTMI_MODE_HW) { + debug("%s: OTG is not supported. defaulting to PERIPHERAL\n", + dev->name); + mode = USB_DR_MODE_PERIPHERAL; + } + + switch (mode) { + case USB_DR_MODE_PERIPHERAL: + use_id_pin = 0; + host_mode = 0; + break; + case USB_DR_MODE_HOST: + use_id_pin = 0; + host_mode = 1; + break; + case USB_DR_MODE_OTG: + default: + use_id_pin = 1; + host_mode = 0; + break; + } + + reg = readl(base + utmi_status_offset); + + reg &= ~(USBOTGSS_UTMI_OTG_STATUS_SW_MODE); + if (!use_id_pin) + reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + + writel(reg, base + utmi_status_offset); + + reg &= ~(USBOTGSS_UTMI_OTG_STATUS_SESSEND | + USBOTGSS_UTMI_OTG_STATUS_VBUSVALID | + USBOTGSS_UTMI_OTG_STATUS_IDDIG); + + reg |= USBOTGSS_UTMI_OTG_STATUS_SESSVALID | + USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT; + + if (!host_mode) + reg |= USBOTGSS_UTMI_OTG_STATUS_IDDIG | + USBOTGSS_UTMI_OTG_STATUS_VBUSVALID; + + writel(reg, base + utmi_status_offset); + + unmap_physmem(base, MAP_NOCACHE); +} + +struct dwc3_glue_ops ti_ops = { + .select_dr_mode = dwc3_ti_select_dr_mode, +}; + +static int dwc3_glue_bind(struct udevice *parent) { const void *fdt = gd->fdt_blob; int node; @@ -109,29 +214,32 @@ static int dwc3_generic_bind(struct udevice *parent) const char *name = fdt_get_name(fdt, node, NULL); enum usb_dr_mode dr_mode; struct udevice *dev; - const char *driver; + const char *driver = NULL; debug("%s: subnode name: %s\n", __func__, name); - if (strncmp(name, "dwc3@", 4)) - continue; dr_mode = usb_get_dr_mode(node); switch (dr_mode) { case USB_DR_MODE_PERIPHERAL: case USB_DR_MODE_OTG: +#if CONFIG_IS_ENABLED(DM_USB_GADGET) debug("%s: dr_mode: OTG or Peripheral\n", __func__); driver = "dwc3-generic-peripheral"; +#endif break; case USB_DR_MODE_HOST: debug("%s: dr_mode: HOST\n", __func__); - driver = "dwc3-generic-host"; + driver = "xhci-dwc3"; break; default: debug("%s: unsupported dr_mode\n", __func__); return -ENODEV; }; + if (!driver) + continue; + ret = device_bind_driver_to_node(parent, driver, name, offset_to_ofnode(node), &dev); if (ret) { @@ -144,14 +252,107 @@ static int dwc3_generic_bind(struct udevice *parent) return 0; } -static const struct udevice_id dwc3_generic_ids[] = { +static int dwc3_glue_reset_init(struct udevice *dev, + struct dwc3_glue_data *glue) +{ + int ret; + + ret = reset_get_bulk(dev, &glue->resets); + if (ret == -ENOTSUPP) + return 0; + else if (ret) + return ret; + + ret = reset_deassert_bulk(&glue->resets); + if (ret) { + reset_release_bulk(&glue->resets); + return ret; + } + + return 0; +} + +static int dwc3_glue_clk_init(struct udevice *dev, + struct dwc3_glue_data *glue) +{ + int ret; + + ret = clk_get_bulk(dev, &glue->clks); + if (ret == -ENOSYS) + return 0; + if (ret) + return ret; + +#if CONFIG_IS_ENABLED(CLK) + ret = clk_enable_bulk(&glue->clks); + if (ret) { + clk_release_bulk(&glue->clks); + return ret; + } +#endif + + return 0; +} + +static int dwc3_glue_probe(struct udevice *dev) +{ + struct dwc3_glue_ops *ops = (struct dwc3_glue_ops *)dev_get_driver_data(dev); + struct dwc3_glue_data *glue = dev_get_platdata(dev); + struct udevice *child = NULL; + int index = 0; + int ret; + + glue->regs = dev_read_addr(dev); + + ret = dwc3_glue_clk_init(dev, glue); + if (ret) + return ret; + + ret = dwc3_glue_reset_init(dev, glue); + if (ret) + return ret; + + ret = device_find_first_child(dev, &child); + if (ret) + return ret; + + while (child) { + enum usb_dr_mode dr_mode; + + dr_mode = usb_get_dr_mode(dev_of_offset(child)); + device_find_next_child(&child); + if (ops && ops->select_dr_mode) + ops->select_dr_mode(dev, index, dr_mode); + index++; + } + + return 0; +} + +static int dwc3_glue_remove(struct udevice *dev) +{ + struct dwc3_glue_data *glue = dev_get_platdata(dev); + + reset_release_bulk(&glue->resets); + + clk_release_bulk(&glue->clks); + + return dm_scan_fdt_dev(dev); +} + +static const struct udevice_id dwc3_glue_ids[] = { { .compatible = "xlnx,zynqmp-dwc3" }, + { .compatible = "ti,dwc3", .data = (ulong)&ti_ops }, { } }; U_BOOT_DRIVER(dwc3_generic_wrapper) = { .name = "dwc3-generic-wrapper", .id = UCLASS_MISC, - .of_match = dwc3_generic_ids, - .bind = dwc3_generic_bind, + .of_match = dwc3_glue_ids, + .bind = dwc3_glue_bind, + .probe = dwc3_glue_probe, + .remove = dwc3_glue_remove, + .platdata_auto_alloc_size = sizeof(struct dwc3_glue_data), + }; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 4f68887b8d..818efb3e8d 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -12,7 +12,7 @@ * * commit c00552ebaf : Merge 3.18-rc7 into usb-next */ - +#include <common.h> #include <linux/kernel.h> #include <linux/list.h> |