| /* |
| * 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_M_READABLE | |
| SBI_DOMAIN_MEMREGION_M_WRITABLE); |
| 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; |
| } |