summaryrefslogtreecommitdiff
path: root/chip/npcx/hwtimer.c
blob: 76f1822a9480150c519a4b4a30896ab442f41e68 (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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* Hardware timers driver */

#include "clock.h"
#include "clock_chip.h"
#include "common.h"
#include "hooks.h"
#include "hwtimer.h"
#include "hwtimer_chip.h"
#include "math_util.h"
#include "registers.h"
#include "console.h"
#include "task.h"
#include "timer.h"
#include "util.h"

/* Depth of event timer */
#define TICK_EVT_DEPTH         16 /* Depth of event timer Unit: bits */
#define TICK_EVT_INTERVAL      (1 << TICK_EVT_DEPTH) /* Unit: us */
#define TICK_EVT_INTERVAL_MASK (TICK_EVT_INTERVAL - 1) /* Mask of interval */
#define TICK_EVT_MAX_CNT     (TICK_EVT_INTERVAL - 1) /* Maximum event counter */

/* Time when event will be expired unit:us */
static volatile uint32_t evt_expired_us;
/* 32-bits event counter */
static volatile uint32_t evt_cnt;
/* Debugger information */
#if DEBUG_TMR
static volatile uint32_t evt_cnt_us_dbg;
static volatile uint32_t cur_cnt_us_dbg;
#endif

#if !(DEBUG_TMR)
#define CPUTS(...)
#define CPRINTS(...)
#else
#define CPUTS(outstr) cputs(CC_CLOCK, outstr)
#define CPRINTS(format, args...) cprints(CC_CLOCK, format, ## args)
#endif

/*****************************************************************************/
/* Internal functions */
void init_hw_timer(int itim_no, enum ITIM_SOURCE_CLOCK_T source)
{
	/* Select which clock to use for this timer */
	UPDATE_BIT(NPCX_ITCTS(itim_no), NPCX_ITCTS_CKSEL,
			source != ITIM_SOURCE_CLOCK_APB2);

	/* Clear timeout status */
	SET_BIT(NPCX_ITCTS(itim_no), NPCX_ITCTS_TO_STS);

	/* ITIM timeout interrupt enable */
	SET_BIT(NPCX_ITCTS(itim_no), NPCX_ITCTS_TO_IE);

	/* ITIM timeout wake-up enable */
	SET_BIT(NPCX_ITCTS(itim_no), NPCX_ITCTS_TO_WUE);
}

/*****************************************************************************/
/* HWTimer event handlers */
void __hw_clock_event_set(uint32_t deadline)
{
	fp_t inv_evt_tick = FLOAT_TO_FP(INT_32K_CLOCK/(float)SECOND);
	int32_t  evt_cnt_us;
	/* Is deadline min value? */
	if (evt_expired_us != 0 && evt_expired_us < deadline)
		return;

	/* mark min event value */
	evt_expired_us = deadline;
	evt_cnt_us = deadline - __hw_clock_source_read();
#if DEBUG_TMR
	evt_cnt_us_dbg = deadline - __hw_clock_source_read();
#endif
	/* Deadline is behind current timer */
	if (evt_cnt_us < 0)
		evt_cnt_us = 1;

	/* Event module disable */
	CLEAR_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITCTS_ITEN);

	/*
	 * ITIM count down : event expired : Unit: 1/32768 sec
	 * It must exceed evt_expired_us for process_timers function
	 */
	evt_cnt = FP_TO_INT((fp_inter_t)(evt_cnt_us) * inv_evt_tick);
	if (evt_cnt > TICK_EVT_MAX_CNT) {
		CPRINTS("Event overflow! 0x%08x, us is %d\r\n",
				evt_cnt, evt_cnt_us);
		evt_cnt = TICK_EVT_MAX_CNT;
	}

	/* Wait for module disable to take effect before updating count */
	while (IS_BIT_SET(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITCTS_ITEN))
		;

	NPCX_ITCNT16(ITIM_EVENT_NO) = MAX(evt_cnt, 1);

	/* Event module enable */
	SET_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITCTS_ITEN);

	/* Wait for module enable */
	while (!IS_BIT_SET(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITCTS_ITEN))
		;

	/* Enable interrupt of ITIM */
	task_enable_irq(ITIM16_INT(ITIM_EVENT_NO));
}

