xref: /linux/drivers/irqchip/irq-ath79-misc.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
207ba4b06SAlban Bedel /*
307ba4b06SAlban Bedel  *  Atheros AR71xx/AR724x/AR913x MISC interrupt controller
407ba4b06SAlban Bedel  *
507ba4b06SAlban Bedel  *  Copyright (C) 2015 Alban Bedel <albeu@free.fr>
607ba4b06SAlban Bedel  *  Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
707ba4b06SAlban Bedel  *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
807ba4b06SAlban Bedel  *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
907ba4b06SAlban Bedel  *
1007ba4b06SAlban Bedel  *  Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
1107ba4b06SAlban Bedel  */
1207ba4b06SAlban Bedel 
1307ba4b06SAlban Bedel #include <linux/irqchip.h>
1407ba4b06SAlban Bedel #include <linux/irqchip/chained_irq.h>
1507ba4b06SAlban Bedel #include <linux/of_address.h>
1607ba4b06SAlban Bedel #include <linux/of_irq.h>
1707ba4b06SAlban Bedel 
1807ba4b06SAlban Bedel #define AR71XX_RESET_REG_MISC_INT_STATUS	0
1907ba4b06SAlban Bedel #define AR71XX_RESET_REG_MISC_INT_ENABLE	4
2007ba4b06SAlban Bedel 
2107ba4b06SAlban Bedel #define ATH79_MISC_IRQ_COUNT			32
22a1e8783dSPetr Štetiar #define ATH79_MISC_PERF_IRQ			5
23a1e8783dSPetr Štetiar 
24a1e8783dSPetr Štetiar static int ath79_perfcount_irq;
25a1e8783dSPetr Štetiar 
26a1e8783dSPetr Štetiar int get_c0_perfcount_int(void)
27a1e8783dSPetr Štetiar {
28a1e8783dSPetr Štetiar 	return ath79_perfcount_irq;
29a1e8783dSPetr Štetiar }
30a1e8783dSPetr Štetiar EXPORT_SYMBOL_GPL(get_c0_perfcount_int);
3107ba4b06SAlban Bedel 
3207ba4b06SAlban Bedel static void ath79_misc_irq_handler(struct irq_desc *desc)
3307ba4b06SAlban Bedel {
3407ba4b06SAlban Bedel 	struct irq_domain *domain = irq_desc_get_handler_data(desc);
3507ba4b06SAlban Bedel 	struct irq_chip *chip = irq_desc_get_chip(desc);
3607ba4b06SAlban Bedel 	void __iomem *base = domain->host_data;
3707ba4b06SAlban Bedel 	u32 pending;
3807ba4b06SAlban Bedel 
3907ba4b06SAlban Bedel 	chained_irq_enter(chip, desc);
4007ba4b06SAlban Bedel 
4107ba4b06SAlban Bedel 	pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) &
4207ba4b06SAlban Bedel 		  __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
4307ba4b06SAlban Bedel 
4407ba4b06SAlban Bedel 	if (!pending) {
4507ba4b06SAlban Bedel 		spurious_interrupt();
4607ba4b06SAlban Bedel 		chained_irq_exit(chip, desc);
4707ba4b06SAlban Bedel 		return;
4807ba4b06SAlban Bedel 	}
4907ba4b06SAlban Bedel 
5007ba4b06SAlban Bedel 	while (pending) {
5107ba4b06SAlban Bedel 		int bit = __ffs(pending);
5207ba4b06SAlban Bedel 
5307ba4b06SAlban Bedel 		generic_handle_irq(irq_linear_revmap(domain, bit));
5407ba4b06SAlban Bedel 		pending &= ~BIT(bit);
5507ba4b06SAlban Bedel 	}
5607ba4b06SAlban Bedel 
5707ba4b06SAlban Bedel 	chained_irq_exit(chip, desc);
5807ba4b06SAlban Bedel }
5907ba4b06SAlban Bedel 
6007ba4b06SAlban Bedel static void ar71xx_misc_irq_unmask(struct irq_data *d)
6107ba4b06SAlban Bedel {
6207ba4b06SAlban Bedel 	void __iomem *base = irq_data_get_irq_chip_data(d);
6307ba4b06SAlban Bedel 	unsigned int irq = d->hwirq;
6407ba4b06SAlban Bedel 	u32 t;
6507ba4b06SAlban Bedel 
6607ba4b06SAlban Bedel 	t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
6707ba4b06SAlban Bedel 	__raw_writel(t | BIT(irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
6807ba4b06SAlban Bedel 
6907ba4b06SAlban Bedel 	/* flush write */
7007ba4b06SAlban Bedel 	__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
7107ba4b06SAlban Bedel }
7207ba4b06SAlban Bedel 
7307ba4b06SAlban Bedel static void ar71xx_misc_irq_mask(struct irq_data *d)
7407ba4b06SAlban Bedel {
7507ba4b06SAlban Bedel 	void __iomem *base = irq_data_get_irq_chip_data(d);
7607ba4b06SAlban Bedel 	unsigned int irq = d->hwirq;
7707ba4b06SAlban Bedel 	u32 t;
7807ba4b06SAlban Bedel 
7907ba4b06SAlban Bedel 	t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
8007ba4b06SAlban Bedel 	__raw_writel(t & ~BIT(irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
8107ba4b06SAlban Bedel 
8207ba4b06SAlban Bedel 	/* flush write */
8307ba4b06SAlban Bedel 	__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
8407ba4b06SAlban Bedel }
8507ba4b06SAlban Bedel 
8607ba4b06SAlban Bedel static void ar724x_misc_irq_ack(struct irq_data *d)
8707ba4b06SAlban Bedel {
8807ba4b06SAlban Bedel 	void __iomem *base = irq_data_get_irq_chip_data(d);
8907ba4b06SAlban Bedel 	unsigned int irq = d->hwirq;
9007ba4b06SAlban Bedel 	u32 t;
9107ba4b06SAlban Bedel 
9207ba4b06SAlban Bedel 	t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
9307ba4b06SAlban Bedel 	__raw_writel(t & ~BIT(irq), base + AR71XX_RESET_REG_MISC_INT_STATUS);
9407ba4b06SAlban Bedel 
9507ba4b06SAlban Bedel 	/* flush write */
9607ba4b06SAlban Bedel 	__raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
9707ba4b06SAlban Bedel }
9807ba4b06SAlban Bedel 
9907ba4b06SAlban Bedel static struct irq_chip ath79_misc_irq_chip = {
10007ba4b06SAlban Bedel 	.name		= "MISC",
10107ba4b06SAlban Bedel 	.irq_unmask	= ar71xx_misc_irq_unmask,
10207ba4b06SAlban Bedel 	.irq_mask	= ar71xx_misc_irq_mask,
10307ba4b06SAlban Bedel };
10407ba4b06SAlban Bedel 
10507ba4b06SAlban Bedel static int misc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
10607ba4b06SAlban Bedel {
10707ba4b06SAlban Bedel 	irq_set_chip_and_handler(irq, &ath79_misc_irq_chip, handle_level_irq);
10807ba4b06SAlban Bedel 	irq_set_chip_data(irq, d->host_data);
10907ba4b06SAlban Bedel 	return 0;
11007ba4b06SAlban Bedel }
11107ba4b06SAlban Bedel 
11207ba4b06SAlban Bedel static const struct irq_domain_ops misc_irq_domain_ops = {
11307ba4b06SAlban Bedel 	.xlate = irq_domain_xlate_onecell,
11407ba4b06SAlban Bedel 	.map = misc_map,
11507ba4b06SAlban Bedel };
11607ba4b06SAlban Bedel 
11707ba4b06SAlban Bedel static void __init ath79_misc_intc_domain_init(
11807ba4b06SAlban Bedel 	struct irq_domain *domain, int irq)
11907ba4b06SAlban Bedel {
12007ba4b06SAlban Bedel 	void __iomem *base = domain->host_data;
12107ba4b06SAlban Bedel 
122a1e8783dSPetr Štetiar 	ath79_perfcount_irq = irq_create_mapping(domain, ATH79_MISC_PERF_IRQ);
123a1e8783dSPetr Štetiar 
12407ba4b06SAlban Bedel 	/* Disable and clear all interrupts */
12507ba4b06SAlban Bedel 	__raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_ENABLE);
12607ba4b06SAlban Bedel 	__raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_STATUS);
12707ba4b06SAlban Bedel 
12807ba4b06SAlban Bedel 	irq_set_chained_handler_and_data(irq, ath79_misc_irq_handler, domain);
12907ba4b06SAlban Bedel }
13007ba4b06SAlban Bedel 
13107ba4b06SAlban Bedel static int __init ath79_misc_intc_of_init(
13207ba4b06SAlban Bedel 	struct device_node *node, struct device_node *parent)
13307ba4b06SAlban Bedel {
13407ba4b06SAlban Bedel 	struct irq_domain *domain;
13507ba4b06SAlban Bedel 	void __iomem *base;
13607ba4b06SAlban Bedel 	int irq;
13707ba4b06SAlban Bedel 
13807ba4b06SAlban Bedel 	irq = irq_of_parse_and_map(node, 0);
13907ba4b06SAlban Bedel 	if (!irq) {
14007ba4b06SAlban Bedel 		pr_err("Failed to get MISC IRQ\n");
14107ba4b06SAlban Bedel 		return -EINVAL;
14207ba4b06SAlban Bedel 	}
14307ba4b06SAlban Bedel 
14407ba4b06SAlban Bedel 	base = of_iomap(node, 0);
14507ba4b06SAlban Bedel 	if (!base) {
14607ba4b06SAlban Bedel 		pr_err("Failed to get MISC IRQ registers\n");
14707ba4b06SAlban Bedel 		return -ENOMEM;
14807ba4b06SAlban Bedel 	}
14907ba4b06SAlban Bedel 
15007ba4b06SAlban Bedel 	domain = irq_domain_add_linear(node, ATH79_MISC_IRQ_COUNT,
15107ba4b06SAlban Bedel 				&misc_irq_domain_ops, base);
15207ba4b06SAlban Bedel 	if (!domain) {
15307ba4b06SAlban Bedel 		pr_err("Failed to add MISC irqdomain\n");
15407ba4b06SAlban Bedel 		return -EINVAL;
15507ba4b06SAlban Bedel 	}
15607ba4b06SAlban Bedel 
15707ba4b06SAlban Bedel 	ath79_misc_intc_domain_init(domain, irq);
15807ba4b06SAlban Bedel 	return 0;
15907ba4b06SAlban Bedel }
16007ba4b06SAlban Bedel 
16107ba4b06SAlban Bedel static int __init ar7100_misc_intc_of_init(
16207ba4b06SAlban Bedel 	struct device_node *node, struct device_node *parent)
16307ba4b06SAlban Bedel {
16407ba4b06SAlban Bedel 	ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask;
16507ba4b06SAlban Bedel 	return ath79_misc_intc_of_init(node, parent);
16607ba4b06SAlban Bedel }
16707ba4b06SAlban Bedel 
16807ba4b06SAlban Bedel IRQCHIP_DECLARE(ar7100_misc_intc, "qca,ar7100-misc-intc",
16907ba4b06SAlban Bedel 		ar7100_misc_intc_of_init);
17007ba4b06SAlban Bedel 
17107ba4b06SAlban Bedel static int __init ar7240_misc_intc_of_init(
17207ba4b06SAlban Bedel 	struct device_node *node, struct device_node *parent)
17307ba4b06SAlban Bedel {
17407ba4b06SAlban Bedel 	ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack;
17507ba4b06SAlban Bedel 	return ath79_misc_intc_of_init(node, parent);
17607ba4b06SAlban Bedel }
17707ba4b06SAlban Bedel 
17807ba4b06SAlban Bedel IRQCHIP_DECLARE(ar7240_misc_intc, "qca,ar7240-misc-intc",
17907ba4b06SAlban Bedel 		ar7240_misc_intc_of_init);
18007ba4b06SAlban Bedel 
18107ba4b06SAlban Bedel void __init ath79_misc_irq_init(void __iomem *regs, int irq,
18207ba4b06SAlban Bedel 				int irq_base, bool is_ar71xx)
18307ba4b06SAlban Bedel {
18407ba4b06SAlban Bedel 	struct irq_domain *domain;
18507ba4b06SAlban Bedel 
18607ba4b06SAlban Bedel 	if (is_ar71xx)
18707ba4b06SAlban Bedel 		ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask;
18807ba4b06SAlban Bedel 	else
18907ba4b06SAlban Bedel 		ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack;
19007ba4b06SAlban Bedel 
19107ba4b06SAlban Bedel 	domain = irq_domain_add_legacy(NULL, ATH79_MISC_IRQ_COUNT,
19207ba4b06SAlban Bedel 			irq_base, 0, &misc_irq_domain_ops, regs);
19307ba4b06SAlban Bedel 	if (!domain)
19407ba4b06SAlban Bedel 		panic("Failed to create MISC irqdomain");
19507ba4b06SAlban Bedel 
19607ba4b06SAlban Bedel 	ath79_misc_intc_domain_init(domain, irq);
19707ba4b06SAlban Bedel }
198