Branch data Line data Source code
1 : : /*
2 : : * Copyright 2010 - 2011 Broadcom Corporation. All rights reserved.
3 : : *
4 : : * Unless you and Broadcom execute a separate written software license
5 : : * agreement governing use of this software, this software is licensed to you
6 : : * under the terms of the GNU General Public License version 2, available at
7 : : * http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
8 : : *
9 : : * Notwithstanding the above, under no circumstances may you combine this
10 : : * software in any way with any other Broadcom software provided under a
11 : : * license other than the GPL, without Broadcom's express prior written
12 : : * consent.
13 : : */
14 : :
15 : : #include <linux/kernel.h>
16 : : #include <linux/module.h>
17 : : #include <linux/fs.h>
18 : : #include <linux/device.h>
19 : : #include <linux/cdev.h>
20 : : #include <linux/mm.h>
21 : : #include <linux/slab.h>
22 : : #include <linux/debugfs.h>
23 : : #include <linux/uaccess.h>
24 : : #include <linux/dma-mapping.h>
25 : : #include <linux/broadcom/vc_mem.h>
26 : :
27 : : #define DRIVER_NAME "vc-mem"
28 : :
29 : : /* Device (/dev) related variables */
30 : : static dev_t vc_mem_devnum;
31 : : static struct class *vc_mem_class;
32 : : static struct cdev vc_mem_cdev;
33 : : static int vc_mem_inited;
34 : :
35 : : #ifdef CONFIG_DEBUG_FS
36 : : static struct dentry *vc_mem_debugfs_entry;
37 : : #endif
38 : :
39 : : /*
40 : : * Videocore memory addresses and size
41 : : *
42 : : * Drivers that wish to know the videocore memory addresses and sizes should
43 : : * use these variables instead of the MM_IO_BASE and MM_ADDR_IO defines in
44 : : * headers. This allows the other drivers to not be tied down to a a certain
45 : : * address/size at compile time.
46 : : *
47 : : * In the future, the goal is to have the videocore memory virtual address and
48 : : * size be calculated at boot time rather than at compile time. The decision of
49 : : * where the videocore memory resides and its size would be in the hands of the
50 : : * bootloader (and/or kernel). When that happens, the values of these variables
51 : : * would be calculated and assigned in the init function.
52 : : */
53 : : /* In the 2835 VC in mapped above ARM, but ARM has full access to VC space */
54 : : unsigned long mm_vc_mem_phys_addr;
55 : : EXPORT_SYMBOL(mm_vc_mem_phys_addr);
56 : : unsigned int mm_vc_mem_size;
57 : : EXPORT_SYMBOL(mm_vc_mem_size);
58 : : unsigned int mm_vc_mem_base;
59 : : EXPORT_SYMBOL(mm_vc_mem_base);
60 : :
61 : : static uint phys_addr;
62 : : static uint mem_size;
63 : : static uint mem_base;
64 : :
65 : : static int
66 : 0 : vc_mem_open(struct inode *inode, struct file *file)
67 : : {
68 : : (void)inode;
69 : :
70 : : pr_debug("%s: called file = 0x%p\n", __func__, file);
71 : :
72 : 0 : return 0;
73 : : }
74 : :
75 : : static int
76 : 0 : vc_mem_release(struct inode *inode, struct file *file)
77 : : {
78 : : (void)inode;
79 : :
80 : : pr_debug("%s: called file = 0x%p\n", __func__, file);
81 : :
82 : 0 : return 0;
83 : : }
84 : :
85 : : static void
86 : : vc_mem_get_size(void)
87 : : {
88 : : }
89 : :
90 : : static void
91 : : vc_mem_get_base(void)
92 : : {
93 : : }
94 : :
95 : : int
96 : 0 : vc_mem_get_current_size(void)
97 : : {
98 : 0 : return mm_vc_mem_size;
99 : : }
100 : : EXPORT_SYMBOL_GPL(vc_mem_get_current_size);
101 : :
102 : : static long
103 : 0 : vc_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
104 : : {
105 : : int rc = 0;
106 : :
107 : : (void) cmd;
108 : : (void) arg;
109 : :
110 : : pr_debug("%s: called file = 0x%p, cmd %08x\n", __func__, file, cmd);
111 : :
112 [ # # # # : 0 : switch (cmd) {
# ]
113 : : case VC_MEM_IOC_MEM_PHYS_ADDR:
114 : : {
115 : : pr_debug("%s: VC_MEM_IOC_MEM_PHYS_ADDR=0x%p\n",
116 : : __func__, (void *)mm_vc_mem_phys_addr);
117 : :
118 [ # # ]: 0 : if (copy_to_user((void *)arg, &mm_vc_mem_phys_addr,
119 : : sizeof(mm_vc_mem_phys_addr))) {
120 : : rc = -EFAULT;
121 : : }
122 : : break;
123 : : }
124 : : case VC_MEM_IOC_MEM_SIZE:
125 : : {
126 : : /* Get the videocore memory size first */
127 : : vc_mem_get_size();
128 : :
129 : : pr_debug("%s: VC_MEM_IOC_MEM_SIZE=%x\n", __func__,
130 : : mm_vc_mem_size);
131 : :
132 [ # # ]: 0 : if (copy_to_user((void *)arg, &mm_vc_mem_size,
133 : : sizeof(mm_vc_mem_size))) {
134 : : rc = -EFAULT;
135 : : }
136 : : break;
137 : : }
138 : : case VC_MEM_IOC_MEM_BASE:
139 : : {
140 : : /* Get the videocore memory base */
141 : : vc_mem_get_base();
142 : :
143 : : pr_debug("%s: VC_MEM_IOC_MEM_BASE=%x\n", __func__,
144 : : mm_vc_mem_base);
145 : :
146 [ # # ]: 0 : if (copy_to_user((void *)arg, &mm_vc_mem_base,
147 : : sizeof(mm_vc_mem_base))) {
148 : : rc = -EFAULT;
149 : : }
150 : : break;
151 : : }
152 : : case VC_MEM_IOC_MEM_LOAD:
153 : : {
154 : : /* Get the videocore memory base */
155 : : vc_mem_get_base();
156 : :
157 : : pr_debug("%s: VC_MEM_IOC_MEM_LOAD=%x\n", __func__,
158 : : mm_vc_mem_base);
159 : :
160 [ # # ]: 0 : if (copy_to_user((void *)arg, &mm_vc_mem_base,
161 : : sizeof(mm_vc_mem_base))) {
162 : : rc = -EFAULT;
163 : : }
164 : : break;
165 : : }
166 : : default:
167 : : {
168 : : return -ENOTTY;
169 : : }
170 : : }
171 : : pr_debug("%s: file = 0x%p returning %d\n", __func__, file, rc);
172 : :
173 : 0 : return rc;
174 : : }
175 : :
176 : : #ifdef CONFIG_COMPAT
177 : : static long
178 : : vc_mem_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
179 : : {
180 : : int rc = 0;
181 : :
182 : : switch (cmd) {
183 : : case VC_MEM_IOC_MEM_PHYS_ADDR32:
184 : : pr_debug("%s: VC_MEM_IOC_MEM_PHYS_ADDR32=0x%p\n",
185 : : __func__, (void *)mm_vc_mem_phys_addr);
186 : :
187 : : /* This isn't correct, but will cover us for now as
188 : : * VideoCore is 32bit only.
189 : : */
190 : : if (copy_to_user((void *)arg, &mm_vc_mem_phys_addr,
191 : : sizeof(compat_ulong_t)))
192 : : rc = -EFAULT;
193 : :
194 : : break;
195 : :
196 : : default:
197 : : rc = vc_mem_ioctl(file, cmd, arg);
198 : : break;
199 : : }
200 : :
201 : : return rc;
202 : : }
203 : : #endif
204 : :
205 : : static int
206 : 0 : vc_mem_mmap(struct file *filp, struct vm_area_struct *vma)
207 : : {
208 : : int rc = 0;
209 : 0 : unsigned long length = vma->vm_end - vma->vm_start;
210 : 0 : unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
211 : :
212 : : pr_debug("%s: vm_start = 0x%08lx vm_end = 0x%08lx vm_pgoff = 0x%08lx\n",
213 : : __func__, (long)vma->vm_start, (long)vma->vm_end,
214 : : (long)vma->vm_pgoff);
215 : :
216 [ # # ]: 0 : if (offset + length > mm_vc_mem_size) {
217 : 0 : pr_err("%s: length %ld is too big\n", __func__, length);
218 : 0 : return -EINVAL;
219 : : }
220 : : /* Do not cache the memory map */
221 : 0 : vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
222 : :
223 : 0 : rc = remap_pfn_range(vma, vma->vm_start,
224 : 0 : (mm_vc_mem_phys_addr >> PAGE_SHIFT) +
225 : : vma->vm_pgoff, length, vma->vm_page_prot);
226 [ # # ]: 0 : if (rc)
227 : 0 : pr_err("%s: remap_pfn_range failed (rc=%d)\n", __func__, rc);
228 : :
229 : 0 : return rc;
230 : : }
231 : :
232 : : /* File Operations for the driver. */
233 : : static const struct file_operations vc_mem_fops = {
234 : : .owner = THIS_MODULE,
235 : : .open = vc_mem_open,
236 : : .release = vc_mem_release,
237 : : .unlocked_ioctl = vc_mem_ioctl,
238 : : #ifdef CONFIG_COMPAT
239 : : .compat_ioctl = vc_mem_compat_ioctl,
240 : : #endif
241 : : .mmap = vc_mem_mmap,
242 : : };
243 : :
244 : : #ifdef CONFIG_DEBUG_FS
245 : : static void vc_mem_debugfs_deinit(void)
246 : : {
247 : 0 : debugfs_remove_recursive(vc_mem_debugfs_entry);
248 : 0 : vc_mem_debugfs_entry = NULL;
249 : : }
250 : :
251 : :
252 : 207 : static int vc_mem_debugfs_init(
253 : : struct device *dev)
254 : : {
255 : 207 : vc_mem_debugfs_entry = debugfs_create_dir(DRIVER_NAME, NULL);
256 [ - + ]: 207 : if (!vc_mem_debugfs_entry) {
257 : 0 : dev_warn(dev, "could not create debugfs entry\n");
258 : 0 : return -EFAULT;
259 : : }
260 : :
261 [ - + ]: 207 : if (!debugfs_create_x32("vc_mem_phys_addr",
262 : : 0444,
263 : : vc_mem_debugfs_entry,
264 : : (u32 *)&mm_vc_mem_phys_addr)) {
265 : 0 : dev_warn(dev, "%s:could not create vc_mem_phys entry\n",
266 : : __func__);
267 : 0 : goto fail;
268 : : }
269 : :
270 [ - + ]: 207 : if (!debugfs_create_x32("vc_mem_size",
271 : : 0444,
272 : : vc_mem_debugfs_entry,
273 : : (u32 *)&mm_vc_mem_size)) {
274 : 0 : dev_warn(dev, "%s:could not create vc_mem_size entry\n",
275 : : __func__);
276 : 0 : goto fail;
277 : : }
278 : :
279 [ - + ]: 207 : if (!debugfs_create_x32("vc_mem_base",
280 : : 0444,
281 : : vc_mem_debugfs_entry,
282 : : (u32 *)&mm_vc_mem_base)) {
283 : 0 : dev_warn(dev, "%s:could not create vc_mem_base entry\n",
284 : : __func__);
285 : 0 : goto fail;
286 : : }
287 : :
288 : : return 0;
289 : :
290 : : fail:
291 : : vc_mem_debugfs_deinit();
292 : 0 : return -EFAULT;
293 : : }
294 : :
295 : : #endif /* CONFIG_DEBUG_FS */
296 : :
297 : : /* Module load/unload functions */
298 : :
299 : : static int __init
300 : 207 : vc_mem_init(void)
301 : : {
302 : : int rc = -EFAULT;
303 : : struct device *dev;
304 : :
305 : : pr_debug("%s: called\n", __func__);
306 : :
307 : 207 : mm_vc_mem_phys_addr = phys_addr;
308 : 207 : mm_vc_mem_size = mem_size;
309 : 207 : mm_vc_mem_base = mem_base;
310 : :
311 : : vc_mem_get_size();
312 : :
313 : 207 : pr_info("vc-mem: phys_addr:0x%08lx mem_base=0x%08x mem_size:0x%08x(%u MiB)\n",
314 : : mm_vc_mem_phys_addr, mm_vc_mem_base, mm_vc_mem_size,
315 : : mm_vc_mem_size / (1024 * 1024));
316 : :
317 : 207 : rc = alloc_chrdev_region(&vc_mem_devnum, 0, 1, DRIVER_NAME);
318 [ - + ]: 207 : if (rc < 0) {
319 : 0 : pr_err("%s: alloc_chrdev_region failed (rc=%d)\n",
320 : : __func__, rc);
321 : 0 : goto out_err;
322 : : }
323 : :
324 : 207 : cdev_init(&vc_mem_cdev, &vc_mem_fops);
325 : 207 : rc = cdev_add(&vc_mem_cdev, vc_mem_devnum, 1);
326 [ - + ]: 207 : if (rc) {
327 : 0 : pr_err("%s: cdev_add failed (rc=%d)\n", __func__, rc);
328 : 0 : goto out_unregister;
329 : : }
330 : :
331 : 207 : vc_mem_class = class_create(THIS_MODULE, DRIVER_NAME);
332 [ - + ]: 207 : if (IS_ERR(vc_mem_class)) {
333 : : rc = PTR_ERR(vc_mem_class);
334 : 0 : pr_err("%s: class_create failed (rc=%d)\n", __func__, rc);
335 : 0 : goto out_cdev_del;
336 : : }
337 : :
338 : 207 : dev = device_create(vc_mem_class, NULL, vc_mem_devnum, NULL,
339 : : DRIVER_NAME);
340 [ - + ]: 207 : if (IS_ERR(dev)) {
341 : : rc = PTR_ERR(dev);
342 : 0 : pr_err("%s: device_create failed (rc=%d)\n", __func__, rc);
343 : : goto out_class_destroy;
344 : : }
345 : :
346 : : #ifdef CONFIG_DEBUG_FS
347 : : /* don't fail if the debug entries cannot be created */
348 : 207 : vc_mem_debugfs_init(dev);
349 : : #endif
350 : :
351 : 207 : vc_mem_inited = 1;
352 : 207 : return 0;
353 : :
354 : : device_destroy(vc_mem_class, vc_mem_devnum);
355 : :
356 : : out_class_destroy:
357 : 0 : class_destroy(vc_mem_class);
358 : 0 : vc_mem_class = NULL;
359 : :
360 : : out_cdev_del:
361 : 0 : cdev_del(&vc_mem_cdev);
362 : :
363 : : out_unregister:
364 : 0 : unregister_chrdev_region(vc_mem_devnum, 1);
365 : :
366 : : out_err:
367 : : return -1;
368 : : }
369 : :
370 : : static void __exit
371 : 0 : vc_mem_exit(void)
372 : : {
373 : : pr_debug("%s: called\n", __func__);
374 : :
375 [ # # ]: 0 : if (vc_mem_inited) {
376 : : #if CONFIG_DEBUG_FS
377 : : vc_mem_debugfs_deinit();
378 : : #endif
379 : 0 : device_destroy(vc_mem_class, vc_mem_devnum);
380 : 0 : class_destroy(vc_mem_class);
381 : 0 : cdev_del(&vc_mem_cdev);
382 : 0 : unregister_chrdev_region(vc_mem_devnum, 1);
383 : : }
384 : 0 : }
385 : :
386 : : module_init(vc_mem_init);
387 : : module_exit(vc_mem_exit);
388 : : MODULE_LICENSE("GPL");
389 : : MODULE_AUTHOR("Broadcom Corporation");
390 : :
391 : : module_param(phys_addr, uint, 0644);
392 : : module_param(mem_size, uint, 0644);
393 : : module_param(mem_base, uint, 0644);
|