From: "Chai, Chong Yi" <chong.yi.c...@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.moha...@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.moha...@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.moha...@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.moha...@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.moha...@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.moha...@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 -- _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto