1/* $OpenBSD: exehci.c,v 1.11 2021/10/24 17:52:27 mpi 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 22#include <machine/intr.h> 23#include <machine/bus.h> 24#include <machine/fdt.h> 25 26#include <dev/usb/usb.h> 27#include <dev/usb/usbdi.h> 28#include <dev/usb/usbdivar.h> 29 30#include <dev/ofw/openfirm.h> 31#include <dev/ofw/ofw_clock.h> 32#include <dev/ofw/ofw_gpio.h> 33#include <dev/ofw/ofw_misc.h> 34#include <dev/ofw/fdt.h> 35 36#include <dev/usb/ehcireg.h> 37#include <dev/usb/ehcivar.h> 38 39/* registers */ 40#define USBPHY_CTRL0 0x00 41#define USBPHY_TUNE0 0x04 42#define HSICPHY_CTRL1 0x10 43#define HSICPHY_TUNE1 0x14 44#define HSICPHY_CTRL2 0x20 45#define HSICPHY_TUNE2 0x24 46#define EHCI_CTRL 0x30 47#define OHCI_CTRL 0x34 48#define USBOTG_SYS 0x38 49#define USBOTG_TUNE 0x40 50 51/* bits and bytes */ 52#define CLK_24MHZ 5 53 54#define HOST_CTRL0_PHYSWRSTALL (1U << 31) 55#define HOST_CTRL0_COMMONON_N (1 << 9) 56#define HOST_CTRL0_SIDDQ (1 << 6) 57#define HOST_CTRL0_FORCESLEEP (1 << 5) 58#define HOST_CTRL0_FORCESUSPEND (1 << 4) 59#define HOST_CTRL0_WORDINTERFACE (1 << 3) 60#define HOST_CTRL0_UTMISWRST (1 << 2) 61#define HOST_CTRL0_LINKSWRST (1 << 1) 62#define HOST_CTRL0_PHYSWRST (1 << 0) 63 64#define HOST_CTRL0_FSEL_MASK (7 << 16) 65 66#define EHCI_CTRL_ENAINCRXALIGN (1 << 29) 67#define EHCI_CTRL_ENAINCR4 (1 << 28) 68#define EHCI_CTRL_ENAINCR8 (1 << 27) 69#define EHCI_CTRL_ENAINCR16 (1 << 26) 70 71/* SYSREG registers */ 72#define USB20PHY_CFG 0x230 73#define USB20PHY_CFG_HOST_LINK_EN (1 << 0) 74 75/* PMU registers */ 76#define USB_HOST_POWER_5250 0x708 77#define USB_HOST_POWER_54XX 0x70c 78#define USB_HOST_POWER_EN (1 << 0) 79 80int exehci_match(struct device *, void *, void *); 81void exehci_attach(struct device *, struct device *, void *); 82int exehci_detach(struct device *, int); 83 84struct exehci_softc { 85 struct ehci_softc sc; 86 void *sc_ih; 87 int sc_phy; 88 bus_space_handle_t ph_ioh; 89}; 90 91const struct cfattach exehci_ca = { 92 sizeof (struct exehci_softc), exehci_match, exehci_attach, 93 exehci_detach 94}; 95 96struct cfdriver exehci_cd = { 97 NULL, "exehci", DV_DULL 98}; 99 100void exehci_setup(struct exehci_softc *); 101 102int 103exehci_match(struct device *parent, void *match, void *aux) 104{ 105 struct fdt_attach_args *faa = aux; 106 107 return OF_is_compatible(faa->fa_node, "samsung,exynos4210-ehci"); 108} 109 110void 111exehci_attach(struct device *parent, struct device *self, void *aux) 112{ 113 struct exehci_softc *sc = (struct exehci_softc *)self; 114 struct fdt_attach_args *faa = aux; 115 usbd_status r; 116 char *devname = sc->sc.sc_bus.bdev.dv_xname; 117 uint32_t phys[2]; 118 uint32_t phy_reg[2]; 119 int node; 120 121 if (faa->fa_nreg < 1) 122 return; 123 124 node = OF_child(faa->fa_node); 125 if (node == 0) 126 node = faa->fa_node; 127 128 if (OF_getpropintarray(node, "phys", phys, 129 sizeof(phys)) != sizeof(phys)) 130 return; 131 132 sc->sc_phy = OF_getnodebyphandle(phys[0]); 133 if (sc->sc_phy == 0) 134 return; 135 136 if (OF_getpropintarray(sc->sc_phy, "reg", phy_reg, 137 sizeof(phy_reg)) != sizeof(phy_reg)) 138 return; 139 140 sc->sc.iot = faa->fa_iot; 141 sc->sc.sc_bus.dmatag = faa->fa_dmat; 142 sc->sc.sc_size = faa->fa_reg[0].size; 143 144 /* Map I/O space */ 145 if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr, 146 faa->fa_reg[0].size, 0, &sc->sc.ioh)) { 147 printf(": cannot map mem space\n"); 148 goto out; 149 } 150 151 if (bus_space_map(sc->sc.iot, phy_reg[0], 152 phy_reg[1], 0, &sc->ph_ioh)) { 153 printf(": cannot map mem space\n"); 154 goto mem0; 155 } 156 157 printf("\n"); 158 159 clock_enable_all(faa->fa_node); 160 161 exehci_setup(sc); 162 163 sc->sc_ih = arm_intr_establish_fdt(faa->fa_node, IPL_USB, 164 ehci_intr, &sc->sc, devname); 165 if (sc->sc_ih == NULL) { 166 printf(": unable to establish interrupt\n"); 167 goto mem1; 168 } 169 170 strlcpy(sc->sc.sc_vendor, "Exynos 5", sizeof(sc->sc.sc_vendor)); 171 r = ehci_init(&sc->sc); 172 if (r != USBD_NORMAL_COMPLETION) { 173 printf("%s: init failed, error=%d\n", devname, r); 174 goto intr; 175 } 176 177 config_found(self, &sc->sc.sc_bus, usbctlprint); 178 179 goto out; 180 181intr: 182 arm_intr_disestablish(sc->sc_ih); 183 sc->sc_ih = NULL; 184mem1: 185 bus_space_unmap(sc->sc.iot, sc->ph_ioh, phy_reg[1]); 186mem0: 187 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); 188 sc->sc.sc_size = 0; 189out: 190 return; 191} 192 193int 194exehci_detach(struct device *self, int flags) 195{ 196 struct exehci_softc *sc = (struct exehci_softc *)self; 197 int rv; 198 199 rv = ehci_detach(self, flags); 200 if (rv) 201 return rv; 202 203 if (sc->sc_ih != NULL) { 204 arm_intr_disestablish(sc->sc_ih); 205 sc->sc_ih = NULL; 206 } 207 208 if (sc->sc.sc_size) { 209 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); 210 sc->sc.sc_size = 0; 211 } 212 213 return 0; 214} 215 216void 217exehci_setup(struct exehci_softc *sc) 218{ 219 struct regmap *pmurm, *sysrm; 220 uint32_t pmureg, sysreg; 221 bus_size_t offset; 222 uint32_t val; 223 int node; 224 225#if 0 226 /* VBUS, GPIO_X11, only on SMDK5250 and Chromebooks */ 227 exgpio_set_dir(0xa9, EXGPIO_DIR_OUT); 228 exgpio_set_bit(0xa9); 229 delay(3000); 230#endif 231 232 /* Enable host mode. */ 233 sysreg = OF_getpropint(sc->sc_phy, "samsung,sysreg-phandle", 0); 234 sysrm = regmap_byphandle(sysreg); 235 if (sysrm) { 236 val = regmap_read_4(sysrm, USB20PHY_CFG); 237 val |= USB20PHY_CFG_HOST_LINK_EN; 238 regmap_write_4(sysrm, USB20PHY_CFG, val); 239 } 240 241 /* Power up the PHY block. */ 242 pmureg = OF_getpropint(sc->sc_phy, "samsung,pmureg-phandle", 0); 243 pmurm = regmap_byphandle(pmureg); 244 if (pmurm) { 245 node = OF_getnodebyphandle(pmureg); 246 if (OF_is_compatible(node, "samsung,exynos5250-pmu")) 247 offset = USB_HOST_POWER_5250; 248 else 249 offset = USB_HOST_POWER_54XX; 250 251 val = regmap_read_4(pmurm, offset); 252 val |= USB_HOST_POWER_EN; 253 regmap_write_4(pmurm, offset, val); 254 } 255 256 delay(10000); 257 258 /* Setting up host and device simultaneously */ 259 val = bus_space_read_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0); 260 val &= ~(HOST_CTRL0_FSEL_MASK | 261 /* HOST Phy setting */ 262 HOST_CTRL0_PHYSWRST | 263 HOST_CTRL0_PHYSWRSTALL | 264 HOST_CTRL0_SIDDQ | 265 HOST_CTRL0_FORCESUSPEND | 266 HOST_CTRL0_FORCESLEEP); 267 val |= (/* Setting up the ref freq */ 268 CLK_24MHZ << 16 | 269 HOST_CTRL0_COMMONON_N | 270 /* HOST Phy setting */ 271 HOST_CTRL0_LINKSWRST | 272 HOST_CTRL0_UTMISWRST); 273 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0, val); 274 delay(10000); 275 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0, 276 bus_space_read_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0) & 277 ~(HOST_CTRL0_LINKSWRST | HOST_CTRL0_UTMISWRST)); 278 delay(20000); 279 280 /* EHCI Ctrl setting */ 281 bus_space_write_4(sc->sc.iot, sc->ph_ioh, EHCI_CTRL, 282 bus_space_read_4(sc->sc.iot, sc->ph_ioh, EHCI_CTRL) | 283 EHCI_CTRL_ENAINCRXALIGN | 284 EHCI_CTRL_ENAINCR4 | 285 EHCI_CTRL_ENAINCR8 | 286 EHCI_CTRL_ENAINCR16); 287 288#if 0 289 /* HSIC USB Hub initialization. */ 290 if (1) { 291 exgpio_set_dir(0xc8, EXGPIO_DIR_OUT); 292 exgpio_clear_bit(0xc8); 293 delay(1000); 294 exgpio_set_bit(0xc8); 295 delay(5000); 296 297 val = bus_space_read_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1); 298 val &= ~(HOST_CTRL0_SIDDQ | 299 HOST_CTRL0_FORCESLEEP | 300 HOST_CTRL0_FORCESUSPEND); 301 bus_space_write_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1, val); 302 val |= HOST_CTRL0_PHYSWRST; 303 bus_space_write_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1, val); 304 delay(1000); 305 val &= ~HOST_CTRL0_PHYSWRST; 306 bus_space_write_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1, val); 307 } 308#endif 309 310 /* PHY clock and power setup time */ 311 delay(50000); 312} 313