1/*-
2 * Copyright (c) 2011,2016 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Andrew Turner under
6 * sponsorship from the FreeBSD Foundation.
7 *
8 * Developed by Damjan Marion <damjan.marion@gmail.com>
9 *
10 * Based on OMAP4 GIC code by Ben Gray
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. The name of the company nor the name of the author may be used to
21 *    endorse or promote products derived from this software without specific
22 *    prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#include "opt_acpi.h"
38#include "opt_platform.h"
39
40#include <sys/cdefs.h>
41__FBSDID("$FreeBSD$");
42
43#include <sys/param.h>
44#include <sys/systm.h>
45#include <sys/bus.h>
46#include <sys/kernel.h>
47#include <sys/malloc.h>
48#include <sys/module.h>
49
50#include <machine/intr.h>
51
52#include <contrib/dev/acpica/include/acpi.h>
53#include <dev/acpica/acpivar.h>
54
55#include <arm/arm/gic.h>
56#include <arm/arm/gic_common.h>
57
58struct gic_acpi_devinfo {
59	struct resource_list	rl;
60};
61
62static device_identify_t gic_acpi_identify;
63static device_probe_t gic_acpi_probe;
64static device_attach_t gic_acpi_attach;
65static bus_get_resource_list_t gic_acpi_get_resource_list;
66static bool arm_gic_add_children(device_t);
67
68static device_method_t gic_acpi_methods[] = {
69	/* Device interface */
70	DEVMETHOD(device_identify,	gic_acpi_identify),
71	DEVMETHOD(device_probe,		gic_acpi_probe),
72	DEVMETHOD(device_attach,	gic_acpi_attach),
73
74	/* Bus interface */
75	DEVMETHOD(bus_get_resource_list, gic_acpi_get_resource_list),
76
77	DEVMETHOD_END,
78};
79
80DEFINE_CLASS_1(gic, gic_acpi_driver, gic_acpi_methods,
81    sizeof(struct arm_gic_softc), arm_gic_driver);
82
83static devclass_t gic_acpi_devclass;
84
85EARLY_DRIVER_MODULE(gic, acpi, gic_acpi_driver, gic_acpi_devclass, 0, 0,
86    BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
87
88struct madt_table_data {
89	device_t parent;
90	ACPI_MADT_GENERIC_DISTRIBUTOR *dist;
91	ACPI_MADT_GENERIC_INTERRUPT *intr[MAXCPU];
92};
93
94static void
95madt_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
96{
97	struct madt_table_data *madt_data;
98	ACPI_MADT_GENERIC_INTERRUPT *intr;
99
100	madt_data = (struct madt_table_data *)arg;
101
102	switch(entry->Type) {
103	case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR:
104		if (madt_data->dist != NULL) {
105			if (bootverbose)
106				device_printf(madt_data->parent,
107				    "gic: Already have a distributor table");
108		} else
109			madt_data->dist =
110			    (ACPI_MADT_GENERIC_DISTRIBUTOR *)entry;
111		break;
112	case ACPI_MADT_TYPE_GENERIC_INTERRUPT:
113		intr = (ACPI_MADT_GENERIC_INTERRUPT *)entry;
114		if (intr->CpuInterfaceNumber < MAXCPU)
115			madt_data->intr[intr->CpuInterfaceNumber] = intr;
116		break;
117	}
118}
119
120static void
121gic_acpi_identify(driver_t *driver, device_t parent)
122{
123	struct madt_table_data madt_data;
124	ACPI_MADT_GENERIC_INTERRUPT *intr;
125	ACPI_TABLE_MADT *madt;
126	vm_paddr_t physaddr;
127	device_t dev;
128	int i;
129
130	physaddr = acpi_find_table(ACPI_SIG_MADT);
131	if (physaddr == 0)
132		return;
133
134	madt = acpi_map_table(physaddr, ACPI_SIG_MADT);
135	if (madt == NULL) {
136		device_printf(parent, "gic: Unable to map the MADT\n");
137		return;
138	}
139
140	bzero(&madt_data, sizeof(madt_data));
141	madt_data.parent = parent;
142	madt_data.dist = NULL;
143
144	acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
145	    madt_handler, &madt_data);
146
147	/* Check the version of the GIC we have */
148	switch (madt_data.dist->Version) {
149	case ACPI_MADT_GIC_VERSION_NONE:
150	case ACPI_MADT_GIC_VERSION_V1:
151	case ACPI_MADT_GIC_VERSION_V2:
152		break;
153	default:
154		goto out;
155	}
156
157	intr = NULL;
158	for (i = 0; i < MAXCPU; i++) {
159		if (madt_data.intr[i] != NULL) {
160			if (intr == NULL) {
161				intr = madt_data.intr[i];
162			} else if (intr->BaseAddress !=
163			    madt_data.intr[i]->BaseAddress) {
164				device_printf(parent,
165"gic: Not all CPU interfaces at the same address, this may fail\n");
166			}
167		}
168	}
169	if (intr == NULL) {
170		device_printf(parent, "gic: No CPU interfaces found\n");
171		goto out;
172	}
173
174	dev = BUS_ADD_CHILD(parent, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE,
175	    "gic", -1);
176	if (dev == NULL) {
177		device_printf(parent, "add gic child failed\n");
178		goto out;
179	}
180
181	BUS_SET_RESOURCE(parent, dev, SYS_RES_MEMORY, 0,
182	    madt_data.dist->BaseAddress, 4 * 1024);
183	BUS_SET_RESOURCE(parent, dev, SYS_RES_MEMORY, 1,
184	    intr->BaseAddress, 4 * 1024);
185
186	acpi_set_private(dev, (void *)(uintptr_t)madt_data.dist->Version);
187out:
188	acpi_unmap_table(madt);
189}
190
191static int
192gic_acpi_probe(device_t dev)
193{
194
195	switch((uintptr_t)acpi_get_private(dev)) {
196	case ACPI_MADT_GIC_VERSION_NONE:
197	case ACPI_MADT_GIC_VERSION_V1:
198	case ACPI_MADT_GIC_VERSION_V2:
199		break;
200	default:
201		return (ENXIO);
202	}
203
204	device_set_desc(dev, "ARM Generic Interrupt Controller");
205	return (BUS_PROBE_NOWILDCARD);
206}
207
208static int
209gic_acpi_attach(device_t dev)
210{
211	struct arm_gic_softc *sc = device_get_softc(dev);
212	intptr_t xref;
213	int err;
214
215	sc->gic_bus = GIC_BUS_ACPI;
216
217	err = arm_gic_attach(dev);
218	if (err != 0)
219		return (err);
220
221	xref = ACPI_INTR_XREF;
222
223	/*
224	 * Now, when everything is initialized, it's right time to
225	 * register interrupt controller to interrupt framefork.
226	 */
227	if (intr_pic_register(dev, xref) == NULL) {
228		device_printf(dev, "could not register PIC\n");
229		goto cleanup;
230	}
231
232	/*
233	 * Controller is root:
234	 */
235	if (intr_pic_claim_root(dev, xref, arm_gic_intr, sc,
236	    GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) {
237		device_printf(dev, "could not set PIC as a root\n");
238		intr_pic_deregister(dev, xref);
239		goto cleanup;
240	}
241	/* If we have children probe and attach them */
242	if (arm_gic_add_children(dev)) {
243		bus_generic_probe(dev);
244		return (bus_generic_attach(dev));
245	}
246
247	return (0);
248
249cleanup:
250	arm_gic_detach(dev);
251	return(ENXIO);
252}
253
254static struct resource_list *
255gic_acpi_get_resource_list(device_t bus, device_t child)
256{
257	struct gic_acpi_devinfo *di;
258
259	di = device_get_ivars(child);
260	KASSERT(di != NULL, ("gic_acpi_get_resource_list: No devinfo"));
261
262	return (&di->rl);
263}
264
265static void
266madt_gicv2m_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
267{
268	struct arm_gic_softc *sc;
269	ACPI_MADT_GENERIC_MSI_FRAME *msi;
270	struct gic_acpi_devinfo *dinfo;
271	device_t dev, cdev;
272
273	if (entry->Type == ACPI_MADT_TYPE_GENERIC_MSI_FRAME) {
274		sc = arg;
275		dev = sc->gic_dev;
276		msi = (ACPI_MADT_GENERIC_MSI_FRAME *)entry;
277
278		device_printf(dev, "frame: %x %lx %x %u %u\n", msi->MsiFrameId,
279		    msi->BaseAddress, msi->Flags, msi->SpiCount, msi->SpiBase);
280
281		cdev = device_add_child(dev, NULL, -1);
282		if (cdev == NULL)
283			return;
284
285		dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
286		resource_list_init(&dinfo->rl);
287		resource_list_add(&dinfo->rl, SYS_RES_MEMORY, 0,
288		    msi->BaseAddress, msi->BaseAddress + PAGE_SIZE - 1,
289		    PAGE_SIZE);
290		device_set_ivars(cdev, dinfo);
291	}
292}
293
294static bool
295arm_gic_add_children(device_t dev)
296{
297	struct arm_gic_softc *sc = device_get_softc(dev);
298	ACPI_TABLE_MADT *madt;
299	vm_paddr_t physaddr;
300
301	/* This should return a valid address as it did in gic_acpi_identify */
302	physaddr = acpi_find_table(ACPI_SIG_MADT);
303	if (physaddr == 0)
304		return (false);
305
306	madt = acpi_map_table(physaddr, ACPI_SIG_MADT);
307	if (madt == NULL) {
308		device_printf(dev, "gic: Unable to map the MADT\n");
309		return (false);
310	}
311
312	acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
313	    madt_gicv2m_handler, sc);
314
315	acpi_unmap_table(madt);
316
317	return (true);
318}
319
320static int
321arm_gicv2m_acpi_probe(device_t dev)
322{
323
324	if (gic_get_bus(dev) != GIC_BUS_ACPI)
325		return (EINVAL);
326
327	if (gic_get_hw_rev(dev) > 2)
328		return (EINVAL);
329
330	device_set_desc(dev, "ARM Generic Interrupt Controller MSI/MSIX");
331	return (BUS_PROBE_DEFAULT);
332}
333
334static int
335arm_gicv2m_acpi_attach(device_t dev)
336{
337	struct arm_gicv2m_softc *sc;
338
339	sc = device_get_softc(dev);
340	sc->sc_xref = ACPI_MSI_XREF;
341
342	return (arm_gicv2m_attach(dev));
343}
344
345static device_method_t arm_gicv2m_acpi_methods[] = {
346	/* Device interface */
347	DEVMETHOD(device_probe,		arm_gicv2m_acpi_probe),
348	DEVMETHOD(device_attach,	arm_gicv2m_acpi_attach),
349
350	/* End */
351	DEVMETHOD_END
352};
353
354DEFINE_CLASS_1(gicv2m, arm_gicv2m_acpi_driver, arm_gicv2m_acpi_methods,
355    sizeof(struct arm_gicv2m_softc), arm_gicv2m_driver);
356
357static devclass_t arm_gicv2m_acpi_devclass;
358
359EARLY_DRIVER_MODULE(gicv2m_acpi, gic, arm_gicv2m_acpi_driver,
360    arm_gicv2m_acpi_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
361