diff options
author | Dimitri Sivanich <sivanich@sgi.com> | 2009-09-23 18:57:15 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-24 10:21:03 -0400 |
commit | fbd8ae106850b6a0215c2776e70a75a1b93cafc2 (patch) | |
tree | 88c63571c3daf7da4ce5779db4b12e99c9d90dff /drivers | |
parent | 459ca8b4ed1889b0a69bbe21888e6af136d495f3 (diff) |
drivers/char/uv_mmtimer.c: add memory mapped RTC driver for UV
This driver memory maps the UV Hub RTC.
Signed-off-by: Dimitri Sivanich <sivanich@sgi.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/Kconfig | 8 | ||||
-rw-r--r-- | drivers/char/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/uv_mmtimer.c | 216 |
3 files changed, 225 insertions, 0 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 6a06913b01d3..08a6f50ae791 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig | |||
@@ -1087,6 +1087,14 @@ config MMTIMER | |||
1087 | The mmtimer device allows direct userspace access to the | 1087 | The mmtimer device allows direct userspace access to the |
1088 | Altix system timer. | 1088 | Altix system timer. |
1089 | 1089 | ||
1090 | config UV_MMTIMER | ||
1091 | tristate "UV_MMTIMER Memory mapped RTC for SGI UV" | ||
1092 | depends on X86_UV | ||
1093 | default m | ||
1094 | help | ||
1095 | The uv_mmtimer device allows direct userspace access to the | ||
1096 | UV system timer. | ||
1097 | |||
1090 | source "drivers/char/tpm/Kconfig" | 1098 | source "drivers/char/tpm/Kconfig" |
1091 | 1099 | ||
1092 | config TELCLOCK | 1100 | config TELCLOCK |
diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 66f779ad4f4c..19a79dd79eee 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile | |||
@@ -58,6 +58,7 @@ obj-$(CONFIG_RAW_DRIVER) += raw.o | |||
58 | obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o | 58 | obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o |
59 | obj-$(CONFIG_MSPEC) += mspec.o | 59 | obj-$(CONFIG_MSPEC) += mspec.o |
60 | obj-$(CONFIG_MMTIMER) += mmtimer.o | 60 | obj-$(CONFIG_MMTIMER) += mmtimer.o |
61 | obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o | ||
61 | obj-$(CONFIG_VIOTAPE) += viotape.o | 62 | obj-$(CONFIG_VIOTAPE) += viotape.o |
62 | obj-$(CONFIG_HVCS) += hvcs.o | 63 | obj-$(CONFIG_HVCS) += hvcs.o |
63 | obj-$(CONFIG_IBM_BSR) += bsr.o | 64 | obj-$(CONFIG_IBM_BSR) += bsr.o |
diff --git a/drivers/char/uv_mmtimer.c b/drivers/char/uv_mmtimer.c new file mode 100644 index 000000000000..867b67be9f0a --- /dev/null +++ b/drivers/char/uv_mmtimer.c | |||
@@ -0,0 +1,216 @@ | |||
1 | /* | ||
2 | * Timer device implementation for SGI UV platform. | ||
3 | * | ||
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 | ||
6 | * for more details. | ||
7 | * | ||
8 | * Copyright (c) 2009 Silicon Graphics, Inc. All rights reserved. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/ioctl.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/fs.h> | ||
20 | #include <linux/mmtimer.h> | ||
21 | #include <linux/miscdevice.h> | ||
22 | #include <linux/posix-timers.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/time.h> | ||
25 | #include <linux/math64.h> | ||
26 | #include <linux/smp_lock.h> | ||
27 | |||
28 | #include <asm/genapic.h> | ||
29 | #include <asm/uv/uv_hub.h> | ||
30 | #include <asm/uv/bios.h> | ||
31 | #include <asm/uv/uv.h> | ||
32 | |||
33 | MODULE_AUTHOR("Dimitri Sivanich <sivanich@sgi.com>"); | ||
34 | MODULE_DESCRIPTION("SGI UV Memory Mapped RTC Timer"); | ||
35 | MODULE_LICENSE("GPL"); | ||
36 | |||
37 | /* name of the device, usually in /dev */ | ||
38 | #define UV_MMTIMER_NAME "mmtimer" | ||
39 | #define UV_MMTIMER_DESC "SGI UV Memory Mapped RTC Timer" | ||
40 | #define UV_MMTIMER_VERSION "1.0" | ||
41 | |||
42 | static long uv_mmtimer_ioctl(struct file *file, unsigned int cmd, | ||
43 | unsigned long arg); | ||
44 | static int uv_mmtimer_mmap(struct file *file, struct vm_area_struct *vma); | ||
45 | |||
46 | /* | ||
47 | * Period in femtoseconds (10^-15 s) | ||
48 | */ | ||
49 | static unsigned long uv_mmtimer_femtoperiod; | ||
50 | |||
51 | static const struct file_operations uv_mmtimer_fops = { | ||
52 | .owner = THIS_MODULE, | ||
53 | .mmap = uv_mmtimer_mmap, | ||
54 | .unlocked_ioctl = uv_mmtimer_ioctl, | ||
55 | }; | ||
56 | |||
57 | /** | ||
58 | * uv_mmtimer_ioctl - ioctl interface for /dev/uv_mmtimer | ||
59 | * @file: file structure for the device | ||
60 | * @cmd: command to execute | ||
61 | * @arg: optional argument to command | ||
62 | * | ||
63 | * Executes the command specified by @cmd. Returns 0 for success, < 0 for | ||
64 | * failure. | ||
65 | * | ||
66 | * Valid commands: | ||
67 | * | ||
68 | * %MMTIMER_GETOFFSET - Should return the offset (relative to the start | ||
69 | * of the page where the registers are mapped) for the counter in question. | ||
70 | * | ||
71 | * %MMTIMER_GETRES - Returns the resolution of the clock in femto (10^-15) | ||
72 | * seconds | ||
73 | * | ||
74 | * %MMTIMER_GETFREQ - Copies the frequency of the clock in Hz to the address | ||
75 | * specified by @arg | ||
76 | * | ||
77 | * %MMTIMER_GETBITS - Returns the number of bits in the clock's counter | ||
78 | * | ||
79 | * %MMTIMER_MMAPAVAIL - Returns 1 if registers can be mmap'd into userspace | ||
80 | * | ||
81 | * %MMTIMER_GETCOUNTER - Gets the current value in the counter and places it | ||
82 | * in the address specified by @arg. | ||
83 | */ | ||
84 | static long uv_mmtimer_ioctl(struct file *file, unsigned int cmd, | ||
85 | unsigned long arg) | ||
86 | { | ||
87 | int ret = 0; | ||
88 | |||
89 | switch (cmd) { | ||
90 | case MMTIMER_GETOFFSET: /* offset of the counter */ | ||
91 | /* | ||
92 | * UV RTC register is on its own page | ||
93 | */ | ||
94 | if (PAGE_SIZE <= (1 << 16)) | ||
95 | ret = ((UV_LOCAL_MMR_BASE | UVH_RTC) & (PAGE_SIZE-1)) | ||
96 | / 8; | ||
97 | else | ||
98 | ret = -ENOSYS; | ||
99 | break; | ||
100 | |||
101 | case MMTIMER_GETRES: /* resolution of the clock in 10^-15 s */ | ||
102 | if (copy_to_user((unsigned long __user *)arg, | ||
103 | &uv_mmtimer_femtoperiod, sizeof(unsigned long))) | ||
104 | ret = -EFAULT; | ||
105 | break; | ||
106 | |||
107 | case MMTIMER_GETFREQ: /* frequency in Hz */ | ||
108 | if (copy_to_user((unsigned long __user *)arg, | ||
109 | &sn_rtc_cycles_per_second, | ||
110 | sizeof(unsigned long))) | ||
111 | ret = -EFAULT; | ||
112 | break; | ||
113 | |||
114 | case MMTIMER_GETBITS: /* number of bits in the clock */ | ||
115 | ret = hweight64(UVH_RTC_REAL_TIME_CLOCK_MASK); | ||
116 | break; | ||
117 | |||
118 | case MMTIMER_MMAPAVAIL: /* can we mmap the clock into userspace? */ | ||
119 | ret = (PAGE_SIZE <= (1 << 16)) ? 1 : 0; | ||
120 | break; | ||
121 | |||
122 | case MMTIMER_GETCOUNTER: | ||
123 | if (copy_to_user((unsigned long __user *)arg, | ||
124 | (unsigned long *)uv_local_mmr_address(UVH_RTC), | ||
125 | sizeof(unsigned long))) | ||
126 | ret = -EFAULT; | ||
127 | break; | ||
128 | default: | ||
129 | ret = -ENOTTY; | ||
130 | break; | ||
131 | } | ||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * uv_mmtimer_mmap - maps the clock's registers into userspace | ||
137 | * @file: file structure for the device | ||
138 | * @vma: VMA to map the registers into | ||
139 | * | ||
140 | * Calls remap_pfn_range() to map the clock's registers into | ||
141 | * the calling process' address space. | ||
142 | */ | ||
143 | static int uv_mmtimer_mmap(struct file *file, struct vm_area_struct *vma) | ||
144 | { | ||
145 | unsigned long uv_mmtimer_addr; | ||
146 | |||
147 | if (vma->vm_end - vma->vm_start != PAGE_SIZE) | ||
148 | return -EINVAL; | ||
149 | |||
150 | if (vma->vm_flags & VM_WRITE) | ||
151 | return -EPERM; | ||
152 | |||
153 | if (PAGE_SIZE > (1 << 16)) | ||
154 | return -ENOSYS; | ||
155 | |||
156 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||
157 | |||
158 | uv_mmtimer_addr = UV_LOCAL_MMR_BASE | UVH_RTC; | ||
159 | uv_mmtimer_addr &= ~(PAGE_SIZE - 1); | ||
160 | uv_mmtimer_addr &= 0xfffffffffffffffUL; | ||
161 | |||
162 | if (remap_pfn_range(vma, vma->vm_start, uv_mmtimer_addr >> PAGE_SHIFT, | ||
163 | PAGE_SIZE, vma->vm_page_prot)) { | ||
164 | printk(KERN_ERR "remap_pfn_range failed in uv_mmtimer_mmap\n"); | ||
165 | return -EAGAIN; | ||
166 | } | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | static struct miscdevice uv_mmtimer_miscdev = { | ||
172 | MISC_DYNAMIC_MINOR, | ||
173 | UV_MMTIMER_NAME, | ||
174 | &uv_mmtimer_fops | ||
175 | }; | ||
176 | |||
177 | |||
178 | /** | ||
179 | * uv_mmtimer_init - device initialization routine | ||
180 | * | ||
181 | * Does initial setup for the uv_mmtimer device. | ||
182 | */ | ||
183 | static int __init uv_mmtimer_init(void) | ||
184 | { | ||
185 | if (!is_uv_system()) { | ||
186 | printk(KERN_ERR "%s: Hardware unsupported\n", UV_MMTIMER_NAME); | ||
187 | return -1; | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * Sanity check the cycles/sec variable | ||
192 | */ | ||
193 | if (sn_rtc_cycles_per_second < 100000) { | ||
194 | printk(KERN_ERR "%s: unable to determine clock frequency\n", | ||
195 | UV_MMTIMER_NAME); | ||
196 | return -1; | ||
197 | } | ||
198 | |||
199 | uv_mmtimer_femtoperiod = ((unsigned long)1E15 + | ||
200 | sn_rtc_cycles_per_second / 2) / | ||
201 | sn_rtc_cycles_per_second; | ||
202 | |||
203 | if (misc_register(&uv_mmtimer_miscdev)) { | ||
204 | printk(KERN_ERR "%s: failed to register device\n", | ||
205 | UV_MMTIMER_NAME); | ||
206 | return -1; | ||
207 | } | ||
208 | |||
209 | printk(KERN_INFO "%s: v%s, %ld MHz\n", UV_MMTIMER_DESC, | ||
210 | UV_MMTIMER_VERSION, | ||
211 | sn_rtc_cycles_per_second/(unsigned long)1E6); | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | module_init(uv_mmtimer_init); | ||