1 /*- 2 * Copyright (c) 2012-2015 Oleksandr Tymoshenko <gonzo@freebsd.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 __FBSDID("$FreeBSD$"); 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/bus.h> 32 #include <sys/kernel.h> 33 #include <sys/module.h> 34 #include <sys/malloc.h> 35 #include <sys/rman.h> 36 #include <sys/timeet.h> 37 #include <sys/timetc.h> 38 #include <sys/watchdog.h> 39 #include <machine/bus.h> 40 #include <machine/cpu.h> 41 #include <machine/frame.h> 42 #include <machine/intr.h> 43 44 #include <dev/fdt/fdt_common.h> 45 #include <dev/ofw/openfirm.h> 46 #include <dev/ofw/ofw_bus.h> 47 #include <dev/ofw/ofw_bus_subr.h> 48 49 #include <machine/bus.h> 50 /* XXXMDC Is this necessary at all? */ 51 #if defined(__aarch64__) 52 #else 53 #include <machine/fdt.h> 54 #endif 55 56 #include "vchiq_arm.h" 57 #include "vchiq_2835.h" 58 59 #define VCHIQ_LOCK do { \ 60 mtx_lock(&bcm_vchiq_sc->lock); \ 61 } while(0) 62 63 #define VCHIQ_UNLOCK do { \ 64 mtx_unlock(&bcm_vchiq_sc->lock); \ 65 } while(0) 66 67 #ifdef DEBUG 68 #define dprintf(fmt, args...) printf(fmt, ##args) 69 #else 70 #define dprintf(fmt, args...) 71 #endif 72 73 struct bcm_vchiq_softc { 74 struct mtx lock; 75 struct resource * mem_res; 76 struct resource * irq_res; 77 void* intr_hl; 78 bus_space_tag_t bst; 79 bus_space_handle_t bsh; 80 int regs_offset; 81 }; 82 83 static struct bcm_vchiq_softc *bcm_vchiq_sc = NULL; 84 85 86 #define CONFIG_INVALID 0 87 #define CONFIG_VALID 1 << 0 88 #define BSD_REG_ADDRS 1 << 1 89 #define LONG_BULK_SPACE 1 << 2 90 91 /* 92 * Also controls the use of the standard VC address offset for bulk data DMA 93 * (normal bulks use that offset; bulks for long address spaces use physical 94 * page addresses) 95 */ 96 extern unsigned int g_long_bulk_space; 97 98 99 /* 100 * XXXMDC 101 * The man page for ofw_bus_is_compatible describes ``features'' 102 * as ``can be used''. Here we use understand them as ``must be used'' 103 */ 104 105 static struct ofw_compat_data compat_data[] = { 106 {"broadcom,bcm2835-vchiq", BSD_REG_ADDRS | CONFIG_VALID}, 107 {"brcm,bcm2835-vchiq", CONFIG_VALID}, 108 {"brcm,bcm2711-vchiq", LONG_BULK_SPACE | CONFIG_VALID}, 109 {NULL, CONFIG_INVALID} 110 }; 111 112 #define vchiq_read_4(reg) \ 113 bus_space_read_4(bcm_vchiq_sc->bst, bcm_vchiq_sc->bsh, (reg) + \ 114 bcm_vchiq_sc->regs_offset) 115 #define vchiq_write_4(reg, val) \ 116 bus_space_write_4(bcm_vchiq_sc->bst, bcm_vchiq_sc->bsh, (reg) + \ 117 bcm_vchiq_sc->regs_offset, val) 118 119 /* 120 * Extern functions */ 121 void vchiq_exit(void); 122 int vchiq_init(void); 123 124 extern VCHIQ_STATE_T g_state; 125 extern int g_cache_line_size; 126 127 static void 128 bcm_vchiq_intr(void *arg) 129 { 130 VCHIQ_STATE_T *state = &g_state; 131 unsigned int status; 132 133 /* Read (and clear) the doorbell */ 134 status = vchiq_read_4(0x40); 135 136 if (status & 0x4) { /* Was the doorbell rung? */ 137 remote_event_pollall(state); 138 } 139 } 140 141 void 142 remote_event_signal(REMOTE_EVENT_T *event) 143 { 144 145 wmb(); 146 147 event->fired = 1; 148 /* The test on the next line also ensures the write on the previous line 149 has completed */ 150 /* UPDATE: not on arm64, it would seem... */ 151 #if defined(__aarch64__) 152 dsb(sy); 153 #endif 154 if (event->armed) { 155 /* trigger vc interrupt */ 156 #if defined(__aarch64__) 157 dsb(sy); 158 #else 159 dsb(); 160 #endif 161 vchiq_write_4(0x48, 0); 162 } 163 } 164 165 static int 166 bcm_vchiq_probe(device_t dev) 167 { 168 169 if ((ofw_bus_search_compatible(dev, compat_data)->ocd_data & CONFIG_VALID) == 0) 170 return (ENXIO); 171 172 device_set_desc(dev, "BCM2835 VCHIQ"); 173 return (BUS_PROBE_DEFAULT); 174 } 175 176 /* debug_sysctl */ 177 extern int vchiq_core_log_level; 178 extern int vchiq_arm_log_level; 179 180 static int 181 bcm_vchiq_attach(device_t dev) 182 { 183 struct bcm_vchiq_softc *sc = device_get_softc(dev); 184 phandle_t node; 185 pcell_t cell; 186 int rid = 0; 187 188 if (bcm_vchiq_sc != NULL) 189 return (EINVAL); 190 191 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 192 if (sc->mem_res == NULL) { 193 device_printf(dev, "could not allocate memory resource\n"); 194 return (ENXIO); 195 } 196 197 sc->bst = rman_get_bustag(sc->mem_res); 198 sc->bsh = rman_get_bushandle(sc->mem_res); 199 200 rid = 0; 201 sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 202 if (sc->irq_res == NULL) { 203 device_printf(dev, "could not allocate interrupt resource\n"); 204 return (ENXIO); 205 } 206 207 uintptr_t dev_compat_d = ofw_bus_search_compatible(dev, compat_data)->ocd_data; 208 /* XXXMDC: shouldn't happen (checked for in probe)--but, for symmetry */ 209 if ((dev_compat_d & CONFIG_VALID) == 0){ 210 device_printf(dev, "attempting to attach using invalid config.\n"); 211 bus_release_resource(dev, SYS_RES_IRQ, rid, sc->irq_res); 212 return (EINVAL); 213 } 214 if ((dev_compat_d & BSD_REG_ADDRS) == 0) 215 sc->regs_offset = -0x40; 216 if(dev_compat_d & LONG_BULK_SPACE) 217 g_long_bulk_space = 1; 218 219 node = ofw_bus_get_node(dev); 220 if ((OF_getencprop(node, "cache-line-size", &cell, sizeof(cell))) > 0) 221 g_cache_line_size = cell; 222 223 vchiq_core_initialize(); 224 225 /* debug_sysctl */ 226 struct sysctl_ctx_list *ctx_l = device_get_sysctl_ctx(dev); 227 struct sysctl_oid *tree_node = device_get_sysctl_tree(dev); 228 struct sysctl_oid_list *tree = SYSCTL_CHILDREN(tree_node); 229 SYSCTL_ADD_INT( 230 ctx_l, tree, OID_AUTO, "log", CTLFLAG_RW, 231 &vchiq_core_log_level, vchiq_core_log_level, "log level" 232 ); 233 SYSCTL_ADD_INT( 234 ctx_l, tree, OID_AUTO, "arm_log", CTLFLAG_RW, 235 &vchiq_arm_log_level, vchiq_arm_log_level, "arm log level" 236 ); 237 238 /* Setup and enable the timer */ 239 if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 240 NULL, bcm_vchiq_intr, sc, 241 &sc->intr_hl) != 0) { 242 bus_release_resource(dev, SYS_RES_IRQ, rid, 243 sc->irq_res); 244 device_printf(dev, "Unable to setup the clock irq handler.\n"); 245 return (ENXIO); 246 } 247 248 mtx_init(&sc->lock, "vchiq", 0, MTX_DEF); 249 bcm_vchiq_sc = sc; 250 251 vchiq_init(); 252 253 bus_identify_children(dev); 254 bus_attach_children(dev); 255 256 return (0); 257 } 258 259 static int 260 bcm_vchiq_detach(device_t dev) 261 { 262 struct bcm_vchiq_softc *sc = device_get_softc(dev); 263 264 vchiq_exit(); 265 266 if (sc->intr_hl) 267 bus_teardown_intr(dev, sc->irq_res, sc->intr_hl); 268 bus_release_resource(dev, SYS_RES_IRQ, 0, 269 sc->irq_res); 270 bus_release_resource(dev, SYS_RES_MEMORY, 0, 271 sc->mem_res); 272 273 mtx_destroy(&sc->lock); 274 275 return (0); 276 } 277 278 279 static device_method_t bcm_vchiq_methods[] = { 280 DEVMETHOD(device_probe, bcm_vchiq_probe), 281 DEVMETHOD(device_attach, bcm_vchiq_attach), 282 DEVMETHOD(device_detach, bcm_vchiq_detach), 283 284 /* Bus interface */ 285 DEVMETHOD(bus_add_child, bus_generic_add_child), 286 287 { 0, 0 } 288 }; 289 290 static driver_t bcm_vchiq_driver = { 291 "vchiq", 292 bcm_vchiq_methods, 293 sizeof(struct bcm_vchiq_softc), 294 }; 295 296 DRIVER_MODULE(vchiq, simplebus, bcm_vchiq_driver, 0, 0); 297 MODULE_VERSION(vchiq, 1); 298