static int sdw_handle_slave_alerts()

in bus.c [1495:1695]


static int sdw_handle_slave_alerts(struct sdw_slave *slave)
{
	struct sdw_slave_intr_status slave_intr;
	u8 clear = 0, bit, port_status[15] = {0};
	int port_num, stat, ret, count = 0;
	unsigned long port;
	bool slave_notify;
	u8 sdca_cascade = 0;
	u8 buf, buf2[2], _buf, _buf2[2];
	bool parity_check;
	bool parity_quirk;

	sdw_modify_slave_status(slave, SDW_SLAVE_ALERT);

	ret = pm_runtime_get_sync(&slave->dev);
	if (ret < 0 && ret != -EACCES) {
		dev_err(&slave->dev, "Failed to resume device: %d\n", ret);
		pm_runtime_put_noidle(&slave->dev);
		return ret;
	}

	/* Read Intstat 1, Intstat 2 and Intstat 3 registers */
	ret = sdw_read_no_pm(slave, SDW_SCP_INT1);
	if (ret < 0) {
		dev_err(&slave->dev,
			"SDW_SCP_INT1 read failed:%d\n", ret);
		goto io_err;
	}
	buf = ret;

	ret = sdw_nread_no_pm(slave, SDW_SCP_INTSTAT2, 2, buf2);
	if (ret < 0) {
		dev_err(&slave->dev,
			"SDW_SCP_INT2/3 read failed:%d\n", ret);
		goto io_err;
	}

	if (slave->prop.is_sdca) {
		ret = sdw_read_no_pm(slave, SDW_DP0_INT);
		if (ret < 0) {
			dev_err(&slave->dev,
				"SDW_DP0_INT read failed:%d\n", ret);
			goto io_err;
		}
		sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;
	}

	do {
		slave_notify = false;

		/*
		 * Check parity, bus clash and Slave (impl defined)
		 * interrupt
		 */
		if (buf & SDW_SCP_INT1_PARITY) {
			parity_check = slave->prop.scp_int1_mask & SDW_SCP_INT1_PARITY;
			parity_quirk = !slave->first_interrupt_done &&
				(slave->prop.quirks & SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY);

			if (parity_check && !parity_quirk)
				dev_err(&slave->dev, "Parity error detected\n");
			clear |= SDW_SCP_INT1_PARITY;
		}

		if (buf & SDW_SCP_INT1_BUS_CLASH) {
			if (slave->prop.scp_int1_mask & SDW_SCP_INT1_BUS_CLASH)
				dev_err(&slave->dev, "Bus clash detected\n");
			clear |= SDW_SCP_INT1_BUS_CLASH;
		}

		/*
		 * When bus clash or parity errors are detected, such errors
		 * are unlikely to be recoverable errors.
		 * TODO: In such scenario, reset bus. Make this configurable
		 * via sysfs property with bus reset being the default.
		 */

		if (buf & SDW_SCP_INT1_IMPL_DEF) {
			if (slave->prop.scp_int1_mask & SDW_SCP_INT1_IMPL_DEF) {
				dev_dbg(&slave->dev, "Slave impl defined interrupt\n");
				slave_notify = true;
			}
			clear |= SDW_SCP_INT1_IMPL_DEF;
		}

		/* the SDCA interrupts are cleared in the codec driver .interrupt_callback() */
		if (sdca_cascade)
			slave_notify = true;

		/* Check port 0 - 3 interrupts */
		port = buf & SDW_SCP_INT1_PORT0_3;

		/* To get port number corresponding to bits, shift it */
		port = FIELD_GET(SDW_SCP_INT1_PORT0_3, port);
		for_each_set_bit(bit, &port, 8) {
			sdw_handle_port_interrupt(slave, bit,
						  &port_status[bit]);
		}

		/* Check if cascade 2 interrupt is present */
		if (buf & SDW_SCP_INT1_SCP2_CASCADE) {
			port = buf2[0] & SDW_SCP_INTSTAT2_PORT4_10;
			for_each_set_bit(bit, &port, 8) {
				/* scp2 ports start from 4 */
				port_num = bit + 3;
				sdw_handle_port_interrupt(slave,
						port_num,
						&port_status[port_num]);
			}
		}

		/* now check last cascade */
		if (buf2[0] & SDW_SCP_INTSTAT2_SCP3_CASCADE) {
			port = buf2[1] & SDW_SCP_INTSTAT3_PORT11_14;
			for_each_set_bit(bit, &port, 8) {
				/* scp3 ports start from 11 */
				port_num = bit + 10;
				sdw_handle_port_interrupt(slave,
						port_num,
						&port_status[port_num]);
			}
		}

		/* Update the Slave driver */
		if (slave_notify && slave->ops &&
		    slave->ops->interrupt_callback) {
			slave_intr.sdca_cascade = sdca_cascade;
			slave_intr.control_port = clear;
			memcpy(slave_intr.port, &port_status,
			       sizeof(slave_intr.port));

			slave->ops->interrupt_callback(slave, &slave_intr);
		}

		/* Ack interrupt */
		ret = sdw_write_no_pm(slave, SDW_SCP_INT1, clear);
		if (ret < 0) {
			dev_err(&slave->dev,
				"SDW_SCP_INT1 write failed:%d\n", ret);
			goto io_err;
		}

		/* at this point all initial interrupt sources were handled */
		slave->first_interrupt_done = true;

		/*
		 * Read status again to ensure no new interrupts arrived
		 * while servicing interrupts.
		 */
		ret = sdw_read_no_pm(slave, SDW_SCP_INT1);
		if (ret < 0) {
			dev_err(&slave->dev,
				"SDW_SCP_INT1 recheck read failed:%d\n", ret);
			goto io_err;
		}
		_buf = ret;

		ret = sdw_nread_no_pm(slave, SDW_SCP_INTSTAT2, 2, _buf2);
		if (ret < 0) {
			dev_err(&slave->dev,
				"SDW_SCP_INT2/3 recheck read failed:%d\n", ret);
			goto io_err;
		}

		if (slave->prop.is_sdca) {
			ret = sdw_read_no_pm(slave, SDW_DP0_INT);
			if (ret < 0) {
				dev_err(&slave->dev,
					"SDW_DP0_INT recheck read failed:%d\n", ret);
				goto io_err;
			}
			sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;
		}

		/*
		 * Make sure no interrupts are pending, but filter to limit loop
		 * to interrupts identified in the first status read
		 */
		buf &= _buf;
		buf2[0] &= _buf2[0];
		buf2[1] &= _buf2[1];
		stat = buf || buf2[0] || buf2[1] || sdca_cascade;

		/*
		 * Exit loop if Slave is continuously in ALERT state even
		 * after servicing the interrupt multiple times.
		 */
		count++;

		/* we can get alerts while processing so keep retrying */
	} while (stat != 0 && count < SDW_READ_INTR_CLEAR_RETRY);

	if (count == SDW_READ_INTR_CLEAR_RETRY)
		dev_warn(&slave->dev, "Reached MAX_RETRY on alert read\n");

io_err:
	pm_runtime_mark_last_busy(&slave->dev);
	pm_runtime_put_autosuspend(&slave->dev);

	return ret;
}