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

📄 dma.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  linux/arch/arm/mach-pnx4008/dma.c * *  PNX4008 DMA registration and IRQ dispatching * *  Author:	Vitaly Wool *  Copyright:	MontaVista Software Inc. (c) 2005 * *  Based on the code from Nicolas Pitre * *  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. */#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/interrupt.h>#include <linux/errno.h>#include <linux/err.h>#include <linux/dma-mapping.h>#include <linux/clk.h>#include <asm/system.h>#include <asm/hardware.h>#include <asm/dma.h>#include <asm/dma-mapping.h>#include <asm/io.h>#include <asm/mach/dma.h>#include <asm/arch/clock.h>static struct dma_channel {	char *name;	void (*irq_handler) (int, int, void *);	void *data;	struct pnx4008_dma_ll *ll;	u32 ll_dma;	void *target_addr;	int target_id;} dma_channels[MAX_DMA_CHANNELS];static struct ll_pool {	void *vaddr;	void *cur;	dma_addr_t dma_addr;	int count;} ll_pool;static DEFINE_SPINLOCK(ll_lock);struct pnx4008_dma_ll *pnx4008_alloc_ll_entry(dma_addr_t * ll_dma){	struct pnx4008_dma_ll *ll = NULL;	unsigned long flags;	spin_lock_irqsave(&ll_lock, flags);	if (ll_pool.count > 4) { /* can give one more */		ll = *(struct pnx4008_dma_ll **) ll_pool.cur;		*ll_dma = ll_pool.dma_addr + ((void *)ll - ll_pool.vaddr);		*(void **)ll_pool.cur = **(void ***)ll_pool.cur;		memset(ll, 0, sizeof(*ll));		ll_pool.count--;	}	spin_unlock_irqrestore(&ll_lock, flags);	return ll;}EXPORT_SYMBOL_GPL(pnx4008_alloc_ll_entry);void pnx4008_free_ll_entry(struct pnx4008_dma_ll * ll, dma_addr_t ll_dma){	unsigned long flags;	if (ll) {		if ((unsigned long)((long)ll - (long)ll_pool.vaddr) > 0x4000) {			printk(KERN_ERR "Trying to free entry not allocated by DMA\n");			BUG();		}		if (ll->flags & DMA_BUFFER_ALLOCATED)			ll->free(ll->alloc_data);		spin_lock_irqsave(&ll_lock, flags);		*(long *)ll = *(long *)ll_pool.cur;		*(long *)ll_pool.cur = (long)ll;		ll_pool.count++;		spin_unlock_irqrestore(&ll_lock, flags);	}}EXPORT_SYMBOL_GPL(pnx4008_free_ll_entry);void pnx4008_free_ll(u32 ll_dma, struct pnx4008_dma_ll * ll){	struct pnx4008_dma_ll *ptr;	u32 dma;	while (ll) {		dma = ll->next_dma;		ptr = ll->next;		pnx4008_free_ll_entry(ll, ll_dma);		ll_dma = dma;		ll = ptr;	}}EXPORT_SYMBOL_GPL(pnx4008_free_ll);static int dma_channels_requested = 0;static inline void dma_increment_usage(void){	if (!dma_channels_requested++) {		struct clk *clk = clk_get(0, "dma_ck");		if (!IS_ERR(clk)) {			clk_set_rate(clk, 1);			clk_put(clk);		}		pnx4008_config_dma(-1, -1, 1);	}}static inline void dma_decrement_usage(void){	if (!--dma_channels_requested) {		struct clk *clk = clk_get(0, "dma_ck");		if (!IS_ERR(clk)) {			clk_set_rate(clk, 0);			clk_put(clk);		}		pnx4008_config_dma(-1, -1, 0);	}}static DEFINE_SPINLOCK(dma_lock);static inline void pnx4008_dma_lock(void){	spin_lock_irq(&dma_lock);}static inline void pnx4008_dma_unlock(void){	spin_unlock_irq(&dma_lock);}#define VALID_CHANNEL(c)	(((c) >= 0) && ((c) < MAX_DMA_CHANNELS))int pnx4008_request_channel(char *name, int ch,			    void (*irq_handler) (int, int, void *), void *data){	int i, found = 0;	/* basic sanity checks */	if (!name || (ch != -1 && !VALID_CHANNEL(ch)))		return -EINVAL;	pnx4008_dma_lock();	/* try grabbing a DMA channel with the requested priority */	for (i = MAX_DMA_CHANNELS - 1; i >= 0; i--) {		if (!dma_channels[i].name && (ch == -1 || ch == i)) {			found = 1;			break;		}	}	if (found) {		dma_increment_usage();		dma_channels[i].name = name;		dma_channels[i].irq_handler = irq_handler;		dma_channels[i].data = data;		dma_channels[i].ll = NULL;		dma_channels[i].ll_dma = 0;	} else {		printk(KERN_WARNING "No more available DMA channels for %s\n",		       name);		i = -ENODEV;	}	pnx4008_dma_unlock();	return i;}EXPORT_SYMBOL_GPL(pnx4008_request_channel);void pnx4008_free_channel(int ch){	if (!dma_channels[ch].name) {		printk(KERN_CRIT		       "%s: trying to free channel %d which is already freed\n",		       __FUNCTION__, ch);		return;	}	pnx4008_dma_lock();	pnx4008_free_ll(dma_channels[ch].ll_dma, dma_channels[ch].ll);	dma_channels[ch].ll = NULL;	dma_decrement_usage();	dma_channels[ch].name = NULL;	pnx4008_dma_unlock();}EXPORT_SYMBOL_GPL(pnx4008_free_channel);int pnx4008_config_dma(int ahb_m1_be, int ahb_m2_be, int enable){	unsigned long dma_cfg = __raw_readl(DMAC_CONFIG);	switch (ahb_m1_be) {	case 0:		dma_cfg &= ~(1 << 1);		break;	case 1:		dma_cfg |= (1 << 1);		break;	default:		break;	}	switch (ahb_m2_be) {	case 0:		dma_cfg &= ~(1 << 2);		break;	case 1:		dma_cfg |= (1 << 2);		break;	default:		break;	}	switch (enable) {	case 0:		dma_cfg &= ~(1 << 0);		break;	case 1:		dma_cfg |= (1 << 0);		break;	default:		break;	}	pnx4008_dma_lock();	__raw_writel(dma_cfg, DMAC_CONFIG);	pnx4008_dma_unlock();	return 0;}EXPORT_SYMBOL_GPL(pnx4008_config_dma);int pnx4008_dma_pack_control(const struct pnx4008_dma_ch_ctrl * ch_ctrl,			     unsigned long *ctrl){	int i = 0, dbsize, sbsize, err = 0;	if (!ctrl || !ch_ctrl) {		err = -EINVAL;		goto out;	}	*ctrl = 0;	switch (ch_ctrl->tc_mask) {	case 0:		break;	case 1:		*ctrl |= (1 << 31);		break;	default:		err = -EINVAL;		goto out;	}	switch (ch_ctrl->cacheable) {	case 0:		break;	case 1:		*ctrl |= (1 << 30);		break;	default:		err = -EINVAL;		goto out;	}	switch (ch_ctrl->bufferable) {	case 0:		break;	case 1:		*ctrl |= (1 << 29);		break;	default:		err = -EINVAL;		goto out;	}	switch (ch_ctrl->priv_mode) {	case 0:		break;	case 1:		*ctrl |= (1 << 28);		break;	default:		err = -EINVAL;		goto out;	}	switch (ch_ctrl->di) {	case 0:		break;	case 1:		*ctrl |= (1 << 27);		break;	default:		err = -EINVAL;		goto out;	}	switch (ch_ctrl->si) {	case 0:		break;	case 1:		*ctrl |= (1 << 26);		break;	default:		err = -EINVAL;		goto out;	}	switch (ch_ctrl->dest_ahb1) {	case 0:		break;	case 1:		*ctrl |= (1 << 25);		break;	default:		err = -EINVAL;		goto out;	}	switch (ch_ctrl->src_ahb1) {	case 0:		break;	case 1:		*ctrl |= (1 << 24);		break;	default:		err = -EINVAL;		goto out;	}	switch (ch_ctrl->dwidth) {	case WIDTH_BYTE:		*ctrl &= ~(7 << 21);		break;	case WIDTH_HWORD:		*ctrl &= ~(7 << 21);		*ctrl |= (1 << 21);		break;	case WIDTH_WORD:		*ctrl &= ~(7 << 21);		*ctrl |= (2 << 21);		break;	default:		err = -EINVAL;		goto out;	}	switch (ch_ctrl->swidth) {	case WIDTH_BYTE:		*ctrl &= ~(7 << 18);		break;	case WIDTH_HWORD:		*ctrl &= ~(7 << 18);		*ctrl |= (1 << 18);		break;	case WIDTH_WORD:		*ctrl &= ~(7 << 18);		*ctrl |= (2 << 18);		break;	default:		err = -EINVAL;		goto out;	}	dbsize = ch_ctrl->dbsize;	while (!(dbsize & 1)) {		i++;		dbsize >>= 1;	}	if (ch_ctrl->dbsize != 1 || i > 8 || i == 1) {		err = -EINVAL;		goto out;	} else if (i > 1)		i--;	*ctrl &= ~(7 << 15);	*ctrl |= (i << 15);	sbsize = ch_ctrl->sbsize;	while (!(sbsize & 1)) {		i++;		sbsize >>= 1;	}	if (ch_ctrl->sbsize != 1 || i > 8 || i == 1) {		err = -EINVAL;		goto out;	} else if (i > 1)		i--;	*ctrl &= ~(7 << 12);	*ctrl |= (i << 12);	if (ch_ctrl->tr_size > 0x7ff) {		err = -E2BIG;		goto out;	}	*ctrl &= ~0x7ff;	*ctrl |= ch_ctrl->tr_size & 0x7ff;out:	return err;}EXPORT_SYMBOL_GPL(pnx4008_dma_pack_control);int pnx4008_dma_parse_control(unsigned long ctrl,			      struct pnx4008_dma_ch_ctrl * ch_ctrl){	int err = 0;	if (!ch_ctrl) {		err = -EINVAL;		goto out;	}	ch_ctrl->tr_size = ctrl & 0x7ff;	ctrl >>= 12;	ch_ctrl->sbsize = 1 << (ctrl & 7);	if (ch_ctrl->sbsize > 1)		ch_ctrl->sbsize <<= 1;	ctrl >>= 3;	ch_ctrl->dbsize = 1 << (ctrl & 7);	if (ch_ctrl->dbsize > 1)		ch_ctrl->dbsize <<= 1;	ctrl >>= 3;	switch (ctrl & 7) {	case 0:		ch_ctrl->swidth = WIDTH_BYTE;		break;	case 1:		ch_ctrl->swidth = WIDTH_HWORD;		break;	case 2:		ch_ctrl->swidth = WIDTH_WORD;		break;	default:		err = -EINVAL;		goto out;	}	ctrl >>= 3;	switch (ctrl & 7) {	case 0:		ch_ctrl->dwidth = WIDTH_BYTE;		break;	case 1:		ch_ctrl->dwidth = WIDTH_HWORD;		break;	case 2:		ch_ctrl->dwidth = WIDTH_WORD;		break;	default:		err = -EINVAL;		goto out;	}	ctrl >>= 3;	ch_ctrl->src_ahb1 = ctrl & 1;	ctrl >>= 1;	ch_ctrl->dest_ahb1 = ctrl & 1;	ctrl >>= 1;	ch_ctrl->si = ctrl & 1;	ctrl >>= 1;	ch_ctrl->di = ctrl & 1;	ctrl >>= 1;	ch_ctrl->priv_mode = ctrl & 1;	ctrl >>= 1;	ch_ctrl->bufferable = ctrl & 1;	ctrl >>= 1;	ch_ctrl->cacheable = ctrl & 1;	ctrl >>= 1;	ch_ctrl->tc_mask = ctrl & 1;out:	return err;}EXPORT_SYMBOL_GPL(pnx4008_dma_parse_control);int pnx4008_dma_pack_config(const struct pnx4008_dma_ch_config * ch_cfg,			    unsigned long *cfg){	int err = 0;	if (!cfg || !ch_cfg) {		err = -EINVAL;		goto out;	}	*cfg = 0;	switch (ch_cfg->halt) {	case 0:		break;	case 1:		*cfg |= (1 << 18);		break;	default:		err = -EINVAL;		goto out;	}	switch (ch_cfg->active) {	case 0:		break;	case 1:		*cfg |= (1 << 17);		break;	default:		err = -EINVAL;		goto out;	}	switch (ch_cfg->lock) {	case 0:		break;	case 1:		*cfg |= (1 << 16);		break;

⌨️ 快捷键说明

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