root/drivers/char/uv_mmtimer.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. uv_mmtimer_ioctl
  2. uv_mmtimer_mmap
  3. uv_mmtimer_init

   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 
  27 #include <asm/genapic.h>
  28 #include <asm/uv/uv_hub.h>
  29 #include <asm/uv/bios.h>
  30 #include <asm/uv/uv.h>
  31 
  32 MODULE_AUTHOR("Dimitri Sivanich <sivanich@sgi.com>");
  33 MODULE_DESCRIPTION("SGI UV Memory Mapped RTC Timer");
  34 MODULE_LICENSE("GPL");
  35 
  36 /* name of the device, usually in /dev */
  37 #define UV_MMTIMER_NAME "mmtimer"
  38 #define UV_MMTIMER_DESC "SGI UV Memory Mapped RTC Timer"
  39 #define UV_MMTIMER_VERSION "1.0"
  40 
  41 static long uv_mmtimer_ioctl(struct file *file, unsigned int cmd,
  42                                                 unsigned long arg);
  43 static int uv_mmtimer_mmap(struct file *file, struct vm_area_struct *vma);
  44 
  45 /*
  46  * Period in femtoseconds (10^-15 s)
  47  */
  48 static unsigned long uv_mmtimer_femtoperiod;
  49 
  50 static const struct file_operations uv_mmtimer_fops = {
  51         .owner = THIS_MODULE,
  52         .mmap = uv_mmtimer_mmap,
  53         .unlocked_ioctl = uv_mmtimer_ioctl,
  54         .llseek = noop_llseek,
  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                  * Starting with HUB rev 2.0, the UV RTC register is
  93                  * replicated across all cachelines of it's own page.
  94                  * This allows faster simultaneous reads from a given socket.
  95                  *
  96                  * The offset returned is in 64 bit units.
  97                  */
  98                 if (uv_get_min_hub_revision_id() == 1)
  99                         ret = 0;
 100                 else
 101                         ret = ((uv_blade_processor_id() * L1_CACHE_BYTES) %
 102                                         PAGE_SIZE) / 8;
 103                 break;
 104 
 105         case MMTIMER_GETRES: /* resolution of the clock in 10^-15 s */
 106                 if (copy_to_user((unsigned long __user *)arg,
 107                                 &uv_mmtimer_femtoperiod, sizeof(unsigned long)))
 108                         ret = -EFAULT;
 109                 break;
 110 
 111         case MMTIMER_GETFREQ: /* frequency in Hz */
 112                 if (copy_to_user((unsigned long __user *)arg,
 113                                 &sn_rtc_cycles_per_second,
 114                                 sizeof(unsigned long)))
 115                         ret = -EFAULT;
 116                 break;
 117 
 118         case MMTIMER_GETBITS: /* number of bits in the clock */
 119                 ret = hweight64(UVH_RTC_REAL_TIME_CLOCK_MASK);
 120                 break;
 121 
 122         case MMTIMER_MMAPAVAIL:
 123                 ret = 1;
 124                 break;
 125 
 126         case MMTIMER_GETCOUNTER:
 127                 if (copy_to_user((unsigned long __user *)arg,
 128                                 (unsigned long *)uv_local_mmr_address(UVH_RTC),
 129                                 sizeof(unsigned long)))
 130                         ret = -EFAULT;
 131                 break;
 132         default:
 133                 ret = -ENOTTY;
 134                 break;
 135         }
 136         return ret;
 137 }
 138 
 139 /**
 140  * uv_mmtimer_mmap - maps the clock's registers into userspace
 141  * @file: file structure for the device
 142  * @vma: VMA to map the registers into
 143  *
 144  * Calls remap_pfn_range() to map the clock's registers into
 145  * the calling process' address space.
 146  */
 147 static int uv_mmtimer_mmap(struct file *file, struct vm_area_struct *vma)
 148 {
 149         unsigned long uv_mmtimer_addr;
 150 
 151         if (vma->vm_end - vma->vm_start != PAGE_SIZE)
 152                 return -EINVAL;
 153 
 154         if (vma->vm_flags & VM_WRITE)
 155                 return -EPERM;
 156 
 157         if (PAGE_SIZE > (1 << 16))
 158                 return -ENOSYS;
 159 
 160         vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 161 
 162         uv_mmtimer_addr = UV_LOCAL_MMR_BASE | UVH_RTC;
 163         uv_mmtimer_addr &= ~(PAGE_SIZE - 1);
 164         uv_mmtimer_addr &= 0xfffffffffffffffUL;
 165 
 166         if (remap_pfn_range(vma, vma->vm_start, uv_mmtimer_addr >> PAGE_SHIFT,
 167                                         PAGE_SIZE, vma->vm_page_prot)) {
 168                 printk(KERN_ERR "remap_pfn_range failed in uv_mmtimer_mmap\n");
 169                 return -EAGAIN;
 170         }
 171 
 172         return 0;
 173 }
 174 
 175 static struct miscdevice uv_mmtimer_miscdev = {
 176         MISC_DYNAMIC_MINOR,
 177         UV_MMTIMER_NAME,
 178         &uv_mmtimer_fops
 179 };
 180 
 181 
 182 /**
 183  * uv_mmtimer_init - device initialization routine
 184  *
 185  * Does initial setup for the uv_mmtimer device.
 186  */
 187 static int __init uv_mmtimer_init(void)
 188 {
 189         if (!is_uv_system()) {
 190                 printk(KERN_ERR "%s: Hardware unsupported\n", UV_MMTIMER_NAME);
 191                 return -1;
 192         }
 193 
 194         /*
 195          * Sanity check the cycles/sec variable
 196          */
 197         if (sn_rtc_cycles_per_second < 100000) {
 198                 printk(KERN_ERR "%s: unable to determine clock frequency\n",
 199                        UV_MMTIMER_NAME);
 200                 return -1;
 201         }
 202 
 203         uv_mmtimer_femtoperiod = ((unsigned long)1E15 +
 204                                 sn_rtc_cycles_per_second / 2) /
 205                                 sn_rtc_cycles_per_second;
 206 
 207         if (misc_register(&uv_mmtimer_miscdev)) {
 208                 printk(KERN_ERR "%s: failed to register device\n",
 209                        UV_MMTIMER_NAME);
 210                 return -1;
 211         }
 212 
 213         printk(KERN_INFO "%s: v%s, %ld MHz\n", UV_MMTIMER_DESC,
 214                 UV_MMTIMER_VERSION,
 215                 sn_rtc_cycles_per_second/(unsigned long)1E6);
 216 
 217         return 0;
 218 }
 219 
 220 module_init(uv_mmtimer_init);

/* [<][>][^][v][top][bottom][index][help] */