blob: 01fcedd9741565f053256f2a0251598ea085559d [file] [log] [blame]
/*
* Copyright 2017, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
/* @AUTHOR(akroh@ertos.nicta.com.au) */
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <utils/util.h>
#include <platsupport/plat/timer.h>
#define XOTMR_FIN 19200000UL
#define SLPTMR_FIN 32768UL
static inline uint64_t
get_tcxo_hz(void)
{
return 7 * 1000 * 1000;
}
static inline uint64_t
get_xo_hz(void)
{
return XOTMR_FIN;
}
static inline uint64_t
get_slp_hz(void)
{
return SLPTMR_FIN;
}
/** Timer map offsets **/
#define KPSSSECURE_OFFSET 0x000
#define KPSSGPT0_OFFSET 0x004
#define KPSSGPT1_OFFSET 0x014
#define KPSSDGT_OFFSET 0x024
#define KPSSWDT0_OFFSET 0x038
#define KPSSWDT1_OFFSET 0x060
#define KPSSSTAT_OFFSET 0x088
#define GSSSECURE_OFFSET KPSSSECURE_OFFSET
#define GSSGPT0_OFFSET KPSSGPT0_OFFSET
#define GSSGPT1_OFFSET KPSSGPT1_OFFSET
#define GSSDGT_OFFSET KPSSDGT_OFFSET
#define GSSWDT0_OFFSET KPSSWDT0_OFFSET
#define GSSWDT1_OFFSET KPSSWDT1_OFFSET
#define GSSSTAT_OFFSET KPSSSTAT_OFFSET
#define PPSSXOTMR0_OFFSET 0x800
#define PPSSXOTMR1_OFFSET 0x818
#define PPSSTMR0_OFFSET 0x000
#define PPSSTMR1_OFFSET 0x018
#define PPSSWDT_OFFSET 0x800
#define PPSSCLKCTNTL_OFFSET 0x824
#define RPMGPT0_OFFSET 0x000
#define RPMGPT0_CLK_CTL 0x010
#define RPMGPT1_OFFSET 0x040
#define RPMWDT_OFFSET 0x060
#define RPMTMRSTS_OFFSET 0x100
/* Clock control Bitmaps */
#define PPSSCLKCTNTL_WDGON (1U << 1)
#define PPSSCLKCTNTL_SLPON (1U << 1)
#define TMRSTS(x, i) ((x) << (8 * i))
#define TMRSTS_WR_PEND(i) TMRSTS(1U << 3, i)
#define TMRSTS_CLR_PEND(i) TMRSTS(1U << 2, i)
#define TMRSTS_CLR_ON_MTCH_EN(i) TMRSTS(1U << 1, i)
#define TMRSTS_EN(i) TMRSTS(1U << 0, i)
#define TMRSTS_PEND(i) (TMRSTS_WR_PEND(i) | TMRSTS_CLR_PEND(i))
#define RPMTMRSTS_WDOG_EN (1U << 17)
#define RPMTMRSTS_WDOG_UNMASKED_INT_EN (1U << 16)
#define RPMTMRSTS_TMR1_WR_PEND TMRSTS_WR_PEND(1)
#define RPMTMRSTS_TMR1_CLR_PEND TMRSTS_CLR_PEND(1)
#define RPMTMRSTS_TMR1_CLK_ON_MTCH TMRSTS_CLR_ON_MTCH(1)
#define RPMTMRSTS_TMR1_PEND TMRSTS_PEND(1)
#define RPMTMRSTS_TMR1_EN TMRSTS_EN(1)
#define RPMTMRSTS_TMR0_WR_PEND TMRSTS_WR_PEND(0)
#define RPMTMRSTS_TMR0_CLR_PEND TMRSTS_CLR_PEND(0)
#define RPMTMRSTS_TMR0_CLR_ON_MTCH TMRSTS_CLR_ON_MTCH(0)
#define RPMTMRSTS_TMR0_EN TMRSTS_EN(0)
#define RPMTMRSTS_TMR0_PEND TMRSTS_PEND(0)
#define KPSSGPTSTS_TMR1_WR_PEND TMRSTS_WR_PEND(1)
#define KPSSGPTSTS_TMR1_CLR_PEND TMRSTS_CLR_PEND(1)
#define KPSSGPTSTS_TMR1_CLK_ON_MTCH TMRSTS_CLR_ON_MTCH(1)
#define KPSSGPTSTS_TMR1_EN TMRSTS_EN(1)
#define KPSSGPTSTS_TMR1_PEND TMRSTS_PEND(1)
#define KPSSGPTSTS_TMR0_WR_PEND TMRSTS_WR_PEND(0)
#define KPSSGPTSTS_TMR0_CLR_PEND TMRSTS_CLR_PEND(0)
#define KPSSGPTSTS_TMR0_CLR_ON_MTCH TMRSTS_CLR_ON_MTCH(0)
#define KPSSGPTSTS_TMR0_EN TMRSTS_EN(0)
#define KPSSGPTSTS_TMR0_PEND TMRSTS_PEND(0)
#define GSSGPTSTS_TMR1_WR_PEND TMRSTS_WR_PEND(1)
#define GSSGPTSTS_TMR1_CLR_PEND TMRSTS_CLR_PEND(1)
#define GSSGPTSTS_TMR1_CLK_ON_MTCH TMRSTS_CLR_ON_MTCH(1)
#define GSSGPTSTS_TMR1_EN TMRSTS_EN(1)
#define GSSGPTSTS_TMR1_PEND TMRSTS_PEND(1)
#define GSSGPTSTS_TMR0_WR_PEND TMRSTS_WR_PEND(0)
#define GSSGPTSTS_TMR0_CLR_PEND TMRSTS_CLR_PEND(0)
#define GSSGPTSTS_TMR0_CLR_ON_MTCH TMRSTS_CLR_ON_MTCH(0)
#define GSSGPTSTS_TMR0_EN TMRSTS_EN(0)
#define GSSGPTSTS_TMR0_PEND TMRSTS_PEND(0)
#define RPMGPT0_DIV(x) ((x) - 1)
#define RPMGPT0_DIV4 RPMGPT0_DIV(4)
#define RPMGPT0_DIV3 RPMGPT0_DIV(3)
#define RPMGPT0_DIV2 RPMGPT0_DIV(2)
#define RPMGPT0_DIV1 RPMGPT0_DIV(1)
#define TMR_CTRL_ON (1U << 5)
#define TMR_CTRL_EN (1U << 4)
#define TMR_CTRL_MODE(x) ((x) << 2)
#define TMR_CTRL_MODE_FREE_RUN TMR_CTRL_MODE(0x0)
#define TMR_CTRL_MODE_ONE_SHOT TMR_CTRL_MODE(0x1)
#define TMR_CTRL_MODE_PERIODIC TMR_CTRL_MODE(0x2)
#define TMR_CTRL_MODE_MASK TMR_CTRL_MODE(0x3)
#define TMR_CTRL_PRESCALE(x) ((x) << 0)
#define TMR_CTRL_PRESCALE_DIV32 TMR_CTRL_PRESCALE(0x0)
#define TMR_CTRL_PRESCALE_DIV8 TMR_CTRL_PRESCALE(0x1)
#define TMR_CTRL_PRESCALE_DIV4 TMR_CTRL_PRESCALE(0x2)
#define TMR_CTRL_PRESCALE_DIV2 TMR_CTRL_PRESCALE(0x3)
#define TMR_CTRL_PRESCALE_MASK TMR_CTRL_PRESCALE(0x3)
#define TMR_STAT_MATCH_UPDATE (1U << 4)
#define TMR_STAT_COUNT_UPDATE (1U << 3)
#define TMR_STAT_CONTROL_UPDATE (1U << 2)
#define TMR_STAT_CLR_INT_UPDATE (1U << 1)
#define TMR_STAT_CLK_CNT_UPDATE (1U << 0)
/** General bitmaps **/
#define DGTTMR_EN_CLR_ON_MTCH_EN (1U << 1)
#define DGTTMR_EN_EN (1U << 0)
#define DGTTMR_CLK_CTRL(x) ((x) << 0)
#define DGTTMR_CLK_CTRL_DIV1 DGTTMR_CLK_CTRL(0x0)
#define DGTTMR_CLK_CTRL_DIV2 DGTTMR_CLK_CTRL(0x1)
#define DGTTMR_CLK_CTRL_DIV3 DGTTMR_CLK_CTRL(0x2)
#define DGTTMR_CLK_CTRL_DIV4 DGTTMR_CLK_CTRL(0x3)
#define DGTTMR_CLK_CTRL_MASK DGTTMR_CLK_CTRL(0x3)
#define WDTRESET (1U << 0)
#define WDTFREEZE (1U << 0)
#define WDTINTEN_EN (1U << 1)
#define WDTINTEN_UNMASKED_INTEN (1U << 0)
#define WDTSTAT_COUNT(x) ((x) << 3)
#define WDTSTAT_GETCOUNT(x) ((x) >> 3)
#define WDTSTAT_CNT_RESET_STAT (1U << 2)
#define WDTSTAT_FROZEN(x) (1U << 1)
#define WDTSTAT_EXPIRED(x) (1U << 0)
#define WDTEXP_SYNC (1U << 14)
#define WDTEXP_DATA(x) (((x) & 0x3fff) << 0)
#define WDTEXP_GETDATA(x) (((x) >> 0) & 0x3fff)
#define WDTBARK_SYNC (1U << 14)
#define WDTBARK_DATA(x) (((x) & 0x3fff) << 0)
#define WDTBARK_GETDATA(x) (((x) >> 0) & 0x3fff)
#define WDTTESTLOADSTAT_SYNC (1U << 0)
#define WDTTESTLOAD_LOAD (1U << 0)
#define WDTTEST_SYNC (1U << 14)
#define WDTTEST_LOAD(x) (((x) & 0x3fff) << 0)
#define WDTTEST_GETLOAD(x) (((x) >> 0) & 0x3fff)
#define GPTEN_CLR_ON_MTCH_EN (1U << 1)
#define GPTEN_EN (1U << 0)
/** Helpers **/
#define TIMER_VADDR_OFFSET(base, offset) ((void*)((uintptr_t)base + offset))
#define TIMER_REG(base, offset) *(volatile uint32_t *)TIMER_VADDR_OFFSET(base, offset)
/** Timer register maps **/
struct tmr_regs {
uint32_t match; /* +0x00 */
uint32_t count; /* +0x04 */
uint32_t control; /* +0x08 */
uint32_t clear_int; /* +0x0C */
uint32_t clear_cnt; /* +0x10 */
uint32_t status; /* +0x14 */
};
typedef volatile struct tmr_regs tmr_regs_t;
struct dgt_regs {
uint32_t mtch; /* +0x00 */
uint32_t cnt; /* +0x04 */
uint32_t en; /* +0x08 */
uint32_t clr; /* +0x0C */
uint32_t clk_ctl; /* +0x10 */
};
typedef volatile struct dgt_regs dgt_regs_t;
struct wdt_regs {
uint32_t reset; /* +0x00 */
uint32_t freeze; /* +0x04 */
uint32_t unmasked_int_en; /* +0x08 */
uint32_t status; /* +0x0C */
uint32_t expired_width; /* +0x10 */
uint32_t bark_time; /* +0x14 */
uint32_t test_load_status; /* +0x18 */
uint32_t test_load; /* +0x1C */
uint32_t test; /* +0x20 */
/* Only for KRAIT and GSS */
uint32_t bite_time; /* +0x24 */
};
typedef volatile struct wdt_regs wdt_regs_t;
struct gpt_regs {
uint32_t mtch; /* +0x00 */
uint32_t cnt; /* +0x04 */
uint32_t en; /* +0x08 */
uint32_t clr; /* +0x0C */
};
typedef volatile struct gpt_regs gpt_regs_t;
/**** GPT timer functions ****/
int
gpt_timer_start(timer_t *timer)
{
gpt_regs_t* regs = (gpt_regs_t*)timer->data;
regs->en |= GPTEN_EN;
return 0;
}
int
gpt_timer_stop(timer_t *timer)
{
gpt_regs_t* regs = (gpt_regs_t*)timer->data;
regs->en &= ~GPTEN_EN;
return 0;
}
uint64_t
gpt_get_time(timer_t *timer)
{
gpt_regs_t* regs = (gpt_regs_t*)timer->data;
return regs->cnt;
}
int
gpt_periodic(timer_t *timer, uint64_t ns)
{
gpt_regs_t* regs = (gpt_regs_t*)timer->data;
uintptr_t sts_base = (uintptr_t)timer->data & ~0xfff;
volatile uint32_t* sts = (volatile uint32_t*)sts_base;
uint64_t fin;
switch (timer->id) {
case TMR_RPM_GPT0 :
fin = get_xo_hz() / 4;
break;
case TMR_RPM_GPT1:
case TMR_KPSS_GPT0:
case TMR_KPSS_GPT1:
case TMR_GSS_GPT0:
case TMR_GSS_GPT1:
fin = get_slp_hz();
break;
default:
assert(!"invalid timer for GPT operation");
return -1;
}
/* Clear the timer on match */
regs->en = GPTEN_CLR_ON_MTCH_EN;
switch (timer->id) {
case TMR_RPM_GPT0 :
while (sts[RPMTMRSTS_OFFSET / 4] & RPMTMRSTS_TMR0_WR_PEND);
break;
case TMR_RPM_GPT1:
while (sts[RPMTMRSTS_OFFSET / 4] & RPMTMRSTS_TMR1_WR_PEND);
break;
case TMR_KPSS_GPT0:
case TMR_GSS_GPT0:
while (sts[KPSSSTAT_OFFSET / 4] & KPSSGPTSTS_TMR0_WR_PEND);
break;
case TMR_KPSS_GPT1:
case TMR_GSS_GPT1:
while (sts[KPSSSTAT_OFFSET / 4] & KPSSGPTSTS_TMR1_WR_PEND);
break;
default:
break;
}
/* Reset the timer */
regs->clr = 0xC0FFEE;
/* Configure match value */
regs->mtch = (fin * ns) / (1000UL * 1000 * 1000);
switch (timer->id) {
case TMR_RPM_GPT0 :
while (sts[RPMTMRSTS_OFFSET / 4] & RPMTMRSTS_TMR0_PEND);
break;
case TMR_RPM_GPT1:
while (sts[RPMTMRSTS_OFFSET / 4] & RPMTMRSTS_TMR1_PEND);
break;
case TMR_KPSS_GPT0:
case TMR_GSS_GPT0:
while (sts[KPSSSTAT_OFFSET / 4] & KPSSGPTSTS_TMR0_PEND);
break;
case TMR_KPSS_GPT1:
case TMR_GSS_GPT1:
while (sts[KPSSSTAT_OFFSET / 4] & KPSSGPTSTS_TMR1_PEND);
break;
default:
break;
}
return 0;
}
void
gpt_handle_irq(UNUSED timer_t *timer)
{
/* Nothing to do */
}
/* DGT timer functions */
int
dgt_timer_start(timer_t *timer)
{
dgt_regs_t* regs = (dgt_regs_t*)timer->data;
regs->en |= DGTTMR_EN_EN;
return 0;
}
int
dgt_timer_stop(timer_t *timer)
{
dgt_regs_t* regs = (dgt_regs_t*)timer->data;
regs->en &= ~DGTTMR_EN_EN;
return 0;
}
uint64_t
dgt_get_time(timer_t *timer)
{
dgt_regs_t* regs = (dgt_regs_t*)timer->data;
return regs->cnt;
}
int
dgt_periodic(timer_t *timer, uint64_t ns)
{
dgt_regs_t* regs = (dgt_regs_t*)timer->data;
uint64_t fin_hz = get_tcxo_hz();
int div;
/* Disable */
regs->en = 0;
/* Configure */
regs->clr = 0xC0FFEE;
/* DIV should always be 4 due to clock domain delay */
regs->clk_ctl = DGTTMR_CLK_CTRL_DIV4;
div = 4;
regs->mtch = (fin_hz * ns) / (div * 1000 * 1000 * 1000);
regs->en = DGTTMR_EN_CLR_ON_MTCH_EN;
return 0;
}
void
dgt_handle_irq(UNUSED timer_t *timer)
{
/* Nothing to do */
}
/**** TMR ****/
int
tmr_timer_start(timer_t *timer)
{
tmr_regs_t* regs = (tmr_regs_t*)timer->data;
regs->control |= ~TMR_CTRL_EN;
return 0;
}
int
tmr_timer_stop(timer_t *timer)
{
tmr_regs_t* regs = (tmr_regs_t*)timer->data;
regs->control &= ~TMR_CTRL_EN;
return 0;
}
uint64_t
tmr_get_time(timer_t *timer)
{
tmr_regs_t* regs = (tmr_regs_t*)timer->data;
return regs->count;
}
int
tmr_periodic(timer_t *timer, uint64_t ns)
{
tmr_regs_t* regs = (tmr_regs_t*)timer->data;
uint64_t fin_hz;
/* Find input clock frequency */
switch (timer->id) {
case TMR_PPSS_XO_TMR0:
case TMR_PPSS_XO_TMR1:
fin_hz = get_tcxo_hz();
break;
case TMR_PPSS_SLP_TMR0:
case TMR_PPSS_SLP_TMR1:
fin_hz = get_slp_hz();
break;
default:
assert(!"Invalid timer for this call");
return -1;
}
/* Turn on the timer */
regs->control = TMR_CTRL_ON;
while (regs->status & TMR_STAT_CONTROL_UPDATE);
/* Reset and config */
regs->control |= TMR_CTRL_PRESCALE_DIV4 | TMR_CTRL_MODE_PERIODIC;
regs->clear_int = 0xC0FFEE;
regs->clear_cnt = 0xC0FFEE;
regs->match = (fin_hz * ns) / (4UL * 1000 * 1000 * 1000);
/* Wait for registers to be updated */
while (regs->status);
return 0;
}
void
tmr_handle_irq(timer_t *timer)
{
tmr_regs_t* regs = (tmr_regs_t*)timer->data;
/* Reset the IRQ */
regs->clear_int = 0xC0FFEE;
}
/**** WDT ****/
int
wdt_timer_start(timer_t *timer)
{
wdt_regs_t* regs = (wdt_regs_t*)timer->data;
/* Don't send a reset */
regs->reset = 0;
/* Enable the auto-kicker */
regs->freeze = WDTFREEZE;
regs->unmasked_int_en = WDTINTEN_EN | WDTINTEN_UNMASKED_INTEN;
return 0;
}
uint64_t
wdt_get_time(timer_t *timer)
{
wdt_regs_t* regs = (wdt_regs_t*)timer->data;
return WDTSTAT_GETCOUNT(regs->status);
}
int
wdt_periodic(timer_t *timer, uint64_t ns)
{
wdt_regs_t* regs = (wdt_regs_t*)timer->data;
uint64_t fin_hz = get_slp_hz();
uint32_t bark_time;
/* Disable */
regs->unmasked_int_en = WDTINTEN_EN;
/* Enable the auto-kicker */
regs->freeze = WDTFREEZE;
/* Don't reset */
regs->reset = 0;
/* Set the counter value */
bark_time = (fin_hz * ns) / (1000 * 1000);
regs->bark_time = WDTBARK_DATA(bark_time);
assert(!"Not yet implemented");
return 0;
}
int timer_init(timer_t *timer, timer_config_t config)
{
if (config.id < 0 || config.id >= NTIMERS) {
return EINVAL;
}
timer->id = config.id;
/* Default handlers */
switch (config.id) {
case TMR_PPSS_XO_TMR0:
timer->data = TIMER_VADDR_OFFSET(config.vaddr, PPSSXOTMR0_OFFSET);
break;
case TMR_PPSS_XO_TMR1:
timer->data = TIMER_VADDR_OFFSET(config.vaddr, PPSSXOTMR1_OFFSET);
break;
case TMR_PPSS_SLP_TMR0:
TIMER_REG(config.vaddr, PPSSCLKCTNTL_OFFSET) |= PPSSCLKCTNTL_SLPON;
timer->data = TIMER_VADDR_OFFSET(config.vaddr, PPSSTMR0_OFFSET);
break;
case TMR_PPSS_SLP_TMR1:
TIMER_REG(config.vaddr, PPSSCLKCTNTL_OFFSET) |= PPSSCLKCTNTL_SLPON;
timer->data = TIMER_VADDR_OFFSET(config.vaddr, PPSSTMR1_OFFSET);
break;
case TMR_PPSS_SLP_WDOG:
TIMER_REG(config.vaddr, PPSSCLKCTNTL_OFFSET) |= PPSSCLKCTNTL_WDGON;
timer->data = TIMER_VADDR_OFFSET(config.vaddr, PPSSWDT_OFFSET);
break;
/* KPSS */
case TMR_KPSS_GPT0:
timer->data = TIMER_VADDR_OFFSET(config.vaddr, KPSSGPT0_OFFSET);
break;
case TMR_KPSS_GPT1:
timer->data = TIMER_VADDR_OFFSET(config.vaddr, KPSSGPT1_OFFSET);
break;
case TMR_KPSS_DGT:
timer->data = TIMER_VADDR_OFFSET(config.vaddr, KPSSDGT_OFFSET);
break;
case TMR_KPSS_WDT0:
timer->data = TIMER_VADDR_OFFSET(config.vaddr, KPSSGPT0_OFFSET);
break;
case TMR_KPSS_WDT1:
timer->data = TIMER_VADDR_OFFSET(config.vaddr, KPSSGPT1_OFFSET);
break;
/* GSS */
case TMR_GSS_GPT0:
timer->data = TIMER_VADDR_OFFSET(config.vaddr, GSSGPT0_OFFSET);
break;
case TMR_GSS_GPT1:
timer->data = TIMER_VADDR_OFFSET(config.vaddr, GSSGPT1_OFFSET);
break;
case TMR_GSS_DGT:
timer->data = TIMER_VADDR_OFFSET(config.vaddr, GSSDGT_OFFSET);
break;
case TMR_GSS_WDT0:
timer->data = TIMER_VADDR_OFFSET(config.vaddr, GSSGPT0_OFFSET);
break;
case TMR_GSS_WDT1:
timer->data = TIMER_VADDR_OFFSET(config.vaddr, GSSGPT1_OFFSET);
break;
/* RPM */
case TMR_RPM_GPT0:
TIMER_REG(config.vaddr, RPMGPT0_CLK_CTL) = RPMGPT0_DIV4;
timer->data = TIMER_VADDR_OFFSET(config.vaddr, RPMGPT0_OFFSET);
break;
case TMR_RPM_GPT1:
timer->data = TIMER_VADDR_OFFSET(config.vaddr, RPMGPT1_OFFSET);
break;
case TMR_RPM_WDOG:
timer->data = TIMER_VADDR_OFFSET(config.vaddr, RPMWDT_OFFSET);
break;
default:
return EINVAL;
}
return 0;
}