diff options
author | Nylon Chen <nylon7@andestech.com> | 2019-07-18 10:52:25 +0300 |
---|---|---|
committer | Anup Patel <anup@brainfault.org> | 2019-08-23 13:55:56 +0300 |
commit | 3cbb419def46e95b04d43f2d95f126f653e55589 (patch) | |
tree | 144ad198c2fdc185082a6034946ff0d9e07c7a0a /platform/andes | |
parent | a2a7763ac7651e64c3928ba0a13be9317bd48c4d (diff) | |
download | opensbi-3cbb419def46e95b04d43f2d95f126f653e55589.tar.xz |
platform: Add Andes AE350 initial support
This commit provides basic support for the AE350 platform.
Signed-off-by: Zong Li <zongbox@gmail.com>
Signed-off-by: Nylon Chen <nylon7@andestech.com>
Diffstat (limited to 'platform/andes')
-rw-r--r-- | platform/andes/ae350/config.mk | 36 | ||||
-rw-r--r-- | platform/andes/ae350/objects.mk | 11 | ||||
-rw-r--r-- | platform/andes/ae350/platform.c | 194 | ||||
-rw-r--r-- | platform/andes/ae350/platform.h | 67 | ||||
-rw-r--r-- | platform/andes/ae350/plicsw.c | 145 | ||||
-rw-r--r-- | platform/andes/ae350/plicsw.h | 46 | ||||
-rw-r--r-- | platform/andes/ae350/plmt.c | 97 | ||||
-rw-r--r-- | platform/andes/ae350/plmt.h | 23 |
8 files changed, 619 insertions, 0 deletions
diff --git a/platform/andes/ae350/config.mk b/platform/andes/ae350/config.mk new file mode 100644 index 0000000..f555ef5 --- /dev/null +++ b/platform/andes/ae350/config.mk @@ -0,0 +1,36 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Andes Technology Corporation +# +# Authors: +# Zong Li <zong@andestech.com> +# Nylon Chen <nylon7@andestech.com> + +# Compiler flags +platform-cppflags-y = +platform-cflags-y = +platform-asflags-y = +platform-ldflags-y = + +# Blobs to build +FW_TEXT_START=0x00000000 + +FW_DYNAMIC=y + +FW_JUMP=y +ifeq ($(PLATFORM_RISCV_XLEN), 32) + FW_JUMP_ADDR=0x400000 +else + FW_JUMP_ADDR=0x200000 +endif +FW_JUMP_FDT_ADDR=0x2000000 + +FW_PAYLOAD=y +ifeq ($(PLATFORM_RISCV_XLEN), 32) + FW_PAYLOAD_OFFSET=0x400000 +else + FW_PAYLOAD_OFFSET=0x200000 +endif + +FW_PAYLOAD_FDT_ADDR=0x2000000 diff --git a/platform/andes/ae350/objects.mk b/platform/andes/ae350/objects.mk new file mode 100644 index 0000000..4ed152e --- /dev/null +++ b/platform/andes/ae350/objects.mk @@ -0,0 +1,11 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Andes Technology Corporation +# +# Authors: +# Zong Li <zong@andestech.com> +# Nylon Chen <nylon7@andestech.com> +# + +platform-objs-y += plicsw.o plmt.o platform.o diff --git a/platform/andes/ae350/platform.c b/platform/andes/ae350/platform.c new file mode 100644 index 0000000..d14266d --- /dev/null +++ b/platform/andes/ae350/platform.c @@ -0,0 +1,194 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Andes Technology Corporation + * + * Authors: + * Zong Li <zong@andestech.com> + * Nylon Chen <nylon7@andestech.com> + */ + +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_const.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_console.h> +#include <sbi_utils/serial/uart8250.h> +#include <sbi_utils/irqchip/plic.h> +#include "platform.h" +#include "plmt.h" +#include "plicsw.h" + +/* Platform final initialization. */ +static int ae350_final_init(bool cold_boot) +{ + void *fdt; + + /* enable L1 cache */ + uintptr_t mcache_ctl_val = csr_read(CSR_MCACHECTL); + + if (!(mcache_ctl_val & V5_MCACHE_CTL_IC_EN)) + mcache_ctl_val |= V5_MCACHE_CTL_IC_EN; + if (!(mcache_ctl_val & V5_MCACHE_CTL_DC_EN)) + mcache_ctl_val |= V5_MCACHE_CTL_DC_EN; + if (!(mcache_ctl_val & V5_MCACHE_CTL_CCTL_SUEN)) + mcache_ctl_val |= V5_MCACHE_CTL_CCTL_SUEN; + csr_write(CSR_MCACHECTL, mcache_ctl_val); + + /* enable L2 cache */ + uint32_t *l2c_ctl_base = (void *)AE350_L2C_ADDR + V5_L2C_CTL_OFFSET; + uint32_t l2c_ctl_val = *l2c_ctl_base; + + if (!(l2c_ctl_val & V5_L2C_CTL_ENABLE_MASK)) + l2c_ctl_val |= V5_L2C_CTL_ENABLE_MASK; + *l2c_ctl_base = l2c_ctl_val; + + if (!cold_boot) + return 0; + + fdt = sbi_scratch_thishart_arg1_ptr(); + plic_fdt_fixup(fdt, "riscv,plic0"); + + return 0; +} + +/* Get number of PMP regions for given HART. */ +static u32 ae350_pmp_region_count(u32 hartid) +{ + return 1; +} + +/* + * Get PMP regions details (namely: protection, base address, and size) for + * a given HART. + */ +static int ae350_pmp_region_info(u32 hartid, u32 index, ulong *prot, + ulong *addr, ulong *log2size) +{ + int ret = 0; + + switch (index) { + case 0: + *prot = PMP_R | PMP_W | PMP_X; + *addr = 0; + *log2size = __riscv_xlen; + break; + default: + ret = -1; + break; + }; + + return ret; +} + +/* Initialize the platform console. */ +static int ae350_console_init(void) +{ + return uart8250_init(AE350_UART_ADDR, + AE350_UART_FREQUENCY, + AE350_UART_BAUDRATE, + AE350_UART_REG_SHIFT, + AE350_UART_REG_WIDTH); +} + +/* Initialize the platform interrupt controller for current HART. */ +static int ae350_irqchip_init(bool cold_boot) +{ + u32 hartid = sbi_current_hartid(); + int ret; + + if (cold_boot) { + ret = plic_cold_irqchip_init(AE350_PLIC_ADDR, + AE350_PLIC_NUM_SOURCES, + AE350_HART_COUNT); + if (ret) + return ret; + } + + return plic_warm_irqchip_init(hartid, 2 * hartid, 2 * hartid + 1); +} + +/* Initialize IPI for current HART. */ +static int ae350_ipi_init(bool cold_boot) +{ + int ret; + + if (cold_boot) { + ret = plicsw_cold_ipi_init(AE350_PLICSW_ADDR, + AE350_HART_COUNT); + if (ret) + return ret; + } + + return plicsw_warm_ipi_init(); +} + +/* Initialize platform timer for current HART. */ +static int ae350_timer_init(bool cold_boot) +{ + int ret; + + if (cold_boot) { + ret = plmt_cold_timer_init(AE350_PLMT_ADDR, + AE350_HART_COUNT); + if (ret) + return ret; + } + + return plmt_warm_timer_init(); +} + +/* Reboot the platform. */ +static int ae350_system_reboot(u32 type) +{ + /* For now nothing to do. */ + sbi_printf("System reboot\n"); + return 0; +} + +/* Shutdown or poweroff the platform. */ +static int ae350_system_shutdown(u32 type) +{ + /* For now nothing to do. */ + sbi_printf("System shutdown\n"); + return 0; +} + +/* Platform descriptor. */ +const struct sbi_platform_operations platform_ops = { + + .final_init = ae350_final_init, + + .pmp_region_count = ae350_pmp_region_count, + .pmp_region_info = ae350_pmp_region_info, + + .console_init = ae350_console_init, + .console_putc = uart8250_putc, + .console_getc = uart8250_getc, + + .irqchip_init = ae350_irqchip_init, + + .ipi_init = ae350_ipi_init, + .ipi_send = plicsw_ipi_send, + .ipi_clear = plicsw_ipi_clear, + + .timer_init = ae350_timer_init, + .timer_value = plmt_timer_value, + .timer_event_start = plmt_timer_event_start, + .timer_event_stop = plmt_timer_event_stop, + + .system_reboot = ae350_system_reboot, + .system_shutdown = ae350_system_shutdown +}; + +const struct sbi_platform platform = { + + .opensbi_version = OPENSBI_VERSION, + .platform_version = SBI_PLATFORM_VERSION(0x0, 0x01), + .name = "Andes AE350", + .features = SBI_PLATFORM_DEFAULT_FEATURES, + .hart_count = AE350_HART_COUNT, + .hart_stack_size = AE350_HART_STACK_SIZE, + .disabled_hart_mask = 0, + .platform_ops_addr = (unsigned long)&platform_ops +}; diff --git a/platform/andes/ae350/platform.h b/platform/andes/ae350/platform.h new file mode 100644 index 0000000..eff2996 --- /dev/null +++ b/platform/andes/ae350/platform.h @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Andes Technology Corporation + * + * Authors: + * Zong Li <zong@andestech.com> + * Nylon Chen <nylon7@andestech.com> + */ + +#ifndef _AE350_PLATFORM_H_ +#define _AE350_PLATFORM_H_ + +#define AE350_HART_COUNT 4 +#define AE350_HART_STACK_SIZE 8192 + +#define AE350_PLIC_ADDR 0xe4000000 +#define AE350_PLIC_NUM_SOURCES 71 + +#define AE350_PLICSW_ADDR 0xe6400000 + +#define AE350_PLMT_ADDR 0xe6000000 + +#define AE350_L2C_ADDR 0xe0500000 + +#define AE350_UART_ADDR_OFFSET 0x20 +#define AE350_UART_ADDR (0xf0300000 + AE350_UART_ADDR_OFFSET) +#define AE350_UART_FREQUENCY 19660800 +#define AE350_UART_BAUDRATE 38400 +#define AE350_UART_REG_SHIFT 2 +#define AE350_UART_REG_WIDTH 0 + +/* nds mcache_ctl register*/ +#define CSR_MCACHECTL 0x7ca + +#define V5_MCACHE_CTL_IC_EN_OFFSET 0 +#define V5_MCACHE_CTL_DC_EN_OFFSET 1 +#define V5_MCACHE_CTL_IC_ECCEN_OFFSET 2 +#define V5_MCACHE_CTL_DC_ECCEN_OFFSET 4 +#define V5_MCACHE_CTL_IC_RWECC_OFFSET 6 +#define V5_MCACHE_CTL_DC_RWECC_OFFSET 7 +#define V5_MCACHE_CTL_CCTL_SUEN_OFFSET 8 + +#define V5_MCACHE_CTL_IC_EN (1UL << V5_MCACHE_CTL_IC_EN_OFFSET) +#define V5_MCACHE_CTL_DC_EN (1UL << V5_MCACHE_CTL_DC_EN_OFFSET) +#define V5_MCACHE_CTL_IC_RWECC (1UL << V5_MCACHE_CTL_IC_RWECC_OFFSET) +#define V5_MCACHE_CTL_DC_RWECC (1UL << V5_MCACHE_CTL_DC_RWECC_OFFSET) +#define V5_MCACHE_CTL_CCTL_SUEN (1UL << V5_MCACHE_CTL_CCTL_SUEN_OFFSET) + +#define V5_L2C_CTL_OFFSET 0x8 +#define V5_L2C_CTL_ENABLE_OFFSET 0 +#define V5_L2C_CTL_IPFDPT_OFFSET 3 +#define V5_L2C_CTL_DPFDPT_OFFSET 5 +#define V5_L2C_CTL_TRAMOCTL_OFFSET 8 +#define V5_L2C_CTL_TRAMICTL_OFFSET 10 +#define V5_L2C_CTL_DRAMOCTL_OFFSET 11 +#define V5_L2C_CTL_DRAMICTL_OFFSET 13 + +#define V5_L2C_CTL_ENABLE_MASK (1UL << V5_L2C_CTL_ENABLE_OFFSET) +#define V5_L2C_CTL_IPFDPT_MASK (3UL << V5_L2C_CTL_IPFDPT_OFFSET) +#define V5_L2C_CTL_DPFDPT_MASK (3UL << V5_L2C_CTL_DPFDPT_OFFSET) +#define V5_L2C_CTL_TRAMOCTL_MASK (3UL << V5_L2C_CTL_TRAMOCTL_OFFSET) +#define V5_L2C_CTL_TRAMICTL_MASK (1UL << V5_L2C_CTL_TRAMICTL_OFFSET) +#define V5_L2C_CTL_DRAMOCTL_MASK (3UL << V5_L2C_CTL_DRAMOCTL_OFFSET) +#define V5_L2C_CTL_DRAMICTL_MASK (1UL << V5_L2C_CTL_DRAMICTL_OFFSET) + +#endif /* _AE350_PLATFORM_H_ */ diff --git a/platform/andes/ae350/plicsw.c b/platform/andes/ae350/plicsw.c new file mode 100644 index 0000000..17fe947 --- /dev/null +++ b/platform/andes/ae350/plicsw.c @@ -0,0 +1,145 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Andes Technology Corporation + * + * Authors: + * Zong Li <zong@andestech.com> + * Nylon Chen <nylon7@andestech.com> + */ + +#include <sbi/sbi_types.h> +#include <sbi/sbi_hart.h> +#include <sbi/riscv_io.h> +#include "plicsw.h" +#include "platform.h" + +static u32 plicsw_ipi_hart_count; +static struct plicsw plicsw_dev[AE350_HART_COUNT]; + +static inline void plicsw_claim(void) +{ + u32 source_hart = sbi_current_hartid(); + + plicsw_dev[source_hart].source_id = + readl(plicsw_dev[source_hart].plicsw_claim); +} + +static inline void plicsw_complete(void) +{ + u32 source_hart = sbi_current_hartid(); + u32 source = plicsw_dev[source_hart].source_id; + + writel(source, plicsw_dev[source_hart].plicsw_claim); +} + +static inline u32 plicsw_get_pending(u32 source_hart, u32 target_hart) +{ + return readl(plicsw_dev[source_hart].plicsw_pending) + & (PLICSW_HART_MASK >> target_hart); +} + +static inline void plic_sw_pending(u32 target_hart) +{ + /* + * The pending array registers are w1s type. + * IPI pending array mapping as following: + * + * Pending array start address: base + 0x1000 + * ------------------------------------- + * | hart 3 | hart 2 | hart 1 | hart 0 | + * ------------------------------------- + * Each hart X can send IPI to another hart by setting the + * corresponding bit in hart X own region(see the below). + * + * In each hart region: + * ----------------------------------------------- + * | bit 7 | bit 6 | bit 5 | bit 4 | ... | bit 0 | + * ----------------------------------------------- + * The bit 7 is used to send IPI to hart 0 + * The bit 6 is used to send IPI to hart 1 + * The bit 5 is used to send IPI to hart 2 + * The bit 4 is used to send IPI to hart 3 + */ + u32 source_hart = sbi_current_hartid(); + u32 target_offset = (PLICSW_PENDING_PER_HART - 1) - target_hart; + u32 per_hart_offset = PLICSW_PENDING_PER_HART * source_hart; + u32 val = 1 << target_offset << per_hart_offset; + + writel(val, plicsw_dev[source_hart].plicsw_pending); +} + +void plicsw_ipi_send(u32 target_hart) +{ + if (plicsw_ipi_hart_count <= target_hart) + return; + + /* Set PLICSW IPI */ + plic_sw_pending(target_hart); +} + +void plicsw_ipi_clear(u32 target_hart) +{ + if (plicsw_ipi_hart_count <= target_hart) + return; + + /* Clear CLINT IPI */ + plicsw_claim(); + plicsw_complete(); +} + +int plicsw_warm_ipi_init(void) +{ + u32 hartid = sbi_current_hartid(); + + if (!plicsw_dev[hartid].plicsw_pending + && !plicsw_dev[hartid].plicsw_enable + && !plicsw_dev[hartid].plicsw_claim) + return -1; + + /* Clear PLICSW IPI */ + plicsw_ipi_clear(hartid); + + return 0; +} + +int plicsw_cold_ipi_init(unsigned long base, u32 hart_count) +{ + /* Setup source priority */ + uint32_t *priority = (void *)base + PLICSW_PRIORITY_BASE; + + for (int i = 0; i < AE350_HART_COUNT*PLICSW_PENDING_PER_HART; i++) + writel(1, &priority[i]); + + /* Setup target enable.*/ + uint32_t enable_mask = PLICSW_HART_MASK; + + for (int i = 0; i < AE350_HART_COUNT; i++) { + uint32_t *enable = (void *)base + PLICSW_ENABLE_BASE + + PLICSW_ENABLE_PER_HART * i; + writel(enable_mask, &enable[0]); + enable_mask >>= 1; + } + + /* Figure-out PLICSW IPI register address */ + plicsw_ipi_hart_count = hart_count; + + for (u32 hartid = 0; hartid < AE350_HART_COUNT; hartid++) { + plicsw_dev[hartid].source_id = 0; + plicsw_dev[hartid].plicsw_pending = + (void *)base + + PLICSW_PENDING_BASE + + ((hartid / 4) * 4); + plicsw_dev[hartid].plicsw_enable = + (void *)base + + PLICSW_ENABLE_BASE + + PLICSW_ENABLE_PER_HART * hartid; + plicsw_dev[hartid].plicsw_claim = + (void *)base + + PLICSW_CONTEXT_BASE + + PLICSW_CONTEXT_CLAIM + + PLICSW_CONTEXT_PER_HART * hartid; + } + + return 0; +} diff --git a/platform/andes/ae350/plicsw.h b/platform/andes/ae350/plicsw.h new file mode 100644 index 0000000..8be6194 --- /dev/null +++ b/platform/andes/ae350/plicsw.h @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Andes Technology Corporation + * + * Authors: + * Zong Li <zong@andestech.com> + * Nylon Chen <nylon7@andestech.com> + */ + +#ifndef _AE350_PLICSW_H_ +#define _AE350_PLICSW_H_ + +#define PLICSW_PRIORITY_BASE 0x4 + +#define PLICSW_PENDING_BASE 0x1000 +#define PLICSW_PENDING_PER_HART 0x8 + +#define PLICSW_ENABLE_BASE 0x2000 +#define PLICSW_ENABLE_PER_HART 0x80 + +#define PLICSW_CONTEXT_BASE 0x200000 +#define PLICSW_CONTEXT_PER_HART 0x1000 +#define PLICSW_CONTEXT_CLAIM 0x4 + +#define PLICSW_HART_MASK 0x80808080 + +struct plicsw { + u32 source_id; + + volatile uint32_t *plicsw_pending; + volatile uint32_t *plicsw_enable; + volatile uint32_t *plicsw_claim; +}; + +void plicsw_ipi_send(u32 target_hart); + +void plicsw_ipi_sync(u32 target_hart); + +void plicsw_ipi_clear(u32 target_hart); + +int plicsw_warm_ipi_init(void); + +int plicsw_cold_ipi_init(unsigned long base, u32 hart_count); + +#endif /* _AE350_PLICSW_H_ */ diff --git a/platform/andes/ae350/plmt.c b/platform/andes/ae350/plmt.c new file mode 100644 index 0000000..db80813 --- /dev/null +++ b/platform/andes/ae350/plmt.c @@ -0,0 +1,97 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Andes Technology Corporation + * + * Authors: + * Zong Li <zong@andestech.com> + * Nylon Chen <nylon7@andestech.com> + */ + +#include <sbi/riscv_io.h> +#include <sbi/sbi_hart.h> + +static u32 plmt_time_hart_count; +static volatile void *plmt_time_base; +static volatile u64 *plmt_time_val; +static volatile u64 *plmt_time_cmp; + +u64 plmt_timer_value(void) +{ +#if __riscv_xlen == 64 + return readq_relaxed(plmt_time_val); +#else + u32 lo, hi; + + do { + hi = readl_relaxed((void *)plmt_time_val + 0x04); + lo = readl_relaxed(plmt_time_val); + } while (hi != readl_relaxed((void *)plmt_time_val + 0x04)); + + return ((u64)hi << 32) | (u64)lo; +#endif +} + +void plmt_timer_event_stop(void) +{ + u32 target_hart = sbi_current_hartid(); + + if (plmt_time_hart_count <= target_hart) + return; + + /* Clear PLMT Time Compare */ +#if __riscv_xlen == 64 + writeq_relaxed(-1ULL, &plmt_time_cmp[target_hart]); +#else + writel_relaxed(-1UL, &plmt_time_cmp[target_hart]); + writel_relaxed(-1UL, (void *)(&plmt_time_cmp[target_hart]) + 0x04); +#endif +} + +void plmt_timer_event_start(u64 next_event) +{ + u32 target_hart = sbi_current_hartid(); + + if (plmt_time_hart_count <= target_hart) + return; + + /* Program PLMT Time Compare */ +#if __riscv_xlen == 64 + writeq_relaxed(next_event, &plmt_time_cmp[target_hart]); +#else + u32 mask = -1UL; + + writel_relaxed(next_event & mask, &plmt_time_cmp[target_hart]); + writel_relaxed(next_event >> 32, + (void *)(&plmt_time_cmp[target_hart]) + 0x04); +#endif + +} + +int plmt_warm_timer_init(void) +{ + u32 target_hart = sbi_current_hartid(); + + if (plmt_time_hart_count <= target_hart || !plmt_time_base) + return -1; + + /* Clear PLMT Time Compare */ +#if __riscv_xlen == 64 + writeq_relaxed(-1ULL, &plmt_time_cmp[target_hart]); +#else + writel_relaxed(-1UL, &plmt_time_cmp[target_hart]); + writel_relaxed(-1UL, (void *)(&plmt_time_cmp[target_hart]) + 0x04); +#endif + + return 0; +} + +int plmt_cold_timer_init(unsigned long base, u32 hart_count) +{ + plmt_time_hart_count = hart_count; + plmt_time_base = (void *)base; + plmt_time_val = (u64 *)(plmt_time_base); + plmt_time_cmp = (u64 *)(plmt_time_base + 0x8); + + return 0; +} diff --git a/platform/andes/ae350/plmt.h b/platform/andes/ae350/plmt.h new file mode 100644 index 0000000..129fcf8 --- /dev/null +++ b/platform/andes/ae350/plmt.h @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Andes Technology Corporation + * + * Authors: + * Zong Li <zong@andestech.com> + */ + +#ifndef _AE350_PLMT_H_ +#define _AE350_PLMT_H_ + +u64 plmt_timer_value(void); + +void plmt_timer_event_stop(void); + +void plmt_timer_event_start(u64 next_event); + +int plmt_warm_timer_init(void); + +int plmt_cold_timer_init(unsigned long base, u32 hart_count); + +#endif /* _AE350_PLMT_H_ */ |