[Skiboot] [PATCH V7 1/8] occ: Add support for OPAL-OCC command/response interface

Cyril Bur cyrilbur at gmail.com
Fri Jul 21 17:02:42 AEST 2017


On Wed, 2017-07-19 at 23:52 +0530, Shilpasri G Bhat wrote:
> This patch adds support for a shared memory based command/response
> interface between OCC and OPAL. In HOMER, there is an OPAL command
> buffer and an OCC response buffer which is used to send inband
> commands to OCC.
> 
> The OPAL-OCC command/response sequence is as follows:
> 
> 1. Check if both 'OCC Progress' bit in OCC response flag and 'Cmd Ready'
>    bit in OPAL command flag are set to zero. If yes then proceed with
>    below steps to send a command to OCC.
> 2. Write the command value, request ID and command specific data
>    to the OPAL command buffer.
> 3. Clear the response flag and set the 'Cmd Ready' bit in OPAL command
>    flag to indicate command is ready.
> 4. OCC will poll the command flag every 4ms to check if 'Cmd Ready' bit
>    is set by OPAL. If the bit is set then OCC will set the 'OCC Progress'
>    bit.
> 5. OCC will process the command and write the response to the OCC response
>    buffer and set the 'Rsp Ready' bit in the response flag and sends an
>    interrupt.
> 8. OPAL will receive the interrupt and queue the response to the host.
> 
> Signed-off-by: Shilpasri G Bhat <shilpa.bhat at linux.vnet.ibm.com>
> ---
> Changes from V6:
> - Updated error messages
> - Add pstate tabel valid pointer in the struct cmd_interface
> 
>  hw/occ.c           | 421 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  include/opal-api.h |   1 +
>  include/opal-msg.h |   2 +-
>  3 files changed, 419 insertions(+), 5 deletions(-)
> 

[snip]

