📄 dma.c
字号:
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 + -