lkml.org 
[lkml]   [2004]   [May]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
From
SubjectRe: [PATCH] HPET driver
Robert Picco wrote:
> diff -ruN -X /home/picco/losl/dontdiff
> linux-2.6.6-orig/drivers/char/hpet.c linux-2.6.6-hpet/drivers/char/hpet.c
> --- linux-2.6.6-orig/drivers/char/hpet.c 1969-12-31
> 19:00:00.000000000 -0500
> +++ linux-2.6.6-hpet/drivers/char/hpet.c 2004-05-10
> 15:43:48.000000000 -0400
> @@ -0,0 +1,1116 @@

run this through Lindent... non-standard indentation


> +/*
> + * Intel & MS High Precision Event Timer Implementation.
> + * Contributors:
> + * Venki Pallipadi
> + * Bob Picco
> + */
> +
> +#include <linux/config.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/types.h>
> +#include <linux/devfs_fs_kernel.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 <asm/current.h>
> +#include <asm/uaccess.h>
> +#include <asm/system.h>
> +#include <asm/io.h>
> +#include <asm/irq.h>
> +#include <asm/bitops.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;
> +static char hpetname[] = "hpet/XX";
> +
> +/* A lock for concurrent access by app and isr hpet activity. */
> +static spinlock_t hpet_lock = SPIN_LOCK_UNLOCKED;
> +/* A lock for concurrent intermodule access to hpet and isr hpet
> activity. */
> +static spinlock_t hpet_task_lock = SPIN_LOCK_UNLOCKED;
> +
> +struct hpet_dev {
> + struct hpets *hd_hpets;
> + volatile struct hpet *hd_hpet;
> + volatile struct hpet_timer
> + *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;
> + int hd_minor;
> +};

kill the 'volatile', it's preferred to put barriers and such in the code
where they are needed.


