MPC85xx i2c interface bug
dwilson at dslextreme.com
dwilson at dslextreme.com
Wed Nov 30 07:42:54 EST 2005
We have found a problem in the i2c-mpc.c file (linux 2.4 kernel). The
logic for the i2c transfer termination seems to have been inverted.
The original function is:
/* Perform one i2c_msg transfer. It could be a read, or a write.
* This function assumes that the address byte has been sent
* already, and only handles the message contents */
static int mpc_i2c_xfer_bytes(struct mpc_i2c_private* priv, struct
i2c_msg* pm)
{
volatile struct mpc_i2c *regs = priv->regs;
char *buf = pm->buf;
int len = pm->len;
int i;
int ret = 0;
if(pm->flags & I2C_M_RD) {
/* Change to read mode */
priv->write(®s->i2ccr, 0, MPC_I2CCR_MTX);
/* If there is only one byte, we need to TXAK now */
if(len == 1)
priv->write(®s->i2ccr, 0, MPC_I2CCR_TXAK);
/* Do a dummy read, to initiate the first read */
priv->read(®s->i2cdr);
}
for(i = 0; i < len; ++i) {
/* If this is a read, read a byte, otherwise, write a byte */
if(pm->flags & I2C_M_RD) {
/* Wait for the previous read to finish */
ret = wait_for_txcomplete(priv);
if (ret)
return ret;
/* If this is the 2nd to last byte, send
* the TXAK signal */
if(i == len - 2) {
priv->write(®s->i2ccr, 0, MPC_I2CCR_TXAK);
}
/* If this is the last byte, send STOP */
if (i == len - 1)
mpc_i2c_stop(priv);
buf[i] = priv->read(®s->i2cdr);
} else {
/* Otherwise, it's a write */
priv->write(®s->i2cdr, buf[i], MPC_I2C_FULLREG);
/* Wait for the transaction to complete */
ret = wait_for_txcomplete(priv);
if(ret)
return ret;
/* Return error if we didn't get the ack */
if((priv->read(®s->i2csr) & MPC_I2CSR_RXAK)) {
printk(KERN_DEBUG "NO ACK!\n");
ret = -EREMOTEIO;
}
}
/* If there was an error, abort */
if(ret)
return ret;
}
return ret;
}
The problem is that the TXAK bit is set incorrectly: it should be set to a
zero when acks are to be sent and then set to a 1 just before reading the
next-to-last byte in the transfer (8555 Processor Reference Manual
(MPC8555ERM rev1), section 11.3.1.3 and also Figure 11-8).
Changes to the above code are thus:
302c302
< priv->write(®s->i2ccr, 0, MPC_I2CCR_MTX);
---
> priv->write(®s->i2ccr, 0, MPC_I2CCR_MTX | MPC_I2CCR_TXAK);
306c306
< priv->write(®s->i2ccr, 0, MPC_I2CCR_TXAK);
---
> priv->write(®s->i2ccr, MPC_I2CCR_TXAK,
MPC_I2CCR_TXAK);
324c324
< priv->write(®s->i2ccr, 0, MPC_I2CCR_TXAK);
---
> priv->write(®s->i2ccr, MPC_I2CCR_TXAK,
MPC_I2CCR_TXAK);
Thanks,
Dan.
More information about the Linuxppc-embedded
mailing list