Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

soundwire: bus: Allow SoundWire peripherals to register IRQ handlers

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>

authored by

Lucas Tanure and committed by
Lee Jones
12a95123 06c2afb8

+53
+32
drivers/soundwire/bus.c
··· 3 3 4 4 #include <linux/acpi.h> 5 5 #include <linux/delay.h> 6 + #include <linux/irq.h> 6 7 #include <linux/mod_devicetable.h> 7 8 #include <linux/pm_runtime.h> 8 9 #include <linux/soundwire/sdw_registers.h> ··· 25 24 bus->id = rc; 26 25 return 0; 27 26 } 27 + 28 + static int sdw_irq_map(struct irq_domain *h, unsigned int virq, 29 + irq_hw_number_t hw) 30 + { 31 + struct sdw_bus *bus = h->host_data; 32 + 33 + irq_set_chip_data(virq, bus); 34 + irq_set_chip(virq, &bus->irq_chip); 35 + irq_set_nested_thread(virq, 1); 36 + irq_set_noprobe(virq); 37 + 38 + return 0; 39 + } 40 + 41 + static const struct irq_domain_ops sdw_domain_ops = { 42 + .map = sdw_irq_map, 43 + }; 28 44 29 45 /** 30 46 * sdw_bus_master_add() - add a bus Master instance ··· 169 151 bus->params.curr_bank = SDW_BANK0; 170 152 bus->params.next_bank = SDW_BANK1; 171 153 154 + bus->irq_chip.name = dev_name(bus->dev); 155 + bus->domain = irq_domain_create_linear(fwnode, SDW_MAX_DEVICES, 156 + &sdw_domain_ops, bus); 157 + if (!bus->domain) { 158 + dev_err(bus->dev, "Failed to add IRQ domain\n"); 159 + return -EINVAL; 160 + } 161 + 172 162 return 0; 173 163 } 174 164 EXPORT_SYMBOL(sdw_bus_master_add); ··· 213 187 void sdw_bus_master_delete(struct sdw_bus *bus) 214 188 { 215 189 device_for_each_child(bus->dev, NULL, sdw_delete_slave); 190 + 191 + irq_domain_remove(bus->domain); 192 + 216 193 sdw_master_device_del(bus); 217 194 218 195 sdw_bus_debugfs_exit(bus); ··· 1753 1724 if (slave->probed) { 1754 1725 struct device *dev = &slave->dev; 1755 1726 struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); 1727 + 1728 + if (slave->prop.use_domain_irq && slave->irq) 1729 + handle_nested_irq(slave->irq); 1756 1730 1757 1731 if (drv->ops && drv->ops->interrupt_callback) { 1758 1732 slave_intr.sdca_cascade = sdca_cascade;
+12
drivers/soundwire/bus_type.c
··· 122 122 if (drv->ops && drv->ops->read_prop) 123 123 drv->ops->read_prop(slave); 124 124 125 + if (slave->prop.use_domain_irq) { 126 + slave->irq = irq_create_mapping(slave->bus->domain, slave->dev_num); 127 + if (!slave->irq) 128 + dev_warn(dev, "Failed to map IRQ\n"); 129 + } 130 + 125 131 /* init the sysfs as we have properties now */ 126 132 ret = sdw_slave_sysfs_init(slave); 127 133 if (ret < 0) ··· 172 166 int ret = 0; 173 167 174 168 mutex_lock(&slave->sdw_dev_lock); 169 + 175 170 slave->probed = false; 171 + 172 + if (slave->prop.use_domain_irq) 173 + irq_dispose_mapping(irq_find_mapping(slave->bus->domain, 174 + slave->dev_num)); 175 + 176 176 mutex_unlock(&slave->sdw_dev_lock); 177 177 178 178 if (drv->remove)
+9
include/linux/soundwire/sdw.h
··· 6 6 7 7 #include <linux/bug.h> 8 8 #include <linux/lockdep_types.h> 9 + #include <linux/irq.h> 10 + #include <linux/irqdomain.h> 9 11 #include <linux/mod_devicetable.h> 10 12 #include <linux/bitfield.h> 11 13 ··· 372 370 * @clock_reg_supported: the Peripheral implements the clock base and scale 373 371 * registers introduced with the SoundWire 1.2 specification. SDCA devices 374 372 * do not need to set this boolean property as the registers are required. 373 + * @use_domain_irq: call actual IRQ handler on slave, as well as callback 375 374 */ 376 375 struct sdw_slave_prop { 377 376 u32 mipi_revision; ··· 397 394 u8 scp_int1_mask; 398 395 u32 quirks; 399 396 bool clock_reg_supported; 397 + bool use_domain_irq; 400 398 }; 401 399 402 400 #define SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY BIT(0) ··· 645 641 * struct sdw_slave - SoundWire Slave 646 642 * @id: MIPI device ID 647 643 * @dev: Linux device 644 + * @irq: IRQ number 648 645 * @status: Status reported by the Slave 649 646 * @bus: Bus handle 650 647 * @prop: Slave properties ··· 675 670 struct sdw_slave { 676 671 struct sdw_slave_id id; 677 672 struct device dev; 673 + int irq; 678 674 enum sdw_slave_status status; 679 675 struct sdw_bus *bus; 680 676 struct sdw_slave_prop prop; ··· 891 885 * is used to compute and program bus bandwidth, clock, frame shape, 892 886 * transport and port parameters 893 887 * @debugfs: Bus debugfs 888 + * @domain: IRQ domain 894 889 * @defer_msg: Defer message 895 890 * @clk_stop_timeout: Clock stop timeout computed 896 891 * @bank_switch_timeout: Bank switch timeout computed ··· 927 920 #ifdef CONFIG_DEBUG_FS 928 921 struct dentry *debugfs; 929 922 #endif 923 + struct irq_chip irq_chip; 924 + struct irq_domain *domain; 930 925 struct sdw_defer defer_msg; 931 926 unsigned int clk_stop_timeout; 932 927 u32 bank_switch_timeout;