summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSheng-Liang Song <ssl@chromium.org>2014-08-26 09:00:15 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-09-04 10:41:33 +0000
commit740b3e6107c2e849c6c76d43e429450d022cde26 (patch)
tree7aa1deaa5650bbae391601ded8e68bd8f5644907
parentd2503de95800d71b6e1be7b4d34a321c0bbb33dd (diff)
downloadchrome-ec-740b3e6107c2e849c6c76d43e429450d022cde26.tar.gz
EC: Add smbus interface read & write APIs
Ref: http://smbus.org/specs/smbus20.pdf - Support software CRC8 generation and checking. - Support read/write word (2-bytes) - Support read/write blocks (up to 32 bytes) Ported from ToT CL:209747 BUG=chrome-os-partner:24741 BRANCH=ToT,glimmer TEST=Verified with smart battery firmware update application on glimmer. Passed LGC & Simplo Battery. Signed-off-by: Sheng-Liang Song <ssl@chromium.org> Change-Id: Id1718d689e5081677b759b41f015629f6ab6b7c4 Reviewed-on: https://chromium-review.googlesource.com/214247 Reviewed-by: Shawn Nematbakhsh <shawnn@chromium.org>
-rw-r--r--chip/host/i2c.c21
-rw-r--r--chip/lm4/i2c.c5
-rw-r--r--common/build.mk2
-rw-r--r--common/crc8.c23
-rw-r--r--common/smbus.c236
-rw-r--r--include/common.h2
-rw-r--r--include/config.h5
-rw-r--r--include/crc8.h21
-rw-r--r--include/i2c.h5
-rw-r--r--include/smbus.h158
10 files changed, 478 insertions, 0 deletions
diff --git a/chip/host/i2c.c b/chip/host/i2c.c
index 3f8a8b8241..1ce7da9340 100644
--- a/chip/host/i2c.c
+++ b/chip/host/i2c.c
@@ -150,3 +150,24 @@ int i2c_read_string(int port, int slave_addr, int offset, uint8_t *data,
}
return EC_ERROR_UNKNOWN;
}
+
+int smbus_write_word(uint8_t i2c_port, uint8_t slave_addr,
+ uint8_t smbus_cmd, uint16_t d16)
+{
+ return i2c_write16(i2c_port, slave_addr, smbus_cmd, d16);
+}
+
+int smbus_read_word(uint8_t i2c_port, uint8_t slave_addr,
+ uint8_t smbus_cmd, uint16_t *p16)
+{
+ int rv, d16 = 0;
+ rv = i2c_read16(i2c_port, slave_addr, smbus_cmd, &d16);
+ *p16 = d16;
+ return rv;
+}
+
+int smbus_read_string(int i2c_port, uint8_t slave_addr, uint8_t smbus_cmd,
+ uint8_t *data, int len)
+{
+ return i2c_read_string(i2c_port, slave_addr, smbus_cmd, data, len);
+}
diff --git a/chip/lm4/i2c.c b/chip/lm4/i2c.c
index 2c6bbab8c4..8b7bd182ca 100644
--- a/chip/lm4/i2c.c
+++ b/chip/lm4/i2c.c
@@ -70,6 +70,11 @@ struct i2c_port_data {
};
static struct i2c_port_data pdata[I2C_PORT_COUNT];
+int i2c_is_busy(int port)
+{
+ return LM4_I2C_MCS(port) & LM4_I2C_MCS_BUSBSY;
+}
+
/**
* I2C transfer engine.
*
diff --git a/common/build.mk b/common/build.mk
index 62f6750933..50bd82c94a 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -29,6 +29,7 @@ common-$(CONFIG_CHARGER_TPS65090)+=pmu_tps65090_charger.o
common-$(CONFIG_CMD_I2CWEDGE)+=i2c_wedge.o
common-$(CONFIG_COMMON_PANIC_OUTPUT)+=panic_output.o
common-$(CONFIG_COMMON_TIMER)+=timer.o
+common-$(CONFIG_CRC8)+= crc8.o
common-$(CONFIG_PMU_POWERINFO)+=pmu_tps65090_powerinfo.o
common-$(CONFIG_PMU_TPS65090)+=pmu_tps65090.o
common-$(CONFIG_EOPTION)+=eoption.o
@@ -55,6 +56,7 @@ common-$(CONFIG_POWER_BUTTON_X86)+=power_button_x86.o
common-$(CONFIG_PSTORE)+=pstore_commands.o
common-$(CONFIG_PWM)+=pwm.o
common-$(CONFIG_PWM_KBLIGHT)+=pwm_kblight.o
+common-$(CONFIG_SMBUS)+= smbus.o
common-$(CONFIG_SWITCH)+=switch.o
common-$(CONFIG_TEMP_SENSOR)+=temp_sensor.o thermal.o
common-$(CONFIG_USB_PORT_POWER_DUMB)+=usb_port_power_dumb.o
diff --git a/common/crc8.c b/common/crc8.c
new file mode 100644
index 0000000000..b752698d14
--- /dev/null
+++ b/common/crc8.c
@@ -0,0 +1,23 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "common.h"
+#include "crc8.h"
+
+uint8_t crc8(const uint8_t *data, int len)
+{
+ unsigned crc = 0;
+ int i, j;
+
+ for (j = len; j; j--, data++) {
+ crc ^= (*data << 8);
+ for (i = 8; i; i--) {
+ if (crc & 0x8000)
+ crc ^= (0x1070 << 3);
+ crc <<= 1;
+ }
+ }
+
+ return (uint8_t)(crc >> 8);
+}
diff --git a/common/smbus.c b/common/smbus.c
new file mode 100644
index 0000000000..aea915f54e
--- /dev/null
+++ b/common/smbus.c
@@ -0,0 +1,236 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * smbus cross-platform code for Chrome EC
+ * ref: http://smbus.org/specs/smbus20.pdf
+ */
+
+#include "common.h"
+#include "console.h"
+#include "util.h"
+#include "i2c.h"
+#include "smbus.h"
+#include "crc8.h"
+#include "shared_mem.h"
+
+/**
+ * @brief smbus write common interface
+ * [S][slave_addr][A][smbus_cmd][A]...[P]
+ */
+struct smbus_wr_if {
+ uint8_t slave_addr;/**< i2c_addr << 1 */
+ uint8_t smbus_cmd; /**< smbus cmd */
+ uint8_t data[0]; /**< smbus data */
+} __packed;
+
+/**
+ * @brief smbus read common interface
+ * [S][slave_addr][A][smbus_cmd][A][slave_addr_rd][A]...[P]
+ */
+struct smbus_rd_if {
+ uint8_t slave_addr; /**< (i2c_addr << 1)*/
+ uint8_t smbus_cmd; /**< smbus cmd */
+ uint8_t slave_addr_rd;/**< (i2c_addr << 1) | 0x1 */
+ uint8_t data[0]; /**< smbus data */
+} __packed;
+
+
+#define CPRINTF(format, args...) cprintf(CC_I2C, format, ## args)
+
+/*
+ * smbus interface write n bytes
+ * case 1: n-1 byte data, 1 byte PEC
+ * [S][i2c Address][Wr=0][A][cmd][A] ...[Di][Ai]... [PEC][A][P]
+ *
+ * case 2: 1 byte data-size, n -2 byte data, 1 byte PEC
+ * [S][i2c Address][Wr=0][A][cmd][A][size][A] ...[Di][Ai]... [PEC][A][P]
+ */
+static int smbus_if_write(int i2c_port, struct smbus_wr_if *intf,
+ uint8_t size_n, uint8_t data_n, uint8_t pec_n)
+{
+ int rv;
+ uint8_t n;
+ data_n = MIN(data_n, SMBUS_MAX_BLOCK_SIZE);
+ n = size_n + data_n + pec_n;
+ if (pec_n)
+ intf->data[n-1] = crc8((const uint8_t *)intf,
+ n - 1 + sizeof(struct smbus_wr_if));
+ i2c_lock(i2c_port, 1);
+ rv = i2c_is_busy(i2c_port);
+ if (!rv)
+ rv = i2c_xfer(i2c_port, intf->slave_addr,
+ &intf->smbus_cmd, n + 1, NULL, 0, I2C_XFER_SINGLE);
+ else
+ rv = EC_ERROR_BUSY;
+ i2c_lock(i2c_port, 0);
+ if (rv)
+ CPRINTF("smbus wr i2c_xfer error:%d cmd:%02X n:%d\n",
+ rv, intf->smbus_cmd, n);
+ return rv;
+}
+
+/*
+ * smbus interface read n bytes
+ * tx 8-bit smbus cmd, and read n bytes
+ *
+ * case 1: n-1 byte data, 1 byte PEC
+ * [S][i2c addr][Wr=0][A][cmd][A]
+ * [S][i2c addr][Rd=1][A]...[Di][Ai]...[PEC][A][P]
+ *
+ * case 2: 1 byte data-size, n - 2 byte data, 1 byte PEC
+ * [S][i2c addr][Wr=0][A][cmd][A]
+ * [S][i2c addr][Rd=1][A][size][A]...[Di][Ai]...[PEC][A][P]
+ */
+static int smbus_if_read(int i2c_port, struct smbus_rd_if *intf,
+ uint8_t size_n, uint8_t *pdata_n, uint8_t pec_n)
+{
+ int rv;
+ uint8_t pec, n, data_n;
+
+ data_n = MIN(*pdata_n, SMBUS_MAX_BLOCK_SIZE);
+ n = size_n + data_n + pec_n;
+
+ i2c_lock(i2c_port, 1);
+
+ /* Check if smbus is busy */
+ rv = i2c_is_busy(i2c_port);
+ if (rv) {
+ rv = EC_ERROR_BUSY;
+ CPRINTF("smbus_cmd:%02X bus busy error:%d\n",
+ intf->smbus_cmd, rv);
+ i2c_lock(i2c_port, 0);
+ return rv;
+ }
+
+ rv = i2c_xfer(i2c_port, intf->slave_addr,
+ &(intf->smbus_cmd), 1, intf->data, n, I2C_XFER_SINGLE);
+
+ i2c_lock(i2c_port, 0);
+
+ if (rv)
+ return rv;
+
+ if (pec_n == 0)
+ return EC_SUCCESS;
+
+ /*
+ * Compute and Check Packet Error Code (crc8)
+ */
+ intf->slave_addr_rd = intf->slave_addr | 0x01;
+ if (size_n) {
+ data_n = MIN(data_n, intf->data[0]);
+ data_n = MIN(data_n, SMBUS_MAX_BLOCK_SIZE);
+ }
+
+ if (*pdata_n != data_n) {
+ CPRINTF("smbus read[%02X] size %02X != %02X\n",
+ intf->smbus_cmd, *pdata_n, data_n);
+ return EC_ERROR_INVAL;
+ }
+
+ n = size_n + data_n + pec_n;
+ pec = crc8((const uint8_t *)intf, n - 1 + sizeof(struct smbus_rd_if));
+ if (pec != intf->data[n-1]) {
+ CPRINTF("smbus read[%02X] PEC %02X != %02X\n",
+ intf->smbus_cmd, intf->data[n-1], pec);
+ return EC_ERROR_CRC;
+ }
+ return EC_SUCCESS;
+}
+
+int smbus_write_word(uint8_t i2c_port, uint8_t slave_addr,
+ uint8_t smbus_cmd, uint16_t d16)
+{
+ int rv;
+ struct smbus_wr_word *s;
+ rv = shared_mem_acquire(sizeof(struct smbus_wr_word), (char **)&s);
+ if (rv) {
+ CPRINTF("smbus write wd[%02X] mem error\n", smbus_cmd);
+ return rv;
+ }
+ s->slave_addr = slave_addr,
+ s->smbus_cmd = smbus_cmd;
+ s->data[0] = d16 & 0xFF;
+ s->data[1] = (d16 >> 8) & 0xFF;
+ rv = smbus_if_write(i2c_port, (struct smbus_wr_if *)s, 0, 2, 1);
+ shared_mem_release(s);
+ return rv;
+}
+
+int smbus_write_block(uint8_t i2c_port, uint8_t slave_addr,
+ uint8_t smbus_cmd, uint8_t *data, uint8_t len)
+{
+ int rv;
+ struct smbus_wr_block *s;
+ rv = shared_mem_acquire(sizeof(struct smbus_wr_block), (char **)&s);
+ if (rv) {
+ CPRINTF("smbus write block[%02X] mem error\n",
+ smbus_cmd);
+ return rv;
+ }
+ s->slave_addr = slave_addr,
+ s->smbus_cmd = smbus_cmd;
+ s->size = MIN(len, SMBUS_MAX_BLOCK_SIZE);
+ memmove(s->data, data, s->size);
+ rv = smbus_if_write(i2c_port, (struct smbus_wr_if *)s, 1, s->size, 1);
+ shared_mem_release(s);
+ return rv;
+}
+
+int smbus_read_word(uint8_t i2c_port, uint8_t slave_addr,
+ uint8_t smbus_cmd, uint16_t *p16)
+{
+ int rv;
+ uint8_t data_n = 2;
+ struct smbus_rd_word s;
+ s.slave_addr = slave_addr;
+ s.smbus_cmd = smbus_cmd;
+ rv = smbus_if_read(i2c_port, (struct smbus_rd_if *)&s, 0, &data_n, 1);
+ if (rv == EC_SUCCESS)
+ *p16 = (s.data[1] << 8) | s.data[0];
+ else
+ *p16 = 0;
+ return rv;
+}
+
+int smbus_read_block(uint8_t i2c_port, uint8_t slave_addr,
+ uint8_t smbus_cmd, uint8_t *data, uint8_t *plen)
+{
+ int rv;
+ struct smbus_rd_block *s;
+ uint8_t len = *plen;
+ rv = shared_mem_acquire(sizeof(struct smbus_rd_block), (char **)&s);
+
+ if (rv) {
+ CPRINTF("smbus read block[%02X] mem error\n",
+ smbus_cmd);
+ return rv;
+ }
+ s->slave_addr = slave_addr,
+ s->smbus_cmd = smbus_cmd;
+ s->size = MIN(len, SMBUS_MAX_BLOCK_SIZE);
+
+ rv = smbus_if_read(i2c_port, (struct smbus_rd_if *)s, 1, &s->size, 1);
+ s->size = MIN(s->size, SMBUS_MAX_BLOCK_SIZE);
+ s->size = MIN(s->size, len);
+ *plen = s->size;
+ if (rv == EC_SUCCESS)
+ memmove(data, s->data, s->size);
+ else
+ memset(data, 0x0, s->size);
+
+ shared_mem_release(s);
+ return rv;
+}
+
+int smbus_read_string(int i2c_port, uint8_t slave_addr, uint8_t smbus_cmd,
+ uint8_t *data, uint8_t len)
+{
+ int rv;
+ len -= 1;
+ rv = smbus_read_block(i2c_port, slave_addr,
+ smbus_cmd, data, &len);
+ data[len] = '\0';
+ return rv;
+}
diff --git a/include/common.h b/include/common.h
index 5f236a1f35..113b165108 100644
--- a/include/common.h
+++ b/include/common.h
@@ -79,6 +79,8 @@ enum ec_error_list {
EC_ERROR_NOT_POWERED = 8,
/* Failed because component is not calibrated */
EC_ERROR_NOT_CALIBRATED = 9,
+ /* Failed because CRC error */
+ EC_ERROR_CRC = 10,
/* Invalid console command param (PARAMn means parameter n is bad) */
EC_ERROR_PARAM1 = 11,
EC_ERROR_PARAM2 = 12,
diff --git a/include/config.h b/include/config.h
index e61cd5002d..34b9518d67 100644
--- a/include/config.h
+++ b/include/config.h
@@ -310,6 +310,9 @@
*/
#undef CONFIG_CONSOLE_RESTRICTED_INPUT
+/* Include crc8 utility functions */
+#undef CONFIG_CRC8
+
/*****************************************************************************/
/*
* Debugging config
@@ -677,6 +680,8 @@
/* Allow the board to use a GPIO for the SCI# signal. */
#undef CONFIG_SCI_GPIO
+/* Support smbus interface */
+#undef CONFIG_SMBUS
/* Support SPI interfaces */
#undef CONFIG_SPI
diff --git a/include/crc8.h b/include/crc8.h
new file mode 100644
index 0000000000..94980effc5
--- /dev/null
+++ b/include/crc8.h
@@ -0,0 +1,21 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Very simple 8-bit CRC function.
+ */
+#ifndef __EC_CRC8_H__
+#define __EC_CRC8_H__
+
+/**
+ * crc8
+ * Return CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial. A table-based
+ * algorithm would be faster, but for only a few bytes it isn't worth the code
+ * size.
+ * @param data uint8_t *, input, a pointer to input data
+ * @param len int, input, size of iput data in byte
+ * @return the crc-8 of the input data.
+ */
+uint8_t crc8(const uint8_t *data, int len);
+
+#endif /* __EC_CRC8_H__ */
diff --git a/include/i2c.h b/include/i2c.h
index ac572960df..87fe4cc3d0 100644
--- a/include/i2c.h
+++ b/include/i2c.h
@@ -143,6 +143,11 @@ int i2c_read8(int port, int slave_addr, int offset, int *data);
int i2c_write8(int port, int slave_addr, int offset, int data);
/**
+ * @return none-zero if i2c bus is busy
+ */
+int i2c_is_busy(int port);
+
+/**
* Attempt to unwedge an I2C bus.
*
* @param port I2C port
diff --git a/include/smbus.h b/include/smbus.h
new file mode 100644
index 0000000000..8f1bfeb9c9
--- /dev/null
+++ b/include/smbus.h
@@ -0,0 +1,158 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * @file smbus.h
+ * @brief smbus interface APIs
+ * @see http://smbus.org/specs/smbus20.pdf
+ */
+#ifndef __EC_SMBUS_H__
+#define __EC_SMBUS_H__
+
+/** Maximum transfer of a SMBUS block transfer */
+#define SMBUS_MAX_BLOCK_SIZE 32
+
+/**
+ * smbus write word
+ * write 2 byte data + 1 byte pec
+ */
+struct smbus_wr_word {
+ uint8_t slave_addr;/**< i2c_addr << 1 */
+ uint8_t smbus_cmd; /**< smbus cmd */
+ uint8_t data[3]; /**< smbus data */
+} __packed;
+
+/**
+ * smbus write block data
+ * smbus write 1 byte size + 32 byte data + 1 byte pec
+ */
+struct smbus_wr_block {
+ uint8_t slave_addr;/**< i2c_addr << 1 */
+ uint8_t smbus_cmd; /**< smbus cmd */
+ uint8_t size; /**< write size */
+ uint8_t data[SMBUS_MAX_BLOCK_SIZE+1];
+} __packed;
+
+/**
+ * smbus read word
+ * smbus read 2 byte + 1 pec
+ */
+struct smbus_rd_word {
+ uint8_t slave_addr; /**< i2c_addr << 1 */
+ uint8_t smbus_cmd; /**< smbus cmd */
+ uint8_t slave_addr_rd;/**< (i2c_addr << 1) | 0x1 */
+ uint8_t data[3]; /**< smbus data */
+} __packed;
+
+/**
+ * smbus read block data
+ * smbus read 1 byte size + 32 byte data + 1 byte pec
+ */
+struct smbus_rd_block {
+ uint8_t slave_addr; /**< i2c_addr << 1 */
+ uint8_t smbus_cmd; /**< smbus cmd */
+ uint8_t slave_addr_rd;/**< (i2c_addr << 1) | 0x1 */
+ uint8_t size; /**< read block size */
+ uint8_t data[SMBUS_MAX_BLOCK_SIZE+1]; /**< smbus data */
+} __packed;
+
+/**
+ * smbus_write_word
+ * smbus write 2 bytes
+ * @param i2c_port uint8_t, i2c port address
+ * @param slave_addr uint8_t, i2c slave address:= (i2c_addr << 1)
+ * @param smbus_cmd uint8_t, smbus command
+ * @param d16 uint16_t, 2-bytes data
+ * @return error_code
+ * EC_SUCCESS if success; none-zero if fail
+ * EC_ERROR_BUSY if interface is bussy
+ * none zero error code if fail
+ */
+int smbus_write_word(uint8_t i2c_port, uint8_t slave_addr,
+ uint8_t smbus_cmd, uint16_t d16);
+
+/**
+ * smbus_write_block
+ * smbus write upto 32 bytes
+ * case 1: n-1 byte data, 1 byte PEC
+ * [S][i2c Address][Wr=0][A][cmd][A] ...[Di][Ai]... [PEC][A][P]
+ *
+ * case 2: 1 byte data-size, n -2 byte data, 1 byte PEC
+ * [S][i2c Address][Wr=0][A][cmd][A][size][A] ...[Di][Ai]... [PEC][A][P]
+ *
+ * @param i2c_port uint8_t, i2c port address
+ * @param slave_addr uint8_t, i2c slave address:= (i2c_addr << 1)
+ * @param smbus_cmd uint8_t, smbus command
+ * @param data uint8_t *, n-bytes data
+ * @param len uint8_t, data length
+ * @return error_code
+ * EC_SUCCESS if success; none-zero if fail
+ * EC_ERROR_BUSY if interface is bussy
+ * none zero error code if fail
+ */
+int smbus_write_block(uint8_t i2c_port, uint8_t slave_addr,
+ uint8_t smbus_cmd, uint8_t *data, uint8_t len);
+
+/**
+ * smbus_read_word
+ * smbus read 2 bytes
+ * @param i2c_port uint8_t, i2c port address
+ * @param slave_addr uint8_t, i2c slave address:= (i2c_addr << 1)
+ * @param smbus_cmd uint8_t, smbus command
+ * @param p16 uint16_t *, a pointer to 2-bytes data
+ * @return error_code
+ * EC_SUCCESS if success; none-zero if fail
+ * EC_ERROR_BUSY if interface is bussy
+ * none zero error code if fail
+ */
+int smbus_read_word(uint8_t i2c_port, uint8_t slave_addr,
+ uint8_t smbus_cmd, uint16_t *p16);
+
+/**
+ * smbus_read_block
+ * smbus read upto 32 bytes
+ * case 1: n-1 byte data, 1 byte PEC
+ * [S][i2c addr][Wr=0][A][cmd][A]
+ * [S][i2c addr][Rd=1][A]...[Di][Ai]...[PEC][A][P]
+ *
+ * case 2: 1 byte data-size, n - 2 byte data, 1 byte PEC
+ * [S][i2c addr][Wr=0][A][cmd][A]
+ * [S][i2c addr][Rd=1][A][size][A]...[Di][Ai]...[PEC][A][P]
+ *
+ * @param i2c_port uint8_t, i2c port address
+ * @param slave_addr uint8_t, i2c slave address:= (i2c_addr << 1)
+ * @param smbus_cmd uint8_t, smbus command
+ * @param data uint8_t *, n-bytes data
+ * @param plen uint8_t *, a pointer data length
+ * @return error_code
+ * EC_SUCCESS if success; none-zero if fail
+ * EC_ERROR_BUSY if interface is bussy
+ * none zero error code if fail
+ */
+int smbus_read_block(uint8_t i2c_port, uint8_t slave_addr,
+ uint8_t smbus_cmd, uint8_t *data, uint8_t *plen);
+
+/**
+ * smbus_read_string
+ * smbus read ascii string (upto 32-byte data + 1-byte NULL)
+ * Read bytestream from <slaveaddr>:<smbus_cmd> with format:
+ * [length_N] [byte_0] [byte_1] ... [byte_N-1][byte_N='\0']
+ *
+ * <len> : the max length of receving buffer. to read N bytes
+ * ascii, len should be at least N+1 to include the
+ * terminating 0 (NULL).
+ *
+ * @param i2c_port uint8_t, i2c port address
+ * @param slave_addr uint8_t, i2c slave address:= (i2c_addr << 1)
+ * @param smbus_cmd uint8_t, smbus command
+ * @param data uint8_t *, n-bytes data
+ * @param len uint8_t, data length
+ * @return error_code
+ * EC_SUCCESS if success; none-zero if fail
+ * EC_ERROR_BUSY if interface is bussy
+ * none zero error code if fail
+ */
+int smbus_read_string(int i2c_port, uint8_t slave_addr, uint8_t smbus_cmd,
+ uint8_t *data, uint8_t len);
+
+#endif /* __EC_SMBUS_H__ */