mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-11 02:15:14 +00:00
Implement I2C passthru command
This is a revised version of passthru which more closely resembles the
kernel interface. It allows multiple read/write messages in a single
transaction, and sends back one accumulated result.
BUG=chrome-os-partner:18778
BRANCH=none
TEST=On link, from root shell:
ectool i2cxfer 0 0xb 6 0x21
Read bytes: 0x05 0x41 0x52 0x52 0x4f 0x57
(I did not actually run this with the updated code)
On pit, in U-Boot:
Read i2c values:
Peach # crosec i2c md 48 0
0000: 00 00 3e 00 12 20 4b bf ff ff 20 00 1e 1e 1e 1f ..>.. K... .....
Peach # crosec i2c md 48 0 20
0000: 00 00 3e 00 12 20 4b bf ff ff 20 00 1e 1e 1e 1f ..>.. K... .....
0010: 1f 1f 1f 1f 1f 1f 20 00 00 07 00 00 00 00 00 00 ...... .........
Update value at offset 10:
Peach # crosec i2c mw 48 10 4
Peach # crosec i2c md 48 0 20
0000: 00 00 3e 00 12 00 0b 1f 1f ff 20 00 1e 1e 1e 1f ..>....... .....
0010: 04 1f 1f 1f 1f 1f 20 00 00 07 00 00 00 00 00 00 ...... .........
Peach #
On pit, in kernel:
localhost ~ # i2cdetect -y -a -r 20
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
localhost ~ # i2cdump -f -y 20 0x48
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 00 00 3e 00 12 00 0b 1f 1f ff 20 00 1e 1e 1e 1f ..>.?.???. .????
10: 1f 1f 0e 1f 1f 0e 20 00 00 07 00 00 00 00 00 00 ?????? ..?......
20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
localhost ~ # i2cset -f -y 20 0x48 0x10 0
localhost ~ # i2cdump -f -y 20 0x48
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 00 00 3e 00 12 00 0b 1f 1f ff 20 00 1e 1e 1e 1f ..>.?.???. .????
10: 00 1f 0e 1f 1f 0e 20 00 00 07 00 00 00 00 00 00 .????? ..?......
20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
localhost ~ # i2cset -f -y 20 0x48 0x10 0x1f
localhost ~ # i2cdump -f -y 20 0x48
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 00 00 3e 00 12 00 0b 1f 1f ff 20 00 1e 1e 1e 1f ..>.?.???. .????
10: 1f 1f 0e 1f 1f 0e 20 00 00 07 00 00 00 00 00 00 ?????? ..?......
Change-Id: I14d47e1712828f726ac5caddc4beede251570ad3
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Updated to simplify protocol:
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/49958
Commit-Queue: Doug Anderson <dianders@chromium.org>
Reviewed-by: Doug Anderson <dianders@chromium.org>
Tested-by: Doug Anderson <dianders@chromium.org>
This commit is contained in:
committed by
ChromeBot
parent
d2ca284bc6
commit
1e4b0b6194
@@ -24,6 +24,7 @@
|
||||
#endif
|
||||
#define CONFIG_CUSTOM_KEYSCAN
|
||||
#define CONFIG_EXTPOWER_GPIO
|
||||
#define CONFIG_I2C_PASSTHRU_RESTRICTED
|
||||
#ifdef HAS_TASK_KEYPROTO
|
||||
#define CONFIG_KEYBOARD_PROTOCOL_8042
|
||||
#endif
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
#include "util.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
#define CPUTS(outstr) cputs(CC_I2C, outstr)
|
||||
#define CPRINTF(format, args...) cprintf(CC_I2C, format, ## args)
|
||||
|
||||
extern const struct i2c_port_t i2c_ports[I2C_PORTS_USED];
|
||||
|
||||
static struct mutex port_mutex[I2C_PORT_COUNT];
|
||||
@@ -123,8 +126,10 @@ static int i2c_command_read(struct host_cmd_handler_args *args)
|
||||
struct ec_response_i2c_read *r = args->response;
|
||||
int data, rv = -1;
|
||||
|
||||
#ifdef CONFIG_I2C_PASSTHRU_RESTRICTED
|
||||
if (system_is_locked())
|
||||
return EC_RES_ACCESS_DENIED;
|
||||
#endif
|
||||
|
||||
if (p->read_size == 16)
|
||||
rv = i2c_read16(p->port, p->addr, p->offset, &data);
|
||||
@@ -145,8 +150,10 @@ static int i2c_command_write(struct host_cmd_handler_args *args)
|
||||
const struct ec_params_i2c_write *p = args->params;
|
||||
int rv = -1;
|
||||
|
||||
#ifdef CONFIG_I2C_PASSTHRU_RESTRICTED
|
||||
if (system_is_locked())
|
||||
return EC_RES_ACCESS_DENIED;
|
||||
#endif
|
||||
|
||||
if (p->write_size == 16)
|
||||
rv = i2c_write16(p->port, p->addr, p->offset, p->data);
|
||||
@@ -160,6 +167,159 @@ static int i2c_command_write(struct host_cmd_handler_args *args)
|
||||
}
|
||||
DECLARE_HOST_COMMAND(EC_CMD_I2C_WRITE, i2c_command_write, EC_VER_MASK(0));
|
||||
|
||||
/* TODO: remove temporary extra debugging for help host-side debugging */
|
||||
#ifdef CONFIG_I2C_DEBUG_PASSTHRU
|
||||
#define PTHRUPRINTF(format, args...) cprintf(CC_I2C, format, ## args)
|
||||
#else
|
||||
#define PTHRUPRINTF(format, args...)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Perform the voluminous checking required for this message
|
||||
*
|
||||
* @param args Arguments
|
||||
* @return 0 if OK, EC_RES_INVALID_PARAM on error
|
||||
*/
|
||||
static int check_i2c_params(const struct host_cmd_handler_args *args)
|
||||
{
|
||||
const struct ec_params_i2c_passthru *params = args->params;
|
||||
const struct ec_params_i2c_passthru_msg *msg;
|
||||
int read_len = 0, write_len = 0;
|
||||
unsigned int size;
|
||||
int msgnum;
|
||||
|
||||
if (args->params_size < sizeof(*params)) {
|
||||
PTHRUPRINTF("[%T i2c passthru no params, params_size=%d, need at least %d]\n",
|
||||
args->params_size, sizeof(*params));
|
||||
return EC_RES_INVALID_PARAM;
|
||||
}
|
||||
size = sizeof(*params) + params->num_msgs * sizeof(*msg);
|
||||
if (args->params_size < size) {
|
||||
PTHRUPRINTF("[%T i2c passthru params_size=%d, need at least %d]\n",
|
||||
args->params_size, size);
|
||||
return EC_RES_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (params->port >= I2C_PORT_COUNT) {
|
||||
PTHRUPRINTF("[%T i2c passthru invalid port %d]\n",
|
||||
params->port);
|
||||
return EC_RES_INVALID_PARAM;
|
||||
}
|
||||
|
||||
/* Loop and process messages */;
|
||||
for (msgnum = 0, msg = params->msg; msgnum < params->num_msgs;
|
||||
msgnum++, msg++) {
|
||||
unsigned int addr_flags = msg->addr_flags;
|
||||
|
||||
/* Parse slave address if necessary */
|
||||
if (addr_flags & EC_I2C_FLAG_10BIT) {
|
||||
/* 10-bit addressing not supported yet */
|
||||
PTHRUPRINTF("[%T i2c passthru no 10-bit addressing]\n");
|
||||
return EC_RES_INVALID_PARAM;
|
||||
}
|
||||
|
||||
PTHRUPRINTF("[%T i2c passthru port=%d, %s, addr=0x%02x, len=0x%02x]\n",
|
||||
params->port,
|
||||
addr_flags & EC_I2C_FLAG_READ ? "read" : "write",
|
||||
addr_flags & EC_I2C_ADDR_MASK,
|
||||
msg->len);
|
||||
|
||||
if (addr_flags & EC_I2C_FLAG_READ)
|
||||
read_len += msg->len;
|
||||
else
|
||||
write_len += msg->len;
|
||||
}
|
||||
|
||||
/* Check there is room for the data */
|
||||
if (args->response_max <
|
||||
sizeof(struct ec_response_i2c_passthru) + read_len) {
|
||||
PTHRUPRINTF("[%T i2c passthru overflow1]\n");
|
||||
return EC_RES_INVALID_PARAM;
|
||||
}
|
||||
|
||||
/* Must have bytes to write */
|
||||
if (args->params_size < size + write_len) {
|
||||
PTHRUPRINTF("[%T i2c passthru overflow2]\n");
|
||||
return EC_RES_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return EC_RES_SUCCESS;
|
||||
}
|
||||
|
||||
static int i2c_command_passthru(struct host_cmd_handler_args *args)
|
||||
{
|
||||
const struct ec_params_i2c_passthru *params = args->params;
|
||||
const struct ec_params_i2c_passthru_msg *msg;
|
||||
struct ec_response_i2c_passthru *resp = args->response;
|
||||
const uint8_t *out;
|
||||
int in_len;
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_I2C_PASSTHRU_RESTRICTED
|
||||
if (system_is_locked())
|
||||
return EC_RES_ACCESS_DENIED;
|
||||
#endif
|
||||
|
||||
ret = check_i2c_params(args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Loop and process messages */
|
||||
resp->i2c_status = 0;
|
||||
out = args->params + sizeof(*params) + params->num_msgs * sizeof(*msg);
|
||||
in_len = 0;
|
||||
|
||||
i2c_lock(params->port, 1);
|
||||
|
||||
for (resp->num_msgs = 0, msg = params->msg;
|
||||
resp->num_msgs < params->num_msgs;
|
||||
resp->num_msgs++, msg++) {
|
||||
/* EC uses 8-bit slave address */
|
||||
unsigned int addr = (msg->addr_flags & EC_I2C_ADDR_MASK) << 1;
|
||||
int xferflags = I2C_XFER_START;
|
||||
int read_len = 0, write_len = 0;
|
||||
int rv;
|
||||
|
||||
if (msg->addr_flags & EC_I2C_FLAG_READ)
|
||||
read_len = msg->len;
|
||||
else
|
||||
write_len = msg->len;
|
||||
|
||||
/* Set stop bit for last message */
|
||||
if (resp->num_msgs == params->num_msgs - 1)
|
||||
xferflags |= I2C_XFER_STOP;
|
||||
|
||||
/* Transfer next message */
|
||||
PTHRUPRINTF("[%T i2c passthru xfer port=%x, addr=%x, out=%p, write_len=%x, data=%p, read_len=%x, xferflags=%x]\n",
|
||||
params->port, addr, out, write_len,
|
||||
&resp->data[in_len], read_len, xferflags);
|
||||
rv = i2c_xfer(params->port, addr, out, write_len,
|
||||
&resp->data[in_len], read_len, xferflags);
|
||||
if (rv) {
|
||||
/* Driver will have sent a stop bit here */
|
||||
if (rv == EC_ERROR_TIMEOUT)
|
||||
resp->i2c_status = EC_I2C_STATUS_TIMEOUT;
|
||||
else
|
||||
resp->i2c_status = EC_I2C_STATUS_NAK;
|
||||
break;
|
||||
}
|
||||
|
||||
in_len += read_len;
|
||||
out += write_len;
|
||||
}
|
||||
args->response_size = sizeof(*resp) + in_len;
|
||||
|
||||
/* Unlock port */
|
||||
i2c_lock(params->port, 0);
|
||||
|
||||
/*
|
||||
* Return success even if transfer failed so response is sent. Host
|
||||
* will check message status to determine the transfer result.
|
||||
*/
|
||||
return EC_RES_SUCCESS;
|
||||
}
|
||||
DECLARE_HOST_COMMAND(EC_CMD_I2C_PASSTHRU, i2c_command_passthru, EC_VER_MASK(0));
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Console commands */
|
||||
|
||||
|
||||
@@ -1140,7 +1140,7 @@ struct ec_response_gpio_get {
|
||||
#define EC_CMD_I2C_READ 0x94
|
||||
|
||||
struct ec_params_i2c_read {
|
||||
uint16_t addr;
|
||||
uint16_t addr; /* 8-bit address (7-bit shifted << 1) */
|
||||
uint8_t read_size; /* Either 8 or 16. */
|
||||
uint8_t port;
|
||||
uint8_t offset;
|
||||
@@ -1154,7 +1154,7 @@ struct ec_response_i2c_read {
|
||||
|
||||
struct ec_params_i2c_write {
|
||||
uint16_t data;
|
||||
uint16_t addr;
|
||||
uint16_t addr; /* 8-bit address (7-bit shifted << 1) */
|
||||
uint8_t write_size; /* Either 8 or 16. */
|
||||
uint8_t port;
|
||||
uint8_t offset;
|
||||
@@ -1253,6 +1253,45 @@ struct ec_response_power_info {
|
||||
uint16_t usb_current_limit;
|
||||
} __packed;
|
||||
|
||||
/*****************************************************************************/
|
||||
/* I2C passthru command */
|
||||
|
||||
#define EC_CMD_I2C_PASSTHRU 0x9e
|
||||
|
||||
/* Slave address is 10 (not 7) bit */
|
||||
#define EC_I2C_FLAG_10BIT (1 << 16)
|
||||
|
||||
/* Read data; if not present, message is a write */
|
||||
#define EC_I2C_FLAG_READ (1 << 15)
|
||||
|
||||
/* Mask for address */
|
||||
#define EC_I2C_ADDR_MASK 0x3ff
|
||||
|
||||
#define EC_I2C_STATUS_NAK (1 << 0) /* Transfer was not acknowledged */
|
||||
#define EC_I2C_STATUS_TIMEOUT (1 << 1) /* Timeout during transfer */
|
||||
|
||||
/* Any error */
|
||||
#define EC_I2C_STATUS_ERROR (EC_I2C_STATUS_NAK | EC_I2C_STATUS_TIMEOUT)
|
||||
|
||||
struct ec_params_i2c_passthru_msg {
|
||||
uint16_t addr_flags; /* I2C slave address (7 or 10 bits) and flags */
|
||||
uint16_t len; /* Number of bytes to write*/
|
||||
} __packed;
|
||||
|
||||
struct ec_params_i2c_passthru {
|
||||
uint8_t port; /* I2C port number */
|
||||
uint8_t num_msgs; /* Number of messages */
|
||||
struct ec_params_i2c_passthru_msg msg[];
|
||||
/* Data for all messages is concatenated here */
|
||||
} __packed;
|
||||
|
||||
struct ec_response_i2c_passthru {
|
||||
uint8_t i2c_status; /* Status flags (EC_I2C_STATUS_...) */
|
||||
uint8_t num_msgs; /* Number of messages processed */
|
||||
uint8_t data[]; /* Data for all messages concatenated here */
|
||||
} __packed;
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Temporary debug commands. TODO: remove this crosbug.com/p/13849 */
|
||||
|
||||
|
||||
121
util/ectool.c
121
util/ectool.c
@@ -94,6 +94,8 @@ const char help_str[] =
|
||||
" Read I2C bus\n"
|
||||
" i2cwrite\n"
|
||||
" Write I2C bus\n"
|
||||
" i2cxfer <port> <slave_addr> <read_count> [write bytes...]\n"
|
||||
" Perform I2C transfer on EC's I2C bus\n"
|
||||
" keyscan <beat_us> <filename>\n"
|
||||
" Test low-level key scanning\n"
|
||||
" lightbar [CMDS]\n"
|
||||
@@ -1933,6 +1935,10 @@ int cmd_i2c_read(int argc, char *argv[])
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: use I2C_XFER command if supported, then fall back to I2C_READ
|
||||
*/
|
||||
|
||||
rv = ec_command(EC_CMD_I2C_READ, 0, &p, sizeof(p), &r, sizeof(r));
|
||||
|
||||
if (rv < 0)
|
||||
@@ -1987,6 +1993,10 @@ int cmd_i2c_write(int argc, char *argv[])
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: use I2C_XFER command if supported, then fall back to I2C_WRITE
|
||||
*/
|
||||
|
||||
rv = ec_command(EC_CMD_I2C_WRITE, 0, &p, sizeof(p), NULL, 0);
|
||||
|
||||
if (rv < 0)
|
||||
@@ -1998,6 +2008,116 @@ int cmd_i2c_write(int argc, char *argv[])
|
||||
}
|
||||
|
||||
|
||||
int cmd_i2c_xfer(int argc, char *argv[])
|
||||
{
|
||||
union {
|
||||
struct ec_params_i2c_passthru p;
|
||||
uint8_t outbuf[EC_HOST_PARAM_SIZE];
|
||||
} params;
|
||||
union {
|
||||
struct ec_response_i2c_passthru r;
|
||||
uint8_t inbuf[EC_HOST_PARAM_SIZE];
|
||||
} response;
|
||||
struct ec_params_i2c_passthru *p = ¶ms.p;
|
||||
struct ec_response_i2c_passthru *r = &response.r;
|
||||
struct ec_params_i2c_passthru_msg *msg = p->msg;
|
||||
unsigned int addr;
|
||||
uint8_t *pdata;
|
||||
char *e;
|
||||
int read_len, write_len;
|
||||
int size;
|
||||
int rv, i;
|
||||
|
||||
if (argc < 4) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <port> <slave_addr> <read_count> "
|
||||
"[write bytes...]\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
p->port = strtol(argv[1], &e, 0);
|
||||
if (e && *e) {
|
||||
fprintf(stderr, "Bad port.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr = strtol(argv[2], &e, 0) & 0x7f;
|
||||
if (e && *e) {
|
||||
fprintf(stderr, "Bad slave address.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
read_len = strtol(argv[3], &e, 0);
|
||||
if (e && *e) {
|
||||
fprintf(stderr, "Bad read length.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Skip over params to bytes to write */
|
||||
argc -= 4;
|
||||
argv += 4;
|
||||
write_len = argc;
|
||||
p->num_msgs = (read_len != 0) + (write_len != 0);
|
||||
|
||||
size = sizeof(*p) + p->num_msgs * sizeof(*msg);
|
||||
if (size + write_len > sizeof(params)) {
|
||||
fprintf(stderr, "Params too large for buffer\n");
|
||||
return -1;
|
||||
}
|
||||
if (sizeof(*r) + read_len > sizeof(response)) {
|
||||
fprintf(stderr, "Read length too big for buffer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pdata = (uint8_t *)p + size;
|
||||
if (write_len) {
|
||||
msg->addr_flags = addr;
|
||||
msg->len = write_len;
|
||||
|
||||
for (i = 0; i < write_len; i++) {
|
||||
pdata[i] = strtol(argv[i], &e, 0);
|
||||
if (e && *e) {
|
||||
fprintf(stderr, "Bad write byte %d\n", i);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
msg++;
|
||||
}
|
||||
|
||||
if (read_len) {
|
||||
msg->addr_flags = addr | EC_I2C_FLAG_READ;
|
||||
msg->len = read_len;
|
||||
}
|
||||
|
||||
rv = ec_command(EC_CMD_I2C_PASSTHRU, 0, p, size + write_len,
|
||||
r, sizeof(*r) + read_len);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
/* Parse response */
|
||||
if (r->i2c_status & (EC_I2C_STATUS_NAK | EC_I2C_STATUS_TIMEOUT)) {
|
||||
fprintf(stderr, "Transfer failed with status=0x%x\n",
|
||||
r->i2c_status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rv < sizeof(*r) + read_len) {
|
||||
fprintf(stderr, "Truncated read response\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (read_len) {
|
||||
printf("Read bytes:");
|
||||
for (i = 0; i < read_len; i++)
|
||||
printf(" %#02x", r->data[i]);
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("Write successful.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_lcd_backlight(int argc, char *argv[])
|
||||
{
|
||||
struct ec_params_switch_enable_backlight p;
|
||||
@@ -2787,6 +2907,7 @@ const struct command commands[] = {
|
||||
{"kbpress", cmd_kbpress},
|
||||
{"i2cread", cmd_i2c_read},
|
||||
{"i2cwrite", cmd_i2c_write},
|
||||
{"i2cxfer", cmd_i2c_xfer},
|
||||
{"lightbar", cmd_lightbar},
|
||||
{"keyconfig", cmd_keyconfig},
|
||||
{"keyscan", cmd_keyscan},
|
||||
|
||||
Reference in New Issue
Block a user