2005-04-17 02:20:36 +04:00
/*
* Intel & MS High Precision Event Timer Implementation .
*
* Copyright ( C ) 2003 Intel Corporation
* Venki Pallipadi
* ( c ) Copyright 2004 Hewlett - Packard Development Company , L . P .
* Bob Picco < robert . picco @ hp . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/config.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/miscdevice.h>
# include <linux/major.h>
# include <linux/ioport.h>
# include <linux/fcntl.h>
# include <linux/init.h>
# include <linux/poll.h>
# include <linux/proc_fs.h>
# include <linux/spinlock.h>
# include <linux/sysctl.h>
# include <linux/wait.h>
# include <linux/bcd.h>
# include <linux/seq_file.h>
# include <linux/bitops.h>
# include <asm/current.h>
# include <asm/uaccess.h>
# include <asm/system.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/div64.h>
# include <linux/acpi.h>
# include <acpi/acpi_bus.h>
# include <linux/hpet.h>
/*
* The High Precision Event Timer driver .
* This driver is closely modelled after the rtc . c driver .
* http : //www.intel.com/labs/platcomp/hpet/hpetspec.htm
*/
# define HPET_USER_FREQ (64)
# define HPET_DRIFT (500)
static u32 hpet_ntimer , hpet_nhpet , hpet_max_freq = HPET_USER_FREQ ;
/* A lock for concurrent access by app and isr hpet activity. */
static DEFINE_SPINLOCK ( hpet_lock ) ;
/* A lock for concurrent intermodule access to hpet and isr hpet activity. */
static DEFINE_SPINLOCK ( hpet_task_lock ) ;
# define HPET_DEV_NAME (7)
struct hpet_dev {
struct hpets * hd_hpets ;
struct hpet __iomem * hd_hpet ;
struct hpet_timer __iomem * hd_timer ;
unsigned long hd_ireqfreq ;
unsigned long hd_irqdata ;
wait_queue_head_t hd_waitqueue ;
struct fasync_struct * hd_async_queue ;
struct hpet_task * hd_task ;
unsigned int hd_flags ;
unsigned int hd_irq ;
unsigned int hd_hdwirq ;
char hd_name [ HPET_DEV_NAME ] ;
} ;
struct hpets {
struct hpets * hp_next ;
struct hpet __iomem * hp_hpet ;
unsigned long hp_hpet_phys ;
struct time_interpolator * hp_interpolator ;
unsigned long hp_period ;
unsigned long hp_delta ;
unsigned int hp_ntimer ;
unsigned int hp_which ;
struct hpet_dev hp_dev [ 1 ] ;
} ;
static struct hpets * hpets ;
# define HPET_OPEN 0x0001
# define HPET_IE 0x0002 /* interrupt enabled */
# define HPET_PERIODIC 0x0004
# if BITS_PER_LONG == 64
# define write_counter(V, MC) writeq(V, MC)
# define read_counter(MC) readq(MC)
# else
# define write_counter(V, MC) writel(V, MC)
# define read_counter(MC) readl(MC)
# endif
# ifndef readq
static unsigned long long __inline readq ( void __iomem * addr )
{
return readl ( addr ) | ( ( ( unsigned long long ) readl ( addr + 4 ) ) < < 32LL ) ;
}
# endif
# ifndef writeq
static void __inline writeq ( unsigned long long v , void __iomem * addr )
{
writel ( v & 0xffffffff , addr ) ;
writel ( v > > 32 , addr + 4 ) ;
}
# endif
static irqreturn_t hpet_interrupt ( int irq , void * data , struct pt_regs * regs )
{
struct hpet_dev * devp ;
unsigned long isr ;
devp = data ;
spin_lock ( & hpet_lock ) ;
devp - > hd_irqdata + + ;
/*
* For non - periodic timers , increment the accumulator .
* This has the effect of treating non - periodic like periodic .
*/
if ( ( devp - > hd_flags & ( HPET_IE | HPET_PERIODIC ) ) = = HPET_IE ) {
unsigned long m , t ;
t = devp - > hd_ireqfreq ;
m = read_counter ( & devp - > hd_hpet - > hpet_mc ) ;
write_counter ( t + m + devp - > hd_hpets - > hp_delta ,
& devp - > hd_timer - > hpet_compare ) ;
}
isr = ( 1 < < ( devp - devp - > hd_hpets - > hp_dev ) ) ;
writeq ( isr , & devp - > hd_hpet - > hpet_isr ) ;
spin_unlock ( & hpet_lock ) ;
spin_lock ( & hpet_task_lock ) ;
if ( devp - > hd_task )
devp - > hd_task - > ht_func ( devp - > hd_task - > ht_data ) ;
spin_unlock ( & hpet_task_lock ) ;
wake_up_interruptible ( & devp - > hd_waitqueue ) ;
kill_fasync ( & devp - > hd_async_queue , SIGIO , POLL_IN ) ;
return IRQ_HANDLED ;
}
static int hpet_open ( struct inode * inode , struct file * file )
{
struct hpet_dev * devp ;
struct hpets * hpetp ;
int i ;
if ( file - > f_mode & FMODE_WRITE )
return - EINVAL ;
spin_lock_irq ( & hpet_lock ) ;
for ( devp = NULL , hpetp = hpets ; hpetp & & ! devp ; hpetp = hpetp - > hp_next )
for ( i = 0 ; i < hpetp - > hp_ntimer ; i + + )
if ( hpetp - > hp_dev [ i ] . hd_flags & HPET_OPEN
| | hpetp - > hp_dev [ i ] . hd_task )
continue ;
else {
devp = & hpetp - > hp_dev [ i ] ;
break ;
}
if ( ! devp ) {
spin_unlock_irq ( & hpet_lock ) ;
return - EBUSY ;
}
file - > private_data = devp ;
devp - > hd_irqdata = 0 ;
devp - > hd_flags | = HPET_OPEN ;
spin_unlock_irq ( & hpet_lock ) ;
return 0 ;
}
static ssize_t
hpet_read ( struct file * file , char __user * buf , size_t count , loff_t * ppos )
{
DECLARE_WAITQUEUE ( wait , current ) ;
unsigned long data ;
ssize_t retval ;
struct hpet_dev * devp ;
devp = file - > private_data ;
if ( ! devp - > hd_ireqfreq )
return - EIO ;
if ( count < sizeof ( unsigned long ) )
return - EINVAL ;
add_wait_queue ( & devp - > hd_waitqueue , & wait ) ;
for ( ; ; ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
spin_lock_irq ( & hpet_lock ) ;
data = devp - > hd_irqdata ;
devp - > hd_irqdata = 0 ;
spin_unlock_irq ( & hpet_lock ) ;
if ( data )
break ;
else if ( file - > f_flags & O_NONBLOCK ) {
retval = - EAGAIN ;
goto out ;
} else if ( signal_pending ( current ) ) {
retval = - ERESTARTSYS ;
goto out ;
}
schedule ( ) ;
}
retval = put_user ( data , ( unsigned long __user * ) buf ) ;
if ( ! retval )
retval = sizeof ( unsigned long ) ;
out :
__set_current_state ( TASK_RUNNING ) ;
remove_wait_queue ( & devp - > hd_waitqueue , & wait ) ;
return retval ;
}
static unsigned int hpet_poll ( struct file * file , poll_table * wait )
{
unsigned long v ;
struct hpet_dev * devp ;
devp = file - > private_data ;
if ( ! devp - > hd_ireqfreq )
return 0 ;
poll_wait ( file , & devp - > hd_waitqueue , wait ) ;
spin_lock_irq ( & hpet_lock ) ;
v = devp - > hd_irqdata ;
spin_unlock_irq ( & hpet_lock ) ;
if ( v ! = 0 )
return POLLIN | POLLRDNORM ;
return 0 ;
}
static int hpet_mmap ( struct file * file , struct vm_area_struct * vma )
{
# ifdef CONFIG_HPET_MMAP
struct hpet_dev * devp ;
unsigned long addr ;
if ( ( ( vma - > vm_end - vma - > vm_start ) ! = PAGE_SIZE ) | | vma - > vm_pgoff )
return - EINVAL ;
devp = file - > private_data ;
addr = devp - > hd_hpets - > hp_hpet_phys ;
if ( addr & ( PAGE_SIZE - 1 ) )
return - ENOSYS ;
vma - > vm_flags | = VM_IO ;
vma - > vm_page_prot = pgprot_noncached ( vma - > vm_page_prot ) ;
addr = __pa ( addr ) ;
if ( io_remap_pfn_range ( vma , vma - > vm_start , addr > > PAGE_SHIFT ,
PAGE_SIZE , vma - > vm_page_prot ) ) {
printk ( KERN_ERR " remap_pfn_range failed in hpet.c \n " ) ;
return - EAGAIN ;
}
return 0 ;
# else
return - ENOSYS ;
# endif
}
static int hpet_fasync ( int fd , struct file * file , int on )
{
struct hpet_dev * devp ;
devp = file - > private_data ;
if ( fasync_helper ( fd , file , on , & devp - > hd_async_queue ) > = 0 )
return 0 ;
else
return - EIO ;
}
static int hpet_release ( struct inode * inode , struct file * file )
{
struct hpet_dev * devp ;
struct hpet_timer __iomem * timer ;
int irq = 0 ;
devp = file - > private_data ;
timer = devp - > hd_timer ;
spin_lock_irq ( & hpet_lock ) ;
writeq ( ( readq ( & timer - > hpet_config ) & ~ Tn_INT_ENB_CNF_MASK ) ,
& timer - > hpet_config ) ;
irq = devp - > hd_irq ;
devp - > hd_irq = 0 ;
devp - > hd_ireqfreq = 0 ;
if ( devp - > hd_flags & HPET_PERIODIC
& & readq ( & timer - > hpet_config ) & Tn_TYPE_CNF_MASK ) {
unsigned long v ;
v = readq ( & timer - > hpet_config ) ;
v ^ = Tn_TYPE_CNF_MASK ;
writeq ( v , & timer - > hpet_config ) ;
}
devp - > hd_flags & = ~ ( HPET_OPEN | HPET_IE | HPET_PERIODIC ) ;
spin_unlock_irq ( & hpet_lock ) ;
if ( irq )
free_irq ( irq , devp ) ;
if ( file - > f_flags & FASYNC )
hpet_fasync ( - 1 , file , 0 ) ;
file - > private_data = NULL ;
return 0 ;
}
static int hpet_ioctl_common ( struct hpet_dev * , int , unsigned long , int ) ;
static int
hpet_ioctl ( struct inode * inode , struct file * file , unsigned int cmd ,
unsigned long arg )
{
struct hpet_dev * devp ;
devp = file - > private_data ;
return hpet_ioctl_common ( devp , cmd , arg , 0 ) ;
}
static int hpet_ioctl_ieon ( struct hpet_dev * devp )
{
struct hpet_timer __iomem * timer ;
struct hpet __iomem * hpet ;
struct hpets * hpetp ;
int irq ;
unsigned long g , v , t , m ;
unsigned long flags , isr ;
timer = devp - > hd_timer ;
hpet = devp - > hd_hpet ;
hpetp = devp - > hd_hpets ;
v = readq ( & timer - > hpet_config ) ;
spin_lock_irq ( & hpet_lock ) ;
if ( devp - > hd_flags & HPET_IE ) {
spin_unlock_irq ( & hpet_lock ) ;
return - EBUSY ;
}
devp - > hd_flags | = HPET_IE ;
spin_unlock_irq ( & hpet_lock ) ;
t = readq ( & timer - > hpet_config ) ;
irq = devp - > hd_hdwirq ;
if ( irq ) {
sprintf ( devp - > hd_name , " hpet%d " , ( int ) ( devp - hpetp - > hp_dev ) ) ;
if ( request_irq
( irq , hpet_interrupt , SA_INTERRUPT , devp - > hd_name , ( void * ) devp ) ) {
printk ( KERN_ERR " hpet: IRQ %d is not free \n " , irq ) ;
irq = 0 ;
}
}
if ( irq = = 0 ) {
spin_lock_irq ( & hpet_lock ) ;
devp - > hd_flags ^ = HPET_IE ;
spin_unlock_irq ( & hpet_lock ) ;
return - EIO ;
}
devp - > hd_irq = irq ;
t = devp - > hd_ireqfreq ;
v = readq ( & timer - > hpet_config ) ;
g = v | Tn_INT_ENB_CNF_MASK ;
if ( devp - > hd_flags & HPET_PERIODIC ) {
write_counter ( t , & timer - > hpet_compare ) ;
g | = Tn_TYPE_CNF_MASK ;
v | = Tn_TYPE_CNF_MASK ;
writeq ( v , & timer - > hpet_config ) ;
v | = Tn_VAL_SET_CNF_MASK ;
writeq ( v , & timer - > hpet_config ) ;
local_irq_save ( flags ) ;
m = read_counter ( & hpet - > hpet_mc ) ;
write_counter ( t + m + hpetp - > hp_delta , & timer - > hpet_compare ) ;
} else {
local_irq_save ( flags ) ;
m = read_counter ( & hpet - > hpet_mc ) ;
write_counter ( t + m + hpetp - > hp_delta , & timer - > hpet_compare ) ;
}
isr = ( 1 < < ( devp - hpets - > hp_dev ) ) ;
writeq ( isr , & hpet - > hpet_isr ) ;
writeq ( g , & timer - > hpet_config ) ;
local_irq_restore ( flags ) ;
return 0 ;
}
static inline unsigned long hpet_time_div ( unsigned long dis )
{
unsigned long long m = 1000000000000000ULL ;
do_div ( m , dis ) ;
return ( unsigned long ) m ;
}
static int
hpet_ioctl_common ( struct hpet_dev * devp , int cmd , unsigned long arg , int kernel )
{
struct hpet_timer __iomem * timer ;
struct hpet __iomem * hpet ;
struct hpets * hpetp ;
int err ;
unsigned long v ;
switch ( cmd ) {
case HPET_IE_OFF :
case HPET_INFO :
case HPET_EPI :
case HPET_DPI :
case HPET_IRQFREQ :
timer = devp - > hd_timer ;
hpet = devp - > hd_hpet ;
hpetp = devp - > hd_hpets ;
break ;
case HPET_IE_ON :
return hpet_ioctl_ieon ( devp ) ;
default :
return - EINVAL ;
}
err = 0 ;
switch ( cmd ) {
case HPET_IE_OFF :
if ( ( devp - > hd_flags & HPET_IE ) = = 0 )
break ;
v = readq ( & timer - > hpet_config ) ;
v & = ~ Tn_INT_ENB_CNF_MASK ;
writeq ( v , & timer - > hpet_config ) ;
if ( devp - > hd_irq ) {
free_irq ( devp - > hd_irq , devp ) ;
devp - > hd_irq = 0 ;
}
devp - > hd_flags ^ = HPET_IE ;
break ;
case HPET_INFO :
{
struct hpet_info info ;
info . hi_ireqfreq = hpet_time_div ( hpetp - > hp_period *
devp - > hd_ireqfreq ) ;
info . hi_flags =
readq ( & timer - > hpet_config ) & Tn_PER_INT_CAP_MASK ;
info . hi_hpet = devp - > hd_hpets - > hp_which ;
info . hi_timer = devp - devp - > hd_hpets - > hp_dev ;
if ( copy_to_user ( ( void __user * ) arg , & info , sizeof ( info ) ) )
err = - EFAULT ;
break ;
}
case HPET_EPI :
v = readq ( & timer - > hpet_config ) ;
if ( ( v & Tn_PER_INT_CAP_MASK ) = = 0 ) {
err = - ENXIO ;
break ;
}
devp - > hd_flags | = HPET_PERIODIC ;
break ;
case HPET_DPI :
v = readq ( & timer - > hpet_config ) ;
if ( ( v & Tn_PER_INT_CAP_MASK ) = = 0 ) {
err = - ENXIO ;
break ;
}
if ( devp - > hd_flags & HPET_PERIODIC & &
readq ( & timer - > hpet_config ) & Tn_TYPE_CNF_MASK ) {
v = readq ( & timer - > hpet_config ) ;
v ^ = Tn_TYPE_CNF_MASK ;
writeq ( v , & timer - > hpet_config ) ;
}
devp - > hd_flags & = ~ HPET_PERIODIC ;
break ;
case HPET_IRQFREQ :
if ( ! kernel & & ( arg > hpet_max_freq ) & &
! capable ( CAP_SYS_RESOURCE ) ) {
err = - EACCES ;
break ;
}
if ( arg & ( arg - 1 ) ) {
err = - EINVAL ;
break ;
}
devp - > hd_ireqfreq = hpet_time_div ( hpetp - > hp_period * arg ) ;
}
return err ;
}
static struct file_operations hpet_fops = {
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. read = hpet_read ,
. poll = hpet_poll ,
. ioctl = hpet_ioctl ,
. open = hpet_open ,
. release = hpet_release ,
. fasync = hpet_fasync ,
. mmap = hpet_mmap ,
} ;
EXPORT_SYMBOL ( hpet_alloc ) ;
EXPORT_SYMBOL ( hpet_register ) ;
EXPORT_SYMBOL ( hpet_unregister ) ;
EXPORT_SYMBOL ( hpet_control ) ;
int hpet_register ( struct hpet_task * tp , int periodic )
{
unsigned int i ;
u64 mask ;
struct hpet_timer __iomem * timer ;
struct hpet_dev * devp ;
struct hpets * hpetp ;
switch ( periodic ) {
case 1 :
mask = Tn_PER_INT_CAP_MASK ;
break ;
case 0 :
mask = 0 ;
break ;
default :
return - EINVAL ;
}
spin_lock_irq ( & hpet_task_lock ) ;
spin_lock ( & hpet_lock ) ;
for ( devp = NULL , hpetp = hpets ; hpetp & & ! devp ; hpetp = hpetp - > hp_next )
for ( timer = hpetp - > hp_hpet - > hpet_timers , i = 0 ;
i < hpetp - > hp_ntimer ; i + + , timer + + ) {
if ( ( readq ( & timer - > hpet_config ) & Tn_PER_INT_CAP_MASK )
! = mask )
continue ;
devp = & hpetp - > hp_dev [ i ] ;
if ( devp - > hd_flags & HPET_OPEN | | devp - > hd_task ) {
devp = NULL ;
continue ;
}
tp - > ht_opaque = devp ;
devp - > hd_task = tp ;
break ;
}
spin_unlock ( & hpet_lock ) ;
spin_unlock_irq ( & hpet_task_lock ) ;
if ( tp - > ht_opaque )
return 0 ;
else
return - EBUSY ;
}
static inline int hpet_tpcheck ( struct hpet_task * tp )
{
struct hpet_dev * devp ;
struct hpets * hpetp ;
devp = tp - > ht_opaque ;
if ( ! devp )
return - ENXIO ;
for ( hpetp = hpets ; hpetp ; hpetp = hpetp - > hp_next )
if ( devp > = hpetp - > hp_dev
& & devp < ( hpetp - > hp_dev + hpetp - > hp_ntimer )
& & devp - > hd_hpet = = hpetp - > hp_hpet )
return 0 ;
return - ENXIO ;
}
int hpet_unregister ( struct hpet_task * tp )
{
struct hpet_dev * devp ;
struct hpet_timer __iomem * timer ;
int err ;
if ( ( err = hpet_tpcheck ( tp ) ) )
return err ;
spin_lock_irq ( & hpet_task_lock ) ;
spin_lock ( & hpet_lock ) ;
devp = tp - > ht_opaque ;
if ( devp - > hd_task ! = tp ) {
spin_unlock ( & hpet_lock ) ;
spin_unlock_irq ( & hpet_task_lock ) ;
return - ENXIO ;
}
timer = devp - > hd_timer ;
writeq ( ( readq ( & timer - > hpet_config ) & ~ Tn_INT_ENB_CNF_MASK ) ,
& timer - > hpet_config ) ;
devp - > hd_flags & = ~ ( HPET_IE | HPET_PERIODIC ) ;
devp - > hd_task = NULL ;
spin_unlock ( & hpet_lock ) ;
spin_unlock_irq ( & hpet_task_lock ) ;
return 0 ;
}
int hpet_control ( struct hpet_task * tp , unsigned int cmd , unsigned long arg )
{
struct hpet_dev * devp ;
int err ;
if ( ( err = hpet_tpcheck ( tp ) ) )
return err ;
spin_lock_irq ( & hpet_lock ) ;
devp = tp - > ht_opaque ;
if ( devp - > hd_task ! = tp ) {
spin_unlock_irq ( & hpet_lock ) ;
return - ENXIO ;
}
spin_unlock_irq ( & hpet_lock ) ;
return hpet_ioctl_common ( devp , cmd , arg , 1 ) ;
}
static ctl_table hpet_table [ ] = {
{
. ctl_name = 1 ,
. procname = " max-user-freq " ,
. data = & hpet_max_freq ,
. maxlen = sizeof ( int ) ,
. mode = 0644 ,
. proc_handler = & proc_dointvec ,
} ,
{ . ctl_name = 0 }
} ;
static ctl_table hpet_root [ ] = {
{
. ctl_name = 1 ,
. procname = " hpet " ,
. maxlen = 0 ,
. mode = 0555 ,
. child = hpet_table ,
} ,
{ . ctl_name = 0 }
} ;
static ctl_table dev_root [ ] = {
{
. ctl_name = CTL_DEV ,
. procname = " dev " ,
. maxlen = 0 ,
. mode = 0555 ,
. child = hpet_root ,
} ,
{ . ctl_name = 0 }
} ;
static struct ctl_table_header * sysctl_header ;
static void hpet_register_interpolator ( struct hpets * hpetp )
{
# ifdef CONFIG_TIME_INTERPOLATION
struct time_interpolator * ti ;
ti = kmalloc ( sizeof ( * ti ) , GFP_KERNEL ) ;
if ( ! ti )
return ;
memset ( ti , 0 , sizeof ( * ti ) ) ;
ti - > source = TIME_SOURCE_MMIO64 ;
ti - > shift = 10 ;
ti - > addr = & hpetp - > hp_hpet - > hpet_mc ;
ti - > frequency = hpet_time_div ( hpets - > hp_period ) ;
ti - > drift = ti - > frequency * HPET_DRIFT / 1000000 ;
ti - > mask = - 1 ;
hpetp - > hp_interpolator = ti ;
register_time_interpolator ( ti ) ;
# endif
}
/*
* Adjustment for when arming the timer with
* initial conditions . That is , main counter
* ticks expired before interrupts are enabled .
*/
# define TICK_CALIBRATE (1000UL)
static unsigned long hpet_calibrate ( struct hpets * hpetp )
{
struct hpet_timer __iomem * timer = NULL ;
unsigned long t , m , count , i , flags , start ;
struct hpet_dev * devp ;
int j ;
struct hpet __iomem * hpet ;
for ( j = 0 , devp = hpetp - > hp_dev ; j < hpetp - > hp_ntimer ; j + + , devp + + )
if ( ( devp - > hd_flags & HPET_OPEN ) = = 0 ) {
timer = devp - > hd_timer ;
break ;
}
if ( ! timer )
return 0 ;
hpet = hpets - > hp_hpet ;
t = read_counter ( & timer - > hpet_compare ) ;
i = 0 ;
count = hpet_time_div ( hpetp - > hp_period * TICK_CALIBRATE ) ;
local_irq_save ( flags ) ;
start = read_counter ( & hpet - > hpet_mc ) ;
do {
m = read_counter ( & hpet - > hpet_mc ) ;
write_counter ( t + m + hpetp - > hp_delta , & timer - > hpet_compare ) ;
} while ( i + + , ( m - start ) < count ) ;
local_irq_restore ( flags ) ;
return ( m - start ) / i ;
}
int hpet_alloc ( struct hpet_data * hdp )
{
u64 cap , mcfg ;
struct hpet_dev * devp ;
u32 i , ntimer ;
struct hpets * hpetp ;
size_t siz ;
struct hpet __iomem * hpet ;
static struct hpets * last = ( struct hpets * ) 0 ;
unsigned long ns ;
/*
* hpet_alloc can be called by platform dependent code .
* if platform dependent code has allocated the hpet
* ACPI also reports hpet , then we catch it here .
*/
for ( hpetp = hpets ; hpetp ; hpetp = hpetp - > hp_next )
if ( hpetp - > hp_hpet = = hdp - > hd_address )
return 0 ;
siz = sizeof ( struct hpets ) + ( ( hdp - > hd_nirqs - 1 ) *
sizeof ( struct hpet_dev ) ) ;
hpetp = kmalloc ( siz , GFP_KERNEL ) ;
if ( ! hpetp )
return - ENOMEM ;
memset ( hpetp , 0 , siz ) ;
hpetp - > hp_which = hpet_nhpet + + ;
hpetp - > hp_hpet = hdp - > hd_address ;
hpetp - > hp_hpet_phys = hdp - > hd_phys_address ;
hpetp - > hp_ntimer = hdp - > hd_nirqs ;
for ( i = 0 ; i < hdp - > hd_nirqs ; i + + )
hpetp - > hp_dev [ i ] . hd_hdwirq = hdp - > hd_irq [ i ] ;
hpet = hpetp - > hp_hpet ;
cap = readq ( & hpet - > hpet_cap ) ;
ntimer = ( ( cap & HPET_NUM_TIM_CAP_MASK ) > > HPET_NUM_TIM_CAP_SHIFT ) + 1 ;
if ( hpetp - > hp_ntimer ! = ntimer ) {
printk ( KERN_WARNING " hpet: number irqs doesn't agree "
" with number of timers \n " ) ;
kfree ( hpetp ) ;
return - ENODEV ;
}
if ( last )
last - > hp_next = hpetp ;
else
hpets = hpetp ;
last = hpetp ;
hpetp - > hp_period = ( cap & HPET_COUNTER_CLK_PERIOD_MASK ) > >
HPET_COUNTER_CLK_PERIOD_SHIFT ;
printk ( KERN_INFO " hpet%d: at MMIO 0x%lx, IRQ%s " ,
hpetp - > hp_which , hdp - > hd_phys_address ,
hpetp - > hp_ntimer > 1 ? " s " : " " ) ;
for ( i = 0 ; i < hpetp - > hp_ntimer ; i + + )
printk ( " %s %d " , i > 0 ? " , " : " " , hdp - > hd_irq [ i ] ) ;
printk ( " \n " ) ;
ns = hpetp - > hp_period ; /* femptoseconds, 10^-15 */
2005-06-26 01:57:05 +04:00
ns / = 1000000 ; /* convert to nanoseconds, 10^-9 */
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " hpet%d: %ldns tick, %d %d-bit timers \n " ,
hpetp - > hp_which , ns , hpetp - > hp_ntimer ,
cap & HPET_COUNTER_SIZE_MASK ? 64 : 32 ) ;
mcfg = readq ( & hpet - > hpet_config ) ;
if ( ( mcfg & HPET_ENABLE_CNF_MASK ) = = 0 ) {
write_counter ( 0L , & hpet - > hpet_mc ) ;
mcfg | = HPET_ENABLE_CNF_MASK ;
writeq ( mcfg , & hpet - > hpet_config ) ;
}
for ( i = 0 , devp = hpetp - > hp_dev ; i < hpetp - > hp_ntimer ;
i + + , hpet_ntimer + + , devp + + ) {
unsigned long v ;
struct hpet_timer __iomem * timer ;
timer = & hpet - > hpet_timers [ devp - hpetp - > hp_dev ] ;
v = readq ( & timer - > hpet_config ) ;
devp - > hd_hpets = hpetp ;
devp - > hd_hpet = hpet ;
devp - > hd_timer = timer ;
/*
* If the timer was reserved by platform code ,
* then make timer unavailable for opens .
*/
if ( hdp - > hd_state & ( 1 < < i ) ) {
devp - > hd_flags = HPET_OPEN ;
continue ;
}
init_waitqueue_head ( & devp - > hd_waitqueue ) ;
}
hpetp - > hp_delta = hpet_calibrate ( hpetp ) ;
hpet_register_interpolator ( hpetp ) ;
return 0 ;
}
static acpi_status hpet_resources ( struct acpi_resource * res , void * data )
{
struct hpet_data * hdp ;
acpi_status status ;
struct acpi_resource_address64 addr ;
struct hpets * hpetp ;
hdp = data ;
status = acpi_resource_to_address64 ( res , & addr ) ;
if ( ACPI_SUCCESS ( status ) ) {
unsigned long size ;
size = addr . max_address_range - addr . min_address_range + 1 ;
hdp - > hd_phys_address = addr . min_address_range ;
hdp - > hd_address = ioremap ( addr . min_address_range , size ) ;
for ( hpetp = hpets ; hpetp ; hpetp = hpetp - > hp_next )
if ( hpetp - > hp_hpet = = hdp - > hd_address )
return - EBUSY ;
} else if ( res - > id = = ACPI_RSTYPE_EXT_IRQ ) {
struct acpi_resource_ext_irq * irqp ;
int i ;
irqp = & res - > data . extended_irq ;
if ( irqp - > number_of_interrupts > 0 ) {
hdp - > hd_nirqs = irqp - > number_of_interrupts ;
for ( i = 0 ; i < hdp - > hd_nirqs ; i + + )
hdp - > hd_irq [ i ] =
acpi_register_gsi ( irqp - > interrupts [ i ] ,
irqp - > edge_level ,
irqp - > active_high_low ) ;
}
}
return AE_OK ;
}
static int hpet_acpi_add ( struct acpi_device * device )
{
acpi_status result ;
struct hpet_data data ;
memset ( & data , 0 , sizeof ( data ) ) ;
result =
acpi_walk_resources ( device - > handle , METHOD_NAME__CRS ,
hpet_resources , & data ) ;
if ( ACPI_FAILURE ( result ) )
return - ENODEV ;
if ( ! data . hd_address | | ! data . hd_nirqs ) {
printk ( " %s: no address or irqs in _CRS \n " , __FUNCTION__ ) ;
return - ENODEV ;
}
return hpet_alloc ( & data ) ;
}
static int hpet_acpi_remove ( struct acpi_device * device , int type )
{
/* XXX need to unregister interpolator, dealloc mem, etc */
return - EINVAL ;
}
static struct acpi_driver hpet_acpi_driver = {
. name = " hpet " ,
. ids = " PNP0103 " ,
. ops = {
. add = hpet_acpi_add ,
. remove = hpet_acpi_remove ,
} ,
} ;
static struct miscdevice hpet_misc = { HPET_MINOR , " hpet " , & hpet_fops } ;
static int __init hpet_init ( void )
{
int result ;
result = misc_register ( & hpet_misc ) ;
if ( result < 0 )
return - ENODEV ;
sysctl_header = register_sysctl_table ( dev_root , 0 ) ;
result = acpi_bus_register_driver ( & hpet_acpi_driver ) ;
if ( result < 0 ) {
if ( sysctl_header )
unregister_sysctl_table ( sysctl_header ) ;
misc_deregister ( & hpet_misc ) ;
return result ;
}
return 0 ;
}
static void __exit hpet_exit ( void )
{
acpi_bus_unregister_driver ( & hpet_acpi_driver ) ;
if ( sysctl_header )
unregister_sysctl_table ( sysctl_header ) ;
misc_deregister ( & hpet_misc ) ;
return ;
}
module_init ( hpet_init ) ;
module_exit ( hpet_exit ) ;
MODULE_AUTHOR ( " Bob Picco <Robert.Picco@hp.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;