Add mailbox support for Stratix SoC Signed-off-by: Ley Foon Tan <ley.foon....@intel.com> Signed-off-by: Chin Liang See <chin.liang....@intel.com> --- arch/arm/mach-socfpga/Makefile | 1 + arch/arm/mach-socfpga/include/mach/mailbox_s10.h | 155 +++++++++ arch/arm/mach-socfpga/mailbox_s10.c | 378 ++++++++++++++++++++++ 3 files changed, 534 insertions(+), 0 deletions(-) create mode 100644 arch/arm/mach-socfpga/include/mach/mailbox_s10.h create mode 100644 arch/arm/mach-socfpga/mailbox_s10.c
diff --git a/arch/arm/mach-socfpga/Makefile b/arch/arm/mach-socfpga/Makefile index b253914..43e18d2 100644 --- a/arch/arm/mach-socfpga/Makefile +++ b/arch/arm/mach-socfpga/Makefile @@ -32,6 +32,7 @@ endif ifdef CONFIG_TARGET_SOCFPGA_STRATIX10 obj-y += clock_manager_s10.o +obj-y += mailbox_s10.o obj-y += misc_s10.o obj-y += reset_manager_s10.o obj-y += system_manager_s10.o diff --git a/arch/arm/mach-socfpga/include/mach/mailbox_s10.h b/arch/arm/mach-socfpga/include/mach/mailbox_s10.h new file mode 100644 index 0000000..85e7f84 --- /dev/null +++ b/arch/arm/mach-socfpga/include/mach/mailbox_s10.h @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2017-2018 Intel Corporation <www.intel.com> + * + */ + +#ifndef _MAILBOX_S10_H_ +#define _MAILBOX_S10_H_ + +/* user define Uboot ID */ +#define MBOX_CLIENT_ID_UBOOT 0xB +#define MBOX_ID_UBOOT 0x1 + +#define MBOX_CMD_DIRECT 0 +#define MBOX_CMD_INDIRECT 1 + +#define MBOX_MAX_CMD_INDEX 2047 +#define MBOX_CMD_BUFFER_SIZE 32 +#define MBOX_RESP_BUFFER_SIZE 16 + +#define MBOX_HDR_CMD_LSB 0 +#define MBOX_HDR_CMD_MSK (BIT(11) - 1) +#define MBOX_HDR_I_LSB 11 +#define MBOX_HDR_I_MSK BIT(11) +#define MBOX_HDR_LEN_LSB 12 +#define MBOX_HDR_LEN_MSK 0x007FF000 +#define MBOX_HDR_ID_LSB 24 +#define MBOX_HDR_ID_MSK 0x0F000000 +#define MBOX_HDR_CLIENT_LSB 28 +#define MBOX_HDR_CLIENT_MSK 0xF0000000 + +/* Interrupt flags */ +#define MBOX_FLAGS_INT_COE BIT(0) /* COUT update interrupt enable */ +#define MBOX_FLAGS_INT_RIE BIT(1) /* RIN update interrupt enable */ +#define MBOX_FLAGS_INT_UAE BIT(8) /* Urgent ACK interrupt enable */ +#define MBOX_ALL_INTRS (MBOX_FLAGS_INT_COE | \ + MBOX_FLAGS_INT_RIE | \ + MBOX_FLAGS_INT_UAE) + +/* Status */ +#define MBOX_STATUS_UA_MSK BIT(8) + +#define MBOX_CMD_HEADER(client, id, len, indirect, cmd) \ + ((((cmd) << MBOX_HDR_CMD_LSB) & MBOX_HDR_CMD_MSK) | \ + (((indirect) << MBOX_HDR_I_LSB) & MBOX_HDR_I_MSK) | \ + (((len) << MBOX_HDR_LEN_LSB) & MBOX_HDR_LEN_MSK) | \ + (((id) << MBOX_HDR_ID_LSB) & MBOX_HDR_ID_MSK) | \ + (((client) << MBOX_HDR_CLIENT_LSB) & MBOX_HDR_CLIENT_MSK)) + +#define MBOX_RESP_ERR_GET(resp) \ + (((resp) & MBOX_HDR_CMD_MSK) >> MBOX_HDR_CMD_LSB) +#define MBOX_RESP_LEN_GET(resp) \ + (((resp) & MBOX_HDR_LEN_MSK) >> MBOX_HDR_LEN_LSB) +#define MBOX_RESP_ID_GET(resp) \ + (((resp) & MBOX_HDR_ID_MSK) >> MBOX_HDR_ID_LSB) +#define MBOX_RESP_CLIENT_GET(resp) \ + (((resp) & MBOX_HDR_CLIENT_MSK) >> MBOX_HDR_CLIENT_LSB) + +/* Response error list */ +enum ALT_SDM_MBOX_RESP_CODE { + /* CMD completed successfully, but check resp ARGS for any errors */ + MBOX_RESP_STATOK = 0, + /* CMD is incorrectly formatted in some way */ + MBOX_RESP_INVALID_COMMAND = 1, + /* BootROM Command code not undesrtood */ + MBOX_RESP_UNKNOWN_BR = 2, + /* CMD code not recognized by firmware */ + MBOX_RESP_UNKNOWN = 3, + /* Indicates that the device is not configured */ + MBOX_RESP_NOT_CONFIGURED = 256, + /* Indicates that the device is busy */ + MBOX_RESP_DEVICE_BUSY = 0x1FF, + /* Indicates that there is no valid response available */ + MBOX_RESP_NO_VALID_RESP_AVAILABLE = 0x2FF, + /* General Error */ + MBOX_RESP_ERROR = 0x3FF, +}; + +/* Mailbox command list */ +#define MBOX_RESTART 2 +#define MBOX_CONFIG_STATUS 4 +#define MBOX_RECONFIG 6 +#define MBOX_RECONFIG_MSEL 7 +#define MBOX_RECONFIG_DATA 8 +#define MBOX_RECONFIG_STATUS 9 +#define MBOX_QSPI_OPEN 50 +#define MBOX_QSPI_CLOSE 51 +#define MBOX_QSPI_DIRECT 59 +#define MBOX_REBOOT_HPS 71 + +struct socfpga_mailbox { + u32 cin; /* command valid offset */ + u32 rout; /* response output offset */ + u32 urg; /* urgent command */ + u32 flags; /* interrupt enables */ + u32 pad_0x10_0x1f[4]; /* 0x10 - 0x1F reserved */ + u32 cout; /* command free offset */ + u32 rin; /* respond valid offset */ + u32 pad_0x28; /* 0x28 reserved */ + u32 status; /* mailbox status */ + u32 pad_0x30_0x3f[4]; /* 0x30 - 0x3F reserved */ + u32 cmd_buf[MBOX_CMD_BUFFER_SIZE]; /* 0x40 - 0xBC circular command + * buffer to SDM + */ + u32 resp_buf[MBOX_RESP_BUFFER_SIZE]; /* 0xC0 - 0xFF circular + * response buffer + */ +}; + +/* Use define other than put into struct socfpga_mailbox to save spaces */ +#define MBOX_DOORBELL_TO_SDM_REG (SOCFPGA_MAILBOX_ADDRESS + 0x400) +#define MBOX_DOORBELL_FROM_SDM_REG (SOCFPGA_MAILBOX_ADDRESS + 0x480) + +/******** Status and bit information returned by RECONFIG_STATUS ********/ +#define RECONFIG_STATUS_RESPONSE_LEN 6 +#define RECONFIG_STATUS_STATE 0 +#define RECONFIG_STATUS_PIN_STATUS 2 +#define RECONFIG_STATUS_SOFTFUNC_STATUS 3 + +#define MBOX_CFGSTAT_STATE_IDLE 0x00000000 +#define MBOX_CFGSTAT_STATE_CONFIG 0x10000000 +#define MBOX_CFGSTAT_STATE_FAILACK 0x08000000 +#define MBOX_CFGSTAT_STATE_ERROR_INVALID 0xf0000001 +#define MBOX_CFGSTAT_STATE_ERROR_CORRUPT 0xf0000002 +#define MBOX_CFGSTAT_STATE_ERROR_AUTH 0xf0000003 +#define MBOX_CFGSTAT_STATE_ERROR_CORE_IO 0xf0000004 +#define MBOX_CFGSTAT_STATE_ERROR_HARDWARE 0xf0000005 +#define MBOX_CFGSTAT_STATE_ERROR_FAKE 0xf0000006 +#define MBOX_CFGSTAT_STATE_ERROR_BOOT_INFO 0xf0000007 +#define MBOX_CFGSTAT_STATE_ERROR_QSPI_ERROR 0xf0000008 + +#define RCF_SOFTFUNC_STATUS_CONF_DONE BIT(0) +#define RCF_SOFTFUNC_STATUS_INIT_DONE BIT(1) +#define RCF_SOFTFUNC_STATUS_SEU_ERROR BIT(3) +#define RCF_PIN_STATUS_NSTATUS BIT(31) +/************************************************************************/ + +int mbox_send_cmd(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg, u8 urgent, + u32 *resp_buf_len, u32 *resp_buf); +int mbox_send_cmd_psci(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg, + u8 urgent, u32 *resp_buf_len, u32 *resp_buf); +int mbox_send_cmd_only(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg); +int mbox_send_cmd_only_psci(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg); +int mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len); +int mbox_rcv_resp_psci(u32 *resp_buf, u32 resp_buf_max_len); +int mbox_init(void); + +#ifdef CONFIG_CADENCE_QSPI +int mbox_qspi_close(void); +int mbox_qspi_open(void); +#endif + +int mbox_reset_cold(void); + +#endif /* _MAILBOX_S10_H_ */ diff --git a/arch/arm/mach-socfpga/mailbox_s10.c b/arch/arm/mach-socfpga/mailbox_s10.c new file mode 100644 index 0000000..ed713a9 --- /dev/null +++ b/arch/arm/mach-socfpga/mailbox_s10.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017-2018 Intel Corporation <www.intel.com> + * + */ + +#include <common.h> +#include <wait_bit.h> +#include <asm/io.h> +#include <asm/arch/mailbox_s10.h> +#include <asm/arch/system_manager.h> +#include <asm/secure.h> + +DECLARE_GLOBAL_DATA_PTR; + +static __always_inline int mbox_polling_resp(u32 rout) +{ + static const struct socfpga_mailbox *mbox_base = + (void *)SOCFPGA_MAILBOX_ADDRESS; + u32 rin; + unsigned long i = ~0; + + while (i) { + rin = readl(&mbox_base->rin); + if (rout != rin) + return 0; + + i--; + } + + return -ETIMEDOUT; +} + +/* Check for available slot and write to circular buffer. + * It also update command valid offset (cin) register. + */ +static __always_inline int mbox_fill_cmd_circular_buff(u32 header, u32 len, + u32 *arg) +{ + static const struct socfpga_mailbox *mbox_base = + (void *)SOCFPGA_MAILBOX_ADDRESS; + u32 cin; + u32 cout; + u32 i; + + cin = readl(&mbox_base->cin) % MBOX_CMD_BUFFER_SIZE; + cout = readl(&mbox_base->cout) % MBOX_CMD_BUFFER_SIZE; + + /* if command buffer is full or not enough free space + * to fit the data + */ + if (((cin + 1) % MBOX_CMD_BUFFER_SIZE) == cout || + ((MBOX_CMD_BUFFER_SIZE - cin + cout - 1) % + MBOX_CMD_BUFFER_SIZE) < len) + return -ENOMEM; + + /* write header to circular buffer */ + writel(header, &mbox_base->cmd_buf[cin++]); + /* wrapping around when it reach the buffer size */ + cin %= MBOX_CMD_BUFFER_SIZE; + + /* write arguments */ + for (i = 0; i < len; i++) { + writel(arg[i], &mbox_base->cmd_buf[cin++]); + /* wrapping around when it reach the buffer size */ + cin %= MBOX_CMD_BUFFER_SIZE; + } + + /* write command valid offset */ + writel(cin, &mbox_base->cin); + + return 0; +} + +/* Check the command and fill it into circular buffer */ +static __always_inline int mbox_prepare_cmd_only(u8 id, u32 cmd, + u8 is_indirect, u32 len, + u32 *arg) +{ + u32 header; + int ret; + + /* Total length is command + argument length */ + if ((len + 1) > MBOX_CMD_BUFFER_SIZE) + return -EINVAL; + + if (cmd > MBOX_MAX_CMD_INDEX) + return -EINVAL; + + header = MBOX_CMD_HEADER(MBOX_CLIENT_ID_UBOOT, id, len, + (is_indirect) ? 1 : 0, cmd); + + ret = mbox_fill_cmd_circular_buff(header, len, arg); + + return ret; +} + +/* Send command only without waiting for responses from SDM */ +static __always_inline int __mbox_send_cmd_only(u8 id, u32 cmd, + u8 is_indirect, u32 len, + u32 *arg) +{ + int ret = mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg); + /* write doorbell */ + writel(1, MBOX_DOORBELL_TO_SDM_REG); + + return ret; +} + +/* Return number of responses received in buffer */ +static __always_inline int __mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len) +{ + static const struct socfpga_mailbox *mbox_base = + (void *)SOCFPGA_MAILBOX_ADDRESS; + u32 rin; + u32 rout; + u32 resp_len = 0; + + /* clear doorbell from SDM if it was SET */ + if (readl((const u32 *)MBOX_DOORBELL_FROM_SDM_REG) & 1) + writel(0, MBOX_DOORBELL_FROM_SDM_REG); + + /* read current response offset */ + rout = readl(&mbox_base->rout); + /* read response valid offset */ + rin = readl(&mbox_base->rin); + + while (rin != rout && (resp_len < resp_buf_max_len)) { + /* Response received */ + if (resp_buf) + resp_buf[resp_len++] = + readl(&mbox_base->resp_buf[rout]); + rout++; + /* wrapping around when it reach the buffer size */ + rout %= MBOX_RESP_BUFFER_SIZE; + /* update next ROUT */ + writel(rout, &mbox_base->rout); + } + + return resp_len; +} + +/* Support one command and up to 31 words argument length only */ +static __always_inline int __mbox_send_cmd(u8 id, u32 cmd, u8 is_indirect, + u32 len, u32 *arg, u8 urgent, + u32 *resp_buf_len, u32 *resp_buf) +{ + static const struct socfpga_mailbox *mbox_base = + (void *)SOCFPGA_MAILBOX_ADDRESS; + + u32 rin; + u32 resp; + u32 rout; + u32 status; + u32 resp_len; + u32 buf_len; + int ret; + + ret = mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg); + if (ret) + return ret; + + if (urgent) { + /* Read status because it is toggled */ + status = readl(&mbox_base->status) & MBOX_STATUS_UA_MSK; + /* Send command as urgent command */ + writel(1, &mbox_base->urg); + } + + /* write doorbell */ + writel(1, MBOX_DOORBELL_TO_SDM_REG); + + while (1) { + ret = ~0; + + /* Wait for doorbell from SDM */ + while (!readl(MBOX_DOORBELL_FROM_SDM_REG) && ret--) + ; + if (!ret) + return -ETIMEDOUT; + + /* clear interrupt */ + writel(0, MBOX_DOORBELL_FROM_SDM_REG); + + if (urgent) { + u32 new_status = readl(&mbox_base->status); + /* urgent command doesn't have response */ + writel(0, &mbox_base->urg); + /* Urgent ACK is toggled */ + if ((new_status & MBOX_STATUS_UA_MSK) ^ status) + return 0; + + return -ECOMM; + } + + /* read current response offset */ + rout = readl(&mbox_base->rout); + + /* read response valid offset */ + rin = readl(&mbox_base->rin); + + if (rout != rin) { + /* Response received */ + resp = readl(&mbox_base->resp_buf[rout]); + rout++; + /* wrapping around when it reach the buffer size */ + rout %= MBOX_RESP_BUFFER_SIZE; + /* update next ROUT */ + writel(rout, &mbox_base->rout); + + /* check client ID and ID */ + if ((MBOX_RESP_CLIENT_GET(resp) == MBOX_CLIENT_ID_UBOOT) && + (MBOX_RESP_ID_GET(resp) == id)) { + ret = MBOX_RESP_ERR_GET(resp); + if (ret) + return ret; + + if (resp_buf_len) { + buf_len = *resp_buf_len; + *resp_buf_len = 0; + } else { + buf_len = 0; + } + + resp_len = MBOX_RESP_LEN_GET(resp); + while (resp_len) { + ret = mbox_polling_resp(rout); + if (ret) + return ret; + /* we need to process response buffer + * even caller doesn't need it + */ + resp = readl(&mbox_base->resp_buf[rout]); + rout++; + resp_len--; + rout %= MBOX_RESP_BUFFER_SIZE; + writel(rout, &mbox_base->rout); + if (buf_len) { + /* copy response to buffer */ + resp_buf[*resp_buf_len] = resp; + (*resp_buf_len)++; + buf_len--; + } + } + return ret; + } + } + }; + + return -EIO; +} + +int mbox_init(void) +{ + static const struct socfpga_mailbox *mbox_base = + (void *)SOCFPGA_MAILBOX_ADDRESS; + int ret; + + /* enable mailbox interrupts */ + writel(MBOX_ALL_INTRS, &mbox_base->flags); + + /* Ensure urgent request is cleared */ + writel(0, &mbox_base->urg); + + /* Ensure the Doorbell Interrupt is cleared */ + writel(0, MBOX_DOORBELL_FROM_SDM_REG); + + ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RESTART, MBOX_CMD_DIRECT, 0, + NULL, 1, 0, NULL); + if (ret) + return ret; + + /* Renable mailbox interrupts after MBOX_RESTART */ + writel(MBOX_ALL_INTRS, &mbox_base->flags); + + return 0; +} + +#ifdef CONFIG_CADENCE_QSPI +int mbox_qspi_close(void) +{ + return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_CLOSE, MBOX_CMD_DIRECT, + 0, NULL, 0, 0, NULL); +} + +int mbox_qspi_open(void) +{ + static const struct socfpga_system_manager *sysmgr_regs = + (struct socfpga_system_manager *)SOCFPGA_SYSMGR_ADDRESS; + + int ret; + u32 resp_buf[1]; + u32 resp_buf_len; + + ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN, MBOX_CMD_DIRECT, + 0, NULL, 0, 0, NULL); + if (ret) { + /* retry again by closing and reopen the QSPI again */ + ret = mbox_qspi_close(); + if (ret) + return ret; + + ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN, + MBOX_CMD_DIRECT, 0, NULL, 0, 0, NULL); + if (ret) + return ret; + } + + /* HPS will directly control the QSPI controller, no longer mailbox */ + resp_buf_len = 1; + ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_DIRECT, MBOX_CMD_DIRECT, + 0, NULL, 0, (u32 *)&resp_buf_len, + (u32 *)&resp_buf); + if (ret) + goto error; + + /* We are getting QSPI ref clock and set into sysmgr boot register */ + printf("QSPI: Reference clock at %d Hz\n", resp_buf[0]); + writel(resp_buf[0], &sysmgr_regs->boot_scratch_cold0); + + return 0; + +error: + mbox_qspi_close(); + + return ret; +} +#endif /* CONFIG_CADENCE_QSPI */ + +int mbox_reset_cold(void) +{ + int ret; + + ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_REBOOT_HPS, MBOX_CMD_DIRECT, + 0, NULL, 0, 0, NULL); + if (ret) { + /* mailbox sent failure, wait for watchdog to kick in */ + while (1) + ; + } + return 0; +} + +int mbox_send_cmd(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg, + u8 urgent, u32 *resp_buf_len, u32 *resp_buf) +{ + return __mbox_send_cmd(id, cmd, is_indirect, len, arg, urgent, + resp_buf_len, resp_buf); +} + +int __secure mbox_send_cmd_psci(u8 id, u32 cmd, u8 is_indirect, u32 len, + u32 *arg, u8 urgent, u32 *resp_buf_len, + u32 *resp_buf) +{ + return __mbox_send_cmd(id, cmd, is_indirect, len, arg, urgent, + resp_buf_len, resp_buf); +} + +int mbox_send_cmd_only(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg) +{ + return __mbox_send_cmd_only(id, cmd, is_indirect, len, arg); +} + +int __secure mbox_send_cmd_only_psci(u8 id, u32 cmd, u8 is_indirect, u32 len, + u32 *arg) +{ + return __mbox_send_cmd_only(id, cmd, is_indirect, len, arg); +} + +int mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len) +{ + return __mbox_rcv_resp(resp_buf, resp_buf_max_len); +} + +int __secure mbox_rcv_resp_psci(u32 *resp_buf, u32 resp_buf_max_len) +{ + return __mbox_rcv_resp(resp_buf, resp_buf_max_len); +} -- 1.7.1 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot