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);

Reply via email to