Module Name:    src
Committed By:   jmcneill
Date:           Mon Aug 30 23:26:26 UTC 2021

Modified Files:
        src/sys/arch/evbarm/conf: GENERIC64 files.generic64
Added Files:
        src/sys/arch/arm/apple: apple_dart.c apple_intc.c apple_pcie.c
            apple_platform.c apple_wdog.c files.apple

Log Message:
Add initial support for Apple M1.

Currently supported devices:
 - Interrupt controller
 - IOMMU
 - PCIe (USB3 and ethernet)
 - Reboot via watchdog
 - Framebuffer console


To generate a diff of this commit:
cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/apple/apple_dart.c \
    src/sys/arch/arm/apple/apple_intc.c src/sys/arch/arm/apple/apple_pcie.c \
    src/sys/arch/arm/apple/apple_platform.c \
    src/sys/arch/arm/apple/apple_wdog.c src/sys/arch/arm/apple/files.apple
cvs rdiff -u -r1.182 -r1.183 src/sys/arch/evbarm/conf/GENERIC64
cvs rdiff -u -r1.17 -r1.18 src/sys/arch/evbarm/conf/files.generic64

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/evbarm/conf/GENERIC64
diff -u src/sys/arch/evbarm/conf/GENERIC64:1.182 src/sys/arch/evbarm/conf/GENERIC64:1.183
--- src/sys/arch/evbarm/conf/GENERIC64:1.182	Thu Aug 26 17:08:34 2021
+++ src/sys/arch/evbarm/conf/GENERIC64	Mon Aug 30 23:26:26 2021
@@ -1,5 +1,5 @@
 #
-#	$NetBSD: GENERIC64,v 1.182 2021/08/26 17:08:34 thorpej Exp $
+#	$NetBSD: GENERIC64,v 1.183 2021/08/30 23:26:26 jmcneill Exp $
 #
 #	GENERIC ARM (aarch64) kernel
 #
@@ -12,6 +12,7 @@ maxusers	64
 
 options 	CPU_CORTEX
 options 	CPU_THUNDERX
+options 	SOC_APPLE
 options 	SOC_BCM2837
 options 	SOC_MESONG12
 options 	SOC_MESONGXBB
@@ -140,12 +141,13 @@ syscon*		at fdt? pass 1		# Generic Syste
 sunxisramc*	at fdt? pass 1		# Allwinner System Control
 
 # Timer
-gtmr*		at fdt? pass 1		# ARM Generic Timer
+gtmr*		at fdt? pass 2		# ARM Generic Timer
 gtmr*		at acpi?
 armgtmr0	at gtmr?
 tegratimer*	at fdt?			# Timers
 
 # Watchdog
+applewdog*	at fdt?			# Apple watchdog
 bcmpmwdog*	at fdt?			# Broadcom BCM283x watchdog
 dwcwdt* 	at fdt?			# DesignWare watchdog
 mesongxwdt*	at fdt?			# Amlogic Meson GX watchdog
@@ -153,6 +155,7 @@ sbsawdt*	at acpi?		# ARM SBSA-compliant 
 sunxiwdt*	at fdt?			# Allwinner watchdog
 
 # Interrupt controller
+appleintc*	at fdt? pass 1		# Apple AIC
 bcmicu*		at fdt? pass 1		# Broadcom BCM283x ICU
 tegralic*	at fdt? pass 1		# NVIDIA Tegra LIC
 gic*		at fdt? pass 1		# ARM GICv2
@@ -163,6 +166,9 @@ gicvthree*	at acpi?
 imx7gpc*	at fdt? pass 2		# IMX GPCv2
 sunxinmi*	at fdt? pass 2		# Allwinner NMI / R_INTC
 
+# IOMMU
+appledart*	at fdt? pass 3		# Apple DART
+
 # Memory controller
 tegramc*	at fdt?	pass 4		# NVIDIA Tegra MC
 
@@ -233,6 +239,7 @@ tegra210xpad*	at fdt?			# NVIDIA Tegra X
 tegra210xphy*	at tegra210xpad?
 
 # PCIE
+applepcie*	at fdt?			# Apple PCIe
 pcihost*	at fdt?			# Generic PCI host controller
 acpipchb*	at acpi?		# ACPI PCIe host bridge
 rkpcie*		at fdt?			# Rockchip AXI PCIE
@@ -251,6 +258,7 @@ genet*		at fdt?
 #scx*		at acpi?			# Socionext Gigabit Ethernet
 #scx*		at fdt?
 aq*		at pci? dev ? function ?	# Aquantia AQC 10 gigabit
+bge*		at pci? dev ? function ?	# Broadcom 570x gigabit Ethernet
 ena*		at pci? dev ? function ?	# Amazon.com Elastic Network Adapter
 ixg* 		at pci? dev ? function ?	# Intel 8259x 10 gigabit
 ixv* 		at pci? dev ? function ?	# Intel 8259x 10G virtual function

Index: src/sys/arch/evbarm/conf/files.generic64
diff -u src/sys/arch/evbarm/conf/files.generic64:1.17 src/sys/arch/evbarm/conf/files.generic64:1.18
--- src/sys/arch/evbarm/conf/files.generic64:1.17	Wed Dec 23 14:42:38 2020
+++ src/sys/arch/evbarm/conf/files.generic64	Mon Aug 30 23:26:26 2021
@@ -1,4 +1,4 @@
-#	$NetBSD: files.generic64,v 1.17 2020/12/23 14:42:38 skrll Exp $
+#	$NetBSD: files.generic64,v 1.18 2021/08/30 23:26:26 jmcneill Exp $
 #
 # A generic (aarch64) kernel configuration info
 #
@@ -12,6 +12,7 @@ include "arch/evbarm/conf/files.fdt"
 #
 include "arch/arm/acpi/files.acpi"
 include "arch/arm/amlogic/files.meson"
+include "arch/arm/apple/files.apple"
 include "arch/arm/broadcom/files.bcm2835"
 include "arch/arm/nvidia/files.tegra"
 include "arch/arm/nxp/files.imx"

Added files:

