apic_timer.c revision 223b8c65a9498294013b99c37d5b9024433237ec
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* Copyright (c) 2010, Intel Corporation.
* All rights reserved.
*/
/*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/psm_common.h>
#include <sys/x86_archext.h>
#include <sys/archsystm.h>
#include <sys/machsystm.h>
#include <sys/apic_timer.h>
/*
*/
int apic_oneshot = 0;
uint_t apic_nsec_per_intr = 0;
static hrtime_t apic_nsec_max;
static void periodic_timer_enable(void);
static void periodic_timer_disable(void);
static void periodic_timer_reprogram(hrtime_t);
static void oneshot_timer_enable(void);
static void oneshot_timer_disable(void);
static void oneshot_timer_reprogram(hrtime_t);
static void deadline_timer_enable(void);
static void deadline_timer_disable(void);
static void deadline_timer_reprogram(hrtime_t);
extern int apic_clkvect;
extern uint32_t apic_divide_reg_init;
/*
* apic timer data structure
*/
typedef struct apic_timer {
int mode;
void (*apic_timer_enable_ops)(void);
void (*apic_timer_disable_ops)(void);
void (*apic_timer_reprogram_ops)(hrtime_t);
} apic_timer_t;
static apic_timer_t apic_timer;
/*
* apic timer initialization
*
* For the one-shot mode request case, the function returns the
* resolution (in nanoseconds) for the hardware timer interrupt.
* If one-shot mode capability is not available, the return value
* will be 0.
*/
int
apic_timer_init(int hertz)
{
uint_t apic_ticks = 0;
int ret, timer_mode;
static int firsttime = 1;
if (firsttime) {
/* first time calibrate on CPU0 only */
/* total number of PIT ticks corresponding to apic_ticks */
/*
* Determine the number of nanoseconds per APIC clock tick
* and then determine how many APIC ticks to interrupt at the
* desired frequency
* apic_ticks / (pitticks / PIT_HZ) = apic_ticks_per_s
* (apic_ticks * PIT_HZ) / pitticks = apic_ticks_per_s
* apic_ticks_per_ns = (apic_ticks * PIT_HZ) / (pitticks * 10^9)
* pic_ticks_per_SFns =
* (SF * apic_ticks * PIT_HZ) / (pitticks * 10^9)
*/
/* the interval timer initial count is 32 bit max */
firsttime = 0;
}
if (hertz == 0) {
/* requested one_shot */
/*
* return 0 if TSC is not supported.
*/
if (!tsc_gethrtime_enable)
return (0);
/*
* return 0 if one_shot is not preferred.
* here, APIC_TIMER_DEADLINE is also an one_shot mode.
*/
if ((apic_timer_preferred_mode != APIC_TIMER_MODE_ONESHOT) &&
return (0);
apic_oneshot = 1;
if ((apic_timer_preferred_mode == APIC_TIMER_MODE_DEADLINE) &&
} else {
}
} else {
/* periodic */
/* program the local APIC to interrupt at the given frequency */
apic_oneshot = 0;
}
/*
* initialize apic_timer data structure, install the timer ops
*/
switch (timer_mode) {
default:
/* FALLTHROUGH */
case APIC_TIMER_MODE_ONESHOT:
break;
case APIC_TIMER_MODE_PERIODIC:
break;
case APIC_TIMER_MODE_DEADLINE:
break;
}
return (ret);
}
/*
* periodic timer mode ops
*/
/* periodic timer enable */
static void
periodic_timer_enable(void)
{
}
/* periodic timer disable */
static void
periodic_timer_disable(void)
{
}
/* periodic timer reprogram */
static void
{
/* time is the interval for periodic mode */
if (ticks < apic_min_timer_ticks)
}
/*
* oneshot timer mode ops
*/
/* oneshot timer enable */
static void
oneshot_timer_enable(void)
{
(apic_clkvect + APIC_BASE_VECT));
}
/* oneshot timer disable */
static void
oneshot_timer_disable(void)
{
}
/* oneshot timer reprogram */
static void
{
if (delta <= 0) {
/*
* requested to generate an interrupt in the past
* generate an interrupt as soon as possible
*/
} else if (delta > apic_nsec_max) {
/*
* requested to generate an interrupt at a time
* further than what we are capable of. Set to max
* the hardware can handle
*/
ticks = APIC_MAXVAL;
#ifdef DEBUG
" %lld too far in future, current time"
#endif
} else {
}
if (ticks < apic_min_timer_ticks)
}
/*
* deadline timer mode ops
*/
/* deadline timer enable */
static void
deadline_timer_enable(void)
{
/*
* Now we have to serialize this per the SDM. That is to
* say, the above enabling can race in the pipeline with
* changes to the MSR. We need to make sure the above
* operation is complete before we proceed to reprogram
* the deadline value in reprogram(). The algorithm
* recommended by the Intel SDM 3A in 10.5.1.4 is:
*
* a) write a big value to the deadline register
* b) read the register back
* c) if it reads zero, go back to a and try again
*/
do {
/* write a really big value */
} while (ticks == 0);
}
/* deadline timer disable */
static void
deadline_timer_disable(void)
{
}
/* deadline timer reprogram */
static void
{
/*
* Note that this entire routine is called with
* CBE_HIGH_PIL, so we needn't worry about preemption.
*/
/* The unscalehrtime wants unsigned values. */
/* Now we shouldn't be interrupted, we can set the deadline */
}
/*
* This function will reprogram the timer.
*
* When in oneshot mode the argument is the absolute time in future to
* generate the interrupt at.
*
* When in periodic mode, the argument is the interval at which the
* interrupts should be generated. There is no need to support the periodic
* mode timer change at this time.
*/
void
{
/*
* we should be Called from high PIL context (CBE_HIGH_PIL),
* so kpreempt is disabled.
*/
}
/*
* This function will enable timer interrupts.
*/
void
apic_timer_enable(void)
{
/*
* we should be Called from high PIL context (CBE_HIGH_PIL),
* so kpreempt is disabled.
*/
}
/*
* This function will disable timer interrupts.
*/
void
apic_timer_disable(void)
{
/*
* we should be Called from high PIL context (CBE_HIGH_PIL),
* so kpreempt is disabled.
*/
}
/*
* Set timer far into the future and return timer
* current count in nanoseconds.
*/
apic_timer_stop_count(void)
{
int enable_val, count_val;
/*
* Should be called with interrupts disabled.
*/
ASSERT(!interrupts_enabled());
return (ns_val);
}
/*
* Reprogram timer after Deep C-State.
*/
void
{
}