14e46a66eSAndrew Turner /* 24e46a66eSAndrew Turner * Copyright 2015 Andrew Turner. 34e46a66eSAndrew Turner * All rights reserved. 44e46a66eSAndrew Turner * 54e46a66eSAndrew Turner * Redistribution and use in source and binary forms, with or without 64e46a66eSAndrew Turner * modification, are permitted provided that the following conditions are 74e46a66eSAndrew Turner * met: 84e46a66eSAndrew Turner * 94e46a66eSAndrew Turner * 1. Redistributions of source code must retain the above copyright 104e46a66eSAndrew Turner * notice, this list of conditions and the following disclaimer. 114e46a66eSAndrew Turner * 2. Redistributions in binary form must reproduce the above copyright 124e46a66eSAndrew Turner * notice, this list of conditions and the following disclaimer in the 134e46a66eSAndrew Turner * documentation and/or other materials provided with the distribution. 144e46a66eSAndrew Turner * 154e46a66eSAndrew Turner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 164e46a66eSAndrew Turner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 174e46a66eSAndrew Turner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 184e46a66eSAndrew Turner * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE 194e46a66eSAndrew Turner * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 204e46a66eSAndrew Turner * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 214e46a66eSAndrew Turner * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 224e46a66eSAndrew Turner * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 234e46a66eSAndrew Turner * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 244e46a66eSAndrew Turner * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 254e46a66eSAndrew Turner * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 264e46a66eSAndrew Turner */ 274e46a66eSAndrew Turner 284e46a66eSAndrew Turner #include <sys/cdefs.h> 294e46a66eSAndrew Turner __FBSDID("$FreeBSD$"); 304e46a66eSAndrew Turner 314e46a66eSAndrew Turner #include <sys/param.h> 324e46a66eSAndrew Turner #include <sys/systm.h> 334e46a66eSAndrew Turner #include <sys/bus.h> 344e46a66eSAndrew Turner #include <sys/kernel.h> 354e46a66eSAndrew Turner #include <sys/module.h> 364e46a66eSAndrew Turner #include <sys/rman.h> 374e46a66eSAndrew Turner 384e46a66eSAndrew Turner #include <machine/bus.h> 394e46a66eSAndrew Turner #include <machine/resource.h> 404e46a66eSAndrew Turner 414e46a66eSAndrew Turner #include <dev/ofw/ofw_bus_subr.h> 424e46a66eSAndrew Turner #include <dev/ofw/ofw_bus.h> 434e46a66eSAndrew Turner 444e46a66eSAndrew Turner #include <arm/broadcom/bcm2835/bcm2836.h> 454e46a66eSAndrew Turner 464e46a66eSAndrew Turner #define ARM_LOCAL_BASE 0x40000000 474e46a66eSAndrew Turner #define ARM_LOCAL_SIZE 0x00001000 484e46a66eSAndrew Turner 494e46a66eSAndrew Turner #define ARM_LOCAL_CONTROL 0x00 504e46a66eSAndrew Turner #define ARM_LOCAL_PRESCALER 0x08 514e46a66eSAndrew Turner #define PRESCALER_19_2 0x80000000 /* 19.2 MHz */ 524e46a66eSAndrew Turner #define ARM_LOCAL_INT_TIMER(n) (0x40 + (n) * 4) 534e46a66eSAndrew Turner #define ARM_LOCAL_INT_MAILBOX(n) (0x50 + (n) * 4) 544e46a66eSAndrew Turner #define ARM_LOCAL_INT_PENDING(n) (0x60 + (n) * 4) 55*3084b64cSSvatopluk Kraus #define INT_PENDING_MASK 0x011f 56962940ceSLuiz Otavio O Souza #define MAILBOX0_IRQ 4 57962940ceSLuiz Otavio O Souza #define MAILBOX0_IRQEN (1 << 0) 584e46a66eSAndrew Turner 594e46a66eSAndrew Turner /* 604e46a66eSAndrew Turner * A driver for features of the bcm2836. 614e46a66eSAndrew Turner */ 624e46a66eSAndrew Turner 634e46a66eSAndrew Turner struct bcm2836_softc { 644e46a66eSAndrew Turner device_t sc_dev; 654e46a66eSAndrew Turner struct resource *sc_mem; 664e46a66eSAndrew Turner }; 674e46a66eSAndrew Turner 684e46a66eSAndrew Turner static device_identify_t bcm2836_identify; 694e46a66eSAndrew Turner static device_probe_t bcm2836_probe; 704e46a66eSAndrew Turner static device_attach_t bcm2836_attach; 714e46a66eSAndrew Turner 724e46a66eSAndrew Turner struct bcm2836_softc *softc; 734e46a66eSAndrew Turner 744e46a66eSAndrew Turner static void 754e46a66eSAndrew Turner bcm2836_identify(driver_t *driver, device_t parent) 764e46a66eSAndrew Turner { 774e46a66eSAndrew Turner 784e46a66eSAndrew Turner if (BUS_ADD_CHILD(parent, 0, "bcm2836", -1) == NULL) 794e46a66eSAndrew Turner device_printf(parent, "add child failed\n"); 804e46a66eSAndrew Turner } 814e46a66eSAndrew Turner 824e46a66eSAndrew Turner static int 834e46a66eSAndrew Turner bcm2836_probe(device_t dev) 844e46a66eSAndrew Turner { 854e46a66eSAndrew Turner 864e46a66eSAndrew Turner if (softc != NULL) 874e46a66eSAndrew Turner return (ENXIO); 884e46a66eSAndrew Turner 894e46a66eSAndrew Turner device_set_desc(dev, "Broadcom bcm2836"); 904e46a66eSAndrew Turner 914e46a66eSAndrew Turner return (BUS_PROBE_DEFAULT); 924e46a66eSAndrew Turner } 934e46a66eSAndrew Turner 944e46a66eSAndrew Turner static int 954e46a66eSAndrew Turner bcm2836_attach(device_t dev) 964e46a66eSAndrew Turner { 974e46a66eSAndrew Turner int i, rid; 984e46a66eSAndrew Turner 994e46a66eSAndrew Turner softc = device_get_softc(dev); 1004e46a66eSAndrew Turner softc->sc_dev = dev; 1014e46a66eSAndrew Turner 1024e46a66eSAndrew Turner rid = 0; 1034e46a66eSAndrew Turner softc->sc_mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 1044e46a66eSAndrew Turner ARM_LOCAL_BASE, ARM_LOCAL_BASE + ARM_LOCAL_SIZE, ARM_LOCAL_SIZE, 1054e46a66eSAndrew Turner RF_ACTIVE); 1064e46a66eSAndrew Turner if (softc->sc_mem == NULL) { 1074e46a66eSAndrew Turner device_printf(dev, "could not allocate memory resource\n"); 1084e46a66eSAndrew Turner return (ENXIO); 1094e46a66eSAndrew Turner } 1104e46a66eSAndrew Turner 1114e46a66eSAndrew Turner bus_write_4(softc->sc_mem, ARM_LOCAL_CONTROL, 0); 1124e46a66eSAndrew Turner bus_write_4(softc->sc_mem, ARM_LOCAL_PRESCALER, PRESCALER_19_2); 1134e46a66eSAndrew Turner 1144e46a66eSAndrew Turner for (i = 0; i < 4; i++) 1154e46a66eSAndrew Turner bus_write_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i), 0); 1164e46a66eSAndrew Turner 1174e46a66eSAndrew Turner for (i = 0; i < 4; i++) 1184e46a66eSAndrew Turner bus_write_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(i), 1); 1194e46a66eSAndrew Turner 1204e46a66eSAndrew Turner return (0); 1214e46a66eSAndrew Turner } 1224e46a66eSAndrew Turner 1234e46a66eSAndrew Turner int 1244e46a66eSAndrew Turner bcm2836_get_next_irq(int last_irq) 1254e46a66eSAndrew Turner { 1264e46a66eSAndrew Turner uint32_t reg; 1274e46a66eSAndrew Turner int cpu; 1284e46a66eSAndrew Turner int irq; 1294e46a66eSAndrew Turner 1304e46a66eSAndrew Turner cpu = PCPU_GET(cpuid); 1314e46a66eSAndrew Turner 1324e46a66eSAndrew Turner reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_PENDING(cpu)); 1334e46a66eSAndrew Turner reg &= INT_PENDING_MASK; 1344e46a66eSAndrew Turner if (reg == 0) 1354e46a66eSAndrew Turner return (-1); 1364e46a66eSAndrew Turner 1374e46a66eSAndrew Turner irq = ffs(reg) - 1; 1384e46a66eSAndrew Turner 1394e46a66eSAndrew Turner return (irq); 1404e46a66eSAndrew Turner } 1414e46a66eSAndrew Turner 1424e46a66eSAndrew Turner void 1434e46a66eSAndrew Turner bcm2836_mask_irq(uintptr_t irq) 1444e46a66eSAndrew Turner { 1454e46a66eSAndrew Turner uint32_t reg; 146962940ceSLuiz Otavio O Souza #ifdef SMP 147962940ceSLuiz Otavio O Souza int cpu; 148962940ceSLuiz Otavio O Souza #endif 1494e46a66eSAndrew Turner int i; 1504e46a66eSAndrew Turner 151962940ceSLuiz Otavio O Souza if (irq < MAILBOX0_IRQ) { 1524e46a66eSAndrew Turner for (i = 0; i < 4; i++) { 153962940ceSLuiz Otavio O Souza reg = bus_read_4(softc->sc_mem, 154962940ceSLuiz Otavio O Souza ARM_LOCAL_INT_TIMER(i)); 1554e46a66eSAndrew Turner reg &= ~(1 << irq); 156962940ceSLuiz Otavio O Souza bus_write_4(softc->sc_mem, 157962940ceSLuiz Otavio O Souza ARM_LOCAL_INT_TIMER(i), reg); 158962940ceSLuiz Otavio O Souza } 159962940ceSLuiz Otavio O Souza #ifdef SMP 160962940ceSLuiz Otavio O Souza } else if (irq == MAILBOX0_IRQ) { 161962940ceSLuiz Otavio O Souza /* Mailbox 0 for IPI */ 162962940ceSLuiz Otavio O Souza cpu = PCPU_GET(cpuid); 163962940ceSLuiz Otavio O Souza reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu)); 164962940ceSLuiz Otavio O Souza reg &= ~MAILBOX0_IRQEN; 165962940ceSLuiz Otavio O Souza bus_write_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu), reg); 166962940ceSLuiz Otavio O Souza #endif 1674e46a66eSAndrew Turner } 1684e46a66eSAndrew Turner } 1694e46a66eSAndrew Turner 1704e46a66eSAndrew Turner void 1714e46a66eSAndrew Turner bcm2836_unmask_irq(uintptr_t irq) 1724e46a66eSAndrew Turner { 1734e46a66eSAndrew Turner uint32_t reg; 174962940ceSLuiz Otavio O Souza #ifdef SMP 175962940ceSLuiz Otavio O Souza int cpu; 176962940ceSLuiz Otavio O Souza #endif 1774e46a66eSAndrew Turner int i; 1784e46a66eSAndrew Turner 179962940ceSLuiz Otavio O Souza if (irq < MAILBOX0_IRQ) { 1804e46a66eSAndrew Turner for (i = 0; i < 4; i++) { 181962940ceSLuiz Otavio O Souza reg = bus_read_4(softc->sc_mem, 182962940ceSLuiz Otavio O Souza ARM_LOCAL_INT_TIMER(i)); 1834e46a66eSAndrew Turner reg |= (1 << irq); 184962940ceSLuiz Otavio O Souza bus_write_4(softc->sc_mem, 185962940ceSLuiz Otavio O Souza ARM_LOCAL_INT_TIMER(i), reg); 186962940ceSLuiz Otavio O Souza } 187962940ceSLuiz Otavio O Souza #ifdef SMP 188962940ceSLuiz Otavio O Souza } else if (irq == MAILBOX0_IRQ) { 189962940ceSLuiz Otavio O Souza /* Mailbox 0 for IPI */ 190962940ceSLuiz Otavio O Souza cpu = PCPU_GET(cpuid); 191962940ceSLuiz Otavio O Souza reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu)); 192962940ceSLuiz Otavio O Souza reg |= MAILBOX0_IRQEN; 193962940ceSLuiz Otavio O Souza bus_write_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu), reg); 194962940ceSLuiz Otavio O Souza #endif 1954e46a66eSAndrew Turner } 1964e46a66eSAndrew Turner } 1974e46a66eSAndrew Turner 1984e46a66eSAndrew Turner static device_method_t bcm2836_methods[] = { 1994e46a66eSAndrew Turner /* Device interface */ 2004e46a66eSAndrew Turner DEVMETHOD(device_identify, bcm2836_identify), 2014e46a66eSAndrew Turner DEVMETHOD(device_probe, bcm2836_probe), 2024e46a66eSAndrew Turner DEVMETHOD(device_attach, bcm2836_attach), 2034e46a66eSAndrew Turner 2044e46a66eSAndrew Turner DEVMETHOD_END 2054e46a66eSAndrew Turner }; 2064e46a66eSAndrew Turner 2074e46a66eSAndrew Turner static devclass_t bcm2836_devclass; 2084e46a66eSAndrew Turner 2094e46a66eSAndrew Turner static driver_t bcm2836_driver = { 2104e46a66eSAndrew Turner "bcm2836", 2114e46a66eSAndrew Turner bcm2836_methods, 2124e46a66eSAndrew Turner sizeof(struct bcm2836_softc), 2134e46a66eSAndrew Turner }; 2144e46a66eSAndrew Turner 2154e46a66eSAndrew Turner EARLY_DRIVER_MODULE(bcm2836, nexus, bcm2836_driver, bcm2836_devclass, 0, 0, 2164e46a66eSAndrew Turner BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 217