⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dma.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  linux/arch/arm/mach-imx/dma.c * *  imx DMA registration and IRQ dispatching * *  This program 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. * *  2004-03-03 Sascha Hauer <sascha@saschahauer.de> *             initial version heavily inspired by *             linux/arch/arm/mach-pxa/dma.c * *  2005-04-17 Pavel Pisa <pisa@cmp.felk.cvut.cz> *             Changed to support scatter gather DMA *             by taking Russell's code from RiscPC * *  2006-05-31 Pavel Pisa <pisa@cmp.felk.cvut.cz> *             Corrected error handling code. * */#undef DEBUG#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/interrupt.h>#include <linux/errno.h>#include <asm/system.h>#include <asm/irq.h>#include <asm/hardware.h>#include <asm/dma.h>#include <asm/arch/imx-dma.h>struct imx_dma_channel imx_dma_channels[IMX_DMA_CHANNELS];/* * imx_dma_sg_next - prepare next chunk for scatter-gather DMA emulation * @dma_ch: i.MX DMA channel number * @lastcount: number of bytes transferred during last transfer * * Functions prepares DMA controller for next sg data chunk transfer. * The @lastcount argument informs function about number of bytes transferred * during last block. Zero value can be used for @lastcount to setup DMA * for the first chunk. */static inline int imx_dma_sg_next(imx_dmach_t dma_ch, unsigned int lastcount){	struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch];	unsigned int nextcount;	unsigned int nextaddr;	if (!imxdma->name) {		printk(KERN_CRIT "%s: called for  not allocated channel %d\n",		       __FUNCTION__, dma_ch);		return 0;	}	imxdma->resbytes -= lastcount;	if (!imxdma->sg) {		pr_debug("imxdma%d: no sg data\n", dma_ch);		return 0;	}	imxdma->sgbc += lastcount;	if ((imxdma->sgbc >= imxdma->sg->length) || !imxdma->resbytes) {		if ((imxdma->sgcount <= 1) || !imxdma->resbytes) {			pr_debug("imxdma%d: sg transfer limit reached\n",				 dma_ch);			imxdma->sgcount=0;			imxdma->sg = NULL;			return 0;		} else {			imxdma->sgcount--;			imxdma->sg++;			imxdma->sgbc = 0;		}	}	nextcount = imxdma->sg->length - imxdma->sgbc;	nextaddr = imxdma->sg->dma_address + imxdma->sgbc;	if(imxdma->resbytes < nextcount)		nextcount = imxdma->resbytes;	if ((imxdma->dma_mode & DMA_MODE_MASK) == DMA_MODE_READ)		DAR(dma_ch) = nextaddr;	else		SAR(dma_ch) = nextaddr;	CNTR(dma_ch) = nextcount;	pr_debug("imxdma%d: next sg chunk dst 0x%08x, src 0x%08x, size 0x%08x\n",		 dma_ch, DAR(dma_ch), SAR(dma_ch), CNTR(dma_ch));	return nextcount;}/* * imx_dma_setup_sg_base - scatter-gather DMA emulation * @dma_ch: i.MX DMA channel number * @sg: pointer to the scatter-gather list/vector * @sgcount: scatter-gather list hungs count * * Functions sets up i.MX DMA state for emulated scatter-gather transfer * and sets up channel registers to be ready for the first chunk */static intimx_dma_setup_sg_base(imx_dmach_t dma_ch,		      struct scatterlist *sg, unsigned int sgcount){	struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch];	imxdma->sg = sg;	imxdma->sgcount = sgcount;	imxdma->sgbc = 0;	return imx_dma_sg_next(dma_ch, 0);}/** * imx_dma_setup_single - setup i.MX DMA channel for linear memory to/from device transfer * @dma_ch: i.MX DMA channel number * @dma_address: the DMA/physical memory address of the linear data block *		to transfer * @dma_length: length of the data block in bytes * @dev_addr: physical device port address * @dmamode: DMA transfer mode, %DMA_MODE_READ from the device to the memory *           or %DMA_MODE_WRITE from memory to the device * * The function setups DMA channel source and destination addresses for transfer * specified by provided parameters. The scatter-gather emulation is disabled, * because linear data block * form the physical address range is transferred. * Return value: if incorrect parameters are provided -%EINVAL. *		Zero indicates success. */intimx_dma_setup_single(imx_dmach_t dma_ch, dma_addr_t dma_address,		     unsigned int dma_length, unsigned int dev_addr,		     dmamode_t dmamode){	struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch];	imxdma->sg = NULL;	imxdma->sgcount = 0;	imxdma->dma_mode = dmamode;	imxdma->resbytes = dma_length;	if (!dma_address) {		printk(KERN_ERR "imxdma%d: imx_dma_setup_single null address\n",		       dma_ch);		return -EINVAL;	}	if (!dma_length) {		printk(KERN_ERR "imxdma%d: imx_dma_setup_single zero length\n",		       dma_ch);		return -EINVAL;	}	if ((dmamode & DMA_MODE_MASK) == DMA_MODE_READ) {		pr_debug("imxdma%d: mx_dma_setup_single2dev dma_addressg=0x%08x dma_length=%d dev_addr=0x%08x for read\n",			dma_ch, (unsigned int)dma_address, dma_length,			dev_addr);		SAR(dma_ch) = dev_addr;		DAR(dma_ch) = (unsigned int)dma_address;	} else if ((dmamode & DMA_MODE_MASK) == DMA_MODE_WRITE) {		pr_debug("imxdma%d: mx_dma_setup_single2dev dma_addressg=0x%08x dma_length=%d dev_addr=0x%08x for write\n",			dma_ch, (unsigned int)dma_address, dma_length,			dev_addr);		SAR(dma_ch) = (unsigned int)dma_address;		DAR(dma_ch) = dev_addr;	} else {		printk(KERN_ERR "imxdma%d: imx_dma_setup_single bad dmamode\n",		       dma_ch);		return -EINVAL;	}	CNTR(dma_ch) = dma_length;	return 0;}/** * imx_dma_setup_sg - setup i.MX DMA channel SG list to/from device transfer * @dma_ch: i.MX DMA channel number * @sg: pointer to the scatter-gather list/vector * @sgcount: scatter-gather list hungs count * @dma_length: total length of the transfer request in bytes * @dev_addr: physical device port address * @dmamode: DMA transfer mode, %DMA_MODE_READ from the device to the memory *           or %DMA_MODE_WRITE from memory to the device * * The function sets up DMA channel state and registers to be ready for transfer * specified by provided parameters. The scatter-gather emulation is set up * according to the parameters. * * The full preparation of the transfer requires setup of more register * by the caller before imx_dma_enable() can be called. * * %BLR(dma_ch) holds transfer burst length in bytes, 0 means 64 bytes * * %RSSR(dma_ch) has to be set to the DMA request line source %DMA_REQ_xxx * * %CCR(dma_ch) has to specify transfer parameters, the next settings is typical * for linear or simple scatter-gather transfers if %DMA_MODE_READ is specified * * %CCR_DMOD_LINEAR | %CCR_DSIZ_32 | %CCR_SMOD_FIFO | %CCR_SSIZ_x * * The typical setup for %DMA_MODE_WRITE is specified by next options combination * * %CCR_SMOD_LINEAR | %CCR_SSIZ_32 | %CCR_DMOD_FIFO | %CCR_DSIZ_x * * Be careful here and do not mistakenly mix source and target device * port sizes constants, they are really different: * %CCR_SSIZ_8, %CCR_SSIZ_16, %CCR_SSIZ_32, * %CCR_DSIZ_8, %CCR_DSIZ_16, %CCR_DSIZ_32 * * Return value: if incorrect parameters are provided -%EINVAL. * Zero indicates success. */intimx_dma_setup_sg(imx_dmach_t dma_ch,		 struct scatterlist *sg, unsigned int sgcount, unsigned int dma_length,		 unsigned int dev_addr, dmamode_t dmamode){	int res;	struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch];	imxdma->sg = NULL;	imxdma->sgcount = 0;	imxdma->dma_mode = dmamode;	imxdma->resbytes = dma_length;	if (!sg || !sgcount) {		printk(KERN_ERR "imxdma%d: imx_dma_setup_sg epty sg list\n",		       dma_ch);		return -EINVAL;	}	if (!sg->length) {		printk(KERN_ERR "imxdma%d: imx_dma_setup_sg zero length\n",		       dma_ch);		return -EINVAL;	}	if ((dmamode & DMA_MODE_MASK) == DMA_MODE_READ) {		pr_debug("imxdma%d: mx_dma_setup_sg2dev sg=%p sgcount=%d total length=%d dev_addr=0x%08x for read\n",			dma_ch, sg, sgcount, dma_length, dev_addr);		SAR(dma_ch) = dev_addr;	} else if ((dmamode & DMA_MODE_MASK) == DMA_MODE_WRITE) {		pr_debug("imxdma%d: mx_dma_setup_sg2dev sg=%p sgcount=%d total length=%d dev_addr=0x%08x for write\n",			dma_ch, sg, sgcount, dma_length, dev_addr);		DAR(dma_ch) = dev_addr;	} else {		printk(KERN_ERR "imxdma%d: imx_dma_setup_sg bad dmamode\n",		       dma_ch);		return -EINVAL;	}	res = imx_dma_setup_sg_base(dma_ch, sg, sgcount);	if (res <= 0) {		printk(KERN_ERR "imxdma%d: no sg chunk ready\n", dma_ch);		return -EINVAL;	}	return 0;}/** * imx_dma_setup_handlers - setup i.MX DMA channel end and error notification handlers * @dma_ch: i.MX DMA channel number * @irq_handler: the pointer to the function called if the transfer *		ends successfully * @err_handler: the pointer to the function called if the premature *		end caused by error occurs * @data: user specified value to be passed to the handlers */intimx_dma_setup_handlers(imx_dmach_t dma_ch,		       void (*irq_handler) (int, void *),		       void (*err_handler) (int, void *, int),		       void *data){	struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch];	unsigned long flags;	if (!imxdma->name) {		printk(KERN_CRIT "%s: called for  not allocated channel %d\n",		       __FUNCTION__, dma_ch);		return -ENODEV;	}	local_irq_save(flags);	DISR = (1 << dma_ch);	imxdma->irq_handler = irq_handler;	imxdma->err_handler = err_handler;	imxdma->data = data;	local_irq_restore(flags);	return 0;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -