static irqreturn_t sata_dwc_isr(int irq, void *dev_instance)
{
    struct ata_host *host = (struct ata_host *)dev_instance;
    struct sata_dwc_device *hsdev = HSDEV_FROM_HOST(host);
    struct ata_port *ap;
    struct ata_queued_cmd *qc;
    unsigned long flags;
    u8 status, tag;
    int handled, num_processed, port = 0;
    uint intpr, sactive, sactive2, tag_mask;
    struct sata_dwc_device_port *hsdevp;
    host_pvt.sata_dwc_sactive_issued = 0;

    spin_lock_irqsave(&host->lock, flags);


    intpr = in_le32(&hsdev->sata_dwc_regs->intpr);

    ap = host->ports[port];
    hsdevp = HSDEVP_FROM_AP(ap);

    dev_dbg(ap->dev, "%s intpr=0x%08x active_tag=%d\n", __func__, intpr,
            ap->link.active_tag);


    if (intpr & SATA_DWC_INTPR_ERR) {
        sata_dwc_error_intr(ap, hsdev, intpr);
        handled = 1;
        goto DONE;
    }


    if (intpr & SATA_DWC_INTPR_NEWFP) {
        clear_interrupt_bit(hsdev, SATA_DWC_INTPR_NEWFP);

        tag = (u8)(in_le32(&hsdev->sata_dwc_regs->fptagr));
        dev_dbg(ap->dev, "%s: NEWFP tag=%d\n", __func__, tag);
        if (hsdevp->cmd_issued[tag] != SATA_DWC_CMD_ISSUED_PEND)
            dev_warn(ap->dev, "CMD tag=%d not pending?\n", tag);

        host_pvt.sata_dwc_sactive_issued |= qcmd_tag_to_mask(tag);

        qc = ata_qc_from_tag(ap, tag);
        qc->ap->link.active_tag = tag;
        sata_dwc_bmdma_start_by_tag(qc, tag);

        handled = 1;
        goto DONE;
    }
    sactive = core_scr_read(SCR_ACTIVE);
    tag_mask = (host_pvt.sata_dwc_sactive_issued | sactive) ^ sactive;


    if (host_pvt.sata_dwc_sactive_issued == 0 && tag_mask == 0) {
        if (ap->link.active_tag == ATA_TAG_POISON)
            tag = 0;
        else
            tag = ap->link.active_tag;
        qc = ata_qc_from_tag(ap, tag);


        if (unlikely(!qc || (qc->tf.flags & ATA_TFLAG_POLLING))) {
            dev_err(ap->dev, "%s interrupt with no active qc "
                    "qc=%p\n", __func__, qc);
            ap->ops->sff_check_status(ap);
            handled = 1;
            goto DONE;
        }
        status = ap->ops->sff_check_status(ap);

        qc->ap->link.active_tag = tag;
        hsdevp->cmd_issued[tag] = SATA_DWC_CMD_ISSUED_NOT;

        if (status & ATA_ERR) {
            dev_dbg(ap->dev, "interrupt ATA_ERR (0x%x)\n", status);
            sata_dwc_qc_complete(ap, qc, 1);
            handled = 1;
            goto DONE;
        }

        dev_dbg(ap->dev, "%s non-NCQ cmd interrupt, protocol: %s\n",
                __func__, get_prot_descript(qc->tf.protocol));
DRVSTILLBUSY:
        if (ata_is_dma(qc->tf.protocol)) {
            host_pvt.dma_interrupt_count++;
            if (hsdevp->dma_pending[tag] == \
                    SATA_DWC_DMA_PENDING_NONE) {
                dev_err(ap->dev, "%s: DMA not pending "
                        "intpr=0x%08x status=0x%08x pending"
                        "=%d\n", __func__, intpr, status,
                        hsdevp->dma_pending[tag]);
            }

            if ((host_pvt.dma_interrupt_count % 2) == 0)
                sata_dwc_dma_xfer_complete(ap, 1);
        } else if (ata_is_pio(qc->tf.protocol)) {
            ata_sff_hsm_move(ap, qc, status, 0);
            handled = 1;
            goto DONE;
        } else {
            if (unlikely(sata_dwc_qc_complete(ap, qc, 1)))
                goto DRVSTILLBUSY;
        }

        handled = 1;
        goto DONE;
    }



    sactive = core_scr_read(SCR_ACTIVE);
    tag_mask = (host_pvt.sata_dwc_sactive_issued | sactive) ^ sactive;

    if (sactive != 0 || (host_pvt.sata_dwc_sactive_issued) > 1 || \
            tag_mask > 1) {
        dev_dbg(ap->dev, "%s NCQ:sactive=0x%08x  sactive_issued=0x%08x"
                "tag_mask=0x%08x\n", __func__, sactive,
                host_pvt.sata_dwc_sactive_issued, tag_mask);
    }

    if ((tag_mask | (host_pvt.sata_dwc_sactive_issued)) != \
            (host_pvt.sata_dwc_sactive_issued)) {
        dev_warn(ap->dev, "Bad tag mask?  sactive=0x%08x "
                 "(host_pvt.sata_dwc_sactive_issued)=0x%08x  tag_mask"
                 "=0x%08x\n", sactive, host_pvt.sata_dwc_sactive_issued,
                 tag_mask);
    }


    status = ap->ops->sff_check_status(ap);
    dev_dbg(ap->dev, "%s ATA status register=0x%x\n", __func__, status);

    tag = 0;
    num_processed = 0;
    while (tag_mask) {
        num_processed++;
        while (!(tag_mask & 0x00000001)) {
            tag++;
            tag_mask <<= 1;
        }

        tag_mask &= (~0x00000001);
        qc = ata_qc_from_tag(ap, tag);


        qc->ap->link.active_tag = tag;
        hsdevp->cmd_issued[tag] = SATA_DWC_CMD_ISSUED_NOT;


        if (status & ATA_ERR) {
            dev_dbg(ap->dev, "%s ATA_ERR (0x%x)\n", __func__,
                    status);
            sata_dwc_qc_complete(ap, qc, 1);
            handled = 1;
            goto DONE;
        }


        dev_dbg(ap->dev, "%s NCQ command, protocol: %s\n", __func__,
                get_prot_descript(qc->tf.protocol));
        if (ata_is_dma(qc->tf.protocol)) {
            host_pvt.dma_interrupt_count++;
            if (hsdevp->dma_pending[tag] == \
                    SATA_DWC_DMA_PENDING_NONE)
                dev_warn(ap->dev, "%s: DMA not pending?\n",
                         __func__);
            if ((host_pvt.dma_interrupt_count % 2) == 0)
                sata_dwc_dma_xfer_complete(ap, 1);
        } else {
            if (unlikely(sata_dwc_qc_complete(ap, qc, 1)))
                goto STILLBUSY;
        }
        continue;

STILLBUSY:
        ap->stats.idle_irq++;
        dev_warn(ap->dev, "STILL BUSY IRQ ata%d: irq trap\n",
                 ap->print_id);
    }

    sactive2 = core_scr_read(SCR_ACTIVE);
    if (sactive2 != sactive) {
        dev_dbg(ap->dev, "More completed - sactive=0x%x sactive2"
                "=0x%x\n", sactive, sactive2);
    }
    handled = 1;

DONE:
    spin_unlock_irqrestore(&host->lock, flags);
    return IRQ_RETVAL(handled);
}
Пример #2
0
/*
 * Function : sata_dwc_isr
 * arguments : irq, void *dev_instance, struct pt_regs *regs
 * Return value : irqreturn_t - status of IRQ
 * This Interrupt handler called via port ops registered function.
 * .irq_handler = sata_dwc_isr
 */
