in arm_scmi/voltage.c [113:215]
static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
struct voltage_info *vinfo)
{
int ret, dom;
struct scmi_xfer *td, *tl;
struct device *dev = ph->dev;
struct scmi_msg_resp_domain_attributes *resp_dom;
struct scmi_msg_resp_describe_levels *resp_levels;
ret = ph->xops->xfer_get_init(ph, VOLTAGE_DOMAIN_ATTRIBUTES,
sizeof(__le32), sizeof(*resp_dom), &td);
if (ret)
return ret;
resp_dom = td->rx.buf;
ret = ph->xops->xfer_get_init(ph, VOLTAGE_DESCRIBE_LEVELS,
sizeof(__le64), 0, &tl);
if (ret)
goto outd;
resp_levels = tl->rx.buf;
for (dom = 0; dom < vinfo->num_domains; dom++) {
u32 desc_index = 0;
u16 num_returned = 0, num_remaining = 0;
struct scmi_msg_cmd_describe_levels *cmd;
struct scmi_voltage_info *v;
/* Retrieve domain attributes at first ... */
put_unaligned_le32(dom, td->tx.buf);
ret = ph->xops->do_xfer(ph, td);
/* Skip domain on comms error */
if (ret)
continue;
v = vinfo->domains + dom;
v->id = dom;
v->attributes = le32_to_cpu(resp_dom->attr);
strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE);
cmd = tl->tx.buf;
/* ...then retrieve domain levels descriptions */
do {
u32 flags;
int cnt;
cmd->domain_id = cpu_to_le32(v->id);
cmd->level_index = cpu_to_le32(desc_index);
ret = ph->xops->do_xfer(ph, tl);
if (ret)
break;
flags = le32_to_cpu(resp_levels->flags);
num_returned = NUM_RETURNED_LEVELS(flags);
num_remaining = NUM_REMAINING_LEVELS(flags);
/* Allocate space for num_levels if not already done */
if (!v->num_levels) {
ret = scmi_init_voltage_levels(dev, v,
num_returned,
num_remaining,
SUPPORTS_SEGMENTED_LEVELS(flags));
if (ret)
break;
}
if (desc_index + num_returned > v->num_levels) {
dev_err(ph->dev,
"No. of voltage levels can't exceed %d\n",
v->num_levels);
ret = -EINVAL;
break;
}
for (cnt = 0; cnt < num_returned; cnt++) {
s32 val;
val =
(s32)le32_to_cpu(resp_levels->voltage[cnt]);
v->levels_uv[desc_index + cnt] = val;
if (val < 0)
v->negative_volts_allowed = true;
}
desc_index += num_returned;
ph->xops->reset_rx_to_maxsz(ph, tl);
/* check both to avoid infinite loop due to buggy fw */
} while (num_returned && num_remaining);
if (ret) {
v->num_levels = 0;
devm_kfree(dev, v->levels_uv);
}
ph->xops->reset_rx_to_maxsz(ph, td);
}
ph->xops->xfer_put(ph, tl);
outd:
ph->xops->xfer_put(ph, td);
return ret;
}