📄 dma-omap24xx.c
字号:
/* * linux/arch/arm/mach-omap2/dma-omap24xx.c * * Support functions for the OMAP2 internal DMA channels. * * Copyright (C) 2004 Texas Instruments, Inc. * * Copied from: * linux/arch/arm/omap/dma.c * Copyright (C) 2003 Nokia Corporation * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */#include <linux/module.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/spinlock.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <asm/system.h>#include <asm/irq.h>#include <asm/hardware.h>#include <asm/dma.h>#include <asm/io.h>/* enable this option to run the dma tests *///#define DMA_TEST#undef DMA_TEST#define OMAP_DMA4_CCR_EN (1 << 7)//#define DEBUG_PRINTS#undef DEBUG_PRINTS#ifdef DEBUG_PRINTS#define debug_printk(x) printk x#else#define debug_printk(x)#endifenum { READY, QUEUED, RUNNING };enum { DMA_CH_ALLOC_DONE, DMA_CH_PARAMS_SET_DONE, DMA_CH_STARTED, DMA_CH_QUEUED, DMA_CH_DONE, DMA_CH_PAUSED, DMA_CH_LINK_ENABLED};typedef struct omap_dma_lch_t { int dev_id; int reserved; const char *dev_name; void (*callback) (int lch, u16 ch_status, void *data); void *data; int next_channel; int status; int params_set; lch_chain *mychain; int enabled_irqs;} omap_dma_lch;static int dma_chan_count;static spinlock_t dma_chan_lock;static omap_dma_lch dma_chan[OMAP24XX_LOGICAL_DMA_CH_COUNT];static void clear_lch_regs(int lch){ int i; u32 lch_base = OMAP_DMA4_BASE + lch * 0x60 + 0x80; //printk(KERN_INFO "\n lch_base = %x", lch_base); for (i = 0; i < 0x44; i += 4) writel(0, lch_base + i);}/*dma_trigger - hardware trigger number for hardware sychronized channel */void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, int frame_count, int sync_mode, int dma_trigger, int src_or_dst_synch){ u32 w; int ch = lch; w = readl(OMAP_DMA4_CSDP_REG(ch)); w &= ~0x03; w |= data_type; writel(w, OMAP_DMA4_CSDP_REG(ch)); if (dma_trigger) { w = readl(OMAP_DMA4_CCR_REG(ch)); if (dma_trigger > 63) w |= 1 << 20; if (dma_trigger > 31) w |= 1 << 19; w |= (dma_trigger & 0x1f); if (sync_mode & 0x1) w |= 1 << 18; if (sync_mode & 0x2) w |= 1 << 5; if (src_or_dst_synch) w |= 1 << 24; /* source synch */ else w &= ~(1 << 24); /* dest synch */ writel(w, OMAP_DMA4_CCR_REG(ch)); } writel(elem_count, OMAP_DMA4_CEN_REG(ch)); writel(frame_count, OMAP_DMA4_CFN_REG(ch));}void omap_set_dma_src_params(int lch, int src_amode, int src_start, int src_ei, int src_fi){ u32 w; w = readl(OMAP_DMA4_CCR_REG(lch)); w &= ~(0x03 << 12); w |= src_amode << 12; writel(w, OMAP_DMA4_CCR_REG(lch)); writel(src_start, OMAP_DMA4_CSSA_REG(lch)); writel(src_ei, OMAP_DMA4_CSEI_REG(lch)); writel(src_fi, OMAP_DMA4_CSFI_REG(lch));}void omap_set_dma_dest_params(int lch, int dest_amode, int dest_start, int dst_ei, int dst_fi){ u32 w; w = readl(OMAP_DMA4_CCR_REG(lch)); w &= ~(0x03 << 14); w |= dest_amode << 14; writel(w, OMAP_DMA4_CCR_REG(lch)); writel(dest_start, OMAP_DMA4_CDSA_REG(lch)); writel(dst_ei, OMAP_DMA4_CDEI_REG(lch)); writel(dst_fi, OMAP_DMA4_CDFI_REG(lch));}void omap_set_dma_params(int lch, dma_channel_params params){ omap_set_dma_transfer_params(lch, params.data_type, params.elem_count, params.frame_count, params.sync_mode, params.trigger, params.src_or_dst_synch); omap_set_dma_src_params(lch, params.src_amode, params.src_start, params.src_ei, params.src_fi); omap_set_dma_dest_params(lch, params.dst_amode, params.dst_start, params.dst_ei, params.dst_fi); dma_chan[lch].params_set = 1;}int omap_set_dma_params_chain(lch_chain * chain, dma_channel_params params){ int lch; unsigned long flags; spin_lock_irqsave(&dma_chan_lock, flags); //debug debug_printk((KERN_INFO "\n omap_set_dma_params_chain : ")); /* queue full */ if (dma_chan[chain->queue_tail].next_channel == chain->queue_head) { debug_printk("\n error : queue full"); return -EAGAIN; } if (!chain->started) lch = chain->queue_head; else lch = dma_chan[chain->queue_tail].next_channel; //debug debug_printk((KERN_INFO "\n chain = %x , dma_chan[lch].params_started = %d channel = %d ", chain, dma_chan[lch].params_set, lch)); /* omap_set_dma_params_chain and omap_start_dma_chain should be called in lock steps */ if (dma_chan[lch].params_set == 1) { printk(KERN_INFO "\n omap_set_dma_params_chain and omap_start_dma_chain should be called in lock steps ch = %d ", lch); return -EAGAIN; } omap_set_dma_params(lch, params); spin_unlock_irqrestore(&dma_chan_lock, flags); return 0;}int omap_start_dma(int lch){ u32 w; /* start the dma enable the lch */ /* and return success */ w = readl(OMAP_DMA4_CCR_REG(lch)); w |= OMAP_DMA4_CCR_EN; writel(w, OMAP_DMA4_CCR_REG(lch)); return 0;}int omap_get_dma_pos_dst(int lch){ return (readl(OMAP_DMA4_CDAC_REG(lch)));}int omap_get_dma_pos_src(int lch){ return (readl(OMAP_DMA4_CSAC_REG(lch)));}int omap_start_dma_chain(lch_chain * chain){ int lch; unsigned long flags; spin_lock_irqsave(&dma_chan_lock, flags); //debug debug_printk((KERN_INFO "\n omap_start_dma_chain")); if (!chain->started) { /* dma chain not started */ //debug debug_printk((KERN_INFO "\n chain started ")); lch = chain->queue_head; if (!dma_chan[lch].params_set) { printk(KERN_INFO "\n error : trying to start/queue channel %d without setting tha parameters", lch); return -1; //error condition } /* start the dma enable the lch */ /* and return success */ omap_start_dma(lch); chain->started = 1; } else { /* enable the chain for the channel in queue */ //debug debug_printk(("\n channel chain queued")); lch = dma_chan[chain->queue_tail].next_channel;; if (!dma_chan[lch].params_set) { printk(KERN_INFO "\n error : trying to start/queue channel %d without setting tha parameters", lch); return -1; //error condition } enable_chain(chain->queue_tail, lch); /* next dma transfer queued */ chain->queue_tail = lch; } chain->queued = chain->queued + 1; debug_printk(("\n startdma : chain->head = %d , chain -> queue->tail = %d queued = %d", chain->queue_head, chain->queue_tail, chain->queued)); spin_unlock_irqrestore(&dma_chan_lock, flags); return 0;}/* Chain functions *//* START */void setup_chain(int lch1, int lch2){ writel(lch2, OMAP_DMA4_CLNK_CTRL_REG(lch1));}void remove_chain(int lch){ writel(0x0, OMAP_DMA4_CLNK_CTRL_REG(lch));}void enable_chain(int lch1, int lch2){ /* next dma transfer queued */ int w; w = readl(OMAP_DMA4_CLNK_CTRL_REG(lch1)); if ((w & 0x1f) != lch2) printk(KERN_INFO "\n error in linking channel, channels should be linked upfront, it is only enabled here"); w |= (1 << 15); writel(w, OMAP_DMA4_CLNK_CTRL_REG(lch1));}void disable_chain(int lch){ int w; w = readl(OMAP_DMA4_CLNK_CTRL_REG(lch)); w &= ~(1 << 15); writel(w, OMAP_DMA4_CLNK_CTRL_REG(lch));}/* Chain functions *//* END */void omap_stop_dma(int lch){ u32 w; w = readl(OMAP_DMA4_CCR_REG(lch)); w &= ~OMAP_DMA4_CCR_EN; writel(w, OMAP_DMA4_CCR_REG(lch));}void omap_stop_dma_chain(lch_chain * chain){ int lch; int i; lch = chain->queue_head; omap_stop_dma(lch); chain->started = 0; /* chain stopped */ /* traverse through all the chained logical channels and disable them */ for (i = 0; i < chain->num_channels; i++) { omap_stop_dma(lch); disable_chain(lch); lch = dma_chan[lch].next_channel; }}void omap_enable_dma_irq(int lch){ u32 w; w = readl(OMAP_DMA4_CICR_REG(lch)); w |= dma_chan[lch].enabled_irqs; writel(w, OMAP_DMA4_CICR_REG(lch));}void omap_disable_dma_irq(int lch){ u32 w; w = readl(OMAP_DMA4_CICR_REG(lch)); w &= dma_chan[lch].enabled_irqs; writel(w, OMAP_DMA4_CICR_REG(lch));}static int dma_handle_ch(int ch){ u32 w; u32 csr_reg; w = readl(OMAP_DMA4_CSR_REG(ch)); csr_reg = w; debug_printk((KERN_INFO "\n interrupt recieved = %x", w)); if (!w) return 0; if (unlikely(dma_chan[ch].dev_id == -1)) return 0; if (unlikely(w & OMAP_DMA_TOUT_IRQ)) printk(KERN_INFO "DMA timeout with device %d\n", dma_chan[ch].dev_id); if (unlikely(w & OMAP_DMA_DROP_IRQ)) printk(KERN_INFO "DMA synchronization event drop occurred with device %d\n", dma_chan[ch].dev_id); /* if transfer completed for a channel , update the data structures */ /* this interrupt is required by the framework */ if (w & OMAP_DMA_BLOCK_IRQ) { lch_chain *chain = dma_chan[ch].mychain; debug_printk((KERN_INFO "\n got end of block interrupt for ch = %d ", ch)); if (chain) { if (chain->queue_head != ch) printk(KERN_INFO "\n error in queueing logic QH=%d CH=%d",chain->queue_head,ch); // should never be true chain->queued = chain->queued - 1; if (chain->queued <= 0) { debug_printk ("\n queueing underrun : restarting the chain"); chain->queue_head = ch; chain->queue_tail = ch; chain->started = 0; omap_stop_dma(ch); //clear_lch_regs(ch); } else { chain->queue_head = dma_chan[ch].next_channel; } dma_chan[ch].params_set = 0; debug_printk(("\n end_dma : chain = %x chain->head = %d , chain -> queue->tail = %d queued = %d", chain, chain->queue_head, chain->queue_tail, chain->queued)); //chain->queued = chain->queued - 1; } } w = writel(0x20, OMAP_DMA4_CSR_REG(ch)); w = readl(OMAP_DMA4_IRQSTATUS_L0); w = 1 << (ch); /* ch in this function is from 0-31 while in register it is 1-32 */ writel(w, OMAP_DMA4_IRQSTATUS_L0);#ifdef DMA_TEST /* debug */ w = readl(OMAP_DMA4_IRQSTATUS_L0); debug_printk((KERN_INFO "IRQSTATUS_L0 = %x", w)); w = readl(OMAP_DMA4_CSR_REG(ch)); debug_printk((KERN_INFO "CSR_REG = %x", w)); w = readl(OMAP_DMA4_CICR_REG(ch)); debug_printk((KERN_INFO "CICR_REG = %x", w));#endif if (likely(dma_chan[ch].callback != NULL)) dma_chan[ch].callback(ch, w, dma_chan[ch].data); return 0;}static irqreturn_t omap24xx_dma4_irq_demux(int irq, void *dev_id, struct pt_regs *regs){ u32 w; int i; w = readl(OMAP_DMA4_IRQSTATUS_L0); debug_printk((KERN_INFO "\n demux IRQSTATUS_L0 = %x", w)); for (i = 1; i <= OMAP24XX_LOGICAL_DMA_CH_COUNT; i++) { int val = w & (1 << (i - 1)); if (val) dma_handle_ch(i - 1); /* STATUS register count is from 1-32 while our is 0-31 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -