1239281Sgonzo/*-
2239281Sgonzo * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
3239281Sgonzo * All rights reserved.
4239281Sgonzo *
5239281Sgonzo * Based on OMAP3 INTC code by Ben Gray
6239281Sgonzo *
7239281Sgonzo * Redistribution and use in source and binary forms, with or without
8239281Sgonzo * modification, are permitted provided that the following conditions
9239281Sgonzo * are met:
10239281Sgonzo * 1. Redistributions of source code must retain the above copyright
11239281Sgonzo *    notice, this list of conditions and the following disclaimer.
12239281Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
13239281Sgonzo *    notice, this list of conditions and the following disclaimer in the
14239281Sgonzo *    documentation and/or other materials provided with the distribution.
15239281Sgonzo *
16239281Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17239281Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18239281Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19239281Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20239281Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21239281Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22239281Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23239281Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24239281Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25239281Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26239281Sgonzo * SUCH DAMAGE.
27239281Sgonzo */
28239281Sgonzo
29239281Sgonzo
30239281Sgonzo#include <sys/cdefs.h>
31239281Sgonzo__FBSDID("$FreeBSD: releng/10.2/sys/arm/ti/aintc.c 283329 2015-05-23 21:12:51Z ian $");
32239281Sgonzo
33239281Sgonzo#include <sys/param.h>
34239281Sgonzo#include <sys/systm.h>
35239281Sgonzo#include <sys/bus.h>
36239281Sgonzo#include <sys/kernel.h>
37239281Sgonzo#include <sys/ktr.h>
38239281Sgonzo#include <sys/module.h>
39239281Sgonzo#include <sys/rman.h>
40239281Sgonzo#include <machine/bus.h>
41239281Sgonzo#include <machine/intr.h>
42239281Sgonzo
43239281Sgonzo#include <dev/fdt/fdt_common.h>
44239281Sgonzo#include <dev/ofw/openfirm.h>
45239281Sgonzo#include <dev/ofw/ofw_bus.h>
46239281Sgonzo#include <dev/ofw/ofw_bus_subr.h>
47239281Sgonzo
48239281Sgonzo#define INTC_REVISION		0x00
49239281Sgonzo#define INTC_SYSCONFIG		0x10
50239281Sgonzo#define INTC_SYSSTATUS		0x14
51239281Sgonzo#define INTC_SIR_IRQ		0x40
52239281Sgonzo#define INTC_CONTROL		0x48
53239281Sgonzo#define INTC_THRESHOLD		0x68
54239281Sgonzo#define INTC_MIR_CLEAR(x)	(0x88 + ((x) * 0x20))
55239281Sgonzo#define INTC_MIR_SET(x)		(0x8C + ((x) * 0x20))
56239281Sgonzo#define INTC_ISR_SET(x)		(0x90 + ((x) * 0x20))
57239281Sgonzo#define INTC_ISR_CLEAR(x)	(0x94 + ((x) * 0x20))
58239281Sgonzo
59239281Sgonzostruct ti_aintc_softc {
60239281Sgonzo	device_t		sc_dev;
61239281Sgonzo	struct resource *	aintc_res[3];
62239281Sgonzo	bus_space_tag_t		aintc_bst;
63239281Sgonzo	bus_space_handle_t	aintc_bsh;
64239281Sgonzo	uint8_t			ver;
65239281Sgonzo};
66239281Sgonzo
67239281Sgonzostatic struct resource_spec ti_aintc_spec[] = {
68239281Sgonzo	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
69239281Sgonzo	{ -1, 0 }
70239281Sgonzo};
71239281Sgonzo
72239281Sgonzo
73239281Sgonzostatic struct ti_aintc_softc *ti_aintc_sc = NULL;
74239281Sgonzo
75283329Sian#define	aintc_read_4(_sc, reg)		\
76283329Sian    bus_space_read_4((_sc)->aintc_bst, (_sc)->aintc_bsh, (reg))
77283329Sian#define	aintc_write_4(_sc, reg, val)		\
78283329Sian    bus_space_write_4((_sc)->aintc_bst, (_sc)->aintc_bsh, (reg), (val))
79239281Sgonzo
80239281Sgonzo
81283329Sianstatic void
82283329Sianaintc_post_filter(void *arg)
83283329Sian{
84283329Sian
85283329Sian	arm_irq_memory_barrier(0);
86283329Sian	aintc_write_4(ti_aintc_sc, INTC_CONTROL, 1); /* EOI */
87283329Sian}
88283329Sian
89239281Sgonzostatic int
90239281Sgonzoti_aintc_probe(device_t dev)
91239281Sgonzo{
92266152Sian	if (!ofw_bus_status_okay(dev))
93266152Sian		return (ENXIO);
94266152Sian
95266152Sian
96239281Sgonzo	if (!ofw_bus_is_compatible(dev, "ti,aintc"))
97239281Sgonzo		return (ENXIO);
98239281Sgonzo	device_set_desc(dev, "TI AINTC Interrupt Controller");
99239281Sgonzo	return (BUS_PROBE_DEFAULT);
100239281Sgonzo}
101239281Sgonzo
102239281Sgonzostatic int
103239281Sgonzoti_aintc_attach(device_t dev)
104239281Sgonzo{
105239281Sgonzo	struct		ti_aintc_softc *sc = device_get_softc(dev);
106239281Sgonzo	uint32_t x;
107239281Sgonzo
108239281Sgonzo	sc->sc_dev = dev;
109239281Sgonzo
110239281Sgonzo	if (ti_aintc_sc)
111239281Sgonzo		return (ENXIO);
112239281Sgonzo
113239281Sgonzo	if (bus_alloc_resources(dev, ti_aintc_spec, sc->aintc_res)) {
114239281Sgonzo		device_printf(dev, "could not allocate resources\n");
115239281Sgonzo		return (ENXIO);
116239281Sgonzo	}
117239281Sgonzo
118239281Sgonzo	sc->aintc_bst = rman_get_bustag(sc->aintc_res[0]);
119239281Sgonzo	sc->aintc_bsh = rman_get_bushandle(sc->aintc_res[0]);
120239281Sgonzo
121239281Sgonzo	ti_aintc_sc = sc;
122239281Sgonzo
123283329Sian	x = aintc_read_4(sc, INTC_REVISION);
124239281Sgonzo	device_printf(dev, "Revision %u.%u\n",(x >> 4) & 0xF, x & 0xF);
125239281Sgonzo
126239281Sgonzo	/* SoftReset */
127283329Sian	aintc_write_4(sc, INTC_SYSCONFIG, 2);
128239281Sgonzo
129239281Sgonzo	/* Wait for reset to complete */
130283329Sian	while(!(aintc_read_4(sc, INTC_SYSSTATUS) & 1));
131239281Sgonzo
132239281Sgonzo	/*Set Priority Threshold */
133283329Sian	aintc_write_4(sc, INTC_THRESHOLD, 0xFF);
134239281Sgonzo
135283329Sian	arm_post_filter = aintc_post_filter;
136283329Sian
137239281Sgonzo	return (0);
138239281Sgonzo}
139239281Sgonzo
140239281Sgonzostatic device_method_t ti_aintc_methods[] = {
141239281Sgonzo	DEVMETHOD(device_probe,		ti_aintc_probe),
142239281Sgonzo	DEVMETHOD(device_attach,	ti_aintc_attach),
143239281Sgonzo	{ 0, 0 }
144239281Sgonzo};
145239281Sgonzo
146239281Sgonzostatic driver_t ti_aintc_driver = {
147239281Sgonzo	"aintc",
148239281Sgonzo	ti_aintc_methods,
149239281Sgonzo	sizeof(struct ti_aintc_softc),
150239281Sgonzo};
151239281Sgonzo
152239281Sgonzostatic devclass_t ti_aintc_devclass;
153239281Sgonzo
154239281SgonzoDRIVER_MODULE(aintc, simplebus, ti_aintc_driver, ti_aintc_devclass, 0, 0);
155239281Sgonzo
156239281Sgonzoint
157239281Sgonzoarm_get_next_irq(int last_irq)
158239281Sgonzo{
159283329Sian	struct ti_aintc_softc *sc = ti_aintc_sc;
160239281Sgonzo	uint32_t active_irq;
161239281Sgonzo
162239281Sgonzo	/* Get the next active interrupt */
163283329Sian	active_irq = aintc_read_4(sc, INTC_SIR_IRQ);
164239281Sgonzo
165239281Sgonzo	/* Check for spurious interrupt */
166239281Sgonzo	if ((active_irq & 0xffffff80)) {
167283329Sian		device_printf(sc->sc_dev,
168283329Sian		    "Spurious interrupt detected (0x%08x)\n", active_irq);
169283329Sian		aintc_write_4(sc, INTC_SIR_IRQ, 0);
170239281Sgonzo		return -1;
171239281Sgonzo	}
172239281Sgonzo
173239281Sgonzo	if (active_irq != last_irq)
174239281Sgonzo		return active_irq;
175239281Sgonzo	else
176239281Sgonzo		return -1;
177239281Sgonzo}
178239281Sgonzo
179239281Sgonzovoid
180239281Sgonzoarm_mask_irq(uintptr_t nb)
181239281Sgonzo{
182283329Sian	struct ti_aintc_softc *sc = ti_aintc_sc;
183283329Sian
184283329Sian	aintc_write_4(sc, INTC_MIR_SET(nb >> 5), (1UL << (nb & 0x1F)));
185283329Sian	aintc_write_4(sc, INTC_CONTROL, 1); /* EOI */
186239281Sgonzo}
187239281Sgonzo
188239281Sgonzovoid
189239281Sgonzoarm_unmask_irq(uintptr_t nb)
190239281Sgonzo{
191283329Sian	struct ti_aintc_softc *sc = ti_aintc_sc;
192266755Sian
193266755Sian	arm_irq_memory_barrier(nb);
194283329Sian	aintc_write_4(sc, INTC_MIR_CLEAR(nb >> 5), (1UL << (nb & 0x1F)));
195239281Sgonzo}
196