/* * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2025 Bo Gan * */ #include #include #include #include #include #include #include #include /* 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, };