274 lines
8.9 KiB
C
274 lines
8.9 KiB
C
/*
|
|
* Copyright (c) 2023 HPMicro
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
*/
|
|
|
|
#include "hpm_smbus.h"
|
|
|
|
static uint8_t hpm_smbus_pec_crc8(uint8_t *data, uint32_t len);
|
|
|
|
|
|
hpm_stat_t hpm_smbus_master_write_byte(I2C_Type *ptr, uint8_t slave_address, uint8_t data)
|
|
{
|
|
hpm_stat_t stat;
|
|
uint8_t buf[3];
|
|
/* addr + rw bit*/
|
|
buf[0] = slave_address << 1;
|
|
buf[1] = data;
|
|
buf[2] = hpm_smbus_pec_crc8(buf, 2);
|
|
stat = i2c_master_write(ptr, (const uint16_t)slave_address, &buf[1], sizeof(buf) - 1);
|
|
return stat;
|
|
}
|
|
|
|
hpm_stat_t hpm_smbus_master_read_byte(I2C_Type *ptr, uint8_t slave_address, uint8_t *data)
|
|
{
|
|
uint8_t buf[3];
|
|
hpm_stat_t stat;
|
|
uint8_t pec;
|
|
/* addr + rw bit*/
|
|
buf[0] = (slave_address << 1);
|
|
stat = i2c_master_read(ptr, (const uint16_t)slave_address, &buf[1], sizeof(buf) - 1);
|
|
if (stat == status_success) {
|
|
pec = hpm_smbus_pec_crc8(buf, sizeof(buf) - 1);
|
|
if (pec == buf[2]) {
|
|
*data = buf[1];
|
|
} else {
|
|
stat = status_fail;
|
|
}
|
|
}
|
|
return stat;
|
|
}
|
|
|
|
hpm_stat_t hpm_smbus_master_write_byte_in_command(I2C_Type *ptr, uint8_t slave_address, uint8_t command, uint8_t data)
|
|
{
|
|
hpm_stat_t stat;
|
|
uint8_t buf[4];
|
|
/* addr + rw bit*/
|
|
buf[0] = slave_address << 1;
|
|
buf[1] = command;
|
|
buf[2] = data;
|
|
buf[3] = hpm_smbus_pec_crc8(buf, sizeof(buf) - 1);
|
|
stat = i2c_master_write(ptr, (const uint16_t)slave_address, &buf[1], sizeof(buf) - 1);
|
|
return stat;
|
|
}
|
|
|
|
hpm_stat_t hpm_smbus_master_write_word_in_command(I2C_Type *ptr, uint8_t slave_address, uint8_t command, uint16_t data)
|
|
{
|
|
hpm_stat_t stat;
|
|
uint8_t buf[5];
|
|
/* addr + rw bit*/
|
|
buf[0] = slave_address << 1;
|
|
buf[1] = command;
|
|
*(uint16_t *)(&buf[2]) = data;
|
|
buf[4] = hpm_smbus_pec_crc8(buf, sizeof(buf) - 1);
|
|
stat = i2c_master_write(ptr, (const uint16_t)slave_address, &buf[1], sizeof(buf) - 1);
|
|
return stat;
|
|
}
|
|
|
|
hpm_stat_t hpm_smbus_master_read_byte_in_command(I2C_Type *ptr, uint8_t slave_address, uint8_t command, uint8_t *data)
|
|
{
|
|
hpm_stat_t stat;
|
|
uint8_t pec;
|
|
uint8_t buf[5];
|
|
/* addr + rw bit*/
|
|
buf[0] = (slave_address << 1);
|
|
buf[1] = command;
|
|
/* write command code in smbus spec*/
|
|
stat = i2c_master_seq_transmit(ptr, (const uint16_t)slave_address, &command, sizeof(uint8_t), i2c_frist_frame);
|
|
if (stat == status_success) {
|
|
/* read */
|
|
buf[2] = (slave_address << 1) | 0x01;
|
|
/* now change dir,restart, read the byte */
|
|
stat = i2c_master_seq_receive(ptr, (const uint16_t)slave_address, &buf[3], 1, i2c_frist_frame);
|
|
/* read the pec */
|
|
stat = i2c_master_seq_receive(ptr, (const uint16_t)slave_address, &buf[4], 1, i2c_last_frame);
|
|
if (stat == status_success) {
|
|
pec = hpm_smbus_pec_crc8(buf, sizeof(buf) - 1);
|
|
if (pec == buf[4]) {
|
|
*data = buf[3];
|
|
} else {
|
|
stat = status_fail;
|
|
}
|
|
}
|
|
}
|
|
return stat;
|
|
}
|
|
|
|
hpm_stat_t hpm_smbus_master_read_word_in_command(I2C_Type *ptr, uint8_t slave_address, uint8_t command, uint16_t *data)
|
|
{
|
|
hpm_stat_t stat;
|
|
uint8_t pec;
|
|
uint8_t buf[6];
|
|
/* addr + rw bit*/
|
|
buf[0] = (slave_address << 1);
|
|
buf[1] = command;
|
|
/* write command code in smbus spec*/
|
|
stat = i2c_master_seq_transmit(ptr, (const uint16_t)slave_address, &command, sizeof(uint8_t), i2c_frist_frame);
|
|
if (stat == status_success) {
|
|
/* read */
|
|
buf[2] = (slave_address << 1) | 0x01;
|
|
/* now change dir,restart, read the word (16 bits)*/
|
|
stat = i2c_master_seq_receive(ptr, (const uint16_t)slave_address, &buf[3], 2, i2c_frist_frame);
|
|
/* read the pec */
|
|
stat = i2c_master_seq_receive(ptr, (const uint16_t)slave_address, &buf[5], 1, i2c_last_frame);
|
|
if (stat == status_success) {
|
|
pec = hpm_smbus_pec_crc8(buf, sizeof(buf) - 1);
|
|
if (pec == buf[5]) {
|
|
*data = *(uint16_t *)(&buf[3]);
|
|
} else {
|
|
stat = status_fail;
|
|
}
|
|
}
|
|
}
|
|
return stat;
|
|
}
|
|
|
|
hpm_stat_t hpm_smbus_master_write_block_in_command(I2C_Type *ptr, uint8_t slave_address, uint8_t command, uint8_t *data, uint32_t size)
|
|
{
|
|
hpm_stat_t stat;
|
|
uint8_t buf[I2C_SOC_TRANSFER_COUNT_MAX];
|
|
uint16_t buf_size;
|
|
/* frame included addr, command, data, and pec */
|
|
assert(size > 0 && size <= (I2C_SOC_TRANSFER_COUNT_MAX - 3));
|
|
/* addr + rw bit*/
|
|
buf[0] = slave_address << 1;
|
|
buf[1] = command;
|
|
buf[2] = size;
|
|
memcpy(&buf[3], data, size);
|
|
buf[size + 3] = hpm_smbus_pec_crc8(buf, size + 3);
|
|
buf_size = size + 4;
|
|
stat = i2c_master_write(ptr, (const uint16_t)slave_address, buf, buf_size);
|
|
return stat;
|
|
}
|
|
|
|
hpm_stat_t hpm_smbus_master_read_block_in_command(I2C_Type *ptr, uint8_t slave_address, uint8_t command, uint8_t *data, uint32_t size)
|
|
{
|
|
hpm_stat_t stat;
|
|
uint8_t pec;
|
|
uint8_t buf[I2C_SOC_TRANSFER_COUNT_MAX];
|
|
/* frame included addr, command, data, and pec */
|
|
assert(size > 0 && size <= (I2C_SOC_TRANSFER_COUNT_MAX - 3));
|
|
/* addr + rw bit*/
|
|
buf[0] = (slave_address << 1);
|
|
buf[1] = command;
|
|
/* write command code in smbus spec*/
|
|
stat = i2c_master_seq_transmit(ptr, (const uint16_t)slave_address, &command, sizeof(uint8_t), i2c_frist_frame);
|
|
/* read */
|
|
buf[2] = (slave_address << 1) | 0x01;
|
|
if (stat == status_success) {
|
|
/* now change dir,restart, read the block count*/
|
|
stat = i2c_master_seq_receive(ptr, (const uint16_t)slave_address, &buf[3], 1, i2c_frist_frame);
|
|
/* read data*/
|
|
stat = i2c_master_seq_receive(ptr, (const uint16_t)slave_address, &buf[4], size, i2c_next_frame);
|
|
/* read pec */
|
|
stat = i2c_master_seq_receive(ptr, (const uint16_t)slave_address, &buf[size + 4], 1, i2c_last_frame);
|
|
if (stat == status_success) {
|
|
pec = hpm_smbus_pec_crc8(buf, sizeof(buf) - 1);
|
|
if (pec == buf[size + 4]) {
|
|
memcpy(data, &buf[4], size);
|
|
} else {
|
|
stat = status_fail;
|
|
}
|
|
}
|
|
}
|
|
return stat;
|
|
}
|
|
|
|
hpm_stat_t hpm_smbus_master_write(I2C_Type *ptr, uint8_t slave_address, uint8_t *data, uint32_t size)
|
|
{
|
|
hpm_stat_t stat;
|
|
uint8_t buf[I2C_SOC_TRANSFER_COUNT_MAX];
|
|
uint16_t buf_size;
|
|
/* frame included addr, data, and pec */
|
|
assert(size > 0 && size <= (I2C_SOC_TRANSFER_COUNT_MAX - 1));
|
|
/* addr + rw bit*/
|
|
buf[0] = (slave_address << 1) | 0x01;
|
|
memcpy(&buf[1], data, size);
|
|
buf[size + 1] = hpm_smbus_pec_crc8(buf, size + 1);
|
|
buf_size = size + 1;
|
|
stat = i2c_master_write(ptr, (const uint16_t)slave_address, &buf[1], buf_size);
|
|
return stat;
|
|
}
|
|
|
|
hpm_stat_t hpm_smbus_master_read(I2C_Type *ptr, uint8_t slave_address, uint8_t *data, uint32_t size)
|
|
{
|
|
hpm_stat_t stat;
|
|
uint8_t pec;
|
|
uint8_t buf[I2C_SOC_TRANSFER_COUNT_MAX];
|
|
/* frame included addr, data, and pec */
|
|
assert(size > 0 && size <= (I2C_SOC_TRANSFER_COUNT_MAX - 2));
|
|
buf[0] = (slave_address << 1);
|
|
stat = i2c_master_read(ptr, slave_address, &buf[1], size + 1);
|
|
if (stat == status_success) {
|
|
pec = hpm_smbus_pec_crc8(buf, size + 1);
|
|
if (pec == buf[size + 1]) {
|
|
memcpy(data, &buf[1], size);
|
|
} else {
|
|
stat = status_fail;
|
|
}
|
|
}
|
|
return stat;
|
|
}
|
|
|
|
hpm_stat_t hpm_smbus_slave_write(I2C_Type *ptr, uint8_t *data, uint32_t size)
|
|
{
|
|
hpm_stat_t stat;
|
|
uint8_t buf[I2C_SOC_TRANSFER_COUNT_MAX];
|
|
uint16_t buf_size;
|
|
uint8_t slave_address;
|
|
/* frame included addr, data, and pec */
|
|
assert(size > 0 && size <= (I2C_SOC_TRANSFER_COUNT_MAX - 1));
|
|
slave_address = ptr->ADDR;
|
|
/* addr + rw bit*/
|
|
buf[0] = (slave_address << 1);
|
|
memcpy(&buf[1], data, size);
|
|
buf[size + 1] = hpm_smbus_pec_crc8(buf, size + 1);
|
|
buf_size = size + 1;
|
|
stat = i2c_slave_write(ptr, &buf[1], buf_size);
|
|
return stat;
|
|
}
|
|
|
|
hpm_stat_t hpm_smbus_slave_read(I2C_Type *ptr, uint8_t *data, uint32_t size)
|
|
{
|
|
hpm_stat_t stat;
|
|
uint8_t pec;
|
|
uint8_t buf[I2C_SOC_TRANSFER_COUNT_MAX];
|
|
uint8_t slave_address;
|
|
/* frame included addr, data, and pec */
|
|
assert(size > 0 && size <= (I2C_SOC_TRANSFER_COUNT_MAX - 2));
|
|
/* addr + rw bit*/
|
|
slave_address = ptr->ADDR;
|
|
buf[0] = (slave_address << 1) | 0x01;
|
|
stat = i2c_slave_read(ptr, &buf[1], size + 1);
|
|
if (stat == status_success) {
|
|
pec = hpm_smbus_pec_crc8(buf, size + 1);
|
|
if (pec == buf[size + 1]) {
|
|
memcpy(data, &buf[1], size);
|
|
} else {
|
|
stat = status_fail;
|
|
}
|
|
}
|
|
return stat;
|
|
}
|
|
|
|
static uint8_t hpm_smbus_pec_crc8(uint8_t *data, uint32_t len)
|
|
{
|
|
/* The PEC is a CRC-8 error-checking byte, calculated on all the message bytes (including addresses and read/write bits) */
|
|
uint32_t i;
|
|
uint8_t crc = 0x00;
|
|
while (len--) {
|
|
crc ^= *data++;
|
|
for (i = 0; i < 8; i++) {
|
|
if (crc & 0x80) {
|
|
crc = (crc << 1) ^ 0x07;
|
|
} else {
|
|
crc <<= 1;
|
|
}
|
|
}
|
|
}
|
|
return crc;
|
|
}
|
|
|