/* * hsu_dma_do_irq() - DMA interrupt handler * @chip: HSUART DMA chip * @nr: DMA channel number * @status: Channel Status Register value * * Description: * This function handles Channel Error and Descriptor Done interrupts. * This function should be called after determining that the DMA interrupt * is not a normal timeout interrupt, ie. hsu_dma_get_status() returned 0. * * Return: * 0 for invalid channel number, 1 otherwise. */ int hsu_dma_do_irq(struct hsu_dma_chip *chip, unsigned short nr, u32 status) { struct hsu_dma_chan *hsuc; struct hsu_dma_desc *desc; unsigned long flags; /* Sanity check */ if (nr >= chip->hsu->nr_channels) return 0; hsuc = &chip->hsu->chan[nr]; spin_lock_irqsave(&hsuc->vchan.lock, flags); desc = hsuc->desc; if (desc) { if (status & HSU_CH_SR_CHE) { desc->status = DMA_ERROR; } else if (desc->active < desc->nents) { hsu_dma_start_channel(hsuc); } else { vchan_cookie_complete(&desc->vdesc); desc->status = DMA_COMPLETE; hsu_dma_start_transfer(hsuc); } } spin_unlock_irqrestore(&hsuc->vchan.lock, flags); return 1; }
irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short nr) { struct hsu_dma_chan *hsuc; struct hsu_dma_desc *desc; unsigned long flags; u32 sr; /* Sanity check */ if (nr >= chip->hsu->nr_channels) return IRQ_NONE; hsuc = &chip->hsu->chan[nr]; /* * No matter what situation, need read clear the IRQ status * There is a bug, see Errata 5, HSD 2900918 */ sr = hsu_dma_chan_get_sr(hsuc); if (!sr) return IRQ_NONE; /* Timeout IRQ, need wait some time, see Errata 2 */ if (hsuc->direction == DMA_DEV_TO_MEM && (sr & HSU_CH_SR_DESCTO_ANY)) udelay(2); sr &= ~HSU_CH_SR_DESCTO_ANY; if (!sr) return IRQ_HANDLED; spin_lock_irqsave(&hsuc->vchan.lock, flags); desc = hsuc->desc; if (desc) { if (sr & HSU_CH_SR_CHE) { desc->status = DMA_ERROR; } else if (desc->active < desc->nents) { hsu_dma_start_channel(hsuc); } else { vchan_cookie_complete(&desc->vdesc); desc->status = DMA_COMPLETE; hsu_dma_start_transfer(hsuc); } } spin_unlock_irqrestore(&hsuc->vchan.lock, flags); return IRQ_HANDLED; }
static void hsu_dma_start_transfer(struct hsu_dma_chan *hsuc) { struct virt_dma_desc *vdesc; /* Get the next descriptor */ vdesc = vchan_next_desc(&hsuc->vchan); if (!vdesc) { hsuc->desc = NULL; return; } list_del(&vdesc->node); hsuc->desc = to_hsu_dma_desc(vdesc); /* Start the channel with a new descriptor */ hsu_dma_start_channel(hsuc); }