Last edited 3 days ago

How to configure system resources on STM32MP2

Applicable for STM32MP21x lines, STM32MP23x lines, STM32MP25x lines

1. Article Purpose[edit | edit source]

The purpose of this article is to explain how to configure the system resources used by the application when these resources are shared between the Arm® Cortex®-M33 and the Arm® Cortex®-A35 contexts.

In this article, it is assumed that the system runs in production boot mode.

It applies to:

2. Introduction[edit | edit source]

On STM32MP2 series, some system resources are directly usable by the application, while others are managed in secure world and exposed through SCMI services.

When a peripheral is used by the application, the developer must first identify how its associated system resource is managed:

  • directly by the application
  • through a secure OS service
  • already by platform software

Typical system resources are:

  • clocks
  • voltage domains
  • regulator-backed resources
Information
This article focuses on the system resource management use case from the application point of view. Generic SCMI concepts, architecture, protocols and transports are already described in SCMI overview.

3. RCC clock security model and RIF inheritance[edit | edit source]

On STM32MP2 series, the global clock tree -- PLLs, bus clock dividers, and flexgen kernel clock sources -- is a secure world asset. The RCC registers that control these resources are protected by the RCC RISUP and are accessible only from the Secure world:

  • In A35-TD flavor : TF-A configures PLLs and flexgen at boot; OP-TEE manages runtime clock services.
  • In M33-TD flavor : TF-M secure partition owns the clock tree and exposes clock services via PSA/SCMI APIs.

Nonsecure bus transactions targeting clock source or flexgen registers in RCC are silently discarded at the hardware level by the RCC RISUP, regardless of the peripheral's RIFSC assignment. This is the hardware-level reason why SCMI (A35-TD flavor ) and the PSA clock service (M33-TD flavor ) are the mandatory interfaces for requesting clock source changes from nonsecure software -- not a policy choice, but a hardware constraint.

Not all RCC registers are affected equally. Peripheral bus clock enable bits -- the simple on/off gate for a peripheral's bus clock -- may be directly accessible to the M33, depending on the peripheral type and its RIFSC assignment. The following subsections describe how this works.

3.1. RIF security context inheritance between IP and RCC[edit | edit source]

For standard peripherals protected by RIFSC, a hardware inheritance links the peripheral RISUP entry in RIFSC to its corresponding RCC clock enable bit:

Only the execution context owning a peripheral (via its RIFSC CID assignment) can write the associated RCC clock enable bit.

When the Secure world assigns a peripheral to M33 (CID2) in RIFSC, the matching RCC clock enable bit becomes writable by M33 nonsecure code automatically. No additional RCC configuration is required.

Information
This inheritance applies only to the peripheral bus clock enable bit. It does not grant access to clock source selection or frequency configuration registers (flexgen), which remain secure-only.
Warning
This inheritance is not implemented for RIF-aware peripherals (GPIO, EXTI, RTC, HSEM, IPCC, PWR...). These are handled through the dedicated mechanism described below.

3.2. RIF-aware peripherals and RIF semaphore mode[edit | edit source]

RIF-aware peripherals such as GPIO integrate RIF protection directly inside the peripheral itself, not through RIFSC. Their clock enable bits in RCC are shared across multiple bus masters in the same register word. For example, RCC_AHB4ENR holds enable bits for all GPIO banks, each potentially owned by a different execution context. A plain read-modify-write from M33 could silently clear a bit that A35 Linux just set, and vice versa.

RCC, as a RIF-aware peripheral, uses RIF semaphore mode for these shared register words. In this mode, the RIF configuration maintains a CID whitelist (SEMWL) -- the set of CIDs authorized to access the resource. A master with an authorized CID must first acquire the RIF semaphore (a lock field embedded within the RCC RISUP register) before performing the read-modify-write, then release it. The STM32MP2 HAL implements this transparently: __HAL_RCC_GPIOx_CLK_ENABLE() acquires the RIF semaphore, updates the clock enable bit, then releases it.

In practice, the Secure world pre-enables GPIO clocks for all banks during platform initialization (OP-TEE in A35-TD flavor , TF-M in M33-TD flavor ). The M33 nonsecure application does not need to call __HAL_RCC_GPIOx_CLK_ENABLE() for GPIO banks it uses -- the clock is already running before M33 starts.

Warning
Never bypass __HAL_RCC_GPIOx_CLK_ENABLE() with a direct RCC register write on STM32MP2. The RIF semaphore acquire/release sequence is mandatory and is already implemented inside the HAL macro.

