You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

411 lines
7.6 KiB

/*
* (C) Copyright 2017 Rockchip Electronics Co., Ltd
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <asm/io.h>
#include <asm/u-boot-arm.h>
#include <dm.h>
#include <irq-generic.h>
#include "irq-internal.h"
DECLARE_GLOBAL_DATA_PTR;
struct irq_desc {
interrupt_handler_t *handle_irq;
void *data;
u32 flag;
u32 count;
};
struct irqchip_desc {
struct irq_chip *gic;
struct irq_chip *gpio;
struct irq_chip *virq;
int suspend_irq[PLATFORM_SUSPEND_MAX_IRQ];
int suspend_num;
};
static struct irq_desc irq_desc[PLATFORM_MAX_IRQ];
static struct irqchip_desc irqchip;
static bool intr_setup;
int bad_irq(int irq)
{
if (!intr_setup) {
IRQ_W("Interrupt framework is not setup\n");
return -EINVAL;
}
if (irq < PLATFORM_MAX_IRQ) {
if (!irq_desc[irq].handle_irq)
return -EINVAL;
} else {
if (bad_virq(irq)) {
IRQ_E("Unknown virq: %d\n", irq);
return -EINVAL;
}
}
return 0;
}
/* general interrupt handler for gpio chip */
void __generic_gpio_handle_irq(int irq)
{
if (bad_irq(irq))
return;
if (irq < PLATFORM_GIC_MAX_IRQ) {
IRQ_W("IRQ %d: Invalid GPIO irq\n", irq);
return;
}
if (irq_desc[irq].handle_irq) {
irq_desc[irq].count++;
irq_desc[irq].handle_irq(irq, irq_desc[irq].data);
}
}
void __do_generic_irq_handler(void)
{
u32 irq;
assert(irqchip.gic->irq_get);
assert(irqchip.gic->irq_eoi);
irq = irqchip.gic->irq_get();
if (irq < PLATFORM_GIC_MAX_IRQ) {
if (irq_desc[irq].handle_irq) {
irq_desc[irq].count++;
irq_desc[irq].handle_irq(irq, irq_desc[irq].data);
}
}
irqchip.gic->irq_eoi(irq);
}
int irq_is_busy(int irq)
{
return (irq >= 0 && irq_desc[irq].handle_irq) ? -EBUSY : 0;
}
static int bad_irq_chip(struct irq_chip *chip)
{
return (!chip->name || !chip->irq_init || !chip->irq_enable ||
!chip->irq_disable) ? -EINVAL : 0;
}
static int __do_arch_irq_init(void)
{
int ret = -EINVAL;
/* After relocation done, bss data intr_setup */
if (!(gd->flags & GD_FLG_RELOC)) {
IRQ_W("Interrupt framework should initialize after reloc\n");
return -EINVAL;
}
/*
* We set true before arch_gpio_irq_init() to avoid fail when
* request irq for gpio banks.
*/
intr_setup = true;
memset(irq_desc, 0, sizeof(irq_desc));
irqchip.gic = arch_gic_get_irqchip();
if (bad_irq_chip(irqchip.gic)) {
IRQ_E("Bad gic irqchip\n");
goto out;
}
irqchip.gpio = arch_gpio_get_irqchip();
if (bad_irq_chip(irqchip.gpio)) {
IRQ_E("Bad gpio irqchip\n");
goto out;
}
irqchip.virq = arch_virq_get_irqchip();
if (bad_irq_chip(irqchip.virq)) {
IRQ_E("Bad virq irqchip\n");
goto out;
}
ret = irqchip.gic->irq_init();
if (ret) {
IRQ_E("GIC Interrupt setup failed, ret=%d\n", ret);
goto out;
}
ret = irqchip.gpio->irq_init();
if (ret) {
IRQ_E("GPIO Interrupt setup failed, ret=%d\n", ret);
goto out;
}
ret = irqchip.virq->irq_init();
if (ret) {
IRQ_E("VIRQ Interrupt setup failed, ret=%d\n", ret);
goto out;
}
return 0;
out:
intr_setup = false;
return ret;
}
int irq_handler_enable(int irq)
{
int ret;
if (bad_irq(irq))
return -EINVAL;
if (irq < PLATFORM_GIC_MAX_IRQ)
ret = irqchip.gic->irq_enable(irq);
else if (irq < PLATFORM_GPIO_MAX_IRQ)
ret = irqchip.gpio->irq_enable(irq);
else
ret = irqchip.virq->irq_enable(irq);
if (!ret && irq < PLATFORM_MAX_IRQ)
irq_desc[irq].flag |= IRQ_FLG_ENABLE;
return ret;
}
int irq_handler_disable(int irq)
{
int ret;
if (bad_irq(irq))
return -EINVAL;
if (irq < PLATFORM_GIC_MAX_IRQ)
ret = irqchip.gic->irq_disable(irq);
else if (irq < PLATFORM_GPIO_MAX_IRQ)
ret = irqchip.gpio->irq_disable(irq);
else
ret = irqchip.virq->irq_disable(irq);
if (!ret && irq < PLATFORM_MAX_IRQ)
irq_desc[irq].flag &= ~IRQ_FLG_ENABLE;
return ret;
}
int irq_set_irq_type(int irq, unsigned int type)
{
if (bad_irq(irq))
return -EINVAL;
if (irq < PLATFORM_GIC_MAX_IRQ)
return irqchip.gic->irq_set_type(irq, type);
else if (irq < PLATFORM_GPIO_MAX_IRQ)
return irqchip.gpio->irq_set_type(irq, type);
else
return -ENOSYS;
}
int irq_revert_irq_type(int irq)
{
if (bad_irq(irq))
return -EINVAL;
if (irq < PLATFORM_GIC_MAX_IRQ)
return 0;
else if (irq < PLATFORM_GPIO_MAX_IRQ)
return irqchip.gpio->irq_revert_type(irq);
else
return -ENOSYS;
}
int irq_get_gpio_level(int irq)
{
if (bad_irq(irq))
return -EINVAL;
if (irq < PLATFORM_GIC_MAX_IRQ)
return 0;
else if (irq < PLATFORM_GPIO_MAX_IRQ)
return irqchip.gpio->irq_get_gpio_level(irq);
else
return -ENOSYS;
}
void irq_install_handler(int irq, interrupt_handler_t *handler, void *data)
{
if (!intr_setup) {
IRQ_W("Interrupt framework is not intr_setup\n");
return;
}
if (irq < PLATFORM_MAX_IRQ) {
if (!handler || irq_desc[irq].handle_irq)
return;
irq_desc[irq].handle_irq = handler;
irq_desc[irq].data = data;
} else {
virq_install_handler(irq, handler, data);
}
}
void irq_free_handler(int irq)
{
if (irq_handler_disable(irq))
return;
if (irq < PLATFORM_MAX_IRQ) {
irq_desc[irq].handle_irq = NULL;
irq_desc[irq].data = NULL;
} else {
virq_free_handler(irq);
}
}
int irq_handler_enable_suspend_only(int irq)
{
if (bad_irq(irq))
return -EINVAL;
if (irqchip.suspend_num >= PLATFORM_SUSPEND_MAX_IRQ) {
printf("Over max count(%d) of suspend irq\n",
PLATFORM_SUSPEND_MAX_IRQ);
return -EPERM;
}
irqchip.suspend_irq[irqchip.suspend_num++] = irq;
return 0;
}
int irqs_suspend(void)
{
int i;
for (i = 0; i < irqchip.suspend_num; i++)
irq_handler_enable(irqchip.suspend_irq[i]);
return irqchip.gic->irq_suspend();
}
int irqs_resume(void)
{
int i;
for (i = 0; i < irqchip.suspend_num; i++)
irq_handler_disable(irqchip.suspend_irq[i]);
return irqchip.gic->irq_resume();
}
#ifdef CONFIG_ARM64
void do_irq(struct pt_regs *pt_regs, unsigned int esr)
{
#ifdef CONFIG_ROCKCHIP_DEBUGGER
printf("\n>>> Rockchip Debugger:\n");
show_regs(pt_regs);
#endif
__do_generic_irq_handler();
}
#else
void do_irq(struct pt_regs *pt_regs)
{
#ifdef CONFIG_ROCKCHIP_DEBUGGER
printf("\n>>> Rockchp Debugger:\n");
show_regs(pt_regs);
#endif
__do_generic_irq_handler();
}
#endif
int arch_interrupt_init(void)
{
#ifndef CONFIG_ARM64
unsigned long cpsr __maybe_unused;
/* stack has been reserved in: arch_reserve_stacks() */
IRQ_STACK_START = gd->irq_sp;
IRQ_STACK_START_IN = gd->irq_sp;
__asm__ __volatile__("mrs %0, cpsr\n"
: "=r" (cpsr)
:
: "memory");
__asm__ __volatile__("msr cpsr_c, %0\n"
"mov sp, %1\n"
:
: "r" (IRQ_MODE | I_BIT |
F_BIT | (cpsr & ~FIQ_MODE)),
"r" (IRQ_STACK_START)
: "memory");
__asm__ __volatile__("msr cpsr_c, %0"
:
: "r" (cpsr)
: "memory");
#endif
return __do_arch_irq_init();
}
int interrupt_init(void)
{
return arch_interrupt_init();
}
void enable_interrupts(void)
{
local_irq_enable();
}
int disable_interrupts(void)
{
int flags;
local_irq_save(flags);
return flags;
}
static int do_dump_irqs(cmd_tbl_t *cmdtp, int flag,
int argc, char * const argv[])
{
struct udevice *dev;
char *drv_name;
int pirq;
printf(" IRQ En Handler Driver Name Trig\n");
printf("----------------------------------------------------------------------\n");
for (pirq = 0; pirq < PLATFORM_MAX_IRQ; pirq++) {
if (!irq_desc[pirq].handle_irq)
continue;
dev = (struct udevice *)irq_desc[pirq].data;
if (strstr(dev->name, "gpio"))
drv_name = "IRQ";
else
drv_name = dev->driver->name;
printf(" %3d %d 0x%08lx %-12s %-12s %d\n",
pirq, irq_desc[pirq].flag & IRQ_FLG_ENABLE ? 1 : 0,
(ulong)irq_desc[pirq].handle_irq,
drv_name, dev->name, irq_desc[pirq].count);
virqs_show(pirq);
}
return 0;
}
U_BOOT_CMD(
dump_irqs, 1, 1, do_dump_irqs, "Dump IRQs", ""
);