xref: /openbsd-src/sys/arch/armv7/exynos/crosec.c (revision 9fdf0c627b1fec102f212f847a6f7676c1829e65)
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