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 0x0f 56 57 /* 58 * A driver for features of the bcm2836. 59 */ 60 61 struct bcm2836_softc { 62 device_t sc_dev; 63 struct resource *sc_mem; 64 }; 65 66 static device_identify_t bcm2836_identify; 67 static device_probe_t bcm2836_probe; 68 static device_attach_t bcm2836_attach; 69 70 struct bcm2836_softc *softc; 71 72 static void 73 bcm2836_identify(driver_t *driver, device_t parent) 74 { 75 76 if (BUS_ADD_CHILD(parent, 0, "bcm2836", -1) == NULL) 77 device_printf(parent, "add child failed\n"); 78 } 79 80 static int 81 bcm2836_probe(device_t dev) 82 { 83 84 if (softc != NULL) 85 return (ENXIO); 86 87 device_set_desc(dev, "Broadcom bcm2836"); 88 89 return (BUS_PROBE_DEFAULT); 90 } 91 92 static int 93 bcm2836_attach(device_t dev) 94 { 95 int i, rid; 96 97 softc = device_get_softc(dev); 98 softc->sc_dev = dev; 99 100 rid = 0; 101 softc->sc_mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 102 ARM_LOCAL_BASE, ARM_LOCAL_BASE + ARM_LOCAL_SIZE, ARM_LOCAL_SIZE, 103 RF_ACTIVE); 104 if (softc->sc_mem == NULL) { 105 device_printf(dev, "could not allocate memory resource\n"); 106 return (ENXIO); 107 } 108 109 bus_write_4(softc->sc_mem, ARM_LOCAL_CONTROL, 0); 110 bus_write_4(softc->sc_mem, ARM_LOCAL_PRESCALER, PRESCALER_19_2); 111 112 for (i = 0; i < 4; i++) 113 bus_write_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i), 0); 114 115 for (i = 0; i < 4; i++) 116 bus_write_4(softc->sc_mem, ARM_LOCAL_INT_MAILBOX(i), 1); 117 118 return (0); 119 } 120 121 int 122 bcm2836_get_next_irq(int last_irq) 123 { 124 uint32_t reg; 125 int cpu; 126 int irq; 127 128 cpu = PCPU_GET(cpuid); 129 130 reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_PENDING(cpu)); 131 reg &= INT_PENDING_MASK; 132 if (reg == 0) 133 return (-1); 134 135 irq = ffs(reg) - 1; 136 137 return (irq); 138 } 139 140 void 141 bcm2836_mask_irq(uintptr_t irq) 142 { 143 uint32_t reg; 144 int i; 145 146 for (i = 0; i < 4; i++) { 147 reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i)); 148 reg &= ~(1 << irq); 149 bus_write_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i), reg); 150 } 151 } 152 153 void 154 bcm2836_unmask_irq(uintptr_t irq) 155 { 156 uint32_t reg; 157 int i; 158 159 for (i = 0; i < 4; i++) { 160 reg = bus_read_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i)); 161 reg |= (1 << irq); 162 bus_write_4(softc->sc_mem, ARM_LOCAL_INT_TIMER(i), reg); 163 } 164 } 165 166 static device_method_t bcm2836_methods[] = { 167 /* Device interface */ 168 DEVMETHOD(device_identify, bcm2836_identify), 169 DEVMETHOD(device_probe, bcm2836_probe), 170 DEVMETHOD(device_attach, bcm2836_attach), 171 172 DEVMETHOD_END 173 }; 174 175 static devclass_t bcm2836_devclass; 176 177 static driver_t bcm2836_driver = { 178 "bcm2836", 179 bcm2836_methods, 180 sizeof(struct bcm2836_softc), 181 }; 182 183 EARLY_DRIVER_MODULE(bcm2836, nexus, bcm2836_driver, bcm2836_devclass, 0, 0, 184 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 185