Skip to contentSkip to conversation

Add memory mapped RTC driver for UV

PATCH In review Dimitri Sivanich 3 participants 4 messages Sep 11, 2009

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Add memory mapped RTC driver for UV

This driver memory maps the UV Hub RTC.

---

 drivers/char/Kconfig      |    8 +
 drivers/char/Makefile     |    1 
 drivers/char/uv_mmtimer.c |  214 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 223 insertions(+)
drivers/char/Kconfig +8 -0
Index: linux/drivers/char/Kconfig
===================================================================
@@ -1087,6 +1087,14 @@ config MMTIMER
 	  The mmtimer device allows direct userspace access to the
 	  Altix system timer.
 
+config UV_MMTIMER
+	tristate "UV_MMTIMER Memory mapped RTC for SGI UV"
+	depends on X86_UV
+	default m
+	help
+	  The uv_mmtimer device allows direct userspace access to the
+	  UV system timer.
+
 source "drivers/char/tpm/Kconfig"
 
 config TELCLOCK
drivers/char/Makefile +1 -0
Index: linux/drivers/char/Makefile
===================================================================
@@ -58,6 +58,7 @@ obj-$(CONFIG_RAW_DRIVER)	+= raw.o
 obj-$(CONFIG_SGI_SNSC)		+= snsc.o snsc_event.o
 obj-$(CONFIG_MSPEC)		+= mspec.o
 obj-$(CONFIG_MMTIMER)		+= mmtimer.o
+obj-$(CONFIG_UV_MMTIMER)	+= uv_mmtimer.o
 obj-$(CONFIG_VIOTAPE)		+= viotape.o
 obj-$(CONFIG_HVCS)		+= hvcs.o
 obj-$(CONFIG_IBM_BSR)		+= bsr.o
drivers/char/uv_mmtimer.c new file +214 -0

214 lines not on this page · raw message

maintainer
#1 lore raw
Quoted 22 lines · On Thu, 10 Sep 2009 20:30:54 -0500 Dimitri Sivanich <[email protected]> wrote:

This driver memory maps the UV Hub RTC.

...

+/**
+ * uv_mmtimer_ioctl - ioctl interface for /dev/uv_mmtimer
+ * @file: file structure for the device
+ * @cmd: command to execute
+ * @arg: optional argument to command
+ *
+ * Executes the command specified by @cmd. Returns 0 for success, < 0 for
+ * failure.
+ *
+ * Valid commands:
+ *
+ * %MMTIMER_GETOFFSET - Should return the offset (relative to the start
+ * of the page where the registers are mapped) for the counter in question.
+ *
+ * %MMTIMER_GETRES - Returns the resolution of the clock in femto (10^-15)
+ * seconds
+ *
+ * %MMTIMER_GETFREQ - Copies the frequency of the clock in Hz to the address

Quoted 8 lines

+ * specified by @arg
+ *
+ * %MMTIMER_GETBITS - Returns the number of bits in the clock's counter
+ *
+ * %MMTIMER_MMAPAVAIL - Returns 1 if registers can be mmap'd into userspace
+ *
+ * %MMTIMER_GETCOUNTER - Gets the current value in the counter and places it
+ * in the address specified by @arg.

Are these % thingies part of kerneldoc?

drivers/char/uv_mmtimer.c line 92

Quoted 10 lines

+ */

drivers/char/uv_mmtimer.c new file +9 -0
+static long uv_mmtimer_ioctl(struct file *file, unsigned int cmd,
+						unsigned long arg)
+{
+	int ret = 0;
+
+	switch (cmd) {
+	case MMTIMER_GETOFFSET:	/* offset of the counter */
+		/*
+		 * UV RTC register is on it's own page

"its" ;)

Quoted 67 lines
drivers/char/uv_mmtimer.c new file +67 -0
+		 */
+		if (PAGE_SIZE <= (1 << 16))
+			ret = ((UV_LOCAL_MMR_BASE | UVH_RTC) & (PAGE_SIZE-1))
+				/ 8;
+		else
+			ret = -ENOSYS;
+		break;
+
+	case MMTIMER_GETRES: /* resolution of the clock in 10^-15 s */
+		if (copy_to_user((unsigned long __user *)arg,
+				&uv_mmtimer_femtoperiod, sizeof(unsigned long)))
+			ret = -EFAULT;
+		break;
+
+	case MMTIMER_GETFREQ: /* frequency in Hz */
+		if (copy_to_user((unsigned long __user *)arg,
+				&sn_rtc_cycles_per_second,
+				sizeof(unsigned long)))
+			ret = -EFAULT;
+		break;
+
+	case MMTIMER_GETBITS: /* number of bits in the clock */
+		ret = hweight64(UVH_RTC_REAL_TIME_CLOCK_MASK);
+		break;
+
+	case MMTIMER_MMAPAVAIL: /* can we mmap the clock into userspace? */
+		ret = (PAGE_SIZE <= (1 << 16)) ? 1 : 0;
+		break;
+
+	case MMTIMER_GETCOUNTER:
+		if (copy_to_user((unsigned long __user *)arg,
+				(unsigned long *)uv_local_mmr_address(UVH_RTC),
+				sizeof(unsigned long)))
+			ret = -EFAULT;
+		break;
+	default:
+		ret = -ENOTTY;
+		break;
+	}
+	return ret;
+}
+
+/**
+ * uv_mmtimer_mmap - maps the clock's registers into userspace
+ * @file: file structure for the device
+ * @vma: VMA to map the registers into
+ *
+ * Calls remap_pfn_range() to map the clock's registers into
+ * the calling process' address space.
+ */
+static int uv_mmtimer_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	unsigned long uv_mmtimer_addr;
+
+	if (vma->vm_end - vma->vm_start != PAGE_SIZE)
+		return -EINVAL;
+
+	if (vma->vm_flags & VM_WRITE)
+		return -EPERM;
+
+	if (PAGE_SIZE > (1 << 16))
+		return -ENOSYS;
+
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+	uv_mmtimer_addr = UV_LOCAL_MMR_BASE | UVH_RTC;
+	uv_mmtimer_addr &= ~(PAGE_SIZE - 1);

