diff options
Diffstat (limited to 'arch/blackfin/mach-common')
| -rw-r--r-- | arch/blackfin/mach-common/Makefile | 13 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/arch_checks.c | 63 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/cache-c.c | 85 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/cache.S | 321 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/cacheinit.S | 77 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/clock.h | 27 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/clocks-init.c | 121 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/cpufreq.c | 200 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/dpmc.c | 79 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/dpmc_modes.S | 258 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/entry.S | 840 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/head.S | 229 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/interrupt.S | 228 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/ints-priority.c | 1670 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/irqpanic.c | 146 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/lock.S | 206 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/pm.c | 287 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/scb-init.c | 52 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/smp.c | 433 |
19 files changed, 3186 insertions, 2149 deletions
diff --git a/arch/blackfin/mach-common/Makefile b/arch/blackfin/mach-common/Makefile index 422bfee34ad..f0997920404 100644 --- a/arch/blackfin/mach-common/Makefile +++ b/arch/blackfin/mach-common/Makefile @@ -3,9 +3,14 @@ # obj-y := \ - cache.o cacheinit.o entry.o \ - interrupt.o lock.o irqpanic.o arch_checks.o ints-priority.o + cache.o cache-c.o entry.o head.o \ + interrupt.o arch_checks.o ints-priority.o -obj-$(CONFIG_PM) += pm.o dpmc_modes.o -obj-$(CONFIG_CPU_FREQ) += cpufreq.o +obj-$(CONFIG_PM) += pm.o +ifneq ($(CONFIG_BF60x),y) +obj-$(CONFIG_PM) += dpmc_modes.o +endif +obj-$(CONFIG_SCB_PRIORITY) += scb-init.o obj-$(CONFIG_CPU_VOLTAGE) += dpmc.o +obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_BFIN_KERNEL_CLOCK) += clocks-init.o diff --git a/arch/blackfin/mach-common/arch_checks.c b/arch/blackfin/mach-common/arch_checks.c index caaab49e9cf..d8643fdd0fc 100644 --- a/arch/blackfin/mach-common/arch_checks.c +++ b/arch/blackfin/mach-common/arch_checks.c @@ -1,34 +1,14 @@ /* - * File: arch/blackfin/mach-common/arch_checks.c - * Based on: - * Author: Robin Getz <rgetz@blackfin.uclinux.org> + * Do some checking to make sure things are OK * - * Created: 25Jul07 - * Description: Do some checking to make sure things are OK + * Copyright 2007-2010 Analog Devices Inc. * - * Modified: - * 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 + * Licensed under the GPL-2 or later. */ -#include <asm/mach/anomaly.h> -#include <asm/mach-common/clocks.h> +#include <asm/fixed_code.h> +#include <mach/anomaly.h> +#include <asm/clocks.h> #ifdef CONFIG_BFIN_KERNEL_CLOCK @@ -54,8 +34,33 @@ #endif /* CONFIG_BFIN_KERNEL_CLOCK */ -#ifdef CONFIG_MEM_SIZE -#if (CONFIG_MEM_SIZE % 4) -#error "SDRAM mem size must be multible of 4MB" +#if CONFIG_BOOT_LOAD < FIXED_CODE_END +# error "The kernel load address must be after the fixed code section" +#endif + +#if (CONFIG_BOOT_LOAD & 0x3) +# error "The kernel load address must be 4 byte aligned" +#endif + +/* The entire kernel must be able to make a 24bit pcrel call to start of L1 */ +#if ((0xffffffff - L1_CODE_START + 1) + CONFIG_BOOT_LOAD) > 0x1000000 +# error "The kernel load address is too high; keep it below 10meg for safety" #endif + +#if ANOMALY_05000263 && defined(CONFIG_MPU) +# error the MPU will not function safely while Anomaly 05000263 applies +#endif + +#if ANOMALY_05000448 +# error You are using a part with anomaly 05000448, this issue causes random memory read/write failures - that means random crashes. +#endif + +/* if 220 exists, can not set External Memory WB and L2 not_cached, either External Memory not_cached and L2 WB */ +#if ANOMALY_05000220 && \ + (defined(CONFIG_BFIN_EXTMEM_WRITEBACK) || defined(CONFIG_BFIN_L2_WRITEBACK)) +# error "Anomaly 05000220 does not allow you to use Write Back cache with L2 or External Memory" +#endif + +#if ANOMALY_05000491 && !defined(CONFIG_ICACHE_FLUSH_L1) +# error You need IFLUSH in L1 inst while Anomaly 05000491 applies #endif diff --git a/arch/blackfin/mach-common/cache-c.c b/arch/blackfin/mach-common/cache-c.c new file mode 100644 index 00000000000..f4adedc9289 --- /dev/null +++ b/arch/blackfin/mach-common/cache-c.c @@ -0,0 +1,85 @@ +/* + * Blackfin cache control code (simpler control-style functions) + * + * Copyright 2004-2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <asm/blackfin.h> +#include <asm/cplbinit.h> + +/* Invalidate the Entire Data cache by + * clearing DMC[1:0] bits + */ +void blackfin_invalidate_entire_dcache(void) +{ + u32 dmem = bfin_read_DMEM_CONTROL(); + bfin_write_DMEM_CONTROL(dmem & ~0xc); + SSYNC(); + bfin_write_DMEM_CONTROL(dmem); + SSYNC(); +} + +/* Invalidate the Entire Instruction cache by + * clearing IMC bit + */ +void blackfin_invalidate_entire_icache(void) +{ + u32 imem = bfin_read_IMEM_CONTROL(); + bfin_write_IMEM_CONTROL(imem & ~0x4); + SSYNC(); + bfin_write_IMEM_CONTROL(imem); + SSYNC(); +} + +#if defined(CONFIG_BFIN_ICACHE) || defined(CONFIG_BFIN_DCACHE) + +static void +bfin_cache_init(struct cplb_entry *cplb_tbl, unsigned long cplb_addr, + unsigned long cplb_data, unsigned long mem_control, + unsigned long mem_mask) +{ + int i; +#ifdef CONFIG_L1_PARITY_CHECK + u32 ctrl; + + if (cplb_addr == DCPLB_ADDR0) { + ctrl = bfin_read32(mem_control) | (1 << RDCHK); + CSYNC(); + bfin_write32(mem_control, ctrl); + SSYNC(); + } +#endif + + for (i = 0; i < MAX_CPLBS; i++) { + bfin_write32(cplb_addr + i * 4, cplb_tbl[i].addr); + bfin_write32(cplb_data + i * 4, cplb_tbl[i].data); + } + + _enable_cplb(mem_control, mem_mask); +} + +#ifdef CONFIG_BFIN_ICACHE +void bfin_icache_init(struct cplb_entry *icplb_tbl) +{ + bfin_cache_init(icplb_tbl, ICPLB_ADDR0, ICPLB_DATA0, IMEM_CONTROL, + (IMC | ENICPLB)); +} +#endif + +#ifdef CONFIG_BFIN_DCACHE +void bfin_dcache_init(struct cplb_entry *dcplb_tbl) +{ + /* + * Anomaly notes: + * 05000287 - We implement workaround #2 - Change the DMEM_CONTROL + * register, so that the port preferences for DAG0 and DAG1 are set + * to port B + */ + bfin_cache_init(dcplb_tbl, DCPLB_ADDR0, DCPLB_DATA0, DMEM_CONTROL, + (DMEM_CNTR | PORT_PREF0 | (ANOMALY_05000287 ? PORT_PREF1 : 0))); +} +#endif + +#endif diff --git a/arch/blackfin/mach-common/cache.S b/arch/blackfin/mach-common/cache.S index 0521b158820..9f4dd35bfd7 100644 --- a/arch/blackfin/mach-common/cache.S +++ b/arch/blackfin/mach-common/cache.S @@ -1,263 +1,124 @@ /* - * File: arch/blackfin/mach-common/cache.S - * Based on: - * Author: LG Soft India + * Blackfin cache control code * - * Created: - * Description: cache control support + * Copyright 2004-2008 Analog Devices Inc. * - * 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 + * Licensed under the GPL-2 or later. */ #include <linux/linkage.h> -#include <asm/cplb.h> -#include <asm/entry.h> #include <asm/blackfin.h> #include <asm/cache.h> +#include <asm/page.h> + +/* 05000443 - IFLUSH cannot be last instruction in hardware loop */ +#if ANOMALY_05000443 +# define BROK_FLUSH_INST "IFLUSH" +#else +# define BROK_FLUSH_INST "no anomaly! yeah!" +#endif + +/* Since all L1 caches work the same way, we use the same method for flushing + * them. Only the actual flush instruction differs. We write this in asm as + * GCC can be hard to coax into writing nice hardware loops. + * + * Also, we assume the following register setup: + * R0 = start address + * R1 = end address + */ +.macro do_flush flushins:req label -.text -.align 2 -ENTRY(_cache_invalidate) - - /* - * Icache or DcacheA or DcacheB Invalidation - * or any combination thereof - * R0 has bits - * CPLB_ENABLE_ICACHE_P,CPLB_ENABLE_DCACHE_P,CPLB_ENABLE_DCACHE2_P - * set as required - */ - [--SP] = R7; - - R7 = R0; - CC = BITTST(R7,CPLB_ENABLE_ICACHE_P); - IF !CC JUMP .Lno_icache; - [--SP] = RETS; - CALL _icache_invalidate; - RETS = [SP++]; -.Lno_icache: - CC = BITTST(R7,CPLB_ENABLE_DCACHE_P); - IF !CC JUMP .Lno_dcache_a; - R0 = 0; /* specifies bank A */ - [--SP] = RETS; - CALL _dcache_invalidate; - RETS = [SP++]; -.Lno_dcache_a: - CC = BITTST(R7,CPLB_ENABLE_DCACHE2_P); - IF !CC JUMP .Lno_dcache_b; - R0 = 0; - BITSET(R0, 23); /* specifies bank B */ - [--SP] = RETS; - CALL _dcache_invalidate; - RETS = [SP++]; -.Lno_dcache_b: - R7 = [SP++]; - RTS; -ENDPROC(_cache_invalidate) + R2 = -L1_CACHE_BYTES; -/* Invalidate the Entire Instruction cache by - * disabling IMC bit - */ -ENTRY(_icache_invalidate) -ENTRY(_invalidate_entire_icache) - [--SP] = ( R7:5); + /* start = (start & -L1_CACHE_BYTES) */ + R0 = R0 & R2; - P0.L = LO(IMEM_CONTROL); - P0.H = HI(IMEM_CONTROL); - R7 = [P0]; + /* end = ((end - 1) & -L1_CACHE_BYTES) + L1_CACHE_BYTES; */ + R1 += -1; + R1 = R1 & R2; + R1 += L1_CACHE_BYTES; - /* Clear the IMC bit , All valid bits in the instruction - * cache are set to the invalid state - */ - BITCLR(R7,IMC_P); - CLI R6; - SSYNC; /* SSYNC required before invalidating cache. */ - .align 8; - [P0] = R7; - SSYNC; - STI R6; + /* count = (end - start) >> L1_CACHE_SHIFT */ + R2 = R1 - R0; + R2 >>= L1_CACHE_SHIFT; + P1 = R2; - /* Configures the instruction cache agian */ - R6 = (IMC | ENICPLB); - R7 = R7 | R6; +.ifnb \label +\label : +.endif + P0 = R0; - CLI R6; - SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */ - .align 8; - [P0] = R7; - SSYNC; - STI R6; + LSETUP (1f, 2f) LC1 = P1; +1: +.ifeqs "\flushins", BROK_FLUSH_INST + \flushins [P0++]; + nop; + nop; +2: nop; +.else +2: \flushins [P0++]; +.endif - ( R7:5) = [SP++]; RTS; -ENDPROC(_invalidate_entire_icache) -ENDPROC(_icache_invalidate) +.endm -/* - * blackfin_cache_flush_range(start, end) - * Invalidate all cache lines assocoiated with this - * area of memory. - * - * start: Start address - * end: End address - */ +#ifdef CONFIG_ICACHE_FLUSH_L1 +.section .l1.text +#else +.text +#endif + +/* Invalidate all instruction cache lines assocoiated with this memory area */ +#ifdef CONFIG_SMP +# define _blackfin_icache_flush_range _blackfin_icache_flush_range_l1 +#endif ENTRY(_blackfin_icache_flush_range) - R2 = -L1_CACHE_BYTES; - R2 = R0 & R2; - P0 = R2; - P1 = R1; - CSYNC(R3); - IFLUSH [P0]; -1: - IFLUSH [P0++]; - CC = P0 < P1 (iu); - IF CC JUMP 1b (bp); - IFLUSH [P0]; - SSYNC(R3); - RTS; + do_flush IFLUSH ENDPROC(_blackfin_icache_flush_range) -/* - * blackfin_icache_dcache_flush_range(start, end) - * FLUSH all cache lines assocoiated with this - * area of memory. - * - * start: Start address - * end: End address - */ +#ifdef CONFIG_SMP +.text +# undef _blackfin_icache_flush_range +ENTRY(_blackfin_icache_flush_range) + p0.L = LO(DSPID); + p0.H = HI(DSPID); + r3 = [p0]; + r3 = r3.b (z); + p2 = r3; + p0.L = _blackfin_iflush_l1_entry; + p0.H = _blackfin_iflush_l1_entry; + p0 = p0 + (p2 << 2); + p1 = [p0]; + jump (p1); +ENDPROC(_blackfin_icache_flush_range) +#endif -ENTRY(_blackfin_icache_dcache_flush_range) - R2 = -L1_CACHE_BYTES; - R2 = R0 & R2; - P0 = R2; - P1 = R1; - CSYNC(R3); - IFLUSH [P0]; -1: - FLUSH [P0]; - IFLUSH [P0++]; - CC = P0 < P1 (iu); - IF CC JUMP 1b (bp); - IFLUSH [P0]; - FLUSH [P0]; - SSYNC(R3); - RTS; -ENDPROC(_blackfin_icache_dcache_flush_range) +#ifdef CONFIG_DCACHE_FLUSH_L1 +.section .l1.text +#else +.text +#endif /* Throw away all D-cached data in specified region without any obligation to - * write them back. However, we must clean the D-cached entries around the - * boundaries of the start and/or end address is not cache aligned. - * - * Start: start address, - * end : end address. + * write them back. Since the Blackfin ISA does not have an "invalidate" + * instruction, we use flush/invalidate. Perhaps as a speed optimization we + * could bang on the DTEST MMRs ... */ - ENTRY(_blackfin_dcache_invalidate_range) - R2 = -L1_CACHE_BYTES; - R2 = R0 & R2; - P0 = R2; - P1 = R1; - CSYNC(R3); - FLUSHINV[P0]; -1: - FLUSHINV[P0++]; - CC = P0 < P1 (iu); - IF CC JUMP 1b (bp); - - /* If the data crosses a cache line, then we'll be pointing to - * the last cache line, but won't have flushed/invalidated it yet, - * so do one more. - */ - FLUSHINV[P0]; - SSYNC(R3); - RTS; + do_flush FLUSHINV ENDPROC(_blackfin_dcache_invalidate_range) -/* Invalidate the Entire Data cache by - * clearing DMC[1:0] bits - */ -ENTRY(_invalidate_entire_dcache) -ENTRY(_dcache_invalidate) - [--SP] = ( R7:6); - - P0.L = LO(DMEM_CONTROL); - P0.H = HI(DMEM_CONTROL); - R7 = [P0]; - - /* Clear the DMC[1:0] bits, All valid bits in the data - * cache are set to the invalid state - */ - BITCLR(R7,DMC0_P); - BITCLR(R7,DMC1_P); - CLI R6; - SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */ - .align 8; - [P0] = R7; - SSYNC; - STI R6; - - /* Configures the data cache again */ - - R6 = DMEM_CNTR; - R7 = R7 | R6; - - CLI R6; - SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */ - .align 8; - [P0] = R7; - SSYNC; - STI R6; - - ( R7:6) = [SP++]; - RTS; -ENDPROC(_dcache_invalidate) -ENDPROC(_invalidate_entire_dcache) - +/* Flush all data cache lines assocoiated with this memory area */ ENTRY(_blackfin_dcache_flush_range) - R2 = -L1_CACHE_BYTES; - R2 = R0 & R2; - P0 = R2; - P1 = R1; - CSYNC(R3); - FLUSH[P0]; -1: - FLUSH[P0++]; - CC = P0 < P1 (iu); - IF CC JUMP 1b (bp); - - /* If the data crosses a cache line, then we'll be pointing to - * the last cache line, but won't have flushed it yet, so do - * one more. - */ - FLUSH[P0]; - SSYNC(R3); - RTS; + do_flush FLUSH, .Ldfr ENDPROC(_blackfin_dcache_flush_range) +/* Our headers convert the page structure to an address, so just need to flush + * its contents like normal. We know the start address is page aligned (which + * greater than our cache alignment), as is the end address. So just jump into + * the middle of the dcache flush function. + */ ENTRY(_blackfin_dflush_page) P1 = 1 << (PAGE_SHIFT - L1_CACHE_SHIFT); - P0 = R0; - CSYNC(R3); - FLUSH[P0]; - LSETUP (.Lfl1, .Lfl1) LC0 = P1; -.Lfl1: FLUSH [P0++]; - SSYNC(R3); - RTS; + jump .Ldfr; ENDPROC(_blackfin_dflush_page) diff --git a/arch/blackfin/mach-common/cacheinit.S b/arch/blackfin/mach-common/cacheinit.S deleted file mode 100644 index 22fada0c1cb..00000000000 --- a/arch/blackfin/mach-common/cacheinit.S +++ /dev/null @@ -1,77 +0,0 @@ -/* - * File: arch/blackfin/mach-common/cacheinit.S - * Based on: - * Author: LG Soft India - * - * Created: ? - * Description: cache initialization - * - * 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 - */ - -/* This function sets up the data and instruction cache. The - * tables like icplb table, dcplb table and Page Descriptor table - * are defined in cplbtab.h. You can configure those tables for - * your suitable requirements - */ - -#include <linux/linkage.h> -#include <asm/blackfin.h> - -.text - -#if ANOMALY_05000125 -#if defined(CONFIG_BFIN_ICACHE) -ENTRY(_bfin_write_IMEM_CONTROL) - - /* Enable Instruction Cache */ - P0.l = LO(IMEM_CONTROL); - P0.h = HI(IMEM_CONTROL); - - /* Anomaly 05000125 */ - CLI R1; - SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */ - .align 8; - [P0] = R0; - SSYNC; - STI R1; - RTS; - -ENDPROC(_bfin_write_IMEM_CONTROL) -#endif - -#if defined(CONFIG_BFIN_DCACHE) -ENTRY(_bfin_write_DMEM_CONTROL) - P0.l = LO(DMEM_CONTROL); - P0.h = HI(DMEM_CONTROL); - - CLI R1; - SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */ - .align 8; - [P0] = R0; - SSYNC; - STI R1; - RTS; - -ENDPROC(_bfin_write_DMEM_CONTROL) -#endif - -#endif diff --git a/arch/blackfin/mach-common/clock.h b/arch/blackfin/mach-common/clock.h new file mode 100644 index 00000000000..645ff460a1f --- /dev/null +++ b/arch/blackfin/mach-common/clock.h @@ -0,0 +1,27 @@ +#ifndef __MACH_COMMON_CLKDEV_H +#define __MACH_COMMON_CLKDEV_H + +#include <linux/clk.h> + +struct clk_ops { + unsigned long (*get_rate)(struct clk *clk); + unsigned long (*round_rate)(struct clk *clk, unsigned long rate); + int (*set_rate)(struct clk *clk, unsigned long rate); + int (*enable)(struct clk *clk); + int (*disable)(struct clk *clk); +}; + +struct clk { + const char *name; + unsigned long rate; + spinlock_t lock; + u32 flags; + const struct clk_ops *ops; + const struct params *params; + void __iomem *reg; + u32 mask; + u32 shift; +}; + +#endif + diff --git a/arch/blackfin/mach-common/clocks-init.c b/arch/blackfin/mach-common/clocks-init.c new file mode 100644 index 00000000000..d436bd907fc --- /dev/null +++ b/arch/blackfin/mach-common/clocks-init.c @@ -0,0 +1,121 @@ +/* + * arch/blackfin/mach-common/clocks-init.c - reprogram clocks / memory + * + * Copyright 2004-2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/linkage.h> +#include <asm/blackfin.h> + +#include <asm/dma.h> +#include <asm/clocks.h> +#include <asm/mem_init.h> +#include <asm/dpmc.h> + +#ifdef CONFIG_BF60x + +#define CGU_CTL_VAL ((CONFIG_VCO_MULT << 8) | CLKIN_HALF) +#define CGU_DIV_VAL \ + ((CONFIG_CCLK_DIV << CSEL_OFFSET) | \ + (CONFIG_SCLK_DIV << SYSSEL_OFFSET) | \ + (CONFIG_SCLK0_DIV << S0SEL_OFFSET) | \ + (CONFIG_SCLK1_DIV << S1SEL_OFFSET) | \ + (CONFIG_DCLK_DIV << DSEL_OFFSET)) + +#define CONFIG_BFIN_DCLK (((CONFIG_CLKIN_HZ * CONFIG_VCO_MULT) / CONFIG_DCLK_DIV) / 1000000) +#if ((CONFIG_BFIN_DCLK != 125) && \ + (CONFIG_BFIN_DCLK != 133) && (CONFIG_BFIN_DCLK != 150) && \ + (CONFIG_BFIN_DCLK != 166) && (CONFIG_BFIN_DCLK != 200) && \ + (CONFIG_BFIN_DCLK != 225) && (CONFIG_BFIN_DCLK != 250)) +#error "DCLK must be in (125, 133, 150, 166, 200, 225, 250)MHz" +#endif + +#else +#define SDGCTL_WIDTH (1 << 31) /* SDRAM external data path width */ +#define PLL_CTL_VAL \ + (((CONFIG_VCO_MULT & 63) << 9) | CLKIN_HALF | \ + (PLL_BYPASS << 8) | (ANOMALY_05000305 ? 0 : 0x8000)) +#endif + +__attribute__((l1_text)) +static void do_sync(void) +{ + __builtin_bfin_ssync(); +} + +__attribute__((l1_text)) +void init_clocks(void) +{ + /* Kill any active DMAs as they may trigger external memory accesses + * in the middle of reprogramming things, and that'll screw us up. + * For example, any automatic DMAs left by U-Boot for splash screens. + */ +#ifdef CONFIG_BF60x + init_cgu(CGU_DIV_VAL, CGU_CTL_VAL); + init_dmc(CONFIG_BFIN_DCLK); +#else + size_t i; + for (i = 0; i < MAX_DMA_CHANNELS; ++i) { + struct dma_register *dma = dma_io_base_addr[i]; + dma->cfg = 0; + } + + do_sync(); + +#ifdef SIC_IWR0 + bfin_write_SIC_IWR0(IWR_ENABLE(0)); +# ifdef SIC_IWR1 + /* BF52x system reset does not properly reset SIC_IWR1 which + * will screw up the bootrom as it relies on MDMA0/1 waking it + * up from IDLE instructions. See this report for more info: + * http://blackfin.uclinux.org/gf/tracker/4323 + */ + if (ANOMALY_05000435) + bfin_write_SIC_IWR1(IWR_ENABLE(10) | IWR_ENABLE(11)); + else + bfin_write_SIC_IWR1(IWR_DISABLE_ALL); +# endif +# ifdef SIC_IWR2 + bfin_write_SIC_IWR2(IWR_DISABLE_ALL); +# endif +#else + bfin_write_SIC_IWR(IWR_ENABLE(0)); +#endif + do_sync(); +#ifdef EBIU_SDGCTL + bfin_write_EBIU_SDGCTL(bfin_read_EBIU_SDGCTL() | SRFS); + do_sync(); +#endif + +#ifdef CLKBUFOE + bfin_write16(VR_CTL, bfin_read_VR_CTL() | CLKBUFOE); + do_sync(); + __asm__ __volatile__("IDLE;"); +#endif + bfin_write_PLL_LOCKCNT(0x300); + do_sync(); + /* We always write PLL_CTL thus avoiding Anomaly 05000242 */ + bfin_write16(PLL_CTL, PLL_CTL_VAL); + __asm__ __volatile__("IDLE;"); + bfin_write_PLL_DIV(CONFIG_CCLK_ACT_DIV | CONFIG_SCLK_DIV); +#ifdef EBIU_SDGCTL + bfin_write_EBIU_SDRRC(mem_SDRRC); + bfin_write_EBIU_SDGCTL((bfin_read_EBIU_SDGCTL() & SDGCTL_WIDTH) | mem_SDGCTL); +#else + bfin_write_EBIU_RSTCTL(bfin_read_EBIU_RSTCTL() & ~(SRREQ)); + do_sync(); + bfin_write_EBIU_RSTCTL(bfin_read_EBIU_RSTCTL() | 0x1); + bfin_write_EBIU_DDRCTL0(mem_DDRCTL0); + bfin_write_EBIU_DDRCTL1(mem_DDRCTL1); + bfin_write_EBIU_DDRCTL2(mem_DDRCTL2); +#ifdef CONFIG_MEM_EBIU_DDRQUE + bfin_write_EBIU_DDRQUE(CONFIG_MEM_EBIU_DDRQUE); +#endif +#endif +#endif + do_sync(); + bfin_read16(0); + +} diff --git a/arch/blackfin/mach-common/cpufreq.c b/arch/blackfin/mach-common/cpufreq.c deleted file mode 100644 index 75cdad291e8..00000000000 --- a/arch/blackfin/mach-common/cpufreq.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * File: arch/blackfin/mach-common/cpufreq.c - * Based on: - * Author: - * - * Created: - * Description: Blackfin core clock scaling - * - * Modified: - * Copyright 2004-2008 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 <linux/kernel.h> -#include <linux/types.h> -#include <linux/init.h> -#include <linux/cpufreq.h> -#include <linux/fs.h> -#include <asm/blackfin.h> -#include <asm/time.h> - - -/* this is the table of CCLK frequencies, in Hz */ -/* .index is the entry in the auxillary dpm_state_table[] */ -static struct cpufreq_frequency_table bfin_freq_table[] = { - { - .frequency = CPUFREQ_TABLE_END, - .index = 0, - }, - { - .frequency = CPUFREQ_TABLE_END, - .index = 1, - }, - { - .frequency = CPUFREQ_TABLE_END, - .index = 2, - }, - { - .frequency = CPUFREQ_TABLE_END, - .index = 0, - }, -}; - -static struct bfin_dpm_state { - unsigned int csel; /* system clock divider */ - unsigned int tscale; /* change the divider on the core timer interrupt */ -} dpm_state_table[3]; - -/* - normalized to maximum frequncy offset for CYCLES, - used in time-ts cycles clock source, but could be used - somewhere also. - */ -unsigned long long __bfin_cycles_off; -unsigned int __bfin_cycles_mod; - -/**************************************************************************/ - -static unsigned int bfin_getfreq(unsigned int cpu) -{ - /* The driver only support single cpu */ - if (cpu != 0) - return -1; - - return get_cclk(); -} - - -static int bfin_target(struct cpufreq_policy *policy, - unsigned int target_freq, unsigned int relation) -{ - unsigned int index, plldiv, tscale; - unsigned long flags, cclk_hz; - struct cpufreq_freqs freqs; - cycles_t cycles; - - if (cpufreq_frequency_table_target(policy, bfin_freq_table, - target_freq, relation, &index)) - return -EINVAL; - - cclk_hz = bfin_freq_table[index].frequency; - - freqs.old = bfin_getfreq(0); - freqs.new = cclk_hz; - freqs.cpu = 0; - - pr_debug("cpufreq: changing cclk to %lu; target = %u, oldfreq = %u\n", - cclk_hz, target_freq, freqs.old); - - cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - local_irq_save(flags); - plldiv = (bfin_read_PLL_DIV() & SSEL) | dpm_state_table[index].csel; - tscale = dpm_state_table[index].tscale; - bfin_write_PLL_DIV(plldiv); - /* we have to adjust the core timer, because it is using cclk */ - bfin_write_TSCALE(tscale); - cycles = get_cycles(); - SSYNC(); - cycles += 10; /* ~10 cycles we loose after get_cycles() */ - __bfin_cycles_off += (cycles << __bfin_cycles_mod) - (cycles << index); - __bfin_cycles_mod = index; - local_irq_restore(flags); - /* TODO: just test case for cycles clock source, remove later */ - pr_debug("cpufreq: done\n"); - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); - - return 0; -} - -static int bfin_verify_speed(struct cpufreq_policy *policy) -{ - return cpufreq_frequency_table_verify(policy, bfin_freq_table); -} - -static int __init __bfin_cpu_init(struct cpufreq_policy *policy) -{ - - unsigned long cclk, sclk, csel, min_cclk; - int index; - - if (policy->cpu != 0) - return -EINVAL; - - cclk = get_cclk(); - sclk = get_sclk(); - -#if ANOMALY_05000273 || (!defined(CONFIG_BF54x) && defined(CONFIG_BFIN_DCACHE)) - min_cclk = sclk * 2; -#else - min_cclk = sclk; -#endif - csel = ((bfin_read_PLL_DIV() & CSEL) >> 4); - - for (index = 0; (cclk >> index) >= min_cclk && csel <= 3; index++, csel++) { - bfin_freq_table[index].frequency = cclk >> index; - dpm_state_table[index].csel = csel << 4; /* Shift now into PLL_DIV bitpos */ - dpm_state_table[index].tscale = (TIME_SCALE / (1 << csel)) - 1; - - pr_debug("cpufreq: freq:%d csel:%d tscale:%d\n", - bfin_freq_table[index].frequency, - dpm_state_table[index].csel, - dpm_state_table[index].tscale); - } - - policy->governor = CPUFREQ_DEFAULT_GOVERNOR; - - policy->cpuinfo.transition_latency = (bfin_read_PLL_LOCKCNT() / (sclk / 1000000)) * 1000; - /*Now ,only support one cpu */ - policy->cur = cclk; - cpufreq_frequency_table_get_attr(bfin_freq_table, policy->cpu); - return cpufreq_frequency_table_cpuinfo(policy, bfin_freq_table); -} - -static struct freq_attr *bfin_freq_attr[] = { - &cpufreq_freq_attr_scaling_available_freqs, - NULL, -}; - -static struct cpufreq_driver bfin_driver = { - .verify = bfin_verify_speed, - .target = bfin_target, - .get = bfin_getfreq, - .init = __bfin_cpu_init, - .name = "bfin cpufreq", - .owner = THIS_MODULE, - .attr = bfin_freq_attr, -}; - -static int __init bfin_cpu_init(void) -{ - return cpufreq_register_driver(&bfin_driver); -} - -static void __exit bfin_cpu_exit(void) -{ - cpufreq_unregister_driver(&bfin_driver); -} - -MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); -MODULE_DESCRIPTION("cpufreq driver for Blackfin"); -MODULE_LICENSE("GPL"); - -module_init(bfin_cpu_init); -module_exit(bfin_cpu_exit); diff --git a/arch/blackfin/mach-common/dpmc.c b/arch/blackfin/mach-common/dpmc.c index 02c7efd1bcf..724a8c5f557 100644 --- a/arch/blackfin/mach-common/dpmc.c +++ b/arch/blackfin/mach-common/dpmc.c @@ -19,9 +19,6 @@ #define DRIVER_NAME "bfin dpmc" -#define dprintk(msg...) \ - cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, DRIVER_NAME, msg) - struct bfin_dpmc_platform_data *pdata; /** @@ -61,17 +58,64 @@ err_out: } #ifdef CONFIG_CPU_FREQ +# ifdef CONFIG_SMP +static void bfin_idle_this_cpu(void *info) +{ + unsigned long flags = 0; + unsigned long iwr0, iwr1, iwr2; + unsigned int cpu = smp_processor_id(); + + local_irq_save_hw(flags); + bfin_iwr_set_sup0(&iwr0, &iwr1, &iwr2); + + platform_clear_ipi(cpu, IRQ_SUPPLE_0); + SSYNC(); + asm("IDLE;"); + bfin_iwr_restore(iwr0, iwr1, iwr2); + + local_irq_restore_hw(flags); +} + +static void bfin_idle_cpu(void) +{ + smp_call_function(bfin_idle_this_cpu, NULL, 0); +} + +static void bfin_wakeup_cpu(void) +{ + unsigned int cpu; + unsigned int this_cpu = smp_processor_id(); + cpumask_t mask; + + cpumask_copy(&mask, cpu_online_mask); + cpumask_clear_cpu(this_cpu, &mask); + for_each_cpu(cpu, &mask) + platform_send_ipi_cpu(cpu, IRQ_SUPPLE_0); +} + +# else +static void bfin_idle_cpu(void) {} +static void bfin_wakeup_cpu(void) {} +# endif + static int vreg_cpufreq_notifier(struct notifier_block *nb, unsigned long val, void *data) { struct cpufreq_freqs *freq = data; + if (freq->cpu != CPUFREQ_CPU) + return 0; + if (val == CPUFREQ_PRECHANGE && freq->old < freq->new) { + bfin_idle_cpu(); bfin_set_vlev(bfin_get_vlev(freq->new)); udelay(pdata->vr_settling_time); /* Wait until Volatge settled */ - - } else if (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) + bfin_wakeup_cpu(); + } else if (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) { + bfin_idle_cpu(); bfin_set_vlev(bfin_get_vlev(freq->new)); + bfin_wakeup_cpu(); + } return 0; } @@ -85,7 +129,7 @@ static struct notifier_block vreg_cpufreq_notifier_block = { * bfin_dpmc_probe - * */ -static int __devinit bfin_dpmc_probe(struct platform_device *pdev) +static int bfin_dpmc_probe(struct platform_device *pdev) { if (pdev->dev.platform_data) pdata = pdev->dev.platform_data; @@ -99,7 +143,7 @@ static int __devinit bfin_dpmc_probe(struct platform_device *pdev) /** * bfin_dpmc_remove - */ -static int __devexit bfin_dpmc_remove(struct platform_device *pdev) +static int bfin_dpmc_remove(struct platform_device *pdev) { pdata = NULL; return cpufreq_unregister_notifier(&vreg_cpufreq_notifier_block, @@ -108,29 +152,12 @@ static int __devexit bfin_dpmc_remove(struct platform_device *pdev) struct platform_driver bfin_dpmc_device_driver = { .probe = bfin_dpmc_probe, - .remove = __devexit_p(bfin_dpmc_remove), + .remove = bfin_dpmc_remove, .driver = { .name = DRIVER_NAME, } }; - -/** - * bfin_dpmc_init - Init driver - */ -static int __init bfin_dpmc_init(void) -{ - return platform_driver_register(&bfin_dpmc_device_driver); -} -module_init(bfin_dpmc_init); - -/** - * bfin_dpmc_exit - break down driver - */ -static void __exit bfin_dpmc_exit(void) -{ - platform_driver_unregister(&bfin_dpmc_device_driver); -} -module_exit(bfin_dpmc_exit); +module_platform_driver(bfin_dpmc_device_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("cpu power management driver for Blackfin"); diff --git a/arch/blackfin/mach-common/dpmc_modes.S b/arch/blackfin/mach-common/dpmc_modes.S index b7981d31c39..de99f3aac2c 100644 --- a/arch/blackfin/mach-common/dpmc_modes.S +++ b/arch/blackfin/mach-common/dpmc_modes.S @@ -6,20 +6,16 @@ #include <linux/linkage.h> #include <asm/blackfin.h> -#include <asm/mach/irq.h> - +#include <mach/irq.h> +#include <asm/dpmc.h> .section .l1.text - ENTRY(_sleep_mode) - [--SP] = ( R7:0, P5:0 ); - [--SP] = RETS; + [--SP] = (R7:4, P5:3); + [--SP] = RETS; call _set_sic_iwr; - R0 = 0xFFFF (Z); - call _set_rtc_istat; - P0.H = hi(PLL_CTL); P0.L = lo(PLL_CTL); R1 = W[P0](z); @@ -46,95 +42,53 @@ ENTRY(_sleep_mode) BITCLR (R7, 5); w[p0] = R7.L; IDLE; + + bfin_init_pm_bench_cycles; + call _test_pll_locked; RETS = [SP++]; - ( R7:0, P5:0 ) = [SP++]; + (R7:4, P5:3) = [SP++]; RTS; +ENDPROC(_sleep_mode) -ENTRY(_hibernate_mode) - [--SP] = ( R7:0, P5:0 ); - [--SP] = RETS; - - call _set_sic_iwr; - - R0 = 0xFFFF (Z); - call _set_rtc_istat; - - P0.H = hi(VR_CTL); - P0.L = lo(VR_CTL); - R1 = W[P0](z); - BITSET (R1, 8); - BITCLR (R1, 0); - BITCLR (R1, 1); - W[P0] = R1.L; - SSYNC; - - CLI R2; - IDLE; - - /* Actually, adding anything may not be necessary...SDRAM contents - * are lost - */ - -ENTRY(_deep_sleep) - [--SP] = ( R7:0, P5:0 ); - [--SP] = RETS; +/* + * This func never returns as it puts the part into hibernate, and + * is only called from do_hibernate, so we don't bother saving or + * restoring any of the normal C runtime state. When we wake up, + * the entry point will be in do_hibernate and not here. + * + * We accept just one argument -- the value to write to VR_CTL. + */ - CLI R4; +ENTRY(_hibernate_mode) + /* Save/setup the regs we need early for minor pipeline optimization */ + R4 = R0; - R0 = IWR_ENABLE(0); + P3.H = hi(VR_CTL); + P3.L = lo(VR_CTL); + /* Disable all wakeup sources */ + R0 = IWR_DISABLE_ALL; R1 = IWR_DISABLE_ALL; R2 = IWR_DISABLE_ALL; - call _set_sic_iwr; - call _set_dram_srfs; - - /* Clear all the interrupts,bits sticky */ - R0 = 0xFFFF (Z); - call _set_rtc_istat - - P0.H = hi(PLL_CTL); - P0.L = lo(PLL_CTL); - R0 = W[P0](z); - BITSET (R0, 5); - W[P0] = R0.L; - - call _test_pll_locked; - SSYNC; - IDLE; - call _unset_dram_srfs; + /* Finally, we climb into our cave to hibernate */ + W[P3] = R4.L; - call _test_pll_locked; + bfin_init_pm_bench_cycles; - R0 = IWR_ENABLE(0); - R1 = IWR_DISABLE_ALL; - R2 = IWR_DISABLE_ALL; - - call _set_sic_iwr; - - P0.H = hi(PLL_CTL); - P0.L = lo(PLL_CTL); - R0 = w[p0](z); - BITCLR (R0, 3); - BITCLR (R0, 5); - BITCLR (R0, 8); - w[p0] = R0; + CLI R2; IDLE; - call _test_pll_locked; - - STI R4; - - RETS = [SP++]; - ( R7:0, P5:0 ) = [SP++]; - RTS; +.Lforever: + jump .Lforever; +ENDPROC(_hibernate_mode) ENTRY(_sleep_deeper) - [--SP] = ( R7:0, P5:0 ); - [--SP] = RETS; + [--SP] = (R7:4, P5:3); + [--SP] = RETS; CLI R4; @@ -149,9 +103,6 @@ ENTRY(_sleep_deeper) call _set_sic_iwr; call _set_dram_srfs; /* Set SDRAM Self Refresh */ - /* Clear all the interrupts,bits sticky */ - R0 = 0xFFFF (Z); - call _set_rtc_istat; P0.H = hi(PLL_DIV); P0.L = lo(PLL_DIV); R6 = W[P0](z); @@ -213,6 +164,8 @@ ENTRY(_sleep_deeper) SSYNC; IDLE; + bfin_init_pm_bench_cycles; + call _test_pll_locked; P0.H = hi(PLL_DIV); @@ -230,85 +183,95 @@ ENTRY(_sleep_deeper) STI R4; RETS = [SP++]; - ( R7:0, P5:0 ) = [SP++]; + (R7:4, P5:3) = [SP++]; RTS; +ENDPROC(_sleep_deeper) ENTRY(_set_dram_srfs) /* set the dram to self refresh mode */ -#if defined(CONFIG_BF54x) + SSYNC; +#if defined(EBIU_RSTCTL) /* DDR */ P0.H = hi(EBIU_RSTCTL); P0.L = lo(EBIU_RSTCTL); R2 = [P0]; - R3.H = hi(SRREQ); - R3.L = lo(SRREQ); -#else - P0.H = hi(EBIU_SDGCTL); + BITSET(R2, 3); /* SRREQ enter self-refresh mode */ + [P0] = R2; + SSYNC; +1: + R2 = [P0]; + CC = BITTST(R2, 4); + if !CC JUMP 1b; +#else /* SDRAM */ P0.L = lo(EBIU_SDGCTL); + P0.H = hi(EBIU_SDGCTL); + P1.L = lo(EBIU_SDSTAT); + P1.H = hi(EBIU_SDSTAT); + R2 = [P0]; - R3.H = hi(SRFS); - R3.L = lo(SRFS); -#endif - R2 = R2|R3; + BITSET(R2, 24); /* SRFS enter self-refresh mode */ [P0] = R2; - ssync; -#if defined(CONFIG_BF54x) -.LSRR_MODE: + SSYNC; + +1: + R2 = w[P1]; + SSYNC; + cc = BITTST(R2, 1); /* SDSRA poll self-refresh status */ + if !cc jump 1b; + R2 = [P0]; - CC = BITTST(R2, 4); - if !CC JUMP .LSRR_MODE; + BITCLR(R2, 0); /* SCTLE disable CLKOUT */ + [P0] = R2; #endif RTS; +ENDPROC(_set_dram_srfs) ENTRY(_unset_dram_srfs) /* set the dram out of self refresh mode */ -#if defined(CONFIG_BF54x) + +#if defined(EBIU_RSTCTL) /* DDR */ P0.H = hi(EBIU_RSTCTL); P0.L = lo(EBIU_RSTCTL); R2 = [P0]; - R3.H = hi(SRREQ); - R3.L = lo(SRREQ); -#else - P0.H = hi(EBIU_SDGCTL); + BITCLR(R2, 3); /* clear SRREQ bit */ + [P0] = R2; +#elif defined(EBIU_SDGCTL) /* SDRAM */ + /* release CLKOUT from self-refresh */ P0.L = lo(EBIU_SDGCTL); + P0.H = hi(EBIU_SDGCTL); + R2 = [P0]; - R3.H = hi(SRFS); - R3.L = lo(SRFS); + BITSET(R2, 0); /* SCTLE enable CLKOUT */ + [P0] = R2 + SSYNC; + + /* release SDRAM from self-refresh */ + R2 = [P0]; + BITCLR(R2, 24); /* clear SRFS bit */ + [P0] = R2 #endif - R3 = ~R3; - R2 = R2&R3; - [P0] = R2; - ssync; + + SSYNC; RTS; +ENDPROC(_unset_dram_srfs) ENTRY(_set_sic_iwr) -#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) - P0.H = hi(SIC_IWR0); - P0.L = lo(SIC_IWR0); - P1.H = hi(SIC_IWR1); - P1.L = lo(SIC_IWR1); - [P1] = R1; -#if defined(CONFIG_BF54x) - P1.H = hi(SIC_IWR2); - P1.L = lo(SIC_IWR2); - [P1] = R2; -#endif +#ifdef SIC_IWR0 + P0.H = hi(SYSMMR_BASE); + P0.L = lo(SYSMMR_BASE); + [P0 + (SIC_IWR0 - SYSMMR_BASE)] = R0; + [P0 + (SIC_IWR1 - SYSMMR_BASE)] = R1; +# ifdef SIC_IWR2 + [P0 + (SIC_IWR2 - SYSMMR_BASE)] = R2; +# endif #else P0.H = hi(SIC_IWR); P0.L = lo(SIC_IWR); -#endif [P0] = R0; +#endif SSYNC; RTS; - -ENTRY(_set_rtc_istat) -#ifndef CONFIG_BF561 - P0.H = hi(RTC_ISTAT); - P0.L = lo(RTC_ISTAT); - w[P0] = R0.L; - SSYNC; -#endif - RTS; +ENDPROC(_set_sic_iwr) ENTRY(_test_pll_locked) P0.H = hi(PLL_STAT); @@ -318,3 +281,40 @@ ENTRY(_test_pll_locked) CC = BITTST(R0,5); IF !CC JUMP 1b; RTS; +ENDPROC(_test_pll_locked) + +.section .text +ENTRY(_do_hibernate) + bfin_cpu_reg_save; + bfin_sys_mmr_save; + bfin_core_mmr_save; + + /* Setup args to hibernate mode early for pipeline optimization */ + R0 = M3; + P1.H = _hibernate_mode; + P1.L = _hibernate_mode; + + /* Save Magic, return address and Stack Pointer */ + P0 = 0; + R1.H = 0xDEAD; /* Hibernate Magic */ + R1.L = 0xBEEF; + R2.H = .Lpm_resume_here; + R2.L = .Lpm_resume_here; + [P0++] = R1; /* Store Hibernate Magic */ + [P0++] = R2; /* Save Return Address */ + [P0++] = SP; /* Save Stack Pointer */ + + /* Must use an indirect call as we need to jump to L1 */ + call (P1); /* Goodbye */ + +.Lpm_resume_here: + + bfin_core_mmr_restore; + bfin_sys_mmr_restore; + bfin_cpu_reg_restore; + + [--sp] = RETI; /* Clear Global Interrupt Disable */ + SP += 4; + + RTS; +ENDPROC(_do_hibernate) diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S index 038f70e0be6..86b5a095c5a 100644 --- a/arch/blackfin/mach-common/entry.S +++ b/arch/blackfin/mach-common/entry.S @@ -1,32 +1,11 @@ /* - * File: arch/blackfin/mach-common/entry.S - * Based on: - * Author: Linus Torvalds + * Contains the system-call and fault low-level handling routines. + * This also contains the timer-interrupt handler, as well as all + * interrupts and faults that can result in a task-switch. * - * Created: ? - * Description: contains the system-call and fault low-level handling routines. - * This also contains the timer-interrupt handler, as well as all - * interrupts and faults that can result in a task-switch. + * Copyright 2005-2009 Analog Devices Inc. * - * 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 + * Licensed under the GPL-2 or later. */ /* NOTE: This code handles signal-recognition, which happens every time @@ -42,16 +21,10 @@ #include <asm/thread_info.h> /* TIF_NEED_RESCHED */ #include <asm/asm-offsets.h> #include <asm/trace.h> +#include <asm/traps.h> -#include <asm/mach-common/context.S> +#include <asm/context.S> -#if defined(CONFIG_BFIN_SCRATCH_REG_RETN) -# define EX_SCRATCH_REG RETN -#elif defined(CONFIG_BFIN_SCRATCH_REG_RETE) -# define EX_SCRATCH_REG RETE -#else -# define EX_SCRATCH_REG CYCLES -#endif #ifdef CONFIG_EXCPT_IRQ_SYSC_L1 .section .l1.text @@ -75,22 +48,24 @@ ENTRY(_ex_workaround_261) * handle it. */ P4 = R7; /* Store EXCAUSE */ - p5.l = _last_cplb_fault_retx; - p5.h = _last_cplb_fault_retx; - r7 = [p5]; + + GET_PDA(p5, r7); + r7 = [p5 + PDA_LFRETX]; r6 = retx; - [p5] = r6; + [p5 + PDA_LFRETX] = r6; cc = r6 == r7; if !cc jump _bfin_return_from_exception; /* fall through */ R7 = P4; - R6 = 0x26; /* Data CPLB Miss */ + R6 = VEC_CPLB_M; /* Data CPLB Miss */ cc = R6 == R7; if cc jump _ex_dcplb_miss (BP); - R6 = 0x23; /* Data CPLB Miss */ +#ifdef CONFIG_MPU + R6 = VEC_CPLB_VL; /* Data CPLB Violation */ cc = R6 == R7; if cc jump _ex_dcplb_viol (BP); - /* Handle 0x23 Data CPLB Protection Violation +#endif + /* Handle Data CPLB Protection Violation * and Data CPLB Multiple Hits - Linux Trap Zero */ jump _ex_trap_c; @@ -111,45 +86,45 @@ ENTRY(_ex_dcplb_viol) ENTRY(_ex_dcplb_miss) ENTRY(_ex_icplb_miss) (R7:6,P5:4) = [sp++]; - ASTAT = [sp++]; - SAVE_ALL_SYS -#ifdef CONFIG_MPU + /* We leave the previously pushed ASTAT on the stack. */ + SAVE_CONTEXT_CPLB + /* We must load R1 here, _before_ DEBUG_HWTRACE_SAVE, since that * will change the stack pointer. */ R0 = SEQSTAT; R1 = SP; -#endif + DEBUG_HWTRACE_SAVE(p5, r7) -#ifdef CONFIG_MPU + sp += -12; call _cplb_hdr; sp += 12; CC = R0 == 0; IF !CC JUMP _handle_bad_cplb; -#else - call __cplb_hdr; + +#ifdef CONFIG_DEBUG_DOUBLEFAULT + /* While we were processing this, did we double fault? */ + r7 = SEQSTAT; /* reason code is in bit 5:0 */ + r6.l = lo(SEQSTAT_EXCAUSE); + r6.h = hi(SEQSTAT_EXCAUSE); + r7 = r7 & r6; + r6 = 0x25; + CC = R7 == R6; + if CC JUMP _double_fault; #endif + DEBUG_HWTRACE_RESTORE(p5, r7) - RESTORE_ALL_SYS + RESTORE_CONTEXT_CPLB + ASTAT = [SP++]; SP = EX_SCRATCH_REG; rtx; ENDPROC(_ex_icplb_miss) ENTRY(_ex_syscall) - (R7:6,P5:4) = [sp++]; - ASTAT = [sp++]; raise 15; /* invoked by TRAP #0, for sys call */ - sp = EX_SCRATCH_REG; - rtx + jump.s _bfin_return_from_exception; ENDPROC(_ex_syscall) -ENTRY(_ex_soft_bp) - r7 = retx; - r7 += -2; - retx = r7; - jump.s _ex_trap_c; -ENDPROC(_ex_soft_bp) - ENTRY(_ex_single_step) /* If we just returned from an interrupt, the single step event is for the RTI instruction. */ @@ -158,6 +133,39 @@ ENTRY(_ex_single_step) cc = r7 == r6; if cc jump _bfin_return_from_exception; +#ifdef CONFIG_KGDB + /* Don't do single step in hardware exception handler */ + p5.l = lo(IPEND); + p5.h = hi(IPEND); + r6 = [p5]; + cc = bittst(r6, 4); + if cc jump _bfin_return_from_exception; + cc = bittst(r6, 5); + if cc jump _bfin_return_from_exception; + + /* skip single step if current interrupt priority is higher than + * that of the first instruction, from which gdb starts single step */ + r6 >>= 6; + r7 = 10; +.Lfind_priority_start: + cc = bittst(r6, 0); + if cc jump .Lfind_priority_done; + r6 >>= 1; + r7 += -1; + cc = r7 == 0; + if cc jump .Lfind_priority_done; + jump.s .Lfind_priority_start; +.Lfind_priority_done: + p4.l = _kgdb_single_step; + p4.h = _kgdb_single_step; + r6 = [p4]; + cc = r6 == 0; + if cc jump .Ldo_single_step; + r6 += -1; + cc = r6 < r7; + if cc jump 1f; +.Ldo_single_step: +#else /* If we were in user mode, do the single step normally. */ p5.l = lo(IPEND); p5.h = hi(IPEND); @@ -166,11 +174,23 @@ ENTRY(_ex_single_step) r7 = r7 & r6; cc = r7 == 0; if !cc jump 1f; - +#endif +#ifdef CONFIG_EXACT_HWERR + /* Read the ILAT, and to check to see if the process we are + * single stepping caused a previous hardware error + * If so, do not single step, (which lowers to IRQ5, and makes + * us miss the error). + */ + p5.l = lo(ILAT); + p5.h = hi(ILAT); + r7 = [p5]; + cc = bittst(r7, EVT_IVHW_P); + if cc jump 1f; +#endif /* Single stepping only a single instruction, so clear the trace * bit here. */ r7 = syscfg; - bitclr (r7, 0); + bitclr (r7, SYSCFG_SSSTEP_P); syscfg = R7; jump _ex_trap_c; @@ -203,7 +223,7 @@ ENTRY(_ex_single_step) if !cc jump _bfin_return_from_exception; r7 = syscfg; - bitclr (r7, 0); + bitclr (r7, SYSCFG_SSSTEP_P); /* Turn off single step */ syscfg = R7; /* Fall through to _bfin_return_from_exception. */ @@ -216,6 +236,20 @@ ENTRY(_bfin_return_from_exception) R7=LC1; LC1=R7; #endif + +#ifdef CONFIG_DEBUG_DOUBLEFAULT + /* While we were processing the current exception, + * did we cause another, and double fault? + */ + r7 = SEQSTAT; /* reason code is in bit 5:0 */ + r6.l = lo(SEQSTAT_EXCAUSE); + r6.h = hi(SEQSTAT_EXCAUSE); + r7 = r7 & r6; + r6 = VEC_UNCOV; + CC = R7 == R6; + if CC JUMP _double_fault; +#endif + (R7:6,P5:4) = [sp++]; ASTAT = [sp++]; sp = EX_SCRATCH_REG; @@ -227,125 +261,145 @@ ENTRY(_handle_bad_cplb) /* To get here, we just tried and failed to change a CPLB * so, handle things in trap_c (C code), by lowering to * IRQ5, just like we normally do. Since this is not a - * "normal" return path, we have a do alot of stuff to + * "normal" return path, we have a do a lot of stuff to * the stack to get ready so, we can fall through - we * need to make a CPLB exception look like a normal exception */ - - RESTORE_ALL_SYS - [--sp] = ASTAT; + RESTORE_CONTEXT_CPLB + /* ASTAT is still on the stack, where it is needed. */ [--sp] = (R7:6,P5:4); ENTRY(_ex_replaceable) nop; ENTRY(_ex_trap_c) + /* The only thing that has been saved in this context is + * (R7:6,P5:4), ASTAT & SP - don't use anything else + */ + + GET_PDA(p5, r6); + /* Make sure we are not in a double fault */ p4.l = lo(IPEND); p4.h = hi(IPEND); r7 = [p4]; CC = BITTST (r7, 5); if CC jump _double_fault; + [p5 + PDA_EXIPEND] = r7; /* Call C code (trap_c) to handle the exception, which most * likely involves sending a signal to the current process. * To avoid double faults, lower our priority to IRQ5 first. */ - P5.h = _exception_to_level5; - P5.l = _exception_to_level5; + r7.h = _exception_to_level5; + r7.l = _exception_to_level5; p4.l = lo(EVT5); p4.h = hi(EVT5); - [p4] = p5; + [p4] = r7; csync; + /* + * Save these registers, as they are only valid in exception context + * (where we are now - as soon as we defer to IRQ5, they can change) + * DCPLB_STATUS and ICPLB_STATUS are also only valid in EVT3, + * but they are not very interesting, so don't save them + */ + p4.l = lo(DCPLB_FAULT_ADDR); p4.h = hi(DCPLB_FAULT_ADDR); r7 = [p4]; - p5.h = _saved_dcplb_fault_addr; - p5.l = _saved_dcplb_fault_addr; - [p5] = r7; - - r7 = [p4 + (ICPLB_FAULT_ADDR - DCPLB_FAULT_ADDR)]; - p5.h = _saved_icplb_fault_addr; - p5.l = _saved_icplb_fault_addr; - [p5] = r7; + [p5 + PDA_DCPLB] = r7; - p4.l = _excpt_saved_stuff; - p4.h = _excpt_saved_stuff; + p4.l = lo(ICPLB_FAULT_ADDR); + p4.h = hi(ICPLB_FAULT_ADDR); + r6 = [p4]; + [p5 + PDA_ICPLB] = r6; r6 = retx; - [p4] = r6; + [p5 + PDA_RETX] = r6; + + r6 = SEQSTAT; + [p5 + PDA_SEQSTAT] = r6; + /* Save the state of single stepping */ r6 = SYSCFG; - [p4 + 4] = r6; - BITCLR(r6, 0); + [p5 + PDA_SYSCFG] = r6; + /* Clear it while we handle the exception in IRQ5 mode */ + BITCLR(r6, SYSCFG_SSSTEP_P); SYSCFG = r6; - /* Disable all interrupts, but make sure level 5 is enabled so - * we can switch to that level. Save the old mask. */ + /* Save the current IMASK, since we change in order to jump to level 5 */ cli r6; - [p4 + 8] = r6; + [p5 + PDA_EXIMASK] = r6; p4.l = lo(SAFE_USER_INSTRUCTION); p4.h = hi(SAFE_USER_INSTRUCTION); retx = p4; + /* Disable all interrupts, but make sure level 5 is enabled so + * we can switch to that level. + */ r6 = 0x3f; sti r6; - (R7:6,P5:4) = [sp++]; - ASTAT = [sp++]; - SP = EX_SCRATCH_REG; + /* In case interrupts are disabled IPEND[4] (global interrupt disable bit) + * clear it (re-enabling interrupts again) by the special sequence of pushing + * RETI onto the stack. This way we can lower ourselves to IVG5 even if the + * exception was taken after the interrupt handler was called but before it + * got a chance to enable global interrupts itself. + */ + [--sp] = reti; + sp += 4; + raise 5; - rtx; + jump.s _bfin_return_from_exception; ENDPROC(_ex_trap_c) /* We just realized we got an exception, while we were processing a different - * exception. This is a unrecoverable event, so crash + * exception. This is a unrecoverable event, so crash. + * Note: this cannot be ENTRY() as we jump here with "if cc jump" ... */ ENTRY(_double_fault) - /* Turn caches & protection off, to ensure we don't get any more - * double exceptions - */ - - P4.L = LO(IMEM_CONTROL); - P4.H = HI(IMEM_CONTROL); - - R5 = [P4]; /* Control Register*/ - BITCLR(R5,ENICPLB_P); - SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */ - .align 8; - [P4] = R5; - SSYNC; - - P4.L = LO(DMEM_CONTROL); - P4.H = HI(DMEM_CONTROL); - R5 = [P4]; - BITCLR(R5,ENDCPLB_P); - SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */ - .align 8; - [P4] = R5; - SSYNC; - - /* Fix up the stack */ - (R7:6,P5:4) = [sp++]; - ASTAT = [sp++]; - SP = EX_SCRATCH_REG; - - /* We should be out of the exception stack, and back down into - * kernel or user space stack - */ - SAVE_ALL_SYS + /* Turn caches & protection off, to ensure we don't get any more + * double exceptions + */ + + P4.L = LO(IMEM_CONTROL); + P4.H = HI(IMEM_CONTROL); + + R5 = [P4]; /* Control Register*/ + BITCLR(R5,ENICPLB_P); + CSYNC; /* Disabling of CPLBs should be proceeded by a CSYNC */ + [P4] = R5; + SSYNC; + + P4.L = LO(DMEM_CONTROL); + P4.H = HI(DMEM_CONTROL); + R5 = [P4]; + BITCLR(R5,ENDCPLB_P); + CSYNC; /* Disabling of CPLBs should be proceeded by a CSYNC */ + [P4] = R5; + SSYNC; + + /* Fix up the stack */ + (R7:6,P5:4) = [sp++]; + ASTAT = [sp++]; + SP = EX_SCRATCH_REG; + + /* We should be out of the exception stack, and back down into + * kernel or user space stack + */ + SAVE_ALL_SYS /* The dumping functions expect the return address in the RETI * slot. */ r6 = retx; [sp + PT_PC] = r6; - r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ - SP += -12; - call _double_fault_c; - SP += 12; + r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ + SP += -12; + pseudo_long_call _double_fault_c, p5; + SP += 12; .L_double_fault_panic: JUMP .L_double_fault_panic @@ -354,38 +408,56 @@ ENDPROC(_double_fault) ENTRY(_exception_to_level5) SAVE_ALL_SYS - p4.l = _excpt_saved_stuff; - p4.h = _excpt_saved_stuff; - r6 = [p4]; + GET_PDA(p5, r7); /* Fetch current PDA */ + r6 = [p5 + PDA_RETX]; [sp + PT_PC] = r6; - r6 = [p4 + 4]; + r6 = [p5 + PDA_SYSCFG]; [sp + PT_SYSCFG] = r6; - /* Restore interrupt mask. We haven't pushed RETI, so this - * doesn't enable interrupts until we return from this handler. */ - r6 = [p4 + 8]; - sti r6; + r6 = [p5 + PDA_SEQSTAT]; /* Read back seqstat */ + [sp + PT_SEQSTAT] = r6; /* Restore the hardware error vector. */ - P5.h = _evt_ivhw; - P5.l = _evt_ivhw; + r7.h = _evt_ivhw; + r7.l = _evt_ivhw; p4.l = lo(EVT5); p4.h = hi(EVT5); - [p4] = p5; + [p4] = r7; csync; - p2.l = lo(IPEND); - p2.h = hi(IPEND); - csync; - r0 = [p2]; /* Read current IPEND */ - [sp + PT_IPEND] = r0; /* Store IPEND */ +#ifdef CONFIG_DEBUG_DOUBLEFAULT + /* Now that we have the hardware error vector programmed properly + * we can re-enable interrupts (IPEND[4]), so if the _trap_c causes + * another hardware error, we can catch it (self-nesting). + */ + [--sp] = reti; + sp += 4; +#endif + + r7 = [p5 + PDA_EXIPEND] /* Read the IPEND from the Exception state */ + [sp + PT_IPEND] = r7; /* Store IPEND onto the stack */ r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ SP += -12; - call _trap_c; + pseudo_long_call _trap_c, p4; SP += 12; + /* If interrupts were off during the exception (IPEND[4] = 1), turn them off + * before we return. + */ + CC = BITTST(r7, EVT_IRPTEN_P) + if !CC jump 1f; + /* this will load a random value into the reti register - but that is OK, + * since we do restore it to the correct value in the 'RESTORE_ALL_SYS' macro + */ + sp += -4; + reti = [sp++]; +1: + /* restore the interrupt mask (IMASK) */ + r6 = [p5 + PDA_EXIMASK]; + sti r6; + call _ret_from_exception; RESTORE_ALL_SYS rti; @@ -397,12 +469,51 @@ ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/ * scratch register (for want of a better option). */ EX_SCRATCH_REG = sp; - sp.l = _exception_stack_top; - sp.h = _exception_stack_top; + GET_PDA_SAFE(sp); + sp = [sp + PDA_EXSTACK]; /* Try to deal with syscalls quickly. */ [--sp] = ASTAT; [--sp] = (R7:6,P5:4); + + ANOMALY_283_315_WORKAROUND(p5, r7) + +#ifdef CONFIG_EXACT_HWERR + /* Make sure all pending read/writes complete. This will ensure any + * accesses which could cause hardware errors completes, and signal + * the the hardware before we do something silly, like crash the + * kernel. We don't need to work around anomaly 05000312, since + * we are already atomic + */ + ssync; +#endif + +#ifdef CONFIG_DEBUG_DOUBLEFAULT + /* + * Save these registers, as they are only valid in exception context + * (where we are now - as soon as we defer to IRQ5, they can change) + * DCPLB_STATUS and ICPLB_STATUS are also only valid in EVT3, + * but they are not very interesting, so don't save them + */ + + GET_PDA(p5, r7); + p4.l = lo(DCPLB_FAULT_ADDR); + p4.h = hi(DCPLB_FAULT_ADDR); + r7 = [p4]; + [p5 + PDA_DF_DCPLB] = r7; + + p4.l = lo(ICPLB_FAULT_ADDR); + p4.h = hi(ICPLB_FAULT_ADDR); + r7 = [p4]; + [p5 + PDA_DF_ICPLB] = r7; + + r7 = retx; + [p5 + PDA_DF_RETX] = r7; + r7 = SEQSTAT; /* reason code is in bit 5:0 */ + [p5 + PDA_DF_SEQSTAT] = r7; +#else + r7 = SEQSTAT; /* reason code is in bit 5:0 */ +#endif r6.l = lo(SEQSTAT_EXCAUSE); r6.h = hi(SEQSTAT_EXCAUSE); r7 = r7 & r6; @@ -419,61 +530,6 @@ ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/ jump .Lsyscall_really_exit; ENDPROC(_trap) -ENTRY(_kernel_execve) - link SIZEOF_PTREGS; - p0 = sp; - r3 = SIZEOF_PTREGS / 4; - r4 = 0(x); -0: - [p0++] = r4; - r3 += -1; - cc = r3 == 0; - if !cc jump 0b (bp); - - p0 = sp; - sp += -16; - [sp + 12] = p0; - call _do_execve; - SP += 16; - cc = r0 == 0; - if ! cc jump 1f; - /* Success. Copy our temporary pt_regs to the top of the kernel - * stack and do a normal exception return. - */ - r1 = sp; - r0 = (-KERNEL_STACK_SIZE) (x); - r1 = r1 & r0; - p2 = r1; - p3 = [p2]; - r0 = KERNEL_STACK_SIZE - 4 (z); - p1 = r0; - p1 = p1 + p2; - - p0 = fp; - r4 = [p0--]; - r3 = SIZEOF_PTREGS / 4; -0: - r4 = [p0--]; - [p1--] = r4; - r3 += -1; - cc = r3 == 0; - if ! cc jump 0b (bp); - - r0 = (KERNEL_STACK_SIZE - SIZEOF_PTREGS) (z); - p1 = r0; - p1 = p1 + p2; - sp = p1; - r0 = syscfg; - [SP + PT_SYSCFG] = r0; - [p3 + (TASK_THREAD + THREAD_KSP)] = sp; - - RESTORE_CONTEXT; - rti; -1: - unlink; - rts; -ENDPROC(_kernel_execve) - ENTRY(_system_call) /* Store IPEND */ p2.l = lo(IPEND); @@ -494,13 +550,19 @@ ENTRY(_system_call) p2 = [p2]; [p2+(TASK_THREAD+THREAD_KSP)] = sp; - - /* Check the System Call */ - r7 = __NR_syscall; - /* System call number is passed in P0 */ - r6 = p0; - cc = r6 < r7; - if ! cc jump .Lbadsys; +#ifdef CONFIG_IPIPE + r0 = sp; + SP += -12; + pseudo_long_call ___ipipe_syscall_root, p0; + SP += 12; + cc = r0 == 1; + if cc jump .Lsyscall_really_exit; + cc = r0 == -1; + if cc jump .Lresume_userspace; + r3 = [sp + PT_R3]; + r4 = [sp + PT_R4]; + p0 = [sp + PT_ORIG_P0]; +#endif /* CONFIG_IPIPE */ /* are we tracing syscalls?*/ r7 = sp; @@ -511,6 +573,14 @@ ENTRY(_system_call) r7 = [p2+TI_FLAGS]; CC = BITTST(r7,TIF_SYSCALL_TRACE); if CC JUMP _sys_trace; + CC = BITTST(r7,TIF_SINGLESTEP); + if CC JUMP _sys_trace; + + /* Make sure the system call # is valid */ + p4 = __NR_syscall; + /* System call number is passed in P0 */ + cc = p4 <= p0; + if cc jump .Lbadsys; /* Execute the appropriate system call */ @@ -548,32 +618,49 @@ ENTRY(_system_call) r7 = r7 & r4; .Lsyscall_resched: +#ifdef CONFIG_IPIPE + cc = BITTST(r7, TIF_IRQ_SYNC); + if !cc jump .Lsyscall_no_irqsync; + /* + * Clear IPEND[4] manually to undo what resume_userspace_1 just did; + * we need this so that high priority domain interrupts may still + * preempt the current domain while the pipeline log is being played + * back. + */ + [--sp] = reti; + SP += 4; /* don't merge with next insn to keep the pattern obvious */ + SP += -12; + pseudo_long_call ___ipipe_sync_root, p4; + SP += 12; + jump .Lresume_userspace_1; +.Lsyscall_no_irqsync: +#endif cc = BITTST(r7, TIF_NEED_RESCHED); if !cc jump .Lsyscall_sigpending; /* Reenable interrupts. */ [--sp] = reti; - r0 = [sp++]; + sp += 4; SP += -12; - call _schedule; + pseudo_long_call _schedule, p4; SP += 12; jump .Lresume_userspace_1; .Lsyscall_sigpending: - cc = BITTST(r7, TIF_RESTORE_SIGMASK); - if cc jump .Lsyscall_do_signals; cc = BITTST(r7, TIF_SIGPENDING); + if cc jump .Lsyscall_do_signals; + cc = BITTST(r7, TIF_NOTIFY_RESUME); if !cc jump .Lsyscall_really_exit; .Lsyscall_do_signals: /* Reenable interrupts. */ [--sp] = reti; - r0 = [sp++]; + sp += 4; r0 = sp; SP += -12; - call _do_signal; + pseudo_long_call _do_notify_resume, p5; SP += 12; .Lsyscall_really_exit: @@ -582,12 +669,21 @@ ENTRY(_system_call) rts; ENDPROC(_system_call) +/* Do not mark as ENTRY() to avoid error in assembler ... + * this symbol need not be global anyways, so ... + */ _sys_trace: - call _syscall_trace; - - /* Execute the appropriate system call */ + r0 = sp; + pseudo_long_call _syscall_trace_enter, p5; + /* Make sure the system call # is valid */ p4 = [SP + PT_P0]; + p3 = __NR_syscall; + cc = p3 <= p4; + r0 = -ENOSYS; + if cc jump .Lsys_trace_badsys; + + /* Execute the appropriate system call */ p5.l = _sys_call_table; p5.h = _sys_call_table; p5 = p5 + (p4 << 2); @@ -605,9 +701,11 @@ _sys_trace: SP += -12; call (p5); SP += 24; +.Lsys_trace_badsys: [sp + PT_R0] = r0; - call _syscall_trace; + r0 = sp; + pseudo_long_call _syscall_trace_leave, p5; jump .Lresume_userspace; ENDPROC(_sys_trace) @@ -655,13 +753,22 @@ _new_old_task: rets = [sp++]; /* - * When we come out of resume, r0 carries "old" task, becuase we are + * When we come out of resume, r0 carries "old" task, because we are * in "new" task. */ rts; ENDPROC(_resume) ENTRY(_ret_from_exception) +#ifdef CONFIG_IPIPE + p2.l = _ipipe_percpu_domain; + p2.h = _ipipe_percpu_domain; + r0.l = _ipipe_root; + r0.h = _ipipe_root; + r2 = [p2]; + cc = r0 == r2; + if !cc jump 4f; /* not on behalf of the root domain, get out */ +#endif /* CONFIG_IPIPE */ p2.l = lo(IPEND); p2.h = hi(IPEND); @@ -709,15 +816,149 @@ ENTRY(_ret_from_exception) p1.h = _schedule_and_signal; [p0] = p1; csync; - raise 15; /* raise evt14 to do signal or reschedule */ + raise 15; /* raise evt15 to do signal or reschedule */ 4: r0 = syscfg; - bitclr(r0, 0); + bitclr(r0, SYSCFG_SSSTEP_P); /* Turn off single step */ syscfg = r0; 5: rts; ENDPROC(_ret_from_exception) +#if defined(CONFIG_PREEMPT) + +ENTRY(_up_to_irq14) +#if ANOMALY_05000281 || ANOMALY_05000461 + r0.l = lo(SAFE_USER_INSTRUCTION); + r0.h = hi(SAFE_USER_INSTRUCTION); + reti = r0; +#endif + +#ifdef CONFIG_DEBUG_HWERR + /* enable irq14 & hwerr interrupt, until we transition to _evt_evt14 */ + r0 = (EVT_IVG14 | EVT_IVHW | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU); +#else + /* Only enable irq14 interrupt, until we transition to _evt_evt14 */ + r0 = (EVT_IVG14 | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU); +#endif + sti r0; + + p0.l = lo(EVT14); + p0.h = hi(EVT14); + p1.l = _evt_up_evt14; + p1.h = _evt_up_evt14; + [p0] = p1; + csync; + + raise 14; +1: + jump 1b; +ENDPROC(_up_to_irq14) + +ENTRY(_evt_up_evt14) +#ifdef CONFIG_DEBUG_HWERR + r0 = (EVT_IVHW | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU); + sti r0; +#else + cli r0; +#endif +#ifdef CONFIG_TRACE_IRQFLAGS + [--sp] = rets; + sp += -12; + call _trace_hardirqs_off; + sp += 12; + rets = [sp++]; +#endif + [--sp] = RETI; + SP += 4; + + /* restore normal evt14 */ + p0.l = lo(EVT14); + p0.h = hi(EVT14); + p1.l = _evt_evt14; + p1.h = _evt_evt14; + [p0] = p1; + csync; + + rts; +ENDPROC(_evt_up_evt14) + +#endif + +#ifdef CONFIG_IPIPE + +_resume_kernel_from_int: + r1 = LO(~0x8000) (Z); + r1 = r0 & r1; + r0 = 1; + r0 = r1 - r0; + r2 = r1 & r0; + cc = r2 == 0; + /* Sync the root stage only from the outer interrupt level. */ + if !cc jump .Lnosync; + r0.l = ___ipipe_sync_root; + r0.h = ___ipipe_sync_root; + [--sp] = reti; + [--sp] = rets; + [--sp] = ( r7:4, p5:3 ); + SP += -12; + call ___ipipe_call_irqtail + SP += 12; + ( r7:4, p5:3 ) = [sp++]; + rets = [sp++]; + reti = [sp++]; +.Lnosync: + rts +#elif defined(CONFIG_PREEMPT) + +_resume_kernel_from_int: + /* check preempt_count */ + r7 = sp; + r4.l = lo(ALIGN_PAGE_MASK); + r4.h = hi(ALIGN_PAGE_MASK); + r7 = r7 & r4; + p5 = r7; + r7 = [p5 + TI_PREEMPT]; + cc = r7 == 0x0; + if !cc jump .Lreturn_to_kernel; +.Lneed_schedule: + r7 = [p5 + TI_FLAGS]; + r4.l = lo(_TIF_WORK_MASK); + r4.h = hi(_TIF_WORK_MASK); + r7 = r7 & r4; + cc = BITTST(r7, TIF_NEED_RESCHED); + if !cc jump .Lreturn_to_kernel; + /* + * let schedule done at level 15, otherwise sheduled process will run + * at high level and block low level interrupt + */ + r6 = reti; /* save reti */ + r5.l = .Lkernel_schedule; + r5.h = .Lkernel_schedule; + reti = r5; + rti; +.Lkernel_schedule: + [--sp] = rets; + sp += -12; + pseudo_long_call _preempt_schedule_irq, p4; + sp += 12; + rets = [sp++]; + + [--sp] = rets; + sp += -12; + /* up to irq14 so that reti after restore_all can return to irq15(kernel) */ + pseudo_long_call _up_to_irq14, p4; + sp += 12; + rets = [sp++]; + + reti = r6; /* restore reti so that origin process can return to interrupted point */ + + jump .Lneed_schedule; +#else + +#define _resume_kernel_from_int .Lreturn_to_kernel +#endif + ENTRY(_return_from_int) /* If someone else already raised IRQ 15, do nothing. */ csync; @@ -725,7 +966,7 @@ ENTRY(_return_from_int) p2.h = hi(ILAT); r0 = [p2]; cc = bittst (r0, EVT_IVG15_P); - if cc jump 2f; + if cc jump .Lreturn_to_kernel; /* if not return to user mode, get out */ p2.l = lo(IPEND); @@ -739,7 +980,7 @@ ENTRY(_return_from_int) r1 = r0 - r1; r2 = r0 & r1; cc = r2 == 0; - if !cc jump 2f; + if !cc jump _resume_kernel_from_int; /* Lower the interrupt level to 15. */ p0.l = lo(EVT15); @@ -748,7 +989,7 @@ ENTRY(_return_from_int) p1.h = _schedule_and_signal_from_int; [p0] = p1; csync; -#if ANOMALY_05000281 +#if ANOMALY_05000281 || ANOMALY_05000461 r0.l = lo(SAFE_USER_INSTRUCTION); r0.h = hi(SAFE_USER_INSTRUCTION); reti = r0; @@ -757,32 +998,49 @@ ENTRY(_return_from_int) STI r0; raise 15; /* raise evt15 to do signal or reschedule */ rti; -2: +.Lreturn_to_kernel: rts; ENDPROC(_return_from_int) ENTRY(_lower_to_irq14) -#if ANOMALY_05000281 +#if ANOMALY_05000281 || ANOMALY_05000461 r0.l = lo(SAFE_USER_INSTRUCTION); r0.h = hi(SAFE_USER_INSTRUCTION); reti = r0; #endif - r0 = 0x401f; + +#ifdef CONFIG_DEBUG_HWERR + /* enable irq14 & hwerr interrupt, until we transition to _evt_evt14 */ + r0 = (EVT_IVG14 | EVT_IVHW | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU); +#else + /* Only enable irq14 interrupt, until we transition to _evt_evt14 */ + r0 = (EVT_IVG14 | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU); +#endif sti r0; raise 14; rti; -ENTRY(_evt14_softirq) +ENDPROC(_lower_to_irq14) + +ENTRY(_evt_evt14) #ifdef CONFIG_DEBUG_HWERR - r0 = 0x3f; + r0 = (EVT_IVHW | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU); sti r0; #else cli r0; #endif +#ifdef CONFIG_TRACE_IRQFLAGS + [--sp] = rets; + sp += -12; + call _trace_hardirqs_off; + sp += 12; + rets = [sp++]; +#endif [--sp] = RETI; SP += 4; rts; +ENDPROC(_evt_evt14) -_schedule_and_signal_from_int: +ENTRY(_schedule_and_signal_from_int) /* To end up here, vector 15 was changed - so we have to change it * back. */ @@ -800,18 +1058,40 @@ _schedule_and_signal_from_int: p1 = rets; [sp + PT_RESERVED] = p1; - p0.l = _irq_flags; - p0.h = _irq_flags; +#ifdef CONFIG_TRACE_IRQFLAGS + /* trace_hardirqs_on() checks if all irqs are disabled. But here IRQ 15 + * is turned on, so disable all irqs. */ + cli r0; + sp += -12; + call _trace_hardirqs_on; + sp += 12; +#endif +#ifdef CONFIG_SMP + GET_PDA(p0, r0); /* Fetch current PDA (can't migrate to other CPU here) */ + r0 = [p0 + PDA_IRQFLAGS]; +#else + p0.l = _bfin_irq_flags; + p0.h = _bfin_irq_flags; r0 = [p0]; +#endif sti r0; + /* finish the userspace "atomic" functions for it */ + r1.l = lo(FIXED_CODE_END); + r1.h = hi(FIXED_CODE_END); + r2 = [sp + PT_PC]; + cc = r1 <= r2; + if cc jump .Lresume_userspace (bp); + r0 = sp; sp += -12; - call _finish_atomic_sections; + + pseudo_long_call _finish_atomic_sections, p5; sp += 12; jump.s .Lresume_userspace; +ENDPROC(_schedule_and_signal_from_int) -_schedule_and_signal: +ENTRY(_schedule_and_signal) SAVE_CONTEXT_SYSCALL /* To end up here, vector 15 was changed - so we have to change it * back. @@ -829,7 +1109,7 @@ _schedule_and_signal: 1: RESTORE_CONTEXT rti; -ENDPROC(_lower_to_irq14) +ENDPROC(_schedule_and_signal) /* We handle this 100% in exception space - to reduce overhead * Only potiential problem is if the software buffer gets swapped out of the @@ -901,12 +1181,14 @@ ENTRY(_software_trace_buff) .endr #endif /* CONFIG_DEBUG_BFIN_HWTRACE_EXPAND */ -#if CONFIG_EARLY_PRINTK +#ifdef CONFIG_EARLY_PRINTK __INIT ENTRY(_early_trap) SAVE_ALL_SYS trace_buffer_stop(p0,r0); + ANOMALY_283_315_WORKAROUND(p4, r5) + /* Turn caches off, to ensure we don't get double exceptions */ P4.L = LO(IMEM_CONTROL); @@ -914,9 +1196,7 @@ ENTRY(_early_trap) R5 = [P4]; /* Control Register*/ BITCLR(R5,ENICPLB_P); - CLI R1; - SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */ - .align 8; + CSYNC; /* Disabling of CPLBs should be proceeded by a CSYNC */ [P4] = R5; SSYNC; @@ -924,11 +1204,9 @@ ENTRY(_early_trap) P4.H = HI(DMEM_CONTROL); R5 = [P4]; BITCLR(R5,ENDCPLB_P); - SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */ - .align 8; + CSYNC; /* Disabling of CPLBs should be proceeded by a CSYNC */ [P4] = R5; SSYNC; - STI R1; r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ r1 = RETX; @@ -957,8 +1235,13 @@ ENTRY(_ex_table) * EXCPT instruction can provide 4 bits of EXCAUSE, allowing 16 to be user defined */ .long _ex_syscall /* 0x00 - User Defined - Linux Syscall */ - .long _ex_soft_bp /* 0x01 - User Defined - Software breakpoint */ + .long _ex_trap_c /* 0x01 - User Defined - Software breakpoint */ +#ifdef CONFIG_KGDB + .long _ex_trap_c /* 0x02 - User Defined - KGDB initial connection + and break signal trap */ +#else .long _ex_replaceable /* 0x02 - User Defined */ +#endif .long _ex_trap_c /* 0x03 - User Defined - userspace stack overflow */ .long _ex_trap_c /* 0x04 - User Defined - dump trace buffer */ .long _ex_replaceable /* 0x05 - User Defined */ @@ -1030,7 +1313,7 @@ END(_ex_table) ENTRY(_sys_call_table) .long _sys_restart_syscall /* 0 */ .long _sys_exit - .long _sys_fork + .long _sys_ni_syscall /* fork */ .long _sys_read .long _sys_write .long _sys_open /* 5 */ @@ -1148,12 +1431,12 @@ ENTRY(_sys_call_table) .long _sys_ni_syscall /* old sys_ipc */ .long _sys_fsync .long _sys_ni_syscall /* old sys_sigreturn */ - .long _sys_clone /* 120 */ + .long _bfin_clone /* 120 */ .long _sys_setdomainname .long _sys_newuname .long _sys_ni_syscall /* old sys_modify_ldt */ .long _sys_adjtimex - .long _sys_ni_syscall /* 125 */ /* sys_mprotect */ + .long _sys_mprotect /* 125 */ .long _sys_ni_syscall /* old sys_sigprocmask */ .long _sys_ni_syscall /* old "creat_module" */ .long _sys_init_module @@ -1172,16 +1455,16 @@ ENTRY(_sys_call_table) .long _sys_getdents .long _sys_ni_syscall /* sys_select */ .long _sys_flock - .long _sys_ni_syscall /* sys_msync */ + .long _sys_msync .long _sys_readv /* 145 */ .long _sys_writev .long _sys_getsid .long _sys_fdatasync .long _sys_sysctl - .long _sys_ni_syscall /* 150 */ /* sys_mlock */ - .long _sys_ni_syscall /* sys_munlock */ - .long _sys_ni_syscall /* sys_mlockall */ - .long _sys_ni_syscall /* sys_munlockall */ + .long _sys_mlock /* 150 */ + .long _sys_munlock + .long _sys_mlockall + .long _sys_munlockall .long _sys_sched_setparam .long _sys_sched_getparam /* 155 */ .long _sys_sched_setscheduler @@ -1197,7 +1480,7 @@ ENTRY(_sys_call_table) .long _sys_ni_syscall /* for vm86 */ .long _sys_ni_syscall /* old "query_module" */ .long _sys_ni_syscall /* sys_poll */ - .long _sys_nfsservctl + .long _sys_ni_syscall /* old nfsservctl */ .long _sys_setresgid /* setresgid16 */ /* 170 */ .long _sys_getresgid /* getresgid16 */ .long _sys_prctl @@ -1220,7 +1503,7 @@ ENTRY(_sys_call_table) .long _sys_ni_syscall /* streams2 */ .long _sys_vfork /* 190 */ .long _sys_getrlimit - .long _sys_mmap2 + .long _sys_mmap_pgoff .long _sys_truncate64 .long _sys_ftruncate64 .long _sys_stat64 /* 195 */ @@ -1246,8 +1529,8 @@ ENTRY(_sys_call_table) .long _sys_setfsuid /* 215 */ .long _sys_setfsgid .long _sys_pivot_root - .long _sys_ni_syscall /* sys_mincore */ - .long _sys_ni_syscall /* sys_madvise */ + .long _sys_mincore + .long _sys_madvise .long _sys_getdents64 /* 220 */ .long _sys_fcntl64 .long _sys_ni_syscall /* reserved for TUX */ @@ -1303,7 +1586,7 @@ ENTRY(_sys_call_table) .long _sys_utimes .long _sys_fadvise64_64 .long _sys_ni_syscall /* vserver */ - .long _sys_ni_syscall /* 275, mbind */ + .long _sys_mbind /* 275 */ .long _sys_ni_syscall /* get_mempolicy */ .long _sys_ni_syscall /* set_mempolicy */ .long _sys_mq_open @@ -1388,28 +1671,31 @@ ENTRY(_sys_call_table) .long _sys_semtimedop .long _sys_timerfd_settime .long _sys_timerfd_gettime + .long _sys_signalfd4 /* 360 */ + .long _sys_eventfd2 + .long _sys_epoll_create1 + .long _sys_dup3 + .long _sys_pipe2 + .long _sys_inotify_init1 /* 365 */ + .long _sys_preadv + .long _sys_pwritev + .long _sys_rt_tgsigqueueinfo + .long _sys_perf_event_open + .long _sys_recvmmsg /* 370 */ + .long _sys_fanotify_init + .long _sys_fanotify_mark + .long _sys_prlimit64 + .long _sys_cacheflush + .long _sys_name_to_handle_at /* 375 */ + .long _sys_open_by_handle_at + .long _sys_clock_adjtime + .long _sys_syncfs + .long _sys_setns + .long _sys_sendmmsg /* 380 */ + .long _sys_process_vm_readv + .long _sys_process_vm_writev .rept NR_syscalls-(.-_sys_call_table)/4 .long _sys_ni_syscall .endr - - /* - * Used to save the real RETX, IMASK and SYSCFG when temporarily - * storing safe values across the transition from exception to IRQ5. - */ -_excpt_saved_stuff: - .long 0; - .long 0; - .long 0; - -_exception_stack: - .rept 1024 - .long 0; - .endr -_exception_stack_top: - -#if ANOMALY_05000261 -/* Used by the assembly entry point to work around an anomaly. */ -_last_cplb_fault_retx: - .long 0; -#endif +END(_sys_call_table) diff --git a/arch/blackfin/mach-common/head.S b/arch/blackfin/mach-common/head.S new file mode 100644 index 00000000000..31515f0146f --- /dev/null +++ b/arch/blackfin/mach-common/head.S @@ -0,0 +1,229 @@ +/* + * Common Blackfin startup code + * + * Copyright 2004-2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/linkage.h> +#include <linux/init.h> +#include <asm/blackfin.h> +#include <asm/thread_info.h> +#include <asm/trace.h> +#include <asm/asm-offsets.h> + +__INIT + +ENTRY(__init_clear_bss) + r2 = r2 - r1; + cc = r2 == 0; + if cc jump .L_bss_done; + r2 >>= 2; + p1 = r1; + p2 = r2; + lsetup (1f, 1f) lc0 = p2; +1: [p1++] = r0; +.L_bss_done: + rts; +ENDPROC(__init_clear_bss) + +ENTRY(__start) + /* R0: argument of command line string, passed from uboot, save it */ + R7 = R0; + + /* Enable Cycle Counter and Nesting Of Interrupts */ +#ifdef CONFIG_BFIN_SCRATCH_REG_CYCLES + R0 = SYSCFG_SNEN; +#else + R0 = SYSCFG_SNEN | SYSCFG_CCEN; +#endif + SYSCFG = R0; + + /* Optimization register tricks: keep a base value in the + * reserved P registers so we use the load/store with an + * offset syntax. R0 = [P5 + <constant>]; + * P5 - core MMR base + * R6 - 0 + */ + r6 = 0; + p5.l = 0; + p5.h = hi(COREMMR_BASE); + + /* Zero out registers required by Blackfin ABI */ + + /* Disable circular buffers */ + L0 = r6; + L1 = r6; + L2 = r6; + L3 = r6; + + /* Disable hardware loops in case we were started by 'go' */ + LC0 = r6; + LC1 = r6; + + /* + * Clear ITEST_COMMAND and DTEST_COMMAND registers, + * Leaving these as non-zero can confuse the emulator + */ + [p5 + (DTEST_COMMAND - COREMMR_BASE)] = r6; + [p5 + (ITEST_COMMAND - COREMMR_BASE)] = r6; + CSYNC; + + trace_buffer_init(p0,r0); + + /* Turn off the icache */ + r1 = [p5 + (IMEM_CONTROL - COREMMR_BASE)]; + BITCLR (r1, ENICPLB_P); + [p5 + (IMEM_CONTROL - COREMMR_BASE)] = r1; + SSYNC; + + /* Turn off the dcache */ + r1 = [p5 + (DMEM_CONTROL - COREMMR_BASE)]; + BITCLR (r1, ENDCPLB_P); + [p5 + (DMEM_CONTROL - COREMMR_BASE)] = r1; + SSYNC; + + /* in case of double faults, save a few things */ + p1.l = _initial_pda; + p1.h = _initial_pda; + r4 = RETX; +#ifdef CONFIG_DEBUG_DOUBLEFAULT + /* Only save these if we are storing them, + * This happens here, since L1 gets clobbered + * below + */ + GET_PDA(p0, r0); + r0 = [p0 + PDA_DF_RETX]; + r1 = [p0 + PDA_DF_DCPLB]; + r2 = [p0 + PDA_DF_ICPLB]; + r3 = [p0 + PDA_DF_SEQSTAT]; + [p1 + PDA_INIT_DF_RETX] = r0; + [p1 + PDA_INIT_DF_DCPLB] = r1; + [p1 + PDA_INIT_DF_ICPLB] = r2; + [p1 + PDA_INIT_DF_SEQSTAT] = r3; +#endif + [p1 + PDA_INIT_RETX] = r4; + + /* Initialize stack pointer */ + sp.l = _init_thread_union + THREAD_SIZE; + sp.h = _init_thread_union + THREAD_SIZE; + fp = sp; + usp = sp; + +#ifdef CONFIG_EARLY_PRINTK + call _init_early_exception_vectors; + r0 = (EVT_IVHW | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU); + sti r0; +#endif + + r0 = r6; + /* Zero out all of the fun bss regions */ +#if L1_DATA_A_LENGTH > 0 + r1.l = __sbss_l1; + r1.h = __sbss_l1; + r2.l = __ebss_l1; + r2.h = __ebss_l1; + call __init_clear_bss +#endif +#if L1_DATA_B_LENGTH > 0 + r1.l = __sbss_b_l1; + r1.h = __sbss_b_l1; + r2.l = __ebss_b_l1; + r2.h = __ebss_b_l1; + call __init_clear_bss +#endif +#if L2_LENGTH > 0 + r1.l = __sbss_l2; + r1.h = __sbss_l2; + r2.l = __ebss_l2; + r2.h = __ebss_l2; + call __init_clear_bss +#endif + r1.l = ___bss_start; + r1.h = ___bss_start; + r2.l = ___bss_stop; + r2.h = ___bss_stop; + call __init_clear_bss + + /* Put The Code for PLL Programming and SDRAM Programming in L1 ISRAM */ + call _bfin_relocate_l1_mem; + +#ifdef CONFIG_ROMKERNEL + call _bfin_relocate_xip_data; +#endif + +#ifdef CONFIG_BFIN_KERNEL_CLOCK + /* Only use on-chip scratch space for stack when absolutely required + * to avoid Anomaly 05000227 ... we know the init_clocks() func only + * uses L1 text and stack space and no other memory region. + */ +# define KERNEL_CLOCK_STACK (L1_SCRATCH_START + L1_SCRATCH_LENGTH - 12) + sp.l = lo(KERNEL_CLOCK_STACK); + sp.h = hi(KERNEL_CLOCK_STACK); + call _init_clocks; + sp = usp; /* usp hasn't been touched, so restore from there */ +#endif + + /* This section keeps the processor in supervisor mode + * during kernel boot. Switches to user mode at end of boot. + * See page 3-9 of Hardware Reference manual for documentation. + */ + + /* EVT15 = _real_start */ + + p1.l = _real_start; + p1.h = _real_start; + [p5 + (EVT15 - COREMMR_BASE)] = p1; + csync; + +#ifdef CONFIG_EARLY_PRINTK + r0 = (EVT_IVG15 | EVT_IVHW | EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU) (z); +#else + r0 = EVT_IVG15 (z); +#endif + sti r0; + + raise 15; +#ifdef CONFIG_EARLY_PRINTK + p0.l = _early_trap; + p0.h = _early_trap; +#else + p0.l = .LWAIT_HERE; + p0.h = .LWAIT_HERE; +#endif + reti = p0; +#if ANOMALY_05000281 + nop; nop; nop; +#endif + rti; + +.LWAIT_HERE: + jump .LWAIT_HERE; +ENDPROC(__start) + +/* A little BF561 glue ... */ +#ifndef WDOG_CTL +# define WDOG_CTL WDOGA_CTL +#endif + +ENTRY(_real_start) + /* Enable nested interrupts */ + [--sp] = reti; + /* watchdog off for now */ + p0.l = lo(WDOG_CTL); + p0.h = hi(WDOG_CTL); + r0 = 0xAD6(z); + w[p0] = r0; + ssync; + /* Pass the u-boot arguments to the global value command line */ + R0 = R7; + call _cmdline_init; + + sp += -12 + 4; /* +4 is for reti loading above */ + call _init_pda + sp += 12; + jump.l _start_kernel; +ENDPROC(_real_start) + +__FINIT diff --git a/arch/blackfin/mach-common/interrupt.S b/arch/blackfin/mach-common/interrupt.S index 7f752c87fe4..469ce7282dc 100644 --- a/arch/blackfin/mach-common/interrupt.S +++ b/arch/blackfin/mach-common/interrupt.S @@ -1,35 +1,15 @@ /* - * File: arch/blackfin/mach-common/interrupt.S - * Based on: - * Author: D. Jeff Dionne <jeff@ryeham.ee.ryerson.ca> - * Kenneth Albanowski <kjahds@kjahds.com> - * - * Created: ? - * Description: Interrupt Entries - * - * Modified: - * Copyright 2004-2006 Analog Devices Inc. - * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * Interrupt Entries * - * 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. + * Copyright 2005-2009 Analog Devices Inc. + * D. Jeff Dionne <jeff@ryeham.ee.ryerson.ca> + * Kenneth Albanowski <kjahds@kjahds.com> * - * 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 + * Licensed under the GPL-2 or later. */ #include <asm/blackfin.h> -#include <asm/mach/irq.h> +#include <mach/irq.h> #include <linux/linkage.h> #include <asm/entry.h> #include <asm/asm-offsets.h> @@ -37,7 +17,7 @@ #include <asm/traps.h> #include <asm/thread_info.h> -#include <asm/mach-common/context.S> +#include <asm/context.S> .extern _ret_from_exception @@ -108,6 +88,13 @@ __common_int_entry: #else cli r1; #endif +#ifdef CONFIG_TRACE_IRQFLAGS + [--sp] = r0; + sp += -12; + call _trace_hardirqs_off; + sp += 12; + r0 = [sp++]; +#endif [--sp] = RETI; /* orig_pc */ /* Clear all L registers. */ r1 = 0 (x); @@ -119,48 +106,77 @@ __common_int_entry: fp = 0; #endif -#if ANOMALY_05000283 || ANOMALY_05000315 - cc = r7 == r7; - p5.h = HI(CHIPID); - p5.l = LO(CHIPID); - if cc jump 1f; - r7.l = W[p5]; -1: -#endif + ANOMALY_283_315_WORKAROUND(p5, r7) + r1 = sp; SP += -12; - call _do_irq; +#ifdef CONFIG_IPIPE + call ___ipipe_grab_irq SP += 12; - call _return_from_int; + cc = r0 == 0; + if cc jump .Lcommon_restore_context; +#else /* CONFIG_IPIPE */ + +#ifdef CONFIG_PREEMPT + r7 = sp; + r4.l = lo(ALIGN_PAGE_MASK); + r4.h = hi(ALIGN_PAGE_MASK); + r7 = r7 & r4; + p5 = r7; + r7 = [p5 + TI_PREEMPT]; /* get preempt count */ + r7 += 1; /* increment it */ + [p5 + TI_PREEMPT] = r7; +#endif + pseudo_long_call _do_irq, p2; + +#ifdef CONFIG_PREEMPT + r7 += -1; + [p5 + TI_PREEMPT] = r7; /* restore preempt count */ +#endif + + SP += 12; +#endif /* CONFIG_IPIPE */ + pseudo_long_call _return_from_int, p2; .Lcommon_restore_context: RESTORE_CONTEXT rti; /* interrupt routine for ivhw - 5 */ ENTRY(_evt_ivhw) + /* In case a single action kicks off multiple memory transactions, (like + * a cache line fetch, - this can cause multiple hardware errors, let's + * catch them all. First - make sure all the actions are complete, and + * the core sees the hardware errors. + */ + SSYNC; + SSYNC; + SAVE_ALL_SYS #ifdef CONFIG_FRAME_POINTER fp = 0; #endif -#if ANOMALY_05000283 - cc = r7 == r7; - p5.h = HI(CHIPID); - p5.l = LO(CHIPID); - if cc jump 1f; - r7.l = W[p5]; -1: -#endif + ANOMALY_283_315_WORKAROUND(p5, r7) -#ifdef CONFIG_HARDWARE_PM - r7 = [sp + PT_SEQSTAT]; - r7 = r7 >>> 0xe; - r6 = 0x1F; - r7 = r7 & r6; - r5 = 0x12; - cc = r7 == r5; - if cc jump .Lcall_do_ovf; /* deal with performance counter overflow */ -#endif + /* Handle all stacked hardware errors + * To make sure we don't hang forever, only do it 10 times + */ + R0 = 0; + R2 = 10; +1: + P0.L = LO(ILAT); + P0.H = HI(ILAT); + R1 = [P0]; + CC = BITTST(R1, EVT_IVHW_P); + IF ! CC JUMP 2f; + /* OK a hardware error is pending - clear it */ + R1 = EVT_IVHW_P; + [P0] = R1; + R0 += 1; + CC = R1 == R2; + if CC JUMP 2f; + JUMP 1b; +2: # We are going to dump something out, so make sure we print IPEND properly p2.l = lo(IPEND); p2.h = hi(IPEND); @@ -176,33 +192,47 @@ ENTRY(_evt_ivhw) r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ SP += -12; - call _trap_c; + pseudo_long_call _trap_c, p5; SP += 12; - call _ret_from_exception; +#ifdef EBIU_ERRMST + /* make sure EBIU_ERRMST is clear */ + p0.l = LO(EBIU_ERRMST); + p0.h = HI(EBIU_ERRMST); + r0.l = (CORE_ERROR | CORE_MERROR); + w[p0] = r0.l; +#endif + + pseudo_long_call _ret_from_exception, p2; + .Lcommon_restore_all_sys: RESTORE_ALL_SYS rti; - -#ifdef CONFIG_HARDWARE_PM -.Lcall_do_ovf: - - SP += -12; - call _pm_overflow; - SP += 12; - - jump .Lcommon_restore_all_sys; -#endif - ENDPROC(_evt_ivhw) /* Interrupt routine for evt2 (NMI). - * We don't actually use this, so just return. * For inner circle type details, please see: - * http://docs.blackfin.uclinux.org/doku.php?id=linux:nmi + * http://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:nmi */ ENTRY(_evt_nmi) +#ifndef CONFIG_NMI_WATCHDOG .weak _evt_nmi +#else + /* Not take account of CPLBs, this handler will not return */ + SAVE_ALL_SYS + r0 = sp; + r1 = retn; + [sp + PT_PC] = r1; + trace_buffer_save(p4,r5); + + ANOMALY_283_315_WORKAROUND(p4, r5) + + SP += -12; + call _do_nmi; + SP += 12; +1: + jump 1b; +#endif rtn; ENDPROC(_evt_nmi) @@ -233,6 +263,64 @@ ENTRY(_evt_system_call) #ifdef CONFIG_FRAME_POINTER fp = 0; #endif - call _system_call; + pseudo_long_call _system_call, p2; jump .Lcommon_restore_context; ENDPROC(_evt_system_call) + +#ifdef CONFIG_IPIPE +/* + * __ipipe_call_irqtail: lowers the current priority level to EVT15 + * before running a user-defined routine, then raises the priority + * level to EVT14 to prepare the caller for a normal interrupt + * return through RTI. + * + * We currently use this feature in two occasions: + * + * - before branching to __ipipe_irq_tail_hook as requested by a high + * priority domain after the pipeline delivered an interrupt, + * e.g. such as Xenomai, in order to start its rescheduling + * procedure, since we may not switch tasks when IRQ levels are + * nested on the Blackfin, so we have to fake an interrupt return + * so that we may reschedule immediately. + * + * - before branching to __ipipe_sync_root(), in order to play any interrupt + * pending for the root domain (i.e. the Linux kernel). This lowers + * the core priority level enough so that Linux IRQ handlers may + * never delay interrupts handled by high priority domains; we defer + * those handlers until this point instead. This is a substitute + * to using a threaded interrupt model for the Linux kernel. + * + * r0: address of user-defined routine + * context: caller must have preempted EVT15, hw interrupts must be off. + */ +ENTRY(___ipipe_call_irqtail) + p0 = r0; + r0.l = 1f; + r0.h = 1f; + reti = r0; + rti; +1: + [--sp] = rets; + [--sp] = ( r7:4, p5:3 ); + sp += -12; + call (p0); + sp += 12; + ( r7:4, p5:3 ) = [sp++]; + rets = [sp++]; + +#ifdef CONFIG_DEBUG_HWERR + /* enable irq14 & hwerr interrupt, until we transition to _evt_evt14 */ + r0 = (EVT_IVG14 | EVT_IVHW | \ + EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU); +#else + /* Only enable irq14 interrupt, until we transition to _evt_evt14 */ + r0 = (EVT_IVG14 | \ + EVT_IRPTEN | EVT_EVX | EVT_NMI | EVT_RST | EVT_EMU); +#endif + sti r0; + raise 14; /* Branches to _evt_evt14 */ +2: + jump 2b; /* Likely paranoid. */ +ENDPROC(___ipipe_call_irqtail) + +#endif /* CONFIG_IPIPE */ diff --git a/arch/blackfin/mach-common/ints-priority.c b/arch/blackfin/mach-common/ints-priority.c index f5fd768022e..1f94784eab6 100644 --- a/arch/blackfin/mach-common/ints-priority.c +++ b/arch/blackfin/mach-common/ints-priority.c @@ -1,55 +1,32 @@ /* - * File: arch/blackfin/mach-common/ints-priority.c - * Based on: - * Author: + * Set up the interrupt priorities * - * Created: ? - * Description: Set up the interrupt priorities + * Copyright 2004-2009 Analog Devices Inc. + * 2003 Bas Vermeulen <bas@buyways.nl> + * 2002 Arcturus Networks Inc. MaTed <mated@sympatico.ca> + * 2000-2001 Lineo, Inc. D. Jefff Dionne <jeff@lineo.ca> + * 1999 D. Jeff Dionne <jeff@uclinux.org> + * 1996 Roman Zippel * - * Modified: - * 1996 Roman Zippel - * 1999 D. Jeff Dionne <jeff@uclinux.org> - * 2000-2001 Lineo, Inc. D. Jefff Dionne <jeff@lineo.ca> - * 2002 Arcturus Networks Inc. MaTed <mated@sympatico.ca> - * 2003 Metrowerks/Motorola - * 2003 Bas Vermeulen <bas@buyways.nl> - * Copyright 2004-2008 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 + * Licensed under the GPL-2 */ #include <linux/module.h> #include <linux/kernel_stat.h> #include <linux/seq_file.h> #include <linux/irq.h> -#ifdef CONFIG_KGDB -#include <linux/kgdb.h> +#include <linux/sched.h> +#include <linux/syscore_ops.h> +#include <asm/delay.h> +#ifdef CONFIG_IPIPE +#include <linux/ipipe.h> #endif #include <asm/traps.h> #include <asm/blackfin.h> #include <asm/gpio.h> #include <asm/irq_handler.h> - -#ifdef BF537_FAMILY -# define BF537_GENERIC_ERROR_INT_DEMUX -#else -# undef BF537_GENERIC_ERROR_INT_DEMUX -#endif +#include <asm/dpmc.h> +#include <asm/traps.h> /* * NOTES: @@ -58,29 +35,31 @@ * - */ +#ifndef CONFIG_SMP /* Initialize this to an actual value to force it into the .data * section so that we know it is properly initialized at entry into * the kernel but before bss is initialized to zero (which is where * it would live otherwise). The 0x1f magic represents the IRQs we * cannot actually mask out in hardware. */ -unsigned long irq_flags = 0x1f; - -/* The number of spurious interrupts */ -atomic_t num_spurious; +unsigned long bfin_irq_flags = 0x1f; +EXPORT_SYMBOL(bfin_irq_flags); +#endif #ifdef CONFIG_PM unsigned long bfin_sic_iwr[3]; /* Up to 3 SIC_IWRx registers */ +unsigned vr_wakeup; #endif -struct ivgx { +#ifndef SEC_GCTL +static struct ivgx { /* irq number for request_irq, available in mach-bf5xx/irq.h */ unsigned int irqno; /* corresponding bit in the SIC_ISR register */ unsigned int isrflag; } ivg_table[NR_PERI_INTS]; -struct ivg_slice { +static struct ivg_slice { /* position of first irq in ivg_table for given ivg */ struct ivgx *ifirst; struct ivgx *istop; @@ -95,809 +74,881 @@ static void __init search_IAR(void) { unsigned ivg, irq_pos = 0; for (ivg = 0; ivg <= IVG13 - IVG7; ivg++) { - int irqn; + int irqN; ivg7_13[ivg].istop = ivg7_13[ivg].ifirst = &ivg_table[irq_pos]; - for (irqn = 0; irqn < NR_PERI_INTS; irqn++) { - int iar_shift = (irqn & 7) * 4; - if (ivg == (0xf & -#ifndef CONFIG_BF52x - bfin_read32((unsigned long *)SIC_IAR0 + - (irqn >> 3)) >> iar_shift)) { + for (irqN = 0; irqN < NR_PERI_INTS; irqN += 4) { + int irqn; + u32 iar = + bfin_read32((unsigned long *)SIC_IAR0 + +#if defined(CONFIG_BF51x) || defined(CONFIG_BF52x) || \ + defined(CONFIG_BF538) || defined(CONFIG_BF539) + ((irqN % 32) >> 3) + ((irqN / 32) * ((SIC_IAR4 - SIC_IAR0) / 4)) #else - bfin_read32((unsigned long *)SIC_IAR0 + - ((irqn%32) >> 3) + ((irqn / 32) * 16)) >> iar_shift)) { + (irqN >> 3) #endif - ivg_table[irq_pos].irqno = IVG7 + irqn; - ivg_table[irq_pos].isrflag = 1 << (irqn % 32); - ivg7_13[ivg].istop++; - irq_pos++; + ); + for (irqn = irqN; irqn < irqN + 4; ++irqn) { + int iar_shift = (irqn & 7) * 4; + if (ivg == (0xf & (iar >> iar_shift))) { + ivg_table[irq_pos].irqno = IVG7 + irqn; + ivg_table[irq_pos].isrflag = 1 << (irqn % 32); + ivg7_13[ivg].istop++; + irq_pos++; + } } } } } +#endif /* * This is for core internal IRQs */ - -static void bfin_ack_noop(unsigned int irq) +void bfin_ack_noop(struct irq_data *d) { /* Dummy function. */ } -static void bfin_core_mask_irq(unsigned int irq) +static void bfin_core_mask_irq(struct irq_data *d) { - irq_flags &= ~(1 << irq); - if (!irqs_disabled()) - local_irq_enable(); + bfin_irq_flags &= ~(1 << d->irq); + if (!hard_irqs_disabled()) + hard_local_irq_enable(); } -static void bfin_core_unmask_irq(unsigned int irq) +static void bfin_core_unmask_irq(struct irq_data *d) { - irq_flags |= 1 << irq; + bfin_irq_flags |= 1 << d->irq; /* * If interrupts are enabled, IMASK must contain the same value - * as irq_flags. Make sure that invariant holds. If interrupts + * as bfin_irq_flags. Make sure that invariant holds. If interrupts * are currently disabled we need not do anything; one of the * callers will take care of setting IMASK to the proper value * when reenabling interrupts. - * local_irq_enable just does "STI irq_flags", so it's exactly + * local_irq_enable just does "STI bfin_irq_flags", so it's exactly * what we need. */ - if (!irqs_disabled()) - local_irq_enable(); + if (!hard_irqs_disabled()) + hard_local_irq_enable(); return; } -static void bfin_internal_mask_irq(unsigned int irq) +#ifndef SEC_GCTL +void bfin_internal_mask_irq(unsigned int irq) { -#ifdef CONFIG_BF53x + unsigned long flags = hard_local_irq_save(); +#ifdef SIC_IMASK0 + unsigned mask_bank = BFIN_SYSIRQ(irq) / 32; + unsigned mask_bit = BFIN_SYSIRQ(irq) % 32; + bfin_write_SIC_IMASK(mask_bank, bfin_read_SIC_IMASK(mask_bank) & + ~(1 << mask_bit)); +# if defined(CONFIG_SMP) || defined(CONFIG_ICC) + bfin_write_SICB_IMASK(mask_bank, bfin_read_SICB_IMASK(mask_bank) & + ~(1 << mask_bit)); +# endif +#else bfin_write_SIC_IMASK(bfin_read_SIC_IMASK() & - ~(1 << SIC_SYSIRQ(irq))); + ~(1 << BFIN_SYSIRQ(irq))); +#endif /* end of SIC_IMASK0 */ + hard_local_irq_restore(flags); +} + +static void bfin_internal_mask_irq_chip(struct irq_data *d) +{ + bfin_internal_mask_irq(d->irq); +} + +#ifdef CONFIG_SMP +void bfin_internal_unmask_irq_affinity(unsigned int irq, + const struct cpumask *affinity) #else - unsigned mask_bank, mask_bit; - mask_bank = SIC_SYSIRQ(irq) / 32; - mask_bit = SIC_SYSIRQ(irq) % 32; - bfin_write_SIC_IMASK(mask_bank, bfin_read_SIC_IMASK(mask_bank) & - ~(1 << mask_bit)); +void bfin_internal_unmask_irq(unsigned int irq) +#endif +{ + unsigned long flags = hard_local_irq_save(); + +#ifdef SIC_IMASK0 + unsigned mask_bank = BFIN_SYSIRQ(irq) / 32; + unsigned mask_bit = BFIN_SYSIRQ(irq) % 32; +# ifdef CONFIG_SMP + if (cpumask_test_cpu(0, affinity)) +# endif + bfin_write_SIC_IMASK(mask_bank, + bfin_read_SIC_IMASK(mask_bank) | + (1 << mask_bit)); +# ifdef CONFIG_SMP + if (cpumask_test_cpu(1, affinity)) + bfin_write_SICB_IMASK(mask_bank, + bfin_read_SICB_IMASK(mask_bank) | + (1 << mask_bit)); +# endif +#else + bfin_write_SIC_IMASK(bfin_read_SIC_IMASK() | + (1 << BFIN_SYSIRQ(irq))); #endif - SSYNC(); + hard_local_irq_restore(flags); } -static void bfin_internal_unmask_irq(unsigned int irq) +#ifdef CONFIG_SMP +static void bfin_internal_unmask_irq_chip(struct irq_data *d) { -#ifdef CONFIG_BF53x - bfin_write_SIC_IMASK(bfin_read_SIC_IMASK() | - (1 << SIC_SYSIRQ(irq))); + bfin_internal_unmask_irq_affinity(d->irq, d->affinity); +} + +static int bfin_internal_set_affinity(struct irq_data *d, + const struct cpumask *mask, bool force) +{ + bfin_internal_mask_irq(d->irq); + bfin_internal_unmask_irq_affinity(d->irq, mask); + + return 0; +} #else - unsigned mask_bank, mask_bit; - mask_bank = SIC_SYSIRQ(irq) / 32; - mask_bit = SIC_SYSIRQ(irq) % 32; - bfin_write_SIC_IMASK(mask_bank, bfin_read_SIC_IMASK(mask_bank) | - (1 << mask_bit)); -#endif - SSYNC(); +static void bfin_internal_unmask_irq_chip(struct irq_data *d) +{ + bfin_internal_unmask_irq(d->irq); } +#endif -#ifdef CONFIG_PM +#if defined(CONFIG_PM) int bfin_internal_set_wake(unsigned int irq, unsigned int state) { - unsigned bank, bit; + u32 bank, bit, wakeup = 0; unsigned long flags; - bank = SIC_SYSIRQ(irq) / 32; - bit = SIC_SYSIRQ(irq) % 32; + bank = BFIN_SYSIRQ(irq) / 32; + bit = BFIN_SYSIRQ(irq) % 32; + + switch (irq) { +#ifdef IRQ_RTC + case IRQ_RTC: + wakeup |= WAKE; + break; +#endif +#ifdef IRQ_CAN0_RX + case IRQ_CAN0_RX: + wakeup |= CANWE; + break; +#endif +#ifdef IRQ_CAN1_RX + case IRQ_CAN1_RX: + wakeup |= CANWE; + break; +#endif +#ifdef IRQ_USB_INT0 + case IRQ_USB_INT0: + wakeup |= USBWE; + break; +#endif +#ifdef CONFIG_BF54x + case IRQ_CNT: + wakeup |= ROTWE; + break; +#endif + default: + break; + } - local_irq_save(flags); + flags = hard_local_irq_save(); - if (state) + if (state) { bfin_sic_iwr[bank] |= (1 << bit); - else + vr_wakeup |= wakeup; + + } else { bfin_sic_iwr[bank] &= ~(1 << bit); + vr_wakeup &= ~wakeup; + } - local_irq_restore(flags); + hard_local_irq_restore(flags); return 0; } -#endif - -static struct irq_chip bfin_core_irqchip = { - .ack = bfin_ack_noop, - .mask = bfin_core_mask_irq, - .unmask = bfin_core_unmask_irq, -}; -static struct irq_chip bfin_internal_irqchip = { - .ack = bfin_ack_noop, - .mask = bfin_internal_mask_irq, - .unmask = bfin_internal_unmask_irq, - .mask_ack = bfin_internal_mask_irq, - .disable = bfin_internal_mask_irq, - .enable = bfin_internal_unmask_irq, -#ifdef CONFIG_PM - .set_wake = bfin_internal_set_wake, +static int bfin_internal_set_wake_chip(struct irq_data *d, unsigned int state) +{ + return bfin_internal_set_wake(d->irq, state); +} +#else +inline int bfin_internal_set_wake(unsigned int irq, unsigned int state) +{ + return 0; +} +# define bfin_internal_set_wake_chip NULL #endif -}; - -#ifdef BF537_GENERIC_ERROR_INT_DEMUX -static int error_int_mask; -static void bfin_generic_error_mask_irq(unsigned int irq) +#else /* SEC_GCTL */ +static void bfin_sec_preflow_handler(struct irq_data *d) { - error_int_mask &= ~(1L << (irq - IRQ_PPI_ERROR)); + unsigned long flags = hard_local_irq_save(); + unsigned int sid = BFIN_SYSIRQ(d->irq); + + bfin_write_SEC_SCI(0, SEC_CSID, sid); - if (!error_int_mask) - bfin_internal_mask_irq(IRQ_GENERIC_ERROR); + hard_local_irq_restore(flags); } -static void bfin_generic_error_unmask_irq(unsigned int irq) +static void bfin_sec_mask_ack_irq(struct irq_data *d) { - bfin_internal_unmask_irq(IRQ_GENERIC_ERROR); - error_int_mask |= 1L << (irq - IRQ_PPI_ERROR); -} + unsigned long flags = hard_local_irq_save(); + unsigned int sid = BFIN_SYSIRQ(d->irq); -static struct irq_chip bfin_generic_error_irqchip = { - .ack = bfin_ack_noop, - .mask_ack = bfin_generic_error_mask_irq, - .mask = bfin_generic_error_mask_irq, - .unmask = bfin_generic_error_unmask_irq, -}; + bfin_write_SEC_SCI(0, SEC_CSID, sid); -static void bfin_demux_error_irq(unsigned int int_err_irq, - struct irq_desc *inta_desc) + hard_local_irq_restore(flags); +} + +static void bfin_sec_unmask_irq(struct irq_data *d) { - int irq = 0; + unsigned long flags = hard_local_irq_save(); + unsigned int sid = BFIN_SYSIRQ(d->irq); - SSYNC(); + bfin_write32(SEC_END, sid); -#if (defined(CONFIG_BF537) || defined(CONFIG_BF536)) - if (bfin_read_EMAC_SYSTAT() & EMAC_ERR_MASK) - irq = IRQ_MAC_ERROR; - else -#endif - if (bfin_read_SPORT0_STAT() & SPORT_ERR_MASK) - irq = IRQ_SPORT0_ERROR; - else if (bfin_read_SPORT1_STAT() & SPORT_ERR_MASK) - irq = IRQ_SPORT1_ERROR; - else if (bfin_read_PPI_STATUS() & PPI_ERR_MASK) - irq = IRQ_PPI_ERROR; - else if (bfin_read_CAN_GIF() & CAN_ERR_MASK) - irq = IRQ_CAN_ERROR; - else if (bfin_read_SPI_STAT() & SPI_ERR_MASK) - irq = IRQ_SPI_ERROR; - else if ((bfin_read_UART0_IIR() & UART_ERR_MASK_STAT1) && - (bfin_read_UART0_IIR() & UART_ERR_MASK_STAT0)) - irq = IRQ_UART0_ERROR; - else if ((bfin_read_UART1_IIR() & UART_ERR_MASK_STAT1) && - (bfin_read_UART1_IIR() & UART_ERR_MASK_STAT0)) - irq = IRQ_UART1_ERROR; + hard_local_irq_restore(flags); +} - if (irq) { - if (error_int_mask & (1L << (irq - IRQ_PPI_ERROR))) { - struct irq_desc *desc = irq_desc + irq; - desc->handle_irq(irq, desc); - } else { +static void bfin_sec_enable_ssi(unsigned int sid) +{ + unsigned long flags = hard_local_irq_save(); + uint32_t reg_sctl = bfin_read_SEC_SCTL(sid); - switch (irq) { - case IRQ_PPI_ERROR: - bfin_write_PPI_STATUS(PPI_ERR_MASK); - break; -#if (defined(CONFIG_BF537) || defined(CONFIG_BF536)) - case IRQ_MAC_ERROR: - bfin_write_EMAC_SYSTAT(EMAC_ERR_MASK); - break; -#endif - case IRQ_SPORT0_ERROR: - bfin_write_SPORT0_STAT(SPORT_ERR_MASK); - break; + reg_sctl |= SEC_SCTL_SRC_EN; + bfin_write_SEC_SCTL(sid, reg_sctl); - case IRQ_SPORT1_ERROR: - bfin_write_SPORT1_STAT(SPORT_ERR_MASK); - break; + hard_local_irq_restore(flags); +} - case IRQ_CAN_ERROR: - bfin_write_CAN_GIS(CAN_ERR_MASK); - break; +static void bfin_sec_disable_ssi(unsigned int sid) +{ + unsigned long flags = hard_local_irq_save(); + uint32_t reg_sctl = bfin_read_SEC_SCTL(sid); - case IRQ_SPI_ERROR: - bfin_write_SPI_STAT(SPI_ERR_MASK); - break; + reg_sctl &= ((uint32_t)~SEC_SCTL_SRC_EN); + bfin_write_SEC_SCTL(sid, reg_sctl); - default: - break; - } + hard_local_irq_restore(flags); +} - pr_debug("IRQ %d:" - " MASKED PERIPHERAL ERROR INTERRUPT ASSERTED\n", - irq); - } - } else - printk(KERN_ERR - "%s : %s : LINE %d :\nIRQ ?: PERIPHERAL ERROR" - " INTERRUPT ASSERTED BUT NO SOURCE FOUND\n", - __func__, __FILE__, __LINE__); +static void bfin_sec_set_ssi_coreid(unsigned int sid, unsigned int coreid) +{ + unsigned long flags = hard_local_irq_save(); + uint32_t reg_sctl = bfin_read_SEC_SCTL(sid); + reg_sctl &= ((uint32_t)~SEC_SCTL_CTG); + bfin_write_SEC_SCTL(sid, reg_sctl | ((coreid << 20) & SEC_SCTL_CTG)); + + hard_local_irq_restore(flags); } -#endif /* BF537_GENERIC_ERROR_INT_DEMUX */ -#if !defined(CONFIG_BF54x) +static void bfin_sec_enable_sci(unsigned int sid) +{ + unsigned long flags = hard_local_irq_save(); + uint32_t reg_sctl = bfin_read_SEC_SCTL(sid); -static unsigned short gpio_enabled[gpio_bank(MAX_BLACKFIN_GPIOS)]; -static unsigned short gpio_edge_triggered[gpio_bank(MAX_BLACKFIN_GPIOS)]; + if (sid == BFIN_SYSIRQ(IRQ_WATCH0)) + reg_sctl |= SEC_SCTL_FAULT_EN; + else + reg_sctl |= SEC_SCTL_INT_EN; + bfin_write_SEC_SCTL(sid, reg_sctl); -extern void bfin_gpio_irq_prepare(unsigned gpio); + hard_local_irq_restore(flags); +} -static void bfin_gpio_ack_irq(unsigned int irq) +static void bfin_sec_disable_sci(unsigned int sid) { - u16 gpionr = irq - IRQ_PF0; + unsigned long flags = hard_local_irq_save(); + uint32_t reg_sctl = bfin_read_SEC_SCTL(sid); - if (gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) { - set_gpio_data(gpionr, 0); - SSYNC(); - } + reg_sctl &= ((uint32_t)~SEC_SCTL_INT_EN); + bfin_write_SEC_SCTL(sid, reg_sctl); + + hard_local_irq_restore(flags); } -static void bfin_gpio_mask_ack_irq(unsigned int irq) +static void bfin_sec_enable(struct irq_data *d) { - u16 gpionr = irq - IRQ_PF0; + unsigned long flags = hard_local_irq_save(); + unsigned int sid = BFIN_SYSIRQ(d->irq); - if (gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) { - set_gpio_data(gpionr, 0); - SSYNC(); - } + bfin_sec_enable_sci(sid); + bfin_sec_enable_ssi(sid); - set_gpio_maska(gpionr, 0); - SSYNC(); + hard_local_irq_restore(flags); } -static void bfin_gpio_mask_irq(unsigned int irq) +static void bfin_sec_disable(struct irq_data *d) { - set_gpio_maska(irq - IRQ_PF0, 0); - SSYNC(); + unsigned long flags = hard_local_irq_save(); + unsigned int sid = BFIN_SYSIRQ(d->irq); + + bfin_sec_disable_sci(sid); + bfin_sec_disable_ssi(sid); + + hard_local_irq_restore(flags); } -static void bfin_gpio_unmask_irq(unsigned int irq) +static void bfin_sec_set_priority(unsigned int sec_int_levels, u8 *sec_int_priority) { - set_gpio_maska(irq - IRQ_PF0, 1); - SSYNC(); + unsigned long flags = hard_local_irq_save(); + uint32_t reg_sctl; + int i; + + bfin_write_SEC_SCI(0, SEC_CPLVL, sec_int_levels); + + for (i = 0; i < SYS_IRQS - BFIN_IRQ(0); i++) { + reg_sctl = bfin_read_SEC_SCTL(i) & ~SEC_SCTL_PRIO; + reg_sctl |= sec_int_priority[i] << SEC_SCTL_PRIO_OFFSET; + bfin_write_SEC_SCTL(i, reg_sctl); + } + + hard_local_irq_restore(flags); } -static unsigned int bfin_gpio_irq_startup(unsigned int irq) +void bfin_sec_raise_irq(unsigned int irq) { - u16 gpionr = irq - IRQ_PF0; - - if (!(gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))) - bfin_gpio_irq_prepare(gpionr); + unsigned long flags = hard_local_irq_save(); + unsigned int sid = BFIN_SYSIRQ(irq); - gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); - bfin_gpio_unmask_irq(irq); + bfin_write32(SEC_RAISE, sid); - return 0; + hard_local_irq_restore(flags); } -static void bfin_gpio_irq_shutdown(unsigned int irq) +static void init_software_driven_irq(void) { - bfin_gpio_mask_irq(irq); - gpio_enabled[gpio_bank(irq - IRQ_PF0)] &= ~gpio_bit(irq - IRQ_PF0); + bfin_sec_set_ssi_coreid(34, 0); + bfin_sec_set_ssi_coreid(35, 1); + + bfin_sec_enable_sci(35); + bfin_sec_enable_ssi(35); + bfin_sec_set_ssi_coreid(36, 0); + bfin_sec_set_ssi_coreid(37, 1); + bfin_sec_enable_sci(37); + bfin_sec_enable_ssi(37); } -static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) +void bfin_sec_resume(void) { - u16 gpionr = irq - IRQ_PF0; + bfin_write_SEC_SCI(0, SEC_CCTL, SEC_CCTL_RESET); + udelay(100); + bfin_write_SEC_GCTL(SEC_GCTL_EN); + bfin_write_SEC_SCI(0, SEC_CCTL, SEC_CCTL_EN | SEC_CCTL_NMI_EN); +} - if (type == IRQ_TYPE_PROBE) { - /* only probe unenabled GPIO interrupt lines */ - if (gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr)) - return 0; - type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; - } +void handle_sec_sfi_fault(uint32_t gstat) +{ - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING | - IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { - if (!(gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))) - bfin_gpio_irq_prepare(gpionr); +} - gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); - } else { - gpio_enabled[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); - return 0; +void handle_sec_sci_fault(uint32_t gstat) +{ + uint32_t core_id; + uint32_t cstat; + + core_id = gstat & SEC_GSTAT_SCI; + cstat = bfin_read_SEC_SCI(core_id, SEC_CSTAT); + if (cstat & SEC_CSTAT_ERR) { + switch (cstat & SEC_CSTAT_ERRC) { + case SEC_CSTAT_ACKERR: + printk(KERN_DEBUG "sec ack err\n"); + break; + default: + printk(KERN_DEBUG "sec sci unknow err\n"); + } } - set_gpio_inen(gpionr, 0); - set_gpio_dir(gpionr, 0); - - if ((type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) - == (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) - set_gpio_both(gpionr, 1); - else - set_gpio_both(gpionr, 0); - - if ((type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW))) - set_gpio_polar(gpionr, 1); /* low or falling edge denoted by one */ - else - set_gpio_polar(gpionr, 0); /* high or rising edge denoted by zero */ - - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { - set_gpio_edge(gpionr, 1); - set_gpio_inen(gpionr, 1); - gpio_edge_triggered[gpio_bank(gpionr)] |= gpio_bit(gpionr); - set_gpio_data(gpionr, 0); - - } else { - set_gpio_edge(gpionr, 0); - gpio_edge_triggered[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); - set_gpio_inen(gpionr, 1); - } +} - SSYNC(); +void handle_sec_ssi_fault(uint32_t gstat) +{ + uint32_t sid; + uint32_t sstat; - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) - set_irq_handler(irq, handle_edge_irq); - else - set_irq_handler(irq, handle_level_irq); + sid = gstat & SEC_GSTAT_SID; + sstat = bfin_read_SEC_SSTAT(sid); - return 0; } -#ifdef CONFIG_PM -int bfin_gpio_set_wake(unsigned int irq, unsigned int state) +void handle_sec_fault(uint32_t sec_gstat) { - unsigned gpio = irq_to_gpio(irq); + if (sec_gstat & SEC_GSTAT_ERR) { - if (state) - gpio_pm_wakeup_request(gpio, PM_WAKE_IGNORE); - else - gpio_pm_wakeup_free(gpio); + switch (sec_gstat & SEC_GSTAT_ERRC) { + case 0: + handle_sec_sfi_fault(sec_gstat); + break; + case SEC_GSTAT_SCIERR: + handle_sec_sci_fault(sec_gstat); + break; + case SEC_GSTAT_SSIERR: + handle_sec_ssi_fault(sec_gstat); + break; + } - return 0; + + } } -#endif -static struct irq_chip bfin_gpio_irqchip = { - .ack = bfin_gpio_ack_irq, - .mask = bfin_gpio_mask_irq, - .mask_ack = bfin_gpio_mask_ack_irq, - .unmask = bfin_gpio_unmask_irq, - .set_type = bfin_gpio_irq_type, - .startup = bfin_gpio_irq_startup, - .shutdown = bfin_gpio_irq_shutdown, -#ifdef CONFIG_PM - .set_wake = bfin_gpio_set_wake, -#endif +static struct irqaction bfin_fault_irq = { + .name = "Blackfin fault", }; -static void bfin_demux_gpio_irq(unsigned int inta_irq, - struct irq_desc *desc) +static irqreturn_t bfin_fault_routine(int irq, void *data) { - unsigned int i, gpio, mask, irq, search = 0; + struct pt_regs *fp = get_irq_regs(); - switch (inta_irq) { -#if defined(CONFIG_BF53x) - case IRQ_PROG_INTA: - irq = IRQ_PF0; - search = 1; - break; -# if defined(BF537_FAMILY) && !(defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)) - case IRQ_MAC_RX: - irq = IRQ_PH0; - break; -# endif -#elif defined(CONFIG_BF52x) - case IRQ_PORTF_INTA: - irq = IRQ_PF0; - break; - case IRQ_PORTG_INTA: - irq = IRQ_PG0; - break; - case IRQ_PORTH_INTA: - irq = IRQ_PH0; + switch (irq) { + case IRQ_C0_DBL_FAULT: + double_fault_c(fp); break; -#elif defined(CONFIG_BF561) - case IRQ_PROG0_INTA: - irq = IRQ_PF0; + case IRQ_C0_HW_ERR: + dump_bfin_process(fp); + dump_bfin_mem(fp); + show_regs(fp); + printk(KERN_NOTICE "Kernel Stack\n"); + show_stack(current, NULL); + print_modules(); + panic("Core 0 hardware error"); break; - case IRQ_PROG1_INTA: - irq = IRQ_PF16; + case IRQ_C0_NMI_L1_PARITY_ERR: + panic("Core 0 NMI L1 parity error"); break; - case IRQ_PROG2_INTA: - irq = IRQ_PF32; + case IRQ_SEC_ERR: + pr_err("SEC error\n"); + handle_sec_fault(bfin_read32(SEC_GSTAT)); break; -#endif default: - BUG(); - return; - } - - if (search) { - for (i = 0; i < MAX_BLACKFIN_GPIOS; i += GPIO_BANKSIZE) { - irq += i; - - mask = get_gpiop_data(i) & - (gpio_enabled[gpio_bank(i)] & - get_gpiop_maska(i)); - - while (mask) { - if (mask & 1) { - desc = irq_desc + irq; - desc->handle_irq(irq, desc); - } - irq++; - mask >>= 1; - } - } - } else { - gpio = irq_to_gpio(irq); - mask = get_gpiop_data(gpio) & - (gpio_enabled[gpio_bank(gpio)] & - get_gpiop_maska(gpio)); - - do { - if (mask & 1) { - desc = irq_desc + irq; - desc->handle_irq(irq, desc); - } - irq++; - mask >>= 1; - } while (mask); + panic("Unknown fault %d", irq); } + return IRQ_HANDLED; } +#endif /* SEC_GCTL */ -#else /* CONFIG_BF54x */ - -#define NR_PINT_SYS_IRQS 4 -#define NR_PINT_BITS 32 -#define NR_PINTS 160 -#define IRQ_NOT_AVAIL 0xFF - -#define PINT_2_BANK(x) ((x) >> 5) -#define PINT_2_BIT(x) ((x) & 0x1F) -#define PINT_BIT(x) (1 << (PINT_2_BIT(x))) - -static unsigned char irq2pint_lut[NR_PINTS]; -static unsigned char pint2irq_lut[NR_PINT_SYS_IRQS * NR_PINT_BITS]; - -static unsigned int gpio_both_edge_triggered[NR_PINT_SYS_IRQS]; -static unsigned short gpio_enabled[gpio_bank(MAX_BLACKFIN_GPIOS)]; - - -struct pin_int_t { - unsigned int mask_set; - unsigned int mask_clear; - unsigned int request; - unsigned int assign; - unsigned int edge_set; - unsigned int edge_clear; - unsigned int invert_set; - unsigned int invert_clear; - unsigned int pinstate; - unsigned int latch; +static struct irq_chip bfin_core_irqchip = { + .name = "CORE", + .irq_mask = bfin_core_mask_irq, + .irq_unmask = bfin_core_unmask_irq, }; -static struct pin_int_t *pint[NR_PINT_SYS_IRQS] = { - (struct pin_int_t *)PINT0_MASK_SET, - (struct pin_int_t *)PINT1_MASK_SET, - (struct pin_int_t *)PINT2_MASK_SET, - (struct pin_int_t *)PINT3_MASK_SET, +#ifndef SEC_GCTL +static struct irq_chip bfin_internal_irqchip = { + .name = "INTN", + .irq_mask = bfin_internal_mask_irq_chip, + .irq_unmask = bfin_internal_unmask_irq_chip, + .irq_disable = bfin_internal_mask_irq_chip, + .irq_enable = bfin_internal_unmask_irq_chip, +#ifdef CONFIG_SMP + .irq_set_affinity = bfin_internal_set_affinity, +#endif + .irq_set_wake = bfin_internal_set_wake_chip, }; +#else +static struct irq_chip bfin_sec_irqchip = { + .name = "SEC", + .irq_mask_ack = bfin_sec_mask_ack_irq, + .irq_mask = bfin_sec_mask_ack_irq, + .irq_unmask = bfin_sec_unmask_irq, + .irq_eoi = bfin_sec_unmask_irq, + .irq_disable = bfin_sec_disable, + .irq_enable = bfin_sec_enable, +}; +#endif -extern void bfin_gpio_irq_prepare(unsigned gpio); - -inline unsigned short get_irq_base(u8 bank, u8 bmap) +void bfin_handle_irq(unsigned irq) { +#ifdef CONFIG_IPIPE + struct pt_regs regs; /* Contents not used. */ + ipipe_trace_irq_entry(irq); + __ipipe_handle_irq(irq, ®s); + ipipe_trace_irq_exit(irq); +#else /* !CONFIG_IPIPE */ + generic_handle_irq(irq); +#endif /* !CONFIG_IPIPE */ +} - u16 irq_base; +#if defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE) +static int mac_stat_int_mask; - if (bank < 2) { /*PA-PB */ - irq_base = IRQ_PA0 + bmap * 16; - } else { /*PC-PJ */ - irq_base = IRQ_PC0 + bmap * 16; +static void bfin_mac_status_ack_irq(unsigned int irq) +{ + switch (irq) { + case IRQ_MAC_MMCINT: + bfin_write_EMAC_MMC_TIRQS( + bfin_read_EMAC_MMC_TIRQE() & + bfin_read_EMAC_MMC_TIRQS()); + bfin_write_EMAC_MMC_RIRQS( + bfin_read_EMAC_MMC_RIRQE() & + bfin_read_EMAC_MMC_RIRQS()); + break; + case IRQ_MAC_RXFSINT: + bfin_write_EMAC_RX_STKY( + bfin_read_EMAC_RX_IRQE() & + bfin_read_EMAC_RX_STKY()); + break; + case IRQ_MAC_TXFSINT: + bfin_write_EMAC_TX_STKY( + bfin_read_EMAC_TX_IRQE() & + bfin_read_EMAC_TX_STKY()); + break; + case IRQ_MAC_WAKEDET: + bfin_write_EMAC_WKUP_CTL( + bfin_read_EMAC_WKUP_CTL() | MPKS | RWKS); + break; + default: + /* These bits are W1C */ + bfin_write_EMAC_SYSTAT(1L << (irq - IRQ_MAC_PHYINT)); + break; } - - return irq_base; - } - /* Whenever PINTx_ASSIGN is altered init_pint_lut() must be executed! */ -void init_pint_lut(void) +static void bfin_mac_status_mask_irq(struct irq_data *d) { - u16 bank, bit, irq_base, bit_pos; - u32 pint_assign; - u8 bmap; - - memset(irq2pint_lut, IRQ_NOT_AVAIL, sizeof(irq2pint_lut)); + unsigned int irq = d->irq; - for (bank = 0; bank < NR_PINT_SYS_IRQS; bank++) { - - pint_assign = pint[bank]->assign; + mac_stat_int_mask &= ~(1L << (irq - IRQ_MAC_PHYINT)); +#ifdef BF537_FAMILY + switch (irq) { + case IRQ_MAC_PHYINT: + bfin_write_EMAC_SYSCTL(bfin_read_EMAC_SYSCTL() & ~PHYIE); + break; + default: + break; + } +#else + if (!mac_stat_int_mask) + bfin_internal_mask_irq(IRQ_MAC_ERROR); +#endif + bfin_mac_status_ack_irq(irq); +} - for (bit = 0; bit < NR_PINT_BITS; bit++) { +static void bfin_mac_status_unmask_irq(struct irq_data *d) +{ + unsigned int irq = d->irq; - bmap = (pint_assign >> ((bit / 8) * 8)) & 0xFF; +#ifdef BF537_FAMILY + switch (irq) { + case IRQ_MAC_PHYINT: + bfin_write_EMAC_SYSCTL(bfin_read_EMAC_SYSCTL() | PHYIE); + break; + default: + break; + } +#else + if (!mac_stat_int_mask) + bfin_internal_unmask_irq(IRQ_MAC_ERROR); +#endif + mac_stat_int_mask |= 1L << (irq - IRQ_MAC_PHYINT); +} - irq_base = get_irq_base(bank, bmap); +#ifdef CONFIG_PM +int bfin_mac_status_set_wake(struct irq_data *d, unsigned int state) +{ +#ifdef BF537_FAMILY + return bfin_internal_set_wake(IRQ_GENERIC_ERROR, state); +#else + return bfin_internal_set_wake(IRQ_MAC_ERROR, state); +#endif +} +#else +# define bfin_mac_status_set_wake NULL +#endif - irq_base += (bit % 8) + ((bit / 8) & 1 ? 8 : 0); - bit_pos = bit + bank * NR_PINT_BITS; +static struct irq_chip bfin_mac_status_irqchip = { + .name = "MACST", + .irq_mask = bfin_mac_status_mask_irq, + .irq_unmask = bfin_mac_status_unmask_irq, + .irq_set_wake = bfin_mac_status_set_wake, +}; - pint2irq_lut[bit_pos] = irq_base - SYS_IRQS; - irq2pint_lut[irq_base - SYS_IRQS] = bit_pos; +void bfin_demux_mac_status_irq(unsigned int int_err_irq, + struct irq_desc *inta_desc) +{ + int i, irq = 0; + u32 status = bfin_read_EMAC_SYSTAT(); + for (i = 0; i <= (IRQ_MAC_STMDONE - IRQ_MAC_PHYINT); i++) + if (status & (1L << i)) { + irq = IRQ_MAC_PHYINT + i; + break; } - } - + if (irq) { + if (mac_stat_int_mask & (1L << (irq - IRQ_MAC_PHYINT))) { + bfin_handle_irq(irq); + } else { + bfin_mac_status_ack_irq(irq); + pr_debug("IRQ %d:" + " MASKED MAC ERROR INTERRUPT ASSERTED\n", + irq); + } + } else + printk(KERN_ERR + "%s : %s : LINE %d :\nIRQ ?: MAC ERROR" + " INTERRUPT ASSERTED BUT NO SOURCE FOUND" + "(EMAC_SYSTAT=0x%X)\n", + __func__, __FILE__, __LINE__, status); } +#endif -static void bfin_gpio_ack_irq(unsigned int irq) +static inline void bfin_set_irq_handler(unsigned irq, irq_flow_handler_t handle) { - u8 pint_val = irq2pint_lut[irq - SYS_IRQS]; - u32 pintbit = PINT_BIT(pint_val); - u8 bank = PINT_2_BANK(pint_val); +#ifdef CONFIG_IPIPE + handle = handle_level_irq; +#endif + __irq_set_handler_locked(irq, handle); +} - if (unlikely(gpio_both_edge_triggered[bank] & pintbit)) { - if (pint[bank]->invert_set & pintbit) - pint[bank]->invert_clear = pintbit; - else - pint[bank]->invert_set = pintbit; - } - pint[bank]->request = pintbit; +#ifdef CONFIG_GPIO_ADI + +static DECLARE_BITMAP(gpio_enabled, MAX_BLACKFIN_GPIOS); - SSYNC(); +static void bfin_gpio_ack_irq(struct irq_data *d) +{ + /* AFAIK ack_irq in case mask_ack is provided + * get's only called for edge sense irqs + */ + set_gpio_data(irq_to_gpio(d->irq), 0); } -static void bfin_gpio_mask_ack_irq(unsigned int irq) +static void bfin_gpio_mask_ack_irq(struct irq_data *d) { - u8 pint_val = irq2pint_lut[irq - SYS_IRQS]; - u32 pintbit = PINT_BIT(pint_val); - u8 bank = PINT_2_BANK(pint_val); + unsigned int irq = d->irq; + u32 gpionr = irq_to_gpio(irq); - if (unlikely(gpio_both_edge_triggered[bank] & pintbit)) { - if (pint[bank]->invert_set & pintbit) - pint[bank]->invert_clear = pintbit; - else - pint[bank]->invert_set = pintbit; - } + if (!irqd_is_level_type(d)) + set_gpio_data(gpionr, 0); - pint[bank]->request = pintbit; - pint[bank]->mask_clear = pintbit; - SSYNC(); + set_gpio_maska(gpionr, 0); } -static void bfin_gpio_mask_irq(unsigned int irq) +static void bfin_gpio_mask_irq(struct irq_data *d) { - u8 pint_val = irq2pint_lut[irq - SYS_IRQS]; - - pint[PINT_2_BANK(pint_val)]->mask_clear = PINT_BIT(pint_val); - SSYNC(); + set_gpio_maska(irq_to_gpio(d->irq), 0); } -static void bfin_gpio_unmask_irq(unsigned int irq) +static void bfin_gpio_unmask_irq(struct irq_data *d) { - u8 pint_val = irq2pint_lut[irq - SYS_IRQS]; - u32 pintbit = PINT_BIT(pint_val); - u8 bank = PINT_2_BANK(pint_val); - - pint[bank]->request = pintbit; - pint[bank]->mask_set = pintbit; - SSYNC(); + set_gpio_maska(irq_to_gpio(d->irq), 1); } -static unsigned int bfin_gpio_irq_startup(unsigned int irq) +static unsigned int bfin_gpio_irq_startup(struct irq_data *d) { - u16 gpionr = irq_to_gpio(irq); - u8 pint_val = irq2pint_lut[irq - SYS_IRQS]; + u32 gpionr = irq_to_gpio(d->irq); - if (pint_val == IRQ_NOT_AVAIL) { - printk(KERN_ERR - "GPIO IRQ %d :Not in PINT Assign table " - "Reconfigure Interrupt to Port Assignemt\n", irq); - return -ENODEV; - } - - if (!(gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))) + if (__test_and_set_bit(gpionr, gpio_enabled)) bfin_gpio_irq_prepare(gpionr); - gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); - bfin_gpio_unmask_irq(irq); + bfin_gpio_unmask_irq(d); return 0; } -static void bfin_gpio_irq_shutdown(unsigned int irq) +static void bfin_gpio_irq_shutdown(struct irq_data *d) { - u16 gpionr = irq_to_gpio(irq); + u32 gpionr = irq_to_gpio(d->irq); - bfin_gpio_mask_irq(irq); - gpio_enabled[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); + bfin_gpio_mask_irq(d); + __clear_bit(gpionr, gpio_enabled); + bfin_gpio_irq_free(gpionr); } -static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) +static int bfin_gpio_irq_type(struct irq_data *d, unsigned int type) { - - u16 gpionr = irq_to_gpio(irq); - u8 pint_val = irq2pint_lut[irq - SYS_IRQS]; - u32 pintbit = PINT_BIT(pint_val); - u8 bank = PINT_2_BANK(pint_val); - - if (pint_val == IRQ_NOT_AVAIL) - return -ENODEV; + unsigned int irq = d->irq; + int ret; + char buf[16]; + u32 gpionr = irq_to_gpio(irq); if (type == IRQ_TYPE_PROBE) { /* only probe unenabled GPIO interrupt lines */ - if (gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr)) + if (test_bit(gpionr, gpio_enabled)) return 0; type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; } if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { - if (!(gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))) + + snprintf(buf, 16, "gpio-irq%d", irq); + ret = bfin_gpio_irq_request(gpionr, buf); + if (ret) + return ret; + + if (__test_and_set_bit(gpionr, gpio_enabled)) bfin_gpio_irq_prepare(gpionr); - gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); } else { - gpio_enabled[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); + __clear_bit(gpionr, gpio_enabled); return 0; } - if ((type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW))) - pint[bank]->invert_set = pintbit; /* low or falling edge denoted by one */ - else - pint[bank]->invert_clear = pintbit; /* high or rising edge denoted by zero */ + set_gpio_inen(gpionr, 0); + set_gpio_dir(gpionr, 0); if ((type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) - == (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { - - gpio_both_edge_triggered[bank] |= pintbit; + == (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) + set_gpio_both(gpionr, 1); + else + set_gpio_both(gpionr, 0); - if (gpio_get_value(gpionr)) - pint[bank]->invert_set = pintbit; - else - pint[bank]->invert_clear = pintbit; - } else { - gpio_both_edge_triggered[bank] &= ~pintbit; - } + if ((type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW))) + set_gpio_polar(gpionr, 1); /* low or falling edge denoted by one */ + else + set_gpio_polar(gpionr, 0); /* high or rising edge denoted by zero */ if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { - pint[bank]->edge_set = pintbit; - set_irq_handler(irq, handle_edge_irq); + set_gpio_edge(gpionr, 1); + set_gpio_inen(gpionr, 1); + set_gpio_data(gpionr, 0); + } else { - pint[bank]->edge_clear = pintbit; - set_irq_handler(irq, handle_level_irq); + set_gpio_edge(gpionr, 0); + set_gpio_inen(gpionr, 1); } - SSYNC(); + if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) + bfin_set_irq_handler(irq, handle_edge_irq); + else + bfin_set_irq_handler(irq, handle_level_irq); return 0; } -#ifdef CONFIG_PM -u32 pint_saved_masks[NR_PINT_SYS_IRQS]; -u32 pint_wakeup_masks[NR_PINT_SYS_IRQS]; +static void bfin_demux_gpio_block(unsigned int irq) +{ + unsigned int gpio, mask; + + gpio = irq_to_gpio(irq); + mask = get_gpiop_data(gpio) & get_gpiop_maska(gpio); -int bfin_gpio_set_wake(unsigned int irq, unsigned int state) + while (mask) { + if (mask & 1) + bfin_handle_irq(irq); + irq++; + mask >>= 1; + } +} + +void bfin_demux_gpio_irq(unsigned int inta_irq, + struct irq_desc *desc) { - u32 pint_irq; - u8 pint_val = irq2pint_lut[irq - SYS_IRQS]; - u32 bank = PINT_2_BANK(pint_val); - u32 pintbit = PINT_BIT(pint_val); - - switch (bank) { - case 0: - pint_irq = IRQ_PINT0; + unsigned int irq; + + switch (inta_irq) { +#if defined(BF537_FAMILY) + case IRQ_PF_INTA_PG_INTA: + bfin_demux_gpio_block(IRQ_PF0); + irq = IRQ_PG0; + break; + case IRQ_PH_INTA_MAC_RX: + irq = IRQ_PH0; + break; +#elif defined(BF533_FAMILY) + case IRQ_PROG_INTA: + irq = IRQ_PF0; + break; +#elif defined(BF538_FAMILY) + case IRQ_PORTF_INTA: + irq = IRQ_PF0; + break; +#elif defined(CONFIG_BF52x) || defined(CONFIG_BF51x) + case IRQ_PORTF_INTA: + irq = IRQ_PF0; break; - case 2: - pint_irq = IRQ_PINT2; + case IRQ_PORTG_INTA: + irq = IRQ_PG0; break; - case 3: - pint_irq = IRQ_PINT3; + case IRQ_PORTH_INTA: + irq = IRQ_PH0; break; - case 1: - pint_irq = IRQ_PINT1; +#elif defined(CONFIG_BF561) + case IRQ_PROG0_INTA: + irq = IRQ_PF0; break; + case IRQ_PROG1_INTA: + irq = IRQ_PF16; + break; + case IRQ_PROG2_INTA: + irq = IRQ_PF32; + break; +#endif default: - return -EINVAL; + BUG(); + return; } - bfin_internal_set_wake(pint_irq, state); - - if (state) - pint_wakeup_masks[bank] |= pintbit; - else - pint_wakeup_masks[bank] &= ~pintbit; - - return 0; + bfin_demux_gpio_block(irq); } -u32 bfin_pm_setup(void) -{ - u32 val, i; - - for (i = 0; i < NR_PINT_SYS_IRQS; i++) { - val = pint[i]->mask_clear; - pint_saved_masks[i] = val; - if (val ^ pint_wakeup_masks[i]) { - pint[i]->mask_clear = val; - pint[i]->mask_set = pint_wakeup_masks[i]; - } - } +#ifdef CONFIG_PM - return 0; +static int bfin_gpio_set_wake(struct irq_data *d, unsigned int state) +{ + return bfin_gpio_pm_wakeup_ctrl(irq_to_gpio(d->irq), state); } -void bfin_pm_restore(void) -{ - u32 i, val; +#else + +# define bfin_gpio_set_wake NULL - for (i = 0; i < NR_PINT_SYS_IRQS; i++) { - val = pint_saved_masks[i]; - if (val ^ pint_wakeup_masks[i]) { - pint[i]->mask_clear = pint[i]->mask_clear; - pint[i]->mask_set = val; - } - } -} #endif static struct irq_chip bfin_gpio_irqchip = { - .ack = bfin_gpio_ack_irq, - .mask = bfin_gpio_mask_irq, - .mask_ack = bfin_gpio_mask_ack_irq, - .unmask = bfin_gpio_unmask_irq, - .set_type = bfin_gpio_irq_type, - .startup = bfin_gpio_irq_startup, - .shutdown = bfin_gpio_irq_shutdown, -#ifdef CONFIG_PM - .set_wake = bfin_gpio_set_wake, -#endif + .name = "GPIO", + .irq_ack = bfin_gpio_ack_irq, + .irq_mask = bfin_gpio_mask_irq, + .irq_mask_ack = bfin_gpio_mask_ack_irq, + .irq_unmask = bfin_gpio_unmask_irq, + .irq_disable = bfin_gpio_mask_irq, + .irq_enable = bfin_gpio_unmask_irq, + .irq_set_type = bfin_gpio_irq_type, + .irq_startup = bfin_gpio_irq_startup, + .irq_shutdown = bfin_gpio_irq_shutdown, + .irq_set_wake = bfin_gpio_set_wake, }; -static void bfin_demux_gpio_irq(unsigned int inta_irq, - struct irq_desc *desc) -{ - u8 bank, pint_val; - u32 request, irq; +#endif - switch (inta_irq) { - case IRQ_PINT0: - bank = 0; - break; - case IRQ_PINT2: - bank = 2; - break; - case IRQ_PINT3: - bank = 3; - break; - case IRQ_PINT1: - bank = 1; - break; - default: - return; - } +#ifdef CONFIG_PM - pint_val = bank * NR_PINT_BITS; +#ifdef SEC_GCTL +static u32 save_pint_sec_ctl[NR_PINT_SYS_IRQS]; - request = pint[bank]->request; +static int sec_suspend(void) +{ + u32 bank; - while (request) { - if (request & 1) { - irq = pint2irq_lut[pint_val] + SYS_IRQS; - desc = irq_desc + irq; - desc->handle_irq(irq, desc); - } - pint_val++; - request >>= 1; - } + for (bank = 0; bank < NR_PINT_SYS_IRQS; bank++) + save_pint_sec_ctl[bank] = bfin_read_SEC_SCTL(bank + BFIN_SYSIRQ(IRQ_PINT0)); + return 0; +} + +static void sec_resume(void) +{ + u32 bank; + bfin_write_SEC_SCI(0, SEC_CCTL, SEC_CCTL_RESET); + udelay(100); + bfin_write_SEC_GCTL(SEC_GCTL_EN); + bfin_write_SEC_SCI(0, SEC_CCTL, SEC_CCTL_EN | SEC_CCTL_NMI_EN); + + for (bank = 0; bank < NR_PINT_SYS_IRQS; bank++) + bfin_write_SEC_SCTL(bank + BFIN_SYSIRQ(IRQ_PINT0), save_pint_sec_ctl[bank]); } + +static struct syscore_ops sec_pm_syscore_ops = { + .suspend = sec_suspend, + .resume = sec_resume, +}; #endif -void __init init_exception_vectors(void) -{ - SSYNC(); +#endif +void init_exception_vectors(void) +{ /* cannot program in software: * evt0 - emulation (jtag) * evt1 - reset @@ -913,61 +964,53 @@ void __init init_exception_vectors(void) bfin_write_EVT11(evt_evt11); bfin_write_EVT12(evt_evt12); bfin_write_EVT13(evt_evt13); - bfin_write_EVT14(evt14_softirq); + bfin_write_EVT14(evt_evt14); bfin_write_EVT15(evt_system_call); CSYNC(); } +#ifndef SEC_GCTL /* * This function should be called during kernel startup to initialize * the BFin IRQ handling routines. */ + int __init init_arch_irq(void) { int irq; unsigned long ilat = 0; + /* Disable all the peripheral intrs - page 4-29 HW Ref manual */ -#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) +#ifdef SIC_IMASK0 bfin_write_SIC_IMASK0(SIC_UNMASK_ALL); bfin_write_SIC_IMASK1(SIC_UNMASK_ALL); -# ifdef CONFIG_BF54x +# ifdef SIC_IMASK2 bfin_write_SIC_IMASK2(SIC_UNMASK_ALL); # endif +# if defined(CONFIG_SMP) || defined(CONFIG_ICC) + bfin_write_SICB_IMASK0(SIC_UNMASK_ALL); + bfin_write_SICB_IMASK1(SIC_UNMASK_ALL); +# endif #else bfin_write_SIC_IMASK(SIC_UNMASK_ALL); #endif local_irq_disable(); -#ifdef CONFIG_BF54x -# ifdef CONFIG_PINTx_REASSIGN - pint[0]->assign = CONFIG_PINT0_ASSIGN; - pint[1]->assign = CONFIG_PINT1_ASSIGN; - pint[2]->assign = CONFIG_PINT2_ASSIGN; - pint[3]->assign = CONFIG_PINT3_ASSIGN; -# endif - /* Whenever PINTx_ASSIGN is altered init_pint_lut() must be executed! */ - init_pint_lut(); -#endif - for (irq = 0; irq <= SYS_IRQS; irq++) { if (irq <= IRQ_CORETMR) - set_irq_chip(irq, &bfin_core_irqchip); + irq_set_chip(irq, &bfin_core_irqchip); else - set_irq_chip(irq, &bfin_internal_irqchip); + irq_set_chip(irq, &bfin_internal_irqchip); switch (irq) { -#if defined(CONFIG_BF53x) +#if !BFIN_GPIO_PINT +#if defined(BF537_FAMILY) + case IRQ_PH_INTA_MAC_RX: + case IRQ_PF_INTA_PG_INTA: +#elif defined(BF533_FAMILY) case IRQ_PROG_INTA: -# if defined(BF537_FAMILY) && !(defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)) - case IRQ_MAC_RX: -# endif -#elif defined(CONFIG_BF54x) - case IRQ_PINT0: - case IRQ_PINT1: - case IRQ_PINT2: - case IRQ_PINT3: -#elif defined(CONFIG_BF52x) +#elif defined(CONFIG_BF52x) || defined(CONFIG_BF51x) case IRQ_PORTF_INTA: case IRQ_PORTG_INTA: case IRQ_PORTH_INTA: @@ -975,34 +1018,65 @@ int __init init_arch_irq(void) case IRQ_PROG0_INTA: case IRQ_PROG1_INTA: case IRQ_PROG2_INTA: +#elif defined(BF538_FAMILY) + case IRQ_PORTF_INTA: +#endif + irq_set_chained_handler(irq, bfin_demux_gpio_irq); + break; #endif - set_irq_chained_handler(irq, - bfin_demux_gpio_irq); +#if defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE) + case IRQ_MAC_ERROR: + irq_set_chained_handler(irq, + bfin_demux_mac_status_irq); break; -#ifdef BF537_GENERIC_ERROR_INT_DEMUX - case IRQ_GENERIC_ERROR: - set_irq_handler(irq, bfin_demux_error_irq); +#endif +#if defined(CONFIG_SMP) || defined(CONFIG_ICC) + case IRQ_SUPPLE_0: + case IRQ_SUPPLE_1: + irq_set_handler(irq, handle_percpu_irq); + break; +#endif +#ifdef CONFIG_TICKSOURCE_CORETMR + case IRQ_CORETMR: +# ifdef CONFIG_SMP + irq_set_handler(irq, handle_percpu_irq); +# else + irq_set_handler(irq, handle_simple_irq); +# endif break; #endif + +#ifdef CONFIG_TICKSOURCE_GPTMR0 + case IRQ_TIMER0: + irq_set_handler(irq, handle_simple_irq); + break; +#endif + default: - set_irq_handler(irq, handle_simple_irq); +#ifdef CONFIG_IPIPE + irq_set_handler(irq, handle_level_irq); +#else + irq_set_handler(irq, handle_simple_irq); +#endif break; } } -#ifdef BF537_GENERIC_ERROR_INT_DEMUX - for (irq = IRQ_PPI_ERROR; irq <= IRQ_UART1_ERROR; irq++) - set_irq_chip_and_handler(irq, &bfin_generic_error_irqchip, + init_mach_irq(); + +#if (defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)) + for (irq = IRQ_MAC_PHYINT; irq <= IRQ_MAC_STMDONE; irq++) + irq_set_chip_and_handler(irq, &bfin_mac_status_irqchip, handle_level_irq); #endif - /* if configured as edge, then will be changed to do_edge_IRQ */ - for (irq = GPIO_IRQ_BASE; irq < NR_IRQS; irq++) - set_irq_chip_and_handler(irq, &bfin_gpio_irqchip, +#ifdef CONFIG_GPIO_ADI + for (irq = GPIO_IRQ_BASE; + irq < (GPIO_IRQ_BASE + MAX_BLACKFIN_GPIOS); irq++) + irq_set_chip_and_handler(irq, &bfin_gpio_irqchip, handle_level_irq); - - +#endif bfin_write_IMASK(0); CSYNC(); ilat = bfin_read_ILAT(); @@ -1011,7 +1085,7 @@ int __init init_arch_irq(void) CSYNC(); printk(KERN_INFO "Configuring Blackfin Priority Driven Interrupts\n"); - /* IMASK=xxx is equivalent to STI xx or irq_flags=xx, + /* IMASK=xxx is equivalent to STI xx or bfin_irq_flags=xx, * local_irq_enable() */ program_IAR(); @@ -1019,67 +1093,281 @@ int __init init_arch_irq(void) search_IAR(); /* Enable interrupts IVG7-15 */ - irq_flags = irq_flags | IMASK_IVG15 | - IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 | - IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW; + bfin_irq_flags |= IMASK_IVG15 | + IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 | + IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW; + -#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) - bfin_write_SIC_IWR0(IWR_ENABLE_ALL); - bfin_write_SIC_IWR1(IWR_ENABLE_ALL); -# ifdef CONFIG_BF54x - bfin_write_SIC_IWR2(IWR_ENABLE_ALL); + /* This implicitly covers ANOMALY_05000171 + * Boot-ROM code modifies SICA_IWRx wakeup registers + */ +#ifdef SIC_IWR0 + bfin_write_SIC_IWR0(IWR_DISABLE_ALL); +# ifdef SIC_IWR1 + /* BF52x/BF51x system reset does not properly reset SIC_IWR1 which + * will screw up the bootrom as it relies on MDMA0/1 waking it + * up from IDLE instructions. See this report for more info: + * http://blackfin.uclinux.org/gf/tracker/4323 + */ + if (ANOMALY_05000435) + bfin_write_SIC_IWR1(IWR_ENABLE(10) | IWR_ENABLE(11)); + else + bfin_write_SIC_IWR1(IWR_DISABLE_ALL); +# endif +# ifdef SIC_IWR2 + bfin_write_SIC_IWR2(IWR_DISABLE_ALL); # endif #else - bfin_write_SIC_IWR(IWR_ENABLE_ALL); + bfin_write_SIC_IWR(IWR_DISABLE_ALL); #endif - return 0; } #ifdef CONFIG_DO_IRQ_L1 __attribute__((l1_text)) #endif -void do_irq(int vec, struct pt_regs *fp) +static int vec_to_irq(int vec) { - if (vec == EVT_IVTMR_P) { - vec = IRQ_CORETMR; + struct ivgx *ivg = ivg7_13[vec - IVG7].ifirst; + struct ivgx *ivg_stop = ivg7_13[vec - IVG7].istop; + unsigned long sic_status[3]; + if (likely(vec == EVT_IVTMR_P)) + return IRQ_CORETMR; +#ifdef SIC_ISR + sic_status[0] = bfin_read_SIC_IMASK() & bfin_read_SIC_ISR(); +#else + if (smp_processor_id()) { +# ifdef SICB_ISR0 + /* This will be optimized out in UP mode. */ + sic_status[0] = bfin_read_SICB_ISR0() & bfin_read_SICB_IMASK0(); + sic_status[1] = bfin_read_SICB_ISR1() & bfin_read_SICB_IMASK1(); +# endif } else { - struct ivgx *ivg = ivg7_13[vec - IVG7].ifirst; - struct ivgx *ivg_stop = ivg7_13[vec - IVG7].istop; -#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) - unsigned long sic_status[3]; - sic_status[0] = bfin_read_SIC_ISR0() & bfin_read_SIC_IMASK0(); sic_status[1] = bfin_read_SIC_ISR1() & bfin_read_SIC_IMASK1(); -#ifdef CONFIG_BF54x - sic_status[2] = bfin_read_SIC_ISR2() & bfin_read_SIC_IMASK2(); + } #endif - for (;; ivg++) { - if (ivg >= ivg_stop) { - atomic_inc(&num_spurious); - return; - } - if (sic_status[(ivg->irqno - IVG7) / 32] & ivg->isrflag) - break; - } +#ifdef SIC_ISR2 + sic_status[2] = bfin_read_SIC_ISR2() & bfin_read_SIC_IMASK2(); +#endif + + for (;; ivg++) { + if (ivg >= ivg_stop) + return -1; +#ifdef SIC_ISR + if (sic_status[0] & ivg->isrflag) #else - unsigned long sic_status; + if (sic_status[(ivg->irqno - IVG7) / 32] & ivg->isrflag) +#endif + return ivg->irqno; + } +} + +#else /* SEC_GCTL */ + +/* + * This function should be called during kernel startup to initialize + * the BFin IRQ handling routines. + */ + +int __init init_arch_irq(void) +{ + int irq; + unsigned long ilat = 0; + + bfin_write_SEC_GCTL(SEC_GCTL_RESET); - sic_status = bfin_read_SIC_IMASK() & bfin_read_SIC_ISR(); + local_irq_disable(); - for (;; ivg++) { - if (ivg >= ivg_stop) { - atomic_inc(&num_spurious); - return; - } else if (sic_status & ivg->isrflag) - break; + for (irq = 0; irq <= SYS_IRQS; irq++) { + if (irq <= IRQ_CORETMR) { + irq_set_chip_and_handler(irq, &bfin_core_irqchip, + handle_simple_irq); +#if defined(CONFIG_TICKSOURCE_CORETMR) && defined(CONFIG_SMP) + if (irq == IRQ_CORETMR) + irq_set_handler(irq, handle_percpu_irq); +#endif + } else if (irq >= BFIN_IRQ(34) && irq <= BFIN_IRQ(37)) { + irq_set_chip_and_handler(irq, &bfin_sec_irqchip, + handle_percpu_irq); + } else { + irq_set_chip(irq, &bfin_sec_irqchip); + irq_set_handler(irq, handle_fasteoi_irq); + __irq_set_preflow_handler(irq, bfin_sec_preflow_handler); } + } + + bfin_write_IMASK(0); + CSYNC(); + ilat = bfin_read_ILAT(); + CSYNC(); + bfin_write_ILAT(ilat); + CSYNC(); + + printk(KERN_INFO "Configuring Blackfin Priority Driven Interrupts\n"); + + bfin_sec_set_priority(CONFIG_SEC_IRQ_PRIORITY_LEVELS, sec_int_priority); + + /* Enable interrupts IVG7-15 */ + bfin_irq_flags |= IMASK_IVG15 | + IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 | + IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW; + + + bfin_write_SEC_FCTL(SEC_FCTL_EN | SEC_FCTL_SYSRST_EN | SEC_FCTL_FLTIN_EN); + bfin_sec_enable_sci(BFIN_SYSIRQ(IRQ_WATCH0)); + bfin_sec_enable_ssi(BFIN_SYSIRQ(IRQ_WATCH0)); + bfin_write_SEC_SCI(0, SEC_CCTL, SEC_CCTL_RESET); + udelay(100); + bfin_write_SEC_GCTL(SEC_GCTL_EN); + bfin_write_SEC_SCI(0, SEC_CCTL, SEC_CCTL_EN | SEC_CCTL_NMI_EN); + bfin_write_SEC_SCI(1, SEC_CCTL, SEC_CCTL_EN | SEC_CCTL_NMI_EN); + + init_software_driven_irq(); + +#ifdef CONFIG_PM + register_syscore_ops(&sec_pm_syscore_ops); +#endif + + bfin_fault_irq.handler = bfin_fault_routine; +#ifdef CONFIG_L1_PARITY_CHECK + setup_irq(IRQ_C0_NMI_L1_PARITY_ERR, &bfin_fault_irq); +#endif + setup_irq(IRQ_C0_DBL_FAULT, &bfin_fault_irq); + setup_irq(IRQ_SEC_ERR, &bfin_fault_irq); + + return 0; +} + +#ifdef CONFIG_DO_IRQ_L1 +__attribute__((l1_text)) +#endif +static int vec_to_irq(int vec) +{ + if (likely(vec == EVT_IVTMR_P)) + return IRQ_CORETMR; + + return BFIN_IRQ(bfin_read_SEC_SCI(0, SEC_CSID)); +} +#endif /* SEC_GCTL */ + +#ifdef CONFIG_DO_IRQ_L1 +__attribute__((l1_text)) #endif - vec = ivg->irqno; +void do_irq(int vec, struct pt_regs *fp) +{ + int irq = vec_to_irq(vec); + if (irq == -1) + return; + asm_do_IRQ(irq, fp); +} + +#ifdef CONFIG_IPIPE + +int __ipipe_get_irq_priority(unsigned irq) +{ + int ient, prio; + + if (irq <= IRQ_CORETMR) + return irq; + +#ifdef SEC_GCTL + if (irq >= BFIN_IRQ(0)) + return IVG11; +#else + for (ient = 0; ient < NR_PERI_INTS; ient++) { + struct ivgx *ivg = ivg_table + ient; + if (ivg->irqno == irq) { + for (prio = 0; prio <= IVG13-IVG7; prio++) { + if (ivg7_13[prio].ifirst <= ivg && + ivg7_13[prio].istop > ivg) + return IVG7 + prio; + } + } } - asm_do_IRQ(vec, fp); +#endif + + return IVG15; +} + +/* Hw interrupts are disabled on entry (check SAVE_CONTEXT). */ +#ifdef CONFIG_DO_IRQ_L1 +__attribute__((l1_text)) +#endif +asmlinkage int __ipipe_grab_irq(int vec, struct pt_regs *regs) +{ + struct ipipe_percpu_domain_data *p = ipipe_root_cpudom_ptr(); + struct ipipe_domain *this_domain = __ipipe_current_domain; + int irq, s = 0; -#ifdef CONFIG_KGDB - kgdb_process_breakpoint(); + irq = vec_to_irq(vec); + if (irq == -1) + return 0; + + if (irq == IRQ_SYSTMR) { +#if !defined(CONFIG_GENERIC_CLOCKEVENTS) || defined(CONFIG_TICKSOURCE_GPTMR0) + bfin_write_TIMER_STATUS(1); /* Latch TIMIL0 */ #endif + /* This is basically what we need from the register frame. */ + __raw_get_cpu_var(__ipipe_tick_regs).ipend = regs->ipend; + __raw_get_cpu_var(__ipipe_tick_regs).pc = regs->pc; + if (this_domain != ipipe_root_domain) + __raw_get_cpu_var(__ipipe_tick_regs).ipend &= ~0x10; + else + __raw_get_cpu_var(__ipipe_tick_regs).ipend |= 0x10; + } + + /* + * We don't want Linux interrupt handlers to run at the + * current core priority level (i.e. < EVT15), since this + * might delay other interrupts handled by a high priority + * domain. Here is what we do instead: + * + * - we raise the SYNCDEFER bit to prevent + * __ipipe_handle_irq() to sync the pipeline for the root + * stage for the incoming interrupt. Upon return, that IRQ is + * pending in the interrupt log. + * + * - we raise the TIF_IRQ_SYNC bit for the current thread, so + * that _schedule_and_signal_from_int will eventually sync the + * pipeline from EVT15. + */ + if (this_domain == ipipe_root_domain) { + s = __test_and_set_bit(IPIPE_SYNCDEFER_FLAG, &p->status); + barrier(); + } + + ipipe_trace_irq_entry(irq); + __ipipe_handle_irq(irq, regs); + ipipe_trace_irq_exit(irq); + + if (user_mode(regs) && + !ipipe_test_foreign_stack() && + (current->ipipe_flags & PF_EVTRET) != 0) { + /* + * Testing for user_regs() does NOT fully eliminate + * foreign stack contexts, because of the forged + * interrupt returns we do through + * __ipipe_call_irqtail. In that case, we might have + * preempted a foreign stack context in a high + * priority domain, with a single interrupt level now + * pending after the irqtail unwinding is done. In + * which case user_mode() is now true, and the event + * gets dispatched spuriously. + */ + current->ipipe_flags &= ~PF_EVTRET; + __ipipe_dispatch_event(IPIPE_EVENT_RETURN, regs); + } + + if (this_domain == ipipe_root_domain) { + set_thread_flag(TIF_IRQ_SYNC); + if (!s) { + __clear_bit(IPIPE_SYNCDEFER_FLAG, &p->status); + return !test_bit(IPIPE_STALL_FLAG, &p->status); + } + } + + return 0; } + +#endif /* CONFIG_IPIPE */ diff --git a/arch/blackfin/mach-common/irqpanic.c b/arch/blackfin/mach-common/irqpanic.c deleted file mode 100644 index 606ded9ff4e..00000000000 --- a/arch/blackfin/mach-common/irqpanic.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * File: arch/blackfin/mach-common/irqpanic.c - * Based on: - * Author: - * - * Created: ? - * Description: panic kernel with dump information - * - * Modified: rgetz - added cache checking code 14Feb06 - * 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 <linux/module.h> -#include <linux/kernel_stat.h> -#include <linux/sched.h> -#include <asm/traps.h> -#include <asm/blackfin.h> - -#include "../oprofile/op_blackfin.h" - -#ifdef CONFIG_DEBUG_ICACHE_CHECK -#define L1_ICACHE_START 0xffa10000 -#define L1_ICACHE_END 0xffa13fff -void irq_panic(int reason, struct pt_regs *regs) __attribute__ ((l1_text)); -#endif - -/* - * irq_panic - calls panic with string setup - */ -asmlinkage void irq_panic(int reason, struct pt_regs *regs) -{ -#ifdef CONFIG_DEBUG_ICACHE_CHECK - unsigned int cmd, tag, ca, cache_hi, cache_lo, *pa; - unsigned short i, j, die; - unsigned int bad[10][6]; - - /* check entire cache for coherency - * Since printk is in cacheable memory, - * don't call it until you have checked everything - */ - - die = 0; - i = 0; - - /* check icache */ - - for (ca = L1_ICACHE_START; ca <= L1_ICACHE_END && i < 10; ca += 32) { - - /* Grab various address bits for the itest_cmd fields */ - cmd = (((ca & 0x3000) << 4) | /* ca[13:12] for SBNK[1:0] */ - ((ca & 0x0c00) << 16) | /* ca[11:10] for WAYSEL[1:0] */ - ((ca & 0x3f8)) | /* ca[09:03] for SET[4:0] and DW[1:0] */ - 0); /* Access Tag, Read access */ - - SSYNC(); - bfin_write_ITEST_COMMAND(cmd); - SSYNC(); - tag = bfin_read_ITEST_DATA0(); - SSYNC(); - - /* if tag is marked as valid, check it */ - if (tag & 1) { - /* The icache is arranged in 4 groups of 64-bits */ - for (j = 0; j < 32; j += 8) { - cmd = ((((ca + j) & 0x3000) << 4) | /* ca[13:12] for SBNK[1:0] */ - (((ca + j) & 0x0c00) << 16) | /* ca[11:10] for WAYSEL[1:0] */ - (((ca + j) & 0x3f8)) | /* ca[09:03] for SET[4:0] and DW[1:0] */ - 4); /* Access Data, Read access */ - - SSYNC(); - bfin_write_ITEST_COMMAND(cmd); - SSYNC(); - - cache_hi = bfin_read_ITEST_DATA1(); - cache_lo = bfin_read_ITEST_DATA0(); - - pa = ((unsigned int *)((tag & 0xffffcc00) | - ((ca + j) & ~(0xffffcc00)))); - - /* - * Debugging this, enable - * - * printk("addr: %08x %08x%08x | %08x%08x\n", - * ((unsigned int *)((tag & 0xffffcc00) | ((ca+j) & ~(0xffffcc00)))), - * cache_hi, cache_lo, *(pa+1), *pa); - */ - - if (cache_hi != *(pa + 1) || cache_lo != *pa) { - /* Since icache is not working, stay out of it, by not printing */ - die = 1; - bad[i][0] = (ca + j); - bad[i][1] = cache_hi; - bad[i][2] = cache_lo; - bad[i][3] = ((tag & 0xffffcc00) | - ((ca + j) & ~(0xffffcc00))); - bad[i][4] = *(pa + 1); - bad[i][5] = *(pa); - i++; - } - } - } - } - if (die) { - printk(KERN_EMERG "icache coherency error\n"); - for (j = 0; j <= i; j++) { - printk(KERN_EMERG - "cache address : %08x cache value : %08x%08x\n", - bad[j][0], bad[j][1], bad[j][2]); - printk(KERN_EMERG - "physical address: %08x SDRAM value : %08x%08x\n", - bad[j][3], bad[j][4], bad[j][5]); - } - panic("icache coherency error"); - } else { - printk(KERN_EMERG "icache checked, and OK\n"); - } -#endif - -} - -#ifdef CONFIG_HARDWARE_PM -/* - * call the handler of Performance overflow - */ -asmlinkage void pm_overflow(int irq, struct pt_regs *regs) -{ - pm_overflow_handler(irq, regs); -} -#endif diff --git a/arch/blackfin/mach-common/lock.S b/arch/blackfin/mach-common/lock.S deleted file mode 100644 index 30b887e67dd..00000000000 --- a/arch/blackfin/mach-common/lock.S +++ /dev/null @@ -1,206 +0,0 @@ -/* - * File: arch/blackfin/mach-common/lock.S - * Based on: - * Author: LG Soft India - * - * Created: ? - * Description: kernel locks - * - * 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 <linux/linkage.h> -#include <asm/cplb.h> -#include <asm/blackfin.h> - -.text - -#ifdef CONFIG_BFIN_ICACHE_LOCK - -/* When you come here, it is assumed that - * R0 - Which way to be locked - */ - -ENTRY(_cache_grab_lock) - - [--SP]=( R7:0,P5:0 ); - - P1.H = HI(IMEM_CONTROL); - P1.L = LO(IMEM_CONTROL); - P5.H = HI(ICPLB_ADDR0); - P5.L = LO(ICPLB_ADDR0); - P4.H = HI(ICPLB_DATA0); - P4.L = LO(ICPLB_DATA0); - R7 = R0; - - /* If the code of interest already resides in the cache - * invalidate the entire cache itself. - * invalidate_entire_icache; - */ - - SP += -12; - [--SP] = RETS; - CALL _invalidate_entire_icache; - RETS = [SP++]; - SP += 12; - - /* Disable the Interrupts*/ - - CLI R3; - -.LLOCK_WAY: - - /* Way0 - 0xFFA133E0 - * Way1 - 0xFFA137E0 - * Way2 - 0xFFA13BE0 Total Way Size = 4K - * Way3 - 0xFFA13FE0 - */ - - /* Procedure Ex. -Set the locks for other ways by setting ILOC[3:1] - * Only Way0 of the instruction cache can now be - * replaced by a new code - */ - - R5 = R7; - CC = BITTST(R7,0); - IF CC JUMP .LCLEAR1; - R7 = 0; - BITSET(R7,0); - JUMP .LDONE1; - -.LCLEAR1: - R7 = 0; - BITCLR(R7,0); -.LDONE1: R4 = R7 << 3; - R7 = [P1]; - R7 = R7 | R4; - SSYNC; /* SSYNC required writing to IMEM_CONTROL. */ - .align 8; - [P1] = R7; - SSYNC; - - R7 = R5; - CC = BITTST(R7,1); - IF CC JUMP .LCLEAR2; - R7 = 0; - BITSET(R7,1); - JUMP .LDONE2; - -.LCLEAR2: - R7 = 0; - BITCLR(R7,1); -.LDONE2: R4 = R7 << 3; - R7 = [P1]; - R7 = R7 | R4; - SSYNC; /* SSYNC required writing to IMEM_CONTROL. */ - .align 8; - [P1] = R7; - SSYNC; - - R7 = R5; - CC = BITTST(R7,2); - IF CC JUMP .LCLEAR3; - R7 = 0; - BITSET(R7,2); - JUMP .LDONE3; -.LCLEAR3: - R7 = 0; - BITCLR(R7,2); -.LDONE3: R4 = R7 << 3; - R7 = [P1]; - R7 = R7 | R4; - SSYNC; /* SSYNC required writing to IMEM_CONTROL. */ - .align 8; - [P1] = R7; - SSYNC; - - - R7 = R5; - CC = BITTST(R7,3); - IF CC JUMP .LCLEAR4; - R7 = 0; - BITSET(R7,3); - JUMP .LDONE4; -.LCLEAR4: - R7 = 0; - BITCLR(R7,3); -.LDONE4: R4 = R7 << 3; - R7 = [P1]; - R7 = R7 | R4; - SSYNC; /* SSYNC required writing to IMEM_CONTROL. */ - .align 8; - [P1] = R7; - SSYNC; - - STI R3; - - ( R7:0,P5:0 ) = [SP++]; - - RTS; -ENDPROC(_cache_grab_lock) - -/* After the execution of critical code, the code is now locked into - * the cache way. Now we need to set ILOC. - * - * R0 - Which way to be locked - */ - -ENTRY(_cache_lock) - - [--SP]=( R7:0,P5:0 ); - - P1.H = HI(IMEM_CONTROL); - P1.L = LO(IMEM_CONTROL); - - /* Disable the Interrupts*/ - CLI R3; - - R7 = [P1]; - R2 = ~(0x78) (X); /* mask out ILOC */ - R7 = R7 & R2; - R0 = R0 << 3; - R7 = R0 | R7; - SSYNC; /* SSYNC required writing to IMEM_CONTROL. */ - .align 8; - [P1] = R7; - SSYNC; - /* Renable the Interrupts */ - STI R3; - - ( R7:0,P5:0 ) = [SP++]; - RTS; -ENDPROC(_cache_lock) - -#endif /* BFIN_ICACHE_LOCK */ - -/* Return the ILOC bits of IMEM_CONTROL - */ - -ENTRY(_read_iloc) - P1.H = HI(IMEM_CONTROL); - P1.L = LO(IMEM_CONTROL); - R1 = 0xF; - R0 = [P1]; - R0 = R0 >> 3; - R0 = R0 & R1; - - RTS; -ENDPROC(_read_iloc) diff --git a/arch/blackfin/mach-common/pm.c b/arch/blackfin/mach-common/pm.c index 0be805ca423..1387a94bcfd 100644 --- a/arch/blackfin/mach-common/pm.c +++ b/arch/blackfin/mach-common/pm.c @@ -1,96 +1,209 @@ /* - * File: arch/blackfin/mach-common/pm.c - * Based on: arm/mach-omap/pm.c - * Author: Cliff Brake <cbrake@accelent.com> Copyright (c) 2001 + * Blackfin power management * - * Created: 2001 - * Description: Blackfin power management + * Copyright 2006-2009 Analog Devices Inc. * - * Modified: Nicolas Pitre - PXA250 support - * Copyright (c) 2002 Monta Vista Software, Inc. - * David Singleton - OMAP1510 - * Copyright (c) 2002 Monta Vista Software, Inc. - * Dirk Behme <dirk.behme@de.bosch.com> - OMAP1510/1610 - * Copyright 2004 - * Copyright 2004-2008 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 + * Licensed under the GPL-2 + * based on arm/mach-omap/pm.c + * Copyright 2001, Cliff Brake <cbrake@accelent.com> and others */ #include <linux/suspend.h> #include <linux/sched.h> #include <linux/proc_fs.h> +#include <linux/slab.h> #include <linux/io.h> #include <linux/irq.h> -#include <asm/dpmc.h> +#include <asm/cplb.h> #include <asm/gpio.h> +#include <asm/dma.h> +#include <asm/dpmc.h> +#include <asm/pm.h> -#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_H -#define WAKEUP_TYPE PM_WAKE_HIGH +#ifdef CONFIG_BF60x +struct bfin_cpu_pm_fns *bfin_cpu_pm; #endif -#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_L -#define WAKEUP_TYPE PM_WAKE_LOW +void bfin_pm_suspend_standby_enter(void) +{ +#if !BFIN_GPIO_PINT + bfin_pm_standby_setup(); #endif -#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_EDGE_F -#define WAKEUP_TYPE PM_WAKE_FALLING +#ifdef CONFIG_BF60x + bfin_cpu_pm->enter(PM_SUSPEND_STANDBY); +#else +# ifdef CONFIG_PM_BFIN_SLEEP_DEEPER + sleep_deeper(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); +# else + sleep_mode(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); +# endif #endif -#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_EDGE_R -#define WAKEUP_TYPE PM_WAKE_RISING +#if !BFIN_GPIO_PINT + bfin_pm_standby_restore(); #endif -#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_EDGE_B -#define WAKEUP_TYPE PM_WAKE_BOTH_EDGES +#ifndef CONFIG_BF60x +#ifdef SIC_IWR0 + bfin_write_SIC_IWR0(IWR_DISABLE_ALL); +# ifdef SIC_IWR1 + /* BF52x system reset does not properly reset SIC_IWR1 which + * will screw up the bootrom as it relies on MDMA0/1 waking it + * up from IDLE instructions. See this report for more info: + * http://blackfin.uclinux.org/gf/tracker/4323 + */ + if (ANOMALY_05000435) + bfin_write_SIC_IWR1(IWR_ENABLE(10) | IWR_ENABLE(11)); + else + bfin_write_SIC_IWR1(IWR_DISABLE_ALL); +# endif +# ifdef SIC_IWR2 + bfin_write_SIC_IWR2(IWR_DISABLE_ALL); +# endif +#else + bfin_write_SIC_IWR(IWR_DISABLE_ALL); #endif -void bfin_pm_suspend_standby_enter(void) +#endif +} + +int bf53x_suspend_l1_mem(unsigned char *memptr) +{ + dma_memcpy_nocache(memptr, (const void *) L1_CODE_START, + L1_CODE_LENGTH); + dma_memcpy_nocache(memptr + L1_CODE_LENGTH, + (const void *) L1_DATA_A_START, L1_DATA_A_LENGTH); + dma_memcpy_nocache(memptr + L1_CODE_LENGTH + L1_DATA_A_LENGTH, + (const void *) L1_DATA_B_START, L1_DATA_B_LENGTH); + memcpy(memptr + L1_CODE_LENGTH + L1_DATA_A_LENGTH + + L1_DATA_B_LENGTH, (const void *) L1_SCRATCH_START, + L1_SCRATCH_LENGTH); + + return 0; +} + +int bf53x_resume_l1_mem(unsigned char *memptr) +{ + dma_memcpy_nocache((void *) L1_CODE_START, memptr, L1_CODE_LENGTH); + dma_memcpy_nocache((void *) L1_DATA_A_START, memptr + L1_CODE_LENGTH, + L1_DATA_A_LENGTH); + dma_memcpy_nocache((void *) L1_DATA_B_START, memptr + L1_CODE_LENGTH + + L1_DATA_A_LENGTH, L1_DATA_B_LENGTH); + memcpy((void *) L1_SCRATCH_START, memptr + L1_CODE_LENGTH + + L1_DATA_A_LENGTH + L1_DATA_B_LENGTH, L1_SCRATCH_LENGTH); + + return 0; +} + +#if defined(CONFIG_BFIN_EXTMEM_WRITEBACK) || defined(CONFIG_BFIN_L2_WRITEBACK) +# ifdef CONFIG_BF60x +__attribute__((l1_text)) +# endif +static void flushinv_all_dcache(void) +{ + register u32 way, bank, subbank, set; + register u32 status, addr; + u32 dmem_ctl = bfin_read_DMEM_CONTROL(); + + for (bank = 0; bank < 2; ++bank) { + if (!(dmem_ctl & (1 << (DMC1_P - bank)))) + continue; + + for (way = 0; way < 2; ++way) + for (subbank = 0; subbank < 4; ++subbank) + for (set = 0; set < 64; ++set) { + + bfin_write_DTEST_COMMAND( + way << 26 | + bank << 23 | + subbank << 16 | + set << 5 + ); + CSYNC(); + status = bfin_read_DTEST_DATA0(); + + /* only worry about valid/dirty entries */ + if ((status & 0x3) != 0x3) + continue; + + + /* construct the address using the tag */ + addr = (status & 0xFFFFC800) | (subbank << 12) | (set << 5); + + /* flush it */ + __asm__ __volatile__("FLUSHINV[%0];" : : "a"(addr)); + } + } +} +#endif + +int bfin_pm_suspend_mem_enter(void) { -#ifdef CONFIG_PM_WAKEUP_BY_GPIO - gpio_pm_wakeup_request(CONFIG_PM_WAKEUP_GPIO_NUMBER, WAKEUP_TYPE); + int ret; +#ifndef CONFIG_BF60x + int wakeup; #endif - u32 flags; + unsigned char *memptr = kmalloc(L1_CODE_LENGTH + L1_DATA_A_LENGTH + + L1_DATA_B_LENGTH + L1_SCRATCH_LENGTH, + GFP_ATOMIC); - local_irq_save(flags); - bfin_pm_setup(); + if (memptr == NULL) { + panic("bf53x_suspend_l1_mem malloc failed"); + return -ENOMEM; + } -#ifdef CONFIG_PM_BFIN_SLEEP_DEEPER - sleep_deeper(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); -#else - sleep_mode(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); +#ifndef CONFIG_BF60x + wakeup = bfin_read_VR_CTL() & ~FREQ; + wakeup |= SCKELOW; + +#ifdef CONFIG_PM_BFIN_WAKE_PH6 + wakeup |= PHYWE; +#endif +#ifdef CONFIG_PM_BFIN_WAKE_GP + wakeup |= GPWE; +#endif #endif - bfin_pm_restore(); + ret = blackfin_dma_suspend(); -#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) - bfin_write_SIC_IWR0(IWR_ENABLE_ALL); - bfin_write_SIC_IWR1(IWR_ENABLE_ALL); -# ifdef CONFIG_BF54x - bfin_write_SIC_IWR2(IWR_ENABLE_ALL); -# endif + if (ret) { + kfree(memptr); + return ret; + } + +#ifdef CONFIG_GPIO_ADI + bfin_gpio_pm_hibernate_suspend(); +#endif + +#if defined(CONFIG_BFIN_EXTMEM_WRITEBACK) || defined(CONFIG_BFIN_L2_WRITEBACK) + flushinv_all_dcache(); +#endif + _disable_dcplb(); + _disable_icplb(); + bf53x_suspend_l1_mem(memptr); + +#ifndef CONFIG_BF60x + do_hibernate(wakeup | vr_wakeup); /* See you later! */ #else - bfin_write_SIC_IWR(IWR_ENABLE_ALL); + bfin_cpu_pm->enter(PM_SUSPEND_MEM); #endif - local_irq_restore(flags); + bf53x_resume_l1_mem(memptr); + + _enable_icplb(); + _enable_dcplb(); + +#ifdef CONFIG_GPIO_ADI + bfin_gpio_pm_hibernate_restore(); +#endif + blackfin_dma_resume(); + + kfree(memptr); + + return 0; } /* @@ -101,7 +214,24 @@ void bfin_pm_suspend_standby_enter(void) */ static int bfin_pm_valid(suspend_state_t state) { - return (state == PM_SUSPEND_STANDBY); + return (state == PM_SUSPEND_STANDBY +#if !(defined(BF533_FAMILY) || defined(CONFIG_BF561)) + /* + * On BF533/2/1: + * If we enter Hibernate the SCKE Pin is driven Low, + * so that the SDRAM enters Self Refresh Mode. + * However when the reset sequence that follows hibernate + * state is executed, SCKE is driven High, taking the + * SDRAM out of Self Refresh. + * + * If you reconfigure and access the SDRAM "very quickly", + * you are likely to avoid errors, otherwise the SDRAM + * start losing its contents. + * An external HW workaround is possible using logic gates. + */ + || state == PM_SUSPEND_MEM +#endif + ); } /* @@ -115,10 +245,9 @@ static int bfin_pm_enter(suspend_state_t state) case PM_SUSPEND_STANDBY: bfin_pm_suspend_standby_enter(); break; - case PM_SUSPEND_MEM: - return -ENOTSUPP; - + bfin_pm_suspend_mem_enter(); + break; default: return -EINVAL; } @@ -126,9 +255,39 @@ static int bfin_pm_enter(suspend_state_t state) return 0; } -struct platform_suspend_ops bfin_pm_ops = { +#ifdef CONFIG_BFIN_PM_WAKEUP_TIME_BENCH +void bfin_pm_end(void) +{ + u32 cycle, cycle2; + u64 usec64; + u32 usec; + + __asm__ __volatile__ ( + "1: %0 = CYCLES2\n" + "%1 = CYCLES\n" + "%2 = CYCLES2\n" + "CC = %2 == %0\n" + "if ! CC jump 1b\n" + : "=d,a" (cycle2), "=d,a" (cycle), "=d,a" (usec) : : "CC" + ); + + usec64 = ((u64)cycle2 << 32) + cycle; + do_div(usec64, get_cclk() / USEC_PER_SEC); + usec = usec64; + if (usec == 0) + usec = 1; + + pr_info("PM: resume of kernel completes after %ld msec %03ld usec\n", + usec / USEC_PER_MSEC, usec % USEC_PER_MSEC); +} +#endif + +static const struct platform_suspend_ops bfin_pm_ops = { .enter = bfin_pm_enter, .valid = bfin_pm_valid, +#ifdef CONFIG_BFIN_PM_WAKEUP_TIME_BENCH + .end = bfin_pm_end, +#endif }; static int __init bfin_pm_init(void) diff --git a/arch/blackfin/mach-common/scb-init.c b/arch/blackfin/mach-common/scb-init.c new file mode 100644 index 00000000000..8923398db66 --- /dev/null +++ b/arch/blackfin/mach-common/scb-init.c @@ -0,0 +1,52 @@ +/* + * arch/blackfin/mach-common/scb-init.c - reprogram system cross bar priority + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <asm/scb.h> + +__attribute__((l1_text)) +inline void scb_mi_write(unsigned long scb_mi_arbw, unsigned int slots, + unsigned char *scb_mi_prio) +{ + unsigned int i; + + for (i = 0; i < slots; ++i) + bfin_write32(scb_mi_arbw, (i << SCB_SLOT_OFFSET) | scb_mi_prio[i]); +} + +__attribute__((l1_text)) +inline void scb_mi_read(unsigned long scb_mi_arbw, unsigned int slots, + unsigned char *scb_mi_prio) +{ + unsigned int i; + + for (i = 0; i < slots; ++i) { + bfin_write32(scb_mi_arbw, (0xFF << SCB_SLOT_OFFSET) | i); + scb_mi_prio[i] = bfin_read32(scb_mi_arbw); + } +} + +__attribute__((l1_text)) +void init_scb(void) +{ + unsigned int i, j; + unsigned char scb_tmp_prio[32]; + + pr_info("Init System Crossbar\n"); + for (i = 0; scb_data[i].scb_mi_arbr > 0; ++i) { + + scb_mi_write(scb_data[i].scb_mi_arbw, scb_data[i].scb_mi_slots, scb_data[i].scb_mi_prio); + + pr_debug("scb priority at 0x%lx:\n", scb_data[i].scb_mi_arbr); + scb_mi_read(scb_data[i].scb_mi_arbw, scb_data[i].scb_mi_slots, scb_tmp_prio); + for (j = 0; j < scb_data[i].scb_mi_slots; ++j) + pr_debug("slot %d = %d\n", j, scb_tmp_prio[j]); + } + +} diff --git a/arch/blackfin/mach-common/smp.c b/arch/blackfin/mach-common/smp.c new file mode 100644 index 00000000000..ba6c30d8534 --- /dev/null +++ b/arch/blackfin/mach-common/smp.c @@ -0,0 +1,433 @@ +/* + * IPI management based on arch/arm/kernel/smp.c (Copyright 2002 ARM Limited) + * + * Copyright 2007-2009 Analog Devices Inc. + * Philippe Gerum <rpm@xenomai.org> + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/cache.h> +#include <linux/clockchips.h> +#include <linux/profile.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/cpu.h> +#include <linux/smp.h> +#include <linux/cpumask.h> +#include <linux/seq_file.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/atomic.h> +#include <asm/cacheflush.h> +#include <asm/irq_handler.h> +#include <asm/mmu_context.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/processor.h> +#include <asm/ptrace.h> +#include <asm/cpu.h> +#include <asm/time.h> +#include <linux/err.h> + +/* + * Anomaly notes: + * 05000120 - we always define corelock as 32-bit integer in L2 + */ +struct corelock_slot corelock __attribute__ ((__section__(".l2.bss"))); + +#ifdef CONFIG_ICACHE_FLUSH_L1 +unsigned long blackfin_iflush_l1_entry[NR_CPUS]; +#endif + +struct blackfin_initial_pda initial_pda_coreb; + +enum ipi_message_type { + BFIN_IPI_NONE, + BFIN_IPI_TIMER, + BFIN_IPI_RESCHEDULE, + BFIN_IPI_CALL_FUNC, + BFIN_IPI_CPU_STOP, +}; + +struct blackfin_flush_data { + unsigned long start; + unsigned long end; +}; + +void *secondary_stack; + +static struct blackfin_flush_data smp_flush_data; + +static DEFINE_SPINLOCK(stop_lock); + +/* A magic number - stress test shows this is safe for common cases */ +#define BFIN_IPI_MSGQ_LEN 5 + +/* Simple FIFO buffer, overflow leads to panic */ +struct ipi_data { + atomic_t count; + atomic_t bits; +}; + +static DEFINE_PER_CPU(struct ipi_data, bfin_ipi); + +static void ipi_cpu_stop(unsigned int cpu) +{ + spin_lock(&stop_lock); + printk(KERN_CRIT "CPU%u: stopping\n", cpu); + dump_stack(); + spin_unlock(&stop_lock); + + set_cpu_online(cpu, false); + + local_irq_disable(); + + while (1) + SSYNC(); +} + +static void ipi_flush_icache(void *info) +{ + struct blackfin_flush_data *fdata = info; + + /* Invalidate the memory holding the bounds of the flushed region. */ + blackfin_dcache_invalidate_range((unsigned long)fdata, + (unsigned long)fdata + sizeof(*fdata)); + + /* Make sure all write buffers in the data side of the core + * are flushed before trying to invalidate the icache. This + * needs to be after the data flush and before the icache + * flush so that the SSYNC does the right thing in preventing + * the instruction prefetcher from hitting things in cached + * memory at the wrong time -- it runs much further ahead than + * the pipeline. + */ + SSYNC(); + + /* ipi_flaush_icache is invoked by generic flush_icache_range, + * so call blackfin arch icache flush directly here. + */ + blackfin_icache_flush_range(fdata->start, fdata->end); +} + +/* Use IRQ_SUPPLE_0 to request reschedule. + * When returning from interrupt to user space, + * there is chance to reschedule */ +static irqreturn_t ipi_handler_int0(int irq, void *dev_instance) +{ + unsigned int cpu = smp_processor_id(); + + platform_clear_ipi(cpu, IRQ_SUPPLE_0); + return IRQ_HANDLED; +} + +DECLARE_PER_CPU(struct clock_event_device, coretmr_events); +void ipi_timer(void) +{ + int cpu = smp_processor_id(); + struct clock_event_device *evt = &per_cpu(coretmr_events, cpu); + evt->event_handler(evt); +} + +static irqreturn_t ipi_handler_int1(int irq, void *dev_instance) +{ + struct ipi_data *bfin_ipi_data; + unsigned int cpu = smp_processor_id(); + unsigned long pending; + unsigned long msg; + + platform_clear_ipi(cpu, IRQ_SUPPLE_1); + + smp_rmb(); + bfin_ipi_data = &__get_cpu_var(bfin_ipi); + while ((pending = atomic_xchg(&bfin_ipi_data->bits, 0)) != 0) { + msg = 0; + do { + msg = find_next_bit(&pending, BITS_PER_LONG, msg + 1); + switch (msg) { + case BFIN_IPI_TIMER: + ipi_timer(); + break; + case BFIN_IPI_RESCHEDULE: + scheduler_ipi(); + break; + case BFIN_IPI_CALL_FUNC: + generic_smp_call_function_interrupt(); + break; + case BFIN_IPI_CPU_STOP: + ipi_cpu_stop(cpu); + break; + default: + goto out; + } + atomic_dec(&bfin_ipi_data->count); + } while (msg < BITS_PER_LONG); + + } +out: + return IRQ_HANDLED; +} + +static void bfin_ipi_init(void) +{ + unsigned int cpu; + struct ipi_data *bfin_ipi_data; + for_each_possible_cpu(cpu) { + bfin_ipi_data = &per_cpu(bfin_ipi, cpu); + atomic_set(&bfin_ipi_data->bits, 0); + atomic_set(&bfin_ipi_data->count, 0); + } +} + +void send_ipi(const struct cpumask *cpumask, enum ipi_message_type msg) +{ + unsigned int cpu; + struct ipi_data *bfin_ipi_data; + unsigned long flags; + + local_irq_save(flags); + for_each_cpu(cpu, cpumask) { + bfin_ipi_data = &per_cpu(bfin_ipi, cpu); + atomic_set_mask((1 << msg), &bfin_ipi_data->bits); + atomic_inc(&bfin_ipi_data->count); + } + local_irq_restore(flags); + smp_wmb(); + for_each_cpu(cpu, cpumask) + platform_send_ipi_cpu(cpu, IRQ_SUPPLE_1); +} + +void arch_send_call_function_single_ipi(int cpu) +{ + send_ipi(cpumask_of(cpu), BFIN_IPI_CALL_FUNC); +} + +void arch_send_call_function_ipi_mask(const struct cpumask *mask) +{ + send_ipi(mask, BFIN_IPI_CALL_FUNC); +} + +void smp_send_reschedule(int cpu) +{ + send_ipi(cpumask_of(cpu), BFIN_IPI_RESCHEDULE); + + return; +} + +void smp_send_msg(const struct cpumask *mask, unsigned long type) +{ + send_ipi(mask, type); +} + +void smp_timer_broadcast(const struct cpumask *mask) +{ + smp_send_msg(mask, BFIN_IPI_TIMER); +} + +void smp_send_stop(void) +{ + cpumask_t callmap; + + preempt_disable(); + cpumask_copy(&callmap, cpu_online_mask); + cpumask_clear_cpu(smp_processor_id(), &callmap); + if (!cpumask_empty(&callmap)) + send_ipi(&callmap, BFIN_IPI_CPU_STOP); + + preempt_enable(); + + return; +} + +int __cpu_up(unsigned int cpu, struct task_struct *idle) +{ + int ret; + + secondary_stack = task_stack_page(idle) + THREAD_SIZE; + + ret = platform_boot_secondary(cpu, idle); + + secondary_stack = NULL; + + return ret; +} + +static void setup_secondary(unsigned int cpu) +{ + unsigned long ilat; + + bfin_write_IMASK(0); + CSYNC(); + ilat = bfin_read_ILAT(); + CSYNC(); + bfin_write_ILAT(ilat); + CSYNC(); + + /* Enable interrupt levels IVG7-15. IARs have been already + * programmed by the boot CPU. */ + bfin_irq_flags |= IMASK_IVG15 | + IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 | + IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW; +} + +void secondary_start_kernel(void) +{ + unsigned int cpu = smp_processor_id(); + struct mm_struct *mm = &init_mm; + + if (_bfin_swrst & SWRST_DBL_FAULT_B) { + printk(KERN_EMERG "CoreB Recovering from DOUBLE FAULT event\n"); +#ifdef CONFIG_DEBUG_DOUBLEFAULT + printk(KERN_EMERG " While handling exception (EXCAUSE = %#x) at %pF\n", + initial_pda_coreb.seqstat_doublefault & SEQSTAT_EXCAUSE, + initial_pda_coreb.retx_doublefault); + printk(KERN_NOTICE " DCPLB_FAULT_ADDR: %pF\n", + initial_pda_coreb.dcplb_doublefault_addr); + printk(KERN_NOTICE " ICPLB_FAULT_ADDR: %pF\n", + initial_pda_coreb.icplb_doublefault_addr); +#endif + printk(KERN_NOTICE " The instruction at %pF caused a double exception\n", + initial_pda_coreb.retx); + } + + /* + * We want the D-cache to be enabled early, in case the atomic + * support code emulates cache coherence (see + * __ARCH_SYNC_CORE_DCACHE). + */ + init_exception_vectors(); + + local_irq_disable(); + + /* Attach the new idle task to the global mm. */ + atomic_inc(&mm->mm_users); + atomic_inc(&mm->mm_count); + current->active_mm = mm; + + preempt_disable(); + + setup_secondary(cpu); + + platform_secondary_init(cpu); + /* setup local core timer */ + bfin_local_timer_setup(); + + local_irq_enable(); + + bfin_setup_caches(cpu); + + notify_cpu_starting(cpu); + /* + * Calibrate loops per jiffy value. + * IRQs need to be enabled here - D-cache can be invalidated + * in timer irq handler, so core B can read correct jiffies. + */ + calibrate_delay(); + + /* We are done with local CPU inits, unblock the boot CPU. */ + set_cpu_online(cpu, true); + cpu_startup_entry(CPUHP_ONLINE); +} + +void __init smp_prepare_boot_cpu(void) +{ +} + +void __init smp_prepare_cpus(unsigned int max_cpus) +{ + platform_prepare_cpus(max_cpus); + bfin_ipi_init(); + platform_request_ipi(IRQ_SUPPLE_0, ipi_handler_int0); + platform_request_ipi(IRQ_SUPPLE_1, ipi_handler_int1); +} + +void __init smp_cpus_done(unsigned int max_cpus) +{ + unsigned long bogosum = 0; + unsigned int cpu; + + for_each_online_cpu(cpu) + bogosum += loops_per_jiffy; + + printk(KERN_INFO "SMP: Total of %d processors activated " + "(%lu.%02lu BogoMIPS).\n", + num_online_cpus(), + bogosum / (500000/HZ), + (bogosum / (5000/HZ)) % 100); +} + +void smp_icache_flush_range_others(unsigned long start, unsigned long end) +{ + smp_flush_data.start = start; + smp_flush_data.end = end; + + preempt_disable(); + if (smp_call_function(&ipi_flush_icache, &smp_flush_data, 1)) + printk(KERN_WARNING "SMP: failed to run I-cache flush request on other CPUs\n"); + preempt_enable(); +} +EXPORT_SYMBOL_GPL(smp_icache_flush_range_others); + +#ifdef __ARCH_SYNC_CORE_ICACHE +unsigned long icache_invld_count[NR_CPUS]; +void resync_core_icache(void) +{ + unsigned int cpu = get_cpu(); + blackfin_invalidate_entire_icache(); + icache_invld_count[cpu]++; + put_cpu(); +} +EXPORT_SYMBOL(resync_core_icache); +#endif + +#ifdef __ARCH_SYNC_CORE_DCACHE +unsigned long dcache_invld_count[NR_CPUS]; +unsigned long barrier_mask __attribute__ ((__section__(".l2.bss"))); + +void resync_core_dcache(void) +{ + unsigned int cpu = get_cpu(); + blackfin_invalidate_entire_dcache(); + dcache_invld_count[cpu]++; + put_cpu(); +} +EXPORT_SYMBOL(resync_core_dcache); +#endif + +#ifdef CONFIG_HOTPLUG_CPU +int __cpu_disable(void) +{ + unsigned int cpu = smp_processor_id(); + + if (cpu == 0) + return -EPERM; + + set_cpu_online(cpu, false); + return 0; +} + +static DECLARE_COMPLETION(cpu_killed); + +int __cpu_die(unsigned int cpu) +{ + return wait_for_completion_timeout(&cpu_killed, 5000); +} + +void cpu_die(void) +{ + complete(&cpu_killed); + + atomic_dec(&init_mm.mm_users); + atomic_dec(&init_mm.mm_count); + + local_irq_disable(); + platform_cpu_die(); +} +#endif |
