aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Brownell <dbrownell@users.sourceforge.net>2008-07-29 15:47:38 -0400
committerIngo Molnar <mingo@elte.hu>2008-07-31 12:45:41 -0400
commit64a76f667d987a559ad0726b4692c987800b22bc (patch)
tree863fe7378dceb010d84dc723f734192b0c5b0e1f
parent85e9ca333d03fbd56b9e123c8456f0d98e20faad (diff)
hpet: /dev/hpet - fixes and cleanup
Minor /dev/hpet updates and bugfixes: * Remove dead code, mostly remnants of an incomplete/unusable kernel interface ... noted when addressing "sparse" warnings: + hpet_unregister() and a routine it calls + hpet_task and all references, including hpet_task_lock + hpet_data.hd_flags (and HPET_DATA_PLATFORM) * Correct and improve boot message: + displays *counter* (shared between comparators) bit width, not *timer* bit widths (which are often mixed) + relabel "timers" as "comparators"; this is less confusing, they are not independent like normal timers are (sigh) + display MHz not Hz; it's never less than 10 MHz. * Tighten and correct the userspace interface code + don't accidentally program comparators in 64-bit mode using 32-bit values ... always force comparators into 32-bit mode + provide the correct bit definition flagging comparators with periodic capability ... the ABI is unchanged * Update Documentation/hpet.txt + be more correct and current + expand description a bit + don't mention that now-gone kernel interface Plus, add a FIXME comment for something that could cause big trouble on systems with more capable HPETs than at least Intel seems to ship. It seems that few folk use this userspace interface; it's not very usable given the general lack of HPET IRQ routing. I'm told that the only real point of it any more is to mmap for fast timestamps; IMO that's handled better through the gettimeofday() vsyscall. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Acked-by: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--Documentation/timers/hpet.txt43
-rw-r--r--arch/x86/kernel/hpet.c6
-rw-r--r--drivers/char/hpet.c90
-rw-r--r--include/linux/hpet.h11
4 files changed, 51 insertions, 99 deletions
diff --git a/Documentation/timers/hpet.txt b/Documentation/timers/hpet.txt
index 6ad52d9dad6c..e7c09abcfab4 100644
--- a/Documentation/timers/hpet.txt
+++ b/Documentation/timers/hpet.txt
@@ -1,21 +1,32 @@
1 High Precision Event Timer Driver for Linux 1 High Precision Event Timer Driver for Linux
2 2
3The High Precision Event Timer (HPET) hardware is the future replacement 3The High Precision Event Timer (HPET) hardware follows a specification
4for the 8254 and Real Time Clock (RTC) periodic timer functionality. 4by Intel and Microsoft which can be found at
5Each HPET can have up to 32 timers. It is possible to configure the 5
6first two timers as legacy replacements for 8254 and RTC periodic timers. 6 http://www.intel.com/technology/architecture/hpetspec.htm
7A specification done by Intel and Microsoft can be found at 7
8<http://www.intel.com/technology/architecture/hpetspec.htm>. 8Each HPET has one fixed-rate counter (at 10+ MHz, hence "High Precision")
9and up to 32 comparators. Normally three or more comparators are provided,
10each of which can generate oneshot interupts and at least one of which has
11additional hardware to support periodic interrupts. The comparators are
12also called "timers", which can be misleading since usually timers are
13independent of each other ... these share a counter, complicating resets.
14
15HPET devices can support two interrupt routing modes. In one mode, the
16comparators are additional interrupt sources with no particular system
17role. Many x86 BIOS writers don't route HPET interrupts at all, which
18prevents use of that mode. They support the other "legacy replacement"
19mode where the first two comparators block interrupts from 8254 timers
20and from the RTC.
9 21
10The driver supports detection of HPET driver allocation and initialization 22The driver supports detection of HPET driver allocation and initialization
11of the HPET before the driver module_init routine is called. This enables 23of the HPET before the driver module_init routine is called. This enables
12platform code which uses timer 0 or 1 as the main timer to intercept HPET 24platform code which uses timer 0 or 1 as the main timer to intercept HPET
13initialization. An example of this initialization can be found in 25initialization. An example of this initialization can be found in
14arch/i386/kernel/time_hpet.c. 26arch/x86/kernel/hpet.c.
15 27
16The driver provides two APIs which are very similar to the API found in 28The driver provides a userspace API which resembles the API found in the
17the rtc.c driver. There is a user space API and a kernel space API. 29RTC driver framework. An example user space program is provided below.
18An example user space program is provided below.
19 30
20#include <stdio.h> 31#include <stdio.h>
21#include <stdlib.h> 32#include <stdlib.h>
@@ -286,15 +297,3 @@ out:
286 297
287 return; 298 return;
288} 299}
289
290The kernel API has three interfaces exported from the driver:
291
292 hpet_register(struct hpet_task *tp, int periodic)
293 hpet_unregister(struct hpet_task *tp)
294 hpet_control(struct hpet_task *tp, unsigned int cmd, unsigned long arg)
295
296The kernel module using this interface fills in the ht_func and ht_data
297members of the hpet_task structure before calling hpet_register.
298hpet_control simply vectors to the hpet_ioctl routine and has the same
299commands and respective arguments as the user API. hpet_unregister
300is used to terminate usage of the HPET timer reserved by hpet_register.
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index ad2b15a1334d..82d459186fd8 100644
--- a/arch/x86/kernel/hpet.c
+++ b/arch/x86/kernel/hpet.c
@@ -115,13 +115,17 @@ static void hpet_reserve_platform_timers(unsigned long id)
115 hd.hd_phys_address = hpet_address; 115 hd.hd_phys_address = hpet_address;
116 hd.hd_address = hpet; 116 hd.hd_address = hpet;
117 hd.hd_nirqs = nrtimers; 117 hd.hd_nirqs = nrtimers;
118 hd.hd_flags = HPET_DATA_PLATFORM;
119 hpet_reserve_timer(&hd, 0); 118 hpet_reserve_timer(&hd, 0);
120 119
121#ifdef CONFIG_HPET_EMULATE_RTC 120#ifdef CONFIG_HPET_EMULATE_RTC
122 hpet_reserve_timer(&hd, 1); 121 hpet_reserve_timer(&hd, 1);
123#endif 122#endif
124 123
124 /*
125 * NOTE that hd_irq[] reflects IOAPIC input pins (LEGACY_8254
126 * is wrong for i8259!) not the output IRQ. Many BIOS writers
127 * don't bother configuring *any* comparator interrupts.
128 */
125 hd.hd_irq[0] = HPET_LEGACY_8254; 129 hd.hd_irq[0] = HPET_LEGACY_8254;
126 hd.hd_irq[1] = HPET_LEGACY_RTC; 130 hd.hd_irq[1] = HPET_LEGACY_RTC;
127 131
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index f3981ffe20f0..4bc1da4d4f80 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -53,6 +53,11 @@
53 53
54#define HPET_RANGE_SIZE 1024 /* from HPET spec */ 54#define HPET_RANGE_SIZE 1024 /* from HPET spec */
55 55
56
57/* WARNING -- don't get confused. These macros are never used
58 * to write the (single) counter, and rarely to read it.
59 * They're badly named; to fix, someday.
60 */
56#if BITS_PER_LONG == 64 61#if BITS_PER_LONG == 64
57#define write_counter(V, MC) writeq(V, MC) 62#define write_counter(V, MC) writeq(V, MC)
58#define read_counter(MC) readq(MC) 63#define read_counter(MC) readq(MC)
@@ -77,7 +82,7 @@ static struct clocksource clocksource_hpet = {
77 .rating = 250, 82 .rating = 250,
78 .read = read_hpet, 83 .read = read_hpet,
79 .mask = CLOCKSOURCE_MASK(64), 84 .mask = CLOCKSOURCE_MASK(64),
80 .mult = 0, /*to be caluclated*/ 85 .mult = 0, /* to be calculated */
81 .shift = 10, 86 .shift = 10,
82 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 87 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
83}; 88};
@@ -86,8 +91,6 @@ static struct clocksource *hpet_clocksource;
86 91
87/* A lock for concurrent access by app and isr hpet activity. */ 92/* A lock for concurrent access by app and isr hpet activity. */
88static DEFINE_SPINLOCK(hpet_lock); 93static DEFINE_SPINLOCK(hpet_lock);
89/* A lock for concurrent intermodule access to hpet and isr hpet activity. */
90static DEFINE_SPINLOCK(hpet_task_lock);
91 94
92#define HPET_DEV_NAME (7) 95#define HPET_DEV_NAME (7)
93 96
@@ -99,7 +102,6 @@ struct hpet_dev {
99 unsigned long hd_irqdata; 102 unsigned long hd_irqdata;
100 wait_queue_head_t hd_waitqueue; 103 wait_queue_head_t hd_waitqueue;
101 struct fasync_struct *hd_async_queue; 104 struct fasync_struct *hd_async_queue;
102 struct hpet_task *hd_task;
103 unsigned int hd_flags; 105 unsigned int hd_flags;
104 unsigned int hd_irq; 106 unsigned int hd_irq;
105 unsigned int hd_hdwirq; 107 unsigned int hd_hdwirq;
@@ -173,11 +175,6 @@ static irqreturn_t hpet_interrupt(int irq, void *data)
173 writel(isr, &devp->hd_hpet->hpet_isr); 175 writel(isr, &devp->hd_hpet->hpet_isr);
174 spin_unlock(&hpet_lock); 176 spin_unlock(&hpet_lock);
175 177
176 spin_lock(&hpet_task_lock);
177 if (devp->hd_task)
178 devp->hd_task->ht_func(devp->hd_task->ht_data);
179 spin_unlock(&hpet_task_lock);
180
181 wake_up_interruptible(&devp->hd_waitqueue); 178 wake_up_interruptible(&devp->hd_waitqueue);
182 179
183 kill_fasync(&devp->hd_async_queue, SIGIO, POLL_IN); 180 kill_fasync(&devp->hd_async_queue, SIGIO, POLL_IN);
@@ -260,8 +257,7 @@ static int hpet_open(struct inode *inode, struct file *file)
260 257
261 for (devp = NULL, hpetp = hpets; hpetp && !devp; hpetp = hpetp->hp_next) 258 for (devp = NULL, hpetp = hpets; hpetp && !devp; hpetp = hpetp->hp_next)
262 for (i = 0; i < hpetp->hp_ntimer; i++) 259 for (i = 0; i < hpetp->hp_ntimer; i++)
263 if (hpetp->hp_dev[i].hd_flags & HPET_OPEN 260 if (hpetp->hp_dev[i].hd_flags & HPET_OPEN)
264 || hpetp->hp_dev[i].hd_task)
265 continue; 261 continue;
266 else { 262 else {
267 devp = &hpetp->hp_dev[i]; 263 devp = &hpetp->hp_dev[i];
@@ -504,7 +500,11 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp)
504 devp->hd_irq = irq; 500 devp->hd_irq = irq;
505 t = devp->hd_ireqfreq; 501 t = devp->hd_ireqfreq;
506 v = readq(&timer->hpet_config); 502 v = readq(&timer->hpet_config);
507 g = v | Tn_INT_ENB_CNF_MASK; 503
504 /* 64-bit comparators are not yet supported through the ioctls,
505 * so force this into 32-bit mode if it supports both modes
506 */
507 g = v | Tn_32MODE_CNF_MASK | Tn_INT_ENB_CNF_MASK;
508 508
509 if (devp->hd_flags & HPET_PERIODIC) { 509 if (devp->hd_flags & HPET_PERIODIC) {
510 write_counter(t, &timer->hpet_compare); 510 write_counter(t, &timer->hpet_compare);
@@ -514,6 +514,12 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp)
514 v |= Tn_VAL_SET_CNF_MASK; 514 v |= Tn_VAL_SET_CNF_MASK;
515 writeq(v, &timer->hpet_config); 515 writeq(v, &timer->hpet_config);
516 local_irq_save(flags); 516 local_irq_save(flags);
517
518 /* NOTE: what we modify here is a hidden accumulator
519 * register supported by periodic-capable comparators.
520 * We never want to modify the (single) counter; that
521 * would affect all the comparators.
522 */
517 m = read_counter(&hpet->hpet_mc); 523 m = read_counter(&hpet->hpet_mc);
518 write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare); 524 write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare);
519 } else { 525 } else {
@@ -667,57 +673,6 @@ static int hpet_is_known(struct hpet_data *hdp)
667 return 0; 673 return 0;
668} 674}
669 675
670static inline int hpet_tpcheck(struct hpet_task *tp)
671{
672 struct hpet_dev *devp;
673 struct hpets *hpetp;
674
675 devp = tp->ht_opaque;
676
677 if (!devp)
678 return -ENXIO;
679
680 for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next)
681 if (devp >= hpetp->hp_dev
682 && devp < (hpetp->hp_dev + hpetp->hp_ntimer)
683 && devp->hd_hpet == hpetp->hp_hpet)
684 return 0;
685
686 return -ENXIO;
687}
688
689#if 0
690int hpet_unregister(struct hpet_task *tp)
691{
692 struct hpet_dev *devp;
693 struct hpet_timer __iomem *timer;
694 int err;
695
696 if ((err = hpet_tpcheck(tp)))
697 return err;
698
699 spin_lock_irq(&hpet_task_lock);
700 spin_lock(&hpet_lock);
701
702 devp = tp->ht_opaque;
703 if (devp->hd_task != tp) {
704 spin_unlock(&hpet_lock);
705 spin_unlock_irq(&hpet_task_lock);
706 return -ENXIO;
707 }
708
709 timer = devp->hd_timer;
710 writeq((readq(&timer->hpet_config) & ~Tn_INT_ENB_CNF_MASK),
711 &timer->hpet_config);
712 devp->hd_flags &= ~(HPET_IE | HPET_PERIODIC);
713 devp->hd_task = NULL;
714 spin_unlock(&hpet_lock);
715 spin_unlock_irq(&hpet_task_lock);
716
717 return 0;
718}
719#endif /* 0 */
720
721static ctl_table hpet_table[] = { 676static ctl_table hpet_table[] = {
722 { 677 {
723 .ctl_name = CTL_UNNUMBERED, 678 .ctl_name = CTL_UNNUMBERED,
@@ -872,9 +827,12 @@ int hpet_alloc(struct hpet_data *hdp)
872 printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); 827 printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]);
873 printk("\n"); 828 printk("\n");
874 829
875 printk(KERN_INFO "hpet%u: %u %d-bit timers, %Lu Hz\n", 830 printk(KERN_INFO
876 hpetp->hp_which, hpetp->hp_ntimer, 831 "hpet%u: %u comparators, %d-bit %u.%06u MHz counter\n",
877 cap & HPET_COUNTER_SIZE_MASK ? 64 : 32, hpetp->hp_tick_freq); 832 hpetp->hp_which, hpetp->hp_ntimer,
833 cap & HPET_COUNTER_SIZE_MASK ? 64 : 32,
834 (unsigned) (hpetp->hp_tick_freq / 1000000),
835 (unsigned) (hpetp->hp_tick_freq % 1000000));
878 836
879 mcfg = readq(&hpet->hpet_config); 837 mcfg = readq(&hpet->hpet_config);
880 if ((mcfg & HPET_ENABLE_CNF_MASK) == 0) { 838 if ((mcfg & HPET_ENABLE_CNF_MASK) == 0) {
diff --git a/include/linux/hpet.h b/include/linux/hpet.h
index 6d2626b63a9a..79f63a27bcef 100644
--- a/include/linux/hpet.h
+++ b/include/linux/hpet.h
@@ -92,23 +92,14 @@ struct hpet {
92 * exported interfaces 92 * exported interfaces
93 */ 93 */
94 94
95struct hpet_task {
96 void (*ht_func) (void *);
97 void *ht_data;
98 void *ht_opaque;
99};
100
101struct hpet_data { 95struct hpet_data {
102 unsigned long hd_phys_address; 96 unsigned long hd_phys_address;
103 void __iomem *hd_address; 97 void __iomem *hd_address;
104 unsigned short hd_nirqs; 98 unsigned short hd_nirqs;
105 unsigned short hd_flags;
106 unsigned int hd_state; /* timer allocated */ 99 unsigned int hd_state; /* timer allocated */
107 unsigned int hd_irq[HPET_MAX_TIMERS]; 100 unsigned int hd_irq[HPET_MAX_TIMERS];
108}; 101};
109 102
110#define HPET_DATA_PLATFORM 0x0001 /* platform call to hpet_alloc */
111
112static inline void hpet_reserve_timer(struct hpet_data *hd, int timer) 103static inline void hpet_reserve_timer(struct hpet_data *hd, int timer)
113{ 104{
114 hd->hd_state |= (1 << timer); 105 hd->hd_state |= (1 << timer);
@@ -126,7 +117,7 @@ struct hpet_info {
126 unsigned short hi_timer; 117 unsigned short hi_timer;
127}; 118};
128 119
129#define HPET_INFO_PERIODIC 0x0001 /* timer is periodic */ 120#define HPET_INFO_PERIODIC 0x0010 /* periodic-capable comparator */
130 121
131#define HPET_IE_ON _IO('h', 0x01) /* interrupt on */ 122#define HPET_IE_ON _IO('h', 0x01) /* interrupt on */
132#define HPET_IE_OFF _IO('h', 0x02) /* interrupt off */ 123#define HPET_IE_OFF _IO('h', 0x02) /* interrupt off */