drivers/char/uv_mmtimer.c line 169

Quoted 10 lines
drivers/char/uv_mmtimer.c new file +10 -0
+	uv_mmtimer_addr &= 0xfffffffffffffffUL;
+
+	if (remap_pfn_range(vma, vma->vm_start, uv_mmtimer_addr >> PAGE_SHIFT,
+					PAGE_SIZE, vma->vm_page_prot)) {
+		printk(KERN_ERR "remap_pfn_range failed in uv_mmtimer_mmap\n");
+		return -EAGAIN;
+	}
+
+	return 0;
+}

Methinks we should be setting vma->vm_flags's VM_IO here and perhaps also VM_RESERVED.

Quoted 6 lines
drivers/char/uv_mmtimer.c new file +6 -0
+static struct miscdevice uv_mmtimer_miscdev = {
+	MISC_DYNAMIC_MINOR,
+	UV_MMTIMER_NAME,
+	&uv_mmtimer_fops
+};
+

drivers/char/uv_mmtimer.c line 186

Quoted 10 lines
drivers/char/uv_mmtimer.c new file +10 -0
+
+/**
+ * uv_mmtimer_init - device initialization routine
+ *
+ * Does initial setup for the uv_mmtimer device.
+ */
+static int __init uv_mmtimer_init(void)
+{
+	if (!is_uv_system())
+		return 0;

This will leave the module loaded but inactive. Would it make more sense to return an error code in this case so that a) the user discovers that he wasted his time and b) the module will get booted out again?

Quoted 17 lines
drivers/char/uv_mmtimer.c new file +17 -0
+	/*
+	 * Sanity check the cycles/sec variable
+	 */
+	if (sn_rtc_cycles_per_second < 100000) {
+		printk(KERN_ERR "%s: unable to determine clock frequency\n",
+		       UV_MMTIMER_NAME);
+		return -1;
+	}
+
+	uv_mmtimer_femtoperiod = ((unsigned long)1E15 +
+				sn_rtc_cycles_per_second / 2) /
+				sn_rtc_cycles_per_second;
+
+	if (misc_register(&uv_mmtimer_miscdev)) {
+		printk(KERN_ERR "%s: failed to register device\n",
+		       UV_MMTIMER_NAME);
+		return -1;

drivers/char/uv_mmtimer.c line 214

Quoted 10 lines
drivers/char/uv_mmtimer.c new file +10 -0
+	}
+
+	printk(KERN_INFO "%s: v%s, %ld MHz\n", UV_MMTIMER_DESC,
+		UV_MMTIMER_VERSION,
+		sn_rtc_cycles_per_second/(unsigned long)1E6);
+
+	return 0;
+}
+
+module_init(uv_mmtimer_init);

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#2 lore raw
Quoted 120 lines · Andrew Morton
Quoted 22 lines · On Thu, 10 Sep 2009 20:30:54 -0500 Dimitri Sivanich <[email protected]> wrote:

This driver memory maps the UV Hub RTC.

...

