1 /* $OpenBSD: aplmbox.c,v 1.6 2023/07/23 11:17:49 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/ofw_power.h> 29 #include <dev/ofw/fdt.h> 30 31 #include <arm64/dev/aplmbox.h> 32 33 #define MBOX_A2I_CTRL 0x110 34 #define MBOX_A2I_CTRL_FULL (1 << 16) 35 #define MBOX_I2A_CTRL 0x114 36 #define MBOX_I2A_CTRL_EMPTY (1 << 17) 37 #define MBOX_A2I_SEND0 0x800 38 #define MBOX_A2I_SEND1 0x808 39 #define MBOX_I2A_RECV0 0x830 40 #define MBOX_I2A_RECV1 0x838 41 42 #define HREAD4(sc, reg) \ 43 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 44 #define HREAD8(sc, reg) \ 45 (bus_space_read_8((sc)->sc_iot, (sc)->sc_ioh, (reg))) 46 #define HWRITE4(sc, reg, val) \ 47 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 48 #define HWRITE8(sc, reg, val) \ 49 bus_space_write_8((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 50 51 struct aplmbox_softc { 52 struct device sc_dev; 53 bus_space_tag_t sc_iot; 54 bus_space_handle_t sc_ioh; 55 56 void *sc_ih; 57 void (*sc_rx_callback)(void *); 58 void *sc_rx_arg; 59 60 struct mbox_device sc_md; 61 }; 62 63 int aplmbox_match(struct device *, void *, void *); 64 void aplmbox_attach(struct device *, struct device *, void *); 65 66 const struct cfattach aplmbox_ca = { 67 sizeof (struct aplmbox_softc), aplmbox_match, aplmbox_attach 68 }; 69 70 struct cfdriver aplmbox_cd = { 71 NULL, "aplmbox", DV_DULL 72 }; 73 74 int aplmbox_intr(void *); 75 void *aplmbox_channel(void *, uint32_t *, struct mbox_client *); 76 int aplmbox_send(void *, const void *, size_t); 77 int aplmbox_recv(void *, void *, size_t); 78 79 int 80 aplmbox_match(struct device *parent, void *match, void *aux) 81 { 82 struct fdt_attach_args *faa = aux; 83 84 return (OF_is_compatible(faa->fa_node, "apple,asc-mailbox") || 85 OF_is_compatible(faa->fa_node, "apple,asc-mailbox-v4")); 86 } 87 88 void 89 aplmbox_attach(struct device *parent, struct device *self, void *aux) 90 { 91 struct aplmbox_softc *sc = (struct aplmbox_softc *)self; 92 struct fdt_attach_args *faa = aux; 93 int idx; 94 95 if (faa->fa_nreg < 1) { 96 printf(": no registers\n"); 97 return; 98 } 99 100 sc->sc_iot = faa->fa_iot; 101 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 102 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 103 printf(": can't map registers\n"); 104 return; 105 } 106 107 idx = OF_getindex(faa->fa_node, "recv-not-empty", "interrupt-names"); 108 if (idx > 0) { 109 sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, idx, IPL_BIO, 110 aplmbox_intr, sc, sc->sc_dev.dv_xname); 111 } 112 113 printf("\n"); 114 115 power_domain_enable(faa->fa_node); 116 117 sc->sc_md.md_node = faa->fa_node; 118 sc->sc_md.md_cookie = sc; 119 sc->sc_md.md_channel = aplmbox_channel; 120 sc->sc_md.md_send = aplmbox_send; 121 sc->sc_md.md_recv = aplmbox_recv; 122 mbox_register(&sc->sc_md); 123 } 124 125 int 126 aplmbox_intr(void *arg) 127 { 128 struct aplmbox_softc *sc = arg; 129 uint32_t ctrl; 130 131 ctrl = HREAD4(sc, MBOX_I2A_CTRL); 132 if (ctrl & MBOX_I2A_CTRL_EMPTY) 133 return 0; 134 135 if (sc->sc_rx_callback) { 136 sc->sc_rx_callback(sc->sc_rx_arg); 137 } else { 138 printf("%s: 0x%016llx 0x%016llx\n", sc->sc_dev.dv_xname, 139 HREAD8(sc, MBOX_I2A_RECV0), HREAD8(sc, MBOX_I2A_RECV1)); 140 } 141 142 return 1; 143 } 144 145 void * 146 aplmbox_channel(void *cookie, uint32_t *cells, struct mbox_client *mc) 147 { 148 struct aplmbox_softc *sc = cookie; 149 150 if (mc) { 151 sc->sc_rx_callback = mc->mc_rx_callback; 152 sc->sc_rx_arg = mc->mc_rx_arg; 153 154 if (mc->mc_flags & MC_WAKEUP) 155 intr_set_wakeup(sc->sc_ih); 156 } 157 158 return sc; 159 } 160 161 int 162 aplmbox_send(void *cookie, const void *data, size_t len) 163 { 164 struct aplmbox_softc *sc = cookie; 165 const struct aplmbox_msg *msg = data; 166 uint32_t ctrl; 167 168 if (len != sizeof(struct aplmbox_msg)) 169 return EINVAL; 170 171 ctrl = HREAD4(sc, MBOX_A2I_CTRL); 172 if (ctrl & MBOX_A2I_CTRL_FULL) 173 return EBUSY; 174 175 HWRITE8(sc, MBOX_A2I_SEND0, msg->data0); 176 HWRITE8(sc, MBOX_A2I_SEND1, msg->data1); 177 178 return 0; 179 } 180 181 int 182 aplmbox_recv(void *cookie, void *data, size_t len) 183 { 184 struct aplmbox_softc *sc = cookie; 185 struct aplmbox_msg *msg = data; 186 uint32_t ctrl; 187 188 if (len != sizeof(struct aplmbox_msg)) 189 return EINVAL; 190 191 ctrl = HREAD4(sc, MBOX_I2A_CTRL); 192 if (ctrl & MBOX_I2A_CTRL_EMPTY) 193 return EWOULDBLOCK; 194 195 msg->data0 = HREAD8(sc, MBOX_I2A_RECV0); 196 msg->data1 = HREAD8(sc, MBOX_I2A_RECV1); 197 198 return 0; 199 } 200