diff options
author | Vijendar Mukunda <Vijendar.Mukunda@amd.com> | 2023-03-21 06:08:58 +0100 |
---|---|---|
committer | Vinod Koul <vkoul@kernel.org> | 2023-04-12 12:00:36 +0200 |
commit | 65f93e4096a07abd41acf0d240715bd8c7ef7eeb (patch) | |
tree | 1fe5222f5277dea32e03b173a25f9357e1c84e78 /drivers/soundwire | |
parent | soundwire: amd: enable build for AMD SoundWire manager driver (diff) | |
download | linux-65f93e4096a07abd41acf0d240715bd8c7ef7eeb.tar.xz linux-65f93e4096a07abd41acf0d240715bd8c7ef7eeb.zip |
soundwire: amd: add SoundWire manager interrupt handling
Add support for handling SoundWire manager interrupts.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/lkml/20230227154801.50319-6-Vijendar.Mukunda@amd.com
Link: https://lore.kernel.org/r/20230321050901.115439-6-Vijendar.Mukunda@amd.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
Diffstat (limited to 'drivers/soundwire')
-rw-r--r-- | drivers/soundwire/amd_manager.c | 126 | ||||
-rw-r--r-- | drivers/soundwire/amd_manager.h | 1 |
2 files changed, 127 insertions, 0 deletions
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c index f12dad6c3226..49b7133cb41f 100644 --- a/drivers/soundwire/amd_manager.c +++ b/drivers/soundwire/amd_manager.c @@ -327,6 +327,47 @@ static enum sdw_command_response amd_sdw_xfer_msg(struct sdw_bus *bus, struct sd return SDW_CMD_OK; } +static void amd_sdw_fill_slave_status(struct amd_sdw_manager *amd_manager, u16 index, u32 status) +{ + switch (status) { + case SDW_SLAVE_ATTACHED: + case SDW_SLAVE_UNATTACHED: + case SDW_SLAVE_ALERT: + amd_manager->status[index] = status; + break; + default: + amd_manager->status[index] = SDW_SLAVE_RESERVED; + break; + } +} + +static void amd_sdw_process_ping_status(u64 response, struct amd_sdw_manager *amd_manager) +{ + u64 slave_stat; + u32 val; + u16 dev_index; + + /* slave status response */ + slave_stat = FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_0_3, response); + slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8; + dev_dbg(amd_manager->dev, "slave_stat:0x%llx\n", slave_stat); + for (dev_index = 0; dev_index <= SDW_MAX_DEVICES; ++dev_index) { + val = (slave_stat >> (dev_index * 2)) & AMD_SDW_MCP_SLAVE_STATUS_MASK; + dev_dbg(amd_manager->dev, "val:0x%x\n", val); + amd_sdw_fill_slave_status(amd_manager, dev_index, val); + } +} + +static void amd_sdw_read_and_process_ping_status(struct amd_sdw_manager *amd_manager) +{ + u64 response; + + mutex_lock(&amd_manager->bus.msg_lock); + response = amd_sdw_send_cmd_get_resp(amd_manager, 0, 0); + mutex_unlock(&amd_manager->bus.msg_lock); + amd_sdw_process_ping_status(response, amd_manager); +} + static u32 amd_sdw_read_ping_status(struct sdw_bus *bus) { struct amd_sdw_manager *amd_manager = to_amd_sdw(bus); @@ -723,6 +764,89 @@ static int amd_sdw_register_dais(struct amd_sdw_manager *amd_manager) dais, num_dais); } +static void amd_sdw_update_slave_status_work(struct work_struct *work) +{ + struct amd_sdw_manager *amd_manager = + container_of(work, struct amd_sdw_manager, amd_sdw_work); + int retry_count = 0; + + if (amd_manager->status[0] == SDW_SLAVE_ATTACHED) { + writel(0, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7); + writel(0, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11); + } + +update_status: + sdw_handle_slave_status(&amd_manager->bus, amd_manager->status); + /* + * During the peripheral enumeration sequence, the SoundWire manager interrupts + * are masked. Once the device number programming is done for all peripherals, + * interrupts will be unmasked. Read the peripheral device status from ping command + * and process the response. This sequence will ensure all peripheral devices enumerated + * and initialized properly. + */ + if (amd_manager->status[0] == SDW_SLAVE_ATTACHED) { + if (retry_count++ < SDW_MAX_DEVICES) { + writel(AMD_SDW_IRQ_MASK_0TO7, amd_manager->mmio + + ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7); + writel(AMD_SDW_IRQ_MASK_8TO11, amd_manager->mmio + + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11); + amd_sdw_read_and_process_ping_status(amd_manager); + goto update_status; + } else { + dev_err_ratelimited(amd_manager->dev, + "Device0 detected after %d iterations\n", + retry_count); + } + } +} + +static void amd_sdw_update_slave_status(u32 status_change_0to7, u32 status_change_8to11, + struct amd_sdw_manager *amd_manager) +{ + u64 slave_stat; + u32 val; + int dev_index; + + if (status_change_0to7 == AMD_SDW_SLAVE_0_ATTACHED) + memset(amd_manager->status, 0, sizeof(amd_manager->status)); + slave_stat = status_change_0to7; + slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STATUS_8TO_11, status_change_8to11) << 32; + dev_dbg(amd_manager->dev, "status_change_0to7:0x%x status_change_8to11:0x%x\n", + status_change_0to7, status_change_8to11); + if (slave_stat) { + for (dev_index = 0; dev_index <= SDW_MAX_DEVICES; ++dev_index) { + if (slave_stat & AMD_SDW_MCP_SLAVE_STATUS_VALID_MASK(dev_index)) { + val = (slave_stat >> AMD_SDW_MCP_SLAVE_STAT_SHIFT_MASK(dev_index)) & + AMD_SDW_MCP_SLAVE_STATUS_MASK; + amd_sdw_fill_slave_status(amd_manager, dev_index, val); + } + } + } +} + +static void amd_sdw_irq_thread(struct work_struct *work) +{ + struct amd_sdw_manager *amd_manager = + container_of(work, struct amd_sdw_manager, amd_sdw_irq_thread); + u32 status_change_8to11; + u32 status_change_0to7; + + status_change_8to11 = readl(amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_8TO11); + status_change_0to7 = readl(amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_0TO7); + dev_dbg(amd_manager->dev, "[SDW%d] SDW INT: 0to7=0x%x, 8to11=0x%x\n", + amd_manager->instance, status_change_0to7, status_change_8to11); + if (status_change_8to11 & AMD_SDW_PREQ_INTR_STAT) { + amd_sdw_read_and_process_ping_status(amd_manager); + } else { + /* Check for the updated status on peripheral device */ + amd_sdw_update_slave_status(status_change_0to7, status_change_8to11, amd_manager); + } + if (status_change_8to11 || status_change_0to7) + schedule_work(&amd_manager->amd_sdw_work); + writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_8TO11); + writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_0TO7); +} + static void amd_sdw_probe_work(struct work_struct *work) { struct amd_sdw_manager *amd_manager = container_of(work, struct amd_sdw_manager, @@ -815,6 +939,8 @@ static int amd_sdw_manager_probe(struct platform_device *pdev) return ret; } dev_set_drvdata(dev, amd_manager); + INIT_WORK(&amd_manager->amd_sdw_irq_thread, amd_sdw_irq_thread); + INIT_WORK(&amd_manager->amd_sdw_work, amd_sdw_update_slave_status_work); INIT_WORK(&amd_manager->probe_work, amd_sdw_probe_work); /* * Instead of having lengthy probe sequence, use deferred probe. diff --git a/drivers/soundwire/amd_manager.h b/drivers/soundwire/amd_manager.h index c92e0dee2cb1..fca7d792bf5f 100644 --- a/drivers/soundwire/amd_manager.h +++ b/drivers/soundwire/amd_manager.h @@ -185,6 +185,7 @@ #define AMD_SDW1_PAD_KEEPER_EN_MASK 0x10 #define AMD_SDW0_PAD_KEEPER_DISABLE_MASK 0x1e #define AMD_SDW1_PAD_KEEPER_DISABLE_MASK 0xf +#define AMD_SDW_PREQ_INTR_STAT BIT(19) static u32 amd_sdw_freq_tbl[AMD_SDW_MAX_FREQ_NUM] = { AMD_SDW_DEFAULT_CLK_FREQ, |