[PATCH v4 2/8] scpi: Add alternative legacy structures, functions and macros

Sudeep Holla sudeep.holla at arm.com
Mon Oct 10 07:36:34 PDT 2016


Hi Neil,

Sorry, I could not reply to your response on v3. Anyways I will review v4.

On 05/10/16 08:33, Neil Armstrong wrote:
> This patch adds support for the Legacy SCPI protocol in early JUNO versions and
> shipped Amlogic ARMv8 based SoCs. Some Rockchip SoC are also known to use this
> version of protocol with extended vendor commands
> .
> In order to support the legacy SCPI protocol variant, add back the structures
> and macros that varies against the final specification.
> Then add indirection table for legacy commands.
> Finally Add bitmap field for channel selection since the Legacy protocol mandates to
> send a selected subset of the commands on the high priority channel instead of the
> low priority channel.
>
> The message sending path differs from the final SCPI procotocol because the
> Amlogic SCP firmware always reply 1 instead of a special value containing the command
> byte and replied rx data length.
> For this reason commands queuing cannot be used and we assume the reply command is
> the head of the rx_pending list since we ensure sequential command sending with a
> separate dedicated mutex.
>
> Signed-off-by: Neil Armstrong <narmstrong at baylibre.com>
> ---
>  drivers/firmware/arm_scpi.c | 221 +++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 199 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c
> index 498afa0..6244eb1 100644
> --- a/drivers/firmware/arm_scpi.c
> +++ b/drivers/firmware/arm_scpi.c

[...]

