[PATCH 6/9] irqchip/loongson-liointc: Add ACPI init support

From: Huacai Chen
Date: Mon Jul 05 2021 - 23:11:30 EST


We are preparing to add new Loongson (based on LoongArch, not MIPS)
support. LoongArch use ACPI other than DT as its boot protocol, so
add ACPI init support.

Signed-off-by: Huacai Chen <chenhuacai@xxxxxxxxxxx>
---
drivers/irqchip/irq-loongson-liointc.c | 140 +++++++++++++++++++++++--
1 file changed, 132 insertions(+), 8 deletions(-)

diff --git a/drivers/irqchip/irq-loongson-liointc.c b/drivers/irqchip/irq-loongson-liointc.c
index 649c58391618..591fc1192e73 100644
--- a/drivers/irqchip/irq-loongson-liointc.c
+++ b/drivers/irqchip/irq-loongson-liointc.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@xxxxxxxxxxx>
+ * Jianmin Lv <lvjianmin@xxxxxxxxxxx>
+ * Huacai Chen <chenhuacai@xxxxxxxxxxx>
* Loongson Local IO Interrupt Controller support
*/

@@ -19,7 +21,7 @@
#include <loongson.h>

#define LIOINTC_CHIP_IRQ 32
-#define LIOINTC_NUM_PARENT 4
+#define LIOINTC_NUM_PARENT 4
#define LIOINTC_NUM_CORES 4

#define LIOINTC_INTC_CHIP_START 0x20
@@ -53,7 +55,7 @@ static void liointc_chained_handle_irq(struct irq_desc *desc)
struct liointc_handler_data *handler = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
struct irq_chip_generic *gc = handler->priv->gc;
- int core = get_ebase_cpunum() % LIOINTC_NUM_CORES;
+ int core = cpu_logical_map(smp_processor_id()) % LIOINTC_NUM_CORES;
u32 pending;

chained_irq_enter(chip, desc);
@@ -143,8 +145,12 @@ static void liointc_resume(struct irq_chip_generic *gc)
irq_gc_unlock_irqrestore(gc, flags);
}

-static const char * const parent_names[] = {"int0", "int1", "int2", "int3"};
-static const char * const core_reg_names[] = {"isr0", "isr1", "isr2", "isr3"};
+static int parent_irq[LIOINTC_NUM_PARENT];
+static u32 parent_int_map[LIOINTC_NUM_PARENT];
+static const char *const parent_names[] = {"int0", "int1", "int2", "int3"};
+static const char *const core_reg_names[] = {"isr0", "isr1", "isr2", "isr3"};
+
+#ifdef CONFIG_OF

