xref: /openbsd-src/sys/arch/arm64/dev/aplspmi.c (revision 471aeecfc619bc9b69519928152daf993376c2a1)
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