diff options
Diffstat (limited to 'arch/blackfin/mach-common')
24 files changed, 4042 insertions, 4059 deletions
diff --git a/arch/blackfin/mach-common/Makefile b/arch/blackfin/mach-common/Makefile index 4d7733dfd5d..f0997920404 100644 --- a/arch/blackfin/mach-common/Makefile +++ b/arch/blackfin/mach-common/Makefile @@ -3,10 +3,14 @@ # obj-y := \ - cache.o cacheinit.o cplbhdlr.o cplbmgr.o entry.o \ - interrupt.o lock.o irqpanic.o arch_checks.o + cache.o cache-c.o entry.o head.o \ + interrupt.o arch_checks.o ints-priority.o -obj-$(CONFIG_CPLB_INFO) += cplbinfo.o -obj-$(CONFIG_BFIN_SINGLE_CORE) += ints-priority-sc.o -obj-$(CONFIG_BFIN_DUAL_CORE) += ints-priority-dc.o -obj-$(CONFIG_PM) += pm.o dpmc.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 2f6ce397780..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,7 +34,33 @@ #endif /* CONFIG_BFIN_KERNEL_CLOCK */ -#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/cplbhdlr.S b/arch/blackfin/mach-common/cplbhdlr.S deleted file mode 100644 index 2788532de72..00000000000 --- a/arch/blackfin/mach-common/cplbhdlr.S +++ /dev/null @@ -1,130 +0,0 @@ -/* - * File: arch/blackfin/mach-common/cplbhdlr.S - * Based on: - * Author: LG Soft India - * - * Created: ? - * Description: CPLB exception handler - * - * Modified: - * Copyright 2004-2006 Analog Devices Inc. - * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include <linux/linkage.h> -#include <asm/cplb.h> -#include <asm/entry.h> - -#ifdef CONFIG_EXCPT_IRQ_SYSC_L1 -.section .l1.text -#else -.text -#endif - -.type _cplb_mgr, STT_FUNC; -.type _panic_cplb_error, STT_FUNC; - -.align 2 - -ENTRY(__cplb_hdr) - R2 = SEQSTAT; - - /* Mask the contents of SEQSTAT and leave only EXCAUSE in R2 */ - R2 <<= 26; - R2 >>= 26; - - R1 = 0x23; /* Data access CPLB protection violation */ - CC = R2 == R1; - IF !CC JUMP .Lnot_data_write; - R0 = 2; /* is a write to data space*/ - JUMP .Lis_icplb_miss; - -.Lnot_data_write: - R1 = 0x2C; /* CPLB miss on an instruction fetch */ - CC = R2 == R1; - R0 = 0; /* is_data_miss == False*/ - IF CC JUMP .Lis_icplb_miss; - - R1 = 0x26; - CC = R2 == R1; - IF !CC JUMP .Lunknown; - - R0 = 1; /* is_data_miss == True*/ - -.Lis_icplb_miss: - -#if defined(CONFIG_BFIN_ICACHE) || defined(CONFIG_BFIN_DCACHE) -# if defined(CONFIG_BFIN_ICACHE) && !defined(CONFIG_BFIN_DCACHE) - R1 = CPLB_ENABLE_ICACHE; -# endif -# if !defined(CONFIG_BFIN_ICACHE) && defined(CONFIG_BFIN_DCACHE) - R1 = CPLB_ENABLE_DCACHE; -# endif -# if defined(CONFIG_BFIN_ICACHE) && defined(CONFIG_BFIN_DCACHE) - R1 = CPLB_ENABLE_DCACHE | CPLB_ENABLE_ICACHE; -# endif -#else - R1 = 0; -#endif - - [--SP] = RETS; - CALL _cplb_mgr; - RETS = [SP++]; - CC = R0 == 0; - IF !CC JUMP .Lnot_replaced; - RTS; - -/* - * Diagnostic exception handlers - */ -.Lunknown: - R0 = CPLB_UNKNOWN_ERR; - JUMP .Lcplb_error; - -.Lnot_replaced: - CC = R0 == CPLB_NO_UNLOCKED; - IF !CC JUMP .Lnext_check; - R0 = CPLB_NO_UNLOCKED; - JUMP .Lcplb_error; - -.Lnext_check: - CC = R0 == CPLB_NO_ADDR_MATCH; - IF !CC JUMP .Lnext_check2; - R0 = CPLB_NO_ADDR_MATCH; - JUMP .Lcplb_error; - -.Lnext_check2: - CC = R0 == CPLB_PROT_VIOL; - IF !CC JUMP .Lstrange_return_from_cplb_mgr; - R0 = CPLB_PROT_VIOL; - JUMP .Lcplb_error; - -.Lstrange_return_from_cplb_mgr: - IDLE; - CSYNC; - JUMP .Lstrange_return_from_cplb_mgr; - -.Lcplb_error: - R1 = sp; - SP += -12; - call _panic_cplb_error; - SP += 12; - JUMP _handle_bad_cplb; - -ENDPROC(__cplb_hdr) diff --git a/arch/blackfin/mach-common/cplbinfo.c b/arch/blackfin/mach-common/cplbinfo.c deleted file mode 100644 index 785ca981697..00000000000 --- a/arch/blackfin/mach-common/cplbinfo.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * File: arch/blackfin/mach-common/cplbinfo.c - * Based on: - * Author: Sonic Zhang <sonic.zhang@analog.com> - * - * Created: Jan. 2005 - * Description: Display CPLB status - * - * Modified: - * Copyright 2004-2006 Analog Devices Inc. - * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/proc_fs.h> -#include <linux/uaccess.h> - -#include <asm/current.h> -#include <asm/system.h> -#include <asm/cplb.h> -#include <asm/blackfin.h> - -#define CPLB_I 1 -#define CPLB_D 2 - -#define SYNC_SYS SSYNC() -#define SYNC_CORE CSYNC() - -#define CPLB_BIT_PAGESIZE 0x30000 - -static int page_size_table[4] = { - 0x00000400, /* 1K */ - 0x00001000, /* 4K */ - 0x00100000, /* 1M */ - 0x00400000 /* 4M */ -}; - -static char page_size_string_table[][4] = { "1K", "4K", "1M", "4M" }; - -static int cplb_find_entry(unsigned long *cplb_addr, - unsigned long *cplb_data, unsigned long addr, - unsigned long data) -{ - int ii; - - for (ii = 0; ii < 16; ii++) - if (addr >= cplb_addr[ii] && addr < cplb_addr[ii] + - page_size_table[(cplb_data[ii] & CPLB_BIT_PAGESIZE) >> 16] - && (cplb_data[ii] == data)) - return ii; - - return -1; -} - -static char *cplb_print_entry(char *buf, int type) -{ - unsigned long *p_addr = dpdt_table; - unsigned long *p_data = dpdt_table + 1; - unsigned long *p_icount = dpdt_swapcount_table; - unsigned long *p_ocount = dpdt_swapcount_table + 1; - unsigned long *cplb_addr = (unsigned long *)DCPLB_ADDR0; - unsigned long *cplb_data = (unsigned long *)DCPLB_DATA0; - int entry = 0, used_cplb = 0; - - if (type == CPLB_I) { - buf += sprintf(buf, "Instruction CPLB entry:\n"); - p_addr = ipdt_table; - p_data = ipdt_table + 1; - p_icount = ipdt_swapcount_table; - p_ocount = ipdt_swapcount_table + 1; - cplb_addr = (unsigned long *)ICPLB_ADDR0; - cplb_data = (unsigned long *)ICPLB_DATA0; - } else - buf += sprintf(buf, "Data CPLB entry:\n"); - - buf += sprintf(buf, "Address\t\tData\tSize\tValid\tLocked\tSwapin\n\tiCount\toCount\n"); - - while (*p_addr != 0xffffffff) { - entry = cplb_find_entry(cplb_addr, cplb_data, *p_addr, *p_data); - if (entry >= 0) - used_cplb |= 1 << entry; - - buf += - sprintf(buf, - "0x%08lx\t0x%05lx\t%s\t%c\t%c\t%2d\t%ld\t%ld\n", - *p_addr, *p_data, - page_size_string_table[(*p_data & 0x30000) >> 16], - (*p_data & CPLB_VALID) ? 'Y' : 'N', - (*p_data & CPLB_LOCK) ? 'Y' : 'N', entry, *p_icount, - *p_ocount); - - p_addr += 2; - p_data += 2; - p_icount += 2; - p_ocount += 2; - } - - if (used_cplb != 0xffff) { - buf += sprintf(buf, "Unused/mismatched CPLBs:\n"); - - for (entry = 0; entry < 16; entry++) - if (0 == ((1 << entry) & used_cplb)) { - int flags = cplb_data[entry]; - buf += - sprintf(buf, - "%2d: 0x%08lx\t0x%05x\t%s\t%c\t%c\n", - entry, cplb_addr[entry], flags, - page_size_string_table[(flags & - 0x30000) >> - 16], - (flags & CPLB_VALID) ? 'Y' : 'N', - (flags & CPLB_LOCK) ? 'Y' : 'N'); - } - } - - buf += sprintf(buf, "\n"); - - return buf; -} - -static int cplbinfo_proc_output(char *buf) -{ - char *p; - - p = buf; - - p += sprintf(p, "------------------ CPLB Information ------------------\n\n"); - - if (bfin_read_IMEM_CONTROL() & ENICPLB) - p = cplb_print_entry(p, CPLB_I); - else - p += sprintf(p, "Instruction CPLB is disabled.\n\n"); - - if (bfin_read_DMEM_CONTROL() & ENDCPLB) - p = cplb_print_entry(p, CPLB_D); - else - p += sprintf(p, "Data CPLB is disabled.\n"); - - return p - buf; -} - -static int cplbinfo_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len; - - len = cplbinfo_proc_output(page); - if (len <= off + count) - *eof = 1; - *start = page + off; - len -= off; - if (len > count) - len = count; - if (len < 0) - len = 0; - return len; -} - -static int cplbinfo_write_proc(struct file *file, const char __user *buffer, - unsigned long count, void *data) -{ - printk(KERN_INFO "Reset the CPLB swap in/out counts.\n"); - memset(ipdt_swapcount_table, 0, MAX_SWITCH_I_CPLBS * sizeof(unsigned long)); - memset(dpdt_swapcount_table, 0, MAX_SWITCH_D_CPLBS * sizeof(unsigned long)); - - return count; -} - -static int __init cplbinfo_init(void) -{ - struct proc_dir_entry *entry; - - entry = create_proc_entry("cplbinfo", 0, NULL); - if (!entry) - return -ENOMEM; - - entry->read_proc = cplbinfo_read_proc; - entry->write_proc = cplbinfo_write_proc; - entry->data = NULL; - - return 0; -} - -static void __exit cplbinfo_exit(void) -{ - remove_proc_entry("cplbinfo", NULL); -} - -module_init(cplbinfo_init); -module_exit(cplbinfo_exit); diff --git a/arch/blackfin/mach-common/cplbmgr.S b/arch/blackfin/mach-common/cplbmgr.S deleted file mode 100644 index 946703ef48f..00000000000 --- a/arch/blackfin/mach-common/cplbmgr.S +++ /dev/null @@ -1,608 +0,0 @@ -/* - * File: arch/blackfin/mach-common/cplbmgtr.S - * Based on: - * Author: LG Soft India - * - * Created: ? - * Description: CPLB replacement routine for CPLB mismatch - * - * Modified: - * Copyright 2004-2006 Analog Devices Inc. - * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* Usage: int _cplb_mgr(is_data_miss,int enable_cache) - * is_data_miss==2 => Mark as Dirty, write to the clean data page - * is_data_miss==1 => Replace a data CPLB. - * is_data_miss==0 => Replace an instruction CPLB. - * - * Returns: - * CPLB_RELOADED => Successfully updated CPLB table. - * CPLB_NO_UNLOCKED => All CPLBs are locked, so cannot be evicted. - * This indicates that the CPLBs in the configuration - * tablei are badly configured, as this should never - * occur. - * CPLB_NO_ADDR_MATCH => The address being accessed, that triggered the - * exception, is not covered by any of the CPLBs in - * the configuration table. The application is - * presumably misbehaving. - * CPLB_PROT_VIOL => The address being accessed, that triggered the - * exception, was not a first-write to a clean Write - * Back Data page, and so presumably is a genuine - * violation of the page's protection attributes. - * The application is misbehaving. - */ - -#include <linux/linkage.h> -#include <asm/blackfin.h> -#include <asm/cplb.h> - -#ifdef CONFIG_EXCPT_IRQ_SYSC_L1 -.section .l1.text -#else -.text -#endif - -.align 2; -ENTRY(_cplb_mgr) - - [--SP]=( R7:4,P5:3 ); - - CC = R0 == 2; - IF CC JUMP .Ldcplb_write; - - CC = R0 == 0; - IF !CC JUMP .Ldcplb_miss_compare; - - /* ICPLB Miss Exception. We need to choose one of the - * currently-installed CPLBs, and replace it with one - * from the configuration table. - */ - - P4.L = LO(ICPLB_FAULT_ADDR); - P4.H = HI(ICPLB_FAULT_ADDR); - - P1 = 16; - P5.L = _page_size_table; - P5.H = _page_size_table; - - P0.L = LO(ICPLB_DATA0); - P0.H = HI(ICPLB_DATA0); - R4 = [P4]; /* Get faulting address*/ - R6 = 64; /* Advance past the fault address, which*/ - R6 = R6 + R4; /* we'll use if we find a match*/ - R3 = ((16 << 8) | 2); /* Extract mask, bits 16 and 17.*/ - - R5 = 0; -.Lisearch: - - R1 = [P0-0x100]; /* Address for this CPLB */ - - R0 = [P0++]; /* Info for this CPLB*/ - CC = BITTST(R0,0); /* Is the CPLB valid?*/ - IF !CC JUMP .Lnomatch; /* Skip it, if not.*/ - CC = R4 < R1(IU); /* If fault address less than page start*/ - IF CC JUMP .Lnomatch; /* then skip this one.*/ - R2 = EXTRACT(R0,R3.L) (Z); /* Get page size*/ - P1 = R2; - P1 = P5 + (P1<<2); /* index into page-size table*/ - R2 = [P1]; /* Get the page size*/ - R1 = R1 + R2; /* and add to page start, to get page end*/ - CC = R4 < R1(IU); /* and see whether fault addr is in page.*/ - IF !CC R4 = R6; /* If so, advance the address and finish loop.*/ - IF !CC JUMP .Lisearch_done; -.Lnomatch: - /* Go around again*/ - R5 += 1; - CC = BITTST(R5, 4); /* i.e CC = R5 >= 16*/ - IF !CC JUMP .Lisearch; - -.Lisearch_done: - I0 = R4; /* Fault address we'll search for*/ - - /* set up pointers */ - P0.L = LO(ICPLB_DATA0); - P0.H = HI(ICPLB_DATA0); - - /* The replacement procedure for ICPLBs */ - - P4.L = LO(IMEM_CONTROL); - P4.H = HI(IMEM_CONTROL); - - /* disable cplbs */ - R5 = [P4]; /* Control Register*/ - BITCLR(R5,ENICPLB_P); - CLI R1; - SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */ - .align 8; - [P4] = R5; - SSYNC; - STI R1; - - R1 = -1; /* end point comparison */ - R3 = 16; /* counter */ - - /* Search through CPLBs for first non-locked entry */ - /* Overwrite it by moving everyone else up by 1 */ -.Licheck_lock: - R0 = [P0++]; - R3 = R3 + R1; - CC = R3 == R1; - IF CC JUMP .Lall_locked; - CC = BITTST(R0, 0); /* an invalid entry is good */ - IF !CC JUMP .Lifound_victim; - CC = BITTST(R0,1); /* but a locked entry isn't */ - IF CC JUMP .Licheck_lock; - -.Lifound_victim: -#ifdef CONFIG_CPLB_INFO - R7 = [P0 - 0x104]; - P2.L = _ipdt_table; - P2.H = _ipdt_table; - P3.L = _ipdt_swapcount_table; - P3.H = _ipdt_swapcount_table; - P3 += -4; -.Licount: - R2 = [P2]; /* address from config table */ - P2 += 8; - P3 += 8; - CC = R2==-1; - IF CC JUMP .Licount_done; - CC = R7==R2; - IF !CC JUMP .Licount; - R7 = [P3]; - R7 += 1; - [P3] = R7; - CSYNC; -.Licount_done: -#endif - LC0=R3; - LSETUP(.Lis_move,.Lie_move) LC0; -.Lis_move: - R0 = [P0]; - [P0 - 4] = R0; - R0 = [P0 - 0x100]; - [P0-0x104] = R0; -.Lie_move:P0+=4; - - /* We've made space in the ICPLB table, so that ICPLB15 - * is now free to be overwritten. Next, we have to determine - * which CPLB we need to install, from the configuration - * table. This is a matter of getting the start-of-page - * addresses and page-lengths from the config table, and - * determining whether the fault address falls within that - * range. - */ - - P2.L = _ipdt_table; - P2.H = _ipdt_table; -#ifdef CONFIG_CPLB_INFO - P3.L = _ipdt_swapcount_table; - P3.H = _ipdt_swapcount_table; - P3 += -8; -#endif - P0.L = _page_size_table; - P0.H = _page_size_table; - - /* Retrieve our fault address (which may have been advanced - * because the faulting instruction crossed a page boundary). - */ - - R0 = I0; - - /* An extraction pattern, to get the page-size bits from - * the CPLB data entry. Bits 16-17, so two bits at posn 16. - */ - - R1 = ((16<<8)|2); -.Linext: R4 = [P2++]; /* address from config table */ - R2 = [P2++]; /* data from config table */ -#ifdef CONFIG_CPLB_INFO - P3 += 8; -#endif - - CC = R4 == -1; /* End of config table*/ - IF CC JUMP .Lno_page_in_table; - - /* See if failed address > start address */ - CC = R4 <= R0(IU); - IF !CC JUMP .Linext; - - /* extract page size (17:16)*/ - R3 = EXTRACT(R2, R1.L) (Z); - - /* add page size to addr to get range */ - - P5 = R3; - P5 = P0 + (P5 << 2); /* scaled, for int access*/ - R3 = [P5]; - R3 = R3 + R4; - - /* See if failed address < (start address + page size) */ - CC = R0 < R3(IU); - IF !CC JUMP .Linext; - - /* We've found a CPLB in the config table that covers - * the faulting address, so install this CPLB into the - * last entry of the table. - */ - - P1.L = LO(ICPLB_DATA15); /* ICPLB_DATA15 */ - P1.H = HI(ICPLB_DATA15); - [P1] = R2; - [P1-0x100] = R4; -#ifdef CONFIG_CPLB_INFO - R3 = [P3]; - R3 += 1; - [P3] = R3; -#endif - - /* P4 points to IMEM_CONTROL, and R5 contains its old - * value, after we disabled ICPLBS. Re-enable them. - */ - - BITSET(R5,ENICPLB_P); - CLI R2; - SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */ - .align 8; - [P4] = R5; - SSYNC; - STI R2; - - ( R7:4,P5:3 ) = [SP++]; - R0 = CPLB_RELOADED; - RTS; - -/* FAILED CASES*/ -.Lno_page_in_table: - ( R7:4,P5:3 ) = [SP++]; - R0 = CPLB_NO_ADDR_MATCH; - RTS; -.Lall_locked: - ( R7:4,P5:3 ) = [SP++]; - R0 = CPLB_NO_UNLOCKED; - RTS; -.Lprot_violation: - ( R7:4,P5:3 ) = [SP++]; - R0 = CPLB_PROT_VIOL; - RTS; - -.Ldcplb_write: - - /* if a DCPLB is marked as write-back (CPLB_WT==0), and - * it is clean (CPLB_DIRTY==0), then a write to the - * CPLB's page triggers a protection violation. We have to - * mark the CPLB as dirty, to indicate that there are - * pending writes associated with the CPLB. - */ - - P4.L = LO(DCPLB_STATUS); - P4.H = HI(DCPLB_STATUS); - P3.L = LO(DCPLB_DATA0); - P3.H = HI(DCPLB_DATA0); - R5 = [P4]; - - /* A protection violation can be caused by more than just writes - * to a clean WB page, so we have to ensure that: - * - It's a write - * - to a clean WB page - * - and is allowed in the mode the access occurred. - */ - - CC = BITTST(R5, 16); /* ensure it was a write*/ - IF !CC JUMP .Lprot_violation; - - /* to check the rest, we have to retrieve the DCPLB.*/ - - /* The low half of DCPLB_STATUS is a bit mask*/ - - R2 = R5.L (Z); /* indicating which CPLB triggered the event.*/ - R3 = 30; /* so we can use this to determine the offset*/ - R2.L = SIGNBITS R2; - R2 = R2.L (Z); /* into the DCPLB table.*/ - R3 = R3 - R2; - P4 = R3; - P3 = P3 + (P4<<2); - R3 = [P3]; /* Retrieve the CPLB*/ - - /* Now we can check whether it's a clean WB page*/ - - CC = BITTST(R3, 14); /* 0==WB, 1==WT*/ - IF CC JUMP .Lprot_violation; - CC = BITTST(R3, 7); /* 0 == clean, 1 == dirty*/ - IF CC JUMP .Lprot_violation; - - /* Check whether the write is allowed in the mode that was active.*/ - - R2 = 1<<3; /* checking write in user mode*/ - CC = BITTST(R5, 17); /* 0==was user, 1==was super*/ - R5 = CC; - R2 <<= R5; /* if was super, check write in super mode*/ - R2 = R3 & R2; - CC = R2 == 0; - IF CC JUMP .Lprot_violation; - - /* It's a genuine write-to-clean-page.*/ - - BITSET(R3, 7); /* mark as dirty*/ - [P3] = R3; /* and write back.*/ - NOP; - CSYNC; - ( R7:4,P5:3 ) = [SP++]; - R0 = CPLB_RELOADED; - RTS; - -.Ldcplb_miss_compare: - - /* Data CPLB Miss event. We need to choose a CPLB to - * evict, and then locate a new CPLB to install from the - * config table, that covers the faulting address. - */ - - P1.L = LO(DCPLB_DATA15); - P1.H = HI(DCPLB_DATA15); - - P4.L = LO(DCPLB_FAULT_ADDR); - P4.H = HI(DCPLB_FAULT_ADDR); - R4 = [P4]; - I0 = R4; - - /* The replacement procedure for DCPLBs*/ - - R6 = R1; /* Save for later*/ - - /* Turn off CPLBs while we work.*/ - P4.L = LO(DMEM_CONTROL); - P4.H = HI(DMEM_CONTROL); - R5 = [P4]; - BITCLR(R5,ENDCPLB_P); - CLI R0; - SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */ - .align 8; - [P4] = R5; - SSYNC; - STI R0; - - /* Start looking for a CPLB to evict. Our order of preference - * is: invalid CPLBs, clean CPLBs, dirty CPLBs. Locked CPLBs - * are no good. - */ - - I1.L = LO(DCPLB_DATA0); - I1.H = HI(DCPLB_DATA0); - P1 = 2; - P2 = 16; - I2.L = _dcplb_preference; - I2.H = _dcplb_preference; - LSETUP(.Lsdsearch1, .Ledsearch1) LC0 = P1; -.Lsdsearch1: - R0 = [I2++]; /* Get the bits we're interested in*/ - P0 = I1; /* Go back to start of table*/ - LSETUP (.Lsdsearch2, .Ledsearch2) LC1 = P2; -.Lsdsearch2: - R1 = [P0++]; /* Fetch each installed CPLB in turn*/ - R2 = R1 & R0; /* and test for interesting bits.*/ - CC = R2 == 0; /* If none are set, it'll do.*/ - IF !CC JUMP .Lskip_stack_check; - - R2 = [P0 - 0x104]; /* R2 - PageStart */ - P3.L = _page_size_table; /* retrieve end address */ - P3.H = _page_size_table; /* retrieve end address */ - R3 = 0x1002; /* 16th - position, 2 bits -length */ -#if ANOMALY_05000209 - nop; /* Anomaly 05000209 */ -#endif - R7 = EXTRACT(R1,R3.l); - R7 = R7 << 2; /* Page size index offset */ - P5 = R7; - P3 = P3 + P5; - R7 = [P3]; /* page size in bytes */ - - R7 = R2 + R7; /* R7 - PageEnd */ - R4 = SP; /* Test SP is in range */ - - CC = R7 < R4; /* if PageEnd < SP */ - IF CC JUMP .Ldfound_victim; - R3 = 0x284; /* stack length from start of trap till - * the point. - * 20 stack locations for future modifications - */ - R4 = R4 + R3; - CC = R4 < R2; /* if SP + stacklen < PageStart */ - IF CC JUMP .Ldfound_victim; -.Lskip_stack_check: - -.Ledsearch2: NOP; -.Ledsearch1: NOP; - - /* If we got here, we didn't find a DCPLB we considered - * replacable, which means all of them were locked. - */ - - JUMP .Lall_locked; -.Ldfound_victim: - -#ifdef CONFIG_CPLB_INFO - R7 = [P0 - 0x104]; - P2.L = _dpdt_table; - P2.H = _dpdt_table; - P3.L = _dpdt_swapcount_table; - P3.H = _dpdt_swapcount_table; - P3 += -4; -.Ldicount: - R2 = [P2]; - P2 += 8; - P3 += 8; - CC = R2==-1; - IF CC JUMP .Ldicount_done; - CC = R7==R2; - IF !CC JUMP .Ldicount; - R7 = [P3]; - R7 += 1; - [P3] = R7; -.Ldicount_done: -#endif - - /* Clean down the hardware loops*/ - R2 = 0; - LC1 = R2; - LC0 = R2; - - /* There's a suitable victim in [P0-4] (because we've - * advanced already). - */ - -.LDdoverwrite: - - /* [P0-4] is a suitable victim CPLB, so we want to - * overwrite it by moving all the following CPLBs - * one space closer to the start. - */ - - R1.L = LO(DCPLB_DATA16); /* DCPLB_DATA15 + 4 */ - R1.H = HI(DCPLB_DATA16); - R0 = P0; - - /* If the victim happens to be in DCPLB15, - * we don't need to move anything. - */ - - CC = R1 == R0; - IF CC JUMP .Lde_moved; - R1 = R1 - R0; - R1 >>= 2; - P1 = R1; - LSETUP(.Lds_move, .Lde_move) LC0=P1; -.Lds_move: - R0 = [P0++]; /* move data */ - [P0 - 8] = R0; - R0 = [P0-0x104] /* move address */ -.Lde_move: [P0-0x108] = R0; - - /* We've now made space in DCPLB15 for the new CPLB to be - * installed. The next stage is to locate a CPLB in the - * config table that covers the faulting address. - */ - -.Lde_moved:NOP; - R0 = I0; /* Our faulting address */ - - P2.L = _dpdt_table; - P2.H = _dpdt_table; -#ifdef CONFIG_CPLB_INFO - P3.L = _dpdt_swapcount_table; - P3.H = _dpdt_swapcount_table; - P3 += -8; -#endif - - P1.L = _page_size_table; - P1.H = _page_size_table; - - /* An extraction pattern, to retrieve bits 17:16.*/ - - R1 = (16<<8)|2; -.Ldnext: R4 = [P2++]; /* address */ - R2 = [P2++]; /* data */ -#ifdef CONFIG_CPLB_INFO - P3 += 8; -#endif - - CC = R4 == -1; - IF CC JUMP .Lno_page_in_table; - - /* See if failed address > start address */ - CC = R4 <= R0(IU); - IF !CC JUMP .Ldnext; - - /* extract page size (17:16)*/ - R3 = EXTRACT(R2, R1.L) (Z); - - /* add page size to addr to get range */ - - P5 = R3; - P5 = P1 + (P5 << 2); - R3 = [P5]; - R3 = R3 + R4; - - /* See if failed address < (start address + page size) */ - CC = R0 < R3(IU); - IF !CC JUMP .Ldnext; - - /* We've found the CPLB that should be installed, so - * write it into CPLB15, masking off any caching bits - * if necessary. - */ - - P1.L = LO(DCPLB_DATA15); - P1.H = HI(DCPLB_DATA15); - - /* If the DCPLB has cache bits set, but caching hasn't - * been enabled, then we want to mask off the cache-in-L1 - * bit before installing. Moreover, if caching is off, we - * also want to ensure that the DCPLB has WT mode set, rather - * than WB, since WB pages still trigger first-write exceptions - * even when not caching is off, and the page isn't marked as - * cachable. Finally, we could mark the page as clean, not dirty, - * but we choose to leave that decision to the user; if the user - * chooses to have a CPLB pre-defined as dirty, then they always - * pay the cost of flushing during eviction, but don't pay the - * cost of first-write exceptions to mark the page as dirty. - */ - -#ifdef CONFIG_BFIN_WT - BITSET(R6, 14); /* Set WT*/ -#endif - - [P1] = R2; - [P1-0x100] = R4; -#ifdef CONFIG_CPLB_INFO - R3 = [P3]; - R3 += 1; - [P3] = R3; -#endif - - /* We've installed the CPLB, so re-enable CPLBs. P4 - * points to DMEM_CONTROL, and R5 is the value we - * last wrote to it, when we were disabling CPLBs. - */ - - BITSET(R5,ENDCPLB_P); - CLI R2; - .align 8; - [P4] = R5; - SSYNC; - STI R2; - - ( R7:4,P5:3 ) = [SP++]; - R0 = CPLB_RELOADED; - RTS; -ENDPROC(_cplb_mgr) - -.data -.align 4; -_page_size_table: -.byte4 0x00000400; /* 1K */ -.byte4 0x00001000; /* 4K */ -.byte4 0x00100000; /* 1M */ -.byte4 0x00400000; /* 4M */ - -.align 4; -_dcplb_preference: -.byte4 0x00000001; /* valid bit */ -.byte4 0x00000002; /* lock bit */ diff --git a/arch/blackfin/mach-common/dpmc.S b/arch/blackfin/mach-common/dpmc.S deleted file mode 100644 index 39fbc286110..00000000000 --- a/arch/blackfin/mach-common/dpmc.S +++ /dev/null @@ -1,418 +0,0 @@ -/* - * File: arch/blackfin/mach-common/dpmc.S - * Based on: - * Author: LG Soft India - * - * Created: ? - * Description: Watchdog Timer APIs - * - * 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/blackfin.h> -#include <asm/mach/irq.h> - -.text - -ENTRY(_unmask_wdog_wakeup_evt) - [--SP] = ( R7:0, P5:0 ); -#if defined(CONFIG_BF561) - P0.H = hi(SICA_IWR1); - P0.L = lo(SICA_IWR1); -#else - P0.h = HI(SIC_IWR); - P0.l = LO(SIC_IWR); -#endif - R7 = [P0]; -#if defined(CONFIG_BF561) - BITSET(R7, 27); -#else - BITSET(R7,(IRQ_WATCH - IVG7)); -#endif - [P0] = R7; - SSYNC; - - ( R7:0, P5:0 ) = [SP++]; - RTS; - -.LWRITE_TO_STAT: - /* When watch dog timer is enabled, a write to STAT will load the - * contents of CNT to STAT - */ - R7 = 0x0000(z); -#if defined(CONFIG_BF561) - P0.h = HI(WDOGA_STAT); - P0.l = LO(WDOGA_STAT); -#else - P0.h = HI(WDOG_STAT); - P0.l = LO(WDOG_STAT); -#endif - [P0] = R7; - SSYNC; - JUMP .LSKIP_WRITE_TO_STAT; - -ENTRY(_program_wdog_timer) - [--SP] = ( R7:0, P5:0 ); -#if defined(CONFIG_BF561) - P0.h = HI(WDOGA_CNT); - P0.l = LO(WDOGA_CNT); -#else - P0.h = HI(WDOG_CNT); - P0.l = LO(WDOG_CNT); -#endif - [P0] = R0; - SSYNC; - -#if defined(CONFIG_BF561) - P0.h = HI(WDOGA_CTL); - P0.l = LO(WDOGA_CTL); -#else - P0.h = HI(WDOG_CTL); - P0.l = LO(WDOG_CTL); -#endif - R7 = W[P0](Z); - CC = BITTST(R7,1); - if !CC JUMP .LWRITE_TO_STAT; - CC = BITTST(R7,2); - if !CC JUMP .LWRITE_TO_STAT; - -.LSKIP_WRITE_TO_STAT: -#if defined(CONFIG_BF561) - P0.h = HI(WDOGA_CTL); - P0.l = LO(WDOGA_CTL); -#else - P0.h = HI(WDOG_CTL); - P0.l = LO(WDOG_CTL); -#endif - R7 = W[P0](Z); - BITCLR(R7,1); /* Enable GP event */ - BITSET(R7,2); - W[P0] = R7.L; - SSYNC; - NOP; - - R7 = W[P0](Z); - BITCLR(R7,4); /* Enable the wdog counter */ - W[P0] = R7.L; - SSYNC; - - ( R7:0, P5:0 ) = [SP++]; - RTS; - -ENTRY(_clear_wdog_wakeup_evt) - [--SP] = ( R7:0, P5:0 ); - -#if defined(CONFIG_BF561) - P0.h = HI(WDOGA_CTL); - P0.l = LO(WDOGA_CTL); -#else - P0.h = HI(WDOG_CTL); - P0.l = LO(WDOG_CTL); -#endif - R7 = 0x0AD6(Z); - W[P0] = R7.L; - SSYNC; - - R7 = W[P0](Z); - BITSET(R7,15); - W[P0] = R7.L; - SSYNC; - - R7 = W[P0](Z); - BITSET(R7,1); - BITSET(R7,2); - W[P0] = R7.L; - SSYNC; - - ( R7:0, P5:0 ) = [SP++]; - RTS; - -ENTRY(_disable_wdog_timer) - [--SP] = ( R7:0, P5:0 ); -#if defined(CONFIG_BF561) - P0.h = HI(WDOGA_CTL); - P0.l = LO(WDOGA_CTL); -#else - P0.h = HI(WDOG_CTL); - P0.l = LO(WDOG_CTL); -#endif - R7 = 0xAD6(Z); - W[P0] = R7.L; - SSYNC; - ( R7:0, P5:0 ) = [SP++]; - RTS; - -#if !defined(CONFIG_BF561) - -.section .l1.text - -ENTRY(_sleep_mode) - [--SP] = ( R7:0, P5:0 ); - [--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); - BITSET (R1, 3); - W[P0] = R1.L; - - CLI R2; - SSYNC; - IDLE; - STI R2; - - call _test_pll_locked; - - R0 = IWR_ENABLE(0); - call _set_sic_iwr; - - P0.H = hi(PLL_CTL); - P0.L = lo(PLL_CTL); - R7 = w[p0](z); - BITCLR (R7, 3); - BITCLR (R7, 5); - w[p0] = R7.L; - IDLE; - call _test_pll_locked; - - RETS = [SP++]; - ( R7:0, P5:0 ) = [SP++]; - RTS; - -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; - - CLI R4; - - call _set_sic_iwr; - - call _set_sdram_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_sdram_srfs; - - call _test_pll_locked; - - R0 = IWR_ENABLE(0); - 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; - IDLE; - call _test_pll_locked; - - STI R4; - - RETS = [SP++]; - ( R7:0, P5:0 ) = [SP++]; - RTS; - -ENTRY(_sleep_deeper) - [--SP] = ( R7:0, P5:0 ); - [--SP] = RETS; - - CLI R4; - - P3 = R0; - R0 = IWR_ENABLE(0); - call _set_sic_iwr; - call _set_sdram_srfs; - - /* 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); - R0.L = 0xF; - W[P0] = R0.l; - - P0.H = hi(PLL_CTL); - P0.L = lo(PLL_CTL); - R5 = W[P0](z); - R0.L = (CONFIG_MIN_VCO_HZ/CONFIG_CLKIN_HZ) << 9; - W[P0] = R0.l; - - SSYNC; - IDLE; - - call _test_pll_locked; - - P0.H = hi(VR_CTL); - P0.L = lo(VR_CTL); - R7 = W[P0](z); - R1 = 0x6; - R1 <<= 16; - R2 = 0x0404(Z); - R1 = R1|R2; - - R2 = DEPOSIT(R7, R1); - W[P0] = R2; - - SSYNC; - IDLE; - - call _test_pll_locked; - - P0.H = hi(PLL_CTL); - P0.L = lo(PLL_CTL); - R0 = W[P0](z); - BITSET (R0, 3); - W[P0] = R0.L; - - R0 = P3; - call _set_sic_iwr; - - SSYNC; - IDLE; - - call _test_pll_locked; - - R0 = IWR_ENABLE(0); - call _set_sic_iwr; - - P0.H = hi(VR_CTL); - P0.L = lo(VR_CTL); - W[P0]= R7; - - SSYNC; - IDLE; - - call _test_pll_locked; - - P0.H = hi(PLL_DIV); - P0.L = lo(PLL_DIV); - W[P0]= R6; - - P0.H = hi(PLL_CTL); - P0.L = lo(PLL_CTL); - w[p0] = R5; - IDLE; - call _test_pll_locked; - - call _unset_sdram_srfs; - - STI R4; - - RETS = [SP++]; - ( R7:0, P5:0 ) = [SP++]; - RTS; - -ENTRY(_set_sdram_srfs) - /* set the sdram to self refresh mode */ - P0.H = hi(EBIU_SDGCTL); - P0.L = lo(EBIU_SDGCTL); - R2 = [P0]; - R3.H = hi(SRFS); - R3.L = lo(SRFS); - R2 = R2|R3; - [P0] = R2; - ssync; - RTS; - -ENTRY(_unset_sdram_srfs) - /* set the sdram out of self refresh mode */ - P0.H = hi(EBIU_SDGCTL); - P0.L = lo(EBIU_SDGCTL); - R2 = [P0]; - R3.H = hi(SRFS); - R3.L = lo(SRFS); - R3 = ~R3; - R2 = R2&R3; - [P0] = R2; - ssync; - RTS; - -ENTRY(_set_sic_iwr) - P0.H = hi(SIC_IWR); - P0.L = lo(SIC_IWR); - [P0] = R0; - SSYNC; - RTS; - -ENTRY(_set_rtc_istat) - P0.H = hi(RTC_ISTAT); - P0.L = lo(RTC_ISTAT); - w[P0] = R0.L; - SSYNC; - RTS; - -ENTRY(_test_pll_locked) - P0.H = hi(PLL_STAT); - P0.L = lo(PLL_STAT); -1: - R0 = W[P0] (Z); - CC = BITTST(R0,5); - IF !CC JUMP 1b; - RTS; -#endif diff --git a/arch/blackfin/mach-common/dpmc.c b/arch/blackfin/mach-common/dpmc.c new file mode 100644 index 00000000000..724a8c5f557 --- /dev/null +++ b/arch/blackfin/mach-common/dpmc.c @@ -0,0 +1,164 @@ +/* + * Copyright 2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/types.h> +#include <linux/cpufreq.h> + +#include <asm/delay.h> +#include <asm/dpmc.h> + +#define DRIVER_NAME "bfin dpmc" + +struct bfin_dpmc_platform_data *pdata; + +/** + * bfin_set_vlev - Update VLEV field in VR_CTL Reg. + * Avoid BYPASS sequence + */ +static void bfin_set_vlev(unsigned int vlev) +{ + unsigned pll_lcnt; + + pll_lcnt = bfin_read_PLL_LOCKCNT(); + + bfin_write_PLL_LOCKCNT(1); + bfin_write_VR_CTL((bfin_read_VR_CTL() & ~VLEV) | vlev); + bfin_write_PLL_LOCKCNT(pll_lcnt); +} + +/** + * bfin_get_vlev - Get CPU specific VLEV from platform device data + */ +static unsigned int bfin_get_vlev(unsigned int freq) +{ + int i; + + if (!pdata) + goto err_out; + + freq >>= 16; + + for (i = 0; i < pdata->tabsize; i++) + if (freq <= (pdata->tuple_tab[i] & 0xFFFF)) + return pdata->tuple_tab[i] >> 16; + +err_out: + printk(KERN_WARNING "DPMC: No suitable CCLK VDDINT voltage pair found\n"); + return VLEV_120; +} + +#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 */ + 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; +} + +static struct notifier_block vreg_cpufreq_notifier_block = { + .notifier_call = vreg_cpufreq_notifier +}; +#endif /* CONFIG_CPU_FREQ */ + +/** + * bfin_dpmc_probe - + * + */ +static int bfin_dpmc_probe(struct platform_device *pdev) +{ + if (pdev->dev.platform_data) + pdata = pdev->dev.platform_data; + else + return -EINVAL; + + return cpufreq_register_notifier(&vreg_cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); +} + +/** + * bfin_dpmc_remove - + */ +static int bfin_dpmc_remove(struct platform_device *pdev) +{ + pdata = NULL; + return cpufreq_unregister_notifier(&vreg_cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); +} + +struct platform_driver bfin_dpmc_device_driver = { + .probe = bfin_dpmc_probe, + .remove = bfin_dpmc_remove, + .driver = { + .name = DRIVER_NAME, + } +}; +module_platform_driver(bfin_dpmc_device_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("cpu power management driver for Blackfin"); +MODULE_LICENSE("GPL"); diff --git a/arch/blackfin/mach-common/dpmc_modes.S b/arch/blackfin/mach-common/dpmc_modes.S new file mode 100644 index 00000000000..de99f3aac2c --- /dev/null +++ b/arch/blackfin/mach-common/dpmc_modes.S @@ -0,0 +1,320 @@ +/* + * Copyright 2004-2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/linkage.h> +#include <asm/blackfin.h> +#include <mach/irq.h> +#include <asm/dpmc.h> + +.section .l1.text +ENTRY(_sleep_mode) + [--SP] = (R7:4, P5:3); + [--SP] = RETS; + + call _set_sic_iwr; + + P0.H = hi(PLL_CTL); + P0.L = lo(PLL_CTL); + R1 = W[P0](z); + BITSET (R1, 3); + W[P0] = R1.L; + + CLI R2; + SSYNC; + IDLE; + STI R2; + + call _test_pll_locked; + + 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); + R7 = w[p0](z); + BITCLR (R7, 3); + BITCLR (R7, 5); + w[p0] = R7.L; + IDLE; + + bfin_init_pm_bench_cycles; + + call _test_pll_locked; + + RETS = [SP++]; + (R7:4, P5:3) = [SP++]; + RTS; +ENDPROC(_sleep_mode) + +/* + * 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. + */ + +ENTRY(_hibernate_mode) + /* Save/setup the regs we need early for minor pipeline optimization */ + R4 = R0; + + 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; + SSYNC; + + /* Finally, we climb into our cave to hibernate */ + W[P3] = R4.L; + + bfin_init_pm_bench_cycles; + + CLI R2; + IDLE; +.Lforever: + jump .Lforever; +ENDPROC(_hibernate_mode) + +ENTRY(_sleep_deeper) + [--SP] = (R7:4, P5:3); + [--SP] = RETS; + + CLI R4; + + P3 = R0; + P4 = R1; + P5 = R2; + + R0 = IWR_ENABLE(0); + R1 = IWR_DISABLE_ALL; + R2 = IWR_DISABLE_ALL; + + call _set_sic_iwr; + call _set_dram_srfs; /* Set SDRAM Self Refresh */ + + P0.H = hi(PLL_DIV); + P0.L = lo(PLL_DIV); + R6 = W[P0](z); + R0.L = 0xF; + W[P0] = R0.l; /* Set Max VCO to SCLK divider */ + + P0.H = hi(PLL_CTL); + P0.L = lo(PLL_CTL); + R5 = W[P0](z); + R0.L = (CONFIG_MIN_VCO_HZ/CONFIG_CLKIN_HZ) << 9; + W[P0] = R0.l; /* Set Min CLKIN to VCO multiplier */ + + SSYNC; + IDLE; + + call _test_pll_locked; + + P0.H = hi(VR_CTL); + P0.L = lo(VR_CTL); + R7 = W[P0](z); + R1 = 0x6; + R1 <<= 16; + R2 = 0x0404(Z); + R1 = R1|R2; + + R2 = DEPOSIT(R7, R1); + W[P0] = R2; /* Set Min Core Voltage */ + + SSYNC; + IDLE; + + call _test_pll_locked; + + R0 = P3; + R1 = P4; + R3 = P5; + call _set_sic_iwr; /* Set Awake from IDLE */ + + P0.H = hi(PLL_CTL); + P0.L = lo(PLL_CTL); + R0 = W[P0](z); + BITSET (R0, 3); + W[P0] = R0.L; /* Turn CCLK OFF */ + SSYNC; + IDLE; + + call _test_pll_locked; + + R0 = IWR_ENABLE(0); + R1 = IWR_DISABLE_ALL; + R2 = IWR_DISABLE_ALL; + + call _set_sic_iwr; /* Set Awake from IDLE PLL */ + + P0.H = hi(VR_CTL); + P0.L = lo(VR_CTL); + W[P0]= R7; + + SSYNC; + IDLE; + + bfin_init_pm_bench_cycles; + + call _test_pll_locked; + + P0.H = hi(PLL_DIV); + P0.L = lo(PLL_DIV); + W[P0]= R6; /* Restore CCLK and SCLK divider */ + + P0.H = hi(PLL_CTL); + P0.L = lo(PLL_CTL); + w[p0] = R5; /* Restore VCO multiplier */ + IDLE; + call _test_pll_locked; + + call _unset_dram_srfs; /* SDRAM Self Refresh Off */ + + STI R4; + + RETS = [SP++]; + (R7:4, P5:3) = [SP++]; + RTS; +ENDPROC(_sleep_deeper) + +ENTRY(_set_dram_srfs) + /* set the dram to self refresh mode */ + SSYNC; +#if defined(EBIU_RSTCTL) /* DDR */ + P0.H = hi(EBIU_RSTCTL); + P0.L = lo(EBIU_RSTCTL); + R2 = [P0]; + 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]; + BITSET(R2, 24); /* SRFS enter self-refresh mode */ + [P0] = R2; + SSYNC; + +1: + R2 = w[P1]; + SSYNC; + cc = BITTST(R2, 1); /* SDSRA poll self-refresh status */ + if !cc jump 1b; + + R2 = [P0]; + 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(EBIU_RSTCTL) /* DDR */ + P0.H = hi(EBIU_RSTCTL); + P0.L = lo(EBIU_RSTCTL); + R2 = [P0]; + 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]; + 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 + + SSYNC; + RTS; +ENDPROC(_unset_dram_srfs) + +ENTRY(_set_sic_iwr) +#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); + [P0] = R0; +#endif + + SSYNC; + RTS; +ENDPROC(_set_sic_iwr) + +ENTRY(_test_pll_locked) + P0.H = hi(PLL_STAT); + P0.L = lo(PLL_STAT); +1: + R0 = W[P0] (Z); + 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 e3ad5802868..86b5a095c5a 100644 --- a/arch/blackfin/mach-common/entry.S +++ b/arch/blackfin/mach-common/entry.S @@ -1,65 +1,30 @@ /* - * 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 * after a timer-interrupt and after each system call. */ - +#include <linux/init.h> #include <linux/linkage.h> #include <linux/unistd.h> #include <asm/blackfin.h> #include <asm/errno.h> +#include <asm/fixed_code.h> #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 - -#if ANOMALY_05000281 -ENTRY(_safe_speculative_execution) - NOP; - NOP; - NOP; - jump _safe_speculative_execution; -ENDPROC(_safe_speculative_execution) -#endif #ifdef CONFIG_EXCPT_IRQ_SYSC_L1 .section .l1.text @@ -71,198 +36,370 @@ ENDPROC(_safe_speculative_execution) * This one does not lower the level to IRQ5, and thus can be used to * patch up CPLB misses on the kernel stack. */ -ENTRY(_ex_dcplb) #if ANOMALY_05000261 +#define _ex_dviol _ex_workaround_261 +#define _ex_dmiss _ex_workaround_261 +#define _ex_dmult _ex_workaround_261 + +ENTRY(_ex_workaround_261) /* * Work around an anomaly: if we see a new DCPLB fault, return * without doing anything. Then, if we get the same fault again, * handle it. */ - p5.l = _last_cplb_fault_retx; - p5.h = _last_cplb_fault_retx; - r7 = [p5]; + P4 = R7; /* Store EXCAUSE */ + + GET_PDA(p5, r7); + r7 = [p5 + PDA_LFRETX]; r6 = retx; - [p5] = r6; + [p5 + PDA_LFRETX] = r6; cc = r6 == r7; - if !cc jump _return_from_exception; + if !cc jump _bfin_return_from_exception; /* fall through */ + R7 = P4; + R6 = VEC_CPLB_M; /* Data CPLB Miss */ + cc = R6 == R7; + if cc jump _ex_dcplb_miss (BP); +#ifdef CONFIG_MPU + R6 = VEC_CPLB_VL; /* Data CPLB Violation */ + cc = R6 == R7; + if cc jump _ex_dcplb_viol (BP); +#endif + /* Handle Data CPLB Protection Violation + * and Data CPLB Multiple Hits - Linux Trap Zero + */ + jump _ex_trap_c; +ENDPROC(_ex_workaround_261) + +#else +#ifdef CONFIG_MPU +#define _ex_dviol _ex_dcplb_viol +#else +#define _ex_dviol _ex_trap_c +#endif +#define _ex_dmiss _ex_dcplb_miss +#define _ex_dmult _ex_trap_c #endif -ENDPROC(_ex_dcplb) -ENTRY(_ex_icplb) + +ENTRY(_ex_dcplb_viol) +ENTRY(_ex_dcplb_miss) +ENTRY(_ex_icplb_miss) (R7:6,P5:4) = [sp++]; - ASTAT = [sp++]; - SAVE_ALL_SYS - call __cplb_hdr; - DEBUG_START_HWTRACE(p5, r7) - RESTORE_ALL_SYS + /* 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; + + DEBUG_HWTRACE_SAVE(p5, r7) + + sp += -12; + call _cplb_hdr; + sp += 12; + CC = R0 == 0; + IF !CC JUMP _handle_bad_cplb; + +#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_CONTEXT_CPLB + ASTAT = [SP++]; SP = EX_SCRATCH_REG; rtx; -ENDPROC(_ex_icplb) +ENDPROC(_ex_icplb_miss) ENTRY(_ex_syscall) - DEBUG_START_HWTRACE(p5, r7) - (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. */ r7 = retx; r6 = reti; cc = r7 == r6; - if cc jump _return_from_exception - r7 = syscfg; - bitclr (r7, 0); - syscfg = R7; + 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); r6 = [p5]; - cc = bittst(r6, 5); - if !cc jump _ex_trap_c; - p4.l = lo(EVT5); - p4.h = hi(EVT5); - r6.h = _exception_to_level5; - r6.l = _exception_to_level5; - r7 = [p4]; - cc = r6 == r7; - if !cc jump _ex_trap_c; + r7 = 0xffe0 (z); + 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, SYSCFG_SSSTEP_P); + syscfg = R7; + jump _ex_trap_c; + +1: + /* + * We were in an interrupt handler. By convention, all of them save + * SYSCFG with their first instruction, so by checking whether our + * RETX points at the entry point, we can determine whether to allow + * a single step, or whether to clear SYSCFG. + * + * First, find out the interrupt level and the event vector for it. + */ + p5.l = lo(EVT0); + p5.h = hi(EVT0); + p5 += -4; +2: + r7 = rot r7 by -1; + p5 += 4; + if !cc jump 2b; + + /* What we actually do is test for the _second_ instruction in the + * IRQ handler. That way, if there are insns following the restore + * of SYSCFG after leaving the handler, we will not turn off SYSCFG + * for them. */ + + r7 = [p5]; + r7 += 2; + r6 = RETX; + cc = R7 == R6; + if !cc jump _bfin_return_from_exception; + + r7 = syscfg; + bitclr (r7, SYSCFG_SSSTEP_P); /* Turn off single step */ + syscfg = R7; -ENTRY(_return_from_exception) - DEBUG_START_HWTRACE(p5, r7) + /* Fall through to _bfin_return_from_exception. */ +ENDPROC(_ex_single_step) + +ENTRY(_bfin_return_from_exception) #if ANOMALY_05000257 R7=LC0; LC0=R7; 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; rtx; -ENDPROC(_ex_soft_bp) +ENDPROC(_bfin_return_from_exception) ENTRY(_handle_bad_cplb) + DEBUG_HWTRACE_RESTORE(p5, r7) /* 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 */ - - DEBUG_START_HWTRACE(p5, r7) - RESTORE_ALL_SYS - [--sp] = ASTAT; - [--sp] = (R7:6, P5:4); + 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; - /* Disable all interrupts, but make sure level 5 is enabled so - * we can switch to that level. Save the old mask. */ + /* + * 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 + PDA_DCPLB] = r7; + + p4.l = lo(ICPLB_FAULT_ADDR); + p4.h = hi(ICPLB_FAULT_ADDR); + r6 = [p4]; + [p5 + PDA_ICPLB] = r6; + + r6 = retx; + [p5 + PDA_RETX] = r6; + + r6 = SEQSTAT; + [p5 + PDA_SEQSTAT] = r6; + + /* Save the state of single stepping */ + r6 = SYSCFG; + [p5 + PDA_SYSCFG] = r6; + /* Clear it while we handle the exception in IRQ5 mode */ + BITCLR(r6, SYSCFG_SSSTEP_P); + SYSCFG = r6; + + /* Save the current IMASK, since we change in order to jump to level 5 */ cli r6; - p4.l = _excpt_saved_imask; - p4.h = _excpt_saved_imask; - [p4] = 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; - /* Save the excause into a circular buffer, in case the instruction - * which caused this excecptions causes others. + /* 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. */ - P5.l = _in_ptr_excause; - P5.h = _in_ptr_excause; - R7 = [P5]; - R7 += 4; - R6 = 0xF; - R7 = R7 & R6; - [P5] = R7; - R6.l = _excause_circ_buf; - R6.h = _excause_circ_buf; - R7 = R7 + R6; - p5 = R7; - R6 = SEQSTAT; - [P5] = R6; - - DEBUG_START_HWTRACE(p5, r7) - (R7:6,P5:4) = [sp++]; - ASTAT = [sp++]; - SP = EX_SCRATCH_REG; + [--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 - - r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ - SP += -12; - call _double_fault_c; - SP += 12; + /* 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; + pseudo_long_call _double_fault_c, p5; + SP += 12; .L_double_fault_panic: JUMP .L_double_fault_panic @@ -271,62 +408,55 @@ ENDPROC(_double_fault) ENTRY(_exception_to_level5) SAVE_ALL_SYS - /* Restore interrupt mask. We haven't pushed RETI, so this - * doesn't enable interrupts until we return from this handler. */ - p4.l = _excpt_saved_imask; - p4.h = _excpt_saved_imask; - r6 = [p4]; - sti r6; + GET_PDA(p5, r7); /* Fetch current PDA */ + r6 = [p5 + PDA_RETX]; + [sp + PT_PC] = r6; + + r6 = [p5 + PDA_SYSCFG]; + [sp + PT_SYSCFG] = 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; - csync; - - p2.l = lo(IPEND); - p2.h = hi(IPEND); + [p4] = r7; csync; - r0 = [p2]; /* Read current IPEND */ - [sp + PT_IPEND] = r0; /* Store IPEND */ - /* Pop the excause from the circular buffer and push it on the stack - * (in the right place - if you change the location of SEQSTAT, you - * must change this offset. +#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). */ -.L_excep_to_5_again: - P5.l = _out_ptr_excause; - P5.h = _out_ptr_excause; - R7 = [P5]; - R7 += 4; - R6 = 0xF; - R7 = R7 & R6; - [P5] = R7; - R6.l = _excause_circ_buf; - R6.h = _excause_circ_buf; - R7 = R7 + R6; - P5 = R7; - R1 = [P5]; - [SP + 8] = r1; + [--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; - /* See if anything else is in the exception buffer - * if there is, process it + /* If interrupts were off during the exception (IPEND[4] = 1), turn them off + * before we return. */ - P5.l = _out_ptr_excause; - P5.h = _out_ptr_excause; - P4.l = _in_ptr_excause; - P4.h = _in_ptr_excause; - R6 = [P5]; - R7 = [P4]; - CC = R6 == R7; - if ! CC JUMP .L_excep_to_5_again + 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 @@ -339,13 +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); - DEBUG_STOP_HWTRACE(p5, r7) + [--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; @@ -362,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); @@ -437,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; @@ -454,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 */ @@ -491,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: @@ -525,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); @@ -548,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) @@ -598,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); @@ -613,9 +777,7 @@ ENTRY(_ret_from_exception) [sp + PT_IPEND] = r0; 1: - r1 = 0x37(Z); - r2 = ~r1; - r2.h = 0; + r2 = LO(~0x37) (Z); r0 = r2 & r0; cc = r0 == 0; if !cc jump 4f; /* if not return to user mode, get out */ @@ -654,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; @@ -670,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); @@ -684,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); @@ -693,41 +989,58 @@ ENTRY(_return_from_int) p1.h = _schedule_and_signal_from_int; [p0] = p1; csync; -#if ANOMALY_05000281 - r0.l = _safe_speculative_execution; - r0.h = _safe_speculative_execution; +#if ANOMALY_05000281 || ANOMALY_05000461 + r0.l = lo(SAFE_USER_INSTRUCTION); + r0.h = hi(SAFE_USER_INSTRUCTION); reti = r0; #endif r0 = 0x801f (z); 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 - r0.l = _safe_speculative_execution; - r0.h = _safe_speculative_execution; +#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. */ @@ -745,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. @@ -774,21 +1109,7 @@ _schedule_and_signal: 1: RESTORE_CONTEXT rti; -ENDPROC(_lower_to_irq14) - -/* Make sure when we start, that the circular buffer is initialized properly - * R0 and P0 are call clobbered, so we can use them here. - */ -ENTRY(_init_exception_buff) - r0 = 0; - p0.h = _in_ptr_excause; - p0.l = _in_ptr_excause; - [p0] = r0; - p0.h = _out_ptr_excause; - p0.l = _out_ptr_excause; - [p0] = r0; - rts; -ENDPROC(_init_exception_buff) +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 @@ -843,7 +1164,7 @@ ENTRY(_ex_trace_buff_full) LC0 = [sp++]; P2 = [sp++]; P3 = [sp++]; - jump _return_from_exception; + jump _bfin_return_from_exception; ENDPROC(_ex_trace_buff_full) #if CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN == 4 @@ -860,12 +1181,14 @@ ENTRY(_software_trace_buff) .endr #endif /* CONFIG_DEBUG_BFIN_HWTRACE_EXPAND */ -#if CONFIG_EARLY_PRINTK -.section .init.text +#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); @@ -873,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; @@ -883,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; @@ -896,6 +1215,7 @@ ENTRY(_early_trap) call _early_trap_c; SP += 12; ENDPROC(_early_trap) +__FINIT #endif /* CONFIG_EARLY_PRINTK */ /* @@ -908,16 +1228,22 @@ ENDPROC(_early_trap) #else .data #endif + ENTRY(_ex_table) /* entry for each EXCAUSE[5:0] * This table must be in sync with the table in ./kernel/traps.c * 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_replaceable /* 0x04 - User Defined */ + .long _ex_trap_c /* 0x04 - User Defined - dump trace buffer */ .long _ex_replaceable /* 0x05 - User Defined */ .long _ex_replaceable /* 0x06 - User Defined */ .long _ex_replaceable /* 0x07 - User Defined */ @@ -952,16 +1278,16 @@ ENTRY(_ex_table) .long _ex_trap_c /* 0x20 - Reserved */ .long _ex_trap_c /* 0x21 - Undefined Instruction */ .long _ex_trap_c /* 0x22 - Illegal Instruction Combination */ - .long _ex_dcplb /* 0x23 - Data CPLB Protection Violation */ + .long _ex_dviol /* 0x23 - Data CPLB Protection Violation */ .long _ex_trap_c /* 0x24 - Data access misaligned */ .long _ex_trap_c /* 0x25 - Unrecoverable Event */ - .long _ex_dcplb /* 0x26 - Data CPLB Miss */ - .long _ex_trap_c /* 0x27 - Data CPLB Multiple Hits - Linux Trap Zero */ + .long _ex_dmiss /* 0x26 - Data CPLB Miss */ + .long _ex_dmult /* 0x27 - Data CPLB Multiple Hits - Linux Trap Zero */ .long _ex_trap_c /* 0x28 - Emulation Watchpoint */ .long _ex_trap_c /* 0x29 - Instruction fetch access error (535 only) */ .long _ex_trap_c /* 0x2A - Instruction fetch misaligned */ - .long _ex_icplb /* 0x2B - Instruction CPLB protection Violation */ - .long _ex_icplb /* 0x2C - Instruction CPLB miss */ + .long _ex_trap_c /* 0x2B - Instruction CPLB protection Violation */ + .long _ex_icplb_miss /* 0x2C - Instruction CPLB miss */ .long _ex_trap_c /* 0x2D - Instruction CPLB Multiple Hits */ .long _ex_trap_c /* 0x2E - Illegal use of Supervisor Resource */ .long _ex_trap_c /* 0x2E - Illegal use of Supervisor Resource */ @@ -987,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 */ @@ -1105,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 @@ -1129,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 @@ -1154,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 @@ -1177,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 */ @@ -1203,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 */ @@ -1260,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 @@ -1334,7 +1660,7 @@ ENTRY(_sys_call_table) .long _sys_epoll_pwait .long _sys_utimensat .long _sys_signalfd - .long _sys_timerfd + .long _sys_timerfd_create .long _sys_eventfd /* 350 */ .long _sys_pread64 .long _sys_pwrite64 @@ -1342,34 +1668,34 @@ ENTRY(_sys_call_table) .long _sys_set_robust_list .long _sys_get_robust_list /* 355 */ .long _sys_fallocate + .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 -_excpt_saved_imask: - .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 -/* - * Single instructions can have multiple faults, which need to be - * handled by traps.c, in irq5. We store the exception cause to ensure - * we don't miss a double fault condition - */ -ENTRY(_in_ptr_excause) - .long 0; -ENTRY(_out_ptr_excause) - .long 0; -ALIGN -ENTRY(_excause_circ_buf) - .rept 4 - .long 0 - .endr +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 c6b32fe0f6e..469ce7282dc 100644 --- a/arch/blackfin/mach-common/interrupt.S +++ b/arch/blackfin/mach-common/interrupt.S @@ -1,42 +1,25 @@ /* - * 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 <linux/autoconf.h> +#include <mach/irq.h> #include <linux/linkage.h> #include <asm/entry.h> #include <asm/asm-offsets.h> #include <asm/trace.h> +#include <asm/traps.h> +#include <asm/thread_info.h> + +#include <asm/context.S> -#include <asm/mach-common/context.S> +.extern _ret_from_exception #ifdef CONFIG_I_ENTRY_L1 .section .l1.text @@ -105,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); @@ -116,74 +106,133 @@ __common_int_entry: fp = 0; #endif -#if ANOMALY_05000283 || ANOMALY_05000315 - cc = r7 == r7; - p5.h = 0xffc0; - p5.l = 0x0014; - 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; + 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; - call _return_from_int; +#endif /* CONFIG_IPIPE */ + pseudo_long_call _return_from_int, p2; .Lcommon_restore_context: RESTORE_CONTEXT rti; /* interrupt routine for ivhw - 5 */ ENTRY(_evt_ivhw) - SAVE_CONTEXT + /* 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 = 0xffc0; - p5.l = 0x0014; - if cc jump 1f; - r7.l = W[p5]; -1: -#endif - trace_buffer_stop(p0, r0); + ANOMALY_283_315_WORKAROUND(p5, r7) - r0 = IRQ_HWERR; - r1 = sp; - -#ifdef CONFIG_HARDWARE_PM - r7 = 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); + r0 = [p2]; + [sp + PT_IPEND] = r0; - SP += -12; - call _irq_panic; - SP += 12; - rti; -#ifdef CONFIG_HARDWARE_PM -.Lcall_do_ovf: + /* set the EXCAUSE to HWERR for trap_c */ + r0 = [sp + PT_SEQSTAT]; + R1.L = LO(VEC_HWERR); + R1.H = HI(VEC_HWERR); + R0 = R0 | R1; + [sp + PT_SEQSTAT] = R0; + r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ SP += -12; - call _pm_overflow; + pseudo_long_call _trap_c, p5; SP += 12; - jump .Lcommon_restore_context; +#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; +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) @@ -214,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-dc.c b/arch/blackfin/mach-common/ints-priority-dc.c deleted file mode 100644 index 2db3546fc87..00000000000 --- a/arch/blackfin/mach-common/ints-priority-dc.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - * File: arch/blackfin/mach-common/ints-priority-dc.c - * Based on: - * Author: - * - * Created: ? - * Description: Set up the interrupt priorities - * - * 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-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/seq_file.h> -#include <linux/irq.h> -#ifdef CONFIG_KGDB -#include <linux/kgdb.h> -#endif -#include <asm/traps.h> -#include <asm/blackfin.h> -#include <asm/gpio.h> -#include <asm/irq_handler.h> - -/* - * NOTES: - * - we have separated the physical Hardware interrupt from the - * levels that the LINUX kernel sees (see the description in irq.h) - * - - */ - -unsigned long irq_flags = 0; - -/* The number of spurious interrupts */ -atomic_t num_spurious; - -struct ivgx { - /* irq number for request_irq, available in mach-bf561/irq.h */ - int irqno; - /* corresponding bit in the SICA_ISR0 register */ - int isrflag0; - /* corresponding bit in the SICA_ISR1 register */ - int isrflag1; -} ivg_table[NR_PERI_INTS]; - -struct ivg_slice { - /* position of first irq in ivg_table for given ivg */ - struct ivgx *ifirst; - struct ivgx *istop; -} ivg7_13[IVG13 - IVG7 + 1]; - -static void search_IAR(void); - -/* - * Search SIC_IAR and fill tables with the irqvalues - * and their positions in the SIC_ISR register. - */ -static void __init search_IAR(void) -{ - unsigned ivg, irq_pos = 0; - for (ivg = 0; ivg <= IVG13 - IVG7; ivg++) { - 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 & - bfin_read32((unsigned long *)SICA_IAR0 + - (irqn >> 3)) >> iar_shift)) { - ivg_table[irq_pos].irqno = IVG7 + irqn; - ivg_table[irq_pos].isrflag0 = - (irqn < 32 ? (1 << irqn) : 0); - ivg_table[irq_pos].isrflag1 = - (irqn < 32 ? 0 : (1 << (irqn - 32))); - ivg7_13[ivg].istop++; - irq_pos++; - } - } - } -} - -/* - * This is for BF561 internal IRQs - */ - -static void ack_noop(unsigned int irq) -{ - /* Dummy function. */ -} - -static void bf561_core_mask_irq(unsigned int irq) -{ - irq_flags &= ~(1 << irq); - if (!irqs_disabled()) - local_irq_enable(); -} - -static void bf561_core_unmask_irq(unsigned int irq) -{ - irq_flags |= 1 << irq; - /* - * If interrupts are enabled, IMASK must contain the same value - * as 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 - * what we need. - */ - if (!irqs_disabled()) - local_irq_enable(); - return; -} - -static void bf561_internal_mask_irq(unsigned int irq) -{ - unsigned long irq_mask; - if ((irq - (IRQ_CORETMR + 1)) < 32) { - irq_mask = (1 << (irq - (IRQ_CORETMR + 1))); - bfin_write_SICA_IMASK0(bfin_read_SICA_IMASK0() & ~irq_mask); - } else { - irq_mask = (1 << (irq - (IRQ_CORETMR + 1) - 32)); - bfin_write_SICA_IMASK1(bfin_read_SICA_IMASK1() & ~irq_mask); - } -} - -static void bf561_internal_unmask_irq(unsigned int irq) -{ - unsigned long irq_mask; - - if ((irq - (IRQ_CORETMR + 1)) < 32) { - irq_mask = (1 << (irq - (IRQ_CORETMR + 1))); - bfin_write_SICA_IMASK0(bfin_read_SICA_IMASK0() | irq_mask); - } else { - irq_mask = (1 << (irq - (IRQ_CORETMR + 1) - 32)); - bfin_write_SICA_IMASK1(bfin_read_SICA_IMASK1() | irq_mask); - } - SSYNC(); -} - -static struct irq_chip bf561_core_irqchip = { - .ack = ack_noop, - .mask = bf561_core_mask_irq, - .unmask = bf561_core_unmask_irq, -}; - -static struct irq_chip bf561_internal_irqchip = { - .ack = ack_noop, - .mask = bf561_internal_mask_irq, - .unmask = bf561_internal_unmask_irq, -}; - -#ifdef CONFIG_IRQCHIP_DEMUX_GPIO -static unsigned short gpio_enabled[gpio_bank(MAX_BLACKFIN_GPIOS)]; -static unsigned short gpio_edge_triggered[gpio_bank(MAX_BLACKFIN_GPIOS)]; - -static void bf561_gpio_ack_irq(unsigned int irq) -{ - u16 gpionr = irq - IRQ_PF0; - - if (gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) { - set_gpio_data(gpionr, 0); - SSYNC(); - } -} - -static void bf561_gpio_mask_ack_irq(unsigned int irq) -{ - u16 gpionr = irq - IRQ_PF0; - - if (gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) { - set_gpio_data(gpionr, 0); - SSYNC(); - } - - set_gpio_maska(gpionr, 0); - SSYNC(); -} - -static void bf561_gpio_mask_irq(unsigned int irq) -{ - set_gpio_maska(irq - IRQ_PF0, 0); - SSYNC(); -} - -static void bf561_gpio_unmask_irq(unsigned int irq) -{ - set_gpio_maska(irq - IRQ_PF0, 1); - SSYNC(); -} - -static unsigned int bf561_gpio_irq_startup(unsigned int irq) -{ - unsigned int ret; - u16 gpionr = irq - IRQ_PF0; - - if (!(gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))) { - - ret = gpio_request(gpionr, "IRQ"); - if (ret) - return ret; - - } - - gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); - bf561_gpio_unmask_irq(irq); - - return ret; - -} - -static void bf561_gpio_irq_shutdown(unsigned int irq) -{ - bf561_gpio_mask_irq(irq); - gpio_free(irq - IRQ_PF0); - gpio_enabled[gpio_bank(irq - IRQ_PF0)] &= ~gpio_bit(irq - IRQ_PF0); -} - -static int bf561_gpio_irq_type(unsigned int irq, unsigned int type) -{ - - unsigned int ret; - u16 gpionr = irq - IRQ_PF0; - - - 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; - - } - - 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))) { - - ret = gpio_request(gpionr, "IRQ"); - if (ret) - return ret; - - } - - gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); - } else { - gpio_enabled[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); - return 0; - } - - - set_gpio_dir(gpionr, 0); - set_gpio_inen(gpionr, 1); - - - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { - gpio_edge_triggered[gpio_bank(gpionr)] |= gpio_bit(gpionr); - set_gpio_edge(gpionr, 1); - } else { - set_gpio_edge(gpionr, 0); - gpio_edge_triggered[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); - } - - 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 */ - - SSYNC(); - - 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); - - return 0; -} - -static struct irq_chip bf561_gpio_irqchip = { - .ack = bf561_gpio_ack_irq, - .mask = bf561_gpio_mask_irq, - .mask_ack = bf561_gpio_mask_ack_irq, - .unmask = bf561_gpio_unmask_irq, - .set_type = bf561_gpio_irq_type, - .startup = bf561_gpio_irq_startup, - .shutdown = bf561_gpio_irq_shutdown -}; - -static void bf561_demux_gpio_irq(unsigned int inta_irq, - struct irq_desc *intb_desc) -{ - int irq, flag_d, mask; - u16 gpio; - - switch (inta_irq) { - case IRQ_PROG0_INTA: - irq = IRQ_PF0; - break; - case IRQ_PROG1_INTA: - irq = IRQ_PF16; - break; - case IRQ_PROG2_INTA: - irq = IRQ_PF32; - break; - default: - dump_stack(); - return; - } - - gpio = irq - IRQ_PF0; - - flag_d = get_gpiop_data(gpio); - mask = flag_d & (gpio_enabled[gpio_bank(gpio)] & - get_gpiop_maska(gpio)); - - do { - if (mask & 1) { - struct irq_desc *desc = irq_desc + irq; - desc->handle_irq(irq, desc); - } - irq++; - mask >>= 1; - } while (mask); - - -} - -#endif /* CONFIG_IRQCHIP_DEMUX_GPIO */ - -void __init init_exception_vectors(void) -{ - SSYNC(); - - /* cannot program in software: - * evt0 - emulation (jtag) - * evt1 - reset - */ - bfin_write_EVT2(evt_nmi); - bfin_write_EVT3(trap); - bfin_write_EVT5(evt_ivhw); - bfin_write_EVT6(evt_timer); - bfin_write_EVT7(evt_evt7); - bfin_write_EVT8(evt_evt8); - bfin_write_EVT9(evt_evt9); - bfin_write_EVT10(evt_evt10); - bfin_write_EVT11(evt_evt11); - bfin_write_EVT12(evt_evt12); - bfin_write_EVT13(evt_evt13); - bfin_write_EVT14(evt14_softirq); - bfin_write_EVT15(evt_system_call); - CSYNC(); -} - -/* - * 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 */ - bfin_write_SICA_IMASK0(SIC_UNMASK_ALL); - bfin_write_SICA_IMASK1(SIC_UNMASK_ALL); - SSYNC(); - - bfin_write_SICA_IWR0(IWR_ENABLE_ALL); - bfin_write_SICA_IWR1(IWR_ENABLE_ALL); - - local_irq_disable(); - - init_exception_buff(); - - for (irq = 0; irq <= SYS_IRQS; irq++) { - if (irq <= IRQ_CORETMR) - set_irq_chip(irq, &bf561_core_irqchip); - else - set_irq_chip(irq, &bf561_internal_irqchip); -#ifdef CONFIG_IRQCHIP_DEMUX_GPIO - if ((irq != IRQ_PROG0_INTA) && - (irq != IRQ_PROG1_INTA) && (irq != IRQ_PROG2_INTA)) { -#endif - set_irq_handler(irq, handle_simple_irq); -#ifdef CONFIG_IRQCHIP_DEMUX_GPIO - } else { - set_irq_chained_handler(irq, bf561_demux_gpio_irq); - } -#endif - - } - -#ifdef CONFIG_IRQCHIP_DEMUX_GPIO - for (irq = IRQ_PF0; irq <= IRQ_PF47; irq++) { - set_irq_chip(irq, &bf561_gpio_irqchip); - /* if configured as edge, then will be changed to do_edge_IRQ */ - set_irq_handler(irq, handle_level_irq); - } -#endif - bfin_write_IMASK(0); - CSYNC(); - ilat = bfin_read_ILAT(); - CSYNC(); - bfin_write_ILAT(ilat); - CSYNC(); - - printk(KERN_INFO "Configuring Blackfin Priority Driven Interrupts\n"); - /* IMASK=xxx is equivalent to STI xx or irq_flags=xx, - * local_irq_enable() - */ - program_IAR(); - /* Therefore it's better to setup IARs before interrupts enabled */ - 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; - - return 0; -} - -#ifdef CONFIG_DO_IRQ_L1 -void do_irq(int vec, struct pt_regs *fp)__attribute__((l1_text)); -#endif - -void do_irq(int vec, struct pt_regs *fp) -{ - if (vec == EVT_IVTMR_P) { - vec = IRQ_CORETMR; - } else { - struct ivgx *ivg = ivg7_13[vec - IVG7].ifirst; - struct ivgx *ivg_stop = ivg7_13[vec - IVG7].istop; - unsigned long sic_status0, sic_status1; - - SSYNC(); - sic_status0 = bfin_read_SICA_IMASK0() & bfin_read_SICA_ISR0(); - sic_status1 = bfin_read_SICA_IMASK1() & bfin_read_SICA_ISR1(); - - for (;; ivg++) { - if (ivg >= ivg_stop) { - atomic_inc(&num_spurious); - return; - } else if ((sic_status0 & ivg->isrflag0) || - (sic_status1 & ivg->isrflag1)) - break; - } - vec = ivg->irqno; - } - asm_do_IRQ(vec, fp); - -#ifdef CONFIG_KGDB - kgdb_process_breakpoint(); -#endif -} diff --git a/arch/blackfin/mach-common/ints-priority-sc.c b/arch/blackfin/mach-common/ints-priority-sc.c deleted file mode 100644 index d3b7672b2b9..00000000000 --- a/arch/blackfin/mach-common/ints-priority-sc.c +++ /dev/null @@ -1,919 +0,0 @@ -/* - * File: arch/blackfin/mach-common/ints-priority-sc.c - * Based on: - * Author: - * - * Created: ? - * Description: Set up the interrupt priorities - * - * 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-2007 Analog Devices Inc. - * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include <linux/module.h> -#include <linux/kernel_stat.h> -#include <linux/seq_file.h> -#include <linux/irq.h> -#ifdef CONFIG_KGDB -#include <linux/kgdb.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 - -/* - * NOTES: - * - we have separated the physical Hardware interrupt from the - * levels that the LINUX kernel sees (see the description in irq.h) - * - - */ - -unsigned long irq_flags = 0; - -/* The number of spurious interrupts */ -atomic_t num_spurious; - -struct ivgx { - /* irq number for request_irq, available in mach-bf533/irq.h */ - unsigned int irqno; - /* corresponding bit in the SIC_ISR register */ - unsigned int isrflag; -} ivg_table[NR_PERI_INTS]; - -struct ivg_slice { - /* position of first irq in ivg_table for given ivg */ - struct ivgx *ifirst; - struct ivgx *istop; -} ivg7_13[IVG13 - IVG7 + 1]; - -static void search_IAR(void); - -/* - * Search SIC_IAR and fill tables with the irqvalues - * and their positions in the SIC_ISR register. - */ -static void __init search_IAR(void) -{ - unsigned ivg, irq_pos = 0; - for (ivg = 0; ivg <= IVG13 - IVG7; ivg++) { - 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 & - bfin_read32((unsigned long *)SIC_IAR0 + - (irqn >> 3)) >> iar_shift)) { - ivg_table[irq_pos].irqno = IVG7 + irqn; - ivg_table[irq_pos].isrflag = 1 << (irqn % 32); - ivg7_13[ivg].istop++; - irq_pos++; - } - } - } -} - -/* - * This is for BF533 internal IRQs - */ - -static void ack_noop(unsigned int irq) -{ - /* Dummy function. */ -} - -static void bfin_core_mask_irq(unsigned int irq) -{ - irq_flags &= ~(1 << irq); - if (!irqs_disabled()) - local_irq_enable(); -} - -static void bfin_core_unmask_irq(unsigned int irq) -{ - irq_flags |= 1 << irq; - /* - * If interrupts are enabled, IMASK must contain the same value - * as 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 - * what we need. - */ - if (!irqs_disabled()) - local_irq_enable(); - return; -} - -static void bfin_internal_mask_irq(unsigned int irq) -{ -#ifndef CONFIG_BF54x - bfin_write_SIC_IMASK(bfin_read_SIC_IMASK() & - ~(1 << (irq - (IRQ_CORETMR + 1)))); -#else - unsigned mask_bank, mask_bit; - mask_bank = (irq - (IRQ_CORETMR + 1)) / 32; - mask_bit = (irq - (IRQ_CORETMR + 1)) % 32; - bfin_write_SIC_IMASK(mask_bank, bfin_read_SIC_IMASK(mask_bank) & - ~(1 << mask_bit)); -#endif - SSYNC(); -} - -static void bfin_internal_unmask_irq(unsigned int irq) -{ -#ifndef CONFIG_BF54x - bfin_write_SIC_IMASK(bfin_read_SIC_IMASK() | - (1 << (irq - (IRQ_CORETMR + 1)))); -#else - unsigned mask_bank, mask_bit; - mask_bank = (irq - (IRQ_CORETMR + 1)) / 32; - mask_bit = (irq - (IRQ_CORETMR + 1)) % 32; - bfin_write_SIC_IMASK(mask_bank, bfin_read_SIC_IMASK(mask_bank) | - (1 << mask_bit)); -#endif - SSYNC(); -} - -static struct irq_chip bfin_core_irqchip = { - .ack = ack_noop, - .mask = bfin_core_mask_irq, - .unmask = bfin_core_unmask_irq, -}; - -static struct irq_chip bfin_internal_irqchip = { - .ack = ack_noop, - .mask = bfin_internal_mask_irq, - .unmask = bfin_internal_unmask_irq, -}; - -#ifdef BF537_GENERIC_ERROR_INT_DEMUX -static int error_int_mask; - -static void bfin_generic_error_ack_irq(unsigned int irq) -{ - -} - -static void bfin_generic_error_mask_irq(unsigned int irq) -{ - error_int_mask &= ~(1L << (irq - IRQ_PPI_ERROR)); - - if (!error_int_mask) { - local_irq_disable(); - bfin_write_SIC_IMASK(bfin_read_SIC_IMASK() & - ~(1 << - (IRQ_GENERIC_ERROR - - (IRQ_CORETMR + 1)))); - SSYNC(); - local_irq_enable(); - } -} - -static void bfin_generic_error_unmask_irq(unsigned int irq) -{ - local_irq_disable(); - bfin_write_SIC_IMASK(bfin_read_SIC_IMASK() | 1 << - (IRQ_GENERIC_ERROR - (IRQ_CORETMR + 1))); - SSYNC(); - local_irq_enable(); - - error_int_mask |= 1L << (irq - IRQ_PPI_ERROR); -} - -static struct irq_chip bfin_generic_error_irqchip = { - .ack = bfin_generic_error_ack_irq, - .mask = bfin_generic_error_mask_irq, - .unmask = bfin_generic_error_unmask_irq, -}; - -static void bfin_demux_error_irq(unsigned int int_err_irq, - struct irq_desc *intb_desc) -{ - int irq = 0; - - SSYNC(); - -#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; - - if (irq) { - if (error_int_mask & (1L << (irq - IRQ_PPI_ERROR))) { - struct irq_desc *desc = irq_desc + irq; - desc->handle_irq(irq, desc); - } else { - - 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; - - case IRQ_SPORT1_ERROR: - bfin_write_SPORT1_STAT(SPORT_ERR_MASK); - break; - - case IRQ_CAN_ERROR: - bfin_write_CAN_GIS(CAN_ERR_MASK); - break; - - case IRQ_SPI_ERROR: - bfin_write_SPI_STAT(SPI_ERR_MASK); - break; - - default: - break; - } - - 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", - __FUNCTION__, __FILE__, __LINE__); - -} -#endif /* BF537_GENERIC_ERROR_INT_DEMUX */ - -#if defined(CONFIG_IRQCHIP_DEMUX_GPIO) && !defined(CONFIG_BF54x) - -static unsigned short gpio_enabled[gpio_bank(MAX_BLACKFIN_GPIOS)]; -static unsigned short gpio_edge_triggered[gpio_bank(MAX_BLACKFIN_GPIOS)]; - -static void bfin_gpio_ack_irq(unsigned int irq) -{ - u16 gpionr = irq - IRQ_PF0; - - if (gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) { - set_gpio_data(gpionr, 0); - SSYNC(); - } -} - -static void bfin_gpio_mask_ack_irq(unsigned int irq) -{ - u16 gpionr = irq - IRQ_PF0; - - if (gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) { - set_gpio_data(gpionr, 0); - SSYNC(); - } - - set_gpio_maska(gpionr, 0); - SSYNC(); -} - -static void bfin_gpio_mask_irq(unsigned int irq) -{ - set_gpio_maska(irq - IRQ_PF0, 0); - SSYNC(); -} - -static void bfin_gpio_unmask_irq(unsigned int irq) -{ - set_gpio_maska(irq - IRQ_PF0, 1); - SSYNC(); -} - -static unsigned int bfin_gpio_irq_startup(unsigned int irq) -{ - unsigned int ret; - u16 gpionr = irq - IRQ_PF0; - - if (!(gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))) { - ret = gpio_request(gpionr, "IRQ"); - if (ret) - return ret; - } - - gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); - bfin_gpio_unmask_irq(irq); - - return ret; -} - -static void bfin_gpio_irq_shutdown(unsigned int irq) -{ - bfin_gpio_mask_irq(irq); - gpio_free(irq - IRQ_PF0); - gpio_enabled[gpio_bank(irq - IRQ_PF0)] &= ~gpio_bit(irq - IRQ_PF0); -} - -static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) -{ - - unsigned int ret; - u16 gpionr = irq - IRQ_PF0; - - 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; - } - - 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))) { - ret = gpio_request(gpionr, "IRQ"); - if (ret) - return ret; - } - - gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); - } else { - gpio_enabled[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); - return 0; - } - - set_gpio_dir(gpionr, 0); - set_gpio_inen(gpionr, 1); - - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { - gpio_edge_triggered[gpio_bank(gpionr)] |= gpio_bit(gpionr); - set_gpio_edge(gpionr, 1); - } else { - set_gpio_edge(gpionr, 0); - gpio_edge_triggered[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); - } - - 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 */ - - SSYNC(); - - 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); - - return 0; -} - -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 -}; - -static void bfin_demux_gpio_irq(unsigned int intb_irq, - struct irq_desc *intb_desc) -{ - u16 i; - struct irq_desc *desc; - - for (i = 0; i < MAX_BLACKFIN_GPIOS; i += 16) { - int irq = IRQ_PF0 + i; - int flag_d = get_gpiop_data(i); - int mask = - flag_d & (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 /* CONFIG_IRQCHIP_DEMUX_GPIO */ - -#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]; - -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 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, -}; - -unsigned short get_irq_base(u8 bank, u8 bmap) -{ - - u16 irq_base; - - if (bank < 2) { /*PA-PB */ - irq_base = IRQ_PA0 + bmap * 16; - } else { /*PC-PJ */ - irq_base = IRQ_PC0 + bmap * 16; - } - - return irq_base; - -} - - /* Whenever PINTx_ASSIGN is altered init_pint_lut() must be executed! */ -void init_pint_lut(void) -{ - u16 bank, bit, irq_base, bit_pos; - u32 pint_assign; - u8 bmap; - - memset(irq2pint_lut, IRQ_NOT_AVAIL, sizeof(irq2pint_lut)); - - for (bank = 0; bank < NR_PINT_SYS_IRQS; bank++) { - - pint_assign = pint[bank]->assign; - - for (bit = 0; bit < NR_PINT_BITS; bit++) { - - bmap = (pint_assign >> ((bit / 8) * 8)) & 0xFF; - - irq_base = get_irq_base(bank, bmap); - - irq_base += (bit % 8) + ((bit / 8) & 1 ? 8 : 0); - bit_pos = bit + bank * NR_PINT_BITS; - - pint2irq_lut[bit_pos] = irq_base - SYS_IRQS; - irq2pint_lut[irq_base - SYS_IRQS] = bit_pos; - - } - - } - -} - -static unsigned short gpio_enabled[gpio_bank(MAX_BLACKFIN_GPIOS)]; - -static void bfin_gpio_ack_irq(unsigned int irq) -{ - u8 pint_val = irq2pint_lut[irq - SYS_IRQS]; - - pint[PINT_2_BANK(pint_val)]->request = PINT_BIT(pint_val); - SSYNC(); -} - -static void bfin_gpio_mask_ack_irq(unsigned int irq) -{ - 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_clear = pintbit; - SSYNC(); -} - -static void bfin_gpio_mask_irq(unsigned int irq) -{ - u8 pint_val = irq2pint_lut[irq - SYS_IRQS]; - - pint[PINT_2_BANK(pint_val)]->mask_clear = PINT_BIT(pint_val); - SSYNC(); -} - -static void bfin_gpio_unmask_irq(unsigned int irq) -{ - 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(); -} - -static unsigned int bfin_gpio_irq_startup(unsigned int irq) -{ - unsigned int ret; - u16 gpionr = irq - IRQ_PA0; - u8 pint_val = irq2pint_lut[irq - SYS_IRQS]; - - 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))) { - ret = gpio_request(gpionr, "IRQ"); - if (ret) - return ret; - } - - gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); - bfin_gpio_unmask_irq(irq); - - return ret; -} - -static void bfin_gpio_irq_shutdown(unsigned int irq) -{ - bfin_gpio_mask_irq(irq); - gpio_free(irq - IRQ_PA0); - gpio_enabled[gpio_bank(irq - IRQ_PA0)] &= ~gpio_bit(irq - IRQ_PA0); -} - -static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) -{ - - unsigned int ret; - u16 gpionr = irq - IRQ_PA0; - 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; - - 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; - } - - 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))) { - ret = gpio_request(gpionr, "IRQ"); - if (ret) - return ret; - } - - gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); - } else { - gpio_enabled[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); - return 0; - } - - gpio_direction_input(gpionr); - - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { - pint[bank]->edge_set = pintbit; - } else { - pint[bank]->edge_clear = pintbit; - } - - 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_set = pintbit; /* high or rising edge denoted by zero */ - - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) - pint[bank]->invert_set = pintbit; - else - pint[bank]->invert_set = pintbit; - - SSYNC(); - - 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); - - return 0; -} - -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 -}; - -static void bfin_demux_gpio_irq(unsigned int intb_irq, - struct irq_desc *intb_desc) -{ - u8 bank, pint_val; - u32 request, irq; - struct irq_desc *desc; - - switch (intb_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; - } - - pint_val = bank * NR_PINT_BITS; - - request = pint[bank]->request; - - 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; - } - -} -#endif /* CONFIG_IRQCHIP_DEMUX_GPIO */ - -void __init init_exception_vectors(void) -{ - SSYNC(); - - /* cannot program in software: - * evt0 - emulation (jtag) - * evt1 - reset - */ - bfin_write_EVT2(evt_nmi); - bfin_write_EVT3(trap); - bfin_write_EVT5(evt_ivhw); - bfin_write_EVT6(evt_timer); - bfin_write_EVT7(evt_evt7); - bfin_write_EVT8(evt_evt8); - bfin_write_EVT9(evt_evt9); - bfin_write_EVT10(evt_evt10); - bfin_write_EVT11(evt_evt11); - bfin_write_EVT12(evt_evt12); - bfin_write_EVT13(evt_evt13); - bfin_write_EVT14(evt14_softirq); - bfin_write_EVT15(evt_system_call); - CSYNC(); -} - -/* - * 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 */ -#ifdef CONFIG_BF54x - bfin_write_SIC_IMASK0(SIC_UNMASK_ALL); - bfin_write_SIC_IMASK1(SIC_UNMASK_ALL); - bfin_write_SIC_IMASK2(SIC_UNMASK_ALL); - bfin_write_SIC_IWR0(IWR_ENABLE_ALL); - bfin_write_SIC_IWR1(IWR_ENABLE_ALL); - bfin_write_SIC_IWR2(IWR_ENABLE_ALL); -#else - bfin_write_SIC_IMASK(SIC_UNMASK_ALL); - bfin_write_SIC_IWR(IWR_ENABLE_ALL); -#endif - SSYNC(); - - local_irq_disable(); - -#if defined(CONFIG_IRQCHIP_DEMUX_GPIO) && defined(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); - else - set_irq_chip(irq, &bfin_internal_irqchip); -#ifdef BF537_GENERIC_ERROR_INT_DEMUX - if (irq != IRQ_GENERIC_ERROR) { -#endif - - switch (irq) { -#ifdef CONFIG_IRQCHIP_DEMUX_GPIO -#ifndef CONFIG_BF54x - case IRQ_PROG_INTA: - set_irq_chained_handler(irq, - bfin_demux_gpio_irq); - break; -#if defined(BF537_FAMILY) && !(defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)) - case IRQ_MAC_RX: - set_irq_chained_handler(irq, - bfin_demux_gpio_irq); - break; -#endif -#else - case IRQ_PINT0: - set_irq_chained_handler(irq, - bfin_demux_gpio_irq); - break; - case IRQ_PINT1: - set_irq_chained_handler(irq, - bfin_demux_gpio_irq); - break; - case IRQ_PINT2: - set_irq_chained_handler(irq, - bfin_demux_gpio_irq); - break; - case IRQ_PINT3: - set_irq_chained_handler(irq, - bfin_demux_gpio_irq); - break; -#endif /*CONFIG_BF54x */ -#endif - default: - set_irq_handler(irq, handle_simple_irq); - break; - } - -#ifdef BF537_GENERIC_ERROR_INT_DEMUX - } else { - set_irq_handler(irq, bfin_demux_error_irq); - } -#endif - } -#ifdef BF537_GENERIC_ERROR_INT_DEMUX - for (irq = IRQ_PPI_ERROR; irq <= IRQ_UART1_ERROR; irq++) { - set_irq_chip(irq, &bfin_generic_error_irqchip); - set_irq_handler(irq, handle_level_irq); - } -#endif - -#ifdef CONFIG_IRQCHIP_DEMUX_GPIO -#ifndef CONFIG_BF54x - for (irq = IRQ_PF0; irq < NR_IRQS; irq++) { -#else - for (irq = IRQ_PA0; irq < NR_IRQS; irq++) { -#endif - set_irq_chip(irq, &bfin_gpio_irqchip); - /* if configured as edge, then will be changed to do_edge_IRQ */ - set_irq_handler(irq, handle_level_irq); - } -#endif - bfin_write_IMASK(0); - CSYNC(); - ilat = bfin_read_ILAT(); - CSYNC(); - bfin_write_ILAT(ilat); - CSYNC(); - - printk(KERN_INFO "Configuring Blackfin Priority Driven Interrupts\n"); - /* IMASK=xxx is equivalent to STI xx or irq_flags=xx, - * local_irq_enable() - */ - program_IAR(); - /* Therefore it's better to setup IARs before interrupts enabled */ - 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; - - return 0; -} - -#ifdef CONFIG_DO_IRQ_L1 -void do_irq(int vec, struct pt_regs *fp) __attribute__((l1_text)); -#endif - -void do_irq(int vec, struct pt_regs *fp) -{ - if (vec == EVT_IVTMR_P) { - vec = IRQ_CORETMR; - } else { - struct ivgx *ivg = ivg7_13[vec - IVG7].ifirst; - struct ivgx *ivg_stop = ivg7_13[vec - IVG7].istop; -#ifdef CONFIG_BF54x - unsigned long sic_status[3]; - - SSYNC(); - sic_status[0] = bfin_read_SIC_ISR(0) & bfin_read_SIC_IMASK(0); - sic_status[1] = bfin_read_SIC_ISR(1) & bfin_read_SIC_IMASK(1); - sic_status[2] = bfin_read_SIC_ISR(2) & bfin_read_SIC_IMASK(2); - - for (;; ivg++) { - if (ivg >= ivg_stop) { - atomic_inc(&num_spurious); - return; - } - if (sic_status[(ivg->irqno - IVG7) / 32] & ivg->isrflag) - break; - } -#else - unsigned long sic_status; - SSYNC(); - sic_status = bfin_read_SIC_IMASK() & bfin_read_SIC_ISR(); - - for (;; ivg++) { - if (ivg >= ivg_stop) { - atomic_inc(&num_spurious); - return; - } else if (sic_status & ivg->isrflag) - break; - } -#endif - vec = ivg->irqno; - } - asm_do_IRQ(vec, fp); - -#ifdef CONFIG_KGDB - kgdb_process_breakpoint(); -#endif -} diff --git a/arch/blackfin/mach-common/ints-priority.c b/arch/blackfin/mach-common/ints-priority.c new file mode 100644 index 00000000000..1f94784eab6 --- /dev/null +++ b/arch/blackfin/mach-common/ints-priority.c @@ -0,0 +1,1373 @@ +/* + * 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 + * + * Licensed under the GPL-2 + */ + +#include <linux/module.h> +#include <linux/kernel_stat.h> +#include <linux/seq_file.h> +#include <linux/irq.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> +#include <asm/dpmc.h> +#include <asm/traps.h> + +/* + * NOTES: + * - we have separated the physical Hardware interrupt from the + * levels that the LINUX kernel sees (see the description in irq.h) + * - + */ + +#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 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 + +#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]; + +static struct ivg_slice { + /* position of first irq in ivg_table for given ivg */ + struct ivgx *ifirst; + struct ivgx *istop; +} ivg7_13[IVG13 - IVG7 + 1]; + + +/* + * Search SIC_IAR and fill tables with the irqvalues + * and their positions in the SIC_ISR register. + */ +static void __init search_IAR(void) +{ + unsigned ivg, irq_pos = 0; + for (ivg = 0; ivg <= IVG13 - IVG7; ivg++) { + int irqN; + + ivg7_13[ivg].istop = ivg7_13[ivg].ifirst = &ivg_table[irq_pos]; + + 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 + (irqN >> 3) +#endif + ); + 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 + */ +void bfin_ack_noop(struct irq_data *d) +{ + /* Dummy function. */ +} + +static void bfin_core_mask_irq(struct irq_data *d) +{ + bfin_irq_flags &= ~(1 << d->irq); + if (!hard_irqs_disabled()) + hard_local_irq_enable(); +} + +static void bfin_core_unmask_irq(struct irq_data *d) +{ + bfin_irq_flags |= 1 << d->irq; + /* + * If interrupts are enabled, IMASK must contain the same value + * 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 bfin_irq_flags", so it's exactly + * what we need. + */ + if (!hard_irqs_disabled()) + hard_local_irq_enable(); + return; +} + +#ifndef SEC_GCTL +void bfin_internal_mask_irq(unsigned int irq) +{ + 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 << 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 +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 + hard_local_irq_restore(flags); +} + +#ifdef CONFIG_SMP +static void bfin_internal_unmask_irq_chip(struct irq_data *d) +{ + 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 +static void bfin_internal_unmask_irq_chip(struct irq_data *d) +{ + bfin_internal_unmask_irq(d->irq); +} +#endif + +#if defined(CONFIG_PM) +int bfin_internal_set_wake(unsigned int irq, unsigned int state) +{ + u32 bank, bit, wakeup = 0; + unsigned long flags; + 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; + } + + flags = hard_local_irq_save(); + + if (state) { + bfin_sic_iwr[bank] |= (1 << bit); + vr_wakeup |= wakeup; + + } else { + bfin_sic_iwr[bank] &= ~(1 << bit); + vr_wakeup &= ~wakeup; + } + + hard_local_irq_restore(flags); + + return 0; +} + +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 + +#else /* SEC_GCTL */ +static void bfin_sec_preflow_handler(struct irq_data *d) +{ + unsigned long flags = hard_local_irq_save(); + unsigned int sid = BFIN_SYSIRQ(d->irq); + + bfin_write_SEC_SCI(0, SEC_CSID, sid); + + hard_local_irq_restore(flags); +} + +static void bfin_sec_mask_ack_irq(struct irq_data *d) +{ + unsigned long flags = hard_local_irq_save(); + unsigned int sid = BFIN_SYSIRQ(d->irq); + + bfin_write_SEC_SCI(0, SEC_CSID, sid); + + hard_local_irq_restore(flags); +} + +static void bfin_sec_unmask_irq(struct irq_data *d) +{ + unsigned long flags = hard_local_irq_save(); + unsigned int sid = BFIN_SYSIRQ(d->irq); + + bfin_write32(SEC_END, sid); + + hard_local_irq_restore(flags); +} + +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); + + reg_sctl |= SEC_SCTL_SRC_EN; + bfin_write_SEC_SCTL(sid, reg_sctl); + + hard_local_irq_restore(flags); +} + +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); + + reg_sctl &= ((uint32_t)~SEC_SCTL_SRC_EN); + bfin_write_SEC_SCTL(sid, reg_sctl); + + hard_local_irq_restore(flags); +} + +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); +} + +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); + + 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); + + hard_local_irq_restore(flags); +} + +static void bfin_sec_disable_sci(unsigned int sid) +{ + unsigned long flags = hard_local_irq_save(); + uint32_t reg_sctl = bfin_read_SEC_SCTL(sid); + + reg_sctl &= ((uint32_t)~SEC_SCTL_INT_EN); + bfin_write_SEC_SCTL(sid, reg_sctl); + + hard_local_irq_restore(flags); +} + +static void bfin_sec_enable(struct irq_data *d) +{ + unsigned long flags = hard_local_irq_save(); + unsigned int sid = BFIN_SYSIRQ(d->irq); + + bfin_sec_enable_sci(sid); + bfin_sec_enable_ssi(sid); + + hard_local_irq_restore(flags); +} + +static void bfin_sec_disable(struct irq_data *d) +{ + 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_sec_set_priority(unsigned int sec_int_levels, u8 *sec_int_priority) +{ + 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); +} + +void bfin_sec_raise_irq(unsigned int irq) +{ + unsigned long flags = hard_local_irq_save(); + unsigned int sid = BFIN_SYSIRQ(irq); + + bfin_write32(SEC_RAISE, sid); + + hard_local_irq_restore(flags); +} + +static void init_software_driven_irq(void) +{ + 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); +} + +void bfin_sec_resume(void) +{ + 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); +} + +void handle_sec_sfi_fault(uint32_t gstat) +{ + +} + +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"); + } + } + +} + +void handle_sec_ssi_fault(uint32_t gstat) +{ + uint32_t sid; + uint32_t sstat; + + sid = gstat & SEC_GSTAT_SID; + sstat = bfin_read_SEC_SSTAT(sid); + +} + +void handle_sec_fault(uint32_t sec_gstat) +{ + if (sec_gstat & SEC_GSTAT_ERR) { + + 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; + } + + + } +} + +static struct irqaction bfin_fault_irq = { + .name = "Blackfin fault", +}; + +static irqreturn_t bfin_fault_routine(int irq, void *data) +{ + struct pt_regs *fp = get_irq_regs(); + + switch (irq) { + case IRQ_C0_DBL_FAULT: + double_fault_c(fp); + break; + 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_C0_NMI_L1_PARITY_ERR: + panic("Core 0 NMI L1 parity error"); + break; + case IRQ_SEC_ERR: + pr_err("SEC error\n"); + handle_sec_fault(bfin_read32(SEC_GSTAT)); + break; + default: + panic("Unknown fault %d", irq); + } + + return IRQ_HANDLED; +} +#endif /* SEC_GCTL */ + +static struct irq_chip bfin_core_irqchip = { + .name = "CORE", + .irq_mask = bfin_core_mask_irq, + .irq_unmask = bfin_core_unmask_irq, +}; + +#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 + +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 */ +} + +#if defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE) +static int mac_stat_int_mask; + +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; + } +} + +static void bfin_mac_status_mask_irq(struct irq_data *d) +{ + unsigned int irq = d->irq; + + 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); +} + +static void bfin_mac_status_unmask_irq(struct irq_data *d) +{ + unsigned int irq = d->irq; + +#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); +} + +#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 + +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, +}; + +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 inline void bfin_set_irq_handler(unsigned irq, irq_flow_handler_t handle) +{ +#ifdef CONFIG_IPIPE + handle = handle_level_irq; +#endif + __irq_set_handler_locked(irq, handle); +} + +#ifdef CONFIG_GPIO_ADI + +static DECLARE_BITMAP(gpio_enabled, MAX_BLACKFIN_GPIOS); + +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(struct irq_data *d) +{ + unsigned int irq = d->irq; + u32 gpionr = irq_to_gpio(irq); + + if (!irqd_is_level_type(d)) + set_gpio_data(gpionr, 0); + + set_gpio_maska(gpionr, 0); +} + +static void bfin_gpio_mask_irq(struct irq_data *d) +{ + set_gpio_maska(irq_to_gpio(d->irq), 0); +} + +static void bfin_gpio_unmask_irq(struct irq_data *d) +{ + set_gpio_maska(irq_to_gpio(d->irq), 1); +} + +static unsigned int bfin_gpio_irq_startup(struct irq_data *d) +{ + u32 gpionr = irq_to_gpio(d->irq); + + if (__test_and_set_bit(gpionr, gpio_enabled)) + bfin_gpio_irq_prepare(gpionr); + + bfin_gpio_unmask_irq(d); + + return 0; +} + +static void bfin_gpio_irq_shutdown(struct irq_data *d) +{ + u32 gpionr = irq_to_gpio(d->irq); + + bfin_gpio_mask_irq(d); + __clear_bit(gpionr, gpio_enabled); + bfin_gpio_irq_free(gpionr); +} + +static int bfin_gpio_irq_type(struct irq_data *d, unsigned int type) +{ + 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 (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)) { + + 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); + + } else { + __clear_bit(gpionr, gpio_enabled); + return 0; + } + + 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); + set_gpio_data(gpionr, 0); + + } else { + set_gpio_edge(gpionr, 0); + set_gpio_inen(gpionr, 1); + } + + 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; +} + +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); + + 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) +{ + 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 IRQ_PORTG_INTA: + irq = IRQ_PG0; + break; + case IRQ_PORTH_INTA: + irq = IRQ_PH0; + break; +#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: + BUG(); + return; + } + + bfin_demux_gpio_block(irq); +} + +#ifdef CONFIG_PM + +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); +} + +#else + +# define bfin_gpio_set_wake NULL + +#endif + +static struct irq_chip bfin_gpio_irqchip = { + .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, +}; + +#endif + +#ifdef CONFIG_PM + +#ifdef SEC_GCTL +static u32 save_pint_sec_ctl[NR_PINT_SYS_IRQS]; + +static int sec_suspend(void) +{ + u32 bank; + + 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 + +#endif + +void init_exception_vectors(void) +{ + /* cannot program in software: + * evt0 - emulation (jtag) + * evt1 - reset + */ + bfin_write_EVT2(evt_nmi); + bfin_write_EVT3(trap); + bfin_write_EVT5(evt_ivhw); + bfin_write_EVT6(evt_timer); + bfin_write_EVT7(evt_evt7); + bfin_write_EVT8(evt_evt8); + bfin_write_EVT9(evt_evt9); + bfin_write_EVT10(evt_evt10); + bfin_write_EVT11(evt_evt11); + bfin_write_EVT12(evt_evt12); + bfin_write_EVT13(evt_evt13); + 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 */ +#ifdef SIC_IMASK0 + bfin_write_SIC_IMASK0(SIC_UNMASK_ALL); + bfin_write_SIC_IMASK1(SIC_UNMASK_ALL); +# 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(); + + for (irq = 0; irq <= SYS_IRQS; irq++) { + if (irq <= IRQ_CORETMR) + irq_set_chip(irq, &bfin_core_irqchip); + else + irq_set_chip(irq, &bfin_internal_irqchip); + + switch (irq) { +#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: +#elif defined(CONFIG_BF52x) || defined(CONFIG_BF51x) + case IRQ_PORTF_INTA: + case IRQ_PORTG_INTA: + case IRQ_PORTH_INTA: +#elif defined(CONFIG_BF561) + 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 +#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; +#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: +#ifdef CONFIG_IPIPE + irq_set_handler(irq, handle_level_irq); +#else + irq_set_handler(irq, handle_simple_irq); +#endif + break; + } + } + + 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 */ +#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(); + CSYNC(); + bfin_write_ILAT(ilat); + CSYNC(); + + printk(KERN_INFO "Configuring Blackfin Priority Driven Interrupts\n"); + /* IMASK=xxx is equivalent to STI xx or bfin_irq_flags=xx, + * local_irq_enable() + */ + program_IAR(); + /* Therefore it's better to setup IARs before interrupts enabled */ + search_IAR(); + + /* 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; + + + /* 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_DISABLE_ALL); +#endif + return 0; +} + +#ifdef CONFIG_DO_IRQ_L1 +__attribute__((l1_text)) +#endif +static int vec_to_irq(int vec) +{ + 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 { + sic_status[0] = bfin_read_SIC_ISR0() & bfin_read_SIC_IMASK0(); + sic_status[1] = bfin_read_SIC_ISR1() & bfin_read_SIC_IMASK1(); + } +#endif +#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 + 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); + + local_irq_disable(); + + 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 +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; + } + } + } +#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; + + 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 f05e3dadaf3..00000000000 --- a/arch/blackfin/mach-common/irqpanic.c +++ /dev/null @@ -1,194 +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) -{ - int sig = 0; - siginfo_t info; - -#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 - - printk(KERN_EMERG "\n"); - printk(KERN_EMERG "Exception: IRQ 0x%x entered\n", reason); - printk(KERN_EMERG " code=[0x%08lx], stack frame=0x%08lx, " - " bad PC=0x%08lx\n", - (unsigned long)regs->seqstat, - (unsigned long)regs, - (unsigned long)regs->pc); - if (reason == 0x5) { - printk(KERN_EMERG "----------- HARDWARE ERROR -----------\n"); - - /* There is only need to check for Hardware Errors, since other - * EXCEPTIONS are handled in TRAPS.c (MH) - */ - switch (regs->seqstat & SEQSTAT_HWERRCAUSE) { - case (SEQSTAT_HWERRCAUSE_SYSTEM_MMR): /* System MMR Error */ - info.si_code = BUS_ADRALN; - sig = SIGBUS; - printk(KERN_EMERG HWC_x2); - break; - case (SEQSTAT_HWERRCAUSE_EXTERN_ADDR): /* External Memory Addressing Error */ - info.si_code = BUS_ADRERR; - sig = SIGBUS; - printk(KERN_EMERG HWC_x3); - break; - case (SEQSTAT_HWERRCAUSE_PERF_FLOW): /* Performance Monitor Overflow */ - printk(KERN_EMERG HWC_x12); - break; - case (SEQSTAT_HWERRCAUSE_RAISE_5): /* RAISE 5 instruction */ - printk(KERN_EMERG HWC_x18); - break; - default: /* Reserved */ - printk(KERN_EMERG HWC_default); - break; - } - } - - regs->ipend = bfin_read_IPEND(); - dump_bfin_regs(regs, (void *)regs->pc); - if (0 == (info.si_signo = sig) || 0 == user_mode(regs)) /* in kernelspace */ - panic("Unhandled IRQ or exceptions!\n"); - else { /* in userspace */ - info.si_errno = 0; - info.si_addr = (void *)regs->pc; - force_sig_info(sig, &info, current); - } -} - -#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 28b87fe9ce3..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 = 0xFFFFFF87 (X); - 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 b1030272220..1387a94bcfd 100644 --- a/arch/blackfin/mach-common/pm.c +++ b/arch/blackfin/mach-common/pm.c @@ -1,156 +1,253 @@ /* - * 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: Power management for the bfin + * 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-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 + * based on arm/mach-omap/pm.c + * Copyright 2001, Cliff Brake <cbrake@accelent.com> and others */ -#include <linux/pm.h> +#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) -{ -#ifdef CONFIG_PM_WAKEUP_BY_GPIO - gpio_pm_wakeup_request(CONFIG_PM_WAKEUP_GPIO_NUMBER, WAKEUP_TYPE); #endif +} -#if defined(CONFIG_PM_WAKEUP_BY_GPIO) || defined(CONFIG_PM_WAKEUP_GPIO_API) - { - u32 flags; - - local_irq_save(flags); +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); - sleep_deeper(gpio_pm_setup()); /*Goto Sleep*/ + return 0; +} - gpio_pm_restore(); +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); - bfin_write_SIC_IWR(IWR_ENABLE_ALL); + return 0; +} - local_irq_restore(flags); +#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 -#if defined(CONFIG_PM_WAKEUP_GPIO_BY_SIC_IWR) - sleep_deeper(CONFIG_PM_WAKEUP_SIC_IWR); - bfin_write_SIC_IWR(IWR_ENABLE_ALL); -#endif /* CONFIG_PM_WAKEUP_GPIO_BY_SIC_IWR */ -} +int bfin_pm_suspend_mem_enter(void) +{ + int ret; +#ifndef CONFIG_BF60x + int wakeup; +#endif + unsigned char *memptr = kmalloc(L1_CODE_LENGTH + L1_DATA_A_LENGTH + + L1_DATA_B_LENGTH + L1_SCRATCH_LENGTH, + GFP_ATOMIC); -/* - * bfin_pm_prepare - Do preliminary suspend work. - * @state: suspend state we're entering. - * - */ -static int bfin_pm_prepare(suspend_state_t state) -{ - int error = 0; + if (memptr == NULL) { + panic("bf53x_suspend_l1_mem malloc failed"); + return -ENOMEM; + } - switch (state) { - case PM_SUSPEND_STANDBY: - break; +#ifndef CONFIG_BF60x + wakeup = bfin_read_VR_CTL() & ~FREQ; + wakeup |= SCKELOW; - case PM_SUSPEND_MEM: - return -ENOTSUPP; +#ifdef CONFIG_PM_BFIN_WAKE_PH6 + wakeup |= PHYWE; +#endif +#ifdef CONFIG_PM_BFIN_WAKE_GP + wakeup |= GPWE; +#endif +#endif - default: - return -EINVAL; + ret = blackfin_dma_suspend(); + + if (ret) { + kfree(memptr); + return ret; } - return error; +#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_cpu_pm->enter(PM_SUSPEND_MEM); +#endif + + 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; } /* - * bfin_pm_enter - Actually enter a sleep state. - * @state: State we're entering. + * bfin_pm_valid - Tell the PM core that we only support the standby sleep + * state + * @state: suspend state we're checking. * */ -static int bfin_pm_enter(suspend_state_t state) +static int bfin_pm_valid(suspend_state_t state) { - switch (state) { - case PM_SUSPEND_STANDBY: - bfin_pm_suspend_standby_enter(); - break; - - case PM_SUSPEND_MEM: - return -ENOTSUPP; - - default: - return -EINVAL; - } - - return 0; + 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 + ); } /* - * bfin_pm_finish - Finish up suspend sequence. - * @state: State we're coming out of. + * bfin_pm_enter - Actually enter a sleep state. + * @state: State we're entering. * - * This is called after we wake back up (or if entering the sleep state - * failed). */ -static int bfin_pm_finish(suspend_state_t state) +static int bfin_pm_enter(suspend_state_t state) { switch (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; } @@ -158,21 +255,44 @@ static int bfin_pm_finish(suspend_state_t state) return 0; } -static int bfin_pm_valid(suspend_state_t state) +#ifdef CONFIG_BFIN_PM_WAKEUP_TIME_BENCH +void bfin_pm_end(void) { - return (state == PM_SUSPEND_STANDBY); + 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 -struct pm_ops bfin_pm_ops = { - .prepare = bfin_pm_prepare, +static const struct platform_suspend_ops bfin_pm_ops = { .enter = bfin_pm_enter, - .finish = bfin_pm_finish, .valid = bfin_pm_valid, +#ifdef CONFIG_BFIN_PM_WAKEUP_TIME_BENCH + .end = bfin_pm_end, +#endif }; static int __init bfin_pm_init(void) { - pm_set_ops(&bfin_pm_ops); + suspend_set_ops(&bfin_pm_ops); return 0; } 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 |
