18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR MIT)
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Microsemi Ocelot IRQ controller driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2017 Microsemi Corporation
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/bitops.h>
88c2ecf20Sopenharmony_ci#include <linux/irq.h>
98c2ecf20Sopenharmony_ci#include <linux/of_address.h>
108c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
118c2ecf20Sopenharmony_ci#include <linux/irqchip.h>
128c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define ICPU_CFG_INTR_INTR_STICKY	0x10
168c2ecf20Sopenharmony_ci#define ICPU_CFG_INTR_INTR_ENA		0x18
178c2ecf20Sopenharmony_ci#define ICPU_CFG_INTR_INTR_ENA_CLR	0x1c
188c2ecf20Sopenharmony_ci#define ICPU_CFG_INTR_INTR_ENA_SET	0x20
198c2ecf20Sopenharmony_ci#define ICPU_CFG_INTR_DST_INTR_IDENT(x)	(0x38 + 0x4 * (x))
208c2ecf20Sopenharmony_ci#define ICPU_CFG_INTR_INTR_TRIGGER(x)	(0x5c + 0x4 * (x))
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define OCELOT_NR_IRQ 24
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic void ocelot_irq_unmask(struct irq_data *data)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
278c2ecf20Sopenharmony_ci	struct irq_chip_type *ct = irq_data_get_chip_type(data);
288c2ecf20Sopenharmony_ci	unsigned int mask = data->mask;
298c2ecf20Sopenharmony_ci	u32 val;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	irq_gc_lock(gc);
328c2ecf20Sopenharmony_ci	val = irq_reg_readl(gc, ICPU_CFG_INTR_INTR_TRIGGER(0)) |
338c2ecf20Sopenharmony_ci	      irq_reg_readl(gc, ICPU_CFG_INTR_INTR_TRIGGER(1));
348c2ecf20Sopenharmony_ci	if (!(val & mask))
358c2ecf20Sopenharmony_ci		irq_reg_writel(gc, mask, ICPU_CFG_INTR_INTR_STICKY);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	*ct->mask_cache &= ~mask;
388c2ecf20Sopenharmony_ci	irq_reg_writel(gc, mask, ICPU_CFG_INTR_INTR_ENA_SET);
398c2ecf20Sopenharmony_ci	irq_gc_unlock(gc);
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic void ocelot_irq_handler(struct irq_desc *desc)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
458c2ecf20Sopenharmony_ci	struct irq_domain *d = irq_desc_get_handler_data(desc);
468c2ecf20Sopenharmony_ci	struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, 0);
478c2ecf20Sopenharmony_ci	u32 reg = irq_reg_readl(gc, ICPU_CFG_INTR_DST_INTR_IDENT(0));
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	chained_irq_enter(chip, desc);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	while (reg) {
528c2ecf20Sopenharmony_ci		u32 hwirq = __fls(reg);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci		generic_handle_irq(irq_find_mapping(d, hwirq));
558c2ecf20Sopenharmony_ci		reg &= ~(BIT(hwirq));
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	chained_irq_exit(chip, desc);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic int __init ocelot_irq_init(struct device_node *node,
628c2ecf20Sopenharmony_ci				  struct device_node *parent)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	struct irq_domain *domain;
658c2ecf20Sopenharmony_ci	struct irq_chip_generic *gc;
668c2ecf20Sopenharmony_ci	int parent_irq, ret;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	parent_irq = irq_of_parse_and_map(node, 0);
698c2ecf20Sopenharmony_ci	if (!parent_irq)
708c2ecf20Sopenharmony_ci		return -EINVAL;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	domain = irq_domain_add_linear(node, OCELOT_NR_IRQ,
738c2ecf20Sopenharmony_ci				       &irq_generic_chip_ops, NULL);
748c2ecf20Sopenharmony_ci	if (!domain) {
758c2ecf20Sopenharmony_ci		pr_err("%pOFn: unable to add irq domain\n", node);
768c2ecf20Sopenharmony_ci		return -ENOMEM;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	ret = irq_alloc_domain_generic_chips(domain, OCELOT_NR_IRQ, 1,
808c2ecf20Sopenharmony_ci					     "icpu", handle_level_irq,
818c2ecf20Sopenharmony_ci					     0, 0, 0);
828c2ecf20Sopenharmony_ci	if (ret) {
838c2ecf20Sopenharmony_ci		pr_err("%pOFn: unable to alloc irq domain gc\n", node);
848c2ecf20Sopenharmony_ci		goto err_domain_remove;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	gc = irq_get_domain_generic_chip(domain, 0);
888c2ecf20Sopenharmony_ci	gc->reg_base = of_iomap(node, 0);
898c2ecf20Sopenharmony_ci	if (!gc->reg_base) {
908c2ecf20Sopenharmony_ci		pr_err("%pOFn: unable to map resource\n", node);
918c2ecf20Sopenharmony_ci		ret = -ENOMEM;
928c2ecf20Sopenharmony_ci		goto err_gc_free;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	gc->chip_types[0].regs.ack = ICPU_CFG_INTR_INTR_STICKY;
968c2ecf20Sopenharmony_ci	gc->chip_types[0].regs.mask = ICPU_CFG_INTR_INTR_ENA_CLR;
978c2ecf20Sopenharmony_ci	gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
988c2ecf20Sopenharmony_ci	gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
998c2ecf20Sopenharmony_ci	gc->chip_types[0].chip.irq_unmask = ocelot_irq_unmask;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/* Mask and ack all interrupts */
1028c2ecf20Sopenharmony_ci	irq_reg_writel(gc, 0, ICPU_CFG_INTR_INTR_ENA);
1038c2ecf20Sopenharmony_ci	irq_reg_writel(gc, 0xffffffff, ICPU_CFG_INTR_INTR_STICKY);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	irq_set_chained_handler_and_data(parent_irq, ocelot_irq_handler,
1068c2ecf20Sopenharmony_ci					 domain);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return 0;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cierr_gc_free:
1118c2ecf20Sopenharmony_ci	irq_free_generic_chip(gc);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cierr_domain_remove:
1148c2ecf20Sopenharmony_ci	irq_domain_remove(domain);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	return ret;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(ocelot_icpu, "mscc,ocelot-icpu-intr", ocelot_irq_init);
119