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