common/sensor/dev/pex89000.c (261 lines of code) (raw):
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "sensor.h"
#include "hal_i2c.h"
#define BRCM_I2C5_CMD_READ 0b100
#define BRCM_I2C5_CMD_WRITE 0b011
#define BRCM_I2C5_ACCESS_MODE_FULL 2
#define BRCM_I2C5_ACCESS_MODE_LOWER18 0
#define BRCM_CHIME_AXI_CSR_ADDR 0x001F0100
#define BRCM_CHIME_AXI_CSR_DATA 0x001F0104
#define BRCM_CHIME_AXI_CSR_CTL 0x001F0108
#define BRCM_TEMP_SENSOR0_CTL_REG1 0xFFE78504
#define BRCM_TEMP_SENSOR0_STAT_REG0 0xFFE78538
#define BRCM_TEMP_SENSOR0_CTL_REG1_RESET 0x000653E8
#define BRCM_FULL_ADDR_BIT 0x007C0000 // bits[22:18]
#define TEMP 0xFE // TBD: sensor offset
typedef struct {
uint8_t oft9_2bit : 8;
uint8_t oft11_10bit : 2;
uint8_t be : 4;
uint8_t reserve3 : 1;
uint8_t oft12bit : 1;
uint8_t oft17_13bit : 5;
uint8_t access_mode : 2;
uint8_t reserve2 : 1;
uint8_t cmd : 3;
uint8_t reserve1 : 5;
} __packed __aligned(4) HW_I2C_Cmd;
static void swap32(uint32_t *d)
{
if (!d)
return;
uint32_t t = ((*d >> 24) & 0xFF) | ((*d >> 8) & 0xFF00) | ((*d << 8) & 0xFF0000) |
((*d << 24) & 0xFF000000);
*d = t;
}
/*
* be: byte enables
* oft: Atlas register address
* cmd: read or write command
* access_mode: access mode
* data: DWORD data
* buf: encoded byte array to send to pesw
*/
static void pex89000_i2c_encode(uint32_t oft, uint8_t be, uint8_t cmd, uint8_t access_mode,
HW_I2C_Cmd *buf)
{
buf->reserve1 = 0;
buf->cmd = cmd;
buf->reserve2 = 0;
buf->access_mode = access_mode;
buf->oft17_13bit = (oft >> 13) & 0x1F;
buf->oft12bit = (oft >> 12) & 0x1;
buf->reserve3 = 0;
buf->be = be;
buf->oft11_10bit = (oft >> 10) & 0x3;
buf->oft9_2bit = (oft >> 2) & 0xFF;
}
static uint8_t set_full_addr(uint8_t bus, uint8_t addr, uint32_t oft)
{
/* I2C write command to set address bits[22:18] in 0x2CC[4:0] */
uint8_t sub_oft = (uint8_t)(oft & BRCM_FULL_ADDR_BIT) >> 18; // sub_oft: bit[22:18]
HW_I2C_Cmd cmd;
pex89000_i2c_encode(0x2CC, 0xF, BRCM_I2C5_CMD_WRITE, BRCM_I2C5_ACCESS_MODE_LOWER18, &cmd);
uint8_t data[8] = { 0 };
memcpy(&data[0], &cmd, sizeof(cmd));
memcpy(&data[7], &sub_oft, sizeof(sub_oft));
uint8_t retry = 5;
I2C_MSG msg;
msg.bus = bus;
msg.slave_addr = addr;
msg.tx_len = sizeof(data);
memcpy(&msg.data[0], data, sizeof(data));
if (i2c_master_write(&msg, retry)) {
/* write fail */
printf("set AXI register to full mode failed!\n");
return 1;
}
return 0;
}
static uint8_t pex89000_chime_read(uint8_t bus, uint8_t addr, uint32_t oft, uint8_t *resp,
uint16_t resp_len)
{
if (set_full_addr(bus, addr, oft)) // fail to set full addr
return 1;
HW_I2C_Cmd cmd;
pex89000_i2c_encode(oft, 0xF, BRCM_I2C5_CMD_READ, BRCM_I2C5_ACCESS_MODE_FULL, &cmd);
uint8_t retry = 5;
I2C_MSG msg;
msg.bus = bus;
msg.slave_addr = addr;
msg.tx_len = sizeof(cmd);
msg.rx_len = resp_len;
memcpy(&msg.data[0], &cmd, sizeof(cmd));
if (i2c_master_read(&msg, retry)) {
/* read fail */
printf("pex89000 read failed!!\n");
return 1;
}
memcpy(resp, &msg.data[0], resp_len);
return 0;
}
static uint8_t pex89000_chime_write(uint8_t bus, uint8_t addr, uint32_t oft, uint8_t *data,
uint8_t data_len)
{
if (set_full_addr(bus, addr, oft))
return 1;
HW_I2C_Cmd cmd;
pex89000_i2c_encode(oft, 0xF, BRCM_I2C5_CMD_WRITE, BRCM_I2C5_ACCESS_MODE_FULL, &cmd);
/* This buffer is for PEC calculated and write to switch by I2C */
uint8_t *data_buf = (uint8_t *)malloc(sizeof(cmd) + data_len);
if (!data_buf)
return 1;
memcpy(data_buf, &cmd, sizeof(cmd));
memcpy(data_buf + sizeof(cmd), data, data_len);
uint8_t retry = 5;
I2C_MSG msg;
msg.bus = bus;
msg.slave_addr = addr;
msg.tx_len = sizeof(cmd) + data_len;
memcpy(&msg.data[0], data_buf, sizeof(cmd) + data_len);
if (i2c_master_write(&msg, retry)) {
/* write fail */
printf("pex89000 write failed!!\n");
free(data_buf);
return 1;
}
free(data_buf);
return 0;
}
static uint8_t pend_for_read_valid(uint8_t bus, uint8_t addr)
{
uint8_t rty = 50;
uint32_t resp = 0;
while (rty--) {
if (pex89000_chime_read(bus, addr, BRCM_CHIME_AXI_CSR_CTL, (uint8_t *)&resp,
sizeof(resp))) {
k_msleep(10);
continue;
}
if ((resp >> 24) & 0x8) { // CHIME_to_AXI_CSR Control Status -> Read_data_vaild
return 0; // success
}
k_msleep(10);
}
return 1;
}
static uint8_t pex89000_chime_to_axi_write(uint8_t bus, uint8_t addr, uint32_t oft, uint32_t data)
{
uint32_t wbuf = oft;
swap32(&wbuf);
if (pex89000_chime_write(bus, addr, BRCM_CHIME_AXI_CSR_ADDR, (uint8_t *)&wbuf,
sizeof(wbuf))) {
return 1;
}
wbuf = data;
swap32(&wbuf);
if (pex89000_chime_write(bus, addr, BRCM_CHIME_AXI_CSR_DATA, (uint8_t *)&wbuf,
sizeof(wbuf))) {
return 1;
}
wbuf = 0x1; // CHIME_to_AXI_CSR Control Status: write command
swap32(&wbuf);
if (pex89000_chime_write(bus, addr, BRCM_CHIME_AXI_CSR_CTL, (uint8_t *)&wbuf,
sizeof(wbuf))) {
return 1;
}
return 0;
}
static uint8_t pex89000_chime_to_axi_read(uint8_t bus, uint8_t addr, uint32_t oft, uint32_t *resp)
{
uint32_t data = oft;
swap32(&data);
if (pex89000_chime_write(bus, addr, BRCM_CHIME_AXI_CSR_ADDR, (uint8_t *)&data,
sizeof(data))) {
return 1;
}
data = 0x2; // CHIME_to_AXI_CSR Control Status: read command
swap32(&data);
if (pex89000_chime_write(bus, addr, BRCM_CHIME_AXI_CSR_CTL, (uint8_t *)&data,
sizeof(data))) {
return 1;
}
k_msleep(10);
if (pend_for_read_valid(bus, addr)) {
printf("read data invaild\n");
return 1;
}
if (pex89000_chime_read(bus, addr, BRCM_CHIME_AXI_CSR_DATA, (uint8_t *)resp,
sizeof(resp))) {
return 1;
}
swap32(resp);
return 0;
}
uint8_t pex89000_die_temp(uint8_t bus, uint8_t addr, sensor_val *val)
{
if (!val)
return 1;
uint32_t resp = 0;
//check 0xFFE78504 value
if (!pex89000_chime_to_axi_read(bus, addr, BRCM_TEMP_SENSOR0_CTL_REG1, &resp)) {
if (resp != BRCM_TEMP_SENSOR0_CTL_REG1_RESET) {
printf("ADC temperature control register1 check fail!\n");
return 1;
}
resp = 0;
} else {
printf("CHIME to AXI Read 0xFFE78504 fail!\n");
return 1;
}
//Write 0xFFE78504 = 200653E8
if (pex89000_chime_to_axi_write(bus, addr, BRCM_TEMP_SENSOR0_CTL_REG1,
BRCM_TEMP_SENSOR0_CTL_REG1_RESET)) {
printf("CHIME to AXI Write 0xFFE78504 fail!\n");
return 1;
}
//Read 0xFFE78538
if (!pex89000_chime_to_axi_read(bus, addr, BRCM_TEMP_SENSOR0_STAT_REG0, &resp)) {
float tmp = (resp & 0xFFFF) / 128;
val->integer = (int)tmp;
val->fraction = (int)(tmp * 1000) % 1000;
return 0;
}
return 1;
}
uint8_t pex89000_read(uint8_t sensor_num, int *reading)
{
if (reading == NULL)
return SENSOR_UNSPECIFIED_ERROR;
pex89000_init_arg *init_arg =
(pex89000_init_arg *)sensor_config[SensorNum_SensorCfg_map[sensor_num]].init_args;
uint8_t rc = SENSOR_UNSPECIFIED_ERROR;
int ret = k_mutex_lock(&init_arg->brcm_pciesw, K_MSEC(5000)); // TBD: timeout 5s
if (ret) {
printk("pex89000_die_temp: mutex[%d] get fail status: %x\n", init_arg->idx, ret);
return rc;
}
switch (sensor_config[SensorNum_SensorCfg_map[sensor_num]].offset) {
case TEMP:
if (pex89000_die_temp(sensor_config[SensorNum_SensorCfg_map[sensor_num]].port,
sensor_config[SensorNum_SensorCfg_map[sensor_num]].slave_addr,
(sensor_val *)reading)) {
printf("sensor pex89000_read read temp fail!\n");
rc = SENSOR_FAIL_TO_ACCESS;
goto exit;
}
break;
default:
printf("sensor pex89000_read type fail!\n");
rc = SENSOR_UNSPECIFIED_ERROR;
goto exit;
}
rc = SENSOR_READ_SUCCESS;
exit:
k_mutex_unlock(&init_arg->brcm_pciesw);
return rc;
}
bool pex89000_init(uint8_t sensor_num)
{
sensor_config[SensorNum_SensorCfg_map[sensor_num]].read = pex89000_read;
if (sensor_config[SensorNum_SensorCfg_map[sensor_num]].init_args == NULL) {
printk("pex89000_init: init_arg is NULL\n");
return SENSOR_INIT_UNSPECIFIED_ERROR;
}
pex89000_init_arg *init_arg =
(pex89000_init_arg *)sensor_config[SensorNum_SensorCfg_map[sensor_num]].init_args;
if (k_mutex_init(&init_arg->brcm_pciesw)) {
printf("pex89000 mutex %d init fail\n", init_arg->idx);
init_arg->is_init = false;
return SENSOR_INIT_UNSPECIFIED_ERROR;
}
init_arg->is_init = true;
return SENSOR_INIT_SUCCESS;
}