irqreturn_t sdw_cdns_irq()

in cadence_master.c [865:947]


irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
{
	struct sdw_cdns *cdns = dev_id;
	u32 int_status;

	/* Check if the link is up */
	if (!cdns->link_up)
		return IRQ_NONE;

	int_status = cdns_readl(cdns, CDNS_MCP_INTSTAT);

	/* check for reserved values read as zero */
	if (int_status & CDNS_MCP_INT_RESERVED)
		return IRQ_NONE;

	if (!(int_status & CDNS_MCP_INT_IRQ))
		return IRQ_NONE;

	if (int_status & CDNS_MCP_INT_RX_WL) {
		cdns_read_response(cdns);

		if (cdns->defer) {
			cdns_fill_msg_resp(cdns, cdns->defer->msg,
					   cdns->defer->length, 0);
			complete(&cdns->defer->complete);
			cdns->defer = NULL;
		} else {
			complete(&cdns->tx_complete);
		}
	}

	if (int_status & CDNS_MCP_INT_PARITY) {
		/* Parity error detected by Master */
		dev_err_ratelimited(cdns->dev, "Parity error\n");
	}

	if (int_status & CDNS_MCP_INT_CTRL_CLASH) {
		/* Slave is driving bit slot during control word */
		dev_err_ratelimited(cdns->dev, "Bus clash for control word\n");
	}

	if (int_status & CDNS_MCP_INT_DATA_CLASH) {
		/*
		 * Multiple slaves trying to drive bit slot, or issue with
		 * ownership of data bits or Slave gone bonkers
		 */
		dev_err_ratelimited(cdns->dev, "Bus clash for data word\n");
	}

	if (cdns->bus.params.m_data_mode != SDW_PORT_DATA_MODE_NORMAL &&
	    int_status & CDNS_MCP_INT_DPINT) {
		u32 port_intstat;

		/* just log which ports report an error */
		port_intstat = cdns_readl(cdns, CDNS_MCP_PORT_INTSTAT);
		dev_err_ratelimited(cdns->dev, "DP interrupt: PortIntStat %8x\n",
				    port_intstat);

		/* clear status w/ write1 */
		cdns_writel(cdns, CDNS_MCP_PORT_INTSTAT, port_intstat);
	}

	if (int_status & CDNS_MCP_INT_SLAVE_MASK) {
		/* Mask the Slave interrupt and wake thread */
		cdns_updatel(cdns, CDNS_MCP_INTMASK,
			     CDNS_MCP_INT_SLAVE_MASK, 0);

		int_status &= ~CDNS_MCP_INT_SLAVE_MASK;

		/*
		 * Deal with possible race condition between interrupt
		 * handling and disabling interrupts on suspend.
		 *
		 * If the master is in the process of disabling
		 * interrupts, don't schedule a workqueue
		 */
		if (cdns->interrupt_enabled)
			schedule_work(&cdns->work);
	}

	cdns_writel(cdns, CDNS_MCP_INTSTAT, int_status);
	return IRQ_HANDLED;
}