in smu.c [824:899]
int smu_queue_i2c(struct smu_i2c_cmd *cmd)
{
unsigned long flags;
if (smu == NULL)
return -ENODEV;
/* Fill most fields of scmd */
cmd->scmd.cmd = SMU_CMD_I2C_COMMAND;
cmd->scmd.done = smu_i2c_low_completion;
cmd->scmd.misc = cmd;
cmd->scmd.reply_buf = cmd->pdata;
cmd->scmd.reply_len = sizeof(cmd->pdata);
cmd->scmd.data_buf = (u8 *)(char *)&cmd->info;
cmd->scmd.status = 1;
cmd->stage = 0;
cmd->pdata[0] = 0xff;
cmd->retries = 20;
cmd->status = 1;
/* Check transfer type, sanitize some "info" fields
* based on transfer type and do more checking
*/
cmd->info.caddr = cmd->info.devaddr;
cmd->read = cmd->info.devaddr & 0x01;
switch(cmd->info.type) {
case SMU_I2C_TRANSFER_SIMPLE:
cmd->info.sublen = 0;
memset(cmd->info.subaddr, 0, sizeof(cmd->info.subaddr));
break;
case SMU_I2C_TRANSFER_COMBINED:
cmd->info.devaddr &= 0xfe;
fallthrough;
case SMU_I2C_TRANSFER_STDSUB:
if (cmd->info.sublen > 3)
return -EINVAL;
break;
default:
return -EINVAL;
}
/* Finish setting up command based on transfer direction
*/
if (cmd->read) {
if (cmd->info.datalen > SMU_I2C_READ_MAX)
return -EINVAL;
memset(cmd->info.data, 0xff, cmd->info.datalen);
cmd->scmd.data_len = 9;
} else {
if (cmd->info.datalen > SMU_I2C_WRITE_MAX)
return -EINVAL;
cmd->scmd.data_len = 9 + cmd->info.datalen;
}
DPRINTK("SMU: i2c enqueuing command\n");
DPRINTK("SMU: %s, len=%d bus=%x addr=%x sub0=%x type=%x\n",
cmd->read ? "read" : "write", cmd->info.datalen,
cmd->info.bus, cmd->info.caddr,
cmd->info.subaddr[0], cmd->info.type);
/* Enqueue command in i2c list, and if empty, enqueue also in
* main command list
*/
spin_lock_irqsave(&smu->lock, flags);
if (smu->cmd_i2c_cur == NULL) {
smu->cmd_i2c_cur = cmd;
list_add_tail(&cmd->scmd.link, &smu->cmd_list);
if (smu->cmd_cur == NULL)
smu_start_cmd();
} else
list_add_tail(&cmd->link, &smu->cmd_i2c_list);
spin_unlock_irqrestore(&smu->lock, flags);
return 0;
}