1244195Sgonzo/*-
2330897Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3330897Seadler *
4330897Seadler * Copyright (c) 2012-2017 Oleksandr Tymoshenko <gonzo@bluezbox.com>
5244195Sgonzo * All rights reserved.
6244195Sgonzo *
7244195Sgonzo * Redistribution and use in source and binary forms, with or without
8244195Sgonzo * modification, are permitted provided that the following conditions
9244195Sgonzo * are met:
10244195Sgonzo * 1. Redistributions of source code must retain the above copyright
11244195Sgonzo *    notice, this list of conditions and the following disclaimer.
12244195Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
13244195Sgonzo *    notice, this list of conditions and the following disclaimer in the
14244195Sgonzo *    documentation and/or other materials provided with the distribution.
15244195Sgonzo *
16244195Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17244195Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18244195Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19244195Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20244195Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21244195Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22244195Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23244195Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24244195Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25244195Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26244195Sgonzo * SUCH DAMAGE.
27244195Sgonzo */
28244195Sgonzo
29244195Sgonzo#include <sys/cdefs.h>
30244195Sgonzo__FBSDID("$FreeBSD: stable/11/sys/arm/arm/pl190.c 330897 2018-03-14 03:19:51Z eadler $");
31244195Sgonzo
32244195Sgonzo#include <sys/param.h>
33244195Sgonzo#include <sys/systm.h>
34244195Sgonzo#include <sys/bus.h>
35244195Sgonzo#include <sys/kernel.h>
36244195Sgonzo#include <sys/ktr.h>
37244195Sgonzo#include <sys/module.h>
38330897Seadler#include <sys/proc.h>
39244195Sgonzo#include <sys/rman.h>
40244195Sgonzo#include <machine/bus.h>
41244195Sgonzo#include <machine/intr.h>
42244195Sgonzo
43244195Sgonzo#include <dev/ofw/openfirm.h>
44244195Sgonzo#include <dev/ofw/ofw_bus.h>
45244195Sgonzo#include <dev/ofw/ofw_bus_subr.h>
46244195Sgonzo
47330897Seadler#include "pic_if.h"
48330897Seadler
49244195Sgonzo#ifdef  DEBUG
50244195Sgonzo#define dprintf(fmt, args...) printf(fmt, ##args)
51244195Sgonzo#else
52244195Sgonzo#define dprintf(fmt, args...)
53244195Sgonzo#endif
54244195Sgonzo
55244195Sgonzo#define	VICIRQSTATUS	0x000
56244195Sgonzo#define	VICFIQSTATUS	0x004
57244195Sgonzo#define	VICRAWINTR	0x008
58244195Sgonzo#define	VICINTSELECT	0x00C
59244195Sgonzo#define	VICINTENABLE	0x010
60244195Sgonzo#define	VICINTENCLEAR	0x014
61244195Sgonzo#define	VICSOFTINT	0x018
62244195Sgonzo#define	VICSOFTINTCLEAR	0x01C
63244195Sgonzo#define	VICPROTECTION	0x020
64244195Sgonzo#define	VICPERIPHID	0xFE0
65244195Sgonzo#define	VICPRIMECELLID	0xFF0
66244195Sgonzo
67244195Sgonzo#define	VIC_NIRQS	32
68244195Sgonzo
69330897Seadlerstruct pl190_intc_irqsrc {
70330897Seadler	struct intr_irqsrc		isrc;
71330897Seadler	u_int				irq;
72330897Seadler};
73330897Seadler
74244195Sgonzostruct pl190_intc_softc {
75330897Seadler	device_t		dev;
76330897Seadler	struct mtx		mtx;
77244195Sgonzo	struct resource *	intc_res;
78330897Seadler	struct pl190_intc_irqsrc	isrcs[VIC_NIRQS];
79244195Sgonzo};
80244195Sgonzo
81330897Seadler#define	INTC_VIC_READ_4(sc, reg)		\
82330897Seadler    bus_read_4(sc->intc_res, (reg))
83330897Seadler#define	INTC_VIC_WRITE_4(sc, reg, val)		\
84330897Seadler    bus_write_4(sc->intc_res, (reg), (val))
85244195Sgonzo
86330897Seadler#define	VIC_LOCK(_sc) mtx_lock_spin(&(_sc)->mtx)
87330897Seadler#define	VIC_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->mtx)
88244195Sgonzo
89330897Seadlerstatic inline void
90330897Seadlerpl190_intc_irq_dispatch(struct pl190_intc_softc *sc, u_int irq,
91330897Seadler    struct trapframe *tf)
92330897Seadler{
93330897Seadler	struct pl190_intc_irqsrc *src;
94330897Seadler
95330897Seadler	src = &sc->isrcs[irq];
96330897Seadler	if (intr_isrc_dispatch(&src->isrc, tf) != 0)
97330897Seadler		device_printf(sc->dev, "Stray irq %u detected\n", irq);
98330897Seadler}
99330897Seadler
100244195Sgonzostatic int
101330897Seadlerpl190_intc_intr(void *arg)
102330897Seadler{
103330897Seadler	struct pl190_intc_softc *sc;
104330897Seadler	u_int cpu;
105330897Seadler	uint32_t num, pending;
106330897Seadler	struct trapframe *tf;
107330897Seadler
108330897Seadler	sc = arg;
109330897Seadler	cpu = PCPU_GET(cpuid);
110330897Seadler	tf = curthread->td_intr_frame;
111330897Seadler
112330897Seadler	VIC_LOCK(sc);
113330897Seadler	pending = INTC_VIC_READ_4(sc, VICIRQSTATUS);
114330897Seadler	VIC_UNLOCK(sc);
115330897Seadler	for (num = 0 ; num < VIC_NIRQS; num++) {
116330897Seadler		if (pending & (1 << num))
117330897Seadler			pl190_intc_irq_dispatch(sc, num, tf);
118330897Seadler	}
119330897Seadler
120330897Seadler	return (FILTER_HANDLED);
121330897Seadler}
122330897Seadler
123330897Seadlerstatic void
124330897Seadlerpl190_intc_disable_intr(device_t dev, struct intr_irqsrc *isrc)
125330897Seadler{
126330897Seadler	struct pl190_intc_softc *sc;
127330897Seadler	struct pl190_intc_irqsrc *src;
128330897Seadler
129330897Seadler	sc = device_get_softc(dev);
130330897Seadler	src = (struct pl190_intc_irqsrc *)isrc;
131330897Seadler
132330897Seadler	VIC_LOCK(sc);
133330897Seadler	INTC_VIC_WRITE_4(sc, VICINTENCLEAR, (1 << src->irq));
134330897Seadler	VIC_UNLOCK(sc);
135330897Seadler}
136330897Seadler
137330897Seadlerstatic void
138330897Seadlerpl190_intc_enable_intr(device_t dev, struct intr_irqsrc *isrc)
139330897Seadler{
140330897Seadler	struct pl190_intc_softc *sc;
141330897Seadler	struct pl190_intc_irqsrc *src;
142330897Seadler
143330897Seadler	sc = device_get_softc(dev);
144330897Seadler	src = (struct pl190_intc_irqsrc *)isrc;
145330897Seadler
146330897Seadler	VIC_LOCK(sc);
147330897Seadler	INTC_VIC_WRITE_4(sc, VICINTENABLE, (1 << src->irq));
148330897Seadler	VIC_UNLOCK(sc);
149330897Seadler}
150330897Seadler
151330897Seadlerstatic int
152330897Seadlerpl190_intc_map_intr(device_t dev, struct intr_map_data *data,
153330897Seadler    struct intr_irqsrc **isrcp)
154330897Seadler{
155330897Seadler	struct intr_map_data_fdt *daf;
156330897Seadler	struct pl190_intc_softc *sc;
157330897Seadler
158330897Seadler	if (data->type != INTR_MAP_DATA_FDT)
159330897Seadler		return (ENOTSUP);
160330897Seadler
161330897Seadler	daf = (struct intr_map_data_fdt *)data;
162330897Seadler	if (daf->ncells != 1 || daf->cells[0] >= VIC_NIRQS)
163330897Seadler		return (EINVAL);
164330897Seadler
165330897Seadler	sc = device_get_softc(dev);
166330897Seadler	*isrcp = &sc->isrcs[daf->cells[0]].isrc;
167330897Seadler	return (0);
168330897Seadler}
169330897Seadler
170330897Seadlerstatic void
171330897Seadlerpl190_intc_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
172330897Seadler{
173330897Seadler	pl190_intc_disable_intr(dev, isrc);
174330897Seadler}
175330897Seadler
176330897Seadlerstatic void
177330897Seadlerpl190_intc_post_ithread(device_t dev, struct intr_irqsrc *isrc)
178330897Seadler{
179330897Seadler	struct pl190_intc_irqsrc *src;
180330897Seadler
181330897Seadler	src = (struct pl190_intc_irqsrc *)isrc;
182330897Seadler	pl190_intc_enable_intr(dev, isrc);
183330897Seadler	arm_irq_memory_barrier(src->irq);
184330897Seadler}
185330897Seadler
186330897Seadlerstatic void
187330897Seadlerpl190_intc_post_filter(device_t dev, struct intr_irqsrc *isrc)
188330897Seadler{
189330897Seadler	struct pl190_intc_irqsrc *src;
190330897Seadler
191330897Seadler	src = (struct pl190_intc_irqsrc *)isrc;
192330897Seadler	arm_irq_memory_barrier(src->irq);
193330897Seadler}
194330897Seadler
195330897Seadlerstatic int
196330897Seadlerpl190_intc_setup_intr(device_t dev, struct intr_irqsrc *isrc,
197330897Seadler    struct resource *res, struct intr_map_data *data)
198330897Seadler{
199330897Seadler
200330897Seadler	return (0);
201330897Seadler}
202330897Seadler
203330897Seadlerstatic int
204244195Sgonzopl190_intc_probe(device_t dev)
205244195Sgonzo{
206261410Sian
207261410Sian	if (!ofw_bus_status_okay(dev))
208261410Sian		return (ENXIO);
209261410Sian
210244195Sgonzo	if (!ofw_bus_is_compatible(dev, "arm,versatile-vic"))
211244195Sgonzo		return (ENXIO);
212244195Sgonzo	device_set_desc(dev, "ARM PL190 VIC");
213244195Sgonzo	return (BUS_PROBE_DEFAULT);
214244195Sgonzo}
215244195Sgonzo
216244195Sgonzostatic int
217244195Sgonzopl190_intc_attach(device_t dev)
218244195Sgonzo{
219330897Seadler	struct		pl190_intc_softc *sc;
220244195Sgonzo	uint32_t	id;
221244195Sgonzo	int		i, rid;
222330897Seadler	struct		pl190_intc_irqsrc *isrcs;
223330897Seadler	struct intr_pic *pic;
224330897Seadler	int		error;
225330897Seadler	uint32_t	irq;
226330897Seadler	const char	*name;
227330897Seadler	phandle_t	xref;
228244195Sgonzo
229330897Seadler	sc = device_get_softc(dev);
230330897Seadler	sc->dev = dev;
231330897Seadler	mtx_init(&sc->mtx, device_get_nameunit(dev), "pl190",
232330897Seadler	    MTX_SPIN);
233244195Sgonzo
234244195Sgonzo	/* Request memory resources */
235244195Sgonzo	rid = 0;
236244195Sgonzo	sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
237244195Sgonzo	    RF_ACTIVE);
238244195Sgonzo	if (sc->intc_res == NULL) {
239244195Sgonzo		device_printf(dev, "Error: could not allocate memory resources\n");
240244195Sgonzo		return (ENXIO);
241244195Sgonzo	}
242244195Sgonzo
243244195Sgonzo	/*
244244195Sgonzo	 * All interrupts should use IRQ line
245244195Sgonzo	 */
246330897Seadler	INTC_VIC_WRITE_4(sc, VICINTSELECT, 0x00000000);
247244195Sgonzo	/* Disable all interrupts */
248330897Seadler	INTC_VIC_WRITE_4(sc, VICINTENCLEAR, 0xffffffff);
249244195Sgonzo
250244195Sgonzo	id = 0;
251244195Sgonzo	for (i = 3; i >= 0; i--) {
252283366Sandrew		id = (id << 8) |
253330897Seadler		     (INTC_VIC_READ_4(sc, VICPERIPHID + i*4) & 0xff);
254244195Sgonzo	}
255244195Sgonzo
256244195Sgonzo	device_printf(dev, "Peripheral ID: %08x\n", id);
257244195Sgonzo
258244195Sgonzo	id = 0;
259244195Sgonzo	for (i = 3; i >= 0; i--) {
260283366Sandrew		id = (id << 8) |
261330897Seadler		     (INTC_VIC_READ_4(sc, VICPRIMECELLID + i*4) & 0xff);
262244195Sgonzo	}
263244195Sgonzo
264244195Sgonzo	device_printf(dev, "PrimeCell ID: %08x\n", id);
265244195Sgonzo
266330897Seadler	/* PIC attachment */
267330897Seadler	isrcs = sc->isrcs;
268330897Seadler	name = device_get_nameunit(sc->dev);
269330897Seadler	for (irq = 0; irq < VIC_NIRQS; irq++) {
270330897Seadler		isrcs[irq].irq = irq;
271330897Seadler		error = intr_isrc_register(&isrcs[irq].isrc, sc->dev,
272330897Seadler		    0, "%s,%u", name, irq);
273330897Seadler		if (error != 0)
274330897Seadler			return (error);
275330897Seadler	}
276330897Seadler
277330897Seadler	xref = OF_xref_from_node(ofw_bus_get_node(sc->dev));
278330897Seadler	pic = intr_pic_register(sc->dev, xref);
279330897Seadler	if (pic == NULL)
280330897Seadler		return (ENXIO);
281330897Seadler
282330897Seadler	return (intr_pic_claim_root(sc->dev, xref, pl190_intc_intr, sc, 0));
283244195Sgonzo}
284244195Sgonzo
285244195Sgonzostatic device_method_t pl190_intc_methods[] = {
286244195Sgonzo	DEVMETHOD(device_probe,		pl190_intc_probe),
287244195Sgonzo	DEVMETHOD(device_attach,	pl190_intc_attach),
288330897Seadler
289330897Seadler	DEVMETHOD(pic_disable_intr,	pl190_intc_disable_intr),
290330897Seadler	DEVMETHOD(pic_enable_intr,	pl190_intc_enable_intr),
291330897Seadler	DEVMETHOD(pic_map_intr,		pl190_intc_map_intr),
292330897Seadler	DEVMETHOD(pic_post_filter,	pl190_intc_post_filter),
293330897Seadler	DEVMETHOD(pic_post_ithread,	pl190_intc_post_ithread),
294330897Seadler	DEVMETHOD(pic_pre_ithread,	pl190_intc_pre_ithread),
295330897Seadler	DEVMETHOD(pic_setup_intr,	pl190_intc_setup_intr),
296330897Seadler
297330897Seadler	DEVMETHOD_END
298244195Sgonzo};
299244195Sgonzo
300244195Sgonzostatic driver_t pl190_intc_driver = {
301244195Sgonzo	"intc",
302244195Sgonzo	pl190_intc_methods,
303244195Sgonzo	sizeof(struct pl190_intc_softc),
304244195Sgonzo};
305244195Sgonzo
306244195Sgonzostatic devclass_t pl190_intc_devclass;
307244195Sgonzo
308283366SandrewEARLY_DRIVER_MODULE(intc, simplebus, pl190_intc_driver, pl190_intc_devclass,
309269605Sian    0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
310