| /* |
| * SPDX-License-Identifier: BSD-2-Clause |
| * |
| * Copyright (c) Nuclei Corporation or its affiliates. |
| * |
| * Authors: |
| * lujun <lujun@nucleisys.com> |
| * hqfang <578567190@qq.com> |
| */ |
| |
| #include <sbi/riscv_asm.h> |
| #include <sbi/riscv_io.h> |
| #include <sbi/riscv_encoding.h> |
| #include <sbi/sbi_console.h> |
| #include <sbi/sbi_const.h> |
| #include <sbi/sbi_platform.h> |
| #include <sbi/sbi_system.h> |
| #include <sbi_utils/fdt/fdt_helper.h> |
| #include <sbi_utils/fdt/fdt_fixup.h> |
| #include <sbi_utils/ipi/aclint_mswi.h> |
| #include <sbi_utils/irqchip/plic.h> |
| #include <sbi_utils/serial/sifive-uart.h> |
| #include <sbi_utils/timer/aclint_mtimer.h> |
| |
| /* clang-format off */ |
| |
| #define UX600_HART_COUNT 1 |
| #define UX600_TIMER_FREQ 32768 |
| |
| /* Nuclei timer base address */ |
| #define UX600_NUCLEI_TIMER_ADDR 0x2000000 |
| #define UX600_NUCLEI_TIMER_MSFTRST_OFS 0xFF0 |
| #define UX600_NUCLEI_TIMER_MSFTRST_KEY 0x80000A5F |
| /* The clint compatiable timer offset is 0x1000 against nuclei timer */ |
| #define UX600_CLINT_TIMER_ADDR (UX600_NUCLEI_TIMER_ADDR + 0x1000) |
| #define UX600_ACLINT_MSWI_ADDR (UX600_CLINT_TIMER_ADDR + \ |
| CLINT_MSWI_OFFSET) |
| #define UX600_ACLINT_MTIMER_ADDR (UX600_CLINT_TIMER_ADDR + \ |
| CLINT_MTIMER_OFFSET) |
| |
| #define UX600_PLIC_ADDR 0x8000000 |
| #define UX600_PLIC_SIZE (0x200000 + \ |
| (UX600_HART_COUNT * 0x1000)) |
| #define UX600_PLIC_NUM_SOURCES 0x35 |
| #define UX600_PLIC_NUM_PRIORITIES 7 |
| |
| #define UX600_UART0_ADDR 0x10013000 |
| #define UX600_UART1_ADDR 0x10023000 |
| |
| #define UX600_DEBUG_UART UX600_UART0_ADDR |
| |
| #ifndef UX600_UART_BAUDRATE |
| #define UX600_UART_BAUDRATE 57600 |
| #endif |
| |
| #define UX600_GPIO_ADDR 0x10012000 |
| #define UX600_GPIO_IOF_EN_OFS 0x38 |
| #define UX600_GPIO_IOF_SEL_OFS 0x3C |
| #define UX600_GPIO_IOF_UART0_MASK 0x00030000 |
| |
| #define UX600_TIMER_VALUE() readl((void *)UX600_NUCLEI_TIMER_ADDR) |
| |
| /* clang-format on */ |
| static u32 ux600_clk_freq = 8000000; |
| |
| static struct plic_data plic = { |
| .addr = UX600_PLIC_ADDR, |
| .size = UX600_PLIC_SIZE, |
| .num_src = UX600_PLIC_NUM_SOURCES, |
| }; |
| |
| static struct aclint_mswi_data mswi = { |
| .addr = UX600_ACLINT_MSWI_ADDR, |
| .size = ACLINT_MSWI_SIZE, |
| .first_hartid = 0, |
| .hart_count = UX600_HART_COUNT, |
| }; |
| |
| static struct aclint_mtimer_data mtimer = { |
| .mtime_freq = UX600_TIMER_FREQ, |
| .mtime_addr = UX600_ACLINT_MTIMER_ADDR + |
| ACLINT_DEFAULT_MTIME_OFFSET, |
| .mtime_size = ACLINT_DEFAULT_MTIME_SIZE, |
| .mtimecmp_addr = UX600_ACLINT_MTIMER_ADDR + |
| ACLINT_DEFAULT_MTIMECMP_OFFSET, |
| .mtimecmp_size = ACLINT_DEFAULT_MTIMECMP_SIZE, |
| .first_hartid = 0, |
| .hart_count = UX600_HART_COUNT, |
| .has_64bit_mmio = true, |
| }; |
| |
| static u32 measure_cpu_freq(u32 n) |
| { |
| u32 start_mtime, delta_mtime; |
| u32 mtime_freq = UX600_TIMER_FREQ; |
| u32 tmp = (u32)UX600_TIMER_VALUE(); |
| u32 start_mcycle, delta_mcycle, freq; |
| |
| /* Don't start measuring until we see an mtime tick */ |
| do { |
| start_mtime = (u32)UX600_TIMER_VALUE(); |
| } while (start_mtime == tmp); |
| |
| start_mcycle = csr_read(mcycle); |
| |
| do { |
| delta_mtime = (u32)UX600_TIMER_VALUE() - start_mtime; |
| } while (delta_mtime < n); |
| |
| delta_mcycle = csr_read(mcycle) - start_mcycle; |
| |
| freq = (delta_mcycle / delta_mtime) * mtime_freq |
| + ((delta_mcycle % delta_mtime) * mtime_freq) / delta_mtime; |
| |
| return freq; |
| } |
| |
| static u32 ux600_get_clk_freq(void) |
| { |
| u32 cpu_freq; |
| |
| /* warm up */ |
| measure_cpu_freq(1); |
| /* measure for real */ |
| cpu_freq = measure_cpu_freq(100); |
| |
| return cpu_freq; |
| } |
| |
| static int ux600_system_reset_check(u32 type, u32 reason) |
| { |
| return 1; |
| } |
| |
| static void ux600_system_reset(u32 type, u32 reason) |
| { |
| /* Reset system using MSFTRST register in Nuclei Timer. */ |
| writel(UX600_NUCLEI_TIMER_MSFTRST_KEY, (void *)(UX600_NUCLEI_TIMER_ADDR |
| + UX600_NUCLEI_TIMER_MSFTRST_OFS)); |
| while(1); |
| } |
| |
| static struct sbi_system_reset_device ux600_reset = { |
| .name = "nuclei_ux600_reset", |
| .system_reset_check = ux600_system_reset_check, |
| .system_reset = ux600_system_reset |
| }; |
| |
| static int ux600_early_init(bool cold_boot) |
| { |
| u32 regval; |
| |
| if (cold_boot) |
| sbi_system_reset_add_device(&ux600_reset); |
| |
| /* Measure CPU Frequency using Timer */ |
| ux600_clk_freq = ux600_get_clk_freq(); |
| |
| /* Init GPIO UART pinmux */ |
| regval = readl((void *)(UX600_GPIO_ADDR + UX600_GPIO_IOF_SEL_OFS)) & |
| ~UX600_GPIO_IOF_UART0_MASK; |
| writel(regval, (void *)(UX600_GPIO_ADDR + UX600_GPIO_IOF_SEL_OFS)); |
| regval = readl((void *)(UX600_GPIO_ADDR + UX600_GPIO_IOF_EN_OFS)) | |
| UX600_GPIO_IOF_UART0_MASK; |
| writel(regval, (void *)(UX600_GPIO_ADDR + UX600_GPIO_IOF_EN_OFS)); |
| return 0; |
| } |
| |
| static void ux600_modify_dt(void *fdt) |
| { |
| fdt_fixups(fdt); |
| } |
| |
| static int ux600_final_init(bool cold_boot) |
| { |
| void *fdt; |
| |
| if (!cold_boot) |
| return 0; |
| |
| fdt = fdt_get_address(); |
| ux600_modify_dt(fdt); |
| |
| return 0; |
| } |
| |
| static int ux600_console_init(void) |
| { |
| return sifive_uart_init(UX600_DEBUG_UART, ux600_clk_freq, |
| UX600_UART_BAUDRATE); |
| } |
| |
| static int ux600_irqchip_init(bool cold_boot) |
| { |
| int rc; |
| u32 hartid = current_hartid(); |
| |
| if (cold_boot) { |
| rc = plic_cold_irqchip_init(&plic); |
| if (rc) |
| return rc; |
| } |
| |
| return plic_warm_irqchip_init(&plic, (hartid) ? (2 * hartid - 1) : 0, |
| (hartid) ? (2 * hartid) : -1); |
| } |
| |
| static int ux600_ipi_init(bool cold_boot) |
| { |
| int rc; |
| |
| if (cold_boot) { |
| rc = aclint_mswi_cold_init(&mswi); |
| if (rc) |
| return rc; |
| } |
| |
| return aclint_mswi_warm_init(); |
| } |
| |
| static int ux600_timer_init(bool cold_boot) |
| { |
| int rc; |
| |
| if (cold_boot) { |
| rc = aclint_mtimer_cold_init(&mtimer, NULL); |
| if (rc) |
| return rc; |
| } |
| |
| return aclint_mtimer_warm_init(); |
| } |
| |
| const struct sbi_platform_operations platform_ops = { |
| .early_init = ux600_early_init, |
| .final_init = ux600_final_init, |
| .console_init = ux600_console_init, |
| .irqchip_init = ux600_irqchip_init, |
| .ipi_init = ux600_ipi_init, |
| .timer_init = ux600_timer_init, |
| }; |
| |
| const struct sbi_platform platform = { |
| .opensbi_version = OPENSBI_VERSION, |
| .platform_version = SBI_PLATFORM_VERSION(0x0U, 0x01U), |
| .name = "Nuclei UX600", |
| .features = SBI_PLATFORM_DEFAULT_FEATURES, |
| .hart_count = UX600_HART_COUNT, |
| .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, |
| .heap_size = |
| SBI_PLATFORM_DEFAULT_HEAP_SIZE(UX600_HART_COUNT), |
| .platform_ops_addr = (unsigned long)&platform_ops |
| }; |