Date: Tue, 7 Sep 2021 12:09:53 GMT From: Jessica Clarke <jrtc27@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Subject: git: a06f7ad58487 - stable/13 - fu740_pci_dw: Add SiFive FU740 PCIe controller driver Message-ID: <202109071209.187C9rko087503@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch stable/13 has been updated by jrtc27: URL: https://cgit.FreeBSD.org/src/commit/?id=a06f7ad58487304ca207d007f59d17e93f0ada4f commit a06f7ad58487304ca207d007f59d17e93f0ada4f Author: Jessica Clarke <jrtc27@FreeBSD.org> AuthorDate: 2021-08-07 18:27:31 +0000 Commit: Jessica Clarke <jrtc27@FreeBSD.org> CommitDate: 2021-09-07 12:07:31 +0000 fu740_pci_dw: Add SiFive FU740 PCIe controller driver Reviewed by: mhorne MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D31033 (cherry picked from commit 896e217a0eae692b1fe3de5d1354478541fb13ff) --- sys/conf/files.riscv | 2 + sys/riscv/conf/GENERIC | 1 + sys/riscv/sifive/files.sifive | 1 + sys/riscv/sifive/fu740_pci_dw.c | 465 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 469 insertions(+) diff --git a/sys/conf/files.riscv b/sys/conf/files.riscv index 7ecea016b9a3..c6adc92d8a3a 100644 --- a/sys/conf/files.riscv +++ b/sys/conf/files.riscv @@ -5,6 +5,8 @@ cddl/dev/fbt/riscv/fbt_isa.c optional dtrace_fbt | dtraceall compile-with "${ crypto/des/des_enc.c optional netsmb dev/ofw/ofw_cpu.c optional fdt dev/ofw/ofwpci.c optional pci fdt +dev/pci/pci_dw.c optional pci fdt +dev/pci/pci_dw_if.m optional pci fdt dev/pci/pci_host_generic.c optional pci dev/pci/pci_host_generic_fdt.c optional pci fdt dev/uart/uart_cpu_fdt.c optional uart fdt diff --git a/sys/riscv/conf/GENERIC b/sys/riscv/conf/GENERIC index dbe4641ff43d..c7e18ed7351b 100644 --- a/sys/riscv/conf/GENERIC +++ b/sys/riscv/conf/GENERIC @@ -166,6 +166,7 @@ options FDT makeoptions MODULES_EXTRA+="dtb/sifive" # SiFive device drivers +device fu740_pci_dw device sifive_gpio device sifive_spi include "../sifive/std.sifive" diff --git a/sys/riscv/sifive/files.sifive b/sys/riscv/sifive/files.sifive index 9ea71c4fcd27..f38bacb48e3b 100644 --- a/sys/riscv/sifive/files.sifive +++ b/sys/riscv/sifive/files.sifive @@ -1,6 +1,7 @@ # $FreeBSD$ riscv/sifive/fe310_aon.c optional fe310aon +riscv/sifive/fu740_pci_dw.c optional fu740_pci_dw pci fdt riscv/sifive/sifive_gpio.c optional sifive_gpio gpio riscv/sifive/sifive_prci.c standard riscv/sifive/sifive_spi.c optional sifive_spi spibus diff --git a/sys/riscv/sifive/fu740_pci_dw.c b/sys/riscv/sifive/fu740_pci_dw.c new file mode 100644 index 000000000000..fbb62cc44e65 --- /dev/null +++ b/sys/riscv/sifive/fu740_pci_dw.c @@ -0,0 +1,465 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2021 Jessica Clarke <jrtc27@FreeBSD.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* SiFive FU740 DesignWare PCIe driver */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/gpio.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/rman.h> + +#include <machine/bus.h> +#include <machine/intr.h> +#include <machine/resource.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> +#include <dev/gpio/gpiobusvar.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_pci.h> +#include <dev/ofw/ofwpci.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcib_private.h> +#include <dev/pci/pci_dw.h> + +#include "pcib_if.h" +#include "pci_dw_if.h" + +#define FUDW_PHYS 2 +#define FUDW_LANES_PER_PHY 4 + +#define FUDW_MGMT_PERST_N 0x0 +#define FUDW_MGMT_LTSSM_EN 0x10 +#define FUDW_MGMT_HOLD_PHY_RST 0x18 +#define FUDW_MGMT_DEVICE_TYPE 0x708 +#define FUDW_MGMT_DEVICE_TYPE_RC 0x4 +#define FUDW_MGMT_PHY_CR_PARA_REG(_n, _r) \ + (0x860 + (_n) * 0x40 + FUDW_MGMT_PHY_CR_PARA_##_r) +#define FUDW_MGMT_PHY_CR_PARA_ADDR 0x0 +#define FUDW_MGMT_PHY_CR_PARA_READ_EN 0x10 +#define FUDW_MGMT_PHY_CR_PARA_READ_DATA 0x18 +#define FUDW_MGMT_PHY_CR_PARA_SEL 0x20 +#define FUDW_MGMT_PHY_CR_PARA_WRITE_DATA 0x28 +#define FUDW_MGMT_PHY_CR_PARA_WRITE_EN 0x30 +#define FUDW_MGMT_PHY_CR_PARA_ACK 0x38 + +#define FUDW_MGMT_PHY_LANE(_n) (0x1008 + (_n) * 0x100) +#define FUDW_MGMT_PHY_LANE_CDR_TRACK_EN (1 << 0) +#define FUDW_MGMT_PHY_LANE_LOS_THRESH (1 << 5) +#define FUDW_MGMT_PHY_LANE_TERM_EN (1 << 9) +#define FUDW_MGMT_PHY_LANE_TERM_ACDC (1 << 10) +#define FUDW_MGMT_PHY_LANE_EN (1 << 11) +#define FUDW_MGMT_PHY_LANE_INIT \ + (FUDW_MGMT_PHY_LANE_CDR_TRACK_EN | FUDW_MGMT_PHY_LANE_LOS_THRESH | \ + FUDW_MGMT_PHY_LANE_TERM_EN | FUDW_MGMT_PHY_LANE_TERM_ACDC | \ + FUDW_MGMT_PHY_LANE_EN) + +#define FUDW_DBI_PORT_DBG1 0x72c +#define FUDW_DBI_PORT_DBG1_LINK_UP (1 << 4) +#define FUDW_DBI_PORT_DBG1_LINK_IN_TRAINING (1 << 29) + +struct fupci_softc { + struct pci_dw_softc dw_sc; + device_t dev; + struct resource *mgmt_res; + gpio_pin_t porst_pin; + gpio_pin_t pwren_pin; + clk_t pcie_aux_clk; + hwreset_t pcie_aux_rst; +}; + +#define FUDW_MGMT_READ(_sc, _o) bus_read_4((_sc)->mgmt_res, (_o)) +#define FUDW_MGMT_WRITE(_sc, _o, _v) bus_write_4((_sc)->mgmt_res, (_o), (_v)) + +static struct ofw_compat_data compat_data[] = { + { "sifive,fu740-pcie", 1 }, + { NULL, 0 }, +}; + +/* Currently unused; included for completeness */ +static int __unused +fupci_phy_read(struct fupci_softc *sc, int phy, uint32_t reg, uint32_t *val) +{ + unsigned timeout; + uint32_t ack; + + FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ADDR), reg); + FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, READ_EN), 1); + + timeout = 10; + do { + ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK)); + if (ack != 0) + break; + DELAY(10); + } while (--timeout > 0); + + if (timeout == 0) { + device_printf(sc->dev, "Timeout waiting for read ACK\n"); + return (ETIMEDOUT); + } + + *val = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, READ_DATA)); + FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, READ_EN), 0); + + timeout = 10; + do { + ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK)); + if (ack == 0) + break; + DELAY(10); + } while (--timeout > 0); + + if (timeout == 0) { + device_printf(sc->dev, "Timeout waiting for read un-ACK\n"); + return (ETIMEDOUT); + } + + return (0); +} + +static int +fupci_phy_write(struct fupci_softc *sc, int phy, uint32_t reg, uint32_t val) +{ + unsigned timeout; + uint32_t ack; + + FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ADDR), reg); + FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, WRITE_DATA), val); + FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, WRITE_EN), 1); + + timeout = 10; + do { + ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK)); + if (ack != 0) + break; + DELAY(10); + } while (--timeout > 0); + + if (timeout == 0) { + device_printf(sc->dev, "Timeout waiting for write ACK\n"); + return (ETIMEDOUT); + } + + FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, WRITE_EN), 0); + + timeout = 10; + do { + ack = FUDW_MGMT_READ(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, ACK)); + if (ack == 0) + break; + DELAY(10); + } while (--timeout > 0); + + if (timeout == 0) { + device_printf(sc->dev, "Timeout waiting for write un-ACK\n"); + return (ETIMEDOUT); + } + + return (0); +} + +static int +fupci_phy_init(struct fupci_softc *sc) +{ + device_t dev; + int error, phy, lane; + + dev = sc->dev; + + /* Assert core power-on reset (active low) */ + error = gpio_pin_set_active(sc->porst_pin, false); + if (error != 0) { + device_printf(dev, "Cannot assert power-on reset: %d\n", + error); + return (error); + } + + /* Assert PERST_N */ + FUDW_MGMT_WRITE(sc, FUDW_MGMT_PERST_N, 0); + + /* Enable power */ + error = gpio_pin_set_active(sc->pwren_pin, true); + if (error != 0) { + device_printf(dev, "Cannot enable power: %d\n", error); + return (error); + } + + /* Hold PERST for 100ms as per the PCIe spec */ + DELAY(100); + + /* Deassert PERST_N */ + FUDW_MGMT_WRITE(sc, FUDW_MGMT_PERST_N, 1); + + /* Deassert core power-on reset (active low) */ + error = gpio_pin_set_active(sc->porst_pin, true); + if (error != 0) { + device_printf(dev, "Cannot deassert power-on reset: %d\n", + error); + return (error); + } + + /* Enable the aux clock */ + error = clk_enable(sc->pcie_aux_clk); + if (error != 0) { + device_printf(dev, "Cannot enable aux clock: %d\n", error); + return (error); + } + + /* Hold LTSSM in reset whilst initialising the PHYs */ + FUDW_MGMT_WRITE(sc, FUDW_MGMT_HOLD_PHY_RST, 1); + + /* Deassert the aux reset */ + error = hwreset_deassert(sc->pcie_aux_rst); + if (error != 0) { + device_printf(dev, "Cannot deassert aux reset: %d\n", error); + return (error); + } + + /* Enable control register interface */ + for (phy = 0; phy < FUDW_PHYS; ++phy) + FUDW_MGMT_WRITE(sc, FUDW_MGMT_PHY_CR_PARA_REG(phy, SEL), 1); + + /* Wait for enable to take effect */ + DELAY(1); + + /* Initialise lane configuration */ + for (phy = 0; phy < FUDW_PHYS; ++phy) { + for (lane = 0; lane < FUDW_LANES_PER_PHY; ++lane) + fupci_phy_write(sc, phy, FUDW_MGMT_PHY_LANE(lane), + FUDW_MGMT_PHY_LANE_INIT); + } + + /* Disable the aux clock whilst taking the LTSSM out of reset */ + error = clk_disable(sc->pcie_aux_clk); + if (error != 0) { + device_printf(dev, "Cannot disable aux clock: %d\n", error); + return (error); + } + + /* Take LTSSM out of reset */ + FUDW_MGMT_WRITE(sc, FUDW_MGMT_HOLD_PHY_RST, 0); + + /* Enable the aux clock again */ + error = clk_enable(sc->pcie_aux_clk); + if (error != 0) { + device_printf(dev, "Cannot re-enable aux clock: %d\n", error); + return (error); + } + + /* Put the controller in Root Complex mode */ + FUDW_MGMT_WRITE(sc, FUDW_MGMT_DEVICE_TYPE, FUDW_MGMT_DEVICE_TYPE_RC); + + return (0); +} + +static void +fupci_dbi_protect(struct fupci_softc *sc, bool protect) +{ + uint32_t reg; + + reg = pci_dw_dbi_rd4(sc->dev, DW_MISC_CONTROL_1); + if (protect) + reg &= ~DBI_RO_WR_EN; + else + reg |= DBI_RO_WR_EN; + pci_dw_dbi_wr4(sc->dev, DW_MISC_CONTROL_1, reg); +} + +static int +fupci_init(struct fupci_softc *sc) +{ + /* Enable 32-bit I/O window */ + fupci_dbi_protect(sc, false); + pci_dw_dbi_wr2(sc->dev, PCIR_IOBASEL_1, + (PCIM_BRIO_32 << 8) | PCIM_BRIO_32); + fupci_dbi_protect(sc, true); + + /* Enable LTSSM */ + FUDW_MGMT_WRITE(sc, FUDW_MGMT_LTSSM_EN, 1); + + return (0); +} + +static int +fupci_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "SiFive FU740 PCIe Controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +fupci_attach(device_t dev) +{ + struct fupci_softc *sc; + phandle_t node; + int error, rid; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + sc->dev = dev; + + rid = 0; + error = ofw_bus_find_string_index(node, "reg-names", "dbi", &rid); + if (error != 0) { + device_printf(dev, "Cannot get DBI memory: %d\n", error); + goto fail; + } + sc->dw_sc.dbi_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->dw_sc.dbi_res == NULL) { + device_printf(dev, "Cannot allocate DBI memory\n"); + error = ENXIO; + goto fail; + } + + rid = 0; + error = ofw_bus_find_string_index(node, "reg-names", "mgmt", &rid); + if (error != 0) { + device_printf(dev, "Cannot get management space memory: %d\n", + error); + goto fail; + } + sc->mgmt_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mgmt_res == NULL) { + device_printf(dev, "Cannot allocate management space memory\n"); + error = ENXIO; + goto fail; + } + + error = gpio_pin_get_by_ofw_property(dev, node, "reset-gpios", + &sc->porst_pin); + /* Old U-Boot device tree uses perstn-gpios */ + if (error == ENOENT) + error = gpio_pin_get_by_ofw_property(dev, node, "perstn-gpios", + &sc->porst_pin); + if (error != 0) { + device_printf(dev, "Cannot get power-on reset GPIO: %d\n", + error); + goto fail; + } + error = gpio_pin_setflags(sc->porst_pin, GPIO_PIN_OUTPUT); + if (error != 0) { + device_printf(dev, "Cannot configure power-on reset GPIO: %d\n", + error); + goto fail; + } + + error = gpio_pin_get_by_ofw_property(dev, node, "pwren-gpios", + &sc->pwren_pin); + if (error != 0) { + device_printf(dev, "Cannot get power enable GPIO: %d\n", + error); + goto fail; + } + error = gpio_pin_setflags(sc->pwren_pin, GPIO_PIN_OUTPUT); + if (error != 0) { + device_printf(dev, "Cannot configure power enable GPIO: %d\n", + error); + goto fail; + } + + error = clk_get_by_ofw_name(dev, node, "pcie_aux", &sc->pcie_aux_clk); + /* Old U-Boot device tree uses pcieaux */ + if (error == ENOENT) + error = clk_get_by_ofw_name(dev, node, "pcieaux", + &sc->pcie_aux_clk); + if (error != 0) { + device_printf(dev, "Cannot get aux clock: %d\n", error); + goto fail; + } + + error = hwreset_get_by_ofw_idx(dev, node, 0, &sc->pcie_aux_rst); + if (error != 0) { + device_printf(dev, "Cannot get aux reset: %d\n", error); + goto fail; + } + + error = fupci_phy_init(sc); + if (error != 0) + goto fail; + + error = pci_dw_init(dev); + if (error != 0) + goto fail; + + error = fupci_init(sc); + if (error != 0) + goto fail; + + return (bus_generic_attach(dev)); + +fail: + /* XXX Cleanup */ + return (error); +} + +static int +fupci_get_link(device_t dev, bool *status) +{ + uint32_t reg; + + reg = pci_dw_dbi_rd4(dev, FUDW_DBI_PORT_DBG1); + *status = (reg & FUDW_DBI_PORT_DBG1_LINK_UP) != 0 && + (reg & FUDW_DBI_PORT_DBG1_LINK_IN_TRAINING) == 0; + + return (0); +} + +static device_method_t fupci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fupci_probe), + DEVMETHOD(device_attach, fupci_attach), + + /* PCI DW interface */ + DEVMETHOD(pci_dw_get_link, fupci_get_link), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(pcib, fupci_driver, fupci_methods, + sizeof(struct fupci_softc), pci_dw_driver); +static devclass_t fupci_devclass; +DRIVER_MODULE(fu740_pci_dw, simplebus, fupci_driver, fupci_devclass, + NULL, NULL);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202109071209.187C9rko087503>