aboutsummaryrefslogtreecommitdiffstats
path: root/arch/blackfin/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/blackfin/kernel')
-rw-r--r--arch/blackfin/kernel/Makefile14
-rw-r--r--arch/blackfin/kernel/asm-offsets.c136
-rw-r--r--arch/blackfin/kernel/bfin_dma_5xx.c742
-rw-r--r--arch/blackfin/kernel/bfin_gpio.c637
-rw-r--r--arch/blackfin/kernel/bfin_ksyms.c119
-rw-r--r--arch/blackfin/kernel/dma-mapping.c183
-rw-r--r--arch/blackfin/kernel/dualcore_test.c49
-rw-r--r--arch/blackfin/kernel/entry.S94
-rw-r--r--arch/blackfin/kernel/flat.c101
-rw-r--r--arch/blackfin/kernel/init_task.c60
-rw-r--r--arch/blackfin/kernel/irqchip.c147
-rw-r--r--arch/blackfin/kernel/module.c429
-rw-r--r--arch/blackfin/kernel/process.c394
-rw-r--r--arch/blackfin/kernel/ptrace.c430
-rw-r--r--arch/blackfin/kernel/setup.c902
-rw-r--r--arch/blackfin/kernel/signal.c356
-rw-r--r--arch/blackfin/kernel/sys_bfin.c115
-rw-r--r--arch/blackfin/kernel/time.c326
-rw-r--r--arch/blackfin/kernel/traps.c649
-rw-r--r--arch/blackfin/kernel/vmlinux.lds.S228
20 files changed, 6111 insertions, 0 deletions
diff --git a/arch/blackfin/kernel/Makefile b/arch/blackfin/kernel/Makefile
new file mode 100644
index 000000000000..f3b7d2f9d49c
--- /dev/null
+++ b/arch/blackfin/kernel/Makefile
@@ -0,0 +1,14 @@
+#
+# arch/blackfin/kernel/Makefile
+#
+
+extra-y := init_task.o vmlinux.lds
+
+obj-y := \
+ entry.o process.o bfin_ksyms.o ptrace.o setup.o signal.o \
+ sys_bfin.o time.o traps.o irqchip.o dma-mapping.o bfin_gpio.o \
+ flat.o
+
+obj-$(CONFIG_MODULES) += module.o
+obj-$(CONFIG_BFIN_DMA_5XX) += bfin_dma_5xx.o
+obj-$(CONFIG_DUAL_CORE_TEST_MODULE) += dualcore_test.o
diff --git a/arch/blackfin/kernel/asm-offsets.c b/arch/blackfin/kernel/asm-offsets.c
new file mode 100644
index 000000000000..41d9a9f89700
--- /dev/null
+++ b/arch/blackfin/kernel/asm-offsets.c
@@ -0,0 +1,136 @@
+/*
+ * File: arch/blackfin/kernel/asm-offsets.c
+ * Based on:
+ * Author:
+ *
+ * Created:
+ * Description: generate definitions needed by assembly language modules.
+ *
+ * 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/stddef.h>
+#include <linux/sched.h>
+#include <linux/kernel_stat.h>
+#include <linux/ptrace.h>
+#include <linux/hardirq.h>
+#include <asm/irq.h>
+#include <asm/thread_info.h>
+
+#define DEFINE(sym, val) \
+ asm volatile("\n->" #sym " %0 " #val : : "i" (val))
+
+int main(void)
+{
+ /* offsets into the task struct */
+ DEFINE(TASK_STATE, offsetof(struct task_struct, state));
+ DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags));
+ DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace));
+ DEFINE(TASK_BLOCKED, offsetof(struct task_struct, blocked));
+ DEFINE(TASK_THREAD, offsetof(struct task_struct, thread));
+ DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, thread_info));
+ DEFINE(TASK_MM, offsetof(struct task_struct, mm));
+ DEFINE(TASK_ACTIVE_MM, offsetof(struct task_struct, active_mm));
+ DEFINE(TASK_SIGPENDING, offsetof(struct task_struct, pending));
+
+ /* offsets into the irq_cpustat_t struct */
+ DEFINE(CPUSTAT_SOFTIRQ_PENDING,
+ offsetof(irq_cpustat_t, __softirq_pending));
+
+ /* offsets into the thread struct */
+ DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp));
+ DEFINE(THREAD_USP, offsetof(struct thread_struct, usp));
+ DEFINE(THREAD_SR, offsetof(struct thread_struct, seqstat));
+ DEFINE(PT_SR, offsetof(struct thread_struct, seqstat));
+ DEFINE(THREAD_ESP0, offsetof(struct thread_struct, esp0));
+ DEFINE(THREAD_PC, offsetof(struct thread_struct, pc));
+ DEFINE(KERNEL_STACK_SIZE, THREAD_SIZE);
+
+ /* offsets into the pt_regs */
+ DEFINE(PT_ORIG_P0, offsetof(struct pt_regs, orig_p0));
+ DEFINE(PT_ORIG_PC, offsetof(struct pt_regs, orig_pc));
+ DEFINE(PT_R0, offsetof(struct pt_regs, r0));
+ DEFINE(PT_R1, offsetof(struct pt_regs, r1));
+ DEFINE(PT_R2, offsetof(struct pt_regs, r2));
+ DEFINE(PT_R3, offsetof(struct pt_regs, r3));
+ DEFINE(PT_R4, offsetof(struct pt_regs, r4));
+ DEFINE(PT_R5, offsetof(struct pt_regs, r5));
+ DEFINE(PT_R6, offsetof(struct pt_regs, r6));
+ DEFINE(PT_R7, offsetof(struct pt_regs, r7));
+
+ DEFINE(PT_P0, offsetof(struct pt_regs, p0));
+ DEFINE(PT_P1, offsetof(struct pt_regs, p1));
+ DEFINE(PT_P2, offsetof(struct pt_regs, p2));
+ DEFINE(PT_P3, offsetof(struct pt_regs, p3));
+ DEFINE(PT_P4, offsetof(struct pt_regs, p4));
+ DEFINE(PT_P5, offsetof(struct pt_regs, p5));
+
+ DEFINE(PT_FP, offsetof(struct pt_regs, fp));
+ DEFINE(PT_USP, offsetof(struct pt_regs, usp));
+ DEFINE(PT_I0, offsetof(struct pt_regs, i0));
+ DEFINE(PT_I1, offsetof(struct pt_regs, i1));
+ DEFINE(PT_I2, offsetof(struct pt_regs, i2));
+ DEFINE(PT_I3, offsetof(struct pt_regs, i3));
+ DEFINE(PT_M0, offsetof(struct pt_regs, m0));
+ DEFINE(PT_M1, offsetof(struct pt_regs, m1));
+ DEFINE(PT_M2, offsetof(struct pt_regs, m2));
+ DEFINE(PT_M3, offsetof(struct pt_regs, m3));
+ DEFINE(PT_L0, offsetof(struct pt_regs, l0));
+ DEFINE(PT_L1, offsetof(struct pt_regs, l1));
+ DEFINE(PT_L2, offsetof(struct pt_regs, l2));
+ DEFINE(PT_L3, offsetof(struct pt_regs, l3));
+ DEFINE(PT_B0, offsetof(struct pt_regs, b0));
+ DEFINE(PT_B1, offsetof(struct pt_regs, b1));
+ DEFINE(PT_B2, offsetof(struct pt_regs, b2));
+ DEFINE(PT_B3, offsetof(struct pt_regs, b3));
+ DEFINE(PT_A0X, offsetof(struct pt_regs, a0x));
+ DEFINE(PT_A0W, offsetof(struct pt_regs, a0w));
+ DEFINE(PT_A1X, offsetof(struct pt_regs, a1x));
+ DEFINE(PT_A1W, offsetof(struct pt_regs, a1w));
+ DEFINE(PT_LC0, offsetof(struct pt_regs, lc0));
+ DEFINE(PT_LC1, offsetof(struct pt_regs, lc1));
+ DEFINE(PT_LT0, offsetof(struct pt_regs, lt0));
+ DEFINE(PT_LT1, offsetof(struct pt_regs, lt1));
+ DEFINE(PT_LB0, offsetof(struct pt_regs, lb0));
+ DEFINE(PT_LB1, offsetof(struct pt_regs, lb1));
+ DEFINE(PT_ASTAT, offsetof(struct pt_regs, astat));
+ DEFINE(PT_RESERVED, offsetof(struct pt_regs, reserved));
+ DEFINE(PT_RETS, offsetof(struct pt_regs, rets));
+ DEFINE(PT_PC, offsetof(struct pt_regs, pc));
+ DEFINE(PT_RETX, offsetof(struct pt_regs, retx));
+ DEFINE(PT_RETN, offsetof(struct pt_regs, retn));
+ DEFINE(PT_RETE, offsetof(struct pt_regs, rete));
+ DEFINE(PT_SEQSTAT, offsetof(struct pt_regs, seqstat));
+ DEFINE(PT_SYSCFG, offsetof(struct pt_regs, syscfg));
+ DEFINE(PT_IPEND, offsetof(struct pt_regs, ipend));
+ DEFINE(SIZEOF_PTREGS, sizeof(struct pt_regs));
+ DEFINE(PT_TEXT_ADDR, sizeof(struct pt_regs)); /* Needed by gdb */
+ DEFINE(PT_TEXT_END_ADDR, 4 + sizeof(struct pt_regs));/* Needed by gdb */
+ DEFINE(PT_DATA_ADDR, 8 + sizeof(struct pt_regs)); /* Needed by gdb */
+ DEFINE(PT_FDPIC_EXEC, 12 + sizeof(struct pt_regs)); /* Needed by gdb */
+ DEFINE(PT_FDPIC_INTERP, 16 + sizeof(struct pt_regs));/* Needed by gdb */
+
+ /* signal defines */
+ DEFINE(SIGSEGV, SIGSEGV);
+ DEFINE(SIGTRAP, SIGTRAP);
+
+ return 0;
+}
diff --git a/arch/blackfin/kernel/bfin_dma_5xx.c b/arch/blackfin/kernel/bfin_dma_5xx.c
new file mode 100644
index 000000000000..8ea079ebecb5
--- /dev/null
+++ b/arch/blackfin/kernel/bfin_dma_5xx.c
@@ -0,0 +1,742 @@
+/*
+ * File: arch/blackfin/kernel/bfin_dma_5xx.c
+ * Based on:
+ * Author:
+ *
+ * Created:
+ * Description: This file contains the simple DMA Implementation for Blackfin
+ *
+ * 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/errno.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+
+#include <asm/dma.h>
+#include <asm/cacheflush.h>
+
+/* Remove unused code not exported by symbol or internally called */
+#define REMOVE_DEAD_CODE
+
+/**************************************************************************
+ * Global Variables
+***************************************************************************/
+
+static struct dma_channel dma_ch[MAX_BLACKFIN_DMA_CHANNEL];
+#if defined (CONFIG_BF561)
+static struct dma_register *base_addr[MAX_BLACKFIN_DMA_CHANNEL] = {
+ (struct dma_register *) DMA1_0_NEXT_DESC_PTR,
+ (struct dma_register *) DMA1_1_NEXT_DESC_PTR,
+ (struct dma_register *) DMA1_2_NEXT_DESC_PTR,
+ (struct dma_register *) DMA1_3_NEXT_DESC_PTR,
+ (struct dma_register *) DMA1_4_NEXT_DESC_PTR,
+ (struct dma_register *) DMA1_5_NEXT_DESC_PTR,
+ (struct dma_register *) DMA1_6_NEXT_DESC_PTR,
+ (struct dma_register *) DMA1_7_NEXT_DESC_PTR,
+ (struct dma_register *) DMA1_8_NEXT_DESC_PTR,
+ (struct dma_register *) DMA1_9_NEXT_DESC_PTR,
+ (struct dma_register *) DMA1_10_NEXT_DESC_PTR,
+ (struct dma_register *) DMA1_11_NEXT_DESC_PTR,
+ (struct dma_register *) DMA2_0_NEXT_DESC_PTR,
+ (struct dma_register *) DMA2_1_NEXT_DESC_PTR,
+ (struct dma_register *) DMA2_2_NEXT_DESC_PTR,
+ (struct dma_register *) DMA2_3_NEXT_DESC_PTR,
+ (struct dma_register *) DMA2_4_NEXT_DESC_PTR,
+ (struct dma_register *) DMA2_5_NEXT_DESC_PTR,
+ (struct dma_register *) DMA2_6_NEXT_DESC_PTR,
+ (struct dma_register *) DMA2_7_NEXT_DESC_PTR,
+ (struct dma_register *) DMA2_8_NEXT_DESC_PTR,
+ (struct dma_register *) DMA2_9_NEXT_DESC_PTR,
+ (struct dma_register *) DMA2_10_NEXT_DESC_PTR,
+ (struct dma_register *) DMA2_11_NEXT_DESC_PTR,
+ (struct dma_register *) MDMA1_D0_NEXT_DESC_PTR,
+ (struct dma_register *) MDMA1_S0_NEXT_DESC_PTR,
+ (struct dma_register *) MDMA1_D1_NEXT_DESC_PTR,
+ (struct dma_register *) MDMA1_S1_NEXT_DESC_PTR,
+ (struct dma_register *) MDMA2_D0_NEXT_DESC_PTR,
+ (struct dma_register *) MDMA2_S0_NEXT_DESC_PTR,
+ (struct dma_register *) MDMA2_D1_NEXT_DESC_PTR,
+ (struct dma_register *) MDMA2_S1_NEXT_DESC_PTR,
+ (struct dma_register *) IMDMA_D0_NEXT_DESC_PTR,
+ (struct dma_register *) IMDMA_S0_NEXT_DESC_PTR,
+ (struct dma_register *) IMDMA_D1_NEXT_DESC_PTR,
+ (struct dma_register *) IMDMA_S1_NEXT_DESC_PTR,
+};
+#else
+static struct dma_register *base_addr[MAX_BLACKFIN_DMA_CHANNEL] = {
+ (struct dma_register *) DMA0_NEXT_DESC_PTR,
+ (struct dma_register *) DMA1_NEXT_DESC_PTR,
+ (struct dma_register *) DMA2_NEXT_DESC_PTR,
+ (struct dma_register *) DMA3_NEXT_DESC_PTR,
+ (struct dma_register *) DMA4_NEXT_DESC_PTR,
+ (struct dma_register *) DMA5_NEXT_DESC_PTR,
+ (struct dma_register *) DMA6_NEXT_DESC_PTR,
+ (struct dma_register *) DMA7_NEXT_DESC_PTR,
+#if (defined(CONFIG_BF537) || defined(CONFIG_BF534) || defined(CONFIG_BF536))
+ (struct dma_register *) DMA8_NEXT_DESC_PTR,
+ (struct dma_register *) DMA9_NEXT_DESC_PTR,
+ (struct dma_register *) DMA10_NEXT_DESC_PTR,
+ (struct dma_register *) DMA11_NEXT_DESC_PTR,
+#endif
+ (struct dma_register *) MDMA_D0_NEXT_DESC_PTR,
+ (struct dma_register *) MDMA_S0_NEXT_DESC_PTR,
+ (struct dma_register *) MDMA_D1_NEXT_DESC_PTR,
+ (struct dma_register *) MDMA_S1_NEXT_DESC_PTR,
+};
+#endif
+
+/*------------------------------------------------------------------------------
+ * Set the Buffer Clear bit in the Configuration register of specific DMA
+ * channel. This will stop the descriptor based DMA operation.
+ *-----------------------------------------------------------------------------*/
+static void clear_dma_buffer(unsigned int channel)
+{
+ dma_ch[channel].regs->cfg |= RESTART;
+ SSYNC();
+ dma_ch[channel].regs->cfg &= ~RESTART;
+ SSYNC();
+}
+
+int __init blackfin_dma_init(void)
+{
+ int i;
+
+ printk(KERN_INFO "Blackfin DMA Controller\n");
+
+ for (i = 0; i < MAX_BLACKFIN_DMA_CHANNEL; i++) {
+ dma_ch[i].chan_status = DMA_CHANNEL_FREE;
+ dma_ch[i].regs = base_addr[i];
+ mutex_init(&(dma_ch[i].dmalock));
+ }
+
+ return 0;
+}
+
+arch_initcall(blackfin_dma_init);
+
+/*
+ * Form the channel find the irq number for that channel.
+ */
+#if !defined(CONFIG_BF561)
+
+static int bf533_channel2irq(unsigned int channel)
+{
+ int ret_irq = -1;
+
+ switch (channel) {
+ case CH_PPI:
+ ret_irq = IRQ_PPI;
+ break;
+
+#if (defined(CONFIG_BF537) || defined(CONFIG_BF534) || defined(CONFIG_BF536))
+ case CH_EMAC_RX:
+ ret_irq = IRQ_MAC_RX;
+ break;
+
+ case CH_EMAC_TX:
+ ret_irq = IRQ_MAC_TX;
+ break;
+
+ case CH_UART1_RX:
+ ret_irq = IRQ_UART1_RX;
+ break;
+
+ case CH_UART1_TX:
+ ret_irq = IRQ_UART1_TX;
+ break;
+#endif
+
+ case CH_SPORT0_RX:
+ ret_irq = IRQ_SPORT0_RX;
+ break;
+
+ case CH_SPORT0_TX:
+ ret_irq = IRQ_SPORT0_TX;
+ break;
+
+ case CH_SPORT1_RX:
+ ret_irq = IRQ_SPORT1_RX;
+ break;
+
+ case CH_SPORT1_TX:
+ ret_irq = IRQ_SPORT1_TX;
+ break;
+
+ case CH_SPI:
+ ret_irq = IRQ_SPI;
+ break;
+
+ case CH_UART_RX:
+ ret_irq = IRQ_UART_RX;
+ break;
+
+ case CH_UART_TX:
+ ret_irq = IRQ_UART_TX;
+ break;
+
+ case CH_MEM_STREAM0_SRC:
+ case CH_MEM_STREAM0_DEST:
+ ret_irq = IRQ_MEM_DMA0;
+ break;
+
+ case CH_MEM_STREAM1_SRC:
+ case CH_MEM_STREAM1_DEST:
+ ret_irq = IRQ_MEM_DMA1;
+ break;
+ }
+ return ret_irq;
+}
+
+# define channel2irq(channel) bf533_channel2irq(channel)
+
+#else
+
+static int bf561_channel2irq(unsigned int channel)
+{
+ int ret_irq = -1;
+
+ switch (channel) {
+ case CH_PPI0:
+ ret_irq = IRQ_PPI0;
+ break;
+ case CH_PPI1:
+ ret_irq = IRQ_PPI1;
+ break;
+ case CH_SPORT0_RX:
+ ret_irq = IRQ_SPORT0_RX;
+ break;
+ case CH_SPORT0_TX:
+ ret_irq = IRQ_SPORT0_TX;
+ break;
+ case CH_SPORT1_RX:
+ ret_irq = IRQ_SPORT1_RX;
+ break;
+ case CH_SPORT1_TX:
+ ret_irq = IRQ_SPORT1_TX;
+ break;
+ case CH_SPI:
+ ret_irq = IRQ_SPI;
+ break;
+ case CH_UART_RX:
+ ret_irq = IRQ_UART_RX;
+ break;
+ case CH_UART_TX:
+ ret_irq = IRQ_UART_TX;
+ break;
+
+ case CH_MEM_STREAM0_SRC:
+ case CH_MEM_STREAM0_DEST:
+ ret_irq = IRQ_MEM_DMA0;
+ break;
+ case CH_MEM_STREAM1_SRC:
+ case CH_MEM_STREAM1_DEST:
+ ret_irq = IRQ_MEM_DMA1;
+ break;
+ case CH_MEM_STREAM2_SRC:
+ case CH_MEM_STREAM2_DEST:
+ ret_irq = IRQ_MEM_DMA2;
+ break;
+ case CH_MEM_STREAM3_SRC:
+ case CH_MEM_STREAM3_DEST:
+ ret_irq = IRQ_MEM_DMA3;
+ break;
+
+ case CH_IMEM_STREAM0_SRC:
+ case CH_IMEM_STREAM0_DEST:
+ ret_irq = IRQ_IMEM_DMA0;
+ break;
+ case CH_IMEM_STREAM1_SRC:
+ case CH_IMEM_STREAM1_DEST:
+ ret_irq = IRQ_IMEM_DMA1;
+ break;
+ }
+ return ret_irq;
+}
+
+# define channel2irq(channel) bf561_channel2irq(channel)
+
+#endif
+
+/*------------------------------------------------------------------------------
+ * Request the specific DMA channel from the system.
+ *-----------------------------------------------------------------------------*/
+int request_dma(unsigned int channel, char *device_id)
+{
+
+ pr_debug("request_dma() : BEGIN \n");
+ mutex_lock(&(dma_ch[channel].dmalock));
+
+ if ((dma_ch[channel].chan_status == DMA_CHANNEL_REQUESTED)
+ || (dma_ch[channel].chan_status == DMA_CHANNEL_ENABLED)) {
+ mutex_unlock(&(dma_ch[channel].dmalock));
+ pr_debug("DMA CHANNEL IN USE \n");
+ return -EBUSY;
+ } else {
+ dma_ch[channel].chan_status = DMA_CHANNEL_REQUESTED;
+ pr_debug("DMA CHANNEL IS ALLOCATED \n");
+ }
+
+ mutex_unlock(&(dma_ch[channel].dmalock));
+
+ dma_ch[channel].device_id = device_id;
+ dma_ch[channel].irq_callback = NULL;
+
+ /* This is to be enabled by putting a restriction -
+ * you have to request DMA, before doing any operations on
+ * descriptor/channel
+ */
+ pr_debug("request_dma() : END \n");
+ return channel;
+}
+EXPORT_SYMBOL(request_dma);
+
+int set_dma_callback(unsigned int channel, dma_interrupt_t callback, void *data)
+{
+ int ret_irq = 0;
+
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ if (callback != NULL) {
+ int ret_val;
+ ret_irq = channel2irq(channel);
+
+ dma_ch[channel].data = data;
+
+ ret_val =
+ request_irq(ret_irq, (void *)callback, IRQF_DISABLED,
+ dma_ch[channel].device_id, data);
+ if (ret_val) {
+ printk(KERN_NOTICE
+ "Request irq in DMA engine failed.\n");
+ return -EPERM;
+ }
+ dma_ch[channel].irq_callback = callback;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(set_dma_callback);
+
+void free_dma(unsigned int channel)
+{
+ int ret_irq;
+
+ pr_debug("freedma() : BEGIN \n");
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ /* Halt the DMA */
+ disable_dma(channel);
+ clear_dma_buffer(channel);
+
+ if (dma_ch[channel].irq_callback != NULL) {
+ ret_irq = channel2irq(channel);
+ free_irq(ret_irq, dma_ch[channel].data);
+ }
+
+ /* Clear the DMA Variable in the Channel */
+ mutex_lock(&(dma_ch[channel].dmalock));
+ dma_ch[channel].chan_status = DMA_CHANNEL_FREE;
+ mutex_unlock(&(dma_ch[channel].dmalock));
+
+ pr_debug("freedma() : END \n");
+}
+EXPORT_SYMBOL(free_dma);
+
+void dma_enable_irq(unsigned int channel)
+{
+ int ret_irq;
+
+ pr_debug("dma_enable_irq() : BEGIN \n");
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ ret_irq = channel2irq(channel);
+ enable_irq(ret_irq);
+}
+EXPORT_SYMBOL(dma_enable_irq);
+
+void dma_disable_irq(unsigned int channel)
+{
+ int ret_irq;
+
+ pr_debug("dma_disable_irq() : BEGIN \n");
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ ret_irq = channel2irq(channel);
+ disable_irq(ret_irq);
+}
+EXPORT_SYMBOL(dma_disable_irq);
+
+int dma_channel_active(unsigned int channel)
+{
+ if (dma_ch[channel].chan_status == DMA_CHANNEL_FREE) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+EXPORT_SYMBOL(dma_channel_active);
+
+/*------------------------------------------------------------------------------
+* stop the specific DMA channel.
+*-----------------------------------------------------------------------------*/
+void disable_dma(unsigned int channel)
+{
+ pr_debug("stop_dma() : BEGIN \n");
+
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ dma_ch[channel].regs->cfg &= ~DMAEN; /* Clean the enable bit */
+ SSYNC();
+ dma_ch[channel].chan_status = DMA_CHANNEL_REQUESTED;
+ /* Needs to be enabled Later */
+ pr_debug("stop_dma() : END \n");
+ return;
+}
+EXPORT_SYMBOL(disable_dma);
+
+void enable_dma(unsigned int channel)
+{
+ pr_debug("enable_dma() : BEGIN \n");
+
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ dma_ch[channel].chan_status = DMA_CHANNEL_ENABLED;
+ dma_ch[channel].regs->curr_x_count = 0;
+ dma_ch[channel].regs->curr_y_count = 0;
+
+ dma_ch[channel].regs->cfg |= DMAEN; /* Set the enable bit */
+ SSYNC();
+ pr_debug("enable_dma() : END \n");
+ return;
+}
+EXPORT_SYMBOL(enable_dma);
+
+/*------------------------------------------------------------------------------
+* Set the Start Address register for the specific DMA channel
+* This function can be used for register based DMA,
+* to setup the start address
+* addr: Starting address of the DMA Data to be transferred.
+*-----------------------------------------------------------------------------*/
+void set_dma_start_addr(unsigned int channel, unsigned long addr)
+{
+ pr_debug("set_dma_start_addr() : BEGIN \n");
+
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ dma_ch[channel].regs->start_addr = addr;
+ SSYNC();
+ pr_debug("set_dma_start_addr() : END\n");
+}
+EXPORT_SYMBOL(set_dma_start_addr);
+
+void set_dma_next_desc_addr(unsigned int channel, unsigned long addr)
+{
+ pr_debug("set_dma_next_desc_addr() : BEGIN \n");
+
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ dma_ch[channel].regs->next_desc_ptr = addr;
+ SSYNC();
+ pr_debug("set_dma_start_addr() : END\n");
+}
+EXPORT_SYMBOL(set_dma_next_desc_addr);
+
+void set_dma_x_count(unsigned int channel, unsigned short x_count)
+{
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ dma_ch[channel].regs->x_count = x_count;
+ SSYNC();
+}
+EXPORT_SYMBOL(set_dma_x_count);
+
+void set_dma_y_count(unsigned int channel, unsigned short y_count)
+{
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ dma_ch[channel].regs->y_count = y_count;
+ SSYNC();
+}
+EXPORT_SYMBOL(set_dma_y_count);
+
+void set_dma_x_modify(unsigned int channel, short x_modify)
+{
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ dma_ch[channel].regs->x_modify = x_modify;
+ SSYNC();
+}
+EXPORT_SYMBOL(set_dma_x_modify);
+
+void set_dma_y_modify(unsigned int channel, short y_modify)
+{
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ dma_ch[channel].regs->y_modify = y_modify;
+ SSYNC();
+}
+EXPORT_SYMBOL(set_dma_y_modify);
+
+void set_dma_config(unsigned int channel, unsigned short config)
+{
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ dma_ch[channel].regs->cfg = config;
+ SSYNC();
+}
+EXPORT_SYMBOL(set_dma_config);
+
+unsigned short
+set_bfin_dma_config(char direction, char flow_mode,
+ char intr_mode, char dma_mode, char width)
+{
+ unsigned short config;
+
+ config =
+ ((direction << 1) | (width << 2) | (dma_mode << 4) |
+ (intr_mode << 6) | (flow_mode << 12) | RESTART);
+ return config;
+}
+EXPORT_SYMBOL(set_bfin_dma_config);
+
+void set_dma_sg(unsigned int channel, struct dmasg * sg, int nr_sg)
+{
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ dma_ch[channel].regs->cfg |= ((nr_sg & 0x0F) << 8);
+
+ dma_ch[channel].regs->next_desc_ptr = (unsigned int)sg;
+
+ SSYNC();
+}
+EXPORT_SYMBOL(set_dma_sg);
+
+/*------------------------------------------------------------------------------
+ * Get the DMA status of a specific DMA channel from the system.
+ *-----------------------------------------------------------------------------*/
+unsigned short get_dma_curr_irqstat(unsigned int channel)
+{
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ return dma_ch[channel].regs->irq_status;
+}
+EXPORT_SYMBOL(get_dma_curr_irqstat);
+
+/*------------------------------------------------------------------------------
+ * Clear the DMA_DONE bit in DMA status. Stop the DMA completion interrupt.
+ *-----------------------------------------------------------------------------*/
+void clear_dma_irqstat(unsigned int channel)
+{
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+ dma_ch[channel].regs->irq_status |= 3;
+}
+EXPORT_SYMBOL(clear_dma_irqstat);
+
+/*------------------------------------------------------------------------------
+ * Get current DMA xcount of a specific DMA channel from the system.
+ *-----------------------------------------------------------------------------*/
+unsigned short get_dma_curr_xcount(unsigned int channel)
+{
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ return dma_ch[channel].regs->curr_x_count;
+}
+EXPORT_SYMBOL(get_dma_curr_xcount);
+
+/*------------------------------------------------------------------------------
+ * Get current DMA ycount of a specific DMA channel from the system.
+ *-----------------------------------------------------------------------------*/
+unsigned short get_dma_curr_ycount(unsigned int channel)
+{
+ BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
+ && channel < MAX_BLACKFIN_DMA_CHANNEL));
+
+ return dma_ch[channel].regs->curr_y_count;
+}
+EXPORT_SYMBOL(get_dma_curr_ycount);
+
+void *dma_memcpy(void *dest, const void *src, size_t size)
+{
+ int direction; /* 1 - address decrease, 0 - address increase */
+ int flag_align; /* 1 - address aligned, 0 - address unaligned */
+ int flag_2D; /* 1 - 2D DMA needed, 0 - 1D DMA needed */
+
+ if (size <= 0)
+ return NULL;
+
+ if ((unsigned long)src < memory_end)
+ blackfin_dcache_flush_range((unsigned int)src,
+ (unsigned int)(src + size));
+
+ bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR);
+
+ if ((unsigned long)src < (unsigned long)dest)
+ direction = 1;
+ else
+ direction = 0;
+
+ if ((((unsigned long)dest % 2) == 0) && (((unsigned long)src % 2) == 0)
+ && ((size % 2) == 0))
+ flag_align = 1;
+ else
+ flag_align = 0;
+
+ if (size > 0x10000) /* size > 64K */
+ flag_2D = 1;
+ else
+ flag_2D = 0;
+
+ /* Setup destination and source start address */
+ if (direction) {
+ if (flag_align) {
+ bfin_write_MDMA_D0_START_ADDR(dest + size - 2);
+ bfin_write_MDMA_S0_START_ADDR(src + size - 2);
+ } else {
+ bfin_write_MDMA_D0_START_ADDR(dest + size - 1);
+ bfin_write_MDMA_S0_START_ADDR(src + size - 1);
+ }
+ } else {
+ bfin_write_MDMA_D0_START_ADDR(dest);
+ bfin_write_MDMA_S0_START_ADDR(src);
+ }
+
+ /* Setup destination and source xcount */
+ if (flag_2D) {
+ if (flag_align) {
+ bfin_write_MDMA_D0_X_COUNT(1024 / 2);
+ bfin_write_MDMA_S0_X_COUNT(1024 / 2);
+ } else {
+ bfin_write_MDMA_D0_X_COUNT(1024);
+ bfin_write_MDMA_S0_X_COUNT(1024);
+ }
+ bfin_write_MDMA_D0_Y_COUNT(size >> 10);
+ bfin_write_MDMA_S0_Y_COUNT(size >> 10);
+ } else {
+ if (flag_align) {
+ bfin_write_MDMA_D0_X_COUNT(size / 2);
+ bfin_write_MDMA_S0_X_COUNT(size / 2);
+ } else {
+ bfin_write_MDMA_D0_X_COUNT(size);
+ bfin_write_MDMA_S0_X_COUNT(size);
+ }
+ }
+
+ /* Setup destination and source xmodify and ymodify */
+ if (direction) {
+ if (flag_align) {
+ bfin_write_MDMA_D0_X_MODIFY(-2);
+ bfin_write_MDMA_S0_X_MODIFY(-2);
+ if (flag_2D) {
+ bfin_write_MDMA_D0_Y_MODIFY(-2);
+ bfin_write_MDMA_S0_Y_MODIFY(-2);
+ }
+ } else {
+ bfin_write_MDMA_D0_X_MODIFY(-1);
+ bfin_write_MDMA_S0_X_MODIFY(-1);
+ if (flag_2D) {
+ bfin_write_MDMA_D0_Y_MODIFY(-1);
+ bfin_write_MDMA_S0_Y_MODIFY(-1);
+ }
+ }
+ } else {
+ if (flag_align) {
+ bfin_write_MDMA_D0_X_MODIFY(2);
+ bfin_write_MDMA_S0_X_MODIFY(2);
+ if (flag_2D) {
+ bfin_write_MDMA_D0_Y_MODIFY(2);
+ bfin_write_MDMA_S0_Y_MODIFY(2);
+ }
+ } else {
+ bfin_write_MDMA_D0_X_MODIFY(1);
+ bfin_write_MDMA_S0_X_MODIFY(1);
+ if (flag_2D) {
+ bfin_write_MDMA_D0_Y_MODIFY(1);
+ bfin_write_MDMA_S0_Y_MODIFY(1);
+ }
+ }
+ }
+
+ /* Enable source DMA */
+ if (flag_2D) {
+ if (flag_align) {
+ bfin_write_MDMA_S0_CONFIG(DMAEN | DMA2D | WDSIZE_16);
+ bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | DMA2D | WDSIZE_16);
+ } else {
+ bfin_write_MDMA_S0_CONFIG(DMAEN | DMA2D);
+ bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | DMA2D);
+ }
+ } else {
+ if (flag_align) {
+ bfin_write_MDMA_S0_CONFIG(DMAEN | WDSIZE_16);
+ bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | WDSIZE_16);
+ } else {
+ bfin_write_MDMA_S0_CONFIG(DMAEN);
+ bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN);
+ }
+ }
+
+ while (!(bfin_read_MDMA_D0_IRQ_STATUS() & DMA_DONE))
+ ;
+
+ bfin_write_MDMA_D0_IRQ_STATUS(bfin_read_MDMA_D0_IRQ_STATUS() |
+ (DMA_DONE | DMA_ERR));
+
+ bfin_write_MDMA_S0_CONFIG(0);
+ bfin_write_MDMA_D0_CONFIG(0);
+
+ if ((unsigned long)dest < memory_end)
+ blackfin_dcache_invalidate_range((unsigned int)dest,
+ (unsigned int)(dest + size));
+
+ return dest;
+}
+EXPORT_SYMBOL(dma_memcpy);
+
+void *safe_dma_memcpy(void *dest, const void *src, size_t size)
+{
+ int flags = 0;
+ void *addr;
+ local_irq_save(flags);
+ addr = dma_memcpy(dest, src, size);
+ local_irq_restore(flags);
+ return addr;
+}
+EXPORT_SYMBOL(safe_dma_memcpy);
diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c
new file mode 100644
index 000000000000..e9f24a9a46ba
--- /dev/null
+++ b/arch/blackfin/kernel/bfin_gpio.c
@@ -0,0 +1,637 @@
+/*
+ * File: arch/blackfin/kernel/bfin_gpio.c
+ * Based on:
+ * Author: Michael Hennerich (hennerich@blackfin.uclinux.org)
+ *
+ * Created:
+ * Description: GPIO Abstraction Layer
+ *
+ * Modified:
+ * Copyright 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
+ */
+
+/*
+* Number BF537/6/4 BF561 BF533/2/1
+*
+* GPIO_0 PF0 PF0 PF0
+* GPIO_1 PF1 PF1 PF1
+* GPIO_2 PF2 PF2 PF2
+* GPIO_3 PF3 PF3 PF3
+* GPIO_4 PF4 PF4 PF4
+* GPIO_5 PF5 PF5 PF5
+* GPIO_6 PF6 PF6 PF6
+* GPIO_7 PF7 PF7 PF7
+* GPIO_8 PF8 PF8 PF8
+* GPIO_9 PF9 PF9 PF9
+* GPIO_10 PF10 PF10 PF10
+* GPIO_11 PF11 PF11 PF11
+* GPIO_12 PF12 PF12 PF12
+* GPIO_13 PF13 PF13 PF13
+* GPIO_14 PF14 PF14 PF14
+* GPIO_15 PF15 PF15 PF15
+* GPIO_16 PG0 PF16
+* GPIO_17 PG1 PF17
+* GPIO_18 PG2 PF18
+* GPIO_19 PG3 PF19
+* GPIO_20 PG4 PF20
+* GPIO_21 PG5 PF21
+* GPIO_22 PG6 PF22
+* GPIO_23 PG7 PF23
+* GPIO_24 PG8 PF24
+* GPIO_25 PG9 PF25
+* GPIO_26 PG10 PF26
+* GPIO_27 PG11 PF27
+* GPIO_28 PG12 PF28
+* GPIO_29 PG13 PF29
+* GPIO_30 PG14 PF30
+* GPIO_31 PG15 PF31
+* GPIO_32 PH0 PF32
+* GPIO_33 PH1 PF33
+* GPIO_34 PH2 PF34
+* GPIO_35 PH3 PF35
+* GPIO_36 PH4 PF36
+* GPIO_37 PH5 PF37
+* GPIO_38 PH6 PF38
+* GPIO_39 PH7 PF39
+* GPIO_40 PH8 PF40
+* GPIO_41 PH9 PF41
+* GPIO_42 PH10 PF42
+* GPIO_43 PH11 PF43
+* GPIO_44 PH12 PF44
+* GPIO_45 PH13 PF45
+* GPIO_46 PH14 PF46
+* GPIO_47 PH15 PF47
+*/
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <asm/blackfin.h>
+#include <asm/gpio.h>
+#include <linux/irq.h>
+
+#ifdef BF533_FAMILY
+static struct gpio_port_t *gpio_bankb[gpio_bank(MAX_BLACKFIN_GPIOS)] = {
+ (struct gpio_port_t *) FIO_FLAG_D,
+};
+#endif
+
+#ifdef BF537_FAMILY
+static struct gpio_port_t *gpio_bankb[gpio_bank(MAX_BLACKFIN_GPIOS)] = {
+ (struct gpio_port_t *) PORTFIO,
+ (struct gpio_port_t *) PORTGIO,
+ (struct gpio_port_t *) PORTHIO,
+};
+
+static unsigned short *port_fer[gpio_bank(MAX_BLACKFIN_GPIOS)] = {
+ (unsigned short *) PORTF_FER,
+ (unsigned short *) PORTG_FER,
+ (unsigned short *) PORTH_FER,
+};
+
+#endif
+
+#ifdef BF561_FAMILY
+static struct gpio_port_t *gpio_bankb[gpio_bank(MAX_BLACKFIN_GPIOS)] = {
+ (struct gpio_port_t *) FIO0_FLAG_D,
+ (struct gpio_port_t *) FIO1_FLAG_D,
+ (struct gpio_port_t *) FIO2_FLAG_D,
+};
+#endif
+
+static unsigned short reserved_map[gpio_bank(MAX_BLACKFIN_GPIOS)];
+
+#ifdef CONFIG_PM
+static unsigned short wakeup_map[gpio_bank(MAX_BLACKFIN_GPIOS)];
+static unsigned char wakeup_flags_map[MAX_BLACKFIN_GPIOS];
+static struct gpio_port_s gpio_bank_saved[gpio_bank(MAX_BLACKFIN_GPIOS)];
+
+#ifdef BF533_FAMILY
+static unsigned int sic_iwr_irqs[gpio_bank(MAX_BLACKFIN_GPIOS)] = {IRQ_PROG_INTB};
+#endif
+
+#ifdef BF537_FAMILY
+static unsigned int sic_iwr_irqs[gpio_bank(MAX_BLACKFIN_GPIOS)] = {IRQ_PROG_INTB, IRQ_PORTG_INTB, IRQ_MAC_TX};
+#endif
+
+#ifdef BF561_FAMILY
+static unsigned int sic_iwr_irqs[gpio_bank(MAX_BLACKFIN_GPIOS)] = {IRQ_PROG0_INTB, IRQ_PROG1_INTB, IRQ_PROG2_INTB};
+#endif
+
+#endif /* CONFIG_PM */
+
+inline int check_gpio(unsigned short gpio)
+{
+ if (gpio > MAX_BLACKFIN_GPIOS)
+ return -EINVAL;
+ return 0;
+}
+
+#ifdef BF537_FAMILY
+void port_setup(unsigned short gpio, unsigned short usage)
+{
+ if (usage == GPIO_USAGE) {
+ if (*port_fer[gpio_bank(gpio)] & gpio_bit(gpio))
+ printk(KERN_WARNING "bfin-gpio: Possible Conflict with Peripheral "
+ "usage and GPIO %d detected!\n", gpio);
+ *port_fer[gpio_bank(gpio)] &= ~gpio_bit(gpio);
+ } else
+ *port_fer[gpio_bank(gpio)] |= gpio_bit(gpio);
+ SSYNC();
+}
+#else
+# define port_setup(...) do { } while (0)
+#endif
+
+
+void default_gpio(unsigned short gpio)
+{
+ unsigned short bank,bitmask;
+
+ bank = gpio_bank(gpio);
+ bitmask = gpio_bit(gpio);
+
+ gpio_bankb[bank]->maska_clear = bitmask;
+ gpio_bankb[bank]->maskb_clear = bitmask;
+ SSYNC();
+ gpio_bankb[bank]->inen &= ~bitmask;
+ gpio_bankb[bank]->dir &= ~bitmask;
+ gpio_bankb[bank]->polar &= ~bitmask;
+ gpio_bankb[bank]->both &= ~bitmask;
+ gpio_bankb[bank]->edge &= ~bitmask;
+}
+
+
+int __init bfin_gpio_init(void)
+{
+ int i;
+
+ printk(KERN_INFO "Blackfin GPIO Controller\n");
+
+ for (i = 0; i < MAX_BLACKFIN_GPIOS; i+=GPIO_BANKSIZE)
+ reserved_map[gpio_bank(i)] = 0;
+
+#if defined(BF537_FAMILY) && (defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE))
+# if defined(CONFIG_BFIN_MAC_RMII)
+ reserved_map[PORT_H] = 0xC373;
+# else
+ reserved_map[PORT_H] = 0xFFFF;
+# endif
+#endif
+
+ return 0;
+}
+
+arch_initcall(bfin_gpio_init);
+
+
+/***********************************************************
+*
+* FUNCTIONS: Blackfin General Purpose Ports Access Functions
+*
+* INPUTS/OUTPUTS:
+* gpio - GPIO Number between 0 and MAX_BLACKFIN_GPIOS
+*
+*
+* DESCRIPTION: These functions abstract direct register access
+* to Blackfin processor General Purpose
+* Ports Regsiters
+*
+* CAUTION: These functions do not belong to the GPIO Driver API
+*************************************************************
+* MODIFICATION HISTORY :
+**************************************************************/
+
+/* Set a specific bit */
+
+#define SET_GPIO(name) \
+void set_gpio_ ## name(unsigned short gpio, unsigned short arg) \
+{ \
+ unsigned long flags; \
+ BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio))); \
+ local_irq_save(flags); \
+ if (arg) \
+ gpio_bankb[gpio_bank(gpio)]->name |= gpio_bit(gpio); \
+ else \
+ gpio_bankb[gpio_bank(gpio)]->name &= ~gpio_bit(gpio); \
+ local_irq_restore(flags); \
+} \
+EXPORT_SYMBOL(set_gpio_ ## name);
+
+SET_GPIO(dir)
+SET_GPIO(inen)
+SET_GPIO(polar)
+SET_GPIO(edge)
+SET_GPIO(both)
+
+
+#define SET_GPIO_SC(name) \
+void set_gpio_ ## name(unsigned short gpio, unsigned short arg) \
+{ \
+ BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio))); \
+ if (arg) \
+ gpio_bankb[gpio_bank(gpio)]->name ## _set = gpio_bit(gpio); \
+ else \
+ gpio_bankb[gpio_bank(gpio)]->name ## _clear = gpio_bit(gpio); \
+} \
+EXPORT_SYMBOL(set_gpio_ ## name);
+
+SET_GPIO_SC(maska)
+SET_GPIO_SC(maskb)
+
+#if defined(ANOMALY_05000311)
+void set_gpio_data(unsigned short gpio, unsigned short arg)
+{
+ unsigned long flags;
+ BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio)));
+ local_irq_save(flags);
+ if (arg)
+ gpio_bankb[gpio_bank(gpio)]->data_set = gpio_bit(gpio);
+ else
+ gpio_bankb[gpio_bank(gpio)]->data_clear = gpio_bit(gpio);
+ bfin_read_CHIPID();
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL(set_gpio_data);
+#else
+SET_GPIO_SC(data)
+#endif
+
+
+#if defined(ANOMALY_05000311)
+void set_gpio_toggle(unsigned short gpio)
+{
+ unsigned long flags;
+ BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio)));
+ local_irq_save(flags);
+ gpio_bankb[gpio_bank(gpio)]->toggle = gpio_bit(gpio);
+ bfin_read_CHIPID();
+ local_irq_restore(flags);
+}
+#else
+void set_gpio_toggle(unsigned short gpio)
+{
+ BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio)));
+ gpio_bankb[gpio_bank(gpio)]->toggle = gpio_bit(gpio);
+}
+#endif
+EXPORT_SYMBOL(set_gpio_toggle);
+
+
+/*Set current PORT date (16-bit word)*/
+
+#define SET_GPIO_P(name) \
+void set_gpiop_ ## name(unsigned short gpio, unsigned short arg) \
+{ \
+ gpio_bankb[gpio_bank(gpio)]->name = arg; \
+} \
+EXPORT_SYMBOL(set_gpiop_ ## name);
+
+SET_GPIO_P(dir)
+SET_GPIO_P(inen)
+SET_GPIO_P(polar)
+SET_GPIO_P(edge)
+SET_GPIO_P(both)
+SET_GPIO_P(maska)
+SET_GPIO_P(maskb)
+
+
+#if defined(ANOMALY_05000311)
+void set_gpiop_data(unsigned short gpio, unsigned short arg)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ gpio_bankb[gpio_bank(gpio)]->data = arg;
+ bfin_read_CHIPID();
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL(set_gpiop_data);
+#else
+SET_GPIO_P(data)
+#endif
+
+
+
+/* Get a specific bit */
+
+#define GET_GPIO(name) \
+unsigned short get_gpio_ ## name(unsigned short gpio) \
+{ \
+ return (0x01 & (gpio_bankb[gpio_bank(gpio)]->name >> gpio_sub_n(gpio))); \
+} \
+EXPORT_SYMBOL(get_gpio_ ## name);
+
+GET_GPIO(dir)
+GET_GPIO(inen)
+GET_GPIO(polar)
+GET_GPIO(edge)
+GET_GPIO(both)
+GET_GPIO(maska)
+GET_GPIO(maskb)
+
+
+#if defined(ANOMALY_05000311)
+unsigned short get_gpio_data(unsigned short gpio)
+{
+ unsigned long flags;
+ unsigned short ret;
+ BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio)));
+ local_irq_save(flags);
+ ret = 0x01 & (gpio_bankb[gpio_bank(gpio)]->data >> gpio_sub_n(gpio));
+ bfin_read_CHIPID();
+ local_irq_restore(flags);
+ return ret;
+}
+EXPORT_SYMBOL(get_gpio_data);
+#else
+GET_GPIO(data)
+#endif
+
+/*Get current PORT date (16-bit word)*/
+
+#define GET_GPIO_P(name) \
+unsigned short get_gpiop_ ## name(unsigned short gpio) \
+{ \
+ return (gpio_bankb[gpio_bank(gpio)]->name);\
+} \
+EXPORT_SYMBOL(get_gpiop_ ## name);
+
+GET_GPIO_P(dir)
+GET_GPIO_P(inen)
+GET_GPIO_P(polar)
+GET_GPIO_P(edge)
+GET_GPIO_P(both)
+GET_GPIO_P(maska)
+GET_GPIO_P(maskb)
+
+#if defined(ANOMALY_05000311)
+unsigned short get_gpiop_data(unsigned short gpio)
+{
+ unsigned long flags;
+ unsigned short ret;
+ local_irq_save(flags);
+ ret = gpio_bankb[gpio_bank(gpio)]->data;
+ bfin_read_CHIPID();
+ local_irq_restore(flags);
+ return ret;
+}
+EXPORT_SYMBOL(get_gpiop_data);
+#else
+GET_GPIO_P(data)
+#endif
+
+#ifdef CONFIG_PM
+/***********************************************************
+*
+* FUNCTIONS: Blackfin PM Setup API
+*
+* INPUTS/OUTPUTS:
+* gpio - GPIO Number between 0 and MAX_BLACKFIN_GPIOS
+* type -
+* PM_WAKE_RISING
+* PM_WAKE_FALLING
+* PM_WAKE_HIGH
+* PM_WAKE_LOW
+* PM_WAKE_BOTH_EDGES
+*
+* DESCRIPTION: Blackfin PM Driver API
+*
+* CAUTION:
+*************************************************************
+* MODIFICATION HISTORY :
+**************************************************************/
+int gpio_pm_wakeup_request(unsigned short gpio, unsigned char type)
+{
+ unsigned long flags;
+
+ if ((check_gpio(gpio) < 0) || !type)
+ return -EINVAL;
+
+ local_irq_save(flags);
+
+ wakeup_map[gpio_bank(gpio)] |= gpio_bit(gpio);
+ wakeup_flags_map[gpio] = type;
+ local_irq_restore(flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(gpio_pm_wakeup_request);
+
+void gpio_pm_wakeup_free(unsigned short gpio)
+{
+ unsigned long flags;
+
+ if (check_gpio(gpio) < 0)
+ return;
+
+ local_irq_save(flags);
+
+ wakeup_map[gpio_bank(gpio)] &= ~gpio_bit(gpio);
+
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL(gpio_pm_wakeup_free);
+
+static int bfin_gpio_wakeup_type(unsigned short gpio, unsigned char type)
+{
+ port_setup(gpio, GPIO_USAGE);
+ set_gpio_dir(gpio, 0);
+ set_gpio_inen(gpio, 1);
+
+ if (type & (PM_WAKE_RISING | PM_WAKE_FALLING))
+ set_gpio_edge(gpio, 1);
+ else
+ set_gpio_edge(gpio, 0);
+
+ if ((type & (PM_WAKE_BOTH_EDGES)) == (PM_WAKE_BOTH_EDGES))
+ set_gpio_both(gpio, 1);
+ else
+ set_gpio_both(gpio, 0);
+
+ if ((type & (PM_WAKE_FALLING | PM_WAKE_LOW)))
+ set_gpio_polar(gpio, 1);
+ else
+ set_gpio_polar(gpio, 0);
+
+ SSYNC();
+
+ return 0;
+}
+
+u32 gpio_pm_setup(void)
+{
+ u32 sic_iwr = 0;
+ u16 bank, mask, i, gpio;
+
+ for (i = 0; i < MAX_BLACKFIN_GPIOS; i+=GPIO_BANKSIZE) {
+ mask = wakeup_map[gpio_bank(i)];
+ bank = gpio_bank(i);
+
+ gpio_bank_saved[bank].maskb = gpio_bankb[bank]->maskb;
+ gpio_bankb[bank]->maskb = 0;
+
+ if (mask) {
+#ifdef BF537_FAMILY
+ gpio_bank_saved[bank].fer = *port_fer[bank];
+#endif
+ gpio_bank_saved[bank].inen = gpio_bankb[bank]->inen;
+ gpio_bank_saved[bank].polar = gpio_bankb[bank]->polar;
+ gpio_bank_saved[bank].dir = gpio_bankb[bank]->dir;
+ gpio_bank_saved[bank].edge = gpio_bankb[bank]->edge;
+ gpio_bank_saved[bank].both = gpio_bankb[bank]->both;
+
+ gpio = i;
+
+ while (mask) {
+ if (mask & 1) {
+ bfin_gpio_wakeup_type(gpio, wakeup_flags_map[gpio]);
+ set_gpio_data(gpio, 0); /*Clear*/
+ }
+ gpio++;
+ mask >>= 1;
+ }
+
+ sic_iwr |= 1 << (sic_iwr_irqs[bank] - (IRQ_CORETMR + 1));
+ gpio_bankb[bank]->maskb_set = wakeup_map[gpio_bank(i)];
+ }
+ }
+
+ if (sic_iwr)
+ return sic_iwr;
+ else
+ return IWR_ENABLE_ALL;
+}
+
+
+void gpio_pm_restore(void)
+{
+ u16 bank, mask, i;
+
+ for (i = 0; i < MAX_BLACKFIN_GPIOS; i+=GPIO_BANKSIZE) {
+ mask = wakeup_map[gpio_bank(i)];
+ bank = gpio_bank(i);
+
+ if (mask) {
+#ifdef BF537_FAMILY
+ *port_fer[bank] = gpio_bank_saved[bank].fer;
+#endif
+ gpio_bankb[bank]->inen = gpio_bank_saved[bank].inen;
+ gpio_bankb[bank]->dir = gpio_bank_saved[bank].dir;
+ gpio_bankb[bank]->polar = gpio_bank_saved[bank].polar;
+ gpio_bankb[bank]->edge = gpio_bank_saved[bank].edge;
+ gpio_bankb[bank]->both = gpio_bank_saved[bank].both;
+ }
+
+ gpio_bankb[bank]->maskb = gpio_bank_saved[bank].maskb;
+ }
+}
+
+#endif
+
+/***********************************************************
+*
+* FUNCTIONS: Blackfin GPIO Driver
+*
+* INPUTS/OUTPUTS:
+* gpio - GPIO Number between 0 and MAX_BLACKFIN_GPIOS
+*
+*
+* DESCRIPTION: Blackfin GPIO Driver API
+*
+* CAUTION:
+*************************************************************
+* MODIFICATION HISTORY :
+**************************************************************/
+
+int gpio_request(unsigned short gpio, const char *label)
+{
+ unsigned long flags;
+
+ if (check_gpio(gpio) < 0)
+ return -EINVAL;
+
+ local_irq_save(flags);
+
+ if (unlikely(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio))) {
+ printk(KERN_ERR "bfin-gpio: GPIO %d is already reserved!\n", gpio);
+ dump_stack();
+ local_irq_restore(flags);
+ return -EBUSY;
+ }
+ reserved_map[gpio_bank(gpio)] |= gpio_bit(gpio);
+
+ local_irq_restore(flags);
+
+ port_setup(gpio, GPIO_USAGE);
+
+ return 0;
+}
+EXPORT_SYMBOL(gpio_request);
+
+
+void gpio_free(unsigned short gpio)
+{
+ unsigned long flags;
+
+ if (check_gpio(gpio) < 0)
+ return;
+
+ local_irq_save(flags);
+
+ if (unlikely(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio)))) {
+ printk(KERN_ERR "bfin-gpio: GPIO %d wasn't reserved!\n", gpio);
+ dump_stack();
+ local_irq_restore(flags);
+ return;
+ }
+
+ default_gpio(gpio);
+
+ reserved_map[gpio_bank(gpio)] &= ~gpio_bit(gpio);
+
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL(gpio_free);
+
+
+void gpio_direction_input(unsigned short gpio)
+{
+ unsigned long flags;
+
+ BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio)));
+
+ local_irq_save(flags);
+ gpio_bankb[gpio_bank(gpio)]->dir &= ~gpio_bit(gpio);
+ gpio_bankb[gpio_bank(gpio)]->inen |= gpio_bit(gpio);
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL(gpio_direction_input);
+
+void gpio_direction_output(unsigned short gpio)
+{
+ unsigned long flags;
+
+ BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio)));
+
+ local_irq_save(flags);
+ gpio_bankb[gpio_bank(gpio)]->inen &= ~gpio_bit(gpio);
+ gpio_bankb[gpio_bank(gpio)]->dir |= gpio_bit(gpio);
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL(gpio_direction_output);
diff --git a/arch/blackfin/kernel/bfin_ksyms.c b/arch/blackfin/kernel/bfin_ksyms.c
new file mode 100644
index 000000000000..f64ecb638fab
--- /dev/null
+++ b/arch/blackfin/kernel/bfin_ksyms.c
@@ -0,0 +1,119 @@
+/*
+ * File: arch/blackfin/kernel/bfin_ksyms.c
+ * Based on: none - original work
+ * Author:
+ *
+ * Created:
+ * Description:
+ *
+ * Modified:
+ * Copyright 2004-2006 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <asm/irq.h>
+#include <asm/checksum.h>
+#include <asm/cacheflush.h>
+#include <asm/uaccess.h>
+
+/* platform dependent support */
+
+EXPORT_SYMBOL(__ioremap);
+EXPORT_SYMBOL(strcmp);
+EXPORT_SYMBOL(strncmp);
+EXPORT_SYMBOL(dump_thread);
+
+EXPORT_SYMBOL(ip_fast_csum);
+
+EXPORT_SYMBOL(kernel_thread);
+
+EXPORT_SYMBOL(__up);
+EXPORT_SYMBOL(__down);
+EXPORT_SYMBOL(__down_trylock);
+EXPORT_SYMBOL(__down_interruptible);
+
+EXPORT_SYMBOL(is_in_rom);
+
+/* Networking helper routines. */
+EXPORT_SYMBOL(csum_partial_copy);
+
+/* The following are special because they're not called
+ * explicitly (the C compiler generates them). Fortunately,
+ * their interface isn't gonna change any time soon now, so
+ * it's OK to leave it out of version control.
+ */
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(memchr);
+EXPORT_SYMBOL(get_wchan);
+
+/*
+ * libgcc functions - functions that are used internally by the
+ * compiler... (prototypes are not correct though, but that
+ * doesn't really matter since they're not versioned).
+ */
+extern void __ashldi3(void);
+extern void __ashrdi3(void);
+extern void __smulsi3_highpart(void);
+extern void __umulsi3_highpart(void);
+extern void __divsi3(void);
+extern void __lshrdi3(void);
+extern void __modsi3(void);
+extern void __muldi3(void);
+extern void __udivsi3(void);
+extern void __umodsi3(void);
+
+/* gcc lib functions */
+EXPORT_SYMBOL(__ashldi3);
+EXPORT_SYMBOL(__ashrdi3);
+EXPORT_SYMBOL(__umulsi3_highpart);
+EXPORT_SYMBOL(__smulsi3_highpart);
+EXPORT_SYMBOL(__divsi3);
+EXPORT_SYMBOL(__lshrdi3);
+EXPORT_SYMBOL(__modsi3);
+EXPORT_SYMBOL(__muldi3);
+EXPORT_SYMBOL(__udivsi3);
+EXPORT_SYMBOL(__umodsi3);
+
+EXPORT_SYMBOL(outsb);
+EXPORT_SYMBOL(insb);
+EXPORT_SYMBOL(outsw);
+EXPORT_SYMBOL(insw);
+EXPORT_SYMBOL(outsl);
+EXPORT_SYMBOL(insl);
+EXPORT_SYMBOL(irq_flags);
+EXPORT_SYMBOL(iounmap);
+EXPORT_SYMBOL(blackfin_dcache_invalidate_range);
+EXPORT_SYMBOL(blackfin_icache_dcache_flush_range);
+EXPORT_SYMBOL(blackfin_icache_flush_range);
+EXPORT_SYMBOL(blackfin_dcache_flush_range);
+EXPORT_SYMBOL(blackfin_dflush_page);
+
+EXPORT_SYMBOL(csum_partial);
+EXPORT_SYMBOL(__init_begin);
+EXPORT_SYMBOL(__init_end);
+EXPORT_SYMBOL(_ebss_l1);
+EXPORT_SYMBOL(_stext_l1);
+EXPORT_SYMBOL(_etext_l1);
+EXPORT_SYMBOL(_sdata_l1);
+EXPORT_SYMBOL(_ebss_b_l1);
+EXPORT_SYMBOL(_sdata_b_l1);
diff --git a/arch/blackfin/kernel/dma-mapping.c b/arch/blackfin/kernel/dma-mapping.c
new file mode 100644
index 000000000000..539eb24e062f
--- /dev/null
+++ b/arch/blackfin/kernel/dma-mapping.c
@@ -0,0 +1,183 @@
+/*
+ * File: arch/blackfin/kernel/dma-mapping.c
+ * Based on:
+ * Author:
+ *
+ * Created:
+ * Description: Dynamic DMA mapping support.
+ *
+ * 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/types.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/bootmem.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <asm/cacheflush.h>
+#include <asm/io.h>
+#include <asm/bfin-global.h>
+
+static spinlock_t dma_page_lock;
+static unsigned int *dma_page;
+static unsigned int dma_pages;
+static unsigned long dma_base;
+static unsigned long dma_size;
+static unsigned int dma_initialized;
+
+void dma_alloc_init(unsigned long start, unsigned long end)
+{
+ spin_lock_init(&dma_page_lock);
+ dma_initialized = 0;
+
+ dma_page = (unsigned int *)__get_free_page(GFP_KERNEL);
+ memset(dma_page, 0, PAGE_SIZE);
+ dma_base = PAGE_ALIGN(start);
+ dma_size = PAGE_ALIGN(end) - PAGE_ALIGN(start);
+ dma_pages = dma_size >> PAGE_SHIFT;
+ memset((void *)dma_base, 0, DMA_UNCACHED_REGION);
+ dma_initialized = 1;
+
+ printk(KERN_INFO "%s: dma_page @ 0x%p - %d pages at 0x%08lx\n", __FUNCTION__,
+ dma_page, dma_pages, dma_base);
+}
+
+static inline unsigned int get_pages(size_t size)
+{
+ return ((size - 1) >> PAGE_SHIFT) + 1;
+}
+
+static unsigned long __alloc_dma_pages(unsigned int pages)
+{
+ unsigned long ret = 0, flags;
+ int i, count = 0;
+
+ if (dma_initialized == 0)
+ dma_alloc_init(_ramend - DMA_UNCACHED_REGION, _ramend);
+
+ spin_lock_irqsave(&dma_page_lock, flags);
+
+ for (i = 0; i < dma_pages;) {
+ if (dma_page[i++] == 0) {
+ if (++count == pages) {
+ while (count--)
+ dma_page[--i] = 1;
+ ret = dma_base + (i << PAGE_SHIFT);
+ break;
+ }
+ } else
+ count = 0;
+ }
+ spin_unlock_irqrestore(&dma_page_lock, flags);
+ return ret;
+}
+
+static void __free_dma_pages(unsigned long addr, unsigned int pages)
+{
+ unsigned long page = (addr - dma_base) >> PAGE_SHIFT;
+ unsigned long flags;
+ int i;
+
+ if ((page + pages) > dma_pages) {
+ printk(KERN_ERR "%s: freeing outside range.\n", __FUNCTION__);
+ BUG();
+ }
+
+ spin_lock_irqsave(&dma_page_lock, flags);
+ for (i = page; i < page + pages; i++) {
+ dma_page[i] = 0;
+ }
+ spin_unlock_irqrestore(&dma_page_lock, flags);
+}
+
+void *dma_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t * dma_handle, gfp_t gfp)
+{
+ void *ret;
+
+ ret = (void *)__alloc_dma_pages(get_pages(size));
+
+ if (ret) {
+ memset(ret, 0, size);
+ *dma_handle = virt_to_phys(ret);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(dma_alloc_coherent);
+
+void
+dma_free_coherent(struct device *dev, size_t size, void *vaddr,
+ dma_addr_t dma_handle)
+{
+ __free_dma_pages((unsigned long)vaddr, get_pages(size));
+}
+EXPORT_SYMBOL(dma_free_coherent);
+
+/*
+ * Dummy functions defined for some existing drivers
+ */
+
+dma_addr_t
+dma_map_single(struct device *dev, void *ptr, size_t size,
+ enum dma_data_direction direction)
+{
+ BUG_ON(direction == DMA_NONE);
+
+ invalidate_dcache_range((unsigned long)ptr,
+ (unsigned long)ptr + size);
+
+ return (dma_addr_t) ptr;
+}
+EXPORT_SYMBOL(dma_map_single);
+
+int
+dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+ enum dma_data_direction direction)
+{
+ int i;
+
+ BUG_ON(direction == DMA_NONE);
+
+ for (i = 0; i < nents; i++)
+ invalidate_dcache_range(sg_dma_address(&sg[i]),
+ sg_dma_address(&sg[i]) +
+ sg_dma_len(&sg[i]));
+
+ return nents;
+}
+EXPORT_SYMBOL(dma_map_sg);
+
+void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
+ enum dma_data_direction direction)
+{
+ BUG_ON(direction == DMA_NONE);
+}
+EXPORT_SYMBOL(dma_unmap_single);
+
+void dma_unmap_sg(struct device *dev, struct scatterlist *sg,
+ int nhwentries, enum dma_data_direction direction)
+{
+ BUG_ON(direction == DMA_NONE);
+}
+EXPORT_SYMBOL(dma_unmap_sg);
diff --git a/arch/blackfin/kernel/dualcore_test.c b/arch/blackfin/kernel/dualcore_test.c
new file mode 100644
index 000000000000..8b89c99f9dfa
--- /dev/null
+++ b/arch/blackfin/kernel/dualcore_test.c
@@ -0,0 +1,49 @@
+/*
+ * File: arch/blackfin/kernel/dualcore_test.c
+ * Based on:
+ * Author:
+ *
+ * Created:
+ * Description: Small test code for CoreB on a BF561
+ *
+ * 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/init.h>
+#include <linux/module.h>
+
+static int *testarg = (int*)0xfeb00000;
+
+static int test_init(void)
+{
+ *testarg = 1;
+ printk("Dual core test module inserted: set testarg = [%d]\n @ [%p]\n",
+ *testarg, testarg);
+ return 0;
+}
+
+static void test_exit(void)
+{
+ printk("Dual core test module removed: testarg = [%d]\n", *testarg);
+}
+
+module_init(test_init);
+module_exit(test_exit);
diff --git a/arch/blackfin/kernel/entry.S b/arch/blackfin/kernel/entry.S
new file mode 100644
index 000000000000..5880b270bd50
--- /dev/null
+++ b/arch/blackfin/kernel/entry.S
@@ -0,0 +1,94 @@
+/*
+ * File: arch/blackfin/kernel/entry.S
+ * Based on:
+ * Author:
+ *
+ * Created:
+ * Description:
+ *
+ * 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/thread_info.h>
+#include <asm/errno.h>
+#include <asm/asm-offsets.h>
+
+#include <asm/mach-common/context.S>
+
+#ifdef CONFIG_EXCPT_IRQ_SYSC_L1
+.section .l1.text
+#else
+.text
+#endif
+
+ENTRY(_ret_from_fork)
+ SP += -12;
+ call _schedule_tail;
+ SP += 12;
+ r0 = [sp + PT_IPEND];
+ cc = bittst(r0,1);
+ if cc jump .Lin_kernel;
+ RESTORE_CONTEXT
+ rti;
+.Lin_kernel:
+ bitclr(r0,1);
+ [sp + PT_IPEND] = r0;
+ /* do a 'fake' RTI by jumping to [RETI]
+ * to avoid clearing supervisor mode in child
+ */
+ RESTORE_ALL_SYS
+ p0 = reti;
+ jump (p0);
+
+ENTRY(_sys_fork)
+ r0 = -EINVAL;
+ rts;
+
+ENTRY(_sys_vfork)
+ r0 = sp;
+ r0 += 24;
+ [--sp] = rets;
+ SP += -12;
+ call _bfin_vfork;
+ SP += 12;
+ rets = [sp++];
+ rts;
+
+ENTRY(_sys_clone)
+ r0 = sp;
+ r0 += 24;
+ [--sp] = rets;
+ SP += -12;
+ call _bfin_clone;
+ SP += 12;
+ rets = [sp++];
+ rts;
+
+ENTRY(_sys_rt_sigreturn)
+ r0 = sp;
+ r0 += 24;
+ [--sp] = rets;
+ SP += -12;
+ call _do_rt_sigreturn;
+ SP += 12;
+ rets = [sp++];
+ rts;
diff --git a/arch/blackfin/kernel/flat.c b/arch/blackfin/kernel/flat.c
new file mode 100644
index 000000000000..a92587b628b5
--- /dev/null
+++ b/arch/blackfin/kernel/flat.c
@@ -0,0 +1,101 @@
+/*
+ * arch/blackfin/kernel/flat.c
+ *
+ * Copyright (C) 2007 Analog Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/flat.h>
+
+#define FLAT_BFIN_RELOC_TYPE_16_BIT 0
+#define FLAT_BFIN_RELOC_TYPE_16H_BIT 1
+#define FLAT_BFIN_RELOC_TYPE_32_BIT 2
+
+unsigned long bfin_get_addr_from_rp(unsigned long *ptr,
+ unsigned long relval,
+ unsigned long flags,
+ unsigned long *persistent)
+{
+ unsigned short *usptr = (unsigned short *)ptr;
+ int type = (relval >> 26) & 7;
+ unsigned long val;
+
+ switch (type) {
+ case FLAT_BFIN_RELOC_TYPE_16_BIT:
+ case FLAT_BFIN_RELOC_TYPE_16H_BIT:
+ usptr = (unsigned short *)ptr;
+ pr_debug("*usptr = %x", get_unaligned(usptr));
+ val = get_unaligned(usptr);
+ val += *persistent;
+ break;
+
+ case FLAT_BFIN_RELOC_TYPE_32_BIT:
+ pr_debug("*ptr = %lx", get_unaligned(ptr));
+ val = get_unaligned(ptr);
+ break;
+
+ default:
+ pr_debug("BINFMT_FLAT: Unknown relocation type %x\n",
+ type);
+
+ return 0;
+ }
+
+ /*
+ * Stack-relative relocs contain the offset into the stack, we
+ * have to add the stack's start address here and return 1 from
+ * flat_addr_absolute to prevent the normal address calculations
+ */
+ if (relval & (1 << 29))
+ return val + current->mm->context.end_brk;
+
+ if ((flags & FLAT_FLAG_GOTPIC) == 0)
+ val = htonl(val);
+ return val;
+}
+EXPORT_SYMBOL(bfin_get_addr_from_rp);
+
+/*
+ * Insert the address ADDR into the symbol reference at RP;
+ * RELVAL is the raw relocation-table entry from which RP is derived
+ */
+void bfin_put_addr_at_rp(unsigned long *ptr, unsigned long addr,
+ unsigned long relval)
+{
+ unsigned short *usptr = (unsigned short *)ptr;
+ int type = (relval >> 26) & 7;
+
+ switch (type) {
+ case FLAT_BFIN_RELOC_TYPE_16_BIT:
+ put_unaligned(addr, usptr);
+ pr_debug("new value %x at %p", get_unaligned(usptr),
+ usptr);
+ break;
+
+ case FLAT_BFIN_RELOC_TYPE_16H_BIT:
+ put_unaligned(addr >> 16, usptr);
+ pr_debug("new value %x", get_unaligned(usptr));
+ break;
+
+ case FLAT_BFIN_RELOC_TYPE_32_BIT:
+ put_unaligned(addr, ptr);
+ pr_debug("new ptr =%lx", get_unaligned(ptr));
+ break;
+ }
+}
+EXPORT_SYMBOL(bfin_put_addr_at_rp);
diff --git a/arch/blackfin/kernel/init_task.c b/arch/blackfin/kernel/init_task.c
new file mode 100644
index 000000000000..b45188f8512e
--- /dev/null
+++ b/arch/blackfin/kernel/init_task.c
@@ -0,0 +1,60 @@
+/*
+ * File: arch/blackfin/kernel/init_task.c
+ * Based on:
+ * Author:
+ *
+ * Created:
+ * Description: This file contains the simple DMA Implementation for Blackfin
+ *
+ * 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/mm.h>
+#include <linux/module.h>
+#include <linux/init_task.h>
+#include <linux/mqueue.h>
+
+static struct fs_struct init_fs = INIT_FS;
+static struct files_struct init_files = INIT_FILES;
+static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
+static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
+
+struct mm_struct init_mm = INIT_MM(init_mm);
+EXPORT_SYMBOL(init_mm);
+
+/*
+ * Initial task structure.
+ *
+ * All other task structs will be allocated on slabs in fork.c
+ */
+struct task_struct init_task = INIT_TASK(init_task);
+EXPORT_SYMBOL(init_task);
+
+/*
+ * Initial thread structure.
+ *
+ * We need to make sure that this is 8192-byte aligned due to the
+ * way process stacks are handled. This is done by having a special
+ * "init_task" linker map entry.
+ */
+union thread_union init_thread_union
+ __attribute__ ((__section__(".data.init_task"))) = {
+INIT_THREAD_INFO(init_task)};
diff --git a/arch/blackfin/kernel/irqchip.c b/arch/blackfin/kernel/irqchip.c
new file mode 100644
index 000000000000..df5bf022cf79
--- /dev/null
+++ b/arch/blackfin/kernel/irqchip.c
@@ -0,0 +1,147 @@
+/*
+ * File: arch/blackfin/kernel/irqchip.c
+ * Based on:
+ * Author:
+ *
+ * Created:
+ * Description: This file contains the simple DMA Implementation for Blackfin
+ *
+ * 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/kernel_stat.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/seq_file.h>
+#include <linux/kallsyms.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+static unsigned long irq_err_count;
+static spinlock_t irq_controller_lock;
+
+/*
+ * Dummy mask/unmask handler
+ */
+void dummy_mask_unmask_irq(unsigned int irq)
+{
+}
+
+void ack_bad_irq(unsigned int irq)
+{
+ irq_err_count += 1;
+ printk(KERN_ERR "IRQ: spurious interrupt %d\n", irq);
+}
+EXPORT_SYMBOL(ack_bad_irq);
+
+static struct irq_chip bad_chip = {
+ .ack = dummy_mask_unmask_irq,
+ .mask = dummy_mask_unmask_irq,
+ .unmask = dummy_mask_unmask_irq,
+};
+
+static struct irq_desc bad_irq_desc = {
+ .chip = &bad_chip,
+ .handle_irq = handle_bad_irq,
+ .depth = 1,
+};
+
+int show_interrupts(struct seq_file *p, void *v)
+{
+ int i = *(loff_t *) v;
+ struct irqaction *action;
+ unsigned long flags;
+
+ if (i < NR_IRQS) {
+ spin_lock_irqsave(&irq_desc[i].lock, flags);
+ action = irq_desc[i].action;
+ if (!action)
+ goto unlock;
+
+ seq_printf(p, "%3d: %10u ", i, kstat_irqs(i));
+ seq_printf(p, " %s", action->name);
+ for (action = action->next; action; action = action->next)
+ seq_printf(p, ", %s", action->name);
+
+ seq_putc(p, '\n');
+ unlock:
+ spin_unlock_irqrestore(&irq_desc[i].lock, flags);
+ } else if (i == NR_IRQS) {
+ seq_printf(p, "Err: %10lu\n", irq_err_count);
+ }
+ return 0;
+}
+
+/*
+ * do_IRQ handles all hardware IRQ's. Decoded IRQs should not
+ * come via this function. Instead, they should provide their
+ * own 'handler'
+ */
+
+#ifdef CONFIG_DO_IRQ_L1
+asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs)__attribute__((l1_text));
+#endif
+
+asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
+{
+ struct pt_regs *old_regs;
+ struct irq_desc *desc = irq_desc + irq;
+ unsigned short pending, other_ints;
+
+ old_regs = set_irq_regs(regs);
+
+ /*
+ * Some hardware gives randomly wrong interrupts. Rather
+ * than crashing, do something sensible.
+ */
+ if (irq >= NR_IRQS)
+ desc = &bad_irq_desc;
+
+ irq_enter();
+
+ generic_handle_irq(irq);
+
+ /* If we're the only interrupt running (ignoring IRQ15 which is for
+ syscalls), lower our priority to IRQ14 so that softirqs run at
+ that level. If there's another, lower-level interrupt, irq_exit
+ will defer softirqs to that. */
+ CSYNC();
+ pending = bfin_read_IPEND() & ~0x8000;
+ other_ints = pending & (pending - 1);
+ if (other_ints == 0)
+ lower_to_irq14();
+ irq_exit();
+
+ set_irq_regs(old_regs);
+}
+
+void __init init_IRQ(void)
+{
+ struct irq_desc *desc;
+ int irq;
+
+ spin_lock_init(&irq_controller_lock);
+ for (irq = 0, desc = irq_desc; irq < NR_IRQS; irq++, desc++) {
+ *desc = bad_irq_desc;
+ }
+
+ init_arch_irq();
+}
diff --git a/arch/blackfin/kernel/module.c b/arch/blackfin/kernel/module.c
new file mode 100644
index 000000000000..372f756f1ad9
--- /dev/null
+++ b/arch/blackfin/kernel/module.c
@@ -0,0 +1,429 @@
+/*
+ * File: arch/blackfin/kernel/module.c
+ * Based on:
+ * Author:
+ *
+ * Created:
+ * Description:
+ *
+ * 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/moduleloader.h>
+#include <linux/elf.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <asm/dma.h>
+#include <asm/cacheflush.h>
+
+/*
+ * handle arithmetic relocations.
+ * See binutils/bfd/elf32-bfin.c for more details
+ */
+#define RELOC_STACK_SIZE 100
+static uint32_t reloc_stack[RELOC_STACK_SIZE];
+static unsigned int reloc_stack_tos;
+
+#define is_reloc_stack_empty() ((reloc_stack_tos > 0)?0:1)
+
+static void reloc_stack_push(uint32_t value)
+{
+ reloc_stack[reloc_stack_tos++] = value;
+}
+
+static uint32_t reloc_stack_pop(void)
+{
+ return reloc_stack[--reloc_stack_tos];
+}
+
+static uint32_t reloc_stack_operate(unsigned int oper, struct module *mod)
+{
+ uint32_t value;
+
+ switch (oper) {
+ case R_add:
+ value = reloc_stack[reloc_stack_tos - 2] +
+ reloc_stack[reloc_stack_tos - 1];
+ reloc_stack_tos -= 2;
+ break;
+ case R_sub:
+ value = reloc_stack[reloc_stack_tos - 2] -
+ reloc_stack[reloc_stack_tos - 1];
+ reloc_stack_tos -= 2;
+ break;
+ case R_mult:
+ value = reloc_stack[reloc_stack_tos - 2] *
+ reloc_stack[reloc_stack_tos - 1];
+ reloc_stack_tos -= 2;
+ break;
+ case R_div:
+ value = reloc_stack[reloc_stack_tos - 2] /
+ reloc_stack[reloc_stack_tos - 1];
+ reloc_stack_tos -= 2;
+ break;
+ case R_mod:
+ value = reloc_stack[reloc_stack_tos - 2] %
+ reloc_stack[reloc_stack_tos - 1];
+ reloc_stack_tos -= 2;
+ break;
+ case R_lshift:
+ value = reloc_stack[reloc_stack_tos - 2] <<
+ reloc_stack[reloc_stack_tos - 1];
+ reloc_stack_tos -= 2;
+ break;
+ case R_rshift:
+ value = reloc_stack[reloc_stack_tos - 2] >>
+ reloc_stack[reloc_stack_tos - 1];
+ reloc_stack_tos -= 2;
+ break;
+ case R_and:
+ value = reloc_stack[reloc_stack_tos - 2] &
+ reloc_stack[reloc_stack_tos - 1];
+ reloc_stack_tos -= 2;
+ break;
+ case R_or:
+ value = reloc_stack[reloc_stack_tos - 2] |
+ reloc_stack[reloc_stack_tos - 1];
+ reloc_stack_tos -= 2;
+ break;
+ case R_xor:
+ value = reloc_stack[reloc_stack_tos - 2] ^
+ reloc_stack[reloc_stack_tos - 1];
+ reloc_stack_tos -= 2;
+ break;
+ case R_land:
+ value = reloc_stack[reloc_stack_tos - 2] &&
+ reloc_stack[reloc_stack_tos - 1];
+ reloc_stack_tos -= 2;
+ break;
+ case R_lor:
+ value = reloc_stack[reloc_stack_tos - 2] ||
+ reloc_stack[reloc_stack_tos - 1];
+ reloc_stack_tos -= 2;
+ break;
+ case R_neg:
+ value = -reloc_stack[reloc_stack_tos - 1];
+ reloc_stack_tos--;
+ break;
+ case R_comp:
+ value = ~reloc_stack[reloc_stack_tos - 1];
+ reloc_stack_tos -= 1;
+ break;
+ default:
+ printk(KERN_WARNING "module %s: unhandled reloction\n",
+ mod->name);
+ return 0;
+ }
+
+ /* now push the new value back on stack */
+ reloc_stack_push(value);
+
+ return value;
+}
+
+void *module_alloc(unsigned long size)
+{
+ if (size == 0)
+ return NULL;
+ return vmalloc(size);
+}
+
+/* Free memory returned from module_alloc */
+void module_free(struct module *mod, void *module_region)
+{
+ vfree(module_region);
+}
+
+/* Transfer the section to the L1 memory */
+int
+module_frob_arch_sections(Elf_Ehdr * hdr, Elf_Shdr * sechdrs,
+ char *secstrings, struct module *mod)
+{
+ Elf_Shdr *s, *sechdrs_end = sechdrs + hdr->e_shnum;
+ void *dest = NULL;
+
+ for (s = sechdrs; s < sechdrs_end; ++s) {
+ if ((strcmp(".l1.text", secstrings + s->sh_name) == 0) ||
+ ((strcmp(".text", secstrings + s->sh_name)==0) &&
+ (hdr->e_flags & FLG_CODE_IN_L1) && (s->sh_size > 0))) {
+ mod->arch.text_l1 = s;
+ dest = l1_inst_sram_alloc(s->sh_size);
+ if (dest == NULL) {
+ printk(KERN_ERR
+ "module %s: L1 instruction memory allocation failed\n",
+ mod->name);
+ return -1;
+ }
+ dma_memcpy(dest, (void *)s->sh_addr, s->sh_size);
+ s->sh_flags &= ~SHF_ALLOC;
+ s->sh_addr = (unsigned long)dest;
+ }
+ if ((strcmp(".l1.data", secstrings + s->sh_name) == 0)||
+ ((strcmp(".data", secstrings + s->sh_name)==0) &&
+ (hdr->e_flags & FLG_DATA_IN_L1) && (s->sh_size > 0))) {
+ mod->arch.data_a_l1 = s;
+ dest = l1_data_sram_alloc(s->sh_size);
+ if (dest == NULL) {
+ printk(KERN_ERR
+ "module %s: L1 data memory allocation failed\n",
+ mod->name);
+ return -1;
+ }
+ memcpy(dest, (void *)s->sh_addr, s->sh_size);
+ s->sh_flags &= ~SHF_ALLOC;
+ s->sh_addr = (unsigned long)dest;
+ }
+ if (strcmp(".l1.bss", secstrings + s->sh_name) == 0 ||
+ ((strcmp(".bss", secstrings + s->sh_name)==0) &&
+ (hdr->e_flags & FLG_DATA_IN_L1) && (s->sh_size > 0))) {
+ mod->arch.bss_a_l1 = s;
+ dest = l1_data_sram_alloc(s->sh_size);
+ if (dest == NULL) {
+ printk(KERN_ERR
+ "module %s: L1 data memory allocation failed\n",
+ mod->name);
+ return -1;
+ }
+ memset(dest, 0, s->sh_size);
+ s->sh_flags &= ~SHF_ALLOC;
+ s->sh_addr = (unsigned long)dest;
+ }
+ if (strcmp(".l1.data.B", secstrings + s->sh_name) == 0) {
+ mod->arch.data_b_l1 = s;
+ dest = l1_data_B_sram_alloc(s->sh_size);
+ if (dest == NULL) {
+ printk(KERN_ERR
+ "module %s: L1 data memory allocation failed\n",
+ mod->name);
+ return -1;
+ }
+ memcpy(dest, (void *)s->sh_addr, s->sh_size);
+ s->sh_flags &= ~SHF_ALLOC;
+ s->sh_addr = (unsigned long)dest;
+ }
+ if (strcmp(".l1.bss.B", secstrings + s->sh_name) == 0) {
+ mod->arch.bss_b_l1 = s;
+ dest = l1_data_B_sram_alloc(s->sh_size);
+ if (dest == NULL) {
+ printk(KERN_ERR
+ "module %s: L1 data memory allocation failed\n",
+ mod->name);
+ return -1;
+ }
+ memset(dest, 0, s->sh_size);
+ s->sh_flags &= ~SHF_ALLOC;
+ s->sh_addr = (unsigned long)dest;
+ }
+ }
+ return 0;
+}
+
+int
+apply_relocate(Elf_Shdr * sechdrs, const char *strtab,
+ unsigned int symindex, unsigned int relsec, struct module *me)
+{
+ printk(KERN_ERR "module %s: .rel unsupported\n", me->name);
+ return -ENOEXEC;
+}
+
+/*************************************************************************/
+/* FUNCTION : apply_relocate_add */
+/* ABSTRACT : Blackfin specific relocation handling for the loadable */
+/* modules. Modules are expected to be .o files. */
+/* Arithmetic relocations are handled. */
+/* We do not expect LSETUP to be split and hence is not */
+/* handled. */
+/* R_byte and R_byte2 are also not handled as the gas */
+/* does not generate it. */
+/*************************************************************************/
+int
+apply_relocate_add(Elf_Shdr * sechdrs, const char *strtab,
+ unsigned int symindex, unsigned int relsec,
+ struct module *mod)
+{
+ unsigned int i;
+ unsigned short tmp;
+ Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr;
+ Elf32_Sym *sym;
+ uint32_t *location32;
+ uint16_t *location16;
+ uint32_t value;
+
+ pr_debug("Applying relocate section %u to %u\n", relsec,
+ sechdrs[relsec].sh_info);
+ for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
+ /* This is where to make the change */
+ location16 =
+ (uint16_t *) (sechdrs[sechdrs[relsec].sh_info].sh_addr +
+ rel[i].r_offset);
+ location32 = (uint32_t *) location16;
+ /* This is the symbol it is referring to. Note that all
+ undefined symbols have been resolved. */
+ sym = (Elf32_Sym *) sechdrs[symindex].sh_addr
+ + ELF32_R_SYM(rel[i].r_info);
+ if (is_reloc_stack_empty()) {
+ value = sym->st_value;
+ } else {
+ value = reloc_stack_pop();
+ }
+ value += rel[i].r_addend;
+ pr_debug("location is %x, value is %x type is %d \n",
+ (unsigned int) location32, value,
+ ELF32_R_TYPE(rel[i].r_info));
+
+ switch (ELF32_R_TYPE(rel[i].r_info)) {
+
+ case R_pcrel24:
+ case R_pcrel24_jump_l:
+ /* Add the value, subtract its postition */
+ location16 =
+ (uint16_t *) (sechdrs[sechdrs[relsec].sh_info].
+ sh_addr + rel[i].r_offset - 2);
+ location32 = (uint32_t *) location16;
+ value -= (uint32_t) location32;
+ value >>= 1;
+ pr_debug("value is %x, before %x-%x after %x-%x\n", value,
+ *location16, *(location16 + 1),
+ (*location16 & 0xff00) | (value >> 16 & 0x00ff),
+ value & 0xffff);
+ *location16 =
+ (*location16 & 0xff00) | (value >> 16 & 0x00ff);
+ *(location16 + 1) = value & 0xffff;
+ break;
+ case R_pcrel12_jump:
+ case R_pcrel12_jump_s:
+ value -= (uint32_t) location32;
+ value >>= 1;
+ *location16 = (value & 0xfff);
+ break;
+ case R_pcrel10:
+ value -= (uint32_t) location32;
+ value >>= 1;
+ *location16 = (value & 0x3ff);
+ break;
+ case R_luimm16:
+ pr_debug("before %x after %x\n", *location16,
+ (value & 0xffff));
+ tmp = (value & 0xffff);
+ if((unsigned long)location16 >= L1_CODE_START) {
+ dma_memcpy(location16, &tmp, 2);
+ } else
+ *location16 = tmp;
+ break;
+ case R_huimm16:
+ pr_debug("before %x after %x\n", *location16,
+ ((value >> 16) & 0xffff));
+ tmp = ((value >> 16) & 0xffff);
+ if((unsigned long)location16 >= L1_CODE_START) {
+ dma_memcpy(location16, &tmp, 2);
+ } else
+ *location16 = tmp;
+ break;
+ case R_rimm16:
+ *location16 = (value & 0xffff);
+ break;
+ case R_byte4_data:
+ pr_debug("before %x after %x\n", *location32, value);
+ *location32 = value;
+ break;
+ case R_push:
+ reloc_stack_push(value);
+ break;
+ case R_const:
+ reloc_stack_push(rel[i].r_addend);
+ break;
+ case R_add:
+ case R_sub:
+ case R_mult:
+ case R_div:
+ case R_mod:
+ case R_lshift:
+ case R_rshift:
+ case R_and:
+ case R_or:
+ case R_xor:
+ case R_land:
+ case R_lor:
+ case R_neg:
+ case R_comp:
+ reloc_stack_operate(ELF32_R_TYPE(rel[i].r_info), mod);
+ break;
+ default:
+ printk(KERN_ERR "module %s: Unknown relocation: %u\n",
+ mod->name, ELF32_R_TYPE(rel[i].r_info));
+ return -ENOEXEC;
+ }
+ }
+ return 0;
+}
+
+int
+module_finalize(const Elf_Ehdr * hdr,
+ const Elf_Shdr * sechdrs, struct module *mod)
+{
+ unsigned int i, strindex = 0, symindex = 0;
+ char *secstrings;
+
+ secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+
+ for (i = 1; i < hdr->e_shnum; i++) {
+ /* Internal symbols and strings. */
+ if (sechdrs[i].sh_type == SHT_SYMTAB) {
+ symindex = i;
+ strindex = sechdrs[i].sh_link;
+ }
+ }
+
+ for (i = 1; i < hdr->e_shnum; i++) {
+ const char *strtab = (char *)sechdrs[strindex].sh_addr;
+ unsigned int info = sechdrs[i].sh_info;
+
+ /* Not a valid relocation section? */
+ if (info >= hdr->e_shnum)
+ continue;
+
+ if ((sechdrs[i].sh_type == SHT_RELA) &&
+ ((strcmp(".rela.l1.text", secstrings + sechdrs[i].sh_name) == 0)||
+ ((strcmp(".rela.text", secstrings + sechdrs[i].sh_name) == 0) &&
+ (hdr->e_flags & FLG_CODE_IN_L1)))) {
+ apply_relocate_add((Elf_Shdr *) sechdrs, strtab,
+ symindex, i, mod);
+ }
+ }
+ return 0;
+}
+
+void module_arch_cleanup(struct module *mod)
+{
+ if ((mod->arch.text_l1) && (mod->arch.text_l1->sh_addr))
+ l1_inst_sram_free((void*)mod->arch.text_l1->sh_addr);
+ if ((mod->arch.data_a_l1) && (mod->arch.data_a_l1->sh_addr))
+ l1_data_sram_free((void*)mod->arch.data_a_l1->sh_addr);
+ if ((mod->arch.bss_a_l1) && (mod->arch.bss_a_l1->sh_addr))
+ l1_data_sram_free((void*)mod->arch.bss_a_l1->sh_addr);
+ if ((mod->arch.data_b_l1) && (mod->arch.data_b_l1->sh_addr))
+ l1_data_B_sram_free((void*)mod->arch.data_b_l1->sh_addr);
+ if ((mod->arch.bss_b_l1) && (mod->arch.bss_b_l1->sh_addr))
+ l1_data_B_sram_free((void*)mod->arch.bss_b_l1->sh_addr);
+}
diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c
new file mode 100644
index 000000000000..3eff7439d8d3
--- /dev/null
+++ b/arch/blackfin/kernel/process.c
@@ -0,0 +1,394 @@
+/*
+ * File: arch/blackfin/kernel/process.c
+ * Based on:
+ * Author:
+ *
+ * Created:
+ * Description: Blackfin architecture-dependent process handling.
+ *
+ * Modified:
+ * Copyright 2004-2006 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/unistd.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+
+#include <asm/blackfin.h>
+#include <asm/uaccess.h>
+
+#define LED_ON 0
+#define LED_OFF 1
+
+asmlinkage void ret_from_fork(void);
+
+/* Points to the SDRAM backup memory for the stack that is currently in
+ * L1 scratchpad memory.
+ */
+void *current_l1_stack_save;
+
+/* The number of tasks currently using a L1 stack area. The SRAM is
+ * allocated/deallocated whenever this changes from/to zero.
+ */
+int nr_l1stack_tasks;
+
+/* Start and length of the area in L1 scratchpad memory which we've allocated
+ * for process stacks.
+ */
+void *l1_stack_base;
+unsigned long l1_stack_len;
+
+/*
+ * Powermanagement idle function, if any..
+ */
+void (*pm_idle)(void) = NULL;
+EXPORT_SYMBOL(pm_idle);
+
+void (*pm_power_off)(void) = NULL;
+EXPORT_SYMBOL(pm_power_off);
+
+/*
+ * We are using a different LED from the one used to indicate timer interrupt.
+ */
+#if defined(CONFIG_BFIN_IDLE_LED)
+static inline void leds_switch(int flag)
+{
+ unsigned short tmp = 0;
+
+ tmp = bfin_read_CONFIG_BFIN_IDLE_LED_PORT();
+ SSYNC();
+
+ if (flag == LED_ON)
+ tmp &= ~CONFIG_BFIN_IDLE_LED_PIN; /* light on */
+ else
+ tmp |= CONFIG_BFIN_IDLE_LED_PIN; /* light off */
+
+ bfin_write_CONFIG_BFIN_IDLE_LED_PORT(tmp);
+ SSYNC();
+
+}
+#else
+static inline void leds_switch(int flag)
+{
+}
+#endif
+
+/*
+ * The idle loop on BFIN
+ */
+#ifdef CONFIG_IDLE_L1
+void default_idle(void)__attribute__((l1_text));
+void cpu_idle(void)__attribute__((l1_text));
+#endif
+
+void default_idle(void)
+{
+ while (!need_resched()) {
+ leds_switch(LED_OFF);
+ local_irq_disable();
+ if (likely(!need_resched()))
+ idle_with_irq_disabled();
+ local_irq_enable();
+ leds_switch(LED_ON);
+ }
+}
+
+void (*idle)(void) = default_idle;
+
+/*
+ * The idle thread. There's no useful work to be
+ * done, so just try to conserve power and have a
+ * low exit latency (ie sit in a loop waiting for
+ * somebody to say that they'd like to reschedule)
+ */
+void cpu_idle(void)
+{
+ /* endless idle loop with no priority at all */
+ while (1) {
+ idle();
+ preempt_enable_no_resched();
+ schedule();
+ preempt_disable();
+ }
+}
+
+void machine_restart(char *__unused)
+{
+#if defined(CONFIG_BLKFIN_CACHE)
+ bfin_write_IMEM_CONTROL(0x01);
+ SSYNC();
+#endif
+ bfin_reset();
+ /* Dont do anything till the reset occurs */
+ while (1) {
+ SSYNC();
+ }
+}
+
+void machine_halt(void)
+{
+ for (;;)
+ asm volatile ("idle");
+}
+
+void machine_power_off(void)
+{
+ for (;;)
+ asm volatile ("idle");
+}
+
+void show_regs(struct pt_regs *regs)
+{
+ printk(KERN_NOTICE "\n");
+ printk(KERN_NOTICE
+ "PC: %08lu Status: %04lu SysStatus: %04lu RETS: %08lu\n",
+ regs->pc, regs->astat, regs->seqstat, regs->rets);
+ printk(KERN_NOTICE
+ "A0.x: %08lx A0.w: %08lx A1.x: %08lx A1.w: %08lx\n",
+ regs->a0x, regs->a0w, regs->a1x, regs->a1w);
+ printk(KERN_NOTICE "P0: %08lx P1: %08lx P2: %08lx P3: %08lx\n",
+ regs->p0, regs->p1, regs->p2, regs->p3);
+ printk(KERN_NOTICE "P4: %08lx P5: %08lx\n", regs->p4, regs->p5);
+ printk(KERN_NOTICE "R0: %08lx R1: %08lx R2: %08lx R3: %08lx\n",
+ regs->r0, regs->r1, regs->r2, regs->r3);
+ printk(KERN_NOTICE "R4: %08lx R5: %08lx R6: %08lx R7: %08lx\n",
+ regs->r4, regs->r5, regs->r6, regs->r7);
+
+ if (!(regs->ipend))
+ printk("USP: %08lx\n", rdusp());
+}
+
+/* Fill in the fpu structure for a core dump. */
+
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t * fpregs)
+{
+ return 1;
+}
+
+/*
+ * This gets run with P1 containing the
+ * function to call, and R1 containing
+ * the "args". Note P0 is clobbered on the way here.
+ */
+void kernel_thread_helper(void);
+__asm__(".section .text\n"
+ ".align 4\n"
+ "_kernel_thread_helper:\n\t"
+ "\tsp += -12;\n\t"
+ "\tr0 = r1;\n\t" "\tcall (p1);\n\t" "\tcall _do_exit;\n" ".previous");
+
+/*
+ * Create a kernel thread.
+ */
+pid_t kernel_thread(int (*fn) (void *), void *arg, unsigned long flags)
+{
+ struct pt_regs regs;
+
+ memset(&regs, 0, sizeof(regs));
+
+ regs.r1 = (unsigned long)arg;
+ regs.p1 = (unsigned long)fn;
+ regs.pc = (unsigned long)kernel_thread_helper;
+ regs.orig_p0 = -1;
+ /* Set bit 2 to tell ret_from_fork we should be returning to kernel
+ mode. */
+ regs.ipend = 0x8002;
+ __asm__ __volatile__("%0 = syscfg;":"=da"(regs.syscfg):);
+ return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL,
+ NULL);
+}
+
+void flush_thread(void)
+{
+}
+
+asmlinkage int bfin_vfork(struct pt_regs *regs)
+{
+ return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL,
+ NULL);
+}
+
+asmlinkage int bfin_clone(struct pt_regs *regs)
+{
+ unsigned long clone_flags;
+ unsigned long newsp;
+
+ /* syscall2 puts clone_flags in r0 and usp in r1 */
+ clone_flags = regs->r0;
+ newsp = regs->r1;
+ if (!newsp)
+ newsp = rdusp();
+ else
+ newsp -= 12;
+ return do_fork(clone_flags, newsp, regs, 0, NULL, NULL);
+}
+
+int
+copy_thread(int nr, unsigned long clone_flags,
+ unsigned long usp, unsigned long topstk,
+ struct task_struct *p, struct pt_regs *regs)
+{
+ struct pt_regs *childregs;
+
+ childregs = (struct pt_regs *) (task_stack_page(p) + THREAD_SIZE) - 1;
+ *childregs = *regs;
+ childregs->r0 = 0;
+
+ p->thread.usp = usp;
+ p->thread.ksp = (unsigned long)childregs;
+ p->thread.pc = (unsigned long)ret_from_fork;
+
+ return 0;
+}
+
+/*
+ * fill in the user structure for a core dump..
+ */
+void dump_thread(struct pt_regs *regs, struct user *dump)
+{
+ dump->magic = CMAGIC;
+ dump->start_code = 0;
+ dump->start_stack = rdusp() & ~(PAGE_SIZE - 1);
+ dump->u_tsize = ((unsigned long)current->mm->end_code) >> PAGE_SHIFT;
+ dump->u_dsize = ((unsigned long)(current->mm->brk +
+ (PAGE_SIZE - 1))) >> PAGE_SHIFT;
+ dump->u_dsize -= dump->u_tsize;
+ dump->u_ssize = 0;
+
+ if (dump->start_stack < TASK_SIZE)
+ dump->u_ssize =
+ ((unsigned long)(TASK_SIZE -
+ dump->start_stack)) >> PAGE_SHIFT;
+
+ dump->u_ar0 = (struct user_regs_struct *)((int)&dump->regs - (int)dump);
+
+ dump->regs.r0 = regs->r0;
+ dump->regs.r1 = regs->r1;
+ dump->regs.r2 = regs->r2;
+ dump->regs.r3 = regs->r3;
+ dump->regs.r4 = regs->r4;
+ dump->regs.r5 = regs->r5;
+ dump->regs.r6 = regs->r6;
+ dump->regs.r7 = regs->r7;
+ dump->regs.p0 = regs->p0;
+ dump->regs.p1 = regs->p1;
+ dump->regs.p2 = regs->p2;
+ dump->regs.p3 = regs->p3;
+ dump->regs.p4 = regs->p4;
+ dump->regs.p5 = regs->p5;
+ dump->regs.orig_p0 = regs->orig_p0;
+ dump->regs.a0w = regs->a0w;
+ dump->regs.a1w = regs->a1w;
+ dump->regs.a0x = regs->a0x;
+ dump->regs.a1x = regs->a1x;
+ dump->regs.rets = regs->rets;
+ dump->regs.astat = regs->astat;
+ dump->regs.pc = regs->pc;
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+
+asmlinkage int sys_execve(char *name, char **argv, char **envp)
+{
+ int error;
+ char *filename;
+ struct pt_regs *regs = (struct pt_regs *)((&name) + 6);
+
+ lock_kernel();
+ filename = getname(name);
+ error = PTR_ERR(filename);
+ if (IS_ERR(filename))
+ goto out;
+ error = do_execve(filename, argv, envp, regs);
+ putname(filename);
+ out:
+ unlock_kernel();
+ return error;
+}
+
+unsigned long get_wchan(struct task_struct *p)
+{
+ unsigned long fp, pc;
+ unsigned long stack_page;
+ int count = 0;
+ if (!p || p == current || p->state == TASK_RUNNING)
+ return 0;
+
+ stack_page = (unsigned long)p;
+ fp = p->thread.usp;
+ do {
+ if (fp < stack_page + sizeof(struct thread_info) ||
+ fp >= 8184 + stack_page)
+ return 0;
+ pc = ((unsigned long *)fp)[1];
+ if (!in_sched_functions(pc))
+ return pc;
+ fp = *(unsigned long *)fp;
+ }
+ while (count++ < 16);
+ return 0;
+}
+
+#if defined(CONFIG_ACCESS_CHECK)
+int _access_ok(unsigned long addr, unsigned long size)
+{
+
+ if (addr > (addr + size))
+ return 0;
+ if (segment_eq(get_fs(),KERNEL_DS))
+ return 1;
+#ifdef CONFIG_MTD_UCLINUX
+ if (addr >= memory_start && (addr + size) <= memory_end)
+ return 1;
+ if (addr >= memory_mtd_end && (addr + size) <= physical_mem_end)
+ return 1;
+#else
+ if (addr >= memory_start && (addr + size) <= physical_mem_end)
+ return 1;
+#endif
+ if (addr >= (unsigned long)__init_begin &&
+ addr + size <= (unsigned long)__init_end)
+ return 1;
+ if (addr >= L1_SCRATCH_START
+ && addr + size <= L1_SCRATCH_START + L1_SCRATCH_LENGTH)
+ return 1;
+#if L1_CODE_LENGTH != 0
+ if (addr >= L1_CODE_START + (_etext_l1 - _stext_l1)
+ && addr + size <= L1_CODE_START + L1_CODE_LENGTH)
+ return 1;
+#endif
+#if L1_DATA_A_LENGTH != 0
+ if (addr >= L1_DATA_A_START + (_ebss_l1 - _sdata_l1)
+ && addr + size <= L1_DATA_A_START + L1_DATA_A_LENGTH)
+ return 1;
+#endif
+#if L1_DATA_B_LENGTH != 0
+ if (addr >= L1_DATA_B_START
+ && addr + size <= L1_DATA_B_START + L1_DATA_B_LENGTH)
+ return 1;
+#endif
+ return 0;
+}
+EXPORT_SYMBOL(_access_ok);
+#endif /* CONFIG_ACCESS_CHECK */
diff --git a/arch/blackfin/kernel/ptrace.c b/arch/blackfin/kernel/ptrace.c
new file mode 100644
index 000000000000..d7c8e514cb92
--- /dev/null
+++ b/arch/blackfin/kernel/ptrace.c
@@ -0,0 +1,430 @@
+/*
+ * File: arch/blackfin/kernel/ptrace.c
+ * Based on: Taken from linux/kernel/ptrace.c
+ * Author: linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
+ *
+ * Created: 1/23/92
+ * Description:
+ *
+ * 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/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/signal.h>
+
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/asm-offsets.h>
+#include <asm/dma.h>
+
+#define MAX_SHARED_LIBS 3
+#define TEXT_OFFSET 0
+/*
+ * does not yet catch signals sent when the child dies.
+ * in exit.c or in signal.c.
+ */
+
+/* determines which bits in the SYSCFG reg the user has access to. */
+/* 1 = access 0 = no access */
+#define SYSCFG_MASK 0x0007 /* SYSCFG reg */
+/* sets the trace bits. */
+#define TRACE_BITS 0x0001
+
+/* Find the stack offset for a register, relative to thread.esp0. */
+#define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg)
+
+/*
+ * Get the address of the live pt_regs for the specified task.
+ * These are saved onto the top kernel stack when the process
+ * is not running.
+ *
+ * Note: if a user thread is execve'd from kernel space, the
+ * kernel stack will not be empty on entry to the kernel, so
+ * ptracing these tasks will fail.
+ */
+static inline struct pt_regs *get_user_regs(struct task_struct *task)
+{
+ return (struct pt_regs *)
+ ((unsigned long)task->thread_info +
+ (THREAD_SIZE - sizeof(struct pt_regs)));
+}
+
+/*
+ * Get all user integer registers.
+ */
+static inline int ptrace_getregs(struct task_struct *tsk, void __user * uregs)
+{
+ struct pt_regs *regs = get_user_regs(tsk);
+ return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0;
+}
+
+/* Mapping from PT_xxx to the stack offset at which the register is
+ * saved. Notice that usp has no stack-slot and needs to be treated
+ * specially (see get_reg/put_reg below).
+ */
+
+/*
+ * Get contents of register REGNO in task TASK.
+ */
+static inline long get_reg(struct task_struct *task, int regno)
+{
+ unsigned char *reg_ptr;
+
+ struct pt_regs *regs =
+ (struct pt_regs *)((unsigned long)task->thread_info +
+ (THREAD_SIZE - sizeof(struct pt_regs)));
+ reg_ptr = (char *)regs;
+
+ switch (regno) {
+ case PT_USP:
+ return task->thread.usp;
+ default:
+ if (regno <= 216)
+ return *(long *)(reg_ptr + regno);
+ }
+ /* slight mystery ... never seems to come here but kernel misbehaves without this code! */
+
+ printk(KERN_WARNING "Request to get for unknown register %d\n", regno);
+ return 0;
+}
+
+/*
+ * Write contents of register REGNO in task TASK.
+ */
+static inline int
+put_reg(struct task_struct *task, int regno, unsigned long data)
+{
+ char * reg_ptr;
+
+ struct pt_regs *regs =
+ (struct pt_regs *)((unsigned long)task->thread_info +
+ (THREAD_SIZE - sizeof(struct pt_regs)));
+ reg_ptr = (char *)regs;
+
+ switch (regno) {
+ case PT_PC:
+ /*********************************************************************/
+ /* At this point the kernel is most likely in exception. */
+ /* The RETX register will be used to populate the pc of the process. */
+ /*********************************************************************/
+ regs->retx = data;
+ regs->pc = data;
+ break;
+ case PT_RETX:
+ break; /* regs->retx = data; break; */
+ case PT_USP:
+ regs->usp = data;
+ task->thread.usp = data;
+ break;
+ default:
+ if (regno <= 216)
+ *(long *)(reg_ptr + regno) = data;
+ }
+ return 0;
+}
+
+/*
+ * check that an address falls within the bounds of the target process's memory mappings
+ */
+static inline int is_user_addr_valid(struct task_struct *child,
+ unsigned long start, unsigned long len)
+{
+ struct vm_list_struct *vml;
+ struct sram_list_struct *sraml;
+
+ for (vml = child->mm->context.vmlist; vml; vml = vml->next)
+ if (start >= vml->vma->vm_start && start + len <= vml->vma->vm_end)
+ return 0;
+
+ for (sraml = child->mm->context.sram_list; sraml; sraml = sraml->next)
+ if (start >= (unsigned long)sraml->addr
+ && start + len <= (unsigned long)sraml->addr + sraml->length)
+ return 0;
+
+ return -EIO;
+}
+
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Make sure the single step bit is not set.
+ */
+void ptrace_disable(struct task_struct *child)
+{
+ unsigned long tmp;
+ /* make sure the single step bit is not set. */
+ tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16);
+ put_reg(child, PT_SR, tmp);
+}
+
+long arch_ptrace(struct task_struct *child, long request, long addr, long data)
+{
+ int ret;
+ int add = 0;
+
+ switch (request) {
+ /* when I and D space are separate, these will need to be fixed. */
+ case PTRACE_PEEKDATA:
+ pr_debug("ptrace: PEEKDATA\n");
+ add = MAX_SHARED_LIBS * 4; /* space between text and data */
+ /* fall through */
+ case PTRACE_PEEKTEXT: /* read word at location addr. */
+ {
+ unsigned long tmp = 0;
+ int copied;
+
+ ret = -EIO;
+ pr_debug("ptrace: PEEKTEXT at addr 0x%08lx + add %d %ld\n", addr, add,
+ sizeof(data));
+ if (is_user_addr_valid(child, addr + add, sizeof(tmp)) < 0)
+ break;
+ pr_debug("ptrace: user address is valid\n");
+
+#if L1_CODE_LENGTH != 0
+ if (addr + add >= L1_CODE_START
+ && addr + add + sizeof(tmp) <= L1_CODE_START + L1_CODE_LENGTH) {
+ safe_dma_memcpy (&tmp, (const void *)(addr + add), sizeof(tmp));
+ copied = sizeof(tmp);
+ } else
+#endif
+ copied =
+ access_process_vm(child, addr + add, &tmp,
+ sizeof(tmp), 0);
+ pr_debug("ptrace: copied size %d [0x%08lx]\n", copied, tmp);
+ if (copied != sizeof(tmp))
+ break;
+ ret = put_user(tmp, (unsigned long *)data);
+ break;
+ }
+
+ /* read the word at location addr in the USER area. */
+ case PTRACE_PEEKUSR:
+ {
+ unsigned long tmp;
+ ret = -EIO;
+ tmp = 0;
+ if ((addr & 3) || (addr > (sizeof(struct pt_regs) + 16))) {
+ printk(KERN_WARNING "ptrace error : PEEKUSR : temporarily returning "
+ "0 - %x sizeof(pt_regs) is %lx\n",
+ (int)addr, sizeof(struct pt_regs));
+ break;
+ }
+ if (addr == sizeof(struct pt_regs)) {
+ /* PT_TEXT_ADDR */
+ tmp = child->mm->start_code + TEXT_OFFSET;
+ } else if (addr == (sizeof(struct pt_regs) + 4)) {
+ /* PT_TEXT_END_ADDR */
+ tmp = child->mm->end_code;
+ } else if (addr == (sizeof(struct pt_regs) + 8)) {
+ /* PT_DATA_ADDR */
+ tmp = child->mm->start_data;
+#ifdef CONFIG_BINFMT_ELF_FDPIC
+ } else if (addr == (sizeof(struct pt_regs) + 12)) {
+ tmp = child->mm->context.exec_fdpic_loadmap;
+ } else if (addr == (sizeof(struct pt_regs) + 16)) {
+ tmp = child->mm->context.interp_fdpic_loadmap;
+#endif
+ } else {
+ tmp = get_reg(child, addr);
+ }
+ ret = put_user(tmp, (unsigned long *)data);
+ break;
+ }
+
+ /* when I and D space are separate, this will have to be fixed. */
+ case PTRACE_POKEDATA:
+ printk(KERN_NOTICE "ptrace: PTRACE_PEEKDATA\n");
+ /* fall through */
+ case PTRACE_POKETEXT: /* write the word at location addr. */
+ {
+ int copied;
+
+ ret = -EIO;
+ pr_debug("ptrace: POKETEXT at addr 0x%08lx + add %d %ld bytes %lx\n",
+ addr, add, sizeof(data), data);
+ if (is_user_addr_valid(child, addr + add, sizeof(data)) < 0)
+ break;
+ pr_debug("ptrace: user address is valid\n");
+
+#if L1_CODE_LENGTH != 0
+ if (addr + add >= L1_CODE_START
+ && addr + add + sizeof(data) <= L1_CODE_START + L1_CODE_LENGTH) {
+ safe_dma_memcpy ((void *)(addr + add), &data, sizeof(data));
+ copied = sizeof(data);
+ } else
+#endif
+ copied =
+ access_process_vm(child, addr + add, &data,
+ sizeof(data), 1);
+ pr_debug("ptrace: copied size %d\n", copied);
+ if (copied != sizeof(data))
+ break;
+ ret = 0;
+ break;
+ }
+
+ case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
+ ret = -EIO;
+ if ((addr & 3) || (addr > (sizeof(struct pt_regs) + 16))) {
+ printk(KERN_WARNING "ptrace error : POKEUSR: temporarily returning 0\n");
+ break;
+ }
+
+ if (addr >= (sizeof(struct pt_regs))) {
+ ret = 0;
+ break;
+ }
+ if (addr == PT_SYSCFG) {
+ data &= SYSCFG_MASK;
+ data |= get_reg(child, PT_SYSCFG);
+ }
+ ret = put_reg(child, addr, data);
+ break;
+
+ case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
+ case PTRACE_CONT:
+ { /* restart after signal. */
+ long tmp;
+
+ pr_debug("ptrace_cont\n");
+
+ ret = -EIO;
+ if (!valid_signal(data))
+ break;
+ if (request == PTRACE_SYSCALL)
+ set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ else
+ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+
+ child->exit_code = data;
+ /* make sure the single step bit is not set. */
+ tmp = get_reg(child, PT_SYSCFG) & ~(TRACE_BITS);
+ put_reg(child, PT_SYSCFG, tmp);
+ pr_debug("before wake_up_process\n");
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+ /*
+ * make the child exit. Best I can do is send it a sigkill.
+ * perhaps it should be put in the status that it wants to
+ * exit.
+ */
+ case PTRACE_KILL:
+ {
+ long tmp;
+ ret = 0;
+ if (child->exit_state == EXIT_ZOMBIE) /* already dead */
+ break;
+ child->exit_code = SIGKILL;
+ /* make sure the single step bit is not set. */
+ tmp = get_reg(child, PT_SYSCFG) & ~(TRACE_BITS);
+ put_reg(child, PT_SYSCFG, tmp);
+ wake_up_process(child);
+ break;
+ }
+
+ case PTRACE_SINGLESTEP:
+ { /* set the trap flag. */
+ long tmp;
+
+ pr_debug("single step\n");
+ ret = -EIO;
+ if (!valid_signal(data))
+ break;
+ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+
+ tmp = get_reg(child, PT_SYSCFG) | (TRACE_BITS);
+ put_reg(child, PT_SYSCFG, tmp);
+
+ child->exit_code = data;
+ /* give it a chance to run. */
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+ case PTRACE_DETACH:
+ { /* detach a process that was attached. */
+ ret = ptrace_detach(child, data);
+ break;
+ }
+
+ case PTRACE_GETREGS:
+ {
+
+ /* Get all gp regs from the child. */
+ ret = ptrace_getregs(child, (void __user *)data);
+ break;
+ }
+
+ case PTRACE_SETREGS:
+ {
+ printk(KERN_NOTICE
+ "ptrace: SETREGS: **** NOT IMPLEMENTED ***\n");
+ /* Set all gp regs in the child. */
+ ret = 0;
+ break;
+ }
+ default:
+ ret = ptrace_request(child, request, addr, data);
+ break;
+ }
+
+ return ret;
+}
+
+asmlinkage void syscall_trace(void)
+{
+
+ if (!test_thread_flag(TIF_SYSCALL_TRACE))
+ return;
+
+ if (!(current->ptrace & PT_PTRACED))
+ return;
+
+ /* the 0x80 provides a way for the tracing parent to distinguish
+ * between a syscall stop and SIGTRAP delivery
+ */
+ ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
+ ? 0x80 : 0));
+
+ /*
+ * this isn't the same as continuing with a signal, but it will do
+ * for normal use. strace only continues with a signal if the
+ * stopping signal is not SIGTRAP. -brl
+ */
+ if (current->exit_code) {
+ send_sig(current->exit_code, current, 1);
+ current->exit_code = 0;
+ }
+}
diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c
new file mode 100644
index 000000000000..342bb8dd56ac
--- /dev/null
+++ b/arch/blackfin/kernel/setup.c
@@ -0,0 +1,902 @@
+/*
+ * File: arch/blackfin/kernel/setup.c
+ * Based on:
+ * Author:
+ *
+ * Created:
+ * Description:
+ *
+ * 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/delay.h>
+#include <linux/console.h>
+#include <linux/bootmem.h>
+#include <linux/seq_file.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+
+#include <linux/ext2_fs.h>
+#include <linux/cramfs_fs.h>
+#include <linux/romfs_fs.h>
+
+#include <asm/cacheflush.h>
+#include <asm/blackfin.h>
+#include <asm/cplbinit.h>
+
+unsigned long memory_start, memory_end, physical_mem_end;
+unsigned long reserved_mem_dcache_on;
+unsigned long reserved_mem_icache_on;
+EXPORT_SYMBOL(memory_start);
+EXPORT_SYMBOL(memory_end);
+EXPORT_SYMBOL(physical_mem_end);
+EXPORT_SYMBOL(_ramend);
+
+#ifdef CONFIG_MTD_UCLINUX
+unsigned long memory_mtd_end, memory_mtd_start, mtd_size;
+unsigned long _ebss;
+EXPORT_SYMBOL(memory_mtd_end);
+EXPORT_SYMBOL(memory_mtd_start);
+EXPORT_SYMBOL(mtd_size);
+#endif
+
+char command_line[COMMAND_LINE_SIZE];
+
+#if defined(CONFIG_BLKFIN_DCACHE) || defined(CONFIG_BLKFIN_CACHE)
+static void generate_cpl_tables(void);
+#endif
+
+void __init bf53x_cache_init(void)
+{
+#if defined(CONFIG_BLKFIN_DCACHE) || defined(CONFIG_BLKFIN_CACHE)
+ generate_cpl_tables();
+#endif
+
+#ifdef CONFIG_BLKFIN_CACHE
+ bfin_icache_init();
+ printk(KERN_INFO "Instruction Cache Enabled\n");
+#endif
+
+#ifdef CONFIG_BLKFIN_DCACHE
+ bfin_dcache_init();
+ printk(KERN_INFO "Data Cache Enabled"
+# if defined CONFIG_BLKFIN_WB
+ " (write-back)"
+# elif defined CONFIG_BLKFIN_WT
+ " (write-through)"
+# endif
+ "\n");
+#endif
+}
+
+void bf53x_relocate_l1_mem(void)
+{
+ unsigned long l1_code_length;
+ unsigned long l1_data_a_length;
+ unsigned long l1_data_b_length;
+
+ l1_code_length = _etext_l1 - _stext_l1;
+ if (l1_code_length > L1_CODE_LENGTH)
+ l1_code_length = L1_CODE_LENGTH;
+ /* cannot complain as printk is not available as yet.
+ * But we can continue booting and complain later!
+ */
+
+ /* Copy _stext_l1 to _etext_l1 to L1 instruction SRAM */
+ dma_memcpy(_stext_l1, _l1_lma_start, l1_code_length);
+
+ l1_data_a_length = _ebss_l1 - _sdata_l1;
+ if (l1_data_a_length > L1_DATA_A_LENGTH)
+ l1_data_a_length = L1_DATA_A_LENGTH;
+
+ /* Copy _sdata_l1 to _ebss_l1 to L1 data bank A SRAM */
+ dma_memcpy(_sdata_l1, _l1_lma_start + l1_code_length, l1_data_a_length);
+
+ l1_data_b_length = _ebss_b_l1 - _sdata_b_l1;
+ if (l1_data_b_length > L1_DATA_B_LENGTH)
+ l1_data_b_length = L1_DATA_B_LENGTH;
+
+ /* Copy _sdata_b_l1 to _ebss_b_l1 to L1 data bank B SRAM */
+ dma_memcpy(_sdata_b_l1, _l1_lma_start + l1_code_length +
+ l1_data_a_length, l1_data_b_length);
+
+}
+
+/*
+ * Initial parsing of the command line. Currently, we support:
+ * - Controlling the linux memory size: mem=xxx[KMG]
+ * - Controlling the physical memory size: max_mem=xxx[KMG][$][#]
+ * $ -> reserved memory is dcacheable
+ * # -> reserved memory is icacheable
+ */
+static __init void parse_cmdline_early(char *cmdline_p)
+{
+ char c = ' ', *to = cmdline_p;
+ unsigned int memsize;
+ for (;;) {
+ if (c == ' ') {
+
+ if (!memcmp(to, "mem=", 4)) {
+ to += 4;
+ memsize = memparse(to, &to);
+ if (memsize)
+ _ramend = memsize;
+
+ } else if (!memcmp(to, "max_mem=", 8)) {
+ to += 8;
+ memsize = memparse(to, &to);
+ if (memsize) {
+ physical_mem_end = memsize;
+ if (*to != ' ') {
+ if (*to == '$'
+ || *(to + 1) == '$')
+ reserved_mem_dcache_on =
+ 1;
+ if (*to == '#'
+ || *(to + 1) == '#')
+ reserved_mem_icache_on =
+ 1;
+ }
+ }
+ }
+
+ }
+ c = *(to++);
+ if (!c)
+ break;
+ }
+}
+
+void __init setup_arch(char **cmdline_p)
+{
+ int bootmap_size;
+ unsigned long l1_length, sclk, cclk;
+#ifdef CONFIG_MTD_UCLINUX
+ unsigned long mtd_phys = 0;
+#endif
+
+ cclk = get_cclk();
+ sclk = get_sclk();
+
+#if !defined(CONFIG_BFIN_KERNEL_CLOCK) && defined(ANOMALY_05000273)
+ if (cclk == sclk)
+ panic("ANOMALY 05000273, SCLK can not be same as CCLK");
+#endif
+
+#if defined(ANOMALY_05000266)
+ bfin_read_IMDMA_D0_IRQ_STATUS();
+ bfin_read_IMDMA_D1_IRQ_STATUS();
+#endif
+
+#ifdef DEBUG_SERIAL_EARLY_INIT
+ bfin_console_init(); /* early console registration */
+ /* this give a chance to get printk() working before crash. */
+#endif
+
+#if defined(CONFIG_CHR_DEV_FLASH) || defined(CONFIG_BLK_DEV_FLASH)
+ /* we need to initialize the Flashrom device here since we might
+ * do things with flash early on in the boot
+ */
+ flash_probe();
+#endif
+
+#if defined(CONFIG_CMDLINE_BOOL)
+ memset(command_line, 0, sizeof(command_line));
+ strncpy(&command_line[0], CONFIG_CMDLINE, sizeof(command_line));
+ command_line[sizeof(command_line) - 1] = 0;
+#endif
+
+ /* Keep a copy of command line */
+ *cmdline_p = &command_line[0];
+ memcpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
+ boot_command_line[COMMAND_LINE_SIZE - 1] = 0;
+
+ /* setup memory defaults from the user config */
+ physical_mem_end = 0;
+ _ramend = CONFIG_MEM_SIZE * 1024 * 1024;
+
+ parse_cmdline_early(&command_line[0]);
+
+ if (physical_mem_end == 0)
+ physical_mem_end = _ramend;
+
+ /* by now the stack is part of the init task */
+ memory_end = _ramend - DMA_UNCACHED_REGION;
+
+ _ramstart = (unsigned long)__bss_stop;
+ memory_start = PAGE_ALIGN(_ramstart);
+
+#if defined(CONFIG_MTD_UCLINUX)
+ /* generic memory mapped MTD driver */
+ memory_mtd_end = memory_end;
+
+ mtd_phys = _ramstart;
+ mtd_size = PAGE_ALIGN(*((unsigned long *)(mtd_phys + 8)));
+
+# if defined(CONFIG_EXT2_FS) || defined(CONFIG_EXT3_FS)
+ if (*((unsigned short *)(mtd_phys + 0x438)) == EXT2_SUPER_MAGIC)
+ mtd_size =
+ PAGE_ALIGN(*((unsigned long *)(mtd_phys + 0x404)) << 10);
+# endif
+
+# if defined(CONFIG_CRAMFS)
+ if (*((unsigned long *)(mtd_phys)) == CRAMFS_MAGIC)
+ mtd_size = PAGE_ALIGN(*((unsigned long *)(mtd_phys + 0x4)));
+# endif
+
+# if defined(CONFIG_ROMFS_FS)
+ if (((unsigned long *)mtd_phys)[0] == ROMSB_WORD0
+ && ((unsigned long *)mtd_phys)[1] == ROMSB_WORD1)
+ mtd_size =
+ PAGE_ALIGN(be32_to_cpu(((unsigned long *)mtd_phys)[2]));
+# if (defined(CONFIG_BLKFIN_CACHE) && defined(ANOMALY_05000263))
+ /* Due to a Hardware Anomaly we need to limit the size of usable
+ * instruction memory to max 60MB, 56 if HUNT_FOR_ZERO is on
+ * 05000263 - Hardware loop corrupted when taking an ICPLB exception
+ */
+# if (defined(CONFIG_DEBUG_HUNT_FOR_ZERO))
+ if (memory_end >= 56 * 1024 * 1024)
+ memory_end = 56 * 1024 * 1024;
+# else
+ if (memory_end >= 60 * 1024 * 1024)
+ memory_end = 60 * 1024 * 1024;
+# endif /* CONFIG_DEBUG_HUNT_FOR_ZERO */
+# endif /* ANOMALY_05000263 */
+# endif /* CONFIG_ROMFS_FS */
+
+ memory_end -= mtd_size;
+
+ if (mtd_size == 0) {
+ console_init();
+ panic("Don't boot kernel without rootfs attached.\n");
+ }
+
+ /* Relocate MTD image to the top of memory after the uncached memory area */
+ dma_memcpy((char *)memory_end, __bss_stop, mtd_size);
+
+ memory_mtd_start = memory_end;
+ _ebss = memory_mtd_start; /* define _ebss for compatible */
+#endif /* CONFIG_MTD_UCLINUX */
+
+#if (defined(CONFIG_BLKFIN_CACHE) && defined(ANOMALY_05000263))
+ /* Due to a Hardware Anomaly we need to limit the size of usable
+ * instruction memory to max 60MB, 56 if HUNT_FOR_ZERO is on
+ * 05000263 - Hardware loop corrupted when taking an ICPLB exception
+ */
+#if (defined(CONFIG_DEBUG_HUNT_FOR_ZERO))
+ if (memory_end >= 56 * 1024 * 1024)
+ memory_end = 56 * 1024 * 1024;
+#else
+ if (memory_end >= 60 * 1024 * 1024)
+ memory_end = 60 * 1024 * 1024;
+#endif /* CONFIG_DEBUG_HUNT_FOR_ZERO */
+ printk(KERN_NOTICE "Warning: limiting memory to %liMB due to hardware anomaly 05000263\n", memory_end >> 20);
+#endif /* ANOMALY_05000263 */
+
+#if !defined(CONFIG_MTD_UCLINUX)
+ memory_end -= SIZE_4K; /*In case there is no valid CPLB behind memory_end make sure we don't get to close*/
+#endif
+ init_mm.start_code = (unsigned long)_stext;
+ init_mm.end_code = (unsigned long)_etext;
+ init_mm.end_data = (unsigned long)_edata;
+ init_mm.brk = (unsigned long)0;
+
+ init_leds();
+
+ printk(KERN_INFO "Blackfin support (C) 2004-2007 Analog Devices, Inc.\n");
+ printk(KERN_INFO "Compiled for ADSP-%s Rev 0.%d\n", CPU, bfin_compiled_revid());
+ if (bfin_revid() != bfin_compiled_revid())
+ printk(KERN_ERR "Warning: Compiled for Rev %d, but running on Rev %d\n",
+ bfin_compiled_revid(), bfin_revid());
+ if (bfin_revid() < SUPPORTED_REVID)
+ printk(KERN_ERR "Warning: Unsupported Chip Revision ADSP-%s Rev 0.%d detected\n",
+ CPU, bfin_revid());
+ printk(KERN_INFO "Blackfin Linux support by http://blackfin.uclinux.org/\n");
+
+ printk(KERN_INFO "Processor Speed: %lu MHz core clock and %lu Mhz System Clock\n",
+ cclk / 1000000, sclk / 1000000);
+
+#if defined(ANOMALY_05000273)
+ if ((cclk >> 1) <= sclk)
+ printk("\n\n\nANOMALY_05000273: CCLK must be >= 2*SCLK !!!\n\n\n");
+#endif
+
+ printk(KERN_INFO "Board Memory: %ldMB\n", physical_mem_end >> 20);
+ printk(KERN_INFO "Kernel Managed Memory: %ldMB\n", _ramend >> 20);
+
+ printk(KERN_INFO "Memory map:\n"
+ KERN_INFO " text = 0x%p-0x%p\n"
+ KERN_INFO " init = 0x%p-0x%p\n"
+ KERN_INFO " data = 0x%p-0x%p\n"
+ KERN_INFO " stack = 0x%p-0x%p\n"
+ KERN_INFO " bss = 0x%p-0x%p\n"
+ KERN_INFO " available = 0x%p-0x%p\n"
+#ifdef CONFIG_MTD_UCLINUX
+ KERN_INFO " rootfs = 0x%p-0x%p\n"
+#endif
+#if DMA_UNCACHED_REGION > 0
+ KERN_INFO " DMA Zone = 0x%p-0x%p\n"
+#endif
+ , _stext, _etext,
+ __init_begin, __init_end,
+ _sdata, _edata,
+ (void*)&init_thread_union, (void*)((int)(&init_thread_union) + 0x2000),
+ __bss_start, __bss_stop,
+ (void*)_ramstart, (void*)memory_end
+#ifdef CONFIG_MTD_UCLINUX
+ , (void*)memory_mtd_start, (void*)(memory_mtd_start + mtd_size)
+#endif
+#if DMA_UNCACHED_REGION > 0
+ , (void*)(_ramend - DMA_UNCACHED_REGION), (void*)(_ramend)
+#endif
+ );
+
+ /*
+ * give all the memory to the bootmap allocator, tell it to put the
+ * boot mem_map at the start of memory
+ */
+ bootmap_size = init_bootmem_node(NODE_DATA(0), memory_start >> PAGE_SHIFT, /* map goes here */
+ PAGE_OFFSET >> PAGE_SHIFT,
+ memory_end >> PAGE_SHIFT);
+ /*
+ * free the usable memory, we have to make sure we do not free
+ * the bootmem bitmap so we then reserve it after freeing it :-)
+ */
+ free_bootmem(memory_start, memory_end - memory_start);
+
+ reserve_bootmem(memory_start, bootmap_size);
+ /*
+ * get kmalloc into gear
+ */
+ paging_init();
+
+ /* check the size of the l1 area */
+ l1_length = _etext_l1 - _stext_l1;
+ if (l1_length > L1_CODE_LENGTH)
+ panic("L1 memory overflow\n");
+
+ l1_length = _ebss_l1 - _sdata_l1;
+ if (l1_length > L1_DATA_A_LENGTH)
+ panic("L1 memory overflow\n");
+
+ bf53x_cache_init();
+
+#if defined(CONFIG_SMC91X) || defined(CONFIG_SMC91X_MODULE)
+# if defined(CONFIG_BFIN_SHARED_FLASH_ENET) && defined(CONFIG_BFIN533_STAMP)
+ /* setup BF533_STAMP CPLD to route AMS3 to Ethernet MAC */
+ bfin_write_FIO_DIR(bfin_read_FIO_DIR() | (1 << CONFIG_ENET_FLASH_PIN));
+ bfin_write_FIO_FLAG_S(1 << CONFIG_ENET_FLASH_PIN);
+ SSYNC();
+# endif
+# if defined (CONFIG_BFIN561_EZKIT)
+ bfin_write_FIO0_DIR(bfin_read_FIO0_DIR() | (1 << 12));
+ SSYNC();
+# endif /* defined (CONFIG_BFIN561_EZKIT) */
+#endif
+
+ printk(KERN_INFO "Hardware Trace Enabled\n");
+ bfin_write_TBUFCTL(0x03);
+}
+
+#if defined(CONFIG_BF561)
+static struct cpu cpu[2];
+#else
+static struct cpu cpu[1];
+#endif
+static int __init topology_init(void)
+{
+#if defined (CONFIG_BF561)
+ register_cpu(&cpu[0], 0);
+ register_cpu(&cpu[1], 1);
+ return 0;
+#else
+ return register_cpu(cpu, 0);
+#endif
+}
+
+subsys_initcall(topology_init);
+
+#if defined(CONFIG_BLKFIN_DCACHE) || defined(CONFIG_BLKFIN_CACHE)
+u16 lock_kernel_check(u32 start, u32 end)
+{
+ if ((start <= (u32) _stext && end >= (u32) _end)
+ || (start >= (u32) _stext && end <= (u32) _end))
+ return IN_KERNEL;
+ return 0;
+}
+
+static unsigned short __init
+fill_cplbtab(struct cplb_tab *table,
+ unsigned long start, unsigned long end,
+ unsigned long block_size, unsigned long cplb_data)
+{
+ int i;
+
+ switch (block_size) {
+ case SIZE_4M:
+ i = 3;
+ break;
+ case SIZE_1M:
+ i = 2;
+ break;
+ case SIZE_4K:
+ i = 1;
+ break;
+ case SIZE_1K:
+ default:
+ i = 0;
+ break;
+ }
+
+ cplb_data = (cplb_data & ~(3 << 16)) | (i << 16);
+
+ while ((start < end) && (table->pos < table->size)) {
+
+ table->tab[table->pos++] = start;
+
+ if (lock_kernel_check(start, start + block_size) == IN_KERNEL)
+ table->tab[table->pos++] =
+ cplb_data | CPLB_LOCK | CPLB_DIRTY;
+ else
+ table->tab[table->pos++] = cplb_data;
+
+ start += block_size;
+ }
+ return 0;
+}
+
+static unsigned short __init
+close_cplbtab(struct cplb_tab *table)
+{
+
+ while (table->pos < table->size) {
+
+ table->tab[table->pos++] = 0;
+ table->tab[table->pos++] = 0; /* !CPLB_VALID */
+ }
+ return 0;
+}
+
+static void __init generate_cpl_tables(void)
+{
+
+ u16 i, j, process;
+ u32 a_start, a_end, as, ae, as_1m;
+
+ struct cplb_tab *t_i = NULL;
+ struct cplb_tab *t_d = NULL;
+ struct s_cplb cplb;
+
+ cplb.init_i.size = MAX_CPLBS;
+ cplb.init_d.size = MAX_CPLBS;
+ cplb.switch_i.size = MAX_SWITCH_I_CPLBS;
+ cplb.switch_d.size = MAX_SWITCH_D_CPLBS;
+
+ cplb.init_i.pos = 0;
+ cplb.init_d.pos = 0;
+ cplb.switch_i.pos = 0;
+ cplb.switch_d.pos = 0;
+
+ cplb.init_i.tab = icplb_table;
+ cplb.init_d.tab = dcplb_table;
+ cplb.switch_i.tab = ipdt_table;
+ cplb.switch_d.tab = dpdt_table;
+
+ cplb_data[SDRAM_KERN].end = memory_end;
+
+#ifdef CONFIG_MTD_UCLINUX
+ cplb_data[SDRAM_RAM_MTD].start = memory_mtd_start;
+ cplb_data[SDRAM_RAM_MTD].end = memory_mtd_start + mtd_size;
+ cplb_data[SDRAM_RAM_MTD].valid = mtd_size > 0;
+# if defined(CONFIG_ROMFS_FS)
+ cplb_data[SDRAM_RAM_MTD].attr |= I_CPLB;
+
+ /*
+ * The ROMFS_FS size is often not multiple of 1MB.
+ * This can cause multiple CPLB sets covering the same memory area.
+ * This will then cause multiple CPLB hit exceptions.
+ * Workaround: We ensure a contiguous memory area by extending the kernel
+ * memory section over the mtd section.
+ * For ROMFS_FS memory must be covered with ICPLBs anyways.
+ * So there is no difference between kernel and mtd memory setup.
+ */
+
+ cplb_data[SDRAM_KERN].end = memory_mtd_start + mtd_size;;
+ cplb_data[SDRAM_RAM_MTD].valid = 0;
+
+# endif
+#else
+ cplb_data[SDRAM_RAM_MTD].valid = 0;
+#endif
+
+ cplb_data[SDRAM_DMAZ].start = _ramend - DMA_UNCACHED_REGION;
+ cplb_data[SDRAM_DMAZ].end = _ramend;
+
+ cplb_data[RES_MEM].start = _ramend;
+ cplb_data[RES_MEM].end = physical_mem_end;
+
+ if (reserved_mem_dcache_on)
+ cplb_data[RES_MEM].d_conf = SDRAM_DGENERIC;
+ else
+ cplb_data[RES_MEM].d_conf = SDRAM_DNON_CHBL;
+
+ if (reserved_mem_icache_on)
+ cplb_data[RES_MEM].i_conf = SDRAM_IGENERIC;
+ else
+ cplb_data[RES_MEM].i_conf = SDRAM_INON_CHBL;
+
+ for (i = ZERO_P; i <= L2_MEM; i++) {
+
+ if (cplb_data[i].valid) {
+
+ as_1m = cplb_data[i].start % SIZE_1M;
+
+ /* We need to make sure all sections are properly 1M aligned
+ * However between Kernel Memory and the Kernel mtd section, depending on the
+ * rootfs size, there can be overlapping memory areas.
+ */
+
+ if (as_1m && i!=L1I_MEM && i!=L1D_MEM) {
+#ifdef CONFIG_MTD_UCLINUX
+ if (i == SDRAM_RAM_MTD) {
+ if ((cplb_data[SDRAM_KERN].end + 1) > cplb_data[SDRAM_RAM_MTD].start)
+ cplb_data[SDRAM_RAM_MTD].start = (cplb_data[i].start & (-2*SIZE_1M)) + SIZE_1M;
+ else
+ cplb_data[SDRAM_RAM_MTD].start = (cplb_data[i].start & (-2*SIZE_1M));
+ } else
+#endif
+ printk(KERN_WARNING "Unaligned Start of %s at 0x%X\n",
+ cplb_data[i].name, cplb_data[i].start);
+ }
+
+ as = cplb_data[i].start % SIZE_4M;
+ ae = cplb_data[i].end % SIZE_4M;
+
+ if (as)
+ a_start = cplb_data[i].start + (SIZE_4M - (as));
+ else
+ a_start = cplb_data[i].start;
+
+ a_end = cplb_data[i].end - ae;
+
+ for (j = INITIAL_T; j <= SWITCH_T; j++) {
+
+ switch (j) {
+ case INITIAL_T:
+ if (cplb_data[i].attr & INITIAL_T) {
+ t_i = &cplb.init_i;
+ t_d = &cplb.init_d;
+ process = 1;
+ } else
+ process = 0;
+ break;
+ case SWITCH_T:
+ if (cplb_data[i].attr & SWITCH_T) {
+ t_i = &cplb.switch_i;
+ t_d = &cplb.switch_d;
+ process = 1;
+ } else
+ process = 0;
+ break;
+ default:
+ process = 0;
+ break;
+ }
+
+ if (process) {
+ if (cplb_data[i].attr & I_CPLB) {
+
+ if (cplb_data[i].psize) {
+ fill_cplbtab(t_i,
+ cplb_data[i].start,
+ cplb_data[i].end,
+ cplb_data[i].psize,
+ cplb_data[i].i_conf);
+ } else {
+ /*icplb_table */
+#if (defined(CONFIG_BLKFIN_CACHE) && defined(ANOMALY_05000263))
+ if (i == SDRAM_KERN) {
+ fill_cplbtab(t_i,
+ cplb_data[i].start,
+ cplb_data[i].end,
+ SIZE_4M,
+ cplb_data[i].i_conf);
+ } else
+#endif
+ {
+ fill_cplbtab(t_i,
+ cplb_data[i].start,
+ a_start,
+ SIZE_1M,
+ cplb_data[i].i_conf);
+ fill_cplbtab(t_i,
+ a_start,
+ a_end,
+ SIZE_4M,
+ cplb_data[i].i_conf);
+ fill_cplbtab(t_i, a_end,
+ cplb_data[i].end,
+ SIZE_1M,
+ cplb_data[i].i_conf);
+ }
+ }
+
+ }
+ if (cplb_data[i].attr & D_CPLB) {
+
+ if (cplb_data[i].psize) {
+ fill_cplbtab(t_d,
+ cplb_data[i].start,
+ cplb_data[i].end,
+ cplb_data[i].psize,
+ cplb_data[i].d_conf);
+ } else {
+/*dcplb_table*/
+ fill_cplbtab(t_d,
+ cplb_data[i].start,
+ a_start, SIZE_1M,
+ cplb_data[i].d_conf);
+ fill_cplbtab(t_d, a_start,
+ a_end, SIZE_4M,
+ cplb_data[i].d_conf);
+ fill_cplbtab(t_d, a_end,
+ cplb_data[i].end,
+ SIZE_1M,
+ cplb_data[i].d_conf);
+
+ }
+
+ }
+ }
+ }
+
+ }
+ }
+
+/* close tables */
+
+ close_cplbtab(&cplb.init_i);
+ close_cplbtab(&cplb.init_d);
+
+ cplb.init_i.tab[cplb.init_i.pos] = -1;
+ cplb.init_d.tab[cplb.init_d.pos] = -1;
+ cplb.switch_i.tab[cplb.switch_i.pos] = -1;
+ cplb.switch_d.tab[cplb.switch_d.pos] = -1;
+
+}
+
+#endif
+
+static inline u_long get_vco(void)
+{
+ u_long msel;
+ u_long vco;
+
+ msel = (bfin_read_PLL_CTL() >> 9) & 0x3F;
+ if (0 == msel)
+ msel = 64;
+
+ vco = CONFIG_CLKIN_HZ;
+ vco >>= (1 & bfin_read_PLL_CTL()); /* DF bit */
+ vco = msel * vco;
+ return vco;
+}
+
+/*Get the Core clock*/
+u_long get_cclk(void)
+{
+ u_long csel, ssel;
+ if (bfin_read_PLL_STAT() & 0x1)
+ return CONFIG_CLKIN_HZ;
+
+ ssel = bfin_read_PLL_DIV();
+ csel = ((ssel >> 4) & 0x03);
+ ssel &= 0xf;
+ if (ssel && ssel < (1 << csel)) /* SCLK > CCLK */
+ return get_vco() / ssel;
+ return get_vco() >> csel;
+}
+
+EXPORT_SYMBOL(get_cclk);
+
+/* Get the System clock */
+u_long get_sclk(void)
+{
+ u_long ssel;
+
+ if (bfin_read_PLL_STAT() & 0x1)
+ return CONFIG_CLKIN_HZ;
+
+ ssel = (bfin_read_PLL_DIV() & 0xf);
+ if (0 == ssel) {
+ printk(KERN_WARNING "Invalid System Clock\n");
+ ssel = 1;
+ }
+
+ return get_vco() / ssel;
+}
+
+EXPORT_SYMBOL(get_sclk);
+
+/*
+ * Get CPU information for use by the procfs.
+ */
+static int show_cpuinfo(struct seq_file *m, void *v)
+{
+ char *cpu, *mmu, *fpu, *name;
+ uint32_t revid;
+
+ u_long cclk = 0, sclk = 0;
+ u_int dcache_size = 0, dsup_banks = 0;
+
+ cpu = CPU;
+ mmu = "none";
+ fpu = "none";
+ revid = bfin_revid();
+ name = bfin_board_name;
+
+ cclk = get_cclk();
+ sclk = get_sclk();
+
+ seq_printf(m, "CPU:\t\tADSP-%s Rev. 0.%d\n"
+ "MMU:\t\t%s\n"
+ "FPU:\t\t%s\n"
+ "Core Clock:\t%9lu Hz\n"
+ "System Clock:\t%9lu Hz\n"
+ "BogoMips:\t%lu.%02lu\n"
+ "Calibration:\t%lu loops\n",
+ cpu, revid, mmu, fpu,
+ cclk,
+ sclk,
+ (loops_per_jiffy * HZ) / 500000,
+ ((loops_per_jiffy * HZ) / 5000) % 100,
+ (loops_per_jiffy * HZ));
+ seq_printf(m, "Board Name:\t%s\n", name);
+ seq_printf(m, "Board Memory:\t%ld MB\n", physical_mem_end >> 20);
+ seq_printf(m, "Kernel Memory:\t%ld MB\n", (unsigned long)_ramend >> 20);
+ if (bfin_read_IMEM_CONTROL() & (ENICPLB | IMC))
+ seq_printf(m, "I-CACHE:\tON\n");
+ else
+ seq_printf(m, "I-CACHE:\tOFF\n");
+ if ((bfin_read_DMEM_CONTROL()) & (ENDCPLB | DMC_ENABLE))
+ seq_printf(m, "D-CACHE:\tON"
+#if defined CONFIG_BLKFIN_WB
+ " (write-back)"
+#elif defined CONFIG_BLKFIN_WT
+ " (write-through)"
+#endif
+ "\n");
+ else
+ seq_printf(m, "D-CACHE:\tOFF\n");
+
+
+ switch(bfin_read_DMEM_CONTROL() & (1 << DMC0_P | 1 << DMC1_P)) {
+ case ACACHE_BSRAM:
+ seq_printf(m, "DBANK-A:\tCACHE\n" "DBANK-B:\tSRAM\n");
+ dcache_size = 16;
+ dsup_banks = 1;
+ break;
+ case ACACHE_BCACHE:
+ seq_printf(m, "DBANK-A:\tCACHE\n" "DBANK-B:\tCACHE\n");
+ dcache_size = 32;
+ dsup_banks = 2;
+ break;
+ case ASRAM_BSRAM:
+ seq_printf(m, "DBANK-A:\tSRAM\n" "DBANK-B:\tSRAM\n");
+ dcache_size = 0;
+ dsup_banks = 0;
+ break;
+ default:
+ break;
+ }
+
+
+ seq_printf(m, "I-CACHE Size:\t%dKB\n", BLKFIN_ICACHESIZE / 1024);
+ seq_printf(m, "D-CACHE Size:\t%dKB\n", dcache_size);
+ seq_printf(m, "I-CACHE Setup:\t%d Sub-banks/%d Ways, %d Lines/Way\n",
+ BLKFIN_ISUBBANKS, BLKFIN_IWAYS, BLKFIN_ILINES);
+ seq_printf(m,
+ "D-CACHE Setup:\t%d Super-banks/%d Sub-banks/%d Ways, %d Lines/Way\n",
+ dsup_banks, BLKFIN_DSUBBANKS, BLKFIN_DWAYS,
+ BLKFIN_DLINES);
+#ifdef CONFIG_BLKFIN_CACHE_LOCK
+ switch (read_iloc()) {
+ case WAY0_L:
+ seq_printf(m, "Way0 Locked-Down\n");
+ break;
+ case WAY1_L:
+ seq_printf(m, "Way1 Locked-Down\n");
+ break;
+ case WAY01_L:
+ seq_printf(m, "Way0,Way1 Locked-Down\n");
+ break;
+ case WAY2_L:
+ seq_printf(m, "Way2 Locked-Down\n");
+ break;
+ case WAY02_L:
+ seq_printf(m, "Way0,Way2 Locked-Down\n");
+ break;
+ case WAY12_L:
+ seq_printf(m, "Way1,Way2 Locked-Down\n");
+ break;
+ case WAY012_L:
+ seq_printf(m, "Way0,Way1 & Way2 Locked-Down\n");
+ break;
+ case WAY3_L:
+ seq_printf(m, "Way3 Locked-Down\n");
+ break;
+ case WAY03_L:
+ seq_printf(m, "Way0,Way3 Locked-Down\n");
+ break;
+ case WAY13_L:
+ seq_printf(m, "Way1,Way3 Locked-Down\n");
+ break;
+ case WAY013_L:
+ seq_printf(m, "Way 0,Way1,Way3 Locked-Down\n");
+ break;
+ case WAY32_L:
+ seq_printf(m, "Way3,Way2 Locked-Down\n");
+ break;
+ case WAY320_L:
+ seq_printf(m, "Way3,Way2,Way0 Locked-Down\n");
+ break;
+ case WAY321_L:
+ seq_printf(m, "Way3,Way2,Way1 Locked-Down\n");
+ break;
+ case WAYALL_L:
+ seq_printf(m, "All Ways are locked\n");
+ break;
+ default:
+ seq_printf(m, "No Ways are locked\n");
+ }
+#endif
+ return 0;
+}
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+ return *pos < NR_CPUS ? ((void *)0x12345678) : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ ++*pos;
+ return c_start(m, pos);
+}
+
+static void c_stop(struct seq_file *m, void *v)
+{
+}
+
+struct seq_operations cpuinfo_op = {
+ .start = c_start,
+ .next = c_next,
+ .stop = c_stop,
+ .show = show_cpuinfo,
+};
+
+void cmdline_init(unsigned long r0)
+{
+ if (r0)
+ strncpy(command_line, (char *)r0, COMMAND_LINE_SIZE);
+}
diff --git a/arch/blackfin/kernel/signal.c b/arch/blackfin/kernel/signal.c
new file mode 100644
index 000000000000..316e65c3439d
--- /dev/null
+++ b/arch/blackfin/kernel/signal.c
@@ -0,0 +1,356 @@
+/*
+ * File: arch/blackfin/kernel/signal.c
+ * Based on:
+ * Author:
+ *
+ * Created:
+ * Description:
+ *
+ * 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/signal.h>
+#include <linux/syscalls.h>
+#include <linux/ptrace.h>
+#include <linux/tty.h>
+#include <linux/personality.h>
+#include <linux/binfmts.h>
+#include <linux/freezer.h>
+
+#include <asm/uaccess.h>
+#include <asm/cacheflush.h>
+#include <asm/ucontext.h>
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+struct fdpic_func_descriptor {
+ unsigned long text;
+ unsigned long GOT;
+};
+
+struct rt_sigframe {
+ int sig;
+ struct siginfo *pinfo;
+ void *puc;
+ char retcode[8];
+ struct siginfo info;
+ struct ucontext uc;
+};
+
+asmlinkage int sys_sigaltstack(const stack_t * uss, stack_t * uoss)
+{
+ return do_sigaltstack(uss, uoss, rdusp());
+}
+
+static inline int
+rt_restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc, int *pr0)
+{
+ unsigned long usp = 0;
+ int err = 0;
+
+#define RESTORE(x) err |= __get_user(regs->x, &sc->sc_##x)
+
+ /* restore passed registers */
+ RESTORE(r0); RESTORE(r1); RESTORE(r2); RESTORE(r3);
+ RESTORE(r4); RESTORE(r5); RESTORE(r6); RESTORE(r7);
+ RESTORE(p0); RESTORE(p1); RESTORE(p2); RESTORE(p3);
+ RESTORE(p4); RESTORE(p5);
+ err |= __get_user(usp, &sc->sc_usp);
+ wrusp(usp);
+ RESTORE(a0w); RESTORE(a1w);
+ RESTORE(a0x); RESTORE(a1x);
+ RESTORE(astat);
+ RESTORE(rets);
+ RESTORE(pc);
+ RESTORE(retx);
+ RESTORE(fp);
+ RESTORE(i0); RESTORE(i1); RESTORE(i2); RESTORE(i3);
+ RESTORE(m0); RESTORE(m1); RESTORE(m2); RESTORE(m3);
+ RESTORE(l0); RESTORE(l1); RESTORE(l2); RESTORE(l3);
+ RESTORE(b0); RESTORE(b1); RESTORE(b2); RESTORE(b3);
+ RESTORE(lc0); RESTORE(lc1);
+ RESTORE(lt0); RESTORE(lt1);
+ RESTORE(lb0); RESTORE(lb1);
+ RESTORE(seqstat);
+
+ regs->orig_p0 = -1; /* disable syscall checks */
+
+ *pr0 = regs->r0;
+ return err;
+}
+
+asmlinkage int do_rt_sigreturn(unsigned long __unused)
+{
+ struct pt_regs *regs = (struct pt_regs *)__unused;
+ unsigned long usp = rdusp();
+ struct rt_sigframe *frame = (struct rt_sigframe *)(usp);
+ sigset_t set;
+ int r0;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+ if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+ goto badframe;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ spin_lock_irq(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (rt_restore_sigcontext(regs, &frame->uc.uc_mcontext, &r0))
+ goto badframe;
+
+ if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->usp) == -EFAULT)
+ goto badframe;
+
+ return r0;
+
+ badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+static inline int rt_setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs)
+{
+ int err = 0;
+
+#define SETUP(x) err |= __put_user(regs->x, &sc->sc_##x)
+
+ SETUP(r0); SETUP(r1); SETUP(r2); SETUP(r3);
+ SETUP(r4); SETUP(r5); SETUP(r6); SETUP(r7);
+ SETUP(p0); SETUP(p1); SETUP(p2); SETUP(p3);
+ SETUP(p4); SETUP(p5);
+ err |= __put_user(rdusp(), &sc->sc_usp);
+ SETUP(a0w); SETUP(a1w);
+ SETUP(a0x); SETUP(a1x);
+ SETUP(astat);
+ SETUP(rets);
+ SETUP(pc);
+ SETUP(retx);
+ SETUP(fp);
+ SETUP(i0); SETUP(i1); SETUP(i2); SETUP(i3);
+ SETUP(m0); SETUP(m1); SETUP(m2); SETUP(m3);
+ SETUP(l0); SETUP(l1); SETUP(l2); SETUP(l3);
+ SETUP(b0); SETUP(b1); SETUP(b2); SETUP(b3);
+ SETUP(lc0); SETUP(lc1);
+ SETUP(lt0); SETUP(lt1);
+ SETUP(lb0); SETUP(lb1);
+ SETUP(seqstat);
+
+ return err;
+}
+
+static inline void push_cache(unsigned long vaddr, unsigned int len)
+{
+ flush_icache_range(vaddr, vaddr + len);
+}
+
+static inline void *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
+ size_t frame_size)
+{
+ unsigned long usp;
+
+ /* Default to using normal stack. */
+ usp = rdusp();
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if (ka->sa.sa_flags & SA_ONSTACK) {
+ if (!on_sig_stack(usp))
+ usp = current->sas_ss_sp + current->sas_ss_size;
+ }
+ return (void *)((usp - frame_size) & -8UL);
+}
+
+static int
+setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t * info,
+ sigset_t * set, struct pt_regs *regs)
+{
+ struct rt_sigframe *frame;
+ int err = 0;
+
+ frame = get_sigframe(ka, regs, sizeof(*frame));
+
+ err |= __put_user((current_thread_info()->exec_domain
+ && current_thread_info()->exec_domain->signal_invmap
+ && sig < 32
+ ? current_thread_info()->exec_domain->
+ signal_invmap[sig] : sig), &frame->sig);
+
+ err |= __put_user(&frame->info, &frame->pinfo);
+ err |= __put_user(&frame->uc, &frame->puc);
+ err |= copy_siginfo_to_user(&frame->info, info);
+
+ /* Create the ucontext. */
+ err |= __put_user(0, &frame->uc.uc_flags);
+ err |= __put_user(0, &frame->uc.uc_link);
+ err |=
+ __put_user((void *)current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
+ err |= __put_user(sas_ss_flags(rdusp()), &frame->uc.uc_stack.ss_flags);
+ err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
+ err |= rt_setup_sigcontext(&frame->uc.uc_mcontext, regs);
+ err |= copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
+ /* Set up to return from userspace. */
+ err |= __put_user(0x28, &(frame->retcode[0]));
+ err |= __put_user(0xe1, &(frame->retcode[1]));
+ err |= __put_user(0xad, &(frame->retcode[2]));
+ err |= __put_user(0x00, &(frame->retcode[3]));
+ err |= __put_user(0xa0, &(frame->retcode[4]));
+ err |= __put_user(0x00, &(frame->retcode[5]));
+
+ if (err)
+ goto give_sigsegv;
+
+ push_cache((unsigned long)&frame->retcode, sizeof(frame->retcode));
+
+ /* Set up registers for signal handler */
+ wrusp((unsigned long)frame);
+ if (get_personality & FDPIC_FUNCPTRS) {
+ struct fdpic_func_descriptor __user *funcptr =
+ (struct fdpic_func_descriptor *) ka->sa.sa_handler;
+ __get_user(regs->pc, &funcptr->text);
+ __get_user(regs->p3, &funcptr->GOT);
+ } else
+ regs->pc = (unsigned long)ka->sa.sa_handler;
+ regs->rets = (unsigned long)(frame->retcode);
+
+ regs->r0 = frame->sig;
+ regs->r1 = (unsigned long)(&frame->info);
+ regs->r2 = (unsigned long)(&frame->uc);
+
+ return 0;
+
+ give_sigsegv:
+ if (sig == SIGSEGV)
+ ka->sa.sa_handler = SIG_DFL;
+ force_sig(SIGSEGV, current);
+ return -EFAULT;
+}
+
+static inline void
+handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler)
+{
+ switch (regs->r0) {
+ case -ERESTARTNOHAND:
+ if (!has_handler)
+ goto do_restart;
+ regs->r0 = -EINTR;
+ break;
+
+ case -ERESTARTSYS:
+ if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) {
+ regs->r0 = -EINTR;
+ break;
+ }
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ do_restart:
+ regs->p0 = regs->orig_p0;
+ regs->r0 = regs->orig_r0;
+ regs->pc -= 2;
+ break;
+ }
+}
+
+/*
+ * OK, we're invoking a handler
+ */
+static int
+handle_signal(int sig, siginfo_t *info, struct k_sigaction *ka,
+ sigset_t *oldset, struct pt_regs *regs)
+{
+ int ret;
+
+ /* are we from a system call? to see pt_regs->orig_p0 */
+ if (regs->orig_p0 >= 0)
+ /* If so, check system call restarting.. */
+ handle_restart(regs, ka, 1);
+
+ /* set up the stack frame */
+ ret = setup_rt_frame(sig, ka, info, oldset, regs);
+
+ if (ret == 0) {
+ spin_lock_irq(&current->sighand->siglock);
+ sigorsets(&current->blocked, &current->blocked,
+ &ka->sa.sa_mask);
+ if (!(ka->sa.sa_flags & SA_NODEFER))
+ sigaddset(&current->blocked, sig);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+ }
+ return ret;
+}
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ *
+ * Note that we go through the signals twice: once to check the signals
+ * that the kernel can handle, and then we build all the user-level signal
+ * handling stack-frames in one go after that.
+ */
+asmlinkage void do_signal(struct pt_regs *regs)
+{
+ siginfo_t info;
+ int signr;
+ struct k_sigaction ka;
+ sigset_t *oldset;
+
+ current->thread.esp0 = (unsigned long)regs;
+
+ if (try_to_freeze())
+ goto no_signal;
+
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
+ oldset = &current->saved_sigmask;
+ else
+ oldset = &current->blocked;
+
+ signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+ if (signr > 0) {
+ /* Whee! Actually deliver the signal. */
+ if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
+ /* a signal was successfully delivered; the saved
+ * sigmask will have been stored in the signal frame,
+ * and will be restored by sigreturn, so we can simply
+ * clear the TIF_RESTORE_SIGMASK flag */
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
+ clear_thread_flag(TIF_RESTORE_SIGMASK);
+ }
+
+ return;
+ }
+
+no_signal:
+ /* Did we come from a system call? */
+ if (regs->orig_p0 >= 0)
+ /* Restart the system call - no handlers present */
+ handle_restart(regs, NULL, 0);
+
+ /* if there's no signal to deliver, we just put the saved sigmask
+ * back */
+ if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
+ clear_thread_flag(TIF_RESTORE_SIGMASK);
+ sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
+ }
+}
diff --git a/arch/blackfin/kernel/sys_bfin.c b/arch/blackfin/kernel/sys_bfin.c
new file mode 100644
index 000000000000..f436e6743f5a
--- /dev/null
+++ b/arch/blackfin/kernel/sys_bfin.c
@@ -0,0 +1,115 @@
+/*
+ * File: arch/blackfin/kernel/sys_bfin.c
+ * Based on:
+ * Author:
+ *
+ * Created:
+ * Description: This file contains various random system calls that
+ * have a non-standard calling sequence on the Linux/bfin
+ * platform.
+ *
+ * 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/smp_lock.h>
+#include <linux/spinlock.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <linux/syscalls.h>
+#include <linux/mman.h>
+#include <linux/file.h>
+
+#include <asm/cacheflush.h>
+#include <asm/uaccess.h>
+#include <asm/ipc.h>
+#include <asm/dma.h>
+#include <asm/unistd.h>
+
+/*
+ * sys_pipe() is the normal C calling standard for creating
+ * a pipe. It's not the way unix traditionally does this, though.
+ */
+asmlinkage int sys_pipe(unsigned long *fildes)
+{
+ int fd[2];
+ int error;
+
+ error = do_pipe(fd);
+ if (!error) {
+ if (copy_to_user(fildes, fd, 2 * sizeof(int)))
+ error = -EFAULT;
+ }
+ return error;
+}
+
+/* common code for old and new mmaps */
+static inline long
+do_mmap2(unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags,
+ unsigned long fd, unsigned long pgoff)
+{
+ int error = -EBADF;
+ struct file *file = NULL;
+
+ flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+ if (!(flags & MAP_ANONYMOUS)) {
+ file = fget(fd);
+ if (!file)
+ goto out;
+ }
+
+ down_write(&current->mm->mmap_sem);
+ error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+ up_write(&current->mm->mmap_sem);
+
+ if (file)
+ fput(file);
+ out:
+ return error;
+}
+
+asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags,
+ unsigned long fd, unsigned long pgoff)
+{
+ return do_mmap2(addr, len, prot, flags, fd, pgoff);
+}
+
+asmlinkage int sys_getpagesize(void)
+{
+ return PAGE_SIZE;
+}
+
+asmlinkage void *sys_sram_alloc(size_t size, unsigned long flags)
+{
+ return sram_alloc_with_lsl(size, flags);
+}
+
+asmlinkage int sys_sram_free(const void *addr)
+{
+ return sram_free_with_lsl(addr);
+}
+
+asmlinkage void *sys_dma_memcpy(void *dest, const void *src, size_t len)
+{
+ return safe_dma_memcpy(dest, src, len);
+}
diff --git a/arch/blackfin/kernel/time.c b/arch/blackfin/kernel/time.c
new file mode 100644
index 000000000000..f578176b6d92
--- /dev/null
+++ b/arch/blackfin/kernel/time.c
@@ -0,0 +1,326 @@
+/*
+ * File: arch/blackfin/kernel/time.c
+ * Based on: none - original work
+ * Author:
+ *
+ * Created:
+ * Description: This file contains the bfin-specific time handling details.
+ * Most of the stuff is located in the machine specific files.
+ *
+ * Modified:
+ * Copyright 2004-2006 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/profile.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/irq.h>
+
+#include <asm/blackfin.h>
+
+/* This is an NTP setting */
+#define TICK_SIZE (tick_nsec / 1000)
+
+static void time_sched_init(irqreturn_t(*timer_routine)
+ (int, void *));
+static unsigned long gettimeoffset(void);
+static inline void do_leds(void);
+
+#if (defined(CONFIG_BFIN_ALIVE_LED) || defined(CONFIG_BFIN_IDLE_LED))
+void __init init_leds(void)
+{
+ unsigned int tmp = 0;
+
+#if defined(CONFIG_BFIN_ALIVE_LED)
+ /* config pins as output. */
+ tmp = bfin_read_CONFIG_BFIN_ALIVE_LED_DPORT();
+ SSYNC();
+ bfin_write_CONFIG_BFIN_ALIVE_LED_DPORT(tmp | CONFIG_BFIN_ALIVE_LED_PIN);
+ SSYNC();
+
+ /* First set led be off */
+ tmp = bfin_read_CONFIG_BFIN_ALIVE_LED_PORT();
+ SSYNC();
+ bfin_write_CONFIG_BFIN_ALIVE_LED_PORT(tmp | CONFIG_BFIN_ALIVE_LED_PIN); /* light off */
+ SSYNC();
+#endif
+
+#if defined(CONFIG_BFIN_IDLE_LED)
+ /* config pins as output. */
+ tmp = bfin_read_CONFIG_BFIN_IDLE_LED_DPORT();
+ SSYNC();
+ bfin_write_CONFIG_BFIN_IDLE_LED_DPORT(tmp | CONFIG_BFIN_IDLE_LED_PIN);
+ SSYNC();
+
+ /* First set led be off */
+ tmp = bfin_read_CONFIG_BFIN_IDLE_LED_PORT();
+ SSYNC();
+ bfin_write_CONFIG_BFIN_IDLE_LED_PORT(tmp | CONFIG_BFIN_IDLE_LED_PIN); /* light off */
+ SSYNC();
+#endif
+}
+#else
+void __init init_leds(void)
+{
+}
+#endif
+
+#if defined(CONFIG_BFIN_ALIVE_LED)
+static inline void do_leds(void)
+{
+ static unsigned int count = 50;
+ static int flag = 0;
+ unsigned short tmp = 0;
+
+ if (--count == 0) {
+ count = 50;
+ flag = ~flag;
+ }
+ tmp = bfin_read_CONFIG_BFIN_ALIVE_LED_PORT();
+ SSYNC();
+
+ if (flag)
+ tmp &= ~CONFIG_BFIN_ALIVE_LED_PIN; /* light on */
+ else
+ tmp |= CONFIG_BFIN_ALIVE_LED_PIN; /* light off */
+
+ bfin_write_CONFIG_BFIN_ALIVE_LED_PORT(tmp);
+ SSYNC();
+
+}
+#else
+static inline void do_leds(void)
+{
+}
+#endif
+
+static struct irqaction bfin_timer_irq = {
+ .name = "BFIN Timer Tick",
+ .flags = IRQF_DISABLED
+};
+
+/*
+ * The way that the Blackfin core timer works is:
+ * - CCLK is divided by a programmable 8-bit pre-scaler (TSCALE)
+ * - Every time TSCALE ticks, a 32bit is counted down (TCOUNT)
+ *
+ * If you take the fastest clock (1ns, or 1GHz to make the math work easier)
+ * 10ms is 10,000,000 clock ticks, which fits easy into a 32-bit counter
+ * (32 bit counter is 4,294,967,296ns or 4.2 seconds) so, we don't need
+ * to use TSCALE, and program it to zero (which is pass CCLK through).
+ * If you feel like using it, try to keep HZ * TIMESCALE to some
+ * value that divides easy (like power of 2).
+ */
+
+#define TIME_SCALE 1
+
+static void
+time_sched_init(irqreturn_t(*timer_routine) (int, void *))
+{
+ u32 tcount;
+
+ /* power up the timer, but don't enable it just yet */
+ bfin_write_TCNTL(1);
+ CSYNC();
+
+ /*
+ * the TSCALE prescaler counter.
+ */
+ bfin_write_TSCALE((TIME_SCALE - 1));
+
+ tcount = ((get_cclk() / (HZ * TIME_SCALE)) - 1);
+ bfin_write_TPERIOD(tcount);
+ bfin_write_TCOUNT(tcount);
+
+ /* now enable the timer */
+ CSYNC();
+
+ bfin_write_TCNTL(7);
+
+ bfin_timer_irq.handler = (irq_handler_t)timer_routine;
+ /* call setup_irq instead of request_irq because request_irq calls
+ * kmalloc which has not been initialized yet
+ */
+ setup_irq(IRQ_CORETMR, &bfin_timer_irq);
+}
+
+/*
+ * Should return useconds since last timer tick
+ */
+static unsigned long gettimeoffset(void)
+{
+ unsigned long offset;
+ unsigned long clocks_per_jiffy;
+
+ clocks_per_jiffy = bfin_read_TPERIOD();
+ offset =
+ (clocks_per_jiffy -
+ bfin_read_TCOUNT()) / (((clocks_per_jiffy + 1) * HZ) /
+ USEC_PER_SEC);
+
+ /* Check if we just wrapped the counters and maybe missed a tick */
+ if ((bfin_read_ILAT() & (1 << IRQ_CORETMR))
+ && (offset < (100000 / HZ / 2)))
+ offset += (USEC_PER_SEC / HZ);
+
+ return offset;
+}
+
+static inline int set_rtc_mmss(unsigned long nowtime)
+{
+ return 0;
+}
+
+/*
+ * timer_interrupt() needs to keep up the real-time clock,
+ * as well as call the "do_timer()" routine every clocktick
+ */
+#ifdef CONFIG_CORE_TIMER_IRQ_L1
+irqreturn_t timer_interrupt(int irq, void *dummy)__attribute__((l1_text));
+#endif
+
+irqreturn_t timer_interrupt(int irq, void *dummy)
+{
+ /* last time the cmos clock got updated */
+ static long last_rtc_update = 0;
+
+ write_seqlock(&xtime_lock);
+
+ do_timer(1);
+ do_leds();
+
+#ifndef CONFIG_SMP
+ update_process_times(user_mode(get_irq_regs()));
+#endif
+ profile_tick(CPU_PROFILING);
+
+ /*
+ * If we have an externally synchronized Linux clock, then update
+ * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
+ * called as close as possible to 500 ms before the new second starts.
+ */
+
+ if (ntp_synced() &&
+ xtime.tv_sec > last_rtc_update + 660 &&
+ (xtime.tv_nsec / NSEC_PER_USEC) >=
+ 500000 - ((unsigned)TICK_SIZE) / 2
+ && (xtime.tv_nsec / NSEC_PER_USEC) <=
+ 500000 + ((unsigned)TICK_SIZE) / 2) {
+ if (set_rtc_mmss(xtime.tv_sec) == 0)
+ last_rtc_update = xtime.tv_sec;
+ else
+ /* Do it again in 60s. */
+ last_rtc_update = xtime.tv_sec - 600;
+ }
+ write_sequnlock(&xtime_lock);
+ return IRQ_HANDLED;
+}
+
+void __init time_init(void)
+{
+ time_t secs_since_1970 = (365 * 37 + 9) * 24 * 60 * 60; /* 1 Jan 2007 */
+
+#ifdef CONFIG_RTC_DRV_BFIN
+ /* [#2663] hack to filter junk RTC values that would cause
+ * userspace to have to deal with time values greater than
+ * 2^31 seconds (which uClibc cannot cope with yet)
+ */
+ if ((bfin_read_RTC_STAT() & 0xC0000000) == 0xC0000000) {
+ printk(KERN_NOTICE "bfin-rtc: invalid date; resetting\n");
+ bfin_write_RTC_STAT(0);
+ }
+#endif
+
+ /* Initialize xtime. From now on, xtime is updated with timer interrupts */
+ xtime.tv_sec = secs_since_1970;
+ xtime.tv_nsec = 0;
+
+ wall_to_monotonic.tv_sec = -xtime.tv_sec;
+
+ time_sched_init(timer_interrupt);
+}
+
+#ifndef CONFIG_GENERIC_TIME
+void do_gettimeofday(struct timeval *tv)
+{
+ unsigned long flags;
+ unsigned long seq;
+ unsigned long usec, sec;
+
+ do {
+ seq = read_seqbegin_irqsave(&xtime_lock, flags);
+ usec = gettimeoffset();
+ sec = xtime.tv_sec;
+ usec += (xtime.tv_nsec / NSEC_PER_USEC);
+ }
+ while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
+
+ while (usec >= USEC_PER_SEC) {
+ usec -= USEC_PER_SEC;
+ sec++;
+ }
+
+ tv->tv_sec = sec;
+ tv->tv_usec = usec;
+}
+EXPORT_SYMBOL(do_gettimeofday);
+
+int do_settimeofday(struct timespec *tv)
+{
+ time_t wtm_sec, sec = tv->tv_sec;
+ long wtm_nsec, nsec = tv->tv_nsec;
+
+ if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
+ return -EINVAL;
+
+ write_seqlock_irq(&xtime_lock);
+ /*
+ * This is revolting. We need to set the xtime.tv_usec
+ * correctly. However, the value in this location is
+ * is value at the last tick.
+ * Discover what correction gettimeofday
+ * would have done, and then undo it!
+ */
+ nsec -= (gettimeoffset() * NSEC_PER_USEC);
+
+ wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
+ wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
+
+ set_normalized_timespec(&xtime, sec, nsec);
+ set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
+
+ ntp_clear();
+
+ write_sequnlock_irq(&xtime_lock);
+ clock_was_set();
+
+ return 0;
+}
+EXPORT_SYMBOL(do_settimeofday);
+#endif /* !CONFIG_GENERIC_TIME */
+
+/*
+ * Scheduler clock - returns current time in nanosec units.
+ */
+unsigned long long sched_clock(void)
+{
+ return (unsigned long long)jiffies *(NSEC_PER_SEC / HZ);
+}
diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c
new file mode 100644
index 000000000000..9556b73de808
--- /dev/null
+++ b/arch/blackfin/kernel/traps.c
@@ -0,0 +1,649 @@
+/*
+ * File: arch/blackfin/kernel/traps.c
+ * Based on:
+ * Author: Hamish Macdonald
+ *
+ * Created:
+ * Description: uses S/W interrupt 15 for the system calls
+ *
+ * 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/uaccess.h>
+#include <asm/traps.h>
+#include <asm/cacheflush.h>
+#include <asm/blackfin.h>
+#include <asm/uaccess.h>
+#include <asm/irq_handler.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+
+#ifdef CONFIG_KGDB
+# include <linux/debugger.h>
+# include <linux/kgdb.h>
+#endif
+
+/* Initiate the event table handler */
+void __init trap_init(void)
+{
+ CSYNC();
+ bfin_write_EVT3(trap);
+ CSYNC();
+}
+
+asmlinkage void trap_c(struct pt_regs *fp);
+
+int kstack_depth_to_print = 48;
+
+static int printk_address(unsigned long address)
+{
+ struct vm_list_struct *vml;
+ struct task_struct *p;
+ struct mm_struct *mm;
+
+#ifdef CONFIG_KALLSYMS
+ unsigned long offset = 0, symsize;
+ const char *symname;
+ char *modname;
+ char *delim = ":";
+ char namebuf[128];
+
+ /* look up the address and see if we are in kernel space */
+ symname = kallsyms_lookup(address, &symsize, &offset, &modname, namebuf);
+
+ if (symname) {
+ /* yeah! kernel space! */
+ if (!modname)
+ modname = delim = "";
+ return printk("<0x%p> { %s%s%s%s + 0x%lx }",
+ (void*)address, delim, modname, delim, symname,
+ (unsigned long)offset);
+
+ }
+#endif
+
+ /* looks like we're off in user-land, so let's walk all the
+ * mappings of all our processes and see if we can't be a whee
+ * bit more specific
+ */
+ write_lock_irq(&tasklist_lock);
+ for_each_process(p) {
+ mm = get_task_mm(p);
+ if (!mm)
+ continue;
+
+ vml = mm->context.vmlist;
+ while (vml) {
+ struct vm_area_struct *vma = vml->vma;
+
+ if (address >= vma->vm_start && address < vma->vm_end) {
+ char *name = p->comm;
+ struct file *file = vma->vm_file;
+ if (file) {
+ char _tmpbuf[256];
+ name = d_path(file->f_dentry,
+ file->f_vfsmnt,
+ _tmpbuf,
+ sizeof(_tmpbuf));
+ }
+
+ write_unlock_irq(&tasklist_lock);
+ return printk("<0x%p> [ %s + 0x%lx ]",
+ (void*)address, name,
+ (unsigned long)
+ ((address - vma->vm_start) +
+ (vma->vm_pgoff << PAGE_SHIFT)));
+ }
+
+ vml = vml->next;
+ }
+ }
+ write_unlock_irq(&tasklist_lock);
+
+ /* we were unable to find this address anywhere */
+ return printk("[<0x%p>]", (void*)address);
+}
+
+#define trace_buffer_save(x) \
+ do { \
+ (x) = bfin_read_TBUFCTL(); \
+ bfin_write_TBUFCTL((x) & ~TBUFEN); \
+ } while (0)
+#define trace_buffer_restore(x) \
+ do { \
+ bfin_write_TBUFCTL((x)); \
+ } while (0)
+
+asmlinkage void trap_c(struct pt_regs *fp)
+{
+ int j, sig = 0;
+ siginfo_t info;
+ unsigned long trapnr = fp->seqstat & SEQSTAT_EXCAUSE;
+
+#ifdef CONFIG_KGDB
+# define CHK_DEBUGGER_TRAP() do { CHK_DEBUGGER(trapnr, sig, info.si_code, fp,); } while (0)
+# define CHK_DEBUGGER_TRAP_MAYBE() do { if (kgdb_connected) CHK_DEBUGGER_TRAP(); } while (0)
+#else
+# define CHK_DEBUGGER_TRAP() do { } while (0)
+# define CHK_DEBUGGER_TRAP_MAYBE() do { } while (0)
+#endif
+
+ trace_buffer_save(j);
+
+ /* trap_c() will be called for exceptions. During exceptions
+ * processing, the pc value should be set with retx value.
+ * With this change we can cleanup some code in signal.c- TODO
+ */
+ fp->orig_pc = fp->retx;
+ /* printk("exception: 0x%x, ipend=%x, reti=%x, retx=%x\n",
+ trapnr, fp->ipend, fp->pc, fp->retx); */
+
+ /* send the appropriate signal to the user program */
+ switch (trapnr) {
+
+ /* This table works in conjuction with the one in ./mach-common/entry.S
+ * Some exceptions are handled there (in assembly, in exception space)
+ * Some are handled here, (in C, in interrupt space)
+ * Some, like CPLB, are handled in both, where the normal path is
+ * handled in assembly/exception space, and the error path is handled
+ * here
+ */
+
+ /* 0x00 - Linux Syscall, getting here is an error */
+ /* 0x01 - userspace gdb breakpoint, handled here */
+ case VEC_EXCPT01:
+ info.si_code = TRAP_ILLTRAP;
+ sig = SIGTRAP;
+ CHK_DEBUGGER_TRAP_MAYBE();
+ /* Check if this is a breakpoint in kernel space */
+ if (fp->ipend & 0xffc0)
+ return;
+ else
+ break;
+#ifdef CONFIG_KGDB
+ case VEC_EXCPT02 : /* gdb connection */
+ info.si_code = TRAP_ILLTRAP;
+ sig = SIGTRAP;
+ CHK_DEBUGGER_TRAP();
+ return;
+#else
+ /* 0x02 - User Defined, Caught by default */
+#endif
+ /* 0x03 - Atomic test and set */
+ case VEC_EXCPT03:
+ info.si_code = SEGV_STACKFLOW;
+ sig = SIGSEGV;
+ printk(KERN_EMERG EXC_0x03);
+ CHK_DEBUGGER_TRAP();
+ break;
+ /* 0x04 - spinlock - handled by _ex_spinlock,
+ getting here is an error */
+ /* 0x05 - User Defined, Caught by default */
+ /* 0x06 - User Defined, Caught by default */
+ /* 0x07 - User Defined, Caught by default */
+ /* 0x08 - User Defined, Caught by default */
+ /* 0x09 - User Defined, Caught by default */
+ /* 0x0A - User Defined, Caught by default */
+ /* 0x0B - User Defined, Caught by default */
+ /* 0x0C - User Defined, Caught by default */
+ /* 0x0D - User Defined, Caught by default */
+ /* 0x0E - User Defined, Caught by default */
+ /* 0x0F - User Defined, Caught by default */
+ /* 0x10 HW Single step, handled here */
+ case VEC_STEP:
+ info.si_code = TRAP_STEP;
+ sig = SIGTRAP;
+ CHK_DEBUGGER_TRAP_MAYBE();
+ /* Check if this is a single step in kernel space */
+ if (fp->ipend & 0xffc0)
+ return;
+ else
+ break;
+ /* 0x11 - Trace Buffer Full, handled here */
+ case VEC_OVFLOW:
+ info.si_code = TRAP_TRACEFLOW;
+ sig = SIGTRAP;
+ printk(KERN_EMERG EXC_0x11);
+ CHK_DEBUGGER_TRAP();
+ break;
+ /* 0x12 - Reserved, Caught by default */
+ /* 0x13 - Reserved, Caught by default */
+ /* 0x14 - Reserved, Caught by default */
+ /* 0x15 - Reserved, Caught by default */
+ /* 0x16 - Reserved, Caught by default */
+ /* 0x17 - Reserved, Caught by default */
+ /* 0x18 - Reserved, Caught by default */
+ /* 0x19 - Reserved, Caught by default */
+ /* 0x1A - Reserved, Caught by default */
+ /* 0x1B - Reserved, Caught by default */
+ /* 0x1C - Reserved, Caught by default */
+ /* 0x1D - Reserved, Caught by default */
+ /* 0x1E - Reserved, Caught by default */
+ /* 0x1F - Reserved, Caught by default */
+ /* 0x20 - Reserved, Caught by default */
+ /* 0x21 - Undefined Instruction, handled here */
+ case VEC_UNDEF_I:
+ info.si_code = ILL_ILLOPC;
+ sig = SIGILL;
+ printk(KERN_EMERG EXC_0x21);
+ CHK_DEBUGGER_TRAP();
+ break;
+ /* 0x22 - Illegal Instruction Combination, handled here */
+ case VEC_ILGAL_I:
+ info.si_code = ILL_ILLPARAOP;
+ sig = SIGILL;
+ printk(KERN_EMERG EXC_0x22);
+ CHK_DEBUGGER_TRAP();
+ break;
+ /* 0x23 - Data CPLB Protection Violation,
+ normal case is handled in _cplb_hdr */
+ case VEC_CPLB_VL:
+ info.si_code = ILL_CPLB_VI;
+ sig = SIGILL;
+ printk(KERN_EMERG EXC_0x23);
+ CHK_DEBUGGER_TRAP();
+ break;
+ /* 0x24 - Data access misaligned, handled here */
+ case VEC_MISALI_D:
+ info.si_code = BUS_ADRALN;
+ sig = SIGBUS;
+ printk(KERN_EMERG EXC_0x24);
+ CHK_DEBUGGER_TRAP();
+ break;
+ /* 0x25 - Unrecoverable Event, handled here */
+ case VEC_UNCOV:
+ info.si_code = ILL_ILLEXCPT;
+ sig = SIGILL;
+ printk(KERN_EMERG EXC_0x25);
+ CHK_DEBUGGER_TRAP();
+ break;
+ /* 0x26 - Data CPLB Miss, normal case is handled in _cplb_hdr,
+ error case is handled here */
+ case VEC_CPLB_M:
+ info.si_code = BUS_ADRALN;
+ sig = SIGBUS;
+ printk(KERN_EMERG EXC_0x26);
+ CHK_DEBUGGER_TRAP();
+ break;
+ /* 0x27 - Data CPLB Multiple Hits - Linux Trap Zero, handled here */
+ case VEC_CPLB_MHIT:
+ info.si_code = ILL_CPLB_MULHIT;
+#ifdef CONFIG_DEBUG_HUNT_FOR_ZERO
+ sig = SIGSEGV;
+ printk(KERN_EMERG "\n\nNULL pointer access (probably)\n");
+#else
+ sig = SIGILL;
+ printk(KERN_EMERG EXC_0x27);
+#endif
+ CHK_DEBUGGER_TRAP();
+ break;
+ /* 0x28 - Emulation Watchpoint, handled here */
+ case VEC_WATCH:
+ info.si_code = TRAP_WATCHPT;
+ sig = SIGTRAP;
+ pr_debug(EXC_0x28);
+ CHK_DEBUGGER_TRAP_MAYBE();
+ /* Check if this is a watchpoint in kernel space */
+ if (fp->ipend & 0xffc0)
+ return;
+ else
+ break;
+#ifdef CONFIG_BF535
+ /* 0x29 - Instruction fetch access error (535 only) */
+ case VEC_ISTRU_VL: /* ADSP-BF535 only (MH) */
+ info.si_code = BUS_OPFETCH;
+ sig = SIGBUS;
+ printk(KERN_EMERG "BF535: VEC_ISTRU_VL\n");
+ CHK_DEBUGGER_TRAP();
+ break;
+#else
+ /* 0x29 - Reserved, Caught by default */
+#endif
+ /* 0x2A - Instruction fetch misaligned, handled here */
+ case VEC_MISALI_I:
+ info.si_code = BUS_ADRALN;
+ sig = SIGBUS;
+ printk(KERN_EMERG EXC_0x2A);
+ CHK_DEBUGGER_TRAP();
+ break;
+ /* 0x2B - Instruction CPLB protection Violation,
+ handled in _cplb_hdr */
+ case VEC_CPLB_I_VL:
+ info.si_code = ILL_CPLB_VI;
+ sig = SIGILL;
+ printk(KERN_EMERG EXC_0x2B);
+ CHK_DEBUGGER_TRAP();
+ break;
+ /* 0x2C - Instruction CPLB miss, handled in _cplb_hdr */
+ case VEC_CPLB_I_M:
+ info.si_code = ILL_CPLB_MISS;
+ sig = SIGBUS;
+ printk(KERN_EMERG EXC_0x2C);
+ CHK_DEBUGGER_TRAP();
+ break;
+ /* 0x2D - Instruction CPLB Multiple Hits, handled here */
+ case VEC_CPLB_I_MHIT:
+ info.si_code = ILL_CPLB_MULHIT;
+#ifdef CONFIG_DEBUG_HUNT_FOR_ZERO
+ sig = SIGSEGV;
+ printk(KERN_EMERG "\n\nJump to address 0 - 0x0fff\n");
+#else
+ sig = SIGILL;
+ printk(KERN_EMERG EXC_0x2D);
+#endif
+ CHK_DEBUGGER_TRAP();
+ break;
+ /* 0x2E - Illegal use of Supervisor Resource, handled here */
+ case VEC_ILL_RES:
+ info.si_code = ILL_PRVOPC;
+ sig = SIGILL;
+ printk(KERN_EMERG EXC_0x2E);
+ CHK_DEBUGGER_TRAP();
+ break;
+ /* 0x2F - Reserved, Caught by default */
+ /* 0x30 - Reserved, Caught by default */
+ /* 0x31 - Reserved, Caught by default */
+ /* 0x32 - Reserved, Caught by default */
+ /* 0x33 - Reserved, Caught by default */
+ /* 0x34 - Reserved, Caught by default */
+ /* 0x35 - Reserved, Caught by default */
+ /* 0x36 - Reserved, Caught by default */
+ /* 0x37 - Reserved, Caught by default */
+ /* 0x38 - Reserved, Caught by default */
+ /* 0x39 - Reserved, Caught by default */
+ /* 0x3A - Reserved, Caught by default */
+ /* 0x3B - Reserved, Caught by default */
+ /* 0x3C - Reserved, Caught by default */
+ /* 0x3D - Reserved, Caught by default */
+ /* 0x3E - Reserved, Caught by default */
+ /* 0x3F - Reserved, Caught by default */
+ default:
+ info.si_code = TRAP_ILLTRAP;
+ sig = SIGTRAP;
+ printk(KERN_EMERG "Caught Unhandled Exception, code = %08lx\n",
+ (fp->seqstat & SEQSTAT_EXCAUSE));
+ CHK_DEBUGGER_TRAP();
+ break;
+ }
+
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_addr = (void *)fp->pc;
+ force_sig_info(sig, &info, current);
+ if (sig != 0 && sig != SIGTRAP) {
+ unsigned long stack;
+ dump_bfin_regs(fp, (void *)fp->retx);
+ dump_bfin_trace_buffer();
+ show_stack(current, &stack);
+ if (current->mm == NULL)
+ panic("Kernel exception");
+ }
+
+ /* if the address that we are about to return to is not valid, set it
+ * to a valid address, if we have a current application or panic
+ */
+ if (!(fp->pc <= physical_mem_end
+#if L1_CODE_LENGTH != 0
+ || (fp->pc >= L1_CODE_START &&
+ fp->pc <= (L1_CODE_START + L1_CODE_LENGTH))
+#endif
+ )) {
+ if (current->mm) {
+ fp->pc = current->mm->start_code;
+ } else {
+ printk(KERN_EMERG "I can't return to memory that doesn't exist - bad things happen\n");
+ panic("Help - I've fallen and can't get up\n");
+ }
+ }
+
+ trace_buffer_restore(j);
+ return;
+}
+
+/* Typical exception handling routines */
+
+void dump_bfin_trace_buffer(void)
+{
+ int tflags;
+ trace_buffer_save(tflags);
+
+ if (likely(bfin_read_TBUFSTAT() & TBUFCNT)) {
+ int i;
+ printk(KERN_EMERG "Hardware Trace:\n");
+ for (i = 0; bfin_read_TBUFSTAT() & TBUFCNT; i++) {
+ printk(KERN_EMERG "%2i Target : ", i);
+ printk_address((unsigned long)bfin_read_TBUF());
+ printk("\n" KERN_EMERG " Source : ");
+ printk_address((unsigned long)bfin_read_TBUF());
+ printk("\n");
+ }
+ }
+
+ trace_buffer_restore(tflags);
+}
+EXPORT_SYMBOL(dump_bfin_trace_buffer);
+
+static void show_trace(struct task_struct *tsk, unsigned long *sp)
+{
+ unsigned long addr;
+
+ printk("\nCall Trace:");
+#ifdef CONFIG_KALLSYMS
+ printk("\n");
+#endif
+
+ while (!kstack_end(sp)) {
+ addr = *sp++;
+ /*
+ * If the address is either in the text segment of the
+ * kernel, or in the region which contains vmalloc'ed
+ * memory, it *may* be the address of a calling
+ * routine; if so, print it so that someone tracing
+ * down the cause of the crash will be able to figure
+ * out the call path that was taken.
+ */
+ if (kernel_text_address(addr))
+ print_ip_sym(addr);
+ }
+
+ printk("\n");
+}
+
+void show_stack(struct task_struct *task, unsigned long *stack)
+{
+ unsigned long *endstack, addr;
+ int i;
+
+ /* Cannot call dump_bfin_trace_buffer() here as show_stack() is
+ * called externally in some places in the kernel.
+ */
+
+ if (!stack) {
+ if (task)
+ stack = (unsigned long *)task->thread.ksp;
+ else
+ stack = (unsigned long *)&stack;
+ }
+
+ addr = (unsigned long)stack;
+ endstack = (unsigned long *)PAGE_ALIGN(addr);
+
+ printk(KERN_EMERG "Stack from %08lx:", (unsigned long)stack);
+ for (i = 0; i < kstack_depth_to_print; i++) {
+ if (stack + 1 > endstack)
+ break;
+ if (i % 8 == 0)
+ printk("\n" KERN_EMERG " ");
+ printk(" %08lx", *stack++);
+ }
+
+ show_trace(task, stack);
+}
+
+void dump_stack(void)
+{
+ unsigned long stack;
+ int tflags;
+ trace_buffer_save(tflags);
+ dump_bfin_trace_buffer();
+ show_stack(current, &stack);
+ trace_buffer_restore(tflags);
+}
+
+EXPORT_SYMBOL(dump_stack);
+
+void dump_bfin_regs(struct pt_regs *fp, void *retaddr)
+{
+ if (current->pid) {
+ printk("\nCURRENT PROCESS:\n\n");
+ printk("COMM=%s PID=%d\n", current->comm, current->pid);
+ } else {
+ printk
+ ("\nNo Valid pid - Either things are really messed up, or you are in the kernel\n");
+ }
+
+ if (current->mm) {
+ printk("TEXT = 0x%p-0x%p DATA = 0x%p-0x%p\n"
+ "BSS = 0x%p-0x%p USER-STACK = 0x%p\n\n",
+ (void*)current->mm->start_code,
+ (void*)current->mm->end_code,
+ (void*)current->mm->start_data,
+ (void*)current->mm->end_data,
+ (void*)current->mm->end_data,
+ (void*)current->mm->brk,
+ (void*)current->mm->start_stack);
+ }
+
+ printk("return address: 0x%p; contents of [PC-16...PC+8]:\n", retaddr);
+ if (retaddr != 0 && retaddr <= (void*)physical_mem_end
+#if L1_CODE_LENGTH != 0
+ /* FIXME: Copy the code out of L1 Instruction SRAM through dma
+ memcpy. */
+ && !(retaddr >= (void*)L1_CODE_START
+ && retaddr < (void*)(L1_CODE_START + L1_CODE_LENGTH))
+#endif
+ ) {
+ int i = 0;
+ unsigned short x = 0;
+ for (i = -16; i < 8; i++) {
+ if (get_user(x, (unsigned short *)retaddr + i))
+ break;
+#ifndef CONFIG_DEBUG_HWERR
+ /* If one of the last few instructions was a STI
+ * it is likily that the error occured awhile ago
+ * and we just noticed
+ */
+ if (x >= 0x0040 && x <= 0x0047 && i <= 0)
+ panic("\n\nWARNING : You should reconfigure the kernel to turn on\n"
+ " 'Hardware error interrupt debugging'\n"
+ " The rest of this error is meanless\n");
+#endif
+
+ if (i == -8)
+ printk("\n");
+ if (i == 0)
+ printk("X\n");
+ printk("%04x ", x);
+ }
+ } else
+ printk("Cannot look at the [PC] for it is in unreadable L1 SRAM - sorry\n");
+
+ printk("\n\n");
+
+ printk("RETE: %08lx RETN: %08lx RETX: %08lx RETS: %08lx\n",
+ fp->rete, fp->retn, fp->retx, fp->rets);
+ printk("IPEND: %04lx SYSCFG: %04lx\n", fp->ipend, fp->syscfg);
+ printk("SEQSTAT: %08lx SP: %08lx\n", (long)fp->seqstat, (long)fp);
+ printk("R0: %08lx R1: %08lx R2: %08lx R3: %08lx\n",
+ fp->r0, fp->r1, fp->r2, fp->r3);
+ printk("R4: %08lx R5: %08lx R6: %08lx R7: %08lx\n",
+ fp->r4, fp->r5, fp->r6, fp->r7);
+ printk("P0: %08lx P1: %08lx P2: %08lx P3: %08lx\n",
+ fp->p0, fp->p1, fp->p2, fp->p3);
+ printk("P4: %08lx P5: %08lx FP: %08lx\n", fp->p4, fp->p5, fp->fp);
+ printk("A0.w: %08lx A0.x: %08lx A1.w: %08lx A1.x: %08lx\n",
+ fp->a0w, fp->a0x, fp->a1w, fp->a1x);
+
+ printk("LB0: %08lx LT0: %08lx LC0: %08lx\n", fp->lb0, fp->lt0,
+ fp->lc0);
+ printk("LB1: %08lx LT1: %08lx LC1: %08lx\n", fp->lb1, fp->lt1,
+ fp->lc1);
+ printk("B0: %08lx L0: %08lx M0: %08lx I0: %08lx\n", fp->b0, fp->l0,
+ fp->m0, fp->i0);
+ printk("B1: %08lx L1: %08lx M1: %08lx I1: %08lx\n", fp->b1, fp->l1,
+ fp->m1, fp->i1);
+ printk("B2: %08lx L2: %08lx M2: %08lx I2: %08lx\n", fp->b2, fp->l2,
+ fp->m2, fp->i2);
+ printk("B3: %08lx L3: %08lx M3: %08lx I3: %08lx\n", fp->b3, fp->l3,
+ fp->m3, fp->i3);
+
+ printk("\nUSP: %08lx ASTAT: %08lx\n", rdusp(), fp->astat);
+ if ((long)fp->seqstat & SEQSTAT_EXCAUSE) {
+ printk(KERN_EMERG "DCPLB_FAULT_ADDR=%p\n", (void*)bfin_read_DCPLB_FAULT_ADDR());
+ printk(KERN_EMERG "ICPLB_FAULT_ADDR=%p\n", (void*)bfin_read_ICPLB_FAULT_ADDR());
+ }
+
+ printk("\n\n");
+}
+
+#ifdef CONFIG_SYS_BFIN_SPINLOCK_L1
+asmlinkage int sys_bfin_spinlock(int *spinlock)__attribute__((l1_text));
+#endif
+
+asmlinkage int sys_bfin_spinlock(int *spinlock)
+{
+ int ret = 0;
+ int tmp = 0;
+
+ local_irq_disable();
+ ret = get_user(tmp, spinlock);
+ if (ret == 0) {
+ if (tmp)
+ ret = 1;
+ tmp = 1;
+ put_user(tmp, spinlock);
+ }
+ local_irq_enable();
+ return ret;
+}
+
+void panic_cplb_error(int cplb_panic, struct pt_regs *fp)
+{
+ switch (cplb_panic) {
+ case CPLB_NO_UNLOCKED:
+ printk(KERN_EMERG "All CPLBs are locked\n");
+ break;
+ case CPLB_PROT_VIOL:
+ return;
+ case CPLB_NO_ADDR_MATCH:
+ return;
+ case CPLB_UNKNOWN_ERR:
+ printk(KERN_EMERG "Unknown CPLB Exception\n");
+ break;
+ }
+
+ printk(KERN_EMERG "DCPLB_FAULT_ADDR=%p\n", (void*)bfin_read_DCPLB_FAULT_ADDR());
+ printk(KERN_EMERG "ICPLB_FAULT_ADDR=%p\n", (void*)bfin_read_ICPLB_FAULT_ADDR());
+ dump_bfin_regs(fp, (void *)fp->retx);
+ dump_stack();
+ panic("Unrecoverable event\n");
+}
diff --git a/arch/blackfin/kernel/vmlinux.lds.S b/arch/blackfin/kernel/vmlinux.lds.S
new file mode 100644
index 000000000000..6ae9ebbd8e58
--- /dev/null
+++ b/arch/blackfin/kernel/vmlinux.lds.S
@@ -0,0 +1,228 @@
+/*
+ * File: arch/blackfin/kernel/vmlinux.lds.S
+ * Based on: none - original work
+ * Author:
+ *
+ * Created: Tue Sep 21 2004
+ * Description: Master linker script for blackfin architecture
+ *
+ * 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
+ */
+
+#define VMLINUX_SYMBOL(_sym_) _##_sym_
+
+#include <asm-generic/vmlinux.lds.h>
+#include <asm/mem_map.h>
+
+
+OUTPUT_FORMAT("elf32-bfin")
+ENTRY(__start)
+_jiffies = _jiffies_64;
+
+MEMORY
+{
+ ram : ORIGIN = CONFIG_BOOT_LOAD, LENGTH = (CONFIG_MEM_SIZE * 1024 * 1024) - (CONFIG_BOOT_LOAD)
+ l1_data_a : ORIGIN = L1_DATA_A_START, LENGTH = L1_DATA_A_LENGTH
+ l1_data_b : ORIGIN = L1_DATA_B_START, LENGTH = L1_DATA_B_LENGTH
+ l1_code : ORIGIN = L1_CODE_START, LENGTH = L1_CODE_LENGTH
+ l1_scratch : ORIGIN = L1_SCRATCH_START, LENGTH = L1_SCRATCH_LENGTH
+}
+
+SECTIONS
+{
+ . = CONFIG_BOOT_LOAD;
+
+ .text :
+ {
+ _text = .;
+ __stext = .;
+ *(.text)
+ SCHED_TEXT
+ *(.text.lock)
+ . = ALIGN(16);
+ ___start___ex_table = .;
+ *(__ex_table)
+ ___stop___ex_table = .;
+
+ *($code)
+ *(.rodata)
+ *(.rodata.*)
+ *(__vermagic) /* Kernel version magic */
+ *(.rodata1)
+ *(.fixup)
+ *(.spinlock.text)
+
+ /* Kernel symbol table: Normal symbols */
+ . = ALIGN(4);
+ ___start___ksymtab = .;
+ *(__ksymtab)
+ ___stop___ksymtab = .;
+
+ /* Kernel symbol table: GPL-only symbols */
+ ___start___ksymtab_gpl = .;
+ *(__ksymtab_gpl)
+ ___stop___ksymtab_gpl = .;
+
+ /* Kernel symbol table: Normal unused symbols */ \
+ ___start___ksymtab_unused = .;
+ *(__ksymtab_unused)
+ ___stop___ksymtab_unused = .;
+
+ /* Kernel symbol table: GPL-only unused symbols */
+ ___start___ksymtab_unused_gpl = .;
+ *(__ksymtab_unused_gpl)
+ ___stop___ksymtab_unused_gpl = .;
+
+
+ /* Kernel symbol table: GPL-future symbols */
+ ___start___ksymtab_gpl_future = .;
+ *(__ksymtab_gpl_future)
+ ___stop___ksymtab_gpl_future = .;
+
+ /* Kernel symbol table: Normal symbols */
+ ___start___kcrctab = .;
+ *(__kcrctab)
+ ___stop___kcrctab = .;
+
+ /* Kernel symbol table: GPL-only symbols */
+ ___start___kcrctab_gpl = .;
+ *(__kcrctab_gpl)
+ ___stop___kcrctab_gpl = .;
+
+ /* Kernel symbol table: GPL-future symbols */
+ ___start___kcrctab_gpl_future = .;
+ *(__kcrctab_gpl_future)
+ ___stop___kcrctab_gpl_future = .;
+
+ /* Kernel symbol table: strings */
+ *(__ksymtab_strings)
+
+ . = ALIGN(4);
+ __etext = .;
+ } > ram
+
+ .init :
+ {
+ . = ALIGN(4096);
+ ___init_begin = .;
+ __sinittext = .;
+ *(.init.text)
+ __einittext = .;
+ *(.init.data)
+ . = ALIGN(16);
+ ___setup_start = .;
+ *(.init.setup)
+ ___setup_end = .;
+ ___start___param = .;
+ *(__param)
+ ___stop___param = .;
+ ___initcall_start = .;
+ INITCALLS
+ ___initcall_end = .;
+ ___con_initcall_start = .;
+ *(.con_initcall.init)
+ ___con_initcall_end = .;
+ ___security_initcall_start = .;
+ *(.security_initcall.init)
+ ___security_initcall_end = .;
+ . = ALIGN(4);
+ ___initramfs_start = .;
+ *(.init.ramfs)
+ ___initramfs_end = .;
+ . = ALIGN(4);
+ ___init_end = .;
+ } > ram
+
+ __l1_lma_start = .;
+
+ .text_l1 :
+ {
+ . = ALIGN(4);
+ __stext_l1 = .;
+ *(.l1.text)
+
+ . = ALIGN(4);
+ __etext_l1 = .;
+ } > l1_code AT > ram
+
+ .data_l1 :
+ {
+ . = ALIGN(4);
+ __sdata_l1 = .;
+ *(.l1.data)
+ __edata_l1 = .;
+
+ . = ALIGN(4);
+ __sbss_l1 = .;
+ *(.l1.bss)
+
+ . = ALIGN(32);
+ *(.data_l1.cacheline_aligned)
+
+ . = ALIGN(4);
+ __ebss_l1 = .;
+ } > l1_data_a AT > ram
+ .data_b_l1 :
+ {
+ . = ALIGN(4);
+ __sdata_b_l1 = .;
+ *(.l1.data.B)
+ __edata_b_l1 = .;
+
+ . = ALIGN(4);
+ __sbss_b_l1 = .;
+ *(.l1.bss.B)
+
+ . = ALIGN(4);
+ __ebss_b_l1 = .;
+ } > l1_data_b AT > ram
+
+ .data :
+ {
+ __sdata = .;
+ . = ALIGN(0x2000);
+ *(.data.init_task)
+ *(.data)
+
+ . = ALIGN(32);
+ *(.data.cacheline_aligned)
+
+ . = ALIGN(0x2000);
+ __edata = .;
+ } > ram
+
+ /DISCARD/ : { /* Exit code and data*/
+ *(.exit.text)
+ *(.exit.data)
+ *(.exitcall.exit)
+ } > ram
+
+ .bss :
+ {
+ . = ALIGN(4);
+ ___bss_start = .;
+ *(.bss)
+ *(COMMON)
+ . = ALIGN(4);
+ ___bss_stop = .;
+ __end = . ;
+ } > ram
+}