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