| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * (C) Copyright 2007 Michal Simek |
| * (C) Copyright 2004 Atmark Techno, Inc. |
| * |
| * Michal SIMEK <monstr@monstr.eu> |
| * Yasushi SHOJI <yashi@atmark-techno.com> |
| */ |
| |
| #include <common.h> |
| #include <command.h> |
| #include <fdtdec.h> |
| #include <irq_func.h> |
| #include <log.h> |
| #include <malloc.h> |
| #include <asm/global_data.h> |
| #include <asm/microblaze_intc.h> |
| #include <asm/asm.h> |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| void enable_interrupts(void) |
| { |
| debug("Enable interrupts for the whole CPU\n"); |
| MSRSET(0x2); |
| } |
| |
| int disable_interrupts(void) |
| { |
| unsigned int msr; |
| |
| MFS(msr, rmsr); |
| MSRCLR(0x2); |
| return (msr & 0x2) != 0; |
| } |
| |
| static struct irq_action *vecs; |
| static u32 irq_no; |
| |
| /* mapping structure to interrupt controller */ |
| microblaze_intc_t *intc; |
| |
| /* default handler */ |
| static void def_hdlr(void) |
| { |
| puts("def_hdlr\n"); |
| } |
| |
| static void enable_one_interrupt(int irq) |
| { |
| int mask; |
| int offset = 1; |
| |
| offset <<= irq; |
| mask = intc->ier; |
| intc->ier = (mask | offset); |
| |
| debug("Enable one interrupt irq %x - mask %x,ier %x\n", offset, mask, |
| intc->ier); |
| debug("INTC isr %x, ier %x, iar %x, mer %x\n", intc->isr, intc->ier, |
| intc->iar, intc->mer); |
| } |
| |
| static void disable_one_interrupt(int irq) |
| { |
| int mask; |
| int offset = 1; |
| |
| offset <<= irq; |
| mask = intc->ier; |
| intc->ier = (mask & ~offset); |
| |
| debug("Disable one interrupt irq %x - mask %x,ier %x\n", irq, mask, |
| intc->ier); |
| debug("INTC isr %x, ier %x, iar %x, mer %x\n", intc->isr, intc->ier, |
| intc->iar, intc->mer); |
| } |
| |
| int install_interrupt_handler(int irq, interrupt_handler_t *hdlr, void *arg) |
| { |
| struct irq_action *act; |
| |
| /* irq out of range */ |
| if ((irq < 0) || (irq > irq_no)) { |
| puts("IRQ out of range\n"); |
| return -1; |
| } |
| act = &vecs[irq]; |
| if (hdlr) { /* enable */ |
| act->handler = hdlr; |
| act->arg = arg; |
| act->count = 0; |
| enable_one_interrupt(irq); |
| return 0; |
| } |
| |
| /* Disable */ |
| act->handler = (interrupt_handler_t *)def_hdlr; |
| act->arg = (void *)irq; |
| disable_one_interrupt(irq); |
| return 1; |
| } |
| |
| /* initialization interrupt controller - hardware */ |
| static void intc_init(void) |
| { |
| intc->mer = 0; |
| intc->ier = 0; |
| intc->iar = 0xFFFFFFFF; |
| /* XIntc_Start - hw_interrupt enable and all interrupt enable */ |
| intc->mer = 0x3; |
| |
| debug("INTC isr %x, ier %x, iar %x, mer %x\n", intc->isr, intc->ier, |
| intc->iar, intc->mer); |
| } |
| |
| int interrupt_init(void) |
| { |
| int i; |
| const void *blob = gd->fdt_blob; |
| int node = 0; |
| |
| debug("INTC: Initialization\n"); |
| |
| node = fdt_node_offset_by_compatible(blob, node, |
| "xlnx,xps-intc-1.00.a"); |
| if (node != -1) { |
| fdt_addr_t base = fdtdec_get_addr(blob, node, "reg"); |
| if (base == FDT_ADDR_T_NONE) |
| return -1; |
| |
| debug("INTC: Base addr %lx\n", base); |
| intc = (microblaze_intc_t *)base; |
| irq_no = fdtdec_get_int(blob, node, "xlnx,num-intr-inputs", 0); |
| debug("INTC: IRQ NO %x\n", irq_no); |
| } else { |
| return node; |
| } |
| |
| if (irq_no) { |
| vecs = calloc(1, sizeof(struct irq_action) * irq_no); |
| if (vecs == NULL) { |
| puts("Interrupt vector allocation failed\n"); |
| return -1; |
| } |
| |
| /* initialize irq list */ |
| for (i = 0; i < irq_no; i++) { |
| vecs[i].handler = (interrupt_handler_t *)def_hdlr; |
| vecs[i].arg = (void *)i; |
| vecs[i].count = 0; |
| } |
| /* initialize intc controller */ |
| intc_init(); |
| enable_interrupts(); |
| } else { |
| puts("Undefined interrupt controller\n"); |
| } |
| return 0; |
| } |
| |
| void interrupt_handler(void) |
| { |
| int irqs = intc->ivr; /* find active interrupt */ |
| int mask = 1; |
| int value; |
| struct irq_action *act = vecs + irqs; |
| |
| debug("INTC isr %x, ier %x, iar %x, mer %x\n", intc->isr, intc->ier, |
| intc->iar, intc->mer); |
| #ifdef DEBUG |
| R14(value); |
| #endif |
| debug("Interrupt handler on %x line, r14 %x\n", irqs, value); |
| |
| debug("Jumping to interrupt handler rutine addr %x,count %x,arg %x\n", |
| (u32)act->handler, act->count, (u32)act->arg); |
| act->handler(act->arg); |
| act->count++; |
| |
| intc->iar = mask << irqs; |
| |
| debug("Dump INTC reg, isr %x, ier %x, iar %x, mer %x\n", intc->isr, |
| intc->ier, intc->iar, intc->mer); |
| #ifdef DEBUG |
| R14(value); |
| #endif |
| debug("Interrupt handler on %x line, r14 %x\n", irqs, value); |
| } |
| |
| #if defined(CONFIG_CMD_IRQ) |
| int do_irqinfo(struct cmd_tbl *cmdtp, int flag, int argc, const char *argv[]) |
| { |
| int i; |
| struct irq_action *act = vecs; |
| |
| if (irq_no) { |
| puts("\nInterrupt-Information:\n\n" |
| "Nr Routine Arg Count\n" |
| "-----------------------------\n"); |
| |
| for (i = 0; i < irq_no; i++) { |
| if (act->handler != (interrupt_handler_t *)def_hdlr) { |
| printf("%02d %08x %08x %d\n", i, |
| (int)act->handler, (int)act->arg, |
| act->count); |
| } |
| act++; |
| } |
| puts("\n"); |
| } else { |
| puts("Undefined interrupt controller\n"); |
| } |
| return 0; |
| } |
| #endif |