From 0732dd21869418b4d437b8d1aef239d5348fc94d Mon Sep 17 00:00:00 2001 From: Kuiying Wang Date: Fri, 3 Jan 2020 12:52:29 +0800 Subject: [PATCH] Enable interrupt in u-boot. Ast2600 is Cortex-A7 GIC V2 is used as the interrupt controller GIC includes GICD and GICC Testedby: 1. Enable interrupt based SW handshake for ESPI 2. Both ArcherCity and Ast2600 EVB are working well. Signed-off-by: Kuiying Wang Signed-off-by: Jae Hyun Yoo --- Kconfig | 14 ++ arch/arm/lib/stack.c | 9 ++ arch/arm/lib/vectors.S | 30 ++++- board/aspeed/ast2600_intel/ast-espi.c | 3 +- board/aspeed/ast2600_intel/ast-irq.c | 185 +++++++++++++------------- board/aspeed/ast2600_intel/ast-irq.h | 8 -- board/aspeed/ast2600_intel/intel.c | 1 - 7 files changed, 145 insertions(+), 105 deletions(-) delete mode 100644 board/aspeed/ast2600_intel/ast-irq.h diff --git a/Kconfig b/Kconfig index c3dfa8de47c8..b62bcdbccf1e 100644 --- a/Kconfig +++ b/Kconfig @@ -239,6 +239,20 @@ config BUILD_TARGET special image will be automatically built upon calling make / buildman. +config USE_IRQ + bool "Use interrupts" + default n + +config STACKSIZE_IRQ + depends on USE_IRQ + int "Size for IRQ stack (only if USE_IRQ enabled)" + default 16384 + +config STACKSIZE_FIQ + depends on USE_IRQ + int "Size for FIQ stack (only if USE_IRQ enabled)" + default 16384 + endmenu # General setup menu "Boot images" diff --git a/arch/arm/lib/stack.c b/arch/arm/lib/stack.c index c89a219dd26d..d9a7f49c5623 100644 --- a/arch/arm/lib/stack.c +++ b/arch/arm/lib/stack.c @@ -24,6 +24,15 @@ int arch_reserve_stacks(void) gd->irq_sp = gd->start_addr_sp; # if !defined(CONFIG_ARM64) +# ifdef CONFIG_USE_IRQ + gd->start_addr_sp -= (CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ); + printf("Reserving %zu Bytes for IRQ stack at: %08lx\n", + CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ, gd->start_addr_sp); + + /* 8-byte alignment for ARM ABI compliance */ + gd->start_addr_sp &= ~0x07; +# endif + /* leave 3 words for abort-stack, plus 1 for alignment */ gd->start_addr_sp -= 16; # endif diff --git a/arch/arm/lib/vectors.S b/arch/arm/lib/vectors.S index 2ca6e2494a7a..5a5e60dbdde4 100644 --- a/arch/arm/lib/vectors.S +++ b/arch/arm/lib/vectors.S @@ -154,6 +154,17 @@ IRQ_STACK_START_IN: .word 0x0badc0de #endif +#ifdef CONFIG_USE_IRQ +/* IRQ stack memory (calculated at run-time) */ +.globl IRQ_STACK_START +IRQ_STACK_START: + .word 0x0badc0de +/* IRQ stack memory (calculated at run-time) */ +.globl FIQ_STACK_START +FIQ_STACK_START: + .word 0x0badc0de +#endif + @ @ IRQ stack frame. @ @@ -277,17 +288,30 @@ not_used: bad_save_user_regs bl do_not_used - +#ifdef CONFIG_USE_IRQ + .align 5 +irq: + get_irq_stack + irq_save_user_regs + bl do_irq + irq_restore_user_regs + .align 5 +fiq: + get_fiq_stack + /* someone ought to write a more effective fiq_save_user_regs */ + irq_save_user_regs + bl do_fiq + irq_restore_user_regs +#else .align 5 irq: get_bad_stack bad_save_user_regs bl do_irq - .align 5 fiq: get_bad_stack bad_save_user_regs bl do_fiq - +#endif /* CONFIG_USE_IRQ */ #endif /* CONFIG_SPL_BUILD */ diff --git a/board/aspeed/ast2600_intel/ast-espi.c b/board/aspeed/ast2600_intel/ast-espi.c index 0fdbf089a450..1d7ae529612d 100644 --- a/board/aspeed/ast2600_intel/ast-espi.c +++ b/board/aspeed/ast2600_intel/ast-espi.c @@ -142,7 +142,7 @@ static void espi_handshake_ack(void) } } -int espi_irq_handler(struct pt_regs *regs) +static void espi_irq_handler(void *cookie) { uint32_t irq_status = readl(AST_ESPI_BASE + ESPI008); @@ -226,7 +226,6 @@ int espi_irq_handler(struct pt_regs *regs) readl(AST_ESPI_BASE + ESPI11C), readl(AST_ESPI_BASE + ESPI094), readl(AST_ESPI_BASE + ESPI12C), irq_status); - return 0; } void espi_init(void) diff --git a/board/aspeed/ast2600_intel/ast-irq.c b/board/aspeed/ast2600_intel/ast-irq.c index f817f8cd7c81..106bb3b4ffb2 100644 --- a/board/aspeed/ast2600_intel/ast-irq.c +++ b/board/aspeed/ast2600_intel/ast-irq.c @@ -1,14 +1,7 @@ -/* - * Copyright 2018 Intel Corporation - * - * 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. - */ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018-2020, Intel Corporation. #include -#include #include DECLARE_GLOBAL_DATA_PTR; @@ -18,19 +11,6 @@ DECLARE_GLOBAL_DATA_PTR; #define GIC_INTERFACE_OFFSET 0x4000 #define GIC_VIRT_OFFSET 0x6000 -#define VIC_STATUS_L 0x80 -#define VIC_STATUS_H 0x84 -#define VIC_IRQ_SELECTION_L 0x98 -#define VIC_IRQ_SELECTION_H 0x9C -#define VIC_ENABLE_L 0xA0 -#define VIC_ENABLE_H 0xA4 -#define VIC_ENABLE_CLEAR_L 0xA8 -#define VIC_ENABLE_CLEAR_H 0xAC -#define VIC_INTERRUPT_CLEAR_L 0xD8 -#define VIC_INTERRUPT_CLEAR_H 0xDC - -#define VIC_CLEAR_ALL (~0) - /* GIC_DISTRIBUTOR_OFFSET register offsets */ #define GICD_CTLR 0x000 #define GICD_TYPER 0x004 @@ -82,7 +62,9 @@ DECLARE_GLOBAL_DATA_PTR; #define GICC_IIDR 0x00fc #define GICC_DIR 0x1000 -#define GIC_CPU_IMPLEMENTER_MAGIC 0x0102143b +#define GIC_CPU_IMPLEMENTER_MAGIC 0x0102143b +#define GICC_IAR_INT_ID_MASK 0x3ff +#define GIC_CPU_DEACTIVATE 0x1000 /* GIC_INTERFACE_OFFSET register offsets */ #define GICH_HCR 0x000 @@ -116,9 +98,10 @@ DECLARE_GLOBAL_DATA_PTR; #define GIC_VIRT_CPU_IMPLEMENTER_MAGIC 0x0102143b -#define GICD_CTLR_ENABLE 0x03 - -#define GICD_INT_DEF_PRI 0xa0 +#define GICD_CTLR_ENABLE 0x03 /*enable group 0 and 1*/ +#define GICC_CTLR_ENABLE 0x03 +#define GICD_ITARGET_ALL 0xffffffff +#define GICD_INT_DEF_PRI 0xa0 #define GICD_INT_DEF_PRI_X4 (\ (GICD_INT_DEF_PRI << 24) |\ (GICD_INT_DEF_PRI << 16) |\ @@ -129,21 +112,32 @@ DECLARE_GLOBAL_DATA_PTR; #define GICD_INT_EN_CLR_X32 0xffffffff #define GICD_INT_EN_CLR_PPI 0xffff0000 #define GICD_INT_EN_SET_SGI 0x0000ffff +#define GICD_ICFG_LEVEL_TRIGGER 0x55555555 +#define GICC_UNMASK_ALL_PRIORITY 0xff #define gicd_readl(OFFSET) readl(gbase + GIC_DISTRIBUTOR_OFFSET + (OFFSET)) #define gicd_writel(VALUE, OFFSET) \ writel((VALUE), gbase + GIC_DISTRIBUTOR_OFFSET + (OFFSET)) #define gicc_readl(OFFSET) readl(gbase + GIC_CPU_OFFSET + (OFFSET)) +#define gicc_writel(VALUE, OFFSET) \ + writel((VALUE), gbase + GIC_CPU_OFFSET + (OFFSET)) #define gich_readl(OFFSET) readl(gbase + GIC_INTERFACE_OFFSET + (OFFSET)) #define gicv_readl(OFFSET) readl(gbase + GIC_VIRT_OFFSET + (OFFSET)) - -static size_t max_irq = 0; - #define ITLINES_MASK 0x1f #define ITLINES_SHIFT 5 - #define GIC_MAX_IRQ 1020 +#define SPI_INT_NUM_MIN 32 +#define MAX_IRQ 0xfffffffe +#define DEBUG_IRQ_ENABLED 0 +#if DEBUG_IRQ_ENABLED +#define DBG_IRQ printf +#else +#define DBG_IRQ(...) +#endif + +static size_t max_irq = 0; static interrupt_handler_t *handlers[GIC_MAX_IRQ] = {NULL}; +static void *cookies[GIC_MAX_IRQ] = {NULL}; static unsigned long irq_total = 0; static unsigned long irq_counts[GIC_MAX_IRQ] = {0}; static uint32_t gbase = 0; @@ -159,24 +153,31 @@ static inline uint32_t gic_base(void) static void enable_gic(void) { - uint32_t gicd_ctlr; + uint32_t gicd_ctlr, gicc_ctlr; + DBG_IRQ(" %s()\n", __FUNCTION__); /* add GIC offset ref table 1-3 for interrupt distributor address */ gicd_ctlr = gicd_readl(GICD_CTLR); + gicc_ctlr = gicc_readl(GICC_CTLR); gicd_writel(gicd_ctlr | GICD_CTLR_ENABLE, GICD_CTLR); + gicc_writel(gicc_ctlr | GICC_CTLR_ENABLE, GICC_CTLR); } static void disable_gic(void) { - uint32_t gicd_ctlr; - + uint32_t gicd_ctlr, gicc_ctlr; + DBG_IRQ(" %s()\n", __FUNCTION__); /* add GIC offset ref table 1-3 for interrupt distributor address */ gicd_ctlr = gicd_readl(GICD_CTLR); gicd_writel(gicd_ctlr & ~GICD_CTLR_ENABLE, GICD_CTLR); + gicc_ctlr = gicc_readl(GICC_CTLR); + gicc_writel(gicc_ctlr & ~GICC_CTLR_ENABLE, GICC_CTLR); } static void enable_irq_id(unsigned int id) { + DBG_IRQ(" %s()\n", __FUNCTION__); + uint32_t grp = id >> ITLINES_SHIFT; uint32_t grp_bit = 1 << (id & ITLINES_MASK); gicd_writel(grp_bit, GICD_ISENABLERn + grp * sizeof(uint32_t)); @@ -184,6 +185,7 @@ static void enable_irq_id(unsigned int id) static void disable_irq_id(unsigned int id) { + DBG_IRQ(" %s()\n", __FUNCTION__); uint32_t grp = id >> ITLINES_SHIFT; uint32_t grp_bit = 1 << (id & ITLINES_MASK); gicd_writel(grp_bit, GICD_ICENABLERn + grp * sizeof(uint32_t)); @@ -193,34 +195,49 @@ static int gic_probe(void) { int i; gbase = gic_base(); - enable_gic(); + DBG_IRQ("gic_probe GIC base = 0x%x, magicd=0x%x\n", + gbase, gicd_readl(GICD_IIDR)); if (gicd_readl(GICD_IIDR) != GIC_DISTRIBUTOR_IMPLEMENTER_MAGIC && gicc_readl(GICC_IIDR) != GIC_CPU_IMPLEMENTER_MAGIC && gicv_readl(GICV_IIDR) != GIC_VIRT_CPU_IMPLEMENTER_MAGIC) { + printf("error: magic check \n"); return 0; } + + disable_gic(); + /* GIC supports up to 1020 lines */ - max_irq = ((gicd_readl(GICD_TYPER) & ITLINES_MASK) + 1) << ITLINES_SHIFT; + max_irq = ((gicd_readl(GICD_TYPER) & ITLINES_MASK) + 1) * 32; if (max_irq > GIC_MAX_IRQ) max_irq = GIC_MAX_IRQ; /* set all lines to be level triggered N-N */ for (i = 32; i < max_irq; i += 16) - gicd_writel(0, GICD_ICFGRn + i / 4); - - /* Set priority on all interrupts. */ - for (i = 0; i < max_irq; i += 4) + gicd_writel(GICD_ICFG_LEVEL_TRIGGER, GICD_ICFGRn + i / 4); + + DBG_IRQ("max_irq = 0x%x, typer=0x%x, config=0x%x, maxirq=0x%x\n", max_irq, + (gicd_readl(GICD_TYPER) & ITLINES_MASK) + 1, + gicd_readl(GICD_ICFGRn + 0x8), + ((gicd_readl(GICD_TYPER) & ITLINES_MASK) + 1) * 0x20); + /* Set priority and target on all interrupts. */ + for (i = 0; i < max_irq; i += 4) { gicd_writel(GICD_INT_DEF_PRI_X4, GICD_IPRIORITYRn + i); + gicd_writel(GICD_ITARGET_ALL, GICD_ITARGETSRn + i); + } /* Deactivate and disable all SPIs. */ for (i = 32; i < max_irq; i += 32) { gicd_writel(GICD_INT_EN_CLR_X32, GICD_ICACTIVERn + i / 8); gicd_writel(GICD_INT_EN_CLR_X32, GICD_ICENABLERn + i / 8); } - gicd_writel(GICD_INT_EN_CLR_X32, GICD_ICACTIVERn); - gicd_writel(GICD_INT_EN_CLR_PPI, GICD_ICENABLERn); + gicd_writel(GICD_INT_EN_CLR_X32, GICD_ICACTIVERn); + gicd_writel(GICD_INT_EN_CLR_PPI, GICD_ICENABLERn); gicd_writel(GICD_INT_EN_SET_SGI, GICD_ISENABLERn); + /* unmask all priority */ + gicc_writel(GICC_UNMASK_ALL_PRIORITY, GICC_PMRn); + + enable_gic(); return 0; } @@ -228,6 +245,7 @@ static int gic_probe(void) void irq_free_handler (int irq); static void gic_shutdown(void) { + DBG_IRQ(" %s()\n", __FUNCTION__); int i; for (i = 0; i < max_irq; i++) { @@ -238,6 +256,7 @@ static void gic_shutdown(void) int arch_interrupt_init_early(void) { + DBG_IRQ(" %s()\n", __FUNCTION__); return 0; } @@ -247,28 +266,28 @@ int arch_interrupt_init(void) for (i = 0; i < GIC_MAX_IRQ; i++) { handlers[i] = NULL; + cookies[i] = NULL; irq_counts[i] = 0; } + DBG_IRQ("arch_interrupt_init\n"); return gic_probe(); } int arch_interrupt_fini(void) { + DBG_IRQ(" %s()\n", __FUNCTION__); gic_shutdown(); return 0; } int interrupt_init (void) { - /* - * setup up stacks if necessary - */ + /* setup up stacks if necessary */ + IRQ_STACK_START = gd->irq_sp + 8; IRQ_STACK_START_IN = gd->irq_sp + 8; - printf("%s()\n", __FUNCTION__); + DBG_IRQ(" %s()\n", __FUNCTION__); return arch_interrupt_init(); - - return 0; } int global_interrupts_enabled (void) @@ -286,12 +305,12 @@ void enable_interrupts (void) { unsigned long cpsr; __asm__ __volatile__("mrs %0, cpsr\n" - "bic %0, %0, #0x80\n" + "bic %0, %0, #0x1c0\n" "msr cpsr_c, %0" : "=r" (cpsr) : : "memory"); - + DBG_IRQ(" %s()\n", __FUNCTION__); return; } @@ -304,11 +323,13 @@ int disable_interrupts (void) : "=r" (cpsr), "=r" (temp) : : "memory"); + DBG_IRQ(" %s()\n", __FUNCTION__); return (cpsr & 0x80) == 0; } -void irq_install_handler(int irq, interrupt_handler_t *handler, void *ctx) +void irq_install_handler(int irq, interrupt_handler_t *handler, void *cookie) { + DBG_IRQ(" %s()\n", __FUNCTION__); if (irq > max_irq) { printf("irq %d out of range\n", irq); return; @@ -317,19 +338,22 @@ void irq_install_handler(int irq, interrupt_handler_t *handler, void *ctx) printf("irq %d already in use (%p)\n", irq, handlers[irq]); return; } - printf("registering handler for irq %d\n", irq); + DBG_IRQ("registering handler for irq %d\n", irq); handlers[irq] = handler; + cookies[irq] = cookie; enable_irq_id(irq); } void irq_free_handler (int irq) { + DBG_IRQ(" %s()\n", __FUNCTION__); if (irq >= max_irq) { printf("irq %d out of range\n", irq); return; } if (handlers[irq]) { handlers[irq] = NULL; + cookies[irq] = NULL; disable_irq_id(irq); } } @@ -339,8 +363,10 @@ int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) int i; int enabled = global_interrupts_enabled(); printf("GIC base = 0x%x\n", gbase); - printf("interrupts %sabled\n", (enabled ? "en" : "dis")); + printf("Number of interrupt sources = %d\n", max_irq); + printf("Interrupts %sabled\n", (enabled ? "en" : "dis")); uint32_t grp_en = 0; + for (i = 0; i < max_irq; i++) { if ((i & ITLINES_MASK) == 0) grp_en = gicd_readl(GICD_ISENABLERn + @@ -348,52 +374,29 @@ int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) int irq_enabled = grp_en & (1 << (i & ITLINES_MASK)); if (!irq_enabled) continue; - printf("% 2i (% 3s): %lu\n", i, + printf("%2d (%3s): %lu\n", i, (irq_enabled ? "on" : "off"), irq_counts[i]); } - printf("total: %lu\n", irq_total); + printf("Total: %lu\n", irq_total); + return 0; } void do_irq(struct pt_regs *pt_regs) { - int i; - if (!gbase) { - static int printed_msg = 0; - if (!printed_msg) - { - printed_msg = 1; - printf("interrupt before configured!\n"); - } - return; - } - irq_total++; - uint32_t grp_pend = 0; - for (i = 0; i < max_irq; i++) { - /* limit reads of the pending register to once in 32 */ - if ((i & ITLINES_MASK) == 0) - grp_pend = gicd_readl(GICD_ISPENDRn + - (i >> ITLINES_SHIFT) * sizeof(uint32_t)); - uint32_t pending = grp_pend & (1 << (i & ITLINES_MASK)); - if (pending) { - irq_counts[i]++; - /* mask via GICD_ICENABLERn */ - gicd_writel(pending, GICD_ICENABLERn + - (i >> ITLINES_SHIFT) * sizeof(uint32_t)); - if (handlers[i]) { - handlers[i](pt_regs); - /* unmask via GICD_ISENABLERn */ - gicd_writel(pending, GICD_ISENABLERn + - (i >> ITLINES_SHIFT) * sizeof(uint32_t)); - /* clear pending via GICD_ICPENDRn */ - gicd_writel(pending, GICD_ICPENDRn + - (i >> ITLINES_SHIFT) * sizeof(uint32_t)); - } else { - printf("unexpected interrupt %i; masking\n", i); - /* clear pending via GICD_ICPENDRn */ - gicd_writel(pending, GICD_ISPENDRn + - (i >> ITLINES_SHIFT) * sizeof(uint32_t)); - } + uint32_t irqstat, irqnr; + + if (irq_total < MAX_IRQ) + irq_total++; + irqstat = gicc_readl(GICC_IAR); + irqnr = irqstat & GICC_IAR_INT_ID_MASK; + + if (irqnr > SPI_INT_NUM_MIN && irqnr < GIC_MAX_IRQ) { + gicc_writel(irqnr, GICC_EOIR); + if (irq_counts[irqnr] < MAX_IRQ) + irq_counts[irqnr]++; + if (handlers[irqnr]) { + handlers[irqnr](cookies[irqnr]); } } } diff --git a/board/aspeed/ast2600_intel/ast-irq.h b/board/aspeed/ast2600_intel/ast-irq.h deleted file mode 100644 index 9957f2baa7ff..000000000000 --- a/board/aspeed/ast2600_intel/ast-irq.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _AST_IRQ_H_ -#define _AST_IRQ_H_ - -int request_irq(int irq, interrupt_handler_t *handler); -int release_irq(int irq); -int arch_interrupt_init_early(void); - -#endif diff --git a/board/aspeed/ast2600_intel/intel.c b/board/aspeed/ast2600_intel/intel.c index 14a20b27e178..d03a446846bc 100644 --- a/board/aspeed/ast2600_intel/intel.c +++ b/board/aspeed/ast2600_intel/intel.c @@ -239,7 +239,6 @@ static void timer_handler(void *regs) printf("+"); } -extern int arch_interrupt_init_early(void); int board_early_init_f(void) { /* This is called before relocation; beware! */ -- 2.17.1