summaryrefslogtreecommitdiff
path: root/chip/max32660/hwtimer_chip.c
blob: a6469ae7952453df6a7d5ae28a087c3ec86f9919 (plain)
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
/* Copyright 2019 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* MAX32660 HW Timer module for Chrome EC */

#include "clock.h"
#include "console.h"
#include "common.h"
#include "hooks.h"
#include "hwtimer.h"
#include "task.h"
#include "timer.h"
#include "registers.h"
#include "tmr_regs.h"
#include "gcr_regs.h"

/* Define the rollover timer */
#define TMR_ROLLOVER MXC_TMR0
#define TMR_ROLLOVER_IRQ EC_TMR0_IRQn

/* Define the event timer */
#define TMR_EVENT MXC_TMR1
#define TMR_EVENT_IRQ EC_TMR1_IRQn

#define ROLLOVER_EVENT 1
#define NOT_ROLLOVER_EVENT 0

#define TMR_PRESCALER MXC_V_TMR_CN_PRES_DIV8
#define TMR_DIV (1 << TMR_PRESCALER)

/* The frequency of timer using the prescaler */
#define TIMER_FREQ_HZ (PeripheralClock / TMR_DIV)

/* Console output macros */
#define CPUTS(outstr) cputs(CC_SYSTEM, outstr)
#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ##args)

static uint32_t last_deadline;

/* brief Timer prescaler values */
enum tmr_pres {
	TMR_PRES_1 = MXC_V_TMR_CN_PRES_DIV1, /// Divide input clock by 1
	TMR_PRES_2 = MXC_V_TMR_CN_PRES_DIV2, /// Divide input clock by 2
	TMR_PRES_4 = MXC_V_TMR_CN_PRES_DIV4, /// Divide input clock by 4
	TMR_PRES_8 = MXC_V_TMR_CN_PRES_DIV8, /// Divide input clock by 8
	TMR_PRES_16 = MXC_V_TMR_CN_PRES_DIV16, /// Divide input clock by 16
	TMR_PRES_32 = MXC_V_TMR_CN_PRES_DIV32, /// Divide input clock by 32
	TMR_PRES_64 = MXC_V_TMR_CN_PRES_DIV64, /// Divide input clock by 64
	TMR_PRES_128 = MXC_V_TMR_CN_PRES_DIV128, /// Divide input clock by 128
	TMR_PRES_256 = (0x20 << MXC_F_TMR_CN_PRES_POS), /// Divide input clock
							/// by 256
	TMR_PRES_512 = (0x21 << MXC_F_TMR_CN_PRES_POS), /// Divide input clock
							/// by 512
	TMR_PRES_1024 = (0x22 << MXC_F_TMR_CN_PRES_POS), /// Divide input clock
							 /// by 1024
	TMR_PRES_2048 = (0x23 << MXC_F_TMR_CN_PRES_POS), /// Divide input clock
							 /// by 2048
	TMR_PRES_4096 = (0x24 << MXC_F_TMR_CN_PRES_POS), /// Divide input clock
							 /// by 4096
};

/* Timer modes */
enum tmr_mode {
	TMR_MODE_ONESHOT = MXC_V_TMR_CN_TMODE_ONESHOT, /// Timer Mode ONESHOT
	TMR_MODE_CONTINUOUS = MXC_V_TMR_CN_TMODE_CONTINUOUS, /// Timer Mode
							     /// CONTINUOUS
	TMR_MODE_COUNTER = MXC_V_TMR_CN_TMODE_COUNTER, /// Timer Mode COUNTER
	TMR_MODE_PWM = MXC_V_TMR_CN_TMODE_PWM, /// Timer Mode PWM
	TMR_MODE_CAPTURE = MXC_V_TMR_CN_TMODE_CAPTURE, /// Timer Mode CAPTURE
	TMR_MODE_COMPARE = MXC_V_TMR_CN_TMODE_COMPARE, /// Timer Mode COMPARE
	TMR_MODE_GATED = MXC_V_TMR_CN_TMODE_GATED, /// Timer Mode GATED
	TMR_MODE_CAPTURE_COMPARE =
		MXC_V_TMR_CN_TMODE_CAPTURECOMPARE /// Timer Mode CAPTURECOMPARE
};

/*
 * Calculate the number of microseconds for a given timer tick
 */
static inline uint32_t ticks_to_usecs(uint32_t ticks)
{
	return (uint64_t)ticks * SECOND / TIMER_FREQ_HZ;
}

/*
 * Calculate the number of timer ticks for a given microsecond value
 */
static inline uint32_t usecs_to_ticks(uint32_t usecs)
{
	return ((uint64_t)(usecs)*TIMER_FREQ_HZ / SECOND);
}

