diff options
Diffstat (limited to 'arch/blackfin/kernel')
-rw-r--r-- | arch/blackfin/kernel/Makefile | 14 | ||||
-rw-r--r-- | arch/blackfin/kernel/asm-offsets.c | 136 | ||||
-rw-r--r-- | arch/blackfin/kernel/bfin_dma_5xx.c | 742 | ||||
-rw-r--r-- | arch/blackfin/kernel/bfin_gpio.c | 637 | ||||
-rw-r--r-- | arch/blackfin/kernel/bfin_ksyms.c | 119 | ||||
-rw-r--r-- | arch/blackfin/kernel/dma-mapping.c | 183 | ||||
-rw-r--r-- | arch/blackfin/kernel/dualcore_test.c | 49 | ||||
-rw-r--r-- | arch/blackfin/kernel/entry.S | 94 | ||||
-rw-r--r-- | arch/blackfin/kernel/flat.c | 101 | ||||
-rw-r--r-- | arch/blackfin/kernel/init_task.c | 60 | ||||
-rw-r--r-- | arch/blackfin/kernel/irqchip.c | 147 | ||||
-rw-r--r-- | arch/blackfin/kernel/module.c | 429 | ||||
-rw-r--r-- | arch/blackfin/kernel/process.c | 394 | ||||
-rw-r--r-- | arch/blackfin/kernel/ptrace.c | 430 | ||||
-rw-r--r-- | arch/blackfin/kernel/setup.c | 902 | ||||
-rw-r--r-- | arch/blackfin/kernel/signal.c | 356 | ||||
-rw-r--r-- | arch/blackfin/kernel/sys_bfin.c | 115 | ||||
-rw-r--r-- | arch/blackfin/kernel/time.c | 326 | ||||
-rw-r--r-- | arch/blackfin/kernel/traps.c | 649 | ||||
-rw-r--r-- | arch/blackfin/kernel/vmlinux.lds.S | 228 |
20 files changed, 6111 insertions, 0 deletions
diff --git a/arch/blackfin/kernel/Makefile b/arch/blackfin/kernel/Makefile new file mode 100644 index 00000000000..f3b7d2f9d49 --- /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 00000000000..41d9a9f8970 --- /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 00000000000..8ea079ebecb --- /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 00000000000..e9f24a9a46b --- /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 00000000000..f64ecb638fa --- /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 00000000000..539eb24e062 --- /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 00000000000..8b89c99f9df --- /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 00000000000..5880b270bd5 --- /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 00000000000..a92587b628b --- /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 00000000000..b45188f8512 --- /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 00000000000..df5bf022cf7 --- /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 00000000000..372f756f1ad --- /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 00000000000..3eff7439d8d --- /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(®s, 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, ®s, 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 00000000000..d7c8e514cb9 --- /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 00000000000..342bb8dd56a --- /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 00000000000..316e65c3439 --- /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(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->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(¤t->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, + &ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + spin_unlock_irq(¤t->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 = ¤t->saved_sigmask; + else + oldset = ¤t->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, ¤t->saved_sigmask, NULL); + } +} diff --git a/arch/blackfin/kernel/sys_bfin.c b/arch/blackfin/kernel/sys_bfin.c new file mode 100644 index 00000000000..f436e6743f5 --- /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(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->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 00000000000..f578176b6d9 --- /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 00000000000..9556b73de80 --- /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 00000000000..6ae9ebbd8e5 --- /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 +} |