xref: /openbsd-src/sys/arch/arm64/dev/aplspmi.c (revision 471aeecfc619bc9b69519928152daf993376c2a1)
1 /*	$OpenBSD: aplspmi.c,v 1.2 2022/04/06 18:59:26 naddy 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 
22 #include <machine/bus.h>
23 #include <machine/fdt.h>
24 
25 #include <dev/ofw/openfirm.h>
26 #include <dev/ofw/fdt.h>
27 
28 #include <dev/fdt/spmivar.h>
29 
30 /*
31  * This driver is based on preliminary device tree bindings and will
32  * almost certainly need changes once the official bindings land in
33  * mainline Linux.  Support for these preliminary bindings will be
34  * dropped as soon as official bindings are available.
35  */
36 
37 #define SPMI_STAT		0x00
38 #define  SPMI_STAT_RXEMPTY		(1 << 24)
39 #define  SPMI_STAT_TXEMPTY		(1 << 8)
40 #define SPMI_CMD		0x04
41 #define  SPMI_CMD_ADDR(x)		((x) << 16)
42 #define  SPMI_CMD_LAST			(1 << 15)
43 #define  SPMI_CMD_SID(x)		((x) << 8)
44 #define SPMI_RESP		0x08
45 #define SPMI_INTEN(i)		(0x20 + (i) * 4)
46 #define SPMI_INTSTAT(i)		(0x60 + (i) * 4)
47 
48 #define HREAD4(sc, reg)							\
49 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
50 #define HWRITE4(sc, reg, val)						\
51 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
52 
53 struct aplspmi_softc {
54 	struct device		sc_dev;
55 	bus_space_tag_t		sc_iot;
56 	bus_space_handle_t	sc_ioh;
57 
58 	struct spmi_controller	sc_tag;
59 };
60 
61 int	aplspmi_match(struct device *, void *, void *);
62 void	aplspmi_attach(struct device *, struct device *, void *);
63 
64 const struct cfattach	aplspmi_ca = {
65 	sizeof (struct aplspmi_softc), aplspmi_match, aplspmi_attach
66 };
67 
68 struct cfdriver aplspmi_cd = {
69 	NULL, "aplspmi", DV_DULL
70 };
71 
72 int	aplspmi_print(void *, const char *);
73 int	aplspmi_cmd_read(void *, uint8_t, uint8_t, uint16_t, void *, size_t);
74 int	aplspmi_cmd_write(void *, uint8_t, uint8_t, uint16_t,
75 	    const void *, size_t);
76 
77 int
aplspmi_match(struct device * parent,void * match,void * aux)78 aplspmi_match(struct device *parent, void *match, void *aux)
79 {
80 	struct fdt_attach_args *faa = aux;
81 
82 	return OF_is_compatible(faa->fa_node, "apple,spmi");
83 }
84 
85 void
aplspmi_attach(struct device * parent,struct device * self,void * aux)86 aplspmi_attach(struct device *parent, struct device *self, void *aux)
87 {
88 	struct aplspmi_softc *sc = (struct aplspmi_softc *)self;
89 	struct fdt_attach_args *faa = aux;
90 	struct spmi_attach_args sa;
91 	char name[32];
92 	uint32_t reg[2];
93 	int node;
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 	printf("\n");
108 
109 	sc->sc_tag.sc_cookie = sc;
110 	sc->sc_tag.sc_cmd_read = aplspmi_cmd_read;
111 	sc->sc_tag.sc_cmd_write = aplspmi_cmd_write;
112 
113 	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
114 		if (OF_getpropintarray(node, "reg", reg,
115 		    sizeof(reg)) != sizeof(reg))
116 			continue;
117 
118 		memset(name, 0, sizeof(name));
119 		if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
120 			continue;
121 		if (name[0] == '\0')
122 			continue;
123 
124 		memset(&sa, 0, sizeof(sa));
125 		sa.sa_tag = &sc->sc_tag;
126 		sa.sa_sid = reg[0];
127 		sa.sa_name = name;
128 		sa.sa_node = node;
129 		config_found(self, &sa, aplspmi_print);
130 	}
131 }
132 
133 int
aplspmi_print(void * aux,const char * pnp)134 aplspmi_print(void *aux, const char *pnp)
135 {
136 	struct spmi_attach_args *sa = aux;
137 
138 	if (pnp != NULL)
139 		printf("\"%s\" at %s", sa->sa_name, pnp);
140 	printf(" sid 0x%x", sa->sa_sid);
141 
142 	return UNCONF;
143 }
144 
145 int
aplspmi_read_resp(struct aplspmi_softc * sc,uint32_t * resp)146 aplspmi_read_resp(struct aplspmi_softc *sc, uint32_t *resp)
147 {
148 	int retry;
149 
150 	for (retry = 1000; retry > 0; retry--) {
151 		if ((HREAD4(sc, SPMI_STAT) & SPMI_STAT_RXEMPTY) == 0)
152 			break;
153 		delay(1);
154 	}
155 	if (retry == 0)
156 		return ETIMEDOUT;
157 
158 	*resp = HREAD4(sc, SPMI_RESP);
159 	return 0;
160 }
161 
162 int
aplspmi_cmd_read(void * cookie,uint8_t sid,uint8_t cmd,uint16_t addr,void * buf,size_t len)163 aplspmi_cmd_read(void *cookie, uint8_t sid, uint8_t cmd, uint16_t addr,
164     void *buf, size_t len)
165 {
166 	struct aplspmi_softc *sc = cookie;
167 	uint8_t *cbuf = buf;
168 	uint32_t resp;
169 	int error;
170 
171 	if (len == 0 || len > 8)
172 		return EINVAL;
173 
174 	HWRITE4(sc, SPMI_CMD, SPMI_CMD_SID(sid) | cmd | SPMI_CMD_ADDR(addr) |
175 	    (len - 1) | SPMI_CMD_LAST);
176 
177 	error = aplspmi_read_resp(sc, &resp);
178 	if (error)
179 		return error;
180 
181 	while (len > 0) {
182 		error = aplspmi_read_resp(sc, &resp);
183 		if (error)
184 			return error;
185 		memcpy(cbuf, &resp, MIN(len, 4));
186 		cbuf += MIN(len, 4);
187 		len -= MIN(len, 4);
188 	}
189 
190 	return 0;
191 }
192 
193 int
aplspmi_cmd_write(void * cookie,uint8_t sid,uint8_t cmd,uint16_t addr,const void * buf,size_t len)194 aplspmi_cmd_write(void *cookie, uint8_t sid, uint8_t cmd, uint16_t addr,
195     const void *buf, size_t len)
196 {
197 	struct aplspmi_softc *sc = cookie;
198 	const uint8_t *cbuf = buf;
199 	uint32_t data, resp;
200 
201 	if (len == 0 || len > 8)
202 		return EINVAL;
203 
204 	HWRITE4(sc, SPMI_CMD, SPMI_CMD_SID(sid) | cmd | SPMI_CMD_ADDR(addr) |
205 	    (len - 1) | SPMI_CMD_LAST);
206 
207 	while (len > 0) {
208 		memcpy(&data, cbuf, MIN(len, 4));
209 		HWRITE4(sc, SPMI_CMD, data);
210 		cbuf += MIN(len, 4);
211 		len -= MIN(len, 4);
212 	}
213 
214 	return aplspmi_read_resp(sc, &resp);
215 }
216