Commit e0b78e9f authored by Marc Zyngier's avatar Marc Zyngier
Browse files

Merge branch irq/loongarch-fixes-6.5 into irq/irqchip-next



* irq/loongarch-fixes-6.5:
  : .
  : Yet another series of random fixes for the Loongson/Loongarch
  : string of interrupt controller, covering
  :
  : - affinity setting,
  : - trigger polarity,
  : - wake-up,
  : - DT support
  : .
  irqchip/loongson-eiointc: Add DT init support
  dt-bindings: interrupt-controller: Add Loongson EIOINTC
  irqchip/loongson-eiointc: Fix irq affinity setting during resume
  irqchip/loongson-liointc: Add IRQCHIP_SKIP_SET_WAKE flag
  irqchip/loongson-liointc: Fix IRQ trigger polarity
  irqchip/loongson-pch-pic: Fix potential incorrect hwirq assignment
  irqchip/loongson-pch-pic: Fix initialization of HT vector register

Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parents d59b553f a3f1132c
Loading
Loading
Loading
Loading
+59 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interrupt-controller/loongson,eiointc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Loongson Extended I/O Interrupt Controller

maintainers:
  - Binbin Zhou <zhoubinbin@loongson.cn>

description: |
  This interrupt controller is found on the Loongson-3 family chips and
  Loongson-2K series chips and is used to distribute interrupts directly to
  individual cores without forwarding them through the HT's interrupt line.

allOf:
  - $ref: /schemas/interrupt-controller.yaml#

properties:
  compatible:
    enum:
      - loongson,ls2k0500-eiointc
      - loongson,ls2k2000-eiointc

  reg:
    maxItems: 1

  interrupts:
    maxItems: 1

  interrupt-controller: true

  '#interrupt-cells':
    const: 1

required:
  - compatible
  - reg
  - interrupts
  - interrupt-controller
  - '#interrupt-cells'

unevaluatedProperties: false

examples:
  - |
    eiointc: interrupt-controller@1fe11600 {
      compatible = "loongson,ls2k0500-eiointc";
      reg = <0x1fe10000 0x10000>;

      interrupt-controller;
      #interrupt-cells = <1>;

      interrupt-parent = <&cpuintc>;
      interrupts = <3>;
    };

...
+99 −36
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ static int nr_pics;

