in hid-mcp2221.c [407:551]
static int mcp_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
unsigned short flags, char read_write,
u8 command, int size,
union i2c_smbus_data *data)
{
int ret;
struct mcp2221 *mcp = i2c_get_adapdata(adapter);
hid_hw_power(mcp->hdev, PM_HINT_FULLON);
mutex_lock(&mcp->lock);
ret = mcp_set_i2c_speed(mcp);
if (ret)
goto exit;
switch (size) {
case I2C_SMBUS_QUICK:
if (read_write == I2C_SMBUS_READ)
ret = mcp_i2c_smbus_read(mcp, NULL, MCP2221_I2C_RD_DATA,
addr, 0, &data->byte);
else
ret = mcp_smbus_write(mcp, addr, command, NULL,
0, MCP2221_I2C_WR_DATA, 1);
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_READ)
ret = mcp_i2c_smbus_read(mcp, NULL, MCP2221_I2C_RD_DATA,
addr, 1, &data->byte);
else
ret = mcp_smbus_write(mcp, addr, command, NULL,
0, MCP2221_I2C_WR_DATA, 1);
break;
case I2C_SMBUS_BYTE_DATA:
if (read_write == I2C_SMBUS_READ) {
ret = mcp_smbus_write(mcp, addr, command, NULL,
0, MCP2221_I2C_WR_NO_STOP, 0);
if (ret)
goto exit;
ret = mcp_i2c_smbus_read(mcp, NULL,
MCP2221_I2C_RD_RPT_START,
addr, 1, &data->byte);
} else {
ret = mcp_smbus_write(mcp, addr, command, &data->byte,
1, MCP2221_I2C_WR_DATA, 1);
}
break;
case I2C_SMBUS_WORD_DATA:
if (read_write == I2C_SMBUS_READ) {
ret = mcp_smbus_write(mcp, addr, command, NULL,
0, MCP2221_I2C_WR_NO_STOP, 0);
if (ret)
goto exit;
ret = mcp_i2c_smbus_read(mcp, NULL,
MCP2221_I2C_RD_RPT_START,
addr, 2, (u8 *)&data->word);
} else {
ret = mcp_smbus_write(mcp, addr, command,
(u8 *)&data->word, 2,
MCP2221_I2C_WR_DATA, 1);
}
break;
case I2C_SMBUS_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
ret = mcp_smbus_write(mcp, addr, command, NULL,
0, MCP2221_I2C_WR_NO_STOP, 1);
if (ret)
goto exit;
mcp->rxbuf_idx = 0;
mcp->rxbuf = data->block;
mcp->txbuf[0] = MCP2221_I2C_GET_DATA;
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
if (ret)
goto exit;
} else {
if (!data->block[0]) {
ret = -EINVAL;
goto exit;
}
ret = mcp_smbus_write(mcp, addr, command, data->block,
data->block[0] + 1,
MCP2221_I2C_WR_DATA, 1);
}
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
ret = mcp_smbus_write(mcp, addr, command, NULL,
0, MCP2221_I2C_WR_NO_STOP, 1);
if (ret)
goto exit;
mcp->rxbuf_idx = 0;
mcp->rxbuf = data->block;
mcp->txbuf[0] = MCP2221_I2C_GET_DATA;
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
if (ret)
goto exit;
} else {
if (!data->block[0]) {
ret = -EINVAL;
goto exit;
}
ret = mcp_smbus_write(mcp, addr, command,
&data->block[1], data->block[0],
MCP2221_I2C_WR_DATA, 1);
}
break;
case I2C_SMBUS_PROC_CALL:
ret = mcp_smbus_write(mcp, addr, command,
(u8 *)&data->word,
2, MCP2221_I2C_WR_NO_STOP, 0);
if (ret)
goto exit;
ret = mcp_i2c_smbus_read(mcp, NULL,
MCP2221_I2C_RD_RPT_START,
addr, 2, (u8 *)&data->word);
break;
case I2C_SMBUS_BLOCK_PROC_CALL:
ret = mcp_smbus_write(mcp, addr, command, data->block,
data->block[0] + 1,
MCP2221_I2C_WR_NO_STOP, 0);
if (ret)
goto exit;
ret = mcp_i2c_smbus_read(mcp, NULL,
MCP2221_I2C_RD_RPT_START,
addr, I2C_SMBUS_BLOCK_MAX,
data->block);
break;
default:
dev_err(&mcp->adapter.dev,
"unsupported smbus transaction size:%d\n", size);
ret = -EOPNOTSUPP;
}
exit:
hid_hw_power(mcp->hdev, PM_HINT_NORMAL);
mutex_unlock(&mcp->lock);
return ret;
}