xref: /openbsd-src/sys/dev/fdt/tipd.c (revision 23d6429a03bae5a80cf2ced691eaaced32e41aa8)
1*23d6429aSkettenis /*	$OpenBSD: tipd.c,v 1.3 2023/07/23 11:42:44 kettenis Exp $	*/
2ade86d6eSkettenis /*
3ade86d6eSkettenis  * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org>
4ade86d6eSkettenis  *
5ade86d6eSkettenis  * Permission to use, copy, modify, and distribute this software for any
6ade86d6eSkettenis  * purpose with or without fee is hereby granted, provided that the above
7ade86d6eSkettenis  * copyright notice and this permission notice appear in all copies.
8ade86d6eSkettenis  *
9ade86d6eSkettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10ade86d6eSkettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11ade86d6eSkettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12ade86d6eSkettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13ade86d6eSkettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14ade86d6eSkettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15ade86d6eSkettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16ade86d6eSkettenis  */
17ade86d6eSkettenis 
18ade86d6eSkettenis #include <sys/param.h>
19ade86d6eSkettenis #include <sys/systm.h>
20ade86d6eSkettenis #include <sys/device.h>
21ade86d6eSkettenis #include <sys/malloc.h>
22ade86d6eSkettenis 
23ade86d6eSkettenis #include <machine/intr.h>
24ade86d6eSkettenis #include <machine/fdt.h>
25ade86d6eSkettenis 
26ade86d6eSkettenis #include <dev/ofw/openfirm.h>
27ade86d6eSkettenis #include <dev/ofw/ofw_misc.h>
28ade86d6eSkettenis #include <dev/ofw/fdt.h>
29ade86d6eSkettenis 
30ade86d6eSkettenis #include <dev/i2c/i2cvar.h>
31ade86d6eSkettenis 
328634d8ffSkettenis #define TPS_CMD1		0x08
338634d8ffSkettenis #define TPS_DATA1		0x09
34ade86d6eSkettenis #define TPS_INT_EVENT_1		0x14
35ade86d6eSkettenis #define TPS_INT_EVENT_2		0x15
36ade86d6eSkettenis #define TPS_INT_MASK_1		0x16
37ade86d6eSkettenis #define TPS_INT_MASK_2		0x17
38ade86d6eSkettenis #define TPS_INT_CLEAR_1		0x18
39ade86d6eSkettenis #define TPS_INT_CLEAR_2		0x19
40ade86d6eSkettenis #define TPS_STATUS		0x1a
41ade86d6eSkettenis #define  TPS_STATUS_PLUG_PRESENT	(1 << 0)
428634d8ffSkettenis #define TPS_SYSTEM_POWER_STATE	0x20
438634d8ffSkettenis #define  TPS_SYSTEM_POWER_STATE_S0	0
448634d8ffSkettenis #define  TPS_SYSTEM_POWER_STATE_S5	5
45*23d6429aSkettenis #define TPS_POWER_STATUS	0x3f
468634d8ffSkettenis 
478634d8ffSkettenis #define TPS_CMD(s)	((s[3] << 24) | (s[2] << 16) | (s[1] << 8) | s[0])
48ade86d6eSkettenis 
49ade86d6eSkettenis /*
50ade86d6eSkettenis  * Interrupt bits on the CD321x controllers used by Apple differ from
51ade86d6eSkettenis  * those used by the standard TPS6598x controllers.
52ade86d6eSkettenis  */
53ade86d6eSkettenis #define CD_INT_PLUG_EVENT		(1 << 1)
54ade86d6eSkettenis 
55ade86d6eSkettenis struct tipd_softc {
56ade86d6eSkettenis 	struct device		sc_dev;
57ade86d6eSkettenis 	i2c_tag_t		sc_tag;
58ade86d6eSkettenis 	i2c_addr_t		sc_addr;
59ade86d6eSkettenis 
60ade86d6eSkettenis 	void			*sc_ih;
61ade86d6eSkettenis 
62ade86d6eSkettenis 	struct device_ports	sc_ports;
63*23d6429aSkettenis 	uint32_t		sc_status;
64ade86d6eSkettenis };
65ade86d6eSkettenis 
66ade86d6eSkettenis int	tipd_match(struct device *, void *, void *);
67ade86d6eSkettenis void	tipd_attach(struct device *, struct device *, void *);
688634d8ffSkettenis int	tipd_activate(struct device *, int);
69ade86d6eSkettenis 
70ade86d6eSkettenis const struct cfattach tipd_ca = {
718634d8ffSkettenis 	sizeof(struct tipd_softc), tipd_match, tipd_attach, NULL,
728634d8ffSkettenis 	tipd_activate
73ade86d6eSkettenis };
74ade86d6eSkettenis 
75ade86d6eSkettenis struct cfdriver tipd_cd = {
76ade86d6eSkettenis 	NULL, "tipd", DV_DULL
77ade86d6eSkettenis };
78ade86d6eSkettenis 
79ade86d6eSkettenis int	tipd_intr(void *);
80ade86d6eSkettenis 
81ade86d6eSkettenis int	tipd_read_4(struct tipd_softc *, uint8_t, uint32_t *);
82ade86d6eSkettenis int	tipd_read_8(struct tipd_softc *, uint8_t, uint64_t *);
838634d8ffSkettenis int	tipd_write_4(struct tipd_softc *, uint8_t, uint32_t);
84ade86d6eSkettenis int	tipd_write_8(struct tipd_softc *, uint8_t, uint64_t);
858634d8ffSkettenis int	tipd_exec(struct tipd_softc *, const char *,
868634d8ffSkettenis 	    const void *, size_t, void *, size_t);
87ade86d6eSkettenis 
88ade86d6eSkettenis int
tipd_match(struct device * parent,void * match,void * aux)89ade86d6eSkettenis tipd_match(struct device *parent, void *match, void *aux)
90ade86d6eSkettenis {
91ade86d6eSkettenis 	struct i2c_attach_args *ia = aux;
92ade86d6eSkettenis 
93ade86d6eSkettenis 	return iic_is_compatible(ia, "apple,cd321x");
94ade86d6eSkettenis }
95ade86d6eSkettenis 
96ade86d6eSkettenis void
tipd_attach(struct device * parent,struct device * self,void * aux)97ade86d6eSkettenis tipd_attach(struct device *parent, struct device *self, void *aux)
98ade86d6eSkettenis {
99ade86d6eSkettenis 	struct tipd_softc *sc = (struct tipd_softc *)self;
100ade86d6eSkettenis 	struct i2c_attach_args *ia = aux;
101ade86d6eSkettenis 	int node = *(int *)ia->ia_cookie;
102ade86d6eSkettenis 
103ade86d6eSkettenis 	sc->sc_tag = ia->ia_tag;
104ade86d6eSkettenis 	sc->sc_addr = ia->ia_addr;
105ade86d6eSkettenis 
106ade86d6eSkettenis 	sc->sc_ih = fdt_intr_establish(node, IPL_BIO, tipd_intr,
107ade86d6eSkettenis 	    sc, sc->sc_dev.dv_xname);
108ade86d6eSkettenis 	if (sc->sc_ih == NULL) {
109ade86d6eSkettenis 		printf(": can't establish interrupt\n");
110ade86d6eSkettenis 		return;
111ade86d6eSkettenis 	}
112ade86d6eSkettenis 
113ade86d6eSkettenis 	printf("\n");
114ade86d6eSkettenis 
115*23d6429aSkettenis 	tipd_read_4(sc, TPS_STATUS, &sc->sc_status);
116ade86d6eSkettenis 	tipd_write_8(sc, TPS_INT_MASK_1, CD_INT_PLUG_EVENT);
117ade86d6eSkettenis 
118ade86d6eSkettenis 	node = OF_getnodebyname(node, "connector");
119ade86d6eSkettenis 	if (node) {
120ade86d6eSkettenis 		sc->sc_ports.dp_node = node;
121ade86d6eSkettenis 		device_ports_register(&sc->sc_ports, -1);
122ade86d6eSkettenis 	}
123ade86d6eSkettenis }
124ade86d6eSkettenis 
1258634d8ffSkettenis int
tipd_activate(struct device * self,int act)1268634d8ffSkettenis tipd_activate(struct device *self, int act)
1278634d8ffSkettenis {
1288634d8ffSkettenis 	struct tipd_softc *sc = (struct tipd_softc *)self;
1298634d8ffSkettenis 	uint8_t state;
1308634d8ffSkettenis 	int error;
1318634d8ffSkettenis 
1328634d8ffSkettenis 	switch (act) {
133*23d6429aSkettenis 	case DVACT_QUIESCE:
134*23d6429aSkettenis 		tipd_write_8(sc, TPS_INT_MASK_1, 0);
135*23d6429aSkettenis 		break;
1368634d8ffSkettenis 	case DVACT_SUSPEND:
1378634d8ffSkettenis 		state = TPS_SYSTEM_POWER_STATE_S5;
1388634d8ffSkettenis 		error = tipd_exec(sc, "SSPS", &state, sizeof(state), NULL, 0);
1398634d8ffSkettenis 		if (error)
1408634d8ffSkettenis 			printf("%s: powerdown failed\n", sc->sc_dev.dv_xname);
1418634d8ffSkettenis 		break;
1428634d8ffSkettenis 	case DVACT_RESUME:
1438634d8ffSkettenis 		state = TPS_SYSTEM_POWER_STATE_S0;
1448634d8ffSkettenis 		error = tipd_exec(sc, "SSPS", &state, sizeof(state), NULL, 0);
1458634d8ffSkettenis 		if (error)
1468634d8ffSkettenis 			printf("%s: powerup failed\n", sc->sc_dev.dv_xname);
1478634d8ffSkettenis 		break;
148*23d6429aSkettenis 	case DVACT_WAKEUP:
149*23d6429aSkettenis 		tipd_read_4(sc, TPS_STATUS, &sc->sc_status);
150*23d6429aSkettenis 		tipd_write_8(sc, TPS_INT_MASK_1, CD_INT_PLUG_EVENT);
151*23d6429aSkettenis 		break;
1528634d8ffSkettenis 	}
1538634d8ffSkettenis 
1548634d8ffSkettenis 	return 0;
1558634d8ffSkettenis }
1568634d8ffSkettenis 
157ade86d6eSkettenis void
tipd_connect(struct tipd_softc * sc)158ade86d6eSkettenis tipd_connect(struct tipd_softc *sc)
159ade86d6eSkettenis {
160ade86d6eSkettenis 	struct endpoint *ep, *rep;
161ade86d6eSkettenis 	struct usb_controller_port *port;
162ade86d6eSkettenis 
163ade86d6eSkettenis 	ep = endpoint_byreg(&sc->sc_ports, 0, -1);
164ade86d6eSkettenis 	if (ep == NULL)
165ade86d6eSkettenis 		return;
166ade86d6eSkettenis 	rep = endpoint_remote(ep);
167ade86d6eSkettenis 	if (rep == NULL || rep->ep_type != EP_USB_CONTROLLER_PORT)
168ade86d6eSkettenis 		return;
169ade86d6eSkettenis 	port = endpoint_get_cookie(rep);
170ade86d6eSkettenis 	if (port && port->up_connect)
171ade86d6eSkettenis 		port->up_connect(port->up_cookie);
172ade86d6eSkettenis }
173ade86d6eSkettenis 
174ade86d6eSkettenis void
tipd_disconnect(struct tipd_softc * sc)175ade86d6eSkettenis tipd_disconnect(struct tipd_softc *sc)
176ade86d6eSkettenis {
177ade86d6eSkettenis 	struct endpoint *ep, *rep;
178ade86d6eSkettenis 	struct usb_controller_port *port;
179ade86d6eSkettenis 
180ade86d6eSkettenis 	ep = endpoint_byreg(&sc->sc_ports, 0, -1);
181ade86d6eSkettenis 	if (ep == NULL)
182ade86d6eSkettenis 		return;
183ade86d6eSkettenis 	rep = endpoint_remote(ep);
184ade86d6eSkettenis 	if (rep == NULL || rep->ep_type != EP_USB_CONTROLLER_PORT)
185ade86d6eSkettenis 		return;
186ade86d6eSkettenis 	port = endpoint_get_cookie(rep);
187ade86d6eSkettenis 	if (port && port->up_disconnect)
188ade86d6eSkettenis 		port->up_disconnect(port->up_cookie);
189ade86d6eSkettenis }
190ade86d6eSkettenis 
191ade86d6eSkettenis int
tipd_intr(void * arg)192ade86d6eSkettenis tipd_intr(void *arg)
193ade86d6eSkettenis {
194ade86d6eSkettenis 	struct tipd_softc *sc = arg;
195ade86d6eSkettenis 	uint64_t event;
196ade86d6eSkettenis 	uint32_t status;
197ade86d6eSkettenis 	int error;
198ade86d6eSkettenis 
199ade86d6eSkettenis 	error = tipd_read_8(sc, TPS_INT_EVENT_1, &event);
200ade86d6eSkettenis 	if (error)
201ade86d6eSkettenis 		return 0;
202ade86d6eSkettenis 
203ade86d6eSkettenis 	if (event == 0)
204ade86d6eSkettenis 		return 0;
205ade86d6eSkettenis 
206ade86d6eSkettenis 	if (event & CD_INT_PLUG_EVENT) {
207ade86d6eSkettenis 		error = tipd_read_4(sc, TPS_STATUS, &status);
208ade86d6eSkettenis 		if (error)
209ade86d6eSkettenis 			goto fail;
210ade86d6eSkettenis 
211*23d6429aSkettenis 		/*
212*23d6429aSkettenis 		 * We may get a spurious plug event upon resume.  Make
213*23d6429aSkettenis 		 * sure we only signal a new connection when the plug
214*23d6429aSkettenis 		 * present state really changed.
215*23d6429aSkettenis 		 */
216*23d6429aSkettenis 		if ((status ^ sc->sc_status) & TPS_STATUS_PLUG_PRESENT) {
217ade86d6eSkettenis 			if (status & TPS_STATUS_PLUG_PRESENT)
218ade86d6eSkettenis 				tipd_connect(sc);
219ade86d6eSkettenis 			else
220ade86d6eSkettenis 				tipd_disconnect(sc);
221*23d6429aSkettenis 			sc->sc_status = status;
222*23d6429aSkettenis 		}
223ade86d6eSkettenis 	}
224ade86d6eSkettenis 
225ade86d6eSkettenis fail:
226ade86d6eSkettenis 	tipd_write_8(sc, TPS_INT_CLEAR_1, event);
227ade86d6eSkettenis 	return 1;
228ade86d6eSkettenis }
229ade86d6eSkettenis 
230ade86d6eSkettenis int
tipd_read_4(struct tipd_softc * sc,uint8_t reg,uint32_t * val)231ade86d6eSkettenis tipd_read_4(struct tipd_softc *sc, uint8_t reg, uint32_t *val)
232ade86d6eSkettenis {
233ade86d6eSkettenis 	uint8_t buf[5];
234ade86d6eSkettenis 	int error;
235ade86d6eSkettenis 
236ade86d6eSkettenis 	iic_acquire_bus(sc->sc_tag, 0);
237ade86d6eSkettenis 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
238ade86d6eSkettenis 	    sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0);
239ade86d6eSkettenis 	iic_release_bus(sc->sc_tag, 0);
240ade86d6eSkettenis 
241ade86d6eSkettenis 	if (error == 0)
242ade86d6eSkettenis 		*val = lemtoh32(&buf[1]);
243ade86d6eSkettenis 
244ade86d6eSkettenis 	return error;
245ade86d6eSkettenis }
246ade86d6eSkettenis 
247ade86d6eSkettenis int
tipd_read_8(struct tipd_softc * sc,uint8_t reg,uint64_t * val)248ade86d6eSkettenis tipd_read_8(struct tipd_softc *sc, uint8_t reg, uint64_t *val)
249ade86d6eSkettenis {
250ade86d6eSkettenis 	uint8_t buf[9];
251ade86d6eSkettenis 	int error;
252ade86d6eSkettenis 
253ade86d6eSkettenis 	iic_acquire_bus(sc->sc_tag, 0);
254ade86d6eSkettenis 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
255ade86d6eSkettenis 	    sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0);
256ade86d6eSkettenis 	iic_release_bus(sc->sc_tag, 0);
257ade86d6eSkettenis 
258ade86d6eSkettenis 	if (error == 0)
259ade86d6eSkettenis 		*val = lemtoh64(&buf[1]);
260ade86d6eSkettenis 
261ade86d6eSkettenis 	return error;
262ade86d6eSkettenis }
263ade86d6eSkettenis 
264ade86d6eSkettenis int
tipd_write_4(struct tipd_softc * sc,uint8_t reg,uint32_t val)2658634d8ffSkettenis tipd_write_4(struct tipd_softc *sc, uint8_t reg, uint32_t val)
2668634d8ffSkettenis {
2678634d8ffSkettenis 	uint8_t buf[5];
2688634d8ffSkettenis 	int error;
2698634d8ffSkettenis 
2708634d8ffSkettenis 	buf[0] = 4;
2718634d8ffSkettenis 	htolem32(&buf[1], val);
2728634d8ffSkettenis 
2738634d8ffSkettenis 	iic_acquire_bus(sc->sc_tag, 0);
2748634d8ffSkettenis 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
2758634d8ffSkettenis 	    sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0);
2768634d8ffSkettenis 	iic_release_bus(sc->sc_tag, 0);
2778634d8ffSkettenis 
2788634d8ffSkettenis 	return error;
2798634d8ffSkettenis }
2808634d8ffSkettenis 
2818634d8ffSkettenis int
tipd_write_8(struct tipd_softc * sc,uint8_t reg,uint64_t val)282ade86d6eSkettenis tipd_write_8(struct tipd_softc *sc, uint8_t reg, uint64_t val)
283ade86d6eSkettenis {
284ade86d6eSkettenis 	uint8_t buf[9];
285ade86d6eSkettenis 	int error;
286ade86d6eSkettenis 
287ade86d6eSkettenis 	buf[0] = 8;
288ade86d6eSkettenis 	htolem64(&buf[1], val);
289ade86d6eSkettenis 
290ade86d6eSkettenis 	iic_acquire_bus(sc->sc_tag, 0);
291ade86d6eSkettenis 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
292ade86d6eSkettenis 	    sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0);
293ade86d6eSkettenis 	iic_release_bus(sc->sc_tag, 0);
294ade86d6eSkettenis 
295ade86d6eSkettenis 	return error;
296ade86d6eSkettenis }
2978634d8ffSkettenis 
2988634d8ffSkettenis int
tipd_exec(struct tipd_softc * sc,const char * cmd,const void * wbuf,size_t wlen,void * rbuf,size_t rlen)2998634d8ffSkettenis tipd_exec(struct tipd_softc *sc, const char *cmd, const void *wbuf,
3008634d8ffSkettenis     size_t wlen, void *rbuf, size_t rlen)
3018634d8ffSkettenis {
3028634d8ffSkettenis 	char buf[65];
3038634d8ffSkettenis 	uint32_t val;
3048634d8ffSkettenis 	int timo, error;
3058634d8ffSkettenis 	uint8_t reg = TPS_DATA1;
3068634d8ffSkettenis 
3078634d8ffSkettenis 	if (wlen >= sizeof(buf) - 1)
3088634d8ffSkettenis 		return EINVAL;
3098634d8ffSkettenis 
3108634d8ffSkettenis 	error = tipd_read_4(sc, TPS_CMD1, &val);
3118634d8ffSkettenis 	if (error)
3128634d8ffSkettenis 		return error;
3138634d8ffSkettenis 	if (val == TPS_CMD("!CMD"))
3148634d8ffSkettenis 		return EBUSY;
3158634d8ffSkettenis 
3168634d8ffSkettenis 	if (wlen > 0) {
3178634d8ffSkettenis 		buf[0] = wlen;
3188634d8ffSkettenis 		memcpy(&buf[1], wbuf, wlen);
3198634d8ffSkettenis 		iic_acquire_bus(sc->sc_tag, 0);
3208634d8ffSkettenis 		error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
3218634d8ffSkettenis 		    sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0);
3228634d8ffSkettenis 		iic_release_bus(sc->sc_tag, 0);
3238634d8ffSkettenis 		if (error)
3248634d8ffSkettenis 			return error;
3258634d8ffSkettenis 	}
3268634d8ffSkettenis 
3278634d8ffSkettenis 	error = tipd_write_4(sc, TPS_CMD1, TPS_CMD(cmd));
3288634d8ffSkettenis 	if (error)
3298634d8ffSkettenis 		return error;
3308634d8ffSkettenis 
3318634d8ffSkettenis 	for (timo = 1000; timo > 0; timo--) {
3328634d8ffSkettenis 		error = tipd_read_4(sc, TPS_CMD1, &val);
3338634d8ffSkettenis 		if (error)
3348634d8ffSkettenis 			return error;
3358634d8ffSkettenis 		if (val == TPS_CMD("!CMD"))
3368634d8ffSkettenis 			return EBUSY;
3378634d8ffSkettenis 		if (val == 0)
3388634d8ffSkettenis 			break;
3398634d8ffSkettenis 		delay(10);
3408634d8ffSkettenis 	}
3418634d8ffSkettenis 
3428634d8ffSkettenis 	if (timo == 0)
3438634d8ffSkettenis 		return ETIMEDOUT;
3448634d8ffSkettenis 
3458634d8ffSkettenis 	if (rlen > 0) {
3468634d8ffSkettenis 		memset(buf, 0, sizeof(buf));
3478634d8ffSkettenis 		iic_acquire_bus(sc->sc_tag, 0);
3488634d8ffSkettenis 		error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
3498634d8ffSkettenis 		    sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0);
3508634d8ffSkettenis 		iic_release_bus(sc->sc_tag, 0);
3518634d8ffSkettenis 		if (error)
3528634d8ffSkettenis 			return error;
3538634d8ffSkettenis 		if (buf[0] < rlen)
3548634d8ffSkettenis 			return EIO;
3558634d8ffSkettenis 		memcpy(rbuf, &buf[1], rlen);
3568634d8ffSkettenis 	}
3578634d8ffSkettenis 
3588634d8ffSkettenis 	return 0;
3598634d8ffSkettenis }
360