Blame view
drivers/clocksource/acpi_pm.c
6.51 KB
5d0cf410e [PATCH] Time: i38... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* * linux/drivers/clocksource/acpi_pm.c * * This file contains the ACPI PM based clocksource. * * This code was largely moved from the i386 timer_pm.c file * which was (C) Dominik Brodowski <linux@brodo.de> 2003 * and contained the following comments: * * Driver to use the Power Management Timer (PMTMR) available in some * southbridges as primary timing source for the Linux kernel. * * Based on parts of linux/drivers/acpi/hardware/hwtimer.c, timer_pit.c, * timer_hpet.c, and on Arjan van de Ven's implementation for 2.4. * * This file is licensed under the GPL v2. */ |
d66bea57e [PATCH] Allow ear... |
18 |
#include <linux/acpi_pmtmr.h> |
5d0cf410e [PATCH] Time: i38... |
19 |
#include <linux/clocksource.h> |
08604bd99 time: move PIT_TI... |
20 |
#include <linux/timex.h> |
5d0cf410e [PATCH] Time: i38... |
21 22 23 |
#include <linux/errno.h> #include <linux/init.h> #include <linux/pci.h> |
4ab6a2191 clocksource, acpi... |
24 |
#include <linux/delay.h> |
5d0cf410e [PATCH] Time: i38... |
25 |
#include <asm/io.h> |
5d0cf410e [PATCH] Time: i38... |
26 27 28 |
/* * The I/O port the PMTMR resides at. * The location is detected during setup_arch(), |
8ce8e2f99 ACPI: correct pat... |
29 |
* in arch/i386/kernel/acpi/boot.c |
5d0cf410e [PATCH] Time: i38... |
30 |
*/ |
7d622d479 [PATCH] make pmtm... |
31 |
u32 pmtmr_ioport __read_mostly; |
5d0cf410e [PATCH] Time: i38... |
32 |
|
5d0cf410e [PATCH] Time: i38... |
33 34 35 36 37 |
static inline u32 read_pmtmr(void) { /* mask the output to 24 bits */ return inl(pmtmr_ioport) & ACPI_PM_MASK; } |
d66bea57e [PATCH] Allow ear... |
38 |
u32 acpi_pm_read_verified(void) |
5d0cf410e [PATCH] Time: i38... |
39 40 41 42 43 44 |
{ u32 v1 = 0, v2 = 0, v3 = 0; /* * It has been reported that because of various broken * chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM clock |
7d622d479 [PATCH] make pmtm... |
45 |
* source is not latched, you must read it multiple |
5d0cf410e [PATCH] Time: i38... |
46 47 48 49 50 51 |
* times to ensure a safe value is read: */ do { v1 = read_pmtmr(); v2 = read_pmtmr(); v3 = read_pmtmr(); |
78f32668e [PATCH] clocksour... |
52 53 |
} while (unlikely((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2))); |
5d0cf410e [PATCH] Time: i38... |
54 |
|
d66bea57e [PATCH] Allow ear... |
55 56 |
return v2; } |
8e19608e8 clocksource: pass... |
57 |
static cycle_t acpi_pm_read(struct clocksource *cs) |
5d0cf410e [PATCH] Time: i38... |
58 59 60 61 62 63 64 65 66 |
{ return (cycle_t)read_pmtmr(); } static struct clocksource clocksource_acpi_pm = { .name = "acpi_pm", .rating = 200, .read = acpi_pm_read, .mask = (cycle_t)ACPI_PM_MASK, |
73b08d2aa [PATCH] clocksour... |
67 |
.flags = CLOCK_SOURCE_IS_CONTINUOUS, |
5d0cf410e [PATCH] Time: i38... |
68 69 70 71 |
}; #ifdef CONFIG_PCI |
f5f1a24a2 [PATCH] clocksour... |
72 |
static int __devinitdata acpi_pm_good; |
5d0cf410e [PATCH] Time: i38... |
73 74 |
static int __init acpi_pm_good_setup(char *__str) { |
f5f1a24a2 [PATCH] clocksour... |
75 76 |
acpi_pm_good = 1; return 1; |
5d0cf410e [PATCH] Time: i38... |
77 78 |
} __setup("acpi_pm_good", acpi_pm_good_setup); |
8e19608e8 clocksource: pass... |
79 |
static cycle_t acpi_pm_read_slow(struct clocksource *cs) |
0a57b7830 clocksource, acpi... |
80 81 82 |
{ return (cycle_t)acpi_pm_read_verified(); } |
5d0cf410e [PATCH] Time: i38... |
83 84 |
static inline void acpi_pm_need_workaround(void) { |
d66bea57e [PATCH] Allow ear... |
85 |
clocksource_acpi_pm.read = acpi_pm_read_slow; |
1ff100d76 [PATCH] correct s... |
86 |
clocksource_acpi_pm.rating = 120; |
5d0cf410e [PATCH] Time: i38... |
87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
} /* * PIIX4 Errata: * * The power management timer may return improper results when read. * Although the timer value settles properly after incrementing, * while incrementing there is a 3 ns window every 69.8 ns where the * timer value is indeterminate (a 4.2% chance that the data will be * incorrect when read). As a result, the ACPI free running count up * timer specification is violated due to erroneous reads. */ static void __devinit acpi_pm_check_blacklist(struct pci_dev *dev) { |
5d0cf410e [PATCH] Time: i38... |
101 102 |
if (acpi_pm_good) return; |
5d0cf410e [PATCH] Time: i38... |
103 |
/* the bug has been fixed in PIIX4M */ |
44c10138f PCI: Change all d... |
104 |
if (dev->revision < 3) { |
5d0cf410e [PATCH] Time: i38... |
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
printk(KERN_WARNING "* Found PM-Timer Bug on the chipset." " Due to workarounds for a bug, " "* this clock source is slow. Consider trying" " other clock sources "); acpi_pm_need_workaround(); } } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, acpi_pm_check_blacklist); static void __devinit acpi_pm_check_graylist(struct pci_dev *dev) { if (acpi_pm_good) return; printk(KERN_WARNING "* The chipset may have PM-Timer Bug. Due to" " workarounds for a bug, " "* this clock source is slow. If you are sure your timer" " does not have " "* this bug, please use \"acpi_pm_good\" to disable the" " workaround "); acpi_pm_need_workaround(); } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, acpi_pm_check_graylist); |
78f32668e [PATCH] clocksour... |
137 138 |
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_LE, acpi_pm_check_graylist); |
5d0cf410e [PATCH] Time: i38... |
139 |
#endif |
562f9c574 [PATCH] time: re-... |
140 |
#ifndef CONFIG_X86_64 |
1164dd009 x86: move mach-de... |
141 |
#include <asm/mach_timer.h> |
562f9c574 [PATCH] time: re-... |
142 |
#define PMTMR_EXPECTED_RATE \ |
cbf1599b3 time: x86: Remove... |
143 |
((CALIBRATE_LATCH * (PMTMR_TICKS_PER_SEC >> 10)) / (PIT_TICK_RATE>>10)) |
562f9c574 [PATCH] time: re-... |
144 145 146 147 148 149 |
/* * Some boards have the PMTMR running way too fast. We check * the PMTMR rate against PIT channel 2 to catch these cases. */ static int verify_pmtmr_rate(void) { |
dfdf748a6 clocksource, acpi... |
150 |
cycle_t value1, value2; |
562f9c574 [PATCH] time: re-... |
151 152 153 |
unsigned long count, delta; mach_prepare_counter(); |
8e19608e8 clocksource: pass... |
154 |
value1 = clocksource_acpi_pm.read(&clocksource_acpi_pm); |
562f9c574 [PATCH] time: re-... |
155 |
mach_countup(&count); |
8e19608e8 clocksource: pass... |
156 |
value2 = clocksource_acpi_pm.read(&clocksource_acpi_pm); |
562f9c574 [PATCH] time: re-... |
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
delta = (value2 - value1) & ACPI_PM_MASK; /* Check that the PMTMR delta is within 5% of what we expect */ if (delta < (PMTMR_EXPECTED_RATE * 19) / 20 || delta > (PMTMR_EXPECTED_RATE * 21) / 20) { printk(KERN_INFO "PM-Timer running at invalid rate: %lu%% " "of normal - aborting. ", 100UL * delta / PMTMR_EXPECTED_RATE); return -1; } return 0; } #else #define verify_pmtmr_rate() (0) #endif |
5d0cf410e [PATCH] Time: i38... |
174 |
|
4ab6a2191 clocksource, acpi... |
175 176 |
/* Number of monotonicity checks to perform during initialization */ #define ACPI_PM_MONOTONICITY_CHECKS 10 |
f1926ce63 clocksource, acpi... |
177 178 |
/* Number of reads we try to get two different values */ #define ACPI_PM_READ_CHECKS 10000 |
4ab6a2191 clocksource, acpi... |
179 |
|
5d0cf410e [PATCH] Time: i38... |
180 181 |
static int __init init_acpi_pm_clocksource(void) { |
dfdf748a6 clocksource, acpi... |
182 |
cycle_t value1, value2; |
f1926ce63 clocksource, acpi... |
183 |
unsigned int i, j = 0; |
5d0cf410e [PATCH] Time: i38... |
184 185 186 |
if (!pmtmr_ioport) return -ENODEV; |
5d0cf410e [PATCH] Time: i38... |
187 |
/* "verify" this timing source: */ |
4ab6a2191 clocksource, acpi... |
188 |
for (j = 0; j < ACPI_PM_MONOTONICITY_CHECKS; j++) { |
f1926ce63 clocksource, acpi... |
189 |
udelay(100 * j); |
8e19608e8 clocksource: pass... |
190 |
value1 = clocksource_acpi_pm.read(&clocksource_acpi_pm); |
f1926ce63 clocksource, acpi... |
191 |
for (i = 0; i < ACPI_PM_READ_CHECKS; i++) { |
8e19608e8 clocksource: pass... |
192 |
value2 = clocksource_acpi_pm.read(&clocksource_acpi_pm); |
4ab6a2191 clocksource, acpi... |
193 194 195 |
if (value2 == value1) continue; if (value2 > value1) |
4ab6a2191 clocksource, acpi... |
196 197 |
break; if ((value2 < value1) && ((value2) < 0xFFF)) |
4ab6a2191 clocksource, acpi... |
198 199 200 201 202 |
break; printk(KERN_INFO "PM-Timer had inconsistent results:" " 0x%#llx, 0x%#llx - aborting. ", value1, value2); |
db6b175fa acpi_pm: Clear pm... |
203 |
pmtmr_ioport = 0; |
4ab6a2191 clocksource, acpi... |
204 205 |
return -EINVAL; } |
f1926ce63 clocksource, acpi... |
206 207 208 209 |
if (i == ACPI_PM_READ_CHECKS) { printk(KERN_INFO "PM-Timer failed consistency check " " (0x%#llx) - aborting. ", value1); |
db6b175fa acpi_pm: Clear pm... |
210 |
pmtmr_ioport = 0; |
f1926ce63 clocksource, acpi... |
211 212 |
return -ENODEV; } |
5d0cf410e [PATCH] Time: i38... |
213 |
} |
5d0cf410e [PATCH] Time: i38... |
214 |
|
db6b175fa acpi_pm: Clear pm... |
215 216 |
if (verify_pmtmr_rate() != 0){ pmtmr_ioport = 0; |
562f9c574 [PATCH] time: re-... |
217 |
return -ENODEV; |
db6b175fa acpi_pm: Clear pm... |
218 |
} |
562f9c574 [PATCH] time: re-... |
219 |
|
f12a15be6 x86: Convert comm... |
220 221 |
return clocksource_register_hz(&clocksource_acpi_pm, PMTMR_TICKS_PER_SEC); |
5d0cf410e [PATCH] Time: i38... |
222 |
} |
6bb74df48 [PATCH] clocksour... |
223 224 225 226 |
/* We use fs_initcall because we want the PCI fixups to have run * but we still need to load before device_initcall */ fs_initcall(init_acpi_pm_clocksource); |
6b148507d pmtmr: allow comm... |
227 228 229 230 231 232 233 234 235 236 237 |
/* * Allow an override of the IOPort. Stupid BIOSes do not tell us about * the PMTimer, but we might know where it is. */ static int __init parse_pmtmr(char *arg) { unsigned long base; if (strict_strtoul(arg, 16, &base)) return -EINVAL; |
ee974e01e clocksource: chec... |
238 239 240 241 |
#ifdef CONFIG_X86_64 if (base > UINT_MAX) return -ERANGE; #endif |
14351760e Fix printk format... |
242 243 |
printk(KERN_INFO "PMTMR IOPort override: 0x%04x -> 0x%04lx ", |
ee974e01e clocksource: chec... |
244 |
pmtmr_ioport, base); |
6b148507d pmtmr: allow comm... |
245 246 247 248 249 |
pmtmr_ioport = base; return 1; } __setup("pmtmr=", parse_pmtmr); |