xref: /rk3399_rockchip-uboot/drivers/misc/cros_ec_i2c.c (revision e8c12662364fbcf5ea917d341f707534c8574900)
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>
1778764a4eSHung-ying Tyan #include <i2c.h>
1878764a4eSHung-ying Tyan #include <cros_ec.h>
1978764a4eSHung-ying Tyan 
2078764a4eSHung-ying Tyan #ifdef DEBUG_TRACE
2178764a4eSHung-ying Tyan #define debug_trace(fmt, b...)	debug(fmt, #b)
2278764a4eSHung-ying Tyan #else
2378764a4eSHung-ying Tyan #define debug_trace(fmt, b...)
2478764a4eSHung-ying Tyan #endif
2578764a4eSHung-ying Tyan 
2678764a4eSHung-ying Tyan int cros_ec_i2c_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version,
2778764a4eSHung-ying Tyan 		     const uint8_t *dout, int dout_len,
2878764a4eSHung-ying Tyan 		     uint8_t **dinp, int din_len)
2978764a4eSHung-ying Tyan {
3078764a4eSHung-ying Tyan 	int old_bus = 0;
3178764a4eSHung-ying Tyan 	/* version8, cmd8, arglen8, out8[dout_len], csum8 */
3278764a4eSHung-ying Tyan 	int out_bytes = dout_len + 4;
3378764a4eSHung-ying Tyan 	/* response8, arglen8, in8[din_len], checksum8 */
3478764a4eSHung-ying Tyan 	int in_bytes = din_len + 3;
3578764a4eSHung-ying Tyan 	uint8_t *ptr;
3678764a4eSHung-ying Tyan 	/* Receive input data, so that args will be dword aligned */
3778764a4eSHung-ying Tyan 	uint8_t *in_ptr;
38*e8c12662SRandall Spangler 	int len, csum, ret;
3978764a4eSHung-ying Tyan 
4078764a4eSHung-ying Tyan 	old_bus = i2c_get_bus_num();
4178764a4eSHung-ying Tyan 
4278764a4eSHung-ying Tyan 	/*
4378764a4eSHung-ying Tyan 	 * Sanity-check I/O sizes given transaction overhead in internal
4478764a4eSHung-ying Tyan 	 * buffers.
4578764a4eSHung-ying Tyan 	 */
4678764a4eSHung-ying Tyan 	if (out_bytes > sizeof(dev->dout)) {
4778764a4eSHung-ying Tyan 		debug("%s: Cannot send %d bytes\n", __func__, dout_len);
4878764a4eSHung-ying Tyan 		return -1;
4978764a4eSHung-ying Tyan 	}
5078764a4eSHung-ying Tyan 	if (in_bytes > sizeof(dev->din)) {
5178764a4eSHung-ying Tyan 		debug("%s: Cannot receive %d bytes\n", __func__, din_len);
5278764a4eSHung-ying Tyan 		return -1;
5378764a4eSHung-ying Tyan 	}
5478764a4eSHung-ying Tyan 	assert(dout_len >= 0);
5578764a4eSHung-ying Tyan 	assert(dinp);
5678764a4eSHung-ying Tyan 
5778764a4eSHung-ying Tyan 	/*
5878764a4eSHung-ying Tyan 	 * Copy command and data into output buffer so we can do a single I2C
5978764a4eSHung-ying Tyan 	 * burst transaction.
6078764a4eSHung-ying Tyan 	 */
6178764a4eSHung-ying Tyan 	ptr = dev->dout;
6278764a4eSHung-ying Tyan 
6378764a4eSHung-ying Tyan 	/*
6478764a4eSHung-ying Tyan 	 * in_ptr starts of pointing to a dword-aligned input data buffer.
6578764a4eSHung-ying Tyan 	 * We decrement it back by the number of header bytes we expect to
6678764a4eSHung-ying Tyan 	 * receive, so that the first parameter of the resulting input data
6778764a4eSHung-ying Tyan 	 * will be dword aligned.
6878764a4eSHung-ying Tyan 	 */
6978764a4eSHung-ying Tyan 	in_ptr = dev->din + sizeof(int64_t);
70*e8c12662SRandall Spangler 
71*e8c12662SRandall Spangler 	if (dev->protocol_version != 2) {
72*e8c12662SRandall Spangler 		/* Something we don't support */
73*e8c12662SRandall Spangler 		debug("%s: Protocol version %d unsupported\n",
74*e8c12662SRandall Spangler 		      __func__, dev->protocol_version);
75*e8c12662SRandall Spangler 		return -1;
76*e8c12662SRandall Spangler 	}
77*e8c12662SRandall Spangler 
7878764a4eSHung-ying Tyan 	*ptr++ = EC_CMD_VERSION0 + cmd_version;
7978764a4eSHung-ying Tyan 	*ptr++ = cmd;
8078764a4eSHung-ying Tyan 	*ptr++ = dout_len;
8178764a4eSHung-ying Tyan 	in_ptr -= 2;	/* Expect status, length bytes */
82*e8c12662SRandall Spangler 
8378764a4eSHung-ying Tyan 	memcpy(ptr, dout, dout_len);
8478764a4eSHung-ying Tyan 	ptr += dout_len;
8578764a4eSHung-ying Tyan 
8678764a4eSHung-ying Tyan 	*ptr++ = (uint8_t)
8778764a4eSHung-ying Tyan 		cros_ec_calc_checksum(dev->dout, dout_len + 3);
8878764a4eSHung-ying Tyan 
8978764a4eSHung-ying Tyan 	/* Set to the proper i2c bus */
9078764a4eSHung-ying Tyan 	if (i2c_set_bus_num(dev->bus_num)) {
9178764a4eSHung-ying Tyan 		debug("%s: Cannot change to I2C bus %d\n", __func__,
9278764a4eSHung-ying Tyan 			dev->bus_num);
9378764a4eSHung-ying Tyan 		return -1;
9478764a4eSHung-ying Tyan 	}
9578764a4eSHung-ying Tyan 
9678764a4eSHung-ying Tyan 	/* Send output data */
9778764a4eSHung-ying Tyan 	cros_ec_dump_data("out", -1, dev->dout, out_bytes);
9878764a4eSHung-ying Tyan 	ret = i2c_write(dev->addr, 0, 0, dev->dout, out_bytes);
9978764a4eSHung-ying Tyan 	if (ret) {
10078764a4eSHung-ying Tyan 		debug("%s: Cannot complete I2C write to 0x%x\n",
10178764a4eSHung-ying Tyan 			__func__, dev->addr);
10278764a4eSHung-ying Tyan 		ret = -1;
10378764a4eSHung-ying Tyan 	}
10478764a4eSHung-ying Tyan 
10578764a4eSHung-ying Tyan 	if (!ret) {
10678764a4eSHung-ying Tyan 		ret = i2c_read(dev->addr, 0, 0, in_ptr, in_bytes);
10778764a4eSHung-ying Tyan 		if (ret) {
10878764a4eSHung-ying Tyan 			debug("%s: Cannot complete I2C read from 0x%x\n",
10978764a4eSHung-ying Tyan 				__func__, dev->addr);
11078764a4eSHung-ying Tyan 			ret = -1;
11178764a4eSHung-ying Tyan 		}
11278764a4eSHung-ying Tyan 	}
11378764a4eSHung-ying Tyan 
11478764a4eSHung-ying Tyan 	/* Return to original bus number */
11578764a4eSHung-ying Tyan 	i2c_set_bus_num(old_bus);
11678764a4eSHung-ying Tyan 	if (ret)
11778764a4eSHung-ying Tyan 		return ret;
11878764a4eSHung-ying Tyan 
11978764a4eSHung-ying Tyan 	if (*in_ptr != EC_RES_SUCCESS) {
12078764a4eSHung-ying Tyan 		debug("%s: Received bad result code %d\n", __func__, *in_ptr);
12178764a4eSHung-ying Tyan 		return -(int)*in_ptr;
12278764a4eSHung-ying Tyan 	}
12378764a4eSHung-ying Tyan 
12478764a4eSHung-ying Tyan 	len = in_ptr[1];
12578764a4eSHung-ying Tyan 	if (len + 3 > sizeof(dev->din)) {
12678764a4eSHung-ying Tyan 		debug("%s: Received length %#02x too large\n",
12778764a4eSHung-ying Tyan 		      __func__, len);
12878764a4eSHung-ying Tyan 		return -1;
12978764a4eSHung-ying Tyan 	}
13078764a4eSHung-ying Tyan 	csum = cros_ec_calc_checksum(in_ptr, 2 + len);
13178764a4eSHung-ying Tyan 	if (csum != in_ptr[2 + len]) {
13278764a4eSHung-ying Tyan 		debug("%s: Invalid checksum rx %#02x, calced %#02x\n",
13378764a4eSHung-ying Tyan 		      __func__, in_ptr[2 + din_len], csum);
13478764a4eSHung-ying Tyan 		return -1;
13578764a4eSHung-ying Tyan 	}
13678764a4eSHung-ying Tyan 	din_len = min(din_len, len);
13778764a4eSHung-ying Tyan 	cros_ec_dump_data("in", -1, in_ptr, din_len + 3);
13878764a4eSHung-ying Tyan 
13978764a4eSHung-ying Tyan 	/* Return pointer to dword-aligned input data, if any */
14078764a4eSHung-ying Tyan 	*dinp = dev->din + sizeof(int64_t);
14178764a4eSHung-ying Tyan 
14278764a4eSHung-ying Tyan 	return din_len;
14378764a4eSHung-ying Tyan }
14478764a4eSHung-ying Tyan 
14578764a4eSHung-ying Tyan int cros_ec_i2c_decode_fdt(struct cros_ec_dev *dev, const void *blob)
14678764a4eSHung-ying Tyan {
14778764a4eSHung-ying Tyan 	/* Decode interface-specific FDT params */
14878764a4eSHung-ying Tyan 	dev->max_frequency = fdtdec_get_int(blob, dev->node,
14978764a4eSHung-ying Tyan 					    "i2c-max-frequency", 100000);
15078764a4eSHung-ying Tyan 	dev->bus_num = i2c_get_bus_num_fdt(dev->parent_node);
15178764a4eSHung-ying Tyan 	if (dev->bus_num == -1) {
15278764a4eSHung-ying Tyan 		debug("%s: Failed to read bus number\n", __func__);
15378764a4eSHung-ying Tyan 		return -1;
15478764a4eSHung-ying Tyan 	}
15578764a4eSHung-ying Tyan 	dev->addr = fdtdec_get_int(blob, dev->node, "reg", -1);
15678764a4eSHung-ying Tyan 	if (dev->addr == -1) {
15778764a4eSHung-ying Tyan 		debug("%s: Failed to read device address\n", __func__);
15878764a4eSHung-ying Tyan 		return -1;
15978764a4eSHung-ying Tyan 	}
16078764a4eSHung-ying Tyan 
16178764a4eSHung-ying Tyan 	return 0;
16278764a4eSHung-ying Tyan }
16378764a4eSHung-ying Tyan 
16478764a4eSHung-ying Tyan /**
16578764a4eSHung-ying Tyan  * Initialize I2C protocol.
16678764a4eSHung-ying Tyan  *
16778764a4eSHung-ying Tyan  * @param dev		CROS_EC device
16878764a4eSHung-ying Tyan  * @param blob		Device tree blob
16978764a4eSHung-ying Tyan  * @return 0 if ok, -1 on error
17078764a4eSHung-ying Tyan  */
17178764a4eSHung-ying Tyan int cros_ec_i2c_init(struct cros_ec_dev *dev, const void *blob)
17278764a4eSHung-ying Tyan {
17378764a4eSHung-ying Tyan 	i2c_init(dev->max_frequency, dev->addr);
17478764a4eSHung-ying Tyan 
17578764a4eSHung-ying Tyan 	return 0;
17678764a4eSHung-ying Tyan }
177