diff options
Diffstat (limited to 'drivers/soc/microchip/mpfs-sys-controller.c')
| -rw-r--r-- | drivers/soc/microchip/mpfs-sys-controller.c | 56 | 
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)) {  |