/* * U-boot - traps.c Routines related to interrupts and exceptions * * Copyright (c) 2005-2007 Analog Devices Inc. * * This file is based on * No original Copyright holder listed, * Probabily original (C) Roman Zippel (assigned DJD, 1999) * * Copyright 2003 Metrowerks - for Blackfin * Copyright 2000-2001 Lineo, Inc. D. Jeff Dionne <jeff@lineo.ca> * Copyright 1999-2000 D. Jeff Dionne, <jeff@uclinux.org> * * (C) Copyright 2000-2004 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA */ #include <common.h> #include <linux/types.h> #include <asm/errno.h> #include <asm/irq.h> #include <asm/system.h> #include <asm/traps.h> #include <asm/machdep.h> #include "cpu.h" #include <asm/arch/anomaly.h> #include <asm/cplb.h> #include <asm/io.h> void init_IRQ(void) { blackfin_init_IRQ(); return; } void process_int(unsigned long vec, struct pt_regs *fp) { printf("interrupt\n"); return; } extern unsigned int icplb_table[page_descriptor_table_size][2]; extern unsigned int dcplb_table[page_descriptor_table_size][2]; unsigned long last_cplb_fault_retx; static unsigned int cplb_sizes[4] = { 1024, 4 * 1024, 1024 * 1024, 4 * 1024 * 1024 }; void trap_c(struct pt_regs *regs) { unsigned int addr; unsigned long trapnr = (regs->seqstat) & SEQSTAT_EXCAUSE; unsigned int i, j, size, *I0, *I1; unsigned short data = 0; switch (trapnr) { /* 0x26 - Data CPLB Miss */ case VEC_CPLB_M: #ifdef ANOMALY_05000261 /* * Work around an anomaly: if we see a new DCPLB fault, * return without doing anything. Then, * if we get the same fault again, handle it. */ addr = last_cplb_fault_retx; last_cplb_fault_retx = regs->retx; printf("this time, curr = 0x%08x last = 0x%08x\n", addr, last_cplb_fault_retx); if (addr != last_cplb_fault_retx) goto trap_c_return; #endif data = 1; case VEC_CPLB_I_M: if (data) { addr = *(unsigned int *)pDCPLB_FAULT_ADDR; } else { addr = *(unsigned int *)pICPLB_FAULT_ADDR; } for (i = 0; i < page_descriptor_table_size; i++) { if (data) { size = cplb_sizes[dcplb_table[i][1] >> 16]; j = dcplb_table[i][0]; } else { size = cplb_sizes[icplb_table[i][1] >> 16]; j = icplb_table[i][0]; } if ((j <= addr) && ((j + size) > addr)) { debug("found %i 0x%08x\n", i, j); break; } } if (i == page_descriptor_table_size) { printf("something is really wrong\n"); do_reset(NULL, 0, 0, NULL); } /* Turn the cache off */ if (data) { sync(); asm(" .align 8; "); *(unsigned int *)DMEM_CONTROL &= ~(ACACHE_BCACHE | ENDCPLB | PORT_PREF0); sync(); } else { sync(); asm(" .align 8; "); *(unsigned int *)IMEM_CONTROL &= ~(IMC | ENICPLB); sync(); } if (data) { I0 = (unsigned int *)DCPLB_ADDR0; I1 = (unsigned int *)DCPLB_DATA0; } else { I0 = (unsigned int *)ICPLB_ADDR0; I1 = (unsigned int *)ICPLB_DATA0; } j = 0; while (*I1 & CPLB_LOCK) { debug("skipping %i %08p - %08x\n", j, I1, *I1); *I0++; *I1++; j++; } debug("remove %i 0x%08x 0x%08x\n", j, *I0, *I1); for (; j < 15; j++) { debug("replace %i 0x%08x 0x%08x\n", j, I0, I0 + 1); *I0++ = *(I0 + 1); *I1++ = *(I1 + 1); } if (data) { *I0 = dcplb_table[i][0]; *I1 = dcplb_table[i][1]; I0 = (unsigned int *)DCPLB_ADDR0; I1 = (unsigned int *)DCPLB_DATA0; } else { *I0 = icplb_table[i][0]; *I1 = icplb_table[i][1]; I0 = (unsigned int *)ICPLB_ADDR0; I1 = (unsigned int *)ICPLB_DATA0; } for (j = 0; j < 16; j++) { debug("%i 0x%08x 0x%08x\n", j, *I0++, *I1++); } /* Turn the cache back on */ if (data) { j = *(unsigned int *)DMEM_CONTROL; sync(); asm(" .align 8; "); *(unsigned int *)DMEM_CONTROL = ACACHE_BCACHE | ENDCPLB | PORT_PREF0 | j; sync(); } else { sync(); asm(" .align 8; "); *(unsigned int *)IMEM_CONTROL = IMC | ENICPLB; sync(); } break; default: /* All traps come here */ printf("code=[0x%x], ", (unsigned int)(regs->seqstat & 0x3f)); printf("stack frame=0x%x, ", (unsigned int)regs); printf("bad PC=0x%04x\n", (unsigned int)regs->pc); dump(regs); printf("\n\n"); printf("Unhandled IRQ or exceptions!\n"); printf("Please reset the board \n"); do_reset(NULL, 0, 0, NULL); } return; } void dump(struct pt_regs *fp) { debug("RETE: %08lx RETN: %08lx RETX: %08lx RETS: %08lx\n", fp->rete, fp->retn, fp->retx, fp->rets); debug("IPEND: %04lx SYSCFG: %04lx\n", fp->ipend, fp->syscfg); debug("SEQSTAT: %08lx SP: %08lx\n", (long)fp->seqstat, (long)fp); debug("R0: %08lx R1: %08lx R2: %08lx R3: %08lx\n", fp->r0, fp->r1, fp->r2, fp->r3); debug("R4: %08lx R5: %08lx R6: %08lx R7: %08lx\n", fp->r4, fp->r5, fp->r6, fp->r7); debug("P0: %08lx P1: %08lx P2: %08lx P3: %08lx\n", fp->p0, fp->p1, fp->p2, fp->p3); debug("P4: %08lx P5: %08lx FP: %08lx\n", fp->p4, fp->p5, fp->fp); debug("A0.w: %08lx A0.x: %08lx A1.w: %08lx A1.x: %08lx\n", fp->a0w, fp->a0x, fp->a1w, fp->a1x); debug("LB0: %08lx LT0: %08lx LC0: %08lx\n", fp->lb0, fp->lt0, fp->lc0); debug("LB1: %08lx LT1: %08lx LC1: %08lx\n", fp->lb1, fp->lt1, fp->lc1); debug("B0: %08lx L0: %08lx M0: %08lx I0: %08lx\n", fp->b0, fp->l0, fp->m0, fp->i0); debug("B1: %08lx L1: %08lx M1: %08lx I1: %08lx\n", fp->b1, fp->l1, fp->m1, fp->i1); debug("B2: %08lx L2: %08lx M2: %08lx I2: %08lx\n", fp->b2, fp->l2, fp->m2, fp->i2); debug("B3: %08lx L3: %08lx M3: %08lx I3: %08lx\n", fp->b3, fp->l3, fp->m3, fp->i3); debug("DCPLB_FAULT_ADDR=%p\n", *pDCPLB_FAULT_ADDR); debug("ICPLB_FAULT_ADDR=%p\n", *pICPLB_FAULT_ADDR); }