struct eiointc_priv {
	u32			node;
	u32			vec_count;
	nodemask_t		node_map;
	cpumask_t		cpuspan_map;
	struct fwnode_handle	*domain_handle;
@@ -153,18 +154,18 @@ static int eiointc_router_init(unsigned int cpu)
	if ((cpu_logical_map(cpu) % CORES_PER_EIO_NODE) == 0) {
		eiointc_enable();

		for (i = 0; i < VEC_COUNT / 32; i++) {
		for (i = 0; i < eiointc_priv[0]->vec_count / 32; i++) {
			data = (((1 << (i * 2 + 1)) << 16) | (1 << (i * 2)));
			iocsr_write32(data, EIOINTC_REG_NODEMAP + i * 4);
		}

		for (i = 0; i < VEC_COUNT / 32 / 4; i++) {
		for (i = 0; i < eiointc_priv[0]->vec_count / 32 / 4; i++) {
			bit = BIT(1 + index); /* Route to IP[1 + index] */
			data = bit | (bit << 8) | (bit << 16) | (bit << 24);
			iocsr_write32(data, EIOINTC_REG_IPMAP + i * 4);
		}

		for (i = 0; i < VEC_COUNT / 4; i++) {
		for (i = 0; i < eiointc_priv[0]->vec_count / 4; i++) {
			/* Route to Node-0 Core-0 */
			if (index == 0)
				bit = BIT(cpu_logical_map(0));
@@ -175,7 +176,7 @@ static int eiointc_router_init(unsigned int cpu)
			iocsr_write32(data, EIOINTC_REG_ROUTE + i * 4);
		}

		for (i = 0; i < VEC_COUNT / 32; i++) {
		for (i = 0; i < eiointc_priv[0]->vec_count / 32; i++) {
			data = 0xffffffff;
			iocsr_write32(data, EIOINTC_REG_ENABLE + i * 4);
			iocsr_write32(data, EIOINTC_REG_BOUNCE + i * 4);
@@ -195,7 +196,7 @@ static void eiointc_irq_dispatch(struct irq_desc *desc)

	chained_irq_enter(chip, desc);

	for (i = 0; i < VEC_REG_COUNT; i++) {
	for (i = 0; i < eiointc_priv[0]->vec_count / VEC_COUNT_PER_REG; i++) {
		pending = iocsr_read64(EIOINTC_REG_ISR + (i << 3));
		iocsr_write64(pending, EIOINTC_REG_ISR + (i << 3));
		while (pending) {
@@ -310,11 +311,11 @@ static void eiointc_resume(void)
	eiointc_router_init(0);

	for (i = 0; i < nr_pics; i++) {
		for (j = 0; j < VEC_COUNT; j++) {
		for (j = 0; j < eiointc_priv[0]->vec_count; j++) {
			desc = irq_resolve_mapping(eiointc_priv[i]->eiointc_domain, j);
			if (desc && desc->handle_irq && desc->handle_irq != handle_bad_irq) {
				raw_spin_lock(&desc->lock);
				irq_data = &desc->irq_data;
				irq_data = irq_domain_get_irq_data(eiointc_priv[i]->eiointc_domain, irq_desc_get_irq(desc));
				eiointc_set_irq_affinity(irq_data, irq_data->common->affinity, 0);
				raw_spin_unlock(&desc->lock);
			}
@@ -375,11 +376,47 @@ static int __init acpi_cascade_irqdomain_init(void)
	return 0;
}

static int __init eiointc_init(struct eiointc_priv *priv, int parent_irq,
			       u64 node_map)
{
	int i;

	node_map = node_map ? node_map : -1ULL;
	for_each_possible_cpu(i) {
		if (node_map & (1ULL << (cpu_to_eio_node(i)))) {
			node_set(cpu_to_eio_node(i), priv->node_map);
			cpumask_or(&priv->cpuspan_map, &priv->cpuspan_map,
				   cpumask_of(i));
		}
	}

	priv->eiointc_domain = irq_domain_create_linear(priv->domain_handle,
							priv->vec_count,
							&eiointc_domain_ops,
							priv);
	if (!priv->eiointc_domain) {
		pr_err("loongson-extioi: cannot add IRQ domain\n");
		return -ENOMEM;
	}

	eiointc_priv[nr_pics++] = priv;
	eiointc_router_init(0);
	irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, priv);

	if (nr_pics == 1) {
		register_syscore_ops(&eiointc_syscore_ops);
		cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_LOONGARCH_STARTING,
					  "irqchip/loongarch/intc:starting",
					  eiointc_router_init, NULL);
	}

	return 0;
}

int __init eiointc_acpi_init(struct irq_domain *parent,
				     struct acpi_madt_eio_pic *acpi_eiointc)
{
	int i, ret, parent_irq;
	unsigned long node_map;
	int parent_irq, ret;
	struct eiointc_priv *priv;
	int node;

@@ -394,37 +431,14 @@ int __init eiointc_acpi_init(struct irq_domain *parent,
		goto out_free_priv;
	}

	priv->vec_count = VEC_COUNT;
	priv->node = acpi_eiointc->node;
	node_map = acpi_eiointc->node_map ? : -1ULL;

	for_each_possible_cpu(i) {
		if (node_map & (1ULL << cpu_to_eio_node(i))) {
			node_set(cpu_to_eio_node(i), priv->node_map);
			cpumask_or(&priv->cpuspan_map, &priv->cpuspan_map, cpumask_of(i));
		}
	}

	/* Setup IRQ domain */
	priv->eiointc_domain = irq_domain_create_linear(priv->domain_handle, VEC_COUNT,
					&eiointc_domain_ops, priv);
	if (!priv->eiointc_domain) {
		pr_err("loongson-eiointc: cannot add IRQ domain\n");
		goto out_free_handle;
	}

	eiointc_priv[nr_pics++] = priv;

	eiointc_router_init(0);

	parent_irq = irq_create_mapping(parent, acpi_eiointc->cascade);
	irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, priv);

	if (nr_pics == 1) {
		register_syscore_ops(&eiointc_syscore_ops);
		cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_LOONGARCH_STARTING,
				  "irqchip/loongarch/intc:starting",
				  eiointc_router_init, NULL);
	}
	ret = eiointc_init(priv, parent_irq, acpi_eiointc->node_map);
	if (ret < 0)
		goto out_free_handle;

	if (cpu_has_flatmode)
		node = cpu_to_node(acpi_eiointc->node * CORES_PER_EIO_NODE);
@@ -432,7 +446,10 @@ int __init eiointc_acpi_init(struct irq_domain *parent,
		node = acpi_eiointc->node;
	acpi_set_vec_parent(node, priv->eiointc_domain, pch_group);
	acpi_set_vec_parent(node, priv->eiointc_domain, msi_group);

	ret = acpi_cascade_irqdomain_init();
	if (ret < 0)
		goto out_free_handle;

	return ret;

@@ -444,3 +461,49 @@ int __init eiointc_acpi_init(struct irq_domain *parent,

	return -ENOMEM;
}

static int __init eiointc_of_init(struct device_node *of_node,
				  struct device_node *parent)
{
	int parent_irq, ret;
	struct eiointc_priv *priv;

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

	parent_irq = irq_of_parse_and_map(of_node, 0);
	if (parent_irq <= 0) {
		ret = -ENODEV;
		goto out_free_priv;
	}

	ret = irq_set_handler_data(parent_irq, priv);
	if (ret < 0)
		goto out_free_priv;

	/*
	 * In particular, the number of devices supported by the LS2K0500
	 * extended I/O interrupt vector is 128.
	 */
	if (of_device_is_compatible(of_node, "loongson,ls2k0500-eiointc"))
		priv->vec_count = 128;
	else
		priv->vec_count = VEC_COUNT;

	priv->node = 0;
	priv->domain_handle = of_node_to_fwnode(of_node);

	ret = eiointc_init(priv, parent_irq, 0);
	if (ret < 0)
		goto out_free_priv;

	return 0;

out_free_priv:
	kfree(priv);
	return ret;
}

IRQCHIP_DECLARE(loongson_ls2k0500_eiointc, "loongson,ls2k0500-eiointc", eiointc_of_init);
IRQCHIP_DECLARE(loongson_ls2k2000_eiointc, "loongson,ls2k2000-eiointc", eiointc_of_init);
+9 −4
Original line number Diff line number Diff line
@@ -32,6 +32,10 @@
#define LIOINTC_REG_INTC_EN_STATUS	(LIOINTC_INTC_CHIP_START + 0x04)
#define LIOINTC_REG_INTC_ENABLE	(LIOINTC_INTC_CHIP_START + 0x08)
#define LIOINTC_REG_INTC_DISABLE	(LIOINTC_INTC_CHIP_START + 0x0c)
/*
 * LIOINTC_REG_INTC_POL register is only valid for Loongson-2K series, and
 * Loongson-3 series behave as noops.
 */
#define LIOINTC_REG_INTC_POL	(LIOINTC_INTC_CHIP_START + 0x10)
#define LIOINTC_REG_INTC_EDGE	(LIOINTC_INTC_CHIP_START + 0x14)

@@ -116,19 +120,19 @@ static int liointc_set_type(struct irq_data *data, unsigned int type)
	switch (type) {
	case IRQ_TYPE_LEVEL_HIGH:
		liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false);
		liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true);
		liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false);
		break;
	case IRQ_TYPE_LEVEL_LOW:
		liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false);
		liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false);
		liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true);
		break;
	case IRQ_TYPE_EDGE_RISING:
		liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, true);
		liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true);
		liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false);
		break;
	case IRQ_TYPE_EDGE_FALLING:
		liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, true);
		liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false);
		liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true);
		break;
	default:
		irq_gc_unlock_irqrestore(gc, flags);
