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
61struct ec_softc {
62	device_t	dev;
63	int		have_arbitrator;
64	pcell_t		our_gpio;
65	pcell_t		ec_gpio;
66};
67
68struct 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
76static int
77bus_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
110static int
111bus_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
131static int
132ec_probe(device_t dev)
133{
134
135	device_set_desc(dev, "Chromebook Embedded Controller");
136	return (BUS_PROBE_DEFAULT);
137}
138
139static int
140fill_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
155int
156ec_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
205int 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
221static void
222configure_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 != -1 &&
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
243static int
244ec_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
270static int
271ec_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
284static 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
291static driver_t ec_driver = {
292	"chrome_ec",
293	ec_methods,
294	sizeof(struct ec_softc),
295};
296
297static devclass_t ec_devclass;
298
299DRIVER_MODULE(chrome_ec, iicbus, ec_driver, ec_devclass, 0, 0);
300MODULE_VERSION(chrome_ec, 1);
301MODULE_DEPEND(chrome_ec, iicbus, 1, 1, 1);
302