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 ***