sdk-hwV1.3/lichee/brandy-2.0/u-boot-2018/arch/riscv/mach-sunxi/gic.c

347 lines
7.3 KiB
C

/*
* (C) Copyright 2007-2013
* 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.
*
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/gic.h>
#include <sunxi_board.h>
DECLARE_GLOBAL_DATA_PTR;
struct _irq_handler {
void *m_data;
void (*m_func)(void *data);
};
struct _irq_handler sunxi_int_handlers[GIC_IRQ_NUM];
extern int interrupts_is_open(void);
static void gic_spi_set_target(int irq_no, int cpu_id);
static void default_isr(void *data)
{
printf("default_isr(): called from IRQ 0x%lx\n", (phys_addr_t)data);
while
(1);
}
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;
}
if (irq_no == AW_IRQ_NMI) {
*(volatile unsigned int *)(0x01f00c00 + 0x10) |= 1;
*(volatile unsigned int *)(0x01f00c00 + 0x40) |= 1;
}
//gic_spi_set_target(irq_no, get_core_pos());
offset = irq_no >> 5;
reg_val = readl(GIC_SET_EN(offset));
reg_val |= 1 << (irq_no & 0x1f);
writel(reg_val, GIC_SET_EN(offset));
return 0;
}
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;
}
if (irq_no == AW_IRQ_NMI) {
*(volatile unsigned int *)(0x01f00c00 + 0x10) |= 1;
*(volatile unsigned int *)(0x01f00c00 + 0x40) &= ~1;
}
gic_spi_set_target(irq_no, 0);
offset = irq_no >> 5;
reg_val = (1 << (irq_no & 0x1f));
writel(reg_val, GIC_CLR_EN(offset));
return 0;
}
static void gic_sgi_handler(uint irq_no)
{
printf("SGI irq %d coming... \n", irq_no);
}
static void gic_ppi_handler(uint irq_no)
{
printf("PPI irq %d coming... \n", irq_no);
}
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);
}
}
static void gic_clear_pending(uint irq_no)
{
uint reg_val;
uint offset;
offset = irq_no >> 5;
//reg_val = readl(GIC_PEND_CLR(offset));
reg_val = (1 << (irq_no & 0x1f));
writel(reg_val, GIC_PEND_CLR(offset));
return ;
}
void irq_install_handler (int irq, interrupt_handler_t handle_irq, void *data)
{
int flag = interrupts_is_open();
//when irq_handler call this function , irq enable bit has already disabled in irq_mode,so don't need to enable I bit
if (flag) {
disable_interrupts();
}
if (irq >= GIC_IRQ_NUM || !handle_irq) {
goto __END;
}
sunxi_int_handlers[irq].m_data = data;
sunxi_int_handlers[irq].m_func = handle_irq;
__END:
if (flag) {
enable_interrupts();
}
}
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();
}
void do_irq (struct pt_regs *pt_regs)
{
u32 idnum;
#ifdef CONFIG_ARCH_SUN8IW6P1
/* fix gic bug when enable secure*/
if (sunxi_probe_secure_os())
idnum = readl(GIC_AIAR_REG);
else
idnum = readl(GIC_INT_ACK_REG);
#else
idnum = readl(GIC_INT_ACK_REG);
#endif
if ((idnum == 1022) || (idnum == 1023)) {
printf("spurious irq !!\n");
return;
}
if (idnum >= GIC_IRQ_NUM) {
debug("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);
#ifdef CONFIG_ARCH_SUN8IW6P1
/* fix gic bug when enable secure*/
if (sunxi_probe_secure_os())
writel(idnum, GIC_AEOI_REG);
else
writel(idnum, GIC_END_INT_REG);
#else
writel(idnum, GIC_END_INT_REG);
#endif
writel(idnum, GIC_DEACT_INT_REG);
gic_clear_pending(idnum);
return;
}
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;
}
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) {
debug("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 ;
}
static void gic_spi_set_target(int irq_no, int cpu_id)
{
uint reg_val, offset;
volatile void __iomem *addr;
irq_no -= 32;
/* dispatch the usb interrupt to CPU1 */
addr = GIC_SPI_PROC_TARG(irq_no >> 2);
reg_val = readl(addr);
offset = 8 * (irq_no & 3);
reg_val &= ~(0xff << offset);
reg_val |= (((1 << cpu_id) & 0xf) << offset);
writel(reg_val, addr);
return ;
}
int arch_interrupt_init (void)
{
int i;
for (i = 0; i < GIC_IRQ_NUM; i++)
sunxi_int_handlers[i].m_data = default_isr;
if (sunxi_probe_secure_monitor() || sunxi_probe_secure_os())
tick_printf("gic: sec monitor mode\n");
else {
tick_printf("gic: normal mode\n");
gic_distributor_init();
gic_cpuif_init();
}
return 0;
}
int arch_interrupt_exit(void)
{
if (!(sunxi_probe_secure_monitor() || sunxi_probe_secure_os())) {
gic_distributor_init();
gic_cpuif_init();
}
return 0;
}
int sunxi_gic_cpu_interface_init(int cpu)
{
gic_cpuif_init();
return 0;
}
int sunxi_gic_cpu_interface_exit(void)
{
writel(0, GIC_CPU_IF_CTRL);
return 0;
}