platform: generic: eswin: Add shutdown/reboot support for Hifive Premier P550

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>
This commit is contained in:
Bo Gan
2025-12-18 02:42:43 -08:00
committed by Anup Patel
parent fbff3a7e07
commit 73183a60ab
5 changed files with 207 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
/*
* 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,
};