mirror of
https://github.com/riscv-software-src/opensbi.git
synced 2025-12-22 06:12:02 +00:00
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:
118
platform/generic/eswin/hfp.c
Normal file
118
platform/generic/eswin/hfp.c
Normal 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,
|
||||
};
|
||||
Reference in New Issue
Block a user