diff --git a/module/spmi/CMakeLists.txt b/module/spmi/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..75fbfb13df5535c55cba87af630656c493e10326 --- /dev/null +++ b/module/spmi/CMakeLists.txt @@ -0,0 +1,14 @@ +# +# Arm SCP/MCP Software +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +add_library(${SCP_MODULE_TARGET} SCP_MODULE) + +target_include_directories(${SCP_MODULE_TARGET} + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") + +target_sources(${SCP_MODULE_TARGET} + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/mod_spmi.c") diff --git a/module/spmi/Module.cmake b/module/spmi/Module.cmake new file mode 100644 index 0000000000000000000000000000000000000000..bfae09249593a8c9056742e2dce60651abd678a5 --- /dev/null +++ b/module/spmi/Module.cmake @@ -0,0 +1,9 @@ +# +# Arm SCP/MCP Software +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +set(SCP_MODULE "spmi") +set(SCP_MODULE_TARGET "module-spmi") diff --git a/module/spmi/doc/module_spmi_architecture.md b/module/spmi/doc/module_spmi_architecture.md new file mode 100644 index 0000000000000000000000000000000000000000..330cf3afe6681b59b65a2efff45078fbeda8862c --- /dev/null +++ b/module/spmi/doc/module_spmi_architecture.md @@ -0,0 +1,109 @@ +\ingroup GroupModules Modules +\defgroup GroupSPMI SPMI HAL + +Module SPMI Architecture +======================== + +# Overview {#module_spmi_architecture_overview} + +System Power Management Interface (SPMI) is a two-wire interface that connects +the power management IC to the power management controller of the SoC. +This module implements a Hardware Abstraction Layer (HAL) API for SPMI +transactions. + +# Architecture {#module_spmi_architecture_architecture} + +The SPMI module provides an abstraction layer to send and receive SPMI commands +over the SoCs external SPMI bus. The SPMI module provides the interface to SPMI +controller driver, thus decoupling the SPMI controller driver platform implementation +from the client SPMI device driver. + +The SPMI module defines a driver interface on which it relies to send and +receive SPMI transactions to/from the bus. The driver interface is also +used to indicate reception of SPMI transaction from a request capable completer. + +A response event notifies the caller of the transaction completion or receipt +of a transmission from request capable completer. + +The SPMI HAL module design is based on the I2C HAL module design. + +The SPMI module provides support for concurrent accesses to an SPMI bus. If a +SPMI transaction is requested on an SPMI bus while the bus is busy processing +another transaction, the transaction request is queued. The queuing and +processing of SPMI transaction requests follow a FIFO logic. When a transaction +is completed, the processing of the transaction request at the head of the +queue, if any, is initiated. + +# Flow {#module_spmi_architecture_flow} + +The following schematic describes the transaction flow for an SPMI controller +transmission. It is assumed that all the sequence occurs without any +concurrent access and that the driver handles the transmission asynchronously. +The flow for a reception is similar. + + Client SPMI HAL SPMI Driver SPMI ISR (Driver) + | | | | + | spmi_read | | | + +-+ | | | + | +-------------->+-+ | | + | | | +- - + | | + | +<--------------+-+ |process_ | | + +-+ FWK_PENDING | |event E1 | | + | | | | | + | +-+<- -+ | | + | | | | | + | | | | | + | | | send_command | | + | | +-------------->+-+ | + | | +<--------------+-+ | + | +-+ FWK_PENDING / | | + | | FWK_SUCCESS | | + | | | | + | | | transaction +-+ + | | | _completed | | + | +-+<---------------+--------------+ | + | +- -+ | | | | + | process_ | +-+ | | | + | event E2 | | | | | + | +- >+-+ | +-+ + | | | | | + +-+<- - - - - - - +-+ | | + +-+ process_ | | | + | event R1 | | | + + E1 : Request read event + E2 : Request completed event + R1 : Response to the request event E1 + ---> : Function call/return + - -> : Asynchronous call via the event/notification interface + +The client calls *spmi_read* API of the SPMI module. +This function creates and sends the SPMI device request event which +defines the selected target on the bus and the data to be transmitted. +It returns FWK_PENDING to the client. + +When processing the request event, the SPMI module initiates the transfer by +programming the SPMI controller through the *send_command* API of the +SPMI driver. The driver module can process the request synchronously or +asynchronously. + +In the case where the processing of the request completes +immediately (synchronous handling by the driver) and another transaction request +is pending, the processing of this last transaction request is not initiated +immediately to avoid multiple transactions being processed within the same event +processing. A reload event is then sent and the processing of the next pending +request is initiated as part of the processing of the reload event. + +In case of asynchronous processing by the driver, the driver calls the +*transaction_completed* API of the SPMI module. The function creates and sends +the SPMI request completed event. + +SPMI HAL will enter a PANIC state, if its process_event function is called with +an unsupported event type. Once the SPMI HAL is in panic state, it will not +accept further requests. PANIC state can only be reached by a software error +or corruption where an event was sent to SPMI HAL from code outside of SPMI HAL +It should never be in panic state in normal operation. + +For both synchronous and asynchronous processing by driver, the response is +sent from HAL to client as per the +[deferred response architecture](doc/deferred_response_architecture.md) diff --git a/module/spmi/include/internal/spmi.h b/module/spmi/include/internal/spmi.h new file mode 100644 index 0000000000000000000000000000000000000000..0e8b91a03cf8c46a997c8fd8e8f93f93fa849e0b --- /dev/null +++ b/module/spmi/include/internal/spmi.h @@ -0,0 +1,59 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Description: + * Macros and functions used internally by SPMI HAL module + */ + +#ifndef INTERNAL_SPMI_H +#define INTERNAL_SPMI_H + +#include + +/*! SPMI Register Address bitmasks */ +#define SPMI_5_BIT_ADDRESS_MASK 0x0000001F +#define SPMI_8_BIT_ADDRESS_MASK 0x000000FF +#define SPMI_16_BIT_ADDRESS_MASK 0x0000FFFF + +#define SPMI_REG_0_ADDRESS 0x00000000 + +/*! + * \brief Event indices + */ +enum mod_spmi_internal_event_idx { + MOD_SPMI_EVENT_IDX_SEND_COMMAND, + MOD_SPMI_EVENT_IDX_TRANSACTION_COMPLETED, + MOD_SPMI_EVENT_IDX_PROCESS_NEXT_REQUEST, + MOD_SPMI_EVENT_IDX_TOTAL_COUNT, +}; + +/*! + * \brief SPMI Power Commands + */ +enum mod_spmi_internal_power_command { + MOD_SPMI_POWER_CMD_RESET = MOD_SPMI_CMD_RESET, + MOD_SPMI_POWER_CMD_SLEEP, + MOD_SPMI_POWER_CMD_SHUTDOWN, + MOD_SPMI_POWER_CMD_WAKEUP, +}; + +/*! Send command to driver event identifier */ +static const fwk_id_t mod_spmi_event_id_send_command = + FWK_ID_EVENT_INIT(FWK_MODULE_IDX_SPMI, MOD_SPMI_EVENT_IDX_SEND_COMMAND); + +/*! Request completed event identifier */ +static const fwk_id_t mod_spmi_event_id_transaction_completed = + FWK_ID_EVENT_INIT( + FWK_MODULE_IDX_SPMI, + MOD_SPMI_EVENT_IDX_TRANSACTION_COMPLETED); + +/*! Process next request event identifier */ +static const fwk_id_t mod_spmi_event_id_process_next_request = + FWK_ID_EVENT_INIT( + FWK_MODULE_IDX_SPMI, + MOD_SPMI_EVENT_IDX_PROCESS_NEXT_REQUEST); + +#endif /* INTERNAL_SPMI_H */ diff --git a/module/spmi/include/mod_spmi.h b/module/spmi/include/mod_spmi.h new file mode 100644 index 0000000000000000000000000000000000000000..dc0b7c34c5c57987c75ecad994597dfbe58e50cf --- /dev/null +++ b/module/spmi/include/mod_spmi.h @@ -0,0 +1,384 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Description: + * SPMI HAL module - a module requiring access to peripheral through + * the SPMI bus can bind to this module + */ + +#ifndef MOD_SPMI_H +#define MOD_SPMI_H + +#include +#include +#include +#include +#include + +#include +#include + +/*! + * \addtogroup GroupModules Modules + * \{ + */ + +/*! + * \defgroup GroupSPMI HAL + * + * \details Support for transmitting and receiving data through SPMI Bus + * \{ + */ + +/*! + * \brief Configuration data for an SPMI device + */ +struct mod_spmi_dev_config { + /*! + * Identifier of the element of the SPMI controller driver corresponding + * to this device + */ + fwk_id_t driver_id; + + /*! + * Identifier of the driver API + */ + fwk_id_t api_id; +}; + +/*! + * \brief Parameters of the SPMI event sent from + * driver to HAL + */ +struct mod_spmi_event_param { + /*! + * Status of the SPMI transaction + */ + int status; +}; + +/*! + * \brief SPMI Commands + */ +enum mod_spmi_command { + MOD_SPMI_CMD_EXT_REG_WRITE, + MOD_SPMI_CMD_RESET, + MOD_SPMI_CMD_SLEEP, + MOD_SPMI_CMD_SHUTDOWN, + MOD_SPMI_CMD_WAKEUP, + MOD_SPMI_CMD_AUTHENTICATE, + MOD_SPMI_CMD_MASTER_READ, + MOD_SPMI_CMD_MASTER_WRITE, + MOD_SPMI_CMD_TBO, + MOD_SPMI_CMD_DDB_MASTER_READ, + MOD_SPMI_CMD_DDB_SLAVE_READ, + MOD_SPMI_CMD_EXT_REG_READ, + MOD_SPMI_CMD_EXT_REG_WRITE_LONG, + MOD_SPMI_CMD_EXT_REG_READ_LONG, + MOD_SPMI_CMD_REG_WRITE, + MOD_SPMI_CMD_REG_READ, + MOD_SPMI_CMD_REG0_WRITE, +}; + +/*! + * \brief SPMI transaction request parameters + */ +struct mod_spmi_request { + /*! + *\brief SPMI command + */ + enum mod_spmi_command command; + + /*! + * \brief SPMI data byte count + */ + uint8_t byte_count; + + /*! + * \brief ID of the target requester/completer on SPMI bus + */ + uint8_t target_id; + + /*! + * \brief Register Address + */ + uint32_t reg_address; + + /*! + * \brief Pointer to data to be transmitted/received + */ + uint8_t *data; +}; + +/*! + * \brief SPMI driver interface. + * + * \details The interface the SPMI HAL module relies on + * to perform actions on an SPMI device. + */ +struct mod_spmi_driver_api { + /*! + * \brief Send a SPMI command as SPMI requester + * + * \details When the function returns the command may not be completed. + * The driver can assume the integrity of the data during the + * transmission. When the transmission operation has finished, + * the driver shall report it through the SPMI HAL module driver + * response API + * + * \param dev_id Identifier of the SPMI device + * \param request Information for the SPMI command to be sent + * + * \retval ::FWK_PENDING The request was submitted + * \retval ::FWK_SUCCESS The request was successfully completed + * \retval ::FWK_E_PARAM One or more parameters were invalid + * \return One of the standard framework status codes + */ + int (*send_command)(fwk_id_t dev_id, struct mod_spmi_request *request); +}; + +/*! + * \brief SPMI HAL module interface. + */ +struct mod_spmi_api { + /*! + * \brief Request read of data as SPMI requester from a SPMI completer + * + * \details When the function returns the reception is not completed, + * possibly not even started. The data buffer must stay allocated + * and its content must not be modified until the reception is + * completed or aborted. When the reception operation has finished + * a response event is sent to the client. + * + * \param dev_id Identifier of the SPMI device + * \param target_id 4-bit address of the target on the SPMI bus + * \param reg_addr 16-bit address of the register within SPMI completer + * \param data Pointer to the buffer to receive data from target + * \param byte_count Number of data bytes to receive + * + * \retval ::FWK_PENDING The request was submitted + * \retval ::FWK_E_PARAM One or more parameters were invalid + * \retval ::FWK_E_BUSY An SPMI transaction is already on-going + * \retval ::FWK_E_DEVICE The reception is aborted due to a device error + * \return One of the standard framework status codes + */ + int (*completer_read)( + fwk_id_t dev_id, + uint8_t target_id, + uint32_t reg_addr, + uint8_t *data, + uint8_t byte_count); + + /*! + * \brief Request write of data as SPMI requester to a SPMI completer + * + * \details When the function returns the transmission is not completed, + * possibly not even started. The data buffer must stay allocated + * and its content must not be modified until the reception is + * completed or aborted. When the reception operation has finished + * a response event is sent to the client. + * + * \param dev_id Identifier of the SPMI device + * \param target_id 4-bit address of the target on the SPMI bus + * \param reg_addr 16-bit address of the register within SPMI completer + * \param data Pointer to the buffer to send data to the target + * \param byte_count Number of data bytes to send + * + * \retval ::FWK_PENDING The request was submitted. + * \retval ::FWK_E_PARAM One or more parameters were invalid. + * \retval ::FWK_E_BUSY An SPMI transaction is already on-going. + * \retval ::FWK_E_DEVICE The reception is aborted due to a device error + * \return One of the standard framework status codes. + */ + int (*completer_write)( + fwk_id_t dev_id, + uint8_t target_id, + uint32_t reg_addr, + uint8_t *data, + uint8_t byte_count); + + /*! + * \brief Send a power command as SPMI requester to a SPMI completer + * + * \details When the function returns the transmission is not completed, + * possibly not even started. When the transmission operation has + * finished a response event is sent to the client. + * + * \param dev_id Identifier of the SPMI device + * \param target_id 4-bit address of the target on the SPMI bus + * \param command Power operation to send to the target + * + * \retval ::FWK_PENDING The request was submitted. + * \retval ::FWK_E_PARAM One or more parameters were invalid. + * \retval ::FWK_E_BUSY An SPMI transaction is already on-going. + * \retval ::FWK_E_DEVICE The reception is aborted due to a device error + * \return One of the standard framework status codes. + */ + int (*power_operation)( + fwk_id_t dev_id, + uint8_t target_id, + enum mod_spmi_command command); + + /*! + * \brief Send a authentication command as SPMI requester to a SPMI + * completer + * + * \details When the function returns the transmission is not completed, + * possibly not even started. When the transmission operation has + * finished a response event is sent to the client. + * + * \param dev_id Identifier of the SPMI device + * \param target_id 4-bit address of the target on the SPMI bus + * \param data Buffer to hold authentication data + * \param byte_count Number of data bytes to send + * + * \retval ::FWK_PENDING The request was submitted. + * \retval ::FWK_E_PARAM One or more parameters were invalid. + * \retval ::FWK_E_BUSY An SPMI transaction is already on-going. + * \retval ::FWK_E_DEVICE The reception is aborted due to a device error + * \return One of the standard framework status codes. + */ + int (*authenticate)( + fwk_id_t dev_id, + uint8_t target_id, + uint8_t *data, + uint8_t byte_count); + + /*! + * \brief Request read of data as SPMI requester from another requester + * + * \details When the function returns the reception is not completed, + * possibly not even started. The data buffer must stay allocated + * and its content must not be modified until the reception is + * completed or aborted. When the reception operation has finished + * a response event is sent to the client. + * + * \param dev_id Identifier of the SPMI device + * \param target_id 4-bit address of the target on the SPMI bus + * \param reg_addr 16-bit address of the register within SPMI requester + * \param data Pointer to the buffer to receive data from the target + * + * \retval ::FWK_PENDING The request was submitted. + * \retval ::FWK_E_PARAM One or more parameters were invalid. + * \retval ::FWK_E_BUSY An SPMI transaction is already on-going. + * \retval ::FWK_E_DEVICE The reception is aborted due to a device error + * \return One of the standard framework status codes. + */ + int (*requester_read)( + fwk_id_t dev_id, + uint8_t target_id, + uint32_t reg_addr, + uint8_t *data); + + /*! + * \brief Request write of data as SPMI requester to another requester + * + * \details When the function returns the transmission is not completed, + * possibly not even started. The data buffer must stay allocated + * and its content must not be modified until the transmission is + * completed or aborted. When the transmission operation has + * finished a response event is sent to the client. + * + * \param dev_id Identifier of the SPMI device + * \param target_id 4-bit address of the target on the SPMI bus + * \param reg_addr 16-bit address of the register within SPMI requester + * \param data Pointer to the buffer to send data to the target + * + * \retval ::FWK_PENDING The request was submitted. + * \retval ::FWK_E_PARAM One or more parameters were invalid. + * \retval ::FWK_E_BUSY An SPMI transaction is already on-going. + * \retval ::FWK_E_DEVICE The reception is aborted due to a device error + * \return One of the standard framework status codes. + */ + int (*requester_write)( + fwk_id_t dev_id, + uint8_t target_id, + uint32_t reg_addr, + uint8_t *data); + + /*! + * \brief Read Device Descriptor Block as SPMI requester + * from another SPMI requester or completer + * + * \details When the function returns the reception is not completed, + * possibly not even started. The data buffer must stay allocated + * and its content must not be modified until the reception is + * completed or aborted. When the reception operation has finished + * a response event is sent to the client. + * + * \param dev_id Identifier of the SPMI device + * \param target_id 4-bit address of the target on the SPMI bus + * \param is_requester Is the target SPMI device requester or completer + * \param data Pointer to the buffer to hold the DDB data + * + * \retval ::FWK_PENDING The request was submitted. + * \retval ::FWK_E_PARAM One or more parameters were invalid. + * \retval ::FWK_E_BUSY An SPMI transaction is already on-going. + * \retval ::FWK_E_DEVICE The reception is aborted due to a device error + * \return One of the standard framework status codes. + */ + int (*device_descriptor_block_read)( + fwk_id_t dev_id, + uint8_t target_id, + bool is_requester, + uint8_t *data); +}; + +/*! + * \brief SPMI HAL module driver response API. + * + * \details The interface the SPMI HAL module exposes to its module drivers to + * report reception from Request Capable Completer. In future this API + * can be extended to report other events from the driver if required. + */ +struct mod_spmi_driver_response_api { + /*! + * \brief Function called back after the completion or abortion of an SPMI + * transaction request + * + * \param dev_id Identifier of the SPMI device + * \param spmi_status SPMI transaction status + */ + void (*transaction_completed)(fwk_id_t dev_id, int spmi_status); +}; + +/*! + * \defgroup GroupSPMIIds Identifiers + * \{ + */ + +/*! + * \brief API indices. + */ +enum mod_spmi_api_idx { + /*! APIs to send SPMI commands - to be used by the PSU driver module */ + MOD_SPMI_API_IDX_SPMI, + + /*! API used by SPMI controller driver module to send driver response */ + MOD_SPMI_API_IDX_DRIVER_RESPONSE, + + /*! Number of exposed interfaces */ + MOD_SPMI_API_IDX_COUNT, +}; + +/*! SPMI API identifier */ +extern const fwk_id_t mod_spmi_api_id_spmi; +/*! Driver response API identifier */ +extern const fwk_id_t mod_spmi_api_id_driver_response; + +/*! + * \} + */ + +/*! + * \} + */ + +/*! + * \} + */ + +#endif /* MOD_SPMI_H */ diff --git a/module/spmi/src/mod_spmi.c b/module/spmi/src/mod_spmi.c new file mode 100644 index 0000000000000000000000000000000000000000..d93e2710bcbc1ba54c71aeac6fc4fca6164cf95d --- /dev/null +++ b/module/spmi/src/mod_spmi.c @@ -0,0 +1,534 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Description: + * SPMI HAL module - a module requiring access to peripheral through + * the SPMI bus can bind to this module + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/*! SPMI API identifier */ +const fwk_id_t mod_spmi_api_id_spmi = + FWK_ID_API_INIT(FWK_MODULE_IDX_SPMI, MOD_SPMI_API_IDX_SPMI); + +/*! Driver response API identifier */ +const fwk_id_t mod_spmi_api_id_driver_response = + FWK_ID_API_INIT(FWK_MODULE_IDX_SPMI, MOD_SPMI_API_IDX_DRIVER_RESPONSE); + +/*! State of the SPMI device - idle, busy or ready to process next request */ +enum mod_spmi_dev_state { + /*! The SPMI controller driver is not processing any request, and + SPMI HAL has no request pending to be sent to SPMI driver */ + MOD_SPMI_DEV_STATE_IDLE, + /*! The SPMI controller driver is processing a request */ + MOD_SPMI_DEV_STATE_BUSY, + /*! The SPMI HAL is in a unrecoverable error state and cannot + process any requests */ + MOD_SPMI_DEV_STATE_PANIC, +}; + +/*! Context for SPMI device */ +struct mod_spmi_dev_ctx { + const struct mod_spmi_dev_config *config; + const struct mod_spmi_driver_api *driver_api; + struct mod_spmi_request request; + enum mod_spmi_dev_state state; +}; + +static struct mod_spmi_dev_ctx *ctx_table; + +/* + * Static helpers + */ +static inline void get_ctx(fwk_id_t id, struct mod_spmi_dev_ctx **ctx) +{ + *ctx = ctx_table + fwk_id_get_element_idx(id); +} + +static int respond_to_caller(fwk_id_t dev_id, int drv_status) +{ + int status; + struct fwk_event resp; + struct mod_spmi_event_param *param = + (struct mod_spmi_event_param *)resp.params; + + status = fwk_get_first_delayed_response(dev_id, &resp); + if (status != FWK_SUCCESS) { + return status; + } + + param->status = drv_status; + + return fwk_put_event(&resp); +} + +static inline int send_command_and_set_dev_state( + struct mod_spmi_dev_ctx *ctx, + struct mod_spmi_request *request, + struct fwk_event *resp_event) +{ + int status; + struct mod_spmi_event_param *resp_param; + + ctx->request = *request; + ctx->state = MOD_SPMI_DEV_STATE_BUSY; + status = + ctx->driver_api->send_command(ctx->config->driver_id, &ctx->request); + if (status == FWK_PENDING) { + resp_event->is_delayed_response = true; + } else { + /* The request has succeeded or failed, respond now */ + resp_param = (struct mod_spmi_event_param *)resp_event->params; + resp_param->status = status; + ctx->state = MOD_SPMI_DEV_STATE_IDLE; + } + + return status; +} + +static int mod_spmi_process_send_command( + struct mod_spmi_dev_ctx *ctx, + const struct fwk_event *event, + struct fwk_event *resp_event) +{ + int status = FWK_SUCCESS; + struct mod_spmi_event_param *event_param; + + switch (ctx->state) { + case MOD_SPMI_DEV_STATE_IDLE: + status = send_command_and_set_dev_state( + ctx, (struct mod_spmi_request *)event->params, resp_event); + break; + + case MOD_SPMI_DEV_STATE_BUSY: + FWK_LOG_DEBUG("SPMI HAL: SPMI device busy - queueing up request"); + resp_event->is_delayed_response = true; + break; + + case MOD_SPMI_DEV_STATE_PANIC: + default: + FWK_LOG_ERR( + "SPMI HAL: SPMI device in panic or unknown state" + "- Cannot handle request"); + event_param = (struct mod_spmi_event_param *)resp_event->params; + event_param->status = FWK_E_PANIC; + return FWK_E_PANIC; + } + + return status; +} + +static int process_next_request(fwk_id_t dev_id, struct mod_spmi_dev_ctx *ctx) +{ + int status, drv_status; + bool is_empty; + struct fwk_event delayed_response; + struct fwk_event_light next_event; + + status = fwk_is_delayed_response_list_empty(dev_id, &is_empty); + if (status != FWK_SUCCESS) { + return status; + } + + if (is_empty) { + ctx->state = MOD_SPMI_DEV_STATE_IDLE; + return FWK_SUCCESS; + } + + status = fwk_get_first_delayed_response(dev_id, &delayed_response); + if (status != FWK_SUCCESS) { + return status; + } + + drv_status = send_command_and_set_dev_state( + ctx, + (struct mod_spmi_request *)delayed_response.params, + &delayed_response); + if (drv_status != FWK_PENDING) { + /* Driver has responded synchronously, send the delayed response now*/ + status = fwk_put_event(&delayed_response); + + fwk_is_delayed_response_list_empty(dev_id, &is_empty); + if (!is_empty) { + next_event.target_id = dev_id; + next_event.id = mod_spmi_event_id_process_next_request; + status = fwk_put_event(&next_event); + } + } + + return status; +} + +static int create_spmi_request( + fwk_id_t dev_id, + struct mod_spmi_request *request) +{ + int status; + struct fwk_event event; + struct mod_spmi_request *event_param = + (struct mod_spmi_request *)event.params; + + event = (struct fwk_event){ + .target_id = dev_id, + .response_requested = true, + .id = mod_spmi_event_id_send_command, + }; + *event_param = *request; + + status = fwk_put_event(&event); + if (status == FWK_SUCCESS) { + /* + * The request has been successfully queued for later processing by the + * SPMI device but processing of this request has not yet begun. The + * caller is notified that the SPMI request is in progress. + */ + return FWK_PENDING; + } + + return status; +} + +/* + * SPMI API + */ +static int mod_spmi_send_completer_read_command( + fwk_id_t dev_id, + uint8_t target_id, + uint32_t reg_addr, + uint8_t *data, + uint8_t byte_count) +{ + if (!fwk_expect(byte_count != 0)) { + return FWK_E_PARAM; + } + + if (!fwk_expect(data != NULL)) { + return FWK_E_PARAM; + } + + struct mod_spmi_request request = { + .target_id = target_id, + .reg_address = reg_addr, + .data = data, + .byte_count = byte_count, + .command = MOD_SPMI_CMD_REG_READ, + }; + + if (reg_addr & ~(SPMI_8_BIT_ADDRESS_MASK)) { + request.command = MOD_SPMI_CMD_EXT_REG_READ_LONG; + } else if (reg_addr & ~(SPMI_5_BIT_ADDRESS_MASK)) { + request.command = MOD_SPMI_CMD_EXT_REG_READ; + } + + return create_spmi_request(dev_id, &request); +} + +static int mod_spmi_send_completer_write_command( + fwk_id_t dev_id, + uint8_t target_id, + uint32_t reg_addr, + uint8_t *data, + uint8_t byte_count) +{ + if (!fwk_expect(byte_count != 0)) { + return FWK_E_PARAM; + } + + if (!fwk_expect(data != NULL)) { + return FWK_E_PARAM; + } + + struct mod_spmi_request request = { + .target_id = target_id, + .reg_address = reg_addr, + .data = data, + .byte_count = byte_count, + .command = MOD_SPMI_CMD_REG_WRITE, + }; + + if (reg_addr & ~(SPMI_8_BIT_ADDRESS_MASK)) { + request.command = MOD_SPMI_CMD_EXT_REG_WRITE_LONG; + } else if (reg_addr & ~(SPMI_5_BIT_ADDRESS_MASK)) { + request.command = MOD_SPMI_CMD_EXT_REG_WRITE; + } else if (reg_addr == SPMI_REG_0_ADDRESS) { + request.command = MOD_SPMI_CMD_REG0_WRITE; + } + + return create_spmi_request(dev_id, &request); +} + +static int mod_spmi_send_power_command( + fwk_id_t dev_id, + uint8_t target_id, + enum mod_spmi_command command) +{ + /* Validate the command */ + if ((command < MOD_SPMI_CMD_RESET) || (command > MOD_SPMI_CMD_WAKEUP)) { + FWK_LOG_ERR("SPMI HAL: invalid power command"); + return FWK_E_PARAM; + } + + struct mod_spmi_request request = { + .target_id = target_id, + .command = command, + }; + + return create_spmi_request(dev_id, &request); +} + +static int mod_spmi_send_auth_command( + fwk_id_t dev_id, + uint8_t target_id, + uint8_t *data, + uint8_t byte_count) +{ + struct mod_spmi_request request = { + .target_id = target_id, + .command = MOD_SPMI_CMD_AUTHENTICATE, + .data = data, + .byte_count = byte_count, + }; + + return create_spmi_request(dev_id, &request); +} + +static int mod_spmi_send_requester_read( + fwk_id_t dev_id, + uint8_t target_id, + uint32_t reg_addr, + uint8_t *data) +{ + struct mod_spmi_request request = { + .target_id = target_id, + .command = MOD_SPMI_CMD_MASTER_READ, + .data = data, + }; + + return create_spmi_request(dev_id, &request); +} + +static int mod_spmi_send_requester_write( + fwk_id_t dev_id, + uint8_t target_id, + uint32_t reg_addr, + uint8_t *data) +{ + struct mod_spmi_request request = { + .target_id = target_id, + .command = MOD_SPMI_CMD_MASTER_WRITE, + .data = data, + }; + + return create_spmi_request(dev_id, &request); +} + +static int mod_spmi_send_ddb_read( + fwk_id_t dev_id, + uint8_t target_id, + bool is_requester, + uint8_t *data) +{ + struct mod_spmi_request request = { + .target_id = target_id, + .command = MOD_SPMI_CMD_DDB_MASTER_READ, + .data = data, + }; + + return create_spmi_request(dev_id, &request); +} + +static struct mod_spmi_api spmi_api = { + .completer_read = mod_spmi_send_completer_read_command, + .completer_write = mod_spmi_send_completer_write_command, + .power_operation = mod_spmi_send_power_command, + .authenticate = mod_spmi_send_auth_command, + .requester_read = mod_spmi_send_requester_read, + .requester_write = mod_spmi_send_requester_write, + .device_descriptor_block_read = mod_spmi_send_ddb_read +}; + +/* + * Driver response API + */ +static void transaction_completed(fwk_id_t dev_id, int spmi_status) +{ + int status; + struct fwk_event event; + struct mod_spmi_event_param *param = + (struct mod_spmi_event_param *)event.params; + + event = (struct fwk_event){ + .target_id = dev_id, + .source_id = dev_id, + .id = mod_spmi_event_id_transaction_completed, + }; + + param->status = spmi_status; + + status = fwk_put_event(&event); + fwk_assert(status == FWK_SUCCESS); +} + +static struct mod_spmi_driver_response_api driver_response_api = { + .transaction_completed = transaction_completed, +}; + +/* + * Framework handlers + */ +static int mod_spmi_init( + fwk_id_t module_id, + unsigned int element_count, + const void *unused) +{ + ctx_table = fwk_mm_calloc(element_count, sizeof(ctx_table[0])); + + return FWK_SUCCESS; +} + +static int mod_spmi_dev_init( + fwk_id_t element_id, + unsigned int unused, + const void *data) +{ + struct mod_spmi_dev_ctx *ctx; + + get_ctx(element_id, &ctx); + ctx->config = (struct mod_spmi_dev_config *)data; + ctx->state = MOD_SPMI_DEV_STATE_IDLE; + + return FWK_SUCCESS; +} + +static int mod_spmi_bind(fwk_id_t id, unsigned int round) +{ + int status; + struct mod_spmi_dev_ctx *ctx; + + /* + * Only bind in first round of calls + * Nothing to do for module + */ + if ((round > 0) || fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) { + return FWK_SUCCESS; + } + + get_ctx(id, &ctx); + + /* Bind to driver */ + status = fwk_module_bind( + ctx->config->driver_id, ctx->config->api_id, &ctx->driver_api); + + return status; +} + +static int mod_spmi_process_bind_request( + fwk_id_t source_id, + fwk_id_t target_id, + fwk_id_t api_id, + const void **api) +{ + struct mod_spmi_dev_ctx *ctx; + + if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT)) { + return FWK_E_PARAM; + } + + get_ctx(target_id, &ctx); + + switch (fwk_id_get_api_idx(api_id)) { + case MOD_SPMI_API_IDX_DRIVER_RESPONSE: + if (fwk_id_is_equal(source_id, ctx->config->driver_id)) { + *api = &driver_response_api; + } else { + return FWK_E_PARAM; + } + break; + + case MOD_SPMI_API_IDX_SPMI: + *api = &spmi_api; + break; + + default: + return FWK_E_PARAM; + } + + return FWK_SUCCESS; +} + +static int mod_spmi_process_event( + const struct fwk_event *event, + struct fwk_event *resp_event) +{ + int status; + struct mod_spmi_dev_ctx *ctx; + struct mod_spmi_event_param *event_param; + enum mod_spmi_internal_event_idx event_id_type; + + get_ctx(event->target_id, &ctx); + event_id_type = + (enum mod_spmi_internal_event_idx)fwk_id_get_event_idx(event->id); + + switch (event_id_type) { + case MOD_SPMI_EVENT_IDX_SEND_COMMAND: + status = mod_spmi_process_send_command(ctx, event, resp_event); + break; + + case MOD_SPMI_EVENT_IDX_TRANSACTION_COMPLETED: + if (ctx->state != MOD_SPMI_DEV_STATE_BUSY) { + return FWK_E_STATE; + } + + event_param = (struct mod_spmi_event_param *)event->params; + status = respond_to_caller(event->target_id, event_param->status); + if (process_next_request(event->target_id, ctx) != FWK_SUCCESS) { + return FWK_E_DEVICE; + } + break; + + case MOD_SPMI_EVENT_IDX_PROCESS_NEXT_REQUEST: + status = process_next_request(event->target_id, ctx); + break; + + default: + FWK_LOG_ERR( + "SPMI HAL: Received event of unknown type -- entering panic state"); + status = FWK_E_PANIC; + break; + } + + if (status != FWK_SUCCESS) { + ctx->state = MOD_SPMI_DEV_STATE_PANIC; + } + + return status; +} + +const struct fwk_module module_spmi = { + .type = FWK_MODULE_TYPE_HAL, + .api_count = (unsigned int)MOD_SPMI_API_IDX_COUNT, + .event_count = (unsigned int)MOD_SPMI_EVENT_IDX_TOTAL_COUNT, + .init = mod_spmi_init, + .element_init = mod_spmi_dev_init, + .bind = mod_spmi_bind, + .process_bind_request = mod_spmi_process_bind_request, + .process_event = mod_spmi_process_event, +}; diff --git a/module/spmi/test/CMakeLists.txt b/module/spmi/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..bed5b8a7b18f7bfae477d7e40bb7ae371a430a54 --- /dev/null +++ b/module/spmi/test/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# Arm SCP/MCP Software +# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +set(TEST_SRC mod_spmi) +set(TEST_FILE mod_spmi) + +set(UNIT_TEST_TARGET mod_${TEST_MODULE}_unit_test) + +set(MODULE_SRC ${MODULE_ROOT}/${TEST_MODULE}/src) +set(MODULE_INC ${MODULE_ROOT}/${TEST_MODULE}/include) + +set(MODULE_UT_SRC ${CMAKE_CURRENT_LIST_DIR}) +set(MODULE_UT_INC ${CMAKE_CURRENT_LIST_DIR}) +set(MODULE_UT_MOCK_SRC ${CMAKE_CURRENT_LIST_DIR}/mocks) + +list(APPEND MOCK_REPLACEMENTS fwk_id) +list(APPEND MOCK_REPLACEMENTS fwk_module) +list(APPEND MOCK_REPLACEMENTS fwk_core) + +include(${SCP_ROOT}/unit_test/module_common.cmake) diff --git a/module/spmi/test/fwk_module_idx.h b/module/spmi/test/fwk_module_idx.h new file mode 100644 index 0000000000000000000000000000000000000000..ca218e57abc5dc8039a58e8d1df594cbd2aa19d3 --- /dev/null +++ b/module/spmi/test/fwk_module_idx.h @@ -0,0 +1,31 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef TEST_FWK_MODULE_IDX_H +#define TEST_FWK_MODULE_IDX_H + +#include + +enum fwk_module_idx { + FWK_MODULE_IDX_SPMI, + FWK_MODULE_IDX_SPMI_CONTROLLER, + FWK_MODULE_IDX_COUNT, +}; + +static const fwk_id_t fwk_module_id_spmi = + FWK_ID_MODULE_INIT(FWK_MODULE_IDX_SPMI); + +static const fwk_id_t fwk_module_id_spmi_element = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SPMI, 0); + +static const fwk_id_t fwk_module_id_spmi_controller = + FWK_ID_MODULE_INIT(FWK_MODULE_IDX_SPMI_CONTROLLER); + +static const fwk_id_t fwk_module_id_spmi_controller_element = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SPMI_CONTROLLER, 0); + +#endif /* TEST_FWK_MODULE_IDX_H */ diff --git a/module/spmi/test/mocks/.clang-format b/module/spmi/test/mocks/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..eeca2395f4c7551e2016f0d10a971844a3684994 --- /dev/null +++ b/module/spmi/test/mocks/.clang-format @@ -0,0 +1,4 @@ +{ + "DisableFormat": true, + "SortIncludes": false, +} diff --git a/module/spmi/test/mocks/Mockmod_spmi_extra.c b/module/spmi/test/mocks/Mockmod_spmi_extra.c new file mode 100644 index 0000000000000000000000000000000000000000..02a91a828440aff01ed6272e8411afb2f32276d5 --- /dev/null +++ b/module/spmi/test/mocks/Mockmod_spmi_extra.c @@ -0,0 +1,234 @@ +/* AUTOGENERATED FILE. DO NOT EDIT. */ +#include +#include +#include +#include "cmock.h" +#include "Mockmod_spmi_extra.h" + +static const char* CMockString_dev_id = "dev_id"; +static const char* CMockString_mod_spmi_driver_send_command = "mod_spmi_driver_send_command"; +static const char* CMockString_request = "request"; + +typedef struct _CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE +{ + UNITY_LINE_TYPE LineNumber; + char ExpectAnyArgsBool; + int ReturnVal; + fwk_id_t Expected_dev_id; + struct mod_spmi_request* Expected_request; + int Expected_request_Depth; + char ReturnThruPtr_request_Used; + struct mod_spmi_request* ReturnThruPtr_request_Val; + size_t ReturnThruPtr_request_Size; + char IgnoreArg_dev_id; + char IgnoreArg_request; + +} CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE; + +static struct Mockmod_spmi_extraInstance +{ + char mod_spmi_driver_send_command_IgnoreBool; + int mod_spmi_driver_send_command_FinalReturn; + char mod_spmi_driver_send_command_CallbackBool; + CMOCK_mod_spmi_driver_send_command_CALLBACK mod_spmi_driver_send_command_CallbackFunctionPointer; + int mod_spmi_driver_send_command_CallbackCalls; + CMOCK_MEM_INDEX_TYPE mod_spmi_driver_send_command_CallInstance; +} Mock; + +extern jmp_buf AbortFrame; + +void Mockmod_spmi_extra_Verify(void) +{ + UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM; + CMOCK_MEM_INDEX_TYPE call_instance; + call_instance = Mock.mod_spmi_driver_send_command_CallInstance; + if (Mock.mod_spmi_driver_send_command_IgnoreBool) + call_instance = CMOCK_GUTS_NONE; + if (CMOCK_GUTS_NONE != call_instance) + { + UNITY_SET_DETAIL(CMockString_mod_spmi_driver_send_command); + UNITY_TEST_FAIL(cmock_line, CMockStringCalledLess); + } + if (Mock.mod_spmi_driver_send_command_CallbackFunctionPointer != NULL) + { + call_instance = CMOCK_GUTS_NONE; + (void)call_instance; + } +} + +void Mockmod_spmi_extra_Init(void) +{ + Mockmod_spmi_extra_Destroy(); +} + +void Mockmod_spmi_extra_Destroy(void) +{ + CMock_Guts_MemFreeAll(); + memset(&Mock, 0, sizeof(Mock)); +} + +int mod_spmi_driver_send_command(fwk_id_t dev_id, struct mod_spmi_request* request) +{ + UNITY_LINE_TYPE cmock_line = TEST_LINE_NUM; + CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE* cmock_call_instance; + UNITY_SET_DETAIL(CMockString_mod_spmi_driver_send_command); + cmock_call_instance = (CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE*)CMock_Guts_GetAddressFor(Mock.mod_spmi_driver_send_command_CallInstance); + Mock.mod_spmi_driver_send_command_CallInstance = CMock_Guts_MemNext(Mock.mod_spmi_driver_send_command_CallInstance); + if (Mock.mod_spmi_driver_send_command_IgnoreBool) + { + UNITY_CLR_DETAILS(); + if (cmock_call_instance == NULL) + return Mock.mod_spmi_driver_send_command_FinalReturn; + Mock.mod_spmi_driver_send_command_FinalReturn = cmock_call_instance->ReturnVal; + return cmock_call_instance->ReturnVal; + } + if (!Mock.mod_spmi_driver_send_command_CallbackBool && + Mock.mod_spmi_driver_send_command_CallbackFunctionPointer != NULL) + { + int cmock_cb_ret = Mock.mod_spmi_driver_send_command_CallbackFunctionPointer(dev_id, request, Mock.mod_spmi_driver_send_command_CallbackCalls++); + UNITY_CLR_DETAILS(); + return cmock_cb_ret; + } + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringCalledMore); + cmock_line = cmock_call_instance->LineNumber; + if (!cmock_call_instance->ExpectAnyArgsBool) + { + if (!cmock_call_instance->IgnoreArg_dev_id) + { + UNITY_SET_DETAILS(CMockString_mod_spmi_driver_send_command,CMockString_dev_id); + UNITY_TEST_ASSERT_EQUAL_MEMORY((void*)(&cmock_call_instance->Expected_dev_id), (void*)(&dev_id), sizeof(fwk_id_t), cmock_line, CMockStringMismatch); + } + if (!cmock_call_instance->IgnoreArg_request) + { + UNITY_SET_DETAILS(CMockString_mod_spmi_driver_send_command,CMockString_request); + if (cmock_call_instance->Expected_request == NULL) + { UNITY_TEST_ASSERT_NULL(request, cmock_line, CMockStringExpNULL); } + else + { UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((void*)(cmock_call_instance->Expected_request), (void*)(request), sizeof(struct mod_spmi_request), cmock_call_instance->Expected_request_Depth, cmock_line, CMockStringMismatch); } + } + } + if (Mock.mod_spmi_driver_send_command_CallbackFunctionPointer != NULL) + { + cmock_call_instance->ReturnVal = Mock.mod_spmi_driver_send_command_CallbackFunctionPointer(dev_id, request, Mock.mod_spmi_driver_send_command_CallbackCalls++); + } + if (cmock_call_instance->ReturnThruPtr_request_Used) + { + UNITY_TEST_ASSERT_NOT_NULL(request, cmock_line, CMockStringPtrIsNULL); + memcpy((void*)request, (void*)cmock_call_instance->ReturnThruPtr_request_Val, + cmock_call_instance->ReturnThruPtr_request_Size); + } + UNITY_CLR_DETAILS(); + return cmock_call_instance->ReturnVal; +} + +void CMockExpectParameters_mod_spmi_driver_send_command(CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE* cmock_call_instance, fwk_id_t dev_id, struct mod_spmi_request* request, int request_Depth); +void CMockExpectParameters_mod_spmi_driver_send_command(CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE* cmock_call_instance, fwk_id_t dev_id, struct mod_spmi_request* request, int request_Depth) +{ + memcpy((void*)(&cmock_call_instance->Expected_dev_id), (void*)(&dev_id), + sizeof(fwk_id_t[sizeof(dev_id) == sizeof(fwk_id_t) ? 1 : -1])); /* add fwk_id_t to :treat_as_array if this causes an error */ + cmock_call_instance->IgnoreArg_dev_id = 0; + cmock_call_instance->Expected_request = request; + cmock_call_instance->Expected_request_Depth = request_Depth; + cmock_call_instance->IgnoreArg_request = 0; + cmock_call_instance->ReturnThruPtr_request_Used = 0; +} + +void mod_spmi_driver_send_command_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, int cmock_to_return) +{ + CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE)); + CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE* cmock_call_instance = (CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory); + memset(cmock_call_instance, 0, sizeof(*cmock_call_instance)); + Mock.mod_spmi_driver_send_command_CallInstance = CMock_Guts_MemChain(Mock.mod_spmi_driver_send_command_CallInstance, cmock_guts_index); + Mock.mod_spmi_driver_send_command_IgnoreBool = (char)0; + cmock_call_instance->LineNumber = cmock_line; + cmock_call_instance->ExpectAnyArgsBool = (char)0; + cmock_call_instance->ReturnVal = cmock_to_return; + Mock.mod_spmi_driver_send_command_IgnoreBool = (char)1; +} + +void mod_spmi_driver_send_command_CMockStopIgnore(void) +{ + if(Mock.mod_spmi_driver_send_command_IgnoreBool) + Mock.mod_spmi_driver_send_command_CallInstance = CMock_Guts_MemNext(Mock.mod_spmi_driver_send_command_CallInstance); + Mock.mod_spmi_driver_send_command_IgnoreBool = (char)0; +} + +void mod_spmi_driver_send_command_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, int cmock_to_return) +{ + CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE)); + CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE* cmock_call_instance = (CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory); + memset(cmock_call_instance, 0, sizeof(*cmock_call_instance)); + Mock.mod_spmi_driver_send_command_CallInstance = CMock_Guts_MemChain(Mock.mod_spmi_driver_send_command_CallInstance, cmock_guts_index); + Mock.mod_spmi_driver_send_command_IgnoreBool = (char)0; + cmock_call_instance->LineNumber = cmock_line; + cmock_call_instance->ExpectAnyArgsBool = (char)0; + cmock_call_instance->ReturnVal = cmock_to_return; + cmock_call_instance->ExpectAnyArgsBool = (char)1; +} + +void mod_spmi_driver_send_command_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, fwk_id_t dev_id, struct mod_spmi_request* request, int cmock_to_return) +{ + CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE)); + CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE* cmock_call_instance = (CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory); + memset(cmock_call_instance, 0, sizeof(*cmock_call_instance)); + Mock.mod_spmi_driver_send_command_CallInstance = CMock_Guts_MemChain(Mock.mod_spmi_driver_send_command_CallInstance, cmock_guts_index); + Mock.mod_spmi_driver_send_command_IgnoreBool = (char)0; + cmock_call_instance->LineNumber = cmock_line; + cmock_call_instance->ExpectAnyArgsBool = (char)0; + CMockExpectParameters_mod_spmi_driver_send_command(cmock_call_instance, dev_id, request, 1); + cmock_call_instance->ReturnVal = cmock_to_return; +} + +void mod_spmi_driver_send_command_AddCallback(CMOCK_mod_spmi_driver_send_command_CALLBACK Callback) +{ + Mock.mod_spmi_driver_send_command_IgnoreBool = (char)0; + Mock.mod_spmi_driver_send_command_CallbackBool = (char)1; + Mock.mod_spmi_driver_send_command_CallbackFunctionPointer = Callback; +} + +void mod_spmi_driver_send_command_Stub(CMOCK_mod_spmi_driver_send_command_CALLBACK Callback) +{ + Mock.mod_spmi_driver_send_command_IgnoreBool = (char)0; + Mock.mod_spmi_driver_send_command_CallbackBool = (char)0; + Mock.mod_spmi_driver_send_command_CallbackFunctionPointer = Callback; +} + +void mod_spmi_driver_send_command_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, fwk_id_t dev_id, struct mod_spmi_request* request, int request_Depth, int cmock_to_return) +{ + CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE)); + CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE* cmock_call_instance = (CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory); + memset(cmock_call_instance, 0, sizeof(*cmock_call_instance)); + Mock.mod_spmi_driver_send_command_CallInstance = CMock_Guts_MemChain(Mock.mod_spmi_driver_send_command_CallInstance, cmock_guts_index); + Mock.mod_spmi_driver_send_command_IgnoreBool = (char)0; + cmock_call_instance->LineNumber = cmock_line; + cmock_call_instance->ExpectAnyArgsBool = (char)0; + CMockExpectParameters_mod_spmi_driver_send_command(cmock_call_instance, dev_id, request, request_Depth); + cmock_call_instance->ReturnVal = cmock_to_return; +} + +void mod_spmi_driver_send_command_CMockReturnMemThruPtr_request(UNITY_LINE_TYPE cmock_line, struct mod_spmi_request* request, size_t cmock_size) +{ + CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE* cmock_call_instance = (CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.mod_spmi_driver_send_command_CallInstance)); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringPtrPreExp); + cmock_call_instance->ReturnThruPtr_request_Used = 1; + cmock_call_instance->ReturnThruPtr_request_Val = request; + cmock_call_instance->ReturnThruPtr_request_Size = cmock_size; +} + +void mod_spmi_driver_send_command_CMockIgnoreArg_dev_id(UNITY_LINE_TYPE cmock_line) +{ + CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE* cmock_call_instance = (CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.mod_spmi_driver_send_command_CallInstance)); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringIgnPreExp); + cmock_call_instance->IgnoreArg_dev_id = 1; +} + +void mod_spmi_driver_send_command_CMockIgnoreArg_request(UNITY_LINE_TYPE cmock_line) +{ + CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE* cmock_call_instance = (CMOCK_mod_spmi_driver_send_command_CALL_INSTANCE*)CMock_Guts_GetAddressFor(CMock_Guts_MemEndOfChain(Mock.mod_spmi_driver_send_command_CallInstance)); + UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringIgnPreExp); + cmock_call_instance->IgnoreArg_request = 1; +} diff --git a/module/spmi/test/mocks/Mockmod_spmi_extra.h b/module/spmi/test/mocks/Mockmod_spmi_extra.h new file mode 100644 index 0000000000000000000000000000000000000000..2bff86ceb476557f350e6796e529552869dfacfa --- /dev/null +++ b/module/spmi/test/mocks/Mockmod_spmi_extra.h @@ -0,0 +1,56 @@ +/* AUTOGENERATED FILE. DO NOT EDIT. */ +#ifndef _MOCKMOD_SPMI_EXTRA_H +#define _MOCKMOD_SPMI_EXTRA_H + +#include "unity.h" +#include "mod_spmi_extra.h" + +/* Ignore the following warnings, since we are copying code */ +#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0))) +#pragma GCC diagnostic push +#endif +#if !defined(__clang__) +#pragma GCC diagnostic ignored "-Wpragmas" +#endif +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wduplicate-decl-specifier" +#endif + +void Mockmod_spmi_extra_Init(void); +void Mockmod_spmi_extra_Destroy(void); +void Mockmod_spmi_extra_Verify(void); + + + + +#define mod_spmi_driver_send_command_IgnoreAndReturn(cmock_retval) mod_spmi_driver_send_command_CMockIgnoreAndReturn(__LINE__, cmock_retval) +void mod_spmi_driver_send_command_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, int cmock_to_return); +#define mod_spmi_driver_send_command_StopIgnore() mod_spmi_driver_send_command_CMockStopIgnore() +void mod_spmi_driver_send_command_CMockStopIgnore(void); +#define mod_spmi_driver_send_command_ExpectAnyArgsAndReturn(cmock_retval) mod_spmi_driver_send_command_CMockExpectAnyArgsAndReturn(__LINE__, cmock_retval) +void mod_spmi_driver_send_command_CMockExpectAnyArgsAndReturn(UNITY_LINE_TYPE cmock_line, int cmock_to_return); +#define mod_spmi_driver_send_command_ExpectAndReturn(dev_id, request, cmock_retval) mod_spmi_driver_send_command_CMockExpectAndReturn(__LINE__, dev_id, request, cmock_retval) +void mod_spmi_driver_send_command_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, fwk_id_t dev_id, struct mod_spmi_request* request, int cmock_to_return); +typedef int (* CMOCK_mod_spmi_driver_send_command_CALLBACK)(fwk_id_t dev_id, struct mod_spmi_request* request, int cmock_num_calls); +void mod_spmi_driver_send_command_AddCallback(CMOCK_mod_spmi_driver_send_command_CALLBACK Callback); +void mod_spmi_driver_send_command_Stub(CMOCK_mod_spmi_driver_send_command_CALLBACK Callback); +#define mod_spmi_driver_send_command_StubWithCallback mod_spmi_driver_send_command_Stub +#define mod_spmi_driver_send_command_ExpectWithArrayAndReturn(dev_id, request, request_Depth, cmock_retval) mod_spmi_driver_send_command_CMockExpectWithArrayAndReturn(__LINE__, dev_id, request, request_Depth, cmock_retval) +void mod_spmi_driver_send_command_CMockExpectWithArrayAndReturn(UNITY_LINE_TYPE cmock_line, fwk_id_t dev_id, struct mod_spmi_request* request, int request_Depth, int cmock_to_return); +#define mod_spmi_driver_send_command_ReturnThruPtr_request(request) mod_spmi_driver_send_command_CMockReturnMemThruPtr_request(__LINE__, request, sizeof(struct mod_spmi_request)) +#define mod_spmi_driver_send_command_ReturnArrayThruPtr_request(request, cmock_len) mod_spmi_driver_send_command_CMockReturnMemThruPtr_request(__LINE__, request, cmock_len * sizeof(*request)) +#define mod_spmi_driver_send_command_ReturnMemThruPtr_request(request, cmock_size) mod_spmi_driver_send_command_CMockReturnMemThruPtr_request(__LINE__, request, cmock_size) +void mod_spmi_driver_send_command_CMockReturnMemThruPtr_request(UNITY_LINE_TYPE cmock_line, struct mod_spmi_request* request, size_t cmock_size); +#define mod_spmi_driver_send_command_IgnoreArg_dev_id() mod_spmi_driver_send_command_CMockIgnoreArg_dev_id(__LINE__) +void mod_spmi_driver_send_command_CMockIgnoreArg_dev_id(UNITY_LINE_TYPE cmock_line); +#define mod_spmi_driver_send_command_IgnoreArg_request() mod_spmi_driver_send_command_CMockIgnoreArg_request(__LINE__) +void mod_spmi_driver_send_command_CMockIgnoreArg_request(UNITY_LINE_TYPE cmock_line); + +#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0))) +#pragma GCC diagnostic pop +#endif +#endif + +#endif diff --git a/module/spmi/test/mod_spmi_extra.h b/module/spmi/test/mod_spmi_extra.h new file mode 100644 index 0000000000000000000000000000000000000000..e668dadf366205b1b838292f50e38a13efa2566c --- /dev/null +++ b/module/spmi/test/mod_spmi_extra.h @@ -0,0 +1,41 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Description: + * SPMI unit test header to generate mock APIs for driver + */ + +#ifndef MOD_SPMI_EXTRA_H +#define MOD_SPMI_EXTRA_H + +#include + +/*! + * \brief (UNIT Tests) SPMI module to SPMI Controller driver API + */ + +/*! + * \brief Send a SPMI command as SPMI requester + * + * \details When the function returns the read may not be completed. + * The driver can assume the integrity of the data during the + * transmission. When the transmission operation has finished, + * the driver shall report it through the SPMI HAL module driver + * response API + * + * \param dev_id Identifier of the SPMI device + * \param request Request information for the SPMI command + * + * \retval ::FWK_PENDING The request was submitted + * \retval ::FWK_SUCCESS The request was successfully completed + * \retval ::FWK_E_PARAM One or more parameters were invalid + * \return One of the standard framework status codes + */ +int mod_spmi_driver_send_command( + fwk_id_t dev_id, + struct mod_spmi_request *request); + +#endif /* MOD_SPMI_EXTRA_H */ diff --git a/module/spmi/test/mod_spmi_unit_test.c b/module/spmi/test/mod_spmi_unit_test.c new file mode 100644 index 0000000000000000000000000000000000000000..276790c6aaf3a3f754f1df4656b496394bbcb3ae --- /dev/null +++ b/module/spmi/test/mod_spmi_unit_test.c @@ -0,0 +1,506 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "scp_unity.h" +#include "unity.h" + +#include +#include +#include +#include +#include + +#include UNIT_TEST_SRC + +enum spmi_element { + SPMI_INSTANCE_0, + SPMI_INSTANCE_COUNT, +}; + +static const struct fwk_element test_spmi_element_table[] = { + [0] = { + .name = "SPMI0", + .data = &(struct mod_spmi_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SPMI_CONTROLLER, 0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SPMI_CONTROLLER, + MOD_SPMI_API_IDX_SPMI), + }, + }, + [1] = {0}, +}; + +static const struct fwk_element *spmi_get_element_table(fwk_id_t module_id) +{ + return test_spmi_element_table; +} + +struct fwk_module_config test_config_spmi = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(spmi_get_element_table), +}; + +struct mod_spmi_driver_api *driver_api = &(struct mod_spmi_driver_api){ + .send_command = mod_spmi_driver_send_command, +}; + +void setUp(void) +{ + /* Set up device context */ + ctx_table = fwk_mm_calloc(SPMI_INSTANCE_COUNT, sizeof(ctx_table[0])); + + ctx_table[0].config = + (struct mod_spmi_dev_config *)test_spmi_element_table[0].data; + ctx_table[0].driver_api = driver_api; +} + +void tearDown(void) +{ + /* Do nothing */ +} + +void test_mod_spmi_send_completer_read_command_success(void) +{ + int status; + static uint8_t buffer[4]; + + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_SUCCESS); + status = mod_spmi_send_completer_read_command( + fwk_module_id_spmi_controller_element, 0, 0, buffer, 4); + TEST_ASSERT_EQUAL(FWK_PENDING, status); +} + +void test_mod_spmi_send_completer_read_command_fail(void) +{ + int status; + static uint8_t buffer[4]; + + /* Test by passing NULL pointer instead of data */ + status = mod_spmi_send_completer_read_command( + fwk_module_id_spmi_controller_element, 0, 0, NULL, 4); + TEST_ASSERT_EQUAL(FWK_E_PARAM, status); + + /* Test by passing 0 byte count */ + status = mod_spmi_send_completer_read_command( + fwk_module_id_spmi_controller_element, 0, 0, buffer, 0); + TEST_ASSERT_EQUAL(FWK_E_PARAM, status); + + /* Test with failure in creating spmi request */ + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_E_PARAM); + status = mod_spmi_send_completer_read_command( + fwk_module_id_spmi_controller_element, 0, 0, buffer, 4); + TEST_ASSERT_EQUAL(FWK_E_PARAM, status); +} + +void test_mod_spmi_send_completer_write_command_success(void) +{ + int status; + static uint8_t buffer[4]; + + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_SUCCESS); + status = mod_spmi_send_completer_write_command( + fwk_module_id_spmi_controller_element, 0, 0, buffer, 4); + TEST_ASSERT_EQUAL(FWK_PENDING, status); +} + +void test_mod_spmi_send_completer_write_command(void) +{ + int status; + static uint8_t buffer[4]; + + /* Test by passing NULL pointer instead of data */ + status = mod_spmi_send_completer_write_command( + fwk_module_id_spmi_controller_element, 0, 0, NULL, 4); + TEST_ASSERT_EQUAL(FWK_E_PARAM, status); + + /* Test by passing 0 byte count */ + status = mod_spmi_send_completer_write_command( + fwk_module_id_spmi_controller_element, 0, 0, buffer, 0); + TEST_ASSERT_EQUAL(FWK_E_PARAM, status); + + /* Test with failure in creating spmi request */ + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_E_PARAM); + status = mod_spmi_send_completer_write_command( + fwk_module_id_spmi_controller_element, 0, 0, buffer, 4); + TEST_ASSERT_EQUAL(FWK_E_PARAM, status); +} + +void test_mod_spmi_send_power_command_success(void) +{ + int status; + + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_SUCCESS); + status = mod_spmi_send_power_command( + fwk_module_id_spmi_controller_element, 0, MOD_SPMI_CMD_RESET); + TEST_ASSERT_EQUAL(FWK_PENDING, status); +} + +void test_mod_spmi_send_power_command_fail(void) +{ + int status; + + /* Test with failure in creating spmi request */ + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_E_PARAM); + status = mod_spmi_send_power_command( + fwk_module_id_spmi_controller_element, 0, MOD_SPMI_CMD_RESET); + TEST_ASSERT_EQUAL(FWK_E_PARAM, status); +} + +void test_mod_spmi_send_auth_command_success(void) +{ + int status; + uint8_t buffer[4]; + + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_SUCCESS); + status = mod_spmi_send_auth_command( + fwk_module_id_spmi_controller_element, 0, buffer, 4); + TEST_ASSERT_EQUAL(FWK_PENDING, status); +} + +void test_mod_spmi_send_requester_read_success(void) +{ + int status; + uint8_t buffer[4]; + + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_SUCCESS); + status = mod_spmi_send_requester_read( + fwk_module_id_spmi_controller_element, 0, 0, buffer); + TEST_ASSERT_EQUAL(FWK_PENDING, status); +} + +void test_mod_spmi_send_requester_write_success(void) +{ + int status; + uint8_t buffer[4]; + + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_SUCCESS); + status = mod_spmi_send_requester_write( + fwk_module_id_spmi_controller_element, 0, 0, buffer); + TEST_ASSERT_EQUAL(FWK_PENDING, status); +} + +void test_mod_spmi_send_ddb_read_success(void) +{ + int status; + uint8_t buffer[4]; + + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_SUCCESS); + status = mod_spmi_send_ddb_read( + fwk_module_id_spmi_controller_element, 0, 1, buffer); + TEST_ASSERT_EQUAL(FWK_PENDING, status); +} + +void test_mod_spmi_init(void) +{ + int status; + + status = mod_spmi_init(fwk_module_id_spmi, 1, NULL); + TEST_ASSERT_EQUAL(FWK_SUCCESS, status); +} + +void test_create_spmi_request_success(void) +{ + int status; + struct mod_spmi_request request; + + request.command = MOD_SPMI_CMD_RESET; + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_SUCCESS); + status = + create_spmi_request(fwk_module_id_spmi_controller_element, &request); + TEST_ASSERT_EQUAL(FWK_PENDING, status); +} + +void test_create_spmi_request_fail(void) +{ + int status; + struct mod_spmi_request request; + + request.command = MOD_SPMI_CMD_RESET; + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_E_PARAM); + status = + create_spmi_request(fwk_module_id_spmi_controller_element, &request); + TEST_ASSERT_EQUAL(FWK_E_PARAM, status); +} + +void test_mod_spmi_device_init(void) +{ + int status; + void *data = (void *)&test_spmi_element_table[0].data; + + fwk_module_is_valid_element_id_ExpectAndReturn( + fwk_module_id_spmi_controller_element, true); + fwk_id_get_element_idx_ExpectAndReturn( + fwk_module_id_spmi_controller_element, 0); + status = mod_spmi_dev_init(fwk_module_id_spmi_controller_element, 0, data); + TEST_ASSERT_EQUAL(FWK_SUCCESS, status); +} + +void test_respond_to_caller_success(void) +{ + int status; + + fwk_get_first_delayed_response_ExpectAnyArgsAndReturn(FWK_SUCCESS); + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_SUCCESS); + status = + respond_to_caller(fwk_module_id_spmi_controller_element, FWK_SUCCESS); + TEST_ASSERT_EQUAL(FWK_SUCCESS, status); +} + +void test_respond_to_caller_fail(void) +{ + int status; + + fwk_get_first_delayed_response_ExpectAnyArgsAndReturn(FWK_E_PARAM); + status = + respond_to_caller(fwk_module_id_spmi_controller_element, FWK_SUCCESS); + TEST_ASSERT_EQUAL(FWK_E_PARAM, status); + + fwk_get_first_delayed_response_ExpectAnyArgsAndReturn(FWK_SUCCESS); + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_E_PARAM); + status = + respond_to_caller(fwk_module_id_spmi_controller_element, FWK_SUCCESS); + TEST_ASSERT_EQUAL(FWK_E_PARAM, status); +} + +void test_transaction_completed(void) +{ + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_SUCCESS); + transaction_completed(fwk_module_id_spmi_controller_element, FWK_SUCCESS); +} + +void test_mod_spmi_bind(void) +{ + int status; + + /* Bind for round > 0 should return FWK_SUCCESS */ + status = mod_spmi_bind(fwk_module_id_spmi_controller_element, 1); + TEST_ASSERT_EQUAL(FWK_SUCCESS, status); + + /* Bind for fwk_id_t module should return FWK_SUCCESS */ + fwk_id_is_type_ExpectAndReturn( + fwk_module_id_spmi_controller, FWK_ID_TYPE_MODULE, true); + status = mod_spmi_bind(fwk_module_id_spmi_controller, 0); + TEST_ASSERT_EQUAL(FWK_SUCCESS, status); + + fwk_module_bind_ExpectAndReturn( + ctx_table[0].config->driver_id, + ctx_table[0].config->api_id, + &ctx_table[0].driver_api, + FWK_SUCCESS); + fwk_id_is_type_ExpectAndReturn( + fwk_module_id_spmi_controller_element, FWK_ID_TYPE_MODULE, false); + fwk_module_is_valid_element_id_ExpectAndReturn( + fwk_module_id_spmi_controller_element, true); + fwk_id_get_element_idx_ExpectAndReturn( + fwk_module_id_spmi_controller_element, 0); + status = mod_spmi_bind(fwk_module_id_spmi_controller_element, 0); + TEST_ASSERT_EQUAL(FWK_SUCCESS, status); +} + +void test_mod_spmi_process_bind_request_success(void) +{ + int status; + static struct mod_spmi_driver_response_api *spmi_driver_response_api; + + fwk_id_is_type_ExpectAndReturn( + fwk_module_id_spmi_element, FWK_ID_TYPE_ELEMENT, true); + fwk_module_is_valid_element_id_ExpectAndReturn( + fwk_module_id_spmi_element, true); + fwk_id_get_element_idx_ExpectAndReturn(fwk_module_id_spmi_element, 0); + fwk_id_is_equal_ExpectAndReturn( + fwk_module_id_spmi_controller_element, + fwk_module_id_spmi_controller_element, + true); + fwk_id_is_equal_ExpectAndReturn( + mod_spmi_api_id_driver_response, mod_spmi_api_id_driver_response, true); + fwk_id_get_api_idx_ExpectAndReturn( + mod_spmi_api_id_driver_response, MOD_SPMI_API_IDX_DRIVER_RESPONSE); + status = mod_spmi_process_bind_request( + fwk_module_id_spmi_controller_element, + fwk_module_id_spmi_element, + mod_spmi_api_id_driver_response, + (const void **)&spmi_driver_response_api); + TEST_ASSERT_EQUAL(FWK_SUCCESS, status); + TEST_ASSERT_EQUAL_PTR(&driver_response_api, spmi_driver_response_api); +} + +void test_mod_spmi_process_bind_request_fail(void) +{ + int status; + + fwk_id_is_type_ExpectAndReturn( + fwk_module_id_spmi_controller, FWK_ID_TYPE_ELEMENT, false); + status = mod_spmi_process_bind_request( + fwk_module_id_spmi_controller, + fwk_module_id_spmi_controller, + fwk_module_id_spmi_controller, + NULL); + TEST_ASSERT_EQUAL(FWK_E_PARAM, status); +} + +void test_process_next_request_success(void) +{ + int status; + struct mod_spmi_dev_ctx *ctx = &ctx_table[0]; + bool empty; + struct fwk_event delayed_response; + + empty = true; + fwk_is_delayed_response_list_empty_ExpectAnyArgsAndReturn(FWK_SUCCESS); + fwk_is_delayed_response_list_empty_ReturnThruPtr_is_empty(&empty); + status = process_next_request(fwk_module_id_spmi_controller_element, ctx); + TEST_ASSERT_EQUAL(FWK_SUCCESS, status); + TEST_ASSERT_EQUAL(ctx->state, MOD_SPMI_DEV_STATE_IDLE); + + empty = false; + fwk_is_delayed_response_list_empty_ExpectAnyArgsAndReturn(FWK_SUCCESS); + fwk_is_delayed_response_list_empty_ReturnThruPtr_is_empty(&empty); + fwk_is_delayed_response_list_empty_ExpectAnyArgsAndReturn(FWK_SUCCESS); + fwk_get_first_delayed_response_ExpectAnyArgsAndReturn(FWK_SUCCESS); + fwk_get_first_delayed_response_ReturnThruPtr_event(&delayed_response); + fwk_get_first_delayed_response_ExpectAnyArgsAndReturn(FWK_SUCCESS); + fwk_id_get_event_idx_ExpectAnyArgsAndReturn( + MOD_SPMI_EVENT_IDX_SEND_COMMAND); + mod_spmi_driver_send_command_ExpectAnyArgsAndReturn(FWK_PENDING); + status = process_next_request(fwk_module_id_spmi_controller_element, ctx); + TEST_ASSERT_EQUAL(FWK_SUCCESS, status); +} + +void test_process_next_request_fail(void) +{ + int status; + struct mod_spmi_dev_ctx *ctx = &ctx_table[0]; + bool empty = true; + struct fwk_event delayed_response; + + fwk_is_delayed_response_list_empty_ExpectAnyArgsAndReturn(FWK_E_PARAM); + mod_spmi_driver_send_command_ExpectAnyArgsAndReturn(FWK_E_PARAM); + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_E_PARAM); + __fwk_put_event_light_ExpectAnyArgsAndReturn(FWK_E_PARAM); + status = process_next_request(fwk_module_id_spmi_controller_element, ctx); + TEST_ASSERT_EQUAL(FWK_E_PARAM, status); + + empty = false; + fwk_is_delayed_response_list_empty_ExpectAnyArgsAndReturn(FWK_SUCCESS); + fwk_is_delayed_response_list_empty_ReturnThruPtr_is_empty(&empty); + fwk_is_delayed_response_list_empty_ExpectAnyArgsAndReturn(FWK_SUCCESS); + __fwk_put_event_light_ExpectAnyArgsAndReturn(FWK_E_PARAM); + fwk_get_first_delayed_response_ExpectAnyArgsAndReturn(FWK_E_PARAM); + status = process_next_request(fwk_module_id_spmi_controller_element, ctx); + TEST_ASSERT_EQUAL(FWK_E_PARAM, status); + + empty = false; + fwk_is_delayed_response_list_empty_ExpectAnyArgsAndReturn(FWK_SUCCESS); + fwk_is_delayed_response_list_empty_ReturnThruPtr_is_empty(&empty); + fwk_get_first_delayed_response_ExpectAnyArgsAndReturn(FWK_SUCCESS); + fwk_get_first_delayed_response_ReturnThruPtr_event(&delayed_response); + fwk_id_get_event_idx_ExpectAnyArgsAndReturn( + MOD_SPMI_EVENT_IDX_SEND_COMMAND); + mod_spmi_driver_send_command_ExpectAnyArgsAndReturn(FWK_E_PARAM); + __fwk_put_event_ExpectAnyArgsAndReturn(FWK_E_PARAM); + status = process_next_request(fwk_module_id_spmi_controller_element, ctx); + TEST_ASSERT_EQUAL(FWK_E_PARAM, status); +} + +void test_mod_spmi_process_send_command_success(void) +{ + int status; + struct fwk_event event, resp_event; + struct mod_spmi_request request; + struct mod_spmi_request *event_param = + (struct mod_spmi_request *)event.params; + + request.command = MOD_SPMI_CMD_EXT_REG_READ_LONG; + + event.source_id = fwk_module_id_spmi_controller_element; + event.target_id = fwk_module_id_spmi_controller_element; + event.is_response = false; + event.response_requested = false; + event.is_notification = false; + event.is_delayed_response = false; + event.id = mod_spmi_event_id_send_command; + *event_param = request; + + fwk_module_is_valid_element_id_ExpectAndReturn( + fwk_module_id_spmi_controller_element, true); + fwk_id_get_event_idx_ExpectAnyArgsAndReturn( + MOD_SPMI_EVENT_IDX_SEND_COMMAND); + fwk_id_get_event_idx_ExpectAnyArgsAndReturn( + MOD_SPMI_EVENT_IDX_SEND_COMMAND); + fwk_id_get_element_idx_ExpectAndReturn( + fwk_module_id_spmi_controller_element, 0); + mod_spmi_driver_send_command_ExpectAnyArgsAndReturn(FWK_SUCCESS); + status = mod_spmi_process_send_command(&ctx_table[0], &event, &resp_event); + TEST_ASSERT_EQUAL(FWK_SUCCESS, status); +} + +void test_mod_spmi_process_event_success(void) +{ + int status; + struct fwk_event event, resp_event; + struct mod_spmi_request request; + struct mod_spmi_request *event_param = + (struct mod_spmi_request *)event.params; + + request.command = MOD_SPMI_CMD_EXT_REG_READ_LONG; + + event.source_id = fwk_module_id_spmi_controller_element; + event.target_id = fwk_module_id_spmi_controller_element; + event.is_response = false; + event.response_requested = false; + event.is_notification = false; + event.is_delayed_response = false; + event.id = mod_spmi_event_id_send_command; + *event_param = request; + + fwk_module_is_valid_element_id_ExpectAndReturn( + fwk_module_id_spmi_controller_element, true); + fwk_id_get_event_idx_ExpectAnyArgsAndReturn( + MOD_SPMI_EVENT_IDX_SEND_COMMAND); + fwk_id_get_event_idx_ExpectAnyArgsAndReturn( + MOD_SPMI_EVENT_IDX_SEND_COMMAND); + fwk_id_get_element_idx_ExpectAndReturn( + fwk_module_id_spmi_controller_element, 0); + mod_spmi_driver_send_command_ExpectAnyArgsAndReturn(FWK_SUCCESS); + status = mod_spmi_process_send_command(&ctx_table[0], &event, &resp_event); + TEST_ASSERT_EQUAL(FWK_SUCCESS, status); +} + +int spmi_test_main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(test_mod_spmi_init); + RUN_TEST(test_mod_spmi_device_init); + RUN_TEST(test_mod_spmi_bind); + RUN_TEST(test_mod_spmi_process_bind_request_success); + RUN_TEST(test_mod_spmi_process_bind_request_fail); + RUN_TEST(test_respond_to_caller_success); + RUN_TEST(test_respond_to_caller_fail); + RUN_TEST(test_transaction_completed); + RUN_TEST(test_process_next_request_success); + RUN_TEST(test_process_next_request_fail); + RUN_TEST(test_mod_spmi_process_event_success); + RUN_TEST(test_mod_spmi_process_send_command_success); + RUN_TEST(test_create_spmi_request_success); + RUN_TEST(test_create_spmi_request_fail); + RUN_TEST(test_mod_spmi_send_completer_read_command_success); + RUN_TEST(test_mod_spmi_send_completer_read_command_fail); + RUN_TEST(test_mod_spmi_send_completer_write_command_success); + RUN_TEST(test_mod_spmi_send_power_command_fail); + RUN_TEST(test_mod_spmi_send_auth_command_success); + RUN_TEST(test_mod_spmi_send_requester_read_success); + RUN_TEST(test_mod_spmi_send_requester_write_success); + RUN_TEST(test_mod_spmi_send_ddb_read_success); + + return UNITY_END(); +} + +#if !defined(TEST_ON_TARGET) +int main(void) +{ + return spmi_test_main(); +} +#endif diff --git a/unit_test/CMakeLists.txt b/unit_test/CMakeLists.txt index e61ab14f30dfdc3863aec05d7ea2b6951bf481f4..ffcd1df320e8e3c3463d92e37b51adf46d2d6632 100644 --- a/unit_test/CMakeLists.txt +++ b/unit_test/CMakeLists.txt @@ -135,6 +135,7 @@ list(APPEND UNIT_MODULE scmi_system_power_req) list(APPEND UNIT_MODULE sensor) list(APPEND UNIT_MODULE sensor_smcf_drv) list(APPEND UNIT_MODULE smcf) +list(APPEND UNIT_MODULE spmi) list(APPEND UNIT_MODULE thermal_mgmt) list(APPEND UNIT_MODULE traffic_cop) list(APPEND UNIT_MODULE xr77128)