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/param.h>
41#include <sys/systm.h>
42#include <sys/bus.h>
43#include <sys/kernel.h>
44#include <sys/malloc.h>
45#include <sys/module.h>
46
47#include <machine/intr.h>
48
49#include <contrib/dev/acpica/include/acpi.h>
50#include <dev/acpica/acpivar.h>
51
52#include <arm/arm/gic.h>
53#include <arm/arm/gic_common.h>
54
55struct gic_acpi_devinfo {
56	struct resource_list	rl;
57};
58
59static device_identify_t gic_acpi_identify;
60static device_probe_t gic_acpi_probe;
61static device_attach_t gic_acpi_attach;
62static bus_get_resource_list_t gic_acpi_get_resource_list;
63static bool arm_gic_add_children(device_t);
64
65static device_method_t gic_acpi_methods[] = {
66	/* Device interface */
67	DEVMETHOD(device_identify,	gic_acpi_identify),
68	DEVMETHOD(device_probe,		gic_acpi_probe),
69	DEVMETHOD(device_attach,	gic_acpi_attach),
70
71	/* Bus interface */
72	DEVMETHOD(bus_get_resource_list, gic_acpi_get_resource_list),
73
74	DEVMETHOD_END,
75};
76
77DEFINE_CLASS_1(gic, gic_acpi_driver, gic_acpi_methods,
78    sizeof(struct arm_gic_softc), arm_gic_driver);
79
80EARLY_DRIVER_MODULE(gic, acpi, gic_acpi_driver, 0, 0,
81    BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
82
83struct madt_table_data {
84	device_t parent;
85	ACPI_MADT_GENERIC_DISTRIBUTOR *dist;
86	ACPI_MADT_GENERIC_INTERRUPT *intr[GIC_MAXCPU];
87};
88
89static void
90madt_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
91{
92	struct madt_table_data *madt_data;
93	ACPI_MADT_GENERIC_INTERRUPT *intr;
94
95	madt_data = (struct madt_table_data *)arg;
96
97	switch(entry->Type) {
98	case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR:
99		if (madt_data->dist != NULL) {
100			if (bootverbose)
101				device_printf(madt_data->parent,
102				    "gic: Already have a distributor table");
103		} else
104			madt_data->dist =
105			    (ACPI_MADT_GENERIC_DISTRIBUTOR *)entry;
106		break;
107	case ACPI_MADT_TYPE_GENERIC_INTERRUPT:
108		intr = (ACPI_MADT_GENERIC_INTERRUPT *)entry;
109		if (intr->CpuInterfaceNumber < GIC_MAXCPU)
110			madt_data->intr[intr->CpuInterfaceNumber] = intr;
111		break;
112	}
113}
114
115static void
116gic_acpi_identify(driver_t *driver, device_t parent)
117{
118	struct madt_table_data madt_data;
119	ACPI_MADT_GENERIC_INTERRUPT *intr;
120	ACPI_TABLE_MADT *madt;
121	vm_paddr_t physaddr;
122	device_t dev;
123	int i;
124
125	physaddr = acpi_find_table(ACPI_SIG_MADT);
126	if (physaddr == 0)
127		return;
128
129	madt = acpi_map_table(physaddr, ACPI_SIG_MADT);
130	if (madt == NULL) {
131		device_printf(parent, "gic: Unable to map the MADT\n");
132		return;
133	}
134
135	bzero(&madt_data, sizeof(madt_data));
136	madt_data.parent = parent;
137	madt_data.dist = NULL;
138
139	acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
140	    madt_handler, &madt_data);
141
142	/* Check the version of the GIC we have */
143	switch (madt_data.dist->Version) {
144	case ACPI_MADT_GIC_VERSION_NONE:
145	case ACPI_MADT_GIC_VERSION_V1:
146	case ACPI_MADT_GIC_VERSION_V2:
147		break;
148	default:
149		goto out;
150	}
151
152	intr = NULL;
153	for (i = 0; i < GIC_MAXCPU; i++) {
154		if (madt_data.intr[i] != NULL) {
155			if (intr == NULL) {
156				intr = madt_data.intr[i];
157			} else if (intr->BaseAddress !=
158			    madt_data.intr[i]->BaseAddress) {
159				device_printf(parent,
160"gic: Not all CPU interfaces at the same address, this may fail\n");
161			}
162		}
163	}
164	if (intr == NULL) {
165		device_printf(parent, "gic: No CPU interfaces found\n");
166		goto out;
167	}
168
169	dev = BUS_ADD_CHILD(parent, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE,
170	    "gic", -1);
171	if (dev == NULL) {
172		device_printf(parent, "add gic child failed\n");
173		goto out;
174	}
175
176	BUS_SET_RESOURCE(parent, dev, SYS_RES_MEMORY, 0,
177	    madt_data.dist->BaseAddress, 4 * 1024);
178	BUS_SET_RESOURCE(parent, dev, SYS_RES_MEMORY, 1,
179	    intr->BaseAddress, 4 * 1024);
180
181	acpi_set_private(dev, (void *)(uintptr_t)madt_data.dist->Version);
182out:
183	acpi_unmap_table(madt);
184}
185
186static int
187gic_acpi_probe(device_t dev)
188{
189
190	switch((uintptr_t)acpi_get_private(dev)) {
191	case ACPI_MADT_GIC_VERSION_NONE:
192	case ACPI_MADT_GIC_VERSION_V1:
193	case ACPI_MADT_GIC_VERSION_V2:
194		break;
195	default:
196		return (ENXIO);
197	}
198
199	device_set_desc(dev, "ARM Generic Interrupt Controller");
200	return (BUS_PROBE_NOWILDCARD);
201}
202
203static int
204gic_acpi_attach(device_t dev)
205{
206	struct arm_gic_softc *sc = device_get_softc(dev);
207	intptr_t xref;
208	int err;
209
210	sc->gic_bus = GIC_BUS_ACPI;
211
212	err = arm_gic_attach(dev);
213	if (err != 0)
214		return (err);
215
216	xref = ACPI_INTR_XREF;
217
218	/*
219	 * Now, when everything is initialized, it's right time to
220	 * register interrupt controller to interrupt framefork.
221	 */
222	if (intr_pic_register(dev, xref) == NULL) {
223		device_printf(dev, "could not register PIC\n");
224		goto cleanup;
225	}
226
227	/*
228	 * Controller is root:
229	 */
230	if (intr_pic_claim_root(dev, xref, arm_gic_intr, sc) != 0) {
231		device_printf(dev, "could not set PIC as a root\n");
232		intr_pic_deregister(dev, xref);
233		goto cleanup;
234	}
235
236#ifdef SMP
237	if (intr_ipi_pic_register(dev, 0) != 0) {
238		device_printf(dev, "could not register for IPIs\n");
239		goto cleanup;
240	}
241#endif
242
243	/* If we have children probe and attach them */
244	if (arm_gic_add_children(dev)) {
245		bus_generic_probe(dev);
246		return (bus_generic_attach(dev));
247	}
248
249	return (0);
250
251cleanup:
252	arm_gic_detach(dev);
253	return(ENXIO);
254}
255
256static struct resource_list *
257gic_acpi_get_resource_list(device_t bus, device_t child)
258{
259	struct gic_acpi_devinfo *di;
260
261	di = device_get_ivars(child);
262	KASSERT(di != NULL, ("gic_acpi_get_resource_list: No devinfo"));
263
264	return (&di->rl);
265}
266
267static void
268madt_gicv2m_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
269{
270	struct arm_gic_softc *sc;
271	ACPI_MADT_GENERIC_MSI_FRAME *msi;
272	struct gic_acpi_devinfo *dinfo;
273	device_t dev, cdev;
274
275	if (entry->Type == ACPI_MADT_TYPE_GENERIC_MSI_FRAME) {
276		sc = arg;
277		dev = sc->gic_dev;
278		msi = (ACPI_MADT_GENERIC_MSI_FRAME *)entry;
279
280		device_printf(dev, "frame: %x %lx %x %u %u\n", msi->MsiFrameId,
281		    msi->BaseAddress, msi->Flags, msi->SpiCount, msi->SpiBase);
282
283		cdev = device_add_child(dev, NULL, -1);
284		if (cdev == NULL)
285			return;
286
287		dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
288		resource_list_init(&dinfo->rl);
289		resource_list_add(&dinfo->rl, SYS_RES_MEMORY, 0,
290		    msi->BaseAddress, msi->BaseAddress + PAGE_SIZE - 1,
291		    PAGE_SIZE);
292		device_set_ivars(cdev, dinfo);
293	}
294}
295
296static bool
297arm_gic_add_children(device_t dev)
298{
299	struct arm_gic_softc *sc = device_get_softc(dev);
300	ACPI_TABLE_MADT *madt;
301	vm_paddr_t physaddr;
302
303	/* This should return a valid address as it did in gic_acpi_identify */
304	physaddr = acpi_find_table(ACPI_SIG_MADT);
305	if (physaddr == 0)
306		return (false);
307
308	madt = acpi_map_table(physaddr, ACPI_SIG_MADT);
309	if (madt == NULL) {
310		device_printf(dev, "gic: Unable to map the MADT\n");
311		return (false);
312	}
313
314	acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
315	    madt_gicv2m_handler, sc);
316
317	acpi_unmap_table(madt);
318
319	return (true);
320}
321
322static int
323arm_gicv2m_acpi_probe(device_t dev)
324{
325
326	if (gic_get_bus(dev) != GIC_BUS_ACPI)
327		return (EINVAL);
328
329	if (gic_get_hw_rev(dev) > 2)
330		return (EINVAL);
331
332	device_set_desc(dev, "ARM Generic Interrupt Controller MSI/MSIX");
333	return (BUS_PROBE_DEFAULT);
334}
335
336static int
337arm_gicv2m_acpi_attach(device_t dev)
338{
339	struct arm_gicv2m_softc *sc;
340
341	sc = device_get_softc(dev);
342	sc->sc_xref = ACPI_MSI_XREF;
343
344	return (arm_gicv2m_attach(dev));
345}
346
347static device_method_t arm_gicv2m_acpi_methods[] = {
348	/* Device interface */
349	DEVMETHOD(device_probe,		arm_gicv2m_acpi_probe),
350	DEVMETHOD(device_attach,	arm_gicv2m_acpi_attach),
351
352	/* End */
353	DEVMETHOD_END
354};
355
356DEFINE_CLASS_1(gicv2m, arm_gicv2m_acpi_driver, arm_gicv2m_acpi_methods,
357    sizeof(struct arm_gicv2m_softc), arm_gicv2m_driver);
358
359EARLY_DRIVER_MODULE(gicv2m_acpi, gic, arm_gicv2m_acpi_driver, 0, 0,
360    BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
361