linux/drivers/soundwire/irq.c
Charles Keepax aab12022b0 soundwire: bus: Add internal slave ID and use for IRQs
Currently the SoundWire IRQ code uses the dev_num to create an IRQ
mapping for each slave. However, there is an issue there, the dev_num
is only allocated when the slave enumerates on the bus and enumeration
may happen before or after probe of the slave driver. In the case
enumeration happens after probe of the slave driver then the IRQ
mapping will use dev_num before it is set. This could cause multiple
slaves to use zero as their IRQ mapping.

It is very desirable to have the IRQ mapped before the slave probe
is called, so drivers can do resource allocation in probe as normal. To
solve these issues add an internal ID created for each slave when it is
probed and use that for mapping the IRQ.

Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
Link: https://lore.kernel.org/r/20250429101808.348462-3-ckeepax@opensource.cirrus.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
2025-05-14 12:42:50 +01:00

63 lines
1.4 KiB
C

// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2023 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
#include <linux/device.h>
#include <linux/fwnode.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/soundwire/sdw.h>
#include "irq.h"
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,
};
int sdw_irq_create(struct sdw_bus *bus,
struct fwnode_handle *fwnode)
{
bus->irq_chip.name = dev_name(bus->dev);
bus->domain = irq_domain_create_linear(fwnode, SDW_FW_MAX_DEVICES,
&sdw_domain_ops, bus);
if (!bus->domain) {
dev_err(bus->dev, "Failed to add IRQ domain\n");
return -EINVAL;
}
return 0;
}
void sdw_irq_delete(struct sdw_bus *bus)
{
irq_domain_remove(bus->domain);
}
static void sdw_irq_dispose_mapping(void *data)
{
struct sdw_slave *slave = data;
irq_dispose_mapping(slave->irq);
}
void sdw_irq_create_mapping(struct sdw_slave *slave)
{
slave->irq = irq_create_mapping(slave->bus->domain, slave->index);
if (!slave->irq)
dev_warn(&slave->dev, "Failed to map IRQ\n");
devm_add_action_or_reset(&slave->dev, sdw_irq_dispose_mapping, slave);
}