162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2006,2007 Felix Fietkau <nbd@openwrt.org>
462306a36Sopenharmony_ci * Copyright (C) 2006,2007 Eugene Konev <ejka@openwrt.org>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/interrupt.h>
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci#include <linux/irq.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <asm/irq_cpu.h>
1262306a36Sopenharmony_ci#include <asm/mipsregs.h>
1362306a36Sopenharmony_ci#include <asm/mach-ar7/ar7.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define EXCEPT_OFFSET	0x80
1662306a36Sopenharmony_ci#define PACE_OFFSET	0xA0
1762306a36Sopenharmony_ci#define CHNLS_OFFSET	0x200
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define REG_OFFSET(irq, reg)	((irq) / 32 * 0x4 + reg * 0x10)
2062306a36Sopenharmony_ci#define SEC_REG_OFFSET(reg)	(EXCEPT_OFFSET + reg * 0x8)
2162306a36Sopenharmony_ci#define SEC_SR_OFFSET		(SEC_REG_OFFSET(0))	/* 0x80 */
2262306a36Sopenharmony_ci#define CR_OFFSET(irq)		(REG_OFFSET(irq, 1))	/* 0x10 */
2362306a36Sopenharmony_ci#define SEC_CR_OFFSET		(SEC_REG_OFFSET(1))	/* 0x88 */
2462306a36Sopenharmony_ci#define ESR_OFFSET(irq)		(REG_OFFSET(irq, 2))	/* 0x20 */
2562306a36Sopenharmony_ci#define SEC_ESR_OFFSET		(SEC_REG_OFFSET(2))	/* 0x90 */
2662306a36Sopenharmony_ci#define ECR_OFFSET(irq)		(REG_OFFSET(irq, 3))	/* 0x30 */
2762306a36Sopenharmony_ci#define SEC_ECR_OFFSET		(SEC_REG_OFFSET(3))	/* 0x98 */
2862306a36Sopenharmony_ci#define PIR_OFFSET		(0x40)
2962306a36Sopenharmony_ci#define MSR_OFFSET		(0x44)
3062306a36Sopenharmony_ci#define PM_OFFSET(irq)		(REG_OFFSET(irq, 5))	/* 0x50 */
3162306a36Sopenharmony_ci#define TM_OFFSET(irq)		(REG_OFFSET(irq, 6))	/* 0x60 */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define REG(addr) ((u32 *)(KSEG1ADDR(AR7_REGS_IRQ) + addr))
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define CHNL_OFFSET(chnl) (CHNLS_OFFSET + (chnl * 4))
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic int ar7_irq_base;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic void ar7_unmask_irq(struct irq_data *d)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	writel(1 << ((d->irq - ar7_irq_base) % 32),
4262306a36Sopenharmony_ci	       REG(ESR_OFFSET(d->irq - ar7_irq_base)));
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic void ar7_mask_irq(struct irq_data *d)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	writel(1 << ((d->irq - ar7_irq_base) % 32),
4862306a36Sopenharmony_ci	       REG(ECR_OFFSET(d->irq - ar7_irq_base)));
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic void ar7_ack_irq(struct irq_data *d)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	writel(1 << ((d->irq - ar7_irq_base) % 32),
5462306a36Sopenharmony_ci	       REG(CR_OFFSET(d->irq - ar7_irq_base)));
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void ar7_unmask_sec_irq(struct irq_data *d)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	writel(1 << (d->irq - ar7_irq_base - 40), REG(SEC_ESR_OFFSET));
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic void ar7_mask_sec_irq(struct irq_data *d)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	writel(1 << (d->irq - ar7_irq_base - 40), REG(SEC_ECR_OFFSET));
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic void ar7_ack_sec_irq(struct irq_data *d)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	writel(1 << (d->irq - ar7_irq_base - 40), REG(SEC_CR_OFFSET));
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic struct irq_chip ar7_irq_type = {
7362306a36Sopenharmony_ci	.name = "AR7",
7462306a36Sopenharmony_ci	.irq_unmask = ar7_unmask_irq,
7562306a36Sopenharmony_ci	.irq_mask = ar7_mask_irq,
7662306a36Sopenharmony_ci	.irq_ack = ar7_ack_irq
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic struct irq_chip ar7_sec_irq_type = {
8062306a36Sopenharmony_ci	.name = "AR7",
8162306a36Sopenharmony_ci	.irq_unmask = ar7_unmask_sec_irq,
8262306a36Sopenharmony_ci	.irq_mask = ar7_mask_sec_irq,
8362306a36Sopenharmony_ci	.irq_ack = ar7_ack_sec_irq,
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic void __init ar7_irq_init(int base)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	int i;
8962306a36Sopenharmony_ci	/*
9062306a36Sopenharmony_ci	 * Disable interrupts and clear pending
9162306a36Sopenharmony_ci	 */
9262306a36Sopenharmony_ci	writel(0xffffffff, REG(ECR_OFFSET(0)));
9362306a36Sopenharmony_ci	writel(0xff, REG(ECR_OFFSET(32)));
9462306a36Sopenharmony_ci	writel(0xffffffff, REG(SEC_ECR_OFFSET));
9562306a36Sopenharmony_ci	writel(0xffffffff, REG(CR_OFFSET(0)));
9662306a36Sopenharmony_ci	writel(0xff, REG(CR_OFFSET(32)));
9762306a36Sopenharmony_ci	writel(0xffffffff, REG(SEC_CR_OFFSET));
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	ar7_irq_base = base;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	for (i = 0; i < 40; i++) {
10262306a36Sopenharmony_ci		writel(i, REG(CHNL_OFFSET(i)));
10362306a36Sopenharmony_ci		/* Primary IRQ's */
10462306a36Sopenharmony_ci		irq_set_chip_and_handler(base + i, &ar7_irq_type,
10562306a36Sopenharmony_ci					 handle_level_irq);
10662306a36Sopenharmony_ci		/* Secondary IRQ's */
10762306a36Sopenharmony_ci		if (i < 32)
10862306a36Sopenharmony_ci			irq_set_chip_and_handler(base + i + 40,
10962306a36Sopenharmony_ci						 &ar7_sec_irq_type,
11062306a36Sopenharmony_ci						 handle_level_irq);
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (request_irq(2, no_action, IRQF_NO_THREAD, "AR7 cascade interrupt",
11462306a36Sopenharmony_ci			NULL))
11562306a36Sopenharmony_ci		pr_err("Failed to request irq 2 (AR7 cascade interrupt)\n");
11662306a36Sopenharmony_ci	if (request_irq(ar7_irq_base, no_action, IRQF_NO_THREAD,
11762306a36Sopenharmony_ci			"AR7 cascade interrupt", NULL)) {
11862306a36Sopenharmony_ci		pr_err("Failed to request irq %d (AR7 cascade interrupt)\n",
11962306a36Sopenharmony_ci		       ar7_irq_base);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	set_c0_status(IE_IRQ0);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_civoid __init arch_init_irq(void)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	mips_cpu_irq_init();
12762306a36Sopenharmony_ci	ar7_irq_init(8);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void ar7_cascade(void)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	u32 status;
13362306a36Sopenharmony_ci	int i, irq;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/* Primary IRQ's */
13662306a36Sopenharmony_ci	irq = readl(REG(PIR_OFFSET)) & 0x3f;
13762306a36Sopenharmony_ci	if (irq) {
13862306a36Sopenharmony_ci		do_IRQ(ar7_irq_base + irq);
13962306a36Sopenharmony_ci		return;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* Secondary IRQ's are cascaded through primary '0' */
14362306a36Sopenharmony_ci	writel(1, REG(CR_OFFSET(irq)));
14462306a36Sopenharmony_ci	status = readl(REG(SEC_SR_OFFSET));
14562306a36Sopenharmony_ci	for (i = 0; i < 32; i++) {
14662306a36Sopenharmony_ci		if (status & 1) {
14762306a36Sopenharmony_ci			do_IRQ(ar7_irq_base + i + 40);
14862306a36Sopenharmony_ci			return;
14962306a36Sopenharmony_ci		}
15062306a36Sopenharmony_ci		status >>= 1;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	spurious_interrupt();
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ciasmlinkage void plat_irq_dispatch(void)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
15962306a36Sopenharmony_ci	if (pending & STATUSF_IP7)		/* cpu timer */
16062306a36Sopenharmony_ci		do_IRQ(7);
16162306a36Sopenharmony_ci	else if (pending & STATUSF_IP2)		/* int0 hardware line */
16262306a36Sopenharmony_ci		ar7_cascade();
16362306a36Sopenharmony_ci	else
16462306a36Sopenharmony_ci		spurious_interrupt();
16562306a36Sopenharmony_ci}
166