2 * Copyright 2009-2010, Stefano Ceccherini (stefano.ceccherini@gmail.com)
3 * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved.
4 * Distributed under the terms of the MIT License.
14 #include <arch/x86/timer.h>
15 #include <arch/x86/arch_hpet.h>
17 #include <boot/kernel_args.h>
23 #define TRACE(x) dprintf x
30 static struct hpet_regs
*sHPETRegs
;
31 static volatile struct hpet_timer
*sTimer
;
32 static uint64 sHPETPeriod
;
34 static int hpet_get_priority();
35 static status_t
hpet_set_hardware_timer(bigtime_t relativeTimeout
);
36 static status_t
hpet_clear_hardware_timer();
37 static status_t
hpet_init(struct kernel_args
*args
);
40 struct timer_info gHPETTimer
= {
43 &hpet_set_hardware_timer
,
44 &hpet_clear_hardware_timer
,
52 // TODO: Fix HPET in SMP mode.
53 if (smp_get_num_cpus() > 1)
56 // HPET timers, being off-chip, are more expensive to setup
63 hpet_timer_interrupt(void *arg
)
65 //dprintf_no_syslog("HPET timer_interrupt!!!!\n");
66 return timer_interrupt();
70 static inline bigtime_t
71 hpet_convert_timeout(const bigtime_t
&relativeTimeout
)
73 return ((relativeTimeout
* 1000000000ULL)
74 / sHPETPeriod
) + sHPETRegs
->u0
.counter64
;
78 #define MIN_TIMEOUT 3000
81 hpet_set_hardware_timer(bigtime_t relativeTimeout
)
83 cpu_status state
= disable_interrupts();
85 // enable timer interrupt
86 sTimer
->config
|= HPET_CONF_TIMER_INT_ENABLE
;
89 if (relativeTimeout
< MIN_TIMEOUT
)
90 relativeTimeout
= MIN_TIMEOUT
;
92 bigtime_t timerValue
= hpet_convert_timeout(relativeTimeout
);
94 sTimer
->u0
.comparator64
= timerValue
;
96 restore_interrupts(state
);
103 hpet_clear_hardware_timer()
105 // Disable timer interrupt
106 sTimer
->config
&= ~HPET_CONF_TIMER_INT_ENABLE
;
112 hpet_set_enabled(bool enabled
)
115 sHPETRegs
->config
|= HPET_CONF_MASK_ENABLED
;
117 sHPETRegs
->config
&= ~HPET_CONF_MASK_ENABLED
;
123 hpet_set_legacy(bool enabled
)
125 if (!HPET_IS_LEGACY_CAPABLE(sHPETRegs
)) {
126 dprintf("hpet_init: HPET doesn't support legacy mode. Skipping.\n");
127 return B_NOT_SUPPORTED
;
131 sHPETRegs
->config
|= HPET_CONF_MASK_LEGACY
;
133 sHPETRegs
->config
&= ~HPET_CONF_MASK_LEGACY
;
141 hpet_dump_timer(volatile struct hpet_timer
*timer
)
143 dprintf("HPET Timer %ld:\n", (timer
- sHPETRegs
->timer
));
145 dprintf("\troutable IRQs: ");
146 uint32 interrupts
= (uint32
)HPET_GET_CAP_TIMER_ROUTE(timer
);
147 for (int i
= 0; i
< 32; i
++) {
148 if (interrupts
& (1 << i
))
152 dprintf("\tconfiguration: 0x%llx\n", timer
->config
);
153 dprintf("\tFSB Enabled: %s\n",
154 timer
->config
& HPET_CONF_TIMER_FSB_ENABLE
? "Yes" : "No");
155 dprintf("\tInterrupt Enabled: %s\n",
156 timer
->config
& HPET_CONF_TIMER_INT_ENABLE
? "Yes" : "No");
157 dprintf("\tTimer type: %s\n",
158 timer
->config
& HPET_CONF_TIMER_TYPE
? "Periodic" : "OneShot");
159 dprintf("\tInterrupt Type: %s\n",
160 timer
->config
& HPET_CONF_TIMER_INT_TYPE
? "Level" : "Edge");
162 dprintf("\tconfigured IRQ: %lld\n",
163 HPET_GET_CONF_TIMER_INT_ROUTE(timer
));
165 if (timer
->config
& HPET_CONF_TIMER_FSB_ENABLE
) {
166 dprintf("\tfsb_route[0]: 0x%llx\n", timer
->fsb_route
[0]);
167 dprintf("\tfsb_route[1]: 0x%llx\n", timer
->fsb_route
[1]);
174 hpet_init_timer(volatile struct hpet_timer
*timer
)
178 uint32 interrupt
= 0;
180 sTimer
->config
|= (interrupt
<< HPET_CONF_TIMER_INT_ROUTE_SHIFT
)
181 & HPET_CONF_TIMER_INT_ROUTE_MASK
;
183 // Non-periodic mode, edge triggered
184 sTimer
->config
&= ~(HPET_CONF_TIMER_TYPE
| HPET_CONF_TIMER_INT_TYPE
);
186 sTimer
->config
&= ~HPET_CONF_TIMER_FSB_ENABLE
;
187 sTimer
->config
&= ~HPET_CONF_TIMER_32MODE
;
190 sTimer
->config
|= HPET_CONF_TIMER_INT_ENABLE
;
193 hpet_dump_timer(sTimer
);
201 uint64 initialValue
= sHPETRegs
->u0
.counter64
;
203 uint64 finalValue
= sHPETRegs
->u0
.counter64
;
205 if (initialValue
== finalValue
) {
206 dprintf("hpet_test: counter does not increment\n");
215 hpet_init(struct kernel_args
*args
)
217 /* hpet_acpi_probe() through a similar "scan spots" table
219 Seems to be the most elegant solution right now. */
220 if (args
->arch_args
.hpet
== NULL
)
223 if (sHPETRegs
== NULL
) {
224 sHPETRegs
= (struct hpet_regs
*)args
->arch_args
.hpet
.Pointer();
225 if (vm_map_physical_memory(B_SYSTEM_TEAM
, "hpet",
226 (void **)&sHPETRegs
, B_EXACT_ADDRESS
, B_PAGE_SIZE
,
227 B_KERNEL_READ_AREA
| B_KERNEL_WRITE_AREA
,
228 (phys_addr_t
)args
->arch_args
.hpet_phys
, true) < B_OK
) {
229 // Would it be better to panic here?
230 dprintf("hpet_init: Failed to map memory for the HPET registers.");
235 sHPETPeriod
= HPET_GET_PERIOD(sHPETRegs
);
237 TRACE(("hpet_init: HPET is at %p.\n\tVendor ID: %llx, rev: %llx, period: %lld\n",
238 sHPETRegs
, HPET_GET_VENDOR_ID(sHPETRegs
), HPET_GET_REVID(sHPETRegs
),
241 status_t status
= hpet_set_enabled(false);
245 status
= hpet_set_legacy(true);
249 uint32 numTimers
= HPET_GET_NUM_TIMERS(sHPETRegs
) + 1;
251 TRACE(("hpet_init: HPET supports %lu timers, and is %s bits wide.\n",
252 numTimers
, HPET_IS_64BIT(sHPETRegs
) ? "64" : "32"));
254 TRACE(("hpet_init: configuration: 0x%llx, timer_interrupts: 0x%llx\n",
255 sHPETRegs
->config
, sHPETRegs
->interrupt_status
));
258 dprintf("hpet_init: HPET does not have at least 3 timers. Skipping.\n");
263 for (uint32 c
= 0; c
< numTimers
; c
++)
264 hpet_dump_timer(&sHPETRegs
->timer
[c
]);
267 hpet_init_timer(&sHPETRegs
->timer
[0]);
269 status
= hpet_set_enabled(true);
274 status
= hpet_test();
279 int32 configuredIRQ
= HPET_GET_CONF_TIMER_INT_ROUTE(sTimer
);
281 install_io_interrupt_handler(configuredIRQ
, &hpet_timer_interrupt
,
282 NULL
, B_NO_LOCK_VECTOR
);