1/*- 2 * Copyright (c) 2010 Jakub Wojciech Klama <jceel@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD$"); 30 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/bus.h> 34#include <sys/kernel.h> 35#include <sys/module.h> 36#include <sys/malloc.h> 37#include <sys/rman.h> 38#include <sys/timetc.h> 39#include <machine/bus.h> 40#include <machine/intr.h> 41 42#include <dev/fdt/fdt_common.h> 43#include <dev/ofw/openfirm.h> 44 45#include <dev/ofw/ofw_bus.h> 46#include <dev/ofw/ofw_bus_subr.h> 47 48#include <arm/lpc/lpcreg.h> 49 50struct lpc_intc_softc { 51 struct resource * li_res; 52 bus_space_tag_t li_bst; 53 bus_space_handle_t li_bsh; 54}; 55 56static int lpc_intc_probe(device_t); 57static int lpc_intc_attach(device_t); 58static void lpc_intc_eoi(void *); 59 60static struct lpc_intc_softc *intc_softc = NULL; 61 62#define intc_read_4(reg) \ 63 bus_space_read_4(intc_softc->li_bst, intc_softc->li_bsh, reg) 64#define intc_write_4(reg, val) \ 65 bus_space_write_4(intc_softc->li_bst, intc_softc->li_bsh, reg, val) 66 67static int 68lpc_intc_probe(device_t dev) 69{ 70 71 if (!ofw_bus_is_compatible(dev, "lpc,pic")) 72 return (ENXIO); 73 74 device_set_desc(dev, "LPC32x0 Interrupt Controller"); 75 return (BUS_PROBE_DEFAULT); 76} 77 78static int 79lpc_intc_attach(device_t dev) 80{ 81 struct lpc_intc_softc *sc = device_get_softc(dev); 82 int rid = 0; 83 84 if (intc_softc) 85 return (ENXIO); 86 87 sc->li_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 88 RF_ACTIVE); 89 if (!sc->li_res) { 90 device_printf(dev, "could not alloc resources\n"); 91 return (ENXIO); 92 } 93 94 sc->li_bst = rman_get_bustag(sc->li_res); 95 sc->li_bsh = rman_get_bushandle(sc->li_res); 96 intc_softc = sc; 97 arm_post_filter = lpc_intc_eoi; 98 99 /* Clear interrupt status registers and disable all interrupts */ 100 intc_write_4(LPC_INTC_MIC_ER, 0); 101 intc_write_4(LPC_INTC_SIC1_ER, 0); 102 intc_write_4(LPC_INTC_SIC2_ER, 0); 103 intc_write_4(LPC_INTC_MIC_RSR, ~0); 104 intc_write_4(LPC_INTC_SIC1_RSR, ~0); 105 intc_write_4(LPC_INTC_SIC2_RSR, ~0); 106 return (0); 107} 108 109static device_method_t lpc_intc_methods[] = { 110 DEVMETHOD(device_probe, lpc_intc_probe), 111 DEVMETHOD(device_attach, lpc_intc_attach), 112 { 0, 0 } 113}; 114 115static driver_t lpc_intc_driver = { 116 "pic", 117 lpc_intc_methods, 118 sizeof(struct lpc_intc_softc), 119}; 120 121static devclass_t lpc_intc_devclass; 122 123DRIVER_MODULE(pic, simplebus, lpc_intc_driver, lpc_intc_devclass, 0, 0); 124 125int 126arm_get_next_irq(int last) 127{ 128 uint32_t value; 129 int i; 130 131 /* IRQs 0-31 are mapped to LPC_INTC_MIC_SR */ 132 value = intc_read_4(LPC_INTC_MIC_SR); 133 for (i = 0; i < 32; i++) { 134 if (value & (1 << i)) 135 return (i); 136 } 137 138 /* IRQs 32-63 are mapped to LPC_INTC_SIC1_SR */ 139 value = intc_read_4(LPC_INTC_SIC1_SR); 140 for (i = 0; i < 32; i++) { 141 if (value & (1 << i)) 142 return (i + 32); 143 } 144 145 /* IRQs 64-95 are mapped to LPC_INTC_SIC2_SR */ 146 value = intc_read_4(LPC_INTC_SIC2_SR); 147 for (i = 0; i < 32; i++) { 148 if (value & (1 << i)) 149 return (i + 64); 150 } 151 152 return (-1); 153} 154 155void 156arm_mask_irq(uintptr_t nb) 157{ 158 int reg; 159 uint32_t value; 160 161 /* Make sure that interrupt isn't active already */ 162 lpc_intc_eoi((void *)nb); 163 164 if (nb > 63) { 165 nb -= 64; 166 reg = LPC_INTC_SIC2_ER; 167 } else if (nb > 31) { 168 nb -= 32; 169 reg = LPC_INTC_SIC1_ER; 170 } else 171 reg = LPC_INTC_MIC_ER; 172 173 /* Clear bit in ER register */ 174 value = intc_read_4(reg); 175 value &= ~(1 << nb); 176 intc_write_4(reg, value); 177} 178 179void 180arm_unmask_irq(uintptr_t nb) 181{ 182 int reg; 183 uint32_t value; 184 185 if (nb > 63) { 186 nb -= 64; 187 reg = LPC_INTC_SIC2_ER; 188 } else if (nb > 31) { 189 nb -= 32; 190 reg = LPC_INTC_SIC1_ER; 191 } else 192 reg = LPC_INTC_MIC_ER; 193 194 /* Set bit in ER register */ 195 value = intc_read_4(reg); 196 value |= (1 << nb); 197 intc_write_4(reg, value); 198} 199 200static void 201lpc_intc_eoi(void *data) 202{ 203 int reg; 204 int nb = (int)data; 205 uint32_t value; 206 207 if (nb > 63) { 208 nb -= 64; 209 reg = LPC_INTC_SIC2_RSR; 210 } else if (nb > 31) { 211 nb -= 32; 212 reg = LPC_INTC_SIC1_RSR; 213 } else 214 reg = LPC_INTC_MIC_RSR; 215 216 /* Set bit in RSR register */ 217 value = intc_read_4(reg); 218 value |= (1 << nb); 219 intc_write_4(reg, value); 220 221} 222 223struct fdt_fixup_entry fdt_fixup_table[] = { 224 { NULL, NULL } 225}; 226 227static int 228fdt_pic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, 229 int *pol) 230{ 231 if (!fdt_is_compatible(node, "lpc,pic")) 232 return (ENXIO); 233 234 *interrupt = fdt32_to_cpu(intr[0]); 235 *trig = INTR_TRIGGER_CONFORM; 236 *pol = INTR_POLARITY_CONFORM; 237 return (0); 238} 239 240fdt_pic_decode_t fdt_pic_table[] = { 241 &fdt_pic_decode_ic, 242 NULL 243}; 244