> +struct hpets {
> + struct hpets *hp_next;
> + volatile struct hpet *hp_hpet;
> + 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
> +
> +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++;
> +
> + if ((devp->hd_flags & (HPET_IE | HPET_PERIODIC)) == HPET_IE) {
> + unsigned long m, t;
> +
> + t = devp->hd_ireqfreq;
> + m = devp->hd_hpet->hpet_mc;
> + devp->hd_timer->hpet_compare = t + m + devp->hd_hpets->hp_delta;
> + }
> +
> + isr = (1 << (devp - devp->hd_hpets->hp_dev));
> + devp->hd_hpet->hpet_isr = 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 struct hpet_dev *
> +hpet_minor_to_dev (int minor)
> +{
> + struct hpets *hpetp;
> + int i;
> +
> + for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next)
> + for (i = 0; i < hpetp->hp_ntimer; i++)
> + if (hpetp->hp_dev[i].hd_minor == minor)
> + return &hpetp->hp_dev[i];
> + return 0;
> +}
> +
> +static int
> +hpet_open (struct inode *inode, struct file *file)
> +{
> + struct hpet_dev *devp;
> +
> + devp = hpet_minor_to_dev(MINOR(inode->i_rdev));

locking of hpets?


> + vma->vm_flags |= (VM_IO | VM_SHM | VM_LOCKED);
> + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
> + addr = __pa(addr);

where did these flags come from? don't you just want VM_RESERVED?


> + if (remap_page_range(vma, vma->vm_start, addr, PAGE_SIZE,
> vma->vm_page_prot)) {
> + printk(KERN_ERR "remap_page_range failed in hpet.c\n");
> + return -EAGAIN;
> + }

why not ->nopage like in sound/oss/via82cxxx_audio.c?


> +static int
> +hpet_release (struct inode *inode, struct file *file)
> +{
> + struct hpet_dev *devp;
> + volatile struct hpet_timer *timer;
> +
> + devp = file->private_data;
> + timer = devp->hd_timer;
> +
> + spin_lock_irq(&hpet_lock);
> +
> + timer->hpet_config &= ~Tn_INT_ENB_CNF_MASK;
> +
> + if (devp->hd_irq) {
> + free_irq(devp->hd_irq, devp);
> + devp->hd_irq = 0;
> + }
> +
> + devp->hd_ireqfreq = 0;
> +
> + if (devp->hd_flags & HPET_PERIODIC && timer->hpet_config &
> Tn_TYPE_CNF_MASK) {
> + unsigned long v;
> +
> + v = timer->hpet_config;
> + v ^= Tn_TYPE_CNF_MASK;
> + timer->hpet_config = v;
> + }
> +
> + devp->hd_flags &= ~(HPET_OPEN | HPET_IE | HPET_PERIODIC);
> + spin_unlock_irq(&hpet_lock);
> +
> + if (file->f_flags & FASYNC)
> + hpet_fasync(-1, file, 0);
> +
> + file->private_data = 0;
> + 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)
> +{
> + volatile struct hpet_timer *timer;
> + volatile struct hpet *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 = 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 = timer->hpet_config;
> + irq = devp->hd_hdwirq;
> +
> + if (irq) {
> + char name[7];
> +
> + sprintf(name, "hpet%d", (int) (devp - hpetp->hp_dev));
> +
> + if (request_irq(irq, hpet_interrupt, SA_INTERRUPT, 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 = timer->hpet_config;
> + g = v | Tn_INT_ENB_CNF_MASK;
> +
> + if (devp->hd_flags & HPET_PERIODIC) {
> + timer->hpet_compare = t;
> + g |= Tn_TYPE_CNF_MASK;
> + v |= Tn_TYPE_CNF_MASK;
> + timer->hpet_config = v;
> + v |= Tn_VAL_SET_CNF_MASK;
> + timer->hpet_config = v;
> + local_irq_save(flags);
> + m = hpet->hpet_mc;
> + timer->hpet_compare = t + m + hpetp->hp_delta;
> + } else {
> + local_irq_save(flags);
> + m = hpet->hpet_mc;
> + timer->hpet_compare = t + m + hpetp->hp_delta;
> + }
> +
> + isr = (1 << (devp - hpets->hp_dev));
> + hpet->hpet_isr = isr;
> + timer->hpet_config = g;
> + 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)
> +{
> + volatile struct hpet_timer *timer;
> + volatile struct hpet *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 = timer->hpet_config;
> + v &= ~Tn_INT_ENB_CNF_MASK;
> + timer->hpet_config = v;
> + if (devp->hd_irq) {
> + free_irq(devp->hd_irq, devp);
> + devp->hd_irq = 0;
> + }
> + devp->hd_flags ^= HPET_IE;
> + break;

do you really want to be freeing the irq outside of the open-close model?


> + case HPET_INFO:
> + {
> + struct hpet_info info;
> +
> + info.hi_ireqfreq = hpet_time_div(hpetp->hp_period *
> + devp->hd_ireqfreq);
> + info.hi_flags = timer->hpet_config & Tn_PER_INT_CAP_MASK;
> + if (copy_to_user((void *) arg, &info , sizeof (info)))
> + err = -EFAULT;
> + break;
> + }
> + case HPET_EPI:
> + v = timer->hpet_config;
> + if ((v & Tn_PER_INT_CAP_MASK) == 0) {
> + err = -ENXIO;
> + break;
> + }
> + devp->hd_flags |= HPET_PERIODIC;
> + break;
> + case HPET_DPI:
> + v = timer->hpet_config;
> + if ((v & Tn_PER_INT_CAP_MASK) == 0) {
> + err = -ENXIO;
> + break;
> + }
> + if (devp->hd_flags & HPET_PERIODIC &&
> + timer->hpet_config & Tn_TYPE_CNF_MASK) {
> + v = timer->hpet_config;
> + v ^= Tn_TYPE_CNF_MASK;
> + timer->hpet_config = v;
> + }
> + 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;
> + volatile struct hpet_timer *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 = 0, hpetp = hpets; hpetp && !devp; hpetp = hpetp->hp_next)
> + for (timer = hpetp->hp_hpet->hpet_timers, i = 0; i <
> hpetp->hp_ntimer; i++, timer++) {
> + if ((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 = 0;
> + 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;
> + volatile struct hpet_timer *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;
> + timer->hpet_config &= ~Tn_INT_ENB_CNF_MASK;
> + devp->hd_flags &= ~( HPET_IE | HPET_PERIODIC);
> + devp->hd_task = 0;
> + 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);
> +}
> +
> +
> +
> +#ifdef CONFIG_TIME_INTERPOLATION
> +
> +static unsigned long hpet_offset, last_wall_hpet;
> +static long hpet_nsecs_per_cycle, hpet_cycles_per_sec;
> +
> +static unsigned long
> +hpet_getoffset (void)
> +{
> + return hpet_offset + (hpets->hp_hpet->hpet_mc - last_wall_hpet) *
> hpet_nsecs_per_cycle;
> +}
> +
> +static void
> +hpet_update (long delta)
> +{
> + unsigned long mc;
> + unsigned long offset;
> + volatile struct hpet *hpet;
> +
> + hpet = hpets->hp_hpet;
> + mc = hpet->hpet_mc;
> + offset = hpet_offset + (mc - last_wall_hpet) * hpet_nsecs_per_cycle;
> +
> + if (delta < 0 || (unsigned long) delta < offset)
> + hpet_offset = offset - delta;
> + else
> + hpet_offset = 0;
> + last_wall_hpet = mc;
> +}
> +
> +static void
> +hpet_reset (void)
> +{
> + volatile struct hpet *hpet;
> +
> + hpet = hpets->hp_hpet;
> + hpet_offset = 0;
> + last_wall_hpet = hpet->hpet_mc;
> +}
> +
> +static struct time_interpolator hpet_interpolator = {
> + .get_offset = hpet_getoffset,
> + .update = hpet_update,
> + .reset = hpet_reset
> +};
> +
> +#endif
> +
> +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_start (struct seq_file *s, loff_t *pos)
> +{
> + struct hpets *hpetp;
> + loff_t n;
> +
> + for (n = *pos, hpetp = hpets; hpetp; hpetp = hpetp->hp_next)
> + if (!n--)
> + return hpetp;
> +
> + return 0;
> +}
> +
> +static void *
> +hpet_next (struct seq_file *s, void *v, loff_t *pos)
> +{
> + struct hpets *hpetp;
> +
> + hpetp = v;
> + ++*pos;
> + return hpetp->hp_next;
> +}
> +
> +static void
> +hpet_stop (struct seq_file *s, void *v)
> +{
> + return;
> +}
> +
> +static int
> +hpet_show (struct seq_file *s, void *v)
> +{
> + struct hpets *hpetp;
> + volatile struct hpet *hpet;
> + u64 cap, vendor, period;
> +
> + hpetp = v;
> + hpet = hpetp->hp_hpet;
> +
> + cap = hpet->hpet_cap;
> + period = (cap & HPET_COUNTER_CLK_PERIOD_MASK) >>
> + HPET_COUNTER_CLK_PERIOD_SHIFT;
> + vendor = (cap & HPET_VENDOR_ID_MASK) >> HPET_VENDOR_ID_SHIFT;
> +
> + seq_printf(s, "HPET%d period = %d 10**-15 vendor = 0x%x number
> timer = %d\n",
> + hpetp->hp_which, (u32) period, (u32) vendor, hpetp->hp_ntimer);
> +
> + return 0;
> +}
> +
> +static struct seq_operations hpet_seq_ops = {
> + .start = hpet_start,
> + .next = hpet_next,
> + .stop = hpet_stop,
> + .show = hpet_show
> +};
> +
> +static int
> +hpet_proc_open (struct inode *inode, struct file *file)
> +{
> + return seq_open(file, &hpet_seq_ops);
> +}
> +
> +static struct file_operations hpet_proc_fops = {
> + .open = hpet_proc_open,
> + .read = seq_read,
> + .llseek = seq_lseek,
> + .release = seq_release
> +};
> +
> +
> +/*
> + * 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 __init
> +hpet_calibrate (struct hpets *hpetp)
> +{
> + volatile struct hpet_timer *timer;
> + unsigned long t, m, count, i, flags, start;
> + struct hpet_dev *devp;
> + int j;
> + volatile struct hpet *hpet;
> +
> + for (timer = 0, 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 = timer->hpet_compare;
> +
> + i = 0;
> + count = hpet_time_div(hpetp->hp_period * TICK_CALIBRATE);
> +
> + local_irq_save(flags);
> +
> + start = hpet->hpet_mc;
> +
> + do {
> + m = hpet->hpet_mc;
> + timer->hpet_compare = t + m + hpetp->hp_delta;
> + } while (i++, (m - start) < count);
> +
> + local_irq_restore(flags);
> +
> + return (m - start) / i;
> +}
> +
> +static void __init
> +hpet_init_chrdev (void)
> +{
> + static int once;
> +
> + if (!once++ && register_chrdev(HPET_MAJOR, "hpet", &hpet_fops))
> + panic("unable to to major %d for hpet device", HPET_MAJOR);
> +
> + return;
> +}
> +
> +static void __init
> +hpet_post_platform (void)
> +{
> + struct hpets *hpetp;
> + u32 i, ntimer;
> + struct hpet_dev *devp;
> +
> + hpet_init_chrdev();
> +
> + for (ntimer = 0, hpetp = hpets; hpetp; hpetp = hpetp->hp_next,
> ntimer++)
> + for (i = 0, devp = hpetp->hp_dev; i < hpetp->hp_ntimer; i++,
> devp++) {
> +
> + if (devp->hd_flags & HPET_OPEN)
> + continue;
> +
> + sprintf(&hpetname[5], "%d", ntimer);
> + devfs_mk_cdev(MKDEV(HPET_MAJOR, ntimer),
> + S_IFCHR|S_IRUSR|S_IWUSR, hpetname);
> + init_waitqueue_head(&devp->hd_waitqueue);
> + }
> +
> + return;
> +}
> +
> +int __init
> +hpet_alloc (struct hpet_data *hdp)
> +{
> + u64 cap, mcfg;
> + struct hpet_dev *devp;
> + u32 i, ntimer;
> + struct hpets *hpetp;
> + size_t siz;
> + volatile struct hpet *hpet;
> + static struct hpets *last __initdata = (struct hpets *) 0;
> +
> + for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next)
> + if (hpetp->hp_hpet == (struct 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 = (struct hpet *) hdp->hd_address;
> +
> + if (last)
> + last->hp_next = hpetp;
> + else
> + hpets = hpetp;
> +
> + last = hpetp;
> +
> + 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 = hpet->hpet_cap;
> +
> + ntimer = ((cap & HPET_NUM_TIM_CAP_MASK) >> HPET_NUM_TIM_CAP_SHIFT)
> + 1;
> +
> + if (!ntimer) {
> + printk(KERN_WARNING "hpet: no timers in config data\n");
> + return -ENODEV;
> + }
> + else if (hpetp->hp_ntimer != ntimer) {
> + printk(KERN_WARNING "hpet: number irqs doesn't agree"
> + " with number of timers\n");
> + return -ENODEV;
> + }
> +
> + hpetp->hp_period = (cap & HPET_COUNTER_CLK_PERIOD_MASK) >>
> + HPET_COUNTER_CLK_PERIOD_SHIFT;
> +
> + mcfg = hpet->hpet_config;
> + if ((mcfg & HPET_ENABLE_CNF_MASK) == 0) {
> + hpet->hpet_mc = 0L;
> + mcfg |= HPET_ENABLE_CNF_MASK;
> + hpet->hpet_config = mcfg;
> + }
> +
> + /*
> + * Create character devices and init wait queue.
> + * If this is a platform call, then device initialization
> + * occurs during startup call to hpet_init.
> + */
> + if (hdp->hd_flags ^ HPET_DATA_PLATFORM)
> + hpet_init_chrdev();
> +
> + for (i = 0, devp = hpetp->hp_dev; i < hpetp->hp_ntimer; i++,
> hpet_ntimer++, devp++) {
> + unsigned long v;
> + volatile struct hpet_timer *timer;
> +
> + timer = &hpet->hpet_timers[devp - hpetp->hp_dev];
> + v = timer->hpet_config;
> +
> + devp->hd_hpets = hpetp;
> + devp->hd_hpet = hpet;
> + devp->hd_timer = timer;
> +
> + devp->hd_minor = hpet_ntimer;
> +
> + if (hdp->hd_state & (1 << i)) {
> + devp->hd_flags = HPET_OPEN;
> + continue;
> + }
> +
> + if (hdp->hd_flags & HPET_DATA_PLATFORM)
> + continue;
> +
> + sprintf(&hpetname[5], "%d", hpet_ntimer);
> + devfs_mk_cdev(MKDEV(HPET_MAJOR, hpet_ntimer),
> + S_IFCHR|S_IRUSR|S_IWUSR, hpetname);
> + init_waitqueue_head(&devp->hd_waitqueue);
> + }
> +
> + hpetp->hp_delta = hpet_calibrate(hpetp);
> +
> + return 0;
> +}
> +
> +static acpi_status __init
> +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_address = (unsigned long)
> ioremap(addr.min_address_range, size);
> +
> + for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next)
> + if (hpetp->hp_hpet == (struct 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++)
> +#ifdef CONFIG_IA64
> + hdp->hd_irq[i] = acpi_register_irq(irqp->interrupts[i],
> + irqp->active_high_low, irqp->edge_level);
> +#else
> + hdp->hd_irq[i] = irqp->interrupts[i];
> +#endif
> + }
> + }
> +
> + return AE_OK;
> +}
> +
> +static int __init
> +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 __init
> +hpet_acpi_remove (struct acpi_device *device, int type)
> +{
> + return 0;
> +}
> +
> +static struct acpi_driver hpet_acpi_driver __initdata = {
> + .name = "hpet",
> + .class = "",
> + .ids = "PNP0103",
> + .ops = {
> + .add = hpet_acpi_add,
> + .remove = hpet_acpi_remove,
> + },
> +};
> +
> +
> +
> +static int __init
> +hpet_init (void)
> +{
> + struct proc_dir_entry *entry;
> +
> + if (hpets)
> + hpet_post_platform();
> +
> + (void) acpi_bus_register_driver(&hpet_acpi_driver);
> +
> + if (hpets) {
> + entry = create_proc_entry("driver/hpet", 0, 0);
> +
> + if (entry)
> + entry->proc_fops = &hpet_proc_fops;
> +
> + sysctl_header = register_sysctl_table(dev_root, 0);
> +
> +#ifdef CONFIG_TIME_INTERPOLATION
> + {
> + volatile struct hpet *hpet;
> +
> + hpet = hpets->hp_hpet;
> + hpet_cycles_per_sec = hpet_time_div(hpets->hp_period);
> + hpet_interpolator.frequency = hpet_cycles_per_sec;
> + hpet_interpolator.drift = hpet_cycles_per_sec *
> + HPET_DRIFT / 1000000;
> + hpet_nsecs_per_cycle = 1000000000 / hpet_cycles_per_sec;
> + register_time_interpolator(&hpet_interpolator);
> + }
> +#endif
> + return 0;
> + }
> + else
> + return -ENODEV;
> +}
> +
> +static void __exit
> +hpet_exit (void)
> +{
> + acpi_bus_unregister_driver(&hpet_acpi_driver);
> +
> + if (hpets) {
> + unregister_sysctl_table(sysctl_header);
> + remove_proc_entry("driver/hpet", NULL);
> + }
> +
> + return;
> +}

Finally, would prefer sysfs use to procfs.

No major objections though.

Jeff



-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2005-03-22 14:03    [from the cache]
©2003-2011 Jasper Spaans