diff options
44 files changed, 1142 insertions, 225 deletions
diff --git a/docs/firmware-design.md b/docs/firmware-design.md index 68cad2e3..90ce4b1b 100644 --- a/docs/firmware-design.md +++ b/docs/firmware-design.md @@ -716,7 +716,7 @@ required support. |`PSCI_FEATURES` | Yes | | |`CPU_FREEZE` | No | | |`CPU_DEFAULT_SUSPEND` | No | | -|`CPU_HW_STATE` | No | | +|`NODE_HW_STATE` | Yes* | | |`SYSTEM_SUSPEND` | Yes* | | |`PSCI_SET_SUSPEND_MODE`| No | | |`PSCI_STAT_RESIDENCY` | Yes* | | diff --git a/docs/porting-guide.md b/docs/porting-guide.md index 8dad4a05..195c9374 100644 --- a/docs/porting-guide.md +++ b/docs/porting-guide.md @@ -1832,6 +1832,20 @@ This function can also be used in case the platform wants to support local power state encoding for `power_state` parameter of PSCI_STAT_COUNT/RESIDENCY APIs as described in Section 5.18 of [PSCI]. +#### plat_psci_ops.get_node_hw_state() + +This is an optional function. If implemented this function is intended to return +the power state of a node (identified by the first parameter, the `MPIDR`) in +the power domain topology (identified by the second parameter, `power_level`), +as retrieved from a power controller or equivalent component on the platform. +Upon successful completion, the implementation must map and return the final +status among `HW_ON`, `HW_OFF` or `HW_STANDBY`. Upon encountering failures, it +must return either `PSCI_E_INVALID_PARAMS` or `PSCI_E_NOT_SUPPORTED` as +appropriate. + +Implementations are not expected to handle `power_levels` greater than +`PLAT_MAX_PWR_LVL`. + 3.6 Interrupt Management framework (in BL31) ---------------------------------------------- BL31 implements an Interrupt Management Framework (IMF) to manage interrupts diff --git a/docs/user-guide.md b/docs/user-guide.md index a6959b14..d545262c 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -64,7 +64,7 @@ Cygwin, and Msys (MinGW) shells, using version 4.9.1 of the GNU toolchain. Install the required packages to build Trusted Firmware with the following command: - sudo apt-get install build-essential gcc make git + sudo apt-get install build-essential gcc make git libssl-dev Download and install the AArch64 little-endian GCC cross compiler as indicated in the [Linaro instructions][Linaro SW Instructions]. @@ -74,8 +74,6 @@ In addition, the following optional packages and tools may be needed: * `device-tree-compiler` package if you need to rebuild the Flattened Device Tree (FDT) source files (`.dts` files) provided with this software. -* `libssl-dev` package if Trusted Board Boot is enabled in the build. - * For debugging, ARM [Development Studio 5 (DS-5)][DS-5]. diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index d13e2c9c..ac433725 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -120,10 +120,12 @@ void gicv3_driver_init(const gicv3_driver_data_t *plat_driver_data) ******************************************************************************/ void gicv3_distif_init(void) { + unsigned int bitmap = 0; + assert(driver_data); assert(driver_data->gicd_base); - assert(driver_data->g1s_interrupt_array); - assert(driver_data->g0_interrupt_array); + assert(driver_data->g1s_interrupt_array || + driver_data->g0_interrupt_array); assert(IS_IN_EL3()); @@ -146,21 +148,25 @@ void gicv3_distif_init(void) gicv3_spis_configure_defaults(driver_data->gicd_base); /* Configure the G1S SPIs */ - gicv3_secure_spis_configure(driver_data->gicd_base, + if (driver_data->g1s_interrupt_array) { + gicv3_secure_spis_configure(driver_data->gicd_base, driver_data->g1s_interrupt_num, driver_data->g1s_interrupt_array, INTR_GROUP1S); + bitmap |= CTLR_ENABLE_G1S_BIT; + } /* Configure the G0 SPIs */ - gicv3_secure_spis_configure(driver_data->gicd_base, + if (driver_data->g0_interrupt_array) { + gicv3_secure_spis_configure(driver_data->gicd_base, driver_data->g0_interrupt_num, driver_data->g0_interrupt_array, INTR_GROUP0); + bitmap |= CTLR_ENABLE_G0_BIT; + } /* Enable the secure SPIs now that they have been configured */ - gicd_set_ctlr(driver_data->gicd_base, - CTLR_ENABLE_G1S_BIT | CTLR_ENABLE_G0_BIT, - RWP_TRUE); + gicd_set_ctlr(driver_data->gicd_base, bitmap, RWP_TRUE); } /******************************************************************************* @@ -177,8 +183,8 @@ void gicv3_rdistif_init(unsigned int proc_num) assert(driver_data->rdistif_base_addrs); assert(driver_data->gicd_base); assert(gicd_read_ctlr(driver_data->gicd_base) & CTLR_ARE_S_BIT); - assert(driver_data->g1s_interrupt_array); - assert(driver_data->g0_interrupt_array); + assert(driver_data->g1s_interrupt_array || + driver_data->g0_interrupt_array); assert(IS_IN_EL3()); @@ -188,16 +194,20 @@ void gicv3_rdistif_init(unsigned int proc_num) gicv3_ppi_sgi_configure_defaults(gicr_base); /* Configure the G1S SGIs/PPIs */ - gicv3_secure_ppi_sgi_configure(gicr_base, - driver_data->g1s_interrupt_num, - driver_data->g1s_interrupt_array, - INTR_GROUP1S); + if (driver_data->g1s_interrupt_array) { + gicv3_secure_ppi_sgi_configure(gicr_base, + driver_data->g1s_interrupt_num, + driver_data->g1s_interrupt_array, + INTR_GROUP1S); + } /* Configure the G0 SGIs/PPIs */ - gicv3_secure_ppi_sgi_configure(gicr_base, - driver_data->g0_interrupt_num, - driver_data->g0_interrupt_array, - INTR_GROUP0); + if (driver_data->g0_interrupt_array) { + gicv3_secure_ppi_sgi_configure(gicr_base, + driver_data->g0_interrupt_num, + driver_data->g0_interrupt_array, + INTR_GROUP0); + } } /******************************************************************************* diff --git a/include/lib/pmf/pmf_asm_macros.S b/include/lib/pmf/pmf_asm_macros.S index 42dcd794..be7338b0 100644 --- a/include/lib/pmf/pmf_asm_macros.S +++ b/include/lib/pmf/pmf_asm_macros.S @@ -34,12 +34,11 @@ #define PMF_TS_SIZE 8 /* - * This macro calculates the offset into the memory - * region where the per-cpu timestamp value is stored - * for the given service name and timestamp id. + * This macro calculates the address of the per-cpu timestamp + * for the given service name and local timestamp id. * Clobbers: x0 - x9 */ - .macro pmf_calc_timestamp_offset _name _tid + .macro pmf_calc_timestamp_addr _name _tid mov x9, x30 bl plat_my_core_pos mov x30, x9 diff --git a/include/lib/pmf/pmf_helpers.h b/include/lib/pmf/pmf_helpers.h index 9be6050f..bb4242c1 100644 --- a/include/lib/pmf/pmf_helpers.h +++ b/include/lib/pmf/pmf_helpers.h @@ -71,7 +71,9 @@ typedef struct pmf_svc_desc { */ #define PMF_ALLOCATE_TIMESTAMP_MEMORY(_name, _total_id) \ unsigned long long pmf_ts_mem_ ## _name[_total_id] \ - __section("pmf_timestamp_array") __used; + __aligned(CACHE_WRITEBACK_GRANULE) \ + __section("pmf_timestamp_array") \ + __used; /* * Convenience macro to validate tid index for the given TS array. diff --git a/include/lib/psci/psci.h b/include/lib/psci/psci.h index a583fef7..02cbbf35 100644 --- a/include/lib/psci/psci.h +++ b/include/lib/psci/psci.h @@ -78,6 +78,8 @@ #define PSCI_SYSTEM_OFF 0x84000008 #define PSCI_SYSTEM_RESET 0x84000009 #define PSCI_FEATURES 0x8400000A +#define PSCI_NODE_HW_STATE_AARCH32 0x8400000d +#define PSCI_NODE_HW_STATE_AARCH64 0xc400000d #define PSCI_SYSTEM_SUSPEND_AARCH32 0x8400000E #define PSCI_SYSTEM_SUSPEND_AARCH64 0xc400000E #define PSCI_STAT_RESIDENCY_AARCH32 0x84000010 @@ -200,6 +202,17 @@ typedef enum { } aff_info_state_t; /* + * These are the power states reported by PSCI_NODE_HW_STATE API for the + * specified CPU. The definitions of these states can be found in Section 5.15.3 + * of PSCI specification (ARM DEN 0022C). + */ +typedef enum { + HW_ON = 0, + HW_OFF = 1, + HW_STANDBY = 2 +} node_hw_state_t; + +/* * Macro to represent invalid affinity level within PSCI. */ #define PSCI_INVALID_PWR_LVL (PLAT_MAX_PWR_LVL + 1) @@ -293,6 +306,7 @@ typedef struct plat_psci_ops { int (*translate_power_state_by_mpidr)(u_register_t mpidr, unsigned int power_state, psci_power_state_t *output_state); + int (*get_node_hw_state)(u_register_t mpidr, unsigned int power_level); } plat_psci_ops_t; /******************************************************************************* @@ -330,6 +344,8 @@ int psci_affinity_info(u_register_t target_affinity, int psci_migrate(u_register_t target_cpu); int psci_migrate_info_type(void); long psci_migrate_info_up_cpu(void); +int psci_node_hw_state(u_register_t target_cpu, + unsigned int power_level); int psci_features(unsigned int psci_fid); void __dead2 psci_power_down_wfi(void); void psci_arch_setup(void); diff --git a/include/lib/xlat_tables.h b/include/lib/xlat_tables.h index d2ac6db6..0e9800ab 100644 --- a/include/lib/xlat_tables.h +++ b/include/lib/xlat_tables.h @@ -32,13 +32,27 @@ #define __XLAT_TABLES_H__ /* Miscellaneous MMU related constants */ +#define NUM_2MB_IN_GB (1 << 9) +#define NUM_4K_IN_2MB (1 << 9) +#define NUM_GB_IN_4GB (1 << 2) + +#define TWO_MB_SHIFT 21 +#define ONE_GB_SHIFT 30 #define FOUR_KB_SHIFT 12 +#define ONE_GB_INDEX(x) ((x) >> ONE_GB_SHIFT) +#define TWO_MB_INDEX(x) ((x) >> TWO_MB_SHIFT) +#define FOUR_KB_INDEX(x) ((x) >> FOUR_KB_SHIFT) + #define INVALID_DESC 0x0 #define BLOCK_DESC 0x1 /* Table levels 0-2 */ #define TABLE_DESC 0x3 /* Table levels 0-2 */ #define PAGE_DESC 0x3 /* Table level 3 */ +#define FIRST_LEVEL_DESC_N ONE_GB_SHIFT +#define SECOND_LEVEL_DESC_N TWO_MB_SHIFT +#define THIRD_LEVEL_DESC_N FOUR_KB_SHIFT + #define XN (1ull << 2) #define PXN (1ull << 1) #define CONT_HINT (1ull << 0) diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h index 25aab249..d2e8729b 100644 --- a/include/plat/arm/common/plat_arm.h +++ b/include/plat/arm/common/plat_arm.h @@ -37,6 +37,12 @@ #include <utils.h> #include <xlat_tables.h> +/******************************************************************************* + * Forward declarations + ******************************************************************************/ +struct bl31_params; +struct meminfo; + #define ARM_CASSERT_MMAP \ CASSERT((ARRAY_SIZE(plat_arm_mmap) + ARM_BL_REGIONS) \ <= MAX_MMAP_REGIONS, \ @@ -145,7 +151,7 @@ void arm_bl1_platform_setup(void); void arm_bl1_plat_arch_setup(void); /* BL2 utility functions */ -void arm_bl2_early_platform_setup(meminfo_t *mem_layout); +void arm_bl2_early_platform_setup(struct meminfo *mem_layout); void arm_bl2_platform_setup(void); void arm_bl2_plat_arch_setup(void); uint32_t arm_get_spsr_for_bl32_entry(void); @@ -158,7 +164,7 @@ void arm_bl2u_platform_setup(void); void arm_bl2u_plat_arch_setup(void); /* BL31 utility functions */ -void arm_bl31_early_platform_setup(bl31_params_t *from_bl2, +void arm_bl31_early_platform_setup(struct bl31_params *from_bl2, void *plat_params_from_bl2); void arm_bl31_platform_setup(void); void arm_bl31_plat_runtime_setup(void); diff --git a/include/plat/arm/css/common/css_def.h b/include/plat/arm/css/common/css_def.h index 636daf29..173de1b4 100644 --- a/include/plat/arm/css/common/css_def.h +++ b/include/plat/arm/css/common/css_def.h @@ -148,5 +148,15 @@ /* Trusted mailbox base address common to all CSS */ #define PLAT_ARM_TRUSTED_MAILBOX_BASE ARM_TRUSTED_SRAM_BASE +/* + * Parsing of CPU and Cluster states, as returned by 'Get CSS Power State' SCP + * command + */ +#define CSS_CLUSTER_PWR_STATE_ON 0 +#define CSS_CLUSTER_PWR_STATE_OFF 3 + +#define CSS_CPU_PWR_STATE_ON 1 +#define CSS_CPU_PWR_STATE_OFF 0 +#define CSS_CPU_PWR_STATE(state, n) (((state) >> (n)) & 1) #endif /* __CSS_DEF_H__ */ diff --git a/include/plat/arm/css/common/css_pm.h b/include/plat/arm/css/common/css_pm.h index ea6a5d25..4a6ca816 100644 --- a/include/plat/arm/css/common/css_pm.h +++ b/include/plat/arm/css/common/css_pm.h @@ -45,5 +45,6 @@ void __dead2 css_system_off(void); void __dead2 css_system_reset(void); void css_cpu_standby(plat_local_state_t cpu_state); void css_get_sys_suspend_power_state(psci_power_state_t *req_state); +int css_node_hw_state(u_register_t mpidr, unsigned int power_level); #endif /* __CSS_PM_H__ */ diff --git a/lib/psci/psci_main.c b/lib/psci/psci_main.c index 3ad3dd40..23bd106c 100644 --- a/lib/psci/psci_main.c +++ b/lib/psci/psci_main.c @@ -295,6 +295,31 @@ long psci_migrate_info_up_cpu(void) return resident_cpu_mpidr; } +int psci_node_hw_state(u_register_t target_cpu, + unsigned int power_level) +{ + int rc; + + /* Validate target_cpu */ + rc = psci_validate_mpidr(target_cpu); + if (rc != PSCI_E_SUCCESS) + return PSCI_E_INVALID_PARAMS; + + /* Validate power_level against PLAT_MAX_PWR_LVL */ + if (power_level > PLAT_MAX_PWR_LVL) + return PSCI_E_INVALID_PARAMS; + + /* + * Dispatch this call to platform to query power controller, and pass on + * to the caller what it returns + */ + assert(psci_plat_pm_ops->get_node_hw_state); + rc = psci_plat_pm_ops->get_node_hw_state(target_cpu, power_level); + assert((rc >= HW_ON && rc <= HW_STANDBY) || rc == PSCI_E_NOT_SUPPORTED + || rc == PSCI_E_INVALID_PARAMS); + return rc; +} + int psci_features(unsigned int psci_fid) { unsigned int local_caps = psci_caps; @@ -378,6 +403,9 @@ u_register_t psci_smc_handler(uint32_t smc_fid, case PSCI_MIG_INFO_UP_CPU_AARCH32: return psci_migrate_info_up_cpu(); + case PSCI_NODE_HW_STATE_AARCH32: + return psci_node_hw_state(x1, x2); + case PSCI_SYSTEM_SUSPEND_AARCH32: return psci_system_suspend(x1, x2); @@ -422,6 +450,9 @@ u_register_t psci_smc_handler(uint32_t smc_fid, case PSCI_MIG_INFO_UP_CPU_AARCH64: return psci_migrate_info_up_cpu(); + case PSCI_NODE_HW_STATE_AARCH64: + return psci_node_hw_state(x1, x2); + case PSCI_SYSTEM_SUSPEND_AARCH64: return psci_system_suspend(x1, x2); diff --git a/lib/psci/psci_private.h b/lib/psci/psci_private.h index b795c8e0..781b3b52 100644 --- a/lib/psci/psci_private.h +++ b/lib/psci/psci_private.h @@ -68,6 +68,7 @@ define_psci_cap(PSCI_AFFINITY_INFO_AARCH64) | \ define_psci_cap(PSCI_MIG_AARCH64) | \ define_psci_cap(PSCI_MIG_INFO_UP_CPU_AARCH64) | \ + define_psci_cap(PSCI_NODE_HW_STATE_AARCH64) | \ define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64) | \ define_psci_cap(PSCI_STAT_RESIDENCY_AARCH64) | \ define_psci_cap(PSCI_STAT_COUNT_AARCH64)) diff --git a/lib/psci/psci_setup.c b/lib/psci/psci_setup.c index 20d06352..263ab68f 100644 --- a/lib/psci/psci_setup.c +++ b/lib/psci/psci_setup.c @@ -238,6 +238,13 @@ int psci_setup(uintptr_t mailbox_ep) plat_setup_psci_ops(mailbox_ep, &psci_plat_pm_ops); assert(psci_plat_pm_ops); + /* + * Flush `psci_plat_pm_ops` as it will be accessed by secondary CPUs + * during warm boot before data cache is enabled. + */ + flush_dcache_range((uintptr_t)&psci_plat_pm_ops, + sizeof(psci_plat_pm_ops)); + /* Initialize the psci capability */ psci_caps = PSCI_GENERIC_CAP; @@ -256,6 +263,8 @@ int psci_setup(uintptr_t mailbox_ep) psci_caps |= define_psci_cap(PSCI_SYSTEM_OFF); if (psci_plat_pm_ops->system_reset) psci_caps |= define_psci_cap(PSCI_SYSTEM_RESET); + if (psci_plat_pm_ops->get_node_hw_state) + psci_caps |= define_psci_cap(PSCI_NODE_HW_STATE_AARCH64); #if ENABLE_PSCI_STAT psci_caps |= define_psci_cap(PSCI_STAT_RESIDENCY_AARCH64); diff --git a/plat/arm/board/fvp/fvp_pm.c b/plat/arm/board/fvp/fvp_pm.c index 3976ef2b..66c0c3df 100644 --- a/plat/arm/board/fvp/fvp_pm.c +++ b/plat/arm/board/fvp/fvp_pm.c @@ -287,6 +287,42 @@ static void __dead2 fvp_system_reset(void) panic(); } +static int fvp_node_hw_state(u_register_t target_cpu, + unsigned int power_level) +{ + unsigned int psysr; + int ret; + + /* + * The format of 'power_level' is implementation-defined, but 0 must + * mean a CPU. We also allow 1 to denote the cluster + */ + if (power_level != ARM_PWR_LVL0 && power_level != ARM_PWR_LVL1) + return PSCI_E_INVALID_PARAMS; + + /* + * Read the status of the given MPDIR from FVP power controller. The + * power controller only gives us on/off status, so map that to expected + * return values of the PSCI call + */ + psysr = fvp_pwrc_read_psysr(target_cpu); + if (psysr == PSYSR_INVALID) + return PSCI_E_INVALID_PARAMS; + + switch (power_level) { + case ARM_PWR_LVL0: + ret = (psysr & PSYSR_AFF_L0) ? HW_ON : HW_OFF; + break; + case ARM_PWR_LVL1: + ret = (psysr & PSYSR_AFF_L1) ? HW_ON : HW_OFF; + break; + default: + assert(0); + } + + return ret; +} + /******************************************************************************* * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard * platform layer will take care of registering the handlers with PSCI. @@ -301,5 +337,6 @@ const plat_psci_ops_t plat_arm_psci_pm_ops = { .system_off = fvp_system_off, .system_reset = fvp_system_reset, .validate_power_state = arm_validate_power_state, - .validate_ns_entrypoint = arm_validate_ns_entrypoint + .validate_ns_entrypoint = arm_validate_ns_entrypoint, + .get_node_hw_state = fvp_node_hw_state }; diff --git a/plat/arm/board/juno/juno_pm.c b/plat/arm/board/juno/juno_pm.c index cbf994a4..c355d94d 100644 --- a/plat/arm/board/juno/juno_pm.c +++ b/plat/arm/board/juno/juno_pm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -88,5 +88,6 @@ const plat_psci_ops_t plat_arm_psci_pm_ops = { .validate_power_state = juno_validate_power_state, .validate_ns_entrypoint = arm_validate_ns_entrypoint, .get_sys_suspend_power_state = css_get_sys_suspend_power_state, - .translate_power_state_by_mpidr = juno_translate_power_state_by_mpidr + .translate_power_state_by_mpidr = juno_translate_power_state_by_mpidr, + .get_node_hw_state = css_node_hw_state }; diff --git a/plat/arm/css/common/css_pm.c b/plat/arm/css/common/css_pm.c index 801d9375..7607f617 100644 --- a/plat/arm/css/common/css_pm.c +++ b/plat/arm/css/common/css_pm.c @@ -299,6 +299,43 @@ void css_get_sys_suspend_power_state(psci_power_state_t *req_state) } /******************************************************************************* + * Handler to query CPU/cluster power states from SCP + ******************************************************************************/ +int css_node_hw_state(u_register_t mpidr, unsigned int power_level) +{ + int rc, element; + unsigned int cpu_state, cluster_state; + + /* + * The format of 'power_level' is implementation-defined, but 0 must + * mean a CPU. We also allow 1 to denote the cluster + */ + if (power_level != ARM_PWR_LVL0 && power_level != ARM_PWR_LVL1) + return PSCI_E_INVALID_PARAMS; + + /* Query SCP */ + rc = scpi_get_css_power_state(mpidr, &cpu_state, &cluster_state); + if (rc != 0) + return PSCI_E_INVALID_PARAMS; + + /* Map power states of CPU and cluster to expected PSCI return codes */ + if (power_level == ARM_PWR_LVL0) { + /* + * The CPU state returned by SCP is an 8-bit bit mask + * corresponding to each CPU in the cluster + */ + element = mpidr & MPIDR_AFFLVL_MASK; + return CSS_CPU_PWR_STATE(cpu_state, element) == + CSS_CPU_PWR_STATE_ON ? HW_ON : HW_OFF; + } else { + assert(cluster_state == CSS_CLUSTER_PWR_STATE_ON || + cluster_state == CSS_CLUSTER_PWR_STATE_OFF); + return cluster_state == CSS_CLUSTER_PWR_STATE_ON ? HW_ON : + HW_OFF; + } +} + +/******************************************************************************* * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard * platform will take care of registering the handlers with PSCI. ******************************************************************************/ @@ -312,5 +349,6 @@ const plat_psci_ops_t plat_arm_psci_pm_ops = { .system_off = css_system_off, .system_reset = css_system_reset, .validate_power_state = arm_validate_power_state, - .validate_ns_entrypoint = arm_validate_ns_entrypoint + .validate_ns_entrypoint = arm_validate_ns_entrypoint, + .get_node_hw_state = css_node_hw_state }; diff --git a/plat/arm/css/common/css_scpi.c b/plat/arm/css/common/css_scpi.c index 02d573c9..90a8939d 100644 --- a/plat/arm/css/common/css_scpi.c +++ b/plat/arm/css/common/css_scpi.c @@ -41,11 +41,18 @@ #define SCPI_SHARED_MEM_AP_TO_SCP (PLAT_CSS_SCP_COM_SHARED_MEM_BASE \ + 0x100) +/* Header and payload addresses for commands from AP to SCP */ #define SCPI_CMD_HEADER_AP_TO_SCP \ ((scpi_cmd_t *) SCPI_SHARED_MEM_AP_TO_SCP) #define SCPI_CMD_PAYLOAD_AP_TO_SCP \ ((void *) (SCPI_SHARED_MEM_AP_TO_SCP + sizeof(scpi_cmd_t))) +/* Header and payload addresses for responses from SCP to AP */ +#define SCPI_RES_HEADER_SCP_TO_AP \ + ((scpi_cmd_t *) SCPI_SHARED_MEM_SCP_TO_AP) +#define SCPI_RES_PAYLOAD_SCP_TO_AP \ + ((void *) (SCPI_SHARED_MEM_SCP_TO_AP + sizeof(scpi_cmd_t))) + /* ID of the MHU slot used for the SCPI protocol */ #define SCPI_MHU_SLOT_ID 0 @@ -163,6 +170,68 @@ void scpi_set_css_power_state(unsigned mpidr, scpi_power_state_t cpu_state, scpi_secure_message_end(); } +/* + * Query and obtain CSS power state from SCP. + * + * In response to the query, SCP returns power states of all CPUs in all + * clusters of the system. The returned response is then filtered based on the + * supplied MPIDR. Power states of requested cluster and CPUs within are updated + * via. supplied non-NULL pointer arguments. + * + * Returns 0 on success, or -1 on errors. + */ +int scpi_get_css_power_state(unsigned int mpidr, unsigned int *cpu_state_p, + unsigned int *cluster_state_p) +{ + scpi_cmd_t *cmd; + scpi_cmd_t response; + int power_state, cpu, cluster, rc = -1; + + /* + * Extract CPU and cluster membership of the given MPIDR. SCPI caters + * for only up to 0xf clusters, and 8 CPUs per cluster + */ + cpu = mpidr & MPIDR_AFFLVL_MASK; + cluster = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; + if (cpu >= 8 || cluster >= 0xf) + return -1; + + scpi_secure_message_start(); + + /* Populate request headers */ + cmd = memset(SCPI_CMD_HEADER_AP_TO_SCP, 0, sizeof(*cmd)); + cmd->id = SCPI_CMD_GET_CSS_POWER_STATE; + + /* + * Send message and wait for SCP's response + */ + scpi_secure_message_send(0); + scpi_secure_message_receive(&response); + + if (response.status != SCP_OK) + goto exit; + + /* Validate SCP response */ + if (!CHECK_RESPONSE(response, cluster)) + goto exit; + + /* Extract power states for required cluster */ + power_state = *(((uint16_t *) SCPI_RES_PAYLOAD_SCP_TO_AP) + cluster); + if (CLUSTER_ID(power_state) != cluster) + goto exit; + + /* Update power state via. pointers */ + if (cluster_state_p) + *cluster_state_p = CLUSTER_POWER_STATE(power_state); + if (cpu_state_p) + *cpu_state_p = CPU_POWER_STATE(power_state); + rc = 0; + +exit: + scpi_secure_message_end(); + return rc; +} + uint32_t scpi_sys_power_state(scpi_system_state_t system_state) { scpi_cmd_t *cmd; diff --git a/plat/arm/css/common/css_scpi.h b/plat/arm/css/common/css_scpi.h index 4a601f3e..1fb55e4d 100644 --- a/plat/arm/css/common/css_scpi.h +++ b/plat/arm/css/common/css_scpi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2016, ARM Limited and Contributors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -81,9 +81,34 @@ typedef uint32_t scpi_status_t; typedef enum { SCPI_CMD_SCP_READY = 0x01, SCPI_CMD_SET_CSS_POWER_STATE = 0x03, + SCPI_CMD_GET_CSS_POWER_STATE = 0x04, SCPI_CMD_SYS_POWER_STATE = 0x05 } scpi_command_t; +/* + * Macros to parse SCP response to GET_CSS_POWER_STATE command + * + * [3:0] : cluster ID + * [7:4] : cluster state: 0 = on; 3 = off; rest are reserved + * [15:8]: on/off state for individual CPUs in the cluster + * + * Payload is in little-endian + */ +#define CLUSTER_ID(_resp) ((_resp) & 0xf) +#define CLUSTER_POWER_STATE(_resp) (((_resp) >> 4) & 0xf) + +/* Result is a bit mask of CPU on/off states in the cluster */ +#define CPU_POWER_STATE(_resp) (((_resp) >> 8) & 0xff) + +/* + * For GET_CSS_POWER_STATE, SCP returns the power states of every cluster. The + * size of response depends on the number of clusters in the system. The + * SCP-to-AP payload contains 2 bytes per cluster. Make sure the response is + * large enough to contain power states of a given cluster + */ +#define CHECK_RESPONSE(_resp, _clus) \ + (_resp.size >= (((_clus) + 1) * 2)) + typedef enum { scpi_power_on = 0, scpi_power_retention = 1, @@ -101,6 +126,8 @@ extern void scpi_set_css_power_state(unsigned mpidr, scpi_power_state_t cpu_state, scpi_power_state_t cluster_state, scpi_power_state_t css_state); +int scpi_get_css_power_state(unsigned int mpidr, unsigned int *cpu_state_p, + unsigned int *cluster_state_p); uint32_t scpi_sys_power_state(scpi_system_state_t system_state); diff --git a/plat/rockchip/common/include/plat_params.h b/plat/rockchip/common/include/plat_params.h index cad45353..8c647968 100644 --- a/plat/rockchip/common/include/plat_params.h +++ b/plat/rockchip/common/include/plat_params.h @@ -63,11 +63,31 @@ * alignment fault will occur during accessing its data member. */ +#define BL31_GPIO_DIR_OUT 0 +#define BL31_GPIO_DIR_IN 1 + +#define BL31_GPIO_LEVEL_LOW 0 +#define BL31_GPIO_LEVEL_HIGH 1 + +#define BL31_GPIO_PULL_NONE 0 +#define BL31_GPIO_PULL_UP 1 +#define BL31_GPIO_PULL_DOWN 2 + /* param type */ enum { PARAM_NONE = 0, PARAM_RESET, PARAM_POWEROFF, + PARAM_SUSPEND_GPIO, + PARAM_SUSPEND_APIO, +}; + +struct apio_info { + uint8_t apio1 : 1; + uint8_t apio2 : 1; + uint8_t apio3 : 1; + uint8_t apio4 : 1; + uint8_t apio5 : 1; }; struct gpio_info { @@ -88,4 +108,9 @@ struct bl31_gpio_param { struct gpio_info gpio; }; +struct bl31_apio_param { + struct bl31_plat_param h; + struct apio_info apio; +}; + #endif /* __PLAT_PARAMS_H__ */ diff --git a/plat/rockchip/common/include/plat_private.h b/plat/rockchip/common/include/plat_private.h index b9b634e7..ad012666 100644 --- a/plat/rockchip/common/include/plat_private.h +++ b/plat/rockchip/common/include/plat_private.h @@ -121,8 +121,10 @@ uintptr_t plat_get_sec_entrypoint(void); void platform_cpu_warmboot(void); -void *plat_get_rockchip_gpio_reset(void); -void *plat_get_rockchip_gpio_poweroff(void); +struct gpio_info *plat_get_rockchip_gpio_reset(void); +struct gpio_info *plat_get_rockchip_gpio_poweroff(void); +struct gpio_info *plat_get_rockchip_suspend_gpio(uint32_t *count); +struct apio_info *plat_get_rockchip_suspend_apio(void); void plat_rockchip_gpio_init(void); extern const unsigned char rockchip_power_domain_tree_desc[]; diff --git a/plat/rockchip/common/params_setup.c b/plat/rockchip/common/params_setup.c index 2a49556f..646c1e13 100644 --- a/plat/rockchip/common/params_setup.c +++ b/plat/rockchip/common/params_setup.c @@ -40,25 +40,39 @@ #include <plat_private.h> #include <string.h> -static struct bl31_plat_param *bl31_params_head; -static struct bl31_gpio_param param_reset; -static struct bl31_gpio_param param_poweroff; +static struct gpio_info param_reset; +static struct gpio_info param_poweroff; +static struct bl31_apio_param param_apio; static struct gpio_info *rst_gpio; static struct gpio_info *poweroff_gpio; +static struct gpio_info suspend_gpio[10]; +uint32_t suspend_gpio_cnt; +static struct apio_info *suspend_apio; -void *plat_get_rockchip_gpio_reset(void) +struct gpio_info *plat_get_rockchip_gpio_reset(void) { return rst_gpio; } -void *plat_get_rockchip_gpio_poweroff(void) +struct gpio_info *plat_get_rockchip_gpio_poweroff(void) { return poweroff_gpio; } +struct gpio_info *plat_get_rockchip_suspend_gpio(uint32_t *count) +{ + *count = suspend_gpio_cnt; + + return &suspend_gpio[0]; +} + +struct apio_info *plat_get_rockchip_suspend_apio(void) +{ + return suspend_apio; +} + void params_early_setup(void *plat_param_from_bl2) { - struct bl31_plat_param *param; struct bl31_plat_param *bl2_param; struct bl31_gpio_param *gpio_param; @@ -67,25 +81,38 @@ void params_early_setup(void *plat_param_from_bl2) while (bl2_param) { switch (bl2_param->type) { case PARAM_RESET: - param = (struct bl31_plat_param *)¶m_reset; - memcpy((void *)param, (void *)bl2_param, - sizeof(struct bl31_gpio_param)); - gpio_param = (struct bl31_gpio_param *)param; - rst_gpio = &gpio_param->gpio; + gpio_param = (struct bl31_gpio_param *)bl2_param; + memcpy(¶m_reset, &gpio_param->gpio, + sizeof(struct gpio_info)); + rst_gpio = ¶m_reset; break; case PARAM_POWEROFF: - param = (struct bl31_plat_param *)¶m_poweroff; - memcpy((void *)param, (void *)bl2_param, - sizeof(struct bl31_gpio_param)); - gpio_param = (struct bl31_gpio_param *)param; - poweroff_gpio = &gpio_param->gpio; + gpio_param = (struct bl31_gpio_param *)bl2_param; + memcpy(¶m_poweroff, &gpio_param->gpio, + sizeof(struct gpio_info)); + poweroff_gpio = ¶m_poweroff; + break; + case PARAM_SUSPEND_GPIO: + if (suspend_gpio_cnt >= ARRAY_SIZE(suspend_gpio)) { + ERROR("exceed support suspend gpio number\n"); + break; + } + gpio_param = (struct bl31_gpio_param *)bl2_param; + memcpy(&suspend_gpio[suspend_gpio_cnt], + &gpio_param->gpio, + sizeof(struct gpio_info)); + suspend_gpio_cnt++; + break; + case PARAM_SUSPEND_APIO: + memcpy(¶m_apio, bl2_param, + sizeof(struct bl31_apio_param)); + suspend_apio = ¶m_apio.apio; break; default: - NOTICE("not expected type found\n"); - return; /* don't continue if unexpected type found */ + ERROR("not expected type found %ld\n", + bl2_param->type); + break; } - param->next = bl31_params_head; - bl31_params_head = param; bl2_param = bl2_param->next; } } diff --git a/plat/rockchip/common/plat_pm.c b/plat/rockchip/common/plat_pm.c index 3c2c79a6..d28100d9 100755..100644 --- a/plat/rockchip/common/plat_pm.c +++ b/plat/rockchip/common/plat_pm.c @@ -283,9 +283,10 @@ void rockchip_pwr_domain_suspend_finish(const psci_power_state_t *target_state) rockchip_ops->cores_pwr_dm_resume(); /* * Program the gic per-cpu distributor or re-distributor interface. - * For sys power domain operation, resuming of the gic needs to operate in - * rockchip_ops->sys_pwr_dm_resume, according to the sys power mode implements. - */ + * For sys power domain operation, resuming of the gic needs to operate + * in rockchip_ops->sys_pwr_dm_resume, according to the sys power mode + * implements. + */ plat_rockchip_gic_cpuif_enable(); comm_finish: diff --git a/plat/rockchip/rk3399/drivers/dram/dram.c b/plat/rockchip/rk3399/drivers/dram/dram.c index ddae84d3..94aa0766 100644 --- a/plat/rockchip/rk3399/drivers/dram/dram.c +++ b/plat/rockchip/rk3399/drivers/dram/dram.c @@ -2147,7 +2147,7 @@ static int to_get_clk_index(unsigned int mhz) { int pll_cnt, i; - pll_cnt = sizeof(dpll_rates_table) / sizeof(struct pll_div); + pll_cnt = ARRAY_SIZE(dpll_rates_table); /* Assumming rate_table is in descending order */ for (i = 0; i < pll_cnt; i++) { @@ -2155,6 +2155,10 @@ static int to_get_clk_index(unsigned int mhz) break; } + /* if mhz lower than lowest frequency in table, use lowest frequency */ + if (i == pll_cnt) + i = pll_cnt - 1; + return i; } @@ -2174,7 +2178,7 @@ uint32_t rkclk_prepare_pll_timing(unsigned int mhz) return (24 * fbdiv) / refdiv / postdiv1 / postdiv2; } -uint64_t ddr_get_rate(void) +uint32_t ddr_get_rate(void) { uint32_t refdiv, postdiv1, fbdiv, postdiv2; @@ -2464,7 +2468,6 @@ static uint32_t prepare_ddr_timing(uint32_t mhz) * target freq. */ dram_get_parameter(&rk3399_dram_status.timing_config, &dram_timing); - gen_rk3399_ctl_params(&rk3399_dram_status.timing_config, &dram_timing, index); gen_rk3399_pi_params(&rk3399_dram_status.timing_config, @@ -2494,7 +2497,7 @@ void print_dram_status_info(void) tf_printf("%u\n", p[i]); } -uint64_t ddr_set_rate(uint64_t hz) +uint32_t ddr_set_rate(uint32_t hz) { uint32_t low_power, index; uint32_t mhz = hz / (1000 * 1000); @@ -2503,13 +2506,13 @@ uint64_t ddr_set_rate(uint64_t hz) rk3399_dram_status.index_freq[rk3399_dram_status.current_index]) goto out; + index = to_get_clk_index(mhz); + mhz = dpll_rates_table[index].mhz; + low_power = exit_low_power(); index = prepare_ddr_timing(mhz); - if (index > 1) { - /* set timing error, quit */ - mhz = 0; + if (index > 1) goto out; - } dcf_start(mhz, index); wait_dcf_done(); @@ -2526,7 +2529,7 @@ out: return mhz; } -uint64_t ddr_round_rate(uint64_t hz) +uint32_t ddr_round_rate(uint32_t hz) { int index; uint32_t mhz = hz / (1000 * 1000); @@ -2536,7 +2539,7 @@ uint64_t ddr_round_rate(uint64_t hz) return dpll_rates_table[index].mhz * 1000 * 1000; } -uint64_t dts_timing_receive(uint64_t timing, uint64_t index) +uint32_t dts_timing_receive(uint32_t timing, uint32_t index) { uint32_t *p = (uint32_t *) &dts_parameter; static uint32_t receive_nums; diff --git a/plat/rockchip/rk3399/drivers/dram/dram.h b/plat/rockchip/rk3399/drivers/dram/dram.h index 62c5170a..4f990704 100644 --- a/plat/rockchip/rk3399/drivers/dram/dram.h +++ b/plat/rockchip/rk3399/drivers/dram/dram.h @@ -321,9 +321,9 @@ struct drv_odt_lp_config { #define DDR_RESTORE_SP(save_sp) ddr_save_sp(save_sp) void ddr_init(void); -uint64_t ddr_set_rate(uint64_t hz); -uint64_t ddr_round_rate(uint64_t hz); -uint64_t ddr_get_rate(void); +uint32_t ddr_set_rate(uint32_t hz); +uint32_t ddr_round_rate(uint32_t hz); +uint32_t ddr_get_rate(void); void clr_dcf_irq(void); -uint64_t dts_timing_receive(uint64_t timing, uint64_t index); +uint32_t dts_timing_receive(uint32_t timing, uint32_t index); #endif diff --git a/plat/rockchip/rk3399/drivers/gpio/rk3399_gpio.c b/plat/rockchip/rk3399/drivers/gpio/rk3399_gpio.c index eca9fbcc..4995d564 100644 --- a/plat/rockchip/rk3399/drivers/gpio/rk3399_gpio.c +++ b/plat/rockchip/rk3399/drivers/gpio/rk3399_gpio.c @@ -52,48 +52,105 @@ uint32_t gpio_port[] = { #define PMU_GPIO_PORT0 0 #define PMU_GPIO_PORT1 1 +#define GPIO_PORT2 2 +#define GPIO_PORT3 3 +#define GPIO_PORT4 4 #define PMU_GRF_GPIO0A_P 0x40 #define GRF_GPIO2A_P 0xe040 #define GPIO_P_MASK 0x03 -/* - * gpio clock disabled when not operate - * so need to enable gpio clock before operate gpio - * after setting, need to disable gpio clock - * gate 1: disable clock; 0: enable clock - */ -static void gpio_clk(int gpio, uint32_t gate) +#define GET_GPIO_PORT(pin) (pin / 32) +#define GET_GPIO_NUM(pin) (pin % 32) +#define GET_GPIO_BANK(pin) ((pin % 32) / 8) +#define GET_GPIO_ID(pin) ((pin % 32) % 8) + +/* returns old clock state, enables clock, in order to do GPIO access */ +static int gpio_get_clock(uint32_t gpio_number) { - uint32_t port = gpio / 32; + uint32_t port = GET_GPIO_PORT(gpio_number); + uint32_t clock_state = 0; assert(port < 5); switch (port) { - case 0: + case PMU_GPIO_PORT0: + clock_state = (mmio_read_32(PMUCRU_BASE + + CRU_PMU_CLKGATE_CON(1)) >> + PCLK_GPIO0_GATE_SHIFT) & 0x01; mmio_write_32(PMUCRU_BASE + CRU_PMU_CLKGATE_CON(1), - BITS_WITH_WMASK(gate, CLK_GATE_MASK, + BITS_WITH_WMASK(0, CLK_GATE_MASK, PCLK_GPIO0_GATE_SHIFT)); break; - case 1: + case PMU_GPIO_PORT1: + clock_state = (mmio_read_32(PMUCRU_BASE + + CRU_PMU_CLKGATE_CON(1)) >> + PCLK_GPIO1_GATE_SHIFT) & 0x01; mmio_write_32(PMUCRU_BASE + CRU_PMU_CLKGATE_CON(1), - BITS_WITH_WMASK(gate, CLK_GATE_MASK, + BITS_WITH_WMASK(0, CLK_GATE_MASK, PCLK_GPIO1_GATE_SHIFT)); break; - case 2: + case GPIO_PORT2: + clock_state = (mmio_read_32(CRU_BASE + + CRU_CLKGATE_CON(31)) >> + PCLK_GPIO2_GATE_SHIFT) & 0x01; mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), - BITS_WITH_WMASK(gate, CLK_GATE_MASK, + BITS_WITH_WMASK(0, CLK_GATE_MASK, PCLK_GPIO2_GATE_SHIFT)); break; - case 3: + case GPIO_PORT3: + clock_state = (mmio_read_32(CRU_BASE + + CRU_CLKGATE_CON(31)) >> + PCLK_GPIO3_GATE_SHIFT) & 0x01; mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), - BITS_WITH_WMASK(gate, CLK_GATE_MASK, + BITS_WITH_WMASK(0, CLK_GATE_MASK, PCLK_GPIO3_GATE_SHIFT)); + break; + case GPIO_PORT4: + clock_state = (mmio_read_32(CRU_BASE + + CRU_CLKGATE_CON(31)) >> + PCLK_GPIO4_GATE_SHIFT) & 0x01; + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), + BITS_WITH_WMASK(0, CLK_GATE_MASK, + PCLK_GPIO4_GATE_SHIFT)); + break; + default: + break; + } + return clock_state; +} + +/* restores old state of gpio clock */ +void gpio_put_clock(uint32_t gpio_number, uint32_t clock_state) +{ + uint32_t port = GET_GPIO_PORT(gpio_number); + + switch (port) { + case PMU_GPIO_PORT0: + mmio_write_32(PMUCRU_BASE + CRU_PMU_CLKGATE_CON(1), + BITS_WITH_WMASK(clock_state, CLK_GATE_MASK, + PCLK_GPIO0_GATE_SHIFT)); + break; + case PMU_GPIO_PORT1: + mmio_write_32(PMUCRU_BASE + CRU_PMU_CLKGATE_CON(1), + BITS_WITH_WMASK(clock_state, CLK_GATE_MASK, + PCLK_GPIO1_GATE_SHIFT)); + break; + case GPIO_PORT2: + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), + BITS_WITH_WMASK(clock_state, CLK_GATE_MASK, + PCLK_GPIO2_GATE_SHIFT)); break; - case 4: + case GPIO_PORT3: mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), - BITS_WITH_WMASK(gate, CLK_GATE_MASK, + BITS_WITH_WMASK(clock_state, CLK_GATE_MASK, + PCLK_GPIO3_GATE_SHIFT)); + + break; + case GPIO_PORT4: + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), + BITS_WITH_WMASK(clock_state, CLK_GATE_MASK, PCLK_GPIO4_GATE_SHIFT)); break; default: @@ -101,16 +158,58 @@ static void gpio_clk(int gpio, uint32_t gate) } } +static int get_pull(int gpio) +{ + uint32_t port = GET_GPIO_PORT(gpio); + uint32_t bank = GET_GPIO_BANK(gpio); + uint32_t id = GET_GPIO_ID(gpio); + uint32_t val, clock_state; + + assert((port < 5) && (bank < 4)); + + clock_state = gpio_get_clock(gpio); + + if (port == PMU_GPIO_PORT0 || port == PMU_GPIO_PORT1) { + val = mmio_read_32(PMUGRF_BASE + PMU_GRF_GPIO0A_P + + port * 16 + bank * 4); + val = (val >> (id * 2)) & GPIO_P_MASK; + } else { + val = mmio_read_32(GRF_BASE + GRF_GPIO2A_P + + (port - 2) * 16 + bank * 4); + val = (val >> (id * 2)) & GPIO_P_MASK; + } + gpio_put_clock(gpio, clock_state); + + /* + * in gpio0a, gpio0b, gpio2c, gpio2d, + * 00: Z + * 01: pull down + * 10: Z + * 11: pull up + * different with other gpio, so need to correct it + */ + if (((port == 0) && (bank < 2)) || ((port == 2) && (bank > 1))) { + if (val == 3) + val = GPIO_PULL_UP; + else if (val == 1) + val = GPIO_PULL_DOWN; + else + val = 0; + } + + return val; +} + static void set_pull(int gpio, int pull) { - uint32_t port = gpio / 32; - uint32_t num = gpio % 32; - uint32_t bank = num / 8; - uint32_t id = num % 8; + uint32_t port = GET_GPIO_PORT(gpio); + uint32_t bank = GET_GPIO_BANK(gpio); + uint32_t id = GET_GPIO_ID(gpio); + uint32_t clock_state; - assert((port < 5) && (num < 32)); + assert((port < 5) && (bank < 4)); - gpio_clk(gpio, 0); + clock_state = gpio_get_clock(gpio); /* * in gpio0a, gpio0b, gpio2c, gpio2d, @@ -120,7 +219,7 @@ static void set_pull(int gpio, int pull) * 11: pull up * different with other gpio, so need to correct it */ - if (((port == 0) && (bank < 2)) || ((port == 2) && (bank > 2))) { + if (((port == 0) && (bank < 2)) || ((port == 2) && (bank > 1))) { if (pull == GPIO_PULL_UP) pull = 3; else if (pull == GPIO_PULL_DOWN) @@ -138,17 +237,18 @@ static void set_pull(int gpio, int pull) (port - 2) * 16 + bank * 4, BITS_WITH_WMASK(pull, GPIO_P_MASK, id * 2)); } - gpio_clk(gpio, 1); + gpio_put_clock(gpio, clock_state); } static void set_direction(int gpio, int direction) { - uint32_t port = gpio / 32; - uint32_t num = gpio % 32; + uint32_t port = GET_GPIO_PORT(gpio); + uint32_t num = GET_GPIO_NUM(gpio); + uint32_t clock_state; assert((port < 5) && (num < 32)); - gpio_clk(gpio, 0); + clock_state = gpio_get_clock(gpio); /* * in gpio.h @@ -158,18 +258,18 @@ static void set_direction(int gpio, int direction) * so need to revert direction value */ mmio_setbits_32(gpio_port[port] + SWPORTA_DDR, !direction << num); - gpio_clk(gpio, 1); + gpio_put_clock(gpio, clock_state); } static int get_direction(int gpio) { - uint32_t port = gpio / 32; - uint32_t num = gpio % 32; - int direction; + uint32_t port = GET_GPIO_PORT(gpio); + uint32_t num = GET_GPIO_NUM(gpio); + int direction, clock_state; assert((port < 5) && (num < 32)); - gpio_clk(gpio, 0); + clock_state = gpio_get_clock(gpio); /* * in gpio.h @@ -180,37 +280,38 @@ static int get_direction(int gpio) */ direction = !((mmio_read_32(gpio_port[port] + SWPORTA_DDR) >> num) & 0x1); - gpio_clk(gpio, 1); + gpio_put_clock(gpio, clock_state); return direction; } static int get_value(int gpio) { - uint32_t port = gpio / 32; - uint32_t num = gpio % 32; - int value; + uint32_t port = GET_GPIO_PORT(gpio); + uint32_t num = GET_GPIO_NUM(gpio); + int value, clock_state; assert((port < 5) && (num < 32)); - gpio_clk(gpio, 0); + clock_state = gpio_get_clock(gpio); value = (mmio_read_32(gpio_port[port] + EXT_PORTA) >> num) & 0x1; - gpio_clk(gpio, 1); + gpio_put_clock(gpio, clock_state); return value; } static void set_value(int gpio, int value) { - uint32_t port = gpio / 32; - uint32_t num = gpio % 32; + uint32_t port = GET_GPIO_PORT(gpio); + uint32_t num = GET_GPIO_NUM(gpio); + uint32_t clock_state; assert((port < 5) && (num < 32)); - gpio_clk(gpio, 0); + clock_state = gpio_get_clock(gpio); mmio_clrsetbits_32(gpio_port[port] + SWPORTA_DR, 1 << num, !!value << num); - gpio_clk(gpio, 0); + gpio_put_clock(gpio, clock_state); } const gpio_ops_t rk3399_gpio_ops = { @@ -219,6 +320,7 @@ const gpio_ops_t rk3399_gpio_ops = { .get_value = get_value, .set_value = set_value, .set_pull = set_pull, + .get_pull = get_pull, }; void plat_rockchip_gpio_init(void) diff --git a/plat/rockchip/rk3399/drivers/pmu/pmu.c b/plat/rockchip/rk3399/drivers/pmu/pmu.c index 50953d83..07a5b1e7 100755..100644 --- a/plat/rockchip/rk3399/drivers/pmu/pmu.c +++ b/plat/rockchip/rk3399/drivers/pmu/pmu.c @@ -874,6 +874,190 @@ static void clr_hw_idle(uint32_t hw_idle) mmio_clrbits_32(PMU_BASE + PMU_BUS_CLR, hw_idle); } +static uint32_t iomux_status[12]; +static uint32_t pull_mode_status[12]; +static uint32_t gpio_direction[3]; +static uint32_t gpio_2_4_clk_gate; + +static void suspend_apio(void) +{ + struct apio_info *suspend_apio; + int i; + + suspend_apio = plat_get_rockchip_suspend_apio(); + + if (!suspend_apio) + return; + + /* save gpio2 ~ gpio4 iomux and pull mode */ + for (i = 0; i < 12; i++) { + iomux_status[i] = mmio_read_32(GRF_BASE + + GRF_GPIO2A_IOMUX + i * 4); + pull_mode_status[i] = mmio_read_32(GRF_BASE + + GRF_GPIO2A_P + i * 4); + } + + /* store gpio2 ~ gpio4 clock gate state */ + gpio_2_4_clk_gate = (mmio_read_32(CRU_BASE + CRU_CLKGATE_CON(31)) >> + PCLK_GPIO2_GATE_SHIFT) & 0x07; + + /* enable gpio2 ~ gpio4 clock gate */ + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), + BITS_WITH_WMASK(0, 0x07, PCLK_GPIO2_GATE_SHIFT)); + + /* save gpio2 ~ gpio4 direction */ + gpio_direction[0] = mmio_read_32(GPIO2_BASE + 0x04); + gpio_direction[1] = mmio_read_32(GPIO3_BASE + 0x04); + gpio_direction[2] = mmio_read_32(GPIO4_BASE + 0x04); + + /* apio1 charge gpio3a0 ~ gpio3c7 */ + if (suspend_apio->apio1) { + + /* set gpio3a0 ~ gpio3c7 iomux to gpio */ + mmio_write_32(GRF_BASE + GRF_GPIO3A_IOMUX, + REG_SOC_WMSK | GRF_IOMUX_GPIO); + mmio_write_32(GRF_BASE + GRF_GPIO3B_IOMUX, + REG_SOC_WMSK | GRF_IOMUX_GPIO); + mmio_write_32(GRF_BASE + GRF_GPIO3C_IOMUX, + REG_SOC_WMSK | GRF_IOMUX_GPIO); + + /* set gpio3a0 ~ gpio3c7 pull mode to pull none */ + mmio_write_32(GRF_BASE + GRF_GPIO3A_P, REG_SOC_WMSK | 0); + mmio_write_32(GRF_BASE + GRF_GPIO3B_P, REG_SOC_WMSK | 0); + mmio_write_32(GRF_BASE + GRF_GPIO3C_P, REG_SOC_WMSK | 0); + + /* set gpio3a0 ~ gpio3c7 to input */ + mmio_clrbits_32(GPIO3_BASE + 0x04, 0x00ffffff); + } + + /* apio2 charge gpio2a0 ~ gpio2b4 */ + if (suspend_apio->apio2) { + + /* set gpio2a0 ~ gpio2b4 iomux to gpio */ + mmio_write_32(GRF_BASE + GRF_GPIO2A_IOMUX, + REG_SOC_WMSK | GRF_IOMUX_GPIO); + mmio_write_32(GRF_BASE + GRF_GPIO2B_IOMUX, + REG_SOC_WMSK | GRF_IOMUX_GPIO); + + /* set gpio2a0 ~ gpio2b4 pull mode to pull none */ + mmio_write_32(GRF_BASE + GRF_GPIO2A_P, REG_SOC_WMSK | 0); + mmio_write_32(GRF_BASE + GRF_GPIO2B_P, REG_SOC_WMSK | 0); + + /* set gpio2a0 ~ gpio2b4 to input */ + mmio_clrbits_32(GPIO2_BASE + 0x04, 0x00001fff); + } + + /* apio3 charge gpio2c0 ~ gpio2d4*/ + if (suspend_apio->apio3) { + + /* set gpio2a0 ~ gpio2b4 iomux to gpio */ + mmio_write_32(GRF_BASE + GRF_GPIO2C_IOMUX, + REG_SOC_WMSK | GRF_IOMUX_GPIO); + mmio_write_32(GRF_BASE + GRF_GPIO2D_IOMUX, + REG_SOC_WMSK | GRF_IOMUX_GPIO); + + /* set gpio2c0 ~ gpio2d4 pull mode to pull none */ + mmio_write_32(GRF_BASE + GRF_GPIO2C_P, REG_SOC_WMSK | 0); + mmio_write_32(GRF_BASE + GRF_GPIO2D_P, REG_SOC_WMSK | 0); + + /* set gpio2c0 ~ gpio2d4 to input */ + mmio_clrbits_32(GPIO2_BASE + 0x04, 0x1fff0000); + } + + /* apio4 charge gpio4c0 ~ gpio4c7, gpio4d0 ~ gpio4d6 */ + if (suspend_apio->apio4) { + + /* set gpio4c0 ~ gpio4d6 iomux to gpio */ + mmio_write_32(GRF_BASE + GRF_GPIO4C_IOMUX, + REG_SOC_WMSK | GRF_IOMUX_GPIO); + mmio_write_32(GRF_BASE + GRF_GPIO4D_IOMUX, + REG_SOC_WMSK | GRF_IOMUX_GPIO); + + /* set gpio4c0 ~ gpio4d6 pull mode to pull none */ + mmio_write_32(GRF_BASE + GRF_GPIO4C_P, REG_SOC_WMSK | 0); + mmio_write_32(GRF_BASE + GRF_GPIO4D_P, REG_SOC_WMSK | 0); + + /* set gpio4c0 ~ gpio4d6 to input */ + mmio_clrbits_32(GPIO4_BASE + 0x04, 0x7fff0000); + } + + /* apio5 charge gpio3d0 ~ gpio3d7, gpio4a0 ~ gpio4a7*/ + if (suspend_apio->apio5) { + /* set gpio3d0 ~ gpio4a7 iomux to gpio */ + mmio_write_32(GRF_BASE + GRF_GPIO3D_IOMUX, + REG_SOC_WMSK | GRF_IOMUX_GPIO); + mmio_write_32(GRF_BASE + GRF_GPIO4A_IOMUX, + REG_SOC_WMSK | GRF_IOMUX_GPIO); + + /* set gpio3d0 ~ gpio4a7 pull mode to pull none */ + mmio_write_32(GRF_BASE + GRF_GPIO3D_P, REG_SOC_WMSK | 0); + mmio_write_32(GRF_BASE + GRF_GPIO4A_P, REG_SOC_WMSK | 0); + + /* set gpio4c0 ~ gpio4d6 to input */ + mmio_clrbits_32(GPIO3_BASE + 0x04, 0xff000000); + mmio_clrbits_32(GPIO4_BASE + 0x04, 0x000000ff); + } +} + +static void resume_apio(void) +{ + struct apio_info *suspend_apio; + int i; + + suspend_apio = plat_get_rockchip_suspend_apio(); + + if (!suspend_apio) + return; + + for (i = 0; i < 12; i++) { + mmio_write_32(GRF_BASE + GRF_GPIO2A_P + i * 4, + REG_SOC_WMSK | pull_mode_status[i]); + mmio_write_32(GRF_BASE + GRF_GPIO2A_IOMUX + i * 4, + REG_SOC_WMSK | iomux_status[i]); + } + + /* set gpio2 ~ gpio4 direction back to store value */ + mmio_write_32(GPIO2_BASE + 0x04, gpio_direction[0]); + mmio_write_32(GPIO3_BASE + 0x04, gpio_direction[1]); + mmio_write_32(GPIO4_BASE + 0x04, gpio_direction[2]); + + /* set gpio2 ~ gpio4 clock gate back to store value */ + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), + BITS_WITH_WMASK(gpio_2_4_clk_gate, 0x07, + PCLK_GPIO2_GATE_SHIFT)); +} + +static void suspend_gpio(void) +{ + struct gpio_info *suspend_gpio; + uint32_t count; + int i; + + suspend_gpio = plat_get_rockchip_suspend_gpio(&count); + + for (i = 0; i < count; i++) { + gpio_set_value(suspend_gpio[i].index, suspend_gpio[i].polarity); + gpio_set_direction(suspend_gpio[i].index, GPIO_DIR_OUT); + udelay(1); + } +} + +static void resume_gpio(void) +{ + struct gpio_info *suspend_gpio; + uint32_t count; + int i; + + suspend_gpio = plat_get_rockchip_suspend_gpio(&count); + + for (i = count - 1; i >= 0; i--) { + gpio_set_value(suspend_gpio[i].index, + !suspend_gpio[i].polarity); + gpio_set_direction(suspend_gpio[i].index, GPIO_DIR_OUT); + udelay(1); + } +} + static int sys_pwr_domain_suspend(void) { uint32_t wait_cnt = 0; @@ -919,7 +1103,6 @@ static int sys_pwr_domain_suspend(void) } } mmio_setbits_32(PMU_BASE + PMU_PWRDN_CON, BIT(PMU_SCU_B_PWRDWN_EN)); - /* * Disabling PLLs/PWM/DVFS is approaching WFI which is * the last steps in suspend. @@ -928,6 +1111,8 @@ static int sys_pwr_domain_suspend(void) disable_dvfs_plls(); disable_pwms(); disable_nodvfs_plls(); + suspend_apio(); + suspend_gpio(); return 0; } @@ -937,6 +1122,8 @@ static int sys_pwr_domain_resume(void) uint32_t wait_cnt = 0; uint32_t status = 0; + resume_apio(); + resume_gpio(); enable_nodvfs_plls(); enable_pwms(); /* PWM regulators take time to come up; give 300us to be safe. */ @@ -952,7 +1139,6 @@ static int sys_pwr_domain_resume(void) * somewhere. */ mmio_write_32(PMU_BASE + PMU_WAKEUP_STATUS, 0xffffffff); - mmio_write_32(PMU_BASE + PMU_WKUP_CFG4, 0x00); mmio_write_32(SGRF_BASE + SGRF_SOC_CON0_1(1), @@ -1003,6 +1189,9 @@ static int sys_pwr_domain_resume(void) BIT(PMU_CLR_PERILP) | BIT(PMU_CLR_PMU) | BIT(PMU_CLR_GIC)); + + plat_rockchip_gic_cpuif_enable(); + return 0; } @@ -1010,7 +1199,7 @@ void __dead2 soc_soft_reset(void) { struct gpio_info *rst_gpio; - rst_gpio = (struct gpio_info *)plat_get_rockchip_gpio_reset(); + rst_gpio = plat_get_rockchip_gpio_reset(); if (rst_gpio) { gpio_set_direction(rst_gpio->index, GPIO_DIR_OUT); @@ -1027,7 +1216,7 @@ void __dead2 soc_system_off(void) { struct gpio_info *poweroff_gpio; - poweroff_gpio = (struct gpio_info *)plat_get_rockchip_gpio_poweroff(); + poweroff_gpio = plat_get_rockchip_gpio_poweroff(); if (poweroff_gpio) { /* diff --git a/plat/rockchip/rk3399/drivers/pmu/pmu.h b/plat/rockchip/rk3399/drivers/pmu/pmu.h index 65fe7dbe..ab2896b1 100644 --- a/plat/rockchip/rk3399/drivers/pmu/pmu.h +++ b/plat/rockchip/rk3399/drivers/pmu/pmu.h @@ -875,7 +875,32 @@ enum pmu_core_pwr_st { #define MAX_WAIT_COUNT 1000 #define GRF_SOC_CON4 0x0e210 -#define GRF_GPIO4C_IOMUX 0x0e028 + +#define GRF_GPIO2A_IOMUX 0xe000 +#define GRF_GPIO2B_IOMUX 0xe004 +#define GRF_GPIO2C_IOMUX 0xe008 +#define GRF_GPIO2D_IOMUX 0xe00c +#define GRF_GPIO3A_IOMUX 0xe010 +#define GRF_GPIO3B_IOMUX 0xe014 +#define GRF_GPIO3C_IOMUX 0xe018 +#define GRF_GPIO3D_IOMUX 0xe01c +#define GRF_GPIO4A_IOMUX 0xe020 +#define GRF_GPIO4B_IOMUX 0xe024 +#define GRF_GPIO4C_IOMUX 0xe028 +#define GRF_GPIO4D_IOMUX 0xe02c + +#define GRF_GPIO2A_P 0xe040 +#define GRF_GPIO2B_P 0xe044 +#define GRF_GPIO2C_P 0xe048 +#define GRF_GPIO2D_P 0xe04C +#define GRF_GPIO3A_P 0xe050 +#define GRF_GPIO3B_P 0xe054 +#define GRF_GPIO3C_P 0xe058 +#define GRF_GPIO3D_P 0xe05C +#define GRF_GPIO4A_P 0xe060 +#define GRF_GPIO4B_P 0xe064 +#define GRF_GPIO4C_P 0xe068 +#define GRF_GPIO4D_P 0xe06C #define PMUGRF_GPIO0A_SMT 0x0120 #define PMUGRF_SOC_CON0 0x0180 diff --git a/plat/rockchip/rk3399/plat_sip_calls.c b/plat/rockchip/rk3399/plat_sip_calls.c index 6069be2a..a20ee2d8 100644 --- a/plat/rockchip/rk3399/plat_sip_calls.c +++ b/plat/rockchip/rk3399/plat_sip_calls.c @@ -31,33 +31,33 @@ #include <runtime_svc.h> #include <dram.h> -#define RK_SIP_DDR_CFG64 0x82000008 -#define CONFIG_DRAM_INIT 0x00 -#define CONFIG_DRAM_SET_RATE 0x01 -#define CONFIG_DRAM_ROUND_RATE 0x02 -#define CONFIG_DRAM_SET_AT_SR 0x03 -#define CONFIG_DRAM_GET_BW 0x04 -#define CONFIG_DRAM_GET_RATE 0x05 -#define CONFIG_DRAM_CLR_IRQ 0x06 -#define CONFIG_DRAM_SET_PARAM 0x07 +#define RK_SIP_DDR_CFG 0x82000008 +#define DRAM_INIT 0x00 +#define DRAM_SET_RATE 0x01 +#define DRAM_ROUND_RATE 0x02 +#define DRAM_SET_AT_SR 0x03 +#define DRAM_GET_BW 0x04 +#define DRAM_GET_RATE 0x05 +#define DRAM_CLR_IRQ 0x06 +#define DRAM_SET_PARAM 0x07 -uint64_t ddr_smc_handler(uint64_t arg0, uint64_t arg1, uint64_t id) +uint32_t ddr_smc_handler(uint64_t arg0, uint64_t arg1, uint64_t id) { switch (id) { - case CONFIG_DRAM_INIT: + case DRAM_INIT: ddr_init(); break; - case CONFIG_DRAM_SET_RATE: - return ddr_set_rate(arg0); - case CONFIG_DRAM_ROUND_RATE: - return ddr_round_rate(arg0); - case CONFIG_DRAM_GET_RATE: + case DRAM_SET_RATE: + return ddr_set_rate((uint32_t)arg0); + case DRAM_ROUND_RATE: + return ddr_round_rate((uint32_t)arg0); + case DRAM_GET_RATE: return ddr_get_rate(); - case CONFIG_DRAM_CLR_IRQ: + case DRAM_CLR_IRQ: clr_dcf_irq(); break; - case CONFIG_DRAM_SET_PARAM: - dts_timing_receive(arg0, arg1); + case DRAM_SET_PARAM: + dts_timing_receive((uint32_t)arg0, (uint32_t)arg1); break; default: break; @@ -76,7 +76,7 @@ uint64_t rockchip_plat_sip_handler(uint32_t smc_fid, uint64_t flags) { switch (smc_fid) { - case RK_SIP_DDR_CFG64: + case RK_SIP_DDR_CFG: SMC_RET1(handle, ddr_smc_handler(x1, x2, x3)); default: ERROR("%s: unhandled SMC (0x%x)\n", __func__, smc_fid); diff --git a/plat/xilinx/zynqmp/aarch64/zynqmp_common.c b/plat/xilinx/zynqmp/aarch64/zynqmp_common.c index 1ba301de..60a16058 100644 --- a/plat/xilinx/zynqmp/aarch64/zynqmp_common.c +++ b/plat/xilinx/zynqmp/aarch64/zynqmp_common.c @@ -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) diff --git a/plat/xilinx/zynqmp/bl31_zynqmp_setup.c b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c index d878b86b..c05b094e 100644 --- a/plat/xilinx/zynqmp/bl31_zynqmp_setup.c +++ b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c @@ -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) diff --git a/plat/xilinx/zynqmp/include/platform_def.h b/plat/xilinx/zynqmp/include/platform_def.h index a35bd129..3c1a9e5c 100644 --- a/plat/xilinx/zynqmp/include/platform_def.h +++ b/plat/xilinx/zynqmp/include/platform_def.h @@ -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) diff --git a/plat/xilinx/zynqmp/plat_psci.c b/plat/xilinx/zynqmp/plat_psci.c index 56eb742a..55227ea9 100644 --- a/plat/xilinx/zynqmp/plat_psci.c +++ b/plat/xilinx/zynqmp/plat_psci.c @@ -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; } diff --git a/plat/xilinx/zynqmp/platform.mk b/plat/xilinx/zynqmp/platform.mk index 9bde5ff6..33c55a05 100644 --- a/plat/xilinx/zynqmp/platform.mk +++ b/plat/xilinx/zynqmp/platform.mk @@ -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 \ diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index eac7801b..e859ee39 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -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); +} diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h index 22bdb477..26d83e75 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h @@ -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_ */ diff --git a/plat/xilinx/zynqmp/pm_service/pm_client.c b/plat/xilinx/zynqmp/pm_service/pm_client.c index cf0d5f08..b77a1cf0 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_client.c +++ b/plat/xilinx/zynqmp/pm_service/pm_client.c @@ -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 - * - * 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. + * irq_to_pm_node - Get PM node ID corresponding to the interrupt number + * @irq: Interrupt number * - * Return: Returns status, either success or error+reason + * Return: PM node ID corresponding to the specified interrupt + */ +static enum pm_node_id irq_to_pm_node(unsigned int irq) +{ + assert(irq <= IRQ_MAX); + return irq_node_map[irq]; +} + +/** + * pm_client_set_wakeup_sources - Set all slaves with enabled interrupts as wake + * sources in the PMU firmware */ -enum pm_ret_status set_ocm_retention(void) +static void pm_client_set_wakeup_sources(void) { - enum pm_ret_status ret; - - /* OCM_BANK_0 will always be occupied */ - ret = pm_set_requirement(NODE_OCM_BANK_0, PM_CAP_CONTEXT, 0, - REQ_ACK_NO); - - /* 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); - - return ret; + uint32_t reg_num; + uint8_t pm_wakeup_nodes_set[NODE_MAX]; + uintptr_t isenabler1 = BASE_GICD_BASE + GICD_ISENABLER + 4; + + memset(&pm_wakeup_nodes_set, 0, sizeof(pm_wakeup_nodes_set)); + + 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); diff --git a/plat/xilinx/zynqmp/pm_service/pm_client.h b/plat/xilinx/zynqmp/pm_service/pm_client.h index 9483b0d1..7f80d5b4 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_client.h +++ b/plat/xilinx/zynqmp/pm_service/pm_client.h @@ -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); diff --git a/plat/xilinx/zynqmp/pm_service/pm_defs.h b/plat/xilinx/zynqmp/pm_service/pm_defs.h index adeec64b..aec335a5 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_defs.h +++ b/plat/xilinx/zynqmp/pm_service/pm_defs.h @@ -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 { diff --git a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c index e3c25c31..9c08ffb9 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c +++ b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c @@ -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); diff --git a/plat/xilinx/zynqmp/zynqmp_def.h b/plat/xilinx/zynqmp/zynqmp_def.h index 4bb332e0..65bc25f8 100644 --- a/plat/xilinx/zynqmp/zynqmp_def.h +++ b/plat/xilinx/zynqmp/zynqmp_def.h @@ -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__ */ diff --git a/plat/xilinx/zynqmp/zynqmp_private.h b/plat/xilinx/zynqmp/zynqmp_private.h index ddef37b8..abcdebc3 100644 --- a/plat/xilinx/zynqmp/zynqmp_private.h +++ b/plat/xilinx/zynqmp/zynqmp_private.h @@ -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, diff --git a/tools/fiptool/Makefile b/tools/fiptool/Makefile index 3bc372a2..df76a75e 100644 --- a/tools/fiptool/Makefile +++ b/tools/fiptool/Makefile @@ -44,6 +44,7 @@ ifeq (${DEBUG},1) else CFLAGS += -O2 endif +LDLIBS := -lcrypto ifeq (${V},0) Q := @ @@ -62,7 +63,7 @@ all: ${PROJECT} fip_create ${PROJECT}: ${OBJECTS} Makefile @echo " LD $@" - ${Q}${CC} ${OBJECTS} -o $@ + ${Q}${CC} ${OBJECTS} -o $@ ${LDLIBS} @${ECHO_BLANK_LINE} @echo "Built $@ successfully" @${ECHO_BLANK_LINE} diff --git a/tools/fiptool/fiptool.c b/tools/fiptool/fiptool.c index 68ddcf5a..b3f02f6c 100644 --- a/tools/fiptool/fiptool.c +++ b/tools/fiptool/fiptool.c @@ -42,6 +42,8 @@ #include <string.h> #include <unistd.h> +#include <openssl/sha.h> + #include "fiptool.h" #include "firmware_image_package.h" #include "tbbr_config.h" @@ -354,6 +356,14 @@ static void add_opt(struct option *opts, int idx, char *name, opts[idx].val = val; } +static void md_print(unsigned char *md, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + printf("%02x", md[i]); +} + static int info_cmd(int argc, char *argv[]) { image_t *image; @@ -363,7 +373,7 @@ static int info_cmd(int argc, char *argv[]) int i; if (argc != 2) - usage(); + info_usage(); argc--, argv++; parse_fip(argv[0], &toc_header); @@ -391,10 +401,16 @@ static int info_cmd(int argc, char *argv[]) (unsigned long long)image_offset, (unsigned long long)image_size); if (image->toc_entry != NULL) - printf(", cmdline=\"--%s\"\n", + printf(", cmdline=\"--%s\"", image->toc_entry->cmdline_name); - else - putchar('\n'); + if (verbose) { + unsigned char md[SHA256_DIGEST_LENGTH]; + + SHA256(image->buffer, image_size, md); + printf(", sha256="); + md_print(md, sizeof(md)); + } + putchar('\n'); image_offset += image_size; } @@ -405,6 +421,7 @@ static int info_cmd(int argc, char *argv[]) static void info_usage(void) { printf("fiptool info FIP_FILENAME\n"); + exit(1); } static int pack_images(char *filename, uint64_t toc_flags) @@ -538,7 +555,7 @@ static int create_cmd(int argc, char *argv[]) int i; if (argc < 2) - usage(); + create_usage(); i = fill_common_opts(opts, required_argument); add_opt(opts, i, "plat-toc-flags", required_argument, @@ -567,14 +584,14 @@ static int create_cmd(int argc, char *argv[]) parse_plat_toc_flags(optarg, &toc_flags); break; default: - usage(); + create_usage(); } } argc -= optind; argv += optind; if (argc == 0) - usage(); + create_usage(); update_fip(); @@ -595,6 +612,7 @@ static void create_usage(void) for (; toc_entry->cmdline_name != NULL; toc_entry++) printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, toc_entry->name); + exit(1); } static int update_cmd(int argc, char *argv[]) @@ -607,7 +625,7 @@ static int update_cmd(int argc, char *argv[]) int i; if (argc < 2) - usage(); + update_usage(); i = fill_common_opts(opts, required_argument); add_opt(opts, i, "out", required_argument, 'o'); @@ -642,14 +660,14 @@ static int update_cmd(int argc, char *argv[]) snprintf(outfile, sizeof(outfile), "%s", optarg); break; default: - usage(); + update_usage(); } } argc -= optind; argv += optind; if (argc == 0) - usage(); + update_usage(); if (outfile[0] == '\0') snprintf(outfile, sizeof(outfile), "%s", argv[0]); @@ -682,6 +700,7 @@ static void update_usage(void) for (; toc_entry->cmdline_name != NULL; toc_entry++) printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, toc_entry->name); + exit(1); } static int unpack_cmd(int argc, char *argv[]) @@ -694,7 +713,7 @@ static int unpack_cmd(int argc, char *argv[]) int i; if (argc < 2) - usage(); + unpack_usage(); i = fill_common_opts(opts, required_argument); add_opt(opts, i, "force", no_argument, 'f'); @@ -724,14 +743,14 @@ static int unpack_cmd(int argc, char *argv[]) snprintf(outdir, sizeof(outdir), "%s", optarg); break; default: - usage(); + unpack_usage(); } } argc -= optind; argv += optind; if (argc == 0) - usage(); + unpack_usage(); parse_fip(argv[0], NULL); @@ -806,6 +825,7 @@ static void unpack_usage(void) toc_entry->name); fputc('\n', stderr); printf("If no options are provided, all images will be unpacked.\n"); + exit(1); } static int remove_cmd(int argc, char *argv[]) @@ -818,7 +838,7 @@ static int remove_cmd(int argc, char *argv[]) int i; if (argc < 2) - usage(); + remove_usage(); i = fill_common_opts(opts, no_argument); add_opt(opts, i, "force", no_argument, 'f'); @@ -844,14 +864,14 @@ static int remove_cmd(int argc, char *argv[]) snprintf(outfile, sizeof(outfile), "%s", optarg); break; default: - usage(); + remove_usage(); } } argc -= optind; argv += optind; if (argc == 0) - usage(); + remove_usage(); if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag) log_errx("File %s already exists, use --force to overwrite it", @@ -896,6 +916,7 @@ static void remove_usage(void) for (; toc_entry->cmdline_name != NULL; toc_entry++) printf(" --%-16s\t%s\n", toc_entry->cmdline_name, toc_entry->name); + exit(1); } static int version_cmd(int argc, char *argv[]) @@ -912,6 +933,7 @@ static int version_cmd(int argc, char *argv[]) static void version_usage(void) { printf("fiptool version\n"); + exit(1); } static int help_cmd(int argc, char *argv[]) @@ -924,10 +946,8 @@ static int help_cmd(int argc, char *argv[]) for (i = 0; i < NELEM(cmds); i++) { if (strcmp(cmds[i].name, argv[0]) == 0 && - cmds[i].usage != NULL) { + cmds[i].usage != NULL) cmds[i].usage(); - break; - } } if (i == NELEM(cmds)) printf("No help for subcommand '%s'\n", argv[0]); |