📄 dma.c
字号:
if (chan == NULL) return -EINVAL; switch (op) { case S3C2410_DMAOP_START: return s3c2410_dma_start(chan); case S3C2410_DMAOP_STOP: return s3c2410_dma_dostop(chan); case S3C2410_DMAOP_PAUSE: case S3C2410_DMAOP_RESUME: return -ENOENT; case S3C2410_DMAOP_FLUSH: return s3c2410_dma_flush(chan); case S3C2410_DMAOP_STARTED: return s3c2410_dma_started(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){ struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); pr_debug("%s: chan=%d, xfer_unit=%d, dcon=%08x\n", __FUNCTION__, channel, xferunit, dcon); if (chan == NULL) return -EINVAL; pr_debug("%s: Initial dcon is %08x\n", __FUNCTION__, dcon); dcon |= chan->dcon & dma_sel.dcon_mask; pr_debug("%s: New dcon is %08x\n", __FUNCTION__, dcon); 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){ struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); if (chan == NULL) return -EINVAL; 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){ struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); if (chan == NULL) return -EINVAL; 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){ struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); if (chan == NULL) return -EINVAL; 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=source is AHB, 1=source is APB * * devaddr: physical address of the source*/int s3c2410_dma_devconfig(int channel, enum s3c2410_dmasrc source, int hwcfg, unsigned long devaddr){ struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); if (chan == NULL) return -EINVAL; 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){ struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); if (chan == NULL) return -EINVAL; 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){ struct s3c2410_dma_chan *cp = container_of(dev, struct s3c2410_dma_chan, 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 */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(struct kmem_cache *c, void *p){ memset(p, 0, sizeof(struct s3c2410_dma_buf));}/* initialisation code */static int __init s3c24xx_dma_sysclass_init(void){ int ret = sysdev_class_register(&dma_sysclass); if (ret != 0) printk(KERN_ERR "dma sysclass registration failed\n"); return ret;}core_initcall(s3c24xx_dma_sysclass_init);static int __init s3c24xx_dma_sysdev_register(void){ struct s3c2410_dma_chan *cp = s3c2410_chans; int channel, ret; for (channel = 0; channel < dma_channels; cp++, channel++) { cp->dev.cls = &dma_sysclass; cp->dev.id = channel; ret = sysdev_register(&cp->dev); if (ret) { printk(KERN_ERR "error registering dev for dma %d\n", channel); return ret; } } return 0;}late_initcall(s3c24xx_dma_sysdev_register);int __init s3c24xx_dma_init(unsigned int channels, unsigned int irq, unsigned int stride){ struct s3c2410_dma_chan *cp; int channel; int ret; printk("S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics\n"); dma_channels = channels; dma_base = ioremap(S3C24XX_PA_DMA, stride * channels); if (dma_base == NULL) { printk(KERN_ERR "dma failed to remap register block\n"); return -ENOMEM; } dma_kmem = kmem_cache_create("dma_desc", sizeof(struct s3c2410_dma_buf), 0, SLAB_HWCACHE_ALIGN, s3c2410_dma_cache_ctor); if (dma_kmem == NULL) { printk(KERN_ERR "dma failed to make kmem cache\n"); ret = -ENOMEM; goto err; } for (channel = 0; channel < channels; channel++) { cp = &s3c2410_chans[channel]; memset(cp, 0, sizeof(struct s3c2410_dma_chan)); /* dma channel irqs are in order.. */ cp->number = channel; cp->irq = channel + irq; cp->regs = dma_base + (channel * stride); /* point current stats somewhere */ cp->stats = &cp->stats_store; cp->stats_store.timeout_shortest = LONG_MAX; /* basic channel configuration */ cp->load_timeout = 1<<18; 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;}int __init s3c2410_dma_init(void){ return s3c24xx_dma_init(4, IRQ_DMA0, 0x40);}static inline int is_channel_valid(unsigned int channel){ return (channel & DMA_CH_VALID);}static struct s3c24xx_dma_order *dma_order;/* s3c2410_dma_map_channel() * * turn the virtual channel number into a real, and un-used hardware * channel. * * first, try the dma ordering given to us by either the relevant * dma code, or the board. Then just find the first usable free * channel*/static struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel){ struct s3c24xx_dma_order_ch *ord = NULL; struct s3c24xx_dma_map *ch_map; struct s3c2410_dma_chan *dmach; int ch; if (dma_sel.map == NULL || channel > dma_sel.map_size) return NULL; ch_map = dma_sel.map + channel; /* first, try the board mapping */ if (dma_order) { ord = &dma_order->channels[channel]; for (ch = 0; ch < dma_channels; ch++) { if (!is_channel_valid(ord->list[ch])) continue; if (s3c2410_chans[ord->list[ch]].in_use == 0) { ch = ord->list[ch] & ~DMA_CH_VALID; goto found; } } if (ord->flags & DMA_CH_NEVER) return NULL; } /* second, search the channel map for first free */ for (ch = 0; ch < dma_channels; ch++) { if (!is_channel_valid(ch_map->channels[ch])) continue; if (s3c2410_chans[ch].in_use == 0) { printk("mapped channel %d to %d\n", channel, ch); break; } } if (ch >= dma_channels) return NULL; /* update our channel mapping */ found: dmach = &s3c2410_chans[ch]; dma_chan_map[channel] = dmach; /* select the channel */ (dma_sel.select)(dmach, ch_map); return dmach;}static int s3c24xx_dma_check_entry(struct s3c24xx_dma_map *map, int ch){ return 0;}int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel){ struct s3c24xx_dma_map *nmap; size_t map_sz = sizeof(*nmap) * sel->map_size; int ptr; nmap = kmalloc(map_sz, GFP_KERNEL); if (nmap == NULL) return -ENOMEM; memcpy(nmap, sel->map, map_sz); memcpy(&dma_sel, sel, sizeof(*sel)); dma_sel.map = nmap; for (ptr = 0; ptr < sel->map_size; ptr++) s3c24xx_dma_check_entry(nmap+ptr, ptr); return 0;}int __init s3c24xx_dma_order_set(struct s3c24xx_dma_order *ord){ struct s3c24xx_dma_order *nord = dma_order; if (nord == NULL) nord = kmalloc(sizeof(struct s3c24xx_dma_order), GFP_KERNEL); if (nord == NULL) { printk(KERN_ERR "no memory to store dma channel order\n"); return -ENOMEM; } dma_order = nord; memcpy(nord, ord, sizeof(struct s3c24xx_dma_order)); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -