162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Atheros AR71xx/AR724x/AR913x specific interrupt handling
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2015 Alban Bedel <albeu@free.fr>
662306a36Sopenharmony_ci *  Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
762306a36Sopenharmony_ci *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
862306a36Sopenharmony_ci *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *  Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/irqchip.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <asm/irq_cpu.h>
1862306a36Sopenharmony_ci#include <asm/mach-ath79/ath79.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/*
2162306a36Sopenharmony_ci * The IP2/IP3 lines are tied to a PCI/WMAC/USB device. Drivers for
2262306a36Sopenharmony_ci * these devices typically allocate coherent DMA memory, however the
2362306a36Sopenharmony_ci * DMA controller may still have some unsynchronized data in the FIFO.
2462306a36Sopenharmony_ci * Issue a flush in the handlers to ensure that the driver sees
2562306a36Sopenharmony_ci * the update.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * This array map the interrupt lines to the DDR write buffer channels.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic unsigned irq_wb_chan[8] = {
3162306a36Sopenharmony_ci	-1, -1, -1, -1, -1, -1, -1, -1,
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ciasmlinkage void plat_irq_dispatch(void)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	unsigned long pending;
3762306a36Sopenharmony_ci	int irq;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	pending = read_c0_status() & read_c0_cause() & ST0_IM;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (!pending) {
4262306a36Sopenharmony_ci		spurious_interrupt();
4362306a36Sopenharmony_ci		return;
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	pending >>= CAUSEB_IP;
4762306a36Sopenharmony_ci	while (pending) {
4862306a36Sopenharmony_ci		irq = fls(pending) - 1;
4962306a36Sopenharmony_ci		if (irq < ARRAY_SIZE(irq_wb_chan) && irq_wb_chan[irq] != -1)
5062306a36Sopenharmony_ci			ath79_ddr_wb_flush(irq_wb_chan[irq]);
5162306a36Sopenharmony_ci		do_IRQ(MIPS_CPU_IRQ_BASE + irq);
5262306a36Sopenharmony_ci		pending &= ~BIT(irq);
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int __init ar79_cpu_intc_of_init(
5762306a36Sopenharmony_ci	struct device_node *node, struct device_node *parent)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	int err, i, count;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* Fill the irq_wb_chan table */
6262306a36Sopenharmony_ci	count = of_count_phandle_with_args(
6362306a36Sopenharmony_ci		node, "qca,ddr-wb-channels", "#qca,ddr-wb-channel-cells");
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
6662306a36Sopenharmony_ci		struct of_phandle_args args;
6762306a36Sopenharmony_ci		u32 irq = i;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci		of_property_read_u32_index(
7062306a36Sopenharmony_ci			node, "qca,ddr-wb-channel-interrupts", i, &irq);
7162306a36Sopenharmony_ci		if (irq >= ARRAY_SIZE(irq_wb_chan))
7262306a36Sopenharmony_ci			continue;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		err = of_parse_phandle_with_args(
7562306a36Sopenharmony_ci			node, "qca,ddr-wb-channels",
7662306a36Sopenharmony_ci			"#qca,ddr-wb-channel-cells",
7762306a36Sopenharmony_ci			i, &args);
7862306a36Sopenharmony_ci		if (err)
7962306a36Sopenharmony_ci			return err;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci		irq_wb_chan[irq] = args.args[0];
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return mips_cpu_irq_of_init(node, parent);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ciIRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc",
8762306a36Sopenharmony_ci		ar79_cpu_intc_of_init);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_civoid __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	irq_wb_chan[2] = irq_wb_chan2;
9262306a36Sopenharmony_ci	irq_wb_chan[3] = irq_wb_chan3;
9362306a36Sopenharmony_ci	mips_cpu_irq_init();
9462306a36Sopenharmony_ci}
95