xref: /openbmc/u-boot/drivers/misc/cros_ec.c (revision d94604d5)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
288364387SHung-ying Tyan /*
388364387SHung-ying Tyan  * Chromium OS cros_ec driver
488364387SHung-ying Tyan  *
588364387SHung-ying Tyan  * Copyright (c) 2012 The Chromium OS Authors.
688364387SHung-ying Tyan  */
788364387SHung-ying Tyan 
888364387SHung-ying Tyan /*
9836bb6e8SSimon Glass  * This is the interface to the Chrome OS EC. It provides keyboard functions,
10836bb6e8SSimon Glass  * power control and battery management. Quite a few other functions are
11836bb6e8SSimon Glass  * provided to enable the EC software to be updated, talk to the EC's I2C bus
12836bb6e8SSimon Glass  * and store a small amount of data in a memory which persists while the EC
13836bb6e8SSimon Glass  * is not reset.
1488364387SHung-ying Tyan  */
1588364387SHung-ying Tyan 
16ac806523SSimon Glass #define LOG_CATEGORY UCLASS_CROS_EC
17ac806523SSimon Glass 
1888364387SHung-ying Tyan #include <common.h>
1988364387SHung-ying Tyan #include <command.h>
2084d6cbd3SSimon Glass #include <dm.h>
2188364387SHung-ying Tyan #include <i2c.h>
2288364387SHung-ying Tyan #include <cros_ec.h>
2388364387SHung-ying Tyan #include <fdtdec.h>
2488364387SHung-ying Tyan #include <malloc.h>
2588364387SHung-ying Tyan #include <spi.h>
261221ce45SMasahiro Yamada #include <linux/errno.h>
2788364387SHung-ying Tyan #include <asm/io.h>
2888364387SHung-ying Tyan #include <asm-generic/gpio.h>
2984d6cbd3SSimon Glass #include <dm/device-internal.h>
302ec9d171SSimon Glass #include <dm/of_extra.h>
3184d6cbd3SSimon Glass #include <dm/uclass-internal.h>
3288364387SHung-ying Tyan 
3388364387SHung-ying Tyan #ifdef DEBUG_TRACE
3488364387SHung-ying Tyan #define debug_trace(fmt, b...)	debug(fmt, #b)
3588364387SHung-ying Tyan #else
3688364387SHung-ying Tyan #define debug_trace(fmt, b...)
3788364387SHung-ying Tyan #endif
3888364387SHung-ying Tyan 
3988364387SHung-ying Tyan enum {
4088364387SHung-ying Tyan 	/* Timeout waiting for a flash erase command to complete */
4188364387SHung-ying Tyan 	CROS_EC_CMD_TIMEOUT_MS	= 5000,
4288364387SHung-ying Tyan 	/* Timeout waiting for a synchronous hash to be recomputed */
4388364387SHung-ying Tyan 	CROS_EC_CMD_HASH_TIMEOUT_MS = 2000,
4488364387SHung-ying Tyan };
4588364387SHung-ying Tyan 
4672ef8bfdSSimon Glass #define INVALID_HCMD 0xFF
4772ef8bfdSSimon Glass 
4872ef8bfdSSimon Glass /*
4972ef8bfdSSimon Glass  * Map UHEPI masks to non UHEPI commands in order to support old EC FW
5072ef8bfdSSimon Glass  * which does not support UHEPI command.
5172ef8bfdSSimon Glass  */
5272ef8bfdSSimon Glass static const struct {
5372ef8bfdSSimon Glass 	u8 set_cmd;
5472ef8bfdSSimon Glass 	u8 clear_cmd;
5572ef8bfdSSimon Glass 	u8 get_cmd;
5672ef8bfdSSimon Glass } event_map[] = {
5772ef8bfdSSimon Glass 	[EC_HOST_EVENT_MAIN] = {
5872ef8bfdSSimon Glass 		INVALID_HCMD, EC_CMD_HOST_EVENT_CLEAR,
5972ef8bfdSSimon Glass 		INVALID_HCMD,
6072ef8bfdSSimon Glass 	},
6172ef8bfdSSimon Glass 	[EC_HOST_EVENT_B] = {
6272ef8bfdSSimon Glass 		INVALID_HCMD, EC_CMD_HOST_EVENT_CLEAR_B,
6372ef8bfdSSimon Glass 		EC_CMD_HOST_EVENT_GET_B,
6472ef8bfdSSimon Glass 	},
6572ef8bfdSSimon Glass 	[EC_HOST_EVENT_SCI_MASK] = {
6672ef8bfdSSimon Glass 		EC_CMD_HOST_EVENT_SET_SCI_MASK, INVALID_HCMD,
6772ef8bfdSSimon Glass 		EC_CMD_HOST_EVENT_GET_SCI_MASK,
6872ef8bfdSSimon Glass 	},
6972ef8bfdSSimon Glass 	[EC_HOST_EVENT_SMI_MASK] = {
7072ef8bfdSSimon Glass 		EC_CMD_HOST_EVENT_SET_SMI_MASK, INVALID_HCMD,
7172ef8bfdSSimon Glass 		EC_CMD_HOST_EVENT_GET_SMI_MASK,
7272ef8bfdSSimon Glass 	},
7372ef8bfdSSimon Glass 	[EC_HOST_EVENT_ALWAYS_REPORT_MASK] = {
7472ef8bfdSSimon Glass 		INVALID_HCMD, INVALID_HCMD, INVALID_HCMD,
7572ef8bfdSSimon Glass 	},
7672ef8bfdSSimon Glass 	[EC_HOST_EVENT_ACTIVE_WAKE_MASK] = {
7772ef8bfdSSimon Glass 		EC_CMD_HOST_EVENT_SET_WAKE_MASK, INVALID_HCMD,
7872ef8bfdSSimon Glass 		EC_CMD_HOST_EVENT_GET_WAKE_MASK,
7972ef8bfdSSimon Glass 	},
8072ef8bfdSSimon Glass 	[EC_HOST_EVENT_LAZY_WAKE_MASK_S0IX] = {
8172ef8bfdSSimon Glass 		EC_CMD_HOST_EVENT_SET_WAKE_MASK, INVALID_HCMD,
8272ef8bfdSSimon Glass 		EC_CMD_HOST_EVENT_GET_WAKE_MASK,
8372ef8bfdSSimon Glass 	},
8472ef8bfdSSimon Glass 	[EC_HOST_EVENT_LAZY_WAKE_MASK_S3] = {
8572ef8bfdSSimon Glass 		EC_CMD_HOST_EVENT_SET_WAKE_MASK, INVALID_HCMD,
8672ef8bfdSSimon Glass 		EC_CMD_HOST_EVENT_GET_WAKE_MASK,
8772ef8bfdSSimon Glass 	},
8872ef8bfdSSimon Glass 	[EC_HOST_EVENT_LAZY_WAKE_MASK_S5] = {
8972ef8bfdSSimon Glass 		EC_CMD_HOST_EVENT_SET_WAKE_MASK, INVALID_HCMD,
9072ef8bfdSSimon Glass 		EC_CMD_HOST_EVENT_GET_WAKE_MASK,
9172ef8bfdSSimon Glass 	},
9272ef8bfdSSimon Glass };
9372ef8bfdSSimon Glass 
cros_ec_dump_data(const char * name,int cmd,const uint8_t * data,int len)9488364387SHung-ying Tyan void cros_ec_dump_data(const char *name, int cmd, const uint8_t *data, int len)
9588364387SHung-ying Tyan {
9688364387SHung-ying Tyan #ifdef DEBUG
9788364387SHung-ying Tyan 	int i;
9888364387SHung-ying Tyan 
9988364387SHung-ying Tyan 	printf("%s: ", name);
10088364387SHung-ying Tyan 	if (cmd != -1)
10188364387SHung-ying Tyan 		printf("cmd=%#x: ", cmd);
10288364387SHung-ying Tyan 	for (i = 0; i < len; i++)
10388364387SHung-ying Tyan 		printf("%02x ", data[i]);
10488364387SHung-ying Tyan 	printf("\n");
10588364387SHung-ying Tyan #endif
10688364387SHung-ying Tyan }
10788364387SHung-ying Tyan 
10888364387SHung-ying Tyan /*
10988364387SHung-ying Tyan  * Calculate a simple 8-bit checksum of a data block
11088364387SHung-ying Tyan  *
11188364387SHung-ying Tyan  * @param data	Data block to checksum
11288364387SHung-ying Tyan  * @param size	Size of data block in bytes
11388364387SHung-ying Tyan  * @return checksum value (0 to 255)
11488364387SHung-ying Tyan  */
cros_ec_calc_checksum(const uint8_t * data,int size)11588364387SHung-ying Tyan int cros_ec_calc_checksum(const uint8_t *data, int size)
11688364387SHung-ying Tyan {
11788364387SHung-ying Tyan 	int csum, i;
11888364387SHung-ying Tyan 
11988364387SHung-ying Tyan 	for (i = csum = 0; i < size; i++)
12088364387SHung-ying Tyan 		csum += data[i];
12188364387SHung-ying Tyan 	return csum & 0xff;
12288364387SHung-ying Tyan }
12388364387SHung-ying Tyan 
1242d8ede58SSimon Glass /**
1252d8ede58SSimon Glass  * Create a request packet for protocol version 3.
1262d8ede58SSimon Glass  *
1272d8ede58SSimon Glass  * The packet is stored in the device's internal output buffer.
1282d8ede58SSimon Glass  *
1292d8ede58SSimon Glass  * @param dev		CROS-EC device
1302d8ede58SSimon Glass  * @param cmd		Command to send (EC_CMD_...)
1312d8ede58SSimon Glass  * @param cmd_version	Version of command to send (EC_VER_...)
1322d8ede58SSimon Glass  * @param dout          Output data (may be NULL If dout_len=0)
1332d8ede58SSimon Glass  * @param dout_len      Size of output data in bytes
1342d8ede58SSimon Glass  * @return packet size in bytes, or <0 if error.
1352d8ede58SSimon Glass  */
create_proto3_request(struct cros_ec_dev * cdev,int cmd,int cmd_version,const void * dout,int dout_len)1366322a7b6SSimon Glass static int create_proto3_request(struct cros_ec_dev *cdev,
1372d8ede58SSimon Glass 				 int cmd, int cmd_version,
1382d8ede58SSimon Glass 				 const void *dout, int dout_len)
1392d8ede58SSimon Glass {
1406322a7b6SSimon Glass 	struct ec_host_request *rq = (struct ec_host_request *)cdev->dout;
1412d8ede58SSimon Glass 	int out_bytes = dout_len + sizeof(*rq);
1422d8ede58SSimon Glass 
1432d8ede58SSimon Glass 	/* Fail if output size is too big */
1446322a7b6SSimon Glass 	if (out_bytes > (int)sizeof(cdev->dout)) {
1452d8ede58SSimon Glass 		debug("%s: Cannot send %d bytes\n", __func__, dout_len);
1462d8ede58SSimon Glass 		return -EC_RES_REQUEST_TRUNCATED;
1472d8ede58SSimon Glass 	}
1482d8ede58SSimon Glass 
1492d8ede58SSimon Glass 	/* Fill in request packet */
1502d8ede58SSimon Glass 	rq->struct_version = EC_HOST_REQUEST_VERSION;
1512d8ede58SSimon Glass 	rq->checksum = 0;
1522d8ede58SSimon Glass 	rq->command = cmd;
1532d8ede58SSimon Glass 	rq->command_version = cmd_version;
1542d8ede58SSimon Glass 	rq->reserved = 0;
1552d8ede58SSimon Glass 	rq->data_len = dout_len;
1562d8ede58SSimon Glass 
1572d8ede58SSimon Glass 	/* Copy data after header */
1582d8ede58SSimon Glass 	memcpy(rq + 1, dout, dout_len);
1592d8ede58SSimon Glass 
1602d8ede58SSimon Glass 	/* Write checksum field so the entire packet sums to 0 */
1616322a7b6SSimon Glass 	rq->checksum = (uint8_t)(-cros_ec_calc_checksum(cdev->dout, out_bytes));
1622d8ede58SSimon Glass 
1636322a7b6SSimon Glass 	cros_ec_dump_data("out", cmd, cdev->dout, out_bytes);
1642d8ede58SSimon Glass 
1652d8ede58SSimon Glass 	/* Return size of request packet */
1662d8ede58SSimon Glass 	return out_bytes;
1672d8ede58SSimon Glass }
1682d8ede58SSimon Glass 
1692d8ede58SSimon Glass /**
1702d8ede58SSimon Glass  * Prepare the device to receive a protocol version 3 response.
1712d8ede58SSimon Glass  *
1722d8ede58SSimon Glass  * @param dev		CROS-EC device
1732d8ede58SSimon Glass  * @param din_len       Maximum size of response in bytes
1742d8ede58SSimon Glass  * @return maximum expected number of bytes in response, or <0 if error.
1752d8ede58SSimon Glass  */
prepare_proto3_response_buffer(struct cros_ec_dev * cdev,int din_len)1766322a7b6SSimon Glass static int prepare_proto3_response_buffer(struct cros_ec_dev *cdev, int din_len)
1772d8ede58SSimon Glass {
1782d8ede58SSimon Glass 	int in_bytes = din_len + sizeof(struct ec_host_response);
1792d8ede58SSimon Glass 
1802d8ede58SSimon Glass 	/* Fail if input size is too big */
1816322a7b6SSimon Glass 	if (in_bytes > (int)sizeof(cdev->din)) {
1822d8ede58SSimon Glass 		debug("%s: Cannot receive %d bytes\n", __func__, din_len);
1832d8ede58SSimon Glass 		return -EC_RES_RESPONSE_TOO_BIG;
1842d8ede58SSimon Glass 	}
1852d8ede58SSimon Glass 
1862d8ede58SSimon Glass 	/* Return expected size of response packet */
1872d8ede58SSimon Glass 	return in_bytes;
1882d8ede58SSimon Glass }
1892d8ede58SSimon Glass 
1902d8ede58SSimon Glass /**
1912d8ede58SSimon Glass  * Handle a protocol version 3 response packet.
1922d8ede58SSimon Glass  *
1932d8ede58SSimon Glass  * The packet must already be stored in the device's internal input buffer.
1942d8ede58SSimon Glass  *
1952d8ede58SSimon Glass  * @param dev		CROS-EC device
1962d8ede58SSimon Glass  * @param dinp          Returns pointer to response data
1972d8ede58SSimon Glass  * @param din_len       Maximum size of response in bytes
1988bbb38b1SSimon Glass  * @return number of bytes of response data, or <0 if error. Note that error
1998bbb38b1SSimon Glass  * codes can be from errno.h or -ve EC_RES_INVALID_CHECKSUM values (and they
2008bbb38b1SSimon Glass  * overlap!)
2012d8ede58SSimon Glass  */
handle_proto3_response(struct cros_ec_dev * dev,uint8_t ** dinp,int din_len)2022d8ede58SSimon Glass static int handle_proto3_response(struct cros_ec_dev *dev,
2032d8ede58SSimon Glass 				  uint8_t **dinp, int din_len)
2042d8ede58SSimon Glass {
2052d8ede58SSimon Glass 	struct ec_host_response *rs = (struct ec_host_response *)dev->din;
2062d8ede58SSimon Glass 	int in_bytes;
2072d8ede58SSimon Glass 	int csum;
2082d8ede58SSimon Glass 
2092d8ede58SSimon Glass 	cros_ec_dump_data("in-header", -1, dev->din, sizeof(*rs));
2102d8ede58SSimon Glass 
2112d8ede58SSimon Glass 	/* Check input data */
2122d8ede58SSimon Glass 	if (rs->struct_version != EC_HOST_RESPONSE_VERSION) {
2132d8ede58SSimon Glass 		debug("%s: EC response version mismatch\n", __func__);
2142d8ede58SSimon Glass 		return -EC_RES_INVALID_RESPONSE;
2152d8ede58SSimon Glass 	}
2162d8ede58SSimon Glass 
2172d8ede58SSimon Glass 	if (rs->reserved) {
2182d8ede58SSimon Glass 		debug("%s: EC response reserved != 0\n", __func__);
2192d8ede58SSimon Glass 		return -EC_RES_INVALID_RESPONSE;
2202d8ede58SSimon Glass 	}
2212d8ede58SSimon Glass 
2222d8ede58SSimon Glass 	if (rs->data_len > din_len) {
2232d8ede58SSimon Glass 		debug("%s: EC returned too much data\n", __func__);
2242d8ede58SSimon Glass 		return -EC_RES_RESPONSE_TOO_BIG;
2252d8ede58SSimon Glass 	}
2262d8ede58SSimon Glass 
2272d8ede58SSimon Glass 	cros_ec_dump_data("in-data", -1, dev->din + sizeof(*rs), rs->data_len);
2282d8ede58SSimon Glass 
2292d8ede58SSimon Glass 	/* Update in_bytes to actual data size */
2302d8ede58SSimon Glass 	in_bytes = sizeof(*rs) + rs->data_len;
2312d8ede58SSimon Glass 
2322d8ede58SSimon Glass 	/* Verify checksum */
2332d8ede58SSimon Glass 	csum = cros_ec_calc_checksum(dev->din, in_bytes);
2342d8ede58SSimon Glass 	if (csum) {
2352d8ede58SSimon Glass 		debug("%s: EC response checksum invalid: 0x%02x\n", __func__,
2362d8ede58SSimon Glass 		      csum);
2372d8ede58SSimon Glass 		return -EC_RES_INVALID_CHECKSUM;
2382d8ede58SSimon Glass 	}
2392d8ede58SSimon Glass 
2402d8ede58SSimon Glass 	/* Return error result, if any */
2412d8ede58SSimon Glass 	if (rs->result)
2422d8ede58SSimon Glass 		return -(int)rs->result;
2432d8ede58SSimon Glass 
2442d8ede58SSimon Glass 	/* If we're still here, set response data pointer and return length */
2452d8ede58SSimon Glass 	*dinp = (uint8_t *)(rs + 1);
2462d8ede58SSimon Glass 
2472d8ede58SSimon Glass 	return rs->data_len;
2482d8ede58SSimon Glass }
2492d8ede58SSimon Glass 
send_command_proto3(struct cros_ec_dev * cdev,int cmd,int cmd_version,const void * dout,int dout_len,uint8_t ** dinp,int din_len)2506322a7b6SSimon Glass static int send_command_proto3(struct cros_ec_dev *cdev,
2512d8ede58SSimon Glass 			       int cmd, int cmd_version,
2522d8ede58SSimon Glass 			       const void *dout, int dout_len,
2532d8ede58SSimon Glass 			       uint8_t **dinp, int din_len)
2542d8ede58SSimon Glass {
25584d6cbd3SSimon Glass 	struct dm_cros_ec_ops *ops;
2562d8ede58SSimon Glass 	int out_bytes, in_bytes;
2572d8ede58SSimon Glass 	int rv;
2582d8ede58SSimon Glass 
2592d8ede58SSimon Glass 	/* Create request packet */
2606322a7b6SSimon Glass 	out_bytes = create_proto3_request(cdev, cmd, cmd_version,
2612d8ede58SSimon Glass 					  dout, dout_len);
2622d8ede58SSimon Glass 	if (out_bytes < 0)
2632d8ede58SSimon Glass 		return out_bytes;
2642d8ede58SSimon Glass 
2652d8ede58SSimon Glass 	/* Prepare response buffer */
2666322a7b6SSimon Glass 	in_bytes = prepare_proto3_response_buffer(cdev, din_len);
2672d8ede58SSimon Glass 	if (in_bytes < 0)
2682d8ede58SSimon Glass 		return in_bytes;
2692d8ede58SSimon Glass 
2706322a7b6SSimon Glass 	ops = dm_cros_ec_get_ops(cdev->dev);
2716322a7b6SSimon Glass 	rv = ops->packet ? ops->packet(cdev->dev, out_bytes, in_bytes) :
2726322a7b6SSimon Glass 			-ENOSYS;
2732d8ede58SSimon Glass 	if (rv < 0)
2742d8ede58SSimon Glass 		return rv;
2752d8ede58SSimon Glass 
2762d8ede58SSimon Glass 	/* Process the response */
2776322a7b6SSimon Glass 	return handle_proto3_response(cdev, dinp, din_len);
2782d8ede58SSimon Glass }
2792d8ede58SSimon Glass 
send_command(struct cros_ec_dev * dev,uint cmd,int cmd_version,const void * dout,int dout_len,uint8_t ** dinp,int din_len)2809fea76f5SSimon Glass static int send_command(struct cros_ec_dev *dev, uint cmd, int cmd_version,
28188364387SHung-ying Tyan 			const void *dout, int dout_len,
28288364387SHung-ying Tyan 			uint8_t **dinp, int din_len)
28388364387SHung-ying Tyan {
28484d6cbd3SSimon Glass 	struct dm_cros_ec_ops *ops;
2852d8ede58SSimon Glass 	int ret = -1;
2862d8ede58SSimon Glass 
2872d8ede58SSimon Glass 	/* Handle protocol version 3 support */
2882d8ede58SSimon Glass 	if (dev->protocol_version == 3) {
2892d8ede58SSimon Glass 		return send_command_proto3(dev, cmd, cmd_version,
2902d8ede58SSimon Glass 					   dout, dout_len, dinp, din_len);
2912d8ede58SSimon Glass 	}
29288364387SHung-ying Tyan 
29384d6cbd3SSimon Glass 	ops = dm_cros_ec_get_ops(dev->dev);
29484d6cbd3SSimon Glass 	ret = ops->command(dev->dev, cmd, cmd_version,
29584d6cbd3SSimon Glass 			   (const uint8_t *)dout, dout_len, dinp, din_len);
29688364387SHung-ying Tyan 
29788364387SHung-ying Tyan 	return ret;
29888364387SHung-ying Tyan }
29988364387SHung-ying Tyan 
30088364387SHung-ying Tyan /**
30188364387SHung-ying Tyan  * Send a command to the CROS-EC device and return the reply.
30288364387SHung-ying Tyan  *
30388364387SHung-ying Tyan  * The device's internal input/output buffers are used.
30488364387SHung-ying Tyan  *
30588364387SHung-ying Tyan  * @param dev		CROS-EC device
30688364387SHung-ying Tyan  * @param cmd		Command to send (EC_CMD_...)
30788364387SHung-ying Tyan  * @param cmd_version	Version of command to send (EC_VER_...)
30888364387SHung-ying Tyan  * @param dout          Output data (may be NULL If dout_len=0)
30988364387SHung-ying Tyan  * @param dout_len      Size of output data in bytes
31088364387SHung-ying Tyan  * @param dinp          Response data (may be NULL If din_len=0).
31188364387SHung-ying Tyan  *			If not NULL, it will be updated to point to the data
31288364387SHung-ying Tyan  *			and will always be double word aligned (64-bits)
31388364387SHung-ying Tyan  * @param din_len       Maximum size of response in bytes
3148bbb38b1SSimon Glass  * @return number of bytes in response, or -ve on error
31588364387SHung-ying Tyan  */
ec_command_inptr(struct udevice * dev,uint8_t cmd,int cmd_version,const void * dout,int dout_len,uint8_t ** dinp,int din_len)3166322a7b6SSimon Glass static int ec_command_inptr(struct udevice *dev, uint8_t cmd,
317e6c5c94aSSimon Glass 			    int cmd_version, const void *dout, int dout_len,
318e6c5c94aSSimon Glass 			    uint8_t **dinp, int din_len)
31988364387SHung-ying Tyan {
3206322a7b6SSimon Glass 	struct cros_ec_dev *cdev = dev_get_uclass_priv(dev);
3212ab83f0dSSimon Glass 	uint8_t *din = NULL;
32288364387SHung-ying Tyan 	int len;
32388364387SHung-ying Tyan 
3246322a7b6SSimon Glass 	len = send_command(cdev, cmd, cmd_version, dout, dout_len, &din,
3256322a7b6SSimon Glass 			   din_len);
32688364387SHung-ying Tyan 
32788364387SHung-ying Tyan 	/* If the command doesn't complete, wait a while */
32888364387SHung-ying Tyan 	if (len == -EC_RES_IN_PROGRESS) {
3292ab83f0dSSimon Glass 		struct ec_response_get_comms_status *resp = NULL;
33088364387SHung-ying Tyan 		ulong start;
33188364387SHung-ying Tyan 
33288364387SHung-ying Tyan 		/* Wait for command to complete */
33388364387SHung-ying Tyan 		start = get_timer(0);
33488364387SHung-ying Tyan 		do {
33588364387SHung-ying Tyan 			int ret;
33688364387SHung-ying Tyan 
33788364387SHung-ying Tyan 			mdelay(50);	/* Insert some reasonable delay */
3386322a7b6SSimon Glass 			ret = send_command(cdev, EC_CMD_GET_COMMS_STATUS, 0,
33988364387SHung-ying Tyan 					   NULL, 0,
34088364387SHung-ying Tyan 					   (uint8_t **)&resp, sizeof(*resp));
34188364387SHung-ying Tyan 			if (ret < 0)
34288364387SHung-ying Tyan 				return ret;
34388364387SHung-ying Tyan 
34488364387SHung-ying Tyan 			if (get_timer(start) > CROS_EC_CMD_TIMEOUT_MS) {
34588364387SHung-ying Tyan 				debug("%s: Command %#02x timeout\n",
34688364387SHung-ying Tyan 				      __func__, cmd);
34788364387SHung-ying Tyan 				return -EC_RES_TIMEOUT;
34888364387SHung-ying Tyan 			}
34988364387SHung-ying Tyan 		} while (resp->flags & EC_COMMS_STATUS_PROCESSING);
35088364387SHung-ying Tyan 
35188364387SHung-ying Tyan 		/* OK it completed, so read the status response */
35288364387SHung-ying Tyan 		/* not sure why it was 0 for the last argument */
3536322a7b6SSimon Glass 		len = send_command(cdev, EC_CMD_RESEND_RESPONSE, 0, NULL, 0,
3546322a7b6SSimon Glass 				   &din, din_len);
35588364387SHung-ying Tyan 	}
35688364387SHung-ying Tyan 
357e907bf2dSSimon Glass 	debug("%s: len=%d, din=%p\n", __func__, len, din);
35888364387SHung-ying Tyan 	if (dinp) {
35988364387SHung-ying Tyan 		/* If we have any data to return, it must be 64bit-aligned */
36088364387SHung-ying Tyan 		assert(len <= 0 || !((uintptr_t)din & 7));
36188364387SHung-ying Tyan 		*dinp = din;
36288364387SHung-ying Tyan 	}
36388364387SHung-ying Tyan 
36488364387SHung-ying Tyan 	return len;
36588364387SHung-ying Tyan }
36688364387SHung-ying Tyan 
36788364387SHung-ying Tyan /**
36888364387SHung-ying Tyan  * Send a command to the CROS-EC device and return the reply.
36988364387SHung-ying Tyan  *
37088364387SHung-ying Tyan  * The device's internal input/output buffers are used.
37188364387SHung-ying Tyan  *
37288364387SHung-ying Tyan  * @param dev		CROS-EC device
37388364387SHung-ying Tyan  * @param cmd		Command to send (EC_CMD_...)
37488364387SHung-ying Tyan  * @param cmd_version	Version of command to send (EC_VER_...)
37588364387SHung-ying Tyan  * @param dout          Output data (may be NULL If dout_len=0)
37688364387SHung-ying Tyan  * @param dout_len      Size of output data in bytes
37788364387SHung-ying Tyan  * @param din           Response data (may be NULL If din_len=0).
37888364387SHung-ying Tyan  *			It not NULL, it is a place for ec_command() to copy the
37988364387SHung-ying Tyan  *      data to.
38088364387SHung-ying Tyan  * @param din_len       Maximum size of response in bytes
3818bbb38b1SSimon Glass  * @return number of bytes in response, or -ve on error
38288364387SHung-ying Tyan  */
ec_command(struct udevice * dev,uint cmd,int cmd_version,const void * dout,int dout_len,void * din,int din_len)3839fea76f5SSimon Glass static int ec_command(struct udevice *dev, uint cmd, int cmd_version,
38488364387SHung-ying Tyan 		      const void *dout, int dout_len,
38588364387SHung-ying Tyan 		      void *din, int din_len)
38688364387SHung-ying Tyan {
38788364387SHung-ying Tyan 	uint8_t *in_buffer;
38888364387SHung-ying Tyan 	int len;
38988364387SHung-ying Tyan 
39088364387SHung-ying Tyan 	assert((din_len == 0) || din);
39188364387SHung-ying Tyan 	len = ec_command_inptr(dev, cmd, cmd_version, dout, dout_len,
39288364387SHung-ying Tyan 			       &in_buffer, din_len);
39388364387SHung-ying Tyan 	if (len > 0) {
39488364387SHung-ying Tyan 		/*
39588364387SHung-ying Tyan 		 * If we were asked to put it somewhere, do so, otherwise just
39688364387SHung-ying Tyan 		 * disregard the result.
39788364387SHung-ying Tyan 		 */
39888364387SHung-ying Tyan 		if (din && in_buffer) {
39988364387SHung-ying Tyan 			assert(len <= din_len);
40088364387SHung-ying Tyan 			memmove(din, in_buffer, len);
40188364387SHung-ying Tyan 		}
40288364387SHung-ying Tyan 	}
40388364387SHung-ying Tyan 	return len;
40488364387SHung-ying Tyan }
40588364387SHung-ying Tyan 
cros_ec_scan_keyboard(struct udevice * dev,struct mbkp_keyscan * scan)406745009c4SSimon Glass int cros_ec_scan_keyboard(struct udevice *dev, struct mbkp_keyscan *scan)
40788364387SHung-ying Tyan {
4086322a7b6SSimon Glass  	if (ec_command(dev, EC_CMD_MKBP_STATE, 0, NULL, 0, scan,
4092ab83f0dSSimon Glass 		       sizeof(scan->data)) != sizeof(scan->data))
41088364387SHung-ying Tyan 		return -1;
41188364387SHung-ying Tyan 
41288364387SHung-ying Tyan 	return 0;
41388364387SHung-ying Tyan }
41488364387SHung-ying Tyan 
cros_ec_read_id(struct udevice * dev,char * id,int maxlen)4156322a7b6SSimon Glass int cros_ec_read_id(struct udevice *dev, char *id, int maxlen)
41688364387SHung-ying Tyan {
41788364387SHung-ying Tyan 	struct ec_response_get_version *r;
418ac806523SSimon Glass 	int ret;
41988364387SHung-ying Tyan 
420ac806523SSimon Glass 	ret = ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0,
421ac806523SSimon Glass 			       (uint8_t **)&r, sizeof(*r));
422ac806523SSimon Glass 	if (ret != sizeof(*r)) {
423*a749c09aSSimon Glass 		log_err("Got rc %d, expected %u\n", ret, (uint)sizeof(*r));
42488364387SHung-ying Tyan 		return -1;
425ac806523SSimon Glass 	}
42688364387SHung-ying Tyan 
4272ab83f0dSSimon Glass 	if (maxlen > (int)sizeof(r->version_string_ro))
42888364387SHung-ying Tyan 		maxlen = sizeof(r->version_string_ro);
42988364387SHung-ying Tyan 
43088364387SHung-ying Tyan 	switch (r->current_image) {
43188364387SHung-ying Tyan 	case EC_IMAGE_RO:
43288364387SHung-ying Tyan 		memcpy(id, r->version_string_ro, maxlen);
43388364387SHung-ying Tyan 		break;
43488364387SHung-ying Tyan 	case EC_IMAGE_RW:
43588364387SHung-ying Tyan 		memcpy(id, r->version_string_rw, maxlen);
43688364387SHung-ying Tyan 		break;
43788364387SHung-ying Tyan 	default:
438ac806523SSimon Glass 		log_err("Invalid EC image %d\n", r->current_image);
43988364387SHung-ying Tyan 		return -1;
44088364387SHung-ying Tyan 	}
44188364387SHung-ying Tyan 
44288364387SHung-ying Tyan 	id[maxlen - 1] = '\0';
44388364387SHung-ying Tyan 	return 0;
44488364387SHung-ying Tyan }
44588364387SHung-ying Tyan 
cros_ec_read_version(struct udevice * dev,struct ec_response_get_version ** versionp)4466322a7b6SSimon Glass int cros_ec_read_version(struct udevice *dev,
44788364387SHung-ying Tyan 			 struct ec_response_get_version **versionp)
44888364387SHung-ying Tyan {
44988364387SHung-ying Tyan 	if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0,
45088364387SHung-ying Tyan 			(uint8_t **)versionp, sizeof(**versionp))
4512ab83f0dSSimon Glass 			!= sizeof(**versionp))
45288364387SHung-ying Tyan 		return -1;
45388364387SHung-ying Tyan 
45488364387SHung-ying Tyan 	return 0;
45588364387SHung-ying Tyan }
45688364387SHung-ying Tyan 
cros_ec_read_build_info(struct udevice * dev,char ** strp)4576322a7b6SSimon Glass int cros_ec_read_build_info(struct udevice *dev, char **strp)
45888364387SHung-ying Tyan {
45988364387SHung-ying Tyan 	if (ec_command_inptr(dev, EC_CMD_GET_BUILD_INFO, 0, NULL, 0,
460836bb6e8SSimon Glass 			(uint8_t **)strp, EC_PROTO2_MAX_PARAM_SIZE) < 0)
46188364387SHung-ying Tyan 		return -1;
46288364387SHung-ying Tyan 
46388364387SHung-ying Tyan 	return 0;
46488364387SHung-ying Tyan }
46588364387SHung-ying Tyan 
cros_ec_read_current_image(struct udevice * dev,enum ec_current_image * image)4666322a7b6SSimon Glass int cros_ec_read_current_image(struct udevice *dev,
46788364387SHung-ying Tyan 			       enum ec_current_image *image)
46888364387SHung-ying Tyan {
46988364387SHung-ying Tyan 	struct ec_response_get_version *r;
47088364387SHung-ying Tyan 
47188364387SHung-ying Tyan 	if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0,
4722ab83f0dSSimon Glass 			(uint8_t **)&r, sizeof(*r)) != sizeof(*r))
47388364387SHung-ying Tyan 		return -1;
47488364387SHung-ying Tyan 
47588364387SHung-ying Tyan 	*image = r->current_image;
47688364387SHung-ying Tyan 	return 0;
47788364387SHung-ying Tyan }
47888364387SHung-ying Tyan 
cros_ec_wait_on_hash_done(struct udevice * dev,struct ec_response_vboot_hash * hash)4796322a7b6SSimon Glass static int cros_ec_wait_on_hash_done(struct udevice *dev,
48088364387SHung-ying Tyan 				     struct ec_response_vboot_hash *hash)
48188364387SHung-ying Tyan {
48288364387SHung-ying Tyan 	struct ec_params_vboot_hash p;
48388364387SHung-ying Tyan 	ulong start;
48488364387SHung-ying Tyan 
48588364387SHung-ying Tyan 	start = get_timer(0);
48688364387SHung-ying Tyan 	while (hash->status == EC_VBOOT_HASH_STATUS_BUSY) {
48788364387SHung-ying Tyan 		mdelay(50);	/* Insert some reasonable delay */
48888364387SHung-ying Tyan 
48988364387SHung-ying Tyan 		p.cmd = EC_VBOOT_HASH_GET;
49088364387SHung-ying Tyan 		if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p),
49188364387SHung-ying Tyan 		       hash, sizeof(*hash)) < 0)
49288364387SHung-ying Tyan 			return -1;
49388364387SHung-ying Tyan 
49488364387SHung-ying Tyan 		if (get_timer(start) > CROS_EC_CMD_HASH_TIMEOUT_MS) {
49588364387SHung-ying Tyan 			debug("%s: EC_VBOOT_HASH_GET timeout\n", __func__);
49688364387SHung-ying Tyan 			return -EC_RES_TIMEOUT;
49788364387SHung-ying Tyan 		}
49888364387SHung-ying Tyan 	}
49988364387SHung-ying Tyan 	return 0;
50088364387SHung-ying Tyan }
50188364387SHung-ying Tyan 
cros_ec_read_hash(struct udevice * dev,uint hash_offset,struct ec_response_vboot_hash * hash)502a12ef7e2SSimon Glass int cros_ec_read_hash(struct udevice *dev, uint hash_offset,
503a12ef7e2SSimon Glass 		      struct ec_response_vboot_hash *hash)
50488364387SHung-ying Tyan {
50588364387SHung-ying Tyan 	struct ec_params_vboot_hash p;
50688364387SHung-ying Tyan 	int rv;
50788364387SHung-ying Tyan 
50888364387SHung-ying Tyan 	p.cmd = EC_VBOOT_HASH_GET;
509a12ef7e2SSimon Glass 	p.offset = hash_offset;
51088364387SHung-ying Tyan 	if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p),
51188364387SHung-ying Tyan 		       hash, sizeof(*hash)) < 0)
51288364387SHung-ying Tyan 		return -1;
51388364387SHung-ying Tyan 
51488364387SHung-ying Tyan 	/* If the EC is busy calculating the hash, fidget until it's done. */
51588364387SHung-ying Tyan 	rv = cros_ec_wait_on_hash_done(dev, hash);
51688364387SHung-ying Tyan 	if (rv)
51788364387SHung-ying Tyan 		return rv;
51888364387SHung-ying Tyan 
51988364387SHung-ying Tyan 	/* If the hash is valid, we're done. Otherwise, we have to kick it off
52088364387SHung-ying Tyan 	 * again and wait for it to complete. Note that we explicitly assume
52188364387SHung-ying Tyan 	 * that hashing zero bytes is always wrong, even though that would
52288364387SHung-ying Tyan 	 * produce a valid hash value. */
52388364387SHung-ying Tyan 	if (hash->status == EC_VBOOT_HASH_STATUS_DONE && hash->size)
52488364387SHung-ying Tyan 		return 0;
52588364387SHung-ying Tyan 
52688364387SHung-ying Tyan 	debug("%s: No valid hash (status=%d size=%d). Compute one...\n",
52788364387SHung-ying Tyan 	      __func__, hash->status, hash->size);
52888364387SHung-ying Tyan 
529836bb6e8SSimon Glass 	p.cmd = EC_VBOOT_HASH_START;
53088364387SHung-ying Tyan 	p.hash_type = EC_VBOOT_HASH_TYPE_SHA256;
53188364387SHung-ying Tyan 	p.nonce_size = 0;
532a12ef7e2SSimon Glass 	p.offset = hash_offset;
53388364387SHung-ying Tyan 
53488364387SHung-ying Tyan 	if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p),
53588364387SHung-ying Tyan 		       hash, sizeof(*hash)) < 0)
53688364387SHung-ying Tyan 		return -1;
53788364387SHung-ying Tyan 
53888364387SHung-ying Tyan 	rv = cros_ec_wait_on_hash_done(dev, hash);
53988364387SHung-ying Tyan 	if (rv)
54088364387SHung-ying Tyan 		return rv;
54188364387SHung-ying Tyan 
54288364387SHung-ying Tyan 	debug("%s: hash done\n", __func__);
54388364387SHung-ying Tyan 
54488364387SHung-ying Tyan 	return 0;
54588364387SHung-ying Tyan }
54688364387SHung-ying Tyan 
cros_ec_invalidate_hash(struct udevice * dev)5476322a7b6SSimon Glass static int cros_ec_invalidate_hash(struct udevice *dev)
54888364387SHung-ying Tyan {
54988364387SHung-ying Tyan 	struct ec_params_vboot_hash p;
55088364387SHung-ying Tyan 	struct ec_response_vboot_hash *hash;
55188364387SHung-ying Tyan 
55288364387SHung-ying Tyan 	/* We don't have an explict command for the EC to discard its current
55388364387SHung-ying Tyan 	 * hash value, so we'll just tell it to calculate one that we know is
55488364387SHung-ying Tyan 	 * wrong (we claim that hashing zero bytes is always invalid).
55588364387SHung-ying Tyan 	 */
55688364387SHung-ying Tyan 	p.cmd = EC_VBOOT_HASH_RECALC;
55788364387SHung-ying Tyan 	p.hash_type = EC_VBOOT_HASH_TYPE_SHA256;
55888364387SHung-ying Tyan 	p.nonce_size = 0;
55988364387SHung-ying Tyan 	p.offset = 0;
56088364387SHung-ying Tyan 	p.size = 0;
56188364387SHung-ying Tyan 
56288364387SHung-ying Tyan 	debug("%s:\n", __func__);
56388364387SHung-ying Tyan 
56488364387SHung-ying Tyan 	if (ec_command_inptr(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p),
56588364387SHung-ying Tyan 		       (uint8_t **)&hash, sizeof(*hash)) < 0)
56688364387SHung-ying Tyan 		return -1;
56788364387SHung-ying Tyan 
56888364387SHung-ying Tyan 	/* No need to wait for it to finish */
56988364387SHung-ying Tyan 	return 0;
57088364387SHung-ying Tyan }
57188364387SHung-ying Tyan 
cros_ec_reboot(struct udevice * dev,enum ec_reboot_cmd cmd,uint8_t flags)5726322a7b6SSimon Glass int cros_ec_reboot(struct udevice *dev, enum ec_reboot_cmd cmd, uint8_t flags)
57388364387SHung-ying Tyan {
57488364387SHung-ying Tyan 	struct ec_params_reboot_ec p;
57588364387SHung-ying Tyan 
57688364387SHung-ying Tyan 	p.cmd = cmd;
57788364387SHung-ying Tyan 	p.flags = flags;
57888364387SHung-ying Tyan 
57988364387SHung-ying Tyan 	if (ec_command_inptr(dev, EC_CMD_REBOOT_EC, 0, &p, sizeof(p), NULL, 0)
58088364387SHung-ying Tyan 			< 0)
58188364387SHung-ying Tyan 		return -1;
58288364387SHung-ying Tyan 
58388364387SHung-ying Tyan 	if (!(flags & EC_REBOOT_FLAG_ON_AP_SHUTDOWN)) {
58488364387SHung-ying Tyan 		/*
58588364387SHung-ying Tyan 		 * EC reboot will take place immediately so delay to allow it
58688364387SHung-ying Tyan 		 * to complete.  Note that some reboot types (EC_REBOOT_COLD)
58788364387SHung-ying Tyan 		 * will reboot the AP as well, in which case we won't actually
58888364387SHung-ying Tyan 		 * get to this point.
58988364387SHung-ying Tyan 		 */
59088364387SHung-ying Tyan 		/*
59188364387SHung-ying Tyan 		 * TODO(rspangler@chromium.org): Would be nice if we had a
59288364387SHung-ying Tyan 		 * better way to determine when the reboot is complete.  Could
59388364387SHung-ying Tyan 		 * we poll a memory-mapped LPC value?
59488364387SHung-ying Tyan 		 */
59588364387SHung-ying Tyan 		udelay(50000);
59688364387SHung-ying Tyan 	}
59788364387SHung-ying Tyan 
59888364387SHung-ying Tyan 	return 0;
59988364387SHung-ying Tyan }
60088364387SHung-ying Tyan 
cros_ec_interrupt_pending(struct udevice * dev)601745009c4SSimon Glass int cros_ec_interrupt_pending(struct udevice *dev)
60288364387SHung-ying Tyan {
603745009c4SSimon Glass 	struct cros_ec_dev *cdev = dev_get_uclass_priv(dev);
604745009c4SSimon Glass 
60588364387SHung-ying Tyan 	/* no interrupt support : always poll */
606745009c4SSimon Glass 	if (!dm_gpio_is_valid(&cdev->ec_int))
6072ab83f0dSSimon Glass 		return -ENOENT;
60888364387SHung-ying Tyan 
609745009c4SSimon Glass 	return dm_gpio_get_value(&cdev->ec_int);
61088364387SHung-ying Tyan }
61188364387SHung-ying Tyan 
cros_ec_info(struct udevice * dev,struct ec_response_mkbp_info * info)6126322a7b6SSimon Glass int cros_ec_info(struct udevice *dev, struct ec_response_mkbp_info *info)
61388364387SHung-ying Tyan {
614836bb6e8SSimon Glass 	if (ec_command(dev, EC_CMD_MKBP_INFO, 0, NULL, 0, info,
6152ab83f0dSSimon Glass 		       sizeof(*info)) != sizeof(*info))
61688364387SHung-ying Tyan 		return -1;
61788364387SHung-ying Tyan 
61888364387SHung-ying Tyan 	return 0;
61988364387SHung-ying Tyan }
62088364387SHung-ying Tyan 
cros_ec_get_event_mask(struct udevice * dev,uint type,uint32_t * mask)62172ef8bfdSSimon Glass int cros_ec_get_event_mask(struct udevice *dev, uint type, uint32_t *mask)
62272ef8bfdSSimon Glass {
62372ef8bfdSSimon Glass 	struct ec_response_host_event_mask rsp;
62472ef8bfdSSimon Glass 	int ret;
62572ef8bfdSSimon Glass 
62672ef8bfdSSimon Glass 	ret = ec_command(dev, type, 0, NULL, 0, &rsp, sizeof(rsp));
62772ef8bfdSSimon Glass 	if (ret < 0)
62872ef8bfdSSimon Glass 		return ret;
62972ef8bfdSSimon Glass 	else if (ret != sizeof(rsp))
63072ef8bfdSSimon Glass 		return -EINVAL;
63172ef8bfdSSimon Glass 
63272ef8bfdSSimon Glass 	*mask = rsp.mask;
63372ef8bfdSSimon Glass 
63472ef8bfdSSimon Glass 	return 0;
63572ef8bfdSSimon Glass }
63672ef8bfdSSimon Glass 
cros_ec_set_event_mask(struct udevice * dev,uint type,uint32_t mask)63772ef8bfdSSimon Glass int cros_ec_set_event_mask(struct udevice *dev, uint type, uint32_t mask)
63872ef8bfdSSimon Glass {
63972ef8bfdSSimon Glass 	struct ec_params_host_event_mask req;
64072ef8bfdSSimon Glass 	int ret;
64172ef8bfdSSimon Glass 
64272ef8bfdSSimon Glass 	req.mask = mask;
64372ef8bfdSSimon Glass 
64472ef8bfdSSimon Glass 	ret = ec_command(dev, type, 0, &req, sizeof(req), NULL, 0);
64572ef8bfdSSimon Glass 	if (ret < 0)
64672ef8bfdSSimon Glass 		return ret;
64772ef8bfdSSimon Glass 
64872ef8bfdSSimon Glass 	return 0;
64972ef8bfdSSimon Glass }
65072ef8bfdSSimon Glass 
cros_ec_get_host_events(struct udevice * dev,uint32_t * events_ptr)6516322a7b6SSimon Glass int cros_ec_get_host_events(struct udevice *dev, uint32_t *events_ptr)
65288364387SHung-ying Tyan {
65388364387SHung-ying Tyan 	struct ec_response_host_event_mask *resp;
65488364387SHung-ying Tyan 
65588364387SHung-ying Tyan 	/*
65688364387SHung-ying Tyan 	 * Use the B copy of the event flags, because the main copy is already
65788364387SHung-ying Tyan 	 * used by ACPI/SMI.
65888364387SHung-ying Tyan 	 */
65988364387SHung-ying Tyan 	if (ec_command_inptr(dev, EC_CMD_HOST_EVENT_GET_B, 0, NULL, 0,
6602ab83f0dSSimon Glass 		       (uint8_t **)&resp, sizeof(*resp)) < (int)sizeof(*resp))
66188364387SHung-ying Tyan 		return -1;
66288364387SHung-ying Tyan 
66388364387SHung-ying Tyan 	if (resp->mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_INVALID))
66488364387SHung-ying Tyan 		return -1;
66588364387SHung-ying Tyan 
66688364387SHung-ying Tyan 	*events_ptr = resp->mask;
66788364387SHung-ying Tyan 	return 0;
66888364387SHung-ying Tyan }
66988364387SHung-ying Tyan 
cros_ec_clear_host_events(struct udevice * dev,uint32_t events)6706322a7b6SSimon Glass int cros_ec_clear_host_events(struct udevice *dev, uint32_t events)
67188364387SHung-ying Tyan {
67288364387SHung-ying Tyan 	struct ec_params_host_event_mask params;
67388364387SHung-ying Tyan 
67488364387SHung-ying Tyan 	params.mask = events;
67588364387SHung-ying Tyan 
67688364387SHung-ying Tyan 	/*
67788364387SHung-ying Tyan 	 * Use the B copy of the event flags, so it affects the data returned
67888364387SHung-ying Tyan 	 * by cros_ec_get_host_events().
67988364387SHung-ying Tyan 	 */
68088364387SHung-ying Tyan 	if (ec_command_inptr(dev, EC_CMD_HOST_EVENT_CLEAR_B, 0,
68188364387SHung-ying Tyan 		       &params, sizeof(params), NULL, 0) < 0)
68288364387SHung-ying Tyan 		return -1;
68388364387SHung-ying Tyan 
68488364387SHung-ying Tyan 	return 0;
68588364387SHung-ying Tyan }
68688364387SHung-ying Tyan 
cros_ec_flash_protect(struct udevice * dev,uint32_t set_mask,uint32_t set_flags,struct ec_response_flash_protect * resp)6876322a7b6SSimon Glass int cros_ec_flash_protect(struct udevice *dev, uint32_t set_mask,
6886322a7b6SSimon Glass 			  uint32_t set_flags,
68988364387SHung-ying Tyan 			  struct ec_response_flash_protect *resp)
69088364387SHung-ying Tyan {
69188364387SHung-ying Tyan 	struct ec_params_flash_protect params;
69288364387SHung-ying Tyan 
69388364387SHung-ying Tyan 	params.mask = set_mask;
69488364387SHung-ying Tyan 	params.flags = set_flags;
69588364387SHung-ying Tyan 
69688364387SHung-ying Tyan 	if (ec_command(dev, EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT,
69788364387SHung-ying Tyan 		       &params, sizeof(params),
6982ab83f0dSSimon Glass 		       resp, sizeof(*resp)) != sizeof(*resp))
69988364387SHung-ying Tyan 		return -1;
70088364387SHung-ying Tyan 
70188364387SHung-ying Tyan 	return 0;
70288364387SHung-ying Tyan }
70388364387SHung-ying Tyan 
cros_ec_entering_mode(struct udevice * dev,int mode)70472ef8bfdSSimon Glass int cros_ec_entering_mode(struct udevice *dev, int mode)
70572ef8bfdSSimon Glass {
70672ef8bfdSSimon Glass 	int rc;
70772ef8bfdSSimon Glass 
70872ef8bfdSSimon Glass 	rc = ec_command(dev, EC_CMD_ENTERING_MODE, 0, &mode, sizeof(mode),
70972ef8bfdSSimon Glass 			NULL, 0);
71072ef8bfdSSimon Glass 	if (rc)
71172ef8bfdSSimon Glass 		return -1;
71272ef8bfdSSimon Glass 	return 0;
71372ef8bfdSSimon Glass }
71472ef8bfdSSimon Glass 
cros_ec_check_version(struct udevice * dev)7156322a7b6SSimon Glass static int cros_ec_check_version(struct udevice *dev)
71688364387SHung-ying Tyan {
7176322a7b6SSimon Glass 	struct cros_ec_dev *cdev = dev_get_uclass_priv(dev);
71888364387SHung-ying Tyan 	struct ec_params_hello req;
71988364387SHung-ying Tyan 	struct ec_response_hello *resp;
72088364387SHung-ying Tyan 
72172a38e06SSimon Glass 	struct dm_cros_ec_ops *ops;
72272a38e06SSimon Glass 	int ret;
72372a38e06SSimon Glass 
7246322a7b6SSimon Glass 	ops = dm_cros_ec_get_ops(dev);
72572a38e06SSimon Glass 	if (ops->check_version) {
7266322a7b6SSimon Glass 		ret = ops->check_version(dev);
72772a38e06SSimon Glass 		if (ret)
72872a38e06SSimon Glass 			return ret;
72972a38e06SSimon Glass 	}
73088364387SHung-ying Tyan 
73188364387SHung-ying Tyan 	/*
73288364387SHung-ying Tyan 	 * TODO(sjg@chromium.org).
73388364387SHung-ying Tyan 	 * There is a strange oddity here with the EC. We could just ignore
73488364387SHung-ying Tyan 	 * the response, i.e. pass the last two parameters as NULL and 0.
73588364387SHung-ying Tyan 	 * In this case we won't read back very many bytes from the EC.
73688364387SHung-ying Tyan 	 * On the I2C bus the EC gets upset about this and will try to send
73788364387SHung-ying Tyan 	 * the bytes anyway. This means that we will have to wait for that
73888364387SHung-ying Tyan 	 * to complete before continuing with a new EC command.
73988364387SHung-ying Tyan 	 *
74088364387SHung-ying Tyan 	 * This problem is probably unique to the I2C bus.
74188364387SHung-ying Tyan 	 *
74288364387SHung-ying Tyan 	 * So for now, just read all the data anyway.
74388364387SHung-ying Tyan 	 */
744e8c12662SRandall Spangler 
745a6070283SRandall Spangler 	/* Try sending a version 3 packet */
7466322a7b6SSimon Glass 	cdev->protocol_version = 3;
747d11e8fd8SSimon Glass 	req.in_data = 0;
748a6070283SRandall Spangler 	if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req),
7499fea76f5SSimon Glass 			     (uint8_t **)&resp, sizeof(*resp)) > 0)
750a6070283SRandall Spangler 		return 0;
751a6070283SRandall Spangler 
752e8c12662SRandall Spangler 	/* Try sending a version 2 packet */
7536322a7b6SSimon Glass 	cdev->protocol_version = 2;
75488364387SHung-ying Tyan 	if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req),
7559fea76f5SSimon Glass 			     (uint8_t **)&resp, sizeof(*resp)) > 0)
756e8c12662SRandall Spangler 		return 0;
75788364387SHung-ying Tyan 
758e8c12662SRandall Spangler 	/*
759e8c12662SRandall Spangler 	 * Fail if we're still here, since the EC doesn't understand any
760e8c12662SRandall Spangler 	 * protcol version we speak.  Version 1 interface without command
761e8c12662SRandall Spangler 	 * version is no longer supported, and we don't know about any new
762e8c12662SRandall Spangler 	 * protocol versions.
763e8c12662SRandall Spangler 	 */
7646322a7b6SSimon Glass 	cdev->protocol_version = 0;
765e8c12662SRandall Spangler 	printf("%s: ERROR: old EC interface not supported\n", __func__);
766e8c12662SRandall Spangler 	return -1;
76788364387SHung-ying Tyan }
76888364387SHung-ying Tyan 
cros_ec_test(struct udevice * dev)7696322a7b6SSimon Glass int cros_ec_test(struct udevice *dev)
77088364387SHung-ying Tyan {
77188364387SHung-ying Tyan 	struct ec_params_hello req;
77288364387SHung-ying Tyan 	struct ec_response_hello *resp;
77388364387SHung-ying Tyan 
77488364387SHung-ying Tyan 	req.in_data = 0x12345678;
77588364387SHung-ying Tyan 	if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req),
77688364387SHung-ying Tyan 		       (uint8_t **)&resp, sizeof(*resp)) < sizeof(*resp)) {
77788364387SHung-ying Tyan 		printf("ec_command_inptr() returned error\n");
77888364387SHung-ying Tyan 		return -1;
77988364387SHung-ying Tyan 	}
78088364387SHung-ying Tyan 	if (resp->out_data != req.in_data + 0x01020304) {
78188364387SHung-ying Tyan 		printf("Received invalid handshake %x\n", resp->out_data);
78288364387SHung-ying Tyan 		return -1;
78388364387SHung-ying Tyan 	}
78488364387SHung-ying Tyan 
78588364387SHung-ying Tyan 	return 0;
78688364387SHung-ying Tyan }
78788364387SHung-ying Tyan 
cros_ec_flash_offset(struct udevice * dev,enum ec_flash_region region,uint32_t * offset,uint32_t * size)7886322a7b6SSimon Glass int cros_ec_flash_offset(struct udevice *dev, enum ec_flash_region region,
78988364387SHung-ying Tyan 		      uint32_t *offset, uint32_t *size)
79088364387SHung-ying Tyan {
79188364387SHung-ying Tyan 	struct ec_params_flash_region_info p;
79288364387SHung-ying Tyan 	struct ec_response_flash_region_info *r;
79388364387SHung-ying Tyan 	int ret;
79488364387SHung-ying Tyan 
79588364387SHung-ying Tyan 	p.region = region;
79688364387SHung-ying Tyan 	ret = ec_command_inptr(dev, EC_CMD_FLASH_REGION_INFO,
79788364387SHung-ying Tyan 			 EC_VER_FLASH_REGION_INFO,
79888364387SHung-ying Tyan 			 &p, sizeof(p), (uint8_t **)&r, sizeof(*r));
79988364387SHung-ying Tyan 	if (ret != sizeof(*r))
80088364387SHung-ying Tyan 		return -1;
80188364387SHung-ying Tyan 
80288364387SHung-ying Tyan 	if (offset)
80388364387SHung-ying Tyan 		*offset = r->offset;
80488364387SHung-ying Tyan 	if (size)
80588364387SHung-ying Tyan 		*size = r->size;
80688364387SHung-ying Tyan 
80788364387SHung-ying Tyan 	return 0;
80888364387SHung-ying Tyan }
80988364387SHung-ying Tyan 
cros_ec_flash_erase(struct udevice * dev,uint32_t offset,uint32_t size)8106322a7b6SSimon Glass int cros_ec_flash_erase(struct udevice *dev, uint32_t offset, uint32_t size)
81188364387SHung-ying Tyan {
81288364387SHung-ying Tyan 	struct ec_params_flash_erase p;
81388364387SHung-ying Tyan 
81488364387SHung-ying Tyan 	p.offset = offset;
81588364387SHung-ying Tyan 	p.size = size;
81688364387SHung-ying Tyan 	return ec_command_inptr(dev, EC_CMD_FLASH_ERASE, 0, &p, sizeof(p),
81788364387SHung-ying Tyan 			NULL, 0);
81888364387SHung-ying Tyan }
81988364387SHung-ying Tyan 
82088364387SHung-ying Tyan /**
82188364387SHung-ying Tyan  * Write a single block to the flash
82288364387SHung-ying Tyan  *
82388364387SHung-ying Tyan  * Write a block of data to the EC flash. The size must not exceed the flash
82488364387SHung-ying Tyan  * write block size which you can obtain from cros_ec_flash_write_burst_size().
82588364387SHung-ying Tyan  *
82688364387SHung-ying Tyan  * The offset starts at 0. You can obtain the region information from
82788364387SHung-ying Tyan  * cros_ec_flash_offset() to find out where to write for a particular region.
82888364387SHung-ying Tyan  *
82988364387SHung-ying Tyan  * Attempting to write to the region where the EC is currently running from
83088364387SHung-ying Tyan  * will result in an error.
83188364387SHung-ying Tyan  *
83288364387SHung-ying Tyan  * @param dev		CROS-EC device
83388364387SHung-ying Tyan  * @param data		Pointer to data buffer to write
83488364387SHung-ying Tyan  * @param offset	Offset within flash to write to.
83588364387SHung-ying Tyan  * @param size		Number of bytes to write
83688364387SHung-ying Tyan  * @return 0 if ok, -1 on error
83788364387SHung-ying Tyan  */
cros_ec_flash_write_block(struct udevice * dev,const uint8_t * data,uint32_t offset,uint32_t size)8386322a7b6SSimon Glass static int cros_ec_flash_write_block(struct udevice *dev, const uint8_t *data,
8396322a7b6SSimon Glass 				     uint32_t offset, uint32_t size)
84088364387SHung-ying Tyan {
841bae5b97eSMoritz Fischer 	struct ec_params_flash_write *p;
842bae5b97eSMoritz Fischer 	int ret;
84388364387SHung-ying Tyan 
844bae5b97eSMoritz Fischer 	p = malloc(sizeof(*p) + size);
845bae5b97eSMoritz Fischer 	if (!p)
846bae5b97eSMoritz Fischer 		return -ENOMEM;
84788364387SHung-ying Tyan 
848bae5b97eSMoritz Fischer 	p->offset = offset;
849bae5b97eSMoritz Fischer 	p->size = size;
850bae5b97eSMoritz Fischer 	assert(data && p->size <= EC_FLASH_WRITE_VER0_SIZE);
851bae5b97eSMoritz Fischer 	memcpy(p + 1, data, p->size);
852bae5b97eSMoritz Fischer 
853bae5b97eSMoritz Fischer 	ret = ec_command_inptr(dev, EC_CMD_FLASH_WRITE, 0,
854bae5b97eSMoritz Fischer 			  p, sizeof(*p) + size, NULL, 0) >= 0 ? 0 : -1;
855bae5b97eSMoritz Fischer 
856bae5b97eSMoritz Fischer 	free(p);
857bae5b97eSMoritz Fischer 
858bae5b97eSMoritz Fischer 	return ret;
85988364387SHung-ying Tyan }
86088364387SHung-ying Tyan 
86188364387SHung-ying Tyan /**
86288364387SHung-ying Tyan  * Return optimal flash write burst size
86388364387SHung-ying Tyan  */
cros_ec_flash_write_burst_size(struct udevice * dev)8646322a7b6SSimon Glass static int cros_ec_flash_write_burst_size(struct udevice *dev)
86588364387SHung-ying Tyan {
866836bb6e8SSimon Glass 	return EC_FLASH_WRITE_VER0_SIZE;
86788364387SHung-ying Tyan }
86888364387SHung-ying Tyan 
86988364387SHung-ying Tyan /**
87088364387SHung-ying Tyan  * Check if a block of data is erased (all 0xff)
87188364387SHung-ying Tyan  *
87288364387SHung-ying Tyan  * This function is useful when dealing with flash, for checking whether a
87388364387SHung-ying Tyan  * data block is erased and thus does not need to be programmed.
87488364387SHung-ying Tyan  *
87588364387SHung-ying Tyan  * @param data		Pointer to data to check (must be word-aligned)
87688364387SHung-ying Tyan  * @param size		Number of bytes to check (must be word-aligned)
87788364387SHung-ying Tyan  * @return 0 if erased, non-zero if any word is not erased
87888364387SHung-ying Tyan  */
cros_ec_data_is_erased(const uint32_t * data,int size)87988364387SHung-ying Tyan static int cros_ec_data_is_erased(const uint32_t *data, int size)
88088364387SHung-ying Tyan {
88188364387SHung-ying Tyan 	assert(!(size & 3));
88288364387SHung-ying Tyan 	size /= sizeof(uint32_t);
88388364387SHung-ying Tyan 	for (; size > 0; size -= 4, data++)
88488364387SHung-ying Tyan 		if (*data != -1U)
88588364387SHung-ying Tyan 			return 0;
88688364387SHung-ying Tyan 
88788364387SHung-ying Tyan 	return 1;
88888364387SHung-ying Tyan }
88988364387SHung-ying Tyan 
890281ca88fSMoritz Fischer /**
891281ca88fSMoritz Fischer  * Read back flash parameters
892281ca88fSMoritz Fischer  *
893281ca88fSMoritz Fischer  * This function reads back parameters of the flash as reported by the EC
894281ca88fSMoritz Fischer  *
895281ca88fSMoritz Fischer  * @param dev  Pointer to device
896281ca88fSMoritz Fischer  * @param info Pointer to output flash info struct
897281ca88fSMoritz Fischer  */
cros_ec_read_flashinfo(struct udevice * dev,struct ec_response_flash_info * info)8986322a7b6SSimon Glass int cros_ec_read_flashinfo(struct udevice *dev,
899281ca88fSMoritz Fischer 			   struct ec_response_flash_info *info)
900281ca88fSMoritz Fischer {
901281ca88fSMoritz Fischer 	int ret;
902281ca88fSMoritz Fischer 
903281ca88fSMoritz Fischer 	ret = ec_command(dev, EC_CMD_FLASH_INFO, 0,
904281ca88fSMoritz Fischer 			 NULL, 0, info, sizeof(*info));
905281ca88fSMoritz Fischer 	if (ret < 0)
906281ca88fSMoritz Fischer 		return ret;
907281ca88fSMoritz Fischer 
908281ca88fSMoritz Fischer 	return ret < sizeof(*info) ? -1 : 0;
909281ca88fSMoritz Fischer }
910281ca88fSMoritz Fischer 
cros_ec_flash_write(struct udevice * dev,const uint8_t * data,uint32_t offset,uint32_t size)9116322a7b6SSimon Glass int cros_ec_flash_write(struct udevice *dev, const uint8_t *data,
91288364387SHung-ying Tyan 			uint32_t offset, uint32_t size)
91388364387SHung-ying Tyan {
9146322a7b6SSimon Glass 	struct cros_ec_dev *cdev = dev_get_uclass_priv(dev);
91588364387SHung-ying Tyan 	uint32_t burst = cros_ec_flash_write_burst_size(dev);
91688364387SHung-ying Tyan 	uint32_t end, off;
91788364387SHung-ying Tyan 	int ret;
91888364387SHung-ying Tyan 
919dc05ac0fSSimon Glass 	if (!burst)
920dc05ac0fSSimon Glass 		return -EINVAL;
921dc05ac0fSSimon Glass 
92288364387SHung-ying Tyan 	/*
92388364387SHung-ying Tyan 	 * TODO: round up to the nearest multiple of write size.  Can get away
92488364387SHung-ying Tyan 	 * without that on link right now because its write size is 4 bytes.
92588364387SHung-ying Tyan 	 */
92688364387SHung-ying Tyan 	end = offset + size;
92788364387SHung-ying Tyan 	for (off = offset; off < end; off += burst, data += burst) {
92888364387SHung-ying Tyan 		uint32_t todo;
92988364387SHung-ying Tyan 
93088364387SHung-ying Tyan 		/* If the data is empty, there is no point in programming it */
93188364387SHung-ying Tyan 		todo = min(end - off, burst);
9326322a7b6SSimon Glass 		if (cdev->optimise_flash_write &&
93388364387SHung-ying Tyan 		    cros_ec_data_is_erased((uint32_t *)data, todo))
93488364387SHung-ying Tyan 			continue;
93588364387SHung-ying Tyan 
93688364387SHung-ying Tyan 		ret = cros_ec_flash_write_block(dev, data, off, todo);
93788364387SHung-ying Tyan 		if (ret)
93888364387SHung-ying Tyan 			return ret;
93988364387SHung-ying Tyan 	}
94088364387SHung-ying Tyan 
94188364387SHung-ying Tyan 	return 0;
94288364387SHung-ying Tyan }
94388364387SHung-ying Tyan 
94488364387SHung-ying Tyan /**
94572ef8bfdSSimon Glass  * Run verification on a slot
94672ef8bfdSSimon Glass  *
94772ef8bfdSSimon Glass  * @param me     CrosEc instance
94872ef8bfdSSimon Glass  * @param region Region to run verification on
94972ef8bfdSSimon Glass  * @return 0 if success or not applicable. Non-zero if verification failed.
95072ef8bfdSSimon Glass  */
cros_ec_efs_verify(struct udevice * dev,enum ec_flash_region region)95172ef8bfdSSimon Glass int cros_ec_efs_verify(struct udevice *dev, enum ec_flash_region region)
95272ef8bfdSSimon Glass {
95372ef8bfdSSimon Glass 	struct ec_params_efs_verify p;
95472ef8bfdSSimon Glass 	int rv;
95572ef8bfdSSimon Glass 
95672ef8bfdSSimon Glass 	log_info("EFS: EC is verifying updated image...\n");
95772ef8bfdSSimon Glass 	p.region = region;
95872ef8bfdSSimon Glass 
95972ef8bfdSSimon Glass 	rv = ec_command(dev, EC_CMD_EFS_VERIFY, 0, &p, sizeof(p), NULL, 0);
96072ef8bfdSSimon Glass 	if (rv >= 0) {
96172ef8bfdSSimon Glass 		log_info("EFS: Verification success\n");
96272ef8bfdSSimon Glass 		return 0;
96372ef8bfdSSimon Glass 	}
96472ef8bfdSSimon Glass 	if (rv == -EC_RES_INVALID_COMMAND) {
96572ef8bfdSSimon Glass 		log_info("EFS: EC doesn't support EFS_VERIFY command\n");
96672ef8bfdSSimon Glass 		return 0;
96772ef8bfdSSimon Glass 	}
96872ef8bfdSSimon Glass 	log_info("EFS: Verification failed\n");
96972ef8bfdSSimon Glass 
97072ef8bfdSSimon Glass 	return rv;
97172ef8bfdSSimon Glass }
97272ef8bfdSSimon Glass 
97372ef8bfdSSimon Glass /**
97488364387SHung-ying Tyan  * Read a single block from the flash
97588364387SHung-ying Tyan  *
97688364387SHung-ying Tyan  * Read a block of data from the EC flash. The size must not exceed the flash
97788364387SHung-ying Tyan  * write block size which you can obtain from cros_ec_flash_write_burst_size().
97888364387SHung-ying Tyan  *
97988364387SHung-ying Tyan  * The offset starts at 0. You can obtain the region information from
98088364387SHung-ying Tyan  * cros_ec_flash_offset() to find out where to read for a particular region.
98188364387SHung-ying Tyan  *
98288364387SHung-ying Tyan  * @param dev		CROS-EC device
98388364387SHung-ying Tyan  * @param data		Pointer to data buffer to read into
98488364387SHung-ying Tyan  * @param offset	Offset within flash to read from
98588364387SHung-ying Tyan  * @param size		Number of bytes to read
98688364387SHung-ying Tyan  * @return 0 if ok, -1 on error
98788364387SHung-ying Tyan  */
cros_ec_flash_read_block(struct udevice * dev,uint8_t * data,uint32_t offset,uint32_t size)9886322a7b6SSimon Glass static int cros_ec_flash_read_block(struct udevice *dev, uint8_t *data,
98988364387SHung-ying Tyan 				    uint32_t offset, uint32_t size)
99088364387SHung-ying Tyan {
99188364387SHung-ying Tyan 	struct ec_params_flash_read p;
99288364387SHung-ying Tyan 
99388364387SHung-ying Tyan 	p.offset = offset;
99488364387SHung-ying Tyan 	p.size = size;
99588364387SHung-ying Tyan 
99688364387SHung-ying Tyan 	return ec_command(dev, EC_CMD_FLASH_READ, 0,
99788364387SHung-ying Tyan 			  &p, sizeof(p), data, size) >= 0 ? 0 : -1;
99888364387SHung-ying Tyan }
99988364387SHung-ying Tyan 
cros_ec_flash_read(struct udevice * dev,uint8_t * data,uint32_t offset,uint32_t size)10006322a7b6SSimon Glass int cros_ec_flash_read(struct udevice *dev, uint8_t *data, uint32_t offset,
100188364387SHung-ying Tyan 		       uint32_t size)
100288364387SHung-ying Tyan {
100388364387SHung-ying Tyan 	uint32_t burst = cros_ec_flash_write_burst_size(dev);
100488364387SHung-ying Tyan 	uint32_t end, off;
100588364387SHung-ying Tyan 	int ret;
100688364387SHung-ying Tyan 
100788364387SHung-ying Tyan 	end = offset + size;
100888364387SHung-ying Tyan 	for (off = offset; off < end; off += burst, data += burst) {
100988364387SHung-ying Tyan 		ret = cros_ec_flash_read_block(dev, data, off,
101088364387SHung-ying Tyan 					    min(end - off, burst));
101188364387SHung-ying Tyan 		if (ret)
101288364387SHung-ying Tyan 			return ret;
101388364387SHung-ying Tyan 	}
101488364387SHung-ying Tyan 
101588364387SHung-ying Tyan 	return 0;
101688364387SHung-ying Tyan }
101788364387SHung-ying Tyan 
cros_ec_flash_update_rw(struct udevice * dev,const uint8_t * image,int image_size)10186322a7b6SSimon Glass int cros_ec_flash_update_rw(struct udevice *dev, const uint8_t *image,
1019e6c5c94aSSimon Glass 			    int image_size)
102088364387SHung-ying Tyan {
102188364387SHung-ying Tyan 	uint32_t rw_offset, rw_size;
102288364387SHung-ying Tyan 	int ret;
102388364387SHung-ying Tyan 
10246f1c0430SSimon Glass 	if (cros_ec_flash_offset(dev, EC_FLASH_REGION_ACTIVE, &rw_offset,
10256f1c0430SSimon Glass 		&rw_size))
102688364387SHung-ying Tyan 		return -1;
10272ab83f0dSSimon Glass 	if (image_size > (int)rw_size)
102888364387SHung-ying Tyan 		return -1;
102988364387SHung-ying Tyan 
103088364387SHung-ying Tyan 	/* Invalidate the existing hash, just in case the AP reboots
103188364387SHung-ying Tyan 	 * unexpectedly during the update. If that happened, the EC RW firmware
103288364387SHung-ying Tyan 	 * would be invalid, but the EC would still have the original hash.
103388364387SHung-ying Tyan 	 */
103488364387SHung-ying Tyan 	ret = cros_ec_invalidate_hash(dev);
103588364387SHung-ying Tyan 	if (ret)
103688364387SHung-ying Tyan 		return ret;
103788364387SHung-ying Tyan 
103888364387SHung-ying Tyan 	/*
103988364387SHung-ying Tyan 	 * Erase the entire RW section, so that the EC doesn't see any garbage
104088364387SHung-ying Tyan 	 * past the new image if it's smaller than the current image.
104188364387SHung-ying Tyan 	 *
104288364387SHung-ying Tyan 	 * TODO: could optimize this to erase just the current image, since
104388364387SHung-ying Tyan 	 * presumably everything past that is 0xff's.  But would still need to
104488364387SHung-ying Tyan 	 * round up to the nearest multiple of erase size.
104588364387SHung-ying Tyan 	 */
104688364387SHung-ying Tyan 	ret = cros_ec_flash_erase(dev, rw_offset, rw_size);
104788364387SHung-ying Tyan 	if (ret)
104888364387SHung-ying Tyan 		return ret;
104988364387SHung-ying Tyan 
105088364387SHung-ying Tyan 	/* Write the image */
105188364387SHung-ying Tyan 	ret = cros_ec_flash_write(dev, image, rw_offset, image_size);
105288364387SHung-ying Tyan 	if (ret)
105388364387SHung-ying Tyan 		return ret;
105488364387SHung-ying Tyan 
105588364387SHung-ying Tyan 	return 0;
105688364387SHung-ying Tyan }
105788364387SHung-ying Tyan 
cros_ec_read_nvdata(struct udevice * dev,uint8_t * block,int size)10586322a7b6SSimon Glass int cros_ec_read_nvdata(struct udevice *dev, uint8_t *block, int size)
105988364387SHung-ying Tyan {
106088364387SHung-ying Tyan 	struct ec_params_vbnvcontext p;
106188364387SHung-ying Tyan 	int len;
106288364387SHung-ying Tyan 
106372ef8bfdSSimon Glass 	if (size != EC_VBNV_BLOCK_SIZE && size != EC_VBNV_BLOCK_SIZE_V2)
10646322a7b6SSimon Glass 		return -EINVAL;
10656322a7b6SSimon Glass 
106688364387SHung-ying Tyan 	p.op = EC_VBNV_CONTEXT_OP_READ;
106788364387SHung-ying Tyan 
106888364387SHung-ying Tyan 	len = ec_command(dev, EC_CMD_VBNV_CONTEXT, EC_VER_VBNV_CONTEXT,
106972ef8bfdSSimon Glass 			 &p, sizeof(uint32_t) + size, block, size);
107072ef8bfdSSimon Glass 	if (len != size) {
107172ef8bfdSSimon Glass 		log_err("Expected %d bytes, got %d\n", size, len);
10726322a7b6SSimon Glass 		return -EIO;
107372ef8bfdSSimon Glass 	}
107488364387SHung-ying Tyan 
107588364387SHung-ying Tyan 	return 0;
107688364387SHung-ying Tyan }
107788364387SHung-ying Tyan 
cros_ec_write_nvdata(struct udevice * dev,const uint8_t * block,int size)10786322a7b6SSimon Glass int cros_ec_write_nvdata(struct udevice *dev, const uint8_t *block, int size)
107988364387SHung-ying Tyan {
108088364387SHung-ying Tyan 	struct ec_params_vbnvcontext p;
108188364387SHung-ying Tyan 	int len;
108288364387SHung-ying Tyan 
108372ef8bfdSSimon Glass 	if (size != EC_VBNV_BLOCK_SIZE && size != EC_VBNV_BLOCK_SIZE_V2)
10846322a7b6SSimon Glass 		return -EINVAL;
108588364387SHung-ying Tyan 	p.op = EC_VBNV_CONTEXT_OP_WRITE;
108672ef8bfdSSimon Glass 	memcpy(p.block, block, size);
108788364387SHung-ying Tyan 
108888364387SHung-ying Tyan 	len = ec_command_inptr(dev, EC_CMD_VBNV_CONTEXT, EC_VER_VBNV_CONTEXT,
108972ef8bfdSSimon Glass 			&p, sizeof(uint32_t) + size, NULL, 0);
109088364387SHung-ying Tyan 	if (len < 0)
109188364387SHung-ying Tyan 		return -1;
109288364387SHung-ying Tyan 
109388364387SHung-ying Tyan 	return 0;
109488364387SHung-ying Tyan }
109588364387SHung-ying Tyan 
cros_ec_battery_cutoff(struct udevice * dev,uint8_t flags)109672ef8bfdSSimon Glass int cros_ec_battery_cutoff(struct udevice *dev, uint8_t flags)
109772ef8bfdSSimon Glass {
109872ef8bfdSSimon Glass 	struct ec_params_battery_cutoff p;
109972ef8bfdSSimon Glass 	int len;
110072ef8bfdSSimon Glass 
110172ef8bfdSSimon Glass 	p.flags = flags;
110272ef8bfdSSimon Glass 	len = ec_command(dev, EC_CMD_BATTERY_CUT_OFF, 1, &p, sizeof(p),
110372ef8bfdSSimon Glass 			 NULL, 0);
110472ef8bfdSSimon Glass 
110572ef8bfdSSimon Glass 	if (len < 0)
110672ef8bfdSSimon Glass 		return -1;
110772ef8bfdSSimon Glass 	return 0;
110872ef8bfdSSimon Glass }
110972ef8bfdSSimon Glass 
cros_ec_set_ldo(struct udevice * dev,uint8_t index,uint8_t state)1110f48eaf01SSimon Glass int cros_ec_set_ldo(struct udevice *dev, uint8_t index, uint8_t state)
111188364387SHung-ying Tyan {
111288364387SHung-ying Tyan 	struct ec_params_ldo_set params;
111388364387SHung-ying Tyan 
111488364387SHung-ying Tyan 	params.index = index;
111588364387SHung-ying Tyan 	params.state = state;
111688364387SHung-ying Tyan 
11176322a7b6SSimon Glass 	if (ec_command_inptr(dev, EC_CMD_LDO_SET, 0, &params, sizeof(params),
111888364387SHung-ying Tyan 			     NULL, 0))
111988364387SHung-ying Tyan 		return -1;
112088364387SHung-ying Tyan 
112188364387SHung-ying Tyan 	return 0;
112288364387SHung-ying Tyan }
112388364387SHung-ying Tyan 
cros_ec_get_ldo(struct udevice * dev,uint8_t index,uint8_t * state)1124f48eaf01SSimon Glass int cros_ec_get_ldo(struct udevice *dev, uint8_t index, uint8_t *state)
112588364387SHung-ying Tyan {
112688364387SHung-ying Tyan 	struct ec_params_ldo_get params;
112788364387SHung-ying Tyan 	struct ec_response_ldo_get *resp;
112888364387SHung-ying Tyan 
112988364387SHung-ying Tyan 	params.index = index;
113088364387SHung-ying Tyan 
11316322a7b6SSimon Glass 	if (ec_command_inptr(dev, EC_CMD_LDO_GET, 0, &params, sizeof(params),
1132f48eaf01SSimon Glass 			     (uint8_t **)&resp, sizeof(*resp)) !=
1133f48eaf01SSimon Glass 			     sizeof(*resp))
113488364387SHung-ying Tyan 		return -1;
113588364387SHung-ying Tyan 
113688364387SHung-ying Tyan 	*state = resp->state;
113788364387SHung-ying Tyan 
113888364387SHung-ying Tyan 	return 0;
113988364387SHung-ying Tyan }
114088364387SHung-ying Tyan 
cros_ec_register(struct udevice * dev)114184d6cbd3SSimon Glass int cros_ec_register(struct udevice *dev)
114284d6cbd3SSimon Glass {
1143e564f054SSimon Glass 	struct cros_ec_dev *cdev = dev_get_uclass_priv(dev);
114484d6cbd3SSimon Glass 	char id[MSG_BYTES];
114584d6cbd3SSimon Glass 
114684d6cbd3SSimon Glass 	cdev->dev = dev;
114732f8a19fSSimon Glass 	gpio_request_by_name(dev, "ec-interrupt", 0, &cdev->ec_int,
114832f8a19fSSimon Glass 			     GPIOD_IS_IN);
11492ec9d171SSimon Glass 	cdev->optimise_flash_write = dev_read_bool(dev, "optimise-flash-write");
115084d6cbd3SSimon Glass 
11516322a7b6SSimon Glass 	if (cros_ec_check_version(dev)) {
115284d6cbd3SSimon Glass 		debug("%s: Could not detect CROS-EC version\n", __func__);
115384d6cbd3SSimon Glass 		return -CROS_EC_ERR_CHECK_VERSION;
115484d6cbd3SSimon Glass 	}
115584d6cbd3SSimon Glass 
11566322a7b6SSimon Glass 	if (cros_ec_read_id(dev, id, sizeof(id))) {
115784d6cbd3SSimon Glass 		debug("%s: Could not read KBC ID\n", __func__);
115884d6cbd3SSimon Glass 		return -CROS_EC_ERR_READ_ID;
115984d6cbd3SSimon Glass 	}
116084d6cbd3SSimon Glass 
116184d6cbd3SSimon Glass 	/* Remember this device for use by the cros_ec command */
1162c4b206dfSSimon Glass 	debug("Google Chrome EC v%d CROS-EC driver ready, id '%s'\n",
1163c4b206dfSSimon Glass 	      cdev->protocol_version, id);
116484d6cbd3SSimon Glass 
116584d6cbd3SSimon Glass 	return 0;
116684d6cbd3SSimon Glass }
116788364387SHung-ying Tyan 
cros_ec_decode_ec_flash(struct udevice * dev,struct fdt_cros_ec * config)11682ec9d171SSimon Glass int cros_ec_decode_ec_flash(struct udevice *dev, struct fdt_cros_ec *config)
1169d7f25f35SSimon Glass {
11702ec9d171SSimon Glass 	ofnode flash_node, node;
1171d7f25f35SSimon Glass 
11722ec9d171SSimon Glass 	flash_node = dev_read_subnode(dev, "flash");
11732ec9d171SSimon Glass 	if (!ofnode_valid(flash_node)) {
1174d7f25f35SSimon Glass 		debug("Failed to find flash node\n");
1175d7f25f35SSimon Glass 		return -1;
1176d7f25f35SSimon Glass 	}
1177d7f25f35SSimon Glass 
11785e0a7341SSimon Glass 	if (ofnode_read_fmap_entry(flash_node,  &config->flash)) {
11792ec9d171SSimon Glass 		debug("Failed to decode flash node in chrome-ec\n");
1180d7f25f35SSimon Glass 		return -1;
1181d7f25f35SSimon Glass 	}
1182d7f25f35SSimon Glass 
11832ec9d171SSimon Glass 	config->flash_erase_value = ofnode_read_s32_default(flash_node,
1184d7f25f35SSimon Glass 							    "erase-value", -1);
11853991f42eSSimon Glass 	ofnode_for_each_subnode(node, flash_node) {
11862ec9d171SSimon Glass 		const char *name = ofnode_get_name(node);
1187d7f25f35SSimon Glass 		enum ec_flash_region region;
1188d7f25f35SSimon Glass 
1189d7f25f35SSimon Glass 		if (0 == strcmp(name, "ro")) {
1190d7f25f35SSimon Glass 			region = EC_FLASH_REGION_RO;
1191d7f25f35SSimon Glass 		} else if (0 == strcmp(name, "rw")) {
11926f1c0430SSimon Glass 			region = EC_FLASH_REGION_ACTIVE;
1193d7f25f35SSimon Glass 		} else if (0 == strcmp(name, "wp-ro")) {
1194d7f25f35SSimon Glass 			region = EC_FLASH_REGION_WP_RO;
1195d7f25f35SSimon Glass 		} else {
1196d7f25f35SSimon Glass 			debug("Unknown EC flash region name '%s'\n", name);
1197d7f25f35SSimon Glass 			return -1;
1198d7f25f35SSimon Glass 		}
1199d7f25f35SSimon Glass 
12005e0a7341SSimon Glass 		if (ofnode_read_fmap_entry(node, &config->region[region])) {
1201d7f25f35SSimon Glass 			debug("Failed to decode flash region in chrome-ec'\n");
1202d7f25f35SSimon Glass 			return -1;
1203d7f25f35SSimon Glass 		}
1204d7f25f35SSimon Glass 	}
1205d7f25f35SSimon Glass 
1206d7f25f35SSimon Glass 	return 0;
1207d7f25f35SSimon Glass }
1208d7f25f35SSimon Glass 
cros_ec_i2c_tunnel(struct udevice * dev,int port,struct i2c_msg * in,int nmsgs)12096d1a718fSMoritz Fischer int cros_ec_i2c_tunnel(struct udevice *dev, int port, struct i2c_msg *in,
12106d1a718fSMoritz Fischer 		       int nmsgs)
1211cc456bd7SSimon Glass {
1212cc456bd7SSimon Glass 	union {
1213cc456bd7SSimon Glass 		struct ec_params_i2c_passthru p;
1214cc456bd7SSimon Glass 		uint8_t outbuf[EC_PROTO2_MAX_PARAM_SIZE];
1215cc456bd7SSimon Glass 	} params;
1216cc456bd7SSimon Glass 	union {
1217cc456bd7SSimon Glass 		struct ec_response_i2c_passthru r;
1218cc456bd7SSimon Glass 		uint8_t inbuf[EC_PROTO2_MAX_PARAM_SIZE];
1219cc456bd7SSimon Glass 	} response;
1220cc456bd7SSimon Glass 	struct ec_params_i2c_passthru *p = &params.p;
1221cc456bd7SSimon Glass 	struct ec_response_i2c_passthru *r = &response.r;
1222cc456bd7SSimon Glass 	struct ec_params_i2c_passthru_msg *msg;
1223cc456bd7SSimon Glass 	uint8_t *pdata, *read_ptr = NULL;
1224cc456bd7SSimon Glass 	int read_len;
1225cc456bd7SSimon Glass 	int size;
1226cc456bd7SSimon Glass 	int rv;
1227cc456bd7SSimon Glass 	int i;
1228cc456bd7SSimon Glass 
12296d1a718fSMoritz Fischer 	p->port = port;
1230cc456bd7SSimon Glass 
1231cc456bd7SSimon Glass 	p->num_msgs = nmsgs;
1232cc456bd7SSimon Glass 	size = sizeof(*p) + p->num_msgs * sizeof(*msg);
1233cc456bd7SSimon Glass 
1234cc456bd7SSimon Glass 	/* Create a message to write the register address and optional data */
1235cc456bd7SSimon Glass 	pdata = (uint8_t *)p + size;
1236cc456bd7SSimon Glass 
1237cc456bd7SSimon Glass 	read_len = 0;
1238cc456bd7SSimon Glass 	for (i = 0, msg = p->msg; i < nmsgs; i++, msg++, in++) {
1239cc456bd7SSimon Glass 		bool is_read = in->flags & I2C_M_RD;
1240cc456bd7SSimon Glass 
1241cc456bd7SSimon Glass 		msg->addr_flags = in->addr;
1242cc456bd7SSimon Glass 		msg->len = in->len;
1243cc456bd7SSimon Glass 		if (is_read) {
1244cc456bd7SSimon Glass 			msg->addr_flags |= EC_I2C_FLAG_READ;
1245cc456bd7SSimon Glass 			read_len += in->len;
1246cc456bd7SSimon Glass 			read_ptr = in->buf;
1247cc456bd7SSimon Glass 			if (sizeof(*r) + read_len > sizeof(response)) {
1248cc456bd7SSimon Glass 				puts("Read length too big for buffer\n");
1249cc456bd7SSimon Glass 				return -1;
1250cc456bd7SSimon Glass 			}
1251cc456bd7SSimon Glass 		} else {
1252cc456bd7SSimon Glass 			if (pdata - (uint8_t *)p + in->len > sizeof(params)) {
1253cc456bd7SSimon Glass 				puts("Params too large for buffer\n");
1254cc456bd7SSimon Glass 				return -1;
1255cc456bd7SSimon Glass 			}
1256cc456bd7SSimon Glass 			memcpy(pdata, in->buf, in->len);
1257cc456bd7SSimon Glass 			pdata += in->len;
1258cc456bd7SSimon Glass 		}
1259cc456bd7SSimon Glass 	}
1260cc456bd7SSimon Glass 
12616322a7b6SSimon Glass 	rv = ec_command(dev, EC_CMD_I2C_PASSTHRU, 0, p, pdata - (uint8_t *)p,
1262cc456bd7SSimon Glass 			r, sizeof(*r) + read_len);
1263cc456bd7SSimon Glass 	if (rv < 0)
1264cc456bd7SSimon Glass 		return rv;
1265cc456bd7SSimon Glass 
1266cc456bd7SSimon Glass 	/* Parse response */
1267cc456bd7SSimon Glass 	if (r->i2c_status & EC_I2C_STATUS_ERROR) {
1268cc456bd7SSimon Glass 		printf("Transfer failed with status=0x%x\n", r->i2c_status);
1269cc456bd7SSimon Glass 		return -1;
1270cc456bd7SSimon Glass 	}
1271cc456bd7SSimon Glass 
1272cc456bd7SSimon Glass 	if (rv < sizeof(*r) + read_len) {
1273cc456bd7SSimon Glass 		puts("Truncated read response\n");
1274cc456bd7SSimon Glass 		return -1;
1275cc456bd7SSimon Glass 	}
1276cc456bd7SSimon Glass 
1277cc456bd7SSimon Glass 	/* We only support a single read message for each transfer */
1278cc456bd7SSimon Glass 	if (read_len)
1279cc456bd7SSimon Glass 		memcpy(read_ptr, r->data, read_len);
1280cc456bd7SSimon Glass 
1281cc456bd7SSimon Glass 	return 0;
1282cc456bd7SSimon Glass }
1283cc456bd7SSimon Glass 
cros_ec_check_feature(struct udevice * dev,int feature)128472ef8bfdSSimon Glass int cros_ec_check_feature(struct udevice *dev, int feature)
128572ef8bfdSSimon Glass {
128672ef8bfdSSimon Glass 	struct ec_response_get_features r;
128772ef8bfdSSimon Glass 	int rv;
128872ef8bfdSSimon Glass 
128972ef8bfdSSimon Glass 	rv = ec_command(dev, EC_CMD_GET_FEATURES, 0, &r, sizeof(r), NULL, 0);
129072ef8bfdSSimon Glass 	if (rv)
129172ef8bfdSSimon Glass 		return rv;
129272ef8bfdSSimon Glass 
129372ef8bfdSSimon Glass 	if (feature >= 8 * sizeof(r.flags))
129472ef8bfdSSimon Glass 		return -1;
129572ef8bfdSSimon Glass 
129672ef8bfdSSimon Glass 	return r.flags[feature / 32] & EC_FEATURE_MASK_0(feature);
129772ef8bfdSSimon Glass }
129872ef8bfdSSimon Glass 
129972ef8bfdSSimon Glass /*
130072ef8bfdSSimon Glass  * Query the EC for specified mask indicating enabled events.
130172ef8bfdSSimon Glass  * The EC maintains separate event masks for SMI, SCI and WAKE.
130272ef8bfdSSimon Glass  */
cros_ec_uhepi_cmd(struct udevice * dev,uint mask,uint action,uint64_t * value)130372ef8bfdSSimon Glass static int cros_ec_uhepi_cmd(struct udevice *dev, uint mask, uint action,
130472ef8bfdSSimon Glass 			     uint64_t *value)
130572ef8bfdSSimon Glass {
130672ef8bfdSSimon Glass 	int ret;
130772ef8bfdSSimon Glass 	struct ec_params_host_event req;
130872ef8bfdSSimon Glass 	struct ec_response_host_event rsp;
130972ef8bfdSSimon Glass 
131072ef8bfdSSimon Glass 	req.action = action;
131172ef8bfdSSimon Glass 	req.mask_type = mask;
131272ef8bfdSSimon Glass 	if (action != EC_HOST_EVENT_GET)
131372ef8bfdSSimon Glass 		req.value = *value;
131472ef8bfdSSimon Glass 	else
131572ef8bfdSSimon Glass 		*value = 0;
131672ef8bfdSSimon Glass 	ret = ec_command(dev, EC_CMD_HOST_EVENT, 0, &req, sizeof(req), &rsp,
131772ef8bfdSSimon Glass 			 sizeof(rsp));
131872ef8bfdSSimon Glass 
131972ef8bfdSSimon Glass 	if (action != EC_HOST_EVENT_GET)
132072ef8bfdSSimon Glass 		return ret;
132172ef8bfdSSimon Glass 	if (ret == 0)
132272ef8bfdSSimon Glass 		*value = rsp.value;
132372ef8bfdSSimon Glass 
132472ef8bfdSSimon Glass 	return ret;
132572ef8bfdSSimon Glass }
132672ef8bfdSSimon Glass 
cros_ec_handle_non_uhepi_cmd(struct udevice * dev,uint hcmd,uint action,uint64_t * value)132772ef8bfdSSimon Glass static int cros_ec_handle_non_uhepi_cmd(struct udevice *dev, uint hcmd,
132872ef8bfdSSimon Glass 					uint action, uint64_t *value)
132972ef8bfdSSimon Glass {
133072ef8bfdSSimon Glass 	int ret = -1;
133172ef8bfdSSimon Glass 	struct ec_params_host_event_mask req;
133272ef8bfdSSimon Glass 	struct ec_response_host_event_mask rsp;
133372ef8bfdSSimon Glass 
133472ef8bfdSSimon Glass 	if (hcmd == INVALID_HCMD)
133572ef8bfdSSimon Glass 		return ret;
133672ef8bfdSSimon Glass 
133772ef8bfdSSimon Glass 	if (action != EC_HOST_EVENT_GET)
133872ef8bfdSSimon Glass 		req.mask = (uint32_t)*value;
133972ef8bfdSSimon Glass 	else
134072ef8bfdSSimon Glass 		*value = 0;
134172ef8bfdSSimon Glass 
134272ef8bfdSSimon Glass 	ret = ec_command(dev, hcmd, 0, &req, sizeof(req), &rsp, sizeof(rsp));
134372ef8bfdSSimon Glass 	if (action != EC_HOST_EVENT_GET)
134472ef8bfdSSimon Glass 		return ret;
134572ef8bfdSSimon Glass 	if (ret == 0)
134672ef8bfdSSimon Glass 		*value = rsp.mask;
134772ef8bfdSSimon Glass 
134872ef8bfdSSimon Glass 	return ret;
134972ef8bfdSSimon Glass }
135072ef8bfdSSimon Glass 
cros_ec_is_uhepi_supported(struct udevice * dev)135172ef8bfdSSimon Glass bool cros_ec_is_uhepi_supported(struct udevice *dev)
135272ef8bfdSSimon Glass {
135372ef8bfdSSimon Glass #define UHEPI_SUPPORTED 1
135472ef8bfdSSimon Glass #define UHEPI_NOT_SUPPORTED 2
135572ef8bfdSSimon Glass 	static int uhepi_support;
135672ef8bfdSSimon Glass 
135772ef8bfdSSimon Glass 	if (!uhepi_support) {
135872ef8bfdSSimon Glass 		uhepi_support = cros_ec_check_feature(dev,
135972ef8bfdSSimon Glass 			EC_FEATURE_UNIFIED_WAKE_MASKS) > 0 ? UHEPI_SUPPORTED :
136072ef8bfdSSimon Glass 			UHEPI_NOT_SUPPORTED;
136172ef8bfdSSimon Glass 		log_debug("Chrome EC: UHEPI %s\n",
136272ef8bfdSSimon Glass 			  uhepi_support == UHEPI_SUPPORTED ? "supported" :
136372ef8bfdSSimon Glass 			  "not supported");
136472ef8bfdSSimon Glass 	}
136572ef8bfdSSimon Glass 	return uhepi_support == UHEPI_SUPPORTED;
136672ef8bfdSSimon Glass }
136772ef8bfdSSimon Glass 
cros_ec_get_mask(struct udevice * dev,uint type)136872ef8bfdSSimon Glass static int cros_ec_get_mask(struct udevice *dev, uint type)
136972ef8bfdSSimon Glass {
137072ef8bfdSSimon Glass 	u64 value = 0;
137172ef8bfdSSimon Glass 
137272ef8bfdSSimon Glass 	if (cros_ec_is_uhepi_supported(dev)) {
137372ef8bfdSSimon Glass 		cros_ec_uhepi_cmd(dev, type, EC_HOST_EVENT_GET, &value);
137472ef8bfdSSimon Glass 	} else {
137572ef8bfdSSimon Glass 		assert(type < ARRAY_SIZE(event_map));
137672ef8bfdSSimon Glass 		cros_ec_handle_non_uhepi_cmd(dev, event_map[type].get_cmd,
137772ef8bfdSSimon Glass 					     EC_HOST_EVENT_GET, &value);
137872ef8bfdSSimon Glass 	}
137972ef8bfdSSimon Glass 	return value;
138072ef8bfdSSimon Glass }
138172ef8bfdSSimon Glass 
cros_ec_clear_mask(struct udevice * dev,uint type,u64 mask)138272ef8bfdSSimon Glass static int cros_ec_clear_mask(struct udevice *dev, uint type, u64 mask)
138372ef8bfdSSimon Glass {
138472ef8bfdSSimon Glass 	if (cros_ec_is_uhepi_supported(dev))
138572ef8bfdSSimon Glass 		return cros_ec_uhepi_cmd(dev, type, EC_HOST_EVENT_CLEAR, &mask);
138672ef8bfdSSimon Glass 
138772ef8bfdSSimon Glass 	assert(type < ARRAY_SIZE(event_map));
138872ef8bfdSSimon Glass 
138972ef8bfdSSimon Glass 	return cros_ec_handle_non_uhepi_cmd(dev, event_map[type].clear_cmd,
139072ef8bfdSSimon Glass 					    EC_HOST_EVENT_CLEAR, &mask);
139172ef8bfdSSimon Glass }
139272ef8bfdSSimon Glass 
cros_ec_get_events_b(struct udevice * dev)139372ef8bfdSSimon Glass uint64_t cros_ec_get_events_b(struct udevice *dev)
139472ef8bfdSSimon Glass {
139572ef8bfdSSimon Glass 	return cros_ec_get_mask(dev, EC_HOST_EVENT_B);
139672ef8bfdSSimon Glass }
139772ef8bfdSSimon Glass 
cros_ec_clear_events_b(struct udevice * dev,uint64_t mask)139872ef8bfdSSimon Glass int cros_ec_clear_events_b(struct udevice *dev, uint64_t mask)
139972ef8bfdSSimon Glass {
140072ef8bfdSSimon Glass 	log_debug("Chrome EC: clear events_b mask to 0x%016llx\n", mask);
140172ef8bfdSSimon Glass 
140272ef8bfdSSimon Glass 	return cros_ec_clear_mask(dev, EC_HOST_EVENT_B, mask);
140372ef8bfdSSimon Glass }
140472ef8bfdSSimon Glass 
cros_ec_read_limit_power(struct udevice * dev,int * limit_powerp)140572ef8bfdSSimon Glass int cros_ec_read_limit_power(struct udevice *dev, int *limit_powerp)
140672ef8bfdSSimon Glass {
140772ef8bfdSSimon Glass 	struct ec_params_charge_state p;
140872ef8bfdSSimon Glass 	struct ec_response_charge_state r;
140972ef8bfdSSimon Glass 	int ret;
141072ef8bfdSSimon Glass 
141172ef8bfdSSimon Glass 	p.cmd = CHARGE_STATE_CMD_GET_PARAM;
141272ef8bfdSSimon Glass 	p.get_param.param = CS_PARAM_LIMIT_POWER;
141372ef8bfdSSimon Glass 	ret = ec_command(dev, EC_CMD_CHARGE_STATE, 0, &p, sizeof(p),
141472ef8bfdSSimon Glass 			 &r, sizeof(r));
141572ef8bfdSSimon Glass 
141672ef8bfdSSimon Glass 	/*
141772ef8bfdSSimon Glass 	 * If our EC doesn't support the LIMIT_POWER parameter, assume that
141872ef8bfdSSimon Glass 	 * LIMIT_POWER is not requested.
141972ef8bfdSSimon Glass 	 */
142072ef8bfdSSimon Glass 	if (ret == -EC_RES_INVALID_PARAM || ret == -EC_RES_INVALID_COMMAND) {
142172ef8bfdSSimon Glass 		log_warning("PARAM_LIMIT_POWER not supported by EC\n");
142272ef8bfdSSimon Glass 		return -ENOSYS;
142372ef8bfdSSimon Glass 	}
142472ef8bfdSSimon Glass 
142572ef8bfdSSimon Glass 	if (ret != sizeof(r.get_param))
142672ef8bfdSSimon Glass 		return -EINVAL;
142772ef8bfdSSimon Glass 
142872ef8bfdSSimon Glass 	*limit_powerp = r.get_param.value;
142972ef8bfdSSimon Glass 	return 0;
143072ef8bfdSSimon Glass }
143172ef8bfdSSimon Glass 
cros_ec_config_powerbtn(struct udevice * dev,uint32_t flags)143272ef8bfdSSimon Glass int cros_ec_config_powerbtn(struct udevice *dev, uint32_t flags)
143372ef8bfdSSimon Glass {
143472ef8bfdSSimon Glass 	struct ec_params_config_power_button params;
143572ef8bfdSSimon Glass 	int ret;
143672ef8bfdSSimon Glass 
143772ef8bfdSSimon Glass 	params.flags = flags;
143872ef8bfdSSimon Glass 	ret = ec_command(dev, EC_CMD_CONFIG_POWER_BUTTON, 0,
143972ef8bfdSSimon Glass 			 &params, sizeof(params), NULL, 0);
144072ef8bfdSSimon Glass 	if (ret < 0)
144172ef8bfdSSimon Glass 		return ret;
144272ef8bfdSSimon Glass 
144372ef8bfdSSimon Glass 	return 0;
144472ef8bfdSSimon Glass }
144572ef8bfdSSimon Glass 
cros_ec_get_lid_shutdown_mask(struct udevice * dev)144672ef8bfdSSimon Glass int cros_ec_get_lid_shutdown_mask(struct udevice *dev)
144772ef8bfdSSimon Glass {
144872ef8bfdSSimon Glass 	u32 mask;
144972ef8bfdSSimon Glass 	int ret;
145072ef8bfdSSimon Glass 
145172ef8bfdSSimon Glass 	ret = cros_ec_get_event_mask(dev, EC_CMD_HOST_EVENT_GET_SMI_MASK,
145272ef8bfdSSimon Glass 				     &mask);
145372ef8bfdSSimon Glass 	if (ret < 0)
145472ef8bfdSSimon Glass 		return ret;
145572ef8bfdSSimon Glass 
145672ef8bfdSSimon Glass 	return !!(mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_CLOSED));
145772ef8bfdSSimon Glass }
145872ef8bfdSSimon Glass 
cros_ec_set_lid_shutdown_mask(struct udevice * dev,int enable)145972ef8bfdSSimon Glass int cros_ec_set_lid_shutdown_mask(struct udevice *dev, int enable)
146072ef8bfdSSimon Glass {
146172ef8bfdSSimon Glass 	u32 mask;
146272ef8bfdSSimon Glass 	int ret;
146372ef8bfdSSimon Glass 
146472ef8bfdSSimon Glass 	ret = cros_ec_get_event_mask(dev, EC_CMD_HOST_EVENT_GET_SMI_MASK,
146572ef8bfdSSimon Glass 				     &mask);
146672ef8bfdSSimon Glass 	if (ret < 0)
146772ef8bfdSSimon Glass 		return ret;
146872ef8bfdSSimon Glass 
1469*a749c09aSSimon Glass 	/* Set lid close event state in the EC SMI event mask */
147072ef8bfdSSimon Glass 	if (enable)
147172ef8bfdSSimon Glass 		mask |= EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_CLOSED);
147272ef8bfdSSimon Glass 	else
147372ef8bfdSSimon Glass 		mask &= ~EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_CLOSED);
147472ef8bfdSSimon Glass 
147572ef8bfdSSimon Glass 	ret = cros_ec_set_event_mask(dev, EC_CMD_HOST_EVENT_SET_SMI_MASK, mask);
147672ef8bfdSSimon Glass 	if (ret < 0)
147772ef8bfdSSimon Glass 		return ret;
147872ef8bfdSSimon Glass 
147972ef8bfdSSimon Glass 	printf("EC: %sabled lid close event\n", enable ? "en" : "dis");
148072ef8bfdSSimon Glass 	return 0;
148172ef8bfdSSimon Glass }
148272ef8bfdSSimon Glass 
148384d6cbd3SSimon Glass UCLASS_DRIVER(cros_ec) = {
148484d6cbd3SSimon Glass 	.id		= UCLASS_CROS_EC,
148584d6cbd3SSimon Glass 	.name		= "cros_ec",
148684d6cbd3SSimon Glass 	.per_device_auto_alloc_size = sizeof(struct cros_ec_dev),
148791195485SSimon Glass 	.post_bind	= dm_scan_fdt_dev,
14884bf6f2adSSimon Glass 	.flags		= DM_UC_FLAG_ALLOC_PRIV_DMA,
148984d6cbd3SSimon Glass };
1490