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_priority(); 24 static status_t apic_set_hardware_timer(bigtime_t relativeTimeout); 25 static status_t apic_clear_hardware_timer(); 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_priority, 36 &apic_set_hardware_timer, 37 &apic_clear_hardware_timer, 38 &apic_init 39 }; 40 41 42 static int 43 apic_get_priority() 44 { 45 return 3; 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 if (sApicPtr == NULL) 81 return B_ERROR; 82 83 if (relativeTimeout < MIN_TIMEOUT) 84 relativeTimeout = MIN_TIMEOUT; 85 86 // calculation should be ok, since it's going to be 64-bit 87 uint32 ticks = ((relativeTimeout * sApicTicsPerSec) / 1000000); 88 89 cpu_status state = disable_interrupts(); 90 91 uint32 config = _apic_read(APIC_LVT_TIMER) | APIC_LVT_MASKED; // mask the timer 92 _apic_write(APIC_LVT_TIMER, config); 93 94 _apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the timer 95 96 config = _apic_read(APIC_LVT_TIMER) & ~APIC_LVT_MASKED; // unmask the timer 97 _apic_write(APIC_LVT_TIMER, config); 98 99 //TRACE_TIMER(("arch_smp_set_apic_timer: config 0x%lx, timeout %Ld, tics/sec %lu, tics %lu\n", 100 // config, relativeTimeout, sApicTicsPerSec, ticks)); 101 102 _apic_write(APIC_INITIAL_TIMER_COUNT, ticks); // start it up 103 104 restore_interrupts(state); 105 106 return B_OK; 107 } 108 109 110 static status_t 111 apic_clear_hardware_timer() 112 { 113 if (sApicPtr == NULL) 114 return B_ERROR; 115 116 cpu_status state = disable_interrupts(); 117 118 uint32 config = _apic_read(APIC_LVT_TIMER) | APIC_LVT_MASKED; 119 // mask the timer 120 _apic_write(APIC_LVT_TIMER, config); 121 122 _apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the timer 123 124 restore_interrupts(state); 125 return B_OK; 126 } 127 128 129 static status_t 130 apic_init(struct kernel_args *args) 131 { 132 /* If we're in this method, arch_smp called the special init function. 133 Therefore, if we got here with sApicPtr NULL, there is no APIC! */ 134 if (sApicPtr == NULL) 135 return B_ERROR; 136 137 return B_OK; 138 } 139 140 141 status_t 142 apic_init_timer(struct kernel_args *args, int32 cpu) 143 { 144 if (args->arch_args.apic == NULL) 145 return B_ERROR; 146 147 /* This is in place of apic_preinit; if we're not already initialized, 148 register the interrupt handler and set the pointers */ 149 if (sApicPtr == NULL) { 150 sApicPtr = (void *)args->arch_args.apic; 151 sApicTicsPerSec = args->arch_args.apic_time_cv_factor; 152 install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE, 153 &apic_timer_interrupt, NULL, B_NO_LOCK_VECTOR); 154 } 155 156 /* setup timer */ 157 uint32 config = _apic_read(APIC_LVT_TIMER) & APIC_LVT_TIMER_MASK; 158 config |= 0xfb | APIC_LVT_MASKED; // vector 0xfb, timer masked 159 _apic_write(APIC_LVT_TIMER, config); 160 161 _apic_write(APIC_INITIAL_TIMER_COUNT, 0); // zero out the clock 162 163 config = _apic_read(APIC_TIMER_DIVIDE_CONFIG) & 0xfffffff0; 164 config |= APIC_TIMER_DIVIDE_CONFIG_1; // clock division by 1 165 _apic_write(APIC_TIMER_DIVIDE_CONFIG, config); 166 167 return B_OK; 168 } 169