1*471aeecfSnaddy /* $OpenBSD: aplspmi.c,v 1.2 2022/04/06 18:59:26 naddy Exp $ */
25224032cSkettenis /*
35224032cSkettenis * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
45224032cSkettenis *
55224032cSkettenis * Permission to use, copy, modify, and distribute this software for any
65224032cSkettenis * purpose with or without fee is hereby granted, provided that the above
75224032cSkettenis * copyright notice and this permission notice appear in all copies.
85224032cSkettenis *
95224032cSkettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
105224032cSkettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
115224032cSkettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
125224032cSkettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
135224032cSkettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
145224032cSkettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
155224032cSkettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
165224032cSkettenis */
175224032cSkettenis
185224032cSkettenis #include <sys/param.h>
195224032cSkettenis #include <sys/systm.h>
205224032cSkettenis #include <sys/device.h>
215224032cSkettenis
225224032cSkettenis #include <machine/bus.h>
235224032cSkettenis #include <machine/fdt.h>
245224032cSkettenis
255224032cSkettenis #include <dev/ofw/openfirm.h>
265224032cSkettenis #include <dev/ofw/fdt.h>
275224032cSkettenis
285224032cSkettenis #include <dev/fdt/spmivar.h>
295224032cSkettenis
305224032cSkettenis /*
315224032cSkettenis * This driver is based on preliminary device tree bindings and will
325224032cSkettenis * almost certainly need changes once the official bindings land in
335224032cSkettenis * mainline Linux. Support for these preliminary bindings will be
345224032cSkettenis * dropped as soon as official bindings are available.
355224032cSkettenis */
365224032cSkettenis
375224032cSkettenis #define SPMI_STAT 0x00
385224032cSkettenis #define SPMI_STAT_RXEMPTY (1 << 24)
395224032cSkettenis #define SPMI_STAT_TXEMPTY (1 << 8)
405224032cSkettenis #define SPMI_CMD 0x04
415224032cSkettenis #define SPMI_CMD_ADDR(x) ((x) << 16)
425224032cSkettenis #define SPMI_CMD_LAST (1 << 15)
435224032cSkettenis #define SPMI_CMD_SID(x) ((x) << 8)
445224032cSkettenis #define SPMI_RESP 0x08
455224032cSkettenis #define SPMI_INTEN(i) (0x20 + (i) * 4)
465224032cSkettenis #define SPMI_INTSTAT(i) (0x60 + (i) * 4)
475224032cSkettenis
485224032cSkettenis #define HREAD4(sc, reg) \
495224032cSkettenis (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
505224032cSkettenis #define HWRITE4(sc, reg, val) \
515224032cSkettenis bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
525224032cSkettenis
535224032cSkettenis struct aplspmi_softc {
545224032cSkettenis struct device sc_dev;
555224032cSkettenis bus_space_tag_t sc_iot;
565224032cSkettenis bus_space_handle_t sc_ioh;
575224032cSkettenis
585224032cSkettenis struct spmi_controller sc_tag;
595224032cSkettenis };
605224032cSkettenis
615224032cSkettenis int aplspmi_match(struct device *, void *, void *);
625224032cSkettenis void aplspmi_attach(struct device *, struct device *, void *);
635224032cSkettenis
64*471aeecfSnaddy const struct cfattach aplspmi_ca = {
655224032cSkettenis sizeof (struct aplspmi_softc), aplspmi_match, aplspmi_attach
665224032cSkettenis };
675224032cSkettenis
685224032cSkettenis struct cfdriver aplspmi_cd = {
695224032cSkettenis NULL, "aplspmi", DV_DULL
705224032cSkettenis };
715224032cSkettenis
725224032cSkettenis int aplspmi_print(void *, const char *);
735224032cSkettenis int aplspmi_cmd_read(void *, uint8_t, uint8_t, uint16_t, void *, size_t);
745224032cSkettenis int aplspmi_cmd_write(void *, uint8_t, uint8_t, uint16_t,
755224032cSkettenis const void *, size_t);
765224032cSkettenis
775224032cSkettenis int
aplspmi_match(struct device * parent,void * match,void * aux)785224032cSkettenis aplspmi_match(struct device *parent, void *match, void *aux)
795224032cSkettenis {
805224032cSkettenis struct fdt_attach_args *faa = aux;
815224032cSkettenis
825224032cSkettenis return OF_is_compatible(faa->fa_node, "apple,spmi");
835224032cSkettenis }
845224032cSkettenis
855224032cSkettenis void
aplspmi_attach(struct device * parent,struct device * self,void * aux)865224032cSkettenis aplspmi_attach(struct device *parent, struct device *self, void *aux)
875224032cSkettenis {
885224032cSkettenis struct aplspmi_softc *sc = (struct aplspmi_softc *)self;
895224032cSkettenis struct fdt_attach_args *faa = aux;
905224032cSkettenis struct spmi_attach_args sa;
915224032cSkettenis char name[32];
925224032cSkettenis uint32_t reg[2];
935224032cSkettenis int node;
945224032cSkettenis
955224032cSkettenis if (faa->fa_nreg < 1) {
965224032cSkettenis printf(": no registers\n");
975224032cSkettenis return;
985224032cSkettenis }
995224032cSkettenis
1005224032cSkettenis sc->sc_iot = faa->fa_iot;
1015224032cSkettenis if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
1025224032cSkettenis faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
1035224032cSkettenis printf(": can't map registers\n");
1045224032cSkettenis return;
1055224032cSkettenis }
1065224032cSkettenis
1075224032cSkettenis printf("\n");
1085224032cSkettenis
1095224032cSkettenis sc->sc_tag.sc_cookie = sc;
1105224032cSkettenis sc->sc_tag.sc_cmd_read = aplspmi_cmd_read;
1115224032cSkettenis sc->sc_tag.sc_cmd_write = aplspmi_cmd_write;
1125224032cSkettenis
1135224032cSkettenis for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
1145224032cSkettenis if (OF_getpropintarray(node, "reg", reg,
1155224032cSkettenis sizeof(reg)) != sizeof(reg))
1165224032cSkettenis continue;
1175224032cSkettenis
1185224032cSkettenis memset(name, 0, sizeof(name));
1195224032cSkettenis if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
1205224032cSkettenis continue;
1215224032cSkettenis if (name[0] == '\0')
1225224032cSkettenis continue;
1235224032cSkettenis
1245224032cSkettenis memset(&sa, 0, sizeof(sa));
1255224032cSkettenis sa.sa_tag = &sc->sc_tag;
1265224032cSkettenis sa.sa_sid = reg[0];
1275224032cSkettenis sa.sa_name = name;
1285224032cSkettenis sa.sa_node = node;
1295224032cSkettenis config_found(self, &sa, aplspmi_print);
1305224032cSkettenis }
1315224032cSkettenis }
1325224032cSkettenis
1335224032cSkettenis int
aplspmi_print(void * aux,const char * pnp)1345224032cSkettenis aplspmi_print(void *aux, const char *pnp)
1355224032cSkettenis {
1365224032cSkettenis struct spmi_attach_args *sa = aux;
1375224032cSkettenis
1385224032cSkettenis if (pnp != NULL)
1395224032cSkettenis printf("\"%s\" at %s", sa->sa_name, pnp);
1405224032cSkettenis printf(" sid 0x%x", sa->sa_sid);
1415224032cSkettenis
1425224032cSkettenis return UNCONF;
1435224032cSkettenis }
1445224032cSkettenis
1455224032cSkettenis int
aplspmi_read_resp(struct aplspmi_softc * sc,uint32_t * resp)1465224032cSkettenis aplspmi_read_resp(struct aplspmi_softc *sc, uint32_t *resp)
1475224032cSkettenis {
1485224032cSkettenis int retry;
1495224032cSkettenis
1505224032cSkettenis for (retry = 1000; retry > 0; retry--) {
1515224032cSkettenis if ((HREAD4(sc, SPMI_STAT) & SPMI_STAT_RXEMPTY) == 0)
1525224032cSkettenis break;
1535224032cSkettenis delay(1);
1545224032cSkettenis }
1555224032cSkettenis if (retry == 0)
1565224032cSkettenis return ETIMEDOUT;
1575224032cSkettenis
1585224032cSkettenis *resp = HREAD4(sc, SPMI_RESP);
1595224032cSkettenis return 0;
1605224032cSkettenis }
1615224032cSkettenis
1625224032cSkettenis int
aplspmi_cmd_read(void * cookie,uint8_t sid,uint8_t cmd,uint16_t addr,void * buf,size_t len)1635224032cSkettenis aplspmi_cmd_read(void *cookie, uint8_t sid, uint8_t cmd, uint16_t addr,
1645224032cSkettenis void *buf, size_t len)
1655224032cSkettenis {
1665224032cSkettenis struct aplspmi_softc *sc = cookie;
1675224032cSkettenis uint8_t *cbuf = buf;
1685224032cSkettenis uint32_t resp;
1695224032cSkettenis int error;
1705224032cSkettenis
1715224032cSkettenis if (len == 0 || len > 8)
1725224032cSkettenis return EINVAL;
1735224032cSkettenis
1745224032cSkettenis HWRITE4(sc, SPMI_CMD, SPMI_CMD_SID(sid) | cmd | SPMI_CMD_ADDR(addr) |
1755224032cSkettenis (len - 1) | SPMI_CMD_LAST);
1765224032cSkettenis
1775224032cSkettenis error = aplspmi_read_resp(sc, &resp);
1785224032cSkettenis if (error)
1795224032cSkettenis return error;
1805224032cSkettenis
1815224032cSkettenis while (len > 0) {
1825224032cSkettenis error = aplspmi_read_resp(sc, &resp);
1835224032cSkettenis if (error)
1845224032cSkettenis return error;
1855224032cSkettenis memcpy(cbuf, &resp, MIN(len, 4));
1865224032cSkettenis cbuf += MIN(len, 4);
1875224032cSkettenis len -= MIN(len, 4);
1885224032cSkettenis }
1895224032cSkettenis
1905224032cSkettenis return 0;
1915224032cSkettenis }
1925224032cSkettenis
1935224032cSkettenis int
aplspmi_cmd_write(void * cookie,uint8_t sid,uint8_t cmd,uint16_t addr,const void * buf,size_t len)1945224032cSkettenis aplspmi_cmd_write(void *cookie, uint8_t sid, uint8_t cmd, uint16_t addr,
1955224032cSkettenis const void *buf, size_t len)
1965224032cSkettenis {
1975224032cSkettenis struct aplspmi_softc *sc = cookie;
1985224032cSkettenis const uint8_t *cbuf = buf;
1995224032cSkettenis uint32_t data, resp;
2005224032cSkettenis
2015224032cSkettenis if (len == 0 || len > 8)
2025224032cSkettenis return EINVAL;
2035224032cSkettenis
2045224032cSkettenis HWRITE4(sc, SPMI_CMD, SPMI_CMD_SID(sid) | cmd | SPMI_CMD_ADDR(addr) |
2055224032cSkettenis (len - 1) | SPMI_CMD_LAST);
2065224032cSkettenis
2075224032cSkettenis while (len > 0) {
2085224032cSkettenis memcpy(&data, cbuf, MIN(len, 4));
2095224032cSkettenis HWRITE4(sc, SPMI_CMD, data);
2105224032cSkettenis cbuf += MIN(len, 4);
2115224032cSkettenis len -= MIN(len, 4);
2125224032cSkettenis }
2135224032cSkettenis
2145224032cSkettenis return aplspmi_read_resp(sc, &resp);
2155224032cSkettenis }
216