重构以下代码 void i2c_as_slave_xfer_enable(int bus) { u32 ctrl_bits; /* Disable Master function ...

这段代码展示了如何在I2C总线上作为从设备时,通过读取和修改I2C_FUNCTION_CONTROL_REG寄存器来禁用主机功能,并将缓冲区页面选择设为0的操作。

重构代码:

void i2c_as_slave_xfer_enable(int bus) {
    u32 ctrl_bits;
    // 禁用主机功能,设置缓冲区页面选择为0
    ctrl_bits = i2c_as_read_reg(bus, I2C_FUNCTION_CONTROL_REG);
    //...
}

其中该代码禁用了I2C总线上的主机功能,并将缓冲区页面选择设置为0。

/* * 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 请根据以上几段代码,帮我分析我的报错原因可能是什么,并给出完整解决方案”
08-06
/* Copyright © 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”); } }” “测试步骤为 ”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 请根据以上几段代码,帮我分析我的报错原因可能是什么,并给出完整解决方案”
08-06
#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的原因可能是什么,并给出完整解决方案”
08-07
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值