diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/Kconfig | 10 | ||||
-rw-r--r-- | drivers/char/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/hpet.c | 4 | ||||
-rw-r--r-- | drivers/char/mem.c | 39 | ||||
-rw-r--r-- | drivers/char/mspec.c | 421 | ||||
-rw-r--r-- | drivers/char/watchdog/Kconfig | 8 | ||||
-rw-r--r-- | drivers/char/watchdog/shwdt.c | 110 |
7 files changed, 555 insertions, 38 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 52ea94b891f5..1b21c3a911d9 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig | |||
@@ -439,6 +439,14 @@ config SGI_MBCS | |||
439 | If you have an SGI Altix with an attached SABrick | 439 | If you have an SGI Altix with an attached SABrick |
440 | say Y or M here, otherwise say N. | 440 | say Y or M here, otherwise say N. |
441 | 441 | ||
442 | config MSPEC | ||
443 | tristate "Memory special operations driver" | ||
444 | depends on IA64 | ||
445 | help | ||
446 | If you have an ia64 and you want to enable memory special | ||
447 | operations support (formerly known as fetchop), say Y here, | ||
448 | otherwise say N. | ||
449 | |||
442 | source "drivers/serial/Kconfig" | 450 | source "drivers/serial/Kconfig" |
443 | 451 | ||
444 | config UNIX98_PTYS | 452 | config UNIX98_PTYS |
@@ -739,7 +747,7 @@ config NVRAM | |||
739 | 747 | ||
740 | config RTC | 748 | config RTC |
741 | tristate "Enhanced Real Time Clock Support" | 749 | tristate "Enhanced Real Time Clock Support" |
742 | depends on !PPC && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARM | 750 | depends on !PPC && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARM && !SUPERH |
743 | ---help--- | 751 | ---help--- |
744 | If you say Y here and create a character special file /dev/rtc with | 752 | If you say Y here and create a character special file /dev/rtc with |
745 | major number 10 and minor number 135 using mknod ("man mknod"), you | 753 | major number 10 and minor number 135 using mknod ("man mknod"), you |
diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 8c6dfc621520..b583d0cd9fbe 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile | |||
@@ -47,6 +47,7 @@ obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o | |||
47 | obj-$(CONFIG_HVC_DRIVER) += hvc_console.o | 47 | obj-$(CONFIG_HVC_DRIVER) += hvc_console.o |
48 | obj-$(CONFIG_RAW_DRIVER) += raw.o | 48 | obj-$(CONFIG_RAW_DRIVER) += raw.o |
49 | obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o | 49 | obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o |
50 | obj-$(CONFIG_MSPEC) += mspec.o | ||
50 | obj-$(CONFIG_MMTIMER) += mmtimer.o | 51 | obj-$(CONFIG_MMTIMER) += mmtimer.o |
51 | obj-$(CONFIG_VIOCONS) += viocons.o | 52 | obj-$(CONFIG_VIOCONS) += viocons.o |
52 | obj-$(CONFIG_VIOTAPE) += viotape.o | 53 | obj-$(CONFIG_VIOTAPE) += viotape.o |
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 8afba339f05a..58b0eb581114 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c | |||
@@ -868,8 +868,8 @@ int hpet_alloc(struct hpet_data *hdp) | |||
868 | do_div(temp, period); | 868 | do_div(temp, period); |
869 | hpetp->hp_tick_freq = temp; /* ticks per second */ | 869 | hpetp->hp_tick_freq = temp; /* ticks per second */ |
870 | 870 | ||
871 | printk(KERN_INFO "hpet%d: at MMIO 0x%lx (virtual 0x%p), IRQ%s", | 871 | printk(KERN_INFO "hpet%d: at MMIO 0x%lx, IRQ%s", |
872 | hpetp->hp_which, hdp->hd_phys_address, hdp->hd_address, | 872 | hpetp->hp_which, hdp->hd_phys_address, |
873 | hpetp->hp_ntimer > 1 ? "s" : ""); | 873 | hpetp->hp_ntimer > 1 ? "s" : ""); |
874 | for (i = 0; i < hpetp->hp_ntimer; i++) | 874 | for (i = 0; i < hpetp->hp_ntimer; i++) |
875 | printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); | 875 | printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); |
diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 917b20402664..4ac70ec697f0 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c | |||
@@ -238,6 +238,32 @@ static pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, | |||
238 | } | 238 | } |
239 | #endif | 239 | #endif |
240 | 240 | ||
241 | #ifndef CONFIG_MMU | ||
242 | static unsigned long get_unmapped_area_mem(struct file *file, | ||
243 | unsigned long addr, | ||
244 | unsigned long len, | ||
245 | unsigned long pgoff, | ||
246 | unsigned long flags) | ||
247 | { | ||
248 | if (!valid_mmap_phys_addr_range(pgoff, len)) | ||
249 | return (unsigned long) -EINVAL; | ||
250 | return pgoff; | ||
251 | } | ||
252 | |||
253 | /* can't do an in-place private mapping if there's no MMU */ | ||
254 | static inline int private_mapping_ok(struct vm_area_struct *vma) | ||
255 | { | ||
256 | return vma->vm_flags & VM_MAYSHARE; | ||
257 | } | ||
258 | #else | ||
259 | #define get_unmapped_area_mem NULL | ||
260 | |||
261 | static inline int private_mapping_ok(struct vm_area_struct *vma) | ||
262 | { | ||
263 | return 1; | ||
264 | } | ||
265 | #endif | ||
266 | |||
241 | static int mmap_mem(struct file * file, struct vm_area_struct * vma) | 267 | static int mmap_mem(struct file * file, struct vm_area_struct * vma) |
242 | { | 268 | { |
243 | size_t size = vma->vm_end - vma->vm_start; | 269 | size_t size = vma->vm_end - vma->vm_start; |
@@ -245,6 +271,9 @@ static int mmap_mem(struct file * file, struct vm_area_struct * vma) | |||
245 | if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) | 271 | if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) |
246 | return -EINVAL; | 272 | return -EINVAL; |
247 | 273 | ||
274 | if (!private_mapping_ok(vma)) | ||
275 | return -ENOSYS; | ||
276 | |||
248 | vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff, | 277 | vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff, |
249 | size, | 278 | size, |
250 | vma->vm_page_prot); | 279 | vma->vm_page_prot); |
@@ -782,6 +811,7 @@ static const struct file_operations mem_fops = { | |||
782 | .write = write_mem, | 811 | .write = write_mem, |
783 | .mmap = mmap_mem, | 812 | .mmap = mmap_mem, |
784 | .open = open_mem, | 813 | .open = open_mem, |
814 | .get_unmapped_area = get_unmapped_area_mem, | ||
785 | }; | 815 | }; |
786 | 816 | ||
787 | static const struct file_operations kmem_fops = { | 817 | static const struct file_operations kmem_fops = { |
@@ -790,6 +820,7 @@ static const struct file_operations kmem_fops = { | |||
790 | .write = write_kmem, | 820 | .write = write_kmem, |
791 | .mmap = mmap_kmem, | 821 | .mmap = mmap_kmem, |
792 | .open = open_kmem, | 822 | .open = open_kmem, |
823 | .get_unmapped_area = get_unmapped_area_mem, | ||
793 | }; | 824 | }; |
794 | 825 | ||
795 | static const struct file_operations null_fops = { | 826 | static const struct file_operations null_fops = { |
@@ -815,6 +846,10 @@ static const struct file_operations zero_fops = { | |||
815 | .mmap = mmap_zero, | 846 | .mmap = mmap_zero, |
816 | }; | 847 | }; |
817 | 848 | ||
849 | /* | ||
850 | * capabilities for /dev/zero | ||
851 | * - permits private mappings, "copies" are taken of the source of zeros | ||
852 | */ | ||
818 | static struct backing_dev_info zero_bdi = { | 853 | static struct backing_dev_info zero_bdi = { |
819 | .capabilities = BDI_CAP_MAP_COPY, | 854 | .capabilities = BDI_CAP_MAP_COPY, |
820 | }; | 855 | }; |
@@ -862,9 +897,13 @@ static int memory_open(struct inode * inode, struct file * filp) | |||
862 | switch (iminor(inode)) { | 897 | switch (iminor(inode)) { |
863 | case 1: | 898 | case 1: |
864 | filp->f_op = &mem_fops; | 899 | filp->f_op = &mem_fops; |
900 | filp->f_mapping->backing_dev_info = | ||
901 | &directly_mappable_cdev_bdi; | ||
865 | break; | 902 | break; |
866 | case 2: | 903 | case 2: |
867 | filp->f_op = &kmem_fops; | 904 | filp->f_op = &kmem_fops; |
905 | filp->f_mapping->backing_dev_info = | ||
906 | &directly_mappable_cdev_bdi; | ||
868 | break; | 907 | break; |
869 | case 3: | 908 | case 3: |
870 | filp->f_op = &null_fops; | 909 | filp->f_op = &null_fops; |
diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c new file mode 100644 index 000000000000..5426b1e5595f --- /dev/null +++ b/drivers/char/mspec.c | |||
@@ -0,0 +1,421 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001-2006 Silicon Graphics, Inc. All rights | ||
3 | * reserved. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of version 2 of the GNU General Public License | ||
7 | * as published by the Free Software Foundation. | ||
8 | */ | ||
9 | |||
10 | /* | ||
11 | * SN Platform Special Memory (mspec) Support | ||
12 | * | ||
13 | * This driver exports the SN special memory (mspec) facility to user | ||
14 | * processes. | ||
15 | * There are three types of memory made available thru this driver: | ||
16 | * fetchops, uncached and cached. | ||
17 | * | ||
18 | * Fetchops are atomic memory operations that are implemented in the | ||
19 | * memory controller on SGI SN hardware. | ||
20 | * | ||
21 | * Uncached are used for memory write combining feature of the ia64 | ||
22 | * cpu. | ||
23 | * | ||
24 | * Cached are used for areas of memory that are used as cached addresses | ||
25 | * on our partition and used as uncached addresses from other partitions. | ||
26 | * Due to a design constraint of the SN2 Shub, you can not have processors | ||
27 | * on the same FSB perform both a cached and uncached reference to the | ||
28 | * same cache line. These special memory cached regions prevent the | ||
29 | * kernel from ever dropping in a TLB entry and therefore prevent the | ||
30 | * processor from ever speculating a cache line from this page. | ||
31 | */ | ||
32 | |||
33 | #include <linux/config.h> | ||
34 | #include <linux/types.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/module.h> | ||
37 | #include <linux/init.h> | ||
38 | #include <linux/errno.h> | ||
39 | #include <linux/miscdevice.h> | ||
40 | #include <linux/spinlock.h> | ||
41 | #include <linux/mm.h> | ||
42 | #include <linux/vmalloc.h> | ||
43 | #include <linux/string.h> | ||
44 | #include <linux/slab.h> | ||
45 | #include <linux/numa.h> | ||
46 | #include <asm/page.h> | ||
47 | #include <asm/system.h> | ||
48 | #include <asm/pgtable.h> | ||
49 | #include <asm/atomic.h> | ||
50 | #include <asm/tlbflush.h> | ||
51 | #include <asm/uncached.h> | ||
52 | #include <asm/sn/addrs.h> | ||
53 | #include <asm/sn/arch.h> | ||
54 | #include <asm/sn/mspec.h> | ||
55 | #include <asm/sn/sn_cpuid.h> | ||
56 | #include <asm/sn/io.h> | ||
57 | #include <asm/sn/bte.h> | ||
58 | #include <asm/sn/shubio.h> | ||
59 | |||
60 | |||
61 | #define FETCHOP_ID "SGI Fetchop," | ||
62 | #define CACHED_ID "Cached," | ||
63 | #define UNCACHED_ID "Uncached" | ||
64 | #define REVISION "4.0" | ||
65 | #define MSPEC_BASENAME "mspec" | ||
66 | |||
67 | /* | ||
68 | * Page types allocated by the device. | ||
69 | */ | ||
70 | enum { | ||
71 | MSPEC_FETCHOP = 1, | ||
72 | MSPEC_CACHED, | ||
73 | MSPEC_UNCACHED | ||
74 | }; | ||
75 | |||
76 | static int is_sn2; | ||
77 | |||
78 | /* | ||
79 | * One of these structures is allocated when an mspec region is mmaped. The | ||
80 | * structure is pointed to by the vma->vm_private_data field in the vma struct. | ||
81 | * This structure is used to record the addresses of the mspec pages. | ||
82 | */ | ||
83 | struct vma_data { | ||
84 | atomic_t refcnt; /* Number of vmas sharing the data. */ | ||
85 | spinlock_t lock; /* Serialize access to the vma. */ | ||
86 | int count; /* Number of pages allocated. */ | ||
87 | int type; /* Type of pages allocated. */ | ||
88 | unsigned long maddr[0]; /* Array of MSPEC addresses. */ | ||
89 | }; | ||
90 | |||
91 | /* used on shub2 to clear FOP cache in the HUB */ | ||
92 | static unsigned long scratch_page[MAX_NUMNODES]; | ||
93 | #define SH2_AMO_CACHE_ENTRIES 4 | ||
94 | |||
95 | static inline int | ||
96 | mspec_zero_block(unsigned long addr, int len) | ||
97 | { | ||
98 | int status; | ||
99 | |||
100 | if (is_sn2) { | ||
101 | if (is_shub2()) { | ||
102 | int nid; | ||
103 | void *p; | ||
104 | int i; | ||
105 | |||
106 | nid = nasid_to_cnodeid(get_node_number(__pa(addr))); | ||
107 | p = (void *)TO_AMO(scratch_page[nid]); | ||
108 | |||
109 | for (i=0; i < SH2_AMO_CACHE_ENTRIES; i++) { | ||
110 | FETCHOP_LOAD_OP(p, FETCHOP_LOAD); | ||
111 | p += FETCHOP_VAR_SIZE; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | status = bte_copy(0, addr & ~__IA64_UNCACHED_OFFSET, len, | ||
116 | BTE_WACQUIRE | BTE_ZERO_FILL, NULL); | ||
117 | } else { | ||
118 | memset((char *) addr, 0, len); | ||
119 | status = 0; | ||
120 | } | ||
121 | return status; | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * mspec_open | ||
126 | * | ||
127 | * Called when a device mapping is created by a means other than mmap | ||
128 | * (via fork, etc.). Increments the reference count on the underlying | ||
129 | * mspec data so it is not freed prematurely. | ||
130 | */ | ||
131 | static void | ||
132 | mspec_open(struct vm_area_struct *vma) | ||
133 | { | ||
134 | struct vma_data *vdata; | ||
135 | |||
136 | vdata = vma->vm_private_data; | ||
137 | atomic_inc(&vdata->refcnt); | ||
138 | } | ||
139 | |||
140 | /* | ||
141 | * mspec_close | ||
142 | * | ||
143 | * Called when unmapping a device mapping. Frees all mspec pages | ||
144 | * belonging to the vma. | ||
145 | */ | ||
146 | static void | ||
147 | mspec_close(struct vm_area_struct *vma) | ||
148 | { | ||
149 | struct vma_data *vdata; | ||
150 | int i, pages, result, vdata_size; | ||
151 | |||
152 | vdata = vma->vm_private_data; | ||
153 | if (!atomic_dec_and_test(&vdata->refcnt)) | ||
154 | return; | ||
155 | |||
156 | pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; | ||
157 | vdata_size = sizeof(struct vma_data) + pages * sizeof(long); | ||
158 | for (i = 0; i < pages; i++) { | ||
159 | if (vdata->maddr[i] == 0) | ||
160 | continue; | ||
161 | /* | ||
162 | * Clear the page before sticking it back | ||
163 | * into the pool. | ||
164 | */ | ||
165 | result = mspec_zero_block(vdata->maddr[i], PAGE_SIZE); | ||
166 | if (!result) | ||
167 | uncached_free_page(vdata->maddr[i]); | ||
168 | else | ||
169 | printk(KERN_WARNING "mspec_close(): " | ||
170 | "failed to zero page %i\n", | ||
171 | result); | ||
172 | } | ||
173 | |||
174 | if (vdata_size <= PAGE_SIZE) | ||
175 | kfree(vdata); | ||
176 | else | ||
177 | vfree(vdata); | ||
178 | } | ||
179 | |||
180 | |||
181 | /* | ||
182 | * mspec_nopfn | ||
183 | * | ||
184 | * Creates a mspec page and maps it to user space. | ||
185 | */ | ||
186 | static unsigned long | ||
187 | mspec_nopfn(struct vm_area_struct *vma, unsigned long address) | ||
188 | { | ||
189 | unsigned long paddr, maddr; | ||
190 | unsigned long pfn; | ||
191 | int index; | ||
192 | struct vma_data *vdata = vma->vm_private_data; | ||
193 | |||
194 | index = (address - vma->vm_start) >> PAGE_SHIFT; | ||
195 | maddr = (volatile unsigned long) vdata->maddr[index]; | ||
196 | if (maddr == 0) { | ||
197 | maddr = uncached_alloc_page(numa_node_id()); | ||
198 | if (maddr == 0) | ||
199 | return NOPFN_OOM; | ||
200 | |||
201 | spin_lock(&vdata->lock); | ||
202 | if (vdata->maddr[index] == 0) { | ||
203 | vdata->count++; | ||
204 | vdata->maddr[index] = maddr; | ||
205 | } else { | ||
206 | uncached_free_page(maddr); | ||
207 | maddr = vdata->maddr[index]; | ||
208 | } | ||
209 | spin_unlock(&vdata->lock); | ||
210 | } | ||
211 | |||
212 | if (vdata->type == MSPEC_FETCHOP) | ||
213 | paddr = TO_AMO(maddr); | ||
214 | else | ||
215 | paddr = __pa(TO_CAC(maddr)); | ||
216 | |||
217 | pfn = paddr >> PAGE_SHIFT; | ||
218 | |||
219 | return pfn; | ||
220 | } | ||
221 | |||
222 | static struct vm_operations_struct mspec_vm_ops = { | ||
223 | .open = mspec_open, | ||
224 | .close = mspec_close, | ||
225 | .nopfn = mspec_nopfn | ||
226 | }; | ||
227 | |||
228 | /* | ||
229 | * mspec_mmap | ||
230 | * | ||
231 | * Called when mmaping the device. Initializes the vma with a fault handler | ||
232 | * and private data structure necessary to allocate, track, and free the | ||
233 | * underlying pages. | ||
234 | */ | ||
235 | static int | ||
236 | mspec_mmap(struct file *file, struct vm_area_struct *vma, int type) | ||
237 | { | ||
238 | struct vma_data *vdata; | ||
239 | int pages, vdata_size; | ||
240 | |||
241 | if (vma->vm_pgoff != 0) | ||
242 | return -EINVAL; | ||
243 | |||
244 | if ((vma->vm_flags & VM_SHARED) == 0) | ||
245 | return -EINVAL; | ||
246 | |||
247 | if ((vma->vm_flags & VM_WRITE) == 0) | ||
248 | return -EPERM; | ||
249 | |||
250 | pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; | ||
251 | vdata_size = sizeof(struct vma_data) + pages * sizeof(long); | ||
252 | if (vdata_size <= PAGE_SIZE) | ||
253 | vdata = kmalloc(vdata_size, GFP_KERNEL); | ||
254 | else | ||
255 | vdata = vmalloc(vdata_size); | ||
256 | if (!vdata) | ||
257 | return -ENOMEM; | ||
258 | memset(vdata, 0, vdata_size); | ||
259 | |||
260 | vdata->type = type; | ||
261 | spin_lock_init(&vdata->lock); | ||
262 | vdata->refcnt = ATOMIC_INIT(1); | ||
263 | vma->vm_private_data = vdata; | ||
264 | |||
265 | vma->vm_flags |= (VM_IO | VM_LOCKED | VM_RESERVED | VM_PFNMAP); | ||
266 | if (vdata->type == MSPEC_FETCHOP || vdata->type == MSPEC_UNCACHED) | ||
267 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||
268 | vma->vm_ops = &mspec_vm_ops; | ||
269 | |||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static int | ||
274 | fetchop_mmap(struct file *file, struct vm_area_struct *vma) | ||
275 | { | ||
276 | return mspec_mmap(file, vma, MSPEC_FETCHOP); | ||
277 | } | ||
278 | |||
279 | static int | ||
280 | cached_mmap(struct file *file, struct vm_area_struct *vma) | ||
281 | { | ||
282 | return mspec_mmap(file, vma, MSPEC_CACHED); | ||
283 | } | ||
284 | |||
285 | static int | ||
286 | uncached_mmap(struct file *file, struct vm_area_struct *vma) | ||
287 | { | ||
288 | return mspec_mmap(file, vma, MSPEC_UNCACHED); | ||
289 | } | ||
290 | |||
291 | static struct file_operations fetchop_fops = { | ||
292 | .owner = THIS_MODULE, | ||
293 | .mmap = fetchop_mmap | ||
294 | }; | ||
295 | |||
296 | static struct miscdevice fetchop_miscdev = { | ||
297 | .minor = MISC_DYNAMIC_MINOR, | ||
298 | .name = "sgi_fetchop", | ||
299 | .fops = &fetchop_fops | ||
300 | }; | ||
301 | |||
302 | static struct file_operations cached_fops = { | ||
303 | .owner = THIS_MODULE, | ||
304 | .mmap = cached_mmap | ||
305 | }; | ||
306 | |||
307 | static struct miscdevice cached_miscdev = { | ||
308 | .minor = MISC_DYNAMIC_MINOR, | ||
309 | .name = "mspec_cached", | ||
310 | .fops = &cached_fops | ||
311 | }; | ||
312 | |||
313 | static struct file_operations uncached_fops = { | ||
314 | .owner = THIS_MODULE, | ||
315 | .mmap = uncached_mmap | ||
316 | }; | ||
317 | |||
318 | static struct miscdevice uncached_miscdev = { | ||
319 | .minor = MISC_DYNAMIC_MINOR, | ||
320 | .name = "mspec_uncached", | ||
321 | .fops = &uncached_fops | ||
322 | }; | ||
323 | |||
324 | /* | ||
325 | * mspec_init | ||
326 | * | ||
327 | * Called at boot time to initialize the mspec facility. | ||
328 | */ | ||
329 | static int __init | ||
330 | mspec_init(void) | ||
331 | { | ||
332 | int ret; | ||
333 | int nid; | ||
334 | |||
335 | /* | ||
336 | * The fetchop device only works on SN2 hardware, uncached and cached | ||
337 | * memory drivers should both be valid on all ia64 hardware | ||
338 | */ | ||
339 | if (ia64_platform_is("sn2")) { | ||
340 | is_sn2 = 1; | ||
341 | if (is_shub2()) { | ||
342 | ret = -ENOMEM; | ||
343 | for_each_online_node(nid) { | ||
344 | int actual_nid; | ||
345 | int nasid; | ||
346 | unsigned long phys; | ||
347 | |||
348 | scratch_page[nid] = uncached_alloc_page(nid); | ||
349 | if (scratch_page[nid] == 0) | ||
350 | goto free_scratch_pages; | ||
351 | phys = __pa(scratch_page[nid]); | ||
352 | nasid = get_node_number(phys); | ||
353 | actual_nid = nasid_to_cnodeid(nasid); | ||
354 | if (actual_nid != nid) | ||
355 | goto free_scratch_pages; | ||
356 | } | ||
357 | } | ||
358 | |||
359 | ret = misc_register(&fetchop_miscdev); | ||
360 | if (ret) { | ||
361 | printk(KERN_ERR | ||
362 | "%s: failed to register device %i\n", | ||
363 | FETCHOP_ID, ret); | ||
364 | goto free_scratch_pages; | ||
365 | } | ||
366 | } | ||
367 | ret = misc_register(&cached_miscdev); | ||
368 | if (ret) { | ||
369 | printk(KERN_ERR "%s: failed to register device %i\n", | ||
370 | CACHED_ID, ret); | ||
371 | if (is_sn2) | ||
372 | misc_deregister(&fetchop_miscdev); | ||
373 | goto free_scratch_pages; | ||
374 | } | ||
375 | ret = misc_register(&uncached_miscdev); | ||
376 | if (ret) { | ||
377 | printk(KERN_ERR "%s: failed to register device %i\n", | ||
378 | UNCACHED_ID, ret); | ||
379 | misc_deregister(&cached_miscdev); | ||
380 | if (is_sn2) | ||
381 | misc_deregister(&fetchop_miscdev); | ||
382 | goto free_scratch_pages; | ||
383 | } | ||
384 | |||
385 | printk(KERN_INFO "%s %s initialized devices: %s %s %s\n", | ||
386 | MSPEC_BASENAME, REVISION, is_sn2 ? FETCHOP_ID : "", | ||
387 | CACHED_ID, UNCACHED_ID); | ||
388 | |||
389 | return 0; | ||
390 | |||
391 | free_scratch_pages: | ||
392 | for_each_node(nid) { | ||
393 | if (scratch_page[nid] != 0) | ||
394 | uncached_free_page(scratch_page[nid]); | ||
395 | } | ||
396 | return ret; | ||
397 | } | ||
398 | |||
399 | static void __exit | ||
400 | mspec_exit(void) | ||
401 | { | ||
402 | int nid; | ||
403 | |||
404 | misc_deregister(&uncached_miscdev); | ||
405 | misc_deregister(&cached_miscdev); | ||
406 | if (is_sn2) { | ||
407 | misc_deregister(&fetchop_miscdev); | ||
408 | |||
409 | for_each_node(nid) { | ||
410 | if (scratch_page[nid] != 0) | ||
411 | uncached_free_page(scratch_page[nid]); | ||
412 | } | ||
413 | } | ||
414 | } | ||
415 | |||
416 | module_init(mspec_init); | ||
417 | module_exit(mspec_exit); | ||
418 | |||
419 | MODULE_AUTHOR("Silicon Graphics, Inc. <linux-altix@sgi.com>"); | ||
420 | MODULE_DESCRIPTION("Driver for SGI SN special memory operations"); | ||
421 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index fff89c2d88fd..f114d7b5bb2a 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig | |||
@@ -510,6 +510,14 @@ config SH_WDT | |||
510 | To compile this driver as a module, choose M here: the | 510 | To compile this driver as a module, choose M here: the |
511 | module will be called shwdt. | 511 | module will be called shwdt. |
512 | 512 | ||
513 | config SH_WDT_MMAP | ||
514 | bool "Allow mmap of SH WDT" | ||
515 | default n | ||
516 | depends on SH_WDT | ||
517 | help | ||
518 | If you say Y here, user applications will be able to mmap the | ||
519 | WDT/CPG registers. | ||
520 | # | ||
513 | # SPARC64 Architecture | 521 | # SPARC64 Architecture |
514 | 522 | ||
515 | config WATCHDOG_CP1XXX | 523 | config WATCHDOG_CP1XXX |
diff --git a/drivers/char/watchdog/shwdt.c b/drivers/char/watchdog/shwdt.c index 1355038f1044..e5b8c64f1d65 100644 --- a/drivers/char/watchdog/shwdt.c +++ b/drivers/char/watchdog/shwdt.c | |||
@@ -27,7 +27,7 @@ | |||
27 | #include <linux/notifier.h> | 27 | #include <linux/notifier.h> |
28 | #include <linux/ioport.h> | 28 | #include <linux/ioport.h> |
29 | #include <linux/fs.h> | 29 | #include <linux/fs.h> |
30 | 30 | #include <linux/mm.h> | |
31 | #include <asm/io.h> | 31 | #include <asm/io.h> |
32 | #include <asm/uaccess.h> | 32 | #include <asm/uaccess.h> |
33 | #include <asm/watchdog.h> | 33 | #include <asm/watchdog.h> |
@@ -125,7 +125,6 @@ static void sh_wdt_start(void) | |||
125 | 125 | ||
126 | /** | 126 | /** |
127 | * sh_wdt_stop - Stop the Watchdog | 127 | * sh_wdt_stop - Stop the Watchdog |
128 | * | ||
129 | * Stops the watchdog. | 128 | * Stops the watchdog. |
130 | */ | 129 | */ |
131 | static void sh_wdt_stop(void) | 130 | static void sh_wdt_stop(void) |
@@ -141,22 +140,20 @@ static void sh_wdt_stop(void) | |||
141 | 140 | ||
142 | /** | 141 | /** |
143 | * sh_wdt_keepalive - Keep the Userspace Watchdog Alive | 142 | * sh_wdt_keepalive - Keep the Userspace Watchdog Alive |
144 | * | ||
145 | * The Userspace watchdog got a KeepAlive: schedule the next heartbeat. | 143 | * The Userspace watchdog got a KeepAlive: schedule the next heartbeat. |
146 | */ | 144 | */ |
147 | static void sh_wdt_keepalive(void) | 145 | static inline void sh_wdt_keepalive(void) |
148 | { | 146 | { |
149 | next_heartbeat = jiffies + (heartbeat * HZ); | 147 | next_heartbeat = jiffies + (heartbeat * HZ); |
150 | } | 148 | } |
151 | 149 | ||
152 | /** | 150 | /** |
153 | * sh_wdt_set_heartbeat - Set the Userspace Watchdog heartbeat | 151 | * sh_wdt_set_heartbeat - Set the Userspace Watchdog heartbeat |
154 | * | ||
155 | * Set the Userspace Watchdog heartbeat | 152 | * Set the Userspace Watchdog heartbeat |
156 | */ | 153 | */ |
157 | static int sh_wdt_set_heartbeat(int t) | 154 | static int sh_wdt_set_heartbeat(int t) |
158 | { | 155 | { |
159 | if ((t < 1) || (t > 3600)) /* arbitrary upper limit */ | 156 | if (unlikely((t < 1) || (t > 3600))) /* arbitrary upper limit */ |
160 | return -EINVAL; | 157 | return -EINVAL; |
161 | 158 | ||
162 | heartbeat = t; | 159 | heartbeat = t; |
@@ -165,7 +162,6 @@ static int sh_wdt_set_heartbeat(int t) | |||
165 | 162 | ||
166 | /** | 163 | /** |
167 | * sh_wdt_ping - Ping the Watchdog | 164 | * sh_wdt_ping - Ping the Watchdog |
168 | * | ||
169 | * @data: Unused | 165 | * @data: Unused |
170 | * | 166 | * |
171 | * Clears overflow bit, resets timer counter. | 167 | * Clears overflow bit, resets timer counter. |
@@ -182,14 +178,13 @@ static void sh_wdt_ping(unsigned long data) | |||
182 | sh_wdt_write_cnt(0); | 178 | sh_wdt_write_cnt(0); |
183 | 179 | ||
184 | mod_timer(&timer, next_ping_period(clock_division_ratio)); | 180 | mod_timer(&timer, next_ping_period(clock_division_ratio)); |
185 | } else { | 181 | } else |
186 | printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); | 182 | printk(KERN_WARNING PFX "Heartbeat lost! Will not ping " |
187 | } | 183 | "the watchdog\n"); |
188 | } | 184 | } |
189 | 185 | ||
190 | /** | 186 | /** |
191 | * sh_wdt_open - Open the Device | 187 | * sh_wdt_open - Open the Device |
192 | * | ||
193 | * @inode: inode of device | 188 | * @inode: inode of device |
194 | * @file: file handle of device | 189 | * @file: file handle of device |
195 | * | 190 | * |
@@ -209,7 +204,6 @@ static int sh_wdt_open(struct inode *inode, struct file *file) | |||
209 | 204 | ||
210 | /** | 205 | /** |
211 | * sh_wdt_close - Close the Device | 206 | * sh_wdt_close - Close the Device |
212 | * | ||
213 | * @inode: inode of device | 207 | * @inode: inode of device |
214 | * @file: file handle of device | 208 | * @file: file handle of device |
215 | * | 209 | * |
@@ -220,7 +214,8 @@ static int sh_wdt_close(struct inode *inode, struct file *file) | |||
220 | if (shwdt_expect_close == 42) { | 214 | if (shwdt_expect_close == 42) { |
221 | sh_wdt_stop(); | 215 | sh_wdt_stop(); |
222 | } else { | 216 | } else { |
223 | printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); | 217 | printk(KERN_CRIT PFX "Unexpected close, not " |
218 | "stopping watchdog!\n"); | ||
224 | sh_wdt_keepalive(); | 219 | sh_wdt_keepalive(); |
225 | } | 220 | } |
226 | 221 | ||
@@ -232,7 +227,6 @@ static int sh_wdt_close(struct inode *inode, struct file *file) | |||
232 | 227 | ||
233 | /** | 228 | /** |
234 | * sh_wdt_write - Write to Device | 229 | * sh_wdt_write - Write to Device |
235 | * | ||
236 | * @file: file handle of device | 230 | * @file: file handle of device |
237 | * @buf: buffer to write | 231 | * @buf: buffer to write |
238 | * @count: length of buffer | 232 | * @count: length of buffer |
@@ -264,8 +258,56 @@ static ssize_t sh_wdt_write(struct file *file, const char *buf, | |||
264 | } | 258 | } |
265 | 259 | ||
266 | /** | 260 | /** |
267 | * sh_wdt_ioctl - Query Device | 261 | * sh_wdt_mmap - map WDT/CPG registers into userspace |
262 | * @file: file structure for the device | ||
263 | * @vma: VMA to map the registers into | ||
264 | * | ||
265 | * A simple mmap() implementation for the corner cases where the counter | ||
266 | * needs to be mapped in userspace directly. Due to the relatively small | ||
267 | * size of the area, neighbouring registers not necessarily tied to the | ||
268 | * CPG will also be accessible through the register page, so this remains | ||
269 | * configurable for users that really know what they're doing. | ||
268 | * | 270 | * |
271 | * Additionaly, the register page maps in the CPG register base relative | ||
272 | * to the nearest page-aligned boundary, which requires that userspace do | ||
273 | * the appropriate CPU subtype math for calculating the page offset for | ||
274 | * the counter value. | ||
275 | */ | ||
276 | static int sh_wdt_mmap(struct file *file, struct vm_area_struct *vma) | ||
277 | { | ||
278 | int ret = -ENOSYS; | ||
279 | |||
280 | #ifdef CONFIG_SH_WDT_MMAP | ||
281 | unsigned long addr; | ||
282 | |||
283 | /* Only support the simple cases where we map in a register page. */ | ||
284 | if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff) | ||
285 | return -EINVAL; | ||
286 | |||
287 | /* | ||
288 | * Pick WTCNT as the start, it's usually the first register after the | ||
289 | * FRQCR, and neither one are generally page-aligned out of the box. | ||
290 | */ | ||
291 | addr = WTCNT & ~(PAGE_SIZE - 1); | ||
292 | |||
293 | vma->vm_flags |= VM_IO; | ||
294 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||
295 | |||
296 | if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, | ||
297 | PAGE_SIZE, vma->vm_page_prot)) { | ||
298 | printk(KERN_ERR PFX "%s: io_remap_pfn_range failed\n", | ||
299 | __FUNCTION__); | ||
300 | return -EAGAIN; | ||
301 | } | ||
302 | |||
303 | ret = 0; | ||
304 | #endif | ||
305 | |||
306 | return ret; | ||
307 | } | ||
308 | |||
309 | /** | ||
310 | * sh_wdt_ioctl - Query Device | ||
269 | * @inode: inode of device | 311 | * @inode: inode of device |
270 | * @file: file handle of device | 312 | * @file: file handle of device |
271 | * @cmd: watchdog command | 313 | * @cmd: watchdog command |
@@ -326,7 +368,6 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file, | |||
326 | 368 | ||
327 | /** | 369 | /** |
328 | * sh_wdt_notify_sys - Notifier Handler | 370 | * sh_wdt_notify_sys - Notifier Handler |
329 | * | ||
330 | * @this: notifier block | 371 | * @this: notifier block |
331 | * @code: notifier event | 372 | * @code: notifier event |
332 | * @unused: unused | 373 | * @unused: unused |
@@ -337,9 +378,8 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file, | |||
337 | static int sh_wdt_notify_sys(struct notifier_block *this, | 378 | static int sh_wdt_notify_sys(struct notifier_block *this, |
338 | unsigned long code, void *unused) | 379 | unsigned long code, void *unused) |
339 | { | 380 | { |
340 | if (code == SYS_DOWN || code == SYS_HALT) { | 381 | if (code == SYS_DOWN || code == SYS_HALT) |
341 | sh_wdt_stop(); | 382 | sh_wdt_stop(); |
342 | } | ||
343 | 383 | ||
344 | return NOTIFY_DONE; | 384 | return NOTIFY_DONE; |
345 | } | 385 | } |
@@ -351,10 +391,12 @@ static const struct file_operations sh_wdt_fops = { | |||
351 | .ioctl = sh_wdt_ioctl, | 391 | .ioctl = sh_wdt_ioctl, |
352 | .open = sh_wdt_open, | 392 | .open = sh_wdt_open, |
353 | .release = sh_wdt_close, | 393 | .release = sh_wdt_close, |
394 | .mmap = sh_wdt_mmap, | ||
354 | }; | 395 | }; |
355 | 396 | ||
356 | static struct watchdog_info sh_wdt_info = { | 397 | static struct watchdog_info sh_wdt_info = { |
357 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, | 398 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | |
399 | WDIOF_MAGICCLOSE, | ||
358 | .firmware_version = 1, | 400 | .firmware_version = 1, |
359 | .identity = "SH WDT", | 401 | .identity = "SH WDT", |
360 | }; | 402 | }; |
@@ -371,7 +413,6 @@ static struct miscdevice sh_wdt_miscdev = { | |||
371 | 413 | ||
372 | /** | 414 | /** |
373 | * sh_wdt_init - Initialize module | 415 | * sh_wdt_init - Initialize module |
374 | * | ||
375 | * Registers the device and notifier handler. Actual device | 416 | * Registers the device and notifier handler. Actual device |
376 | * initialization is handled by sh_wdt_open(). | 417 | * initialization is handled by sh_wdt_open(). |
377 | */ | 418 | */ |
@@ -381,15 +422,15 @@ static int __init sh_wdt_init(void) | |||
381 | 422 | ||
382 | if ((clock_division_ratio < 0x5) || (clock_division_ratio > 0x7)) { | 423 | if ((clock_division_ratio < 0x5) || (clock_division_ratio > 0x7)) { |
383 | clock_division_ratio = WTCSR_CKS_4096; | 424 | clock_division_ratio = WTCSR_CKS_4096; |
384 | printk(KERN_INFO PFX "clock_division_ratio value must be 0x5<=x<=0x7, using %d\n", | 425 | printk(KERN_INFO PFX "clock_division_ratio value must " |
385 | clock_division_ratio); | 426 | "be 0x5<=x<=0x7, using %d\n", clock_division_ratio); |
386 | } | 427 | } |
387 | 428 | ||
388 | if (sh_wdt_set_heartbeat(heartbeat)) | 429 | rc = sh_wdt_set_heartbeat(heartbeat); |
389 | { | 430 | if (unlikely(rc)) { |
390 | heartbeat = WATCHDOG_HEARTBEAT; | 431 | heartbeat = WATCHDOG_HEARTBEAT; |
391 | printk(KERN_INFO PFX "heartbeat value must be 1<=x<=3600, using %d\n", | 432 | printk(KERN_INFO PFX "heartbeat value must " |
392 | heartbeat); | 433 | "be 1<=x<=3600, using %d\n", heartbeat); |
393 | } | 434 | } |
394 | 435 | ||
395 | init_timer(&timer); | 436 | init_timer(&timer); |
@@ -397,15 +438,16 @@ static int __init sh_wdt_init(void) | |||
397 | timer.data = 0; | 438 | timer.data = 0; |
398 | 439 | ||
399 | rc = register_reboot_notifier(&sh_wdt_notifier); | 440 | rc = register_reboot_notifier(&sh_wdt_notifier); |
400 | if (rc) { | 441 | if (unlikely(rc)) { |
401 | printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n", rc); | 442 | printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n", |
443 | rc); | ||
402 | return rc; | 444 | return rc; |
403 | } | 445 | } |
404 | 446 | ||
405 | rc = misc_register(&sh_wdt_miscdev); | 447 | rc = misc_register(&sh_wdt_miscdev); |
406 | if (rc) { | 448 | if (unlikely(rc)) { |
407 | printk(KERN_ERR PFX "Can't register miscdev on minor=%d (err=%d)\n", | 449 | printk(KERN_ERR PFX "Can't register miscdev on " |
408 | sh_wdt_miscdev.minor, rc); | 450 | "minor=%d (err=%d)\n", sh_wdt_miscdev.minor, rc); |
409 | unregister_reboot_notifier(&sh_wdt_notifier); | 451 | unregister_reboot_notifier(&sh_wdt_notifier); |
410 | return rc; | 452 | return rc; |
411 | } | 453 | } |
@@ -418,7 +460,6 @@ static int __init sh_wdt_init(void) | |||
418 | 460 | ||
419 | /** | 461 | /** |
420 | * sh_wdt_exit - Deinitialize module | 462 | * sh_wdt_exit - Deinitialize module |
421 | * | ||
422 | * Unregisters the device and notifier handler. Actual device | 463 | * Unregisters the device and notifier handler. Actual device |
423 | * deinitialization is handled by sh_wdt_close(). | 464 | * deinitialization is handled by sh_wdt_close(). |
424 | */ | 465 | */ |
@@ -434,14 +475,13 @@ MODULE_LICENSE("GPL"); | |||
434 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | 475 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |
435 | 476 | ||
436 | module_param(clock_division_ratio, int, 0); | 477 | module_param(clock_division_ratio, int, 0); |
437 | MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). Defaults to 0x7."); | 478 | MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). (default=" __MODULE_STRING(clock_division_ratio) ")"); |
438 | 479 | ||
439 | module_param(heartbeat, int, 0); | 480 | module_param(heartbeat, int, 0); |
440 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); | 481 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); |
441 | 482 | ||
442 | module_param(nowayout, int, 0); | 483 | module_param(nowayout, int, 0); |
443 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); | 484 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
444 | 485 | ||
445 | module_init(sh_wdt_init); | 486 | module_init(sh_wdt_init); |
446 | module_exit(sh_wdt_exit); | 487 | module_exit(sh_wdt_exit); |
447 | |||