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 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 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 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 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 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 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 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 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 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 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