1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright 2021 Jessica Clarke <jrtc27@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 */ 28 29 /* SiFive FU740 DesignWare PCIe driver */ 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/bus.h> 34 #include <sys/gpio.h> 35 #include <sys/kernel.h> 36 #include <sys/module.h> 37 #include <sys/rman.h> 38 39 #include <machine/bus.h> 40 #include <machine/intr.h> 41 #include <machine/resource.h> 42 43 #include <dev/extres/clk/clk.h> 44 #include <dev/extres/hwreset/hwreset.h> 45 #include <dev/gpio/gpiobusvar.h> 46 #include <dev/ofw/ofw_bus.h> 47 #include <dev/ofw/ofw_bus_subr.h> 48 #include <dev/ofw/ofw_pci.h> 49 #include <dev/ofw/ofwpci.h> 50 #include <dev/pci/pcivar.h> 51 #include <dev/pci/pcireg.h> 52 #include <dev/pci/pcib_private.h> 53 #include <dev/pci/pci_dw.h> 54 55 #include "pcib_if.h" 56 #include "pci_dw_if.h" 57 58 #define FUDW_PHYS 2 59 #define FUDW_LANES_PER_PHY 4 60 61 #define FUDW_MGMT_PERST_N 0x0 62 #define FUDW_MGMT_LTSSM_EN 0x10 63 #define FUDW_MGMT_HOLD_PHY_RST 0x18 64 #define FUDW_MGMT_DEVICE_TYPE 0x708 65 #define FUDW_MGMT_DEVICE_TYPE_RC 0x4 66 #define FUDW_MGMT_PHY_CR_PARA_REG(_n, _r) \ 67 (0x860 + (_n) * 0x40 + FUDW_MGMT_PHY_CR_PARA_##_r) 68 #define FUDW_MGMT_PHY_CR_PARA_ADDR 0x0 69 #define FUDW_MGMT_PHY_CR_PARA_READ_EN 0x10 70 #define FUDW_MGMT_PHY_CR_PARA_READ_DATA 0x18 71 #define FUDW_MGMT_PHY_CR_PARA_SEL 0x20 72 #define FUDW_MGMT_PHY_CR_PARA_WRITE_DATA 0x28 73 #define FUDW_MGMT_PHY_CR_PARA_WRITE_EN 0x30 74 #define FUDW_MGMT_PHY_CR_PARA_ACK 0x38 75 76 #define FUDW_MGMT_PHY_LANE(_n) (0x1008 + (_n) * 0x100) 77 #define FUDW_MGMT_PHY_LANE_CDR_TRACK_EN (1 << 0) 78 #define FUDW_MGMT_PHY_LANE_LOS_THRESH (1 << 5) 79 #define FUDW_MGMT_PHY_LANE_TERM_EN (1 << 9) 80 #define FUDW_MGMT_PHY_LANE_TERM_ACDC (1 << 10) 81 #define FUDW_MGMT_PHY_LANE_EN (1 << 11) 82 #define FUDW_MGMT_PHY_LANE_INIT \ 83 (FUDW_MGMT_PHY_LANE_CDR_TRACK_EN | FUDW_MGMT_PHY_LANE_LOS_THRESH | \ 84 FUDW_MGMT_PHY_LANE_TERM_EN | FUDW_MGMT_PHY_LANE_TERM_ACDC | \ 85 FUDW_MGMT_PHY_LANE_EN) 86 87 #define FUDW_DBI_PORT_DBG1 0x72c 88 #define FUDW_DBI_PORT_DBG1_LINK_UP (1 << 4) 89 #define FUDW_DBI_PORT_DBG1_LINK_IN_TRAINING (1 << 29) 90 91 struct fupci_softc { 92 struct pci_dw_softc dw_sc; 93 device_t dev; 94 struct resource *mgmt_res; 95 gpio_pin_t porst_pin; 96 gpio_pin_t pwren_pin; 97 clk_t pcie_aux_clk; 98 hwreset_t pcie_aux_rst; 99 }; 100 101 #define FUDW_MGMT_READ(_sc, _o) bus_read_4((_sc)->mgmt_res, (_o)) 102 #define FUDW_MGMT_WRITE(_sc, _o, _v) bus_write_4((_sc)->mgmt_res, (_o), (_v)) 103 104 static struct ofw_compat_data compat_data[] = { 105 { "sifive,fu740-pcie", 1 }, 106 { NULL, 0 }, 107 }; 108 109 /* Currently unused; included for completeness */ 110 static int __unused 111 fupci_phy_read(struct fupci_softc *sc, int phy, uint32_t reg, uint32_t *val) 112 { 113 unsigned timeout; 114 uint32_t ack; 115 116 FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ADDR), reg); 117 FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, READ_EN), 1); 118 119 timeout = 10; 120 do { 121 ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK)); 122 if (ack != 0) 123 break; 124 DELAY(10); 125 } while (--timeout > 0); 126 127 if (timeout == 0) { 128 device_printf(sc->dev, "Timeout waiting for read ACK\n"); 129 return (ETIMEDOUT); 130 } 131 132 *val = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, READ_DATA)); 133 FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, READ_EN), 0); 134 135 timeout = 10; 136 do { 137 ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK)); 138 if (ack == 0) 139 break; 140 DELAY(10); 141 } while (--timeout > 0); 142 143 if (timeout == 0) { 144 device_printf(sc->dev, "Timeout waiting for read un-ACK\n"); 145 return (ETIMEDOUT); 146 } 147 148 return (0); 149 } 150 151 static int 152 fupci_phy_write(struct fupci_softc *sc, int phy, uint32_t reg, uint32_t val) 153 { 154 unsigned timeout; 155 uint32_t ack; 156 157 FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ADDR), reg); 158 FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, WRITE_DATA), val); 159 FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, WRITE_EN), 1); 160 161 timeout = 10; 162 do { 163 ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK)); 164 if (ack != 0) 165 break; 166 DELAY(10); 167 } while (--timeout > 0); 168 169 if (timeout == 0) { 170 device_printf(sc->dev, "Timeout waiting for write ACK\n"); 171 return (ETIMEDOUT); 172 } 173 174 FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, WRITE_EN), 0); 175 176 timeout = 10; 177 do { 178 ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK)); 179 if (ack == 0) 180 break; 181 DELAY(10); 182 } while (--timeout > 0); 183 184 if (timeout == 0) { 185 device_printf(sc->dev, "Timeout waiting for write un-ACK\n"); 186 return (ETIMEDOUT); 187 } 188 189 return (0); 190 } 191 192 static int 193 fupci_phy_init(struct fupci_softc *sc) 194 { 195 device_t dev; 196 int error, phy, lane; 197 198 dev = sc->dev; 199 200 /* Assert core power-on reset (active low) */ 201 error = gpio_pin_set_active(sc->porst_pin, false); 202 if (error != 0) { 203 device_printf(dev, "Cannot assert power-on reset: %d\n", 204 error); 205 return (error); 206 } 207 208 /* Assert PERST_N */ 209 FUDW_MGMT_WRITE(sc, FUDW_MGMT_PERST_N, 0); 210 211 /* Enable power */ 212 error = gpio_pin_set_active(sc->pwren_pin, true); 213 if (error != 0) { 214 device_printf(dev, "Cannot enable power: %d\n", error); 215 return (error); 216 } 217 218 /* Hold PERST for 100ms as per the PCIe spec */ 219 DELAY(100); 220 221 /* Deassert PERST_N */ 222 FUDW_MGMT_WRITE(sc, FUDW_MGMT_PERST_N, 1); 223 224 /* Deassert core power-on reset (active low) */ 225 error = gpio_pin_set_active(sc->porst_pin, true); 226 if (error != 0) { 227 device_printf(dev, "Cannot deassert power-on reset: %d\n", 228 error); 229 return (error); 230 } 231 232 /* Enable the aux clock */ 233 error = clk_enable(sc->pcie_aux_clk); 234 if (error != 0) { 235 device_printf(dev, "Cannot enable aux clock: %d\n", error); 236 return (error); 237 } 238 239 /* Hold LTSSM in reset whilst initialising the PHYs */ 240 FUDW_MGMT_WRITE(sc, FUDW_MGMT_HOLD_PHY_RST, 1); 241 242 /* Deassert the aux reset */ 243 error = hwreset_deassert(sc->pcie_aux_rst); 244 if (error != 0) { 245 device_printf(dev, "Cannot deassert aux reset: %d\n", error); 246 return (error); 247 } 248 249 /* Enable control register interface */ 250 for (phy = 0; phy < FUDW_PHYS; ++phy) 251 FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, SEL), 1); 252 253 /* Wait for enable to take effect */ 254 DELAY(1); 255 256 /* Initialise lane configuration */ 257 for (phy = 0; phy < FUDW_PHYS; ++phy) { 258 for (lane = 0; lane < FUDW_LANES_PER_PHY; ++lane) 259 fupci_phy_write(sc, phy, FUDW_MGMT_PHY_LANE(lane), 260 FUDW_MGMT_PHY_LANE_INIT); 261 } 262 263 /* Disable the aux clock whilst taking the LTSSM out of reset */ 264 error = clk_disable(sc->pcie_aux_clk); 265 if (error != 0) { 266 device_printf(dev, "Cannot disable aux clock: %d\n", error); 267 return (error); 268 } 269 270 /* Take LTSSM out of reset */ 271 FUDW_MGMT_WRITE(sc, FUDW_MGMT_HOLD_PHY_RST, 0); 272 273 /* Enable the aux clock again */ 274 error = clk_enable(sc->pcie_aux_clk); 275 if (error != 0) { 276 device_printf(dev, "Cannot re-enable aux clock: %d\n", error); 277 return (error); 278 } 279 280 /* Put the controller in Root Complex mode */ 281 FUDW_MGMT_WRITE(sc, FUDW_MGMT_DEVICE_TYPE, FUDW_MGMT_DEVICE_TYPE_RC); 282 283 return (0); 284 } 285 286 static void 287 fupci_dbi_protect(struct fupci_softc *sc, bool protect) 288 { 289 uint32_t reg; 290 291 reg = pci_dw_dbi_rd4(sc->dev, DW_MISC_CONTROL_1); 292 if (protect) 293 reg &= ~DBI_RO_WR_EN; 294 else 295 reg |= DBI_RO_WR_EN; 296 pci_dw_dbi_wr4(sc->dev, DW_MISC_CONTROL_1, reg); 297 } 298 299 static int 300 fupci_init(struct fupci_softc *sc) 301 { 302 /* Enable 32-bit I/O window */ 303 fupci_dbi_protect(sc, false); 304 pci_dw_dbi_wr2(sc->dev, PCIR_IOBASEL_1, 305 (PCIM_BRIO_32 << 8) | PCIM_BRIO_32); 306 fupci_dbi_protect(sc, true); 307 308 /* Enable LTSSM */ 309 FUDW_MGMT_WRITE(sc, FUDW_MGMT_LTSSM_EN, 1); 310 311 return (0); 312 } 313 314 static int 315 fupci_probe(device_t dev) 316 { 317 if (!ofw_bus_status_okay(dev)) 318 return (ENXIO); 319 320 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 321 return (ENXIO); 322 323 device_set_desc(dev, "SiFive FU740 PCIe Controller"); 324 325 return (BUS_PROBE_DEFAULT); 326 } 327 328 static int 329 fupci_attach(device_t dev) 330 { 331 struct fupci_softc *sc; 332 phandle_t node; 333 int error, rid; 334 335 sc = device_get_softc(dev); 336 node = ofw_bus_get_node(dev); 337 sc->dev = dev; 338 339 rid = 0; 340 error = ofw_bus_find_string_index(node, "reg-names", "dbi", &rid); 341 if (error != 0) { 342 device_printf(dev, "Cannot get DBI memory: %d\n", error); 343 goto fail; 344 } 345 sc->dw_sc.dbi_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 346 RF_ACTIVE); 347 if (sc->dw_sc.dbi_res == NULL) { 348 device_printf(dev, "Cannot allocate DBI memory\n"); 349 error = ENXIO; 350 goto fail; 351 } 352 353 rid = 0; 354 error = ofw_bus_find_string_index(node, "reg-names", "mgmt", &rid); 355 if (error != 0) { 356 device_printf(dev, "Cannot get management space memory: %d\n", 357 error); 358 goto fail; 359 } 360 sc->mgmt_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 361 RF_ACTIVE); 362 if (sc->mgmt_res == NULL) { 363 device_printf(dev, "Cannot allocate management space memory\n"); 364 error = ENXIO; 365 goto fail; 366 } 367 368 error = gpio_pin_get_by_ofw_property(dev, node, "reset-gpios", 369 &sc->porst_pin); 370 /* Old U-Boot device tree uses perstn-gpios */ 371 if (error == ENOENT) 372 error = gpio_pin_get_by_ofw_property(dev, node, "perstn-gpios", 373 &sc->porst_pin); 374 if (error != 0) { 375 device_printf(dev, "Cannot get power-on reset GPIO: %d\n", 376 error); 377 goto fail; 378 } 379 error = gpio_pin_setflags(sc->porst_pin, GPIO_PIN_OUTPUT); 380 if (error != 0) { 381 device_printf(dev, "Cannot configure power-on reset GPIO: %d\n", 382 error); 383 goto fail; 384 } 385 386 error = gpio_pin_get_by_ofw_property(dev, node, "pwren-gpios", 387 &sc->pwren_pin); 388 if (error != 0) { 389 device_printf(dev, "Cannot get power enable GPIO: %d\n", 390 error); 391 goto fail; 392 } 393 error = gpio_pin_setflags(sc->pwren_pin, GPIO_PIN_OUTPUT); 394 if (error != 0) { 395 device_printf(dev, "Cannot configure power enable GPIO: %d\n", 396 error); 397 goto fail; 398 } 399 400 error = clk_get_by_ofw_name(dev, node, "pcie_aux", &sc->pcie_aux_clk); 401 /* Old U-Boot device tree uses pcieaux */ 402 if (error == ENOENT) 403 error = clk_get_by_ofw_name(dev, node, "pcieaux", 404 &sc->pcie_aux_clk); 405 if (error != 0) { 406 device_printf(dev, "Cannot get aux clock: %d\n", error); 407 goto fail; 408 } 409 410 error = hwreset_get_by_ofw_idx(dev, node, 0, &sc->pcie_aux_rst); 411 if (error != 0) { 412 device_printf(dev, "Cannot get aux reset: %d\n", error); 413 goto fail; 414 } 415 416 error = fupci_phy_init(sc); 417 if (error != 0) 418 goto fail; 419 420 error = pci_dw_init(dev); 421 if (error != 0) 422 goto fail; 423 424 error = fupci_init(sc); 425 if (error != 0) 426 goto fail; 427 428 return (bus_generic_attach(dev)); 429 430 fail: 431 /* XXX Cleanup */ 432 return (error); 433 } 434 435 static int 436 fupci_get_link(device_t dev, bool *status) 437 { 438 uint32_t reg; 439 440 reg = pci_dw_dbi_rd4(dev, FUDW_DBI_PORT_DBG1); 441 *status = (reg & FUDW_DBI_PORT_DBG1_LINK_UP) != 0 && 442 (reg & FUDW_DBI_PORT_DBG1_LINK_IN_TRAINING) == 0; 443 444 return (0); 445 } 446 447 static device_method_t fupci_methods[] = { 448 /* Device interface */ 449 DEVMETHOD(device_probe, fupci_probe), 450 DEVMETHOD(device_attach, fupci_attach), 451 452 /* PCI DW interface */ 453 DEVMETHOD(pci_dw_get_link, fupci_get_link), 454 455 DEVMETHOD_END 456 }; 457 458 DEFINE_CLASS_1(pcib, fupci_driver, fupci_methods, 459 sizeof(struct fupci_softc), pci_dw_driver); 460 DRIVER_MODULE(fu740_pci_dw, simplebus, fupci_driver, NULL, NULL); 461