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; }
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; }