bcm2835_intr.c revision 280558
1239922Sgonzo/*- 2239922Sgonzo * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org> 3239922Sgonzo * All rights reserved. 4239922Sgonzo * 5239922Sgonzo * Based on OMAP3 INTC code by Ben Gray 6239922Sgonzo * 7239922Sgonzo * Redistribution and use in source and binary forms, with or without 8239922Sgonzo * modification, are permitted provided that the following conditions 9239922Sgonzo * are met: 10239922Sgonzo * 1. Redistributions of source code must retain the above copyright 11239922Sgonzo * notice, this list of conditions and the following disclaimer. 12239922Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 13239922Sgonzo * notice, this list of conditions and the following disclaimer in the 14239922Sgonzo * documentation and/or other materials provided with the distribution. 15239922Sgonzo * 16239922Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17239922Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18239922Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19239922Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20239922Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21239922Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22239922Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23239922Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24239922Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25239922Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26239922Sgonzo * SUCH DAMAGE. 27239922Sgonzo */ 28239922Sgonzo 29239922Sgonzo 30239922Sgonzo#include <sys/cdefs.h> 31239922Sgonzo__FBSDID("$FreeBSD: head/sys/arm/broadcom/bcm2835/bcm2835_intr.c 280558 2015-03-25 10:59:42Z andrew $"); 32239922Sgonzo 33239922Sgonzo#include <sys/param.h> 34239922Sgonzo#include <sys/systm.h> 35239922Sgonzo#include <sys/bus.h> 36239922Sgonzo#include <sys/kernel.h> 37239922Sgonzo#include <sys/ktr.h> 38239922Sgonzo#include <sys/module.h> 39239922Sgonzo#include <sys/rman.h> 40239922Sgonzo#include <machine/bus.h> 41239922Sgonzo#include <machine/intr.h> 42239922Sgonzo 43239922Sgonzo#include <dev/fdt/fdt_common.h> 44239922Sgonzo#include <dev/ofw/openfirm.h> 45239922Sgonzo#include <dev/ofw/ofw_bus.h> 46239922Sgonzo#include <dev/ofw/ofw_bus_subr.h> 47239922Sgonzo 48280558Sandrew#ifdef SOC_BCM2836 49280558Sandrew#include <arm/broadcom/bcm2835/bcm2836.h> 50280558Sandrew#endif 51280558Sandrew 52239922Sgonzo#define INTC_PENDING_BASIC 0x00 53239922Sgonzo#define INTC_PENDING_BANK1 0x04 54239922Sgonzo#define INTC_PENDING_BANK2 0x08 55239922Sgonzo#define INTC_FIQ_CONTROL 0x0C 56239922Sgonzo#define INTC_ENABLE_BANK1 0x10 57239922Sgonzo#define INTC_ENABLE_BANK2 0x14 58239922Sgonzo#define INTC_ENABLE_BASIC 0x18 59239922Sgonzo#define INTC_DISABLE_BANK1 0x1C 60239922Sgonzo#define INTC_DISABLE_BANK2 0x20 61239922Sgonzo#define INTC_DISABLE_BASIC 0x24 62239922Sgonzo 63239922Sgonzo#define BANK1_START 8 64239922Sgonzo#define BANK1_END (BANK1_START + 32 - 1) 65239922Sgonzo#define BANK2_START (BANK1_START + 32) 66239922Sgonzo#define BANK2_END (BANK2_START + 32 - 1) 67266470Shselasky#define BANK3_START (BANK2_START + 32) 68280558Sandrew#define BANK3_END (BANK3_START + 32 - 1) 69239922Sgonzo 70239922Sgonzo#define IS_IRQ_BASIC(n) (((n) >= 0) && ((n) < BANK1_START)) 71239922Sgonzo#define IS_IRQ_BANK1(n) (((n) >= BANK1_START) && ((n) <= BANK1_END)) 72239922Sgonzo#define IS_IRQ_BANK2(n) (((n) >= BANK2_START) && ((n) <= BANK2_END)) 73280558Sandrew#define ID_IRQ_BCM2836(n) (((n) >= BANK3_START) && ((n) <= BANK3_END)) 74239922Sgonzo#define IRQ_BANK1(n) ((n) - BANK1_START) 75239922Sgonzo#define IRQ_BANK2(n) ((n) - BANK2_START) 76239922Sgonzo 77239922Sgonzo#ifdef DEBUG 78239922Sgonzo#define dprintf(fmt, args...) printf(fmt, ##args) 79239922Sgonzo#else 80239922Sgonzo#define dprintf(fmt, args...) 81239922Sgonzo#endif 82239922Sgonzo 83239922Sgonzostruct bcm_intc_softc { 84239922Sgonzo device_t sc_dev; 85239922Sgonzo struct resource * intc_res; 86239922Sgonzo bus_space_tag_t intc_bst; 87239922Sgonzo bus_space_handle_t intc_bsh; 88239922Sgonzo}; 89239922Sgonzo 90239922Sgonzostatic struct bcm_intc_softc *bcm_intc_sc = NULL; 91239922Sgonzo 92276017Sandrew#define intc_read_4(_sc, reg) \ 93276017Sandrew bus_space_read_4((_sc)->intc_bst, (_sc)->intc_bsh, (reg)) 94276017Sandrew#define intc_write_4(_sc, reg, val) \ 95276017Sandrew bus_space_write_4((_sc)->intc_bst, (_sc)->intc_bsh, (reg), (val)) 96239922Sgonzo 97239922Sgonzostatic int 98239922Sgonzobcm_intc_probe(device_t dev) 99239922Sgonzo{ 100261410Sian 101261410Sian if (!ofw_bus_status_okay(dev)) 102261410Sian return (ENXIO); 103261410Sian 104239922Sgonzo if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-armctrl-ic")) 105239922Sgonzo return (ENXIO); 106239922Sgonzo device_set_desc(dev, "BCM2835 Interrupt Controller"); 107239922Sgonzo return (BUS_PROBE_DEFAULT); 108239922Sgonzo} 109239922Sgonzo 110239922Sgonzostatic int 111239922Sgonzobcm_intc_attach(device_t dev) 112239922Sgonzo{ 113239922Sgonzo struct bcm_intc_softc *sc = device_get_softc(dev); 114239922Sgonzo int rid = 0; 115239922Sgonzo 116239922Sgonzo sc->sc_dev = dev; 117239922Sgonzo 118239922Sgonzo if (bcm_intc_sc) 119239922Sgonzo return (ENXIO); 120239922Sgonzo 121239922Sgonzo sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 122239922Sgonzo if (sc->intc_res == NULL) { 123239922Sgonzo device_printf(dev, "could not allocate memory resource\n"); 124239922Sgonzo return (ENXIO); 125239922Sgonzo } 126239922Sgonzo 127239922Sgonzo sc->intc_bst = rman_get_bustag(sc->intc_res); 128239922Sgonzo sc->intc_bsh = rman_get_bushandle(sc->intc_res); 129239922Sgonzo 130239922Sgonzo bcm_intc_sc = sc; 131239922Sgonzo 132239922Sgonzo return (0); 133239922Sgonzo} 134239922Sgonzo 135239922Sgonzostatic device_method_t bcm_intc_methods[] = { 136239922Sgonzo DEVMETHOD(device_probe, bcm_intc_probe), 137239922Sgonzo DEVMETHOD(device_attach, bcm_intc_attach), 138239922Sgonzo { 0, 0 } 139239922Sgonzo}; 140239922Sgonzo 141239922Sgonzostatic driver_t bcm_intc_driver = { 142239922Sgonzo "intc", 143239922Sgonzo bcm_intc_methods, 144239922Sgonzo sizeof(struct bcm_intc_softc), 145239922Sgonzo}; 146239922Sgonzo 147239922Sgonzostatic devclass_t bcm_intc_devclass; 148239922Sgonzo 149239922SgonzoDRIVER_MODULE(intc, simplebus, bcm_intc_driver, bcm_intc_devclass, 0, 0); 150239922Sgonzo 151239922Sgonzoint 152239922Sgonzoarm_get_next_irq(int last_irq) 153239922Sgonzo{ 154276017Sandrew struct bcm_intc_softc *sc = bcm_intc_sc; 155239922Sgonzo uint32_t pending; 156239922Sgonzo int32_t irq = last_irq + 1; 157280558Sandrew#ifdef SOC_BCM2836 158280558Sandrew int ret; 159280558Sandrew#endif 160239922Sgonzo 161239922Sgonzo /* Sanity check */ 162239922Sgonzo if (irq < 0) 163239922Sgonzo irq = 0; 164266470Shselasky 165280558Sandrew#ifdef SOC_BCM2836 166280558Sandrew if ((ret = bcm2836_get_next_irq(irq)) >= 0) 167280558Sandrew return (ret + BANK3_START); 168280558Sandrew#endif 169280558Sandrew 170239922Sgonzo /* TODO: should we mask last_irq? */ 171266470Shselasky if (irq < BANK1_START) { 172276017Sandrew pending = intc_read_4(sc, INTC_PENDING_BASIC); 173266470Shselasky if ((pending & 0xFF) == 0) { 174266470Shselasky irq = BANK1_START; /* skip to next bank */ 175266470Shselasky } else do { 176266470Shselasky if (pending & (1 << irq)) 177266470Shselasky return irq; 178266470Shselasky irq++; 179266470Shselasky } while (irq < BANK1_START); 180239922Sgonzo } 181266470Shselasky if (irq < BANK2_START) { 182276017Sandrew pending = intc_read_4(sc, INTC_PENDING_BANK1); 183266470Shselasky if (pending == 0) { 184266470Shselasky irq = BANK2_START; /* skip to next bank */ 185266470Shselasky } else do { 186266470Shselasky if (pending & (1 << IRQ_BANK1(irq))) 187266470Shselasky return irq; 188266470Shselasky irq++; 189266470Shselasky } while (irq < BANK2_START); 190239922Sgonzo } 191266470Shselasky if (irq < BANK3_START) { 192276017Sandrew pending = intc_read_4(sc, INTC_PENDING_BANK2); 193266470Shselasky if (pending != 0) do { 194266470Shselasky if (pending & (1 << IRQ_BANK2(irq))) 195266470Shselasky return irq; 196266470Shselasky irq++; 197266470Shselasky } while (irq < BANK3_START); 198239922Sgonzo } 199239922Sgonzo return (-1); 200239922Sgonzo} 201239922Sgonzo 202239922Sgonzovoid 203239922Sgonzoarm_mask_irq(uintptr_t nb) 204239922Sgonzo{ 205276017Sandrew struct bcm_intc_softc *sc = bcm_intc_sc; 206239922Sgonzo dprintf("%s: %d\n", __func__, nb); 207239922Sgonzo 208239922Sgonzo if (IS_IRQ_BASIC(nb)) 209276017Sandrew intc_write_4(sc, INTC_DISABLE_BASIC, (1 << nb)); 210239922Sgonzo else if (IS_IRQ_BANK1(nb)) 211276017Sandrew intc_write_4(sc, INTC_DISABLE_BANK1, (1 << IRQ_BANK1(nb))); 212239922Sgonzo else if (IS_IRQ_BANK2(nb)) 213276017Sandrew intc_write_4(sc, INTC_DISABLE_BANK2, (1 << IRQ_BANK2(nb))); 214280558Sandrew#ifdef SOC_BCM2836 215280558Sandrew else if (ID_IRQ_BCM2836(nb)) 216280558Sandrew bcm2836_mask_irq(nb - BANK3_START); 217280558Sandrew#endif 218239922Sgonzo else 219239922Sgonzo printf("arm_mask_irq: Invalid IRQ number: %d\n", nb); 220239922Sgonzo} 221239922Sgonzo 222239922Sgonzovoid 223239922Sgonzoarm_unmask_irq(uintptr_t nb) 224239922Sgonzo{ 225276017Sandrew struct bcm_intc_softc *sc = bcm_intc_sc; 226239922Sgonzo dprintf("%s: %d\n", __func__, nb); 227239922Sgonzo 228239922Sgonzo if (IS_IRQ_BASIC(nb)) 229276017Sandrew intc_write_4(sc, INTC_ENABLE_BASIC, (1 << nb)); 230239922Sgonzo else if (IS_IRQ_BANK1(nb)) 231276017Sandrew intc_write_4(sc, INTC_ENABLE_BANK1, (1 << IRQ_BANK1(nb))); 232239922Sgonzo else if (IS_IRQ_BANK2(nb)) 233276017Sandrew intc_write_4(sc, INTC_ENABLE_BANK2, (1 << IRQ_BANK2(nb))); 234280558Sandrew#ifdef SOC_BCM2836 235280558Sandrew else if (ID_IRQ_BCM2836(nb)) 236280558Sandrew bcm2836_unmask_irq(nb - BANK3_START); 237280558Sandrew#endif 238239922Sgonzo else 239239922Sgonzo printf("arm_mask_irq: Invalid IRQ number: %d\n", nb); 240239922Sgonzo} 241