1*9fdf0c62Smpi /* $OpenBSD: crosec.c,v 1.5 2021/10/24 17:52:27 mpi Exp $ */
207829fe8Sbmercer /*
307829fe8Sbmercer * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se>
407829fe8Sbmercer *
507829fe8Sbmercer * Permission to use, copy, modify, and distribute this software for any
607829fe8Sbmercer * purpose with or without fee is hereby granted, provided that the above
707829fe8Sbmercer * copyright notice and this permission notice appear in all copies.
807829fe8Sbmercer *
907829fe8Sbmercer * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1007829fe8Sbmercer * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1107829fe8Sbmercer * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1207829fe8Sbmercer * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1307829fe8Sbmercer * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1407829fe8Sbmercer * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1507829fe8Sbmercer * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1607829fe8Sbmercer */
1707829fe8Sbmercer
1807829fe8Sbmercer #include <sys/param.h>
1907829fe8Sbmercer #include <sys/systm.h>
2007829fe8Sbmercer #include <sys/device.h>
2107829fe8Sbmercer
2207829fe8Sbmercer #include <armv7/exynos/crosecvar.h>
2307829fe8Sbmercer
2407829fe8Sbmercer #ifdef DEBUG
2507829fe8Sbmercer #define DPRINTF(x) printf x
2607829fe8Sbmercer #else
2707829fe8Sbmercer #define DPRINTF(x)
2807829fe8Sbmercer #endif
2907829fe8Sbmercer
3007829fe8Sbmercer int cros_ec_match(struct device *, void *, void *);
3107829fe8Sbmercer void cros_ec_attach(struct device *, struct device *, void *);
3207829fe8Sbmercer
3307829fe8Sbmercer int cros_ec_send_command(struct cros_ec_softc *, uint8_t,
3407829fe8Sbmercer int, const void *, int, uint8_t **, int);
3507829fe8Sbmercer int cros_ec_command(struct cros_ec_softc *, uint8_t,
3607829fe8Sbmercer int, const void *, int, void *, int);
3707829fe8Sbmercer int cros_ec_command_inptr(struct cros_ec_softc *, uint8_t,
3807829fe8Sbmercer int, const void *, int, uint8_t **, int);
3907829fe8Sbmercer int cros_ec_i2c_command(struct cros_ec_softc *, uint8_t,
4007829fe8Sbmercer int, const uint8_t *, int, uint8_t **, int);
4107829fe8Sbmercer int cros_ec_calc_checksum(const uint8_t *, int);
4207829fe8Sbmercer
43*9fdf0c62Smpi const struct cfattach crosec_ca = {
4407829fe8Sbmercer sizeof(struct cros_ec_softc), cros_ec_match, cros_ec_attach
4507829fe8Sbmercer };
4607829fe8Sbmercer
4707829fe8Sbmercer struct cfdriver crosec_cd = {
4807829fe8Sbmercer NULL, "crosec", DV_DULL
4907829fe8Sbmercer };
5007829fe8Sbmercer
5107829fe8Sbmercer int
cros_ec_match(struct device * parent,void * match,void * aux)5207829fe8Sbmercer cros_ec_match(struct device *parent, void *match, void *aux)
5307829fe8Sbmercer {
5407829fe8Sbmercer struct i2c_attach_args *ia = aux;
5507829fe8Sbmercer
56f326c416Skettenis if (strcmp(ia->ia_name, "google,cros-ec-i2c") == 0)
5707829fe8Sbmercer return 1;
5807829fe8Sbmercer return 0;
5907829fe8Sbmercer }
6007829fe8Sbmercer
6107829fe8Sbmercer void
cros_ec_attach(struct device * parent,struct device * self,void * aux)6207829fe8Sbmercer cros_ec_attach(struct device *parent, struct device *self, void *aux)
6307829fe8Sbmercer {
6407829fe8Sbmercer struct cros_ec_softc *sc = (struct cros_ec_softc *)self;
6507829fe8Sbmercer struct i2c_attach_args *ia = aux;
6607829fe8Sbmercer
6707829fe8Sbmercer sc->sc_tag = ia->ia_tag;
6807829fe8Sbmercer sc->sc_addr = ia->ia_addr;
6907829fe8Sbmercer
7007829fe8Sbmercer printf("\n");
7107829fe8Sbmercer
7207829fe8Sbmercer if (cros_ec_check_version(sc)) {
7307829fe8Sbmercer printf("%s: could not initialize ChromeOS EC\n", __func__);
7407829fe8Sbmercer return;
7507829fe8Sbmercer }
7607829fe8Sbmercer
7707829fe8Sbmercer if (cros_ec_init_keyboard(sc)) {
7807829fe8Sbmercer printf("%s: could not initialize keyboard\n", __func__);
7907829fe8Sbmercer return;
8007829fe8Sbmercer }
8107829fe8Sbmercer }
8207829fe8Sbmercer
8307829fe8Sbmercer int
cros_ec_check_version(struct cros_ec_softc * sc)8407829fe8Sbmercer cros_ec_check_version(struct cros_ec_softc *sc)
8507829fe8Sbmercer {
8607829fe8Sbmercer struct ec_params_hello req;
8707829fe8Sbmercer struct ec_response_hello *resp;
8807829fe8Sbmercer
8907829fe8Sbmercer sc->cmd_version_is_supported = 1;
9007829fe8Sbmercer if (cros_ec_command_inptr(sc, EC_CMD_HELLO, 0, &req, sizeof(req),
9107829fe8Sbmercer (uint8_t **)&resp, sizeof(*resp)) > 0) {
9207829fe8Sbmercer /* new version supported */
9307829fe8Sbmercer sc->cmd_version_is_supported = 1;
9407829fe8Sbmercer } else {
9507829fe8Sbmercer printf("%s: old EC interface not supported\n", __func__);
9607829fe8Sbmercer return -1;
9707829fe8Sbmercer }
9807829fe8Sbmercer
9907829fe8Sbmercer return 0;
10007829fe8Sbmercer }
10107829fe8Sbmercer
10207829fe8Sbmercer 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)10307829fe8Sbmercer cros_ec_i2c_command(struct cros_ec_softc *sc, uint8_t cmd, int cmd_version,
10407829fe8Sbmercer const uint8_t *out, int out_len, uint8_t **in, int in_len)
10507829fe8Sbmercer {
10607829fe8Sbmercer int out_bytes, in_bytes, ret;
10707829fe8Sbmercer uint8_t *ptr = sc->out;
10807829fe8Sbmercer uint8_t *inptr = sc->in;
10907829fe8Sbmercer
11007829fe8Sbmercer inptr += sizeof(uint64_t); /* returned data should be 64-bit aligned */
11107829fe8Sbmercer if (!sc->cmd_version_is_supported) {
11207829fe8Sbmercer /* old-style */
11307829fe8Sbmercer *ptr++ = cmd;
11407829fe8Sbmercer out_bytes = out_len + 1;
11507829fe8Sbmercer in_bytes = in_len + 2;
11607829fe8Sbmercer inptr--; /* status byte */
11707829fe8Sbmercer } else {
11807829fe8Sbmercer /* new-style */
11907829fe8Sbmercer *ptr++ = EC_CMD_VERSION0 + cmd_version;
12007829fe8Sbmercer *ptr++ = cmd;
12107829fe8Sbmercer *ptr++ = out_len;
12207829fe8Sbmercer out_bytes = out_len + 4;
12307829fe8Sbmercer in_bytes = in_len + 3;
12407829fe8Sbmercer inptr -= 2; /* status byte, length */
12507829fe8Sbmercer }
12607829fe8Sbmercer memcpy(ptr, out, out_len);
12707829fe8Sbmercer ptr += out_len;
12807829fe8Sbmercer
12907829fe8Sbmercer if (sc->cmd_version_is_supported)
13007829fe8Sbmercer *ptr++ = (uint8_t)
13107829fe8Sbmercer cros_ec_calc_checksum(sc->out, out_len + 3);
13207829fe8Sbmercer
13307829fe8Sbmercer iic_acquire_bus(sc->sc_tag, 0);
13407829fe8Sbmercer ret = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
13507829fe8Sbmercer sc->sc_addr, NULL, 0, &sc->out, out_bytes, 0);
13607829fe8Sbmercer if (ret) {
13707829fe8Sbmercer DPRINTF(("%s: I2C write failed\n", __func__));
13807829fe8Sbmercer iic_release_bus(sc->sc_tag, 0);
13907829fe8Sbmercer return -1;
14007829fe8Sbmercer }
14107829fe8Sbmercer
14207829fe8Sbmercer ret = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
14307829fe8Sbmercer sc->sc_addr, NULL, 0, inptr, in_bytes, 0);
14407829fe8Sbmercer if (ret) {
14507829fe8Sbmercer DPRINTF(("%s: I2C read failed\n", __func__));
14607829fe8Sbmercer iic_release_bus(sc->sc_tag, 0);
14707829fe8Sbmercer return -1;
14807829fe8Sbmercer }
14907829fe8Sbmercer
15007829fe8Sbmercer iic_release_bus(sc->sc_tag, 0);
15107829fe8Sbmercer
15207829fe8Sbmercer if (*inptr != EC_RES_SUCCESS) {
15307829fe8Sbmercer DPRINTF(("%s: bad result\n", __func__));
15407829fe8Sbmercer return -(int)*inptr;
15507829fe8Sbmercer }
15607829fe8Sbmercer
15707829fe8Sbmercer if (sc->cmd_version_is_supported) {
15807829fe8Sbmercer int len, csum;
15907829fe8Sbmercer
16007829fe8Sbmercer len = inptr[1];
16107829fe8Sbmercer if (len > sizeof(sc->in)) {
16207829fe8Sbmercer DPRINTF(("%s: Received length too large\n", __func__));
16307829fe8Sbmercer return -1;
16407829fe8Sbmercer }
16507829fe8Sbmercer csum = cros_ec_calc_checksum(inptr, 2 + len);
16607829fe8Sbmercer if (csum != inptr[2 + len]) {
16707829fe8Sbmercer DPRINTF(("%s: Invalid checksum\n", __func__));
16807829fe8Sbmercer return -1;
16907829fe8Sbmercer }
17007829fe8Sbmercer in_len = min(in_len, len);
17107829fe8Sbmercer }
17207829fe8Sbmercer
17307829fe8Sbmercer *in = sc->in + sizeof(uint64_t);
17407829fe8Sbmercer
17507829fe8Sbmercer return in_len;
17607829fe8Sbmercer }
17707829fe8Sbmercer
17807829fe8Sbmercer 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)17907829fe8Sbmercer cros_ec_send_command(struct cros_ec_softc *sc, uint8_t cmd, int cmd_version,
18007829fe8Sbmercer const void *out, int out_len, uint8_t **in, int in_len)
18107829fe8Sbmercer {
18207829fe8Sbmercer return cros_ec_i2c_command(sc, cmd, cmd_version,
18307829fe8Sbmercer (const uint8_t *)out, out_len, in, in_len);
18407829fe8Sbmercer }
18507829fe8Sbmercer
18607829fe8Sbmercer 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)18707829fe8Sbmercer cros_ec_command(struct cros_ec_softc *sc, uint8_t cmd,
18807829fe8Sbmercer int cmd_version, const void *out, int out_len,
18907829fe8Sbmercer void *in, int in_len) {
19007829fe8Sbmercer uint8_t *in_buffer;
19107829fe8Sbmercer int len;
19207829fe8Sbmercer
19307829fe8Sbmercer len = cros_ec_command_inptr(sc, cmd, cmd_version, out, out_len,
19407829fe8Sbmercer &in_buffer, in_len);
19507829fe8Sbmercer
19607829fe8Sbmercer if (len > 0) {
19707829fe8Sbmercer if (in && in_buffer) {
19807829fe8Sbmercer len = min(in_len, len);
19907829fe8Sbmercer memmove(in, in_buffer, len);
20007829fe8Sbmercer }
20107829fe8Sbmercer }
20207829fe8Sbmercer return len;
20307829fe8Sbmercer }
20407829fe8Sbmercer
20507829fe8Sbmercer 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)20607829fe8Sbmercer cros_ec_command_inptr(struct cros_ec_softc *sc, uint8_t cmd,
20707829fe8Sbmercer int cmd_version, const void *out, int out_len,
20807829fe8Sbmercer uint8_t **inp, int in_len) {
20907829fe8Sbmercer uint8_t *in;
21007829fe8Sbmercer int len;
21107829fe8Sbmercer
21207829fe8Sbmercer len = cros_ec_send_command(sc, cmd, cmd_version,
21307829fe8Sbmercer (const uint8_t *)out, out_len, &in, in_len);
21407829fe8Sbmercer
21507829fe8Sbmercer /* Wait for the command to complete. */
21607829fe8Sbmercer if (len == -EC_RES_IN_PROGRESS) {
21707829fe8Sbmercer struct ec_response_get_comms_status *resp;
21807829fe8Sbmercer
21907829fe8Sbmercer do {
22007829fe8Sbmercer int ret;
22107829fe8Sbmercer
22207829fe8Sbmercer delay(50000);
2232f504a94Sjsg ret = cros_ec_send_command(sc, EC_CMD_GET_COMMS_STATUS, 0,
22407829fe8Sbmercer NULL, 0,
22507829fe8Sbmercer (uint8_t **)&resp, sizeof(*resp));
22607829fe8Sbmercer if (ret < 0)
22707829fe8Sbmercer return ret;
22807829fe8Sbmercer
22907829fe8Sbmercer //timeout CROS_EC_CMD_TIMEOUT_MS
23007829fe8Sbmercer //return -EC_RES_TIMEOUT
23107829fe8Sbmercer } while (resp->flags & EC_COMMS_STATUS_PROCESSING);
23207829fe8Sbmercer
23307829fe8Sbmercer /* Let's get the response. */
23407829fe8Sbmercer len = cros_ec_send_command(sc, EC_CMD_RESEND_RESPONSE, 0,
23507829fe8Sbmercer out, out_len, &in, in_len);
23607829fe8Sbmercer }
23707829fe8Sbmercer
23807829fe8Sbmercer if (inp != NULL)
23907829fe8Sbmercer *inp = in;
24007829fe8Sbmercer
24107829fe8Sbmercer return len;
24207829fe8Sbmercer }
24307829fe8Sbmercer
24407829fe8Sbmercer int
cros_ec_calc_checksum(const uint8_t * data,int size)24507829fe8Sbmercer cros_ec_calc_checksum(const uint8_t *data, int size)
24607829fe8Sbmercer {
24707829fe8Sbmercer int csum, i;
24807829fe8Sbmercer
24907829fe8Sbmercer for (i = csum = 0; i < size; i++)
25007829fe8Sbmercer csum += data[i];
25107829fe8Sbmercer return csum & 0xff;
25207829fe8Sbmercer }
25307829fe8Sbmercer
25407829fe8Sbmercer int
cros_ec_scan_keyboard(struct cros_ec_softc * sc,uint8_t * scan,int len)25507829fe8Sbmercer cros_ec_scan_keyboard(struct cros_ec_softc *sc, uint8_t *scan, int len)
25607829fe8Sbmercer {
25707829fe8Sbmercer if (cros_ec_command(sc, EC_CMD_CROS_EC_STATE, 0, NULL, 0, scan,
25807829fe8Sbmercer len) < len)
25907829fe8Sbmercer return -1;
26007829fe8Sbmercer
26107829fe8Sbmercer return 0;
26207829fe8Sbmercer }
26307829fe8Sbmercer
26407829fe8Sbmercer int
cros_ec_info(struct cros_ec_softc * sc,struct ec_response_cros_ec_info * info)26507829fe8Sbmercer cros_ec_info(struct cros_ec_softc *sc, struct ec_response_cros_ec_info *info)
26607829fe8Sbmercer {
26707829fe8Sbmercer if (cros_ec_command(sc, EC_CMD_CROS_EC_INFO, 0, NULL, 0, info,
26807829fe8Sbmercer sizeof(*info)) < sizeof(*info))
26907829fe8Sbmercer return -1;
27007829fe8Sbmercer
27107829fe8Sbmercer return 0;
27207829fe8Sbmercer }
273