diff options
author | Manoj Kumar <manoj.kumar3@arm.com> | 2018-10-26 11:14:06 +0530 |
---|---|---|
committer | davidcunado-arm <david.cunado@arm.com> | 2018-10-30 16:58:33 +0000 |
commit | 6950ec3eaa8e1a819bff1a05b1923812c2cdfd92 (patch) | |
tree | cad4c6c9f812a96b04805928f3783ee236fd5cff /product/n1sdp | |
parent | a4a1ed21f6a87dd41341af8ce3686d4156e48caf (diff) |
n1sdp/n1sdp_system: add n1sdp_system module
This patch adds the n1sdp_system module to product/n1sdp/module
directory.
n1sdp_system is responsible for switching ON SYSTOP domain, copying
AP firmware to DDR and powering ON primary core.
Change-Id: I50d35401975c3a8f1774aa5505727fe87ea1d028
Signed-off-by: Manoj Kumar <manoj.kumar3@arm.com>
Diffstat (limited to 'product/n1sdp')
3 files changed, 541 insertions, 0 deletions
diff --git a/product/n1sdp/module/n1sdp_system/include/mod_n1sdp_system.h b/product/n1sdp/module/n1sdp_system/include/mod_n1sdp_system.h new file mode 100644 index 00000000..bd56147f --- /dev/null +++ b/product/n1sdp/module/n1sdp_system/include/mod_n1sdp_system.h @@ -0,0 +1,94 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2018, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Description: + * N1SDP System Support + */ + +#ifndef MOD_N1SDP_SYSTEM_H +#define MOD_N1SDP_SYSTEM_H + +/*! + * \addtogroup GroupN1SDPModule N1SDP Product Modules + * @{ + */ + +/*! + * \defgroup GroupN1SDPSystem N1SDP System Support + * @{ + */ + +/*! + * \brief N1SDP system macro definitions. + */ + +/*! + * N1SDP AP cores do not have internal ROM memory to boot code once they are + * released from reset. It is the responsibility of SCP to copy the AP + * firmware to internal/external memory and set the RVBAR register of the + * AP cores to corresponding memory's base address and then switch ON + * the PPU of primary core to release from reset. This macro specifies the + * base address of the memory (DDR) to which AP firmware will be copied to + * and therefore the value to set in the RVBAR of all AP cores. + */ +#define AP_CPU_RESET_ADDR UINT64_C(0xFF000000) + +/*! DDR Base address where AP BL33 (UEFI) will be copied to. */ +#define AP_BL33_BASE_ADDR UINT64_C(0xE0000000) + +/*! Address translation enable bit */ +#define ADDR_TRANS_EN UINT32_C(0x1) + +/*! + * Number of bits to shift in AP's memory map address to map to SCP's + * 1MB window. + */ +#define SCP_AP_1MB_WINDOW_ADDR_SHIFT 20 + +/*! + * \brief API indices. + */ +enum mod_n1sdp_system_api_idx { + /*! API index for the driver interface of the system_power module */ + MOD_N1SDP_SYSTEM_API_IDX_SYSTEM_POWER_DRIVER, + + /*! API index for AP memory access */ + MOD_N1SDP_SYSTEM_API_IDX_AP_MEMORY_ACCESS, + + /*! Number of exposed interfaces */ + MOD_N1SDP_SYSTEM_API_COUNT, +}; + +/*! + * \brief APIs to enable/disable access AP memory in 1MB window. + */ +struct mod_n1sdp_system_ap_memory_access_api { + /*! + * \brief Pointer to function that enables windowed access to AP memory. + * + * \param addr Base address of the 1MB AP memory window. + * + * \retval void. + */ + void (*enable_ap_memory_access)(uint32_t addr); + + /*! + * \brief Pointer to function that disables windowed access to AP memory. + * + * \retval void. + */ + void (*disable_ap_memory_access)(void); +}; + +/*! + * @} + */ + +/*! + * @} + */ + +#endif /* MOD_N1SDP_SYSTEM_H */ diff --git a/product/n1sdp/module/n1sdp_system/src/Makefile b/product/n1sdp/module/n1sdp_system/src/Makefile new file mode 100644 index 00000000..e0519a7e --- /dev/null +++ b/product/n1sdp/module/n1sdp_system/src/Makefile @@ -0,0 +1,11 @@ +# +# Arm SCP/MCP Software +# Copyright (c) 2018, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := N1SDP System +BS_LIB_SOURCES = mod_n1sdp_system.c + +include $(BS_DIR)/lib.mk diff --git a/product/n1sdp/module/n1sdp_system/src/mod_n1sdp_system.c b/product/n1sdp/module/n1sdp_system/src/mod_n1sdp_system.c new file mode 100644 index 00000000..6f5375d7 --- /dev/null +++ b/product/n1sdp/module/n1sdp_system/src/mod_n1sdp_system.c @@ -0,0 +1,436 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2018, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Description: + * N1SDP System Support. + */ + +#include <assert.h> +#include <stdint.h> +#include <string.h> +#include <fmw_cmsis.h> +#include <fwk_id.h> +#include <fwk_interrupt.h> +#include <fwk_macros.h> +#include <fwk_mm.h> +#include <fwk_module.h> +#include <fwk_module_idx.h> +#include <fwk_notification.h> +#include <mod_clock.h> +#include <mod_n1sdp_flash.h> +#include <mod_n1sdp_system.h> +#include <mod_log.h> +#include <mod_power_domain.h> +#include <mod_ppu_v1.h> +#include <mod_system_power.h> +#include <n1sdp_core.h> +#include <n1sdp_scp_pik.h> +#include <n1sdp_scp_irq.h> +#include <n1sdp_scp_mmap.h> +#include <config_clock.h> + +/* Module context */ +struct n1sdp_system_ctx { + + /* Log API pointer */ + const struct mod_log_api *log_api; + + /* Pointer to the Interrupt Service Routine API of the PPU_V1 module */ + const struct ppu_v1_isr_api *ppu_v1_isr_api; + + /* Power domain module restricted API pointer */ + struct mod_pd_restricted_api *mod_pd_restricted_api; + + /* Pointer to N1SDP Flash APIs */ + const struct mod_n1sdp_flash_api *flash_api; +}; + +struct n1sdp_system_isr { + unsigned int interrupt; + void (*handler)(void); +}; + +static struct n1sdp_system_ctx n1sdp_system_ctx; +const struct fwk_module_config config_n1sdp_system = { 0 }; + +/* + * PPU Interrupt Service Routines for cluster and core power domains + */ + +static void ppu_cores_isr(void) +{ + uint32_t status = PIK_SCP->CPU_PPU_INT_STATUS[0]; + unsigned int core_idx; + + while (status != 0) { + core_idx = __builtin_ctz(status); + status &= ~(1 << core_idx); + + if (core_idx >= n1sdp_core_get_core_count()) + continue; + + n1sdp_system_ctx.ppu_v1_isr_api->ppu_interrupt_handler( + FWK_ID_ELEMENT(FWK_MODULE_IDX_PPU_V1, core_idx)); + } +} + +static void ppu_clusters_isr(void) +{ + uint32_t status = PIK_SCP->CLUS_PPU_INT_STATUS; + unsigned int cluster_idx; + + while (status != 0) { + cluster_idx = __builtin_ctz(status); + + n1sdp_system_ctx.ppu_v1_isr_api->ppu_interrupt_handler( + FWK_ID_ELEMENT(FWK_MODULE_IDX_PPU_V1, + n1sdp_core_get_core_count() + cluster_idx)); + + status &= ~(1 << cluster_idx); + } +} + +/* + * PPU Interrupt Service Routine table + */ + +static struct n1sdp_system_isr isrs[] = { + [0] = { .interrupt = PPU_CORES0_IRQ, + .handler = ppu_cores_isr }, + [1] = { .interrupt = PPU_CLUSTERS_IRQ, + .handler = ppu_clusters_isr }, +}; + +/* + * System power module's driver API + */ + +static int n1sdp_system_shutdown( + enum mod_pd_system_shutdown system_shutdown) +{ + NVIC_SystemReset(); + + return FWK_E_DEVICE; +} + +static const struct mod_system_power_driver_api + n1sdp_system_power_driver_api = { + .system_shutdown = n1sdp_system_shutdown, +}; + +/* + * AP memory 1MB windowed access driver API + */ + +/* + * Function to set the AP address for which the SCP 1MB window maps to. + * dram_1mb_window_address should be 1MB aligned with no trailing zeros + * which means that the 1MB aligned base address should be right shifted by + * 20 bits before passing to this function. Bit 0 of ADDR_TRANS register + * is enable bit so the dram_1mb_window_address will be shifted left by + * one bit before programming by this function. + */ +static void n1sdp_system_enable_ap_memory_access( + uint32_t dram_1mb_window_address) +{ + uint32_t addr_trans_reg = 0; + + addr_trans_reg = (dram_1mb_window_address << 1); + addr_trans_reg |= ADDR_TRANS_EN; + PIK_SCP->ADDR_TRANS = addr_trans_reg; +} + +/* + * Function to disable 1MB address translation window. + */ +static void n1sdp_system_disable_ap_memory_access(void) +{ + PIK_SCP->ADDR_TRANS &= ~ADDR_TRANS_EN; +} + +struct mod_n1sdp_system_ap_memory_access_api + n1sdp_system_ap_memory_access_api = { + .enable_ap_memory_access = n1sdp_system_enable_ap_memory_access, + .disable_ap_memory_access = n1sdp_system_disable_ap_memory_access, +}; + +static int n1sdp_system_copy_to_ap_memory(uint64_t dram_address, + uint32_t spi_address, + uint32_t size) +{ + uint32_t scp_address = 0; + uint32_t copy_size = 0; + uint32_t addr_offset = 0; + int status = FWK_SUCCESS; + + while (size != 0) { + addr_offset = (uint32_t)(dram_address & SCP_AP_1MB_WINDOW_ADDR_MASK); + + /* Map 1MB window to corresponding address in AP memory map. */ + n1sdp_system_enable_ap_memory_access( + dram_address >> SCP_AP_1MB_WINDOW_ADDR_SHIFT); + + /* Get destination for this copy in SCP address space. */ + scp_address = SCP_AP_1MB_WINDOW_BASE | addr_offset; + + /* Get the size for this copy operation. */ + if (size > (SCP_AP_1MB_WINDOW_SIZE - addr_offset)) { + /* + * If the copy operation will wrap around the end of the 1MB window + * we need to cut it off at the wrap around point and change the + * the window address. + */ + copy_size = (uint32_t)(SCP_AP_1MB_WINDOW_SIZE - addr_offset); + } else { + /* All remaining data is within the current 1MB window. */ + copy_size = size; + } + + /* Copy the data into the selected 1MB window. */ + memcpy((void *)scp_address, (void *)spi_address, copy_size); + + if (memcmp((void *)spi_address, (void *)scp_address, copy_size) != 0) { + n1sdp_system_ctx.log_api->log(MOD_LOG_GROUP_INFO, + "[N1SDP SYSTEM] Copy failed at destination address: 0x%08x\n", + scp_address); + status = FWK_E_DATA; + goto exit; + } + + /* Update variables before starting over. */ + dram_address = dram_address + copy_size; + spi_address = spi_address + copy_size; + size = size - copy_size; + } + +exit: + /* Disable the 1MB window. */ + n1sdp_system_disable_ap_memory_access(); + + return status; +} + +/* + * Functions fulfilling the framework's module interface + */ + +static int n1sdp_system_mod_init(fwk_id_t module_id, unsigned int unused, + const void *unused2) +{ + int status; + unsigned int idx; + struct n1sdp_system_isr *isr; + + for (idx = 0; idx < FWK_ARRAY_SIZE(isrs); idx++) { + isr = &isrs[idx]; + status = fwk_interrupt_set_isr(isr->interrupt, isr->handler); + if (status != FWK_SUCCESS) + return status; + } + + return FWK_SUCCESS; +} + +static int n1sdp_system_bind(fwk_id_t id, unsigned int round) +{ + int status; + + if (round == 0) { + status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_LOG), + FWK_ID_API(FWK_MODULE_IDX_LOG, 0), &n1sdp_system_ctx.log_api); + if (status != FWK_SUCCESS) + return status; + } + + status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_N1SDP_FLASH), + FWK_ID_API(FWK_MODULE_IDX_N1SDP_FLASH, 0), + &n1sdp_system_ctx.flash_api); + if (status != FWK_SUCCESS) + return status; + + status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_POWER_DOMAIN), + FWK_ID_API(FWK_MODULE_IDX_POWER_DOMAIN, MOD_PD_API_IDX_RESTRICTED), + &n1sdp_system_ctx.mod_pd_restricted_api); + if (status != FWK_SUCCESS) + return status; + + return fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_PPU_V1), + FWK_ID_API(FWK_MODULE_IDX_PPU_V1, MOD_PPU_V1_API_IDX_ISR), + &n1sdp_system_ctx.ppu_v1_isr_api); +} + +static int n1sdp_system_process_bind_request(fwk_id_t requester_id, + fwk_id_t pd_id, fwk_id_t api_id, const void **api) +{ + switch (fwk_id_get_api_idx(api_id)) { + case MOD_N1SDP_SYSTEM_API_IDX_SYSTEM_POWER_DRIVER: + *api = &n1sdp_system_power_driver_api; + break; + case MOD_N1SDP_SYSTEM_API_IDX_AP_MEMORY_ACCESS: + *api = &n1sdp_system_ap_memory_access_api; + break; + default: + return FWK_E_PARAM; + } + + return FWK_SUCCESS; +} + +static int n1sdp_system_start(fwk_id_t id) +{ + int status; + + status = fwk_notification_subscribe( + mod_clock_notification_id_state_changed, + FWK_ID_ELEMENT(FWK_MODULE_IDX_CLOCK, CLOCK_IDX_INTERCONNECT), + id); + if (status != FWK_SUCCESS) + return status; + + n1sdp_system_ctx.log_api->log(MOD_LOG_GROUP_DEBUG, + "[N1SDP SYSTEM] Requesting SYSTOP initialization...\n"); + + return n1sdp_system_ctx.mod_pd_restricted_api->set_composite_state_async( + FWK_ID_ELEMENT(FWK_MODULE_IDX_POWER_DOMAIN, 0), false, + MOD_PD_COMPOSITE_STATE(MOD_PD_LEVEL_2, 0, MOD_PD_STATE_ON, + MOD_PD_STATE_OFF, MOD_PD_STATE_OFF)); +} + +static int n1sdp_system_process_notification(const struct fwk_event *event, + struct fwk_event *resp_event) +{ + struct clock_notification_params *params = NULL; + struct mod_pd_restricted_api *mod_pd_restricted_api = NULL; + struct mod_n1sdp_fip_descriptor *fip_desc_table = NULL; + unsigned int fip_count; + unsigned int i; + unsigned int core_idx; + unsigned int cluster_idx; + unsigned int cluster_count; + int fip_index_bl31 = -1; + int fip_index_bl33 = -1; + int status; + + assert(fwk_id_is_equal(event->id, + mod_clock_notification_id_state_changed)); + assert(fwk_id_is_type(event->target_id, FWK_ID_TYPE_MODULE)); + + params = (struct clock_notification_params *)event->params; + + /* + * Initialize primary core when the system is initialized for the + * first time only. + */ + if (params->new_state == MOD_CLOCK_STATE_RUNNING) { + + n1sdp_system_ctx.log_api->log(MOD_LOG_GROUP_INFO, + "[N1SDP SYSTEM] Looking for AP firmware in flash memory...\n"); + + status = n1sdp_system_ctx.flash_api->get_n1sdp_fip_descriptor_count( + FWK_ID_MODULE(FWK_MODULE_IDX_N1SDP_SYSTEM), &fip_count); + if (status != FWK_SUCCESS) + return status; + + status = n1sdp_system_ctx.flash_api->get_n1sdp_fip_descriptor_table( + FWK_ID_MODULE(FWK_MODULE_IDX_N1SDP_SYSTEM), &fip_desc_table); + if (status != FWK_SUCCESS) + return status; + + for (i = 0; i < fip_count; i++) { + if (fip_desc_table[i].type == MOD_N1SDP_FIP_TYPE_TF_BL31) { + n1sdp_system_ctx.log_api->log(MOD_LOG_GROUP_INFO, + "[N1SDP SYSTEM] Found BL31 at address: 0x%08x," + " size: %u, flags: 0x%x\n", + fip_desc_table[i].address, fip_desc_table[i].size, + fip_desc_table[i].flags); + fip_index_bl31 = i; + } else if (fip_desc_table[i].type == MOD_N1SDP_FIP_TYPE_NS_BL33) { + n1sdp_system_ctx.log_api->log(MOD_LOG_GROUP_INFO, + "[N1SDP SYSTEM] Found BL33 at address: 0x%08x," + " size: %u, flags: 0x%x\n", + fip_desc_table[i].address, fip_desc_table[i].size, + fip_desc_table[i].flags); + fip_index_bl33 = i; + } + } + + if ((fip_index_bl31 < 0) || (fip_index_bl33 < 0)) { + n1sdp_system_ctx.log_api->log(MOD_LOG_GROUP_INFO, + "[N1SDP SYSTEM] Error! " + "FIP does not have all required binaries\n"); + return FWK_E_PANIC; + } + + n1sdp_system_ctx.log_api->log(MOD_LOG_GROUP_INFO, + "[N1SDP SYSTEM] Copying AP BL31 to address 0x%x...\n", + AP_CPU_RESET_ADDR); + + status = n1sdp_system_copy_to_ap_memory(AP_CPU_RESET_ADDR, + fip_desc_table[fip_index_bl31].address, + fip_desc_table[fip_index_bl31].size); + if (status != FWK_SUCCESS) + return FWK_E_PANIC; + + n1sdp_system_ctx.log_api->log(MOD_LOG_GROUP_INFO, + "[N1SDP SYSTEM] Copying AP BL33 to address 0x%x...\n", + AP_BL33_BASE_ADDR); + + status = n1sdp_system_copy_to_ap_memory(AP_BL33_BASE_ADDR, + fip_desc_table[fip_index_bl33].address, + fip_desc_table[fip_index_bl33].size); + if (status != FWK_SUCCESS) + return FWK_E_PANIC; + + mod_pd_restricted_api = n1sdp_system_ctx.mod_pd_restricted_api; + + n1sdp_system_ctx.log_api->log(MOD_LOG_GROUP_INFO, + "[N1SDP SYSTEM] Setting AP Reset Address to 0x%x\n", + AP_CPU_RESET_ADDR); + + cluster_count = n1sdp_core_get_cluster_count(); + for (cluster_idx = 0; cluster_idx < cluster_count; cluster_idx++) { + for (core_idx = 0; + core_idx < n1sdp_core_get_core_per_cluster_count(cluster_idx); + core_idx++) { + PIK_CLUSTER(cluster_idx)->STATIC_CONFIG[core_idx].RVBARADDR_LW + = (uint32_t)(AP_CPU_RESET_ADDR); + PIK_CLUSTER(cluster_idx)->STATIC_CONFIG[core_idx].RVBARADDR_UP + = (uint32_t)(AP_CPU_RESET_ADDR >> 32); + } + } + + n1sdp_system_ctx.log_api->log(MOD_LOG_GROUP_DEBUG, + "[N1SDP SYSTEM] Switching ON primary core...\n"); + + status = mod_pd_restricted_api->set_composite_state_async( + FWK_ID_ELEMENT(FWK_MODULE_IDX_POWER_DOMAIN, 0), + false, + MOD_PD_COMPOSITE_STATE(MOD_PD_LEVEL_2, 0, MOD_PD_STATE_ON, + MOD_PD_STATE_ON, MOD_PD_STATE_ON)); + if (status != FWK_SUCCESS) + return status; + + /* + * Unsubscribe to interconnect clock state change notification as + * it has to be processed only once during system startup. + */ + return fwk_notification_unsubscribe(event->id, event->source_id, + event->target_id); + } + + return FWK_SUCCESS; +} + +const struct fwk_module module_n1sdp_system = { + .name = "N1SDP_SYSTEM", + .type = FWK_MODULE_TYPE_DRIVER, + .api_count = MOD_N1SDP_SYSTEM_API_COUNT, + .init = n1sdp_system_mod_init, + .bind = n1sdp_system_bind, + .process_bind_request = n1sdp_system_process_bind_request, + .process_notification = n1sdp_system_process_notification, + .start = n1sdp_system_start, +}; |