mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-29 18:11:05 +00:00
Re-write smbus driver to fix arbitrary length read and improve code organization. BUG=chromium:576911 BRANCH=None TEST=Manual on sentry. Verify that smbus communication with battery is functional. Change-Id: I63c4bc3df40755cd41b3d9956af0ab9d2145a253 Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/333787 Commit-Ready: Shawn N <shawnn@chromium.org> Tested-by: Shawn N <shawnn@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
172 lines
3.9 KiB
C
172 lines
3.9 KiB
C
/* 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];
|
|
int rv;
|
|
|
|
i2c_lock(i2c_port, 1);
|
|
|
|
/* 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);
|
|
rv = i2c_xfer(i2c_port, slave_addr,
|
|
buf + 1, 4, NULL, 0, I2C_XFER_SINGLE);
|
|
|
|
i2c_lock(i2c_port, 0);
|
|
return rv;
|
|
}
|
|
|
|
/* 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(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(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(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);
|
|
|
|
i2c_lock(i2c_port, 1);
|
|
|
|
/* Read data bytes + CRC byte */
|
|
rv = i2c_xfer(i2c_port, slave_addr,
|
|
&smbus_cmd, 1, buf, 3, I2C_XFER_SINGLE);
|
|
|
|
/* 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;
|
|
|
|
i2c_lock(i2c_port, 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(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(i2c_port, slave_addr, NULL, 0, data, read_len, 0);
|
|
if (rv)
|
|
goto smbus_read_block_done;
|
|
|
|
/* Read CRC + verify */
|
|
rv = i2c_xfer(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;
|
|
}
|