xref: /openbsd/sys/arch/armv7/exynos/crosec.c (revision 264ca280)
1 /* $OpenBSD: crosec.c,v 1.2 2016/06/10 06:42:53 jsg Exp $ */
2 /*
3  * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se>
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/sensors.h>
22 #include <sys/malloc.h>
23 
24 #include <armv7/exynos/crosecvar.h>
25 
26 #ifdef DEBUG
27 #define DPRINTF(x) printf x
28 #else
29 #define DPRINTF(x)
30 #endif
31 
32 int	cros_ec_match(struct device *, void *, void *);
33 void	cros_ec_attach(struct device *, struct device *, void *);
34 
35 int	cros_ec_send_command(struct cros_ec_softc *, uint8_t,
36 		int, const void *, int, uint8_t **, int);
37 int	cros_ec_command(struct cros_ec_softc *, uint8_t,
38 		int, const void *, int, void *, int);
39 int	cros_ec_command_inptr(struct cros_ec_softc *, uint8_t,
40 		int, const void *, int, uint8_t **, int);
41 int	cros_ec_i2c_command(struct cros_ec_softc *, uint8_t,
42 		int, const uint8_t *, int, uint8_t **, int);
43 int	cros_ec_calc_checksum(const uint8_t *, int);
44 
45 struct cfattach crosec_ca = {
46 	sizeof(struct cros_ec_softc), cros_ec_match, cros_ec_attach
47 };
48 
49 struct cfdriver crosec_cd = {
50 	NULL, "crosec", DV_DULL
51 };
52 
53 int
54 cros_ec_match(struct device *parent, void *match, void *aux)
55 {
56 	struct i2c_attach_args *ia = aux;
57 
58 	if (strcmp(ia->ia_name, "crosec") == 0)
59 		return 1;
60 	return 0;
61 }
62 
63 void
64 cros_ec_attach(struct device *parent, struct device *self, void *aux)
65 {
66 	struct cros_ec_softc *sc = (struct cros_ec_softc *)self;
67 	struct i2c_attach_args *ia = aux;
68 
69 	sc->sc_tag = ia->ia_tag;
70 	sc->sc_addr = ia->ia_addr;
71 
72 	printf("\n");
73 
74 	if (cros_ec_check_version(sc)) {
75 		printf("%s: could not initialize ChromeOS EC\n", __func__);
76 		return;
77 	}
78 
79 	if (cros_ec_init_keyboard(sc)) {
80 		printf("%s: could not initialize keyboard\n", __func__);
81 		return;
82 	}
83 }
84 
85 int
86 cros_ec_check_version(struct cros_ec_softc *sc)
87 {
88 	struct ec_params_hello req;
89 	struct ec_response_hello *resp;
90 
91 	sc->cmd_version_is_supported = 1;
92 	if (cros_ec_command_inptr(sc, EC_CMD_HELLO, 0, &req, sizeof(req),
93 				(uint8_t **)&resp, sizeof(*resp)) > 0) {
94 		/* new version supported */
95 		sc->cmd_version_is_supported = 1;
96 	} else {
97 		printf("%s: old EC interface not supported\n", __func__);
98 		return -1;
99 	}
100 
101 	return 0;
102 }
103 
104 int
105 cros_ec_i2c_command(struct cros_ec_softc *sc, uint8_t cmd, int cmd_version,
106 		const uint8_t *out, int out_len, uint8_t **in, int in_len)
107 {
108 	int out_bytes, in_bytes, ret;
109 	uint8_t *ptr = sc->out;
110 	uint8_t *inptr = sc->in;
111 
112 	inptr += sizeof(uint64_t); /* returned data should be 64-bit aligned */
113 	if (!sc->cmd_version_is_supported) {
114 		/* old-style */
115 		*ptr++ = cmd;
116 		out_bytes = out_len + 1;
117 		in_bytes = in_len + 2;
118 		inptr--; /* status byte */
119 	} else {
120 		/* new-style */
121 		*ptr++ = EC_CMD_VERSION0 + cmd_version;
122 		*ptr++ = cmd;
123 		*ptr++ = out_len;
124 		out_bytes = out_len + 4;
125 		in_bytes = in_len + 3;
126 		inptr -= 2; /* status byte, length */
127 	}
128 	memcpy(ptr, out, out_len);
129 	ptr += out_len;
130 
131 	if (sc->cmd_version_is_supported)
132 		*ptr++ = (uint8_t)
133 			 cros_ec_calc_checksum(sc->out, out_len + 3);
134 
135 	iic_acquire_bus(sc->sc_tag, 0);
136 	ret = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
137 	    sc->sc_addr, NULL, 0, &sc->out, out_bytes, 0);
138 	if (ret) {
139 		DPRINTF(("%s: I2C write failed\n", __func__));
140 		iic_release_bus(sc->sc_tag, 0);
141 		return -1;
142 	}
143 
144 	ret = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
145 	    sc->sc_addr, NULL, 0, inptr, in_bytes, 0);
146 	if (ret) {
147 		DPRINTF(("%s: I2C read failed\n", __func__));
148 		iic_release_bus(sc->sc_tag, 0);
149 		return -1;
150 	}
151 
152 	iic_release_bus(sc->sc_tag, 0);
153 
154 	if (*inptr != EC_RES_SUCCESS) {
155 		DPRINTF(("%s: bad result\n", __func__));
156 		return -(int)*inptr;
157 	}
158 
159 	if (sc->cmd_version_is_supported) {
160 		int len, csum;
161 
162 		len = inptr[1];
163 		if (len > sizeof(sc->in)) {
164 			DPRINTF(("%s: Received length too large\n", __func__));
165 			return -1;
166 		}
167 		csum = cros_ec_calc_checksum(inptr, 2 + len);
168 		if (csum != inptr[2 + len]) {
169 			DPRINTF(("%s: Invalid checksum\n", __func__));
170 			return -1;
171 		}
172 		in_len = min(in_len, len);
173 	}
174 
175 	*in = sc->in + sizeof(uint64_t);
176 
177 	return in_len;
178 }
179 
180 int
181 cros_ec_send_command(struct cros_ec_softc *sc, uint8_t cmd, int cmd_version,
182 		const void *out, int out_len, uint8_t **in, int in_len)
183 {
184 	return cros_ec_i2c_command(sc, cmd, cmd_version,
185 				(const uint8_t *)out, out_len, in, in_len);
186 }
187 
188 int
189 cros_ec_command(struct cros_ec_softc *sc, uint8_t cmd,
190 		int cmd_version, const void *out, int out_len,
191 		void *in, int in_len) {
192 	uint8_t *in_buffer;
193 	int len;
194 
195 	len = cros_ec_command_inptr(sc, cmd, cmd_version, out, out_len,
196 			&in_buffer, in_len);
197 
198 	if (len > 0) {
199 		if (in && in_buffer) {
200 			len = min(in_len, len);
201 			memmove(in, in_buffer, len);
202 		}
203 	}
204 	return len;
205 }
206 
207 int
208 cros_ec_command_inptr(struct cros_ec_softc *sc, uint8_t cmd,
209 		int cmd_version, const void *out, int out_len,
210 		uint8_t **inp, int in_len) {
211 	uint8_t *in;
212 	int len;
213 
214 	len = cros_ec_send_command(sc, cmd, cmd_version,
215 			(const uint8_t *)out, out_len, &in, in_len);
216 
217 	/* Wait for the command to complete. */
218 	if (len == -EC_RES_IN_PROGRESS) {
219 		struct ec_response_get_comms_status *resp;
220 
221 		do {
222 			int ret;
223 
224 			delay(50000);
225 			ret = cros_ec_send_command(sc, EC_CMD_GET_COMMS_STATUS, 0,
226 					NULL, 0,
227 					(uint8_t **)&resp, sizeof(*resp));
228 			if (ret < 0)
229 				return ret;
230 
231 			//timeout CROS_EC_CMD_TIMEOUT_MS
232 			//return -EC_RES_TIMEOUT
233 		} while (resp->flags & EC_COMMS_STATUS_PROCESSING);
234 
235 		/* Let's get the response. */
236 		len = cros_ec_send_command(sc, EC_CMD_RESEND_RESPONSE, 0,
237 				out, out_len, &in, in_len);
238 	}
239 
240 	if (inp != NULL)
241 		*inp = in;
242 
243 	return len;
244 }
245 
246 int
247 cros_ec_calc_checksum(const uint8_t *data, int size)
248 {
249 	int csum, i;
250 
251 	for (i = csum = 0; i < size; i++)
252 		csum += data[i];
253 	return csum & 0xff;
254 }
255 
256 int
257 cros_ec_scan_keyboard(struct cros_ec_softc *sc, uint8_t *scan, int len)
258 {
259 	if (cros_ec_command(sc, EC_CMD_CROS_EC_STATE, 0, NULL, 0, scan,
260 			len) < len)
261 		return -1;
262 
263 	return 0;
264 }
265 
266 int
267 cros_ec_info(struct cros_ec_softc *sc, struct ec_response_cros_ec_info *info)
268 {
269 	if (cros_ec_command(sc, EC_CMD_CROS_EC_INFO, 0, NULL, 0, info,
270 				sizeof(*info)) < sizeof(*info))
271 		return -1;
272 
273 	return 0;
274 }
275