]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/char/hpet.c
[PATCH] hpet: allow shared interrupts
[karo-tx-linux.git] / drivers / char / hpet.c
index 5fe8461271fc35812222bd2b2e2f8e138d1ffc02..8e59639fb03c6b3d32dc2bf18703c615b2ca0a08 100644 (file)
@@ -49,7 +49,7 @@
 #define        HPET_USER_FREQ  (64)
 #define        HPET_DRIFT      (500)
 
-static u32 hpet_ntimer, hpet_nhpet, hpet_max_freq = HPET_USER_FREQ;
+static u32 hpet_nhpet, hpet_max_freq = HPET_USER_FREQ;
 
 /* A lock for concurrent access by app and isr hpet activity. */
 static DEFINE_SPINLOCK(hpet_lock);
@@ -78,7 +78,7 @@ struct hpets {
        struct hpet __iomem *hp_hpet;
        unsigned long hp_hpet_phys;
        struct time_interpolator *hp_interpolator;
-       unsigned long hp_period;
+       unsigned long long hp_tick_freq;
        unsigned long hp_delta;
        unsigned int hp_ntimer;
        unsigned int hp_which;
@@ -90,6 +90,7 @@ static struct hpets *hpets;
 #define        HPET_OPEN               0x0001
 #define        HPET_IE                 0x0002  /* interrupt enabled */
 #define        HPET_PERIODIC           0x0004
+#define        HPET_SHARED_IRQ         0x0008
 
 #if BITS_PER_LONG == 64
 #define        write_counter(V, MC)    writeq(V, MC)
@@ -100,14 +101,14 @@ static struct hpets *hpets;
 #endif
 
 #ifndef readq
-static unsigned long long __inline readq(void __iomem *addr)
+static inline unsigned long long 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)
+static inline void writeq(unsigned long long v, void __iomem *addr)
 {
        writel(v & 0xffffffff, addr);
        writel(v >> 32, addr + 4);
@@ -120,6 +121,11 @@ static irqreturn_t hpet_interrupt(int irq, void *data, struct pt_regs *regs)
        unsigned long isr;
 
        devp = data;
+       isr = 1 << (devp - devp->hd_hpets->hp_dev);
+
+       if ((devp->hd_flags & HPET_SHARED_IRQ) &&
+           !(isr & readl(&devp->hd_hpet->hpet_isr)))
+               return IRQ_NONE;
 
        spin_lock(&hpet_lock);
        devp->hd_irqdata++;
@@ -137,8 +143,8 @@ static irqreturn_t hpet_interrupt(int irq, void *data, struct pt_regs *regs)
                              &devp->hd_timer->hpet_compare);
        }
 
-       isr = (1 << (devp - devp->hd_hpets->hp_dev));
-       writeq(isr, &devp->hd_hpet->hpet_isr);
+       if (devp->hd_flags & HPET_SHARED_IRQ)
+               writel(isr, &devp->hd_hpet->hpet_isr);
        spin_unlock(&hpet_lock);
 
        spin_lock(&hpet_task_lock);
@@ -273,7 +279,6 @@ static int hpet_mmap(struct file *file, struct vm_area_struct *vma)
 
        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)) {
@@ -365,7 +370,9 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp)
        hpet = devp->hd_hpet;
        hpetp = devp->hd_hpets;
 
-       v = readq(&timer->hpet_config);
+       if (!devp->hd_ireqfreq)
+               return -EIO;
+
        spin_lock_irq(&hpet_lock);
 
        if (devp->hd_flags & HPET_IE) {
@@ -374,16 +381,21 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp)
        }
 
        devp->hd_flags |= HPET_IE;
+
+       if (readl(&timer->hpet_config) & Tn_INT_TYPE_CNF_MASK)
+               devp->hd_flags |= HPET_SHARED_IRQ;
        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));
+               unsigned long irq_flags;
 
-               if (request_irq
-                   (irq, hpet_interrupt, SA_INTERRUPT, devp->hd_name, (void *)devp)) {
+               sprintf(devp->hd_name, "hpet%d", (int)(devp - hpetp->hp_dev));
+               irq_flags = devp->hd_flags & HPET_SHARED_IRQ
+                                               ? SA_SHIRQ : SA_INTERRUPT;
+               if (request_irq(irq, hpet_interrupt, irq_flags,
+                               devp->hd_name, (void *)devp)) {
                        printk(KERN_ERR "hpet: IRQ %d is not free\n", irq);
                        irq = 0;
                }
@@ -417,20 +429,24 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp)
                write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare);
        }
 
