octxctl.c revision 1.1
1/* $OpenBSD: octxctl.c,v 1.1 2017/08/01 16:18:12 visa Exp $ */ 2 3/* 4 * Copyright (c) 2017 Visa Hankala 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19/* 20 * Driver for OCTEON USB3 controller bridge. 21 */ 22 23#include <sys/param.h> 24#include <sys/systm.h> 25#include <sys/device.h> 26#include <sys/malloc.h> 27 28#include <machine/fdt.h> 29#include <machine/octeonvar.h> 30 31#include <dev/ofw/fdt.h> 32#include <dev/ofw/openfirm.h> 33 34#include <octeon/dev/iobusvar.h> 35#include <octeon/dev/octxctlreg.h> 36 37#define XCTL_RD_8(sc, reg) \ 38 bus_space_read_8((sc)->sc_iot, (sc)->sc_ioh, (reg)) 39#define XCTL_WR_8(sc, reg, val) \ 40 bus_space_write_8((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 41 42struct octxctl_softc { 43 struct device sc_dev; 44 bus_space_tag_t sc_iot; 45 bus_space_handle_t sc_ioh; 46}; 47 48int octxctl_match(struct device *, void *, void *); 49void octxctl_attach(struct device *, struct device *, void *); 50 51int octxctl_dwc3_init(struct octxctl_softc *, struct fdt_reg *); 52void octxctl_uctl_init(struct octxctl_softc *, uint64_t, uint64_t); 53uint8_t octxctl_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t); 54uint16_t octxctl_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t); 55uint32_t octxctl_read_4(bus_space_tag_t, bus_space_handle_t, bus_size_t); 56void octxctl_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t, 57 uint8_t); 58void octxctl_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t, 59 uint16_t); 60void octxctl_write_4(bus_space_tag_t, bus_space_handle_t, bus_size_t, 61 uint32_t); 62 63const struct cfattach octxctl_ca = { 64 sizeof(struct octxctl_softc), octxctl_match, octxctl_attach 65}; 66 67struct cfdriver octxctl_cd = { 68 NULL, "octxctl", DV_DULL 69}; 70 71bus_space_t octxctl_tag = { 72 .bus_base = PHYS_TO_XKPHYS(0, CCA_NC), 73 ._space_read_1 = octxctl_read_1, 74 ._space_read_2 = octxctl_read_2, 75 ._space_read_4 = octxctl_read_4, 76 ._space_write_1 = octxctl_write_1, 77 ._space_write_2 = octxctl_write_2, 78 ._space_write_4 = octxctl_write_4, 79 ._space_map = iobus_space_map, 80 ._space_unmap = iobus_space_unmap, 81 ._space_subregion = generic_space_region, 82 ._space_vaddr = generic_space_vaddr 83}; 84 85int 86octxctl_match(struct device *parent, void *match, void *aux) 87{ 88 struct fdt_attach_args *faa = aux; 89 int child; 90 91 if (OF_is_compatible(faa->fa_node, "cavium,octeon-7130-usb-uctl") == 0) 92 return 0; 93 if ((child = OF_child(faa->fa_node)) == 0) 94 return 0; 95 return OF_is_compatible(child, "cavium,octeon-7130-xhci"); 96} 97 98void 99octxctl_attach(struct device *parent, struct device *self, void *aux) 100{ 101 char clock_type_hs[32]; 102 char clock_type_ss[32]; 103 struct fdt_reg child_reg; 104 struct fdt_attach_args child_faa; 105 struct fdt_attach_args *faa = aux; 106 struct octxctl_softc *sc = (struct octxctl_softc *)self; 107 uint64_t clock_freq, clock_sel; 108 uint32_t reg[4]; 109 int child; 110 111 if (faa->fa_nreg != 1) { 112 printf(": expected one IO space, got %d\n", faa->fa_nreg); 113 return; 114 } 115 116 child = OF_child(faa->fa_node); 117 if (OF_getpropint(faa->fa_node, "#address-cells", 0) != 2 || 118 OF_getpropint(faa->fa_node, "#size-cells", 0) != 2) { 119 printf(": invalid fdt reg cells\n"); 120 return; 121 } 122 if (OF_getproplen(child, "reg") != sizeof(reg)) { 123 printf(": invalid child fdt reg\n"); 124 return; 125 } 126 OF_getpropintarray(child, "reg", reg, sizeof(reg)); 127 child_reg.addr = ((uint64_t)reg[0] << 32) | reg[1]; 128 child_reg.size = ((uint64_t)reg[2] << 32) | reg[3]; 129 130 clock_freq = OF_getpropint(faa->fa_node, "refclk-frequency", 0); 131 132 if (OF_getprop(faa->fa_node, "refclk-type-hs", clock_type_hs, 133 sizeof(clock_type_hs)) < 0) 134 goto error; 135 if (OF_getprop(faa->fa_node, "refclk-type-ss", clock_type_ss, 136 sizeof(clock_type_ss)) < 0) 137 goto error; 138 clock_sel = 0; 139 if (strcmp(clock_type_ss, "dlmc_ref_clk1") == 0) 140 clock_sel |= 1; 141 if (strcmp(clock_type_hs, "pll_ref_clk") == 0) 142 clock_sel |= 2; 143 144 sc->sc_iot = faa->fa_iot; 145 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 146 0, &sc->sc_ioh)) { 147 printf(": could not map registers\n"); 148 goto error; 149 } 150 151 octxctl_uctl_init(sc, clock_freq, clock_sel); 152 153 if (octxctl_dwc3_init(sc, &child_reg) != 0) { 154 /* Error message has been printed already. */ 155 goto error; 156 } 157 158 printf("\n"); 159 160 memset(&child_faa, 0, sizeof(child_faa)); 161 child_faa.fa_name = ""; 162 child_faa.fa_node = child; 163 child_faa.fa_iot = &octxctl_tag; 164 child_faa.fa_dmat = faa->fa_dmat; 165 child_faa.fa_reg = &child_reg; 166 child_faa.fa_nreg = 1; 167 /* child_faa.fa_intr is not utilized. */ 168 169 config_found(self, &child_faa, NULL); 170 171 return; 172 173error: 174 if (sc->sc_ioh != 0) 175 bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size); 176} 177 178void 179octxctl_uctl_init(struct octxctl_softc *sc, uint64_t clock_freq, 180 uint64_t clock_sel) 181{ 182 static const uint32_t clock_divs[] = { 1, 2, 4, 6, 8, 16, 24, 32 }; 183 uint64_t i, val; 184 uint64_t ioclock = octeon_ioclock_speed(); 185 uint64_t mpll_mult; 186 uint64_t refclk_fsel; 187 188 /* 189 * Put the bridge controller, USB core, PHY, and clock divider 190 * into reset. 191 */ 192 val = XCTL_RD_8(sc, XCTL_CTL); 193 val |= XCTL_CTL_UCTL_RST; 194 val |= XCTL_CTL_UAHC_RST; 195 val |= XCTL_CTL_UPHY_RST; 196 XCTL_WR_8(sc, XCTL_CTL, val); 197 val = XCTL_RD_8(sc, XCTL_CTL); 198 val |= XCTL_CTL_CLKDIV_RST; 199 XCTL_WR_8(sc, XCTL_CTL, val); 200 201 /* Select IO clock divisor. */ 202 for (i = 0; i < nitems(clock_divs); i++) { 203 if (ioclock / clock_divs[i] < 300000000) 204 break; 205 } 206 207 /* Update the divisor and enable the clock. */ 208 val = XCTL_RD_8(sc, XCTL_CTL); 209 val &= ~XCTL_CTL_CLKDIV_SEL; 210 val |= (i << XCTL_CTL_CLKDIV_SEL_SHIFT) & XCTL_CTL_CLKDIV_SEL; 211 val |= XCTL_CTL_CLK_EN; 212 XCTL_WR_8(sc, XCTL_CTL, val); 213 214 /* Take the clock divider out of reset. */ 215 val = XCTL_RD_8(sc, XCTL_CTL); 216 val &= ~XCTL_CTL_CLKDIV_RST; 217 XCTL_WR_8(sc, XCTL_CTL, val); 218 219 /* Select the reference clock. */ 220 switch (clock_freq) { 221 case 50000000: 222 refclk_fsel = 0x07; 223 mpll_mult = 0x32; 224 break; 225 case 125000000: 226 refclk_fsel = 0x07; 227 mpll_mult = 0x28; 228 break; 229 case 100000000: 230 default: 231 if (clock_sel < 2) 232 refclk_fsel = 0x07; 233 else 234 refclk_fsel = 0x27; 235 mpll_mult = 0x19; 236 break; 237 } 238 239 /* Set the clock and power up PHYs. */ 240 val = XCTL_RD_8(sc, XCTL_CTL); 241 val &= ~XCTL_CTL_REFCLK_SEL; 242 val |= clock_sel << XCTL_CTL_REFCLK_SEL_SHIFT; 243 val &= ~XCTL_CTL_REFCLK_DIV2; 244 val &= ~XCTL_CTL_REFCLK_FSEL; 245 val |= refclk_fsel << XCTL_CTL_REFCLK_FSEL_SHIFT; 246 val &= ~XCTL_CTL_MPLL_MULT; 247 val |= mpll_mult << XCTL_CTL_MPLL_MULT_SHIFT; 248 val |= XCTL_CTL_SSC_EN; 249 val |= XCTL_CTL_REFCLK_SSP_EN; 250 val |= XCTL_CTL_SSPOWER_EN; 251 val |= XCTL_CTL_HSPOWER_EN; 252 XCTL_WR_8(sc, XCTL_CTL, val); 253 254 delay(100); 255 256 /* Take the bridge out of reset. */ 257 val = XCTL_RD_8(sc, XCTL_CTL); 258 val &= ~XCTL_CTL_UCTL_RST; 259 XCTL_WR_8(sc, XCTL_CTL, val); 260 261 delay(100); 262 263 /* Disable port power control. */ 264 val = XCTL_RD_8(sc, XCTL_HOST_CFG); 265 val &= ~XCTL_HOST_CFG_PPC_EN; 266 XCTL_WR_8(sc, XCTL_HOST_CFG, val); 267 268 /* Enable host-only mode. */ 269 val = XCTL_RD_8(sc, XCTL_CTL); 270 val &= ~XCTL_CTL_DRD_MODE; 271 XCTL_WR_8(sc, XCTL_CTL, val); 272 273 delay(100); 274 275 /* Take the USB core out of reset. */ 276 val = XCTL_RD_8(sc, XCTL_CTL); 277 val &= ~XCTL_CTL_UAHC_RST; 278 XCTL_WR_8(sc, XCTL_CTL, val); 279 280 delay(100); 281 282 val = XCTL_RD_8(sc, XCTL_CTL); 283 val |= XCTL_CTL_CSCLK_EN; 284 XCTL_WR_8(sc, XCTL_CTL, val); 285 286 /* Take the PHY out of reset. */ 287 val = XCTL_RD_8(sc, XCTL_CTL); 288 val &= ~XCTL_CTL_UPHY_RST; 289 XCTL_WR_8(sc, XCTL_CTL, val); 290 (void)XCTL_RD_8(sc, XCTL_CTL); 291 292 /* Fix endianess. */ 293 val = XCTL_RD_8(sc, XCTL_SHIM_CFG); 294 val &= ~XCTL_SHIM_CFG_CSR_BYTE_SWAP; 295 val &= ~XCTL_SHIM_CFG_DMA_BYTE_SWAP; 296 val |= 3ull << XCTL_SHIM_CFG_CSR_BYTE_SWAP_SHIFT; 297 val |= 1ull << XCTL_SHIM_CFG_DMA_BYTE_SWAP_SHIFT; 298 XCTL_WR_8(sc, XCTL_SHIM_CFG, val); 299 (void)XCTL_RD_8(sc, XCTL_SHIM_CFG); 300} 301 302int 303octxctl_dwc3_init(struct octxctl_softc *sc, struct fdt_reg *reg) 304{ 305 bus_space_handle_t ioh; 306 uint32_t rev; 307 uint32_t val; 308 int error = 0; 309 310 if (bus_space_map(sc->sc_iot, reg->addr, reg->size, 0, &ioh) != 0) { 311 printf(": could not map USB3 core registers\n"); 312 return EIO; 313 } 314 315 val = bus_space_read_4(sc->sc_iot, ioh, DWC3_GSNPSID); 316 if ((val & 0xffff0000u) != 0x55330000u) { 317 printf(": no DWC3 core\n"); 318 error = EIO; 319 goto out; 320 } 321 rev = val & 0xffffu; 322 printf(": DWC3 rev 0x%04x", rev); 323 324 val = bus_space_read_4(sc->sc_iot, ioh, DWC3_GUSB3PIPECTL(0)); 325 val &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX; 326 val |= DWC3_GUSB3PIPECTL_SUSPHY; 327 bus_space_write_4(sc->sc_iot, ioh, DWC3_GUSB3PIPECTL(0), val); 328 329 val = bus_space_read_4(sc->sc_iot, ioh, DWC3_GUSB2PHYCFG(0)); 330 val |= DWC3_GUSB2PHYCFG_SUSPHY; 331 bus_space_write_4(sc->sc_iot, ioh, DWC3_GUSB2PHYCFG(0), val); 332 333 /* Set the controller into host mode. */ 334 val = bus_space_read_4(sc->sc_iot, ioh, DWC3_GCTL); 335 val &= ~DWC3_GCTL_PRTCAP_MASK; 336 val |= DWC3_GCTL_PRTCAP_HOST; 337 bus_space_write_4(sc->sc_iot, ioh, DWC3_GCTL, val); 338 339 val = bus_space_read_4(sc->sc_iot, ioh, DWC3_GCTL); 340 val &= ~DWC3_GCTL_SCALEDOWN_MASK; 341 val &= ~DWC3_GCTL_DISSCRAMBLE; 342 if (rev >= DWC3_REV_210A && rev <= DWC3_REV_250A) 343 val |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC; 344 else 345 val &= ~DWC3_GCTL_DSBLCLKGTNG; 346 bus_space_write_4(sc->sc_iot, ioh, DWC3_GCTL, val); 347 348out: 349 bus_space_unmap(sc->sc_iot, ioh, reg->size); 350 351 return 0; 352} 353 354/* 355 * Bus access routines for xhci(4). 356 */ 357 358uint8_t 359octxctl_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) 360{ 361 return *(volatile uint8_t *)(h + (o ^ 3)); 362} 363 364uint16_t 365octxctl_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) 366{ 367 return *(volatile uint16_t *)(h + (o ^ 2)); 368} 369 370uint32_t 371octxctl_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) 372{ 373 return *(volatile uint32_t *)(h + o); 374} 375 376void 377octxctl_write_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, 378 uint8_t v) 379{ 380 *(volatile uint8_t *)(h + (o ^ 3)) = v; 381} 382 383void 384octxctl_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, 385 uint16_t v) 386{ 387 *(volatile uint16_t *)(h + (o ^ 2)) = v; 388} 389 390void 391octxctl_write_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, 392 uint32_t v) 393{ 394 *(volatile uint32_t *)(h + o) = v; 395} 396