18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
78c2ecf20Sopenharmony_ci#include <linux/irq.h>
88c2ecf20Sopenharmony_ci#include <asm/irq_cpu.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <loongson1.h>
118c2ecf20Sopenharmony_ci#include <irq.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define LS1X_INTC_REG(n, x) \
148c2ecf20Sopenharmony_ci		((void __iomem *)KSEG1ADDR(LS1X_INTC_BASE + (n * 0x18) + (x)))
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define LS1X_INTC_INTISR(n)		LS1X_INTC_REG(n, 0x0)
178c2ecf20Sopenharmony_ci#define LS1X_INTC_INTIEN(n)		LS1X_INTC_REG(n, 0x4)
188c2ecf20Sopenharmony_ci#define LS1X_INTC_INTSET(n)		LS1X_INTC_REG(n, 0x8)
198c2ecf20Sopenharmony_ci#define LS1X_INTC_INTCLR(n)		LS1X_INTC_REG(n, 0xc)
208c2ecf20Sopenharmony_ci#define LS1X_INTC_INTPOL(n)		LS1X_INTC_REG(n, 0x10)
218c2ecf20Sopenharmony_ci#define LS1X_INTC_INTEDGE(n)		LS1X_INTC_REG(n, 0x14)
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic void ls1x_irq_ack(struct irq_data *d)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
268c2ecf20Sopenharmony_ci	unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	__raw_writel(__raw_readl(LS1X_INTC_INTCLR(n))
298c2ecf20Sopenharmony_ci			| (1 << bit), LS1X_INTC_INTCLR(n));
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic void ls1x_irq_mask(struct irq_data *d)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
358c2ecf20Sopenharmony_ci	unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	__raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
388c2ecf20Sopenharmony_ci			& ~(1 << bit), LS1X_INTC_INTIEN(n));
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void ls1x_irq_mask_ack(struct irq_data *d)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
448c2ecf20Sopenharmony_ci	unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	__raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
478c2ecf20Sopenharmony_ci			& ~(1 << bit), LS1X_INTC_INTIEN(n));
488c2ecf20Sopenharmony_ci	__raw_writel(__raw_readl(LS1X_INTC_INTCLR(n))
498c2ecf20Sopenharmony_ci			| (1 << bit), LS1X_INTC_INTCLR(n));
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic void ls1x_irq_unmask(struct irq_data *d)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
558c2ecf20Sopenharmony_ci	unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	__raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
588c2ecf20Sopenharmony_ci			| (1 << bit), LS1X_INTC_INTIEN(n));
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic int ls1x_irq_settype(struct irq_data *d, unsigned int type)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
648c2ecf20Sopenharmony_ci	unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	switch (type) {
678c2ecf20Sopenharmony_ci	case IRQ_TYPE_LEVEL_HIGH:
688c2ecf20Sopenharmony_ci		__raw_writel(__raw_readl(LS1X_INTC_INTPOL(n))
698c2ecf20Sopenharmony_ci			| (1 << bit), LS1X_INTC_INTPOL(n));
708c2ecf20Sopenharmony_ci		__raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n))
718c2ecf20Sopenharmony_ci			& ~(1 << bit), LS1X_INTC_INTEDGE(n));
728c2ecf20Sopenharmony_ci		break;
738c2ecf20Sopenharmony_ci	case IRQ_TYPE_LEVEL_LOW:
748c2ecf20Sopenharmony_ci		__raw_writel(__raw_readl(LS1X_INTC_INTPOL(n))
758c2ecf20Sopenharmony_ci			& ~(1 << bit), LS1X_INTC_INTPOL(n));
768c2ecf20Sopenharmony_ci		__raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n))
778c2ecf20Sopenharmony_ci			& ~(1 << bit), LS1X_INTC_INTEDGE(n));
788c2ecf20Sopenharmony_ci		break;
798c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_RISING:
808c2ecf20Sopenharmony_ci		__raw_writel(__raw_readl(LS1X_INTC_INTPOL(n))
818c2ecf20Sopenharmony_ci			| (1 << bit), LS1X_INTC_INTPOL(n));
828c2ecf20Sopenharmony_ci		__raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n))
838c2ecf20Sopenharmony_ci			| (1 << bit), LS1X_INTC_INTEDGE(n));
848c2ecf20Sopenharmony_ci		break;
858c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_FALLING:
868c2ecf20Sopenharmony_ci		__raw_writel(__raw_readl(LS1X_INTC_INTPOL(n))
878c2ecf20Sopenharmony_ci			& ~(1 << bit), LS1X_INTC_INTPOL(n));
888c2ecf20Sopenharmony_ci		__raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n))
898c2ecf20Sopenharmony_ci			| (1 << bit), LS1X_INTC_INTEDGE(n));
908c2ecf20Sopenharmony_ci		break;
918c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_BOTH:
928c2ecf20Sopenharmony_ci		__raw_writel(__raw_readl(LS1X_INTC_INTPOL(n))
938c2ecf20Sopenharmony_ci			& ~(1 << bit), LS1X_INTC_INTPOL(n));
948c2ecf20Sopenharmony_ci		__raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n))
958c2ecf20Sopenharmony_ci			| (1 << bit), LS1X_INTC_INTEDGE(n));
968c2ecf20Sopenharmony_ci		break;
978c2ecf20Sopenharmony_ci	case IRQ_TYPE_NONE:
988c2ecf20Sopenharmony_ci		break;
998c2ecf20Sopenharmony_ci	default:
1008c2ecf20Sopenharmony_ci		return -EINVAL;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return 0;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic struct irq_chip ls1x_irq_chip = {
1078c2ecf20Sopenharmony_ci	.name		= "LS1X-INTC",
1088c2ecf20Sopenharmony_ci	.irq_ack	= ls1x_irq_ack,
1098c2ecf20Sopenharmony_ci	.irq_mask	= ls1x_irq_mask,
1108c2ecf20Sopenharmony_ci	.irq_mask_ack	= ls1x_irq_mask_ack,
1118c2ecf20Sopenharmony_ci	.irq_unmask	= ls1x_irq_unmask,
1128c2ecf20Sopenharmony_ci	.irq_set_type   = ls1x_irq_settype,
1138c2ecf20Sopenharmony_ci};
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic void ls1x_irq_dispatch(int n)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	u32 int_status, irq;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/* Get pending sources, masked by current enables */
1208c2ecf20Sopenharmony_ci	int_status = __raw_readl(LS1X_INTC_INTISR(n)) &
1218c2ecf20Sopenharmony_ci			__raw_readl(LS1X_INTC_INTIEN(n));
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (int_status) {
1248c2ecf20Sopenharmony_ci		irq = LS1X_IRQ(n, __ffs(int_status));
1258c2ecf20Sopenharmony_ci		do_IRQ(irq);
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ciasmlinkage void plat_irq_dispatch(void)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	unsigned int pending;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	pending = read_c0_cause() & read_c0_status() & ST0_IM;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (pending & CAUSEF_IP7)
1368c2ecf20Sopenharmony_ci		do_IRQ(TIMER_IRQ);
1378c2ecf20Sopenharmony_ci	else if (pending & CAUSEF_IP2)
1388c2ecf20Sopenharmony_ci		ls1x_irq_dispatch(0); /* INT0 */
1398c2ecf20Sopenharmony_ci	else if (pending & CAUSEF_IP3)
1408c2ecf20Sopenharmony_ci		ls1x_irq_dispatch(1); /* INT1 */
1418c2ecf20Sopenharmony_ci	else if (pending & CAUSEF_IP4)
1428c2ecf20Sopenharmony_ci		ls1x_irq_dispatch(2); /* INT2 */
1438c2ecf20Sopenharmony_ci	else if (pending & CAUSEF_IP5)
1448c2ecf20Sopenharmony_ci		ls1x_irq_dispatch(3); /* INT3 */
1458c2ecf20Sopenharmony_ci	else if (pending & CAUSEF_IP6)
1468c2ecf20Sopenharmony_ci		ls1x_irq_dispatch(4); /* INT4 */
1478c2ecf20Sopenharmony_ci	else
1488c2ecf20Sopenharmony_ci		spurious_interrupt();
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic void __init ls1x_irq_init(int base)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	int n;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	/* Disable interrupts and clear pending,
1578c2ecf20Sopenharmony_ci	 * setup all IRQs as high level triggered
1588c2ecf20Sopenharmony_ci	 */
1598c2ecf20Sopenharmony_ci	for (n = 0; n < INTN; n++) {
1608c2ecf20Sopenharmony_ci		__raw_writel(0x0, LS1X_INTC_INTIEN(n));
1618c2ecf20Sopenharmony_ci		__raw_writel(0xffffffff, LS1X_INTC_INTCLR(n));
1628c2ecf20Sopenharmony_ci		__raw_writel(0xffffffff, LS1X_INTC_INTPOL(n));
1638c2ecf20Sopenharmony_ci		/* set DMA0, DMA1 and DMA2 to edge trigger */
1648c2ecf20Sopenharmony_ci		__raw_writel(n ? 0x0 : 0xe000, LS1X_INTC_INTEDGE(n));
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	for (n = base; n < NR_IRQS; n++) {
1698c2ecf20Sopenharmony_ci		irq_set_chip_and_handler(n, &ls1x_irq_chip,
1708c2ecf20Sopenharmony_ci					 handle_level_irq);
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (request_irq(INT0_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL))
1748c2ecf20Sopenharmony_ci		pr_err("Failed to request irq %d (cascade)\n", INT0_IRQ);
1758c2ecf20Sopenharmony_ci	if (request_irq(INT1_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL))
1768c2ecf20Sopenharmony_ci		pr_err("Failed to request irq %d (cascade)\n", INT1_IRQ);
1778c2ecf20Sopenharmony_ci	if (request_irq(INT2_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL))
1788c2ecf20Sopenharmony_ci		pr_err("Failed to request irq %d (cascade)\n", INT2_IRQ);
1798c2ecf20Sopenharmony_ci	if (request_irq(INT3_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL))
1808c2ecf20Sopenharmony_ci		pr_err("Failed to request irq %d (cascade)\n", INT3_IRQ);
1818c2ecf20Sopenharmony_ci#if defined(CONFIG_LOONGSON1_LS1C)
1828c2ecf20Sopenharmony_ci	if (request_irq(INT4_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL))
1838c2ecf20Sopenharmony_ci		pr_err("Failed to request irq %d (cascade)\n", INT4_IRQ);
1848c2ecf20Sopenharmony_ci#endif
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_civoid __init arch_init_irq(void)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	mips_cpu_irq_init();
1908c2ecf20Sopenharmony_ci	ls1x_irq_init(LS1X_IRQ_BASE);
1918c2ecf20Sopenharmony_ci}
192