blob: 79b81e7bff9c0387cc45b79d1691f8c5358c483c [file] [log] [blame]
/* Copyright 2019 SiFive, Inc */
/* SPDX-License-Identifier: Apache-2.0 */
#include <metal/machine/platform.h>
#ifdef METAL_SIFIVE_RTC0
#include <metal/drivers/sifive_rtc0.h>
#include <metal/machine.h>
#include <limits.h>
/* RTCCFG */
#define METAL_RTCCFG_RTCSCALE_MASK 0xF
#define METAL_RTCCFG_ENALWAYS (1 << 12)
#define METAL_RTCCFG_IP0 (1 << 28)
/* RTCCMP0 */
#define METAL_RTCCMP0_MAX UINT32_MAX
#define RTC_REG(base, offset) (((unsigned long)base + offset))
#define RTC_REGW(base, offset) (__METAL_ACCESS_ONCE((__metal_io_u32 *)RTC_REG(base, offset)))
uint64_t __metal_driver_sifive_rtc0_get_rate(const struct metal_rtc *const rtc) {
const struct metal_clock *const clock = __metal_driver_sifive_rtc0_clock(rtc);
return metal_clock_get_rate_hz(clock);
}
uint64_t __metal_driver_sifive_rtc0_set_rate(const struct metal_rtc *const rtc, const uint64_t rate) {
const struct metal_clock *const clock = __metal_driver_sifive_rtc0_clock(rtc);
return metal_clock_get_rate_hz(clock);
}
uint64_t __metal_driver_sifive_rtc0_get_compare(const struct metal_rtc *const rtc) {
const uint64_t base = __metal_driver_sifive_rtc0_control_base(rtc);
const uint32_t shift = RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCFG) & METAL_RTCCFG_RTCSCALE_MASK;
return ((uint64_t)RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCMP0) << shift);
}
uint64_t __metal_driver_sifive_rtc0_set_compare(const struct metal_rtc *const rtc, const uint64_t compare) {
const uint64_t base = __metal_driver_sifive_rtc0_control_base(rtc);
/* Determine the bit shift and shifted value to store in rtccmp0/rtccfg.scale */
uint32_t shift = 0;
uint64_t comp_shifted = compare;
while (comp_shifted > METAL_RTCCMP0_MAX) {
shift += 1;
comp_shifted = comp_shifted >> shift;
}
/* Set the value of rtccfg.scale */
uint32_t cfg = RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCFG);
cfg &= ~(METAL_RTCCFG_RTCSCALE_MASK);
cfg |= (METAL_RTCCFG_RTCSCALE_MASK & shift);
RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCFG) = cfg;
/* Set the value of rtccmp0 */
RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCMP0) = (uint32_t) comp_shifted;
return __metal_driver_sifive_rtc0_get_compare(rtc);
}
uint64_t __metal_driver_sifive_rtc0_get_count(const struct metal_rtc *const rtc) {
const uint64_t base = __metal_driver_sifive_rtc0_control_base(rtc);
uint64_t count = RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCOUNTHI);
count <<= 32;
count |= RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCOUNTLO);
return count;
}
uint64_t __metal_driver_sifive_rtc0_set_count(const struct metal_rtc *const rtc, const uint64_t count) {
const uint64_t base = __metal_driver_sifive_rtc0_control_base(rtc);
RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCOUNTHI) = (UINT_MAX & (count >> 32));
RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCOUNTLO) = (UINT_MAX & count);
return __metal_driver_sifive_rtc0_get_count(rtc);
}
int __metal_driver_sifive_rtc0_run(const struct metal_rtc *const rtc, const enum metal_rtc_run_option option) {
const uint64_t base = __metal_driver_sifive_rtc0_control_base(rtc);
switch (option) {
default:
case METAL_RTC_STOP:
RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCFG) &= ~(METAL_RTCCFG_ENALWAYS);
break;
case METAL_RTC_RUN:
RTC_REGW(base, METAL_SIFIVE_RTC0_RTCCFG) |= METAL_RTCCFG_ENALWAYS;
break;
}
return 0;
}
struct metal_interrupt *__metal_driver_sifive_rtc0_get_interrupt(const struct metal_rtc *const rtc) {
return __metal_driver_sifive_rtc0_interrupt_parent(rtc);
}
int __metal_driver_sifive_rtc0_get_interrupt_id(const struct metal_rtc *const rtc) {
return __metal_driver_sifive_rtc0_interrupt_line(rtc);
}
__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_rtc0) = {
.rtc.get_rate = __metal_driver_sifive_rtc0_get_rate,
.rtc.set_rate = __metal_driver_sifive_rtc0_set_rate,
.rtc.get_compare = __metal_driver_sifive_rtc0_get_compare,
.rtc.set_compare = __metal_driver_sifive_rtc0_set_compare,
.rtc.get_count = __metal_driver_sifive_rtc0_get_count,
.rtc.set_count = __metal_driver_sifive_rtc0_set_count,
.rtc.run = __metal_driver_sifive_rtc0_run,
.rtc.get_interrupt = __metal_driver_sifive_rtc0_get_interrupt,
.rtc.get_interrupt_id = __metal_driver_sifive_rtc0_get_interrupt_id,
};
#endif