1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2015 Landon Fuller <landon@landonf.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer, 12 * without modification. 13 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 14 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 15 * redistribution must be conditioned upon including a substantially 16 * similar Disclaimer requirement for further binary redistribution. 17 * 18 * NO WARRANTY 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 22 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 23 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 24 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 27 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29 * THE POSSIBILITY OF SUCH DAMAGES. 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 /* 36 * Broadcom Common PCI/PCIe Support. 37 * 38 * This base driver implementation is shared by the bhnd_pcib (root complex) 39 * and bhnd_pci_hostb (host bridge) drivers. 40 */ 41 42 #include <sys/param.h> 43 #include <sys/malloc.h> 44 #include <sys/kernel.h> 45 #include <sys/bus.h> 46 #include <sys/module.h> 47 #include <sys/systm.h> 48 49 #include <machine/bus.h> 50 #include <sys/rman.h> 51 #include <machine/resource.h> 52 53 #include <dev/bhnd/bhnd.h> 54 #include <dev/mdio/mdio.h> 55 56 #include "bhnd_pcireg.h" 57 #include "bhnd_pcivar.h" 58 59 static int bhnd_pcie_mdio_wait_idle(struct bhnd_pci_softc *sc); 60 static int bhnd_pcie_mdio_ioctl(struct bhnd_pci_softc *sc, uint32_t cmd); 61 static int bhnd_pcie_mdio_enable(struct bhnd_pci_softc *sc); 62 static void bhnd_pcie_mdio_disable(struct bhnd_pci_softc *sc); 63 static int bhnd_pcie_mdio_cmd_write(struct bhnd_pci_softc *sc, 64 uint32_t cmd); 65 static int bhnd_pcie_mdio_cmd_read(struct bhnd_pci_softc *sc, uint32_t cmd, 66 uint16_t *data_read); 67 68 static struct bhnd_device_quirk bhnd_pci_quirks[]; 69 static struct bhnd_device_quirk bhnd_pcie_quirks[]; 70 71 #define BHND_PCI_QUIRKS bhnd_pci_quirks 72 #define BHND_PCIE_QUIRKS bhnd_pcie_quirks 73 #define BHND_PCI_DEV(_core, _desc, ...) \ 74 { BHND_DEVICE(BCM, _core, _desc, BHND_ ## _core ## _QUIRKS, \ 75 ## __VA_ARGS__), BHND_PCI_REGFMT_ ## _core } 76 77 static const struct bhnd_pci_device { 78 struct bhnd_device device; 79 bhnd_pci_regfmt_t regfmt; /**< register format */ 80 } bhnd_pci_devs[] = { 81 BHND_PCI_DEV(PCI, "Host-PCI bridge", BHND_DF_HOSTB), 82 BHND_PCI_DEV(PCI, "PCI-BHND bridge", BHND_DF_SOC), 83 BHND_PCI_DEV(PCIE, "PCIe-G1 Host-PCI bridge", BHND_DF_HOSTB), 84 BHND_PCI_DEV(PCIE, "PCIe-G1 PCI-BHND bridge", BHND_DF_SOC), 85 86 { BHND_DEVICE_END, 0 } 87 }; 88 89 /* Device quirks tables */ 90 static struct bhnd_device_quirk bhnd_pci_quirks[] = { BHND_DEVICE_QUIRK_END }; 91 static struct bhnd_device_quirk bhnd_pcie_quirks[] = { 92 BHND_CORE_QUIRK(HWREV_GTE(10), BHND_PCI_QUIRK_SD_C22_EXTADDR), 93 94 BHND_DEVICE_QUIRK_END 95 }; 96 97 #define BHND_PCIE_MDIO_CTL_DELAY 10 /**< usec delay required between 98 * MDIO_CTL/MDIO_DATA accesses. */ 99 #define BHND_PCIE_MDIO_RETRY_DELAY 2000 /**< usec delay before retrying 100 * BHND_PCIE_MDIOCTL_DONE. */ 101 #define BHND_PCIE_MDIO_RETRY_COUNT 200 /**< number of times to loop waiting 102 * for BHND_PCIE_MDIOCTL_DONE. */ 103 104 #define BHND_PCI_READ_4(_sc, _reg) \ 105 bhnd_bus_read_4((_sc)->mem_res, (_reg)) 106 #define BHND_PCI_WRITE_4(_sc, _reg, _val) \ 107 bhnd_bus_write_4((_sc)->mem_res, (_reg), (_val)) 108 109 #define BHND_PCIE_ASSERT(sc) \ 110 KASSERT(bhnd_get_class(sc->dev) == BHND_DEVCLASS_PCIE, \ 111 ("not a pcie device!")); 112 113 int 114 bhnd_pci_generic_probe(device_t dev) 115 { 116 const struct bhnd_device *id; 117 118 id = bhnd_device_lookup(dev, &bhnd_pci_devs[0].device, 119 sizeof(bhnd_pci_devs[0])); 120 if (id == NULL) 121 return (ENXIO); 122 123 bhnd_set_custom_core_desc(dev, id->desc); 124 return (BUS_PROBE_DEFAULT); 125 } 126 127 int 128 bhnd_pci_generic_attach(device_t dev) 129 { 130 struct bhnd_pci_softc *sc; 131 int error; 132 133 sc = device_get_softc(dev); 134 sc->dev = dev; 135 sc->quirks = bhnd_device_quirks(dev, &bhnd_pci_devs[0].device, 136 sizeof(bhnd_pci_devs[0])); 137 138 /* Allocate bus resources */ 139 sc->mem_res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, 140 RF_ACTIVE); 141 if (sc->mem_res == NULL) 142 return (ENXIO); 143 144 BHND_PCI_LOCK_INIT(sc); 145 146 /* Probe and attach children */ 147 if ((error = bus_generic_attach(dev))) 148 goto cleanup; 149 150 return (0); 151 152 cleanup: 153 bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); 154 BHND_PCI_LOCK_DESTROY(sc); 155 156 return (error); 157 } 158 159 int 160 bhnd_pci_generic_detach(device_t dev) 161 { 162 struct bhnd_pci_softc *sc; 163 int error; 164 165 sc = device_get_softc(dev); 166 167 if ((error = bus_generic_detach(dev))) 168 return (error); 169 170 bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); 171 172 BHND_PCI_LOCK_DESTROY(sc); 173 174 return (0); 175 } 176 177 static struct resource_list * 178 bhnd_pci_get_resource_list(device_t dev, device_t child) 179 { 180 struct bhnd_pci_devinfo *dinfo; 181 182 if (device_get_parent(child) != dev) 183 return (NULL); 184 185 dinfo = device_get_ivars(child); 186 return (&dinfo->resources); 187 } 188 189 static device_t 190 bhnd_pci_add_child(device_t dev, u_int order, const char *name, int unit) 191 { 192 struct bhnd_pci_devinfo *dinfo; 193 device_t child; 194 195 child = device_add_child_ordered(dev, order, name, unit); 196 if (child == NULL) 197 return (NULL); 198 199 dinfo = malloc(sizeof(struct bhnd_pci_devinfo), M_DEVBUF, M_NOWAIT); 200 if (dinfo == NULL) { 201 device_delete_child(dev, child); 202 return (NULL); 203 } 204 205 resource_list_init(&dinfo->resources); 206 207 device_set_ivars(child, dinfo); 208 return (child); 209 } 210 211 static void 212 bhnd_pci_child_deleted(device_t dev, device_t child) 213 { 214 struct bhnd_pci_devinfo *dinfo; 215 216 if (device_get_parent(child) != dev) 217 return; 218 219 dinfo = device_get_ivars(child); 220 if (dinfo != NULL) { 221 resource_list_free(&dinfo->resources); 222 free(dinfo, M_DEVBUF); 223 } 224 225 device_set_ivars(child, NULL); 226 } 227 228 int 229 bhnd_pci_generic_suspend(device_t dev) 230 { 231 return (bus_generic_suspend(dev)); 232 } 233 234 int 235 bhnd_pci_generic_resume(device_t dev) 236 { 237 return (bus_generic_resume(dev)); 238 } 239 240 /** 241 * Read a 32-bit PCIe TLP/DLLP/PLP protocol register. 242 * 243 * @param sc The bhndb_pci driver state. 244 * @param addr The protocol register offset. 245 */ 246 uint32_t 247 bhnd_pcie_read_proto_reg(struct bhnd_pci_softc *sc, uint32_t addr) 248 { 249 uint32_t val; 250 251 BHND_PCIE_ASSERT(sc); 252 253 BHND_PCI_LOCK(sc); 254 BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_ADDR, addr); 255 val = BHND_PCI_READ_4(sc, BHND_PCIE_IND_DATA); 256 BHND_PCI_UNLOCK(sc); 257 258 return (val); 259 } 260 261 /** 262 * Write a 32-bit PCIe TLP/DLLP/PLP protocol register value. 263 * 264 * @param sc The bhndb_pci driver state. 265 * @param addr The protocol register offset. 266 * @param val The value to write to @p addr. 267 */ 268 void 269 bhnd_pcie_write_proto_reg(struct bhnd_pci_softc *sc, uint32_t addr, 270 uint32_t val) 271 { 272 BHND_PCIE_ASSERT(sc); 273 274 BHND_PCI_LOCK(sc); 275 BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_ADDR, addr); 276 BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_DATA, val); 277 BHND_PCI_UNLOCK(sc); 278 } 279 280 /* Spin until the MDIO device reports itself as idle, or timeout is reached. */ 281 static int 282 bhnd_pcie_mdio_wait_idle(struct bhnd_pci_softc *sc) 283 { 284 uint32_t ctl; 285 286 /* Spin waiting for the BUSY flag to clear */ 287 for (int i = 0; i < BHND_PCIE_MDIO_RETRY_COUNT; i++) { 288 ctl = BHND_PCI_READ_4(sc, BHND_PCIE_MDIO_CTL); 289 if ((ctl & BHND_PCIE_MDIOCTL_DONE)) 290 return (0); 291 292 DELAY(BHND_PCIE_MDIO_RETRY_DELAY); 293 } 294 295 return (ETIMEDOUT); 296 } 297 298 299 /** 300 * Write an MDIO IOCTL and wait for completion. 301 */ 302 static int 303 bhnd_pcie_mdio_ioctl(struct bhnd_pci_softc *sc, uint32_t cmd) 304 { 305 BHND_PCI_LOCK_ASSERT(sc, MA_OWNED); 306 307 BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_CTL, cmd); 308 DELAY(BHND_PCIE_MDIO_CTL_DELAY); 309 return (0); 310 } 311 312 /** 313 * Enable MDIO device 314 */ 315 static int 316 bhnd_pcie_mdio_enable(struct bhnd_pci_softc *sc) 317 { 318 uint32_t ctl; 319 320 BHND_PCIE_ASSERT(sc); 321 322 /* Enable MDIO clock and preamble mode */ 323 ctl = BHND_PCIE_MDIOCTL_PREAM_EN|BHND_PCIE_MDIOCTL_DIVISOR_VAL; 324 return (bhnd_pcie_mdio_ioctl(sc, ctl)); 325 } 326 327 /** 328 * Disable MDIO device. 329 */ 330 static void 331 bhnd_pcie_mdio_disable(struct bhnd_pci_softc *sc) 332 { 333 if (bhnd_pcie_mdio_ioctl(sc, 0)) 334 device_printf(sc->dev, "failed to disable MDIO clock\n"); 335 } 336 337 338 /** 339 * Issue a write command and wait for completion 340 */ 341 static int 342 bhnd_pcie_mdio_cmd_write(struct bhnd_pci_softc *sc, uint32_t cmd) 343 { 344 int error; 345 346 BHND_PCI_LOCK_ASSERT(sc, MA_OWNED); 347 348 cmd |= BHND_PCIE_MDIODATA_START|BHND_PCIE_MDIODATA_TA|BHND_PCIE_MDIODATA_CMD_WRITE; 349 350 BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_DATA, cmd); 351 DELAY(BHND_PCIE_MDIO_CTL_DELAY); 352 353 if ((error = bhnd_pcie_mdio_wait_idle(sc))) 354 return (error); 355 356 return (0); 357 } 358 359 /** 360 * Issue an an MDIO read command, wait for completion, and return 361 * the result in @p data_read. 362 */ 363 static int 364 bhnd_pcie_mdio_cmd_read(struct bhnd_pci_softc *sc, uint32_t cmd, 365 uint16_t *data_read) 366 { 367 int error; 368 369 BHND_PCI_LOCK_ASSERT(sc, MA_OWNED); 370 371 cmd |= BHND_PCIE_MDIODATA_START|BHND_PCIE_MDIODATA_TA|BHND_PCIE_MDIODATA_CMD_READ; 372 BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_DATA, cmd); 373 DELAY(BHND_PCIE_MDIO_CTL_DELAY); 374 375 if ((error = bhnd_pcie_mdio_wait_idle(sc))) 376 return (error); 377 378 *data_read = (BHND_PCI_READ_4(sc, BHND_PCIE_MDIO_DATA) & 379 BHND_PCIE_MDIODATA_DATA_MASK); 380 return (0); 381 } 382 383 384 int 385 bhnd_pcie_mdio_read(struct bhnd_pci_softc *sc, int phy, int reg) 386 { 387 uint32_t cmd; 388 uint16_t val; 389 int error; 390 391 /* Enable MDIO access */ 392 BHND_PCI_LOCK(sc); 393 bhnd_pcie_mdio_enable(sc); 394 395 /* Issue the read */ 396 cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg); 397 error = bhnd_pcie_mdio_cmd_read(sc, cmd, &val); 398 399 /* Disable MDIO access */ 400 bhnd_pcie_mdio_disable(sc); 401 BHND_PCI_UNLOCK(sc); 402 403 if (error) 404 return (~0U); 405 406 return (val); 407 } 408 409 int 410 bhnd_pcie_mdio_write(struct bhnd_pci_softc *sc, int phy, int reg, int val) 411 { 412 uint32_t cmd; 413 int error; 414 415 /* Enable MDIO access */ 416 BHND_PCI_LOCK(sc); 417 bhnd_pcie_mdio_enable(sc); 418 419 /* Issue the write */ 420 cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg) | (val & BHND_PCIE_MDIODATA_DATA_MASK); 421 error = bhnd_pcie_mdio_cmd_write(sc, cmd); 422 423 /* Disable MDIO access */ 424 bhnd_pcie_mdio_disable(sc); 425 BHND_PCI_UNLOCK(sc); 426 427 return (error); 428 } 429 430 int 431 bhnd_pcie_mdio_read_ext(struct bhnd_pci_softc *sc, int phy, int devaddr, 432 int reg) 433 { 434 uint32_t cmd; 435 uint16_t val; 436 int error; 437 438 if (devaddr == MDIO_DEVADDR_NONE) 439 return (bhnd_pcie_mdio_read(sc, phy, reg)); 440 441 /* Extended register access is only supported for the SerDes device, 442 * using the non-standard C22 extended address mechanism */ 443 if (!(sc->quirks & BHND_PCI_QUIRK_SD_C22_EXTADDR) || 444 phy != BHND_PCIE_PHYADDR_SD) 445 { 446 return (~0U); 447 } 448 449 /* Enable MDIO access */ 450 BHND_PCI_LOCK(sc); 451 bhnd_pcie_mdio_enable(sc); 452 453 /* Write the block address to the address extension register */ 454 cmd = BHND_PCIE_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) | devaddr; 455 if ((error = bhnd_pcie_mdio_cmd_write(sc, cmd))) 456 goto cleanup; 457 458 /* Issue the read */ 459 cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg); 460 error = bhnd_pcie_mdio_cmd_read(sc, cmd, &val); 461 462 cleanup: 463 bhnd_pcie_mdio_disable(sc); 464 BHND_PCI_UNLOCK(sc); 465 466 if (error) 467 return (~0U); 468 469 return (val); 470 } 471 472 int 473 bhnd_pcie_mdio_write_ext(struct bhnd_pci_softc *sc, int phy, int devaddr, 474 int reg, int val) 475 { 476 uint32_t cmd; 477 int error; 478 479 if (devaddr == MDIO_DEVADDR_NONE) 480 return (bhnd_pcie_mdio_write(sc, phy, reg, val)); 481 482 /* Extended register access is only supported for the SerDes device, 483 * using the non-standard C22 extended address mechanism */ 484 if (!(sc->quirks & BHND_PCI_QUIRK_SD_C22_EXTADDR) || 485 phy != BHND_PCIE_PHYADDR_SD) 486 { 487 return (~0U); 488 } 489 490 /* Enable MDIO access */ 491 BHND_PCI_LOCK(sc); 492 bhnd_pcie_mdio_enable(sc); 493 494 /* Write the block address to the address extension register */ 495 cmd = BHND_PCIE_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) | devaddr; 496 if ((error = bhnd_pcie_mdio_cmd_write(sc, cmd))) 497 goto cleanup; 498 499 /* Issue the write */ 500 cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg) | 501 (val & BHND_PCIE_MDIODATA_DATA_MASK); 502 error = bhnd_pcie_mdio_cmd_write(sc, cmd); 503 504 cleanup: 505 bhnd_pcie_mdio_disable(sc); 506 BHND_PCI_UNLOCK(sc); 507 508 return (error); 509 } 510 511 static device_method_t bhnd_pci_methods[] = { 512 /* Device interface */ 513 DEVMETHOD(device_probe, bhnd_pci_generic_probe), 514 DEVMETHOD(device_attach, bhnd_pci_generic_attach), 515 DEVMETHOD(device_detach, bhnd_pci_generic_detach), 516 DEVMETHOD(device_suspend, bhnd_pci_generic_suspend), 517 DEVMETHOD(device_resume, bhnd_pci_generic_resume), 518 519 /* Bus interface */ 520 DEVMETHOD(bus_add_child, bhnd_pci_add_child), 521 DEVMETHOD(bus_child_deleted, bhnd_pci_child_deleted), 522 DEVMETHOD(bus_print_child, bus_generic_print_child), 523 DEVMETHOD(bus_get_resource_list, bhnd_pci_get_resource_list), 524 DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 525 DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 526 DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), 527 528 DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource), 529 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 530 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 531 DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), 532 DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), 533 534 DEVMETHOD_END 535 }; 536 537 DEFINE_CLASS_0(bhnd_pci, bhnd_pci_driver, bhnd_pci_methods, sizeof(struct bhnd_pci_softc)); 538 MODULE_DEPEND(bhnd_pci, bhnd, 1, 1, 1); 539 MODULE_DEPEND(bhnd_pci, pci, 1, 1, 1); 540 MODULE_VERSION(bhnd_pci, 1); 541