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

📄 dma.c

📁 本驱动程序在linux2.6.17中测试通过。yangxing msn:lelma_yx@hotmail.com 希望对SPI操作的朋友有所帮助。 一、工作方式: 从设备:SPI为MASTE
💻 C
📖 第 1 页 / 共 2 页
字号:
		break;	}	if (buf != NULL) {		/* update the chain to make sure that if we load any more		 * buffers when we call the callback function, things should		 * work properly */		chan->curr = buf->next;		buf->next  = NULL;		if (buf->magic != BUF_MAGIC) {			printk(KERN_ERR "dma%d: %s: buf %p incorrect magic\n",			       chan->number, __FUNCTION__, buf);			return IRQ_HANDLED;		}		s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK);		/* free resouces */		s3c2410_dma_freebuf(buf);	} else {	}	if (chan->next != NULL) {		unsigned long flags;		switch (chan->load_state) {		case S3C2410_DMALOAD_1RUNNING:			/* don't need to do anything for this state */			break;		case S3C2410_DMALOAD_NONE:			/* can load buffer immediately */			break;		case S3C2410_DMALOAD_1LOADED:			if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {				/* flag error? */				printk(KERN_ERR "dma%d: timeout waiting for load\n",				       chan->number);				return IRQ_HANDLED;			}			break;		case S3C2410_DMALOAD_1LOADED_1RUNNING:			goto no_load;		default:			printk(KERN_ERR "dma%d: unknown load_state in irq, %d\n",			       chan->number, chan->load_state);			return IRQ_HANDLED;		}		local_irq_save(flags);		s3c2410_dma_loadbuffer(chan, chan->next);		local_irq_restore(flags);	} else {		s3c2410_dma_lastxfer(chan);		/* see if we can stop this channel.. */		if (chan->load_state == S3C2410_DMALOAD_NONE) {			pr_debug("dma%d: end of transfer, stopping channel (%ld)\n",				 chan->number, jiffies);			s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP);		}	} no_load:	return IRQ_HANDLED;}/* s3c2410_request_dma * * get control of an dma channel*/int s3c2410_dma_request(unsigned int channel, s3c2410_dma_client_t *client,			void *dev){	s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];	unsigned long flags;	int err;	pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n",		 channel, client->name, dev);	check_channel(channel);	local_irq_save(flags);	dbg_showchan(chan);	if (chan->in_use) {		if (client != chan->client) {			printk(KERN_ERR "dma%d: already in use\n", channel);			local_irq_restore(flags);			return -EBUSY;		} else {			printk(KERN_ERR "dma%d: client already has channel\n", channel);		}	}	chan->client = client;	chan->in_use = 1;	if (!chan->irq_claimed) {		pr_debug("dma%d: %s : requesting irq %d\n",			 channel, __FUNCTION__, chan->irq);		err = request_irq(chan->irq, s3c2410_dma_irq, SA_INTERRUPT,				  client->name, (void *)chan);		if (err) {			chan->in_use = 0;			local_irq_restore(flags);			printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n",			       client->name, chan->irq, chan->number);			return err;		}		chan->irq_claimed = 1;		chan->irq_enabled = 1;	}	local_irq_restore(flags);	/* need to setup */	pr_debug("%s: channel initialised, %p\n", __FUNCTION__, chan);	return 0;}EXPORT_SYMBOL(s3c2410_dma_request);/* s3c2410_dma_free * * release the given channel back to the system, will stop and flush * any outstanding transfers, and ensure the channel is ready for the * next claimant. * * Note, although a warning is currently printed if the freeing client * info is not the same as the registrant's client info, the free is still * allowed to go through.*/int s3c2410_dma_free(dmach_t channel, s3c2410_dma_client_t *client){	s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];	unsigned long flags;	check_channel(channel);	local_irq_save(flags);	if (chan->client != client) {		printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n",		       channel, chan->client, client);	}	/* sort out stopping and freeing the channel */	if (chan->state != S3C2410_DMA_IDLE) {		pr_debug("%s: need to stop dma channel %p\n",		       __FUNCTION__, chan);		/* possibly flush the channel */		s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STOP);	}	chan->client = NULL;	chan->in_use = 0;	if (chan->irq_claimed)		free_irq(chan->irq, (void *)chan);	chan->irq_claimed = 0;	local_irq_restore(flags);	return 0;}EXPORT_SYMBOL(s3c2410_dma_free);static int s3c2410_dma_dostop(s3c2410_dma_chan_t *chan){	unsigned long tmp;	unsigned long flags;	pr_debug("%s:\n", __FUNCTION__);	dbg_showchan(chan);	local_irq_save(flags);	s3c2410_dma_call_op(chan,  S3C2410_DMAOP_STOP);	tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);	tmp |= S3C2410_DMASKTRIG_STOP;	dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp);#if 0	/* should also clear interrupts, according to WinCE BSP */	tmp = dma_rdreg(chan, S3C2410_DMA_DCON);	tmp |= S3C2410_DCON_NORELOAD;	dma_wrreg(chan, S3C2410_DMA_DCON, tmp);#endif	chan->state      = S3C2410_DMA_IDLE;	chan->load_state = S3C2410_DMALOAD_NONE;	local_irq_restore(flags);	return 0;}/* s3c2410_dma_flush * * stop the channel, and remove all current and pending transfers*/static int s3c2410_dma_flush(s3c2410_dma_chan_t *chan){	s3c2410_dma_buf_t *buf, *next;	unsigned long flags;	pr_debug("%s:\n", __FUNCTION__);	local_irq_save(flags);	if (chan->state != S3C2410_DMA_IDLE) {		pr_debug("%s: stopping channel...\n", __FUNCTION__ );		s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP);	}	buf = chan->curr;	if (buf == NULL)		buf = chan->next;	chan->curr = chan->next = chan->end = NULL;	if (buf != NULL) {		for ( ; buf != NULL; buf = next) {			next = buf->next;			pr_debug("%s: free buffer %p, next %p\n",			       __FUNCTION__, buf, buf->next);			s3c2410_dma_buffdone(chan, buf, S3C2410_RES_ABORT);			s3c2410_dma_freebuf(buf);		}	}	local_irq_restore(flags);	return 0;}ints3c2410_dma_ctrl(dmach_t channel, s3c2410_chan_op_t op){	s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];	check_channel(channel);	switch (op) {	case S3C2410_DMAOP_START:		return s3c2410_dma_start(chan);	case S3C2410_DMAOP_STOP:		return s3c2410_dma_dostop(chan);	case S3C2410_DMAOP_PAUSE:		return -ENOENT;	case S3C2410_DMAOP_RESUME:		return -ENOENT;	case S3C2410_DMAOP_FLUSH:		return s3c2410_dma_flush(chan);	case S3C2410_DMAOP_TIMEOUT:		return 0;	}	return -ENOENT;      /* unknown, don't bother */}EXPORT_SYMBOL(s3c2410_dma_ctrl);/* DMA configuration for each channel * * DISRCC -> source of the DMA (AHB,APB) * DISRC  -> source address of the DMA * DIDSTC -> destination of the DMA (AHB,APD) * DIDST  -> destination address of the DMA*//* s3c2410_dma_config * * xfersize:     size of unit in bytes (1,2,4) * dcon:         base value of the DCONx register*/int s3c2410_dma_config(dmach_t channel,		       int xferunit,		       int dcon){	s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];	pr_debug("%s: chan=%d, xfer_unit=%d, dcon=%08x\n",		 __FUNCTION__, channel, xferunit, dcon);	check_channel(channel);	switch (xferunit) {	case 1:		dcon |= S3C2410_DCON_BYTE;		break;	case 2:		dcon |= S3C2410_DCON_HALFWORD;		break;	case 4:		dcon |= S3C2410_DCON_WORD;		break;	default:		pr_debug("%s: bad transfer size %d\n", __FUNCTION__, xferunit);		return -EINVAL;	}	dcon |= S3C2410_DCON_HWTRIG;	dcon |= S3C2410_DCON_INTREQ;	pr_debug("%s: dcon now %08x\n", __FUNCTION__, dcon);	chan->dcon = dcon;	chan->xfer_unit = xferunit;	return 0;}EXPORT_SYMBOL(s3c2410_dma_config);int s3c2410_dma_setflags(dmach_t channel, unsigned int flags){	s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];	check_channel(channel);	pr_debug("%s: chan=%p, flags=%08x\n", __FUNCTION__, chan, flags);	chan->flags = flags;	return 0;}EXPORT_SYMBOL(s3c2410_dma_setflags);/* do we need to protect the settings of the fields from * irq?*/int s3c2410_dma_set_opfn(dmach_t channel, s3c2410_dma_opfn_t rtn){	s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];	check_channel(channel);	pr_debug("%s: chan=%p, op rtn=%p\n", __FUNCTION__, chan, rtn);	chan->op_fn = rtn;	return 0;}EXPORT_SYMBOL(s3c2410_dma_set_opfn);int s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn){	s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];	check_channel(channel);	pr_debug("%s: chan=%p, callback rtn=%p\n", __FUNCTION__, chan, rtn);	chan->callback_fn = rtn;	return 0;}EXPORT_SYMBOL(s3c2410_dma_set_buffdone_fn);/* s3c2410_dma_devconfig * * configure the dma source/destination hardware type and address * * source:    S3C2410_DMASRC_HW: source is hardware *            S3C2410_DMASRC_MEM: source is memory * * hwcfg:     the value for xxxSTCn register, *            bit 0: 0=increment pointer, 1=leave pointer *            bit 1: 0=soucre is AHB, 1=soucre is APB * * devaddr:   physical address of the source*/int s3c2410_dma_devconfig(int channel,			  s3c2410_dmasrc_t source,			  int hwcfg,			  unsigned long devaddr){	s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];	check_channel(channel);	pr_debug("%s: source=%d, hwcfg=%08x, devaddr=%08lx\n",		 __FUNCTION__, (int)source, hwcfg, devaddr);	chan->source = source;	chan->dev_addr = devaddr;	switch (source) {	case S3C2410_DMASRC_HW:		/* source is hardware */		pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d\n",			 __FUNCTION__, devaddr, hwcfg);		dma_wrreg(chan, S3C2410_DMA_DISRCC, hwcfg & 3);		dma_wrreg(chan, S3C2410_DMA_DISRC,  devaddr);		dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0));		chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST);		return 0;	case S3C2410_DMASRC_MEM:		/* source is memory */		pr_debug( "%s: mem source, devaddr=%08lx, hwcfg=%d\n",			  __FUNCTION__, devaddr, hwcfg);		dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0));		dma_wrreg(chan, S3C2410_DMA_DIDST,  devaddr);		dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3);		chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC);		return 0;	}	printk(KERN_ERR "dma%d: invalid source type (%d)\n", channel, source);	return -EINVAL;}EXPORT_SYMBOL(s3c2410_dma_devconfig);/* s3c2410_dma_getposition * * returns the current transfer points for the dma source and destination*/int s3c2410_dma_getposition(dmach_t channel, dma_addr_t *src, dma_addr_t *dst){ 	s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; 	check_channel(channel);	if (src != NULL) 		*src = dma_rdreg(chan, S3C2410_DMA_DCSRC); 	if (dst != NULL) 		*dst = dma_rdreg(chan, S3C2410_DMA_DCDST); 	return 0;}EXPORT_SYMBOL(s3c2410_dma_getposition);/* system device class */#ifdef CONFIG_PMstatic int s3c2410_dma_suspend(struct sys_device *dev, pm_message_t state){	s3c2410_dma_chan_t *cp = container_of(dev, s3c2410_dma_chan_t, dev);	printk(KERN_DEBUG "suspending dma channel %d\n", cp->number);	if (dma_rdreg(cp, S3C2410_DMA_DMASKTRIG) & S3C2410_DMASKTRIG_ON) {		/* the dma channel is still working, which is probably		 * a bad thing to do over suspend/resume. We stop the		 * channel and assume that the client is either going to		 * retry after resume, or that it is broken.		 */		printk(KERN_INFO "dma: stopping channel %d due to suspend\n",		       cp->number);		s3c2410_dma_dostop(cp);	}	return 0;}static int s3c2410_dma_resume(struct sys_device *dev){	return 0;}#else#define s3c2410_dma_suspend NULL#define s3c2410_dma_resume  NULL#endif /* CONFIG_PM */static struct sysdev_class dma_sysclass = {	set_kset_name("s3c24xx-dma"),	.suspend	= s3c2410_dma_suspend,	.resume		= s3c2410_dma_resume,};/* kmem cache implementation */static void s3c2410_dma_cache_ctor(void *p, kmem_cache_t *c, unsigned long f){	memset(p, 0, sizeof(s3c2410_dma_buf_t));}/* initialisation code */static int __init s3c2410_init_dma(void){	s3c2410_dma_chan_t *cp;	int channel;	int ret;	printk("S3C2410 DMA Driver, (c) 2003-2004 Simtec Electronics\n");	dma_base = ioremap(S3C24XX_PA_DMA, 0x200);	if (dma_base == NULL) {		printk(KERN_ERR "dma failed to remap register block\n");		return -ENOMEM;	}	ret = sysdev_class_register(&dma_sysclass);	if (ret != 0) {		printk(KERN_ERR "dma sysclass registration failed\n");		goto err;	}	dma_kmem = kmem_cache_create("dma_desc", sizeof(s3c2410_dma_buf_t), 0,				     SLAB_HWCACHE_ALIGN,				     s3c2410_dma_cache_ctor, NULL);	if (dma_kmem == NULL) {		printk(KERN_ERR "dma failed to make kmem cache\n");		ret = -ENOMEM;		goto err;	}	for (channel = 0; channel < S3C2410_DMA_CHANNELS; channel++) {		cp = &s3c2410_chans[channel];		memset(cp, 0, sizeof(s3c2410_dma_chan_t));		/* dma channel irqs are in order.. */		cp->number = channel;		cp->irq    = channel + IRQ_DMA0;		cp->regs   = dma_base + (channel*0x40);		/* point current stats somewhere */		cp->stats  = &cp->stats_store;		cp->stats_store.timeout_shortest = LONG_MAX;		/* basic channel configuration */		cp->load_timeout = 1<<18;		/* register system device */		cp->dev.cls = &dma_sysclass;		cp->dev.id  = channel;		ret = sysdev_register(&cp->dev);		printk("DMA channel %d at %p, irq %d\n",		       cp->number, cp->regs, cp->irq);	}	return 0; err:	kmem_cache_destroy(dma_kmem);	iounmap(dma_base);	dma_base = NULL;	return ret;}__initcall(s3c2410_init_dma);

⌨️ 快捷键说明

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