lpc_intc.c revision 295509
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: head/sys/arm/lpc/lpc_intc.c 295509 2016-02-11 11:49:27Z andrew $"); 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(_sc, _reg) \ 63 bus_space_read_4((_sc)->li_bst, (_sc)->li_bsh, (_reg)) 64#define intc_write_4(_sc, _reg, _val) \ 65 bus_space_write_4((_sc)->li_bst, (_sc)->li_bsh, (_reg), (_val)) 66 67static int 68lpc_intc_probe(device_t dev) 69{ 70 71 if (!ofw_bus_status_okay(dev)) 72 return (ENXIO); 73 74 if (!ofw_bus_is_compatible(dev, "lpc,pic")) 75 return (ENXIO); 76 77 device_set_desc(dev, "LPC32x0 Interrupt Controller"); 78 return (BUS_PROBE_DEFAULT); 79} 80 81static int 82lpc_intc_attach(device_t dev) 83{ 84 struct lpc_intc_softc *sc = device_get_softc(dev); 85 int rid = 0; 86 87 if (intc_softc) 88 return (ENXIO); 89 90 sc->li_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 91 RF_ACTIVE); 92 if (!sc->li_res) { 93 device_printf(dev, "could not alloc resources\n"); 94 return (ENXIO); 95 } 96 97 sc->li_bst = rman_get_bustag(sc->li_res); 98 sc->li_bsh = rman_get_bushandle(sc->li_res); 99 intc_softc = sc; 100 arm_post_filter = lpc_intc_eoi; 101 102 /* Clear interrupt status registers and disable all interrupts */ 103 intc_write_4(sc, LPC_INTC_MIC_ER, 0); 104 intc_write_4(sc, LPC_INTC_SIC1_ER, 0); 105 intc_write_4(sc, LPC_INTC_SIC2_ER, 0); 106 intc_write_4(sc, LPC_INTC_MIC_RSR, ~0); 107 intc_write_4(sc, LPC_INTC_SIC1_RSR, ~0); 108 intc_write_4(sc, LPC_INTC_SIC2_RSR, ~0); 109 return (0); 110} 111 112static device_method_t lpc_intc_methods[] = { 113 DEVMETHOD(device_probe, lpc_intc_probe), 114 DEVMETHOD(device_attach, lpc_intc_attach), 115 { 0, 0 } 116}; 117 118static driver_t lpc_intc_driver = { 119 "pic", 120 lpc_intc_methods, 121 sizeof(struct lpc_intc_softc), 122}; 123 124static devclass_t lpc_intc_devclass; 125 126DRIVER_MODULE(pic, simplebus, lpc_intc_driver, lpc_intc_devclass, 0, 0); 127 128int 129arm_get_next_irq(int last) 130{ 131 struct lpc_intc_softc *sc = intc_softc; 132 uint32_t value; 133 int i; 134 135 /* IRQs 0-31 are mapped to LPC_INTC_MIC_SR */ 136 value = intc_read_4(sc, LPC_INTC_MIC_SR); 137 for (i = 0; i < 32; i++) { 138 if (value & (1 << i)) 139 return (i); 140 } 141 142 /* IRQs 32-63 are mapped to LPC_INTC_SIC1_SR */ 143 value = intc_read_4(sc, LPC_INTC_SIC1_SR); 144 for (i = 0; i < 32; i++) { 145 if (value & (1 << i)) 146 return (i + 32); 147 } 148 149 /* IRQs 64-95 are mapped to LPC_INTC_SIC2_SR */ 150 value = intc_read_4(sc, LPC_INTC_SIC2_SR); 151 for (i = 0; i < 32; i++) { 152 if (value & (1 << i)) 153 return (i + 64); 154 } 155 156 return (-1); 157} 158 159void 160arm_mask_irq(uintptr_t nb) 161{ 162 struct lpc_intc_softc *sc = intc_softc; 163 int reg; 164 uint32_t value; 165 166 /* Make sure that interrupt isn't active already */ 167 lpc_intc_eoi((void *)nb); 168 169 if (nb > 63) { 170 nb -= 64; 171 reg = LPC_INTC_SIC2_ER; 172 } else if (nb > 31) { 173 nb -= 32; 174 reg = LPC_INTC_SIC1_ER; 175 } else 176 reg = LPC_INTC_MIC_ER; 177 178 /* Clear bit in ER register */ 179 value = intc_read_4(sc, reg); 180 value &= ~(1 << nb); 181 intc_write_4(sc, reg, value); 182} 183 184void 185arm_unmask_irq(uintptr_t nb) 186{ 187 struct lpc_intc_softc *sc = intc_softc; 188 int reg; 189 uint32_t value; 190 191 if (nb > 63) { 192 nb -= 64; 193 reg = LPC_INTC_SIC2_ER; 194 } else if (nb > 31) { 195 nb -= 32; 196 reg = LPC_INTC_SIC1_ER; 197 } else 198 reg = LPC_INTC_MIC_ER; 199 200 /* Set bit in ER register */ 201 value = intc_read_4(sc, reg); 202 value |= (1 << nb); 203 intc_write_4(sc, reg, value); 204} 205 206static void 207lpc_intc_eoi(void *data) 208{ 209 struct lpc_intc_softc *sc = intc_softc; 210 int reg; 211 int nb = (int)data; 212 uint32_t value; 213 214 if (nb > 63) { 215 nb -= 64; 216 reg = LPC_INTC_SIC2_RSR; 217 } else if (nb > 31) { 218 nb -= 32; 219 reg = LPC_INTC_SIC1_RSR; 220 } else 221 reg = LPC_INTC_MIC_RSR; 222 223 /* Set bit in RSR register */ 224 value = intc_read_4(sc, reg); 225 value |= (1 << nb); 226 intc_write_4(sc, reg, value); 227 228} 229 230struct fdt_fixup_entry fdt_fixup_table[] = { 231 { NULL, NULL } 232}; 233 234#ifndef ARM_INTRNG 235static int 236fdt_pic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, 237 int *pol) 238{ 239 if (!fdt_is_compatible(node, "lpc,pic")) 240 return (ENXIO); 241 242 *interrupt = fdt32_to_cpu(intr[0]); 243 *trig = INTR_TRIGGER_CONFORM; 244 *pol = INTR_POLARITY_CONFORM; 245 return (0); 246} 247 248fdt_pic_decode_t fdt_pic_table[] = { 249 &fdt_pic_decode_ic, 250 NULL 251}; 252#endif 253