From 349bceadd7c30c1a01f78f62d68da0b9b79af346 Mon Sep 17 00:00:00 2001 From: Denis Brockus Date: Sun, 8 Dec 2019 13:13:09 -0700 Subject: i2c: add generic read/modify/write operations i2c_update is used to set or clear a mask i2c_field_update is used to clear out a field before the set BUG=none BRANCH=none TEST=make buildall -j Change-Id: I7f8f93f4894fb9635092931a93961d328eacfeb9 Signed-off-by: Denis Brockus Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1956437 Reviewed-by: Jack Rosenthal Reviewed-by: Jett Rink Commit-Queue: Jett Rink Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2584544 Reviewed-by: Zhuohao Lee Tested-by: David Huang Auto-Submit: David Huang --- common/i2c_master.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/i2c.h | 51 +++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) diff --git a/common/i2c_master.c b/common/i2c_master.c index f6d5b2fdc5..1aa4dc7bc7 100644 --- a/common/i2c_master.c +++ b/common/i2c_master.c @@ -306,6 +306,92 @@ int i2c_write8(int port, int slave_addr, int offset, int data) return i2c_xfer(port, slave_addr, buf, 2, 0, 0); } +int i2c_update8(const int port, + const uint16_t slave_addr_flags, + const int offset, + const uint8_t mask, + const enum mask_update_action action) +{ + int rv; + int val, oldval; + + rv = i2c_read8(port, slave_addr_flags, offset, &oldval); + if (rv) + return rv; + + val = (action == MASK_SET) ? oldval | mask + : oldval & ~mask; + + if (val != oldval) + return i2c_write8(port, slave_addr_flags, offset, val); + + return EC_SUCCESS; +} + +int i2c_update16(const int port, + const uint16_t slave_addr_flags, + const int offset, + const uint16_t mask, + const enum mask_update_action action) +{ + int rv; + int val, oldval; + + rv = i2c_read16(port, slave_addr_flags, offset, &oldval); + if (rv) + return rv; + + val = (action == MASK_SET) ? oldval | mask + : oldval & ~mask; + + if (val != oldval) + return i2c_write16(port, slave_addr_flags, offset, val); + + return EC_SUCCESS; +} + +int i2c_field_update8(const int port, + const uint16_t slave_addr_flags, + const int offset, + const uint8_t field_mask, + const uint8_t set_value) +{ + int rv; + int val, oldval; + + rv = i2c_read8(port, slave_addr_flags, offset, &oldval); + if (rv) + return rv; + + val = (oldval & (~field_mask)) | set_value; + + if (val != oldval) + return i2c_write8(port, slave_addr_flags, offset, val); + + return EC_SUCCESS; +} + +int i2c_field_update16(const int port, + const uint16_t slave_addr_flags, + const int offset, + const uint16_t field_mask, + const uint16_t set_value) +{ + int rv; + int val, oldval; + + rv = i2c_read16(port, slave_addr_flags, offset, &oldval); + if (rv) + return rv; + + val = (oldval & (~field_mask)) | set_value; + + if (val != oldval) + return i2c_write16(port, slave_addr_flags, offset, val); + + return EC_SUCCESS; +} + int i2c_read_string(int port, int slave_addr, int offset, uint8_t *data, int len) { diff --git a/include/i2c.h b/include/i2c.h index 3d6e445571..58e8bfe25a 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -32,6 +32,16 @@ enum i2c_freq { I2C_FREQ_COUNT, }; +/* + * I2C mask update actions. + * MASK_SET will OR the mask into the old value + * MASK_CLR will AND the ~mask from the old value + */ +enum mask_update_action { + MASK_CLR, + MASK_SET +}; + struct i2c_info_t { uint16_t port; /* Physical port for device */ uint16_t addr; /* 8-bit (or 11-bit) address */ @@ -270,6 +280,47 @@ int i2c_read8(int port, int slave_addr, int offset, int *data); */ int i2c_write8(int port, int slave_addr, int offset, int data); +/** + * Read, modify, write an i2c register to the slave at 7-bit slave address + * , at the specified 8-bit in the slave's + * address space. The will specify whether this is setting the + * bit value(s) or clearing them. If the value to be written is the + * same as the original value of the register, the write will not be + * performed. + */ +int i2c_update8(const int port, + const uint16_t slave_addr_flags, + const int offset, + const uint8_t mask, + const enum mask_update_action action); + +int i2c_update16(const int port, + const uint16_t slave_addr_flags, + const int offset, + const uint16_t mask, + const enum mask_update_action action); + +/** + * Read, modify, write field of an i2c register to the slave at 7-bit + * slave address , at the specified 8-bit in + * the slave's address space. The register will be read, the + * will be cleared and then the will be set. The field mask + * and the set value do not have to be in the same bit locations. If the + * new value is not the same as the original value, the new value will be + * written back out to the device, otherwise no write will be performed. + */ +int i2c_field_update8(const int port, + const uint16_t slave_addr_flags, + const int offset, + const uint8_t field_mask, + const uint8_t set_value); + +int i2c_field_update16(const int port, + const uint16_t slave_addr_flags, + const int offset, + const uint16_t field_mask, + const uint16_t set_value); + /** * @return non-zero if i2c bus is busy */ -- cgit v1.2.1