3.3. CID filtering and A35 low-power modes (A35-TD flavor only)[edit | edit source]

RCC implements automatic peripheral clock gating when a processor enters (LP/LPLV)Stop mode: all peripherals whose RIFSC CID owner is the sleeping processor are automatically clock-gated.

In A35-TD flavor , if a peripheral used by M33 is assigned to A35 (CID1) in RIFSC with CID filtering enabled (CFEN=1), its clock is automatically stopped when A35 enters Stop mode -- even if M33 is actively using it. This results in a silent hang or bus fault on the M33 side.

Warning
Do not use CID filtering (CFEN=1) for peripherals that the M33 must keep clocked while A35 is in low-power Stop mode. Set CFEN=0 and protect access using Secure/Privilege level filtering only.

This hazard does not apply to RIF-aware peripherals, which are already excluded from CID-based automatic clock gating by design.

4. When SCMI is needed[edit | edit source]

SCMI is needed when a system resource is not supposed to be directly programmed by the calling world, typically because it is:

  • hosted in the secure world
  • assigned to the Trusted Domain
  • shared between several agents
  • subject to centralized platform policy

Typical examples are:

  • parent clocks or root clocks used by several peripherals
  • voltage domains supplying shared devices
  • regulator-backed resources managed by the secure side

In such cases, the resource is still physically programmed through the platform hardware blocks such as RCC or PMIC-related interfaces, but the access path is indirect: the normal world sends a request through SCMI and the secure world performs the operation if allowed.

5. SCMI protocols[edit | edit source]

SCMI protocols are the message-based interfaces used to expose system resources from the secure owner to the application.

5.1. SCMI clock management protocol[edit | edit source]

5.1.1. General[edit | edit source]

The SCMI Clock Management Protocol is used to manage platform-controlled clocks.

It is used to:

  • enable or disable clocks
  • set rates
  • read current rates
  • read clock attributes
  • read or set parent clocks
  • query or apply clock configuration
  • optionally query clock permissions
  • optionally receive notifications for clock rate changes

5.1.2. Uses[edit | edit source]

On STM32MP2 series, a clock resource can belong to two different control models:

  • Direct local control:
    • the clock is directly accessible to the execution context
    • the local software uses the RCC driver or HAL RCC driver
    • this is generally the case for nonsecured leaf clocks already assigned to the caller
  • SCMI-managed control:
    • the clock is managed by the secure world
    • the client requests operations through SCMI
    • this is required for root clocks, parent clocks, shared clocks, or clocks subject to secure policy

In many cases, a local peripheral clock is only a leaf clock whose parent clock remains under secure control. In that case:

  • the parent clock must first be initialized by the secure side
  • the local peripheral clock may then be enabled by the owning driver
Information
When a clock is reported as enabled through SCMI, this does not automatically guarantee ownership of the resource from the caller point of view. If the application requires the clock to stay enabled, it should explicitly request it through the SCMI service.

5.1.3. Clock management examples from Cortex-M33[edit | edit source]

The following three examples illustrate each clock management model in practice. For the hardware background behind each case, see RCC clock security model and RIF inheritance.

Example Clock model M33 action Secure actor (A35-TD flavor ) Secure actor (M33-TD flavor )
ADC3 RIFSC inheritance -- bus clock enable only __HAL_RCC_ADC3_CLK_ENABLE() direct TF-A assigns CID2 at boot TF-M assigns CID2 at boot
I2C1 Flexgen kernel clock -- secure-only RCC registers SCMI / PSA clock service mandatory, then __HAL_RCC_I2C1_CLK_ENABLE() TF-A via SCMI over IPCC TF-M via PSA API / SCMI
GPIOF RIF-aware peripheral -- no RIFSC inheritance Nothing -- Secure world pre-enables OP-TEE platform init TF-M platform init
5.1.3.1. Example 1: ADC3 -- bus clock enabled directly via RIFSC inheritance[edit | edit source]

ADC3 is a standard peripheral protected by RIFSC. When the Secure world assigns ADC3 to M33 (CID2), the RCC clock enable bit (RCC_AHB2ENR.ADC3EN) is automatically writable by M33 nonsecure code. No SCMI call is needed. This behavior is identical in A35-TD flavor and M33-TD flavor .

M33 nonsecure firmware (same for both TD modes)
/* RIF inheritance: RIFSC CID2 assignment propagates to RCC_AHB2ENR.ADC3EN   */
__HAL_RCC_ADC3_CLK_ENABLE();