static void __iomem *liointc_get_reg_byname(struct device_node *node,
const char *name)
@@ -165,8 +171,6 @@ static int __init liointc_of_init(struct device_node *node,
struct irq_chip_type *ct;
struct liointc_priv *priv;
void __iomem *base;
- u32 of_parent_int_map[LIOINTC_NUM_PARENT];
- int parent_irq[LIOINTC_NUM_PARENT];
bool have_parent = FALSE;
int sz, i, err = 0;

@@ -210,7 +214,7 @@ static int __init liointc_of_init(struct device_node *node,

sz = of_property_read_variable_u32_array(node,
"loongson,parent_int_map",
- &of_parent_int_map[0],
+ &parent_int_map[0],
LIOINTC_NUM_PARENT,
LIOINTC_NUM_PARENT);
if (sz < 4) {
@@ -220,7 +224,7 @@ static int __init liointc_of_init(struct device_node *node,
}

for (i = 0; i < LIOINTC_NUM_PARENT; i++)
- priv->handler[i].parent_int_map = of_parent_int_map[i];
+ priv->handler[i].parent_int_map = parent_int_map[i];

/* Setup IRQ domain */
domain = irq_domain_add_linear(node, 32,
@@ -310,3 +314,123 @@ static int __init liointc_of_init(struct device_node *node,
IRQCHIP_DECLARE(loongson_liointc_1_0, "loongson,liointc-1.0", liointc_of_init);
IRQCHIP_DECLARE(loongson_liointc_1_0a, "loongson,liointc-1.0a", liointc_of_init);
IRQCHIP_DECLARE(loongson_liointc_2_0, "loongson,liointc-2.0", liointc_of_init);
+
+#endif
+
+#ifdef CONFIG_ACPI
+
+struct fwnode_handle *liointc_acpi_init(struct acpi_madt_lio_pic *acpi_liointc)
+{
+ int i, err;
+ void __iomem *base;
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+ struct irq_domain *domain;
+ struct liointc_priv *priv;
+ struct fwnode_handle *domain_handle;
+
+ parent_int_map[0] = acpi_liointc->cascade_map[0];
+ parent_int_map[1] = acpi_liointc->cascade_map[1];
+
+ parent_irq[0] = LOONGSON_CPU_IRQ_BASE + acpi_liointc->cascade[0];
+ if (!cpu_has_extioi)
+ parent_irq[1] = LOONGSON_CPU_IRQ_BASE + acpi_liointc->cascade[1];
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return NULL;
+
+ base = ioremap(acpi_liointc->address, acpi_liointc->size);
+ if (!base)
+ goto out_free_priv;
+
+ for (i = 0; i < LIOINTC_NUM_CORES; i++)
+ priv->core_isr[i] = base + LIOINTC_REG_INTC_STATUS;
+
+ domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)priv);
+ if (!domain_handle) {
+ pr_err("Unable to allocate domain handle\n");
+ return NULL;
+ }
+
+ for (i = 0; i < LIOINTC_NUM_PARENT; i++)
+ priv->handler[i].parent_int_map = parent_int_map[i];
+
+ /* Setup IRQ domain */
+ domain = irq_domain_create_linear(domain_handle, LIOINTC_CHIP_IRQ,
+ &irq_generic_chip_ops, priv);
+ if (!domain) {
+ pr_err("loongson-liointc: cannot add IRQ domain\n");
+ goto out_iounmap;
+ }
+
+ err = irq_alloc_domain_generic_chips(domain, LIOINTC_CHIP_IRQ, 1,
+ "LIOINTC", handle_level_irq,
+ 0, IRQ_NOPROBE, 0);
+ if (err) {
+ pr_err("loongson-liointc: unable to register IRQ domain\n");
+ goto out_free_domain;
+ }
+
+
+ /* Disable all IRQs */
+ writel(0xffffffff, base + LIOINTC_REG_INTC_DISABLE);
+ /* Set to level triggered */
+ writel(0x0, base + LIOINTC_REG_INTC_EDGE);
+
+ /* Generate parent INT part of map cache */
+ for (i = 0; i < LIOINTC_NUM_PARENT; i++) {
+ u32 pending = priv->handler[i].parent_int_map;
+
+ while (pending) {
+ int bit = __ffs(pending);
+
+ priv->map_cache[bit] = BIT(i) << LIOINTC_SHIFT_INTx;
+ pending &= ~BIT(bit);
+ }
+ }
+
+ for (i = 0; i < LIOINTC_CHIP_IRQ; i++) {
+ /* Generate core part of map cache */
+ priv->map_cache[i] |= BIT(loongson_sysconf.boot_cpu_id);
+ writeb(priv->map_cache[i], base + i);
+ }
+
+ gc = irq_get_domain_generic_chip(domain, 0);
+ gc->private = priv;
+ gc->reg_base = base;
+ gc->domain = domain;
+ gc->resume = liointc_resume;
+
+ ct = gc->chip_types;
+ ct->regs.enable = LIOINTC_REG_INTC_ENABLE;
+ ct->regs.disable = LIOINTC_REG_INTC_DISABLE;
+ ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
+ ct->chip.irq_mask = irq_gc_mask_disable_reg;
+ ct->chip.irq_mask_ack = irq_gc_mask_disable_reg;
+
+ gc->mask_cache = 0;
+ priv->gc = gc;
+
+ for (i = 0; i < LIOINTC_NUM_PARENT; i++) {
+ if (parent_irq[i] <= 0)
+ continue;
+
+ priv->handler[i].priv = priv;
+ irq_set_chained_handler_and_data(parent_irq[i],
+ liointc_chained_handle_irq, &priv->handler[i]);
+ }
+
+ return domain_handle;
+
+out_free_domain:
+ irq_domain_remove(domain);
+out_iounmap:
+ iounmap(base);
+out_free_priv:
+ kfree(priv);
+
+ return NULL;
+}
+
+#endif
--
2.27.0