From: Jeff Garzik <jgarzik@pobox.com>
To: Robert Picco <Robert.Picco@hp.com>
Cc: linux-kernel@vger.kernel.org, "Pallipadi,
Venkatesh" <venkatesh.pallipadi@intel.com>,
Andrew Morton <akpm@osdl.org>
Subject: Re: [PATCH] HPET driver
Date: Thu, 13 May 2004 19:17:24 -0400 [thread overview]
Message-ID: <40A40204.1060509@pobox.com> (raw)
In-Reply-To: <40A3F805.5090804@hp.com>
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
next prev parent reply other threads:[~2004-05-13 23:20 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-05-13 22:34 [PATCH] HPET driver Robert Picco
2004-05-13 23:17 ` Jeff Garzik [this message]
2004-05-13 23:42 ` Andrew Morton
2004-05-13 23:46 ` Andrew Morton
2004-05-13 23:53 ` HPET docs Jeff Garzik
2004-05-13 23:49 ` [PATCH] HPET driver Jeff Garzik
2004-05-14 11:19 ` Vojtech Pavlik
2004-05-14 16:59 ` Jeff Garzik
2004-05-17 22:33 ` Robert Picco
2004-05-17 22:39 ` Jeff Garzik
2004-05-17 22:43 ` Jeff Garzik
2004-05-17 23:05 ` Andrew Morton
2004-05-17 23:06 ` Jeff Garzik
2004-05-17 23:12 ` Andrew Morton
2004-05-17 23:18 ` Jeff Garzik
2004-05-17 23:25 ` Russell King
2004-05-17 23:41 ` Jeff Garzik
2004-05-17 23:33 ` Andrew Morton
2004-05-17 23:42 ` Jeff Garzik
2004-05-18 1:46 ` Andrew Morton
2004-05-18 1:59 ` Jeff Garzik
[not found] ` <m1vfit3939.fsf_-_@ebiederm.dsl.xmission.com>
[not found] ` <52fz9xpp5l.fsf@topspin.com>
2004-05-18 23:01 ` readq/writeq on 32bit machines Eric W. Biederman
2004-05-19 0:58 ` Roland Dreier
2004-05-19 3:14 ` Eric W. Biederman
[not found] ` <16553.28862.590897.171478@napali.hpl.hp.com>
2004-05-20 2:01 ` [PATCH] HPET driver Jeff Garzik
2004-05-13 23:18 ` Andrew Morton
[not found] <200406181616.i5IGGECd003812@hera.kernel.org>
2004-06-18 20:57 ` Jeff Garzik
2004-06-18 21:55 ` Andrew Morton
2004-06-18 22:45 ` Jeff Garzik
2004-06-23 21:34 ` Robert Picco
2004-06-23 21:41 ` Jeff Garzik
2004-06-23 22:23 ` Andrew Morton
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=40A40204.1060509@pobox.com \
--to=jgarzik@pobox.com \
--cc=Robert.Picco@hp.com \
--cc=akpm@osdl.org \
--cc=linux-kernel@vger.kernel.org \
--cc=venkatesh.pallipadi@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.