in busses/i2c-pasemi-core.c [165:313]
static int pasemi_smb_xfer(struct i2c_adapter *adapter,
u16 addr, unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data *data)
{
struct pasemi_smbus *smbus = adapter->algo_data;
unsigned int rd;
int read_flag, err;
int len = 0, i;
/* All our ops take 8-bit shifted addresses */
addr <<= 1;
read_flag = read_write == I2C_SMBUS_READ;
pasemi_smb_clear(smbus);
switch (size) {
case I2C_SMBUS_QUICK:
TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START |
MTXFIFO_STOP);
break;
case I2C_SMBUS_BYTE:
TXFIFO_WR(smbus, addr | read_flag | MTXFIFO_START);
if (read_write)
TXFIFO_WR(smbus, 1 | MTXFIFO_STOP | MTXFIFO_READ);
else
TXFIFO_WR(smbus, MTXFIFO_STOP | command);
break;
case I2C_SMBUS_BYTE_DATA:
TXFIFO_WR(smbus, addr | MTXFIFO_START);
TXFIFO_WR(smbus, command);
if (read_write) {
TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);
TXFIFO_WR(smbus, 1 | MTXFIFO_READ | MTXFIFO_STOP);
} else {
TXFIFO_WR(smbus, MTXFIFO_STOP | data->byte);
}
break;
case I2C_SMBUS_WORD_DATA:
TXFIFO_WR(smbus, addr | MTXFIFO_START);
TXFIFO_WR(smbus, command);
if (read_write) {
TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);
TXFIFO_WR(smbus, 2 | MTXFIFO_READ | MTXFIFO_STOP);
} else {
TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M);
TXFIFO_WR(smbus, MTXFIFO_STOP | (data->word >> 8));
}
break;
case I2C_SMBUS_BLOCK_DATA:
TXFIFO_WR(smbus, addr | MTXFIFO_START);
TXFIFO_WR(smbus, command);
if (read_write) {
TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);
TXFIFO_WR(smbus, 1 | MTXFIFO_READ);
rd = RXFIFO_RD(smbus);
len = min_t(u8, (rd & MRXFIFO_DATA_M),
I2C_SMBUS_BLOCK_MAX);
TXFIFO_WR(smbus, len | MTXFIFO_READ |
MTXFIFO_STOP);
} else {
len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX);
TXFIFO_WR(smbus, len);
for (i = 1; i < len; i++)
TXFIFO_WR(smbus, data->block[i]);
TXFIFO_WR(smbus, data->block[len] | MTXFIFO_STOP);
}
break;
case I2C_SMBUS_PROC_CALL:
read_write = I2C_SMBUS_READ;
TXFIFO_WR(smbus, addr | MTXFIFO_START);
TXFIFO_WR(smbus, command);
TXFIFO_WR(smbus, data->word & MTXFIFO_DATA_M);
TXFIFO_WR(smbus, (data->word >> 8) & MTXFIFO_DATA_M);
TXFIFO_WR(smbus, addr | I2C_SMBUS_READ | MTXFIFO_START);
TXFIFO_WR(smbus, 2 | MTXFIFO_STOP | MTXFIFO_READ);
break;
case I2C_SMBUS_BLOCK_PROC_CALL:
len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX - 1);
read_write = I2C_SMBUS_READ;
TXFIFO_WR(smbus, addr | MTXFIFO_START);
TXFIFO_WR(smbus, command);
TXFIFO_WR(smbus, len);
for (i = 1; i <= len; i++)
TXFIFO_WR(smbus, data->block[i]);
TXFIFO_WR(smbus, addr | I2C_SMBUS_READ);
TXFIFO_WR(smbus, MTXFIFO_READ | 1);
rd = RXFIFO_RD(smbus);
len = min_t(u8, (rd & MRXFIFO_DATA_M),
I2C_SMBUS_BLOCK_MAX - len);
TXFIFO_WR(smbus, len | MTXFIFO_READ | MTXFIFO_STOP);
break;
default:
dev_warn(&adapter->dev, "Unsupported transaction %d\n", size);
return -EINVAL;
}
err = pasemi_smb_waitready(smbus);
if (err)
goto reset_out;
if (read_write == I2C_SMBUS_WRITE)
return 0;
switch (size) {
case I2C_SMBUS_BYTE:
case I2C_SMBUS_BYTE_DATA:
rd = RXFIFO_RD(smbus);
if (rd & MRXFIFO_EMPTY) {
err = -ENODATA;
goto reset_out;
}
data->byte = rd & MRXFIFO_DATA_M;
break;
case I2C_SMBUS_WORD_DATA:
case I2C_SMBUS_PROC_CALL:
rd = RXFIFO_RD(smbus);
if (rd & MRXFIFO_EMPTY) {
err = -ENODATA;
goto reset_out;
}
data->word = rd & MRXFIFO_DATA_M;
rd = RXFIFO_RD(smbus);
if (rd & MRXFIFO_EMPTY) {
err = -ENODATA;
goto reset_out;
}
data->word |= (rd & MRXFIFO_DATA_M) << 8;
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_BLOCK_PROC_CALL:
data->block[0] = len;
for (i = 1; i <= len; i ++) {
rd = RXFIFO_RD(smbus);
if (rd & MRXFIFO_EMPTY) {
err = -ENODATA;
goto reset_out;
}
data->block[i] = rd & MRXFIFO_DATA_M;
}
break;
}
return 0;
reset_out:
pasemi_reset(smbus);
return err;
}