/* 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" #define CPRINTF(format, args...) cprintf(CC_I2C, format, ## args) /* Write 2 bytes using smbus word access protocol */ int smbus_write_word(uint8_t i2c_port, uint8_t slave_addr, uint8_t smbus_cmd, uint16_t d16) { uint8_t buf[5]; /* Command sequence for CRC calculation */ buf[0] = slave_addr; buf[1] = smbus_cmd; buf[2] = d16 & 0xff; buf[3] = (d16 >> 8) & 0xff; buf[4] = crc8(buf, 4); return i2c_xfer(i2c_port, slave_addr, buf + 1, 4, NULL, 0); } /* Write up to SMBUS_MAX_BLOCK_SIZE bytes using smbus block access protocol */ int smbus_write_block(uint8_t i2c_port, uint8_t slave_addr, uint8_t smbus_cmd, uint8_t *data, uint8_t len) { uint8_t buf[3]; int rv; /* Command sequence for CRC calculation */ buf[0] = slave_addr; buf[1] = smbus_cmd; buf[2] = len; i2c_lock(i2c_port, 1); /* Send command + length */ rv = i2c_xfer_unlocked(i2c_port, slave_addr, buf + 1, 2, NULL, 0, I2C_XFER_START); if (rv != EC_SUCCESS) goto smbus_write_block_done; /* Send data */ rv = i2c_xfer_unlocked(i2c_port, slave_addr, data, len, NULL, 0, 0); if (rv != EC_SUCCESS) goto smbus_write_block_done; /* Send CRC */ buf[0] = crc8(buf, 3); buf[0] = crc8_arg(data, len, buf[0]); rv = i2c_xfer_unlocked(i2c_port, slave_addr, buf, 1, NULL, 0, I2C_XFER_STOP); smbus_write_block_done: i2c_lock(i2c_port, 0); return rv; } /* Read 2 bytes using smbus word access protocol */ int smbus_read_word(uint8_t i2c_port, uint8_t slave_addr, uint8_t smbus_cmd, uint16_t *p16) { uint8_t buf[3]; int rv; uint8_t crc; /* Command sequence for CRC calculation */ buf[0] = slave_addr; buf[1] = smbus_cmd; buf[2] = slave_addr | 0x1; crc = crc8(buf, 3); /* Read data bytes + CRC byte */ rv = i2c_xfer(i2c_port, slave_addr, &smbus_cmd, 1, buf, 3); /* Verify CRC */ if (crc8_arg(buf, 2, crc) != buf[2]) rv = EC_ERROR_CRC; if (rv == EC_SUCCESS) *p16 = (buf[1] << 8) | buf[0]; else *p16 = 0; return rv; } /* Read up to SMBUS_MAX_BLOCK_SIZE bytes using smbus block access protocol */ int smbus_read_block(uint8_t i2c_port, uint8_t slave_addr, uint8_t smbus_cmd, uint8_t *data, uint8_t *plen) { int rv, read_len; uint8_t buf[4]; uint8_t crc; int do_crc = 1; /* Command sequence for CRC calculation */ buf[0] = slave_addr; buf[1] = smbus_cmd; buf[2] = slave_addr | 0x1; i2c_lock(i2c_port, 1); /* First read size from slave */ rv = i2c_xfer_unlocked(i2c_port, slave_addr, &smbus_cmd, 1, buf + 3, 1, I2C_XFER_START); if (rv != EC_SUCCESS) goto smbus_read_block_done; crc = crc8(buf, 4); /* * If our input buffer isn't large enough to hold the entire input, * don't bother verifying crc since it may require reading numerous * data bytes that will be thrown away. */ read_len = MIN(buf[3], SMBUS_MAX_BLOCK_SIZE); if (*plen < read_len) { do_crc = 0; read_len = *plen; } /* Now read back all bytes */ rv = i2c_xfer_unlocked(i2c_port, slave_addr, NULL, 0, data, read_len, 0); if (rv) goto smbus_read_block_done; /* Read CRC + verify */ rv = i2c_xfer_unlocked(i2c_port, slave_addr, NULL, 0, buf, 1, I2C_XFER_STOP); if (do_crc && crc8_arg(data, read_len, crc) != buf[0]) rv = EC_ERROR_CRC; smbus_read_block_done: if (rv != EC_SUCCESS) memset(data, 0x0, *plen); else *plen = read_len; i2c_lock(i2c_port, 0); 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; }