in brcmstb-avs-cpufreq.c [224:314]
static int __issue_avs_command(struct private_data *priv, unsigned int cmd,
unsigned int num_in, unsigned int num_out,
u32 args[])
{
void __iomem *base = priv->base;
unsigned long time_left;
unsigned int i;
int ret;
u32 val;
ret = down_interruptible(&priv->sem);
if (ret)
return ret;
/*
* Make sure no other command is currently running: cmd is 0 if AVS
* co-processor is idle. Due to the guard above, we should almost never
* have to wait here.
*/
for (i = 0, val = 1; val != 0 && i < AVS_LOOP_LIMIT; i++)
val = readl(base + AVS_MBOX_COMMAND);
/* Give the caller a chance to retry if AVS is busy. */
if (i == AVS_LOOP_LIMIT) {
ret = -EAGAIN;
goto out;
}
/* Clear status before we begin. */
writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS);
/* Provide input parameters */
for (i = 0; i < num_in; i++)
writel(args[i], base + AVS_MBOX_PARAM(i));
/* Protect from spurious interrupts. */
reinit_completion(&priv->done);
/* Now issue the command & tell firmware to wake up to process it. */
writel(cmd, base + AVS_MBOX_COMMAND);
writel(AVS_CPU_L2_INT_MASK, priv->avs_intr_base + AVS_CPU_L2_SET0);
/* Wait for AVS co-processor to finish processing the command. */
time_left = wait_for_avs_command(priv, AVS_TIMEOUT);
/*
* If the AVS status is not in the expected range, it means AVS didn't
* complete our command in time, and we return an error. Also, if there
* is no "time left", we timed out waiting for the interrupt.
*/
val = readl(base + AVS_MBOX_STATUS);
if (time_left == 0 || val == 0 || val > AVS_STATUS_MAX) {
dev_err(priv->dev, "AVS command %#x didn't complete in time\n",
cmd);
dev_err(priv->dev, " Time left: %u ms, AVS status: %#x\n",
jiffies_to_msecs(time_left), val);
ret = -ETIMEDOUT;
goto out;
}
/* Process returned values */
for (i = 0; i < num_out; i++)
args[i] = readl(base + AVS_MBOX_PARAM(i));
/* Clear status to tell AVS co-processor we are done. */
writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS);
/* Convert firmware errors to errno's as much as possible. */
switch (val) {
case AVS_STATUS_INVALID:
ret = -EINVAL;
break;
case AVS_STATUS_NO_SUPP:
ret = -ENOTSUPP;
break;
case AVS_STATUS_NO_MAP:
ret = -ENOENT;
break;
case AVS_STATUS_MAP_SET:
ret = -EEXIST;
break;
case AVS_STATUS_FAILURE:
ret = -EIO;
break;
}
out:
up(&priv->sem);
return ret;
}