static irqreturn_t sata_dwc_isr(int irq, void *dev_instance)
{
	struct ata_host *host = (struct ata_host *)dev_instance;
	struct sata_dwc_device *hsdev = HSDEV_FROM_HOST(host);
	struct ata_port *ap;
	struct ata_queued_cmd *qc;
	unsigned long flags;
	u8 status, tag;
	int handled, num_processed, port = 0;
	uint intpr, sactive, sactive2, tag_mask;
	struct sata_dwc_device_port *hsdevp;
	host_pvt.sata_dwc_sactive_issued = 0;

	spin_lock_irqsave(&host->lock, flags);

	/* Read the interrupt register */
	intpr = in_le32(&hsdev->sata_dwc_regs->intpr);

	ap = host->ports[port];
	hsdevp = HSDEVP_FROM_AP(ap);

	dev_dbg(ap->dev, "%s intpr=0x%08x active_tag=%d\n", __func__, intpr,
		ap->link.active_tag);

	/* Check for error interrupt */
	if (intpr & SATA_DWC_INTPR_ERR) {
		sata_dwc_error_intr(ap, hsdev, intpr);
		handled = 1;
		goto DONE;
	}

	/* Check for DMA SETUP FIS (FP DMA) interrupt */
	if (intpr & SATA_DWC_INTPR_NEWFP) {
		clear_interrupt_bit(hsdev, SATA_DWC_INTPR_NEWFP);

		tag = (u8)(in_le32(&hsdev->sata_dwc_regs->fptagr));
		dev_dbg(ap->dev, "%s: NEWFP tag=%d\n", __func__, tag);
		if (hsdevp->cmd_issued[tag] != SATA_DWC_CMD_ISSUED_PEND)
			dev_warn(ap->dev, "CMD tag=%d not pending?\n", tag);

		host_pvt.sata_dwc_sactive_issued |= qcmd_tag_to_mask(tag);

		qc = ata_qc_from_tag(ap, tag);
		/*
		 * Start FP DMA for NCQ command.  At this point the tag is the
		 * active tag.  It is the tag that matches the command about to
		 * be completed.
		 */
		qc->ap->link.active_tag = tag;
		sata_dwc_bmdma_start_by_tag(qc, tag);

		handled = 1;
		goto DONE;
	}
	sactive = core_scr_read(SCR_ACTIVE);
	tag_mask = (host_pvt.sata_dwc_sactive_issued | sactive) ^ sactive;

	/* If no sactive issued and tag_mask is zero then this is not NCQ */
	if (host_pvt.sata_dwc_sactive_issued == 0 && tag_mask == 0) {
		if (ap->link.active_tag == ATA_TAG_POISON)
			tag = 0;
		else
			tag = ap->link.active_tag;
		qc = ata_qc_from_tag(ap, tag);

		/* DEV interrupt w/ no active qc? */
		if (unlikely(!qc || (qc->tf.flags & ATA_TFLAG_POLLING))) {
			dev_err(ap->dev, "%s interrupt with no active qc "
				"qc=%p\n", __func__, qc);
			ap->ops->sff_check_status(ap);
			handled = 1;
			goto DONE;
		}
		status = ap->ops->sff_check_status(ap);

		qc->ap->link.active_tag = tag;
		hsdevp->cmd_issued[tag] = SATA_DWC_CMD_ISSUED_NOT;

		if (status & ATA_ERR) {
			dev_dbg(ap->dev, "interrupt ATA_ERR (0x%x)\n", status);
			sata_dwc_qc_complete(ap, qc, 1);
			handled = 1;
			goto DONE;
		}

		dev_dbg(ap->dev, "%s non-NCQ cmd interrupt, protocol: %s\n",
			__func__, get_prot_descript(qc->tf.protocol));
DRVSTILLBUSY:
		if (ata_is_dma(qc->tf.protocol)) {
			/*
			 * Each DMA transaction produces 2 interrupts. The DMAC
			 * transfer complete interrupt and the SATA controller
			 * operation done interrupt. The command should be
			 * completed only after both interrupts are seen.
			 */
			host_pvt.dma_interrupt_count++;
			if (hsdevp->dma_pending[tag] == \
					SATA_DWC_DMA_PENDING_NONE) {
				dev_err(ap->dev, "%s: DMA not pending "
					"intpr=0x%08x status=0x%08x pending"
					"=%d\n", __func__, intpr, status,
					hsdevp->dma_pending[tag]);
			}

			if ((host_pvt.dma_interrupt_count % 2) == 0)
				sata_dwc_dma_xfer_complete(ap, 1);
		} else if (ata_is_pio(qc->tf.protocol)) {
			ata_sff_hsm_move(ap, qc, status, 0);
			handled = 1;
			goto DONE;
		} else {
			if (unlikely(sata_dwc_qc_complete(ap, qc, 1)))
				goto DRVSTILLBUSY;
		}

		handled = 1;
		goto DONE;
	}

	/*
	 * This is a NCQ command. At this point we need to figure out for which
	 * tags we have gotten a completion interrupt.  One interrupt may serve
	 * as completion for more than one operation when commands are queued
	 * (NCQ).  We need to process each completed command.
	 */

	 /* process completed commands */
	sactive = core_scr_read(SCR_ACTIVE);
	tag_mask = (host_pvt.sata_dwc_sactive_issued | sactive) ^ sactive;

	if (sactive != 0 || (host_pvt.sata_dwc_sactive_issued) > 1 || \
							tag_mask > 1) {
		dev_dbg(ap->dev, "%s NCQ:sactive=0x%08x  sactive_issued=0x%08x"
			"tag_mask=0x%08x\n", __func__, sactive,
			host_pvt.sata_dwc_sactive_issued, tag_mask);
	}

	if ((tag_mask | (host_pvt.sata_dwc_sactive_issued)) != \
					(host_pvt.sata_dwc_sactive_issued)) {
		dev_warn(ap->dev, "Bad tag mask?  sactive=0x%08x "
			 "(host_pvt.sata_dwc_sactive_issued)=0x%08x  tag_mask"
			 "=0x%08x\n", sactive, host_pvt.sata_dwc_sactive_issued,
			  tag_mask);
	}

	/* read just to clear ... not bad if currently still busy */
	status = ap->ops->sff_check_status(ap);
	dev_dbg(ap->dev, "%s ATA status register=0x%x\n", __func__, status);

	tag = 0;
	num_processed = 0;
	while (tag_mask) {
		num_processed++;
		while (!(tag_mask & 0x00000001)) {
			tag++;
			tag_mask <<= 1;
		}

		tag_mask &= (~0x00000001);
		qc = ata_qc_from_tag(ap, tag);

		/* To be picked up by completion functions */
		qc->ap->link.active_tag = tag;
		hsdevp->cmd_issued[tag] = SATA_DWC_CMD_ISSUED_NOT;

		/* Let libata/scsi layers handle error */
		if (status & ATA_ERR) {
			dev_dbg(ap->dev, "%s ATA_ERR (0x%x)\n", __func__,
				status);
			sata_dwc_qc_complete(ap, qc, 1);
			handled = 1;
			goto DONE;
		}

		/* Process completed command */
		dev_dbg(ap->dev, "%s NCQ command, protocol: %s\n", __func__,
			get_prot_descript(qc->tf.protocol));
		if (ata_is_dma(qc->tf.protocol)) {
			host_pvt.dma_interrupt_count++;
			if (hsdevp->dma_pending[tag] == \
					SATA_DWC_DMA_PENDING_NONE)
				dev_warn(ap->dev, "%s: DMA not pending?\n",
					__func__);
			if ((host_pvt.dma_interrupt_count % 2) == 0)
				sata_dwc_dma_xfer_complete(ap, 1);
		} else {
			if (unlikely(sata_dwc_qc_complete(ap, qc, 1)))
				goto STILLBUSY;
		}
		continue;

STILLBUSY:
		ap->stats.idle_irq++;
		dev_warn(ap->dev, "STILL BUSY IRQ ata%d: irq trap\n",
			ap->print_id);
	} /* while tag_mask */

	/*
	 * Check to see if any commands completed while we were processing our
	 * initial set of completed commands (read status clears interrupts,
	 * so we might miss a completed command interrupt if one came in while
	 * we were processing --we read status as part of processing a completed
	 * command).
	 */
	sactive2 = core_scr_read(SCR_ACTIVE);
	if (sactive2 != sactive) {
		dev_dbg(ap->dev, "More completed - sactive=0x%x sactive2"
			"=0x%x\n", sactive, sactive2);
	}
	handled = 1;

DONE:
	spin_unlock_irqrestore(&host->lock, flags);
	return IRQ_RETVAL(handled);
}