@@ -291,6 +295,7 @@ static int liointc_init(phys_addr_t addr, unsigned long size, int revision,
	ct->chip.irq_mask = irq_gc_mask_disable_reg;
	ct->chip.irq_mask_ack = irq_gc_mask_disable_reg;
	ct->chip.irq_set_type = liointc_set_type;
	ct->chip.flags = IRQCHIP_SKIP_SET_WAKE;

	gc->mask_cache = 0;
	priv->gc = gc;
+4 −6
Original line number Diff line number Diff line
@@ -164,7 +164,7 @@ static int pch_pic_domain_translate(struct irq_domain *d,
		if (fwspec->param_count < 2)
			return -EINVAL;

		*hwirq = fwspec->param[0] + priv->ht_vec_base;
		*hwirq = fwspec->param[0];
		*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
	} else {
		if (fwspec->param_count < 1)
@@ -196,7 +196,7 @@ static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,

	parent_fwspec.fwnode = domain->parent->fwnode;
	parent_fwspec.param_count = 1;
	parent_fwspec.param[0] = hwirq;
	parent_fwspec.param[0] = hwirq + priv->ht_vec_base;

	err = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
	if (err)
@@ -401,14 +401,12 @@ static int __init acpi_cascade_irqdomain_init(void)
int __init pch_pic_acpi_init(struct irq_domain *parent,
					struct acpi_madt_bio_pic *acpi_pchpic)
{
	int ret, vec_base;
	int ret;
	struct fwnode_handle *domain_handle;

	if (find_pch_pic(acpi_pchpic->gsi_base) >= 0)
		return 0;

	vec_base = acpi_pchpic->gsi_base - GSI_MIN_PCH_IRQ;

	domain_handle = irq_domain_alloc_fwnode(&acpi_pchpic->address);
	if (!domain_handle) {
		pr_err("Unable to allocate domain handle\n");
@@ -416,7 +414,7 @@ int __init pch_pic_acpi_init(struct irq_domain *parent,
	}

	ret = pch_pic_init(acpi_pchpic->address, acpi_pchpic->size,
				vec_base, parent, domain_handle, acpi_pchpic->gsi_base);
				0, parent, domain_handle, acpi_pchpic->gsi_base);

	if (ret < 0) {
		irq_domain_free_fwnode(domain_handle);