1/*- 2 * Copyright (c) 2012, 2013 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Oleksandr Rybalko under sponsorship 6 * from the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: stable/11/sys/arm/freescale/imx/tzic.c 323403 2017-09-10 23:41:23Z ian $"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/bus.h> 36#include <sys/kernel.h> 37#include <sys/module.h> 38#include <sys/rman.h> 39#include <sys/proc.h> 40#include <sys/lock.h> 41#include <sys/mutex.h> 42 43#include <machine/bus.h> 44#include <machine/intr.h> 45 46#include <dev/fdt/fdt_common.h> 47#include <dev/ofw/openfirm.h> 48#include <dev/ofw/ofw_bus.h> 49#include <dev/ofw/ofw_bus_subr.h> 50 51#include <arm/freescale/imx/imx51_tzicreg.h> 52 53#include "pic_if.h" 54 55#define TZIC_NIRQS 128 56 57struct tzic_irqsrc { 58 struct intr_irqsrc isrc; 59 u_int irq; 60}; 61 62struct tzic_softc { 63 device_t dev; 64 struct resource *tzicregs; 65 struct tzic_irqsrc isrcs[TZIC_NIRQS]; 66}; 67 68static struct tzic_softc *tzic_sc; 69 70static inline uint32_t 71tzic_read_4(struct tzic_softc *sc, int reg) 72{ 73 74 return (bus_read_4(sc->tzicregs, reg)); 75} 76 77static inline void 78tzic_write_4(struct tzic_softc *sc, int reg, uint32_t val) 79{ 80 81 bus_write_4(sc->tzicregs, reg, val); 82} 83 84static inline void 85tzic_irq_eoi(struct tzic_softc *sc) 86{ 87 88 tzic_write_4(sc, TZIC_PRIOMASK, 0xff); 89} 90 91static inline void 92tzic_irq_mask(struct tzic_softc *sc, u_int irq) 93{ 94 95 tzic_write_4(sc, TZIC_ENCLEAR(irq >> 5), (1u << (irq & 0x1f))); 96} 97 98static inline void 99tzic_irq_unmask(struct tzic_softc *sc, u_int irq) 100{ 101 102 tzic_write_4(sc, TZIC_ENSET(irq >> 5), (1u << (irq & 0x1f))); 103} 104 105static int 106tzic_intr(void *arg) 107{ 108 struct tzic_softc *sc = arg; 109 int b, i, irq; 110 uint32_t pending; 111 112 /* Get active interrupt */ 113 for (i = 0; i < TZIC_NIRQS / 32; ++i) { 114 pending = tzic_read_4(sc, TZIC_PND(i)); 115 if ((b = 31 - __builtin_clz(pending)) < 0) 116 continue; 117 irq = i * 32 + b; 118 tzic_write_4(sc, TZIC_PRIOMASK, 0); 119 if (intr_isrc_dispatch(&sc->isrcs[irq].isrc, 120 curthread->td_intr_frame) != 0) { 121 tzic_irq_mask(sc, irq); 122 tzic_irq_eoi(sc); 123 arm_irq_memory_barrier(irq); 124 if (bootverbose) { 125 device_printf(sc->dev, 126 "Stray irq %u disabled\n", irq); 127 } 128 } 129 return (FILTER_HANDLED); 130 } 131 132 if (bootverbose) 133 device_printf(sc->dev, "Spurious interrupt detected\n"); 134 135 return (FILTER_HANDLED); 136} 137 138static void 139tzic_enable_intr(device_t dev, struct intr_irqsrc *isrc) 140{ 141 u_int irq = ((struct tzic_irqsrc *)isrc)->irq; 142 struct tzic_softc *sc = device_get_softc(dev); 143 144 arm_irq_memory_barrier(irq); 145 tzic_irq_unmask(sc, irq); 146} 147 148static void 149tzic_disable_intr(device_t dev, struct intr_irqsrc *isrc) 150{ 151 u_int irq = ((struct tzic_irqsrc *)isrc)->irq; 152 struct tzic_softc *sc = device_get_softc(dev); 153 154 tzic_irq_mask(sc, irq); 155} 156 157static int 158tzic_map_intr(device_t dev, struct intr_map_data *data, 159 struct intr_irqsrc **isrcp) 160{ 161 struct intr_map_data_fdt *daf; 162 struct tzic_softc *sc; 163 164 if (data->type != INTR_MAP_DATA_FDT) 165 return (ENOTSUP); 166 167 daf = (struct intr_map_data_fdt *)data; 168 if (daf->ncells != 1 || daf->cells[0] >= TZIC_NIRQS) 169 return (EINVAL); 170 171 sc = device_get_softc(dev); 172 *isrcp = &sc->isrcs[daf->cells[0]].isrc; 173 174 return (0); 175} 176 177static void 178tzic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) 179{ 180 struct tzic_softc *sc = device_get_softc(dev); 181 182 tzic_irq_mask(sc, ((struct tzic_irqsrc *)isrc)->irq); 183 tzic_irq_eoi(sc); 184} 185 186static void 187tzic_post_ithread(device_t dev, struct intr_irqsrc *isrc) 188{ 189 190 tzic_enable_intr(dev, isrc); 191} 192 193static void 194tzic_post_filter(device_t dev, struct intr_irqsrc *isrc) 195{ 196 197 tzic_irq_eoi(device_get_softc(dev)); 198} 199 200static int 201tzic_pic_attach(struct tzic_softc *sc) 202{ 203 struct intr_pic *pic; 204 const char *name; 205 intptr_t xref; 206 int error; 207 u_int irq; 208 209 name = device_get_nameunit(sc->dev); 210 for (irq = 0; irq < TZIC_NIRQS; irq++) { 211 sc->isrcs[irq].irq = irq; 212 error = intr_isrc_register(&sc->isrcs[irq].isrc, 213 sc->dev, 0, "%s,%u", name, irq); 214 if (error != 0) 215 return (error); 216 } 217 218 xref = OF_xref_from_node(ofw_bus_get_node(sc->dev)); 219 pic = intr_pic_register(sc->dev, xref); 220 if (pic == NULL) 221 return (ENXIO); 222 223 return (intr_pic_claim_root(sc->dev, xref, tzic_intr, sc, 0)); 224} 225 226static int 227tzic_probe(device_t dev) 228{ 229 230 if (!ofw_bus_status_okay(dev)) 231 return (ENXIO); 232 233 if (ofw_bus_is_compatible(dev, "fsl,tzic")) { 234 device_set_desc(dev, "TrustZone Interrupt Controller"); 235 return (BUS_PROBE_DEFAULT); 236 } 237 return (ENXIO); 238} 239 240static int 241tzic_attach(device_t dev) 242{ 243 struct tzic_softc *sc = device_get_softc(dev); 244 int i; 245 246 if (tzic_sc) 247 return (ENXIO); 248 tzic_sc = sc; 249 sc->dev = dev; 250 251 i = 0; 252 sc->tzicregs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &i, 253 RF_ACTIVE); 254 if (sc->tzicregs == NULL) { 255 device_printf(dev, "could not allocate resources\n"); 256 return (ENXIO); 257 } 258 259 /* route all interrupts to IRQ. secure interrupts are for FIQ */ 260 for (i = 0; i < 4; i++) 261 tzic_write_4(sc, TZIC_INTSEC(i), 0xffffffff); 262 263 /* disable all interrupts */ 264 for (i = 0; i < 4; i++) 265 tzic_write_4(sc, TZIC_ENCLEAR(i), 0xffffffff); 266 267 /* Set all interrupts to priority 0 (max). */ 268 for (i = 0; i < 128 / 4; ++i) 269 tzic_write_4(sc, TZIC_PRIORITY(i), 0); 270 271 /* 272 * Set priority mask to lowest (unmasked) prio, set synchronizer to 273 * low-latency mode (as opposed to low-power), enable the controller. 274 */ 275 tzic_write_4(sc, TZIC_PRIOMASK, 0xff); 276 tzic_write_4(sc, TZIC_SYNCCTRL, 0); 277 tzic_write_4(sc, TZIC_INTCNTL, INTCNTL_NSEN_MASK|INTCNTL_NSEN|INTCNTL_EN); 278 279 /* Register as a root pic. */ 280 if (tzic_pic_attach(sc) != 0) { 281 device_printf(dev, "could not attach PIC\n"); 282 return (ENXIO); 283 } 284 285 return (0); 286} 287 288static device_method_t tzic_methods[] = { 289 DEVMETHOD(device_probe, tzic_probe), 290 DEVMETHOD(device_attach, tzic_attach), 291 292 DEVMETHOD(pic_disable_intr, tzic_disable_intr), 293 DEVMETHOD(pic_enable_intr, tzic_enable_intr), 294 DEVMETHOD(pic_map_intr, tzic_map_intr), 295 DEVMETHOD(pic_post_filter, tzic_post_filter), 296 DEVMETHOD(pic_post_ithread, tzic_post_ithread), 297 DEVMETHOD(pic_pre_ithread, tzic_pre_ithread), 298 299 DEVMETHOD_END 300}; 301 302static driver_t tzic_driver = { 303 "tzic", 304 tzic_methods, 305 sizeof(struct tzic_softc), 306}; 307 308static devclass_t tzic_devclass; 309 310EARLY_DRIVER_MODULE(tzic, ofwbus, tzic_driver, tzic_devclass, 0, 0, 311 BUS_PASS_INTERRUPT); 312