“/*
* Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2022-2022. All rights reserved.
* Description: pmbus v200 物理层实现源文件
* Create: 2022/05/31
*/
#include <linux/delay.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/types.h>
#include <linux/bitfield.h>
#include "hisi_pmbus.h"
#define STANDARD_MODE 1
#define FAST_MODE 2
#define GPIO_LEVEL_LOW 0
#define GPIO_LEVEL_HIGH 1
#define SET_LOW_HIGH_TIMES 9
#define AVS_WR_OPEN_REG 0x0004
#define AVS_INT_STATUS_REG 0x0008
#define AVS_INT_CLEAR_REG 0x0020
#define TRIGGER_CFG_REG 0x00B8
#define PMBUS_DISABLE 0x00
#define PMBUS_ENABLE 0x01
/* PMBUSIF_REG_GEN Base address of Module's Register */
#define PMBUSIF_REG_GEN_BASE (0x800)
#define I2C_CON_REG (PMBUSIF_REG_GEN_BASE + 0x0) /* I2C控制寄存器。 */
#define I2C_CON_MASTER_ENABLE BIT(0)
#define I2C_CON_SPEED_MASK (0x6U)
#define I2C_CON_RESTART_EN BIT(5)
#define I2C_CON_SLAVE_DISABLE BIT(6)
#define I2C_DATA_CMD_REG (PMBUSIF_REG_GEN_BASE + 0x10) /* I2C数据操作寄存器。 */
#define I2C_SS_SCL_HCNT_REG (PMBUSIF_REG_GEN_BASE + 0x14) /* I2C标准速度模式SCL高电平配置寄存器。 */
#define I2C_SS_SCL_LCNT_REG (PMBUSIF_REG_GEN_BASE + 0x18) /* I2C标准速度模式SCL低电平配置寄存器。 */
#define I2C_FS_SCL_HCNT_REG (PMBUSIF_REG_GEN_BASE + 0x1C) /* I2C快速模式SCL高电平配置寄存器。 */
#define I2C_FS_SCL_LCNT_REG (PMBUSIF_REG_GEN_BASE + 0x20) /* I2C快速模式SCL低电平配置寄存器。 */
#define I2C_INTR_STAT_REG (PMBUSIF_REG_GEN_BASE + 0x2C) /* I2C屏蔽后中断状态寄存器。 */
#define I2C_INTR_MASK_REG (PMBUSIF_REG_GEN_BASE + 0x30) /* I2C中断屏蔽寄存器。 */
#define I2C_INTR_RAW_REG (PMBUSIF_REG_GEN_BASE + 0x34) /* I2C原始中断状态寄存器。 */
#define I2C_INTR_RAW_TX_ABRT BIT(6)
#define I2C_INTR_RAW_ALERT_DET BIT(12)
#define I2C_INTR_RAW_SCL_LOW_TOUT BIT(15)
#define I2C_INTR_RAW_PMBUS_CMD_FINISH BIT(17)
#define I2C_ENABLE_REG (PMBUSIF_REG_GEN_BASE + 0x6C) /* I2C工作使能寄存器。 */
#define I2C_STATUS_REG (PMBUSIF_REG_GEN_BASE + 0x70) /* I2C状态寄存器。 */
#define I2C_RXFLR_REG (PMBUSIF_REG_GEN_BASE + 0x78) /* RX_FIFO有效数据指示寄存器。 */
#define I2C_SDA_HOLD_REG (PMBUSIF_REG_GEN_BASE + 0x7C) /* SDA保持时间配置寄存器。 */
#define I2C_ENABLE_STATUS_REG (PMBUSIF_REG_GEN_BASE + 0x9C) /* I2C状态寄存器。 */
#define I2C_SCL_SWITCH_REG (PMBUSIF_REG_GEN_BASE + 0xA0) /* I2C防挂死SCL使能寄存器。 */
#define I2C_SCL_SIM_REG (PMBUSIF_REG_GEN_BASE + 0xA4) /* I2C防挂死SCL模拟寄存器。 */
#define I2C_LOCK_REG (PMBUSIF_REG_GEN_BASE + 0xAC) /* I2C lock寄存器。 */
#define I2C_SDA_SWITCH_REG (PMBUSIF_REG_GEN_BASE + 0xB0) /* I2C防挂死SDA使能寄存器。 */
#define I2C_SDA_SIM_REG (PMBUSIF_REG_GEN_BASE + 0xB4) /* I2C防挂死SDA模拟寄存器。 */
#define I2C_PMBUS_CTRL_REG (PMBUSIF_REG_GEN_BASE + 0x104) /* PMBUS全局控制寄存器。 */
#define I2C_PMBUS_CTRL_PEC_EN BIT(2)
#define I2C_PMBUS_CTRL_ALERT_EN BIT(1)
#define I2C_LOW_TIMEOUT_REG (PMBUSIF_REG_GEN_BASE + 0x108) /* SCL低电平超时值配置寄存器。 */
#define I2C_PMBUS_SCL_DET_REG (PMBUSIF_REG_GEN_BASE + 0x12C) /* PMBUS SCL检测寄存器。 */
#define I2C_PMBUS_SCL_DET_IDLE_DET_EN BIT(0)
#define I2C_PMBUS_SCL_DET_TIMEOUT_EN BIT(1)
#define I2C_PMBUS_IDLECNT_REG (PMBUSIF_REG_GEN_BASE + 0x130) /* SCL高电平空闲值配置寄存器。 */
#define I2C_PMBUS_RST_REG (PMBUSIF_REG_GEN_BASE + 0x134) /* 软件复位配置寄存器。 */
/* PMBUS_PROC_REG_GEN Base address of Module's Register */
#define PMBUS_PROC_REG_GEN_BASE (0xA00)
#define PMBUS_WR_OPEN_REG (PMBUS_PROC_REG_GEN_BASE + 0x4) /* PMBUS全局参数保护寄存器。 */
#define PMBUS_INT_CLR_REG (PMBUS_PROC_REG_GEN_BASE + 0x10) /* PMBUS中断清除寄存器 */
#define PMBUS_WAIT_CNT 30000
/* PMU CMD */
#define STOP_EN (1U << 10)
#define ADDR_EN (1U << 9)
#define CMD_READ (1U << 8)
#define SDA_IN BIT(9)
#define PMBUS_I2C_RECOVERY_CYCLE_CNT 10
static inline void pmbus_reg_write(struct io_region *reg_region, u32 reg, u32 val)
{
pr_debug("[iWare][Debug] %s reg=%#x val =%#x\r\n", __FUNCTION__, reg, val);
iowrite32(val, reg_region->io_base + reg);
}
static inline u32 pmbus_reg_read(struct io_region *reg_region, u32 reg)
{
u32 val;
val = ioread32(reg_region->io_base + reg);
pr_debug("[iWare][Debug] %s reg=%#x val =%#x\r\n", __FUNCTION__, reg, val);
return val;
}
/* try to recovery the bus if sda locked to low level */
static void pmbus_recovery_bus(struct io_region *reg_region)
{
int i;
u32 status;
status = pmbus_reg_read(reg_region, I2C_STATUS_REG);
/* if SDA keep low, assume the bus hang up */
if ((status & SDA_IN) == 0) {
/* disable pmbus */
pmbus_reg_write(reg_region, I2C_ENABLE_REG, 0x0);
/* enable output software simulaition */
pmbus_reg_write(reg_region, I2C_SCL_SWITCH_REG, 0x1);
pmbus_reg_write(reg_region, I2C_SDA_SWITCH_REG, 0x1);
/* output at least 9 clocks to try to recover the bus */
for (i = 0; i < PMBUS_I2C_RECOVERY_CYCLE_CNT; i++) {
pmbus_reg_write(reg_region, I2C_SCL_SIM_REG, 0x0);
udelay(50); // 延时50us
pmbus_reg_write(reg_region, I2C_SCL_SIM_REG, 0x1);
udelay(50); // 延时50us
}
/* disable output software simulaition */
pmbus_reg_write(reg_region, I2C_SCL_SWITCH_REG, 0x0);
pmbus_reg_write(reg_region, I2C_SDA_SWITCH_REG, 0x0);
/* enable pmbus */
pmbus_reg_write(reg_region, I2C_ENABLE_REG, 0x1);
pr_info("[iWare][Info] pmbus hang recovery done\n");
}
}
static int pmbus_wait_write_finish(struct io_region *reg_region)
{
int i;
u32 status = 0;
for (i = 0; i < PMBUS_WAIT_CNT; i++) {
status = pmbus_reg_read(reg_region, I2C_INTR_RAW_REG);
if (((status & I2C_INTR_RAW_SCL_LOW_TOUT) == 0) &&
((status & I2C_INTR_RAW_TX_ABRT) == 0) &&
((status & I2C_INTR_RAW_PMBUS_CMD_FINISH) != 0)) {
// 清除所有中断
pmbus_reg_write(reg_region, I2C_INTR_RAW_REG, 0xffffffff);
return 0;
}
udelay(1);
}
pr_err("[iWare][Error] pmbus_write timeout! raw_int_status:0x%x\n", status);
// 清除所有中断
pmbus_reg_write(reg_region, I2C_INTR_RAW_REG, 0xffffffff);
pmbus_recovery_bus(reg_region);
return -EBUSY;
}
static int pmbus_send_byte_v200(struct io_region *reg_region, struct pmbus_msg *msg)
{
pmbus_reg_write(reg_region, I2C_DATA_CMD_REG, ADDR_EN | msg->slave_addr);
if ((msg->type & PMBUS_FLAG_EXT) != 0) {
pmbus_reg_write(reg_region, I2C_DATA_CMD_REG, STOP_EN | msg->command);
pmbus_reg_write(reg_region, I2C_DATA_CMD_REG, STOP_EN | msg->command_ext);
} else {
pmbus_reg_write(reg_region, I2C_DATA_CMD_REG, STOP_EN | msg->command);
}
return pmbus_wait_write_finish(reg_region);
}
static int pmbus_write_bytes_v200(struct io_region *reg_region, struct pmbus_msg *msg)
{
unsigned int i;
pmbus_reg_write(reg_region, I2C_DATA_CMD_REG, ADDR_EN | msg->slave_addr);
pmbus_reg_write(reg_region, I2C_DATA_CMD_REG, msg->command);
if ((msg->type & PMBUS_FLAG_EXT) != 0) {
pmbus_reg_write(reg_region, I2C_DATA_CMD_REG, msg->command_ext);
}
for (i = 0; i < msg->data_len - 1; i++) {
pmbus_reg_write(reg_region, I2C_DATA_CMD_REG, msg->data[i]);
}
pmbus_reg_write(reg_region, I2C_DATA_CMD_REG, STOP_EN | msg->data[msg->data_len - 1]);
return pmbus_wait_write_finish(reg_region);
}
#define PMBUS_READ_WAIT_TIMEOUT 1000000ULL
#define PMUBS_READ_WAIT_DELAY_US 1UL
static int pmbus_wait_read_finish(struct io_region *reg_region, u8 read_len)
{
int ret;
u32 data_num;
ret = readl_poll_timeout(reg_region->io_base + I2C_RXFLR_REG, data_num, (data_num >= read_len),
PMUBS_READ_WAIT_DELAY_US, PMBUS_READ_WAIT_TIMEOUT);
if (ret != 0) {
pr_err("[iWare][Error] wait read_finish timeout!! read_len[%u] fifo num[%u], raw_int_status:0x%x\n", read_len,
data_num, pmbus_reg_read(reg_region, I2C_INTR_RAW_REG));
// 清除所有中断
pmbus_reg_write(reg_region, I2C_INTR_RAW_REG, 0xffffffff);
pmbus_recovery_bus(reg_region);
return ret;
}
return 0;
}
static void pmbus_clear_rx_fifo(struct io_region *reg_region)
{
u8 rx_fifo_data_num;
u8 i;
u32 tmp;
/* clean rx fifo */
rx_fifo_data_num = (u8)pmbus_reg_read(reg_region, I2C_RXFLR_REG);
for (i = 0; i < rx_fifo_data_num; i++) {
tmp = pmbus_reg_read(reg_region, I2C_DATA_CMD_REG); // 把fifo读清
}
}
static int pmbus_read_bytes_v200(struct io_region *reg_region, struct pmbus_msg *msg)
{
int ret;
unsigned int i;
u32 status;
/* 先把rx fifo读清 */
pmbus_clear_rx_fifo(reg_region);
pmbus_reg_write(reg_region, I2C_DATA_CMD_REG, ADDR_EN | msg->slave_addr);
pmbus_reg_write(reg_region, I2C_DATA_CMD_REG, msg->command);
if ((msg->type & PMBUS_FLAG_EXT) != 0) { // 扩展16bit 命令
pmbus_reg_write(reg_region, I2C_DATA_CMD_REG, msg->command_ext);
}
pmbus_reg_write(reg_region, I2C_DATA_CMD_REG, ADDR_EN | CMD_READ | msg->slave_addr);
for (i = 0; i < msg->data_len - 1; i++) {
pmbus_reg_write(reg_region, I2C_DATA_CMD_REG, CMD_READ);
}
pmbus_reg_write(reg_region, I2C_DATA_CMD_REG, STOP_EN | CMD_READ);
ret = pmbus_wait_read_finish(reg_region, msg->data_len);
if (ret != 0) {
pr_err("[iWare][Error] %s slave_addr(0x%x), cmd(0x%x), time out\n", __func__, msg->slave_addr, msg->command);
pmbus_recovery_bus(reg_region);
return -EAGAIN;
}
for (i = 0; i < msg->data_len; i++) {
msg->data[i] = (u8)pmbus_reg_read(reg_region, I2C_DATA_CMD_REG);
}
/* for block read, first read code is data length */
if ((msg->type & PMBUS_FLAG_BLOCK) != 0) {
if (msg->data[0] > msg->data_len) {
pr_info("[iWare][Info] pmbus read slave[0x%02x] command[0x%02x] block data may lossed, toalLen[%u]\n",
msg->slave_addr, msg->command, msg->data[0]);
}
}
// 清中断
status = pmbus_reg_read(reg_region, I2C_INTR_RAW_REG);
pmbus_reg_write(reg_region, I2C_INTR_RAW_REG, status);
return 0;
}
static int pmbus_reset_v200(struct io_region *reg_region)
{
pmbus_reg_write(reg_region, I2C_PMBUS_RST_REG, 1);
udelay(1);
pmbus_reg_write(reg_region, I2C_PMBUS_RST_REG, 0);
udelay(1);
return 0;
}
/* 不同soc解锁码不同, chip dtsi里配置
309a 1260
avs_wr_unlock_key 0x5a5a5a5a 0x1ACCE551
pmbus_wr_unlock_key 0x5a5a5a5a 0x1ACCE551
i2c_unlock_key 0x5a5a5a5a 0x36313832
*/
static void pmbus_unlock_reg(struct io_region *reg_region, struct pmbus_unlock_key *key)
{
/* unlock avs wr */
pmbus_reg_write(reg_region, AVS_WR_OPEN_REG, key->avs_wr_unlock_key);
/* unlock pmbus wr */
pmbus_reg_write(reg_region, PMBUS_WR_OPEN_REG, key->pmbus_wr_unlock_key);
/* unlock pmbus i2c wr */
pmbus_reg_write(reg_region, I2C_LOCK_REG, key->i2c_unlock_key);
}
static void pmbus_config_timing_cnt(struct io_region *reg_region, u8 speed_mode, struct pmbus_timings_cfg *pmbus_cfg)
{
if (speed_mode == STANDARD_MODE) { // 标准模式
pmbus_reg_write(reg_region, I2C_SS_SCL_LCNT_REG, pmbus_cfg->lcnt);
pmbus_reg_write(reg_region, I2C_SS_SCL_HCNT_REG, pmbus_cfg->hcnt);
} else { // 快速模式
pmbus_reg_write(reg_region, I2C_FS_SCL_LCNT_REG, pmbus_cfg->lcnt);
pmbus_reg_write(reg_region, I2C_FS_SCL_HCNT_REG, pmbus_cfg->hcnt);
}
// 屏蔽所有中断
pmbus_reg_write(reg_region, I2C_INTR_MASK_REG, 0xFFFFFFFF);
pmbus_reg_write(reg_region, I2C_SDA_HOLD_REG, pmbus_cfg->hold_cnt);
// PMBus的SCL低电平超时值(PMBus协议规定为25~35ms)
pmbus_reg_write(reg_region, I2C_LOW_TIMEOUT_REG, pmbus_cfg->timeout_cnt); // 默认30ms
// PMBus的SCL高电平空闲值(PMBus协议规定为>50us)
pmbus_reg_write(reg_region, I2C_PMBUS_IDLECNT_REG, pmbus_cfg->idle_cnt); // 默认100us
}
static int pmbus_init_v200(struct io_region *reg_region, u32 pec_en, u32 bus_freq_hz, struct pmbus_timings_cfg *cfg,
struct pmbus_unlock_key *key)
{
u32 val = 0;
u8 speed_mode;
int ret;
if (bus_freq_hz > PMBUS_MAX_FAST_MODE_FREQ) {
pr_err("[iWare][Error] invalid para bus_freq_hz =%u\r\n", bus_freq_hz);
return -EINVAL;
}
ret = pmbus_reset_v200(reg_region);
if (ret != 0) {
return ret;
}
/* unlock */
pmbus_unlock_reg(reg_region, key);
/* stop triger */
pmbus_reg_write(reg_region, TRIGGER_CFG_REG, 0x0);
/* disable pmbus */
pmbus_reg_write(reg_region, I2C_ENABLE_REG, PMBUS_DISABLE);
speed_mode = (bus_freq_hz <= PMBUS_MAX_STANDARD_MODE_FREQ) ? STANDARD_MODE : FAST_MODE;
val |= I2C_CON_MASTER_ENABLE | I2C_CON_SLAVE_DISABLE | I2C_CON_RESTART_EN;
val |= (u32)FIELD_PREP(I2C_CON_SPEED_MASK, speed_mode);
pmbus_reg_write(reg_region, I2C_CON_REG, val);
pmbus_config_timing_cnt(reg_region, speed_mode, cfg);
/* config scl detect */
pmbus_reg_write(reg_region, I2C_PMBUS_SCL_DET_REG, I2C_PMBUS_SCL_DET_IDLE_DET_EN | I2C_PMBUS_SCL_DET_TIMEOUT_EN);
/* enable pec and alert */
val = I2C_PMBUS_CTRL_ALERT_EN;
if (pec_en != 0) {
pr_info("[iWare][Info] pmbus enable pec \r\n");
val |= I2C_PMBUS_CTRL_PEC_EN;
}
pmbus_reg_write(reg_region, I2C_PMBUS_CTRL_REG, val);
/* enable pmbus */
pmbus_reg_write(reg_region, I2C_ENABLE_REG, PMBUS_ENABLE);
// 清中断
pmbus_reg_write(reg_region, AVS_INT_CLEAR_REG, 0xFFFFFFFF);
pmbus_reg_write(reg_region, PMBUS_INT_CLR_REG, 0xFFFFFFFF);
pmbus_reg_write(reg_region, I2C_INTR_RAW_REG, 0xFFFFFFFF);
return 0;
}
const struct hisi_pmbus_ops hisi_pmbus_v200_ops = {
.init = pmbus_init_v200,
.reset = pmbus_reset_v200,
.send_byte = pmbus_send_byte_v200,
.read_bytes = pmbus_read_bytes_v200,
.write_bytes = pmbus_write_bytes_v200,
};
const struct hisi_pmbus_ops *hisi_pmbus_get_ops(void)
{
return &hisi_pmbus_v200_ops;
}
”
“int kdrv_pmbus_write_word(u32 bus_id, u8 slave_addr, u16 command, u16 data)
{
struct hisi_pmbus_data *pmbus = NULL;
struct pmbus_msg msg = { 0 };
int ret;
pmbus = hisi_pmbus_data_get_by_id(bus_id); if (pmbus == NULL) { pr_err("[iWare][Error] kdrv_pmbus_write_word,get pmbus data fail: pmbus_id=%u,\n", bus_id); return -EPERM; } mutex_lock(&pmbus->lock); msg.data = (u8 *)&data; msg.data_len = sizeof(u16); hisi_pmbus_fill_msg(&msg, PMBUS_WRITE_WORD, slave_addr, command); ret = hisi_pmbus_xfer(pmbus, &msg); if (ret != 0) { mutex_unlock(&pmbus->lock); return ret; } mutex_unlock(&pmbus->lock); return 0;
}
EXPORT_SYMBOL(kdrv_pmbus_write_word);”
“int test_kdrv_pmbus_write_word(u32 bus_id, u32 slave_addr, u32 cmd, u32 data)
{
int ret;
ret = kdrv_pmbus_write_word(bus_id, slave_addr, cmd, (u16)data); if (ret) { pr_err("kdrv_pmbus_write_word fail bus_id:%u, addr:%#x, cmd:%#x\n", bus_id, slave_addr, cmd); return ret; } pr_info("write word: bus_id:%#x, addr:%#x, cmd:%#x, data:%#x\n", bus_id, slave_addr, cmd, data); return 0;
}”
“static void pmbus_recovery_bus(struct io_region *reg_region)
{
int i;
u32 status;
status = pmbus_reg_read(reg_region, I2C_STATUS_REG); /* if SDA keep low, assume the bus hang up */ if ((status & SDA_IN) == 0) { /* disable pmbus */ pmbus_reg_write(reg_region, I2C_ENABLE_REG, 0x0); /* enable output software simulaition */ pmbus_reg_write(reg_region, I2C_SCL_SWITCH_REG, 0x1); pmbus_reg_write(reg_region, I2C_SDA_SWITCH_REG, 0x1); /* output at least 9 clocks to try to recover the bus */ for (i = 0; i < PMBUS_I2C_RECOVERY_CYCLE_CNT; i++) { pmbus_reg_write(reg_region, I2C_SCL_SIM_REG, 0x0); udelay(50); // 延时50us pmbus_reg_write(reg_region, I2C_SCL_SIM_REG, 0x1); udelay(50); // 延时50us } /* disable output software simulaition */ pmbus_reg_write(reg_region, I2C_SCL_SWITCH_REG, 0x0); pmbus_reg_write(reg_region, I2C_SDA_SWITCH_REG, 0x0); /* enable pmbus */ pmbus_reg_write(reg_region, I2C_ENABLE_REG, 0x1); pr_info("[iWare][Info] pmbus hang recovery done\n"); }
}”
“
[60585.219727] [iWare][Error] pmbus_write timeout! raw_int_status:0x10
[60585.227034] [iWare][Info] pmbus hang recovery done
[60585.231848] kdrv_pmbus_write_word fail bus_id:1, addr:0x70, cmd:0x21
请根据以上几段代码,帮我分析我的报错原因可能是什么,并给出完整解决方案”