Page MenuHomeFreeBSD

D47919.diff
No OneTemporary

D47919.diff

diff --git a/sys/riscv/starfive/files.starfive b/sys/riscv/starfive/files.starfive
--- a/sys/riscv/starfive/files.starfive
+++ b/sys/riscv/starfive/files.starfive
@@ -4,5 +4,6 @@
dev/clk/starfive/jh7110_clk_sys.c standard
dev/clk/starfive/jh7110_clk_stg.c standard
dev/mmc/host/dwmmc_starfive.c optional dwmmc_starfive fdt
+riscv/starfive/jh7110_pcie.c optional fdt pci
riscv/starfive/starfive_syscon.c standard
diff --git a/sys/riscv/starfive/jh7110_pcie.c b/sys/riscv/starfive/jh7110_pcie.c
new file mode 100644
--- /dev/null
+++ b/sys/riscv/starfive/jh7110_pcie.c
@@ -0,0 +1,1038 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Jari Sihvola <jsihv@gmx.com>
+ */
+
+/* JH7110 PCIe controller driver */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/clk/clk.h>
+#include <dev/hwreset/hwreset.h>
+#include <dev/regulator/regulator.h>
+#include <dev/syscon/syscon.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/pci_host_generic.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcib_private.h>
+
+#include "msi_if.h"
+#include "ofw_bus_if.h"
+#include "pcib_if.h"
+#include "pic_if.h"
+#include "syscon_if.h"
+
+#define IRQ_LOCAL_MASK 0x180
+#define IRQ_LOCAL_STATUS 0x184
+#define IRQ_MSI_BASE 0x190
+#define IRQ_MSI_STATUS 0x194
+
+#define MSI_MASK 0x10000000
+#define INTX_MASK 0xf000000
+#define ERROR_MASK 0x80770000
+
+#define MSI_COUNT 32
+#define MSI_USED 0x1
+#define MSI_PCIE0_MASK_OFFSET 0xa0;
+#define MSI_PCIE1_MASK_OFFSET 0xf0;
+
+#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800
+#define ATR0_AXI4_SLV0_SRC_ADDR 0x804
+#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808
+#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810
+#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80c
+#define ATR_ENTRY_SIZE 0x20
+#define ATR0_PCIE_ATR_SIZE 0x25
+#define ATR0_PCIE_ATR_SIZE_SHIFT 1
+#define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600
+#define ATR0_PCIE_WIN0_SRC_ADDR 0x604
+#define ATR0_ENABLE 1
+
+#define PCIE_TXRX_INTERFACE 0x0
+#define PCIE_CONF_INTERFACE 0x1
+#define PCIE_WINCONF 0xfc
+#define PREF_MEM_WIN_64_SUPPORT (1U << 3)
+
+#define STG_AXI4_SLVL_AW_MASK 0x7fff
+#define STG_AXI4_SLVL_AR_MASK 0x7fff00
+#define STG_PCIE0_BASE 0x48
+#define STG_PCIE1_BASE 0x1f8
+#define STG_RP_NEP_OFFSET 0xe8
+#define STG_K_RP_NEP (1U << 8)
+#define STG_CKREF_MASK 0xC0000
+#define STG_CKREF_VAL 0x80000
+#define STG_CLKREQ (1U << 22)
+#define STG_AR_OFFSET 0x78
+#define STG_AW_OFFSET 0x7c
+#define STG_AXI4_SLVL_ARFUNC_SHIFT 0x8
+#define STG_LNKSTA_OFFSET 0x170
+#define STG_LINK_UP (1U << 5)
+
+#define PHY_FUNC_SHIFT 9
+#define PHY_FUNC_DIS (1U << 15)
+#define PCI_MISC_REG 0xb4
+#define PCI_GENERAL_SETUP_REG 0x80
+#define PCI_CONF_SPACE_REGS 0x1000
+#define ROOTPORT_ENABLE 0x1
+#define PMSG_RX_SUPPORT_REG 0x3f0
+#define PMSG_LTR_SUPPORT (1U << 2)
+#define PCI_CLASS_BRIDGE_PCI 0x0604
+#define PCI_IDS_CLASS_CODE_SHIFT 16
+#define PCIE_PCI_IDS_REG 0x9c
+#define REV_ID_MASK 0xff
+
+#define PLDA_AXI_POST_ERR (1U << 16)
+#define PLDA_AXI_FETCH_ERR (1U << 17)
+#define PLDA_AXI_DISCARD_ERR (1U << 18)
+#define PLDA_PCIE_POST_ERR (1U << 20)
+#define PLDA_PCIE_FETCH_ERR (1U << 21)
+#define PLDA_PCIE_DISCARD_ERR (1U << 22)
+#define PLDA_SYS_ERR (1U << 31)
+
+/* Compatible devices. */
+static struct ofw_compat_data compat_data[] = {
+ {"starfive,jh7110-pcie", 1},
+ {NULL, 0},
+};
+
+struct jh7110_pcie_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+ u_int is_used;
+};
+
+struct jh7110_pcie_softc {
+ struct ofw_pci_softc ofw_pci;
+ device_t dev;
+ phandle_t node;
+
+ struct resource *reg_mem_res;
+ struct resource *cfg_mem_res;
+ struct resource *irq_res;
+ struct jh7110_pcie_irqsrc *isrcs;
+ void *irq_cookie;
+ struct syscon *stg_syscon;
+ uint64_t stg_baddr;
+
+ struct ofw_pci_range range_mem32;
+ struct ofw_pci_range range_mem64;
+
+ struct mtx msi_mtx;
+ uint64_t msi_mask_offset;
+
+ gpio_pin_t perst_pin;
+
+ clk_t clk_noc;
+ clk_t clk_tl;
+ clk_t clk_axi;
+ clk_t clk_apb;
+
+ hwreset_t rst_mst0;
+ hwreset_t rst_slv0;
+ hwreset_t rst_slv;
+ hwreset_t rst_brg;
+ hwreset_t rst_core;
+ hwreset_t rst_apb;
+};
+
+#define LOW32(val) (uint32_t)(val)
+#define HI32(val) (uint32_t)(val >> 32)
+
+#define RD4(sc, reg) bus_read_4((sc)->reg_mem_res, (reg))
+#define WR4(sc, reg, val) bus_write_4((sc)->reg_mem_res, (reg), (val))
+
+static int
+jh7110_pcie_ilog(int x)
+{
+ int log = 0;
+ while (x >>= 1)
+ log++;
+
+ return (log);
+}
+
+static uint32_t
+jh7110_pcie_read_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, int bytes)
+{
+ struct jh7110_pcie_softc *sc;
+ uint32_t data, offset;
+
+ sc = device_get_softc(dev);
+ offset = PCIE_ADDR_OFFSET(bus, slot, func, reg);
+
+ /* Certain config registers are not supposed to be accessed from here */
+ if (bus == 0 && (offset == PCIR_BAR(0) || offset == PCIR_BAR(1)))
+ return (~0U);
+
+ switch (bytes) {
+ case 1:
+ data = bus_read_1(sc->cfg_mem_res, offset);
+ break;
+ case 2:
+ data = le16toh(bus_read_2(sc->cfg_mem_res, offset));
+ break;
+ case 4:
+ data = le32toh(bus_read_4(sc->cfg_mem_res, offset));
+ break;
+ default:
+ return (~0U);
+ }
+
+ return (data);
+}
+
+static void
+jh7110_pcie_write_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, uint32_t val, int bytes)
+{
+ struct jh7110_pcie_softc *sc;
+ uint32_t offset;
+
+ sc = device_get_softc(dev);
+ offset = PCIE_ADDR_OFFSET(bus, slot, func, reg);
+
+ /* Certain config registers are not supposed to be accessed from here */
+ if (bus == 0 && (offset == PCIR_BAR(0) || offset == PCIR_BAR(1)))
+ return;
+
+ switch (bytes) {
+ case 1:
+ bus_write_1(sc->cfg_mem_res, offset, val);
+ break;
+ case 2:
+ bus_write_2(sc->cfg_mem_res, offset, htole16(val));
+ break;
+ case 4:
+ bus_write_4(sc->cfg_mem_res, offset, htole32(val));
+ break;
+ default:
+ return;
+ }
+}
+
+
+static int
+jh7110_pcie_intr(void *arg)
+{
+ struct jh7110_pcie_softc *sc;
+ struct trapframe *tf;
+ struct jh7110_pcie_irqsrc *irq;
+ uint32_t reg, irqbits;
+ int err, i;
+
+ sc = (struct jh7110_pcie_softc *)arg;
+ tf = curthread->td_intr_frame;
+
+ reg = RD4(sc, IRQ_LOCAL_STATUS);
+ if (reg == 0)
+ return (ENXIO);
+
+ if ((reg & MSI_MASK) != 0) {
+ WR4(sc, IRQ_LOCAL_STATUS, MSI_MASK);
+
+ irqbits = RD4(sc, IRQ_MSI_STATUS);
+ for (i = 0; irqbits != 0; i++) {
+ if ((irqbits & (1U << i)) != 0) {
+ irq = &sc->isrcs[i];
+ err = intr_isrc_dispatch(&irq->isrc, tf);
+ if (err != 0)
+ device_printf(sc->dev,
+ "MSI 0x%x gives error %d\n",
+ i, err);
+ irqbits &= ~(1U << i);
+ }
+ }
+ }
+ if ((reg & INTX_MASK) != 0) {
+ irqbits = (reg & INTX_MASK);
+ WR4(sc, IRQ_LOCAL_STATUS, irqbits);
+ }
+ if ((reg & ERROR_MASK) != 0) {
+ irqbits = (reg & ERROR_MASK);
+ if ((reg & PLDA_AXI_POST_ERR) != 0)
+ device_printf(sc->dev, "axi post error\n");
+ if ((reg & PLDA_AXI_FETCH_ERR) != 0)
+ device_printf(sc->dev, "axi fetch error\n");
+ if ((reg & PLDA_AXI_DISCARD_ERR) != 0)
+ device_printf(sc->dev, "axi discard error\n");
+ if ((reg & PLDA_PCIE_POST_ERR) != 0)
+ device_printf(sc->dev, "pcie post error\n");
+ if ((reg & PLDA_PCIE_FETCH_ERR) != 0)
+ device_printf(sc->dev, "pcie fetch error\n");
+ if ((reg & PLDA_PCIE_DISCARD_ERR) != 0)
+ device_printf(sc->dev, "pcie discard error\n");
+ if ((reg & PLDA_SYS_ERR) != 0)
+ device_printf(sc->dev, "pcie sys error\n");
+ WR4(sc, IRQ_LOCAL_STATUS, irqbits);
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static int
+jh7110_pcie_route_interrupt(device_t bus, device_t dev, int pin)
+{
+ struct jh7110_pcie_softc *sc;
+ u_int irq;
+
+ sc = device_get_softc(bus);
+ irq = intr_map_clone_irq(rman_get_start(sc->irq_res));
+ device_printf(bus, "route pin %d for device %d.%d to %u\n",
+ pin, pci_get_slot(dev), pci_get_function(dev), irq);
+
+ return (irq);
+}
+
+static int
+jh7110_pcie_maxslots(device_t dev)
+{
+ return (PCI_SLOTMAX);
+}
+
+static int
+jh7110_pcie_msi_alloc_msi(device_t dev, device_t child, int count, int maxcount,
+ device_t *pic, struct intr_irqsrc **srcs)
+{
+ struct jh7110_pcie_softc *sc;
+ int i, beg;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->msi_mtx);
+
+ /* Search for a requested contiguous region */
+ for (beg = 0; beg + count < MSI_COUNT; ) {
+ for (i = beg; i < beg + count; i++) {
+ if (sc->isrcs[i].is_used == MSI_USED)
+ goto next;
+ }
+ goto found;
+next:
+ beg = i + 1;
+ }
+
+ /* Requested area not found */
+ mtx_unlock(&sc->msi_mtx);
+ device_printf(dev, "warning: failed to allocate %d MSIs.\n", count);
+
+ return (ENXIO);
+
+found:
+ /* Mark and allocate messages */
+ for (i = 0; i < count; ++i) {
+ sc->isrcs[i + beg].is_used = MSI_USED;
+ srcs[i] = &(sc->isrcs[i + beg].isrc);
+ }
+
+ mtx_unlock(&sc->msi_mtx);
+ *pic = device_get_parent(dev);
+
+ return (0);
+}
+
+
+static int
+jh7110_pcie_alloc_msi(device_t pci, device_t child, int count,
+ int maxcount, int *irqs)
+{
+ phandle_t msi_parent;
+ int err;
+
+ msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
+ err = intr_alloc_msi(pci, child, msi_parent, count, maxcount, irqs);
+
+ return (err);
+}
+
+static int
+jh7110_pcie_release_msi(device_t pci, device_t child, int count, int *irqs)
+{
+ phandle_t msi_parent;
+ int err;
+
+ msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
+ err = intr_release_msi(pci, child, msi_parent, count, irqs);
+
+ return (err);
+}
+
+static int
+jh7110_pcie_msi_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
+ uint64_t *addr, uint32_t *data)
+{
+ struct jh7110_pcie_irqsrc *jhirq = (struct jh7110_pcie_irqsrc *)isrc;
+
+ *addr = IRQ_MSI_BASE;
+ *data = jhirq->irq;
+
+ return (0);
+}
+
+
+static int
+jh7110_pcie_map_msi(device_t pci, device_t child, int irq, uint64_t *addr,
+ uint32_t *data)
+{
+ phandle_t msi_parent;
+ int err;
+
+ msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
+
+ err = intr_map_msi(pci, child, msi_parent, irq, addr, data);
+ if (err != 0) {
+ device_printf(pci, "intr_map_msi() failed\n");
+ return (err);
+ }
+
+ return (err);
+}
+
+static int
+jh7110_pcie_alloc_msix(device_t pci, device_t child, int *irq)
+{
+ return (jh7110_pcie_alloc_msi(pci, child, 1, 32, irq));
+}
+
+static int
+jh7110_pcie_release_msix(device_t pci, device_t child, int irq)
+{
+ phandle_t msi_parent;
+ int err;
+
+ msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
+ err = intr_release_msix(pci, child, msi_parent, irq);
+
+ return (err);
+}
+
+static int
+jh7110_pcie_msi_alloc_msix(device_t dev, device_t child, device_t *pic,
+ struct intr_irqsrc **isrcp)
+{
+ return (jh7110_pcie_msi_alloc_msi(dev, child, 1, 32, pic, isrcp));
+}
+
+static int
+jh7110_pcie_msi_release_msi(device_t dev, device_t child, int count,
+ struct intr_irqsrc **isrc)
+{
+ struct jh7110_pcie_softc *sc;
+ struct jh7110_pcie_irqsrc *irq;
+ int i;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->msi_mtx);
+
+ for (i = 0; i < count; i++) {
+ irq = (struct jh7110_pcie_irqsrc *)isrc[i];
+
+ KASSERT((irq->is_used & MSI_USED) == MSI_USED,
+ ("%s: Trying to release an unused MSI(-X) interrupt",
+ __func__));
+
+ irq->is_used = 0;
+ }
+
+ mtx_unlock(&sc->msi_mtx);
+ return (0);
+}
+
+static int
+jh7110_pcie_msi_release_msix(device_t dev, device_t child,
+ struct intr_irqsrc *isrc)
+{
+ return (jh7110_pcie_msi_release_msi(dev, child, 1, &isrc));
+}
+
+static void
+jh7110_pcie_msi_mask(device_t dev, struct intr_irqsrc *isrc, bool mask)
+{
+ struct jh7110_pcie_softc *sc;
+ struct jh7110_pcie_irqsrc *jhirq = (struct jh7110_pcie_irqsrc *)isrc;
+ uint32_t reg, irq;
+
+ sc = device_get_softc(dev);
+ irq = jhirq->irq;
+
+ reg = bus_read_4(sc->cfg_mem_res, sc->msi_mask_offset);
+ if (mask != 0)
+ reg &= ~(1U << irq);
+ else
+ reg |= (1U << irq);
+ bus_write_4(sc->cfg_mem_res, sc->msi_mask_offset, reg);
+}
+
+static void
+jh7110_pcie_msi_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ jh7110_pcie_msi_mask(dev, isrc, true);
+}
+
+static void
+jh7110_pcie_msi_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ jh7110_pcie_msi_mask(dev, isrc, false);
+}
+
+static void
+jh7110_pcie_msi_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct jh7110_pcie_softc *sc;
+ struct jh7110_pcie_irqsrc *jhirq = (struct jh7110_pcie_irqsrc *)isrc;
+ uint32_t irq;
+
+ sc = device_get_softc(dev);
+ irq = jhirq->irq;
+
+ /* MSI bottom ack */
+ WR4(sc, IRQ_MSI_STATUS, (1U << irq));
+}
+
+static int
+jh7110_pcie_decode_ranges(struct jh7110_pcie_softc *sc,
+ struct ofw_pci_range *ranges, int nranges)
+{
+ int i;
+
+ for (i = 0; i < nranges; i++) {
+ if (((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) ==
+ OFW_PCI_PHYS_HI_SPACE_MEM64)) {
+ if (sc->range_mem64.size != 0) {
+ device_printf(sc->dev,
+ "Duplicate range mem64 found in DT\n");
+ return (ENXIO);
+ }
+ sc->range_mem64 = ranges[i];
+ } else if (((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) ==
+ OFW_PCI_PHYS_HI_SPACE_MEM32)) {
+ if (sc->range_mem32.size != 0) {
+ device_printf(sc->dev,
+ "Duplicated range mem32 found in DT\n");
+ return (ENXIO);
+ }
+ sc->range_mem32 = ranges[i];
+ }
+ }
+ return (0);
+}
+
+static int
+jh7110_pcie_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, "Starfive JH7110 PCIe controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static void
+jh7110_pcie_set_atr(device_t dev, uint64_t axi_begin, uint64_t pci_begin,
+ uint64_t win_size, uint32_t win_idx)
+{
+ struct jh7110_pcie_softc *sc;
+ uint32_t val, taddr_size;
+
+ sc = device_get_softc(dev);
+
+ if (win_idx == 0)
+ val = PCIE_CONF_INTERFACE;
+ else
+ val = PCIE_TXRX_INTERFACE;
+
+ WR4(sc, ATR0_AXI4_SLV0_TRSL_PARAM + win_idx * ATR_ENTRY_SIZE, val);
+
+ taddr_size = jh7110_pcie_ilog(win_size) - 1;
+ val = LOW32(axi_begin) | taddr_size << ATR0_PCIE_ATR_SIZE_SHIFT |
+ ATR0_ENABLE;
+
+ WR4(sc, ATR0_AXI4_SLV0_SRCADDR_PARAM + win_idx * ATR_ENTRY_SIZE, val);
+
+ val = HI32(axi_begin);
+ WR4(sc, ATR0_AXI4_SLV0_SRC_ADDR + win_idx * ATR_ENTRY_SIZE, val);
+
+ val = LOW32(pci_begin);
+ WR4(sc, ATR0_AXI4_SLV0_TRSL_ADDR_LSB + win_idx * ATR_ENTRY_SIZE, val);
+
+ val = HI32(pci_begin);
+ WR4(sc, ATR0_AXI4_SLV0_TRSL_ADDR_UDW + win_idx * ATR_ENTRY_SIZE, val);
+
+ val = RD4(sc, ATR0_PCIE_WIN0_SRCADDR_PARAM);
+ val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT);
+
+ WR4(sc, ATR0_PCIE_WIN0_SRCADDR_PARAM, val);
+ WR4(sc, ATR0_PCIE_WIN0_SRC_ADDR, 0);
+}
+
+static int
+jh7110_pcie_parse_fdt_resources(struct jh7110_pcie_softc *sc)
+{
+ int err;
+ uint32_t val;
+
+ /* Getting clocks */
+ if (clk_get_by_ofw_name(sc->dev, 0, "noc", &sc->clk_noc) != 0) {
+ device_printf(sc->dev, "could not get noc clock\n");
+ sc->clk_noc = NULL;
+ return (ENXIO);
+ }
+ if (clk_get_by_ofw_name(sc->dev, 0, "tl", &sc->clk_tl) != 0) {
+ device_printf(sc->dev, "could not get tl clock\n");
+ sc->clk_tl = NULL;
+ return (ENXIO);
+ }
+ if (clk_get_by_ofw_name(sc->dev, 0, "axi_mst0", &sc->clk_axi) != 0) {
+ device_printf(sc->dev, "could not get axi_mst0 clock\n");
+ sc->clk_axi = NULL;
+ return (ENXIO);
+ }
+ if (clk_get_by_ofw_name(sc->dev, 0, "apb", &sc->clk_apb) != 0) {
+ device_printf(sc->dev, "could not get apb clock\n");
+ sc->clk_apb = NULL;
+ return (ENXIO);
+ }
+
+ /* Getting resets */
+ err = hwreset_get_by_ofw_name(sc->dev, 0, "mst0", &sc->rst_mst0);
+ if (err != 0) {
+ device_printf(sc->dev, "cannot get 'rst_mst0' reset\n");
+ return (ENXIO);
+ }
+ err = hwreset_get_by_ofw_name(sc->dev, 0, "slv0", &sc->rst_slv0);
+ if (err != 0) {
+ device_printf(sc->dev, "cannot get 'rst_slv0' reset\n");
+ return (ENXIO);
+ }
+ err = hwreset_get_by_ofw_name(sc->dev, 0, "slv", &sc->rst_slv);
+ if (err != 0) {
+ device_printf(sc->dev, "cannot get 'rst_slv' reset\n");
+ return (ENXIO);
+ }
+ err = hwreset_get_by_ofw_name(sc->dev, 0, "brg", &sc->rst_brg);
+ if (err != 0) {
+ device_printf(sc->dev, "cannot get 'rst_brg' reset\n");
+ return (ENXIO);
+ }
+ err = hwreset_get_by_ofw_name(sc->dev, 0, "core", &sc->rst_core);
+ if (err != 0) {
+ device_printf(sc->dev, "cannot get 'rst_core' reset\n");
+ return (ENXIO);
+ }
+ err = hwreset_get_by_ofw_name(sc->dev, 0, "apb", &sc->rst_apb);
+ if (err != 0) {
+ device_printf(sc->dev, "cannot get 'rst_apb' reset\n");
+ return (ENXIO);
+ }
+
+ /* Getting PCI endpoint reset pin */
+ err = gpio_pin_get_by_ofw_property(sc->dev, sc->node, "perst-gpios",
+ &sc->perst_pin);
+ if (err != 0) {
+ device_printf(sc->dev, "Cannot get perst-gpios\n");
+ return (ENXIO);
+ }
+
+ /* Getting syscon property */
+ if (syscon_get_by_ofw_property(sc->dev, sc->node, "starfive,stg-syscon",
+ &sc->stg_syscon) != 0) {
+ device_printf(sc->dev, "Cannot get starfive,stg-syscon\n");
+ return (ENXIO);
+ }
+
+ /* Assigning syscon base address and MSI mask offset */
+ err = OF_getencprop(sc->node, "linux,pci-domain", &val, sizeof(val));
+ if (err == -1) {
+ device_printf(sc->dev,
+ "Couldn't get pci-domain property, error: %d\n", err);
+ return (ENXIO);
+ }
+ if (val == 0) {
+ sc->stg_baddr = STG_PCIE0_BASE;
+ sc->msi_mask_offset = MSI_PCIE0_MASK_OFFSET;
+ } else if (val == 1) {
+ sc->stg_baddr = STG_PCIE1_BASE;
+ sc->msi_mask_offset = MSI_PCIE1_MASK_OFFSET;
+ } else {
+ device_printf(sc->dev, "Error: an invalid pci-domain value\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static void
+jh7110_pcie_release_resources(device_t dev)
+{
+ struct jh7110_pcie_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->irq_res != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_cookie);
+ if (sc->irq_res != NULL)
+ bus_free_resource(dev, SYS_RES_IRQ, sc->irq_res);
+ if (sc->reg_mem_res != NULL)
+ bus_free_resource(dev, SYS_RES_MEMORY, sc->reg_mem_res);
+ if (sc->cfg_mem_res != NULL)
+ bus_free_resource(dev, SYS_RES_MEMORY, sc->cfg_mem_res);
+
+ if (sc->clk_noc != NULL)
+ clk_release(sc->clk_noc);
+ if (sc->clk_tl != NULL)
+ clk_release(sc->clk_tl);
+ if (sc->clk_axi != NULL)
+ clk_release(sc->clk_axi);
+ if (sc->clk_apb != NULL)
+ clk_release(sc->clk_apb);
+
+ gpio_pin_release(sc->perst_pin);
+
+ hwreset_release(sc->rst_mst0);
+ hwreset_release(sc->rst_slv0);
+ hwreset_release(sc->rst_slv);
+ hwreset_release(sc->rst_brg);
+ hwreset_release(sc->rst_core);
+ hwreset_release(sc->rst_apb);
+}
+
+static int
+jh7110_pcie_detach(device_t dev)
+{
+ ofw_pcib_fini(dev);
+ jh7110_pcie_release_resources(dev);
+
+ return (0);
+}
+
+static int
+jh7110_pcie_attach(device_t dev)
+{
+ struct jh7110_pcie_softc *sc;
+ phandle_t xref;
+ uint32_t val;
+ int i, err, rid, irq, win_idx = 0;
+ char name[INTR_ISRC_NAMELEN];
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->node = ofw_bus_get_node(dev);
+
+ sc->irq_res = NULL;
+ sc->reg_mem_res = NULL;
+ sc->cfg_mem_res = NULL;
+ sc->clk_noc = NULL;
+ sc->clk_tl = NULL;
+ sc->clk_axi = NULL;
+ sc->clk_apb = NULL;
+
+ mtx_init(&sc->msi_mtx, "jh7110_pcie, msi_mtx", NULL, MTX_DEF);
+
+ /* Allocating memory */
+ err = ofw_bus_find_string_index(sc->node, "reg-names", "apb", &rid);
+ if (err != 0) {
+ device_printf(dev, "Cannot get apb memory\n");
+ goto out;
+ }
+
+ sc->reg_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->reg_mem_res == NULL) {
+ device_printf(dev, "Cannot allocate apb memory\n");
+ err = ENXIO;
+ goto out;
+ }
+
+ err = ofw_bus_find_string_index(sc->node, "reg-names", "cfg", &rid);
+ if (err != 0) {
+ device_printf(dev, "Cannot get cfg memory\n");
+ err = ENXIO;
+ goto out;
+ }
+
+ sc->cfg_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->cfg_mem_res == NULL) {
+ device_printf(dev, "Cannot allocate cfg memory\n");
+ err = ENXIO;
+ goto out;
+ }
+
+ /* Getting device tree properties */
+ if (jh7110_pcie_parse_fdt_resources(sc) != 0)
+ goto out;
+
+ /* Setting GPIO pin flags */
+ gpio_pin_setflags(sc->perst_pin, GPIO_PIN_OUTPUT |
+ GPIO_PIN_PRESET_HIGH);
+
+ /* Clearing interrupts, enabling MSI */
+ WR4(sc, IRQ_LOCAL_STATUS, 0xffffffff);
+ WR4(sc, IRQ_LOCAL_MASK, INTX_MASK | ERROR_MASK | MSI_MASK);
+
+ /* Setting host up */
+ SYSCON_MODIFY_4(sc->stg_syscon, sc->stg_baddr + STG_RP_NEP_OFFSET,
+ STG_K_RP_NEP, STG_K_RP_NEP);
+ SYSCON_MODIFY_4(sc->stg_syscon, sc->stg_baddr + STG_AW_OFFSET,
+ STG_CKREF_MASK, STG_CKREF_VAL);
+ SYSCON_MODIFY_4(sc->stg_syscon, sc->stg_baddr + STG_AW_OFFSET,
+ STG_CLKREQ, STG_CLKREQ);
+
+ /* Enabling clocks */
+ if (clk_enable(sc->clk_noc) != 0) {
+ device_printf(dev, "could not enable noc clock\n");
+ goto out;
+ }
+ if (clk_enable(sc->clk_tl) != 0) {
+ device_printf(dev, "could not enable tl clock\n");
+ goto out;
+ }
+ if (clk_enable(sc->clk_axi) != 0) {
+ device_printf(dev, "could not enable axi_mst0 clock\n");
+ goto out;
+ }
+ if (clk_enable(sc->clk_apb) != 0) {
+ device_printf(dev, "could not enable apb clock\n");
+ goto out;
+ }
+
+ /* Deasserting resets */
+ err = hwreset_deassert(sc->rst_mst0);
+ if (err != 0) {
+ device_printf(sc->dev, "cannot deassert 'mst0' reset\n");
+ goto out;
+ }
+ err = hwreset_deassert(sc->rst_slv0);
+ if (err != 0) {
+ device_printf(sc->dev, "cannot deassert 'slv0' reset\n");
+ goto out;
+ }
+ err = hwreset_deassert(sc->rst_slv);
+ if (err != 0) {
+ device_printf(sc->dev, "cannot deassert 'slv' reset\n");
+ goto out;
+ }
+ err = hwreset_deassert(sc->rst_brg);
+ if (err != 0) {
+ device_printf(sc->dev, "cannot deassert 'brg' reset\n");
+ goto out;
+ }
+ err = hwreset_deassert(sc->rst_core);
+ if (err != 0) {
+ device_printf(sc->dev, "cannot deassert 'core' reset\n");
+ goto out;
+ }
+ err = hwreset_deassert(sc->rst_apb);
+ if (err != 0) {
+ device_printf(sc->dev, "cannot deassert 'apb' reset\n");
+ goto out;
+ }
+
+ err = gpio_pin_set_active(sc->perst_pin, true);
+ if (err != 0) {
+ device_printf(dev, "Cannot activate gpio pin, error %d\n", err);
+ goto out;
+ }
+
+ /* Switching off PHY functions 1-3 */
+ for (i = 1; i != 4; i++) {
+ SYSCON_MODIFY_4(sc->stg_syscon, sc->stg_baddr + STG_AR_OFFSET,
+ STG_AXI4_SLVL_AR_MASK, (i << PHY_FUNC_SHIFT)
+ << STG_AXI4_SLVL_ARFUNC_SHIFT);
+ SYSCON_MODIFY_4(sc->stg_syscon, sc->stg_baddr + STG_AW_OFFSET,
+ STG_AXI4_SLVL_AW_MASK, i << PHY_FUNC_SHIFT);
+
+ val = RD4(sc, PCI_MISC_REG);
+ WR4(sc, PCI_MISC_REG, val | PHY_FUNC_DIS);
+ }
+
+ SYSCON_MODIFY_4(sc->stg_syscon, sc->stg_baddr + STG_AR_OFFSET,
+ STG_AXI4_SLVL_AR_MASK, 0);
+ SYSCON_MODIFY_4(sc->stg_syscon, sc->stg_baddr + STG_AW_OFFSET,
+ STG_AXI4_SLVL_AW_MASK, 0);
+
+ /* Enabling root port */
+ val = RD4(sc, PCI_GENERAL_SETUP_REG);
+ WR4(sc, PCI_GENERAL_SETUP_REG, val | ROOTPORT_ENABLE);
+
+ /* Zeroing RC BAR */
+ WR4(sc, PCI_CONF_SPACE_REGS + PCIR_BAR(0), 0);
+ WR4(sc, PCI_CONF_SPACE_REGS + PCIR_BAR(1), 0);
+
+ /* Setting standard class */
+ val = RD4(sc, PCIE_PCI_IDS_REG);
+ val &= REV_ID_MASK;
+ val |= (PCI_CLASS_BRIDGE_PCI << PCI_IDS_CLASS_CODE_SHIFT);
+ WR4(sc, PCIE_PCI_IDS_REG, val);
+
+ /* Disabling latency tolerance reporting */
+ val = RD4(sc, PMSG_RX_SUPPORT_REG);
+ WR4(sc, PMSG_RX_SUPPORT_REG, val & ~PMSG_LTR_SUPPORT);
+
+ /* Setting support for 64-bit pref window */
+ val = RD4(sc, PCIE_WINCONF);
+ WR4(sc, PCIE_WINCONF, val | PREF_MEM_WIN_64_SUPPORT);
+
+ /* Holding PCI endpoint reset (perst) for 100ms, setting the pin */
+ DELAY(100);
+ err = gpio_pin_set_active(sc->perst_pin, false);
+ if (err != 0) {
+ device_printf(dev, "Cannot deassert perst pin: %d\n", err);
+ goto out;
+ }
+
+ /* Setting up an address translation window */
+ jh7110_pcie_set_atr(dev, rman_get_start(sc->cfg_mem_res), 0,
+ rman_get_size(sc->cfg_mem_res), win_idx);
+
+ err = ofw_pcib_init(dev);
+ if (err != 0) {
+ device_printf(dev, "ofw_pcib_init() fails\n");
+ goto out;
+ }
+
+ jh7110_pcie_decode_ranges(sc, sc->ofw_pci.sc_range,
+ sc->ofw_pci.sc_nrange);
+
+ jh7110_pcie_set_atr(dev, sc->range_mem32.pci, sc->range_mem32.pci,
+ sc->range_mem32.size, ++win_idx);
+ jh7110_pcie_set_atr(dev, sc->range_mem64.pci, sc->range_mem64.pci,
+ sc->range_mem64.size, ++win_idx);
+
+ /* Checking data link status */
+ for (i = 0; i != 1000; i++) {
+ val = SYSCON_READ_4(sc->stg_syscon,
+ sc->stg_baddr + STG_LNKSTA_OFFSET);
+ if ((val & STG_LINK_UP) != 0) {
+ device_printf(dev, "Link up\n");
+ break;
+ }
+ DELAY(100);
+ }
+ if ((val & STG_LINK_UP) == 0) {
+ device_printf(dev, "Cannot establish data link\n");
+ goto out;
+ }
+
+ /* Setup interrupts */
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "Cannot allocate IRQ resource\n");
+ err = ENXIO;
+ goto out_full;
+ }
+
+ err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ jh7110_pcie_intr, NULL, sc, &sc->irq_cookie);
+ if (err != 0) {
+ device_printf(dev, "Cannot setup interrupt handler\n");
+ err = ENXIO;
+ goto out_full;
+ }
+
+ sc->isrcs = malloc(sizeof(*sc->isrcs) * MSI_COUNT, M_DEVBUF,
+ M_WAITOK | M_ZERO);
+
+ snprintf(name, INTR_ISRC_NAMELEN, "%s, MSI",
+ device_get_nameunit(sc->dev));
+
+ for (irq = 0; irq < MSI_COUNT; irq++) {
+ sc->isrcs[irq].irq = irq;
+ err = intr_isrc_register(&sc->isrcs[irq].isrc, sc->dev, 0,
+ "%s,%u", name, irq);
+ if (err != 0) {
+ device_printf(dev,
+ "intr_isrs_register failed for MSI irq %d\n", irq);
+ goto out_full;
+ }
+ }
+
+ xref = OF_xref_from_node(sc->node);
+ OF_device_register_xref(xref, dev);
+
+ err = intr_msi_register(dev, xref);
+ if (err != 0) {
+ device_printf(dev, "intr_msi_register() fails\n");
+ goto out_full;
+ }
+
+ device_add_child(dev, "pci", DEVICE_UNIT_ANY);
+ bus_attach_children(dev);
+
+ return (0);
+
+out_full:
+ ofw_pcib_fini(dev);
+out:
+ jh7110_pcie_release_resources(dev);
+
+ return (err);
+}
+
+static device_method_t jh7110_pcie_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jh7110_pcie_probe),
+ DEVMETHOD(device_attach, jh7110_pcie_attach),
+ DEVMETHOD(device_detach, jh7110_pcie_detach),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, jh7110_pcie_maxslots),
+ DEVMETHOD(pcib_read_config, jh7110_pcie_read_config),
+ DEVMETHOD(pcib_write_config, jh7110_pcie_write_config),
+ DEVMETHOD(pcib_route_interrupt, jh7110_pcie_route_interrupt),
+ DEVMETHOD(pcib_map_msi, jh7110_pcie_map_msi),
+ DEVMETHOD(pcib_alloc_msi, jh7110_pcie_alloc_msi),
+ DEVMETHOD(pcib_release_msi, jh7110_pcie_release_msi),
+ DEVMETHOD(pcib_alloc_msix, jh7110_pcie_alloc_msix),
+ DEVMETHOD(pcib_release_msix, jh7110_pcie_release_msix),
+ DEVMETHOD(pcib_request_feature, pcib_request_feature_allow),
+
+ /* MSI/MSI-X */
+ DEVMETHOD(msi_alloc_msi, jh7110_pcie_msi_alloc_msi),
+ DEVMETHOD(msi_alloc_msix, jh7110_pcie_msi_alloc_msix),
+ DEVMETHOD(msi_release_msi, jh7110_pcie_msi_release_msi),
+ DEVMETHOD(msi_release_msix, jh7110_pcie_msi_release_msix),
+ DEVMETHOD(msi_map_msi, jh7110_pcie_msi_map_msi),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_enable_intr, jh7110_pcie_msi_enable_intr),
+ DEVMETHOD(pic_disable_intr, jh7110_pcie_msi_disable_intr),
+ DEVMETHOD(pic_pre_ithread, jh7110_pcie_msi_pre_ithread),
+
+ /* OFW bus interface */
+ DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
+ DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
+ DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
+ DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
+ DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(pcib, jh7110_pcie_driver, jh7110_pcie_methods,
+ sizeof(struct jh7110_pcie_softc), ofw_pcib_driver);
+DRIVER_MODULE(jh7110_pcie, simplebus, jh7110_pcie_driver, NULL, NULL);

File Metadata

Mime Type
text/plain
Expires
Sun, Jan 19, 4:50 AM (20 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15924353
Default Alt Text
D47919.diff (27 KB)

Event Timeline