Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1753e74e authored by Sergey Ryazanov's avatar Sergey Ryazanov Committed by Ralf Baechle
Browse files

MIPS: ath25: add interrupts handling routines



Add interrupts initialization and handling routines, also add AHB bus
error interrupt handlers for both SoCs families.

Signed-off-by: default avatarSergey Ryazanov <ryazanov.s.a@gmail.com>
Cc: Linux MIPS <linux-mips@linux-mips.org>
Patchwork: https://patchwork.linux-mips.org/patch/8240/


Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent ba910345
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -103,6 +103,7 @@ config ATH25
	select CSRC_R4K
	select DMA_NONCOHERENT
	select IRQ_CPU
	select IRQ_DOMAIN
	select SYS_HAS_CPU_MIPS32_R1
	select SYS_SUPPORTS_BIG_ENDIAN
	select SYS_SUPPORTS_32BIT_KERNEL
+114 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/reboot.h>
#include <asm/bootinfo.h>
#include <asm/reboot.h>
@@ -26,6 +29,7 @@
#include "ar2315_regs.h"

static void __iomem *ar2315_rst_base;
static struct irq_domain *ar2315_misc_irq_domain;

static inline u32 ar2315_rst_reg_read(u32 reg)
{
@@ -46,6 +50,116 @@ static inline void ar2315_rst_reg_mask(u32 reg, u32 mask, u32 val)
	ar2315_rst_reg_write(reg, ret);
}

static irqreturn_t ar2315_ahb_err_handler(int cpl, void *dev_id)
{
	ar2315_rst_reg_write(AR2315_AHB_ERR0, AR2315_AHB_ERROR_DET);
	ar2315_rst_reg_read(AR2315_AHB_ERR1);

	pr_emerg("AHB fatal error\n");
	machine_restart("AHB error"); /* Catastrophic failure */

	return IRQ_HANDLED;
}

static struct irqaction ar2315_ahb_err_interrupt  = {
	.handler	= ar2315_ahb_err_handler,
	.name		= "ar2315-ahb-error",
};

static void ar2315_misc_irq_handler(unsigned irq, struct irq_desc *desc)
{
	u32 pending = ar2315_rst_reg_read(AR2315_ISR) &
		      ar2315_rst_reg_read(AR2315_IMR);
	unsigned nr, misc_irq = 0;

	if (pending) {
		struct irq_domain *domain = irq_get_handler_data(irq);

		nr = __ffs(pending);
		misc_irq = irq_find_mapping(domain, nr);
	}

	if (misc_irq) {
		if (nr == AR2315_MISC_IRQ_GPIO)
			ar2315_rst_reg_write(AR2315_ISR, AR2315_ISR_GPIO);
		else if (nr == AR2315_MISC_IRQ_WATCHDOG)
			ar2315_rst_reg_write(AR2315_ISR, AR2315_ISR_WD);
		generic_handle_irq(misc_irq);
	} else {
		spurious_interrupt();
	}
}

static void ar2315_misc_irq_unmask(struct irq_data *d)
{
	ar2315_rst_reg_mask(AR2315_IMR, 0, BIT(d->hwirq));
}

static void ar2315_misc_irq_mask(struct irq_data *d)
{
	ar2315_rst_reg_mask(AR2315_IMR, BIT(d->hwirq), 0);
}

static struct irq_chip ar2315_misc_irq_chip = {
	.name		= "ar2315-misc",
	.irq_unmask	= ar2315_misc_irq_unmask,
	.irq_mask	= ar2315_misc_irq_mask,
};

static int ar2315_misc_irq_map(struct irq_domain *d, unsigned irq,
			       irq_hw_number_t hw)
{
	irq_set_chip_and_handler(irq, &ar2315_misc_irq_chip, handle_level_irq);
	return 0;
}

static struct irq_domain_ops ar2315_misc_irq_domain_ops = {
	.map = ar2315_misc_irq_map,
};

/*
 * Called when an interrupt is received, this function
 * determines exactly which interrupt it was, and it
 * invokes the appropriate handler.
 *
 * Implicitly, we also define interrupt priority by
 * choosing which to dispatch first.
 */
