2006-06-26 00:25:12 -07:00
# include <linux/clocksource.h>
2007-02-16 01:28:04 -08:00
# include <linux/clockchips.h>
2008-09-06 14:19:17 +02:00
# include <linux/interrupt.h>
# include <linux/sysdev.h>
2007-10-12 23:04:06 +02:00
# include <linux/delay.h>
2006-06-26 00:25:12 -07:00
# include <linux/errno.h>
# include <linux/hpet.h>
# include <linux/init.h>
2008-09-05 18:02:17 -07:00
# include <linux/cpu.h>
2008-09-06 14:19:17 +02:00
# include <linux/pm.h>
# include <linux/io.h>
2006-06-26 00:25:12 -07:00
2007-10-12 23:04:06 +02:00
# include <asm/fixmap.h>
2007-10-12 23:04:06 +02:00
# include <asm/i8253.h>
2008-09-06 14:19:17 +02:00
# include <asm/hpet.h>
2006-06-26 00:25:12 -07:00
2008-09-06 14:19:17 +02:00
# define HPET_MASK CLOCKSOURCE_MASK(32)
# define HPET_SHIFT 22
2006-06-26 00:25:12 -07:00
2008-01-30 13:30:00 +01:00
/* FSEC = 10^-15
NSEC = 10 ^ - 9 */
2008-09-06 14:19:17 +02:00
# define FSEC_PER_NSEC 1000000L
2006-06-26 00:25:12 -07:00
2008-09-05 18:02:18 -07:00
# define HPET_DEV_USED_BIT 2
# define HPET_DEV_USED (1 << HPET_DEV_USED_BIT)
# define HPET_DEV_VALID 0x8
# define HPET_DEV_FSB_CAP 0x1000
# define HPET_DEV_PERI_CAP 0x2000
# define EVT_TO_HPET_DEV(evt) container_of(evt, struct hpet_dev, evt)
2007-02-16 01:28:04 -08:00
/*
* HPET address is set in acpi / boot . c , when an ACPI entry exists
*/
2008-09-06 14:19:17 +02:00
unsigned long hpet_address ;
2008-09-05 18:02:18 -07:00
unsigned long hpet_num_timers ;
2008-09-06 14:19:17 +02:00
static void __iomem * hpet_virt_address ;
2007-02-16 01:28:04 -08:00
2008-09-05 18:02:17 -07:00
struct hpet_dev {
2008-09-06 14:19:17 +02:00
struct clock_event_device evt ;
unsigned int num ;
int cpu ;
unsigned int irq ;
unsigned int flags ;
char name [ 10 ] ;
2008-09-05 18:02:17 -07:00
} ;
2008-09-05 18:02:18 -07:00
static struct hpet_dev * hpet_devs ;
static DEFINE_PER_CPU ( struct hpet_dev * , cpu_hpet_dev ) ;
2007-10-12 23:04:23 +02:00
unsigned long hpet_readl ( unsigned long a )
2007-02-16 01:28:04 -08:00
{
return readl ( hpet_virt_address + a ) ;
}
static inline void hpet_writel ( unsigned long d , unsigned long a )
{
writel ( d , hpet_virt_address + a ) ;
}
2007-10-12 23:04:06 +02:00
# ifdef CONFIG_X86_64
# include <asm/pgtable.h>
2008-07-13 14:50:56 -07:00
# endif
2007-10-12 23:04:06 +02:00
2007-10-12 23:04:06 +02:00
static inline void hpet_set_mapping ( void )
{
hpet_virt_address = ioremap_nocache ( hpet_address , HPET_MMAP_SIZE ) ;
2008-07-13 14:50:56 -07:00
# ifdef CONFIG_X86_64
__set_fixmap ( VSYSCALL_HPET , hpet_address , PAGE_KERNEL_VSYSCALL_NOCACHE ) ;
# endif
2007-10-12 23:04:06 +02:00
}
static inline void hpet_clear_mapping ( void )
{
iounmap ( hpet_virt_address ) ;
hpet_virt_address = NULL ;
}
2007-02-16 01:28:04 -08:00
/*
* HPET command line enable / disable
*/
static int boot_hpet_disable ;
2007-10-19 20:35:02 +02:00
int hpet_force_user ;
2007-02-16 01:28:04 -08:00
2008-09-06 14:19:17 +02:00
static int __init hpet_setup ( char * str )
2007-02-16 01:28:04 -08:00
{
if ( str ) {
if ( ! strncmp ( " disable " , str , 7 ) )
boot_hpet_disable = 1 ;
2007-10-19 20:35:02 +02:00
if ( ! strncmp ( " force " , str , 5 ) )
hpet_force_user = 1 ;
2007-02-16 01:28:04 -08:00
}
return 1 ;
}
__setup ( " hpet= " , hpet_setup ) ;
2007-10-12 23:04:06 +02:00
static int __init disable_hpet ( char * str )
{
boot_hpet_disable = 1 ;
return 1 ;
}
__setup ( " nohpet " , disable_hpet ) ;
2007-02-16 01:28:04 -08:00
static inline int is_hpet_capable ( void )
{
2008-09-06 14:19:17 +02:00
return ! boot_hpet_disable & & hpet_address ;
2007-02-16 01:28:04 -08:00
}
/*
* HPET timer interrupt enable / disable
*/
static int hpet_legacy_int_enabled ;
/**
* is_hpet_enabled - check whether the hpet timer interrupt is enabled
*/
int is_hpet_enabled ( void )
{
return is_hpet_capable ( ) & & hpet_legacy_int_enabled ;
}
2008-01-30 13:33:28 +01:00
EXPORT_SYMBOL_GPL ( is_hpet_enabled ) ;
2007-02-16 01:28:04 -08:00
/*
* When the hpet driver ( / dev / hpet ) is enabled , we need to reserve
* timer 0 and timer 1 in case of RTC emulation .
*/
# ifdef CONFIG_HPET
x86: using HPET in MSI mode and setting up per CPU HPET timers, fix
On Sat, Sep 06, 2008 at 06:03:53AM -0700, Ingo Molnar wrote:
>
> it crashes two testsystems, the fault on a NULL pointer in hpet init,
> with:
>
> initcall print_all_ICs+0x0/0x520 returned 0 after 26 msecs
> calling hpet_late_init+0x0/0x1c0
> BUG: unable to handle kernel NULL pointer dereference at 000000000000008c
> IP: [<ffffffff80d228be>] hpet_late_init+0xfe/0x1c0
> PGD 0
> Oops: 0000 [1] SMP
> CPU 0
> Modules linked in:
> Pid: 1, comm: swapper Not tainted 2.6.27-rc5 #29725
> RIP: 0010:[<ffffffff80d228be>] [<ffffffff80d228be>] hpet_late_init+0xfe/0x1c0
> RSP: 0018:ffff88003fa07dd0 EFLAGS: 00010246
> RAX: 0000000000000000 RBX: 0000000000000003 RCX: 0000000000000000
> RDX: ffffc20000000160 RSI: 0000000000000000 RDI: 0000000000000003
> RBP: ffff88003fa07e90 R08: 0000000000000000 R09: ffff88003fa07dd0
> R10: 0000000000000001 R11: 0000000000000000 R12: ffff88003fa07dd0
> R13: 0000000000000002 R14: ffffc20000000000 R15: 000000006f57e511
> FS: 0000000000000000(0000) GS:ffffffff80cf6a80(0000) knlGS:0000000000000000
> CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b
> CR2: 000000000000008c CR3: 0000000000201000 CR4: 00000000000006e0
> DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
> Process swapper (pid: 1, threadinfo ffff88003fa06000, task ffff88003fa08000)
> Stack: 00000000fed00000 ffffc20000000000 0000000100000003 0000000800000002
> 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> Call Trace:
> [<ffffffff80d227c0>] ? hpet_late_init+0x0/0x1c0
> [<ffffffff80209045>] do_one_initcall+0x45/0x190
> [<ffffffff80296f39>] ? register_irq_proc+0x19/0xe0
> [<ffffffff80d0d140>] ? early_idt_handler+0x0/0x73
> [<ffffffff80d0dabc>] kernel_init+0x14c/0x1b0
> [<ffffffff80942ac1>] ? trace_hardirqs_on_thunk+0x3a/0x3f
> [<ffffffff8020dbd9>] child_rip+0xa/0x11
> [<ffffffff8020ceee>] ? restore_args+0x0/0x30
> [<ffffffff80d0d970>] ? kernel_init+0x0/0x1b0
> [<ffffffff8020dbcf>] ? child_rip+0x0/0x11
> Code: 20 48 83 c1 01 48 39 f1 75 e3 44 89 e8 4c 8b 05 29 29 22 00 31 f6 48 8d 78 01 66 66 90 89 f0 48 8d 04 80 48 c1 e0 05 4a 8d 0c 00 <f6> 81 8c 00 00 00 08 74 26 8b 81 80 00 00 00 8b 91 88 00 00 00
> RIP [<ffffffff80d228be>] hpet_late_init+0xfe/0x1c0
> RSP <ffff88003fa07dd0>
> CR2: 000000000000008c
> Kernel panic - not syncing: Fatal exception
There was one code path, with CONFIG_PCI_MSI disabled, where we were accessing
hpet_devs without initialization. That resulted in the above crash. The change
below adds a check for hpet_devs.
Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-09-08 10:18:40 -07:00
static void hpet_reserve_msi_timers ( struct hpet_data * hd )
{
int i ;
if ( ! hpet_devs )
return ;
for ( i = 0 ; i < hpet_num_timers ; i + + ) {
struct hpet_dev * hdev = & hpet_devs [ i ] ;
if ( ! ( hdev - > flags & HPET_DEV_VALID ) )
continue ;
hd - > hd_irq [ hdev - > num ] = hdev - > irq ;
hpet_reserve_timer ( hd , hdev - > num ) ;
}
}
2007-02-16 01:28:04 -08:00
static void hpet_reserve_platform_timers ( unsigned long id )
{
struct hpet __iomem * hpet = hpet_virt_address ;
2008-01-30 13:30:03 +01:00
struct hpet_timer __iomem * timer = & hpet - > hpet_timers [ 2 ] ;
unsigned int nrtimers , i ;
2007-02-16 01:28:04 -08:00
struct hpet_data hd ;
nrtimers = ( ( id & HPET_ID_NUMBER ) > > HPET_ID_NUMBER_SHIFT ) + 1 ;
2008-09-06 14:19:17 +02:00
memset ( & hd , 0 , sizeof ( hd ) ) ;
hd . hd_phys_address = hpet_address ;
hd . hd_address = hpet ;
hd . hd_nirqs = nrtimers ;
2007-02-16 01:28:04 -08:00
hpet_reserve_timer ( & hd , 0 ) ;
# ifdef CONFIG_HPET_EMULATE_RTC
hpet_reserve_timer ( & hd , 1 ) ;
# endif
2008-04-04 16:26:10 +02:00
2008-07-29 12:47:38 -07:00
/*
* NOTE that hd_irq [ ] reflects IOAPIC input pins ( LEGACY_8254
* is wrong for i8259 ! ) not the output IRQ . Many BIOS writers
* don ' t bother configuring * any * comparator interrupts .
*/
2007-02-16 01:28:04 -08:00
hd . hd_irq [ 0 ] = HPET_LEGACY_8254 ;
hd . hd_irq [ 1 ] = HPET_LEGACY_RTC ;
2008-04-27 14:04:14 +02:00
for ( i = 2 ; i < nrtimers ; timer + + , i + + ) {
2008-09-06 14:19:17 +02:00
hd . hd_irq [ i ] = ( readl ( & timer - > hpet_config ) &
Tn_INT_ROUTE_CNF_MASK ) > > Tn_INT_ROUTE_CNF_SHIFT ;
2008-04-27 14:04:14 +02:00
}
2008-04-04 16:26:10 +02:00
x86: using HPET in MSI mode and setting up per CPU HPET timers, fix
On Sat, Sep 06, 2008 at 06:03:53AM -0700, Ingo Molnar wrote:
>
> it crashes two testsystems, the fault on a NULL pointer in hpet init,
> with:
>
> initcall print_all_ICs+0x0/0x520 returned 0 after 26 msecs
> calling hpet_late_init+0x0/0x1c0
> BUG: unable to handle kernel NULL pointer dereference at 000000000000008c
> IP: [<ffffffff80d228be>] hpet_late_init+0xfe/0x1c0
> PGD 0
> Oops: 0000 [1] SMP
> CPU 0
> Modules linked in:
> Pid: 1, comm: swapper Not tainted 2.6.27-rc5 #29725
> RIP: 0010:[<ffffffff80d228be>] [<ffffffff80d228be>] hpet_late_init+0xfe/0x1c0
> RSP: 0018:ffff88003fa07dd0 EFLAGS: 00010246
> RAX: 0000000000000000 RBX: 0000000000000003 RCX: 0000000000000000
> RDX: ffffc20000000160 RSI: 0000000000000000 RDI: 0000000000000003
> RBP: ffff88003fa07e90 R08: 0000000000000000 R09: ffff88003fa07dd0
> R10: 0000000000000001 R11: 0000000000000000 R12: ffff88003fa07dd0
> R13: 0000000000000002 R14: ffffc20000000000 R15: 000000006f57e511
> FS: 0000000000000000(0000) GS:ffffffff80cf6a80(0000) knlGS:0000000000000000
> CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b
> CR2: 000000000000008c CR3: 0000000000201000 CR4: 00000000000006e0
> DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
> Process swapper (pid: 1, threadinfo ffff88003fa06000, task ffff88003fa08000)
> Stack: 00000000fed00000 ffffc20000000000 0000000100000003 0000000800000002
> 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> Call Trace:
> [<ffffffff80d227c0>] ? hpet_late_init+0x0/0x1c0
> [<ffffffff80209045>] do_one_initcall+0x45/0x190
> [<ffffffff80296f39>] ? register_irq_proc+0x19/0xe0
> [<ffffffff80d0d140>] ? early_idt_handler+0x0/0x73
> [<ffffffff80d0dabc>] kernel_init+0x14c/0x1b0
> [<ffffffff80942ac1>] ? trace_hardirqs_on_thunk+0x3a/0x3f
> [<ffffffff8020dbd9>] child_rip+0xa/0x11
> [<ffffffff8020ceee>] ? restore_args+0x0/0x30
> [<ffffffff80d0d970>] ? kernel_init+0x0/0x1b0
> [<ffffffff8020dbcf>] ? child_rip+0x0/0x11
> Code: 20 48 83 c1 01 48 39 f1 75 e3 44 89 e8 4c 8b 05 29 29 22 00 31 f6 48 8d 78 01 66 66 90 89 f0 48 8d 04 80 48 c1 e0 05 4a 8d 0c 00 <f6> 81 8c 00 00 00 08 74 26 8b 81 80 00 00 00 8b 91 88 00 00 00
> RIP [<ffffffff80d228be>] hpet_late_init+0xfe/0x1c0
> RSP <ffff88003fa07dd0>
> CR2: 000000000000008c
> Kernel panic - not syncing: Fatal exception
There was one code path, with CONFIG_PCI_MSI disabled, where we were accessing
hpet_devs without initialization. That resulted in the above crash. The change
below adds a check for hpet_devs.
Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2008-09-08 10:18:40 -07:00
hpet_reserve_msi_timers ( & hd ) ;
2008-09-05 18:02:18 -07:00
2007-02-16 01:28:04 -08:00
hpet_alloc ( & hd ) ;
2008-04-04 16:26:10 +02:00
2007-02-16 01:28:04 -08:00
}
# else
static void hpet_reserve_platform_timers ( unsigned long id ) { }
# endif
/*
* Common hpet info
*/
static unsigned long hpet_period ;
2007-10-12 23:04:23 +02:00
static void hpet_legacy_set_mode ( enum clock_event_mode mode ,
2007-02-16 01:28:04 -08:00
struct clock_event_device * evt ) ;
2007-10-12 23:04:23 +02:00
static int hpet_legacy_next_event ( unsigned long delta ,
2007-02-16 01:28:04 -08:00
struct clock_event_device * evt ) ;
/*
* The hpet clock event device
*/
static struct clock_event_device hpet_clockevent = {
. name = " hpet " ,
. features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ,
2007-10-12 23:04:23 +02:00
. set_mode = hpet_legacy_set_mode ,
. set_next_event = hpet_legacy_next_event ,
2007-02-16 01:28:04 -08:00
. shift = 32 ,
. irq = 0 ,
2007-10-12 23:04:23 +02:00
. rating = 50 ,
2007-02-16 01:28:04 -08:00
} ;
static void hpet_start_counter ( void )
{
unsigned long cfg = hpet_readl ( HPET_CFG ) ;
cfg & = ~ HPET_CFG_ENABLE ;
hpet_writel ( cfg , HPET_CFG ) ;
hpet_writel ( 0 , HPET_COUNTER ) ;
hpet_writel ( 0 , HPET_COUNTER + 4 ) ;
cfg | = HPET_CFG_ENABLE ;
hpet_writel ( cfg , HPET_CFG ) ;
}
2007-10-12 23:04:23 +02:00
static void hpet_resume_device ( void )
{
2007-10-12 23:04:24 +02:00
force_hpet_resume ( ) ;
2007-10-12 23:04:23 +02:00
}
static void hpet_restart_counter ( void )
{
hpet_resume_device ( ) ;
hpet_start_counter ( ) ;
}
2007-10-12 23:04:23 +02:00
static void hpet_enable_legacy_int ( void )
2007-02-16 01:28:04 -08:00
{
unsigned long cfg = hpet_readl ( HPET_CFG ) ;
cfg | = HPET_CFG_LEGACY ;
hpet_writel ( cfg , HPET_CFG ) ;
hpet_legacy_int_enabled = 1 ;
}
2007-10-12 23:04:23 +02:00
static void hpet_legacy_clockevent_register ( void )
{
/* Start HPET legacy interrupts */
hpet_enable_legacy_int ( ) ;
/*
x86: clean up computation of HPET .mult variables
While reading through the HPET code I realized that the
computation of .mult variables could be done with less
lines of code, resulting in a 1.6% text size saving
for hpet.o
So I propose the following patch, which applies against
today's Linus -git tree.
>From 0c6507e400e9ca5f7f14331e18f8c12baf75a9d3 Mon Sep 17 00:00:00 2001
From: Carlos R. Mafra <crmafra@ift.unesp.br>
Date: Mon, 5 May 2008 19:38:53 -0300
The computation of clocksource_hpet.mult
tmp = (u64)hpet_period << HPET_SHIFT;
do_div(tmp, FSEC_PER_NSEC);
clocksource_hpet.mult = (u32)tmp;
can be streamlined if we note that it is equal to
clocksource_hpet.mult = div_sc(hpet_period, FSEC_PER_NSEC, HPET_SHIFT);
Furthermore, the computation of hpet_clockevent.mult
uint64_t hpet_freq;
hpet_freq = 1000000000000000ULL;
do_div(hpet_freq, hpet_period);
hpet_clockevent.mult = div_sc((unsigned long) hpet_freq,
NSEC_PER_SEC, hpet_clockevent.shift);
can also be streamlined with the observation that hpet_period and hpet_freq are
inverse to each other (in proper units).
So instead of computing hpet_freq and using (schematically)
div_sc(hpet_freq, 10^9, shift) we use the trick of calling with the
arguments in reverse order, div_sc(10^6, hpet_period, shift).
The different power of ten is due to frequency being in Hertz (1/sec)
and the period being in units of femtosecond. Explicitly,
mult = (hpet_freq * 2^shift)/10^9 (before)
mult = (10^6 * 2^shift)/hpet_period (after)
because hpet_freq = 10^15/hpet_period.
The comments in the code are also updated to reflect the changes.
As a result,
text data bss dec hex filename
2957 425 92 3474 d92 arch/x86/kernel/hpet.o
3006 425 92 3523 dc3 arch/x86/kernel/hpet.o.old
a 1.6% reduction in text size.
Signed-off-by: Carlos R. Mafra <crmafra@ift.unesp.br>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2008-05-05 20:11:22 -03:00
* The mult factor is defined as ( include / linux / clockchips . h )
* mult / 2 ^ shift = cyc / ns ( in contrast to ns / cyc in clocksource . h )
* hpet_period is in units of femtoseconds ( per cycle ) , so
* mult / 2 ^ shift = cyc / ns = 10 ^ 6 / hpet_period
* mult = ( 10 ^ 6 * 2 ^ shift ) / hpet_period
* mult = ( FSEC_PER_NSEC < < hpet_clockevent . shift ) / hpet_period
2007-10-12 23:04:23 +02:00
*/
x86: clean up computation of HPET .mult variables
While reading through the HPET code I realized that the
computation of .mult variables could be done with less
lines of code, resulting in a 1.6% text size saving
for hpet.o
So I propose the following patch, which applies against
today's Linus -git tree.
>From 0c6507e400e9ca5f7f14331e18f8c12baf75a9d3 Mon Sep 17 00:00:00 2001
From: Carlos R. Mafra <crmafra@ift.unesp.br>
Date: Mon, 5 May 2008 19:38:53 -0300
The computation of clocksource_hpet.mult
tmp = (u64)hpet_period << HPET_SHIFT;
do_div(tmp, FSEC_PER_NSEC);
clocksource_hpet.mult = (u32)tmp;
can be streamlined if we note that it is equal to
clocksource_hpet.mult = div_sc(hpet_period, FSEC_PER_NSEC, HPET_SHIFT);
Furthermore, the computation of hpet_clockevent.mult
uint64_t hpet_freq;
hpet_freq = 1000000000000000ULL;
do_div(hpet_freq, hpet_period);
hpet_clockevent.mult = div_sc((unsigned long) hpet_freq,
NSEC_PER_SEC, hpet_clockevent.shift);
can also be streamlined with the observation that hpet_period and hpet_freq are
inverse to each other (in proper units).
So instead of computing hpet_freq and using (schematically)
div_sc(hpet_freq, 10^9, shift) we use the trick of calling with the
arguments in reverse order, div_sc(10^6, hpet_period, shift).
The different power of ten is due to frequency being in Hertz (1/sec)
and the period being in units of femtosecond. Explicitly,
mult = (hpet_freq * 2^shift)/10^9 (before)
mult = (10^6 * 2^shift)/hpet_period (after)
because hpet_freq = 10^15/hpet_period.
The comments in the code are also updated to reflect the changes.
As a result,
text data bss dec hex filename
2957 425 92 3474 d92 arch/x86/kernel/hpet.o
3006 425 92 3523 dc3 arch/x86/kernel/hpet.o.old
a 1.6% reduction in text size.
Signed-off-by: Carlos R. Mafra <crmafra@ift.unesp.br>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2008-05-05 20:11:22 -03:00
hpet_clockevent . mult = div_sc ( ( unsigned long ) FSEC_PER_NSEC ,
hpet_period , hpet_clockevent . shift ) ;
2007-10-12 23:04:23 +02:00
/* Calculate the min / max delta */
hpet_clockevent . max_delta_ns = clockevent_delta2ns ( 0x7FFFFFFF ,
& hpet_clockevent ) ;
2008-09-03 21:37:24 +00:00
/* 5 usec minimum reprogramming delta. */
hpet_clockevent . min_delta_ns = 5000 ;
2007-10-12 23:04:23 +02:00
/*
* Start hpet with the boot cpu mask and make it
* global after the IO_APIC has been initialized .
*/
hpet_clockevent . cpumask = cpumask_of_cpu ( smp_processor_id ( ) ) ;
clockevents_register_device ( & hpet_clockevent ) ;
global_clock_event = & hpet_clockevent ;
printk ( KERN_DEBUG " hpet clockevent registered \n " ) ;
}
2008-09-05 18:02:18 -07:00
static int hpet_setup_msi_irq ( unsigned int irq ) ;
2008-09-05 18:02:16 -07:00
static void hpet_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * evt , int timer )
2007-02-16 01:28:04 -08:00
{
unsigned long cfg , cmp , now ;
uint64_t delta ;
2008-09-06 14:19:17 +02:00
switch ( mode ) {
2007-02-16 01:28:04 -08:00
case CLOCK_EVT_MODE_PERIODIC :
2008-09-05 18:02:16 -07:00
delta = ( ( uint64_t ) ( NSEC_PER_SEC / HZ ) ) * evt - > mult ;
delta > > = evt - > shift ;
2007-02-16 01:28:04 -08:00
now = hpet_readl ( HPET_COUNTER ) ;
cmp = now + ( unsigned long ) delta ;
2008-09-05 18:02:16 -07:00
cfg = hpet_readl ( HPET_Tn_CFG ( timer ) ) ;
2007-02-16 01:28:04 -08:00
cfg | = HPET_TN_ENABLE | HPET_TN_PERIODIC |
HPET_TN_SETVAL | HPET_TN_32BIT ;
2008-09-05 18:02:16 -07:00
hpet_writel ( cfg , HPET_Tn_CFG ( timer ) ) ;
2007-02-16 01:28:04 -08:00
/*
* The first write after writing TN_SETVAL to the
* config register sets the counter value , the second
* write sets the period .
*/
2008-09-05 18:02:16 -07:00
hpet_writel ( cmp , HPET_Tn_CMP ( timer ) ) ;
2007-02-16 01:28:04 -08:00
udelay ( 1 ) ;
2008-09-05 18:02:16 -07:00
hpet_writel ( ( unsigned long ) delta , HPET_Tn_CMP ( timer ) ) ;
2007-02-16 01:28:04 -08:00
break ;
case CLOCK_EVT_MODE_ONESHOT :
2008-09-05 18:02:16 -07:00
cfg = hpet_readl ( HPET_Tn_CFG ( timer ) ) ;
2007-02-16 01:28:04 -08:00
cfg & = ~ HPET_TN_PERIODIC ;
cfg | = HPET_TN_ENABLE | HPET_TN_32BIT ;
2008-09-05 18:02:16 -07:00
hpet_writel ( cfg , HPET_Tn_CFG ( timer ) ) ;
2007-02-16 01:28:04 -08:00
break ;
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
2008-09-05 18:02:16 -07:00
cfg = hpet_readl ( HPET_Tn_CFG ( timer ) ) ;
2007-02-16 01:28:04 -08:00
cfg & = ~ HPET_TN_ENABLE ;
2008-09-05 18:02:16 -07:00
hpet_writel ( cfg , HPET_Tn_CFG ( timer ) ) ;
2007-02-16 01:28:04 -08:00
break ;
2007-07-21 04:37:34 -07:00
case CLOCK_EVT_MODE_RESUME :
2008-09-05 18:02:18 -07:00
if ( timer = = 0 ) {
hpet_enable_legacy_int ( ) ;
} else {
struct hpet_dev * hdev = EVT_TO_HPET_DEV ( evt ) ;
hpet_setup_msi_irq ( hdev - > irq ) ;
disable_irq ( hdev - > irq ) ;
irq_set_affinity ( hdev - > irq , cpumask_of_cpu ( hdev - > cpu ) ) ;
enable_irq ( hdev - > irq ) ;
}
2007-07-21 04:37:34 -07:00
break ;
2007-02-16 01:28:04 -08:00
}
}
2008-09-05 18:02:16 -07:00
static int hpet_next_event ( unsigned long delta ,
struct clock_event_device * evt , int timer )
2007-02-16 01:28:04 -08:00
{
2008-09-06 03:03:32 +02:00
u32 cnt ;
2007-02-16 01:28:04 -08:00
cnt = hpet_readl ( HPET_COUNTER ) ;
2008-09-06 03:03:32 +02:00
cnt + = ( u32 ) delta ;
2008-09-05 18:02:16 -07:00
hpet_writel ( cnt , HPET_Tn_CMP ( timer ) ) ;
2007-02-16 01:28:04 -08:00
2008-09-06 03:06:08 +02:00
/*
* We need to read back the CMP register to make sure that
* what we wrote hit the chip before we compare it to the
* counter .
*/
WARN_ON ( ( u32 ) hpet_readl ( HPET_T0_CMP ) ! = cnt ) ;
2008-09-06 03:03:32 +02:00
return ( s32 ) ( ( u32 ) hpet_readl ( HPET_COUNTER ) - cnt ) > = 0 ? - ETIME : 0 ;
2007-02-16 01:28:04 -08:00
}
2008-09-05 18:02:16 -07:00
static void hpet_legacy_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * evt )
{
hpet_set_mode ( mode , evt , 0 ) ;
}
static int hpet_legacy_next_event ( unsigned long delta ,
struct clock_event_device * evt )
{
return hpet_next_event ( delta , evt , 0 ) ;
}
2008-09-05 18:02:17 -07:00
/*
* HPET MSI Support
*/
2008-09-05 18:02:18 -07:00
# ifdef CONFIG_PCI_MSI
2008-09-05 18:02:17 -07:00
void hpet_msi_unmask ( unsigned int irq )
{
struct hpet_dev * hdev = get_irq_data ( irq ) ;
unsigned long cfg ;
/* unmask it */
cfg = hpet_readl ( HPET_Tn_CFG ( hdev - > num ) ) ;
cfg | = HPET_TN_FSB ;
hpet_writel ( cfg , HPET_Tn_CFG ( hdev - > num ) ) ;
}
void hpet_msi_mask ( unsigned int irq )
{
unsigned long cfg ;
struct hpet_dev * hdev = get_irq_data ( irq ) ;
/* mask it */
cfg = hpet_readl ( HPET_Tn_CFG ( hdev - > num ) ) ;
cfg & = ~ HPET_TN_FSB ;
hpet_writel ( cfg , HPET_Tn_CFG ( hdev - > num ) ) ;
}
void hpet_msi_write ( unsigned int irq , struct msi_msg * msg )
{
struct hpet_dev * hdev = get_irq_data ( irq ) ;
hpet_writel ( msg - > data , HPET_Tn_ROUTE ( hdev - > num ) ) ;
hpet_writel ( msg - > address_lo , HPET_Tn_ROUTE ( hdev - > num ) + 4 ) ;
}
void hpet_msi_read ( unsigned int irq , struct msi_msg * msg )
{
struct hpet_dev * hdev = get_irq_data ( irq ) ;
msg - > data = hpet_readl ( HPET_Tn_ROUTE ( hdev - > num ) ) ;
msg - > address_lo = hpet_readl ( HPET_Tn_ROUTE ( hdev - > num ) + 4 ) ;
msg - > address_hi = 0 ;
}
2008-09-05 18:02:18 -07:00
static void hpet_msi_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * evt )
{
struct hpet_dev * hdev = EVT_TO_HPET_DEV ( evt ) ;
hpet_set_mode ( mode , evt , hdev - > num ) ;
}
static int hpet_msi_next_event ( unsigned long delta ,
struct clock_event_device * evt )
{
struct hpet_dev * hdev = EVT_TO_HPET_DEV ( evt ) ;
return hpet_next_event ( delta , evt , hdev - > num ) ;
}
static int hpet_setup_msi_irq ( unsigned int irq )
{
if ( arch_setup_hpet_msi ( irq ) ) {
destroy_irq ( irq ) ;
return - EINVAL ;
}
return 0 ;
}
static int hpet_assign_irq ( struct hpet_dev * dev )
{
unsigned int irq ;
irq = create_irq ( ) ;
if ( ! irq )
return - EINVAL ;
set_irq_data ( irq , dev ) ;
if ( hpet_setup_msi_irq ( irq ) )
return - EINVAL ;
dev - > irq = irq ;
return 0 ;
}
static irqreturn_t hpet_interrupt_handler ( int irq , void * data )
{
struct hpet_dev * dev = ( struct hpet_dev * ) data ;
struct clock_event_device * hevt = & dev - > evt ;
if ( ! hevt - > event_handler ) {
printk ( KERN_INFO " Spurious HPET timer interrupt on HPET timer %d \n " ,
dev - > num ) ;
return IRQ_HANDLED ;
}
hevt - > event_handler ( hevt ) ;
return IRQ_HANDLED ;
}
static int hpet_setup_irq ( struct hpet_dev * dev )
{
if ( request_irq ( dev - > irq , hpet_interrupt_handler ,
IRQF_SHARED | IRQF_NOBALANCING , dev - > name , dev ) )
return - 1 ;
disable_irq ( dev - > irq ) ;
irq_set_affinity ( dev - > irq , cpumask_of_cpu ( dev - > cpu ) ) ;
enable_irq ( dev - > irq ) ;
return 0 ;
}
/* This should be called in specific @cpu */
static void init_one_hpet_msi_clockevent ( struct hpet_dev * hdev , int cpu )
{
struct clock_event_device * evt = & hdev - > evt ;
uint64_t hpet_freq ;
WARN_ON ( cpu ! = smp_processor_id ( ) ) ;
if ( ! ( hdev - > flags & HPET_DEV_VALID ) )
return ;
if ( hpet_setup_msi_irq ( hdev - > irq ) )
return ;
hdev - > cpu = cpu ;
per_cpu ( cpu_hpet_dev , cpu ) = hdev ;
evt - > name = hdev - > name ;
hpet_setup_irq ( hdev ) ;
evt - > irq = hdev - > irq ;
evt - > rating = 110 ;
evt - > features = CLOCK_EVT_FEAT_ONESHOT ;
if ( hdev - > flags & HPET_DEV_PERI_CAP )
evt - > features | = CLOCK_EVT_FEAT_PERIODIC ;
evt - > set_mode = hpet_msi_set_mode ;
evt - > set_next_event = hpet_msi_next_event ;
evt - > shift = 32 ;
/*
* The period is a femto seconds value . We need to calculate the
* scaled math multiplication factor for nanosecond to hpet tick
* conversion .
*/
hpet_freq = 1000000000000000ULL ;
do_div ( hpet_freq , hpet_period ) ;
evt - > mult = div_sc ( ( unsigned long ) hpet_freq ,
NSEC_PER_SEC , evt - > shift ) ;
/* Calculate the max delta */
evt - > max_delta_ns = clockevent_delta2ns ( 0x7FFFFFFF , evt ) ;
/* 5 usec minimum reprogramming delta. */
evt - > min_delta_ns = 5000 ;
evt - > cpumask = cpumask_of_cpu ( hdev - > cpu ) ;
clockevents_register_device ( evt ) ;
}
# ifdef CONFIG_HPET
/* Reserve at least one timer for userspace (/dev/hpet) */
# define RESERVE_TIMERS 1
# else
# define RESERVE_TIMERS 0
# endif
void hpet_msi_capability_lookup ( unsigned int start_timer )
{
unsigned int id ;
unsigned int num_timers ;
unsigned int num_timers_used = 0 ;
int i ;
id = hpet_readl ( HPET_ID ) ;
num_timers = ( ( id & HPET_ID_NUMBER ) > > HPET_ID_NUMBER_SHIFT ) ;
num_timers + + ; /* Value read out starts from 0 */
hpet_devs = kzalloc ( sizeof ( struct hpet_dev ) * num_timers , GFP_KERNEL ) ;
if ( ! hpet_devs )
return ;
hpet_num_timers = num_timers ;
for ( i = start_timer ; i < num_timers - RESERVE_TIMERS ; i + + ) {
struct hpet_dev * hdev = & hpet_devs [ num_timers_used ] ;
unsigned long cfg = hpet_readl ( HPET_Tn_CFG ( i ) ) ;
/* Only consider HPET timer with MSI support */
if ( ! ( cfg & HPET_TN_FSB_CAP ) )
continue ;
hdev - > flags = 0 ;
if ( cfg & HPET_TN_PERIODIC_CAP )
hdev - > flags | = HPET_DEV_PERI_CAP ;
hdev - > num = i ;
sprintf ( hdev - > name , " hpet%d " , i ) ;
if ( hpet_assign_irq ( hdev ) )
continue ;
hdev - > flags | = HPET_DEV_FSB_CAP ;
hdev - > flags | = HPET_DEV_VALID ;
num_timers_used + + ;
if ( num_timers_used = = num_possible_cpus ( ) )
break ;
}
printk ( KERN_INFO " HPET: %d timers in total, %d timers will be used for per-cpu timer \n " ,
num_timers , num_timers_used ) ;
}
static struct hpet_dev * hpet_get_unused_timer ( void )
{
int i ;
if ( ! hpet_devs )
return NULL ;
for ( i = 0 ; i < hpet_num_timers ; i + + ) {
struct hpet_dev * hdev = & hpet_devs [ i ] ;
if ( ! ( hdev - > flags & HPET_DEV_VALID ) )
continue ;
if ( test_and_set_bit ( HPET_DEV_USED_BIT ,
( unsigned long * ) & hdev - > flags ) )
continue ;
return hdev ;
}
return NULL ;
}
struct hpet_work_struct {
struct delayed_work work ;
struct completion complete ;
} ;
static void hpet_work ( struct work_struct * w )
{
struct hpet_dev * hdev ;
int cpu = smp_processor_id ( ) ;
struct hpet_work_struct * hpet_work ;
hpet_work = container_of ( w , struct hpet_work_struct , work . work ) ;
hdev = hpet_get_unused_timer ( ) ;
if ( hdev )
init_one_hpet_msi_clockevent ( hdev , cpu ) ;
complete ( & hpet_work - > complete ) ;
}
static int hpet_cpuhp_notify ( struct notifier_block * n ,
unsigned long action , void * hcpu )
{
unsigned long cpu = ( unsigned long ) hcpu ;
struct hpet_work_struct work ;
struct hpet_dev * hdev = per_cpu ( cpu_hpet_dev , cpu ) ;
switch ( action & 0xf ) {
case CPU_ONLINE :
INIT_DELAYED_WORK ( & work . work , hpet_work ) ;
init_completion ( & work . complete ) ;
/* FIXME: add schedule_work_on() */
schedule_delayed_work_on ( cpu , & work . work , 0 ) ;
wait_for_completion ( & work . complete ) ;
break ;
case CPU_DEAD :
if ( hdev ) {
free_irq ( hdev - > irq , hdev ) ;
hdev - > flags & = ~ HPET_DEV_USED ;
per_cpu ( cpu_hpet_dev , cpu ) = NULL ;
}
break ;
}
return NOTIFY_OK ;
}
# else
2008-09-08 16:19:09 -07:00
static int hpet_setup_msi_irq ( unsigned int irq )
{
return 0 ;
}
2008-09-05 18:02:18 -07:00
void hpet_msi_capability_lookup ( unsigned int start_timer )
{
return ;
}
static int hpet_cpuhp_notify ( struct notifier_block * n ,
unsigned long action , void * hcpu )
{
return NOTIFY_OK ;
}
# endif
2007-03-05 00:30:50 -08:00
/*
* Clock source related code
*/
static cycle_t read_hpet ( void )
{
return ( cycle_t ) hpet_readl ( HPET_COUNTER ) ;
}
2007-10-12 23:04:06 +02:00
# ifdef CONFIG_X86_64
static cycle_t __vsyscall_fn vread_hpet ( void )
{
return readl ( ( const void __iomem * ) fix_to_virt ( VSYSCALL_HPET ) + 0xf0 ) ;
}
# endif
2007-03-05 00:30:50 -08:00
static struct clocksource clocksource_hpet = {
. name = " hpet " ,
. rating = 250 ,
. read = read_hpet ,
. mask = HPET_MASK ,
. shift = HPET_SHIFT ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
2007-10-12 23:04:23 +02:00
. resume = hpet_restart_counter ,
2007-10-12 23:04:06 +02:00
# ifdef CONFIG_X86_64
. vread = vread_hpet ,
# endif
2007-03-05 00:30:50 -08:00
} ;
2007-10-12 23:04:23 +02:00
static int hpet_clocksource_register ( void )
2007-02-16 01:28:04 -08:00
{
x86: clean up computation of HPET .mult variables
While reading through the HPET code I realized that the
computation of .mult variables could be done with less
lines of code, resulting in a 1.6% text size saving
for hpet.o
So I propose the following patch, which applies against
today's Linus -git tree.
>From 0c6507e400e9ca5f7f14331e18f8c12baf75a9d3 Mon Sep 17 00:00:00 2001
From: Carlos R. Mafra <crmafra@ift.unesp.br>
Date: Mon, 5 May 2008 19:38:53 -0300
The computation of clocksource_hpet.mult
tmp = (u64)hpet_period << HPET_SHIFT;
do_div(tmp, FSEC_PER_NSEC);
clocksource_hpet.mult = (u32)tmp;
can be streamlined if we note that it is equal to
clocksource_hpet.mult = div_sc(hpet_period, FSEC_PER_NSEC, HPET_SHIFT);
Furthermore, the computation of hpet_clockevent.mult
uint64_t hpet_freq;
hpet_freq = 1000000000000000ULL;
do_div(hpet_freq, hpet_period);
hpet_clockevent.mult = div_sc((unsigned long) hpet_freq,
NSEC_PER_SEC, hpet_clockevent.shift);
can also be streamlined with the observation that hpet_period and hpet_freq are
inverse to each other (in proper units).
So instead of computing hpet_freq and using (schematically)
div_sc(hpet_freq, 10^9, shift) we use the trick of calling with the
arguments in reverse order, div_sc(10^6, hpet_period, shift).
The different power of ten is due to frequency being in Hertz (1/sec)
and the period being in units of femtosecond. Explicitly,
mult = (hpet_freq * 2^shift)/10^9 (before)
mult = (10^6 * 2^shift)/hpet_period (after)
because hpet_freq = 10^15/hpet_period.
The comments in the code are also updated to reflect the changes.
As a result,
text data bss dec hex filename
2957 425 92 3474 d92 arch/x86/kernel/hpet.o
3006 425 92 3523 dc3 arch/x86/kernel/hpet.o.old
a 1.6% reduction in text size.
Signed-off-by: Carlos R. Mafra <crmafra@ift.unesp.br>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2008-05-05 20:11:22 -03:00
u64 start , now ;
2007-07-21 17:11:12 +02:00
cycle_t t1 ;
2007-02-16 01:28:04 -08:00
/* Start the counter */
hpet_start_counter ( ) ;
2007-07-21 17:11:12 +02:00
/* Verify whether hpet counter works */
t1 = read_hpet ( ) ;
rdtscll ( start ) ;
/*
* We don ' t know the TSC frequency yet , but waiting for
* 200000 TSC cycles is safe :
* 4 GHz = = 50u s
* 1 GHz = = 200u s
*/
do {
rep_nop ( ) ;
rdtscll ( now ) ;
} while ( ( now - start ) < 200000UL ) ;
if ( t1 = = read_hpet ( ) ) {
printk ( KERN_WARNING
" HPET counter not counting. HPET disabled \n " ) ;
2007-10-12 23:04:23 +02:00
return - ENODEV ;
2007-07-21 17:11:12 +02:00
}
x86: clean up computation of HPET .mult variables
While reading through the HPET code I realized that the
computation of .mult variables could be done with less
lines of code, resulting in a 1.6% text size saving
for hpet.o
So I propose the following patch, which applies against
today's Linus -git tree.
>From 0c6507e400e9ca5f7f14331e18f8c12baf75a9d3 Mon Sep 17 00:00:00 2001
From: Carlos R. Mafra <crmafra@ift.unesp.br>
Date: Mon, 5 May 2008 19:38:53 -0300
The computation of clocksource_hpet.mult
tmp = (u64)hpet_period << HPET_SHIFT;
do_div(tmp, FSEC_PER_NSEC);
clocksource_hpet.mult = (u32)tmp;
can be streamlined if we note that it is equal to
clocksource_hpet.mult = div_sc(hpet_period, FSEC_PER_NSEC, HPET_SHIFT);
Furthermore, the computation of hpet_clockevent.mult
uint64_t hpet_freq;
hpet_freq = 1000000000000000ULL;
do_div(hpet_freq, hpet_period);
hpet_clockevent.mult = div_sc((unsigned long) hpet_freq,
NSEC_PER_SEC, hpet_clockevent.shift);
can also be streamlined with the observation that hpet_period and hpet_freq are
inverse to each other (in proper units).
So instead of computing hpet_freq and using (schematically)
div_sc(hpet_freq, 10^9, shift) we use the trick of calling with the
arguments in reverse order, div_sc(10^6, hpet_period, shift).
The different power of ten is due to frequency being in Hertz (1/sec)
and the period being in units of femtosecond. Explicitly,
mult = (hpet_freq * 2^shift)/10^9 (before)
mult = (10^6 * 2^shift)/hpet_period (after)
because hpet_freq = 10^15/hpet_period.
The comments in the code are also updated to reflect the changes.
As a result,
text data bss dec hex filename
2957 425 92 3474 d92 arch/x86/kernel/hpet.o
3006 425 92 3523 dc3 arch/x86/kernel/hpet.o.old
a 1.6% reduction in text size.
Signed-off-by: Carlos R. Mafra <crmafra@ift.unesp.br>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2008-05-05 20:11:22 -03:00
/*
* The definition of mult is ( include / linux / clocksource . h )
* mult / 2 ^ shift = ns / cyc and hpet_period is in units of fsec / cyc
* so we first need to convert hpet_period to ns / cyc units :
* mult / 2 ^ shift = ns / cyc = hpet_period / 10 ^ 6
* mult = ( hpet_period * 2 ^ shift ) / 10 ^ 6
* mult = ( hpet_period < < shift ) / FSEC_PER_NSEC
2007-03-05 00:30:50 -08:00
*/
x86: clean up computation of HPET .mult variables
While reading through the HPET code I realized that the
computation of .mult variables could be done with less
lines of code, resulting in a 1.6% text size saving
for hpet.o
So I propose the following patch, which applies against
today's Linus -git tree.
>From 0c6507e400e9ca5f7f14331e18f8c12baf75a9d3 Mon Sep 17 00:00:00 2001
From: Carlos R. Mafra <crmafra@ift.unesp.br>
Date: Mon, 5 May 2008 19:38:53 -0300
The computation of clocksource_hpet.mult
tmp = (u64)hpet_period << HPET_SHIFT;
do_div(tmp, FSEC_PER_NSEC);
clocksource_hpet.mult = (u32)tmp;
can be streamlined if we note that it is equal to
clocksource_hpet.mult = div_sc(hpet_period, FSEC_PER_NSEC, HPET_SHIFT);
Furthermore, the computation of hpet_clockevent.mult
uint64_t hpet_freq;
hpet_freq = 1000000000000000ULL;
do_div(hpet_freq, hpet_period);
hpet_clockevent.mult = div_sc((unsigned long) hpet_freq,
NSEC_PER_SEC, hpet_clockevent.shift);
can also be streamlined with the observation that hpet_period and hpet_freq are
inverse to each other (in proper units).
So instead of computing hpet_freq and using (schematically)
div_sc(hpet_freq, 10^9, shift) we use the trick of calling with the
arguments in reverse order, div_sc(10^6, hpet_period, shift).
The different power of ten is due to frequency being in Hertz (1/sec)
and the period being in units of femtosecond. Explicitly,
mult = (hpet_freq * 2^shift)/10^9 (before)
mult = (10^6 * 2^shift)/hpet_period (after)
because hpet_freq = 10^15/hpet_period.
The comments in the code are also updated to reflect the changes.
As a result,
text data bss dec hex filename
2957 425 92 3474 d92 arch/x86/kernel/hpet.o
3006 425 92 3523 dc3 arch/x86/kernel/hpet.o.old
a 1.6% reduction in text size.
Signed-off-by: Carlos R. Mafra <crmafra@ift.unesp.br>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2008-05-05 20:11:22 -03:00
clocksource_hpet . mult = div_sc ( hpet_period , FSEC_PER_NSEC , HPET_SHIFT ) ;
2007-03-05 00:30:50 -08:00
clocksource_register ( & clocksource_hpet ) ;
2007-10-12 23:04:23 +02:00
return 0 ;
}
2008-02-05 00:48:13 +01:00
/**
* hpet_enable - Try to setup the HPET timer . Returns 1 on success .
2007-10-12 23:04:23 +02:00
*/
int __init hpet_enable ( void )
{
unsigned long id ;
2008-08-14 12:17:06 +02:00
int i ;
2007-10-12 23:04:23 +02:00
if ( ! is_hpet_capable ( ) )
return 0 ;
hpet_set_mapping ( ) ;
/*
* Read the period and check for a sane value :
*/
hpet_period = hpet_readl ( HPET_PERIOD ) ;
2008-08-14 12:17:06 +02:00
/*
* AMD SB700 based systems with spread spectrum enabled use a
* SMM based HPET emulation to provide proper frequency
* setting . The SMM code is initialized with the first HPET
* register access and takes some time to complete . During
* this time the config register reads 0xffffffff . We check
* for max . 1000 loops whether the config register reads a non
* 0xffffffff value to make sure that HPET is up and running
* before we go further . A counting loop is safe , as the HPET
* access takes thousands of CPU cycles . On non SB700 based
* machines this check is only done once and has no side
* effects .
*/
for ( i = 0 ; hpet_readl ( HPET_CFG ) = = 0xFFFFFFFF ; i + + ) {
if ( i = = 1000 ) {
printk ( KERN_WARNING
" HPET config register value = 0xFFFFFFFF. "
" Disabling HPET \n " ) ;
goto out_nohpet ;
}
}
2007-10-12 23:04:23 +02:00
if ( hpet_period < HPET_MIN_PERIOD | | hpet_period > HPET_MAX_PERIOD )
goto out_nohpet ;
/*
* Read the HPET ID register to retrieve the IRQ routing
* information and the number of channels
*/
id = hpet_readl ( HPET_ID ) ;
# ifdef CONFIG_HPET_EMULATE_RTC
/*
* The legacy routing mode needs at least two channels , tick timer
* and the rtc emulation channel .
*/
if ( ! ( id & HPET_ID_NUMBER ) )
goto out_nohpet ;
# endif
if ( hpet_clocksource_register ( ) )
goto out_nohpet ;
2007-02-16 01:28:04 -08:00
if ( id & HPET_ID_LEGSUP ) {
2007-10-12 23:04:23 +02:00
hpet_legacy_clockevent_register ( ) ;
2008-09-05 18:02:18 -07:00
hpet_msi_capability_lookup ( 2 ) ;
2007-02-16 01:28:04 -08:00
return 1 ;
}
2008-09-05 18:02:18 -07:00
hpet_msi_capability_lookup ( 0 ) ;
2007-02-16 01:28:04 -08:00
return 0 ;
2006-06-26 00:25:12 -07:00
2007-02-16 01:28:04 -08:00
out_nohpet :
2007-10-12 23:04:06 +02:00
hpet_clear_mapping ( ) ;
2007-03-29 15:46:48 +02:00
boot_hpet_disable = 1 ;
2007-02-16 01:28:04 -08:00
return 0 ;
}
2007-10-12 23:04:06 +02:00
/*
* Needs to be late , as the reserve_timer code calls kalloc !
*
* Not a problem on i386 as hpet_enable is called from late_time_init ,
* but on x86_64 it is necessary !
*/
static __init int hpet_late_init ( void )
{
2008-09-05 18:02:18 -07:00
int cpu ;
2007-10-12 23:04:23 +02:00
if ( boot_hpet_disable )
2007-10-12 23:04:06 +02:00
return - ENODEV ;
2007-10-12 23:04:23 +02:00
if ( ! hpet_address ) {
if ( ! force_hpet_address )
return - ENODEV ;
hpet_address = force_hpet_address ;
hpet_enable ( ) ;
if ( ! hpet_virt_address )
return - ENODEV ;
}
2007-10-12 23:04:06 +02:00
hpet_reserve_platform_timers ( hpet_readl ( HPET_ID ) ) ;
2007-10-12 23:04:23 +02:00
2008-09-05 18:02:18 -07:00
for_each_online_cpu ( cpu ) {
hpet_cpuhp_notify ( NULL , CPU_ONLINE , ( void * ) ( long ) cpu ) ;
}
/* This notifier should be called after workqueue is ready */
hotcpu_notifier ( hpet_cpuhp_notify , - 20 ) ;
2007-10-12 23:04:06 +02:00
return 0 ;
}
fs_initcall ( hpet_late_init ) ;
2007-12-03 17:17:10 +01:00
void hpet_disable ( void )
{
if ( is_hpet_capable ( ) ) {
unsigned long cfg = hpet_readl ( HPET_CFG ) ;
if ( hpet_legacy_int_enabled ) {
cfg & = ~ HPET_CFG_LEGACY ;
hpet_legacy_int_enabled = 0 ;
}
cfg & = ~ HPET_CFG_ENABLE ;
hpet_writel ( cfg , HPET_CFG ) ;
}
}
2007-02-16 01:28:04 -08:00
# ifdef CONFIG_HPET_EMULATE_RTC
/* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET
* is enabled , we support RTC interrupt functionality in software .
* RTC has 3 kinds of interrupts :
* 1 ) Update Interrupt - generate an interrupt , every sec , when RTC clock
* is updated
* 2 ) Alarm Interrupt - generate an interrupt at a specific time of day
* 3 ) Periodic Interrupt - generate periodic interrupt , with frequencies
* 2 Hz - 8192 Hz ( 2 Hz - 64 Hz for non - root user ) ( all freqs in powers of 2 )
* ( 1 ) and ( 2 ) above are implemented using polling at a frequency of
* 64 Hz . The exact frequency is a tradeoff between accuracy and interrupt
* overhead . ( DEFAULT_RTC_INT_FREQ )
* For ( 3 ) , we use interrupts at 64 Hz or user specified periodic
* frequency , whichever is higher .
*/
# include <linux/mc146818rtc.h>
# include <linux/rtc.h>
2008-01-30 13:33:28 +01:00
# include <asm/rtc.h>
2007-02-16 01:28:04 -08:00
# define DEFAULT_RTC_INT_FREQ 64
# define DEFAULT_RTC_SHIFT 6
# define RTC_NUM_INTS 1
static unsigned long hpet_rtc_flags ;
2008-07-23 21:30:47 -07:00
static int hpet_prev_update_sec ;
2007-02-16 01:28:04 -08:00
static struct rtc_time hpet_alarm_time ;
static unsigned long hpet_pie_count ;
static unsigned long hpet_t1_cmp ;
static unsigned long hpet_default_delta ;
static unsigned long hpet_pie_delta ;
static unsigned long hpet_pie_limit ;
2008-01-30 13:33:28 +01:00
static rtc_irq_handler irq_handler ;
/*
* Registers a IRQ handler .
*/
int hpet_register_irq_handler ( rtc_irq_handler handler )
{
if ( ! is_hpet_enabled ( ) )
return - ENODEV ;
if ( irq_handler )
return - EBUSY ;
irq_handler = handler ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( hpet_register_irq_handler ) ;
/*
* Deregisters the IRQ handler registered with hpet_register_irq_handler ( )
* and does cleanup .
*/
void hpet_unregister_irq_handler ( rtc_irq_handler handler )
{
if ( ! is_hpet_enabled ( ) )
return ;
irq_handler = NULL ;
hpet_rtc_flags = 0 ;
}
EXPORT_SYMBOL_GPL ( hpet_unregister_irq_handler ) ;
2007-02-16 01:28:04 -08:00
/*
* Timer 1 for RTC emulation . We use one shot mode , as periodic mode
* is not supported by all HPET implementations for timer 1.
*
* hpet_rtc_timer_init ( ) is called when the rtc is initialized .
*/
int hpet_rtc_timer_init ( void )
{
unsigned long cfg , cnt , delta , flags ;
if ( ! is_hpet_enabled ( ) )
return 0 ;
if ( ! hpet_default_delta ) {
uint64_t clc ;
clc = ( uint64_t ) hpet_clockevent . mult * NSEC_PER_SEC ;
clc > > = hpet_clockevent . shift + DEFAULT_RTC_SHIFT ;
hpet_default_delta = ( unsigned long ) clc ;
}
if ( ! ( hpet_rtc_flags & RTC_PIE ) | | hpet_pie_limit )
delta = hpet_default_delta ;
else
delta = hpet_pie_delta ;
local_irq_save ( flags ) ;
cnt = delta + hpet_readl ( HPET_COUNTER ) ;
hpet_writel ( cnt , HPET_T1_CMP ) ;
hpet_t1_cmp = cnt ;
cfg = hpet_readl ( HPET_T1_CFG ) ;
cfg & = ~ HPET_TN_PERIODIC ;
cfg | = HPET_TN_ENABLE | HPET_TN_32BIT ;
hpet_writel ( cfg , HPET_T1_CFG ) ;
local_irq_restore ( flags ) ;
return 1 ;
}
2008-01-30 13:33:28 +01:00
EXPORT_SYMBOL_GPL ( hpet_rtc_timer_init ) ;
2007-02-16 01:28:04 -08:00
/*
* The functions below are called from rtc driver .
* Return 0 if HPET is not being used .
* Otherwise do the necessary changes and return 1.
*/
int hpet_mask_rtc_irq_bit ( unsigned long bit_mask )
{
if ( ! is_hpet_enabled ( ) )
return 0 ;
hpet_rtc_flags & = ~ bit_mask ;
return 1 ;
}
2008-01-30 13:33:28 +01:00
EXPORT_SYMBOL_GPL ( hpet_mask_rtc_irq_bit ) ;
2007-02-16 01:28:04 -08:00
int hpet_set_rtc_irq_bit ( unsigned long bit_mask )
{
unsigned long oldbits = hpet_rtc_flags ;
if ( ! is_hpet_enabled ( ) )
return 0 ;
hpet_rtc_flags | = bit_mask ;
2008-07-23 21:30:47 -07:00
if ( ( bit_mask & RTC_UIE ) & & ! ( oldbits & RTC_UIE ) )
hpet_prev_update_sec = - 1 ;
2007-02-16 01:28:04 -08:00
if ( ! oldbits )
hpet_rtc_timer_init ( ) ;
return 1 ;
}
2008-01-30 13:33:28 +01:00
EXPORT_SYMBOL_GPL ( hpet_set_rtc_irq_bit ) ;
2007-02-16 01:28:04 -08:00
int hpet_set_alarm_time ( unsigned char hrs , unsigned char min ,
unsigned char sec )
{
if ( ! is_hpet_enabled ( ) )
return 0 ;
hpet_alarm_time . tm_hour = hrs ;
hpet_alarm_time . tm_min = min ;
hpet_alarm_time . tm_sec = sec ;
return 1 ;
}
2008-01-30 13:33:28 +01:00
EXPORT_SYMBOL_GPL ( hpet_set_alarm_time ) ;
2007-02-16 01:28:04 -08:00
int hpet_set_periodic_freq ( unsigned long freq )
{
uint64_t clc ;
if ( ! is_hpet_enabled ( ) )
return 0 ;
if ( freq < = DEFAULT_RTC_INT_FREQ )
hpet_pie_limit = DEFAULT_RTC_INT_FREQ / freq ;
else {
clc = ( uint64_t ) hpet_clockevent . mult * NSEC_PER_SEC ;
do_div ( clc , freq ) ;
clc > > = hpet_clockevent . shift ;
hpet_pie_delta = ( unsigned long ) clc ;
}
return 1 ;
}
2008-01-30 13:33:28 +01:00
EXPORT_SYMBOL_GPL ( hpet_set_periodic_freq ) ;
2007-02-16 01:28:04 -08:00
int hpet_rtc_dropped_irq ( void )
{
return is_hpet_enabled ( ) ;
}
2008-01-30 13:33:28 +01:00
EXPORT_SYMBOL_GPL ( hpet_rtc_dropped_irq ) ;
2007-02-16 01:28:04 -08:00
static void hpet_rtc_timer_reinit ( void )
{
unsigned long cfg , delta ;
int lost_ints = - 1 ;
if ( unlikely ( ! hpet_rtc_flags ) ) {
cfg = hpet_readl ( HPET_T1_CFG ) ;
cfg & = ~ HPET_TN_ENABLE ;
hpet_writel ( cfg , HPET_T1_CFG ) ;
return ;
}
if ( ! ( hpet_rtc_flags & RTC_PIE ) | | hpet_pie_limit )
delta = hpet_default_delta ;
else
delta = hpet_pie_delta ;
/*
* Increment the comparator value until we are ahead of the
* current count .
*/
do {
hpet_t1_cmp + = delta ;
hpet_writel ( hpet_t1_cmp , HPET_T1_CMP ) ;
lost_ints + + ;
} while ( ( long ) ( hpet_readl ( HPET_COUNTER ) - hpet_t1_cmp ) > 0 ) ;
if ( lost_ints ) {
if ( hpet_rtc_flags & RTC_PIE )
hpet_pie_count + = lost_ints ;
if ( printk_ratelimit ( ) )
2008-07-23 21:30:47 -07:00
printk ( KERN_WARNING " hpet1: lost %d rtc interrupts \n " ,
2007-02-16 01:28:04 -08:00
lost_ints ) ;
}
}
irqreturn_t hpet_rtc_interrupt ( int irq , void * dev_id )
{
struct rtc_time curr_time ;
unsigned long rtc_int_flag = 0 ;
hpet_rtc_timer_reinit ( ) ;
2008-01-30 13:33:28 +01:00
memset ( & curr_time , 0 , sizeof ( struct rtc_time ) ) ;
2007-02-16 01:28:04 -08:00
if ( hpet_rtc_flags & ( RTC_UIE | RTC_AIE ) )
2008-01-30 13:33:28 +01:00
get_rtc_time ( & curr_time ) ;
2007-02-16 01:28:04 -08:00
if ( hpet_rtc_flags & RTC_UIE & &
curr_time . tm_sec ! = hpet_prev_update_sec ) {
2008-07-23 21:30:47 -07:00
if ( hpet_prev_update_sec > = 0 )
rtc_int_flag = RTC_UF ;
2007-02-16 01:28:04 -08:00
hpet_prev_update_sec = curr_time . tm_sec ;
}
if ( hpet_rtc_flags & RTC_PIE & &
+ + hpet_pie_count > = hpet_pie_limit ) {
rtc_int_flag | = RTC_PF ;
hpet_pie_count = 0 ;
}
2008-01-15 16:44:38 +01:00
if ( hpet_rtc_flags & RTC_AIE & &
2007-02-16 01:28:04 -08:00
( curr_time . tm_sec = = hpet_alarm_time . tm_sec ) & &
( curr_time . tm_min = = hpet_alarm_time . tm_min ) & &
( curr_time . tm_hour = = hpet_alarm_time . tm_hour ) )
rtc_int_flag | = RTC_AF ;
if ( rtc_int_flag ) {
rtc_int_flag | = ( RTC_IRQF | ( RTC_NUM_INTS < < 8 ) ) ;
2008-01-30 13:33:28 +01:00
if ( irq_handler )
irq_handler ( rtc_int_flag , dev_id ) ;
2007-02-16 01:28:04 -08:00
}
return IRQ_HANDLED ;
}
2008-01-30 13:33:28 +01:00
EXPORT_SYMBOL_GPL ( hpet_rtc_interrupt ) ;
2007-02-16 01:28:04 -08:00
# endif