1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Layerscape DWC3 Glue layer 4 * 5 * Copyright (C) 2021 Michael Walle <michael@walle.cc> 6 * 7 * Based on dwc3-generic.c. 8 */ 9 10#include <common.h> 11#include <dm.h> 12#include <dm/device_compat.h> 13#include <dm/device-internal.h> 14#include <dm/lists.h> 15#include <dwc3-uboot.h> 16#include <linux/usb/gadget.h> 17#include <usb.h> 18#include "core.h" 19#include "gadget.h" 20#include <usb/xhci.h> 21 22struct dwc3_layerscape_plat { 23 fdt_addr_t base; 24 u32 maximum_speed; 25 enum usb_dr_mode dr_mode; 26}; 27 28struct dwc3_layerscape_priv { 29 void *base; 30 struct dwc3 dwc3; 31 struct phy_bulk phys; 32}; 33 34struct dwc3_layerscape_host_priv { 35 struct xhci_ctrl xhci_ctrl; 36 struct dwc3_layerscape_priv gen_priv; 37}; 38 39static int dwc3_layerscape_probe(struct udevice *dev, 40 struct dwc3_layerscape_priv *priv) 41{ 42 int rc; 43 struct dwc3_layerscape_plat *plat = dev_get_plat(dev); 44 struct dwc3 *dwc3 = &priv->dwc3; 45 46 dwc3->dev = dev; 47 dwc3->maximum_speed = plat->maximum_speed; 48 dwc3->dr_mode = plat->dr_mode; 49 if (CONFIG_IS_ENABLED(OF_CONTROL)) 50 dwc3_of_parse(dwc3); 51 52 rc = dwc3_setup_phy(dev, &priv->phys); 53 if (rc && rc != -ENOTSUPP) 54 return rc; 55 56 priv->base = map_physmem(plat->base, DWC3_OTG_REGS_END, MAP_NOCACHE); 57 dwc3->regs = priv->base + DWC3_GLOBALS_REGS_START; 58 59 rc = dwc3_init(dwc3); 60 if (rc) { 61 unmap_physmem(priv->base, MAP_NOCACHE); 62 return rc; 63 } 64 65 return 0; 66} 67 68static int dwc3_layerscape_remove(struct udevice *dev, 69 struct dwc3_layerscape_priv *priv) 70{ 71 struct dwc3 *dwc3 = &priv->dwc3; 72 73 dwc3_remove(dwc3); 74 dwc3_shutdown_phy(dev, &priv->phys); 75 unmap_physmem(dwc3->regs, MAP_NOCACHE); 76 77 return 0; 78} 79 80static int dwc3_layerscape_of_to_plat(struct udevice *dev) 81{ 82 struct dwc3_layerscape_plat *plat = dev_get_plat(dev); 83 ofnode node = dev_ofnode(dev); 84 85 plat->base = dev_read_addr(dev); 86 87 plat->maximum_speed = usb_get_maximum_speed(node); 88 if (plat->maximum_speed == USB_SPEED_UNKNOWN) { 89 dev_dbg(dev, "No USB maximum speed specified. Using super speed\n"); 90 plat->maximum_speed = USB_SPEED_SUPER; 91 } 92 93 plat->dr_mode = usb_get_dr_mode(node); 94 if (plat->dr_mode == USB_DR_MODE_UNKNOWN) { 95 dev_err(dev, "Invalid usb mode setup\n"); 96 return -ENODEV; 97 } 98 99 return 0; 100} 101 102#if CONFIG_IS_ENABLED(DM_USB_GADGET) 103int dm_usb_gadget_handle_interrupts(struct udevice *dev) 104{ 105 struct dwc3_layerscape_priv *priv = dev_get_priv(dev); 106 107 dwc3_gadget_uboot_handle_interrupt(&priv->dwc3); 108 109 return 0; 110} 111 112static int dwc3_layerscape_peripheral_probe(struct udevice *dev) 113{ 114 struct dwc3_layerscape_priv *priv = dev_get_priv(dev); 115 116 return dwc3_layerscape_probe(dev, priv); 117} 118 119static int dwc3_layerscape_peripheral_remove(struct udevice *dev) 120{ 121 struct dwc3_layerscape_priv *priv = dev_get_priv(dev); 122 123 return dwc3_layerscape_remove(dev, priv); 124} 125 126U_BOOT_DRIVER(dwc3_layerscape_peripheral) = { 127 .name = "dwc3-layerscape-peripheral", 128 .id = UCLASS_USB_GADGET_GENERIC, 129 .of_to_plat = dwc3_layerscape_of_to_plat, 130 .probe = dwc3_layerscape_peripheral_probe, 131 .remove = dwc3_layerscape_peripheral_remove, 132 .priv_auto = sizeof(struct dwc3_layerscape_priv), 133 .plat_auto = sizeof(struct dwc3_layerscape_plat), 134}; 135#endif 136 137#if CONFIG_IS_ENABLED(USB_HOST) 138static int dwc3_layerscape_host_probe(struct udevice *dev) 139{ 140 struct xhci_hcor *hcor; 141 struct xhci_hccr *hccr; 142 struct dwc3_layerscape_host_priv *priv = dev_get_priv(dev); 143 int rc; 144 145 rc = dwc3_layerscape_probe(dev, &priv->gen_priv); 146 if (rc) 147 return rc; 148 149 hccr = priv->gen_priv.base; 150 hcor = priv->gen_priv.base + HC_LENGTH(xhci_readl(&hccr->cr_capbase)); 151 152 return xhci_register(dev, hccr, hcor); 153} 154 155static int dwc3_layerscape_host_remove(struct udevice *dev) 156{ 157 struct dwc3_layerscape_host_priv *priv = dev_get_priv(dev); 158 int rc; 159 160 rc = xhci_deregister(dev); 161 if (rc) 162 return rc; 163 164 return dwc3_layerscape_remove(dev, &priv->gen_priv); 165} 166 167U_BOOT_DRIVER(dwc3_layerscape_host) = { 168 .name = "dwc3-layerscape-host", 169 .id = UCLASS_USB, 170 .of_to_plat = dwc3_layerscape_of_to_plat, 171 .probe = dwc3_layerscape_host_probe, 172 .remove = dwc3_layerscape_host_remove, 173 .priv_auto = sizeof(struct dwc3_layerscape_host_priv), 174 .plat_auto = sizeof(struct dwc3_layerscape_plat), 175 .ops = &xhci_usb_ops, 176 .flags = DM_FLAG_ALLOC_PRIV_DMA, 177}; 178#endif 179 180static int dwc3_layerscape_bind(struct udevice *dev) 181{ 182 ofnode node = dev_ofnode(dev); 183 const char *name = ofnode_get_name(node); 184 enum usb_dr_mode dr_mode; 185 char *driver; 186 187 dr_mode = usb_get_dr_mode(node); 188 189 switch (dr_mode) { 190#if CONFIG_IS_ENABLED(DM_USB_GADGET) 191 case USB_DR_MODE_PERIPHERAL: 192 dev_dbg(dev, "Using peripheral mode\n"); 193 driver = "dwc3-layerscape-peripheral"; 194 break; 195#endif 196#if CONFIG_IS_ENABLED(USB_HOST) 197 case USB_DR_MODE_HOST: 198 dev_dbg(dev, "Using host mode\n"); 199 driver = "dwc3-layerscape-host"; 200 break; 201#endif 202 default: 203 dev_dbg(dev, "Unsupported dr_mode\n"); 204 return -ENODEV; 205 }; 206 207 return device_bind_driver_to_node(dev, driver, name, node, NULL); 208} 209 210static const struct udevice_id dwc3_layerscape_ids[] = { 211 { .compatible = "fsl,layerscape-dwc3" }, 212 { .compatible = "fsl,ls1028a-dwc3" }, 213 { } 214}; 215 216U_BOOT_DRIVER(dwc3_layerscape_wrapper) = { 217 .name = "dwc3-layerscape-wrapper", 218 .id = UCLASS_NOP, 219 .of_match = dwc3_layerscape_ids, 220 .bind = dwc3_layerscape_bind, 221}; 222