162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
462306a36Sopenharmony_ci *  Ingenic XBurst platform IRQ support
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/errno.h>
862306a36Sopenharmony_ci#include <linux/init.h>
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/ioport.h>
1262306a36Sopenharmony_ci#include <linux/irqchip.h>
1362306a36Sopenharmony_ci#include <linux/of_address.h>
1462306a36Sopenharmony_ci#include <linux/of_irq.h>
1562306a36Sopenharmony_ci#include <linux/timex.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/delay.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <asm/io.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistruct ingenic_intc_data {
2262306a36Sopenharmony_ci	void __iomem *base;
2362306a36Sopenharmony_ci	struct irq_domain *domain;
2462306a36Sopenharmony_ci	unsigned num_chips;
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define JZ_REG_INTC_STATUS	0x00
2862306a36Sopenharmony_ci#define JZ_REG_INTC_MASK	0x04
2962306a36Sopenharmony_ci#define JZ_REG_INTC_SET_MASK	0x08
3062306a36Sopenharmony_ci#define JZ_REG_INTC_CLEAR_MASK	0x0c
3162306a36Sopenharmony_ci#define JZ_REG_INTC_PENDING	0x10
3262306a36Sopenharmony_ci#define CHIP_SIZE		0x20
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic irqreturn_t intc_cascade(int irq, void *data)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct ingenic_intc_data *intc = irq_get_handler_data(irq);
3762306a36Sopenharmony_ci	struct irq_domain *domain = intc->domain;
3862306a36Sopenharmony_ci	struct irq_chip_generic *gc;
3962306a36Sopenharmony_ci	uint32_t pending;
4062306a36Sopenharmony_ci	unsigned i;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	for (i = 0; i < intc->num_chips; i++) {
4362306a36Sopenharmony_ci		gc = irq_get_domain_generic_chip(domain, i * 32);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci		pending = irq_reg_readl(gc, JZ_REG_INTC_PENDING);
4662306a36Sopenharmony_ci		if (!pending)
4762306a36Sopenharmony_ci			continue;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci		while (pending) {
5062306a36Sopenharmony_ci			int bit = __fls(pending);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci			generic_handle_domain_irq(domain, bit + (i * 32));
5362306a36Sopenharmony_ci			pending &= ~BIT(bit);
5462306a36Sopenharmony_ci		}
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	return IRQ_HANDLED;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic int __init ingenic_intc_of_init(struct device_node *node,
6162306a36Sopenharmony_ci				       unsigned num_chips)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct ingenic_intc_data *intc;
6462306a36Sopenharmony_ci	struct irq_chip_generic *gc;
6562306a36Sopenharmony_ci	struct irq_chip_type *ct;
6662306a36Sopenharmony_ci	struct irq_domain *domain;
6762306a36Sopenharmony_ci	int parent_irq, err = 0;
6862306a36Sopenharmony_ci	unsigned i;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	intc = kzalloc(sizeof(*intc), GFP_KERNEL);
7162306a36Sopenharmony_ci	if (!intc) {
7262306a36Sopenharmony_ci		err = -ENOMEM;
7362306a36Sopenharmony_ci		goto out_err;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	parent_irq = irq_of_parse_and_map(node, 0);
7762306a36Sopenharmony_ci	if (!parent_irq) {
7862306a36Sopenharmony_ci		err = -EINVAL;
7962306a36Sopenharmony_ci		goto out_free;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	err = irq_set_handler_data(parent_irq, intc);
8362306a36Sopenharmony_ci	if (err)
8462306a36Sopenharmony_ci		goto out_unmap_irq;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	intc->num_chips = num_chips;
8762306a36Sopenharmony_ci	intc->base = of_iomap(node, 0);
8862306a36Sopenharmony_ci	if (!intc->base) {
8962306a36Sopenharmony_ci		err = -ENODEV;
9062306a36Sopenharmony_ci		goto out_unmap_irq;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	domain = irq_domain_add_linear(node, num_chips * 32,
9462306a36Sopenharmony_ci				       &irq_generic_chip_ops, NULL);
9562306a36Sopenharmony_ci	if (!domain) {
9662306a36Sopenharmony_ci		err = -ENOMEM;
9762306a36Sopenharmony_ci		goto out_unmap_base;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	intc->domain = domain;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	err = irq_alloc_domain_generic_chips(domain, 32, 1, "INTC",
10362306a36Sopenharmony_ci					     handle_level_irq, 0,
10462306a36Sopenharmony_ci					     IRQ_NOPROBE | IRQ_LEVEL, 0);
10562306a36Sopenharmony_ci	if (err)
10662306a36Sopenharmony_ci		goto out_domain_remove;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	for (i = 0; i < num_chips; i++) {
10962306a36Sopenharmony_ci		gc = irq_get_domain_generic_chip(domain, i * 32);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		gc->wake_enabled = IRQ_MSK(32);
11262306a36Sopenharmony_ci		gc->reg_base = intc->base + (i * CHIP_SIZE);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		ct = gc->chip_types;
11562306a36Sopenharmony_ci		ct->regs.enable = JZ_REG_INTC_CLEAR_MASK;
11662306a36Sopenharmony_ci		ct->regs.disable = JZ_REG_INTC_SET_MASK;
11762306a36Sopenharmony_ci		ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
11862306a36Sopenharmony_ci		ct->chip.irq_mask = irq_gc_mask_disable_reg;
11962306a36Sopenharmony_ci		ct->chip.irq_mask_ack = irq_gc_mask_disable_reg;
12062306a36Sopenharmony_ci		ct->chip.irq_set_wake = irq_gc_set_wake;
12162306a36Sopenharmony_ci		ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		/* Mask all irqs */
12462306a36Sopenharmony_ci		irq_reg_writel(gc, IRQ_MSK(32), JZ_REG_INTC_SET_MASK);
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (request_irq(parent_irq, intc_cascade, IRQF_NO_SUSPEND,
12862306a36Sopenharmony_ci			"SoC intc cascade interrupt", NULL))
12962306a36Sopenharmony_ci		pr_err("Failed to register SoC intc cascade interrupt\n");
13062306a36Sopenharmony_ci	return 0;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ciout_domain_remove:
13362306a36Sopenharmony_ci	irq_domain_remove(domain);
13462306a36Sopenharmony_ciout_unmap_base:
13562306a36Sopenharmony_ci	iounmap(intc->base);
13662306a36Sopenharmony_ciout_unmap_irq:
13762306a36Sopenharmony_ci	irq_dispose_mapping(parent_irq);
13862306a36Sopenharmony_ciout_free:
13962306a36Sopenharmony_ci	kfree(intc);
14062306a36Sopenharmony_ciout_err:
14162306a36Sopenharmony_ci	return err;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic int __init intc_1chip_of_init(struct device_node *node,
14562306a36Sopenharmony_ci				     struct device_node *parent)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	return ingenic_intc_of_init(node, 1);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ciIRQCHIP_DECLARE(jz4740_intc, "ingenic,jz4740-intc", intc_1chip_of_init);
15062306a36Sopenharmony_ciIRQCHIP_DECLARE(jz4725b_intc, "ingenic,jz4725b-intc", intc_1chip_of_init);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic int __init intc_2chip_of_init(struct device_node *node,
15362306a36Sopenharmony_ci	struct device_node *parent)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	return ingenic_intc_of_init(node, 2);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ciIRQCHIP_DECLARE(jz4760_intc, "ingenic,jz4760-intc", intc_2chip_of_init);
15862306a36Sopenharmony_ciIRQCHIP_DECLARE(jz4770_intc, "ingenic,jz4770-intc", intc_2chip_of_init);
15962306a36Sopenharmony_ciIRQCHIP_DECLARE(jz4775_intc, "ingenic,jz4775-intc", intc_2chip_of_init);
16062306a36Sopenharmony_ciIRQCHIP_DECLARE(jz4780_intc, "ingenic,jz4780-intc", intc_2chip_of_init);
161