/*
 * Copyright (C) 2017,2020 Freie Universität Berlin
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 */

/**
 * @ingroup     cpu_sam3
 * @ingroup     drivers_periph_rtt
 *
 * @note        The hardware RTT unit does neither support overflow interrupts
 *              nor setting the counter value. For this, this RTT driver does
 *              not implement those functions.
 * @{
 *
 * @file
 * @brief       Low-level RTT driver implementation
 *
 * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
 *
 * @}
 */

#include "cpu.h"
#include "periph/rtt.h"

#define ENABLE_DEBUG 0
#include "debug.h"

static struct {
    rtt_cb_t cb;
    void *arg;
} isr_ctx;

void rtt_init(void)
{
    /* enable RTT module */
    rtt_poweron();

    /* configure and apply the pre-scaler */
    RTT->RTT_MR = RTT_MR_RTPRES(CHIP_FREQ_XTAL_32K / RTT_FREQUENCY);
    RTT->RTT_MR |= RTT_MR_RTTRST;

    /* resetting the timer takes two slow clock cycles, so we wait for this to
     * complete */
    while (RTT->RTT_VR != 0) {}

    /* configure NVIC line */
    NVIC_EnableIRQ(RTT_IRQn);
}

uint32_t rtt_get_counter(void)
{
    return RTT->RTT_VR;
}

void rtt_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg)
{
    /* cancel any existing alarm */
    RTT->RTT_MR &= ~(RTT_MR_ALMIEN);

    /* set new alarm */
    isr_ctx.cb  = cb;
    isr_ctx.arg = arg;
    /* the alarm value is RTT_AR + 1, so we need to subtract 1 from the target
     * value here */
    RTT->RTT_AR = (alarm - 1);

    /* (re-)enable the alarm */
    RTT->RTT_MR |= RTT_MR_ALMIEN;
}

uint32_t rtt_get_alarm(void)
{
    if (RTT->RTT_MR & RTT_MR_ALMIEN) {
        /* the alarm value is RTT_AR + 1, see rtt_set_alarm() */
        return RTT->RTT_AR + 1;
    }
    return 0;
}

void rtt_clear_alarm(void)
{
    RTT->RTT_MR &= ~(RTT_MR_ALMIEN);
}

void rtt_poweron(void)
{
    PMC->PMC_PCER0 |= (1 << ID_RTT);
}

void rtt_poweroff(void)
{
    PMC->PMC_PCER0 &= ~(1 << ID_RTT);
}

void isr_rtt(void)
{
    uint32_t state = RTT->RTT_SR;       /* this clears all pending flags */
    if (state & RTT_SR_ALMS) {
        RTT->RTT_MR &= ~(RTT_MR_ALMIEN);
        isr_ctx.cb(isr_ctx.arg);
    }

    cortexm_isr_end();
}
