Blame view

drivers/char/hpet.c 24.5 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * Intel & MS High Precision Event Timer Implementation.
   *
   * Copyright (C) 2003 Intel Corporation
   *	Venki Pallipadi
   * (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
   *	Bob Picco <robert.picco@hp.com>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
16
17
18
19
20
21
22
  #include <linux/interrupt.h>
  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/types.h>
  #include <linux/miscdevice.h>
  #include <linux/major.h>
  #include <linux/ioport.h>
  #include <linux/fcntl.h>
  #include <linux/init.h>
  #include <linux/poll.h>
f23f6e08c   Al Viro   [PATCH] severing ...
23
  #include <linux/mm.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
28
29
30
  #include <linux/proc_fs.h>
  #include <linux/spinlock.h>
  #include <linux/sysctl.h>
  #include <linux/wait.h>
  #include <linux/bcd.h>
  #include <linux/seq_file.h>
  #include <linux/bitops.h>
54066a57c   Arnd Bergmann   hpet: kill BKL, a...
31
  #include <linux/compat.h>
0aa366f35   Tony Luck   [IA64] Convert to...
32
  #include <linux/clocksource.h>
0ca01763a   Jaswinder Singh Rajput   hpet: fix style p...
33
  #include <linux/uaccess.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
34
  #include <linux/slab.h>
0ca01763a   Jaswinder Singh Rajput   hpet: fix style p...
35
  #include <linux/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
  
  #include <asm/current.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
  #include <asm/system.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
41
42
43
44
45
46
47
48
  #include <asm/irq.h>
  #include <asm/div64.h>
  
  #include <linux/acpi.h>
  #include <acpi/acpi_bus.h>
  #include <linux/hpet.h>
  
  /*
   * The High Precision Event Timer driver.
   * This driver is closely modelled after the rtc.c driver.
e45f2c077   Denis V. Lunev   x86: correct link...
49
   * http://www.intel.com/hardwaredesign/hpetspec_1.pdf
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
   */
  #define	HPET_USER_FREQ	(64)
  #define	HPET_DRIFT	(500)
757c47240   Randy Dunlap   [PATCH] hpet: all...
53
  #define HPET_RANGE_SIZE		1024	/* from HPET spec */
64a76f667   David Brownell   hpet: /dev/hpet -...
54
55
56
57
58
  
  /* WARNING -- don't get confused.  These macros are never used
   * to write the (single) counter, and rarely to read it.
   * They're badly named; to fix, someday.
   */
0aa366f35   Tony Luck   [IA64] Convert to...
59
60
61
62
63
64
65
  #if BITS_PER_LONG == 64
  #define	write_counter(V, MC)	writeq(V, MC)
  #define	read_counter(MC)	readq(MC)
  #else
  #define	write_counter(V, MC)	writel(V, MC)
  #define	read_counter(MC)	readl(MC)
  #endif
54066a57c   Arnd Bergmann   hpet: kill BKL, a...
66
  static DEFINE_MUTEX(hpet_mutex); /* replaces BKL */
642d30bbc   Clemens Ladisch   [PATCH] hpet: rem...
67
  static u32 hpet_nhpet, hpet_max_freq = HPET_USER_FREQ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68

3dffec45c   S.Çağlar Onur   Silent drivers/ch...
69
70
  /* This clocksource driver currently only works on ia64 */
  #ifdef CONFIG_IA64
0aa366f35   Tony Luck   [IA64] Convert to...
71
  static void __iomem *hpet_mctr;
8e19608e8   Magnus Damm   clocksource: pass...
72
  static cycle_t read_hpet(struct clocksource *cs)
0aa366f35   Tony Luck   [IA64] Convert to...
73
74
75
76
77
  {
  	return (cycle_t)read_counter((void __iomem *)hpet_mctr);
  }
  
  static struct clocksource clocksource_hpet = {
0ca01763a   Jaswinder Singh Rajput   hpet: fix style p...
78
79
80
81
  	.name		= "hpet",
  	.rating		= 250,
  	.read		= read_hpet,
  	.mask		= CLOCKSOURCE_MASK(64),
0ca01763a   Jaswinder Singh Rajput   hpet: fix style p...
82
  	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
0aa366f35   Tony Luck   [IA64] Convert to...
83
84
  };
  static struct clocksource *hpet_clocksource;
3dffec45c   S.Çağlar Onur   Silent drivers/ch...
85
  #endif
0aa366f35   Tony Luck   [IA64] Convert to...
86

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
88
  /* A lock for concurrent access by app and isr hpet activity. */
  static DEFINE_SPINLOCK(hpet_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
90
91
92
93
94
95
96
97
98
99
  
  #define	HPET_DEV_NAME	(7)
  
  struct hpet_dev {
  	struct hpets *hd_hpets;
  	struct hpet __iomem *hd_hpet;
  	struct hpet_timer __iomem *hd_timer;
  	unsigned long hd_ireqfreq;
  	unsigned long hd_irqdata;
  	wait_queue_head_t hd_waitqueue;
  	struct fasync_struct *hd_async_queue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
103
104
105
106
107
108
109
  	unsigned int hd_flags;
  	unsigned int hd_irq;
  	unsigned int hd_hdwirq;
  	char hd_name[HPET_DEV_NAME];
  };
  
  struct hpets {
  	struct hpets *hp_next;
  	struct hpet __iomem *hp_hpet;
  	unsigned long hp_hpet_phys;
0aa366f35   Tony Luck   [IA64] Convert to...
110
  	struct clocksource *hp_clocksource;
ba3f213f8   Clemens Ladisch   [PATCH] HPET: mak...
111
  	unsigned long long hp_tick_freq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
114
115
116
117
118
119
120
121
122
  	unsigned long hp_delta;
  	unsigned int hp_ntimer;
  	unsigned int hp_which;
  	struct hpet_dev hp_dev[1];
  };
  
  static struct hpets *hpets;
  
  #define	HPET_OPEN		0x0001
  #define	HPET_IE			0x0002	/* interrupt enabled */
  #define	HPET_PERIODIC		0x0004
0d2908617   Clemens Ladisch   [PATCH] hpet: all...
123
  #define	HPET_SHARED_IRQ		0x0008
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
  
  #ifndef readq
887c27f36   Adrian Bunk   [PATCH] fix unusu...
127
  static inline unsigned long long readq(void __iomem *addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
131
132
133
  {
  	return readl(addr) | (((unsigned long long)readl(addr + 4)) << 32LL);
  }
  #endif
  
  #ifndef writeq
887c27f36   Adrian Bunk   [PATCH] fix unusu...
134
  static inline void writeq(unsigned long long v, void __iomem *addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
137
138
139
  {
  	writel(v & 0xffffffff, addr);
  	writel(v >> 32, addr + 4);
  }
  #endif
7d12e780e   David Howells   IRQ: Maintain reg...
140
  static irqreturn_t hpet_interrupt(int irq, void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
144
145
  {
  	struct hpet_dev *devp;
  	unsigned long isr;
  
  	devp = data;
0d2908617   Clemens Ladisch   [PATCH] hpet: all...
146
147
148
149
150
  	isr = 1 << (devp - devp->hd_hpets->hp_dev);
  
  	if ((devp->hd_flags & HPET_SHARED_IRQ) &&
  	    !(isr & readl(&devp->hd_hpet->hpet_isr)))
  		return IRQ_NONE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
153
154
155
156
157
158
159
  
  	spin_lock(&hpet_lock);
  	devp->hd_irqdata++;
  
  	/*
  	 * For non-periodic timers, increment the accumulator.
  	 * This has the effect of treating non-periodic like periodic.
  	 */
  	if ((devp->hd_flags & (HPET_IE | HPET_PERIODIC)) == HPET_IE) {
273ef9509   Nils Carlson   drivers/char/hpet...
160
161
162
  		unsigned long m, t, mc, base, k;
  		struct hpet __iomem *hpet = devp->hd_hpet;
  		struct hpets *hpetp = devp->hd_hpets;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
  
  		t = devp->hd_ireqfreq;
ae21cf924   Nils Carlson   hpet: hpet driver...
165
  		m = read_counter(&devp->hd_timer->hpet_compare);
273ef9509   Nils Carlson   drivers/char/hpet...
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
  		mc = read_counter(&hpet->hpet_mc);
  		/* The time for the next interrupt would logically be t + m,
  		 * however, if we are very unlucky and the interrupt is delayed
  		 * for longer than t then we will completely miss the next
  		 * interrupt if we set t + m and an application will hang.
  		 * Therefore we need to make a more complex computation assuming
  		 * that there exists a k for which the following is true:
  		 * k * t + base < mc + delta
  		 * (k + 1) * t + base > mc + delta
  		 * where t is the interval in hpet ticks for the given freq,
  		 * base is the theoretical start value 0 < base < t,
  		 * mc is the main counter value at the time of the interrupt,
  		 * delta is the time it takes to write the a value to the
  		 * comparator.
  		 * k may then be computed as (mc - base + delta) / t .
  		 */
  		base = mc % t;
  		k = (mc - base + hpetp->hp_delta) / t;
  		write_counter(t * (k + 1) + base,
  			      &devp->hd_timer->hpet_compare);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
  	}
0d2908617   Clemens Ladisch   [PATCH] hpet: all...
187
188
  	if (devp->hd_flags & HPET_SHARED_IRQ)
  		writel(isr, &devp->hd_hpet->hpet_isr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  	spin_unlock(&hpet_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
193
194
195
  	wake_up_interruptible(&devp->hd_waitqueue);
  
  	kill_fasync(&devp->hd_async_queue, SIGIO, POLL_IN);
  
  	return IRQ_HANDLED;
  }
70ef6d595   Kevin Hao   x86: get irq for ...
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
  static void hpet_timer_set_irq(struct hpet_dev *devp)
  {
  	unsigned long v;
  	int irq, gsi;
  	struct hpet_timer __iomem *timer;
  
  	spin_lock_irq(&hpet_lock);
  	if (devp->hd_hdwirq) {
  		spin_unlock_irq(&hpet_lock);
  		return;
  	}
  
  	timer = devp->hd_timer;
  
  	/* we prefer level triggered mode */
  	v = readl(&timer->hpet_config);
  	if (!(v & Tn_INT_TYPE_CNF_MASK)) {
  		v |= Tn_INT_TYPE_CNF_MASK;
  		writel(v, &timer->hpet_config);
  	}
  	spin_unlock_irq(&hpet_lock);
  
  	v = (readq(&timer->hpet_config) & Tn_INT_ROUTE_CAP_MASK) >>
  				 Tn_INT_ROUTE_CAP_SHIFT;
  
  	/*
  	 * In PIC mode, skip IRQ0-4, IRQ6-9, IRQ12-15 which is always used by
  	 * legacy device. In IO APIC mode, we skip all the legacy IRQS.
  	 */
  	if (acpi_irq_model == ACPI_IRQ_MODEL_PIC)
  		v &= ~0xf3df;
  	else
  		v &= ~0xffff;
e5d615111   Akinobu Mita   hpet: use for_eac...
229
  	for_each_set_bit(irq, &v, HPET_MAX_IRQ) {
1f45f5621   Yinghai Lu   drivers/char: use...
230
  		if (irq >= nr_irqs) {
70ef6d595   Kevin Hao   x86: get irq for ...
231
232
233
  			irq = HPET_MAX_IRQ;
  			break;
  		}
a2f809b08   Yinghai Lu   irq: change ACPI ...
234
  		gsi = acpi_register_gsi(NULL, irq, ACPI_LEVEL_SENSITIVE,
70ef6d595   Kevin Hao   x86: get irq for ...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
  					ACPI_ACTIVE_LOW);
  		if (gsi > 0)
  			break;
  
  		/* FIXME: Setup interrupt source table */
  	}
  
  	if (irq < HPET_MAX_IRQ) {
  		spin_lock_irq(&hpet_lock);
  		v = readl(&timer->hpet_config);
  		v |= irq << Tn_INT_ROUTE_CNF_SHIFT;
  		writel(v, &timer->hpet_config);
  		devp->hd_hdwirq = gsi;
  		spin_unlock_irq(&hpet_lock);
  	}
  	return;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
256
257
258
259
  static int hpet_open(struct inode *inode, struct file *file)
  {
  	struct hpet_dev *devp;
  	struct hpets *hpetp;
  	int i;
  
  	if (file->f_mode & FMODE_WRITE)
  		return -EINVAL;
54066a57c   Arnd Bergmann   hpet: kill BKL, a...
260
  	mutex_lock(&hpet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
264
  	spin_lock_irq(&hpet_lock);
  
  	for (devp = NULL, hpetp = hpets; hpetp && !devp; hpetp = hpetp->hp_next)
  		for (i = 0; i < hpetp->hp_ntimer; i++)
64a76f667   David Brownell   hpet: /dev/hpet -...
265
  			if (hpetp->hp_dev[i].hd_flags & HPET_OPEN)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
267
268
269
270
271
272
273
  				continue;
  			else {
  				devp = &hpetp->hp_dev[i];
  				break;
  			}
  
  	if (!devp) {
  		spin_unlock_irq(&hpet_lock);
54066a57c   Arnd Bergmann   hpet: kill BKL, a...
274
  		mutex_unlock(&hpet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
276
277
278
279
280
281
  		return -EBUSY;
  	}
  
  	file->private_data = devp;
  	devp->hd_irqdata = 0;
  	devp->hd_flags |= HPET_OPEN;
  	spin_unlock_irq(&hpet_lock);
54066a57c   Arnd Bergmann   hpet: kill BKL, a...
282
  	mutex_unlock(&hpet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283

70ef6d595   Kevin Hao   x86: get irq for ...
284
  	hpet_timer_set_irq(devp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
  	return 0;
  }
  
  static ssize_t
  hpet_read(struct file *file, char __user *buf, size_t count, loff_t * ppos)
  {
  	DECLARE_WAITQUEUE(wait, current);
  	unsigned long data;
  	ssize_t retval;
  	struct hpet_dev *devp;
  
  	devp = file->private_data;
  	if (!devp->hd_ireqfreq)
  		return -EIO;
  
  	if (count < sizeof(unsigned long))
  		return -EINVAL;
  
  	add_wait_queue(&devp->hd_waitqueue, &wait);
  
  	for ( ; ; ) {
  		set_current_state(TASK_INTERRUPTIBLE);
  
  		spin_lock_irq(&hpet_lock);
  		data = devp->hd_irqdata;
  		devp->hd_irqdata = 0;
  		spin_unlock_irq(&hpet_lock);
  
  		if (data)
  			break;
  		else if (file->f_flags & O_NONBLOCK) {
  			retval = -EAGAIN;
  			goto out;
  		} else if (signal_pending(current)) {
  			retval = -ERESTARTSYS;
  			goto out;
  		}
  		schedule();
  	}
  
  	retval = put_user(data, (unsigned long __user *)buf);
  	if (!retval)
  		retval = sizeof(unsigned long);
  out:
  	__set_current_state(TASK_RUNNING);
  	remove_wait_queue(&devp->hd_waitqueue, &wait);
  
  	return retval;
  }
  
  static unsigned int hpet_poll(struct file *file, poll_table * wait)
  {
  	unsigned long v;
  	struct hpet_dev *devp;
  
  	devp = file->private_data;
  
  	if (!devp->hd_ireqfreq)
  		return 0;
  
  	poll_wait(file, &devp->hd_waitqueue, wait);
  
  	spin_lock_irq(&hpet_lock);
  	v = devp->hd_irqdata;
  	spin_unlock_irq(&hpet_lock);
  
  	if (v != 0)
  		return POLLIN | POLLRDNORM;
  
  	return 0;
  }
  
  static int hpet_mmap(struct file *file, struct vm_area_struct *vma)
  {
  #ifdef	CONFIG_HPET_MMAP
  	struct hpet_dev *devp;
  	unsigned long addr;
  
  	if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff)
  		return -EINVAL;
  
  	devp = file->private_data;
  	addr = devp->hd_hpets->hp_hpet_phys;
  
  	if (addr & (PAGE_SIZE - 1))
  		return -ENOSYS;
  
  	vma->vm_flags |= VM_IO;
  	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
375
376
  
  	if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT,
  					PAGE_SIZE, vma->vm_page_prot)) {
3e6716e74   Randy Dunlap   [PATCH] hpet: hpe...
377
378
  		printk(KERN_ERR "%s: io_remap_pfn_range failed
  ",
bf9d89295   Harvey Harrison   drivers/char: rep...
379
  			__func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
  		return -EAGAIN;
  	}
  
  	return 0;
  #else
  	return -ENOSYS;
  #endif
  }
  
  static int hpet_fasync(int fd, struct file *file, int on)
  {
  	struct hpet_dev *devp;
  
  	devp = file->private_data;
  
  	if (fasync_helper(fd, file, on, &devp->hd_async_queue) >= 0)
  		return 0;
  	else
  		return -EIO;
  }
  
  static int hpet_release(struct inode *inode, struct file *file)
  {
  	struct hpet_dev *devp;
  	struct hpet_timer __iomem *timer;
  	int irq = 0;
  
  	devp = file->private_data;
  	timer = devp->hd_timer;
  
  	spin_lock_irq(&hpet_lock);
  
  	writeq((readq(&timer->hpet_config) & ~Tn_INT_ENB_CNF_MASK),
  	       &timer->hpet_config);
  
  	irq = devp->hd_irq;
  	devp->hd_irq = 0;
  
  	devp->hd_ireqfreq = 0;
  
  	if (devp->hd_flags & HPET_PERIODIC
  	    && readq(&timer->hpet_config) & Tn_TYPE_CNF_MASK) {
  		unsigned long v;
  
  		v = readq(&timer->hpet_config);
  		v ^= Tn_TYPE_CNF_MASK;
  		writeq(v, &timer->hpet_config);
  	}
  
  	devp->hd_flags &= ~(HPET_OPEN | HPET_IE | HPET_PERIODIC);
  	spin_unlock_irq(&hpet_lock);
  
  	if (irq)
  		free_irq(irq, devp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
435
436
  	file->private_data = NULL;
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
438
439
440
441
442
443
444
445
446
447
448
  static int hpet_ioctl_ieon(struct hpet_dev *devp)
  {
  	struct hpet_timer __iomem *timer;
  	struct hpet __iomem *hpet;
  	struct hpets *hpetp;
  	int irq;
  	unsigned long g, v, t, m;
  	unsigned long flags, isr;
  
  	timer = devp->hd_timer;
  	hpet = devp->hd_hpet;
  	hpetp = devp->hd_hpets;
9090e6db8   Clemens Ladisch   [PATCH] hpet: dis...
449
450
  	if (!devp->hd_ireqfreq)
  		return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
453
454
455
456
457
458
  	spin_lock_irq(&hpet_lock);
  
  	if (devp->hd_flags & HPET_IE) {
  		spin_unlock_irq(&hpet_lock);
  		return -EBUSY;
  	}
  
  	devp->hd_flags |= HPET_IE;
0d2908617   Clemens Ladisch   [PATCH] hpet: all...
459
460
461
  
  	if (readl(&timer->hpet_config) & Tn_INT_TYPE_CNF_MASK)
  		devp->hd_flags |= HPET_SHARED_IRQ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
  	spin_unlock_irq(&hpet_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
465
  	irq = devp->hd_hdwirq;
  
  	if (irq) {
0d2908617   Clemens Ladisch   [PATCH] hpet: all...
466
  		unsigned long irq_flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467

96e9694df   Clemens Ladisch   hpet: fix unwante...
468
469
470
471
472
473
474
475
476
477
478
479
480
481
  		if (devp->hd_flags & HPET_SHARED_IRQ) {
  			/*
  			 * To prevent the interrupt handler from seeing an
  			 * unwanted interrupt status bit, program the timer
  			 * so that it will not fire in the near future ...
  			 */
  			writel(readl(&timer->hpet_config) & ~Tn_TYPE_CNF_MASK,
  			       &timer->hpet_config);
  			write_counter(read_counter(&hpet->hpet_mc),
  				      &timer->hpet_compare);
  			/* ... and clear any left-over status. */
  			isr = 1 << (devp - devp->hd_hpets->hp_dev);
  			writel(isr, &hpet->hpet_isr);
  		}
0d2908617   Clemens Ladisch   [PATCH] hpet: all...
482
483
  		sprintf(devp->hd_name, "hpet%d", (int)(devp - hpetp->hp_dev));
  		irq_flags = devp->hd_flags & HPET_SHARED_IRQ
0f2ed4c6b   Thomas Gleixner   [PATCH] irq-flags...
484
  						? IRQF_SHARED : IRQF_DISABLED;
0d2908617   Clemens Ladisch   [PATCH] hpet: all...
485
486
  		if (request_irq(irq, hpet_interrupt, irq_flags,
  				devp->hd_name, (void *)devp)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
  			printk(KERN_ERR "hpet: IRQ %d is not free
  ", irq);
  			irq = 0;
  		}
  	}
  
  	if (irq == 0) {
  		spin_lock_irq(&hpet_lock);
  		devp->hd_flags ^= HPET_IE;
  		spin_unlock_irq(&hpet_lock);
  		return -EIO;
  	}
  
  	devp->hd_irq = irq;
  	t = devp->hd_ireqfreq;
  	v = readq(&timer->hpet_config);
64a76f667   David Brownell   hpet: /dev/hpet -...
503
504
505
506
507
  
  	/* 64-bit comparators are not yet supported through the ioctls,
  	 * so force this into 32-bit mode if it supports both modes
  	 */
  	g = v | Tn_32MODE_CNF_MASK | Tn_INT_ENB_CNF_MASK;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
509
  
  	if (devp->hd_flags & HPET_PERIODIC) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510
  		g |= Tn_TYPE_CNF_MASK;
ae21cf924   Nils Carlson   hpet: hpet driver...
511
  		v |= Tn_TYPE_CNF_MASK | Tn_VAL_SET_CNF_MASK;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
513
  		writeq(v, &timer->hpet_config);
  		local_irq_save(flags);
64a76f667   David Brownell   hpet: /dev/hpet -...
514

ae21cf924   Nils Carlson   hpet: hpet driver...
515
516
  		/*
  		 * NOTE: First we modify the hidden accumulator
64a76f667   David Brownell   hpet: /dev/hpet -...
517
518
  		 * register supported by periodic-capable comparators.
  		 * We never want to modify the (single) counter; that
ae21cf924   Nils Carlson   hpet: hpet driver...
519
520
  		 * would affect all the comparators. The value written
  		 * is the counter value when the first interrupt is due.
64a76f667   David Brownell   hpet: /dev/hpet -...
521
  		 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522
523
  		m = read_counter(&hpet->hpet_mc);
  		write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare);
ae21cf924   Nils Carlson   hpet: hpet driver...
524
525
526
527
528
  		/*
  		 * Then we modify the comparator, indicating the period
  		 * for subsequent interrupt.
  		 */
  		write_counter(t, &timer->hpet_compare);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
530
531
532
533
  	} else {
  		local_irq_save(flags);
  		m = read_counter(&hpet->hpet_mc);
  		write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare);
  	}
0d2908617   Clemens Ladisch   [PATCH] hpet: all...
534
  	if (devp->hd_flags & HPET_SHARED_IRQ) {
3d5640d1c   Clemens Ladisch   [PATCH] hpet: fix...
535
  		isr = 1 << (devp - devp->hd_hpets->hp_dev);
0d2908617   Clemens Ladisch   [PATCH] hpet: all...
536
537
  		writel(isr, &hpet->hpet_isr);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538
539
540
541
542
  	writeq(g, &timer->hpet_config);
  	local_irq_restore(flags);
  
  	return 0;
  }
ba3f213f8   Clemens Ladisch   [PATCH] HPET: mak...
543
544
545
  /* converts Hz to number of timer ticks */
  static inline unsigned long hpet_time_div(struct hpets *hpets,
  					  unsigned long dis)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
  {
ba3f213f8   Clemens Ladisch   [PATCH] HPET: mak...
547
  	unsigned long long m;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548

ba3f213f8   Clemens Ladisch   [PATCH] HPET: mak...
549
  	m = hpets->hp_tick_freq + (dis >> 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
  	do_div(m, dis);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551
552
553
554
  	return (unsigned long)m;
  }
  
  static int
54066a57c   Arnd Bergmann   hpet: kill BKL, a...
555
556
  hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg,
  		  struct hpet_info *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
  {
  	struct hpet_timer __iomem *timer;
  	struct hpet __iomem *hpet;
  	struct hpets *hpetp;
  	int err;
  	unsigned long v;
  
  	switch (cmd) {
  	case HPET_IE_OFF:
  	case HPET_INFO:
  	case HPET_EPI:
  	case HPET_DPI:
  	case HPET_IRQFREQ:
  		timer = devp->hd_timer;
  		hpet = devp->hd_hpet;
  		hpetp = devp->hd_hpets;
  		break;
  	case HPET_IE_ON:
  		return hpet_ioctl_ieon(devp);
  	default:
  		return -EINVAL;
  	}
  
  	err = 0;
  
  	switch (cmd) {
  	case HPET_IE_OFF:
  		if ((devp->hd_flags & HPET_IE) == 0)
  			break;
  		v = readq(&timer->hpet_config);
  		v &= ~Tn_INT_ENB_CNF_MASK;
  		writeq(v, &timer->hpet_config);
  		if (devp->hd_irq) {
  			free_irq(devp->hd_irq, devp);
  			devp->hd_irq = 0;
  		}
  		devp->hd_flags ^= HPET_IE;
  		break;
  	case HPET_INFO:
  		{
dae512edc   Vasiliy Kulikov   drivers/char/hpet...
597
  			memset(info, 0, sizeof(*info));
af95eade6   Clemens Ladisch   [PATCH] hpet: fix...
598
  			if (devp->hd_ireqfreq)
54066a57c   Arnd Bergmann   hpet: kill BKL, a...
599
  				info->hi_ireqfreq =
af95eade6   Clemens Ladisch   [PATCH] hpet: fix...
600
  					hpet_time_div(hpetp, devp->hd_ireqfreq);
54066a57c   Arnd Bergmann   hpet: kill BKL, a...
601
  			info->hi_flags =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
  			    readq(&timer->hpet_config) & Tn_PER_INT_CAP_MASK;
54066a57c   Arnd Bergmann   hpet: kill BKL, a...
603
604
  			info->hi_hpet = hpetp->hp_which;
  			info->hi_timer = devp - hpetp->hp_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
  			break;
  		}
  	case HPET_EPI:
  		v = readq(&timer->hpet_config);
  		if ((v & Tn_PER_INT_CAP_MASK) == 0) {
  			err = -ENXIO;
  			break;
  		}
  		devp->hd_flags |= HPET_PERIODIC;
  		break;
  	case HPET_DPI:
  		v = readq(&timer->hpet_config);
  		if ((v & Tn_PER_INT_CAP_MASK) == 0) {
  			err = -ENXIO;
  			break;
  		}
  		if (devp->hd_flags & HPET_PERIODIC &&
  		    readq(&timer->hpet_config) & Tn_TYPE_CNF_MASK) {
  			v = readq(&timer->hpet_config);
  			v ^= Tn_TYPE_CNF_MASK;
  			writeq(v, &timer->hpet_config);
  		}
  		devp->hd_flags &= ~HPET_PERIODIC;
  		break;
  	case HPET_IRQFREQ:
54066a57c   Arnd Bergmann   hpet: kill BKL, a...
630
  		if ((arg > hpet_max_freq) &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
632
633
634
  		    !capable(CAP_SYS_RESOURCE)) {
  			err = -EACCES;
  			break;
  		}
189e2dd13   Clemens Ladisch   [PATCH] hpet: all...
635
  		if (!arg) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
637
638
  			err = -EINVAL;
  			break;
  		}
ba3f213f8   Clemens Ladisch   [PATCH] HPET: mak...
639
  		devp->hd_ireqfreq = hpet_time_div(hpetp, arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
641
642
643
  	}
  
  	return err;
  }
54066a57c   Arnd Bergmann   hpet: kill BKL, a...
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
  static long
  hpet_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  {
  	struct hpet_info info;
  	int err;
  
  	mutex_lock(&hpet_mutex);
  	err = hpet_ioctl_common(file->private_data, cmd, arg, &info);
  	mutex_unlock(&hpet_mutex);
  
  	if ((cmd == HPET_INFO) && !err &&
  	    (copy_to_user((void __user *)arg, &info, sizeof(info))))
  		err = -EFAULT;
  
  	return err;
  }
  
  #ifdef CONFIG_COMPAT
  struct compat_hpet_info {
  	compat_ulong_t hi_ireqfreq;	/* Hz */
  	compat_ulong_t hi_flags;	/* information */
  	unsigned short hi_hpet;
  	unsigned short hi_timer;
  };
  
  static long
  hpet_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  {
  	struct hpet_info info;
  	int err;
  
  	mutex_lock(&hpet_mutex);
  	err = hpet_ioctl_common(file->private_data, cmd, arg, &info);
  	mutex_unlock(&hpet_mutex);
  
  	if ((cmd == HPET_INFO) && !err) {
  		struct compat_hpet_info __user *u = compat_ptr(arg);
  		if (put_user(info.hi_ireqfreq, &u->hi_ireqfreq) ||
  		    put_user(info.hi_flags, &u->hi_flags) ||
  		    put_user(info.hi_hpet, &u->hi_hpet) ||
  		    put_user(info.hi_timer, &u->hi_timer))
  			err = -EFAULT;
  	}
  
  	return err;
  }
  #endif
62322d255   Arjan van de Ven   [PATCH] make more...
691
  static const struct file_operations hpet_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
692
693
694
695
  	.owner = THIS_MODULE,
  	.llseek = no_llseek,
  	.read = hpet_read,
  	.poll = hpet_poll,
55929332c   Arnd Bergmann   drivers: Push dow...
696
  	.unlocked_ioctl = hpet_ioctl,
54066a57c   Arnd Bergmann   hpet: kill BKL, a...
697
698
699
  #ifdef CONFIG_COMPAT
  	.compat_ioctl = hpet_compat_ioctl,
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
700
701
702
703
704
  	.open = hpet_open,
  	.release = hpet_release,
  	.fasync = hpet_fasync,
  	.mmap = hpet_mmap,
  };
3e6716e74   Randy Dunlap   [PATCH] hpet: hpe...
705
706
707
708
709
710
711
712
713
714
  static int hpet_is_known(struct hpet_data *hdp)
  {
  	struct hpets *hpetp;
  
  	for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next)
  		if (hpetp->hp_hpet_phys == hdp->hd_phys_address)
  			return 1;
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715
716
  static ctl_table hpet_table[] = {
  	{
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
718
719
720
  	 .procname = "max-user-freq",
  	 .data = &hpet_max_freq,
  	 .maxlen = sizeof(int),
  	 .mode = 0644,
6d4561110   Eric W. Biederman   sysctl: Drop & in...
721
  	 .proc_handler = proc_dointvec,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
722
  	 },
894d24911   Eric W. Biederman   sysctl drivers: R...
723
  	{}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
724
725
726
727
  };
  
  static ctl_table hpet_root[] = {
  	{
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
728
729
730
731
732
  	 .procname = "hpet",
  	 .maxlen = 0,
  	 .mode = 0555,
  	 .child = hpet_table,
  	 },
894d24911   Eric W. Biederman   sysctl drivers: R...
733
  	{}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
734
735
736
737
  };
  
  static ctl_table dev_root[] = {
  	{
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
738
739
740
741
742
  	 .procname = "dev",
  	 .maxlen = 0,
  	 .mode = 0555,
  	 .child = hpet_root,
  	 },
894d24911   Eric W. Biederman   sysctl drivers: R...
743
  	{}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
744
745
746
  };
  
  static struct ctl_table_header *sysctl_header;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
747
748
749
750
751
752
  /*
   * Adjustment for when arming the timer with
   * initial conditions.  That is, main counter
   * ticks expired before interrupts are enabled.
   */
  #define	TICK_CALIBRATE	(1000UL)
303d379c5   Yasunori Goto   hpet: fix the pos...
753
  static unsigned long __hpet_calibrate(struct hpets *hpetp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
  {
  	struct hpet_timer __iomem *timer = NULL;
  	unsigned long t, m, count, i, flags, start;
  	struct hpet_dev *devp;
  	int j;
  	struct hpet __iomem *hpet;
  
  	for (j = 0, devp = hpetp->hp_dev; j < hpetp->hp_ntimer; j++, devp++)
  		if ((devp->hd_flags & HPET_OPEN) == 0) {
  			timer = devp->hd_timer;
  			break;
  		}
  
  	if (!timer)
  		return 0;
3d5640d1c   Clemens Ladisch   [PATCH] hpet: fix...
769
  	hpet = hpetp->hp_hpet;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
770
771
772
  	t = read_counter(&timer->hpet_compare);
  
  	i = 0;
ba3f213f8   Clemens Ladisch   [PATCH] HPET: mak...
773
  	count = hpet_time_div(hpetp, TICK_CALIBRATE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774
775
776
777
778
779
780
781
782
783
784
785
786
787
  
  	local_irq_save(flags);
  
  	start = read_counter(&hpet->hpet_mc);
  
  	do {
  		m = read_counter(&hpet->hpet_mc);
  		write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare);
  	} while (i++, (m - start) < count);
  
  	local_irq_restore(flags);
  
  	return (m - start) / i;
  }
303d379c5   Yasunori Goto   hpet: fix the pos...
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
  static unsigned long hpet_calibrate(struct hpets *hpetp)
  {
  	unsigned long ret = -1;
  	unsigned long tmp;
  
  	/*
  	 * Try to calibrate until return value becomes stable small value.
  	 * If SMI interruption occurs in calibration loop, the return value
  	 * will be big. This avoids its impact.
  	 */
  	for ( ; ; ) {
  		tmp = __hpet_calibrate(hpetp);
  		if (ret <= tmp)
  			break;
  		ret = tmp;
  	}
  
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807
808
  int hpet_alloc(struct hpet_data *hdp)
  {
5761d64b2   Thomas Gleixner   x86: revert assig...
809
  	u64 cap, mcfg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
810
  	struct hpet_dev *devp;
5761d64b2   Thomas Gleixner   x86: revert assig...
811
  	u32 i, ntimer;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
813
814
  	struct hpets *hpetp;
  	size_t siz;
  	struct hpet __iomem *hpet;
0ca01763a   Jaswinder Singh Rajput   hpet: fix style p...
815
  	static struct hpets *last;
5761d64b2   Thomas Gleixner   x86: revert assig...
816
  	unsigned long period;
ba3f213f8   Clemens Ladisch   [PATCH] HPET: mak...
817
  	unsigned long long temp;
f92a789d2   David Brownell   hpet: /dev/hpet -...
818
  	u32 remainder;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
819
820
821
  
  	/*
  	 * hpet_alloc can be called by platform dependent code.
3e6716e74   Randy Dunlap   [PATCH] hpet: hpe...
822
823
  	 * If platform dependent code has allocated the hpet that
  	 * ACPI has also reported, then we catch it here.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
824
  	 */
3e6716e74   Randy Dunlap   [PATCH] hpet: hpe...
825
826
827
  	if (hpet_is_known(hdp)) {
  		printk(KERN_DEBUG "%s: duplicate HPET ignored
  ",
bf9d89295   Harvey Harrison   drivers/char: rep...
828
  			__func__);
3e6716e74   Randy Dunlap   [PATCH] hpet: hpe...
829
830
  		return 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
831
832
833
  
  	siz = sizeof(struct hpets) + ((hdp->hd_nirqs - 1) *
  				      sizeof(struct hpet_dev));
3e6716e74   Randy Dunlap   [PATCH] hpet: hpe...
834
  	hpetp = kzalloc(siz, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
835
836
837
  
  	if (!hpetp)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
838
839
840
841
842
  	hpetp->hp_which = hpet_nhpet++;
  	hpetp->hp_hpet = hdp->hd_address;
  	hpetp->hp_hpet_phys = hdp->hd_phys_address;
  
  	hpetp->hp_ntimer = hdp->hd_nirqs;
e3f37a54f   Balaji Rao   x86: assign IRQs ...
843

5761d64b2   Thomas Gleixner   x86: revert assig...
844
845
  	for (i = 0; i < hdp->hd_nirqs; i++)
  		hpetp->hp_dev[i].hd_hdwirq = hdp->hd_irq[i];
37a47db8d   Balaji Rao   x86: assign IRQs ...
846

5761d64b2   Thomas Gleixner   x86: revert assig...
847
  	hpet = hpetp->hp_hpet;
e3f37a54f   Balaji Rao   x86: assign IRQs ...
848

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
  	cap = readq(&hpet->hpet_cap);
  
  	ntimer = ((cap & HPET_NUM_TIM_CAP_MASK) >> HPET_NUM_TIM_CAP_SHIFT) + 1;
  
  	if (hpetp->hp_ntimer != ntimer) {
  		printk(KERN_WARNING "hpet: number irqs doesn't agree"
  		       " with number of timers
  ");
  		kfree(hpetp);
  		return -ENODEV;
  	}
  
  	if (last)
  		last->hp_next = hpetp;
  	else
  		hpets = hpetp;
  
  	last = hpetp;
ba3f213f8   Clemens Ladisch   [PATCH] HPET: mak...
867
868
869
870
871
872
  	period = (cap & HPET_COUNTER_CLK_PERIOD_MASK) >>
  		HPET_COUNTER_CLK_PERIOD_SHIFT; /* fs, 10^-15 */
  	temp = 1000000000000000uLL; /* 10^15 femtoseconds per second */
  	temp += period >> 1; /* round */
  	do_div(temp, period);
  	hpetp->hp_tick_freq = temp; /* ticks per second */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873

3034d11c9   Andi Kleen   [PATCH] Don't pri...
874
875
  	printk(KERN_INFO "hpet%d: at MMIO 0x%lx, IRQ%s",
  		hpetp->hp_which, hdp->hd_phys_address,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
876
877
  		hpetp->hp_ntimer > 1 ? "s" : "");
  	for (i = 0; i < hpetp->hp_ntimer; i++)
5761d64b2   Thomas Gleixner   x86: revert assig...
878
  		printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
879
880
  	printk("
  ");
f92a789d2   David Brownell   hpet: /dev/hpet -...
881
882
  	temp = hpetp->hp_tick_freq;
  	remainder = do_div(temp, 1000000);
64a76f667   David Brownell   hpet: /dev/hpet -...
883
884
885
886
887
  	printk(KERN_INFO
  		"hpet%u: %u comparators, %d-bit %u.%06u MHz counter
  ",
  		hpetp->hp_which, hpetp->hp_ntimer,
  		cap & HPET_COUNTER_SIZE_MASK ? 64 : 32,
f92a789d2   David Brownell   hpet: /dev/hpet -...
888
  		(unsigned) temp, remainder);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
889
890
891
892
893
894
895
  
  	mcfg = readq(&hpet->hpet_config);
  	if ((mcfg & HPET_ENABLE_CNF_MASK) == 0) {
  		write_counter(0L, &hpet->hpet_mc);
  		mcfg |= HPET_ENABLE_CNF_MASK;
  		writeq(mcfg, &hpet->hpet_config);
  	}
642d30bbc   Clemens Ladisch   [PATCH] hpet: rem...
896
  	for (i = 0, devp = hpetp->hp_dev; i < hpetp->hp_ntimer; i++, devp++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
897
898
899
  		struct hpet_timer __iomem *timer;
  
  		timer = &hpet->hpet_timers[devp - hpetp->hp_dev];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
  
  		devp->hd_hpets = hpetp;
  		devp->hd_hpet = hpet;
  		devp->hd_timer = timer;
  
  		/*
  		 * If the timer was reserved by platform code,
  		 * then make timer unavailable for opens.
  		 */
  		if (hdp->hd_state & (1 << i)) {
  			devp->hd_flags = HPET_OPEN;
  			continue;
  		}
  
  		init_waitqueue_head(&devp->hd_waitqueue);
  	}
  
  	hpetp->hp_delta = hpet_calibrate(hpetp);
0aa366f35   Tony Luck   [IA64] Convert to...
918

3b2b64fd3   Linus Torvalds   Do not use the ia...
919
920
  /* This clocksource driver currently only works on ia64 */
  #ifdef CONFIG_IA64
0aa366f35   Tony Luck   [IA64] Convert to...
921
922
  	if (!hpet_clocksource) {
  		hpet_mctr = (void __iomem *)&hpetp->hp_hpet->hpet_mc;
574c44fa8   Andy Lutomirski   ia64: Replace clo...
923
  		clocksource_hpet.archdata.fsys_mmio = hpet_mctr;
d60c30417   John Stultz   ia64: convert to ...
924
  		clocksource_register_hz(&clocksource_hpet, hpetp->hp_tick_freq);
0aa366f35   Tony Luck   [IA64] Convert to...
925
926
927
  		hpetp->hp_clocksource = &clocksource_hpet;
  		hpet_clocksource = &clocksource_hpet;
  	}
3b2b64fd3   Linus Torvalds   Do not use the ia...
928
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
929
930
931
932
933
934
935
936
937
  
  	return 0;
  }
  
  static acpi_status hpet_resources(struct acpi_resource *res, void *data)
  {
  	struct hpet_data *hdp;
  	acpi_status status;
  	struct acpi_resource_address64 addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
938
939
940
941
942
943
  
  	hdp = data;
  
  	status = acpi_resource_to_address64(res, &addr);
  
  	if (ACPI_SUCCESS(status)) {
50eca3eb8   Bob Moore   [ACPI] ACPICA 200...
944
  		hdp->hd_phys_address = addr.minimum;
9224a867c   Bjorn Helgaas   HPET: fix ACPI me...
945
  		hdp->hd_address = ioremap(addr.minimum, addr.address_length);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
946

3e6716e74   Randy Dunlap   [PATCH] hpet: hpe...
947
  		if (hpet_is_known(hdp)) {
3e6716e74   Randy Dunlap   [PATCH] hpet: hpe...
948
  			iounmap(hdp->hd_address);
78e1ca49c   Zhao Yakui   ACPI: hpet: ACPI ...
949
  			return AE_ALREADY_EXISTS;
3e6716e74   Randy Dunlap   [PATCH] hpet: hpe...
950
  		}
50eca3eb8   Bob Moore   [ACPI] ACPICA 200...
951
952
  	} else if (res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
  		struct acpi_resource_fixed_memory32 *fixmem32;
757c47240   Randy Dunlap   [PATCH] hpet: all...
953
954
955
  
  		fixmem32 = &res->data.fixed_memory32;
  		if (!fixmem32)
78e1ca49c   Zhao Yakui   ACPI: hpet: ACPI ...
956
  			return AE_NO_MEMORY;
757c47240   Randy Dunlap   [PATCH] hpet: all...
957

50eca3eb8   Bob Moore   [ACPI] ACPICA 200...
958
959
  		hdp->hd_phys_address = fixmem32->address;
  		hdp->hd_address = ioremap(fixmem32->address,
757c47240   Randy Dunlap   [PATCH] hpet: all...
960
  						HPET_RANGE_SIZE);
3e6716e74   Randy Dunlap   [PATCH] hpet: hpe...
961
  		if (hpet_is_known(hdp)) {
3e6716e74   Randy Dunlap   [PATCH] hpet: hpe...
962
  			iounmap(hdp->hd_address);
78e1ca49c   Zhao Yakui   ACPI: hpet: ACPI ...
963
  			return AE_ALREADY_EXISTS;
3e6716e74   Randy Dunlap   [PATCH] hpet: hpe...
964
  		}
50eca3eb8   Bob Moore   [ACPI] ACPICA 200...
965
966
  	} else if (res->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
  		struct acpi_resource_extended_irq *irqp;
be5efffb7   Bjorn Helgaas   [PATCH] HPET: han...
967
  		int i, irq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
968
969
  
  		irqp = &res->data.extended_irq;
be5efffb7   Bjorn Helgaas   [PATCH] HPET: han...
970
  		for (i = 0; i < irqp->interrupt_count; i++) {
a2f809b08   Yinghai Lu   irq: change ACPI ...
971
  			irq = acpi_register_gsi(NULL, irqp->interrupts[i],
be5efffb7   Bjorn Helgaas   [PATCH] HPET: han...
972
973
974
975
976
977
  				      irqp->triggering, irqp->polarity);
  			if (irq < 0)
  				return AE_ERROR;
  
  			hdp->hd_irq[hdp->hd_nirqs] = irq;
  			hdp->hd_nirqs++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
  		}
  	}
  
  	return AE_OK;
  }
  
  static int hpet_acpi_add(struct acpi_device *device)
  {
  	acpi_status result;
  	struct hpet_data data;
  
  	memset(&data, 0, sizeof(data));
  
  	result =
  	    acpi_walk_resources(device->handle, METHOD_NAME__CRS,
  				hpet_resources, &data);
  
  	if (ACPI_FAILURE(result))
  		return -ENODEV;
  
  	if (!data.hd_address || !data.hd_nirqs) {
a56d53187   Jiri Slaby   hpet: unmap unuse...
999
1000
  		if (data.hd_address)
  			iounmap(data.hd_address);
bf9d89295   Harvey Harrison   drivers/char: rep...
1001
1002
  		printk("%s: no address or irqs in _CRS
  ", __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1003
1004
1005
1006
1007
1008
1009
1010
  		return -ENODEV;
  	}
  
  	return hpet_alloc(&data);
  }
  
  static int hpet_acpi_remove(struct acpi_device *device, int type)
  {
0aa366f35   Tony Luck   [IA64] Convert to...
1011
  	/* XXX need to unregister clocksource, dealloc mem, etc */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1012
1013
  	return -EINVAL;
  }
1ba90e3a8   Thomas Renninger   ACPI: autoload mo...
1014
1015
1016
1017
1018
  static const struct acpi_device_id hpet_device_ids[] = {
  	{"PNP0103", 0},
  	{"", 0},
  };
  MODULE_DEVICE_TABLE(acpi, hpet_device_ids);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1019
1020
  static struct acpi_driver hpet_acpi_driver = {
  	.name = "hpet",
1ba90e3a8   Thomas Renninger   ACPI: autoload mo...
1021
  	.ids = hpet_device_ids,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
  	.ops = {
  		.add = hpet_acpi_add,
  		.remove = hpet_acpi_remove,
  		},
  };
  
  static struct miscdevice hpet_misc = { HPET_MINOR, "hpet", &hpet_fops };
  
  static int __init hpet_init(void)
  {
  	int result;
  
  	result = misc_register(&hpet_misc);
  	if (result < 0)
  		return -ENODEV;
0b4d41471   Eric W. Biederman   [PATCH] sysctl: r...
1037
  	sysctl_header = register_sysctl_table(dev_root);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
  
  	result = acpi_bus_register_driver(&hpet_acpi_driver);
  	if (result < 0) {
  		if (sysctl_header)
  			unregister_sysctl_table(sysctl_header);
  		misc_deregister(&hpet_misc);
  		return result;
  	}
  
  	return 0;
  }
  
  static void __exit hpet_exit(void)
  {
  	acpi_bus_unregister_driver(&hpet_acpi_driver);
  
  	if (sysctl_header)
  		unregister_sysctl_table(sysctl_header);
  	misc_deregister(&hpet_misc);
  
  	return;
  }
  
  module_init(hpet_init);
  module_exit(hpet_exit);
  MODULE_AUTHOR("Bob Picco <Robert.Picco@hp.com>");
  MODULE_LICENSE("GPL");