#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/seq_file.h> // single_open
#include <linux/slab.h> // kfree kzalloc
#include <linux/proc_fs.h> // proc_creat
#include <linux/uaccess.h> // copy_from_user
#include <linux/version.h>
#include "kdrv_pmbus_api.h"
static int pmbus_test_show(struct seq_file *seq, void *arg)
{
return 0;
}
static int pmbus_test_open(struct inode *pnode, struct file *pfile)
{
return single_open(pfile, pmbus_test_show, NULL);
}
int test_kdrv_pmbus_write_block_data(u32 bus_id, u32 slave_addr, u32 cmd, u32 len)
{
u8 *buf = NULL;
int i;
u32 ret;
buf = (u8 *)kzalloc(len, GFP_KERNEL);
if (buf == NULL) {
pr_err("kzalloc err!\r\n");
return -ENOMEM;
}
buf[0] = len - 1;
for (i = 1; i < len; i++) {
buf[i] = i;
}
ret = kdrv_pmbus_write_block_data(bus_id, slave_addr, cmd, buf, len);
if (ret) {
pr_err("kdrv_pmbus_write_block_data fail\r\n");
kfree(buf);
return ret;
}
kfree(buf);
return 0;
}
int test_kdrv_pmbus_read_block_data(u32 bus_id, u32 slave_addr, u32 cmd, u32 len)
{
int ret;
int i;
u8 *buf = NULL;
buf = (u8 *)kzalloc(len, GFP_KERNEL);
if (buf == NULL) {
pr_err("kzalloc err!\r\n");
return -ENOMEM;
}
ret = kdrv_pmbus_read_block_data(bus_id, slave_addr, cmd, buf, len);
if (ret) {
pr_err("kdrv_pmbus_read_block_data fail bus_id:%#x, addr:%#x, cmd:%#x\n", bus_id, slave_addr, cmd);
kfree(buf);
return ret;
}
for (i = 0; i < len; i++) {
pr_info("read: bus_id:%#x, addr:%#x, cmd:%#x, buf[%d]:[0x%02x], len:%u\n", bus_id, slave_addr, cmd, i,
buf[i], len);
}
kfree(buf);
return 0;
}
int test_kdrv_pmbus_read_byte(u32 bus_id, u32 slave_addr, u32 cmd)
{
int ret;
u8 data;
ret = kdrv_pmbus_read_byte(bus_id, slave_addr, cmd, &data);
if (ret) {
pr_err("kdrv_pmbus_read_byte fail bus_id:%u, addr:%#x, cmd:%#x\n", bus_id, slave_addr, cmd);
return ret;
}
pr_info("read: bus_id:%#x, addr:%#x, cmd:%#x, data:%#x\n", bus_id, slave_addr, cmd, data);
return 0;
}
int test_kdrv_pmbus_read_word(u32 bus_id, u32 slave_addr, u32 cmd)
{
int ret;
u16 data;
ret = kdrv_pmbus_read_word(bus_id, slave_addr, cmd, &data);
if (ret) {
pr_err("kdrv_pmbus_read_word fail bus_id:%u, addr:%#x, cmd:%#x\n", bus_id, slave_addr, cmd);
return ret;
}
pr_info("read: bus_id:%#x, addr:%#x, cmd:%#x, data:%#x\n", bus_id, slave_addr, cmd, data);
return 0;
}
int test_kdrv_pmbus_write_byte(u32 bus_id, u32 slave_addr, u32 cmd, u32 data)
{
int ret;
ret = kdrv_pmbus_write_byte(bus_id, slave_addr, cmd, (u8)data);
if (ret) {
pr_err("kdrv_pmbus_write_byte fail bus_id:%u, addr:%#x, cmd:%#x\n", bus_id, slave_addr, cmd);
return ret;
}
pr_info("write byte: bus_id:%#x, addr:%#x, cmd:%#x, data:%#x\n", bus_id, slave_addr, cmd, data);
return 0;
}
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;
}
int test_kdrv_pmbus_write_cmd(u32 bus_id, u32 slave_addr, u32 cmd)
{
int ret;
ret = kdrv_pmbus_write_cmd(bus_id, slave_addr, cmd);
if (ret) {
pr_err("kdrv_pmbus_write_cmd 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\n", bus_id, slave_addr, cmd);
return 0;
}
#define PMBUS_CMD_READ_BYTE 0
#define PMBUS_CMD_READ_WORD 1
#define PMBUS_CMD_WRITE_BYTE 2
#define PMBUS_CMD_WRITE_WORD 3
#define PMBUS_CMD_READ_BLOCK 4
#define PMBUS_CMD_WRITE_BLOCK 5
#define PMBUS_CMD_WRITE_CMD 6
#define PMBUS_CMD_RESET 7
#define PMBUS_CMD_INIT 8
#define CFG_LINE_SIZE 120
static ssize_t pmbus_test_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos)
{
u32 cmd;
u32 bus_id;
u32 command;
u32 data;
u32 slave_addr;
char *next = NULL;
char line[CFG_LINE_SIZE] = {0};
if (len > (CFG_LINE_SIZE - 1)) {
return -E2BIG;
}
if (copy_from_user(line, buf, len)) {
return -EINVAL;
}
/* usage: echo <cmd> [bus_id][slave_addr] [cmd] > pmbus_test */
cmd = simple_strtoul(line, &next, 0);
bus_id = simple_strtoul(next + 1, &next, 0);
switch (cmd) {
case PMBUS_CMD_READ_BYTE:
slave_addr = simple_strtoul(next + 1, &next, 0);
command = simple_strtoul(next + 1, &next, 0);
(void)test_kdrv_pmbus_read_byte(bus_id, slave_addr, command);
break;
case PMBUS_CMD_READ_WORD:
slave_addr = simple_strtoul(next + 1, &next, 0);
command = simple_strtoul(next + 1, &next, 0);
(void)test_kdrv_pmbus_read_word(bus_id, slave_addr, command);
break;
case PMBUS_CMD_WRITE_BYTE:
slave_addr = simple_strtoul(next + 1, &next, 0);
command = simple_strtoul(next + 1, &next, 0);
data = simple_strtoul(next + 1, &next, 0);
(void)test_kdrv_pmbus_write_byte(bus_id, slave_addr, command, (u8)data);
break;
case PMBUS_CMD_WRITE_WORD:
slave_addr = simple_strtoul(next + 1, &next, 0);
command = simple_strtoul(next + 1, &next, 0);
data = simple_strtoul(next + 1, &next, 0);
(void)test_kdrv_pmbus_write_word(bus_id, slave_addr, command, (u16)data);
break;
case PMBUS_CMD_READ_BLOCK:
slave_addr = simple_strtoul(next + 1, &next, 0);
command = simple_strtoul(next + 1, &next, 0);
data = simple_strtoul(next + 1, &next, 0);
(void)test_kdrv_pmbus_read_block_data(bus_id, slave_addr, command, data);
break;
case PMBUS_CMD_WRITE_BLOCK:
slave_addr = simple_strtoul(next + 1, &next, 0);
command = simple_strtoul(next + 1, &next, 0);
data = simple_strtoul(next + 1, &next, 0);
(void)test_kdrv_pmbus_write_block_data(bus_id, slave_addr, command, data);
break;
case PMBUS_CMD_WRITE_CMD:
slave_addr = simple_strtoul(next + 1, &next, 0);
command = simple_strtoul(next + 1, &next, 0);
(void)test_kdrv_pmbus_write_cmd(bus_id, slave_addr, command);
break;
case PMBUS_CMD_RESET:
(void)kdrv_pmbus_reset(bus_id);
break;
case PMBUS_CMD_INIT:
data = simple_strtoul(next + 1, &next, 0);
(void)kdrv_pmbus_init(bus_id, data);
break;
default:
break;
}
return len;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
static const struct proc_ops g_pmbus_operations = {
.proc_open = pmbus_test_open,
.proc_read = seq_read,
.proc_write = pmbus_test_write,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
#else
static const struct file_operations g_pmbus_operations = {
.open = pmbus_test_open,
.read = seq_read,
.write = pmbus_test_write,
.llseek = seq_lseek,
.release = single_release,
};
#endif
int __init pmbus_test_mod_init(void)
{
(void)proc_create("pmbus_test", S_IRUSR, NULL, &g_pmbus_operations);
printk("pmbus_test_mod_init\r\n");
return 0;
}
void __exit pmbus_test_mod_exit(void)
{
(void)remove_proc_entry("pmbus_test", NULL);
printk("pmbus_test_mod_exit\r\n");
}
module_init(pmbus_test_mod_init);
module_exit(pmbus_test_mod_exit);
MODULE_DESCRIPTION("udrv pmbus api test");
MODULE_LICENSE("GPL");"
#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;
}"
#include <linux/device.h>
#include <linux/errno.h>
#include "hisi_pmbus.h"
#include "kdrv_pmbus_api.h"
#define PMBUS_BLOCK_MAX 32
static void hisi_pmbus_fill_msg(struct pmbus_msg *msg, u8 type, u8 slave_addr, u16 command)
{
u8 cmd;
cmd = (u8)(command >> 0x8); // 高8位为extended cmd 指示这是一条扩展命令
if ((cmd == PMBUS_MFR_SPECIFIC_COMMAND_EXT || cmd == PMBUS_COMMAND_EXT)) {
msg->type = type | PMBUS_FLAG_EXT;
msg->command = cmd;
msg->command_ext = (u8)command;
} else {
msg->type = type;
msg->command = (u8)command;
}
msg->slave_addr = slave_addr;
}
static int hisi_pmbus_xfer(struct hisi_pmbus_data *pmbus, struct pmbus_msg *msg)
{
int ret;
if ((msg->data == NULL) && (msg->data_len > 0)) {
dev_err(pmbus->dev, "[iWare][Error] msg->data = NULL, msg.data_len %zu\r\n", msg->data_len);
return -EINVAL;
}
if ((msg->type & PMBUS_FLAG_NO_DATA) != 0) {
ret = pmbus->ops->send_byte(&pmbus->reg_region, msg);
} else if ((msg->type & PMBUS_FLAG_READ) != 0) {
ret = pmbus->ops->read_bytes(&pmbus->reg_region, msg);
} else { /* pmbus write */
ret = pmbus->ops->write_bytes(&pmbus->reg_region, msg);
}
return ret;
}
int kdrv_pmbus_reset(u32 bus_id)
{
struct hisi_pmbus_data *pmbus = NULL;
pmbus = hisi_pmbus_data_get_by_id(bus_id);
if (pmbus == NULL) {
pr_err("[iWare][Error] get pmbus data fail: pmbus_id=%u,\n", bus_id);
return -EPERM;
}
mutex_lock(&pmbus->lock);
pmbus->ops->reset(&pmbus->reg_region);
mutex_unlock(&pmbus->lock);
return 0;
}
EXPORT_SYMBOL(kdrv_pmbus_reset);
int kdrv_pmbus_init(u32 bus_id, u32 bus_freq_hz)
{
struct hisi_pmbus_data *pmbus = NULL;
int ret;
u32 total_cnt;
pmbus = hisi_pmbus_data_get_by_id(bus_id);
if (pmbus == NULL) {
pr_err("[iWare][Error] get pmbus data fail: pmbus_id=%u,\n", bus_id);
return -EPERM;
}
if (bus_freq_hz > PMBUS_MAX_FAST_MODE_FREQ || bus_freq_hz == 0) {
pr_err("[iWare][Error] invalid para bus_freq_hz =%u \r\n", bus_freq_hz);
return -EINVAL;
}
mutex_lock(&pmbus->lock);
pmbus->timing.bus_freq_hz = bus_freq_hz;
total_cnt = (u32)DIV_ROUND_UP_ULL(pmbus->timing.clk_freq_mhz * HZ_PER_MHZ, bus_freq_hz);
pmbus->timing_cfg.hcnt = (pmbus->timing.scl_high_ratio * total_cnt) / 100; // 占空比放大了100倍 这里除以100
pmbus->timing_cfg.lcnt = total_cnt - pmbus->timing_cfg.hcnt;
pr_info("[iWare][Info] %s bus_freq_hz %u, total_cnt %u, hcnt %u lcnt %u\n", __func__, bus_freq_hz, total_cnt,
pmbus->timing_cfg.hcnt, pmbus->timing_cfg.lcnt);
ret = pmbus->ops->init(&pmbus->reg_region, pmbus->pec_en, pmbus->timing.bus_freq_hz, &pmbus->timing_cfg,
&pmbus->key);
if (ret != 0) {
mutex_unlock(&pmbus->lock);
pr_err("[iWare][Error] kdrv_pmbus_init failed ret=%d\n", ret);
return ret;
}
mutex_unlock(&pmbus->lock);
return 0;
}
EXPORT_SYMBOL(kdrv_pmbus_init);
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 kdrv_pmbus_write_byte(u32 bus_id, u8 slave_addr, u16 command, u8 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_byte, get pmbus data fail: pmbus_id=%u,\n", bus_id);
return -EPERM;
}
mutex_lock(&pmbus->lock);
msg.data = &data;
msg.data_len = sizeof(u8);
hisi_pmbus_fill_msg(&msg, PMBUS_WRITE_BYTE, 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_byte);
int kdrv_pmbus_write_cmd(u32 bus_id, u8 slave_addr, u16 command)
{
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_cmd, get pmbus data fail: pmbus_id=%u,\n", bus_id);
return -EPERM;
}
mutex_lock(&pmbus->lock);
hisi_pmbus_fill_msg(&msg, PMBUS_SEND_BYTE, 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_cmd);
int kdrv_pmbus_read_byte(u32 bus_id, u8 slave_addr, u16 command, u8 *data)
{
struct hisi_pmbus_data *pmbus = NULL;
struct pmbus_msg msg = { 0 };
int ret;
if (data == NULL) {
pr_err("[iWare][Error] ivalid para, data is null point,\n");
return -EINVAL;
}
pmbus = hisi_pmbus_data_get_by_id(bus_id);
if (pmbus == NULL) {
pr_err("[iWare][Error] get pmbus data fail: pmbus_id=%u,\n", bus_id);
return -EPERM;
}
mutex_lock(&pmbus->lock);
msg.data = data;
msg.data_len = sizeof(u8);
hisi_pmbus_fill_msg(&msg, PMBUS_READ_BYTE, 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_read_byte);
int kdrv_pmbus_read_word(u32 bus_id, u8 slave_addr, u16 command, u16 *data)
{
struct hisi_pmbus_data *pmbus = NULL;
struct pmbus_msg msg = { 0 };
int ret;
u8 tmp[2] = {0}; // 2个字节
if (data == NULL) {
pr_err("[iWare][Error] ivalid para, data is null point,\n");
return -EINVAL;
}
pmbus = hisi_pmbus_data_get_by_id(bus_id);
if (pmbus == NULL) {
pr_err("[iWare][Error] get pmbus data fail: pmbus_id=%u,\n", bus_id);
return -EPERM;
}
mutex_lock(&pmbus->lock);
msg.data = tmp;
msg.data_len = sizeof(u16);
hisi_pmbus_fill_msg(&msg, PMBUS_READ_WORD, slave_addr, command);
ret = hisi_pmbus_xfer(pmbus, &msg);
if (ret != 0) {
mutex_unlock(&pmbus->lock);
return ret;
}
*data = (((u16)tmp[1]) << 8) + ((u16)tmp[0]); // 高8位
mutex_unlock(&pmbus->lock);
return 0;
}
EXPORT_SYMBOL(kdrv_pmbus_read_word);
int kdrv_pmbus_write_block_data(u32 bus_id, u8 slave_addr, u16 command, u8 *pbuf, u32 len)
{
struct hisi_pmbus_data *pmbus = NULL;
struct pmbus_msg msg = { 0 };
int ret;
if ((pbuf == NULL) || (len == 0)) {
pr_err("[iWare][Error] para err: bus_id(%d),slave_addr(0x%x), len(%u)\n", bus_id, slave_addr, len);
return -EINVAL;
}
if (len > PMBUS_BLOCK_MAX) {
pr_err("[iWare][Error] para err: len(%u) exceed PMBUS_BLOCK_MAX(32)\n", len);
return -EINVAL;
}
pmbus = hisi_pmbus_data_get_by_id(bus_id);
if (pmbus == NULL) {
pr_err("[iWare][Error] get pmbus data fail: pmbus_id=%u,\n", bus_id);
return -EPERM;
}
mutex_lock(&pmbus->lock);
msg.data = pbuf;
msg.data_len = (u8)len;
hisi_pmbus_fill_msg(&msg, PMBUS_WRITE_BLOCK, 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_block_data);
int kdrv_pmbus_read_block_data(u32 bus_id, u8 slave_addr, u16 command, u8 *pbuf, u32 len)
{
struct hisi_pmbus_data *pmbus = NULL;
struct pmbus_msg msg = { 0 };
int ret;
if ((pbuf == NULL) || (len == 0)) {
pr_err("[iWare][Error] para err: bus_id(%d),slave_addr(0x%x), len(%u)\n", bus_id, slave_addr, len);
return -EINVAL;
}
if (len > PMBUS_BLOCK_MAX) {
pr_err("[iWare][Error] para err: len(%u) exceed PMBUS_BLOCK_MAX(32)\n", len);
return -EINVAL;
}
pmbus = hisi_pmbus_data_get_by_id(bus_id);
if (pmbus == NULL) {
pr_err("[iWare][Error] get pmbus data fail: pmbus_id=%u,\n", bus_id);
return -EPERM;
}
mutex_lock(&pmbus->lock);
msg.data = pbuf;
msg.data_len = (u8)len;
hisi_pmbus_fill_msg(&msg, PMBUS_READ_BLOCK, 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_read_block_data);
"
“测试步骤为
”insmod /lib/udrivers/hi309a_pmbus_api_test.ko
devmem 0xfa860204 w 0
devmem 0xfa860220 w 0
echo 3 1 0x70 0x21 0xf4 > /proc/pmbus_test (设置输出电压)
“
然后再设置输出电压之后报以下错误
[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
请根据以上几段代码,帮我分析我出现kdrv_pmbus_write_word fail bus_id:1, addr:0x70, cmd:0x21的原因可能是什么,并给出完整解决方案”
最新发布