149d67454SMichal Simek // SPDX-License-Identifier: GPL-2.0 249d67454SMichal Simek /* 349d67454SMichal Simek * Generic DWC3 Glue layer 449d67454SMichal Simek * 549d67454SMichal Simek * Copyright (C) 2016 - 2018 Xilinx, Inc. 649d67454SMichal Simek * 749d67454SMichal Simek * Based on dwc3-omap.c. 849d67454SMichal Simek */ 949d67454SMichal Simek 1049d67454SMichal Simek #include <common.h> 11*93991cf1SJean-Jacques Hiblot #include <asm-generic/io.h> 1249d67454SMichal Simek #include <dm.h> 1349d67454SMichal Simek #include <dm/device-internal.h> 1449d67454SMichal Simek #include <dm/lists.h> 15446e3a20SJean-Jacques Hiblot #include <dwc3-uboot.h> 1649d67454SMichal Simek #include <linux/usb/ch9.h> 1749d67454SMichal Simek #include <linux/usb/gadget.h> 1849d67454SMichal Simek #include <malloc.h> 1949d67454SMichal Simek #include <usb.h> 2049d67454SMichal Simek #include "core.h" 2149d67454SMichal Simek #include "gadget.h" 22446e3a20SJean-Jacques Hiblot #include <reset.h> 23446e3a20SJean-Jacques Hiblot #include <clk.h> 2449d67454SMichal Simek 25687ab545SJean-Jacques Hiblot #if CONFIG_IS_ENABLED(DM_USB_GADGET) 26446e3a20SJean-Jacques Hiblot struct dwc3_generic_peripheral { 27446e3a20SJean-Jacques Hiblot struct dwc3 dwc3; 28446e3a20SJean-Jacques Hiblot struct phy *phys; 29446e3a20SJean-Jacques Hiblot int num_phys; 30446e3a20SJean-Jacques Hiblot fdt_addr_t base; 31446e3a20SJean-Jacques Hiblot }; 32446e3a20SJean-Jacques Hiblot 33ff8d7558SJean-Jacques Hiblot int dm_usb_gadget_handle_interrupts(struct udevice *dev) 3449d67454SMichal Simek { 35446e3a20SJean-Jacques Hiblot struct dwc3_generic_peripheral *priv = dev_get_priv(dev); 36446e3a20SJean-Jacques Hiblot struct dwc3 *dwc3 = &priv->dwc3; 3749d67454SMichal Simek 38446e3a20SJean-Jacques Hiblot dwc3_gadget_uboot_handle_interrupt(dwc3); 3949d67454SMichal Simek 4049d67454SMichal Simek return 0; 4149d67454SMichal Simek } 4249d67454SMichal Simek 4349d67454SMichal Simek static int dwc3_generic_peripheral_probe(struct udevice *dev) 4449d67454SMichal Simek { 45446e3a20SJean-Jacques Hiblot int rc; 46446e3a20SJean-Jacques Hiblot struct dwc3_generic_peripheral *priv = dev_get_priv(dev); 47446e3a20SJean-Jacques Hiblot struct dwc3 *dwc3 = &priv->dwc3; 4849d67454SMichal Simek 49446e3a20SJean-Jacques Hiblot rc = dwc3_setup_phy(dev, &priv->phys, &priv->num_phys); 50446e3a20SJean-Jacques Hiblot if (rc) 51446e3a20SJean-Jacques Hiblot return rc; 52446e3a20SJean-Jacques Hiblot 53446e3a20SJean-Jacques Hiblot dwc3->regs = map_physmem(priv->base, DWC3_OTG_REGS_END, MAP_NOCACHE); 54446e3a20SJean-Jacques Hiblot dwc3->regs += DWC3_GLOBALS_REGS_START; 55446e3a20SJean-Jacques Hiblot dwc3->dev = dev; 56446e3a20SJean-Jacques Hiblot 57446e3a20SJean-Jacques Hiblot rc = dwc3_init(dwc3); 58446e3a20SJean-Jacques Hiblot if (rc) { 59446e3a20SJean-Jacques Hiblot unmap_physmem(dwc3->regs, MAP_NOCACHE); 60446e3a20SJean-Jacques Hiblot return rc; 61446e3a20SJean-Jacques Hiblot } 62446e3a20SJean-Jacques Hiblot 63446e3a20SJean-Jacques Hiblot return 0; 6449d67454SMichal Simek } 6549d67454SMichal Simek 6649d67454SMichal Simek static int dwc3_generic_peripheral_remove(struct udevice *dev) 6749d67454SMichal Simek { 68446e3a20SJean-Jacques Hiblot struct dwc3_generic_peripheral *priv = dev_get_priv(dev); 69446e3a20SJean-Jacques Hiblot struct dwc3 *dwc3 = &priv->dwc3; 7049d67454SMichal Simek 71446e3a20SJean-Jacques Hiblot dwc3_remove(dwc3); 72446e3a20SJean-Jacques Hiblot dwc3_shutdown_phy(dev, priv->phys, priv->num_phys); 73446e3a20SJean-Jacques Hiblot unmap_physmem(dwc3->regs, MAP_NOCACHE); 7449d67454SMichal Simek 7549d67454SMichal Simek return 0; 7649d67454SMichal Simek } 7749d67454SMichal Simek 7849d67454SMichal Simek static int dwc3_generic_peripheral_ofdata_to_platdata(struct udevice *dev) 7949d67454SMichal Simek { 80446e3a20SJean-Jacques Hiblot struct dwc3_generic_peripheral *priv = dev_get_priv(dev); 81446e3a20SJean-Jacques Hiblot struct dwc3 *dwc3 = &priv->dwc3; 8249d67454SMichal Simek int node = dev_of_offset(dev); 8349d67454SMichal Simek 84446e3a20SJean-Jacques Hiblot priv->base = devfdt_get_addr(dev); 8549d67454SMichal Simek 86446e3a20SJean-Jacques Hiblot dwc3->maximum_speed = usb_get_maximum_speed(node); 87446e3a20SJean-Jacques Hiblot if (dwc3->maximum_speed == USB_SPEED_UNKNOWN) { 8849d67454SMichal Simek pr_err("Invalid usb maximum speed\n"); 8949d67454SMichal Simek return -ENODEV; 9049d67454SMichal Simek } 9149d67454SMichal Simek 92446e3a20SJean-Jacques Hiblot dwc3->dr_mode = usb_get_dr_mode(node); 93446e3a20SJean-Jacques Hiblot if (dwc3->dr_mode == USB_DR_MODE_UNKNOWN) { 9449d67454SMichal Simek pr_err("Invalid usb mode setup\n"); 9549d67454SMichal Simek return -ENODEV; 9649d67454SMichal Simek } 9749d67454SMichal Simek 9849d67454SMichal Simek return 0; 9949d67454SMichal Simek } 10049d67454SMichal Simek 10149d67454SMichal Simek U_BOOT_DRIVER(dwc3_generic_peripheral) = { 10249d67454SMichal Simek .name = "dwc3-generic-peripheral", 10301311624SJean-Jacques Hiblot .id = UCLASS_USB_GADGET_GENERIC, 10449d67454SMichal Simek .ofdata_to_platdata = dwc3_generic_peripheral_ofdata_to_platdata, 10549d67454SMichal Simek .probe = dwc3_generic_peripheral_probe, 10649d67454SMichal Simek .remove = dwc3_generic_peripheral_remove, 107446e3a20SJean-Jacques Hiblot .priv_auto_alloc_size = sizeof(struct dwc3_generic_peripheral), 10849d67454SMichal Simek }; 109687ab545SJean-Jacques Hiblot #endif 11049d67454SMichal Simek 111446e3a20SJean-Jacques Hiblot struct dwc3_glue_data { 112446e3a20SJean-Jacques Hiblot struct clk_bulk clks; 113446e3a20SJean-Jacques Hiblot struct reset_ctl_bulk resets; 114*93991cf1SJean-Jacques Hiblot fdt_addr_t regs; 115*93991cf1SJean-Jacques Hiblot }; 116*93991cf1SJean-Jacques Hiblot 117*93991cf1SJean-Jacques Hiblot struct dwc3_glue_ops { 118*93991cf1SJean-Jacques Hiblot void (*select_dr_mode)(struct udevice *dev, int index, 119*93991cf1SJean-Jacques Hiblot enum usb_dr_mode mode); 120446e3a20SJean-Jacques Hiblot }; 121446e3a20SJean-Jacques Hiblot 122446e3a20SJean-Jacques Hiblot static int dwc3_glue_bind(struct udevice *parent) 12349d67454SMichal Simek { 12449d67454SMichal Simek const void *fdt = gd->fdt_blob; 12549d67454SMichal Simek int node; 12649d67454SMichal Simek int ret; 12749d67454SMichal Simek 12849d67454SMichal Simek for (node = fdt_first_subnode(fdt, dev_of_offset(parent)); node > 0; 12949d67454SMichal Simek node = fdt_next_subnode(fdt, node)) { 13049d67454SMichal Simek const char *name = fdt_get_name(fdt, node, NULL); 13149d67454SMichal Simek enum usb_dr_mode dr_mode; 13249d67454SMichal Simek struct udevice *dev; 133446e3a20SJean-Jacques Hiblot const char *driver = NULL; 13449d67454SMichal Simek 13549d67454SMichal Simek debug("%s: subnode name: %s\n", __func__, name); 13649d67454SMichal Simek 13749d67454SMichal Simek dr_mode = usb_get_dr_mode(node); 13849d67454SMichal Simek 13949d67454SMichal Simek switch (dr_mode) { 14049d67454SMichal Simek case USB_DR_MODE_PERIPHERAL: 14149d67454SMichal Simek case USB_DR_MODE_OTG: 142446e3a20SJean-Jacques Hiblot #if CONFIG_IS_ENABLED(DM_USB_GADGET) 14349d67454SMichal Simek debug("%s: dr_mode: OTG or Peripheral\n", __func__); 14449d67454SMichal Simek driver = "dwc3-generic-peripheral"; 145446e3a20SJean-Jacques Hiblot #endif 14649d67454SMichal Simek break; 14749d67454SMichal Simek case USB_DR_MODE_HOST: 14849d67454SMichal Simek debug("%s: dr_mode: HOST\n", __func__); 149446e3a20SJean-Jacques Hiblot driver = "xhci-dwc3"; 15049d67454SMichal Simek break; 15149d67454SMichal Simek default: 15249d67454SMichal Simek debug("%s: unsupported dr_mode\n", __func__); 15349d67454SMichal Simek return -ENODEV; 15449d67454SMichal Simek }; 15549d67454SMichal Simek 156446e3a20SJean-Jacques Hiblot if (!driver) 157446e3a20SJean-Jacques Hiblot continue; 158446e3a20SJean-Jacques Hiblot 15949d67454SMichal Simek ret = device_bind_driver_to_node(parent, driver, name, 16049d67454SMichal Simek offset_to_ofnode(node), &dev); 16149d67454SMichal Simek if (ret) { 16249d67454SMichal Simek debug("%s: not able to bind usb device mode\n", 16349d67454SMichal Simek __func__); 16449d67454SMichal Simek return ret; 16549d67454SMichal Simek } 16649d67454SMichal Simek } 16749d67454SMichal Simek 16849d67454SMichal Simek return 0; 16949d67454SMichal Simek } 17049d67454SMichal Simek 171446e3a20SJean-Jacques Hiblot static int dwc3_glue_reset_init(struct udevice *dev, 172446e3a20SJean-Jacques Hiblot struct dwc3_glue_data *glue) 173446e3a20SJean-Jacques Hiblot { 174446e3a20SJean-Jacques Hiblot int ret; 175446e3a20SJean-Jacques Hiblot 176446e3a20SJean-Jacques Hiblot ret = reset_get_bulk(dev, &glue->resets); 177446e3a20SJean-Jacques Hiblot if (ret == -ENOTSUPP) 178446e3a20SJean-Jacques Hiblot return 0; 179446e3a20SJean-Jacques Hiblot else if (ret) 180446e3a20SJean-Jacques Hiblot return ret; 181446e3a20SJean-Jacques Hiblot 182446e3a20SJean-Jacques Hiblot ret = reset_deassert_bulk(&glue->resets); 183446e3a20SJean-Jacques Hiblot if (ret) { 184446e3a20SJean-Jacques Hiblot reset_release_bulk(&glue->resets); 185446e3a20SJean-Jacques Hiblot return ret; 186446e3a20SJean-Jacques Hiblot } 187446e3a20SJean-Jacques Hiblot 188446e3a20SJean-Jacques Hiblot return 0; 189446e3a20SJean-Jacques Hiblot } 190446e3a20SJean-Jacques Hiblot 191446e3a20SJean-Jacques Hiblot static int dwc3_glue_clk_init(struct udevice *dev, 192446e3a20SJean-Jacques Hiblot struct dwc3_glue_data *glue) 193446e3a20SJean-Jacques Hiblot { 194446e3a20SJean-Jacques Hiblot int ret; 195446e3a20SJean-Jacques Hiblot 196446e3a20SJean-Jacques Hiblot ret = clk_get_bulk(dev, &glue->clks); 197446e3a20SJean-Jacques Hiblot if (ret == -ENOSYS) 198446e3a20SJean-Jacques Hiblot return 0; 199446e3a20SJean-Jacques Hiblot if (ret) 200446e3a20SJean-Jacques Hiblot return ret; 201446e3a20SJean-Jacques Hiblot 202446e3a20SJean-Jacques Hiblot #if CONFIG_IS_ENABLED(CLK) 203446e3a20SJean-Jacques Hiblot ret = clk_enable_bulk(&glue->clks); 204446e3a20SJean-Jacques Hiblot if (ret) { 205446e3a20SJean-Jacques Hiblot clk_release_bulk(&glue->clks); 206446e3a20SJean-Jacques Hiblot return ret; 207446e3a20SJean-Jacques Hiblot } 208446e3a20SJean-Jacques Hiblot #endif 209446e3a20SJean-Jacques Hiblot 210446e3a20SJean-Jacques Hiblot return 0; 211446e3a20SJean-Jacques Hiblot } 212446e3a20SJean-Jacques Hiblot 213446e3a20SJean-Jacques Hiblot static int dwc3_glue_probe(struct udevice *dev) 214446e3a20SJean-Jacques Hiblot { 215*93991cf1SJean-Jacques Hiblot struct dwc3_glue_ops *ops = (struct dwc3_glue_ops *)dev_get_driver_data(dev); 216446e3a20SJean-Jacques Hiblot struct dwc3_glue_data *glue = dev_get_platdata(dev); 217*93991cf1SJean-Jacques Hiblot struct udevice *child = NULL; 218*93991cf1SJean-Jacques Hiblot int index = 0; 219446e3a20SJean-Jacques Hiblot int ret; 220446e3a20SJean-Jacques Hiblot 221*93991cf1SJean-Jacques Hiblot glue->regs = dev_read_addr(dev); 222*93991cf1SJean-Jacques Hiblot 223446e3a20SJean-Jacques Hiblot ret = dwc3_glue_clk_init(dev, glue); 224446e3a20SJean-Jacques Hiblot if (ret) 225446e3a20SJean-Jacques Hiblot return ret; 226446e3a20SJean-Jacques Hiblot 227446e3a20SJean-Jacques Hiblot ret = dwc3_glue_reset_init(dev, glue); 228446e3a20SJean-Jacques Hiblot if (ret) 229446e3a20SJean-Jacques Hiblot return ret; 230446e3a20SJean-Jacques Hiblot 231*93991cf1SJean-Jacques Hiblot ret = device_find_first_child(dev, &child); 232*93991cf1SJean-Jacques Hiblot if (ret) 233*93991cf1SJean-Jacques Hiblot return ret; 234*93991cf1SJean-Jacques Hiblot 235*93991cf1SJean-Jacques Hiblot while (child) { 236*93991cf1SJean-Jacques Hiblot enum usb_dr_mode dr_mode; 237*93991cf1SJean-Jacques Hiblot 238*93991cf1SJean-Jacques Hiblot dr_mode = usb_get_dr_mode(dev_of_offset(child)); 239*93991cf1SJean-Jacques Hiblot device_find_next_child(&child); 240*93991cf1SJean-Jacques Hiblot if (ops && ops->select_dr_mode) 241*93991cf1SJean-Jacques Hiblot ops->select_dr_mode(dev, index, dr_mode); 242*93991cf1SJean-Jacques Hiblot index++; 243*93991cf1SJean-Jacques Hiblot } 244*93991cf1SJean-Jacques Hiblot 245446e3a20SJean-Jacques Hiblot return 0; 246446e3a20SJean-Jacques Hiblot } 247446e3a20SJean-Jacques Hiblot 248446e3a20SJean-Jacques Hiblot static int dwc3_glue_remove(struct udevice *dev) 249446e3a20SJean-Jacques Hiblot { 250446e3a20SJean-Jacques Hiblot struct dwc3_glue_data *glue = dev_get_platdata(dev); 251446e3a20SJean-Jacques Hiblot 252446e3a20SJean-Jacques Hiblot reset_release_bulk(&glue->resets); 253446e3a20SJean-Jacques Hiblot 254446e3a20SJean-Jacques Hiblot clk_release_bulk(&glue->clks); 255446e3a20SJean-Jacques Hiblot 256446e3a20SJean-Jacques Hiblot return dm_scan_fdt_dev(dev); 257446e3a20SJean-Jacques Hiblot } 258446e3a20SJean-Jacques Hiblot 259446e3a20SJean-Jacques Hiblot static const struct udevice_id dwc3_glue_ids[] = { 26049d67454SMichal Simek { .compatible = "xlnx,zynqmp-dwc3" }, 26149d67454SMichal Simek { } 26249d67454SMichal Simek }; 26349d67454SMichal Simek 26449d67454SMichal Simek U_BOOT_DRIVER(dwc3_generic_wrapper) = { 26549d67454SMichal Simek .name = "dwc3-generic-wrapper", 26649d67454SMichal Simek .id = UCLASS_MISC, 267446e3a20SJean-Jacques Hiblot .of_match = dwc3_glue_ids, 268446e3a20SJean-Jacques Hiblot .bind = dwc3_glue_bind, 269446e3a20SJean-Jacques Hiblot .probe = dwc3_glue_probe, 270446e3a20SJean-Jacques Hiblot .remove = dwc3_glue_remove, 271446e3a20SJean-Jacques Hiblot .platdata_auto_alloc_size = sizeof(struct dwc3_glue_data), 272446e3a20SJean-Jacques Hiblot 27349d67454SMichal Simek }; 274