Browse Source

watchdog: Add a simple mmap() stub for shwdt.

In some applications people have expressed a need for an mmap() method,
so we implement a simple stub for this that maps back a page with the
counter in it.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Paul Mundt 18 years ago
parent
commit
f118420be8
2 changed files with 59 additions and 1 deletions
  1. 8 0
      drivers/char/watchdog/Kconfig
  2. 51 1
      drivers/char/watchdog/shwdt.c

+ 8 - 0
drivers/char/watchdog/Kconfig

@@ -510,6 +510,14 @@ config SH_WDT
 	  To compile this driver as a module, choose M here: the
 	  module will be called shwdt.
 
+config SH_WDT_MMAP
+	bool "Allow mmap of SH WDT"
+	default n
+	depends on SH_WDT
+	help
+	  If you say Y here, user applications will be able to mmap the
+	  WDT/CPG registers.
+#
 # SPARC64 Architecture
 
 config WATCHDOG_CP1XXX

+ 51 - 1
drivers/char/watchdog/shwdt.c

@@ -27,7 +27,7 @@
 #include <linux/notifier.h>
 #include <linux/ioport.h>
 #include <linux/fs.h>
-
+#include <linux/mm.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
 #include <asm/watchdog.h>
@@ -257,6 +257,55 @@ static ssize_t sh_wdt_write(struct file *file, const char *buf,
 	return count;
 }
 
+/**
+ * 	sh_wdt_mmap - map WDT/CPG registers into userspace
+ * 	@file: file structure for the device
+ * 	@vma: VMA to map the registers into
+ *
+ * 	A simple mmap() implementation for the corner cases where the counter
+ * 	needs to be mapped in userspace directly. Due to the relatively small
+ * 	size of the area, neighbouring registers not necessarily tied to the
+ * 	CPG will also be accessible through the register page, so this remains
+ * 	configurable for users that really know what they're doing.
+ *
+ *	Additionaly, the register page maps in the CPG register base relative
+ *	to the nearest page-aligned boundary, which requires that userspace do
+ *	the appropriate CPU subtype math for calculating the page offset for
+ *	the counter value.
+ */
+static int sh_wdt_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	int ret = -ENOSYS;
+
+#ifdef CONFIG_SH_WDT_MMAP
+	unsigned long addr;
+
+	/* Only support the simple cases where we map in a register page. */
+	if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff)
+		return -EINVAL;
+
+	/*
+	 * Pick WTCNT as the start, it's usually the first register after the
+	 * FRQCR, and neither one are generally page-aligned out of the box.
+	 */
+	addr = WTCNT & ~(PAGE_SIZE - 1);
+
+	vma->vm_flags |= VM_IO;
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+	if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT,
+			       PAGE_SIZE, vma->vm_page_prot)) {
+		printk(KERN_ERR PFX "%s: io_remap_pfn_range failed\n",
+		       __FUNCTION__);
+		return -EAGAIN;
+	}
+
+	ret = 0;
+#endif
+
+	return ret;
+}
+
 /**
  * 	sh_wdt_ioctl - Query Device
  * 	@inode: inode of device
@@ -342,6 +391,7 @@ static const struct file_operations sh_wdt_fops = {
 	.ioctl		= sh_wdt_ioctl,
 	.open		= sh_wdt_open,
 	.release	= sh_wdt_close,
+	.mmap		= sh_wdt_mmap,
 };
 
 static struct watchdog_info sh_wdt_info = {