RCC_PeriphClkInitTypeDef PeriphClkInit = {0};
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC3;
PeriphClkInit.Adc3ClockSelection   = RCC_ADC3CLKSOURCE_PER;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);

hadc3.Instance                = ADC3;
hadc3.Init.Resolution         = ADC_RESOLUTION_12B;
hadc3.Init.ScanConvMode       = DISABLE;
hadc3.Init.ContinuousConvMode = DISABLE;
hadc3.Init.ExternalTrigConv   = ADC_SOFTWARE_START;
hadc3.Init.DataAlign          = ADC_DATAALIGN_RIGHT;
hadc3.Init.NbrOfConversion    = 1;
HAL_ADC_Init(&hadc3);
Warning
If __HAL_RCC_ADC3_CLK_ENABLE() is called before the Secure world has assigned ADC3 to CID2 in RIFSC, the write is silently ignored and subsequent HAL calls will hang.
5.1.3.2. Example 2: I2C1 -- flexgen kernel clock via SCMI (A35-TD flavor ) or PSA clock service (M33-TD flavor )[edit | edit source]

I2C1 is a standard peripheral whose flexgen kernel clock source is configured via secure-only RCC registers. The M33 nonsecure application must request kernel clock configuration through the Secure world before enabling the I2C1 bus clock via RIFSC inheritance. This is not a policy constraint -- it is a hardware requirement: NS writes to flexgen registers are silently discarded by the RCC RISUP.

A35-TD flavor M33-TD flavor
Secure service SCMI over IPCC to TF-A PSA clock service or SCMI to TF-M
Clock resource definition TF-A device tree clocks node TF-M platform BSP clock table
M33 firmware -- A35-TD flavor
/* Step 1: Enable I2C1 flexgen kernel clock via SCMI -- mandatory              */
/* Flexgen registers are secure-only; NS writes silently dropped by RCC RISUP */
scmi_status = scmi_clock_enable(SCMI_CHANNEL_M33, SCMI_CLK_I2C1);
if (scmi_status != SCMI_SUCCESS) { Error_Handler(); }

/* Step 2: Enable I2C1 bus clock -- allowed via RIFSC CID2 inheritance         */
__HAL_RCC_I2C1_CLK_ENABLE();

hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed     = 100000;
hi2c1.Init.DutyCycle      = I2C_DUTYCYCLE_2;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
HAL_I2C_Init(&hi2c1);
M33 firmware -- M33-TD flavor
/* Step 1: Enable I2C1 kernel clock via TF-M PSA clock service                */
psa_status = psa_call(TFM_CLOCK_SERVICE_HANDLE, TFM_CLOCK_ENABLE,
                      (psa_invec[]){ {&i2c1_clk_id, sizeof(i2c1_clk_id)} }, 1,
                      NULL, 0);
if (psa_status != PSA_SUCCESS) { Error_Handler(); }

/* Step 2: Enable I2C1 bus clock -- allowed via RIFSC CID2 inheritance         */
__HAL_RCC_I2C1_CLK_ENABLE();

hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed     = 100000;
hi2c1.Init.DutyCycle      = I2C_DUTYCYCLE_2;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
HAL_I2C_Init(&hi2c1);
Information
SCMI_CHANNEL_M33 and SCMI_CLK_I2C1 are platform-specific. In A35-TD flavor refer to the TF-A platform device tree. In M33-TD flavor refer to the TF-M platform BSP clock table.
5.1.3.3. Example 3: GPIOF -- no action from M33, clock pre-enabled by Secure world[edit | edit source]

GPIO is a RIF-aware peripheral. The RIFSC inheritance mechanism is not implemented for RIF-aware peripherals. The Secure world (OP-TEE in A35-TD flavor , TF-M in M33-TD flavor ) pre-enables GPIO clocks during platform initialization using RIF semaphore-protected RCC writes (RIF semaphore mode on the RCC RISUP). The M33 nonsecure application does not need to call __HAL_RCC_GPIOF_CLK_ENABLE().

M33 nonsecure firmware (same for both TD modes)
/*
 * DO NOT call __HAL_RCC_GPIOF_CLK_ENABLE() here.
 * {{TrustedDomainFlavor | flavor=A35-TD}}: OP-TEE pre-enabled GPIOF clock via RIF semaphore mode on the RCC RISUP.
 *{{TrustedDomainFlavor | flavor=M33-TD}}: TF-M tfm_platform_init() pre-enabled GPIOF clock.
 * GPIO is RIF-aware -- RIFSC inheritance does not apply.
 */
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin   = GPIO_PIN_4;
GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull  = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

