Merge pull request #709 from Xilinx/zynqmp-2016-09

xilinx: ZynqMP updates
- new SIP calls for bitstream programming
- new SIP call to discover the SOC silicon version
- support the delay timer
This commit is contained in:
davidcunado-arm 2016-09-16 13:57:10 +01:00 committed by GitHub
commit a8de89c974
13 changed files with 300 additions and 61 deletions

View File

@ -29,6 +29,7 @@
*/
#include <debug.h>
#include <generic_delay_timer.h>
#include <mmio.h>
#include <platform.h>
#include <xlat_tables.h>
@ -89,6 +90,18 @@ static unsigned int zynqmp_get_system_timer_freq(void)
return 100000000;
}
unsigned int zynqmp_get_silicon_id(void)
{
uint32_t id;
id = mmio_read_32(ZYNQMP_CSU_BASEADDR + ZYNQMP_CSU_IDCODE_OFFSET);
id &= ZYNQMP_CSU_IDCODE_DEVICE_CODE_MASK | ZYNQMP_CSU_IDCODE_SVD_MASK;
id >>= ZYNQMP_CSU_IDCODE_SVD_SHIFT;
return id;
}
#if LOG_LEVEL >= LOG_LEVEL_NOTICE
static const struct {
unsigned int id;
@ -140,18 +153,6 @@ static const struct {
},
};
static unsigned int zynqmp_get_silicon_id(void)
{
uint32_t id;
id = mmio_read_32(ZYNQMP_CSU_BASEADDR + ZYNQMP_CSU_IDCODE_OFFSET);
id &= ZYNQMP_CSU_IDCODE_DEVICE_CODE_MASK | ZYNQMP_CSU_IDCODE_SVD_MASK;
id >>= ZYNQMP_CSU_IDCODE_SVD_SHIFT;
return id;
}
static char *zynqmp_get_silicon_idcode_name(void)
{
unsigned int id;
@ -289,6 +290,8 @@ void zynqmp_config_setup(void)
/* Program freq register in System counter and enable system counter. */
mmio_write_32(IOU_SCNTRS_BASEFREQ, zynqmp_get_system_timer_freq());
mmio_write_32(IOU_SCNTRS_CONTROL, IOU_SCNTRS_CONTROL_EN);
generic_delay_timer_init();
}
unsigned int plat_get_syscnt_freq2(void)

View File

@ -118,11 +118,31 @@ void bl31_early_platform_setup(bl31_params_t *from_bl2,
NOTICE("BL31: Non secure code at 0x%lx\n", bl33_image_ep_info.pc);
}
/* Enable the test setup */
#ifndef ZYNQMP_TESTING
static void zynqmp_testing_setup(void) { }
#else
static void zynqmp_testing_setup(void)
{
uint32_t actlr_el3, actlr_el2;
/* Enable CPU ACTLR AND L2ACTLR RW access from non-secure world */
actlr_el3 = read_actlr_el3();
actlr_el2 = read_actlr_el2();
actlr_el3 |= ACTLR_EL3_L2ACTLR_BIT | ACTLR_EL3_CPUACTLR_BIT;
actlr_el2 |= ACTLR_EL3_L2ACTLR_BIT | ACTLR_EL3_CPUACTLR_BIT;
write_actlr_el3(actlr_el3);
write_actlr_el2(actlr_el2);
}
#endif
void bl31_platform_setup(void)
{
/* Initialize the gic cpu and distributor interfaces */
plat_arm_gic_driver_init();
plat_arm_gic_init();
zynqmp_testing_setup();
}
void bl31_plat_runtime_setup(void)

View File

@ -56,8 +56,7 @@
* little space for growth.
*/
#ifndef ZYNQMP_ATF_MEM_BASE
# define BL31_BASE 0xfffe5000
# define BL31_PROGBITS_LIMIT 0xffffa000
# define BL31_BASE 0xfffea000
# define BL31_LIMIT 0xffffffff
#else
# define BL31_BASE (ZYNQMP_ATF_MEM_BASE)
@ -101,11 +100,7 @@
******************************************************************************/
#define ADDR_SPACE_SIZE (1ull << 32)
#define MAX_MMAP_REGIONS 7
#if IMAGE_BL32
# define MAX_XLAT_TABLES 5
#else
# define MAX_XLAT_TABLES 4
#endif
#define MAX_XLAT_TABLES 5
#define CACHE_WRITEBACK_SHIFT 6
#define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT)

