aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/Kconfig10
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/hpet.c4
-rw-r--r--drivers/char/mem.c39
-rw-r--r--drivers/char/mspec.c421
-rw-r--r--drivers/char/watchdog/Kconfig8
-rw-r--r--drivers/char/watchdog/shwdt.c110
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
442config 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
442source "drivers/serial/Kconfig" 450source "drivers/serial/Kconfig"
443 451
444config UNIX98_PTYS 452config UNIX98_PTYS
@@ -739,7 +747,7 @@ config NVRAM
739 747
740config RTC 748config 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
47obj-$(CONFIG_HVC_DRIVER) += hvc_console.o 47obj-$(CONFIG_HVC_DRIVER) += hvc_console.o
48obj-$(CONFIG_RAW_DRIVER) += raw.o 48obj-$(CONFIG_RAW_DRIVER) += raw.o
49obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o 49obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o
50obj-$(CONFIG_MSPEC) += mspec.o
50obj-$(CONFIG_MMTIMER) += mmtimer.o 51obj-$(CONFIG_MMTIMER) += mmtimer.o
51obj-$(CONFIG_VIOCONS) += viocons.o 52obj-$(CONFIG_VIOCONS) += viocons.o
52obj-$(CONFIG_VIOTAPE) += viotape.o 53obj-$(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
242static 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 */
254static 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
261static inline int private_mapping_ok(struct vm_area_struct *vma)
262{
263 return 1;
264}
265#endif
266
241static int mmap_mem(struct file * file, struct vm_area_struct * vma) 267static 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
787static const struct file_operations kmem_fops = { 817static 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
795static const struct file_operations null_fops = { 826static 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 */
818static struct backing_dev_info zero_bdi = { 853static 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 */
70enum {
71 MSPEC_FETCHOP = 1,
72 MSPEC_CACHED,
73 MSPEC_UNCACHED
74};
75
76static 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 */
83struct 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 */
92static unsigned long scratch_page[MAX_NUMNODES];
93#define SH2_AMO_CACHE_ENTRIES 4
94
95static inline int
96mspec_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 */
131static void
132mspec_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 */
146static void
147mspec_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 */
186static unsigned long
187mspec_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
222static 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 */
235static int
236mspec_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
273static int
274fetchop_mmap(struct file *file, struct vm_area_struct *vma)
275{
276 return mspec_mmap(file, vma, MSPEC_FETCHOP);
277}
278
279static int
280cached_mmap(struct file *file, struct vm_area_struct *vma)
281{
282 return mspec_mmap(file, vma, MSPEC_CACHED);
283}
284
285static int
286uncached_mmap(struct file *file, struct vm_area_struct *vma)
287{
288 return mspec_mmap(file, vma, MSPEC_UNCACHED);
289}
290
291static struct file_operations fetchop_fops = {
292 .owner = THIS_MODULE,
293 .mmap = fetchop_mmap
294};
295
296static struct miscdevice fetchop_miscdev = {
297 .minor = MISC_DYNAMIC_MINOR,
298 .name = "sgi_fetchop",
299 .fops = &fetchop_fops
300};
301
302static struct file_operations cached_fops = {
303 .owner = THIS_MODULE,
304 .mmap = cached_mmap
305};
306
307static struct miscdevice cached_miscdev = {
308 .minor = MISC_DYNAMIC_MINOR,
309 .name = "mspec_cached",
310 .fops = &cached_fops
311};
312
313static struct file_operations uncached_fops = {
314 .owner = THIS_MODULE,
315 .mmap = uncached_mmap
316};
317
318static 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 */
329static int __init
330mspec_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
399static void __exit
400mspec_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
416module_init(mspec_init);
417module_exit(mspec_exit);
418
419MODULE_AUTHOR("Silicon Graphics, Inc. <linux-altix@sgi.com>");
420MODULE_DESCRIPTION("Driver for SGI SN special memory operations");
421MODULE_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
513config 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
515config WATCHDOG_CP1XXX 523config 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 */
131static void sh_wdt_stop(void) 130static 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 */
147static void sh_wdt_keepalive(void) 145static 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 */
157static int sh_wdt_set_heartbeat(int t) 154static 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 */
276static 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,
337static int sh_wdt_notify_sys(struct notifier_block *this, 378static 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
356static struct watchdog_info sh_wdt_info = { 397static 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");
434MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 475MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
435 476
436module_param(clock_division_ratio, int, 0); 477module_param(clock_division_ratio, int, 0);
437MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). Defaults to 0x7."); 478MODULE_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
439module_param(heartbeat, int, 0); 480module_param(heartbeat, int, 0);
440MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); 481MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
441 482
442module_param(nowayout, int, 0); 483module_param(nowayout, int, 0);
443MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); 484MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
444 485
445module_init(sh_wdt_init); 486module_init(sh_wdt_init);
446module_exit(sh_wdt_exit); 487module_exit(sh_wdt_exit);
447