HAL_GPIO_WritePin(GPIOF, GPIO_PIN_4, GPIO_PIN_SET);
Warning
The GPIOF RIF feature assignment (which pins M33 can access) must be configured by the Secure world. If pin 4 is not assigned to M33, HAL_GPIO_Init() and HAL_GPIO_WritePin() are silently ignored.

5.1.4. Typical APIs[edit | edit source]

Useful APIs in the implementation include:

  • scmi_clock_protocol_version()
  • scmi_clock_gate()
  • scmi_clock_enable()
  • scmi_clock_disable()
  • scmi_clock_get_config()
  • scmi_clock_get_attributes()
  • scmi_clock_get_rate()
  • scmi_clock_set_rate()

5.1.5. Clock API examples[edit | edit source]

This section provides practical examples of the SCMI Clock APIs:

  • query the SCMI Clock protocol version
  • enable or disable platform-managed clocks
  • read back clock configuration
  • read clock attributes
  • read clock rate
  • set clock rate
5.1.5.1. Available APIs[edit | edit source]

The following APIs are used in the clock examples:

int scmi_clock_gate(struct scmi_channel *channel, unsigned int clock_id, int enable);

int scmi_clock_get_config(struct scmi_channel *channel,
                          unsigned int clock_id,
                          uint32_t *flags,
                          uint32_t *attributes,
                          uint32_t *config);

int scmi_clock_get_attributes(struct scmi_channel *channel,
                              unsigned int clock_id,
                              char *name,
                              int len,
                              uint32_t *attributes);

int scmi_clock_enable(struct scmi_channel *channel, unsigned int clock_id);

int scmi_clock_disable(struct scmi_channel *channel, unsigned int clock_id);

int scmi_clock_protocol_version(struct scmi_channel *channel, uint32_t *version);
int scmi_clock_set_rate(struct scmi_channel *channel, unsigned int clock_id, unsigned int rate_khz);

int scmi_clock_get_rate(struct scmi_channel *channel,
                        unsigned int clock_id,
                        unsigned int *rate_khz);
5.1.5.2. API explanation[edit | edit source]
5.1.5.2.1. scmi_clock_protocol_version()[edit | edit source]

This API reads the SCMI Clock Management Protocol version supported by the SCMI server.

Read protocol version

uint32_t version = 0;

g_message_received_flag = scmi_clock_protocol_version(&scmi_channel, &version);
printf("SCMI Clock Protocol Version = 0x%lx, Return received from SCMI server = %ld\r\n",
       version, g_message_received_flag);
5.1.5.2.2. scmi_clock_gate()[edit | edit source]

This API is used to request clock enable or disable through the SCMI server.

Parameters:

  • clock_id: SCMI clock identifier
  • enable:
    • 1 to enable the clock
    • 0 to disable the clock

Enable clock 7

/* Enable clock 7 */
g_message_received_flag = scmi_clock_gate(&scmi_channel, 7, 1);
printf("Clock 7 gate: Return received from SCMI server = %ld\r\n",
       g_message_received_flag);

Disable clock 7

/* Disable clock 7 */
g_message_received_flag = scmi_clock_gate(&scmi_channel, 7, 0);
printf("Clock 7 gate disable: Return received from SCMI server = %ld\r\n",
       g_message_received_flag);
5.1.5.2.3. scmi_clock_enable() and scmi_clock_disable()[edit | edit source]

These APIs are helper calls used to explicitly enable or disable a clock through the SCMI service.

Enable clock 12

/* Enable clock 12 */
g_message_received_flag = scmi_clock_enable(&scmi_channel, 12);
printf("Clock 12 enable: Return received from SCMI server = %ld\r\n",
       g_message_received_flag);

Disable clock 12

/* Disable clock 12 */
g_message_received_flag = scmi_clock_disable(&scmi_channel, 12);
printf("Clock 12 disable: Return received from SCMI server = %ld\r\n",
       g_message_received_flag);
5.1.5.2.4. scmi_clock_get_config()[edit | edit source]

This API reads the configuration of a given clock from the SCMI server, which also includes enabling or disabling the clock.

Read clock configuration

