1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com> 5 * 6 * This work was supported by Innovate UK project 105694, "Digital Security 7 * by Design (DSbD) Technology Platform Prototype". 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/bus.h> 37 #include <sys/rman.h> 38 #include <sys/kernel.h> 39 #include <sys/module.h> 40 41 #include <machine/bus.h> 42 43 #include <dev/fdt/simplebus.h> 44 #include <dev/fdt/fdt_common.h> 45 #include <dev/ofw/ofw_bus_subr.h> 46 47 #include "arm_doorbell.h" 48 49 #define MHU_CHAN_RX_LP 0x000 /* Low priority channel */ 50 #define MHU_CHAN_RX_HP 0x020 /* High priority channel */ 51 #define MHU_CHAN_RX_SEC 0x200 /* Secure channel */ 52 #define MHU_INTR_STAT 0x00 53 #define MHU_INTR_SET 0x08 54 #define MHU_INTR_CLEAR 0x10 55 56 #define MHU_TX_REG_OFFSET 0x100 57 58 #define DOORBELL_N_CHANNELS 3 59 #define DOORBELL_N_DOORBELLS (DOORBELL_N_CHANNELS * 32) 60 61 struct arm_doorbell dbells[DOORBELL_N_DOORBELLS]; 62 63 static struct resource_spec arm_doorbell_spec[] = { 64 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 65 { SYS_RES_IRQ, 0, RF_ACTIVE }, 66 { SYS_RES_IRQ, 1, RF_ACTIVE }, 67 { -1, 0 } 68 }; 69 70 struct arm_doorbell_softc { 71 struct resource *res[3]; 72 void *lp_intr_cookie; 73 void *hp_intr_cookie; 74 device_t dev; 75 }; 76 77 static void 78 arm_doorbell_lp_intr(void *arg) 79 { 80 struct arm_doorbell_softc *sc; 81 struct arm_doorbell *db; 82 uint32_t reg; 83 int i; 84 85 sc = arg; 86 87 reg = bus_read_4(sc->res[0], MHU_CHAN_RX_LP + MHU_INTR_STAT); 88 for (i = 0; i < 32; i++) { 89 if (reg & (1 << i)) { 90 db = &dbells[i]; 91 bus_write_4(sc->res[0], MHU_CHAN_RX_LP + MHU_INTR_CLEAR, 92 (1 << i)); 93 if (db->func != NULL) 94 db->func(db->arg); 95 } 96 } 97 } 98 99 static void 100 arm_doorbell_hp_intr(void *arg) 101 { 102 struct arm_doorbell_softc *sc; 103 struct arm_doorbell *db; 104 uint32_t reg; 105 int i; 106 107 sc = arg; 108 109 reg = bus_read_4(sc->res[0], MHU_CHAN_RX_HP + MHU_INTR_STAT); 110 for (i = 0; i < 32; i++) { 111 if (reg & (1 << i)) { 112 db = &dbells[i]; 113 bus_write_4(sc->res[0], MHU_CHAN_RX_HP + MHU_INTR_CLEAR, 114 (1 << i)); 115 if (db->func != NULL) 116 db->func(db->arg); 117 } 118 } 119 } 120 121 static int 122 arm_doorbell_probe(device_t dev) 123 { 124 125 if (!ofw_bus_is_compatible(dev, "arm,mhu-doorbell")) 126 return (ENXIO); 127 128 if (!ofw_bus_status_okay(dev)) 129 return (ENXIO); 130 131 device_set_desc(dev, "ARM MHU Doorbell"); 132 133 return (BUS_PROBE_DEFAULT); 134 } 135 136 static int 137 arm_doorbell_attach(device_t dev) 138 { 139 struct arm_doorbell_softc *sc; 140 phandle_t node; 141 int error; 142 143 sc = device_get_softc(dev); 144 sc->dev = dev; 145 146 node = ofw_bus_get_node(dev); 147 if (node == -1) 148 return (ENXIO); 149 150 if (bus_alloc_resources(dev, arm_doorbell_spec, sc->res) != 0) { 151 device_printf(dev, "Can't allocate resources for device.\n"); 152 return (ENXIO); 153 } 154 155 /* Setup interrupt handlers. */ 156 error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, 157 NULL, arm_doorbell_lp_intr, sc, &sc->lp_intr_cookie); 158 if (error != 0) { 159 device_printf(dev, "Can't setup LP interrupt handler.\n"); 160 bus_release_resources(dev, arm_doorbell_spec, sc->res); 161 return (ENXIO); 162 } 163 164 error = bus_setup_intr(dev, sc->res[2], INTR_TYPE_MISC | INTR_MPSAFE, 165 NULL, arm_doorbell_hp_intr, sc, &sc->hp_intr_cookie); 166 if (error != 0) { 167 device_printf(dev, "Can't setup HP interrupt handler.\n"); 168 bus_release_resources(dev, arm_doorbell_spec, sc->res); 169 return (ENXIO); 170 } 171 172 OF_device_register_xref(OF_xref_from_node(node), dev); 173 174 return (0); 175 } 176 177 static int 178 arm_doorbell_detach(device_t dev) 179 { 180 181 return (EBUSY); 182 } 183 184 struct arm_doorbell * 185 arm_doorbell_ofw_get(device_t dev, const char *name) 186 { 187 phandle_t node, parent; 188 struct arm_doorbell *db; 189 device_t db_dev; 190 pcell_t *cells; 191 int nmboxes; 192 int ncells; 193 int idx; 194 int db_id; 195 int error; 196 int chan; 197 198 node = ofw_bus_get_node(dev); 199 200 error = ofw_bus_parse_xref_list_get_length(node, "mboxes", 201 "#mbox-cells", &nmboxes); 202 if (error) { 203 device_printf(dev, "%s can't get mboxes list.\n", __func__); 204 return (NULL); 205 } 206 207 if (nmboxes == 0) { 208 device_printf(dev, "%s mbox list is empty.\n", __func__); 209 return (NULL); 210 } 211 212 error = ofw_bus_find_string_index(node, "mbox-names", name, &idx); 213 if (error != 0) { 214 device_printf(dev, "%s can't find string index.\n", 215 __func__); 216 return (NULL); 217 } 218 219 error = ofw_bus_parse_xref_list_alloc(node, "mboxes", "#mbox-cells", 220 idx, &parent, &ncells, &cells); 221 if (error != 0) { 222 device_printf(dev, "%s can't get mbox device xref\n", 223 __func__); 224 return (NULL); 225 } 226 227 if (ncells != 2) { 228 device_printf(dev, "Unexpected data size.\n"); 229 OF_prop_free(cells); 230 return (NULL); 231 } 232 233 db_dev = OF_device_from_xref(parent); 234 if (db_dev == NULL) { 235 device_printf(dev, "%s: Can't get arm_doorbell device\n", 236 __func__); 237 OF_prop_free(cells); 238 return (NULL); 239 } 240 241 chan = cells[0]; 242 if (chan >= DOORBELL_N_CHANNELS) { 243 device_printf(dev, "Unexpected channel number.\n"); 244 OF_prop_free(cells); 245 return (NULL); 246 } 247 248 db_id = cells[1]; 249 if (db_id >= 32) { 250 device_printf(dev, "Unexpected channel bit.\n"); 251 OF_prop_free(cells); 252 return (NULL); 253 } 254 255 db = &dbells[chan * db_id]; 256 db->dev = dev; 257 db->db_dev = db_dev; 258 db->chan = chan; 259 db->db = db_id; 260 261 OF_prop_free(cells); 262 263 return (db); 264 } 265 266 void 267 arm_doorbell_set(struct arm_doorbell *db) 268 { 269 struct arm_doorbell_softc *sc; 270 uint32_t offset; 271 272 sc = device_get_softc(db->db_dev); 273 274 switch (db->chan) { 275 case 0: 276 offset = MHU_CHAN_RX_LP; 277 break; 278 case 1: 279 offset = MHU_CHAN_RX_HP; 280 break; 281 case 2: 282 offset = MHU_CHAN_RX_SEC; 283 break; 284 default: 285 panic("not reached"); 286 }; 287 288 offset |= MHU_TX_REG_OFFSET; 289 290 bus_write_4(sc->res[0], offset + MHU_INTR_SET, (1 << db->db)); 291 } 292 293 int 294 arm_doorbell_get(struct arm_doorbell *db) 295 { 296 struct arm_doorbell_softc *sc; 297 uint32_t offset; 298 uint32_t reg; 299 300 sc = device_get_softc(db->db_dev); 301 302 switch (db->chan) { 303 case 0: 304 offset = MHU_CHAN_RX_LP; 305 break; 306 case 1: 307 offset = MHU_CHAN_RX_HP; 308 break; 309 case 2: 310 offset = MHU_CHAN_RX_SEC; 311 break; 312 default: 313 panic("not reached"); 314 }; 315 316 reg = bus_read_4(sc->res[0], offset + MHU_INTR_STAT); 317 if (reg & (1 << db->db)) { 318 bus_write_4(sc->res[0], offset + MHU_INTR_CLEAR, 319 (1 << db->db)); 320 return (1); 321 } 322 323 return (0); 324 } 325 326 void 327 arm_doorbell_set_handler(struct arm_doorbell *db, void (*func)(void *), 328 void *arg) 329 { 330 331 db->func = func; 332 db->arg = arg; 333 } 334 335 static device_method_t arm_doorbell_methods[] = { 336 DEVMETHOD(device_probe, arm_doorbell_probe), 337 DEVMETHOD(device_attach, arm_doorbell_attach), 338 DEVMETHOD(device_detach, arm_doorbell_detach), 339 DEVMETHOD_END 340 }; 341 342 DEFINE_CLASS_1(arm_doorbell, arm_doorbell_driver, arm_doorbell_methods, 343 sizeof(struct arm_doorbell_softc), simplebus_driver); 344 345 EARLY_DRIVER_MODULE(arm_doorbell, simplebus, arm_doorbell_driver, 0, 0, 346 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 347 MODULE_VERSION(arm_doorbell, 1); 348