Hi buddies,
I'm developing an i2c slave driver using linux 2.6.34 on MPC8306.
In my test, I use a RS232-to-I2C adapter connected to PC as the i2c master.
When doing master receive and slave transmit, when the last byte has been xfered,
the slave always pull the I2C_SDA low and doesn't release the i2c bus,
STOP can't be received. The datasheet seems doesn't make it clear that how to
release I2C_SDA. Is there anybody facing the same issue?
Please help, thanks in advance.:smileysilly:
As the i2c protocol says, no ACK on the last byte transmit before STOP, and I found
I2CnSR register RXAK bit set, which mean no ACK is received. So, I think the slave
hang the bus and waiting for ACK from master.
Below is my i2c slave driver interrupt routine.
static irqreturn_t fsl_i2cli_irq_handler(int irq, void *device_id)
{
struct fsl_i2cli_priv *i2cli = device_id;
if (i2cli != fsl_i2cli)
return IRQ_HANDLED;
if ((readb(i2cli->base + MPC_I2C2SR) & I2C2SR_MIF) == 0)
return IRQ_HANDLED;
/* If we are addressed as slave, check I2CnSR[SRW] and
set I2CnCR[MTX] accordingly */
if (readb(i2cli->base + MPC_I2C2SR) & I2C2SR_MAAS) {
int tmp;
u8 cr, x;
PDEBUG("#### Addressed as an i2c slave ####\n");
tmp = readb(i2cli->base + MPC_I2C2SR) & I2C2SR_SRW;
i2cli->mode = tmp > 0 ? 1 : 0;
PDEBUG("xfer mode: %s\n", (i2cli->mode == 0 ?
"Slave receive" : "Slave transmit"));
cr = readb(i2cli->base + MPC_I2C2CR);
if (i2cli->mode == 0)
writeb((cr & ~I2C2CR_MTX), i2cli->base + MPC_I2C2CR);
else if (i2cli->mode == 1)
writeb((cr | I2C2CR_MTX), i2cli->base + MPC_I2C2CR);
writeb(0, i2cli->base + MPC_I2C2SR);
/* dummy read on first byte */
x = readb(i2cli->base + MPC_I2C2DR);
PDEBUG("Dummy read x: 0x%02x\n", x);
}
/* Set by the falling edge of the 9th clock of a byte transfer */
if (readb(i2cli->base + MPC_I2C2SR) & I2C2SR_MCF) {
PDEBUG("Data xfered\n");
writeb(0, i2cli->base + MPC_I2C2SR);
/* If it's ACK on Master START command, do a dummy read */
if (!i2cli->f_ack_on_start) {
readb(i2cli->base + MPC_I2C2DR);
i2cli->f_ack_on_start = 1;
goto verify_mbb;
}
/* Data xfer buffer is full */
if ((i2cli->mode == 0) &&
(i2cli->buf_ptr - i2cli->buf >= I2CLI_XFER_BUF_SIZE)) {
i2cli->xfer_done = 1;
wake_up_interruptible(&i2cli->wait_qh);
goto end;
}
/* No more data to xfer in slave transmit mode */
if ((i2cli->mode == 1) && (i2cli->buf_ptr - i2cli->buf >= i2cli->size)) {
i2cli->xfer_done = 1;
wake_up_interruptible(&i2cli->wait_qh);
goto end;
}
if (i2cli->mode == 0) {
/* Slave receive */
u8 data = readb(i2cli->base + MPC_I2C2DR);
PDEBUG("Read data: 0x%02x\n", data);
PDEBUG("buf: %p, buf_ptr: %p\n", i2cli->buf, i2cli->buf_ptr);
i2cli->size++;
*i2cli->buf_ptr++ = data;
} else {
/* Slave transmit */
writeb(*i2cli->buf_ptr, i2cli->base + MPC_I2C2DR);
PDEBUG("Write data: 0x%02x\n", *i2cli->buf_ptr);
PDEBUG("buf: %p, buf_ptr: %p\n", i2cli->buf, i2cli->buf_ptr);
i2cli->buf_ptr++;
}
}
verify_mbb:
/* If a STOP condition is detected, I2CnSR[MBB] is cleared,
* Seems only make effect in case of slave receive */
if ((readb(i2cli->base + MPC_I2C2SR) & I2C2SR_MBB) == 0) {
PDEBUG("Stoped\n");
i2cli->xfer_done = 1;
wake_up_interruptible(&i2cli->wait_qh);
}
end:
return IRQ_HANDLED;
}
Message was edited by: Jia Guo