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 { BHND_DEVICE_END, 0 } 86 }; 87 88 /* Device quirks tables */ 89 static struct bhnd_device_quirk bhnd_pci_quirks[] = { BHND_DEVICE_QUIRK_END }; 90 static struct bhnd_device_quirk bhnd_pcie_quirks[] = { 91 BHND_CORE_QUIRK(HWREV_GTE(10), BHND_PCI_QUIRK_SD_C22_EXTADDR), 92 93 BHND_DEVICE_QUIRK_END 94 }; 95 96 #define BHND_PCIE_MDIO_CTL_DELAY 10 /**< usec delay required between 97 * MDIO_CTL/MDIO_DATA accesses. */ 98 #define BHND_PCIE_MDIO_RETRY_DELAY 2000 /**< usec delay before retrying 99 * BHND_PCIE_MDIOCTL_DONE. */ 100 #define BHND_PCIE_MDIO_RETRY_COUNT 200 /**< number of times to loop waiting 101 * for BHND_PCIE_MDIOCTL_DONE. */ 102 103 #define BHND_PCI_READ_4(_sc, _reg) \ 104 bhnd_bus_read_4((_sc)->mem_res, (_reg)) 105 #define BHND_PCI_WRITE_4(_sc, _reg, _val) \ 106 bhnd_bus_write_4((_sc)->mem_res, (_reg), (_val)) 107 108 #define BHND_PCIE_ASSERT(sc) \ 109 KASSERT(bhnd_get_class(sc->dev) == BHND_DEVCLASS_PCIE, \ 110 ("not a pcie device!")); 111 112 int 113 bhnd_pci_generic_probe(device_t dev) 114 { 115 const struct bhnd_device *id; 116 117 id = bhnd_device_lookup(dev, &bhnd_pci_devs[0].device, 118 sizeof(bhnd_pci_devs[0])); 119 if (id == NULL) 120 return (ENXIO); 121 122 bhnd_set_custom_core_desc(dev, id->desc); 123 return (BUS_PROBE_DEFAULT); 124 } 125 126 int 127 bhnd_pci_generic_attach(device_t dev) 128 { 129 struct bhnd_pci_softc *sc; 130 int error; 131 132 sc = device_get_softc(dev); 133 sc->dev = dev; 134 sc->quirks = bhnd_device_quirks(dev, &bhnd_pci_devs[0].device, 135 sizeof(bhnd_pci_devs[0])); 136 137 /* Allocate bus resources */ 138 sc->mem_res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, 139 RF_ACTIVE); 140 if (sc->mem_res == NULL) 141 return (ENXIO); 142 143 BHND_PCI_LOCK_INIT(sc); 144 145 /* Probe and attach children */ 146 if ((error = bus_generic_attach(dev))) 147 goto cleanup; 148 149 return (0); 150 151 cleanup: 152 bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); 153 BHND_PCI_LOCK_DESTROY(sc); 154 155 return (error); 156 } 157 158 int 159 bhnd_pci_generic_detach(device_t dev) 160 { 161 struct bhnd_pci_softc *sc; 162 int error; 163 164 sc = device_get_softc(dev); 165 166 if ((error = bus_generic_detach(dev))) 167 return (error); 168 169 bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); 170 171 BHND_PCI_LOCK_DESTROY(sc); 172 173 return (0); 174 } 175 176 static struct resource_list * 177 bhnd_pci_get_resource_list(device_t dev, device_t child) 178 { 179 struct bhnd_pci_devinfo *dinfo; 180 181 if (device_get_parent(child) != dev) 182 return (NULL); 183 184 dinfo = device_get_ivars(child); 185 return (&dinfo->resources); 186 } 187 188 static device_t 189 bhnd_pci_add_child(device_t dev, u_int order, const char *name, int unit) 190 { 191 struct bhnd_pci_devinfo *dinfo; 192 device_t child; 193 194 child = device_add_child_ordered(dev, order, name, unit); 195 if (child == NULL) 196 return (NULL); 197 198 dinfo = malloc(sizeof(struct bhnd_pci_devinfo), M_DEVBUF, M_NOWAIT); 199 if (dinfo == NULL) { 200 device_delete_child(dev, child); 201 return (NULL); 202 } 203 204 resource_list_init(&dinfo->resources); 205 206 device_set_ivars(child, dinfo); 207 return (child); 208 } 209 210 static void 211 bhnd_pci_child_deleted(device_t dev, device_t child) 212 { 213 struct bhnd_pci_devinfo *dinfo; 214 215 if (device_get_parent(child) != dev) 216 return; 217 218 dinfo = device_get_ivars(child); 219 if (dinfo != NULL) { 220 resource_list_free(&dinfo->resources); 221 free(dinfo, M_DEVBUF); 222 } 223 224 device_set_ivars(child, NULL); 225 } 226 227 int 228 bhnd_pci_generic_suspend(device_t dev) 229 { 230 return (bus_generic_suspend(dev)); 231 } 232 233 int 234 bhnd_pci_generic_resume(device_t dev) 235 { 236 return (bus_generic_resume(dev)); 237 } 238 239 /** 240 * Read a 32-bit PCIe TLP/DLLP/PLP protocol register. 241 * 242 * @param sc The bhndb_pci driver state. 243 * @param addr The protocol register offset. 244 */ 245 uint32_t 246 bhnd_pcie_read_proto_reg(struct bhnd_pci_softc *sc, uint32_t addr) 247 { 248 uint32_t val; 249 250 BHND_PCIE_ASSERT(sc); 251 252 BHND_PCI_LOCK(sc); 253 BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_ADDR, addr); 254 val = BHND_PCI_READ_4(sc, BHND_PCIE_IND_DATA); 255 BHND_PCI_UNLOCK(sc); 256 257 return (val); 258 } 259 260 /** 261 * Write a 32-bit PCIe TLP/DLLP/PLP protocol register value. 262 * 263 * @param sc The bhndb_pci driver state. 264 * @param addr The protocol register offset. 265 * @param val The value to write to @p addr. 266 */ 267 void 268 bhnd_pcie_write_proto_reg(struct bhnd_pci_softc *sc, uint32_t addr, 269 uint32_t val) 270 { 271 BHND_PCIE_ASSERT(sc); 272 273 BHND_PCI_LOCK(sc); 274 BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_ADDR, addr); 275 BHND_PCI_WRITE_4(sc, BHND_PCIE_IND_DATA, val); 276 BHND_PCI_UNLOCK(sc); 277 } 278 279 /* Spin until the MDIO device reports itself as idle, or timeout is reached. */ 280 static int 281 bhnd_pcie_mdio_wait_idle(struct bhnd_pci_softc *sc) 282 { 283 uint32_t ctl; 284 285 /* Spin waiting for the BUSY flag to clear */ 286 for (int i = 0; i < BHND_PCIE_MDIO_RETRY_COUNT; i++) { 287 ctl = BHND_PCI_READ_4(sc, BHND_PCIE_MDIO_CTL); 288 if ((ctl & BHND_PCIE_MDIOCTL_DONE)) 289 return (0); 290 291 DELAY(BHND_PCIE_MDIO_RETRY_DELAY); 292 } 293 294 return (ETIMEDOUT); 295 } 296 297 /** 298 * Write an MDIO IOCTL and wait for completion. 299 */ 300 static int 301 bhnd_pcie_mdio_ioctl(struct bhnd_pci_softc *sc, uint32_t cmd) 302 { 303 BHND_PCI_LOCK_ASSERT(sc, MA_OWNED); 304 305 BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_CTL, cmd); 306 DELAY(BHND_PCIE_MDIO_CTL_DELAY); 307 return (0); 308 } 309 310 /** 311 * Enable MDIO device 312 */ 313 static int 314 bhnd_pcie_mdio_enable(struct bhnd_pci_softc *sc) 315 { 316 uint32_t ctl; 317 318 BHND_PCIE_ASSERT(sc); 319 320 /* Enable MDIO clock and preamble mode */ 321 ctl = BHND_PCIE_MDIOCTL_PREAM_EN|BHND_PCIE_MDIOCTL_DIVISOR_VAL; 322 return (bhnd_pcie_mdio_ioctl(sc, ctl)); 323 } 324 325 /** 326 * Disable MDIO device. 327 */ 328 static void 329 bhnd_pcie_mdio_disable(struct bhnd_pci_softc *sc) 330 { 331 if (bhnd_pcie_mdio_ioctl(sc, 0)) 332 device_printf(sc->dev, "failed to disable MDIO clock\n"); 333 } 334 335 /** 336 * Issue a write command and wait for completion 337 */ 338 static int 339 bhnd_pcie_mdio_cmd_write(struct bhnd_pci_softc *sc, uint32_t cmd) 340 { 341 int error; 342 343 BHND_PCI_LOCK_ASSERT(sc, MA_OWNED); 344 345 cmd |= BHND_PCIE_MDIODATA_START|BHND_PCIE_MDIODATA_TA|BHND_PCIE_MDIODATA_CMD_WRITE; 346 347 BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_DATA, cmd); 348 DELAY(BHND_PCIE_MDIO_CTL_DELAY); 349 350 if ((error = bhnd_pcie_mdio_wait_idle(sc))) 351 return (error); 352 353 return (0); 354 } 355 356 /** 357 * Issue an MDIO read command, wait for completion, and return 358 * the result in @p data_read. 359 */ 360 static int 361 bhnd_pcie_mdio_cmd_read(struct bhnd_pci_softc *sc, uint32_t cmd, 362 uint16_t *data_read) 363 { 364 int error; 365 366 BHND_PCI_LOCK_ASSERT(sc, MA_OWNED); 367 368 cmd |= BHND_PCIE_MDIODATA_START|BHND_PCIE_MDIODATA_TA|BHND_PCIE_MDIODATA_CMD_READ; 369 BHND_PCI_WRITE_4(sc, BHND_PCIE_MDIO_DATA, cmd); 370 DELAY(BHND_PCIE_MDIO_CTL_DELAY); 371 372 if ((error = bhnd_pcie_mdio_wait_idle(sc))) 373 return (error); 374 375 *data_read = (BHND_PCI_READ_4(sc, BHND_PCIE_MDIO_DATA) & 376 BHND_PCIE_MDIODATA_DATA_MASK); 377 return (0); 378 } 379 380 int 381 bhnd_pcie_mdio_read(struct bhnd_pci_softc *sc, int phy, int reg) 382 { 383 uint32_t cmd; 384 uint16_t val; 385 int error; 386 387 /* Enable MDIO access */ 388 BHND_PCI_LOCK(sc); 389 bhnd_pcie_mdio_enable(sc); 390 391 /* Issue the read */ 392 cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg); 393 error = bhnd_pcie_mdio_cmd_read(sc, cmd, &val); 394 395 /* Disable MDIO access */ 396 bhnd_pcie_mdio_disable(sc); 397 BHND_PCI_UNLOCK(sc); 398 399 if (error) 400 return (~0U); 401 402 return (val); 403 } 404 405 int 406 bhnd_pcie_mdio_write(struct bhnd_pci_softc *sc, int phy, int reg, int val) 407 { 408 uint32_t cmd; 409 int error; 410 411 /* Enable MDIO access */ 412 BHND_PCI_LOCK(sc); 413 bhnd_pcie_mdio_enable(sc); 414 415 /* Issue the write */ 416 cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg) | (val & BHND_PCIE_MDIODATA_DATA_MASK); 417 error = bhnd_pcie_mdio_cmd_write(sc, cmd); 418 419 /* Disable MDIO access */ 420 bhnd_pcie_mdio_disable(sc); 421 BHND_PCI_UNLOCK(sc); 422 423 return (error); 424 } 425 426 int 427 bhnd_pcie_mdio_read_ext(struct bhnd_pci_softc *sc, int phy, int devaddr, 428 int reg) 429 { 430 uint32_t cmd; 431 uint16_t val; 432 int error; 433 434 if (devaddr == MDIO_DEVADDR_NONE) 435 return (bhnd_pcie_mdio_read(sc, phy, reg)); 436 437 /* Extended register access is only supported for the SerDes device, 438 * using the non-standard C22 extended address mechanism */ 439 if (!(sc->quirks & BHND_PCI_QUIRK_SD_C22_EXTADDR) || 440 phy != BHND_PCIE_PHYADDR_SD) 441 { 442 return (~0U); 443 } 444 445 /* Enable MDIO access */ 446 BHND_PCI_LOCK(sc); 447 bhnd_pcie_mdio_enable(sc); 448 449 /* Write the block address to the address extension register */ 450 cmd = BHND_PCIE_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) | devaddr; 451 if ((error = bhnd_pcie_mdio_cmd_write(sc, cmd))) 452 goto cleanup; 453 454 /* Issue the read */ 455 cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg); 456 error = bhnd_pcie_mdio_cmd_read(sc, cmd, &val); 457 458 cleanup: 459 bhnd_pcie_mdio_disable(sc); 460 BHND_PCI_UNLOCK(sc); 461 462 if (error) 463 return (~0U); 464 465 return (val); 466 } 467 468 int 469 bhnd_pcie_mdio_write_ext(struct bhnd_pci_softc *sc, int phy, int devaddr, 470 int reg, int val) 471 { 472 uint32_t cmd; 473 int error; 474 475 if (devaddr == MDIO_DEVADDR_NONE) 476 return (bhnd_pcie_mdio_write(sc, phy, reg, val)); 477 478 /* Extended register access is only supported for the SerDes device, 479 * using the non-standard C22 extended address mechanism */ 480 if (!(sc->quirks & BHND_PCI_QUIRK_SD_C22_EXTADDR) || 481 phy != BHND_PCIE_PHYADDR_SD) 482 { 483 return (~0U); 484 } 485 486 /* Enable MDIO access */ 487 BHND_PCI_LOCK(sc); 488 bhnd_pcie_mdio_enable(sc); 489 490 /* Write the block address to the address extension register */ 491 cmd = BHND_PCIE_MDIODATA_ADDR(phy, BHND_PCIE_SD_ADDREXT) | devaddr; 492 if ((error = bhnd_pcie_mdio_cmd_write(sc, cmd))) 493 goto cleanup; 494 495 /* Issue the write */ 496 cmd = BHND_PCIE_MDIODATA_ADDR(phy, reg) | 497 (val & BHND_PCIE_MDIODATA_DATA_MASK); 498 error = bhnd_pcie_mdio_cmd_write(sc, cmd); 499 500 cleanup: 501 bhnd_pcie_mdio_disable(sc); 502 BHND_PCI_UNLOCK(sc); 503 504 return (error); 505 } 506 507 static device_method_t bhnd_pci_methods[] = { 508 /* Device interface */ 509 DEVMETHOD(device_probe, bhnd_pci_generic_probe), 510 DEVMETHOD(device_attach, bhnd_pci_generic_attach), 511 DEVMETHOD(device_detach, bhnd_pci_generic_detach), 512 DEVMETHOD(device_suspend, bhnd_pci_generic_suspend), 513 DEVMETHOD(device_resume, bhnd_pci_generic_resume), 514 515 /* Bus interface */ 516 DEVMETHOD(bus_add_child, bhnd_pci_add_child), 517 DEVMETHOD(bus_child_deleted, bhnd_pci_child_deleted), 518 DEVMETHOD(bus_print_child, bus_generic_print_child), 519 DEVMETHOD(bus_get_resource_list, bhnd_pci_get_resource_list), 520 DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 521 DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 522 DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), 523 524 DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource), 525 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 526 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 527 DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), 528 DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), 529 530 DEVMETHOD_END 531 }; 532 533 DEFINE_CLASS_0(bhnd_pci, bhnd_pci_driver, bhnd_pci_methods, sizeof(struct bhnd_pci_softc)); 534 MODULE_DEPEND(bhnd_pci, bhnd, 1, 1, 1); 535 MODULE_DEPEND(bhnd_pci, pci, 1, 1, 1); 536 MODULE_VERSION(bhnd_pci, 1); 537