mirror of
https://github.com/riscv-software-src/opensbi.git
synced 2025-12-22 06:12:02 +00:00
Hifive Premier P550[1] is a Mini-DTX form factor board with EIC7700X. It has a STM32F407VET6 onboard MCU acting as the BMC, controlling ATX power on/off while providing remote management features. The EIC7700X SoC/SoM communicates with the BMC via UART2, using ESWIN's protocol. The messages transmitted are fixed sizes (267 bytes), and depending on the type, can be directional or bi-directional. The shutdown and cold reboot requests are directional messages from SoC to BMC (NOTIFY type) with CMD_POWER_OFF or CMD_RESTART. The payload of shutdown/cold reboot requests should be empty and are ignored by the BMC at the moment. A HFP (Hifive Premier) specific reset device is registered in addition to the SoC reset device. For shutdown and cold reboot, the board-level reset takes precedence. The definitions of the SoC <-> BMC message protocol is taken from ESWIN's repo [2]. The only file used from that repo is `hf_common.h` It's disjunctively dual licensed as (GPL-2.0-only OR BSD-2-Clause), hence, compatible with the license of OpenSBI. It's heavily modified and renamed as platform/generic/include/eswin/hfp.h. The author and copyright in the original file are retained. Validated shutdown/cold reboot working on Hifive Premier P550. [1] https://www.sifive.com/boards/hifive-premier-p550#documentation [2] https://github.com/eswincomputing/hifive-premier-p550-mcu-patches.git Signed-off-by: Bo Gan <ganboing@gmail.com> Reviewed-by: Anup Patel <anup@brainfault.org> Link: https://lore.kernel.org/r/20251218104243.562667-8-ganboing@gmail.com Signed-off-by: Anup Patel <anup@brainfault.org>
119 lines
2.9 KiB
C
119 lines
2.9 KiB
C
/*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* Copyright (c) 2025 Bo Gan <ganboing@gmail.com>
|
|
*
|
|
*/
|
|
|
|
#include <sbi/riscv_io.h>
|
|
#include <sbi/sbi_string.h>
|
|
#include <sbi/sbi_system.h>
|
|
#include <sbi/sbi_hart.h>
|
|
#include <sbi/sbi_ecall_interface.h>
|
|
#include <sbi_utils/serial/uart8250.h>
|
|
#include <eswin/eic770x.h>
|
|
#include <eswin/hfp.h>
|
|
|
|
/* HFP -> HiFive Premier P550 */
|
|
|
|
#define HFP_MCU_UART_PORT 2
|
|
#define HFP_MCU_UART_BAUDRATE 115200
|
|
|
|
static unsigned long eic770x_sysclk_rate(void)
|
|
{
|
|
/* syscfg clock is a mux of 24Mhz xtal clock and spll0_fout3/divisor */
|
|
uint32_t syscfg_clk = readl_relaxed((void*)EIC770X_SYSCRG_SYSCLK);
|
|
|
|
if (EIC770X_SYSCLK_SEL(syscfg_clk))
|
|
return EIC770X_XTAL_CLK_RATE;
|
|
|
|
return EIC770X_SPLL0_OUT3_RATE / EIC770X_SYSCLK_DIV(syscfg_clk);
|
|
}
|
|
|
|
static void eic770x_enable_uart_clk(unsigned port)
|
|
{
|
|
uint32_t lsp_clk_en = readl_relaxed((void*)EIC770X_SYSCRG_LSPCLK0);
|
|
|
|
lsp_clk_en |= EIC770X_UART_CLK_BIT(port);
|
|
writel(lsp_clk_en, (void*)EIC770X_SYSCRG_LSPCLK0);
|
|
}
|
|
|
|
static void hfp_send_bmc_msg(uint8_t type, uint8_t cmd,
|
|
const uint8_t *data, uint8_t len)
|
|
{
|
|
unsigned long sysclk_rate;
|
|
struct uart8250_device uart_dev;
|
|
union {
|
|
struct hfp_bmc_message msg;
|
|
char as_char[sizeof(struct hfp_bmc_message)];
|
|
} xmit = {{
|
|
.header_magic = MAGIC_HEADER,
|
|
.type = type,
|
|
.cmd = cmd,
|
|
.data_len = len,
|
|
.tail_magic = MAGIC_TAIL,
|
|
}};
|
|
|
|
/**
|
|
* Re-initialize UART.
|
|
* S-mode OS may have changed the clock frequency of syscfg clock
|
|
* which is the clock of all low speed peripherals, including UARTs.
|
|
* S-mode OS may also have disabled the UART2 clock via clock gate.
|
|
* (lsp_clk_en0 bit 17-21 controls UART0-4). Thus, we re-calculate
|
|
* the clock rate, enable UART clock, and re-initialize UART.
|
|
*/
|
|
|
|
sysclk_rate = eic770x_sysclk_rate();
|
|
eic770x_enable_uart_clk(HFP_MCU_UART_PORT);
|
|
|
|
uart8250_device_init(&uart_dev,
|
|
EIC770X_UART(HFP_MCU_UART_PORT),
|
|
sysclk_rate,
|
|
HFP_MCU_UART_BAUDRATE,
|
|
EIC770X_UART_REG_SHIFT,
|
|
EIC770X_UART_REG_WIDTH,
|
|
0, 0);
|
|
|
|
sbi_memcpy(&xmit.msg.data, data, len);
|
|
hfp_bmc_checksum_msg(&xmit.msg);
|
|
|
|
for (unsigned int i = 0; i < sizeof(xmit.as_char); i++)
|
|
uart8250_device_putc(&uart_dev, xmit.as_char[i]);
|
|
}
|
|
|
|
static int hfp_system_reset_check(u32 type, u32 reason)
|
|
{
|
|
switch (type) {
|
|
case SBI_SRST_RESET_TYPE_COLD_REBOOT:
|
|
case SBI_SRST_RESET_TYPE_SHUTDOWN:
|
|
return 255;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void hfp_system_reset(u32 type, u32 reason)
|
|
{
|
|
switch (type) {
|
|
case SBI_SRST_RESET_TYPE_SHUTDOWN:
|
|
hfp_send_bmc_msg(HFP_MSG_NOTIFY, HFP_CMD_POWER_OFF,
|
|
NULL, 0);
|
|
break;
|
|
case SBI_SRST_RESET_TYPE_COLD_REBOOT:
|
|
hfp_send_bmc_msg(HFP_MSG_NOTIFY, HFP_CMD_RESTART,
|
|
NULL, 0);
|
|
break;
|
|
}
|
|
sbi_hart_hang();
|
|
}
|
|
|
|
static struct sbi_system_reset_device hfp_reset = {
|
|
.name = "hfp_reset",
|
|
.system_reset_check = hfp_system_reset_check,
|
|
.system_reset = hfp_system_reset,
|
|
};
|
|
|
|
const struct eic770x_board_override hfp_override = {
|
|
.reset_dev = &hfp_reset,
|
|
};
|