blob: 296418d04260b88184a6096efb39ccf36c6c0ebf [file] [log] [blame]
Harini Jayaramanef7805f2011-09-28 12:45:31 -06001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13/* #define DEBUG */
14
15#include <linux/module.h>
16#include <linux/fs.h>
17#include <linux/mm.h>
18#include <linux/err.h>
19#include <linux/slab.h>
20#include <linux/errno.h>
21#include <linux/device.h>
22#include <linux/uaccess.h>
23#include <linux/miscdevice.h>
24#include <linux/memory_alloc.h>
25#include "msm-buspm-dev.h"
26
27#define MSM_BUSPM_DRV_NAME "msm-buspm-dev"
28
29/*
30 * Allocate kernel buffer.
31 * Currently limited to one buffer per file descriptor. If alloc() is
32 * called twice for the same descriptor, the original buffer is freed.
33 * There is also no locking protection so the same descriptor can not be shared.
34 */
35
36static inline void *msm_buspm_dev_get_vaddr(struct file *filp)
37{
38 struct msm_buspm_map_dev *dev = filp->private_data;
39
40 return (dev) ? dev->vaddr : NULL;
41}
42
43static inline unsigned long msm_buspm_dev_get_paddr(struct file *filp)
44{
45 struct msm_buspm_map_dev *dev = filp->private_data;
46
47 return (dev) ? dev->paddr : 0L;
48}
49
50static void msm_buspm_dev_free(struct file *filp)
51{
52 struct msm_buspm_map_dev *dev = filp->private_data;
53
54 if (dev) {
55 pr_debug("freeing memory at 0x%p\n", dev->vaddr);
56 free_contiguous_memory(dev->vaddr);
57 dev->paddr = 0L;
58 dev->vaddr = NULL;
59 }
60}
61
62static int msm_buspm_dev_open(struct inode *inode, struct file *filp)
63{
64 struct msm_buspm_map_dev *dev;
65
66 if (capable(CAP_SYS_ADMIN)) {
67 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
68 if (dev)
69 filp->private_data = dev;
70 else
71 return -ENOMEM;
72 } else {
73 return -EPERM;
74 }
75
76 return 0;
77}
78
79static int
80msm_buspm_dev_alloc(struct file *filp, struct buspm_alloc_params data)
81{
82 unsigned long paddr;
83 void *vaddr;
84 struct msm_buspm_map_dev *dev = filp->private_data;
85
86 /* If buffer already allocated, then free it */
87 if (dev->vaddr)
88 msm_buspm_dev_free(filp);
89
90 /* Allocate uncached memory */
91 vaddr = allocate_contiguous_ebi(data.size, PAGE_SIZE, 0);
92 paddr = (vaddr) ? memory_pool_node_paddr(vaddr) : 0L;
93
94 if (vaddr == NULL) {
95 pr_err("allocation of 0x%x bytes failed", data.size);
96 return -ENOMEM;
97 }
98
99 dev->vaddr = vaddr;
100 dev->paddr = paddr;
101 dev->buflen = data.size;
102 filp->f_pos = 0;
103 pr_debug("virt addr = 0x%p\n", dev->vaddr);
104 pr_debug("phys addr = 0x%lx\n", dev->paddr);
105
106 return 0;
107}
108
109static long
110msm_buspm_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
111{
112 struct buspm_xfer_req xfer;
113 struct buspm_alloc_params alloc_data;
114 unsigned long paddr;
115 int retval = 0;
116 void *buf = msm_buspm_dev_get_vaddr(filp);
117 unsigned char *dbgbuf = buf;
118
119 switch (cmd) {
120 case MSM_BUSPM_IOC_FREE:
121 pr_debug("cmd = 0x%x (FREE)\n", cmd);
122 msm_buspm_dev_free(filp);
123 break;
124
125 case MSM_BUSPM_IOC_ALLOC:
126 pr_debug("cmd = 0x%x (ALLOC)\n", cmd);
127 retval = __get_user(alloc_data.size, (size_t __user *)arg);
128
129 if (retval == 0)
130 retval = msm_buspm_dev_alloc(filp, alloc_data);
131 break;
132
133 case MSM_BUSPM_IOC_RD_PHYS_ADDR:
134 pr_debug("Read Physical Address\n");
135 paddr = msm_buspm_dev_get_paddr(filp);
136 if (paddr == 0L) {
137 retval = -EINVAL;
138 } else {
139 pr_debug("phys addr = 0x%lx\n", paddr);
140 retval = __put_user(paddr,
141 (unsigned long __user *)arg);
142 }
143 break;
144
145 case MSM_BUSPM_IOC_RDBUF:
146 pr_debug("Read Buffer: 0x%x%x%x%x\n",
147 dbgbuf[0], dbgbuf[1], dbgbuf[2], dbgbuf[3]);
148
149 if (!buf) {
150 retval = -EINVAL;
151 break;
152 }
153
154 if (copy_from_user(&xfer, (void __user *)arg, sizeof(xfer))) {
155 retval = -EFAULT;
156 break;
157 }
158
159 if ((xfer.size <= sizeof(buf)) &&
160 (copy_to_user((void __user *)xfer.data, buf,
161 xfer.size))) {
162 retval = -EFAULT;
163 break;
164 }
165 break;
166
167 case MSM_BUSPM_IOC_WRBUF:
168 pr_debug("Write Buffer\n");
169
170 if (!buf) {
171 retval = -EINVAL;
172 break;
173 }
174
175 if (copy_from_user(&xfer, (void __user *)arg, sizeof(xfer))) {
176 retval = -EFAULT;
177 break;
178 }
179
180 if ((sizeof(buf) <= xfer.size) &&
181 (copy_from_user(buf, (void __user *)xfer.data,
182 xfer.size))) {
183 retval = -EFAULT;
184 break;
185 }
186 break;
187
188 default:
189 pr_debug("Unknown command 0x%x\n", cmd);
190 retval = -EINVAL;
191 break;
192 }
193
194 return retval;
195}
196
197static int msm_buspm_dev_release(struct inode *inode, struct file *filp)
198{
199 struct msm_buspm_map_dev *dev = filp->private_data;
200
201 msm_buspm_dev_free(filp);
202 kfree(dev);
203 filp->private_data = NULL;
204
205 return 0;
206}
207
208static int msm_buspm_dev_mmap(struct file *filp, struct vm_area_struct *vma)
209{
210 pr_debug("vma = 0x%p\n", vma);
211
212 /* Mappings are uncached */
213 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
214 if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
215 vma->vm_end - vma->vm_start, vma->vm_page_prot))
216 return -EFAULT;
217
218 return 0;
219}
220
221static const struct file_operations msm_buspm_dev_fops = {
222 .owner = THIS_MODULE,
223 .mmap = msm_buspm_dev_mmap,
224 .open = msm_buspm_dev_open,
225 .unlocked_ioctl = msm_buspm_dev_ioctl,
226 .llseek = noop_llseek,
227 .release = msm_buspm_dev_release,
228};
229
230struct miscdevice msm_buspm_misc = {
231 .minor = MISC_DYNAMIC_MINOR,
232 .name = MSM_BUSPM_DRV_NAME,
233 .fops = &msm_buspm_dev_fops,
234};
235
236static int __init msm_buspm_dev_init(void)
237{
238 int ret = 0;
239
240 ret = misc_register(&msm_buspm_misc);
241 if (ret < 0)
242 pr_err("%s: Cannot register misc device\n", __func__);
243
244 return ret;
245}
246
247static void __exit msm_buspm_dev_exit(void)
248{
249 misc_deregister(&msm_buspm_misc);
250}
251module_init(msm_buspm_dev_init);
252module_exit(msm_buspm_dev_exit);
253
254MODULE_LICENSE("GPL v2");
255MODULE_VERSION("1.0");
256MODULE_ALIAS("platform:"MSM_BUSPM_DRV_NAME);