lpc_intc.c revision 266152
150472Speter/*- 231900Swosch * Copyright (c) 2010 Jakub Wojciech Klama <jceel@FreeBSD.org> 343805Swosch * All rights reserved. 443805Swosch * 531900Swosch * Redistribution and use in source and binary forms, with or without 631900Swosch * modification, are permitted provided that the following conditions 731900Swosch * are met: 861Snate * 1. Redistributions of source code must retain the above copyright 95498Sache * notice, this list of conditions and the following disclaimer. 1094Snate * 2. Redistributions in binary form must reproduce the above copyright 1194Snate * notice, this list of conditions and the following disclaimer in the 1241155Swosch * documentation and/or other materials provided with the distribution. 1394Snate * 14130Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15130Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16107Sconklin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1794Snate * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 185498Sache * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 195498Sache * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 205498Sache * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2131900Swosch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2231900Swosch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2331900Swosch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2431900Swosch * SUCH DAMAGE. 2531928Swosch * 2631928Swosch */ 2731928Swosch 2839886Sobrien#include <sys/cdefs.h> 2931900Swosch__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/lpc_intc.c 266152 2014-05-15 16:11:06Z ian $"); 3031900Swosch 3131900Swosch#include <sys/param.h> 3231900Swosch#include <sys/systm.h> 3331900Swosch#include <sys/bus.h> 34107Sconklin#include <sys/kernel.h> 35107Sconklin#include <sys/module.h> 3694Snate#include <sys/malloc.h> 3761Snate#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_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(LPC_INTC_MIC_ER, 0); 104 intc_write_4(LPC_INTC_SIC1_ER, 0); 105 intc_write_4(LPC_INTC_SIC2_ER, 0); 106 intc_write_4(LPC_INTC_MIC_RSR, ~0); 107 intc_write_4(LPC_INTC_SIC1_RSR, ~0); 108 intc_write_4(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 uint32_t value; 132 int i; 133 134 /* IRQs 0-31 are mapped to LPC_INTC_MIC_SR */ 135 value = intc_read_4(LPC_INTC_MIC_SR); 136 for (i = 0; i < 32; i++) { 137 if (value & (1 << i)) 138 return (i); 139 } 140 141 /* IRQs 32-63 are mapped to LPC_INTC_SIC1_SR */ 142 value = intc_read_4(LPC_INTC_SIC1_SR); 143 for (i = 0; i < 32; i++) { 144 if (value & (1 << i)) 145 return (i + 32); 146 } 147 148 /* IRQs 64-95 are mapped to LPC_INTC_SIC2_SR */ 149 value = intc_read_4(LPC_INTC_SIC2_SR); 150 for (i = 0; i < 32; i++) { 151 if (value & (1 << i)) 152 return (i + 64); 153 } 154 155 return (-1); 156} 157 158void 159arm_mask_irq(uintptr_t nb) 160{ 161 int reg; 162 uint32_t value; 163 164 /* Make sure that interrupt isn't active already */ 165 lpc_intc_eoi((void *)nb); 166 167 if (nb > 63) { 168 nb -= 64; 169 reg = LPC_INTC_SIC2_ER; 170 } else if (nb > 31) { 171 nb -= 32; 172 reg = LPC_INTC_SIC1_ER; 173 } else 174 reg = LPC_INTC_MIC_ER; 175 176 /* Clear bit in ER register */ 177 value = intc_read_4(reg); 178 value &= ~(1 << nb); 179 intc_write_4(reg, value); 180} 181 182void 183arm_unmask_irq(uintptr_t nb) 184{ 185 int reg; 186 uint32_t value; 187 188 if (nb > 63) { 189 nb -= 64; 190 reg = LPC_INTC_SIC2_ER; 191 } else if (nb > 31) { 192 nb -= 32; 193 reg = LPC_INTC_SIC1_ER; 194 } else 195 reg = LPC_INTC_MIC_ER; 196 197 /* Set bit in ER register */ 198 value = intc_read_4(reg); 199 value |= (1 << nb); 200 intc_write_4(reg, value); 201} 202 203static void 204lpc_intc_eoi(void *data) 205{ 206 int reg; 207 int nb = (int)data; 208 uint32_t value; 209 210 if (nb > 63) { 211 nb -= 64; 212 reg = LPC_INTC_SIC2_RSR; 213 } else if (nb > 31) { 214 nb -= 32; 215 reg = LPC_INTC_SIC1_RSR; 216 } else 217 reg = LPC_INTC_MIC_RSR; 218 219 /* Set bit in RSR register */ 220 value = intc_read_4(reg); 221 value |= (1 << nb); 222 intc_write_4(reg, value); 223 224} 225 226struct fdt_fixup_entry fdt_fixup_table[] = { 227 { NULL, NULL } 228}; 229 230static int 231fdt_pic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, 232 int *pol) 233{ 234 if (!fdt_is_compatible(node, "lpc,pic")) 235 return (ENXIO); 236 237 *interrupt = fdt32_to_cpu(intr[0]); 238 *trig = INTR_TRIGGER_CONFORM; 239 *pol = INTR_POLARITY_CONFORM; 240 return (0); 241} 242 243fdt_pic_decode_t fdt_pic_table[] = { 244 &fdt_pic_decode_ic, 245 NULL 246}; 247