1 /* 2 * Copyright 2015 Andrew Turner. 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 are 7 * met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE 19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 22 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/bus.h> 34 #include <sys/kernel.h> 35 #include <sys/module.h> 36 #include <sys/rman.h> 37 38 #include <machine/bus.h> 39 #include <machine/resource.h> 40 41 #include <dev/ofw/ofw_bus_subr.h> 42 #include <dev/ofw/ofw_bus.h> 43 44 #include <arm/broadcom/bcm2835/bcm2836.h> 45 46 #define ARM_LOCAL_BASE 0x40000000 47 #define ARM_LOCAL_SIZE 0x00001000 48 49 #define ARM_LOCAL_CONTROL 0x00 50 #define ARM_LOCAL_PRESCALER 0x08 51 #define PRESCALER_19_2 0x80000000 /* 19.2 MHz */ 52 #define ARM_LOCAL_INT_TIMER(n) (0x40 + (n) * 4) 53 #define ARM_LOCAL_INT_MAILBOX(n) (0x50 + (n) * 4) 54 #define ARM_LOCAL_INT_PENDING(n) (0x60 + (n) * 4) 55 #define INT_PENDING_MASK 0x01f 56 #define MAILBOX0_IRQ 4 57 #define MAILBOX0_IRQEN (1 << 0) 58 59 /* 60 * A driver for features of the bcm2836. 61 */ 62 63 struct bcm2836_softc { 64 device_t sc_dev; 65 struct resource *sc_mem; 66 }; 67 68 static device_identify_t bcm2836_identify; 69 static device_probe_t bcm2836_probe; 70 static device_attach_t bcm2836_attach; 71 72 struct bcm2836_softc *softc; 73 74 static void 75 bcm2836_identify(driver_t *driver, device_t parent) 76 { 77 78 if (BUS_ADD_CHILD(parent, 0, "bcm2836", -1) == NULL) 79 device_printf(parent, "add child failed\n"); 80 } 81 82 static int 83 bcm2836_probe(device_t dev) 84 { 85 86 if (softc != NULL) 87 return (ENXIO); 88 89 device_set_desc(dev, "Broadcom bcm2836"); 90 91 return (BUS_PROBE_DEFAULT); 92 } 93 94 static int 95 bcm2836_attach(device_t dev) 96 { 97 int i, rid; 98 99 softc = device_get_softc(dev); 100 softc->sc_dev = dev; 101 102 rid = 0; 103 softc->sc_mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 104 ARM_LOCAL_BASE, ARM_LOCAL_BASE + ARM_LOCAL_SIZE, ARM_LOCAL_SIZE, 105 RF_ACTIVE); 106 if (softc->sc_mem == NULL) { 107 device_printf(dev, "could not allocate memory resource\n"); 108 return (ENXIO); 109 } 110 111 bus_write_4(softc->sc_mem, ARM_LOCAL_CONTROL, 0); 112 bus_write_4(softc->sc_mem, ARM_LOCAL_PRESCALER, PRESCALER_19_2); 113 114 for (i = 0; i < 4; i++) 115 bus_write_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i), 0); 116 117 for (i = 0; i < 4; i++) 118 bus_write_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(i), 1); 119 120 return (0); 121 } 122 123 int 124 bcm2836_get_next_irq(int last_irq) 125 { 126 uint32_t reg; 127 int cpu; 128 int irq; 129 130 cpu = PCPU_GET(cpuid); 131 132 reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_PENDING(cpu)); 133 reg &= INT_PENDING_MASK; 134 if (reg == 0) 135 return (-1); 136 137 irq = ffs(reg) - 1; 138 139 return (irq); 140 } 141 142 void 143 bcm2836_mask_irq(uintptr_t irq) 144 { 145 uint32_t reg; 146 #ifdef SMP 147 int cpu; 148 #endif 149 int i; 150 151 if (irq < MAILBOX0_IRQ) { 152 for (i = 0; i < 4; i++) { 153 reg = bus_read_4(softc->sc_mem, 154 ARM_LOCAL_INT_TIMER(i)); 155 reg &= ~(1 << irq); 156 bus_write_4(softc->sc_mem, 157 ARM_LOCAL_INT_TIMER(i), reg); 158 } 159 #ifdef SMP 160 } else if (irq == MAILBOX0_IRQ) { 161 /* Mailbox 0 for IPI */ 162 cpu = PCPU_GET(cpuid); 163 reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu)); 164 reg &= ~MAILBOX0_IRQEN; 165 bus_write_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu), reg); 166 #endif 167 } 168 } 169 170 void 171 bcm2836_unmask_irq(uintptr_t irq) 172 { 173 uint32_t reg; 174 #ifdef SMP 175 int cpu; 176 #endif 177 int i; 178 179 if (irq < MAILBOX0_IRQ) { 180 for (i = 0; i < 4; i++) { 181 reg = bus_read_4(softc->sc_mem, 182 ARM_LOCAL_INT_TIMER(i)); 183 reg |= (1 << irq); 184 bus_write_4(softc->sc_mem, 185 ARM_LOCAL_INT_TIMER(i), reg); 186 } 187 #ifdef SMP 188 } else if (irq == MAILBOX0_IRQ) { 189 /* Mailbox 0 for IPI */ 190 cpu = PCPU_GET(cpuid); 191 reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu)); 192 reg |= MAILBOX0_IRQEN; 193 bus_write_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(cpu), reg); 194 #endif 195 } 196 } 197 198 static device_method_t bcm2836_methods[] = { 199 /* Device interface */ 200 DEVMETHOD(device_identify, bcm2836_identify), 201 DEVMETHOD(device_probe, bcm2836_probe), 202 DEVMETHOD(device_attach, bcm2836_attach), 203 204 DEVMETHOD_END 205 }; 206 207 static devclass_t bcm2836_devclass; 208 209 static driver_t bcm2836_driver = { 210 "bcm2836", 211 bcm2836_methods, 212 sizeof(struct bcm2836_softc), 213 }; 214 215 EARLY_DRIVER_MODULE(bcm2836, nexus, bcm2836_driver, bcm2836_devclass, 0, 0, 216 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 217