Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F107786257
D47919.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
27 KB
Referenced Files
None
Subscribers
None
D47919.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D47919: Add StarFive JH7110's PCIE controller driver
Attached
Detach File
Event Timeline
Log In to Comment