From 13fe24f37df20e580a5a364e67ec8cf3219d8f8c Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Sun, 27 Jan 2008 15:38:56 +0800 Subject: [Blackfin] arch: fix bug - trap_tests fails to recover on some tests. http://blackfin.uclinux.org/gf/project/uclinux-dist/tracker/?action=TrackerItemEdit&tracker_item_id=3719 When the CPLBs get a miss, we do: - find a victim in the HW table - remove the victim - find the replacement in the software table - put it into the HW table. If we can't find a replacement in the software table, we accidently leave a duplicate in the HW table. This patch ensures that duplicate is marked as not valid. What we should do is find the replacement in the software table, before we find a victim in the HW table - but its too late in the release cycle to do that much restructuring of this code. Rather that duplicate code, connect Hardware Errors (irq5) into trap_c, so user space processes get killed properly. The rest of irq_panic() can be moved into traps.c (later) There is still a small corner case that causes problems when a pheriperal interrupt goes off a single cycle before a user space hardware error. This causes a kernel panic, rather than the user space process being killed. But, this checkin makes things work in 99.9% of the cases, and is a vast improvement from what is there today (which fails 100% of the time). Signed-off-by: Robin Getz Signed-off-by: Bryan Wu --- arch/blackfin/kernel/traps.c | 54 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 8 deletions(-) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c index 21a55ef19cbd..4be5ff0be60f 100644 --- a/arch/blackfin/kernel/traps.c +++ b/arch/blackfin/kernel/traps.c @@ -433,6 +433,36 @@ asmlinkage void trap_c(struct pt_regs *fp) /* 0x3D - Reserved, Caught by default */ /* 0x3E - Reserved, Caught by default */ /* 0x3F - Reserved, Caught by default */ + case VEC_HWERR: + info.si_code = BUS_ADRALN; + sig = SIGBUS; + switch (fp->seqstat & SEQSTAT_HWERRCAUSE) { + /* System MMR Error */ + case (SEQSTAT_HWERRCAUSE_SYSTEM_MMR): + info.si_code = BUS_ADRALN; + sig = SIGBUS; + printk(KERN_NOTICE HWC_x2(KERN_NOTICE)); + break; + /* External Memory Addressing Error */ + case (SEQSTAT_HWERRCAUSE_EXTERN_ADDR): + info.si_code = BUS_ADRERR; + sig = SIGBUS; + printk(KERN_NOTICE HWC_x3(KERN_NOTICE)); + break; + /* Performance Monitor Overflow */ + case (SEQSTAT_HWERRCAUSE_PERF_FLOW): + printk(KERN_NOTICE HWC_x12(KERN_NOTICE)); + break; + /* RAISE 5 instruction */ + case (SEQSTAT_HWERRCAUSE_RAISE_5): + printk(KERN_NOTICE HWC_x18(KERN_NOTICE)); + break; + default: /* Reserved */ + printk(KERN_NOTICE HWC_default(KERN_NOTICE)); + break; + } + CHK_DEBUGGER_TRAP(); + break; default: info.si_code = TRAP_ILLTRAP; sig = SIGTRAP; @@ -447,7 +477,11 @@ asmlinkage void trap_c(struct pt_regs *fp) if (sig != SIGTRAP) { unsigned long stack; dump_bfin_process(fp); - dump_bfin_mem((void *)fp->retx); + /* Is it an interrupt, or an exception? */ + if (trapnr == VEC_HWERR) + dump_bfin_mem((void *)fp->pc); + else + dump_bfin_mem((void *)fp->retx); show_regs(fp); /* Print out the trace buffer if it makes sense */ @@ -672,12 +706,11 @@ void dump_bfin_mem(void *retaddr) * context, which should mean an oops is happening */ if (oops_in_progress && x >= 0x0040 && x <= 0x0047 && i <= 0) - panic("\n\nWARNING : You should reconfigure" + printk(KERN_EMERG "\n" + KERN_EMERG "WARNING : You should reconfigure" " the kernel to turn on\n" - " 'Hardware error interrupt" - " debugging'\n" - " The rest of this error" - " is meanless\n"); + KERN_EMERG " 'Hardware error interrupt debugging'\n" + KERN_EMERG " The rest of this error is meanless\n"); #endif if (i == (unsigned int)retaddr) printk("[%04x]", x); @@ -698,6 +731,10 @@ void show_regs(struct pt_regs *fp) printk(KERN_NOTICE "\n" KERN_NOTICE "SEQUENCER STATUS:\n"); printk(KERN_NOTICE " SEQSTAT: %08lx IPEND: %04lx SYSCFG: %04lx\n", (long)fp->seqstat, fp->ipend, fp->syscfg); + printk(KERN_NOTICE " HWERRCAUSE: 0x%lx\n", + (fp->seqstat & SEQSTAT_HWERRCAUSE) >> 14); + printk(KERN_NOTICE " EXCAUSE : 0x%lx\n", + fp->seqstat & SEQSTAT_EXCAUSE); decode_address(buf, fp->rete); printk(KERN_NOTICE " RETE: %s\n", buf); @@ -708,9 +745,10 @@ void show_regs(struct pt_regs *fp) decode_address(buf, fp->rets); printk(KERN_NOTICE " RETS: %s\n", buf); decode_address(buf, fp->pc); - printk(KERN_NOTICE " PC: %s\n", buf); + printk(KERN_NOTICE " PC : %s\n", buf); - if ((long)fp->seqstat & SEQSTAT_EXCAUSE) { + if (((long)fp->seqstat & SEQSTAT_EXCAUSE) && + (((long)fp->seqstat & SEQSTAT_EXCAUSE) != VEC_HWERR)) { decode_address(buf, bfin_read_DCPLB_FAULT_ADDR()); printk(KERN_NOTICE "DCPLB_FAULT_ADDR: %s\n", buf); decode_address(buf, bfin_read_ICPLB_FAULT_ADDR()); -- cgit v1.2.1 From d8f66c8c1ea8e948483ee4739ad91120f5f7de51 Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Mon, 24 Dec 2007 15:27:56 +0800 Subject: [Blackfin] arch: fix bug gdb testing on hardware has regression http://blackfin.uclinux.org/gf/project/toolchain/tracker/?action=TrackerItemEdit&tracker_item_id=3651 As Bernd predicted, this was only necessary because of other problems in the kenel - fixing those, and this is not necessary, so remove it. Signed-off-by: Robin Getz Signed-off-by: Bryan Wu --- arch/blackfin/kernel/traps.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c index 4be5ff0be60f..8bbfef31666b 100644 --- a/arch/blackfin/kernel/traps.c +++ b/arch/blackfin/kernel/traps.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -508,13 +509,6 @@ asmlinkage void trap_c(struct pt_regs *fp) info.si_addr = (void *)fp->pc; force_sig_info(sig, &info, current); - /* Ensure that bad return addresses don't end up in an infinite - * loop, due to speculative loads/reads. This needs to be done after - * the signal has been sent. - */ - if (trapnr == VEC_CPLB_I_M && sig != SIGTRAP) - fp->pc = SAFE_USER_INSTRUCTION; - trace_buffer_restore(j); return; } @@ -727,6 +721,9 @@ void dump_bfin_mem(void *retaddr) void show_regs(struct pt_regs *fp) { char buf [150]; + struct irqaction *action; + unsigned int i; + unsigned long flags; printk(KERN_NOTICE "\n" KERN_NOTICE "SEQUENCER STATUS:\n"); printk(KERN_NOTICE " SEQSTAT: %08lx IPEND: %04lx SYSCFG: %04lx\n", @@ -735,6 +732,32 @@ void show_regs(struct pt_regs *fp) (fp->seqstat & SEQSTAT_HWERRCAUSE) >> 14); printk(KERN_NOTICE " EXCAUSE : 0x%lx\n", fp->seqstat & SEQSTAT_EXCAUSE); + for (i = 6; i <= 15 ; i++) { + if (fp->ipend & (1 << i)) { + decode_address(buf, bfin_read32(EVT0 + 4*i)); + printk(KERN_NOTICE " physical IVG%i asserted : %s\n", i, buf); + } + } + + /* if no interrupts are going off, don't print this out */ + if (fp->ipend & ~0x3F) { + for (i = 0; i < (NR_IRQS - 1); i++) { + spin_lock_irqsave(&irq_desc[i].lock, flags); + action = irq_desc[i].action; + if (!action) + goto unlock; + + decode_address(buf, (unsigned int)action->handler); + printk(KERN_NOTICE " logical irq %3d mapped : %s", i, buf); + for (action = action->next; action; action = action->next) { + decode_address(buf, (unsigned int)action->handler); + printk(", %s", buf); + } + printk("\n"); +unlock: + spin_unlock_irqrestore(&irq_desc[i].lock, flags); + } + } decode_address(buf, fp->rete); printk(KERN_NOTICE " RETE: %s\n", buf); -- cgit v1.2.1 From b03b08ba9c7235861adf4dde712dade0bb756fe0 Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Sun, 23 Dec 2007 22:57:01 +0800 Subject: [Blackfin] arch: Clean up dump_bfin_mem Clean up dump_bfin_mem so that it will display content from the kernel, as well as l1 instruction, when deferred HW errors happen, print out the last frame info if it makes sense. Signed-off-by: Robin Getz Signed-off-by: Bryan Wu --- arch/blackfin/kernel/early_printk.c | 2 +- arch/blackfin/kernel/process.c | 1 + arch/blackfin/kernel/traps.c | 132 +++++++++++++++++++++++------------- 3 files changed, 86 insertions(+), 49 deletions(-) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/early_printk.c b/arch/blackfin/kernel/early_printk.c index 724f4a5a1d46..c892777267d5 100644 --- a/arch/blackfin/kernel/early_printk.c +++ b/arch/blackfin/kernel/early_printk.c @@ -205,7 +205,7 @@ asmlinkage void __init early_trap_c(struct pt_regs *fp, void *retaddr) if (likely(early_console == NULL)) setup_early_printk(DEFAULT_EARLY_PORT); - dump_bfin_mem((void *)fp->retx); + dump_bfin_mem(fp); show_regs(fp); dump_bfin_trace_buffer(); diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c index 5bf15125f0d6..fff3dd9eadda 100644 --- a/arch/blackfin/kernel/process.c +++ b/arch/blackfin/kernel/process.c @@ -327,6 +327,7 @@ void finish_atomic_sections (struct pt_regs *regs) } #if defined(CONFIG_ACCESS_CHECK) +/* Return 1 if access to memory range is OK, 0 otherwise */ int _access_ok(unsigned long addr, unsigned long size) { if (size == 0) diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c index 8bbfef31666b..c90f16825f98 100644 --- a/arch/blackfin/kernel/traps.c +++ b/arch/blackfin/kernel/traps.c @@ -39,6 +39,7 @@ #include #include #include +#include #ifdef CONFIG_KGDB # include @@ -171,7 +172,7 @@ asmlinkage void double_fault_c(struct pt_regs *fp) oops_in_progress = 1; printk(KERN_EMERG "\n" KERN_EMERG "Double Fault\n"); dump_bfin_process(fp); - dump_bfin_mem((void *)fp->retx); + dump_bfin_mem(fp); show_regs(fp); panic("Double Fault - unrecoverable event\n"); @@ -196,6 +197,10 @@ asmlinkage void trap_c(struct pt_regs *fp) * we will kernel panic, so the system reboots. * If KGDB is enabled, don't set this for kernel breakpoints */ + + /* TODO: check to see if we are in some sort of deferred HWERR + * that we should be able to recover from, not kernel panic + */ if ((bfin_read_IPEND() & 0xFFC0) #ifdef CONFIG_KGDB && trapnr != VEC_EXCPT02 @@ -478,11 +483,7 @@ asmlinkage void trap_c(struct pt_regs *fp) if (sig != SIGTRAP) { unsigned long stack; dump_bfin_process(fp); - /* Is it an interrupt, or an exception? */ - if (trapnr == VEC_HWERR) - dump_bfin_mem((void *)fp->pc); - else - dump_bfin_mem((void *)fp->retx); + dump_bfin_mem(fp); show_regs(fp); /* Print out the trace buffer if it makes sense */ @@ -644,8 +645,10 @@ void dump_bfin_process(struct pt_regs *fp) if (oops_in_progress) printk(KERN_EMERG "Kernel OOPS in progress\n"); - if (context & 0x0020) - printk(KERN_NOTICE "Deferred excecption or HW Error context\n"); + if (context & 0x0020 && (fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR) + printk(KERN_NOTICE "HW Error context\n"); + else if (context & 0x0020) + printk(KERN_NOTICE "Defered Exception context\n"); else if (context & 0x3FC0) printk(KERN_NOTICE "Interrupt context\n"); else if (context & 0x4000) @@ -673,49 +676,82 @@ void dump_bfin_process(struct pt_regs *fp) "No Valid process in current context\n"); } -void dump_bfin_mem(void *retaddr) +void dump_bfin_mem(struct pt_regs *fp) { + unsigned short *addr, *erraddr, val = 0, err = 0; + char sti = 0, buf[6]; - if (retaddr >= (void *)FIXED_CODE_START && retaddr < (void *)physical_mem_end -#if L1_CODE_LENGTH != 0 - /* FIXME: Copy the code out of L1 Instruction SRAM through dma - memcpy. */ - && !(retaddr >= (void *)L1_CODE_START - && retaddr < (void *)(L1_CODE_START + L1_CODE_LENGTH)) -#endif - ) { - int i = ((unsigned int)retaddr & 0xFFFFFFF0) - 32; - unsigned short x = 0; - printk(KERN_NOTICE "return address: [0x%p]; contents of:", retaddr); - for (; i < ((unsigned int)retaddr & 0xFFFFFFF0) + 32; i += 2) { - if (!(i & 0xF)) - printk("\n" KERN_NOTICE "0x%08x: ", i); - - if (get_user(x, (unsigned short *)i)) - break; + if (unlikely((fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR)) + erraddr = (void *)fp->pc; + else + erraddr = (void *)fp->retx; + + printk(KERN_NOTICE "return address: [0x%p]; contents of:", erraddr); + + for (addr = (unsigned short *)((unsigned long)erraddr & ~0xF) - 0x10; + addr < (unsigned short *)((unsigned long)erraddr & ~0xF) + 0x10; + addr++) { + if (!((unsigned long)addr & 0xF)) + printk("\n" KERN_NOTICE "0x%p: ", addr); + + if (get_user(val, addr)) { + if (addr >= (unsigned short *)L1_CODE_START && + addr < (unsigned short *)(L1_CODE_START + L1_CODE_LENGTH)) { + dma_memcpy(&val, addr, sizeof(val)); + sprintf(buf, "%04x", val); + } else if (addr >= (unsigned short *)FIXED_CODE_START && + addr <= (unsigned short *)memory_start) { + val = bfin_read16(addr); + sprintf(buf, "%04x", val); + } else { + val = 0; + sprintf(buf, "????"); + } + } else + sprintf(buf, "%04x", val); + + if (addr == erraddr) { + printk("[%s]", buf); + err = val; + } else + printk(" %s ", buf); + + /* Do any previous instructions turn on interrupts? */ + if (addr <= erraddr && /* in the past */ + ((val >= 0x0040 && val <= 0x0047) || /* STI instruction */ + val == 0x017b)) /* [SP++] = RETI */ + sti = 1; + } + + printk("\n"); + + /* Hardware error interrupts can be deferred */ + if (unlikely(sti && (fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR && + oops_in_progress)){ + printk(KERN_NOTICE "Looks like this was a deferred error - sorry\n"); #ifndef CONFIG_DEBUG_HWERR - /* If one of the last few instructions was a STI - * it is likely that the error occured awhile ago - * and we just noticed. This only happens in kernel - * context, which should mean an oops is happening - */ - if (oops_in_progress && x >= 0x0040 && x <= 0x0047 && i <= 0) - printk(KERN_EMERG "\n" - KERN_EMERG "WARNING : You should reconfigure" - " the kernel to turn on\n" - KERN_EMERG " 'Hardware error interrupt debugging'\n" - KERN_EMERG " The rest of this error is meanless\n"); -#endif - if (i == (unsigned int)retaddr) - printk("[%04x]", x); - else - printk(" %04x ", x); + printk(KERN_NOTICE "The remaining message may be meaningless\n" + KERN_NOTICE "You should enable CONFIG_DEBUG_HWERR to get a" + " better idea where it came from\n"); +#else + /* If we are handling only one peripheral interrupt + * and current mm and pid are valid, and the last error + * was in that user space process's text area + * print it out - because that is where the problem exists + */ + if ((!(((fp)->ipend & ~0x30) & (((fp)->ipend & ~0x30) - 1))) && + (current->pid && current->mm)) { + /* And the last RETI points to the current userspace context */ + if ((fp + 1)->pc >= current->mm->start_code && + (fp + 1)->pc <= current->mm->end_code) { + printk(KERN_NOTICE "It might be better to look around here : \n"); + printk(KERN_NOTICE "-------------------------------------------\n"); + show_regs(fp + 1); + printk(KERN_NOTICE "-------------------------------------------\n"); + } } - printk("\n"); - } else - printk("\n" KERN_NOTICE - "Cannot look at the [PC] <%p> for it is" - " in unreadable memory - sorry\n", retaddr); +#endif + } } void show_regs(struct pt_regs *fp) @@ -885,7 +921,7 @@ void panic_cplb_error(int cplb_panic, struct pt_regs *fp) printk(KERN_EMERG "DCPLB_FAULT_ADDR=%p\n", (void *)bfin_read_DCPLB_FAULT_ADDR()); printk(KERN_EMERG "ICPLB_FAULT_ADDR=%p\n", (void *)bfin_read_ICPLB_FAULT_ADDR()); dump_bfin_process(fp); - dump_bfin_mem((void *)fp->retx); + dump_bfin_mem(fp); show_regs(fp); dump_stack(); panic("Unrecoverable event\n"); -- cgit v1.2.1 From 54a1668ce53fe701f1d36651b591ced388e97275 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 24 Dec 2007 14:59:03 +0800 Subject: [Blackfin] arch: scrub dead alive/idle LED code if it does get re-added, it needs to be in the boards directory, not common code ... or it needs a re-implementation Signed-off-by: Mike Frysinger Signed-off-by: Bryan Wu --- arch/blackfin/kernel/process.c | 31 ------------------- arch/blackfin/kernel/setup.c | 2 -- arch/blackfin/kernel/time.c | 70 ------------------------------------------ 3 files changed, 103 deletions(-) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c index fff3dd9eadda..023dc80af187 100644 --- a/arch/blackfin/kernel/process.c +++ b/arch/blackfin/kernel/process.c @@ -39,9 +39,6 @@ #include #include -#define LED_ON 0 -#define LED_OFF 1 - asmlinkage void ret_from_fork(void); /* Points to the SDRAM backup memory for the stack that is currently in @@ -69,32 +66,6 @@ EXPORT_SYMBOL(pm_idle); void (*pm_power_off)(void) = NULL; EXPORT_SYMBOL(pm_power_off); -/* - * We are using a different LED from the one used to indicate timer interrupt. - */ -#if defined(CONFIG_BFIN_IDLE_LED) -static inline void leds_switch(int flag) -{ - unsigned short tmp = 0; - - tmp = bfin_read_CONFIG_BFIN_IDLE_LED_PORT(); - SSYNC(); - - if (flag == LED_ON) - tmp &= ~CONFIG_BFIN_IDLE_LED_PIN; /* light on */ - else - tmp |= CONFIG_BFIN_IDLE_LED_PIN; /* light off */ - - bfin_write_CONFIG_BFIN_IDLE_LED_PORT(tmp); - SSYNC(); - -} -#else -static inline void leds_switch(int flag) -{ -} -#endif - /* * The idle loop on BFIN */ @@ -106,12 +77,10 @@ void cpu_idle(void)__attribute__((l1_text)); void default_idle(void) { while (!need_resched()) { - leds_switch(LED_OFF); local_irq_disable(); if (likely(!need_resched())) idle_with_irq_disabled(); local_irq_enable(); - leds_switch(LED_ON); } } diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c index d2822010b7ce..a03c2dfff4a3 100644 --- a/arch/blackfin/kernel/setup.c +++ b/arch/blackfin/kernel/setup.c @@ -315,8 +315,6 @@ void __init setup_arch(char **cmdline_p) init_mm.end_data = (unsigned long)_edata; init_mm.brk = (unsigned long)0; - init_leds(); - _bfin_swrst = bfin_read_SWRST(); if (_bfin_swrst & RESET_DOUBLE) diff --git a/arch/blackfin/kernel/time.c b/arch/blackfin/kernel/time.c index beef057bd1dc..5bd64e341df3 100644 --- a/arch/blackfin/kernel/time.c +++ b/arch/blackfin/kernel/time.c @@ -42,75 +42,6 @@ static void time_sched_init(irqreturn_t(*timer_routine) (int, void *)); static unsigned long gettimeoffset(void); -static inline void do_leds(void); - -#if (defined(CONFIG_BFIN_ALIVE_LED) || defined(CONFIG_BFIN_IDLE_LED)) -void __init init_leds(void) -{ - unsigned int tmp = 0; - -#if defined(CONFIG_BFIN_ALIVE_LED) - /* config pins as output. */ - tmp = bfin_read_CONFIG_BFIN_ALIVE_LED_DPORT(); - SSYNC(); - bfin_write_CONFIG_BFIN_ALIVE_LED_DPORT(tmp | CONFIG_BFIN_ALIVE_LED_PIN); - SSYNC(); - - /* First set led be off */ - tmp = bfin_read_CONFIG_BFIN_ALIVE_LED_PORT(); - SSYNC(); - bfin_write_CONFIG_BFIN_ALIVE_LED_PORT(tmp | CONFIG_BFIN_ALIVE_LED_PIN); /* light off */ - SSYNC(); -#endif - -#if defined(CONFIG_BFIN_IDLE_LED) - /* config pins as output. */ - tmp = bfin_read_CONFIG_BFIN_IDLE_LED_DPORT(); - SSYNC(); - bfin_write_CONFIG_BFIN_IDLE_LED_DPORT(tmp | CONFIG_BFIN_IDLE_LED_PIN); - SSYNC(); - - /* First set led be off */ - tmp = bfin_read_CONFIG_BFIN_IDLE_LED_PORT(); - SSYNC(); - bfin_write_CONFIG_BFIN_IDLE_LED_PORT(tmp | CONFIG_BFIN_IDLE_LED_PIN); /* light off */ - SSYNC(); -#endif -} -#else -void __init init_leds(void) -{ -} -#endif - -#if defined(CONFIG_BFIN_ALIVE_LED) -static inline void do_leds(void) -{ - static unsigned int count = 50; - static int flag; - unsigned short tmp = 0; - - if (--count == 0) { - count = 50; - flag = ~flag; - } - tmp = bfin_read_CONFIG_BFIN_ALIVE_LED_PORT(); - SSYNC(); - - if (flag) - tmp &= ~CONFIG_BFIN_ALIVE_LED_PIN; /* light on */ - else - tmp |= CONFIG_BFIN_ALIVE_LED_PIN; /* light off */ - - bfin_write_CONFIG_BFIN_ALIVE_LED_PORT(tmp); - SSYNC(); - -} -#else -static inline void do_leds(void) -{ -} -#endif static struct irqaction bfin_timer_irq = { .name = "BFIN Timer Tick", @@ -205,7 +136,6 @@ irqreturn_t timer_interrupt(int irq, void *dummy) write_seqlock(&xtime_lock); do_timer(1); - do_leds(); #ifndef CONFIG_SMP update_process_times(user_mode(get_irq_regs())); -- cgit v1.2.1 From 1545a1111a02b5aafe6f141e133a6269c5741285 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 24 Dec 2007 16:54:48 +0800 Subject: [Blackfin] arch: add support for BF523/BF524/BF526 Signed-off-by: Mike Frysinger Signed-off-by: Bryan Wu --- arch/blackfin/kernel/bfin_gpio.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c index ce85d4bf34ca..ea13d2a22d69 100644 --- a/arch/blackfin/kernel/bfin_gpio.c +++ b/arch/blackfin/kernel/bfin_gpio.c @@ -83,6 +83,7 @@ #include #include #include +#include #include #include #include @@ -1194,3 +1195,36 @@ void bfin_gpio_reset_spi0_ssel1(void) } #endif /*BF548_FAMILY */ + +#if defined(CONFIG_PROC_FS) +static int gpio_proc_read(char *buf, char **start, off_t offset, + int len, int *unused_i, void *unused_v) +{ + int c, outlen = 0; + + for (c = 0; c < MAX_RESOURCES; c++) { + if (!check_gpio(c) && (reserved_gpio_map[gpio_bank(c)] & gpio_bit(c))) + len = sprintf(buf, "GPIO_%d: %s \tGPIO %s\n", c, + get_label(c), get_gpio_dir(c) ? "OUTPUT" : "INPUT"); + else if (reserved_peri_map[gpio_bank(c)] & gpio_bit(c)) + len = sprintf(buf, "GPIO_%d: %s \tPeripheral\n", c, get_label(c)); + else + continue; + buf += len; + outlen += len; + } + return outlen; +} + +static __init int gpio_register_proc(void) +{ + struct proc_dir_entry *proc_gpio; + + proc_gpio = create_proc_entry("gpio", S_IRUGO, NULL); + if (proc_gpio) + proc_gpio->read_proc = gpio_proc_read; + return proc_gpio != NULL; +} + +__initcall(gpio_register_proc); +#endif -- cgit v1.2.1 From fac3cf432ef9b6bfd64b35b95afe0b7e0079da74 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Mon, 24 Dec 2007 20:07:03 +0800 Subject: [Blackfin] arch: Fix gpio label handling early serial init also utilizes the peripheral request api - however at this point bfin_gpio_init didn't allocate memory for the labels. So we always have two zombies (allocated pin functions without labels) This happens before the initcalls - We now allocate memory statically. Define MAX_RESOURCES individually for each cpu. Signed-off-by: Michael Hennerich Signed-off-by: Bryan Wu --- arch/blackfin/kernel/bfin_gpio.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c index ea13d2a22d69..ffee36910288 100644 --- a/arch/blackfin/kernel/bfin_gpio.c +++ b/arch/blackfin/kernel/bfin_gpio.c @@ -179,15 +179,13 @@ static struct gpio_port_t *gpio_array[gpio_bank(MAX_BLACKFIN_GPIOS)] = { #endif static unsigned short reserved_gpio_map[gpio_bank(MAX_BLACKFIN_GPIOS)]; -static unsigned short reserved_peri_map[gpio_bank(MAX_BLACKFIN_GPIOS + 16)]; +static unsigned short reserved_peri_map[gpio_bank(MAX_RESOURCES)]; -#define MAX_RESOURCES 256 #define RESOURCE_LABEL_SIZE 16 -struct str_ident { +static struct str_ident { char name[RESOURCE_LABEL_SIZE]; -} *str_ident; - +} str_ident[MAX_RESOURCES]; #ifdef CONFIG_PM static unsigned short wakeup_map[gpio_bank(MAX_BLACKFIN_GPIOS)]; @@ -251,6 +249,11 @@ static char *get_label(unsigned short ident) static int cmp_label(unsigned short ident, const char *label) { + if (label == NULL) { + dump_stack(); + printk(KERN_ERR "Please provide none-null label\n"); + } + if (label && str_ident) return strncmp(str_ident[ident].name, label, strlen(label)); @@ -419,12 +422,6 @@ static void default_gpio(unsigned short gpio) static int __init bfin_gpio_init(void) { - str_ident = kcalloc(MAX_RESOURCES, - sizeof(struct str_ident), GFP_KERNEL); - if (str_ident == NULL) - return -ENOMEM; - - memset(str_ident, 0, MAX_RESOURCES * sizeof(struct str_ident)); printk(KERN_INFO "Blackfin GPIO Controller\n"); @@ -785,6 +782,14 @@ void gpio_pm_restore(void) } #endif +#else /* BF548_FAMILY */ + +unsigned short get_gpio_dir(unsigned short gpio) +{ + return (0x01 & (gpio_array[gpio_bank(gpio)]->port_dir_clear >> gpio_sub_n(gpio))); +} +EXPORT_SYMBOL(get_gpio_dir); + #endif /* BF548_FAMILY */ /*********************************************************** @@ -1204,10 +1209,10 @@ static int gpio_proc_read(char *buf, char **start, off_t offset, for (c = 0; c < MAX_RESOURCES; c++) { if (!check_gpio(c) && (reserved_gpio_map[gpio_bank(c)] & gpio_bit(c))) - len = sprintf(buf, "GPIO_%d: %s \tGPIO %s\n", c, + len = sprintf(buf, "GPIO_%d: %s \t\tGPIO %s\n", c, get_label(c), get_gpio_dir(c) ? "OUTPUT" : "INPUT"); else if (reserved_peri_map[gpio_bank(c)] & gpio_bit(c)) - len = sprintf(buf, "GPIO_%d: %s \tPeripheral\n", c, get_label(c)); + len = sprintf(buf, "GPIO_%d: %s \t\tPeripheral\n", c, get_label(c)); else continue; buf += len; -- cgit v1.2.1 From 79f1ec862ae2e693b85fd7c94654ba1779ff5863 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 24 Dec 2007 20:03:51 +0800 Subject: [Blackfin] arch: Spelling fixes Signed-off-by: Joe Perches Signed-off-by: Mike Frysinger Signed-off-by: Bryan Wu --- arch/blackfin/kernel/early_printk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/early_printk.c b/arch/blackfin/kernel/early_printk.c index c892777267d5..60f67f90fe35 100644 --- a/arch/blackfin/kernel/early_printk.c +++ b/arch/blackfin/kernel/early_printk.c @@ -187,7 +187,7 @@ asmlinkage void __init init_early_exception_vectors(void) bfin_write_EVT15(early_trap); CSYNC(); - /* Set all the return from interupt, exception, NMI to a known place + /* Set all the return from interrupt, exception, NMI to a known place * so if we do a RETI, RETX or RETN by mistake - we go somewhere known * Note - don't change RETS - we are in a subroutine, or * RETE - since it might screw up if emulator is attached -- cgit v1.2.1 From a546b0ac5988348446e4fd5987df699b4c9b1f2a Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Mon, 24 Dec 2007 20:19:51 +0800 Subject: [Blackfin] arch: Fix BUG - kernel sometimes would stuck with KEYBOARD_GPIO on Make sure the SYSTEM reset completes before we issue the CORE reset Signed-off-by: Michael Hennerich Signed-off-by: Bryan Wu --- arch/blackfin/kernel/reboot.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/reboot.c b/arch/blackfin/kernel/reboot.c index ae28aac6fec1..06501a594dda 100644 --- a/arch/blackfin/kernel/reboot.c +++ b/arch/blackfin/kernel/reboot.c @@ -34,9 +34,11 @@ void bfin_reset(void) while (1) { /* initiate system soft reset with magic 0x7 */ bfin_write_SWRST(0x7); + bfin_read_SWRST(); asm("ssync;"); /* clear system soft reset */ bfin_write_SWRST(0); + bfin_read_SWRST(); asm("ssync;"); /* issue core reset */ asm("raise 1"); -- cgit v1.2.1 From 6b5eace2f15b53d5a6849078d22e78db77625929 Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Thu, 10 Jan 2008 17:57:56 +0800 Subject: [Blackfin] arch: Don't oops_in_progress if single step is comming from the kernel Don't oops_in_progress if single step is comming from the kernel, which happens if a single step occurs after a exception cause. This fixes up the remaining issues in the toolchain bug. Signed-off-by: Robin Getz Signed-off-by: Bryan Wu --- arch/blackfin/kernel/traps.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c index c90f16825f98..5e840727a258 100644 --- a/arch/blackfin/kernel/traps.c +++ b/arch/blackfin/kernel/traps.c @@ -201,9 +201,9 @@ asmlinkage void trap_c(struct pt_regs *fp) /* TODO: check to see if we are in some sort of deferred HWERR * that we should be able to recover from, not kernel panic */ - if ((bfin_read_IPEND() & 0xFFC0) + if ((bfin_read_IPEND() & 0xFFC0) && (trapnr != VEC_STEP) #ifdef CONFIG_KGDB - && trapnr != VEC_EXCPT02 + && (trapnr != VEC_EXCPT02) #endif ){ console_verbose(); -- cgit v1.2.1 From aee3a29240ad167ad7875d859506d8bb90431c70 Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Fri, 11 Jan 2008 16:53:00 +0800 Subject: [Blackfin] arch: print out list of modules if kernel is crashing and tell people if the kernel is tainted Signed-off-by: Robin Getz Signed-off-by: Bryan Wu --- arch/blackfin/kernel/traps.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c index 5e840727a258..66b5f3e3ae2a 100644 --- a/arch/blackfin/kernel/traps.c +++ b/arch/blackfin/kernel/traps.c @@ -497,6 +497,7 @@ asmlinkage void trap_c(struct pt_regs *fp) dump_bfin_trace_buffer(); show_stack(current, &stack); if (oops_in_progress) { + print_modules(); #ifndef CONFIG_ACCESS_CHECK printk(KERN_EMERG "Please turn on " "CONFIG_ACCESS_CHECK\n"); @@ -761,7 +762,7 @@ void show_regs(struct pt_regs *fp) unsigned int i; unsigned long flags; - printk(KERN_NOTICE "\n" KERN_NOTICE "SEQUENCER STATUS:\n"); + printk(KERN_NOTICE "\n" KERN_NOTICE "SEQUENCER STATUS:\t\t%s\n", print_tainted()); printk(KERN_NOTICE " SEQSTAT: %08lx IPEND: %04lx SYSCFG: %04lx\n", (long)fp->seqstat, fp->ipend, fp->syscfg); printk(KERN_NOTICE " HWERRCAUSE: 0x%lx\n", -- cgit v1.2.1 From 7a1a6d00618bce53ae88e501ff5d4b82522db926 Mon Sep 17 00:00:00 2001 From: Bernd Schmidt Date: Fri, 11 Jan 2008 16:58:44 +0800 Subject: [Blackfin] arch: move all code related to CPLB handling into a new subdirectory under kernel/ Signed-off-by: Bernd Schmidt Signed-off-by: Bryan Wu --- arch/blackfin/kernel/Makefile | 2 +- arch/blackfin/kernel/cacheinit.c | 67 --- arch/blackfin/kernel/cplb-nompu/Makefile | 8 + arch/blackfin/kernel/cplb-nompu/cacheinit.c | 67 +++ arch/blackfin/kernel/cplb-nompu/cplbhdlr.S | 130 ++++++ arch/blackfin/kernel/cplb-nompu/cplbinfo.c | 208 +++++++++ arch/blackfin/kernel/cplb-nompu/cplbinit.c | 437 +++++++++++++++++++ arch/blackfin/kernel/cplb-nompu/cplbmgr.S | 646 ++++++++++++++++++++++++++++ arch/blackfin/kernel/cplbinit.c | 437 ------------------- 9 files changed, 1497 insertions(+), 505 deletions(-) delete mode 100644 arch/blackfin/kernel/cacheinit.c create mode 100644 arch/blackfin/kernel/cplb-nompu/Makefile create mode 100644 arch/blackfin/kernel/cplb-nompu/cacheinit.c create mode 100644 arch/blackfin/kernel/cplb-nompu/cplbhdlr.S create mode 100644 arch/blackfin/kernel/cplb-nompu/cplbinfo.c create mode 100644 arch/blackfin/kernel/cplb-nompu/cplbinit.c create mode 100644 arch/blackfin/kernel/cplb-nompu/cplbmgr.S delete mode 100644 arch/blackfin/kernel/cplbinit.c (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/Makefile b/arch/blackfin/kernel/Makefile index 8a4cfb293b27..318b9b692a48 100644 --- a/arch/blackfin/kernel/Makefile +++ b/arch/blackfin/kernel/Makefile @@ -7,7 +7,7 @@ extra-y := init_task.o vmlinux.lds obj-y := \ entry.o process.o bfin_ksyms.o ptrace.o setup.o signal.o \ sys_bfin.o time.o traps.o irqchip.o dma-mapping.o flat.o \ - fixed_code.o cplbinit.o cacheinit.o reboot.o bfin_gpio.o + fixed_code.o reboot.o bfin_gpio.o obj-$(CONFIG_BFIN_GPTIMERS) += gptimers.o obj-$(CONFIG_MODULES) += module.o diff --git a/arch/blackfin/kernel/cacheinit.c b/arch/blackfin/kernel/cacheinit.c deleted file mode 100644 index 62cbba7364b0..000000000000 --- a/arch/blackfin/kernel/cacheinit.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2004-2007 Analog Devices Inc. - * - * 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, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#include -#include -#include -#include - -#if defined(CONFIG_BFIN_ICACHE) -void bfin_icache_init(void) -{ - unsigned long *table = icplb_table; - unsigned long ctrl; - int i; - - for (i = 0; i < MAX_CPLBS; i++) { - unsigned long addr = *table++; - unsigned long data = *table++; - if (addr == (unsigned long)-1) - break; - bfin_write32(ICPLB_ADDR0 + i * 4, addr); - bfin_write32(ICPLB_DATA0 + i * 4, data); - } - ctrl = bfin_read_IMEM_CONTROL(); - ctrl |= IMC | ENICPLB; - bfin_write_IMEM_CONTROL(ctrl); -} -#endif - -#if defined(CONFIG_BFIN_DCACHE) -void bfin_dcache_init(void) -{ - unsigned long *table = dcplb_table; - unsigned long ctrl; - int i; - - for (i = 0; i < MAX_CPLBS; i++) { - unsigned long addr = *table++; - unsigned long data = *table++; - if (addr == (unsigned long)-1) - break; - bfin_write32(DCPLB_ADDR0 + i * 4, addr); - bfin_write32(DCPLB_DATA0 + i * 4, data); - } - ctrl = bfin_read_DMEM_CONTROL(); - ctrl |= DMEM_CNTR; - bfin_write_DMEM_CONTROL(ctrl); -} -#endif diff --git a/arch/blackfin/kernel/cplb-nompu/Makefile b/arch/blackfin/kernel/cplb-nompu/Makefile new file mode 100644 index 000000000000..d36ea9b5382e --- /dev/null +++ b/arch/blackfin/kernel/cplb-nompu/Makefile @@ -0,0 +1,8 @@ +# +# arch/blackfin/kernel/cplb-nompu/Makefile +# + +obj-y := cplbinit.o cacheinit.o cplbhdlr.o cplbmgr.o + +obj-$(CONFIG_CPLB_INFO) += cplbinfo.o + diff --git a/arch/blackfin/kernel/cplb-nompu/cacheinit.c b/arch/blackfin/kernel/cplb-nompu/cacheinit.c new file mode 100644 index 000000000000..62cbba7364b0 --- /dev/null +++ b/arch/blackfin/kernel/cplb-nompu/cacheinit.c @@ -0,0 +1,67 @@ +/* + * Copyright 2004-2007 Analog Devices Inc. + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include + +#if defined(CONFIG_BFIN_ICACHE) +void bfin_icache_init(void) +{ + unsigned long *table = icplb_table; + unsigned long ctrl; + int i; + + for (i = 0; i < MAX_CPLBS; i++) { + unsigned long addr = *table++; + unsigned long data = *table++; + if (addr == (unsigned long)-1) + break; + bfin_write32(ICPLB_ADDR0 + i * 4, addr); + bfin_write32(ICPLB_DATA0 + i * 4, data); + } + ctrl = bfin_read_IMEM_CONTROL(); + ctrl |= IMC | ENICPLB; + bfin_write_IMEM_CONTROL(ctrl); +} +#endif + +#if defined(CONFIG_BFIN_DCACHE) +void bfin_dcache_init(void) +{ + unsigned long *table = dcplb_table; + unsigned long ctrl; + int i; + + for (i = 0; i < MAX_CPLBS; i++) { + unsigned long addr = *table++; + unsigned long data = *table++; + if (addr == (unsigned long)-1) + break; + bfin_write32(DCPLB_ADDR0 + i * 4, addr); + bfin_write32(DCPLB_DATA0 + i * 4, data); + } + ctrl = bfin_read_DMEM_CONTROL(); + ctrl |= DMEM_CNTR; + bfin_write_DMEM_CONTROL(ctrl); +} +#endif diff --git a/arch/blackfin/kernel/cplb-nompu/cplbhdlr.S b/arch/blackfin/kernel/cplb-nompu/cplbhdlr.S new file mode 100644 index 000000000000..2788532de72b --- /dev/null +++ b/arch/blackfin/kernel/cplb-nompu/cplbhdlr.S @@ -0,0 +1,130 @@ +/* + * File: arch/blackfin/mach-common/cplbhdlr.S + * Based on: + * Author: LG Soft India + * + * Created: ? + * Description: CPLB exception handler + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +#ifdef CONFIG_EXCPT_IRQ_SYSC_L1 +.section .l1.text +#else +.text +#endif + +.type _cplb_mgr, STT_FUNC; +.type _panic_cplb_error, STT_FUNC; + +.align 2 + +ENTRY(__cplb_hdr) + R2 = SEQSTAT; + + /* Mask the contents of SEQSTAT and leave only EXCAUSE in R2 */ + R2 <<= 26; + R2 >>= 26; + + R1 = 0x23; /* Data access CPLB protection violation */ + CC = R2 == R1; + IF !CC JUMP .Lnot_data_write; + R0 = 2; /* is a write to data space*/ + JUMP .Lis_icplb_miss; + +.Lnot_data_write: + R1 = 0x2C; /* CPLB miss on an instruction fetch */ + CC = R2 == R1; + R0 = 0; /* is_data_miss == False*/ + IF CC JUMP .Lis_icplb_miss; + + R1 = 0x26; + CC = R2 == R1; + IF !CC JUMP .Lunknown; + + R0 = 1; /* is_data_miss == True*/ + +.Lis_icplb_miss: + +#if defined(CONFIG_BFIN_ICACHE) || defined(CONFIG_BFIN_DCACHE) +# if defined(CONFIG_BFIN_ICACHE) && !defined(CONFIG_BFIN_DCACHE) + R1 = CPLB_ENABLE_ICACHE; +# endif +# if !defined(CONFIG_BFIN_ICACHE) && defined(CONFIG_BFIN_DCACHE) + R1 = CPLB_ENABLE_DCACHE; +# endif +# if defined(CONFIG_BFIN_ICACHE) && defined(CONFIG_BFIN_DCACHE) + R1 = CPLB_ENABLE_DCACHE | CPLB_ENABLE_ICACHE; +# endif +#else + R1 = 0; +#endif + + [--SP] = RETS; + CALL _cplb_mgr; + RETS = [SP++]; + CC = R0 == 0; + IF !CC JUMP .Lnot_replaced; + RTS; + +/* + * Diagnostic exception handlers + */ +.Lunknown: + R0 = CPLB_UNKNOWN_ERR; + JUMP .Lcplb_error; + +.Lnot_replaced: + CC = R0 == CPLB_NO_UNLOCKED; + IF !CC JUMP .Lnext_check; + R0 = CPLB_NO_UNLOCKED; + JUMP .Lcplb_error; + +.Lnext_check: + CC = R0 == CPLB_NO_ADDR_MATCH; + IF !CC JUMP .Lnext_check2; + R0 = CPLB_NO_ADDR_MATCH; + JUMP .Lcplb_error; + +.Lnext_check2: + CC = R0 == CPLB_PROT_VIOL; + IF !CC JUMP .Lstrange_return_from_cplb_mgr; + R0 = CPLB_PROT_VIOL; + JUMP .Lcplb_error; + +.Lstrange_return_from_cplb_mgr: + IDLE; + CSYNC; + JUMP .Lstrange_return_from_cplb_mgr; + +.Lcplb_error: + R1 = sp; + SP += -12; + call _panic_cplb_error; + SP += 12; + JUMP _handle_bad_cplb; + +ENDPROC(__cplb_hdr) diff --git a/arch/blackfin/kernel/cplb-nompu/cplbinfo.c b/arch/blackfin/kernel/cplb-nompu/cplbinfo.c new file mode 100644 index 000000000000..a4f0b428a34d --- /dev/null +++ b/arch/blackfin/kernel/cplb-nompu/cplbinfo.c @@ -0,0 +1,208 @@ +/* + * File: arch/blackfin/mach-common/cplbinfo.c + * Based on: + * Author: Sonic Zhang + * + * Created: Jan. 2005 + * Description: Display CPLB status + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define CPLB_I 1 +#define CPLB_D 2 + +#define SYNC_SYS SSYNC() +#define SYNC_CORE CSYNC() + +#define CPLB_BIT_PAGESIZE 0x30000 + +static int page_size_table[4] = { + 0x00000400, /* 1K */ + 0x00001000, /* 4K */ + 0x00100000, /* 1M */ + 0x00400000 /* 4M */ +}; + +static char page_size_string_table[][4] = { "1K", "4K", "1M", "4M" }; + +static int cplb_find_entry(unsigned long *cplb_addr, + unsigned long *cplb_data, unsigned long addr, + unsigned long data) +{ + int ii; + + for (ii = 0; ii < 16; ii++) + if (addr >= cplb_addr[ii] && addr < cplb_addr[ii] + + page_size_table[(cplb_data[ii] & CPLB_BIT_PAGESIZE) >> 16] + && (cplb_data[ii] == data)) + return ii; + + return -1; +} + +static char *cplb_print_entry(char *buf, int type) +{ + unsigned long *p_addr = dpdt_table; + unsigned long *p_data = dpdt_table + 1; + unsigned long *p_icount = dpdt_swapcount_table; + unsigned long *p_ocount = dpdt_swapcount_table + 1; + unsigned long *cplb_addr = (unsigned long *)DCPLB_ADDR0; + unsigned long *cplb_data = (unsigned long *)DCPLB_DATA0; + int entry = 0, used_cplb = 0; + + if (type == CPLB_I) { + buf += sprintf(buf, "Instruction CPLB entry:\n"); + p_addr = ipdt_table; + p_data = ipdt_table + 1; + p_icount = ipdt_swapcount_table; + p_ocount = ipdt_swapcount_table + 1; + cplb_addr = (unsigned long *)ICPLB_ADDR0; + cplb_data = (unsigned long *)ICPLB_DATA0; + } else + buf += sprintf(buf, "Data CPLB entry:\n"); + + buf += sprintf(buf, "Address\t\tData\tSize\tValid\tLocked\tSwapin\tiCount\toCount\n"); + + while (*p_addr != 0xffffffff) { + entry = cplb_find_entry(cplb_addr, cplb_data, *p_addr, *p_data); + if (entry >= 0) + used_cplb |= 1 << entry; + + buf += + sprintf(buf, + "0x%08lx\t0x%05lx\t%s\t%c\t%c\t%2d\t%ld\t%ld\n", + *p_addr, *p_data, + page_size_string_table[(*p_data & 0x30000) >> 16], + (*p_data & CPLB_VALID) ? 'Y' : 'N', + (*p_data & CPLB_LOCK) ? 'Y' : 'N', entry, *p_icount, + *p_ocount); + + p_addr += 2; + p_data += 2; + p_icount += 2; + p_ocount += 2; + } + + if (used_cplb != 0xffff) { + buf += sprintf(buf, "Unused/mismatched CPLBs:\n"); + + for (entry = 0; entry < 16; entry++) + if (0 == ((1 << entry) & used_cplb)) { + int flags = cplb_data[entry]; + buf += + sprintf(buf, + "%2d: 0x%08lx\t0x%05x\t%s\t%c\t%c\n", + entry, cplb_addr[entry], flags, + page_size_string_table[(flags & + 0x30000) >> + 16], + (flags & CPLB_VALID) ? 'Y' : 'N', + (flags & CPLB_LOCK) ? 'Y' : 'N'); + } + } + + buf += sprintf(buf, "\n"); + + return buf; +} + +static int cplbinfo_proc_output(char *buf) +{ + char *p; + + p = buf; + + p += sprintf(p, "------------------ CPLB Information ------------------\n\n"); + + if (bfin_read_IMEM_CONTROL() & ENICPLB) + p = cplb_print_entry(p, CPLB_I); + else + p += sprintf(p, "Instruction CPLB is disabled.\n\n"); + + if (bfin_read_DMEM_CONTROL() & ENDCPLB) + p = cplb_print_entry(p, CPLB_D); + else + p += sprintf(p, "Data CPLB is disabled.\n"); + + return p - buf; +} + +static int cplbinfo_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = cplbinfo_proc_output(page); + if (len <= off + count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; +} + +static int cplbinfo_write_proc(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + printk(KERN_INFO "Reset the CPLB swap in/out counts.\n"); + memset(ipdt_swapcount_table, 0, MAX_SWITCH_I_CPLBS * sizeof(unsigned long)); + memset(dpdt_swapcount_table, 0, MAX_SWITCH_D_CPLBS * sizeof(unsigned long)); + + return count; +} + +static int __init cplbinfo_init(void) +{ + struct proc_dir_entry *entry; + + entry = create_proc_entry("cplbinfo", 0, NULL); + if (!entry) + return -ENOMEM; + + entry->read_proc = cplbinfo_read_proc; + entry->write_proc = cplbinfo_write_proc; + entry->data = NULL; + + return 0; +} + +static void __exit cplbinfo_exit(void) +{ + remove_proc_entry("cplbinfo", NULL); +} + +module_init(cplbinfo_init); +module_exit(cplbinfo_exit); diff --git a/arch/blackfin/kernel/cplb-nompu/cplbinit.c b/arch/blackfin/kernel/cplb-nompu/cplbinit.c new file mode 100644 index 000000000000..6320bc45fbba --- /dev/null +++ b/arch/blackfin/kernel/cplb-nompu/cplbinit.c @@ -0,0 +1,437 @@ +/* + * Blackfin CPLB initialization + * + * Copyright 2004-2007 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include +#include +#include + +u_long icplb_table[MAX_CPLBS + 1]; +u_long dcplb_table[MAX_CPLBS + 1]; + +#ifdef CONFIG_CPLB_SWITCH_TAB_L1 +# define PDT_ATTR __attribute__((l1_data)) +#else +# define PDT_ATTR +#endif + +u_long ipdt_table[MAX_SWITCH_I_CPLBS + 1] PDT_ATTR; +u_long dpdt_table[MAX_SWITCH_D_CPLBS + 1] PDT_ATTR; + +#ifdef CONFIG_CPLB_INFO +u_long ipdt_swapcount_table[MAX_SWITCH_I_CPLBS] PDT_ATTR; +u_long dpdt_swapcount_table[MAX_SWITCH_D_CPLBS] PDT_ATTR; +#endif + +struct s_cplb { + struct cplb_tab init_i; + struct cplb_tab init_d; + struct cplb_tab switch_i; + struct cplb_tab switch_d; +}; + +#if defined(CONFIG_BFIN_DCACHE) || defined(CONFIG_BFIN_ICACHE) +static struct cplb_desc cplb_data[] = { + { + .start = 0, + .end = SIZE_1K, + .psize = SIZE_1K, + .attr = INITIAL_T | SWITCH_T | I_CPLB | D_CPLB, + .i_conf = SDRAM_OOPS, + .d_conf = SDRAM_OOPS, +#if defined(CONFIG_DEBUG_HUNT_FOR_ZERO) + .valid = 1, +#else + .valid = 0, +#endif + .name = "Zero Pointer Guard Page", + }, + { + .start = L1_CODE_START, + .end = L1_CODE_START + L1_CODE_LENGTH, + .psize = SIZE_4M, + .attr = INITIAL_T | SWITCH_T | I_CPLB, + .i_conf = L1_IMEMORY, + .d_conf = 0, + .valid = 1, + .name = "L1 I-Memory", + }, + { + .start = L1_DATA_A_START, + .end = L1_DATA_B_START + L1_DATA_B_LENGTH, + .psize = SIZE_4M, + .attr = INITIAL_T | SWITCH_T | D_CPLB, + .i_conf = 0, + .d_conf = L1_DMEMORY, +#if ((L1_DATA_A_LENGTH > 0) || (L1_DATA_B_LENGTH > 0)) + .valid = 1, +#else + .valid = 0, +#endif + .name = "L1 D-Memory", + }, + { + .start = 0, + .end = 0, /* dynamic */ + .psize = 0, + .attr = INITIAL_T | SWITCH_T | I_CPLB | D_CPLB, + .i_conf = SDRAM_IGENERIC, + .d_conf = SDRAM_DGENERIC, + .valid = 1, + .name = "Kernel Memory", + }, + { + .start = 0, /* dynamic */ + .end = 0, /* dynamic */ + .psize = 0, + .attr = INITIAL_T | SWITCH_T | D_CPLB, + .i_conf = SDRAM_IGENERIC, + .d_conf = SDRAM_DNON_CHBL, + .valid = 1, + .name = "uClinux MTD Memory", + }, + { + .start = 0, /* dynamic */ + .end = 0, /* dynamic */ + .psize = SIZE_1M, + .attr = INITIAL_T | SWITCH_T | D_CPLB, + .d_conf = SDRAM_DNON_CHBL, + .valid = 1, + .name = "Uncached DMA Zone", + }, + { + .start = 0, /* dynamic */ + .end = 0, /* dynamic */ + .psize = 0, + .attr = SWITCH_T | D_CPLB, + .i_conf = 0, /* dynamic */ + .d_conf = 0, /* dynamic */ + .valid = 1, + .name = "Reserved Memory", + }, + { + .start = ASYNC_BANK0_BASE, + .end = ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE, + .psize = 0, + .attr = SWITCH_T | D_CPLB, + .d_conf = SDRAM_EBIU, + .valid = 1, + .name = "Asynchronous Memory Banks", + }, + { +#ifdef L2_START + .start = L2_START, + .end = L2_START + L2_LENGTH, + .psize = SIZE_1M, + .attr = SWITCH_T | I_CPLB | D_CPLB, + .i_conf = L2_MEMORY, + .d_conf = L2_MEMORY, + .valid = 1, +#else + .valid = 0, +#endif + .name = "L2 Memory", + }, + { + .start = BOOT_ROM_START, + .end = BOOT_ROM_START + BOOT_ROM_LENGTH, + .psize = SIZE_1M, + .attr = SWITCH_T | I_CPLB | D_CPLB, + .i_conf = SDRAM_IGENERIC, + .d_conf = SDRAM_DGENERIC, + .valid = 1, + .name = "On-Chip BootROM", + }, +}; + +static u16 __init lock_kernel_check(u32 start, u32 end) +{ + if ((end <= (u32) _end && end >= (u32)_stext) || + (start <= (u32) _end && start >= (u32)_stext)) + return IN_KERNEL; + return 0; +} + +static unsigned short __init +fill_cplbtab(struct cplb_tab *table, + unsigned long start, unsigned long end, + unsigned long block_size, unsigned long cplb_data) +{ + int i; + + switch (block_size) { + case SIZE_4M: + i = 3; + break; + case SIZE_1M: + i = 2; + break; + case SIZE_4K: + i = 1; + break; + case SIZE_1K: + default: + i = 0; + break; + } + + cplb_data = (cplb_data & ~(3 << 16)) | (i << 16); + + while ((start < end) && (table->pos < table->size)) { + + table->tab[table->pos++] = start; + + if (lock_kernel_check(start, start + block_size) == IN_KERNEL) + table->tab[table->pos++] = + cplb_data | CPLB_LOCK | CPLB_DIRTY; + else + table->tab[table->pos++] = cplb_data; + + start += block_size; + } + return 0; +} + +static unsigned short __init +close_cplbtab(struct cplb_tab *table) +{ + + while (table->pos < table->size) { + + table->tab[table->pos++] = 0; + table->tab[table->pos++] = 0; /* !CPLB_VALID */ + } + return 0; +} + +/* helper function */ +static void __fill_code_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_end) +{ + if (cplb_data[i].psize) { + fill_cplbtab(t, + cplb_data[i].start, + cplb_data[i].end, + cplb_data[i].psize, + cplb_data[i].i_conf); + } else { +#if defined(CONFIG_BFIN_ICACHE) + if (ANOMALY_05000263 && i == SDRAM_KERN) { + fill_cplbtab(t, + cplb_data[i].start, + cplb_data[i].end, + SIZE_4M, + cplb_data[i].i_conf); + } else +#endif + { + fill_cplbtab(t, + cplb_data[i].start, + a_start, + SIZE_1M, + cplb_data[i].i_conf); + fill_cplbtab(t, + a_start, + a_end, + SIZE_4M, + cplb_data[i].i_conf); + fill_cplbtab(t, a_end, + cplb_data[i].end, + SIZE_1M, + cplb_data[i].i_conf); + } + } +} + +static void __fill_data_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_end) +{ + if (cplb_data[i].psize) { + fill_cplbtab(t, + cplb_data[i].start, + cplb_data[i].end, + cplb_data[i].psize, + cplb_data[i].d_conf); + } else { + fill_cplbtab(t, + cplb_data[i].start, + a_start, SIZE_1M, + cplb_data[i].d_conf); + fill_cplbtab(t, a_start, + a_end, SIZE_4M, + cplb_data[i].d_conf); + fill_cplbtab(t, a_end, + cplb_data[i].end, + SIZE_1M, + cplb_data[i].d_conf); + } +} + +void __init generate_cpl_tables(void) +{ + + u16 i, j, process; + u32 a_start, a_end, as, ae, as_1m; + + struct cplb_tab *t_i = NULL; + struct cplb_tab *t_d = NULL; + struct s_cplb cplb; + + cplb.init_i.size = MAX_CPLBS; + cplb.init_d.size = MAX_CPLBS; + cplb.switch_i.size = MAX_SWITCH_I_CPLBS; + cplb.switch_d.size = MAX_SWITCH_D_CPLBS; + + cplb.init_i.pos = 0; + cplb.init_d.pos = 0; + cplb.switch_i.pos = 0; + cplb.switch_d.pos = 0; + + cplb.init_i.tab = icplb_table; + cplb.init_d.tab = dcplb_table; + cplb.switch_i.tab = ipdt_table; + cplb.switch_d.tab = dpdt_table; + + cplb_data[SDRAM_KERN].end = memory_end; + +#ifdef CONFIG_MTD_UCLINUX + cplb_data[SDRAM_RAM_MTD].start = memory_mtd_start; + cplb_data[SDRAM_RAM_MTD].end = memory_mtd_start + mtd_size; + cplb_data[SDRAM_RAM_MTD].valid = mtd_size > 0; +# if defined(CONFIG_ROMFS_FS) + cplb_data[SDRAM_RAM_MTD].attr |= I_CPLB; + + /* + * The ROMFS_FS size is often not multiple of 1MB. + * This can cause multiple CPLB sets covering the same memory area. + * This will then cause multiple CPLB hit exceptions. + * Workaround: We ensure a contiguous memory area by extending the kernel + * memory section over the mtd section. + * For ROMFS_FS memory must be covered with ICPLBs anyways. + * So there is no difference between kernel and mtd memory setup. + */ + + cplb_data[SDRAM_KERN].end = memory_mtd_start + mtd_size;; + cplb_data[SDRAM_RAM_MTD].valid = 0; + +# endif +#else + cplb_data[SDRAM_RAM_MTD].valid = 0; +#endif + + cplb_data[SDRAM_DMAZ].start = _ramend - DMA_UNCACHED_REGION; + cplb_data[SDRAM_DMAZ].end = _ramend; + + cplb_data[RES_MEM].start = _ramend; + cplb_data[RES_MEM].end = physical_mem_end; + + if (reserved_mem_dcache_on) + cplb_data[RES_MEM].d_conf = SDRAM_DGENERIC; + else + cplb_data[RES_MEM].d_conf = SDRAM_DNON_CHBL; + + if (reserved_mem_icache_on) + cplb_data[RES_MEM].i_conf = SDRAM_IGENERIC; + else + cplb_data[RES_MEM].i_conf = SDRAM_INON_CHBL; + + for (i = ZERO_P; i < ARRAY_SIZE(cplb_data); ++i) { + if (!cplb_data[i].valid) + continue; + + as_1m = cplb_data[i].start % SIZE_1M; + + /* We need to make sure all sections are properly 1M aligned + * However between Kernel Memory and the Kernel mtd section, depending on the + * rootfs size, there can be overlapping memory areas. + */ + + if (as_1m && i != L1I_MEM && i != L1D_MEM) { +#ifdef CONFIG_MTD_UCLINUX + if (i == SDRAM_RAM_MTD) { + if ((cplb_data[SDRAM_KERN].end + 1) > cplb_data[SDRAM_RAM_MTD].start) + cplb_data[SDRAM_RAM_MTD].start = (cplb_data[i].start & (-2*SIZE_1M)) + SIZE_1M; + else + cplb_data[SDRAM_RAM_MTD].start = (cplb_data[i].start & (-2*SIZE_1M)); + } else +#endif + printk(KERN_WARNING "Unaligned Start of %s at 0x%X\n", + cplb_data[i].name, cplb_data[i].start); + } + + as = cplb_data[i].start % SIZE_4M; + ae = cplb_data[i].end % SIZE_4M; + + if (as) + a_start = cplb_data[i].start + (SIZE_4M - (as)); + else + a_start = cplb_data[i].start; + + a_end = cplb_data[i].end - ae; + + for (j = INITIAL_T; j <= SWITCH_T; j++) { + + switch (j) { + case INITIAL_T: + if (cplb_data[i].attr & INITIAL_T) { + t_i = &cplb.init_i; + t_d = &cplb.init_d; + process = 1; + } else + process = 0; + break; + case SWITCH_T: + if (cplb_data[i].attr & SWITCH_T) { + t_i = &cplb.switch_i; + t_d = &cplb.switch_d; + process = 1; + } else + process = 0; + break; + default: + process = 0; + break; + } + + if (!process) + continue; + if (cplb_data[i].attr & I_CPLB) + __fill_code_cplbtab(t_i, i, a_start, a_end); + + if (cplb_data[i].attr & D_CPLB) + __fill_data_cplbtab(t_d, i, a_start, a_end); + } + } + +/* close tables */ + + close_cplbtab(&cplb.init_i); + close_cplbtab(&cplb.init_d); + + cplb.init_i.tab[cplb.init_i.pos] = -1; + cplb.init_d.tab[cplb.init_d.pos] = -1; + cplb.switch_i.tab[cplb.switch_i.pos] = -1; + cplb.switch_d.tab[cplb.switch_d.pos] = -1; + +} + +#endif + diff --git a/arch/blackfin/kernel/cplb-nompu/cplbmgr.S b/arch/blackfin/kernel/cplb-nompu/cplbmgr.S new file mode 100644 index 000000000000..f5cf3accef37 --- /dev/null +++ b/arch/blackfin/kernel/cplb-nompu/cplbmgr.S @@ -0,0 +1,646 @@ +/* + * File: arch/blackfin/mach-common/cplbmgtr.S + * Based on: + * Author: LG Soft India + * + * Created: ? + * Description: CPLB replacement routine for CPLB mismatch + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Usage: int _cplb_mgr(is_data_miss,int enable_cache) + * is_data_miss==2 => Mark as Dirty, write to the clean data page + * is_data_miss==1 => Replace a data CPLB. + * is_data_miss==0 => Replace an instruction CPLB. + * + * Returns: + * CPLB_RELOADED => Successfully updated CPLB table. + * CPLB_NO_UNLOCKED => All CPLBs are locked, so cannot be evicted. + * This indicates that the CPLBs in the configuration + * tablei are badly configured, as this should never + * occur. + * CPLB_NO_ADDR_MATCH => The address being accessed, that triggered the + * exception, is not covered by any of the CPLBs in + * the configuration table. The application is + * presumably misbehaving. + * CPLB_PROT_VIOL => The address being accessed, that triggered the + * exception, was not a first-write to a clean Write + * Back Data page, and so presumably is a genuine + * violation of the page's protection attributes. + * The application is misbehaving. + */ + +#include +#include +#include + +#ifdef CONFIG_EXCPT_IRQ_SYSC_L1 +.section .l1.text +#else +.text +#endif + +.align 2; +ENTRY(_cplb_mgr) + + [--SP]=( R7:4,P5:3 ); + + CC = R0 == 2; + IF CC JUMP .Ldcplb_write; + + CC = R0 == 0; + IF !CC JUMP .Ldcplb_miss_compare; + + /* ICPLB Miss Exception. We need to choose one of the + * currently-installed CPLBs, and replace it with one + * from the configuration table. + */ + + /* A multi-word instruction can cross a page boundary. This means the + * first part of the instruction can be in a valid page, but the + * second part is not, and hence generates the instruction miss. + * However, the fault address is for the start of the instruction, + * not the part that's in the bad page. Therefore, we have to check + * whether the fault address applies to a page that is already present + * in the table. + */ + + P4.L = LO(ICPLB_FAULT_ADDR); + P4.H = HI(ICPLB_FAULT_ADDR); + + P1 = 16; + P5.L = _page_size_table; + P5.H = _page_size_table; + + P0.L = LO(ICPLB_DATA0); + P0.H = HI(ICPLB_DATA0); + R4 = [P4]; /* Get faulting address*/ + R6 = 64; /* Advance past the fault address, which*/ + R6 = R6 + R4; /* we'll use if we find a match*/ + R3 = ((16 << 8) | 2); /* Extract mask, two bits at posn 16 */ + + R5 = 0; +.Lisearch: + + R1 = [P0-0x100]; /* Address for this CPLB */ + + R0 = [P0++]; /* Info for this CPLB*/ + CC = BITTST(R0,0); /* Is the CPLB valid?*/ + IF !CC JUMP .Lnomatch; /* Skip it, if not.*/ + CC = R4 < R1(IU); /* If fault address less than page start*/ + IF CC JUMP .Lnomatch; /* then skip this one.*/ + R2 = EXTRACT(R0,R3.L) (Z); /* Get page size*/ + P1 = R2; + P1 = P5 + (P1<<2); /* index into page-size table*/ + R2 = [P1]; /* Get the page size*/ + R1 = R1 + R2; /* and add to page start, to get page end*/ + CC = R4 < R1(IU); /* and see whether fault addr is in page.*/ + IF !CC R4 = R6; /* If so, advance the address and finish loop.*/ + IF !CC JUMP .Lisearch_done; +.Lnomatch: + /* Go around again*/ + R5 += 1; + CC = BITTST(R5, 4); /* i.e CC = R5 >= 16*/ + IF !CC JUMP .Lisearch; + +.Lisearch_done: + I0 = R4; /* Fault address we'll search for*/ + + /* set up pointers */ + P0.L = LO(ICPLB_DATA0); + P0.H = HI(ICPLB_DATA0); + + /* The replacement procedure for ICPLBs */ + + P4.L = LO(IMEM_CONTROL); + P4.H = HI(IMEM_CONTROL); + + /* Turn off CPLBs while we work, necessary according to HRM before + * modifying CPLB descriptors + */ + R5 = [P4]; /* Control Register*/ + BITCLR(R5,ENICPLB_P); + CLI R1; + SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */ + .align 8; + [P4] = R5; + SSYNC; + STI R1; + + R1 = -1; /* end point comparison */ + R3 = 16; /* counter */ + + /* Search through CPLBs for first non-locked entry */ + /* Overwrite it by moving everyone else up by 1 */ +.Licheck_lock: + R0 = [P0++]; + R3 = R3 + R1; + CC = R3 == R1; + IF CC JUMP .Lall_locked; + CC = BITTST(R0, 0); /* an invalid entry is good */ + IF !CC JUMP .Lifound_victim; + CC = BITTST(R0,1); /* but a locked entry isn't */ + IF CC JUMP .Licheck_lock; + +.Lifound_victim: +#ifdef CONFIG_CPLB_INFO + R7 = [P0 - 0x104]; + P2.L = _ipdt_table; + P2.H = _ipdt_table; + P3.L = _ipdt_swapcount_table; + P3.H = _ipdt_swapcount_table; + P3 += -4; +.Licount: + R2 = [P2]; /* address from config table */ + P2 += 8; + P3 += 8; + CC = R2==-1; + IF CC JUMP .Licount_done; + CC = R7==R2; + IF !CC JUMP .Licount; + R7 = [P3]; + R7 += 1; + [P3] = R7; + CSYNC; +.Licount_done: +#endif + LC0=R3; + LSETUP(.Lis_move,.Lie_move) LC0; +.Lis_move: + R0 = [P0]; + [P0 - 4] = R0; + R0 = [P0 - 0x100]; + [P0-0x104] = R0; +.Lie_move: + P0+=4; + + /* Clear ICPLB_DATA15, in case we don't find a replacement + * otherwise, we would have a duplicate entry, and will crash + */ + R0 = 0; + [P0 - 4] = R0; + + /* We've made space in the ICPLB table, so that ICPLB15 + * is now free to be overwritten. Next, we have to determine + * which CPLB we need to install, from the configuration + * table. This is a matter of getting the start-of-page + * addresses and page-lengths from the config table, and + * determining whether the fault address falls within that + * range. + */ + + P2.L = _ipdt_table; + P2.H = _ipdt_table; +#ifdef CONFIG_CPLB_INFO + P3.L = _ipdt_swapcount_table; + P3.H = _ipdt_swapcount_table; + P3 += -8; +#endif + P0.L = _page_size_table; + P0.H = _page_size_table; + + /* Retrieve our fault address (which may have been advanced + * because the faulting instruction crossed a page boundary). + */ + + R0 = I0; + + /* An extraction pattern, to get the page-size bits from + * the CPLB data entry. Bits 16-17, so two bits at posn 16. + */ + + R1 = ((16<<8)|2); +.Linext: R4 = [P2++]; /* address from config table */ + R2 = [P2++]; /* data from config table */ +#ifdef CONFIG_CPLB_INFO + P3 += 8; +#endif + + CC = R4 == -1; /* End of config table*/ + IF CC JUMP .Lno_page_in_table; + + /* See if failed address > start address */ + CC = R4 <= R0(IU); + IF !CC JUMP .Linext; + + /* extract page size (17:16)*/ + R3 = EXTRACT(R2, R1.L) (Z); + + /* add page size to addr to get range */ + + P5 = R3; + P5 = P0 + (P5 << 2); /* scaled, for int access*/ + R3 = [P5]; + R3 = R3 + R4; + + /* See if failed address < (start address + page size) */ + CC = R0 < R3(IU); + IF !CC JUMP .Linext; + + /* We've found a CPLB in the config table that covers + * the faulting address, so install this CPLB into the + * last entry of the table. + */ + + P1.L = LO(ICPLB_DATA15); /* ICPLB_DATA15 */ + P1.H = HI(ICPLB_DATA15); + [P1] = R2; + [P1-0x100] = R4; +#ifdef CONFIG_CPLB_INFO + R3 = [P3]; + R3 += 1; + [P3] = R3; +#endif + + /* P4 points to IMEM_CONTROL, and R5 contains its old + * value, after we disabled ICPLBS. Re-enable them. + */ + + BITSET(R5,ENICPLB_P); + CLI R2; + SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */ + .align 8; + [P4] = R5; + SSYNC; + STI R2; + + ( R7:4,P5:3 ) = [SP++]; + R0 = CPLB_RELOADED; + RTS; + +/* FAILED CASES*/ +.Lno_page_in_table: + R0 = CPLB_NO_ADDR_MATCH; + JUMP .Lfail_ret; + +.Lall_locked: + R0 = CPLB_NO_UNLOCKED; + JUMP .Lfail_ret; + +.Lprot_violation: + R0 = CPLB_PROT_VIOL; + +.Lfail_ret: + /* Make sure we turn protection/cache back on, even in the failing case */ + BITSET(R5,ENICPLB_P); + CLI R2; + SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */ + .align 8; + [P4] = R5; + SSYNC; + STI R2; + + ( R7:4,P5:3 ) = [SP++]; + RTS; + +.Ldcplb_write: + + /* if a DCPLB is marked as write-back (CPLB_WT==0), and + * it is clean (CPLB_DIRTY==0), then a write to the + * CPLB's page triggers a protection violation. We have to + * mark the CPLB as dirty, to indicate that there are + * pending writes associated with the CPLB. + */ + + P4.L = LO(DCPLB_STATUS); + P4.H = HI(DCPLB_STATUS); + P3.L = LO(DCPLB_DATA0); + P3.H = HI(DCPLB_DATA0); + R5 = [P4]; + + /* A protection violation can be caused by more than just writes + * to a clean WB page, so we have to ensure that: + * - It's a write + * - to a clean WB page + * - and is allowed in the mode the access occurred. + */ + + CC = BITTST(R5, 16); /* ensure it was a write*/ + IF !CC JUMP .Lprot_violation; + + /* to check the rest, we have to retrieve the DCPLB.*/ + + /* The low half of DCPLB_STATUS is a bit mask*/ + + R2 = R5.L (Z); /* indicating which CPLB triggered the event.*/ + R3 = 30; /* so we can use this to determine the offset*/ + R2.L = SIGNBITS R2; + R2 = R2.L (Z); /* into the DCPLB table.*/ + R3 = R3 - R2; + P4 = R3; + P3 = P3 + (P4<<2); + R3 = [P3]; /* Retrieve the CPLB*/ + + /* Now we can check whether it's a clean WB page*/ + + CC = BITTST(R3, 14); /* 0==WB, 1==WT*/ + IF CC JUMP .Lprot_violation; + CC = BITTST(R3, 7); /* 0 == clean, 1 == dirty*/ + IF CC JUMP .Lprot_violation; + + /* Check whether the write is allowed in the mode that was active.*/ + + R2 = 1<<3; /* checking write in user mode*/ + CC = BITTST(R5, 17); /* 0==was user, 1==was super*/ + R5 = CC; + R2 <<= R5; /* if was super, check write in super mode*/ + R2 = R3 & R2; + CC = R2 == 0; + IF CC JUMP .Lprot_violation; + + /* It's a genuine write-to-clean-page.*/ + + BITSET(R3, 7); /* mark as dirty*/ + [P3] = R3; /* and write back.*/ + NOP; + CSYNC; + ( R7:4,P5:3 ) = [SP++]; + R0 = CPLB_RELOADED; + RTS; + +.Ldcplb_miss_compare: + + /* Data CPLB Miss event. We need to choose a CPLB to + * evict, and then locate a new CPLB to install from the + * config table, that covers the faulting address. + */ + + P1.L = LO(DCPLB_DATA15); + P1.H = HI(DCPLB_DATA15); + + P4.L = LO(DCPLB_FAULT_ADDR); + P4.H = HI(DCPLB_FAULT_ADDR); + R4 = [P4]; + I0 = R4; + + /* The replacement procedure for DCPLBs*/ + + R6 = R1; /* Save for later*/ + + /* Turn off CPLBs while we work.*/ + P4.L = LO(DMEM_CONTROL); + P4.H = HI(DMEM_CONTROL); + R5 = [P4]; + BITCLR(R5,ENDCPLB_P); + CLI R0; + SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */ + .align 8; + [P4] = R5; + SSYNC; + STI R0; + + /* Start looking for a CPLB to evict. Our order of preference + * is: invalid CPLBs, clean CPLBs, dirty CPLBs. Locked CPLBs + * are no good. + */ + + I1.L = LO(DCPLB_DATA0); + I1.H = HI(DCPLB_DATA0); + P1 = 2; + P2 = 16; + I2.L = _dcplb_preference; + I2.H = _dcplb_preference; + LSETUP(.Lsdsearch1, .Ledsearch1) LC0 = P1; +.Lsdsearch1: + R0 = [I2++]; /* Get the bits we're interested in*/ + P0 = I1; /* Go back to start of table*/ + LSETUP (.Lsdsearch2, .Ledsearch2) LC1 = P2; +.Lsdsearch2: + R1 = [P0++]; /* Fetch each installed CPLB in turn*/ + R2 = R1 & R0; /* and test for interesting bits.*/ + CC = R2 == 0; /* If none are set, it'll do.*/ + IF !CC JUMP .Lskip_stack_check; + + R2 = [P0 - 0x104]; /* R2 - PageStart */ + P3.L = _page_size_table; /* retrieve end address */ + P3.H = _page_size_table; /* retrieve end address */ + R3 = 0x1002; /* 16th - position, 2 bits -length */ +#if ANOMALY_05000209 + nop; /* Anomaly 05000209 */ +#endif + R7 = EXTRACT(R1,R3.l); + R7 = R7 << 2; /* Page size index offset */ + P5 = R7; + P3 = P3 + P5; + R7 = [P3]; /* page size in bytes */ + + R7 = R2 + R7; /* R7 - PageEnd */ + R4 = SP; /* Test SP is in range */ + + CC = R7 < R4; /* if PageEnd < SP */ + IF CC JUMP .Ldfound_victim; + R3 = 0x284; /* stack length from start of trap till + * the point. + * 20 stack locations for future modifications + */ + R4 = R4 + R3; + CC = R4 < R2; /* if SP + stacklen < PageStart */ + IF CC JUMP .Ldfound_victim; +.Lskip_stack_check: + +.Ledsearch2: NOP; +.Ledsearch1: NOP; + + /* If we got here, we didn't find a DCPLB we considered + * replacable, which means all of them were locked. + */ + + JUMP .Lall_locked; +.Ldfound_victim: + +#ifdef CONFIG_CPLB_INFO + R7 = [P0 - 0x104]; + P2.L = _dpdt_table; + P2.H = _dpdt_table; + P3.L = _dpdt_swapcount_table; + P3.H = _dpdt_swapcount_table; + P3 += -4; +.Ldicount: + R2 = [P2]; + P2 += 8; + P3 += 8; + CC = R2==-1; + IF CC JUMP .Ldicount_done; + CC = R7==R2; + IF !CC JUMP .Ldicount; + R7 = [P3]; + R7 += 1; + [P3] = R7; +.Ldicount_done: +#endif + + /* Clean down the hardware loops*/ + R2 = 0; + LC1 = R2; + LC0 = R2; + + /* There's a suitable victim in [P0-4] (because we've + * advanced already). + */ + +.LDdoverwrite: + + /* [P0-4] is a suitable victim CPLB, so we want to + * overwrite it by moving all the following CPLBs + * one space closer to the start. + */ + + R1.L = LO(DCPLB_DATA16); /* DCPLB_DATA15 + 4 */ + R1.H = HI(DCPLB_DATA16); + R0 = P0; + + /* If the victim happens to be in DCPLB15, + * we don't need to move anything. + */ + + CC = R1 == R0; + IF CC JUMP .Lde_moved; + R1 = R1 - R0; + R1 >>= 2; + P1 = R1; + LSETUP(.Lds_move, .Lde_move) LC0=P1; +.Lds_move: + R0 = [P0++]; /* move data */ + [P0 - 8] = R0; + R0 = [P0-0x104] /* move address */ +.Lde_move: + [P0-0x108] = R0; + +.Lde_moved: + NOP; + + /* Clear DCPLB_DATA15, in case we don't find a replacement + * otherwise, we would have a duplicate entry, and will crash + */ + R0 = 0; + [P0 - 0x4] = R0; + + /* We've now made space in DCPLB15 for the new CPLB to be + * installed. The next stage is to locate a CPLB in the + * config table that covers the faulting address. + */ + + R0 = I0; /* Our faulting address */ + + P2.L = _dpdt_table; + P2.H = _dpdt_table; +#ifdef CONFIG_CPLB_INFO + P3.L = _dpdt_swapcount_table; + P3.H = _dpdt_swapcount_table; + P3 += -8; +#endif + + P1.L = _page_size_table; + P1.H = _page_size_table; + + /* An extraction pattern, to retrieve bits 17:16.*/ + + R1 = (16<<8)|2; +.Ldnext: R4 = [P2++]; /* address */ + R2 = [P2++]; /* data */ +#ifdef CONFIG_CPLB_INFO + P3 += 8; +#endif + + CC = R4 == -1; + IF CC JUMP .Lno_page_in_table; + + /* See if failed address > start address */ + CC = R4 <= R0(IU); + IF !CC JUMP .Ldnext; + + /* extract page size (17:16)*/ + R3 = EXTRACT(R2, R1.L) (Z); + + /* add page size to addr to get range */ + + P5 = R3; + P5 = P1 + (P5 << 2); + R3 = [P5]; + R3 = R3 + R4; + + /* See if failed address < (start address + page size) */ + CC = R0 < R3(IU); + IF !CC JUMP .Ldnext; + + /* We've found the CPLB that should be installed, so + * write it into CPLB15, masking off any caching bits + * if necessary. + */ + + P1.L = LO(DCPLB_DATA15); + P1.H = HI(DCPLB_DATA15); + + /* If the DCPLB has cache bits set, but caching hasn't + * been enabled, then we want to mask off the cache-in-L1 + * bit before installing. Moreover, if caching is off, we + * also want to ensure that the DCPLB has WT mode set, rather + * than WB, since WB pages still trigger first-write exceptions + * even when not caching is off, and the page isn't marked as + * cachable. Finally, we could mark the page as clean, not dirty, + * but we choose to leave that decision to the user; if the user + * chooses to have a CPLB pre-defined as dirty, then they always + * pay the cost of flushing during eviction, but don't pay the + * cost of first-write exceptions to mark the page as dirty. + */ + +#ifdef CONFIG_BFIN_WT + BITSET(R6, 14); /* Set WT*/ +#endif + + [P1] = R2; + [P1-0x100] = R4; +#ifdef CONFIG_CPLB_INFO + R3 = [P3]; + R3 += 1; + [P3] = R3; +#endif + + /* We've installed the CPLB, so re-enable CPLBs. P4 + * points to DMEM_CONTROL, and R5 is the value we + * last wrote to it, when we were disabling CPLBs. + */ + + BITSET(R5,ENDCPLB_P); + CLI R2; + .align 8; + [P4] = R5; + SSYNC; + STI R2; + + ( R7:4,P5:3 ) = [SP++]; + R0 = CPLB_RELOADED; + RTS; +ENDPROC(_cplb_mgr) + +.data +.align 4; +_page_size_table: +.byte4 0x00000400; /* 1K */ +.byte4 0x00001000; /* 4K */ +.byte4 0x00100000; /* 1M */ +.byte4 0x00400000; /* 4M */ + +.align 4; +_dcplb_preference: +.byte4 0x00000001; /* valid bit */ +.byte4 0x00000002; /* lock bit */ diff --git a/arch/blackfin/kernel/cplbinit.c b/arch/blackfin/kernel/cplbinit.c deleted file mode 100644 index 6320bc45fbba..000000000000 --- a/arch/blackfin/kernel/cplbinit.c +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Blackfin CPLB initialization - * - * Copyright 2004-2007 Analog Devices Inc. - * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * 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, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include - -#include -#include -#include - -u_long icplb_table[MAX_CPLBS + 1]; -u_long dcplb_table[MAX_CPLBS + 1]; - -#ifdef CONFIG_CPLB_SWITCH_TAB_L1 -# define PDT_ATTR __attribute__((l1_data)) -#else -# define PDT_ATTR -#endif - -u_long ipdt_table[MAX_SWITCH_I_CPLBS + 1] PDT_ATTR; -u_long dpdt_table[MAX_SWITCH_D_CPLBS + 1] PDT_ATTR; - -#ifdef CONFIG_CPLB_INFO -u_long ipdt_swapcount_table[MAX_SWITCH_I_CPLBS] PDT_ATTR; -u_long dpdt_swapcount_table[MAX_SWITCH_D_CPLBS] PDT_ATTR; -#endif - -struct s_cplb { - struct cplb_tab init_i; - struct cplb_tab init_d; - struct cplb_tab switch_i; - struct cplb_tab switch_d; -}; - -#if defined(CONFIG_BFIN_DCACHE) || defined(CONFIG_BFIN_ICACHE) -static struct cplb_desc cplb_data[] = { - { - .start = 0, - .end = SIZE_1K, - .psize = SIZE_1K, - .attr = INITIAL_T | SWITCH_T | I_CPLB | D_CPLB, - .i_conf = SDRAM_OOPS, - .d_conf = SDRAM_OOPS, -#if defined(CONFIG_DEBUG_HUNT_FOR_ZERO) - .valid = 1, -#else - .valid = 0, -#endif - .name = "Zero Pointer Guard Page", - }, - { - .start = L1_CODE_START, - .end = L1_CODE_START + L1_CODE_LENGTH, - .psize = SIZE_4M, - .attr = INITIAL_T | SWITCH_T | I_CPLB, - .i_conf = L1_IMEMORY, - .d_conf = 0, - .valid = 1, - .name = "L1 I-Memory", - }, - { - .start = L1_DATA_A_START, - .end = L1_DATA_B_START + L1_DATA_B_LENGTH, - .psize = SIZE_4M, - .attr = INITIAL_T | SWITCH_T | D_CPLB, - .i_conf = 0, - .d_conf = L1_DMEMORY, -#if ((L1_DATA_A_LENGTH > 0) || (L1_DATA_B_LENGTH > 0)) - .valid = 1, -#else - .valid = 0, -#endif - .name = "L1 D-Memory", - }, - { - .start = 0, - .end = 0, /* dynamic */ - .psize = 0, - .attr = INITIAL_T | SWITCH_T | I_CPLB | D_CPLB, - .i_conf = SDRAM_IGENERIC, - .d_conf = SDRAM_DGENERIC, - .valid = 1, - .name = "Kernel Memory", - }, - { - .start = 0, /* dynamic */ - .end = 0, /* dynamic */ - .psize = 0, - .attr = INITIAL_T | SWITCH_T | D_CPLB, - .i_conf = SDRAM_IGENERIC, - .d_conf = SDRAM_DNON_CHBL, - .valid = 1, - .name = "uClinux MTD Memory", - }, - { - .start = 0, /* dynamic */ - .end = 0, /* dynamic */ - .psize = SIZE_1M, - .attr = INITIAL_T | SWITCH_T | D_CPLB, - .d_conf = SDRAM_DNON_CHBL, - .valid = 1, - .name = "Uncached DMA Zone", - }, - { - .start = 0, /* dynamic */ - .end = 0, /* dynamic */ - .psize = 0, - .attr = SWITCH_T | D_CPLB, - .i_conf = 0, /* dynamic */ - .d_conf = 0, /* dynamic */ - .valid = 1, - .name = "Reserved Memory", - }, - { - .start = ASYNC_BANK0_BASE, - .end = ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE, - .psize = 0, - .attr = SWITCH_T | D_CPLB, - .d_conf = SDRAM_EBIU, - .valid = 1, - .name = "Asynchronous Memory Banks", - }, - { -#ifdef L2_START - .start = L2_START, - .end = L2_START + L2_LENGTH, - .psize = SIZE_1M, - .attr = SWITCH_T | I_CPLB | D_CPLB, - .i_conf = L2_MEMORY, - .d_conf = L2_MEMORY, - .valid = 1, -#else - .valid = 0, -#endif - .name = "L2 Memory", - }, - { - .start = BOOT_ROM_START, - .end = BOOT_ROM_START + BOOT_ROM_LENGTH, - .psize = SIZE_1M, - .attr = SWITCH_T | I_CPLB | D_CPLB, - .i_conf = SDRAM_IGENERIC, - .d_conf = SDRAM_DGENERIC, - .valid = 1, - .name = "On-Chip BootROM", - }, -}; - -static u16 __init lock_kernel_check(u32 start, u32 end) -{ - if ((end <= (u32) _end && end >= (u32)_stext) || - (start <= (u32) _end && start >= (u32)_stext)) - return IN_KERNEL; - return 0; -} - -static unsigned short __init -fill_cplbtab(struct cplb_tab *table, - unsigned long start, unsigned long end, - unsigned long block_size, unsigned long cplb_data) -{ - int i; - - switch (block_size) { - case SIZE_4M: - i = 3; - break; - case SIZE_1M: - i = 2; - break; - case SIZE_4K: - i = 1; - break; - case SIZE_1K: - default: - i = 0; - break; - } - - cplb_data = (cplb_data & ~(3 << 16)) | (i << 16); - - while ((start < end) && (table->pos < table->size)) { - - table->tab[table->pos++] = start; - - if (lock_kernel_check(start, start + block_size) == IN_KERNEL) - table->tab[table->pos++] = - cplb_data | CPLB_LOCK | CPLB_DIRTY; - else - table->tab[table->pos++] = cplb_data; - - start += block_size; - } - return 0; -} - -static unsigned short __init -close_cplbtab(struct cplb_tab *table) -{ - - while (table->pos < table->size) { - - table->tab[table->pos++] = 0; - table->tab[table->pos++] = 0; /* !CPLB_VALID */ - } - return 0; -} - -/* helper function */ -static void __fill_code_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_end) -{ - if (cplb_data[i].psize) { - fill_cplbtab(t, - cplb_data[i].start, - cplb_data[i].end, - cplb_data[i].psize, - cplb_data[i].i_conf); - } else { -#if defined(CONFIG_BFIN_ICACHE) - if (ANOMALY_05000263 && i == SDRAM_KERN) { - fill_cplbtab(t, - cplb_data[i].start, - cplb_data[i].end, - SIZE_4M, - cplb_data[i].i_conf); - } else -#endif - { - fill_cplbtab(t, - cplb_data[i].start, - a_start, - SIZE_1M, - cplb_data[i].i_conf); - fill_cplbtab(t, - a_start, - a_end, - SIZE_4M, - cplb_data[i].i_conf); - fill_cplbtab(t, a_end, - cplb_data[i].end, - SIZE_1M, - cplb_data[i].i_conf); - } - } -} - -static void __fill_data_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_end) -{ - if (cplb_data[i].psize) { - fill_cplbtab(t, - cplb_data[i].start, - cplb_data[i].end, - cplb_data[i].psize, - cplb_data[i].d_conf); - } else { - fill_cplbtab(t, - cplb_data[i].start, - a_start, SIZE_1M, - cplb_data[i].d_conf); - fill_cplbtab(t, a_start, - a_end, SIZE_4M, - cplb_data[i].d_conf); - fill_cplbtab(t, a_end, - cplb_data[i].end, - SIZE_1M, - cplb_data[i].d_conf); - } -} - -void __init generate_cpl_tables(void) -{ - - u16 i, j, process; - u32 a_start, a_end, as, ae, as_1m; - - struct cplb_tab *t_i = NULL; - struct cplb_tab *t_d = NULL; - struct s_cplb cplb; - - cplb.init_i.size = MAX_CPLBS; - cplb.init_d.size = MAX_CPLBS; - cplb.switch_i.size = MAX_SWITCH_I_CPLBS; - cplb.switch_d.size = MAX_SWITCH_D_CPLBS; - - cplb.init_i.pos = 0; - cplb.init_d.pos = 0; - cplb.switch_i.pos = 0; - cplb.switch_d.pos = 0; - - cplb.init_i.tab = icplb_table; - cplb.init_d.tab = dcplb_table; - cplb.switch_i.tab = ipdt_table; - cplb.switch_d.tab = dpdt_table; - - cplb_data[SDRAM_KERN].end = memory_end; - -#ifdef CONFIG_MTD_UCLINUX - cplb_data[SDRAM_RAM_MTD].start = memory_mtd_start; - cplb_data[SDRAM_RAM_MTD].end = memory_mtd_start + mtd_size; - cplb_data[SDRAM_RAM_MTD].valid = mtd_size > 0; -# if defined(CONFIG_ROMFS_FS) - cplb_data[SDRAM_RAM_MTD].attr |= I_CPLB; - - /* - * The ROMFS_FS size is often not multiple of 1MB. - * This can cause multiple CPLB sets covering the same memory area. - * This will then cause multiple CPLB hit exceptions. - * Workaround: We ensure a contiguous memory area by extending the kernel - * memory section over the mtd section. - * For ROMFS_FS memory must be covered with ICPLBs anyways. - * So there is no difference between kernel and mtd memory setup. - */ - - cplb_data[SDRAM_KERN].end = memory_mtd_start + mtd_size;; - cplb_data[SDRAM_RAM_MTD].valid = 0; - -# endif -#else - cplb_data[SDRAM_RAM_MTD].valid = 0; -#endif - - cplb_data[SDRAM_DMAZ].start = _ramend - DMA_UNCACHED_REGION; - cplb_data[SDRAM_DMAZ].end = _ramend; - - cplb_data[RES_MEM].start = _ramend; - cplb_data[RES_MEM].end = physical_mem_end; - - if (reserved_mem_dcache_on) - cplb_data[RES_MEM].d_conf = SDRAM_DGENERIC; - else - cplb_data[RES_MEM].d_conf = SDRAM_DNON_CHBL; - - if (reserved_mem_icache_on) - cplb_data[RES_MEM].i_conf = SDRAM_IGENERIC; - else - cplb_data[RES_MEM].i_conf = SDRAM_INON_CHBL; - - for (i = ZERO_P; i < ARRAY_SIZE(cplb_data); ++i) { - if (!cplb_data[i].valid) - continue; - - as_1m = cplb_data[i].start % SIZE_1M; - - /* We need to make sure all sections are properly 1M aligned - * However between Kernel Memory and the Kernel mtd section, depending on the - * rootfs size, there can be overlapping memory areas. - */ - - if (as_1m && i != L1I_MEM && i != L1D_MEM) { -#ifdef CONFIG_MTD_UCLINUX - if (i == SDRAM_RAM_MTD) { - if ((cplb_data[SDRAM_KERN].end + 1) > cplb_data[SDRAM_RAM_MTD].start) - cplb_data[SDRAM_RAM_MTD].start = (cplb_data[i].start & (-2*SIZE_1M)) + SIZE_1M; - else - cplb_data[SDRAM_RAM_MTD].start = (cplb_data[i].start & (-2*SIZE_1M)); - } else -#endif - printk(KERN_WARNING "Unaligned Start of %s at 0x%X\n", - cplb_data[i].name, cplb_data[i].start); - } - - as = cplb_data[i].start % SIZE_4M; - ae = cplb_data[i].end % SIZE_4M; - - if (as) - a_start = cplb_data[i].start + (SIZE_4M - (as)); - else - a_start = cplb_data[i].start; - - a_end = cplb_data[i].end - ae; - - for (j = INITIAL_T; j <= SWITCH_T; j++) { - - switch (j) { - case INITIAL_T: - if (cplb_data[i].attr & INITIAL_T) { - t_i = &cplb.init_i; - t_d = &cplb.init_d; - process = 1; - } else - process = 0; - break; - case SWITCH_T: - if (cplb_data[i].attr & SWITCH_T) { - t_i = &cplb.switch_i; - t_d = &cplb.switch_d; - process = 1; - } else - process = 0; - break; - default: - process = 0; - break; - } - - if (!process) - continue; - if (cplb_data[i].attr & I_CPLB) - __fill_code_cplbtab(t_i, i, a_start, a_end); - - if (cplb_data[i].attr & D_CPLB) - __fill_data_cplbtab(t_d, i, a_start, a_end); - } - } - -/* close tables */ - - close_cplbtab(&cplb.init_i); - close_cplbtab(&cplb.init_d); - - cplb.init_i.tab[cplb.init_i.pos] = -1; - cplb.init_d.tab[cplb.init_d.pos] = -1; - cplb.switch_i.tab[cplb.switch_i.pos] = -1; - cplb.switch_d.tab[cplb.switch_d.pos] = -1; - -} - -#endif - -- cgit v1.2.1 From 2047e40d724d42928c0b5994a1568c1b738efdb7 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 22 Jan 2008 15:29:18 +0800 Subject: [Blackfin] arch: set_bfin_dma_config shouldnt set SYNC or RESTART by default - add argument or option Signed-off-by: Michael Hennerich Signed-off-by: Bryan Wu --- arch/blackfin/kernel/bfin_dma_5xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/bfin_dma_5xx.c b/arch/blackfin/kernel/bfin_dma_5xx.c index b54446055a43..fa9debe8d5f4 100644 --- a/arch/blackfin/kernel/bfin_dma_5xx.c +++ b/arch/blackfin/kernel/bfin_dma_5xx.c @@ -339,13 +339,13 @@ EXPORT_SYMBOL(set_dma_config); unsigned short set_bfin_dma_config(char direction, char flow_mode, - char intr_mode, char dma_mode, char width) + char intr_mode, char dma_mode, char width, char syncmode) { unsigned short config; config = ((direction << 1) | (width << 2) | (dma_mode << 4) | - (intr_mode << 6) | (flow_mode << 12) | RESTART); + (intr_mode << 6) | (flow_mode << 12) | (syncmode << 5)); return config; } EXPORT_SYMBOL(set_bfin_dma_config); -- cgit v1.2.1 From b97b8a998397e8c64699559099fa9febffae2b4d Mon Sep 17 00:00:00 2001 From: Bernd Schmidt Date: Sun, 27 Jan 2008 18:39:16 +0800 Subject: [Blackfin] arch: Initial checkin of the memory protection support. Enable it with CONFIG_MPU. Signed-off-by: Bernd Schmidt Signed-off-by: Bryan Wu --- arch/blackfin/kernel/cplb-mpu/Makefile | 8 + arch/blackfin/kernel/cplb-mpu/cacheinit.c | 62 ++++++ arch/blackfin/kernel/cplb-mpu/cplbinfo.c | 144 +++++++++++++ arch/blackfin/kernel/cplb-mpu/cplbinit.c | 91 ++++++++ arch/blackfin/kernel/cplb-mpu/cplbmgr.c | 338 ++++++++++++++++++++++++++++++ arch/blackfin/kernel/setup.c | 10 + 6 files changed, 653 insertions(+) create mode 100644 arch/blackfin/kernel/cplb-mpu/Makefile create mode 100644 arch/blackfin/kernel/cplb-mpu/cacheinit.c create mode 100644 arch/blackfin/kernel/cplb-mpu/cplbinfo.c create mode 100644 arch/blackfin/kernel/cplb-mpu/cplbinit.c create mode 100644 arch/blackfin/kernel/cplb-mpu/cplbmgr.c (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/cplb-mpu/Makefile b/arch/blackfin/kernel/cplb-mpu/Makefile new file mode 100644 index 000000000000..286b69357f97 --- /dev/null +++ b/arch/blackfin/kernel/cplb-mpu/Makefile @@ -0,0 +1,8 @@ +# +# arch/blackfin/kernel/cplb-nompu/Makefile +# + +obj-y := cplbinit.o cacheinit.o cplbmgr.o + +obj-$(CONFIG_CPLB_INFO) += cplbinfo.o + diff --git a/arch/blackfin/kernel/cplb-mpu/cacheinit.c b/arch/blackfin/kernel/cplb-mpu/cacheinit.c new file mode 100644 index 000000000000..9eecfa403187 --- /dev/null +++ b/arch/blackfin/kernel/cplb-mpu/cacheinit.c @@ -0,0 +1,62 @@ +/* + * Copyright 2004-2007 Analog Devices Inc. + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include + +#if defined(CONFIG_BFIN_ICACHE) +void bfin_icache_init(void) +{ + unsigned long ctrl; + int i; + + SSYNC(); + for (i = 0; i < MAX_CPLBS; i++) { + bfin_write32(ICPLB_ADDR0 + i * 4, icplb_tbl[i].addr); + bfin_write32(ICPLB_DATA0 + i * 4, icplb_tbl[i].data); + } + ctrl = bfin_read_IMEM_CONTROL(); + ctrl |= IMC | ENICPLB; + bfin_write_IMEM_CONTROL(ctrl); + SSYNC(); +} +#endif + +#if defined(CONFIG_BFIN_DCACHE) +void bfin_dcache_init(void) +{ + unsigned long ctrl; + int i; + + SSYNC(); + for (i = 0; i < MAX_CPLBS; i++) { + bfin_write32(DCPLB_ADDR0 + i * 4, dcplb_tbl[i].addr); + bfin_write32(DCPLB_DATA0 + i * 4, dcplb_tbl[i].data); + } + + ctrl = bfin_read_DMEM_CONTROL(); + ctrl |= DMEM_CNTR; + bfin_write_DMEM_CONTROL(ctrl); + SSYNC(); +} +#endif diff --git a/arch/blackfin/kernel/cplb-mpu/cplbinfo.c b/arch/blackfin/kernel/cplb-mpu/cplbinfo.c new file mode 100644 index 000000000000..bd072299f7f2 --- /dev/null +++ b/arch/blackfin/kernel/cplb-mpu/cplbinfo.c @@ -0,0 +1,144 @@ +/* + * File: arch/blackfin/mach-common/cplbinfo.c + * Based on: + * Author: Sonic Zhang + * + * Created: Jan. 2005 + * Description: Display CPLB status + * + * Modified: + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define CPLB_I 1 +#define CPLB_D 2 + +#define SYNC_SYS SSYNC() +#define SYNC_CORE CSYNC() + +#define CPLB_BIT_PAGESIZE 0x30000 + +static char page_size_string_table[][4] = { "1K", "4K", "1M", "4M" }; + +static char *cplb_print_entry(char *buf, struct cplb_entry *tbl, int switched) +{ + int i; + buf += sprintf(buf, "Index\tAddress\t\tData\tSize\tU/RD\tU/WR\tS/WR\tSwitch\n"); + for (i = 0; i < MAX_CPLBS; i++) { + unsigned long data = tbl[i].data; + unsigned long addr = tbl[i].addr; + if (!(data & CPLB_VALID)) + continue; + + buf += + sprintf(buf, + "%d\t0x%08lx\t%06lx\t%s\t%c\t%c\t%c\t%c\n", + i, addr, data, + page_size_string_table[(data & 0x30000) >> 16], + (data & CPLB_USER_RD) ? 'Y' : 'N', + (data & CPLB_USER_WR) ? 'Y' : 'N', + (data & CPLB_SUPV_WR) ? 'Y' : 'N', + i < switched ? 'N' : 'Y'); + } + buf += sprintf(buf, "\n"); + + return buf; +} + +int cplbinfo_proc_output(char *buf) +{ + char *p; + + p = buf; + + p += sprintf(p, "------------------ CPLB Information ------------------\n\n"); + + if (bfin_read_IMEM_CONTROL() & ENICPLB) { + p += sprintf(p, "Instruction CPLB entry:\n"); + p = cplb_print_entry(p, icplb_tbl, first_switched_icplb); + } else + p += sprintf(p, "Instruction CPLB is disabled.\n\n"); + + if (1 || bfin_read_DMEM_CONTROL() & ENDCPLB) { + p += sprintf(p, "Data CPLB entry:\n"); + p = cplb_print_entry(p, dcplb_tbl, first_switched_dcplb); + } else + p += sprintf(p, "Data CPLB is disabled.\n"); + + p += sprintf(p, "ICPLB miss: %d\nICPLB supervisor miss: %d\n", + nr_icplb_miss, nr_icplb_supv_miss); + p += sprintf(p, "DCPLB miss: %d\nDCPLB protection fault:%d\n", + nr_dcplb_miss, nr_dcplb_prot); + p += sprintf(p, "CPLB flushes: %d\n", + nr_cplb_flush); + + return p - buf; +} + +static int cplbinfo_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = cplbinfo_proc_output(page); + if (len <= off + count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; +} + +static int __init cplbinfo_init(void) +{ + struct proc_dir_entry *entry; + + entry = create_proc_entry("cplbinfo", 0, NULL); + if (!entry) + return -ENOMEM; + + entry->read_proc = cplbinfo_read_proc; + entry->data = NULL; + + return 0; +} + +static void __exit cplbinfo_exit(void) +{ + remove_proc_entry("cplbinfo", NULL); +} + +module_init(cplbinfo_init); +module_exit(cplbinfo_exit); diff --git a/arch/blackfin/kernel/cplb-mpu/cplbinit.c b/arch/blackfin/kernel/cplb-mpu/cplbinit.c new file mode 100644 index 000000000000..e2e2b5079f5b --- /dev/null +++ b/arch/blackfin/kernel/cplb-mpu/cplbinit.c @@ -0,0 +1,91 @@ +/* + * Blackfin CPLB initialization + * + * Copyright 2004-2007 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include +#include +#include + +struct cplb_entry icplb_tbl[MAX_CPLBS]; +struct cplb_entry dcplb_tbl[MAX_CPLBS]; + +int first_switched_icplb, first_switched_dcplb; +int first_mask_dcplb; + +void __init generate_cpl_tables(void) +{ + int i_d, i_i; + unsigned long addr; + unsigned long d_data, i_data; + unsigned long d_cache = 0, i_cache = 0; + +#ifdef CONFIG_BFIN_ICACHE + i_cache = CPLB_L1_CHBL | ANOMALY_05000158_WORKAROUND; +#endif + +#ifdef CONFIG_BFIN_DCACHE + d_cache = CPLB_L1_CHBL; +#ifdef CONFIG_BLKFIN_WT + d_cache |= CPLB_L1_AOW | CPLB_WT; +#endif +#endif + i_d = i_i = 0; + + /* Set up the zero page. */ + dcplb_tbl[i_d].addr = 0; + dcplb_tbl[i_d++].data = SDRAM_OOPS | PAGE_SIZE_1KB; + +#if 0 + icplb_tbl[i_i].addr = 0; + icplb_tbl[i_i++].data = i_cache | CPLB_USER_RD | PAGE_SIZE_4KB; +#endif + + /* Cover kernel memory with 4M pages. */ + addr = 0; + d_data = d_cache | CPLB_SUPV_WR | CPLB_VALID | PAGE_SIZE_4MB | CPLB_DIRTY; + i_data = i_cache | CPLB_VALID | CPLB_PORTPRIO | PAGE_SIZE_4MB; + + for (; addr < memory_start; addr += 4 * 1024 * 1024) { + dcplb_tbl[i_d].addr = addr; + dcplb_tbl[i_d++].data = d_data; + icplb_tbl[i_i].addr = addr; + icplb_tbl[i_i++].data = i_data | (addr == 0 ? CPLB_USER_RD : 0); + } + + /* Cover L1 memory. One 4M area for code and data each is enough. */ +#if L1_DATA_A_LENGTH > 0 || L1_DATA_B_LENGTH > 0 + dcplb_tbl[i_d].addr = L1_DATA_A_START; + dcplb_tbl[i_d++].data = L1_DMEMORY | PAGE_SIZE_4MB; +#endif + icplb_tbl[i_i].addr = L1_CODE_START; + icplb_tbl[i_i++].data = L1_IMEMORY | PAGE_SIZE_4MB; + + first_mask_dcplb = i_d; + first_switched_dcplb = i_d + (1 << page_mask_order); + first_switched_icplb = i_i; + + while (i_d < MAX_CPLBS) + dcplb_tbl[i_d++].data = 0; + while (i_i < MAX_CPLBS) + icplb_tbl[i_i++].data = 0; +} diff --git a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c new file mode 100644 index 000000000000..c426a22f9907 --- /dev/null +++ b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c @@ -0,0 +1,338 @@ +/* + * Blackfin CPLB exception handling. + * Copyright 2004-2007 Analog Devices Inc. + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include + +#include +#include +#include + +#ifdef CONFIG_BFIN_ICACHE + +#define FAULT_RW (1 << 16) +#define FAULT_USERSUPV (1 << 17) + +int page_mask_nelts; +int page_mask_order; +unsigned long *current_rwx_mask; + +int nr_dcplb_miss, nr_icplb_miss, nr_icplb_supv_miss, nr_dcplb_prot; +int nr_cplb_flush; + +static inline void disable_dcplb(void) +{ + unsigned long ctrl; + SSYNC(); + ctrl = bfin_read_DMEM_CONTROL(); + ctrl &= ~ENDCPLB; + bfin_write_DMEM_CONTROL(ctrl); + SSYNC(); +} + +static inline void enable_dcplb(void) +{ + unsigned long ctrl; + SSYNC(); + ctrl = bfin_read_DMEM_CONTROL(); + ctrl |= ENDCPLB; + bfin_write_DMEM_CONTROL(ctrl); + SSYNC(); +} + +static inline void disable_icplb(void) +{ + unsigned long ctrl; + SSYNC(); + ctrl = bfin_read_IMEM_CONTROL(); + ctrl &= ~ENICPLB; + bfin_write_IMEM_CONTROL(ctrl); + SSYNC(); +} + +static inline void enable_icplb(void) +{ + unsigned long ctrl; + SSYNC(); + ctrl = bfin_read_IMEM_CONTROL(); + ctrl |= ENICPLB; + bfin_write_IMEM_CONTROL(ctrl); + SSYNC(); +} + +/* + * Given the contents of the status register, return the index of the + * CPLB that caused the fault. + */ +static inline int faulting_cplb_index(int status) +{ + int signbits = __builtin_bfin_norm_fr1x32(status & 0xFFFF); + return 30 - signbits; +} + +/* + * Given the contents of the status register and the DCPLB_DATA contents, + * return true if a write access should be permitted. + */ +static inline int write_permitted(int status, unsigned long data) +{ + if (status & FAULT_USERSUPV) + return !!(data & CPLB_SUPV_WR); + else + return !!(data & CPLB_USER_WR); +} + +/* Counters to implement round-robin replacement. */ +static int icplb_rr_index, dcplb_rr_index; + +/* + * Find an ICPLB entry to be evicted and return its index. + */ +static int evict_one_icplb(void) +{ + int i; + for (i = first_switched_icplb; i < MAX_CPLBS; i++) + if ((icplb_tbl[i].data & CPLB_VALID) == 0) + return i; + i = first_switched_icplb + icplb_rr_index; + if (i >= MAX_CPLBS) { + i -= MAX_CPLBS - first_switched_icplb; + icplb_rr_index -= MAX_CPLBS - first_switched_icplb; + } + icplb_rr_index++; + return i; +} + +static int evict_one_dcplb(void) +{ + int i; + for (i = first_switched_dcplb; i < MAX_CPLBS; i++) + if ((dcplb_tbl[i].data & CPLB_VALID) == 0) + return i; + i = first_switched_dcplb + dcplb_rr_index; + if (i >= MAX_CPLBS) { + i -= MAX_CPLBS - first_switched_dcplb; + dcplb_rr_index -= MAX_CPLBS - first_switched_dcplb; + } + dcplb_rr_index++; + return i; +} + +static noinline int dcplb_miss(void) +{ + unsigned long addr = bfin_read_DCPLB_FAULT_ADDR(); + int status = bfin_read_DCPLB_STATUS(); + unsigned long *mask; + int idx; + unsigned long d_data; + + nr_dcplb_miss++; + if (addr >= _ramend) + return CPLB_PROT_VIOL; + + d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB; +#ifdef CONFIG_BFIN_DCACHE + d_data |= CPLB_L1_CHBL | ANOMALY_05000158_WORKAROUND; +#ifdef CONFIG_BLKFIN_WT + d_data |= CPLB_L1_AOW | CPLB_WT; +#endif +#endif + mask = current_rwx_mask; + if (mask) { + int page = addr >> PAGE_SHIFT; + int offs = page >> 5; + int bit = 1 << (page & 31); + + if (mask[offs] & bit) + d_data |= CPLB_USER_RD; + + mask += page_mask_nelts; + if (mask[offs] & bit) + d_data |= CPLB_USER_WR; + } + + idx = evict_one_dcplb(); + + addr &= PAGE_MASK; + dcplb_tbl[idx].addr = addr; + dcplb_tbl[idx].data = d_data; + + disable_dcplb(); + bfin_write32(DCPLB_DATA0 + idx * 4, d_data); + bfin_write32(DCPLB_ADDR0 + idx * 4, addr); + enable_dcplb(); + + return 0; +} + +static noinline int icplb_miss(void) +{ + unsigned long addr = bfin_read_ICPLB_FAULT_ADDR(); + int status = bfin_read_ICPLB_STATUS(); + int idx; + unsigned long i_data; + + nr_icplb_miss++; + if (status & FAULT_USERSUPV) + nr_icplb_supv_miss++; + + if (addr >= _ramend) + return CPLB_PROT_VIOL; + + /* + * First, try to find a CPLB that matches this address. If we + * find one, then the fact that we're in the miss handler means + * that the instruction crosses a page boundary. + */ + for (idx = first_switched_icplb; idx < MAX_CPLBS; idx++) { + if (icplb_tbl[idx].data & CPLB_VALID) { + unsigned long this_addr = icplb_tbl[idx].addr; + if (this_addr <= addr && this_addr + PAGE_SIZE > addr) { + addr += PAGE_SIZE; + break; + } + } + } + + i_data = CPLB_VALID | CPLB_PORTPRIO | PAGE_SIZE_4KB; +#ifdef CONFIG_BFIN_ICACHE + i_data |= CPLB_L1_CHBL | ANOMALY_05000158_WORKAROUND; +#endif + + /* + * Two cases to distinguish - a supervisor access must necessarily + * be for a module page; we grant it unconditionally (could do better + * here in the future). Otherwise, check the x bitmap of the current + * process. + */ + if (!(status & FAULT_USERSUPV)) { + unsigned long *mask = current_rwx_mask; + + if (mask) { + int page = addr >> PAGE_SHIFT; + int offs = page >> 5; + int bit = 1 << (page & 31); + + mask += 2 * page_mask_nelts; + if (mask[offs] & bit) + i_data |= CPLB_USER_RD; + } + } + + idx = evict_one_icplb(); + addr &= PAGE_MASK; + icplb_tbl[idx].addr = addr; + icplb_tbl[idx].data = i_data; + + disable_icplb(); + bfin_write32(ICPLB_DATA0 + idx * 4, i_data); + bfin_write32(ICPLB_ADDR0 + idx * 4, addr); + enable_icplb(); + + return 0; +} + +static noinline int dcplb_protection_fault(void) +{ + unsigned long addr = bfin_read_DCPLB_FAULT_ADDR(); + int status = bfin_read_DCPLB_STATUS(); + + nr_dcplb_prot++; + + if (status & FAULT_RW) { + int idx = faulting_cplb_index(status); + unsigned long data = dcplb_tbl[idx].data; + if (!(data & CPLB_WT) && !(data & CPLB_DIRTY) && + write_permitted(status, data)) { + data |= CPLB_DIRTY; + dcplb_tbl[idx].data = data; + bfin_write32(DCPLB_DATA0 + idx * 4, data); + return 0; + } + } + return CPLB_PROT_VIOL; +} + +int cplb_hdr(int seqstat, struct pt_regs *regs) +{ + int cause = seqstat & 0x3f; + switch (cause) { + case 0x23: + return dcplb_protection_fault(); + case 0x2C: + return icplb_miss(); + case 0x26: + return dcplb_miss(); + default: + return 1; + panic_cplb_error(seqstat, regs); + } +} + +void flush_switched_cplbs(void) +{ + int i; + + nr_cplb_flush++; + + disable_icplb(); + for (i = first_switched_icplb; i < MAX_CPLBS; i++) { + icplb_tbl[i].data = 0; + bfin_write32(ICPLB_DATA0 + i * 4, 0); + } + enable_icplb(); + + disable_dcplb(); + for (i = first_mask_dcplb; i < MAX_CPLBS; i++) { + dcplb_tbl[i].data = 0; + bfin_write32(DCPLB_DATA0 + i * 4, 0); + } + enable_dcplb(); +} + +void set_mask_dcplbs(unsigned long *masks) +{ + int i; + unsigned long addr = (unsigned long)masks; + unsigned long d_data; + current_rwx_mask = masks; + + if (!masks) + return; + + d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB; +#ifdef CONFIG_BFIN_DCACHE + d_data |= CPLB_L1_CHBL; +#ifdef CONFIG_BLKFIN_WT + d_data |= CPLB_L1_AOW | CPLB_WT; +#endif +#endif + + disable_dcplb(); + for (i = first_mask_dcplb; i < first_switched_dcplb; i++) { + dcplb_tbl[i].addr = addr; + dcplb_tbl[i].data = d_data; + bfin_write32(DCPLB_DATA0 + i * 4, d_data); + bfin_write32(DCPLB_ADDR0 + i * 4, addr); + addr += PAGE_SIZE; + } + enable_dcplb(); +} + +#endif diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c index a03c2dfff4a3..1a942a721d51 100644 --- a/arch/blackfin/kernel/setup.c +++ b/arch/blackfin/kernel/setup.c @@ -238,7 +238,12 @@ void __init setup_arch(char **cmdline_p) memory_end = _ramend - DMA_UNCACHED_REGION; _ramstart = (unsigned long)__bss_stop; +#ifdef CONFIG_MPU + /* Round up to multiple of 4MB. */ + memory_start = (_ramstart + 0x3fffff) & ~0x3fffff; +#else memory_start = PAGE_ALIGN(_ramstart); +#endif #if defined(CONFIG_MTD_UCLINUX) /* generic memory mapped MTD driver */ @@ -307,6 +312,11 @@ void __init setup_arch(char **cmdline_p) printk(KERN_NOTICE "Warning: limiting memory to %liMB due to hardware anomaly 05000263\n", memory_end >> 20); #endif /* ANOMALY_05000263 */ +#ifdef CONFIG_MPU + page_mask_nelts = ((_ramend >> PAGE_SHIFT) + 31) / 32; + page_mask_order = get_order(3 * page_mask_nelts * sizeof(long)); +#endif + #if !defined(CONFIG_MTD_UCLINUX) memory_end -= SIZE_4K; /*In case there is no valid CPLB behind memory_end make sure we don't get to close*/ #endif -- cgit v1.2.1 From acbcd2631975cf6f0be5cd294cbfd12226cd9958 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 22 Jan 2008 18:36:20 +0800 Subject: [Blackfin] arch: Fix BUG gpio_direction_output API is not compatitable with GENERIC_GPIO API interface signef-off-by: Michael Hennerich Signed-off-by: Bryan Wu --- arch/blackfin/kernel/bfin_gpio.c | 55 ++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 13 deletions(-) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c index ffee36910288..7312011a4211 100644 --- a/arch/blackfin/kernel/bfin_gpio.c +++ b/arch/blackfin/kernel/bfin_gpio.c @@ -229,6 +229,11 @@ inline int check_gpio(unsigned short gpio) } #endif +void gpio_error(unsigned gpio) +{ + printk(KERN_ERR "bfin-gpio: GPIO %d wasn't requested!\n", gpio); +} + static void set_label(unsigned short ident, const char *label) { @@ -1034,7 +1039,7 @@ EXPORT_SYMBOL(peripheral_free_list); * MODIFICATION HISTORY : **************************************************************/ -int gpio_request(unsigned short gpio, const char *label) +int gpio_request(unsigned gpio, const char *label) { unsigned long flags; @@ -1081,7 +1086,7 @@ int gpio_request(unsigned short gpio, const char *label) } EXPORT_SYMBOL(gpio_request); -void gpio_free(unsigned short gpio) +void gpio_free(unsigned gpio) { unsigned long flags; @@ -1091,7 +1096,7 @@ void gpio_free(unsigned short gpio) local_irq_save(flags); if (unlikely(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio)))) { - printk(KERN_ERR "bfin-gpio: GPIO %d wasn't reserved!\n", gpio); + gpio_error(gpio); dump_stack(); local_irq_restore(flags); return; @@ -1107,34 +1112,47 @@ void gpio_free(unsigned short gpio) } EXPORT_SYMBOL(gpio_free); + #ifdef BF548_FAMILY -void gpio_direction_input(unsigned short gpio) +int gpio_direction_input(unsigned gpio) { unsigned long flags; - BUG_ON(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))); + if (!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) { + gpio_error(gpio); + return -EINVAL; + } + local_irq_save(flags); gpio_array[gpio_bank(gpio)]->port_dir_clear = gpio_bit(gpio); gpio_array[gpio_bank(gpio)]->port_inen |= gpio_bit(gpio); local_irq_restore(flags); + + return 0; } EXPORT_SYMBOL(gpio_direction_input); -void gpio_direction_output(unsigned short gpio) +int gpio_direction_output(unsigned gpio, int value) { unsigned long flags; - BUG_ON(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))); + if (!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) { + gpio_error(gpio); + return -EINVAL; + } local_irq_save(flags); gpio_array[gpio_bank(gpio)]->port_inen &= ~gpio_bit(gpio); + gpio_set_value(gpio, value); gpio_array[gpio_bank(gpio)]->port_dir_set = gpio_bit(gpio); local_irq_restore(flags); + + return 0; } EXPORT_SYMBOL(gpio_direction_output); -void gpio_set_value(unsigned short gpio, unsigned short arg) +void gpio_set_value(unsigned gpio, int arg) { if (arg) gpio_array[gpio_bank(gpio)]->port_set = gpio_bit(gpio); @@ -1144,7 +1162,7 @@ void gpio_set_value(unsigned short gpio, unsigned short arg) } EXPORT_SYMBOL(gpio_set_value); -unsigned short gpio_get_value(unsigned short gpio) +int gpio_get_value(unsigned gpio) { return (1 & (gpio_array[gpio_bank(gpio)]->port_data >> gpio_sub_n(gpio))); } @@ -1152,31 +1170,42 @@ EXPORT_SYMBOL(gpio_get_value); #else -void gpio_direction_input(unsigned short gpio) +int gpio_direction_input(unsigned gpio) { unsigned long flags; - BUG_ON(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))); + if (!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) { + gpio_error(gpio); + return -EINVAL; + } local_irq_save(flags); gpio_bankb[gpio_bank(gpio)]->dir &= ~gpio_bit(gpio); gpio_bankb[gpio_bank(gpio)]->inen |= gpio_bit(gpio); AWA_DUMMY_READ(inen); local_irq_restore(flags); + + return 0; } EXPORT_SYMBOL(gpio_direction_input); -void gpio_direction_output(unsigned short gpio) +int gpio_direction_output(unsigned gpio, int value) { unsigned long flags; - BUG_ON(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))); + if (!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))) { + gpio_error(gpio); + return -EINVAL; + } local_irq_save(flags); gpio_bankb[gpio_bank(gpio)]->inen &= ~gpio_bit(gpio); + gpio_set_value(gpio, value); gpio_bankb[gpio_bank(gpio)]->dir |= gpio_bit(gpio); AWA_DUMMY_READ(dir); local_irq_restore(flags); + + return 0; } EXPORT_SYMBOL(gpio_direction_output); -- cgit v1.2.1 From a2c8cfef6abb33ee49d80c58391ebfc4f94221ef Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 22 Jan 2008 17:20:10 +0800 Subject: [Blackfin] arch: GPIO API cleanup and anomaly update - Add anomaly workaround for bfin_gpio_reset_spi0_ssel1 - Fix style - Update copyright - Remove BUG_ON checks for functions intended to be used only by arch support. GPIO users should only access using the generic GPIO API - Make all GPIO identifier unsigned int Signed-off-by: Michael Hennerich Signed-off-by: Bryan Wu --- arch/blackfin/kernel/bfin_gpio.c | 63 ++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 35 deletions(-) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c index 7312011a4211..6bbe0a2fccb8 100644 --- a/arch/blackfin/kernel/bfin_gpio.c +++ b/arch/blackfin/kernel/bfin_gpio.c @@ -7,7 +7,7 @@ * Description: GPIO Abstraction Layer * * Modified: - * Copyright 2007 Analog Devices Inc. + * Copyright 2008 Analog Devices Inc. * * Bugs: Enter bugs at http://blackfin.uclinux.org/ * @@ -137,7 +137,6 @@ static unsigned short *port_fer[gpio_bank(MAX_BLACKFIN_GPIOS)] = { (unsigned short *) PORTG_FER, (unsigned short *) PORTH_FER, }; - #endif #ifdef BF527_FAMILY @@ -211,7 +210,7 @@ static unsigned int sic_iwr_irqs[gpio_bank(MAX_BLACKFIN_GPIOS)] = {IRQ_PROG0_INT #endif /* CONFIG_PM */ #if defined(BF548_FAMILY) -inline int check_gpio(unsigned short gpio) +inline int check_gpio(unsigned gpio) { if (gpio == GPIO_PB15 || gpio == GPIO_PC14 || gpio == GPIO_PC15 || gpio == GPIO_PH14 || gpio == GPIO_PH15 @@ -221,7 +220,7 @@ inline int check_gpio(unsigned short gpio) return 0; } #else -inline int check_gpio(unsigned short gpio) +inline int check_gpio(unsigned gpio) { if (gpio >= MAX_BLACKFIN_GPIOS) return -EINVAL; @@ -236,7 +235,6 @@ void gpio_error(unsigned gpio) static void set_label(unsigned short ident, const char *label) { - if (label && str_ident) { strncpy(str_ident[ident].name, label, RESOURCE_LABEL_SIZE); @@ -267,7 +265,7 @@ static int cmp_label(unsigned short ident, const char *label) } #if defined(BF527_FAMILY) || defined(BF537_FAMILY) -static void port_setup(unsigned short gpio, unsigned short usage) +static void port_setup(unsigned gpio, unsigned short usage) { if (!check_gpio(gpio)) { if (usage == GPIO_USAGE) @@ -278,7 +276,7 @@ static void port_setup(unsigned short gpio, unsigned short usage) } } #elif defined(BF548_FAMILY) -static void port_setup(unsigned short gpio, unsigned short usage) +static void port_setup(unsigned gpio, unsigned short usage) { if (usage == GPIO_USAGE) gpio_array[gpio_bank(gpio)]->port_fer &= ~gpio_bit(gpio); @@ -399,7 +397,7 @@ inline void portmux_setup(unsigned short portno, unsigned short function) #endif #ifndef BF548_FAMILY -static void default_gpio(unsigned short gpio) +static void default_gpio(unsigned gpio) { unsigned short bank, bitmask; unsigned long flags; @@ -419,7 +417,6 @@ static void default_gpio(unsigned short gpio) gpio_bankb[bank]->edge &= ~bitmask; AWA_DUMMY_READ(edge); local_irq_restore(flags); - } #else # define default_gpio(...) do { } while (0) @@ -457,10 +454,9 @@ arch_initcall(bfin_gpio_init); /* Set a specific bit */ #define SET_GPIO(name) \ -void set_gpio_ ## name(unsigned short gpio, unsigned short arg) \ +void set_gpio_ ## name(unsigned gpio, unsigned short arg) \ { \ unsigned long flags; \ - BUG_ON(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))); \ local_irq_save(flags); \ if (arg) \ gpio_bankb[gpio_bank(gpio)]->name |= gpio_bit(gpio); \ @@ -480,10 +476,9 @@ SET_GPIO(both) #if ANOMALY_05000311 || ANOMALY_05000323 #define SET_GPIO_SC(name) \ -void set_gpio_ ## name(unsigned short gpio, unsigned short arg) \ +void set_gpio_ ## name(unsigned gpio, unsigned short arg) \ { \ unsigned long flags; \ - BUG_ON(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))); \ local_irq_save(flags); \ if (arg) \ gpio_bankb[gpio_bank(gpio)]->name ## _set = gpio_bit(gpio); \ @@ -495,9 +490,8 @@ void set_gpio_ ## name(unsigned short gpio, unsigned short arg) \ EXPORT_SYMBOL(set_gpio_ ## name); #else #define SET_GPIO_SC(name) \ -void set_gpio_ ## name(unsigned short gpio, unsigned short arg) \ +void set_gpio_ ## name(unsigned gpio, unsigned short arg) \ { \ - BUG_ON(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))); \ if (arg) \ gpio_bankb[gpio_bank(gpio)]->name ## _set = gpio_bit(gpio); \ else \ @@ -511,19 +505,17 @@ SET_GPIO_SC(maskb) SET_GPIO_SC(data) #if ANOMALY_05000311 || ANOMALY_05000323 -void set_gpio_toggle(unsigned short gpio) +void set_gpio_toggle(unsigned gpio) { unsigned long flags; - BUG_ON(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))); local_irq_save(flags); gpio_bankb[gpio_bank(gpio)]->toggle = gpio_bit(gpio); AWA_DUMMY_READ(toggle); local_irq_restore(flags); } #else -void set_gpio_toggle(unsigned short gpio) +void set_gpio_toggle(unsigned gpio) { - BUG_ON(!(reserved_gpio_map[gpio_bank(gpio)] & gpio_bit(gpio))); gpio_bankb[gpio_bank(gpio)]->toggle = gpio_bit(gpio); } #endif @@ -534,7 +526,7 @@ EXPORT_SYMBOL(set_gpio_toggle); #if ANOMALY_05000311 || ANOMALY_05000323 #define SET_GPIO_P(name) \ -void set_gpiop_ ## name(unsigned short gpio, unsigned short arg) \ +void set_gpiop_ ## name(unsigned gpio, unsigned short arg) \ { \ unsigned long flags; \ local_irq_save(flags); \ @@ -545,7 +537,7 @@ void set_gpiop_ ## name(unsigned short gpio, unsigned short arg) \ EXPORT_SYMBOL(set_gpiop_ ## name); #else #define SET_GPIO_P(name) \ -void set_gpiop_ ## name(unsigned short gpio, unsigned short arg) \ +void set_gpiop_ ## name(unsigned gpio, unsigned short arg) \ { \ gpio_bankb[gpio_bank(gpio)]->name = arg; \ } \ @@ -561,11 +553,10 @@ SET_GPIO_P(both) SET_GPIO_P(maska) SET_GPIO_P(maskb) - /* Get a specific bit */ #if ANOMALY_05000311 || ANOMALY_05000323 #define GET_GPIO(name) \ -unsigned short get_gpio_ ## name(unsigned short gpio) \ +unsigned short get_gpio_ ## name(unsigned gpio) \ { \ unsigned long flags; \ unsigned short ret; \ @@ -578,7 +569,7 @@ unsigned short get_gpio_ ## name(unsigned short gpio) \ EXPORT_SYMBOL(get_gpio_ ## name); #else #define GET_GPIO(name) \ -unsigned short get_gpio_ ## name(unsigned short gpio) \ +unsigned short get_gpio_ ## name(unsigned gpio) \ { \ return (0x01 & (gpio_bankb[gpio_bank(gpio)]->name >> gpio_sub_n(gpio))); \ } \ @@ -598,7 +589,7 @@ GET_GPIO(maskb) #if ANOMALY_05000311 || ANOMALY_05000323 #define GET_GPIO_P(name) \ -unsigned short get_gpiop_ ## name(unsigned short gpio) \ +unsigned short get_gpiop_ ## name(unsigned gpio) \ { \ unsigned long flags; \ unsigned short ret; \ @@ -611,7 +602,7 @@ unsigned short get_gpiop_ ## name(unsigned short gpio) \ EXPORT_SYMBOL(get_gpiop_ ## name); #else #define GET_GPIO_P(name) \ -unsigned short get_gpiop_ ## name(unsigned short gpio) \ +unsigned short get_gpiop_ ## name(unsigned gpio) \ { \ return (gpio_bankb[gpio_bank(gpio)]->name);\ } \ @@ -648,7 +639,7 @@ GET_GPIO_P(maskb) ************************************************************* * MODIFICATION HISTORY : **************************************************************/ -int gpio_pm_wakeup_request(unsigned short gpio, unsigned char type) +int gpio_pm_wakeup_request(unsigned gpio, unsigned char type) { unsigned long flags; @@ -656,7 +647,6 @@ int gpio_pm_wakeup_request(unsigned short gpio, unsigned char type) return -EINVAL; local_irq_save(flags); - wakeup_map[gpio_bank(gpio)] |= gpio_bit(gpio); wakeup_flags_map[gpio] = type; local_irq_restore(flags); @@ -665,7 +655,7 @@ int gpio_pm_wakeup_request(unsigned short gpio, unsigned char type) } EXPORT_SYMBOL(gpio_pm_wakeup_request); -void gpio_pm_wakeup_free(unsigned short gpio) +void gpio_pm_wakeup_free(unsigned gpio) { unsigned long flags; @@ -680,7 +670,7 @@ void gpio_pm_wakeup_free(unsigned short gpio) } EXPORT_SYMBOL(gpio_pm_wakeup_free); -static int bfin_gpio_wakeup_type(unsigned short gpio, unsigned char type) +static int bfin_gpio_wakeup_type(unsigned gpio, unsigned char type) { port_setup(gpio, GPIO_USAGE); set_gpio_dir(gpio, 0); @@ -789,7 +779,7 @@ void gpio_pm_restore(void) #endif #else /* BF548_FAMILY */ -unsigned short get_gpio_dir(unsigned short gpio) +unsigned short get_gpio_dir(unsigned gpio) { return (0x01 & (gpio_array[gpio_bank(gpio)]->port_dir_clear >> gpio_sub_n(gpio))); } @@ -1123,7 +1113,6 @@ int gpio_direction_input(unsigned gpio) return -EINVAL; } - local_irq_save(flags); gpio_array[gpio_bank(gpio)]->port_dir_clear = gpio_bit(gpio); gpio_array[gpio_bank(gpio)]->port_inen |= gpio_bit(gpio); @@ -1158,7 +1147,6 @@ void gpio_set_value(unsigned gpio, int arg) gpio_array[gpio_bank(gpio)]->port_set = gpio_bit(gpio); else gpio_array[gpio_bank(gpio)]->port_clear = gpio_bit(gpio); - } EXPORT_SYMBOL(gpio_set_value); @@ -1200,7 +1188,12 @@ int gpio_direction_output(unsigned gpio, int value) local_irq_save(flags); gpio_bankb[gpio_bank(gpio)]->inen &= ~gpio_bit(gpio); - gpio_set_value(gpio, value); + + if (value) + gpio_bankb[gpio_bank(gpio)]->data_set = gpio_bit(gpio); + else + gpio_bankb[gpio_bank(gpio)]->data_clear = gpio_bit(gpio); + gpio_bankb[gpio_bank(gpio)]->dir |= gpio_bit(gpio); AWA_DUMMY_READ(dir); local_irq_restore(flags); @@ -1225,6 +1218,7 @@ void bfin_gpio_reset_spi0_ssel1(void) port_setup(gpio, GPIO_USAGE); gpio_bankb[gpio_bank(gpio)]->data_set = gpio_bit(gpio); + AWA_DUMMY_READ(data_set); udelay(1); } @@ -1259,6 +1253,5 @@ static __init int gpio_register_proc(void) proc_gpio->read_proc = gpio_proc_read; return proc_gpio != NULL; } - __initcall(gpio_register_proc); #endif -- cgit v1.2.1 From 444ad82bc3eaa554be40d22dc248e58aeefd54d9 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 22 Jan 2008 18:38:02 +0800 Subject: [Blackfin] arch: Add proper SW System Reset delay sequence Signed-off-by: Michael Hennerich Signed-off-by: Bryan Wu --- arch/blackfin/kernel/reboot.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/reboot.c b/arch/blackfin/kernel/reboot.c index 06501a594dda..483f93dfc1b5 100644 --- a/arch/blackfin/kernel/reboot.c +++ b/arch/blackfin/kernel/reboot.c @@ -19,6 +19,11 @@ #define SYSCR_VAL 0x10 #endif +/* + * Delay min 5 SCLK cycles using worst case CCLK/SCLK ratio (15) + */ +#define SWRST_DELAY (5 * 15) + /* A system soft reset makes external memory unusable * so force this function into L1. */ @@ -34,11 +39,15 @@ void bfin_reset(void) while (1) { /* initiate system soft reset with magic 0x7 */ bfin_write_SWRST(0x7); - bfin_read_SWRST(); - asm("ssync;"); + + /* Wait for System reset to actually reset, needs to be 5 SCLKs, */ + /* Assume CCLK / SCLK ratio is worst case (15), and use 5*15 */ + + asm("LSETUP(.Lfoo,.Lfoo) LC0 = %0\n .Lfoo: NOP;\n" + : : "a" (SWRST_DELAY) : "LC0", "LT0", "LB0"); + /* clear system soft reset */ bfin_write_SWRST(0); - bfin_read_SWRST(); asm("ssync;"); /* issue core reset */ asm("raise 1"); -- cgit v1.2.1 From 96a5c6f9bef027e1187b2f168bb3e08ef21d0b6f Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 22 Jan 2008 19:23:50 +0800 Subject: [Blackfin] arch: Fix bug - kernel build with Debug option enabled fails to boot up writes to I/DMEM_CONTROL must be followed by SSYNC Signed-off-by: Michael Hennerich Signed-off-by: Bryan Wu --- arch/blackfin/kernel/cplb-nompu/cacheinit.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/cplb-nompu/cacheinit.c b/arch/blackfin/kernel/cplb-nompu/cacheinit.c index 62cbba7364b0..8a18399f6072 100644 --- a/arch/blackfin/kernel/cplb-nompu/cacheinit.c +++ b/arch/blackfin/kernel/cplb-nompu/cacheinit.c @@ -42,6 +42,7 @@ void bfin_icache_init(void) ctrl = bfin_read_IMEM_CONTROL(); ctrl |= IMC | ENICPLB; bfin_write_IMEM_CONTROL(ctrl); + SSYNC(); } #endif @@ -63,5 +64,6 @@ void bfin_dcache_init(void) ctrl = bfin_read_DMEM_CONTROL(); ctrl |= DMEM_CNTR; bfin_write_DMEM_CONTROL(ctrl); + SSYNC(); } #endif -- cgit v1.2.1 From ee7883b7466e721a72edacbcba2fe9cf449d82b2 Mon Sep 17 00:00:00 2001 From: Yi Li Date: Sun, 27 Jan 2008 19:56:17 +0800 Subject: [Blackfin] arch: fix bug kernel boot message: memory information is not reasonable Some of the information in kernel boot message is not reasonable. http://blackfin.uclinux.org/gf/project/uclinux-dist/tracker/?action=TrackerItemEdit&tracker_item_id=3846 - use _rambase as the start of kernel image. kernel is in the region [_rambase, _ramstart] - count in pages in per-cpu-page list as available memory - reserved memory now include: [0 - 4K] for bad pointer catching, memory reserved for abnormaly 05000263, memory reserved by kernel itself. Signed-off-by: Yi Li Signed-off-by: Bryan Wu --- arch/blackfin/kernel/setup.c | 1 + 1 file changed, 1 insertion(+) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c index 1a942a721d51..462cae893757 100644 --- a/arch/blackfin/kernel/setup.c +++ b/arch/blackfin/kernel/setup.c @@ -238,6 +238,7 @@ void __init setup_arch(char **cmdline_p) memory_end = _ramend - DMA_UNCACHED_REGION; _ramstart = (unsigned long)__bss_stop; + _rambase = (unsigned long)_stext; #ifdef CONFIG_MPU /* Round up to multiple of 4MB. */ memory_start = (_ramstart + 0x3fffff) & ~0x3fffff; -- cgit v1.2.1 From 01ba2bdc6b639764745ff678caf3fb9e5bcd745a Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sun, 20 Jan 2008 14:15:03 +0100 Subject: all archs: consolidate init and exit sections in vmlinux.lds.h This patch consolidate all definitions of .init.text, .init.data and .exit.text, .exit.data section definitions in the generic vmlinux.lds.h. This is a preparational patch - alone it does not buy us much good. Signed-off-by: Sam Ravnborg --- arch/blackfin/kernel/vmlinux.lds.S | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'arch/blackfin/kernel') diff --git a/arch/blackfin/kernel/vmlinux.lds.S b/arch/blackfin/kernel/vmlinux.lds.S index 9b75bc83c71f..858722421b40 100644 --- a/arch/blackfin/kernel/vmlinux.lds.S +++ b/arch/blackfin/kernel/vmlinux.lds.S @@ -91,13 +91,13 @@ SECTIONS { . = ALIGN(PAGE_SIZE); __sinittext = .; - *(.init.text) + INIT_TEXT __einittext = .; } .init.data : { . = ALIGN(16); - *(.init.data) + INIT_DATA } .init.setup : { @@ -198,8 +198,8 @@ SECTIONS /DISCARD/ : { - *(.exit.text) - *(.exit.data) + EXIT_TEXT + EXIT_DATA *(.exitcall.exit) } } -- cgit v1.2.1