forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathleon_gptimer.c
133 lines (108 loc) · 3.35 KB
/
leon_gptimer.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
* Copyright (c) 2019-2020 Cobham Gaisler AB
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* This driver uses two independent GPTIMER subtimers in the following way:
* - subtimer 0 generates periodic interrupts and the ISR announces ticks.
* - subtimer 1 is used as up-counter.
*/
#define DT_DRV_COMPAT gaisler_gptimer
#include <device.h>
#include <drivers/timer/system_timer.h>
#include <sys_clock.h>
/* GPTIMER subtimer increments each microsecond. */
#define PRESCALER (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / 1000000)
/* GPTIMER Timer instance */
struct gptimer_timer_regs {
uint32_t counter;
uint32_t reload;
uint32_t ctrl;
uint32_t latch;
};
/* A GPTIMER can have maximum of 7 subtimers. */
#define GPTIMER_MAX_SUBTIMERS 7
/* GPTIMER common registers */
struct gptimer_regs {
uint32_t scaler_value;
uint32_t scaler_reload;
uint32_t cfg;
uint32_t latch_cfg;
struct gptimer_timer_regs timer[GPTIMER_MAX_SUBTIMERS];
};
#define GPTIMER_CTRL_WN (1 << 7)
#define GPTIMER_CTRL_IP (1 << 4)
#define GPTIMER_CTRL_IE (1 << 3)
#define GPTIMER_CTRL_LD (1 << 2)
#define GPTIMER_CTRL_RS (1 << 1)
#define GPTIMER_CTRL_EN (1 << 0)
#define GPTIMER_CFG_EL (1 << 11)
#define GPTIMER_CFG_DF (1 << 9)
#define GPTIMER_CFG_SI (1 << 8)
#define GPTIMER_CFG_IRQ (0x1f << 3)
#define GPTIMER_CFG_TIMERS (7 << 0)
static volatile struct gptimer_regs *get_regs(void)
{
return (struct gptimer_regs *) DT_INST_REG_ADDR(0);
}
static int get_timer_irq(void)
{
return DT_INST_IRQN(0);
}
static uint32_t gptimer_ctrl_clear_ip;
static void timer_isr(const void *unused)
{
ARG_UNUSED(unused);
volatile struct gptimer_regs *regs = get_regs();
volatile struct gptimer_timer_regs *tmr = ®s->timer[0];
uint32_t ctrl;
ctrl = tmr->ctrl;
if ((ctrl & GPTIMER_CTRL_IP) == 0) {
return; /* interrupt not for us */
}
/* Clear pending */
tmr->ctrl = GPTIMER_CTRL_IE | GPTIMER_CTRL_RS |
GPTIMER_CTRL_EN | gptimer_ctrl_clear_ip;
sys_clock_announce(1);
}
uint32_t sys_clock_elapsed(void)
{
return 0;
}
uint32_t sys_clock_cycle_get_32(void)
{
volatile struct gptimer_regs *regs = get_regs();
volatile struct gptimer_timer_regs *tmr = ®s->timer[1];
uint32_t counter = tmr->counter;
return (0 - counter) * PRESCALER;
}
static void init_downcounter(volatile struct gptimer_timer_regs *tmr)
{
tmr->reload = 0xFFFFFFFF;
tmr->ctrl = GPTIMER_CTRL_LD | GPTIMER_CTRL_RS | GPTIMER_CTRL_EN;
}
static int sys_clock_driver_init(const struct device *dev)
{
ARG_UNUSED(dev);
const int timer_interrupt = get_timer_irq();
volatile struct gptimer_regs *regs = get_regs();
volatile struct gptimer_timer_regs *tmr = ®s->timer[0];
init_downcounter(®s->timer[1]);
/* Stop timer and probe how CTRL_IP is cleared (write 1 or 0). */
tmr->ctrl = GPTIMER_CTRL_IP;
if ((tmr->ctrl & GPTIMER_CTRL_IP) == 0) {
/* IP bit is cleared by setting it to 1. */
gptimer_ctrl_clear_ip = GPTIMER_CTRL_IP;
}
/* Configure timer scaler for 1 MHz subtimer tick */
regs->scaler_reload = PRESCALER - 1;
tmr->reload = 1000000U / CONFIG_SYS_CLOCK_TICKS_PER_SEC - 1;
tmr->ctrl = GPTIMER_CTRL_IE | GPTIMER_CTRL_LD | GPTIMER_CTRL_RS |
GPTIMER_CTRL_EN;
irq_connect_dynamic(timer_interrupt, 0, timer_isr, NULL, 0);
irq_enable(timer_interrupt);
return 0;
}
SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2,
CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);