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; }
/* Return negative errno on error. */ static s32 sis630_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) { int status; switch (size) { case I2C_SMBUS_QUICK: sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); size = SIS630_QUICK; break; case I2C_SMBUS_BYTE: sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); if (read_write == I2C_SMBUS_WRITE) sis630_write(SMB_CMD, command); size = SIS630_BYTE; break; case I2C_SMBUS_BYTE_DATA: sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); sis630_write(SMB_CMD, command); if (read_write == I2C_SMBUS_WRITE) sis630_write(SMB_BYTE, data->byte); size = SIS630_BYTE_DATA; break; case I2C_SMBUS_PROC_CALL: case I2C_SMBUS_WORD_DATA: sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01)); sis630_write(SMB_CMD, command); if (read_write == I2C_SMBUS_WRITE) { sis630_write(SMB_BYTE, data->word & 0xff); sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8); } size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA); break; case I2C_SMBUS_BLOCK_DATA: sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01)); sis630_write(SMB_CMD, command); size = SIS630_BLOCK_DATA; return sis630_block_data(adap, data, read_write); default: dev_warn(&adap->dev, "Unsupported transaction %d\n", size); return -EOPNOTSUPP; }
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; }