diff options
Diffstat (limited to 'drivers')
32 files changed, 4260 insertions, 318 deletions
diff --git a/drivers/arm/css/mhu/css_mhu.c b/drivers/arm/css/mhu/css_mhu.c new file mode 100644 index 000000000..b7faf7ed9 --- /dev/null +++ b/drivers/arm/css/mhu/css_mhu.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <platform_def.h> + +#include <arch_helpers.h> +#include <drivers/arm/css/css_mhu.h> +#include <lib/bakery_lock.h> +#include <lib/mmio.h> +#include <plat/arm/common/plat_arm.h> + +/* SCP MHU secure channel registers */ +#define SCP_INTR_S_STAT 0x200 +#define SCP_INTR_S_SET 0x208 +#define SCP_INTR_S_CLEAR 0x210 + +/* CPU MHU secure channel registers */ +#define CPU_INTR_S_STAT 0x300 +#define CPU_INTR_S_SET 0x308 +#define CPU_INTR_S_CLEAR 0x310 + +ARM_INSTANTIATE_LOCK; + +/* Weak definition may be overridden in specific CSS based platform */ +#pragma weak plat_arm_pwrc_setup + + +/* + * Slot 31 is reserved because the MHU hardware uses this register bit to + * indicate a non-secure access attempt. The total number of available slots is + * therefore 31 [30:0]. + */ +#define MHU_MAX_SLOT_ID 30 + +void mhu_secure_message_start(unsigned int slot_id) +{ + assert(slot_id <= MHU_MAX_SLOT_ID); + + arm_lock_get(); + + /* Make sure any previous command has finished */ + while (mmio_read_32(PLAT_CSS_MHU_BASE + CPU_INTR_S_STAT) & + (1 << slot_id)) + ; +} + +void mhu_secure_message_send(unsigned int slot_id) +{ + assert(slot_id <= MHU_MAX_SLOT_ID); + assert(!(mmio_read_32(PLAT_CSS_MHU_BASE + CPU_INTR_S_STAT) & + (1 << slot_id))); + + /* Send command to SCP */ + mmio_write_32(PLAT_CSS_MHU_BASE + CPU_INTR_S_SET, 1 << slot_id); +} + +uint32_t mhu_secure_message_wait(void) +{ + /* Wait for response from SCP */ + uint32_t response; + while (!(response = mmio_read_32(PLAT_CSS_MHU_BASE + SCP_INTR_S_STAT))) + ; + + return response; +} + +void mhu_secure_message_end(unsigned int slot_id) +{ + assert(slot_id <= MHU_MAX_SLOT_ID); + + /* + * Clear any response we got by writing one in the relevant slot bit to + * the CLEAR register + */ + mmio_write_32(PLAT_CSS_MHU_BASE + SCP_INTR_S_CLEAR, 1 << slot_id); + + arm_lock_release(); +} + +void __init mhu_secure_init(void) +{ + arm_lock_init(); + + /* + * The STAT register resets to zero. Ensure it is in the expected state, + * as a stale or garbage value would make us think it's a message we've + * already sent. + */ + assert(mmio_read_32(PLAT_CSS_MHU_BASE + CPU_INTR_S_STAT) == 0); +} + +void __init plat_arm_pwrc_setup(void) +{ + mhu_secure_init(); +} diff --git a/drivers/arm/css/mhu/css_mhu_doorbell.c b/drivers/arm/css/mhu/css_mhu_doorbell.c new file mode 100644 index 000000000..885874272 --- /dev/null +++ b/drivers/arm/css/mhu/css_mhu_doorbell.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014-2019, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <platform_def.h> + +#include <arch_helpers.h> +#include <drivers/arm/css/css_mhu_doorbell.h> +#include <drivers/arm/css/scmi.h> + +void mhu_ring_doorbell(struct scmi_channel_plat_info *plat_info) +{ + MHU_RING_DOORBELL(plat_info->db_reg_addr, + plat_info->db_modify_mask, + plat_info->db_preserve_mask); + return; +} + +void mhuv2_ring_doorbell(struct scmi_channel_plat_info *plat_info) +{ + /* wake receiver */ + MHU_V2_ACCESS_REQUEST(MHUV2_BASE_ADDR); + + /* wait for receiver to acknowledge its ready */ + while (MHU_V2_IS_ACCESS_READY(MHUV2_BASE_ADDR) == 0) + ; + + MHU_RING_DOORBELL(plat_info->db_reg_addr, + plat_info->db_modify_mask, + plat_info->db_preserve_mask); + + /* clear the access request for the receiver */ + MHU_V2_CLEAR_REQUEST(MHUV2_BASE_ADDR); + + return; +} diff --git a/drivers/arm/css/scmi/scmi_ap_core_proto.c b/drivers/arm/css/scmi/scmi_ap_core_proto.c new file mode 100644 index 000000000..2caccc2b8 --- /dev/null +++ b/drivers/arm/css/scmi/scmi_ap_core_proto.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/arm/css/scmi.h> + +#include "scmi_private.h" + +/* + * API to set the SCMI AP core reset address and attributes + */ +int scmi_ap_core_set_reset_addr(void *p, uint64_t reset_addr, uint32_t attr) +{ + mailbox_mem_t *mbx_mem; + int token = 0, ret; + scmi_channel_t *ch = (scmi_channel_t *)p; + + validate_scmi_channel(ch); + + scmi_get_channel(ch); + + mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem); + mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_AP_CORE_PROTO_ID, + SCMI_AP_CORE_RESET_ADDR_SET_MSG, token); + mbx_mem->len = SCMI_AP_CORE_RESET_ADDR_SET_MSG_LEN; + mbx_mem->flags = SCMI_FLAG_RESP_POLL; + SCMI_PAYLOAD_ARG3(mbx_mem->payload, reset_addr & 0xffffffff, + reset_addr >> 32, attr); + + scmi_send_sync_command(ch); + + /* Get the return values */ + SCMI_PAYLOAD_RET_VAL1(mbx_mem->payload, ret); + assert(mbx_mem->len == SCMI_AP_CORE_RESET_ADDR_SET_RESP_LEN); + assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)); + + scmi_put_channel(ch); + + return ret; +} + +/* + * API to get the SCMI AP core reset address and attributes + */ +int scmi_ap_core_get_reset_addr(void *p, uint64_t *reset_addr, uint32_t *attr) +{ + mailbox_mem_t *mbx_mem; + int token = 0, ret; + scmi_channel_t *ch = (scmi_channel_t *)p; + uint32_t lo_addr, hi_addr; + + validate_scmi_channel(ch); + + scmi_get_channel(ch); + + mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem); + mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_AP_CORE_PROTO_ID, + SCMI_AP_CORE_RESET_ADDR_GET_MSG, token); + mbx_mem->len = SCMI_AP_CORE_RESET_ADDR_GET_MSG_LEN; + mbx_mem->flags = SCMI_FLAG_RESP_POLL; + + scmi_send_sync_command(ch); + + /* Get the return values */ + SCMI_PAYLOAD_RET_VAL4(mbx_mem->payload, ret, lo_addr, hi_addr, *attr); + *reset_addr = lo_addr | (uint64_t)hi_addr << 32; + assert(mbx_mem->len == SCMI_AP_CORE_RESET_ADDR_GET_RESP_LEN); + assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)); + + scmi_put_channel(ch); + + return ret; +} diff --git a/drivers/arm/css/scmi/scmi_common.c b/drivers/arm/css/scmi/scmi_common.c new file mode 100644 index 000000000..e2c353d85 --- /dev/null +++ b/drivers/arm/css/scmi/scmi_common.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/arm/css/scmi.h> + +#include "scmi_private.h" + +#if HW_ASSISTED_COHERENCY +#define scmi_lock_init(lock) +#define scmi_lock_get(lock) spin_lock(lock) +#define scmi_lock_release(lock) spin_unlock(lock) +#else +#define scmi_lock_init(lock) bakery_lock_init(lock) +#define scmi_lock_get(lock) bakery_lock_get(lock) +#define scmi_lock_release(lock) bakery_lock_release(lock) +#endif + + +/* + * Private helper function to get exclusive access to SCMI channel. + */ +void scmi_get_channel(scmi_channel_t *ch) +{ + assert(ch->lock); + scmi_lock_get(ch->lock); + + /* Make sure any previous command has finished */ + assert(SCMI_IS_CHANNEL_FREE( + ((mailbox_mem_t *)(ch->info->scmi_mbx_mem))->status)); +} + +/* + * Private helper function to transfer ownership of channel from AP to SCP. + */ +void scmi_send_sync_command(scmi_channel_t *ch) +{ + mailbox_mem_t *mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem); + + SCMI_MARK_CHANNEL_BUSY(mbx_mem->status); + + /* + * Ensure that any write to the SCMI payload area is seen by SCP before + * we write to the doorbell register. If these 2 writes were reordered + * by the CPU then SCP would read stale payload data + */ + dmbst(); + + ch->info->ring_doorbell(ch->info); + /* + * Ensure that the write to the doorbell register is ordered prior to + * checking whether the channel is free. + */ + dmbsy(); + + /* Wait for channel to be free */ + while (!SCMI_IS_CHANNEL_FREE(mbx_mem->status)) + ; + + /* + * Ensure that any read to the SCMI payload area is done after reading + * mailbox status. If these 2 reads were reordered then the CPU would + * read invalid payload data + */ + dmbld(); +} + +/* + * Private helper function to release exclusive access to SCMI channel. + */ +void scmi_put_channel(scmi_channel_t *ch) +{ + /* Make sure any previous command has finished */ + assert(SCMI_IS_CHANNEL_FREE( + ((mailbox_mem_t *)(ch->info->scmi_mbx_mem))->status)); + + assert(ch->lock); + scmi_lock_release(ch->lock); +} + +/* + * API to query the SCMI protocol version. + */ +int scmi_proto_version(void *p, uint32_t proto_id, uint32_t *version) +{ + mailbox_mem_t *mbx_mem; + int token = 0, ret; + scmi_channel_t *ch = (scmi_channel_t *)p; + + validate_scmi_channel(ch); + + scmi_get_channel(ch); + + mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem); + mbx_mem->msg_header = SCMI_MSG_CREATE(proto_id, SCMI_PROTO_VERSION_MSG, + token); + mbx_mem->len = SCMI_PROTO_VERSION_MSG_LEN; + mbx_mem->flags = SCMI_FLAG_RESP_POLL; + + scmi_send_sync_command(ch); + + /* Get the return values */ + SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *version); + assert(mbx_mem->len == SCMI_PROTO_VERSION_RESP_LEN); + assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)); + + scmi_put_channel(ch); + + return ret; +} + +/* + * API to query the protocol message attributes for a SCMI protocol. + */ +int scmi_proto_msg_attr(void *p, uint32_t proto_id, + uint32_t command_id, uint32_t *attr) +{ + mailbox_mem_t *mbx_mem; + int token = 0, ret; + scmi_channel_t *ch = (scmi_channel_t *)p; + + validate_scmi_channel(ch); + + scmi_get_channel(ch); + + mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem); + mbx_mem->msg_header = SCMI_MSG_CREATE(proto_id, + SCMI_PROTO_MSG_ATTR_MSG, token); + mbx_mem->len = SCMI_PROTO_MSG_ATTR_MSG_LEN; + mbx_mem->flags = SCMI_FLAG_RESP_POLL; + SCMI_PAYLOAD_ARG1(mbx_mem->payload, command_id); + + scmi_send_sync_command(ch); + + /* Get the return values */ + SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *attr); + assert(mbx_mem->len == SCMI_PROTO_MSG_ATTR_RESP_LEN); + assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)); + + scmi_put_channel(ch); + + return ret; +} + +/* + * SCMI Driver initialization API. Returns initialized channel on success + * or NULL on error. The return type is an opaque void pointer. + */ +void *scmi_init(scmi_channel_t *ch) +{ + uint32_t version; + int ret; + + assert(ch && ch->info); + assert(ch->info->db_reg_addr); + assert(ch->info->db_modify_mask); + assert(ch->info->db_preserve_mask); + assert(ch->info->ring_doorbell != NULL); + + assert(ch->lock); + + scmi_lock_init(ch->lock); + + ch->is_initialized = 1; + + ret = scmi_proto_version(ch, SCMI_PWR_DMN_PROTO_ID, &version); + if (ret != SCMI_E_SUCCESS) { + WARN("SCMI power domain protocol version message failed"); + goto error; + } + + if (!is_scmi_version_compatible(SCMI_PWR_DMN_PROTO_VER, version)) { + WARN("SCMI power domain protocol version 0x%x incompatible with driver version 0x%x", + version, SCMI_PWR_DMN_PROTO_VER); + goto error; + } + + VERBOSE("SCMI power domain protocol version 0x%x detected\n", version); + + ret = scmi_proto_version(ch, SCMI_SYS_PWR_PROTO_ID, &version); + if ((ret != SCMI_E_SUCCESS)) { + WARN("SCMI system power protocol version message failed"); + goto error; + } + + if (!is_scmi_version_compatible(SCMI_SYS_PWR_PROTO_VER, version)) { + WARN("SCMI system power management protocol version 0x%x incompatible with driver version 0x%x", + version, SCMI_SYS_PWR_PROTO_VER); + goto error; + } + + VERBOSE("SCMI system power management protocol version 0x%x detected\n", + version); + + INFO("SCMI driver initialized\n"); + + return (void *)ch; + +error: + ch->is_initialized = 0; + return NULL; +} diff --git a/drivers/arm/css/scmi/scmi_private.h b/drivers/arm/css/scmi/scmi_private.h new file mode 100644 index 000000000..65305736c --- /dev/null +++ b/drivers/arm/css/scmi/scmi_private.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SCMI_PRIVATE_H +#define SCMI_PRIVATE_H + +#include <lib/mmio.h> + +/* + * SCMI power domain management protocol message and response lengths. It is + * calculated as sum of length in bytes of the message header (4) and payload + * area (the number of bytes of parameters or return values in the payload). + */ +#define SCMI_PROTO_VERSION_MSG_LEN 4 +#define SCMI_PROTO_VERSION_RESP_LEN 12 + +#define SCMI_PROTO_MSG_ATTR_MSG_LEN 8 +#define SCMI_PROTO_MSG_ATTR_RESP_LEN 12 + +#define SCMI_AP_CORE_RESET_ADDR_SET_MSG_LEN 16 +#define SCMI_AP_CORE_RESET_ADDR_SET_RESP_LEN 8 + +#define SCMI_AP_CORE_RESET_ADDR_GET_MSG_LEN 4 +#define SCMI_AP_CORE_RESET_ADDR_GET_RESP_LEN 20 + +#define SCMI_PWR_STATE_SET_MSG_LEN 16 +#define SCMI_PWR_STATE_SET_RESP_LEN 8 + +#define SCMI_PWR_STATE_GET_MSG_LEN 8 +#define SCMI_PWR_STATE_GET_RESP_LEN 12 + +#define SCMI_SYS_PWR_STATE_SET_MSG_LEN 12 +#define SCMI_SYS_PWR_STATE_SET_RESP_LEN 8 + +#define SCMI_SYS_PWR_STATE_GET_MSG_LEN 4 +#define SCMI_SYS_PWR_STATE_GET_RESP_LEN 12 + +/* SCMI message header format bit field */ +#define SCMI_MSG_ID_SHIFT 0 +#define SCMI_MSG_ID_WIDTH 8 +#define SCMI_MSG_ID_MASK ((1 << SCMI_MSG_ID_WIDTH) - 1) + +#define SCMI_MSG_TYPE_SHIFT 8 +#define SCMI_MSG_TYPE_WIDTH 2 +#define SCMI_MSG_TYPE_MASK ((1 << SCMI_MSG_TYPE_WIDTH) - 1) + +#define SCMI_MSG_PROTO_ID_SHIFT 10 +#define SCMI_MSG_PROTO_ID_WIDTH 8 +#define SCMI_MSG_PROTO_ID_MASK ((1 << SCMI_MSG_PROTO_ID_WIDTH) - 1) + +#define SCMI_MSG_TOKEN_SHIFT 18 +#define SCMI_MSG_TOKEN_WIDTH 10 +#define SCMI_MSG_TOKEN_MASK ((1 << SCMI_MSG_TOKEN_WIDTH) - 1) + + +/* SCMI mailbox flags */ +#define SCMI_FLAG_RESP_POLL 0 +#define SCMI_FLAG_RESP_INT 1 + +/* SCMI power domain protocol `POWER_STATE_SET` message flags */ +#define SCMI_PWR_STATE_SET_FLAG_SYNC 0 +#define SCMI_PWR_STATE_SET_FLAG_ASYNC 1 + +/* + * Helper macro to create an SCMI message header given protocol, message id + * and token. + */ +#define SCMI_MSG_CREATE(_protocol, _msg_id, _token) \ + ((((_protocol) & SCMI_MSG_PROTO_ID_MASK) << SCMI_MSG_PROTO_ID_SHIFT) | \ + (((_msg_id) & SCMI_MSG_ID_MASK) << SCMI_MSG_ID_SHIFT) | \ + (((_token) & SCMI_MSG_TOKEN_MASK) << SCMI_MSG_TOKEN_SHIFT)) + +/* Helper macro to get the token from a SCMI message header */ +#define SCMI_MSG_GET_TOKEN(_msg) \ + (((_msg) >> SCMI_MSG_TOKEN_SHIFT) & SCMI_MSG_TOKEN_MASK) + +/* SCMI Channel Status bit fields */ +#define SCMI_CH_STATUS_RES0_MASK 0xFFFFFFFE +#define SCMI_CH_STATUS_FREE_SHIFT 0 +#define SCMI_CH_STATUS_FREE_WIDTH 1 +#define SCMI_CH_STATUS_FREE_MASK ((1 << SCMI_CH_STATUS_FREE_WIDTH) - 1) + +/* Helper macros to check and write the channel status */ +#define SCMI_IS_CHANNEL_FREE(status) \ + (!!(((status) >> SCMI_CH_STATUS_FREE_SHIFT) & SCMI_CH_STATUS_FREE_MASK)) + +#define SCMI_MARK_CHANNEL_BUSY(status) do { \ + assert(SCMI_IS_CHANNEL_FREE(status)); \ + (status) &= ~(SCMI_CH_STATUS_FREE_MASK << \ + SCMI_CH_STATUS_FREE_SHIFT); \ + } while (0) + +/* Helper macros to copy arguments to the mailbox payload */ +#define SCMI_PAYLOAD_ARG1(payld_arr, arg1) \ + mmio_write_32((uintptr_t)&payld_arr[0], arg1) + +#define SCMI_PAYLOAD_ARG2(payld_arr, arg1, arg2) do { \ + SCMI_PAYLOAD_ARG1(payld_arr, arg1); \ + mmio_write_32((uintptr_t)&payld_arr[1], arg2); \ + } while (0) + +#define SCMI_PAYLOAD_ARG3(payld_arr, arg1, arg2, arg3) do { \ + SCMI_PAYLOAD_ARG2(payld_arr, arg1, arg2); \ + mmio_write_32((uintptr_t)&payld_arr[2], arg3); \ + } while (0) + +/* Helper macros to read return values from the mailbox payload */ +#define SCMI_PAYLOAD_RET_VAL1(payld_arr, val1) \ + (val1) = mmio_read_32((uintptr_t)&payld_arr[0]) + +#define SCMI_PAYLOAD_RET_VAL2(payld_arr, val1, val2) do { \ + SCMI_PAYLOAD_RET_VAL1(payld_arr, val1); \ + (val2) = mmio_read_32((uintptr_t)&payld_arr[1]); \ + } while (0) + +#define SCMI_PAYLOAD_RET_VAL3(payld_arr, val1, val2, val3) do { \ + SCMI_PAYLOAD_RET_VAL2(payld_arr, val1, val2); \ + (val3) = mmio_read_32((uintptr_t)&payld_arr[2]); \ + } while (0) + +#define SCMI_PAYLOAD_RET_VAL4(payld_arr, val1, val2, val3, val4) do { \ + SCMI_PAYLOAD_RET_VAL3(payld_arr, val1, val2, val3); \ + (val4) = mmio_read_32((uintptr_t)&payld_arr[3]); \ + } while (0) + +/* + * Private data structure for representing the mailbox memory layout. Refer + * the SCMI specification for more details. + */ +typedef struct mailbox_mem { + uint32_t res_a; /* Reserved */ + volatile uint32_t status; + uint64_t res_b; /* Reserved */ + uint32_t flags; + volatile uint32_t len; + uint32_t msg_header; + uint32_t payload[]; +} mailbox_mem_t; + + +/* Private APIs for use within SCMI driver */ +void scmi_get_channel(scmi_channel_t *ch); +void scmi_send_sync_command(scmi_channel_t *ch); +void scmi_put_channel(scmi_channel_t *ch); + +static inline void validate_scmi_channel(scmi_channel_t *ch) +{ + assert(ch && ch->is_initialized); + assert(ch->info && ch->info->scmi_mbx_mem); +} + +#endif /* SCMI_PRIVATE_H */ diff --git a/drivers/arm/css/scmi/scmi_pwr_dmn_proto.c b/drivers/arm/css/scmi/scmi_pwr_dmn_proto.c new file mode 100644 index 000000000..70165dec2 --- /dev/null +++ b/drivers/arm/css/scmi/scmi_pwr_dmn_proto.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/arm/css/scmi.h> + +#include "scmi_private.h" + +/* + * API to set the SCMI power domain power state. + */ +int scmi_pwr_state_set(void *p, uint32_t domain_id, + uint32_t scmi_pwr_state) +{ + mailbox_mem_t *mbx_mem; + int token = 0, ret; + + /* + * Only asynchronous mode of `set power state` command is allowed on + * application processors. + */ + uint32_t pwr_state_set_msg_flag = SCMI_PWR_STATE_SET_FLAG_ASYNC; + scmi_channel_t *ch = (scmi_channel_t *)p; + + validate_scmi_channel(ch); + + scmi_get_channel(ch); + + mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem); + mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_PWR_DMN_PROTO_ID, + SCMI_PWR_STATE_SET_MSG, token); + mbx_mem->len = SCMI_PWR_STATE_SET_MSG_LEN; + mbx_mem->flags = SCMI_FLAG_RESP_POLL; + SCMI_PAYLOAD_ARG3(mbx_mem->payload, pwr_state_set_msg_flag, + domain_id, scmi_pwr_state); + + scmi_send_sync_command(ch); + + /* Get the return values */ + SCMI_PAYLOAD_RET_VAL1(mbx_mem->payload, ret); + assert(mbx_mem->len == SCMI_PWR_STATE_SET_RESP_LEN); + assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)); + + scmi_put_channel(ch); + + return ret; +} + +/* + * API to get the SCMI power domain power state. + */ +int scmi_pwr_state_get(void *p, uint32_t domain_id, + uint32_t *scmi_pwr_state) +{ + mailbox_mem_t *mbx_mem; + int token = 0, ret; + scmi_channel_t *ch = (scmi_channel_t *)p; + + validate_scmi_channel(ch); + + scmi_get_channel(ch); + + mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem); + mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_PWR_DMN_PROTO_ID, + SCMI_PWR_STATE_GET_MSG, token); + mbx_mem->len = SCMI_PWR_STATE_GET_MSG_LEN; + mbx_mem->flags = SCMI_FLAG_RESP_POLL; + SCMI_PAYLOAD_ARG1(mbx_mem->payload, domain_id); + + scmi_send_sync_command(ch); + + /* Get the return values */ + SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *scmi_pwr_state); + assert(mbx_mem->len == SCMI_PWR_STATE_GET_RESP_LEN); + assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)); + + scmi_put_channel(ch); + + return ret; +} diff --git a/drivers/arm/css/scmi/scmi_sys_pwr_proto.c b/drivers/arm/css/scmi/scmi_sys_pwr_proto.c new file mode 100644 index 000000000..a27c4a5d0 --- /dev/null +++ b/drivers/arm/css/scmi/scmi_sys_pwr_proto.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/arm/css/scmi.h> + +#include "scmi_private.h" + +/* + * API to set the SCMI system power state + */ +int scmi_sys_pwr_state_set(void *p, uint32_t flags, uint32_t system_state) +{ + mailbox_mem_t *mbx_mem; + int token = 0, ret; + scmi_channel_t *ch = (scmi_channel_t *)p; + + validate_scmi_channel(ch); + + scmi_get_channel(ch); + + mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem); + mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_SYS_PWR_PROTO_ID, + SCMI_SYS_PWR_STATE_SET_MSG, token); + mbx_mem->len = SCMI_SYS_PWR_STATE_SET_MSG_LEN; + mbx_mem->flags = SCMI_FLAG_RESP_POLL; + SCMI_PAYLOAD_ARG2(mbx_mem->payload, flags, system_state); + + scmi_send_sync_command(ch); + + /* Get the return values */ + SCMI_PAYLOAD_RET_VAL1(mbx_mem->payload, ret); + assert(mbx_mem->len == SCMI_SYS_PWR_STATE_SET_RESP_LEN); + assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)); + + scmi_put_channel(ch); + + return ret; +} + +/* + * API to get the SCMI system power state + */ +int scmi_sys_pwr_state_get(void *p, uint32_t *system_state) +{ + mailbox_mem_t *mbx_mem; + int token = 0, ret; + scmi_channel_t *ch = (scmi_channel_t *)p; + + validate_scmi_channel(ch); + + scmi_get_channel(ch); + + mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem); + mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_SYS_PWR_PROTO_ID, + SCMI_SYS_PWR_STATE_GET_MSG, token); + mbx_mem->len = SCMI_SYS_PWR_STATE_GET_MSG_LEN; + mbx_mem->flags = SCMI_FLAG_RESP_POLL; + + scmi_send_sync_command(ch); + + /* Get the return values */ + SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *system_state); + assert(mbx_mem->len == SCMI_SYS_PWR_STATE_GET_RESP_LEN); + assert(token == SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)); + + scmi_put_channel(ch); + + return ret; +} diff --git a/drivers/arm/css/scp/css_bom_bootloader.c b/drivers/arm/css/scp/css_bom_bootloader.c new file mode 100644 index 000000000..1fc1270ba --- /dev/null +++ b/drivers/arm/css/scp/css_bom_bootloader.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2014-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <stdint.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/arm/css/css_mhu.h> +#include <drivers/arm/css/css_scp.h> +#include <drivers/arm/css/css_scpi.h> +#include <plat/common/platform.h> +#include <platform_def.h> + +/* ID of the MHU slot used for the BOM protocol */ +#define BOM_MHU_SLOT_ID 0 + +/* Boot commands sent from AP -> SCP */ +#define BOOT_CMD_INFO 0x00 +#define BOOT_CMD_DATA 0x01 + +/* BOM command header */ +typedef struct { + uint32_t id : 8; + uint32_t reserved : 24; +} bom_cmd_t; + +typedef struct { + uint32_t image_size; + uint32_t checksum; +} cmd_info_payload_t; + +/* + * Unlike the SCPI protocol, the boot protocol uses the same memory region + * for both AP -> SCP and SCP -> AP transfers; define the address of this... + */ +#define BOM_SHARED_MEM PLAT_CSS_SCP_COM_SHARED_MEM_BASE +#define BOM_CMD_HEADER ((bom_cmd_t *) BOM_SHARED_MEM) +#define BOM_CMD_PAYLOAD ((void *) (BOM_SHARED_MEM + sizeof(bom_cmd_t))) + +typedef struct { + /* Offset from the base address of the Trusted RAM */ + uint32_t offset; + uint32_t block_size; +} cmd_data_payload_t; + +/* + * All CSS platforms load SCP_BL2/SCP_BL2U just below BL2 (this is where BL31 + * usually resides except when ARM_BL31_IN_DRAM is + * set). Ensure that SCP_BL2/SCP_BL2U do not overflow into shared RAM and + * the tb_fw_config. + */ +CASSERT(SCP_BL2_LIMIT <= BL2_BASE, assert_scp_bl2_overwrite_bl2); +CASSERT(SCP_BL2U_LIMIT <= BL2_BASE, assert_scp_bl2u_overwrite_bl2); + +CASSERT(SCP_BL2_BASE >= ARM_TB_FW_CONFIG_LIMIT, assert_scp_bl2_overflow); +CASSERT(SCP_BL2U_BASE >= ARM_TB_FW_CONFIG_LIMIT, assert_scp_bl2u_overflow); + +static void scp_boot_message_start(void) +{ + mhu_secure_message_start(BOM_MHU_SLOT_ID); +} + +static void scp_boot_message_send(size_t payload_size) +{ + /* Ensure that any write to the BOM payload area is seen by SCP before + * we write to the MHU register. If these 2 writes were reordered by + * the CPU then SCP would read stale payload data */ + dmbst(); + + /* Send command to SCP */ + mhu_secure_message_send(BOM_MHU_SLOT_ID); +} + +static uint32_t scp_boot_message_wait(size_t size) +{ + uint32_t mhu_status; + + mhu_status = mhu_secure_message_wait(); + + /* Expect an SCP Boot Protocol message, reject any other protocol */ + if (mhu_status != (1 << BOM_MHU_SLOT_ID)) { + ERROR("MHU: Unexpected protocol (MHU status: 0x%x)\n", + mhu_status); + panic(); + } + + /* Ensure that any read to the BOM payload area is done after reading + * the MHU register. If these 2 reads were reordered then the CPU would + * read invalid payload data */ + dmbld(); + + return *(uint32_t *) BOM_SHARED_MEM; +} + +static void scp_boot_message_end(void) +{ + mhu_secure_message_end(BOM_MHU_SLOT_ID); +} + +int css_scp_boot_image_xfer(void *image, unsigned int image_size) +{ + uint32_t response; + uint32_t checksum; + cmd_info_payload_t *cmd_info_payload; + cmd_data_payload_t *cmd_data_payload; + + assert((uintptr_t) image == SCP_BL2_BASE); + + if ((image_size == 0) || (image_size % 4 != 0)) { + ERROR("Invalid size for the SCP_BL2 image. Must be a multiple of " + "4 bytes and not zero (current size = 0x%x)\n", + image_size); + return -1; + } + + /* Extract the checksum from the image */ + checksum = *(uint32_t *) image; + image = (char *) image + sizeof(checksum); + image_size -= sizeof(checksum); + + mhu_secure_init(); + + VERBOSE("Send info about the SCP_BL2 image to be transferred to SCP\n"); + + /* + * Send information about the SCP firmware image about to be transferred + * to SCP + */ + scp_boot_message_start(); + + BOM_CMD_HEADER->id = BOOT_CMD_INFO; + cmd_info_payload = BOM_CMD_PAYLOAD; + cmd_info_payload->image_size = image_size; + cmd_info_payload->checksum = checksum; + + scp_boot_message_send(sizeof(*cmd_info_payload)); +#if CSS_DETECT_PRE_1_7_0_SCP + { + const uint32_t deprecated_scp_nack_cmd = 0x404; + uint32_t mhu_status; + + VERBOSE("Detecting SCP version incompatibility\n"); + + mhu_status = mhu_secure_message_wait(); + if (mhu_status == deprecated_scp_nack_cmd) { + ERROR("Detected an incompatible version of the SCP firmware.\n"); + ERROR("Only versions from v1.7.0 onwards are supported.\n"); + ERROR("Please update the SCP firmware.\n"); + return -1; + } + + VERBOSE("SCP version looks OK\n"); + } +#endif /* CSS_DETECT_PRE_1_7_0_SCP */ + response = scp_boot_message_wait(sizeof(response)); + scp_boot_message_end(); + + if (response != 0) { + ERROR("SCP BOOT_CMD_INFO returned error %u\n", response); + return -1; + } + + VERBOSE("Transferring SCP_BL2 image to SCP\n"); + + /* Transfer SCP_BL2 image to SCP */ + scp_boot_message_start(); + + BOM_CMD_HEADER->id = BOOT_CMD_DATA; + cmd_data_payload = BOM_CMD_PAYLOAD; + cmd_data_payload->offset = (uintptr_t) image - ARM_TRUSTED_SRAM_BASE; + cmd_data_payload->block_size = image_size; + + scp_boot_message_send(sizeof(*cmd_data_payload)); + response = scp_boot_message_wait(sizeof(response)); + scp_boot_message_end(); + + if (response != 0) { + ERROR("SCP BOOT_CMD_DATA returned error %u\n", response); + return -1; + } + + return 0; +} + +int css_scp_boot_ready(void) +{ + VERBOSE("Waiting for SCP to signal it is ready to go on\n"); + + /* Wait for SCP to signal it's ready */ + return scpi_wait_ready(); +} diff --git a/drivers/arm/css/scp/css_pm_scmi.c b/drivers/arm/css/scp/css_pm_scmi.c new file mode 100644 index 000000000..1966c44ca --- /dev/null +++ b/drivers/arm/css/scp/css_pm_scmi.c @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <string.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/arm/css/css_scp.h> +#include <drivers/arm/css/scmi.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/arm/css/common/css_pm.h> +#include <plat/common/platform.h> +#include <platform_def.h> + +/* + * This file implements the SCP helper functions using SCMI protocol. + */ + +/* + * SCMI power state parameter bit field encoding for ARM CSS platforms. + * + * 31 20 19 16 15 12 11 8 7 4 3 0 + * +-------------------------------------------------------------+ + * | SBZ | Max level | Level 3 | Level 2 | Level 1 | Level 0 | + * | | | state | state | state | state | + * +-------------------------------------------------------------+ + * + * `Max level` encodes the highest level that has a valid power state + * encoded in the power state. + */ +#define SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT 16 +#define SCMI_PWR_STATE_MAX_PWR_LVL_WIDTH 4 +#define SCMI_PWR_STATE_MAX_PWR_LVL_MASK \ + ((1 << SCMI_PWR_STATE_MAX_PWR_LVL_WIDTH) - 1) +#define SCMI_SET_PWR_STATE_MAX_PWR_LVL(_power_state, _max_level) \ + (_power_state) |= ((_max_level) & SCMI_PWR_STATE_MAX_PWR_LVL_MASK)\ + << SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT +#define SCMI_GET_PWR_STATE_MAX_PWR_LVL(_power_state) \ + (((_power_state) >> SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT) \ + & SCMI_PWR_STATE_MAX_PWR_LVL_MASK) + +#define SCMI_PWR_STATE_LVL_WIDTH 4 +#define SCMI_PWR_STATE_LVL_MASK \ + ((1 << SCMI_PWR_STATE_LVL_WIDTH) - 1) +#define SCMI_SET_PWR_STATE_LVL(_power_state, _level, _level_state) \ + (_power_state) |= ((_level_state) & SCMI_PWR_STATE_LVL_MASK) \ + << (SCMI_PWR_STATE_LVL_WIDTH * (_level)) +#define SCMI_GET_PWR_STATE_LVL(_power_state, _level) \ + (((_power_state) >> (SCMI_PWR_STATE_LVL_WIDTH * (_level))) & \ + SCMI_PWR_STATE_LVL_MASK) + +/* + * The SCMI power state enumeration for a power domain level + */ +typedef enum { + scmi_power_state_off = 0, + scmi_power_state_on = 1, + scmi_power_state_sleep = 2, +} scmi_power_state_t; + +/* + * The global handle for invoking the SCMI driver APIs after the driver + * has been initialized. + */ +static void *scmi_handle; + +/* The SCMI channel global object */ +static scmi_channel_t channel; + +ARM_SCMI_INSTANTIATE_LOCK; + +/* + * Helper function to suspend a CPU power domain and its parent power domains + * if applicable. + */ +void css_scp_suspend(const struct psci_power_state *target_state) +{ + int ret; + + /* At least power domain level 0 should be specified to be suspended */ + assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == + ARM_LOCAL_STATE_OFF); + + /* Check if power down at system power domain level is requested */ + if (css_system_pwr_state(target_state) == ARM_LOCAL_STATE_OFF) { + /* Issue SCMI command for SYSTEM_SUSPEND */ + ret = scmi_sys_pwr_state_set(scmi_handle, + SCMI_SYS_PWR_FORCEFUL_REQ, + SCMI_SYS_PWR_SUSPEND); + if (ret != SCMI_E_SUCCESS) { + ERROR("SCMI system power domain suspend return 0x%x unexpected\n", + ret); + panic(); + } + return; + } +#if !HW_ASSISTED_COHERENCY + int lvl; + uint32_t scmi_pwr_state = 0; + /* + * If we reach here, then assert that power down at system power domain + * level is running. + */ + assert(css_system_pwr_state(target_state) == ARM_LOCAL_STATE_RUN); + + /* For level 0, specify `scmi_power_state_sleep` as the power state */ + SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, ARM_PWR_LVL0, + scmi_power_state_sleep); + + for (lvl = ARM_PWR_LVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) { + if (target_state->pwr_domain_state[lvl] == ARM_LOCAL_STATE_RUN) + break; + + assert(target_state->pwr_domain_state[lvl] == + ARM_LOCAL_STATE_OFF); + /* + * Specify `scmi_power_state_off` as power state for higher + * levels. + */ + SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl, + scmi_power_state_off); + } + + SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1); + + ret = scmi_pwr_state_set(scmi_handle, + plat_css_core_pos_to_scmi_dmn_id_map[plat_my_core_pos()], + scmi_pwr_state); + + if (ret != SCMI_E_SUCCESS) { + ERROR("SCMI set power state command return 0x%x unexpected\n", + ret); + panic(); + } +#endif +} + +/* + * Helper function to turn off a CPU power domain and its parent power domains + * if applicable. + */ +void css_scp_off(const struct psci_power_state *target_state) +{ + int lvl = 0, ret; + uint32_t scmi_pwr_state = 0; + + /* At-least the CPU level should be specified to be OFF */ + assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == + ARM_LOCAL_STATE_OFF); + + /* PSCI CPU OFF cannot be used to turn OFF system power domain */ + assert(css_system_pwr_state(target_state) == ARM_LOCAL_STATE_RUN); + + for (; lvl <= PLAT_MAX_PWR_LVL; lvl++) { + if (target_state->pwr_domain_state[lvl] == ARM_LOCAL_STATE_RUN) + break; + + assert(target_state->pwr_domain_state[lvl] == + ARM_LOCAL_STATE_OFF); + SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl, + scmi_power_state_off); + } + + SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1); + + ret = scmi_pwr_state_set(scmi_handle, + plat_css_core_pos_to_scmi_dmn_id_map[plat_my_core_pos()], + scmi_pwr_state); + + if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) { + ERROR("SCMI set power state command return 0x%x unexpected\n", + ret); + panic(); + } +} + +/* + * Helper function to turn ON a CPU power domain and its parent power domains + * if applicable. + */ +void css_scp_on(u_register_t mpidr) +{ + int lvl = 0, ret, core_pos; + uint32_t scmi_pwr_state = 0; + + for (; lvl <= PLAT_MAX_PWR_LVL; lvl++) + SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl, + scmi_power_state_on); + + SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1); + + core_pos = plat_core_pos_by_mpidr(mpidr); + assert(core_pos >= 0 && core_pos < PLATFORM_CORE_COUNT); + + ret = scmi_pwr_state_set(scmi_handle, + plat_css_core_pos_to_scmi_dmn_id_map[core_pos], + scmi_pwr_state); + + if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) { + ERROR("SCMI set power state command return 0x%x unexpected\n", + ret); + panic(); + } +} + +/* + * Helper function to get the power state of a power domain node as reported + * by the SCP. + */ +int css_scp_get_power_state(u_register_t mpidr, unsigned int power_level) +{ + int ret, cpu_idx; + uint32_t scmi_pwr_state = 0, lvl_state; + + /* We don't support get power state at the system power domain level */ + if ((power_level > PLAT_MAX_PWR_LVL) || + (power_level == CSS_SYSTEM_PWR_DMN_LVL)) { + WARN("Invalid power level %u specified for SCMI get power state\n", + power_level); + return PSCI_E_INVALID_PARAMS; + } + + cpu_idx = plat_core_pos_by_mpidr(mpidr); + assert(cpu_idx > -1); + + ret = scmi_pwr_state_get(scmi_handle, + plat_css_core_pos_to_scmi_dmn_id_map[cpu_idx], + &scmi_pwr_state); + + if (ret != SCMI_E_SUCCESS) { + WARN("SCMI get power state command return 0x%x unexpected\n", + ret); + return PSCI_E_INVALID_PARAMS; + } + + /* + * Find the maximum power level described in the get power state + * command. If it is less than the requested power level, then assume + * the requested power level is ON. + */ + if (SCMI_GET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state) < power_level) + return HW_ON; + + lvl_state = SCMI_GET_PWR_STATE_LVL(scmi_pwr_state, power_level); + if (lvl_state == scmi_power_state_on) + return HW_ON; + + assert((lvl_state == scmi_power_state_off) || + (lvl_state == scmi_power_state_sleep)); + return HW_OFF; +} + +void __dead2 css_scp_system_off(int state) +{ + int ret; + + /* + * Disable GIC CPU interface to prevent pending interrupt from waking + * up the AP from WFI. + */ + plat_arm_gic_cpuif_disable(); + + /* + * Issue SCMI command. First issue a graceful + * request and if that fails force the request. + */ + ret = scmi_sys_pwr_state_set(scmi_handle, + SCMI_SYS_PWR_FORCEFUL_REQ, + state); + + if (ret != SCMI_E_SUCCESS) { + ERROR("SCMI system power state set 0x%x returns unexpected 0x%x\n", + state, ret); + panic(); + } + wfi(); + ERROR("CSS set power state: operation not handled.\n"); + panic(); +} + +/* + * Helper function to shutdown the system via SCMI. + */ +void __dead2 css_scp_sys_shutdown(void) +{ + css_scp_system_off(SCMI_SYS_PWR_SHUTDOWN); +} + +/* + * Helper function to reset the system via SCMI. + */ +void __dead2 css_scp_sys_reboot(void) +{ + css_scp_system_off(SCMI_SYS_PWR_COLD_RESET); +} + +static int scmi_ap_core_init(scmi_channel_t *ch) +{ +#if PROGRAMMABLE_RESET_ADDRESS + uint32_t version; + int ret; + + ret = scmi_proto_version(ch, SCMI_AP_CORE_PROTO_ID, &version); + if (ret != SCMI_E_SUCCESS) { + WARN("SCMI AP core protocol version message failed\n"); + return -1; + } + + if (!is_scmi_version_compatible(SCMI_AP_CORE_PROTO_VER, version)) { + WARN("SCMI AP core protocol version 0x%x incompatible with driver version 0x%x\n", + version, SCMI_AP_CORE_PROTO_VER); + return -1; + } + INFO("SCMI AP core protocol version 0x%x detected\n", version); +#endif + return 0; +} + +void __init plat_arm_pwrc_setup(void) +{ + channel.info = plat_css_get_scmi_info(); + channel.lock = ARM_SCMI_LOCK_GET_INSTANCE; + scmi_handle = scmi_init(&channel); + if (scmi_handle == NULL) { + ERROR("SCMI Initialization failed\n"); + panic(); + } + if (scmi_ap_core_init(&channel) < 0) { + ERROR("SCMI AP core protocol initialization failed\n"); + panic(); + } +} + +/****************************************************************************** + * This function overrides the default definition for ARM platforms. Initialize + * the SCMI driver, query capability via SCMI and modify the PSCI capability + * based on that. + *****************************************************************************/ +const plat_psci_ops_t *css_scmi_override_pm_ops(plat_psci_ops_t *ops) +{ + uint32_t msg_attr; + int ret; + + assert(scmi_handle); + + /* Check that power domain POWER_STATE_SET message is supported */ + ret = scmi_proto_msg_attr(scmi_handle, SCMI_PWR_DMN_PROTO_ID, + SCMI_PWR_STATE_SET_MSG, &msg_attr); + if (ret != SCMI_E_SUCCESS) { + ERROR("Set power state command is not supported by SCMI\n"); + panic(); + } + + /* + * Don't support PSCI NODE_HW_STATE call if SCMI doesn't support + * POWER_STATE_GET message. + */ + ret = scmi_proto_msg_attr(scmi_handle, SCMI_PWR_DMN_PROTO_ID, + SCMI_PWR_STATE_GET_MSG, &msg_attr); + if (ret != SCMI_E_SUCCESS) + ops->get_node_hw_state = NULL; + + /* Check if the SCMI SYSTEM_POWER_STATE_SET message is supported */ + ret = scmi_proto_msg_attr(scmi_handle, SCMI_SYS_PWR_PROTO_ID, + SCMI_SYS_PWR_STATE_SET_MSG, &msg_attr); + if (ret != SCMI_E_SUCCESS) { + /* System power management operations are not supported */ + ops->system_off = NULL; + ops->system_reset = NULL; + ops->get_sys_suspend_power_state = NULL; + } else { + if (!(msg_attr & SCMI_SYS_PWR_SUSPEND_SUPPORTED)) { + /* + * System power management protocol is available, but + * it does not support SYSTEM SUSPEND. + */ + ops->get_sys_suspend_power_state = NULL; + } + if (!(msg_attr & SCMI_SYS_PWR_WARM_RESET_SUPPORTED)) { + /* + * WARM reset is not available. + */ + ops->system_reset2 = NULL; + } + } + + return ops; +} + +int css_system_reset2(int is_vendor, int reset_type, u_register_t cookie) +{ + if (is_vendor || (reset_type != PSCI_RESET2_SYSTEM_WARM_RESET)) + return PSCI_E_INVALID_PARAMS; + + css_scp_system_off(SCMI_SYS_PWR_WARM_RESET); + /* + * css_scp_system_off cannot return (it is a __dead function), + * but css_system_reset2 has to return some value, even in + * this case. + */ + return 0; +} + +#if PROGRAMMABLE_RESET_ADDRESS +void plat_arm_program_trusted_mailbox(uintptr_t address) +{ + int ret; + + assert(scmi_handle); + ret = scmi_ap_core_set_reset_addr(scmi_handle, address, + SCMI_AP_CORE_LOCK_ATTR); + if (ret != SCMI_E_SUCCESS) { + ERROR("CSS: Failed to program reset address: %d\n", ret); + panic(); + } +} +#endif diff --git a/drivers/arm/css/scp/css_pm_scpi.c b/drivers/arm/css/scp/css_pm_scpi.c new file mode 100644 index 000000000..b4019ce03 --- /dev/null +++ b/drivers/arm/css/scp/css_pm_scpi.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/arm/css/css_scp.h> +#include <drivers/arm/css/css_scpi.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/arm/css/common/css_pm.h> + +/* + * This file implements the SCP power management functions using SCPI protocol. + */ + +/* + * Helper function to inform power down state to SCP. + */ +void css_scp_suspend(const struct psci_power_state *target_state) +{ + uint32_t cluster_state = scpi_power_on; + uint32_t system_state = scpi_power_on; + + /* Check if power down at system power domain level is requested */ + if (css_system_pwr_state(target_state) == ARM_LOCAL_STATE_OFF) + system_state = scpi_power_retention; + + /* Cluster is to be turned off, so disable coherency */ + if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) + cluster_state = scpi_power_off; + + /* + * Ask the SCP to power down the appropriate components depending upon + * their state. + */ + scpi_set_css_power_state(read_mpidr_el1(), + scpi_power_off, + cluster_state, + system_state); +} + +/* + * Helper function to turn off a CPU power domain and its parent power domains + * if applicable. Since SCPI doesn't differentiate between OFF and suspend, we + * call the suspend helper here. + */ +void css_scp_off(const struct psci_power_state *target_state) +{ + css_scp_suspend(target_state); +} + +/* + * Helper function to turn ON a CPU power domain and its parent power domains + * if applicable. + */ +void css_scp_on(u_register_t mpidr) +{ + /* + * SCP takes care of powering up parent power domains so we + * only need to care about level 0 + */ + scpi_set_css_power_state(mpidr, scpi_power_on, scpi_power_on, + scpi_power_on); +} + +/* + * Helper function to get the power state of a power domain node as reported + * by the SCP. + */ +int css_scp_get_power_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 + */ +#if ARM_PLAT_MT + /* + * The current SCPI driver only caters for single-threaded + * platforms. Hence we ignore the thread ID (which is always 0) + * for such platforms. + */ + element = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; +#else + element = mpidr & MPIDR_AFFLVL_MASK; +#endif /* ARM_PLAT_MT */ + 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; + } +} + +/* + * Helper function to shutdown the system via SCPI. + */ +void __dead2 css_scp_sys_shutdown(void) +{ + uint32_t response; + + /* + * Disable GIC CPU interface to prevent pending interrupt + * from waking up the AP from WFI. + */ + plat_arm_gic_cpuif_disable(); + + /* Send the power down request to the SCP */ + response = scpi_sys_power_state(scpi_system_shutdown); + + if (response != SCP_OK) { + ERROR("CSS System Off: SCP error %u.\n", response); + panic(); + } + wfi(); + ERROR("CSS System Off: operation not handled.\n"); + panic(); +} + +/* + * Helper function to reset the system via SCPI. + */ +void __dead2 css_scp_sys_reboot(void) +{ + uint32_t response; + + /* + * Disable GIC CPU interface to prevent pending interrupt + * from waking up the AP from WFI. + */ + plat_arm_gic_cpuif_disable(); + + /* Send the system reset request to the SCP */ + response = scpi_sys_power_state(scpi_system_reboot); + + if (response != SCP_OK) { + ERROR("CSS System Reset: SCP error %u.\n", response); + panic(); + } + wfi(); + ERROR("CSS System Reset: operation not handled.\n"); + panic(); +} diff --git a/drivers/arm/css/scp/css_sds.c b/drivers/arm/css/scp/css_sds.c new file mode 100644 index 000000000..e42ee10d7 --- /dev/null +++ b/drivers/arm/css/scp/css_sds.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <stdint.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/arm/css/css_scp.h> +#include <drivers/arm/css/sds.h> +#include <drivers/delay_timer.h> +#include <plat/common/platform.h> +#include <platform_def.h> + +int css_scp_boot_image_xfer(void *image, unsigned int image_size) +{ + int ret; + unsigned int image_offset, image_flags; + + ret = sds_init(); + if (ret != SDS_OK) { + ERROR("SCP SDS initialization failed\n"); + panic(); + } + + VERBOSE("Writing SCP image metadata\n"); + image_offset = (uintptr_t) image - ARM_TRUSTED_SRAM_BASE; + ret = sds_struct_write(SDS_SCP_IMG_STRUCT_ID, SDS_SCP_IMG_ADDR_OFFSET, + &image_offset, SDS_SCP_IMG_ADDR_SIZE, + SDS_ACCESS_MODE_NON_CACHED); + if (ret != SDS_OK) + goto sds_fail; + + ret = sds_struct_write(SDS_SCP_IMG_STRUCT_ID, SDS_SCP_IMG_SIZE_OFFSET, + &image_size, SDS_SCP_IMG_SIZE_SIZE, + SDS_ACCESS_MODE_NON_CACHED); + if (ret != SDS_OK) + goto sds_fail; + + VERBOSE("Marking SCP image metadata as valid\n"); + image_flags = SDS_SCP_IMG_VALID_FLAG_BIT; + ret = sds_struct_write(SDS_SCP_IMG_STRUCT_ID, SDS_SCP_IMG_FLAG_OFFSET, + &image_flags, SDS_SCP_IMG_FLAG_SIZE, + SDS_ACCESS_MODE_NON_CACHED); + if (ret != SDS_OK) + goto sds_fail; + + return 0; +sds_fail: + ERROR("SCP SDS write to SCP IMG struct failed\n"); + panic(); +} + +/* + * API to wait for SCP to signal till it's ready after booting the transferred + * image. + */ +int css_scp_boot_ready(void) +{ + uint32_t scp_feature_availability_flags; + int ret, retry = CSS_SCP_READY_10US_RETRIES; + + + VERBOSE("Waiting for SCP RAM to complete its initialization process\n"); + + /* Wait for the SCP RAM Firmware to complete its initialization process */ + while (retry > 0) { + ret = sds_struct_read(SDS_FEATURE_AVAIL_STRUCT_ID, 0, + &scp_feature_availability_flags, + SDS_FEATURE_AVAIL_SIZE, + SDS_ACCESS_MODE_NON_CACHED); + if (ret == SDS_ERR_STRUCT_NOT_FINALIZED) + continue; + + if (ret != SDS_OK) { + ERROR(" sds_struct_read failed\n"); + panic(); + } + + if (scp_feature_availability_flags & + SDS_FEATURE_AVAIL_SCP_RAM_READY_BIT) + return 0; + + udelay(10); + retry--; + } + + ERROR("Timeout of %d ms expired waiting for SCP RAM Ready flag\n", + CSS_SCP_READY_10US_RETRIES/100); + + plat_panic_handler(); +} diff --git a/drivers/arm/css/scpi/css_scpi.c b/drivers/arm/css/scpi/css_scpi.c new file mode 100644 index 000000000..4b73265ad --- /dev/null +++ b/drivers/arm/css/scpi/css_scpi.c @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2014-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <string.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/arm/css/css_mhu.h> +#include <drivers/arm/css/css_scpi.h> +#include <lib/utils.h> +#include <plat/common/platform.h> +#include <platform_def.h> + +#define SCPI_SHARED_MEM_SCP_TO_AP PLAT_CSS_SCP_COM_SHARED_MEM_BASE +#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 + +static void scpi_secure_message_start(void) +{ + mhu_secure_message_start(SCPI_MHU_SLOT_ID); +} + +static void scpi_secure_message_send(size_t payload_size) +{ + /* + * Ensure that any write to the SCPI payload area is seen by SCP before + * we write to the MHU register. If these 2 writes were reordered by + * the CPU then SCP would read stale payload data + */ + dmbst(); + + mhu_secure_message_send(SCPI_MHU_SLOT_ID); +} + +static void scpi_secure_message_receive(scpi_cmd_t *cmd) +{ + uint32_t mhu_status; + + assert(cmd != NULL); + + mhu_status = mhu_secure_message_wait(); + + /* Expect an SCPI message, reject any other protocol */ + if (mhu_status != (1 << SCPI_MHU_SLOT_ID)) { + ERROR("MHU: Unexpected protocol (MHU status: 0x%x)\n", + mhu_status); + panic(); + } + + /* + * Ensure that any read to the SCPI payload area is done after reading + * the MHU register. If these 2 reads were reordered then the CPU would + * read invalid payload data + */ + dmbld(); + + memcpy(cmd, (void *) SCPI_SHARED_MEM_SCP_TO_AP, sizeof(*cmd)); +} + +static void scpi_secure_message_end(void) +{ + mhu_secure_message_end(SCPI_MHU_SLOT_ID); +} + +int scpi_wait_ready(void) +{ + scpi_cmd_t scpi_cmd; + + VERBOSE("Waiting for SCP_READY command...\n"); + + /* Get a message from the SCP */ + scpi_secure_message_start(); + scpi_secure_message_receive(&scpi_cmd); + scpi_secure_message_end(); + + /* We are expecting 'SCP Ready', produce correct error if it's not */ + scpi_status_t status = SCP_OK; + if (scpi_cmd.id != SCPI_CMD_SCP_READY) { + ERROR("Unexpected SCP command: expected command #%u, got command #%u\n", + SCPI_CMD_SCP_READY, scpi_cmd.id); + status = SCP_E_SUPPORT; + } else if (scpi_cmd.size != 0) { + ERROR("SCP_READY command has incorrect size: expected 0, got %u\n", + scpi_cmd.size); + status = SCP_E_SIZE; + } + + VERBOSE("Sending response for SCP_READY command\n"); + + /* + * Send our response back to SCP. + * We are using the same SCPI header, just update the status field. + */ + scpi_cmd.status = status; + scpi_secure_message_start(); + memcpy((void *) SCPI_SHARED_MEM_AP_TO_SCP, &scpi_cmd, sizeof(scpi_cmd)); + scpi_secure_message_send(0); + scpi_secure_message_end(); + + return status == SCP_OK ? 0 : -1; +} + +void scpi_set_css_power_state(unsigned int mpidr, + scpi_power_state_t cpu_state, scpi_power_state_t cluster_state, + scpi_power_state_t css_state) +{ + scpi_cmd_t *cmd; + uint32_t state = 0; + uint32_t *payload_addr; + +#if ARM_PLAT_MT + /* + * The current SCPI driver only caters for single-threaded platforms. + * Hence we ignore the thread ID (which is always 0) for such platforms. + */ + state |= (mpidr >> MPIDR_AFF1_SHIFT) & 0x0f; /* CPU ID */ + state |= ((mpidr >> MPIDR_AFF2_SHIFT) & 0x0f) << 4; /* Cluster ID */ +#else + state |= mpidr & 0x0f; /* CPU ID */ + state |= (mpidr & 0xf00) >> 4; /* Cluster ID */ +#endif /* ARM_PLAT_MT */ + + state |= cpu_state << 8; + state |= cluster_state << 12; + state |= css_state << 16; + + scpi_secure_message_start(); + + /* Populate the command header */ + cmd = SCPI_CMD_HEADER_AP_TO_SCP; + cmd->id = SCPI_CMD_SET_CSS_POWER_STATE; + cmd->set = SCPI_SET_NORMAL; + cmd->sender = 0; + cmd->size = sizeof(state); + /* Populate the command payload */ + payload_addr = SCPI_CMD_PAYLOAD_AP_TO_SCP; + *payload_addr = state; + scpi_secure_message_send(sizeof(state)); + /* + * SCP does not reply to this command in order to avoid MHU interrupts + * from the sender, which could interfere with its power state request. + */ + + 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 + */ +#if ARM_PLAT_MT + /* + * The current SCPI driver only caters for single-threaded platforms. + * Hence we ignore the thread ID (which is always 0) for such platforms. + */ + cpu = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; + cluster = (mpidr >> MPIDR_AFF2_SHIFT) & MPIDR_AFFLVL_MASK; +#else + cpu = mpidr & MPIDR_AFFLVL_MASK; + cluster = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; +#endif /* ARM_PLAT_MT */ + if (cpu >= 8 || cluster >= 0xf) + return -1; + + scpi_secure_message_start(); + + /* Populate request headers */ + zeromem(SCPI_CMD_HEADER_AP_TO_SCP, sizeof(*cmd)); + cmd = SCPI_CMD_HEADER_AP_TO_SCP; + 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; + uint8_t *payload_addr; + scpi_cmd_t response; + + scpi_secure_message_start(); + + /* Populate the command header */ + cmd = SCPI_CMD_HEADER_AP_TO_SCP; + cmd->id = SCPI_CMD_SYS_POWER_STATE; + cmd->set = 0; + cmd->sender = 0; + cmd->size = sizeof(*payload_addr); + /* Populate the command payload */ + payload_addr = SCPI_CMD_PAYLOAD_AP_TO_SCP; + *payload_addr = system_state & 0xff; + scpi_secure_message_send(sizeof(*payload_addr)); + + scpi_secure_message_receive(&response); + + scpi_secure_message_end(); + + return response.status; +} diff --git a/drivers/arm/css/sds/aarch32/sds_helpers.S b/drivers/arm/css/sds/aarch32/sds_helpers.S new file mode 100644 index 000000000..13ff0e186 --- /dev/null +++ b/drivers/arm/css/sds/aarch32/sds_helpers.S @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <asm_macros.S> +#include <drivers/arm/css/sds.h> +#include <platform_def.h> + +#include "../sds_private.h" + + .globl sds_get_primary_cpu_id + + /* + * int sds_get_primary_cpu_id(void); + * Return the primary CPU ID from SDS Structure + * Returns CPUID on success or -1 on failure + */ +func sds_get_primary_cpu_id + ldr r0, =PLAT_ARM_SDS_MEM_BASE + ldr r2, =SDS_REGION_SIGNATURE + ldr r1, [r0] + ubfx r3, r1, #0, #16 + + /* Check if the SDS region signature found */ + cmp r2, r3 + bne 2f + + /* Get the structure count from region descriptor in r1 */ + ubfx r1, r1, #SDS_REGION_STRUCT_COUNT_SHIFT, #SDS_REGION_STRUCT_COUNT_WIDTH + cmp r1, #0 + beq 2f + add r0, r0, #SDS_REGION_DESC_SIZE + + /* Initialize the loop iterator count in r3 */ + mov r3, #0 +loop_begin: + ldrh r2, [r0] + cmp r2, #SDS_AP_CPU_INFO_STRUCT_ID + bne continue_loop + + /* We have found the required structure */ + ldr r0, [r0,#(SDS_HEADER_SIZE + SDS_AP_CPU_INFO_PRIMARY_CPUID_OFFSET)] + bx lr +continue_loop: + /* Increment the loop counter and exit loop if counter == structure count */ + add r3, r3, #0x1 + cmp r1, r3 + beq 2f + + /* Read the 2nd word in header */ + ldr r2, [r0,#4] + /* Get the structure size from header */ + ubfx r2, r2, #SDS_HEADER_STRUCT_SIZE_SHIFT, #SDS_HEADER_STRUCT_SIZE_WIDTH + /* Add the structure size and SDS HEADER SIZE to point to next header */ + add r2, r2, #SDS_HEADER_SIZE + add r0, r0, r2 + b loop_begin +2: + mov r0, #0xffffffff + bx lr +endfunc sds_get_primary_cpu_id diff --git a/drivers/arm/css/sds/aarch64/sds_helpers.S b/drivers/arm/css/sds/aarch64/sds_helpers.S new file mode 100644 index 000000000..3256c2b54 --- /dev/null +++ b/drivers/arm/css/sds/aarch64/sds_helpers.S @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <asm_macros.S> +#include <drivers/arm/css/sds.h> +#include <platform_def.h> + +#include "../sds_private.h" + + .globl sds_get_primary_cpu_id + + /* + * int sds_get_primary_cpu_id(void); + * Return the primary CPI ID from SDS Structure + * Returns CPUID on success or -1 on failure + */ +func sds_get_primary_cpu_id + mov_imm x0, PLAT_ARM_SDS_MEM_BASE + mov w2, #SDS_REGION_SIGNATURE + ldr w1, [x0] + + /* Check if the SDS region signature found */ + cmp w2, w1, uxth + b.ne 2f + + /* Get the structure count from region descriptor in `w1 */ + ubfx w1, w1, #SDS_REGION_STRUCT_COUNT_SHIFT, #SDS_REGION_STRUCT_COUNT_WIDTH + cbz w1, 2f + add x0, x0, #SDS_REGION_DESC_SIZE + + /* Initialize the loop iterator count in w3 */ + mov w3, #0 +loop_begin: + ldrh w2, [x0] + cmp w2, #SDS_AP_CPU_INFO_STRUCT_ID + b.ne continue_loop + + /* We have found the required structure */ + ldr w0, [x0,#(SDS_HEADER_SIZE + SDS_AP_CPU_INFO_PRIMARY_CPUID_OFFSET)] + ret +continue_loop: + /* Increment the loop counter and exit loop if counter == structure count */ + add w3, w3, #0x1 + cmp w1, w3 + b.eq 2f + + /* Read the 2nd word in header */ + ldr w2, [x0,#4] + /* Get the structure size from header */ + ubfx x2, x2, #SDS_HEADER_STRUCT_SIZE_SHIFT, #SDS_HEADER_STRUCT_SIZE_WIDTH + /* Add the structure size and SDS HEADER SIZE to point to next header */ + add x2, x2, #SDS_HEADER_SIZE + add x0, x0, x2 + b loop_begin +2: + mov w0, #0xffffffff + ret +endfunc sds_get_primary_cpu_id diff --git a/drivers/arm/css/sds/sds.c b/drivers/arm/css/sds/sds.c new file mode 100644 index 000000000..1fb196c70 --- /dev/null +++ b/drivers/arm/css/sds/sds.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <stdint.h> +#include <string.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/arm/css/sds.h> +#include <platform_def.h> + +#include "sds_private.h" + +/* + * Variables used to track and maintain the state of the memory region reserved + * for usage by the SDS framework. + */ + +/* Pointer to the base of the SDS memory region */ +static uintptr_t sds_mem_base; + +/* Size of the SDS memory region in bytes */ +static size_t sds_mem_size; + +/* + * Perform some non-exhaustive tests to determine whether any of the fields + * within a Structure Header contain obviously invalid data. + * Returns SDS_OK on success, SDS_ERR_FAIL on error. + */ +static int sds_struct_is_valid(uintptr_t header) +{ + size_t struct_size = GET_SDS_HEADER_STRUCT_SIZE(header); + + /* Zero is not a valid identifier */ + if (GET_SDS_HEADER_ID(header) == 0) + return SDS_ERR_FAIL; + + /* Check SDS Schema version */ + if (GET_SDS_HEADER_VERSION(header) == SDS_REGION_SCH_VERSION) + return SDS_ERR_FAIL; + + /* The SDS Structure sizes have to be multiple of 8 */ + if ((struct_size == 0) || ((struct_size % 8) != 0)) + return SDS_ERR_FAIL; + + if (struct_size > sds_mem_size) + return SDS_ERR_FAIL; + + return SDS_OK; +} + +/* + * Validate the SDS structure headers. + * Returns SDS_OK on success, SDS_ERR_FAIL on error. + */ +static int validate_sds_struct_headers(void) +{ + unsigned int i, structure_count; + uintptr_t header; + + structure_count = GET_SDS_REGION_STRUCTURE_COUNT(sds_mem_base); + + if (structure_count == 0) + return SDS_ERR_FAIL; + + header = sds_mem_base + SDS_REGION_DESC_SIZE; + + /* Iterate over structure headers and validate each one */ + for (i = 0; i < structure_count; i++) { + if (sds_struct_is_valid(header) != SDS_OK) { + WARN("SDS: Invalid structure header detected\n"); + return SDS_ERR_FAIL; + } + header += GET_SDS_HEADER_STRUCT_SIZE(header) + SDS_HEADER_SIZE; + } + return SDS_OK; +} + +/* + * Get the structure header pointer corresponding to the structure ID. + * Returns SDS_OK on success, SDS_ERR_STRUCT_NOT_FOUND on error. + */ +static int get_struct_header(uint32_t structure_id, struct_header_t **header) +{ + unsigned int i, structure_count; + uintptr_t current_header; + + assert(header); + + structure_count = GET_SDS_REGION_STRUCTURE_COUNT(sds_mem_base); + if (structure_count == 0) + return SDS_ERR_STRUCT_NOT_FOUND; + + current_header = ((uintptr_t)sds_mem_base) + SDS_REGION_DESC_SIZE; + + /* Iterate over structure headers to find one with a matching ID */ + for (i = 0; i < structure_count; i++) { + if (GET_SDS_HEADER_ID(current_header) == structure_id) { + *header = (struct_header_t *)current_header; + return SDS_OK; + } + current_header += GET_SDS_HEADER_STRUCT_SIZE(current_header) + + SDS_HEADER_SIZE; + } + + *header = NULL; + return SDS_ERR_STRUCT_NOT_FOUND; +} + +/* + * Check if a structure header corresponding to the structure ID exists. + * Returns SDS_OK if structure header exists else SDS_ERR_STRUCT_NOT_FOUND + * if not found. + */ +int sds_struct_exists(unsigned int structure_id) +{ + struct_header_t *header = NULL; + int ret; + + ret = get_struct_header(structure_id, &header); + if (ret == SDS_OK) { + assert(header); + } + + return ret; +} + +/* + * Read from field in the structure corresponding to `structure_id`. + * `fld_off` is the offset to the field in the structure and `mode` + * indicates whether cache maintenance need to performed prior to the read. + * The `data` is the pointer to store the read data of size specified by `size`. + * Returns SDS_OK on success or corresponding error codes on failure. + */ +int sds_struct_read(uint32_t structure_id, unsigned int fld_off, + void *data, size_t size, sds_access_mode_t mode) +{ + int status; + uintptr_t field_base; + struct_header_t *header = NULL; + + if (!data) + return SDS_ERR_INVALID_PARAMS; + + /* Check if a structure with this ID exists */ + status = get_struct_header(structure_id, &header); + if (status != SDS_OK) + return status; + + assert(header); + + if (mode == SDS_ACCESS_MODE_CACHED) + inv_dcache_range((uintptr_t)header, SDS_HEADER_SIZE + size); + + if (!IS_SDS_HEADER_VALID(header)) { + WARN("SDS: Reading from un-finalized structure 0x%x\n", + structure_id); + return SDS_ERR_STRUCT_NOT_FINALIZED; + } + + if ((fld_off + size) > GET_SDS_HEADER_STRUCT_SIZE(header)) + return SDS_ERR_FAIL; + + field_base = (uintptr_t)header + SDS_HEADER_SIZE + fld_off; + if (check_uptr_overflow(field_base, size - 1)) + return SDS_ERR_FAIL; + + /* Copy the required field in the struct */ + memcpy(data, (void *)field_base, size); + + return SDS_OK; +} + +/* + * Write to the field in the structure corresponding to `structure_id`. + * `fld_off` is the offset to the field in the structure and `mode` + * indicates whether cache maintenance need to performed for the write. + * The `data` is the pointer to data of size specified by `size`. + * Returns SDS_OK on success or corresponding error codes on failure. + */ +int sds_struct_write(uint32_t structure_id, unsigned int fld_off, + void *data, size_t size, sds_access_mode_t mode) +{ + int status; + uintptr_t field_base; + struct_header_t *header = NULL; + + if (!data) + return SDS_ERR_INVALID_PARAMS; + + /* Check if a structure with this ID exists */ + status = get_struct_header(structure_id, &header); + if (status != SDS_OK) + return status; + + assert(header); + + if (mode == SDS_ACCESS_MODE_CACHED) + inv_dcache_range((uintptr_t)header, SDS_HEADER_SIZE + size); + + if (!IS_SDS_HEADER_VALID(header)) { + WARN("SDS: Writing to un-finalized structure 0x%x\n", + structure_id); + return SDS_ERR_STRUCT_NOT_FINALIZED; + } + + if ((fld_off + size) > GET_SDS_HEADER_STRUCT_SIZE(header)) + return SDS_ERR_FAIL; + + field_base = (uintptr_t)header + SDS_HEADER_SIZE + fld_off; + if (check_uptr_overflow(field_base, size - 1)) + return SDS_ERR_FAIL; + + /* Copy the required field in the struct */ + memcpy((void *)field_base, data, size); + + if (mode == SDS_ACCESS_MODE_CACHED) + flush_dcache_range((uintptr_t)field_base, size); + + return SDS_OK; +} + +/* + * Initialize the SDS driver. Also verifies the SDS version and sanity of + * the SDS structure headers. + * Returns SDS_OK on success, SDS_ERR_FAIL on error. + */ +int sds_init(void) +{ + sds_mem_base = (uintptr_t)PLAT_ARM_SDS_MEM_BASE; + + if (!IS_SDS_REGION_VALID(sds_mem_base)) { + WARN("SDS: No valid SDS Memory Region found\n"); + return SDS_ERR_FAIL; + } + + if (GET_SDS_REGION_SCHEMA_VERSION(sds_mem_base) + != SDS_REGION_SCH_VERSION) { + WARN("SDS: Unsupported SDS schema version\n"); + return SDS_ERR_FAIL; + } + + sds_mem_size = GET_SDS_REGION_SIZE(sds_mem_base); + if (sds_mem_size > PLAT_ARM_SDS_MEM_SIZE_MAX) { + WARN("SDS: SDS Memory Region exceeds size limit\n"); + return SDS_ERR_FAIL; + } + + INFO("SDS: Detected SDS Memory Region (%zu bytes)\n", sds_mem_size); + + if (validate_sds_struct_headers() != SDS_OK) + return SDS_ERR_FAIL; + + return SDS_OK; +} diff --git a/drivers/arm/css/sds/sds_private.h b/drivers/arm/css/sds/sds_private.h new file mode 100644 index 000000000..2101dd049 --- /dev/null +++ b/drivers/arm/css/sds/sds_private.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SDS_PRIVATE_H +#define SDS_PRIVATE_H + +/* SDS Header defines */ +#define SDS_HEADER_ID_SHIFT 0 +#define SDS_HEADER_ID_WIDTH 16 +#define SDS_HEADER_ID_MASK ((1 << SDS_HEADER_ID_WIDTH) - 1) + +#define SDS_HEADER_MINOR_VERSION_WIDTH 8 +#define SDS_HEADER_MINOR_VERSION_SHIFT 16 +#define SDS_HEADER_MAJOR_VERSION_WIDTH 8 + +#define MAKE_SDS_HEADER_VERSION(major, minor) \ + (((((major) & 0xff) << SDS_HEADER_MINOR_VERSION_WIDTH) | ((minor) & 0xff))) +#define SDS_HEADER_VERSION_MASK \ + ((1 << (SDS_HEADER_MINOR_VERSION_WIDTH + SDS_HEADER_MAJOR_VERSION_WIDTH)) - 1) + +#define SDS_HEADER_VERSION MAKE_SDS_HEADER_VERSION(1, 0) +#define SDS_HEADER_STRUCT_SIZE_WIDTH 23 +#define SDS_HEADER_STRUCT_SIZE_SHIFT 1 +#define SDS_HEADER_STRUCT_SIZE_MASK ((1 << SDS_HEADER_STRUCT_SIZE_WIDTH) - 1) +#define SDS_HEADER_VALID_MASK 0x1 +#define SDS_HEADER_VALID_SHIFT 0 +#define SDS_HEADER_SIZE 0x8 + +/* Arbitrary, 16 bit value that indicates a valid SDS Memory Region */ +#define SDS_REGION_SIGNATURE 0xAA7A +#define SDS_REGION_SIGNATURE_WIDTH 16 +#define SDS_REGION_SIGNATURE_SHIFT 0 +#define SDS_REGION_SIGNATURE_MASK ((1 << SDS_REGION_SIGNATURE_WIDTH) - 1) + +#define SDS_REGION_STRUCT_COUNT_SHIFT 16 +#define SDS_REGION_STRUCT_COUNT_WIDTH 8 +#define SDS_REGION_STRUCT_COUNT_MASK ((1 << SDS_REGION_STRUCT_COUNT_WIDTH) - 1) + +#define SDS_REGION_SCH_MINOR_SHIFT 24 +#define SDS_REGION_SCH_MINOR_WIDTH 4 +#define SDS_REGION_SCH_MINOR_MASK ((1 << SDS_REGION_SCH_MINOR_WIDTH) - 1) + +#define SDS_REGION_SCH_MAJOR_SHIFT 28 +#define SDS_REGION_SCH_MAJOR_WIDTH 4 +#define SDS_REGION_SCH_MAJOR_MASK ((1 << SDS_REGION_SCH_MAJOR_WIDTH) - 1) + +#define SDS_REGION_SCH_VERSION_MASK \ + ((1 << (SDS_REGION_SCH_MINOR_WIDTH + SDS_REGION_SCH_MAJOR_WIDTH)) - 1) + +#define MAKE_SDS_REGION_SCH_VERSION(maj, min) \ + ((((maj) & SDS_REGION_SCH_MAJOR_MASK) << SDS_REGION_SCH_MINOR_WIDTH) | \ + ((min) & SDS_REGION_SCH_MINOR_MASK)) + +#define SDS_REGION_SCH_VERSION MAKE_SDS_REGION_SCH_VERSION(1, 0) +#define SDS_REGION_REGIONSIZE_OFFSET 0x4 +#define SDS_REGION_DESC_SIZE 0x8 + +#ifndef __ASSEMBLY__ +#include <stddef.h> +#include <stdint.h> + +/* Header containing Shared Data Structure metadata */ +typedef struct structure_header { + uint32_t reg[2]; +} struct_header_t; + +#define GET_SDS_HEADER_ID(_header) \ + ((((struct_header_t *)(_header))->reg[0]) & SDS_HEADER_ID_MASK) +#define GET_SDS_HEADER_VERSION(_header) \ + (((((struct_header_t *)(_header))->reg[0]) >> SDS_HEADER_MINOR_VERSION_SHIFT)\ + & SDS_HEADER_VERSION_MASK) +#define GET_SDS_HEADER_STRUCT_SIZE(_header) \ + (((((struct_header_t *)(_header))->reg[1]) >> SDS_HEADER_STRUCT_SIZE_SHIFT)\ + & SDS_HEADER_STRUCT_SIZE_MASK) +#define IS_SDS_HEADER_VALID(_header) \ + ((((struct_header_t *)(_header))->reg[1]) & SDS_HEADER_VALID_MASK) +#define GET_SDS_STRUCT_FIELD(_header, _field_offset) \ + ((((uint8_t *)(_header)) + sizeof(struct_header_t)) + (_field_offset)) + +/* Region Descriptor describing the SDS Memory Region */ +typedef struct region_descriptor { + uint32_t reg[2]; +} region_desc_t; + +#define IS_SDS_REGION_VALID(region) \ + (((((region_desc_t *)(region))->reg[0]) & SDS_REGION_SIGNATURE_MASK) == SDS_REGION_SIGNATURE) +#define GET_SDS_REGION_STRUCTURE_COUNT(region) \ + (((((region_desc_t *)(region))->reg[0]) >> SDS_REGION_STRUCT_COUNT_SHIFT)\ + & SDS_REGION_STRUCT_COUNT_MASK) +#define GET_SDS_REGION_SCHEMA_VERSION(region) \ + (((((region_desc_t *)(region))->reg[0]) >> SDS_REGION_SCH_MINOR_SHIFT)\ + & SDS_REGION_SCH_VERSION_MASK) +#define GET_SDS_REGION_SIZE(region) ((((region_desc_t *)(region))->reg[1])) + +#endif /* __ASSEMBLY__ */ + +#endif /* SDS_PRIVATE_H */ diff --git a/drivers/arm/fvp/fvp_pwrc.c b/drivers/arm/fvp/fvp_pwrc.c new file mode 100644 index 000000000..75a2b66c5 --- /dev/null +++ b/drivers/arm/fvp/fvp_pwrc.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <drivers/arm/fvp/fvp_pwrc.h> +#include <lib/bakery_lock.h> +#include <lib/mmio.h> +#include <plat/arm/common/plat_arm.h> +#include <platform_def.h> + +/* + * TODO: Someday there will be a generic power controller api. At the moment + * each platform has its own pwrc so just exporting functions is fine. + */ +ARM_INSTANTIATE_LOCK; + +unsigned int fvp_pwrc_get_cpu_wkr(u_register_t mpidr) +{ + return PSYSR_WK(fvp_pwrc_read_psysr(mpidr)); +} + +unsigned int fvp_pwrc_read_psysr(u_register_t mpidr) +{ + unsigned int rc; + arm_lock_get(); + mmio_write_32(PWRC_BASE + PSYSR_OFF, (unsigned int) mpidr); + rc = mmio_read_32(PWRC_BASE + PSYSR_OFF); + arm_lock_release(); + return rc; +} + +void fvp_pwrc_write_pponr(u_register_t mpidr) +{ + arm_lock_get(); + mmio_write_32(PWRC_BASE + PPONR_OFF, (unsigned int) mpidr); + arm_lock_release(); +} + +void fvp_pwrc_write_ppoffr(u_register_t mpidr) +{ + arm_lock_get(); + mmio_write_32(PWRC_BASE + PPOFFR_OFF, (unsigned int) mpidr); + arm_lock_release(); +} + +void fvp_pwrc_set_wen(u_register_t mpidr) +{ + arm_lock_get(); + mmio_write_32(PWRC_BASE + PWKUPR_OFF, + (unsigned int) (PWKUPR_WEN | mpidr)); + arm_lock_release(); +} + +void fvp_pwrc_clr_wen(u_register_t mpidr) +{ + arm_lock_get(); + mmio_write_32(PWRC_BASE + PWKUPR_OFF, + (unsigned int) mpidr); + arm_lock_release(); +} + +void fvp_pwrc_write_pcoffr(u_register_t mpidr) +{ + arm_lock_get(); + mmio_write_32(PWRC_BASE + PCOFFR_OFF, (unsigned int) mpidr); + arm_lock_release(); +} + +/* Nothing else to do here apart from initializing the lock */ +void __init plat_arm_pwrc_setup(void) +{ + arm_lock_init(); +} + + + diff --git a/drivers/console/multi_console.c b/drivers/console/multi_console.c index e94de35c7..a13595923 100644 --- a/drivers/console/multi_console.c +++ b/drivers/console/multi_console.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -76,7 +76,7 @@ int console_putc(int c) console_t *console; for (console = console_list; console != NULL; console = console->next) - if (console->flags & console_state) { + if ((console->flags & console_state) && console->putc) { int ret = console->putc(c, console); if ((err == ERROR_NO_VALID_CONSOLE) || (ret < err)) err = ret; @@ -93,7 +93,7 @@ int console_getc(void) do { /* Keep polling while at least one console works correctly. */ for (console = console_list; console != NULL; console = console->next) - if (console->flags & console_state) { + if ((console->flags & console_state) && console->getc) { int ret = console->getc(console); if (ret >= 0) return ret; @@ -111,7 +111,7 @@ int console_flush(void) console_t *console; for (console = console_list; console != NULL; console = console->next) - if (console->flags & console_state) { + if ((console->flags & console_state) && console->flush) { int ret = console->flush(console); if ((err == ERROR_NO_VALID_CONSOLE) || (ret < err)) err = ret; diff --git a/drivers/partition/partition.c b/drivers/partition/partition.c index 630c1d808..6fa3df0f2 100644 --- a/drivers/partition/partition.c +++ b/drivers/partition/partition.c @@ -16,7 +16,7 @@ #include <plat/common/platform.h> static uint8_t mbr_sector[PARTITION_BLOCK_SIZE]; -partition_entry_list_t list; +static partition_entry_list_t list; #if LOG_LEVEL >= LOG_LEVEL_VERBOSE static void dump_entries(int num) diff --git a/drivers/rpi3/gpio/rpi3_gpio.c b/drivers/rpi3/gpio/rpi3_gpio.c new file mode 100644 index 000000000..b39808ff7 --- /dev/null +++ b/drivers/rpi3/gpio/rpi3_gpio.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2019, Linaro Limited + * Copyright (c) 2019, Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <string.h> +#include <assert.h> +#include <lib/mmio.h> +#include <drivers/delay_timer.h> +#include <drivers/rpi3/gpio/rpi3_gpio.h> + +static struct rpi3_gpio_params rpi3_gpio_params; + +static int rpi3_gpio_get_direction(int gpio); +static void rpi3_gpio_set_direction(int gpio, int direction); +static int rpi3_gpio_get_value(int gpio); +static void rpi3_gpio_set_value(int gpio, int value); +static void rpi3_gpio_set_pull(int gpio, int pull); + +static const gpio_ops_t rpi3_gpio_ops = { + .get_direction = rpi3_gpio_get_direction, + .set_direction = rpi3_gpio_set_direction, + .get_value = rpi3_gpio_get_value, + .set_value = rpi3_gpio_set_value, + .set_pull = rpi3_gpio_set_pull, +}; + +/** + * Get selection of GPIO pinmux settings. + * + * @param gpio The pin number of GPIO. From 0 to 53. + * @return The selection of pinmux. RPI3_GPIO_FUNC_INPUT: input, + * RPI3_GPIO_FUNC_OUTPUT: output, + * RPI3_GPIO_FUNC_ALT0: alt-0, + * RPI3_GPIO_FUNC_ALT1: alt-1, + * RPI3_GPIO_FUNC_ALT2: alt-2, + * RPI3_GPIO_FUNC_ALT3: alt-3, + * RPI3_GPIO_FUNC_ALT4: alt-4, + * RPI3_GPIO_FUNC_ALT5: alt-5 + */ +int rpi3_gpio_get_select(int gpio) +{ + int ret; + uintptr_t reg_base = rpi3_gpio_params.reg_base; + int regN = gpio / 10; + int shift = 3 * (gpio % 10); + uintptr_t reg_sel = reg_base + RPI3_GPIO_GPFSEL(regN); + uint32_t sel = mmio_read_32(reg_sel); + + ret = (sel >> shift) & 0x07; + + return ret; +} + +/** + * Set selection of GPIO pinmux settings. + * + * @param gpio The pin number of GPIO. From 0 to 53. + * @param fsel The selection of pinmux. RPI3_GPIO_FUNC_INPUT: input, + * RPI3_GPIO_FUNC_OUTPUT: output, + * RPI3_GPIO_FUNC_ALT0: alt-0, + * RPI3_GPIO_FUNC_ALT1: alt-1, + * RPI3_GPIO_FUNC_ALT2: alt-2, + * RPI3_GPIO_FUNC_ALT3: alt-3, + * RPI3_GPIO_FUNC_ALT4: alt-4, + * RPI3_GPIO_FUNC_ALT5: alt-5 + */ +void rpi3_gpio_set_select(int gpio, int fsel) +{ + uintptr_t reg_base = rpi3_gpio_params.reg_base; + int regN = gpio / 10; + int shift = 3 * (gpio % 10); + uintptr_t reg_sel = reg_base + RPI3_GPIO_GPFSEL(regN); + uint32_t sel = mmio_read_32(reg_sel); + uint32_t mask = U(0x07) << shift; + + sel = (sel & (~mask)) | ((fsel << shift) & mask); + mmio_write_32(reg_sel, sel); +} + +static int rpi3_gpio_get_direction(int gpio) +{ + int result = rpi3_gpio_get_select(gpio); + + if (result == RPI3_GPIO_FUNC_INPUT) + return GPIO_DIR_IN; + else if (result == RPI3_GPIO_FUNC_OUTPUT) + return GPIO_DIR_OUT; + + return GPIO_DIR_IN; +} + +static void rpi3_gpio_set_direction(int gpio, int direction) +{ + switch (direction) { + case GPIO_DIR_IN: + rpi3_gpio_set_select(gpio, RPI3_GPIO_FUNC_INPUT); + break; + case GPIO_DIR_OUT: + rpi3_gpio_set_select(gpio, RPI3_GPIO_FUNC_OUTPUT); + break; + } +} + +static int rpi3_gpio_get_value(int gpio) +{ + uintptr_t reg_base = rpi3_gpio_params.reg_base; + int regN = gpio / 32; + int shift = gpio % 32; + uintptr_t reg_lev = reg_base + RPI3_GPIO_GPLEV(regN); + uint32_t value = mmio_read_32(reg_lev); + + if ((value >> shift) & 0x01) + return GPIO_LEVEL_HIGH; + return GPIO_LEVEL_LOW; +} + +static void rpi3_gpio_set_value(int gpio, int value) +{ + uintptr_t reg_base = rpi3_gpio_params.reg_base; + int regN = gpio / 32; + int shift = gpio % 32; + uintptr_t reg_set = reg_base + RPI3_GPIO_GPSET(regN); + uintptr_t reg_clr = reg_base + RPI3_GPIO_GPSET(regN); + + switch (value) { + case GPIO_LEVEL_LOW: + mmio_write_32(reg_clr, U(1) << shift); + break; + case GPIO_LEVEL_HIGH: + mmio_write_32(reg_set, U(1) << shift); + break; + } +} + +static void rpi3_gpio_set_pull(int gpio, int pull) +{ + uintptr_t reg_base = rpi3_gpio_params.reg_base; + int regN = gpio / 32; + int shift = gpio % 32; + uintptr_t reg_pud = reg_base + RPI3_GPIO_GPPUD; + uintptr_t reg_clk = reg_base + RPI3_GPIO_GPPUDCLK(regN); + + switch (pull) { + case GPIO_PULL_NONE: + mmio_write_32(reg_pud, 0x0); + break; + case GPIO_PULL_UP: + mmio_write_32(reg_pud, 0x2); + break; + case GPIO_PULL_DOWN: + mmio_write_32(reg_pud, 0x1); + break; + } + mdelay(150); + mmio_write_32(reg_clk, U(1) << shift); + mdelay(150); + mmio_write_32(reg_clk, 0x0); + mmio_write_32(reg_pud, 0x0); +} + +void rpi3_gpio_init(struct rpi3_gpio_params *params) +{ + assert(params != 0); + memcpy(&rpi3_gpio_params, params, sizeof(struct rpi3_gpio_params)); + gpio_init(&rpi3_gpio_ops); +} diff --git a/drivers/st/bsec/bsec.c b/drivers/st/bsec/bsec.c new file mode 100644 index 000000000..aaecf1f83 --- /dev/null +++ b/drivers/st/bsec/bsec.c @@ -0,0 +1,913 @@ +/* + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <limits.h> + +#include <libfdt.h> + +#include <platform_def.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/st/bsec.h> +#include <lib/mmio.h> +#include <lib/spinlock.h> + +#define BSEC_IP_VERSION_1_0 0x10 +#define BSEC_COMPAT "st,stm32mp15-bsec" + +#define OTP_ACCESS_SIZE (round_up(OTP_MAX_SIZE, __WORD_BIT) / __WORD_BIT) + +static uint32_t otp_nsec_access[OTP_ACCESS_SIZE] __unused; + +static uint32_t bsec_power_safmem(bool power); + +/* BSEC access protection */ +static spinlock_t bsec_spinlock; +static uintptr_t bsec_base; + +static void bsec_lock(void) +{ + const uint32_t mask = SCTLR_M_BIT | SCTLR_C_BIT; + + /* Lock is currently required only when MMU and cache are enabled */ + if ((read_sctlr() & mask) == mask) { + spin_lock(&bsec_spinlock); + } +} + +static void bsec_unlock(void) +{ + const uint32_t mask = SCTLR_M_BIT | SCTLR_C_BIT; + + /* Unlock is required only when MMU and cache are enabled */ + if ((read_sctlr() & mask) == mask) { + spin_unlock(&bsec_spinlock); + } +} + +static int bsec_get_dt_node(struct dt_node_info *info) +{ + int node; + + node = dt_get_node(info, -1, BSEC_COMPAT); + if (node < 0) { + return -FDT_ERR_NOTFOUND; + } + + return node; +} + +#if defined(IMAGE_BL32) +static void enable_non_secure_access(uint32_t otp) +{ + otp_nsec_access[otp / __WORD_BIT] |= BIT(otp % __WORD_BIT); + + if (bsec_shadow_register(otp) != BSEC_OK) { + panic(); + } +} + +static bool non_secure_can_access(uint32_t otp) +{ + return (otp_nsec_access[otp / __WORD_BIT] & + BIT(otp % __WORD_BIT)) != 0; +} + +static int bsec_dt_otp_nsec_access(void *fdt, int bsec_node) +{ + int bsec_subnode; + + fdt_for_each_subnode(bsec_subnode, fdt, bsec_node) { + const fdt32_t *cuint; + uint32_t reg; + uint32_t i; + uint32_t size; + uint8_t status; + + cuint = fdt_getprop(fdt, bsec_subnode, "reg", NULL); + if (cuint == NULL) { + panic(); + } + + reg = fdt32_to_cpu(*cuint) / sizeof(uint32_t); + if (reg < STM32MP1_UPPER_OTP_START) { + continue; + } + + status = fdt_get_status(bsec_subnode); + if ((status & DT_NON_SECURE) == 0U) { + continue; + } + + size = fdt32_to_cpu(*(cuint + 1)) / sizeof(uint32_t); + + if ((fdt32_to_cpu(*(cuint + 1)) % sizeof(uint32_t)) != 0) { + size++; + } + + for (i = reg; i < (reg + size); i++) { + enable_non_secure_access(i); + } + } + + return 0; +} +#endif + +static uint32_t otp_bank_offset(uint32_t otp) +{ + assert(otp <= STM32MP1_OTP_MAX_ID); + + return ((otp & ~BSEC_OTP_MASK) >> BSEC_OTP_BANK_SHIFT) * + sizeof(uint32_t); +} + +static uint32_t bsec_check_error(uint32_t otp) +{ + uint32_t bit = BIT(otp & BSEC_OTP_MASK); + uint32_t bank = otp_bank_offset(otp); + + if ((mmio_read_32(bsec_base + BSEC_DISTURBED_OFF + bank) & bit) != 0U) { + return BSEC_DISTURBED; + } + + if ((mmio_read_32(bsec_base + BSEC_ERROR_OFF + bank) & bit) != 0U) { + return BSEC_ERROR; + } + + return BSEC_OK; +} + +/* + * bsec_probe: initialize BSEC driver. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_probe(void) +{ + void *fdt; + int node; + struct dt_node_info bsec_info; + + if (fdt_get_address(&fdt) == 0) { + panic(); + } + + node = bsec_get_dt_node(&bsec_info); + if (node < 0) { + panic(); + } + + bsec_base = bsec_info.base; + +#if defined(IMAGE_BL32) + bsec_dt_otp_nsec_access(fdt, node); +#endif + return BSEC_OK; +} + +/* + * bsec_get_base: return BSEC base address. + */ +uint32_t bsec_get_base(void) +{ + return bsec_base; +} + +/* + * bsec_set_config: enable and configure BSEC. + * cfg: pointer to param structure used to set register. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_set_config(struct bsec_config *cfg) +{ + uint32_t value; + int32_t result; + + value = ((((uint32_t)cfg->freq << BSEC_CONF_FRQ_SHIFT) & + BSEC_CONF_FRQ_MASK) | + (((uint32_t)cfg->pulse_width << BSEC_CONF_PRG_WIDTH_SHIFT) & + BSEC_CONF_PRG_WIDTH_MASK) | + (((uint32_t)cfg->tread << BSEC_CONF_TREAD_SHIFT) & + BSEC_CONF_TREAD_MASK)); + + bsec_lock(); + + mmio_write_32(bsec_base + BSEC_OTP_CONF_OFF, value); + + bsec_unlock(); + + result = bsec_power_safmem((bool)cfg->power & + BSEC_CONF_POWER_UP_MASK); + if (result != BSEC_OK) { + return result; + } + + value = ((((uint32_t)cfg->upper_otp_lock << UPPER_OTP_LOCK_SHIFT) & + UPPER_OTP_LOCK_MASK) | + (((uint32_t)cfg->den_lock << DENREG_LOCK_SHIFT) & + DENREG_LOCK_MASK) | + (((uint32_t)cfg->prog_lock << GPLOCK_LOCK_SHIFT) & + GPLOCK_LOCK_MASK)); + + bsec_lock(); + + mmio_write_32(bsec_base + BSEC_OTP_LOCK_OFF, value); + + bsec_unlock(); + + return BSEC_OK; +} + +/* + * bsec_get_config: return config parameters set in BSEC registers. + * cfg: config param return. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_get_config(struct bsec_config *cfg) +{ + uint32_t value; + + if (cfg == NULL) { + return BSEC_INVALID_PARAM; + } + + value = mmio_read_32(bsec_base + BSEC_OTP_CONF_OFF); + cfg->power = (uint8_t)((value & BSEC_CONF_POWER_UP_MASK) >> + BSEC_CONF_POWER_UP_SHIFT); + cfg->freq = (uint8_t)((value & BSEC_CONF_FRQ_MASK) >> + BSEC_CONF_FRQ_SHIFT); + cfg->pulse_width = (uint8_t)((value & BSEC_CONF_PRG_WIDTH_MASK) >> + BSEC_CONF_PRG_WIDTH_SHIFT); + cfg->tread = (uint8_t)((value & BSEC_CONF_TREAD_MASK) >> + BSEC_CONF_TREAD_SHIFT); + + value = mmio_read_32(bsec_base + BSEC_OTP_LOCK_OFF); + cfg->upper_otp_lock = (uint8_t)((value & UPPER_OTP_LOCK_MASK) >> + UPPER_OTP_LOCK_SHIFT); + cfg->den_lock = (uint8_t)((value & DENREG_LOCK_MASK) >> + DENREG_LOCK_SHIFT); + cfg->prog_lock = (uint8_t)((value & GPLOCK_LOCK_MASK) >> + GPLOCK_LOCK_SHIFT); + + return BSEC_OK; +} + +/* + * bsec_shadow_register: copy SAFMEM OTP to BSEC data. + * otp: OTP number. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_shadow_register(uint32_t otp) +{ + uint32_t result; + bool power_up = false; + + if (otp > STM32MP1_OTP_MAX_ID) { + return BSEC_INVALID_PARAM; + } + + /* Check if shadowing of OTP is locked */ + if (bsec_read_sr_lock(otp)) { + VERBOSE("BSEC: OTP %i is locked and will not be refreshed\n", + otp); + } + + if ((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) { + result = bsec_power_safmem(true); + + if (result != BSEC_OK) { + return result; + } + + power_up = true; + } + + bsec_lock(); + + /* Set BSEC_OTP_CTRL_OFF and set ADDR with the OTP value */ + mmio_write_32(bsec_base + BSEC_OTP_CTRL_OFF, otp | BSEC_READ); + + while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) { + ; + } + + result = bsec_check_error(otp); + + bsec_unlock(); + + if (power_up) { + if (bsec_power_safmem(false) != BSEC_OK) { + panic(); + } + } + + return result; +} + +/* + * bsec_read_otp: read an OTP data value. + * val: read value. + * otp: OTP number. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_read_otp(uint32_t *val, uint32_t otp) +{ + uint32_t result; + + if (otp > STM32MP1_OTP_MAX_ID) { + return BSEC_INVALID_PARAM; + } + + bsec_lock(); + + *val = mmio_read_32(bsec_base + BSEC_OTP_DATA_OFF + + (otp * sizeof(uint32_t))); + + result = bsec_check_error(otp); + + bsec_unlock(); + + return result; +} + +/* + * bsec_write_otp: write value in BSEC data register. + * val: value to write. + * otp: OTP number. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_write_otp(uint32_t val, uint32_t otp) +{ + uint32_t result; + + if (otp > STM32MP1_OTP_MAX_ID) { + return BSEC_INVALID_PARAM; + } + + /* Check if programming of OTP is locked */ + if (bsec_read_sw_lock(otp)) { + VERBOSE("BSEC: OTP %i is locked and write will be ignored\n", + otp); + } + + bsec_lock(); + + mmio_write_32(bsec_base + BSEC_OTP_DATA_OFF + + (otp * sizeof(uint32_t)), val); + + result = bsec_check_error(otp); + + bsec_unlock(); + + return result; +} + +/* + * bsec_program_otp: program a bit in SAFMEM after the prog. + * The OTP data is not refreshed. + * val: value to program. + * otp: OTP number. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_program_otp(uint32_t val, uint32_t otp) +{ + uint32_t result; + bool power_up = false; + + if (otp > STM32MP1_OTP_MAX_ID) { + return BSEC_INVALID_PARAM; + } + + /* Check if programming of OTP is locked */ + if (bsec_read_sp_lock(otp)) { + WARN("BSEC: OTP locked, prog will be ignored\n"); + } + + if ((mmio_read_32(bsec_base + BSEC_OTP_LOCK_OFF) & + BIT(BSEC_LOCK_PROGRAM)) != 0U) { + WARN("BSEC: GPLOCK activated, prog will be ignored\n"); + } + + if ((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) { + result = bsec_power_safmem(true); + + if (result != BSEC_OK) { + return result; + } + + power_up = true; + } + + bsec_lock(); + + /* Set value in write register */ + mmio_write_32(bsec_base + BSEC_OTP_WRDATA_OFF, val); + + /* Set BSEC_OTP_CTRL_OFF and set ADDR with the OTP value */ + mmio_write_32(bsec_base + BSEC_OTP_CTRL_OFF, otp | BSEC_WRITE); + + while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) { + ; + } + + if ((bsec_get_status() & BSEC_MODE_PROGFAIL_MASK) != 0U) { + result = BSEC_PROG_FAIL; + } else { + result = bsec_check_error(otp); + } + + bsec_unlock(); + + if (power_up) { + if (bsec_power_safmem(false) != BSEC_OK) { + panic(); + } + } + + return result; +} + +/* + * bsec_permanent_lock_otp: permanent lock of OTP in SAFMEM. + * otp: OTP number. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_permanent_lock_otp(uint32_t otp) +{ + uint32_t result; + bool power_up = false; + uint32_t data; + uint32_t addr; + + if (otp > STM32MP1_OTP_MAX_ID) { + return BSEC_INVALID_PARAM; + } + + if ((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) { + result = bsec_power_safmem(true); + + if (result != BSEC_OK) { + return result; + } + + power_up = true; + } + + if (otp < STM32MP1_UPPER_OTP_START) { + addr = otp >> ADDR_LOWER_OTP_PERLOCK_SHIFT; + data = DATA_LOWER_OTP_PERLOCK_BIT << + ((otp & DATA_LOWER_OTP_PERLOCK_MASK) << 1U); + } else { + addr = (otp >> ADDR_UPPER_OTP_PERLOCK_SHIFT) + 2U; + data = DATA_UPPER_OTP_PERLOCK_BIT << + (otp & DATA_UPPER_OTP_PERLOCK_MASK); + } + + bsec_lock(); + + /* Set value in write register */ + mmio_write_32(bsec_base + BSEC_OTP_WRDATA_OFF, data); + + /* Set BSEC_OTP_CTRL_OFF and set ADDR with the OTP value */ + mmio_write_32(bsec_base + BSEC_OTP_CTRL_OFF, + addr | BSEC_WRITE | BSEC_LOCK); + + while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) { + ; + } + + if ((bsec_get_status() & BSEC_MODE_PROGFAIL_MASK) != 0U) { + result = BSEC_PROG_FAIL; + } else { + result = bsec_check_error(otp); + } + + bsec_unlock(); + + if (power_up) { + if (bsec_power_safmem(false) != BSEC_OK) { + panic(); + } + } + + return result; +} + +/* + * bsec_write_debug_conf: write value in debug feature + * to enable/disable debug service. + * val: value to write. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_write_debug_conf(uint32_t val) +{ + uint32_t result = BSEC_ERROR; + uint32_t masked_val = val & BSEC_DEN_ALL_MSK; + + bsec_lock(); + + mmio_write_32(bsec_base + BSEC_DEN_OFF, masked_val); + + if ((mmio_read_32(bsec_base + BSEC_DEN_OFF) ^ masked_val) == 0U) { + result = BSEC_OK; + } + + bsec_unlock(); + + return result; +} + +/* + * bsec_read_debug_conf: read debug configuration. + */ +uint32_t bsec_read_debug_conf(void) +{ + return mmio_read_32(bsec_base + BSEC_DEN_OFF); +} + +/* + * bsec_get_status: return status register value. + */ +uint32_t bsec_get_status(void) +{ + return mmio_read_32(bsec_base + BSEC_OTP_STATUS_OFF); +} + +/* + * bsec_get_hw_conf: return hardware configuration. + */ +uint32_t bsec_get_hw_conf(void) +{ + return mmio_read_32(bsec_base + BSEC_IPHW_CFG_OFF); +} + +/* + * bsec_get_version: return BSEC version. + */ +uint32_t bsec_get_version(void) +{ + return mmio_read_32(bsec_base + BSEC_IPVR_OFF); +} + +/* + * bsec_get_id: return BSEC ID. + */ +uint32_t bsec_get_id(void) +{ + return mmio_read_32(bsec_base + BSEC_IP_ID_OFF); +} + +/* + * bsec_get_magic_id: return BSEC magic number. + */ +uint32_t bsec_get_magic_id(void) +{ + return mmio_read_32(bsec_base + BSEC_IP_MAGIC_ID_OFF); +} + +/* + * bsec_write_sr_lock: write shadow-read lock. + * otp: OTP number. + * value: value to write in the register. + * Must be always 1. + * return: true if OTP is locked, else false. + */ +bool bsec_write_sr_lock(uint32_t otp, uint32_t value) +{ + bool result = false; + uint32_t bank = otp_bank_offset(otp); + uint32_t bank_value; + uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); + + bsec_lock(); + + bank_value = mmio_read_32(bsec_base + BSEC_SRLOCK_OFF + bank); + + if ((bank_value & otp_mask) == value) { + /* + * In case of write don't need to write, + * the lock is already set. + */ + if (value != 0U) { + result = true; + } + } else { + if (value != 0U) { + bank_value = bank_value | otp_mask; + } else { + bank_value = bank_value & ~otp_mask; + } + + /* + * We can write 0 in all other OTP + * if the lock is activated in one of other OTP. + * Write 0 has no effect. + */ + mmio_write_32(bsec_base + BSEC_SRLOCK_OFF + bank, bank_value); + result = true; + } + + bsec_unlock(); + + return result; +} + +/* + * bsec_read_sr_lock: read shadow-read lock. + * otp: OTP number. + * return: true if otp is locked, else false. + */ +bool bsec_read_sr_lock(uint32_t otp) +{ + uint32_t bank = otp_bank_offset(otp); + uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); + uint32_t bank_value = mmio_read_32(bsec_base + BSEC_SRLOCK_OFF + bank); + + return (bank_value & otp_mask) != 0U; +} + +/* + * bsec_write_sw_lock: write shadow-write lock. + * otp: OTP number. + * value: Value to write in the register. + * Must be always 1. + * return: true if OTP is locked, else false. + */ +bool bsec_write_sw_lock(uint32_t otp, uint32_t value) +{ + bool result = false; + uint32_t bank = otp_bank_offset(otp); + uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); + uint32_t bank_value; + + bsec_lock(); + + bank_value = mmio_read_32(bsec_base + BSEC_SWLOCK_OFF + bank); + + if ((bank_value & otp_mask) == value) { + /* + * In case of write don't need to write, + * the lock is already set. + */ + if (value != 0U) { + result = true; + } + } else { + if (value != 0U) { + bank_value = bank_value | otp_mask; + } else { + bank_value = bank_value & ~otp_mask; + } + + /* + * We can write 0 in all other OTP + * if the lock is activated in one of other OTP. + * Write 0 has no effect. + */ + mmio_write_32(bsec_base + BSEC_SWLOCK_OFF + bank, bank_value); + result = true; + } + + bsec_unlock(); + + return result; +} + +/* + * bsec_read_sw_lock: read shadow-write lock. + * otp: OTP number. + * return: true if OTP is locked, else false. + */ +bool bsec_read_sw_lock(uint32_t otp) +{ + uint32_t bank = otp_bank_offset(otp); + uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); + uint32_t bank_value = mmio_read_32(bsec_base + BSEC_SWLOCK_OFF + bank); + + return (bank_value & otp_mask) != 0U; +} + +/* + * bsec_write_sp_lock: write shadow-program lock. + * otp: OTP number. + * value: Value to write in the register. + * Must be always 1. + * return: true if OTP is locked, else false. + */ +bool bsec_write_sp_lock(uint32_t otp, uint32_t value) +{ + bool result = false; + uint32_t bank = otp_bank_offset(otp); + uint32_t bank_value; + uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); + + bsec_lock(); + + bank_value = mmio_read_32(bsec_base + BSEC_SPLOCK_OFF + bank); + + if ((bank_value & otp_mask) == value) { + /* + * In case of write don't need to write, + * the lock is already set. + */ + if (value != 0U) { + result = true; + } + } else { + if (value != 0U) { + bank_value = bank_value | otp_mask; + } else { + bank_value = bank_value & ~otp_mask; + } + + /* + * We can write 0 in all other OTP + * if the lock is activated in one of other OTP. + * Write 0 has no effect. + */ + mmio_write_32(bsec_base + BSEC_SPLOCK_OFF + bank, bank_value); + result = true; + } + + bsec_unlock(); + + return result; +} + +/* + * bsec_read_sp_lock: read shadow-program lock. + * otp: OTP number. + * return: true if OTP is locked, else false. + */ +bool bsec_read_sp_lock(uint32_t otp) +{ + uint32_t bank = otp_bank_offset(otp); + uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK); + uint32_t bank_value = mmio_read_32(bsec_base + BSEC_SPLOCK_OFF + bank); + + return (bank_value & otp_mask) != 0U; +} + +/* + * bsec_wr_lock: Read permanent lock status. + * otp: OTP number. + * return: true if OTP is locked, else false. + */ +bool bsec_wr_lock(uint32_t otp) +{ + uint32_t bank = otp_bank_offset(otp); + uint32_t lock_bit = BIT(otp & BSEC_OTP_MASK); + + if ((mmio_read_32(bsec_base + BSEC_WRLOCK_OFF + bank) & + lock_bit) != 0U) { + /* + * In case of write don't need to write, + * the lock is already set. + */ + return true; + } + + return false; +} + +/* + * bsec_otp_lock: Lock Upper OTP or Global programming or debug enable + * service: Service to lock see header file. + * value: Value to write must always set to 1 (only use for debug purpose). + * return: BSEC_OK if succeed. + */ +uint32_t bsec_otp_lock(uint32_t service, uint32_t value) +{ + uintptr_t reg = bsec_base + BSEC_OTP_LOCK_OFF; + + switch (service) { + case BSEC_LOCK_UPPER_OTP: + mmio_write_32(reg, value << BSEC_LOCK_UPPER_OTP); + break; + case BSEC_LOCK_DEBUG: + mmio_write_32(reg, value << BSEC_LOCK_DEBUG); + break; + case BSEC_LOCK_PROGRAM: + mmio_write_32(reg, value << BSEC_LOCK_PROGRAM); + break; + default: + return BSEC_INVALID_PARAM; + } + + return BSEC_OK; +} + +/* + * bsec_power_safmem: Activate or deactivate SAFMEM power. + * power: true to power up, false to power down. + * return: BSEC_OK if succeed. + */ +static uint32_t bsec_power_safmem(bool power) +{ + uint32_t register_val; + uint32_t timeout = BSEC_TIMEOUT_VALUE; + + bsec_lock(); + + register_val = mmio_read_32(bsec_base + BSEC_OTP_CONF_OFF); + + if (power) { + register_val |= BSEC_CONF_POWER_UP_MASK; + } else { + register_val &= ~BSEC_CONF_POWER_UP_MASK; + } + + mmio_write_32(bsec_base + BSEC_OTP_CONF_OFF, register_val); + + /* Waiting loop */ + if (power) { + while (((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) && + (timeout != 0U)) { + timeout--; + } + } else { + while (((bsec_get_status() & BSEC_MODE_PWR_MASK) != 0U) && + (timeout != 0U)) { + timeout--; + } + } + + bsec_unlock(); + + if (timeout == 0U) { + return BSEC_TIMEOUT; + } + + return BSEC_OK; +} + +/* + * bsec_mode_is_closed_device: read OTP secure sub-mode. + * return: false if open_device and true of closed_device. + */ +bool bsec_mode_is_closed_device(void) +{ + uint32_t value; + + if ((bsec_shadow_register(DATA0_OTP) != BSEC_OK) || + (bsec_read_otp(&value, DATA0_OTP) != BSEC_OK)) { + return true; + } + + return (value & DATA0_OTP_SECURED) == DATA0_OTP_SECURED; +} + +/* + * bsec_shadow_read_otp: Load OTP from SAFMEM and provide its value + * otp_value: read value. + * word: OTP number. + * return value: BSEC_OK if no error. + */ +uint32_t bsec_shadow_read_otp(uint32_t *otp_value, uint32_t word) +{ + uint32_t result; + + result = bsec_shadow_register(word); + if (result != BSEC_OK) { + ERROR("BSEC: %u Shadowing Error %i\n", word, result); + return result; + } + + result = bsec_read_otp(otp_value, word); + if (result != BSEC_OK) { + ERROR("BSEC: %u Read Error %i\n", word, result); + } + + return result; +} + +/* + * bsec_check_nsec_access_rights: check non-secure access rights to target OTP. + * otp: OTP number. + * return: BSEC_OK if authorized access. + */ +uint32_t bsec_check_nsec_access_rights(uint32_t otp) +{ +#if defined(IMAGE_BL32) + if (otp > STM32MP1_OTP_MAX_ID) { + return BSEC_INVALID_PARAM; + } + + if (otp >= STM32MP1_UPPER_OTP_START) { + /* Check if BSEC is in OTP-SECURED closed_device state. */ + if (bsec_mode_is_closed_device()) { + if (!non_secure_can_access(otp)) { + return BSEC_ERROR; + } + } + } +#endif + + return BSEC_OK; +} + diff --git a/drivers/st/clk/stm32mp1_clkfunc.c b/drivers/st/clk/stm32mp1_clkfunc.c index 1d9227106..19dfe1b25 100644 --- a/drivers/st/clk/stm32mp1_clkfunc.c +++ b/drivers/st/clk/stm32mp1_clkfunc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -32,10 +32,18 @@ const char *stm32mp_osc_node_label[NB_OSC] = { }; /******************************************************************************* + * This function returns the RCC node in the device tree. + ******************************************************************************/ +static int fdt_get_rcc_node(void *fdt) +{ + return fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT); +} + +/******************************************************************************* * This function reads the frequency of an oscillator from its name. * It reads the value indicated inside the device tree. - * Returns 0 if success, and a negative value else. - * If success, value is stored in the second parameter. + * Returns 0 on success, and a negative FDT/ERRNO error code on failure. + * On success, value is stored in the second parameter. ******************************************************************************/ int fdt_osc_read_freq(const char *name, uint32_t *freq) { @@ -127,7 +135,7 @@ bool fdt_osc_read_bool(enum stm32mp_osc_id osc_id, const char *prop_name) /******************************************************************************* * This function reads a value of a oscillator property from its id. - * Returns value if success, and a default value if property not found. + * Returns value on success, and a default value if property not found. * Default value is passed as parameter. ******************************************************************************/ uint32_t fdt_osc_read_uint32_default(enum stm32mp_osc_id osc_id, @@ -240,7 +248,7 @@ int fdt_rcc_read_uint32_array(const char *prop_name, /******************************************************************************* * This function gets the subnode offset in rcc-clk section from its name. * It reads the values indicated inside the device tree. - * Returns offset if success, and a negative value else. + * Returns offset on success, and a negative FDT/ERRNO error code on failure. ******************************************************************************/ int fdt_rcc_subnode_offset(const char *name) { @@ -251,7 +259,7 @@ int fdt_rcc_subnode_offset(const char *name) return -ENOENT; } - node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT); + node = fdt_get_rcc_node(fdt); if (node < 0) { return -FDT_ERR_NOTFOUND; } @@ -280,7 +288,7 @@ const fdt32_t *fdt_rcc_read_prop(const char *prop_name, int *lenp) return NULL; } - node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT); + node = fdt_get_rcc_node(fdt); if (node < 0) { return NULL; } @@ -297,7 +305,7 @@ const fdt32_t *fdt_rcc_read_prop(const char *prop_name, int *lenp) /******************************************************************************* * This function gets the secure status for rcc node. * It reads secure-status in device tree. - * Returns 1 if rcc is available from secure world, 0 else. + * Returns true if rcc is available from secure world, false if not. ******************************************************************************/ bool fdt_get_rcc_secure_status(void) { @@ -308,18 +316,18 @@ bool fdt_get_rcc_secure_status(void) return false; } - node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_COMPAT); + node = fdt_get_rcc_node(fdt); if (node < 0) { return false; } - return fdt_check_secure_status(node); + return (fdt_get_status(node) & DT_SECURE) != 0U; } /******************************************************************************* * This function reads the stgen base address. * It reads the value indicated inside the device tree. - * Returns address if success, and NULL value else. + * Returns address on success, and NULL value on failure. ******************************************************************************/ uintptr_t fdt_get_stgen_base(void) { @@ -347,7 +355,7 @@ uintptr_t fdt_get_stgen_base(void) /******************************************************************************* * This function gets the clock ID of the given node. * It reads the value indicated inside the device tree. - * Returns ID if success, and a negative value else. + * Returns ID on success, and a negative FDT/ERRNO error code on failure. ******************************************************************************/ int fdt_get_clock_id(int node) { diff --git a/drivers/st/ddr/stm32mp1_ddr.c b/drivers/st/ddr/stm32mp1_ddr.c index aca04504f..79aff6e73 100644 --- a/drivers/st/ddr/stm32mp1_ddr.c +++ b/drivers/st/ddr/stm32mp1_ddr.c @@ -1,9 +1,10 @@ /* - * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * Copyright (C) 2018-2019, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +#include <errno.h> #include <stddef.h> #include <platform_def.h> @@ -12,10 +13,10 @@ #include <arch_helpers.h> #include <common/debug.h> #include <drivers/delay_timer.h> +#include <drivers/st/stm32mp_pmic.h> #include <drivers/st/stm32mp1_clk.h> #include <drivers/st/stm32mp1_ddr.h> #include <drivers/st/stm32mp1_ddr_regs.h> -#include <drivers/st/stm32mp1_pmic.h> #include <drivers/st/stm32mp1_pwr.h> #include <drivers/st/stm32mp1_ram.h> #include <drivers/st/stm32mp1_rcc.h> @@ -233,40 +234,67 @@ struct ddr_reg_info { static const struct ddr_reg_info ddr_registers[REG_TYPE_NB] = { [REG_REG] = { - "static", ddr_reg, ARRAY_SIZE(ddr_reg), DDR_BASE + .name = "static", + .desc = ddr_reg, + .size = ARRAY_SIZE(ddr_reg), + .base = DDR_BASE }, [REG_TIMING] = { - "timing", ddr_timing, ARRAY_SIZE(ddr_timing), DDR_BASE + .name = "timing", + .desc = ddr_timing, + .size = ARRAY_SIZE(ddr_timing), + .base = DDR_BASE }, [REG_PERF] = { - "perf", ddr_perf, ARRAY_SIZE(ddr_perf), DDR_BASE + .name = "perf", + .desc = ddr_perf, + .size = ARRAY_SIZE(ddr_perf), + .base = DDR_BASE }, [REG_MAP] = { - "map", ddr_map, ARRAY_SIZE(ddr_map), DDR_BASE + .name = "map", + .desc = ddr_map, + .size = ARRAY_SIZE(ddr_map), + .base = DDR_BASE }, [REGPHY_REG] = { - "static", ddrphy_reg, ARRAY_SIZE(ddrphy_reg), DDRPHY_BASE + .name = "static", + .desc = ddrphy_reg, + .size = ARRAY_SIZE(ddrphy_reg), + .base = DDRPHY_BASE }, [REGPHY_TIMING] = { - "timing", ddrphy_timing, ARRAY_SIZE(ddrphy_timing), DDRPHY_BASE + .name = "timing", + .desc = ddrphy_timing, + .size = ARRAY_SIZE(ddrphy_timing), + .base = DDRPHY_BASE }, [REGPHY_CAL] = { - "cal", ddrphy_cal, ARRAY_SIZE(ddrphy_cal), DDRPHY_BASE + .name = "cal", + .desc = ddrphy_cal, + .size = ARRAY_SIZE(ddrphy_cal), + .base = DDRPHY_BASE }, [REG_DYN] = { - "dyn", ddr_dyn, ARRAY_SIZE(ddr_dyn), DDR_BASE + .name = "dyn", + .desc = ddr_dyn, + .size = ARRAY_SIZE(ddr_dyn), + .base = DDR_BASE }, [REGPHY_DYN] = { - "dyn", ddrphy_dyn, ARRAY_SIZE(ddrphy_dyn), DDRPHY_BASE + .name = "dyn", + .desc = ddrphy_dyn, + .size = ARRAY_SIZE(ddrphy_dyn), + .base = DDRPHY_BASE }, }; -static uint32_t get_base_addr(const struct ddr_info *priv, enum base_type base) +static uintptr_t get_base_addr(const struct ddr_info *priv, enum base_type base) { if (base == DDRPHY_BASE) { - return (uint32_t)priv->phy; + return (uintptr_t)priv->phy; } else { - return (uint32_t)priv->ctl; + return (uintptr_t)priv->ctl; } } @@ -275,21 +303,22 @@ static void set_reg(const struct ddr_info *priv, const void *param) { unsigned int i; - unsigned int *ptr, value; + unsigned int value; enum base_type base = ddr_registers[type].base; - uint32_t base_addr = get_base_addr(priv, base); + uintptr_t base_addr = get_base_addr(priv, base); const struct reg_desc *desc = ddr_registers[type].desc; VERBOSE("init %s\n", ddr_registers[type].name); for (i = 0; i < ddr_registers[type].size; i++) { - ptr = (unsigned int *)(base_addr + desc[i].offset); + uintptr_t ptr = base_addr + desc[i].offset; + if (desc[i].par_offset == INVALID_OFFSET) { ERROR("invalid parameter offset for %s", desc[i].name); panic(); } else { - value = *((uint32_t *)((uint32_t)param + + value = *((uint32_t *)((uintptr_t)param + desc[i].par_offset)); - mmio_write_32((uint32_t)ptr, value); + mmio_write_32(ptr, value); } } } @@ -305,15 +334,15 @@ static void stm32mp1_ddrphy_idone_wait(struct stm32mp1_ddrphy *phy) time0 = start; do { - pgsr = mmio_read_32((uint32_t)&phy->pgsr); + pgsr = mmio_read_32((uintptr_t)&phy->pgsr); time = get_timer(start); if (time != time0) { - VERBOSE(" > [0x%x] pgsr = 0x%x &\n", - (uint32_t)&phy->pgsr, pgsr); - VERBOSE(" [0x%x] pir = 0x%x (time=%x)\n", - (uint32_t)&phy->pir, - mmio_read_32((uint32_t)&phy->pir), - (uint32_t)time); + VERBOSE(" > [0x%lx] pgsr = 0x%x &\n", + (uintptr_t)&phy->pgsr, pgsr); + VERBOSE(" [0x%lx] pir = 0x%x (time=%lx)\n", + (uintptr_t)&phy->pir, + mmio_read_32((uintptr_t)&phy->pir), + time); } time0 = time; @@ -341,18 +370,18 @@ static void stm32mp1_ddrphy_idone_wait(struct stm32mp1_ddrphy *phy) error++; } } while ((pgsr & DDRPHYC_PGSR_IDONE) == 0U && error == 0); - VERBOSE("\n[0x%x] pgsr = 0x%x\n", - (uint32_t)&phy->pgsr, pgsr); + VERBOSE("\n[0x%lx] pgsr = 0x%x\n", + (uintptr_t)&phy->pgsr, pgsr); } static void stm32mp1_ddrphy_init(struct stm32mp1_ddrphy *phy, uint32_t pir) { uint32_t pir_init = pir | DDRPHYC_PIR_INIT; - mmio_write_32((uint32_t)&phy->pir, pir_init); - VERBOSE("[0x%x] pir = 0x%x -> 0x%x\n", - (uint32_t)&phy->pir, pir_init, - mmio_read_32((uint32_t)&phy->pir)); + mmio_write_32((uintptr_t)&phy->pir, pir_init); + VERBOSE("[0x%lx] pir = 0x%x -> 0x%x\n", + (uintptr_t)&phy->pir, pir_init, + mmio_read_32((uintptr_t)&phy->pir)); /* Need to wait 10 configuration clock before start polling */ udelay(10); @@ -364,9 +393,9 @@ static void stm32mp1_ddrphy_init(struct stm32mp1_ddrphy *phy, uint32_t pir) /* Start quasi dynamic register update */ static void stm32mp1_start_sw_done(struct stm32mp1_ddrctl *ctl) { - mmio_clrbits_32((uint32_t)&ctl->swctl, DDRCTRL_SWCTL_SW_DONE); - VERBOSE("[0x%x] swctl = 0x%x\n", - (uint32_t)&ctl->swctl, mmio_read_32((uint32_t)&ctl->swctl)); + mmio_clrbits_32((uintptr_t)&ctl->swctl, DDRCTRL_SWCTL_SW_DONE); + VERBOSE("[0x%lx] swctl = 0x%x\n", + (uintptr_t)&ctl->swctl, mmio_read_32((uintptr_t)&ctl->swctl)); } /* Wait quasi dynamic register update */ @@ -375,15 +404,15 @@ static void stm32mp1_wait_sw_done_ack(struct stm32mp1_ddrctl *ctl) unsigned long start; uint32_t swstat; - mmio_setbits_32((uint32_t)&ctl->swctl, DDRCTRL_SWCTL_SW_DONE); - VERBOSE("[0x%x] swctl = 0x%x\n", - (uint32_t)&ctl->swctl, mmio_read_32((uint32_t)&ctl->swctl)); + mmio_setbits_32((uintptr_t)&ctl->swctl, DDRCTRL_SWCTL_SW_DONE); + VERBOSE("[0x%lx] swctl = 0x%x\n", + (uintptr_t)&ctl->swctl, mmio_read_32((uintptr_t)&ctl->swctl)); start = get_timer(0); do { - swstat = mmio_read_32((uint32_t)&ctl->swstat); - VERBOSE("[0x%x] swstat = 0x%x ", - (uint32_t)&ctl->swstat, swstat); + swstat = mmio_read_32((uintptr_t)&ctl->swstat); + VERBOSE("[0x%lx] swstat = 0x%x ", + (uintptr_t)&ctl->swstat, swstat); VERBOSE("timer in ms 0x%x = start 0x%lx\r", get_timer(0), start); if (get_timer(start) > plat_get_syscnt_freq2()) { @@ -391,8 +420,8 @@ static void stm32mp1_wait_sw_done_ack(struct stm32mp1_ddrctl *ctl) } } while ((swstat & DDRCTRL_SWSTAT_SW_DONE_ACK) == 0U); - VERBOSE("[0x%x] swstat = 0x%x\n", - (uint32_t)&ctl->swstat, swstat); + VERBOSE("[0x%lx] swstat = 0x%x\n", + (uintptr_t)&ctl->swstat, swstat); } /* Wait quasi dynamic register update */ @@ -406,11 +435,11 @@ static void stm32mp1_wait_operating_mode(struct ddr_info *priv, uint32_t mode) start = get_timer(0); for ( ; ; ) { - stat = mmio_read_32((uint32_t)&priv->ctl->stat); + stat = mmio_read_32((uintptr_t)&priv->ctl->stat); operating_mode = stat & DDRCTRL_STAT_OPERATING_MODE_MASK; selref_type = stat & DDRCTRL_STAT_SELFREF_TYPE_MASK; - VERBOSE("[0x%x] stat = 0x%x\n", - (uint32_t)&priv->ctl->stat, stat); + VERBOSE("[0x%lx] stat = 0x%x\n", + (uintptr_t)&priv->ctl->stat, stat); VERBOSE("timer in ms 0x%x = start 0x%lx\r", get_timer(0), start); if (get_timer(start) > plat_get_syscnt_freq2()) { @@ -441,8 +470,8 @@ static void stm32mp1_wait_operating_mode(struct ddr_info *priv, uint32_t mode) } } - VERBOSE("[0x%x] stat = 0x%x\n", - (uint32_t)&priv->ctl->stat, stat); + VERBOSE("[0x%lx] stat = 0x%x\n", + (uintptr_t)&priv->ctl->stat, stat); } /* Mode Register Writes (MRW or MRS) */ @@ -459,7 +488,7 @@ static void stm32mp1_mode_register_write(struct ddr_info *priv, uint8_t addr, * No write should be performed to MRCTRL0 and MRCTRL1 * if MRSTAT.mr_wr_busy = 1. */ - while ((mmio_read_32((uint32_t)&priv->ctl->mrstat) & + while ((mmio_read_32((uintptr_t)&priv->ctl->mrstat) & DDRCTRL_MRSTAT_MR_WR_BUSY) != 0U) { ; } @@ -472,14 +501,14 @@ static void stm32mp1_mode_register_write(struct ddr_info *priv, uint8_t addr, DDRCTRL_MRCTRL0_MR_RANK_ALL | (((uint32_t)addr << DDRCTRL_MRCTRL0_MR_ADDR_SHIFT) & DDRCTRL_MRCTRL0_MR_ADDR_MASK); - mmio_write_32((uint32_t)&priv->ctl->mrctrl0, mrctrl0); - VERBOSE("[0x%x] mrctrl0 = 0x%x (0x%x)\n", - (uint32_t)&priv->ctl->mrctrl0, - mmio_read_32((uint32_t)&priv->ctl->mrctrl0), mrctrl0); - mmio_write_32((uint32_t)&priv->ctl->mrctrl1, data); - VERBOSE("[0x%x] mrctrl1 = 0x%x\n", - (uint32_t)&priv->ctl->mrctrl1, - mmio_read_32((uint32_t)&priv->ctl->mrctrl1)); + mmio_write_32((uintptr_t)&priv->ctl->mrctrl0, mrctrl0); + VERBOSE("[0x%lx] mrctrl0 = 0x%x (0x%x)\n", + (uintptr_t)&priv->ctl->mrctrl0, + mmio_read_32((uintptr_t)&priv->ctl->mrctrl0), mrctrl0); + mmio_write_32((uintptr_t)&priv->ctl->mrctrl1, data); + VERBOSE("[0x%lx] mrctrl1 = 0x%x\n", + (uintptr_t)&priv->ctl->mrctrl1, + mmio_read_32((uintptr_t)&priv->ctl->mrctrl1)); /* * 3. In a separate APB transaction, write the MRCTRL0.mr_wr to 1. This @@ -489,22 +518,22 @@ static void stm32mp1_mode_register_write(struct ddr_info *priv, uint8_t addr, * initiated until it is deasserted. */ mrctrl0 |= DDRCTRL_MRCTRL0_MR_WR; - mmio_write_32((uint32_t)&priv->ctl->mrctrl0, mrctrl0); + mmio_write_32((uintptr_t)&priv->ctl->mrctrl0, mrctrl0); - while ((mmio_read_32((uint32_t)&priv->ctl->mrstat) & + while ((mmio_read_32((uintptr_t)&priv->ctl->mrstat) & DDRCTRL_MRSTAT_MR_WR_BUSY) != 0U) { ; } - VERBOSE("[0x%x] mrctrl0 = 0x%x\n", - (uint32_t)&priv->ctl->mrctrl0, mrctrl0); + VERBOSE("[0x%lx] mrctrl0 = 0x%x\n", + (uintptr_t)&priv->ctl->mrctrl0, mrctrl0); } /* Switch DDR3 from DLL-on to DLL-off */ static void stm32mp1_ddr3_dll_off(struct ddr_info *priv) { - uint32_t mr1 = mmio_read_32((uint32_t)&priv->phy->mr1); - uint32_t mr2 = mmio_read_32((uint32_t)&priv->phy->mr2); + uint32_t mr1 = mmio_read_32((uintptr_t)&priv->phy->mr1); + uint32_t mr2 = mmio_read_32((uintptr_t)&priv->phy->mr2); uint32_t dbgcam; VERBOSE("mr1: 0x%x\n", mr1); @@ -514,10 +543,10 @@ static void stm32mp1_ddr3_dll_off(struct ddr_info *priv) * 1. Set the DBG1.dis_hif = 1. * This prevents further reads/writes being received on the HIF. */ - mmio_setbits_32((uint32_t)&priv->ctl->dbg1, DDRCTRL_DBG1_DIS_HIF); - VERBOSE("[0x%x] dbg1 = 0x%x\n", - (uint32_t)&priv->ctl->dbg1, - mmio_read_32((uint32_t)&priv->ctl->dbg1)); + mmio_setbits_32((uintptr_t)&priv->ctl->dbg1, DDRCTRL_DBG1_DIS_HIF); + VERBOSE("[0x%lx] dbg1 = 0x%x\n", + (uintptr_t)&priv->ctl->dbg1, + mmio_read_32((uintptr_t)&priv->ctl->dbg1)); /* * 2. Ensure all commands have been flushed from the uMCTL2 by polling @@ -528,9 +557,9 @@ static void stm32mp1_ddr3_dll_off(struct ddr_info *priv) * DBGCAM.dbg_hpr_q_depth = 0. */ do { - dbgcam = mmio_read_32((uint32_t)&priv->ctl->dbgcam); - VERBOSE("[0x%x] dbgcam = 0x%x\n", - (uint32_t)&priv->ctl->dbgcam, dbgcam); + dbgcam = mmio_read_32((uintptr_t)&priv->ctl->dbgcam); + VERBOSE("[0x%lx] dbgcam = 0x%x\n", + (uintptr_t)&priv->ctl->dbgcam, dbgcam); } while ((((dbgcam & DDRCTRL_DBGCAM_DATA_PIPELINE_EMPTY) == DDRCTRL_DBGCAM_DATA_PIPELINE_EMPTY)) && ((dbgcam & DDRCTRL_DBGCAM_DBG_Q_DEPTH) == 0U)); @@ -574,11 +603,11 @@ static void stm32mp1_ddr3_dll_off(struct ddr_info *priv) * PWRCTL.selfref_sw = 1, and polling STAT.operating_mode to ensure * the DDRC has entered self-refresh. */ - mmio_setbits_32((uint32_t)&priv->ctl->pwrctl, + mmio_setbits_32((uintptr_t)&priv->ctl->pwrctl, DDRCTRL_PWRCTL_SELFREF_SW); - VERBOSE("[0x%x] pwrctl = 0x%x\n", - (uint32_t)&priv->ctl->pwrctl, - mmio_read_32((uint32_t)&priv->ctl->pwrctl)); + VERBOSE("[0x%lx] pwrctl = 0x%x\n", + (uintptr_t)&priv->ctl->pwrctl, + mmio_read_32((uintptr_t)&priv->ctl->pwrctl)); /* * 8. Wait until STAT.operating_mode[1:0]==11 indicating that the @@ -594,10 +623,10 @@ static void stm32mp1_ddr3_dll_off(struct ddr_info *priv) */ stm32mp1_start_sw_done(priv->ctl); - mmio_setbits_32((uint32_t)&priv->ctl->mstr, DDRCTRL_MSTR_DLL_OFF_MODE); - VERBOSE("[0x%x] mstr = 0x%x\n", - (uint32_t)&priv->ctl->mstr, - mmio_read_32((uint32_t)&priv->ctl->mstr)); + mmio_setbits_32((uintptr_t)&priv->ctl->mstr, DDRCTRL_MSTR_DLL_OFF_MODE); + VERBOSE("[0x%lx] mstr = 0x%x\n", + (uintptr_t)&priv->ctl->mstr, + mmio_read_32((uintptr_t)&priv->ctl->mstr)); stm32mp1_wait_sw_done_ack(priv->ctl); @@ -611,26 +640,26 @@ static void stm32mp1_ddr3_dll_off(struct ddr_info *priv) /* Change Bypass Mode Frequency Range */ if (stm32mp1_clk_get_rate(DDRPHYC) < 100000000U) { - mmio_clrbits_32((uint32_t)&priv->phy->dllgcr, + mmio_clrbits_32((uintptr_t)&priv->phy->dllgcr, DDRPHYC_DLLGCR_BPS200); } else { - mmio_setbits_32((uint32_t)&priv->phy->dllgcr, + mmio_setbits_32((uintptr_t)&priv->phy->dllgcr, DDRPHYC_DLLGCR_BPS200); } - mmio_setbits_32((uint32_t)&priv->phy->acdllcr, DDRPHYC_ACDLLCR_DLLDIS); + mmio_setbits_32((uintptr_t)&priv->phy->acdllcr, DDRPHYC_ACDLLCR_DLLDIS); - mmio_setbits_32((uint32_t)&priv->phy->dx0dllcr, + mmio_setbits_32((uintptr_t)&priv->phy->dx0dllcr, DDRPHYC_DXNDLLCR_DLLDIS); - mmio_setbits_32((uint32_t)&priv->phy->dx1dllcr, + mmio_setbits_32((uintptr_t)&priv->phy->dx1dllcr, DDRPHYC_DXNDLLCR_DLLDIS); - mmio_setbits_32((uint32_t)&priv->phy->dx2dllcr, + mmio_setbits_32((uintptr_t)&priv->phy->dx2dllcr, DDRPHYC_DXNDLLCR_DLLDIS); - mmio_setbits_32((uint32_t)&priv->phy->dx3dllcr, + mmio_setbits_32((uintptr_t)&priv->phy->dx3dllcr, DDRPHYC_DXNDLLCR_DLLDIS); /* 12. Exit the self-refresh state by setting PWRCTL.selfref_sw = 0. */ - mmio_clrbits_32((uint32_t)&priv->ctl->pwrctl, + mmio_clrbits_32((uintptr_t)&priv->ctl->pwrctl, DDRCTRL_PWRCTL_SELFREF_SW); stm32mp1_wait_operating_mode(priv, DDRCTRL_STAT_OPERATING_MODE_NORMAL); @@ -646,20 +675,20 @@ static void stm32mp1_ddr3_dll_off(struct ddr_info *priv) */ /* 15. Write DBG1.dis_hif = 0 to re-enable reads and writes. */ - mmio_clrbits_32((uint32_t)&priv->ctl->dbg1, DDRCTRL_DBG1_DIS_HIF); - VERBOSE("[0x%x] dbg1 = 0x%x\n", - (uint32_t)&priv->ctl->dbg1, - mmio_read_32((uint32_t)&priv->ctl->dbg1)); + mmio_clrbits_32((uintptr_t)&priv->ctl->dbg1, DDRCTRL_DBG1_DIS_HIF); + VERBOSE("[0x%lx] dbg1 = 0x%x\n", + (uintptr_t)&priv->ctl->dbg1, + mmio_read_32((uintptr_t)&priv->ctl->dbg1)); } static void stm32mp1_refresh_disable(struct stm32mp1_ddrctl *ctl) { stm32mp1_start_sw_done(ctl); /* Quasi-dynamic register update*/ - mmio_setbits_32((uint32_t)&ctl->rfshctl3, + mmio_setbits_32((uintptr_t)&ctl->rfshctl3, DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH); - mmio_clrbits_32((uint32_t)&ctl->pwrctl, DDRCTRL_PWRCTL_POWERDOWN_EN); - mmio_clrbits_32((uint32_t)&ctl->dfimisc, + mmio_clrbits_32((uintptr_t)&ctl->pwrctl, DDRCTRL_PWRCTL_POWERDOWN_EN); + mmio_clrbits_32((uintptr_t)&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); stm32mp1_wait_sw_done_ack(ctl); } @@ -669,14 +698,14 @@ static void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl, { stm32mp1_start_sw_done(ctl); if ((rfshctl3 & DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH) == 0U) { - mmio_clrbits_32((uint32_t)&ctl->rfshctl3, + mmio_clrbits_32((uintptr_t)&ctl->rfshctl3, DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH); } if ((pwrctl & DDRCTRL_PWRCTL_POWERDOWN_EN) != 0U) { - mmio_setbits_32((uint32_t)&ctl->pwrctl, + mmio_setbits_32((uintptr_t)&ctl->pwrctl, DDRCTRL_PWRCTL_POWERDOWN_EN); } - mmio_setbits_32((uint32_t)&ctl->dfimisc, + mmio_setbits_32((uintptr_t)&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); stm32mp1_wait_sw_done_ack(ctl); } @@ -694,12 +723,14 @@ void stm32mp1_ddr_init(struct ddr_info *priv, struct stm32mp1_ddr_config *config) { uint32_t pir; - int ret; + int ret = -EINVAL; if ((config->c_reg.mstr & DDRCTRL_MSTR_DDR3) != 0U) { ret = board_ddr_power_init(STM32MP_DDR3); - } else { + } else if ((config->c_reg.mstr & DDRCTRL_MSTR_LPDDR2) != 0U) { ret = board_ddr_power_init(STM32MP_LPDDR2); + } else { + ERROR("DDR type not supported\n"); } if (ret != 0) { @@ -707,7 +738,7 @@ void stm32mp1_ddr_init(struct ddr_info *priv, } VERBOSE("name = %s\n", config->info.name); - VERBOSE("speed = %d MHz\n", config->info.speed); + VERBOSE("speed = %d kHz\n", config->info.speed); VERBOSE("size = 0x%x\n", config->info.size); /* DDR INIT SEQUENCE */ @@ -746,11 +777,11 @@ void stm32mp1_ddr_init(struct ddr_info *priv, /* 1.5. initialize registers ddr_umctl2 */ /* Stop uMCTL2 before PHY is ready */ - mmio_clrbits_32((uint32_t)&priv->ctl->dfimisc, + mmio_clrbits_32((uintptr_t)&priv->ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); - VERBOSE("[0x%x] dfimisc = 0x%x\n", - (uint32_t)&priv->ctl->dfimisc, - mmio_read_32((uint32_t)&priv->ctl->dfimisc)); + VERBOSE("[0x%lx] dfimisc = 0x%x\n", + (uintptr_t)&priv->ctl->dfimisc, + mmio_read_32((uintptr_t)&priv->ctl->dfimisc)); set_reg(priv, REG_REG, &config->c_reg); @@ -759,23 +790,23 @@ void stm32mp1_ddr_init(struct ddr_info *priv, (DDRCTRL_MSTR_DDR3 | DDRCTRL_MSTR_DLL_OFF_MODE)) == (DDRCTRL_MSTR_DDR3 | DDRCTRL_MSTR_DLL_OFF_MODE)) { VERBOSE("deactivate DLL OFF in mstr\n"); - mmio_clrbits_32((uint32_t)&priv->ctl->mstr, + mmio_clrbits_32((uintptr_t)&priv->ctl->mstr, DDRCTRL_MSTR_DLL_OFF_MODE); - VERBOSE("[0x%x] mstr = 0x%x\n", - (uint32_t)&priv->ctl->mstr, - mmio_read_32((uint32_t)&priv->ctl->mstr)); + VERBOSE("[0x%lx] mstr = 0x%x\n", + (uintptr_t)&priv->ctl->mstr, + mmio_read_32((uintptr_t)&priv->ctl->mstr)); } set_reg(priv, REG_TIMING, &config->c_timing); set_reg(priv, REG_MAP, &config->c_map); /* Skip CTRL init, SDRAM init is done by PHY PUBL */ - mmio_clrsetbits_32((uint32_t)&priv->ctl->init0, + mmio_clrsetbits_32((uintptr_t)&priv->ctl->init0, DDRCTRL_INIT0_SKIP_DRAM_INIT_MASK, DDRCTRL_INIT0_SKIP_DRAM_INIT_NORMAL); - VERBOSE("[0x%x] init0 = 0x%x\n", - (uint32_t)&priv->ctl->init0, - mmio_read_32((uint32_t)&priv->ctl->init0)); + VERBOSE("[0x%lx] init0 = 0x%x\n", + (uintptr_t)&priv->ctl->init0, + mmio_read_32((uintptr_t)&priv->ctl->init0)); set_reg(priv, REG_PERF, &config->c_perf); @@ -797,10 +828,10 @@ void stm32mp1_ddr_init(struct ddr_info *priv, (DDRCTRL_MSTR_DDR3 | DDRCTRL_MSTR_DLL_OFF_MODE)) == (DDRCTRL_MSTR_DDR3 | DDRCTRL_MSTR_DLL_OFF_MODE)) { VERBOSE("deactivate DLL OFF in mr1\n"); - mmio_clrbits_32((uint32_t)&priv->phy->mr1, BIT(0)); - VERBOSE("[0x%x] mr1 = 0x%x\n", - (uint32_t)&priv->phy->mr1, - mmio_read_32((uint32_t)&priv->phy->mr1)); + mmio_clrbits_32((uintptr_t)&priv->phy->mr1, BIT(0)); + VERBOSE("[0x%lx] mr1 = 0x%x\n", + (uintptr_t)&priv->phy->mr1, + mmio_read_32((uintptr_t)&priv->phy->mr1)); } /* @@ -830,11 +861,11 @@ void stm32mp1_ddr_init(struct ddr_info *priv, */ stm32mp1_start_sw_done(priv->ctl); - mmio_setbits_32((uint32_t)&priv->ctl->dfimisc, + mmio_setbits_32((uintptr_t)&priv->ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); - VERBOSE("[0x%x] dfimisc = 0x%x\n", - (uint32_t)&priv->ctl->dfimisc, - mmio_read_32((uint32_t)&priv->ctl->dfimisc)); + VERBOSE("[0x%lx] dfimisc = 0x%x\n", + (uintptr_t)&priv->ctl->dfimisc, + mmio_read_32((uintptr_t)&priv->ctl->dfimisc)); stm32mp1_wait_sw_done_ack(priv->ctl); @@ -884,14 +915,16 @@ void stm32mp1_ddr_init(struct ddr_info *priv, config->c_reg.pwrctl); /* Enable uMCTL2 AXI port 0 */ - mmio_setbits_32((uint32_t)&priv->ctl->pctrl_0, DDRCTRL_PCTRL_N_PORT_EN); - VERBOSE("[0x%x] pctrl_0 = 0x%x\n", - (uint32_t)&priv->ctl->pctrl_0, - mmio_read_32((uint32_t)&priv->ctl->pctrl_0)); + mmio_setbits_32((uintptr_t)&priv->ctl->pctrl_0, + DDRCTRL_PCTRL_N_PORT_EN); + VERBOSE("[0x%lx] pctrl_0 = 0x%x\n", + (uintptr_t)&priv->ctl->pctrl_0, + mmio_read_32((uintptr_t)&priv->ctl->pctrl_0)); /* Enable uMCTL2 AXI port 1 */ - mmio_setbits_32((uint32_t)&priv->ctl->pctrl_1, DDRCTRL_PCTRL_N_PORT_EN); - VERBOSE("[0x%x] pctrl_1 = 0x%x\n", - (uint32_t)&priv->ctl->pctrl_1, - mmio_read_32((uint32_t)&priv->ctl->pctrl_1)); + mmio_setbits_32((uintptr_t)&priv->ctl->pctrl_1, + DDRCTRL_PCTRL_N_PORT_EN); + VERBOSE("[0x%lx] pctrl_1 = 0x%x\n", + (uintptr_t)&priv->ctl->pctrl_1, + mmio_read_32((uintptr_t)&priv->ctl->pctrl_1)); } diff --git a/drivers/st/ddr/stm32mp1_ram.c b/drivers/st/ddr/stm32mp1_ram.c index 127b6c7c1..e65fbeaa2 100644 --- a/drivers/st/ddr/stm32mp1_ram.c +++ b/drivers/st/ddr/stm32mp1_ram.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + * Copyright (C) 2018-2019, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ @@ -25,7 +25,7 @@ static struct ddr_info ddr_priv_data; -int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint16_t mem_speed) +int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint32_t mem_speed) { unsigned long ddrphy_clk, ddr_clk, mem_speed_hz; @@ -33,10 +33,10 @@ int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint16_t mem_speed) ddrphy_clk = stm32mp1_clk_get_rate(DDRPHYC); - VERBOSE("DDR: mem_speed (%d MHz), RCC %ld MHz\n", - mem_speed, ddrphy_clk / 1000U / 1000U); + VERBOSE("DDR: mem_speed (%d kHz), RCC %ld kHz\n", + mem_speed, ddrphy_clk / 1000U); - mem_speed_hz = (uint32_t)mem_speed * 1000U * 1000U; + mem_speed_hz = mem_speed * 1000U; /* Max 10% frequency delta */ if (ddrphy_clk > mem_speed_hz) { @@ -44,9 +44,9 @@ int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint16_t mem_speed) } else { ddr_clk = mem_speed_hz - ddrphy_clk; } - if (ddr_clk > mem_speed_hz) { - ERROR("DDR expected freq %d MHz, current is %ld MHz\n", - mem_speed, ddrphy_clk / 1000U / 1000U); + if (ddr_clk > (mem_speed_hz / 10)) { + ERROR("DDR expected freq %d kHz, current is %ld kHz\n", + mem_speed, ddrphy_clk / 1000U); return -1; } return 0; @@ -208,11 +208,16 @@ static int stm32mp1_ddr_setup(void) return -EINVAL; } - config.info.speed = - (uint16_t)fdt_read_uint32_default(node, "st,mem-speed", - STM32MP1_DDR_SPEED_DFLT); - config.info.size = fdt_read_uint32_default(node, "st,mem-size", - STM32MP1_DDR_SIZE_DFLT); + config.info.speed = fdt_read_uint32_default(node, "st,mem-speed", 0); + if (!config.info.speed) { + VERBOSE("%s: no st,mem-speed\n", __func__); + return -EINVAL; + } + config.info.size = fdt_read_uint32_default(node, "st,mem-size", 0); + if (!config.info.size) { + VERBOSE("%s: no st,mem-size\n", __func__); + return -EINVAL; + } config.info.name = fdt_getprop(fdt, node, "st,mem-name", &len); if (config.info.name == NULL) { VERBOSE("%s: no st,mem-name\n", __func__); @@ -222,7 +227,7 @@ static int stm32mp1_ddr_setup(void) for (idx = 0; idx < ARRAY_SIZE(param); idx++) { ret = fdt_read_uint32_array(node, param[idx].name, - (void *)((uint32_t)&config + + (void *)((uintptr_t)&config + param[idx].offset), param[idx].size); @@ -261,8 +266,8 @@ static int stm32mp1_ddr_setup(void) VERBOSE("%s : ram size(%x, %x)\n", __func__, (uint32_t)priv->info.base, (uint32_t)priv->info.size); - dcsw_op_all(DC_OP_CISW); write_sctlr(read_sctlr() & ~SCTLR_C_BIT); + dcsw_op_all(DC_OP_CISW); uret = ddr_test_data_bus(); if (uret != 0U) { diff --git a/drivers/st/gpio/stm32_gpio.c b/drivers/st/gpio/stm32_gpio.c index 9591e3738..d217c4501 100644 --- a/drivers/st/gpio/stm32_gpio.c +++ b/drivers/st/gpio/stm32_gpio.c @@ -1,87 +1,276 @@ /* - * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved + * Copyright (c) 2016-2019, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ +#include <assert.h> +#include <errno.h> #include <stdbool.h> +#include <libfdt.h> + +#include <platform_def.h> + #include <common/bl_common.h> #include <common/debug.h> #include <drivers/st/stm32_gpio.h> +#include <drivers/st/stm32mp1_clk.h> +#include <drivers/st/stm32mp1_clkfunc.h> #include <lib/mmio.h> +#include <lib/utils_def.h> -static bool check_gpio(uint32_t bank, uint32_t pin) +#define DT_GPIO_BANK_SHIFT 12 +#define DT_GPIO_BANK_MASK GENMASK(16, 12) +#define DT_GPIO_PIN_SHIFT 8 +#define DT_GPIO_PIN_MASK GENMASK(11, 8) +#define DT_GPIO_MODE_MASK GENMASK(7, 0) + +/******************************************************************************* + * This function gets GPIO bank node in DT. + * Returns node offset if status is okay in DT, else return 0 + ******************************************************************************/ +static int ckeck_gpio_bank(void *fdt, uint32_t bank, int pinctrl_node) { - if (pin > GPIO_PIN_MAX) { - ERROR("%s: wrong pin number (%d)\n", __func__, pin); - return false; - } + int pinctrl_subnode; + uint32_t bank_offset = stm32_get_gpio_bank_offset(bank); - if ((bank > GPIO_BANK_K) && (bank != GPIO_BANK_Z)) { - ERROR("%s: wrong GPIO bank number (%d)\n", __func__, bank); - return false; + fdt_for_each_subnode(pinctrl_subnode, fdt, pinctrl_node) { + const fdt32_t *cuint; + + if (fdt_getprop(fdt, pinctrl_subnode, + "gpio-controller", NULL) == NULL) { + continue; + } + + cuint = fdt_getprop(fdt, pinctrl_subnode, "reg", NULL); + if (cuint == NULL) { + continue; + } + + if ((fdt32_to_cpu(*cuint) == bank_offset) && + (fdt_get_status(pinctrl_subnode) != DT_DISABLED)) { + return pinctrl_subnode; + } } - return true; + return 0; } -void set_gpio(uint32_t bank, uint32_t pin, uint32_t mode, uint32_t speed, - uint32_t pull, uint32_t alternate) +/******************************************************************************* + * This function gets the pin settings from DT information. + * When analyze and parsing is done, set the GPIO registers. + * Returns 0 on success and a negative FDT error code on failure. + ******************************************************************************/ +static int dt_set_gpio_config(void *fdt, int node, uint8_t status) { - volatile uint32_t bank_address; + const fdt32_t *cuint, *slewrate; + int len; + int pinctrl_node; + uint32_t i; + uint32_t speed = GPIO_SPEED_LOW; + uint32_t pull = GPIO_NO_PULL; - if (!check_gpio(bank, pin)) { - return; + cuint = fdt_getprop(fdt, node, "pinmux", &len); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; } - if (bank == GPIO_BANK_Z) { - bank_address = STM32_GPIOZ_BANK; + pinctrl_node = fdt_parent_offset(fdt, fdt_parent_offset(fdt, node)); + if (pinctrl_node < 0) { + return -FDT_ERR_NOTFOUND; + } + + slewrate = fdt_getprop(fdt, node, "slew-rate", NULL); + if (slewrate != NULL) { + speed = fdt32_to_cpu(*slewrate); + } + + if (fdt_getprop(fdt, node, "bias-pull-up", NULL) != NULL) { + pull = GPIO_PULL_UP; + } else if (fdt_getprop(fdt, node, "bias-pull-down", NULL) != NULL) { + pull = GPIO_PULL_DOWN; } else { - bank_address = STM32_GPIOA_BANK + - (bank * STM32_GPIO_BANK_OFFSET); + VERBOSE("No bias configured in node %d\n", node); + } + + for (i = 0U; i < ((uint32_t)len / sizeof(uint32_t)); i++) { + uint32_t pincfg; + uint32_t bank; + uint32_t pin; + uint32_t mode; + uint32_t alternate = GPIO_ALTERNATE_(0); + int bank_node; + int clk; + + pincfg = fdt32_to_cpu(*cuint); + cuint++; + + bank = (pincfg & DT_GPIO_BANK_MASK) >> DT_GPIO_BANK_SHIFT; + + pin = (pincfg & DT_GPIO_PIN_MASK) >> DT_GPIO_PIN_SHIFT; + + mode = pincfg & DT_GPIO_MODE_MASK; + + switch (mode) { + case 0: + mode = GPIO_MODE_INPUT; + break; + case 1 ... 16: + alternate = mode - 1U; + mode = GPIO_MODE_ALTERNATE; + break; + case 17: + mode = GPIO_MODE_ANALOG; + break; + default: + mode = GPIO_MODE_OUTPUT; + break; + } + + if (fdt_getprop(fdt, node, "drive-open-drain", NULL) != NULL) { + mode |= GPIO_OPEN_DRAIN; + } + + bank_node = ckeck_gpio_bank(fdt, bank, pinctrl_node); + if (bank_node == 0) { + ERROR("PINCTRL inconsistent in DT\n"); + panic(); + } + + clk = fdt_get_clock_id(bank_node); + if (clk < 0) { + return -FDT_ERR_NOTFOUND; + } + + /* Platform knows the clock: assert it is okay */ + assert((unsigned long)clk == stm32_get_gpio_bank_clock(bank)); + + set_gpio(bank, pin, mode, speed, pull, alternate, status); + } + + return 0; +} + +/******************************************************************************* + * This function gets the pin settings from DT information. + * When analyze and parsing is done, set the GPIO registers. + * Returns 0 on success and a negative FDT/ERRNO error code on failure. + ******************************************************************************/ +int dt_set_pinctrl_config(int node) +{ + const fdt32_t *cuint; + int lenp = 0; + uint32_t i; + uint8_t status = fdt_get_status(node); + void *fdt; + + if (fdt_get_address(&fdt) == 0) { + return -ENOENT; + } + + if (status == DT_DISABLED) { + return -FDT_ERR_NOTFOUND; + } + + cuint = fdt_getprop(fdt, node, "pinctrl-0", &lenp); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; } - mmio_clrbits_32(bank_address + GPIO_MODE_OFFSET, + for (i = 0; i < ((uint32_t)lenp / 4U); i++) { + int p_node, p_subnode; + + p_node = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*cuint)); + if (p_node < 0) { + return -FDT_ERR_NOTFOUND; + } + + fdt_for_each_subnode(p_subnode, fdt, p_node) { + int ret = dt_set_gpio_config(fdt, p_subnode, status); + + if (ret < 0) { + return ret; + } + } + + cuint++; + } + + return 0; +} + +void set_gpio(uint32_t bank, uint32_t pin, uint32_t mode, uint32_t speed, + uint32_t pull, uint32_t alternate, uint8_t status) +{ + uintptr_t base = stm32_get_gpio_bank_base(bank); + unsigned long clock = stm32_get_gpio_bank_clock(bank); + + assert(pin <= GPIO_PIN_MAX); + + stm32mp1_clk_enable(clock); + + mmio_clrbits_32(base + GPIO_MODE_OFFSET, ((uint32_t)GPIO_MODE_MASK << (pin << 1))); - mmio_setbits_32(bank_address + GPIO_MODE_OFFSET, + mmio_setbits_32(base + GPIO_MODE_OFFSET, (mode & ~GPIO_OPEN_DRAIN) << (pin << 1)); if ((mode & GPIO_OPEN_DRAIN) != 0U) { - mmio_setbits_32(bank_address + GPIO_TYPE_OFFSET, - BIT(pin)); + mmio_setbits_32(base + GPIO_TYPE_OFFSET, BIT(pin)); + } else { + mmio_clrbits_32(base + GPIO_TYPE_OFFSET, BIT(pin)); } - mmio_clrbits_32(bank_address + GPIO_SPEED_OFFSET, + mmio_clrbits_32(base + GPIO_SPEED_OFFSET, ((uint32_t)GPIO_SPEED_MASK << (pin << 1))); - mmio_setbits_32(bank_address + GPIO_SPEED_OFFSET, speed << (pin << 1)); + mmio_setbits_32(base + GPIO_SPEED_OFFSET, speed << (pin << 1)); - mmio_clrbits_32(bank_address + GPIO_PUPD_OFFSET, + mmio_clrbits_32(base + GPIO_PUPD_OFFSET, ((uint32_t)GPIO_PULL_MASK << (pin << 1))); - mmio_setbits_32(bank_address + GPIO_PUPD_OFFSET, pull << (pin << 1)); + mmio_setbits_32(base + GPIO_PUPD_OFFSET, pull << (pin << 1)); if (pin < GPIO_ALT_LOWER_LIMIT) { - mmio_clrbits_32(bank_address + GPIO_AFRL_OFFSET, + mmio_clrbits_32(base + GPIO_AFRL_OFFSET, ((uint32_t)GPIO_ALTERNATE_MASK << (pin << 2))); - mmio_setbits_32(bank_address + GPIO_AFRL_OFFSET, + mmio_setbits_32(base + GPIO_AFRL_OFFSET, alternate << (pin << 2)); } else { - mmio_clrbits_32(bank_address + GPIO_AFRH_OFFSET, + mmio_clrbits_32(base + GPIO_AFRH_OFFSET, ((uint32_t)GPIO_ALTERNATE_MASK << ((pin - GPIO_ALT_LOWER_LIMIT) << 2))); - mmio_setbits_32(bank_address + GPIO_AFRH_OFFSET, + mmio_setbits_32(base + GPIO_AFRH_OFFSET, alternate << ((pin - GPIO_ALT_LOWER_LIMIT) << 2)); } VERBOSE("GPIO %u mode set to 0x%x\n", bank, - mmio_read_32(bank_address + GPIO_MODE_OFFSET)); + mmio_read_32(base + GPIO_MODE_OFFSET)); VERBOSE("GPIO %u speed set to 0x%x\n", bank, - mmio_read_32(bank_address + GPIO_SPEED_OFFSET)); + mmio_read_32(base + GPIO_SPEED_OFFSET)); VERBOSE("GPIO %u mode pull to 0x%x\n", bank, - mmio_read_32(bank_address + GPIO_PUPD_OFFSET)); + mmio_read_32(base + GPIO_PUPD_OFFSET)); VERBOSE("GPIO %u mode alternate low to 0x%x\n", bank, - mmio_read_32(bank_address + GPIO_AFRL_OFFSET)); + mmio_read_32(base + GPIO_AFRL_OFFSET)); VERBOSE("GPIO %u mode alternate high to 0x%x\n", bank, - mmio_read_32(bank_address + GPIO_AFRH_OFFSET)); + mmio_read_32(base + GPIO_AFRH_OFFSET)); + + stm32mp1_clk_disable((unsigned long)clock); +} + +void set_gpio_secure_cfg(uint32_t bank, uint32_t pin, bool secure) +{ + uintptr_t base = stm32_get_gpio_bank_base(bank); + int clock = stm32_get_gpio_bank_clock(bank); + + assert(pin <= GPIO_PIN_MAX); + + stm32mp1_clk_enable((unsigned long)clock); + + if (secure) { + mmio_setbits_32(base + GPIO_SECR_OFFSET, BIT(pin)); + } else { + mmio_clrbits_32(base + GPIO_SECR_OFFSET, BIT(pin)); + } + + stm32mp1_clk_disable((unsigned long)clock); } diff --git a/drivers/st/pmic/stm32_i2c.c b/drivers/st/i2c/stm32_i2c.c index f861ba296..2be7afe2d 100644 --- a/drivers/st/pmic/stm32_i2c.c +++ b/drivers/st/i2c/stm32_i2c.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved + * Copyright (c) 2016-2019, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/drivers/st/io/io_stm32image.c b/drivers/st/io/io_stm32image.c index 0164a2d43..dc2977d5a 100644 --- a/drivers/st/io/io_stm32image.c +++ b/drivers/st/io/io_stm32image.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -94,6 +94,8 @@ static int stm32image_dev_open(const uintptr_t init_params, for (i = 0; i < STM32_PART_NUM; i++) { memcpy(stm32image_dev.part_info[i].name, device_info->part_info[i].name, MAX_PART_NAME_SIZE); + stm32image_dev.part_info[i].binary_type = + device_info->part_info[i].binary_type; stm32image_dev.part_info[i].part_offset = device_info->part_info[i].part_offset; stm32image_dev.part_info[i].bkp_offset = @@ -193,21 +195,29 @@ static int stm32image_partition_size(io_entity_t *entity, size_t *length) result = io_read(backend_handle, (uintptr_t)header, MAX_LBA_SIZE, (size_t *)&bytes_read); if (result != 0) { - ERROR("%s: io_read (%i)\n", __func__, result); - break; + if (current_part->bkp_offset == 0U) { + ERROR("%s: io_read (%i)\n", __func__, result); + } + header->magic = 0; } if ((header->magic != BOOT_API_IMAGE_HEADER_MAGIC_NB) || (header->binary_type != current_part->binary_type) || (header->image_length >= stm32image_dev.device_size)) { - WARN("%s: partition %s wrong header\n", - __func__, current_part->name); + VERBOSE("%s: partition %s not found at %x\n", + __func__, current_part->name, *stm32_img); + + if (current_part->bkp_offset == 0U) { + result = -ENOMEM; + break; + } /* Header not correct, check next offset for backup */ *stm32_img += current_part->bkp_offset; if (*stm32_img > stm32image_dev.device_size) { /* No backup found, end of device reached */ - WARN("Out of memory\n"); + WARN("%s : partition %s not found\n", + __func__, current_part->name); result = -ENOMEM; break; } @@ -221,9 +231,13 @@ static int stm32image_partition_size(io_entity_t *entity, size_t *length) return result; } - *length = header->image_length; + if (header->image_length < stm32image_dev.lba_size) { + *length = stm32image_dev.lba_size; + } else { + *length = header->image_length; + } - INFO("STM32 Image size : %i\n", *length); + INFO("STM32 Image size : %lu\n", (unsigned long)*length); return 0; } @@ -266,11 +280,10 @@ static int check_header(boot_api_image_header_t *header, uintptr_t buffer) static int stm32image_partition_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read) { - int result = 0, offset, local_length = 0; + int result = 0; uint8_t *local_buffer = (uint8_t *)buffer; boot_api_image_header_t *header = (boot_api_image_header_t *)first_lba_buffer; - uintptr_t backend_handle; assert(entity != NULL); assert(buffer != 0U); @@ -279,8 +292,17 @@ static int stm32image_partition_read(io_entity_t *entity, uintptr_t buffer, *length_read = 0U; while (*length_read == 0U) { + int offset; + int local_length; + uintptr_t backend_handle; + if (header->magic != BOOT_API_IMAGE_HEADER_MAGIC_NB) { /* Check for backup as image is corrupted */ + if (current_part->bkp_offset == 0U) { + result = -ENOMEM; + break; + } + *stm32_img += current_part->bkp_offset; if (*stm32_img >= stm32image_dev.device_size) { /* End of device reached */ @@ -342,8 +364,8 @@ static int stm32image_partition_read(io_entity_t *entity, uintptr_t buffer, if (result != 0) { ERROR("%s: io_read (%i)\n", __func__, result); *length_read = 0; - io_close(backend_handle); - break; + header->magic = 0; + continue; } result = check_header(header, buffer); @@ -351,8 +373,6 @@ static int stm32image_partition_read(io_entity_t *entity, uintptr_t buffer, ERROR("Header check failed\n"); *length_read = 0; header->magic = 0; - io_close(backend_handle); - break; } io_close(backend_handle); diff --git a/drivers/st/mmc/stm32_sdmmc2.c b/drivers/st/mmc/stm32_sdmmc2.c index 05f5ae17c..57812d899 100644 --- a/drivers/st/mmc/stm32_sdmmc2.c +++ b/drivers/st/mmc/stm32_sdmmc2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, STMicroelectronics - All Rights Reserved + * Copyright (c) 2018-2019, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -17,6 +17,7 @@ #include <common/debug.h> #include <drivers/delay_timer.h> #include <drivers/mmc.h> +#include <drivers/st/stm32_gpio.h> #include <drivers/st/stm32_sdmmc2.h> #include <drivers/st/stm32mp1_clk.h> #include <drivers/st/stm32mp1_rcc.h> @@ -470,12 +471,11 @@ static int stm32_sdmmc2_prepare(int lba, uintptr_t buf, size_t size) } /* Prepare CMD 16*/ - mmio_write_32(base + SDMMC_DTIMER, UINT32_MAX); + mmio_write_32(base + SDMMC_DTIMER, 0); mmio_write_32(base + SDMMC_DLENR, 0); - mmio_clrsetbits_32(base + SDMMC_DCTRLR, - SDMMC_DCTRLR_CLEAR_MASK, SDMMC_DCTRLR_DTDIR); + mmio_write_32(base + SDMMC_DCTRLR, 0); zeromem(&cmd, sizeof(struct mmc_cmd)); @@ -643,7 +643,7 @@ static int stm32_sdmmc2_dt_get_config(void) return -FDT_ERR_NOTFOUND; } - if (fdt_check_status(sdmmc_node) == 0) { + if (fdt_get_status(sdmmc_node) == DT_DISABLED) { return -FDT_ERR_NOTFOUND; } @@ -667,15 +667,15 @@ static int stm32_sdmmc2_dt_get_config(void) cuint++; sdmmc2_params.reset_id = fdt32_to_cpu(*cuint); - if ((fdt_getprop(fdt, sdmmc_node, "st,pin-ckin", NULL)) != NULL) { + if ((fdt_getprop(fdt, sdmmc_node, "st,use-ckin", NULL)) != NULL) { sdmmc2_params.pin_ckin = SDMMC_CLKCR_SELCLKRX_0; } - if ((fdt_getprop(fdt, sdmmc_node, "st,dirpol", NULL)) != NULL) { + if ((fdt_getprop(fdt, sdmmc_node, "st,sig-dir", NULL)) != NULL) { sdmmc2_params.dirpol = SDMMC_POWER_DIRPOL; } - if ((fdt_getprop(fdt, sdmmc_node, "st,negedge", NULL)) != NULL) { + if ((fdt_getprop(fdt, sdmmc_node, "st,neg-edge", NULL)) != NULL) { sdmmc2_params.negedge = SDMMC_CLKCR_NEGEDGE; } diff --git a/drivers/st/pmic/stm32mp1_pmic.c b/drivers/st/pmic/stm32mp_pmic.c index c5bdfc07d..6beabc153 100644 --- a/drivers/st/pmic/stm32mp1_pmic.c +++ b/drivers/st/pmic/stm32mp_pmic.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved + * Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,10 +13,10 @@ #include <common/debug.h> #include <drivers/delay_timer.h> +#include <drivers/st/stm32mp_pmic.h> #include <drivers/st/stm32_gpio.h> #include <drivers/st/stm32mp1_clk.h> -#include <drivers/st/stm32mp1_pmic.h> -#include <drivers/st/stpmu1.h> +#include <drivers/st/stpmic1.h> #include <lib/mmio.h> #include <lib/utils_def.h> @@ -27,23 +27,23 @@ #define MASK_RESET_BUCK3 BIT(2) -#define STPMU1_LDO12356_OUTPUT_MASK (uint8_t)(GENMASK(6, 2)) -#define STPMU1_LDO12356_OUTPUT_SHIFT 2 -#define STPMU1_LDO3_MODE (uint8_t)(BIT(7)) -#define STPMU1_LDO3_DDR_SEL 31U -#define STPMU1_LDO3_1800000 (9U << STPMU1_LDO12356_OUTPUT_SHIFT) +#define STPMIC1_LDO12356_OUTPUT_MASK (uint8_t)(GENMASK(6, 2)) +#define STPMIC1_LDO12356_OUTPUT_SHIFT 2 +#define STPMIC1_LDO3_MODE (uint8_t)(BIT(7)) +#define STPMIC1_LDO3_DDR_SEL 31U +#define STPMIC1_LDO3_1800000 (9U << STPMIC1_LDO12356_OUTPUT_SHIFT) -#define STPMU1_BUCK_OUTPUT_SHIFT 2 -#define STPMU1_BUCK3_1V8 (39U << STPMU1_BUCK_OUTPUT_SHIFT) +#define STPMIC1_BUCK_OUTPUT_SHIFT 2 +#define STPMIC1_BUCK3_1V8 (39U << STPMIC1_BUCK_OUTPUT_SHIFT) -#define STPMU1_DEFAULT_START_UP_DELAY_MS 1 +#define STPMIC1_DEFAULT_START_UP_DELAY_MS 1 static struct i2c_handle_s i2c_handle; static uint32_t pmic_i2c_addr; static int dt_get_pmic_node(void *fdt) { - return fdt_node_offset_by_compatible(fdt, -1, "st,stpmu1"); + return fdt_node_offset_by_compatible(fdt, -1, "st,stpmic1"); } bool dt_check_pmic(void) @@ -61,7 +61,7 @@ bool dt_check_pmic(void) return false; } - return fdt_check_status(node); + return fdt_get_status(node); } static int dt_pmic_i2c_config(struct dt_node_info *i2c_info) @@ -138,16 +138,16 @@ int dt_pmic_enable_boot_on_regulators(void) voltage = (uint16_t)(fdt32_to_cpu(*cuint) / 1000U); node_name = fdt_get_name(fdt, regulator_node, NULL); - if (stpmu1_is_regulator_enabled(node_name) == 0U) { + if (stpmic1_is_regulator_enabled(node_name) == 0U) { int status; - status = stpmu1_regulator_voltage_set(node_name, - voltage); + status = stpmic1_regulator_voltage_set(node_name, + voltage); if (status != 0) { return status; } - status = stpmu1_regulator_enable(node_name); + status = stpmic1_regulator_enable(node_name); if (status != 0) { return status; } @@ -204,7 +204,7 @@ void initialize_pmic_i2c(void) panic(); } - stpmu1_bind_i2c(&i2c_handle, (uint16_t)pmic_i2c_addr); + stpmic1_bind_i2c(&i2c_handle, (uint16_t)pmic_i2c_addr); } void initialize_pmic(void) @@ -214,7 +214,7 @@ void initialize_pmic(void) initialize_pmic_i2c(); - status = stpmu1_register_read(VERSION_STATUS_REG, &read_val); + status = stpmic1_register_read(VERSION_STATUS_REG, &read_val); if (status != 0) { panic(); } @@ -222,7 +222,7 @@ void initialize_pmic(void) INFO("PMIC version = 0x%x\n", read_val); /* Keep VDD on during the reset cycle */ - status = stpmu1_register_update(MASK_RESET_BUCK_REG, + status = stpmic1_register_update(MASK_RESET_BUCK_REG, MASK_RESET_BUCK3, MASK_RESET_BUCK3); if (status != 0) { @@ -239,45 +239,46 @@ int pmic_ddr_power_init(enum ddr_type ddr_type) switch (ddr_type) { case STM32MP_DDR3: /* Set LDO3 to sync mode */ - status = stpmu1_register_read(LDO3_CONTROL_REG, &read_val); + status = stpmic1_register_read(LDO3_CONTROL_REG, &read_val); if (status != 0) { return status; } - read_val &= ~STPMU1_LDO3_MODE; - read_val &= ~STPMU1_LDO12356_OUTPUT_MASK; - read_val |= STPMU1_LDO3_DDR_SEL << STPMU1_LDO12356_OUTPUT_SHIFT; + read_val &= ~STPMIC1_LDO3_MODE; + read_val &= ~STPMIC1_LDO12356_OUTPUT_MASK; + read_val |= STPMIC1_LDO3_DDR_SEL << + STPMIC1_LDO12356_OUTPUT_SHIFT; - status = stpmu1_register_write(LDO3_CONTROL_REG, read_val); + status = stpmic1_register_write(LDO3_CONTROL_REG, read_val); if (status != 0) { return status; } - status = stpmu1_regulator_voltage_set("buck2", 1350); + status = stpmic1_regulator_voltage_set("buck2", 1350); if (status != 0) { return status; } - status = stpmu1_regulator_enable("buck2"); + status = stpmic1_regulator_enable("buck2"); if (status != 0) { return status; } - mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS); - status = stpmu1_regulator_enable("vref_ddr"); + status = stpmic1_regulator_enable("vref_ddr"); if (status != 0) { return status; } - mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS); - status = stpmu1_regulator_enable("ldo3"); + status = stpmic1_regulator_enable("ldo3"); if (status != 0) { return status; } - mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS); break; case STM32MP_LPDDR2: @@ -286,57 +287,57 @@ int pmic_ddr_power_init(enum ddr_type ddr_type) * Set LDO3 to bypass mode if BUCK3 = 1.8V * Set LDO3 to normal mode if BUCK3 != 1.8V */ - status = stpmu1_register_read(BUCK3_CONTROL_REG, &read_val); + status = stpmic1_register_read(BUCK3_CONTROL_REG, &read_val); if (status != 0) { return status; } - if ((read_val & STPMU1_BUCK3_1V8) == STPMU1_BUCK3_1V8) { + if ((read_val & STPMIC1_BUCK3_1V8) == STPMIC1_BUCK3_1V8) { buck3_at_1v8 = true; } - status = stpmu1_register_read(LDO3_CONTROL_REG, &read_val); + status = stpmic1_register_read(LDO3_CONTROL_REG, &read_val); if (status != 0) { return status; } - read_val &= ~STPMU1_LDO3_MODE; - read_val &= ~STPMU1_LDO12356_OUTPUT_MASK; - read_val |= STPMU1_LDO3_1800000; + read_val &= ~STPMIC1_LDO3_MODE; + read_val &= ~STPMIC1_LDO12356_OUTPUT_MASK; + read_val |= STPMIC1_LDO3_1800000; if (buck3_at_1v8) { - read_val |= STPMU1_LDO3_MODE; + read_val |= STPMIC1_LDO3_MODE; } - status = stpmu1_register_write(LDO3_CONTROL_REG, read_val); + status = stpmic1_register_write(LDO3_CONTROL_REG, read_val); if (status != 0) { return status; } - status = stpmu1_regulator_voltage_set("buck2", 1200); + status = stpmic1_regulator_voltage_set("buck2", 1200); if (status != 0) { return status; } - status = stpmu1_regulator_enable("ldo3"); + status = stpmic1_regulator_enable("ldo3"); if (status != 0) { return status; } - mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS); - status = stpmu1_regulator_enable("buck2"); + status = stpmic1_regulator_enable("buck2"); if (status != 0) { return status; } - mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS); - status = stpmu1_regulator_enable("vref_ddr"); + status = stpmic1_regulator_enable("vref_ddr"); if (status != 0) { return status; } - mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS); + mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS); break; default: diff --git a/drivers/st/pmic/stpmu1.c b/drivers/st/pmic/stpmic1.c index 9c36bf64a..465996da9 100644 --- a/drivers/st/pmic/stpmu1.c +++ b/drivers/st/pmic/stpmic1.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved + * Copyright (c) 2016-2019, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,7 +7,7 @@ #include <string.h> #include <common/debug.h> -#include <drivers/st/stpmu1.h> +#include <drivers/st/stpmic1.h> #include <plat/common/platform.h> struct regul_struct { @@ -16,18 +16,22 @@ struct regul_struct { uint8_t voltage_table_size; uint8_t control_reg; uint8_t low_power_reg; + uint8_t pull_down_reg; + uint8_t pull_down; + uint8_t mask_reset_reg; + uint8_t mask_reset; }; -static struct i2c_handle_s *stpmu_i2c_handle; -static uint16_t stpmu_i2c_addr; +static struct i2c_handle_s *pmic_i2c_handle; +static uint16_t pmic_i2c_addr; /* Voltage tables in mV */ static const uint16_t buck1_voltage_table[] = { - 600, - 625, - 650, - 675, - 700, + 725, + 725, + 725, + 725, + 725, 725, 750, 775, @@ -54,7 +58,39 @@ static const uint16_t buck1_voltage_table[] = { 1300, 1325, 1350, - 1350, + 1375, + 1400, + 1425, + 1450, + 1475, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, + 1500, }; static const uint16_t buck2_voltage_table[] = { @@ -308,6 +344,7 @@ static const uint16_t ldo3_voltage_table[] = { 3300, 3300, 3300, + 500, 0xFFFF, /* VREFDDR */ }; @@ -389,6 +426,10 @@ static const struct regul_struct regulators_table[] = { .voltage_table_size = ARRAY_SIZE(buck1_voltage_table), .control_reg = BUCK1_CONTROL_REG, .low_power_reg = BUCK1_PWRCTRL_REG, + .pull_down_reg = BUCK_PULL_DOWN_REG, + .pull_down = BUCK1_PULL_DOWN_SHIFT, + .mask_reset_reg = MASK_RESET_BUCK_REG, + .mask_reset = BUCK1_MASK_RESET, }, { .dt_node_name = "buck2", @@ -396,6 +437,10 @@ static const struct regul_struct regulators_table[] = { .voltage_table_size = ARRAY_SIZE(buck2_voltage_table), .control_reg = BUCK2_CONTROL_REG, .low_power_reg = BUCK2_PWRCTRL_REG, + .pull_down_reg = BUCK_PULL_DOWN_REG, + .pull_down = BUCK2_PULL_DOWN_SHIFT, + .mask_reset_reg = MASK_RESET_BUCK_REG, + .mask_reset = BUCK2_MASK_RESET, }, { .dt_node_name = "buck3", @@ -403,6 +448,10 @@ static const struct regul_struct regulators_table[] = { .voltage_table_size = ARRAY_SIZE(buck3_voltage_table), .control_reg = BUCK3_CONTROL_REG, .low_power_reg = BUCK3_PWRCTRL_REG, + .pull_down_reg = BUCK_PULL_DOWN_REG, + .pull_down = BUCK3_PULL_DOWN_SHIFT, + .mask_reset_reg = MASK_RESET_BUCK_REG, + .mask_reset = BUCK3_MASK_RESET, }, { .dt_node_name = "buck4", @@ -410,6 +459,10 @@ static const struct regul_struct regulators_table[] = { .voltage_table_size = ARRAY_SIZE(buck4_voltage_table), .control_reg = BUCK4_CONTROL_REG, .low_power_reg = BUCK4_PWRCTRL_REG, + .pull_down_reg = BUCK_PULL_DOWN_REG, + .pull_down = BUCK4_PULL_DOWN_SHIFT, + .mask_reset_reg = MASK_RESET_BUCK_REG, + .mask_reset = BUCK4_MASK_RESET, }, { .dt_node_name = "ldo1", @@ -417,6 +470,8 @@ static const struct regul_struct regulators_table[] = { .voltage_table_size = ARRAY_SIZE(ldo1_voltage_table), .control_reg = LDO1_CONTROL_REG, .low_power_reg = LDO1_PWRCTRL_REG, + .mask_reset_reg = MASK_RESET_LDO_REG, + .mask_reset = LDO1_MASK_RESET, }, { .dt_node_name = "ldo2", @@ -424,6 +479,8 @@ static const struct regul_struct regulators_table[] = { .voltage_table_size = ARRAY_SIZE(ldo2_voltage_table), .control_reg = LDO2_CONTROL_REG, .low_power_reg = LDO2_PWRCTRL_REG, + .mask_reset_reg = MASK_RESET_LDO_REG, + .mask_reset = LDO2_MASK_RESET, }, { .dt_node_name = "ldo3", @@ -431,6 +488,8 @@ static const struct regul_struct regulators_table[] = { .voltage_table_size = ARRAY_SIZE(ldo3_voltage_table), .control_reg = LDO3_CONTROL_REG, .low_power_reg = LDO3_PWRCTRL_REG, + .mask_reset_reg = MASK_RESET_LDO_REG, + .mask_reset = LDO3_MASK_RESET, }, { .dt_node_name = "ldo4", @@ -438,6 +497,8 @@ static const struct regul_struct regulators_table[] = { .voltage_table_size = ARRAY_SIZE(ldo4_voltage_table), .control_reg = LDO4_CONTROL_REG, .low_power_reg = LDO4_PWRCTRL_REG, + .mask_reset_reg = MASK_RESET_LDO_REG, + .mask_reset = LDO4_MASK_RESET, }, { .dt_node_name = "ldo5", @@ -445,6 +506,8 @@ static const struct regul_struct regulators_table[] = { .voltage_table_size = ARRAY_SIZE(ldo5_voltage_table), .control_reg = LDO5_CONTROL_REG, .low_power_reg = LDO5_PWRCTRL_REG, + .mask_reset_reg = MASK_RESET_LDO_REG, + .mask_reset = LDO5_MASK_RESET, }, { .dt_node_name = "ldo6", @@ -452,6 +515,8 @@ static const struct regul_struct regulators_table[] = { .voltage_table_size = ARRAY_SIZE(ldo6_voltage_table), .control_reg = LDO6_CONTROL_REG, .low_power_reg = LDO6_PWRCTRL_REG, + .mask_reset_reg = MASK_RESET_LDO_REG, + .mask_reset = LDO6_MASK_RESET, }, { .dt_node_name = "vref_ddr", @@ -459,12 +524,14 @@ static const struct regul_struct regulators_table[] = { .voltage_table_size = ARRAY_SIZE(vref_ddr_voltage_table), .control_reg = VREF_DDR_CONTROL_REG, .low_power_reg = VREF_DDR_PWRCTRL_REG, + .mask_reset_reg = MASK_RESET_LDO_REG, + .mask_reset = VREF_DDR_MASK_RESET, }, }; -#define MAX_REGUL ARRAY_SIZE(regulators_table) +#define MAX_REGUL ARRAY_SIZE(regulators_table) -static const struct regul_struct *stpmu1_get_regulator_data(const char *name) +static const struct regul_struct *get_regulator_data(const char *name) { uint8_t i; @@ -480,10 +547,9 @@ static const struct regul_struct *stpmu1_get_regulator_data(const char *name) return NULL; } -static uint8_t stpmu1_voltage_find_index(const char *name, - uint16_t millivolts) +static uint8_t voltage_to_index(const char *name, uint16_t millivolts) { - const struct regul_struct *regul = stpmu1_get_regulator_data(name); + const struct regul_struct *regul = get_regulator_data(name); uint8_t i; for (i = 0 ; i < regul->voltage_table_size ; i++) { @@ -498,62 +564,132 @@ static uint8_t stpmu1_voltage_find_index(const char *name, return 0; } -int stpmu1_switch_off(void) +int stpmic1_powerctrl_on(void) +{ + return stpmic1_register_update(MAIN_CONTROL_REG, PWRCTRL_PIN_VALID, + PWRCTRL_PIN_VALID); +} + +int stpmic1_switch_off(void) { - return stpmu1_register_update(MAIN_CONTROL_REG, 1, - SOFTWARE_SWITCH_OFF_ENABLED); + return stpmic1_register_update(MAIN_CONTROL_REG, 1, + SOFTWARE_SWITCH_OFF_ENABLED); } -int stpmu1_regulator_enable(const char *name) +int stpmic1_regulator_enable(const char *name) { - const struct regul_struct *regul = stpmu1_get_regulator_data(name); + const struct regul_struct *regul = get_regulator_data(name); - return stpmu1_register_update(regul->control_reg, BIT(0), BIT(0)); + return stpmic1_register_update(regul->control_reg, BIT(0), BIT(0)); } -int stpmu1_regulator_disable(const char *name) +int stpmic1_regulator_disable(const char *name) { - const struct regul_struct *regul = stpmu1_get_regulator_data(name); + const struct regul_struct *regul = get_regulator_data(name); - return stpmu1_register_update(regul->control_reg, 0, BIT(0)); + return stpmic1_register_update(regul->control_reg, 0, BIT(0)); } -uint8_t stpmu1_is_regulator_enabled(const char *name) +uint8_t stpmic1_is_regulator_enabled(const char *name) { uint8_t val; - const struct regul_struct *regul = stpmu1_get_regulator_data(name); + const struct regul_struct *regul = get_regulator_data(name); - if (stpmu1_register_read(regul->control_reg, &val) != 0) { + if (stpmic1_register_read(regul->control_reg, &val) != 0) { panic(); } return (val & 0x1U); } -int stpmu1_regulator_voltage_set(const char *name, uint16_t millivolts) +int stpmic1_regulator_voltage_set(const char *name, uint16_t millivolts) +{ + uint8_t voltage_index = voltage_to_index(name, millivolts); + const struct regul_struct *regul = get_regulator_data(name); + uint8_t mask; + + /* Voltage can be set for buck<N> or ldo<N> (except ldo4) regulators */ + if (strncmp(name, "buck", 4) == 0) { + mask = BUCK_VOLTAGE_MASK; + } else if ((strncmp(name, "ldo", 3) == 0) && + (strncmp(name, "ldo4", 4) != 0)) { + mask = LDO_VOLTAGE_MASK; + } else { + return 0; + } + + return stpmic1_register_update(regul->control_reg, + voltage_index << LDO_BUCK_VOLTAGE_SHIFT, + mask); +} + +int stpmic1_regulator_pull_down_set(const char *name) { - uint8_t voltage_index = stpmu1_voltage_find_index(name, millivolts); - const struct regul_struct *regul = stpmu1_get_regulator_data(name); + const struct regul_struct *regul = get_regulator_data(name); - return stpmu1_register_update(regul->control_reg, voltage_index << 2, - 0xFC); + if (regul->pull_down_reg != 0) { + return stpmic1_register_update(regul->pull_down_reg, + BIT(regul->pull_down), + LDO_BUCK_PULL_DOWN_MASK << + regul->pull_down); + } + + return 0; } -int stpmu1_register_read(uint8_t register_id, uint8_t *value) +int stpmic1_regulator_mask_reset_set(const char *name) { - return stm32_i2c_mem_read(stpmu_i2c_handle, stpmu_i2c_addr, - (uint16_t)register_id, I2C_MEMADD_SIZE_8BIT, - value, 1, 100000); + const struct regul_struct *regul = get_regulator_data(name); + + return stpmic1_register_update(regul->mask_reset_reg, + BIT(regul->mask_reset), + LDO_BUCK_RESET_MASK << + regul->mask_reset); } -int stpmu1_register_write(uint8_t register_id, uint8_t value) +int stpmic1_regulator_voltage_get(const char *name) +{ + const struct regul_struct *regul = get_regulator_data(name); + uint8_t value; + uint8_t mask; + + /* Voltage can be set for buck<N> or ldo<N> (except ldo4) regulators */ + if (strncmp(name, "buck", 4) == 0) { + mask = BUCK_VOLTAGE_MASK; + } else if ((strncmp(name, "ldo", 3) == 0) && + (strncmp(name, "ldo4", 4) != 0)) { + mask = LDO_VOLTAGE_MASK; + } else { + return 0; + } + + if (stpmic1_register_read(regul->control_reg, &value)) + return -1; + + value = (value & mask) >> LDO_BUCK_VOLTAGE_SHIFT; + + if (value > regul->voltage_table_size) + return -1; + + return (int)regul->voltage_table[value]; +} + +int stpmic1_register_read(uint8_t register_id, uint8_t *value) +{ + return stm32_i2c_mem_read(pmic_i2c_handle, pmic_i2c_addr, + (uint16_t)register_id, I2C_MEMADD_SIZE_8BIT, + value, 1, 100000); +} + +int stpmic1_register_write(uint8_t register_id, uint8_t value) { int status; - status = stm32_i2c_mem_write(stpmu_i2c_handle, stpmu_i2c_addr, + status = stm32_i2c_mem_write(pmic_i2c_handle, pmic_i2c_addr, (uint16_t)register_id, I2C_MEMADD_SIZE_8BIT, &value, 1, 100000); +#if ENABLE_ASSERTIONS if (status != 0) { return status; } @@ -561,7 +697,7 @@ int stpmu1_register_write(uint8_t register_id, uint8_t value) if ((register_id != WATCHDOG_CONTROL_REG) && (register_id <= 0x40U)) { uint8_t readval; - status = stpmu1_register_read(register_id, &readval); + status = stpmic1_register_read(register_id, &readval); if (status != 0) { return status; } @@ -570,32 +706,57 @@ int stpmu1_register_write(uint8_t register_id, uint8_t value) return -1; } } +#endif - return 0; + return status; } -int stpmu1_register_update(uint8_t register_id, uint8_t value, uint8_t mask) +int stpmic1_register_update(uint8_t register_id, uint8_t value, uint8_t mask) { int status; uint8_t val; - status = stpmu1_register_read(register_id, &val); + status = stpmic1_register_read(register_id, &val); if (status != 0) { return status; } - /* Clear bits to update */ - val &= ~mask; + val = (val & ~mask) | (value & mask); - /* Update appropriate bits*/ - val |= (value & mask); + return stpmic1_register_write(register_id, val); +} - /* Send new value on I2C Bus */ - return stpmu1_register_write(register_id, val); +void stpmic1_bind_i2c(struct i2c_handle_s *i2c_handle, uint16_t i2c_addr) +{ + pmic_i2c_handle = i2c_handle; + pmic_i2c_addr = i2c_addr; } -void stpmu1_bind_i2c(struct i2c_handle_s *i2c_handle, uint16_t i2c_addr) +void stpmic1_dump_regulators(void) { - stpmu_i2c_handle = i2c_handle; - stpmu_i2c_addr = i2c_addr; + uint32_t i; + + for (i = 0U; i < MAX_REGUL; i++) { + const char *name __unused = regulators_table[i].dt_node_name; + + VERBOSE("PMIC regul %s: %sable, %dmV", + name, + stpmic1_is_regulator_enabled(name) ? "en" : "dis", + stpmic1_regulator_voltage_get(name)); + } +} + +int stpmic1_get_version(unsigned long *version) +{ + int rc; + uint8_t read_val; + + rc = stpmic1_register_read(VERSION_STATUS_REG, &read_val); + if (rc) { + return -1; + } + + *version = (unsigned long)read_val; + + return 0; } diff --git a/drivers/synopsys/ufs/dw_ufs.c b/drivers/synopsys/ufs/dw_ufs.c index c7c8fc22d..6bed981db 100644 --- a/drivers/synopsys/ufs/dw_ufs.c +++ b/drivers/synopsys/ufs/dw_ufs.c @@ -183,7 +183,7 @@ static int dwufs_phy_set_pwr_mode(ufs_params_t *params) return 0; } -const ufs_ops_t dw_ufs_ops = { +static const ufs_ops_t dw_ufs_ops = { .phy_init = dwufs_phy_init, .phy_set_pwr_mode = dwufs_phy_set_pwr_mode, }; diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index 2351c9b04..b2c104612 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -591,7 +591,7 @@ void ufs_write_desc(int idn, int index, uintptr_t buf, size_t size) ufs_query(QUERY_WRITE_DESC, idn, index, 0, buf, size); } -void ufs_read_capacity(int lun, unsigned int *num, unsigned int *size) +static void ufs_read_capacity(int lun, unsigned int *num, unsigned int *size) { utp_utrd_t utrd; resp_upiu_t *resp; |