@@ -17,6 +17,7 @@
#include <linux/soundwire/sdw.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <linux/workqueue.h>
#include "bus.h"
#include "cadence_master.h"
@@ -745,7 +746,7 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
CDNS_MCP_INT_SLAVE_MASK, 0);
int_status &= ~CDNS_MCP_INT_SLAVE_MASK;
- ret = IRQ_WAKE_THREAD;
+ schedule_work(&cdns->work);
}
cdns_writel(cdns, CDNS_MCP_INTSTAT, int_status);
@@ -754,13 +755,14 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id)
EXPORT_SYMBOL(sdw_cdns_irq);
/**
- * sdw_cdns_thread() - Cadence irq thread handler
- * @irq: irq number
- * @dev_id: irq context
+ * To update slave status in a work since we will need to handle
+ * other interrupts eg. CDNS_MCP_INT_RX_WL during the update slave
+ * process.
*/
-irqreturn_t sdw_cdns_thread(int irq, void *dev_id)
+static void cdns_update_slave_status_work(struct work_struct *work)
{
- struct sdw_cdns *cdns = dev_id;
+ struct sdw_cdns *cdns =
+ container_of(work, struct sdw_cdns, work);
u32 slave0, slave1;
dev_dbg_ratelimited(cdns->dev, "Slave status change\n");
@@ -777,9 +779,7 @@ irqreturn_t sdw_cdns_thread(int irq, void *dev_id)
cdns_updatel(cdns, CDNS_MCP_INTMASK,
CDNS_MCP_INT_SLAVE_MASK, CDNS_MCP_INT_SLAVE_MASK);
- return IRQ_HANDLED;
}
-EXPORT_SYMBOL(sdw_cdns_thread);
/*
* init routines
@@ -1187,6 +1187,7 @@ int sdw_cdns_probe(struct sdw_cdns *cdns)
init_completion(&cdns->tx_complete);
cdns->bus.port_ops = &cdns_port_ops;
+ INIT_WORK(&cdns->work, cdns_update_slave_status_work);
return 0;
}
EXPORT_SYMBOL(sdw_cdns_probe);
@@ -126,6 +126,10 @@ struct sdw_cdns {
bool link_up;
unsigned int msg_count;
+
+ struct work_struct work;
+
+ struct list_head list;
};
#define bus_to_cdns(_bus) container_of(_bus, struct sdw_cdns, bus)
@@ -1095,6 +1095,7 @@ static int intel_master_probe(struct sdw_master_device *md, void *link_ctx)
sdw->cdns.msg_count = 0;
sdw->cdns.bus.dev = &md->dev;
sdw->cdns.bus.link_id = md->link_id;
+ sdw->link_res->cdns = &sdw->cdns;
sdw_cdns_probe(&sdw->cdns);
@@ -1119,21 +1120,7 @@ static int intel_master_probe(struct sdw_master_device *md, void *link_ctx)
return 0;
}
- /* Acquire IRQ */
- ret = request_threaded_irq(sdw->link_res->irq,
- sdw_cdns_irq, sdw_cdns_thread,
- IRQF_SHARED, KBUILD_MODNAME, &sdw->cdns);
- if (ret < 0) {
- dev_err(sdw->cdns.dev, "unable to grab IRQ %d, disabling device\n",
- sdw->link_res->irq);
- goto err_init;
- }
-
return 0;
-
-err_init:
- sdw_delete_bus_master(&sdw->cdns.bus);
- return ret;
}
static int intel_master_startup(struct sdw_master_device *md)
@@ -1190,7 +1177,6 @@ static int intel_master_startup(struct sdw_master_device *md)
err_interrupt:
sdw_cdns_enable_interrupt(&sdw->cdns, false);
err_init:
- free_irq(sdw->link_res->irq, sdw);
sdw_delete_bus_master(&sdw->cdns.bus);
return ret;
}
@@ -1204,7 +1190,6 @@ static int intel_master_remove(struct sdw_master_device *md)
if (!sdw->cdns.bus.prop.hw_disabled) {
intel_debugfs_exit(sdw);
sdw_cdns_enable_interrupt(&sdw->cdns, false);
- free_irq(sdw->link_res->irq, sdw);
snd_soc_unregister_component(sdw->cdns.dev);
}
sdw_delete_bus_master(&sdw->cdns.bus);
@@ -25,6 +25,8 @@ struct sdw_intel_link_res {
int irq;
const struct sdw_intel_ops *ops;
struct device *dev;
+ struct sdw_cdns *cdns;
+ struct list_head list;
};
#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1)
@@ -11,8 +11,10 @@
#include <linux/export.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/interrupt.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_intel.h>
+#include "cadence_master.h"
#include "intel.h"
#define SDW_LINK_TYPE 4 /* from Intel ACPI documentation */
@@ -161,6 +163,32 @@ void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable)
}
EXPORT_SYMBOL(sdw_intel_enable_irq);
+static irqreturn_t sdw_intel_irq(int irq, void *dev_id)
+{
+ struct sdw_intel_ctx *ctx = dev_id;
+ u32 int_status;
+
+ int_status = readl(ctx->mmio_base + HDA_DSP_REG_ADSPIS2);
+ if (int_status & HDA_DSP_REG_ADSPIC2_SNDW) {
+ sdw_intel_enable_irq(ctx->mmio_base, false);
+ return IRQ_WAKE_THREAD;
+ }
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t sdw_intel_thread(int irq, void *dev_id)
+{
+ struct sdw_intel_ctx *ctx = dev_id;
+ struct sdw_intel_link_res *link;
+
+ list_for_each_entry(link, &ctx->link_list, list)
+ sdw_cdns_irq(irq, link->cdns);
+
+ sdw_intel_enable_irq(ctx->mmio_base, true);
+ return IRQ_HANDLED;
+}
+
static struct sdw_intel_ctx
*sdw_intel_probe_controller(struct sdw_intel_res *res)
{
@@ -171,6 +199,7 @@ static struct sdw_intel_ctx
u32 link_mask;
int count;
int i;
+ int ret;
if (!res)
return NULL;
@@ -196,10 +225,13 @@ static struct sdw_intel_ctx
ctx->mmio_base = res->mmio_base;
ctx->link_mask = res->link_mask;
ctx->handle = res->handle;
+ ctx->irq = res->irq;
link = ctx->links;
link_mask = ctx->link_mask;
+ INIT_LIST_HEAD(&ctx->link_list);
+
/* Create SDW Master devices */
for (i = 0; i < count; i++, link++) {
if (link_mask && !(link_mask & BIT(i)))
@@ -220,12 +252,22 @@ static struct sdw_intel_ctx
+ (SDW_LINK_SIZE * i);
link->shim = res->mmio_base + SDW_SHIM_BASE;
link->alh = res->mmio_base + SDW_ALH_BASE;
- link->irq = res->irq;
link->ops = res->ops;
link->dev = res->dev;
/* let the SoundWire master driver to its probe */
md->driver->probe(md, link);
+
+ list_add_tail(&link->list, &ctx->link_list);
+ }
+
+ ret = request_threaded_irq(ctx->irq,
+ sdw_intel_irq, sdw_intel_thread,
+ IRQF_SHARED, KBUILD_MODNAME, ctx);
+ if (ret < 0) {
+ dev_err(&adev->dev, "unable to grab IRQ %d, disabling device\n",
+ res->irq);
+ goto err;
}
return ctx;
@@ -373,6 +415,7 @@ EXPORT_SYMBOL(sdw_intel_startup);
*/
void sdw_intel_exit(struct sdw_intel_ctx *ctx)
{
+ free_irq(ctx->irq, ctx);
sdw_intel_cleanup(ctx);
kfree(ctx);
}
@@ -94,7 +94,9 @@ struct sdw_intel_ctx {
void __iomem *mmio_base;
u32 link_mask;
acpi_handle handle;
+ int irq;
struct sdw_intel_link_res *links;
+ struct list_head link_list;
};
/*