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