mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

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>
63 lines
1.4 KiB
C
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);
|
|
}
|