diff options
Diffstat (limited to 'src/occ_405/sensor/sensor_inband_cmd.c')
-rwxr-xr-x | src/occ_405/sensor/sensor_inband_cmd.c | 557 |
1 files changed, 557 insertions, 0 deletions
diff --git a/src/occ_405/sensor/sensor_inband_cmd.c b/src/occ_405/sensor/sensor_inband_cmd.c new file mode 100755 index 0000000..15a8967 --- /dev/null +++ b/src/occ_405/sensor/sensor_inband_cmd.c @@ -0,0 +1,557 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/sensor/sensor_inband_cmd.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2017,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + + +//****************************************************************************** +// Includes +//****************************************************************************** +#include <cmdh_fsp_cmds.h> +#include <sensor_inband_cmd.h> +#include <homer.h> +#include <occ_service_codes.h> +#include <sensor_service_codes.h> +#include <trac.h> +#include <occhw_async.h> +#include <common.h> + + +//****************************************************************************** +// Block Copy Engine (BCE) Defines/Globals +//****************************************************************************** + +// Buffer in SRAM to copy larger commands cmd/rsp buffer from/to main memory using the BCE +DMA_BUFFER(inband_max_cmd_t G_inband_cmd_max_data_bce_buff) = {{0}}; + +// Buffer in SRAM to copy smaller commands cmd/rsp buffer from/to main memory using the BCE +DMA_BUFFER(inband_min_cmd_t G_inband_cmd_min_data_bce_buff) = {{0}}; + +// BCE request structure. Used by BCE functions to schedule copy request. +BceRequest G_inband_cmd_bce_req; + +/** + * Specifies whether the BCE request was scheduled. If false, the request + * finished or has never been scheduled/initialized. + */ +bool G_inband_cmd_req_scheduled = false; + +// Number of tics passed waiting for BCE callback +uint8_t G_bce_callback_wait = 0; + +volatile uint8_t G_inband_occ_cmd_state = INBAND_OCC_CMD_NONE; +volatile uint8_t G_inband_occ_bce_saved_state = INBAND_OCC_CMD_NONE; + +//****************************************************************************** +// Functions +//****************************************************************************** + + +/** + * Logs an error caused by the Block Copy Engine. Does nothing if a BCE error + * has already been logged. + * + * Note that the required error log comment containing tags like 'userdata4' and + * 'devdesc' must be located by the call to this function. It is not located + * inside this function because the value of those tags varies. + * + * @param i_modId Module ID + * @param i_extReasonCode Extended reason code + * @param i_userData1 Userdata1 value + * @param i_userData2 Userdata2 value + */ +void inband_cmd_log_bce_error(uint16_t i_modId, uint16_t i_extReasonCode, + uint32_t i_userData1, uint32_t i_userData2) +{ + static bool L_error_logged = false; + if (!L_error_logged) + { + // Create and commit error + errlHndl_t l_errl = createErrl(i_modId, // Module ID + INBAND_CMD_ERROR, // Reason code + i_extReasonCode, // Extended reason code + ERRL_SEV_INFORMATIONAL, // Severity + NULL, // Trace Buffers + DEFAULT_TRACE_SIZE, // Trace Size + i_userData1, // Userdata1 + i_userData2); // Userdata2 + commitErrl(&l_errl); + L_error_logged = true; + } +} + + +/** + * Returns whether the global BCE request struct is idle and ready for re-use. + * Returns true immediately if the request was not scheduled. If the request + * was scheduled, checks to see if it has finished. + * + * @param i_caller_mod_id Module ID of calling function in case an error occurs + * @return True if BCE request is idle, false otherwise + */ +bool inband_cmd_is_bce_req_idle(uint16_t i_caller_mod_id) +{ + // Number of times we've waited for current request to finish + static uint8_t L_wait_count = 0; + + // If the request was not previously scheduled, then it is idle. This also + // handles the case where the request has not been initialized yet. + if (!G_inband_cmd_req_scheduled) + { + return true; + } + + // Request was scheduled; check if it finished and is now idle + if (async_request_is_idle(&G_inband_cmd_bce_req.request)) + { + // Request is now idle and ready for re-use + G_inband_cmd_req_scheduled = false; + + // If we were waiting for request to finish, trace and clear wait count + if (L_wait_count > 0) + { + TRAC_INFO("inband_cmd_is_bce_req_idle: " + "Request finished after waiting %u times: caller=0x%04X", + L_wait_count, i_caller_mod_id); + L_wait_count = 0; + } + return true; + } + + // Request was scheduled but has not finished. Increment wait count unless + // we are already at the max (to avoid overflow). + if (L_wait_count < UINT8_MAX) + { + ++L_wait_count; + } + + // If this is the first time we've waited for this request, trace it + if (L_wait_count == 1) + { + TRAC_INFO("inband_cmd_is_bce_req_idle: " + "Waiting for request to finish: caller=0x%04X", + i_caller_mod_id); + } + + // If this is the second time we've waited for this request, log BCE error + if (L_wait_count == 2) + { + /* @ + * @errortype + * @moduleid INBAND_CMD_IS_BCE_REQ_IDLE_MOD + * @reasoncode INBAND_CMD_ERROR + * @userdata1 Caller module ID + * @userdata2 0 + * @userdata4 ERC_GENERIC_TIMEOUT + * @devdesc BCE request not finished after waiting twice + */ + inband_cmd_log_bce_error(INBAND_CMD_IS_BCE_REQ_IDLE_MOD, ERC_GENERIC_TIMEOUT, + i_caller_mod_id, 0); + } + + // Return false since request is not idle + return false; +} + +/** + * inband_cmd_bce_callback + * + * Description: Callback function for G_inband_cmd_bce_req BCE request + * NO TRACING OR CALLING FUNCTIONS THAT TRACE ALLOWED + */ +void inband_cmd_bce_callback( void ) +{ + static bool L_processed_at_least_one_cmd = FALSE; + static uint8_t L_last_seq_num_processed = 0; + uint8_t seq_num = 0; + uint8_t cmd_flags = 0; + + // Decide what to do next for processing an in-band command for the BCE that finished + switch (G_inband_occ_cmd_state) + { + case INBAND_OCC_CMD_CHECK_FOR_CMD: + // check for command uses min bce data buffer + // If we processed at least one cmd it is not a new command if same seq number + cmd_flags = G_inband_cmd_min_data_bce_buff.header.flags; + seq_num = G_inband_cmd_min_data_bce_buff.header.seq; + if( ( (!L_processed_at_least_one_cmd) || + (L_processed_at_least_one_cmd && (seq_num != L_last_seq_num_processed)) ) && + ( (cmd_flags & IN_BAND_CMD_READY_MASK) == IN_BAND_CMD_READY_MASK) ) + { + // There is a command to process + G_inband_occ_cmd_state = INBAND_OCC_CMD_START; + // now that we have a cmd save seq num so we don't keep processing the same cmd + L_processed_at_least_one_cmd = TRUE; + L_last_seq_num_processed = seq_num; + } + else + { + // No command + G_inband_occ_cmd_state = INBAND_OCC_CMD_NONE; + } + break; + + case INBAND_OCC_CMD_RSP_READY: + // response is ready send interrupt + G_inband_occ_cmd_state = INBAND_OCC_CMD_RSP_INT; + break; + + default: + // Invalid state. Can't trace here set state to invalid to trace later + G_inband_occ_bce_saved_state = G_inband_occ_cmd_state; + G_inband_occ_cmd_state = INBAND_OCC_INVALID_BCE_CALLBACK; + break; + } +} + +/** + * Copies the specified number of bytes either to main mem or down from main mem + * for handling an inband command using the Block Copy Engine (BCE). + * + * @param i_main_mem_addr Main memory address for copy. Must be 128-byte aligned + * @param i_sram_addr SRAM address for copy. Must be 128-byte aligned + * @param i_byte_count Number of bytes to copy. Must be multiple of 128. + * Must be <= INBAND_OCC_CMD_BCE_BUF_SIZE. 0 bytes is not valid + * @param i_to_main_mem TRUE indicates copy is sram to main memory + * FALSE indicates copy is main memory to sram + * @param i_caller_mod_id Module ID of the calling function in case an error occurs + * @return True if BCE request was successfully scheduled, false otherwise + */ +bool inband_cmd_bce_copy(uint32_t i_main_mem_addr, uint32_t i_sram_addr, + size_t i_byte_count, bool i_to_main_mem, uint16_t i_caller_mod_id) +{ + int l_rc = 0; + + // Verify address and byte count are valid + static bool L_traced_param_error = false; + if (((i_main_mem_addr % 128) != 0) || ((i_sram_addr % 128) != 0) || + ((i_byte_count % 128) != 0) || (i_byte_count > INBAND_CMD_MAX_BCE_BUF_SIZE) || + (i_byte_count == 0) ) + { + if (!L_traced_param_error) + { + TRAC_ERR("inband_cmd_bce_copy: Input parameter error: " + "address=0x%08X SRAM=0x%08X length=%u caller=0x%04X", + i_main_mem_addr, i_sram_addr, i_byte_count, i_caller_mod_id); + L_traced_param_error = true; + } + return false; + } + + // Check if a copy request was previously scheduled and is not yet finished + static bool L_traced_sched_error = false; + if (!inband_cmd_is_bce_req_idle(i_caller_mod_id)) + { + if (!L_traced_sched_error) + { + TRAC_ERR("inband_cmd_bce_copy: Previous request not finished: caller=0x%04X", + i_caller_mod_id); + L_traced_sched_error = true; + } + return false; + } + + // Create BCE request based on if copy is up or down from main memory + if (i_to_main_mem) + { + l_rc = bce_request_create(&G_inband_cmd_bce_req, // Block copy request + &G_pba_bcue_queue, // SRAM up to mainstore + i_main_mem_addr, // Mainstore address + i_sram_addr, // SRAM start address + i_byte_count, // Size of copy + SSX_WAIT_FOREVER, // No timeout + (AsyncRequestCallback) inband_cmd_bce_callback, + NULL, // No call back args + ASYNC_CALLBACK_IMMEDIATE); + } + else + { + l_rc = bce_request_create(&G_inband_cmd_bce_req, // Block copy request + &G_pba_bcde_queue, // mainstore down to SRAM + i_main_mem_addr, // Mainstore address + i_sram_addr, // SRAM start address + i_byte_count, // Size of copy + SSX_WAIT_FOREVER, // No timeout + (AsyncRequestCallback) inband_cmd_bce_callback, + NULL, // No call back args + ASYNC_CALLBACK_IMMEDIATE); + } + + if (l_rc != SSX_OK) // fail to create BCE request? + { + TRAC_ERR("inband_cmd_bce_copy: Request create failure: rc=0x%08X caller=0x%04X", + -l_rc, i_caller_mod_id); + /* @ + * @errortype + * @moduleid INBAND_CMD_BCE_COPY_MOD + * @reasoncode INBAND_CMD_ERROR + * @userdata1 Return code from bce_request_create() + * @userdata2 Caller module ID + * @userdata4 ERC_BCE_REQUEST_CREATE_FAILURE + * @devdesc Failed to create BCE request + */ + inband_cmd_log_bce_error(INBAND_CMD_BCE_COPY_MOD, ERC_BCE_REQUEST_CREATE_FAILURE, + -l_rc, i_caller_mod_id); + return false; + } + + // Schedule BCE request + l_rc = bce_request_schedule(&G_inband_cmd_bce_req); + if (l_rc != SSX_OK) + { + TRAC_ERR("inband_cmd_bce_copy: Request schedule failure: rc=0x%08X caller=0x%04X", + -l_rc, i_caller_mod_id); + /* @ + * @errortype + * @moduleid INBAND_CMD_BCE_COPY_MOD + * @reasoncode INBAND_CMD_ERROR + * @userdata1 Return code from bce_request_schedule() + * @userdata2 Caller module ID + * @userdata4 ERC_BCE_REQUEST_SCHEDULE_FAILURE + * @devdesc Failed to schedule BCE request + */ + inband_cmd_log_bce_error(INBAND_CMD_BCE_COPY_MOD, ERC_BCE_REQUEST_SCHEDULE_FAILURE, + -l_rc, i_caller_mod_id); + return false; + } + + // Successfully scheduled request. Copy is not blocking, so need to check + // whether it finished later. Set flag indicating request is scheduled. + G_inband_cmd_req_scheduled = true; + return true; +} + + +// Function Specification +// +// Name: inband_command_check +// +// Description: Check for command from the inband interface +// +// End Function Specification +void inband_command_check(void) +{ + // Only check for a new command if not currently processing an inband command + if (G_inband_occ_cmd_state == INBAND_OCC_CMD_NONE) + { + // Create and Schedule BCE to read minimum bytes of OCC inband command buffer in HOMER + memset((void*)&G_inband_cmd_min_data_bce_buff, 0x00, sizeof(inband_min_cmd_t)); + bool i_to_main_mem = FALSE; // this request is main mem down to SRAM + G_inband_occ_cmd_state = INBAND_OCC_CMD_CHECK_FOR_CMD; + G_bce_callback_wait = 0; + if (inband_cmd_bce_copy(INBAND_OCC_CMD_ADDRESS_HOMER, (uint32_t)&G_inband_cmd_min_data_bce_buff, + INBAND_CMD_MIN_BCE_BUF_SIZE, i_to_main_mem, INBAND_CMD_CHECK_MOD)) + { + // Copy succeeded. The BCE callback will handle next + } + else + { + // copy failed try again next time + G_inband_occ_cmd_state = INBAND_OCC_CMD_NONE; + } + } +} + +// Function Specification +// +// Name: inband_command_handler +// +// Description: Command handler for inband commands this should only be called if +// it is already known that there is an in-band command in process +// This is checked to be called on every tick +// +// End Function Specification +void inband_command_handler(void) +{ + uint8_t l_reason_code = ERRL_RC_INTERNAL_FAIL; + uint16_t l_cmd_data_len = 0; + uint8_t l_seq_num = 0; + uint8_t l_cmd_type = 0xFF; + uint16_t l_rsp_data_length = 0; + uint16_t l_bce_copy_size = 0; + uint8_t l_bce_padding = 0; + bool l_to_main_mem = TRUE; // BCE requests from here are going SRAM to main memory + bool l_bce_scheduled = FALSE; + + // Decide what to do next for processing an in-band command + if(G_inband_occ_cmd_state == INBAND_OCC_CMD_START) + { + // We use the max buffer for response + memset((void*)&G_inband_cmd_max_data_bce_buff, 0x00, INBAND_CMD_MAX_BCE_BUF_SIZE); + + // When we check for command we used the minimum bce buffer + l_seq_num = G_inband_cmd_min_data_bce_buff.header.seq; + l_cmd_type = G_inband_cmd_min_data_bce_buff.header.cmd_type; + l_cmd_data_len = CONVERT_UINT8_ARRAY_UINT16(G_inband_cmd_min_data_bce_buff.header.data_length[0], + G_inband_cmd_min_data_bce_buff.header.data_length[1]); + + // if data is more than min we will need to do another BCE to read the rest of the cmd + if(l_cmd_data_len > INBAND_MIN_DATA_LENGTH) + { + // invalid command, currently no cmd supported is larger than min requiring 2nd BCE + l_reason_code = ERRL_RC_INVALID_CMD_LEN; + } + else + { + // Have the full command, process it based on command type. + // cmd data is in min data BCE buffer the rsp data will go in the max data BCE buffer + uint8_t* l_cmd_data_ptr = (uint8_t*) &G_inband_cmd_min_data_bce_buff.data; + uint8_t* l_rsp_data_ptr = (uint8_t*) &G_inband_cmd_max_data_bce_buff.data; + + switch(l_cmd_type) + { + case CMDH_CLEAR_SENSOR_DATA: + l_reason_code = cmdh_clear_sensor_data(l_cmd_data_len, + l_cmd_data_ptr, + INBAND_MAX_DATA_LENGTH, + &l_rsp_data_length, + l_rsp_data_ptr); + break; + + case CMDH_SET_PCAP_INBAND: + l_reason_code = cmdh_set_pcap_inband(l_cmd_data_len, + l_cmd_data_ptr, + INBAND_MAX_DATA_LENGTH, + &l_rsp_data_length, + l_rsp_data_ptr); + break; + + case CMDH_WRITE_PSR: + l_reason_code = cmdh_write_psr(l_cmd_data_len, + l_cmd_data_ptr, + INBAND_MAX_DATA_LENGTH, + &l_rsp_data_length, + l_rsp_data_ptr); + break; + + case CMDH_SELECT_SENSOR_GROUPS: + l_reason_code = cmdh_select_sensor_groups(l_cmd_data_len, + l_cmd_data_ptr, + INBAND_MAX_DATA_LENGTH, + &l_rsp_data_length, + l_rsp_data_ptr); + break; + + default: + l_reason_code = ERRL_RC_INVALID_CMD; + break; + } // end switch + } // end cmd process + + // fill in response header in G_inband_cmd_max_data_bce_buff + G_inband_cmd_max_data_bce_buff.header.flags = IN_BAND_RSP_READY_MASK; + G_inband_cmd_max_data_bce_buff.header.seq = l_seq_num; + G_inband_cmd_max_data_bce_buff.header.cmd_type = l_cmd_type; + G_inband_cmd_max_data_bce_buff.header.reserved_rc = l_reason_code; + G_inband_cmd_max_data_bce_buff.header.data_length[0] = ((uint8_t *)&l_rsp_data_length)[0]; + G_inband_cmd_max_data_bce_buff.header.data_length[1] = ((uint8_t *)&l_rsp_data_length)[1]; + + // Copy the response from SRAM to main memory + // Determine BCE copy size, must be factor of 128 + l_bce_copy_size = sizeof(inband_occ_cmd_header_t) + l_rsp_data_length; + l_bce_padding = l_bce_copy_size % 128; + if(l_bce_padding) + { + l_bce_copy_size += (128 - l_bce_padding); + } + + G_bce_callback_wait = 0; + G_inband_occ_cmd_state = INBAND_OCC_CMD_RSP_READY; + l_bce_scheduled = inband_cmd_bce_copy(INBAND_OCC_RSP_ADDRESS_HOMER, + (uint32_t)&G_inband_cmd_max_data_bce_buff, + l_bce_copy_size, + l_to_main_mem, + INBAND_CMD_HANDLER_MOD); + if (!l_bce_scheduled) + { + // failed to copy response + G_inband_occ_cmd_state = INBAND_OCC_CMD_NONE; + } + else + { + TRAC_INFO("inband_command_handler: Command 0x%02X with " + "seq_num 0x%02X finished with RC 0x%02X", + l_cmd_type, l_seq_num, l_reason_code); + } + + } // INBAND_OCC_CMD_START + + else if(G_inband_occ_cmd_state == INBAND_OCC_CMD_RSP_INT) + { + // Send interrupt to indciate response is ready in main mem + notify_host(INTR_REASON_OPAL_SHARED_MEM_CHANGE); + G_inband_occ_cmd_state = INBAND_OCC_CMD_NONE; + } + + else if( (G_inband_occ_cmd_state == INBAND_OCC_CMD_CHECK_FOR_CMD) || + (G_inband_occ_cmd_state == INBAND_OCC_CMD_RSP_READY) ) + { + // Waiting for BCE to finish, these states are handled by BCE callback + // if the callback is never called (BCE failure) for a max wait log error + G_bce_callback_wait++; + if(G_bce_callback_wait == MAX_TICS_INBAND_BCE_CALLBACK_WAIT) + { + TRAC_ERR("inband_command_handler: Timeout waiting for BCE callback cmd state 0x%02X", + G_inband_occ_cmd_state); + G_inband_occ_cmd_state = INBAND_OCC_CMD_NONE; + /* @ + * @errortype + * @moduleid INBAND_CMD_HANDLER_MOD + * @reasoncode INBAND_CMD_ERROR + * @userdata1 Inband command state + * @userdata2 Caller module ID + * @userdata4 ERC_BCE_REQ_CALLBACK_TIMEOUT + * @devdesc Timeout waiting for BCE callback + */ + inband_cmd_log_bce_error(INBAND_CMD_HANDLER_MOD, ERC_BCE_REQ_CALLBACK_TIMEOUT, + G_inband_occ_cmd_state, INBAND_CMD_HANDLER_MOD); + } + } + + else if(G_inband_occ_cmd_state == INBAND_OCC_INVALID_BCE_CALLBACK) + { + // BCE callback was called with invalid state, trace now and set state to none + static bool L_traced_bce_bad_state = FALSE; + if (!L_traced_bce_bad_state) + { + TRAC_ERR("inband_command_handler: inband_cmd_bce_callback detected invalid state %u", + G_inband_occ_bce_saved_state); + L_traced_bce_bad_state = TRUE; + } + G_inband_occ_cmd_state = INBAND_OCC_CMD_NONE; + G_inband_occ_bce_saved_state = INBAND_OCC_CMD_NONE; + } + + else + { + // Invalid state + static bool L_traced_bad_state = FALSE; + if (!L_traced_bad_state) + { + TRAC_ERR("inband_command_handler: Called with invalid state %u", + G_inband_occ_cmd_state); + L_traced_bad_state = TRUE; + } + G_inband_occ_cmd_state = INBAND_OCC_CMD_NONE; + } +} |