xref: /openbsd-src/sys/dev/fdt/tipd.c (revision 23d6429a03bae5a80cf2ced691eaaced32e41aa8)
1 /*	$OpenBSD: tipd.c,v 1.3 2023/07/23 11:42:44 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2022 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/intr.h>
24 #include <machine/fdt.h>
25 
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/ofw_misc.h>
28 #include <dev/ofw/fdt.h>
29 
30 #include <dev/i2c/i2cvar.h>
31 
32 #define TPS_CMD1		0x08
33 #define TPS_DATA1		0x09
34 #define TPS_INT_EVENT_1		0x14
35 #define TPS_INT_EVENT_2		0x15
36 #define TPS_INT_MASK_1		0x16
37 #define TPS_INT_MASK_2		0x17
38 #define TPS_INT_CLEAR_1		0x18
39 #define TPS_INT_CLEAR_2		0x19
40 #define TPS_STATUS		0x1a
41 #define  TPS_STATUS_PLUG_PRESENT	(1 << 0)
42 #define TPS_SYSTEM_POWER_STATE	0x20
43 #define  TPS_SYSTEM_POWER_STATE_S0	0
44 #define  TPS_SYSTEM_POWER_STATE_S5	5
45 #define TPS_POWER_STATUS	0x3f
46 
47 #define TPS_CMD(s)	((s[3] << 24) | (s[2] << 16) | (s[1] << 8) | s[0])
48 
49 /*
50  * Interrupt bits on the CD321x controllers used by Apple differ from
51  * those used by the standard TPS6598x controllers.
52  */
53 #define CD_INT_PLUG_EVENT		(1 << 1)
54 
55 struct tipd_softc {
56 	struct device		sc_dev;
57 	i2c_tag_t		sc_tag;
58 	i2c_addr_t		sc_addr;
59 
60 	void			*sc_ih;
61 
62 	struct device_ports	sc_ports;
63 	uint32_t		sc_status;
64 };
65 
66 int	tipd_match(struct device *, void *, void *);
67 void	tipd_attach(struct device *, struct device *, void *);
68 int	tipd_activate(struct device *, int);
69 
70 const struct cfattach tipd_ca = {
71 	sizeof(struct tipd_softc), tipd_match, tipd_attach, NULL,
72 	tipd_activate
73 };
74 
75 struct cfdriver tipd_cd = {
76 	NULL, "tipd", DV_DULL
77 };
78 
79 int	tipd_intr(void *);
80 
81 int	tipd_read_4(struct tipd_softc *, uint8_t, uint32_t *);
82 int	tipd_read_8(struct tipd_softc *, uint8_t, uint64_t *);
83 int	tipd_write_4(struct tipd_softc *, uint8_t, uint32_t);
84 int	tipd_write_8(struct tipd_softc *, uint8_t, uint64_t);
85 int	tipd_exec(struct tipd_softc *, const char *,
86 	    const void *, size_t, void *, size_t);
87 
88 int
tipd_match(struct device * parent,void * match,void * aux)89 tipd_match(struct device *parent, void *match, void *aux)
90 {
91 	struct i2c_attach_args *ia = aux;
92 
93 	return iic_is_compatible(ia, "apple,cd321x");
94 }
95 
96 void
tipd_attach(struct device * parent,struct device * self,void * aux)97 tipd_attach(struct device *parent, struct device *self, void *aux)
98 {
99 	struct tipd_softc *sc = (struct tipd_softc *)self;
100 	struct i2c_attach_args *ia = aux;
101 	int node = *(int *)ia->ia_cookie;
102 
103 	sc->sc_tag = ia->ia_tag;
104 	sc->sc_addr = ia->ia_addr;
105 
106 	sc->sc_ih = fdt_intr_establish(node, IPL_BIO, tipd_intr,
107 	    sc, sc->sc_dev.dv_xname);
108 	if (sc->sc_ih == NULL) {
109 		printf(": can't establish interrupt\n");
110 		return;
111 	}
112 
113 	printf("\n");
114 
115 	tipd_read_4(sc, TPS_STATUS, &sc->sc_status);
116 	tipd_write_8(sc, TPS_INT_MASK_1, CD_INT_PLUG_EVENT);
117 
118 	node = OF_getnodebyname(node, "connector");
119 	if (node) {
120 		sc->sc_ports.dp_node = node;
121 		device_ports_register(&sc->sc_ports, -1);
122 	}
123 }
124 
125 int
tipd_activate(struct device * self,int act)126 tipd_activate(struct device *self, int act)
127 {
128 	struct tipd_softc *sc = (struct tipd_softc *)self;
129 	uint8_t state;
130 	int error;
131 
132 	switch (act) {
133 	case DVACT_QUIESCE:
134 		tipd_write_8(sc, TPS_INT_MASK_1, 0);
135 		break;
136 	case DVACT_SUSPEND:
137 		state = TPS_SYSTEM_POWER_STATE_S5;
138 		error = tipd_exec(sc, "SSPS", &state, sizeof(state), NULL, 0);
139 		if (error)
140 			printf("%s: powerdown failed\n", sc->sc_dev.dv_xname);
141 		break;
142 	case DVACT_RESUME:
143 		state = TPS_SYSTEM_POWER_STATE_S0;
144 		error = tipd_exec(sc, "SSPS", &state, sizeof(state), NULL, 0);
145 		if (error)
146 			printf("%s: powerup failed\n", sc->sc_dev.dv_xname);
147 		break;
148 	case DVACT_WAKEUP:
149 		tipd_read_4(sc, TPS_STATUS, &sc->sc_status);
150 		tipd_write_8(sc, TPS_INT_MASK_1, CD_INT_PLUG_EVENT);
151 		break;
152 	}
153 
154 	return 0;
155 }
156 
157 void
tipd_connect(struct tipd_softc * sc)158 tipd_connect(struct tipd_softc *sc)
159 {
160 	struct endpoint *ep, *rep;
161 	struct usb_controller_port *port;
162 
163 	ep = endpoint_byreg(&sc->sc_ports, 0, -1);
164 	if (ep == NULL)
165 		return;
166 	rep = endpoint_remote(ep);
167 	if (rep == NULL || rep->ep_type != EP_USB_CONTROLLER_PORT)
168 		return;
169 	port = endpoint_get_cookie(rep);
170 	if (port && port->up_connect)
171 		port->up_connect(port->up_cookie);
172 }
173 
174 void
tipd_disconnect(struct tipd_softc * sc)175 tipd_disconnect(struct tipd_softc *sc)
176 {
177 	struct endpoint *ep, *rep;
178 	struct usb_controller_port *port;
179 
180 	ep = endpoint_byreg(&sc->sc_ports, 0, -1);
181 	if (ep == NULL)
182 		return;
183 	rep = endpoint_remote(ep);
184 	if (rep == NULL || rep->ep_type != EP_USB_CONTROLLER_PORT)
185 		return;
186 	port = endpoint_get_cookie(rep);
187 	if (port && port->up_disconnect)
188 		port->up_disconnect(port->up_cookie);
189 }
190 
191 int
tipd_intr(void * arg)192 tipd_intr(void *arg)
193 {
194 	struct tipd_softc *sc = arg;
195 	uint64_t event;
196 	uint32_t status;
197 	int error;
198 
199 	error = tipd_read_8(sc, TPS_INT_EVENT_1, &event);
200 	if (error)
201 		return 0;
202 
203 	if (event == 0)
204 		return 0;
205 
206 	if (event & CD_INT_PLUG_EVENT) {
207 		error = tipd_read_4(sc, TPS_STATUS, &status);
208 		if (error)
209 			goto fail;
210 
211 		/*
212 		 * We may get a spurious plug event upon resume.  Make
213 		 * sure we only signal a new connection when the plug
214 		 * present state really changed.
215 		 */
216 		if ((status ^ sc->sc_status) & TPS_STATUS_PLUG_PRESENT) {
217 			if (status & TPS_STATUS_PLUG_PRESENT)
218 				tipd_connect(sc);
219 			else
220 				tipd_disconnect(sc);
221 			sc->sc_status = status;
222 		}
223 	}
224 
225 fail:
226 	tipd_write_8(sc, TPS_INT_CLEAR_1, event);
227 	return 1;
228 }
229 
230 int
tipd_read_4(struct tipd_softc * sc,uint8_t reg,uint32_t * val)231 tipd_read_4(struct tipd_softc *sc, uint8_t reg, uint32_t *val)
232 {
233 	uint8_t buf[5];
234 	int error;
235 
236 	iic_acquire_bus(sc->sc_tag, 0);
237 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
238 	    sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0);
239 	iic_release_bus(sc->sc_tag, 0);
240 
241 	if (error == 0)
242 		*val = lemtoh32(&buf[1]);
243 
244 	return error;
245 }
246 
247 int
tipd_read_8(struct tipd_softc * sc,uint8_t reg,uint64_t * val)248 tipd_read_8(struct tipd_softc *sc, uint8_t reg, uint64_t *val)
249 {
250 	uint8_t buf[9];
251 	int error;
252 
253 	iic_acquire_bus(sc->sc_tag, 0);
254 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
255 	    sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0);
256 	iic_release_bus(sc->sc_tag, 0);
257 
258 	if (error == 0)
259 		*val = lemtoh64(&buf[1]);
260 
261 	return error;
262 }
263 
264 int
tipd_write_4(struct tipd_softc * sc,uint8_t reg,uint32_t val)265 tipd_write_4(struct tipd_softc *sc, uint8_t reg, uint32_t val)
266 {
267 	uint8_t buf[5];
268 	int error;
269 
270 	buf[0] = 4;
271 	htolem32(&buf[1], val);
272 
273 	iic_acquire_bus(sc->sc_tag, 0);
274 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
275 	    sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0);
276 	iic_release_bus(sc->sc_tag, 0);
277 
278 	return error;
279 }
280 
281 int
tipd_write_8(struct tipd_softc * sc,uint8_t reg,uint64_t val)282 tipd_write_8(struct tipd_softc *sc, uint8_t reg, uint64_t val)
283 {
284 	uint8_t buf[9];
285 	int error;
286 
287 	buf[0] = 8;
288 	htolem64(&buf[1], val);
289 
290 	iic_acquire_bus(sc->sc_tag, 0);
291 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
292 	    sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0);
293 	iic_release_bus(sc->sc_tag, 0);
294 
295 	return error;
296 }
297 
298 int
tipd_exec(struct tipd_softc * sc,const char * cmd,const void * wbuf,size_t wlen,void * rbuf,size_t rlen)299 tipd_exec(struct tipd_softc *sc, const char *cmd, const void *wbuf,
300     size_t wlen, void *rbuf, size_t rlen)
301 {
302 	char buf[65];
303 	uint32_t val;
304 	int timo, error;
305 	uint8_t reg = TPS_DATA1;
306 
307 	if (wlen >= sizeof(buf) - 1)
308 		return EINVAL;
309 
310 	error = tipd_read_4(sc, TPS_CMD1, &val);
311 	if (error)
312 		return error;
313 	if (val == TPS_CMD("!CMD"))
314 		return EBUSY;
315 
316 	if (wlen > 0) {
317 		buf[0] = wlen;
318 		memcpy(&buf[1], wbuf, wlen);
319 		iic_acquire_bus(sc->sc_tag, 0);
320 		error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
321 		    sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0);
322 		iic_release_bus(sc->sc_tag, 0);
323 		if (error)
324 			return error;
325 	}
326 
327 	error = tipd_write_4(sc, TPS_CMD1, TPS_CMD(cmd));
328 	if (error)
329 		return error;
330 
331 	for (timo = 1000; timo > 0; timo--) {
332 		error = tipd_read_4(sc, TPS_CMD1, &val);
333 		if (error)
334 			return error;
335 		if (val == TPS_CMD("!CMD"))
336 			return EBUSY;
337 		if (val == 0)
338 			break;
339 		delay(10);
340 	}
341 
342 	if (timo == 0)
343 		return ETIMEDOUT;
344 
345 	if (rlen > 0) {
346 		memset(buf, 0, sizeof(buf));
347 		iic_acquire_bus(sc->sc_tag, 0);
348 		error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
349 		    sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0);
350 		iic_release_bus(sc->sc_tag, 0);
351 		if (error)
352 			return error;
353 		if (buf[0] < rlen)
354 			return EIO;
355 		memcpy(rbuf, &buf[1], rlen);
356 	}
357 
358 	return 0;
359 }
360