Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / unisys / visorchipset / file.c
1 /* file.c
2  *
3  * Copyright (C) 2010 - 2013 UNISYS CORPORATION
4  * All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or (at
9  * your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
14  * NON INFRINGEMENT.  See the GNU General Public License for more
15  * details.
16  */
17
18 /* This contains the implementation that allows a usermode program to
19  * communicate with the visorchipset driver using a device/file interface.
20  */
21
22 #include "globals.h"
23 #include "visorchannel.h"
24 #include <linux/mm.h>
25 #include <linux/fs.h>
26 #include "uisutils.h"
27 #include "file.h"
28
29 #define CURRENT_FILE_PC VISOR_CHIPSET_PC_file_c
30
31 static struct cdev file_cdev;
32 static struct visorchannel **file_controlvm_channel;
33
34 void
35 visorchipset_file_cleanup(dev_t major_dev)
36 {
37         if (file_cdev.ops != NULL)
38                 cdev_del(&file_cdev);
39         file_cdev.ops = NULL;
40         unregister_chrdev_region(major_dev, 1);
41 }
42
43 static int
44 visorchipset_open(struct inode *inode, struct file *file)
45 {
46         unsigned minor_number = iminor(inode);
47
48         if (minor_number != 0)
49                 return -ENODEV;
50         file->private_data = NULL;
51         return 0;
52 }
53
54 static int
55 visorchipset_release(struct inode *inode, struct file *file)
56 {
57         return 0;
58 }
59
60 static int
61 visorchipset_mmap(struct file *file, struct vm_area_struct *vma)
62 {
63         ulong physaddr = 0;
64         ulong offset = vma->vm_pgoff << PAGE_SHIFT;
65         GUEST_PHYSICAL_ADDRESS addr = 0;
66
67         /* sv_enable_dfp(); */
68         if (offset & (PAGE_SIZE - 1))
69                 return -ENXIO;  /* need aligned offsets */
70
71         switch (offset) {
72         case VISORCHIPSET_MMAP_CONTROLCHANOFFSET:
73                 vma->vm_flags |= VM_IO;
74                 if (*file_controlvm_channel == NULL) {
75                         return -ENXIO;
76                 }
77                 visorchannel_read(*file_controlvm_channel,
78                         offsetof(struct spar_controlvm_channel_protocol,
79                                  gp_control_channel),
80                         &addr, sizeof(addr));
81                 if (addr == 0) {
82                         return -ENXIO;
83                 }
84                 physaddr = (ulong)addr;
85                 if (remap_pfn_range(vma, vma->vm_start,
86                                     physaddr >> PAGE_SHIFT,
87                                     vma->vm_end - vma->vm_start,
88                                     /*pgprot_noncached */
89                                     (vma->vm_page_prot))) {
90                         return -EAGAIN;
91                 }
92                 break;
93         default:
94                 return -ENOSYS;
95         }
96         return 0;
97 }
98
99 static long visorchipset_ioctl(struct file *file, unsigned int cmd,
100                                 unsigned long arg)
101 {
102         s64 adjustment;
103         s64 vrtc_offset;
104
105         switch (cmd) {
106         case VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET:
107                 /* get the physical rtc offset */
108                 vrtc_offset = issue_vmcall_query_guest_virtual_time_offset();
109                 if (copy_to_user
110                     ((void __user *)arg, &vrtc_offset, sizeof(vrtc_offset))) {
111                         return -EFAULT;
112                 }
113                 return SUCCESS;
114         case VMCALL_UPDATE_PHYSICAL_TIME:
115                 if (copy_from_user
116                     (&adjustment, (void __user *)arg, sizeof(adjustment))) {
117                         return -EFAULT;
118                 }
119                 return issue_vmcall_update_physical_time(adjustment);
120         default:
121                 return -EFAULT;
122         }
123 }
124
125 static const struct file_operations visorchipset_fops = {
126         .owner = THIS_MODULE,
127         .open = visorchipset_open,
128         .read = NULL,
129         .write = NULL,
130         .unlocked_ioctl = visorchipset_ioctl,
131         .release = visorchipset_release,
132         .mmap = visorchipset_mmap,
133 };
134
135 int
136 visorchipset_file_init(dev_t major_dev, struct visorchannel **controlvm_channel)
137 {
138         int rc = 0;
139
140         file_controlvm_channel = controlvm_channel;
141         cdev_init(&file_cdev, &visorchipset_fops);
142         file_cdev.owner = THIS_MODULE;
143         if (MAJOR(major_dev) == 0) {
144                 rc = alloc_chrdev_region(&major_dev, 0, 1, MYDRVNAME);
145                 /* dynamic major device number registration required */
146                 if (rc < 0)
147                         return rc;
148         } else {
149                 /* static major device number registration required */
150                 rc = register_chrdev_region(major_dev, 1, MYDRVNAME);
151                 if (rc < 0)
152                         return rc;
153         }
154         rc = cdev_add(&file_cdev, MKDEV(MAJOR(major_dev), 0), 1);
155         if (rc < 0) {
156                 unregister_chrdev_region(major_dev, 1);
157                 return rc;
158         }
159         return 0;
160 }