diff options
Diffstat (limited to 'drivers/mailbox/mailbox-mpfs.c')
| -rw-r--r-- | drivers/mailbox/mailbox-mpfs.c | 31 | 
1 files changed, 28 insertions, 3 deletions
diff --git a/drivers/mailbox/mailbox-mpfs.c b/drivers/mailbox/mailbox-mpfs.c index cfacb3f320a6..853901acaeec 100644 --- a/drivers/mailbox/mailbox-mpfs.c +++ b/drivers/mailbox/mailbox-mpfs.c @@ -2,7 +2,7 @@  /*   * Microchip PolarFire SoC (MPFS) system controller/mailbox controller driver   * - * Copyright (c) 2020 Microchip Corporation. All rights reserved. + * Copyright (c) 2020-2022 Microchip Corporation. All rights reserved.   *   * Author: Conor Dooley <conor.dooley@microchip.com>   * @@ -56,7 +56,7 @@  #define SCB_STATUS_NOTIFY_MASK BIT(SCB_STATUS_NOTIFY)  #define SCB_STATUS_POS (16) -#define SCB_STATUS_MASK GENMASK_ULL(SCB_STATUS_POS + SCB_MASK_WIDTH, SCB_STATUS_POS) +#define SCB_STATUS_MASK GENMASK(SCB_STATUS_POS + SCB_MASK_WIDTH - 1, SCB_STATUS_POS)  struct mpfs_mbox {  	struct mbox_controller controller; @@ -130,13 +130,38 @@ static void mpfs_mbox_rx_data(struct mbox_chan *chan)  	struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv;  	struct mpfs_mss_response *response = mbox->response;  	u16 num_words = ALIGN((response->resp_size), (4)) / 4U; -	u32 i; +	u32 i, status;  	if (!response->resp_msg) {  		dev_err(mbox->dev, "failed to assign memory for response %d\n", -ENOMEM);  		return;  	} +	/* +	 * The status is stored in bits 31:16 of the SERVICES_SR register. +	 * It is only valid when BUSY == 0. +	 * We should *never* get an interrupt while the controller is +	 * still in the busy state. If we do, something has gone badly +	 * wrong & the content of the mailbox would not be valid. +	 */ +	if (mpfs_mbox_busy(mbox)) { +		dev_err(mbox->dev, "got an interrupt but system controller is busy\n"); +		response->resp_status = 0xDEAD; +		return; +	} + +	status = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET); + +	/* +	 * If the status of the individual servers is non-zero, the service has +	 * failed. The contents of the mailbox at this point are not be valid, +	 * so don't bother reading them. Set the status so that the driver +	 * implementing the service can handle the result. +	 */ +	response->resp_status = (status & SCB_STATUS_MASK) >> SCB_STATUS_POS; +	if (response->resp_status) +		return; +  	if (!mpfs_mbox_busy(mbox)) {  		for (i = 0; i < num_words; i++) {  			response->resp_msg[i] =  | 
