Example #1
0
static irqreturn_t tegra_cec_irq_handler(int irq, void *data)
{
	struct device *dev = data;
	struct tegra_cec *cec = dev_get_drvdata(dev);
	u32 status, mask;

	status = readl(cec->cec_base + TEGRA_CEC_INT_STAT);
	mask = readl(cec->cec_base + TEGRA_CEC_INT_MASK);

	status &= mask;

	if (!status)
		goto out;

	if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) {
		dev_err(dev, "tegra_cec: TX underrun, interrupt timing issue!\n");

		tegra_cec_error_recovery(cec);
		writel(mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY,
			cec->cec_base + TEGRA_CEC_INT_MASK);

		cec->tx_error = -EIO;
		cec->tx_wake = 1;

		wake_up_interruptible(&cec->tx_waitq);

		goto out;
	} else if ((status & TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED) ||
		   (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)) {
		tegra_cec_error_recovery(cec);
		writel(mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY,
			cec->cec_base + TEGRA_CEC_INT_MASK);

		cec->tx_error = -ECOMM;
		cec->tx_wake = 1;

		wake_up_interruptible(&cec->tx_waitq);

		goto out;
	} else if (status & TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED) {
		writel((TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED),
			cec->cec_base + TEGRA_CEC_INT_STAT);

		if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) {
			tegra_cec_error_recovery(cec);

			cec->tx_error = TEGRA_CEC_LADDR_MODE(cec->tx_buf[0]) ?
					-ECONNRESET : -EHOSTUNREACH;
		}
		cec->tx_wake = 1;

		wake_up_interruptible(&cec->tx_waitq);

		goto out;
	} else if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD)
		dev_warn(dev, "tegra_cec: TX NAKed on the fly!\n");

	if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY) {
		if (cec->tx_buf_cur == cec->tx_buf_cnt)
			writel(mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY,
				cec->cec_base + TEGRA_CEC_INT_MASK);
		else
			tegra_cec_native_tx(cec,
				cec->tx_buf[cec->tx_buf_cur++]);
	}

	if (status & (TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN |
		TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED |
		TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED |
		TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED)) {
		writel((TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN |
			TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED |
			TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED |
			TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED),
			cec->cec_base + TEGRA_CEC_INT_STAT);
	} else if (status & TEGRA_CEC_INT_STAT_RX_REGISTER_FULL) {
		writel(TEGRA_CEC_INT_STAT_RX_REGISTER_FULL,
			cec->cec_base + TEGRA_CEC_INT_STAT);
		cec->rx_buffer = readw(cec->cec_base + TEGRA_CEC_RX_REGISTER);
		cec->rx_wake = 1;
		wake_up_interruptible(&cec->rx_waitq);
	}

out:
	return IRQ_HANDLED;
}
Example #2
0
static irqreturn_t tegra_cec_irq_handler(int irq, void *data)
{
	struct device *dev = data;
	struct tegra_cec *cec = dev_get_drvdata(dev);
	u32 status, mask;

	status = cec_read(cec, TEGRA_CEC_INT_STAT);
	mask = cec_read(cec, TEGRA_CEC_INT_MASK);

	status &= mask;

	if (!status)
		return IRQ_HANDLED;

	if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) {
		dev_err(dev, "TX underrun, interrupt timing issue!\n");

		tegra_cec_error_recovery(cec);
		cec_write(cec, TEGRA_CEC_INT_MASK,
			  mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);

		cec->tx_done = true;
		cec->tx_status = CEC_TX_STATUS_ERROR;
		return IRQ_WAKE_THREAD;
	}

	if ((status & TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED) ||
		   (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)) {
		tegra_cec_error_recovery(cec);
		cec_write(cec, TEGRA_CEC_INT_MASK,
			  mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);

		cec->tx_done = true;
		if (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)
			cec->tx_status = CEC_TX_STATUS_LOW_DRIVE;
		else
			cec->tx_status = CEC_TX_STATUS_ARB_LOST;
		return IRQ_WAKE_THREAD;
	}

	if (status & TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED) {
		cec_write(cec, TEGRA_CEC_INT_STAT,
			  TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED);

		if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) {
			tegra_cec_error_recovery(cec);

			cec->tx_done = true;
			cec->tx_status = CEC_TX_STATUS_NACK;
		} else {
			cec->tx_done = true;
			cec->tx_status = CEC_TX_STATUS_OK;
		}
		return IRQ_WAKE_THREAD;
	}

	if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD)
		dev_warn(dev, "TX NAKed on the fly!\n");

	if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY) {
		if (cec->tx_buf_cur == cec->tx_buf_cnt) {
			cec_write(cec, TEGRA_CEC_INT_MASK,
				  mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);
		} else {
			cec_write(cec, TEGRA_CEC_TX_REGISTER,
				  cec->tx_buf[cec->tx_buf_cur++]);
			cec_write(cec, TEGRA_CEC_INT_STAT,
				  TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY);
		}
	}

	if (status & (TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN |
		      TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED |
		      TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED |
		      TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED)) {
		cec_write(cec, TEGRA_CEC_INT_STAT,
			  (TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN |
			   TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED |
			   TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED |
			   TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED));
	} else if (status & TEGRA_CEC_INT_STAT_RX_REGISTER_FULL) {
		u32 v;

		cec_write(cec, TEGRA_CEC_INT_STAT,
			  TEGRA_CEC_INT_STAT_RX_REGISTER_FULL);
		v = cec_read(cec, TEGRA_CEC_RX_REGISTER);
		if (cec->rx_buf_cnt < CEC_MAX_MSG_SIZE)
			cec->rx_buf[cec->rx_buf_cnt++] = v & 0xff;
		if (v & TEGRA_CEC_RX_REGISTER_EOM) {
			cec->rx_done = true;
			return IRQ_WAKE_THREAD;
		}
	}

	return IRQ_HANDLED;
}