exynos5_combiner.c revision 263936
1/*- 2 * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 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 * Samsung Exynos 5 Interrupt Combiner 29 * Chapter 7, Exynos 5 Dual User's Manual Public Rev 1.00 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD: head/sys/arm/samsung/exynos/exynos5_combiner.c 263936 2014-03-30 15:22:36Z br $"); 34 35#include <sys/param.h> 36#include <sys/systm.h> 37#include <sys/bus.h> 38#include <sys/kernel.h> 39#include <sys/module.h> 40#include <sys/malloc.h> 41#include <sys/rman.h> 42#include <sys/timeet.h> 43#include <sys/timetc.h> 44#include <sys/watchdog.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 <machine/bus.h> 52#include <machine/fdt.h> 53#include <machine/cpu.h> 54#include <machine/intr.h> 55 56#include <arm/samsung/exynos/exynos5_common.h> 57#include <arm/samsung/exynos/exynos5_combiner.h> 58 59#define NGRP 32 60#define ITABLE_LEN 24 61 62#define IESR(n) (0x10 * n + 0x0) /* Interrupt enable set */ 63#define IECR(n) (0x10 * n + 0x4) /* Interrupt enable clear */ 64#define ISTR(n) (0x10 * n + 0x8) /* Interrupt status */ 65#define IMSR(n) (0x10 * n + 0xC) /* Interrupt masked status */ 66#define CIPSR 0x100 /* Combined interrupt pending */ 67 68struct combiner_softc { 69 struct resource *res[1 + NGRP]; 70 bus_space_tag_t bst; 71 bus_space_handle_t bsh; 72 void *ih[NGRP]; 73 device_t dev; 74}; 75 76struct combiner_softc *combiner_sc; 77 78static struct resource_spec combiner_spec[] = { 79 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 80 { SYS_RES_IRQ, 0, RF_ACTIVE }, 81 { SYS_RES_IRQ, 1, RF_ACTIVE }, 82 { SYS_RES_IRQ, 2, RF_ACTIVE }, 83 { SYS_RES_IRQ, 3, RF_ACTIVE }, 84 { SYS_RES_IRQ, 4, RF_ACTIVE }, 85 { SYS_RES_IRQ, 5, RF_ACTIVE }, 86 { SYS_RES_IRQ, 6, RF_ACTIVE }, 87 { SYS_RES_IRQ, 7, RF_ACTIVE }, 88 { SYS_RES_IRQ, 8, RF_ACTIVE }, 89 { SYS_RES_IRQ, 9, RF_ACTIVE }, 90 { SYS_RES_IRQ, 10, RF_ACTIVE }, 91 { SYS_RES_IRQ, 11, RF_ACTIVE }, 92 { SYS_RES_IRQ, 12, RF_ACTIVE }, 93 { SYS_RES_IRQ, 13, RF_ACTIVE }, 94 { SYS_RES_IRQ, 14, RF_ACTIVE }, 95 { SYS_RES_IRQ, 15, RF_ACTIVE }, 96 { SYS_RES_IRQ, 16, RF_ACTIVE }, 97 { SYS_RES_IRQ, 17, RF_ACTIVE }, 98 { SYS_RES_IRQ, 18, RF_ACTIVE }, 99 { SYS_RES_IRQ, 19, RF_ACTIVE }, 100 { SYS_RES_IRQ, 20, RF_ACTIVE }, 101 { SYS_RES_IRQ, 21, RF_ACTIVE }, 102 { SYS_RES_IRQ, 22, RF_ACTIVE }, 103 { SYS_RES_IRQ, 23, RF_ACTIVE }, 104 { SYS_RES_IRQ, 24, RF_ACTIVE }, 105 { SYS_RES_IRQ, 25, RF_ACTIVE }, 106 { SYS_RES_IRQ, 26, RF_ACTIVE }, 107 { SYS_RES_IRQ, 27, RF_ACTIVE }, 108 { SYS_RES_IRQ, 28, RF_ACTIVE }, 109 { SYS_RES_IRQ, 29, RF_ACTIVE }, 110 { SYS_RES_IRQ, 30, RF_ACTIVE }, 111 { SYS_RES_IRQ, 31, RF_ACTIVE }, 112 { -1, 0 } 113}; 114 115struct combiner_entry { 116 int combiner_id; 117 int bit; 118 char *source_name; 119}; 120 121static struct combiner_entry interrupt_table[ITABLE_LEN] = { 122 { 63, 1, "EINT[15]" }, 123 { 63, 0, "EINT[14]" }, 124 { 62, 1, "EINT[13]" }, 125 { 62, 0, "EINT[12]" }, 126 { 61, 1, "EINT[11]" }, 127 { 61, 0, "EINT[10]" }, 128 { 60, 1, "EINT[9]" }, 129 { 60, 0, "EINT[8]" }, 130 { 59, 1, "EINT[7]" }, 131 { 59, 0, "EINT[6]" }, 132 { 58, 1, "EINT[5]" }, 133 { 58, 0, "EINT[4]" }, 134 { 57, 3, "MCT_G3" }, 135 { 57, 2, "MCT_G2" }, 136 { 57, 1, "EINT[3]" }, 137 { 57, 0, "EINT[2]" }, 138 { 56, 6, "SYSMMU_G2D[1]" }, 139 { 56, 5, "SYSMMU_G2D[0]" }, 140 { 56, 2, "SYSMMU_FIMC_LITE1[1]" }, 141 { 56, 1, "SYSMMU_FIMC_LITE1[0]" }, 142 { 56, 0, "EINT[1]" }, 143 { 55, 4, "MCT_G1" }, 144 { 55, 3, "MCT_G0" }, 145 { 55, 0, "EINT[0]" }, 146 147 /* TODO: add groups 54-32 */ 148}; 149 150struct combined_intr { 151 uint32_t enabled; 152 void (*ih) (void *); 153 void *ih_user; 154}; 155 156static struct combined_intr intr_map[32][8]; 157 158static void 159combiner_intr(void *arg) 160{ 161 struct combiner_softc *sc; 162 void (*ih) (void *); 163 void *ih_user; 164 int enabled; 165 int intrs; 166 int shift; 167 int cirq; 168 int grp; 169 int i,n; 170 171 sc = arg; 172 173 intrs = READ4(sc, CIPSR); 174 for (grp = 0; grp < 32; grp++) { 175 if (intrs & (1 << grp)) { 176 n = (grp / 4); 177 shift = (grp % 4) * 8; 178 179 cirq = READ4(sc, ISTR(n)); 180 for (i = 0; i < 8; i++) { 181 if (cirq & (1 << (i + shift))) { 182 ih = intr_map[grp][i].ih; 183 ih_user = intr_map[grp][i].ih_user; 184 enabled = intr_map[grp][i].enabled; 185 if (enabled && (ih != NULL)) { 186 ih(ih_user); 187 } 188 } 189 } 190 } 191 } 192} 193 194void 195combiner_setup_intr(char *source_name, void (*ih)(void *), void *ih_user) 196{ 197 struct combiner_entry *entry; 198 struct combined_intr *cirq; 199 struct combiner_softc *sc; 200 int shift; 201 int reg; 202 int grp; 203 int n; 204 int i; 205 206 sc = combiner_sc; 207 208 if (sc == NULL) { 209 device_printf(sc->dev, "Error: combiner is not attached\n"); 210 return; 211 } 212 213 entry = NULL; 214 215 for (i = 0; i < ITABLE_LEN; i++) { 216 if (strcmp(interrupt_table[i].source_name, source_name) == 0) { 217 entry = &interrupt_table[i]; 218 } 219 } 220 221 if (entry == NULL) { 222 device_printf(sc->dev, "Can't find interrupt name %s\n", 223 source_name); 224 return; 225 } 226 227#if 0 228 device_printf(sc->dev, "Setting up interrupt %s\n", source_name); 229#endif 230 231 grp = entry->combiner_id - 32; 232 233 cirq = &intr_map[grp][entry->bit]; 234 cirq->enabled = 1; 235 cirq->ih = ih; 236 cirq->ih_user = ih_user; 237 238 n = grp / 4; 239 shift = (grp % 4) * 8 + entry->bit; 240 241 reg = (1 << shift); 242 WRITE4(sc, IESR(n), reg); 243} 244 245static int 246combiner_probe(device_t dev) 247{ 248 249 if (!ofw_bus_is_compatible(dev, "exynos,combiner")) 250 return (ENXIO); 251 252 device_set_desc(dev, "Samsung Exynos 5 Interrupt Combiner"); 253 return (BUS_PROBE_DEFAULT); 254} 255 256static int 257combiner_attach(device_t dev) 258{ 259 struct combiner_softc *sc; 260 int err; 261 int i; 262 263 sc = device_get_softc(dev); 264 sc->dev = dev; 265 266 if (bus_alloc_resources(dev, combiner_spec, sc->res)) { 267 device_printf(dev, "could not allocate resources\n"); 268 return (ENXIO); 269 } 270 271 /* Memory interface */ 272 sc->bst = rman_get_bustag(sc->res[0]); 273 sc->bsh = rman_get_bushandle(sc->res[0]); 274 275 combiner_sc = sc; 276 277 /* Setup interrupt handler */ 278 for (i = 0; i < NGRP; i++) { 279 err = bus_setup_intr(dev, sc->res[1+i], INTR_TYPE_BIO | \ 280 INTR_MPSAFE, NULL, combiner_intr, sc, &sc->ih[i]); 281 if (err) { 282 device_printf(dev, "Unable to alloc int resource.\n"); 283 return (ENXIO); 284 } 285 } 286 287 return (0); 288} 289 290static device_method_t combiner_methods[] = { 291 DEVMETHOD(device_probe, combiner_probe), 292 DEVMETHOD(device_attach, combiner_attach), 293 { 0, 0 } 294}; 295 296static driver_t combiner_driver = { 297 "combiner", 298 combiner_methods, 299 sizeof(struct combiner_softc), 300}; 301 302static devclass_t combiner_devclass; 303 304DRIVER_MODULE(combiner, simplebus, combiner_driver, combiner_devclass, 0, 0); 305