Skip to content

Commit

Permalink
i2c: imx: Synthesize end of transaction events without idle interrupts
Browse files Browse the repository at this point in the history
Only the Layerscape SoCs have interrupts on bus idle, which facilitate
sending events which complete slave bus transactions.

Add support for synthesizing missing events. If we see a master request,
or a newly addressed slave request, if the last event sent to the backend
was I2C_SLAVE_READ_REQUESTED, send the backend a I2C_SLAVE_READ_PROCESSED
followed by I2C_SLAVE_STOP. For all other events, send an I2C_SLAVE_STOP.

Signed-off-by: Kevin Paul Herbert <kph@platinasystems.com>
Tested-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Wolfram Sang <wsa@kernel.org>
  • Loading branch information
kph authored and wsakernel committed Feb 1, 2021
1 parent e037129 commit 05ae60b
Showing 1 changed file with 52 additions and 7 deletions.
59 changes: 52 additions & 7 deletions drivers/i2c/busses/i2c-imx.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ struct imx_i2c_struct {

struct imx_i2c_dma *dma;
struct i2c_client *slave;
enum i2c_slave_event last_slave_event;
};

static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
Expand Down Expand Up @@ -675,6 +676,36 @@ static void i2c_imx_enable_bus_idle(struct imx_i2c_struct *i2c_imx)
}
}

static void i2c_imx_slave_event(struct imx_i2c_struct *i2c_imx,
enum i2c_slave_event event, u8 *val)
{
i2c_slave_event(i2c_imx->slave, event, val);
i2c_imx->last_slave_event = event;
}

static void i2c_imx_slave_finish_op(struct imx_i2c_struct *i2c_imx)
{
u8 val;

while (i2c_imx->last_slave_event != I2C_SLAVE_STOP) {
switch (i2c_imx->last_slave_event) {
case I2C_SLAVE_READ_REQUESTED:
i2c_imx_slave_event(i2c_imx, I2C_SLAVE_READ_PROCESSED,
&val);
break;

case I2C_SLAVE_WRITE_REQUESTED:
case I2C_SLAVE_READ_PROCESSED:
case I2C_SLAVE_WRITE_RECEIVED:
i2c_imx_slave_event(i2c_imx, I2C_SLAVE_STOP, &val);
break;

case I2C_SLAVE_STOP:
break;
}
}
}

static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
unsigned int status, unsigned int ctl)
{
Expand All @@ -687,9 +718,11 @@ static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
}

if (status & I2SR_IAAS) { /* Addressed as a slave */
i2c_imx_slave_finish_op(i2c_imx);
if (status & I2SR_SRW) { /* Master wants to read from us*/
dev_dbg(&i2c_imx->adapter.dev, "read requested");
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_REQUESTED, &value);
i2c_imx_slave_event(i2c_imx,
I2C_SLAVE_READ_REQUESTED, &value);

/* Slave transmit */
ctl |= I2CR_MTX;
Expand All @@ -699,7 +732,8 @@ static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
} else { /* Master wants to write to us */
dev_dbg(&i2c_imx->adapter.dev, "write requested");
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
i2c_imx_slave_event(i2c_imx,
I2C_SLAVE_WRITE_REQUESTED, &value);

/* Slave receive */
ctl &= ~I2CR_MTX;
Expand All @@ -710,17 +744,20 @@ static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
} else if (!(ctl & I2CR_MTX)) { /* Receive mode */
if (status & I2SR_IBB) { /* No STOP signal detected */
value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
i2c_imx_slave_event(i2c_imx,
I2C_SLAVE_WRITE_RECEIVED, &value);
} else { /* STOP signal is detected */
dev_dbg(&i2c_imx->adapter.dev,
"STOP signal detected");
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_STOP, &value);
i2c_imx_slave_event(i2c_imx,
I2C_SLAVE_STOP, &value);
}
} else if (!(status & I2SR_RXAK)) { /* Transmit mode received ACK */
ctl |= I2CR_MTX;
imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);

i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_PROCESSED, &value);
i2c_imx_slave_event(i2c_imx,
I2C_SLAVE_READ_PROCESSED, &value);

imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
} else { /* Transmit mode received NAK */
Expand Down Expand Up @@ -761,6 +798,7 @@ static int i2c_imx_reg_slave(struct i2c_client *client)
return -EBUSY;

i2c_imx->slave = client;
i2c_imx->last_slave_event = I2C_SLAVE_STOP;

/* Resume */
ret = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
Expand Down Expand Up @@ -813,10 +851,17 @@ static irqreturn_t i2c_imx_isr(int irq, void *dev_id)

status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);

if (status & I2SR_IIF) {
i2c_imx_clear_irq(i2c_imx, I2SR_IIF);
if (i2c_imx->slave && !(ctl & I2CR_MSTA))
return i2c_imx_slave_isr(i2c_imx, status, ctl);
if (i2c_imx->slave) {
if (!(ctl & I2CR_MSTA)) {
return i2c_imx_slave_isr(i2c_imx, status, ctl);
} else if (i2c_imx->last_slave_event !=
I2C_SLAVE_STOP) {
i2c_imx_slave_finish_op(i2c_imx);
}
}
return i2c_imx_master_isr(i2c_imx, status);
}

Expand Down

0 comments on commit 05ae60b

Please sign in to comment.