1/* $NetBSD: acpipchb.c,v 1.32 2022/10/15 11:07:38 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2018 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jared McNeill <jmcneill@invisible.ca>.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: acpipchb.c,v 1.32 2022/10/15 11:07:38 jmcneill Exp $");
34
35#include <sys/param.h>
36#include <sys/bus.h>
37#include <sys/device.h>
38#include <sys/intr.h>
39#include <sys/systm.h>
40#include <sys/kernel.h>
41#include <sys/queue.h>
42#include <sys/mutex.h>
43#include <sys/kmem.h>
44#include <sys/cpu.h>
45
46#include <arm/cpufunc.h>
47#include <arm/bootconfig.h>
48
49#include <dev/pci/pcireg.h>
50#include <dev/pci/pcivar.h>
51#include <dev/pci/pci_resource.h>
52
53#include <dev/acpi/acpivar.h>
54#include <dev/acpi/acpi_pci.h>
55#include <dev/acpi/acpi_mcfg.h>
56
57#include <arm/acpi/acpi_pci_machdep.h>
58
59#define	ACPIPCHB_MAX_RANGES	64	/* XXX arbitrary limit */
60
61struct acpipchb_bus_range {
62	bus_addr_t		min;
63	bus_addr_t		max;
64	bus_addr_t		offset;
65};
66
67struct acpipchb_bus_space {
68	struct bus_space	bs;
69
70	struct acpipchb_bus_range range[ACPIPCHB_MAX_RANGES];
71	int			nrange;
72
73	int			(*map)(void *, bus_addr_t, bus_size_t,
74				       int, bus_space_handle_t *);
75
76	int			flags;
77};
78
79struct acpipchb_softc {
80	device_t		sc_dev;
81
82	bus_space_tag_t		sc_memt;
83
84	ACPI_HANDLE		sc_handle;
85	ACPI_INTEGER		sc_bus;
86
87	struct acpipchb_bus_space sc_pcimem_bst;
88	struct acpipchb_bus_space sc_pciio_bst;
89};
90
91static int	acpipchb_match(device_t, cfdata_t, void *);
92static void	acpipchb_attach(device_t, device_t, void *);
93
94static void	acpipchb_configure_bus(struct acpipchb_softc *, struct pcibus_attach_args *);
95static void	acpipchb_setup_ranges(struct acpipchb_softc *,
96				      struct pcibus_attach_args *);
97static void	acpipchb_setup_quirks(struct acpipchb_softc *,
98				      struct pcibus_attach_args *);
99
100CFATTACH_DECL_NEW(acpipchb, sizeof(struct acpipchb_softc),
101	acpipchb_match, acpipchb_attach, NULL, NULL);
102
103static const char * const compatible[] = {
104	"PNP0A08",
105	NULL
106};
107
108static int
109acpipchb_match(device_t parent, cfdata_t cf, void *aux)
110{
111	struct acpi_attach_args *aa = aux;
112
113	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
114		return 0;
115
116	return acpi_match_hid(aa->aa_node->ad_devinfo, compatible);
117}
118
119static void
120acpipchb_attach(device_t parent, device_t self, void *aux)
121{
122	struct acpipchb_softc * const sc = device_private(self);
123	struct acpi_attach_args *aa = aux;
124	struct pcibus_attach_args pba;
125	ACPI_INTEGER seg, nomsi;
126	ACPI_STATUS rv;
127	uint16_t bus_start;
128	int val;
129
130	sc->sc_dev = self;
131	sc->sc_memt = aa->aa_memt;
132	sc->sc_handle = aa->aa_node->ad_handle;
133
134	/*
135	 * First try to derive the base bus number from _CRS. If that fails,
136	 * try _BBN. If that fails too, assume bus 0.
137	 */
138	if (ACPI_SUCCESS(acpi_pcidev_pciroot_bus(sc->sc_handle, &bus_start))) {
139		sc->sc_bus = bus_start;
140	} else {
141		rv = acpi_eval_integer(sc->sc_handle, "_BBN", &sc->sc_bus);
142		if (ACPI_FAILURE(rv)) {
143			sc->sc_bus = 0;
144		}
145	}
146
147	if (ACPI_FAILURE(acpi_eval_integer(sc->sc_handle, "_SEG", &seg))) {
148		seg = 0;
149	}
150
151	if (ACPI_FAILURE(acpi_dsd_integer(sc->sc_handle, "linux,pcie-nomsi",
152	    &nomsi))) {
153		nomsi = 0;
154	}
155	if (get_bootconf_option(boot_args, "nopcimsi",
156				BOOTOPT_TYPE_BOOLEAN, &val) && val) {
157		nomsi = 1;
158	}
159
160	aprint_naive("\n");
161	aprint_normal(": PCI Express Host Bridge\n");
162
163	acpi_claim_childdevs(self, aa->aa_node);
164
165	memset(&pba, 0, sizeof(pba));
166	pba.pba_flags = aa->aa_pciflags &
167			~(PCI_FLAGS_MEM_OKAY | PCI_FLAGS_IO_OKAY);
168	if (nomsi) {
169		pba.pba_flags &= ~(PCI_FLAGS_MSI_OKAY | PCI_FLAGS_MSIX_OKAY);
170	}
171	pba.pba_memt = 0;
172	pba.pba_iot = 0;
173	pba.pba_dmat = aa->aa_dmat;
174#ifdef _PCI_HAVE_DMA64
175	pba.pba_dmat64 = aa->aa_dmat64;
176#endif
177	pba.pba_pc = aa->aa_pc;
178	pba.pba_bus = sc->sc_bus;
179
180	acpipchb_setup_ranges(sc, &pba);
181	acpipchb_setup_quirks(sc, &pba);
182
183	acpipchb_configure_bus(sc, &pba);
184
185	config_found(self, &pba, pcibusprint,
186	    CFARGS(.devhandle = device_handle(self)));
187}
188
189static void
190acpipchb_configure_bus(struct acpipchb_softc *sc, struct pcibus_attach_args *pba)
191{
192	struct arm32_pci_chipset *md_pc =
193	    (struct arm32_pci_chipset *)pba->pba_pc;
194	struct acpi_pci_context *ap = md_pc->pc_conf_v;
195	const bool mapcfgspace = (ap->ap_flags & ACPI_PCI_FLAG_NO_MCFG) == 0;
196	int error, val;
197
198	if (get_bootconf_option(boot_args, "nopciconf",
199				BOOTOPT_TYPE_BOOLEAN, &val) && val) {
200		return;
201	}
202
203	error = acpimcfg_configure_bus(sc->sc_dev, pba->pba_pc, sc->sc_handle,
204	    sc->sc_bus, mapcfgspace);
205	if (error != 0) {
206		aprint_error_dev(sc->sc_dev, "failed to configure bus, error %d\n",
207		    error);
208	}
209}
210
211struct acpipchb_setup_ranges_args {
212	struct acpipchb_softc *sc;
213	struct pcibus_attach_args *pba;
214};
215
216static int
217acpipchb_bus_space_map(void *t, bus_addr_t bpa, bus_size_t size, int flag,
218    bus_space_handle_t *bshp)
219{
220	struct acpipchb_bus_space * const abs = t;
221	int i;
222
223	if (size == 0)
224		return ERANGE;
225
226	if ((abs->flags & PCI_FLAGS_IO_OKAY) != 0) {
227		/* Force strongly ordered mapping for all I/O space */
228		flag = BUS_SPACE_MAP_NONPOSTED;
229	}
230
231	for (i = 0; i < abs->nrange; i++) {
232		struct acpipchb_bus_range * const range = &abs->range[i];
233		if (bpa >= range->min && bpa + size - 1 <= range->max) {
234			return abs->map(t, bpa + range->offset, size,
235					flag, bshp);
236		}
237	}
238
239	return ERANGE;
240}
241
242static ACPI_STATUS
243acpipchb_setup_ranges_cb(ACPI_RESOURCE *res, void *ctx)
244{
245	struct acpipchb_setup_ranges_args * const args = ctx;
246	struct acpipchb_softc * const sc = args->sc;
247	struct pcibus_attach_args *pba = args->pba;
248	struct acpipchb_bus_space *abs;
249	struct acpipchb_bus_range *range;
250	const char *range_type;
251	u_int pci_flags;
252
253	if (res->Type != ACPI_RESOURCE_TYPE_ADDRESS32 &&
254	    res->Type != ACPI_RESOURCE_TYPE_ADDRESS64) {
255		return AE_OK;
256	}
257
258	switch (res->Data.Address.ResourceType) {
259	case ACPI_IO_RANGE:
260		abs = &sc->sc_pciio_bst;
261		range_type = "I/O";
262		pci_flags = PCI_FLAGS_IO_OKAY;
263		break;
264	case ACPI_MEMORY_RANGE:
265		abs = &sc->sc_pcimem_bst;
266		range_type = "MEM";
267		pci_flags = PCI_FLAGS_MEM_OKAY;
268		break;
269	default:
270		return AE_OK;
271	}
272
273	if (abs->nrange == ACPIPCHB_MAX_RANGES) {
274		aprint_error_dev(sc->sc_dev,
275		    "maximum number of ranges reached (ACPIPCHB_MAX_RANGES)\n");
276		return AE_LIMIT;
277	}
278
279	range = &abs->range[abs->nrange];
280	switch (res->Type) {
281	case ACPI_RESOURCE_TYPE_ADDRESS32:
282		range->min = res->Data.Address32.Address.Minimum;
283		range->max = res->Data.Address32.Address.Maximum;
284		range->offset = res->Data.Address32.Address.TranslationOffset;
285		break;
286	case ACPI_RESOURCE_TYPE_ADDRESS64:
287		range->min = res->Data.Address64.Address.Minimum;
288		range->max = res->Data.Address64.Address.Maximum;
289		range->offset = res->Data.Address64.Address.TranslationOffset;
290		break;
291	default:
292		return AE_OK;
293	}
294	abs->nrange++;
295
296	aprint_debug_dev(sc->sc_dev, "PCI %s [%#lx-%#lx] -> %#lx\n",
297	    range_type, range->min, range->max, range->offset);
298
299	if ((pba->pba_flags & pci_flags) == 0) {
300		abs->bs = *sc->sc_memt;
301		abs->bs.bs_cookie = abs;
302		abs->map = abs->bs.bs_map;
303		abs->flags = pci_flags;
304		abs->bs.bs_map = acpipchb_bus_space_map;
305		if ((pci_flags & PCI_FLAGS_IO_OKAY) != 0) {
306			pba->pba_iot = &abs->bs;
307		} else if ((pci_flags & PCI_FLAGS_MEM_OKAY) != 0) {
308			pba->pba_memt = &abs->bs;
309		}
310		pba->pba_flags |= pci_flags;
311	}
312
313	return AE_OK;
314}
315
316static void
317acpipchb_setup_ranges(struct acpipchb_softc *sc, struct pcibus_attach_args *pba)
318{
319	struct acpipchb_setup_ranges_args args;
320
321	args.sc = sc;
322	args.pba = pba;
323
324	AcpiWalkResources(sc->sc_handle, "_CRS", acpipchb_setup_ranges_cb,
325	    &args);
326}
327
328static void
329acpipchb_setup_quirks(struct acpipchb_softc *sc, struct pcibus_attach_args *pba)
330{
331	struct arm32_pci_chipset *md_pc =
332	    (struct arm32_pci_chipset *)pba->pba_pc;
333	struct acpi_pci_context *ap = md_pc->pc_conf_v;
334
335	pba->pba_flags &= ~ap->ap_pciflags_clear;
336}
337