| /* SPDX-License-Identifier: BSD-3-Clause */ |
| /** |
| * Copyright 2019-2024 NXP |
| * |
| * KEYWORDS: micro-power uPower driver API |
| */ |
| |
| #include <string.h> |
| |
| #include "upower_api.h" |
| #include "upower_soc_defs.h" |
| |
| /* --------------------------------------------------------------- |
| * Common Macros |
| * --------------------------------------------------------------- |
| */ |
| |
| /* tests Service Group busy */ |
| #define UPWR_SG_BUSY(sg) ((sg_busy & (1U << (sg))) == 1U) |
| |
| /* install user callback for the Service Group */ |
| #define UPWR_USR_CALLB(sg, cb) { user_callback[(sg)] = (cb); } |
| |
| /* fills up common message header info */ |
| #define UPWR_MSG_HDR(hdr, sg, fn) { \ |
| (hdr).domain = (uint32_t)pwr_domain; \ |
| (hdr).srvgrp = (sg); \ |
| (hdr).function = (fn); } |
| |
| /* --------------------------------------------------------------- |
| * Common Data Structures |
| * --------------------------------------------------------------- |
| */ |
| static soc_domain_t pwr_domain; |
| |
| static upwr_code_vers_t fw_rom_version; |
| static upwr_code_vers_t fw_ram_version; |
| static uint32_t fw_launch_option; |
| |
| /* shared memory buffers */ |
| #define UPWR_API_BUFFER_SIZE (MAX_SG_EXCEPT_MEM_SIZE + \ |
| MAX_SG_PWRMGMT_MEM_SIZE + MAX_SG_VOLTM_MEM_SIZE) |
| |
| /* service group shared mem buffer pointers */ |
| static void *sh_buffer[UPWR_SG_COUNT]; |
| |
| /* Callbacks registered for each service group : |
| * |
| * NULL means no callback is registered; |
| * for sgrp_callback, it also means the service group is |
| * free to receive a new request. |
| */ |
| static upwr_callb user_callback[UPWR_SG_COUNT]; |
| static UPWR_RX_CALLB_FUNC_T sgrp_callback[UPWR_SG_COUNT]; |
| |
| /* request data structures for each service group */ |
| /* message waiting for TX */ |
| static upwr_down_max_msg sg_req_msg[UPWR_SG_COUNT]; |
| /* waiting message size */ |
| static unsigned int sg_req_siz[UPWR_SG_COUNT]; |
| /* response msg */ |
| static upwr_up_max_msg sg_rsp_msg[UPWR_SG_COUNT]; |
| /* response msg size */ |
| static unsigned int sg_rsp_siz[UPWR_SG_COUNT]; |
| |
| /* tx pending status for each (1 bit per service group) */ |
| static volatile uint32_t sg_tx_pend; |
| /* serv.group of current ongoing Tx, if any */ |
| static volatile upwr_sg_t sg_tx_curr; |
| |
| /* service group busy status, only for this domain (MU index 0) */ |
| /* SG bit = 1 if group is busy with a request */ |
| static volatile uint32_t sg_busy; |
| |
| /* OS-dependent memory allocation function */ |
| static upwr_malloc_ptr_t os_malloc; |
| /* OS-dependent pointer->physical address conversion function */ |
| static upwr_phyadr_ptr_t os_ptr2phy; |
| /* OS-dependent function to lock critical code */ |
| static upwr_lock_ptr_t os_lock; |
| |
| /* pointer to MU structure */ |
| static struct MU_t *mu; |
| |
| /* |
| * indicates that a transmission was done and is pending; this |
| * bit is necessary because the Tx and Rx interrupts are ORed |
| * together, and there is no way of telling if only Rx interrupt |
| * or both occurred just by looking at the MU status registers |
| */ |
| static uint32_t mu_tx_pend; |
| |
| static UPWR_TX_CALLB_FUNC_T mu_tx_callb; |
| static UPWR_RX_CALLB_FUNC_T mu_rx_callb; |
| |
| #define UPWR_API_INIT_WAIT (0U) /* waiting for ROM firmware initialization */ |
| #define UPWR_API_INITLZED (1U) /* ROM firmware initialized */ |
| #define UPWR_API_START_WAIT (2U) /* waiting for start services */ |
| #define UPWR_API_SHUTDOWN_WAIT (3U) /* waiting for shutdown */ |
| #define UPWR_API_READY (4U) /* ready to receive service requests */ |
| |
| volatile upwr_api_state_t api_state; |
| |
| /* default pointer->physical address conversion, returns the same address */ |
| static void *ptr2phys(const void *ptr) |
| { |
| return (void *)ptr; |
| } |
| |
| /* --------------------------------------------------------------- |
| * SHARED MEMORY MANAGEMENT |
| * -------------------------------------------------------------- |
| */ |
| |
| /* |
| * upwr_ptr2offset() - converts a pointer (casted to uint64_t) to an |
| * address offset from the shared memory start. If it does not point |
| * to a shared memory location, the structure pointed is copied to a |
| * buffer in the shared memory, and the buffer offset is returned. |
| * The 2nd argument is the service group to which the buffer belongs; |
| * The 3rd argument is the size of structure to be copied. The 4th argument |
| * is an offset to apply to the copy destination address. The 5th argument |
| * is ptr before the conversion to physical address. 2nd, 3rd. 4th and 5th |
| * arguments are not used if the 1st one points to a location inside the |
| * shared memory. |
| */ |
| |
| static uint32_t upwr_ptr2offset(unsigned long ptr, |
| upwr_sg_t sg, |
| size_t siz, |
| size_t offset, |
| const void *vptr) |
| { |
| if ((ptr >= UPWR_DRAM_SHARED_BASE_ADDR) && |
| ((ptr - UPWR_DRAM_SHARED_BASE_ADDR) < UPWR_DRAM_SHARED_SIZE)) { |
| return (uint32_t)(ptr - UPWR_DRAM_SHARED_BASE_ADDR); |
| } |
| |
| /* pointer is outside the shared memory, copy the struct to buffer */ |
| (void)memcpy((void *)(offset + (char *)sh_buffer[sg]), (void *)vptr, siz); |
| return (uint32_t)((unsigned long)sh_buffer[sg] + offset - UPWR_DRAM_SHARED_BASE_ADDR); |
| } |
| |
| /* |
| * --------------------------------------------------------------- |
| * INTERRUPTS AND CALLBACKS |
| * Service-group specific callbacks are in their own sections |
| * -------------------------------------------------------------- |
| */ |
| |
| /* |
| * upwr_lock()- locks (lock=1) or unlocks (lock=0) a critical code section; |
| * for now it only needs to protect a portion of the code from being |
| * interrupted by the MU. |
| */ |
| static void upwr_lock(int lock) |
| { |
| if (os_lock != NULL) { |
| os_lock(lock); |
| } |
| } |
| |
| /* upwr_exp_isr()- handles the exception interrupt from uPower */ |
| static void upwr_exp_isr(void) |
| { |
| } |
| |
| /* upwr_copy2tr prototype; function definition in auxiliary function section */ |
| void upwr_copy2tr(struct MU_t *local_mu, const uint32_t *msg, unsigned int size); |
| |
| #define UPWR_MU_TSR_EMPTY ((uint32_t)((1UL << UPWR_MU_MSG_SIZE) - 1UL)) |
| |
| /* upwr_txrx_isr()- handles both the Tx and Rx MU interrupts */ |
| void upwr_txrx_isr(void) |
| { |
| /* Tx pending and TX register empty */ |
| if ((mu_tx_pend != 0UL) && (mu->TSR.R == UPWR_MU_TSR_EMPTY)) { |
| mu_tx_pend = 0UL; |
| /* disable the tx interrupts */ |
| mu->TCR.R = 0U; |
| /* urgency flag off, in case it was set */ |
| mu->FCR.B.F0 = 0U; |
| |
| if (mu_tx_callb != NULL) { |
| mu_tx_callb(); |
| } |
| } |
| |
| /* RX ISR occurred */ |
| if (mu->RSR.R != 0UL) { |
| /* disable the interrupt until data is read */ |
| mu->RCR.R = 0U; |
| |
| if (mu_rx_callb != NULL) { |
| mu_rx_callb(); |
| } |
| } |
| } |
| |
| /** |
| * upwr_next_req() - sends the next pending service request message, if any. |
| * |
| * Called upon MU Tx interrupts, it checks if there is any service request |
| * pending amongst the service groups, and sends the request if needed. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: none (void). |
| */ |
| static void upwr_next_req(void) |
| { |
| upwr_sg_t sg = (upwr_sg_t)0U; |
| |
| /* no lock needed here, this is called from an MU ISR */ |
| sg_tx_pend &= ~((uint32_t)1UL << sg_tx_curr); /* no longer pending */ |
| |
| if (sg_tx_pend == 0U) { |
| return; /* no other pending */ |
| } |
| |
| /* find the next one pending */ |
| for (uint32_t mask = 1UL; mask < (1UL << UPWR_SG_COUNT); mask = mask << 1UL) { |
| if ((sg_tx_pend & mask) != 0U) { |
| break; |
| } |
| |
| sg = (upwr_sg_t)(sg + 1U); |
| } |
| |
| sg_tx_curr = sg; |
| if (upwr_tx((uint32_t *)&sg_req_msg[sg], sg_req_siz[sg], upwr_next_req) < 0) { |
| return; /* leave the Tx pending */ |
| } |
| } |
| |
| /** |
| * upwr_mu_int_callback() - general MU interrupt callback. |
| * |
| * Called upon MU Rx interrupts, it calls the Service Group-specific callback, |
| * if any registered, based on the service group field in the received message. |
| * Otherwise, calls the user callback, if any registered. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: none (void). |
| */ |
| static void upwr_mu_int_callback(void) |
| { |
| upwr_sg_t sg; /* service group number */ |
| UPWR_RX_CALLB_FUNC_T sg_callb; /* service group callback */ |
| upwr_up_max_msg rxmsg = {0}; |
| unsigned int size; /* in words */ |
| |
| if (upwr_rx((char *)&rxmsg, &size) < 0) { |
| return; |
| } |
| |
| sg = (upwr_sg_t)rxmsg.hdr.srvgrp; |
| |
| /* copy msg to the service group buffer */ |
| msg_copy((char *)&sg_rsp_msg[sg], (char *)&rxmsg, size); |
| sg_rsp_siz[sg] = size; |
| |
| /* clear the service group busy status */ |
| sg_busy &= ~(1UL << sg); /* no lock needed here, we're in the MU ISR */ |
| |
| sg_callb = sgrp_callback[sg]; |
| if (sg_callb == NULL) { |
| upwr_callb user_callb = user_callback[sg]; |
| /* no service group callback; call the user callback if any */ |
| if (user_callb == NULL) { |
| goto done; /* no user callback */ |
| } |
| |
| /* make the user callback */ |
| user_callb(sg, rxmsg.hdr.function, |
| (upwr_resp_t)rxmsg.hdr.errcode, |
| (size == 2U) ? rxmsg.word2 : rxmsg.hdr.ret); |
| goto done; |
| } |
| |
| /* |
| * finally make the group callback. don't uninstall the group |
| * callback, it is permanent. |
| */ |
| sg_callb(); |
| done: |
| if (rxmsg.hdr.errcode == UPWR_RESP_SHUTDOWN) { /* shutdown error: */ |
| /* |
| * change the API state automatically. so new requests |
| * are rejected by the API immediately |
| */ |
| api_state = UPWR_API_INITLZED; |
| } |
| } |
| |
| /** |
| * upwr_srv_req() - sends a service request message. |
| * @sg: message service group. |
| * @msg: pointer to the message |
| * @size: message size in 32-bit words. |
| * |
| * The message is sent right away if possible, or gets pending to be sent later. |
| * If pending, the message is stored in sg_req_msg and will be sent when the |
| * MU transmission buffer is clear and there are no other pending messages |
| * from higher priority service groups. |
| * |
| * This is an auxiliary function used by the rest of the API calls. |
| * It is normally not called by the driver code, unless maybe for test purposes. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: none (void) |
| */ |
| static void upwr_srv_req(upwr_sg_t sg, |
| uint32_t *msg, |
| unsigned int size) |
| { |
| int rc; |
| |
| upwr_lock(1); |
| sg_busy |= (uint32_t)1U << sg; |
| upwr_lock(0); |
| |
| rc = upwr_tx(msg, size, upwr_next_req); |
| if (rc < 0) { |
| /* queue full, make the transmission pending */ |
| msg_copy((char *)&sg_req_msg[sg], (char *)msg, size); |
| sg_req_siz[sg] = size; |
| |
| upwr_lock(1); |
| sg_tx_curr = sg; |
| sg_tx_pend |= (uint32_t)1U << sg; |
| upwr_lock(0); |
| |
| return; |
| } |
| } |
| |
| /**--------------------------------------------------------------- |
| * INITIALIZATION, CONFIGURATION |
| * |
| * A reference uPower initialization sequence goes as follows: |
| * |
| * 1. host CPU calls upwr_init. |
| * 2. (optional) host checks the ROM version and SoC code calling upwr_vers(...) |
| * and optionally performs any configuration or workaround accordingly. |
| * 3. host CPU calls upwr_start to start the uPower services, passing a |
| * service option number. |
| * If no RAM code is loaded or it has no service options, the launch option |
| * number passed must be 0, which will start the services available in ROM. |
| * upwr_start also receives a pointer to a callback called by the API |
| * when the firmware is ready to receive service requests. |
| * The callback may be replaced by polling, calling upwr_req_status in a loop |
| * or upwr_poll_req_status; in this case the callback pointer may be NULL. |
| * A host may call upwr_start even if the services were already started by |
| * any host: if the launch option is the same, the response will be ok, |
| * but will indicate error if the services were already started with a |
| * different launch option. |
| * 4. host waits for the callback calling, or polling finishing; |
| * if no error is returned, it can start making service calls using the API. |
| * |
| * Variations on that reference sequence are possible: |
| * - the uPower services can be started using the ROM code only, which includes |
| * the basic Power Management services, among others, with launch option |
| * number = 0. |
| * The code RAM can be loaded while these services are running and, |
| * when the loading is done, the services can be re-started with these 2 |
| * requests executed in order: upwr_xcp_shutdown and upwr_start, |
| * using the newly loaded RAM code (launch option > 0). |
| * |
| * NOTE: the initialization call upwr_init is not effective and |
| * returns error when called after the uPower services are started. |
| */ |
| |
| /** |
| * upwr_start_callb() - internal callback for the Rx message from uPower |
| * that indicates the firmware is ready to receive the start commands. |
| * It calls the user callbacks registered in the upwr_start_boot and upwr_start |
| * call. |
| */ |
| void upwr_start_callb(void) |
| { |
| switch (api_state) { |
| case UPWR_API_START_WAIT: { |
| upwr_rdy_callb start_callb = (upwr_rdy_callb)user_callback[UPWR_SG_EXCEPT]; |
| upwr_ready_msg *msg = (upwr_ready_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT]; |
| |
| fw_ram_version.soc_id = fw_rom_version.soc_id; |
| fw_ram_version.vmajor = msg->args.vmajor; |
| fw_ram_version.vminor = msg->args.vminor; |
| fw_ram_version.vfixes = msg->args.vfixes; |
| |
| /* |
| * vmajor == vminor == vfixes == 0 indicates start error |
| * in this case, go back to the INITLZED state |
| */ |
| if ((fw_ram_version.vmajor != 0U) || |
| (fw_ram_version.vminor != 0U) || |
| (fw_ram_version.vfixes != 0U)) { |
| api_state = UPWR_API_READY; |
| |
| /* |
| * initialization is over: |
| * uninstall the user callback just in case |
| */ |
| UPWR_USR_CALLB(UPWR_SG_EXCEPT, NULL); |
| |
| if (fw_launch_option == 0U) { |
| /* |
| * launched ROM firmware: |
| * RAM fw versions must be all 0s |
| */ |
| fw_ram_version.vmajor = 0U; |
| fw_ram_version.vminor = 0U; |
| fw_ram_version.vfixes = 0U; |
| } |
| } else { |
| api_state = UPWR_API_INITLZED; |
| } |
| |
| start_callb(msg->args.vmajor, msg->args.vminor, msg->args.vfixes); |
| } |
| break; |
| |
| case UPWR_API_SHUTDOWN_WAIT: { |
| upwr_callb user_callb = (upwr_callb)user_callback[UPWR_SG_EXCEPT]; |
| upwr_shutdown_msg *msg = (upwr_shutdown_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT]; |
| |
| if ((upwr_resp_t)msg->hdr.errcode == UPWR_RESP_OK) { |
| api_state = UPWR_API_INITLZED; |
| } |
| |
| if (user_callb != NULL) { |
| user_callb(UPWR_SG_EXCEPT, UPWR_XCP_SHUTDOWN, |
| (upwr_resp_t)msg->hdr.errcode, 0U); |
| } |
| } |
| break; |
| |
| case UPWR_API_READY: |
| { |
| upwr_callb user_callb = (upwr_callb)user_callback[UPWR_SG_EXCEPT]; |
| upwr_up_max_msg *msg = (upwr_up_max_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT]; |
| |
| if (user_callb != NULL) { |
| user_callb(UPWR_SG_EXCEPT, msg->hdr.function, |
| (upwr_resp_t)msg->hdr.errcode, |
| (int)((sg_rsp_siz[UPWR_SG_EXCEPT] == 2U) ? |
| msg->word2 : msg->hdr.ret)); |
| } |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| /** |
| * upwr_init() - API initialization; must be the first API call after reset. |
| * @domain: SoC-dependent CPU domain id; identifier used by the firmware in |
| * many services. Defined by SoC-dependent type soc_domain_t found in |
| * upower_soc_defs.h. |
| * @muptr: pointer to the MU instance. |
| * @mallocptr: pointer to the memory allocation function |
| * @physaddrptr: pointer to the function to convert pointers to |
| * physical addresses. If NULL, no conversion is made (pointer=physical address) |
| * @isrinstptr: pointer to the function to install the uPower ISR callbacks; |
| * the function receives the pointers to the MU tx/rx and Exception ISRs |
| * callbacks, which must be called from the actual system ISRs. |
| * The function pointed by isrinstptr must also enable the interrupt at the |
| * core/interrupt controller, but must not enable the interrupt at the MU IP. |
| * The system ISRs are responsible for dealing with the interrupt controller, |
| * performing any other context save/restore, and any other housekeeping. |
| * @lockptr: pointer to a function that prevents MU interrupts (if argrument=1) |
| * or allows it (if argument=0). The API calls this function to make small |
| * specific code portions thread safe. Only MU interrupts must be avoided, |
| * the code may be suspended for other reasons. |
| * If no MU interrupts can happen during the execution of an API call or |
| * callback, even if enabled, for some other reason (e.g. interrupt priority), |
| * then this argument may be NULL. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if failed to allocate memory, or use some other resource. |
| * -2 if any argument is invalid. |
| * -3 if failed to send the ping message. |
| * -4 if failed to receive the initialization message, or was invalid |
| */ |
| int upwr_init(soc_domain_t domain, struct MU_t *muptr, |
| const upwr_malloc_ptr_t mallocptr, |
| const upwr_phyadr_ptr_t phyadrptr, |
| const upwr_inst_isr_ptr_t isrinstptr, |
| const upwr_lock_ptr_t lockptr) |
| { |
| uint32_t j; |
| |
| upwr_sg_t sg; /* service group number */ |
| unsigned int size; |
| unsigned long dom_buffer_base = (domain == RTD_DOMAIN) ? UPWR_API_BUFFER_BASE : |
| ((UPWR_API_BUFFER_ENDPLUS + UPWR_API_BUFFER_BASE) / 2U); |
| |
| upwr_init_msg *msg = (upwr_init_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT]; |
| |
| mu = muptr; |
| /* |
| * Disable tx and rx interrupts in case not called |
| * 1st time after reset |
| */ |
| mu->TCR.R = mu->RCR.R = 0U; |
| |
| os_malloc = mallocptr; |
| os_ptr2phy = (phyadrptr == (upwr_phyadr_ptr_t)NULL) ? ptr2phys : phyadrptr; |
| |
| os_lock = lockptr; |
| api_state = UPWR_API_INIT_WAIT; |
| sg_busy = 0UL; |
| pwr_domain = domain; |
| |
| /* initialize the versions, in case they are polled */ |
| fw_rom_version.soc_id = 0U; |
| fw_rom_version.vmajor = 0U; |
| fw_rom_version.vminor = 0U; |
| fw_rom_version.vfixes = 0U; |
| |
| fw_ram_version.soc_id = 0U; |
| fw_ram_version.vmajor = 0U; |
| fw_ram_version.vminor = 0U; |
| fw_ram_version.vfixes = 0U; |
| |
| mu_tx_pend = (uint32_t)0U; |
| sg_tx_pend = (uint32_t)0U; |
| |
| sg_tx_curr = UPWR_SG_COUNT; /* means none here */ |
| |
| sh_buffer[UPWR_SG_EXCEPT] = (void *)(unsigned long)dom_buffer_base; |
| sh_buffer[UPWR_SG_PWRMGMT] = (void *)(unsigned long)(dom_buffer_base + |
| MAX_SG_EXCEPT_MEM_SIZE); |
| sh_buffer[UPWR_SG_DELAYM] = NULL; |
| sh_buffer[UPWR_SG_VOLTM] = (void *)(unsigned long)(dom_buffer_base + |
| MAX_SG_EXCEPT_MEM_SIZE + MAX_SG_PWRMGMT_MEM_SIZE); |
| sh_buffer[UPWR_SG_CURRM] = NULL; |
| sh_buffer[UPWR_SG_TEMPM] = NULL; |
| sh_buffer[UPWR_SG_DIAG] = NULL; |
| |
| /* (no buffers service groups other than xcp and pwm for now) */ |
| for (j = 0; j < UPWR_SG_COUNT; j++) { |
| user_callback[j] = NULL; |
| /* service group Exception gets the initialization callbacks */ |
| sgrp_callback[j] = (j == UPWR_SG_EXCEPT) ? upwr_start_callb : NULL; |
| /* response messages with an initial consistent content */ |
| sg_rsp_msg[j].hdr.errcode = UPWR_RESP_SHUTDOWN; |
| } |
| |
| /* init message already received, assume takss are running on upower */ |
| if (mu->FSR.B.F0 != 0U) { |
| /* send a ping message down to get the ROM version back */ |
| upwr_xcp_ping_msg ping_msg = {0}; |
| |
| ping_msg.hdr.domain = pwr_domain; |
| ping_msg.hdr.srvgrp = UPWR_SG_EXCEPT; |
| ping_msg.hdr.function = UPWR_XCP_PING; |
| |
| if (mu->RSR.B.RF0 != 0U) { /* first clean any Rx message left over */ |
| (void)upwr_rx((char *)msg, &size); |
| } |
| |
| /* wait any TX left over to be sent */ |
| while (mu->TSR.R != UPWR_MU_TSR_EMPTY) { |
| } |
| |
| /* |
| * now send the ping message; |
| * do not use upwr_tx, which needs API initialized; |
| * just write to the MU TR register(s) |
| */ |
| mu->FCR.B.F0 = 1U; /* flag urgency status */ |
| upwr_copy2tr(mu, (uint32_t *)&ping_msg, sizeof(ping_msg) / 4U); |
| } |
| |
| do { |
| /* |
| * poll for the MU Rx status: wait for an init message, either |
| * 1st sent from uPower after reset or as a response to a ping |
| */ |
| while (mu->RSR.B.RF0 == 0U) { |
| } |
| |
| /* urgency status off, in case it was set */ |
| mu->FCR.B.F0 = 0U; |
| |
| if (upwr_rx((char *)msg, &size) < 0) { |
| return -4; |
| } |
| |
| if (size != (sizeof(upwr_init_msg) / 4U)) { |
| if (mu->FSR.B.F0 != 0U) { |
| continue; /* discard left over msg */ |
| } else { |
| return -4; |
| } |
| } |
| |
| sg = (upwr_sg_t)msg->hdr.srvgrp; |
| if (sg != UPWR_SG_EXCEPT) { |
| if (mu->FSR.B.F0 != 0U) { |
| continue; /* discard left over msg */ |
| } else { |
| return -4; |
| } |
| } |
| |
| if ((upwr_xcp_f_t)msg->hdr.function != UPWR_XCP_INIT) { |
| if (mu->FSR.B.F0 != 0U) { |
| continue; /* discard left over msg */ |
| } else { |
| return -4; |
| } |
| } |
| |
| break; |
| } while (true); |
| |
| fw_rom_version.soc_id = msg->args.soc; |
| fw_rom_version.vmajor = msg->args.vmajor; |
| fw_rom_version.vminor = msg->args.vminor; |
| fw_rom_version.vfixes = msg->args.vfixes; |
| |
| if (upwr_rx_callback(upwr_mu_int_callback) < 0) { |
| /* catastrophic error, but is it possible to happen? */ |
| return -1; |
| } |
| |
| mu_tx_callb = NULL; /* assigned on upwr_tx */ |
| |
| /* install the ISRs and enable the interrupts */ |
| isrinstptr(upwr_txrx_isr, upwr_exp_isr); |
| |
| /* enable only RR[0] receive interrupt */ |
| mu->RCR.R = 1U; |
| |
| api_state = UPWR_API_INITLZED; |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_start() - Starts the uPower services. |
| * @launchopt: a number to select between multiple launch options, |
| * that may define, among other things, which services will be started, |
| * or which services implementations, features etc. |
| * launchopt = 0 selects a subset of services implemented in ROM; |
| * any other number selects service sets implemented in RAM, launched |
| * by the firmware function ram_launch; if an invalid launchopt value is passed, |
| * no services are started, and the callback returns error (see below). |
| * @rdycallb: pointer to the callback to be called when the uPower is ready |
| * to receive service requests. NULL if no callback needed. |
| * The callback receives as arguments the RAM firmware version numbers. |
| * If all 3 numbers (vmajor, vminor, vfixes) are 0, that means the |
| * service launching failed. |
| * Firmware version numbers will be the same as ROM if launchopt = 0, |
| * selecting the ROM services. |
| * |
| * upwr_start can be called by any domain even if the services are already |
| * started: it has no effect, returning success, if the launch option is the |
| * same as the one that actually started the service, and returns error if |
| * called with a different option. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded or |
| * not. |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if a resource failed, |
| * -2 if the domain passed is the same as the caller, |
| * -3 if called in an invalid API state |
| */ |
| int upwr_start(uint32_t launchopt, const upwr_rdy_callb rdycallb) |
| { |
| upwr_start_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_INITLZED) { |
| return -3; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_EXCEPT, (upwr_callb)rdycallb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_START); |
| |
| txmsg.hdr.arg = fw_launch_option = launchopt; |
| |
| if (upwr_tx((uint32_t *)&txmsg, sizeof(txmsg) / 4U, NULL) < 0) { |
| /* catastrophic error, but is it possible to happen? */ |
| return -1; |
| } |
| |
| api_state = UPWR_API_START_WAIT; |
| |
| return 0; |
| } |
| |
| /**--------------------------------------------------------------- |
| * EXCEPTION SERVICE GROUP |
| */ |
| |
| /** |
| * upwr_xcp_config() - Applies general uPower configurations. |
| * @config: pointer to the uPower SoC-dependent configuration struct |
| * upwr_xcp_config_t defined in upower_soc_defs.h. NULL may be passed, meaning |
| * a request to read the configuration, in which case it appears in the callback |
| * argument ret, or can be pointed by argument retptr in the upwr_req_status and |
| * upwr_poll_req_status calls, casted to upwr_xcp_config_t. |
| * @callb: pointer to the callback to be called when the uPower has finished |
| * the configuration, or NULL if no callback needed (polling used instead). |
| * |
| * Some configurations are targeted for a specific domain (see the struct |
| * upwr_xcp_config_t definition in upower_soc_defs.h); this call has implicit |
| * domain target (the same domain from which is called). |
| * |
| * The return value is always the current configuration value, either in a |
| * read-only request (config = NULL) or after setting a new configuration |
| * (non-NULL config). |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded or |
| * not. |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| */ |
| int upwr_xcp_config(const upwr_xcp_config_t *config, const upwr_callb callb) |
| { |
| upwr_xcp_config_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); |
| |
| if (config == NULL) { |
| txmsg.hdr.arg = 1U; /* 1= read, txmsg.word2 ignored */ |
| } else { |
| txmsg.hdr.arg = 0U; /* 1= write */ |
| txmsg.word2 = config->R; |
| } |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_CONFIG); |
| |
| upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_xcp_sw_alarm() - Makes uPower issue an alarm interrupt to given domain. |
| * @domain: identifier of the domain to alarm. Defined by SoC-dependent type |
| * soc_domain_t found in upower_soc_defs.h. |
| * @code: alarm code. Defined by SoC-dependent type upwr_alarm_t found in |
| * upower_soc_defs.h. |
| * @callb: pointer to the callback to be called when the uPower has finished |
| * the alarm, or NULL if no callback needed (polling used instead). |
| * |
| * The function requests the uPower to issue an alarm of the given code as if |
| * it had originated internally. This service is useful mainly to test the |
| * system response to such alarms, or to make the system handle a similar alarm |
| * situation detected externally to uPower. |
| * |
| * The system ISR/code handling the alarm may retrieve the alarm code by calling |
| * the auxiliary function upwr_alarm_code. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded or |
| * not. |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| */ |
| int upwr_xcp_sw_alarm(soc_domain_t domain, |
| upwr_alarm_t code, |
| const upwr_callb callb) |
| { |
| upwr_xcp_swalarm_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SW_ALARM); |
| txmsg.hdr.domain = (uint32_t)domain; |
| txmsg.hdr.arg = (uint32_t)code; |
| |
| upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_xcp_set_ddr_retention() - M33/A35 can use this API to set/clear ddr retention |
| * @domain: identifier of the caller domain. |
| * soc_domain_t found in upower_soc_defs.h. |
| * @enable: true, means that set ddr retention, false clear ddr retention. |
| * @callb: NULL |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| */ |
| int upwr_xcp_set_ddr_retention(soc_domain_t domain, |
| uint32_t enable, |
| const upwr_callb callb) |
| { |
| upwr_xcp_ddr_retn_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SET_DDR_RETN); |
| txmsg.hdr.domain = (uint32_t)domain; |
| txmsg.hdr.arg = (uint32_t)enable; |
| |
| upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_xcp_set_mipi_dsi_ena() - M33/A35 can use this API to set/clear mipi dsi ena |
| * @domain: identifier of the caller domain. |
| * soc_domain_t found in upower_soc_defs.h. |
| * @enable: true, means that set ddr retention, false clear ddr retention. |
| * @callb: NULL |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| */ |
| int upwr_xcp_set_mipi_dsi_ena(soc_domain_t domain, |
| uint32_t enable, |
| const upwr_callb callb) |
| { |
| upwr_xcp_set_mipi_dsi_ena_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SET_MIPI_DSI_ENA); |
| txmsg.hdr.domain = (uint32_t)domain; |
| txmsg.hdr.arg = (uint32_t)enable; |
| |
| upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_xcp_get_mipi_dsi_ena() - M33/A35 can use this API to get mipi dsi ena status |
| * @domain: identifier of the caller domain. |
| * soc_domain_t found in upower_soc_defs.h. |
| * @callb: NULL |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| */ |
| int upwr_xcp_get_mipi_dsi_ena(soc_domain_t domain, const upwr_callb callb) |
| { |
| upwr_xcp_get_mipi_dsi_ena_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_GET_MIPI_DSI_ENA); |
| txmsg.hdr.domain = (uint32_t)domain; |
| |
| upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_xcp_set_osc_mode() - M33/A35 can use this API to set uPower OSC mode |
| * @domain: identifier of the caller domain. |
| * soc_domain_t found in upower_soc_defs.h. |
| * @osc_mode, 0 means low frequency, not 0 means high frequency. |
| * @callb: NULL |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| */ |
| int upwr_xcp_set_osc_mode(soc_domain_t domain, |
| uint32_t osc_mode, |
| const upwr_callb callb) |
| { |
| upwr_xcp_set_osc_mode_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SET_OSC_MODE); |
| txmsg.hdr.domain = (uint32_t)domain; |
| txmsg.hdr.arg = (uint32_t)osc_mode; |
| |
| upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_xcp_set_rtd_use_ddr() - M33 call this API to inform uPower, M33 is using ddr |
| * @domain: identifier of the caller domain. |
| * soc_domain_t found in upower_soc_defs.h. |
| * @is_use_ddr: not 0, true, means that RTD is using ddr. 0, false, means that, RTD |
| * is not using ddr. |
| * @callb: NULL |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| */ |
| int upwr_xcp_set_rtd_use_ddr(soc_domain_t domain, |
| uint32_t is_use_ddr, |
| const upwr_callb callb) |
| { |
| upwr_xcp_rtd_use_ddr_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SET_RTD_USE_DDR); |
| txmsg.hdr.domain = (uint32_t)domain; |
| txmsg.hdr.arg = (uint32_t)is_use_ddr; |
| |
| upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_xcp_set_rtd_apd_llwu() - M33/A35 can use this API to set/clear rtd_llwu apd_llwu |
| * @domain: set which domain (RTD_DOMAIN, APD_DOMAIN) LLWU. |
| * soc_domain_t found in upower_soc_defs.h. |
| * @enable: true, means that set rtd_llwu or apd_llwu, false clear rtd_llwu or apd_llwu. |
| * @callb: NULL |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| */ |
| int upwr_xcp_set_rtd_apd_llwu(soc_domain_t domain, |
| uint32_t enable, |
| const upwr_callb callb) |
| { |
| upwr_xcp_rtd_apd_llwu_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SET_RTD_APD_LLWU); |
| txmsg.hdr.domain = (uint32_t)domain; |
| txmsg.hdr.arg = (uint32_t)enable; |
| |
| upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_xcp_shutdown() - Shuts down all uPower services and power mode tasks. |
| * @callb: pointer to the callback to be called when the uPower has finished |
| * the shutdown, or NULL if no callback needed |
| * (polling used instead). |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded or |
| * not. |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. |
| * |
| * At the callback the uPower/API is back to initialization/start-up phase, |
| * so service request calls return error. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| */ |
| int upwr_xcp_shutdown(const upwr_callb callb) |
| { |
| upwr_xcp_shutdown_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SHUTDOWN); |
| |
| upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| api_state = UPWR_API_SHUTDOWN_WAIT; |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_xcp_i2c_access() - Performs an access through the uPower I2C interface. |
| * @addr: I2C slave address, up to 10 bits. |
| * @data_size: determines the access direction and data size in bytes, up to 4; |
| * negetive data_size determines a read access with size -data_size; |
| * positive data_size determines a write access with size data_size; |
| * data_size=0 is invalid, making the service return error UPWR_RESP_BAD_REQ. |
| * @subaddr_size: size of the sub-address in bytes, up to 4; if subaddr_size=0, |
| * no subaddress is used. |
| * @subaddr: sub-address, only used if subaddr_size > 0. |
| * @wdata: write data, up to 4 bytes; ignored if data_size < 0 (read) |
| * @callb: pointer to the callback to be called when the uPower has finished |
| * the access, or NULL if no callback needed |
| * (polling used instead). |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded or |
| * not. |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_EXCEPT as the service group argument. |
| * |
| * The service performs a read (data_size < 0) or a write (data_size > 0) of |
| * up to 4 bytes on the uPower I2C interface. The data read from I2C comes via |
| * the callback argument ret, or written to the variable pointed by retptr, |
| * if polling is used (calls upwr_req_status or upwr_poll_req_status). |
| * ret (or *retptr) also returns the data written on writes. |
| * |
| * Sub-addressing is supported, with sub-address size determined by the argument |
| * subaddr_size, up to 4 bytes. Sub-addressing is not used if subaddr_size=0. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| */ |
| |
| int upwr_xcp_i2c_access(uint16_t addr, |
| int8_t data_size, |
| uint8_t subaddr_size, |
| uint32_t subaddr, |
| uint32_t wdata, |
| const upwr_callb callb) |
| { |
| unsigned long ptrval = (unsigned long)sh_buffer[UPWR_SG_EXCEPT]; |
| upwr_i2c_access *i2c_acc_ptr = (upwr_i2c_access *)ptrval; |
| upwr_pwm_pmiccfg_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_EXCEPT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_I2C); |
| |
| i2c_acc_ptr->addr = addr; |
| i2c_acc_ptr->subaddr = subaddr; |
| i2c_acc_ptr->subaddr_size = subaddr_size; |
| i2c_acc_ptr->data = wdata; |
| i2c_acc_ptr->data_size = data_size; |
| |
| txmsg.ptr = upwr_ptr2offset(ptrval, |
| UPWR_SG_EXCEPT, |
| (size_t)sizeof(upwr_i2c_access), |
| 0U, |
| i2c_acc_ptr); |
| |
| upwr_srv_req(UPWR_SG_EXCEPT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /**--------------------------------------------------------------- |
| * VOLTAGE MANAGERMENT SERVICE GROUP |
| */ |
| |
| /** |
| * upwr_vtm_pmic_cold_reset() -request cold reset the pmic. |
| * pmic will power cycle all the regulators |
| * @callb: response callback pointer; NULL if no callback needed. |
| * |
| * The function requests uPower to cold reset the pmic. |
| * The request is executed if arguments are within range, with no protections |
| * regarding the adequate voltage value for the given domain process, |
| * temperature and frequency. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_vtm_pmic_cold_reset(upwr_callb callb) |
| { |
| upwr_volt_pmic_cold_reset_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_VOLTM)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_VOLTM, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_VOLTM, UPWR_VTM_PMIC_COLD_RESET); |
| |
| upwr_srv_req(UPWR_SG_VOLTM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_vtm_set_pmic_mode() -request uPower set pmic mode |
| * @pmic_mode: the target mode need to be set |
| * @callb: response callback pointer; NULL if no callback needed. |
| * |
| * The function requests uPower to set pmic mode |
| * The request is executed if arguments are within range, with no protections |
| * regarding the adequate voltage value for the given domain process, |
| * temperature and frequency. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_vtm_set_pmic_mode(uint32_t pmic_mode, upwr_callb callb) |
| { |
| upwr_volt_pmic_set_mode_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_VOLTM)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_VOLTM, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_VOLTM, UPWR_VTM_SET_PMIC_MODE); |
| |
| txmsg.hdr.arg = pmic_mode; |
| |
| upwr_srv_req(UPWR_SG_VOLTM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_vtm_chng_pmic_voltage() - Changes the voltage of a given rail. |
| * @rail: pmic rail id. |
| * @volt: the target voltage of the given rail, accurate to uV |
| * If pass volt value 0, means that power off this rail. |
| * @callb: response callback pointer; NULL if no callback needed. |
| * |
| * The function requests uPower to change the voltage of the given rail. |
| * The request is executed if arguments are within range, with no protections |
| * regarding the adequate voltage value for the given domain process, |
| * temperature and frequency. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_vtm_chng_pmic_voltage(uint32_t rail, uint32_t volt, upwr_callb callb) |
| { |
| upwr_volt_pmic_set_volt_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_VOLTM)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_VOLTM, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_VOLTM, UPWR_VTM_CHNG_PMIC_RAIL_VOLT); |
| |
| txmsg.args.rail = rail; |
| |
| txmsg.args.volt = (volt + PMIC_VOLTAGE_MIN_STEP - 1U) / PMIC_VOLTAGE_MIN_STEP; |
| |
| upwr_srv_req(UPWR_SG_VOLTM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_vtm_get_pmic_voltage() - Get the voltage of a given rail. |
| * @rail: pmic rail id. |
| * @callb: response callback pointer; NULL if no callback needed. |
| * (polling used instead) |
| * |
| * The function requests uPower to get the voltage of the given rail. |
| * The request is executed if arguments are within range, with no protections |
| * regarding the adequate voltage value for the given domain process, |
| * temperature and frequency. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. |
| * |
| * The voltage data read from uPower via |
| * the callback argument ret, or written to the variable pointed by retptr, |
| * if polling is used (calls upwr_req_status or upwr_poll_req_status). |
| * ret (or *retptr) also returns the data written on writes. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_vtm_get_pmic_voltage(uint32_t rail, upwr_callb callb) |
| { |
| upwr_volt_pmic_get_volt_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_VOLTM)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_VOLTM, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_VOLTM, UPWR_VTM_GET_PMIC_RAIL_VOLT); |
| |
| txmsg.args.rail = rail; |
| |
| upwr_srv_req(UPWR_SG_VOLTM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_vtm_power_measure() - request uPower to measure power consumption |
| * @ssel: This field determines which power switches will have their currents |
| * sampled to be accounted for a |
| * current/power measurement. Support 0~7 |
| |
| * SSEL bit # Power Switch |
| * 0 M33 core complex/platform/peripherals |
| * 1 Fusion Core and Peripherals |
| * 2 A35[0] core complex |
| * 3 A35[1] core complex |
| * 4 3DGPU |
| * 5 HiFi4 |
| * 6 DDR Controller (PHY and PLL NOT included) |
| * 7 PXP, EPDC |
| * |
| * @callb: response callback pointer; NULL if no callback needed. |
| * (polling used instead) |
| * |
| * The function requests uPower to measure power consumption |
| * The request is executed if arguments are within range, with no protections |
| * regarding the adequate voltage value for the given domain process, |
| * temperature and frequency. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. |
| * |
| * The power consumption data read from uPower via |
| * the callback argument ret, or written to the variable pointed by retptr, |
| * if polling is used (calls upwr_req_status or upwr_poll_req_status). |
| * ret (or *retptr) also returns the data written on writes. |
| * upower fw needs support cocurrent request from M33 and A35. |
| * |
| * Accurate to uA |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_vtm_power_measure(uint32_t ssel, upwr_callb callb) |
| { |
| upwr_volt_pmeter_meas_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_VOLTM)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_VOLTM, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_VOLTM, UPWR_VTM_PMETER_MEAS); |
| |
| txmsg.hdr.arg = ssel; |
| |
| upwr_srv_req(UPWR_SG_VOLTM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_vtm_vmeter_measure() - request uPower to measure voltage |
| * @vdetsel: Voltage Detector Selector, support 0~3 |
| * 00b - RTD sense point |
| 01b - LDO output |
| 10b - APD domain sense point |
| 11b - AVD domain sense point |
| Refer to upower_defs.h |
| * @callb: response callback pointer; NULL if no callback needed. |
| * (polling used instead) |
| * |
| * The function requests uPower to use vmeter to measure voltage |
| * The request is executed if arguments are within range, with no protections |
| * regarding the adequate voltage value for the given domain process, |
| * temperature and frequency. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_VOLTM as the service group argument. |
| * |
| * The voltage data read from uPower via |
| * the callback argument ret, or written to the variable pointed by retptr, |
| * if polling is used (calls upwr_req_status or upwr_poll_req_status). |
| * ret (or *retptr) also returns the data written on writes. |
| * upower fw needs support cocurrent request from M33 and A35. |
| * |
| * Refer to RM COREREGVL (Core Regulator Voltage Level) |
| * uPower return VDETLVL to user, user can calculate the real voltage: |
| * |
| 0b000000(0x00) - 0.595833V |
| 0b100110(0x26) - 1.007498V |
| <value> - 0.595833V + <value>x10.8333mV |
| 0b110010(0x32) - 1.138V |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_vtm_vmeter_measure(uint32_t vdetsel, upwr_callb callb) |
| { |
| upwr_volt_vmeter_meas_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_VOLTM)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_VOLTM, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_VOLTM, UPWR_VTM_VMETER_MEAS); |
| |
| txmsg.hdr.arg = vdetsel; |
| |
| upwr_srv_req(UPWR_SG_VOLTM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_vtm_pmic_config() - Configures the SoC PMIC (Power Management IC). |
| * @config: pointer to a PMIC-dependent struct defining the PMIC configuration. |
| * @size: size of the struct pointed by config, in bytes. |
| * @callb: pointer to the callback called when configurations are applied. |
| * NULL if no callback is required. |
| * |
| * The function requests uPower to change/define the PMIC configuration. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, -1 if service group is busy, |
| * -2 if the pointer conversion to physical address failed, |
| * -3 if called in an invalid API state. |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_vtm_pmic_config(const void *config, uint32_t size, upwr_callb callb) |
| { |
| upwr_pwm_pmiccfg_msg txmsg = {0}; |
| unsigned long ptrval = 0UL; /* needed for X86, ARM64 */ |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_VOLTM)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_VOLTM, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_VOLTM, UPWR_VTM_PMIC_CONFIG); |
| |
| ptrval = (unsigned long)os_ptr2phy(config); |
| if (ptrval == 0UL) { |
| return -2; /* pointer conversion failed */ |
| } |
| |
| txmsg.ptr = upwr_ptr2offset(ptrval, |
| UPWR_SG_VOLTM, |
| (size_t)size, |
| 0U, |
| config); |
| |
| upwr_srv_req(UPWR_SG_VOLTM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /**--------------------------------------------------------------- |
| * TEMPERATURE MANAGEMENT SERVICE GROUP |
| */ |
| |
| /** |
| * upwr_tpm_get_temperature() - request uPower to get temperature of one temperature sensor |
| * @sensor_id: temperature sensor ID, support 0~2 |
| * @callb: response callback pointer; NULL if no callback needed. |
| * (polling used instead) |
| * |
| * The function requests uPower to measure temperature |
| * The request is executed if arguments are within range, with no protections |
| * regarding the adequate voltage value for the given domain process, |
| * temperature and frequency. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_TEMPM as the service group argument. |
| * |
| * The temperature data read from uPower via |
| * the callback argument ret, or written to the variable pointed by retptr, |
| * if polling is used (calls upwr_req_status or upwr_poll_req_status). |
| * ret (or *retptr) also returns the data written on writes. |
| * |
| * uPower return TSEL to the caller (M33 or A35), caller calculate the real temperature |
| * Tsh = 0.000002673049*TSEL[7:0]^3 + 0.0003734262*TSEL[7:0]^2 + |
| 0.4487042*TSEL[7:0] - 46.98694 |
| * |
| * upower fw needs support cocurrent request from M33 and A35. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_tpm_get_temperature(uint32_t sensor_id, upwr_callb callb) |
| { |
| upwr_temp_get_cur_temp_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_TEMPM)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_TEMPM, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_TEMPM, UPWR_TEMP_GET_CUR_TEMP); |
| |
| txmsg.args.sensor_id = sensor_id; |
| |
| upwr_srv_req(UPWR_SG_TEMPM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /**--------------------------------------------------------------- |
| * DELAY MANAGEMENT SERVICE GROUP |
| */ |
| |
| /** |
| * upwr_dlm_get_delay_margin() - request uPower to get delay margin |
| * @path: The critical path |
| * @index: Use whitch delay meter |
| * @callb: response callback pointer; NULL if no callback needed. |
| * (polling used instead) |
| * |
| * The function requests uPower to get delay margin |
| * The request is executed if arguments are within range, with no protections |
| * regarding the adequate voltage value for the given domain process, |
| * temperature and frequency. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_DELAYM as the service group argument. |
| * |
| * The delay margin data read from uPower via |
| * the callback argument ret, or written to the variable pointed by retptr, |
| * if polling is used (calls upwr_req_status or upwr_poll_req_status). |
| * ret (or *retptr) also returns the data written on writes. |
| * upower fw needs support cocurrent request from M33 and A35. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_dlm_get_delay_margin(uint32_t path, uint32_t index, upwr_callb callb) |
| { |
| upwr_dmeter_get_delay_margin_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_DELAYM)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_DELAYM, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_DELAYM, UPWR_DMETER_GET_DELAY_MARGIN); |
| |
| txmsg.args.path = path; |
| txmsg.args.index = index; |
| |
| upwr_srv_req(UPWR_SG_DELAYM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_dlm_set_delay_margin() - request uPower to set delay margin |
| * @path: The critical path |
| * @index: Use whitch delay meter |
| * @delay_margin: the value of delay margin |
| * @callb: response callback pointer; NULL if no callback needed. |
| * (polling used instead) |
| * |
| * The function requests uPower to set delay margin |
| * The request is executed if arguments are within range, with no protections |
| * regarding the adequate voltage value for the given domain process, |
| * temperature and frequency. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_DELAYM as the service group argument. |
| * |
| * The result of the corresponding critical path, failed or not read from uPower via |
| * the callback argument ret, or written to the variable pointed by retptr, |
| * if polling is used (calls upwr_req_status or upwr_poll_req_status). |
| * ret (or *retptr) also returns the data written on writes. |
| * upower fw needs support cocurrent request from M33 and A35. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_dlm_set_delay_margin(uint32_t path, uint32_t index, uint32_t delay_margin, |
| upwr_callb callb) |
| { |
| upwr_dmeter_set_delay_margin_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_DELAYM)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_DELAYM, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_DELAYM, UPWR_DMETER_SET_DELAY_MARGIN); |
| |
| txmsg.args.path = path; |
| txmsg.args.index = index; |
| txmsg.args.dm = delay_margin; |
| |
| upwr_srv_req(UPWR_SG_DELAYM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_dlm_process_monitor() - request uPower to do process monitor |
| * @chain_sel: Chain Cell Type Selection |
| * Select the chain to be used for the clock signal generation. |
| * Support two types chain cell, 0~1 |
| 0b - P4 type delay cells selected |
| 1b - P16 type delay cells selected |
| * @callb: response callback pointer; NULL if no callback needed. |
| * (polling used instead) |
| * |
| * The function requests uPower to do process monitor |
| * The request is executed if arguments are within range, with no protections |
| * regarding the adequate voltage value for the given domain process, |
| * temperature and frequency. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_DELAYM as the service group argument. |
| * |
| * The result of process monitor, failed or not read from uPower via |
| * the callback argument ret, or written to the variable pointed by retptr, |
| * if polling is used (calls upwr_req_status or upwr_poll_req_status). |
| * ret (or *retptr) also returns the data written on writes. |
| * upower fw needs support cocurrent request from M33 and A35. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_dlm_process_monitor(uint32_t chain_sel, upwr_callb callb) |
| { |
| upwr_pmon_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_DELAYM)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_DELAYM, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_DELAYM, UPWR_PMON_REQ); |
| |
| txmsg.args.chain_sel = chain_sel; |
| |
| upwr_srv_req(UPWR_SG_DELAYM, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /**--------------------------------------------------------------- |
| * POWER MANAGEMENT SERVICE GROUP |
| */ |
| |
| /** |
| * upwr_pwm_dom_power_on() - Commands uPower to power on the platform of other |
| * domain (not necessarily its core(s)); does not release the core reset. |
| * @domain: identifier of the domain to power on. Defined by SoC-dependent type |
| * soc_domain_t found in upower_soc_defs.h. |
| * @boot_start: must be 1 to start the domain core(s) boot(s), releasing |
| * its (their) resets, or 0 otherwise. |
| * @pwroncallb: pointer to the callback to be called when the uPower has |
| * finished the power on procedure, or NULL if no callback needed |
| * (polling used instead). |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded or |
| * not. |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -2 if the domain passed is the same as the caller, |
| * -3 if called in an invalid API state |
| */ |
| int upwr_pwm_dom_power_on(soc_domain_t domain, |
| int boot_start, |
| const upwr_callb pwroncallb) |
| { |
| upwr_pwm_dom_pwron_msg txmsg = {0}; |
| |
| if (pwr_domain == domain) { |
| return -2; |
| } |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_PWRMGMT, (upwr_callb)pwroncallb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_DOM_PWRON); |
| txmsg.hdr.domain = (uint32_t)domain; |
| txmsg.hdr.arg = (uint32_t)boot_start; |
| |
| upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_pwm_boot_start() - Commands uPower to release the reset of other CPU(s), |
| * starting their boots. |
| * @domain: identifier of the domain to release the reset. Defined by |
| * SoC-dependent type soc_domain_t found in upower_soc_defs.h. |
| * @bootcallb: pointer to the callback to be called when the uPower has finished |
| * the boot start procedure, or NULL if no callback needed |
| * (polling used instead). |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded or |
| * not. |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. |
| * |
| * The callback calling doesn't mean the CPUs boots have finished: |
| * it only indicates that uPower released the CPUs resets, and can receive |
| * other power management service group requests. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -2 if the domain passed is the same as the caller, |
| * -3 if called in an invalid API state |
| */ |
| int upwr_pwm_boot_start(soc_domain_t domain, const upwr_callb bootcallb) |
| { |
| upwr_pwm_boot_start_msg txmsg = {0}; |
| |
| if (pwr_domain == domain) { |
| return -2; |
| } |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_PWRMGMT, (upwr_callb)bootcallb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_BOOT); |
| txmsg.hdr.domain = (uint32_t)domain; |
| |
| upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_pwm_param() - Changes Power Management parameters. |
| * @param: pointer to a parameter structure upwr_pwm_param_t, SoC-dependent, |
| * defined in upwr_soc_defines.h. NULL may be passed, meaning |
| * a request to read the parameter set, in which case it appears in the callback |
| * argument ret, or can be pointed by argument retptr in the upwr_req_status and |
| * upwr_poll_req_status calls, casted to upwr_pwm_param_t. |
| * @callb: response callback pointer; NULL if no callback needed. |
| * |
| * The return value is always the current parameter set value, either in a |
| * read-only request (param = NULL) or after setting a new parameter |
| * (non-NULL param). |
| * |
| * Some parameters may be targeted for a specific domain (see the struct |
| * upwr_pwm_param_t definition in upower_soc_defs.h); this call has implicit |
| * domain target (the same domain from which is called). |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded or |
| * not. |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| */ |
| int upwr_pwm_param(upwr_pwm_param_t *param, const upwr_callb callb) |
| { |
| upwr_pwm_param_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_PARAM); |
| |
| if (param == NULL) { |
| txmsg.hdr.arg = 1U; /* 1= read, txmsg.word2 ignored */ |
| } else { |
| txmsg.hdr.arg = 0U; /* 1= write */ |
| txmsg.word2 = param->R; /* just 1 word, so that's ok */ |
| } |
| |
| upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_pwm_chng_reg_voltage() - Changes the voltage at a given regulator. |
| * @reg: regulator id. |
| * @volt: voltage value; value unit is SoC-dependent, converted from mV by the |
| * macro UPWR_VTM_MILIV, or from micro-Volts by the macro UPWR_VTM_MICROV, |
| * both macros in upower_soc_defs.h |
| * @callb: response callback pointer; NULL if no callback needed. |
| * |
| * The function requests uPower to change the voltage of the given regulator. |
| * The request is executed if arguments are within range, with no protections |
| * regarding the adequate voltage value for the given domain process, |
| * temperature and frequency. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_pwm_chng_reg_voltage(uint32_t reg, uint32_t volt, upwr_callb callb) |
| { |
| upwr_pwm_volt_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_VOLT); |
| |
| txmsg.args.reg = reg; |
| txmsg.args.volt = volt; |
| |
| upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_pwm_freq_setup() - Determines the next frequency target for a given |
| * domain and current frequency. |
| * @domain: identifier of the domain to change frequency. Defined by |
| * SoC-dependent type soc_domain_t found in upower_soc_defs.h. |
| * @rail: the pmic regulator number for the target domain. |
| * @stage: DVA adjust stage |
| * refer to upower_defs.h "DVA adjust stage" |
| * @target_freq: the target adjust frequency, accurate to MHz |
| * |
| * refer to upower_defs.h structure definition upwr_pwm_freq_msg |
| * |
| * @callb: response callback pointer; NULL if no callback needed. |
| * |
| * The DVA algorithm is broken down into two phases. |
| * The first phase uses a look up table to get a safe operating voltage |
| * for the requested frequency. |
| * This voltage is guaranteed to work over process and temperature. |
| * |
| * The second step of the second phase is to measure the temperature |
| * using the uPower Temperature Sensor module. |
| * This is accomplished by doing a binary search of the TSEL bit field |
| * in the Temperature Measurement Register (TMR). |
| * The search is repeated until the THIGH bit fields in the same register change value. |
| * There are 3 temperature sensors in 8ULP (APD, AVD, and RTD). |
| * |
| * |
| * The second phase is the fine adjust of the voltage. |
| * This stage is entered only when the new frequency requested |
| * by application was already set as well as the voltage for that frequency. |
| * The first step of the fine adjust is to find what is the current margins |
| * for the monitored critical paths, or, in other words, |
| * how many delay cells will be necessary to generate a setup-timing violation. |
| * The function informs uPower that the given domain frequency has changed or |
| * will change to the given value. uPower firmware will then adjust voltage and |
| * bias to cope with the new frequency (if decreasing) or prepare for it |
| * (if increasing). The function must be called after decreasing the frequency, |
| * and before increasing it. The actual increase in frequency must not occur |
| * before the service returns its response. |
| * |
| * So, for increase clock frequency case, user need to call this API twice, |
| * the first stage gross adjust and the second stage fine adjust. |
| * |
| * for reduce clock frequency case, user can only call this API once, |
| * full stage (combine gross stage and fine adjust) |
| * |
| * The request is executed if arguments are within range. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_pwm_freq_setup(soc_domain_t domain, uint32_t rail, uint32_t stage, uint32_t target_freq, |
| upwr_callb callb) |
| { |
| upwr_pwm_freq_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_FREQ); |
| |
| txmsg.hdr.domain = (uint32_t)domain; |
| txmsg.args.rail = rail; |
| txmsg.args.stage = stage; |
| txmsg.args.target_freq = target_freq; |
| |
| upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_pwm_power_on()- Powers on (not off) one or more switches and ROM/RAMs. |
| * @swton: pointer to an array of words that tells which power switches to |
| * turn on. Each word in the array has 1 bit for each switch. |
| * A bit=1 means the respective switch must be turned on, |
| * bit = 0 means it will stay unchanged (on or off). |
| * The pointer may be set to NULL, in which case no switch will be changed, |
| * unless a memory that it feeds must be turned on. |
| * WARNING: swton must not point to the first shared memory address. |
| * @memon: pointer to an array of words that tells which memories to turn on. |
| * Each word in the array has 1 bit for each switch. |
| * A bit=1 means the respective memory must be turned on, both array and |
| * periphery logic; |
| * bit = 0 means it will stay unchanged (on or off). |
| * The pointer may be set to NULL, in which case no memory will be changed. |
| * WARNING: memon must not point to the first shared memory address. |
| * @callb: pointer to the callback called when configurations are applyed. |
| * NULL if no callback is required. |
| * |
| * The function requests uPower to turn on the PMC and memory array/peripheral |
| * switches that control their power, as specified above. |
| * The request is executed if arguments are within range, with no protections |
| * regarding the adequate memory power state related to overall system state. |
| * |
| * If a memory is requested to turn on, but the power switch that feeds that |
| * memory is not, the power switch will be turned on anyway, if the pwron |
| * array is not provided (that is, if pwron is NULL). |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. |
| * |
| * Callback or polling may return error if the service contends for a resource |
| * already being used by a power mode transition or an ongoing service in |
| * another domain. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, -1 if service group is busy, |
| * -2 if a pointer conversion to physical address failed, |
| * -3 if called in an invalid API state. |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| |
| int upwr_pwm_power_on(const uint32_t swton[], |
| const uint32_t memon[], |
| upwr_callb callb) |
| { |
| upwr_pwm_pwron_msg txmsg = {0}; |
| unsigned long ptrval = 0UL; /* needed for X86, ARM64 */ |
| size_t stsize = 0U; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_PWR_ON); |
| |
| ptrval = (unsigned long)os_ptr2phy((void *)swton); |
| if (swton == NULL) { |
| txmsg.ptrs.ptr0 = 0; /* NULL pointer -> 0 offset */ |
| } else if (ptrval == 0U) { |
| return -2; /* pointer conversion failed */ |
| } else { |
| txmsg.ptrs.ptr0 = upwr_ptr2offset(ptrval, |
| UPWR_SG_PWRMGMT, |
| (stsize = UPWR_PMC_SWT_WORDS * 4U), |
| 0U, |
| swton); |
| } |
| |
| ptrval = (unsigned long)os_ptr2phy((void *)memon); |
| if (memon == NULL) { |
| txmsg.ptrs.ptr1 = 0; /* NULL pointer -> 0 offset */ |
| |
| } else if (ptrval == 0U) { |
| return -2; /* pointer conversion failed */ |
| } else { |
| txmsg.ptrs.ptr1 = upwr_ptr2offset(ptrval, |
| UPWR_SG_PWRMGMT, |
| UPWR_PMC_MEM_WORDS * 4U, |
| stsize, |
| memon); |
| } |
| |
| upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_pwm_power_off()- Powers off (not on) one or more switches and ROM/RAMs. |
| * @swtoff: pointer to an array of words that tells which power switches to |
| * turn off. Each word in the array has 1 bit for each switch. |
| * A bit=1 means the respective switch must be turned off, |
| * bit = 0 means it will stay unchanged (on or off). |
| * The pointer may be set to NULL, in which case no switch will be changed. |
| * WARNING: swtoff must not point to the first shared memory address. |
| * @memoff: pointer to an array of words that tells which memories to turn off. |
| * Each word in the array has 1 bit for each switch. |
| * A bit=1 means the respective memory must be turned off, both array and |
| * periphery logic; |
| * bit = 0 means it will stay unchanged (on or off). |
| * The pointer may be set to NULL, in which case no memory will be changed, |
| * but notice it may be turned off if the switch that feeds it is powered off. |
| * WARNING: memoff must not point to the first shared memory address. |
| * @callb: pointer to the callback called when configurations are applyed. |
| * NULL if no callback is required. |
| * |
| * The function requests uPower to turn off the PMC and memory array/peripheral |
| * switches that control their power, as specified above. |
| * The request is executed if arguments are within range, with no protections |
| * regarding the adequate memory power state related to overall system state. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. |
| * |
| * Callback or polling may return error if the service contends for a resource |
| * already being used by a power mode transition or an ongoing service in |
| * another domain. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, -1 if service group is busy, |
| * -2 if a pointer conversion to physical address failed, |
| * -3 if called in an invalid API state. |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_pwm_power_off(const uint32_t swtoff[], |
| const uint32_t memoff[], |
| upwr_callb callb) |
| { |
| upwr_pwm_pwroff_msg txmsg = {0}; |
| unsigned long ptrval = 0UL; /* needed for X86, ARM64 */ |
| size_t stsize = 0; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_PWR_OFF); |
| |
| ptrval = (unsigned long)os_ptr2phy((void *)swtoff); |
| if (swtoff == NULL) { |
| txmsg.ptrs.ptr0 = 0; /* NULL pointer -> 0 offset */ |
| } else if (ptrval == 0U) { |
| return -2; /* pointer conversion failed */ |
| } else { |
| txmsg.ptrs.ptr0 = upwr_ptr2offset(ptrval, |
| UPWR_SG_PWRMGMT, |
| (stsize = UPWR_PMC_SWT_WORDS * 4U), |
| 0U, |
| swtoff); |
| } |
| |
| ptrval = (unsigned long)os_ptr2phy((void *)memoff); |
| if (memoff == NULL) { |
| txmsg.ptrs.ptr1 = 0; /* NULL pointer -> 0 offset */ |
| } else if (ptrval == 0U) { |
| return -2; /* pointer conversion failed */ |
| } else { |
| txmsg.ptrs.ptr1 = upwr_ptr2offset(ptrval, |
| UPWR_SG_PWRMGMT, |
| UPWR_PMC_MEM_WORDS * 4U, |
| stsize, |
| memoff); |
| } |
| |
| upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_pwm_mem_retain()- Configures one or more memory power switches to |
| * retain its contents, having the power array on, while its peripheral logic |
| * is turned off. |
| * @mem: pointer to an array of words that tells which memories to put in a |
| * retention state. Each word in the array has 1 bit for each memory. |
| * A bit=1 means the respective memory must be put in retention state, |
| * bit = 0 means it will stay unchanged (retention, fully on or off). |
| * @callb: pointer to the callback called when configurations are applyed. |
| * NULL if no callback is required. |
| * |
| * The function requests uPower to turn off the memory peripheral and leave |
| * its array on, as specified above. |
| * The request is executed if arguments are within range. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. |
| * |
| * Callback or polling may return error if the service contends for a resource |
| * already being used by a power mode transition or an ongoing service in |
| * another domain. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, -1 if service group is busy, |
| * -2 if a pointer conversion to physical address failed, |
| * -3 if called in an invalid API state. |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_pwm_mem_retain(const uint32_t mem[], upwr_callb callb) |
| { |
| upwr_pwm_retain_msg txmsg = {0}; |
| unsigned long ptrval = 0UL; /* needed for X86, ARM64 */ |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_RETAIN); |
| |
| ptrval = (unsigned long)os_ptr2phy((void *)mem); |
| if (ptrval == 0U) { |
| return -2; /* pointer conversion failed */ |
| } |
| |
| txmsg.ptr = upwr_ptr2offset(ptrval, |
| UPWR_SG_PWRMGMT, |
| UPWR_PMC_MEM_WORDS * 4U, |
| 0U, |
| mem); |
| |
| upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_pwm_chng_switch_mem() - Turns on/off power on one or more PMC switches |
| * and memories, including their array and peripheral logic. |
| * @swt: pointer to a list of PMC switches to be opened/closed. |
| * The list is structured as an array of struct upwr_switch_board_t |
| * (see upower_defs.h), each one containing a word for up to 32 switches, |
| * one per bit. A bit = 1 means switch closed, bit = 0 means switch open. |
| * struct upwr_switch_board_t also specifies a mask with 1 bit for each |
| * respective switch: mask bit = 1 means the open/close action is applied, |
| * mask bit = 0 means the switch stays unchanged. |
| * The pointer may be set to NULL, in which case no switch will be changed, |
| * unless a memory that it feeds must be turned on. |
| * WARNING: swt must not point to the first shared memory address. |
| * @mem: pointer to a list of switches to be turned on/off. |
| * The list is structured as an array of struct upwr_mem_switches_t |
| * (see upower_defs.h), each one containing 2 word for up to 32 switches, |
| * one per bit, one word for the RAM array power switch, other for the |
| * RAM peripheral logic power switch. A bit = 1 means switch closed, |
| * bit = 0 means switch open. |
| * struct upwr_mem_switches_t also specifies a mask with 1 bit for each |
| * respective switch: mask bit = 1 means the open/close action is applied, |
| * mask bit = 0 means the switch stays unchanged. |
| * The pointer may be set to NULL, in which case no memory switch will be |
| * changed, but notice it may be turned off if the switch that feeds it is |
| * powered off. |
| * WARNING: mem must not point to the first shared memory address. |
| * @callb: pointer to the callback called when the configurations are applied. |
| * NULL if no callback is required. |
| * |
| * The function requests uPower to change the PMC switches and/or memory power |
| * as specified above. |
| * The request is executed if arguments are within range, with no protections |
| * regarding the adequate switch combinations and overall system state. |
| * |
| * If a memory is requested to turn on, but the power switch that feeds that |
| * memory is not, the power switch will be turned on anyway, if the swt |
| * array is not provided (that is, if swt is NULL). |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. |
| * |
| * Callback or polling may return error if the service contends for a resource |
| * already being used by a power mode transition or an ongoing service in |
| * another domain. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, -1 if service group is busy. |
| * -2 if a pointer conversion to physical address failed, |
| * -3 if called in an invalid API state. |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| |
| int upwr_pwm_chng_switch_mem(const struct upwr_switch_board_t swt[], |
| const struct upwr_mem_switches_t mem[], |
| upwr_callb callb) |
| { |
| upwr_pwm_switch_msg txmsg = {0}; |
| unsigned long ptrval = 0UL; /* needed for X86, ARM64 */ |
| size_t stsize = 0U; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_SWITCH); |
| |
| ptrval = (unsigned long)os_ptr2phy((void *)swt); |
| if (swt == NULL) { |
| txmsg.ptrs.ptr0 = 0; /* NULL pointer -> 0 offset */ |
| } else if (ptrval == 0U) { |
| return -2; /* pointer conversion failed */ |
| } else { |
| txmsg.ptrs.ptr0 = upwr_ptr2offset(ptrval, |
| UPWR_SG_PWRMGMT, |
| (stsize = UPWR_PMC_SWT_WORDS * sizeof(struct upwr_switch_board_t)), |
| 0U, |
| swt); |
| } |
| |
| ptrval = (unsigned long)os_ptr2phy((void *)mem); |
| if (mem == NULL) { |
| txmsg.ptrs.ptr1 = 0; /* NULL pointer -> 0 offset */ |
| } else if (ptrval == 0U) { |
| return -2; /* pointer conversion failed */ |
| } else { |
| txmsg.ptrs.ptr1 = upwr_ptr2offset(ptrval, |
| UPWR_SG_PWRMGMT, |
| UPWR_PMC_MEM_WORDS * sizeof(struct upwr_mem_switches_t), |
| stsize, |
| mem); |
| } |
| |
| upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_pwm_pmode_config() - Configures a given power mode in a given domain. |
| * @domain: identifier of the domain to which the power mode belongs. |
| * Defined by SoC-dependent type soc_domain_t found in upower_soc_defs.h. |
| * @pmode: SoC-dependent power mode identifier defined by type abs_pwr_mode_t |
| * found in upower_soc_defs.h. |
| * @config: pointer to an SoC-dependent struct defining the power mode |
| * configuration, found in upower_soc_defs.h. |
| * @callb: pointer to the callback called when configurations are applied. |
| * NULL if no callback is required. |
| * |
| * The function requests uPower to change the power mode configuration as |
| * specified above. The request is executed if arguments are within range, |
| * and complies with SoC-dependent restrictions on value combinations. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, -1 if service group is busy, |
| * -2 if the pointer conversion to physical address failed, |
| * -3 if called in an invalid API state. |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_pwm_pmode_config(soc_domain_t domain, |
| abs_pwr_mode_t pmode, |
| const void *config, |
| upwr_callb callb) |
| { |
| upwr_pwm_pmode_cfg_msg txmsg = {0}; |
| unsigned long ptrval = 0UL; /* needed for X86, ARM64 */ |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_CONFIG); |
| txmsg.hdr.domain = (uint32_t)domain; |
| txmsg.hdr.arg = pmode; |
| |
| ptrval = (unsigned long)os_ptr2phy(config); |
| if (ptrval == 0U) { |
| return -2; /* pointer conversion failed */ |
| } |
| |
| /* |
| * upwr_pwm_pmode_config is an exception: use the pointer |
| * (physical addr) as is |
| */ |
| |
| txmsg.ptr = (uint32_t)ptrval; |
| |
| upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_pwm_reg_config() - Configures the uPower internal regulators. |
| * @config: pointer to the struct defining the regulator configuration; |
| * the struct upwr_reg_config_t is defined in the file upower_defs.h. |
| * @callb: pointer to the callback called when configurations are applied. |
| * NULL if no callback is required. |
| * |
| * The function requests uPower to change/define the configurations of the |
| * internal regulators. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. |
| * |
| * The service may fail with error UPWR_RESP_RESOURCE if a power mode transition |
| * or the same service (called from another domain) is executing simultaneously. |
| * This error should be interpreted as a "try later" response, as the service |
| * will succeed once those concurrent executions are done, and no other is |
| * started. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, -1 if service group is busy, |
| * -2 if the pointer conversion to physical address failed, |
| * -3 if called in an invalid API state. |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| |
| int upwr_pwm_reg_config(const struct upwr_reg_config_t *config, |
| upwr_callb callb) |
| { |
| upwr_pwm_regcfg_msg txmsg = {0}; |
| unsigned long ptrval = 0UL; /* needed for X86, ARM64 */ |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_REGCFG); |
| |
| ptrval = (unsigned long)os_ptr2phy(config); |
| if (ptrval == 0U) { |
| return -2; /* pointer conversion failed */ |
| } |
| |
| txmsg.ptr = upwr_ptr2offset(ptrval, |
| UPWR_SG_PWRMGMT, |
| sizeof(struct upwr_reg_config_t), |
| 0U, |
| config); |
| |
| upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_pwm_chng_dom_bias() - Changes the domain bias. |
| * @bias: pointer to a domain bias configuration struct (see upower_soc_defs.h). |
| * @callb: pointer to the callback called when configurations are applied. |
| * NULL if no callback is required. |
| * |
| * The function requests uPower to change the domain bias configuration as |
| * specified above. The request is executed if arguments are within range, |
| * with no protections regarding the adequate value combinations and |
| * overall system state. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, -1 if service group is busy, |
| * -3 if called in an invalid API state. |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_pwm_chng_dom_bias(const struct upwr_dom_bias_cfg_t *bias, |
| upwr_callb callb) |
| { |
| upwr_pwm_dom_bias_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_DOM_BIAS); |
| |
| /* SoC-dependent argument filling, defined in upower_soc_defs.h */ |
| UPWR_FILL_DOMBIAS_ARGS(txmsg.hdr.domain, bias, txmsg.args); |
| |
| upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_pwm_chng_mem_bias()- Changes a ROM/RAM power bias. |
| * @domain: identifier of the domain upon which the bias is applied. |
| * Defined by SoC-dependent type soc_domain_t found in upower_soc_defs.h. |
| * @bias: pointer to a memory bias configuration struct (see upower_soc_defs.h). |
| * @callb: pointer to the callback called when configurations are applied. |
| * NULL if no callback is required. |
| * |
| * The function requests uPower to change the memory bias configuration as |
| * specified above. The request is executed if arguments are within range, |
| * with no protections regarding the adequate value combinations and |
| * overall system state. |
| * |
| * A callback can be optionally registered, and will be called upon the arrival |
| * of the request response from the uPower firmware, telling if it succeeded |
| * or not. |
| * |
| * A callback may not be registered (NULL pointer), in which case polling has |
| * to be used to check the response, by calling upwr_req_status or |
| * upwr_poll_req_status, using UPWR_SG_PWRMGMT as the service group argument. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, -1 if service group is busy, |
| * -3 if called in an invalid API state. |
| * Note that this is not the error response from the request itself: |
| * it only tells if the request was successfully sent to the uPower. |
| */ |
| int upwr_pwm_chng_mem_bias(soc_domain_t domain, |
| const struct upwr_mem_bias_cfg_t *bias, |
| upwr_callb callb) |
| { |
| upwr_pwm_mem_bias_msg txmsg = {0}; |
| |
| if (api_state != UPWR_API_READY) { |
| return -3; |
| } |
| |
| if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_MEM_BIAS); |
| |
| txmsg.hdr.domain = (uint32_t)domain; |
| |
| /* SoC-dependent argument filling, defined in upower_soc_defs.h */ |
| UPWR_FILL_MEMBIAS_ARGS(bias, txmsg.args); |
| |
| upwr_srv_req(UPWR_SG_PWRMGMT, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /**--------------------------------------------------------------- |
| * DIAGNOSE SERVICE GROUP |
| */ |
| |
| /** |
| * upwr_dgn_mode() - Sets the diagnostic mode. |
| * @mode: diagnostic mode, which can be: |
| * - UPWR_DGN_NONE: no diagnostic recorded |
| * - UPWR_DGN_TRACE: warnings, errors, service, internal activity recorded |
| * - UPWR_DGN_SRVREQ: warnings, errors, service activity recorded |
| * - UPWR_DGN_WARN: warnings and errors recorded |
| * - UPWR_DGN_ALL: trace, service, warnings, errors, task state recorded |
| * - UPWR_DGN_ERROR: only errors recorded |
| * - UPWR_DGN_ALL2ERR: record all until an error occurs, |
| * freeze recording on error |
| * - UPWR_DGN_ALL2HLT: record all until an error occurs, |
| * executes an ebreak on error, which halts the core if enabled through |
| * the debug interface |
| * @callb: pointer to the callback called when mode is changed. |
| * NULL if no callback is required. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok, |
| * -1 if service group is busy, |
| * -3 if called in an invalid API state |
| */ |
| int upwr_dgn_mode(upwr_dgn_mode_t mode, const upwr_callb callb) |
| { |
| upwr_dgn_mode_msg txmsg = {0}; |
| |
| if (UPWR_SG_BUSY(UPWR_SG_DIAG)) { |
| return -1; |
| } |
| |
| UPWR_USR_CALLB(UPWR_SG_DIAG, callb); |
| |
| UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_DIAG, UPWR_DGN_MODE); |
| |
| txmsg.hdr.arg = mode; |
| |
| upwr_srv_req(UPWR_SG_DIAG, (uint32_t *)&txmsg, sizeof(txmsg) / 4U); |
| |
| return 0; |
| } |
| |
| /**--------------------------------------------------------------- |
| * AUXILIARY CALLS |
| */ |
| |
| /** |
| * upwr_rom_version() - informs the ROM firwmware version. |
| * @vmajor: pointer to the variable to get the firmware major version number. |
| * @vminor: pointer to the variable to get the firmware minor version number. |
| * @vfixes: pointer to the variable to get the firmware fixes number. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: SoC id. |
| */ |
| uint32_t upwr_rom_version(uint32_t *vmajor, uint32_t *vminor, uint32_t *vfixes) |
| { |
| uint32_t soc; |
| |
| upwr_lock(1); |
| soc = fw_rom_version.soc_id; |
| *vmajor = fw_rom_version.vmajor; |
| *vminor = fw_rom_version.vminor; |
| *vfixes = fw_rom_version.vfixes; |
| upwr_lock(0); |
| return soc; |
| } |
| |
| /** |
| * upwr_ram_version() - informs the RAM firwmware version. |
| * @vminor: pointer to the variable to get the firmware minor version number. |
| * @vfixes: pointer to the variable to get the firmware fixes number. |
| * |
| * The 3 values returned are 0 if no RAM firmwmare was loaded and initialized. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: firmware major version number. |
| */ |
| uint32_t upwr_ram_version(uint32_t *vminor, uint32_t *vfixes) |
| { |
| uint32_t vmajor; |
| |
| upwr_lock(1); |
| vmajor = fw_ram_version.vmajor; |
| *vminor = fw_ram_version.vminor; |
| *vfixes = fw_ram_version.vfixes; |
| upwr_lock(0); |
| |
| return vmajor; |
| } |
| |
| /** |
| * upwr_req_status() - tells the status of the service group request, and |
| * returns a request return value, if any. |
| * @sg: service group of the request |
| * @sgfptr: pointer to the variable that will hold the function id of |
| * the last request completed; can be NULL, in which case it is not used. |
| * @errptr: pointer to the variable that will hold the error code; |
| * can be NULL, in which case it is not used. |
| * @retptr: pointer to the variable that will hold the value returned |
| * by the last request completed (invalid if the last request completed didn't |
| * return any value); can be NULL, in which case it is not used. |
| * Note that a request may return a value even if service error is returned |
| * (*errptr != UPWR_RESP_OK): that is dependent on the specific service. |
| * |
| * This call can be used in a poll loop of a service request completion in case |
| * a callback was not registered. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: service request status: succeeded, failed, or ongoing (busy) |
| */ |
| upwr_req_status_t upwr_req_status(upwr_sg_t sg, |
| uint32_t *sgfptr, |
| upwr_resp_t *errptr, |
| int *retptr) |
| { |
| upwr_req_status_t status; |
| |
| upwr_lock(1); |
| if (sgfptr != NULL) { |
| *sgfptr = (uint32_t)sg_rsp_msg[sg].hdr.function; |
| } |
| |
| if (errptr != NULL) { |
| *errptr = (upwr_resp_t)sg_rsp_msg[sg].hdr.errcode; |
| } |
| |
| if (retptr != NULL) { |
| *retptr = (int)((sg_rsp_siz[sg] == 2U) ? |
| sg_rsp_msg[sg].word2 : sg_rsp_msg[sg].hdr.ret); |
| } |
| |
| status = ((sg_busy & (1UL << sg)) == 1U) ? UPWR_REQ_BUSY : |
| (sg_rsp_msg[sg].hdr.errcode == UPWR_RESP_OK) ? UPWR_REQ_OK : |
| UPWR_REQ_ERR; |
| upwr_lock(0); |
| return status; |
| } |
| |
| /** |
| * upwr_poll_req_status() - polls the status of the service group request, and |
| * returns a request return value, if any. |
| * @sg: service group of the request |
| * @sgfptr: pointer to the variable that will hold the function id of |
| * the last request completed; can be NULL, in which case it is not used. |
| * @errptr: pointer to the variable that will hold the error code; |
| * can be NULL, in which case it is not used. |
| * @retptr: pointer to the variable that will hold the value returned |
| * by the last request completed (invalid if the last request completed didn't |
| * return any value); can be NULL, in which case it is not used. |
| * Note that a request may return a value even if service error is returned |
| * (*errptr != UPWR_RESP_OK): that is dependent on the specific service. |
| * @attempts: maximum number of polling attempts; if attempts > 0 and is |
| * reached with no service response received, upwr_poll_req_status returns |
| * UPWR_REQ_BUSY and variables pointed by sgfptr, retptr and errptr are not |
| * updated; if attempts = 0, upwr_poll_req_status waits "forever". |
| * |
| * This call can be used to poll a service request completion in case a |
| * callback was not registered. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: service request status: succeeded, failed, or ongoing (busy) |
| */ |
| upwr_req_status_t upwr_poll_req_status(upwr_sg_t sg, |
| uint32_t *sgfptr, |
| upwr_resp_t *errptr, |
| int *retptr, |
| uint32_t attempts) |
| { |
| uint32_t i; |
| upwr_req_status_t ret; |
| |
| if (attempts == 0U) { |
| while ((ret = upwr_req_status(sg, sgfptr, errptr, retptr)) == UPWR_REQ_BUSY) { |
| }; |
| |
| return ret; |
| } |
| |
| for (i = 0U; i < attempts; i++) { |
| ret = upwr_req_status(sg, sgfptr, errptr, retptr); |
| if (ret != UPWR_REQ_BUSY) { |
| break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * upwr_alarm_code() - returns the alarm code of the last alarm occurrence. |
| * |
| * The value returned is not meaningful if no alarm was issued by uPower. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: alarm code, as defined by the type upwr_alarm_t in upwr_soc_defines.h |
| */ |
| upwr_alarm_t upwr_alarm_code(void) |
| { |
| return (upwr_alarm_t)(3U & (mu->FSR.R >> 1U)); /* FSR[2:1] */ |
| } |
| |
| /**--------------------------------------------------------------- |
| * TRANSMIT/RECEIVE PRIMITIVES |
| * --------------------------------------------------------------- |
| */ |
| |
| /* |
| * upwr_copy2tr() - copies a message to the MU TR registers; |
| * fill the TR registers before writing TIEN to avoid early interrupts; |
| * also, fill them from the higher index to the lowest, so the receive |
| * interrupt flag RF[0] will be the last to set, regardless of message size; |
| */ |
| void upwr_copy2tr(struct MU_t *local_mu, const uint32_t *msg, unsigned int size) |
| { |
| for (int i = (int)size - 1; i > -1; i--) { |
| local_mu->TR[i].R = msg[i]; |
| } |
| } |
| |
| /** |
| * upwr_tx() - queues a message for transmission. |
| * @msg : pointer to the message sent. |
| * @size: message size in 32-bit words |
| * @callback: pointer to a function to be called when transmission done; |
| * can be NULL, in which case no callback is done. |
| * |
| * This is an auxiliary function used by the rest of the API calls. |
| * It is normally not called by the driver code, unless maybe for test purposes. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: number of vacant positions left in the transmission queue, or |
| * -1 if the queue was already full when upwr_tx was called, or |
| * -2 if any argument is invalid (like size off-range) |
| */ |
| int upwr_tx(const uint32_t *msg, |
| unsigned int size, |
| UPWR_TX_CALLB_FUNC_T callback) |
| { |
| if (size > UPWR_MU_MSG_SIZE) { |
| return -2; |
| } |
| |
| if (size == 0U) { |
| return -2; |
| } |
| |
| if (mu->TSR.R != UPWR_MU_TSR_EMPTY) { |
| return -1; /* not all TE bits in 1: some data to send still */ |
| } |
| |
| mu_tx_callb = callback; |
| |
| upwr_copy2tr(mu, msg, size); |
| mu->TCR.R = 1UL << (size - 1UL); |
| |
| mu_tx_pend = 1UL; |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_rx() - unqueues a received message from the reception queue. |
| * @msg: pointer to the message destination buffer. |
| * @size: pointer to variable to hold message size in 32-bit words. |
| * |
| * This is an auxiliary function used by the rest of the API calls. |
| * It is normally not called by the driver code, unless maybe for test purposes. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: number of messages remaining in the reception queue, or |
| * -1 if the queue was already empty when upwr_rx was called, or |
| * -2 if any argument is invalid (like mu off-range) |
| */ |
| int upwr_rx(char *msg, unsigned int *size) |
| { |
| unsigned int len = mu->RSR.R; |
| |
| len = (len == 0x0U) ? 0U : |
| (len == 0x1U) ? 1U : |
| #if UPWR_MU_MSG_SIZE > 1 |
| (len == 0x3U) ? 2U : |
| #if UPWR_MU_MSG_SIZE > 2 |
| (len == 0x7U) ? 3U : |
| #if UPWR_MU_MSG_SIZE > 3 |
| (len == 0xFU) ? 4U : |
| #endif |
| #endif |
| #endif |
| 0xFFFFFFFFU; /* something wrong */ |
| |
| if (len == 0xFFFFFFFFU) { |
| return -3; |
| } |
| |
| if (len == 0U) { |
| return -1; |
| } |
| |
| *size = len; |
| |
| /* |
| * copy the received message to the rx queue, |
| * so the interrupts are cleared. |
| */ |
| msg_copy(msg, (char *)&mu->RR[0], len); |
| |
| mu->RCR.R = 1U; /* enable only RR[0] receive interrupt */ |
| |
| return 0; |
| } |
| |
| /** |
| * upwr_rx_callback() - sets up a callback for a message receiving event. |
| * @callback: pointer to a function to be called when a message arrives; |
| * can be NULL, in which case no callback is done. |
| * |
| * This is an auxiliary function used by the rest of the API calls. |
| * It is normally not called by the driver code, unless maybe for test purposes. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: 0 if ok; -2 if any argument is invalid (mu off-range). |
| */ |
| int upwr_rx_callback(UPWR_RX_CALLB_FUNC_T callback) |
| { |
| mu_rx_callb = callback; |
| |
| return 0; |
| } |
| |
| /** |
| * msg_copy() - copies a message. |
| * @dest: pointer to the destination message. |
| * @src : pointer to the source message. |
| * @size: message size in words. |
| * |
| * This is an auxiliary function used by the rest of the API calls. |
| * It is normally not called by the driver code, unless maybe for test purposes. |
| * |
| * Context: no sleep, no locks taken/released. |
| * Return: none (void) |
| */ |
| void msg_copy(char *dest, char *src, unsigned int size) |
| { |
| for (uint32_t i = 0U; i < size * sizeof(uint32_t); i++) { |
| dest[i] = src[i]; |
| } |
| } |