xref: /openbsd-src/sys/dev/fdt/tipd.c (revision 3374c67d44f9b75b98444cbf63020f777792342e)
1 /*	$OpenBSD: tipd.c,v 1.1 2022/12/12 19:18:25 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_INT_EVENT_1		0x14
33 #define TPS_INT_EVENT_2		0x15
34 #define TPS_INT_MASK_1		0x16
35 #define TPS_INT_MASK_2		0x17
36 #define TPS_INT_CLEAR_1		0x18
37 #define TPS_INT_CLEAR_2		0x19
38 #define TPS_STATUS		0x1a
39 #define  TPS_STATUS_PLUG_PRESENT	(1 << 0)
40 
41 /*
42  * Interrupt bits on the CD321x controllers used by Apple differ from
43  * those used by the standard TPS6598x controllers.
44  */
45 #define CD_INT_PLUG_EVENT		(1 << 1)
46 
47 struct tipd_softc {
48 	struct device		sc_dev;
49 	i2c_tag_t		sc_tag;
50 	i2c_addr_t		sc_addr;
51 
52 	void			*sc_ih;
53 
54 	struct device_ports	sc_ports;
55 };
56 
57 int	tipd_match(struct device *, void *, void *);
58 void	tipd_attach(struct device *, struct device *, void *);
59 
60 const struct cfattach tipd_ca = {
61 	sizeof(struct tipd_softc), tipd_match, tipd_attach
62 };
63 
64 struct cfdriver tipd_cd = {
65 	NULL, "tipd", DV_DULL
66 };
67 
68 int	tipd_intr(void *);
69 
70 int	tipd_read_4(struct tipd_softc *, uint8_t, uint32_t *);
71 int	tipd_read_8(struct tipd_softc *, uint8_t, uint64_t *);
72 int	tipd_write_8(struct tipd_softc *, uint8_t, uint64_t);
73 
74 int
75 tipd_match(struct device *parent, void *match, void *aux)
76 {
77 	struct i2c_attach_args *ia = aux;
78 
79 	return iic_is_compatible(ia, "apple,cd321x");
80 }
81 
82 void
83 tipd_attach(struct device *parent, struct device *self, void *aux)
84 {
85 	struct tipd_softc *sc = (struct tipd_softc *)self;
86 	struct i2c_attach_args *ia = aux;
87 	int node = *(int *)ia->ia_cookie;
88 
89 	sc->sc_tag = ia->ia_tag;
90 	sc->sc_addr = ia->ia_addr;
91 
92 	sc->sc_ih = fdt_intr_establish(node, IPL_BIO, tipd_intr,
93 	    sc, sc->sc_dev.dv_xname);
94 	if (sc->sc_ih == NULL) {
95 		printf(": can't establish interrupt\n");
96 		return;
97 	}
98 
99 	printf("\n");
100 
101 	tipd_write_8(sc, TPS_INT_MASK_1, CD_INT_PLUG_EVENT);
102 
103 	node = OF_getnodebyname(node, "connector");
104 	if (node) {
105 		sc->sc_ports.dp_node = node;
106 		device_ports_register(&sc->sc_ports, -1);
107 	}
108 }
109 
110 void
111 tipd_connect(struct tipd_softc *sc)
112 {
113 	struct endpoint *ep, *rep;
114 	struct usb_controller_port *port;
115 
116 	ep = endpoint_byreg(&sc->sc_ports, 0, -1);
117 	if (ep == NULL)
118 		return;
119 	rep = endpoint_remote(ep);
120 	if (rep == NULL || rep->ep_type != EP_USB_CONTROLLER_PORT)
121 		return;
122 	port = endpoint_get_cookie(rep);
123 	if (port && port->up_connect)
124 		port->up_connect(port->up_cookie);
125 }
126 
127 void
128 tipd_disconnect(struct tipd_softc *sc)
129 {
130 	struct endpoint *ep, *rep;
131 	struct usb_controller_port *port;
132 
133 	ep = endpoint_byreg(&sc->sc_ports, 0, -1);
134 	if (ep == NULL)
135 		return;
136 	rep = endpoint_remote(ep);
137 	if (rep == NULL || rep->ep_type != EP_USB_CONTROLLER_PORT)
138 		return;
139 	port = endpoint_get_cookie(rep);
140 	if (port && port->up_disconnect)
141 		port->up_disconnect(port->up_cookie);
142 }
143 
144 int
145 tipd_intr(void *arg)
146 {
147 	struct tipd_softc *sc = arg;
148 	uint64_t event;
149 	uint32_t status;
150 	int error;
151 
152 	error = tipd_read_8(sc, TPS_INT_EVENT_1, &event);
153 	if (error)
154 		return 0;
155 
156 	if (event == 0)
157 		return 0;
158 
159 	if (event & CD_INT_PLUG_EVENT) {
160 		error = tipd_read_4(sc, TPS_STATUS, &status);
161 		if (error)
162 			goto fail;
163 
164 		if (status & TPS_STATUS_PLUG_PRESENT)
165 			tipd_connect(sc);
166 		else
167 			tipd_disconnect(sc);
168 	}
169 
170 fail:
171 	tipd_write_8(sc, TPS_INT_CLEAR_1, event);
172 	return 1;
173 }
174 
175 int
176 tipd_read_4(struct tipd_softc *sc, uint8_t reg, uint32_t *val)
177 {
178 	uint8_t buf[5];
179 	int error;
180 
181 	iic_acquire_bus(sc->sc_tag, 0);
182 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
183 	    sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0);
184 	iic_release_bus(sc->sc_tag, 0);
185 
186 	if (error == 0)
187 		*val = lemtoh32(&buf[1]);
188 
189 	return error;
190 }
191 
192 int
193 tipd_read_8(struct tipd_softc *sc, uint8_t reg, uint64_t *val)
194 {
195 	uint8_t buf[9];
196 	int error;
197 
198 	iic_acquire_bus(sc->sc_tag, 0);
199 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
200 	    sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0);
201 	iic_release_bus(sc->sc_tag, 0);
202 
203 	if (error == 0)
204 		*val = lemtoh64(&buf[1]);
205 
206 	return error;
207 }
208 
209 int
210 tipd_write_8(struct tipd_softc *sc, uint8_t reg, uint64_t val)
211 {
212 	uint8_t buf[9];
213 	int error;
214 
215 	buf[0] = 8;
216 	htolem64(&buf[1], val);
217 
218 	iic_acquire_bus(sc->sc_tag, 0);
219 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
220 	    sc->sc_addr, &reg, sizeof(reg), buf, sizeof(buf), 0);
221 	iic_release_bus(sc->sc_tag, 0);
222 
223 	return error;
224 }
225