g_message_received_flag = scmi_clock_get_config(&scmi_channel, 7, &flags, &attributes, &config);
printf("Clock 7 config after enable: config = 0x%lx, attributes = 0x%lx, Return received from SCMI server = %ld\r\n",
       config, attributes, g_message_received_flag);
5.1.5.2.5. scmi_clock_get_attributes()[edit | edit source]

This API reads the attributes of a given clock.

According to the SCMI Clock Management Protocol, the returned attributes can describe:

  • whether the clock is enabled or disabled
  • whether clock rate change notifications are supported
  • whether clock rate change requested notifications are supported
  • whether the clock has an extended name
  • whether parent clock identifiers are advertised
  • whether extended configuration is supported
  • whether the clock is restricted

Read clock attributes

g_message_received_flag = scmi_clock_get_attributes(&scmi_channel,
                                                    12,
                                                    (char *)&clk_name,
                                                    12,
                                                    &clk_attributes);
printf("Clock domain %d: Name = %s, attributes = 0x%lx, Return received from SCMI server = %ld\r\n",
       12, (char *)&clk_name, clk_attributes, g_message_received_flag);

This API can be used:

  • to retrieve the clock name
  • to inspect the returned attributes
  • to verify the enabled or disabled state

If in the returned value:

  • attributes = 0x1 means the clock is enabled
  • attributes = 0x0 means the clock is disabled
5.1.5.2.6. scmi_clock_get_rate()[edit | edit source]

This API reads the current clock rate from the SCMI server.

If the clock is disabled, the returned value corresponds to the rate at which the clock would run when re-enabled.

Read clock rate

g_message_received_flag = scmi_clock_get_rate(&scmi_channel, 12, &clk_rate_khz);
printf("Clock domain %d: Frequency (kHz) = %d, Return received from SCMI server = %ld\r\n",
       12, clk_rate_khz, g_message_received_flag);
5.1.5.2.7. scmi_clock_set_rate()[edit | edit source]

This API requests a new clock rate through the SCMI server.

Parameters:

  • clock_id: SCMI clock identifier
  • rate_khz: requested clock rate in kHz

Set clock rate

g_message_received_flag = scmi_clock_set_rate(&scmi_channel, 12, 24000);
printf("Clock domain %d: Requested frequency (kHz) = %d, Return received from SCMI server = %ld\r\n",
       12, 24000, g_message_received_flag);

5.2. SCMI Voltage Domain Management Protocol[edit | edit source]

5.2.1. General[edit | edit source]

The SCMI Voltage Domain Management Protocol is used to manage the configuration and voltage levels of voltage domains.

A voltage domain is a group of components supplied by a single voltage source. A voltage domain is different from a power domain: a voltage domain provides the supply level, while a power domain describes the on/off control of a set of components. A voltage domain can contain one or more power domains.

This protocol is especially useful when the voltage source is managed by the secure world and needs to be exposed to another execution context in a controlled way.

5.2.2. Uses[edit | edit source]

The Voltage Domain Management Protocol is used to:

  • discover voltage domains
  • list voltage levels supported by a domain
  • read the current voltage domain configuration
  • read the current voltage level
  • set the voltage domain mode
  • set the voltage level synchronously or asynchronously

Typical system resources associated with this protocol are:

  • shared regulator outputs
  • platform-managed voltage rails
  • I/O supply domains exposed through a secure power controller
  • external regulator or PMIC-backed rails when their control remains in secure world

5.2.3. Voltage domain operating modes[edit | edit source]

A voltage domain can operate in different modes. The SCMI voltage domain protocol defines at least the following architectural modes:

  • OFF: voltage supply to the domain is disabled
  • ON: voltage supply to the domain is active and the supplied components can operate normally

Implementation-defined modes may also exist.

5.2.4. Usage constraints[edit | edit source]

Information
The Voltage Domain Management Protocol should not be used when changing the voltage level requires coordinated changes on other resources that remain under secure platform policy. In such cases, the secure side must manage the complete sequence.

In a multi-agent system:

  • a voltage domain can be exclusive to one agent
  • a voltage domain can be shared by several agents

When the domain is shared, the platform is responsible for arbitration and policy enforcement.

5.2.5. Typical APIs and protocol commands[edit | edit source]

Typical protocol commands are:

  • PROTOCOL_VERSION
  • PROTOCOL_ATTRIBUTES
  • PROTOCOL_MESSAGE_ATTRIBUTES
  • VOLTAGE_DOMAIN_ATTRIBUTES
  • VOLTAGE_DESCRIBE_LEVELS
  • VOLTAGE_CONFIG_SET
  • VOLTAGE_CONFIG_GET
  • VOLTAGE_LEVEL_SET
  • VOLTAGE_LEVEL_GET
  • VOLTAGE_DOMAIN_NAME_GET

