diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2008-10-09 16:31:56 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2008-10-09 16:31:56 -0400 |
commit | 6a4690c22f5da1eb1c898b61b6a80da52fbd976f (patch) | |
tree | a03891a32abe0da191fb765fe669a597e07423c6 /arch/x86/kernel | |
parent | 90bb28b0644f7324f8bd1feb27b35146e6785ba2 (diff) | |
parent | 8ec53663d2698076468b3e1edc4e1b418bd54de3 (diff) |
Merge branch 'ptebits' into devel
Conflicts:
arch/arm/Kconfig
Diffstat (limited to 'arch/x86/kernel')
-rw-r--r-- | arch/x86/kernel/cpuid.c | 15 | ||||
-rw-r--r-- | arch/x86/kernel/msr.c | 38 | ||||
-rw-r--r-- | arch/x86/kernel/tsc.c | 235 |
3 files changed, 218 insertions, 70 deletions
diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c index 14b11b3be31c..8e9cd6a8ec12 100644 --- a/arch/x86/kernel/cpuid.c +++ b/arch/x86/kernel/cpuid.c | |||
@@ -89,6 +89,8 @@ static ssize_t cpuid_read(struct file *file, char __user *buf, | |||
89 | struct cpuid_regs cmd; | 89 | struct cpuid_regs cmd; |
90 | int cpu = iminor(file->f_path.dentry->d_inode); | 90 | int cpu = iminor(file->f_path.dentry->d_inode); |
91 | u64 pos = *ppos; | 91 | u64 pos = *ppos; |
92 | ssize_t bytes = 0; | ||
93 | int err = 0; | ||
92 | 94 | ||
93 | if (count % 16) | 95 | if (count % 16) |
94 | return -EINVAL; /* Invalid chunk size */ | 96 | return -EINVAL; /* Invalid chunk size */ |
@@ -96,14 +98,19 @@ static ssize_t cpuid_read(struct file *file, char __user *buf, | |||
96 | for (; count; count -= 16) { | 98 | for (; count; count -= 16) { |
97 | cmd.eax = pos; | 99 | cmd.eax = pos; |
98 | cmd.ecx = pos >> 32; | 100 | cmd.ecx = pos >> 32; |
99 | smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1); | 101 | err = smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1); |
100 | if (copy_to_user(tmp, &cmd, 16)) | 102 | if (err) |
101 | return -EFAULT; | 103 | break; |
104 | if (copy_to_user(tmp, &cmd, 16)) { | ||
105 | err = -EFAULT; | ||
106 | break; | ||
107 | } | ||
102 | tmp += 16; | 108 | tmp += 16; |
109 | bytes += 16; | ||
103 | *ppos = ++pos; | 110 | *ppos = ++pos; |
104 | } | 111 | } |
105 | 112 | ||
106 | return tmp - buf; | 113 | return bytes ? bytes : err; |
107 | } | 114 | } |
108 | 115 | ||
109 | static int cpuid_open(struct inode *inode, struct file *file) | 116 | static int cpuid_open(struct inode *inode, struct file *file) |
diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c index e43938086885..2e2af5d18191 100644 --- a/arch/x86/kernel/msr.c +++ b/arch/x86/kernel/msr.c | |||
@@ -72,21 +72,28 @@ static ssize_t msr_read(struct file *file, char __user *buf, | |||
72 | u32 data[2]; | 72 | u32 data[2]; |
73 | u32 reg = *ppos; | 73 | u32 reg = *ppos; |
74 | int cpu = iminor(file->f_path.dentry->d_inode); | 74 | int cpu = iminor(file->f_path.dentry->d_inode); |
75 | int err; | 75 | int err = 0; |
76 | ssize_t bytes = 0; | ||
76 | 77 | ||
77 | if (count % 8) | 78 | if (count % 8) |
78 | return -EINVAL; /* Invalid chunk size */ | 79 | return -EINVAL; /* Invalid chunk size */ |
79 | 80 | ||
80 | for (; count; count -= 8) { | 81 | for (; count; count -= 8) { |
81 | err = rdmsr_safe_on_cpu(cpu, reg, &data[0], &data[1]); | 82 | err = rdmsr_safe_on_cpu(cpu, reg, &data[0], &data[1]); |
82 | if (err) | 83 | if (err) { |
83 | return -EIO; | 84 | if (err == -EFAULT) /* Fix idiotic error code */ |
84 | if (copy_to_user(tmp, &data, 8)) | 85 | err = -EIO; |
85 | return -EFAULT; | 86 | break; |
87 | } | ||
88 | if (copy_to_user(tmp, &data, 8)) { | ||
89 | err = -EFAULT; | ||
90 | break; | ||
91 | } | ||
86 | tmp += 2; | 92 | tmp += 2; |
93 | bytes += 8; | ||
87 | } | 94 | } |
88 | 95 | ||
89 | return ((char __user *)tmp) - buf; | 96 | return bytes ? bytes : err; |
90 | } | 97 | } |
91 | 98 | ||
92 | static ssize_t msr_write(struct file *file, const char __user *buf, | 99 | static ssize_t msr_write(struct file *file, const char __user *buf, |
@@ -96,21 +103,28 @@ static ssize_t msr_write(struct file *file, const char __user *buf, | |||
96 | u32 data[2]; | 103 | u32 data[2]; |
97 | u32 reg = *ppos; | 104 | u32 reg = *ppos; |
98 | int cpu = iminor(file->f_path.dentry->d_inode); | 105 | int cpu = iminor(file->f_path.dentry->d_inode); |
99 | int err; | 106 | int err = 0; |
107 | ssize_t bytes = 0; | ||
100 | 108 | ||
101 | if (count % 8) | 109 | if (count % 8) |
102 | return -EINVAL; /* Invalid chunk size */ | 110 | return -EINVAL; /* Invalid chunk size */ |
103 | 111 | ||
104 | for (; count; count -= 8) { | 112 | for (; count; count -= 8) { |
105 | if (copy_from_user(&data, tmp, 8)) | 113 | if (copy_from_user(&data, tmp, 8)) { |
106 | return -EFAULT; | 114 | err = -EFAULT; |
115 | break; | ||
116 | } | ||
107 | err = wrmsr_safe_on_cpu(cpu, reg, data[0], data[1]); | 117 | err = wrmsr_safe_on_cpu(cpu, reg, data[0], data[1]); |
108 | if (err) | 118 | if (err) { |
109 | return -EIO; | 119 | if (err == -EFAULT) /* Fix idiotic error code */ |
120 | err = -EIO; | ||
121 | break; | ||
122 | } | ||
110 | tmp += 2; | 123 | tmp += 2; |
124 | bytes += 8; | ||
111 | } | 125 | } |
112 | 126 | ||
113 | return ((char __user *)tmp) - buf; | 127 | return bytes ? bytes : err; |
114 | } | 128 | } |
115 | 129 | ||
116 | static int msr_open(struct inode *inode, struct file *file) | 130 | static int msr_open(struct inode *inode, struct file *file) |
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index 8e786b0d665a..ac79bd143da8 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c | |||
@@ -127,75 +127,202 @@ static u64 tsc_read_refs(u64 *pm, u64 *hpet) | |||
127 | */ | 127 | */ |
128 | unsigned long native_calibrate_tsc(void) | 128 | unsigned long native_calibrate_tsc(void) |
129 | { | 129 | { |
130 | unsigned long flags; | 130 | u64 tsc1, tsc2, tr1, tr2, tsc, delta, pm1, pm2, hpet1, hpet2; |
131 | u64 tsc1, tsc2, tr1, tr2, delta, pm1, pm2, hpet1, hpet2; | 131 | unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX; |
132 | int hpet = is_hpet_enabled(); | 132 | unsigned long flags, tscmin, tscmax; |
133 | unsigned int tsc_khz_val = 0; | 133 | int hpet = is_hpet_enabled(), pitcnt, i; |
134 | 134 | ||
135 | local_irq_save(flags); | 135 | /* |
136 | 136 | * Run 5 calibration loops to get the lowest frequency value | |
137 | tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL); | 137 | * (the best estimate). We use two different calibration modes |
138 | 138 | * here: | |
139 | outb((inb(0x61) & ~0x02) | 0x01, 0x61); | 139 | * |
140 | 140 | * 1) PIT loop. We set the PIT Channel 2 to oneshot mode and | |
141 | outb(0xb0, 0x43); | 141 | * load a timeout of 50ms. We read the time right after we |
142 | outb((CLOCK_TICK_RATE / (1000 / 50)) & 0xff, 0x42); | 142 | * started the timer and wait until the PIT count down reaches |
143 | outb((CLOCK_TICK_RATE / (1000 / 50)) >> 8, 0x42); | 143 | * zero. In each wait loop iteration we read the TSC and check |
144 | tr1 = get_cycles(); | 144 | * the delta to the previous read. We keep track of the min |
145 | while ((inb(0x61) & 0x20) == 0); | 145 | * and max values of that delta. The delta is mostly defined |
146 | tr2 = get_cycles(); | 146 | * by the IO time of the PIT access, so we can detect when a |
147 | 147 | * SMI/SMM disturbance happend between the two reads. If the | |
148 | tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL); | 148 | * maximum time is significantly larger than the minimum time, |
149 | 149 | * then we discard the result and have another try. | |
150 | local_irq_restore(flags); | 150 | * |
151 | * 2) Reference counter. If available we use the HPET or the | ||
152 | * PMTIMER as a reference to check the sanity of that value. | ||
153 | * We use separate TSC readouts and check inside of the | ||
154 | * reference read for a SMI/SMM disturbance. We dicard | ||
155 | * disturbed values here as well. We do that around the PIT | ||
156 | * calibration delay loop as we have to wait for a certain | ||
157 | * amount of time anyway. | ||
158 | */ | ||
159 | for (i = 0; i < 5; i++) { | ||
160 | |||
161 | tscmin = ULONG_MAX; | ||
162 | tscmax = 0; | ||
163 | pitcnt = 0; | ||
164 | |||
165 | local_irq_save(flags); | ||
166 | |||
167 | /* | ||
168 | * Read the start value and the reference count of | ||
169 | * hpet/pmtimer when available: | ||
170 | */ | ||
171 | tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL); | ||
172 | |||
173 | /* Set the Gate high, disable speaker */ | ||
174 | outb((inb(0x61) & ~0x02) | 0x01, 0x61); | ||
175 | |||
176 | /* | ||
177 | * Setup CTC channel 2* for mode 0, (interrupt on terminal | ||
178 | * count mode), binary count. Set the latch register to 50ms | ||
179 | * (LSB then MSB) to begin countdown. | ||
180 | * | ||
181 | * Some devices need a delay here. | ||
182 | */ | ||
183 | outb(0xb0, 0x43); | ||
184 | outb((CLOCK_TICK_RATE / (1000 / 50)) & 0xff, 0x42); | ||
185 | outb((CLOCK_TICK_RATE / (1000 / 50)) >> 8, 0x42); | ||
186 | |||
187 | tsc = tr1 = tr2 = get_cycles(); | ||
188 | |||
189 | while ((inb(0x61) & 0x20) == 0) { | ||
190 | tr2 = get_cycles(); | ||
191 | delta = tr2 - tsc; | ||
192 | tsc = tr2; | ||
193 | if ((unsigned int) delta < tscmin) | ||
194 | tscmin = (unsigned int) delta; | ||
195 | if ((unsigned int) delta > tscmax) | ||
196 | tscmax = (unsigned int) delta; | ||
197 | pitcnt++; | ||
198 | } | ||
199 | |||
200 | /* | ||
201 | * We waited at least 50ms above. Now read | ||
202 | * pmtimer/hpet reference again | ||
203 | */ | ||
204 | tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL); | ||
205 | |||
206 | local_irq_restore(flags); | ||
207 | |||
208 | /* | ||
209 | * Sanity checks: | ||
210 | * | ||
211 | * If we were not able to read the PIT more than 5000 | ||
212 | * times, then we have been hit by a massive SMI | ||
213 | * | ||
214 | * If the maximum is 10 times larger than the minimum, | ||
215 | * then we got hit by an SMI as well. | ||
216 | */ | ||
217 | if (pitcnt > 5000 && tscmax < 10 * tscmin) { | ||
218 | |||
219 | /* Calculate the PIT value */ | ||
220 | delta = tr2 - tr1; | ||
221 | do_div(delta, 50); | ||
222 | |||
223 | /* We take the smallest value into account */ | ||
224 | tsc_pit_min = min(tsc_pit_min, (unsigned long) delta); | ||
225 | } | ||
226 | |||
227 | /* hpet or pmtimer available ? */ | ||
228 | if (!hpet && !pm1 && !pm2) | ||
229 | continue; | ||
230 | |||
231 | /* Check, whether the sampling was disturbed by an SMI */ | ||
232 | if (tsc1 == ULLONG_MAX || tsc2 == ULLONG_MAX) | ||
233 | continue; | ||
234 | |||
235 | tsc2 = (tsc2 - tsc1) * 1000000LL; | ||
236 | |||
237 | if (hpet) { | ||
238 | if (hpet2 < hpet1) | ||
239 | hpet2 += 0x100000000ULL; | ||
240 | hpet2 -= hpet1; | ||
241 | tsc1 = ((u64)hpet2 * hpet_readl(HPET_PERIOD)); | ||
242 | do_div(tsc1, 1000000); | ||
243 | } else { | ||
244 | if (pm2 < pm1) | ||
245 | pm2 += (u64)ACPI_PM_OVRRUN; | ||
246 | pm2 -= pm1; | ||
247 | tsc1 = pm2 * 1000000000LL; | ||
248 | do_div(tsc1, PMTMR_TICKS_PER_SEC); | ||
249 | } | ||
250 | |||
251 | do_div(tsc2, tsc1); | ||
252 | tsc_ref_min = min(tsc_ref_min, (unsigned long) tsc2); | ||
253 | } | ||
151 | 254 | ||
152 | /* | 255 | /* |
153 | * Preset the result with the raw and inaccurate PIT | 256 | * Now check the results. |
154 | * calibration value | ||
155 | */ | 257 | */ |
156 | delta = (tr2 - tr1); | 258 | if (tsc_pit_min == ULONG_MAX) { |
157 | do_div(delta, 50); | 259 | /* PIT gave no useful value */ |
158 | tsc_khz_val = delta; | 260 | printk(KERN_WARNING "TSC: PIT calibration failed due to " |
261 | "SMI disturbance.\n"); | ||
262 | |||
263 | /* We don't have an alternative source, disable TSC */ | ||
264 | if (!hpet && !pm1 && !pm2) { | ||
265 | printk("TSC: No reference (HPET/PMTIMER) available\n"); | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | /* The alternative source failed as well, disable TSC */ | ||
270 | if (tsc_ref_min == ULONG_MAX) { | ||
271 | printk(KERN_WARNING "TSC: HPET/PMTIMER calibration " | ||
272 | "failed due to SMI disturbance.\n"); | ||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | /* Use the alternative source */ | ||
277 | printk(KERN_INFO "TSC: using %s reference calibration\n", | ||
278 | hpet ? "HPET" : "PMTIMER"); | ||
279 | |||
280 | return tsc_ref_min; | ||
281 | } | ||
159 | 282 | ||
160 | /* hpet or pmtimer available ? */ | 283 | /* We don't have an alternative source, use the PIT calibration value */ |
161 | if (!hpet && !pm1 && !pm2) { | 284 | if (!hpet && !pm1 && !pm2) { |
162 | printk(KERN_INFO "TSC calibrated against PIT\n"); | 285 | printk(KERN_INFO "TSC: Using PIT calibration value\n"); |
163 | goto out; | 286 | return tsc_pit_min; |
164 | } | 287 | } |
165 | 288 | ||
166 | /* Check, whether the sampling was disturbed by an SMI */ | 289 | /* The alternative source failed, use the PIT calibration value */ |
167 | if (tsc1 == ULLONG_MAX || tsc2 == ULLONG_MAX) { | 290 | if (tsc_ref_min == ULONG_MAX) { |
168 | printk(KERN_WARNING "TSC calibration disturbed by SMI, " | 291 | printk(KERN_WARNING "TSC: HPET/PMTIMER calibration failed due " |
169 | "using PIT calibration result\n"); | 292 | "to SMI disturbance. Using PIT calibration\n"); |
170 | goto out; | 293 | return tsc_pit_min; |
171 | } | 294 | } |
172 | 295 | ||
173 | tsc2 = (tsc2 - tsc1) * 1000000LL; | 296 | /* Check the reference deviation */ |
174 | 297 | delta = ((u64) tsc_pit_min) * 100; | |
175 | if (hpet) { | 298 | do_div(delta, tsc_ref_min); |
176 | printk(KERN_INFO "TSC calibrated against HPET\n"); | 299 | |
177 | if (hpet2 < hpet1) | 300 | /* |
178 | hpet2 += 0x100000000ULL; | 301 | * If both calibration results are inside a 5% window, the we |
179 | hpet2 -= hpet1; | 302 | * use the lower frequency of those as it is probably the |
180 | tsc1 = ((u64)hpet2 * hpet_readl(HPET_PERIOD)); | 303 | * closest estimate. |
181 | do_div(tsc1, 1000000); | 304 | */ |
182 | } else { | 305 | if (delta >= 95 && delta <= 105) { |
183 | printk(KERN_INFO "TSC calibrated against PM_TIMER\n"); | 306 | printk(KERN_INFO "TSC: PIT calibration confirmed by %s.\n", |
184 | if (pm2 < pm1) | 307 | hpet ? "HPET" : "PMTIMER"); |
185 | pm2 += (u64)ACPI_PM_OVRRUN; | 308 | printk(KERN_INFO "TSC: using %s calibration value\n", |
186 | pm2 -= pm1; | 309 | tsc_pit_min <= tsc_ref_min ? "PIT" : |
187 | tsc1 = pm2 * 1000000000LL; | 310 | hpet ? "HPET" : "PMTIMER"); |
188 | do_div(tsc1, PMTMR_TICKS_PER_SEC); | 311 | return tsc_pit_min <= tsc_ref_min ? tsc_pit_min : tsc_ref_min; |
189 | } | 312 | } |
190 | 313 | ||
191 | do_div(tsc2, tsc1); | 314 | printk(KERN_WARNING "TSC: PIT calibration deviates from %s: %lu %lu.\n", |
192 | tsc_khz_val = tsc2; | 315 | hpet ? "HPET" : "PMTIMER", tsc_pit_min, tsc_ref_min); |
193 | 316 | ||
194 | out: | 317 | /* |
195 | return tsc_khz_val; | 318 | * The calibration values differ too much. In doubt, we use |
319 | * the PIT value as we know that there are PMTIMERs around | ||
320 | * running at double speed. | ||
321 | */ | ||
322 | printk(KERN_INFO "TSC: Using PIT calibration value\n"); | ||
323 | return tsc_pit_min; | ||
196 | } | 324 | } |
197 | 325 | ||
198 | |||
199 | #ifdef CONFIG_X86_32 | 326 | #ifdef CONFIG_X86_32 |
200 | /* Only called from the Powernow K7 cpu freq driver */ | 327 | /* Only called from the Powernow K7 cpu freq driver */ |
201 | int recalibrate_cpu_khz(void) | 328 | int recalibrate_cpu_khz(void) |