blob: a30eeb04d2dfda6121aa05c365151267cca36896 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "sw/device/lib/dif/dif_aon_timer.h"
#include <assert.h>
#include <stddef.h>
#include "sw/device/lib/base/bitfield.h"
#include "sw/device/lib/base/mmio.h"
#include "aon_timer_regs.h" // Generated.
static_assert(AON_TIMER_INTR_STATE_WKUP_TIMER_EXPIRED_BIT ==
AON_TIMER_INTR_TEST_WKUP_TIMER_EXPIRED_BIT,
"Wake-up IRQ have different indexes in different registers!");
static_assert(AON_TIMER_INTR_STATE_WDOG_TIMER_EXPIRED_BIT ==
AON_TIMER_INTR_TEST_WDOG_TIMER_EXPIRED_BIT,
"Watchdog IRQ have different indexes in different registers!");
const size_t kAonTimerWakeupIrqIndex =
AON_TIMER_INTR_STATE_WKUP_TIMER_EXPIRED_BIT;
const size_t kAonTimerWatchdogIrqIndex =
AON_TIMER_INTR_STATE_WDOG_TIMER_EXPIRED_BIT;
static void aon_timer_wakeup_clear_counter(const dif_aon_timer_t *aon) {
mmio_region_write32(aon->params.base_addr, AON_TIMER_WKUP_COUNT_REG_OFFSET,
0);
}
static void aon_timer_wakeup_toggle(const dif_aon_timer_t *aon, bool enable) {
uint32_t reg =
mmio_region_read32(aon->params.base_addr, AON_TIMER_WKUP_CTRL_REG_OFFSET);
reg = bitfield_bit32_write(reg, AON_TIMER_WKUP_CTRL_ENABLE_BIT, enable);
mmio_region_write32(aon->params.base_addr, AON_TIMER_WKUP_CTRL_REG_OFFSET,
reg);
}
static void aon_timer_watchdog_clear_counter(const dif_aon_timer_t *aon) {
mmio_region_write32(aon->params.base_addr, AON_TIMER_WDOG_COUNT_REG_OFFSET,
0);
}
static void aon_timer_watchdog_toggle(const dif_aon_timer_t *aon, bool enable) {
uint32_t reg =
mmio_region_read32(aon->params.base_addr, AON_TIMER_WDOG_CTRL_REG_OFFSET);
reg = bitfield_bit32_write(reg, AON_TIMER_WDOG_CTRL_ENABLE_BIT, enable);
mmio_region_write32(aon->params.base_addr, AON_TIMER_WDOG_CTRL_REG_OFFSET,
reg);
}
static void aon_timer_watchdog_lock(const dif_aon_timer_t *aon) {
// Clear bit to lock the watchdog configuration register until the next reset.
// Write one to clear the bit.
mmio_region_write32(aon->params.base_addr, AON_TIMER_WDOG_REGWEN_REG_OFFSET,
0x00000001);
}
static bool aon_timer_watchdog_is_locked(const dif_aon_timer_t *aon) {
uint32_t reg = mmio_region_read32(aon->params.base_addr,
AON_TIMER_WDOG_REGWEN_REG_OFFSET);
// Locked when bit is cleared.
return !bitfield_bit32_read(reg, AON_TIMER_WDOG_REGWEN_REGWEN_BIT);
}
static bool aon_timer_get_irq_index(dif_aon_timer_irq_t irq, uint32_t *index) {
switch (irq) {
case kDifAonTimerIrqWakeupThreshold:
*index = kAonTimerWakeupIrqIndex;
break;
case kDifAonTimerIrqWatchdogBarkThreshold:
*index = kAonTimerWatchdogIrqIndex;
break;
default:
return false;
}
return true;
}
dif_aon_timer_result_t dif_aon_timer_init(dif_aon_timer_params_t params,
dif_aon_timer_t *aon) {
if (aon == NULL) {
return kDifAonTimerBadArg;
}
*aon = (dif_aon_timer_t){.params = params};
return kDifAonTimerOk;
}
dif_aon_timer_result_t dif_aon_timer_wakeup_start(const dif_aon_timer_t *aon,
uint32_t threshold,
uint32_t prescaler) {
if (aon == NULL || prescaler > AON_TIMER_WKUP_CTRL_PRESCALER_MASK) {
return kDifAonTimerBadArg;
}
// The timer should be stopped first, otherwise it will continue counting up.
aon_timer_wakeup_toggle(aon, false);
aon_timer_wakeup_clear_counter(aon);
mmio_region_write32(aon->params.base_addr, AON_TIMER_WKUP_THOLD_REG_OFFSET,
threshold);
uint32_t reg =
bitfield_field32_write(0, AON_TIMER_WKUP_CTRL_PRESCALER_FIELD, prescaler);
reg = bitfield_bit32_write(reg, AON_TIMER_WKUP_CTRL_ENABLE_BIT, true);
mmio_region_write32(aon->params.base_addr, AON_TIMER_WKUP_CTRL_REG_OFFSET,
reg);
return kDifAonTimerOk;
}
dif_aon_timer_result_t dif_aon_timer_wakeup_stop(const dif_aon_timer_t *aon) {
if (aon == NULL) {
return kDifAonTimerBadArg;
}
aon_timer_wakeup_toggle(aon, false);
return kDifAonTimerOk;
}
dif_aon_timer_result_t dif_aon_timer_wakeup_restart(
const dif_aon_timer_t *aon) {
if (aon == NULL) {
return kDifAonTimerBadArg;
}
aon_timer_wakeup_clear_counter(aon);
aon_timer_wakeup_toggle(aon, true);
return kDifAonTimerOk;
}
dif_aon_timer_result_t dif_aon_timer_wakeup_get_count(
const dif_aon_timer_t *aon, uint32_t *count) {
if (aon == NULL || count == NULL) {
return kDifAonTimerBadArg;
}
*count = mmio_region_read32(aon->params.base_addr,
AON_TIMER_WKUP_COUNT_REG_OFFSET);
return kDifAonTimerOk;
}
dif_aon_timer_watchdog_result_t dif_aon_timer_watchdog_start(
const dif_aon_timer_t *aon, uint32_t bark_threshold,
uint32_t bite_threshold, bool pause_in_sleep, bool lock) {
if (aon == NULL) {
return kDifAonTimerWatchdogBadArg;
}
if (aon_timer_watchdog_is_locked(aon)) {
return kDifAonTimerWatchdogLocked;
}
// The timer should be stopped first, otherwise it will continue counting up.
aon_timer_watchdog_toggle(aon, false);
aon_timer_watchdog_clear_counter(aon);
mmio_region_write32(aon->params.base_addr,
AON_TIMER_WDOG_BARK_THOLD_REG_OFFSET, bark_threshold);
mmio_region_write32(aon->params.base_addr,
AON_TIMER_WDOG_BITE_THOLD_REG_OFFSET, bite_threshold);
uint32_t reg = bitfield_bit32_write(0, AON_TIMER_WDOG_CTRL_ENABLE_BIT, true);
if (pause_in_sleep) {
reg =
bitfield_bit32_write(reg, AON_TIMER_WDOG_CTRL_PAUSE_IN_SLEEP_BIT, true);
}
mmio_region_write32(aon->params.base_addr, AON_TIMER_WDOG_CTRL_REG_OFFSET,
reg);
// Watchdog control register should only be locked after the last
// control register access.
if (lock) {
aon_timer_watchdog_lock(aon);
}
return kDifAonTimerWatchdogOk;
}
dif_aon_timer_watchdog_result_t dif_aon_timer_watchdog_stop(
const dif_aon_timer_t *aon) {
if (aon == NULL) {
return kDifAonTimerWatchdogBadArg;
}
if (aon_timer_watchdog_is_locked(aon)) {
return kDifAonTimerWatchdogLocked;
}
aon_timer_watchdog_toggle(aon, false);
return kDifAonTimerWatchdogOk;
}
dif_aon_timer_watchdog_result_t dif_aon_timer_watchdog_restart(
const dif_aon_timer_t *aon) {
if (aon == NULL) {
return kDifAonTimerWatchdogBadArg;
}
if (aon_timer_watchdog_is_locked(aon)) {
return kDifAonTimerWatchdogLocked;
}
aon_timer_watchdog_clear_counter(aon);
aon_timer_watchdog_toggle(aon, true);
return kDifAonTimerWatchdogOk;
}
dif_aon_timer_result_t dif_aon_timer_watchdog_get_count(
const dif_aon_timer_t *aon, uint32_t *count) {
if (aon == NULL || count == NULL) {
return kDifAonTimerBadArg;
}
*count = mmio_region_read32(aon->params.base_addr,
AON_TIMER_WDOG_COUNT_REG_OFFSET);
return kDifAonTimerOk;
}
dif_aon_timer_result_t dif_aon_timer_watchdog_pet(const dif_aon_timer_t *aon) {
if (aon == NULL) {
return kDifAonTimerBadArg;
}
aon_timer_watchdog_clear_counter(aon);
return kDifAonTimerOk;
}
dif_aon_timer_watchdog_result_t dif_aon_timer_watchdog_lock(
const dif_aon_timer_t *aon) {
if (aon == NULL) {
return kDifAonTimerWatchdogBadArg;
}
aon_timer_watchdog_lock(aon);
return kDifAonTimerWatchdogOk;
}
dif_aon_timer_result_t dif_aon_timer_watchdog_is_locked(
const dif_aon_timer_t *aon, bool *is_locked) {
if (aon == NULL || is_locked == NULL) {
return kDifAonTimerBadArg;
}
*is_locked = aon_timer_watchdog_is_locked(aon);
return kDifAonTimerOk;
}
dif_aon_timer_result_t dif_aon_timer_irq_is_pending(const dif_aon_timer_t *aon,
dif_aon_timer_irq_t irq,
bool *is_pending) {
if (aon == NULL || is_pending == NULL) {
return kDifAonTimerBadArg;
}
uint32_t index = 0;
if (!aon_timer_get_irq_index(irq, &index)) {
return kDifAonTimerError;
}
uint32_t reg = mmio_region_read32(aon->params.base_addr,
AON_TIMER_INTR_STATE_REG_OFFSET);
*is_pending = bitfield_bit32_read(reg, index);
return kDifAonTimerOk;
}
dif_aon_timer_result_t dif_aon_timer_irq_acknowledge(const dif_aon_timer_t *aon,
dif_aon_timer_irq_t irq) {
if (aon == NULL) {
return kDifAonTimerBadArg;
}
uint32_t index = 0;
if (!aon_timer_get_irq_index(irq, &index)) {
return kDifAonTimerError;
}
// Write one to clear.
uint32_t reg = bitfield_bit32_write(0, index, true);
mmio_region_write32(aon->params.base_addr, AON_TIMER_INTR_STATE_REG_OFFSET,
reg);
return kDifAonTimerOk;
}
dif_aon_timer_result_t dif_aon_timer_irq_force(const dif_aon_timer_t *aon,
dif_aon_timer_irq_t irq) {
if (aon == NULL) {
return kDifAonTimerBadArg;
}
uint32_t index = 0;
if (!aon_timer_get_irq_index(irq, &index)) {
return kDifAonTimerError;
}
// Write only register.
uint32_t reg = bitfield_bit32_write(0, index, true);
mmio_region_write32(aon->params.base_addr, AON_TIMER_INTR_TEST_REG_OFFSET,
reg);
return kDifAonTimerOk;
}