]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | |
2 | From: Jiri Bohac <jbohac@suse.cz> | |
3 | Subject: allow 64-bit mode for HPET Timer0 | |
4 | References: bnc#456700 | |
5 | ||
6 | The kernel uses the HPET timers in 32-bit mode for clock-events. | |
7 | While 32 bits, with a wrap-around time of >4 minutes, is probably | |
8 | good enough for the clock-event purposes, on some chipsets this | |
9 | has a negative side-effect on the HPET main counter. | |
10 | ||
11 | Unlike the original HPET specification 1.0 from 2004, which does not | |
12 | mention any side-effects of setting TN_32MODE_CNF on the | |
13 | individual timers, the ICH9 documentation, for example, says: | |
14 | ||
15 | NOTE: When this bit is set to ‘1’, the hardware counter will | |
16 | do a 32-bit operation on comparator match and rollovers, thus | |
17 | the upper 32-bit of the Timer 0 Comparator Value register is | |
18 | ignored. The upper 32-bit of the main counter is not involved | |
19 | in any rollover from lower 32-bit of the main counter and | |
20 | becomes all zeros. | |
21 | ||
22 | (see http://www.intel.com/assets/pdf/datasheet/316972.pdf, page | |
23 | 819, section 21.1.5, Bit 8). I've seen this behaviour also on | |
24 | ICH8. I have no idea what other chipsets are affected. But I have | |
25 | seen AMD chipsets that Do The Right Thing. | |
26 | ||
27 | This means, that when the kernel configures the Timer 0 to 32-bit | |
28 | mode, on these chipsets it also cripples the 64-bit main counter | |
29 | to 32 bits. | |
30 | ||
31 | The HPET may be mmapped in userspace and the main counter | |
32 | accessed directly by applications, expecting a 64-bit main | |
33 | counter. | |
34 | ||
35 | This patch allows the Timer0 to be configured in 64-bit mode | |
36 | on x86_64 when a hpet64 command-line option is specified. | |
37 | ||
38 | Signed-off-by: Jiri Bohac <jbohac@suse.cz> | |
39 | ||
40 | --- | |
41 | arch/x86/kernel/hpet.c | 89 +++++++++++++++++++++++++++++++++++++++++++------ | |
42 | 1 file changed, 79 insertions(+), 10 deletions(-) | |
43 | ||
44 | --- a/arch/x86/kernel/hpet.c 2009-01-24 16:39:47.000000000 +0100 | |
45 | +++ b/arch/x86/kernel/hpet.c 2009-01-24 16:56:04.000000000 +0100 | |
46 | @@ -24,6 +24,7 @@ | |
47 | */ | |
48 | unsigned long hpet_address; | |
49 | static void __iomem *hpet_virt_address; | |
50 | +static int hpet_legacy_use_64_bits; | |
51 | ||
52 | unsigned long hpet_readl(unsigned long a) | |
53 | { | |
54 | @@ -37,6 +38,33 @@ static inline void hpet_writel(unsigned | |
55 | ||
56 | #ifdef CONFIG_X86_64 | |
57 | #include <asm/pgtable.h> | |
58 | +static inline unsigned long hpet_read_value(unsigned long a) | |
59 | +{ | |
60 | + if (hpet_legacy_use_64_bits) | |
61 | + return readq(hpet_virt_address + a); | |
62 | + else | |
63 | + return readl(hpet_virt_address + a); | |
64 | +} | |
65 | + | |
66 | +static void hpet_write_value(unsigned long d, unsigned long a) | |
67 | +{ | |
68 | + if (hpet_legacy_use_64_bits) | |
69 | + writeq(d, hpet_virt_address + a); | |
70 | + else | |
71 | + writel(d, hpet_virt_address + a); | |
72 | +} | |
73 | + | |
74 | +#else | |
75 | + | |
76 | +static inline unsigned long hpet_read_value(unsigned long a) | |
77 | +{ | |
78 | + return readl(hpet_virt_address + a); | |
79 | +} | |
80 | + | |
81 | +static void hpet_write_value(unsigned long d, unsigned long a) | |
82 | +{ | |
83 | + writel(d, hpet_virt_address + a); | |
84 | +} | |
85 | #endif | |
86 | ||
87 | static inline void hpet_set_mapping(void) | |
88 | @@ -78,6 +106,17 @@ static int __init disable_hpet(char *str | |
89 | } | |
90 | __setup("nohpet", disable_hpet); | |
91 | ||
92 | +#ifdef CONFIG_X86_64 | |
93 | +static int hpet64 = 0; | |
94 | +static int __init hpet64_setup(char *str) | |
95 | +{ | |
96 | + hpet64 = 1; | |
97 | + return 1; | |
98 | +} | |
99 | +__setup("hpet64", hpet64_setup); | |
100 | +#endif | |
101 | + | |
102 | + | |
103 | static inline int is_hpet_capable(void) | |
104 | { | |
105 | return (!boot_hpet_disable && hpet_address); | |
106 | @@ -141,6 +180,7 @@ static void hpet_reserve_platform_timers | |
107 | * Common hpet info | |
108 | */ | |
109 | static unsigned long hpet_period; | |
110 | +static int hpet_legacy_use_64_bits; /* configure T0 in 64-bit mode? */ | |
111 | ||
112 | static void hpet_legacy_set_mode(enum clock_event_mode mode, | |
113 | struct clock_event_device *evt); | |
114 | @@ -192,10 +232,38 @@ static void hpet_enable_legacy_int(void) | |
115 | hpet_legacy_int_enabled = 1; | |
116 | } | |
117 | ||
118 | +static int timer0_use_64_bits(void) | |
119 | +{ | |
120 | +#ifndef CONFIG_X86_64 | |
121 | + /* using the HPET in 64-bit mode without atomic 64-bit | |
122 | + * accesses is too inefficient | |
123 | + */ | |
124 | + return 0; | |
125 | +#else | |
126 | + | |
127 | + if (unlikely(hpet64)) { | |
128 | + u32 id, t0_cfg; | |
129 | + id = hpet_readl(HPET_ID); | |
130 | + t0_cfg = hpet_readl(HPET_T0_CFG); | |
131 | + | |
132 | + if ((id & HPET_ID_64BIT) && (t0_cfg & HPET_TN_64BIT_CAP)) { | |
133 | + printk(KERN_DEBUG "hpet timer0 configured in 64-bit mode\n"); | |
134 | + return 1; | |
135 | + } | |
136 | + else { | |
137 | + printk(KERN_DEBUG "hpet timer0 does not support 64-bit mode\n"); | |
138 | + return 0; | |
139 | + } | |
140 | + } | |
141 | + else return 0; | |
142 | +#endif | |
143 | +} | |
144 | + | |
145 | static void hpet_legacy_clockevent_register(void) | |
146 | { | |
147 | /* Start HPET legacy interrupts */ | |
148 | hpet_enable_legacy_int(); | |
149 | + hpet_legacy_use_64_bits = timer0_use_64_bits(); | |
150 | ||
151 | /* | |
152 | * The mult factor is defined as (include/linux/clockchips.h) | |
153 | @@ -233,26 +301,28 @@ static void hpet_legacy_set_mode(enum cl | |
154 | case CLOCK_EVT_MODE_PERIODIC: | |
155 | delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * hpet_clockevent.mult; | |
156 | delta >>= hpet_clockevent.shift; | |
157 | - now = hpet_readl(HPET_COUNTER); | |
158 | + now = hpet_read_value(HPET_COUNTER); | |
159 | cmp = now + (unsigned long) delta; | |
160 | cfg = hpet_readl(HPET_T0_CFG); | |
161 | cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | | |
162 | - HPET_TN_SETVAL | HPET_TN_32BIT; | |
163 | + HPET_TN_SETVAL | | |
164 | + (hpet_legacy_use_64_bits ? 0 : HPET_TN_32BIT); | |
165 | hpet_writel(cfg, HPET_T0_CFG); | |
166 | /* | |
167 | * The first write after writing TN_SETVAL to the | |
168 | * config register sets the counter value, the second | |
169 | * write sets the period. | |
170 | */ | |
171 | - hpet_writel(cmp, HPET_T0_CMP); | |
172 | + hpet_write_value(cmp, HPET_T0_CMP); | |
173 | udelay(1); | |
174 | - hpet_writel((unsigned long) delta, HPET_T0_CMP); | |
175 | + hpet_write_value((unsigned long) delta, HPET_T0_CMP); | |
176 | break; | |
177 | ||
178 | case CLOCK_EVT_MODE_ONESHOT: | |
179 | cfg = hpet_readl(HPET_T0_CFG); | |
180 | cfg &= ~HPET_TN_PERIODIC; | |
181 | - cfg |= HPET_TN_ENABLE | HPET_TN_32BIT; | |
182 | + cfg |= HPET_TN_ENABLE | | |
183 | + (hpet_legacy_use_64_bits ? 0 : HPET_TN_32BIT); | |
184 | hpet_writel(cfg, HPET_T0_CFG); | |
185 | break; | |
186 | ||
187 | @@ -272,11 +342,11 @@ static void hpet_legacy_set_mode(enum cl | |
188 | static int hpet_legacy_next_event(unsigned long delta, | |
189 | struct clock_event_device *evt) | |
190 | { | |
191 | - u32 cnt; | |
192 | + unsigned long cnt; | |
193 | ||
194 | - cnt = hpet_readl(HPET_COUNTER); | |
195 | + cnt = hpet_read_value(HPET_COUNTER); | |
196 | cnt += (u32) delta; | |
197 | - hpet_writel(cnt, HPET_T0_CMP); | |
198 | + hpet_write_value(cnt, HPET_T0_CMP); | |
199 | ||
200 | hpet_readl(HPET_T0_CMP); /* pre-read for bnc#433746 */ | |
201 | /* | |
202 | @@ -284,9 +354,9 @@ static int hpet_legacy_next_event(unsign | |
203 | * what we wrote hit the chip before we compare it to the | |
204 | * counter. | |
205 | */ | |
206 | - WARN_ON_ONCE((u32)hpet_readl(HPET_T0_CMP) != cnt); | |
207 | + WARN_ON_ONCE((u32)hpet_readl(HPET_T0_CMP) != (u32)cnt); | |
208 | ||
209 | - return (s32)((u32)hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0; | |
210 | + return (s32)((u32)hpet_readl(HPET_COUNTER) - (u32)cnt) >= 0 ? -ETIME : 0; | |
211 | } | |
212 | ||
213 | /* | |
214 | --- a/Documentation/kernel-parameters.txt 2009-01-24 16:39:47.000000000 +0100 | |
215 | +++ b/Documentation/kernel-parameters.txt 2009-01-24 16:46:51.000000000 +0100 | |
216 | @@ -475,6 +475,8 @@ and is between 256 and 4096 characters. | |
217 | force: allow force enabled of undocumented chips (ICH4, | |
218 | VIA, nVidia) | |
219 | ||
220 | + hpet64 [X86-64,HPET] enable 64-bit mode of the HPET timer (bnc#456700) | |
221 | + | |
222 | com20020= [HW,NET] ARCnet - COM20020 chipset | |
223 | Format: | |
224 | <io>[,<irq>[,<nodeID>[,<backplane>[,<ckp>[,<timeout>]]]]] |