blob: de88cda2163b2a91563e8f6d0e202e7ef02d1105 [file] [log] [blame]
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +02001/*
2 * Copyright (c) 2018, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <arch.h>
8#include <arch_helpers.h>
9#include <arm_gic.h>
10#include <assert.h>
11#include <gic_v2.h>
12#include <mmio.h>
13#include <sp804.h>
14
15static unsigned int sp804_freq;
16static uintptr_t sp804_base;
17
18int sp804_timer_program(unsigned long time_out_ms)
19{
20 unsigned int load_val;
21 unsigned char ctrl_reg;
22
23 assert(sp804_base);
24 assert(time_out_ms);
25
26 /* Disable the timer */
27 ctrl_reg = mmio_read_8(sp804_base + SP804_CTRL_OFFSET);
28 ctrl_reg &= ~(TIMER_EN | INT_ENABLE);
29 mmio_write_8(sp804_base + SP804_CTRL_OFFSET, ctrl_reg);
30
31 /* Calculate the load value */
32 load_val = (sp804_freq * time_out_ms) / 1000;
33
34 /* Write the load value to sp804 timer */
35 mmio_write_32(sp804_base + SP804_LOAD_OFFSET, load_val);
36
37 /* Enable the timer */
38 ctrl_reg |= (TIMER_EN | INT_ENABLE);
39 mmio_write_8(sp804_base + SP804_CTRL_OFFSET, ctrl_reg);
40
41 return 0;
42}
43
44static void sp804_timer_disable(void)
45{
46 unsigned char ctrl_reg;
47
48 /*
49 * The interrupt line should be cleared prior to timer disable.
50 * Otherwise the interrupt line level decay from high to quiescent
51 * level is not quick enough which may trigger spurious interrupt.
52 * Write a dummy load value to sp804 timer to clear the interrupt.
53 */
54 mmio_write_32(sp804_base + SP804_LOAD_OFFSET, 0xffff);
55
56 /* De-assert the timer interrupt */
57 mmio_write_8(sp804_base + SP804_INT_CLR_OFFSET, 0x0);
58
59 /* Disable the timer */
60 ctrl_reg = mmio_read_8(sp804_base + SP804_CTRL_OFFSET);
61 ctrl_reg &= ~(TIMER_EN | INT_ENABLE);
62 mmio_write_8(sp804_base + SP804_CTRL_OFFSET, ctrl_reg);
63}
64
65int sp804_timer_cancel(void)
66{
67 assert(sp804_base);
68 sp804_timer_disable();
69 return 0;
70}
71
72int sp804_timer_handler(void)
73{
74 assert(sp804_base);
75 sp804_timer_disable();
76 return 0;
77}
78
79int sp804_timer_init(uintptr_t base_addr, unsigned int timer_freq)
80{
81 unsigned char ctrl_reg;
82
83 /* Check input parameters */
84 assert(base_addr && timer_freq);
85
86 /* Check for duplicate initialization */
87 assert(sp804_base == 0);
88
89 sp804_base = base_addr;
90 sp804_freq = timer_freq;
91
92 /*
93 * Configure the timer in one shot mode, pre-scalar divider to 1,
94 * timer counter width to 32 bits and un-mask the interrupt.
95 */
96 ctrl_reg = ONESHOT_MODE | TIMER_PRE_DIV1 | TIMER_SIZE;
97 mmio_write_8(sp804_base + SP804_CTRL_OFFSET, ctrl_reg);
98
99 return 0;
100}