View File

@ -147,7 +147,7 @@ static void zynqmp_pwr_domain_off(const psci_power_state_t *target_state)
* invoking CPU_on function, during which resume address will
* be set.
*/
pm_self_suspend(proc->node_id, MAX_LATENCY, 0, 0);
pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0);
}
static void zynqmp_nopmu_pwr_domain_suspend(const psci_power_state_t *target_state)
@ -179,6 +179,7 @@ static void zynqmp_nopmu_pwr_domain_suspend(const psci_power_state_t *target_sta
static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state)
{
unsigned int state;
unsigned int cpu_id = plat_my_core_pos();
const struct pm_proc *proc = pm_get_proc(cpu_id);
@ -186,15 +187,14 @@ static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state)
VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
__func__, i, target_state->pwr_domain_state[i]);
state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ?
PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE;
/* Send request to PMU to suspend this core */
pm_self_suspend(proc->node_id, MAX_LATENCY, 0, zynqmp_sec_entry);
pm_self_suspend(proc->node_id, MAX_LATENCY, state, zynqmp_sec_entry);
/* APU is to be turned off */
if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
/* Power down L2 cache */
pm_set_requirement(NODE_L2, 0, 0, REQ_ACK_NO);
/* Send request for OCM retention state */
set_ocm_retention();
/* disable coherency */
plat_arm_interconnect_exit_coherency();
}
@ -242,6 +242,13 @@ static void zynqmp_pwr_domain_suspend_finish(const psci_power_state_t *target_st
/* enable coherency */
plat_arm_interconnect_enter_coherency();
/* APU was turned off */
if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
plat_arm_gic_init();
} else {
gicv2_cpuif_enable();
gicv2_pcpu_distif_init();
}
}
/*******************************************************************************
@ -308,7 +315,20 @@ int zynqmp_validate_power_state(unsigned int power_state,
{
VERBOSE("%s: power_state: 0x%x\n", __func__, power_state);
/* FIXME: populate req_state */
int pstate = psci_get_pstate_type(power_state);
assert(req_state);
/* Sanity check the requested state */
if (pstate == PSTATE_TYPE_STANDBY)
req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE;
else
req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE;
/* We expect the 'state id' to be zero */
if (psci_get_pstate_id(power_state))
return PSCI_E_INVALID_PARAMS;
return PSCI_E_SUCCESS;
}

View File

@ -31,6 +31,7 @@ PROGRAMMABLE_RESET_ADDRESS := 1
PSCI_EXTENDED_STATE_ID := 1
A53_DISABLE_NON_TEMPORAL_HINT := 0
SEPARATE_CODE_AND_RODATA := 1
RESET_TO_BL31 := 1
ifdef ZYNQMP_ATF_MEM_BASE
$(eval $(call add_define,ZYNQMP_ATF_MEM_BASE))
@ -64,6 +65,8 @@ PLAT_INCLUDES := -Iinclude/plat/arm/common/ \
PLAT_BL_COMMON_SOURCES := lib/xlat_tables/xlat_tables_common.c \
lib/xlat_tables/aarch64/xlat_tables.c \
drivers/delay_timer/delay_timer.c \
drivers/delay_timer/generic_delay_timer.c \
drivers/arm/gic/common/gic_common.c \
drivers/arm/gic/v2/gicv2_main.c \
drivers/arm/gic/v2/gicv2_helpers.c \

View File