In asynchronous mode, the protocol may also return:

  • VOLTAGE_LEVEL_SET_COMPLETE

5.2.6. Available APIs[edit | edit source]

The following APIs are used in the voltage domain examples:

int scmi_status_to_ret(int32_t scmi_status);
int scmi_voltage_domain_enable(struct scmi_channel *channel, unsigned int domain_id);
int scmi_voltage_domain_disable(struct scmi_channel *channel, unsigned int domain_id);
int scmi_voltage_domain_name(struct scmi_channel *channel, unsigned int domain_id, char *name, int n);

5.2.7. API explanation[edit | edit source]

5.2.7.1. scmi_status_to_ret()[edit | edit source]

This helper API converts the SCMI protocol return status into a generic software return code usable by the client side.

It is typically used after the SCMI response has been received and decoded.

5.2.7.2. scmi_voltage_domain_enable()[edit | edit source]

This API requests the secure side to enable a voltage domain.

Parameter:

  • domain_id: SCMI voltage domain identifier

Internally this API sends a VOLTAGE_CONFIG_SET request with the domain configuration set to ON.

Enable a voltage domain

int ret = 0;
ret = scmi_voltage_domain_enable(&scmi_channel, VOLTD_SCMI_ADC);
if (ret)
  loc_printf("Failed to enable VOLTD_SCMI_ADC (error code %d)\r\n", ret);
5.2.7.3. scmi_voltage_domain_disable()[edit | edit source]

This API requests the secure side to disable a voltage domain.

Parameter:

  • domain_id: SCMI voltage domain identifier

Internally this API sends a VOLTAGE_CONFIG_SET request with the domain configuration set to OFF.

Disable a voltage domain

int ret = 0;
ret = scmi_voltage_domain_disable(&scmi_channel, VOLTD_SCMI_ADC);
if (ret)
  loc_printf("Failed to disable VOLTD_SCMI_ADC (error code %d)\r\n", ret);
5.2.7.4. scmi_voltage_domain_name()[edit | edit source]

This API reads the name of a voltage domain from the SCMI server.

Parameter:

  • domain_id: SCMI voltage domain identifier

Output:

  • name: buffer containing the voltage domain name returned by the server

Internally this API sends a VOLTAGE_DOMAIN_ATTRIBUTES request and copies the returned name field.

Read voltage domain name

char voltd_name[16] = { 0 };
int ret = 0;
ret = scmi_voltage_domain_name(&scmi_channel, VOLTD_SCMI_ADC, voltd_name, sizeof(voltd_name));
if (!ret)
  loc_printf("Voltage domain name = %s\r\n", voltd_name);

5.2.8. Voltage domain usage example[edit | edit source]

Enable ADC regulator directly or through SCMI

/* Enable ADC regulator */
if (ResMgr_Request(RESMGR_RESOURCE_RIF_PWR, RESMGR_PWR_RESOURCE(0)) == RESMGR_STATUS_ACCESS_OK)
{
  loc_printf("Enable ADC regulator directly\r\n");
  HAL_PWREx_EnableSupply(PWR_PVM_A);
}
else
{
  int ret = 0;
  loc_printf("Enable ADC regulator with SCMI\r\n");
  ret = scmi_voltage_domain_enable(&scmi_channel, VOLTD_SCMI_ADC);
  if (ret)
    loc_printf("Failed to enable VOLTD_SCMI_ADC (error code %d)\r\n", ret);
}

If the voltage regulator resource is assigned to the CM33 core, then CM33 can enable the voltage regulator directly using the local HAL API. In that case, no SCMI call is required.

On the other hand, if the application code is running on CM33, but the same resource is owned by Cortex-A35, then CM33 cannot program the regulator directly. In this case, the SCMI agent running on CM33 must send a request to the SCMI server running on CA35. The CA35 secure side, which has access to the resource, performs the voltage enable operation on behalf of the CM33 client.

This mechanism allows CM33 software to enable or disable a system resource that is not directly accessible to it, while preserving the resource ownership and security policy defined by the platform.

6. How to go further[edit | edit source]

For generic SCMI architecture, transport, source code location, monitoring, tracing and debug information, refer to:

For the Linux kernel frameworks using SCMI-exposed resources, refer to:

7. References[edit | edit source]