1e53470feSOleksandr Tymoshenko /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3af3dc4a7SPedro F. Giffuni *
4e53470feSOleksandr Tymoshenko * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
5e53470feSOleksandr Tymoshenko * All rights reserved.
6e53470feSOleksandr Tymoshenko *
7e53470feSOleksandr Tymoshenko * Based on OMAP3 INTC code by Ben Gray
8e53470feSOleksandr Tymoshenko *
9e53470feSOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without
10e53470feSOleksandr Tymoshenko * modification, are permitted provided that the following conditions
11e53470feSOleksandr Tymoshenko * are met:
12e53470feSOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright
13e53470feSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer.
14e53470feSOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright
15e53470feSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the
16e53470feSOleksandr Tymoshenko * documentation and/or other materials provided with the distribution.
17e53470feSOleksandr Tymoshenko *
18e53470feSOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19e53470feSOleksandr Tymoshenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20e53470feSOleksandr Tymoshenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21e53470feSOleksandr Tymoshenko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22e53470feSOleksandr Tymoshenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23e53470feSOleksandr Tymoshenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24e53470feSOleksandr Tymoshenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25e53470feSOleksandr Tymoshenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26e53470feSOleksandr Tymoshenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27e53470feSOleksandr Tymoshenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28e53470feSOleksandr Tymoshenko * SUCH DAMAGE.
29e53470feSOleksandr Tymoshenko */
30e53470feSOleksandr Tymoshenko
31e53470feSOleksandr Tymoshenko #include <sys/cdefs.h>
3286816217SSvatopluk Kraus #include "opt_platform.h"
3386816217SSvatopluk Kraus
34e53470feSOleksandr Tymoshenko #include <sys/param.h>
35e53470feSOleksandr Tymoshenko #include <sys/systm.h>
36e53470feSOleksandr Tymoshenko #include <sys/bus.h>
37e53470feSOleksandr Tymoshenko #include <sys/kernel.h>
38e53470feSOleksandr Tymoshenko #include <sys/ktr.h>
39e53470feSOleksandr Tymoshenko #include <sys/module.h>
4086816217SSvatopluk Kraus #include <sys/proc.h>
41e53470feSOleksandr Tymoshenko #include <sys/rman.h>
42e53470feSOleksandr Tymoshenko #include <machine/bus.h>
43e53470feSOleksandr Tymoshenko #include <machine/intr.h>
44e53470feSOleksandr Tymoshenko
45e53470feSOleksandr Tymoshenko #include <dev/ofw/openfirm.h>
46e53470feSOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h>
47e53470feSOleksandr Tymoshenko #include <dev/ofw/ofw_bus_subr.h>
48e53470feSOleksandr Tymoshenko
4986816217SSvatopluk Kraus #include "pic_if.h"
5086816217SSvatopluk Kraus
51e53470feSOleksandr Tymoshenko #define INTC_REVISION 0x00
52e53470feSOleksandr Tymoshenko #define INTC_SYSCONFIG 0x10
53e53470feSOleksandr Tymoshenko #define INTC_SYSSTATUS 0x14
54e53470feSOleksandr Tymoshenko #define INTC_SIR_IRQ 0x40
55e53470feSOleksandr Tymoshenko #define INTC_CONTROL 0x48
56e53470feSOleksandr Tymoshenko #define INTC_THRESHOLD 0x68
57e53470feSOleksandr Tymoshenko #define INTC_MIR_CLEAR(x) (0x88 + ((x) * 0x20))
58e53470feSOleksandr Tymoshenko #define INTC_MIR_SET(x) (0x8C + ((x) * 0x20))
59e53470feSOleksandr Tymoshenko #define INTC_ISR_SET(x) (0x90 + ((x) * 0x20))
60e53470feSOleksandr Tymoshenko #define INTC_ISR_CLEAR(x) (0x94 + ((x) * 0x20))
61e53470feSOleksandr Tymoshenko
6286816217SSvatopluk Kraus #define INTC_SIR_SPURIOUS_MASK 0xffffff80
633d6bafd1SSvatopluk Kraus #define INTC_SIR_ACTIVE_MASK 0x7f
6486816217SSvatopluk Kraus
6586816217SSvatopluk Kraus #define INTC_NIRQS 128
6686816217SSvatopluk Kraus
6786816217SSvatopluk Kraus struct ti_aintc_irqsrc {
6886816217SSvatopluk Kraus struct intr_irqsrc tai_isrc;
6986816217SSvatopluk Kraus u_int tai_irq;
7086816217SSvatopluk Kraus };
7186816217SSvatopluk Kraus
72e53470feSOleksandr Tymoshenko struct ti_aintc_softc {
73e53470feSOleksandr Tymoshenko device_t sc_dev;
74e53470feSOleksandr Tymoshenko struct resource * aintc_res[3];
75e53470feSOleksandr Tymoshenko bus_space_tag_t aintc_bst;
76e53470feSOleksandr Tymoshenko bus_space_handle_t aintc_bsh;
77e53470feSOleksandr Tymoshenko uint8_t ver;
7886816217SSvatopluk Kraus struct ti_aintc_irqsrc aintc_isrcs[INTC_NIRQS];
79e53470feSOleksandr Tymoshenko };
80e53470feSOleksandr Tymoshenko
81e53470feSOleksandr Tymoshenko static struct resource_spec ti_aintc_spec[] = {
82e53470feSOleksandr Tymoshenko { SYS_RES_MEMORY, 0, RF_ACTIVE },
83e53470feSOleksandr Tymoshenko { -1, 0 }
84e53470feSOleksandr Tymoshenko };
85e53470feSOleksandr Tymoshenko
863d6451aeSAndrew Turner #define aintc_read_4(_sc, reg) \
873d6451aeSAndrew Turner bus_space_read_4((_sc)->aintc_bst, (_sc)->aintc_bsh, (reg))
883d6451aeSAndrew Turner #define aintc_write_4(_sc, reg, val) \
893d6451aeSAndrew Turner bus_space_write_4((_sc)->aintc_bst, (_sc)->aintc_bsh, (reg), (val))
90e53470feSOleksandr Tymoshenko
915b03aba6SOleksandr Tymoshenko /* List of compatible strings for FDT tree */
925b03aba6SOleksandr Tymoshenko static struct ofw_compat_data compat_data[] = {
935b03aba6SOleksandr Tymoshenko {"ti,am33xx-intc", 1},
945b03aba6SOleksandr Tymoshenko {"ti,omap2-intc", 1},
955b03aba6SOleksandr Tymoshenko {NULL, 0},
965b03aba6SOleksandr Tymoshenko };
97e53470feSOleksandr Tymoshenko
9886816217SSvatopluk Kraus static inline void
ti_aintc_irq_eoi(struct ti_aintc_softc * sc)9986816217SSvatopluk Kraus ti_aintc_irq_eoi(struct ti_aintc_softc *sc)
10086816217SSvatopluk Kraus {
10186816217SSvatopluk Kraus
10286816217SSvatopluk Kraus aintc_write_4(sc, INTC_CONTROL, 1);
10386816217SSvatopluk Kraus }
10486816217SSvatopluk Kraus
10586816217SSvatopluk Kraus static inline void
ti_aintc_irq_mask(struct ti_aintc_softc * sc,u_int irq)10686816217SSvatopluk Kraus ti_aintc_irq_mask(struct ti_aintc_softc *sc, u_int irq)
10786816217SSvatopluk Kraus {
10886816217SSvatopluk Kraus
10986816217SSvatopluk Kraus aintc_write_4(sc, INTC_MIR_SET(irq >> 5), (1UL << (irq & 0x1F)));
11086816217SSvatopluk Kraus }
11186816217SSvatopluk Kraus
11286816217SSvatopluk Kraus static inline void
ti_aintc_irq_unmask(struct ti_aintc_softc * sc,u_int irq)11386816217SSvatopluk Kraus ti_aintc_irq_unmask(struct ti_aintc_softc *sc, u_int irq)
11486816217SSvatopluk Kraus {
11586816217SSvatopluk Kraus
11686816217SSvatopluk Kraus aintc_write_4(sc, INTC_MIR_CLEAR(irq >> 5), (1UL << (irq & 0x1F)));
11786816217SSvatopluk Kraus }
11886816217SSvatopluk Kraus
11986816217SSvatopluk Kraus static int
ti_aintc_intr(void * arg)12086816217SSvatopluk Kraus ti_aintc_intr(void *arg)
12186816217SSvatopluk Kraus {
12286816217SSvatopluk Kraus uint32_t irq;
12386816217SSvatopluk Kraus struct ti_aintc_softc *sc = arg;
12486816217SSvatopluk Kraus
12586816217SSvatopluk Kraus /* Get active interrupt */
12686816217SSvatopluk Kraus irq = aintc_read_4(sc, INTC_SIR_IRQ);
12786816217SSvatopluk Kraus if ((irq & INTC_SIR_SPURIOUS_MASK) != 0) {
12886816217SSvatopluk Kraus device_printf(sc->sc_dev,
12986816217SSvatopluk Kraus "Spurious interrupt detected (0x%08x)\n", irq);
13086816217SSvatopluk Kraus ti_aintc_irq_eoi(sc);
13186816217SSvatopluk Kraus return (FILTER_HANDLED);
13286816217SSvatopluk Kraus }
13386816217SSvatopluk Kraus
13486816217SSvatopluk Kraus /* Only level-sensitive interrupts detection is supported. */
1353d6bafd1SSvatopluk Kraus irq &= INTC_SIR_ACTIVE_MASK;
13686816217SSvatopluk Kraus if (intr_isrc_dispatch(&sc->aintc_isrcs[irq].tai_isrc,
13786816217SSvatopluk Kraus curthread->td_intr_frame) != 0) {
13886816217SSvatopluk Kraus ti_aintc_irq_mask(sc, irq);
13986816217SSvatopluk Kraus ti_aintc_irq_eoi(sc);
14086816217SSvatopluk Kraus device_printf(sc->sc_dev, "Stray irq %u disabled\n", irq);
14186816217SSvatopluk Kraus }
14286816217SSvatopluk Kraus
14386816217SSvatopluk Kraus arm_irq_memory_barrier(irq); /* XXX */
14486816217SSvatopluk Kraus return (FILTER_HANDLED);
14586816217SSvatopluk Kraus }
14686816217SSvatopluk Kraus
14786816217SSvatopluk Kraus static void
ti_aintc_enable_intr(device_t dev,struct intr_irqsrc * isrc)14886816217SSvatopluk Kraus ti_aintc_enable_intr(device_t dev, struct intr_irqsrc *isrc)
14986816217SSvatopluk Kraus {
15086816217SSvatopluk Kraus u_int irq = ((struct ti_aintc_irqsrc *)isrc)->tai_irq;
15186816217SSvatopluk Kraus struct ti_aintc_softc *sc = device_get_softc(dev);
15286816217SSvatopluk Kraus
15386816217SSvatopluk Kraus arm_irq_memory_barrier(irq);
15486816217SSvatopluk Kraus ti_aintc_irq_unmask(sc, irq);
15586816217SSvatopluk Kraus }
15686816217SSvatopluk Kraus
15786816217SSvatopluk Kraus static void
ti_aintc_disable_intr(device_t dev,struct intr_irqsrc * isrc)15886816217SSvatopluk Kraus ti_aintc_disable_intr(device_t dev, struct intr_irqsrc *isrc)
15986816217SSvatopluk Kraus {
16086816217SSvatopluk Kraus u_int irq = ((struct ti_aintc_irqsrc *)isrc)->tai_irq;
16186816217SSvatopluk Kraus struct ti_aintc_softc *sc = device_get_softc(dev);
16286816217SSvatopluk Kraus
16386816217SSvatopluk Kraus ti_aintc_irq_mask(sc, irq);
16486816217SSvatopluk Kraus }
16586816217SSvatopluk Kraus
16686816217SSvatopluk Kraus static int
ti_aintc_map_intr(device_t dev,struct intr_map_data * data,struct intr_irqsrc ** isrcp)16786816217SSvatopluk Kraus ti_aintc_map_intr(device_t dev, struct intr_map_data *data,
16886816217SSvatopluk Kraus struct intr_irqsrc **isrcp)
16986816217SSvatopluk Kraus {
170cd642c88SSvatopluk Kraus struct intr_map_data_fdt *daf;
17186816217SSvatopluk Kraus struct ti_aintc_softc *sc;
17286816217SSvatopluk Kraus
173cd642c88SSvatopluk Kraus if (data->type != INTR_MAP_DATA_FDT)
174cd642c88SSvatopluk Kraus return (ENOTSUP);
175cd642c88SSvatopluk Kraus
176cd642c88SSvatopluk Kraus daf = (struct intr_map_data_fdt *)data;
177cd642c88SSvatopluk Kraus if (daf->ncells != 1 || daf->cells[0] >= INTC_NIRQS)
17886816217SSvatopluk Kraus return (EINVAL);
17986816217SSvatopluk Kraus
18086816217SSvatopluk Kraus sc = device_get_softc(dev);
181cd642c88SSvatopluk Kraus *isrcp = &sc->aintc_isrcs[daf->cells[0]].tai_isrc;
18286816217SSvatopluk Kraus return (0);
18386816217SSvatopluk Kraus }
18486816217SSvatopluk Kraus
18586816217SSvatopluk Kraus static void
ti_aintc_pre_ithread(device_t dev,struct intr_irqsrc * isrc)18686816217SSvatopluk Kraus ti_aintc_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
18786816217SSvatopluk Kraus {
18886816217SSvatopluk Kraus u_int irq = ((struct ti_aintc_irqsrc *)isrc)->tai_irq;
18986816217SSvatopluk Kraus struct ti_aintc_softc *sc = device_get_softc(dev);
19086816217SSvatopluk Kraus
19186816217SSvatopluk Kraus ti_aintc_irq_mask(sc, irq);
19286816217SSvatopluk Kraus ti_aintc_irq_eoi(sc);
19386816217SSvatopluk Kraus }
19486816217SSvatopluk Kraus
19586816217SSvatopluk Kraus static void
ti_aintc_post_ithread(device_t dev,struct intr_irqsrc * isrc)19686816217SSvatopluk Kraus ti_aintc_post_ithread(device_t dev, struct intr_irqsrc *isrc)
19786816217SSvatopluk Kraus {
19886816217SSvatopluk Kraus
19986816217SSvatopluk Kraus ti_aintc_enable_intr(dev, isrc);
20086816217SSvatopluk Kraus }
20186816217SSvatopluk Kraus
20286816217SSvatopluk Kraus static void
ti_aintc_post_filter(device_t dev,struct intr_irqsrc * isrc)20386816217SSvatopluk Kraus ti_aintc_post_filter(device_t dev, struct intr_irqsrc *isrc)
20486816217SSvatopluk Kraus {
20586816217SSvatopluk Kraus
20686816217SSvatopluk Kraus ti_aintc_irq_eoi(device_get_softc(dev));
20786816217SSvatopluk Kraus }
20886816217SSvatopluk Kraus
20986816217SSvatopluk Kraus static int
ti_aintc_pic_attach(struct ti_aintc_softc * sc)21086816217SSvatopluk Kraus ti_aintc_pic_attach(struct ti_aintc_softc *sc)
21186816217SSvatopluk Kraus {
2129346e913SAndrew Turner struct intr_pic *pic;
21386816217SSvatopluk Kraus int error;
21486816217SSvatopluk Kraus uint32_t irq;
21586816217SSvatopluk Kraus const char *name;
21686816217SSvatopluk Kraus intptr_t xref;
21786816217SSvatopluk Kraus
21886816217SSvatopluk Kraus name = device_get_nameunit(sc->sc_dev);
21986816217SSvatopluk Kraus for (irq = 0; irq < INTC_NIRQS; irq++) {
22086816217SSvatopluk Kraus sc->aintc_isrcs[irq].tai_irq = irq;
22186816217SSvatopluk Kraus
22286816217SSvatopluk Kraus error = intr_isrc_register(&sc->aintc_isrcs[irq].tai_isrc,
22386816217SSvatopluk Kraus sc->sc_dev, 0, "%s,%u", name, irq);
22486816217SSvatopluk Kraus if (error != 0)
22586816217SSvatopluk Kraus return (error);
22686816217SSvatopluk Kraus }
22786816217SSvatopluk Kraus
22886816217SSvatopluk Kraus xref = OF_xref_from_node(ofw_bus_get_node(sc->sc_dev));
2299346e913SAndrew Turner pic = intr_pic_register(sc->sc_dev, xref);
2309346e913SAndrew Turner if (pic == NULL)
2319346e913SAndrew Turner return (ENXIO);
23286816217SSvatopluk Kraus
233*85918bebSAyrton Munoz return (intr_pic_claim_root(sc->sc_dev, xref, ti_aintc_intr, sc,
234*85918bebSAyrton Munoz INTR_ROOT_IRQ));
23586816217SSvatopluk Kraus }
23686816217SSvatopluk Kraus
237e53470feSOleksandr Tymoshenko static int
ti_aintc_probe(device_t dev)238e53470feSOleksandr Tymoshenko ti_aintc_probe(device_t dev)
239e53470feSOleksandr Tymoshenko {
240add35ed5SIan Lepore if (!ofw_bus_status_okay(dev))
241add35ed5SIan Lepore return (ENXIO);
242add35ed5SIan Lepore
2435b03aba6SOleksandr Tymoshenko if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
244e53470feSOleksandr Tymoshenko return (ENXIO);
2455b03aba6SOleksandr Tymoshenko
246e53470feSOleksandr Tymoshenko device_set_desc(dev, "TI AINTC Interrupt Controller");
247e53470feSOleksandr Tymoshenko return (BUS_PROBE_DEFAULT);
248e53470feSOleksandr Tymoshenko }
249e53470feSOleksandr Tymoshenko
250e53470feSOleksandr Tymoshenko static int
ti_aintc_attach(device_t dev)251e53470feSOleksandr Tymoshenko ti_aintc_attach(device_t dev)
252e53470feSOleksandr Tymoshenko {
253e53470feSOleksandr Tymoshenko struct ti_aintc_softc *sc = device_get_softc(dev);
254e53470feSOleksandr Tymoshenko uint32_t x;
255e53470feSOleksandr Tymoshenko
256e53470feSOleksandr Tymoshenko sc->sc_dev = dev;
257e53470feSOleksandr Tymoshenko
258e53470feSOleksandr Tymoshenko if (bus_alloc_resources(dev, ti_aintc_spec, sc->aintc_res)) {
259e53470feSOleksandr Tymoshenko device_printf(dev, "could not allocate resources\n");
260e53470feSOleksandr Tymoshenko return (ENXIO);
261e53470feSOleksandr Tymoshenko }
262e53470feSOleksandr Tymoshenko
263e53470feSOleksandr Tymoshenko sc->aintc_bst = rman_get_bustag(sc->aintc_res[0]);
264e53470feSOleksandr Tymoshenko sc->aintc_bsh = rman_get_bushandle(sc->aintc_res[0]);
265e53470feSOleksandr Tymoshenko
2663d6451aeSAndrew Turner x = aintc_read_4(sc, INTC_REVISION);
267e53470feSOleksandr Tymoshenko device_printf(dev, "Revision %u.%u\n",(x >> 4) & 0xF, x & 0xF);
268e53470feSOleksandr Tymoshenko
269e53470feSOleksandr Tymoshenko /* SoftReset */
2703d6451aeSAndrew Turner aintc_write_4(sc, INTC_SYSCONFIG, 2);
271e53470feSOleksandr Tymoshenko
272e53470feSOleksandr Tymoshenko /* Wait for reset to complete */
2733d6451aeSAndrew Turner while(!(aintc_read_4(sc, INTC_SYSSTATUS) & 1));
274e53470feSOleksandr Tymoshenko
275e53470feSOleksandr Tymoshenko /*Set Priority Threshold */
2763d6451aeSAndrew Turner aintc_write_4(sc, INTC_THRESHOLD, 0xFF);
277e53470feSOleksandr Tymoshenko
27886816217SSvatopluk Kraus if (ti_aintc_pic_attach(sc) != 0) {
27986816217SSvatopluk Kraus device_printf(dev, "could not attach PIC\n");
28086816217SSvatopluk Kraus return (ENXIO);
28186816217SSvatopluk Kraus }
282e53470feSOleksandr Tymoshenko return (0);
283e53470feSOleksandr Tymoshenko }
284e53470feSOleksandr Tymoshenko
285e53470feSOleksandr Tymoshenko static device_method_t ti_aintc_methods[] = {
286e53470feSOleksandr Tymoshenko DEVMETHOD(device_probe, ti_aintc_probe),
287e53470feSOleksandr Tymoshenko DEVMETHOD(device_attach, ti_aintc_attach),
28886816217SSvatopluk Kraus
28986816217SSvatopluk Kraus DEVMETHOD(pic_disable_intr, ti_aintc_disable_intr),
29086816217SSvatopluk Kraus DEVMETHOD(pic_enable_intr, ti_aintc_enable_intr),
29186816217SSvatopluk Kraus DEVMETHOD(pic_map_intr, ti_aintc_map_intr),
29286816217SSvatopluk Kraus DEVMETHOD(pic_post_filter, ti_aintc_post_filter),
29386816217SSvatopluk Kraus DEVMETHOD(pic_post_ithread, ti_aintc_post_ithread),
29486816217SSvatopluk Kraus DEVMETHOD(pic_pre_ithread, ti_aintc_pre_ithread),
295e53470feSOleksandr Tymoshenko { 0, 0 }
296e53470feSOleksandr Tymoshenko };
297e53470feSOleksandr Tymoshenko
298e53470feSOleksandr Tymoshenko static driver_t ti_aintc_driver = {
299d47e148eSLuiz Otavio O Souza "ti_aintc",
300e53470feSOleksandr Tymoshenko ti_aintc_methods,
301e53470feSOleksandr Tymoshenko sizeof(struct ti_aintc_softc),
302e53470feSOleksandr Tymoshenko };
303e53470feSOleksandr Tymoshenko
3048537e671SJohn Baldwin EARLY_DRIVER_MODULE(ti_aintc, simplebus, ti_aintc_driver, 0, 0,
3058537e671SJohn Baldwin BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
30631c9adb7SWarner Losh SIMPLEBUS_PNP_INFO(compat_data);
307