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$"); 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 62239278Sgonzo#define intc_read_4(reg) \ 63239278Sgonzo bus_space_read_4(intc_softc->li_bst, intc_softc->li_bsh, reg) 64239278Sgonzo#define intc_write_4(reg, val) \ 65239278Sgonzo bus_space_write_4(intc_softc->li_bst, intc_softc->li_bsh, reg, val) 66239278Sgonzo 67239278Sgonzostatic int 68239278Sgonzolpc_intc_probe(device_t dev) 69239278Sgonzo{ 70239278Sgonzo 71266152Sian if (!ofw_bus_status_okay(dev)) 72266152Sian return (ENXIO); 73266152Sian 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 */ 103239278Sgonzo intc_write_4(LPC_INTC_MIC_ER, 0); 104239278Sgonzo intc_write_4(LPC_INTC_SIC1_ER, 0); 105239278Sgonzo intc_write_4(LPC_INTC_SIC2_ER, 0); 106239278Sgonzo intc_write_4(LPC_INTC_MIC_RSR, ~0); 107239278Sgonzo intc_write_4(LPC_INTC_SIC1_RSR, ~0); 108239278Sgonzo intc_write_4(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{ 131239278Sgonzo uint32_t value; 132239278Sgonzo int i; 133239278Sgonzo 134239278Sgonzo /* IRQs 0-31 are mapped to LPC_INTC_MIC_SR */ 135239278Sgonzo value = intc_read_4(LPC_INTC_MIC_SR); 136239278Sgonzo for (i = 0; i < 32; i++) { 137239278Sgonzo if (value & (1 << i)) 138239278Sgonzo return (i); 139239278Sgonzo } 140239278Sgonzo 141239278Sgonzo /* IRQs 32-63 are mapped to LPC_INTC_SIC1_SR */ 142239278Sgonzo value = intc_read_4(LPC_INTC_SIC1_SR); 143239278Sgonzo for (i = 0; i < 32; i++) { 144239278Sgonzo if (value & (1 << i)) 145239278Sgonzo return (i + 32); 146239278Sgonzo } 147239278Sgonzo 148239278Sgonzo /* IRQs 64-95 are mapped to LPC_INTC_SIC2_SR */ 149239278Sgonzo value = intc_read_4(LPC_INTC_SIC2_SR); 150239278Sgonzo for (i = 0; i < 32; i++) { 151239278Sgonzo if (value & (1 << i)) 152239278Sgonzo return (i + 64); 153239278Sgonzo } 154239278Sgonzo 155239278Sgonzo return (-1); 156239278Sgonzo} 157239278Sgonzo 158239278Sgonzovoid 159239278Sgonzoarm_mask_irq(uintptr_t nb) 160239278Sgonzo{ 161239278Sgonzo int reg; 162239278Sgonzo uint32_t value; 163239278Sgonzo 164239278Sgonzo /* Make sure that interrupt isn't active already */ 165239278Sgonzo lpc_intc_eoi((void *)nb); 166239278Sgonzo 167239278Sgonzo if (nb > 63) { 168239278Sgonzo nb -= 64; 169239278Sgonzo reg = LPC_INTC_SIC2_ER; 170239278Sgonzo } else if (nb > 31) { 171239278Sgonzo nb -= 32; 172239278Sgonzo reg = LPC_INTC_SIC1_ER; 173239278Sgonzo } else 174239278Sgonzo reg = LPC_INTC_MIC_ER; 175239278Sgonzo 176239278Sgonzo /* Clear bit in ER register */ 177239278Sgonzo value = intc_read_4(reg); 178239278Sgonzo value &= ~(1 << nb); 179239278Sgonzo intc_write_4(reg, value); 180239278Sgonzo} 181239278Sgonzo 182239278Sgonzovoid 183239278Sgonzoarm_unmask_irq(uintptr_t nb) 184239278Sgonzo{ 185239278Sgonzo int reg; 186239278Sgonzo uint32_t value; 187239278Sgonzo 188239278Sgonzo if (nb > 63) { 189239278Sgonzo nb -= 64; 190239278Sgonzo reg = LPC_INTC_SIC2_ER; 191239278Sgonzo } else if (nb > 31) { 192239278Sgonzo nb -= 32; 193239278Sgonzo reg = LPC_INTC_SIC1_ER; 194239278Sgonzo } else 195239278Sgonzo reg = LPC_INTC_MIC_ER; 196239278Sgonzo 197239278Sgonzo /* Set bit in ER register */ 198239278Sgonzo value = intc_read_4(reg); 199239278Sgonzo value |= (1 << nb); 200239278Sgonzo intc_write_4(reg, value); 201239278Sgonzo} 202239278Sgonzo 203239278Sgonzostatic void 204239278Sgonzolpc_intc_eoi(void *data) 205239278Sgonzo{ 206239278Sgonzo int reg; 207239278Sgonzo int nb = (int)data; 208239278Sgonzo uint32_t value; 209239278Sgonzo 210239278Sgonzo if (nb > 63) { 211239278Sgonzo nb -= 64; 212239278Sgonzo reg = LPC_INTC_SIC2_RSR; 213239278Sgonzo } else if (nb > 31) { 214239278Sgonzo nb -= 32; 215239278Sgonzo reg = LPC_INTC_SIC1_RSR; 216239278Sgonzo } else 217239278Sgonzo reg = LPC_INTC_MIC_RSR; 218239278Sgonzo 219239278Sgonzo /* Set bit in RSR register */ 220239278Sgonzo value = intc_read_4(reg); 221239278Sgonzo value |= (1 << nb); 222239278Sgonzo intc_write_4(reg, value); 223239278Sgonzo 224239278Sgonzo} 225239278Sgonzo 226239278Sgonzostruct fdt_fixup_entry fdt_fixup_table[] = { 227239278Sgonzo { NULL, NULL } 228239278Sgonzo}; 229239278Sgonzo 230239278Sgonzostatic int 231239278Sgonzofdt_pic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, 232239278Sgonzo int *pol) 233239278Sgonzo{ 234239278Sgonzo if (!fdt_is_compatible(node, "lpc,pic")) 235239278Sgonzo return (ENXIO); 236239278Sgonzo 237239278Sgonzo *interrupt = fdt32_to_cpu(intr[0]); 238239278Sgonzo *trig = INTR_TRIGGER_CONFORM; 239239278Sgonzo *pol = INTR_POLARITY_CONFORM; 240239278Sgonzo return (0); 241239278Sgonzo} 242239278Sgonzo 243239278Sgonzofdt_pic_decode_t fdt_pic_table[] = { 244239278Sgonzo &fdt_pic_decode_ic, 245239278Sgonzo NULL 246239278Sgonzo}; 247