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