aboutsummaryrefslogtreecommitdiff
path: root/drivers/soc/microchip/mpfs-sys-controller.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/soc/microchip/mpfs-sys-controller.c')
-rw-r--r--drivers/soc/microchip/mpfs-sys-controller.c56
1 files changed, 38 insertions, 18 deletions
diff --git a/drivers/soc/microchip/mpfs-sys-controller.c b/drivers/soc/microchip/mpfs-sys-controller.c
index 6e20207b5756..216d9f4ea0ce 100644
--- a/drivers/soc/microchip/mpfs-sys-controller.c
+++ b/drivers/soc/microchip/mpfs-sys-controller.c
@@ -11,12 +11,19 @@
#include <linux/slab.h>
#include <linux/kref.h>
#include <linux/module.h>
+#include <linux/jiffies.h>
#include <linux/interrupt.h>
#include <linux/of_platform.h>
#include <linux/mailbox_client.h>
#include <linux/platform_device.h>
#include <soc/microchip/mpfs.h>
+/*
+ * This timeout must be long, as some services (example: image authentication)
+ * take significant time to complete
+ */
+#define MPFS_SYS_CTRL_TIMEOUT_MS 30000
+
static DEFINE_MUTEX(transaction_lock);
struct mpfs_sys_controller {
@@ -28,35 +35,47 @@ struct mpfs_sys_controller {
int mpfs_blocking_transaction(struct mpfs_sys_controller *sys_controller, struct mpfs_mss_msg *msg)
{
- int ret, err;
+ unsigned long timeout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS);
+ int ret;
- err = mutex_lock_interruptible(&transaction_lock);
- if (err)
- return err;
+ ret = mutex_lock_interruptible(&transaction_lock);
+ if (ret)
+ return ret;
reinit_completion(&sys_controller->c);
ret = mbox_send_message(sys_controller->chan, msg);
- if (ret >= 0) {
- if (wait_for_completion_timeout(&sys_controller->c, HZ)) {
- ret = 0;
- } else {
- ret = -ETIMEDOUT;
- dev_warn(sys_controller->client.dev,
- "MPFS sys controller transaction timeout\n");
- }
+ if (ret < 0) {
+ dev_warn(sys_controller->client.dev, "MPFS sys controller service timeout\n");
+ goto out;
+ }
+
+ /*
+ * Unfortunately, the system controller will only deliver an interrupt
+ * if a service succeeds. mbox_send_message() will block until the busy
+ * flag is gone. If the busy flag is gone but no interrupt has arrived
+ * to trigger the rx callback then the service can be deemed to have
+ * failed.
+ * The caller can then interrogate msg::response::resp_status to
+ * determine the cause of the failure.
+ * mbox_send_message() returns positive integers in the success path, so
+ * ret needs to be cleared if we do get an interrupt.
+ */
+ if (!wait_for_completion_timeout(&sys_controller->c, timeout)) {
+ ret = -EBADMSG;
+ dev_warn(sys_controller->client.dev, "MPFS sys controller service failed\n");
} else {
- dev_err(sys_controller->client.dev,
- "mpfs sys controller transaction returned %d\n", ret);
+ ret = 0;
}
+out:
mutex_unlock(&transaction_lock);
return ret;
}
EXPORT_SYMBOL(mpfs_blocking_transaction);
-static void rx_callback(struct mbox_client *client, void *msg)
+static void mpfs_sys_controller_rx_callback(struct mbox_client *client, void *msg)
{
struct mpfs_sys_controller *sys_controller =
container_of(client, struct mpfs_sys_controller, client);
@@ -66,8 +85,8 @@ static void rx_callback(struct mbox_client *client, void *msg)
static void mpfs_sys_controller_delete(struct kref *kref)
{
- struct mpfs_sys_controller *sys_controller = container_of(kref, struct mpfs_sys_controller,
- consumers);
+ struct mpfs_sys_controller *sys_controller =
+ container_of(kref, struct mpfs_sys_controller, consumers);
mbox_free_channel(sys_controller->chan);
kfree(sys_controller);
@@ -102,8 +121,9 @@ static int mpfs_sys_controller_probe(struct platform_device *pdev)
return -ENOMEM;
sys_controller->client.dev = dev;
- sys_controller->client.rx_callback = rx_callback;
+ sys_controller->client.rx_callback = mpfs_sys_controller_rx_callback;
sys_controller->client.tx_block = 1U;
+ sys_controller->client.tx_tout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS);
sys_controller->chan = mbox_request_channel(&sys_controller->client, 0);
if (IS_ERR(sys_controller->chan)) {