static void ar2315_irq_dispatch(void)
{
	u32 pending = read_c0_status() & read_c0_cause();

	if (pending & CAUSEF_IP3)
		do_IRQ(AR2315_IRQ_WLAN0);
	else if (pending & CAUSEF_IP2)
		do_IRQ(AR2315_IRQ_MISC);
	else if (pending & CAUSEF_IP7)
		do_IRQ(ATH25_IRQ_CPU_CLOCK);
	else
		spurious_interrupt();
}

void __init ar2315_arch_init_irq(void)
{
	struct irq_domain *domain;
	unsigned irq;

	ath25_irq_dispatch = ar2315_irq_dispatch;

	domain = irq_domain_add_linear(NULL, AR2315_MISC_IRQ_COUNT,
				       &ar2315_misc_irq_domain_ops, NULL);
	if (!domain)
		panic("Failed to add IRQ domain");

	irq = irq_create_mapping(domain, AR2315_MISC_IRQ_AHB);
	setup_irq(irq, &ar2315_ahb_err_interrupt);

	irq_set_chained_handler(AR2315_IRQ_MISC, ar2315_misc_irq_handler);
	irq_set_handler_data(AR2315_IRQ_MISC, domain);

	ar2315_misc_irq_domain = domain;
}

static void ar2315_restart(char *command)
{
	void (*mips_reset_vec)(void) = (void *)0xbfc00000;
+2 −0
Original line number Diff line number Diff line
@@ -3,11 +3,13 @@

#ifdef CONFIG_SOC_AR2315

void ar2315_arch_init_irq(void);
void ar2315_plat_time_init(void);
void ar2315_plat_mem_setup(void);

#else

static inline void ar2315_arch_init_irq(void) {}
static inline void ar2315_plat_time_init(void) {}
static inline void ar2315_plat_mem_setup(void) {}

+23 −0
Original line number Diff line number Diff line
@@ -14,6 +14,29 @@
#ifndef __ASM_MACH_ATH25_AR2315_REGS_H
#define __ASM_MACH_ATH25_AR2315_REGS_H

/*
 * IRQs
 */
#define AR2315_IRQ_MISC		(MIPS_CPU_IRQ_BASE + 2)	/* C0_CAUSE: 0x0400 */
#define AR2315_IRQ_WLAN0	(MIPS_CPU_IRQ_BASE + 3)	/* C0_CAUSE: 0x0800 */
#define AR2315_IRQ_ENET0	(MIPS_CPU_IRQ_BASE + 4)	/* C0_CAUSE: 0x1000 */
#define AR2315_IRQ_LCBUS_PCI	(MIPS_CPU_IRQ_BASE + 5)	/* C0_CAUSE: 0x2000 */
#define AR2315_IRQ_WLAN0_POLL	(MIPS_CPU_IRQ_BASE + 6)	/* C0_CAUSE: 0x4000 */

/*
 * Miscellaneous interrupts, which share IP2.
 */
#define AR2315_MISC_IRQ_UART0		0
#define AR2315_MISC_IRQ_I2C_RSVD	1
#define AR2315_MISC_IRQ_SPI		2
#define AR2315_MISC_IRQ_AHB		3
#define AR2315_MISC_IRQ_APB		4
#define AR2315_MISC_IRQ_TIMER		5
#define AR2315_MISC_IRQ_GPIO		6
#define AR2315_MISC_IRQ_WATCHDOG	7
#define AR2315_MISC_IRQ_IR_RSVD		8
#define AR2315_MISC_IRQ_COUNT		9

/*
 * Address map
 */
+112 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/reboot.h>
#include <asm/bootinfo.h>
#include <asm/reboot.h>
@@ -26,6 +29,7 @@
#include "ar5312_regs.h"

static void __iomem *ar5312_rst_base;
static struct irq_domain *ar5312_misc_irq_domain;

static inline u32 ar5312_rst_reg_read(u32 reg)
{
@@ -46,6 +50,114 @@ static inline void ar5312_rst_reg_mask(u32 reg, u32 mask, u32 val)
	ar5312_rst_reg_write(reg, ret);
}

static irqreturn_t ar5312_ahb_err_handler(int cpl, void *dev_id)
{
	u32 proc1 = ar5312_rst_reg_read(AR5312_PROC1);
	u32 proc_addr = ar5312_rst_reg_read(AR5312_PROCADDR); /* clears error */
	u32 dma1 = ar5312_rst_reg_read(AR5312_DMA1);
	u32 dma_addr = ar5312_rst_reg_read(AR5312_DMAADDR);   /* clears error */

	pr_emerg("AHB interrupt: PROCADDR=0x%8.8x PROC1=0x%8.8x DMAADDR=0x%8.8x DMA1=0x%8.8x\n",
		 proc_addr, proc1, dma_addr, dma1);

	machine_restart("AHB error"); /* Catastrophic failure */
	return IRQ_HANDLED;
}

static struct irqaction ar5312_ahb_err_interrupt  = {
	.handler = ar5312_ahb_err_handler,
	.name    = "ar5312-ahb-error",
};

static void ar5312_misc_irq_handler(unsigned irq, struct irq_desc *desc)
{
	u32 pending = ar5312_rst_reg_read(AR5312_ISR) &
		      ar5312_rst_reg_read(AR5312_IMR);
	unsigned nr, misc_irq = 0;

	if (pending) {
		struct irq_domain *domain = irq_get_handler_data(irq);

		nr = __ffs(pending);
		misc_irq = irq_find_mapping(domain, nr);
	}

	if (misc_irq) {
		generic_handle_irq(misc_irq);
		if (nr == AR5312_MISC_IRQ_TIMER)
			ar5312_rst_reg_read(AR5312_TIMER);
	} else {
		spurious_interrupt();
	}
}

/* Enable the specified AR5312_MISC_IRQ interrupt */
static void ar5312_misc_irq_unmask(struct irq_data *d)
{
	ar5312_rst_reg_mask(AR5312_IMR, 0, BIT(d->hwirq));
}

/* Disable the specified AR5312_MISC_IRQ interrupt */
static void ar5312_misc_irq_mask(struct irq_data *d)
{
	ar5312_rst_reg_mask(AR5312_IMR, BIT(d->hwirq), 0);
	ar5312_rst_reg_read(AR5312_IMR); /* flush write buffer */
}

static struct irq_chip ar5312_misc_irq_chip = {
	.name		= "ar5312-misc",
	.irq_unmask	= ar5312_misc_irq_unmask,
	.irq_mask	= ar5312_misc_irq_mask,
};

static int ar5312_misc_irq_map(struct irq_domain *d, unsigned irq,
			       irq_hw_number_t hw)
{
	irq_set_chip_and_handler(irq, &ar5312_misc_irq_chip, handle_level_irq);
	return 0;
}

static struct irq_domain_ops ar5312_misc_irq_domain_ops = {
	.map = ar5312_misc_irq_map,
};

static void ar5312_irq_dispatch(void)
{
	u32 pending = read_c0_status() & read_c0_cause();

	if (pending & CAUSEF_IP2)
		do_IRQ(AR5312_IRQ_WLAN0);
	else if (pending & CAUSEF_IP5)
		do_IRQ(AR5312_IRQ_WLAN1);
	else if (pending & CAUSEF_IP6)
		do_IRQ(AR5312_IRQ_MISC);
	else if (pending & CAUSEF_IP7)
		do_IRQ(ATH25_IRQ_CPU_CLOCK);
	else
		spurious_interrupt();
}

void __init ar5312_arch_init_irq(void)
{
	struct irq_domain *domain;
	unsigned irq;

	ath25_irq_dispatch = ar5312_irq_dispatch;

	domain = irq_domain_add_linear(NULL, AR5312_MISC_IRQ_COUNT,
				       &ar5312_misc_irq_domain_ops, NULL);
	if (!domain)
		panic("Failed to add IRQ domain");

	irq = irq_create_mapping(domain, AR5312_MISC_IRQ_AHB_PROC);
	setup_irq(irq, &ar5312_ahb_err_interrupt);

	irq_set_chained_handler(AR5312_IRQ_MISC, ar5312_misc_irq_handler);
	irq_set_handler_data(AR5312_IRQ_MISC, domain);

	ar5312_misc_irq_domain = domain;
}

static void ar5312_restart(char *command)
{
	/* reset the system */
Loading