1259698Sdim/* 2259698Sdim * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved. 3259698Sdim * Copyright 2002-2005, Axel D��rfler, axeld@pinc-software.de. All rights reserved. 4259698Sdim * Distributed under the terms of the MIT License. 5259698Sdim * 6259698Sdim * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 7259698Sdim * Distributed under the terms of the NewOS License. 8259698Sdim */ 9259698Sdim 10259698Sdim#include <timer.h> 11259698Sdim#include <arch/x86/timer.h> 12259698Sdim 13259698Sdim#include <int.h> 14259698Sdim#include <arch/x86/apic.h> 15259698Sdim 16259698Sdim#include <arch/cpu.h> 17259698Sdim 18259698Sdim#include "apic_timer.h" 19259698Sdim 20259698Sdim 21259698Sdim//#define TRACE_APIC 22259698Sdim#ifdef TRACE_APIC 23259698Sdim# define TRACE(x...) dprintf("apic: " x) 24259698Sdim#else 25259698Sdim# define TRACE(x...) ; 26259698Sdim#endif 27259698Sdim 28259698Sdim 29259698Sdim/* Method Prototypes */ 30259698Sdimstatic int apic_timer_get_priority(); 31259698Sdimstatic status_t apic_timer_set_hardware_timer(bigtime_t relativeTimeout); 32259698Sdimstatic status_t apic_timer_clear_hardware_timer(); 33259698Sdimstatic status_t apic_timer_init(struct kernel_args *args); 34259698Sdim 35259698Sdimstatic uint32 sApicTicsPerSec = 0; 36259698Sdim 37261991Sdimstruct timer_info gAPICTimer = { 38259698Sdim "APIC", 39259698Sdim &apic_timer_get_priority, 40259698Sdim &apic_timer_set_hardware_timer, 41259698Sdim &apic_timer_clear_hardware_timer, 42259698Sdim &apic_timer_init 43259698Sdim}; 44259698Sdim 45259698Sdim 46259698Sdimstatic int 47259698Sdimapic_timer_get_priority() 48259698Sdim{ 49259698Sdim return 3; 50259698Sdim} 51259698Sdim 52259698Sdim 53259698Sdimstatic int32 54259698Sdimapic_timer_interrupt(void *data) 55259698Sdim{ 56259698Sdim return timer_interrupt(); 57259698Sdim} 58259698Sdim 59259698Sdim 60259698Sdim#define MIN_TIMEOUT 1 61259698Sdim 62259698Sdimstatic status_t 63259698Sdimapic_timer_set_hardware_timer(bigtime_t relativeTimeout) 64259698Sdim{ 65259698Sdim if (relativeTimeout < MIN_TIMEOUT) 66259698Sdim relativeTimeout = MIN_TIMEOUT; 67259698Sdim 68259698Sdim // calculation should be ok, since it's going to be 64-bit 69259698Sdim uint32 ticks = ((relativeTimeout * sApicTicsPerSec) / 1000000); 70259698Sdim 71259698Sdim cpu_status state = disable_interrupts(); 72259698Sdim 73259698Sdim uint32 config = apic_lvt_timer() | APIC_LVT_MASKED; // mask the timer 74259698Sdim apic_set_lvt_timer(config); 75259698Sdim 76259698Sdim apic_set_lvt_initial_timer_count(0); // zero out the timer 77259698Sdim 78259698Sdim config = apic_lvt_timer() & ~APIC_LVT_MASKED; // unmask the timer 79259698Sdim apic_set_lvt_timer(config); 80259698Sdim 81259698Sdim TRACE("arch_smp_set_apic_timer: config 0x%" B_PRIx32 ", timeout %" B_PRIdBIGTIME 82259698Sdim ", tics/sec %" B_PRIu32 ", tics %" B_PRId32 "\n", config, relativeTimeout, 83259698Sdim sApicTicsPerSec, ticks); 84259698Sdim 85259698Sdim apic_set_lvt_initial_timer_count(ticks); // start it up 86259698Sdim 87259698Sdim restore_interrupts(state); 88259698Sdim 89259698Sdim return B_OK; 90259698Sdim} 91259698Sdim 92259698Sdim 93259698Sdimstatic status_t 94259698Sdimapic_timer_clear_hardware_timer() 95259698Sdim{ 96259698Sdim cpu_status state = disable_interrupts(); 97259698Sdim 98259698Sdim uint32 config = apic_lvt_timer() | APIC_LVT_MASKED; 99259698Sdim // mask the timer 100259698Sdim apic_set_lvt_timer(config); 101259698Sdim 102259698Sdim apic_set_lvt_initial_timer_count(0); // zero out the timer 103259698Sdim 104259698Sdim restore_interrupts(state); 105259698Sdim return B_OK; 106259698Sdim} 107259698Sdim 108259698Sdim 109259698Sdimstatic status_t 110259698Sdimapic_timer_init(struct kernel_args *args) 111259698Sdim{ 112259698Sdim if (!apic_available()) 113259698Sdim return B_ERROR; 114259698Sdim 115259698Sdim sApicTicsPerSec = args->arch_args.apic_time_cv_factor; 116259698Sdim 117259698Sdim reserve_io_interrupt_vectors(1, 0xfb - ARCH_INTERRUPT_BASE, 118259698Sdim INTERRUPT_TYPE_LOCAL_IRQ); 119259698Sdim install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE, 120259698Sdim &apic_timer_interrupt, NULL, B_NO_LOCK_VECTOR); 121259698Sdim 122259698Sdim return B_OK; 123259698Sdim} 124259698Sdim 125259698Sdim 126259698Sdimstatus_t 127259698Sdimapic_timer_per_cpu_init(struct kernel_args *args, int32 cpu) 128259698Sdim{ 129259698Sdim /* setup timer */ 130259698Sdim uint32 config = apic_lvt_timer() & APIC_LVT_TIMER_MASK; 131259698Sdim config |= 0xfb | APIC_LVT_MASKED; // vector 0xfb, timer masked 132259698Sdim apic_set_lvt_timer(config); 133259698Sdim 134259698Sdim apic_set_lvt_initial_timer_count(0); // zero out the clock 135259698Sdim 136259698Sdim config = apic_lvt_timer_divide_config() & 0xfffffff0; 137259698Sdim config |= APIC_TIMER_DIVIDE_CONFIG_1; // clock division by 1 138259698Sdim apic_set_lvt_timer_divide_config(config); 139259698Sdim return B_OK; 140259698Sdim} 141259698Sdim