/* this function returns the status-register of our i2c-device */ staticinline u32 saa7146_i2c_status(struct saa7146_dev *dev)
{
u32 iicsta = saa7146_read(dev, I2C_STATUS); /* DEB_I2C("status: 0x%08x\n", iicsta); */ return iicsta;
}
/* this function runs through the i2c-messages and prepares the data to be sentthroughthesaa7146.havealookatthespecificationsp.122ff tounderstandthis.itreturnsthenumberofu32stosend,or-1
in case of an error. */ staticint saa7146_i2c_msg_prepare(conststruct i2c_msg *m, int num, __le32 *op)
{ int h1, h2; int i, j, addr; int mem = 0, op_count = 0;
/* first determine size of needed memory */ for(i = 0; i < num; i++) {
mem += m[i].len + 1;
}
/* worst case: we need one u32 for three bytes to be send
plus one extra byte to address the device */
mem = 1 + ((mem-1) / 3);
/* we assume that op points to a memory of at least *SAA7146_I2C_MEMbytessize.ifweexceedthislimit...
*/ if ((4 * mem) > SAA7146_I2C_MEM) { /* DEB_I2C("cannot prepare i2c-message\n"); */ return -ENOMEM;
}
/* be careful: clear out the i2c-mem first */
memset(op,0,sizeof(__le32)*mem);
/* loop through all messages */ for(i = 0; i < num; i++) {
/* loop through all bytes of message i */ for(j = 0; j < m[i].len; j++) { /* insert the data bytes */
h1 = op_count/3; h2 = op_count%3;
op[h1] |= cpu_to_le32( (u32)((u8)m[i].buf[j]) << ((3-h2)*8));
op[h1] |= cpu_to_le32( SAA7146_I2C_CONT << ((3-h2)*2));
op_count++;
}
}
/* have a look at the last byte inserted:
if it was: ...CONT change it to ...STOP */
h1 = (op_count-1)/3; h2 = (op_count-1)%3; if ( SAA7146_I2C_CONT == (0x3 & (le32_to_cpu(op[h1]) >> ((3-h2)*2))) ) {
op[h1] &= ~cpu_to_le32(0x2 << ((3-h2)*2));
op[h1] |= cpu_to_le32(SAA7146_I2C_STOP << ((3-h2)*2));
}
/* return the number of u32s to send */ return mem;
}
/* this functions loops through all i2c-messages. normally, it should determine whichbyteswerereadthroughtheadapterandwritethembacktothecorresponding i2c-message.butinstead,wesimplywritebackallbytes.
fixme: this could be improved. */ staticint saa7146_i2c_msg_cleanup(conststruct i2c_msg *m, int num, __le32 *op)
{ int i, j; int op_count = 0;
/* loop through all messages */ for(i = 0; i < num; i++) {
op_count++;
/* loop through all bytes of message i */ for(j = 0; j < m[i].len; j++) { /* write back all bytes that could have been read */
m[i].buf[j] = (le32_to_cpu(op[op_count/3]) >> ((3-(op_count%3))*8));
op_count++;
}
}
return0;
}
/* this functions resets the i2c-device and returns 0 if everything was fine, otherwise -1 */ staticint saa7146_i2c_reset(struct saa7146_dev *dev)
{ /* get current status */
u32 status = saa7146_i2c_status(dev);
/* Repeat the abort operation. This seems to be necessary
after serious protocol errors caused by e.g. the SAA7740 */
saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
msleep(SAA7146_I2C_DELAY);
/* the data sheet says it might be necessary to clear the status
twice after an abort */
saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
msleep(SAA7146_I2C_DELAY);
}
/* if any error is still present, a fatal error has occurred ... */
status = saa7146_i2c_status(dev); if ( dev->i2c_bitrate != status ) {
DEB_I2C("fatal error. status:0x%08x\n", status); return -1;
}
return0;
}
/* this functions writes out the data-byte 'dword' to the i2c-device. itreturns0ifok,-1ifthetransferfailed,-2ifthetransfer
failed badly (e.g. address error) */ staticint saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int short_delay)
{
u32 status = 0, mc2 = 0; int trial = 0; unsignedlong timeout;
timeout = HZ/100 + 1; /* 10ms */
timeout = wait_event_interruptible_timeout(dev->i2c_wq, dev->i2c_op == 0, timeout); if (timeout == -ERESTARTSYS || dev->i2c_op) {
SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17); if (timeout == -ERESTARTSYS) /* a signal arrived */ return -ERESTARTSYS;
pr_warn("%s %s [irq]: timed out waiting for end of xfer\n",
dev->name, __func__); return -EIO;
}
status = saa7146_read(dev, I2C_STATUS);
} else {
saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword));
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
/* do not poll for i2c-status before upload is complete */
timeout = jiffies + HZ/100 + 1; /* 10ms */ while(1) {
mc2 = (saa7146_read(dev, MC2) & 0x1); if( 0 != mc2 ) { break;
} if (time_after(jiffies,timeout)) {
pr_warn("%s %s: timed out waiting for MC2\n",
dev->name, __func__); return -EIO;
}
} /* wait until we get a transfer done or error */
timeout = jiffies + HZ/100 + 1; /* 10ms */ /* first read usually delivers bogus results... */
saa7146_i2c_status(dev); while(1) {
status = saa7146_i2c_status(dev); if ((status & 0x3) != 1) break; if (time_after(jiffies,timeout)) { /* this is normal when probing the bus *(noanswerfromnonexisistantdevice...)
*/
pr_warn("%s %s [poll]: timed out waiting for end of xfer\n",
dev->name, __func__); return -EIO;
} if (++trial < 50 && short_delay)
udelay(10); else
msleep(1);
}
}
/* give a detailed status report */ if ( 0 != (status & (SAA7146_I2C_SPERR | SAA7146_I2C_APERR |
SAA7146_I2C_DTERR | SAA7146_I2C_DRERR |
SAA7146_I2C_AL | SAA7146_I2C_ERR |
SAA7146_I2C_BUSY)) ) {
if ( 0 == (status & SAA7146_I2C_ERR) || 0 == (status & SAA7146_I2C_BUSY) ) { /* it may take some time until ERR goes high - ignore */
DEB_I2C("unexpected i2c status %04x\n", status);
} if( 0 != (status & SAA7146_I2C_SPERR) ) {
DEB_I2C("error due to invalid start/stop condition\n");
} if( 0 != (status & SAA7146_I2C_DTERR) ) {
DEB_I2C("error in data transmission\n");
} if( 0 != (status & SAA7146_I2C_DRERR) ) {
DEB_I2C("error when receiving data\n");
} if( 0 != (status & SAA7146_I2C_AL) ) {
DEB_I2C("error because arbitration lost\n");
}
/* we handle address-errors here */ if( 0 != (status & SAA7146_I2C_APERR) ) {
DEB_I2C("error in address phase\n"); return -EREMOTEIO;
}
return -EIO;
}
/* read back data, just in case we were reading ... */
*dword = cpu_to_le32(saa7146_read(dev, I2C_TRANSFER));
DEB_I2C("after: 0x%08x\n", *dword); return0;
}
staticint saa7146_i2c_transfer(struct saa7146_dev *dev, conststruct i2c_msg *msgs, int num, int retries)
{ int i = 0, count = 0;
__le32 *buffer = dev->d_i2c.cpu_addr; int err = 0; int short_delay = 0;
if (mutex_lock_interruptible(&dev->i2c_lock)) return -ERESTARTSYS;
/* prepare the message(s), get number of u32s to transfer */
count = saa7146_i2c_msg_prepare(msgs, num, buffer); if ( 0 > count ) {
err = -EIO; goto out;
}
do { /* reset the i2c-device if necessary */
err = saa7146_i2c_reset(dev); if ( 0 > err ) {
DEB_I2C("could not reset i2c-device\n"); goto out;
}
/* write out the u32s one after another */ for(i = 0; i < count; i++) {
err = saa7146_i2c_writeout(dev, &buffer[i], short_delay); if ( 0 != err) { /* this one is unsatisfying: some i2c slaves on some dvbcardsdon'tacknowledgecorrectly,sothesaa7146 thinksthatanaddresserroroccurred.inthatcase,the transactionshouldberetrying,evenifanaddresserror occurred.analogsaa7146basedcardsextensivelyrelyon i2caddressprobing,however,andaddresserrorsindicatethata deviceisreally*not*there.retryinginthatcase increasesthetimethedeviceneedstoprobegreatly,so itshouldbeavoided.Sowebailoutinirqmodeafteran
address error and trust the saa7146 address error detection. */ if (-EREMOTEIO == err && 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) goto out;
DEB_I2C("error while sending message(s). starting again\n"); break;
}
} if( 0 == err ) {
err = num; break;
}
/* delay a bit before retrying */
msleep(10);
} while (err != num && retries--);
/* quit if any error occurred */ if (err != num) goto out;
/* if any things had to be read, get the results */ if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) {
DEB_I2C("could not cleanup i2c-message\n");
err = -EIO; goto out;
}
/* return the number of delivered messages */
DEB_I2C("transmission successful. (msg:%d)\n", err);
out: /* another bug in revision 0: the i2c-registers get uploaded randomly by other
uploads, so we better clear them out before continuing */ if( 0 == dev->revision ) {
__le32 zero = 0;
saa7146_i2c_reset(dev); if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) {
pr_info("revision 0 error. this should never happen\n");
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.