1 /* 2 * STiH407 family DWC3 specific Glue layer 3 * 4 * Copyright (C) 2017, STMicroelectronics - All Rights Reserved 5 * Author(s): Patrice Chotard, <patrice.chotard@st.com> for STMicroelectronics. 6 * 7 * SPDX-License-Identifier: GPL-2.0+ 8 */ 9 10 #include <common.h> 11 #include <asm/io.h> 12 #include <dm.h> 13 #include <errno.h> 14 #include <dm/lists.h> 15 #include <regmap.h> 16 #include <reset-uclass.h> 17 #include <syscon.h> 18 #include <usb.h> 19 20 #include <linux/usb/dwc3.h> 21 #include <linux/usb/otg.h> 22 #include <dwc3-sti-glue.h> 23 24 DECLARE_GLOBAL_DATA_PTR; 25 26 /* 27 * struct sti_dwc3_glue_platdata - dwc3 STi glue driver private structure 28 * @syscfg_base: addr for the glue syscfg 29 * @glue_base: addr for the glue registers 30 * @syscfg_offset: usb syscfg control offset 31 * @powerdown_ctl: rest controller for powerdown signal 32 * @softreset_ctl: reset controller for softreset signal 33 * @mode: drd static host/device config 34 */ 35 struct sti_dwc3_glue_platdata { 36 phys_addr_t syscfg_base; 37 phys_addr_t glue_base; 38 phys_addr_t syscfg_offset; 39 struct reset_ctl powerdown_ctl; 40 struct reset_ctl softreset_ctl; 41 enum usb_dr_mode mode; 42 }; 43 44 static int sti_dwc3_glue_drd_init(struct sti_dwc3_glue_platdata *plat) 45 { 46 unsigned long val; 47 48 val = readl(plat->syscfg_base + plat->syscfg_offset); 49 50 val &= USB3_CONTROL_MASK; 51 52 switch (plat->mode) { 53 case USB_DR_MODE_PERIPHERAL: 54 val &= ~(USB3_DELAY_VBUSVALID 55 | USB3_SEL_FORCE_OPMODE | USB3_FORCE_OPMODE(0x3) 56 | USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2 57 | USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2); 58 59 val |= USB3_DEVICE_NOT_HOST | USB3_FORCE_VBUSVALID; 60 break; 61 62 case USB_DR_MODE_HOST: 63 val &= ~(USB3_DEVICE_NOT_HOST | USB3_FORCE_VBUSVALID 64 | USB3_SEL_FORCE_OPMODE | USB3_FORCE_OPMODE(0x3) 65 | USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2 66 | USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2); 67 68 val |= USB3_DELAY_VBUSVALID; 69 break; 70 71 default: 72 pr_err("Unsupported mode of operation %d\n", plat->mode); 73 return -EINVAL; 74 } 75 writel(val, plat->syscfg_base + plat->syscfg_offset); 76 77 return 0; 78 } 79 80 static void sti_dwc3_glue_init(struct sti_dwc3_glue_platdata *plat) 81 { 82 unsigned long reg; 83 84 reg = readl(plat->glue_base + CLKRST_CTRL); 85 86 reg |= AUX_CLK_EN | EXT_CFG_RESET_N | XHCI_REVISION; 87 reg &= ~SW_PIPEW_RESET_N; 88 89 writel(reg, plat->glue_base + CLKRST_CTRL); 90 91 /* configure mux for vbus, powerpresent and bvalid signals */ 92 reg = readl(plat->glue_base + USB2_VBUS_MNGMNT_SEL1); 93 94 reg |= SEL_OVERRIDE_VBUSVALID(USB2_VBUS_UTMIOTG) | 95 SEL_OVERRIDE_POWERPRESENT(USB2_VBUS_UTMIOTG) | 96 SEL_OVERRIDE_BVALID(USB2_VBUS_UTMIOTG); 97 98 writel(reg, plat->glue_base + USB2_VBUS_MNGMNT_SEL1); 99 100 setbits_le32(plat->glue_base + CLKRST_CTRL, SW_PIPEW_RESET_N); 101 } 102 103 static int sti_dwc3_glue_ofdata_to_platdata(struct udevice *dev) 104 { 105 struct sti_dwc3_glue_platdata *plat = dev_get_platdata(dev); 106 struct udevice *syscon; 107 struct regmap *regmap; 108 int ret; 109 u32 reg[4]; 110 111 ret = ofnode_read_u32_array(dev->node, "reg", reg, ARRAY_SIZE(reg)); 112 if (ret) { 113 pr_err("unable to find st,stih407-dwc3 reg property(%d)\n", ret); 114 return ret; 115 } 116 117 plat->glue_base = reg[0]; 118 plat->syscfg_offset = reg[2]; 119 120 /* get corresponding syscon phandle */ 121 ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "st,syscfg", 122 &syscon); 123 if (ret) { 124 pr_err("unable to find syscon device (%d)\n", ret); 125 return ret; 126 } 127 128 /* get syscfg-reg base address */ 129 regmap = syscon_get_regmap(syscon); 130 if (!regmap) { 131 pr_err("unable to find regmap\n"); 132 return -ENODEV; 133 } 134 plat->syscfg_base = regmap->base; 135 136 /* get powerdown reset */ 137 ret = reset_get_by_name(dev, "powerdown", &plat->powerdown_ctl); 138 if (ret) { 139 pr_err("can't get powerdown reset for %s (%d)", dev->name, ret); 140 return ret; 141 } 142 143 /* get softreset reset */ 144 ret = reset_get_by_name(dev, "softreset", &plat->softreset_ctl); 145 if (ret) 146 pr_err("can't get soft reset for %s (%d)", dev->name, ret); 147 148 return ret; 149 }; 150 151 static int sti_dwc3_glue_bind(struct udevice *dev) 152 { 153 struct sti_dwc3_glue_platdata *plat = dev_get_platdata(dev); 154 ofnode node, dwc3_node; 155 156 /* Find snps,dwc3 node from subnode */ 157 ofnode_for_each_subnode(node, dev->node) { 158 if (ofnode_device_is_compatible(node, "snps,dwc3")) 159 dwc3_node = node; 160 } 161 162 if (!ofnode_valid(node)) { 163 pr_err("Can't find dwc3 subnode for %s\n", dev->name); 164 return -ENODEV; 165 } 166 167 /* retrieve the DWC3 dual role mode */ 168 plat->mode = usb_get_dr_mode(dwc3_node); 169 if (plat->mode == USB_DR_MODE_UNKNOWN) 170 /* by default set dual role mode to HOST */ 171 plat->mode = USB_DR_MODE_HOST; 172 173 return dm_scan_fdt_dev(dev); 174 } 175 176 static int sti_dwc3_glue_probe(struct udevice *dev) 177 { 178 struct sti_dwc3_glue_platdata *plat = dev_get_platdata(dev); 179 int ret; 180 181 /* deassert both powerdown and softreset */ 182 ret = reset_deassert(&plat->powerdown_ctl); 183 if (ret < 0) { 184 pr_err("DWC3 powerdown reset deassert failed: %d", ret); 185 return ret; 186 } 187 188 ret = reset_deassert(&plat->softreset_ctl); 189 if (ret < 0) { 190 pr_err("DWC3 soft reset deassert failed: %d", ret); 191 goto softreset_err; 192 } 193 194 ret = sti_dwc3_glue_drd_init(plat); 195 if (ret) 196 goto init_err; 197 198 sti_dwc3_glue_init(plat); 199 200 return 0; 201 202 init_err: 203 ret = reset_assert(&plat->softreset_ctl); 204 if (ret < 0) { 205 pr_err("DWC3 soft reset deassert failed: %d", ret); 206 return ret; 207 } 208 209 softreset_err: 210 ret = reset_assert(&plat->powerdown_ctl); 211 if (ret < 0) 212 pr_err("DWC3 powerdown reset deassert failed: %d", ret); 213 214 return ret; 215 } 216 217 static int sti_dwc3_glue_remove(struct udevice *dev) 218 { 219 struct sti_dwc3_glue_platdata *plat = dev_get_platdata(dev); 220 int ret; 221 222 /* assert both powerdown and softreset */ 223 ret = reset_assert(&plat->powerdown_ctl); 224 if (ret < 0) { 225 pr_err("DWC3 powerdown reset deassert failed: %d", ret); 226 return ret; 227 } 228 229 ret = reset_assert(&plat->softreset_ctl); 230 if (ret < 0) 231 pr_err("DWC3 soft reset deassert failed: %d", ret); 232 233 return ret; 234 } 235 236 static const struct udevice_id sti_dwc3_glue_ids[] = { 237 { .compatible = "st,stih407-dwc3" }, 238 { } 239 }; 240 241 U_BOOT_DRIVER(dwc3_sti_glue) = { 242 .name = "dwc3_sti_glue", 243 .id = UCLASS_MISC, 244 .of_match = sti_dwc3_glue_ids, 245 .ofdata_to_platdata = sti_dwc3_glue_ofdata_to_platdata, 246 .probe = sti_dwc3_glue_probe, 247 .remove = sti_dwc3_glue_remove, 248 .bind = sti_dwc3_glue_bind, 249 .platdata_auto_alloc_size = sizeof(struct sti_dwc3_glue_platdata), 250 .flags = DM_FLAG_ALLOC_PRIV_DMA, 251 }; 252