Module Name: src Committed By: skrll Date: Wed Jan 1 17:53:08 UTC 2025
Modified Files: src/sys/arch/riscv/conf: GENERIC.common GENERIC64 files.riscv std.riscv std.riscv64 src/sys/arch/riscv/fdt: files.fdt src/sys/arch/riscv/starfive: files.starfive Added Files: src/sys/arch/riscv/fdt: pcihost_fdt.c pcihost_fdtvar.h src/sys/arch/riscv/include: pci_machdep.h src/sys/arch/riscv/starfive: jh7110_pcie.c Log Message: risc-v: add support for PCI and the PCIe controller in the JH7110 SoC. Testing as working with xhci and nvme on VisionFive2. Uses legacy PCI interrupts currently. MSIs to be added later. pcihost_fdt code is 99% the same as the Arm version and should be shared. To generate a diff of this commit: cvs rdiff -u -r1.15 -r1.16 src/sys/arch/riscv/conf/GENERIC.common cvs rdiff -u -r1.18 -r1.19 src/sys/arch/riscv/conf/GENERIC64 cvs rdiff -u -r1.16 -r1.17 src/sys/arch/riscv/conf/files.riscv cvs rdiff -u -r1.2 -r1.3 src/sys/arch/riscv/conf/std.riscv cvs rdiff -u -r1.4 -r1.5 src/sys/arch/riscv/conf/std.riscv64 cvs rdiff -u -r1.3 -r1.4 src/sys/arch/riscv/fdt/files.fdt cvs rdiff -u -r0 -r1.1 src/sys/arch/riscv/fdt/pcihost_fdt.c \ src/sys/arch/riscv/fdt/pcihost_fdtvar.h cvs rdiff -u -r0 -r1.1 src/sys/arch/riscv/include/pci_machdep.h cvs rdiff -u -r1.8 -r1.9 src/sys/arch/riscv/starfive/files.starfive cvs rdiff -u -r0 -r1.1 src/sys/arch/riscv/starfive/jh7110_pcie.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/riscv/conf/GENERIC.common diff -u src/sys/arch/riscv/conf/GENERIC.common:1.15 src/sys/arch/riscv/conf/GENERIC.common:1.16 --- src/sys/arch/riscv/conf/GENERIC.common:1.15 Tue Jul 16 12:01:19 2024 +++ src/sys/arch/riscv/conf/GENERIC.common Wed Jan 1 17:53:07 2025 @@ -1,5 +1,5 @@ # -# $NetBSD: GENERIC.common,v 1.15 2024/07/16 12:01:19 riastradh Exp $ +# $NetBSD: GENERIC.common,v 1.16 2025/01/01 17:53:07 skrll Exp $ # # GENERIC common RISC-V kernel config items shared between 32 and 64 # kernels @@ -149,13 +149,23 @@ qemufwcfg* at fdt? # QEMU Firmware Co # RTC devices gfrtc* at fdt? # Google Goldfish RTC +# PCIE +pcihost* at fdt? # Generic PCI host controller +pci* at pcibus? +ppb* at pci? dev ? function ? +pci* at ppb? + # USB xhci* at fdt? # XHCI +xhci* at pci? # XHCI usb* at usbus? include "dev/usb/usbdevices.config" midi* at midibus? pseudo-device sequencer # MIDI sequencer +# NVMe +nvme* at pci? dev ? function ? +ld* at nvme? nsid ? # Virtio devices virtio* at fdt? # Virtio MMIO device Index: src/sys/arch/riscv/conf/GENERIC64 diff -u src/sys/arch/riscv/conf/GENERIC64:1.18 src/sys/arch/riscv/conf/GENERIC64:1.19 --- src/sys/arch/riscv/conf/GENERIC64:1.18 Tue Nov 12 07:30:12 2024 +++ src/sys/arch/riscv/conf/GENERIC64 Wed Jan 1 17:53:07 2025 @@ -1,4 +1,4 @@ -# $NetBSD: GENERIC64,v 1.18 2024/11/12 07:30:12 skrll Exp $ +# $NetBSD: GENERIC64,v 1.19 2025/01/01 17:53:07 skrll Exp $ # # GENERIC machine description file # @@ -66,6 +66,9 @@ sun6idma* at fdt? pass 4 # Allwinner D # PCIe PHY jh7110pciephy* at fdt? pass 3 # StarFive JH7110 PCIe PHY +# PCIe +jh7110pcie* at fdt? # StarFive JH7110 PCIe + # Pin control jh7100pinctrl* at fdt? pass 2 # StarFive JH7100 pinctrl driver jh7110pinctrl* at fdt? pass 2 # StarFive JH7110 pinctrl driver Index: src/sys/arch/riscv/conf/files.riscv diff -u src/sys/arch/riscv/conf/files.riscv:1.16 src/sys/arch/riscv/conf/files.riscv:1.17 --- src/sys/arch/riscv/conf/files.riscv:1.16 Sat Nov 23 12:03:55 2024 +++ src/sys/arch/riscv/conf/files.riscv Wed Jan 1 17:53:07 2025 @@ -1,4 +1,4 @@ -# $NetBSD: files.riscv,v 1.16 2024/11/23 12:03:55 skrll Exp $ +# $NetBSD: files.riscv,v 1.17 2025/01/01 17:53:07 skrll Exp $ # maxpartitions 16 @@ -73,7 +73,9 @@ file arch/riscv/dev/plic.c plic attach plic at fdt with plic_fdt file arch/riscv/dev/plic_fdt.c plic & fdt +# # Binary compatibility with 32bit NetBSD (COMPAT_NETBSD32) +# file arch/riscv/riscv/core32_machdep.c compat_netbsd32 & coredump file arch/riscv/riscv/netbsd32_machdep.c compat_netbsd32 file arch/riscv/riscv/sig32_machdep.c compat_netbsd32 @@ -81,7 +83,9 @@ include "compat/netbsd32/files.netbsd32" include "arch/riscv/fdt/files.fdt" +# # Machine-independent drivers +# include "dev/ata/files.ata" # ATA drivers include "dev/bluetooth/files.bluetooth" # Bluetooth devices include "dev/i2o/files.i2o" # I2O drivers. @@ -90,4 +94,7 @@ include "dev/scsipi/files.scsipi" include "dev/usb/files.usb" # USB device support include "dev/pci/files.pci" # PCI device support +# +# Machine-dependent drivers +# include "arch/riscv/conf/majors.riscv" Index: src/sys/arch/riscv/conf/std.riscv diff -u src/sys/arch/riscv/conf/std.riscv:1.2 src/sys/arch/riscv/conf/std.riscv:1.3 --- src/sys/arch/riscv/conf/std.riscv:1.2 Tue Aug 13 07:20:23 2024 +++ src/sys/arch/riscv/conf/std.riscv Wed Jan 1 17:53:07 2025 @@ -1,5 +1,5 @@ # -# $NetBSD: std.riscv,v 1.2 2024/08/13 07:20:23 skrll Exp $ +# $NetBSD: std.riscv,v 1.3 2025/01/01 17:53:07 skrll Exp $ # machine riscv @@ -8,6 +8,9 @@ include "conf/std" options EXEC_ELF32 options EXEC_SCRIPT +options PCI_NETBSD_CONFIGURE +options __HAVE_PCI_CONF_HOOK + options SOC_SUN20I_D1 #no defflag COMPAT_09 Index: src/sys/arch/riscv/conf/std.riscv64 diff -u src/sys/arch/riscv/conf/std.riscv64:1.4 src/sys/arch/riscv/conf/std.riscv64:1.5 --- src/sys/arch/riscv/conf/std.riscv64:1.4 Tue Aug 13 07:20:23 2024 +++ src/sys/arch/riscv/conf/std.riscv64 Wed Jan 1 17:53:07 2025 @@ -1,5 +1,5 @@ # -# $NetBSD: std.riscv64,v 1.4 2024/08/13 07:20:23 skrll Exp $ +# $NetBSD: std.riscv64,v 1.5 2025/01/01 17:53:07 skrll Exp $ # machine riscv @@ -8,6 +8,9 @@ include "conf/std" options EXEC_SCRIPT options EXEC_ELF64 +options PCI_NETBSD_CONFIGURE +options __HAVE_PCI_CONF_HOOK + options SOC_SUN20I_D1 makeoptions LP64="yes" Index: src/sys/arch/riscv/fdt/files.fdt diff -u src/sys/arch/riscv/fdt/files.fdt:1.3 src/sys/arch/riscv/fdt/files.fdt:1.4 --- src/sys/arch/riscv/fdt/files.fdt:1.3 Mon Jun 12 18:59:57 2023 +++ src/sys/arch/riscv/fdt/files.fdt Wed Jan 1 17:53:07 2025 @@ -1,4 +1,4 @@ -# $NetBSD: files.fdt,v 1.3 2023/06/12 18:59:57 skrll Exp $ +# $NetBSD: files.fdt,v 1.4 2025/01/01 17:53:07 skrll Exp $ include "dev/pckbport/files.pckbport" @@ -16,3 +16,8 @@ file arch/riscv/fdt/clint_fdt.c clint_f device intc: fdt attach intc at fdt with intc_fdt file arch/riscv/fdt/intc_fdt.c intc_fdt + +# Generic PCI host controller +device pcihost: pcibus +attach pcihost at fdt with pcihost_fdt +file arch/riscv/fdt/pcihost_fdt.c pcihost_fdt Index: src/sys/arch/riscv/starfive/files.starfive diff -u src/sys/arch/riscv/starfive/files.starfive:1.8 src/sys/arch/riscv/starfive/files.starfive:1.9 --- src/sys/arch/riscv/starfive/files.starfive:1.8 Mon Nov 11 20:30:08 2024 +++ src/sys/arch/riscv/starfive/files.starfive Wed Jan 1 17:53:08 2025 @@ -1,4 +1,4 @@ -# $NetBSD: files.starfive,v 1.8 2024/11/11 20:30:08 skrll Exp $ +# $NetBSD: files.starfive,v 1.9 2025/01/01 17:53:08 skrll Exp $ # # Configuration info for StarFive SoCs # @@ -45,6 +45,11 @@ device jh7110pciephy attach jh7110pciephy at fdt with jh7110_pciephy file arch/riscv/starfive/jh7110_pciephy.c jh7110_pciephy +# JH7110 PCIe +device jh7110pcie: pcibus, pcihost_fdt +attach jh7110pcie at fdt with jh7110_pcie +file arch/riscv/starfive/jh7110_pcie.c jh7110_pcie + # JH7110 system control device jh7110syscon attach jh7110syscon at fdt with jh7110_syscon Added files: Index: src/sys/arch/riscv/fdt/pcihost_fdt.c diff -u /dev/null src/sys/arch/riscv/fdt/pcihost_fdt.c:1.1 --- /dev/null Wed Jan 1 17:53:08 2025 +++ src/sys/arch/riscv/fdt/pcihost_fdt.c Wed Jan 1 17:53:07 2025 @@ -0,0 +1,633 @@ +/* $NetBSD: pcihost_fdt.c,v 1.1 2025/01/01 17:53:07 skrll Exp $ */ + +/*- + * Copyright (c) 2018 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: pcihost_fdt.c,v 1.1 2025/01/01 17:53:07 skrll Exp $"); + +#include <sys/param.h> + +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/intr.h> +#include <sys/kernel.h> +#include <sys/kmem.h> +#include <sys/lwp.h> +#include <sys/mutex.h> +#include <sys/queue.h> +#include <sys/systm.h> + +#include <machine/cpu.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pciconf.h> + +#include <dev/fdt/fdtvar.h> + +#include <riscv/fdt/pcihost_fdtvar.h> + +#define PCIHOST_DEFAULT_BUS_MIN 0 +#define PCIHOST_DEFAULT_BUS_MAX 255 + +#define PCIHOST_CACHELINE_SIZE 64 /* riscv_dcache_align */ + +int pcihost_segment = 0; + +static int pcihost_match(device_t, cfdata_t, void *); +static void pcihost_attach(device_t, device_t, void *); + +static int pcihost_config(struct pcihost_softc *); + +static void pcihost_attach_hook(device_t, device_t, + struct pcibus_attach_args *); +static int pcihost_bus_maxdevs(void *, int); +static pcitag_t pcihost_make_tag(void *, int, int, int); +static void pcihost_decompose_tag(void *, pcitag_t, int *, int *, int *); +static u_int pcihost_get_segment(void *); +static pcireg_t pcihost_conf_read(void *, pcitag_t, int); +static void pcihost_conf_write(void *, pcitag_t, int, pcireg_t); +static int pcihost_conf_hook(void *, int, int, int, pcireg_t); +static void pcihost_conf_interrupt(void *, int, int, int, int, int *); + +static int pcihost_intr_map(const struct pci_attach_args *, + pci_intr_handle_t *); +static const char *pcihost_intr_string(void *, pci_intr_handle_t, + char *, size_t); +static const struct evcnt *pcihost_intr_evcnt(void *, pci_intr_handle_t); +static int pcihost_intr_setattr(void *, pci_intr_handle_t *, int, + uint64_t); +static void * pcihost_intr_establish(void *, pci_intr_handle_t, + int, int (*)(void *), void *, + const char *); +static void pcihost_intr_disestablish(void *, void *); + +static int pcihost_bus_space_map(void *, bus_addr_t, bus_size_t, + int, bus_space_handle_t *); + +CFATTACH_DECL_NEW(pcihost_fdt, sizeof(struct pcihost_softc), + pcihost_match, pcihost_attach, NULL, NULL); + +static const struct device_compatible_entry compat_data[] = { + { .compat = "pci-host-cam-generic", .value = PCIHOST_CAM }, + { .compat = "pci-host-ecam-generic", .value = PCIHOST_ECAM }, + DEVICE_COMPAT_EOL +}; + +static int +pcihost_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_compatible_match(faa->faa_phandle, compat_data); +} + +static void +pcihost_attach(device_t parent, device_t self, void *aux) +{ + struct pcihost_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + bus_addr_t cs_addr; + bus_size_t cs_size; + int error; + + if (fdtbus_get_reg(faa->faa_phandle, 0, &cs_addr, &cs_size) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + + sc->sc_dev = self; + sc->sc_dmat = faa->faa_dmat; + sc->sc_bst = faa->faa_bst; + sc->sc_pci_bst = faa->faa_bst; + sc->sc_phandle = faa->faa_phandle; + error = bus_space_map(sc->sc_bst, cs_addr, cs_size, + 0, &sc->sc_bsh); + if (error) { + aprint_error(": couldn't map registers: %d\n", error); + return; + } + sc->sc_type = of_compatible_lookup(sc->sc_phandle, compat_data)->value; + + aprint_naive("\n"); + aprint_normal(": Generic PCI host controller\n"); + + pcihost_init(&sc->sc_pc, sc); + pcihost_init2(sc); +} + +void +pcihost_init2(struct pcihost_softc *sc) +{ + struct pcibus_attach_args pba; + const u_int *data; + int len; + + if ((data = fdtbus_get_prop(sc->sc_phandle, "bus-range", &len)) != NULL) { + if (len != 8) { + aprint_error_dev(sc->sc_dev, "malformed 'bus-range' property\n"); + return; + } + sc->sc_bus_min = be32toh(data[0]); + sc->sc_bus_max = be32toh(data[1]); + } else { + sc->sc_bus_min = PCIHOST_DEFAULT_BUS_MIN; + sc->sc_bus_max = PCIHOST_DEFAULT_BUS_MAX; + } + + /* + * Assign a fixed PCI segment ("domain") number. If the property is not + * present, assign one. The binding spec says if this property is used to + * assign static segment numbers, all host bridges should have segments + * astatic assigned to prevent overlaps. + */ + if (of_getprop_uint32(sc->sc_phandle, "linux,pci-domain", &sc->sc_seg)) + sc->sc_seg = pcihost_segment++; + + if (pcihost_config(sc) != 0) + return; + + memset(&pba, 0, sizeof(pba)); + pba.pba_flags = PCI_FLAGS_MRL_OKAY | + PCI_FLAGS_MRM_OKAY | + PCI_FLAGS_MWI_OKAY | + sc->sc_pci_flags; + pba.pba_iot = &sc->sc_io.bst; + pba.pba_memt = &sc->sc_mem.bst; + pba.pba_dmat = sc->sc_dmat; +#ifdef _PCI_HAVE_DMA64 + pba.pba_dmat64 = sc->sc_dmat; +#endif + pba.pba_pc = &sc->sc_pc; + pba.pba_bus = sc->sc_bus_min; + + config_found(sc->sc_dev, &pba, pcibusprint, + CFARGS(.devhandle = device_handle(sc->sc_dev))); +} + +void +pcihost_init(pci_chipset_tag_t pc, void *priv) +{ + pc->pc_conf_v = priv; + pc->pc_attach_hook = pcihost_attach_hook; + pc->pc_bus_maxdevs = pcihost_bus_maxdevs; + pc->pc_make_tag = pcihost_make_tag; + pc->pc_decompose_tag = pcihost_decompose_tag; + pc->pc_get_segment = pcihost_get_segment; + pc->pc_conf_read = pcihost_conf_read; + pc->pc_conf_write = pcihost_conf_write; + pc->pc_conf_hook = pcihost_conf_hook; + pc->pc_conf_interrupt = pcihost_conf_interrupt; + + pc->pc_intr_v = priv; + pc->pc_intr_map = pcihost_intr_map; + pc->pc_intr_string = pcihost_intr_string; + pc->pc_intr_evcnt = pcihost_intr_evcnt; + pc->pc_intr_setattr = pcihost_intr_setattr; + pc->pc_intr_establish = pcihost_intr_establish; + pc->pc_intr_disestablish = pcihost_intr_disestablish; +} + +static int +pcihost_config(struct pcihost_softc *sc) +{ + const u_int *ranges; + u_int probe_only; + int error, len, type; + bool swap; + + struct pcih_bus_space * const pibs = &sc->sc_io; + pibs->bst = *sc->sc_pci_bst; + pibs->bst.bs_cookie = pibs; + pibs->map = pibs->bst.bs_map; + pibs->flags = PCI_FLAGS_IO_OKAY; + pibs->bst.bs_map = pcihost_bus_space_map; + + struct pcih_bus_space * const pmbs = &sc->sc_mem; + pmbs->bst = *sc->sc_pci_bst; + pmbs->bst.bs_cookie = pmbs; + pmbs->map = pmbs->bst.bs_map; + pmbs->flags = PCI_FLAGS_MEM_OKAY; + pmbs->bst.bs_map = pcihost_bus_space_map; + + /* + * If this flag is set, skip configuration of the PCI bus and use + * existing config. + */ + const int chosen = OF_finddevice("/chosen"); + if (chosen <= 0 || of_getprop_uint32(chosen, "linux,pci-probe-only", &probe_only)) + probe_only = 0; + + if (sc->sc_pci_ranges != NULL) { + ranges = sc->sc_pci_ranges; + len = sc->sc_pci_ranges_cells * 4; + swap = false; + } else { + ranges = fdtbus_get_prop(sc->sc_phandle, "ranges", &len); + if (ranges == NULL) { + aprint_error_dev(sc->sc_dev, "missing 'ranges' property\n"); + return EINVAL; + } + swap = true; + } + struct pciconf_resources *pcires = pciconf_resource_init(); + + /* + * Each entry in the ranges table contains: + * - bus address (3 cells) + * - cpu physical address (2 cells) + * - size (2 cells) + * Total size for each entry is 28 bytes (7 cells). + */ + while (len >= 28) { +#define DECODE32(x,o) (swap ? be32dec(&(x)[o]) : (x)[o]) +#define DECODE64(x,o) (swap ? be64dec(&(x)[o]) : (((uint64_t)((x)[(o)+0]) << 32) + (x)[(o)+1])) + const uint32_t phys_hi = DECODE32(ranges, 0); + uint64_t bus_phys = DECODE64(ranges, 1); + const uint64_t cpu_phys = DECODE64(ranges, 3); + uint64_t size = DECODE64(ranges, 5); +#undef DECODE32 +#undef DECODE64 + + len -= 28; + ranges += 7; + + const bool is64 = (__SHIFTOUT(phys_hi, PHYS_HI_SPACE) == + PHYS_HI_SPACE_MEM64) ? true : false; + switch (__SHIFTOUT(phys_hi, PHYS_HI_SPACE)) { + case PHYS_HI_SPACE_IO: + if (pibs->nranges + 1 >= __arraycount(pibs->ranges)) { + aprint_error_dev(sc->sc_dev, "too many IO ranges\n"); + continue; + } + pibs->ranges[pibs->nranges].bpci = bus_phys; + pibs->ranges[pibs->nranges].bbus = cpu_phys; + pibs->ranges[pibs->nranges].size = size; + ++pibs->nranges; + aprint_verbose_dev(sc->sc_dev, + "IO: %#018" PRIx64 " + %#018" PRIx64 " @ %#018" PRIx64 "\n", + bus_phys, size, cpu_phys); + /* + * Reserve a PC-like legacy IO ports range, perhaps + * for access to VGA registers. + */ + if (bus_phys == 0 && size >= 0x10000) { + bus_phys += 0x1000; + size -= 0x1000; + } + error = pciconf_resource_add(pcires, + PCICONF_RESOURCE_IO, bus_phys, size); + if (error == 0) + sc->sc_pci_flags |= PCI_FLAGS_IO_OKAY; + break; + case PHYS_HI_SPACE_MEM64: + /* FALLTHROUGH */ + case PHYS_HI_SPACE_MEM32: + if (pmbs->nranges + 1 >= __arraycount(pmbs->ranges)) { + aprint_error_dev(sc->sc_dev, "too many mem ranges\n"); + continue; + } + /* both pmem and mem spaces are in the same tag */ + pmbs->ranges[pmbs->nranges].bpci = bus_phys; + pmbs->ranges[pmbs->nranges].bbus = cpu_phys; + pmbs->ranges[pmbs->nranges].size = size; + ++pmbs->nranges; + if ((phys_hi & PHYS_HI_PREFETCH) != 0 || + __SHIFTOUT(phys_hi, PHYS_HI_SPACE) == PHYS_HI_SPACE_MEM64) { + type = PCICONF_RESOURCE_PREFETCHABLE_MEM; + aprint_verbose_dev(sc->sc_dev, + "MMIO (%d-bit prefetchable) : %#018" PRIx64 " + %#018" PRIx64 " @ %#018" PRIx64 "\n", + is64 ? 64 : 32, bus_phys, size, cpu_phys); + } else { + type = PCICONF_RESOURCE_MEM; + aprint_verbose_dev(sc->sc_dev, + "MMIO (%d-bit non-prefetchable): %#018" PRIx64 " + %#018" PRIx64 " @ %#018" PRIx64 "\n", + is64 ? 64 : 32, bus_phys, size, cpu_phys); + } + error = pciconf_resource_add(pcires, type, bus_phys, + size); + if (error == 0) + sc->sc_pci_flags |= PCI_FLAGS_MEM_OKAY; + break; + default: + break; + } + } + + if (probe_only) { + error = 0; + } else { + error = pci_configure_bus(&sc->sc_pc, pcires, sc->sc_bus_min, + PCIHOST_CACHELINE_SIZE); + } + + pciconf_resource_fini(pcires); + if (error) { + aprint_error_dev(sc->sc_dev, "configuration failed: %d\n", error); + return error; + } + + return 0; +} + +static void +pcihost_attach_hook(device_t parent, device_t self, + struct pcibus_attach_args *pba) +{ +} + +static int +pcihost_bus_maxdevs(void *v, int busno) +{ + return 32; +} + +static pcitag_t +pcihost_make_tag(void *v, int b, int d, int f) +{ + return (b << 16) | (d << 11) | (f << 8); +} + +static void +pcihost_decompose_tag(void *v, pcitag_t tag, int *bp, int *dp, int *fp) +{ + if (bp) + *bp = (tag >> 16) & 0xff; + if (dp) + *dp = (tag >> 11) & 0x1f; + if (fp) + *fp = (tag >> 8) & 0x7; +} + +static u_int +pcihost_get_segment(void *v) +{ + struct pcihost_softc *sc = v; + + return sc->sc_seg; +} + +static pcireg_t +pcihost_conf_read(void *v, pcitag_t tag, int offset) +{ + struct pcihost_softc *sc = v; + int b, d, f; + u_int reg; + + pcihost_decompose_tag(v, tag, &b, &d, &f); + + if (b < sc->sc_bus_min || b > sc->sc_bus_max) + return (pcireg_t) -1; + + if (sc->sc_type == PCIHOST_CAM) { + if (offset & ~0xff) + return (pcireg_t) -1; + reg = (b << 16) | (d << 11) | (f << 8) | offset; + } else if (sc->sc_type == PCIHOST_ECAM) { + if (offset & ~0xfff) + return (pcireg_t) -1; + reg = (b << 20) | (d << 15) | (f << 12) | offset; + } else { + return (pcireg_t) -1; + } + + return bus_space_read_4(sc->sc_bst, sc->sc_bsh, reg); +} + +static void +pcihost_conf_write(void *v, pcitag_t tag, int offset, pcireg_t val) +{ + struct pcihost_softc *sc = v; + int b, d, f; + u_int reg; + + pcihost_decompose_tag(v, tag, &b, &d, &f); + + if (b < sc->sc_bus_min || b > sc->sc_bus_max) + return; + + if (sc->sc_type == PCIHOST_CAM) { + if (offset & ~0xff) + return; + reg = (b << 16) | (d << 11) | (f << 8) | offset; + } else if (sc->sc_type == PCIHOST_ECAM) { + if (offset & ~0xfff) + return; + reg = (b << 20) | (d << 15) | (f << 12) | offset; + } else { + return; + } + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, reg, val); +} + +static int +pcihost_conf_hook(void *v, int b, int d, int f, pcireg_t id) +{ + return PCI_CONF_DEFAULT; +} + +static void +pcihost_conf_interrupt(void *v, int bus, int dev, int ipin, int swiz, int *ilinep) +{ +} + +static int +pcihost_intr_map(const struct pci_attach_args *pa, pci_intr_handle_t *ih) +{ + struct pcihost_softc *sc = pa->pa_pc->pc_intr_v; + u_int addr_cells, interrupt_cells; + const u_int *imap, *imask; + int imaplen, imasklen; + u_int match[4]; + int index; + + if (pa->pa_intrpin == 0) + return EINVAL; + + imap = fdtbus_get_prop(sc->sc_phandle, "interrupt-map", &imaplen); + imask = fdtbus_get_prop(sc->sc_phandle, "interrupt-map-mask", &imasklen); + if (imap == NULL || imask == NULL || imasklen != 16) + return EINVAL; + + /* Convert attach args to specifier */ + match[0] = htobe32( + __SHIFTIN(pa->pa_bus, PHYS_HI_BUS) | + __SHIFTIN(pa->pa_device, PHYS_HI_DEVICE) | + __SHIFTIN(pa->pa_function, PHYS_HI_FUNCTION) + ) & imask[0]; + match[1] = htobe32(0) & imask[1]; + match[2] = htobe32(0) & imask[2]; + match[3] = htobe32(pa->pa_intrpin) & imask[3]; + + index = 0; + while (imaplen >= 20) { + const int map_ihandle = fdtbus_get_phandle_from_native(be32toh(imap[4])); + if (of_getprop_uint32(map_ihandle, "#address-cells", &addr_cells)) + addr_cells = 2; + if (of_getprop_uint32(map_ihandle, "#interrupt-cells", &interrupt_cells)) + interrupt_cells = 0; + if (imaplen < (addr_cells + interrupt_cells) * 4) + return ENXIO; + + if ((imap[0] & imask[0]) == match[0] && + (imap[1] & imask[1]) == match[1] && + (imap[2] & imask[2]) == match[2] && + (imap[3] & imask[3]) == match[3]) { + *ih = index; + return 0; + } + + imap += (5 + addr_cells + interrupt_cells); + imaplen -= (5 + addr_cells + interrupt_cells) * 4; + index++; + } + + return EINVAL; +} + +static const u_int * +pcihost_find_intr(struct pcihost_softc *sc, pci_intr_handle_t ih, int *pihandle) +{ + u_int addr_cells, interrupt_cells; + int imaplen, index; + const u_int *imap; + + imap = fdtbus_get_prop(sc->sc_phandle, "interrupt-map", &imaplen); + KASSERT(imap != NULL); + + index = 0; + while (imaplen >= 20) { + const int map_ihandle = fdtbus_get_phandle_from_native(be32toh(imap[4])); + if (of_getprop_uint32(map_ihandle, "#address-cells", &addr_cells)) + addr_cells = 2; + if (of_getprop_uint32(map_ihandle, "#interrupt-cells", &interrupt_cells)) + interrupt_cells = 0; + if (imaplen < (addr_cells + interrupt_cells) * 4) + return NULL; + + if (index == ih) { + *pihandle = map_ihandle; + return imap + 5 + addr_cells; + } + + imap += (5 + addr_cells + interrupt_cells); + imaplen -= (5 + addr_cells + interrupt_cells) * 4; + index++; + } + + return NULL; +} + +static const char * +pcihost_intr_string(void *v, pci_intr_handle_t ih, char *buf, size_t len) +{ + const int irq = __SHIFTOUT(ih, RISCV_PCI_INTR_IRQ); + const int vec = __SHIFTOUT(ih, RISCV_PCI_INTR_MSI_VEC); + struct pcihost_softc *sc = v; + const u_int *specifier; + int ihandle; + + if (ih & RISCV_PCI_INTR_MSIX) { + snprintf(buf, len, "irq %d (MSI-X vec %d)", irq, vec); + } else if (ih & RISCV_PCI_INTR_MSI) { + snprintf(buf, len, "irq %d (MSI vec %d)", irq, vec); + } else { + specifier = pcihost_find_intr(sc, ih & RISCV_PCI_INTR_IRQ, &ihandle); + if (specifier == NULL) + return NULL; + + if (!fdtbus_intr_str_raw(ihandle, specifier, buf, len)) + return NULL; + } + + return buf; +} + +const struct evcnt * +pcihost_intr_evcnt(void *v, pci_intr_handle_t ih) +{ + return NULL; +} + +static int +pcihost_intr_setattr(void *v, pci_intr_handle_t *ih, int attr, uint64_t data) +{ + switch (attr) { + case PCI_INTR_MPSAFE: + if (data) + *ih |= RISCV_PCI_INTR_MPSAFE; + else + *ih &= ~RISCV_PCI_INTR_MPSAFE; + return 0; + default: + return ENODEV; + } +} + +static void * +pcihost_intr_establish(void *v, pci_intr_handle_t pih, int ipl, + int (*callback)(void *), void *arg, const char *xname) +{ + struct pcihost_softc *sc = v; + const int flags = (pih & RISCV_PCI_INTR_MPSAFE) ? FDT_INTR_MPSAFE : 0; + const u_int *specifier; + int ihandle; + + specifier = pcihost_find_intr(sc, pih & RISCV_PCI_INTR_IRQ, &ihandle); + + if (specifier == NULL) + return NULL; + + return fdtbus_intr_establish_raw(ihandle, specifier, ipl, flags, + callback, arg, xname); +} + +static void +pcihost_intr_disestablish(void *v, void *vih) +{ + struct pcihost_softc *sc = v; + + fdtbus_intr_disestablish(sc->sc_phandle, vih); +} + +static int +pcihost_bus_space_map(void *t, bus_addr_t bpa, bus_size_t size, int flag, + bus_space_handle_t *bshp) +{ + struct pcih_bus_space * const pbs = t; + + for (size_t i = 0; i < pbs->nranges; i++) { + const bus_addr_t rmin = pbs->ranges[i].bpci; + const bus_addr_t rmax = pbs->ranges[i].bpci - 1 + pbs->ranges[i].size; + if ((bpa >= rmin) && ((bpa - 1 + size) <= rmax)) { + return pbs->map(t, bpa - pbs->ranges[i].bpci + pbs->ranges[i].bbus, size, flag, bshp); + } + } + + return ERANGE; +} Index: src/sys/arch/riscv/fdt/pcihost_fdtvar.h diff -u /dev/null src/sys/arch/riscv/fdt/pcihost_fdtvar.h:1.1 --- /dev/null Wed Jan 1 17:53:08 2025 +++ src/sys/arch/riscv/fdt/pcihost_fdtvar.h Wed Jan 1 17:53:07 2025 @@ -0,0 +1,100 @@ +/* $NetBSD: pcihost_fdtvar.h,v 1.1 2025/01/01 17:53:07 skrll Exp $ */ + +/*- + * Copyright (c) 2018 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Physical address format bit definitions */ +#define PHYS_HI_RELO __BIT(31) +#define PHYS_HI_PREFETCH __BIT(30) +#define PHYS_HI_ALIASED __BIT(29) +#define PHYS_HI_SPACE __BITS(25,24) +#define PHYS_HI_SPACE_CFG 0 +#define PHYS_HI_SPACE_IO 1 +#define PHYS_HI_SPACE_MEM32 2 +#define PHYS_HI_SPACE_MEM64 3 +#define PHYS_HI_BUS __BITS(23,16) +#define PHYS_HI_DEVICE __BITS(15,11) +#define PHYS_HI_FUNCTION __BITS(10,8) +#define PHYS_HI_REGISTER __BITS(7,0) + +extern int pcihost_segment; + +enum pcihost_type { + PCIHOST_CAM = 1, + PCIHOST_ECAM, +}; + +struct pcihost_msi_handlers; + +struct pcih_bus_space { + struct bus_space bst; + + int (*map)(void *, bus_addr_t, bus_size_t, + int, bus_space_handle_t *); + int flags; + + struct space_range { + bus_addr_t bpci; + bus_addr_t bbus; + bus_size_t size; + } ranges[4]; + size_t nranges; +}; + +struct pcihost_softc { + device_t sc_dev; + bus_dma_tag_t sc_dmat; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + bus_space_tag_t sc_pci_bst; + int sc_phandle; + + enum pcihost_type sc_type; + + u_int sc_seg; + u_int sc_bus_min; + u_int sc_bus_max; + + struct riscv_pci_chipset + sc_pc; + + struct pcih_bus_space sc_io; + struct pcih_bus_space sc_mem; + + int sc_pci_flags; + + const u_int *sc_pci_ranges; + u_int sc_pci_ranges_cells; + +#ifdef __HAVE_PCI_MSI_MSIX + kmutex_t sc_msi_handlers_mutex; + LIST_HEAD(, pcihost_msi_handler) + sc_msi_handlers; +#endif +}; + +void pcihost_init2(struct pcihost_softc *); +void pcihost_init(pci_chipset_tag_t, void *); Index: src/sys/arch/riscv/include/pci_machdep.h diff -u /dev/null src/sys/arch/riscv/include/pci_machdep.h:1.1 --- /dev/null Wed Jan 1 17:53:08 2025 +++ src/sys/arch/riscv/include/pci_machdep.h Wed Jan 1 17:53:08 2025 @@ -0,0 +1,243 @@ +/* $NetBSD: pci_machdep.h,v 1.1 2025/01/01 17:53:08 skrll Exp $ */ + +/*- + * Copyright (c) 2023 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Nick Hudson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1996 Carnegie-Mellon University. + * All rights reserved. + * + * Author: Chris G. Demetriou + * + * Permission to use, copy, modify and distribute this software and + * its documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or software.distribut...@cs.cmu.edu + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#ifndef _RISCV_PCI_MACHDEP_H_ +#define _RISCV_PCI_MACHDEP_H_ + +/* + * Machine-specific definitions for PCI autoconfiguration. + */ + +#define __HAVE_PCI_GET_SEGMENT + +#ifdef _LP64 +#define _PCI_HAVE_DMA64 +#endif + +#include <sys/errno.h> + +/* + * Types provided to machine-independent PCI code + */ +typedef struct riscv_pci_chipset *pci_chipset_tag_t; +typedef u_long pcitag_t; +typedef uint64_t pci_intr_handle_t; + +/* + * pci_intr_handle_t fields + */ +#define RISCV_PCI_INTR_MSI_VEC __BITS(42, 32) +#define RISCV_PCI_INTR_MPSAFE __BIT(31) +#define RISCV_PCI_INTR_MSIX __BIT(30) +#define RISCV_PCI_INTR_MSI __BIT(29) +#define RISCV_PCI_INTR_FRAME __BITS(23, 16) +#define RISCV_PCI_INTR_IRQ __BITS(15, 0) + +#ifdef __HAVE_PCI_MSI_MSIX +/* + * PCI MSI/MSI-X support + */ +typedef enum { + PCI_INTR_TYPE_INTX = 0, + PCI_INTR_TYPE_MSI, + PCI_INTR_TYPE_MSIX, + PCI_INTR_TYPE_SIZE, +} pci_intr_type_t; +#endif /* __HAVE_PCI_MSI_MSIX */ + +/* + * Forward declarations. + */ +struct pci_attach_args; + +/* + * riscv-specific PCI structure and type definitions. + * NOT TO BE USED DIRECTLY BY MACHINE INDEPENDENT CODE. + */ +struct riscv_pci_chipset { + void *pc_conf_v; + void (*pc_attach_hook)(device_t, device_t, + struct pcibus_attach_args *); + int (*pc_bus_maxdevs)(void *, int); + pcitag_t (*pc_make_tag)(void *, int, int, int); + void (*pc_decompose_tag)(void *, pcitag_t, int *, + int *, int *); + u_int (*pc_get_segment)(void *); +#if 0 + // XXXNH devid? + uint32_t (*pc_get_devid)(void *, uint32_t); +#endif + uint32_t (*pc_get_frameid)(void *, uint32_t); + pcireg_t (*pc_conf_read)(void *, pcitag_t, int); + void (*pc_conf_write)(void *, pcitag_t, int, pcireg_t); + + void *pc_intr_v; + int (*pc_intr_map)(const struct pci_attach_args *, + pci_intr_handle_t *); + const char *(*pc_intr_string)(void *, pci_intr_handle_t, + char *, size_t); + const struct evcnt *(*pc_intr_evcnt)(void *, pci_intr_handle_t); + int (*pc_intr_setattr)(void *, pci_intr_handle_t *, + int, uint64_t); + void *(*pc_intr_establish)(void *, pci_intr_handle_t, + int, int (*)(void *), void *, const char *); + void (*pc_intr_disestablish)(void *, void *); + +#ifdef __HAVE_PCI_CONF_HOOK + int (*pc_conf_hook)(void *, int, int, int, pcireg_t); +#endif + void (*pc_conf_interrupt)(void *, int, int, int, int, int *); + +#ifdef __HAVE_PCI_MSI_MSIX + void *pc_msi_v; + pci_intr_type_t (*pc_intr_type)(void *, pci_intr_handle_t); + int (*pc_intr_alloc)(const struct pci_attach_args *, + pci_intr_handle_t **, int *, pci_intr_type_t); + void (*pc_intr_release)(void *, pci_intr_handle_t *, int); + int (*pc_intx_alloc)(const struct pci_attach_args *, + pci_intr_handle_t **); + int (*pc_msi_alloc)(const struct pci_attach_args *, + pci_intr_handle_t **, int *); + int (*pc_msi_alloc_exact)(const struct pci_attach_args *, + pci_intr_handle_t **, int); + int (*pc_msix_alloc)(const struct pci_attach_args *, + pci_intr_handle_t **, int *); + int (*pc_msix_alloc_exact)(const struct pci_attach_args *, + pci_intr_handle_t **, int); + int (*pc_msix_alloc_map)(const struct pci_attach_args *, + pci_intr_handle_t **, u_int *, int); +#endif + + uint32_t pc_cfg_cmd; +}; + +/* + * Functions provided to machine-independent PCI code. + */ +//XXXNH static inlines.. +#define pci_attach_hook(p, s, pba) \ + (*(pba)->pba_pc->pc_attach_hook)((p), (s), (pba)) +#define pci_bus_maxdevs(c, b) \ + (*(c)->pc_bus_maxdevs)((c)->pc_conf_v, (b)) +#define pci_make_tag(c, b, d, f) \ + (*(c)->pc_make_tag)((c)->pc_conf_v, (b), (d), (f)) +#define pci_decompose_tag(c, t, bp, dp, fp) \ + (*(c)->pc_decompose_tag)((c)->pc_conf_v, (t), (bp), (dp), (fp)) +#define pci_get_segment(c) \ + ((c)->pc_get_segment ? (*(c)->pc_get_segment)((c)->pc_conf_v) : 0) +#define pci_get_devid(c, d) \ + ((c)->pc_get_devid ? (*(c)->pc_get_devid)((c)->pc_conf_v, (d)) : (d)) +#define pci_get_frameid(c, d) \ + ((c)->pc_get_frameid ? (*(c)->pc_get_frameid)((c)->pc_conf_v, (d)) : 0) +#define pci_conf_read(c, t, r) \ + (*(c)->pc_conf_read)((c)->pc_conf_v, (t), (r)) +#define pci_conf_write(c, t, r, v) \ + (*(c)->pc_conf_write)((c)->pc_conf_v, (t), (r), (v)) +#define pci_intr_map(pa, ihp) \ + (*(pa)->pa_pc->pc_intr_map)((pa), (ihp)) +#define pci_intr_string(c, ih, buf, len) \ + (*(c)->pc_intr_string)((c)->pc_intr_v, (ih), (buf), (len)) +#define pci_intr_evcnt(c, ih) \ + (*(c)->pc_intr_evcnt)((c)->pc_intr_v, (ih)) +#define pci_intr_establish(c, ih, l, h, a) \ + (*(c)->pc_intr_establish)((c)->pc_intr_v, (ih), (l), (h), (a), NULL) +#define pci_intr_disestablish(c, iv) \ + (*(c)->pc_intr_disestablish)((c)->pc_intr_v, (iv)) +#ifdef __HAVE_PCI_CONF_HOOK +#define pci_conf_hook(c, b, d, f, id) \ + (*(c)->pc_conf_hook)((c)->pc_conf_v, (b), (d), (f), (id)) +#endif +#define pci_conf_interrupt(c, b, d, i, s, p) \ + (*(c)->pc_conf_interrupt)((c)->pc_conf_v, (b), (d), (i), (s), (p)) + +static inline int +pci_intr_setattr(pci_chipset_tag_t pc, pci_intr_handle_t *ihp, + int attr, uint64_t data) +{ + if (!pc->pc_intr_setattr) + return ENODEV; + return pc->pc_intr_setattr(pc, ihp, attr, data); +} + +static inline void * +pci_intr_establish_xname(pci_chipset_tag_t pc, pci_intr_handle_t ih, + int level, int (*fn)(void *), void *arg, const char *xname) +{ + return pc->pc_intr_establish(pc->pc_intr_v, ih, level, fn, arg, xname); +} + +#ifdef __HAVE_PCI_MSI_MSIX +pci_intr_type_t + pci_intr_type(pci_chipset_tag_t, pci_intr_handle_t); +int pci_intr_alloc(const struct pci_attach_args *, pci_intr_handle_t **, + int *, pci_intr_type_t); +void pci_intr_release(pci_chipset_tag_t, pci_intr_handle_t *, int); +int pci_intx_alloc(const struct pci_attach_args *, pci_intr_handle_t **); +int pci_msi_alloc(const struct pci_attach_args *, pci_intr_handle_t **, + int *); +int pci_msi_alloc_exact(const struct pci_attach_args *, + pci_intr_handle_t **, int); +int pci_msix_alloc(const struct pci_attach_args *, pci_intr_handle_t **, + int *); +int pci_msix_alloc_exact(const struct pci_attach_args *, + pci_intr_handle_t **, int); +int pci_msix_alloc_map(const struct pci_attach_args *, pci_intr_handle_t **, + u_int *, int); +#endif /* __HAVE_PCI_MSI_MSIX */ + +#endif /* _RISCV_PCI_MACHDEP_H_ */ Index: src/sys/arch/riscv/starfive/jh7110_pcie.c diff -u /dev/null src/sys/arch/riscv/starfive/jh7110_pcie.c:1.1 --- /dev/null Wed Jan 1 17:53:08 2025 +++ src/sys/arch/riscv/starfive/jh7110_pcie.c Wed Jan 1 17:53:08 2025 @@ -0,0 +1,714 @@ +/* $NetBSD: jh7110_pcie.c,v 1.1 2025/01/01 17:53:08 skrll Exp $ */ + +/*- + * Copyright (c) 2024 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Nick Hudson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: jh7110_pcie.c,v 1.1 2025/01/01 17:53:08 skrll Exp $"); + +#include <sys/param.h> + +#include <sys/bitops.h> +#include <sys/kmem.h> + +#include <dev/fdt/fdtvar.h> +#include <dev/fdt/syscon.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pciconf.h> + +#include <riscv/fdt/pcihost_fdtvar.h> + +struct jh7110_pcie_irq { + struct jh7110_pcie_softc * + jpi_sc; + void *jpi_arg; + int (*jpi_fn)(void *); + int jpi_mpsafe; +}; + +struct jh7110_pcie_softc { + bus_space_tag_t sc_bst; + + struct pcihost_softc sc_phsc; + + bus_space_handle_t sc_apb_bsh; + bus_addr_t sc_apb_addr; + bus_size_t sc_apb_size; + + bus_space_handle_t sc_cfg_bsh; + bus_addr_t sc_cfg_addr; + bus_size_t sc_cfg_size; + + // syscon + const struct syscon * sc_syscon; + bus_size_t sc_stg_base; + + struct fdtbus_gpio_pin *sc_perst_gpio; + + // # pins + struct jh7110_pcie_irq *sc_irq[PCI_INTERRUPT_PIN_MAX]; +}; + +#define RD4(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_apb_bsh, (reg)) +#define WR4(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_apb_bsh, (reg), (val)) + +#define SET4(sc, off, mask) \ + WR4((sc), (off), RD4((sc), (off)) | (mask)) +#define CLR4(sc, off, mask) \ + WR4((sc), (off), RD4((sc), (off)) & ~(mask)) +#define UPD4(sc, off, clr, set) \ + WR4((sc), (off), (RD4((sc), (off)) & ~(clr)) | (set)) + +#define JH7110_PCIE0_CFG_BASE 0x940000000UL +#define JH7110_PCIE1_CFG_BASE 0x9c0000000UL + +/* PLDA register definitions */ +#define PLDA_GEN_SETTINGS 0x80 +#define PLDA_GEN_RP_ENABLE 1 +#define PLDA_PCI_IDS 0x9c +#define PLDA_PCI_IDS_REVISION_MASK __BITS( 7, 0) +#define PLDA_PCI_IDS_CLASSCODE_MASK __BITS(31, 8) +#define PLDA_MISC 0xb4 +#define PLDA_MISC_PHYFUNC_DISABLE __BIT(15) +#define PLDA_WINROM 0xfc +#define PLDA_WINROM_PREF64SUPPORT __BIT(3) + +#define PLDA_IMASK_LOCAL 0x180 +#define PLDA_IMASK_INT_INTA __BIT(24) +#define PLDA_IMASK_INT_INTB __BIT(25) +#define PLDA_IMASK_INT_INTC __BIT(26) +#define PLDA_IMASK_INT_INTD __BIT(27) +#define PLDA_IMASK_INT_INTX __BITS(27, 24) +#define PLDA_IMASK_INT_MSI __BIT(28) +#define PLDA_ISTATUS_LOCAL 0x184 +#define PLDA_ISTATUS_INT_INTA __BIT(24) +#define PLDA_ISTATUS_INT_INTB __BIT(25) +#define PLDA_ISTATUS_INT_INTC __BIT(26) +#define PLDA_ISTATUS_INT_INTD __BIT(27) +#define PLDA_ISTATUS_INT_INTX __BITS(27, 24) +#define PLDA_ISTATUS_INT_MSI __BIT(28) +#define PLDA_IMASK_HOST 0x188 +#define PLDA_ISTATUS_HOST 0x18c +#define PLDA_IMSI_ADDR 0x190 +#define PLDA_ISTATUS_MSI 0x194 +#define PLDA_PMSG_SUPPORT_RX 0x3f0 +#define PLDA_PMSG_LTR_SUPPORT __BIT(2) + +/* PCIe Master table init defines */ +#define PLDA_ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600 +#define PLDA_ATR0_PCIE_ATR_SIZE 0x25 +#define PLDA_ATR0_PCIE_ATR_SIZE_SHIFT 1 +#define PLDA_ATR0_PCIE_WIN0_SRC_ADDR 0x604 +#define PLDA_ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608 +#define PLDA_ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60c +#define PLDA_ATR0_PCIE_WIN0_TRSL_PARAM 0x610 + +#define PLDA_ATR_AXI4_SLV0_SRC_ADDR_LO(n) (0x800 + (n) * 0x20) +#define PLDA_ATR_SIZE_SRC_MASK __BITS(31, 12) +#define PLDA_ATR_SIZE_MASK __BITS(6, 1) +#define PLDA_ATR_IMPL __BIT(0) +#define PLDA_ATR_AXI4_SLV0_SRC_ADDR_HI(n) (0x804 + (n) * 0x20) +#define PLDA_ATR_AXI4_SLV0_TRSL_ADDR_LO(n) (0x808 + (n) * 0x20) +#define PLDA_ATR_AXI4_SLV0_TRSL_ADDR_HI(n) (0x80c + (n) * 0x20) +#define PLDA_ATR_AXI4_SLV0_TRSL_PARAM(n) (0x810 + (n) * 0x20) +#define PLDA_TRSL_ID_PCIE_RX_TX 0 +#define PLDA_TRSL_ID_PCIE_CONFIG 1 + +#define PCIE_FUNC_NUM 4 + +/* system control */ +#define STG_SYSCON_PCIE0_BASE 0x0048 +#define STG_SYSCON_PCIE1_BASE 0x01f8 + +#define STG_SYSCON_AR_OFFSET 0x0078 +#define STG_SYSCON_AXI4_SLVL_AR_MASK __BITS(22, 8) +#define STG_SYSCON_AXI4_SLVL_PHY_AR_MASK __BITS(20,17) +#define STG_SYSCON_AXI4_SLVL_PHY_AR(x) \ + __SHIFTIN((x), STG_SYSCON_AXI4_SLVL_PHY_AR_MASK) + +#define STG_SYSCON_AW_OFFSET 0x007c +#define STG_SYSCON_CLKREQ __BIT(22) +#define STG_SYSCON_CKREF_SRC_MASK __BITS(19, 18) +#define STG_SYSCON_AXI4_SLVL_AW_MASK __BITS(14, 0) +#define STG_SYSCON_AXI4_SLVL_PHY_AW_MASK __BITS(12, 9) +#define STG_SYSCON_AXI4_SLVL_PHY_AW(x) \ + __SHIFTIN((x), STG_SYSCON_AXI4_SLVL_PHY_AW_MASK) + +#define STG_SYSCON_RP_NEP_OFFSET 0x00e8 +#define STG_SYSCON_K_RP_NEP __BIT(8) + +#define STG_SYSCON_LNKSTA_OFFSET 0x0170 +#define DATA_LINK_ACTIVE __BIT(5) + +#define ECAM_BUS_MASK __BITS(27, 20) +#define ECAM_DEV_MASK __BITS(19, 15) +#define ECAM_FUNC_MASK __BITS(14, 12) +#define ECAM_OFFSET_MASK __BITS(11, 0) + +static int +jh7110_pcie_bus_maxdevs(void *v, int bus) +{ + struct pcihost_softc * const phsc = v; + + if (bus >= phsc->sc_bus_min || bus <= phsc->sc_bus_max) + return 1; + return 0; +} + +static pcitag_t +jh7110_pcie_make_tag(void *v, int bus, int dev, int fn) +{ +// struct pcihost_softc * const phsc = v; + + /* Return ECAM address. */ + return + __SHIFTIN(bus, ECAM_BUS_MASK) | + __SHIFTIN(dev, ECAM_DEV_MASK) | + __SHIFTIN(fn, ECAM_FUNC_MASK) | + 0; +} + +static void +jh7110_pcie_decompose_tag(void *v, pcitag_t tag, + int *busp, int *devp, int *fnp) +{ +// struct pcihost_softc * const phsc = v; + + if (busp != NULL) + *busp = __SHIFTOUT(tag, ECAM_BUS_MASK); + if (devp != NULL) + *devp = __SHIFTOUT(tag, ECAM_DEV_MASK); + if (fnp != NULL) + *fnp = __SHIFTOUT(tag, ECAM_FUNC_MASK); +} + +static bool +jh7110_pcie_conf_ok(struct jh7110_pcie_softc *sc, + int bus, int dev, int fn, int offset) +{ + + /* Only one device on root port and the first subordinate port. */ + if (bus < 2 && dev < 1) + return true; + + return false; +} + +static pcireg_t +jh7110_pcie_conf_read(void *v, pcitag_t tag, int offset) +{ + struct pcihost_softc * const phsc = v; + struct jh7110_pcie_softc * const sc = + container_of(phsc, struct jh7110_pcie_softc, sc_phsc); + int bus, dev, fn; + + KASSERT(offset >= 0); + KASSERT(offset < PCI_EXTCONF_SIZE); + + jh7110_pcie_decompose_tag(phsc, tag, &bus, &dev, &fn); + + if (!jh7110_pcie_conf_ok(sc, bus, dev, fn, offset)) + return 0xffffffff; + + bus_size_t reg = + __SHIFTIN(bus, ECAM_BUS_MASK) | + __SHIFTIN(dev, ECAM_DEV_MASK) | + __SHIFTIN(fn, ECAM_FUNC_MASK) | + offset; + + return bus_space_read_4(sc->sc_bst, sc->sc_cfg_bsh, reg); +} + +static void +jh7110_pcie_conf_write(void *v, pcitag_t tag, int offset, pcireg_t data) +{ + struct pcihost_softc * const phsc = v; + struct jh7110_pcie_softc * const sc = + container_of(phsc, struct jh7110_pcie_softc, sc_phsc); + int bus, dev, fn; + + KASSERT(offset >= 0); + KASSERT(offset < PCI_EXTCONF_SIZE); + + jh7110_pcie_decompose_tag(phsc, tag, &bus, &dev, &fn); + + if (!jh7110_pcie_conf_ok(sc, bus, dev, fn, offset)) + return; + + bus_size_t reg = + __SHIFTIN(bus, ECAM_BUS_MASK) | + __SHIFTIN(dev, ECAM_DEV_MASK) | + __SHIFTIN(fn, ECAM_FUNC_MASK) | + offset; + + bus_space_write_4(sc->sc_bst, sc->sc_cfg_bsh, reg, data); +} + +/* INTx interrupt controller */ +static void * +jh7110_pcie_intx_establish(device_t dev, u_int *specifier, int ipl, int flags, + int (*func)(void *), void *arg, const char *xname) +{ + struct jh7110_pcie_softc * const sc = device_private(dev); + const u_int mpsafe = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0; + const u_int pin = be32toh(specifier[0]) - 1; + + KASSERT((RD4(sc, PLDA_IMASK_LOCAL) & (PLDA_IMASK_INT_INTA << pin)) == 0); + + struct jh7110_pcie_irq *jpi = sc->sc_irq[pin]; + if (jpi == NULL) { + jpi = kmem_alloc(sizeof(*jpi), KM_SLEEP); + jpi->jpi_sc = sc; + jpi->jpi_fn = func; + jpi->jpi_arg = arg; + jpi->jpi_mpsafe = mpsafe; + + sc->sc_irq[pin] = jpi; + } else { + device_printf(dev, "shared interrupts not supported\n"); + return NULL; + } + + /* Unmask the interrupt. */ + SET4(sc, PLDA_IMASK_LOCAL, (PLDA_IMASK_INT_INTA << pin)); + + return jpi; +} + + +static void +jh7110_pcie_intx_disestablish(device_t dev, void *ih) +{ + struct jh7110_pcie_softc * const sc = device_private(dev); + struct pcihost_softc * const phsc = &sc->sc_phsc; + + device_printf(dev, "%s\n", __func__); + + fdtbus_intr_disestablish(phsc->sc_phandle, ih); +} + +static bool +jh7110_pcie_intx_string(device_t dev, u_int *specifier, char *buf, + size_t buflen) +{ + struct jh7110_pcie_softc * const sc = device_private(dev); + struct pcihost_softc * const phsc = &sc->sc_phsc; + + fdtbus_intr_str(phsc->sc_phandle, 0, buf, buflen); + + return true; +} + +static int +jh7110_pcie_intx_intr(struct jh7110_pcie_softc *sc, uint32_t status) +{ + int handled = 0; + u_int pin; + + CTASSERT(__arraycount(sc->sc_irq) == 4); + for (pin = 0; pin < __arraycount(sc->sc_irq); pin++) { + if ((status & (PLDA_IMASK_INT_INTA << pin)) == 0) + continue; + + struct jh7110_pcie_irq *jpi = sc->sc_irq[pin]; + + if (jpi == NULL) + continue; + + if (!jpi->jpi_mpsafe) + KERNEL_LOCK(1, NULL); + handled |= jpi->jpi_fn(jpi->jpi_arg); + if (!jpi->jpi_mpsafe) + KERNEL_UNLOCK_ONE(NULL); + } + + return handled; +} + + +static int +jh7110_pcie_intr(void *v) +{ + struct jh7110_pcie_softc * const sc = v; + int handled = 0; + + uint32_t status = RD4(sc, PLDA_ISTATUS_LOCAL); + if (status == 0) + return 0; + + if (status & PLDA_ISTATUS_INT_INTX) + handled |= jh7110_pcie_intx_intr(sc, status); + + WR4(sc, PLDA_ISTATUS_LOCAL, status); + + return handled; +} + +static struct fdtbus_interrupt_controller_func jh7110_pcie_intxfuncs = { + .establish = jh7110_pcie_intx_establish, + .disestablish = jh7110_pcie_intx_disestablish, + .intrstr = jh7110_pcie_intx_string, +}; + +#define SCRD4(sc, off) syscon_read_4((sc), (off)) +#define SCWR4(sc, off, val) syscon_write_4((sc), (off), (val)) + +#define SCSET4(sc, off, mask) \ + SCWR4((sc), (off), SCRD4((sc), (off)) | (mask)) +#define SCCLR4(sc, off, mask) \ + SCWR4((sc), (off), SCRD4((sc), (off)) & ~(mask)) +#define SCUPD4(sc, off, clr, set) \ + SCWR4((sc), (off), (SCRD4((sc), (off)) & ~(clr)) | (set)) + + +static int +jh7110_pcie_host_init(struct jh7110_pcie_softc *sc) +{ + struct pcihost_softc * const phsc = &sc->sc_phsc; + + syscon_lock(sc->sc_syscon); + SCSET4(sc->sc_syscon, sc->sc_stg_base + STG_SYSCON_RP_NEP_OFFSET, + STG_SYSCON_K_RP_NEP); + + SCUPD4(sc->sc_syscon, sc->sc_stg_base + STG_SYSCON_AW_OFFSET, + STG_SYSCON_CKREF_SRC_MASK, + __SHIFTIN(2, STG_SYSCON_CKREF_SRC_MASK)); + + SCSET4(sc->sc_syscon, sc->sc_stg_base + STG_SYSCON_AW_OFFSET, + STG_SYSCON_CLKREQ); + + /* enable clocks */ + struct clk *clk; + fdtbus_clock_assign(phsc->sc_phandle); + for (u_int c = 0; + (clk = fdtbus_clock_get_index(phsc->sc_phandle, c)) != NULL; + c++) { + if (clk_enable(clk) != 0) { + aprint_error_dev(phsc->sc_dev, + ": couldn't enable clock #%d\n", c); + return ENXIO; + } + } + /* de-assert resets */ + struct fdtbus_reset *rst; + for (u_int r = 0; + (rst = fdtbus_reset_get_index(phsc->sc_phandle, r)) != NULL; + r++) { + if (fdtbus_reset_deassert(rst) != 0) { + aprint_error_dev(phsc->sc_dev, + ": couldn't de-assert reset #%d\n", r); + return ENXIO; + } + } + + fdtbus_gpio_write(sc->sc_perst_gpio, 1); + + /* Disable additional functions. */ + for (u_int i = 1; i < PCIE_FUNC_NUM; i++) { + + SCUPD4(sc->sc_syscon, sc->sc_stg_base + STG_SYSCON_AR_OFFSET, + STG_SYSCON_AXI4_SLVL_AR_MASK, + __SHIFTIN(i, STG_SYSCON_AXI4_SLVL_PHY_AR_MASK)); + + SCUPD4(sc->sc_syscon, sc->sc_stg_base + STG_SYSCON_AW_OFFSET, + STG_SYSCON_AXI4_SLVL_AW_MASK, + __SHIFTIN(i, STG_SYSCON_AXI4_SLVL_PHY_AW_MASK)); + + SET4(sc, PLDA_MISC, PLDA_MISC_PHYFUNC_DISABLE); + } + + SCCLR4(sc->sc_syscon, sc->sc_stg_base + STG_SYSCON_AR_OFFSET, + STG_SYSCON_AXI4_SLVL_AR_MASK); + + SCCLR4(sc->sc_syscon, sc->sc_stg_base + STG_SYSCON_AW_OFFSET, + STG_SYSCON_AXI4_SLVL_AW_MASK); + + /* Configure controller as root port. */ + UPD4(sc, PLDA_GEN_SETTINGS, PLDA_GEN_RP_ENABLE, PLDA_GEN_RP_ENABLE); + uint64_t base_addr = 0; + +#define CONFIG_SPACE_ADDR_OFFSET 0x1000 + + SET4(sc, CONFIG_SPACE_ADDR_OFFSET + 0x10, BUS_ADDR_LO32(base_addr)); + SET4(sc, CONFIG_SPACE_ADDR_OFFSET + 0x14, BUS_ADDR_HI32(base_addr)); + + /* Configure as PCI bridge. */ + UPD4(sc, PLDA_PCI_IDS, + PLDA_PCI_IDS_CLASSCODE_MASK, + PCI_CLASS_CODE(PCI_CLASS_BRIDGE, + PCI_SUBCLASS_BRIDGE_PCI, + PCI_INTERFACE_BRIDGE_PCI_PCI)); + + /* Enable prefetchable memory windows. */ + SET4(sc, PLDA_WINROM, PLDA_WINROM_PREF64SUPPORT); + + /* Disable LTR message forwarding. */ + CLR4(sc, PLDA_PMSG_SUPPORT_RX, PLDA_PMSG_LTR_SUPPORT); + + /* + * PERST# must remain asserted for at least 100us after the + * reference clock becomes stable. But also has to remain + * active at least 100ms after power up. Since we may have + * just powered on the device, play it safe and use 100ms. + */ + delay(100 * 1000); + + /* Deassert PERST#. */ + fdtbus_gpio_write(sc->sc_perst_gpio, 0); + + /* Wait for link to come up. */ + uint32_t reg; + for (int timo = 100; timo > 0; timo--) { + reg = SCRD4(sc->sc_syscon, + sc->sc_stg_base + STG_SYSCON_LNKSTA_OFFSET); + if (reg & DATA_LINK_ACTIVE) + break; + delay(1000); + } + + syscon_unlock(sc->sc_syscon); + + if ((reg & DATA_LINK_ACTIVE) == 0) { + aprint_error_dev(phsc->sc_dev, ": link not up\n"); + return ENXIO; + } + + return 0; +} + +static int +jh7110_pcie_atr_init(struct jh7110_pcie_softc *sc) +{ + const uint32_t *ranges; + bus_addr_t phyaddr; // PADDR? + bus_addr_t pciaddr; + bus_size_t size; + + WR4(sc, PLDA_ATR_AXI4_SLV0_SRC_ADDR_LO(0), + PLDA_ATR_IMPL | + __SHIFTIN(ilog2(sc->sc_cfg_size) - 1, PLDA_ATR_SIZE_MASK) | + BUS_ADDR_LO32(sc->sc_cfg_addr)); + WR4(sc, PLDA_ATR_AXI4_SLV0_SRC_ADDR_HI(0), BUS_ADDR_HI32(sc->sc_cfg_addr)); + WR4(sc, PLDA_ATR_AXI4_SLV0_TRSL_ADDR_LO(0), 0); + WR4(sc, PLDA_ATR_AXI4_SLV0_TRSL_ADDR_HI(0), 0); + WR4(sc, PLDA_ATR_AXI4_SLV0_TRSL_PARAM(0), PLDA_TRSL_ID_PCIE_CONFIG); + + struct pcihost_softc * const phsc = &sc->sc_phsc; + int ranges_len; + ranges = fdtbus_get_prop(phsc->sc_phandle, "ranges", &ranges_len); + if (ranges == NULL) { + aprint_error_dev(phsc->sc_dev, + ": couldn't find 'ranges' property\n"); + return ENXIO; + } + const int ranges_cells = ranges_len / sizeof(uint32_t); + + for (u_int i = 0, n = 1; i < ranges_cells && n < 8; i += 7, n++) { + pciaddr = + __SHIFTIN(be32toh(ranges[i + 1]), __BITS(63, 32)) | + __SHIFTIN(be32toh(ranges[i + 2]), __BITS(31, 0)); + phyaddr = + __SHIFTIN(be32toh(ranges[i + 3]), __BITS(63, 32)) | + __SHIFTIN(be32toh(ranges[i + 4]), __BITS(31, 0)); + size = be32toh(ranges[i + 6]); + + WR4(sc, PLDA_ATR_AXI4_SLV0_SRC_ADDR_LO(n), + PLDA_ATR_IMPL | + __SHIFTIN(ilog2(size) /* - 1 */, PLDA_ATR_SIZE_MASK) | + BUS_ADDR_LO32(phyaddr)); + WR4(sc, PLDA_ATR_AXI4_SLV0_SRC_ADDR_HI(n), BUS_ADDR_HI32(phyaddr)); + WR4(sc, PLDA_ATR_AXI4_SLV0_TRSL_ADDR_LO(n), BUS_ADDR_LO32(pciaddr)); + WR4(sc, PLDA_ATR_AXI4_SLV0_TRSL_ADDR_HI(n), BUS_ADDR_HI32(pciaddr)); + WR4(sc, PLDA_ATR_AXI4_SLV0_TRSL_PARAM(n), PLDA_TRSL_ID_PCIE_RX_TX); + } + + uint32_t val; + val = RD4(sc, PLDA_ATR0_PCIE_WIN0_SRCADDR_PARAM); + val |= (PLDA_ATR0_PCIE_ATR_SIZE << PLDA_ATR0_PCIE_ATR_SIZE_SHIFT); + WR4(sc, PLDA_ATR0_PCIE_WIN0_SRCADDR_PARAM, val); + WR4(sc, PLDA_ATR0_PCIE_WIN0_SRC_ADDR, 0); + + return 0; +} + +/* Compat string(s) */ +static const struct device_compatible_entry compat_data[] = { + { .compat = "starfive,jh7110-pcie" }, + DEVICE_COMPAT_EOL +}; + + +static int +jh7110_pcie_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_compatible_match(faa->faa_phandle, compat_data); +} + +static void +jh7110_pcie_attach(device_t parent, device_t self, void *aux) +{ + struct jh7110_pcie_softc * const sc = device_private(self); + struct pcihost_softc * const phsc = &sc->sc_phsc; + struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + int error; + + sc->sc_bst = faa->faa_bst; + + phsc->sc_dev = self; + phsc->sc_bst = faa->faa_bst; + phsc->sc_pci_bst = faa->faa_bst; + phsc->sc_dmat = faa->faa_dmat; + phsc->sc_phandle = faa->faa_phandle; + + /* Handle old and new binding names. */ + if (fdtbus_get_reg_byname(phandle, "apb", + &sc->sc_apb_addr, &sc->sc_apb_size) != 0) { + aprint_error(": couldn't get apb registers\n"); + return; + } + if (fdtbus_get_reg_byname(phandle, "cfg", + &sc->sc_cfg_addr, &sc->sc_cfg_size) != 0) { + aprint_error(": couldn't get cfg registers\n"); + return; + } + + const int mapflags = 0 /*BUS_SPACE_MAP_NONPOSTED*/; + error = bus_space_map(sc->sc_bst, sc->sc_apb_addr, + sc->sc_apb_size, mapflags, &sc->sc_apb_bsh); + if (error) { + aprint_error(": can't map APB registers\n"); + return; + } + error = bus_space_map(sc->sc_bst, sc->sc_cfg_addr, + sc->sc_cfg_size, mapflags, &sc->sc_cfg_bsh); + if (error) { + aprint_error(": can't map CFG registers\n"); + return; + } + int len; + const char *stgsyscon = "starfive,stg-syscon"; + const u_int *stgsyscon_data = + fdtbus_get_prop(phandle, stgsyscon, &len); + if (stgsyscon_data == NULL) { + aprint_error(": couldn't get '%s' property\n", stgsyscon); + return; + } + + if (len != 1 * sizeof(uint32_t)) { + aprint_error(": incorrect '%s' data (len = %u)\n", stgsyscon, + len); + return; + } + int syscon_phandle = + fdtbus_get_phandle_from_native(be32dec(&stgsyscon_data[0])); + + sc->sc_syscon = fdtbus_syscon_lookup(syscon_phandle); + if (sc->sc_syscon == NULL) { + aprint_error(": couldn't get '%s' (%d)\n", stgsyscon, + syscon_phandle); + return; + } + + sc->sc_perst_gpio = fdtbus_gpio_acquire(phandle, + "perst-gpios", GPIO_PIN_OUTPUT); + if (sc->sc_perst_gpio == NULL) { + aprint_error(": couldn't get 'perst-gpios'"); + return; + } + + switch (sc->sc_cfg_addr) { + case JH7110_PCIE0_CFG_BASE: + sc->sc_stg_base = STG_SYSCON_PCIE0_BASE; + break; + case JH7110_PCIE1_CFG_BASE: + sc->sc_stg_base = STG_SYSCON_PCIE1_BASE; + break; + default: + aprint_error(": unknown controller at 0x%lx\n", + sc->sc_cfg_addr); + return; + } + + aprint_naive("\n"); + aprint_normal(": PCIe\n"); + + error = jh7110_pcie_host_init(sc); + if (error) { + /* error already printed */ + return; + } + + /* Configure Address Translation. */ + error = jh7110_pcie_atr_init(sc); + if (error) { + + } + + /* Mask and acknowledge all interrupts. */ + WR4(sc, PLDA_IMASK_LOCAL, 0); + WR4(sc, PLDA_ISTATUS_LOCAL, 0xffffffff); + + char intrstr[128]; + if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { + aprint_error(": failed to decode interrupt\n"); + return; + } + void *ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM, + FDT_INTR_MPSAFE, jh7110_pcie_intr, sc, device_xname(self)); + if (ih == NULL) { + aprint_error_dev(self, "failed to establish interrupt on %s\n", + intrstr); + // XXXNH unwind + return; + } + aprint_normal_dev(self, "interrupting on %s\n", intrstr); + + fdtbus_register_interrupt_controller(self, + OF_child(phsc->sc_phandle), &jh7110_pcie_intxfuncs); + + phsc->sc_type = PCIHOST_ECAM; + pcihost_init(&phsc->sc_pc, phsc); + + phsc->sc_pc.pc_bus_maxdevs = jh7110_pcie_bus_maxdevs; + phsc->sc_pc.pc_make_tag = jh7110_pcie_make_tag; + phsc->sc_pc.pc_decompose_tag = jh7110_pcie_decompose_tag; + phsc->sc_pc.pc_conf_read = jh7110_pcie_conf_read; + phsc->sc_pc.pc_conf_write = jh7110_pcie_conf_write; + + pcihost_init2(phsc); +} + +CFATTACH_DECL_NEW(jh7110_pcie, sizeof(struct jh7110_pcie_softc), + jh7110_pcie_match, jh7110_pcie_attach, NULL, NULL);