Skip to content

Commit 78ab750

Browse files
dcpleunggalak
authored andcommitted
timer: hpet: convert register access to functions
This converts register access from macro to functions. This allows SoCs to override these functions if needed. Signed-off-by: Daniel Leung <[email protected]>
1 parent d9df404 commit 78ab750

File tree

1 file changed

+231
-42
lines changed

1 file changed

+231
-42
lines changed

drivers/timer/hpet.c

Lines changed: 231 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,206 @@
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
1683
DEVICE_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
118292
static 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
132308
int 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
238423
uint32_t sys_clock_cycle_get_32(void)
239424
{
240-
return MAIN_COUNTER_REG;
425+
return hpet_counter_get();
241426
}
242427

243428
__pinned_func
244429
void 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

Comments
 (0)