static int sis630_transaction_wait(struct i2c_adapter *adap, int size) { int temp, result = 0, timeout = 0; /* We will always wait for a fraction of a second! */ do { msleep(1); temp = sis630_read(SMB_STS); /* check if block transmitted */ if (size == SIS630_BLOCK_DATA && (temp & BYTE_DONE_STS)) break; } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT)); /* If the SMBus is still busy, we give up */ if (timeout > MAX_TIMEOUT) { dev_dbg(&adap->dev, "SMBus Timeout!\n"); result = -ETIMEDOUT; } if (temp & SMBERR_STS) { dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); result = -ENXIO; } if (temp & SMBCOL_STS) { dev_err(&adap->dev, "Bus collision!\n"); result = -EAGAIN; } return result; }
static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock) { int temp = 0; /* clear all status "sticky" bits */ sis630_write(SMB_STS, temp); dev_dbg(&adap->dev, "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT)); /* * restore old Host Master Clock if high_clock is set * and oldclock was not 56KHz */ if (high_clock && !(oldclock & 0x20)) sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20)); dev_dbg(&adap->dev, "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT)); }
static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldclock) { int temp; /* Make sure the SMBus host is ready to start transmitting. */ temp = sis630_read(SMB_CNT); if ((temp & (SMB_PROBE | SMB_HOSTBUSY)) != 0x00) { dev_dbg(&adap->dev, "SMBus busy (%02x). Resetting...\n", temp); /* kill smbus transaction */ sis630_write(SMBHOST_CNT, SMB_KILL); temp = sis630_read(SMB_CNT); if (temp & (SMB_PROBE | SMB_HOSTBUSY)) { dev_dbg(&adap->dev, "Failed! (%02x)\n", temp); return -EBUSY; } else { dev_dbg(&adap->dev, "Successful!\n"); } } /* save old clock, so we can prevent machine for hung */ *oldclock = sis630_read(SMB_CNT); dev_dbg(&adap->dev, "saved clock 0x%02x\n", *oldclock); /* disable timeout interrupt, * set Host Master Clock to 56KHz if requested */ if (high_clock) sis630_write(SMB_CNT, SMBCLK_SEL); else sis630_write(SMB_CNT, (*oldclock & ~MSTO_EN)); /* clear all sticky bits */ temp = sis630_read(SMB_STS); sis630_write(SMB_STS, temp & 0x1e); /* start the transaction by setting bit 4 and size */ sis630_write(SMBHOST_CNT, SMB_START | (size & 0x07)); return 0; }
static int sis630_transaction_wait(struct i2c_adapter *adap, int size) { int temp, result = 0, timeout = 0; /* We will always wait for a fraction of a second! */ do { msleep(1); temp = sis630_read(SMB_STS); /* check if block transmitted */ if (size == SIS630_BLOCK_DATA && (temp & 0x10)) break; } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT)); /* If the SMBus is still busy, we give up */ if (timeout > MAX_TIMEOUT) { dev_dbg(&adap->dev, "SMBus Timeout!\n"); result = -ETIMEDOUT; } if (temp & 0x02) { dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); result = -ENXIO; } if (temp & 0x04) { dev_err(&adap->dev, "Bus collision!\n"); result = -EIO; /* TBD: Datasheet say: the software should clear this bit and restart SMBUS operation. Should we do it or user start request again? */ } return result; }
static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *data, int read_write) { int i, len = 0, rc = 0; u8 oldclock = 0; if (read_write == I2C_SMBUS_WRITE) { len = data->block[0]; if (len < 0) len = 0; else if (len > 32) len = 32; sis630_write(SMB_COUNT, len); for (i=1; i <= len; i++) { dev_dbg(&adap->dev, "set data 0x%02x\n", data->block[i]); /* set data */ sis630_write(SMB_BYTE+(i-1)%8, data->block[i]); if (i==8 || (len<8 && i==len)) { dev_dbg(&adap->dev, "start trans len=%d i=%d\n",len ,i); /* first transaction */ rc = sis630_transaction_start(adap, SIS630_BLOCK_DATA, &oldclock); if (rc) return rc; } else if ((i-1)%8 == 7 || i==len) { dev_dbg(&adap->dev, "trans_wait len=%d i=%d\n",len,i); if (i>8) { dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i); /* If this is not first transaction, we must clear sticky bit. clear SMBARY_STS */ sis630_write(SMB_STS,0x10); } rc = sis630_transaction_wait(adap, SIS630_BLOCK_DATA); if (rc) { dev_dbg(&adap->dev, "trans_wait failed\n"); break; } } } } else { /* read request */ data->block[0] = len = 0; rc = sis630_transaction_start(adap, SIS630_BLOCK_DATA, &oldclock); if (rc) return rc; do { rc = sis630_transaction_wait(adap, SIS630_BLOCK_DATA); if (rc) { dev_dbg(&adap->dev, "trans_wait failed\n"); break; } /* if this first transaction then read byte count */ if (len == 0) data->block[0] = sis630_read(SMB_COUNT); /* just to be sure */ if (data->block[0] > 32) data->block[0] = 32; dev_dbg(&adap->dev, "block data read len=0x%x\n", data->block[0]); for (i=0; i < 8 && len < data->block[0]; i++,len++) { dev_dbg(&adap->dev, "read i=%d len=%d\n", i, len); data->block[len+1] = sis630_read(SMB_BYTE+i); } dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i); /* clear SMBARY_STS */ sis630_write(SMB_STS,0x10); } while(len < data->block[0]); } sis630_transaction_end(adap, oldclock); return rc; }