xref: /rk3399_rockchip-uboot/drivers/misc/cros_ec_i2c.c (revision 85df958ce267c602a4ec5f1e41f336c5a8d3b441)
178764a4eSHung-ying Tyan /*
278764a4eSHung-ying Tyan  * Chromium OS cros_ec driver - I2C interface
378764a4eSHung-ying Tyan  *
478764a4eSHung-ying Tyan  * Copyright (c) 2012 The Chromium OS Authors.
578764a4eSHung-ying Tyan  *
61a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
778764a4eSHung-ying Tyan  */
878764a4eSHung-ying Tyan 
978764a4eSHung-ying Tyan /*
1078764a4eSHung-ying Tyan  * The Matrix Keyboard Protocol driver handles talking to the keyboard
1178764a4eSHung-ying Tyan  * controller chip. Mostly this is for keyboard functions, but some other
1278764a4eSHung-ying Tyan  * things have slipped in, so we provide generic services to talk to the
1378764a4eSHung-ying Tyan  * KBC.
1478764a4eSHung-ying Tyan  */
1578764a4eSHung-ying Tyan 
1678764a4eSHung-ying Tyan #include <common.h>
17*85df958cSSimon Glass #include <dm.h>
1878764a4eSHung-ying Tyan #include <i2c.h>
1978764a4eSHung-ying Tyan #include <cros_ec.h>
2078764a4eSHung-ying Tyan 
2178764a4eSHung-ying Tyan #ifdef DEBUG_TRACE
2278764a4eSHung-ying Tyan #define debug_trace(fmt, b...)	debug(fmt, #b)
2378764a4eSHung-ying Tyan #else
2478764a4eSHung-ying Tyan #define debug_trace(fmt, b...)
2578764a4eSHung-ying Tyan #endif
2678764a4eSHung-ying Tyan 
27*85df958cSSimon Glass static int cros_ec_i2c_command(struct udevice *udev, uint8_t cmd,
28*85df958cSSimon Glass 			       int cmd_version, const uint8_t *dout,
29*85df958cSSimon Glass 			       int dout_len, uint8_t **dinp, int din_len)
3078764a4eSHung-ying Tyan {
31*85df958cSSimon Glass 	struct cros_ec_dev *dev = udev->uclass_priv;
3278764a4eSHung-ying Tyan 	/* version8, cmd8, arglen8, out8[dout_len], csum8 */
3378764a4eSHung-ying Tyan 	int out_bytes = dout_len + 4;
3478764a4eSHung-ying Tyan 	/* response8, arglen8, in8[din_len], checksum8 */
3578764a4eSHung-ying Tyan 	int in_bytes = din_len + 3;
3678764a4eSHung-ying Tyan 	uint8_t *ptr;
3778764a4eSHung-ying Tyan 	/* Receive input data, so that args will be dword aligned */
3878764a4eSHung-ying Tyan 	uint8_t *in_ptr;
39e8c12662SRandall Spangler 	int len, csum, ret;
4078764a4eSHung-ying Tyan 
4178764a4eSHung-ying Tyan 	/*
4278764a4eSHung-ying Tyan 	 * Sanity-check I/O sizes given transaction overhead in internal
4378764a4eSHung-ying Tyan 	 * buffers.
4478764a4eSHung-ying Tyan 	 */
4578764a4eSHung-ying Tyan 	if (out_bytes > sizeof(dev->dout)) {
4678764a4eSHung-ying Tyan 		debug("%s: Cannot send %d bytes\n", __func__, dout_len);
4778764a4eSHung-ying Tyan 		return -1;
4878764a4eSHung-ying Tyan 	}
4978764a4eSHung-ying Tyan 	if (in_bytes > sizeof(dev->din)) {
5078764a4eSHung-ying Tyan 		debug("%s: Cannot receive %d bytes\n", __func__, din_len);
5178764a4eSHung-ying Tyan 		return -1;
5278764a4eSHung-ying Tyan 	}
5378764a4eSHung-ying Tyan 	assert(dout_len >= 0);
5478764a4eSHung-ying Tyan 	assert(dinp);
5578764a4eSHung-ying Tyan 
5678764a4eSHung-ying Tyan 	/*
5778764a4eSHung-ying Tyan 	 * Copy command and data into output buffer so we can do a single I2C
5878764a4eSHung-ying Tyan 	 * burst transaction.
5978764a4eSHung-ying Tyan 	 */
6078764a4eSHung-ying Tyan 	ptr = dev->dout;
6178764a4eSHung-ying Tyan 
6278764a4eSHung-ying Tyan 	/*
6378764a4eSHung-ying Tyan 	 * in_ptr starts of pointing to a dword-aligned input data buffer.
6478764a4eSHung-ying Tyan 	 * We decrement it back by the number of header bytes we expect to
6578764a4eSHung-ying Tyan 	 * receive, so that the first parameter of the resulting input data
6678764a4eSHung-ying Tyan 	 * will be dword aligned.
6778764a4eSHung-ying Tyan 	 */
6878764a4eSHung-ying Tyan 	in_ptr = dev->din + sizeof(int64_t);
69e8c12662SRandall Spangler 
70e8c12662SRandall Spangler 	if (dev->protocol_version != 2) {
71e8c12662SRandall Spangler 		/* Something we don't support */
72e8c12662SRandall Spangler 		debug("%s: Protocol version %d unsupported\n",
73e8c12662SRandall Spangler 		      __func__, dev->protocol_version);
74e8c12662SRandall Spangler 		return -1;
75e8c12662SRandall Spangler 	}
76e8c12662SRandall Spangler 
7778764a4eSHung-ying Tyan 	*ptr++ = EC_CMD_VERSION0 + cmd_version;
7878764a4eSHung-ying Tyan 	*ptr++ = cmd;
7978764a4eSHung-ying Tyan 	*ptr++ = dout_len;
8078764a4eSHung-ying Tyan 	in_ptr -= 2;	/* Expect status, length bytes */
81e8c12662SRandall Spangler 
8278764a4eSHung-ying Tyan 	memcpy(ptr, dout, dout_len);
8378764a4eSHung-ying Tyan 	ptr += dout_len;
8478764a4eSHung-ying Tyan 
8578764a4eSHung-ying Tyan 	*ptr++ = (uint8_t)
8678764a4eSHung-ying Tyan 		cros_ec_calc_checksum(dev->dout, dout_len + 3);
8778764a4eSHung-ying Tyan 
8878764a4eSHung-ying Tyan 	/* Send output data */
8978764a4eSHung-ying Tyan 	cros_ec_dump_data("out", -1, dev->dout, out_bytes);
90*85df958cSSimon Glass 	ret = dm_i2c_write(udev, 0, dev->dout, out_bytes);
9178764a4eSHung-ying Tyan 	if (ret) {
92*85df958cSSimon Glass 		debug("%s: Cannot complete I2C write to %s\n", __func__,
93*85df958cSSimon Glass 		      udev->name);
9478764a4eSHung-ying Tyan 		ret = -1;
9578764a4eSHung-ying Tyan 	}
9678764a4eSHung-ying Tyan 
9778764a4eSHung-ying Tyan 	if (!ret) {
98*85df958cSSimon Glass 		ret = dm_i2c_read(udev, 0, in_ptr, in_bytes);
9978764a4eSHung-ying Tyan 		if (ret) {
100*85df958cSSimon Glass 			debug("%s: Cannot complete I2C read from %s\n",
101*85df958cSSimon Glass 			      __func__, udev->name);
10278764a4eSHung-ying Tyan 			ret = -1;
10378764a4eSHung-ying Tyan 		}
10478764a4eSHung-ying Tyan 	}
10578764a4eSHung-ying Tyan 
10678764a4eSHung-ying Tyan 	if (*in_ptr != EC_RES_SUCCESS) {
10778764a4eSHung-ying Tyan 		debug("%s: Received bad result code %d\n", __func__, *in_ptr);
10878764a4eSHung-ying Tyan 		return -(int)*in_ptr;
10978764a4eSHung-ying Tyan 	}
11078764a4eSHung-ying Tyan 
11178764a4eSHung-ying Tyan 	len = in_ptr[1];
11278764a4eSHung-ying Tyan 	if (len + 3 > sizeof(dev->din)) {
11378764a4eSHung-ying Tyan 		debug("%s: Received length %#02x too large\n",
11478764a4eSHung-ying Tyan 		      __func__, len);
11578764a4eSHung-ying Tyan 		return -1;
11678764a4eSHung-ying Tyan 	}
11778764a4eSHung-ying Tyan 	csum = cros_ec_calc_checksum(in_ptr, 2 + len);
11878764a4eSHung-ying Tyan 	if (csum != in_ptr[2 + len]) {
11978764a4eSHung-ying Tyan 		debug("%s: Invalid checksum rx %#02x, calced %#02x\n",
12078764a4eSHung-ying Tyan 		      __func__, in_ptr[2 + din_len], csum);
12178764a4eSHung-ying Tyan 		return -1;
12278764a4eSHung-ying Tyan 	}
12378764a4eSHung-ying Tyan 	din_len = min(din_len, len);
12478764a4eSHung-ying Tyan 	cros_ec_dump_data("in", -1, in_ptr, din_len + 3);
12578764a4eSHung-ying Tyan 
12678764a4eSHung-ying Tyan 	/* Return pointer to dword-aligned input data, if any */
12778764a4eSHung-ying Tyan 	*dinp = dev->din + sizeof(int64_t);
12878764a4eSHung-ying Tyan 
12978764a4eSHung-ying Tyan 	return din_len;
13078764a4eSHung-ying Tyan }
13178764a4eSHung-ying Tyan 
132*85df958cSSimon Glass static int cros_ec_probe(struct udevice *dev)
13378764a4eSHung-ying Tyan {
134*85df958cSSimon Glass 	return cros_ec_register(dev);
13578764a4eSHung-ying Tyan }
13678764a4eSHung-ying Tyan 
137*85df958cSSimon Glass static struct dm_cros_ec_ops cros_ec_ops = {
138*85df958cSSimon Glass 	.command = cros_ec_i2c_command,
139*85df958cSSimon Glass };
14078764a4eSHung-ying Tyan 
141*85df958cSSimon Glass static const struct udevice_id cros_ec_ids[] = {
142*85df958cSSimon Glass 	{ .compatible = "google,cros-ec" },
143*85df958cSSimon Glass 	{ }
144*85df958cSSimon Glass };
14578764a4eSHung-ying Tyan 
146*85df958cSSimon Glass U_BOOT_DRIVER(cros_ec_i2c) = {
147*85df958cSSimon Glass 	.name		= "cros_ec",
148*85df958cSSimon Glass 	.id		= UCLASS_CROS_EC,
149*85df958cSSimon Glass 	.of_match	= cros_ec_ids,
150*85df958cSSimon Glass 	.probe		= cros_ec_probe,
151*85df958cSSimon Glass 	.ops		= &cros_ec_ops,
152*85df958cSSimon Glass };
153