xref: /kernel/linux/linux-5.10/arch/mips/ar7/irq.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2006,2007 Felix Fietkau <nbd@openwrt.org>
48c2ecf20Sopenharmony_ci * Copyright (C) 2006,2007 Eugene Konev <ejka@openwrt.org>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
88c2ecf20Sopenharmony_ci#include <linux/io.h>
98c2ecf20Sopenharmony_ci#include <linux/irq.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <asm/irq_cpu.h>
128c2ecf20Sopenharmony_ci#include <asm/mipsregs.h>
138c2ecf20Sopenharmony_ci#include <asm/mach-ar7/ar7.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define EXCEPT_OFFSET	0x80
168c2ecf20Sopenharmony_ci#define PACE_OFFSET	0xA0
178c2ecf20Sopenharmony_ci#define CHNLS_OFFSET	0x200
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define REG_OFFSET(irq, reg)	((irq) / 32 * 0x4 + reg * 0x10)
208c2ecf20Sopenharmony_ci#define SEC_REG_OFFSET(reg)	(EXCEPT_OFFSET + reg * 0x8)
218c2ecf20Sopenharmony_ci#define SEC_SR_OFFSET		(SEC_REG_OFFSET(0))	/* 0x80 */
228c2ecf20Sopenharmony_ci#define CR_OFFSET(irq)		(REG_OFFSET(irq, 1))	/* 0x10 */
238c2ecf20Sopenharmony_ci#define SEC_CR_OFFSET		(SEC_REG_OFFSET(1))	/* 0x88 */
248c2ecf20Sopenharmony_ci#define ESR_OFFSET(irq)		(REG_OFFSET(irq, 2))	/* 0x20 */
258c2ecf20Sopenharmony_ci#define SEC_ESR_OFFSET		(SEC_REG_OFFSET(2))	/* 0x90 */
268c2ecf20Sopenharmony_ci#define ECR_OFFSET(irq)		(REG_OFFSET(irq, 3))	/* 0x30 */
278c2ecf20Sopenharmony_ci#define SEC_ECR_OFFSET		(SEC_REG_OFFSET(3))	/* 0x98 */
288c2ecf20Sopenharmony_ci#define PIR_OFFSET		(0x40)
298c2ecf20Sopenharmony_ci#define MSR_OFFSET		(0x44)
308c2ecf20Sopenharmony_ci#define PM_OFFSET(irq)		(REG_OFFSET(irq, 5))	/* 0x50 */
318c2ecf20Sopenharmony_ci#define TM_OFFSET(irq)		(REG_OFFSET(irq, 6))	/* 0x60 */
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define REG(addr) ((u32 *)(KSEG1ADDR(AR7_REGS_IRQ) + addr))
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define CHNL_OFFSET(chnl) (CHNLS_OFFSET + (chnl * 4))
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic int ar7_irq_base;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic void ar7_unmask_irq(struct irq_data *d)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	writel(1 << ((d->irq - ar7_irq_base) % 32),
428c2ecf20Sopenharmony_ci	       REG(ESR_OFFSET(d->irq - ar7_irq_base)));
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic void ar7_mask_irq(struct irq_data *d)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	writel(1 << ((d->irq - ar7_irq_base) % 32),
488c2ecf20Sopenharmony_ci	       REG(ECR_OFFSET(d->irq - ar7_irq_base)));
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void ar7_ack_irq(struct irq_data *d)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	writel(1 << ((d->irq - ar7_irq_base) % 32),
548c2ecf20Sopenharmony_ci	       REG(CR_OFFSET(d->irq - ar7_irq_base)));
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic void ar7_unmask_sec_irq(struct irq_data *d)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	writel(1 << (d->irq - ar7_irq_base - 40), REG(SEC_ESR_OFFSET));
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic void ar7_mask_sec_irq(struct irq_data *d)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	writel(1 << (d->irq - ar7_irq_base - 40), REG(SEC_ECR_OFFSET));
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic void ar7_ack_sec_irq(struct irq_data *d)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	writel(1 << (d->irq - ar7_irq_base - 40), REG(SEC_CR_OFFSET));
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic struct irq_chip ar7_irq_type = {
738c2ecf20Sopenharmony_ci	.name = "AR7",
748c2ecf20Sopenharmony_ci	.irq_unmask = ar7_unmask_irq,
758c2ecf20Sopenharmony_ci	.irq_mask = ar7_mask_irq,
768c2ecf20Sopenharmony_ci	.irq_ack = ar7_ack_irq
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic struct irq_chip ar7_sec_irq_type = {
808c2ecf20Sopenharmony_ci	.name = "AR7",
818c2ecf20Sopenharmony_ci	.irq_unmask = ar7_unmask_sec_irq,
828c2ecf20Sopenharmony_ci	.irq_mask = ar7_mask_sec_irq,
838c2ecf20Sopenharmony_ci	.irq_ack = ar7_ack_sec_irq,
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic void __init ar7_irq_init(int base)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	int i;
898c2ecf20Sopenharmony_ci	/*
908c2ecf20Sopenharmony_ci	 * Disable interrupts and clear pending
918c2ecf20Sopenharmony_ci	 */
928c2ecf20Sopenharmony_ci	writel(0xffffffff, REG(ECR_OFFSET(0)));
938c2ecf20Sopenharmony_ci	writel(0xff, REG(ECR_OFFSET(32)));
948c2ecf20Sopenharmony_ci	writel(0xffffffff, REG(SEC_ECR_OFFSET));
958c2ecf20Sopenharmony_ci	writel(0xffffffff, REG(CR_OFFSET(0)));
968c2ecf20Sopenharmony_ci	writel(0xff, REG(CR_OFFSET(32)));
978c2ecf20Sopenharmony_ci	writel(0xffffffff, REG(SEC_CR_OFFSET));
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	ar7_irq_base = base;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	for (i = 0; i < 40; i++) {
1028c2ecf20Sopenharmony_ci		writel(i, REG(CHNL_OFFSET(i)));
1038c2ecf20Sopenharmony_ci		/* Primary IRQ's */
1048c2ecf20Sopenharmony_ci		irq_set_chip_and_handler(base + i, &ar7_irq_type,
1058c2ecf20Sopenharmony_ci					 handle_level_irq);
1068c2ecf20Sopenharmony_ci		/* Secondary IRQ's */
1078c2ecf20Sopenharmony_ci		if (i < 32)
1088c2ecf20Sopenharmony_ci			irq_set_chip_and_handler(base + i + 40,
1098c2ecf20Sopenharmony_ci						 &ar7_sec_irq_type,
1108c2ecf20Sopenharmony_ci						 handle_level_irq);
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (request_irq(2, no_action, IRQF_NO_THREAD, "AR7 cascade interrupt",
1148c2ecf20Sopenharmony_ci			NULL))
1158c2ecf20Sopenharmony_ci		pr_err("Failed to request irq 2 (AR7 cascade interrupt)\n");
1168c2ecf20Sopenharmony_ci	if (request_irq(ar7_irq_base, no_action, IRQF_NO_THREAD,
1178c2ecf20Sopenharmony_ci			"AR7 cascade interrupt", NULL)) {
1188c2ecf20Sopenharmony_ci		pr_err("Failed to request irq %d (AR7 cascade interrupt)\n",
1198c2ecf20Sopenharmony_ci		       ar7_irq_base);
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci	set_c0_status(IE_IRQ0);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_civoid __init arch_init_irq(void)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	mips_cpu_irq_init();
1278c2ecf20Sopenharmony_ci	ar7_irq_init(8);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic void ar7_cascade(void)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	u32 status;
1338c2ecf20Sopenharmony_ci	int i, irq;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/* Primary IRQ's */
1368c2ecf20Sopenharmony_ci	irq = readl(REG(PIR_OFFSET)) & 0x3f;
1378c2ecf20Sopenharmony_ci	if (irq) {
1388c2ecf20Sopenharmony_ci		do_IRQ(ar7_irq_base + irq);
1398c2ecf20Sopenharmony_ci		return;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* Secondary IRQ's are cascaded through primary '0' */
1438c2ecf20Sopenharmony_ci	writel(1, REG(CR_OFFSET(irq)));
1448c2ecf20Sopenharmony_ci	status = readl(REG(SEC_SR_OFFSET));
1458c2ecf20Sopenharmony_ci	for (i = 0; i < 32; i++) {
1468c2ecf20Sopenharmony_ci		if (status & 1) {
1478c2ecf20Sopenharmony_ci			do_IRQ(ar7_irq_base + i + 40);
1488c2ecf20Sopenharmony_ci			return;
1498c2ecf20Sopenharmony_ci		}
1508c2ecf20Sopenharmony_ci		status >>= 1;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	spurious_interrupt();
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ciasmlinkage void plat_irq_dispatch(void)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
1598c2ecf20Sopenharmony_ci	if (pending & STATUSF_IP7)		/* cpu timer */
1608c2ecf20Sopenharmony_ci		do_IRQ(7);
1618c2ecf20Sopenharmony_ci	else if (pending & STATUSF_IP2)		/* int0 hardware line */
1628c2ecf20Sopenharmony_ci		ar7_cascade();
1638c2ecf20Sopenharmony_ci	else
1648c2ecf20Sopenharmony_ci		spurious_interrupt();
1658c2ecf20Sopenharmony_ci}
166