+/**
+ * uv_mmtimer_ioctl - ioctl interface for /dev/uv_mmtimer
+ * @file: file structure for the device
+ * @cmd: command to execute
+ * @arg: optional argument to command
+ *
+ * Executes the command specified by @cmd.  Returns 0 for success, < 0 for
+ * failure.
+ *
+ * Valid commands:
+ *
+ * %MMTIMER_GETOFFSET - Should return the offset (relative to the start
+ * of the page where the registers are mapped) for the counter in question.
+ *
+ * %MMTIMER_GETRES - Returns the resolution of the clock in femto (10^-15)
+ * seconds
+ *
+ * %MMTIMER_GETFREQ - Copies the frequency of the clock in Hz to the address

Quoted 8 lines

+ * specified by @arg
+ *
+ * %MMTIMER_GETBITS - Returns the number of bits in the clock's counter
+ *
+ * %MMTIMER_MMAPAVAIL - Returns 1 if registers can be mmap'd into userspace
+ *
+ * %MMTIMER_GETCOUNTER - Gets the current value in the counter and places it
+ * in the address specified by @arg.

Are these % thingies part of kerneldoc?

drivers/char/uv_mmtimer.c line 92

Quoted 10 lines

+ */

drivers/char/uv_mmtimer.c new file +9 -0
+static long uv_mmtimer_ioctl(struct file *file, unsigned int cmd,
+                                             unsigned long arg)
+{
+     int ret = 0;
+
+     switch (cmd) {
+     case MMTIMER_GETOFFSET: /* offset of the counter */
+             /*
+              * UV RTC register is on it's own page

"its" ;)

Quoted 72 lines

+              */
+             if (PAGE_SIZE <= (1 << 16))
+                     ret = ((UV_LOCAL_MMR_BASE | UVH_RTC) & (PAGE_SIZE-1))
+                             / 8;
+             else
+                     ret = -ENOSYS;
+             break;

drivers/char/uv_mmtimer.c new file +65 -0
+
+     case MMTIMER_GETRES: /* resolution of the clock in 10^-15 s */
+             if (copy_to_user((unsigned long __user *)arg,
+                             &uv_mmtimer_femtoperiod, sizeof(unsigned long)))
+                     ret = -EFAULT;
+             break;
+
+     case MMTIMER_GETFREQ: /* frequency in Hz */
+             if (copy_to_user((unsigned long __user *)arg,
+                             &sn_rtc_cycles_per_second,
+                             sizeof(unsigned long)))
+                     ret = -EFAULT;
+             break;
+
+     case MMTIMER_GETBITS: /* number of bits in the clock */
+             ret = hweight64(UVH_RTC_REAL_TIME_CLOCK_MASK);
+             break;
+
+     case MMTIMER_MMAPAVAIL: /* can we mmap the clock into userspace? */
+             ret = (PAGE_SIZE <= (1 << 16)) ? 1 : 0;
+             break;
+
+     case MMTIMER_GETCOUNTER:
+             if (copy_to_user((unsigned long __user *)arg,
+                             (unsigned long *)uv_local_mmr_address(UVH_RTC),
+                             sizeof(unsigned long)))
+                     ret = -EFAULT;
+             break;
+     default:
+             ret = -ENOTTY;
+             break;
+     }
+     return ret;
+}
+
+/**
+ * uv_mmtimer_mmap - maps the clock's registers into userspace
+ * @file: file structure for the device
+ * @vma: VMA to map the registers into
+ *
+ * Calls remap_pfn_range() to map the clock's registers into
+ * the calling process' address space.
+ */
+static int uv_mmtimer_mmap(struct file *file, struct vm_area_struct *vma)
+{
+     unsigned long uv_mmtimer_addr;
+
+     if (vma->vm_end - vma->vm_start != PAGE_SIZE)
+             return -EINVAL;
+
+     if (vma->vm_flags & VM_WRITE)
+             return -EPERM;
+
+     if (PAGE_SIZE > (1 << 16))
+             return -ENOSYS;
+
+     vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+     uv_mmtimer_addr = UV_LOCAL_MMR_BASE | UVH_RTC;
+     uv_mmtimer_addr &= ~(PAGE_SIZE - 1);
+     uv_mmtimer_addr &= 0xfffffffffffffffUL;
+
+     if (remap_pfn_range(vma, vma->vm_start, uv_mmtimer_addr >> PAGE_SHIFT,
+                                     PAGE_SIZE, vma->vm_page_prot)) {
+             printk(KERN_ERR "remap_pfn_range failed in uv_mmtimer_mmap\n");
Quoted 8 lines

drivers/char/uv_mmtimer.c line 169

Quoted 5 lines

+             return -EAGAIN;
+     }

drivers/char/uv_mmtimer.c new file +3 -0
+
+     return 0;
+}

Methinks we should be setting vma->vm_flags's VM_IO here and perhaps also VM_RESERVED.

remap_pfn_range already does it. :)

Signature · 8 lines
Kind regards,
Minchan Kim

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#3 lore raw
In reply to Andrew Morton
Quoted 19 lines · Andrew Morton
Quoted 9 lines · On Thu, 10 Sep 2009 20:30:54 -0500 Dimitri Sivanich <[email protected]> wrote:

