Commit b8d49bcd authored by Thomas Gleixner's avatar Thomas Gleixner
Browse files

Merge tag 'irqchip-fixes-6.1-1' of...

Merge tag 'irqchip-fixes-6.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/core

Pull irqchip fixes from Marc Zyngier:

  - Fix IMX-MU Kconfig, keeping it private to IMX

  - Fix a register offset for the same IMX-MU driver

  - Fix the ls-extirq irqchip driver that would use the wrong
    flavour of spinlocks

Link: https://lore.kernel.org/r/20221012075125.1244143-1-maz@kernel.org
parents 36de4f94 6c9f7434
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -484,14 +484,15 @@ config IMX_INTMUX
config IMX_MU_MSI
	tristate "i.MX MU used as MSI controller"
	depends on OF && HAS_IOMEM
	depends on ARCH_MXC || COMPILE_TEST
	default m if ARCH_MXC
	select IRQ_DOMAIN
	select IRQ_DOMAIN_HIERARCHY
	select GENERIC_MSI_IRQ_DOMAIN
	help
	  Provide a driver for the MU block used as a CPU-to-CPU MSI
	  controller. This requires a specially crafted DT to make use
	  of this driver.
	  Provide a driver for the i.MX Messaging Unit block used as a
	  CPU-to-CPU MSI controller. This requires a specially crafted DT
	  to make use of this driver.

	  If unsure, say N

+1 −1
Original line number Diff line number Diff line
@@ -292,7 +292,7 @@ static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = {
	.xSR    = {
			[IMX_MU_SR]  = 0xC,
			[IMX_MU_GSR] = 0x118,
			[IMX_MU_GSR] = 0x124,
			[IMX_MU_TSR] = 0x124,
			[IMX_MU_RSR] = 0x12C,
		  },
	.xCR    = {
+63 −24
Original line number Diff line number Diff line
@@ -6,8 +6,7 @@
#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/of_address.h>
#include <linux/slab.h>

#include <dt-bindings/interrupt-controller/arm-gic.h>
@@ -16,13 +15,41 @@
#define LS1021A_SCFGREVCR 0x200

struct ls_extirq_data {
	struct regmap		*syscon;
	u32			intpcr;
	void __iomem		*intpcr;
	raw_spinlock_t		lock;
	bool			big_endian;
	bool			is_ls1021a_or_ls1043a;
	u32			nirq;
	struct irq_fwspec	map[MAXIRQ];
};

static void ls_extirq_intpcr_rmw(struct ls_extirq_data *priv, u32 mask,
				 u32 value)
{
	u32 intpcr;

	/*
	 * Serialize concurrent calls to ls_extirq_set_type() from multiple
	 * IRQ descriptors, making sure the read-modify-write is atomic.
	 */
	raw_spin_lock(&priv->lock);

	if (priv->big_endian)
		intpcr = ioread32be(priv->intpcr);
	else
		intpcr = ioread32(priv->intpcr);

	intpcr &= ~mask;
	intpcr |= value;

	if (priv->big_endian)
		iowrite32be(intpcr, priv->intpcr);
	else
		iowrite32(intpcr, priv->intpcr);

	raw_spin_unlock(&priv->lock);
}

static int
ls_extirq_set_type(struct irq_data *data, unsigned int type)
{
@@ -51,7 +78,8 @@ ls_extirq_set_type(struct irq_data *data, unsigned int type)
	default:
		return -EINVAL;
	}
	regmap_update_bits(priv->syscon, priv->intpcr, mask, value);

	ls_extirq_intpcr_rmw(priv, mask, value);

	return irq_chip_set_type_parent(data, type);
}
@@ -143,7 +171,6 @@ ls_extirq_parse_map(struct ls_extirq_data *priv, struct device_node *node)
static int __init
ls_extirq_of_init(struct device_node *node, struct device_node *parent)
{

	struct irq_domain *domain, *parent_domain;
	struct ls_extirq_data *priv;
	int ret;
@@ -151,40 +178,52 @@ ls_extirq_of_init(struct device_node *node, struct device_node *parent)
	parent_domain = irq_find_host(parent);
	if (!parent_domain) {
		pr_err("Cannot find parent domain\n");
		return -ENODEV;
		ret = -ENODEV;
		goto err_irq_find_host;
	}

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	priv->syscon = syscon_node_to_regmap(node->parent);
	if (IS_ERR(priv->syscon)) {
		ret = PTR_ERR(priv->syscon);
		pr_err("Failed to lookup parent regmap\n");
		goto out;
	if (!priv) {
		ret = -ENOMEM;
		goto err_alloc_priv;
	}
	ret = of_property_read_u32(node, "reg", &priv->intpcr);
	if (ret) {
		pr_err("Missing INTPCR offset value\n");
		goto out;

	/*
	 * All extirq OF nodes are under a scfg/syscon node with
	 * the 'ranges' property
	 */
	priv->intpcr = of_iomap(node, 0);
	if (!priv->intpcr) {
		pr_err("Cannot ioremap OF node %pOF\n", node);
		ret = -ENOMEM;
		goto err_iomap;
	}

	ret = ls_extirq_parse_map(priv, node);
	if (ret)
		goto out;
		goto err_parse_map;

	priv->big_endian = of_device_is_big_endian(parent);
	priv->is_ls1021a_or_ls1043a = of_device_is_compatible(node, "fsl,ls1021a-extirq") ||
				      of_device_is_compatible(node, "fsl,ls1043a-extirq");
	raw_spin_lock_init(&priv->lock);

	domain = irq_domain_add_hierarchy(parent_domain, 0, priv->nirq, node,
					  &extirq_domain_ops, priv);
	if (!domain)
	if (!domain) {
		ret = -ENOMEM;
		goto err_add_hierarchy;
	}

out:
	if (ret)
	return 0;

err_add_hierarchy:
err_parse_map:
	iounmap(priv->intpcr);
err_iomap:
	kfree(priv);
err_alloc_priv:
err_irq_find_host:
	return ret;
}