1326 lines
34 KiB
Diff
1326 lines
34 KiB
Diff
|
From 87b48ac64b6b2aeb97b53eee080e74cb83ff25fe Mon Sep 17 00:00:00 2001
|
||
|
From: popcornmix <popcornmix@gmail.com>
|
||
|
Date: Mon, 19 Nov 2012 18:27:05 +0000
|
||
|
Subject: [PATCH 041/174] Add Simon Hall's dma helper module, useful in future
|
||
|
for X acceleration
|
||
|
|
||
|
---
|
||
|
arch/arm/mach-bcm2708/Kconfig | 8 +
|
||
|
arch/arm/mach-bcm2708/Makefile | 3 +
|
||
|
arch/arm/mach-bcm2708/dmaer.c | 887 ++++++++++++++++++++++++
|
||
|
arch/arm/mach-bcm2708/include/mach/vc_support.h | 69 ++
|
||
|
arch/arm/mach-bcm2708/vc_support.c | 319 +++++++++
|
||
|
5 files changed, 1286 insertions(+)
|
||
|
create mode 100755 arch/arm/mach-bcm2708/dmaer.c
|
||
|
create mode 100755 arch/arm/mach-bcm2708/include/mach/vc_support.h
|
||
|
create mode 100755 arch/arm/mach-bcm2708/vc_support.c
|
||
|
|
||
|
--- a/arch/arm/mach-bcm2708/Kconfig
|
||
|
+++ b/arch/arm/mach-bcm2708/Kconfig
|
||
|
@@ -38,4 +38,12 @@ config BCM2708_SPIDEV
|
||
|
default y
|
||
|
help
|
||
|
Binds spidev driver to the SPI0 master
|
||
|
+
|
||
|
+config BCM2708_DMAER
|
||
|
+ tristate "BCM2708 DMA helper"
|
||
|
+ depends on MACH_BCM2708
|
||
|
+ default n
|
||
|
+ help
|
||
|
+ Enable DMA helper for accelerating X composition
|
||
|
+
|
||
|
endmenu
|
||
|
--- a/arch/arm/mach-bcm2708/Makefile
|
||
|
+++ b/arch/arm/mach-bcm2708/Makefile
|
||
|
@@ -6,3 +6,6 @@ obj-$(CONFIG_MACH_BCM2708) += clock.o b
|
||
|
obj-$(CONFIG_BCM2708_GPIO) += bcm2708_gpio.o
|
||
|
obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o
|
||
|
|
||
|
+obj-$(CONFIG_BCM2708_DMAER) += dmaer_master.o
|
||
|
+dmaer_master-objs := dmaer.o vc_support.o
|
||
|
+
|
||
|
--- /dev/null
|
||
|
+++ b/arch/arm/mach-bcm2708/dmaer.c
|
||
|
@@ -0,0 +1,887 @@
|
||
|
+#include <linux/init.h>
|
||
|
+#include <linux/sched.h>
|
||
|
+#include <linux/module.h>
|
||
|
+#include <linux/types.h>
|
||
|
+#include <linux/kdev_t.h>
|
||
|
+#include <linux/fs.h>
|
||
|
+#include <linux/cdev.h>
|
||
|
+#include <linux/mm.h>
|
||
|
+#include <linux/slab.h>
|
||
|
+#include <linux/pagemap.h>
|
||
|
+#include <linux/device.h>
|
||
|
+#include <linux/jiffies.h>
|
||
|
+#include <linux/timex.h>
|
||
|
+#include <linux/dma-mapping.h>
|
||
|
+
|
||
|
+#include <asm/uaccess.h>
|
||
|
+#include <asm/atomic.h>
|
||
|
+#include <asm/cacheflush.h>
|
||
|
+#include <asm/io.h>
|
||
|
+
|
||
|
+#include <mach/dma.h>
|
||
|
+#include <mach/vc_support.h>
|
||
|
+
|
||
|
+#ifdef ECLIPSE_IGNORE
|
||
|
+
|
||
|
+#define __user
|
||
|
+#define __init
|
||
|
+#define __exit
|
||
|
+#define __iomem
|
||
|
+#define KERN_DEBUG
|
||
|
+#define KERN_ERR
|
||
|
+#define KERN_WARNING
|
||
|
+#define KERN_INFO
|
||
|
+#define _IOWR(a, b, c) b
|
||
|
+#define _IOW(a, b, c) b
|
||
|
+#define _IO(a, b) b
|
||
|
+
|
||
|
+#endif
|
||
|
+
|
||
|
+//#define inline
|
||
|
+
|
||
|
+#define PRINTK(args...) printk(args)
|
||
|
+//#define PRINTK_VERBOSE(args...) printk(args)
|
||
|
+//#define PRINTK(args...)
|
||
|
+#define PRINTK_VERBOSE(args...)
|
||
|
+
|
||
|
+/***** TYPES ****/
|
||
|
+#define PAGES_PER_LIST 500
|
||
|
+struct PageList
|
||
|
+{
|
||
|
+ struct page *m_pPages[PAGES_PER_LIST];
|
||
|
+ unsigned int m_used;
|
||
|
+ struct PageList *m_pNext;
|
||
|
+};
|
||
|
+
|
||
|
+struct VmaPageList
|
||
|
+{
|
||
|
+ //each vma has a linked list of pages associated with it
|
||
|
+ struct PageList *m_pPageHead;
|
||
|
+ struct PageList *m_pPageTail;
|
||
|
+ unsigned int m_refCount;
|
||
|
+};
|
||
|
+
|
||
|
+struct DmaControlBlock
|
||
|
+{
|
||
|
+ unsigned int m_transferInfo;
|
||
|
+ void __user *m_pSourceAddr;
|
||
|
+ void __user *m_pDestAddr;
|
||
|
+ unsigned int m_xferLen;
|
||
|
+ unsigned int m_tdStride;
|
||
|
+ struct DmaControlBlock *m_pNext;
|
||
|
+ unsigned int m_blank1, m_blank2;
|
||
|
+};
|
||
|
+
|
||
|
+/***** DEFINES ******/
|
||
|
+//magic number defining the module
|
||
|
+#define DMA_MAGIC 0xdd
|
||
|
+
|
||
|
+//do user virtual to physical translation of the CB chain
|
||
|
+#define DMA_PREPARE _IOWR(DMA_MAGIC, 0, struct DmaControlBlock *)
|
||
|
+
|
||
|
+//kick the pre-prepared CB chain
|
||
|
+#define DMA_KICK _IOW(DMA_MAGIC, 1, struct DmaControlBlock *)
|
||
|
+
|
||
|
+//prepare it, kick it, wait for it
|
||
|
+#define DMA_PREPARE_KICK_WAIT _IOWR(DMA_MAGIC, 2, struct DmaControlBlock *)
|
||
|
+
|
||
|
+//prepare it, kick it, don't wait for it
|
||
|
+#define DMA_PREPARE_KICK _IOWR(DMA_MAGIC, 3, struct DmaControlBlock *)
|
||
|
+
|
||
|
+//not currently implemented
|
||
|
+#define DMA_WAIT_ONE _IO(DMA_MAGIC, 4, struct DmaControlBlock *)
|
||
|
+
|
||
|
+//wait on all kicked CB chains
|
||
|
+#define DMA_WAIT_ALL _IO(DMA_MAGIC, 5)
|
||
|
+
|
||
|
+//in order to discover the largest AXI burst that should be programmed into the transfer params
|
||
|
+#define DMA_MAX_BURST _IO(DMA_MAGIC, 6)
|
||
|
+
|
||
|
+//set the address range through which the user address is assumed to already by a physical address
|
||
|
+#define DMA_SET_MIN_PHYS _IOW(DMA_MAGIC, 7, unsigned long)
|
||
|
+#define DMA_SET_MAX_PHYS _IOW(DMA_MAGIC, 8, unsigned long)
|
||
|
+#define DMA_SET_PHYS_OFFSET _IOW(DMA_MAGIC, 9, unsigned long)
|
||
|
+
|
||
|
+//used to define the size for the CMA-based allocation *in pages*, can only be done once once the file is opened
|
||
|
+#define DMA_CMA_SET_SIZE _IOW(DMA_MAGIC, 10, unsigned long)
|
||
|
+
|
||
|
+//used to get the version of the module, to test for a capability
|
||
|
+#define DMA_GET_VERSION _IO(DMA_MAGIC, 99)
|
||
|
+
|
||
|
+#define VERSION_NUMBER 1
|
||
|
+
|
||
|
+#define VIRT_TO_BUS_CACHE_SIZE 8
|
||
|
+
|
||
|
+/***** FILE OPS *****/
|
||
|
+static int Open(struct inode *pInode, struct file *pFile);
|
||
|
+static int Release(struct inode *pInode, struct file *pFile);
|
||
|
+static long Ioctl(struct file *pFile, unsigned int cmd, unsigned long arg);
|
||
|
+static ssize_t Read(struct file *pFile, char __user *pUser, size_t count, loff_t *offp);
|
||
|
+static int Mmap(struct file *pFile, struct vm_area_struct *pVma);
|
||
|
+
|
||
|
+/***** VMA OPS ****/
|
||
|
+static void VmaOpen4k(struct vm_area_struct *pVma);
|
||
|
+static void VmaClose4k(struct vm_area_struct *pVma);
|
||
|
+static int VmaFault4k(struct vm_area_struct *pVma, struct vm_fault *pVmf);
|
||
|
+
|
||
|
+/**** DMA PROTOTYPES */
|
||
|
+static struct DmaControlBlock __user *DmaPrepare(struct DmaControlBlock __user *pUserCB, int *pError);
|
||
|
+static int DmaKick(struct DmaControlBlock __user *pUserCB);
|
||
|
+static void DmaWaitAll(void);
|
||
|
+
|
||
|
+/**** GENERIC ****/
|
||
|
+static int __init dmaer_init(void);
|
||
|
+static void __exit dmaer_exit(void);
|
||
|
+
|
||
|
+/*** OPS ***/
|
||
|
+static struct vm_operations_struct g_vmOps4k = {
|
||
|
+ .open = VmaOpen4k,
|
||
|
+ .close = VmaClose4k,
|
||
|
+ .fault = VmaFault4k,
|
||
|
+};
|
||
|
+
|
||
|
+static struct file_operations g_fOps = {
|
||
|
+ .owner = THIS_MODULE,
|
||
|
+ .llseek = 0,
|
||
|
+ .read = Read,
|
||
|
+ .write = 0,
|
||
|
+ .unlocked_ioctl = Ioctl,
|
||
|
+ .open = Open,
|
||
|
+ .release = Release,
|
||
|
+ .mmap = Mmap,
|
||
|
+};
|
||
|
+
|
||
|
+/***** GLOBALS ******/
|
||
|
+static dev_t g_majorMinor;
|
||
|
+
|
||
|
+//tracking usage of the two files
|
||
|
+static atomic_t g_oneLock4k = ATOMIC_INIT(1);
|
||
|
+
|
||
|
+//device operations
|
||
|
+static struct cdev g_cDev;
|
||
|
+static int g_trackedPages = 0;
|
||
|
+
|
||
|
+//dma control
|
||
|
+static unsigned int *g_pDmaChanBase;
|
||
|
+static int g_dmaIrq;
|
||
|
+static int g_dmaChan;
|
||
|
+
|
||
|
+//cma allocation
|
||
|
+static int g_cmaHandle;
|
||
|
+
|
||
|
+//user virtual to bus address translation acceleration
|
||
|
+static unsigned long g_virtAddr[VIRT_TO_BUS_CACHE_SIZE];
|
||
|
+static unsigned long g_busAddr[VIRT_TO_BUS_CACHE_SIZE];
|
||
|
+static unsigned long g_cbVirtAddr;
|
||
|
+static unsigned long g_cbBusAddr;
|
||
|
+static int g_cacheInsertAt;
|
||
|
+static int g_cacheHit, g_cacheMiss;
|
||
|
+
|
||
|
+//off by default
|
||
|
+static void __user *g_pMinPhys;
|
||
|
+static void __user *g_pMaxPhys;
|
||
|
+static unsigned long g_physOffset;
|
||
|
+
|
||
|
+/****** CACHE OPERATIONS ********/
|
||
|
+static inline void FlushAddrCache(void)
|
||
|
+{
|
||
|
+ int count = 0;
|
||
|
+ for (count = 0; count < VIRT_TO_BUS_CACHE_SIZE; count++)
|
||
|
+ g_virtAddr[count] = 0xffffffff; //never going to match as we always chop the bottom bits anyway
|
||
|
+
|
||
|
+ g_cbVirtAddr = 0xffffffff;
|
||
|
+
|
||
|
+ g_cacheInsertAt = 0;
|
||
|
+}
|
||
|
+
|
||
|
+//translate from a user virtual address to a bus address by mapping the page
|
||
|
+//NB this won't lock a page in memory, so to avoid potential paging issues using kernel logical addresses
|
||
|
+static inline void __iomem *UserVirtualToBus(void __user *pUser)
|
||
|
+{
|
||
|
+ int mapped;
|
||
|
+ struct page *pPage;
|
||
|
+ void *phys;
|
||
|
+
|
||
|
+ //map it (requiring that the pointer points to something that does not hang off the page boundary)
|
||
|
+ mapped = get_user_pages(current, current->mm,
|
||
|
+ (unsigned long)pUser, 1,
|
||
|
+ 1, 0,
|
||
|
+ &pPage,
|
||
|
+ 0);
|
||
|
+
|
||
|
+ if (mapped <= 0) //error
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "user virtual %p arm phys %p bus %p\n",
|
||
|
+ pUser, page_address(pPage), (void __iomem *)__virt_to_bus(page_address(pPage)));
|
||
|
+
|
||
|
+ //get the arm physical address
|
||
|
+ phys = page_address(pPage) + offset_in_page(pUser);
|
||
|
+ page_cache_release(pPage);
|
||
|
+
|
||
|
+ //and now the bus address
|
||
|
+ return (void __iomem *)__virt_to_bus(phys);
|
||
|
+}
|
||
|
+
|
||
|
+static inline void __iomem *UserVirtualToBusViaCbCache(void __user *pUser)
|
||
|
+{
|
||
|
+ unsigned long virtual_page = (unsigned long)pUser & ~4095;
|
||
|
+ unsigned long page_offset = (unsigned long)pUser & 4095;
|
||
|
+ unsigned long bus_addr;
|
||
|
+
|
||
|
+ if (g_cbVirtAddr == virtual_page)
|
||
|
+ {
|
||
|
+ bus_addr = g_cbBusAddr + page_offset;
|
||
|
+ g_cacheHit++;
|
||
|
+ return (void __iomem *)bus_addr;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ bus_addr = (unsigned long)UserVirtualToBus(pUser);
|
||
|
+
|
||
|
+ if (!bus_addr)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ g_cbVirtAddr = virtual_page;
|
||
|
+ g_cbBusAddr = bus_addr & ~4095;
|
||
|
+ g_cacheMiss++;
|
||
|
+
|
||
|
+ return (void __iomem *)bus_addr;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+//do the same as above, by query our virt->bus cache
|
||
|
+static inline void __iomem *UserVirtualToBusViaCache(void __user *pUser)
|
||
|
+{
|
||
|
+ int count;
|
||
|
+ //get the page and its offset
|
||
|
+ unsigned long virtual_page = (unsigned long)pUser & ~4095;
|
||
|
+ unsigned long page_offset = (unsigned long)pUser & 4095;
|
||
|
+ unsigned long bus_addr;
|
||
|
+
|
||
|
+ if (pUser >= g_pMinPhys && pUser < g_pMaxPhys)
|
||
|
+ {
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "user->phys passthrough on %p\n", pUser);
|
||
|
+ return (void __iomem *)((unsigned long)pUser + g_physOffset);
|
||
|
+ }
|
||
|
+
|
||
|
+ //check the cache for our entry
|
||
|
+ for (count = 0; count < VIRT_TO_BUS_CACHE_SIZE; count++)
|
||
|
+ if (g_virtAddr[count] == virtual_page)
|
||
|
+ {
|
||
|
+ bus_addr = g_busAddr[count] + page_offset;
|
||
|
+ g_cacheHit++;
|
||
|
+ return (void __iomem *)bus_addr;
|
||
|
+ }
|
||
|
+
|
||
|
+ //not found, look up manually and then insert its page address
|
||
|
+ bus_addr = (unsigned long)UserVirtualToBus(pUser);
|
||
|
+
|
||
|
+ if (!bus_addr)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ g_virtAddr[g_cacheInsertAt] = virtual_page;
|
||
|
+ g_busAddr[g_cacheInsertAt] = bus_addr & ~4095;
|
||
|
+
|
||
|
+ //round robin
|
||
|
+ g_cacheInsertAt++;
|
||
|
+ if (g_cacheInsertAt == VIRT_TO_BUS_CACHE_SIZE)
|
||
|
+ g_cacheInsertAt = 0;
|
||
|
+
|
||
|
+ g_cacheMiss++;
|
||
|
+
|
||
|
+ return (void __iomem *)bus_addr;
|
||
|
+}
|
||
|
+
|
||
|
+/***** FILE OPERATIONS ****/
|
||
|
+static int Open(struct inode *pInode, struct file *pFile)
|
||
|
+{
|
||
|
+ PRINTK(KERN_DEBUG "file opening: %d/%d\n", imajor(pInode), iminor(pInode));
|
||
|
+
|
||
|
+ //check which device we are
|
||
|
+ if (iminor(pInode) == 0) //4k
|
||
|
+ {
|
||
|
+ //only one at a time
|
||
|
+ if (!atomic_dec_and_test(&g_oneLock4k))
|
||
|
+ {
|
||
|
+ atomic_inc(&g_oneLock4k);
|
||
|
+ return -EBUSY;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ //todo there will be trouble if two different processes open the files
|
||
|
+
|
||
|
+ //reset after any file is opened
|
||
|
+ g_pMinPhys = (void __user *)-1;
|
||
|
+ g_pMaxPhys = (void __user *)0;
|
||
|
+ g_physOffset = 0;
|
||
|
+ g_cmaHandle = 0;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int Release(struct inode *pInode, struct file *pFile)
|
||
|
+{
|
||
|
+ PRINTK(KERN_DEBUG "file closing, %d pages tracked\n", g_trackedPages);
|
||
|
+ if (g_trackedPages)
|
||
|
+ PRINTK(KERN_ERR "we\'re leaking memory!\n");
|
||
|
+
|
||
|
+ //wait for any dmas to finish
|
||
|
+ DmaWaitAll();
|
||
|
+
|
||
|
+ //free this memory on the application closing the file or it crashing (implicitly closing the file)
|
||
|
+ if (g_cmaHandle)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_DEBUG "unlocking vc memory\n");
|
||
|
+ if (UnlockVcMemory(g_cmaHandle))
|
||
|
+ PRINTK(KERN_ERR "uh-oh, unable to unlock vc memory!\n");
|
||
|
+ PRINTK(KERN_DEBUG "releasing vc memory\n");
|
||
|
+ if (ReleaseVcMemory(g_cmaHandle))
|
||
|
+ PRINTK(KERN_ERR "uh-oh, unable to release vc memory!\n");
|
||
|
+ }
|
||
|
+
|
||
|
+ if (iminor(pInode) == 0)
|
||
|
+ atomic_inc(&g_oneLock4k);
|
||
|
+ else
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static struct DmaControlBlock __user *DmaPrepare(struct DmaControlBlock __user *pUserCB, int *pError)
|
||
|
+{
|
||
|
+ struct DmaControlBlock kernCB;
|
||
|
+ struct DmaControlBlock __user *pUNext;
|
||
|
+ void __iomem *pSourceBus, __iomem *pDestBus;
|
||
|
+
|
||
|
+ //get the control block into kernel memory so we can work on it
|
||
|
+ if (copy_from_user(&kernCB, pUserCB, sizeof(struct DmaControlBlock)) != 0)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "copy_from_user failed for user cb %p\n", pUserCB);
|
||
|
+ *pError = 1;
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (kernCB.m_pSourceAddr == 0 || kernCB.m_pDestAddr == 0)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "faulty source (%p) dest (%p) addresses for user cb %p\n",
|
||
|
+ kernCB.m_pSourceAddr, kernCB.m_pDestAddr, pUserCB);
|
||
|
+ *pError = 1;
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ pSourceBus = UserVirtualToBusViaCache(kernCB.m_pSourceAddr);
|
||
|
+ pDestBus = UserVirtualToBusViaCache(kernCB.m_pDestAddr);
|
||
|
+
|
||
|
+ if (!pSourceBus || !pDestBus)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "virtual to bus translation failure for source/dest %p/%p->%p/%p\n",
|
||
|
+ kernCB.m_pSourceAddr, kernCB.m_pDestAddr,
|
||
|
+ pSourceBus, pDestBus);
|
||
|
+ *pError = 1;
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ //update the user structure with the new bus addresses
|
||
|
+ kernCB.m_pSourceAddr = pSourceBus;
|
||
|
+ kernCB.m_pDestAddr = pDestBus;
|
||
|
+
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "final source %p dest %p\n", kernCB.m_pSourceAddr, kernCB.m_pDestAddr);
|
||
|
+
|
||
|
+ //sort out the bus address for the next block
|
||
|
+ pUNext = kernCB.m_pNext;
|
||
|
+
|
||
|
+ if (kernCB.m_pNext)
|
||
|
+ {
|
||
|
+ void __iomem *pNextBus;
|
||
|
+ pNextBus = UserVirtualToBusViaCbCache(kernCB.m_pNext);
|
||
|
+
|
||
|
+ if (!pNextBus)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "virtual to bus translation failure for m_pNext\n");
|
||
|
+ *pError = 1;
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ //update the pointer with the bus address
|
||
|
+ kernCB.m_pNext = pNextBus;
|
||
|
+ }
|
||
|
+
|
||
|
+ //write it back to user space
|
||
|
+ if (copy_to_user(pUserCB, &kernCB, sizeof(struct DmaControlBlock)) != 0)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "copy_to_user failed for cb %p\n", pUserCB);
|
||
|
+ *pError = 1;
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ __cpuc_flush_dcache_area(pUserCB, 32);
|
||
|
+
|
||
|
+ *pError = 0;
|
||
|
+ return pUNext;
|
||
|
+}
|
||
|
+
|
||
|
+static int DmaKick(struct DmaControlBlock __user *pUserCB)
|
||
|
+{
|
||
|
+ void __iomem *pBusCB;
|
||
|
+
|
||
|
+ pBusCB = UserVirtualToBusViaCbCache(pUserCB);
|
||
|
+ if (!pBusCB)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "virtual to bus translation failure for cb\n");
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ //flush_cache_all();
|
||
|
+
|
||
|
+ bcm_dma_start(g_pDmaChanBase, (dma_addr_t)pBusCB);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void DmaWaitAll(void)
|
||
|
+{
|
||
|
+ int counter = 0;
|
||
|
+ volatile int inner_count;
|
||
|
+ volatile unsigned int cs;
|
||
|
+ unsigned long time_before, time_after;
|
||
|
+
|
||
|
+ time_before = jiffies;
|
||
|
+ //bcm_dma_wait_idle(g_pDmaChanBase);
|
||
|
+ dsb();
|
||
|
+
|
||
|
+ cs = readl(g_pDmaChanBase);
|
||
|
+
|
||
|
+ while ((cs & 1) == 1)
|
||
|
+ {
|
||
|
+ cs = readl(g_pDmaChanBase);
|
||
|
+ counter++;
|
||
|
+
|
||
|
+ for (inner_count = 0; inner_count < 32; inner_count++);
|
||
|
+
|
||
|
+ asm volatile ("MCR p15,0,r0,c7,c0,4 \n");
|
||
|
+ //cpu_do_idle();
|
||
|
+ if (counter >= 1000000)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_WARNING "DMA failed to finish in a timely fashion\n");
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ time_after = jiffies;
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "done, counter %d, cs %08x", counter, cs);
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "took %ld jiffies, %d HZ\n", time_after - time_before, HZ);
|
||
|
+}
|
||
|
+
|
||
|
+static long Ioctl(struct file *pFile, unsigned int cmd, unsigned long arg)
|
||
|
+{
|
||
|
+ int error = 0;
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "ioctl cmd %x arg %lx\n", cmd, arg);
|
||
|
+
|
||
|
+ switch (cmd)
|
||
|
+ {
|
||
|
+ case DMA_PREPARE:
|
||
|
+ case DMA_PREPARE_KICK:
|
||
|
+ case DMA_PREPARE_KICK_WAIT:
|
||
|
+ {
|
||
|
+ struct DmaControlBlock __user *pUCB = (struct DmaControlBlock *)arg;
|
||
|
+ int steps = 0;
|
||
|
+ unsigned long start_time = jiffies;
|
||
|
+ (void)start_time;
|
||
|
+
|
||
|
+ //flush our address cache
|
||
|
+ FlushAddrCache();
|
||
|
+
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "dma prepare\n");
|
||
|
+
|
||
|
+ //do virtual to bus translation for each entry
|
||
|
+ do
|
||
|
+ {
|
||
|
+ pUCB = DmaPrepare(pUCB, &error);
|
||
|
+ } while (error == 0 && ++steps && pUCB);
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "prepare done in %d steps, %ld\n", steps, jiffies - start_time);
|
||
|
+
|
||
|
+ //carry straight on if we want to kick too
|
||
|
+ if (cmd == DMA_PREPARE || error)
|
||
|
+ {
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "falling out\n");
|
||
|
+ return error ? -EINVAL : 0;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ case DMA_KICK:
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "dma begin\n");
|
||
|
+
|
||
|
+ if (cmd == DMA_KICK)
|
||
|
+ FlushAddrCache();
|
||
|
+
|
||
|
+ DmaKick((struct DmaControlBlock __user *)arg);
|
||
|
+
|
||
|
+ if (cmd != DMA_PREPARE_KICK_WAIT)
|
||
|
+ break;
|
||
|
+/* case DMA_WAIT_ONE:
|
||
|
+ //PRINTK(KERN_DEBUG "dma wait one\n");
|
||
|
+ break;*/
|
||
|
+ case DMA_WAIT_ALL:
|
||
|
+ //PRINTK(KERN_DEBUG "dma wait all\n");
|
||
|
+ DmaWaitAll();
|
||
|
+ break;
|
||
|
+ case DMA_MAX_BURST:
|
||
|
+ if (g_dmaChan == 0)
|
||
|
+ return 10;
|
||
|
+ else
|
||
|
+ return 5;
|
||
|
+ case DMA_SET_MIN_PHYS:
|
||
|
+ g_pMinPhys = (void __user *)arg;
|
||
|
+ PRINTK(KERN_DEBUG "min/max user/phys bypass set to %p %p\n", g_pMinPhys, g_pMaxPhys);
|
||
|
+ break;
|
||
|
+ case DMA_SET_MAX_PHYS:
|
||
|
+ g_pMaxPhys = (void __user *)arg;
|
||
|
+ PRINTK(KERN_DEBUG "min/max user/phys bypass set to %p %p\n", g_pMinPhys, g_pMaxPhys);
|
||
|
+ break;
|
||
|
+ case DMA_SET_PHYS_OFFSET:
|
||
|
+ g_physOffset = arg;
|
||
|
+ PRINTK(KERN_DEBUG "user/phys bypass offset set to %ld\n", g_physOffset);
|
||
|
+ break;
|
||
|
+ case DMA_CMA_SET_SIZE:
|
||
|
+ {
|
||
|
+ unsigned int pBusAddr;
|
||
|
+
|
||
|
+ if (g_cmaHandle)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "memory has already been allocated (handle %d)\n", g_cmaHandle);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ PRINTK(KERN_INFO "allocating %ld bytes of VC memory\n", arg * 4096);
|
||
|
+
|
||
|
+ //get the memory
|
||
|
+ if (AllocateVcMemory(&g_cmaHandle, arg * 4096, 4096, MEM_FLAG_L1_NONALLOCATING | MEM_FLAG_NO_INIT | MEM_FLAG_HINT_PERMALOCK))
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "failed to allocate %ld bytes of VC memory\n", arg * 4096);
|
||
|
+ g_cmaHandle = 0;
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ //get an address for it
|
||
|
+ PRINTK(KERN_INFO "trying to map VC memory\n");
|
||
|
+
|
||
|
+ if (LockVcMemory(&pBusAddr, g_cmaHandle))
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "failed to map CMA handle %d, releasing memory\n", g_cmaHandle);
|
||
|
+ ReleaseVcMemory(g_cmaHandle);
|
||
|
+ g_cmaHandle = 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ PRINTK(KERN_INFO "bus address for CMA memory is %x\n", pBusAddr);
|
||
|
+ return pBusAddr;
|
||
|
+ }
|
||
|
+ case DMA_GET_VERSION:
|
||
|
+ PRINTK(KERN_DEBUG "returning version number, %d\n", VERSION_NUMBER);
|
||
|
+ return VERSION_NUMBER;
|
||
|
+ default:
|
||
|
+ PRINTK(KERN_DEBUG "unknown ioctl: %d\n", cmd);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static ssize_t Read(struct file *pFile, char __user *pUser, size_t count, loff_t *offp)
|
||
|
+{
|
||
|
+ return -EIO;
|
||
|
+}
|
||
|
+
|
||
|
+static int Mmap(struct file *pFile, struct vm_area_struct *pVma)
|
||
|
+{
|
||
|
+ struct PageList *pPages;
|
||
|
+ struct VmaPageList *pVmaList;
|
||
|
+
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "MMAP vma %p, length %ld (%s %d)\n",
|
||
|
+ pVma, pVma->vm_end - pVma->vm_start,
|
||
|
+ current->comm, current->pid);
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "MMAP %p %d (tracked %d)\n", pVma, current->pid, g_trackedPages);
|
||
|
+
|
||
|
+ //make a new page list
|
||
|
+ pPages = (struct PageList *)kmalloc(sizeof(struct PageList), GFP_KERNEL);
|
||
|
+ if (!pPages)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "couldn\'t allocate a new page list (%s %d)\n",
|
||
|
+ current->comm, current->pid);
|
||
|
+ return -ENOMEM;
|
||
|
+ }
|
||
|
+
|
||
|
+ //clear the page list
|
||
|
+ pPages->m_used = 0;
|
||
|
+ pPages->m_pNext = 0;
|
||
|
+
|
||
|
+ //insert our vma and new page list somewhere
|
||
|
+ if (!pVma->vm_private_data)
|
||
|
+ {
|
||
|
+ struct VmaPageList *pList;
|
||
|
+
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "new vma list, making new one (%s %d)\n",
|
||
|
+ current->comm, current->pid);
|
||
|
+
|
||
|
+ //make a new vma list
|
||
|
+ pList = (struct VmaPageList *)kmalloc(sizeof(struct VmaPageList), GFP_KERNEL);
|
||
|
+ if (!pList)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "couldn\'t allocate vma page list (%s %d)\n",
|
||
|
+ current->comm, current->pid);
|
||
|
+ kfree(pPages);
|
||
|
+ return -ENOMEM;
|
||
|
+ }
|
||
|
+
|
||
|
+ //clear this list
|
||
|
+ pVma->vm_private_data = (void *)pList;
|
||
|
+ pList->m_refCount = 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ pVmaList = (struct VmaPageList *)pVma->vm_private_data;
|
||
|
+
|
||
|
+ //add it to the vma list
|
||
|
+ pVmaList->m_pPageHead = pPages;
|
||
|
+ pVmaList->m_pPageTail = pPages;
|
||
|
+
|
||
|
+ pVma->vm_ops = &g_vmOps4k;
|
||
|
+ pVma->vm_flags |= VM_IO;
|
||
|
+
|
||
|
+ VmaOpen4k(pVma);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+/****** VMA OPERATIONS ******/
|
||
|
+
|
||
|
+static void VmaOpen4k(struct vm_area_struct *pVma)
|
||
|
+{
|
||
|
+ struct VmaPageList *pVmaList;
|
||
|
+
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "vma open %p private %p (%s %d), %d live pages\n", pVma, pVma->vm_private_data, current->comm, current->pid, g_trackedPages);
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "OPEN %p %d %ld pages (tracked pages %d)\n",
|
||
|
+ pVma, current->pid, (pVma->vm_end - pVma->vm_start) >> 12,
|
||
|
+ g_trackedPages);
|
||
|
+
|
||
|
+ pVmaList = (struct VmaPageList *)pVma->vm_private_data;
|
||
|
+
|
||
|
+ if (pVmaList)
|
||
|
+ {
|
||
|
+ pVmaList->m_refCount++;
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "ref count is now %d\n", pVmaList->m_refCount);
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "err, open but no vma page list\n");
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static void VmaClose4k(struct vm_area_struct *pVma)
|
||
|
+{
|
||
|
+ struct VmaPageList *pVmaList;
|
||
|
+ int freed = 0;
|
||
|
+
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "vma close %p private %p (%s %d)\n", pVma, pVma->vm_private_data, current->comm, current->pid);
|
||
|
+
|
||
|
+ //wait for any dmas to finish
|
||
|
+ DmaWaitAll();
|
||
|
+
|
||
|
+ //find our vma in the list
|
||
|
+ pVmaList = (struct VmaPageList *)pVma->vm_private_data;
|
||
|
+
|
||
|
+ //may be a fork
|
||
|
+ if (pVmaList)
|
||
|
+ {
|
||
|
+ struct PageList *pPages;
|
||
|
+
|
||
|
+ pVmaList->m_refCount--;
|
||
|
+
|
||
|
+ if (pVmaList->m_refCount == 0)
|
||
|
+ {
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "found vma, freeing pages (%s %d)\n",
|
||
|
+ current->comm, current->pid);
|
||
|
+
|
||
|
+ pPages = pVmaList->m_pPageHead;
|
||
|
+
|
||
|
+ if (!pPages)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "no page list (%s %d)!\n",
|
||
|
+ current->comm, current->pid);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ while (pPages)
|
||
|
+ {
|
||
|
+ struct PageList *next;
|
||
|
+ int count;
|
||
|
+
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "page list (%s %d)\n",
|
||
|
+ current->comm, current->pid);
|
||
|
+
|
||
|
+ next = pPages->m_pNext;
|
||
|
+ for (count = 0; count < pPages->m_used; count++)
|
||
|
+ {
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "freeing page %p (%s %d)\n",
|
||
|
+ pPages->m_pPages[count],
|
||
|
+ current->comm, current->pid);
|
||
|
+ __free_pages(pPages->m_pPages[count], 0);
|
||
|
+ g_trackedPages--;
|
||
|
+ freed++;
|
||
|
+ }
|
||
|
+
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "freeing page list (%s %d)\n",
|
||
|
+ current->comm, current->pid);
|
||
|
+ kfree(pPages);
|
||
|
+ pPages = next;
|
||
|
+ }
|
||
|
+
|
||
|
+ //remove our vma from the list
|
||
|
+ kfree(pVmaList);
|
||
|
+ pVma->vm_private_data = 0;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "ref count is %d, not closing\n", pVmaList->m_refCount);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ PRINTK_VERBOSE(KERN_ERR "uh-oh, vma %p not found (%s %d)!\n", pVma, current->comm, current->pid);
|
||
|
+ PRINTK_VERBOSE(KERN_ERR "CLOSE ERR\n");
|
||
|
+ }
|
||
|
+
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "CLOSE %p %d %d pages (tracked pages %d)",
|
||
|
+ pVma, current->pid, freed, g_trackedPages);
|
||
|
+
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "%d pages open\n", g_trackedPages);
|
||
|
+}
|
||
|
+
|
||
|
+static int VmaFault4k(struct vm_area_struct *pVma, struct vm_fault *pVmf)
|
||
|
+{
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "vma fault for vma %p private %p at offset %ld (%s %d)\n", pVma, pVma->vm_private_data, pVmf->pgoff,
|
||
|
+ current->comm, current->pid);
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "FAULT\n");
|
||
|
+ pVmf->page = alloc_page(GFP_KERNEL);
|
||
|
+
|
||
|
+ if (pVmf->page)
|
||
|
+ {
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "alloc page virtual %p\n", page_address(pVmf->page));
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!pVmf->page)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "vma fault oom (%s %d)\n", current->comm, current->pid);
|
||
|
+ return VM_FAULT_OOM;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ struct VmaPageList *pVmaList;
|
||
|
+
|
||
|
+ get_page(pVmf->page);
|
||
|
+ g_trackedPages++;
|
||
|
+
|
||
|
+ //find our vma in the list
|
||
|
+ pVmaList = (struct VmaPageList *)pVma->vm_private_data;
|
||
|
+
|
||
|
+ if (pVmaList)
|
||
|
+ {
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "vma found (%s %d)\n", current->comm, current->pid);
|
||
|
+
|
||
|
+ if (pVmaList->m_pPageTail->m_used == PAGES_PER_LIST)
|
||
|
+ {
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "making new page list (%s %d)\n", current->comm, current->pid);
|
||
|
+ //making a new page list
|
||
|
+ pVmaList->m_pPageTail->m_pNext = (struct PageList *)kmalloc(sizeof(struct PageList), GFP_KERNEL);
|
||
|
+ if (!pVmaList->m_pPageTail->m_pNext)
|
||
|
+ return -ENOMEM;
|
||
|
+
|
||
|
+ //update the tail pointer
|
||
|
+ pVmaList->m_pPageTail = pVmaList->m_pPageTail->m_pNext;
|
||
|
+ pVmaList->m_pPageTail->m_used = 0;
|
||
|
+ pVmaList->m_pPageTail->m_pNext = 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ PRINTK_VERBOSE(KERN_DEBUG "adding page to list (%s %d)\n", current->comm, current->pid);
|
||
|
+
|
||
|
+ pVmaList->m_pPageTail->m_pPages[pVmaList->m_pPageTail->m_used] = pVmf->page;
|
||
|
+ pVmaList->m_pPageTail->m_used++;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ PRINTK(KERN_ERR "returned page for vma we don\'t know %p (%s %d)\n", pVma, current->comm, current->pid);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+/****** GENERIC FUNCTIONS ******/
|
||
|
+static int __init dmaer_init(void)
|
||
|
+{
|
||
|
+ int result = alloc_chrdev_region(&g_majorMinor, 0, 1, "dmaer");
|
||
|
+ if (result < 0)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "unable to get major device number\n");
|
||
|
+ return result;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ PRINTK(KERN_DEBUG "major device number %d\n", MAJOR(g_majorMinor));
|
||
|
+
|
||
|
+ PRINTK(KERN_DEBUG "vma list size %d, page list size %d, page size %ld\n",
|
||
|
+ sizeof(struct VmaPageList), sizeof(struct PageList), PAGE_SIZE);
|
||
|
+
|
||
|
+ //get a dma channel to work with
|
||
|
+ result = bcm_dma_chan_alloc(BCM_DMA_FEATURE_FAST, (void **)&g_pDmaChanBase, &g_dmaIrq);
|
||
|
+
|
||
|
+ //uncomment to force to channel 0
|
||
|
+ //result = 0;
|
||
|
+ //g_pDmaChanBase = 0xce808000;
|
||
|
+
|
||
|
+ if (result < 0)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "failed to allocate dma channel\n");
|
||
|
+ cdev_del(&g_cDev);
|
||
|
+ unregister_chrdev_region(g_majorMinor, 1);
|
||
|
+ }
|
||
|
+
|
||
|
+ //reset the channel
|
||
|
+ PRINTK(KERN_DEBUG "allocated dma channel %d (%p), initial state %08x\n", result, g_pDmaChanBase, *g_pDmaChanBase);
|
||
|
+ *g_pDmaChanBase = 1 << 31;
|
||
|
+ PRINTK(KERN_DEBUG "post-reset %08x\n", *g_pDmaChanBase);
|
||
|
+
|
||
|
+ g_dmaChan = result;
|
||
|
+
|
||
|
+ //clear the cache stats
|
||
|
+ g_cacheHit = 0;
|
||
|
+ g_cacheMiss = 0;
|
||
|
+
|
||
|
+ //register our device - after this we are go go go
|
||
|
+ cdev_init(&g_cDev, &g_fOps);
|
||
|
+ g_cDev.owner = THIS_MODULE;
|
||
|
+ g_cDev.ops = &g_fOps;
|
||
|
+
|
||
|
+ result = cdev_add(&g_cDev, g_majorMinor, 1);
|
||
|
+ if (result < 0)
|
||
|
+ {
|
||
|
+ PRINTK(KERN_ERR "failed to add character device\n");
|
||
|
+ unregister_chrdev_region(g_majorMinor, 1);
|
||
|
+ bcm_dma_chan_free(g_dmaChan);
|
||
|
+ return result;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void __exit dmaer_exit(void)
|
||
|
+{
|
||
|
+ PRINTK(KERN_INFO "closing dmaer device, cache stats: %d hits %d misses\n", g_cacheHit, g_cacheMiss);
|
||
|
+ //unregister the device
|
||
|
+ cdev_del(&g_cDev);
|
||
|
+ unregister_chrdev_region(g_majorMinor, 1);
|
||
|
+ //free the dma channel
|
||
|
+ bcm_dma_chan_free(g_dmaChan);
|
||
|
+}
|
||
|
+
|
||
|
+MODULE_LICENSE("Dual BSD/GPL");
|
||
|
+MODULE_AUTHOR("Simon Hall");
|
||
|
+module_init(dmaer_init);
|
||
|
+module_exit(dmaer_exit);
|
||
|
+
|
||
|
--- /dev/null
|
||
|
+++ b/arch/arm/mach-bcm2708/include/mach/vc_support.h
|
||
|
@@ -0,0 +1,69 @@
|
||
|
+#ifndef _VC_SUPPORT_H_
|
||
|
+#define _VC_SUPPORT_H_
|
||
|
+
|
||
|
+/*
|
||
|
+ * vc_support.h
|
||
|
+ *
|
||
|
+ * Created on: 25 Nov 2012
|
||
|
+ * Author: Simon
|
||
|
+ */
|
||
|
+
|
||
|
+enum {
|
||
|
+/*
|
||
|
+ If a MEM_HANDLE_T is discardable, the memory manager may resize it to size
|
||
|
+ 0 at any time when it is not locked or retained.
|
||
|
+ */
|
||
|
+ MEM_FLAG_DISCARDABLE = 1 << 0,
|
||
|
+
|
||
|
+ /*
|
||
|
+ If a MEM_HANDLE_T is allocating (or normal), its block of memory will be
|
||
|
+ accessed in an allocating fashion through the cache.
|
||
|
+ */
|
||
|
+ MEM_FLAG_NORMAL = 0 << 2,
|
||
|
+ MEM_FLAG_ALLOCATING = MEM_FLAG_NORMAL,
|
||
|
+
|
||
|
+ /*
|
||
|
+ If a MEM_HANDLE_T is direct, its block of memory will be accessed
|
||
|
+ directly, bypassing the cache.
|
||
|
+ */
|
||
|
+ MEM_FLAG_DIRECT = 1 << 2,
|
||
|
+
|
||
|
+ /*
|
||
|
+ If a MEM_HANDLE_T is coherent, its block of memory will be accessed in a
|
||
|
+ non-allocating fashion through the cache.
|
||
|
+ */
|
||
|
+ MEM_FLAG_COHERENT = 2 << 2,
|
||
|
+
|
||
|
+ /*
|
||
|
+ If a MEM_HANDLE_T is L1-nonallocating, its block of memory will be accessed by
|
||
|
+ the VPU in a fashion which is allocating in L2, but only coherent in L1.
|
||
|
+ */
|
||
|
+ MEM_FLAG_L1_NONALLOCATING = (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT),
|
||
|
+
|
||
|
+ /*
|
||
|
+ If a MEM_HANDLE_T is zero'd, its contents are set to 0 rather than
|
||
|
+ MEM_HANDLE_INVALID on allocation and resize up.
|
||
|
+ */
|
||
|
+ MEM_FLAG_ZERO = 1 << 4,
|
||
|
+
|
||
|
+ /*
|
||
|
+ If a MEM_HANDLE_T is uninitialised, it will not be reset to a defined value
|
||
|
+ (either zero, or all 1's) on allocation.
|
||
|
+ */
|
||
|
+ MEM_FLAG_NO_INIT = 1 << 5,
|
||
|
+
|
||
|
+ /*
|
||
|
+ Hints.
|
||
|
+ */
|
||
|
+ MEM_FLAG_HINT_PERMALOCK = 1 << 6, /* Likely to be locked for long periods of time. */
|
||
|
+};
|
||
|
+
|
||
|
+unsigned int AllocateVcMemory(unsigned int *pHandle, unsigned int size, unsigned int alignment, unsigned int flags);
|
||
|
+unsigned int ReleaseVcMemory(unsigned int handle);
|
||
|
+unsigned int LockVcMemory(unsigned int *pBusAddress, unsigned int handle);
|
||
|
+unsigned int UnlockVcMemory(unsigned int handle);
|
||
|
+
|
||
|
+unsigned int ExecuteVcCode(unsigned int code,
|
||
|
+ unsigned int r0, unsigned int r1, unsigned int r2, unsigned int r3, unsigned int r4, unsigned int r5);
|
||
|
+
|
||
|
+#endif
|
||
|
--- /dev/null
|
||
|
+++ b/arch/arm/mach-bcm2708/vc_support.c
|
||
|
@@ -0,0 +1,319 @@
|
||
|
+/*
|
||
|
+ * vc_support.c
|
||
|
+ *
|
||
|
+ * Created on: 25 Nov 2012
|
||
|
+ * Author: Simon
|
||
|
+ */
|
||
|
+
|
||
|
+#include <linux/module.h>
|
||
|
+#include <mach/vcio.h>
|
||
|
+
|
||
|
+#ifdef ECLIPSE_IGNORE
|
||
|
+
|
||
|
+#define __user
|
||
|
+#define __init
|
||
|
+#define __exit
|
||
|
+#define __iomem
|
||
|
+#define KERN_DEBUG
|
||
|
+#define KERN_ERR
|
||
|
+#define KERN_WARNING
|
||
|
+#define KERN_INFO
|
||
|
+#define _IOWR(a, b, c) b
|
||
|
+#define _IOW(a, b, c) b
|
||
|
+#define _IO(a, b) b
|
||
|
+
|
||
|
+#endif
|
||
|
+
|
||
|
+/****** VC MAILBOX FUNCTIONALITY ******/
|
||
|
+unsigned int AllocateVcMemory(unsigned int *pHandle, unsigned int size, unsigned int alignment, unsigned int flags)
|
||
|
+{
|
||
|
+ struct vc_msg
|
||
|
+ {
|
||
|
+ unsigned int m_msgSize;
|
||
|
+ unsigned int m_response;
|
||
|
+
|
||
|
+ struct vc_tag
|
||
|
+ {
|
||
|
+ unsigned int m_tagId;
|
||
|
+ unsigned int m_sendBufferSize;
|
||
|
+ union {
|
||
|
+ unsigned int m_sendDataSize;
|
||
|
+ unsigned int m_recvDataSize;
|
||
|
+ };
|
||
|
+
|
||
|
+ struct args
|
||
|
+ {
|
||
|
+ union {
|
||
|
+ unsigned int m_size;
|
||
|
+ unsigned int m_handle;
|
||
|
+ };
|
||
|
+ unsigned int m_alignment;
|
||
|
+ unsigned int m_flags;
|
||
|
+ } m_args;
|
||
|
+ } m_tag;
|
||
|
+
|
||
|
+ unsigned int m_endTag;
|
||
|
+ } msg;
|
||
|
+ int s;
|
||
|
+
|
||
|
+ msg.m_msgSize = sizeof(msg);
|
||
|
+ msg.m_response = 0;
|
||
|
+ msg.m_endTag = 0;
|
||
|
+
|
||
|
+ //fill in the tag for the allocation command
|
||
|
+ msg.m_tag.m_tagId = 0x3000c;
|
||
|
+ msg.m_tag.m_sendBufferSize = 12;
|
||
|
+ msg.m_tag.m_sendDataSize = 12;
|
||
|
+
|
||
|
+ //fill in our args
|
||
|
+ msg.m_tag.m_args.m_size = size;
|
||
|
+ msg.m_tag.m_args.m_alignment = alignment;
|
||
|
+ msg.m_tag.m_args.m_flags = flags;
|
||
|
+
|
||
|
+ //run the command
|
||
|
+ s = bcm_mailbox_property(&msg, sizeof(msg));
|
||
|
+
|
||
|
+ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004)
|
||
|
+ {
|
||
|
+ *pHandle = msg.m_tag.m_args.m_handle;
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ printk(KERN_ERR "failed to allocate vc memory: s=%d response=%08x recv data size=%08x\n",
|
||
|
+ s, msg.m_response, msg.m_tag.m_recvDataSize);
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+unsigned int ReleaseVcMemory(unsigned int handle)
|
||
|
+{
|
||
|
+ struct vc_msg
|
||
|
+ {
|
||
|
+ unsigned int m_msgSize;
|
||
|
+ unsigned int m_response;
|
||
|
+
|
||
|
+ struct vc_tag
|
||
|
+ {
|
||
|
+ unsigned int m_tagId;
|
||
|
+ unsigned int m_sendBufferSize;
|
||
|
+ union {
|
||
|
+ unsigned int m_sendDataSize;
|
||
|
+ unsigned int m_recvDataSize;
|
||
|
+ };
|
||
|
+
|
||
|
+ struct args
|
||
|
+ {
|
||
|
+ union {
|
||
|
+ unsigned int m_handle;
|
||
|
+ unsigned int m_error;
|
||
|
+ };
|
||
|
+ } m_args;
|
||
|
+ } m_tag;
|
||
|
+
|
||
|
+ unsigned int m_endTag;
|
||
|
+ } msg;
|
||
|
+ int s;
|
||
|
+
|
||
|
+ msg.m_msgSize = sizeof(msg);
|
||
|
+ msg.m_response = 0;
|
||
|
+ msg.m_endTag = 0;
|
||
|
+
|
||
|
+ //fill in the tag for the release command
|
||
|
+ msg.m_tag.m_tagId = 0x3000f;
|
||
|
+ msg.m_tag.m_sendBufferSize = 4;
|
||
|
+ msg.m_tag.m_sendDataSize = 4;
|
||
|
+
|
||
|
+ //pass across the handle
|
||
|
+ msg.m_tag.m_args.m_handle = handle;
|
||
|
+
|
||
|
+ s = bcm_mailbox_property(&msg, sizeof(msg));
|
||
|
+
|
||
|
+ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004 && msg.m_tag.m_args.m_error == 0)
|
||
|
+ return 0;
|
||
|
+ else
|
||
|
+ {
|
||
|
+ printk(KERN_ERR "failed to release vc memory: s=%d response=%08x recv data size=%08x error=%08x\n",
|
||
|
+ s, msg.m_response, msg.m_tag.m_recvDataSize, msg.m_tag.m_args.m_error);
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+unsigned int LockVcMemory(unsigned int *pBusAddress, unsigned int handle)
|
||
|
+{
|
||
|
+ struct vc_msg
|
||
|
+ {
|
||
|
+ unsigned int m_msgSize;
|
||
|
+ unsigned int m_response;
|
||
|
+
|
||
|
+ struct vc_tag
|
||
|
+ {
|
||
|
+ unsigned int m_tagId;
|
||
|
+ unsigned int m_sendBufferSize;
|
||
|
+ union {
|
||
|
+ unsigned int m_sendDataSize;
|
||
|
+ unsigned int m_recvDataSize;
|
||
|
+ };
|
||
|
+
|
||
|
+ struct args
|
||
|
+ {
|
||
|
+ union {
|
||
|
+ unsigned int m_handle;
|
||
|
+ unsigned int m_busAddress;
|
||
|
+ };
|
||
|
+ } m_args;
|
||
|
+ } m_tag;
|
||
|
+
|
||
|
+ unsigned int m_endTag;
|
||
|
+ } msg;
|
||
|
+ int s;
|
||
|
+
|
||
|
+ msg.m_msgSize = sizeof(msg);
|
||
|
+ msg.m_response = 0;
|
||
|
+ msg.m_endTag = 0;
|
||
|
+
|
||
|
+ //fill in the tag for the lock command
|
||
|
+ msg.m_tag.m_tagId = 0x3000d;
|
||
|
+ msg.m_tag.m_sendBufferSize = 4;
|
||
|
+ msg.m_tag.m_sendDataSize = 4;
|
||
|
+
|
||
|
+ //pass across the handle
|
||
|
+ msg.m_tag.m_args.m_handle = handle;
|
||
|
+
|
||
|
+ s = bcm_mailbox_property(&msg, sizeof(msg));
|
||
|
+
|
||
|
+ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004)
|
||
|
+ {
|
||
|
+ //pick out the bus address
|
||
|
+ *pBusAddress = msg.m_tag.m_args.m_busAddress;
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ printk(KERN_ERR "failed to lock vc memory: s=%d response=%08x recv data size=%08x\n",
|
||
|
+ s, msg.m_response, msg.m_tag.m_recvDataSize);
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+unsigned int UnlockVcMemory(unsigned int handle)
|
||
|
+{
|
||
|
+ struct vc_msg
|
||
|
+ {
|
||
|
+ unsigned int m_msgSize;
|
||
|
+ unsigned int m_response;
|
||
|
+
|
||
|
+ struct vc_tag
|
||
|
+ {
|
||
|
+ unsigned int m_tagId;
|
||
|
+ unsigned int m_sendBufferSize;
|
||
|
+ union {
|
||
|
+ unsigned int m_sendDataSize;
|
||
|
+ unsigned int m_recvDataSize;
|
||
|
+ };
|
||
|
+
|
||
|
+ struct args
|
||
|
+ {
|
||
|
+ union {
|
||
|
+ unsigned int m_handle;
|
||
|
+ unsigned int m_error;
|
||
|
+ };
|
||
|
+ } m_args;
|
||
|
+ } m_tag;
|
||
|
+
|
||
|
+ unsigned int m_endTag;
|
||
|
+ } msg;
|
||
|
+ int s;
|
||
|
+
|
||
|
+ msg.m_msgSize = sizeof(msg);
|
||
|
+ msg.m_response = 0;
|
||
|
+ msg.m_endTag = 0;
|
||
|
+
|
||
|
+ //fill in the tag for the unlock command
|
||
|
+ msg.m_tag.m_tagId = 0x3000e;
|
||
|
+ msg.m_tag.m_sendBufferSize = 4;
|
||
|
+ msg.m_tag.m_sendDataSize = 4;
|
||
|
+
|
||
|
+ //pass across the handle
|
||
|
+ msg.m_tag.m_args.m_handle = handle;
|
||
|
+
|
||
|
+ s = bcm_mailbox_property(&msg, sizeof(msg));
|
||
|
+
|
||
|
+ //check the error code too
|
||
|
+ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004 && msg.m_tag.m_args.m_error == 0)
|
||
|
+ return 0;
|
||
|
+ else
|
||
|
+ {
|
||
|
+ printk(KERN_ERR "failed to unlock vc memory: s=%d response=%08x recv data size=%08x error%08x\n",
|
||
|
+ s, msg.m_response, msg.m_tag.m_recvDataSize, msg.m_tag.m_args.m_error);
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+unsigned int ExecuteVcCode(unsigned int code,
|
||
|
+ unsigned int r0, unsigned int r1, unsigned int r2, unsigned int r3, unsigned int r4, unsigned int r5)
|
||
|
+{
|
||
|
+ struct vc_msg
|
||
|
+ {
|
||
|
+ unsigned int m_msgSize;
|
||
|
+ unsigned int m_response;
|
||
|
+
|
||
|
+ struct vc_tag
|
||
|
+ {
|
||
|
+ unsigned int m_tagId;
|
||
|
+ unsigned int m_sendBufferSize;
|
||
|
+ union {
|
||
|
+ unsigned int m_sendDataSize;
|
||
|
+ unsigned int m_recvDataSize;
|
||
|
+ };
|
||
|
+
|
||
|
+ struct args
|
||
|
+ {
|
||
|
+ union {
|
||
|
+ unsigned int m_pCode;
|
||
|
+ unsigned int m_return;
|
||
|
+ };
|
||
|
+ unsigned int m_r0;
|
||
|
+ unsigned int m_r1;
|
||
|
+ unsigned int m_r2;
|
||
|
+ unsigned int m_r3;
|
||
|
+ unsigned int m_r4;
|
||
|
+ unsigned int m_r5;
|
||
|
+ } m_args;
|
||
|
+ } m_tag;
|
||
|
+
|
||
|
+ unsigned int m_endTag;
|
||
|
+ } msg;
|
||
|
+ int s;
|
||
|
+
|
||
|
+ msg.m_msgSize = sizeof(msg);
|
||
|
+ msg.m_response = 0;
|
||
|
+ msg.m_endTag = 0;
|
||
|
+
|
||
|
+ //fill in the tag for the unlock command
|
||
|
+ msg.m_tag.m_tagId = 0x30010;
|
||
|
+ msg.m_tag.m_sendBufferSize = 28;
|
||
|
+ msg.m_tag.m_sendDataSize = 28;
|
||
|
+
|
||
|
+ //pass across the handle
|
||
|
+ msg.m_tag.m_args.m_pCode = code;
|
||
|
+ msg.m_tag.m_args.m_r0 = r0;
|
||
|
+ msg.m_tag.m_args.m_r1 = r1;
|
||
|
+ msg.m_tag.m_args.m_r2 = r2;
|
||
|
+ msg.m_tag.m_args.m_r3 = r3;
|
||
|
+ msg.m_tag.m_args.m_r4 = r4;
|
||
|
+ msg.m_tag.m_args.m_r5 = r5;
|
||
|
+
|
||
|
+ s = bcm_mailbox_property(&msg, sizeof(msg));
|
||
|
+
|
||
|
+ //check the error code too
|
||
|
+ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004)
|
||
|
+ return msg.m_tag.m_args.m_return;
|
||
|
+ else
|
||
|
+ {
|
||
|
+ printk(KERN_ERR "failed to execute: s=%d response=%08x recv data size=%08x\n",
|
||
|
+ s, msg.m_response, msg.m_tag.m_recvDataSize);
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|