@ -76,7 +76,7 @@
* pm_self_suspend() - PM call for processor to suspend itself
* @nid Node id of the processor or subsystem
* @latency Requested maximum wakeup latency (not supported)
* @state Requested state (not supported)
* @state Requested state
* @address Resume address
*
* This is a blocking call, it will return only once PMU has responded.
@ -97,7 +97,7 @@ enum pm_ret_status pm_self_suspend(enum pm_node_id nid,
* Do client specific suspend operations
* (e.g. set powerdown request bit)
*/
pm_client_suspend(proc);
pm_client_suspend(proc, state);
/* Send request to the PMU */
PM_PACK_PAYLOAD6(payload, PM_SELF_SUSPEND, proc->node_id, latency,
state, address, (address >> 32));
@ -476,7 +476,7 @@ enum pm_ret_status pm_mmio_write(uintptr_t address,
/* Send request to the PMU */
PM_PACK_PAYLOAD4(payload, PM_MMIO_WRITE, address, mask, value);
return pm_ipi_send(primary_proc, payload);
return pm_ipi_send_sync(primary_proc, payload, NULL);
}
/**
@ -497,3 +497,47 @@ enum pm_ret_status pm_mmio_read(uintptr_t address, unsigned int *value)
PM_PACK_PAYLOAD2(payload, PM_MMIO_READ, address);
return pm_ipi_send_sync(primary_proc, payload, value);
}
/**
* pm_fpga_load() - Load the bitstream into the PL.
*
* This function provides access to the xilfpga library to load
* the Bit-stream into PL.
*
* address_low: lower 32-bit Linear memory space address
*
* address_high: higher 32-bit Linear memory space address
*
* size: Number of 32bit words
*
* @return Returns status, either success or error+reason
*/
enum pm_ret_status pm_fpga_load(uint32_t address_low,
uint32_t address_high,
uint32_t size,
uint32_t flags)
{
uint32_t payload[PAYLOAD_ARG_CNT];
/* Send request to the PMU */
PM_PACK_PAYLOAD5(payload, PM_FPGA_LOAD, address_high, address_low,
size, flags);
return pm_ipi_send(primary_proc, payload);
}
/**
* pm_fpga_get_status() - Read value from fpga status register
* @value Value to read
*
* This function provides access to the xilfpga library to get
* the fpga status
* @return Returns status, either success or error+reason
*/
enum pm_ret_status pm_fpga_get_status(unsigned int *value)
{
uint32_t payload[PAYLOAD_ARG_CNT];
/* Send request to the PMU */
PM_PACK_PAYLOAD1(payload, PM_FPGA_GET_STATUS);
return pm_ipi_send_sync(primary_proc, payload, value);
}

View File

@ -109,4 +109,10 @@ enum pm_ret_status pm_mmio_write(uintptr_t address,
unsigned int mask,
unsigned int value);
enum pm_ret_status pm_mmio_read(uintptr_t address, unsigned int *value);
enum pm_ret_status pm_fpga_load(uint32_t address_high,
uint32_t address_low,
uint32_t size,
uint32_t flags);
enum pm_ret_status pm_fpga_get_status(unsigned int *value);
#endif /* _PM_API_SYS_H_ */

View File

@ -33,26 +33,25 @@
* for getting information about and changing state of the APU.
*/
#include <assert.h>
#include <bakery_lock.h>
#include <gicv2.h>
#include <gic_common.h>
#include <bl_common.h>
#include <mmio.h>
#include <string.h>
#include <utils.h>
#include "pm_api_sys.h"
#include "pm_client.h"
#include "pm_ipi.h"
#include "../zynqmp_def.h"
#define OCM_BANK_0 0xFFFC0000
#define OCM_BANK_1 (OCM_BANK_0 + 0x10000)
#define OCM_BANK_2 (OCM_BANK_1 + 0x10000)
#define OCM_BANK_3 (OCM_BANK_2 + 0x10000)
#define IRQ_MAX 84
#define NUM_GICD_ISENABLER ((IRQ_MAX >> 5) + 1)
#define UNDEFINED_CPUID (~0)
DEFINE_BAKERY_LOCK(pm_client_secure_lock);
/* Declaration of linker defined symbol */
extern unsigned long __BL31_END__;
extern const struct pm_ipi apu_ipi;
/* Order in pm_procs_all array must match cpu ids */
@ -79,36 +78,146 @@ static const struct pm_proc const pm_procs_all[] = {
},
};
/* Interrupt to PM node ID map */
static enum pm_node_id irq_node_map[IRQ_MAX + 1] = {
NODE_UNKNOWN,
NODE_UNKNOWN,
NODE_UNKNOWN,
NODE_UNKNOWN, /* 3 */
NODE_UNKNOWN,
NODE_UNKNOWN,
NODE_UNKNOWN,
NODE_UNKNOWN, /* 7 */
NODE_UNKNOWN,
NODE_UNKNOWN,
NODE_UNKNOWN,
NODE_UNKNOWN, /* 11 */
NODE_UNKNOWN,
NODE_UNKNOWN,
NODE_NAND,
NODE_QSPI, /* 15 */
NODE_GPIO,
NODE_I2C_0,
NODE_I2C_1,
NODE_SPI_0, /* 19 */
NODE_SPI_1,
NODE_UART_0,
NODE_UART_1,
NODE_CAN_0, /* 23 */
NODE_CAN_1,
NODE_UNKNOWN,
NODE_RTC,
NODE_RTC, /* 27 */
NODE_UNKNOWN,
NODE_UNKNOWN,
NODE_UNKNOWN,
NODE_UNKNOWN, /* 31 */
NODE_UNKNOWN,
NODE_UNKNOWN,
NODE_UNKNOWN,
NODE_UNKNOWN, /* 35, NODE_IPI_APU */
NODE_TTC_0,
NODE_TTC_0,
NODE_TTC_0,
NODE_TTC_1, /* 39 */
NODE_TTC_1,
NODE_TTC_1,
NODE_TTC_2,
NODE_TTC_2, /* 43 */
NODE_TTC_2,
NODE_TTC_3,
NODE_TTC_3,
NODE_TTC_3, /* 47 */
NODE_SD_0,
NODE_SD_1,
NODE_SD_0,
NODE_SD_1, /* 51 */
NODE_UNKNOWN,
NODE_UNKNOWN,
NODE_UNKNOWN,
NODE_UNKNOWN, /* 55 */
NODE_UNKNOWN,
NODE_ETH_0,
NODE_ETH_0,
NODE_ETH_1, /* 59 */
NODE_ETH_1,
NODE_ETH_2,
NODE_ETH_2,
NODE_ETH_3, /* 63 */
NODE_ETH_3,
NODE_USB_0,
NODE_USB_0,
NODE_USB_0, /* 67 */
NODE_USB_0,
NODE_USB_0,
NODE_USB_1,
NODE_USB_1, /* 71 */
NODE_USB_1,
NODE_USB_1,
NODE_USB_1,
NODE_USB_0, /* 75 */
NODE_USB_0,
NODE_ADMA,
NODE_ADMA,
NODE_ADMA, /* 79 */
NODE_ADMA,
NODE_ADMA,
NODE_ADMA,
NODE_ADMA, /* 83 */
NODE_ADMA,
};
/**
* set_ocm_retention() - Configure OCM memory banks for retention
* irq_to_pm_node - Get PM node ID corresponding to the interrupt number
* @irq: Interrupt number
*
* APU specific requirements for suspend action:
* OCM has to enter retention state in order to preserve saved
* context after suspend request. OCM banks are determined by
* __BL31_END__ linker symbol.
*
* Return: Returns status, either success or error+reason
* Return: PM node ID corresponding to the specified interrupt
*/
enum pm_ret_status set_ocm_retention(void)
static enum pm_node_id irq_to_pm_node(unsigned int irq)
{
enum pm_ret_status ret;
assert(irq <= IRQ_MAX);
return irq_node_map[irq];
}
/* OCM_BANK_0 will always be occupied */
ret = pm_set_requirement(NODE_OCM_BANK_0, PM_CAP_CONTEXT, 0,
REQ_ACK_NO);
/**
* pm_client_set_wakeup_sources - Set all slaves with enabled interrupts as wake
* sources in the PMU firmware
*/
static void pm_client_set_wakeup_sources(void)
{
uint32_t reg_num;
uint8_t pm_wakeup_nodes_set[NODE_MAX];
uintptr_t isenabler1 = BASE_GICD_BASE + GICD_ISENABLER + 4;
/* Check for other OCM banks */
if ((unsigned long)&__BL31_END__ >= OCM_BANK_1)
ret = pm_set_requirement(NODE_OCM_BANK_1, PM_CAP_CONTEXT, 0,
REQ_ACK_NO);
if ((unsigned long)&__BL31_END__ >= OCM_BANK_2)
ret = pm_set_requirement(NODE_OCM_BANK_2, PM_CAP_CONTEXT, 0,
REQ_ACK_NO);
if ((unsigned long)&__BL31_END__ >= OCM_BANK_3)
ret = pm_set_requirement(NODE_OCM_BANK_3, PM_CAP_CONTEXT, 0,
REQ_ACK_NO);
memset(&pm_wakeup_nodes_set, 0, sizeof(pm_wakeup_nodes_set));
return ret;
for (reg_num = 0; reg_num < NUM_GICD_ISENABLER; reg_num++) {
uint32_t base_irq = reg_num << ISENABLER_SHIFT;
uint32_t reg = mmio_read_32(isenabler1 + (reg_num << 2));
if (!reg)
continue;
while (reg) {
enum pm_node_id node;
uint32_t idx, ret, irq, lowest_set = reg & (-reg);
idx = __builtin_ctz(lowest_set);
irq = base_irq + idx;
if (irq > IRQ_MAX)
break;
node = irq_to_pm_node(irq);
reg &= ~lowest_set;
if ((node != NODE_UNKNOWN) &&
(!pm_wakeup_nodes_set[node])) {
ret = pm_set_wakeup_source(NODE_APU, node, 1);
pm_wakeup_nodes_set[node] = !ret;
}
}
}
}
/**
@ -162,11 +271,15 @@ const struct pm_proc *primary_proc = &pm_procs_all[0];
*
* This function should contain any PU-specific actions
* required prior to sending suspend request to PMU
* Actions taken depend on the state system is suspending to.
*/
void pm_client_suspend(const struct pm_proc *proc)
void pm_client_suspend(const struct pm_proc *proc, unsigned int state)
{
bakery_lock_get(&pm_client_secure_lock);
if (state == PM_STATE_SUSPEND_TO_RAM)
pm_client_set_wakeup_sources();
/* Set powerdown request */
mmio_write_32(APU_PWRCTL, mmio_read_32(APU_PWRCTL) | proc->pwrdn_mask);

View File

@ -40,7 +40,7 @@
#include "pm_common.h"
/* Functions to be implemented by each PU */
void pm_client_suspend(const struct pm_proc *proc);
void pm_client_suspend(const struct pm_proc *proc, unsigned int state);
void pm_client_abort_suspend(void);
void pm_client_wakeup(const struct pm_proc *proc);
enum pm_ret_status set_ocm_retention(void);

View File

@ -53,6 +53,10 @@
#define MAX_LATENCY (~0U)
#define MAX_QOS 100U
/* State arguments of the self suspend */
#define PM_STATE_CPU_IDLE 0x0U
#define PM_STATE_SUSPEND_TO_RAM 0xFU
/*********************************************************************
* Enum definitions
********************************************************************/
@ -82,6 +86,10 @@ enum pm_api_id {
PM_RESET_GET_STATUS,
PM_MMIO_WRITE,
PM_MMIO_READ,
PM_INIT,
PM_FPGA_LOAD,
PM_FPGA_GET_STATUS,
PM_GET_CHIPID,
PM_API_MAX
};
@ -143,6 +151,12 @@ enum pm_node_id {
NODE_IOPLL,
NODE_DDR,
NODE_IPI_APU,
NODE_IPI_RPU_0,
NODE_GPU,
NODE_PCIE,
NODE_PCAP,
NODE_RTC,
NODE_MAX
};
enum pm_request_ack {

View File

@ -228,6 +228,22 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3,
ret = pm_mmio_read(pm_arg[0], &value);
SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32);
}
case PM_FPGA_LOAD:
ret = pm_fpga_load(pm_arg[0], pm_arg[1], pm_arg[2], pm_arg[3]);
SMC_RET1(handle, (uint64_t)ret);
case PM_FPGA_GET_STATUS:
{
uint32_t value;
ret = pm_fpga_get_status(&value);
SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32);
}
case PM_GET_CHIPID:
SMC_RET1(handle, zynqmp_get_silicon_id());
default:
WARN("Unimplemented PM Service Call: 0x%x\n", smc_fid);
SMC_RET1(handle, SMC_UNK);

View File

@ -201,4 +201,8 @@
#define ZYNQMP_CSU_VERSION_OFFSET 0x44
/* Access control register defines */
#define ACTLR_EL3_L2ACTLR_BIT (1 << 6)
#define ACTLR_EL3_CPUACTLR_BIT (1 << 0)
#endif /* __ZYNQMP_DEF_H__ */

View File

@ -39,6 +39,7 @@ void zynqmp_config_setup(void);
unsigned int zynqmp_get_uart_clk(void);
int zynqmp_is_pmu_up(void);
unsigned int zynqmp_get_bootmode(void);
unsigned int zynqmp_get_silicon_id(void);
/* For FSBL handover */
void fsbl_atf_handover(entry_point_info_t *bl32_image_ep_info,