[linux-yocto] [[PATCH 1/1] hpet: refactor driver
chong.yi.chai at intel.com
chong.yi.chai at intel.com
Wed Mar 30 20:16:30 PDT 2016
From: "Chai, Chong Yi" <chong.yi.chai at intel.com>
---
features/soc/baytrail/baytrail.scc | 3 +
.../baytrail/hpet-Fix-checkpatch.pl-warnings.patch | 74 +++
.../hpet-implement-start-stop-query-API.patch | 316 +++++++++
features/soc/baytrail/hpet-refactor-driver.patch | 721 +++++++++++++++++++++
4 files changed, 1114 insertions(+)
create mode 100644 features/soc/baytrail/hpet-Fix-checkpatch.pl-warnings.patch
create mode 100644 features/soc/baytrail/hpet-implement-start-stop-query-API.patch
create mode 100644 features/soc/baytrail/hpet-refactor-driver.patch
diff --git a/features/soc/baytrail/baytrail.scc b/features/soc/baytrail/baytrail.scc
index da9f3d5..e6abaa2 100644
--- a/features/soc/baytrail/baytrail.scc
+++ b/features/soc/baytrail/baytrail.scc
@@ -62,3 +62,6 @@ patch serial-8250_pci-mask-UART-TX-completion-intr-in-byt_.patch
patch serial-8250_dw-mask-UART-TX-completion-intr-in-byt_s.patch
patch serial-8250_core-handle_irq-returns-1-only-if-data-w.patch
patch serial-8250-Override-the-DCD-and-DSR-pin-status-for-.patch
+patch hpet-refactor-driver.patch
+patch hpet-implement-start-stop-query-API.patch
+patch hpet-Fix-checkpatch.pl-warnings.patch
diff --git a/features/soc/baytrail/hpet-Fix-checkpatch.pl-warnings.patch b/features/soc/baytrail/hpet-Fix-checkpatch.pl-warnings.patch
new file mode 100644
index 0000000..94455b7
--- /dev/null
+++ b/features/soc/baytrail/hpet-Fix-checkpatch.pl-warnings.patch
@@ -0,0 +1,74 @@
+From 8d517f517218760020ec0d4f251b66092373a640 Mon Sep 17 00:00:00 2001
+From: Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad at intel.com>
+Date: Fri, 21 Aug 2015 12:31:26 +0800
+Subject: [PATCH 156/164] hpet: Fix checkpatch.pl warnings
+
+This commit is to fix the result of running scripts/checkpatch.pl against
+0134-hpet-refactor-driver.patch
+
+Signed-off-by: Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad at intel.com>
+---
+ drivers/char/hpet.c | 19 +++++++++----------
+ 1 files changed, 9 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
+index 0323ceb..f5637c5 100644
+--- a/drivers/char/hpet.c
++++ b/drivers/char/hpet.c
+@@ -1015,8 +1015,7 @@ int hpet_alloc(struct hpet_data *hdp)
+ temp = hpetp->hp_tick_freq;
+ remainder = do_div(temp, 1000000);
+
+- pr_info("hpet%d: at MMIO 0x%lx, %u comparators, "
+- "%d-bit %u.%06u MHz counter\n",
++ pr_info("hpet%d: at MMIO 0x%lx, %u comparators, %d-bit %u.%06u MHz counter\n",
+ hpetp->hp_which, hdp->hd_phys_address, ntimer,
+ cap & HPET_COUNTER_SIZE_MASK ? 64 : 32,
+ (unsigned) temp, remainder);
+@@ -1028,8 +1027,8 @@ int hpet_alloc(struct hpet_data *hdp)
+ if (!hdp->hd_state) {
+ conf = readq(&hpet->hpet_config);
+ if (conf & HPET_LEG_RT_CNF_MASK) {
+- pr_info("hpet%d: disabling legacy replacement "
+- "IRQ routing\n", hpetp->hp_which);
++ pr_info("hpet%d: disabling legacy replacement IRQ routing\n",
++ hpetp->hp_which);
+ conf &= ~HPET_LEG_RT_CNF_MASK;
+ writeq(conf, &hpet->hpet_config);
+ }
+@@ -1052,8 +1051,8 @@ int hpet_alloc(struct hpet_data *hdp)
+ if (hdp->hd_state & (1 << i)) {
+ /* This driver does not manage platform-reserved
+ * timers */
+- pr_info("hpet%d: comparator %d reserved for "
+- "system needs\n", hpetp->hp_which, i);
++ pr_info("hpet%d: comparator %d reserved for system needs\n",
++ hpetp->hp_which, i);
+ devp->hd_flags = HPET_OPEN;
+ continue;
+ }
+@@ -1116,8 +1115,8 @@ int hpet_alloc(struct hpet_data *hdp)
+ }
+
+ /* No useable IRQ */
+- pr_warn("hpet%d: no useable IRQ for "
+- "comparator %d\n", hpetp->hp_which, i);
++ pr_warn("hpet%d: no useable IRQ for comparator %d\n",
++ hpetp->hp_which, i);
+ devp->hd_flags = HPET_OPEN;
+ continue;
+
+@@ -1129,8 +1128,8 @@ setup_irq:
+ conf |= (irq << Tn_INT_ROUTE_CNF_SHIFT);
+ writeq(conf, &timer->hpet_config);
+
+- pr_info("hpet%d: comparator %d available, "
+- "using irq %d\n", hpetp->hp_which, i, gsi);
++ pr_info("hpet%d: comparator %d available, using irq %d\n",
++ hpetp->hp_which, i, gsi);
+
+ hpet_available = true;
+ init_waitqueue_head(&devp->hd_waitqueue);
+--
+1.7.7.6
+
diff --git a/features/soc/baytrail/hpet-implement-start-stop-query-API.patch b/features/soc/baytrail/hpet-implement-start-stop-query-API.patch
new file mode 100644
index 0000000..acca74c
--- /dev/null
+++ b/features/soc/baytrail/hpet-implement-start-stop-query-API.patch
@@ -0,0 +1,316 @@
+From 281970dee227e9baddaca259a8ef6e3fbaa8df7a Mon Sep 17 00:00:00 2001
+From: Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad at intel.com>
+Date: Sun, 26 Jul 2015 11:42:50 +0800
+Subject: [PATCH 135/164] hpet: implement start/stop/query API
+
+This commit exposes the usage of standard Linux API Timer to access HPET via
+/dev/hpet. This will ease userspace application to use HPET for general
+purpose timer (GPT).
+
+Signed-off-by: Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad at intel.com>
+---
+ drivers/char/hpet.c | 188 +++++++++++++++++++++++++++++++++++++++++++-
+ include/uapi/linux/hpet.h | 11 ++-
+ 2 files changed, 192 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
+index 49e6c9d..0323ceb 100644
+--- a/drivers/char/hpet.c
++++ b/drivers/char/hpet.c
+@@ -101,6 +101,7 @@ struct hpet_dev {
+ unsigned int hd_irq;
+ #define HPET_DEV_NAME_SIZE 16
+ char hd_name[HPET_DEV_NAME_SIZE];
++ unsigned long hd_start_tick, hd_timeout_tick;
+ };
+
+ struct hpets {
+@@ -121,6 +122,7 @@ static struct hpets *hpets;
+ #define HPET_IE 0x0002 /* interrupt enabled */
+ #define HPET_PERIODIC 0x0004
+ #define HPET_SHARED_IRQ 0x0008
++#define HPET_STARTED 0x0010 /* started in singleshot mode */
+
+
+ #ifndef readq
+@@ -142,6 +144,7 @@ static irqreturn_t hpet_interrupt(int irq, void *data)
+ {
+ struct hpet_dev *devp;
+ unsigned long isr;
++ unsigned long long conf;
+
+ devp = data;
+ isr = 1 << (devp - devp->hd_hpets->hp_dev);
+@@ -159,7 +162,8 @@ static irqreturn_t hpet_interrupt(int irq, void *data)
+ }
+
+ /* we could race against disable ... */
+- if ((readq(&devp->hd_timer->hpet_config) & Tn_INT_ENB_CNF_MASK) == 0) {
++ conf = readq(&devp->hd_timer->hpet_config);
++ if ((conf & Tn_INT_ENB_CNF_MASK) == 0) {
+ spin_unlock(&hpet_lock);
+ return IRQ_HANDLED;
+ }
+@@ -197,6 +201,10 @@ static irqreturn_t hpet_interrupt(int irq, void *data)
+ k = (mc - base + hpetp->hp_delta) / t;
+ write_counter(t * (k + 1) + base,
+ &devp->hd_timer->hpet_compare);
++ } else if (devp->hd_flags & HPET_STARTED) {
++ conf &= ~Tn_INT_ENB_CNF_MASK;
++ writeq(conf, &devp->hd_timer->hpet_config);
++ devp->hd_flags &= ~HPET_STARTED;
+ }
+
+ spin_unlock(&hpet_lock);
+@@ -304,8 +312,6 @@ hpet_read(struct file *file, char __user *buf, size_t count, loff_t * ppos)
+ struct hpet_dev *devp;
+
+ devp = file->private_data;
+- if (!devp->hd_ireqfreq)
+- return -EIO;
+
+ if (count < sizeof(unsigned long))
+ return -EINVAL;
+@@ -430,7 +436,7 @@ static int hpet_release(struct inode *inode, struct file *file)
+ v &= ~(Tn_INT_ENB_CNF_MASK | Tn_TYPE_CNF_MASK);
+ writeq(v, &timer->hpet_config);
+
+- devp->hd_flags &= ~(HPET_OPEN | HPET_IE | HPET_PERIODIC);
++ devp->hd_flags &= ~(HPET_OPEN | HPET_IE | HPET_PERIODIC | HPET_STARTED);
+ spin_unlock_irq(&hpet_lock);
+
+ free_irq(devp->hd_irq, devp);
+@@ -456,7 +462,7 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp)
+
+ spin_lock_irq(&hpet_lock);
+
+- if (devp->hd_flags & HPET_IE) {
++ if (devp->hd_flags & (HPET_IE | HPET_STARTED)) {
+ spin_unlock_irq(&hpet_lock);
+ return -EBUSY;
+ }
+@@ -508,6 +514,170 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp)
+ return 0;
+ }
+
++static int mul_u64_u64(u64 a, u64 b, u64 *ret)
++{
++ u64 tmp, tmp2;
++
++ if (a < b) {
++ tmp = a;
++ a = b;
++ b = tmp;
++ }
++
++ /* if lower of two does not fit in 32 bits, we have overflow */
++ if (b >= 0x100000000ull)
++ return -EOVERFLOW;
++
++ tmp = (a >> 32) * b;
++ if (tmp >= 0x100000000ull)
++ return -EOVERFLOW;
++ tmp <<= 32;
++
++ tmp2 = (a & 0xffffffff) * b;
++ if (tmp + tmp2 < tmp)
++ return -EOVERFLOW;
++
++ *ret = tmp + tmp2;
++ return 0;
++}
++
++static int hpet_ioctl_start(struct hpet_dev *devp, unsigned long arg)
++{
++ struct timespec timeout_ts;
++ struct hpet_timer __iomem *timer;
++ struct hpet __iomem *hpet;
++ struct hpets *hpetp;
++ unsigned long long timeout_ns, ticks, v;
++ unsigned long flags;
++
++ if (copy_from_user(&timeout_ts, (void __user *)arg, sizeof(timeout_ts)))
++ return -EFAULT;
++
++ timer = devp->hd_timer;
++ hpet = devp->hd_hpet;
++ hpetp = devp->hd_hpets;
++
++ timeout_ns = (unsigned long long) timeout_ts.tv_sec * NSEC_PER_SEC +
++ timeout_ts.tv_nsec;
++
++ /* NSEC_PER_SEC nanoseconds need hpetp->hp_tick_freq counter ticks.
++ *
++ * For timeout_ns, need hpetp->hp_tick_freq * timeout_ns / NSEC_PER_SEC
++ * ticks.
++ *
++ * Need to avoid 64bit overflow when computing that:
++ * - userspace could pass any sort of garbage,
++ * - no guarantee that HPET freq will always stay below 2^32 Hz
++ */
++
++ if (mul_u64_u64(timeout_ns, hpetp->hp_tick_freq, &ticks) != 0)
++ return -EINVAL;
++ do_div(ticks, NSEC_PER_SEC);
++ if (ticks >= 0x100000000ull)
++ return -EINVAL;
++
++ if (ticks < hpetp->hp_delta)
++ ticks = hpetp->hp_delta;
++
++ spin_lock_irq(&hpet_lock);
++
++ if (devp->hd_flags & (HPET_IE | HPET_PERIODIC | HPET_STARTED)) {
++ spin_unlock_irq(&hpet_lock);
++ return -EBUSY;
++ }
++
++ local_irq_save(flags);
++
++ v = read_counter(&hpet->hpet_mc);
++ devp->hd_start_tick = v;
++ v += ticks;
++ devp->hd_timeout_tick = v;
++ write_counter(v, &timer->hpet_compare);
++
++ v = readq(&timer->hpet_config);
++ v |= Tn_INT_ENB_CNF_MASK;
++ v &= ~Tn_TYPE_CNF_MASK;
++ writeq(v, &timer->hpet_config);
++
++ local_irq_restore(flags);
++
++ devp->hd_flags |= HPET_STARTED;
++
++ spin_unlock_irq(&hpet_lock);
++ return 0;
++}
++
++static int hpet_ioctl_stop(struct hpet_dev *devp)
++{
++ struct hpet_timer __iomem *timer;
++ unsigned long long v;
++
++ timer = devp->hd_timer;
++
++ spin_lock(&hpet_lock);
++
++ if (devp->hd_flags & HPET_STARTED) {
++ v = readq(&timer->hpet_config);
++ v &= ~Tn_INT_ENB_CNF_MASK;
++ writeq(v, &timer->hpet_config);
++ devp->hd_flags &= ~HPET_STARTED;
++ }
++
++ spin_unlock(&hpet_lock);
++ return 0;
++}
++
++static int hpet_ioctl_query(struct hpet_dev *devp, unsigned long arg)
++{
++ struct hpet_timer __iomem *timer;
++ struct hpet __iomem *hpet;
++ struct hpets *hpetp;
++ unsigned long m, a, b, ticks;
++ unsigned long long nsecs;
++ struct timespec res;
++
++ timer = devp->hd_timer;
++ hpet = devp->hd_hpet;
++ hpetp = devp->hd_hpets;
++
++ spin_lock(&hpet_lock);
++
++ if (!(devp->hd_flags & HPET_STARTED)) {
++ /* It is bad ideas to return error here since we could
++ * race with irq.
++ * Instread, return zero remaining time.
++ */
++ res.tv_sec = res.tv_nsec = 0;
++ goto out;
++ }
++
++ m = read_counter(&hpet->hpet_mc);
++
++ /* If this value is between devp->hd_start_tick and
++ * devp->hd_timeout_tick, then calculate remaining time,
++ * otherwise return zero.
++ */
++ a = devp->hd_start_tick;
++ b = devp->hd_timeout_tick;
++ if ((a < b && (a <= m && m <= b)) || ((a > b) && (a >= m || m <= b))) {
++ ticks = b - m;
++ nsecs = div64_u64((unsigned long long) ticks * NSEC_PER_SEC,
++ hpetp->hp_tick_freq);
++ res.tv_nsec = do_div(nsecs, NSEC_PER_SEC);
++ res.tv_sec = nsecs;
++ } else {
++ res.tv_sec = res.tv_nsec = 0;
++ }
++
++out:
++ spin_unlock(&hpet_lock);
++
++ if (copy_to_user((void *)arg, &res, sizeof(res)))
++ return -EFAULT;
++
++ return 0;
++}
++
+ /* converts Hz to number of timer ticks */
+ static inline unsigned long hpet_time_div(struct hpets *hpets,
+ unsigned long dis)
+@@ -541,6 +711,12 @@ hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg,
+ break;
+ case HPET_IE_ON:
+ return hpet_ioctl_ieon(devp);
++ case HPET_START:
++ return hpet_ioctl_start(devp, arg);
++ case HPET_STOP:
++ return hpet_ioctl_stop(devp);
++ case HPET_QUERY:
++ return hpet_ioctl_query(devp, arg);
+ default:
+ return -EINVAL;
+ }
+@@ -575,6 +751,8 @@ hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg,
+ v = readq(&timer->hpet_config);
+ if ((v & Tn_PER_INT_CAP_MASK) == 0)
+ err = -ENXIO;
++ else if (devp->hd_flags & HPET_STARTED)
++ err = -EBUSY;
+ else
+ devp->hd_flags |= HPET_PERIODIC;
+ spin_unlock_irq(&hpet_lock);
+diff --git a/include/uapi/linux/hpet.h b/include/uapi/linux/hpet.h
+index 8af3c70..e97ebea 100644
+--- a/include/uapi/linux/hpet.h
++++ b/include/uapi/linux/hpet.h
+@@ -1,8 +1,11 @@
+ #ifndef _UAPI__HPET__
+ #define _UAPI__HPET__
+
+-#include <linux/compiler.h>
+-
++#ifdef __KERNEL__
++#include <linux/time.h>
++#else
++#include <sys/time.h>
++#endif
+
+ struct hpet_info {
+ unsigned long hi_ireqfreq; /* Hz */
+@@ -20,6 +23,10 @@ struct hpet_info {
+ #define HPET_DPI _IO('h', 0x05) /* disable periodic */
+ #define HPET_IRQFREQ _IOW('h', 0x6, unsigned long) /* IRQFREQ usec */
+
++#define HPET_START _IOW('h', 0x7, struct timespec)
++#define HPET_STOP _IO('h', 0x8)
++#define HPET_QUERY _IOR('h', 0x9, struct timespec)
++
+ #define MAX_HPET_TBS 8 /* maximum hpet timer blocks */
+
+ #endif /* _UAPI__HPET__ */
+--
+1.7.7.6
+
diff --git a/features/soc/baytrail/hpet-refactor-driver.patch b/features/soc/baytrail/hpet-refactor-driver.patch
new file mode 100644
index 0000000..d45235e
--- /dev/null
+++ b/features/soc/baytrail/hpet-refactor-driver.patch
@@ -0,0 +1,721 @@
+From d04353466dbbc0e5a4fe42260955cd4d816c4362 Mon Sep 17 00:00:00 2001
+From: Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad at intel.com>
+Date: Sun, 26 Jul 2015 10:42:56 +0800
+Subject: [PATCH 134/164] hpet: refactor driver
+
+This commit performs the following,
+- no longer depends on ACPI tables define IRQs for all comparators
+- setup IRQs for comparators at register time
+ - use data from ACPI as a hint, but override it if it is not sane
+ - do not make timers without configured IRQ available for use
+- device open cleanup:
+ - set interrupt handler at open time, not at IE_ON time (needed for future
+ addition of singleshot interface)
+ - no more need for separate hd_hdwirq and hr_irq fields in struct hpet_dev
+- locking cleanup:
+ - remove hpet_mutex (that was introduced mechanically while removing BKL
+ dependency), keep only hpet_lock spinlock
+ - do not access to hardware while not locked
+ - handle race between interrupt handler and IE_OFF
+- generic cleanup:
+ - always use readq/writeq with hpet_config register
+ - set device name once at init time
+
+Fixing all the above in individual and smaller patches is technically hard.
+To do so, one will have have to add many lines of code to keep things working,
+and then remove these lines in the next patch in the patchset.
+
+Signed-off-by: Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad at intel.com>
+---
+ drivers/char/hpet.c | 459 ++++++++++++++++++++++++++-------------------------
+ 1 files changed, 237 insertions(+), 222 deletions(-)
+
+diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
+index d5d4cd8..49e6c9d 100644
+--- a/drivers/char/hpet.c
++++ b/drivers/char/hpet.c
+@@ -63,7 +63,6 @@
+ #define read_counter(MC) readl(MC)
+ #endif
+
+-static DEFINE_MUTEX(hpet_mutex); /* replaces BKL */
+ static u32 hpet_nhpet, hpet_max_freq = HPET_USER_FREQ;
+
+ /* This clocksource driver currently only works on ia64 */
+@@ -88,7 +87,7 @@ static struct clocksource *hpet_clocksource;
+ /* A lock for concurrent access by app and isr hpet activity. */
+ static DEFINE_SPINLOCK(hpet_lock);
+
+-#define HPET_DEV_NAME (7)
++static bool hpet_available;
+
+ struct hpet_dev {
+ struct hpets *hd_hpets;
+@@ -100,8 +99,8 @@ struct hpet_dev {
+ struct fasync_struct *hd_async_queue;
+ unsigned int hd_flags;
+ unsigned int hd_irq;
+- unsigned int hd_hdwirq;
+- char hd_name[HPET_DEV_NAME];
++#define HPET_DEV_NAME_SIZE 16
++ char hd_name[HPET_DEV_NAME_SIZE];
+ };
+
+ struct hpets {
+@@ -147,11 +146,24 @@ static irqreturn_t hpet_interrupt(int irq, void *data)
+ 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);
++
++ if (devp->hd_flags & HPET_SHARED_IRQ) {
++
++ if (!(readl(&devp->hd_hpet->hpet_isr) & isr)) {
++ spin_unlock(&hpet_lock);
++ return IRQ_NONE;
++ }
++
++ writel(isr, &devp->hd_hpet->hpet_isr);
++ }
++
++ /* we could race against disable ... */
++ if ((readq(&devp->hd_timer->hpet_config) & Tn_INT_ENB_CNF_MASK) == 0) {
++ spin_unlock(&hpet_lock);
++ return IRQ_HANDLED;
++ }
++
+ devp->hd_irqdata++;
+
+ /*
+@@ -187,8 +199,6 @@ static irqreturn_t hpet_interrupt(int irq, void *data)
+ &devp->hd_timer->hpet_compare);
+ }
+
+- if (devp->hd_flags & HPET_SHARED_IRQ)
+- writel(isr, &devp->hd_hpet->hpet_isr);
+ spin_unlock(&hpet_lock);
+
+ wake_up_interruptible(&devp->hd_waitqueue);
+@@ -198,101 +208,91 @@ static irqreturn_t hpet_interrupt(int irq, void *data)
+ return IRQ_HANDLED;
+ }
+
+-static void hpet_timer_set_irq(struct hpet_dev *devp)
++/* called under lock, temporary releases it */
++static int hpet_try_open(struct hpet_dev *devp)
+ {
+- unsigned long v;
+- int irq, gsi;
+ struct hpet_timer __iomem *timer;
+-
+- spin_lock_irq(&hpet_lock);
+- if (devp->hd_hdwirq) {
+- spin_unlock_irq(&hpet_lock);
+- return;
+- }
++ struct hpet __iomem *hpet;
++ struct hpets *hpetp;
++ unsigned long long v;
++ unsigned long irq_flags;
++ int ret;
+
+ timer = devp->hd_timer;
++ hpet = devp->hd_hpet;
++ hpetp = devp->hd_hpets;
+
+- /* we prefer level triggered mode */
+- v = readl(&timer->hpet_config);
+- if (!(v & Tn_INT_TYPE_CNF_MASK)) {
+- v |= Tn_INT_TYPE_CNF_MASK;
+- writel(v, &timer->hpet_config);
+- }
+- spin_unlock_irq(&hpet_lock);
++ /* Just for case: before requesting interrupt,
++ * - force timer into non-periodic mode,
++ * - ensure no match in near future,
++ * - disable interrupt generation and clear any pending interrupt */
+
+- v = (readq(&timer->hpet_config) & Tn_INT_ROUTE_CAP_MASK) >>
+- Tn_INT_ROUTE_CAP_SHIFT;
++ v = readq(&timer->hpet_config);
++ v &= ~(Tn_TYPE_CNF_MASK | Tn_INT_ENB_CNF_MASK);
++ writeq(v, &timer->hpet_config);
+
+- /*
+- * In PIC mode, skip IRQ0-4, IRQ6-9, IRQ12-15 which is always used by
+- * legacy device. In IO APIC mode, we skip all the legacy IRQS.
+- */
+- if (acpi_irq_model == ACPI_IRQ_MODEL_PIC)
+- v &= ~0xf3df;
+- else
+- v &= ~0xffff;
++ write_counter(read_counter(&hpet->hpet_mc) - 1, &timer->hpet_compare);
+
+- for_each_set_bit(irq, &v, HPET_MAX_IRQ) {
+- if (irq >= nr_irqs) {
+- irq = HPET_MAX_IRQ;
+- break;
+- }
++ if (v & Tn_INT_TYPE_CNF_MASK) {
++ devp->hd_flags |= HPET_SHARED_IRQ;
++ irq_flags = IRQF_SHARED;
++ } else
++ irq_flags = 0;
+
+- gsi = acpi_register_gsi(NULL, irq, ACPI_LEVEL_SENSITIVE,
+- ACPI_ACTIVE_LOW);
+- if (gsi > 0)
+- break;
++ if (devp->hd_flags & HPET_SHARED_IRQ)
++ writel(1 << (devp - hpetp->hp_dev), &hpet->hpet_isr);
+
+- /* FIXME: Setup interrupt source table */
+- }
++ devp->hd_irqdata = 0;
+
+- if (irq < HPET_MAX_IRQ) {
+- spin_lock_irq(&hpet_lock);
+- v = readl(&timer->hpet_config);
+- v |= irq << Tn_INT_ROUTE_CNF_SHIFT;
+- writel(v, &timer->hpet_config);
+- devp->hd_hdwirq = gsi;
+- spin_unlock_irq(&hpet_lock);
++ spin_unlock_irq(&hpet_lock);
++ ret = request_irq(devp->hd_irq, hpet_interrupt, irq_flags,
++ devp->hd_name, devp);
++ if (ret < 0) {
++ pr_warn("hpet%d: failed to request irq %d for comparator %d\n",
++ hpetp->hp_which, devp->hd_irq,
++ (int)(devp - hpetp->hp_dev));
+ }
+- return;
++ spin_lock_irq(&hpet_lock);
++
++ return ret;
+ }
+
+ static int hpet_open(struct inode *inode, struct file *file)
+ {
+ struct hpet_dev *devp;
+ struct hpets *hpetp;
+- int i;
++ int i, ret;
+
+ if (file->f_mode & FMODE_WRITE)
+ return -EINVAL;
+
+- mutex_lock(&hpet_mutex);
++ if (!hpet_available)
++ return -ENXIO;
++
+ 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)
+- continue;
+- else {
+- devp = &hpetp->hp_dev[i];
+- break;
+- }
++ ret = -EBUSY;
+
+- if (!devp) {
+- spin_unlock_irq(&hpet_lock);
+- mutex_unlock(&hpet_mutex);
+- return -EBUSY;
++ for (devp = NULL, hpetp = hpets; hpetp; hpetp = hpetp->hp_next) {
++ for (i = 0; i < hpetp->hp_ntimer; i++) {
++ devp = &hpetp->hp_dev[i];
++ if (devp->hd_flags & HPET_OPEN)
++ continue;
++ devp->hd_flags |= HPET_OPEN;
++ ret = hpet_try_open(&hpetp->hp_dev[i]);
++ if (!ret)
++ goto out;
++ devp->hd_flags &= ~HPET_OPEN;
++ }
+ }
+
+- file->private_data = devp;
+- devp->hd_irqdata = 0;
+- devp->hd_flags |= HPET_OPEN;
+- spin_unlock_irq(&hpet_lock);
+- mutex_unlock(&hpet_mutex);
++out:
++ if (!ret)
++ file->private_data = devp;
+
+- hpet_timer_set_irq(devp);
++ spin_unlock_irq(&hpet_lock);
+
+- return 0;
++ return ret;
+ }
+
+ static ssize_t
+@@ -419,35 +419,21 @@ static int hpet_release(struct inode *inode, struct file *file)
+ {
+ struct hpet_dev *devp;
+ struct hpet_timer __iomem *timer;
+- int irq = 0;
++ unsigned long long v;
+
+ 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);
+- }
++ v = readq(&timer->hpet_config);
++ v &= ~(Tn_INT_ENB_CNF_MASK | 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);
++ free_irq(devp->hd_irq, devp);
+
+ file->private_data = NULL;
+ return 0;
+@@ -458,7 +444,6 @@ 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;
+
+@@ -475,50 +460,8 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp)
+ spin_unlock_irq(&hpet_lock);
+ return -EBUSY;
+ }
+-
+ 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);
+-
+- irq = devp->hd_hdwirq;
+-
+- if (irq) {
+- unsigned long irq_flags;
+-
+- if (devp->hd_flags & HPET_SHARED_IRQ) {
+- /*
+- * To prevent the interrupt handler from seeing an
+- * unwanted interrupt status bit, program the timer
+- * so that it will not fire in the near future ...
+- */
+- writel(readl(&timer->hpet_config) & ~Tn_TYPE_CNF_MASK,
+- &timer->hpet_config);
+- write_counter(read_counter(&hpet->hpet_mc),
+- &timer->hpet_compare);
+- /* ... and clear any left-over status. */
+- isr = 1 << (devp - devp->hd_hpets->hp_dev);
+- writel(isr, &hpet->hpet_isr);
+- }
+-
+- sprintf(devp->hd_name, "hpet%d", (int)(devp - hpetp->hp_dev));
+- irq_flags = devp->hd_flags & HPET_SHARED_IRQ ? IRQF_SHARED : 0;
+- 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;
+- }
+- }
+-
+- 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);
+
+@@ -560,6 +503,8 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp)
+ writeq(g, &timer->hpet_config);
+ local_irq_restore(flags);
+
++ spin_unlock_irq(&hpet_lock);
++
+ return 0;
+ }
+
+@@ -604,50 +549,49 @@ hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg,
+
+ 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;
++ spin_lock_irq(&hpet_lock);
++ if ((devp->hd_flags & HPET_IE)) {
++ v = readq(&timer->hpet_config);
++ v &= ~Tn_INT_ENB_CNF_MASK;
++ writeq(v, &timer->hpet_config);
++ devp->hd_flags ^= HPET_IE;
+ }
+- devp->hd_flags ^= HPET_IE;
++ spin_unlock_irq(&hpet_lock);
+ break;
+ case HPET_INFO:
+- {
+- memset(info, 0, sizeof(*info));
+- if (devp->hd_ireqfreq)
+- info->hi_ireqfreq =
+- hpet_time_div(hpetp, devp->hd_ireqfreq);
+- info->hi_flags =
++ memset(info, 0, sizeof(*info));
++ spin_lock_irq(&hpet_lock);
++ if (devp->hd_ireqfreq)
++ info->hi_ireqfreq =
++ hpet_time_div(hpetp, devp->hd_ireqfreq);
++ info->hi_flags =
+ readq(&timer->hpet_config) & Tn_PER_INT_CAP_MASK;
+- info->hi_hpet = hpetp->hp_which;
+- info->hi_timer = devp - hpetp->hp_dev;
+- break;
+- }
++ info->hi_hpet = hpetp->hp_which;
++ info->hi_timer = devp - hpetp->hp_dev;
++ spin_unlock_irq(&hpet_lock);
++ break;
+ case HPET_EPI:
++ spin_lock_irq(&hpet_lock);
+ v = readq(&timer->hpet_config);
+- if ((v & Tn_PER_INT_CAP_MASK) == 0) {
++ if ((v & Tn_PER_INT_CAP_MASK) == 0)
+ err = -ENXIO;
+- break;
+- }
+- devp->hd_flags |= HPET_PERIODIC;
++ else
++ devp->hd_flags |= HPET_PERIODIC;
++ spin_unlock_irq(&hpet_lock);
+ break;
+ case HPET_DPI:
++ spin_lock_irq(&hpet_lock);
+ v = readq(&timer->hpet_config);
+- if ((v & Tn_PER_INT_CAP_MASK) == 0) {
++ 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);
++ else if (devp->hd_flags & HPET_PERIODIC) {
++ if (v & Tn_TYPE_CNF_MASK) {
++ v ^= Tn_TYPE_CNF_MASK;
++ writeq(v, &timer->hpet_config);
++ }
++ devp->hd_flags &= ~HPET_PERIODIC;
+ }
+- devp->hd_flags &= ~HPET_PERIODIC;
++ spin_unlock_irq(&hpet_lock);
+ break;
+ case HPET_IRQFREQ:
+ if ((arg > hpet_max_freq) &&
+@@ -661,7 +605,9 @@ hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg,
+ break;
+ }
+
++ spin_lock_irq(&hpet_lock);
+ devp->hd_ireqfreq = hpet_time_div(hpetp, arg);
++ spin_unlock_irq(&hpet_lock);
+ }
+
+ return err;
+@@ -673,9 +619,7 @@ hpet_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+ struct hpet_info info;
+ int err;
+
+- mutex_lock(&hpet_mutex);
+ err = hpet_ioctl_common(file->private_data, cmd, arg, &info);
+- mutex_unlock(&hpet_mutex);
+
+ if ((cmd == HPET_INFO) && !err &&
+ (copy_to_user((void __user *)arg, &info, sizeof(info))))
+@@ -698,9 +642,7 @@ hpet_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+ struct hpet_info info;
+ int err;
+
+- mutex_lock(&hpet_mutex);
+ err = hpet_ioctl_common(file->private_data, cmd, arg, &info);
+- mutex_unlock(&hpet_mutex);
+
+ if ((cmd == HPET_INFO) && !err) {
+ struct compat_hpet_info __user *u = compat_ptr(arg);
+@@ -840,12 +782,14 @@ static unsigned long hpet_calibrate(struct hpets *hpetp)
+
+ int hpet_alloc(struct hpet_data *hdp)
+ {
+- u64 cap, mcfg;
++ u64 cap, conf;
++ unsigned long mask, mask2, used_mask;
+ struct hpet_dev *devp;
+- u32 i, ntimer;
++ int i, ntimer, irq, gsi;
+ struct hpets *hpetp;
+ size_t siz;
+ struct hpet __iomem *hpet;
++ struct hpet_timer __iomem *timer;
+ static struct hpets *last;
+ unsigned long period;
+ unsigned long long temp;
+@@ -862,43 +806,27 @@ int hpet_alloc(struct hpet_data *hdp)
+ return 0;
+ }
+
+- siz = sizeof(struct hpets) + ((hdp->hd_nirqs - 1) *
+- sizeof(struct hpet_dev));
++ hpet = hdp->hd_address;
++ cap = readq(&hpet->hpet_cap);
++ ntimer = ((cap & HPET_NUM_TIM_CAP_MASK) >> HPET_NUM_TIM_CAP_SHIFT) + 1;
+
++ siz = sizeof(struct hpets) + ((ntimer - 1) * sizeof(struct hpet_dev));
+ hpetp = kzalloc(siz, GFP_KERNEL);
+-
+ if (!hpetp)
+ return -ENOMEM;
+
+ 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;
+- }
++ hpetp->hp_ntimer = ntimer;
+
+ if (last)
+ last->hp_next = hpetp;
+ else
+ hpets = hpetp;
+-
+ last = hpetp;
+
++
+ period = (cap & HPET_COUNTER_CLK_PERIOD_MASK) >>
+ HPET_COUNTER_CLK_PERIOD_SHIFT; /* fs, 10^-15 */
+ temp = 1000000000000000uLL; /* 10^15 femtoseconds per second */
+@@ -906,49 +834,137 @@ int hpet_alloc(struct hpet_data *hdp)
+ 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,
+- hpetp->hp_ntimer > 1 ? "s" : "");
+- for (i = 0; i < hpetp->hp_ntimer; i++)
+- printk(KERN_CONT "%s %d", i > 0 ? "," : "", hdp->hd_irq[i]);
+- printk(KERN_CONT "\n");
+-
+ temp = hpetp->hp_tick_freq;
+ remainder = do_div(temp, 1000000);
+- printk(KERN_INFO
+- "hpet%u: %u comparators, %d-bit %u.%06u MHz counter\n",
+- hpetp->hp_which, hpetp->hp_ntimer,
++
++ pr_info("hpet%d: at MMIO 0x%lx, %u comparators, "
++ "%d-bit %u.%06u MHz counter\n",
++ hpetp->hp_which, hdp->hd_phys_address, ntimer,
+ cap & HPET_COUNTER_SIZE_MASK ? 64 : 32,
+ (unsigned) temp, remainder);
+
+- 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);
++
++ /* If HPET module is not used by platform code (which means, has no
++ * platform-reserved timers), disable legacy replacement IRQ routing
++ * for it */
++ if (!hdp->hd_state) {
++ conf = readq(&hpet->hpet_config);
++ if (conf & HPET_LEG_RT_CNF_MASK) {
++ pr_info("hpet%d: disabling legacy replacement "
++ "IRQ routing\n", hpetp->hp_which);
++ conf &= ~HPET_LEG_RT_CNF_MASK;
++ writeq(conf, &hpet->hpet_config);
++ }
+ }
+
+- for (i = 0, devp = hpetp->hp_dev; i < hpetp->hp_ntimer; i++, devp++) {
+- struct hpet_timer __iomem *timer;
++ used_mask = 0;
++
++ for (i = 0; i < ntimer; i++) {
+
+- timer = &hpet->hpet_timers[devp - hpetp->hp_dev];
++ devp = &hpetp->hp_dev[i];
++ timer = &hpet->hpet_timers[i];
+
+ 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.
+- */
++ snprintf(devp->hd_name, HPET_DEV_NAME_SIZE,
++ "hpet%d.%d", hpetp->hp_which, i);
++
+ if (hdp->hd_state & (1 << i)) {
++ /* This driver does not manage platform-reserved
++ * timers */
++ pr_info("hpet%d: comparator %d reserved for "
++ "system needs\n", hpetp->hp_which, i);
+ devp->hd_flags = HPET_OPEN;
+ continue;
+ }
+
++ /* Find out mask of IRQs applicable for this timer */
++
++ conf = readq(&timer->hpet_config);
++ mask = (conf & Tn_INT_ROUTE_CAP_MASK) >> Tn_INT_ROUTE_CAP_SHIFT;
++ if (nr_irqs < 32)
++ mask &= ((1 << nr_irqs) - 1);
++ /* In PIC mode, blacklist IRQ0-4, IRQ6-9, IRQ12-15.
++ * In IO APIC mode, blacklist all legasy IRQs */
++ if (acpi_irq_model == ACPI_IRQ_MODEL_PIC)
++ mask &= ~0xf3df;
++ else
++ mask &= ~0xffff;
++
++ if (i < hdp->hd_nirqs && hdp->hd_irq[i]) {
++ /* ACPI provided an IRQ for this timer.
++ *
++ * Need to rule out situation when this IRQ selection
++ * is not friendly to the rest of the system (e.g.
++ * it is legacy RTC irq). To make different cases more
++ * similar, compare against the same mask as is used
++ * when we choose IRQ below.
++ *
++ * There is a sort of namespace violation here:
++ * hdp->hd_irq[i] is a result of acpi_register_gsi()
++ * call, while mask is in terms of argument to
++ * that call. However looks like in practice these are
++ * the same... FIXME.
++ */
++ if ((1 << hdp->hd_irq[i]) & mask) {
++ irq = gsi = hdp->hd_irq[i];
++ goto setup_irq;
++ }
++ }
++
++ /* First try IRQs not yet used by other timers */
++ mask2 = mask & (~used_mask);
++ for_each_set_bit(irq, &mask2, HPET_MAX_IRQ) {
++ gsi = acpi_register_gsi(NULL, irq,
++ ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW);
++ if (gsi > 0) {
++ /* level-sensitive */
++ conf |= Tn_INT_TYPE_CNF_MASK;
++ goto setup_irq;
++ }
++ }
++
++ /* Then try any possible IRQ */
++ for_each_set_bit(irq, &mask, HPET_MAX_IRQ) {
++ gsi = acpi_register_gsi(NULL, irq,
++ ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW);
++ if (gsi > 0) {
++ /* level-sensitive */
++ conf |= Tn_INT_TYPE_CNF_MASK;
++ goto setup_irq;
++ }
++ }
++
++ /* No useable IRQ */
++ pr_warn("hpet%d: no useable IRQ for "
++ "comparator %d\n", hpetp->hp_which, i);
++ devp->hd_flags = HPET_OPEN;
++ continue;
++
++setup_irq:
++ devp->hd_irq = gsi;
++ used_mask |= (1 << irq);
++
++ conf &= ~Tn_INT_ROUTE_CNF_MASK;
++ conf |= (irq << Tn_INT_ROUTE_CNF_SHIFT);
++ writeq(conf, &timer->hpet_config);
++
++ pr_info("hpet%d: comparator %d available, "
++ "using irq %d\n", hpetp->hp_which, i, gsi);
++
++ hpet_available = true;
+ init_waitqueue_head(&devp->hd_waitqueue);
+ }
+
++ conf = readq(&hpet->hpet_config);
++ if ((conf & HPET_ENABLE_CNF_MASK) == 0) {
++ write_counter(0L, &hpet->hpet_mc);
++ conf |= HPET_ENABLE_CNF_MASK;
++ writeq(conf, &hpet->hpet_config);
++ }
++
+ hpetp->hp_delta = hpet_calibrate(hpetp);
+
+ /* This clocksource driver currently only works on ia64 */
+@@ -1026,8 +1042,7 @@ static int hpet_acpi_add(struct acpi_device *device)
+
+ memset(&data, 0, sizeof(data));
+
+- result =
+- acpi_walk_resources(device->handle, METHOD_NAME__CRS,
++ result = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
+ hpet_resources, &data);
+
+ if (ACPI_FAILURE(result))
+--
+1.7.7.6
+
--
1.9.1
More information about the linux-yocto
mailing list