1 /* $OpenBSD: ehci_fdt.c,v 1.9 2022/05/23 11:37:22 dlg Exp $ */ 2 3 /* 4 * Copyright (c) 2005 David Gwynne <dlg@openbsd.org> 5 * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/types.h> 21 #include <sys/systm.h> 22 #include <sys/device.h> 23 #include <sys/malloc.h> 24 25 #include <machine/intr.h> 26 #include <machine/bus.h> 27 #include <machine/fdt.h> 28 29 #include <dev/ofw/openfirm.h> 30 #include <dev/ofw/ofw_clock.h> 31 #include <dev/ofw/ofw_pinctrl.h> 32 #include <dev/ofw/ofw_regulator.h> 33 #include <dev/ofw/fdt.h> 34 35 #include <dev/usb/usb.h> 36 #include <dev/usb/usbdi.h> 37 #include <dev/usb/usbdivar.h> 38 #include <dev/usb/usb_mem.h> 39 40 #include <dev/usb/ehcireg.h> 41 #include <dev/usb/ehcivar.h> 42 43 #define MARVELL_EHCI_HOST_OFFSET 0x0100 44 45 struct ehci_fdt_softc { 46 struct ehci_softc sc; 47 48 bus_space_handle_t sc_ioh; 49 bus_size_t sc_size; 50 void *sc_ih; 51 52 int sc_node; 53 }; 54 55 int ehci_fdt_match(struct device *, void *, void *); 56 void ehci_fdt_attach(struct device *, struct device *, void *); 57 int ehci_fdt_detach(struct device *, int); 58 59 const struct cfattach ehci_fdt_ca = { 60 sizeof(struct ehci_fdt_softc), ehci_fdt_match, ehci_fdt_attach, 61 ehci_fdt_detach, ehci_activate 62 }; 63 64 void ehci_init_phys(struct ehci_fdt_softc *); 65 66 int 67 ehci_fdt_match(struct device *parent, void *match, void *aux) 68 { 69 struct fdt_attach_args *faa = aux; 70 71 return OF_is_compatible(faa->fa_node, "generic-ehci") || 72 OF_is_compatible(faa->fa_node, "marvell,armada-3700-ehci"); 73 } 74 75 void 76 ehci_fdt_attach(struct device *parent, struct device *self, void *aux) 77 { 78 struct ehci_fdt_softc *sc = (struct ehci_fdt_softc *)self; 79 struct fdt_attach_args *faa = aux; 80 char *devname = sc->sc.sc_bus.bdev.dv_xname; 81 bus_size_t offset = 0; 82 usbd_status r; 83 84 if (faa->fa_nreg < 1) { 85 printf(": no registers\n"); 86 return; 87 } 88 89 sc->sc_node = faa->fa_node; 90 sc->sc.iot = faa->fa_iot; 91 sc->sc.sc_bus.dmatag = faa->fa_dmat; 92 sc->sc_size = faa->fa_reg[0].size; 93 94 if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr, 95 sc->sc_size, 0, &sc->sc_ioh)) { 96 printf(": can't map registers\n"); 97 goto out; 98 } 99 100 if (OF_is_compatible(faa->fa_node, "marvell,armada-3700-ehci")) 101 offset = MARVELL_EHCI_HOST_OFFSET; 102 103 sc->sc.sc_size = sc->sc_size - offset; 104 if (bus_space_subregion(sc->sc.iot, sc->sc_ioh, offset, 105 sc->sc.sc_size, &sc->sc.ioh)) { 106 printf(": can't map ehci registers\n"); 107 goto unmap; 108 } 109 110 pinctrl_byname(sc->sc_node, "default"); 111 112 clock_enable_all(sc->sc_node); 113 reset_deassert_all(sc->sc_node); 114 115 /* Disable interrupts, so we don't get any spurious ones. */ 116 sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH); 117 EOWRITE2(&sc->sc, EHCI_USBINTR, 0); 118 119 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_USB, 120 ehci_intr, &sc->sc, devname); 121 if (sc->sc_ih == NULL) { 122 printf(": can't establish interrupt\n"); 123 clock_disable_all(sc->sc_node); 124 goto unmap; 125 } 126 127 printf("\n"); 128 129 if (OF_is_compatible(faa->fa_node, "marvell,armada-3700-ehci")) { 130 uint32_t usbmode; 131 132 /* force HOST mode */ 133 sc->sc.sc_flags = EHCIF_USBMODE; 134 135 usbmode = EOREAD4(&sc->sc, EHCI_USBMODE); 136 CLR(usbmode, EHCI_USBMODE_CM_M); 137 SET(usbmode, EHCI_USBMODE_CM_HOST); 138 EOWRITE4(&sc->sc, EHCI_USBMODE, usbmode); 139 } 140 141 ehci_init_phys(sc); 142 143 strlcpy(sc->sc.sc_vendor, "Generic", sizeof(sc->sc.sc_vendor)); 144 r = ehci_init(&sc->sc); 145 if (r != USBD_NORMAL_COMPLETION) { 146 printf("%s: init failed, error=%d\n", devname, r); 147 clock_disable_all(sc->sc_node); 148 goto disestablish_intr; 149 } 150 151 /* Attach usb device. */ 152 config_found(self, &sc->sc.sc_bus, usbctlprint); 153 return; 154 155 disestablish_intr: 156 fdt_intr_disestablish(sc->sc_ih); 157 sc->sc_ih = NULL; 158 unmap: 159 bus_space_unmap(sc->sc.iot, sc->sc_ioh, sc->sc_size); 160 sc->sc.sc_size = 0; 161 out: 162 return; 163 } 164 165 int 166 ehci_fdt_detach(struct device *self, int flags) 167 { 168 struct ehci_fdt_softc *sc = (struct ehci_fdt_softc *)self; 169 int rv; 170 171 rv = ehci_detach(self, flags); 172 if (rv) 173 return rv; 174 175 if (sc->sc_ih != NULL) { 176 fdt_intr_disestablish(sc->sc_ih); 177 sc->sc_ih = NULL; 178 } 179 180 if (sc->sc.sc_size) { 181 bus_space_unmap(sc->sc.iot, sc->sc_ioh, sc->sc_size); 182 sc->sc.sc_size = 0; 183 } 184 185 clock_disable_all(sc->sc_node); 186 return 0; 187 } 188 189 struct ehci_phy { 190 const char *compat; 191 void (*init)(struct ehci_fdt_softc *, uint32_t *); 192 }; 193 194 void sun4i_phy_init(struct ehci_fdt_softc *, uint32_t *); 195 void sun9i_phy_init(struct ehci_fdt_softc *, uint32_t *); 196 197 struct ehci_phy ehci_phys[] = { 198 { "allwinner,sun4i-a10-usb-phy", sun4i_phy_init }, 199 { "allwinner,sun5i-a13-usb-phy", sun4i_phy_init }, 200 { "allwinner,sun6i-a31-usb-phy", sun4i_phy_init }, 201 { "allwinner,sun7i-a20-usb-phy", sun4i_phy_init }, 202 { "allwinner,sun8i-a23-usb-phy", sun4i_phy_init }, 203 { "allwinner,sun8i-a33-usb-phy", sun4i_phy_init }, 204 { "allwinner,sun8i-h3-usb-phy", sun4i_phy_init }, 205 { "allwinner,sun8i-r40-usb-phy", sun4i_phy_init }, 206 { "allwinner,sun8i-v3s-usb-phy", sun4i_phy_init }, 207 { "allwinner,sun50i-h6-usb-phy", sun4i_phy_init }, 208 { "allwinner,sun50i-a64-usb-phy", sun4i_phy_init }, 209 { "allwinner,sun9i-a80-usb-phy", sun9i_phy_init }, 210 }; 211 212 uint32_t * 213 ehci_next_phy(uint32_t *cells) 214 { 215 uint32_t phandle = cells[0]; 216 int node, ncells; 217 218 node = OF_getnodebyphandle(phandle); 219 if (node == 0) 220 return NULL; 221 222 ncells = OF_getpropint(node, "#phy-cells", 0); 223 return cells + ncells + 1; 224 } 225 226 void 227 ehci_init_phy(struct ehci_fdt_softc *sc, uint32_t *cells) 228 { 229 uint32_t phy_supply; 230 int node; 231 int i; 232 233 node = OF_getnodebyphandle(cells[0]); 234 if (node == 0) 235 return; 236 237 for (i = 0; i < nitems(ehci_phys); i++) { 238 if (OF_is_compatible(node, ehci_phys[i].compat)) { 239 ehci_phys[i].init(sc, cells); 240 return; 241 } 242 } 243 244 phy_supply = OF_getpropint(node, "phy-supply", 0); 245 if (phy_supply) 246 regulator_enable(phy_supply); 247 } 248 249 void 250 ehci_init_phys(struct ehci_fdt_softc *sc) 251 { 252 uint32_t *phys; 253 uint32_t *phy; 254 int len; 255 256 len = OF_getproplen(sc->sc_node, "phys"); 257 if (len <= 0) 258 return; 259 260 phys = malloc(len, M_TEMP, M_WAITOK); 261 OF_getpropintarray(sc->sc_node, "phys", phys, len); 262 263 phy = phys; 264 while (phy && phy < phys + (len / sizeof(uint32_t))) { 265 ehci_init_phy(sc, phy); 266 phy = ehci_next_phy(phy); 267 } 268 269 free(phys, M_TEMP, len); 270 } 271 272 /* 273 * Allwinner PHYs. 274 */ 275 276 /* Registers */ 277 #define SUNXI_HCI_ICR 0x800 278 #define SUNXI_ULPI_BYPASS (1 << 0) 279 #define SUNXI_AHB_INCRX_ALIGN (1 << 8) 280 #define SUNXI_AHB_INCR4 (1 << 9) 281 #define SUNXI_AHB_INCR8 (1 << 10) 282 #define SUNXI_AHB_INCR16 (1 << 11) 283 284 void 285 sun4i_phy_init(struct ehci_fdt_softc *sc, uint32_t *cells) 286 { 287 uint32_t vbus_supply; 288 char name[32]; 289 uint32_t val; 290 int node; 291 292 node = OF_getnodebyphandle(cells[0]); 293 if (node == -1) 294 return; 295 296 val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, SUNXI_HCI_ICR); 297 val |= SUNXI_AHB_INCR8 | SUNXI_AHB_INCR4; 298 val |= SUNXI_AHB_INCRX_ALIGN; 299 val |= SUNXI_ULPI_BYPASS; 300 bus_space_write_4(sc->sc.iot, sc->sc.ioh, SUNXI_HCI_ICR, val); 301 302 /* 303 * We need to poke an undocumented register to make the PHY 304 * work on Allwinner A64/H3/H5/R40. 305 */ 306 if (OF_is_compatible(node, "allwinner,sun8i-h3-usb-phy") || 307 OF_is_compatible(node, "allwinner,sun8i-r40-usb-phy") || 308 OF_is_compatible(node, "allwinner,sun50i-h6-usb-phy") || 309 OF_is_compatible(node, "allwinner,sun50i-a64-usb-phy")) { 310 val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, 0x810); 311 val &= ~(1 << 1); 312 bus_space_write_4(sc->sc.iot, sc->sc.ioh, 0x810, val); 313 } 314 315 pinctrl_byname(node, "default"); 316 317 /* 318 * On sun4i, sun5i and sun7i, there is a single clock. The 319 * more recent SoCs have a separate clock for each PHY. 320 */ 321 if (OF_is_compatible(node, "allwinner,sun4i-a10-usb-phy") || 322 OF_is_compatible(node, "allwinner,sun5i-a13-usb-phy") || 323 OF_is_compatible(node, "allwinner,sun7i-a20-usb-phy")) { 324 clock_enable(node, "usb_phy"); 325 } else { 326 snprintf(name, sizeof(name), "usb%d_phy", cells[1]); 327 clock_enable(node, name); 328 } 329 330 snprintf(name, sizeof(name), "usb%d_reset", cells[1]); 331 reset_deassert(node, name); 332 333 snprintf(name, sizeof(name), "usb%d_vbus-supply", cells[1]); 334 vbus_supply = OF_getpropint(node, name, 0); 335 if (vbus_supply) 336 regulator_enable(vbus_supply); 337 } 338 339 void 340 sun9i_phy_init(struct ehci_fdt_softc *sc, uint32_t *cells) 341 { 342 uint32_t phy_supply; 343 uint32_t val; 344 int node; 345 346 node = OF_getnodebyphandle(cells[0]); 347 if (node == -1) 348 return; 349 350 pinctrl_byname(node, "default"); 351 clock_enable(node, "phy"); 352 reset_deassert(node, "phy"); 353 354 val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, SUNXI_HCI_ICR); 355 val |= SUNXI_AHB_INCR16 | SUNXI_AHB_INCR8 | SUNXI_AHB_INCR4; 356 val |= SUNXI_AHB_INCRX_ALIGN; 357 val |= SUNXI_ULPI_BYPASS; 358 bus_space_write_4(sc->sc.iot, sc->sc.ioh, SUNXI_HCI_ICR, val); 359 360 phy_supply = OF_getpropint(node, "phy-supply", 0); 361 if (phy_supply) 362 regulator_enable(phy_supply); 363 } 364