The branch main has been updated by mhorne:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=7d955e02b1d8da8412720cb6d34940c1ee6d5257

commit 7d955e02b1d8da8412720cb6d34940c1ee6d5257
Author:     Jari Sihvola <js...@gmx.com>
AuthorDate: 2025-07-31 21:47:06 +0000
Commit:     Mitchell Horne <mho...@freebsd.org>
CommitDate: 2025-08-07 23:10:32 +0000

    jh7110_pcie: Add StarFive JH7110 PCIe controller driver
    
    JH7110 has two PCIE controller devices. First one is used by board's
    integrated USB which has no driver. Switching PHY to USB mode is not
    currently implemented. This functionality could be added in a form of a
    separate PCIE PHY driver if needed. PHY is on by default and there's no
    need to switch it on.
    
    Pre/post_ithread and post_filter methods are not used for interrupt
    masking since they are meant for level-triggered interrupts whereas
    JH7110's MSI interrupts are edge triggered (and INTx interrupts do not
    use this irqsrc scheme at all). Pre_ithread method is nevertheless used
    for MSI bottom acking.
    
    The driver has been tested with Kingston SNV2S NVME SSD The
    functionality of INTx and MSI interrupts (as opposed to default MSIx)
    has been tested by forcing NVME to use them.
    
    Reviewed by:    mhorne
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D47919
---
 sys/riscv/starfive/files.starfive |    1 +
 sys/riscv/starfive/jh7110_pcie.c  | 1025 +++++++++++++++++++++++++++++++++++++
 2 files changed, 1026 insertions(+)

diff --git a/sys/riscv/starfive/files.starfive 
b/sys/riscv/starfive/files.starfive
index 354b4a9f65ce..4db35e8cb351 100644
--- a/sys/riscv/starfive/files.starfive
+++ b/sys/riscv/starfive/files.starfive
@@ -10,4 +10,5 @@ dev/eqos/if_eqos_if.m                 optional        eqos
 dev/eqos/if_eqos_starfive.c            optional        eqos
 
 riscv/starfive/jh7110_gpio.c           standard
+riscv/starfive/jh7110_pcie.c           optional        pci fdt
 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
index 000000000000..2d0a4be69b2c
--- /dev/null
+++ b/sys/riscv/starfive/jh7110_pcie.c
@@ -0,0 +1,1025 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Jari Sihvola <js...@gmx.com>
+ */
+
+/* JH7110 PCIe controller driver */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/clk/clk.h>
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/hwreset/hwreset.h>
+#include <dev/regulator/regulator.h>
+#include <dev/syscon/syscon.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 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 = ilog2(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)
+{
+       uint32_t val;
+       int err;
+
+       /* 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);
+
+       mtx_destroy(&sc->msi_mtx);
+}
+
+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;
+
+       /* 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;
+       }
*** 91 LINES SKIPPED ***

Reply via email to