1 /* 2 * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved. 3 * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 4 * Distributed under the terms of the MIT License. 5 * 6 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 7 * Distributed under the terms of the NewOS License. 8 */ 9 10 #include <timer.h> 11 #include <arch/x86/timer.h> 12 13 #include <int.h> 14 #include <arch/x86/arch_apic.h> 15 16 #include <arch/cpu.h> 17 #include <arch/smp.h> 18 19 #include "apic.h" 20 21 22 /* Method Prototypes */ 23 static int apic_get_prio(void); 24 static status_t apic_set_hardware_timer(bigtime_t relativeTimeout); 25 static status_t apic_clear_hardware_timer(void); 26 static status_t apic_init(struct kernel_args *args); 27 28 static void *sApicPtr = NULL; 29 static uint32 sApicTicsPerSec = 0; 30 31 extern bool gUsingIOAPIC; 32 33 struct timer_info gAPICTimer = { 34 "APIC", 35 &apic_get_prio, 36 &apic_set_hardware_timer, 37 &apic_clear_hardware_timer, 38 &apic_init 39 }; 40 41 42 static int 43 apic_get_prio(void) 44 { 45 return 2; 46 } 47 48 49 static uint32 50 _apic_read(uint32 offset) 51 { 52 return *(volatile uint32 *)((char *)sApicPtr + offset); 53 } 54 55 56 static void 57 _apic_write(uint32 offset, uint32 data) 58 { 59 *(volatile uint32 *)((char *)sApicPtr + offset) = data; 60 } 61 62 63 static int32 64 apic_timer_interrupt(void *data) 65 { 66 // if we are not using the IO APIC we need to acknowledge the 67 // interrupt ourselfs 68 if (!gUsingIOAPIC) 69 _apic_write(APIC_EOI, 0); 70 71 return timer_interrupt(); 72 } 73 74 75 #define MIN_TIMEOUT 1 76 77 static status_t 78 apic_set_hardware_timer(bigtime_t relativeTimeout) 79 { 80 cpu_status state; 81 uint32 config; 82 uint32 ticks; 83 84 if (sApicPtr == NULL) 85 return B_ERROR; 86 87 if (relativeTimeout < MIN_TIMEOUT) 88 relativeTimeout = MIN_TIMEOUT; 89 90 // calculation should be ok, since it's going to be 64-bit 91 ticks = ((relativeTimeout * sApicTicsPerSec) / 1000000); 92 93 state = disable_interrupts(); 94 95 config = _apic_read(APIC_LVT_TIMER) | APIC_LVT_MASKED; // mask the timer 96 _apic_write(APIC_LVT_TIMER, config); 97 98 _apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the timer 99 100 config = _apic_read(APIC_LVT_TIMER) & ~APIC_LVT_MASKED; // unmask the timer 101 _apic_write(APIC_LVT_TIMER, config); 102 103 //TRACE_TIMER(("arch_smp_set_apic_timer: config 0x%lx, timeout %Ld, tics/sec %lu, tics %lu\n", 104 // config, relativeTimeout, sApicTicsPerSec, ticks)); 105 106 _apic_write(APIC_INITIAL_TIMER_COUNT, ticks); // start it up 107 108 restore_interrupts(state); 109 110 return B_OK; 111 } 112 113 114 static status_t 115 apic_clear_hardware_timer(void) 116 { 117 cpu_status state; 118 uint32 config; 119 120 if (sApicPtr == NULL) 121 return B_ERROR; 122 123 state = disable_interrupts(); 124 125 config = _apic_read(APIC_LVT_TIMER) | APIC_LVT_MASKED; // mask the timer 126 _apic_write(APIC_LVT_TIMER, config); 127 128 _apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the timer 129 130 restore_interrupts(state); 131 return B_OK; 132 } 133 134 135 static status_t 136 apic_init(struct kernel_args *args) 137 { 138 /* If we're in this method, arch_smp called the special init function. 139 Therefore, if we got here with sApicPtr NULL, there is no APIC! */ 140 if (sApicPtr == NULL) 141 return B_ERROR; 142 143 return B_OK; 144 } 145 146 147 status_t 148 apic_init_timer(struct kernel_args *args, int32 cpu) 149 { 150 uint32 config; 151 152 if (args->arch_args.apic == NULL) { 153 return B_ERROR; 154 } 155 156 /* This is in place of apic_preinit; if we're not already initialized, 157 register the interrupt handler and set the pointers */ 158 if (sApicPtr == NULL) { 159 sApicPtr = (void *)args->arch_args.apic; 160 sApicTicsPerSec = args->arch_args.apic_time_cv_factor; 161 install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE, &apic_timer_interrupt, NULL, B_NO_LOCK_VECTOR); 162 } 163 164 /* setup timer */ 165 config = _apic_read(APIC_LVT_TIMER) & APIC_LVT_TIMER_MASK; 166 config |= 0xfb | APIC_LVT_MASKED; // vector 0xfb, timer masked 167 _apic_write(APIC_LVT_TIMER, config); 168 169 _apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the clock 170 171 config = _apic_read(APIC_TIMER_DIVIDE_CONFIG) & 0xfffffff0; 172 config |= APIC_TIMER_DIVIDE_CONFIG_1; // clock division by 1 173 _apic_write(APIC_TIMER_DIVIDE_CONFIG, config); 174 175 return B_OK; 176 } 177