586 lines
12 KiB
C
586 lines
12 KiB
C
/*
|
|
* (C) Copyright 2007-2015
|
|
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
|
* Jerry Wang <wangflord@allwinnertech.com>
|
|
*
|
|
* See file CREDITS for list of people who contributed to this
|
|
* project.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <linux/compiler.h>
|
|
|
|
|
|
#if defined(CONFIG_CMD_IRQ)
|
|
#include <asm/io.h>
|
|
#include <asm/arch/intc.h>
|
|
#include <asm/arch/gic.h>
|
|
#endif
|
|
|
|
#if defined(CONFIG_CMD_IRQ)
|
|
#if 0
|
|
struct _irq_handler
|
|
{
|
|
void *m_data;
|
|
void (*m_func)( void * data);
|
|
};
|
|
|
|
struct _irq_handler sunxi_int_handlers[GIC_IRQ_NUM];
|
|
#endif
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
#endif
|
|
|
|
void enable_interrupts(void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int disable_interrupts(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void show_regs(struct pt_regs *regs)
|
|
{
|
|
int i;
|
|
|
|
printf("ELR: %lx\n", regs->elr);
|
|
printf("LR: %lx\n", regs->regs[30]);
|
|
for (i = 0; i < 29; i += 2)
|
|
printf("x%-2d: %016lx x%-2d: %016lx\n",
|
|
i, regs->regs[i], i+1, regs->regs[i+1]);
|
|
printf("\n");
|
|
}
|
|
|
|
/*
|
|
* do_bad_sync handles the impossible case in the Synchronous Abort vector.
|
|
*/
|
|
void do_bad_sync(struct pt_regs *pt_regs, unsigned int esr)
|
|
{
|
|
printf("Bad mode in \"Synchronous Abort\" handler, esr 0x%08x\n", esr);
|
|
show_regs(pt_regs);
|
|
panic("Resetting CPU ...\n");
|
|
}
|
|
|
|
/*
|
|
* do_bad_irq handles the impossible case in the Irq vector.
|
|
*/
|
|
void do_bad_irq(struct pt_regs *pt_regs, unsigned int esr)
|
|
{
|
|
printf("Bad mode in \"Irq\" handler, esr 0x%08x\n", esr);
|
|
show_regs(pt_regs);
|
|
panic("Resetting CPU ...\n");
|
|
}
|
|
|
|
/*
|
|
* do_bad_fiq handles the impossible case in the Fiq vector.
|
|
*/
|
|
void do_bad_fiq(struct pt_regs *pt_regs, unsigned int esr)
|
|
{
|
|
printf("Bad mode in \"Fiq\" handler, esr 0x%08x\n", esr);
|
|
show_regs(pt_regs);
|
|
panic("Resetting CPU ...\n");
|
|
}
|
|
|
|
/*
|
|
* do_bad_error handles the impossible case in the Error vector.
|
|
*/
|
|
void do_bad_error(struct pt_regs *pt_regs, unsigned int esr)
|
|
{
|
|
printf("Bad mode in \"Error\" handler, esr 0x%08x\n", esr);
|
|
show_regs(pt_regs);
|
|
panic("Resetting CPU ...\n");
|
|
}
|
|
|
|
/*
|
|
* do_sync handles the Synchronous Abort exception.
|
|
*/
|
|
void do_sync(struct pt_regs *pt_regs, unsigned int esr)
|
|
{
|
|
printf("\"Synchronous Abort\" handler, esr 0x%08x\n", esr);
|
|
show_regs(pt_regs);
|
|
panic("Resetting CPU ...\n");
|
|
}
|
|
|
|
/*
|
|
* do_irq handles the Irq exception.
|
|
*/
|
|
#ifndef CONFIG_CMD_IRQ
|
|
void do_irq(struct pt_regs *pt_regs, unsigned int esr)
|
|
{
|
|
printf("\"Irq\" handler, esr 0x%08x\n", esr);
|
|
show_regs(pt_regs);
|
|
panic("Resetting CPU ...\n");
|
|
}
|
|
#else
|
|
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
#if 0
|
|
|
|
static void default_isr(void *data)
|
|
{
|
|
printf("default_isr(): called from IRQ 0x%x\n", (uint)(ulong)data);
|
|
while(1);
|
|
}
|
|
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
int irq_enable(int irq_no)
|
|
{
|
|
uint reg_val;
|
|
uint offset;
|
|
|
|
if (irq_no >= GIC_IRQ_NUM)
|
|
{
|
|
printf("irq NO.(%d) > GIC_IRQ_NUM(%d) !!\n", irq_no, GIC_IRQ_NUM);
|
|
return -1;
|
|
}
|
|
|
|
offset = irq_no >> 5; // 除32
|
|
reg_val = readl(GIC_SET_EN(offset));
|
|
reg_val |= 1 << (irq_no & 0x1f);
|
|
writel(reg_val, GIC_SET_EN(offset));
|
|
|
|
return 0;
|
|
}
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
int irq_disable(int irq_no)
|
|
{
|
|
uint reg_val;
|
|
uint offset;
|
|
|
|
if (irq_no >= GIC_IRQ_NUM)
|
|
{
|
|
printf("irq NO.(%d) > GIC_IRQ_NUM(%d) !!\n", irq_no, GIC_IRQ_NUM);
|
|
return -1;
|
|
}
|
|
|
|
offset = irq_no >> 5; // 除32
|
|
reg_val = (1 << (irq_no & 0x1f));
|
|
writel(reg_val, GIC_CLR_EN(offset));
|
|
|
|
return 0;
|
|
}
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
static void gic_sgi_handler(uint irq_no)
|
|
{
|
|
printf("SGI irq %d coming... \n", irq_no);
|
|
}
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
static void gic_ppi_handler(uint irq_no)
|
|
{
|
|
printf("PPI irq %d coming... \n", irq_no);
|
|
}
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
static void gic_spi_handler(uint irq_no)
|
|
{
|
|
if (sunxi_int_handlers[irq_no].m_func != default_isr)
|
|
{
|
|
sunxi_int_handlers[irq_no].m_func(sunxi_int_handlers[irq_no].m_data);
|
|
}
|
|
}
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
static void gic_clear_pending(uint irq_no)
|
|
{
|
|
uint reg_val;
|
|
uint offset;
|
|
|
|
offset = irq_no >> 5; // 除32
|
|
reg_val = readl(GIC_PEND_CLR(offset));
|
|
reg_val |= (1 << (irq_no & 0x1f));
|
|
writel(reg_val, GIC_PEND_CLR(offset));
|
|
|
|
return ;
|
|
}
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
void do_irq(struct pt_regs *pt_regs, unsigned int esr)
|
|
{
|
|
u32 idnum;
|
|
volatile u32 secmode;
|
|
|
|
secmode = gd->securemode;
|
|
if(secmode)
|
|
idnum = readl(GIC_AIAR_REG);
|
|
else
|
|
idnum = readl(GIC_INT_ACK_REG);
|
|
|
|
if ((idnum == 1022) || (idnum == 1023))
|
|
{
|
|
printf("spurious irq !!\n");
|
|
return;
|
|
}
|
|
if (idnum >= GIC_IRQ_NUM) {
|
|
printf("irq NO.(%d) > GIC_IRQ_NUM(%d) !!\n", idnum, GIC_IRQ_NUM-32);
|
|
return;
|
|
}
|
|
if (idnum < 16)
|
|
gic_sgi_handler(idnum);
|
|
else if (idnum < 32)
|
|
gic_ppi_handler(idnum);
|
|
else
|
|
gic_spi_handler(idnum);
|
|
|
|
if(secmode)
|
|
writel(idnum, GIC_AEOI_REG);
|
|
else
|
|
{
|
|
writel(idnum, GIC_END_INT_REG);
|
|
writel(idnum, GIC_DEACT_INT_REG);
|
|
}
|
|
//writel(idnum, GIC_DEACT_INT_REG);
|
|
gic_clear_pending(idnum);
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
#endif
|
|
/*
|
|
* do_fiq handles the Fiq exception.
|
|
*/
|
|
void do_fiq(struct pt_regs *pt_regs, unsigned int esr)
|
|
{
|
|
printf("\"Fiq\" handler, esr 0x%08x\n", esr);
|
|
show_regs(pt_regs);
|
|
panic("Resetting CPU ...\n");
|
|
}
|
|
|
|
/*
|
|
* do_error handles the Error exception.
|
|
* Errors are more likely to be processor specific,
|
|
* it is defined with weak attribute and can be redefined
|
|
* in processor specific code.
|
|
*/
|
|
void __weak do_error(struct pt_regs *pt_regs, unsigned int esr)
|
|
{
|
|
printf("\"Error\" handler, esr 0x%08x\n", esr);
|
|
show_regs(pt_regs);
|
|
panic("Resetting CPU ...\n");
|
|
}
|
|
|
|
#if 0
|
|
#if defined(CONFIG_CMD_IRQ)
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
void irq_install_handler (int irq, interrupt_handler_t handle_irq, void *data)
|
|
{
|
|
disable_interrupts();
|
|
if (irq >= GIC_IRQ_NUM || !handle_irq)
|
|
{
|
|
enable_interrupts();
|
|
return;
|
|
}
|
|
|
|
sunxi_int_handlers[irq].m_data = data;
|
|
sunxi_int_handlers[irq].m_func = handle_irq;
|
|
|
|
enable_interrupts();
|
|
}
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
void irq_free_handler(int irq)
|
|
{
|
|
disable_interrupts();
|
|
if (irq >= GIC_IRQ_NUM)
|
|
{
|
|
enable_interrupts();
|
|
return;
|
|
}
|
|
|
|
sunxi_int_handlers[irq].m_data = NULL;
|
|
sunxi_int_handlers[irq].m_func = default_isr;
|
|
|
|
enable_interrupts();
|
|
}
|
|
/*
|
|
************************************************************************************************************
|
|
*
|
|
* function
|
|
*
|
|
* name :
|
|
*
|
|
* parmeters :
|
|
*
|
|
* return :
|
|
*
|
|
* note :
|
|
*
|
|
*
|
|
************************************************************************************************************
|
|
*/
|
|
int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
int irq;
|
|
|
|
printf("Interrupt-Information:\n");
|
|
printf("Nr(Max) Routine Arg\n");
|
|
|
|
for (irq = 0; irq < GIC_IRQ_NUM; irq ++)
|
|
{
|
|
if (sunxi_int_handlers[irq].m_func != NULL)
|
|
{
|
|
printf("%d(%d) 0x%08lx 0x%08lx\n",
|
|
irq, GIC_IRQ_NUM,
|
|
(ulong)sunxi_int_handlers[irq].m_func,
|
|
(ulong)sunxi_int_handlers[irq].m_data);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
static void gic_distributor_init(void)
|
|
{
|
|
__u32 cpumask = 0x01010101;
|
|
__u32 gic_irqs;
|
|
__u32 i;
|
|
|
|
writel(0, GIC_DIST_CON);
|
|
|
|
/* check GIC hardware configutation */
|
|
gic_irqs = ((readl(GIC_CON_TYPE) & 0x1f) + 1) * 32;
|
|
if (gic_irqs > 1020)
|
|
{
|
|
gic_irqs = 1020;
|
|
}
|
|
if (gic_irqs < GIC_IRQ_NUM)
|
|
{
|
|
printf("GIC parameter config error, only support %d"
|
|
" irqs < %d(spec define)!!\n", gic_irqs, GIC_IRQ_NUM);
|
|
return ;
|
|
}
|
|
|
|
/* set trigger type to be level-triggered, active low */
|
|
for (i=0; i<GIC_IRQ_NUM; i+=16)
|
|
{
|
|
writel(0, GIC_IRQ_MOD_CFG(i>>4));
|
|
}
|
|
/* set priority */
|
|
for (i=GIC_SRC_SPI(0); i<GIC_IRQ_NUM; i+=4)
|
|
{
|
|
writel(0xa0a0a0a0, GIC_SPI_PRIO((i-32)>>2));
|
|
}
|
|
/* set processor target */
|
|
for (i=32; i<GIC_IRQ_NUM; i+=4)
|
|
{
|
|
writel(cpumask, GIC_SPI_PROC_TARG((i-32)>>2));
|
|
}
|
|
/* disable all interrupts */
|
|
for (i=32; i<GIC_IRQ_NUM; i+=32)
|
|
{
|
|
writel(0xffffffff, GIC_CLR_EN(i>>5));
|
|
}
|
|
/* clear all interrupt active state */
|
|
for (i=32; i<GIC_IRQ_NUM; i+=32)
|
|
{
|
|
writel(0xffffffff, GIC_ACT_CLR(i>>5));
|
|
}
|
|
writel(1, GIC_DIST_CON);
|
|
|
|
return ;
|
|
}
|
|
|
|
static void gic_cpuif_init(void)
|
|
{
|
|
uint i;
|
|
|
|
writel(0, GIC_CPU_IF_CTRL);
|
|
/*
|
|
* Deal with the banked PPI and SGI interrupts - disable all
|
|
* PPI interrupts, ensure all SGI interrupts are enabled.
|
|
*/
|
|
writel(0xffff0000, GIC_CLR_EN(0));
|
|
writel(0x0000ffff, GIC_SET_EN(0));
|
|
/* Set priority on PPI and SGI interrupts */
|
|
for (i=0; i<16; i+=4)
|
|
{
|
|
writel(0xa0a0a0a0, GIC_SGI_PRIO(i>>2));
|
|
}
|
|
for (i=16; i<32; i+=4)
|
|
{
|
|
writel(0xa0a0a0a0, GIC_PPI_PRIO((i-16)>>2));
|
|
}
|
|
|
|
writel(0xf0, GIC_INT_PRIO_MASK);
|
|
writel(1, GIC_CPU_IF_CTRL);
|
|
|
|
return ;
|
|
}
|
|
|
|
#endif
|
|
int interrupt_init(void)
|
|
{
|
|
arch_interrupt_init();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int interrupt_exit(void)
|
|
{
|
|
arch_interrupt_exit();
|
|
return 0;
|
|
}
|
|
|