blob: dba8af67fe84618fd9ab423d258267160164f457 [file] [log] [blame]
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <arch_helpers.h>
#include <assert.h>
#include <drivers/arm/arm_gic.h>
#include <drivers/arm/gic_v2.h>
#include <drivers/arm/sp804.h>
#include <mmio.h>
static unsigned int sp804_freq;
static uintptr_t sp804_base;
int sp804_timer_program(unsigned long time_out_ms)
{
unsigned int load_val;
unsigned char ctrl_reg;
assert(sp804_base);
assert(time_out_ms);
/* Disable the timer */
ctrl_reg = mmio_read_8(sp804_base + SP804_CTRL_OFFSET);
ctrl_reg &= ~(TIMER_EN | INT_ENABLE);
mmio_write_8(sp804_base + SP804_CTRL_OFFSET, ctrl_reg);
/* Calculate the load value */
load_val = (sp804_freq * time_out_ms) / 1000;
/* Write the load value to sp804 timer */
mmio_write_32(sp804_base + SP804_LOAD_OFFSET, load_val);
/* Enable the timer */
ctrl_reg |= (TIMER_EN | INT_ENABLE);
mmio_write_8(sp804_base + SP804_CTRL_OFFSET, ctrl_reg);
return 0;
}
static void sp804_timer_disable(void)
{
unsigned char ctrl_reg;
/*
* The interrupt line should be cleared prior to timer disable.
* Otherwise the interrupt line level decay from high to quiescent
* level is not quick enough which may trigger spurious interrupt.
* Write a dummy load value to sp804 timer to clear the interrupt.
*/
mmio_write_32(sp804_base + SP804_LOAD_OFFSET, 0xffff);
/* De-assert the timer interrupt */
mmio_write_8(sp804_base + SP804_INT_CLR_OFFSET, 0x0);
/* Disable the timer */
ctrl_reg = mmio_read_8(sp804_base + SP804_CTRL_OFFSET);
ctrl_reg &= ~(TIMER_EN | INT_ENABLE);
mmio_write_8(sp804_base + SP804_CTRL_OFFSET, ctrl_reg);
}
int sp804_timer_cancel(void)
{
assert(sp804_base);
sp804_timer_disable();
return 0;
}
int sp804_timer_handler(void)
{
assert(sp804_base);
sp804_timer_disable();
return 0;
}
int sp804_timer_init(uintptr_t base_addr, unsigned int timer_freq)
{
unsigned char ctrl_reg;
/* Check input parameters */
assert(base_addr && timer_freq);
/* Check for duplicate initialization */
assert(sp804_base == 0);
sp804_base = base_addr;
sp804_freq = timer_freq;
/*
* Configure the timer in one shot mode, pre-scalar divider to 1,
* timer counter width to 32 bits and un-mask the interrupt.
*/
ctrl_reg = ONESHOT_MODE | TIMER_PRE_DIV1 | TIMER_SIZE;
mmio_write_8(sp804_base + SP804_CTRL_OFFSET, ctrl_reg);
return 0;
}