xref: /NextBSD/sys/arm/samsung/exynos/chrome_ec.c (revision b137080f19736ee33fede2e88bb54438604cf86b)
1 /*-
2  * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPREC OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNEC FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINEC INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 /*
28  * Samsung Chromebook Embedded Controller
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/bus.h>
37 #include <sys/kernel.h>
38 #include <sys/module.h>
39 #include <sys/malloc.h>
40 #include <sys/rman.h>
41 #include <sys/timeet.h>
42 #include <sys/timetc.h>
43 #include <sys/watchdog.h>
44 #include <sys/gpio.h>
45 
46 #include <dev/ofw/openfirm.h>
47 #include <dev/ofw/ofw_bus.h>
48 #include <dev/ofw/ofw_bus_subr.h>
49 
50 #include <machine/bus.h>
51 #include <machine/cpu.h>
52 #include <machine/intr.h>
53 
54 #include <dev/iicbus/iiconf.h>
55 
56 #include "iicbus_if.h"
57 #include "gpio_if.h"
58 
59 #include <arm/samsung/exynos/chrome_ec.h>
60 
61 struct ec_softc {
62 	device_t	dev;
63 	int		have_arbitrator;
64 	pcell_t		our_gpio;
65 	pcell_t		ec_gpio;
66 };
67 
68 struct ec_softc *ec_sc;
69 
70 /*
71  * bus_claim, bus_release
72  * both functions used for bus arbitration
73  * in multi-master mode
74  */
75 
76 static int
bus_claim(struct ec_softc * sc)77 bus_claim(struct ec_softc *sc)
78 {
79 	device_t gpio_dev;
80 	int status;
81 
82 	if (sc->our_gpio == 0 || sc->ec_gpio == 0) {
83 		device_printf(sc->dev, "i2c arbitrator is not configured\n");
84 		return (1);
85 	}
86 
87 	gpio_dev = devclass_get_device(devclass_find("gpio"), 0);
88 	if (gpio_dev == NULL) {
89 		device_printf(sc->dev, "cant find gpio_dev\n");
90 		return (1);
91 	}
92 
93 	/* Say we want the bus */
94 	GPIO_PIN_SET(gpio_dev, sc->our_gpio, GPIO_PIN_LOW);
95 
96 	/* TODO: insert a delay to allow EC to react. */
97 
98 	/* Check EC decision */
99 	GPIO_PIN_GET(gpio_dev, sc->ec_gpio, &status);
100 
101 	if (status == 1) {
102 		/* Okay. We have bus */
103 		return (0);
104 	}
105 
106 	/* EC is master */
107 	return (-1);
108 }
109 
110 static int
bus_release(struct ec_softc * sc)111 bus_release(struct ec_softc *sc)
112 {
113 	device_t gpio_dev;
114 
115 	if (sc->our_gpio == 0 || sc->ec_gpio == 0) {
116 		device_printf(sc->dev, "i2c arbitrator is not configured\n");
117 		return (1);
118 	}
119 
120 	gpio_dev = devclass_get_device(devclass_find("gpio"), 0);
121 	if (gpio_dev == NULL) {
122 		device_printf(sc->dev, "cant find gpio_dev\n");
123 		return (1);
124 	}
125 
126 	GPIO_PIN_SET(gpio_dev, sc->our_gpio, GPIO_PIN_HIGH);
127 
128 	return (0);
129 }
130 
131 static int
ec_probe(device_t dev)132 ec_probe(device_t dev)
133 {
134 
135 	device_set_desc(dev, "Chromebook Embedded Controller");
136 	return (BUS_PROBE_DEFAULT);
137 }
138 
139 static int
fill_checksum(uint8_t * data_out,int len)140 fill_checksum(uint8_t *data_out, int len)
141 {
142 	int res;
143 	int i;
144 
145 	res = 0;
146 	for (i = 0; i < len; i++) {
147 		res += data_out[i];
148 	}
149 
150 	data_out[len] = (res & 0xff);
151 
152 	return (0);
153 }
154 
155 int
ec_command(uint8_t cmd,uint8_t * dout,uint8_t dout_len,uint8_t * dinp,uint8_t dinp_len)156 ec_command(uint8_t cmd, uint8_t *dout, uint8_t dout_len,
157     uint8_t *dinp, uint8_t dinp_len)
158 {
159 	struct ec_softc *sc;
160 	uint8_t *msg_dout;
161 	uint8_t *msg_dinp;
162 	int ret;
163 	int i;
164 
165 	msg_dout = malloc(dout_len + 4, M_DEVBUF, M_NOWAIT);
166 	msg_dinp = malloc(dinp_len + 3, M_DEVBUF, M_NOWAIT);
167 
168 	if (ec_sc == NULL)
169 		return (-1);
170 
171 	sc = ec_sc;
172 
173 	msg_dout[0] = EC_CMD_VERSION0;
174 	msg_dout[1] = cmd;
175 	msg_dout[2] = dout_len;
176 
177 	for (i = 0; i < dout_len; i++) {
178 		msg_dout[i + 3] = dout[i];
179 	};
180 
181 	fill_checksum(msg_dout, dout_len + 3);
182 
183 	struct iic_msg msgs[] = {
184 		{ 0x1e, IIC_M_WR, dout_len + 4, msg_dout, },
185 		{ 0x1e, IIC_M_RD, dinp_len + 3, msg_dinp, },
186 	};
187 
188 	ret = iicbus_transfer(sc->dev, msgs, 2);
189 	if (ret != 0) {
190 		device_printf(sc->dev, "i2c transfer returned %d\n", ret);
191 		free(msg_dout, M_DEVBUF);
192 		free(msg_dinp, M_DEVBUF);
193 		return (-1);
194 	}
195 
196 	for (i = 0; i < dinp_len; i++) {
197 		dinp[i] = msg_dinp[i + 2];
198 	};
199 
200 	free(msg_dout, M_DEVBUF);
201 	free(msg_dinp, M_DEVBUF);
202 	return (0);
203 }
204 
ec_hello(void)205 int ec_hello(void)
206 {
207 	uint8_t data_in[4];
208 	uint8_t data_out[4];
209 
210 	data_in[0] = 0x40;
211 	data_in[1] = 0x30;
212 	data_in[2] = 0x20;
213 	data_in[3] = 0x10;
214 
215 	ec_command(EC_CMD_HELLO, data_in, 4,
216 	    data_out, 4);
217 
218 	return (0);
219 }
220 
221 static void
configure_i2c_arbitrator(struct ec_softc * sc)222 configure_i2c_arbitrator(struct ec_softc *sc)
223 {
224 	phandle_t arbitrator;
225 
226 	/* TODO: look for compatible entry instead of hard-coded path */
227 	arbitrator = OF_finddevice("/i2c-arbitrator");
228 	if (arbitrator > 0 &&
229 	    OF_hasprop(arbitrator, "freebsd,our-gpio") &&
230 	    OF_hasprop(arbitrator, "freebsd,ec-gpio")) {
231 		sc->have_arbitrator = 1;
232 		OF_getencprop(arbitrator, "freebsd,our-gpio",
233 		    &sc->our_gpio, sizeof(sc->our_gpio));
234 		OF_getencprop(arbitrator, "freebsd,ec-gpio",
235 		    &sc->ec_gpio, sizeof(sc->ec_gpio));
236 	} else {
237 		sc->have_arbitrator = 0;
238 		sc->our_gpio = 0;
239 		sc->ec_gpio = 0;
240 	}
241 }
242 
243 static int
ec_attach(device_t dev)244 ec_attach(device_t dev)
245 {
246 	struct ec_softc *sc;
247 
248 	sc = device_get_softc(dev);
249 	sc->dev = dev;
250 
251 	ec_sc = sc;
252 
253 	configure_i2c_arbitrator(sc);
254 
255 	/*
256 	 * Claim the bus.
257 	 *
258 	 * We don't know cases when EC is master,
259 	 * so hold the bus forever for us.
260 	 *
261 	 */
262 
263 	if (sc->have_arbitrator && bus_claim(sc) != 0) {
264 		return (ENXIO);
265 	}
266 
267 	return (0);
268 }
269 
270 static int
ec_detach(device_t dev)271 ec_detach(device_t dev)
272 {
273 	struct ec_softc *sc;
274 
275 	sc = device_get_softc(dev);
276 
277 	if (sc->have_arbitrator) {
278 		bus_release(sc);
279 	}
280 
281 	return (0);
282 }
283 
284 static device_method_t ec_methods[] = {
285 	DEVMETHOD(device_probe,		ec_probe),
286 	DEVMETHOD(device_attach,	ec_attach),
287 	DEVMETHOD(device_detach,	ec_detach),
288 	{ 0, 0 }
289 };
290 
291 static driver_t ec_driver = {
292 	"chrome_ec",
293 	ec_methods,
294 	sizeof(struct ec_softc),
295 };
296 
297 static devclass_t ec_devclass;
298 
299 DRIVER_MODULE(chrome_ec, iicbus, ec_driver, ec_devclass, 0, 0);
300 MODULE_VERSION(chrome_ec, 1);
301 MODULE_DEPEND(chrome_ec, iicbus, 1, 1, 1);
302