diff options
Diffstat (limited to 'arch/blackfin/kernel')
| -rw-r--r-- | arch/blackfin/kernel/Makefile | 1 | ||||
| -rw-r--r-- | arch/blackfin/kernel/bfin_dma_5xx.c | 10 | ||||
| -rw-r--r-- | arch/blackfin/kernel/bfin_gpio.c | 20 | ||||
| -rw-r--r-- | arch/blackfin/kernel/cplb-mpu/cplbinit.c | 9 | ||||
| -rw-r--r-- | arch/blackfin/kernel/cplb-mpu/cplbmgr.c | 54 | ||||
| -rw-r--r-- | arch/blackfin/kernel/cplb-nompu/cplbinit.c | 9 | ||||
| -rw-r--r-- | arch/blackfin/kernel/dma-mapping.c | 2 | ||||
| -rw-r--r-- | arch/blackfin/kernel/entry.S | 8 | ||||
| -rw-r--r-- | arch/blackfin/kernel/ftrace-entry.S | 23 | ||||
| -rw-r--r-- | arch/blackfin/kernel/ftrace.c | 6 | ||||
| -rw-r--r-- | arch/blackfin/kernel/init_task.c | 2 | ||||
| -rw-r--r-- | arch/blackfin/kernel/kgdb.c | 10 | ||||
| -rw-r--r-- | arch/blackfin/kernel/nmi.c | 299 | ||||
| -rw-r--r-- | arch/blackfin/kernel/process.c | 7 | ||||
| -rw-r--r-- | arch/blackfin/kernel/ptrace.c | 360 | ||||
| -rw-r--r-- | arch/blackfin/kernel/setup.c | 34 | ||||
| -rw-r--r-- | arch/blackfin/kernel/signal.c | 24 | ||||
| -rw-r--r-- | arch/blackfin/kernel/time-ts.c | 205 | ||||
| -rw-r--r-- | arch/blackfin/kernel/traps.c | 32 | ||||
| -rw-r--r-- | arch/blackfin/kernel/vmlinux.lds.S | 70 |
20 files changed, 819 insertions, 366 deletions
diff --git a/arch/blackfin/kernel/Makefile b/arch/blackfin/kernel/Makefile index a8ddbc8ed5af..346a421f1562 100644 --- a/arch/blackfin/kernel/Makefile +++ b/arch/blackfin/kernel/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_CPLB_INFO) += cplbinfo.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_KGDB_TESTS) += kgdb_test.o +obj-$(CONFIG_NMI_WATCHDOG) += nmi.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-$(CONFIG_EARLY_PRINTK) += shadow_console.o obj-$(CONFIG_STACKTRACE) += stacktrace.o diff --git a/arch/blackfin/kernel/bfin_dma_5xx.c b/arch/blackfin/kernel/bfin_dma_5xx.c index 924c00286bab..26403d1c9e65 100644 --- a/arch/blackfin/kernel/bfin_dma_5xx.c +++ b/arch/blackfin/kernel/bfin_dma_5xx.c @@ -91,7 +91,7 @@ late_initcall(proc_dma_init); */ int request_dma(unsigned int channel, const char *device_id) { - pr_debug("request_dma() : BEGIN \n"); + pr_debug("request_dma() : BEGIN\n"); if (device_id == NULL) printk(KERN_WARNING "request_dma(%u): no device_id given\n", channel); @@ -107,7 +107,7 @@ int request_dma(unsigned int channel, const char *device_id) #endif if (atomic_cmpxchg(&dma_ch[channel].chan_status, 0, 1)) { - pr_debug("DMA CHANNEL IN USE \n"); + pr_debug("DMA CHANNEL IN USE\n"); return -EBUSY; } @@ -131,7 +131,7 @@ int request_dma(unsigned int channel, const char *device_id) * you have to request DMA, before doing any operations on * descriptor/channel */ - pr_debug("request_dma() : END \n"); + pr_debug("request_dma() : END\n"); return 0; } EXPORT_SYMBOL(request_dma); @@ -171,7 +171,7 @@ static void clear_dma_buffer(unsigned int channel) void free_dma(unsigned int channel) { - pr_debug("freedma() : BEGIN \n"); + pr_debug("freedma() : BEGIN\n"); BUG_ON(channel >= MAX_DMA_CHANNELS || !atomic_read(&dma_ch[channel].chan_status)); @@ -185,7 +185,7 @@ void free_dma(unsigned int channel) /* Clear the DMA Variable in the Channel */ atomic_set(&dma_ch[channel].chan_status, 0); - pr_debug("freedma() : END \n"); + pr_debug("freedma() : END\n"); } EXPORT_SYMBOL(free_dma); diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c index a174596cc009..e35e20f00d9b 100644 --- a/arch/blackfin/kernel/bfin_gpio.c +++ b/arch/blackfin/kernel/bfin_gpio.c @@ -1289,44 +1289,50 @@ __initcall(gpio_register_proc); #endif #ifdef CONFIG_GPIOLIB -int bfin_gpiolib_direction_input(struct gpio_chip *chip, unsigned gpio) +static int bfin_gpiolib_direction_input(struct gpio_chip *chip, unsigned gpio) { return bfin_gpio_direction_input(gpio); } -int bfin_gpiolib_direction_output(struct gpio_chip *chip, unsigned gpio, int level) +static int bfin_gpiolib_direction_output(struct gpio_chip *chip, unsigned gpio, int level) { return bfin_gpio_direction_output(gpio, level); } -int bfin_gpiolib_get_value(struct gpio_chip *chip, unsigned gpio) +static int bfin_gpiolib_get_value(struct gpio_chip *chip, unsigned gpio) { return bfin_gpio_get_value(gpio); } -void bfin_gpiolib_set_value(struct gpio_chip *chip, unsigned gpio, int value) +static void bfin_gpiolib_set_value(struct gpio_chip *chip, unsigned gpio, int value) { return bfin_gpio_set_value(gpio, value); } -int bfin_gpiolib_gpio_request(struct gpio_chip *chip, unsigned gpio) +static int bfin_gpiolib_gpio_request(struct gpio_chip *chip, unsigned gpio) { return bfin_gpio_request(gpio, chip->label); } -void bfin_gpiolib_gpio_free(struct gpio_chip *chip, unsigned gpio) +static void bfin_gpiolib_gpio_free(struct gpio_chip *chip, unsigned gpio) { return bfin_gpio_free(gpio); } +static int bfin_gpiolib_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) +{ + return gpio + GPIO_IRQ_BASE; +} + static struct gpio_chip bfin_chip = { - .label = "Blackfin-GPIOlib", + .label = "BFIN-GPIO", .direction_input = bfin_gpiolib_direction_input, .get = bfin_gpiolib_get_value, .direction_output = bfin_gpiolib_direction_output, .set = bfin_gpiolib_set_value, .request = bfin_gpiolib_gpio_request, .free = bfin_gpiolib_gpio_free, + .to_irq = bfin_gpiolib_gpio_to_irq, .base = 0, .ngpio = MAX_BLACKFIN_GPIOS, }; diff --git a/arch/blackfin/kernel/cplb-mpu/cplbinit.c b/arch/blackfin/kernel/cplb-mpu/cplbinit.c index 8d42b9e50dfa..30fd6417f069 100644 --- a/arch/blackfin/kernel/cplb-mpu/cplbinit.c +++ b/arch/blackfin/kernel/cplb-mpu/cplbinit.c @@ -64,6 +64,15 @@ void __init generate_cplb_tables_cpu(unsigned int cpu) icplb_tbl[cpu][i_i++].data = i_data | (addr == 0 ? CPLB_USER_RD : 0); } +#ifdef CONFIG_ROMKERNEL + /* Cover kernel XIP flash area */ + addr = CONFIG_ROM_BASE & ~(4 * 1024 * 1024 - 1); + dcplb_tbl[cpu][i_d].addr = addr; + dcplb_tbl[cpu][i_d++].data = d_data | CPLB_USER_RD; + icplb_tbl[cpu][i_i].addr = addr; + icplb_tbl[cpu][i_i++].data = i_data | CPLB_USER_RD; +#endif + /* 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[cpu][i_d].addr = get_l1_data_a_start_cpu(cpu); diff --git a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c index 930c01c06813..87b25b1b30ed 100644 --- a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c +++ b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c @@ -31,6 +31,12 @@ int nr_dcplb_miss[NR_CPUS], nr_icplb_miss[NR_CPUS]; int nr_icplb_supv_miss[NR_CPUS], nr_dcplb_prot[NR_CPUS]; int nr_cplb_flush[NR_CPUS]; +#ifdef CONFIG_EXCPT_IRQ_SYSC_L1 +#define MGR_ATTR __attribute__((l1_text)) +#else +#define MGR_ATTR +#endif + /* * Given the contents of the status register, return the index of the * CPLB that caused the fault. @@ -59,7 +65,7 @@ static int icplb_rr_index[NR_CPUS], dcplb_rr_index[NR_CPUS]; /* * Find an ICPLB entry to be evicted and return its index. */ -static int evict_one_icplb(unsigned int cpu) +MGR_ATTR static int evict_one_icplb(unsigned int cpu) { int i; for (i = first_switched_icplb; i < MAX_CPLBS; i++) @@ -74,7 +80,7 @@ static int evict_one_icplb(unsigned int cpu) return i; } -static int evict_one_dcplb(unsigned int cpu) +MGR_ATTR static int evict_one_dcplb(unsigned int cpu) { int i; for (i = first_switched_dcplb; i < MAX_CPLBS; i++) @@ -89,7 +95,7 @@ static int evict_one_dcplb(unsigned int cpu) return i; } -static noinline int dcplb_miss(unsigned int cpu) +MGR_ATTR static noinline int dcplb_miss(unsigned int cpu) { unsigned long addr = bfin_read_DCPLB_FAULT_ADDR(); int status = bfin_read_DCPLB_STATUS(); @@ -114,10 +120,15 @@ static noinline int dcplb_miss(unsigned int cpu) d_data = L2_DMEMORY; } else if (addr >= physical_mem_end) { if (addr >= ASYNC_BANK0_BASE && addr < ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE) { - addr &= ~(4 * 1024 * 1024 - 1); - d_data &= ~PAGE_SIZE_4KB; - d_data |= PAGE_SIZE_4MB; - d_data |= CPLB_USER_RD | CPLB_USER_WR; + mask = current_rwx_mask[cpu]; + if (mask) { + int page = (addr - (ASYNC_BANK0_BASE - _ramend)) >> PAGE_SHIFT; + int idx = page >> 5; + int bit = 1 << (page & 31); + + if (mask[idx] & bit) + d_data |= CPLB_USER_RD; + } } else if (addr >= BOOT_ROM_START && addr < BOOT_ROM_START + BOOT_ROM_LENGTH && (status & (FAULT_RW | FAULT_USERSUPV)) == FAULT_USERSUPV) { addr &= ~(1 * 1024 * 1024 - 1); @@ -126,7 +137,9 @@ static noinline int dcplb_miss(unsigned int cpu) } else return CPLB_PROT_VIOL; } else if (addr >= _ramend) { - d_data |= CPLB_USER_RD | CPLB_USER_WR; + d_data |= CPLB_USER_RD | CPLB_USER_WR; + if (reserved_mem_dcache_on) + d_data |= CPLB_L1_CHBL; } else { mask = current_rwx_mask[cpu]; if (mask) { @@ -156,7 +169,7 @@ static noinline int dcplb_miss(unsigned int cpu) return 0; } -static noinline int icplb_miss(unsigned int cpu) +MGR_ATTR static noinline int icplb_miss(unsigned int cpu) { unsigned long addr = bfin_read_ICPLB_FAULT_ADDR(); int status = bfin_read_ICPLB_STATUS(); @@ -204,10 +217,19 @@ static noinline int icplb_miss(unsigned int cpu) i_data = L2_IMEMORY; } else if (addr >= physical_mem_end) { if (addr >= ASYNC_BANK0_BASE && addr < ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE) { - addr &= ~(4 * 1024 * 1024 - 1); - i_data &= ~PAGE_SIZE_4KB; - i_data |= PAGE_SIZE_4MB; - i_data |= CPLB_USER_RD; + if (!(status & FAULT_USERSUPV)) { + unsigned long *mask = current_rwx_mask[cpu]; + + if (mask) { + int page = (addr - (ASYNC_BANK0_BASE - _ramend)) >> PAGE_SHIFT; + int idx = page >> 5; + int bit = 1 << (page & 31); + + mask += 2 * page_mask_nelts; + if (mask[idx] & bit) + i_data |= CPLB_USER_RD; + } + } } else if (addr >= BOOT_ROM_START && addr < BOOT_ROM_START + BOOT_ROM_LENGTH && (status & FAULT_USERSUPV)) { addr &= ~(1 * 1024 * 1024 - 1); @@ -217,6 +239,8 @@ static noinline int icplb_miss(unsigned int cpu) return CPLB_PROT_VIOL; } else if (addr >= _ramend) { i_data |= CPLB_USER_RD; + if (reserved_mem_icache_on) + i_data |= CPLB_L1_CHBL; } else { /* * Two cases to distinguish - a supervisor access must @@ -251,7 +275,7 @@ static noinline int icplb_miss(unsigned int cpu) return 0; } -static noinline int dcplb_protection_fault(unsigned int cpu) +MGR_ATTR static noinline int dcplb_protection_fault(unsigned int cpu) { int status = bfin_read_DCPLB_STATUS(); @@ -271,7 +295,7 @@ static noinline int dcplb_protection_fault(unsigned int cpu) return CPLB_PROT_VIOL; } -int cplb_hdr(int seqstat, struct pt_regs *regs) +MGR_ATTR int cplb_hdr(int seqstat, struct pt_regs *regs) { int cause = seqstat & 0x3f; unsigned int cpu = raw_smp_processor_id(); diff --git a/arch/blackfin/kernel/cplb-nompu/cplbinit.c b/arch/blackfin/kernel/cplb-nompu/cplbinit.c index 282a7919821b..bfe75af4e8bd 100644 --- a/arch/blackfin/kernel/cplb-nompu/cplbinit.c +++ b/arch/blackfin/kernel/cplb-nompu/cplbinit.c @@ -56,6 +56,15 @@ void __init generate_cplb_tables_cpu(unsigned int cpu) i_tbl[i_i++].data = SDRAM_IGENERIC | PAGE_SIZE_4MB; } +#ifdef CONFIG_ROMKERNEL + /* Cover kernel XIP flash area */ + addr = CONFIG_ROM_BASE & ~(4 * 1024 * 1024 - 1); + d_tbl[i_d].addr = addr; + d_tbl[i_d++].data = SDRAM_DGENERIC | PAGE_SIZE_4MB; + i_tbl[i_i].addr = addr; + i_tbl[i_i++].data = SDRAM_IGENERIC | PAGE_SIZE_4MB; +#endif + /* Cover L1 memory. One 4M area for code and data each is enough. */ if (cpu == 0) { if (L1_DATA_A_LENGTH || L1_DATA_B_LENGTH) { diff --git a/arch/blackfin/kernel/dma-mapping.c b/arch/blackfin/kernel/dma-mapping.c index e937f323d82c..04ddcfeb7981 100644 --- a/arch/blackfin/kernel/dma-mapping.c +++ b/arch/blackfin/kernel/dma-mapping.c @@ -116,7 +116,7 @@ EXPORT_SYMBOL(dma_free_coherent); void __dma_sync(dma_addr_t addr, size_t size, enum dma_data_direction dir) { - _dma_sync(addr, size, dir); + __dma_sync_inline(addr, size, dir); } EXPORT_SYMBOL(__dma_sync); diff --git a/arch/blackfin/kernel/entry.S b/arch/blackfin/kernel/entry.S index f27dc2292e1b..686478f5f66b 100644 --- a/arch/blackfin/kernel/entry.S +++ b/arch/blackfin/kernel/entry.S @@ -44,7 +44,7 @@ ENTRY(_ret_from_fork) sti r4; #endif /* CONFIG_IPIPE */ SP += -12; - call _schedule_tail; + pseudo_long_call _schedule_tail, p5; SP += 12; r0 = [sp + PT_IPEND]; cc = bittst(r0,1); @@ -79,7 +79,7 @@ ENTRY(_sys_vfork) r0 += 24; [--sp] = rets; SP += -12; - call _bfin_vfork; + pseudo_long_call _bfin_vfork, p2; SP += 12; rets = [sp++]; rts; @@ -90,7 +90,7 @@ ENTRY(_sys_clone) r0 += 24; [--sp] = rets; SP += -12; - call _bfin_clone; + pseudo_long_call _bfin_clone, p2; SP += 12; rets = [sp++]; rts; @@ -101,7 +101,7 @@ ENTRY(_sys_rt_sigreturn) r0 += 24; [--sp] = rets; SP += -12; - call _do_rt_sigreturn; + pseudo_long_call _do_rt_sigreturn, p2; SP += 12; rets = [sp++]; rts; diff --git a/arch/blackfin/kernel/ftrace-entry.S b/arch/blackfin/kernel/ftrace-entry.S index 76dd4fbcd17a..d66446b572c0 100644 --- a/arch/blackfin/kernel/ftrace-entry.S +++ b/arch/blackfin/kernel/ftrace-entry.S @@ -1,7 +1,7 @@ /* * mcount and friends -- ftrace stuff * - * Copyright (C) 2009 Analog Devices Inc. + * Copyright (C) 2009-2010 Analog Devices Inc. * Licensed under the GPL-2 or later. */ @@ -21,6 +21,15 @@ * function will be waiting there. mmmm pie. */ ENTRY(__mcount) +#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST + /* optional micro optimization: return if stopped */ + p1.l = _function_trace_stop; + p1.h = _function_trace_stop; + r3 = [p1]; + cc = r3 == 0; + if ! cc jump _ftrace_stub (bp); +#endif + /* save third function arg early so we can do testing below */ [--sp] = r2; @@ -106,9 +115,12 @@ ENTRY(_ftrace_graph_caller) [--sp] = r1; [--sp] = rets; - /* prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) */ - r0 = sp; - r1 = rets; + /* prepare_ftrace_return(parent, self_addr, frame_pointer) */ + r0 = sp; /* unsigned long *parent */ + r1 = rets; /* unsigned long self_addr */ +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST + r2 = fp; /* unsigned long frame_pointer */ +#endif r0 += 16; /* skip the 4 local regs on stack */ r1 += -MCOUNT_INSN_SIZE; call _prepare_ftrace_return; @@ -127,6 +139,9 @@ ENTRY(_return_to_handler) [--sp] = r1; /* get original return address */ +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST + r0 = fp; /* Blackfin is sane, so omit this */ +#endif call _ftrace_return_to_handler; rets = r0; diff --git a/arch/blackfin/kernel/ftrace.c b/arch/blackfin/kernel/ftrace.c index f2c85ac6f2da..a61d948ea925 100644 --- a/arch/blackfin/kernel/ftrace.c +++ b/arch/blackfin/kernel/ftrace.c @@ -16,7 +16,8 @@ * Hook the return address and push it in the stack of return addrs * in current thread info. */ -void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) +void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, + unsigned long frame_pointer) { struct ftrace_graph_ent trace; unsigned long return_hooker = (unsigned long)&return_to_handler; @@ -24,7 +25,8 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) if (unlikely(atomic_read(¤t->tracing_graph_pause))) return; - if (ftrace_push_return_trace(*parent, self_addr, &trace.depth, 0) == -EBUSY) + if (ftrace_push_return_trace(*parent, self_addr, &trace.depth, + frame_pointer) == -EBUSY) return; trace.func = self_addr; diff --git a/arch/blackfin/kernel/init_task.c b/arch/blackfin/kernel/init_task.c index 118c5b9dedac..d3970e8acd1a 100644 --- a/arch/blackfin/kernel/init_task.c +++ b/arch/blackfin/kernel/init_task.c @@ -28,5 +28,5 @@ EXPORT_SYMBOL(init_task); * "init_task" linker map entry. */ union thread_union init_thread_union - __attribute__ ((__section__(".init_task.data"))) = { + __init_task_data = { INIT_THREAD_INFO(init_task)}; diff --git a/arch/blackfin/kernel/kgdb.c b/arch/blackfin/kernel/kgdb.c index 34c7c3ed2c9c..2c501ceb1e55 100644 --- a/arch/blackfin/kernel/kgdb.c +++ b/arch/blackfin/kernel/kgdb.c @@ -145,7 +145,7 @@ void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) #endif } -struct hw_breakpoint { +static struct hw_breakpoint { unsigned int occupied:1; unsigned int skip:1; unsigned int enabled:1; @@ -155,7 +155,7 @@ struct hw_breakpoint { unsigned int addr; } breakinfo[HW_WATCHPOINT_NUM]; -int bfin_set_hw_break(unsigned long addr, int len, enum kgdb_bptype type) +static int bfin_set_hw_break(unsigned long addr, int len, enum kgdb_bptype type) { int breakno; int bfin_type; @@ -202,7 +202,7 @@ int bfin_set_hw_break(unsigned long addr, int len, enum kgdb_bptype type) return -ENOSPC; } -int bfin_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype type) +static int bfin_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype type) { int breakno; int bfin_type; @@ -230,7 +230,7 @@ int bfin_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype type) return 0; } -void bfin_remove_all_hw_break(void) +static void bfin_remove_all_hw_break(void) { int breakno; @@ -242,7 +242,7 @@ void bfin_remove_all_hw_break(void) breakinfo[breakno].type = TYPE_DATA_WATCHPOINT; } -void bfin_correct_hw_break(void) +static void bfin_correct_hw_break(void) { int breakno; unsigned int wpiactl = 0; diff --git a/arch/blackfin/kernel/nmi.c b/arch/blackfin/kernel/nmi.c new file mode 100644 index 000000000000..0b5f72f17fd0 --- /dev/null +++ b/arch/blackfin/kernel/nmi.c @@ -0,0 +1,299 @@ +/* + * Blackfin nmi_watchdog Driver + * + * Originally based on bfin_wdt.c + * Copyright 2010-2010 Analog Devices Inc. + * Graff Yang <graf.yang@analog.com> + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/bitops.h> +#include <linux/hardirq.h> +#include <linux/sysdev.h> +#include <linux/pm.h> +#include <linux/nmi.h> +#include <linux/smp.h> +#include <linux/timer.h> +#include <asm/blackfin.h> +#include <asm/atomic.h> +#include <asm/cacheflush.h> +#include <asm/bfin_watchdog.h> + +#define DRV_NAME "nmi-wdt" + +#define NMI_WDT_TIMEOUT 5 /* 5 seconds */ +#define NMI_CHECK_TIMEOUT (4 * HZ) /* 4 seconds in jiffies */ +static int nmi_wdt_cpu = 1; + +static unsigned int timeout = NMI_WDT_TIMEOUT; +static int nmi_active; + +static unsigned short wdoga_ctl; +static unsigned int wdoga_cnt; +static struct corelock_slot saved_corelock; +static atomic_t nmi_touched[NR_CPUS]; +static struct timer_list ntimer; + +enum { + COREA_ENTER_NMI = 0, + COREA_EXIT_NMI, + COREB_EXIT_NMI, + + NMI_EVENT_NR, +}; +static unsigned long nmi_event __attribute__ ((__section__(".l2.bss"))); + +/* we are in nmi, non-atomic bit ops is safe */ +static inline void set_nmi_event(int event) +{ + __set_bit(event, &nmi_event); +} + +static inline void wait_nmi_event(int event) +{ + while (!test_bit(event, &nmi_event)) + barrier(); + __clear_bit(event, &nmi_event); +} + +static inline void send_corea_nmi(void) +{ + wdoga_ctl = bfin_read_WDOGA_CTL(); + wdoga_cnt = bfin_read_WDOGA_CNT(); + + bfin_write_WDOGA_CTL(WDEN_DISABLE); + bfin_write_WDOGA_CNT(0); + bfin_write_WDOGA_CTL(WDEN_ENABLE | ICTL_NMI); +} + +static inline void restore_corea_nmi(void) +{ + bfin_write_WDOGA_CTL(WDEN_DISABLE); + bfin_write_WDOGA_CTL(WDOG_EXPIRED | WDEN_DISABLE | ICTL_NONE); + + bfin_write_WDOGA_CNT(wdoga_cnt); + bfin_write_WDOGA_CTL(wdoga_ctl); +} + +static inline void save_corelock(void) +{ + saved_corelock = corelock; + corelock.lock = 0; +} + +static inline void restore_corelock(void) +{ + corelock = saved_corelock; +} + + +static inline void nmi_wdt_keepalive(void) +{ + bfin_write_WDOGB_STAT(0); +} + +static inline void nmi_wdt_stop(void) +{ + bfin_write_WDOGB_CTL(WDEN_DISABLE); +} + +/* before calling this function, you must stop the WDT */ +static inline void nmi_wdt_clear(void) +{ + /* clear TRO bit, disable event generation */ + bfin_write_WDOGB_CTL(WDOG_EXPIRED | WDEN_DISABLE | ICTL_NONE); +} + +static inline void nmi_wdt_start(void) +{ + bfin_write_WDOGB_CTL(WDEN_ENABLE | ICTL_NMI); +} + +static inline int nmi_wdt_running(void) +{ + return ((bfin_read_WDOGB_CTL() & WDEN_MASK) != WDEN_DISABLE); +} + +static inline int nmi_wdt_set_timeout(unsigned long t) +{ + u32 cnt, max_t, sclk; + int run; + + sclk = get_sclk(); + max_t = -1 / sclk; + cnt = t * sclk; + if (t > max_t) { + pr_warning("NMI: timeout value is too large\n"); + return -EINVAL; + } + + run = nmi_wdt_running(); + nmi_wdt_stop(); + bfin_write_WDOGB_CNT(cnt); + if (run) + nmi_wdt_start(); + + timeout = t; + + return 0; +} + +int check_nmi_wdt_touched(void) +{ + unsigned int this_cpu = smp_processor_id(); + unsigned int cpu; + + cpumask_t mask = cpu_online_map; + + if (!atomic_read(&nmi_touched[this_cpu])) + return 0; + + atomic_set(&nmi_touched[this_cpu], 0); + + cpu_clear(this_cpu, mask); + for_each_cpu_mask(cpu, mask) { + invalidate_dcache_range((unsigned long)(&nmi_touched[cpu]), + (unsigned long)(&nmi_touched[cpu])); + if (!atomic_read(&nmi_touched[cpu])) + return 0; + atomic_set(&nmi_touched[cpu], 0); + } + + return 1; +} + +static void nmi_wdt_timer(unsigned long data) +{ + if (check_nmi_wdt_touched()) + nmi_wdt_keepalive(); + + mod_timer(&ntimer, jiffies + NMI_CHECK_TIMEOUT); +} + +static int __init init_nmi_wdt(void) +{ + nmi_wdt_set_timeout(timeout); + nmi_wdt_start(); + nmi_active = true; + + init_timer(&ntimer); + ntimer.function = nmi_wdt_timer; + ntimer.expires = jiffies + NMI_CHECK_TIMEOUT; + add_timer(&ntimer); + + pr_info("nmi_wdt: initialized: timeout=%d sec\n", timeout); + return 0; +} +device_initcall(init_nmi_wdt); + +void touch_nmi_watchdog(void) +{ + atomic_set(&nmi_touched[smp_processor_id()], 1); +} + +/* Suspend/resume support */ +#ifdef CONFIG_PM +static int nmi_wdt_suspend(struct sys_device *dev, pm_message_t state) +{ + nmi_wdt_stop(); + return 0; +} + +static int nmi_wdt_resume(struct sys_device *dev) +{ + if (nmi_active) + nmi_wdt_start(); + return 0; +} + +static struct sysdev_class nmi_sysclass = { + .name = DRV_NAME, + .resume = nmi_wdt_resume, + .suspend = nmi_wdt_suspend, +}; + +static struct sys_device device_nmi_wdt = { + .id = 0, + .cls = &nmi_sysclass, +}; + +static int __init init_nmi_wdt_sysfs(void) +{ + int error; + + if (!nmi_active) + return 0; + + error = sysdev_class_register(&nmi_sysclass); + if (!error) + error = sysdev_register(&device_nmi_wdt); + return error; +} +late_initcall(init_nmi_wdt_sysfs); + +#endif /* CONFIG_PM */ + + +asmlinkage notrace void do_nmi(struct pt_regs *fp) +{ + unsigned int cpu = smp_processor_id(); + nmi_enter(); + + cpu_pda[cpu].__nmi_count += 1; + + if (cpu == nmi_wdt_cpu) { + /* CoreB goes here first */ + + /* reload the WDOG_STAT */ + nmi_wdt_keepalive(); + + /* clear nmi interrupt for CoreB */ + nmi_wdt_stop(); + nmi_wdt_clear(); + + /* trigger NMI interrupt of CoreA */ + send_corea_nmi(); + + /* waiting CoreB to enter NMI */ + wait_nmi_event(COREA_ENTER_NMI); + + /* recover WDOGA's settings */ + restore_corea_nmi(); + + save_corelock(); + + /* corelock is save/cleared, CoreA is dummping messages */ + + wait_nmi_event(COREA_EXIT_NMI); + } else { + /* OK, CoreA entered NMI */ + set_nmi_event(COREA_ENTER_NMI); + } + + pr_emerg("\nNMI Watchdog detected LOCKUP, dump for CPU %d\n", cpu); + dump_bfin_process(fp); + dump_bfin_mem(fp); + show_regs(fp); + dump_bfin_trace_buffer(); + show_stack(current, (unsigned long *)fp); + + if (cpu == nmi_wdt_cpu) { + pr_emerg("This fault is not recoverable, sorry!\n"); + + /* CoreA dump finished, restore the corelock */ + restore_corelock(); + + set_nmi_event(COREB_EXIT_NMI); + } else { + /* CoreB dump finished, notice the CoreA we are done */ + set_nmi_event(COREA_EXIT_NMI); + + /* synchronize with CoreA */ + wait_nmi_event(COREB_EXIT_NMI); + } + + nmi_exit(); +} diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c index b56b0e485e0b..29705cec91de 100644 --- a/arch/blackfin/kernel/process.c +++ b/arch/blackfin/kernel/process.c @@ -98,13 +98,6 @@ void cpu_idle(void) } } -/* Fill in the fpu structure for a core dump. */ - -int dump_fpu(struct pt_regs *regs, elf_fpregset_t * fpregs) -{ - return 1; -} - /* * This gets run with P1 containing the * function to call, and R1 containing diff --git a/arch/blackfin/kernel/ptrace.c b/arch/blackfin/kernel/ptrace.c index 65567dc4b9f5..43eb969405d1 100644 --- a/arch/blackfin/kernel/ptrace.c +++ b/arch/blackfin/kernel/ptrace.c @@ -1,6 +1,6 @@ /* * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds - * these modifications are Copyright 2004-2009 Analog Devices Inc. + * these modifications are Copyright 2004-2010 Analog Devices Inc. * * Licensed under the GPL-2 */ @@ -9,10 +9,13 @@ #include <linux/sched.h> #include <linux/mm.h> #include <linux/smp.h> +#include <linux/elf.h> #include <linux/errno.h> #include <linux/ptrace.h> #include <linux/user.h> +#include <linux/regset.h> #include <linux/signal.h> +#include <linux/tracehook.h> #include <linux/uaccess.h> #include <asm/page.h> @@ -25,90 +28,57 @@ #include <asm/cacheflush.h> #include <asm/mem_map.h> -#define TEXT_OFFSET 0 /* * does not yet catch signals sent when the child dies. * in exit.c or in signal.c. */ -/* determines which bits in the SYSCFG reg the user has access to. */ -/* 1 = access 0 = no access */ -#define SYSCFG_MASK 0x0007 /* SYSCFG reg */ -/* sets the trace bits. */ -#define TRACE_BITS 0x0001 - -/* Find the stack offset for a register, relative to thread.esp0. */ -#define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg) - -/* - * Get the address of the live pt_regs for the specified task. - * These are saved onto the top kernel stack when the process - * is not running. - * - * Note: if a user thread is execve'd from kernel space, the - * kernel stack will not be empty on entry to the kernel, so - * ptracing these tasks will fail. - */ -static inline struct pt_regs *get_user_regs(struct task_struct *task) -{ - return (struct pt_regs *) - ((unsigned long)task_stack_page(task) + - (THREAD_SIZE - sizeof(struct pt_regs))); -} - -/* - * Get all user integer registers. - */ -static inline int ptrace_getregs(struct task_struct *tsk, void __user *uregs) -{ - struct pt_regs regs; - memcpy(®s, get_user_regs(tsk), sizeof(regs)); - regs.usp = tsk->thread.usp; - return copy_to_user(uregs, ®s, sizeof(struct pt_regs)) ? -EFAULT : 0; -} - -/* Mapping from PT_xxx to the stack offset at which the register is - * saved. Notice that usp has no stack-slot and needs to be treated - * specially (see get_reg/put_reg below). - */ - /* * Get contents of register REGNO in task TASK. */ -static inline long get_reg(struct task_struct *task, int regno) +static inline long +get_reg(struct task_struct *task, long regno, unsigned long __user *datap) { - unsigned char *reg_ptr; + long tmp; + struct pt_regs *regs = task_pt_regs(task); - struct pt_regs *regs = - (struct pt_regs *)((unsigned long)task_stack_page(task) + - (THREAD_SIZE - sizeof(struct pt_regs))); - reg_ptr = (char *)regs; + if (regno & 3 || regno > PT_LAST_PSEUDO || regno < 0) + return -EIO; switch (regno) { + case PT_TEXT_ADDR: + tmp = task->mm->start_code; + break; + case PT_TEXT_END_ADDR: + tmp = task->mm->end_code; + break; + case PT_DATA_ADDR: + tmp = task->mm->start_data; + break; case PT_USP: - return task->thread.usp; + tmp = task->thread.usp; + break; default: - if (regno <= 216) - return *(long *)(reg_ptr + regno); + if (regno < sizeof(*regs)) { + void *reg_ptr = regs; + tmp = *(long *)(reg_ptr + regno); + } else + return -EIO; } - /* slight mystery ... never seems to come here but kernel misbehaves without this code! */ - printk(KERN_WARNING "Request to get for unknown register %d\n", regno); - return 0; + return put_user(tmp, datap); } /* * Write contents of register REGNO in task TASK. */ |
