2 * Intel Multimedia Timer device implementation for SGI SN platforms.
4 * This file is subject to the terms and conditions of the GNU General Public
5 * License. See the file "COPYING" in the main directory of this archive
8 * Copyright (c) 2001-2004 Silicon Graphics, Inc. All rights reserved.
10 * This driver exports an API that should be supportable by any HPET or IA-PC
11 * multimedia timer. The code below is currently specific to the SGI Altix
14 * 11/01/01 - jbarnes - initial revision
15 * 9/10/04 - Christoph Lameter - remove interrupt support for kernel inclusion
18 #include <linux/types.h>
19 #include <linux/kernel.h>
20 #include <linux/ioctl.h>
21 #include <linux/module.h>
22 #include <linux/init.h>
23 #include <linux/errno.h>
25 #include <linux/devfs_fs_kernel.h>
26 #include <linux/mmtimer.h>
27 #include <linux/miscdevice.h>
28 #include <asm/uaccess.h>
29 #include <asm/sn/addrs.h>
30 #include <asm/sn/clksupport.h>
32 MODULE_AUTHOR("Jesse Barnes <jbarnes@sgi.com>");
33 MODULE_DESCRIPTION("Multimedia timer support");
34 MODULE_LICENSE("GPL");
36 /* name of the device, usually in /dev */
37 #define MMTIMER_NAME "mmtimer"
38 #define MMTIMER_DESC "IA-PC Multimedia Timer"
39 #define MMTIMER_VERSION "1.0"
41 #define RTC_BITS 55 /* 55 bits for this implementation */
43 static int mmtimer_ioctl(struct inode *inode, struct file *file,
44 unsigned int cmd, unsigned long arg);
45 static int mmtimer_mmap(struct file *file, struct vm_area_struct *vma);
48 * Period in femtoseconds (10^-15 s)
50 static unsigned long mmtimer_femtoperiod = 0;
52 static struct file_operations mmtimer_fops = {
55 .ioctl = mmtimer_ioctl,
59 * mmtimer_ioctl - ioctl interface for /dev/mmtimer
60 * @inode: inode of the device
61 * @file: file structure for the device
62 * @cmd: command to execute
63 * @arg: optional argument to command
65 * Executes the command specified by @cmd. Returns 0 for success, < 0 for
70 * %MMTIMER_GETOFFSET - Should return the offset (relative to the start
71 * of the page where the registers are mapped) for the counter in question.
73 * %MMTIMER_GETRES - Returns the resolution of the clock in femto (10^-15)
76 * %MMTIMER_GETFREQ - Copies the frequency of the clock in Hz to the address
79 * %MMTIMER_GETBITS - Returns the number of bits in the clock's counter
81 * %MMTIMER_MMAPAVAIL - Returns 1 if the registers can be mmap'd into userspace
83 * %MMTIMER_GETCOUNTER - Gets the current value in the counter and places it
84 * in the address specified by @arg.
86 static int mmtimer_ioctl(struct inode *inode, struct file *file,
87 unsigned int cmd, unsigned long arg)
92 case MMTIMER_GETOFFSET: /* offset of the counter */
94 * SN RTC registers are on their own 64k page
96 if(PAGE_SIZE <= (1 << 16))
97 ret = (((long)RTC_COUNTER_ADDR) & (PAGE_SIZE-1)) / 8;
102 case MMTIMER_GETRES: /* resolution of the clock in 10^-15 s */
103 if(copy_to_user((unsigned long *)arg, &mmtimer_femtoperiod,
104 sizeof(unsigned long)))
108 case MMTIMER_GETFREQ: /* frequency in Hz */
109 if(copy_to_user((unsigned long *)arg,
110 &sn_rtc_cycles_per_second,
111 sizeof(unsigned long)))
116 case MMTIMER_GETBITS: /* number of bits in the clock */
120 case MMTIMER_MMAPAVAIL: /* can we mmap the clock into userspace? */
121 ret = (PAGE_SIZE <= (1 << 16)) ? 1 : 0;
124 case MMTIMER_GETCOUNTER:
125 if(copy_to_user((unsigned long *)arg, RTC_COUNTER_ADDR,
126 sizeof(unsigned long)))
138 * mmtimer_mmap - maps the clock's registers into userspace
139 * @file: file structure for the device
140 * @vma: VMA to map the registers into
142 * Calls remap_page_range() to map the clock's registers into
143 * the calling process' address space.
145 static int mmtimer_mmap(struct file *file, struct vm_area_struct *vma)
147 unsigned long mmtimer_addr;
149 if (vma->vm_end - vma->vm_start != PAGE_SIZE)
152 if (vma->vm_flags & VM_WRITE)
155 if (PAGE_SIZE > (1 << 16))
158 vma->vm_flags |= (VM_IO | VM_SHM | VM_LOCKED );
159 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
161 mmtimer_addr = __pa(RTC_COUNTER_ADDR);
162 mmtimer_addr &= ~(PAGE_SIZE - 1);
163 mmtimer_addr &= 0xfffffffffffffffUL;
165 if (remap_page_range(vma, vma->vm_start, mmtimer_addr, PAGE_SIZE,
166 vma->vm_page_prot)) {
167 printk(KERN_ERR "remap_page_range failed in mmtimer.c\n");
174 static struct miscdevice mmtimer_miscdev = {
181 * mmtimer_init - device initialization routine
183 * Does initial setup for the mmtimer device.
185 static int __init mmtimer_init(void)
187 if (!ia64_platform_is("sn2"))
191 * Sanity check the cycles/sec variable
193 if (sn_rtc_cycles_per_second < 100000) {
194 printk(KERN_ERR "%s: unable to determine clock frequency\n",
199 mmtimer_femtoperiod = ((unsigned long)1E15 + sn_rtc_cycles_per_second /
200 2) / sn_rtc_cycles_per_second;
202 strcpy(mmtimer_miscdev.devfs_name, MMTIMER_NAME);
203 if (misc_register(&mmtimer_miscdev)) {
204 printk(KERN_ERR "%s: failed to register device\n",
209 printk(KERN_INFO "%s: v%s, %ld MHz\n", MMTIMER_DESC, MMTIMER_VERSION,
210 sn_rtc_cycles_per_second/(unsigned long)1E6);
215 module_init(mmtimer_init);