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

📄 dma.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	default:		err = -EINVAL;		goto out;	}	switch (ch_cfg->itc) {	case 0:		break;	case 1:		*cfg |= (1 << 15);		break;	default:		err = -EINVAL;		goto out;	}	switch (ch_cfg->ie) {	case 0:		break;	case 1:		*cfg |= (1 << 14);		break;	default:		err = -EINVAL;		goto out;	}	switch (ch_cfg->flow_cntrl) {	case FC_MEM2MEM_DMA:		*cfg &= ~(7 << 11);		break;	case FC_MEM2PER_DMA:		*cfg &= ~(7 << 11);		*cfg |= (1 << 11);		break;	case FC_PER2MEM_DMA:		*cfg &= ~(7 << 11);		*cfg |= (2 << 11);		break;	case FC_PER2PER_DMA:		*cfg &= ~(7 << 11);		*cfg |= (3 << 11);		break;	case FC_PER2PER_DPER:		*cfg &= ~(7 << 11);		*cfg |= (4 << 11);		break;	case FC_MEM2PER_PER:		*cfg &= ~(7 << 11);		*cfg |= (5 << 11);		break;	case FC_PER2MEM_PER:		*cfg &= ~(7 << 11);		*cfg |= (6 << 11);		break;	case FC_PER2PER_SPER:		*cfg |= (7 << 11);		break;	default:		err = -EINVAL;		goto out;	}	*cfg &= ~(0x1f << 6);	*cfg |= ((ch_cfg->dest_per & 0x1f) << 6);	*cfg &= ~(0x1f << 1);	*cfg |= ((ch_cfg->src_per & 0x1f) << 1);out:	return err;}EXPORT_SYMBOL_GPL(pnx4008_dma_pack_config);int pnx4008_dma_parse_config(unsigned long cfg,			     struct pnx4008_dma_ch_config * ch_cfg){	int err = 0;	if (!ch_cfg) {		err = -EINVAL;		goto out;	}	cfg >>= 1;	ch_cfg->src_per = cfg & 0x1f;	cfg >>= 5;	ch_cfg->dest_per = cfg & 0x1f;	cfg >>= 5;	switch (cfg & 7) {	case 0:		ch_cfg->flow_cntrl = FC_MEM2MEM_DMA;		break;	case 1:		ch_cfg->flow_cntrl = FC_MEM2PER_DMA;		break;	case 2:		ch_cfg->flow_cntrl = FC_PER2MEM_DMA;		break;	case 3:		ch_cfg->flow_cntrl = FC_PER2PER_DMA;		break;	case 4:		ch_cfg->flow_cntrl = FC_PER2PER_DPER;		break;	case 5:		ch_cfg->flow_cntrl = FC_MEM2PER_PER;		break;	case 6:		ch_cfg->flow_cntrl = FC_PER2MEM_PER;		break;	case 7:		ch_cfg->flow_cntrl = FC_PER2PER_SPER;	}	cfg >>= 3;	ch_cfg->ie = cfg & 1;	cfg >>= 1;	ch_cfg->itc = cfg & 1;	cfg >>= 1;	ch_cfg->lock = cfg & 1;	cfg >>= 1;	ch_cfg->active = cfg & 1;	cfg >>= 1;	ch_cfg->halt = cfg & 1;out:	return err;}EXPORT_SYMBOL_GPL(pnx4008_dma_parse_config);void pnx4008_dma_split_head_entry(struct pnx4008_dma_config * config,				  struct pnx4008_dma_ch_ctrl * ctrl){	int new_len = ctrl->tr_size, num_entries = 0;	int old_len = new_len;	int src_width, dest_width, count = 1;	switch (ctrl->swidth) {	case WIDTH_BYTE:		src_width = 1;		break;	case WIDTH_HWORD:		src_width = 2;		break;	case WIDTH_WORD:		src_width = 4;		break;	default:		return;	}	switch (ctrl->dwidth) {	case WIDTH_BYTE:		dest_width = 1;		break;	case WIDTH_HWORD:		dest_width = 2;		break;	case WIDTH_WORD:		dest_width = 4;		break;	default:		return;	}	while (new_len > 0x7FF) {		num_entries++;		new_len = (ctrl->tr_size + num_entries) / (num_entries + 1);	}	if (num_entries != 0) {		struct pnx4008_dma_ll *ll = NULL;		config->ch_ctrl &= ~0x7ff;		config->ch_ctrl |= new_len;		if (!config->is_ll) {			config->is_ll = 1;			while (num_entries) {				if (!ll) {					config->ll =					    pnx4008_alloc_ll_entry(&config->								   ll_dma);					ll = config->ll;				} else {					ll->next =					    pnx4008_alloc_ll_entry(&ll->								   next_dma);					ll = ll->next;				}				if (ctrl->si)					ll->src_addr =					    config->src_addr +					    src_width * new_len * count;				else					ll->src_addr = config->src_addr;				if (ctrl->di)					ll->dest_addr =					    config->dest_addr +					    dest_width * new_len * count;				else					ll->dest_addr = config->dest_addr;				ll->ch_ctrl = config->ch_ctrl & 0x7fffffff;				ll->next_dma = 0;				ll->next = NULL;				num_entries--;				count++;			}		} else {			struct pnx4008_dma_ll *ll_old = config->ll;			unsigned long ll_dma_old = config->ll_dma;			while (num_entries) {				if (!ll) {					config->ll =					    pnx4008_alloc_ll_entry(&config->								   ll_dma);					ll = config->ll;				} else {					ll->next =					    pnx4008_alloc_ll_entry(&ll->								   next_dma);					ll = ll->next;				}				if (ctrl->si)					ll->src_addr =					    config->src_addr +					    src_width * new_len * count;				else					ll->src_addr = config->src_addr;				if (ctrl->di)					ll->dest_addr =					    config->dest_addr +					    dest_width * new_len * count;				else					ll->dest_addr = config->dest_addr;				ll->ch_ctrl = config->ch_ctrl & 0x7fffffff;				ll->next_dma = 0;				ll->next = NULL;				num_entries--;				count++;			}			ll->next_dma = ll_dma_old;			ll->next = ll_old;		}		/* adjust last length/tc */		ll->ch_ctrl = config->ch_ctrl & (~0x7ff);		ll->ch_ctrl |= old_len - new_len * (count - 1);		config->ch_ctrl &= 0x7fffffff;	}}EXPORT_SYMBOL_GPL(pnx4008_dma_split_head_entry);void pnx4008_dma_split_ll_entry(struct pnx4008_dma_ll * cur_ll,				struct pnx4008_dma_ch_ctrl * ctrl){	int new_len = ctrl->tr_size, num_entries = 0;	int old_len = new_len;	int src_width, dest_width, count = 1;	switch (ctrl->swidth) {	case WIDTH_BYTE:		src_width = 1;		break;	case WIDTH_HWORD:		src_width = 2;		break;	case WIDTH_WORD:		src_width = 4;		break;	default:		return;	}	switch (ctrl->dwidth) {	case WIDTH_BYTE:		dest_width = 1;		break;	case WIDTH_HWORD:		dest_width = 2;		break;	case WIDTH_WORD:		dest_width = 4;		break;	default:		return;	}	while (new_len > 0x7FF) {		num_entries++;		new_len = (ctrl->tr_size + num_entries) / (num_entries + 1);	}	if (num_entries != 0) {		struct pnx4008_dma_ll *ll = NULL;		cur_ll->ch_ctrl &= ~0x7ff;		cur_ll->ch_ctrl |= new_len;		if (!cur_ll->next) {			while (num_entries) {				if (!ll) {					cur_ll->next =					    pnx4008_alloc_ll_entry(&cur_ll->								   next_dma);					ll = cur_ll->next;				} else {					ll->next =					    pnx4008_alloc_ll_entry(&ll->								   next_dma);					ll = ll->next;				}				if (ctrl->si)					ll->src_addr =					    cur_ll->src_addr +					    src_width * new_len * count;				else					ll->src_addr = cur_ll->src_addr;				if (ctrl->di)					ll->dest_addr =					    cur_ll->dest_addr +					    dest_width * new_len * count;				else					ll->dest_addr = cur_ll->dest_addr;				ll->ch_ctrl = cur_ll->ch_ctrl & 0x7fffffff;				ll->next_dma = 0;				ll->next = NULL;				num_entries--;				count++;			}		} else {			struct pnx4008_dma_ll *ll_old = cur_ll->next;			unsigned long ll_dma_old = cur_ll->next_dma;			while (num_entries) {				if (!ll) {					cur_ll->next =					    pnx4008_alloc_ll_entry(&cur_ll->								   next_dma);					ll = cur_ll->next;				} else {					ll->next =					    pnx4008_alloc_ll_entry(&ll->								   next_dma);					ll = ll->next;				}				if (ctrl->si)					ll->src_addr =					    cur_ll->src_addr +					    src_width * new_len * count;				else					ll->src_addr = cur_ll->src_addr;				if (ctrl->di)					ll->dest_addr =					    cur_ll->dest_addr +					    dest_width * new_len * count;				else					ll->dest_addr = cur_ll->dest_addr;				ll->ch_ctrl = cur_ll->ch_ctrl & 0x7fffffff;				ll->next_dma = 0;				ll->next = NULL;				num_entries--;				count++;			}			ll->next_dma = ll_dma_old;			ll->next = ll_old;		}		/* adjust last length/tc */		ll->ch_ctrl = cur_ll->ch_ctrl & (~0x7ff);		ll->ch_ctrl |= old_len - new_len * (count - 1);		cur_ll->ch_ctrl &= 0x7fffffff;	}}EXPORT_SYMBOL_GPL(pnx4008_dma_split_ll_entry);int pnx4008_config_channel(int ch, struct pnx4008_dma_config * config){	if (!VALID_CHANNEL(ch) || !dma_channels[ch].name)		return -EINVAL;	pnx4008_dma_lock();	__raw_writel(config->src_addr, DMAC_Cx_SRC_ADDR(ch));	__raw_writel(config->dest_addr, DMAC_Cx_DEST_ADDR(ch));	if (config->is_ll)		__raw_writel(config->ll_dma, DMAC_Cx_LLI(ch));	else		__raw_writel(0, DMAC_Cx_LLI(ch));	__raw_writel(config->ch_ctrl, DMAC_Cx_CONTROL(ch));	__raw_writel(config->ch_cfg, DMAC_Cx_CONFIG(ch));	pnx4008_dma_unlock();	return 0;}EXPORT_SYMBOL_GPL(pnx4008_config_channel);int pnx4008_channel_get_config(int ch, struct pnx4008_dma_config * config){	if (!VALID_CHANNEL(ch) || !dma_channels[ch].name || !config)		return -EINVAL;	pnx4008_dma_lock();	config->ch_cfg = __raw_readl(DMAC_Cx_CONFIG(ch));	config->ch_ctrl = __raw_readl(DMAC_Cx_CONTROL(ch));	config->ll_dma = __raw_readl(DMAC_Cx_LLI(ch));	config->is_ll = config->ll_dma ? 1 : 0;	config->src_addr = __raw_readl(DMAC_Cx_SRC_ADDR(ch));	config->dest_addr = __raw_readl(DMAC_Cx_DEST_ADDR(ch));	pnx4008_dma_unlock();	return 0;}EXPORT_SYMBOL_GPL(pnx4008_channel_get_config);int pnx4008_dma_ch_enable(int ch){	unsigned long ch_cfg;	if (!VALID_CHANNEL(ch) || !dma_channels[ch].name)		return -EINVAL;	pnx4008_dma_lock();	ch_cfg = __raw_readl(DMAC_Cx_CONFIG(ch));	ch_cfg |= 1;	__raw_writel(ch_cfg, DMAC_Cx_CONFIG(ch));	pnx4008_dma_unlock();	return 0;}EXPORT_SYMBOL_GPL(pnx4008_dma_ch_enable);int pnx4008_dma_ch_disable(int ch){	unsigned long ch_cfg;	if (!VALID_CHANNEL(ch) || !dma_channels[ch].name)		return -EINVAL;	pnx4008_dma_lock();	ch_cfg = __raw_readl(DMAC_Cx_CONFIG(ch));	ch_cfg &= ~1;	__raw_writel(ch_cfg, DMAC_Cx_CONFIG(ch));	pnx4008_dma_unlock();	return 0;}EXPORT_SYMBOL_GPL(pnx4008_dma_ch_disable);int pnx4008_dma_ch_enabled(int ch){	unsigned long ch_cfg;	if (!VALID_CHANNEL(ch) || !dma_channels[ch].name)		return -EINVAL;	pnx4008_dma_lock();	ch_cfg = __raw_readl(DMAC_Cx_CONFIG(ch));	pnx4008_dma_unlock();	return ch_cfg & 1;}EXPORT_SYMBOL_GPL(pnx4008_dma_ch_enabled);static irqreturn_t dma_irq_handler(int irq, void *dev_id){	int i;	unsigned long dint = __raw_readl(DMAC_INT_STAT);	unsigned long tcint = __raw_readl(DMAC_INT_TC_STAT);	unsigned long eint = __raw_readl(DMAC_INT_ERR_STAT);	unsigned long i_bit;	for (i = MAX_DMA_CHANNELS - 1; i >= 0; i--) {		i_bit = 1 << i;		if (dint & i_bit) {			struct dma_channel *channel = &dma_channels[i];			if (channel->name && channel->irq_handler) {				int cause = 0;				if (eint & i_bit)					cause |= DMA_ERR_INT;				if (tcint & i_bit)					cause |= DMA_TC_INT;				channel->irq_handler(i, cause, channel->data);			} else {				/*				 * IRQ for an unregistered DMA channel				 */				printk(KERN_WARNING				       "spurious IRQ for DMA channel %d\n", i);			}			if (tcint & i_bit)				__raw_writel(i_bit, DMAC_INT_TC_CLEAR);			if (eint & i_bit)				__raw_writel(i_bit, DMAC_INT_ERR_CLEAR);		}	}	return IRQ_HANDLED;}static int __init pnx4008_dma_init(void){	int ret, i;	ret = request_irq(DMA_INT, dma_irq_handler, 0, "DMA", NULL);	if (ret) {		printk(KERN_CRIT "Wow!  Can't register IRQ for DMA\n");		goto out;	}	ll_pool.count = 0x4000 / sizeof(struct pnx4008_dma_ll);	ll_pool.cur = ll_pool.vaddr =	    dma_alloc_coherent(NULL, ll_pool.count * sizeof(struct pnx4008_dma_ll),			       &ll_pool.dma_addr, GFP_KERNEL);	if (!ll_pool.vaddr) {		ret = -ENOMEM;		free_irq(DMA_INT, NULL);		goto out;	}	for (i = 0; i < ll_pool.count - 1; i++) {		void **addr = ll_pool.vaddr + i * sizeof(struct pnx4008_dma_ll);		*addr = (void *)addr + sizeof(struct pnx4008_dma_ll);	}	*(long *)(ll_pool.vaddr +		  (ll_pool.count - 1) * sizeof(struct pnx4008_dma_ll)) =	    (long)ll_pool.vaddr;	__raw_writel(1, DMAC_CONFIG);out:	return ret;}arch_initcall(pnx4008_dma_init);

⌨️ 快捷键说明

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