> +static int write_occ_cmd(struct cmd_interface *chip)
> +{
> +	struct opal_command_buffer *cmd = chip->cmd;
> +	enum occ_cmd ocmd = chip->cdata->cmd;
> +
> +	if (!chip->retry && occ_in_progress(chip)) {
> +		chip->cmd_in_progress = false;
> +		return OPAL_BUSY;
> +	}
> +
> +	cmd->flag = chip->rsp->flag = 0;
> +	cmd->cmd = occ_cmds[ocmd].cmd_value;
> +	cmd->request_id = chip->request_id++;
> +	cmd->data_size = occ_cmds[ocmd].cmd_size;
> +	memcpy(&cmd->data, chip->cdata->data, cmd->data_size);
> +	cmd->flag = OPAL_FLAG_CMD_READY;
> +

Not sure if I've already said this on a review or not, so I'll say it
now.

The changing of the cmd->flag to OPAL_FLAG_CMD_READY is what lets the
OCC know that it can start processing. Obviously then this should be
the last thing done, this question is more for Stewart. What is the
process here for ensuring this always remains the case? For humans a
comment is fine but I don't really like that overall, who reads
comments. For the compiler, at the moment is is also fine because I
think the call to memcpy is preventing it from moving the cmd->flag
store up. However, if the memcpy is done first and with more aggressive
-O flags I did get the compiler to make the mistake.


Other than that though I have no problems with this patch.

Reviewed-by: Cyril Bur <cyril.bur at au1.ibm.com>

> +	schedule_timer(&chip->timeout,
> +		       msecs_to_tb(occ_cmds[ocmd].timeout_ms));
> +
> +	return OPAL_ASYNC_COMPLETION;
> +}
> +
> +static int64_t __unused opal_occ_command(struct cmd_interface *chip, int token,
> +					 struct opal_occ_cmd_data *cdata)
> +{
> +	int rc;
> +
> +	if (!(*chip->valid))
> +		return OPAL_WRONG_STATE;
> +
> +	if (!(PPC_BIT8(chip->occ_role) & occ_cmds[cdata->cmd].role_mask))
> +		return OPAL_PERMISSION;
> +
> +	if (!(PPC_BIT16(*chip->occ_state) & occ_cmds[cdata->cmd].state_mask))
> +		return OPAL_WRONG_STATE;
> +
> +	lock(&chip->queue_lock);
> +	if (chip->cmd_in_progress) {
> +		rc = OPAL_BUSY;
> +		goto out;
> +	}
> +
> +	chip->cdata = cdata;
> +	chip->token = token;
> +	chip->cmd_in_progress = true;
> +	chip->retry = false;
> +	rc = write_occ_cmd(chip);
> +out:
> +	unlock(&chip->queue_lock);
> +	return rc;
> +}
> +
> +static inline bool sanity_check_opal_cmd(struct opal_command_buffer *cmd,
> +					 struct cmd_interface *chip)
> +{
> +	return ((cmd->cmd == occ_cmds[chip->cdata->cmd].cmd_value) &&
> +		(cmd->request_id == chip->request_id - 1) &&
> +		(cmd->data_size == occ_cmds[chip->cdata->cmd].cmd_size));
> +}
> +
> +static inline bool check_occ_rsp(struct opal_command_buffer *cmd,
> +				 struct occ_response_buffer *rsp)
> +{
> +	if (cmd->cmd != rsp->cmd) {
> +		prlog(PR_DEBUG, "OCC: Command value mismatch in OCC response"
> +		      "rsp->cmd = %d cmd->cmd = %d\n", rsp->cmd, cmd->cmd);
> +		return false;
> +	}
> +
> +	if (cmd->request_id != rsp->request_id) {
> +		prlog(PR_DEBUG, "OCC: Request ID mismatch in OCC response"
> +		      "rsp->request_id = %d cmd->request_id = %d\n",
> +		      rsp->request_id, cmd->request_id);
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +static inline void queue_occ_rsp_msg(int token, int rc)
> +{
> +	int ret;
> +
> +	ret = opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, token, rc);
> +	if (ret)
> +		prerror("OCC: Failed to queue OCC response status message\n");
> +}
> +
> +static void occ_cmd_timeout_handler(struct timer *t __unused, void *data,
> +				    uint64_t now __unused)
> +{
> +	struct cmd_interface *chip = data;
> +
> +	lock(&chip->queue_lock);
> +	if (!chip->cmd_in_progress)
> +		goto exit;
> +
> +	if (!chip->retry) {
> +		prlog(PR_DEBUG, "OCC: Command timeout, retrying\n");
> +		chip->retry = true;
> +		write_occ_cmd(chip);
> +	} else {
> +		chip->cmd_in_progress = false;
> +		queue_occ_rsp_msg(chip->token, OPAL_TIMEOUT);
> +		prlog(PR_DEBUG, "OCC: Command timeout after retry\n");
> +	}
> +exit:
> +	unlock(&chip->queue_lock);
> +}
> +
> +static int read_occ_rsp(struct occ_response_buffer *rsp)
> +{
> +	switch (rsp->status) {
> +	case OCC_RSP_SUCCESS:
> +		return OPAL_SUCCESS;
> +	case OCC_RSP_INVALID_COMMAND:
> +		prlog(PR_DEBUG, "OCC: Rsp status: Invalid command\n");
> +		break;
> +	case OCC_RSP_INVALID_CMD_DATA_LENGTH:
> +		prlog(PR_DEBUG, "OCC: Rsp status: Invalid command data length\n");
> +		break;
> +	case OCC_RSP_INVALID_DATA:
> +		prlog(PR_DEBUG, "OCC: Rsp status: Invalid command data\n");
> +		break;
> +	case OCC_RSP_INTERNAL_ERROR:
> +		prlog(PR_DEBUG, "OCC: Rsp status: OCC internal error\n");
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	/* Clear the OCC response flag */
> +	rsp->flag = 0;
> +	return OPAL_INTERNAL_ERROR;
> +}
> +
> +static void handle_occ_rsp(uint32_t chip_id)
> +{
> +	struct cmd_interface *chip;
> +	struct opal_command_buffer *cmd;
> +	struct occ_response_buffer *rsp;
> +
> +	chip = get_chip_cmd_interface(chip_id);
> +	if (!chip)
> +		return;
> +
> +	cmd = chip->cmd;
> +	rsp = chip->rsp;
> +
> +	/*Read rsp*/
> +	if (rsp->flag != OCC_FLAG_RSP_READY)
> +		return;
> +	lock(&chip->queue_lock);
> +	if (!chip->cmd_in_progress)
> +		goto exit;
> +
> +	cancel_timer(&chip->timeout);
> +	if (!sanity_check_opal_cmd(cmd, chip) ||
> +	    !check_occ_rsp(cmd, rsp)) {
> +		if (!chip->retry) {
> +			prlog(PR_DEBUG, "OCC: Command-response mismatch, retrying\n");
> +			chip->retry = true;
> +			write_occ_cmd(chip);
> +		} else {
> +			chip->cmd_in_progress = false;
> +			queue_occ_rsp_msg(chip->token, OPAL_INTERNAL_ERROR);
> +			prlog(PR_DEBUG, "OCC: Command-response mismatch\n");
> +		}
> +		goto exit;
> +	}
> +
> +	chip->cmd_in_progress = false;
> +	queue_occ_rsp_msg(chip->token, read_occ_rsp(chip->rsp));
> +exit:
> +	unlock(&chip->queue_lock);
> +}
> +
> +static void occ_cmd_interface_init(void)
> +{
> +	struct occ_dynamic_data *data;
> +	struct occ_pstate_table *pdata;
> +	struct proc_chip *chip;
> +	int i = 0;
> +
> +	chip = next_chip(NULL);
> +	pdata = get_occ_pstate_table(chip);
> +	if (pdata->version != 0x90)
> +		return;
> +
> +	for_each_chip(chip)
> +		nr_occs++;
> +
> +	chips = malloc(sizeof(*chips) * nr_occs);
> +	assert(chips);
> +
> +	for_each_chip(chip) {
> +		pdata = get_occ_pstate_table(chip);
> +		data = get_occ_dynamic_data(chip);
> +		chips[i].chip_id = chip->id;
> +		chips[i].occ_role = pdata->v9.occ_role;
> +		chips[i].occ_state = &data->occ_state;
> +		chips[i].valid = &pdata->valid;
> +		chips[i].cmd = &data->cmd;
> +		chips[i].rsp = &data->rsp;
> +		init_lock(&chips[i].queue_lock);
> +		chips[i].cmd_in_progress = false;
> +		chips[i].request_id = 0;
> +		init_timer(&chips[i].timeout, occ_cmd_timeout_handler,
> +			   &chips[i]);
> +		i++;
> +	}
> +}
> +
>  /* CPU-OCC PState init */
>  /* Called after OCC init on P8 and P9 */
>  void occ_pstates_init(void)
> @@ -908,6 +1318,9 @@ void occ_pstates_init(void)
>  		chip->throttle = 0;
>  	opal_add_poller(occ_throttle_poll, NULL);
>  	occ_pstates_initialized = true;
> +
> +	/* Init OPAL-OCC command-response interface */
> +	occ_cmd_interface_init();
>  }
>  
>  struct occ_load_req {
> @@ -1407,8 +1820,10 @@ void occ_p9_interrupt(uint32_t chip_id)
>  	if (ireg & OCB_OCI_OCIMISC_IRQ_TMGT)
>  		prd_tmgt_interrupt(chip_id);
>  
> -	if (ireg & OCB_OCI_OCIMISC_IRQ_SHMEM)
> +	if (ireg & OCB_OCI_OCIMISC_IRQ_SHMEM) {
>  		occ_throttle_poll(NULL);
> +		handle_occ_rsp(chip_id);
> +	}
>  
>  	if (ireg & OCB_OCI_OCIMISC_IRQ_I2C)
>  		p9_i2c_bus_owner_change(chip_id);
> @@ -1433,5 +1848,3 @@ void occ_fsp_init(void)
>  	if (fsp_present())
>  		fsp_register_client(&fsp_occ_client, FSP_MCLASS_OCC);
>  }
> -
> -
> diff --git a/include/opal-api.h b/include/opal-api.h
> index 2a8816a..d2137d6 100644
> --- a/include/opal-api.h
> +++ b/include/opal-api.h
> @@ -55,6 +55,7 @@
>  #define OPAL_XSCOM_CTR_OFFLINED	-30
>  #define OPAL_XIVE_PROVISIONING	-31
>  #define OPAL_XIVE_FREE_ACTIVE	-32
> +#define OPAL_TIMEOUT		-33
>  
>  /* API Tokens (in r0) */
>  #define OPAL_INVALID_CALL		       -1
> diff --git a/include/opal-msg.h b/include/opal-msg.h
> index a75bc4e..74163c4 100644
> --- a/include/opal-msg.h
> +++ b/include/opal-msg.h
> @@ -25,7 +25,7 @@
>   * ideally the value matches to the number of modules using async
>   * infrastructure, but not necessarily the same..
>   */
> -#define OPAL_MAX_ASYNC_COMP	8
> +#define OPAL_MAX_ASYNC_COMP	16
>  
>  int _opal_queue_msg(enum opal_msg_type msg_type, void *data,
>  		    void (*consumed)(void *data), size_t num_params,


More information about the Skiboot mailing list