Tue Dec 31 13:54:22 2019 UTC ()
Create bus_dma tags for each device node based on _CCA and _DMA properties
found by walking up the device node tree. These tags encode range
restrictions, address translations, and whether or not the device is
cache coherent.


(jmcneill)
diff -r1.16 -r1.17 src/sys/arch/arm/acpi/acpi_machdep.c

cvs diff -r1.16 -r1.17 src/sys/arch/arm/acpi/acpi_machdep.c (expand / switch to context diff)
--- src/sys/arch/arm/acpi/acpi_machdep.c 2019/12/31 11:42:46 1.16
+++ src/sys/arch/arm/acpi/acpi_machdep.c 2019/12/31 13:54:22 1.17
@@ -1,4 +1,4 @@
-/* $NetBSD: acpi_machdep.c,v 1.16 2019/12/31 11:42:46 jmcneill Exp $ */
+/* $NetBSD: acpi_machdep.c,v 1.17 2019/12/31 13:54:22 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2018 The NetBSD Foundation, Inc.
@@ -32,13 +32,14 @@
 #include "pci.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: acpi_machdep.c,v 1.16 2019/12/31 11:42:46 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: acpi_machdep.c,v 1.17 2019/12/31 13:54:22 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
 #include <sys/cpu.h>
 #include <sys/device.h>
+#include <sys/kmem.h>
 
 #include <uvm/uvm_extern.h>
 
@@ -372,88 +373,123 @@
 	NULL
 };
 
-static bus_dma_tag_t
-arm_acpi_dma_tag_subregion(struct acpi_softc *sc, bus_dma_tag_t dmat,
-    ACPI_HANDLE handle)
+static ACPI_HANDLE
+arm_acpi_dma_module(struct acpi_softc *sc, struct acpi_devnode *ad)
 {
+	ACPI_HANDLE tmp;
+	ACPI_STATUS rv;
+
+	/*
+	 * Search up the tree for a module device with a _DMA method.
+	 */
+	for (; ad != NULL; ad = ad->ad_parent) {
+		if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE)
+			continue;
+		if (!acpi_match_hid(ad->ad_devinfo, module_hid))
+			continue;
+		rv = AcpiGetHandle(ad->ad_handle, "_DMA", &tmp);
+		if (ACPI_SUCCESS(rv))
+			return ad->ad_handle;
+	}
+
+	return NULL;
+}
+
+static void
+arm_acpi_dma_init_ranges(struct acpi_softc *sc, struct acpi_devnode *ad,
+    struct arm32_bus_dma_tag *dmat, uint32_t flags)
+{
 	struct acpi_resources res;
 	struct acpi_mem *mem;
-	bus_dma_tag_t newtag;
+	ACPI_HANDLE module;
 	ACPI_STATUS rv;
-	int error;
+	int n;
 
-	rv = acpi_resource_parse(sc->sc_dev, handle, "_DMA", &res,
+	module = arm_acpi_dma_module(sc, ad->ad_parent);
+	if (module == NULL) {
+default_tag:
+		/* No translation required */
+		dmat->_nranges = 1;
+		dmat->_ranges = kmem_zalloc(sizeof(*dmat->_ranges), KM_SLEEP);
+		dmat->_ranges[0].dr_sysbase = 0;
+		dmat->_ranges[0].dr_busbase = 0;
+		dmat->_ranges[0].dr_len = UINTPTR_MAX;
+		dmat->_ranges[0].dr_flags = flags;
+		return;
+	}
+
+	rv = acpi_resource_parse(sc->sc_dev, module, "_DMA", &res,
 	    &acpi_resource_parse_ops_quiet);
-	if (ACPI_FAILURE(rv))
-		return dmat;	/* no translation required */
+	if (ACPI_FAILURE(rv)) {
+		aprint_error_dev(sc->sc_dev,
+		    "failed to parse _DMA on %s: %s\n",
+		    acpi_name(module), AcpiFormatException(rv));
+		goto default_tag;
+	}
+	if (res.ar_nmem == 0) {
+		acpi_resource_cleanup(&res);
+		goto default_tag;
+	}
 
-	mem = acpi_res_mem(&res, 0);
-	if (mem == NULL)
-		goto done;
+	dmat->_nranges = res.ar_nmem;
+	dmat->_ranges = kmem_zalloc(sizeof(*dmat->_ranges) * res.ar_nmem,
+	    KM_SLEEP);
 
-	aprint_debug_dev(sc->sc_dev, "_DMA range %#lx-%#lx\n",
-	    mem->ar_base, mem->ar_base + mem->ar_length - 1);
+	for (n = 0; n < res.ar_nmem; n++) {
+		mem = acpi_res_mem(&res, n);
+		dmat->_ranges[n].dr_busbase = mem->ar_base;
+		dmat->_ranges[n].dr_sysbase = mem->ar_base;
+		if (mem->ar_decode == ACPI_POS_DECODE)
+		 	dmat->_ranges[n].dr_sysbase += mem->ar_offset;
+		else
+			dmat->_ranges[n].dr_sysbase -= mem->ar_offset;
+		dmat->_ranges[n].dr_len = mem->ar_length;
+		dmat->_ranges[n].dr_flags = flags;
 
-	error = bus_dmatag_subregion(dmat,
-	    mem->ar_base, mem->ar_base + mem->ar_length - 1,
-	    &newtag, BUS_DMA_WAITOK);
-	if (error != 0) {
-		aprint_error_dev(sc->sc_dev,
-		    "_DMA subregion failed: %d\n", error);
-		goto done;
+		aprint_debug_dev(sc->sc_dev,
+		    "%s: DMA sysbase %#lx busbase %#lx len %#lx%s\n",
+		    acpi_name(ad->ad_handle),
+		    dmat->_ranges[n].dr_sysbase,
+		    dmat->_ranges[n].dr_busbase,
+		    dmat->_ranges[n].dr_len,
+		    flags ? " (coherent)" : "");
 	}
-	dmat = newtag;
 
-done:
 	acpi_resource_cleanup(&res);
-
-	return dmat;
 }
 
-static ACPI_HANDLE
-arm_acpi_dma_module(struct acpi_softc *sc, struct acpi_devnode *ad)
+static uint32_t
+arm_acpi_dma_flags(struct acpi_softc *sc, struct acpi_devnode *ad)
 {
-	ACPI_HANDLE tmp;
+	ACPI_INTEGER cca = 1;	/* default cache coherent */
 	ACPI_STATUS rv;
 
-	/*
-	 * Search up the tree for a module device with a _DMA method.
-	 */
 	for (; ad != NULL; ad = ad->ad_parent) {
 		if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE)
 			continue;
-		if (!acpi_match_hid(ad->ad_devinfo, module_hid))
-			continue;
-		rv = AcpiGetHandle(ad->ad_handle, "_DMA", &tmp);
+
+		rv = acpi_eval_integer(ad->ad_handle, "_CCA", &cca);
 		if (ACPI_SUCCESS(rv))
-			return ad->ad_handle;
+			break;
 	}
 
-	return NULL;
+	return cca ? _BUS_DMAMAP_COHERENT : 0;
 }
 
+
 bus_dma_tag_t
 arm_acpi_dma_tag(struct acpi_softc *sc, struct acpi_devnode *ad)
 {
-	ACPI_HANDLE module;
-	ACPI_INTEGER cca;
-	bus_dma_tag_t dmat;
+	struct arm32_bus_dma_tag *dmat;
 
-	if (ACPI_FAILURE(acpi_eval_integer(ad->ad_handle, "_CCA", &cca)))
-		cca = 1;
+	if (ad->ad_dmat != NULL)
+		return ad->ad_dmat;
+		
+	dmat = kmem_alloc(sizeof(*dmat), KM_SLEEP);
+	*dmat = arm_generic_dma_tag;
 
-	if (cca)
-		dmat = &acpi_coherent_dma_tag;
-	else
-		dmat = &arm_generic_dma_tag;
-
-	/*
-	 * If a parent device is a bus, it may define valid DMA ranges
-	 * and translations for child nodes.
-	 */
-	module = arm_acpi_dma_module(sc, ad);
-	if (module != NULL)
-		dmat = arm_acpi_dma_tag_subregion(sc, dmat, module);
+	const uint32_t flags = arm_acpi_dma_flags(sc, ad);
+	arm_acpi_dma_init_ranges(sc, ad, dmat, flags);
 
 	return dmat;
 }