diff options
author | Sheng-Liang Song <ssl@chromium.org> | 2014-07-24 09:22:11 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-08-29 02:57:00 +0000 |
commit | 7bea5174a19b6d3b4cd9f5a7e96f7d492a24fc8f (patch) | |
tree | 4bf5f92421086f20668dd19d85ee129cfd6e93f7 | |
parent | b7f1d5261917c88e734d0e788de74b2bc419c431 (diff) | |
download | chrome-ec-7bea5174a19b6d3b4cd9f5a7e96f7d492a24fc8f.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)
BUG=chrome-os-partner:24741
BRANCH=ToT,glimmer
TEST=Verified with smart battery firmware update application on glimmer.
Passed LGC & Simplo Battery.
Change-Id: Ic2e7f759af80c06741ed49fee1826213429fbf8a
Signed-off-by: Sheng-Liang Song <ssl@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/209747
Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r-- | chip/host/i2c.c | 21 | ||||
-rw-r--r-- | chip/lm4/i2c.c | 5 | ||||
-rw-r--r-- | common/build.mk | 5 | ||||
-rw-r--r-- | common/crc8.c | 23 | ||||
-rw-r--r-- | common/smbus.c | 236 | ||||
-rw-r--r-- | include/common.h | 2 | ||||
-rw-r--r-- | include/config.h | 8 | ||||
-rw-r--r-- | include/crc8.h | 21 | ||||
-rw-r--r-- | include/i2c.h | 5 | ||||
-rw-r--r-- | include/smbus.h | 158 |
10 files changed, 481 insertions, 3 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 dcb34cfce6..51470eed70 100644 --- a/chip/lm4/i2c.c +++ b/chip/lm4/i2c.c @@ -72,6 +72,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 c44711a655..87eaa57cd3 100644 --- a/common/build.mk +++ b/common/build.mk @@ -1,5 +1,5 @@ # -*- makefile -*- -# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# 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. # @@ -33,6 +33,7 @@ common-$(CONFIG_COMMON_GPIO)+=gpio.o common-$(CONFIG_COMMON_PANIC_OUTPUT)+=panic_output.o common-$(CONFIG_COMMON_RUNTIME)+=hooks.o main.o system.o shared_mem.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 @@ -60,6 +61,7 @@ common-$(CONFIG_PSTORE)+=pstore_commands.o common-$(CONFIG_PWM)+=pwm.o common-$(CONFIG_PWM_KBLIGHT)+=pwm_kblight.o common-$(CONFIG_SHA1)+=sha1.o +common-$(CONFIG_SMBUS)+= smbus.o common-$(CONFIG_SOFTWARE_CLZ)+=clz.o common-$(CONFIG_SPI_FLASH)+=spi_flash.o common-$(CONFIG_SWITCH)+=switch.o @@ -79,4 +81,3 @@ common-$(HAS_TASK_KEYSCAN)+=keyboard_scan.o common-$(HAS_TASK_LIGHTBAR)+=lb_common.o lightbar.o common-$(HAS_TASK_MOTIONSENSE)+=motion_sense.o math_util.o common-$(TEST_BUILD)+=test_util.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 cddcde31fa..eb4ebd7c32 100644 --- a/include/common.h +++ b/include/common.h @@ -88,6 +88,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 609d0943b2..8e0f1dd10a 100644 --- a/include/config.h +++ b/include/config.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +/* 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. */ @@ -376,6 +376,9 @@ */ #undef CONFIG_CONSOLE_RESTRICTED_INPUT +/* Include CRC-8 utility function */ +#undef CONFIG_CRC8 + /*****************************************************************************/ /* * Debugging config @@ -803,6 +806,9 @@ /* Emulate the CLZ (Count Leading Zeros) in software for CPU lacking support */ #undef CONFIG_SOFTWARE_CLZ +/* 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 9cb884ba19..a4e105ddac 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -151,6 +151,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 non-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__ */ |