soundwire: bus: Allow SoundWire peripherals to register IRQ handlers
authorLucas Tanure <tanureal@opensource.cirrus.com>
Fri, 4 Aug 2023 10:45:57 +0000 (11:45 +0100)
committerLee Jones <lee@kernel.org>
Thu, 17 Aug 2023 11:06:11 +0000 (12:06 +0100)
Currently the in-band alerts for SoundWire peripherals can only
be communicated to the driver through the interrupt_callback
function. This however is slightly inconvenient for devices that wish
to share IRQ handling code between SoundWire and I2C/SPI, the later
would normally register an IRQ handler with the IRQ subsystem. However
there is no reason the SoundWire in-band IRQs can not also be
communicated as an actual IRQ to the driver.

Add support for SoundWire peripherals to register a normal IRQ
handler to receive SoundWire in-band alerts, allowing code to be
shared across control buses. Note that we allow users to use both the
interrupt_callback and the IRQ handler, this is useful for devices
which must clear additional chip specific SoundWire registers that are
not a part of the normal IRQ flow, or the SoundWire specification.

Signed-off-by: Lucas Tanure <tanureal@opensource.cirrus.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Acked-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Acked-by: Vinod Koul <vkoul@kernel.org>
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Link: https://lore.kernel.org/r/20230804104602.395892-2-ckeepax@opensource.cirrus.com
Signed-off-by: Lee Jones <lee@kernel.org>
drivers/soundwire/bus.c
drivers/soundwire/bus_type.c
include/linux/soundwire/sdw.h

index dba920ec88f6fb17c2c9c26140fc192f8ec2f11c..cf55386256f3f310a1102f122fb296aee0c8453b 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <linux/acpi.h>
 #include <linux/delay.h>
+#include <linux/irq.h>
 #include <linux/mod_devicetable.h>
 #include <linux/pm_runtime.h>
 #include <linux/soundwire/sdw_registers.h>
@@ -25,6 +26,23 @@ static int sdw_get_id(struct sdw_bus *bus)
        return 0;
 }
 
+static int sdw_irq_map(struct irq_domain *h, unsigned int virq,
+                      irq_hw_number_t hw)
+{
+       struct sdw_bus *bus = h->host_data;
+
+       irq_set_chip_data(virq, bus);
+       irq_set_chip(virq, &bus->irq_chip);
+       irq_set_nested_thread(virq, 1);
+       irq_set_noprobe(virq);
+
+       return 0;
+}
+
+static const struct irq_domain_ops sdw_domain_ops = {
+       .map    = sdw_irq_map,
+};
+
 /**
  * sdw_bus_master_add() - add a bus Master instance
  * @bus: bus instance
@@ -151,6 +169,14 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
        bus->params.curr_bank = SDW_BANK0;
        bus->params.next_bank = SDW_BANK1;
 
+       bus->irq_chip.name = dev_name(bus->dev);
+       bus->domain = irq_domain_create_linear(fwnode, SDW_MAX_DEVICES,
+                                              &sdw_domain_ops, bus);
+       if (!bus->domain) {
+               dev_err(bus->dev, "Failed to add IRQ domain\n");
+               return -EINVAL;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL(sdw_bus_master_add);
@@ -187,6 +213,9 @@ static int sdw_delete_slave(struct device *dev, void *data)
 void sdw_bus_master_delete(struct sdw_bus *bus)
 {
        device_for_each_child(bus->dev, NULL, sdw_delete_slave);
+
+       irq_domain_remove(bus->domain);
+
        sdw_master_device_del(bus);
 
        sdw_bus_debugfs_exit(bus);
@@ -1725,6 +1754,9 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
                                struct device *dev = &slave->dev;
                                struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
 
+                               if (slave->prop.use_domain_irq && slave->irq)
+                                       handle_nested_irq(slave->irq);
+
                                if (drv->ops && drv->ops->interrupt_callback) {
                                        slave_intr.sdca_cascade = sdca_cascade;
                                        slave_intr.control_port = clear;
index 1f43ee848eac8f76584c6153483a52c1d5a97b6e..fafbc284e82da48927b9ba5fa17b24eaae5746c2 100644 (file)
@@ -122,6 +122,12 @@ static int sdw_drv_probe(struct device *dev)
        if (drv->ops && drv->ops->read_prop)
                drv->ops->read_prop(slave);
 
+       if (slave->prop.use_domain_irq) {
+               slave->irq = irq_create_mapping(slave->bus->domain, slave->dev_num);
+               if (!slave->irq)
+                       dev_warn(dev, "Failed to map IRQ\n");
+       }
+
        /* init the sysfs as we have properties now */
        ret = sdw_slave_sysfs_init(slave);
        if (ret < 0)
@@ -166,7 +172,13 @@ static int sdw_drv_remove(struct device *dev)
        int ret = 0;
 
        mutex_lock(&slave->sdw_dev_lock);
+
        slave->probed = false;
+
+       if (slave->prop.use_domain_irq)
+               irq_dispose_mapping(irq_find_mapping(slave->bus->domain,
+                                                    slave->dev_num));
+
        mutex_unlock(&slave->sdw_dev_lock);
 
        if (drv->remove)
index f523ceabd0596577796f7ea10790512b1cf3e0fe..8923387a7405bc34da868a27cd3dd76efd2fd606 100644 (file)
@@ -6,6 +6,8 @@
 
 #include <linux/bug.h>
 #include <linux/lockdep_types.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
 #include <linux/mod_devicetable.h>
 #include <linux/bitfield.h>
 
@@ -370,6 +372,7 @@ struct sdw_dpn_prop {
  * @clock_reg_supported: the Peripheral implements the clock base and scale
  * registers introduced with the SoundWire 1.2 specification. SDCA devices
  * do not need to set this boolean property as the registers are required.
+ * @use_domain_irq: call actual IRQ handler on slave, as well as callback
  */
 struct sdw_slave_prop {
        u32 mipi_revision;
@@ -394,6 +397,7 @@ struct sdw_slave_prop {
        u8 scp_int1_mask;
        u32 quirks;
        bool clock_reg_supported;
+       bool use_domain_irq;
 };
 
 #define SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY        BIT(0)
@@ -641,6 +645,7 @@ struct sdw_slave_ops {
  * struct sdw_slave - SoundWire Slave
  * @id: MIPI device ID
  * @dev: Linux device
+ * @irq: IRQ number
  * @status: Status reported by the Slave
  * @bus: Bus handle
  * @prop: Slave properties
@@ -670,6 +675,7 @@ struct sdw_slave_ops {
 struct sdw_slave {
        struct sdw_slave_id id;
        struct device dev;
+       int irq;
        enum sdw_slave_status status;
        struct sdw_bus *bus;
        struct sdw_slave_prop prop;
@@ -885,6 +891,7 @@ struct sdw_master_ops {
  * is used to compute and program bus bandwidth, clock, frame shape,
  * transport and port parameters
  * @debugfs: Bus debugfs
+ * @domain: IRQ domain
  * @defer_msg: Defer message
  * @clk_stop_timeout: Clock stop timeout computed
  * @bank_switch_timeout: Bank switch timeout computed
@@ -920,6 +927,8 @@ struct sdw_bus {
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs;
 #endif
+       struct irq_chip irq_chip;
+       struct irq_domain *domain;
        struct sdw_defer defer_msg;
        unsigned int clk_stop_timeout;
        u32 bank_switch_timeout;