Blame view

drivers/clocksource/acpi_pm.c 6.51 KB
5d0cf410e   John Stultz   [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   Thomas Gleixner   [PATCH] Allow ear...
18
  #include <linux/acpi_pmtmr.h>
5d0cf410e   John Stultz   [PATCH] Time: i38...
19
  #include <linux/clocksource.h>
08604bd99   Arnd Bergmann   time: move PIT_TI...
20
  #include <linux/timex.h>
5d0cf410e   John Stultz   [PATCH] Time: i38...
21
22
23
  #include <linux/errno.h>
  #include <linux/init.h>
  #include <linux/pci.h>
4ab6a2191   Dominik Brodowski   clocksource, acpi...
24
  #include <linux/delay.h>
5d0cf410e   John Stultz   [PATCH] Time: i38...
25
  #include <asm/io.h>
5d0cf410e   John Stultz   [PATCH] Time: i38...
26
27
28
  /*
   * The I/O port the PMTMR resides at.
   * The location is detected during setup_arch(),
8ce8e2f99   Daniel Walker   ACPI: correct pat...
29
   * in arch/i386/kernel/acpi/boot.c
5d0cf410e   John Stultz   [PATCH] Time: i38...
30
   */
7d622d479   Andreas Mohr   [PATCH] make pmtm...
31
  u32 pmtmr_ioport __read_mostly;
5d0cf410e   John Stultz   [PATCH] Time: i38...
32

5d0cf410e   John Stultz   [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   Thomas Gleixner   [PATCH] Allow ear...
38
  u32 acpi_pm_read_verified(void)
5d0cf410e   John Stultz   [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   Andreas Mohr   [PATCH] make pmtm...
45
  	 * source is not latched, you must read it multiple
5d0cf410e   John Stultz   [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   Daniel Walker   [PATCH] clocksour...
52
53
  	} while (unlikely((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1)
  			  || (v3 > v1 && v3 < v2)));
5d0cf410e   John Stultz   [PATCH] Time: i38...
54

d66bea57e   Thomas Gleixner   [PATCH] Allow ear...
55
56
  	return v2;
  }
8e19608e8   Magnus Damm   clocksource: pass...
57
  static cycle_t acpi_pm_read(struct clocksource *cs)
5d0cf410e   John Stultz   [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   Thomas Gleixner   [PATCH] clocksour...
67
  	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
5d0cf410e   John Stultz   [PATCH] Time: i38...
68
69
70
71
  };
  
  
  #ifdef CONFIG_PCI
f5f1a24a2   Daniel Walker   [PATCH] clocksour...
72
  static int __devinitdata acpi_pm_good;
5d0cf410e   John Stultz   [PATCH] Time: i38...
73
74
  static int __init acpi_pm_good_setup(char *__str)
  {
f5f1a24a2   Daniel Walker   [PATCH] clocksour...
75
76
  	acpi_pm_good = 1;
  	return 1;
5d0cf410e   John Stultz   [PATCH] Time: i38...
77
78
  }
  __setup("acpi_pm_good", acpi_pm_good_setup);
8e19608e8   Magnus Damm   clocksource: pass...
79
  static cycle_t acpi_pm_read_slow(struct clocksource *cs)
0a57b7830   Bjorn Helgaas   clocksource, acpi...
80
81
82
  {
  	return (cycle_t)acpi_pm_read_verified();
  }
5d0cf410e   John Stultz   [PATCH] Time: i38...
83
84
  static inline void acpi_pm_need_workaround(void)
  {
d66bea57e   Thomas Gleixner   [PATCH] Allow ear...
85
  	clocksource_acpi_pm.read = acpi_pm_read_slow;
1ff100d76   John Stultz   [PATCH] correct s...
86
  	clocksource_acpi_pm.rating = 120;
5d0cf410e   John Stultz   [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   John Stultz   [PATCH] Time: i38...
101
102
  	if (acpi_pm_good)
  		return;
5d0cf410e   John Stultz   [PATCH] Time: i38...
103
  	/* the bug has been fixed in PIIX4M */
44c10138f   Auke Kok   PCI: Change all d...
104
  	if (dev->revision < 3) {
5d0cf410e   John Stultz   [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   Daniel Walker   [PATCH] clocksour...
137
138
  DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_LE,
  			acpi_pm_check_graylist);
5d0cf410e   John Stultz   [PATCH] Time: i38...
139
  #endif
562f9c574   John Stultz   [PATCH] time: re-...
140
  #ifndef CONFIG_X86_64
1164dd009   Ingo Molnar   x86: move mach-de...
141
  #include <asm/mach_timer.h>
562f9c574   John Stultz   [PATCH] time: re-...
142
  #define PMTMR_EXPECTED_RATE \
cbf1599b3   Deepak Saxena   time: x86: Remove...
143
    ((CALIBRATE_LATCH * (PMTMR_TICKS_PER_SEC >> 10)) / (PIT_TICK_RATE>>10))
562f9c574   John Stultz   [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   Dominik Brodowski   clocksource, acpi...
150
  	cycle_t value1, value2;
562f9c574   John Stultz   [PATCH] time: re-...
151
152
153
  	unsigned long count, delta;
  
  	mach_prepare_counter();
8e19608e8   Magnus Damm   clocksource: pass...
154
  	value1 = clocksource_acpi_pm.read(&clocksource_acpi_pm);
562f9c574   John Stultz   [PATCH] time: re-...
155
  	mach_countup(&count);
8e19608e8   Magnus Damm   clocksource: pass...
156
  	value2 = clocksource_acpi_pm.read(&clocksource_acpi_pm);
562f9c574   John Stultz   [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   John Stultz   [PATCH] Time: i38...
174

4ab6a2191   Dominik Brodowski   clocksource, acpi...
175
176
  /* Number of monotonicity checks to perform during initialization */
  #define ACPI_PM_MONOTONICITY_CHECKS 10
f1926ce63   Dominik Brodowski   clocksource, acpi...
177
178
  /* Number of reads we try to get two different values */
  #define ACPI_PM_READ_CHECKS 10000
4ab6a2191   Dominik Brodowski   clocksource, acpi...
179

5d0cf410e   John Stultz   [PATCH] Time: i38...
180
181
  static int __init init_acpi_pm_clocksource(void)
  {
dfdf748a6   Dominik Brodowski   clocksource, acpi...
182
  	cycle_t value1, value2;
f1926ce63   Dominik Brodowski   clocksource, acpi...
183
  	unsigned int i, j = 0;
5d0cf410e   John Stultz   [PATCH] Time: i38...
184
185
186
  
  	if (!pmtmr_ioport)
  		return -ENODEV;
5d0cf410e   John Stultz   [PATCH] Time: i38...
187
  	/* "verify" this timing source: */
4ab6a2191   Dominik Brodowski   clocksource, acpi...
188
  	for (j = 0; j < ACPI_PM_MONOTONICITY_CHECKS; j++) {
f1926ce63   Dominik Brodowski   clocksource, acpi...
189
  		udelay(100 * j);
8e19608e8   Magnus Damm   clocksource: pass...
190
  		value1 = clocksource_acpi_pm.read(&clocksource_acpi_pm);
f1926ce63   Dominik Brodowski   clocksource, acpi...
191
  		for (i = 0; i < ACPI_PM_READ_CHECKS; i++) {
8e19608e8   Magnus Damm   clocksource: pass...
192
  			value2 = clocksource_acpi_pm.read(&clocksource_acpi_pm);
4ab6a2191   Dominik Brodowski   clocksource, acpi...
193
194
195
  			if (value2 == value1)
  				continue;
  			if (value2 > value1)
4ab6a2191   Dominik Brodowski   clocksource, acpi...
196
197
  				break;
  			if ((value2 < value1) && ((value2) < 0xFFF))
4ab6a2191   Dominik Brodowski   clocksource, acpi...
198
199
200
201
202
  				break;
  			printk(KERN_INFO "PM-Timer had inconsistent results:"
  			       " 0x%#llx, 0x%#llx - aborting.
  ",
  			       value1, value2);
db6b175fa   Konrad Rzeszutek Wilk   acpi_pm: Clear pm...
203
  			pmtmr_ioport = 0;
4ab6a2191   Dominik Brodowski   clocksource, acpi...
204
205
  			return -EINVAL;
  		}
f1926ce63   Dominik Brodowski   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   Konrad Rzeszutek Wilk   acpi_pm: Clear pm...
210
  			pmtmr_ioport = 0;
f1926ce63   Dominik Brodowski   clocksource, acpi...
211
212
  			return -ENODEV;
  		}
5d0cf410e   John Stultz   [PATCH] Time: i38...
213
  	}
5d0cf410e   John Stultz   [PATCH] Time: i38...
214

db6b175fa   Konrad Rzeszutek Wilk   acpi_pm: Clear pm...
215
216
  	if (verify_pmtmr_rate() != 0){
  		pmtmr_ioport = 0;
562f9c574   John Stultz   [PATCH] time: re-...
217
  		return -ENODEV;
db6b175fa   Konrad Rzeszutek Wilk   acpi_pm: Clear pm...
218
  	}
562f9c574   John Stultz   [PATCH] time: re-...
219

f12a15be6   John Stultz   x86: Convert comm...
220
221
  	return clocksource_register_hz(&clocksource_acpi_pm,
  						PMTMR_TICKS_PER_SEC);
5d0cf410e   John Stultz   [PATCH] Time: i38...
222
  }
6bb74df48   John Stultz   [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   Thomas Gleixner   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   David Howells   clocksource: chec...
238
239
240
241
  #ifdef CONFIG_X86_64
  	if (base > UINT_MAX)
  		return -ERANGE;
  #endif
14351760e   Linus Torvalds   Fix printk format...
242
243
  	printk(KERN_INFO "PMTMR IOPort override: 0x%04x -> 0x%04lx
  ",
ee974e01e   David Howells   clocksource: chec...
244
  	       pmtmr_ioport, base);
6b148507d   Thomas Gleixner   pmtmr: allow comm...
245
246
247
248
249
  	pmtmr_ioport = base;
  
  	return 1;
  }
  __setup("pmtmr=", parse_pmtmr);