aboutsummaryrefslogtreecommitdiff
path: root/drivers/firmware/arm_scpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firmware/arm_scpi.c')
-rw-r--r--drivers/firmware/arm_scpi.c56
1 files changed, 43 insertions, 13 deletions
diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c
index f6cfc31d34c7..8d39da71d6b9 100644
--- a/drivers/firmware/arm_scpi.c
+++ b/drivers/firmware/arm_scpi.c
@@ -87,8 +87,6 @@
#define FW_REV_MINOR(x) (((x) & FW_REV_MINOR_MASK) >> FW_REV_MINOR_BITS)
#define FW_REV_PATCH(x) ((x) & FW_REV_PATCH_MASK)
-#define MAX_RX_TIMEOUT (msecs_to_jiffies(30))
-
enum scpi_error_codes {
SCPI_SUCCESS = 0, /* Success */
SCPI_ERR_PARAM = 1, /* Invalid parameter(s) */
@@ -480,6 +478,18 @@ static void scpi_tx_prepare(struct mbox_client *c, void *msg)
mem->command = cpu_to_le32(t->cmd);
}
+static void scpi_tx_done(struct mbox_client *c, void *msg, int result)
+{
+ struct scpi_xfer *t = msg;
+
+ if (!t->rx_buf)
+ complete(&t->done);
+ /*
+ * Messages with rx_buf are expecting a reply and will be on the
+ * rx_pending list, so leave them alone.
+ */
+}
+
static struct scpi_xfer *get_scpi_xfer(struct scpi_chan *ch)
{
struct scpi_xfer *t;
@@ -541,17 +551,24 @@ static int scpi_send_message(u8 idx, void *tx_buf, unsigned int tx_len,
reinit_completion(&msg->done);
ret = mbox_send_message(scpi_chan->chan, msg);
- if (ret < 0 || !rx_buf)
- goto out;
+ if (ret >= 0) {
+ /*
+ * Wait for message to be processed. If we end up having to wait
+ * for a very long time then there is a serious bug, probably in
+ * the firmware.
+ *
+ * IMPORTANT: We must not try and continue after the timeout
+ * because this driver and the mailbox framework still has data
+ * structures referring to the failed request and further
+ * serious bugs will result.
+ */
+ if (!wait_for_completion_timeout(&msg->done, msecs_to_jiffies(10000)))
+ BUG();
- if (!wait_for_completion_timeout(&msg->done, MAX_RX_TIMEOUT))
- ret = -ETIMEDOUT;
- else
/* first status word */
- ret = msg->status;
-out:
- if (ret < 0 && rx_buf) /* remove entry from the list if timed-out */
- scpi_process_cmd(scpi_chan, msg->cmd);
+ if (rx_buf)
+ ret = le32_to_cpu(msg->status);
+ }
put_scpi_xfer(msg, scpi_chan);
/* SCPI error codes > 0, translate them to Linux scale*/
@@ -642,6 +659,8 @@ static int opp_cmp_func(const void *opp1, const void *opp2)
return t1->freq - t2->freq;
}
+static bool juno_cpufreq_limit_hack = 0;
+
static struct scpi_dvfs_info *scpi_dvfs_get_info(u8 domain)
{
struct scpi_dvfs_info *info;
@@ -680,6 +699,14 @@ static struct scpi_dvfs_info *scpi_dvfs_get_info(u8 domain)
sort(info->opps, info->count, sizeof(*opp), opp_cmp_func, NULL);
+ /*
+ * Juno silicon doesn't seem to be able to run the big cluster
+ * (domain == 0) at max frequency in AArch32 mode (it produces
+ * random and weird crashes) so drop the highest OPP in that case...
+ */
+ if (juno_cpufreq_limit_hack && domain == 0)
+ --info->count;
+
scpi_info->dvfs[domain] = info;
return info;
}
@@ -893,6 +920,10 @@ static int scpi_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
+ if (IS_ENABLED(CONFIG_ARM) && of_find_compatible_node(NULL,NULL,"arm,juno")) {
+ juno_cpufreq_limit_hack = true;
+ }
+
scpi_info = devm_kzalloc(dev, sizeof(*scpi_info), GFP_KERNEL);
if (!scpi_info)
return -ENOMEM;
@@ -935,8 +966,7 @@ static int scpi_probe(struct platform_device *pdev)
cl->dev = dev;
cl->rx_callback = scpi_handle_remote_msg;
cl->tx_prepare = scpi_tx_prepare;
- cl->tx_block = true;
- cl->tx_tout = 20;
+ cl->tx_done = scpi_tx_done;
cl->knows_txdone = false; /* controller can't ack */
INIT_LIST_HEAD(&pchan->rx_pending);