1 /* $OpenBSD: aplmbox.c,v 1.5 2022/12/21 22:30:42 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 #include <sys/malloc.h> 22 23 #include <machine/bus.h> 24 #include <machine/fdt.h> 25 26 #include <dev/ofw/openfirm.h> 27 #include <dev/ofw/ofw_misc.h> 28 #include <dev/ofw/fdt.h> 29 30 #include <arm64/dev/aplmbox.h> 31 32 #define MBOX_A2I_CTRL 0x110 33 #define MBOX_A2I_CTRL_FULL (1 << 16) 34 #define MBOX_I2A_CTRL 0x114 35 #define MBOX_I2A_CTRL_EMPTY (1 << 17) 36 #define MBOX_A2I_SEND0 0x800 37 #define MBOX_A2I_SEND1 0x808 38 #define MBOX_I2A_RECV0 0x830 39 #define MBOX_I2A_RECV1 0x838 40 41 #define HREAD4(sc, reg) \ 42 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 43 #define HREAD8(sc, reg) \ 44 (bus_space_read_8((sc)->sc_iot, (sc)->sc_ioh, (reg))) 45 #define HWRITE4(sc, reg, val) \ 46 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 47 #define HWRITE8(sc, reg, val) \ 48 bus_space_write_8((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 49 50 struct aplmbox_softc { 51 struct device sc_dev; 52 bus_space_tag_t sc_iot; 53 bus_space_handle_t sc_ioh; 54 55 void *sc_ih; 56 void (*sc_rx_callback)(void *); 57 void *sc_rx_arg; 58 59 struct mbox_device sc_md; 60 }; 61 62 int aplmbox_match(struct device *, void *, void *); 63 void aplmbox_attach(struct device *, struct device *, void *); 64 65 const struct cfattach aplmbox_ca = { 66 sizeof (struct aplmbox_softc), aplmbox_match, aplmbox_attach 67 }; 68 69 struct cfdriver aplmbox_cd = { 70 NULL, "aplmbox", DV_DULL 71 }; 72 73 int aplmbox_intr(void *); 74 void *aplmbox_channel(void *, uint32_t *, struct mbox_client *); 75 int aplmbox_send(void *, const void *, size_t); 76 int aplmbox_recv(void *, void *, size_t); 77 78 int 79 aplmbox_match(struct device *parent, void *match, void *aux) 80 { 81 struct fdt_attach_args *faa = aux; 82 83 return (OF_is_compatible(faa->fa_node, "apple,asc-mailbox") || 84 OF_is_compatible(faa->fa_node, "apple,asc-mailbox-v4")); 85 } 86 87 void 88 aplmbox_attach(struct device *parent, struct device *self, void *aux) 89 { 90 struct aplmbox_softc *sc = (struct aplmbox_softc *)self; 91 struct fdt_attach_args *faa = aux; 92 int idx; 93 94 if (faa->fa_nreg < 1) { 95 printf(": no registers\n"); 96 return; 97 } 98 99 sc->sc_iot = faa->fa_iot; 100 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 101 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 102 printf(": can't map registers\n"); 103 return; 104 } 105 106 idx = OF_getindex(faa->fa_node, "recv-not-empty", "interrupt-names"); 107 if (idx > 0) { 108 sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, idx, IPL_BIO, 109 aplmbox_intr, sc, sc->sc_dev.dv_xname); 110 } 111 112 printf("\n"); 113 114 sc->sc_md.md_node = faa->fa_node; 115 sc->sc_md.md_cookie = sc; 116 sc->sc_md.md_channel = aplmbox_channel; 117 sc->sc_md.md_send = aplmbox_send; 118 sc->sc_md.md_recv = aplmbox_recv; 119 mbox_register(&sc->sc_md); 120 } 121 122 int 123 aplmbox_intr(void *arg) 124 { 125 struct aplmbox_softc *sc = arg; 126 uint32_t ctrl; 127 128 ctrl = HREAD4(sc, MBOX_I2A_CTRL); 129 if (ctrl & MBOX_I2A_CTRL_EMPTY) 130 return 0; 131 132 if (sc->sc_rx_callback) { 133 sc->sc_rx_callback(sc->sc_rx_arg); 134 } else { 135 printf("%s: 0x%016llx 0x%016llx\n", sc->sc_dev.dv_xname, 136 HREAD8(sc, MBOX_I2A_RECV0), HREAD8(sc, MBOX_I2A_RECV1)); 137 } 138 139 return 1; 140 } 141 142 void * 143 aplmbox_channel(void *cookie, uint32_t *cells, struct mbox_client *mc) 144 { 145 struct aplmbox_softc *sc = cookie; 146 147 if (mc) { 148 sc->sc_rx_callback = mc->mc_rx_callback; 149 sc->sc_rx_arg = mc->mc_rx_arg; 150 151 if (mc->mc_flags & MC_WAKEUP) 152 intr_set_wakeup(sc->sc_ih); 153 } 154 155 return sc; 156 } 157 158 int 159 aplmbox_send(void *cookie, const void *data, size_t len) 160 { 161 struct aplmbox_softc *sc = cookie; 162 const struct aplmbox_msg *msg = data; 163 uint32_t ctrl; 164 165 if (len != sizeof(struct aplmbox_msg)) 166 return EINVAL; 167 168 ctrl = HREAD4(sc, MBOX_A2I_CTRL); 169 if (ctrl & MBOX_A2I_CTRL_FULL) 170 return EBUSY; 171 172 HWRITE8(sc, MBOX_A2I_SEND0, msg->data0); 173 HWRITE8(sc, MBOX_A2I_SEND1, msg->data1); 174 175 return 0; 176 } 177 178 int 179 aplmbox_recv(void *cookie, void *data, size_t len) 180 { 181 struct aplmbox_softc *sc = cookie; 182 struct aplmbox_msg *msg = data; 183 uint32_t ctrl; 184 185 if (len != sizeof(struct aplmbox_msg)) 186 return EINVAL; 187 188 ctrl = HREAD4(sc, MBOX_I2A_CTRL); 189 if (ctrl & MBOX_I2A_CTRL_EMPTY) 190 return EWOULDBLOCK; 191 192 msg->data0 = HREAD8(sc, MBOX_I2A_RECV0); 193 msg->data1 = HREAD8(sc, MBOX_I2A_RECV1); 194 195 return 0; 196 } 197