/* Returns the time-stamp of the next programmed event */
uint32_t __hw_clock_event_get(void)
{
	if (evt_expired_us)
		return evt_expired_us;
	else /* No events. Give maximum deadline */
		return EVT_MAX_EXPIRED_US;
}

/* Get current counter value of event timer */
uint16_t __hw_clock_event_count(void)
{
	uint16_t cnt, cnt2;

	cnt = NPCX_ITCNT16(ITIM_EVENT_NO);
	/* Wait for two consecutive equal values are read */
	while ((cnt2 = NPCX_ITCNT16(ITIM_EVENT_NO)) != cnt)
		cnt = cnt2;

	return cnt;
}

/* Returns time delay cause of deep idle */
uint32_t __hw_clock_get_sleep_time(uint16_t pre_evt_cnt)
{
	fp_t evt_tick = FLOAT_TO_FP(SECOND/(float)INT_32K_CLOCK);
	uint32_t sleep_time;
	uint16_t cnt = __hw_clock_event_count();

	/* Event has been triggered but timer ISR doesn't handle it */
	if (IS_BIT_SET(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITCTS_TO_STS))
		sleep_time = FP_TO_INT((fp_inter_t)(pre_evt_cnt+1) * evt_tick);
	/* Event hasn't been triggered */
	else
		sleep_time = FP_TO_INT((fp_inter_t)(pre_evt_cnt+1 - cnt) *
				       evt_tick);

	return sleep_time;
}

/* Cancel the next event programmed by __hw_clock_event_set */
void __hw_clock_event_clear(void)
{
	/* ITIM event module disable */
	CLEAR_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITCTS_ITEN);

	/* Disable interrupt of Event */
	task_disable_irq(ITIM16_INT(ITIM_EVENT_NO));

	/* Clear event parameters */
	evt_expired_us = 0;
	evt_cnt = 0;
}

/* Irq for hwtimer event */
void __hw_clock_event_irq(void)
{
	/* ITIM event module disable */
	CLEAR_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITCTS_ITEN);

	/* Disable interrupt of event */
	task_disable_irq(ITIM16_INT(ITIM_EVENT_NO));

	/* Clear timeout status for event */
	SET_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITCTS_TO_STS);

	/* Clear event parameters */
	evt_expired_us = 0;
	evt_cnt = 0;

	/* handle upper driver */
	process_timers(0);

#ifdef CONFIG_LOW_POWER_IDLE
	/*
	 * Set event for ITIM32 after process_timers() since no events set if
	 * event's deadline is over 32 bits but current source clock isn't.
	 * ITIM32 is based on apb2 and ec won't wake-up in deep-idle even if it
	 * expires.
	 */
	if (evt_expired_us == 0)
		__hw_clock_event_set(EVT_MAX_EXPIRED_US);
#endif

}
DECLARE_IRQ(ITIM16_INT(ITIM_EVENT_NO), __hw_clock_event_irq, 3);


/*****************************************************************************/
/* HWTimer tick handlers */

/* Modify preload counter of source clock. */
void hw_clock_source_set_preload(uint32_t ts, uint8_t clear)
{
	/* ITIM32 module disable */
	CLEAR_BIT(NPCX_ITCTS(ITIM32), NPCX_ITCTS_ITEN);
	CLEAR_BIT(NPCX_ITCTS(ITIM32), NPCX_ITCTS_CKSEL);

	/* Set preload counter to current time */
	NPCX_ITCNT32 = TICK_ITIM32_MAX_CNT - ts;
	/* Clear timeout status or not */
	if (clear)
		SET_BIT(NPCX_ITCTS(ITIM32), NPCX_ITCTS_TO_STS);
	/* ITIM32 module enable */
	SET_BIT(NPCX_ITCTS(ITIM32), NPCX_ITCTS_ITEN);
}

