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