1263936Sbr/*- 2263936Sbr * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 3263936Sbr * All rights reserved. 4263936Sbr * 5263936Sbr * Redistribution and use in source and binary forms, with or without 6263936Sbr * modification, are permitted provided that the following conditions 7263936Sbr * are met: 8263936Sbr * 1. Redistributions of source code must retain the above copyright 9263936Sbr * notice, this list of conditions and the following disclaimer. 10263936Sbr * 2. Redistributions in binary form must reproduce the above copyright 11263936Sbr * notice, this list of conditions and the following disclaimer in the 12263936Sbr * documentation and/or other materials provided with the distribution. 13263936Sbr * 14263936Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15263936Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16263936Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17263936Sbr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18263936Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19263936Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20263936Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21263936Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22263936Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23263936Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24263936Sbr * SUCH DAMAGE. 25263936Sbr */ 26263936Sbr 27263936Sbr/* 28263936Sbr * Samsung Exynos 5 Interrupt Combiner 29263936Sbr * Chapter 7, Exynos 5 Dual User's Manual Public Rev 1.00 30263936Sbr */ 31263936Sbr 32263936Sbr#include <sys/cdefs.h> 33263936Sbr__FBSDID("$FreeBSD$"); 34263936Sbr 35263936Sbr#include <sys/param.h> 36263936Sbr#include <sys/systm.h> 37263936Sbr#include <sys/bus.h> 38263936Sbr#include <sys/kernel.h> 39263936Sbr#include <sys/module.h> 40263936Sbr#include <sys/malloc.h> 41263936Sbr#include <sys/rman.h> 42263936Sbr#include <sys/timeet.h> 43263936Sbr#include <sys/timetc.h> 44263936Sbr#include <sys/watchdog.h> 45263936Sbr 46263936Sbr#include <dev/fdt/fdt_common.h> 47263936Sbr#include <dev/ofw/openfirm.h> 48263936Sbr#include <dev/ofw/ofw_bus.h> 49263936Sbr#include <dev/ofw/ofw_bus_subr.h> 50263936Sbr 51263936Sbr#include <machine/bus.h> 52263936Sbr#include <machine/fdt.h> 53263936Sbr#include <machine/cpu.h> 54263936Sbr#include <machine/intr.h> 55263936Sbr 56263936Sbr#include <arm/samsung/exynos/exynos5_common.h> 57263936Sbr#include <arm/samsung/exynos/exynos5_combiner.h> 58263936Sbr 59263936Sbr#define NGRP 32 60263936Sbr#define ITABLE_LEN 24 61263936Sbr 62263936Sbr#define IESR(n) (0x10 * n + 0x0) /* Interrupt enable set */ 63263936Sbr#define IECR(n) (0x10 * n + 0x4) /* Interrupt enable clear */ 64263936Sbr#define ISTR(n) (0x10 * n + 0x8) /* Interrupt status */ 65263936Sbr#define IMSR(n) (0x10 * n + 0xC) /* Interrupt masked status */ 66263936Sbr#define CIPSR 0x100 /* Combined interrupt pending */ 67263936Sbr 68263936Sbrstruct combiner_softc { 69263936Sbr struct resource *res[1 + NGRP]; 70263936Sbr bus_space_tag_t bst; 71263936Sbr bus_space_handle_t bsh; 72263936Sbr void *ih[NGRP]; 73263936Sbr device_t dev; 74263936Sbr}; 75263936Sbr 76263936Sbrstruct combiner_softc *combiner_sc; 77263936Sbr 78263936Sbrstatic struct resource_spec combiner_spec[] = { 79263936Sbr { SYS_RES_MEMORY, 0, RF_ACTIVE }, 80263936Sbr { SYS_RES_IRQ, 0, RF_ACTIVE }, 81263936Sbr { SYS_RES_IRQ, 1, RF_ACTIVE }, 82263936Sbr { SYS_RES_IRQ, 2, RF_ACTIVE }, 83263936Sbr { SYS_RES_IRQ, 3, RF_ACTIVE }, 84263936Sbr { SYS_RES_IRQ, 4, RF_ACTIVE }, 85263936Sbr { SYS_RES_IRQ, 5, RF_ACTIVE }, 86263936Sbr { SYS_RES_IRQ, 6, RF_ACTIVE }, 87263936Sbr { SYS_RES_IRQ, 7, RF_ACTIVE }, 88263936Sbr { SYS_RES_IRQ, 8, RF_ACTIVE }, 89263936Sbr { SYS_RES_IRQ, 9, RF_ACTIVE }, 90263936Sbr { SYS_RES_IRQ, 10, RF_ACTIVE }, 91263936Sbr { SYS_RES_IRQ, 11, RF_ACTIVE }, 92263936Sbr { SYS_RES_IRQ, 12, RF_ACTIVE }, 93263936Sbr { SYS_RES_IRQ, 13, RF_ACTIVE }, 94263936Sbr { SYS_RES_IRQ, 14, RF_ACTIVE }, 95263936Sbr { SYS_RES_IRQ, 15, RF_ACTIVE }, 96263936Sbr { SYS_RES_IRQ, 16, RF_ACTIVE }, 97263936Sbr { SYS_RES_IRQ, 17, RF_ACTIVE }, 98263936Sbr { SYS_RES_IRQ, 18, RF_ACTIVE }, 99263936Sbr { SYS_RES_IRQ, 19, RF_ACTIVE }, 100263936Sbr { SYS_RES_IRQ, 20, RF_ACTIVE }, 101263936Sbr { SYS_RES_IRQ, 21, RF_ACTIVE }, 102263936Sbr { SYS_RES_IRQ, 22, RF_ACTIVE }, 103263936Sbr { SYS_RES_IRQ, 23, RF_ACTIVE }, 104263936Sbr { SYS_RES_IRQ, 24, RF_ACTIVE }, 105263936Sbr { SYS_RES_IRQ, 25, RF_ACTIVE }, 106263936Sbr { SYS_RES_IRQ, 26, RF_ACTIVE }, 107263936Sbr { SYS_RES_IRQ, 27, RF_ACTIVE }, 108263936Sbr { SYS_RES_IRQ, 28, RF_ACTIVE }, 109263936Sbr { SYS_RES_IRQ, 29, RF_ACTIVE }, 110263936Sbr { SYS_RES_IRQ, 30, RF_ACTIVE }, 111263936Sbr { SYS_RES_IRQ, 31, RF_ACTIVE }, 112263936Sbr { -1, 0 } 113263936Sbr}; 114263936Sbr 115263936Sbrstruct combiner_entry { 116263936Sbr int combiner_id; 117263936Sbr int bit; 118263936Sbr char *source_name; 119263936Sbr}; 120263936Sbr 121263936Sbrstatic struct combiner_entry interrupt_table[ITABLE_LEN] = { 122263936Sbr { 63, 1, "EINT[15]" }, 123263936Sbr { 63, 0, "EINT[14]" }, 124263936Sbr { 62, 1, "EINT[13]" }, 125263936Sbr { 62, 0, "EINT[12]" }, 126263936Sbr { 61, 1, "EINT[11]" }, 127263936Sbr { 61, 0, "EINT[10]" }, 128263936Sbr { 60, 1, "EINT[9]" }, 129263936Sbr { 60, 0, "EINT[8]" }, 130263936Sbr { 59, 1, "EINT[7]" }, 131263936Sbr { 59, 0, "EINT[6]" }, 132263936Sbr { 58, 1, "EINT[5]" }, 133263936Sbr { 58, 0, "EINT[4]" }, 134263936Sbr { 57, 3, "MCT_G3" }, 135263936Sbr { 57, 2, "MCT_G2" }, 136263936Sbr { 57, 1, "EINT[3]" }, 137263936Sbr { 57, 0, "EINT[2]" }, 138263936Sbr { 56, 6, "SYSMMU_G2D[1]" }, 139263936Sbr { 56, 5, "SYSMMU_G2D[0]" }, 140263936Sbr { 56, 2, "SYSMMU_FIMC_LITE1[1]" }, 141263936Sbr { 56, 1, "SYSMMU_FIMC_LITE1[0]" }, 142263936Sbr { 56, 0, "EINT[1]" }, 143263936Sbr { 55, 4, "MCT_G1" }, 144263936Sbr { 55, 3, "MCT_G0" }, 145263936Sbr { 55, 0, "EINT[0]" }, 146263936Sbr 147263936Sbr /* TODO: add groups 54-32 */ 148263936Sbr}; 149263936Sbr 150263936Sbrstruct combined_intr { 151263936Sbr uint32_t enabled; 152263936Sbr void (*ih) (void *); 153263936Sbr void *ih_user; 154263936Sbr}; 155263936Sbr 156263936Sbrstatic struct combined_intr intr_map[32][8]; 157263936Sbr 158263936Sbrstatic void 159263936Sbrcombiner_intr(void *arg) 160263936Sbr{ 161263936Sbr struct combiner_softc *sc; 162263936Sbr void (*ih) (void *); 163263936Sbr void *ih_user; 164263936Sbr int enabled; 165263936Sbr int intrs; 166263936Sbr int shift; 167263936Sbr int cirq; 168263936Sbr int grp; 169263936Sbr int i,n; 170263936Sbr 171263936Sbr sc = arg; 172263936Sbr 173263936Sbr intrs = READ4(sc, CIPSR); 174263936Sbr for (grp = 0; grp < 32; grp++) { 175263936Sbr if (intrs & (1 << grp)) { 176263936Sbr n = (grp / 4); 177263936Sbr shift = (grp % 4) * 8; 178263936Sbr 179263936Sbr cirq = READ4(sc, ISTR(n)); 180263936Sbr for (i = 0; i < 8; i++) { 181263936Sbr if (cirq & (1 << (i + shift))) { 182263936Sbr ih = intr_map[grp][i].ih; 183263936Sbr ih_user = intr_map[grp][i].ih_user; 184263936Sbr enabled = intr_map[grp][i].enabled; 185263936Sbr if (enabled && (ih != NULL)) { 186263936Sbr ih(ih_user); 187263936Sbr } 188263936Sbr } 189263936Sbr } 190263936Sbr } 191263936Sbr } 192263936Sbr} 193263936Sbr 194263936Sbrvoid 195263936Sbrcombiner_setup_intr(char *source_name, void (*ih)(void *), void *ih_user) 196263936Sbr{ 197263936Sbr struct combiner_entry *entry; 198263936Sbr struct combined_intr *cirq; 199263936Sbr struct combiner_softc *sc; 200263936Sbr int shift; 201263936Sbr int reg; 202263936Sbr int grp; 203263936Sbr int n; 204263936Sbr int i; 205263936Sbr 206263936Sbr sc = combiner_sc; 207263936Sbr 208263936Sbr if (sc == NULL) { 209263936Sbr device_printf(sc->dev, "Error: combiner is not attached\n"); 210263936Sbr return; 211263936Sbr } 212263936Sbr 213263936Sbr entry = NULL; 214263936Sbr 215263936Sbr for (i = 0; i < ITABLE_LEN; i++) { 216263936Sbr if (strcmp(interrupt_table[i].source_name, source_name) == 0) { 217263936Sbr entry = &interrupt_table[i]; 218263936Sbr } 219263936Sbr } 220263936Sbr 221263936Sbr if (entry == NULL) { 222263936Sbr device_printf(sc->dev, "Can't find interrupt name %s\n", 223263936Sbr source_name); 224263936Sbr return; 225263936Sbr } 226263936Sbr 227263936Sbr#if 0 228263936Sbr device_printf(sc->dev, "Setting up interrupt %s\n", source_name); 229263936Sbr#endif 230263936Sbr 231263936Sbr grp = entry->combiner_id - 32; 232263936Sbr 233263936Sbr cirq = &intr_map[grp][entry->bit]; 234263936Sbr cirq->enabled = 1; 235263936Sbr cirq->ih = ih; 236263936Sbr cirq->ih_user = ih_user; 237263936Sbr 238263936Sbr n = grp / 4; 239263936Sbr shift = (grp % 4) * 8 + entry->bit; 240263936Sbr 241263936Sbr reg = (1 << shift); 242263936Sbr WRITE4(sc, IESR(n), reg); 243263936Sbr} 244263936Sbr 245263936Sbrstatic int 246263936Sbrcombiner_probe(device_t dev) 247263936Sbr{ 248263936Sbr 249263936Sbr if (!ofw_bus_is_compatible(dev, "exynos,combiner")) 250263936Sbr return (ENXIO); 251263936Sbr 252263936Sbr device_set_desc(dev, "Samsung Exynos 5 Interrupt Combiner"); 253263936Sbr return (BUS_PROBE_DEFAULT); 254263936Sbr} 255263936Sbr 256263936Sbrstatic int 257263936Sbrcombiner_attach(device_t dev) 258263936Sbr{ 259263936Sbr struct combiner_softc *sc; 260263936Sbr int err; 261263936Sbr int i; 262263936Sbr 263263936Sbr sc = device_get_softc(dev); 264263936Sbr sc->dev = dev; 265263936Sbr 266263936Sbr if (bus_alloc_resources(dev, combiner_spec, sc->res)) { 267263936Sbr device_printf(dev, "could not allocate resources\n"); 268263936Sbr return (ENXIO); 269263936Sbr } 270263936Sbr 271263936Sbr /* Memory interface */ 272263936Sbr sc->bst = rman_get_bustag(sc->res[0]); 273263936Sbr sc->bsh = rman_get_bushandle(sc->res[0]); 274263936Sbr 275263936Sbr combiner_sc = sc; 276263936Sbr 277263936Sbr /* Setup interrupt handler */ 278263936Sbr for (i = 0; i < NGRP; i++) { 279263936Sbr err = bus_setup_intr(dev, sc->res[1+i], INTR_TYPE_BIO | \ 280263936Sbr INTR_MPSAFE, NULL, combiner_intr, sc, &sc->ih[i]); 281263936Sbr if (err) { 282263936Sbr device_printf(dev, "Unable to alloc int resource.\n"); 283263936Sbr return (ENXIO); 284263936Sbr } 285263936Sbr } 286263936Sbr 287263936Sbr return (0); 288263936Sbr} 289263936Sbr 290263936Sbrstatic device_method_t combiner_methods[] = { 291263936Sbr DEVMETHOD(device_probe, combiner_probe), 292263936Sbr DEVMETHOD(device_attach, combiner_attach), 293263936Sbr { 0, 0 } 294263936Sbr}; 295263936Sbr 296263936Sbrstatic driver_t combiner_driver = { 297263936Sbr "combiner", 298263936Sbr combiner_methods, 299263936Sbr sizeof(struct combiner_softc), 300263936Sbr}; 301263936Sbr 302263936Sbrstatic devclass_t combiner_devclass; 303263936Sbr 304263936SbrDRIVER_MODULE(combiner, simplebus, combiner_driver, combiner_devclass, 0, 0); 305