-       isr = (1 << (devp - hpets->hp_dev));
-       writeq(isr, &hpet->hpet_isr);
+       if (devp->hd_flags & HPET_SHARED_IRQ) {
+               isr = 1 << (devp - hpets->hp_dev);
+               writel(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)
+/* converts Hz to number of timer ticks */
+static inline unsigned long hpet_time_div(struct hpets *hpets,
+                                         unsigned long dis)
 {
-       unsigned long long m = 1000000000000000ULL;
+       unsigned long long m;
 
+       m = hpets->hp_tick_freq + (dis >> 1);
        do_div(m, dis);
-
        return (unsigned long)m;
 }
 
@@ -478,7 +494,7 @@ hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg, int kernel)
                {
                        struct hpet_info info;
 
-                       info.hi_ireqfreq = hpet_time_div(hpetp->hp_period *
+                       info.hi_ireqfreq = hpet_time_div(hpetp,
                                                         devp->hd_ireqfreq);
                        info.hi_flags =
                            readq(&timer->hpet_config) & Tn_PER_INT_CAP_MASK;
@@ -517,12 +533,12 @@ hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg, int kernel)
                        break;
                }
 
-               if (arg & (arg - 1)) {
+               if (!arg) {
                        err = -EINVAL;
                        break;
                }
 
-               devp->hd_ireqfreq = hpet_time_div(hpetp->hp_period * arg);
+               devp->hd_ireqfreq = hpet_time_div(hpetp, arg);
        }
 
        return err;
@@ -711,7 +727,7 @@ static void hpet_register_interpolator(struct hpets *hpetp)
        ti->source = TIME_SOURCE_MMIO64;
        ti->shift = 10;
        ti->addr = &hpetp->hp_hpet->hpet_mc;
-       ti->frequency = hpet_time_div(hpets->hp_period);
+       ti->frequency = hpetp->hp_tick_freq;
        ti->drift = HPET_DRIFT;
        ti->mask = -1;
 
@@ -748,7 +764,7 @@ static unsigned long hpet_calibrate(struct hpets *hpetp)
        t = read_counter(&timer->hpet_compare);
 
        i = 0;
-       count = hpet_time_div(hpetp->hp_period * TICK_CALIBRATE);
+       count = hpet_time_div(hpetp, TICK_CALIBRATE);
 
        local_irq_save(flags);
 
@@ -773,7 +789,8 @@ int hpet_alloc(struct hpet_data *hdp)
        size_t siz;
        struct hpet __iomem *hpet;
        static struct hpets *last = (struct hpets *)0;
-       unsigned long ns;
+       unsigned long ns, period;
+       unsigned long long temp;
 
        /*
         * hpet_alloc can be called by platform dependent code.
@@ -823,8 +840,12 @@ int hpet_alloc(struct hpet_data *hdp)
 
        last = hpetp;
 
-       hpetp->hp_period = (cap & HPET_COUNTER_CLK_PERIOD_MASK) >>
-           HPET_COUNTER_CLK_PERIOD_SHIFT;
+       period = (cap & HPET_COUNTER_CLK_PERIOD_MASK) >>
+               HPET_COUNTER_CLK_PERIOD_SHIFT; /* fs, 10^-15 */
+       temp = 1000000000000000uLL; /* 10^15 femtoseconds per second */
+       temp += period >> 1; /* round */
+       do_div(temp, period);
+       hpetp->hp_tick_freq = temp; /* ticks per second */
 
        printk(KERN_INFO "hpet%d: at MMIO 0x%lx, IRQ%s",
                hpetp->hp_which, hdp->hd_phys_address,
@@ -833,8 +854,7 @@ int hpet_alloc(struct hpet_data *hdp)
                printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]);
        printk("\n");
 
-       ns = hpetp->hp_period;  /* femptoseconds, 10^-15 */
-       ns /= 1000000;          /* convert to nanoseconds, 10^-9 */
+       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);
@@ -846,13 +866,10 @@ int hpet_alloc(struct hpet_data *hdp)
                writeq(mcfg, &hpet->hpet_config);
        }
 
-       for (i = 0, devp = hpetp->hp_dev; i < hpetp->hp_ntimer;
-            i++, hpet_ntimer++, devp++) {
-               unsigned long v;
+       for (i = 0, devp = hpetp->hp_dev; i < hpetp->hp_ntimer; i++, devp++) {
                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;