+ * Valid commands:
+ *
+ * %MMTIMER_GETOFFSET - Should return the offset (relative to the start
+ * of the page where the registers are mapped) for the counter in question.
+ *
+ * %MMTIMER_GETRES - Returns the resolution of the clock in femto (10^-15)
+ * seconds
+ *
+ * %MMTIMER_GETFREQ - Copies the frequency of the clock in Hz to the address

Quoted 8 lines

+ * specified by @arg
+ *
+ * %MMTIMER_GETBITS - Returns the number of bits in the clock's counter
+ *
+ * %MMTIMER_MMAPAVAIL - Returns 1 if registers can be mmap'd into userspace
+ *
+ * %MMTIMER_GETCOUNTER - Gets the current value in the counter and places it
+ * in the address specified by @arg.

Quoted 1 line

Are these % thingies part of kerneldoc?

I assume you're referring to Documentation/ioctl/ioctl-number.txt? Added.

Quoted 2 lines

Review

Quoted 1 line
(quoted diff) +1 -0
+		 * UV RTC register is on it's own page

"its" ;)

Fixed ;)

Quoted 4 lines
Quoted 4 lines
(quoted diff) +4 -0
+
+	if (remap_pfn_range(vma, vma->vm_start, uv_mmtimer_addr >> PAGE_SHIFT,
+					PAGE_SIZE, vma->vm_page_prot)) {
+		printk(KERN_ERR "remap_pfn_range failed in uv_mmtimer_mmap\n");
Quoted 8 lines

Review

Quoted 5 lines
(quoted diff) +5 -0
+		return -EAGAIN;
+	}
+
+	return 0;
+}

Methinks we should be setting vma->vm_flags's VM_IO here and perhaps also VM_RESERVED.

As Minchan Kim mentioned, these are already added by remap_pfn_range.

Quoted 10 lines

Review

Quoted 4 lines
(quoted diff) +4 -0
+static int __init uv_mmtimer_init(void)
+{
+	if (!is_uv_system())
+		return 0;

This will leave the module loaded but inactive. Would it make more sense to return an error code in this case so that a) the user discovers that he wasted his time and b) the module will get booted out again?

Done.

Here's the updated patch.
----------------------------------------------------------------------------

This driver memory maps the UV Hub RTC.

---

 Documentation/ioctl/ioctl-number.txt |    1 
 drivers/char/Kconfig                 |    8 +
 drivers/char/Makefile                |    1 
 drivers/char/uv_mmtimer.c            |  216 +++++++++++++++++++++++++++++++
 4 files changed, 226 insertions(+)
drivers/char/Kconfig +8 -0
Index: linux/drivers/char/Kconfig
===================================================================
@@ -1087,6 +1087,14 @@ config MMTIMER
 	  The mmtimer device allows direct userspace access to the
 	  Altix system timer.
 
+config UV_MMTIMER
+	tristate "UV_MMTIMER Memory mapped RTC for SGI UV"
+	depends on X86_UV
+	default m
+	help
+	  The uv_mmtimer device allows direct userspace access to the
+	  UV system timer.
+
 source "drivers/char/tpm/Kconfig"
 
 config TELCLOCK
drivers/char/Makefile +1 -0
Index: linux/drivers/char/Makefile
===================================================================
@@ -58,6 +58,7 @@ obj-$(CONFIG_RAW_DRIVER)	+= raw.o
 obj-$(CONFIG_SGI_SNSC)		+= snsc.o snsc_event.o
 obj-$(CONFIG_MSPEC)		+= mspec.o
 obj-$(CONFIG_MMTIMER)		+= mmtimer.o
+obj-$(CONFIG_UV_MMTIMER)	+= uv_mmtimer.o
 obj-$(CONFIG_VIOTAPE)		+= viotape.o
 obj-$(CONFIG_HVCS)		+= hvcs.o
 obj-$(CONFIG_IBM_BSR)		+= bsr.o
drivers/char/uv_mmtimer.c new file +216 -0

216 lines not on this page · raw message

Documentation/ioctl/ioctl-number.txt +1 -0
Index: linux/Documentation/ioctl/ioctl-number.txt
===================================================================
@@ -135,6 +135,7 @@ Code	Seq#	Include File		Comments
 					<http://mikonos.dia.unisa.it/tcfs>
 'l'	40-7F	linux/udf_fs_i.h	in development:
 					<http://sourceforge.net/projects/linux-udf/>
+'m'	00-09	linux/mmtimer.h
 'm'	all	linux/mtio.h		conflict!
 'm'	all	linux/soundcard.h	conflict!
 'm'	all	linux/synclink.h	conflict!

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>