1313
1414#include <dt-bindings/interrupt-controller/intel-ioapic.h>
1515
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
1683DEVICE_MMIO_TOPLEVEL_STATIC (hpet_regs , DT_DRV_INST (0 ));
1784
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+ }
20164
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+ }
27176
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+ }
31189
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+ }
34202
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 */
42216
43217#ifndef HPET_COUNTER_CLK_PERIOD
44218/* COUNTER_CLK_PERIOD (CLK_PERIOD_REG) is in femtoseconds (1e-15 sec) */
@@ -73,15 +247,15 @@ static void hpet_isr(const void *arg)
73247
74248 k_spinlock_key_t key = k_spin_lock (& lock );
75249
76- uint32_t now = MAIN_COUNTER_REG ;
250+ uint32_t now = hpet_counter_get () ;
77251
78252#if ((DT_INST_IRQ (0 , sense ) & IRQ_TYPE_LEVEL ) == IRQ_TYPE_LEVEL )
79253 /*
80254 * Clear interrupt only if level trigger is selected.
81255 * When edge trigger is selected, spec says only 0 can
82256 * be written.
83257 */
84- INTR_STATUS_REG = TIMER0_INT_STS ;
258+ hpet_int_sts_set ( TIMER0_INT_STS ) ;
85259#endif
86260
87261 if (IS_ENABLED (CONFIG_SMP ) &&
@@ -107,7 +281,7 @@ static void hpet_isr(const void *arg)
107281 if ((int32_t )(next - now ) < HPET_CMP_MIN_DELAY ) {
108282 next += cyc_per_tick ;
109283 }
110- TIMER0_COMPARATOR_REG = next ;
284+ hpet_timer_comparator_set ( next ) ;
111285 }
112286
113287 k_spin_unlock (& lock , key );
@@ -117,28 +291,30 @@ static void hpet_isr(const void *arg)
117291__pinned_func
118292static void set_timer0_irq (unsigned int irq )
119293{
294+ uint32_t val = hpet_timer_conf_get ();
295+
120296 /* 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 );
122298
123299#if ((DT_INST_IRQ (0 , sense ) & IRQ_TYPE_LEVEL ) == IRQ_TYPE_LEVEL )
124300 /* Level trigger */
125- val |= TCONF_INT_LEVEL ;
301+ val |= TIMER_CONF_INT_LEVEL ;
126302#endif
127303
128- TIMER0_CONF_REG = val ;
304+ hpet_timer_conf_set ( val ) ;
129305}
130306
131307__boot_func
132308int sys_clock_driver_init (const struct device * dev )
133309{
134310 extern int z_clock_hw_cycles_per_sec ;
135- uint32_t hz ;
311+ uint32_t hz , reg ;
136312
137313 ARG_UNUSED (dev );
138314 ARG_UNUSED (hz );
139315 ARG_UNUSED (z_clock_hw_cycles_per_sec );
140316
141- DEVICE_MMIO_TOPLEVEL_MAP ( hpet_regs , K_MEM_CACHE_NONE );
317+ hpet_mmio_init ( );
142318
143319 IRQ_CONNECT (DT_INST_IRQN (0 ),
144320 DT_INST_IRQ (0 , priority ),
@@ -147,28 +323,33 @@ int sys_clock_driver_init(const struct device *dev)
147323 irq_enable (DT_INST_IRQN (0 ));
148324
149325#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 () );
151327 z_clock_hw_cycles_per_sec = hz ;
152328 cyc_per_tick = hz / CONFIG_SYS_CLOCK_TICKS_PER_SEC ;
153329
154330 max_ticks = (MAX_TICKS - cyc_per_tick ) / cyc_per_tick ;
155331#endif
156332
333+ last_count = hpet_counter_get ();
334+
157335 /* Note: we set the legacy routing bit, because otherwise
158336 * nothing in Zephyr disables the PIT which then fires
159337 * interrupts into the same IRQ. But that means we're then
160338 * forced to use IRQ2 contra the way the kconfig IRQ selection
161339 * is supposed to work. Should fix this.
162340 */
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 );
167344
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 );
169351
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 );
172353
173354 return 0 ;
174355}
@@ -188,16 +369,20 @@ void sys_clock_set_timeout(int32_t ticks, bool idle)
188369 ARG_UNUSED (idle );
189370
190371#if defined(CONFIG_TICKLESS_KERNEL )
372+ uint32_t reg ;
373+
191374 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 );
193378 return ;
194379 }
195380
196381 ticks = ticks == K_TICKS_FOREVER ? max_ticks : ticks ;
197382 ticks = CLAMP (ticks - 1 , 0 , (int32_t )max_ticks );
198383
199384 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 ;
201386 uint32_t max_cyc = max_ticks * cyc_per_tick ;
202387
203388 /* Round up to next tick boundary. */
@@ -215,7 +400,7 @@ void sys_clock_set_timeout(int32_t ticks, bool idle)
215400 cyc += cyc_per_tick ;
216401 }
217402
218- TIMER0_COMPARATOR_REG = cyc ;
403+ hpet_timer_comparator_set ( cyc ) ;
219404 k_spin_unlock (& lock , key );
220405#endif
221406}
@@ -228,7 +413,7 @@ uint32_t sys_clock_elapsed(void)
228413 }
229414
230415 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 ;
232417
233418 k_spin_unlock (& lock , key );
234419 return ret ;
@@ -237,11 +422,15 @@ uint32_t sys_clock_elapsed(void)
237422__pinned_func
238423uint32_t sys_clock_cycle_get_32 (void )
239424{
240- return MAIN_COUNTER_REG ;
425+ return hpet_counter_get () ;
241426}
242427
243428__pinned_func
244429void sys_clock_idle_exit (void )
245430{
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 );
247436}
0 commit comments