void __hw_clock_event_set(uint32_t deadline)
{
	uint32_t event_time_us;
	uint32_t event_time_ticks;
	uint32_t time_now;

	last_deadline = deadline;
	time_now = __hw_clock_source_read();

	/* check if the deadline has rolled over */
	if (deadline < time_now) {
		event_time_us = (0xFFFFFFFF - time_now) + deadline;
	} else {
		/* How long from the current time to the deadline? */
		event_time_us = (deadline - __hw_clock_source_read());
	}

	/* Convert event_time to ticks rounding up */
	event_time_ticks = usecs_to_ticks(event_time_us) + 1;

	/* set the event time into the timer compare */
	TMR_EVENT->cmp = event_time_ticks;
	/* zero out the timer */
	TMR_EVENT->cnt = 0;
	TMR_EVENT->cn |= MXC_F_TMR_CN_TEN;
}

uint32_t __hw_clock_event_get(void)
{
	return last_deadline;
}

void __hw_clock_event_clear(void)
{
	TMR_EVENT->cn &= ~(MXC_F_TMR_CN_TEN);
}

uint32_t __hw_clock_source_read(void)
{
	uint32_t timer_count_ticks;

	/* Read the timer value and return the results in microseconds */
	timer_count_ticks = TMR_ROLLOVER->cnt;
	return ticks_to_usecs(timer_count_ticks);
}

void __hw_clock_source_set(uint32_t ts)
{
	uint32_t timer_count_ticks;
	timer_count_ticks = usecs_to_ticks(ts);
	TMR_ROLLOVER->cnt = timer_count_ticks;
}

/**
 * Interrupt handler for Timer
 */
static void __timer_event_isr(void)
{
	/* Clear the event timer */
	TMR_EVENT->intr = MXC_F_TMR_INTR_IRQ_CLR;
	/* Process the timer, pass in that this was NOT a rollover event */
	if (TMR_ROLLOVER->intr) {
		TMR_ROLLOVER->intr = MXC_F_TMR_INTR_IRQ_CLR;
		process_timers(ROLLOVER_EVENT);
	} else {
		process_timers(NOT_ROLLOVER_EVENT);
	}
}
/*
 * Declare the EC Timer lower in priority than the I2C interrupt. This
 * allows the I2C driver to process time sensitive interrupts.
 */
DECLARE_IRQ(EC_TMR1_IRQn, __timer_event_isr, 2);

static void init_timer(mxc_tmr_regs_t *timer, enum tmr_pres prescaler,
		       enum tmr_mode mode, uint32_t count)
{
	/* Disable the Timer */
	timer->cn &= ~(MXC_F_TMR_CN_TEN);

	if (timer == MXC_TMR0) {
		/* Enable Timer 0 Clock */
		MXC_GCR->perckcn0 &= ~(MXC_F_GCR_PERCKCN0_T0D);
	} else if (timer == MXC_TMR1) {
		/* Enable Timer 1 Clock */
		MXC_GCR->perckcn0 &= ~(MXC_F_GCR_PERCKCN0_T1D);
	} else if (timer == MXC_TMR2) {
		/* Enable Timer 2 Clock */
		MXC_GCR->perckcn0 &= ~(MXC_F_GCR_PERCKCN0_T2D);
	}

	/* Disable timer and clear settings */
	timer->cn = 0;

	/* Clear interrupt flag */
	timer->intr = MXC_F_TMR_INTR_IRQ_CLR;

	/* Set the prescaler */
	timer->cn = (prescaler << MXC_F_TMR_CN_PRES_POS);

	/* Configure the timer */
	timer->cn = (timer->cn & ~(MXC_F_TMR_CN_TMODE | MXC_F_TMR_CN_TPOL)) |
		    ((mode << MXC_F_TMR_CN_TMODE_POS) & MXC_F_TMR_CN_TMODE) |
		    ((0 << MXC_F_TMR_CN_TPOL_POS) & MXC_F_TMR_CN_TPOL);

	timer->cnt = 0x1;
	timer->cmp = count;
}

int __hw_clock_source_init(uint32_t start_t)
{
	/* Initialize two timers, one for the OS Rollover and one for the OS
	 * Events */
	init_timer(TMR_ROLLOVER, TMR_PRESCALER, TMR_MODE_CONTINUOUS,
		   0xffffffff);
	init_timer(TMR_EVENT, TMR_PRESCALER, TMR_MODE_COMPARE, 0x0);
	__hw_clock_source_set(start_t);

	/* Enable the timers */
	TMR_ROLLOVER->cn |= MXC_F_TMR_CN_TEN;
	TMR_EVENT->cn |= MXC_F_TMR_CN_TEN;

	/* Enable the IRQ */
	task_enable_irq(TMR_EVENT_IRQ);

	/* Return the Event timer IRQ number (NOT the Rollover IRQ) */
	return TMR_EVENT_IRQ;
}

static int hwtimer_display(int argc, const char **argv)
{
	CPRINTS(" TMR_EVENT count 0x%08x", TMR_EVENT->cnt);
	CPRINTS(" TMR_ROLLOVER count 0x%08x", TMR_ROLLOVER->cnt);
	return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(hwtimer, hwtimer_display, "hwtimer",
			"Display hwtimer counts");