1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 /* 25 * Copyright (c) 2010, Intel Corporation. 26 * All rights reserved. 27 */ 28 29 #include <sys/time.h> 30 #include <sys/psm.h> 31 #include <sys/psm_common.h> 32 #include <sys/apic.h> 33 #include <sys/pit.h> 34 #include <sys/x86_archext.h> 35 #include <sys/archsystm.h> 36 #include <sys/machsystm.h> 37 #include <sys/cpuvar.h> 38 #include <sys/clock.h> 39 #include <sys/apic_timer.h> 40 41 /* 42 * preferred apic timer mode, allow tuning from the /etc/system file. 43 */ 44 int apic_timer_preferred_mode = APIC_TIMER_MODE_DEADLINE; 45 46 int apic_oneshot = 0; 47 uint_t apic_hertz_count; 48 uint_t apic_nsec_per_intr = 0; 49 uint64_t apic_ticks_per_SFnsecs; /* # of ticks in SF nsecs */ 50 51 static int apic_min_timer_ticks = 1; /* minimum timer tick */ 52 static hrtime_t apic_nsec_max; 53 54 static void periodic_timer_enable(void); 55 static void periodic_timer_disable(void); 56 static void periodic_timer_reprogram(hrtime_t); 57 static void oneshot_timer_enable(void); 58 static void oneshot_timer_disable(void); 59 static void oneshot_timer_reprogram(hrtime_t); 60 static void deadline_timer_enable(void); 61 static void deadline_timer_disable(void); 62 static void deadline_timer_reprogram(hrtime_t); 63 64 extern int apic_clkvect; 65 extern uint32_t apic_divide_reg_init; 66 67 /* 68 * apic timer data structure 69 */ 70 typedef struct apic_timer { 71 int mode; 72 void (*apic_timer_enable_ops)(void); 73 void (*apic_timer_disable_ops)(void); 74 void (*apic_timer_reprogram_ops)(hrtime_t); 75 } apic_timer_t; 76 77 static apic_timer_t apic_timer; 78 79 /* 80 * apic timer initialization 81 * 82 * For the one-shot mode request case, the function returns the 83 * resolution (in nanoseconds) for the hardware timer interrupt. 84 * If one-shot mode capability is not available, the return value 85 * will be 0. 86 */ 87 int 88 apic_timer_init(int hertz) 89 { 90 uint_t apic_ticks = 0; 91 uint_t pit_ticks; 92 int ret, timer_mode; 93 uint16_t pit_ticks_adj; 94 static int firsttime = 1; 95 96 if (firsttime) { 97 /* first time calibrate on CPU0 only */ 98 99 apic_reg_ops->apic_write(APIC_DIVIDE_REG, apic_divide_reg_init); 100 apic_reg_ops->apic_write(APIC_INIT_COUNT, APIC_MAXVAL); 101 apic_ticks = apic_calibrate(apicadr, &pit_ticks_adj); 102 103 /* total number of PIT ticks corresponding to apic_ticks */ 104 pit_ticks = APIC_TIME_COUNT + pit_ticks_adj; 105 106 /* 107 * Determine the number of nanoseconds per APIC clock tick 108 * and then determine how many APIC ticks to interrupt at the 109 * desired frequency 110 * apic_ticks / (pitticks / PIT_HZ) = apic_ticks_per_s 111 * (apic_ticks * PIT_HZ) / pitticks = apic_ticks_per_s 112 * apic_ticks_per_ns = (apic_ticks * PIT_HZ) / (pitticks * 10^9) 113 * pic_ticks_per_SFns = 114 * (SF * apic_ticks * PIT_HZ) / (pitticks * 10^9) 115 */ 116 apic_ticks_per_SFnsecs = ((SF * apic_ticks * PIT_HZ) / 117 ((uint64_t)pit_ticks * NANOSEC)); 118 119 /* the interval timer initial count is 32 bit max */ 120 apic_nsec_max = APIC_TICKS_TO_NSECS(APIC_MAXVAL); 121 firsttime = 0; 122 } 123 124 if (hertz == 0) { 125 /* requested one_shot */ 126 127 /* 128 * return 0 if TSC is not supported. 129 */ 130 if (!tsc_gethrtime_enable) 131 return (0); 132 /* 133 * return 0 if one_shot is not preferred. 134 * here, APIC_TIMER_DEADLINE is also an one_shot mode. 135 */ 136 if ((apic_timer_preferred_mode != APIC_TIMER_MODE_ONESHOT) && 137 (apic_timer_preferred_mode != APIC_TIMER_MODE_DEADLINE)) 138 return (0); 139 140 apic_oneshot = 1; 141 ret = (int)APIC_TICKS_TO_NSECS(1); 142 if ((apic_timer_preferred_mode == APIC_TIMER_MODE_DEADLINE) && 143 cpuid_deadline_tsc_supported()) { 144 timer_mode = APIC_TIMER_MODE_DEADLINE; 145 } else { 146 timer_mode = APIC_TIMER_MODE_ONESHOT; 147 } 148 } else { 149 /* periodic */ 150 apic_nsec_per_intr = NANOSEC / hertz; 151 apic_hertz_count = APIC_NSECS_TO_TICKS(apic_nsec_per_intr); 152 153 /* program the local APIC to interrupt at the given frequency */ 154 apic_reg_ops->apic_write(APIC_INIT_COUNT, apic_hertz_count); 155 apic_reg_ops->apic_write(APIC_LOCAL_TIMER, 156 (apic_clkvect + APIC_BASE_VECT) | AV_PERIODIC); 157 apic_oneshot = 0; 158 timer_mode = APIC_TIMER_MODE_PERIODIC; 159 ret = NANOSEC / hertz; 160 } 161 162 /* 163 * initialize apic_timer data structure, install the timer ops 164 */ 165 apic_timer.mode = timer_mode; 166 switch (timer_mode) { 167 default: 168 /* FALLTHROUGH */ 169 case APIC_TIMER_MODE_ONESHOT: 170 apic_timer.apic_timer_enable_ops = oneshot_timer_enable; 171 apic_timer.apic_timer_disable_ops = oneshot_timer_disable; 172 apic_timer.apic_timer_reprogram_ops = oneshot_timer_reprogram; 173 break; 174 175 case APIC_TIMER_MODE_PERIODIC: 176 apic_timer.apic_timer_enable_ops = periodic_timer_enable; 177 apic_timer.apic_timer_disable_ops = periodic_timer_disable; 178 apic_timer.apic_timer_reprogram_ops = periodic_timer_reprogram; 179 break; 180 181 case APIC_TIMER_MODE_DEADLINE: 182 apic_timer.apic_timer_enable_ops = deadline_timer_enable; 183 apic_timer.apic_timer_disable_ops = deadline_timer_disable; 184 apic_timer.apic_timer_reprogram_ops = deadline_timer_reprogram; 185 break; 186 } 187 188 return (ret); 189 } 190 191 /* 192 * periodic timer mode ops 193 */ 194 /* periodic timer enable */ 195 static void 196 periodic_timer_enable(void) 197 { 198 apic_reg_ops->apic_write(APIC_LOCAL_TIMER, 199 (apic_clkvect + APIC_BASE_VECT) | AV_PERIODIC); 200 } 201 202 /* periodic timer disable */ 203 static void 204 periodic_timer_disable(void) 205 { 206 apic_reg_ops->apic_write(APIC_LOCAL_TIMER, 207 (apic_clkvect + APIC_BASE_VECT) | AV_MASK); 208 } 209 210 /* periodic timer reprogram */ 211 static void 212 periodic_timer_reprogram(hrtime_t time) 213 { 214 uint_t ticks; 215 /* time is the interval for periodic mode */ 216 ticks = APIC_NSECS_TO_TICKS(time); 217 218 if (ticks < apic_min_timer_ticks) 219 ticks = apic_min_timer_ticks; 220 221 apic_reg_ops->apic_write(APIC_INIT_COUNT, ticks); 222 } 223 224 /* 225 * oneshot timer mode ops 226 */ 227 /* oneshot timer enable */ 228 static void 229 oneshot_timer_enable(void) 230 { 231 apic_reg_ops->apic_write(APIC_LOCAL_TIMER, 232 (apic_clkvect + APIC_BASE_VECT)); 233 } 234 235 /* oneshot timer disable */ 236 static void 237 oneshot_timer_disable(void) 238 { 239 apic_reg_ops->apic_write(APIC_LOCAL_TIMER, 240 (apic_clkvect + APIC_BASE_VECT) | AV_MASK); 241 } 242 243 /* oneshot timer reprogram */ 244 static void 245 oneshot_timer_reprogram(hrtime_t time) 246 { 247 hrtime_t now; 248 int64_t delta; 249 uint_t ticks; 250 251 now = gethrtime(); 252 delta = time - now; 253 254 if (delta <= 0) { 255 /* 256 * requested to generate an interrupt in the past 257 * generate an interrupt as soon as possible 258 */ 259 ticks = apic_min_timer_ticks; 260 } else if (delta > apic_nsec_max) { 261 /* 262 * requested to generate an interrupt at a time 263 * further than what we are capable of. Set to max 264 * the hardware can handle 265 */ 266 ticks = APIC_MAXVAL; 267 #ifdef DEBUG 268 cmn_err(CE_CONT, "apic_timer_reprogram, request at" 269 " %lld too far in future, current time" 270 " %lld \n", time, now); 271 #endif 272 } else { 273 ticks = APIC_NSECS_TO_TICKS(delta); 274 } 275 276 if (ticks < apic_min_timer_ticks) 277 ticks = apic_min_timer_ticks; 278 279 apic_reg_ops->apic_write(APIC_INIT_COUNT, ticks); 280 } 281 282 /* 283 * deadline timer mode ops 284 */ 285 /* deadline timer enable */ 286 static void 287 deadline_timer_enable(void) 288 { 289 apic_reg_ops->apic_write(APIC_LOCAL_TIMER, 290 (apic_clkvect + APIC_BASE_VECT) | AV_DEADLINE); 291 } 292 293 /* deadline timer disable */ 294 static void 295 deadline_timer_disable(void) 296 { 297 apic_reg_ops->apic_write(APIC_LOCAL_TIMER, 298 (apic_clkvect + APIC_BASE_VECT) | AV_MASK); 299 } 300 301 /* deadline timer reprogram */ 302 static void 303 deadline_timer_reprogram(hrtime_t time) 304 { 305 uint64_t ticks; 306 307 if (time <= 0) { 308 /* 309 * generate an immediate interrupt 310 */ 311 ticks = (uint64_t)tsc_read(); 312 } else { 313 ticks = unscalehrtime(time); 314 } 315 316 wrmsr(IA32_DEADLINE_TSC_MSR, ticks); 317 } 318 319 /* 320 * This function will reprogram the timer. 321 * 322 * When in oneshot mode the argument is the absolute time in future to 323 * generate the interrupt at. 324 * 325 * When in periodic mode, the argument is the interval at which the 326 * interrupts should be generated. There is no need to support the periodic 327 * mode timer change at this time. 328 */ 329 void 330 apic_timer_reprogram(hrtime_t time) 331 { 332 /* 333 * we should be Called from high PIL context (CBE_HIGH_PIL), 334 * so kpreempt is disabled. 335 */ 336 apic_timer.apic_timer_reprogram_ops(time); 337 } 338 339 /* 340 * This function will enable timer interrupts. 341 */ 342 void 343 apic_timer_enable(void) 344 { 345 /* 346 * we should be Called from high PIL context (CBE_HIGH_PIL), 347 * so kpreempt is disabled. 348 */ 349 apic_timer.apic_timer_enable_ops(); 350 } 351 352 /* 353 * This function will disable timer interrupts. 354 */ 355 void 356 apic_timer_disable(void) 357 { 358 /* 359 * we should be Called from high PIL context (CBE_HIGH_PIL), 360 * so kpreempt is disabled. 361 */ 362 apic_timer.apic_timer_disable_ops(); 363 } 364 365 /* 366 * Set timer far into the future and return timer 367 * current count in nanoseconds. 368 */ 369 hrtime_t 370 apic_timer_stop_count(void) 371 { 372 hrtime_t ns_val; 373 int enable_val, count_val; 374 375 /* 376 * Should be called with interrupts disabled. 377 */ 378 ASSERT(!interrupts_enabled()); 379 380 enable_val = apic_reg_ops->apic_read(APIC_LOCAL_TIMER); 381 if ((enable_val & AV_MASK) == AV_MASK) 382 return ((hrtime_t)-1); /* timer is disabled */ 383 384 count_val = apic_reg_ops->apic_read(APIC_CURR_COUNT); 385 ns_val = APIC_TICKS_TO_NSECS(count_val); 386 387 apic_reg_ops->apic_write(APIC_INIT_COUNT, APIC_MAXVAL); 388 389 return (ns_val); 390 } 391 392 /* 393 * Reprogram timer after Deep C-State. 394 */ 395 void 396 apic_timer_restart(hrtime_t time) 397 { 398 apic_timer_reprogram(time); 399 } 400