> @@ -307,21 +398,46 @@ static void scpi_process_cmd(struct scpi_chan *ch, u32 cmd)
>  		return;
>  	}
>
> -	list_for_each_entry(t, &ch->rx_pending, node)
> -		if (CMD_XTRACT_UNIQ(t->cmd) == CMD_XTRACT_UNIQ(cmd)) {
> -			list_del(&t->node);
> -			match = t;
> -			break;
> -		}
> +	/* Command type is not replied by the SCP Firmware in legacy Mode
> +	 * We should consider that command is the head of pending RX commands
> +	 * if the list is not empty. In TX only mode, the list would be empty.
> +	 */
> +	if (scpi_info->is_legacy) {
> +		match = list_first_entry(&ch->rx_pending, struct scpi_xfer,
> +					 node);
> +		list_del(&match->node);
> +	} else {
> +		list_for_each_entry(t, &ch->rx_pending, node)
> +			if (CMD_XTRACT_UNIQ(t->cmd) == CMD_XTRACT_UNIQ(cmd)) {
> +				list_del(&t->node);
> +				match = t;
> +				break;
> +			}
> +	}
>  	/* check if wait_for_completion is in progress or timed-out */
>  	if (match && !completion_done(&match->done)) {
> -		struct scpi_shared_mem *mem = ch->rx_payload;
> -		unsigned int len = min(match->rx_len, CMD_SIZE(cmd));
> +		unsigned int len;
> +
> +		if (scpi_info->is_legacy) {
> +			struct legacy_scpi_shared_mem *mem = ch->rx_payload;
> +
> +			/* RX Length is not replied by the lagcy Firmware */
> +			len = match->rx_len;
> +
> +			match->status = le32_to_cpu(mem->status);
> +			memcpy_fromio(match->rx_buf, mem->payload, len);

The above 2 seems common to both, no ?

> +		} else {
> +			struct scpi_shared_mem *mem = ch->rx_payload;
> +
> +			len = min(match->rx_len, CMD_SIZE(cmd));
> +
> +			match->status = le32_to_cpu(mem->status);
> +			memcpy_fromio(match->rx_buf, mem->payload, len);
> +		}
>
> -		match->status = le32_to_cpu(mem->status);
> -		memcpy_fromio(match->rx_buf, mem->payload, len);
>  		if (match->rx_len > len)
>  			memset(match->rx_buf + len, 0, match->rx_len - len);
> +

Spurious ?

>  		complete(&match->done);
>  	}
>  	spin_unlock_irqrestore(&ch->rx_lock, flags);
> @@ -331,7 +447,12 @@ static void scpi_handle_remote_msg(struct mbox_client *c, void *msg)
>  {
>  	struct scpi_chan *ch = container_of(c, struct scpi_chan, cl);
>  	struct scpi_shared_mem *mem = ch->rx_payload;
> -	u32 cmd = le32_to_cpu(mem->command);
> +	u32 cmd;
> +
> +	if (scpi_info->is_legacy)
> +		cmd = *(u32 *)msg;

Do we need do this if it doesn't contain command ?

> +	else
> +		cmd = le32_to_cpu(mem->command);
>
>  	scpi_process_cmd(ch, cmd);
>  }
> @@ -343,17 +464,26 @@ static void scpi_tx_prepare(struct mbox_client *c, void *msg)
>  	struct scpi_chan *ch = container_of(c, struct scpi_chan, cl);
>  	struct scpi_shared_mem *mem = (struct scpi_shared_mem *)ch->tx_payload;
>
> -	if (t->tx_buf)
> -		memcpy_toio(mem->payload, t->tx_buf, t->tx_len);
> +	if (t->tx_buf) {
> +		if (scpi_info->is_legacy)
> +			memcpy_toio(ch->tx_payload, t->tx_buf, t->tx_len);
> +		else
> +			memcpy_toio(mem->payload, t->tx_buf, t->tx_len);
> +	}
> +
>  	if (t->rx_buf) {
>  		if (!(++ch->token))
>  			++ch->token;
>  		ADD_SCPI_TOKEN(t->cmd, ch->token);
> +		if (scpi_info->is_legacy)
> +			t->slot = t->cmd;

I thought passing token was not an issue from your previous response,
but you are overriding it here, why ?

>  		spin_lock_irqsave(&ch->rx_lock, flags);
>  		list_add_tail(&t->node, &ch->rx_pending);
>  		spin_unlock_irqrestore(&ch->rx_lock, flags);
>  	}
> -	mem->command = cpu_to_le32(t->cmd);
> +
> +	if (!scpi_info->is_legacy)
> +		mem->command = cpu_to_le32(t->cmd);
>  }
>
>  static struct scpi_xfer *get_scpi_xfer(struct scpi_chan *ch)
> @@ -396,21 +526,37 @@ static int scpi_send_message(unsigned int offset, void *tx_buf,
>
>  	cmd = scpi_info->scpi_cmds[offset];
>
> -	chan = atomic_inc_return(&scpi_info->next_chan) % scpi_info->num_chans;
> +	if (scpi_info->is_legacy)
> +		chan = test_bit(cmd, scpi_info->cmd_priority) ? 1 : 0;
> +	else
> +		chan = atomic_inc_return(&scpi_info->next_chan) %
> +			scpi_info->num_chans;
>  	scpi_chan = scpi_info->channels + chan;
>
>  	msg = get_scpi_xfer(scpi_chan);
>  	if (!msg)
>  		return -ENOMEM;
>
> -	msg->slot = BIT(SCPI_SLOT);
> -	msg->cmd = PACK_SCPI_CMD(cmd, tx_len);
> +	if (scpi_info->is_legacy) {
> +		msg->cmd = PACK_LEGACY_SCPI_CMD(cmd, tx_len);
> +		msg->slot = msg->cmd;
> +	} else {
> +		msg->slot = BIT(SCPI_SLOT);
> +		msg->cmd = PACK_SCPI_CMD(cmd, tx_len);
> +	}
>  	msg->tx_buf = tx_buf;
>  	msg->tx_len = tx_len;
>  	msg->rx_buf = rx_buf;
>  	msg->rx_len = rx_len;
>  	init_completion(&msg->done);
>
> +	/* Since we cannot distinguish the original command in the
> +	 * MHU reply stat value from a Legacy SCP firmware, ensure
> +	 * sequential command sending to the firmware.
> +	 */

OK this comment now questions the existence of this extra lock.
The mailbox will always send the commands in the sequential order.
It's only firmware that can re-order the response. Since that can't
happen in you case, I really don't see the need for this.

Please explain the race you would see without this locking. Yes I
understand that only one command is supposed to be sent to firmware at a
time. Suppose you allow more callers here, all will wait on the
completion flags and the first in the list gets unblocked right ?
I am just trying to understand if there's real need for this extra
lock when we already have that from the list.

> +	if (scpi_info->is_legacy)
> +		mutex_lock(&scpi_chan->legacy_lock);
> +
>  	ret = mbox_send_message(scpi_chan->chan, msg);
>  	if (ret < 0 || !rx_buf)
>  		goto out;
> @@ -421,9 +567,13 @@ static int scpi_send_message(unsigned int offset, void *tx_buf,
>  		/* first status word */
>  		ret = msg->status;
>  out:
> -	if (ret < 0 && rx_buf) /* remove entry from the list if timed-out */
> +	if (ret < 0 && rx_buf)
> +		/* remove entry from the list if timed-out */
>  		scpi_process_cmd(scpi_chan, msg->cmd);
>
> +	if (scpi_info->is_legacy)
> +		mutex_unlock(&scpi_chan->legacy_lock);
> +
>  	put_scpi_xfer(msg, scpi_chan);
>  	/* SCPI error codes > 0, translate them to Linux scale*/
>  	return ret > 0 ? scpi_to_linux_errno(ret) : ret;

[...]

> @@ -525,7 +687,6 @@ static struct scpi_dvfs_info *scpi_dvfs_get_info(u8 domain)
>
>  	info->count = DVFS_OPP_COUNT(buf.header);
>  	info->latency = DVFS_LATENCY(buf.header) * 1000; /* uS to nS */
> -

Spurious ?

>  	info->opps = kcalloc(info->count, sizeof(*opp), GFP_KERNEL);
>  	if (!info->opps) {
>  		kfree(info);
> @@ -580,9 +741,13 @@ static int scpi_sensor_get_value(u16 sensor, u64 *val)
>
>  	ret = scpi_send_message(CMD_SENSOR_VALUE, &id, sizeof(id),
>  				&buf, sizeof(buf));
> -	if (!ret)
> -		*val = (u64)le32_to_cpu(buf.hi_val) << 32 |
> -			le32_to_cpu(buf.lo_val);
> +	if (!ret) {
> +		if (scpi_info->is_legacy)
> +			*val = (u64)le32_to_cpu(buf.lo_val);
> +		else
> +			*val = (u64)le32_to_cpu(buf.hi_val) << 32 |
> +				le32_to_cpu(buf.lo_val);
> +	}

Not required as I have mentioned couple of times in previous versions,
it's zero filled by the driver.

-- 
Regards,
Sudeep



More information about the linux-amlogic mailing list