diff options
author | Timothy Pearson <tpearson@raptorengineering.com> | 2017-08-23 14:45:25 -0500 |
---|---|---|
committer | Timothy Pearson <tpearson@raptorengineering.com> | 2017-08-23 14:45:25 -0500 |
commit | fcbb27b0ec6dcbc5a5108cb8fb19eae64593d204 (patch) | |
tree | 22962a4387943edc841c72a4e636a068c66d58fd /arch/blackfin/mach-common | |
download | ast2050-linux-kernel-fcbb27b0ec6dcbc5a5108cb8fb19eae64593d204.zip ast2050-linux-kernel-fcbb27b0ec6dcbc5a5108cb8fb19eae64593d204.tar.gz |
Initial import of modified Linux 2.6.28 tree
Original upstream URL:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git | branch linux-2.6.28.y
Diffstat (limited to 'arch/blackfin/mach-common')
-rw-r--r-- | arch/blackfin/mach-common/Makefile | 12 | ||||
-rw-r--r-- | arch/blackfin/mach-common/arch_checks.c | 64 | ||||
-rw-r--r-- | arch/blackfin/mach-common/cache.S | 95 | ||||
-rw-r--r-- | arch/blackfin/mach-common/cpufreq.c | 198 | ||||
-rw-r--r-- | arch/blackfin/mach-common/dpmc.c | 137 | ||||
-rw-r--r-- | arch/blackfin/mach-common/dpmc_modes.S | 799 | ||||
-rw-r--r-- | arch/blackfin/mach-common/entry.S | 1552 | ||||
-rw-r--r-- | arch/blackfin/mach-common/head.S | 305 | ||||
-rw-r--r-- | arch/blackfin/mach-common/interrupt.S | 247 | ||||
-rw-r--r-- | arch/blackfin/mach-common/ints-priority.c | 1152 | ||||
-rw-r--r-- | arch/blackfin/mach-common/irqpanic.c | 146 | ||||
-rw-r--r-- | arch/blackfin/mach-common/lock.S | 223 | ||||
-rw-r--r-- | arch/blackfin/mach-common/pm.c | 340 |
13 files changed, 5270 insertions, 0 deletions
diff --git a/arch/blackfin/mach-common/Makefile b/arch/blackfin/mach-common/Makefile new file mode 100644 index 0000000..e6ed57c --- /dev/null +++ b/arch/blackfin/mach-common/Makefile @@ -0,0 +1,12 @@ +# +# arch/blackfin/mach-common/Makefile +# + +obj-y := \ + cache.o entry.o head.o \ + interrupt.o irqpanic.o arch_checks.o ints-priority.o + +obj-$(CONFIG_BFIN_ICACHE_LOCK) += lock.o +obj-$(CONFIG_PM) += pm.o dpmc_modes.o +obj-$(CONFIG_CPU_FREQ) += cpufreq.o +obj-$(CONFIG_CPU_VOLTAGE) += dpmc.o diff --git a/arch/blackfin/mach-common/arch_checks.c b/arch/blackfin/mach-common/arch_checks.c new file mode 100644 index 0000000..98133b9 --- /dev/null +++ b/arch/blackfin/mach-common/arch_checks.c @@ -0,0 +1,64 @@ +/* + * File: arch/blackfin/mach-common/arch_checks.c + * Based on: + * Author: Robin Getz <rgetz@blackfin.uclinux.org> + * + * Created: 25Jul07 + * Description: Do some checking to make sure things are OK + * + * 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 + */ + +#include <asm/fixed_code.h> +#include <mach/anomaly.h> +#include <asm/clocks.h> + +#ifdef CONFIG_BFIN_KERNEL_CLOCK + +# if (CONFIG_VCO_HZ > CONFIG_MAX_VCO_HZ) +# error "VCO selected is more than maximum value. Please change the VCO multipler" +# endif + +# if (CONFIG_SCLK_HZ > CONFIG_MAX_SCLK_HZ) +# error "Sclk value selected is more than maximum. Please select a proper value for SCLK multiplier" +# endif + +# if (CONFIG_SCLK_HZ < CONFIG_MIN_SCLK_HZ) +# error "Sclk value selected is less than minimum. Please select a proper value for SCLK multiplier" +# endif + +# if (ANOMALY_05000273) && (CONFIG_SCLK_HZ * 2 > CONFIG_CCLK_HZ) +# error "ANOMALY 05000273, please make sure CCLK is at least 2x SCLK" +# endif + +# if (CONFIG_SCLK_HZ > CONFIG_CCLK_HZ) && (CONFIG_SCLK_HZ != CONFIG_CLKIN_HZ) && (CONFIG_CCLK_HZ != CONFIG_CLKIN_HZ) +# error "Please select sclk less than cclk" +# endif + +#endif /* CONFIG_BFIN_KERNEL_CLOCK */ + +#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 diff --git a/arch/blackfin/mach-common/cache.S b/arch/blackfin/mach-common/cache.S new file mode 100644 index 0000000..a028e94 --- /dev/null +++ b/arch/blackfin/mach-common/cache.S @@ -0,0 +1,95 @@ +/* + * Blackfin cache control code + * + * Copyright 2004-2008 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/linkage.h> +#include <asm/blackfin.h> +#include <asm/cache.h> +#include <asm/page.h> + +.text + +/* 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 optflushins optnopins label + + R2 = -L1_CACHE_BYTES; + + /* start = (start & -L1_CACHE_BYTES) */ + R0 = R0 & R2; + + /* end = ((end - 1) & -L1_CACHE_BYTES) + L1_CACHE_BYTES; */ + R1 += -1; + R1 = R1 & R2; + R1 += L1_CACHE_BYTES; + + /* count = (end - start) >> L1_CACHE_SHIFT */ + R2 = R1 - R0; + R2 >>= L1_CACHE_SHIFT; + P1 = R2; + +.ifnb \label +\label : +.endif + P0 = R0; + LSETUP (1f, 2f) LC1 = P1; +1: +.ifnb \optflushins + \optflushins [P0]; +.endif +.ifb \optnopins +2: +.endif + \flushins [P0++]; +.ifnb \optnopins +2: \optnopins; +.endif + + RTS; +.endm + +/* Invalidate all instruction cache lines assocoiated with this memory area */ +ENTRY(_blackfin_icache_flush_range) + do_flush IFLUSH, , nop +ENDPROC(_blackfin_icache_flush_range) + +/* Flush all cache lines assocoiated with this area of memory. */ +ENTRY(_blackfin_icache_dcache_flush_range) + do_flush FLUSH, IFLUSH +ENDPROC(_blackfin_icache_dcache_flush_range) + +/* Throw away all D-cached data in specified region without any obligation to + * 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) + do_flush FLUSHINV +ENDPROC(_blackfin_dcache_invalidate_range) + +/* Flush all data cache lines assocoiated with this memory area */ +ENTRY(_blackfin_dcache_flush_range) + 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); + jump .Ldfr; +ENDPROC(_blackfin_dflush_page) diff --git a/arch/blackfin/mach-common/cpufreq.c b/arch/blackfin/mach-common/cpufreq.c new file mode 100644 index 0000000..dda5443 --- /dev/null +++ b/arch/blackfin/mach-common/cpufreq.c @@ -0,0 +1,198 @@ +/* + * File: arch/blackfin/mach-common/cpufreq.c + * Based on: + * Author: + * + * Created: + * Description: Blackfin core clock scaling + * + * Modified: + * Copyright 2004-2008 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/cpufreq.h> +#include <linux/fs.h> +#include <asm/blackfin.h> +#include <asm/time.h> + + +/* this is the table of CCLK frequencies, in Hz */ +/* .index is the entry in the auxillary dpm_state_table[] */ +static struct cpufreq_frequency_table bfin_freq_table[] = { + { + .frequency = CPUFREQ_TABLE_END, + .index = 0, + }, + { + .frequency = CPUFREQ_TABLE_END, + .index = 1, + }, + { + .frequency = CPUFREQ_TABLE_END, + .index = 2, + }, + { + .frequency = CPUFREQ_TABLE_END, + .index = 0, + }, +}; + +static struct bfin_dpm_state { + unsigned int csel; /* system clock divider */ + unsigned int tscale; /* change the divider on the core timer interrupt */ +} dpm_state_table[3]; + +/* + normalized to maximum frequncy offset for CYCLES, + used in time-ts cycles clock source, but could be used + somewhere also. + */ +unsigned long long __bfin_cycles_off; +unsigned int __bfin_cycles_mod; + +/**************************************************************************/ + +static unsigned int bfin_getfreq_khz(unsigned int cpu) +{ + /* The driver only support single cpu */ + if (cpu != 0) + return -1; + + return get_cclk() / 1000; +} + + +static int bfin_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + unsigned int index, plldiv, tscale; + unsigned long flags, cclk_hz; + struct cpufreq_freqs freqs; + cycles_t cycles; + + if (cpufreq_frequency_table_target(policy, bfin_freq_table, + target_freq, relation, &index)) + return -EINVAL; + + cclk_hz = bfin_freq_table[index].frequency; + + freqs.old = bfin_getfreq_khz(0); + freqs.new = cclk_hz; + freqs.cpu = 0; + + pr_debug("cpufreq: changing cclk to %lu; target = %u, oldfreq = %u\n", + cclk_hz, target_freq, freqs.old); + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + local_irq_save(flags); + plldiv = (bfin_read_PLL_DIV() & SSEL) | dpm_state_table[index].csel; + tscale = dpm_state_table[index].tscale; + bfin_write_PLL_DIV(plldiv); + /* we have to adjust the core timer, because it is using cclk */ + bfin_write_TSCALE(tscale); + cycles = get_cycles(); + SSYNC(); + cycles += 10; /* ~10 cycles we loose after get_cycles() */ + __bfin_cycles_off += (cycles << __bfin_cycles_mod) - (cycles << index); + __bfin_cycles_mod = index; + local_irq_restore(flags); + /* TODO: just test case for cycles clock source, remove later */ + pr_debug("cpufreq: done\n"); + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; +} + +static int bfin_verify_speed(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, bfin_freq_table); +} + +static int __init __bfin_cpu_init(struct cpufreq_policy *policy) +{ + + unsigned long cclk, sclk, csel, min_cclk; + int index; + + if (policy->cpu != 0) + return -EINVAL; + + cclk = get_cclk() / 1000; + sclk = get_sclk() / 1000; + +#if ANOMALY_05000273 || (!defined(CONFIG_BF54x) && defined(CONFIG_BFIN_DCACHE)) + min_cclk = sclk * 2; +#else + min_cclk = sclk; +#endif + csel = ((bfin_read_PLL_DIV() & CSEL) >> 4); + + for (index = 0; (cclk >> index) >= min_cclk && csel <= 3; index++, csel++) { + bfin_freq_table[index].frequency = cclk >> index; + dpm_state_table[index].csel = csel << 4; /* Shift now into PLL_DIV bitpos */ + dpm_state_table[index].tscale = (TIME_SCALE / (1 << csel)) - 1; + + pr_debug("cpufreq: freq:%d csel:0x%x tscale:%d\n", + bfin_freq_table[index].frequency, + dpm_state_table[index].csel, + dpm_state_table[index].tscale); + } + + policy->cpuinfo.transition_latency = (bfin_read_PLL_LOCKCNT() / (sclk / 1000000)) * 1000; + /*Now ,only support one cpu */ + policy->cur = cclk; + cpufreq_frequency_table_get_attr(bfin_freq_table, policy->cpu); + return cpufreq_frequency_table_cpuinfo(policy, bfin_freq_table); +} + +static struct freq_attr *bfin_freq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver bfin_driver = { + .verify = bfin_verify_speed, + .target = bfin_target, + .get = bfin_getfreq_khz, + .init = __bfin_cpu_init, + .name = "bfin cpufreq", + .owner = THIS_MODULE, + .attr = bfin_freq_attr, +}; + +static int __init bfin_cpu_init(void) +{ + return cpufreq_register_driver(&bfin_driver); +} + +static void __exit bfin_cpu_exit(void) +{ + cpufreq_unregister_driver(&bfin_driver); +} + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("cpufreq driver for Blackfin"); +MODULE_LICENSE("GPL"); + +module_init(bfin_cpu_init); +module_exit(bfin_cpu_exit); diff --git a/arch/blackfin/mach-common/dpmc.c b/arch/blackfin/mach-common/dpmc.c new file mode 100644 index 0000000..02c7efd --- /dev/null +++ b/arch/blackfin/mach-common/dpmc.c @@ -0,0 +1,137 @@ +/* + * 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" + +#define dprintk(msg...) \ + cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, DRIVER_NAME, msg) + +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 +static int +vreg_cpufreq_notifier(struct notifier_block *nb, unsigned long val, void *data) +{ + struct cpufreq_freqs *freq = data; + + if (val == CPUFREQ_PRECHANGE && freq->old < freq->new) { + bfin_set_vlev(bfin_get_vlev(freq->new)); + udelay(pdata->vr_settling_time); /* Wait until Volatge settled */ + + } else if (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) + bfin_set_vlev(bfin_get_vlev(freq->new)); + + return 0; +} + +static struct notifier_block vreg_cpufreq_notifier_block = { + .notifier_call = vreg_cpufreq_notifier +}; +#endif /* CONFIG_CPU_FREQ */ + +/** + * bfin_dpmc_probe - + * + */ +static int __devinit 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 __devexit 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 = __devexit_p(bfin_dpmc_remove), + .driver = { + .name = DRIVER_NAME, + } +}; + +/** + * bfin_dpmc_init - Init driver + */ +static int __init bfin_dpmc_init(void) +{ + return platform_driver_register(&bfin_dpmc_device_driver); +} +module_init(bfin_dpmc_init); + +/** + * bfin_dpmc_exit - break down driver + */ +static void __exit bfin_dpmc_exit(void) +{ + platform_driver_unregister(&bfin_dpmc_device_driver); +} +module_exit(bfin_dpmc_exit); + +MODULE_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 0000000..ad5431e --- /dev/null +++ b/arch/blackfin/mach-common/dpmc_modes.S @@ -0,0 +1,799 @@ +/* + * 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: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); + 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; + call _test_pll_locked; + + RETS = [SP++]; + ( R7:0, P5:0 ) = [SP++]; + RTS; +ENDPROC(_sleep_mode) + +ENTRY(_hibernate_mode) + [--SP] = ( R7:0, P5:0 ); + [--SP] = RETS; + + R3 = R0; + R0 = IWR_DISABLE_ALL; + R1 = IWR_DISABLE_ALL; + R2 = IWR_DISABLE_ALL; + call _set_sic_iwr; + call _set_dram_srfs; + SSYNC; + + R0 = 0xFFFF (Z); + call _set_rtc_istat; + + P0.H = hi(VR_CTL); + P0.L = lo(VR_CTL); + + W[P0] = R3.L; + CLI R2; + IDLE; +.Lforever: + jump .Lforever; +ENDPROC(_hibernate_mode) + +ENTRY(_sleep_deeper) + [--SP] = ( R7:0, P5:0 ); + [--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 */ + + /* 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; /* 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; + + 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:0, P5:0 ) = [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); + R2 = [P0]; + BITSET(R2, 24); /* SRFS enter self-refresh mode */ + [P0] = R2; + SSYNC; + + P0.L = lo(EBIU_SDSTAT); + P0.H = hi(EBIU_SDSTAT); +1: + R2 = w[P0]; + SSYNC; + cc = BITTST(R2, 1); /* SDSRA poll self-refresh status */ + if !cc jump 1b; + + P0.L = lo(EBIU_SDGCTL); + P0.H = hi(EBIU_SDGCTL); + 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 */ + + P0.L = lo(EBIU_SDGCTL); /* release CLKOUT from self-refresh */ + P0.H = hi(EBIU_SDGCTL); + R2 = [P0]; + BITSET(R2, 0); /* SCTLE enable CLKOUT */ + [P0] = R2 + SSYNC; + + P0.L = lo(EBIU_SDGCTL); /* release SDRAM from self-refresh */ + P0.H = hi(EBIU_SDGCTL); + R2 = [P0]; + BITCLR(R2, 24); /* clear SRFS bit */ + [P0] = R2 +#endif + SSYNC; + RTS; +ENDPROC(_unset_dram_srfs) + +ENTRY(_set_sic_iwr) +#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) + P0.H = hi(SIC_IWR0); + P0.L = lo(SIC_IWR0); + P1.H = hi(SIC_IWR1); + P1.L = lo(SIC_IWR1); + [P1] = R1; +#if defined(CONFIG_BF54x) + P1.H = hi(SIC_IWR2); + P1.L = lo(SIC_IWR2); + [P1] = R2; +#endif +#else + P0.H = hi(SIC_IWR); + P0.L = lo(SIC_IWR); +#endif + [P0] = R0; + + SSYNC; + RTS; +ENDPROC(_set_sic_iwr) + +ENTRY(_set_rtc_istat) +#ifndef CONFIG_BF561 + P0.H = hi(RTC_ISTAT); + P0.L = lo(RTC_ISTAT); + w[P0] = R0.L; + SSYNC; +#elif (ANOMALY_05000371) + nop; + nop; + nop; + nop; +#endif + RTS; +ENDPROC(_set_rtc_istat) + +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) + [--SP] = ( R7:0, P5:0 ); + [--SP] = RETS; + /* Save System MMRs */ + R2 = R0; + P0.H = hi(PLL_CTL); + P0.L = lo(PLL_CTL); + +#ifdef SIC_IMASK0 + PM_SYS_PUSH(SIC_IMASK0) +#endif +#ifdef SIC_IMASK1 + PM_SYS_PUSH(SIC_IMASK1) +#endif +#ifdef SIC_IMASK2 + PM_SYS_PUSH(SIC_IMASK2) +#endif +#ifdef SIC_IMASK + PM_SYS_PUSH(SIC_IMASK) +#endif +#ifdef SICA_IMASK0 + PM_SYS_PUSH(SICA_IMASK0) +#endif +#ifdef SICA_IMASK1 + PM_SYS_PUSH(SICA_IMASK1) +#endif +#ifdef SIC_IAR2 + PM_SYS_PUSH(SIC_IAR0) + PM_SYS_PUSH(SIC_IAR1) + PM_SYS_PUSH(SIC_IAR2) +#endif +#ifdef SIC_IAR3 + PM_SYS_PUSH(SIC_IAR3) +#endif +#ifdef SIC_IAR4 + PM_SYS_PUSH(SIC_IAR4) + PM_SYS_PUSH(SIC_IAR5) + PM_SYS_PUSH(SIC_IAR6) +#endif +#ifdef SIC_IAR7 + PM_SYS_PUSH(SIC_IAR7) +#endif +#ifdef SIC_IAR8 + PM_SYS_PUSH(SIC_IAR8) + PM_SYS_PUSH(SIC_IAR9) + PM_SYS_PUSH(SIC_IAR10) + PM_SYS_PUSH(SIC_IAR11) +#endif + +#ifdef SICA_IAR0 + PM_SYS_PUSH(SICA_IAR0) + PM_SYS_PUSH(SICA_IAR1) + PM_SYS_PUSH(SICA_IAR2) + PM_SYS_PUSH(SICA_IAR3) + PM_SYS_PUSH(SICA_IAR4) + PM_SYS_PUSH(SICA_IAR5) + PM_SYS_PUSH(SICA_IAR6) + PM_SYS_PUSH(SICA_IAR7) +#endif + +#ifdef SIC_IWR + PM_SYS_PUSH(SIC_IWR) +#endif +#ifdef SIC_IWR0 + PM_SYS_PUSH(SIC_IWR0) +#endif +#ifdef SIC_IWR1 + PM_SYS_PUSH(SIC_IWR1) +#endif +#ifdef SIC_IWR2 + PM_SYS_PUSH(SIC_IWR2) +#endif +#ifdef SICA_IWR0 + PM_SYS_PUSH(SICA_IWR0) +#endif +#ifdef SICA_IWR1 + PM_SYS_PUSH(SICA_IWR1) +#endif + +#ifdef PINT0_ASSIGN + PM_SYS_PUSH(PINT0_ASSIGN) + PM_SYS_PUSH(PINT1_ASSIGN) + PM_SYS_PUSH(PINT2_ASSIGN) + PM_SYS_PUSH(PINT3_ASSIGN) +#endif + + PM_SYS_PUSH(EBIU_AMBCTL0) + PM_SYS_PUSH(EBIU_AMBCTL1) + PM_SYS_PUSH16(EBIU_AMGCTL) + +#ifdef EBIU_FCTL + PM_SYS_PUSH(EBIU_MBSCTL) + PM_SYS_PUSH(EBIU_MODE) + PM_SYS_PUSH(EBIU_FCTL) +#endif + + PM_SYS_PUSH16(SYSCR) + + /* Save Core MMRs */ + P0.H = hi(SRAM_BASE_ADDRESS); + P0.L = lo(SRAM_BASE_ADDRESS); + + PM_PUSH(DMEM_CONTROL) + PM_PUSH(DCPLB_ADDR0) + PM_PUSH(DCPLB_ADDR1) + PM_PUSH(DCPLB_ADDR2) + PM_PUSH(DCPLB_ADDR3) + PM_PUSH(DCPLB_ADDR4) + PM_PUSH(DCPLB_ADDR5) + PM_PUSH(DCPLB_ADDR6) + PM_PUSH(DCPLB_ADDR7) + PM_PUSH(DCPLB_ADDR8) + PM_PUSH(DCPLB_ADDR9) + PM_PUSH(DCPLB_ADDR10) + PM_PUSH(DCPLB_ADDR11) + PM_PUSH(DCPLB_ADDR12) + PM_PUSH(DCPLB_ADDR13) + PM_PUSH(DCPLB_ADDR14) + PM_PUSH(DCPLB_ADDR15) + PM_PUSH(DCPLB_DATA0) + PM_PUSH(DCPLB_DATA1) + PM_PUSH(DCPLB_DATA2) + PM_PUSH(DCPLB_DATA3) + PM_PUSH(DCPLB_DATA4) + PM_PUSH(DCPLB_DATA5) + PM_PUSH(DCPLB_DATA6) + PM_PUSH(DCPLB_DATA7) + PM_PUSH(DCPLB_DATA8) + PM_PUSH(DCPLB_DATA9) + PM_PUSH(DCPLB_DATA10) + PM_PUSH(DCPLB_DATA11) + PM_PUSH(DCPLB_DATA12) + PM_PUSH(DCPLB_DATA13) + PM_PUSH(DCPLB_DATA14) + PM_PUSH(DCPLB_DATA15) + PM_PUSH(IMEM_CONTROL) + PM_PUSH(ICPLB_ADDR0) + PM_PUSH(ICPLB_ADDR1) + PM_PUSH(ICPLB_ADDR2) + PM_PUSH(ICPLB_ADDR3) + PM_PUSH(ICPLB_ADDR4) + PM_PUSH(ICPLB_ADDR5) + PM_PUSH(ICPLB_ADDR6) + PM_PUSH(ICPLB_ADDR7) + PM_PUSH(ICPLB_ADDR8) + PM_PUSH(ICPLB_ADDR9) + PM_PUSH(ICPLB_ADDR10) + PM_PUSH(ICPLB_ADDR11) + PM_PUSH(ICPLB_ADDR12) + PM_PUSH(ICPLB_ADDR13) + PM_PUSH(ICPLB_ADDR14) + PM_PUSH(ICPLB_ADDR15) + PM_PUSH(ICPLB_DATA0) + PM_PUSH(ICPLB_DATA1) + PM_PUSH(ICPLB_DATA2) + PM_PUSH(ICPLB_DATA3) + PM_PUSH(ICPLB_DATA4) + PM_PUSH(ICPLB_DATA5) + PM_PUSH(ICPLB_DATA6) + PM_PUSH(ICPLB_DATA7) + PM_PUSH(ICPLB_DATA8) + PM_PUSH(ICPLB_DATA9) + PM_PUSH(ICPLB_DATA10) + PM_PUSH(ICPLB_DATA11) + PM_PUSH(ICPLB_DATA12) + PM_PUSH(ICPLB_DATA13) + PM_PUSH(ICPLB_DATA14) + PM_PUSH(ICPLB_DATA15) + PM_PUSH(EVT0) + PM_PUSH(EVT1) + PM_PUSH(EVT2) + PM_PUSH(EVT3) + PM_PUSH(EVT4) + PM_PUSH(EVT5) + PM_PUSH(EVT6) + PM_PUSH(EVT7) + PM_PUSH(EVT8) + PM_PUSH(EVT9) + PM_PUSH(EVT10) + PM_PUSH(EVT11) + PM_PUSH(EVT12) + PM_PUSH(EVT13) + PM_PUSH(EVT14) + PM_PUSH(EVT15) + PM_PUSH(IMASK) + PM_PUSH(ILAT) + PM_PUSH(IPRIO) + PM_PUSH(TCNTL) + PM_PUSH(TPERIOD) + PM_PUSH(TSCALE) + PM_PUSH(TCOUNT) + PM_PUSH(TBUFCTL) + + /* Save Core Registers */ + [--sp] = SYSCFG; + [--sp] = ( R7:0, P5:0 ); + [--sp] = fp; + [--sp] = usp; + + [--sp] = i0; + [--sp] = i1; + [--sp] = i2; + [--sp] = i3; + + [--sp] = m0; + [--sp] = m1; + [--sp] = m2; + [--sp] = m3; + + [--sp] = l0; + [--sp] = l1; + [--sp] = l2; + [--sp] = l3; + + [--sp] = b0; + [--sp] = b1; + [--sp] = b2; + [--sp] = b3; + [--sp] = a0.x; + [--sp] = a0.w; + [--sp] = a1.x; + [--sp] = a1.w; + + [--sp] = LC0; + [--sp] = LC1; + [--sp] = LT0; + [--sp] = LT1; + [--sp] = LB0; + [--sp] = LB1; + + [--sp] = ASTAT; + [--sp] = CYCLES; + [--sp] = CYCLES2; + + [--sp] = RETS; + r0 = RETI; + [--sp] = r0; + [--sp] = RETX; + [--sp] = RETN; + [--sp] = RETE; + [--sp] = SEQSTAT; + + /* Save Magic, return address and Stack Pointer */ + P0.H = 0; + P0.L = 0; + R0.H = 0xDEAD; /* Hibernate Magic */ + R0.L = 0xBEEF; + [P0++] = R0; /* Store Hibernate Magic */ + R0.H = .Lpm_resume_here; + R0.L = .Lpm_resume_here; + [P0++] = R0; /* Save Return Address */ + [P0++] = SP; /* Save Stack Pointer */ + P0.H = _hibernate_mode; + P0.L = _hibernate_mode; + R0 = R2; + call (P0); /* Goodbye */ + +.Lpm_resume_here: + + /* Restore Core Registers */ + SEQSTAT = [sp++]; + RETE = [sp++]; + RETN = [sp++]; + RETX = [sp++]; + r0 = [sp++]; + RETI = r0; + RETS = [sp++]; + + CYCLES2 = [sp++]; + CYCLES = [sp++]; + ASTAT = [sp++]; + + LB1 = [sp++]; + LB0 = [sp++]; + LT1 = [sp++]; + LT0 = [sp++]; + LC1 = [sp++]; + LC0 = [sp++]; + + a1.w = [sp++]; + a1.x = [sp++]; + a0.w = [sp++]; + a0.x = [sp++]; + b3 = [sp++]; + b2 = [sp++]; + b1 = [sp++]; + b0 = [sp++]; + + l3 = [sp++]; + l2 = [sp++]; + l1 = [sp++]; + l0 = [sp++]; + + m3 = [sp++]; + m2 = [sp++]; + m1 = [sp++]; + m0 = [sp++]; + + i3 = [sp++]; + i2 = [sp++]; + i1 = [sp++]; + i0 = [sp++]; + + usp = [sp++]; + fp = [sp++]; + + ( R7 : 0, P5 : 0) = [ SP ++ ]; + SYSCFG = [sp++]; + + /* Restore Core MMRs */ + + PM_POP(TBUFCTL) + PM_POP(TCOUNT) + PM_POP(TSCALE) + PM_POP(TPERIOD) + PM_POP(TCNTL) + PM_POP(IPRIO) + PM_POP(ILAT) + PM_POP(IMASK) + PM_POP(EVT15) + PM_POP(EVT14) + PM_POP(EVT13) + PM_POP(EVT12) + PM_POP(EVT11) + PM_POP(EVT10) + PM_POP(EVT9) + PM_POP(EVT8) + PM_POP(EVT7) + PM_POP(EVT6) + PM_POP(EVT5) + PM_POP(EVT4) + PM_POP(EVT3) + PM_POP(EVT2) + PM_POP(EVT1) + PM_POP(EVT0) + PM_POP(ICPLB_DATA15) + PM_POP(ICPLB_DATA14) + PM_POP(ICPLB_DATA13) + PM_POP(ICPLB_DATA12) + PM_POP(ICPLB_DATA11) + PM_POP(ICPLB_DATA10) + PM_POP(ICPLB_DATA9) + PM_POP(ICPLB_DATA8) + PM_POP(ICPLB_DATA7) + PM_POP(ICPLB_DATA6) + PM_POP(ICPLB_DATA5) + PM_POP(ICPLB_DATA4) + PM_POP(ICPLB_DATA3) + PM_POP(ICPLB_DATA2) + PM_POP(ICPLB_DATA1) + PM_POP(ICPLB_DATA0) + PM_POP(ICPLB_ADDR15) + PM_POP(ICPLB_ADDR14) + PM_POP(ICPLB_ADDR13) + PM_POP(ICPLB_ADDR12) + PM_POP(ICPLB_ADDR11) + PM_POP(ICPLB_ADDR10) + PM_POP(ICPLB_ADDR9) + PM_POP(ICPLB_ADDR8) + PM_POP(ICPLB_ADDR7) + PM_POP(ICPLB_ADDR6) + PM_POP(ICPLB_ADDR5) + PM_POP(ICPLB_ADDR4) + PM_POP(ICPLB_ADDR3) + PM_POP(ICPLB_ADDR2) + PM_POP(ICPLB_ADDR1) + PM_POP(ICPLB_ADDR0) + PM_POP(IMEM_CONTROL) + PM_POP(DCPLB_DATA15) + PM_POP(DCPLB_DATA14) + PM_POP(DCPLB_DATA13) + PM_POP(DCPLB_DATA12) + PM_POP(DCPLB_DATA11) + PM_POP(DCPLB_DATA10) + PM_POP(DCPLB_DATA9) + PM_POP(DCPLB_DATA8) + PM_POP(DCPLB_DATA7) + PM_POP(DCPLB_DATA6) + PM_POP(DCPLB_DATA5) + PM_POP(DCPLB_DATA4) + PM_POP(DCPLB_DATA3) + PM_POP(DCPLB_DATA2) + PM_POP(DCPLB_DATA1) + PM_POP(DCPLB_DATA0) + PM_POP(DCPLB_ADDR15) + PM_POP(DCPLB_ADDR14) + PM_POP(DCPLB_ADDR13) + PM_POP(DCPLB_ADDR12) + PM_POP(DCPLB_ADDR11) + PM_POP(DCPLB_ADDR10) + PM_POP(DCPLB_ADDR9) + PM_POP(DCPLB_ADDR8) + PM_POP(DCPLB_ADDR7) + PM_POP(DCPLB_ADDR6) + PM_POP(DCPLB_ADDR5) + PM_POP(DCPLB_ADDR4) + PM_POP(DCPLB_ADDR3) + PM_POP(DCPLB_ADDR2) + PM_POP(DCPLB_ADDR1) + PM_POP(DCPLB_ADDR0) + PM_POP(DMEM_CONTROL) + + /* Restore System MMRs */ + + P0.H = hi(PLL_CTL); + P0.L = lo(PLL_CTL); + PM_SYS_POP16(SYSCR) + +#ifdef EBIU_FCTL + PM_SYS_POP(EBIU_FCTL) + PM_SYS_POP(EBIU_MODE) + PM_SYS_POP(EBIU_MBSCTL) +#endif + PM_SYS_POP16(EBIU_AMGCTL) + PM_SYS_POP(EBIU_AMBCTL1) + PM_SYS_POP(EBIU_AMBCTL0) + +#ifdef PINT0_ASSIGN + PM_SYS_POP(PINT3_ASSIGN) + PM_SYS_POP(PINT2_ASSIGN) + PM_SYS_POP(PINT1_ASSIGN) + PM_SYS_POP(PINT0_ASSIGN) +#endif + +#ifdef SICA_IWR1 + PM_SYS_POP(SICA_IWR1) +#endif +#ifdef SICA_IWR0 + PM_SYS_POP(SICA_IWR0) +#endif +#ifdef SIC_IWR2 + PM_SYS_POP(SIC_IWR2) +#endif +#ifdef SIC_IWR1 + PM_SYS_POP(SIC_IWR1) +#endif +#ifdef SIC_IWR0 + PM_SYS_POP(SIC_IWR0) +#endif +#ifdef SIC_IWR + PM_SYS_POP(SIC_IWR) +#endif + +#ifdef SICA_IAR0 + PM_SYS_POP(SICA_IAR7) + PM_SYS_POP(SICA_IAR6) + PM_SYS_POP(SICA_IAR5) + PM_SYS_POP(SICA_IAR4) + PM_SYS_POP(SICA_IAR3) + PM_SYS_POP(SICA_IAR2) + PM_SYS_POP(SICA_IAR1) + PM_SYS_POP(SICA_IAR0) +#endif + +#ifdef SIC_IAR8 + PM_SYS_POP(SIC_IAR11) + PM_SYS_POP(SIC_IAR10) + PM_SYS_POP(SIC_IAR9) + PM_SYS_POP(SIC_IAR8) +#endif +#ifdef SIC_IAR7 + PM_SYS_POP(SIC_IAR7) +#endif +#ifdef SIC_IAR6 + PM_SYS_POP(SIC_IAR6) + PM_SYS_POP(SIC_IAR5) + PM_SYS_POP(SIC_IAR4) +#endif +#ifdef SIC_IAR3 + PM_SYS_POP(SIC_IAR3) +#endif +#ifdef SIC_IAR2 + PM_SYS_POP(SIC_IAR2) + PM_SYS_POP(SIC_IAR1) + PM_SYS_POP(SIC_IAR0) +#endif +#ifdef SICA_IMASK1 + PM_SYS_POP(SICA_IMASK1) +#endif +#ifdef SICA_IMASK0 + PM_SYS_POP(SICA_IMASK0) +#endif +#ifdef SIC_IMASK + PM_SYS_POP(SIC_IMASK) +#endif +#ifdef SIC_IMASK2 + PM_SYS_POP(SIC_IMASK2) +#endif +#ifdef SIC_IMASK1 + PM_SYS_POP(SIC_IMASK1) +#endif +#ifdef SIC_IMASK0 + PM_SYS_POP(SIC_IMASK0) +#endif + + [--sp] = RETI; /* Clear Global Interrupt Disable */ + SP += 4; + + RETS = [SP++]; + ( R7:0, P5:0 ) = [SP++]; + RTS; +ENDPROC(_do_hibernate) diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S new file mode 100644 index 0000000..bde6dc4 --- /dev/null +++ b/arch/blackfin/mach-common/entry.S @@ -0,0 +1,1552 @@ +/* + * File: arch/blackfin/mach-common/entry.S + * Based on: + * Author: Linus Torvalds + * + * 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. + * + * 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 + */ + +/* 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/context.S> + +#if defined(CONFIG_BFIN_SCRATCH_REG_RETN) +# define EX_SCRATCH_REG RETN +#elif defined(CONFIG_BFIN_SCRATCH_REG_RETE) +# define EX_SCRATCH_REG RETE +#else +# define EX_SCRATCH_REG CYCLES +#endif + +#ifdef CONFIG_EXCPT_IRQ_SYSC_L1 +.section .l1.text +#else +.text +#endif + +/* Slightly simplified and streamlined entry point for CPLB misses. + * This one does not lower the level to IRQ5, and thus can be used to + * patch up CPLB misses on the kernel stack. + */ +#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. + */ + P4 = R7; /* Store EXCAUSE */ + p5.l = _last_cplb_fault_retx; + p5.h = _last_cplb_fault_retx; + r7 = [p5]; + r6 = retx; + [p5] = r6; + cc = r6 == r7; + if !cc jump _bfin_return_from_exception; + /* fall through */ + R7 = P4; + R6 = 0x26; /* Data CPLB Miss */ + cc = R6 == R7; + if cc jump _ex_dcplb_miss (BP); + R6 = 0x23; /* Data CPLB Miss */ + cc = R6 == R7; + if cc jump _ex_dcplb_viol (BP); + /* Handle 0x23 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 + + +ENTRY(_ex_dcplb_viol) +ENTRY(_ex_dcplb_miss) +ENTRY(_ex_icplb_miss) + (R7:6,P5:4) = [sp++]; + ASTAT = [sp++]; + SAVE_ALL_SYS +#ifdef CONFIG_MPU + /* We must load R1 here, _before_ DEBUG_HWTRACE_SAVE, since that + * will change the stack pointer. */ + R0 = SEQSTAT; + R1 = SP; +#endif + DEBUG_HWTRACE_SAVE(p5, r7) +#ifdef CONFIG_MPU + sp += -12; + call _cplb_hdr; + sp += 12; + CC = R0 == 0; + IF !CC JUMP _handle_bad_cplb; +#else + call __cplb_hdr; +#endif + +#ifdef CONFIG_DEBUG_DOUBLEFAULT + /* While we were processing this, did we double fault? */ + r7 = SEQSTAT; /* reason code is in bit 5:0 */ + r6.l = lo(SEQSTAT_EXCAUSE); + r6.h = hi(SEQSTAT_EXCAUSE); + r7 = r7 & r6; + r6 = 0x25; + CC = R7 == R6; + if CC JUMP _double_fault; +#endif + + DEBUG_HWTRACE_RESTORE(p5, r7) + RESTORE_ALL_SYS + SP = EX_SCRATCH_REG; + rtx; +ENDPROC(_ex_icplb_miss) + +ENTRY(_ex_syscall) + raise 15; /* invoked by TRAP #0, for sys call */ + 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 _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]; + r7 = 0xffe0 (z); + r7 = r7 & r6; + cc = r7 == 0; + if !cc jump 1f; +#endif + + /* Single stepping only a single instruction, so clear the trace + * bit here. */ + r7 = syscfg; + bitclr (r7, 0); + 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, 0); + syscfg = 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 = 0x25; + CC = R7 == R6; + if CC JUMP _double_fault; + + /* Did we cause a HW error? */ + p5.l = lo(ILAT); + p5.h = hi(ILAT); + r6 = [p5]; + r7 = 0x20; /* Did I just cause anther HW error? */ + r6 = r7 & r6; + CC = R7 == R6; + if CC JUMP _double_fault; +#endif + + (R7:6,P5:4) = [sp++]; + ASTAT = [sp++]; + sp = EX_SCRATCH_REG; + rtx; +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 + * the stack to get ready so, we can fall through - we + * need to make a CPLB exception look like a normal exception + */ + + RESTORE_ALL_SYS + [--sp] = ASTAT; + [--sp] = (R7:6,P5:4); + +ENTRY(_ex_replaceable) + nop; + +ENTRY(_ex_trap_c) + /* 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; + + /* 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; + p4.l = lo(EVT5); + p4.h = hi(EVT5); + [p4] = p5; + csync; + +#ifndef 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 + */ + + p4.l = lo(DCPLB_FAULT_ADDR); + p4.h = hi(DCPLB_FAULT_ADDR); + r7 = [p4]; + p5.h = _saved_dcplb_fault_addr; + p5.l = _saved_dcplb_fault_addr; + [p5] = r7; + + r7 = [p4 + (ICPLB_FAULT_ADDR - DCPLB_FAULT_ADDR)]; + p5.h = _saved_icplb_fault_addr; + p5.l = _saved_icplb_fault_addr; + [p5] = r7; + + r6 = retx; + p4.l = _saved_retx; + p4.h = _saved_retx; + [p4] = r6; +#endif + r6 = SYSCFG; + [p4 + 4] = r6; + BITCLR(r6, 0); + SYSCFG = r6; + + /* Disable all interrupts, but make sure level 5 is enabled so + * we can switch to that level. Save the old mask. */ + cli r6; + [p4 + 8] = r6; + + p4.l = lo(SAFE_USER_INSTRUCTION); + p4.h = hi(SAFE_USER_INSTRUCTION); + retx = p4; + + r6 = 0x3f; + sti r6; + + raise 5; + 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 + */ +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 + + /* The dumping functions expect the return address in the RETI + * slot. */ + r6 = retx; + [sp + PT_PC] = r6; + + r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ + SP += -12; + call _double_fault_c; + SP += 12; +.L_double_fault_panic: + JUMP .L_double_fault_panic + +ENDPROC(_double_fault) + +ENTRY(_exception_to_level5) + SAVE_ALL_SYS + + p4.l = _saved_retx; + p4.h = _saved_retx; + r6 = [p4]; + [sp + PT_PC] = r6; + + r6 = [p4 + 4]; + [sp + PT_SYSCFG] = r6; + + /* Restore interrupt mask. We haven't pushed RETI, so this + * doesn't enable interrupts until we return from this handler. */ + r6 = [p4 + 8]; + sti r6; + + /* Restore the hardware error vector. */ + P5.h = _evt_ivhw; + P5.l = _evt_ivhw; + p4.l = lo(EVT5); + p4.h = hi(EVT5); + [p4] = p5; + csync; + + p2.l = lo(IPEND); + p2.h = hi(IPEND); + csync; + r0 = [p2]; /* Read current IPEND */ + [sp + PT_IPEND] = r0; /* Store IPEND */ + + r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ + SP += -12; + call _trap_c; + SP += 12; + +#ifdef CONFIG_DEBUG_DOUBLEFAULT + /* Grab ILAT */ + p2.l = lo(ILAT); + p2.h = hi(ILAT); + r0 = [p2]; + r1 = 0x20; /* Did I just cause anther HW error? */ + r0 = r0 & r1; + CC = R0 == R1; + if CC JUMP _double_fault; +#endif + + call _ret_from_exception; + RESTORE_ALL_SYS + rti; +ENDPROC(_exception_to_level5) + +ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/ + /* Since the kernel stack can be anywhere, it's not guaranteed to be + * covered by a CPLB. Switch to an exception stack; use RETN as a + * scratch register (for want of a better option). + */ + EX_SCRATCH_REG = sp; + sp.l = _exception_stack_top; + sp.h = _exception_stack_top; + /* Try to deal with syscalls quickly. */ + [--sp] = ASTAT; + [--sp] = (R7:6,P5:4); + +#if ANOMALY_05000283 || ANOMALY_05000315 + cc = r7 == r7; + p5.h = HI(CHIPID); + p5.l = LO(CHIPID); + if cc jump 1f; + r7.l = W[p5]; +1: +#endif + +#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 + */ + + p4.l = lo(DCPLB_FAULT_ADDR); + p4.h = hi(DCPLB_FAULT_ADDR); + r7 = [p4]; + p5.h = _saved_dcplb_fault_addr; + p5.l = _saved_dcplb_fault_addr; + [p5] = r7; + + r7 = [p4 + (ICPLB_FAULT_ADDR - DCPLB_FAULT_ADDR)]; + p5.h = _saved_icplb_fault_addr; + p5.l = _saved_icplb_fault_addr; + [p5] = r7; + + p4.l = _saved_retx; + p4.h = _saved_retx; + r6 = retx; + [p4] = r6; + + r7 = SEQSTAT; /* reason code is in bit 5:0 */ + p4.l = _saved_seqstat; + p4.h = _saved_seqstat; + [p4] = 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; + p5.h = _ex_table; + p5.l = _ex_table; + p4 = r7; + p5 = p5 + (p4 << 2); + p4 = [p5]; + jump (p4); + +.Lbadsys: + r7 = -ENOSYS; /* signextending enough */ + [sp + PT_R0] = r7; /* return value from system call */ + 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); + p2.h = hi(IPEND); + csync; + r0 = [p2]; + [sp + PT_IPEND] = r0; + + /* Store RETS for now */ + r0 = rets; + [sp + PT_RESERVED] = r0; + /* Set the stack for the current process */ + r7 = sp; + r6.l = lo(ALIGN_PAGE_MASK); + r6.h = hi(ALIGN_PAGE_MASK); + r7 = r7 & r6; /* thread_info */ + p2 = r7; + 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; + + /* are we tracing syscalls?*/ + r7 = sp; + r6.l = lo(ALIGN_PAGE_MASK); + r6.h = hi(ALIGN_PAGE_MASK); + r7 = r7 & r6; + p2 = r7; + r7 = [p2+TI_FLAGS]; + CC = BITTST(r7,TIF_SYSCALL_TRACE); + if CC JUMP _sys_trace; + + /* Execute the appropriate system call */ + + p4 = p0; + p5.l = _sys_call_table; + p5.h = _sys_call_table; + p5 = p5 + (p4 << 2); + r0 = [sp + PT_R0]; + r1 = [sp + PT_R1]; + r2 = [sp + PT_R2]; + p5 = [p5]; + + [--sp] = r5; + [--sp] = r4; + [--sp] = r3; + SP += -12; + call (p5); + SP += 24; + [sp + PT_R0] = r0; + +.Lresume_userspace: + r7 = sp; + r4.l = lo(ALIGN_PAGE_MASK); + r4.h = hi(ALIGN_PAGE_MASK); + r7 = r7 & r4; /* thread_info->flags */ + p5 = r7; +.Lresume_userspace_1: + /* Disable interrupts. */ + [--sp] = reti; + reti = [sp++]; + + r7 = [p5 + TI_FLAGS]; + r4.l = lo(_TIF_WORK_MASK); + r4.h = hi(_TIF_WORK_MASK); + r7 = r7 & r4; + +.Lsyscall_resched: + cc = BITTST(r7, TIF_NEED_RESCHED); + if !cc jump .Lsyscall_sigpending; + + /* Reenable interrupts. */ + [--sp] = reti; + r0 = [sp++]; + + SP += -12; + call _schedule; + 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_really_exit; +.Lsyscall_do_signals: + /* Reenable interrupts. */ + [--sp] = reti; + r0 = [sp++]; + + r0 = sp; + SP += -12; + call _do_signal; + SP += 12; + +.Lsyscall_really_exit: + r5 = [sp + PT_RESERVED]; + rets = r5; + 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 */ + + p4 = [SP + PT_P0]; + p5.l = _sys_call_table; + p5.h = _sys_call_table; + p5 = p5 + (p4 << 2); + r0 = [sp + PT_R0]; + r1 = [sp + PT_R1]; + r2 = [sp + PT_R2]; + r3 = [sp + PT_R3]; + r4 = [sp + PT_R4]; + r5 = [sp + PT_R5]; + p5 = [p5]; + + [--sp] = r5; + [--sp] = r4; + [--sp] = r3; + SP += -12; + call (p5); + SP += 24; + [sp + PT_R0] = r0; + + call _syscall_trace; + jump .Lresume_userspace; +ENDPROC(_sys_trace) + +ENTRY(_resume) + /* + * Beware - when entering resume, prev (the current task) is + * in r0, next (the new task) is in r1. + */ + p0 = r0; + p1 = r1; + [--sp] = rets; + [--sp] = fp; + [--sp] = (r7:4, p5:3); + + /* save usp */ + p2 = usp; + [p0+(TASK_THREAD+THREAD_USP)] = p2; + + /* save current kernel stack pointer */ + [p0+(TASK_THREAD+THREAD_KSP)] = sp; + + /* save program counter */ + r1.l = _new_old_task; + r1.h = _new_old_task; + [p0+(TASK_THREAD+THREAD_PC)] = r1; + + /* restore the kernel stack pointer */ + sp = [p1+(TASK_THREAD+THREAD_KSP)]; + + /* restore user stack pointer */ + p0 = [p1+(TASK_THREAD+THREAD_USP)]; + usp = p0; + + /* restore pc */ + p0 = [p1+(TASK_THREAD+THREAD_PC)]; + jump (p0); + + /* + * Following code actually lands up in a new (old) task. + */ + +_new_old_task: + (r7:4, p5:3) = [sp++]; + fp = [sp++]; + rets = [sp++]; + + /* + * When we come out of resume, r0 carries "old" task, becuase we are + * in "new" task. + */ + rts; +ENDPROC(_resume) + +ENTRY(_ret_from_exception) + p2.l = lo(IPEND); + p2.h = hi(IPEND); + + csync; + r0 = [p2]; + [sp + PT_IPEND] = r0; + +1: + r2 = LO(~0x37) (Z); + r0 = r2 & r0; + cc = r0 == 0; + if !cc jump 4f; /* if not return to user mode, get out */ + + /* Make sure any pending system call or deferred exception + * return in ILAT for this process to get executed, otherwise + * in case context switch happens, system call of + * first process (i.e in ILAT) will be carried + * forward to the switched process + */ + + p2.l = lo(ILAT); + p2.h = hi(ILAT); + r0 = [p2]; + r1 = (EVT_IVG14 | EVT_IVG15) (z); + r0 = r0 & r1; + cc = r0 == 0; + if !cc jump 5f; + + /* Set the stack for the current process */ + r7 = sp; + r4.l = lo(ALIGN_PAGE_MASK); + r4.h = hi(ALIGN_PAGE_MASK); + r7 = r7 & r4; /* thread_info->flags */ + p5 = r7; + r7 = [p5 + TI_FLAGS]; + r4.l = lo(_TIF_WORK_MASK); + r4.h = hi(_TIF_WORK_MASK); + r7 = r7 & r4; + cc = r7 == 0; + if cc jump 4f; + + p0.l = lo(EVT15); + p0.h = hi(EVT15); + p1.l = _schedule_and_signal; + p1.h = _schedule_and_signal; + [p0] = p1; + csync; + raise 15; /* raise evt14 to do signal or reschedule */ +4: + r0 = syscfg; + bitclr(r0, 0); + syscfg = r0; +5: + rts; +ENDPROC(_ret_from_exception) + +ENTRY(_return_from_int) + /* If someone else already raised IRQ 15, do nothing. */ + csync; + p2.l = lo(ILAT); + p2.h = hi(ILAT); + r0 = [p2]; + cc = bittst (r0, EVT_IVG15_P); + if cc jump 2f; + + /* if not return to user mode, get out */ + p2.l = lo(IPEND); + p2.h = hi(IPEND); + r0 = [p2]; + r1 = 0x17(Z); + r2 = ~r1; + r2.h = 0; + r0 = r2 & r0; + r1 = 1; + r1 = r0 - r1; + r2 = r0 & r1; + cc = r2 == 0; + if !cc jump 2f; + + /* Lower the interrupt level to 15. */ + p0.l = lo(EVT15); + p0.h = hi(EVT15); + p1.l = _schedule_and_signal_from_int; + p1.h = _schedule_and_signal_from_int; + [p0] = p1; + csync; +#if ANOMALY_05000281 + 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: + rts; +ENDPROC(_return_from_int) + +ENTRY(_lower_to_irq14) +#if ANOMALY_05000281 + r0.l = lo(SAFE_USER_INSTRUCTION); + r0.h = hi(SAFE_USER_INSTRUCTION); + reti = r0; +#endif + r0 = 0x401f; + sti r0; + raise 14; + rti; +ENTRY(_evt14_softirq) +#ifdef CONFIG_DEBUG_HWERR + r0 = 0x3f; + sti r0; +#else + cli r0; +#endif + [--sp] = RETI; + SP += 4; + rts; + +_schedule_and_signal_from_int: + /* To end up here, vector 15 was changed - so we have to change it + * back. + */ + p0.l = lo(EVT15); + p0.h = hi(EVT15); + p1.l = _evt_system_call; + p1.h = _evt_system_call; + [p0] = p1; + csync; + + /* Set orig_p0 to -1 to indicate this isn't the end of a syscall. */ + r0 = -1 (x); + [sp + PT_ORIG_P0] = r0; + + p1 = rets; + [sp + PT_RESERVED] = p1; + + p0.l = _irq_flags; + p0.h = _irq_flags; + r0 = [p0]; + sti r0; + + r0 = sp; + sp += -12; + call _finish_atomic_sections; + sp += 12; + jump.s .Lresume_userspace; + +_schedule_and_signal: + SAVE_CONTEXT_SYSCALL + /* To end up here, vector 15 was changed - so we have to change it + * back. + */ + p0.l = lo(EVT15); + p0.h = hi(EVT15); + p1.l = _evt_system_call; + p1.h = _evt_system_call; + [p0] = p1; + csync; + p0.l = 1f; + p0.h = 1f; + [sp + PT_RESERVED] = P0; + call .Lresume_userspace; +1: + RESTORE_CONTEXT + rti; +ENDPROC(_lower_to_irq14) + +/* We handle this 100% in exception space - to reduce overhead + * Only potiential problem is if the software buffer gets swapped out of the + * CPLB table - then double fault. - so we don't let this happen in other places + */ +#ifdef CONFIG_DEBUG_BFIN_HWTRACE_EXPAND +ENTRY(_ex_trace_buff_full) + [--sp] = P3; + [--sp] = P2; + [--sp] = LC0; + [--sp] = LT0; + [--sp] = LB0; + P5.L = _trace_buff_offset; + P5.H = _trace_buff_offset; + P3 = [P5]; /* trace_buff_offset */ + P5.L = lo(TBUFSTAT); + P5.H = hi(TBUFSTAT); + R7 = [P5]; + R7 <<= 1; /* double, since we need to read twice */ + LC0 = R7; + R7 <<= 2; /* need to shift over again, + * to get the number of bytes */ + P5.L = lo(TBUF); + P5.H = hi(TBUF); + R6 = ((1 << CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN)*1024) - 1; + + P2 = R7; + P3 = P3 + P2; + R7 = P3; + R7 = R7 & R6; + P3 = R7; + P2.L = _trace_buff_offset; + P2.H = _trace_buff_offset; + [P2] = P3; + + P2.L = _software_trace_buff; + P2.H = _software_trace_buff; + + LSETUP (.Lstart, .Lend) LC0; +.Lstart: + R7 = [P5]; /* read TBUF */ + P4 = P3 + P2; + [P4] = R7; + P3 += -4; + R7 = P3; + R7 = R7 & R6; +.Lend: + P3 = R7; + + LB0 = [sp++]; + LT0 = [sp++]; + LC0 = [sp++]; + P2 = [sp++]; + P3 = [sp++]; + jump _bfin_return_from_exception; +ENDPROC(_ex_trace_buff_full) + +#if CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN == 4 +.data +#else +.section .l1.data.B +#endif /* CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN */ +ENTRY(_trace_buff_offset) + .long 0; +ALIGN +ENTRY(_software_trace_buff) + .rept ((1 << CONFIG_DEBUG_BFIN_HWTRACE_EXPAND_LEN)*256); + .long 0 + .endr +#endif /* CONFIG_DEBUG_BFIN_HWTRACE_EXPAND */ + +#if CONFIG_EARLY_PRINTK +__INIT +ENTRY(_early_trap) + SAVE_ALL_SYS + trace_buffer_stop(p0,r0); + +#if ANOMALY_05000283 || ANOMALY_05000315 + cc = r5 == r5; + p4.h = HI(CHIPID); + p4.l = LO(CHIPID); + if cc jump 1f; + r5.l = W[p4]; +1: +#endif + + /* Turn caches off, to ensure we don't get double exceptions */ + + P4.L = LO(IMEM_CONTROL); + P4.H = HI(IMEM_CONTROL); + + R5 = [P4]; /* Control Register*/ + BITCLR(R5,ENICPLB_P); + CLI R1; + 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; + STI R1; + + r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ + r1 = RETX; + + SP += -12; + call _early_trap_c; + SP += 12; +ENDPROC(_early_trap) +__FINIT +#endif /* CONFIG_EARLY_PRINTK */ + +/* + * Put these in the kernel data section - that should always be covered by + * a CPLB. This is needed to ensure we don't get double fault conditions + */ + +#ifdef CONFIG_SYSCALL_TAB_L1 +.section .l1.data +#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 */ +#ifdef CONFIG_KGDB + .long _ex_trap_c /* 0x02 - User Defined - KGDB initial connection + and break signal trap */ +#else + .long _ex_replaceable /* 0x02 - User Defined */ +#endif + .long _ex_trap_c /* 0x03 - User Defined - userspace stack overflow */ + .long _ex_trap_c /* 0x04 - User Defined - dump trace buffer */ + .long _ex_replaceable /* 0x05 - User Defined */ + .long _ex_replaceable /* 0x06 - User Defined */ + .long _ex_replaceable /* 0x07 - User Defined */ + .long _ex_replaceable /* 0x08 - User Defined */ + .long _ex_replaceable /* 0x09 - User Defined */ + .long _ex_replaceable /* 0x0A - User Defined */ + .long _ex_replaceable /* 0x0B - User Defined */ + .long _ex_replaceable /* 0x0C - User Defined */ + .long _ex_replaceable /* 0x0D - User Defined */ + .long _ex_replaceable /* 0x0E - User Defined */ + .long _ex_replaceable /* 0x0F - User Defined */ + .long _ex_single_step /* 0x10 - HW Single step */ +#ifdef CONFIG_DEBUG_BFIN_HWTRACE_EXPAND + .long _ex_trace_buff_full /* 0x11 - Trace Buffer Full */ +#else + .long _ex_trap_c /* 0x11 - Trace Buffer Full */ +#endif + .long _ex_trap_c /* 0x12 - Reserved */ + .long _ex_trap_c /* 0x13 - Reserved */ + .long _ex_trap_c /* 0x14 - Reserved */ + .long _ex_trap_c /* 0x15 - Reserved */ + .long _ex_trap_c /* 0x16 - Reserved */ + .long _ex_trap_c /* 0x17 - Reserved */ + .long _ex_trap_c /* 0x18 - Reserved */ + .long _ex_trap_c /* 0x19 - Reserved */ + .long _ex_trap_c /* 0x1A - Reserved */ + .long _ex_trap_c /* 0x1B - Reserved */ + .long _ex_trap_c /* 0x1C - Reserved */ + .long _ex_trap_c /* 0x1D - Reserved */ + .long _ex_trap_c /* 0x1E - Reserved */ + .long _ex_trap_c /* 0x1F - Reserved */ + .long _ex_trap_c /* 0x20 - Reserved */ + .long _ex_trap_c /* 0x21 - Undefined Instruction */ + .long _ex_trap_c /* 0x22 - Illegal Instruction Combination */ + .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_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_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 */ + .long _ex_trap_c /* 0x2F - Reserved */ + .long _ex_trap_c /* 0x30 - Reserved */ + .long _ex_trap_c /* 0x31 - Reserved */ + .long _ex_trap_c /* 0x32 - Reserved */ + .long _ex_trap_c /* 0x33 - Reserved */ + .long _ex_trap_c /* 0x34 - Reserved */ + .long _ex_trap_c /* 0x35 - Reserved */ + .long _ex_trap_c /* 0x36 - Reserved */ + .long _ex_trap_c /* 0x37 - Reserved */ + .long _ex_trap_c /* 0x38 - Reserved */ + .long _ex_trap_c /* 0x39 - Reserved */ + .long _ex_trap_c /* 0x3A - Reserved */ + .long _ex_trap_c /* 0x3B - Reserved */ + .long _ex_trap_c /* 0x3C - Reserved */ + .long _ex_trap_c /* 0x3D - Reserved */ + .long _ex_trap_c /* 0x3E - Reserved */ + .long _ex_trap_c /* 0x3F - Reserved */ +END(_ex_table) + +ENTRY(_sys_call_table) + .long _sys_restart_syscall /* 0 */ + .long _sys_exit + .long _sys_fork + .long _sys_read + .long _sys_write + .long _sys_open /* 5 */ + .long _sys_close + .long _sys_ni_syscall /* old waitpid */ + .long _sys_creat + .long _sys_link + .long _sys_unlink /* 10 */ + .long _sys_execve + .long _sys_chdir + .long _sys_time + .long _sys_mknod + .long _sys_chmod /* 15 */ + .long _sys_chown /* chown16 */ + .long _sys_ni_syscall /* old break syscall holder */ + .long _sys_ni_syscall /* old stat */ + .long _sys_lseek + .long _sys_getpid /* 20 */ + .long _sys_mount + .long _sys_ni_syscall /* old umount */ + .long _sys_setuid + .long _sys_getuid + .long _sys_stime /* 25 */ + .long _sys_ptrace + .long _sys_alarm + .long _sys_ni_syscall /* old fstat */ + .long _sys_pause + .long _sys_ni_syscall /* old utime */ /* 30 */ + .long _sys_ni_syscall /* old stty syscall holder */ + .long _sys_ni_syscall /* old gtty syscall holder */ + .long _sys_access + .long _sys_nice + .long _sys_ni_syscall /* 35 */ /* old ftime syscall holder */ + .long _sys_sync + .long _sys_kill + .long _sys_rename + .long _sys_mkdir + .long _sys_rmdir /* 40 */ + .long _sys_dup + .long _sys_pipe + .long _sys_times + .long _sys_ni_syscall /* old prof syscall holder */ + .long _sys_brk /* 45 */ + .long _sys_setgid + .long _sys_getgid + .long _sys_ni_syscall /* old sys_signal */ + .long _sys_geteuid /* geteuid16 */ + .long _sys_getegid /* getegid16 */ /* 50 */ + .long _sys_acct + .long _sys_umount /* recycled never used phys() */ + .long _sys_ni_syscall /* old lock syscall holder */ + .long _sys_ioctl + .long _sys_fcntl /* 55 */ + .long _sys_ni_syscall /* old mpx syscall holder */ + .long _sys_setpgid + .long _sys_ni_syscall /* old ulimit syscall holder */ + .long _sys_ni_syscall /* old old uname */ + .long _sys_umask /* 60 */ + .long _sys_chroot + .long _sys_ustat + .long _sys_dup2 + .long _sys_getppid + .long _sys_getpgrp /* 65 */ + .long _sys_setsid + .long _sys_ni_syscall /* old sys_sigaction */ + .long _sys_sgetmask + .long _sys_ssetmask + .long _sys_setreuid /* setreuid16 */ /* 70 */ + .long _sys_setregid /* setregid16 */ + .long _sys_ni_syscall /* old sys_sigsuspend */ + .long _sys_ni_syscall /* old sys_sigpending */ + .long _sys_sethostname + .long _sys_setrlimit /* 75 */ + .long _sys_ni_syscall /* old getrlimit */ + .long _sys_getrusage + .long _sys_gettimeofday + .long _sys_settimeofday + .long _sys_getgroups /* getgroups16 */ /* 80 */ + .long _sys_setgroups /* setgroups16 */ + .long _sys_ni_syscall /* old_select */ + .long _sys_symlink + .long _sys_ni_syscall /* old lstat */ + .long _sys_readlink /* 85 */ + .long _sys_uselib + .long _sys_ni_syscall /* sys_swapon */ + .long _sys_reboot + .long _sys_ni_syscall /* old_readdir */ + .long _sys_ni_syscall /* sys_mmap */ /* 90 */ + .long _sys_munmap + .long _sys_truncate + .long _sys_ftruncate + .long _sys_fchmod + .long _sys_fchown /* fchown16 */ /* 95 */ + .long _sys_getpriority + .long _sys_setpriority + .long _sys_ni_syscall /* old profil syscall holder */ + .long _sys_statfs + .long _sys_fstatfs /* 100 */ + .long _sys_ni_syscall + .long _sys_ni_syscall /* old sys_socketcall */ + .long _sys_syslog + .long _sys_setitimer + .long _sys_getitimer /* 105 */ + .long _sys_newstat + .long _sys_newlstat + .long _sys_newfstat + .long _sys_ni_syscall /* old uname */ + .long _sys_ni_syscall /* iopl for i386 */ /* 110 */ + .long _sys_vhangup + .long _sys_ni_syscall /* obsolete idle() syscall */ + .long _sys_ni_syscall /* vm86old for i386 */ + .long _sys_wait4 + .long _sys_ni_syscall /* 115 */ /* sys_swapoff */ + .long _sys_sysinfo + .long _sys_ni_syscall /* old sys_ipc */ + .long _sys_fsync + .long _sys_ni_syscall /* old sys_sigreturn */ + .long _sys_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_ni_syscall /* old sys_sigprocmask */ + .long _sys_ni_syscall /* old "creat_module" */ + .long _sys_init_module + .long _sys_delete_module + .long _sys_ni_syscall /* 130: old "get_kernel_syms" */ + .long _sys_quotactl + .long _sys_getpgid + .long _sys_fchdir + .long _sys_bdflush + .long _sys_ni_syscall /* 135 */ /* sys_sysfs */ + .long _sys_personality + .long _sys_ni_syscall /* for afs_syscall */ + .long _sys_setfsuid /* setfsuid16 */ + .long _sys_setfsgid /* setfsgid16 */ + .long _sys_llseek /* 140 */ + .long _sys_getdents + .long _sys_ni_syscall /* sys_select */ + .long _sys_flock + .long _sys_ni_syscall /* 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_sched_setparam + .long _sys_sched_getparam /* 155 */ + .long _sys_sched_setscheduler + .long _sys_sched_getscheduler + .long _sys_sched_yield + .long _sys_sched_get_priority_max + .long _sys_sched_get_priority_min /* 160 */ + .long _sys_sched_rr_get_interval + .long _sys_nanosleep + .long _sys_mremap + .long _sys_setresuid /* setresuid16 */ + .long _sys_getresuid /* getresuid16 */ /* 165 */ + .long _sys_ni_syscall /* for vm86 */ + .long _sys_ni_syscall /* old "query_module" */ + .long _sys_ni_syscall /* sys_poll */ + .long _sys_nfsservctl + .long _sys_setresgid /* setresgid16 */ /* 170 */ + .long _sys_getresgid /* getresgid16 */ + .long _sys_prctl + .long _sys_rt_sigreturn + .long _sys_rt_sigaction + .long _sys_rt_sigprocmask /* 175 */ + .long _sys_rt_sigpending + .long _sys_rt_sigtimedwait + .long _sys_rt_sigqueueinfo + .long _sys_rt_sigsuspend + .long _sys_pread64 /* 180 */ + .long _sys_pwrite64 + .long _sys_lchown /* lchown16 */ + .long _sys_getcwd + .long _sys_capget + .long _sys_capset /* 185 */ + .long _sys_sigaltstack + .long _sys_sendfile + .long _sys_ni_syscall /* streams1 */ + .long _sys_ni_syscall /* streams2 */ + .long _sys_vfork /* 190 */ + .long _sys_getrlimit + .long _sys_mmap2 + .long _sys_truncate64 + .long _sys_ftruncate64 + .long _sys_stat64 /* 195 */ + .long _sys_lstat64 + .long _sys_fstat64 + .long _sys_chown + .long _sys_getuid + .long _sys_getgid /* 200 */ + .long _sys_geteuid + .long _sys_getegid + .long _sys_setreuid + .long _sys_setregid + .long _sys_getgroups /* 205 */ + .long _sys_setgroups + .long _sys_fchown + .long _sys_setresuid + .long _sys_getresuid + .long _sys_setresgid /* 210 */ + .long _sys_getresgid + .long _sys_lchown + .long _sys_setuid + .long _sys_setgid + .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_getdents64 /* 220 */ + .long _sys_fcntl64 + .long _sys_ni_syscall /* reserved for TUX */ + .long _sys_ni_syscall + .long _sys_gettid + .long _sys_readahead /* 225 */ + .long _sys_setxattr + .long _sys_lsetxattr + .long _sys_fsetxattr + .long _sys_getxattr + .long _sys_lgetxattr /* 230 */ + .long _sys_fgetxattr + .long _sys_listxattr + .long _sys_llistxattr + .long _sys_flistxattr + .long _sys_removexattr /* 235 */ + .long _sys_lremovexattr + .long _sys_fremovexattr + .long _sys_tkill + .long _sys_sendfile64 + .long _sys_futex /* 240 */ + .long _sys_sched_setaffinity + .long _sys_sched_getaffinity + .long _sys_ni_syscall /* sys_set_thread_area */ + .long _sys_ni_syscall /* sys_get_thread_area */ + .long _sys_io_setup /* 245 */ + .long _sys_io_destroy + .long _sys_io_getevents + .long _sys_io_submit + .long _sys_io_cancel + .long _sys_ni_syscall /* 250 */ /* sys_alloc_hugepages */ + .long _sys_ni_syscall /* sys_freec_hugepages */ + .long _sys_exit_group + .long _sys_lookup_dcookie + .long _sys_bfin_spinlock + .long _sys_epoll_create /* 255 */ + .long _sys_epoll_ctl + .long _sys_epoll_wait + .long _sys_ni_syscall /* remap_file_pages */ + .long _sys_set_tid_address + .long _sys_timer_create /* 260 */ + .long _sys_timer_settime + .long _sys_timer_gettime + .long _sys_timer_getoverrun + .long _sys_timer_delete + .long _sys_clock_settime /* 265 */ + .long _sys_clock_gettime + .long _sys_clock_getres + .long _sys_clock_nanosleep + .long _sys_statfs64 + .long _sys_fstatfs64 /* 270 */ + .long _sys_tgkill + .long _sys_utimes + .long _sys_fadvise64_64 + .long _sys_ni_syscall /* vserver */ + .long _sys_ni_syscall /* 275, mbind */ + .long _sys_ni_syscall /* get_mempolicy */ + .long _sys_ni_syscall /* set_mempolicy */ + .long _sys_mq_open + .long _sys_mq_unlink + .long _sys_mq_timedsend /* 280 */ + .long _sys_mq_timedreceive + .long _sys_mq_notify + .long _sys_mq_getsetattr + .long _sys_ni_syscall /* kexec_load */ + .long _sys_waitid /* 285 */ + .long _sys_add_key + .long _sys_request_key + .long _sys_keyctl + .long _sys_ioprio_set + .long _sys_ioprio_get /* 290 */ + .long _sys_inotify_init + .long _sys_inotify_add_watch + .long _sys_inotify_rm_watch + .long _sys_ni_syscall /* migrate_pages */ + .long _sys_openat /* 295 */ + .long _sys_mkdirat + .long _sys_mknodat + .long _sys_fchownat + .long _sys_futimesat + .long _sys_fstatat64 /* 300 */ + .long _sys_unlinkat + .long _sys_renameat + .long _sys_linkat + .long _sys_symlinkat + .long _sys_readlinkat /* 305 */ + .long _sys_fchmodat + .long _sys_faccessat + .long _sys_pselect6 + .long _sys_ppoll + .long _sys_unshare /* 310 */ + .long _sys_sram_alloc + .long _sys_sram_free + .long _sys_dma_memcpy + .long _sys_accept + .long _sys_bind /* 315 */ + .long _sys_connect + .long _sys_getpeername + .long _sys_getsockname + .long _sys_getsockopt + .long _sys_listen /* 320 */ + .long _sys_recv + .long _sys_recvfrom + .long _sys_recvmsg + .long _sys_send + .long _sys_sendmsg /* 325 */ + .long _sys_sendto + .long _sys_setsockopt + .long _sys_shutdown + .long _sys_socket + .long _sys_socketpair /* 330 */ + .long _sys_semctl + .long _sys_semget + .long _sys_semop + .long _sys_msgctl + .long _sys_msgget /* 335 */ + .long _sys_msgrcv + .long _sys_msgsnd + .long _sys_shmat + .long _sys_shmctl + .long _sys_shmdt /* 340 */ + .long _sys_shmget + .long _sys_splice + .long _sys_sync_file_range + .long _sys_tee + .long _sys_vmsplice /* 345 */ + .long _sys_epoll_pwait + .long _sys_utimensat + .long _sys_signalfd + .long _sys_timerfd_create + .long _sys_eventfd /* 350 */ + .long _sys_pread64 + .long _sys_pwrite64 + .long _sys_fadvise64 + .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 */ + + .rept NR_syscalls-(.-_sys_call_table)/4 + .long _sys_ni_syscall + .endr +END(_sys_call_table) + +_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 diff --git a/arch/blackfin/mach-common/head.S b/arch/blackfin/mach-common/head.S new file mode 100644 index 0000000..f123a62 --- /dev/null +++ b/arch/blackfin/mach-common/head.S @@ -0,0 +1,305 @@ +/* + * Common Blackfin startup code + * + * Copyright 2004-2008 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * 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> + +__INIT + +#define INITIAL_STACK (L1_SCRATCH_START + L1_SCRATCH_LENGTH - 12) + +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; + R0 = 0; + + /* Clear Out All the data and pointer Registers */ + R1 = R0; + R2 = R0; + R3 = R0; + R4 = R0; + R5 = R0; + R6 = R0; + + P0 = R0; + P1 = R0; + P2 = R0; + P3 = R0; + P4 = R0; + P5 = R0; + + LC0 = r0; + LC1 = r0; + L0 = r0; + L1 = r0; + L2 = r0; + L3 = r0; + + /* Clear Out All the DAG Registers */ + B0 = r0; + B1 = r0; + B2 = r0; + B3 = r0; + + I0 = r0; + I1 = r0; + I2 = r0; + I3 = r0; + + M0 = r0; + M1 = r0; + M2 = r0; + M3 = r0; + + /* + * Clear ITEST_COMMAND and DTEST_COMMAND registers, + * Leaving these as non-zero can confuse the emulator + */ + p0.L = LO(DTEST_COMMAND); + p0.H = HI(DTEST_COMMAND); + [p0] = R0; + [p0 + (ITEST_COMMAND - DTEST_COMMAND)] = R0; + CSYNC; + + trace_buffer_init(p0,r0); + P0 = R1; + R0 = R1; + + /* Turn off the icache */ + p0.l = LO(IMEM_CONTROL); + p0.h = HI(IMEM_CONTROL); + R1 = [p0]; + R0 = ~ENICPLB; + R0 = R0 & R1; + [p0] = R0; + SSYNC; + + /* Turn off the dcache */ + p0.l = LO(DMEM_CONTROL); + p0.h = HI(DMEM_CONTROL); + R1 = [p0]; + R0 = ~ENDCPLB; + R0 = R0 & R1; + [p0] = R0; + SSYNC; + + /* in case of double faults, save a few things */ + p0.l = _init_retx; + p0.h = _init_retx; + R0 = RETX; + [P0] = R0; + +#ifdef CONFIG_DEBUG_DOUBLEFAULT + /* Only save these if we are storing them, + * This happens here, since L1 gets clobbered + * below + */ + p0.l = _saved_retx; + p0.h = _saved_retx; + p1.l = _init_saved_retx; + p1.h = _init_saved_retx; + r0 = [p0]; + [p1] = r0; + + p0.l = _saved_dcplb_fault_addr; + p0.h = _saved_dcplb_fault_addr; + p1.l = _init_saved_dcplb_fault_addr; + p1.h = _init_saved_dcplb_fault_addr; + r0 = [p0]; + [p1] = r0; + + p0.l = _saved_icplb_fault_addr; + p0.h = _saved_icplb_fault_addr; + p1.l = _init_saved_icplb_fault_addr; + p1.h = _init_saved_icplb_fault_addr; + r0 = [p0]; + [p1] = r0; + + p0.l = _saved_seqstat; + p0.h = _saved_seqstat; + p1.l = _init_saved_seqstat; + p1.h = _init_saved_seqstat; + r0 = [p0]; + [p1] = r0; +#endif + + /* Initialize stack pointer */ + sp.l = lo(INITIAL_STACK); + sp.h = hi(INITIAL_STACK); + fp = sp; + usp = sp; + +#ifdef CONFIG_EARLY_PRINTK + call _init_early_exception_vectors; +#endif + + /* Put The Code for PLL Programming and SDRAM Programming in L1 ISRAM */ + call _bfin_relocate_l1_mem; +#ifdef CONFIG_BFIN_KERNEL_CLOCK + call _start_dma_code; +#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 */ + + p0.l = lo(EVT15); + p0.h = hi(EVT15); + p1.l = _real_start; + p1.h = _real_start; + [p0] = p1; + csync; + + p0.l = lo(IMASK); + p0.h = hi(IMASK); + p1.l = IMASK_IVG15; + p1.h = 0x0; + [p0] = p1; + csync; + + raise 15; + p0.l = .LWAIT_HERE; + p0.h = .LWAIT_HERE; + 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; + +#if L1_DATA_A_LENGTH > 0 + r1.l = __sbss_l1; + r1.h = __sbss_l1; + r2.l = __ebss_l1; + r2.h = __ebss_l1; + r0 = 0 (z); + r2 = r2 - r1; + cc = r2 == 0; + if cc jump .L_a_l1_done; + r2 >>= 2; + p1 = r1; + p2 = r2; + lsetup (.L_clear_a_l1, .L_clear_a_l1 ) lc0 = p2; +.L_clear_a_l1: + [p1++] = r0; +.L_a_l1_done: +#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; + r0 = 0 (z); + r2 = r2 - r1; + cc = r2 == 0; + if cc jump .L_b_l1_done; + r2 >>= 2; + p1 = r1; + p2 = r2; + lsetup (.L_clear_b_l1, .L_clear_b_l1 ) lc0 = p2; +.L_clear_b_l1: + [p1++] = r0; +.L_b_l1_done: +#endif + +#if L2_LENGTH > 0 + r1.l = __sbss_l2; + r1.h = __sbss_l2; + r2.l = __ebss_l2; + r2.h = __ebss_l2; + r0 = 0 (z); + r2 = r2 - r1; + cc = r2 == 0; + if cc jump .L_l2_done; + r2 >>= 2; + p1 = r1; + p2 = r2; + lsetup (.L_clear_l2, .L_clear_l2 ) lc0 = p2; +.L_clear_l2: + [p1++] = r0; +.L_l2_done: +#endif + + /* Zero out the bss region + * Note: this will fail if bss is 0 bytes ... + */ + r0 = 0 (z); + r1.l = ___bss_start; + r1.h = ___bss_start; + r2.l = ___bss_stop; + r2.h = ___bss_stop; + r2 = r2 - r1; + r2 >>= 2; + p1 = r1; + p2 = r2; + lsetup (.L_clear_bss, .L_clear_bss) lc0 = p2; +.L_clear_bss: + [p1++] = r0; + + /* In case there is a NULL pointer reference, + * zero out region before stext + */ + p1 = r0; + r2.l = __stext; + r2.h = __stext; + r2 >>= 2; + p2 = r2; + lsetup (.L_clear_zero, .L_clear_zero) lc0 = p2; +.L_clear_zero: + [p1++] = r0; + + /* Pass the u-boot arguments to the global value command line */ + R0 = R7; + call _cmdline_init; + + /* Load the current thread pointer and stack */ + sp.l = _init_thread_union; + sp.h = _init_thread_union; + p1 = THREAD_SIZE (z); + sp = sp + p1; + usp = sp; + fp = sp; + jump.l _start_kernel; +ENDPROC(_real_start) + +__FINIT diff --git a/arch/blackfin/mach-common/interrupt.S b/arch/blackfin/mach-common/interrupt.S new file mode 100644 index 0000000..4a2ec7a --- /dev/null +++ b/arch/blackfin/mach-common/interrupt.S @@ -0,0 +1,247 @@ +/* + * 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/ + * + * 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 <asm/blackfin.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> + +.extern _ret_from_exception + +#ifdef CONFIG_I_ENTRY_L1 +.section .l1.text +#else +.text +#endif + +.align 4 /* just in case */ + +/* Common interrupt entry code. First we do CLI, then push + * RETI, to keep interrupts disabled, but to allow this state to be changed + * by local_bh_enable. + * R0 contains the interrupt number, while R1 may contain the value of IPEND, + * or garbage if IPEND won't be needed by the ISR. */ +__common_int_entry: + [--sp] = fp; + [--sp] = usp; + + [--sp] = i0; + [--sp] = i1; + [--sp] = i2; + [--sp] = i3; + + [--sp] = m0; + [--sp] = m1; + [--sp] = m2; + [--sp] = m3; + + [--sp] = l0; + [--sp] = l1; + [--sp] = l2; + [--sp] = l3; + + [--sp] = b0; + [--sp] = b1; + [--sp] = b2; + [--sp] = b3; + [--sp] = a0.x; + [--sp] = a0.w; + [--sp] = a1.x; + [--sp] = a1.w; + + [--sp] = LC0; + [--sp] = LC1; + [--sp] = LT0; + [--sp] = LT1; + [--sp] = LB0; + [--sp] = LB1; + + [--sp] = ASTAT; + + [--sp] = r0; /* Skip reserved */ + [--sp] = RETS; + r2 = RETI; + [--sp] = r2; + [--sp] = RETX; + [--sp] = RETN; + [--sp] = RETE; + [--sp] = SEQSTAT; + [--sp] = r1; /* IPEND - R1 may or may not be set up before jumping here. */ + + /* Switch to other method of keeping interrupts disabled. */ +#ifdef CONFIG_DEBUG_HWERR + r1 = 0x3f; + sti r1; +#else + cli r1; +#endif + [--sp] = RETI; /* orig_pc */ + /* Clear all L registers. */ + r1 = 0 (x); + l0 = r1; + l1 = r1; + l2 = r1; + l3 = r1; +#ifdef CONFIG_FRAME_POINTER + fp = 0; +#endif + +#if ANOMALY_05000283 || ANOMALY_05000315 + cc = r7 == r7; + p5.h = HI(CHIPID); + p5.l = LO(CHIPID); + if cc jump 1f; + r7.l = W[p5]; +1: +#endif + r1 = sp; + SP += -12; + call _do_irq; + SP += 12; + call _return_from_int; +.Lcommon_restore_context: + RESTORE_CONTEXT + rti; + +/* interrupt routine for ivhw - 5 */ +ENTRY(_evt_ivhw) + SAVE_ALL_SYS +#ifdef CONFIG_FRAME_POINTER + fp = 0; +#endif + +#if ANOMALY_05000283 || ANOMALY_05000315 + cc = r7 == r7; + p5.h = HI(CHIPID); + p5.l = LO(CHIPID); + if cc jump 1f; + r7.l = W[p5]; +1: +#endif + +#ifdef CONFIG_HARDWARE_PM + r7 = [sp + PT_SEQSTAT]; + r7 = r7 >>> 0xe; + r6 = 0x1F; + r7 = r7 & r6; + r5 = 0x12; + cc = r7 == r5; + if cc jump .Lcall_do_ovf; /* deal with performance counter overflow */ +#endif + # 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; + + /* 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 _trap_c; + SP += 12; + +#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 + + call _ret_from_exception; + +.Lcommon_restore_all_sys: + RESTORE_ALL_SYS + rti; + +#ifdef CONFIG_HARDWARE_PM +.Lcall_do_ovf: + + SP += -12; + call _pm_overflow; + SP += 12; + + jump .Lcommon_restore_all_sys; +#endif + +ENDPROC(_evt_ivhw) + +/* Interrupt routine for evt2 (NMI). + * We don't actually use this, so just return. + * For inner circle type details, please see: + * http://docs.blackfin.uclinux.org/doku.php?id=linux:nmi + */ +ENTRY(_evt_nmi) +.weak _evt_nmi + rtn; +ENDPROC(_evt_nmi) + +/* interrupt routine for core timer - 6 */ +ENTRY(_evt_timer) + TIMER_INTERRUPT_ENTRY(EVT_IVTMR_P) + +/* interrupt routine for evt7 - 7 */ +ENTRY(_evt_evt7) + INTERRUPT_ENTRY(EVT_IVG7_P) +ENTRY(_evt_evt8) + INTERRUPT_ENTRY(EVT_IVG8_P) +ENTRY(_evt_evt9) + INTERRUPT_ENTRY(EVT_IVG9_P) +ENTRY(_evt_evt10) + INTERRUPT_ENTRY(EVT_IVG10_P) +ENTRY(_evt_evt11) + INTERRUPT_ENTRY(EVT_IVG11_P) +ENTRY(_evt_evt12) + INTERRUPT_ENTRY(EVT_IVG12_P) +ENTRY(_evt_evt13) + INTERRUPT_ENTRY(EVT_IVG13_P) + + + /* interrupt routine for system_call - 15 */ +ENTRY(_evt_system_call) + SAVE_CONTEXT_SYSCALL +#ifdef CONFIG_FRAME_POINTER + fp = 0; +#endif + call _system_call; + jump .Lcommon_restore_context; +ENDPROC(_evt_system_call) diff --git a/arch/blackfin/mach-common/ints-priority.c b/arch/blackfin/mach-common/ints-priority.c new file mode 100644 index 0000000..34e8a72 --- /dev/null +++ b/arch/blackfin/mach-common/ints-priority.c @@ -0,0 +1,1152 @@ +/* + * File: arch/blackfin/mach-common/ints-priority.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-2008 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/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) + * - + */ + +/* Initialize this to an actual value to force it into the .data + * section so that we know it is properly initialized at entry into + * the kernel but before bss is initialized to zero (which is where + * it would live otherwise). The 0x1f magic represents the IRQs we + * cannot actually mask out in hardware. + */ +unsigned long irq_flags = 0x1f; + +/* The number of spurious interrupts */ +atomic_t num_spurious; + +#ifdef CONFIG_PM +unsigned long bfin_sic_iwr[3]; /* Up to 3 SIC_IWRx registers */ +unsigned vr_wakeup; +#endif + +struct ivgx { + /* irq number for request_irq, available in mach-bf5xx/irq.h */ + unsigned int irqno; + /* corresponding bit in the SIC_ISR register */ + unsigned int isrflag; +} ivg_table[NR_PERI_INTS]; + +struct ivg_slice { + /* 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++) { + int iar_shift = (irqn & 7) * 4; + if (ivg == (0xf & +#ifndef CONFIG_BF52x + bfin_read32((unsigned long *)SIC_IAR0 + + (irqn >> 3)) >> iar_shift)) { +#else + bfin_read32((unsigned long *)SIC_IAR0 + + ((irqn%32) >> 3) + ((irqn / 32) * 16)) >> iar_shift)) { +#endif + ivg_table[irq_pos].irqno = IVG7 + irqn; + ivg_table[irq_pos].isrflag = 1 << (irqn % 32); + ivg7_13[ivg].istop++; + irq_pos++; + } + } + } +} + +/* + * This is for core internal IRQs + */ + +static void bfin_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) +{ +#ifdef CONFIG_BF53x + bfin_write_SIC_IMASK(bfin_read_SIC_IMASK() & + ~(1 << SIC_SYSIRQ(irq))); +#else + unsigned mask_bank, mask_bit; + mask_bank = SIC_SYSIRQ(irq) / 32; + mask_bit = SIC_SYSIRQ(irq) % 32; + bfin_write_SIC_IMASK(mask_bank, bfin_read_SIC_IMASK(mask_bank) & + ~(1 << mask_bit)); +#endif + SSYNC(); +} + +static void bfin_internal_unmask_irq(unsigned int irq) +{ +#ifdef CONFIG_BF53x + bfin_write_SIC_IMASK(bfin_read_SIC_IMASK() | + (1 << SIC_SYSIRQ(irq))); +#else + unsigned mask_bank, mask_bit; + mask_bank = SIC_SYSIRQ(irq) / 32; + mask_bit = SIC_SYSIRQ(irq) % 32; + bfin_write_SIC_IMASK(mask_bank, bfin_read_SIC_IMASK(mask_bank) | + (1 << mask_bit)); +#endif + SSYNC(); +} + +#ifdef CONFIG_PM +int bfin_internal_set_wake(unsigned int irq, unsigned int state) +{ + unsigned bank, bit, wakeup = 0; + unsigned long flags; + bank = SIC_SYSIRQ(irq) / 32; + bit = SIC_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 IRQ_KEY + case IRQ_KEY: + wakeup |= KPADWE; + break; +#endif +#ifdef CONFIG_BF54x + case IRQ_CNT: + wakeup |= ROTWE; + break; +#endif + default: + break; + } + + local_irq_save(flags); + + if (state) { + bfin_sic_iwr[bank] |= (1 << bit); + vr_wakeup |= wakeup; + + } else { + bfin_sic_iwr[bank] &= ~(1 << bit); + vr_wakeup &= ~wakeup; + } + + local_irq_restore(flags); + + return 0; +} +#endif + +static struct irq_chip bfin_core_irqchip = { + .name = "CORE", + .ack = bfin_ack_noop, + .mask = bfin_core_mask_irq, + .unmask = bfin_core_unmask_irq, +}; + +static struct irq_chip bfin_internal_irqchip = { + .name = "INTN", + .ack = bfin_ack_noop, + .mask = bfin_internal_mask_irq, + .unmask = bfin_internal_unmask_irq, + .mask_ack = bfin_internal_mask_irq, + .disable = bfin_internal_mask_irq, + .enable = bfin_internal_unmask_irq, +#ifdef CONFIG_PM + .set_wake = bfin_internal_set_wake, +#endif +}; + +#ifdef BF537_GENERIC_ERROR_INT_DEMUX +static int error_int_mask; + +static void bfin_generic_error_mask_irq(unsigned int irq) +{ + error_int_mask &= ~(1L << (irq - IRQ_PPI_ERROR)); + + if (!error_int_mask) + bfin_internal_mask_irq(IRQ_GENERIC_ERROR); +} + +static void bfin_generic_error_unmask_irq(unsigned int irq) +{ + bfin_internal_unmask_irq(IRQ_GENERIC_ERROR); + error_int_mask |= 1L << (irq - IRQ_PPI_ERROR); +} + +static struct irq_chip bfin_generic_error_irqchip = { + .name = "ERROR", + .ack = bfin_ack_noop, + .mask_ack = bfin_generic_error_mask_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 *inta_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", + __func__, __FILE__, __LINE__); + +} +#endif /* BF537_GENERIC_ERROR_INT_DEMUX */ + +static inline void bfin_set_irq_handler(unsigned irq, irq_flow_handler_t handle) +{ + struct irq_desc *desc = irq_desc + irq; + /* May not call generic set_irq_handler() due to spinlock + recursion. */ + desc->handle_irq = handle; +} + +#if !defined(CONFIG_BF54x) + +static unsigned short gpio_enabled[gpio_bank(MAX_BLACKFIN_GPIOS)]; +static unsigned short gpio_edge_triggered[gpio_bank(MAX_BLACKFIN_GPIOS)]; + +extern void bfin_gpio_irq_prepare(unsigned gpio); + +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) +{ + u16 gpionr = irq - IRQ_PF0; + + if (!(gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))) + bfin_gpio_irq_prepare(gpionr); + + gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); + bfin_gpio_unmask_irq(irq); + + return 0; +} + +static void bfin_gpio_irq_shutdown(unsigned int irq) +{ + bfin_gpio_mask_irq(irq); + gpio_enabled[gpio_bank(irq - IRQ_PF0)] &= ~gpio_bit(irq - IRQ_PF0); +} + +static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) +{ + 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))) + bfin_gpio_irq_prepare(gpionr); + + gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); + } else { + gpio_enabled[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); + 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); + gpio_edge_triggered[gpio_bank(gpionr)] |= gpio_bit(gpionr); + set_gpio_data(gpionr, 0); + + } else { + set_gpio_edge(gpionr, 0); + gpio_edge_triggered[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); + set_gpio_inen(gpionr, 1); + } + + SSYNC(); + + if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) + bfin_set_irq_handler(irq, handle_edge_irq); + else + bfin_set_irq_handler(irq, handle_level_irq); + + return 0; +} + +#ifdef CONFIG_PM +int bfin_gpio_set_wake(unsigned int irq, unsigned int state) +{ + unsigned gpio = irq_to_gpio(irq); + + if (state) + gpio_pm_wakeup_request(gpio, PM_WAKE_IGNORE); + else + gpio_pm_wakeup_free(gpio); + + return 0; +} +#endif + +static struct irq_chip bfin_gpio_irqchip = { + .name = "GPIO", + .ack = bfin_gpio_ack_irq, + .mask = bfin_gpio_mask_irq, + .mask_ack = bfin_gpio_mask_ack_irq, + .unmask = bfin_gpio_unmask_irq, + .disable = bfin_gpio_mask_irq, + .enable = bfin_gpio_unmask_irq, + .set_type = bfin_gpio_irq_type, + .startup = bfin_gpio_irq_startup, + .shutdown = bfin_gpio_irq_shutdown, +#ifdef CONFIG_PM + .set_wake = bfin_gpio_set_wake, +#endif +}; + +static void bfin_demux_gpio_irq(unsigned int inta_irq, + struct irq_desc *desc) +{ + unsigned int i, gpio, mask, irq, search = 0; + + switch (inta_irq) { +#if defined(CONFIG_BF53x) + case IRQ_PROG_INTA: + irq = IRQ_PF0; + search = 1; + break; +# if defined(BF537_FAMILY) && !(defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)) + case IRQ_MAC_RX: + irq = IRQ_PH0; + break; +# endif +#elif defined(CONFIG_BF52x) + case IRQ_PORTF_INTA: + irq = IRQ_PF0; + break; + case IRQ_PORTG_INTA: + irq = IRQ_PG0; + break; + case IRQ_PORTH_INTA: + irq = IRQ_PH0; + 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; + } + + if (search) { + for (i = 0; i < MAX_BLACKFIN_GPIOS; i += GPIO_BANKSIZE) { + irq += i; + + mask = get_gpiop_data(i) & + (gpio_enabled[gpio_bank(i)] & + get_gpiop_maska(i)); + + while (mask) { + if (mask & 1) { + desc = irq_desc + irq; + desc->handle_irq(irq, desc); + } + irq++; + mask >>= 1; + } + } + } else { + gpio = irq_to_gpio(irq); + mask = get_gpiop_data(gpio) & + (gpio_enabled[gpio_bank(gpio)] & + get_gpiop_maska(gpio)); + + do { + if (mask & 1) { + desc = irq_desc + irq; + desc->handle_irq(irq, desc); + } + irq++; + mask >>= 1; + } while (mask); + } + +} + +#else /* CONFIG_BF54x */ + +#define NR_PINT_SYS_IRQS 4 +#define NR_PINT_BITS 32 +#define NR_PINTS 160 +#define IRQ_NOT_AVAIL 0xFF + +#define PINT_2_BANK(x) ((x) >> 5) +#define PINT_2_BIT(x) ((x) & 0x1F) +#define PINT_BIT(x) (1 << (PINT_2_BIT(x))) + +static unsigned char irq2pint_lut[NR_PINTS]; +static unsigned char pint2irq_lut[NR_PINT_SYS_IRQS * NR_PINT_BITS]; + +static unsigned int gpio_both_edge_triggered[NR_PINT_SYS_IRQS]; +static unsigned short gpio_enabled[gpio_bank(MAX_BLACKFIN_GPIOS)]; + + +struct pin_int_t { + unsigned int mask_set; + unsigned int mask_clear; + unsigned int request; + unsigned int assign; + unsigned int edge_set; + unsigned int edge_clear; + unsigned int invert_set; + unsigned int invert_clear; + unsigned int pinstate; + unsigned int latch; +}; + +static struct 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, +}; + +extern void bfin_gpio_irq_prepare(unsigned gpio); + +inline 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 void bfin_gpio_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); + + if (unlikely(gpio_both_edge_triggered[bank] & pintbit)) { + if (pint[bank]->invert_set & pintbit) + pint[bank]->invert_clear = pintbit; + else + pint[bank]->invert_set = pintbit; + } + pint[bank]->request = pintbit; + + 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); + + if (unlikely(gpio_both_edge_triggered[bank] & pintbit)) { + if (pint[bank]->invert_set & pintbit) + pint[bank]->invert_clear = pintbit; + else + pint[bank]->invert_set = pintbit; + } + + pint[bank]->request = pintbit; + 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) +{ + u16 gpionr = irq_to_gpio(irq); + 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))) + bfin_gpio_irq_prepare(gpionr); + + gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); + bfin_gpio_unmask_irq(irq); + + return 0; +} + +static void bfin_gpio_irq_shutdown(unsigned int irq) +{ + u16 gpionr = irq_to_gpio(irq); + + bfin_gpio_mask_irq(irq); + gpio_enabled[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); +} + +static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) +{ + + u16 gpionr = irq_to_gpio(irq); + u8 pint_val = irq2pint_lut[irq - SYS_IRQS]; + u32 pintbit = PINT_BIT(pint_val); + u8 bank = PINT_2_BANK(pint_val); + + if (pint_val == IRQ_NOT_AVAIL) + return -ENODEV; + + 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))) + bfin_gpio_irq_prepare(gpionr); + + gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); + } else { + gpio_enabled[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); + return 0; + } + + if ((type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW))) + pint[bank]->invert_set = pintbit; /* low or falling edge denoted by one */ + else + pint[bank]->invert_clear = pintbit; /* high or rising edge denoted by zero */ + + if ((type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) + == (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { + + gpio_both_edge_triggered[bank] |= pintbit; + + if (gpio_get_value(gpionr)) + pint[bank]->invert_set = pintbit; + else + pint[bank]->invert_clear = pintbit; + } else { + gpio_both_edge_triggered[bank] &= ~pintbit; + } + + if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { + pint[bank]->edge_set = pintbit; + bfin_set_irq_handler(irq, handle_edge_irq); + } else { + pint[bank]->edge_clear = pintbit; + bfin_set_irq_handler(irq, handle_level_irq); + } + + SSYNC(); + + return 0; +} + +#ifdef CONFIG_PM +u32 pint_saved_masks[NR_PINT_SYS_IRQS]; +u32 pint_wakeup_masks[NR_PINT_SYS_IRQS]; + +int bfin_gpio_set_wake(unsigned int irq, unsigned int state) +{ + u32 pint_irq; + u8 pint_val = irq2pint_lut[irq - SYS_IRQS]; + u32 bank = PINT_2_BANK(pint_val); + u32 pintbit = PINT_BIT(pint_val); + + switch (bank) { + case 0: + pint_irq = IRQ_PINT0; + break; + case 2: + pint_irq = IRQ_PINT2; + break; + case 3: + pint_irq = IRQ_PINT3; + break; + case 1: + pint_irq = IRQ_PINT1; + break; + default: + return -EINVAL; + } + + bfin_internal_set_wake(pint_irq, state); + + if (state) + pint_wakeup_masks[bank] |= pintbit; + else + pint_wakeup_masks[bank] &= ~pintbit; + + return 0; +} + +u32 bfin_pm_setup(void) +{ + u32 val, i; + + for (i = 0; i < NR_PINT_SYS_IRQS; i++) { + val = pint[i]->mask_clear; + pint_saved_masks[i] = val; + if (val ^ pint_wakeup_masks[i]) { + pint[i]->mask_clear = val; + pint[i]->mask_set = pint_wakeup_masks[i]; + } + } + + return 0; +} + +void bfin_pm_restore(void) +{ + u32 i, val; + + for (i = 0; i < NR_PINT_SYS_IRQS; i++) { + val = pint_saved_masks[i]; + if (val ^ pint_wakeup_masks[i]) { + pint[i]->mask_clear = pint[i]->mask_clear; + pint[i]->mask_set = val; + } + } +} +#endif + +static struct irq_chip bfin_gpio_irqchip = { + .name = "GPIO", + .ack = bfin_gpio_ack_irq, + .mask = bfin_gpio_mask_irq, + .mask_ack = bfin_gpio_mask_ack_irq, + .unmask = bfin_gpio_unmask_irq, + .disable = bfin_gpio_mask_irq, + .enable = bfin_gpio_unmask_irq, + .set_type = bfin_gpio_irq_type, + .startup = bfin_gpio_irq_startup, + .shutdown = bfin_gpio_irq_shutdown, +#ifdef CONFIG_PM + .set_wake = bfin_gpio_set_wake, +#endif +}; + +static void bfin_demux_gpio_irq(unsigned int inta_irq, + struct irq_desc *desc) +{ + u8 bank, pint_val; + u32 request, irq; + + switch (inta_irq) { + case IRQ_PINT0: + bank = 0; + break; + case IRQ_PINT2: + bank = 2; + break; + case IRQ_PINT3: + bank = 3; + break; + case IRQ_PINT1: + bank = 1; + break; + default: + return; + } + + 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 + +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 */ +#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) + bfin_write_SIC_IMASK0(SIC_UNMASK_ALL); + bfin_write_SIC_IMASK1(SIC_UNMASK_ALL); +# ifdef CONFIG_BF54x + bfin_write_SIC_IMASK2(SIC_UNMASK_ALL); +# endif +#else + bfin_write_SIC_IMASK(SIC_UNMASK_ALL); +#endif + + local_irq_disable(); + +#if (defined(CONFIG_BF537) || defined(CONFIG_BF536)) + /* Clear EMAC Interrupt Status bits so we can demux it later */ + bfin_write_EMAC_SYSTAT(-1); +#endif + +#ifdef CONFIG_BF54x +# ifdef CONFIG_PINTx_REASSIGN + pint[0]->assign = CONFIG_PINT0_ASSIGN; + pint[1]->assign = CONFIG_PINT1_ASSIGN; + pint[2]->assign = CONFIG_PINT2_ASSIGN; + pint[3]->assign = CONFIG_PINT3_ASSIGN; +# endif + /* Whenever PINTx_ASSIGN is altered init_pint_lut() must be executed! */ + init_pint_lut(); +#endif + + for (irq = 0; irq <= SYS_IRQS; irq++) { + if (irq <= IRQ_CORETMR) + set_irq_chip(irq, &bfin_core_irqchip); + else + set_irq_chip(irq, &bfin_internal_irqchip); + + switch (irq) { +#if defined(CONFIG_BF53x) + case IRQ_PROG_INTA: +# if defined(BF537_FAMILY) && !(defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)) + case IRQ_MAC_RX: +# endif +#elif defined(CONFIG_BF54x) + case IRQ_PINT0: + case IRQ_PINT1: + case IRQ_PINT2: + case IRQ_PINT3: +#elif defined(CONFIG_BF52x) + 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: +#endif + set_irq_chained_handler(irq, + bfin_demux_gpio_irq); + break; +#ifdef BF537_GENERIC_ERROR_INT_DEMUX + case IRQ_GENERIC_ERROR: + set_irq_handler(irq, bfin_demux_error_irq); + + break; +#endif + default: + set_irq_handler(irq, handle_simple_irq); + break; + } + } + +#ifdef BF537_GENERIC_ERROR_INT_DEMUX + for (irq = IRQ_PPI_ERROR; irq <= IRQ_UART1_ERROR; irq++) + set_irq_chip_and_handler(irq, &bfin_generic_error_irqchip, + handle_level_irq); +#endif + + /* if configured as edge, then will be changed to do_edge_IRQ */ + for (irq = GPIO_IRQ_BASE; irq < NR_IRQS; irq++) + set_irq_chip_and_handler(irq, &bfin_gpio_irqchip, + handle_level_irq); + + + 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; + +#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) + bfin_write_SIC_IWR0(IWR_DISABLE_ALL); +#if defined(CONFIG_BF52x) + /* 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 + */ + bfin_write_SIC_IWR1(IWR_ENABLE(10) | IWR_ENABLE(11)); +#else + bfin_write_SIC_IWR1(IWR_DISABLE_ALL); +#endif +# ifdef CONFIG_BF54x + 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 +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; +#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) + unsigned long sic_status[3]; + + sic_status[0] = bfin_read_SIC_ISR0() & bfin_read_SIC_IMASK0(); + sic_status[1] = bfin_read_SIC_ISR1() & bfin_read_SIC_IMASK1(); +#ifdef CONFIG_BF54x + sic_status[2] = bfin_read_SIC_ISR2() & bfin_read_SIC_IMASK2(); +#endif + for (;; ivg++) { + if (ivg >= ivg_stop) { + atomic_inc(&num_spurious); + return; + } + if (sic_status[(ivg->irqno - IVG7) / 32] & ivg->isrflag) + break; + } +#else + unsigned long sic_status; + + 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); +} diff --git a/arch/blackfin/mach-common/irqpanic.c b/arch/blackfin/mach-common/irqpanic.c new file mode 100644 index 0000000..606ded9 --- /dev/null +++ b/arch/blackfin/mach-common/irqpanic.c @@ -0,0 +1,146 @@ +/* + * File: arch/blackfin/mach-common/irqpanic.c + * Based on: + * Author: + * + * Created: ? + * Description: panic kernel with dump information + * + * Modified: rgetz - added cache checking code 14Feb06 + * Copyright 2004-2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/module.h> +#include <linux/kernel_stat.h> +#include <linux/sched.h> +#include <asm/traps.h> +#include <asm/blackfin.h> + +#include "../oprofile/op_blackfin.h" + +#ifdef CONFIG_DEBUG_ICACHE_CHECK +#define L1_ICACHE_START 0xffa10000 +#define L1_ICACHE_END 0xffa13fff +void irq_panic(int reason, struct pt_regs *regs) __attribute__ ((l1_text)); +#endif + +/* + * irq_panic - calls panic with string setup + */ +asmlinkage void irq_panic(int reason, struct pt_regs *regs) +{ +#ifdef CONFIG_DEBUG_ICACHE_CHECK + unsigned int cmd, tag, ca, cache_hi, cache_lo, *pa; + unsigned short i, j, die; + unsigned int bad[10][6]; + + /* check entire cache for coherency + * Since printk is in cacheable memory, + * don't call it until you have checked everything + */ + + die = 0; + i = 0; + + /* check icache */ + + for (ca = L1_ICACHE_START; ca <= L1_ICACHE_END && i < 10; ca += 32) { + + /* Grab various address bits for the itest_cmd fields */ + cmd = (((ca & 0x3000) << 4) | /* ca[13:12] for SBNK[1:0] */ + ((ca & 0x0c00) << 16) | /* ca[11:10] for WAYSEL[1:0] */ + ((ca & 0x3f8)) | /* ca[09:03] for SET[4:0] and DW[1:0] */ + 0); /* Access Tag, Read access */ + + SSYNC(); + bfin_write_ITEST_COMMAND(cmd); + SSYNC(); + tag = bfin_read_ITEST_DATA0(); + SSYNC(); + + /* if tag is marked as valid, check it */ + if (tag & 1) { + /* The icache is arranged in 4 groups of 64-bits */ + for (j = 0; j < 32; j += 8) { + cmd = ((((ca + j) & 0x3000) << 4) | /* ca[13:12] for SBNK[1:0] */ + (((ca + j) & 0x0c00) << 16) | /* ca[11:10] for WAYSEL[1:0] */ + (((ca + j) & 0x3f8)) | /* ca[09:03] for SET[4:0] and DW[1:0] */ + 4); /* Access Data, Read access */ + + SSYNC(); + bfin_write_ITEST_COMMAND(cmd); + SSYNC(); + + cache_hi = bfin_read_ITEST_DATA1(); + cache_lo = bfin_read_ITEST_DATA0(); + + pa = ((unsigned int *)((tag & 0xffffcc00) | + ((ca + j) & ~(0xffffcc00)))); + + /* + * Debugging this, enable + * + * printk("addr: %08x %08x%08x | %08x%08x\n", + * ((unsigned int *)((tag & 0xffffcc00) | ((ca+j) & ~(0xffffcc00)))), + * cache_hi, cache_lo, *(pa+1), *pa); + */ + + if (cache_hi != *(pa + 1) || cache_lo != *pa) { + /* Since icache is not working, stay out of it, by not printing */ + die = 1; + bad[i][0] = (ca + j); + bad[i][1] = cache_hi; + bad[i][2] = cache_lo; + bad[i][3] = ((tag & 0xffffcc00) | + ((ca + j) & ~(0xffffcc00))); + bad[i][4] = *(pa + 1); + bad[i][5] = *(pa); + i++; + } + } + } + } + if (die) { + printk(KERN_EMERG "icache coherency error\n"); + for (j = 0; j <= i; j++) { + printk(KERN_EMERG + "cache address : %08x cache value : %08x%08x\n", + bad[j][0], bad[j][1], bad[j][2]); + printk(KERN_EMERG + "physical address: %08x SDRAM value : %08x%08x\n", + bad[j][3], bad[j][4], bad[j][5]); + } + panic("icache coherency error"); + } else { + printk(KERN_EMERG "icache checked, and OK\n"); + } +#endif + +} + +#ifdef CONFIG_HARDWARE_PM +/* + * call the handler of Performance overflow + */ +asmlinkage void pm_overflow(int irq, struct pt_regs *regs) +{ + pm_overflow_handler(irq, regs); +} +#endif diff --git a/arch/blackfin/mach-common/lock.S b/arch/blackfin/mach-common/lock.S new file mode 100644 index 0000000..9daf012 --- /dev/null +++ b/arch/blackfin/mach-common/lock.S @@ -0,0 +1,223 @@ +/* + * 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/blackfin.h> + +.text + +/* When you come here, it is assumed that + * R0 - Which way to be locked + */ + +ENTRY(_cache_grab_lock) + + [--SP]=( R7:0,P5:0 ); + + P1.H = HI(IMEM_CONTROL); + P1.L = LO(IMEM_CONTROL); + P5.H = HI(ICPLB_ADDR0); + P5.L = LO(ICPLB_ADDR0); + P4.H = HI(ICPLB_DATA0); + P4.L = LO(ICPLB_DATA0); + R7 = R0; + + /* If the code of interest already resides in the cache + * invalidate the entire cache itself. + * invalidate_entire_icache; + */ + + SP += -12; + [--SP] = RETS; + CALL _invalidate_entire_icache; + RETS = [SP++]; + SP += 12; + + /* Disable the Interrupts*/ + + CLI R3; + +.LLOCK_WAY: + + /* Way0 - 0xFFA133E0 + * Way1 - 0xFFA137E0 + * Way2 - 0xFFA13BE0 Total Way Size = 4K + * Way3 - 0xFFA13FE0 + */ + + /* Procedure Ex. -Set the locks for other ways by setting ILOC[3:1] + * Only Way0 of the instruction cache can now be + * replaced by a new code + */ + + R5 = R7; + CC = BITTST(R7,0); + IF CC JUMP .LCLEAR1; + R7 = 0; + BITSET(R7,0); + JUMP .LDONE1; + +.LCLEAR1: + R7 = 0; + BITCLR(R7,0); +.LDONE1: R4 = R7 << 3; + R7 = [P1]; + R7 = R7 | R4; + SSYNC; /* SSYNC required writing to IMEM_CONTROL. */ + .align 8; + [P1] = R7; + SSYNC; + + R7 = R5; + CC = BITTST(R7,1); + IF CC JUMP .LCLEAR2; + R7 = 0; + BITSET(R7,1); + JUMP .LDONE2; + +.LCLEAR2: + R7 = 0; + BITCLR(R7,1); +.LDONE2: R4 = R7 << 3; + R7 = [P1]; + R7 = R7 | R4; + SSYNC; /* SSYNC required writing to IMEM_CONTROL. */ + .align 8; + [P1] = R7; + SSYNC; + + R7 = R5; + CC = BITTST(R7,2); + IF CC JUMP .LCLEAR3; + R7 = 0; + BITSET(R7,2); + JUMP .LDONE3; +.LCLEAR3: + R7 = 0; + BITCLR(R7,2); +.LDONE3: R4 = R7 << 3; + R7 = [P1]; + R7 = R7 | R4; + SSYNC; /* SSYNC required writing to IMEM_CONTROL. */ + .align 8; + [P1] = R7; + SSYNC; + + + R7 = R5; + CC = BITTST(R7,3); + IF CC JUMP .LCLEAR4; + R7 = 0; + BITSET(R7,3); + JUMP .LDONE4; +.LCLEAR4: + R7 = 0; + BITCLR(R7,3); +.LDONE4: R4 = R7 << 3; + R7 = [P1]; + R7 = R7 | R4; + SSYNC; /* SSYNC required writing to IMEM_CONTROL. */ + .align 8; + [P1] = R7; + SSYNC; + + STI R3; + + ( R7:0,P5:0 ) = [SP++]; + + RTS; +ENDPROC(_cache_grab_lock) + +/* After the execution of critical code, the code is now locked into + * the cache way. Now we need to set ILOC. + * + * R0 - Which way to be locked + */ + +ENTRY(_cache_lock) + + [--SP]=( R7:0,P5:0 ); + + P1.H = HI(IMEM_CONTROL); + P1.L = LO(IMEM_CONTROL); + + /* Disable the Interrupts*/ + CLI R3; + + R7 = [P1]; + R2 = ~(0x78) (X); /* mask out ILOC */ + R7 = R7 & R2; + R0 = R0 << 3; + R7 = R0 | R7; + SSYNC; /* SSYNC required writing to IMEM_CONTROL. */ + .align 8; + [P1] = R7; + SSYNC; + /* Renable the Interrupts */ + STI R3; + + ( R7:0,P5:0 ) = [SP++]; + RTS; +ENDPROC(_cache_lock) + +/* Invalidate the Entire Instruction cache by + * disabling IMC bit + */ +ENTRY(_invalidate_entire_icache) + [--SP] = ( R7:5); + + P0.L = LO(IMEM_CONTROL); + P0.H = HI(IMEM_CONTROL); + R7 = [P0]; + + /* 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; + + /* Configures the instruction cache agian */ + R6 = (IMC | ENICPLB); + R7 = R7 | R6; + + CLI R6; + SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */ + .align 8; + [P0] = R7; + SSYNC; + STI R6; + + ( R7:5) = [SP++]; + RTS; +ENDPROC(_invalidate_entire_icache) diff --git a/arch/blackfin/mach-common/pm.c b/arch/blackfin/mach-common/pm.c new file mode 100644 index 0000000..e28c6af --- /dev/null +++ b/arch/blackfin/mach-common/pm.c @@ -0,0 +1,340 @@ +/* + * File: arch/blackfin/mach-common/pm.c + * Based on: arm/mach-omap/pm.c + * Author: Cliff Brake <cbrake@accelent.com> Copyright (c) 2001 + * + * Created: 2001 + * Description: Blackfin power management + * + * Modified: Nicolas Pitre - PXA250 support + * Copyright (c) 2002 Monta Vista Software, Inc. + * David Singleton - OMAP1510 + * Copyright (c) 2002 Monta Vista Software, Inc. + * Dirk Behme <dirk.behme@de.bosch.com> - OMAP1510/1610 + * Copyright 2004 + * Copyright 2004-2008 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/suspend.h> +#include <linux/sched.h> +#include <linux/proc_fs.h> +#include <linux/io.h> +#include <linux/irq.h> + +#include <asm/gpio.h> +#include <asm/dma.h> +#include <asm/dpmc.h> + +#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_H +#define WAKEUP_TYPE PM_WAKE_HIGH +#endif + +#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_L +#define WAKEUP_TYPE PM_WAKE_LOW +#endif + +#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_EDGE_F +#define WAKEUP_TYPE PM_WAKE_FALLING +#endif + +#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_EDGE_R +#define WAKEUP_TYPE PM_WAKE_RISING +#endif + +#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_EDGE_B +#define WAKEUP_TYPE PM_WAKE_BOTH_EDGES +#endif + + +void bfin_pm_suspend_standby_enter(void) +{ + unsigned long flags; + +#ifdef CONFIG_PM_WAKEUP_BY_GPIO + gpio_pm_wakeup_request(CONFIG_PM_WAKEUP_GPIO_NUMBER, WAKEUP_TYPE); +#endif + + local_irq_save(flags); + bfin_pm_standby_setup(); + +#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 + + bfin_pm_standby_restore(); + +#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) + bfin_write_SIC_IWR0(IWR_DISABLE_ALL); +#if defined(CONFIG_BF52x) + /* 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 + */ + bfin_write_SIC_IWR1(IWR_ENABLE(10) | IWR_ENABLE(11)); +#else + bfin_write_SIC_IWR1(IWR_DISABLE_ALL); +#endif +# ifdef CONFIG_BF54x + bfin_write_SIC_IWR2(IWR_DISABLE_ALL); +# endif +#else + bfin_write_SIC_IWR(IWR_DISABLE_ALL); +#endif + + local_irq_restore(flags); +} + +int bf53x_suspend_l1_mem(unsigned char *memptr) +{ + dma_memcpy(memptr, (const void *) L1_CODE_START, L1_CODE_LENGTH); + dma_memcpy(memptr + L1_CODE_LENGTH, (const void *) L1_DATA_A_START, + L1_DATA_A_LENGTH); + dma_memcpy(memptr + L1_CODE_LENGTH + L1_DATA_A_LENGTH, + (const void *) L1_DATA_B_START, L1_DATA_B_LENGTH); + memcpy(memptr + L1_CODE_LENGTH + L1_DATA_A_LENGTH + + L1_DATA_B_LENGTH, (const void *) L1_SCRATCH_START, + L1_SCRATCH_LENGTH); + + return 0; +} + +int bf53x_resume_l1_mem(unsigned char *memptr) +{ + dma_memcpy((void *) L1_CODE_START, memptr, L1_CODE_LENGTH); + dma_memcpy((void *) L1_DATA_A_START, memptr + L1_CODE_LENGTH, + L1_DATA_A_LENGTH); + dma_memcpy((void *) L1_DATA_B_START, memptr + L1_CODE_LENGTH + + L1_DATA_A_LENGTH, L1_DATA_B_LENGTH); + memcpy((void *) L1_SCRATCH_START, memptr + L1_CODE_LENGTH + + L1_DATA_A_LENGTH + L1_DATA_B_LENGTH, L1_SCRATCH_LENGTH); + + return 0; +} + +#ifdef CONFIG_BFIN_WB +static void flushinv_all_dcache(void) +{ + u32 way, bank, subbank, set; + 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 + +static inline void dcache_disable(void) +{ +#ifdef CONFIG_BFIN_DCACHE + unsigned long ctrl; + +#ifdef CONFIG_BFIN_WB + flushinv_all_dcache(); +#endif + SSYNC(); + ctrl = bfin_read_DMEM_CONTROL(); + ctrl &= ~ENDCPLB; + bfin_write_DMEM_CONTROL(ctrl); + SSYNC(); +#endif +} + +static inline void dcache_enable(void) +{ +#ifdef CONFIG_BFIN_DCACHE + unsigned long ctrl; + SSYNC(); + ctrl = bfin_read_DMEM_CONTROL(); + ctrl |= ENDCPLB; + bfin_write_DMEM_CONTROL(ctrl); + SSYNC(); +#endif +} + +static inline void icache_disable(void) +{ +#ifdef CONFIG_BFIN_ICACHE + unsigned long ctrl; + SSYNC(); + ctrl = bfin_read_IMEM_CONTROL(); + ctrl &= ~ENICPLB; + bfin_write_IMEM_CONTROL(ctrl); + SSYNC(); +#endif +} + +static inline void icache_enable(void) +{ +#ifdef CONFIG_BFIN_ICACHE + unsigned long ctrl; + SSYNC(); + ctrl = bfin_read_IMEM_CONTROL(); + ctrl |= ENICPLB; + bfin_write_IMEM_CONTROL(ctrl); + SSYNC(); +#endif +} + +int bfin_pm_suspend_mem_enter(void) +{ + unsigned long flags; + int wakeup, ret; + + unsigned char *memptr = kmalloc(L1_CODE_LENGTH + L1_DATA_A_LENGTH + + L1_DATA_B_LENGTH + L1_SCRATCH_LENGTH, + GFP_KERNEL); + + if (memptr == NULL) { + panic("bf53x_suspend_l1_mem malloc failed"); + return -ENOMEM; + } + + wakeup = bfin_read_VR_CTL() & ~FREQ; + wakeup |= SCKELOW; + +#ifdef CONFIG_PM_BFIN_WAKE_PH6 + wakeup |= PHYWE; +#endif +#ifdef CONFIG_PM_BFIN_WAKE_GP + wakeup |= GPWE; +#endif + + local_irq_save(flags); + + ret = blackfin_dma_suspend(); + + if (ret) { + local_irq_restore(flags); + kfree(memptr); + return ret; + } + + bfin_gpio_pm_hibernate_suspend(); + + dcache_disable(); + icache_disable(); + bf53x_suspend_l1_mem(memptr); + + do_hibernate(wakeup | vr_wakeup); /* Goodbye */ + + bf53x_resume_l1_mem(memptr); + + icache_enable(); + dcache_enable(); + + bfin_gpio_pm_hibernate_restore(); + blackfin_dma_resume(); + + local_irq_restore(flags); + kfree(memptr); + + return 0; +} + +/* + * 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_valid(suspend_state_t state) +{ + return (state == PM_SUSPEND_STANDBY +#ifndef BF533_FAMILY + /* + * 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_enter - Actually enter a sleep state. + * @state: State we're entering. + * + */ +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: + bfin_pm_suspend_mem_enter(); + break; + default: + return -EINVAL; + } + + return 0; +} + +struct platform_suspend_ops bfin_pm_ops = { + .enter = bfin_pm_enter, + .valid = bfin_pm_valid, +}; + +static int __init bfin_pm_init(void) +{ + suspend_set_ops(&bfin_pm_ops); + return 0; +} + +__initcall(bfin_pm_init); |