i2c:fix bus broken after once xfer failed

This commit is contained in:
maoxiaochuan
2025-04-02 14:14:45 +08:00
parent ff60a514ff
commit 9bd4ef8edb
+116 -43
View File
@@ -22,6 +22,16 @@
#include <linux/platform_device.h>
#include <linux/reset.h>
// #define I2C_LSFS_DEBUG
#ifdef I2C_LSFS_DEBUG
#define i2c_lsfs_err dev_err
#define i2c_lsfs_info dev_info
#else
#define i2c_lsfs_err(dev, fmt, ...)
#define i2c_lsfs_info(dev, fmt, ...)
#endif
/* Loongson I2C offset registers */
#define I2C_CR1 0x00
#define I2C_CR2 0x04
@@ -33,6 +43,8 @@
#define I2C_FLTR 0x24
/* Loongson I2C control 1*/
#define I2C_CR1_SWRST BIT(15)
#define I2C_CR1_RECOVER BIT(14)
#define I2C_CR1_POS BIT(11)
#define I2C_CR1_ACK BIT(10)
#define I2C_CR1_STOP BIT(9)
@@ -143,9 +155,9 @@ static int ls_i2c_hw_config(struct ls_i2c_dev *i2c_dev)
i2c_set_bits(i2c_dev->base + I2C_CR2, 0x3f);
i2c_set_bits(i2c_dev->base + I2C_TRISE, 0x3f);
if (i2c_dev->speed == LS_I2C_SPEED_STANDARD) {
if (i2c_dev->speed == LS_I2C_SPEED_STANDARD)
val = DIV_ROUND_UP(i2c_dev->freq, 100000 * 2);
} else {
else {
val = DIV_ROUND_UP(i2c_dev->freq, 400000 * 3);
/* Select Fast mode */
@@ -197,6 +209,41 @@ static int i2c_wait_EV_signal(struct ls_i2c_dev *i2c_dev, int reg, int mask, int
// i2c_wait_EV_signal 返回非 0 时所在对应函数返回 I2C_XFER_TIMEOUT_ERROR
#define i2c_wait_EV_signal_t(dev, reg_offset, mask, target) if (i2c_wait_EV_signal(dev, reg_offset, mask, target)) { return -EBUSY;}
/*
* @brief: i2c bus 状态机复位
* @param: i2c_dev: struct ls_i2c_dev
*/
static void i2c_bus_error_reset_end_of_xfer(struct ls_i2c_dev *i2c_dev)
{
unsigned char* base;
unsigned int temp;
unsigned int temp_SR1;
unsigned int temp_CR1_mask;
base = i2c_dev->base;
temp_SR1 = readl((base + I2C_SR1));
if (temp_SR1 & I2C_SR1_BERR)
temp_CR1_mask = I2C_CR1_RECOVER;
// 状态机复位
temp = readl((base + I2C_CR1));
temp |= (I2C_CR1_SWRST | temp_CR1_mask);
writel(temp, (base + I2C_CR1));
// 错误复位
temp = readl((base + I2C_SR1));
temp |= (0xf << 8);
temp ^= (0xf << 8);
writel(temp, (base + I2C_SR1));
temp = readl((base + I2C_SR1));
temp = readl((base + I2C_CR1));
temp |= (I2C_CR1_SWRST | temp_CR1_mask);
temp ^= (I2C_CR1_SWRST | temp_CR1_mask);
writel(temp, (base + I2C_CR1));
}
/*
* @brief: i2c主模式,接收状态结束时的设置
* @param: base: i2c控制器基地址
@@ -277,12 +324,10 @@ static int i2c_xfer_r_3m(struct ls_i2c_dev *i2c_dev, struct i2c_msg *msg, int is
i = 0;
remain_read = msg->len;
while (1)
{
while (1) {
if (remain_read <= 3)
{
break;
}
// 倒数3个字节之前,看 I2C_SR1_RXNE 信号即可,有就读取DR
i2c_wait_EV_signal_t(i2c_dev, I2C_SR1, I2C_SR1_RXNE, 1);
i2c_update_read_msg_data(base, msg, i++);
@@ -320,7 +365,11 @@ static int i2c_xfer_r(struct ls_i2c_dev *i2c_dev, struct i2c_msg *msg, int is_st
temp |= I2C_CR1_ACK;
writel(temp, (base + I2C_CR1));
i2c_wait_EV_signal_t(i2c_dev, I2C_SR1, I2C_SR1_SB, 1);
ret = i2c_wait_EV_signal(i2c_dev, I2C_SR1, I2C_SR1_SB, 1);
if (ret) {
i2c_lsfs_err(i2c_dev->dev, "i2c read SR1_SB error(%d) addr 0x%.2x\n", ret, msg->addr);
goto i2c_xfer_r_end;
}
// 把7bit地址左移一位,然后最后1bit设置为1,代表此从地址用于读取
xfer_data = msg->addr;
@@ -328,33 +377,38 @@ static int i2c_xfer_r(struct ls_i2c_dev *i2c_dev, struct i2c_msg *msg, int is_st
xfer_data |= 1; // bit 0 == 1 read
writeb(xfer_data, (base + I2C_DR));
i2c_wait_EV_signal_t(i2c_dev, I2C_SR1, I2C_SR1_ADDR, 1);
ret = i2c_wait_EV_signal(i2c_dev, I2C_SR1, I2C_SR1_ADDR, 1);
if (ret) {
i2c_lsfs_err(i2c_dev->dev, "i2c read SR1_ADDR error(%d) addr 0x%.2x\n", ret, msg->addr);
goto i2c_xfer_r_end;
}
temp = readl((base + I2C_SR2));
// 针对不同的需要读取的字节数,调用不同的处理函数
if (msg->len == 1) // read 1 byte
{
ret = i2c_xfer_r_1(i2c_dev, msg, is_stop);
}
else if (msg->len == 2) // read 2 byte
{
ret = i2c_xfer_r_2(i2c_dev, msg, is_stop);
}
else if (msg->len > 3) // read 3 or more than 3 byte
{
ret = i2c_xfer_r_3m(i2c_dev, msg, is_stop);
if (ret) {
i2c_lsfs_err(i2c_dev->dev, "i2c read xfer data error(%d) addr 0x%.2x\n", ret, msg->addr);
goto i2c_xfer_r_end;
}
i2c_xfer_r_end:
if (ret)
return ret;
// 如果需要关闭总线,等待busy信号为0,代表总线总体已经空闲
if (is_stop)
{
i2c_wait_EV_signal_t(i2c_dev, I2C_SR2, I2C_SR2_BUSY, 0);
i2c_bus_error_reset_end_of_xfer(i2c_dev);
else {
// 如果需要关闭总线,等待busy信号为0,代表总线总体已经空闲
if (is_stop)
i2c_wait_EV_signal_t(i2c_dev, I2C_SR2, I2C_SR2_BUSY, 0);
}
return 0;
return ret;
}
/*
@@ -369,6 +423,7 @@ static int i2c_xfer_w(struct ls_i2c_dev *i2c_dev, struct i2c_msg *msg, int is_st
unsigned int temp;
int i;
unsigned char xfer_data;
int ret;
base = i2c_dev->base;
@@ -377,46 +432,64 @@ static int i2c_xfer_w(struct ls_i2c_dev *i2c_dev, struct i2c_msg *msg, int is_st
temp |= I2C_CR1_START;
writel(temp, (base + I2C_CR1));
i2c_wait_EV_signal_t(i2c_dev, I2C_SR1, I2C_SR1_SB, 1);
ret = i2c_wait_EV_signal(i2c_dev, I2C_SR1, I2C_SR1_SB, 1);
if (ret) {
i2c_lsfs_err(i2c_dev->dev, "i2c write SR1_SB error(%d) addr 0x%.2x\n", ret, msg->addr);
goto i2c_xfer_w_end;
}
// 把7bit地址左移一位,然后最后1bit设置为1,代表此从地址用于读取
xfer_data = msg->addr;
xfer_data <<= 1; // bit 0 == 0 write
writeb(xfer_data, (base + I2C_DR));
i2c_wait_EV_signal_t(i2c_dev, I2C_SR1, I2C_SR1_ADDR, 1);
ret = i2c_wait_EV_signal(i2c_dev, I2C_SR1, I2C_SR1_ADDR, 1);
if (ret) {
i2c_lsfs_err(i2c_dev->dev, "i2c write SR1_ADDR error(%d) addr 0x%.2x\n", ret, msg->addr);
goto i2c_xfer_w_end;
}
temp = readl((base + I2C_SR2));
// 等待发送数据寄存器为空
i2c_wait_EV_signal_t(i2c_dev, I2C_SR1, I2C_SR1_TXE, 1);
ret = i2c_wait_EV_signal(i2c_dev, I2C_SR1, I2C_SR1_TXE, 1);
if (ret) {
i2c_lsfs_err(i2c_dev->dev, "i2c write SR1_TXE error(%d) addr 0x%.2x\n", ret, msg->addr);
goto i2c_xfer_w_end;
}
i = 0;
while (1)
{
while (1) {
if (i == msg->len)
{
break;
}
xfer_data = msg->buf[i];
++i;
writeb(xfer_data, (base + I2C_DR));
// 等待传送结束
i2c_wait_EV_signal_t(i2c_dev, I2C_SR1, I2C_SR1_BTF, 1);
ret = i2c_wait_EV_signal(i2c_dev, I2C_SR1, I2C_SR1_BTF, 1);
if (ret) {
i2c_lsfs_err(i2c_dev->dev, "i2c write SR1_BTF error(%d) addr 0x%.2x\n", ret, msg->addr);
goto i2c_xfer_w_end;
}
}
if (is_stop)
{
// 关闭总线
temp = readl((base + I2C_CR1));
temp |= (I2C_CR1_STOP);
writel(temp, (base + I2C_CR1));
// 等待busy信号为0,代表总线总体已经空闲
i2c_wait_EV_signal_t(i2c_dev, I2C_SR2, I2C_SR2_BUSY, 0);
i2c_xfer_w_end:
if (ret)
i2c_bus_error_reset_end_of_xfer(i2c_dev);
else {
if (is_stop) {
// 关闭总线
temp = readl((base + I2C_CR1));
temp |= (I2C_CR1_STOP);
writel(temp, (base + I2C_CR1));
// 等待busy信号为0,代表总线总体已经空闲
i2c_wait_EV_signal_t(i2c_dev, I2C_SR2, I2C_SR2_BUSY, 0);
}
}
return 0;
return ret;
}
/**
@@ -434,22 +507,22 @@ static int ls_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int
int stop;
ret = ls_i2c_wait_free_bus(i2c_dev);
if (ret)
if (ret) {
i2c_lsfs_err(i2c_dev->dev, "i2c buf free error(%d)\n", ret);
return ret;
}
for (i = 0; i < num; i++) {
cur_msg = msgs + i;
stop = (i == (num - 1));
if (cur_msg->flags & I2C_M_RD)
{
ret += i2c_xfer_r(i2c_dev, cur_msg, stop);
}
else
{
ret += i2c_xfer_w(i2c_dev, cur_msg, stop);
if (ret) {
i2c_lsfs_err(i2c_dev->dev, "i2c %s error(%d)\n", (cur_msg->flags & I2C_M_RD) ? "read" : "write", ret); // maybe is null
}
if (ret)
dev_err(i2c_dev->dev, "i2c %s error(%d)\r\n", (cur_msg->flags & I2C_M_RD) ? "read" : "write", ret);
}
return (ret < 0) ? ret : num;