1*7ab4cc98Skettenis /* $OpenBSD: aplmbox.c,v 1.6 2023/07/23 11:17:49 kettenis Exp $ */
25575003fSkettenis /*
35575003fSkettenis * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
45575003fSkettenis *
55575003fSkettenis * Permission to use, copy, modify, and distribute this software for any
65575003fSkettenis * purpose with or without fee is hereby granted, provided that the above
75575003fSkettenis * copyright notice and this permission notice appear in all copies.
85575003fSkettenis *
95575003fSkettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
105575003fSkettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
115575003fSkettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
125575003fSkettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
135575003fSkettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
145575003fSkettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
155575003fSkettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
165575003fSkettenis */
175575003fSkettenis
185575003fSkettenis #include <sys/param.h>
195575003fSkettenis #include <sys/systm.h>
205575003fSkettenis #include <sys/device.h>
215575003fSkettenis #include <sys/malloc.h>
225575003fSkettenis
235575003fSkettenis #include <machine/bus.h>
245575003fSkettenis #include <machine/fdt.h>
255575003fSkettenis
265575003fSkettenis #include <dev/ofw/openfirm.h>
275575003fSkettenis #include <dev/ofw/ofw_misc.h>
28*7ab4cc98Skettenis #include <dev/ofw/ofw_power.h>
295575003fSkettenis #include <dev/ofw/fdt.h>
305575003fSkettenis
315575003fSkettenis #include <arm64/dev/aplmbox.h>
325575003fSkettenis
335575003fSkettenis #define MBOX_A2I_CTRL 0x110
345575003fSkettenis #define MBOX_A2I_CTRL_FULL (1 << 16)
355575003fSkettenis #define MBOX_I2A_CTRL 0x114
365575003fSkettenis #define MBOX_I2A_CTRL_EMPTY (1 << 17)
375575003fSkettenis #define MBOX_A2I_SEND0 0x800
385575003fSkettenis #define MBOX_A2I_SEND1 0x808
395575003fSkettenis #define MBOX_I2A_RECV0 0x830
405575003fSkettenis #define MBOX_I2A_RECV1 0x838
415575003fSkettenis
425575003fSkettenis #define HREAD4(sc, reg) \
435575003fSkettenis (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
445575003fSkettenis #define HREAD8(sc, reg) \
455575003fSkettenis (bus_space_read_8((sc)->sc_iot, (sc)->sc_ioh, (reg)))
465575003fSkettenis #define HWRITE4(sc, reg, val) \
475575003fSkettenis bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
485575003fSkettenis #define HWRITE8(sc, reg, val) \
495575003fSkettenis bus_space_write_8((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
505575003fSkettenis
515575003fSkettenis struct aplmbox_softc {
525575003fSkettenis struct device sc_dev;
535575003fSkettenis bus_space_tag_t sc_iot;
545575003fSkettenis bus_space_handle_t sc_ioh;
555575003fSkettenis
565575003fSkettenis void *sc_ih;
575575003fSkettenis void (*sc_rx_callback)(void *);
585575003fSkettenis void *sc_rx_arg;
595575003fSkettenis
605575003fSkettenis struct mbox_device sc_md;
615575003fSkettenis };
625575003fSkettenis
635575003fSkettenis int aplmbox_match(struct device *, void *, void *);
645575003fSkettenis void aplmbox_attach(struct device *, struct device *, void *);
655575003fSkettenis
665575003fSkettenis const struct cfattach aplmbox_ca = {
675575003fSkettenis sizeof (struct aplmbox_softc), aplmbox_match, aplmbox_attach
685575003fSkettenis };
695575003fSkettenis
705575003fSkettenis struct cfdriver aplmbox_cd = {
715575003fSkettenis NULL, "aplmbox", DV_DULL
725575003fSkettenis };
735575003fSkettenis
745575003fSkettenis int aplmbox_intr(void *);
755575003fSkettenis void *aplmbox_channel(void *, uint32_t *, struct mbox_client *);
765575003fSkettenis int aplmbox_send(void *, const void *, size_t);
775575003fSkettenis int aplmbox_recv(void *, void *, size_t);
785575003fSkettenis
795575003fSkettenis int
aplmbox_match(struct device * parent,void * match,void * aux)805575003fSkettenis aplmbox_match(struct device *parent, void *match, void *aux)
815575003fSkettenis {
825575003fSkettenis struct fdt_attach_args *faa = aux;
835575003fSkettenis
84bcf979e4Skettenis return (OF_is_compatible(faa->fa_node, "apple,asc-mailbox") ||
85bcf979e4Skettenis OF_is_compatible(faa->fa_node, "apple,asc-mailbox-v4"));
865575003fSkettenis }
875575003fSkettenis
885575003fSkettenis void
aplmbox_attach(struct device * parent,struct device * self,void * aux)895575003fSkettenis aplmbox_attach(struct device *parent, struct device *self, void *aux)
905575003fSkettenis {
915575003fSkettenis struct aplmbox_softc *sc = (struct aplmbox_softc *)self;
925575003fSkettenis struct fdt_attach_args *faa = aux;
935575003fSkettenis int idx;
945575003fSkettenis
955575003fSkettenis if (faa->fa_nreg < 1) {
965575003fSkettenis printf(": no registers\n");
975575003fSkettenis return;
985575003fSkettenis }
995575003fSkettenis
1005575003fSkettenis sc->sc_iot = faa->fa_iot;
1015575003fSkettenis if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
1025575003fSkettenis faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
1035575003fSkettenis printf(": can't map registers\n");
1045575003fSkettenis return;
1055575003fSkettenis }
1065575003fSkettenis
1075575003fSkettenis idx = OF_getindex(faa->fa_node, "recv-not-empty", "interrupt-names");
1085575003fSkettenis if (idx > 0) {
1095575003fSkettenis sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, idx, IPL_BIO,
1105575003fSkettenis aplmbox_intr, sc, sc->sc_dev.dv_xname);
1115575003fSkettenis }
1125575003fSkettenis
1135575003fSkettenis printf("\n");
1145575003fSkettenis
115*7ab4cc98Skettenis power_domain_enable(faa->fa_node);
116*7ab4cc98Skettenis
1175575003fSkettenis sc->sc_md.md_node = faa->fa_node;
1185575003fSkettenis sc->sc_md.md_cookie = sc;
1195575003fSkettenis sc->sc_md.md_channel = aplmbox_channel;
1205575003fSkettenis sc->sc_md.md_send = aplmbox_send;
1215575003fSkettenis sc->sc_md.md_recv = aplmbox_recv;
1225575003fSkettenis mbox_register(&sc->sc_md);
1235575003fSkettenis }
1245575003fSkettenis
1255575003fSkettenis int
aplmbox_intr(void * arg)1265575003fSkettenis aplmbox_intr(void *arg)
1275575003fSkettenis {
1285575003fSkettenis struct aplmbox_softc *sc = arg;
1295575003fSkettenis uint32_t ctrl;
1305575003fSkettenis
1315575003fSkettenis ctrl = HREAD4(sc, MBOX_I2A_CTRL);
1325575003fSkettenis if (ctrl & MBOX_I2A_CTRL_EMPTY)
1335575003fSkettenis return 0;
1345575003fSkettenis
1355575003fSkettenis if (sc->sc_rx_callback) {
1365575003fSkettenis sc->sc_rx_callback(sc->sc_rx_arg);
1375575003fSkettenis } else {
1385575003fSkettenis printf("%s: 0x%016llx 0x%016llx\n", sc->sc_dev.dv_xname,
1395575003fSkettenis HREAD8(sc, MBOX_I2A_RECV0), HREAD8(sc, MBOX_I2A_RECV1));
1405575003fSkettenis }
1415575003fSkettenis
1425575003fSkettenis return 1;
1435575003fSkettenis }
1445575003fSkettenis
1455575003fSkettenis void *
aplmbox_channel(void * cookie,uint32_t * cells,struct mbox_client * mc)1465575003fSkettenis aplmbox_channel(void *cookie, uint32_t *cells, struct mbox_client *mc)
1475575003fSkettenis {
1485575003fSkettenis struct aplmbox_softc *sc = cookie;
1495575003fSkettenis
1505575003fSkettenis if (mc) {
1515575003fSkettenis sc->sc_rx_callback = mc->mc_rx_callback;
1525575003fSkettenis sc->sc_rx_arg = mc->mc_rx_arg;
1533b6109e0Skettenis
1543b6109e0Skettenis if (mc->mc_flags & MC_WAKEUP)
1555dee5702Skettenis intr_set_wakeup(sc->sc_ih);
1565575003fSkettenis }
1575575003fSkettenis
1585575003fSkettenis return sc;
1595575003fSkettenis }
1605575003fSkettenis
1615575003fSkettenis int
aplmbox_send(void * cookie,const void * data,size_t len)1625575003fSkettenis aplmbox_send(void *cookie, const void *data, size_t len)
1635575003fSkettenis {
1645575003fSkettenis struct aplmbox_softc *sc = cookie;
1655575003fSkettenis const struct aplmbox_msg *msg = data;
1665575003fSkettenis uint32_t ctrl;
1675575003fSkettenis
1685575003fSkettenis if (len != sizeof(struct aplmbox_msg))
1695575003fSkettenis return EINVAL;
1705575003fSkettenis
1715575003fSkettenis ctrl = HREAD4(sc, MBOX_A2I_CTRL);
1725575003fSkettenis if (ctrl & MBOX_A2I_CTRL_FULL)
1735575003fSkettenis return EBUSY;
1745575003fSkettenis
1755575003fSkettenis HWRITE8(sc, MBOX_A2I_SEND0, msg->data0);
1765575003fSkettenis HWRITE8(sc, MBOX_A2I_SEND1, msg->data1);
1775575003fSkettenis
1785575003fSkettenis return 0;
1795575003fSkettenis }
1805575003fSkettenis
1815575003fSkettenis int
aplmbox_recv(void * cookie,void * data,size_t len)1825575003fSkettenis aplmbox_recv(void *cookie, void *data, size_t len)
1835575003fSkettenis {
1845575003fSkettenis struct aplmbox_softc *sc = cookie;
1855575003fSkettenis struct aplmbox_msg *msg = data;
1865575003fSkettenis uint32_t ctrl;
1875575003fSkettenis
1885575003fSkettenis if (len != sizeof(struct aplmbox_msg))
1895575003fSkettenis return EINVAL;
1905575003fSkettenis
1915575003fSkettenis ctrl = HREAD4(sc, MBOX_I2A_CTRL);
1925575003fSkettenis if (ctrl & MBOX_I2A_CTRL_EMPTY)
193fb1acad1Skettenis return EWOULDBLOCK;
1945575003fSkettenis
1955575003fSkettenis msg->data0 = HREAD8(sc, MBOX_I2A_RECV0);
1965575003fSkettenis msg->data1 = HREAD8(sc, MBOX_I2A_RECV1);
1975575003fSkettenis
1985575003fSkettenis return 0;
1995575003fSkettenis }
200