|
@@ -126,28 +126,33 @@ static void hsu_dma_start_transfer(struct hsu_dma_chan *hsuc)
|
|
|
hsu_dma_start_channel(hsuc);
|
|
|
}
|
|
|
|
|
|
-static u32 hsu_dma_chan_get_sr(struct hsu_dma_chan *hsuc)
|
|
|
-{
|
|
|
- unsigned long flags;
|
|
|
- u32 sr;
|
|
|
-
|
|
|
- spin_lock_irqsave(&hsuc->vchan.lock, flags);
|
|
|
- sr = hsu_chan_readl(hsuc, HSU_CH_SR);
|
|
|
- spin_unlock_irqrestore(&hsuc->vchan.lock, flags);
|
|
|
-
|
|
|
- return sr & ~(HSU_CH_SR_DESCE_ANY | HSU_CH_SR_CDESC_ANY);
|
|
|
-}
|
|
|
-
|
|
|
-irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short nr)
|
|
|
+/*
|
|
|
+ * hsu_dma_get_status() - get DMA channel status
|
|
|
+ * @chip: HSUART DMA chip
|
|
|
+ * @nr: DMA channel number
|
|
|
+ * @status: pointer for DMA Channel Status Register value
|
|
|
+ *
|
|
|
+ * Description:
|
|
|
+ * The function reads and clears the DMA Channel Status Register, checks
|
|
|
+ * if it was a timeout interrupt and returns a corresponding value.
|
|
|
+ *
|
|
|
+ * Caller should provide a valid pointer for the DMA Channel Status
|
|
|
+ * Register value that will be returned in @status.
|
|
|
+ *
|
|
|
+ * Return:
|
|
|
+ * 1 for DMA timeout status, 0 for other DMA status, or error code for
|
|
|
+ * invalid parameters or no interrupt pending.
|
|
|
+ */
|
|
|
+int hsu_dma_get_status(struct hsu_dma_chip *chip, unsigned short nr,
|
|
|
+ u32 *status)
|
|
|
{
|
|
|
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;
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
hsuc = &chip->hsu->chan[nr];
|
|
|
|
|
@@ -155,22 +160,65 @@ irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short 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);
|
|
|
+ spin_lock_irqsave(&hsuc->vchan.lock, flags);
|
|
|
+ sr = hsu_chan_readl(hsuc, HSU_CH_SR);
|
|
|
+ spin_unlock_irqrestore(&hsuc->vchan.lock, flags);
|
|
|
+
|
|
|
+ /* Check if any interrupt is pending */
|
|
|
+ sr &= ~(HSU_CH_SR_DESCE_ANY | HSU_CH_SR_CDESC_ANY);
|
|
|
if (!sr)
|
|
|
- return IRQ_NONE;
|
|
|
+ return -EIO;
|
|
|
|
|
|
/* Timeout IRQ, need wait some time, see Errata 2 */
|
|
|
if (sr & HSU_CH_SR_DESCTO_ANY)
|
|
|
udelay(2);
|
|
|
|
|
|
+ /*
|
|
|
+ * At this point, at least one of Descriptor Time Out, Channel Error
|
|
|
+ * or Descriptor Done bits must be set. Clear the Descriptor Time Out
|
|
|
+ * bits and if sr is still non-zero, it must be channel error or
|
|
|
+ * descriptor done which are higher priority than timeout and handled
|
|
|
+ * in hsu_dma_do_irq(). Else, it must be a timeout.
|
|
|
+ */
|
|
|
sr &= ~HSU_CH_SR_DESCTO_ANY;
|
|
|
- if (!sr)
|
|
|
- return IRQ_HANDLED;
|
|
|
+
|
|
|
+ *status = sr;
|
|
|
+
|
|
|
+ return sr ? 0 : 1;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(hsu_dma_get_status);
|
|
|
+
|
|
|
+/*
|
|
|
+ * 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:
|
|
|
+ * IRQ_NONE for invalid channel number, IRQ_HANDLED otherwise.
|
|
|
+ */
|
|
|
+irqreturn_t 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 IRQ_NONE;
|
|
|
+
|
|
|
+ hsuc = &chip->hsu->chan[nr];
|
|
|
|
|
|
spin_lock_irqsave(&hsuc->vchan.lock, flags);
|
|
|
desc = hsuc->desc;
|
|
|
if (desc) {
|
|
|
- if (sr & HSU_CH_SR_CHE) {
|
|
|
+ if (status & HSU_CH_SR_CHE) {
|
|
|
desc->status = DMA_ERROR;
|
|
|
} else if (desc->active < desc->nents) {
|
|
|
hsu_dma_start_channel(hsuc);
|
|
@@ -184,7 +232,7 @@ irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short nr)
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(hsu_dma_irq);
|
|
|
+EXPORT_SYMBOL_GPL(hsu_dma_do_irq);
|
|
|
|
|
|
static struct hsu_dma_desc *hsu_dma_alloc_desc(unsigned int nents)
|
|
|
{
|