blob: a818eed65eb709796ed208470b16e700dbd10e64 [file] [log] [blame]
Gagan Macb315a202012-07-18 15:32:10 -06001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Harini Jayaramanef7805f2011-09-28 12:45:31 -06002 *
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
Gagan Macb315a202012-07-18 15:32:10 -060043static inline unsigned int msm_buspm_dev_get_buflen(struct file *filp)
44{
45 struct msm_buspm_map_dev *dev = filp->private_data;
46
47 return dev ? dev->buflen : 0;
48}
49
Harini Jayaramanef7805f2011-09-28 12:45:31 -060050static inline unsigned long msm_buspm_dev_get_paddr(struct file *filp)
51{
52 struct msm_buspm_map_dev *dev = filp->private_data;
53
54 return (dev) ? dev->paddr : 0L;
55}
56
57static void msm_buspm_dev_free(struct file *filp)
58{
59 struct msm_buspm_map_dev *dev = filp->private_data;
60
61 if (dev) {
62 pr_debug("freeing memory at 0x%p\n", dev->vaddr);
63 free_contiguous_memory(dev->vaddr);
64 dev->paddr = 0L;
65 dev->vaddr = NULL;
66 }
67}
68
69static int msm_buspm_dev_open(struct inode *inode, struct file *filp)
70{
71 struct msm_buspm_map_dev *dev;
72
73 if (capable(CAP_SYS_ADMIN)) {
74 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
75 if (dev)
76 filp->private_data = dev;
77 else
78 return -ENOMEM;
79 } else {
80 return -EPERM;
81 }
82
83 return 0;
84}
85
86static int
87msm_buspm_dev_alloc(struct file *filp, struct buspm_alloc_params data)
88{
89 unsigned long paddr;
90 void *vaddr;
91 struct msm_buspm_map_dev *dev = filp->private_data;
92
93 /* If buffer already allocated, then free it */
94 if (dev->vaddr)
95 msm_buspm_dev_free(filp);
96
97 /* Allocate uncached memory */
98 vaddr = allocate_contiguous_ebi(data.size, PAGE_SIZE, 0);
99 paddr = (vaddr) ? memory_pool_node_paddr(vaddr) : 0L;
100
101 if (vaddr == NULL) {
102 pr_err("allocation of 0x%x bytes failed", data.size);
103 return -ENOMEM;
104 }
105
106 dev->vaddr = vaddr;
107 dev->paddr = paddr;
108 dev->buflen = data.size;
109 filp->f_pos = 0;
110 pr_debug("virt addr = 0x%p\n", dev->vaddr);
111 pr_debug("phys addr = 0x%lx\n", dev->paddr);
112
113 return 0;
114}
115
116static long
117msm_buspm_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
118{
119 struct buspm_xfer_req xfer;
120 struct buspm_alloc_params alloc_data;
121 unsigned long paddr;
122 int retval = 0;
123 void *buf = msm_buspm_dev_get_vaddr(filp);
Gagan Macb315a202012-07-18 15:32:10 -0600124 unsigned int buflen = msm_buspm_dev_get_buflen(filp);
Harini Jayaramanef7805f2011-09-28 12:45:31 -0600125 unsigned char *dbgbuf = buf;
126
127 switch (cmd) {
128 case MSM_BUSPM_IOC_FREE:
129 pr_debug("cmd = 0x%x (FREE)\n", cmd);
130 msm_buspm_dev_free(filp);
131 break;
132
133 case MSM_BUSPM_IOC_ALLOC:
134 pr_debug("cmd = 0x%x (ALLOC)\n", cmd);
135 retval = __get_user(alloc_data.size, (size_t __user *)arg);
136
137 if (retval == 0)
138 retval = msm_buspm_dev_alloc(filp, alloc_data);
139 break;
140
141 case MSM_BUSPM_IOC_RD_PHYS_ADDR:
142 pr_debug("Read Physical Address\n");
143 paddr = msm_buspm_dev_get_paddr(filp);
144 if (paddr == 0L) {
145 retval = -EINVAL;
146 } else {
147 pr_debug("phys addr = 0x%lx\n", paddr);
148 retval = __put_user(paddr,
149 (unsigned long __user *)arg);
150 }
151 break;
152
153 case MSM_BUSPM_IOC_RDBUF:
154 pr_debug("Read Buffer: 0x%x%x%x%x\n",
155 dbgbuf[0], dbgbuf[1], dbgbuf[2], dbgbuf[3]);
156
157 if (!buf) {
158 retval = -EINVAL;
159 break;
160 }
161
162 if (copy_from_user(&xfer, (void __user *)arg, sizeof(xfer))) {
163 retval = -EFAULT;
164 break;
165 }
166
Gagan Macb315a202012-07-18 15:32:10 -0600167 if ((xfer.size <= buflen) &&
Harini Jayaramanef7805f2011-09-28 12:45:31 -0600168 (copy_to_user((void __user *)xfer.data, buf,
169 xfer.size))) {
170 retval = -EFAULT;
171 break;
172 }
173 break;
174
175 case MSM_BUSPM_IOC_WRBUF:
176 pr_debug("Write Buffer\n");
177
178 if (!buf) {
179 retval = -EINVAL;
180 break;
181 }
182
183 if (copy_from_user(&xfer, (void __user *)arg, sizeof(xfer))) {
184 retval = -EFAULT;
185 break;
186 }
187
Gagan Macb315a202012-07-18 15:32:10 -0600188 if ((buflen <= xfer.size) &&
Harini Jayaramanef7805f2011-09-28 12:45:31 -0600189 (copy_from_user(buf, (void __user *)xfer.data,
190 xfer.size))) {
191 retval = -EFAULT;
192 break;
193 }
194 break;
195
196 default:
197 pr_debug("Unknown command 0x%x\n", cmd);
198 retval = -EINVAL;
199 break;
200 }
201
202 return retval;
203}
204
205static int msm_buspm_dev_release(struct inode *inode, struct file *filp)
206{
207 struct msm_buspm_map_dev *dev = filp->private_data;
208
209 msm_buspm_dev_free(filp);
210 kfree(dev);
211 filp->private_data = NULL;
212
213 return 0;
214}
215
216static int msm_buspm_dev_mmap(struct file *filp, struct vm_area_struct *vma)
217{
218 pr_debug("vma = 0x%p\n", vma);
219
220 /* Mappings are uncached */
221 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
222 if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
223 vma->vm_end - vma->vm_start, vma->vm_page_prot))
224 return -EFAULT;
225
226 return 0;
227}
228
229static const struct file_operations msm_buspm_dev_fops = {
230 .owner = THIS_MODULE,
231 .mmap = msm_buspm_dev_mmap,
232 .open = msm_buspm_dev_open,
233 .unlocked_ioctl = msm_buspm_dev_ioctl,
234 .llseek = noop_llseek,
235 .release = msm_buspm_dev_release,
236};
237
238struct miscdevice msm_buspm_misc = {
239 .minor = MISC_DYNAMIC_MINOR,
240 .name = MSM_BUSPM_DRV_NAME,
241 .fops = &msm_buspm_dev_fops,
242};
243
244static int __init msm_buspm_dev_init(void)
245{
246 int ret = 0;
247
248 ret = misc_register(&msm_buspm_misc);
249 if (ret < 0)
250 pr_err("%s: Cannot register misc device\n", __func__);
251
252 return ret;
253}
254
255static void __exit msm_buspm_dev_exit(void)
256{
257 misc_deregister(&msm_buspm_misc);
258}
259module_init(msm_buspm_dev_init);
260module_exit(msm_buspm_dev_exit);
261
262MODULE_LICENSE("GPL v2");
263MODULE_VERSION("1.0");
264MODULE_ALIAS("platform:"MSM_BUSPM_DRV_NAME);