diff options
| author | Yu Chien Peter Lin <peterlin@andestech.com> | 2022-10-14 08:32:46 +0800 |
|---|---|---|
| committer | Anup Patel <anup@brainfault.org> | 2022-10-23 10:26:39 +0530 |
| commit | ef9f02e7fba47412d6c057ba78fd3d89cb4e5fc3 (patch) | |
| tree | b74d62fd2260c139112ec40eb90975f08ffa2f65 /lib/utils | |
| parent | 88f58a3694c936791eb875d3cc85f1cde41c3d09 (diff) | |
| download | opensbi-ef9f02e7fba47412d6c057ba78fd3d89cb4e5fc3.zip opensbi-ef9f02e7fba47412d6c057ba78fd3d89cb4e5fc3.tar.gz opensbi-ef9f02e7fba47412d6c057ba78fd3d89cb4e5fc3.tar.bz2 | |
lib: utils/timer: Add Andes fdt timer support
Since we can get the PLMT base address and timer frequency from
device tree, move plmt timer device to fdt timer framework.
dts example (Quad-core AX45MP):
cpus {
...
timebase-frequency = <0x3938700>;
...
}
soc {
...
plmt0@e6000000 {
compatible = "andestech,plmt0";
reg = <0x00 0xe6000000 0x00 0x100000>;
interrupts-extended = <&cpu0_intc 0x07
&cpu1_intc 0x07
&cpu2_intc 0x07
&cpu3_intc 0x07>;
};
...
}
Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Diffstat (limited to 'lib/utils')
| -rw-r--r-- | lib/utils/fdt/fdt_helper.c | 54 | ||||
| -rw-r--r-- | lib/utils/timer/Kconfig | 9 | ||||
| -rw-r--r-- | lib/utils/timer/andes_plmt.c | 104 | ||||
| -rw-r--r-- | lib/utils/timer/fdt_timer_plmt.c | 51 | ||||
| -rw-r--r-- | lib/utils/timer/objects.mk | 4 |
5 files changed, 222 insertions, 0 deletions
diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c index 6a75d6f..ce52fca 100644 --- a/lib/utils/fdt/fdt_helper.c +++ b/lib/utils/fdt/fdt_helper.c @@ -835,6 +835,60 @@ int fdt_parse_aclint_node(void *fdt, int nodeoffset, bool for_timer, return 0; } +int fdt_parse_plmt_node(void *fdt, int nodeoffset, unsigned long *plmt_base, + unsigned long *plmt_size, u32 *hart_count) +{ + const fdt32_t *val; + int rc, i, count; + uint64_t reg_addr, reg_size, cpu_offset, cpu_intc_offset; + u32 phandle, hwirq, hartid, hcount; + + if (nodeoffset < 0 || !fdt || !plmt_base || + !hart_count || !plmt_size) + return SBI_EINVAL; + + rc = fdt_get_node_addr_size(fdt, nodeoffset, 0, + ®_addr, ®_size); + if (rc < 0 || !plmt_base || !plmt_size) + return SBI_ENODEV; + *plmt_base = reg_addr; + *plmt_size = reg_size; + + val = fdt_getprop(fdt, nodeoffset, "interrupts-extended", &count); + if (!val || count < sizeof(fdt32_t)) + return 0; + count = count / sizeof(fdt32_t); + + hcount = 0; + for (i = 0; i < (count / 2); i++) { + phandle = fdt32_to_cpu(val[2 * i]); + hwirq = fdt32_to_cpu(val[2 * i + 1]); + + cpu_intc_offset = fdt_node_offset_by_phandle(fdt, phandle); + if (cpu_intc_offset < 0) + continue; + + cpu_offset = fdt_parent_offset(fdt, cpu_intc_offset); + if (cpu_intc_offset < 0) + continue; + + rc = fdt_parse_hart_id(fdt, cpu_offset, &hartid); + + if (rc) + continue; + + if (SBI_HARTMASK_MAX_BITS <= hartid) + continue; + + if (hwirq == IRQ_M_TIMER) + hcount++; + } + + *hart_count = hcount; + + return 0; +} + int fdt_parse_compat_addr(void *fdt, uint64_t *addr, const char *compatible) { diff --git a/lib/utils/timer/Kconfig b/lib/utils/timer/Kconfig index 23c48c5..ba211b6 100644 --- a/lib/utils/timer/Kconfig +++ b/lib/utils/timer/Kconfig @@ -14,10 +14,19 @@ config FDT_TIMER_MTIMER select TIMER_MTIMER default n +config FDT_TIMER_PLMT + bool "Andes PLMT FDT driver" + select TIMER_PLMT + default n + endif config TIMER_MTIMER bool "ACLINT MTIMER support" default n +config TIMER_PLMT + bool "Andes PLMT support" + default n + endmenu diff --git a/lib/utils/timer/andes_plmt.c b/lib/utils/timer/andes_plmt.c new file mode 100644 index 0000000..94a86cc --- /dev/null +++ b/lib/utils/timer/andes_plmt.c @@ -0,0 +1,104 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 Andes Technology Corporation + * + * Authors: + * Yu Chien Peter Lin <peterlin@andestech.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_io.h> +#include <sbi/sbi_domain.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_timer.h> +#include <sbi_utils/timer/andes_plmt.h> + +struct plmt_data plmt; + +static 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 +} + +static void plmt_timer_event_stop(void) +{ + u32 target_hart = current_hartid(); + + if (plmt.hart_count <= target_hart) + ebreak(); + + /* 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 +} + +static void plmt_timer_event_start(u64 next_event) +{ + u32 target_hart = current_hartid(); + + if (plmt.hart_count <= target_hart) + ebreak(); + + /* 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 +} + +static struct sbi_timer_device plmt_timer = { + .name = "andes_plmt", + .timer_freq = DEFAULT_AE350_PLMT_FREQ, + .timer_value = plmt_timer_value, + .timer_event_start = plmt_timer_event_start, + .timer_event_stop = plmt_timer_event_stop +}; + +int plmt_cold_timer_init(struct plmt_data *plmt) +{ + int rc; + + /* Add PLMT region to the root domain */ + rc = sbi_domain_root_add_memrange( + (unsigned long)plmt->time_val, plmt->size, PLMT_REGION_ALIGN, + SBI_DOMAIN_MEMREGION_MMIO | SBI_DOMAIN_MEMREGION_READABLE); + if (rc) + return rc; + + plmt_timer.timer_freq = plmt->timer_freq; + + sbi_timer_set_device(&plmt_timer); + + return 0; +} + +int plmt_warm_timer_init(void) +{ + if (!plmt.time_val) + return SBI_ENODEV; + + plmt_timer_event_stop(); + + return 0; +} diff --git a/lib/utils/timer/fdt_timer_plmt.c b/lib/utils/timer/fdt_timer_plmt.c new file mode 100644 index 0000000..e8be91b --- /dev/null +++ b/lib/utils/timer/fdt_timer_plmt.c @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 Andes Technology Corporation + * + * Authors: + * Yu Chien Peter Lin <peterlin@andestech.com> + */ + +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/timer/fdt_timer.h> +#include <sbi_utils/timer/andes_plmt.h> + +extern struct plmt_data plmt; + +static int fdt_plmt_cold_timer_init(void *fdt, int nodeoff, + const struct fdt_match *match) +{ + int rc; + unsigned long plmt_base; + + rc = fdt_parse_plmt_node(fdt, nodeoff, &plmt_base, &plmt.size, + &plmt.hart_count); + if (rc) + return rc; + + plmt.time_val = (u64 *)plmt_base; + plmt.time_cmp = (u64 *)(plmt_base + 0x8); + + rc = fdt_parse_timebase_frequency(fdt, &plmt.timer_freq); + if (rc) + return rc; + + rc = plmt_cold_timer_init(&plmt); + if (rc) + return rc; + + return 0; +} + +static const struct fdt_match timer_plmt_match[] = { + { .compatible = "andestech,plmt0" }, + {}, +}; + +struct fdt_timer fdt_timer_plmt = { + .match_table = timer_plmt_match, + .cold_init = fdt_plmt_cold_timer_init, + .warm_init = plmt_warm_timer_init, + .exit = NULL, +}; diff --git a/lib/utils/timer/objects.mk b/lib/utils/timer/objects.mk index 7f5f3ce..9360a76 100644 --- a/lib/utils/timer/objects.mk +++ b/lib/utils/timer/objects.mk @@ -8,9 +8,13 @@ # libsbiutils-objs-$(CONFIG_TIMER_MTIMER) += timer/aclint_mtimer.o +libsbiutils-objs-$(CONFIG_TIMER_PLMT) += timer/andes_plmt.o libsbiutils-objs-$(CONFIG_FDT_TIMER) += timer/fdt_timer.o libsbiutils-objs-$(CONFIG_FDT_TIMER) += timer/fdt_timer_drivers.o carray-fdt_timer_drivers-$(CONFIG_FDT_TIMER_MTIMER) += fdt_timer_mtimer libsbiutils-objs-$(CONFIG_FDT_TIMER_MTIMER) += timer/fdt_timer_mtimer.o + +carray-fdt_timer_drivers-$(CONFIG_FDT_TIMER_PLMT) += fdt_timer_plmt +libsbiutils-objs-$(CONFIG_FDT_TIMER_PLMT) += timer/fdt_timer_plmt.o |
