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