aboutsummaryrefslogtreecommitdiff
path: root/drivers/soundwire/bus.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/soundwire/bus.c')
-rw-r--r--drivers/soundwire/bus.c55
1 files changed, 46 insertions, 9 deletions
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
index 8eaf31e76677..d1e8c3a54976 100644
--- a/drivers/soundwire/bus.c
+++ b/drivers/soundwire/bus.c
@@ -1280,7 +1280,7 @@ static int sdw_initialize_slave(struct sdw_slave *slave)
static int sdw_handle_dp0_interrupt(struct sdw_slave *slave, u8 *slave_status)
{
- u8 clear = 0, impl_int_mask;
+ u8 clear, impl_int_mask;
int status, status2, ret, count = 0;
status = sdw_read(slave, SDW_DP0_INT);
@@ -1291,6 +1291,8 @@ static int sdw_handle_dp0_interrupt(struct sdw_slave *slave, u8 *slave_status)
}
do {
+ clear = status & ~SDW_DP0_INTERRUPTS;
+
if (status & SDW_DP0_INT_TEST_FAIL) {
dev_err(&slave->dev, "Test fail for port 0\n");
clear |= SDW_DP0_INT_TEST_FAIL;
@@ -1319,7 +1321,7 @@ static int sdw_handle_dp0_interrupt(struct sdw_slave *slave, u8 *slave_status)
*slave_status = clear;
}
- /* clear the interrupt */
+ /* clear the interrupts but don't touch reserved and SDCA_CASCADE fields */
ret = sdw_write(slave, SDW_DP0_INT, clear);
if (ret < 0) {
dev_err(slave->bus->dev,
@@ -1334,12 +1336,13 @@ static int sdw_handle_dp0_interrupt(struct sdw_slave *slave, u8 *slave_status)
"SDW_DP0_INT read failed:%d\n", status2);
return status2;
}
+ /* filter to limit loop to interrupts identified in the first status read */
status &= status2;
count++;
/* we can get alerts while processing so keep retrying */
- } while (status != 0 && count < SDW_READ_INTR_CLEAR_RETRY);
+ } while ((status & SDW_DP0_INTERRUPTS) && (count < SDW_READ_INTR_CLEAR_RETRY));
if (count == SDW_READ_INTR_CLEAR_RETRY)
dev_warn(slave->bus->dev, "Reached MAX_RETRY on DP0 read\n");
@@ -1350,7 +1353,7 @@ static int sdw_handle_dp0_interrupt(struct sdw_slave *slave, u8 *slave_status)
static int sdw_handle_port_interrupt(struct sdw_slave *slave,
int port, u8 *slave_status)
{
- u8 clear = 0, impl_int_mask;
+ u8 clear, impl_int_mask;
int status, status2, ret, count = 0;
u32 addr;
@@ -1367,6 +1370,8 @@ static int sdw_handle_port_interrupt(struct sdw_slave *slave,
}
do {
+ clear = status & ~SDW_DPN_INTERRUPTS;
+
if (status & SDW_DPN_INT_TEST_FAIL) {
dev_err(&slave->dev, "Test fail for port:%d\n", port);
clear |= SDW_DPN_INT_TEST_FAIL;
@@ -1389,7 +1394,7 @@ static int sdw_handle_port_interrupt(struct sdw_slave *slave,
*slave_status = clear;
}
- /* clear the interrupt */
+ /* clear the interrupt but don't touch reserved fields */
ret = sdw_write(slave, addr, clear);
if (ret < 0) {
dev_err(slave->bus->dev,
@@ -1404,12 +1409,13 @@ static int sdw_handle_port_interrupt(struct sdw_slave *slave,
"SDW_DPN_INT read failed:%d\n", status2);
return status2;
}
+ /* filter to limit loop to interrupts identified in the first status read */
status &= status2;
count++;
/* we can get alerts while processing so keep retrying */
- } while (status != 0 && count < SDW_READ_INTR_CLEAR_RETRY);
+ } while ((status & SDW_DPN_INTERRUPTS) && (count < SDW_READ_INTR_CLEAR_RETRY));
if (count == SDW_READ_INTR_CLEAR_RETRY)
dev_warn(slave->bus->dev, "Reached MAX_RETRY on port read");
@@ -1423,7 +1429,8 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
u8 clear = 0, bit, port_status[15] = {0};
int port_num, stat, ret, count = 0;
unsigned long port;
- bool slave_notify = false;
+ bool slave_notify;
+ u8 sdca_cascade = 0;
u8 buf, buf2[2], _buf, _buf2[2];
bool parity_check;
bool parity_quirk;
@@ -1453,7 +1460,19 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
goto io_err;
}
+ if (slave->prop.is_sdca) {
+ ret = sdw_read(slave, SDW_DP0_INT);
+ if (ret < 0) {
+ dev_err(slave->bus->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
@@ -1489,6 +1508,10 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
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;
@@ -1526,6 +1549,7 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
/* 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));
@@ -1563,11 +1587,24 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
goto io_err;
}
- /* Make sure no interrupts are pending */
+ if (slave->prop.is_sdca) {
+ ret = sdw_read(slave, SDW_DP0_INT);
+ if (ret < 0) {
+ dev_err(slave->bus->dev,
+ "SDW_DP0_INT 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];
+ stat = buf || buf2[0] || buf2[1] || sdca_cascade;
/*
* Exit loop if Slave is continuously in ALERT state even