13
13
14
14
#include <dt-bindings/interrupt-controller/intel-ioapic.h>
15
15
16
+ #include <soc.h>
17
+
18
+ /**
19
+ * @file
20
+ * @brief HPET (High Precision Event Timers) driver
21
+ *
22
+ * HPET hardware contains a number of timers which can be used by
23
+ * the operating system, where the number of timers is implementation
24
+ * specific. The timers are implemented as a single up-counter with
25
+ * a set of comparators where the counter increases monotonically.
26
+ * Each timer has a match register and a comparator, and can generate
27
+ * an interrupt when the value in the match register equals the value of
28
+ * the free running counter. Some of these timers can be enabled to
29
+ * generate periodic interrupt.
30
+ *
31
+ * The HPET registers are usually mapped to memory space on x86
32
+ * hardware. If this is not the case, custom register access functions
33
+ * can be used by defining macro HPET_USE_CUSTOM_REG_ACCESS_FUNCS in
34
+ * soc.h, and implementing necessary initialization and access
35
+ * functions as described below.
36
+ *
37
+ * HPET_COUNTER_CLK_PERIOD can be overridden in soc.h if
38
+ * COUNTER_CLK_PERIOD is not in femtoseconds (1e-15 sec).
39
+ *
40
+ * HPET_CMP_MIN_DELAY can be overridden in soc.h to better match
41
+ * the frequency of the timers. Default is 1000 where the value
42
+ * written to the comparator must be 1000 larger than the current
43
+ * main counter value.
44
+ */
45
+
46
+ /* General Configuration register */
47
+ #define GCONF_ENABLE BIT(0)
48
+ #define GCONF_LR BIT(1) /* legacy interrupt routing, */
49
+ /* disables PIT */
50
+
51
+ /* General Interrupt Status register */
52
+ #define TIMER0_INT_STS BIT(0)
53
+
54
+ /* Timer Configuration and Capabilities register */
55
+ #define TIMER_CONF_INT_LEVEL BIT(1)
56
+ #define TIMER_CONF_INT_ENABLE BIT(2)
57
+ #define TIMER_CONF_PERIODIC BIT(3)
58
+ #define TIMER_CONF_VAL_SET BIT(6)
59
+ #define TIMER_CONF_MODE32 BIT(8)
60
+ #define TIMER_CONF_FSB_EN BIT(14) /* FSB interrupt delivery */
61
+ /* enable */
62
+
63
+ /*
64
+ * The following MMIO initialization and register access functions
65
+ * should work on generic x86 hardware. If the targeted SoC requires
66
+ * special handling of HPET registers, these functions will need to be
67
+ * implemented in the SoC layer by first defining the macro
68
+ * HPET_USE_CUSTOM_REG_ACCESS_FUNCS in soc.h to signal such intent.
69
+ *
70
+ * This is a list of functions which must be implemented in the SoC
71
+ * layer:
72
+ * void hpet_mmio_init(void)
73
+ * uint32_t hpet_counter_get(void)
74
+ * uint32_t hpet_counter_clk_period_get(void)
75
+ * uint32_t hpet_gconf_get(void)
76
+ * void hpet_gconf_set(uint32_t val)
77
+ * void hpet_int_sts_set(uint32_t val)
78
+ * uint32_t hpet_timer_conf_get(void)
79
+ * void hpet_timer_conf_set(uint32_t val)
80
+ * void hpet_timer_comparator_set(uint32_t val)
81
+ */
82
+ #ifndef HPET_USE_CUSTOM_REG_ACCESS_FUNCS
16
83
DEVICE_MMIO_TOPLEVEL_STATIC (hpet_regs , DT_DRV_INST (0 ));
17
84
18
- #define HPET_REG32 (off ) (*(volatile uint32_t *)(long) \
19
- (DEVICE_MMIO_TOPLEVEL_GET(hpet_regs) + (off)))
85
+ #define HPET_REG_ADDR (off ) \
86
+ ((mm_reg_t)(DEVICE_MMIO_TOPLEVEL_GET(hpet_regs) + (off)))
87
+
88
+ /* High dword of General Capabilities and ID register */
89
+ #define CLK_PERIOD_REG HPET_REG_ADDR(0x04)
90
+
91
+ /* General Configuration register */
92
+ #define GCONF_REG HPET_REG_ADDR(0x10)
93
+
94
+ /* General Interrupt Status register */
95
+ #define INTR_STATUS_REG HPET_REG_ADDR(0x20)
96
+
97
+ /* Main Counter Register */
98
+ #define MAIN_COUNTER_REG HPET_REG_ADDR(0xf0)
99
+
100
+ /* Timer 0 Configuration and Capabilities register */
101
+ #define TIMER0_CONF_REG HPET_REG_ADDR(0x100)
102
+
103
+ /* Timer 0 Comparator Register */
104
+ #define TIMER0_COMPARATOR_REG HPET_REG_ADDR(0x108)
105
+
106
+ /**
107
+ * @brief Setup memory mappings needed to access HPET registers.
108
+ *
109
+ * This is called in sys_clock_driver_init() to setup any memory
110
+ * mappings needed to access HPET registers.
111
+ */
112
+ static inline void hpet_mmio_init (void )
113
+ {
114
+ DEVICE_MMIO_TOPLEVEL_MAP (hpet_regs , K_MEM_CACHE_NONE );
115
+ }
116
+
117
+ /**
118
+ * @brief Return the value of the main counter.
119
+ *
120
+ * @return Value of Main Counter
121
+ */
122
+ static inline uint32_t hpet_counter_get (void )
123
+ {
124
+ return sys_read32 (MAIN_COUNTER_REG );
125
+ }
126
+
127
+ /**
128
+ * @brief Get COUNTER_CLK_PERIOD
129
+ *
130
+ * Read and return the COUNTER_CLK_PERIOD, which is the high
131
+ * 32-bit of the General Capabilities and ID Register. This can
132
+ * be used to calculate the frequency of the main counter.
133
+ *
134
+ * Usually the period is in femtoseconds. If this is not
135
+ * the case, define HPET_COUNTER_CLK_PERIOD in soc.h so
136
+ * it can be used to calculate frequency.
137
+ *
138
+ * @return COUNTER_CLK_PERIOD
139
+ */
140
+ static inline uint32_t hpet_counter_clk_period_get (void )
141
+ {
142
+ return sys_read32 (CLK_PERIOD_REG );
143
+ }
144
+
145
+ /**
146
+ * @brief Return the value of the General Configuration Register
147
+ *
148
+ * @return Value of the General Configuration Register
149
+ */
150
+ static inline uint32_t hpet_gconf_get (void )
151
+ {
152
+ return sys_read32 (GCONF_REG );
153
+ }
154
+
155
+ /**
156
+ * @brief Write to General Configuration Register
157
+ *
158
+ * @param val Value to be written to the register
159
+ */
160
+ static inline void hpet_gconf_set (uint32_t val )
161
+ {
162
+ sys_write32 (val , GCONF_REG );
163
+ }
20
164
21
- #define CLK_PERIOD_REG HPET_REG32(0x04) /* High dword of caps reg */
22
- #define GENERAL_CONF_REG HPET_REG32(0x10)
23
- #define INTR_STATUS_REG HPET_REG32(0x20)
24
- #define MAIN_COUNTER_REG HPET_REG32(0xf0)
25
- #define TIMER0_CONF_REG HPET_REG32(0x100)
26
- #define TIMER0_COMPARATOR_REG HPET_REG32(0x108)
165
+ /**
166
+ * @brief Write to General Interrupt Status Register
167
+ *
168
+ * This is used to acknowledge and clear interrupt bits.
169
+ *
170
+ * @param val Value to be written to the register
171
+ */
172
+ static inline void hpet_int_sts_set (uint32_t val )
173
+ {
174
+ sys_write32 (val , INTR_STATUS_REG );
175
+ }
27
176
28
- /* GENERAL_CONF_REG bits */
29
- #define GCONF_ENABLE BIT(0)
30
- #define GCONF_LR BIT(1) /* legacy interrupt routing, disables PIT */
177
+ /**
178
+ * @brief Return the value of the Timer Configuration Register
179
+ *
180
+ * This reads and returns the value of the Timer Configuration
181
+ * Register of Timer #0.
182
+ *
183
+ * @return Value of the Timer Configuration Register
184
+ */
185
+ static inline uint32_t hpet_timer_conf_get (void )
186
+ {
187
+ return sys_read32 (TIMER0_CONF_REG );
188
+ }
31
189
32
- /* INTR_STATUS_REG bits */
33
- #define TIMER0_INT_STS BIT(0)
190
+ /**
191
+ * @brief Write to the Timer Configuration Register
192
+ *
193
+ * This writes the specified value to the Timer Configuration
194
+ * Register of Timer #0.
195
+ *
196
+ * @param val Value to be written to the register
197
+ */
198
+ static inline void hpet_timer_conf_set (uint32_t val )
199
+ {
200
+ sys_write32 (val , TIMER0_CONF_REG );
201
+ }
34
202
35
- /* TIMERn_CONF_REG bits */
36
- #define TCONF_INT_LEVEL BIT(1)
37
- #define TCONF_INT_ENABLE BIT(2)
38
- #define TCONF_PERIODIC BIT(3)
39
- #define TCONF_VAL_SET BIT(6)
40
- #define TCONF_MODE32 BIT(8)
41
- #define TCONF_FSB_EN BIT(14) /* FSB interrupt delivery enable */
203
+ /**
204
+ * @brief Write to the Timer Comparator Value Register
205
+ *
206
+ * This writes the specified value to the Timer Comparator
207
+ * Value Register of Timer #0.
208
+ *
209
+ * @param val Value to be written to the register
210
+ */
211
+ static inline void hpet_timer_comparator_set (uint32_t val )
212
+ {
213
+ sys_write32 (val , TIMER0_COMPARATOR_REG );
214
+ }
215
+ #endif /* HPET_USE_CUSTOM_REG_ACCESS_FUNCS */
42
216
43
217
#ifndef HPET_COUNTER_CLK_PERIOD
44
218
/* COUNTER_CLK_PERIOD (CLK_PERIOD_REG) is in femtoseconds (1e-15 sec) */
@@ -73,15 +247,15 @@ static void hpet_isr(const void *arg)
73
247
74
248
k_spinlock_key_t key = k_spin_lock (& lock );
75
249
76
- uint32_t now = MAIN_COUNTER_REG ;
250
+ uint32_t now = hpet_counter_get () ;
77
251
78
252
#if ((DT_INST_IRQ (0 , sense ) & IRQ_TYPE_LEVEL ) == IRQ_TYPE_LEVEL )
79
253
/*
80
254
* Clear interrupt only if level trigger is selected.
81
255
* When edge trigger is selected, spec says only 0 can
82
256
* be written.
83
257
*/
84
- INTR_STATUS_REG = TIMER0_INT_STS ;
258
+ hpet_int_sts_set ( TIMER0_INT_STS ) ;
85
259
#endif
86
260
87
261
if (IS_ENABLED (CONFIG_SMP ) &&
@@ -107,7 +281,7 @@ static void hpet_isr(const void *arg)
107
281
if ((int32_t )(next - now ) < HPET_CMP_MIN_DELAY ) {
108
282
next += cyc_per_tick ;
109
283
}
110
- TIMER0_COMPARATOR_REG = next ;
284
+ hpet_timer_comparator_set ( next ) ;
111
285
}
112
286
113
287
k_spin_unlock (& lock , key );
@@ -117,28 +291,30 @@ static void hpet_isr(const void *arg)
117
291
__pinned_func
118
292
static void set_timer0_irq (unsigned int irq )
119
293
{
294
+ uint32_t val = hpet_timer_conf_get ();
295
+
120
296
/* 5-bit IRQ field starting at bit 9 */
121
- uint32_t val = (TIMER0_CONF_REG & ~(0x1f << 9 )) | ((irq & 0x1f ) << 9 );
297
+ val = (val & ~(0x1f << 9 )) | ((irq & 0x1f ) << 9 );
122
298
123
299
#if ((DT_INST_IRQ (0 , sense ) & IRQ_TYPE_LEVEL ) == IRQ_TYPE_LEVEL )
124
300
/* Level trigger */
125
- val |= TCONF_INT_LEVEL ;
301
+ val |= TIMER_CONF_INT_LEVEL ;
126
302
#endif
127
303
128
- TIMER0_CONF_REG = val ;
304
+ hpet_timer_conf_set ( val ) ;
129
305
}
130
306
131
307
__boot_func
132
308
int sys_clock_driver_init (const struct device * dev )
133
309
{
134
310
extern int z_clock_hw_cycles_per_sec ;
135
- uint32_t hz ;
311
+ uint32_t hz , reg ;
136
312
137
313
ARG_UNUSED (dev );
138
314
ARG_UNUSED (hz );
139
315
ARG_UNUSED (z_clock_hw_cycles_per_sec );
140
316
141
- DEVICE_MMIO_TOPLEVEL_MAP ( hpet_regs , K_MEM_CACHE_NONE );
317
+ hpet_mmio_init ( );
142
318
143
319
IRQ_CONNECT (DT_INST_IRQN (0 ),
144
320
DT_INST_IRQ (0 , priority ),
@@ -147,28 +323,33 @@ int sys_clock_driver_init(const struct device *dev)
147
323
irq_enable (DT_INST_IRQN (0 ));
148
324
149
325
#ifdef CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME
150
- hz = (uint32_t )(HPET_COUNTER_CLK_PERIOD / CLK_PERIOD_REG );
326
+ hz = (uint32_t )(HPET_COUNTER_CLK_PERIOD / hpet_counter_clk_period_get () );
151
327
z_clock_hw_cycles_per_sec = hz ;
152
328
cyc_per_tick = hz / CONFIG_SYS_CLOCK_TICKS_PER_SEC ;
153
329
154
330
max_ticks = (MAX_TICKS - cyc_per_tick ) / cyc_per_tick ;
155
331
#endif
156
332
333
+ last_count = hpet_counter_get ();
334
+
157
335
/* Note: we set the legacy routing bit, because otherwise
158
336
* nothing in Zephyr disables the PIT which then fires
159
337
* interrupts into the same IRQ. But that means we're then
160
338
* forced to use IRQ2 contra the way the kconfig IRQ selection
161
339
* is supposed to work. Should fix this.
162
340
*/
163
- GENERAL_CONF_REG |= GCONF_LR | GCONF_ENABLE ;
164
- TIMER0_CONF_REG &= ~TCONF_PERIODIC ;
165
- TIMER0_CONF_REG &= ~TCONF_FSB_EN ;
166
- TIMER0_CONF_REG |= TCONF_MODE32 ;
341
+ reg = hpet_gconf_get ();
342
+ reg |= GCONF_LR | GCONF_ENABLE ;
343
+ hpet_gconf_set (reg );
167
344
168
- last_count = MAIN_COUNTER_REG ;
345
+ reg = hpet_timer_conf_get ();
346
+ reg &= ~TIMER_CONF_PERIODIC ;
347
+ reg &= ~TIMER_CONF_FSB_EN ;
348
+ reg |= TIMER_CONF_MODE32 ;
349
+ reg |= TIMER_CONF_INT_ENABLE ;
350
+ hpet_timer_conf_set (reg );
169
351
170
- TIMER0_CONF_REG |= TCONF_INT_ENABLE ;
171
- TIMER0_COMPARATOR_REG = MAIN_COUNTER_REG + cyc_per_tick ;
352
+ hpet_timer_comparator_set (last_count + cyc_per_tick );
172
353
173
354
return 0 ;
174
355
}
@@ -188,16 +369,20 @@ void sys_clock_set_timeout(int32_t ticks, bool idle)
188
369
ARG_UNUSED (idle );
189
370
190
371
#if defined(CONFIG_TICKLESS_KERNEL )
372
+ uint32_t reg ;
373
+
191
374
if (ticks == K_TICKS_FOREVER && idle ) {
192
- GENERAL_CONF_REG &= ~GCONF_ENABLE ;
375
+ reg = hpet_gconf_get ();
376
+ reg &= ~GCONF_ENABLE ;
377
+ hpet_gconf_set (reg );
193
378
return ;
194
379
}
195
380
196
381
ticks = ticks == K_TICKS_FOREVER ? max_ticks : ticks ;
197
382
ticks = CLAMP (ticks - 1 , 0 , (int32_t )max_ticks );
198
383
199
384
k_spinlock_key_t key = k_spin_lock (& lock );
200
- uint32_t now = MAIN_COUNTER_REG , cyc , adj ;
385
+ uint32_t now = hpet_counter_get () , cyc , adj ;
201
386
uint32_t max_cyc = max_ticks * cyc_per_tick ;
202
387
203
388
/* Round up to next tick boundary. */
@@ -215,7 +400,7 @@ void sys_clock_set_timeout(int32_t ticks, bool idle)
215
400
cyc += cyc_per_tick ;
216
401
}
217
402
218
- TIMER0_COMPARATOR_REG = cyc ;
403
+ hpet_timer_comparator_set ( cyc ) ;
219
404
k_spin_unlock (& lock , key );
220
405
#endif
221
406
}
@@ -228,7 +413,7 @@ uint32_t sys_clock_elapsed(void)
228
413
}
229
414
230
415
k_spinlock_key_t key = k_spin_lock (& lock );
231
- uint32_t ret = (MAIN_COUNTER_REG - last_count ) / cyc_per_tick ;
416
+ uint32_t ret = (hpet_counter_get () - last_count ) / cyc_per_tick ;
232
417
233
418
k_spin_unlock (& lock , key );
234
419
return ret ;
@@ -237,11 +422,15 @@ uint32_t sys_clock_elapsed(void)
237
422
__pinned_func
238
423
uint32_t sys_clock_cycle_get_32 (void )
239
424
{
240
- return MAIN_COUNTER_REG ;
425
+ return hpet_counter_get () ;
241
426
}
242
427
243
428
__pinned_func
244
429
void sys_clock_idle_exit (void )
245
430
{
246
- GENERAL_CONF_REG |= GCONF_ENABLE ;
431
+ uint32_t reg ;
432
+
433
+ reg = hpet_gconf_get ();
434
+ reg |= GCONF_ENABLE ;
435
+ hpet_gconf_set (reg );
247
436
}
0 commit comments