Index: src/sys/arch/arm/apple/apple_dart.c
diff -u /dev/null src/sys/arch/arm/apple/apple_dart.c:1.1
--- /dev/null	Mon Aug 30 23:26:26 2021
+++ src/sys/arch/arm/apple/apple_dart.c	Mon Aug 30 23:26:26 2021
@@ -0,0 +1,630 @@
+/* $NetBSD: apple_dart.c,v 1.1 2021/08/30 23:26:26 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2021 Mark Kettenis <kette...@openbsd.org>
+ * Copyright (c) 2021 Jared McNeill <jmcne...@invisible.ca>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+//#define APPLE_DART_DEBUG
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: apple_dart.c,v 1.1 2021/08/30 23:26:26 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/kmem.h>
+#include <sys/vmem.h>
+
+#include <arm/cpufunc.h>
+
+#include <dev/fdt/fdtvar.h>
+
+/*
+ * DT node to bus_dma tag mappings
+ */
+
+bus_dma_tag_t apple_dart_iommu_lookup(int);
+
+struct apple_dart_iommu {
+	int phandle;
+	bus_dma_tag_t dmat;
+	LIST_ENTRY(apple_dart_iommu) next;
+};
+
+static LIST_HEAD(, apple_dart_iommu) apple_dart_iommus =
+    LIST_HEAD_INITIALIZER(apple_dart_iommus);
+
+static void
+apple_dart_iommu_register(int phandle, bus_dma_tag_t dmat)
+{
+	struct apple_dart_iommu *iommu;
+
+	iommu = kmem_alloc(sizeof(*iommu), KM_SLEEP);
+	iommu->phandle = phandle;
+	iommu->dmat = dmat;
+	LIST_INSERT_HEAD(&apple_dart_iommus, iommu, next);
+}
+
+bus_dma_tag_t
+apple_dart_iommu_lookup(int phandle)
+{
+	struct apple_dart_iommu *iommu;
+
+	LIST_FOREACH(iommu, &apple_dart_iommus, next) {
+		if (iommu->phandle == phandle) {
+			return iommu->dmat;
+		}
+	}
+
+	panic("Couldn't find IOMMU for node 0x%x", phandle);
+}
+
+/*
+ * DART registers
+ */
+#define	DART_TLB_OP		0x0020
+#define	 DART_TLB_OP_FLUSH	__BIT(20)
+#define	 DART_TLB_OP_BUSY	__BIT(2)
+#define	DART_TLB_OP_SIDMASK	0x0034
+#define	DART_ERR_STATUS		0x0040
+#define	DART_ERR_ADDRL		0x0050
+#define	DART_ERR_ADDRH		0x0054
+#define	DART_CONFIG(sid)	(0x0100 + (sid) * 0x4)
+#define	 DART_CONFIG_TXEN	__BIT(7)
+#define	DART_TTBR(sid, idx)	(0x0200 + (sid) * 0x10 + (idx) * 0x4)
+#define	 DART_TTBR_VALID	__BIT(31)
+#define	 DART_TTBR_SHIFT	12
+
+#define	DART_APERTURE_START	0x00100000
+#define	DART_APERTURE_SIZE	0x3fe00000
+#define	DART_PAGE_SIZE		16384
+#define	DART_PAGE_MASK		(DART_PAGE_SIZE - 1)
+
+#define	DART_L1_TABLE		0xb
+#define	DART_L2_INVAL		0x0
+#define	DART_L2_PAGE		0x3
+
+#define	DART_ROUND_PAGE(pa)	(((pa) + DART_PAGE_MASK) & ~DART_PAGE_MASK)
+#define	DART_TRUNC_PAGE(pa)	((pa) & ~DART_PAGE_MASK)
+
+static const struct device_compatible_entry compat_data[] = {
+	{ .compat = "apple,dart-m1",		.value = 16 },
+	DEVICE_COMPAT_EOL
+};
+
+static struct arm32_dma_range apple_dart_dma_ranges[] = {
+	[0] = {
+		.dr_sysbase = 0,
+		.dr_busbase = 0,
+		.dr_len = UINTPTR_MAX,
+		.dr_flags = _BUS_DMAMAP_COHERENT,
+	}
+};
+
+struct apple_dart_map_state {
+	bus_addr_t ams_dva;
+	bus_size_t ams_len;
+};
+
+struct apple_dart_dma {
+	bus_dmamap_t dma_map;
+	bus_dma_segment_t dma_seg;
+	bus_size_t dma_size;
+	void *dma_kva;
+};
+
+#define	DART_DMA_MAP(_dma)	((_dma)->dma_map)
+#define	DART_DMA_LEN(_dma)	((_dma)->dma_size)
+#define	DART_DMA_DVA(_dma)	((_dma)->dma_map->dm_segs[0].ds_addr)
+#define	DART_DMA_KVA(_dma)	((_dma)->dma_kva)
+
+struct apple_dart_softc {
+	device_t sc_dev;
+	int sc_phandle;
+	bus_space_tag_t sc_bst;
+	bus_space_handle_t sc_bsh;
+	bus_dma_tag_t sc_dmat;
+
+	uint64_t sc_sid_mask;
+	u_int sc_nsid;
+
+	vmem_t *sc_dvamap;
+
+	struct apple_dart_dma *sc_l1;
+	struct apple_dart_dma **sc_l2;
+	u_int sc_nl2;
+
+	struct arm32_bus_dma_tag sc_bus_dmat;
+};
+
+#define DART_READ(sc, reg) \
+	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define	DART_WRITE(sc, reg, val) \
+	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+static void
+apple_dart_flush_tlb(struct apple_dart_softc *sc)
+{
+	dsb(sy);
+	isb();
+
+	DART_WRITE(sc, DART_TLB_OP_SIDMASK, sc->sc_sid_mask);
+	DART_WRITE(sc, DART_TLB_OP, DART_TLB_OP_FLUSH);
+	while ((DART_READ(sc, DART_TLB_OP) & DART_TLB_OP_BUSY) != 0) {
+		__asm volatile ("yield" ::: "memory");
+	}
+}
+
+static struct apple_dart_dma *
+apple_dart_dma_alloc(bus_dma_tag_t dmat, bus_size_t size, bus_size_t align)
+{
+	struct apple_dart_dma *dma;
+	int nsegs, error;
+
+	dma = kmem_zalloc(sizeof(*dma), KM_SLEEP);
+	dma->dma_size = size;
+
+	error = bus_dmamem_alloc(dmat, size, align, 0, &dma->dma_seg, 1,
+	    &nsegs, BUS_DMA_WAITOK);
+	if (error != 0) {
+		goto destroy;
+	}
+
+	error = bus_dmamem_map(dmat, &dma->dma_seg, nsegs, size,
+	    &dma->dma_kva, BUS_DMA_WAITOK | BUS_DMA_NOCACHE);
+	if (error != 0) {
+		goto free;
+	}
+
+	error = bus_dmamap_create(dmat, size, 1, size, 0,
+	    BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &dma->dma_map);
+	if (error != 0) {
+		goto dmafree;
+	}
+
+	error = bus_dmamap_load(dmat, dma->dma_map, dma->dma_kva, size,
+	    NULL, BUS_DMA_WAITOK);
+	if (error != 0) {
+		goto unmap;
+	}
+
+	memset(dma->dma_kva, 0, size);
+
+	return dma;
+
+destroy:
+	bus_dmamap_destroy(dmat, dma->dma_map);
+unmap:
+	bus_dmamem_unmap(dmat, dma->dma_kva, size);
+free:
+	bus_dmamem_free(dmat, &dma->dma_seg, 1);
+dmafree:
+	kmem_free(dma, sizeof(*dma));
+	return NULL;
+}
+
+static int
+apple_dart_intr(void *priv)
+{
+	struct apple_dart_softc * const sc = priv;
+	char fdt_path[128];
+	uint64_t addr;
+	uint32_t status;
+
+	status = DART_READ(sc, DART_ERR_STATUS);
+	addr = DART_READ(sc, DART_ERR_ADDRL);
+	addr |= (uint64_t)DART_READ(sc, DART_ERR_ADDRH) << 32;
+	DART_WRITE(sc, DART_ERR_STATUS, status);
+
+	fdtbus_get_path(sc->sc_phandle, fdt_path, sizeof(fdt_path));
+
+	printf("%s (%s): error addr 0x%016lx status 0x%08x\n",
+	    device_xname(sc->sc_dev), fdt_path, addr, status);
+
+	return 1;
+}
+
+static volatile uint64_t *
+apple_dart_lookup_tte(struct apple_dart_softc *sc, bus_addr_t dva)
+{
+	int idx = dva / DART_PAGE_SIZE;
+	int l2_idx = idx / (DART_PAGE_SIZE / sizeof(uint64_t));
+	int tte_idx = idx % (DART_PAGE_SIZE / sizeof(uint64_t));
+	volatile uint64_t *l2;
+
+	l2 = DART_DMA_KVA(sc->sc_l2[l2_idx]);
+	return &l2[tte_idx];
+}
+
+static void
+apple_dart_unload_map(struct apple_dart_softc *sc, bus_dmamap_t map)
+{
+	struct apple_dart_map_state *ams = map->_dm_iommu;
+	volatile uint64_t *tte;
+	int seg;
+
+	/* For each segment */
+	for (seg = 0; seg < map->dm_nsegs; seg++) {
+		u_long len, dva;
+
+		if (ams[seg].ams_len == 0) {
+			continue;
+		}
+
+		dva = ams[seg].ams_dva;
+		len = ams[seg].ams_len;
+
+		while (len > 0) {
+			tte = apple_dart_lookup_tte(sc, dva);
+			*tte = DART_L2_INVAL;
+
+			dva += DART_PAGE_SIZE;
+			len -= DART_PAGE_SIZE;
+		}
+
+		vmem_xfree(sc->sc_dvamap, ams[seg].ams_dva, ams[seg].ams_len);
+
+		ams[seg].ams_dva = 0;
+		ams[seg].ams_len = 0;
+	}
+
+	apple_dart_flush_tlb(sc);
+}
+
+static int
+apple_dart_load_map(struct apple_dart_softc *sc, bus_dmamap_t map)
+{
+	struct apple_dart_map_state *ams = map->_dm_iommu;
+	volatile uint64_t *tte;
+	int seg, error;
+
+	/* For each segment */
+	for (seg = 0; seg < map->dm_nsegs; seg++) {
+		paddr_t pa = map->dm_segs[seg]._ds_paddr;
+		psize_t off = pa - DART_TRUNC_PAGE(pa);
+		u_long len, dva;
+
+		len = DART_ROUND_PAGE(map->dm_segs[seg].ds_len + off);
+
+#ifdef APPLE_DART_DEBUG
+		device_printf(sc->sc_dev, "load pa=%#lx off=%lu len=%lu ",
+		    pa, off, len);
+#endif
+
+		error = vmem_xalloc(sc->sc_dvamap, len, DART_PAGE_SIZE, 0,
+		    0, VMEM_ADDR_MIN, VMEM_ADDR_MAX, VM_BESTFIT|VM_NOSLEEP,
+		    &dva);
+		if (error != 0) {
+			apple_dart_unload_map(sc, map);
+#ifdef APPLE_DART_DEBUG
+			printf("error=%d\n", error);
+#endif
+			return error;
+		}
+
+#ifdef APPLE_DART_DEBUG
+		printf("dva=%#lx\n", dva);
+#endif
+
+		ams[seg].ams_dva = dva;
+		ams[seg].ams_len = len;
+
+		map->dm_segs[seg].ds_addr = dva + off;
+
+		pa = DART_TRUNC_PAGE(pa);
+		while (len > 0) {
+			tte = apple_dart_lookup_tte(sc, dva);
+			*tte = pa | DART_L2_PAGE;
+
+			pa += DART_PAGE_SIZE;
+			dva += DART_PAGE_SIZE;
+			len -= DART_PAGE_SIZE;
+		}
+	}
+
+	apple_dart_flush_tlb(sc);
+
+	return 0;
+}
+
+static int
+apple_dart_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments,
+    bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamap)
+{
+	struct apple_dart_softc *sc = t->_cookie;
+	struct apple_dart_map_state *ams;
+	bus_dmamap_t map;
+	int error;
+
+	error = sc->sc_dmat->_dmamap_create(sc->sc_dmat, size, nsegments,
+	    maxsegsz, boundary, flags, &map);
+	if (error != 0) {
+		return error;
+	}
+
+	ams = kmem_zalloc(map->_dm_segcnt * sizeof(*ams),
+	    (flags & BUS_DMA_NOWAIT) != 0 ? KM_NOSLEEP : KM_SLEEP);
+	if (ams == NULL) {
+		sc->sc_dmat->_dmamap_destroy(sc->sc_dmat, map);
+		return ENOMEM;
+	}
+
+	map->_dm_iommu = ams;
+	*dmamap = map;
+	return 0;
+}
+
+static void
+apple_dart_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map)
+{
+	struct apple_dart_softc *sc = t->_cookie;
+	struct apple_dart_map_state *ams = map->_dm_iommu;
+
+	kmem_free(ams, map->_dm_segcnt * sizeof(*ams));
+	sc->sc_dmat->_dmamap_destroy(sc->sc_dmat, map);
+}
+
+static int
+apple_dart_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
+    size_t buflen, struct proc *p, int flags)
+{
+	struct apple_dart_softc *sc = t->_cookie;
+	int error;
+
+	error = sc->sc_dmat->_dmamap_load(sc->sc_dmat, map,
+	    buf, buflen, p, flags);
+	if (error != 0) {
+		return error;
+	}
+
+	error = apple_dart_load_map(sc, map);
+	if (error != 0) {
+		sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
+	}
+
+	return error;
+}
+
+static int
+apple_dart_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map,
+    struct mbuf *m, int flags)
+{
+	struct apple_dart_softc *sc = t->_cookie;
+	int error;
+
+	error = sc->sc_dmat->_dmamap_load_mbuf(sc->sc_dmat, map,
+	    m, flags);
+	if (error != 0) {
+		return error;
+	}
+
+	error = apple_dart_load_map(sc, map);
+	if (error != 0) {
+		sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
+	}
+
+	return error;
+}
+
+static int
+apple_dart_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map,
+    struct uio *uio, int flags)
+{
+	struct apple_dart_softc *sc = t->_cookie;
+	int error;
+
+	error = sc->sc_dmat->_dmamap_load_uio(sc->sc_dmat, map,
+	    uio, flags);
+	if (error != 0) {
+		return error;
+	}
+
+	error = apple_dart_load_map(sc, map);
+	if (error != 0) {
+		sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
+	}
+
+	return error;
+}
+
+static int
+apple_dart_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map,
+    bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags)
+{
+	struct apple_dart_softc *sc = t->_cookie;
+	int error;
+
+	error = sc->sc_dmat->_dmamap_load_raw(sc->sc_dmat, map,
+	    segs, nsegs, size, flags);
+	if (error != 0) {
+		return error;
+	}
+
+	error = apple_dart_load_map(sc, map);
+	if (error != 0) {
+		sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
+	}
+
+	return error;
+}
+
+static void
+apple_dart_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map)
+{
+	struct apple_dart_softc *sc = t->_cookie;
+
+	apple_dart_unload_map(sc, map);
+	sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
+}
+
+static int
+apple_dart_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
+apple_dart_attach(device_t parent, device_t self, void *aux)
+{
+	struct apple_dart_softc * const sc = device_private(self);
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	uint64_t sidmask64;
+	uint32_t sidmask32;
+	char intrstr[128];
+	volatile uint64_t *l1;
+	bus_addr_t addr;
+	bus_size_t size;
+	u_int sid, idx;
+	paddr_t pa;
+	void *ih;
+
+	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
+		aprint_error(": couldn't get registers\n");
+		return;
+	}
+	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
+		aprint_error(": couldn't decode interrupt\n");
+		return;
+	}
+
+	sc->sc_dev = self;
+	sc->sc_phandle = phandle;
+	sc->sc_dmat = faa->faa_dmat;
+	sc->sc_bst = faa->faa_bst;
+	if (bus_space_map(sc->sc_bst, addr, size,
+	    _ARM_BUS_SPACE_MAP_STRONGLY_ORDERED, &sc->sc_bsh) != 0) {
+		aprint_error(": couldn't map registers\n");
+		return;
+	}
+	sc->sc_nsid = of_compatible_lookup(phandle, compat_data)->value;
+
+	if (of_getprop_uint64(phandle, "sid-mask", &sidmask64) == 0) {
+		sc->sc_sid_mask = sidmask64;
+	} else if (of_getprop_uint32(phandle, "sid-mask", &sidmask32) == 0) {
+		sc->sc_sid_mask = sidmask32;
+	} else {
+		sc->sc_sid_mask = 0xffff;
+	}
+
+	aprint_naive("\n");
+	aprint_normal(": Apple DART @ %#lx/%#lx, %u SIDs (mask 0x%lx)\n",
+	    addr, size, sc->sc_nsid, sc->sc_sid_mask);
+
+	KASSERT(sc->sc_nsid == 16);
+	KASSERT(sc->sc_sid_mask == 0xffff);
+
+	sc->sc_dvamap = vmem_create(device_xname(self),
+	    DART_APERTURE_START, DART_APERTURE_SIZE, DART_PAGE_SIZE,
+	    NULL, NULL, NULL, 0, VM_SLEEP, IPL_HIGH);
+	if (sc->sc_dvamap == NULL) {
+		aprint_error_dev(self, "couldn't allocate DVA map\n");
+		return;
+	}
+
+	/* Disable translations */
+	for (sid = 0; sid < sc->sc_nsid; sid++) {
+		DART_WRITE(sc, DART_CONFIG(sid), 0);
+	}
+
+	/* Remove page tables */
+	for (sid = 0; sid < sc->sc_nsid; sid++) {
+		for (idx = 0; idx < 4; idx++) {
+			DART_WRITE(sc, DART_TTBR(sid, idx), 0);
+		}
+	}
+	apple_dart_flush_tlb(sc);
+
+	/*
+	 * Build translation tables. We pre-allocate the translation
+	 * tables for the entire aperture such that we don't have to worry
+	 * about growing them in an mpsafe manner later.
+	 */
+
+	const u_int ntte = howmany(DART_APERTURE_START + DART_APERTURE_SIZE - 1,
+				   DART_PAGE_SIZE);
+	const u_int nl2 = howmany(ntte, DART_PAGE_SIZE / sizeof(uint64_t));
+	const u_int nl1 = howmany(nl2, DART_PAGE_SIZE / sizeof(uint64_t));
+
+	sc->sc_l1 = apple_dart_dma_alloc(sc->sc_dmat,
+	    nl1 * DART_PAGE_SIZE, DART_PAGE_SIZE);
+	if (sc->sc_l1 == NULL) {
+		aprint_error_dev(self, "couldn't allocate L1 tables\n");
+		return;
+	}
+	sc->sc_l2 = kmem_zalloc(nl2 * sizeof(*sc->sc_l2), KM_SLEEP);
+	sc->sc_nl2 = nl2;
+
+	l1 = DART_DMA_KVA(sc->sc_l1);
+	for (idx = 0; idx < nl2; idx++) {
+		sc->sc_l2[idx] = apple_dart_dma_alloc(sc->sc_dmat,
+		    DART_PAGE_SIZE, DART_PAGE_SIZE);
+		if (sc->sc_l2[idx] == NULL) {
+			aprint_error_dev(self,
+			    "couldn't allocate L2 tables\n");
+			return;
+		}
+		l1[idx] = DART_DMA_DVA(sc->sc_l2[idx]) | DART_L1_TABLE;
+	}
+
+	/* Install page tables */
+	for (sid = 0; sid < sc->sc_nsid; sid++) {
+		pa = DART_DMA_DVA(sc->sc_l1);
+		for (idx = 0; idx < nl1; idx++) {
+			DART_WRITE(sc, DART_TTBR(sid, idx),
+			    (pa >> DART_TTBR_SHIFT) | DART_TTBR_VALID);
+			pa += DART_PAGE_SIZE;
+		}
+	}
+	apple_dart_flush_tlb(sc);
+
+	/* Enable translations */
+	for (sid = 0; sid < sc->sc_nsid; sid++) {
+		DART_WRITE(sc, DART_CONFIG(sid), DART_CONFIG_TXEN);
+	}
+
+	ih = fdtbus_intr_establish_xname(phandle, 0, IPL_HIGH, FDT_INTR_MPSAFE,
+	    apple_dart_intr, sc, device_xname(self));
+	if (ih == NULL) {
+		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
+		    intrstr);
+		return;
+	}
+	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
+
+	/* Setup bus DMA tag */
+	sc->sc_bus_dmat = *sc->sc_dmat;
+	sc->sc_bus_dmat._ranges = apple_dart_dma_ranges;
+	sc->sc_bus_dmat._nranges = 1;
+	sc->sc_bus_dmat._cookie = sc;
+	sc->sc_bus_dmat._dmamap_create = apple_dart_dmamap_create;
+	sc->sc_bus_dmat._dmamap_destroy = apple_dart_dmamap_destroy;
+	sc->sc_bus_dmat._dmamap_load = apple_dart_dmamap_load;
+	sc->sc_bus_dmat._dmamap_load_mbuf = apple_dart_dmamap_load_mbuf;
+	sc->sc_bus_dmat._dmamap_load_uio = apple_dart_dmamap_load_uio;
+	sc->sc_bus_dmat._dmamap_load_raw = apple_dart_dmamap_load_raw;
+	sc->sc_bus_dmat._dmamap_unload = apple_dart_dmamap_unload;
+
+	apple_dart_iommu_register(phandle, &sc->sc_bus_dmat);
+}
+
+CFATTACH_DECL_NEW(apple_dart, sizeof(struct apple_dart_softc),
+	apple_dart_match, apple_dart_attach, NULL, NULL);
Index: src/sys/arch/arm/apple/apple_intc.c
diff -u /dev/null src/sys/arch/arm/apple/apple_intc.c:1.1
--- /dev/null	Mon Aug 30 23:26:26 2021
+++ src/sys/arch/arm/apple/apple_intc.c	Mon Aug 30 23:26:26 2021
@@ -0,0 +1,516 @@
+/* $NetBSD: apple_intc.c,v 1.1 2021/08/30 23:26:26 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2021 Jared 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 "opt_ddb.h"
+
+#define	_INTR_PRIVATE
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: apple_intc.c,v 1.1 2021/08/30 23:26:26 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/kernel.h>
+#include <sys/lwp.h>
+#include <sys/systm.h>
+#include <sys/cpu.h>
+#include <sys/kmem.h>
+#include <sys/atomic.h>
+
+#include <dev/fdt/fdtvar.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <arm/cpu.h>
+#include <arm/cpufunc.h>
+#include <arm/armreg.h>
+#include <arm/locore.h>
+#include <arm/pic/picvar.h>
+#include <arm/fdt/arm_fdtvar.h>
+
+/*
+ * AIC registers
+ */
+#define	AIC_INFO		0x0004
+#define	 AIC_INFO_NIRQ		__BITS(15,0)
+#define	AIC_WHOAMI		0x2000
+#define	AIC_EVENT		0x2004
+#define	 AIC_EVENT_TYPE		__BITS(31,16)
+#define	  AIC_EVENT_TYPE_NONE	0
+#define	  AIC_EVENT_TYPE_IRQ	1
+#define	  AIC_EVENT_TYPE_IPI	4
+#define	 AIC_EVENT_DATA		__BITS(15,0)
+#define	 AIC_EVENT_IPI_OTHER	1
+#define	AIC_IPI_SEND		0x2008
+#define	AIC_IPI_ACK		0x200c
+#define	AIC_IPI_MASK_CLR	0x2028
+#define	AIC_IPI_OTHER		__BIT(0)
+#define	AIC_AFFINITY(irqno)	(0x3000 + (irqno) * 4)
+#define	AIC_SW_SET(irqno)	(0x4000 + (irqno) / 32 * 4)
+#define	AIC_SW_CLR(irqno)	(0x4080 + (irqno) / 32 * 4)
+#define	AIC_MASK_SET(irqno)	(0x4100 + (irqno) / 32 * 4)
+#define	AIC_MASK_CLR(irqno)	(0x4180 + (irqno) / 32 * 4)
+#define	 AIC_MASK_BIT(irqno)	__BIT((irqno) & 0x1f)
+
+static const struct device_compatible_entry compat_data[] = {
+	{ .compat = "apple,aic" },
+	DEVICE_COMPAT_EOL
+};
+
+struct apple_intc_softc;
+
+struct apple_intc_percpu {
+	struct apple_intc_softc *pc_sc;
+	u_int pc_cpuid;
+	u_int pc_ipimask;
+
+	struct pic_softc pc_pic;
+};
+
+#define	LOCALPIC_SOURCE_TIMER	0
+#define	LOCALPIC_SOURCE_IPI	1
+
+struct apple_intc_softc {
+	device_t sc_dev;		/* device handle */
+	bus_space_tag_t sc_bst;		/* mmio tag */
+	bus_space_handle_t sc_bsh;	/* mmio handle */
+	u_int sc_nirq;			/* number of supported IRQs */
+	u_int *sc_cpuid;		/* map of cpu index to AIC CPU ID */
+	struct apple_intc_percpu *sc_pc; /* per-CPU data for timer and IPIs */
+
+	struct pic_softc sc_pic;
+};
+
+static struct apple_intc_softc *intc_softc;
+
+#define	PICTOSOFTC(pic)	\
+	((void *)((uintptr_t)(pic) - offsetof(struct apple_intc_softc, sc_pic)))
+#define	PICTOPERCPU(pic) \
+	((void *)((uintptr_t)(pic) - offsetof(struct apple_intc_percpu, pc_pic)))
+
+#define AIC_READ(sc, reg) \
+	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define	AIC_WRITE(sc, reg, val) \
+	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+static void
+apple_intc_unblock_irqs(struct pic_softc *pic, size_t irqbase, uint32_t mask)
+{
+	struct apple_intc_softc * const sc = PICTOSOFTC(pic);
+
+	AIC_WRITE(sc, AIC_SW_SET(irqbase), mask);
+	AIC_WRITE(sc, AIC_MASK_CLR(irqbase), mask);
+}
+
+static void
+apple_intc_block_irqs(struct pic_softc *pic, size_t irqbase, uint32_t mask)
+{
+}
+
+static void
+apple_intc_establish_irq(struct pic_softc *pic, struct intrsource *is)
+{
+	struct apple_intc_softc * const sc = PICTOSOFTC(pic);
+
+	KASSERT(is->is_type == IST_LEVEL);
+
+	/* Route to primary PE by default */
+	AIC_WRITE(sc, AIC_AFFINITY(is->is_irq), __BIT(0));
+	AIC_WRITE(sc, AIC_MASK_CLR(is->is_irq),
+	    AIC_MASK_BIT(is->is_irq));
+}
+
+static void
+apple_intc_set_priority(struct pic_softc *pic, int ipl)
+{
+}
+
+static void
+apple_intc_cpu_init(struct pic_softc *pic, struct cpu_info *ci)
+{
+	struct apple_intc_softc * const sc = PICTOSOFTC(pic);
+	const u_int cpuno = cpu_index(ci);
+
+	sc->sc_cpuid[cpuno] = AIC_READ(sc, AIC_WHOAMI);
+}
+
+static const struct pic_ops apple_intc_picops = {
+	.pic_unblock_irqs = apple_intc_unblock_irqs,
+	.pic_block_irqs = apple_intc_block_irqs,
+	.pic_establish_irq = apple_intc_establish_irq,
+	.pic_set_priority = apple_intc_set_priority,
+	.pic_cpu_init = apple_intc_cpu_init,
+};
+
+static void
+apple_intc_local_unblock_irqs(struct pic_softc *pic, size_t irqbase,
+    uint32_t mask)
+{
+	KASSERT(irqbase == 0);
+
+	if ((mask & __BIT(LOCALPIC_SOURCE_TIMER)) != 0) {
+		gtmr_cntv_ctl_write(gtmr_cntv_ctl_read() & ~CNTCTL_IMASK);
+		isb();
+	}
+}
+
+static void
+apple_intc_local_block_irqs(struct pic_softc *pic, size_t irqbase,
+    uint32_t mask)
+{
+	KASSERT(irqbase == 0);
+
+	if ((mask & __BIT(LOCALPIC_SOURCE_TIMER)) != 0) {
+		gtmr_cntv_ctl_write(gtmr_cntv_ctl_read() | CNTCTL_IMASK);
+		isb();
+	}
+}
+
+static void
+apple_intc_local_establish_irq(struct pic_softc *pic, struct intrsource *is)
+{
+}
+
+static void
+apple_intc_local_ipi_send(struct pic_softc *pic, const kcpuset_t *kcp, u_long ipi)
+{
+	struct apple_intc_percpu * const pc = PICTOPERCPU(pic);
+	struct apple_intc_softc * const sc = pc->pc_sc;
+	const u_int target = sc->sc_cpuid[pc->pc_cpuid];
+
+	atomic_or_32(&pc->pc_ipimask, __BIT(ipi));
+	AIC_WRITE(sc, AIC_IPI_SEND, __BIT(target));
+}
+
+static const struct pic_ops apple_intc_localpicops = {
+	.pic_unblock_irqs = apple_intc_local_unblock_irqs,
+	.pic_block_irqs = apple_intc_local_block_irqs,
+	.pic_establish_irq = apple_intc_local_establish_irq,
+	.pic_ipi_send = apple_intc_local_ipi_send,
+};
+
+static void *
+apple_intc_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags,
+    int (*func)(void *), void *arg, const char *xname)
+{
+	struct apple_intc_softc * const sc = device_private(dev);
+	struct apple_intc_percpu * const pc = &sc->sc_pc[cpu_index(curcpu())];
+
+	/* 1st cell is the interrupt type (0=IRQ, 1=FIQ) */
+	const u_int type = be32toh(specifier[0]);
+	/* 2nd cell is the interrupt number */
+	const u_int intno = be32toh(specifier[1]);
+	/* 3rd cell is the interrupt flags */
+
+	const u_int mpsafe = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0;
+	const int irq = type == 0 ?
+	    intno : pc->pc_pic.pic_irqbase + LOCALPIC_SOURCE_TIMER;
+	return intr_establish_xname(irq, ipl, IST_LEVEL | mpsafe, func, arg,
+	    xname);
+}
+
+static void
+apple_intc_fdt_disestablish(device_t dev, void *ih)
+{
+	intr_disestablish(ih);
+}
+
+static bool
+apple_intc_fdt_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen)
+{
+	if (!specifier)
+		return false;
+
+	/* 1st cell is the interrupt type (0=IRQ, 1=FIQ) */
+	const u_int type = be32toh(specifier[0]);
+	/* 2nd cell is the interrupt number */
+	const u_int intno = be32toh(specifier[1]);
+
+	snprintf(buf, buflen, "%s %u", type == 0 ? "IRQ" : "FIQ", intno);
+
+	return true;
+}
+
+static const struct fdtbus_interrupt_controller_func apple_intc_fdt_funcs = {
+	.establish = apple_intc_fdt_establish,
+	.disestablish = apple_intc_fdt_disestablish,
+	.intrstr = apple_intc_fdt_intrstr,
+};
+
+static void
+apple_intc_mark_pending(struct pic_softc *pic, u_int intno)
+{
+	const int group = intno / 32;
+	const uint32_t pending = __BIT(intno & 0x1f);
+	pic_mark_pending_sources(pic, group * 32, pending);
+}
+
+static void
+apple_intc_irq_handler(void *frame)
+{
+	struct cpu_info * const ci = curcpu();
+	struct apple_intc_softc * const sc = intc_softc;
+	struct pic_softc *pic;
+	struct intrsource *is;
+	const int oldipl = ci->ci_cpl;
+	uint16_t evtype, evdata;
+	bus_size_t clr_reg;
+	uint32_t clr_val;
+
+	ci->ci_data.cpu_nintr++;
+
+	for (;;) {
+		const uint32_t ev = AIC_READ(sc, AIC_EVENT);
+		evtype = __SHIFTOUT(ev, AIC_EVENT_TYPE);
+		evdata = __SHIFTOUT(ev, AIC_EVENT_DATA);
+
+		dsb(sy);
+		isb();
+
+		if (evtype == AIC_EVENT_TYPE_IRQ) {
+			KASSERT(evdata < sc->sc_nirq);
+			pic = &sc->sc_pic;
+			is = pic->pic_sources[evdata];
+			KASSERT(is != NULL);
+
+			AIC_WRITE(sc, AIC_SW_CLR(evdata),
+			    __BIT(evdata & 0x1f));
+
+			clr_reg = AIC_MASK_CLR(evdata);
+			clr_val = AIC_MASK_BIT(evdata);
+		} else if (evtype == AIC_EVENT_TYPE_IPI) {
+			KASSERT(evdata == AIC_EVENT_IPI_OTHER);
+			pic = &sc->sc_pc[cpu_index(ci)].pc_pic;
+			is = pic->pic_sources[LOCALPIC_SOURCE_IPI];
+			KASSERT(is != NULL);
+
+			AIC_WRITE(sc, AIC_IPI_ACK, AIC_IPI_OTHER);
+
+			clr_reg = 0;
+			clr_val = 0;
+		} else {
+			break;
+		}
+
+		if (ci->ci_cpl >= is->is_ipl) {
+			apple_intc_mark_pending(pic, is->is_irq);
+		} else {
+			pic_set_priority(ci, is->is_ipl);
+			ENABLE_INTERRUPT();
+			pic_dispatch(is, frame);
+			DISABLE_INTERRUPT();
+
+			if (clr_val != 0) {
+				AIC_WRITE(sc, clr_reg, clr_val);
+			}
+		}
+	}
+
+	if (oldipl != IPL_HIGH) {
+		pic_do_pending_ints(DAIF_I|DAIF_F, oldipl, frame);
+	}
+}
+
+static void
+apple_intc_fiq_handler(void *frame)
+{
+	struct cpu_info * const ci = curcpu();
+	struct apple_intc_softc * const sc = intc_softc;
+	struct pic_softc * const pic = &sc->sc_pc[cpu_index(ci)].pc_pic;
+	const int oldipl = ci->ci_cpl;
+
+	ci->ci_data.cpu_nintr++;
+
+	struct intrsource * const is = pic->pic_sources[LOCALPIC_SOURCE_TIMER];
+
+	dsb(sy);
+	isb();
+
+	if (oldipl >= is->is_ipl) {
+		apple_intc_mark_pending(pic, LOCALPIC_SOURCE_TIMER);
+	} else {
+		pic_set_priority(ci, is->is_ipl);
+		pic_dispatch(is, frame);
+	}
+
+	if (oldipl != IPL_HIGH) {
+		pic_do_pending_ints(DAIF_I|DAIF_F, oldipl, frame);
+	}
+}
+
+static int
+apple_intc_ipi_handler(void *priv)
+{
+	struct apple_intc_percpu * const pc = priv;
+	struct apple_intc_softc * const sc = pc->pc_sc;
+	uint32_t ipimask, bit;
+
+	AIC_WRITE(sc, AIC_IPI_MASK_CLR, AIC_IPI_OTHER);
+	ipimask = atomic_swap_32(&pc->pc_ipimask, 0);
+
+	while ((bit = ffs(ipimask)) > 0) {
+		const u_int ipi = bit - 1;
+
+		switch (ipi) {
+		case IPI_AST:
+			pic_ipi_ast(priv);
+			break;
+		case IPI_NOP:
+			pic_ipi_nop(priv);
+			break;
+#ifdef __HAVE_PREEMPTION
+		case IPI_KPREEMPT:
+			pic_ipi_kpreempt(priv);
+			break;
+#endif
+		case IPI_XCALL:
+			pic_ipi_xcall(priv);
+			break;
+		case IPI_GENERIC:
+			pic_ipi_generic(priv);
+			break;
+		case IPI_SHOOTDOWN:
+			pic_ipi_shootdown(priv);
+			break;
+#ifdef DDB
+		case IPI_DDB:
+			pic_ipi_ddb(priv);
+			break;
+#endif
+		}
+		ipimask &= ~__BIT(ipi);
+	}
+
+	return 1;
+}
+
+static void
+apple_intc_percpu_init(void *priv, struct cpu_info *ci)
+{
+	struct apple_intc_softc * const sc = priv;
+	const u_int cpuno = cpu_index(ci);
+	struct apple_intc_percpu * const pc = &sc->sc_pc[cpuno];
+	struct pic_softc * const pic = &pc->pc_pic;
+
+	pic->pic_cpus = ci->ci_kcpuset;
+
+	pic_add(pic, PIC_IRQBASE_ALLOC);
+
+	if (cpuno != 0) {
+		struct intrsource * const is =
+		    sc->sc_pc[0].pc_pic.pic_sources[LOCALPIC_SOURCE_TIMER];
+		KASSERT(is != NULL);
+
+		intr_establish_xname(pic->pic_irqbase + LOCALPIC_SOURCE_TIMER,
+		    is->is_ipl, is->is_type | (is->is_mpsafe ? IST_MPSAFE : 0),
+		    is->is_func, is->is_arg, is->is_xname);
+	}
+
+	intr_establish_xname(pic->pic_irqbase + LOCALPIC_SOURCE_IPI, IPL_HIGH,
+	    IST_LEVEL | IST_MPSAFE, apple_intc_ipi_handler, pc, "ipi");
+
+}
+
+static int
+apple_intc_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
+apple_intc_attach(device_t parent, device_t self, void *aux)
+{
+	struct apple_intc_softc * const sc = device_private(self);
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	bus_addr_t addr;
+	bus_size_t size;
+	u_int cpuno;
+	int error;
+
+	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
+		aprint_error(": couldn't get registers\n");
+		return;
+	}
+
+	sc->sc_dev = self;
+	sc->sc_bst = faa->faa_bst;
+	if (bus_space_map(sc->sc_bst, addr, size,
+	    _ARM_BUS_SPACE_MAP_STRONGLY_ORDERED, &sc->sc_bsh) != 0) {
+		aprint_error(": couldn't map registers\n");
+		return;
+	}
+
+	sc->sc_nirq = AIC_READ(sc, AIC_INFO) & AIC_INFO_NIRQ;
+
+	aprint_naive("\n");
+	aprint_normal(": Apple AIC (%u IRQs, 1 FIQ)\n", sc->sc_nirq);
+	KASSERT(sc->sc_nirq % 32 == 0);
+
+	sc->sc_pic.pic_ops = &apple_intc_picops;
+	sc->sc_pic.pic_maxsources = sc->sc_nirq;
+	snprintf(sc->sc_pic.pic_name, sizeof(sc->sc_pic.pic_name), "AIC");
+	pic_add(&sc->sc_pic, 0);
+
+	error = fdtbus_register_interrupt_controller(self, phandle,
+	    &apple_intc_fdt_funcs);
+	if (error) {
+		aprint_error_dev(self, "couldn't register with fdtbus: %d\n",
+		    error);
+		return;
+	}
+
+	KASSERT(intc_softc == NULL);
+	intc_softc = sc;
+	arm_fdt_irq_set_handler(apple_intc_irq_handler);
+	arm_fdt_fiq_set_handler(apple_intc_fiq_handler);
+
+	KASSERT(ncpu != 0);
+	sc->sc_cpuid = kmem_zalloc(sizeof(*sc->sc_cpuid) * ncpu, KM_SLEEP);
+	sc->sc_pc = kmem_zalloc(sizeof(*sc->sc_pc) * ncpu, KM_SLEEP);
+	for (cpuno = 0; cpuno < ncpu; cpuno++) {
+		sc->sc_pc[cpuno].pc_sc = sc;
+		sc->sc_pc[cpuno].pc_cpuid = cpuno;
+		sc->sc_pc[cpuno].pc_pic.pic_ops = &apple_intc_localpicops;
+		sc->sc_pc[cpuno].pc_pic.pic_maxsources = 2;
+		snprintf(sc->sc_pc[cpuno].pc_pic.pic_name,
+		    sizeof(sc->sc_pc[cpuno].pc_pic.pic_name), "AIC/%u", cpuno);
+	}
+
+	apple_intc_cpu_init(&sc->sc_pic, curcpu());
+	apple_intc_percpu_init(sc, curcpu());
+	arm_fdt_cpu_hatch_register(sc, apple_intc_percpu_init);
+}
+
+CFATTACH_DECL_NEW(apple_intc, sizeof(struct apple_intc_softc),
+	apple_intc_match, apple_intc_attach, NULL, NULL);
Index: src/sys/arch/arm/apple/apple_pcie.c
diff -u /dev/null src/sys/arch/arm/apple/apple_pcie.c:1.1
--- /dev/null	Mon Aug 30 23:26:26 2021
+++ src/sys/arch/arm/apple/apple_pcie.c	Mon Aug 30 23:26:26 2021
@@ -0,0 +1,554 @@
+/* $NetBSD: apple_pcie.c,v 1.1 2021/08/30 23:26:26 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2021 Jared 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: apple_pcie.c,v 1.1 2021/08/30 23:26:26 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kmem.h>
+#include <sys/bitops.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pciconf.h>
+
+#include <dev/fdt/fdtvar.h>
+
+#include <arm/pci/pci_msi_machdep.h>
+#include <arm/fdt/pcihost_fdtvar.h>
+
+#define	PCIE_MSI_CTRL		0x0124
+#define	 PCIE_MSI_CTRL_EN	(1U << 0)
+#define	 PCIE_MSI_CTRL_32	(5U << 4)
+#define	PCIE_MSI_REMAP		0x0128
+#define	PCIE_MSI_DOORBELL	0x0168
+
+/* XXX apple_dart.c */
+extern bus_dma_tag_t apple_dart_iommu_lookup(int);
+
+struct apple_pcie_softc {
+	struct pcihost_softc	sc_pcihost;
+
+	int			sc_phandle;
+	struct arm_pci_msi	sc_msi;
+	u_int			sc_msi_start;
+	u_int			sc_nmsi;
+	struct pci_attach_args	**sc_msi_pa;
+	void			**sc_msi_ih;
+	uint64_t		sc_msi_addr;
+};
+
+static int	apple_pcie_match(device_t, cfdata_t, void *);
+static void	apple_pcie_attach(device_t, device_t, void *);
+
+static void	apple_pcie_attach_hook(device_t, device_t,
+				       struct pcibus_attach_args *);
+static int	apple_pcie_msi_init(struct apple_pcie_softc *);
+
+CFATTACH_DECL_NEW(apple_pcie, sizeof(struct apple_pcie_softc),
+	apple_pcie_match, apple_pcie_attach, NULL, NULL);
+
+static const struct device_compatible_entry compat_data[] = {
+	{ .compat = "apple,pcie" },
+	DEVICE_COMPAT_EOL
+};
+
+static int
+apple_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
+apple_pcie_attach(device_t parent, device_t self, void *aux)
+{
+	struct apple_pcie_softc * const asc = device_private(self);
+	struct pcihost_softc * const sc = &asc->sc_pcihost;
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	bus_addr_t cs_addr;
+	bus_size_t cs_size;
+	int error;
+
+	if (fdtbus_get_reg(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_phandle = phandle;
+	error = bus_space_map(sc->sc_bst, cs_addr, cs_size,
+	    _ARM_BUS_SPACE_MAP_STRONGLY_ORDERED, &sc->sc_bsh);
+	if (error) {
+		aprint_error(": couldn't map registers: %d\n", error);
+		return;
+	}
+	sc->sc_type = PCIHOST_ECAM;
+
+	if (apple_pcie_msi_init(asc) == 0) {
+		sc->sc_pci_flags |= PCI_FLAGS_MSI_OKAY;
+#if notyet
+		sc->sc_pci_flags |= PCI_FLAGS_MSIX_OKAY;
+#endif
+	}
+
+	aprint_naive("\n");
+	aprint_normal(": Apple PCIe host controller\n");
+
+	pcihost_init(&sc->sc_pc, sc);
+	sc->sc_pc.pc_attach_hook = apple_pcie_attach_hook;
+	pcihost_init2(sc);
+}
+
+static void
+apple_pcie_setup_port(struct apple_pcie_softc *sc, u_int portno)
+{
+	const int phandle = sc->sc_pcihost.sc_phandle;
+	bus_space_tag_t bst = sc->sc_pcihost.sc_bst;
+	char regname[sizeof("portX")];
+	bus_space_handle_t bsh;
+	bus_addr_t addr;
+	bus_size_t size;
+	int error;
+
+	snprintf(regname, sizeof(regname), "port%u", portno);
+	if (fdtbus_get_reg_byname(phandle, regname, &addr, &size) != 0) {
+		aprint_error(": couldn't get %s regs\n", regname);
+		return;
+	}
+	error = bus_space_map(bst, addr, size,
+	    _ARM_BUS_SPACE_MAP_STRONGLY_ORDERED, &bsh);
+	if (error != 0) {
+		aprint_error(": couldn't map %s regs\n", regname);
+		return;
+	}
+
+	/* Doorbell address must be below 4GB */
+	KASSERT((sc->sc_msi_addr & ~0xffffffffUL) == 0);
+
+	bus_space_write_4(bst, bsh, PCIE_MSI_CTRL,
+	    PCIE_MSI_CTRL_32 | PCIE_MSI_CTRL_EN);
+	bus_space_write_4(bst, bsh, PCIE_MSI_REMAP, 0);
+	bus_space_write_4(bst, bsh, PCIE_MSI_DOORBELL,
+	    (uint32_t)sc->sc_msi_addr);
+
+	bus_space_unmap(bst, bsh, size);
+}
+
+static void
+apple_pcie_attach_hook(device_t parent, device_t self,
+    struct pcibus_attach_args *pba)
+{
+	struct apple_pcie_softc *sc = pba->pba_pc->pc_conf_v;
+	const int phandle = sc->sc_pcihost.sc_phandle;
+	const u_int *iommu_map;
+	int len;
+
+	KASSERT(device_is_a(sc->sc_pcihost.sc_dev, "applepcie"));
+
+	iommu_map = fdtbus_get_prop(phandle, "iommu-map", &len);
+	if (iommu_map == NULL) {
+		panic("%s: no iommu-map?!",
+		    device_xname(sc->sc_pcihost.sc_dev));
+		return;
+	}
+
+	while (len >= 16) {
+		const u_int ridbase = be32toh(iommu_map[0]);
+		const u_int xref = fdtbus_get_phandle_from_native(
+		    be32toh(iommu_map[1]));
+
+		const int bus = (ridbase >> 8) & 0xff;
+		if (bus == pba->pba_bus) {
+			pba->pba_dmat = apple_dart_iommu_lookup(xref);
+			pba->pba_dmat64 = pba->pba_dmat;
+			return;
+		}
+
+		iommu_map += 4;
+		len -= 16;
+	}
+
+	//panic("no iommu for bus %d\n", pba->pba_bus);
+	pba->pba_dmat = pba->pba_dmat64 = sc->sc_pcihost.sc_dmat;
+}
+
+static int
+apple_pcie_msi_alloc_msi(struct apple_pcie_softc *sc, int count,
+    const struct pci_attach_args *pa)
+{
+	struct pci_attach_args *new_pa;
+	int msi, n;
+
+	for (msi = 0; msi < sc->sc_nmsi; msi += count) {
+		if (sc->sc_msi_pa[msi] == NULL) {
+			for (n = 1; n < count; n++) {
+				if (msi + n < sc->sc_nmsi &&
+				    sc->sc_msi_pa[msi + n] != NULL) {
+					continue;
+				}
+			}
+
+			for (n = 0; n < count; n++) {
+				new_pa = kmem_alloc(sizeof(*new_pa), KM_SLEEP);
+				memcpy(new_pa, pa, sizeof(*new_pa));
+				sc->sc_msi_pa[msi + n] = new_pa;
+			}
+
+			return msi;
+		}
+	}
+
+	return -1;
+}
+
+static void
+apple_pcie_msi_free_msi(struct apple_pcie_softc *sc, int msi)
+{
+	struct pci_attach_args *pa;
+
+	pa = sc->sc_msi_pa[msi];
+	sc->sc_msi_pa[msi] = NULL;
+
+	if (pa != NULL) {
+		kmem_free(pa, sizeof(*pa));
+	}
+}
+
+static int
+apple_pcie_msi_available_msi(struct apple_pcie_softc *sc)
+{
+	int msi, n;
+
+	for (n = 0, msi = 0; msi < sc->sc_nmsi; msi++) {
+		if (sc->sc_msi_pa[msi] == NULL) {
+			n++;
+		}
+	}
+
+	return n;
+}
+
+static void
+apple_pcie_msi_msi_enable(struct apple_pcie_softc *sc, int msi, int count)
+{
+	const struct pci_attach_args *pa = sc->sc_msi_pa[msi];
+	pci_chipset_tag_t pc = pa->pa_pc;
+	pcitag_t tag = pa->pa_tag;
+	pcireg_t ctl;
+	int off;
+
+	if (!pci_get_capability(pc, tag, PCI_CAP_MSI, &off, NULL))
+		panic("apple_pcie_msi_msi_enable: device is not MSI-capable");
+
+	ctl = pci_conf_read(pc, tag, off + PCI_MSI_CTL);
+	ctl &= ~PCI_MSI_CTL_MSI_ENABLE;
+	pci_conf_write(pc, tag, off + PCI_MSI_CTL, ctl);
+
+	ctl = pci_conf_read(pc, tag, off + PCI_MSI_CTL);
+	ctl &= ~PCI_MSI_CTL_MME_MASK;
+	ctl |= __SHIFTIN(ilog2(count), PCI_MSI_CTL_MME_MASK);
+	pci_conf_write(pc, tag, off + PCI_MSI_CTL, ctl);
+
+	const uint64_t addr = sc->sc_msi_addr;
+	const uint32_t data = msi;
+
+	ctl = pci_conf_read(pc, tag, off + PCI_MSI_CTL);
+	if (ctl & PCI_MSI_CTL_64BIT_ADDR) {
+		pci_conf_write(pc, tag, off + PCI_MSI_MADDR64_LO,
+		    addr & 0xffffffff);
+		pci_conf_write(pc, tag, off + PCI_MSI_MADDR64_HI,
+		    (addr >> 32) & 0xffffffff);
+		pci_conf_write(pc, tag, off + PCI_MSI_MDATA64, data);
+	} else {
+		pci_conf_write(pc, tag, off + PCI_MSI_MADDR,
+		    addr & 0xffffffff);
+		pci_conf_write(pc, tag, off + PCI_MSI_MDATA, data);
+	}
+	ctl |= PCI_MSI_CTL_MSI_ENABLE;
+	pci_conf_write(pc, tag, off + PCI_MSI_CTL, ctl);
+}
+
+static void
+apple_pcie_msi_msi_disable(struct apple_pcie_softc *sc, int msi)
+{
+	const struct pci_attach_args *pa = sc->sc_msi_pa[msi];
+	pci_chipset_tag_t pc = pa->pa_pc;
+	pcitag_t tag = pa->pa_tag;
+	pcireg_t ctl;
+	int off;
+
+	if (!pci_get_capability(pc, tag, PCI_CAP_MSI, &off, NULL))
+		panic("apple_pcie_msi_msi_disable: device is not MSI-capable");
+
+	ctl = pci_conf_read(pc, tag, off + PCI_MSI_CTL);
+	ctl &= ~PCI_MSI_CTL_MSI_ENABLE;
+	pci_conf_write(pc, tag, off + PCI_MSI_CTL, ctl);
+}
+
+static void
+apple_pcie_msi_msix_enable(struct apple_pcie_softc *sc, int msi, int msix_vec,
+    bus_space_tag_t bst, bus_space_handle_t bsh)
+{
+	const struct pci_attach_args *pa = sc->sc_msi_pa[msi];
+	pci_chipset_tag_t pc = pa->pa_pc;
+	pcitag_t tag = pa->pa_tag;
+	pcireg_t ctl;
+	uint32_t val;
+	int off;
+
+	if (!pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, NULL))
+		panic("apple_pcie_msi_msix_enable: device is not MSI-X-capable");
+
+	ctl = pci_conf_read(pc, tag, off + PCI_MSIX_CTL);
+	ctl &= ~PCI_MSIX_CTL_ENABLE;
+	pci_conf_write(pc, tag, off + PCI_MSIX_CTL, ctl);
+
+	const uint64_t addr = sc->sc_msi_addr;
+	const uint32_t data = msi;
+	const uint64_t entry_base = PCI_MSIX_TABLE_ENTRY_SIZE * msix_vec;
+	bus_space_write_4(bst, bsh, entry_base + PCI_MSIX_TABLE_ENTRY_ADDR_LO,
+	    (uint32_t)addr);
+	bus_space_write_4(bst, bsh, entry_base + PCI_MSIX_TABLE_ENTRY_ADDR_HI,
+	    (uint32_t)(addr >> 32));
+	bus_space_write_4(bst, bsh, entry_base + PCI_MSIX_TABLE_ENTRY_DATA,
+	    data);
+	val = bus_space_read_4(bst, bsh,
+	    entry_base + PCI_MSIX_TABLE_ENTRY_VECTCTL);
+	val &= ~PCI_MSIX_VECTCTL_MASK;
+	bus_space_write_4(bst, bsh, entry_base + PCI_MSIX_TABLE_ENTRY_VECTCTL,
+	    val);
+
+	ctl = pci_conf_read(pc, tag, off + PCI_MSIX_CTL);
+	ctl |= PCI_MSIX_CTL_ENABLE;
+	pci_conf_write(pc, tag, off + PCI_MSIX_CTL, ctl);
+}
+
+static void
+apple_pcie_msi_msix_disable(struct apple_pcie_softc *sc, int msi)
+{
+	const struct pci_attach_args *pa = sc->sc_msi_pa[msi];
+	pci_chipset_tag_t pc = pa->pa_pc;
+	pcitag_t tag = pa->pa_tag;
+	pcireg_t ctl;
+	int off;
+
+	if (!pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, NULL))
+		panic("apple_pcie_msi_msix_disable: device is not MSI-X-capable");
+
+	ctl = pci_conf_read(pc, tag, off + PCI_MSIX_CTL);
+	ctl &= ~PCI_MSIX_CTL_ENABLE;
+	pci_conf_write(pc, tag, off + PCI_MSIX_CTL, ctl);
+}
+
+static pci_intr_handle_t *
+apple_pcie_msi_msi_alloc(struct arm_pci_msi *msi, int *count,
+    const struct pci_attach_args *pa, bool exact)
+{
+	struct apple_pcie_softc * const sc = msi->msi_priv;
+	pci_intr_handle_t *vectors;
+	int n, off;
+
+	if (!pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_MSI, &off, NULL))
+		return NULL;
+
+	const int avail = apple_pcie_msi_available_msi(sc);
+	if (exact && *count > avail)
+		return NULL;
+
+	while (*count > avail) {
+		if (avail < *count)
+			(*count) >>= 1;
+	}
+	if (*count == 0)
+		return NULL;
+
+	const int msi_base = apple_pcie_msi_alloc_msi(sc, *count, pa);
+	if (msi_base == -1)
+		return NULL;
+
+	vectors = kmem_alloc(sizeof(*vectors) * *count, KM_SLEEP);
+	for (n = 0; n < *count; n++) {
+		const int msino = msi_base + n;
+		vectors[n] = ARM_PCI_INTR_MSI |
+		    __SHIFTIN(msino, ARM_PCI_INTR_IRQ) |
+		    __SHIFTIN(n, ARM_PCI_INTR_MSI_VEC) |
+		    __SHIFTIN(msi->msi_id, ARM_PCI_INTR_FRAME);
+	}
+
+	apple_pcie_msi_msi_enable(sc, msi_base, *count);
+
+	return vectors;
+}
+
+static pci_intr_handle_t *
+apple_pcie_msi_msix_alloc(struct arm_pci_msi *msi, u_int *table_indexes,
+    int *count, const struct pci_attach_args *pa, bool exact)
+{
+	struct apple_pcie_softc * const sc = msi->msi_priv;
+	pci_intr_handle_t *vectors;
+	bus_space_tag_t bst;
+	bus_space_handle_t bsh;
+	bus_size_t bsz;
+	uint32_t table_offset, table_size;
+	int n, off, bar, error;
+	pcireg_t tbl;
+
+	if (!pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_MSIX, &off, NULL))
+		return NULL;
+
+	const int avail = apple_pcie_msi_available_msi(sc);
+	if (exact && *count > avail)
+		return NULL;
+
+	while (*count > avail) {
+		if (avail < *count)
+			(*count) >>= 1;
+	}
+	if (*count == 0)
+		return NULL;
+
+	tbl = pci_conf_read(pa->pa_pc, pa->pa_tag, off + PCI_MSIX_TBLOFFSET);
+	bar = PCI_BAR0 + (4 * (tbl & PCI_MSIX_TBLBIR_MASK));
+	table_offset = tbl & PCI_MSIX_TBLOFFSET_MASK;
+	table_size = pci_msix_count(pa->pa_pc, pa->pa_tag) * PCI_MSIX_TABLE_ENTRY_SIZE;
+	if (table_size == 0)
+		return NULL;
+
+	error = pci_mapreg_submap(pa, bar, pci_mapreg_type(pa->pa_pc, pa->pa_tag, bar),
+	    BUS_SPACE_MAP_LINEAR, roundup(table_size, PAGE_SIZE), table_offset,
+	    &bst, &bsh, NULL, &bsz);
+	if (error)
+		return NULL;
+
+	const int msi_base = apple_pcie_msi_alloc_msi(sc, *count, pa);
+	if (msi_base == -1) {
+		bus_space_unmap(bst, bsh, bsz);
+		return NULL;
+	}
+
+	vectors = kmem_alloc(sizeof(*vectors) * *count, KM_SLEEP);
+	for (n = 0; n < *count; n++) {
+		const int msino = msi_base + n;
+		const int msix_vec = table_indexes ? table_indexes[n] : n;
+		vectors[msix_vec] = ARM_PCI_INTR_MSIX |
+		    __SHIFTIN(msino, ARM_PCI_INTR_IRQ) |
+		    __SHIFTIN(msix_vec, ARM_PCI_INTR_MSI_VEC) |
+		    __SHIFTIN(msi->msi_id, ARM_PCI_INTR_FRAME);
+
+		apple_pcie_msi_msix_enable(sc, msino, msix_vec, bst, bsh);
+	}
+
+	bus_space_unmap(bst, bsh, bsz);
+
+	return vectors;
+}
+
+static void *
+apple_pcie_msi_intr_establish(struct arm_pci_msi *msi,
+    pci_intr_handle_t ih, int ipl, int (*func)(void *), void *arg, const char *xname)
+{
+	struct apple_pcie_softc * const sc = msi->msi_priv;
+
+	const int msino = __SHIFTOUT(ih, ARM_PCI_INTR_IRQ);
+	const int mpsafe = (ih & ARM_PCI_INTR_MPSAFE) ? FDT_INTR_MPSAFE : 0;
+
+	KASSERT(sc->sc_msi_ih[msino] == NULL);
+	sc->sc_msi_ih[msino] = intr_establish_xname(sc->sc_msi_start + msino,
+	    ipl, IST_LEVEL | (mpsafe ? IST_MPSAFE : 0), func, arg, xname);
+
+	return sc->sc_msi_ih[msino];
+}
+
+static void
+apple_pcie_msi_intr_release(struct arm_pci_msi *msi, pci_intr_handle_t *pih,
+    int count)
+{
+	struct apple_pcie_softc * const sc = msi->msi_priv;
+	int n;
+
+	for (n = 0; n < count; n++) {
+		const int msino = __SHIFTOUT(pih[n], ARM_PCI_INTR_IRQ);
+		if (pih[n] & ARM_PCI_INTR_MSIX)
+			apple_pcie_msi_msix_disable(sc, msino);
+		if (pih[n] & ARM_PCI_INTR_MSI)
+			apple_pcie_msi_msi_disable(sc, msino);
+		apple_pcie_msi_free_msi(sc, msino);
+		if (sc->sc_msi_ih[msino] != NULL) {
+			intr_disestablish(sc->sc_msi_ih[msino]);
+			sc->sc_msi_ih[msino] = NULL;
+		}
+	}
+}
+
+static int
+apple_pcie_msi_init(struct apple_pcie_softc *sc)
+{
+	struct arm_pci_msi *msi = &sc->sc_msi;
+	const int phandle = sc->sc_pcihost.sc_phandle;
+	u_int portno;
+	int len;
+
+	const u_int *data = fdtbus_get_prop(phandle, "msi-ranges", &len);
+	if (len != 8) {
+		aprint_error_dev(sc->sc_pcihost.sc_dev,
+		    "WARNING: bad msi-ranges property, MSI not enabled!\n");
+		return ENXIO;
+	}
+	sc->sc_msi_start = be32toh(data[0]);
+	sc->sc_nmsi = be32toh(data[1]);
+	sc->sc_msi_pa = kmem_zalloc(sizeof(*sc->sc_msi_pa) * sc->sc_nmsi,
+	    KM_SLEEP);
+	sc->sc_msi_ih = kmem_zalloc(sizeof(*sc->sc_msi_ih) * sc->sc_nmsi,
+	    KM_SLEEP);
+
+	if (of_getprop_uint64(phandle, "msi-doorbell", &sc->sc_msi_addr)) {
+		sc->sc_msi_addr = 0xffff000ULL;
+	}
+
+	for (portno = 0; portno < 3; portno++) {
+		apple_pcie_setup_port(sc, portno);
+	}
+
+	msi->msi_dev = sc->sc_pcihost.sc_dev;
+	msi->msi_priv = sc;
+	msi->msi_alloc = apple_pcie_msi_msi_alloc;
+	msi->msix_alloc = apple_pcie_msi_msix_alloc;
+	msi->msi_intr_establish = apple_pcie_msi_intr_establish;
+	msi->msi_intr_release = apple_pcie_msi_intr_release;
+
+	return arm_pci_msi_add(msi);
+}
Index: src/sys/arch/arm/apple/apple_platform.c
diff -u /dev/null src/sys/arch/arm/apple/apple_platform.c:1.1
--- /dev/null	Mon Aug 30 23:26:26 2021
+++ src/sys/arch/arm/apple/apple_platform.c	Mon Aug 30 23:26:26 2021
@@ -0,0 +1,213 @@
+/* $NetBSD: apple_platform.c,v 1.1 2021/08/30 23:26:26 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2021 Jared 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: apple_platform.c,v 1.1 2021/08/30 23:26:26 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/device.h>
+#include <sys/termios.h>
+
+#include <dev/fdt/fdtvar.h>
+#include <arm/fdt/arm_fdtvar.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <machine/bootconfig.h>
+
+#include <net/if_ether.h>
+
+#include <dev/pci/pcivar.h>
+#include <machine/pci_machdep.h>
+
+#include <arm/cpufunc.h>
+
+#include <arm/cortex/gtmr_var.h>
+
+#include <arm/arm/psci.h>
+#include <arm/fdt/psci_fdtvar.h>
+
+#include <libfdt.h>
+
+#include <arch/evbarm/fdt/platform.h>
+
+extern struct bus_space arm_generic_bs_tag;
+
+struct arm32_bus_dma_tag apple_coherent_dma_tag;
+static struct arm32_dma_range apple_coherent_ranges[] = {
+	[0] = {
+		.dr_sysbase = 0,
+		.dr_busbase = 0,
+		.dr_len = UINTPTR_MAX,
+		.dr_flags = _BUS_DMAMAP_COHERENT,
+	}
+};
+
+static void
+apple_platform_bootstrap(void)
+{
+	extern struct arm32_bus_dma_tag arm_generic_dma_tag;
+
+	apple_coherent_dma_tag = arm_generic_dma_tag;
+	apple_coherent_dma_tag._ranges = apple_coherent_ranges;
+	apple_coherent_dma_tag._nranges = __arraycount(apple_coherent_ranges);
+
+	arm_fdt_cpu_bootstrap();
+}
+
+static void
+apple_platform_init_attach_args(struct fdt_attach_args *faa)
+{
+	faa->faa_bst = &arm_generic_bs_tag;
+	faa->faa_dmat = &apple_coherent_dma_tag;
+}
+
+static const struct pmap_devmap *
+apple_platform_devmap(void)
+{
+	/* Size this to hold possible entries for the UART and SMP spin-table */
+	static struct pmap_devmap devmap[] = {
+		DEVMAP_ENTRY_END,
+		DEVMAP_ENTRY_END,
+		DEVMAP_ENTRY_END
+	};
+	bus_addr_t uart_base;
+	vaddr_t devmap_va = KERNEL_IO_VBASE;
+	u_int devmap_index = 0;
+	uint64_t release_addr;
+	int phandle;
+
+	phandle = fdtbus_get_stdout_phandle();
+	if (phandle > 0 && fdtbus_get_reg(phandle, 0, &uart_base, NULL) == 0) {
+		devmap[devmap_index].pd_pa = DEVMAP_ALIGN(uart_base);
+		devmap[devmap_index].pd_va = DEVMAP_ALIGN(devmap_va);
+		devmap[devmap_index].pd_size = DEVMAP_SIZE(L3_SIZE);
+		devmap[devmap_index].pd_prot = VM_PROT_READ | VM_PROT_WRITE;
+		devmap[devmap_index].pd_flags = PMAP_DEV_SO;
+		devmap_va = DEVMAP_SIZE(devmap[devmap_index].pd_va +
+		    devmap[devmap_index].pd_size);
+		devmap_index++;
+	}
+
+	/* XXX hopefully all release addresses are in the same 2M */
+	phandle = OF_finddevice("/cpus/cpu@1");
+	if (phandle > 0 &&
+	    of_getprop_uint64(phandle, "cpu-release-addr", &release_addr) == 0) {
+		devmap[devmap_index].pd_pa = DEVMAP_ALIGN(release_addr);
+		devmap[devmap_index].pd_va = DEVMAP_ALIGN(devmap_va);
+		devmap[devmap_index].pd_size = DEVMAP_SIZE(L2_SIZE);
+		devmap[devmap_index].pd_prot = VM_PROT_READ | VM_PROT_WRITE;
+		devmap[devmap_index].pd_flags = PMAP_WRITE_BACK;
+		devmap_va = DEVMAP_SIZE(devmap[devmap_index].pd_va +
+		    devmap[devmap_index].pd_size);
+		devmap_index++;
+	}
+
+	return devmap;
+}
+
+static u_int
+arm_platform_uart_freq(void)
+{
+	return 0;
+}
+
+static int
+apple_platform_get_mac_address(pci_chipset_tag_t pc, pcitag_t tag,
+    uint8_t *eaddr)
+{
+	int b, d, f;
+	int bridge, len;
+	u_int bdf;
+
+	const int pcie = of_find_bycompat(OF_finddevice("/"), "apple,pcie");
+	if (pcie == -1) {
+		return -1;
+	}
+
+	/* Convert PCI tag to encoding of phys.hi for PCI-PCI bridge regs */
+	pci_decompose_tag(pc, tag, &b, &d, &f);
+	bdf = (b << 16) | (d << 11) | (f << 8);
+
+	for (bridge = OF_child(pcie); bridge; bridge = OF_peer(bridge)) {
+		const int ethernet =
+		    of_find_firstchild_byname(bridge, "ethernet");
+		if (ethernet == -1) {
+			continue;
+		}
+		const u_int *data = fdtbus_get_prop(ethernet, "reg", &len);
+		if (data == NULL || len < 4) {
+			continue;
+		}
+		if (bdf != be32toh(data[0])) {
+			continue;
+		}
+
+		return OF_getprop(ethernet, "local-mac-address",
+		    eaddr, ETHER_ADDR_LEN);
+	}
+
+	return -1;
+}
+
+static void
+apple_platform_device_register(device_t self, void *aux)
+{
+	prop_dictionary_t prop = device_properties(self);
+	uint8_t eaddr[ETHER_ADDR_LEN];
+	int len;
+
+	if (device_is_a(self, "bge") &&
+	    device_is_a(device_parent(self), "pci")) {
+		struct pci_attach_args * const pa = aux;
+
+		len = apple_platform_get_mac_address(pa->pa_pc, pa->pa_tag,
+		    eaddr);
+		if (len == ETHER_ADDR_LEN) {
+			prop_dictionary_set_bool(prop, "without-seeprom", true);
+			prop_dictionary_set_data(prop, "mac-address", eaddr,
+			    sizeof(eaddr));
+		}
+		return;
+	}
+}
+
+static const struct arm_platform apple_arm_platform = {
+	.ap_devmap = apple_platform_devmap,
+	.ap_bootstrap = apple_platform_bootstrap,
+	.ap_init_attach_args = apple_platform_init_attach_args,
+	.ap_reset = psci_fdt_reset,
+	.ap_delay = gtmr_delay,
+	.ap_uart_freq = arm_platform_uart_freq,
+	.ap_device_register = apple_platform_device_register,
+	.ap_mpstart = arm_fdt_cpu_mpstart,
+};
+
+ARM_PLATFORM(apple_arm, "apple,arm-platform", &apple_arm_platform);
Index: src/sys/arch/arm/apple/apple_wdog.c
diff -u /dev/null src/sys/arch/arm/apple/apple_wdog.c:1.1
--- /dev/null	Mon Aug 30 23:26:26 2021
+++ src/sys/arch/arm/apple/apple_wdog.c	Mon Aug 30 23:26:26 2021
@@ -0,0 +1,119 @@
+/* $NetBSD: apple_wdog.c,v 1.1 2021/08/30 23:26:26 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2021 Jared 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: apple_wdog.c,v 1.1 2021/08/30 23:26:26 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/kernel.h>
+#include <sys/lwp.h>
+#include <sys/systm.h>
+
+#include <dev/fdt/fdtvar.h>
+
+#define	WDOG_CHIP_CTL		0x000c
+#define	WDOG_SYS_TMR		0x0010
+#define	WDOG_SYS_RST		0x0014
+#define	WDOG_SYS_CTL		0x001c
+#define	 WDOG_SYS_CTL_ENABLE	__BIT(2)
+
+static const struct device_compatible_entry compat_data[] = {
+	{ .compat = "apple,reboot-v0" },
+	DEVICE_COMPAT_EOL
+};
+
+struct apple_wdog_softc {
+	device_t sc_dev;
+	bus_space_tag_t sc_bst;
+	bus_space_handle_t sc_bsh;
+};
+
+#define WDOG_READ(sc, reg) \
+	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define	WDOG_WRITE(sc, reg, val) \
+	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+static void
+apple_wdog_reset(device_t dev)
+{
+	struct apple_wdog_softc * const sc = device_private(dev);
+
+	WDOG_WRITE(sc, WDOG_SYS_RST, 1);
+	WDOG_WRITE(sc, WDOG_SYS_CTL, WDOG_SYS_CTL_ENABLE);
+	WDOG_WRITE(sc, WDOG_SYS_TMR, 0);
+}
+
+static struct fdtbus_power_controller_func apple_wdog_power_funcs = {
+	.reset = apple_wdog_reset,
+};
+
+static int
+apple_wdog_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
+apple_wdog_attach(device_t parent, device_t self, void *aux)
+{
+	struct apple_wdog_softc * const sc = device_private(self);
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	bus_addr_t addr;
+	bus_size_t size;
+
+	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
+		aprint_error(": couldn't get registers\n");
+		return;
+	}
+
+	sc->sc_dev = self;
+	sc->sc_bst = faa->faa_bst;
+	if (bus_space_map(sc->sc_bst, addr, size,
+	    _ARM_BUS_SPACE_MAP_STRONGLY_ORDERED, &sc->sc_bsh) != 0) {
+		aprint_error(": couldn't map registers\n");
+		return;
+	}
+
+	aprint_naive("\n");
+	aprint_normal(": Apple Watchdog\n");
+
+	WDOG_WRITE(sc, WDOG_CHIP_CTL, 0);
+	WDOG_WRITE(sc, WDOG_SYS_CTL, 0);
+
+	fdtbus_register_power_controller(self, phandle,
+	    &apple_wdog_power_funcs);
+}
+
+CFATTACH_DECL_NEW(apple_wdog, sizeof(struct apple_wdog_softc),
+	apple_wdog_match, apple_wdog_attach, NULL, NULL);
Index: src/sys/arch/arm/apple/files.apple
diff -u /dev/null src/sys/arch/arm/apple/files.apple:1.1
--- /dev/null	Mon Aug 30 23:26:26 2021
+++ src/sys/arch/arm/apple/files.apple	Mon Aug 30 23:26:26 2021
@@ -0,0 +1,30 @@
+#	$NetBSD: files.apple,v 1.1 2021/08/30 23:26:26 jmcneill Exp $
+#
+# Configuration info for Apple Silicon SoCs
+#
+#
+
+file	arch/arm/apple/apple_platform.c		soc_apple
+
+# Interrupt controller
+device	appleintc: pic, pic_splfuncs
+attach	appleintc at fdt with apple_intc
+file	arch/arm/apple/apple_intc.c		apple_intc
+
+# Watchdog timer
+device	applewdog: sysmon_wdog
+attach	applewdog at fdt with apple_wdog
+file	arch/arm/apple/apple_wdog.c		apple_wdog
+
+# PCIe controller
+device	applepcie: pcibus, pcihost_fdt
+attach	applepcie at fdt with apple_pcie
+file	arch/arm/apple/apple_pcie.c		apple_pcie
+
+# IOMMU
+device	appledart
+attach	appledart at fdt with apple_dart
+file	arch/arm/apple/apple_dart.c		apple_dart
+
+# SOC parameters
+defflag	opt_soc.h				SOC_APPLE

Reply via email to