diff options
-rw-r--r-- | Documentation/timers/hpet.txt | 43 | ||||
-rw-r--r-- | arch/x86/kernel/hpet.c | 6 | ||||
-rw-r--r-- | drivers/char/hpet.c | 90 | ||||
-rw-r--r-- | include/linux/hpet.h | 11 |
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 | ||
3 | The High Precision Event Timer (HPET) hardware is the future replacement | 3 | The High Precision Event Timer (HPET) hardware follows a specification |
4 | for the 8254 and Real Time Clock (RTC) periodic timer functionality. | 4 | by Intel and Microsoft which can be found at |
5 | Each HPET can have up to 32 timers. It is possible to configure the | 5 | |
6 | first two timers as legacy replacements for 8254 and RTC periodic timers. | 6 | http://www.intel.com/technology/architecture/hpetspec.htm |
7 | A specification done by Intel and Microsoft can be found at | 7 | |
8 | <http://www.intel.com/technology/architecture/hpetspec.htm>. | 8 | Each HPET has one fixed-rate counter (at 10+ MHz, hence "High Precision") |
9 | and up to 32 comparators. Normally three or more comparators are provided, | ||
10 | each of which can generate oneshot interupts and at least one of which has | ||
11 | additional hardware to support periodic interrupts. The comparators are | ||
12 | also called "timers", which can be misleading since usually timers are | ||
13 | independent of each other ... these share a counter, complicating resets. | ||
14 | |||
15 | HPET devices can support two interrupt routing modes. In one mode, the | ||
16 | comparators are additional interrupt sources with no particular system | ||
17 | role. Many x86 BIOS writers don't route HPET interrupts at all, which | ||
18 | prevents use of that mode. They support the other "legacy replacement" | ||
19 | mode where the first two comparators block interrupts from 8254 timers | ||
20 | and from the RTC. | ||
9 | 21 | ||
10 | The driver supports detection of HPET driver allocation and initialization | 22 | The driver supports detection of HPET driver allocation and initialization |
11 | of the HPET before the driver module_init routine is called. This enables | 23 | of the HPET before the driver module_init routine is called. This enables |
12 | platform code which uses timer 0 or 1 as the main timer to intercept HPET | 24 | platform code which uses timer 0 or 1 as the main timer to intercept HPET |
13 | initialization. An example of this initialization can be found in | 25 | initialization. An example of this initialization can be found in |
14 | arch/i386/kernel/time_hpet.c. | 26 | arch/x86/kernel/hpet.c. |
15 | 27 | ||
16 | The driver provides two APIs which are very similar to the API found in | 28 | The driver provides a userspace API which resembles the API found in the |
17 | the rtc.c driver. There is a user space API and a kernel space API. | 29 | RTC driver framework. An example user space program is provided below. |
18 | An 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 | |||
290 | The 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 | |||
296 | The kernel module using this interface fills in the ht_func and ht_data | ||
297 | members of the hpet_task structure before calling hpet_register. | ||
298 | hpet_control simply vectors to the hpet_ioctl routine and has the same | ||
299 | commands and respective arguments as the user API. hpet_unregister | ||
300 | is 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. */ |
88 | static DEFINE_SPINLOCK(hpet_lock); | 93 | static DEFINE_SPINLOCK(hpet_lock); |
89 | /* A lock for concurrent intermodule access to hpet and isr hpet activity. */ | ||
90 | static 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 | ||
670 | static 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 | ||
690 | int 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 | |||
721 | static ctl_table hpet_table[] = { | 676 | static 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 | ||
95 | struct hpet_task { | ||
96 | void (*ht_func) (void *); | ||
97 | void *ht_data; | ||
98 | void *ht_opaque; | ||
99 | }; | ||
100 | |||
101 | struct hpet_data { | 95 | struct 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 | |||
112 | static inline void hpet_reserve_timer(struct hpet_data *hd, int timer) | 103 | static 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 */ |