1 /* $OpenBSD: exehci.c,v 1.4 2016/07/26 22:10:10 patrick Exp $ */ 2 /* 3 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 #include <sys/kernel.h> 22 #include <sys/rwlock.h> 23 #include <sys/timeout.h> 24 25 #include <machine/intr.h> 26 #include <machine/bus.h> 27 #if NFDT > 0 28 #include <machine/fdt.h> 29 #endif 30 31 #include <dev/usb/usb.h> 32 #include <dev/usb/usbdi.h> 33 #include <dev/usb/usbdivar.h> 34 #include <dev/usb/usb_mem.h> 35 36 #include <armv7/armv7/armv7var.h> 37 #include <armv7/exynos/exsysregvar.h> 38 #include <armv7/exynos/expowervar.h> 39 #include <armv7/exynos/exgpiovar.h> 40 41 #include <dev/usb/ehcireg.h> 42 #include <dev/usb/ehcivar.h> 43 44 /* registers */ 45 #define USBPHY_CTRL0 0x00 46 #define USBPHY_TUNE0 0x04 47 #define HSICPHY_CTRL1 0x10 48 #define HSICPHY_TUNE1 0x14 49 #define HSICPHY_CTRL2 0x20 50 #define HSICPHY_TUNE2 0x24 51 #define EHCI_CTRL 0x30 52 #define OHCI_CTRL 0x34 53 #define USBOTG_SYS 0x38 54 #define USBOTG_TUNE 0x40 55 56 /* bits and bytes */ 57 #define CLK_24MHZ 5 58 59 #define HOST_CTRL0_PHYSWRSTALL (1U << 31) 60 #define HOST_CTRL0_COMMONON_N (1 << 9) 61 #define HOST_CTRL0_SIDDQ (1 << 6) 62 #define HOST_CTRL0_FORCESLEEP (1 << 5) 63 #define HOST_CTRL0_FORCESUSPEND (1 << 4) 64 #define HOST_CTRL0_WORDINTERFACE (1 << 3) 65 #define HOST_CTRL0_UTMISWRST (1 << 2) 66 #define HOST_CTRL0_LINKSWRST (1 << 1) 67 #define HOST_CTRL0_PHYSWRST (1 << 0) 68 69 #define HOST_CTRL0_FSEL_MASK (7 << 16) 70 71 #define EHCI_CTRL_ENAINCRXALIGN (1 << 29) 72 #define EHCI_CTRL_ENAINCR4 (1 << 28) 73 #define EHCI_CTRL_ENAINCR8 (1 << 27) 74 #define EHCI_CTRL_ENAINCR16 (1 << 26) 75 76 int exehci_match(struct device *, void *, void *); 77 void exehci_attach(struct device *, struct device *, void *); 78 int exehci_detach(struct device *, int); 79 80 struct exehci_softc { 81 struct device sc_dev; 82 struct ehci_softc *sc_ehci; 83 void *sc_ih; 84 bus_dma_tag_t sc_dmat; 85 bus_space_tag_t sc_iot; 86 bus_space_handle_t sc_ioh; 87 bus_size_t sc_size; 88 bus_space_handle_t ph_ioh; 89 }; 90 91 struct cfdriver exehci_cd = { 92 NULL, "exehci", DV_DULL 93 }; 94 95 struct cfattach exehci_ca = { 96 sizeof (struct exehci_softc), NULL, exehci_attach, 97 exehci_detach, NULL 98 }; 99 struct cfattach exehci_fdt_ca = { 100 sizeof (struct exehci_softc), exehci_match, exehci_attach, 101 exehci_detach, NULL 102 }; 103 104 void exehci_setup(struct exehci_softc *); 105 106 int 107 exehci_match(struct device *parent, void *v, void *aux) 108 { 109 #if NFDT > 0 110 struct armv7_attach_args *aa = aux; 111 112 if (fdt_node_compatible("samsung,exynos4210-ehci", aa->aa_node)) 113 return 1; 114 #endif 115 116 return 0; 117 } 118 119 void 120 exehci_attach(struct device *parent, struct device *self, void *aux) 121 { 122 struct exehci_softc *sc = (struct exehci_softc *)self; 123 struct ehci_softc *esc; 124 struct armv7_attach_args *aa = aux; 125 struct armv7mem hmem, pmem; 126 int irq; 127 usbd_status r; 128 129 sc->sc_iot = aa->aa_iot; 130 sc->sc_dmat = aa->aa_dmat; 131 132 #if NFDT > 0 133 if (aa->aa_node) { 134 struct fdt_reg hreg, preg; 135 uint32_t ints[3]; 136 137 if (fdt_get_reg(aa->aa_node, 0, &hreg)) 138 panic("%s: could not extract memory data from FDT", 139 __func__); 140 141 /* XXX: In a different way, please. */ 142 void *node = fdt_find_compatible("samsung,exynos5250-usb2-phy"); 143 if (node == NULL || fdt_get_reg(node, 0, &preg)) 144 panic("%s: could not extract phy data from FDT", 145 __func__); 146 147 /* TODO: Add interrupt FDT API. */ 148 if (fdt_node_property_ints(aa->aa_node, "interrupts", 149 ints, 3) != 3) 150 panic("%s: could not extract interrupt data from FDT", 151 __func__); 152 153 hmem.addr = hreg.addr; 154 hmem.size = hreg.size; 155 pmem.addr = preg.addr; 156 pmem.size = preg.size; 157 158 irq = ints[1]; 159 } else 160 #endif 161 { 162 hmem.addr = aa->aa_dev->mem[0].addr; 163 hmem.size = aa->aa_dev->mem[0].size; 164 pmem.addr = aa->aa_dev->mem[1].addr; 165 pmem.size = aa->aa_dev->mem[1].size; 166 irq = aa->aa_dev->irq[0]; 167 } 168 169 /* Map I/O space */ 170 sc->sc_size = hmem.size; 171 if (bus_space_map(sc->sc_iot, hmem.addr, hmem.size, 0, &sc->sc_ioh)) { 172 printf(": cannot map mem space\n"); 173 goto out; 174 } 175 176 if (bus_space_map(sc->sc_iot, pmem.addr, pmem.size, 0, &sc->ph_ioh)) { 177 printf(": cannot map mem space\n"); 178 goto pmem; 179 } 180 181 printf("\n"); 182 183 exehci_setup(sc); 184 185 if ((esc = (struct ehci_softc *)config_found(self, NULL, NULL)) == NULL) 186 goto hmem; 187 188 sc->sc_ehci = esc; 189 esc->iot = sc->sc_iot; 190 esc->ioh = sc->sc_ioh; 191 esc->sc_bus.dmatag = aa->aa_dmat; 192 193 sc->sc_ih = arm_intr_establish(irq, IPL_USB, 194 ehci_intr, esc, esc->sc_bus.bdev.dv_xname); 195 if (sc->sc_ih == NULL) { 196 printf(": unable to establish interrupt\n"); 197 goto hmem; 198 } 199 200 strlcpy(esc->sc_vendor, "Exynos 5", sizeof(esc->sc_vendor)); 201 r = ehci_init(esc); 202 if (r != USBD_NORMAL_COMPLETION) { 203 printf("%s: init failed, error=%d\n", 204 esc->sc_bus.bdev.dv_xname, r); 205 goto intr; 206 } 207 208 printf("\n"); 209 210 config_found((struct device *)esc, &esc->sc_bus, usbctlprint); 211 212 goto out; 213 214 intr: 215 arm_intr_disestablish(sc->sc_ih); 216 sc->sc_ih = NULL; 217 hmem: 218 bus_space_unmap(sc->sc_iot, sc->ph_ioh, hmem.size); 219 pmem: 220 bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size); 221 sc->sc_size = 0; 222 out: 223 return; 224 } 225 226 int 227 exehci_detach(struct device *self, int flags) 228 { 229 struct exehci_softc *sc = (struct exehci_softc *)self; 230 int rv = 0; 231 232 rv = ehci_detach(self, flags); 233 if (rv) 234 return (rv); 235 236 if (sc->sc_ih != NULL) { 237 arm_intr_disestablish(sc->sc_ih); 238 sc->sc_ih = NULL; 239 } 240 241 if (sc->sc_size) { 242 bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size); 243 sc->sc_size = 0; 244 } 245 246 return 0; 247 } 248 249 void 250 exehci_setup(struct exehci_softc *sc) 251 { 252 uint32_t val; 253 254 /* VBUS, GPIO_X11, only on SMDK5250 and Chromebooks */ 255 exgpio_set_dir(0xa9, EXGPIO_DIR_OUT); 256 exgpio_set_bit(0xa9); 257 delay(3000); 258 259 exsysreg_usbhost_mode(1); 260 expower_usbhost_phy_ctrl(1); 261 262 delay(10000); 263 264 /* Setting up host and device simultaneously */ 265 val = bus_space_read_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0); 266 val &= ~(HOST_CTRL0_FSEL_MASK | 267 HOST_CTRL0_COMMONON_N | 268 /* HOST Phy setting */ 269 HOST_CTRL0_PHYSWRST | 270 HOST_CTRL0_PHYSWRSTALL | 271 HOST_CTRL0_SIDDQ | 272 HOST_CTRL0_FORCESUSPEND | 273 HOST_CTRL0_FORCESLEEP); 274 val |= (/* Setting up the ref freq */ 275 CLK_24MHZ << 16 | 276 /* HOST Phy setting */ 277 HOST_CTRL0_LINKSWRST | 278 HOST_CTRL0_UTMISWRST); 279 bus_space_write_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0, val); 280 delay(10000); 281 bus_space_write_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0, 282 bus_space_read_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0) & 283 ~(HOST_CTRL0_LINKSWRST | HOST_CTRL0_UTMISWRST)); 284 delay(20000); 285 286 /* EHCI Ctrl setting */ 287 bus_space_write_4(sc->sc_iot, sc->ph_ioh, EHCI_CTRL, 288 bus_space_read_4(sc->sc_iot, sc->ph_ioh, EHCI_CTRL) | 289 EHCI_CTRL_ENAINCRXALIGN | 290 EHCI_CTRL_ENAINCR4 | 291 EHCI_CTRL_ENAINCR8 | 292 EHCI_CTRL_ENAINCR16); 293 294 /* HSIC USB Hub initialization. */ 295 if (1) { 296 exgpio_set_dir(0xc8, EXGPIO_DIR_OUT); 297 exgpio_clear_bit(0xc8); 298 delay(1000); 299 exgpio_set_bit(0xc8); 300 delay(5000); 301 302 val = bus_space_read_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1); 303 val &= ~(HOST_CTRL0_SIDDQ | 304 HOST_CTRL0_FORCESLEEP | 305 HOST_CTRL0_FORCESUSPEND); 306 bus_space_write_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1, val); 307 val |= HOST_CTRL0_PHYSWRST; 308 bus_space_write_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1, val); 309 delay(1000); 310 val &= ~HOST_CTRL0_PHYSWRST; 311 bus_space_write_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1, val); 312 } 313 314 /* PHY clock and power setup time */ 315 delay(50000); 316 } 317 318 int ehci_ex_match(struct device *, void *, void *); 319 void ehci_ex_attach(struct device *, struct device *, void *); 320 321 struct cfattach ehci_ex_ca = { 322 sizeof (struct ehci_softc), ehci_ex_match, ehci_ex_attach 323 }; 324 325 int 326 ehci_ex_match(struct device *parent, void *v, void *aux) 327 { 328 return 1; 329 } 330 331 void 332 ehci_ex_attach(struct device *parent, struct device *self, void *aux) 333 { 334 } 335