x86/HPET: cleanup

- separate init and resume code paths (so that the [larger] init parts
  can go init .init.* sections)
- drop the separate legacy_hpet_event object, as we can easily re-use
  the first slot of hpet_events[] for that purpose (the whole array is
  otherwise unused when the legacy code is being used)
- use section placement attributes where reasonable

Signed-off-by: Jan Beulich <jbeulich@novell.com>
Acked-by: Wei Gang <gang.wei@intel.com>
This commit is contained in:
Jan Beulich committed 2011-03-12 13:19:34 +00:00
1 parent 8fb9870830
commit b927089900
3 files changed
+189 -152

No files matched your search

+158 -122
View File
@@ -27,6 +27,8 @@
#define HPET_EVT_USED (1 << HPET_EVT_USED_BIT)
#define HPET_EVT_DISABLE_BIT 1
#define HPET_EVT_DISABLE (1 << HPET_EVT_DISABLE_BIT)
#define HPET_EVT_LEGACY_BIT 2
#define HPET_EVT_LEGACY (1 << HPET_EVT_LEGACY_BIT)
struct hpet_event_channel
{
@@ -54,18 +56,18 @@ struct hpet_event_channel
int irq; /* msi irq */
unsigned int flags; /* HPET_EVT_x */
} __cacheline_aligned;
static struct hpet_event_channel legacy_hpet_event;
static struct hpet_event_channel hpet_events[MAX_HPET_NUM] =
{ [0 ... MAX_HPET_NUM-1].irq = -1 };
static unsigned int num_hpets_used; /* msi hpet channels used for broadcast */
/* msi hpet channels used for broadcast */
static unsigned int __read_mostly num_hpets_used;
DEFINE_PER_CPU(struct hpet_event_channel *, cpu_bc_channel);
static int *irq_channel;
static int *__read_mostly irq_channel;
#define irq_to_channel(irq) irq_channel[irq]
unsigned long hpet_address;
unsigned long __read_mostly hpet_address;
/*
* force_hpet_broadcast: by default legacy hpet broadcast will be stopped
@@ -362,58 +364,50 @@ static hw_irq_controller hpet_msi_type = {
.set_affinity = hpet_msi_set_affinity,
};
static int hpet_setup_msi_irq(unsigned int irq)
static void __hpet_setup_msi_irq(unsigned int irq)
{
int ret;
struct msi_msg msg;
struct hpet_event_channel *ch = &hpet_events[irq_to_channel(irq)];
irq_desc_t *desc = irq_to_desc(irq);
if ( desc->handler == &no_irq_type )
{
desc->handler = &hpet_msi_type;
ret = request_irq(irq, hpet_interrupt_handler,
0, "HPET", ch);
if ( ret < 0 )
return ret;
}
else if ( desc->handler != &hpet_msi_type )
{
return -EINVAL;
}
msi_compose_msg(NULL, irq, &msg);
hpet_msi_write(irq, &msg);
}
static int __init hpet_setup_msi_irq(unsigned int irq)
{
int ret;
irq_desc_t *desc = irq_to_desc(irq);
desc->handler = &hpet_msi_type;
ret = request_irq(irq, hpet_interrupt_handler,
0, "HPET", hpet_events + irq_channel[irq]);
if ( ret < 0 )
return ret;
__hpet_setup_msi_irq(irq);
return 0;
}
static int hpet_assign_irq(struct hpet_event_channel *ch)
static int __init hpet_assign_irq(unsigned int idx)
{
int irq = ch->irq;
int irq;
if ( irq < 0 )
{
if ( (irq = create_irq()) < 0 )
return irq;
if ( (irq = create_irq()) < 0 )
return irq;
irq_channel[irq] = ch - &hpet_events[0];
ch->irq = irq;
}
irq_channel[irq] = idx;
/* hpet_setup_msi_irq should also be called for S3 resuming */
if ( hpet_setup_msi_irq(irq) )
{
destroy_irq(irq);
irq_channel[irq] = -1;
ch->irq = -1;
return -EINVAL;
}
return 0;
return irq;
}
static int hpet_fsb_cap_lookup(void)
static int __init hpet_fsb_cap_lookup(void)
{
unsigned int id;
unsigned int num_chs, num_chs_used;
@@ -445,7 +439,7 @@ static int hpet_fsb_cap_lookup(void)
ch->flags = 0;
ch->idx = i;
if ( hpet_assign_irq(ch) )
if ( (ch->irq = hpet_assign_irq(num_chs_used)) < 0 )
continue;
num_chs_used++;
@@ -468,7 +462,7 @@ static struct hpet_event_channel *hpet_get_channel(int cpu)
struct hpet_event_channel *ch;
if ( num_hpets_used == 0 )
return &legacy_hpet_event;
return hpet_events;
spin_lock(&next_lock);
next = next_channel = (next_channel + 1) % num_hpets_used;
@@ -536,7 +530,7 @@ static void hpet_detach_channel(int cpu, struct hpet_event_channel *ch)
#include <asm/mc146818rtc.h>
void (*pv_rtc_handler)(unsigned int port, uint8_t value);
void (*__read_mostly pv_rtc_handler)(unsigned int port, uint8_t value);
static void handle_rtc_once(unsigned int port, uint8_t value)
{
@@ -559,87 +553,114 @@ static void handle_rtc_once(unsigned int port, uint8_t value)
}
}
void hpet_broadcast_init(void)
void __init hpet_broadcast_init(void)
{
u64 hpet_rate;
u64 hpet_rate = hpet_setup();
u32 hpet_id, cfg;
int i;
unsigned int i, n;
if ( irq_channel == NULL )
{
irq_channel = xmalloc_array(int, nr_irqs);
BUG_ON(irq_channel == NULL);
for ( i = 0; i < nr_irqs; i++ )
irq_channel[i] = -1;
}
hpet_rate = hpet_setup();
if ( hpet_rate == 0 )
return;
irq_channel = xmalloc_array(int, nr_irqs);
BUG_ON(irq_channel == NULL);
for ( i = 0; i < nr_irqs; i++ )
irq_channel[i] = -1;
cfg = hpet_read32(HPET_CFG);
num_hpets_used = hpet_fsb_cap_lookup();
if ( num_hpets_used > 0 )
{
/* Stop HPET legacy interrupts */
cfg = hpet_read32(HPET_CFG);
cfg &= ~HPET_CFG_LEGACY;
hpet_write32(cfg, HPET_CFG);
n = num_hpets_used;
}
else
{
xfree(irq_channel);
irq_channel = NULL;
for ( i = 0; i < num_hpets_used; i++ )
{
/* set HPET Tn as oneshot */
cfg = hpet_read32(HPET_Tn_CFG(hpet_events[i].idx));
cfg &= ~HPET_TN_PERIODIC;
cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
hpet_write32(cfg, HPET_Tn_CFG(hpet_events[i].idx));
hpet_events[i].mult = div_sc((unsigned long)hpet_rate,
1000000000ul, 32);
hpet_events[i].shift = 32;
hpet_events[i].next_event = STIME_MAX;
spin_lock_init(&hpet_events[i].lock);
rwlock_init(&hpet_events[i].cpumask_lock);
wmb();
hpet_events[i].event_handler = handle_hpet_broadcast;
}
hpet_id = hpet_read32(HPET_ID);
if ( !(hpet_id & HPET_ID_LEGSUP) )
return;
return;
/* Start HPET legacy interrupts */
cfg |= HPET_CFG_LEGACY;
n = 1;
hpet_events->idx = 0;
if ( !force_hpet_broadcast )
pv_rtc_handler = handle_rtc_once;
}
if ( legacy_hpet_event.flags & HPET_EVT_DISABLE )
return;
hpet_id = hpet_read32(HPET_ID);
if ( !(hpet_id & HPET_ID_LEGSUP) )
return;
/* Start HPET legacy interrupts */
cfg = hpet_read32(HPET_CFG);
cfg |= HPET_CFG_LEGACY;
hpet_write32(cfg, HPET_CFG);
/* set HPET T0 as oneshot */
cfg = hpet_read32(HPET_T0_CFG);
cfg &= ~HPET_TN_PERIODIC;
cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
hpet_write32(cfg, HPET_T0_CFG);
for ( i = 0; i < n; i++ )
{
/* set HPET Tn as oneshot */
cfg = hpet_read32(HPET_Tn_CFG(hpet_events[i].idx));
cfg &= ~HPET_TN_PERIODIC;
cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
hpet_write32(cfg, HPET_Tn_CFG(hpet_events[i].idx));
/*
* The period is a femto seconds value. We need to calculate the scaled
* math multiplication factor for nanosecond to hpet tick conversion.
*/
legacy_hpet_event.mult = div_sc((unsigned long)hpet_rate, 1000000000ul, 32);
legacy_hpet_event.shift = 32;
legacy_hpet_event.next_event = STIME_MAX;
legacy_hpet_event.idx = 0;
legacy_hpet_event.flags = 0;
spin_lock_init(&legacy_hpet_event.lock);
rwlock_init(&legacy_hpet_event.cpumask_lock);
wmb();
legacy_hpet_event.event_handler = handle_hpet_broadcast;
/*
* The period is a femto seconds value. We need to calculate the scaled
* math multiplication factor for nanosecond to hpet tick conversion.
*/
hpet_events[i].mult = div_sc((unsigned long)hpet_rate,
1000000000ul, 32);
hpet_events[i].shift = 32;
hpet_events[i].next_event = STIME_MAX;
spin_lock_init(&hpet_events[i].lock);
rwlock_init(&hpet_events[i].cpumask_lock);
wmb();
hpet_events[i].event_handler = handle_hpet_broadcast;
}
if ( !force_hpet_broadcast )
pv_rtc_handler = handle_rtc_once;
if ( !num_hpets_used )
hpet_events->flags = HPET_EVT_LEGACY;
}
void hpet_broadcast_resume(void)
{
u32 cfg;
unsigned int i, n;
hpet_resume();
cfg = hpet_read32(HPET_CFG);
if ( num_hpets_used > 0 )
{
/* Stop HPET legacy interrupts */
cfg &= ~HPET_CFG_LEGACY;
n = num_hpets_used;
}
else if ( hpet_events->flags & HPET_EVT_DISABLE )
return;
else
{
/* Start HPET legacy interrupts */
cfg |= HPET_CFG_LEGACY;
n = 1;
}
hpet_write32(cfg, HPET_CFG);
for ( i = 0; i < n; i++ )
{
if ( hpet_events[i].irq >= 0 )
__hpet_setup_msi_irq(hpet_events[i].irq);
/* set HPET Tn as oneshot */
cfg = hpet_read32(HPET_Tn_CFG(hpet_events[i].idx));
cfg &= ~HPET_TN_PERIODIC;
cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
hpet_write32(cfg, HPET_Tn_CFG(hpet_events[i].idx));
hpet_events[i].next_event = STIME_MAX;
}
}
void hpet_disable_legacy_broadcast(void)
@@ -647,21 +668,24 @@ void hpet_disable_legacy_broadcast(void)
u32 cfg;
unsigned long flags;
spin_lock_irqsave(&legacy_hpet_event.lock, flags);
if ( !(hpet_events->flags & HPET_EVT_LEGACY) )
return;
legacy_hpet_event.flags |= HPET_EVT_DISABLE;
spin_lock_irqsave(&hpet_events->lock, flags);
hpet_events->flags |= HPET_EVT_DISABLE;
/* disable HPET T0 */
cfg = hpet_read32(HPET_T0_CFG);
cfg = hpet_read32(HPET_Tn_CFG(0));
cfg &= ~HPET_TN_ENABLE;
hpet_write32(cfg, HPET_T0_CFG);
hpet_write32(cfg, HPET_Tn_CFG(0));
/* Stop HPET legacy interrupts */
cfg = hpet_read32(HPET_CFG);
cfg &= ~HPET_CFG_LEGACY;
hpet_write32(cfg, HPET_CFG);
spin_unlock_irqrestore(&legacy_hpet_event.lock, flags);
spin_unlock_irqrestore(&hpet_events->lock, flags);
smp_send_event_check_mask(&cpu_online_map);
}
@@ -679,7 +703,7 @@ void hpet_broadcast_enter(void)
ASSERT(!local_irq_is_enabled());
if ( ch != &legacy_hpet_event )
if ( !(ch->flags & HPET_EVT_LEGACY) )
{
spin_lock(&ch->lock);
hpet_attach_channel(cpu, ch);
@@ -717,7 +741,7 @@ void hpet_broadcast_exit(void)
cpu_clear(cpu, ch->cpumask);
read_unlock_irq(&ch->cpumask_lock);
if ( ch != &legacy_hpet_event )
if ( !(ch->flags & HPET_EVT_LEGACY) )
{
spin_lock_irq(&ch->lock);
hpet_detach_channel(cpu, ch);
@@ -727,7 +751,7 @@ void hpet_broadcast_exit(void)
int hpet_broadcast_is_available(void)
{
return (legacy_hpet_event.event_handler == handle_hpet_broadcast
return ((hpet_events->flags & HPET_EVT_LEGACY)
|| num_hpets_used > 0);
}
@@ -735,22 +759,20 @@ int hpet_legacy_irq_tick(void)
{
this_cpu(irq_count)--;
if ( !legacy_hpet_event.event_handler )
if ( (hpet_events->flags & (HPET_EVT_DISABLE|HPET_EVT_LEGACY)) !=
HPET_EVT_LEGACY )
return 0;
legacy_hpet_event.event_handler(&legacy_hpet_event);
hpet_events->event_handler(hpet_events);
return 1;
}
u64 hpet_setup(void)
u64 __init hpet_setup(void)
{
static u64 hpet_rate;
static u32 system_reset_latch;
u32 hpet_id, hpet_period, cfg;
int i;
static u64 __initdata hpet_rate;
u32 hpet_id, hpet_period;
if ( system_reset_latch == system_reset_counter )
if ( hpet_rate )
return hpet_rate;
system_reset_latch = system_reset_counter;
if ( hpet_address == 0 )
return 0;
@@ -772,10 +794,29 @@ u64 hpet_setup(void)
return 0;
}
hpet_resume();
hpet_rate = 1000000000000000ULL; /* 10^15 */
(void)do_div(hpet_rate, hpet_period);
return hpet_rate;
}
void hpet_resume(void)
{
static u32 system_reset_latch;
u32 hpet_id, cfg;
unsigned int i;
if ( system_reset_latch == system_reset_counter )
return;
system_reset_latch = system_reset_counter;
cfg = hpet_read32(HPET_CFG);
cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY);
hpet_write32(cfg, HPET_CFG);
hpet_id = hpet_read32(HPET_ID);
for ( i = 0; i <= ((hpet_id >> 8) & 31); i++ )
{
cfg = hpet_read32(HPET_Tn_CFG(i));
@@ -786,9 +827,4 @@ u64 hpet_setup(void)
cfg = hpet_read32(HPET_CFG);
cfg |= HPET_CFG_ENABLE;
hpet_write32(cfg, HPET_CFG);
hpet_rate = 1000000000000000ULL; /* 10^15 */
(void)do_div(hpet_rate, hpet_period);
return hpet_rate;
}
+26 -17
View File
@@ -21,6 +21,7 @@
#include <xen/smp.h>
#include <xen/irq.h>
#include <xen/softirq.h>
#include <xen/symbols.h>
#include <xen/keyhandler.h>
#include <xen/guest_access.h>
#include <asm/io.h>
@@ -374,10 +375,7 @@ static int __init init_hpet(struct platform_timesource *pts)
static void resume_hpet(struct platform_timesource *pts)
{
u64 hpet_rate = hpet_setup();
BUG_ON(hpet_rate == 0);
pts->frequency = hpet_rate;
hpet_resume();
}
static struct platform_timesource __initdata plt_hpet =
@@ -1440,10 +1438,12 @@ void __init early_time_init(void)
}
/* keep pit enabled for pit_broadcast working while cpuidle enabled */
static int disable_pit_irq(void)
static int _disable_pit_irq(void(*hpet_broadcast_setup)(void))
{
int ret = 1;
if ( using_pit || !cpu_has_apic )
return 0;
return -1;
/*
* If we do not rely on PIT CH0 then we can use HPET for one-shot timer
@@ -1453,20 +1453,16 @@ static int disable_pit_irq(void)
*/
if ( xen_cpuidle && !boot_cpu_has(X86_FEATURE_ARAT) )
{
hpet_broadcast_init();
hpet_broadcast_setup();
if ( !hpet_broadcast_is_available() )
{
if ( xen_cpuidle == -1 )
if ( xen_cpuidle > 0 )
{
xen_cpuidle = 0;
printk("CPUIDLE: disabled due to no HPET. "
"Force enable with 'cpuidle'.\n");
}
else
{
printk("HPET broadcast init failed, turn to PIT broadcast.\n");
return 0;
print_symbol("%s() failed, turning to PIT broadcast\n",
(unsigned long)hpet_broadcast_setup);
return -1;
}
ret = 0;
}
}
@@ -1475,6 +1471,18 @@ static int disable_pit_irq(void)
outb_p(0, PIT_CH0);
outb_p(0, PIT_CH0);
return ret;
}
static int __init disable_pit_irq(void)
{
if ( !_disable_pit_irq(hpet_broadcast_init) )
{
xen_cpuidle = 0;
printk("CPUIDLE: disabled due to no HPET. "
"Force enable with 'cpuidle'.\n");
}
return 0;
}
__initcall(disable_pit_irq);
@@ -1541,7 +1549,8 @@ int time_resume(void)
resume_platform_timer();
disable_pit_irq();
if ( !_disable_pit_irq(hpet_broadcast_resume) )
BUG();
init_percpu_time();
+5 -13
View File
@@ -14,19 +14,9 @@
#define HPET_CFG 0x010
#define HPET_STATUS 0x020
#define HPET_COUNTER 0x0f0
#define HPET_T0_CFG 0x100
#define HPET_T0_CMP 0x108
#define HPET_T0_ROUTE 0x110
#define HPET_T1_CFG 0x120
#define HPET_T1_CMP 0x128
#define HPET_T1_ROUTE 0x130
#define HPET_T2_CFG 0x140
#define HPET_T2_CMP 0x148
#define HPET_T2_ROUTE 0x150
#define HPET_Tn_CFG(n) (HPET_T0_CFG + n * 0x20)
#define HPET_Tn_CMP(n) (HPET_T0_CMP + n * 0x20)
#define HPET_Tn_ROUTE(n) (HPET_T0_ROUTE + n * 0x20)
#define HPET_Tn_CFG(n) (0x100 + (n) * 0x20)
#define HPET_Tn_CMP(n) (0x108 + (n) * 0x20)
#define HPET_Tn_ROUTE(n) (0x110 + (n) * 0x20)
#define HPET_ID_VENDOR 0xffff0000
#define HPET_ID_LEGSUP 0x00008000
@@ -65,6 +55,7 @@ extern unsigned long hpet_address;
* Return value is zero if HPET is unavailable.
*/
u64 hpet_setup(void);
void hpet_resume(void);
/*
* Callback from legacy timer (PIT channel 0) IRQ handler.
@@ -77,6 +68,7 @@ int hpet_legacy_irq_tick(void);
* rather than using the LAPIC timer. Used for Cx state entry.
*/
void hpet_broadcast_init(void);
void hpet_broadcast_resume(void);
void hpet_broadcast_enter(void);
void hpet_broadcast_exit(void);
int hpet_broadcast_is_available(void);
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.