Commit 173b77e8 authored by Liguo Zhang's avatar Liguo Zhang Committed by Wolfram Sang

i2c: mediatek: add i2c first write then read optimization

For platform with auto restart support, between every transfer,
i2c controller will trigger an interrupt and SW need to handle
it to start new transfer. When doing write-then-read transfer,
instead of restart mechanism, using WRRD mode to have controller
send both transfer in one request to reduce latency.
Signed-off-by: default avatarLiguo Zhang <liguo.zhang@mediatek.com>
Reviewed-by: default avatarEddie Huang <eddie.huang@mediatek.com>
Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
parent 948c58a0
...@@ -132,6 +132,7 @@ struct mtk_i2c_compatible { ...@@ -132,6 +132,7 @@ struct mtk_i2c_compatible {
unsigned char pmic_i2c: 1; unsigned char pmic_i2c: 1;
unsigned char dcm: 1; unsigned char dcm: 1;
unsigned char auto_restart: 1; unsigned char auto_restart: 1;
unsigned char aux_len_reg: 1;
}; };
struct mtk_i2c { struct mtk_i2c {
...@@ -153,6 +154,7 @@ struct mtk_i2c { ...@@ -153,6 +154,7 @@ struct mtk_i2c {
enum mtk_trans_op op; enum mtk_trans_op op;
u16 timing_reg; u16 timing_reg;
u16 high_speed_reg; u16 high_speed_reg;
unsigned char auto_restart;
const struct mtk_i2c_compatible *dev_comp; const struct mtk_i2c_compatible *dev_comp;
}; };
...@@ -178,6 +180,7 @@ static const struct mtk_i2c_compatible mt6577_compat = { ...@@ -178,6 +180,7 @@ static const struct mtk_i2c_compatible mt6577_compat = {
.pmic_i2c = 0, .pmic_i2c = 0,
.dcm = 1, .dcm = 1,
.auto_restart = 0, .auto_restart = 0,
.aux_len_reg = 0,
}; };
static const struct mtk_i2c_compatible mt6589_compat = { static const struct mtk_i2c_compatible mt6589_compat = {
...@@ -185,6 +188,7 @@ static const struct mtk_i2c_compatible mt6589_compat = { ...@@ -185,6 +188,7 @@ static const struct mtk_i2c_compatible mt6589_compat = {
.pmic_i2c = 1, .pmic_i2c = 1,
.dcm = 0, .dcm = 0,
.auto_restart = 0, .auto_restart = 0,
.aux_len_reg = 0,
}; };
static const struct mtk_i2c_compatible mt8173_compat = { static const struct mtk_i2c_compatible mt8173_compat = {
...@@ -192,6 +196,7 @@ static const struct mtk_i2c_compatible mt8173_compat = { ...@@ -192,6 +196,7 @@ static const struct mtk_i2c_compatible mt8173_compat = {
.pmic_i2c = 0, .pmic_i2c = 0,
.dcm = 1, .dcm = 1,
.auto_restart = 1, .auto_restart = 1,
.aux_len_reg = 1,
}; };
static const struct of_device_id mtk_i2c_of_match[] = { static const struct of_device_id mtk_i2c_of_match[] = {
...@@ -373,7 +378,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, ...@@ -373,7 +378,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
i2c->irq_stat = 0; i2c->irq_stat = 0;
if (i2c->dev_comp->auto_restart) if (i2c->auto_restart)
restart_flag = I2C_RS_TRANSFER; restart_flag = I2C_RS_TRANSFER;
reinit_completion(&i2c->msg_complete); reinit_completion(&i2c->msg_complete);
...@@ -411,8 +416,14 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, ...@@ -411,8 +416,14 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
/* Set transfer and transaction len */ /* Set transfer and transaction len */
if (i2c->op == I2C_MASTER_WRRD) { if (i2c->op == I2C_MASTER_WRRD) {
if (i2c->dev_comp->aux_len_reg) {
writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
writew((msgs + 1)->len, i2c->base +
OFFSET_TRANSFER_LEN_AUX);
} else {
writew(msgs->len | ((msgs + 1)->len) << 8, writew(msgs->len | ((msgs + 1)->len) << 8,
i2c->base + OFFSET_TRANSFER_LEN); i2c->base + OFFSET_TRANSFER_LEN);
}
writew(I2C_WRRD_TRANAC_VALUE, i2c->base + OFFSET_TRANSAC_LEN); writew(I2C_WRRD_TRANAC_VALUE, i2c->base + OFFSET_TRANSAC_LEN);
} else { } else {
writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN); writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
...@@ -461,7 +472,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, ...@@ -461,7 +472,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
writel(I2C_DMA_START_EN, i2c->pdmabase + OFFSET_EN); writel(I2C_DMA_START_EN, i2c->pdmabase + OFFSET_EN);
if (!i2c->dev_comp->auto_restart) { if (!i2c->auto_restart) {
start_reg = I2C_TRANSAC_START; start_reg = I2C_TRANSAC_START;
} else { } else {
start_reg = I2C_TRANSAC_START | I2C_RS_MUL_TRIG; start_reg = I2C_TRANSAC_START | I2C_RS_MUL_TRIG;
...@@ -518,6 +529,16 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, ...@@ -518,6 +529,16 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
if (ret) if (ret)
return ret; return ret;
i2c->auto_restart = i2c->dev_comp->auto_restart;
/* checking if we can skip restart and optimize using WRRD mode */
if (i2c->auto_restart && num == 2) {
if (!(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD) &&
msgs[0].addr == msgs[1].addr) {
i2c->auto_restart = 0;
}
}
while (left_num--) { while (left_num--) {
if (!msgs->buf) { if (!msgs->buf) {
dev_dbg(i2c->dev, "data buffer is NULL.\n"); dev_dbg(i2c->dev, "data buffer is NULL.\n");
...@@ -530,7 +551,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, ...@@ -530,7 +551,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
else else
i2c->op = I2C_MASTER_WR; i2c->op = I2C_MASTER_WR;
if (!i2c->dev_comp->auto_restart) { if (!i2c->auto_restart) {
if (num > 1) { if (num > 1) {
/* combined two messages into one transaction */ /* combined two messages into one transaction */
i2c->op = I2C_MASTER_WRRD; i2c->op = I2C_MASTER_WRRD;
...@@ -559,7 +580,7 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id) ...@@ -559,7 +580,7 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
u16 restart_flag = 0; u16 restart_flag = 0;
u16 intr_stat; u16 intr_stat;
if (i2c->dev_comp->auto_restart) if (i2c->auto_restart)
restart_flag = I2C_RS_TRANSFER; restart_flag = I2C_RS_TRANSFER;
intr_stat = readw(i2c->base + OFFSET_INTR_STAT); intr_stat = readw(i2c->base + OFFSET_INTR_STAT);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment