PageRenderTime 39ms CodeModel.GetById 10ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 1ms

/arch/i386/kernel/timers/timer_pm.c

https://bitbucket.org/evzijst/gittest
C | 258 lines | 150 code | 52 blank | 56 comment | 24 complexity | d650bf5adc6ad85cb4f60786437dae28 MD5 | raw file
  1/*
  2 * (C) Dominik Brodowski <linux@brodo.de> 2003
  3 *
  4 * Driver to use the Power Management Timer (PMTMR) available in some
  5 * southbridges as primary timing source for the Linux kernel.
  6 *
  7 * Based on parts of linux/drivers/acpi/hardware/hwtimer.c, timer_pit.c,
  8 * timer_hpet.c, and on Arjan van de Ven's implementation for 2.4.
  9 *
 10 * This file is licensed under the GPL v2.
 11 */
 12
 13
 14#include <linux/kernel.h>
 15#include <linux/module.h>
 16#include <linux/device.h>
 17#include <linux/init.h>
 18#include <asm/types.h>
 19#include <asm/timer.h>
 20#include <asm/smp.h>
 21#include <asm/io.h>
 22#include <asm/arch_hooks.h>
 23
 24#include <linux/timex.h>
 25#include "mach_timer.h"
 26
 27/* Number of PMTMR ticks expected during calibration run */
 28#define PMTMR_TICKS_PER_SEC 3579545
 29#define PMTMR_EXPECTED_RATE \
 30  ((CALIBRATE_LATCH * (PMTMR_TICKS_PER_SEC >> 10)) / (CLOCK_TICK_RATE>>10))
 31
 32
 33/* The I/O port the PMTMR resides at.
 34 * The location is detected during setup_arch(),
 35 * in arch/i386/acpi/boot.c */
 36u32 pmtmr_ioport = 0;
 37
 38
 39/* value of the Power timer at last timer interrupt */
 40static u32 offset_tick;
 41static u32 offset_delay;
 42
 43static unsigned long long monotonic_base;
 44static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED;
 45
 46#define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */
 47
 48/*helper function to safely read acpi pm timesource*/
 49static inline u32 read_pmtmr(void)
 50{
 51	u32 v1=0,v2=0,v3=0;
 52	/* It has been reported that because of various broken
 53	 * chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM time
 54	 * source is not latched, so you must read it multiple
 55	 * times to insure a safe value is read.
 56	 */
 57	do {
 58		v1 = inl(pmtmr_ioport);
 59		v2 = inl(pmtmr_ioport);
 60		v3 = inl(pmtmr_ioport);
 61	} while ((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1)
 62			|| (v3 > v1 && v3 < v2));
 63
 64	/* mask the output to 24 bits */
 65	return v2 & ACPI_PM_MASK;
 66}
 67
 68
 69/*
 70 * Some boards have the PMTMR running way too fast. We check
 71 * the PMTMR rate against PIT channel 2 to catch these cases.
 72 */
 73static int verify_pmtmr_rate(void)
 74{
 75	u32 value1, value2;
 76	unsigned long count, delta;
 77
 78	mach_prepare_counter();
 79	value1 = read_pmtmr();
 80	mach_countup(&count);
 81	value2 = read_pmtmr();
 82	delta = (value2 - value1) & ACPI_PM_MASK;
 83
 84	/* Check that the PMTMR delta is within 5% of what we expect */
 85	if (delta < (PMTMR_EXPECTED_RATE * 19) / 20 ||
 86	    delta > (PMTMR_EXPECTED_RATE * 21) / 20) {
 87		printk(KERN_INFO "PM-Timer running at invalid rate: %lu%% of normal - aborting.\n", 100UL * delta / PMTMR_EXPECTED_RATE);
 88		return -1;
 89	}
 90
 91	return 0;
 92}
 93
 94
 95static int init_pmtmr(char* override)
 96{
 97	u32 value1, value2;
 98	unsigned int i;
 99
100 	if (override[0] && strncmp(override,"pmtmr",5))
101		return -ENODEV;
102
103	if (!pmtmr_ioport)
104		return -ENODEV;
105
106	/* we use the TSC for delay_pmtmr, so make sure it exists */
107	if (!cpu_has_tsc)
108		return -ENODEV;
109
110	/* "verify" this timing source */
111	value1 = read_pmtmr();
112	for (i = 0; i < 10000; i++) {
113		value2 = read_pmtmr();
114		if (value2 == value1)
115			continue;
116		if (value2 > value1)
117			goto pm_good;
118		if ((value2 < value1) && ((value2) < 0xFFF))
119			goto pm_good;
120		printk(KERN_INFO "PM-Timer had inconsistent results: 0x%#x, 0x%#x - aborting.\n", value1, value2);
121		return -EINVAL;
122	}
123	printk(KERN_INFO "PM-Timer had no reasonable result: 0x%#x - aborting.\n", value1);
124	return -ENODEV;
125
126pm_good:
127	if (verify_pmtmr_rate() != 0)
128		return -ENODEV;
129
130	init_cpu_khz();
131	return 0;
132}
133
134static inline u32 cyc2us(u32 cycles)
135{
136	/* The Power Management Timer ticks at 3.579545 ticks per microsecond.
137	 * 1 / PM_TIMER_FREQUENCY == 0.27936511 =~ 286/1024 [error: 0.024%]
138	 *
139	 * Even with HZ = 100, delta is at maximum 35796 ticks, so it can
140	 * easily be multiplied with 286 (=0x11E) without having to fear
141	 * u32 overflows.
142	 */
143	cycles *= 286;
144	return (cycles >> 10);
145}
146
147/*
148 * this gets called during each timer interrupt
149 *   - Called while holding the writer xtime_lock
150 */
151static void mark_offset_pmtmr(void)
152{
153	u32 lost, delta, last_offset;
154	static int first_run = 1;
155	last_offset = offset_tick;
156
157	write_seqlock(&monotonic_lock);
158
159	offset_tick = read_pmtmr();
160
161	/* calculate tick interval */
162	delta = (offset_tick - last_offset) & ACPI_PM_MASK;
163
164	/* convert to usecs */
165	delta = cyc2us(delta);
166
167	/* update the monotonic base value */
168	monotonic_base += delta * NSEC_PER_USEC;
169	write_sequnlock(&monotonic_lock);
170
171	/* convert to ticks */
172	delta += offset_delay;
173	lost = delta / (USEC_PER_SEC / HZ);
174	offset_delay = delta % (USEC_PER_SEC / HZ);
175
176
177	/* compensate for lost ticks */
178	if (lost >= 2)
179		jiffies_64 += lost - 1;
180
181	/* don't calculate delay for first run,
182	   or if we've got less then a tick */
183	if (first_run || (lost < 1)) {
184		first_run = 0;
185		offset_delay = 0;
186	}
187}
188
189
190static unsigned long long monotonic_clock_pmtmr(void)
191{
192	u32 last_offset, this_offset;
193	unsigned long long base, ret;
194	unsigned seq;
195
196
197	/* atomically read monotonic base & last_offset */
198	do {
199		seq = read_seqbegin(&monotonic_lock);
200		last_offset = offset_tick;
201		base = monotonic_base;
202	} while (read_seqretry(&monotonic_lock, seq));
203
204	/* Read the pmtmr */
205	this_offset =  read_pmtmr();
206
207	/* convert to nanoseconds */
208	ret = (this_offset - last_offset) & ACPI_PM_MASK;
209	ret = base + (cyc2us(ret) * NSEC_PER_USEC);
210	return ret;
211}
212
213static void delay_pmtmr(unsigned long loops)
214{
215	unsigned long bclock, now;
216
217	rdtscl(bclock);
218	do
219	{
220		rep_nop();
221		rdtscl(now);
222	} while ((now-bclock) < loops);
223}
224
225
226/*
227 * get the offset (in microseconds) from the last call to mark_offset()
228 *	- Called holding a reader xtime_lock
229 */
230static unsigned long get_offset_pmtmr(void)
231{
232	u32 now, offset, delta = 0;
233
234	offset = offset_tick;
235	now = read_pmtmr();
236	delta = (now - offset)&ACPI_PM_MASK;
237
238	return (unsigned long) offset_delay + cyc2us(delta);
239}
240
241
242/* acpi timer_opts struct */
243static struct timer_opts timer_pmtmr = {
244	.name			= "pmtmr",
245	.mark_offset		= mark_offset_pmtmr,
246	.get_offset		= get_offset_pmtmr,
247	.monotonic_clock 	= monotonic_clock_pmtmr,
248	.delay 			= delay_pmtmr,
249};
250
251struct init_timer_opts __initdata timer_pmtmr_init = {
252	.init = init_pmtmr,
253	.opts = &timer_pmtmr,
254};
255
256MODULE_LICENSE("GPL");
257MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
258MODULE_DESCRIPTION("Power Management Timer (PMTMR) as primary timing source for x86");