📄 dma.c
字号:
/* * linux/arch/arm/mach-davinci/dma.c * * TI DaVinci DMA file * * Copyright (C) 2006 Texas Instruments. * * ---------------------------------------------------------------------------- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ---------------------------------------------------------------------------- * */#include <linux/sched.h>#include <linux/init.h>#include <linux/module.h>#include <linux/interrupt.h>#include <linux/device.h>#include <linux/spinlock.h>#include <asm/io.h>#include <asm/arch/memory.h>#include <linux/kernel.h>#include <asm/arch/hardware.h>#include <asm/arch/irqs.h>#include <asm/arch/edma.h>static spinlock_t dma_chan_lock;static struct device_driver edma_driver;static struct platform_device edma_dev;#define LOCK_INIT spin_lock_init(&dma_chan_lock)#define LOCK spin_lock(&dma_chan_lock)#define UNLOCK spin_unlock(&dma_chan_lock)typedef void (*intr_callback) (void);static int register_dma_interrupts(intr_callback, intr_callback, intr_callback, intr_callback);#define DAVINCI_DMA_REGISTER_BASE DAVINCI_DMA_3PCC_BASEstatic edmacc_regs *get_edma_base(void){ return ((edmacc_regs *) IO_ADDRESS(DAVINCI_DMA_REGISTER_BASE));}static intr_callback cb[4];/* Structure containing the dma channel parameters */static struct davinci_dma_lch { int dev_id; int in_use; /* 1-used 0-unused */ int link_lch; int dma_running; int param_no; int tcc;} dma_chan[DAVINCI_EDMA_NUM_PARAMENTRY];static struct dma_interrupt_data { void (*callback) (int lch, unsigned short ch_status, void *data); void *data;} intr_data[64];/* Each bit field of the elements bellow indicate the corresponding EDMA channel availability on arm side events*/static unsigned long edma_channels_arm[] = { 0xffffffff, 0xffffffff};/* Each bit field of the elements bellow indicate the corresponding QDMA channel availability on arm side events*/static unsigned char qdma_channels_arm[] = { 0xff};/* Each bit field of the elements bellow indicate corresponding PARAM entry availibility on arm side events*/static unsigned long param_entry_arm[] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};/* Each bit field of the elements bellow indicate whether a PARAM entry is free or in use 1 - free 0 - in use*/static unsigned long param_entry_use_status[] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};/* Each bit field of the elements bellow indicate whether a intrerrupt is free or in use 1 - free 0 - in use*/static unsigned long dma_intr_use_status[] = { 0xffffffff, 0xffffffff};/* This lists the DMA channel numbers which does not have any events associated with it*/static int dma_chan_no_event[] = { 0, 1, 12, 13, 14, 15, 25, 30, 31, 45, 46, 47, 55, 56, 57, 58, 59, 60, 61, 62, 63, -1};static int channel_queue_mapping[][2] = {/* {channel no, event queue no } */ {0, 0}, {1, 1}, {2, 0}, {3, 1}, {4, 0}, {5, 1}, {6, 0}, {7, 1}, {8, 0}, {9, 1}, {10, 0}, {11, 1}, {12, 0}, {13, 1}, {14, 0}, {15, 1}, {16, 0}, {17, 1}, {18, 0}, {19, 1}, {20, 0}, {21, 1}, {22, 0}, {23, 1}, {24, 0}, {25, 1}, {26, 0}, {27, 1}, {28, 0}, {29, 1}, {30, 0}, {31, 1}, {32, 0}, {33, 1}, {34, 0}, {35, 1}, {36, 0}, {37, 1}, {38, 0}, {39, 1}, {40, 0}, {41, 1}, {42, 0}, {43, 1}, {44, 0}, {45, 1}, {46, 0}, {47, 1}, {48, 0}, {49, 1}, {50, 0}, {51, 1}, {52, 0}, {53, 1}, {54, 0}, {55, 1}, {56, 0}, {57, 1}, {58, 0}, {59, 1}, {60, 0}, {61, 1}, {62, 0}, {63, 1}, {64, 0}, {65, 1}, {66, 0}, {67, 1}, {68, 0}, {69, 1}, {70, 0}, {71, 1}, {-1, -1}};static int queue_tc_mapping[DAVINCI_EDMA_NUM_EVQUE + 1][2] = {/* {event queue no, TC no} */ {0, 0}, {1, 1}, {-1, -1}};static int queue_priority_mapping[DAVINCI_EDMA_NUM_EVQUE + 1][2] = { /* {event queue no, Priority} */ {0, 0}, {1, 1}, {-1, -1}};static int qdam_to_param_mapping[8] = { 0 };volatile edmacc_regs *ptr_edmacc_regs = NULL;/*****************************************************************************/static void map_dmach_queue(int ch_no, int queue_no){ if (ch_no < DAVINCI_EDMA_NUM_DMACH) { int bit_start = (ch_no % 8) * 4; ptr_edmacc_regs->dmaqnum[ch_no >> 3] &= (~(0x7 << bit_start)); ptr_edmacc_regs->dmaqnum[ch_no >> 3] |= ((queue_no & 0x7) << bit_start); } else if (ch_no >= DAVINCI_EDMA_NUM_DMACH && ch_no < (DAVINCI_EDMA_NUM_DMACH + DAVINCI_EDMA_NUM_QDMACH)) { int bit_start = (ch_no - DAVINCI_EDMA_NUM_DMACH) * 4; ptr_edmacc_regs->qdmaqnum &= (~(0x7 << bit_start)); ptr_edmacc_regs->qdmaqnum |= ((queue_no & 0x7) << bit_start); }}/* For Davinci this Macro supports mapping only for QDMA channels and PaRam entry */static void map_dmach_param(int ch_no, int param_no){ if (ch_no >= DAVINCI_EDMA_NUM_DMACH && ch_no < (DAVINCI_EDMA_NUM_DMACH + DAVINCI_EDMA_NUM_QDMACH)) { ptr_edmacc_regs->qchmap[ch_no - DAVINCI_EDMA_NUM_DMACH] &= ~(PAENTRY | TRWORD); ptr_edmacc_regs->qchmap[ch_no - DAVINCI_EDMA_NUM_DMACH] |= (((param_no & 0x1ff) << 5) | (QDMA_TRWORD << 2)); }}static void map_queue_tc(int queue_no, int tc_no){ int bit_start = queue_no * 4; ptr_edmacc_regs->quetcmap &= ~(0x7 << bit_start); ptr_edmacc_regs->quetcmap |= ((tc_no & 0x7) << bit_start);}static void assign_priority_to_queue(int queue_no, int priority){ int bit_start = queue_no * 4; ptr_edmacc_regs->quepri &= ~(0x7 << bit_start); ptr_edmacc_regs->quepri |= ((priority & 0x7) << bit_start);}/****************************************************************************** * * DMA Param entry requests: Requests for the param structure entry for the dma * channel passed * Arguments: * lch - logical channel for which param entry is being requested. * * Return: param number on success, or negative error number on failure * *****************************************************************************/static int request_param(int lch, int dev_id){ int i = 0, j = 0, is_break = 0; if (lch >= 0 && lch < DAVINCI_EDMA_NUM_DMACH) { /* In davinci there is 1:1 mapping between edma channels and param sets */ LOCK; /* It maintains param entry availability bitmap which could be updated by several thread same channel and so requires protection */ param_entry_use_status[lch / 32] &= (~(1 << (lch % 32))); UNLOCK; return lch; } else { if (dev_id >= DAVINCI_DMA_QDMA0 && dev_id <= DAVINCI_DMA_QDMA7) { i = 0; } else if (dev_id == DAVINCI_EDMA_PARAM_ANY) { i = DAVINCI_EDMA_NUM_DMACH; } /* This allocation alogrithm requires complete lock because availabilty of param entry is checked from structure param_entry_use_status and same struct is updated back also once allocated */ LOCK; while (i < DAVINCI_EDMA_NUM_PARAMENTRY) { j = 0, is_break = 1; if ((param_entry_arm[i / 32] & (1 << (i % 32))) && (param_entry_use_status[i / 32] & (1 << (i % 32)))) { if (dev_id != DAVINCI_EDMA_PARAM_ANY) { while (dma_chan_no_event[j] != -1) { if (dma_chan_no_event[j] == i) { is_break = 0; } j++; } if (!is_break) { break; } } else { break; } i++; } else { i++; } } if (i < DAVINCI_EDMA_NUM_PARAMENTRY) { param_entry_use_status[i / 32] &= (~(1 << (i % 32))); UNLOCK; dev_dbg(&edma_dev.dev, "param no=%d\r\n", i); return i; } else { UNLOCK; return -1; /* no free param */ } }}/****************************************************************************** * * Free dma param entry: Freethe param entry number passed * Arguments: * param_no - Param entry to be released or freed out * * Return: N/A * *****************************************************************************/static void free_param(int param_no){ if (param_no >= 0 && param_no < DAVINCI_EDMA_NUM_PARAMENTRY) { LOCK; /* This is global data structure and could be accessed by several thread */ param_entry_use_status[param_no / 32] |= (1 << (param_no % 32)); UNLOCK; }}/****************************************************************************** * * DMA interrupt requests: Requests for the interrupt on the free channel * * Arguments: * lch - logical channel number for which the interrupt is to be requested * for the free channel. * callback - callback function registered for the requested interrupt * channel * data - channel private data. * * Return: free interrupt channel number on success, or negative error number * on failure * *****************************************************************************/static int request_dma_interrupt(int lch, void (*callback) (int lch, unsigned short ch_status, void *data), void *data, int param_no, int requested_tcc){ signed int free_intr_no = -1; int i = 0, j = 0, is_break = 0; /* edma channels */ if (lch >= 0 && lch < DAVINCI_EDMA_NUM_DMACH) { /* Bitmap dma_intr_use_status is used to identify availabe tcc for interrupt purpose. This could be modified by several thread and same structure is checked availabilty as well as updated once it's found that resource is avialable */ LOCK; if (dma_intr_use_status[lch / 32] & (1 << (lch % 32))) { /* in use */ dma_intr_use_status[lch / 32] &= (~(1 << (lch % 32))); UNLOCK; free_intr_no = lch; dev_dbg(&edma_dev.dev, "interrupt no=%d\r\n", free_intr_no); } else { UNLOCK; dev_dbg(&edma_dev.dev, "EDMA:Error\r\n"); return -1; } } /* qdma channels */ else if (lch >= DAVINCI_EDMA_NUM_DMACH && lch < (DAVINCI_EDMA_NUM_DMACH + DAVINCI_EDMA_NUM_QDMACH)) { if (requested_tcc != TCC_ANY) { /* Complete allocation algo requires lock and as it's shared resources could be invoked by several thread. Structure dma_intr_use_status is used to check whether resource is availabe or not and latter marked as not available in the same structure */ LOCK; if (dma_intr_use_status[requested_tcc / 32] & (1 << (requested_tcc % 32))) { j = 0; is_break = 1; while (dma_chan_no_event[j] != -1) { if (dma_chan_no_event[j] == requested_tcc) { is_break = 0; break; } j++; } if (!is_break) { dma_intr_use_status[requested_tcc / 32] &= (~(1 << (requested_tcc % 32))); free_intr_no = requested_tcc; dev_dbg(&edma_dev.dev, "interrupt no=%d\r\n", free_intr_no); } else { UNLOCK; dev_dbg(&edma_dev.dev, "Error - wrong tcc passed\r\n"); return -1; } UNLOCK; } else { UNLOCK; dev_dbg(&edma_dev.dev, "Error - wrong tcc passed\r\n"); return -1; } } else { i = 0; LOCK; while (i < DAVINCI_EDMA_NUM_DMACH) { j = 0; is_break = 1; if (dma_intr_use_status[i / 32] & (1 << (i % 32))) { while (dma_chan_no_event[j] != -1) { if (dma_chan_no_event[j] == i) { is_break = 0; break; } j++; } if (!is_break) { dma_intr_use_status[i / 32] &= (~(1 << (i % 32))); free_intr_no = i; dev_dbg(&edma_dev.dev, "interrupt no=%d\r\n", free_intr_no); break; } i++; } else { i++; } } UNLOCK; } } else { dev_dbg(&edma_dev.dev, "ERROR lch = %d\r\n", lch); } if (is_break) { dev_dbg(&edma_dev.dev, "While allocating EDMA channel for QDMA"); } if (lch >= DAVINCI_EDMA_NUM_DMACH && lch <
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -