1239278Sgonzo/*- 2239278Sgonzo * Copyright (c) 2010 Jakub Wojciech Klama <jceel@FreeBSD.org> 3239278Sgonzo * All rights reserved. 4239278Sgonzo * 5239278Sgonzo * Redistribution and use in source and binary forms, with or without 6239278Sgonzo * modification, are permitted provided that the following conditions 7239278Sgonzo * are met: 8239278Sgonzo * 1. Redistributions of source code must retain the above copyright 9239278Sgonzo * notice, this list of conditions and the following disclaimer. 10239278Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 11239278Sgonzo * notice, this list of conditions and the following disclaimer in the 12239278Sgonzo * documentation and/or other materials provided with the distribution. 13239278Sgonzo * 14239278Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15239278Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16239278Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17239278Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18239278Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19239278Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20239278Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21239278Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22239278Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23239278Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24239278Sgonzo * SUCH DAMAGE. 25239278Sgonzo * 26239278Sgonzo */ 27239278Sgonzo 28239278Sgonzo#include <sys/cdefs.h> 29239278Sgonzo__FBSDID("$FreeBSD: stable/11/sys/arm/lpc/lpc_intc.c 314506 2017-03-01 19:55:04Z ian $"); 30239278Sgonzo 31239278Sgonzo#include <sys/param.h> 32239278Sgonzo#include <sys/systm.h> 33239278Sgonzo#include <sys/bus.h> 34239278Sgonzo#include <sys/kernel.h> 35239278Sgonzo#include <sys/module.h> 36239278Sgonzo#include <sys/malloc.h> 37239278Sgonzo#include <sys/rman.h> 38239278Sgonzo#include <sys/timetc.h> 39239278Sgonzo#include <machine/bus.h> 40239278Sgonzo#include <machine/intr.h> 41239278Sgonzo 42239278Sgonzo#include <dev/fdt/fdt_common.h> 43239278Sgonzo#include <dev/ofw/openfirm.h> 44239278Sgonzo 45239278Sgonzo#include <dev/ofw/ofw_bus.h> 46239278Sgonzo#include <dev/ofw/ofw_bus_subr.h> 47239278Sgonzo 48239278Sgonzo#include <arm/lpc/lpcreg.h> 49239278Sgonzo 50239278Sgonzostruct lpc_intc_softc { 51239278Sgonzo struct resource * li_res; 52239278Sgonzo bus_space_tag_t li_bst; 53239278Sgonzo bus_space_handle_t li_bsh; 54239278Sgonzo}; 55239278Sgonzo 56239278Sgonzostatic int lpc_intc_probe(device_t); 57239278Sgonzostatic int lpc_intc_attach(device_t); 58239278Sgonzostatic void lpc_intc_eoi(void *); 59239278Sgonzo 60239278Sgonzostatic struct lpc_intc_softc *intc_softc = NULL; 61239278Sgonzo 62276023Sandrew#define intc_read_4(_sc, _reg) \ 63276023Sandrew bus_space_read_4((_sc)->li_bst, (_sc)->li_bsh, (_reg)) 64276023Sandrew#define intc_write_4(_sc, _reg, _val) \ 65276023Sandrew bus_space_write_4((_sc)->li_bst, (_sc)->li_bsh, (_reg), (_val)) 66239278Sgonzo 67239278Sgonzostatic int 68239278Sgonzolpc_intc_probe(device_t dev) 69239278Sgonzo{ 70239278Sgonzo 71261410Sian if (!ofw_bus_status_okay(dev)) 72261410Sian return (ENXIO); 73261410Sian 74239278Sgonzo if (!ofw_bus_is_compatible(dev, "lpc,pic")) 75239278Sgonzo return (ENXIO); 76239278Sgonzo 77239278Sgonzo device_set_desc(dev, "LPC32x0 Interrupt Controller"); 78239278Sgonzo return (BUS_PROBE_DEFAULT); 79239278Sgonzo} 80239278Sgonzo 81239278Sgonzostatic int 82239278Sgonzolpc_intc_attach(device_t dev) 83239278Sgonzo{ 84239278Sgonzo struct lpc_intc_softc *sc = device_get_softc(dev); 85239278Sgonzo int rid = 0; 86239278Sgonzo 87239278Sgonzo if (intc_softc) 88239278Sgonzo return (ENXIO); 89239278Sgonzo 90239278Sgonzo sc->li_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 91239278Sgonzo RF_ACTIVE); 92239278Sgonzo if (!sc->li_res) { 93239278Sgonzo device_printf(dev, "could not alloc resources\n"); 94239278Sgonzo return (ENXIO); 95239278Sgonzo } 96239278Sgonzo 97239278Sgonzo sc->li_bst = rman_get_bustag(sc->li_res); 98239278Sgonzo sc->li_bsh = rman_get_bushandle(sc->li_res); 99239278Sgonzo intc_softc = sc; 100239278Sgonzo arm_post_filter = lpc_intc_eoi; 101239278Sgonzo 102239278Sgonzo /* Clear interrupt status registers and disable all interrupts */ 103276023Sandrew intc_write_4(sc, LPC_INTC_MIC_ER, 0); 104276023Sandrew intc_write_4(sc, LPC_INTC_SIC1_ER, 0); 105276023Sandrew intc_write_4(sc, LPC_INTC_SIC2_ER, 0); 106276023Sandrew intc_write_4(sc, LPC_INTC_MIC_RSR, ~0); 107276023Sandrew intc_write_4(sc, LPC_INTC_SIC1_RSR, ~0); 108276023Sandrew intc_write_4(sc, LPC_INTC_SIC2_RSR, ~0); 109239278Sgonzo return (0); 110239278Sgonzo} 111239278Sgonzo 112239278Sgonzostatic device_method_t lpc_intc_methods[] = { 113239278Sgonzo DEVMETHOD(device_probe, lpc_intc_probe), 114239278Sgonzo DEVMETHOD(device_attach, lpc_intc_attach), 115239278Sgonzo { 0, 0 } 116239278Sgonzo}; 117239278Sgonzo 118239278Sgonzostatic driver_t lpc_intc_driver = { 119239278Sgonzo "pic", 120239278Sgonzo lpc_intc_methods, 121239278Sgonzo sizeof(struct lpc_intc_softc), 122239278Sgonzo}; 123239278Sgonzo 124239278Sgonzostatic devclass_t lpc_intc_devclass; 125239278Sgonzo 126239278SgonzoDRIVER_MODULE(pic, simplebus, lpc_intc_driver, lpc_intc_devclass, 0, 0); 127239278Sgonzo 128239278Sgonzoint 129239278Sgonzoarm_get_next_irq(int last) 130239278Sgonzo{ 131276023Sandrew struct lpc_intc_softc *sc = intc_softc; 132239278Sgonzo uint32_t value; 133239278Sgonzo int i; 134239278Sgonzo 135239278Sgonzo /* IRQs 0-31 are mapped to LPC_INTC_MIC_SR */ 136276023Sandrew value = intc_read_4(sc, LPC_INTC_MIC_SR); 137239278Sgonzo for (i = 0; i < 32; i++) { 138239278Sgonzo if (value & (1 << i)) 139239278Sgonzo return (i); 140239278Sgonzo } 141239278Sgonzo 142239278Sgonzo /* IRQs 32-63 are mapped to LPC_INTC_SIC1_SR */ 143276023Sandrew value = intc_read_4(sc, LPC_INTC_SIC1_SR); 144239278Sgonzo for (i = 0; i < 32; i++) { 145239278Sgonzo if (value & (1 << i)) 146239278Sgonzo return (i + 32); 147239278Sgonzo } 148239278Sgonzo 149239278Sgonzo /* IRQs 64-95 are mapped to LPC_INTC_SIC2_SR */ 150276023Sandrew value = intc_read_4(sc, LPC_INTC_SIC2_SR); 151239278Sgonzo for (i = 0; i < 32; i++) { 152239278Sgonzo if (value & (1 << i)) 153239278Sgonzo return (i + 64); 154239278Sgonzo } 155239278Sgonzo 156239278Sgonzo return (-1); 157239278Sgonzo} 158239278Sgonzo 159239278Sgonzovoid 160239278Sgonzoarm_mask_irq(uintptr_t nb) 161239278Sgonzo{ 162276023Sandrew struct lpc_intc_softc *sc = intc_softc; 163239278Sgonzo int reg; 164239278Sgonzo uint32_t value; 165239278Sgonzo 166239278Sgonzo /* Make sure that interrupt isn't active already */ 167239278Sgonzo lpc_intc_eoi((void *)nb); 168239278Sgonzo 169239278Sgonzo if (nb > 63) { 170239278Sgonzo nb -= 64; 171239278Sgonzo reg = LPC_INTC_SIC2_ER; 172239278Sgonzo } else if (nb > 31) { 173239278Sgonzo nb -= 32; 174239278Sgonzo reg = LPC_INTC_SIC1_ER; 175239278Sgonzo } else 176239278Sgonzo reg = LPC_INTC_MIC_ER; 177239278Sgonzo 178239278Sgonzo /* Clear bit in ER register */ 179276023Sandrew value = intc_read_4(sc, reg); 180239278Sgonzo value &= ~(1 << nb); 181276023Sandrew intc_write_4(sc, reg, value); 182239278Sgonzo} 183239278Sgonzo 184239278Sgonzovoid 185239278Sgonzoarm_unmask_irq(uintptr_t nb) 186239278Sgonzo{ 187276023Sandrew struct lpc_intc_softc *sc = intc_softc; 188239278Sgonzo int reg; 189239278Sgonzo uint32_t value; 190239278Sgonzo 191239278Sgonzo if (nb > 63) { 192239278Sgonzo nb -= 64; 193239278Sgonzo reg = LPC_INTC_SIC2_ER; 194239278Sgonzo } else if (nb > 31) { 195239278Sgonzo nb -= 32; 196239278Sgonzo reg = LPC_INTC_SIC1_ER; 197239278Sgonzo } else 198239278Sgonzo reg = LPC_INTC_MIC_ER; 199239278Sgonzo 200239278Sgonzo /* Set bit in ER register */ 201276023Sandrew value = intc_read_4(sc, reg); 202239278Sgonzo value |= (1 << nb); 203276023Sandrew intc_write_4(sc, reg, value); 204239278Sgonzo} 205239278Sgonzo 206239278Sgonzostatic void 207239278Sgonzolpc_intc_eoi(void *data) 208239278Sgonzo{ 209276023Sandrew struct lpc_intc_softc *sc = intc_softc; 210239278Sgonzo int reg; 211239278Sgonzo int nb = (int)data; 212239278Sgonzo uint32_t value; 213239278Sgonzo 214239278Sgonzo if (nb > 63) { 215239278Sgonzo nb -= 64; 216239278Sgonzo reg = LPC_INTC_SIC2_RSR; 217239278Sgonzo } else if (nb > 31) { 218239278Sgonzo nb -= 32; 219239278Sgonzo reg = LPC_INTC_SIC1_RSR; 220239278Sgonzo } else 221239278Sgonzo reg = LPC_INTC_MIC_RSR; 222239278Sgonzo 223239278Sgonzo /* Set bit in RSR register */ 224276023Sandrew value = intc_read_4(sc, reg); 225239278Sgonzo value |= (1 << nb); 226276023Sandrew intc_write_4(sc, reg, value); 227239278Sgonzo 228239278Sgonzo} 229239278Sgonzo 230298068Sandrew#ifndef INTRNG 231239278Sgonzostatic int 232239278Sgonzofdt_pic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, 233239278Sgonzo int *pol) 234239278Sgonzo{ 235239278Sgonzo if (!fdt_is_compatible(node, "lpc,pic")) 236239278Sgonzo return (ENXIO); 237239278Sgonzo 238239278Sgonzo *interrupt = fdt32_to_cpu(intr[0]); 239239278Sgonzo *trig = INTR_TRIGGER_CONFORM; 240239278Sgonzo *pol = INTR_POLARITY_CONFORM; 241239278Sgonzo return (0); 242239278Sgonzo} 243239278Sgonzo 244239278Sgonzofdt_pic_decode_t fdt_pic_table[] = { 245239278Sgonzo &fdt_pic_decode_ic, 246239278Sgonzo NULL 247239278Sgonzo}; 248295509Sandrew#endif 249