xref: /netbsd-src/sys/arch/arm/acpi/acpipchb.c (revision 84b6468db257bd512ee8a1ad7a66d1ff757f2049)
1*84b6468dSjmcneill /* $NetBSD: acpipchb.c,v 1.33 2025/01/11 11:40:43 jmcneill Exp $ */
2e2ed649eSjmcneill 
3e2ed649eSjmcneill /*-
4e2ed649eSjmcneill  * Copyright (c) 2018 The NetBSD Foundation, Inc.
5e2ed649eSjmcneill  * All rights reserved.
6e2ed649eSjmcneill  *
7e2ed649eSjmcneill  * This code is derived from software contributed to The NetBSD Foundation
8e2ed649eSjmcneill  * by Jared McNeill <jmcneill@invisible.ca>.
9e2ed649eSjmcneill  *
10e2ed649eSjmcneill  * Redistribution and use in source and binary forms, with or without
11e2ed649eSjmcneill  * modification, are permitted provided that the following conditions
12e2ed649eSjmcneill  * are met:
13e2ed649eSjmcneill  * 1. Redistributions of source code must retain the above copyright
14e2ed649eSjmcneill  *    notice, this list of conditions and the following disclaimer.
15e2ed649eSjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
16e2ed649eSjmcneill  *    notice, this list of conditions and the following disclaimer in the
17e2ed649eSjmcneill  *    documentation and/or other materials provided with the distribution.
18e2ed649eSjmcneill  *
19e2ed649eSjmcneill  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20e2ed649eSjmcneill  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21e2ed649eSjmcneill  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22e2ed649eSjmcneill  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23e2ed649eSjmcneill  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24e2ed649eSjmcneill  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25e2ed649eSjmcneill  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26e2ed649eSjmcneill  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27e2ed649eSjmcneill  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28e2ed649eSjmcneill  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29e2ed649eSjmcneill  * POSSIBILITY OF SUCH DAMAGE.
30e2ed649eSjmcneill  */
31e2ed649eSjmcneill 
32e2ed649eSjmcneill #include <sys/cdefs.h>
33*84b6468dSjmcneill __KERNEL_RCSID(0, "$NetBSD: acpipchb.c,v 1.33 2025/01/11 11:40:43 jmcneill Exp $");
34e2ed649eSjmcneill 
35e2ed649eSjmcneill #include <sys/param.h>
36e2ed649eSjmcneill #include <sys/bus.h>
37e2ed649eSjmcneill #include <sys/device.h>
38e2ed649eSjmcneill #include <sys/intr.h>
39e2ed649eSjmcneill #include <sys/systm.h>
40e2ed649eSjmcneill #include <sys/kernel.h>
41e2ed649eSjmcneill #include <sys/queue.h>
42e2ed649eSjmcneill #include <sys/mutex.h>
43e2ed649eSjmcneill #include <sys/kmem.h>
44cf660908Sad #include <sys/cpu.h>
45e2ed649eSjmcneill 
46e2ed649eSjmcneill #include <arm/cpufunc.h>
47cf79ebbbSjmcneill #include <arm/bootconfig.h>
48e2ed649eSjmcneill 
49e2ed649eSjmcneill #include <dev/pci/pcireg.h>
50e2ed649eSjmcneill #include <dev/pci/pcivar.h>
511099f047Sjmcneill #include <dev/pci/pci_resource.h>
52e2ed649eSjmcneill 
53e2ed649eSjmcneill #include <dev/acpi/acpivar.h>
54afb4efc1Sjmcneill #include <dev/acpi/acpi_pci.h>
55e2ed649eSjmcneill #include <dev/acpi/acpi_mcfg.h>
56e2ed649eSjmcneill 
574fb00351Sjmcneill #include <arm/acpi/acpi_pci_machdep.h>
584fb00351Sjmcneill 
598aa946ffSjmcneill #define	ACPIPCHB_MAX_RANGES	64	/* XXX arbitrary limit */
605cc4c843Sjmcneill 
618aa946ffSjmcneill struct acpipchb_bus_range {
625cc4c843Sjmcneill 	bus_addr_t		min;
635cc4c843Sjmcneill 	bus_addr_t		max;
645cc4c843Sjmcneill 	bus_addr_t		offset;
658aa946ffSjmcneill };
668aa946ffSjmcneill 
678aa946ffSjmcneill struct acpipchb_bus_space {
688aa946ffSjmcneill 	struct bus_space	bs;
698aa946ffSjmcneill 
708aa946ffSjmcneill 	struct acpipchb_bus_range range[ACPIPCHB_MAX_RANGES];
718aa946ffSjmcneill 	int			nrange;
725cc4c843Sjmcneill 
735cc4c843Sjmcneill 	int			(*map)(void *, bus_addr_t, bus_size_t,
745cc4c843Sjmcneill 				       int, bus_space_handle_t *);
75059f233aSjmcneill 
76059f233aSjmcneill 	int			flags;
775cc4c843Sjmcneill };
785cc4c843Sjmcneill 
79e2ed649eSjmcneill struct acpipchb_softc {
80e2ed649eSjmcneill 	device_t		sc_dev;
81e2ed649eSjmcneill 
828aa946ffSjmcneill 	bus_space_tag_t		sc_memt;
838aa946ffSjmcneill 
84e2ed649eSjmcneill 	ACPI_HANDLE		sc_handle;
85e2ed649eSjmcneill 	ACPI_INTEGER		sc_bus;
86b6813ca1Sjmcneill 
878aa946ffSjmcneill 	struct acpipchb_bus_space sc_pcimem_bst;
885cc4c843Sjmcneill 	struct acpipchb_bus_space sc_pciio_bst;
89e2ed649eSjmcneill };
90e2ed649eSjmcneill 
91e2ed649eSjmcneill static int	acpipchb_match(device_t, cfdata_t, void *);
92e2ed649eSjmcneill static void	acpipchb_attach(device_t, device_t, void *);
93e2ed649eSjmcneill 
9412a41c4dSjmcneill static void	acpipchb_configure_bus(struct acpipchb_softc *, struct pcibus_attach_args *);
95afa9a575Sjmcneill static void	acpipchb_setup_ranges(struct acpipchb_softc *,
96afa9a575Sjmcneill 				      struct pcibus_attach_args *);
97afa9a575Sjmcneill static void	acpipchb_setup_quirks(struct acpipchb_softc *,
98afa9a575Sjmcneill 				      struct pcibus_attach_args *);
99b6813ca1Sjmcneill 
100e2ed649eSjmcneill CFATTACH_DECL_NEW(acpipchb, sizeof(struct acpipchb_softc),
101e2ed649eSjmcneill 	acpipchb_match, acpipchb_attach, NULL, NULL);
102e2ed649eSjmcneill 
103e2ed649eSjmcneill static const char * const compatible[] = {
104e2ed649eSjmcneill 	"PNP0A08",
105e2ed649eSjmcneill 	NULL
106e2ed649eSjmcneill };
107e2ed649eSjmcneill 
108e2ed649eSjmcneill static int
109e2ed649eSjmcneill acpipchb_match(device_t parent, cfdata_t cf, void *aux)
110e2ed649eSjmcneill {
111e2ed649eSjmcneill 	struct acpi_attach_args *aa = aux;
112e2ed649eSjmcneill 
113e2ed649eSjmcneill 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
114e2ed649eSjmcneill 		return 0;
115e2ed649eSjmcneill 
116e2ed649eSjmcneill 	return acpi_match_hid(aa->aa_node->ad_devinfo, compatible);
117e2ed649eSjmcneill }
118e2ed649eSjmcneill 
119e2ed649eSjmcneill static void
120e2ed649eSjmcneill acpipchb_attach(device_t parent, device_t self, void *aux)
121e2ed649eSjmcneill {
122e2ed649eSjmcneill 	struct acpipchb_softc * const sc = device_private(self);
123e2ed649eSjmcneill 	struct acpi_attach_args *aa = aux;
124e2ed649eSjmcneill 	struct pcibus_attach_args pba;
12586f8722cSjmcneill 	ACPI_INTEGER seg, nomsi;
126afa9a575Sjmcneill 	ACPI_STATUS rv;
127b75afbe8Sjmcneill 	uint16_t bus_start;
1289ebe5263Sjmcneill 	int val;
129e2ed649eSjmcneill 
130e2ed649eSjmcneill 	sc->sc_dev = self;
1318aa946ffSjmcneill 	sc->sc_memt = aa->aa_memt;
132e2ed649eSjmcneill 	sc->sc_handle = aa->aa_node->ad_handle;
133e2ed649eSjmcneill 
134b75afbe8Sjmcneill 	/*
135b75afbe8Sjmcneill 	 * First try to derive the base bus number from _CRS. If that fails,
136b75afbe8Sjmcneill 	 * try _BBN. If that fails too, assume bus 0.
137b75afbe8Sjmcneill 	 */
138b75afbe8Sjmcneill 	if (ACPI_SUCCESS(acpi_pcidev_pciroot_bus(sc->sc_handle, &bus_start))) {
139b75afbe8Sjmcneill 		sc->sc_bus = bus_start;
140b75afbe8Sjmcneill 	} else {
141afa9a575Sjmcneill 		rv = acpi_eval_integer(sc->sc_handle, "_BBN", &sc->sc_bus);
142afa9a575Sjmcneill 		if (ACPI_FAILURE(rv)) {
143e2ed649eSjmcneill 			sc->sc_bus = 0;
144b75afbe8Sjmcneill 		}
145afa9a575Sjmcneill 	}
146e2ed649eSjmcneill 
147afa9a575Sjmcneill 	if (ACPI_FAILURE(acpi_eval_integer(sc->sc_handle, "_SEG", &seg))) {
1484fb00351Sjmcneill 		seg = 0;
149afa9a575Sjmcneill 	}
1504fb00351Sjmcneill 
15186f8722cSjmcneill 	if (ACPI_FAILURE(acpi_dsd_integer(sc->sc_handle, "linux,pcie-nomsi",
15286f8722cSjmcneill 	    &nomsi))) {
15386f8722cSjmcneill 		nomsi = 0;
15486f8722cSjmcneill 	}
1559ebe5263Sjmcneill 	if (get_bootconf_option(boot_args, "nopcimsi",
1569ebe5263Sjmcneill 				BOOTOPT_TYPE_BOOLEAN, &val) && val) {
1579ebe5263Sjmcneill 		nomsi = 1;
1589ebe5263Sjmcneill 	}
15986f8722cSjmcneill 
160e2ed649eSjmcneill 	aprint_naive("\n");
161e2ed649eSjmcneill 	aprint_normal(": PCI Express Host Bridge\n");
162e2ed649eSjmcneill 
163*84b6468dSjmcneill 	acpi_claim_childdevs(self, aa->aa_node, NULL);
164e38da568Sjmcneill 
165e2ed649eSjmcneill 	memset(&pba, 0, sizeof(pba));
166afa9a575Sjmcneill 	pba.pba_flags = aa->aa_pciflags &
167afa9a575Sjmcneill 			~(PCI_FLAGS_MEM_OKAY | PCI_FLAGS_IO_OKAY);
16886f8722cSjmcneill 	if (nomsi) {
16986f8722cSjmcneill 		pba.pba_flags &= ~(PCI_FLAGS_MSI_OKAY | PCI_FLAGS_MSIX_OKAY);
17086f8722cSjmcneill 	}
1718aa946ffSjmcneill 	pba.pba_memt = 0;
172b6813ca1Sjmcneill 	pba.pba_iot = 0;
173a496d771Sjmcneill 	pba.pba_dmat = aa->aa_dmat;
174e2ed649eSjmcneill #ifdef _PCI_HAVE_DMA64
175a496d771Sjmcneill 	pba.pba_dmat64 = aa->aa_dmat64;
176e2ed649eSjmcneill #endif
1776f25f864Sjmcneill 	pba.pba_pc = aa->aa_pc;
178e2ed649eSjmcneill 	pba.pba_bus = sc->sc_bus;
179e2ed649eSjmcneill 
1808aa946ffSjmcneill 	acpipchb_setup_ranges(sc, &pba);
1816f25f864Sjmcneill 	acpipchb_setup_quirks(sc, &pba);
182b6813ca1Sjmcneill 
18312a41c4dSjmcneill 	acpipchb_configure_bus(sc, &pba);
18412a41c4dSjmcneill 
185c66d1ca5Sthorpej 	config_found(self, &pba, pcibusprint,
186c7fb772bSthorpej 	    CFARGS(.devhandle = device_handle(self)));
187e2ed649eSjmcneill }
188b6813ca1Sjmcneill 
18912a41c4dSjmcneill static void
19012a41c4dSjmcneill acpipchb_configure_bus(struct acpipchb_softc *sc, struct pcibus_attach_args *pba)
19112a41c4dSjmcneill {
19212a41c4dSjmcneill 	struct arm32_pci_chipset *md_pc =
19312a41c4dSjmcneill 	    (struct arm32_pci_chipset *)pba->pba_pc;
19412a41c4dSjmcneill 	struct acpi_pci_context *ap = md_pc->pc_conf_v;
1951099f047Sjmcneill 	const bool mapcfgspace = (ap->ap_flags & ACPI_PCI_FLAG_NO_MCFG) == 0;
196cf79ebbbSjmcneill 	int error, val;
19712a41c4dSjmcneill 
198cf79ebbbSjmcneill 	if (get_bootconf_option(boot_args, "nopciconf",
199cf79ebbbSjmcneill 				BOOTOPT_TYPE_BOOLEAN, &val) && val) {
200cf79ebbbSjmcneill 		return;
201cf79ebbbSjmcneill 	}
20212a41c4dSjmcneill 
20312a41c4dSjmcneill 	error = acpimcfg_configure_bus(sc->sc_dev, pba->pba_pc, sc->sc_handle,
2041099f047Sjmcneill 	    sc->sc_bus, mapcfgspace);
20512a41c4dSjmcneill 	if (error != 0) {
20612a41c4dSjmcneill 		aprint_error_dev(sc->sc_dev, "failed to configure bus, error %d\n",
20712a41c4dSjmcneill 		    error);
20812a41c4dSjmcneill 	}
20912a41c4dSjmcneill }
21012a41c4dSjmcneill 
2118aa946ffSjmcneill struct acpipchb_setup_ranges_args {
212b6813ca1Sjmcneill 	struct acpipchb_softc *sc;
213b6813ca1Sjmcneill 	struct pcibus_attach_args *pba;
214b6813ca1Sjmcneill };
215b6813ca1Sjmcneill 
2165cc4c843Sjmcneill static int
2175cc4c843Sjmcneill acpipchb_bus_space_map(void *t, bus_addr_t bpa, bus_size_t size, int flag,
2185cc4c843Sjmcneill     bus_space_handle_t *bshp)
2195cc4c843Sjmcneill {
2205cc4c843Sjmcneill 	struct acpipchb_bus_space * const abs = t;
2218aa946ffSjmcneill 	int i;
2225cc4c843Sjmcneill 
2238aa946ffSjmcneill 	if (size == 0)
2245cc4c843Sjmcneill 		return ERANGE;
2255cc4c843Sjmcneill 
226059f233aSjmcneill 	if ((abs->flags & PCI_FLAGS_IO_OKAY) != 0) {
227059f233aSjmcneill 		/* Force strongly ordered mapping for all I/O space */
2285158b98cSjmcneill 		flag = BUS_SPACE_MAP_NONPOSTED;
229059f233aSjmcneill 	}
230059f233aSjmcneill 
2318aa946ffSjmcneill 	for (i = 0; i < abs->nrange; i++) {
2328aa946ffSjmcneill 		struct acpipchb_bus_range * const range = &abs->range[i];
233afa9a575Sjmcneill 		if (bpa >= range->min && bpa + size - 1 <= range->max) {
234afa9a575Sjmcneill 			return abs->map(t, bpa + range->offset, size,
235afa9a575Sjmcneill 					flag, bshp);
236afa9a575Sjmcneill 		}
2378aa946ffSjmcneill 	}
2388aa946ffSjmcneill 
2398aa946ffSjmcneill 	return ERANGE;
2405cc4c843Sjmcneill }
2415cc4c843Sjmcneill 
242b6813ca1Sjmcneill static ACPI_STATUS
2438aa946ffSjmcneill acpipchb_setup_ranges_cb(ACPI_RESOURCE *res, void *ctx)
244b6813ca1Sjmcneill {
2458aa946ffSjmcneill 	struct acpipchb_setup_ranges_args * const args = ctx;
246b6813ca1Sjmcneill 	struct acpipchb_softc * const sc = args->sc;
247b6813ca1Sjmcneill 	struct pcibus_attach_args *pba = args->pba;
2488aa946ffSjmcneill 	struct acpipchb_bus_space *abs;
2498aa946ffSjmcneill 	struct acpipchb_bus_range *range;
2508aa946ffSjmcneill 	const char *range_type;
2518aa946ffSjmcneill 	u_int pci_flags;
252b6813ca1Sjmcneill 
253b6813ca1Sjmcneill 	if (res->Type != ACPI_RESOURCE_TYPE_ADDRESS32 &&
254afa9a575Sjmcneill 	    res->Type != ACPI_RESOURCE_TYPE_ADDRESS64) {
255b6813ca1Sjmcneill 		return AE_OK;
256afa9a575Sjmcneill 	}
257b6813ca1Sjmcneill 
2588aa946ffSjmcneill 	switch (res->Data.Address.ResourceType) {
2598aa946ffSjmcneill 	case ACPI_IO_RANGE:
2608aa946ffSjmcneill 		abs = &sc->sc_pciio_bst;
2618aa946ffSjmcneill 		range_type = "I/O";
2628aa946ffSjmcneill 		pci_flags = PCI_FLAGS_IO_OKAY;
2638aa946ffSjmcneill 		break;
2648aa946ffSjmcneill 	case ACPI_MEMORY_RANGE:
2658aa946ffSjmcneill 		abs = &sc->sc_pcimem_bst;
2668aa946ffSjmcneill 		range_type = "MEM";
2678aa946ffSjmcneill 		pci_flags = PCI_FLAGS_MEM_OKAY;
2688aa946ffSjmcneill 		break;
2698aa946ffSjmcneill 	default:
270b6813ca1Sjmcneill 		return AE_OK;
271b6813ca1Sjmcneill 	}
272b6813ca1Sjmcneill 
2738aa946ffSjmcneill 	if (abs->nrange == ACPIPCHB_MAX_RANGES) {
2748aa946ffSjmcneill 		aprint_error_dev(sc->sc_dev,
275afa9a575Sjmcneill 		    "maximum number of ranges reached (ACPIPCHB_MAX_RANGES)\n");
276b6813ca1Sjmcneill 		return AE_LIMIT;
277b6813ca1Sjmcneill 	}
278b6813ca1Sjmcneill 
2798aa946ffSjmcneill 	range = &abs->range[abs->nrange];
2808aa946ffSjmcneill 	switch (res->Type) {
2818aa946ffSjmcneill 	case ACPI_RESOURCE_TYPE_ADDRESS32:
2828aa946ffSjmcneill 		range->min = res->Data.Address32.Address.Minimum;
2838aa946ffSjmcneill 		range->max = res->Data.Address32.Address.Maximum;
2848aa946ffSjmcneill 		range->offset = res->Data.Address32.Address.TranslationOffset;
2858aa946ffSjmcneill 		break;
2868aa946ffSjmcneill 	case ACPI_RESOURCE_TYPE_ADDRESS64:
2878aa946ffSjmcneill 		range->min = res->Data.Address64.Address.Minimum;
2888aa946ffSjmcneill 		range->max = res->Data.Address64.Address.Maximum;
2898aa946ffSjmcneill 		range->offset = res->Data.Address64.Address.TranslationOffset;
2908aa946ffSjmcneill 		break;
2918aa946ffSjmcneill 	default:
2928aa946ffSjmcneill 		return AE_OK;
2938aa946ffSjmcneill 	}
2948aa946ffSjmcneill 	abs->nrange++;
2958aa946ffSjmcneill 
296afa9a575Sjmcneill 	aprint_debug_dev(sc->sc_dev, "PCI %s [%#lx-%#lx] -> %#lx\n",
297afa9a575Sjmcneill 	    range_type, range->min, range->max, range->offset);
2988aa946ffSjmcneill 
2998aa946ffSjmcneill 	if ((pba->pba_flags & pci_flags) == 0) {
3008aa946ffSjmcneill 		abs->bs = *sc->sc_memt;
3018aa946ffSjmcneill 		abs->bs.bs_cookie = abs;
3028aa946ffSjmcneill 		abs->map = abs->bs.bs_map;
303059f233aSjmcneill 		abs->flags = pci_flags;
3048aa946ffSjmcneill 		abs->bs.bs_map = acpipchb_bus_space_map;
305afa9a575Sjmcneill 		if ((pci_flags & PCI_FLAGS_IO_OKAY) != 0) {
3068aa946ffSjmcneill 			pba->pba_iot = &abs->bs;
307afa9a575Sjmcneill 		} else if ((pci_flags & PCI_FLAGS_MEM_OKAY) != 0) {
3088aa946ffSjmcneill 			pba->pba_memt = &abs->bs;
309afa9a575Sjmcneill 		}
3108aa946ffSjmcneill 		pba->pba_flags |= pci_flags;
3118aa946ffSjmcneill 	}
3128aa946ffSjmcneill 
3138aa946ffSjmcneill 	return AE_OK;
3148aa946ffSjmcneill }
3158aa946ffSjmcneill 
316b6813ca1Sjmcneill static void
3178aa946ffSjmcneill acpipchb_setup_ranges(struct acpipchb_softc *sc, struct pcibus_attach_args *pba)
318b6813ca1Sjmcneill {
3198aa946ffSjmcneill 	struct acpipchb_setup_ranges_args args;
320b6813ca1Sjmcneill 
321b6813ca1Sjmcneill 	args.sc = sc;
322b6813ca1Sjmcneill 	args.pba = pba;
323b6813ca1Sjmcneill 
324afa9a575Sjmcneill 	AcpiWalkResources(sc->sc_handle, "_CRS", acpipchb_setup_ranges_cb,
325afa9a575Sjmcneill 	    &args);
326b6813ca1Sjmcneill }
3276f25f864Sjmcneill 
3286f25f864Sjmcneill static void
3296f25f864Sjmcneill acpipchb_setup_quirks(struct acpipchb_softc *sc, struct pcibus_attach_args *pba)
3306f25f864Sjmcneill {
331afa9a575Sjmcneill 	struct arm32_pci_chipset *md_pc =
332afa9a575Sjmcneill 	    (struct arm32_pci_chipset *)pba->pba_pc;
3336f25f864Sjmcneill 	struct acpi_pci_context *ap = md_pc->pc_conf_v;
3346f25f864Sjmcneill 
3356f25f864Sjmcneill 	pba->pba_flags &= ~ap->ap_pciflags_clear;
3366f25f864Sjmcneill }
337