* published by the Free Software Foundation.
*/
-#include <linux/config.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/poll.h>
+#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/sysctl.h>
#include <linux/bcd.h>
#include <linux/seq_file.h>
#include <linux/bitops.h>
+#include <linux/clocksource.h>
#include <asm/current.h>
#include <asm/uaccess.h>
#define HPET_USER_FREQ (64)
#define HPET_DRIFT (500)
+#define HPET_RANGE_SIZE 1024 /* from HPET spec */
+
+#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
+
static u32 hpet_nhpet, hpet_max_freq = HPET_USER_FREQ;
+/* This clocksource driver currently only works on ia64 */
+#ifdef CONFIG_IA64
+static void __iomem *hpet_mctr;
+
+static cycle_t read_hpet(void)
+{
+ return (cycle_t)read_counter((void __iomem *)hpet_mctr);
+}
+
+static struct clocksource clocksource_hpet = {
+ .name = "hpet",
+ .rating = 250,
+ .read = read_hpet,
+ .mask = CLOCKSOURCE_MASK(64),
+ .mult = 0, /*to be caluclated*/
+ .shift = 10,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+static struct clocksource *hpet_clocksource;
+#endif
+
/* 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. */
struct hpets *hp_next;
struct hpet __iomem *hp_hpet;
unsigned long hp_hpet_phys;
- struct time_interpolator *hp_interpolator;
+ struct clocksource *hp_clocksource;
unsigned long long hp_tick_freq;
unsigned long hp_delta;
unsigned int hp_ntimer;
#define HPET_PERIODIC 0x0004
#define HPET_SHARED_IRQ 0x0008
-#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 inline unsigned long long readq(void __iomem *addr)
}
#endif
-static irqreturn_t hpet_interrupt(int irq, void *data, struct pt_regs *regs)
+static irqreturn_t hpet_interrupt(int irq, void *data)
{
struct hpet_dev *devp;
unsigned long isr;
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");
+ printk(KERN_ERR "%s: io_remap_pfn_range failed\n",
+ __FUNCTION__);
return -EAGAIN;
}
sprintf(devp->hd_name, "hpet%d", (int)(devp - hpetp->hp_dev));
irq_flags = devp->hd_flags & HPET_SHARED_IRQ
- ? SA_SHIRQ : SA_INTERRUPT;
+ ? IRQF_SHARED : IRQF_DISABLED;
if (request_irq(irq, hpet_interrupt, irq_flags,
devp->hd_name, (void *)devp)) {
printk(KERN_ERR "hpet: IRQ %d is not free\n", irq);
}
if (devp->hd_flags & HPET_SHARED_IRQ) {
- isr = 1 << (devp - hpets->hp_dev);
+ isr = 1 << (devp - devp->hd_hpets->hp_dev);
writel(isr, &hpet->hpet_isr);
}
writeq(g, &timer->hpet_config);
{
struct hpet_info info;
- info.hi_ireqfreq = hpet_time_div(hpetp,
- devp->hd_ireqfreq);
+ if (devp->hd_ireqfreq)
+ info.hi_ireqfreq =
+ hpet_time_div(hpetp, devp->hd_ireqfreq);
+ else
+ info.hi_ireqfreq = 0;
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;
+ info.hi_hpet = hpetp->hp_which;
+ info.hi_timer = devp - hpetp->hp_dev;
if (kernel)
memcpy((void *)arg, &info, sizeof(info));
else
return err;
}
-static struct file_operations hpet_fops = {
+static const struct file_operations hpet_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = hpet_read,
.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)
+static int hpet_is_known(struct hpet_data *hdp)
{
- 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);
+ for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next)
+ if (hpetp->hp_hpet_phys == hdp->hd_phys_address)
+ return 1;
- if (tp->ht_opaque)
- return 0;
- else
- return -EBUSY;
+ return 0;
}
static inline int hpet_tpcheck(struct hpet_task *tp)
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,
+ .ctl_name = CTL_UNNUMBERED,
.procname = "max-user-freq",
.data = &hpet_max_freq,
.maxlen = sizeof(int),
static ctl_table hpet_root[] = {
{
- .ctl_name = 1,
+ .ctl_name = CTL_UNNUMBERED,
.procname = "hpet",
.maxlen = 0,
.mode = 0555,
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 = hpetp->hp_tick_freq;
- ti->drift = HPET_DRIFT;
- 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
if (!timer)
return 0;
- hpet = hpets->hp_hpet;
+ hpet = hpetp->hp_hpet;
t = read_counter(&timer->hpet_compare);
i = 0;
int hpet_alloc(struct hpet_data *hdp)
{
- u64 cap, mcfg;
+ u64 cap, mcfg, hpet_config;
struct hpet_dev *devp;
- u32 i, ntimer;
+ u32 i, ntimer, irq;
struct hpets *hpetp;
size_t siz;
struct hpet __iomem *hpet;
- static struct hpets *last = (struct hpets *)0;
- unsigned long ns, period;
+ static struct hpets *last = NULL;
+ unsigned long period, irq_bitmap;
unsigned long long temp;
/*
* 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.
+ * If platform dependent code has allocated the hpet that
+ * ACPI has also reported, then we catch it here.
*/
- for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next)
- if (hpetp->hp_hpet == hdp->hd_address)
- return 0;
+ if (hpet_is_known(hdp)) {
+ printk(KERN_DEBUG "%s: duplicate HPET ignored\n",
+ __FUNCTION__);
+ return 0;
+ }
siz = sizeof(struct hpets) + ((hdp->hd_nirqs - 1) *
sizeof(struct hpet_dev));
- hpetp = kmalloc(siz, GFP_KERNEL);
+ hpetp = kzalloc(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;
+ hpet = hpetp->hp_hpet;
- for (i = 0; i < hdp->hd_nirqs; i++)
- hpetp->hp_dev[i].hd_hdwirq = hdp->hd_irq[i];
+ /* Assign IRQs statically for legacy devices */
+ hpetp->hp_dev[0].hd_hdwirq = hdp->hd_irq[0];
+ hpetp->hp_dev[1].hd_hdwirq = hdp->hd_irq[1];
- hpet = hpetp->hp_hpet;
+ /* Assign IRQs dynamically for the others */
+ for (i = 2, devp = &hpetp->hp_dev[2]; i < hdp->hd_nirqs; i++, devp++) {
+ struct hpet_timer __iomem *timer;
+
+ timer = &hpet->hpet_timers[devp - hpetp->hp_dev];
+
+ /* Check if there's already an IRQ assigned to the timer */
+ if (hdp->hd_irq[i]) {
+ hpetp->hp_dev[i].hd_hdwirq = hdp->hd_irq[i];
+ continue;
+ }
+
+ hpet_config = readq(&timer->hpet_config);
+ irq_bitmap = (hpet_config & Tn_INT_ROUTE_CAP_MASK)
+ >> Tn_INT_ROUTE_CAP_SHIFT;
+ if (!irq_bitmap)
+ irq = 0; /* No valid IRQ Assignable */
+ else {
+ irq = find_first_bit(&irq_bitmap, 32);
+ do {
+ hpet_config |= irq << Tn_INT_ROUTE_CNF_SHIFT;
+ writeq(hpet_config, &timer->hpet_config);
+
+ /*
+ * Verify whether we have written a valid
+ * IRQ number by reading it back again
+ */
+ hpet_config = readq(&timer->hpet_config);
+ if (irq == (hpet_config & Tn_INT_ROUTE_CNF_MASK)
+ >> Tn_INT_ROUTE_CNF_SHIFT)
+ break; /* Success */
+ } while ((irq = (find_next_bit(&irq_bitmap, 32, irq))));
+ }
+ hpetp->hp_dev[i].hd_hdwirq = irq;
+ }
cap = readq(&hpet->hpet_cap);
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("%s %d", i > 0 ? "," : "",
+ hpetp->hp_dev[i].hd_hdwirq);
printk("\n");
- ns = period / 1000000; /* convert to nanoseconds, 10^-9 */
- 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);
+ printk(KERN_INFO "hpet%u: %u %d-bit timers, %Lu Hz\n",
+ hpetp->hp_which, hpetp->hp_ntimer,
+ cap & HPET_COUNTER_SIZE_MASK ? 64 : 32, hpetp->hp_tick_freq);
mcfg = readq(&hpet->hpet_config);
if ((mcfg & HPET_ENABLE_CNF_MASK) == 0) {
}
hpetp->hp_delta = hpet_calibrate(hpetp);
- hpet_register_interpolator(hpetp);
+
+/* This clocksource driver currently only works on ia64 */
+#ifdef CONFIG_IA64
+ if (!hpet_clocksource) {
+ hpet_mctr = (void __iomem *)&hpetp->hp_hpet->hpet_mc;
+ CLKSRC_FSYS_MMIO_SET(clocksource_hpet.fsys_mmio, hpet_mctr);
+ clocksource_hpet.mult = clocksource_hz2mult(hpetp->hp_tick_freq,
+ clocksource_hpet.shift);
+ clocksource_register(&clocksource_hpet);
+ hpetp->hp_clocksource = &clocksource_hpet;
+ hpet_clocksource = &clocksource_hpet;
+ }
+#endif
return 0;
}
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;
+ hdp->hd_phys_address = addr.minimum;
+ hdp->hd_address = ioremap(addr.minimum, addr.address_length);
+
+ if (hpet_is_known(hdp)) {
+ printk(KERN_DEBUG "%s: 0x%lx is busy\n",
+ __FUNCTION__, hdp->hd_phys_address);
+ iounmap(hdp->hd_address);
+ return AE_ALREADY_EXISTS;
+ }
+ } else if (res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
+ struct acpi_resource_fixed_memory32 *fixmem32;
+
+ fixmem32 = &res->data.fixed_memory32;
+ if (!fixmem32)
+ return AE_NO_MEMORY;
+
+ hdp->hd_phys_address = fixmem32->address;
+ hdp->hd_address = ioremap(fixmem32->address,
+ HPET_RANGE_SIZE);
+
+ if (hpet_is_known(hdp)) {
+ printk(KERN_DEBUG "%s: 0x%lx is busy\n",
+ __FUNCTION__, hdp->hd_phys_address);
+ iounmap(hdp->hd_address);
+ return AE_ALREADY_EXISTS;
+ }
+ } else if (res->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
+ struct acpi_resource_extended_irq *irqp;
+ int i, irq;
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++) {
- int rc =
- acpi_register_gsi(irqp->interrupts[i],
- irqp->edge_level,
- irqp->active_high_low);
- if (rc < 0)
- return AE_ERROR;
- hdp->hd_irq[i] = rc;
- }
+ for (i = 0; i < irqp->interrupt_count; i++) {
+ irq = acpi_register_gsi(irqp->interrupts[i],
+ irqp->triggering, irqp->polarity);
+ if (irq < 0)
+ return AE_ERROR;
+
+ hdp->hd_irq[hdp->hd_nirqs] = irq;
+ hdp->hd_nirqs++;
}
}
static int hpet_acpi_remove(struct acpi_device *device, int type)
{
- /* XXX need to unregister interpolator, dealloc mem, etc */
+ /* XXX need to unregister clocksource, dealloc mem, etc */
return -EINVAL;
}
+static const struct acpi_device_id hpet_device_ids[] = {
+ {"PNP0103", 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, hpet_device_ids);
+
static struct acpi_driver hpet_acpi_driver = {
.name = "hpet",
- .ids = "PNP0103",
+ .ids = hpet_device_ids,
.ops = {
.add = hpet_acpi_add,
.remove = hpet_acpi_remove,
if (result < 0)
return -ENODEV;
- sysctl_header = register_sysctl_table(dev_root, 0);
+ sysctl_header = register_sysctl_table(dev_root);
result = acpi_bus_register_driver(&hpet_acpi_driver);
if (result < 0) {