diff options
Diffstat (limited to 'drivers/soundwire/bus.c')
-rw-r--r-- | drivers/soundwire/bus.c | 55 |
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 |