* MPC85xx i2c interface bug
@ 2005-11-29 20:42 dwilson
2005-11-30 6:14 ` Kumar Gala
0 siblings, 1 reply; 6+ messages in thread
From: dwilson @ 2005-11-29 20:42 UTC (permalink / raw)
To: linuxppc-embedded
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.
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: MPC85xx i2c interface bug 2005-11-29 20:42 MPC85xx i2c interface bug dwilson @ 2005-11-30 6:14 ` Kumar Gala 2005-11-30 7:18 ` Dan Wilson 2005-12-03 17:22 ` Dan Wilson 0 siblings, 2 replies; 6+ messages in thread From: Kumar Gala @ 2005-11-30 6:14 UTC (permalink / raw) To: dwilson; +Cc: linuxppc-embedded Dan, Did you see an issue this change fixed? If so can you provide more details. Also, can you provide your diff as a unified diff (diff -u) so its easier to see where the changes where. I'm trying to figure out if the same issue exists in the 2.6 driver (and if so, why we havent seen it) thanks - kumar On Tue, 29 Nov 2005 dwilson@dslextreme.com wrote: > 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. > > _______________________________________________ > Linuxppc-embedded mailing list > Linuxppc-embedded@ozlabs.org > https://ozlabs.org/mailman/listinfo/linuxppc-embedded > ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: MPC85xx i2c interface bug 2005-11-30 6:14 ` Kumar Gala @ 2005-11-30 7:18 ` Dan Wilson 2005-12-06 17:24 ` Kumar Gala 2005-12-03 17:22 ` Dan Wilson 1 sibling, 1 reply; 6+ messages in thread From: Dan Wilson @ 2005-11-30 7:18 UTC (permalink / raw) To: Kumar Gala; +Cc: linuxppc-embedded On 11/30/2005 at 12:14 AM Kumar Gala wrote: > Dan, > > Did you see an issue this change fixed? If so can you provide more > details. Also, can you provide your diff as a unified diff (diff -u) so > its easier to see where the changes where. > > I'm trying to figure out if the same issue exists in the 2.6 driver (and > if so, why we havent seen it) > > thanks > > - kumar > Yes, there was an issue that this change fixed. Our I2C bus has a number= of devices on it. The first device is at address 0x2c, and is an Analog= Device AD5173BRM50 software programmable 50K ohm resistor. We connected a= logic analyzer to the device and watched the bus activity as linux booted= and the i2c bus scan took place. During this scan, the 8541 attempts to= address each device and then read a byte from the device. With the= original code, the 8541 would reply to the byte read by sending a zero as= the TXACK bit, which instructed the 5173 to send an additional byte, but= the 8541 didn't attempt to retrieve that byte, since it was already moving= on, trying to stop the bus and go on to the next device. The 5173= appeared to not be able to see the stop command since it had received a= command to transmit the next byte. The bus didn't seem to ever recover= from this: if we allowed linux to complete it's boot and then told it to= reboot, the u-boot code was no longer able to identify and configure the= SDRAM, since it is also connected to the now non-functional I2C. With the= new code, linux correctly identifies all of the devices on the I2C, boots= quickly and cleanly, and after a reboot the u-boot code has no problem= coming up again. Here is the diff -u that you requested: @@ -299,11 +299,11 @@ if(pm->flags & I2C_M_RD) { /* Change to read mode */ - priv->write(®s->i2ccr, 0, MPC_I2CCR_MTX); + priv->write(®s->i2ccr, 0, MPC_I2CCR_MTX |= MPC_I2CCR_TXAK); /* If there is only one byte, we need to TXAK now */ if(len =3D=3D 1) - priv->write(®s->i2ccr, 0, MPC_I2CCR_TXAK); + priv->write(®s->i2ccr, MPC_I2CCR_TXAK,= MPC_I2CCR_TXAK); /* Do a dummy read, to initiate the first read */ priv->read(®s->i2cdr); @@ -321,7 +321,7 @@ /* If this is the 2nd to last byte, send * the TXAK signal */ if(i =3D=3D len - 2) { - priv->write(®s->i2ccr, 0,= MPC_I2CCR_TXAK); + priv->write(®s->i2ccr, MPC_I2CCR_TXAK,= MPC_I2CCR_TXAK); } /* If this is the last byte, send STOP */ @@ -383,7 +383,6 @@ priv->write(®s->i2csr, 0, MPC_I2CSR_MIF); mpc_i2c_start(priv); - /* Send each message, chaining them together with repeat STARTs */ for(i =3D 0; i < num && !err; ++i) { err =3D mpc_doAddress(priv, &msgs[i]); Hope this helps, Dan. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: MPC85xx i2c interface bug 2005-11-30 7:18 ` Dan Wilson @ 2005-12-06 17:24 ` Kumar Gala 2005-12-07 4:59 ` Dan Wilson 0 siblings, 1 reply; 6+ messages in thread From: Kumar Gala @ 2005-12-06 17:24 UTC (permalink / raw) To: Dan Wilson; +Cc: ppc list Dan, I'm in agreement with your change for the 2.4 kernel. It looks like the 2.6 kernel driver is handing TXAK correctly. The following is a snippet from the 2.6 driver: if (length) { if (length == 1) writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK); else writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA); /* Dummy read */ readb(i2c->base + MPC_I2C_DR); } for (i = 0; i < length; i++) { if (i2c_wait(i2c, timeout, 0) < 0) return -1; /* Generate txack on next to last byte */ if (i == length - 2) writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK); /* Generate stop on last byte */ if (i == length - 1) writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_TXAK); data[i] = readb(i2c->base + MPC_I2C_DR); } If I'm reading it correctly it matches your changes. If you dont mind looking at this and verifying that you agree that we are setting TXAK as expected. - kumar On Nov 30, 2005, at 1:18 AM, Dan Wilson wrote: > On 11/30/2005 at 12:14 AM Kumar Gala wrote: > >> Dan, >> >> Did you see an issue this change fixed? If so can you provide more >> details. Also, can you provide your diff as a unified diff (diff - >> u) so >> its easier to see where the changes where. >> >> I'm trying to figure out if the same issue exists in the 2.6 >> driver (and >> if so, why we havent seen it) >> >> thanks >> >> - kumar >> > > Yes, there was an issue that this change fixed. Our I2C bus has a > number of devices on it. The first device is at address 0x2c, and > is an Analog Device AD5173BRM50 software programmable 50K ohm > resistor. We connected a logic analyzer to the device and watched > the bus activity as linux booted and the i2c bus scan took place. > During this scan, the 8541 attempts to address each device and then > read a byte from the device. With the original code, the 8541 > would reply to the byte read by sending a zero as the TXACK bit, > which instructed the 5173 to send an additional byte, but the 8541 > didn't attempt to retrieve that byte, since it was already moving > on, trying to stop the bus and go on to the next device. The 5173 > appeared to not be able to see the stop command since it had > received a command to transmit the next byte. The bus didn't seem > to ever recover from this: if we allowed linux to complete it's > boot and then told it to reboot, the u-boot code was no longer able > to identify and configure the SDRAM, since it is also connected to > the now non-functional I2C. With the new code, linux correctly > identifies all of the devices on the I2C, boots quickly and > cleanly, and after a reboot the u-boot code has no problem coming > up again. > > Here is the diff -u that you requested: > > @@ -299,11 +299,11 @@ > > if(pm->flags & I2C_M_RD) { > /* Change to read mode */ > - priv->write(®s->i2ccr, 0, MPC_I2CCR_MTX); > + priv->write(®s->i2ccr, 0, MPC_I2CCR_MTX | > MPC_I2CCR_TXAK); > > /* If there is only one byte, we need to TXAK now */ > if(len == 1) > - priv->write(®s->i2ccr, 0, MPC_I2CCR_TXAK); > + priv->write(®s->i2ccr, MPC_I2CCR_TXAK, > MPC_I2CCR_TXAK); > > /* Do a dummy read, to initiate the first read */ > priv->read(®s->i2cdr); > @@ -321,7 +321,7 @@ > /* If this is the 2nd to last byte, send > * the TXAK signal */ > if(i == len - 2) { > - priv->write(®s->i2ccr, 0, > MPC_I2CCR_TXAK); > + priv->write(®s->i2ccr, > MPC_I2CCR_TXAK, MPC_I2CCR_TXAK); > } > > /* If this is the last byte, send STOP */ > @@ -383,7 +383,6 @@ > priv->write(®s->i2csr, 0, MPC_I2CSR_MIF); > > mpc_i2c_start(priv); > - > /* Send each message, chaining them together with repeat > STARTs */ > for(i = 0; i < num && !err; ++i) { > err = mpc_doAddress(priv, &msgs[i]); > > > Hope this helps, > > Dan. > > > _______________________________________________ > Linuxppc-embedded mailing list > Linuxppc-embedded@ozlabs.org > https://ozlabs.org/mailman/listinfo/linuxppc-embedded ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: MPC85xx i2c interface bug 2005-12-06 17:24 ` Kumar Gala @ 2005-12-07 4:59 ` Dan Wilson 0 siblings, 0 replies; 6+ messages in thread From: Dan Wilson @ 2005-12-07 4:59 UTC (permalink / raw) To: Kumar Gala; +Cc: ppc list On 12/6/2005 at 11:24 AM Kumar Gala wrote: > Dan, > > I'm in agreement with your change for the 2.4 kernel. It looks like > the 2.6 kernel driver is handing TXAK correctly. The following is a > snippet from the 2.6 driver: > > if (length) { > if (length == 1) > writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA > | CCR_TXAK); > else > writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA); > /* Dummy read */ > readb(i2c->base + MPC_I2C_DR); > } > > for (i = 0; i < length; i++) { > if (i2c_wait(i2c, timeout, 0) < 0) > return -1; > > /* Generate txack on next to last byte */ > if (i == length - 2) > writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA > | CCR_TXAK); > /* Generate stop on last byte */ > if (i == length - 1) > writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_TXAK); > data[i] = readb(i2c->base + MPC_I2C_DR); > } > > > If I'm reading it correctly it matches your changes. If you dont > mind looking at this and verifying that you agree that we are setting > TXAK as expected. > > - kumar > Kumar, Yes, the 2.6 code looks good to me too. Thanks much for your help! Dan. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: MPC85xx i2c interface bug 2005-11-30 6:14 ` Kumar Gala 2005-11-30 7:18 ` Dan Wilson @ 2005-12-03 17:22 ` Dan Wilson 1 sibling, 0 replies; 6+ messages in thread From: Dan Wilson @ 2005-12-03 17:22 UTC (permalink / raw) To: Kumar Gala; +Cc: linuxppc-embedded On 11/30/2005 at 12:14 AM Kumar Gala wrote: > Dan, > > Did you see an issue this change fixed? If so can you provide more > details. Also, can you provide your diff as a unified diff (diff -u) so > its easier to see where the changes where. > > I'm trying to figure out if the same issue exists in the 2.6 driver (and > if so, why we havent seen it) > > thanks > > - kumar > Kumar, Did my last message provide all the information you needed? If you= disagree with my proposed patch, I would greatly appreciate hearing your= thoughts. If you need anything else, please let me know. Regards, Dan. ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2005-12-07 4:58 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2005-11-29 20:42 MPC85xx i2c interface bug dwilson 2005-11-30 6:14 ` Kumar Gala 2005-11-30 7:18 ` Dan Wilson 2005-12-06 17:24 ` Kumar Gala 2005-12-07 4:59 ` Dan Wilson 2005-12-03 17:22 ` Dan Wilson
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox