1 /* $OpenBSD: ehci_fdt.c,v 1.6 2019/08/11 11:16:05 kettenis 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 struct ehci_fdt_softc { 44 struct ehci_softc sc; 45 int sc_node; 46 void *sc_ih; 47 }; 48 49 int ehci_fdt_match(struct device *, void *, void *); 50 void ehci_fdt_attach(struct device *, struct device *, void *); 51 int ehci_fdt_detach(struct device *, int); 52 53 struct cfattach ehci_fdt_ca = { 54 sizeof(struct ehci_fdt_softc), ehci_fdt_match, ehci_fdt_attach, 55 ehci_fdt_detach, ehci_activate 56 }; 57 58 void ehci_init_phys(struct ehci_fdt_softc *); 59 60 int 61 ehci_fdt_match(struct device *parent, void *match, void *aux) 62 { 63 struct fdt_attach_args *faa = aux; 64 65 return OF_is_compatible(faa->fa_node, "generic-ehci"); 66 } 67 68 void 69 ehci_fdt_attach(struct device *parent, struct device *self, void *aux) 70 { 71 struct ehci_fdt_softc *sc = (struct ehci_fdt_softc *)self; 72 struct fdt_attach_args *faa = aux; 73 char *devname = sc->sc.sc_bus.bdev.dv_xname; 74 usbd_status r; 75 76 if (faa->fa_nreg < 1) { 77 printf(": no registers\n"); 78 return; 79 } 80 81 sc->sc_node = faa->fa_node; 82 sc->sc.iot = faa->fa_iot; 83 sc->sc.sc_bus.dmatag = faa->fa_dmat; 84 sc->sc.sc_size = faa->fa_reg[0].size; 85 86 if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr, 87 faa->fa_reg[0].size, 0, &sc->sc.ioh)) { 88 printf(": can't map registers\n"); 89 goto out; 90 } 91 92 pinctrl_byname(sc->sc_node, "default"); 93 94 clock_enable_all(sc->sc_node); 95 reset_deassert_all(sc->sc_node); 96 97 /* Disable interrupts, so we don't get any spurious ones. */ 98 sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH); 99 EOWRITE2(&sc->sc, EHCI_USBINTR, 0); 100 101 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_USB, 102 ehci_intr, &sc->sc, devname); 103 if (sc->sc_ih == NULL) { 104 printf(": can't establish interrupt\n"); 105 clock_disable_all(sc->sc_node); 106 goto unmap; 107 } 108 109 printf("\n"); 110 111 ehci_init_phys(sc); 112 113 strlcpy(sc->sc.sc_vendor, "Generic", sizeof(sc->sc.sc_vendor)); 114 r = ehci_init(&sc->sc); 115 if (r != USBD_NORMAL_COMPLETION) { 116 printf("%s: init failed, error=%d\n", devname, r); 117 clock_disable_all(sc->sc_node); 118 goto disestablish_intr; 119 } 120 121 /* Attach usb device. */ 122 config_found(self, &sc->sc.sc_bus, usbctlprint); 123 return; 124 125 disestablish_intr: 126 fdt_intr_disestablish(sc->sc_ih); 127 sc->sc_ih = NULL; 128 unmap: 129 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); 130 sc->sc.sc_size = 0; 131 out: 132 return; 133 } 134 135 int 136 ehci_fdt_detach(struct device *self, int flags) 137 { 138 struct ehci_fdt_softc *sc = (struct ehci_fdt_softc *)self; 139 int rv; 140 141 rv = ehci_detach(self, flags); 142 if (rv) 143 return rv; 144 145 if (sc->sc_ih != NULL) { 146 fdt_intr_disestablish(sc->sc_ih); 147 sc->sc_ih = NULL; 148 } 149 150 if (sc->sc.sc_size) { 151 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); 152 sc->sc.sc_size = 0; 153 } 154 155 clock_disable_all(sc->sc_node); 156 return 0; 157 } 158 159 struct ehci_phy { 160 const char *compat; 161 void (*init)(struct ehci_fdt_softc *, uint32_t *); 162 }; 163 164 void sun4i_phy_init(struct ehci_fdt_softc *, uint32_t *); 165 void sun9i_phy_init(struct ehci_fdt_softc *, uint32_t *); 166 167 struct ehci_phy ehci_phys[] = { 168 { "allwinner,sun4i-a10-usb-phy", sun4i_phy_init }, 169 { "allwinner,sun5i-a13-usb-phy", sun4i_phy_init }, 170 { "allwinner,sun6i-a31-usb-phy", sun4i_phy_init }, 171 { "allwinner,sun7i-a20-usb-phy", sun4i_phy_init }, 172 { "allwinner,sun8i-a23-usb-phy", sun4i_phy_init }, 173 { "allwinner,sun8i-a33-usb-phy", sun4i_phy_init }, 174 { "allwinner,sun8i-h3-usb-phy", sun4i_phy_init }, 175 { "allwinner,sun8i-r40-usb-phy", sun4i_phy_init }, 176 { "allwinner,sun8i-v3s-usb-phy", sun4i_phy_init }, 177 { "allwinner,sun50i-a64-usb-phy", sun4i_phy_init }, 178 { "allwinner,sun9i-a80-usb-phy", sun9i_phy_init }, 179 }; 180 181 uint32_t * 182 ehci_next_phy(uint32_t *cells) 183 { 184 uint32_t phandle = cells[0]; 185 int node, ncells; 186 187 node = OF_getnodebyphandle(phandle); 188 if (node == 0) 189 return NULL; 190 191 ncells = OF_getpropint(node, "#phy-cells", 0); 192 return cells + ncells + 1; 193 } 194 195 void 196 ehci_init_phy(struct ehci_fdt_softc *sc, uint32_t *cells) 197 { 198 uint32_t phy_supply; 199 int node; 200 int i; 201 202 node = OF_getnodebyphandle(cells[0]); 203 if (node == 0) 204 return; 205 206 for (i = 0; i < nitems(ehci_phys); i++) { 207 if (OF_is_compatible(node, ehci_phys[i].compat)) { 208 ehci_phys[i].init(sc, cells); 209 return; 210 } 211 } 212 213 phy_supply = OF_getpropint(node, "phy-supply", 0); 214 if (phy_supply) 215 regulator_enable(phy_supply); 216 } 217 218 void 219 ehci_init_phys(struct ehci_fdt_softc *sc) 220 { 221 uint32_t *phys; 222 uint32_t *phy; 223 int len; 224 225 len = OF_getproplen(sc->sc_node, "phys"); 226 if (len <= 0) 227 return; 228 229 phys = malloc(len, M_TEMP, M_WAITOK); 230 OF_getpropintarray(sc->sc_node, "phys", phys, len); 231 232 phy = phys; 233 while (phy && phy < phys + (len / sizeof(uint32_t))) { 234 ehci_init_phy(sc, phy); 235 phy = ehci_next_phy(phy); 236 } 237 238 free(phys, M_TEMP, len); 239 } 240 241 /* 242 * Allwinner PHYs. 243 */ 244 245 /* Registers */ 246 #define SUNXI_HCI_ICR 0x800 247 #define SUNXI_ULPI_BYPASS (1 << 0) 248 #define SUNXI_AHB_INCRX_ALIGN (1 << 8) 249 #define SUNXI_AHB_INCR4 (1 << 9) 250 #define SUNXI_AHB_INCR8 (1 << 10) 251 #define SUNXI_AHB_INCR16 (1 << 11) 252 253 void 254 sun4i_phy_init(struct ehci_fdt_softc *sc, uint32_t *cells) 255 { 256 uint32_t vbus_supply; 257 char name[32]; 258 uint32_t val; 259 int node; 260 261 node = OF_getnodebyphandle(cells[0]); 262 if (node == -1) 263 return; 264 265 val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, SUNXI_HCI_ICR); 266 val |= SUNXI_AHB_INCR8 | SUNXI_AHB_INCR4; 267 val |= SUNXI_AHB_INCRX_ALIGN; 268 val |= SUNXI_ULPI_BYPASS; 269 bus_space_write_4(sc->sc.iot, sc->sc.ioh, SUNXI_HCI_ICR, val); 270 271 /* 272 * We need to poke an undocumented register to make the PHY 273 * work on Allwinner A64/H3/H5/R40. 274 */ 275 if (OF_is_compatible(node, "allwinner,sun8i-h3-usb-phy") || 276 OF_is_compatible(node, "allwinner,sun8i-r40-usb-phy") || 277 OF_is_compatible(node, "allwinner,sun50i-a64-usb-phy")) { 278 val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, 0x810); 279 val &= ~(1 << 1); 280 bus_space_write_4(sc->sc.iot, sc->sc.ioh, 0x810, val); 281 } 282 283 pinctrl_byname(node, "default"); 284 285 /* 286 * On sun4i, sun5i and sun7i, there is a single clock. The 287 * more recent SoCs have a separate clock for each PHY. 288 */ 289 if (OF_is_compatible(node, "allwinner,sun4i-a10-usb-phy") || 290 OF_is_compatible(node, "allwinner,sun5i-a13-usb-phy") || 291 OF_is_compatible(node, "allwinner,sun7i-a20-usb-phy")) { 292 clock_enable(node, "usb_phy"); 293 } else { 294 snprintf(name, sizeof(name), "usb%d_phy", cells[1]); 295 clock_enable(node, name); 296 } 297 298 snprintf(name, sizeof(name), "usb%d_reset", cells[1]); 299 reset_deassert(node, name); 300 301 snprintf(name, sizeof(name), "usb%d_vbus-supply", cells[1]); 302 vbus_supply = OF_getpropint(node, name, 0); 303 if (vbus_supply) 304 regulator_enable(vbus_supply); 305 } 306 307 void 308 sun9i_phy_init(struct ehci_fdt_softc *sc, uint32_t *cells) 309 { 310 uint32_t phy_supply; 311 uint32_t val; 312 int node; 313 314 node = OF_getnodebyphandle(cells[0]); 315 if (node == -1) 316 return; 317 318 pinctrl_byname(node, "default"); 319 clock_enable(node, "phy"); 320 reset_deassert(node, "phy"); 321 322 val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, SUNXI_HCI_ICR); 323 val |= SUNXI_AHB_INCR16 | SUNXI_AHB_INCR8 | SUNXI_AHB_INCR4; 324 val |= SUNXI_AHB_INCRX_ALIGN; 325 val |= SUNXI_ULPI_BYPASS; 326 bus_space_write_4(sc->sc.iot, sc->sc.ioh, SUNXI_HCI_ICR, val); 327 328 phy_supply = OF_getpropint(node, "phy-supply", 0); 329 if (phy_supply) 330 regulator_enable(phy_supply); 331 } 332