/* Returns the value of the free-running counter used as clock. */
uint32_t __hw_clock_source_read(void)
{
	uint32_t cnt, cnt2;

	cnt = NPCX_ITCNT32;
	/*
	 * Wait for two consecutive equal values are read no matter
	 * ITIM's source clock is APB2 or 32K since mux's delay.
	 */
	while ((cnt2 = NPCX_ITCNT32) != cnt)
		cnt = cnt2;

#if DEBUG_TMR
	cur_cnt_us_dbg = TICK_ITIM32_MAX_CNT - cnt;
#endif
	return TICK_ITIM32_MAX_CNT - cnt;
}

/* Override the current value of the hardware counter */
void __hw_clock_source_set(uint32_t ts)
{
#if DEBUG_TMR
	cur_cnt_us_dbg = TICK_ITIM32_MAX_CNT - ts;
#endif
	hw_clock_source_set_preload(ts, 0);
}

/* Irq for hwtimer tick */
void __hw_clock_source_irq(void)
{
	/* Is timeout trigger trigger? */
	if (IS_BIT_SET(NPCX_ITCTS(ITIM32), NPCX_ITCTS_TO_STS)) {
		/* Restore ITIM32 preload counter value to maximum value */
		hw_clock_source_set_preload(0, 1);
		/* 32-bits timer count overflow */
		process_timers(1);

	} else { /* Handle soft trigger */
		process_timers(0);
#ifdef CONFIG_LOW_POWER_IDLE
		/* Set event for ITIM32. Please see above for detail */
		if (evt_expired_us == 0)
			__hw_clock_event_set(EVT_MAX_EXPIRED_US);
#endif
	}
}
DECLARE_IRQ(NPCX_IRQ_ITIM32, __hw_clock_source_irq, 3);

/* Handle ITIM32 overflow if interrupt is disabled */
void __hw_clock_handle_overflow(uint32_t clksrc_high)
{
	timestamp_t newtime;

	/* Overflow occurred? */
	if (!IS_BIT_SET(NPCX_ITCTS(ITIM32), NPCX_ITCTS_TO_STS))
		return;

	/* Clear timeout status */
	SET_BIT(NPCX_ITCTS(ITIM32), NPCX_ITCTS_TO_STS);

	/*
	 * Restore ITIM32 preload counter value to maximum and execute
	 * process_timers() later in ISR by trigger software interrupt in
	 * force_time().
	 */
	newtime.le.hi = clksrc_high + 1;
	newtime.le.lo = 0;
	force_time(newtime);
}

static void update_prescaler(void)
{
	/*
	 * prescaler to time tick
	 * Ttick_unit = (PRE_8+1) * Tapb2_clk
	 * PRE_8 = (Ttick_unit/Tapb2_clk) -1
	 */
	NPCX_ITPRE(ITIM32)  = (clock_get_apb2_freq() / SECOND) - 1;
	/* Set event tick unit = 1/32768 sec */
	NPCX_ITPRE(ITIM_EVENT_NO) = 0;

}
DECLARE_HOOK(HOOK_FREQ_CHANGE, update_prescaler, HOOK_PRIO_DEFAULT);

void __hw_early_init_hwtimer(uint32_t start_t)
{
	/*
	 * 1. Use ITIM16-1 as internal time reading
	 * 2. Use ITIM16-2 for event handling
	 */

	/* Enable clock for ITIM peripheral */
	clock_enable_peripheral(CGC_OFFSET_TIMER, CGC_TIMER_MASK,
			CGC_MODE_RUN | CGC_MODE_SLEEP);

	/* init tick & event timer first */
	init_hw_timer(ITIM32,  ITIM_SOURCE_CLOCK_APB2);
	init_hw_timer(ITIM_EVENT_NO, ITIM_SOURCE_CLOCK_32K);

	/* Set initial prescaler */
	update_prescaler();

	hw_clock_source_set_preload(start_t, 1);
}

/* Note that early_init_hwtimer() has already executed by this point */
int __hw_clock_source_init(uint32_t start_t)
{
	/*
	 * Override the count with the start value now that counting has
	 * started. Note that we may have already called this function from
	 * gpio_pre_init(), but only in the case where we expected a reset, so
	 * we should not get here in that case.
	 */
	__hw_early_init_hwtimer(start_t);

	/* Enable interrupt of ITIM */
	task_enable_irq(NPCX_IRQ_ITIM32);

	return NPCX_IRQ_ITIM32;
}