From 70b2bd01829b38a1a79caeda05d436b2e5fecf82 Mon Sep 17 00:00:00 2001 From: Kurt Mahan <kmahan@freescale.com> Date: Wed, 31 Oct 2007 17:00:18 -0600 Subject: [PATCH] Core Coldfire/MCF5445x specific code. LTIBName: mcfv4e-coldfire-code Signed-off-by: Kurt Mahan <kmahan@freescale.com> --- arch/m68k/coldfire/Makefile | 11 + arch/m68k/coldfire/cache.c | 215 +++++++++ arch/m68k/coldfire/config.c | 420 ++++++++++++++++++ arch/m68k/coldfire/entry.S | 701 ++++++++++++++++++++++++++++++ arch/m68k/coldfire/head.S | 474 ++++++++++++++++++++ arch/m68k/coldfire/ints.c | 384 ++++++++++++++++ arch/m68k/coldfire/iomap.c | 54 +++ arch/m68k/coldfire/mcf5445x-pci.c | 427 ++++++++++++++++++ arch/m68k/coldfire/muldi3.S | 64 +++ arch/m68k/coldfire/pci.c | 245 +++++++++++ arch/m68k/coldfire/signal.c | 868 +++++++++++++++++++++++++++++++++++++ arch/m68k/coldfire/traps.c | 454 +++++++++++++++++++ arch/m68k/coldfire/vmlinux-cf.lds | 92 ++++ 13 files changed, 4409 insertions(+), 0 deletions(-) create mode 100644 arch/m68k/coldfire/Makefile create mode 100644 arch/m68k/coldfire/cache.c create mode 100644 arch/m68k/coldfire/config.c create mode 100644 arch/m68k/coldfire/entry.S create mode 100644 arch/m68k/coldfire/head.S create mode 100644 arch/m68k/coldfire/ints.c create mode 100644 arch/m68k/coldfire/iomap.c create mode 100644 arch/m68k/coldfire/mcf5445x-pci.c create mode 100644 arch/m68k/coldfire/muldi3.S create mode 100644 arch/m68k/coldfire/pci.c create mode 100644 arch/m68k/coldfire/signal.c create mode 100644 arch/m68k/coldfire/traps.c create mode 100644 arch/m68k/coldfire/vmlinux-cf.lds --- /dev/null +++ b/arch/m68k/coldfire/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for Linux arch/m68k/coldfire source directory +# + +obj-y:= entry.o config.o cache.o signal.o muldi3.o traps.o ints.o + +ifneq ($(strip $(CONFIG_USB) $(CONFIG_USB_GADGET_MCF5445X)),) + obj-y += usb.o usb/ +endif + +obj-$(CONFIG_PCI) += pci.o mcf5445x-pci.o iomap.o --- /dev/null +++ b/arch/m68k/coldfire/cache.c @@ -0,0 +1,215 @@ +/* + * linux/arch/m68k/coldifre/cache.c + * + * Matt Waddel Matt.Waddel@freescale.com + * Copyright Freescale Semiconductor, Inc. 2007 + * + * 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. + */ + +#include <linux/interrupt.h> +#include <asm/cfcache.h> +#include <asm/coldfire.h> +#include <asm/system.h> + +#define _DCACHE_SIZE (2*16384) +#define _ICACHE_SIZE (2*16384) + +#define _SET_SHIFT 4 + +/* + * Masks for cache sizes. Programming note: because the set size is a + * power of two, the mask is also the last address in the set. + */ + +#define _DCACHE_SET_MASK ((_DCACHE_SIZE/64-1)<<_SET_SHIFT) +#define _ICACHE_SET_MASK ((_ICACHE_SIZE/64-1)<<_SET_SHIFT) +#define LAST_DCACHE_ADDR _DCACHE_SET_MASK +#define LAST_ICACHE_ADDR _ICACHE_SET_MASK + +/************************************************************ + * Routine to cleanly flush the cache, pushing all lines and + * invalidating them. + * + * The is the flash-resident version, used after copying the .text + * segment from flash to ram. + *************************************************************/ +void FLASHDcacheFlushInvalidate(void) + __attribute__ ((section (".text_loader"))); + +void FLASHDcacheFlushInvalidate() +{ + unsigned long set; + unsigned long start_set; + unsigned long end_set; + + start_set = 0; + end_set = (unsigned long)LAST_DCACHE_ADDR; + + for (set = start_set; set < end_set; set += (0x10 - 3)) + asm volatile("cpushl %%dc,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%dc,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%dc,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%dc,(%0)" : : "a" (set)); +} + +/************************************************************ + * Routine to cleanly flush the cache, pushing all lines and + * invalidating them. + * + *************************************************************/ +void DcacheFlushInvalidate() +{ + unsigned long set; + unsigned long start_set; + unsigned long end_set; + + start_set = 0; + end_set = (unsigned long)LAST_DCACHE_ADDR; + + for (set = start_set; set < end_set; set += (0x10 - 3)) + asm volatile("cpushl %%dc,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%dc,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%dc,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%dc,(%0)" : : "a" (set)); +} + + + +/****************************************************************************** + * Routine to cleanly flush the a block of cache, pushing all relevant lines + * and invalidating them. + * + ******************************************************************************/ +void DcacheFlushInvalidateCacheBlock(void *start, unsigned long size) +{ + unsigned long set; + unsigned long start_set; + unsigned long end_set; + + /* if size is bigger than the cache can store + * set the size to the maximum amount + */ + + if (size > LAST_DCACHE_ADDR) + size = LAST_DCACHE_ADDR; + + start_set = ((unsigned long)start) & _DCACHE_SET_MASK; + end_set = ((unsigned long)(start+size-1)) & _DCACHE_SET_MASK; + + if (start_set > end_set) { + /* from the begining to the lowest address */ + for (set = 0; set <= end_set; set += (0x10 - 3)) + asm volatile("cpushl %%dc,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%dc,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%dc,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%dc,(%0)" : : "a" (set)); + + /* next loop will finish the cache ie pass the hole */ + end_set = LAST_DCACHE_ADDR; + } + for (set = start_set; set <= end_set; set += (0x10 - 3)) + asm volatile("cpushl %%dc,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%dc,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%dc,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%dc,(%0)" : : "a" (set)); +} + + +void IcacheInvalidateCacheBlock(void *start, unsigned long size) +{ + unsigned long set; + unsigned long start_set; + unsigned long end_set; + + /* if size is bigger than the cache can store + * set the size to the maximum ammount + */ + + if (size > LAST_ICACHE_ADDR) + size = LAST_ICACHE_ADDR; + + start_set = ((unsigned long)start) & _ICACHE_SET_MASK; + end_set = ((unsigned long)(start+size-1)) & _ICACHE_SET_MASK; + + if (start_set > end_set) { + /* from the begining to the lowest address */ + for (set = 0; set <= end_set; set += (0x10 - 3)) + asm volatile("cpushl %%ic,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%ic,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%ic,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%ic,(%0)" : : "a" (set)); + + /* next loop will finish the cache ie pass the hole */ + end_set = LAST_ICACHE_ADDR; + } + for (set = start_set; set <= end_set; set += (0x10 - 3)) + asm volatile("cpushl %%ic,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%ic,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%ic,(%0)\n" + "\taddq%.l #1,%0\n" + "\tcpushl %%ic,(%0)" : : "a" (set)); +} + + +/******************************************************************** + * Disable the data cache completely + ********************************************************************/ +void DcacheDisable(void) +{ + int newValue; + unsigned long flags; + + local_save_flags(flags); + local_irq_disable(); + + DcacheFlushInvalidate(); /* begin by flushing the cache */ + newValue = CACHE_DISABLE_MODE; /* disable it */ + cacr_set(newValue); + local_irq_restore(flags); +} + +/******************************************************************** + * Unconditionally enable the data cache + ********************************************************************/ +void DcacheEnable(void) +{ + cacr_set(CACHE_INITIAL_MODE); +} + + +unsigned long shadow_cacr; + +void cacr_set(unsigned long x) +{ + shadow_cacr = x; + + __asm__ __volatile__ ("movec %0, %%cacr" + : /* no outputs */ + : "r" (shadow_cacr)); +} + +unsigned long cacr_get(void) +{ + return shadow_cacr; +} --- /dev/null +++ b/arch/m68k/coldfire/config.c @@ -0,0 +1,420 @@ +/* + * linux/arch/m68k/coldifre/config.c + * + * Matt Waddel Matt.Waddel@freescale.com + * Copyright Freescale Semiconductor, Inc. 2007 + * + * 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. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/console.h> +#include <linux/bootmem.h> +#include <linux/mm.h> +#include <asm/bootinfo.h> +#include <asm/machdep.h> +#include <asm/coldfire.h> +#include <asm/cfcache.h> +#include <asm/bootinfo.h> +#include <asm/io.h> +#include <asm/cfmmu.h> +#include <asm/setup.h> +#include <asm/irq.h> +#include <asm/traps.h> +#include <asm/movs.h> +#include <asm/movs.h> +#include <asm/page.h> +#include <asm/pgalloc.h> +#include <asm/mcf5445x_intc.h> +#include <asm/mcf5445x_sdramc.h> +#include <asm/mcf5445x_fbcs.h> +#include <asm/mcf5445x_dtim.h> + +/* JKM -- testing */ +#include <linux/pfn.h> +/* JKM */ + +extern int get_irq_list(struct seq_file *p, void *v); +extern char _text, _end; +extern char _etext, _edata, __init_begin, __init_end; +extern struct console mcfrs_console; +extern char m68k_command_line[CL_SIZE]; +extern unsigned long availmem; + +static int irq_enable[NR_IRQS]; +unsigned long num_pages; + +void coldfire_sort_memrec(void) +{ + int i, j; + + /* Sort the m68k_memory records by address */ + for (i = 0; i < m68k_num_memory; ++i) { + for (j = i + 1; j < m68k_num_memory; ++j) { + if (m68k_memory[i].addr > m68k_memory[j].addr) { + struct mem_info tmp; + tmp = m68k_memory[i]; + m68k_memory[i] = m68k_memory[j]; + m68k_memory[j] = tmp; + } + } + } + /* Trim off discontiguous bits */ + for (i = 1; i < m68k_num_memory; ++i) { + if ((m68k_memory[i-1].addr + m68k_memory[i-1].size) != + m68k_memory[i].addr) { + printk(KERN_DEBUG "m68k_parse_bootinfo: addr gap between \ + 0x%lx & 0x%lx\n", + m68k_memory[i-1].addr+m68k_memory[i-1].size, + m68k_memory[i].addr); + m68k_num_memory = i; + break; + } + } +} + +int __init uboot_commandline(char *bootargs) +{ + int len = 0, cmd_line_len; + static struct uboot_record uboot_info; + + extern unsigned long uboot_info_stk; + + /* Add 0x80000000 to get post-remapped kernel memory location */ + uboot_info.bd_info = (*(u32 *)(uboot_info_stk)) + 0x80000000; + uboot_info.initrd_start = (*(u32 *)(uboot_info_stk+4)) + 0x80000000; + uboot_info.initrd_end = (*(u32 *)(uboot_info_stk+8)) + 0x80000000; + uboot_info.cmd_line_start = (*(u32 *)(uboot_info_stk+12)) + 0x80000000; + uboot_info.cmd_line_stop = (*(u32 *)(uboot_info_stk+16)) + 0x80000000; + + cmd_line_len = uboot_info.cmd_line_stop - uboot_info.cmd_line_start; + if ((cmd_line_len > 0) && (cmd_line_len < CL_SIZE-1)) + len = (int)strncpy(bootargs, (char *)uboot_info.cmd_line_start,\ + cmd_line_len); + + return len; +} + +/* + * This routine does things not done in the bootloader. + */ +#define DEFAULT_COMMAND_LINE "root=/dev/mtdblock1 rw rootfstype=jffs2 ip=none mtdparts=physmap-flash.0:5M(kernel)ro,-(jffs2)" +asmlinkage void __init cf_early_init(void) +{ + struct bi_record *record = (struct bi_record *) &_end; + + extern char _end; + + SET_VBR((void *)MCF_RAMBAR1); + + /* Mask all interrupts */ + MCF_INTC0_IMRL = 0xFFFFFFFF; + MCF_INTC0_IMRH = 0xFFFFFFFF; + MCF_INTC1_IMRL = 0xFFFFFFFF; + MCF_INTC1_IMRH = 0xFFFFFFFF; + +#if defined(CONFIG_NOR_FLASH_BASE) + MCF_FBCS_CSAR(1) = CONFIG_NOR_FLASH_BASE; +#else + MCF_FBCS_CSAR(1) = 0x00000000; +#endif + +#if CONFIG_SDRAM_SIZE > (256*1024*1024) + /* Init optional SDRAM chip select */ + MCF_SDRAMC_SDCS(1) = (256*1024*1024) | 0x1B; +#endif + + m68k_machtype = MACH_CFMMU; + m68k_fputype = FPU_CFV4E; + m68k_mmutype = MMU_CFV4E; + m68k_cputype = CPU_CFV4E; + + m68k_num_memory = 0; + m68k_memory[m68k_num_memory].addr = CONFIG_SDRAM_BASE; + m68k_memory[m68k_num_memory++].size = CONFIG_SDRAM_SIZE; + + if (!uboot_commandline(m68k_command_line)) { +#if defined(CONFIG_BOOTPARAM) + strncpy(m68k_command_line, CONFIG_BOOTPARAM_STRING, CL_SIZE-1); +#else + strcpy(m68k_command_line, DEFAULT_COMMAND_LINE); +#endif + } + + +#if defined(CONFIG_BLK_DEV_INITRD) + /* add initrd image */ + record = (struct bi_record *) ((void *)record + record->size); + record->tag = BI_RAMDISK; + record->size = sizeof(record->tag) + sizeof(record->size) + + sizeof(record->data[0]) + sizeof(record->data[1]); +#endif + + /* Mark end of tags. */ + record = (struct bi_record *) ((void *) record + record->size); + record->tag = 0; + record->data[0] = 0; + record->data[1] = 0; + record->size = sizeof(record->tag) + sizeof(record->size) + + sizeof(record->data[0]) + sizeof(record->data[1]); + + /* Invalidate caches via CACR */ + cacr_set(CACHE_DISABLE_MODE); + + /* Turn on caches via CACR, enable EUSP */ + cacr_set(CACHE_INITIAL_MODE); +} + +void settimericr(unsigned int timer, unsigned int level) +{ + volatile unsigned char *icrp; + unsigned int icr; + unsigned char irq; + + if (timer <= 2) { + switch (timer) { + case 2: irq = 33; icr = MCFSIM_ICR_TIMER2; break; + default: irq = 32; icr = MCFSIM_ICR_TIMER1; break; + } + + icrp = (volatile unsigned char *) (icr); + *icrp = level; + coldfire_enable_irq0(irq); + } +} + +/* Assembler routines */ +asmlinkage void buserr(void); +asmlinkage void trap(void); +asmlinkage void system_call(void); +asmlinkage void inthandler(void); + +void __init coldfire_trap_init(void) +{ + int i = 0; + e_vector *vectors; + + vectors = (e_vector *)MCF_RAMBAR1; + /* + * There is a common trap handler and common interrupt + * handler that handle almost every vector. We treat + * the system call and bus error special, they get their + * own first level handlers. + */ + for (i = 3; (i <= 23); i++) + vectors[i] = trap; + for (i = 33; (i <= 63); i++) + vectors[i] = trap; + for (i = 24; (i <= 31); i++) + vectors[i] = inthandler; + for (i = 64; (i < 255); i++) + vectors[i] = inthandler; + + vectors[255] = 0; + vectors[2] = buserr; + vectors[32] = system_call; +} + +void coldfire_tick(void) +{ + /* Reset the ColdFire timer */ + __raw_writeb(MCF_DTIM_DTER_CAP | MCF_DTIM_DTER_REF, MCF_DTIM0_DTER); +} + +void __init coldfire_sched_init(irq_handler_t handler) +{ + unsigned int mcf_timerlevel = 5; + unsigned int mcf_timervector = 64+32; + + __raw_writew(MCF_DTIM_DTMR_RST_RST, MCF_DTIM0_DTMR); + __raw_writel(((MCF_BUSCLK / 16) / HZ), MCF_DTIM0_DTRR); + __raw_writew(MCF_DTIM_DTMR_ORRI | MCF_DTIM_DTMR_CLK_DIV16 | + MCF_DTIM_DTMR_FRR | MCF_DTIM_DTMR_RST_EN, \ + MCF_DTIM0_DTMR); + + request_irq(mcf_timervector, handler, SA_INTERRUPT, \ + "timer", (void *)MCF_DTIM0_DTMR); + + settimericr(1, mcf_timerlevel); +} + +int timerirqpending(int timer) +{ + unsigned int imr = 0; + + switch (timer) { + case 1: imr = 0x1; break; + case 2: imr = 0x2; break; + default: break; + } + + return (getiprh() & imr); +} + +unsigned long coldfire_gettimeoffset(void) +{ + volatile unsigned long trr, tcn, offset; + + tcn = __raw_readw(MCF_DTIM0_DTCN); + trr = __raw_readl(MCF_DTIM0_DTRR); + offset = (tcn * (1000000 / HZ)) / trr; + + /* Check if we just wrapped the counters and maybe missed a tick */ + if ((offset < (1000000 / HZ / 2)) && timerirqpending(1)) + offset += 1000000 / HZ; + return offset; +} + +void coldfire_reboot(void) +{ + /* disable interrupts and do a software reset */ + asm("movew #0x2700, %%sr\n\t" + "moveb #0x80, %%d0\n\t" + "moveb %%d0, 0xfc0a0000\n\t" + : : : "%d0"); +} + +/* int coldfire_hwclk(int i, struct rtc_time *t) +{ + printk ("Real time clock needs porting.\n"); + return 0; +}*/ + +static void coldfire_get_model(char *model) +{ + sprintf(model, "Version 4 ColdFire"); +} + +void coldfire_enable_irq(unsigned int vec) +{ + unsigned long flags; + + vec -= 64; + + if (((int)vec < 0) || (vec > 63)) { + printk(KERN_WARNING "enable_irq %d failed\n", vec); + return; + } + + local_irq_save(flags); + irq_enable[vec]++; + if (vec < 32) + MCF_INTC0_IMRL &= ~(1 << vec); + else + MCF_INTC0_IMRH &= ~(1 << (vec - 32)); + local_irq_restore(flags); +} + +void coldfire_disable_irq(unsigned int vec) +{ + unsigned long flags; + + vec -= 64; + + if (((int)vec < 0) || (vec > 63)) { + printk(KERN_WARNING "disable_irq %d failed\n", vec); + return; + } + + local_irq_save(flags); + if (--irq_enable[vec] == 0) { + if (vec < 32) + MCF_INTC0_IMRL |= (1 << vec); + else + MCF_INTC0_IMRH |= (1 << (vec - 32)); + + } + local_irq_restore(flags); +} + +static void __init +coldfire_bootmem_alloc(unsigned long memory_start, unsigned long memory_end) +{ + unsigned long base_pfn; + + /* compute total pages in system */ + num_pages = PAGE_ALIGN(memory_end - PAGE_OFFSET) >> PAGE_SHIFT; + + /* align start/end to page boundries */ + memory_start = PAGE_ALIGN(memory_start); + memory_end = memory_end & PAGE_MASK; + + /* page numbers */ + base_pfn = __pa(PAGE_OFFSET) >> PAGE_SHIFT; + min_low_pfn = __pa(memory_start) >> PAGE_SHIFT; + max_low_pfn = __pa(memory_end) >> PAGE_SHIFT; + + high_memory = (void *)memory_end; + availmem = memory_start; + + /* setup bootmem data */ + m68k_setup_node(0); + availmem += init_bootmem_node(NODE_DATA(0), min_low_pfn, + base_pfn, max_low_pfn); + availmem = PAGE_ALIGN(availmem); + free_bootmem(__pa(availmem), memory_end - (availmem)); +} + +void __init config_coldfire(void) +{ + unsigned long endmem, startmem; + int i; + + /* + * Calculate endmem from m68k_memory, assume all are contiguous + */ + startmem = ((((int) &_end) + (PAGE_SIZE - 1)) & PAGE_MASK); + endmem = PAGE_OFFSET; + for (i = 0; i < m68k_num_memory; ++i) + endmem += m68k_memory[i].size; + + printk(KERN_INFO "starting up linux startmem 0x%lx, endmem 0x%lx, \ + size %luMB\n", startmem, endmem, (endmem - startmem) >> 20); + + memset(irq_enable, 0, sizeof(irq_enable)); + + /* + * Setup coldfire mach-specific handlers + */ + mach_max_dma_address = 0xffffffff; + mach_sched_init = coldfire_sched_init; + mach_tick = coldfire_tick; + mach_gettimeoffset = coldfire_gettimeoffset; + mach_reset = coldfire_reboot; +/* mach_hwclk = coldfire_hwclk; to be done */ + mach_get_model = coldfire_get_model; + + coldfire_bootmem_alloc(startmem, endmem); + + /* + * initrd setup + */ +/* #ifdef CONFIG_BLK_DEV_INITRD + if (m68k_ramdisk.size) { + reserve_bootmem (__pa(m68k_ramdisk.addr), m68k_ramdisk.size); + initrd_start = (unsigned long) m68k_ramdisk.addr; + initrd_end = initrd_start + m68k_ramdisk.size; + printk (KERN_DEBUG "initrd: %08lx - %08lx\n", initrd_start, + initrd_end); + } +#endif */ + +#if defined(CONFIG_DUMMY_CONSOLE) || defined(CONFIG_FRAMEBUFFER_CONSOLE) + conswitchp = &dummy_con; +#endif + +#if defined(CONFIG_SERIAL_COLDFIRE) + /* + * This causes trouble when it is re-registered later. + * Currently this is fixed by conditionally commenting + * out the register_console in mcf_serial.c + */ + register_console(&mcfrs_console); +#endif +} --- /dev/null +++ b/arch/m68k/coldfire/entry.S @@ -0,0 +1,701 @@ +/* + * arch/m68k/coldfire/entry.S + * + * Copyright (C) 1999-2002, Greg Ungerer (gerg@snapgear.com) + * Copyright (C) 1998 D. Jeff Dionne <jeff@lineo.ca>, + * Kenneth Albanowski <kjahds@kjahds.com>, + * Copyright (C) 2000 Lineo Inc. (www.lineo.com) + * Copyright (C) 2004-2006 Macq Electronique SA. (www.macqel.com) + * Matt Waddel Matt.Waddel@freescale.com + * Kurt Mahan kmahan@freescale.com + * Copyright Freescale Semiconductor, Inc. 2007 + * + * Based on: + * + * arch/m68knommu/platform/5307/entry.S & + * arch/m68k/kernel/entry.S + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file README.legal in the main directory of this archive + * for more details. + * + * Linux/m68k support by Hamish Macdonald + * + * ColdFire support by Greg Ungerer (gerg@snapgear.com) + * 5307 fixes by David W. Miller + * linux 2.4 support David McCullough <davidm@snapgear.com> + * Bug, speed and maintainability fixes by Philippe De Muyter <phdm@macqel.be> + * Ported to mmu Coldfire by Matt Waddel + */ + +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/cf_entry.h> +#include <asm/errno.h> +#include <asm/setup.h> +#include <asm/segment.h> +#include <asm/traps.h> +#include <asm/unistd.h> + +/* + * TASK_INFO: + * + * - TINFO_PREEMPT (struct thread_info / preempt_count) + * Used to keep track of preemptability + * - TINFO_FLAGS (struct thread_info / flags - include/asm-m68k/thread_info.h) + * Various bit flags that are checked for scheduling/tracing + * Bits 0-7 are checked every exception exit + * 8-15 are checked every syscall exit + * + * TIF_SIGPENDING 6 + * TIF_NEED_RESCHED 7 + * TIF_DELAYED_TRACE 14 + * TIF_SYSCALL_TRACE 15 + * TIF_MEMDIE 16 (never checked here) + */ + +.bss + +sw_ksp: +.long 0 + +sw_usp: +.long 0 + +.text + +.globl system_call +.globl buserr +.globl trap +.globl resume +.globl ret_from_exception +.globl ret_from_signal +.globl sys_call_table +.globl ret_from_interrupt +.globl inthandler + +ENTRY(buserr) + SAVE_ALL_INT + GET_CURRENT(%d0) + movel %sp,%sp@- /* stack frame pointer argument */ + jsr buserr_c + addql #4,%sp + jra .Lret_from_exception + +ENTRY(trap) + SAVE_ALL_INT + GET_CURRENT(%d0) + movel %sp,%sp@- /* stack frame pointer argument */ + jsr trap_c + addql #4,%sp + jra .Lret_from_exception + + /* After a fork we jump here directly from resume, + %d1 contains the previous task schedule_tail */ +ENTRY(ret_from_fork) + movel %d1,%sp@- + jsr schedule_tail + addql #4,%sp + jra .Lret_from_exception + +do_trace_entry: + movel #-ENOSYS,%d1 /* needed for strace */ + movel %d1,%sp@(PT_D0) + subql #4,%sp + SAVE_SWITCH_STACK + jbsr syscall_trace + RESTORE_SWITCH_STACK + addql #4,%sp + movel %sp@(PT_ORIG_D0),%d0 + cmpl #NR_syscalls,%d0 + jcs syscall +badsys: + movel #-ENOSYS,%d1 + movel %d1,%sp@(PT_D0) + jra ret_from_exception + +do_trace_exit: + subql #4,%sp + SAVE_SWITCH_STACK + jbsr syscall_trace + RESTORE_SWITCH_STACK + addql #4,%sp + jra .Lret_from_exception + +ENTRY(ret_from_signal) + RESTORE_SWITCH_STACK + addql #4,%sp + jra .Lret_from_exception + +ENTRY(system_call) + SAVE_ALL_SYS + + GET_CURRENT(%d1) + /* save top of frame */ + movel %sp,%curptr@(TASK_THREAD+THREAD_ESP0) + + /* syscall trace */ + tstb %curptr@(TASK_INFO+TINFO_FLAGS+2) + jmi do_trace_entry /* SYSCALL_TRACE is set */ + cmpl #NR_syscalls,%d0 + jcc badsys +syscall: + movel #sys_call_table,%a0 + asll #2,%d0 + addl %d0,%a0 + movel %a0@,%a0 + jsr %a0@ + movel %d0,%sp@(PT_D0) /* save the return value */ +ret_from_syscall: + movew %curptr@(TASK_INFO+TINFO_FLAGS+2),%d0 + jne syscall_exit_work /* flags set so process */ +1: RESTORE_ALL + +syscall_exit_work: + btst #5,%sp@(PT_SR) /* check if returning to kernel */ + bnes 1b /* if so, skip resched, signals */ + + btstl #15,%d0 /* check if SYSCALL_TRACE */ + jne do_trace_exit + btstl #14,%d0 /* check if DELAYED_TRACE */ + jne do_delayed_trace + btstl #6,%d0 /* check if SIGPENDING */ + jne do_signal_return + pea resume_userspace + jra schedule + +ENTRY(ret_from_exception) +.Lret_from_exception: + btst #5,%sp@(PT_SR) /* check if returning to kernel */ + bnes 1f /* if so, skip resched, signals */ + movel %d0,%sp@- /* Only allow interrupts when we are */ + move %sr,%d0 /* last one on the kernel stack, */ + andl #ALLOWINT,%d0 /* otherwise stack overflow can occur */ + move %d0,%sr /* during heavy interrupt load. */ + movel %sp@+,%d0 + +resume_userspace: + moveb %curptr@(TASK_INFO+TINFO_FLAGS+3),%d0 + jne exit_work /* SIGPENDING and/or NEED_RESCHED set */ +1: RESTORE_ALL + +exit_work: + /* save top of frame */ + movel %sp,%curptr@(TASK_THREAD+THREAD_ESP0) + btstl #6,%d0 /* check for SIGPENDING in flags */ + jne do_signal_return + pea resume_userspace + jra schedule + +do_signal_return: + subql #4,%sp /* dummy return address */ + SAVE_SWITCH_STACK + pea %sp@(SWITCH_STACK_SIZE) + clrl %sp@- + bsrl do_signal + addql #8,%sp + RESTORE_SWITCH_STACK + addql #4,%sp + jbra resume_userspace + +do_delayed_trace: + bclr #7,%sp@(PT_SR) /* clear trace bit in SR */ + pea 1 /* send SIGTRAP */ + movel %curptr,%sp@- + pea LSIGTRAP + jbsr send_sig + addql #8,%sp + addql #4,%sp + jbra resume_userspace + +/* + * This is the interrupt handler (for all hardware interrupt + * sources). It figures out the vector number and calls the appropriate + * interrupt service routine directly. + */ +ENTRY(inthandler) + SAVE_ALL_INT + GET_CURRENT(%d0) + addql #1,%curptr@(TASK_INFO+TINFO_PREEMPT) + /* put exception # in d0 */ + movel %sp@(PT_VECTOR),%d0 + swap %d0 /* extract bits 25:18 */ + lsrl #2,%d0 + andl #0x0ff,%d0 + + movel %sp,%sp@- + movel %d0,%sp@- /* put vector # on stack */ +auto_irqhandler_fixup = . + 2 + jbsr process_int /* process the IRQ */ + addql #8,%sp /* pop parameters off stack */ + +ENTRY(ret_from_interrupt) +ret_from_interrupt: + + subql #1,%curptr@(TASK_INFO+TINFO_PREEMPT) + jeq ret_from_last_interrupt +2: RESTORE_ALL + + ALIGN +ret_from_last_interrupt: + moveb %sp@(PT_SR),%d0 + andl #(~ALLOWINT>>8)&0xff,%d0 + jne 2b + + /* check if we need to do software interrupts */ + tstl irq_stat+CPUSTAT_SOFTIRQ_PENDING + jeq .Lret_from_exception + pea ret_from_exception + jra do_softirq + +ENTRY(user_inthandler) + SAVE_ALL_INT + GET_CURRENT(%d0) + addql #1,%curptr@(TASK_INFO+TINFO_PREEMPT) + /* put exception # in d0 */ + movel %sp@(PT_VECTOR),%d0 +user_irqvec_fixup = . + 2 + swap %d0 /* extract bits 25:18 */ + lsrl #2,%d0 + andl #0x0ff,%d0 + + movel %sp,%sp@- + movel %d0,%sp@- /* put vector # on stack */ +user_irqhandler_fixup = . + 2 + jbsr process_int /* process the IRQ */ + addql #8,%sp /* pop parameters off stack */ + + subql #1,%curptr@(TASK_INFO+TINFO_PREEMPT) + jeq ret_from_last_interrupt + RESTORE_ALL + +/* Handler for uninitialized and spurious interrupts */ + +ENTRY(bad_inthandler) + SAVE_ALL_INT + GET_CURRENT(%d0) + addql #1,%curptr@(TASK_INFO+TINFO_PREEMPT) + + movel %sp,%sp@- + jsr handle_badint + addql #4,%sp + + subql #1,%curptr@(TASK_INFO+TINFO_PREEMPT) + jeq ret_from_last_interrupt + RESTORE_ALL + +ENTRY(sys_fork) + SAVE_SWITCH_STACK + pea %sp@(SWITCH_STACK_SIZE) + jbsr m68k_fork + addql #4,%sp + RESTORE_SWITCH_STACK + rts + +ENTRY(sys_clone) + SAVE_SWITCH_STACK + pea %sp@(SWITCH_STACK_SIZE) + jbsr m68k_clone + addql #4,%sp + RESTORE_SWITCH_STACK + rts + +ENTRY(sys_vfork) + SAVE_SWITCH_STACK + pea %sp@(SWITCH_STACK_SIZE) + jbsr m68k_vfork + addql #4,%sp + RESTORE_SWITCH_STACK + rts + +ENTRY(sys_sigsuspend) + SAVE_SWITCH_STACK + pea %sp@(SWITCH_STACK_SIZE) + jbsr do_sigsuspend + addql #4,%sp + RESTORE_SWITCH_STACK + rts + +ENTRY(sys_rt_sigsuspend) + SAVE_SWITCH_STACK + pea %sp@(SWITCH_STACK_SIZE) + jbsr do_rt_sigsuspend + addql #4,%sp + RESTORE_SWITCH_STACK + rts + +ENTRY(sys_sigreturn) + SAVE_SWITCH_STACK + jbsr do_sigreturn + RESTORE_SWITCH_STACK + rts + +ENTRY(sys_rt_sigreturn) + SAVE_SWITCH_STACK + jbsr do_rt_sigreturn + RESTORE_SWITCH_STACK + rts + +resume: + /* + * Beware - when entering resume, prev (the current task) is + * in a0, next (the new task) is in a1,so don't change these + * registers until their contents are no longer needed. + */ + + /* save sr */ + movew %sr,%d0 + movew %d0,%a0@(TASK_THREAD+THREAD_SR) + + /* save usp */ + /* Save USP via %a1 (which is saved/restored from %d0) */ + movel %a1,%d0 + movel %usp,%a1 + movel %a1,%a0@(TASK_THREAD+THREAD_USP) + movel %d0,%a1 + + /* save non-scratch registers on stack */ + SAVE_SWITCH_STACK + + /* save current kernel stack pointer */ + movel %sp,%a0@(TASK_THREAD+THREAD_KSP) + + /* Return previous task in %d1 */ + movel %curptr,%d1 + + /* switch to new task (a1 contains new task) */ + movel %a1,%curptr + + /* restore the kernel stack pointer */ + movel %a1@(TASK_THREAD+THREAD_KSP),%sp + + /* restore non-scratch registers */ + RESTORE_SWITCH_STACK + + /* restore user stack pointer */ + movel %a1@(TASK_THREAD+THREAD_USP),%a0 + movel %a0,%usp + + /* restore status register */ + movew %a1@(TASK_THREAD+THREAD_SR),%d0 + movew %d0,%sr + + rts + +.data +ALIGN +sys_call_table: + .long sys_ni_syscall /* 0 - old "setup()" system call*/ + .long sys_exit + .long sys_fork + .long sys_read + .long sys_write + .long sys_open /* 5 */ + .long sys_close + .long sys_waitpid + .long sys_creat + .long sys_link + .long sys_unlink /* 10 */ + .long sys_execve + .long sys_chdir + .long sys_time + .long sys_mknod + .long sys_chmod /* 15 */ + .long sys_chown16 + .long sys_ni_syscall /* old break syscall holder */ + .long sys_stat + .long sys_lseek + .long sys_getpid /* 20 */ + .long sys_mount + .long sys_oldumount + .long sys_setuid16 + .long sys_getuid16 + .long sys_stime /* 25 */ + .long sys_ptrace + .long sys_alarm + .long sys_fstat + .long sys_pause + .long sys_utime /* 30 */ + .long sys_ni_syscall /* old stty syscall holder */ + .long sys_ni_syscall /* old gtty syscall holder */ + .long sys_access + .long sys_nice + .long sys_ni_syscall /* 35 */ /* old ftime syscall holder */ + .long sys_sync + .long sys_kill + .long sys_rename + .long sys_mkdir + .long sys_rmdir /* 40 */ + .long sys_dup + .long sys_pipe + .long sys_times + .long sys_ni_syscall /* old prof syscall holder */ + .long sys_brk /* 45 */ + .long sys_setgid16 + .long sys_getgid16 + .long sys_signal + .long sys_geteuid16 + .long sys_getegid16 /* 50 */ + .long sys_acct + .long sys_umount /* recycled never used phys() */ + .long sys_ni_syscall /* old lock syscall holder */ + .long sys_ioctl + .long sys_fcntl /* 55 */ + .long sys_ni_syscall /* old mpx syscall holder */ + .long sys_setpgid + .long sys_ni_syscall /* old ulimit syscall holder */ + .long sys_ni_syscall + .long sys_umask /* 60 */ + .long sys_chroot + .long sys_ustat + .long sys_dup2 + .long sys_getppid + .long sys_getpgrp /* 65 */ + .long sys_setsid + .long sys_sigaction + .long sys_sgetmask + .long sys_ssetmask + .long sys_setreuid16 /* 70 */ + .long sys_setregid16 + .long sys_sigsuspend + .long sys_sigpending + .long sys_sethostname + .long sys_setrlimit /* 75 */ + .long sys_old_getrlimit + .long sys_getrusage + .long sys_gettimeofday + .long sys_settimeofday + .long sys_getgroups16 /* 80 */ + .long sys_setgroups16 + .long old_select + .long sys_symlink + .long sys_lstat + .long sys_readlink /* 85 */ + .long sys_uselib + .long sys_swapon + .long sys_reboot + .long old_readdir + .long old_mmap /* 90 */ + .long sys_munmap + .long sys_truncate + .long sys_ftruncate + .long sys_fchmod + .long sys_fchown16 /* 95 */ + .long sys_getpriority + .long sys_setpriority + .long sys_ni_syscall /* old profil syscall holder */ + .long sys_statfs + .long sys_fstatfs /* 100 */ + .long sys_ni_syscall /* ioperm for i386 */ + .long sys_socketcall + .long sys_syslog + .long sys_setitimer + .long sys_getitimer /* 105 */ + .long sys_newstat + .long sys_newlstat + .long sys_newfstat + .long sys_ni_syscall + .long sys_ni_syscall /* 110 */ /* iopl for i386 */ + .long sys_vhangup + .long sys_ni_syscall /* obsolete idle() syscall */ + .long sys_ni_syscall /* vm86old for i386 */ + .long sys_wait4 + .long sys_swapoff /* 115 */ + .long sys_sysinfo + .long sys_ipc + .long sys_fsync + .long sys_sigreturn + .long sys_clone /* 120 */ + .long sys_setdomainname + .long sys_newuname + .long sys_cacheflush /* modify_ldt for i386 */ + .long sys_adjtimex + .long sys_mprotect /* 125 */ + .long sys_sigprocmask + .long sys_ni_syscall /* old "create_module" */ + .long sys_init_module + .long sys_delete_module + .long sys_ni_syscall /* 130 - old "get_kernel_syms" */ + .long sys_quotactl + .long sys_getpgid + .long sys_fchdir + .long sys_bdflush + .long sys_sysfs /* 135 */ + .long sys_personality + .long sys_ni_syscall /* for afs_syscall */ + .long sys_setfsuid16 + .long sys_setfsgid16 + .long sys_llseek /* 140 */ + .long sys_getdents + .long sys_select + .long sys_flock + .long sys_msync + .long sys_readv /* 145 */ + .long sys_writev + .long sys_getsid + .long sys_fdatasync + .long sys_sysctl + .long sys_mlock /* 150 */ + .long sys_munlock + .long sys_mlockall + .long sys_munlockall + .long sys_sched_setparam + .long sys_sched_getparam /* 155 */ + .long sys_sched_setscheduler + .long sys_sched_getscheduler + .long sys_sched_yield + .long sys_sched_get_priority_max + .long sys_sched_get_priority_min /* 160 */ + .long sys_sched_rr_get_interval + .long sys_nanosleep + .long sys_mremap + .long sys_setresuid16 + .long sys_getresuid16 /* 165 */ + .long sys_getpagesize + .long sys_ni_syscall /* old sys_query_module */ + .long sys_poll + .long sys_nfsservctl + .long sys_setresgid16 /* 170 */ + .long sys_getresgid16 + .long sys_prctl + .long sys_rt_sigreturn + .long sys_rt_sigaction + .long sys_rt_sigprocmask /* 175 */ + .long sys_rt_sigpending + .long sys_rt_sigtimedwait + .long sys_rt_sigqueueinfo + .long sys_rt_sigsuspend + .long sys_pread64 /* 180 */ + .long sys_pwrite64 + .long sys_lchown16; + .long sys_getcwd + .long sys_capget + .long sys_capset /* 185 */ + .long sys_sigaltstack + .long sys_sendfile + .long sys_ni_syscall /* streams1 */ + .long sys_ni_syscall /* streams2 */ + .long sys_vfork /* 190 */ + .long sys_getrlimit + .long sys_mmap2 + .long sys_truncate64 + .long sys_ftruncate64 + .long sys_stat64 /* 195 */ + .long sys_lstat64 + .long sys_fstat64 + .long sys_chown + .long sys_getuid + .long sys_getgid /* 200 */ + .long sys_geteuid + .long sys_getegid + .long sys_setreuid + .long sys_setregid + .long sys_getgroups /* 205 */ + .long sys_setgroups + .long sys_fchown + .long sys_setresuid + .long sys_getresuid + .long sys_setresgid /* 210 */ + .long sys_getresgid + .long sys_lchown + .long sys_setuid + .long sys_setgid + .long sys_setfsuid /* 215 */ + .long sys_setfsgid + .long sys_pivot_root + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_getdents64 /* 220 */ + .long sys_gettid + .long sys_tkill + .long sys_setxattr + .long sys_lsetxattr + .long sys_fsetxattr /* 225 */ + .long sys_getxattr + .long sys_lgetxattr + .long sys_fgetxattr + .long sys_listxattr + .long sys_llistxattr /* 230 */ + .long sys_flistxattr + .long sys_removexattr + .long sys_lremovexattr + .long sys_fremovexattr + .long sys_futex /* 235 */ + .long sys_sendfile64 + .long sys_mincore + .long sys_madvise + .long sys_fcntl64 + .long sys_readahead /* 240 */ + .long sys_io_setup + .long sys_io_destroy + .long sys_io_getevents + .long sys_io_submit + .long sys_io_cancel /* 245 */ + .long sys_fadvise64 + .long sys_exit_group + .long sys_lookup_dcookie + .long sys_epoll_create + .long sys_epoll_ctl /* 250 */ + .long sys_epoll_wait + .long sys_remap_file_pages + .long sys_set_tid_address + .long sys_timer_create + .long sys_timer_settime /* 255 */ + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime + .long sys_clock_gettime /* 260 */ + .long sys_clock_getres + .long sys_clock_nanosleep + .long sys_statfs64 + .long sys_fstatfs64 + .long sys_tgkill /* 265 */ + .long sys_utimes + .long sys_fadvise64_64 + .long sys_mbind + .long sys_get_mempolicy + .long sys_set_mempolicy /* 270 */ + .long sys_mq_open + .long sys_mq_unlink + .long sys_mq_timedsend + .long sys_mq_timedreceive + .long sys_mq_notify /* 275 */ + .long sys_mq_getsetattr + .long sys_waitid + .long sys_ni_syscall /* for sys_vserver */ + .long sys_add_key + .long sys_request_key /* 280 */ + .long sys_keyctl + .long sys_ioprio_set + .long sys_ioprio_get + .long sys_inotify_init + .long sys_inotify_add_watch /* 285 */ + .long sys_inotify_rm_watch + .long sys_migrate_pages + .long sys_openat + .long sys_mkdirat + .long sys_mknodat /* 290 */ + .long sys_fchownat + .long sys_futimesat + .long sys_fstatat64 + .long sys_unlinkat + .long sys_renameat /* 295 */ + .long sys_linkat + .long sys_symlinkat + .long sys_readlinkat + .long sys_fchmodat + .long sys_faccessat /* 300 */ + .long sys_ni_syscall /* Reserved for pselect6 */ + .long sys_ni_syscall /* Reserved for ppoll */ + .long sys_unshare + .long sys_set_robust_list + .long sys_get_robust_list /* 305 */ + .long sys_splice + .long sys_sync_file_range + .long sys_tee + .long sys_vmsplice + .long sys_move_pages /* 310 */ + --- /dev/null +++ b/arch/m68k/coldfire/head.S @@ -0,0 +1,474 @@ +/* + * head.S is the MMU enabled ColdFire specific initial boot code + * + * Ported to ColdFire by + * Matt Waddel Matt.Waddel@freescale.com + * Kurt Mahan kmahan@freescale.com + * Copyright Freescale Semiconductor, Inc. 2007 + * + * 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. + * + * Parts of this code came from arch/m68k/kernel/head.S + */ +#include <linux/linkage.h> +#include <linux/init.h> +#include <asm/bootinfo.h> +#include <asm/setup.h> +#include <asm/entry.h> +#include <asm/pgtable.h> +#include <asm/page.h> +#include <asm/coldfire.h> +#include <asm/mcfuart.h> +#include <asm/cfcache.h> + +#define DEBUG + +.globl kernel_pg_dir +.globl availmem +.globl set_context +.globl set_fpga + +#ifdef DEBUG +/* When debugging use readable names for labels */ +#ifdef __STDC__ +#define L(name) .head.S.##name +#else +#define L(name) .head.S./**/name +#endif +#else +#ifdef __STDC__ +#define L(name) .L##name +#else +#define L(name) .L/**/name +#endif +#endif + +/* The __INITDATA stuff is a no-op when ftrace or kgdb are turned on */ +#ifndef __INITDATA +#define __INITDATA .data +#define __FINIT .previous +#endif + +/* + * Setup ACR mappings to provide the following memory map: + * Data + * 0xA0000000 -> 0xAFFFFFFF [0] NO CACHE / PRECISE / SUPER ONLY + * 0xFC000000 -> 0xFCFFFFFF [1] NO CACHE / PRECISE / SUPER ONLY + * Code + * None currently (mapped via TLBs) + */ + +#define ACR0_DEFAULT #0xA00FA048 /* ACR0 default value */ +#define ACR1_DEFAULT #0xFC00A040 /* ACR1 default value */ +#define ACR2_DEFAULT #0x00000000 /* ACR2 default value */ +#define ACR3_DEFAULT #0x00000000 /* ACR3 default value */ + +/* ACR mapping for FPGA (maps 0) */ +#define ACR0_FPGA #0x000FA048 /* ACR0 enable FPGA */ + +/* Several macros to make the writing of subroutines easier: + * - func_start marks the beginning of the routine which setups the frame + * register and saves the registers, it also defines another macro + * to automatically restore the registers again. + * - func_return marks the end of the routine and simply calls the prepared + * macro to restore registers and jump back to the caller. + * - func_define generates another macro to automatically put arguments + * onto the stack call the subroutine and cleanup the stack again. + */ + +.macro load_symbol_address symbol,register + movel #\symbol,\register +.endm + +.macro func_start name,saveregs,savesize,stack=0 +L(\name): + linkw %a6,#-\stack + subal #(\savesize),%sp + moveml \saveregs,%sp@ +.set stackstart,-\stack + +.macro func_return_\name + moveml %sp@,\saveregs + addal #(\savesize),%sp + unlk %a6 + rts +.endm +.endm + +.macro func_return name + func_return_\name +.endm + +.macro func_call name + jbsr L(\name) +.endm + +.macro move_stack nr,arg1,arg2,arg3,arg4 +.if \nr + move_stack "(\nr-1)",\arg2,\arg3,\arg4 + movel \arg1,%sp@- +.endif +.endm + +.macro func_define name,nr=0 +.macro \name arg1,arg2,arg3,arg4 + move_stack \nr,\arg1,\arg2,\arg3,\arg4 + func_call \name +.if \nr + lea %sp@(\nr*4),%sp +.endif +.endm +.endm + +func_define serial_putc,1 + +.macro putc ch + pea \ch + func_call serial_putc + addql #4,%sp +.endm + +.macro dputc ch +#ifdef DEBUG + putc \ch +#endif +.endm + +func_define putn,1 + +.macro dputn nr +#ifdef DEBUG + putn \nr +#endif +.endm + +/* + mmu_map - creates a new TLB entry + + virt_addr Must be on proper boundary + phys_addr Must be on proper boundary + itlb MMUOR_ITLB if instruction TLB or 0 + asid address space ID + shared_global MMUTR_SG if shared between different ASIDs or 0 + size_code MMUDR_SZ1M 1 MB + MMUDR_SZ4K 4 KB + MMUDR_SZ8K 8 KB + MMUDR_SZ16M 16 MB + cache_mode MMUDR_INC instruction non-cacheable + MMUDR_IC instruction cacheable + MMUDR_DWT data writethrough + MMUDR_DCB data copyback + MMUDR_DNCP data non-cacheable, precise + MMUDR_DNCIP data non-cacheable, imprecise + super_prot MMUDR_SP if user mode generates exception or 0 + readable MMUDR_R if permits read access (data TLB) or 0 + writable MMUDR_W if permits write access (data TLB) or 0 + executable MMUDR_X if permits execute access (instruction TLB) or 0 + locked MMUDR_LK prevents TLB entry from being replaced or 0 + temp_data_reg a data register to use for temporary values +*/ +.macro mmu_map virt_addr,phys_addr,itlb,asid,shared_global,size_code,cache_mode,super_prot,readable,writable,executable,locked,temp_data_reg + /* Set up search of TLB. */ + movel #(\virt_addr+1), \temp_data_reg + movel \temp_data_reg, MMUAR + /* Search. */ + movel #(MMUOR_STLB + MMUOR_ADR +\itlb), \temp_data_reg + movew \temp_data_reg, (MMUOR) + /* Set up tag value. */ + movel #(\virt_addr + \asid + \shared_global + MMUTR_V), \temp_data_reg + movel \temp_data_reg, MMUTR + /* Set up data value. */ + movel #(\phys_addr + \size_code + \cache_mode + \super_prot + \readable + \writable + \executable + \locked), \temp_data_reg + movel \temp_data_reg, MMUDR + /* Save it. */ + movel #(MMUOR_ACC + MMUOR_UAA + \itlb), \temp_data_reg + movew \temp_data_reg, (MMUOR) +.endm /* mmu_map */ + +.macro mmu_unmap virt_addr,itlb,temp_data_reg + /* Set up search of TLB. */ + movel #(\virt_addr+1), \temp_data_reg + movel \temp_data_reg, MMUAR + /* Search. */ + movel #(MMUOR_STLB + MMUOR_ADR +\itlb), \temp_data_reg + movew \temp_data_reg, (MMUOR) + /* Test for hit. */ + movel MMUSR,\temp_data_reg + btst #MMUSR_HITN,\temp_data_reg + beq 1f + /* Read the TLB. */ + movel #(MMUOR_RW + MMUOR_ACC +\itlb), \temp_data_reg + movew \temp_data_reg, (MMUOR) + movel MMUSR,\temp_data_reg + /* Set up tag value. */ + movel #0, \temp_data_reg + movel \temp_data_reg, MMUTR + /* Set up data value. */ + movel #0, \temp_data_reg + movel \temp_data_reg, MMUDR + /* Save it. */ + movel #(MMUOR_ACC + MMUOR_UAA + \itlb), \temp_data_reg + movew \temp_data_reg, (MMUOR) +1: +.endm /* mmu_unmap */ + +/* .text */ +.section ".text.head","ax" +ENTRY(_stext) +/* Version numbers of the bootinfo interface -- if we later pass info + * from boot ROM we might want to put something real here. + * + * The area from _stext to _start will later be used as kernel pointer table + */ + bras 1f /* Jump over bootinfo version numbers */ + + .long BOOTINFOV_MAGIC + .long 0 +1: jmp __start-0x80000000 + +.equ kernel_pg_dir,_stext +.equ .,_stext+0x1000 + +ENTRY(_start) + jra __start +__INIT +ENTRY(__start) + +/* Save the location of u-boot info - cmd line, bd_info, etc. */ + movel %a7,%a4 /* Don't use %a4 before cf_early_init */ + addl #0x80000004,%a4 /* 0x80000004= 1 stack push + high mem offset */ + +/* Setup initial stack pointer */ + movel #0x40001000,%sp + +/* Clear usp */ + subl %a0,%a0 + movel %a0,%usp + + movel #(MCF_RAMBAR1 + 0x221), %d0 + movec %d0, %rambar1 + movew #0x2700,%sr + + movel #(MMU_BASE+1),%d0 + movecl %d0,%mmubar + movel #MMUOR_CA,%a0 /* Clear tlb entries */ + movew %a0,(MMUOR) + movel #(MMUOR_CA + MMUOR_ITLB),%a0 /* Use ITLB for searches */ + movew %a0,(MMUOR) + movel #0,%a0 /* Clear Addr Space User ID */ + movecl %a0,%asid + +/* setup ACRs */ + movel ACR0_DEFAULT, %d0 /* ACR0 (DATA) setup */ + movec %d0, %acr0 + movel ACR1_DEFAULT, %d0 /* ACR1 (DATA) setup */ + movec %d0, %acr1 + movel ACR2_DEFAULT, %d0 /* ACR2 (CODE) setup */ + movec %d0, %acr2 + movel ACR3_DEFAULT, %d0 /* ACR3 (CODE) setup */ + movec %d0, %acr3 + + /* If you change the memory size to another value make a matching + change in paging_init(cf-mmu.c) to zones_size[]. */ + + /* Map 256MB as code */ + mmu_map (PAGE_OFFSET+0*0x1000000), (PHYS_OFFSET+0*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+1*0x1000000), (PHYS_OFFSET+1*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+2*0x1000000), (PHYS_OFFSET+2*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+3*0x1000000), (PHYS_OFFSET+3*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+4*0x1000000), (PHYS_OFFSET+4*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+5*0x1000000), (PHYS_OFFSET+5*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+6*0x1000000), (PHYS_OFFSET+6*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+7*0x1000000), (PHYS_OFFSET+7*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+8*0x1000000), (PHYS_OFFSET+8*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+9*0x1000000), (PHYS_OFFSET+9*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+10*0x1000000), (PHYS_OFFSET+10*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+11*0x1000000), (PHYS_OFFSET+11*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+12*0x1000000), (PHYS_OFFSET+12*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+13*0x1000000), (PHYS_OFFSET+13*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+14*0x1000000), (PHYS_OFFSET+14*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+15*0x1000000), (PHYS_OFFSET+15*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_IC, MMUDR_SP, \ + 0, 0, MMUDR_X, MMUDR_LK, %d0 + + /* Map 256MB as data also */ + mmu_map (PAGE_OFFSET+0*0x1000000), (PHYS_OFFSET+0*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+1*0x1000000), (PHYS_OFFSET+1*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+2*0x1000000), (PHYS_OFFSET+2*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+3*0x1000000), (PHYS_OFFSET+3*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+4*0x1000000), (PHYS_OFFSET+4*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+5*0x1000000), (PHYS_OFFSET+5*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+6*0x1000000), (PHYS_OFFSET+6*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+7*0x1000000), (PHYS_OFFSET+7*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+8*0x1000000), (PHYS_OFFSET+8*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+9*0x1000000), (PHYS_OFFSET+9*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+10*0x1000000), (PHYS_OFFSET+10*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+11*0x1000000), (PHYS_OFFSET+11*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+12*0x1000000), (PHYS_OFFSET+12*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+13*0x1000000), (PHYS_OFFSET+13*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+14*0x1000000), (PHYS_OFFSET+14*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + mmu_map (PAGE_OFFSET+15*0x1000000), (PHYS_OFFSET+15*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, MMUDR_LK, %d0 + + /* Do unity mapping to enable the MMU. Map first 16 MB in place as + code (delete TLBs after MMU is enabled and we are executing in high + memory). */ + mmu_map (PHYS_OFFSET+0*0x1000000), (PHYS_OFFSET+0*0x1000000), \ + MMUOR_ITLB, 0, MMUTR_SG, MMUDR_SZ16M, MMUDR_INC, MMUDR_SP, 0, \ + 0, MMUDR_X, 0, %d0 + /* Map first 16 MB as data too. */ + mmu_map (PHYS_OFFSET+0*0x1000000), (PHYS_OFFSET+0*0x1000000), 0, 0, \ + MMUTR_SG, MMUDR_SZ16M, MMUDR_DNCP, MMUDR_SP, MMUDR_R, MMUDR_W, \ + 0, 0, %d0 + + /* Turn on MMU */ + movel #(MMUCR_EN),%a0 + movel %a0,MMUCR + nop /* This synchs the pipeline after a write to MMUCR */ + + movel #__running_high,%a0 /* Get around PC-relative addressing. */ + jmp %a0@ + +ENTRY(__running_high) + load_symbol_address _stext,%sp + movel L(memory_start),%a0 + movel %a0,availmem + load_symbol_address L(phys_kernel_start),%a0 + load_symbol_address _stext,%a1 + subl #_stext,%a1 + addl #PAGE_OFFSET,%a1 + movel %a1,%a0@ + + /* Unmap first 16 MB, code and data. */ + mmu_unmap (PHYS_OFFSET+0*0x1000000), MMUOR_ITLB, %d0 + mmu_unmap (PHYS_OFFSET+0*0x1000000), 0, %d0 + +/* Setup initial stack pointer */ + lea init_task,%a2 + lea init_thread_union+THREAD_SIZE,%sp + subl %a6,%a6 /* clear a6 for gdb */ + +#ifdef CONFIG_MCF_USER_HALT +/* Setup debug control reg to allow halts from user space */ + lea wdbg_uhe,%a0 + wdebug (%a0) +#endif + + movel %a4,uboot_info_stk /* save uboot info to variable */ + jsr cf_early_init + jmp start_kernel + +.section ".text.head","ax" +set_context: +func_start set_context,%d0,(1*4) + movel 12(%sp),%d0 + movec %d0,%asid +func_return set_context + +/* + * set_fpga(addr,val) + * + * Map in 0x00000000 -> 0x0fffffff and then do the write. + */ +set_fpga: + movew %sr,%d1 + movew #0x2700,%sr + movel ACR0_FPGA, %d0 + movec %d0, %acr0 + nop + moveal 4(%sp),%a0 + movel 8(%sp),%a0@ + movel ACR0_DEFAULT, %d0 + movec %d0, %acr0 + nop + movew %d1,%sr + rts + + .data + .align 4 + +availmem: + .long 0 +L(phys_kernel_start): + .long PAGE_OFFSET +L(kernel_end): + .long 0 +L(memory_start): + .long PAGE_OFFSET_RAW + +#ifdef CONFIG_MCF_USER_HALT +/* + * Enable User Halt Enable in the debug control register. + */ +wdbg_uhe: + .word 0x2c80 /* DR0 */ + .word 0x00b0 /* 31:16 */ + .word 0x0400 /* 15:0 -- enable UHE */ + .word 0x0000 /* unused */ +#endif + + --- /dev/null +++ b/arch/m68k/coldfire/ints.c @@ -0,0 +1,384 @@ +/* + * linux/arch/m68k/coldfire/ints.c -- General interrupt handling code + * + * Copyright (C) 1999-2002 Greg Ungerer (gerg@snapgear.com) + * Copyright (C) 1998 D. Jeff Dionne <jeff@lineo.ca>, + * Kenneth Albanowski <kjahds@kjahds.com>, + * Copyright (C) 2000 Lineo Inc. (www.lineo.com) + * Matt Waddel Matt.Waddel@freescale.com + * Copyright Freescale Semiconductor, Inc. 2007 + * Kurt Mahan kmahan@freescale.com + * + * Based on: + * linux/arch/m68k/kernel/ints.c & + * linux/arch/m68knommu/5307/ints.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/kernel_stat.h> +#include <linux/errno.h> +#include <linux/seq_file.h> +#include <linux/interrupt.h> + +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/traps.h> +#include <asm/page.h> +#include <asm/machdep.h> +#include <asm/irq_regs.h> + +#include <asm/mcfsim.h> + +/* + * IRQ Handler lists. + */ +static struct irq_node *irq_list[SYS_IRQS]; +static struct irq_controller *irq_controller[SYS_IRQS]; +static int irq_depth[SYS_IRQS]; + +/* + * IRQ Controller + */ +#ifdef CONFIG_M54455 +void m5445x_irq_enable(unsigned int irq); +void m5445x_irq_disable(unsigned int irq); +static struct irq_controller m5445x_irq_controller = { + .name = "M5445X", + .lock = SPIN_LOCK_UNLOCKED, + .enable = m5445x_irq_enable, + .disable = m5445x_irq_disable, +}; +#endif + +#define POOL_SIZE SYS_IRQS +static struct irq_node pool[POOL_SIZE]; +static struct irq_node *get_irq_node(void); + +/* The number of spurious interrupts */ +unsigned int num_spurious; +asmlinkage void handle_badint(struct pt_regs *regs); + +/* + * void init_IRQ(void) + * + * This function should be called during kernel startup to initialize + * the IRQ handling routines. + */ +void __init init_IRQ(void) +{ + int i; + +#ifdef CONFIG_M54455 + for (i = 0; i < SYS_IRQS; i++) + irq_controller[i] = &m5445x_irq_controller; +#endif +} + +/* + * process_int(unsigned long vec, struct pt_regs *fp) + * + * Process an interrupt. Called from entry.S. + */ +asmlinkage void process_int(unsigned long vec, struct pt_regs *fp) +{ + struct pt_regs *old_regs; + struct irq_node *node; + old_regs = set_irq_regs(fp); + kstat_cpu(0).irqs[vec]++; + + node = irq_list[vec]; + if (!node) + handle_badint(fp); + else { + do { + node->handler(vec, node->dev_id); + node = node->next; + } while (node); + } + + set_irq_regs(old_regs); +} + +/* + * show_interrupts( struct seq_file *p, void *v) + * + * Called to show all the current interrupt information. + */ +int show_interrupts(struct seq_file *p, void *v) +{ + struct irq_controller *contr; + struct irq_node *node; + int i = *(loff_t *) v; + + if ((i < NR_IRQS) && (irq_list[i])) { + contr = irq_controller[i]; + node = irq_list[i]; + seq_printf(p, "%-8s %3u: %10u %s", contr->name, i, + kstat_cpu(0).irqs[i], node->devname); + while ((node = node->next)) + seq_printf(p, ", %s", node->devname); + + seq_printf(p, "\n"); + } + + return 0; +} + +/* + * get_irq_node(void) + * + * Get an irq node from the pool. + */ +struct irq_node *get_irq_node(void) +{ + struct irq_node *p = pool; + int i; + + for (i = 0; i < POOL_SIZE; i++, p++) { + if (!p->handler) { + memset(p, 0, sizeof(struct irq_node)); + return p; + } + } + printk(KERN_INFO "%s(%s:%d): No more irq nodes, I suggest you \ + increase POOL_SIZE", __FUNCTION__, __FILE__, __LINE__); + return NULL; +} + +void init_irq_proc(void) +{ + /* Insert /proc/irq driver here */ +} + +int setup_irq(unsigned int irq, struct irq_node *node) +{ + struct irq_controller *contr; + struct irq_node **prev; + unsigned long flags; + + if (irq >= NR_IRQS || !irq_controller[irq]) { + printk("%s: Incorrect IRQ %d from %s\n", + __FUNCTION__, irq, node->devname); + return -ENXIO; + } + + contr = irq_controller[irq]; + spin_lock_irqsave(&contr->lock, flags); + + prev = irq_list + irq; + if (*prev) { + /* Can't share interrupts unless both agree to */ + if (!((*prev)->flags & node->flags & IRQF_SHARED)) { + spin_unlock_irqrestore(&contr->lock, flags); + return -EBUSY; + } + while (*prev) + prev = &(*prev)->next; + } + + if (!irq_list[irq]) { + if (contr->startup) + contr->startup(irq); + else + contr->enable(irq); + } + node->next = NULL; + *prev = node; + + spin_unlock_irqrestore(&contr->lock, flags); + + return 0; +} + +int request_irq(unsigned int irq, + irq_handler_t handler, + unsigned long flags, const char *devname, void *dev_id) +{ + struct irq_node *node = get_irq_node(); + int res; + + if (!node) + return -ENOMEM; + + node->handler = handler; + node->flags = flags; + node->dev_id = dev_id; + node->devname = devname; + + res = setup_irq(irq, node); + if (res) + node->handler = NULL; + + return res; +} +EXPORT_SYMBOL(request_irq); + +void free_irq(unsigned int irq, void *dev_id) +{ + struct irq_controller *contr; + struct irq_node **p, *node; + unsigned long flags; + + if (irq >= NR_IRQS || !irq_controller[irq]) { + printk(KERN_DEBUG "%s: Incorrect IRQ %d\n", __FUNCTION__, irq); + return; + } + + contr = irq_controller[irq]; + spin_lock_irqsave(&contr->lock, flags); + + p = irq_list + irq; + while ((node = *p)) { + if (node->dev_id == dev_id) + break; + p = &node->next; + } + + if (node) { + *p = node->next; + node->handler = NULL; + } else + printk(KERN_DEBUG "%s: Removing probably wrong IRQ %d\n", + __FUNCTION__, irq); + + if (!irq_list[irq]) { + if (contr->shutdown) + contr->shutdown(irq); + else + contr->disable(irq); + } + + spin_unlock_irqrestore(&contr->lock, flags); +} +EXPORT_SYMBOL(free_irq); + +void enable_irq(unsigned int irq) +{ + struct irq_controller *contr; + unsigned long flags; + + if (irq >= NR_IRQS || !irq_controller[irq]) { + printk(KERN_DEBUG "%s: Incorrect IRQ %d\n", __FUNCTION__, irq); + return; + } + + contr = irq_controller[irq]; + spin_lock_irqsave(&contr->lock, flags); + if (irq_depth[irq]) { + if (!--irq_depth[irq]) { + if (contr->enable) + contr->enable(irq); + } + } else + WARN_ON(1); + spin_unlock_irqrestore(&contr->lock, flags); +} +EXPORT_SYMBOL(enable_irq); + +void disable_irq(unsigned int irq) +{ + struct irq_controller *contr; + unsigned long flags; + + if (irq >= NR_IRQS || !irq_controller[irq]) { + printk(KERN_DEBUG "%s: Incorrect IRQ %d\n", __FUNCTION__, irq); + return; + } + + contr = irq_controller[irq]; + spin_lock_irqsave(&contr->lock, flags); + if (!irq_depth[irq]++) { + if (contr->disable) + contr->disable(irq); + } + spin_unlock_irqrestore(&contr->lock, flags); +} +EXPORT_SYMBOL(disable_irq); + +unsigned long probe_irq_on(void) +{ + return 0; +} +EXPORT_SYMBOL(probe_irq_on); + +int probe_irq_off(unsigned long irqs) +{ + return 0; +} +EXPORT_SYMBOL(probe_irq_off); + +asmlinkage void handle_badint(struct pt_regs *regs) +{ + kstat_cpu(0).irqs[0]++; + num_spurious++; + printk(KERN_DEBUG "unexpected interrupt from %u\n", regs->vector); +} +EXPORT_SYMBOL(handle_badint); + +#ifdef CONFIG_M54455 +/* + * M5445X Implementation + */ +void m5445x_irq_enable(unsigned int irq) +{ + /* enable the interrupt hardware */ + if (irq < 64) + return; + + /* adjust past non-hardware ints */ + irq -= 64; + + /* check for eport */ + if ((irq > 0) && (irq < 8)) { + /* enable eport */ + MCF_EPORT_EPPAR &= ~(3 << (irq*2)); /* level */ + MCF_EPORT_EPDDR &= ~(1 << irq); /* input */ + MCF_EPORT_EPIER |= 1 << irq; /* irq enabled */ + } + + if (irq < 64) { + /* controller 0 */ + MCF_INTC0_ICR(irq) = 0x02; + MCF_INTC0_CIMR = irq; + } else { + /* controller 1 */ + irq -= 64; + MCF_INTC1_ICR(irq) = 0x02; + MCF_INTC1_CIMR = irq; + } +} + +void m5445x_irq_disable(unsigned int irq) +{ + /* disable the interrupt hardware */ + if (irq < 64) + return; + + /* adjust past non-hardware ints */ + irq -= 64; + + /* check for eport */ + if ((irq > 0) && (irq < 8)) { + /* disable eport */ + MCF_EPORT_EPIER &= ~(1 << irq); + } + + if (irq < 64) { + /* controller 0 */ + MCF_INTC0_ICR(irq) = 0x00; + MCF_INTC0_SIMR = irq; + } else { + /* controller 1 */ + irq -= 64; + MCF_INTC1_ICR(irq) = 0x00; + MCF_INTC1_SIMR = irq; + } +} +#endif --- /dev/null +++ b/arch/m68k/coldfire/iomap.c @@ -0,0 +1,54 @@ +/* + * arch/m68k/coldfire/iomap.c + * + * Generic coldfire iomap interface + * + * Based on the sh64 iomap.c by Paul Mundt. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/pci.h> +#include <asm/io.h> + +void __iomem *__attribute__ ((weak)) +ioport_map(unsigned long port, unsigned int len) +{ + return (void __iomem *)port; +} +EXPORT_SYMBOL(pci_iomap); + +void ioport_unmap(void __iomem *addr) +{ + /* Nothing .. */ +} +EXPORT_SYMBOL(pci_iounmap); + +void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max) +{ + unsigned long start = pci_resource_start(dev, bar); + unsigned long len = pci_resource_len(dev, bar); + unsigned long flags = pci_resource_flags(dev, bar); +printk(KERN_INFO "PCI_IOMAP: BAR=%d START=0x%lx LEN=0x%lx FLAGS=0x%lx\n", + bar, start, len, flags); + + if (!len) + return NULL; + if (max && len > max) + len = max; + if (flags & IORESOURCE_IO) + return ioport_map(start, len); + if (flags & IORESOURCE_MEM) + return (void __iomem *)start; + + /* What? */ + return NULL; +} +EXPORT_SYMBOL(ioport_map); + +void pci_iounmap(struct pci_dev *dev, void __iomem *addr) +{ + /* Nothing .. */ +} +EXPORT_SYMBOL(ioport_unmap); --- /dev/null +++ b/arch/m68k/coldfire/mcf5445x-pci.c @@ -0,0 +1,427 @@ +/* + * arch/m68k/coldfire/mcf5445x-pci.c + * + * Coldfire M5445x specific PCI implementation. + * + * Copyright (c) 2007 Freescale Semiconductor, Inc. + * Kurt Mahan <kmahan@freescale.com> + */ + +#include <linux/delay.h> +#include <linux/pci.h> + +#include <asm/mcfsim.h> +#include <asm/pci.h> +#include <asm/irq.h> + +/* + * Layout MCF5445x to PCI memory mappings: + * + * WIN MCF5445x PCI TYPE + * --- -------- --- ---- + * [0] 0xA0000000 -> 0xA7FFFFFF 0xA0000000 -> 0xA7FFFFFF MEM + * [1] 0xA8000000 -> 0xABFFFFFF 0xA8000000 -> 0xABFFFFFF MEM + * [2] 0xAC000000 -> 0xAFFFFFFF 0xAC000000 -> 0xAFFFFFFF IO + */ + +#define MCF5445X_PCI_MEM_BASE 0xA0000000 +#define MCF5445X_PCI_MEM_SIZE 0x0C000000 + +#define MCF5445X_PCI_CONFIG_BASE 0xAC000000 +#define MCF5445X_PCI_CONFIG_SIZE 0x04000000 + +#define MCF5445X_PCI_IO_BASE 0xAC000000 +#define MCF5445X_PCI_IO_SIZE 0x04000000 + +/* PCI Bus memory resource block */ +struct resource pci_iomem_resource = { + .name = "PCI memory space", + .start = MCF5445X_PCI_MEM_BASE, + .flags = IORESOURCE_MEM, + .end = MCF5445X_PCI_MEM_BASE + MCF5445X_PCI_MEM_SIZE - 1 +}; + +/* PCI Bus ioport resource block */ +struct resource pci_ioport_resource = { + .name = "PCI I/O space", + .start = MCF5445X_PCI_IO_BASE, + .flags = IORESOURCE_IO, + .end = MCF5445X_PCI_IO_BASE + MCF5445X_PCI_IO_SIZE - 1 +}; + +/* + * The M54455EVB multiplexes all the PCI interrupts via + * the FPGA and routes them to a single interrupt. The + * PCI spec requires all PCI interrupt routines be smart + * enough to sort out their own interrupts. + * The interrupt source from the FPGA is configured + * to EPORT 3. + */ +#define MCF5445X_PCI_IRQ 0x43 + +#define PCI_SLOTS 4 + +/* + * FPGA Info + */ +#define FPGA_PCI_IRQ_ENABLE (u32 *)0x09000000 +#define FPGA_PCI_IRQ_STATUS (u32 *)0x09000004 +#define FPGA_PCI_IRQ_ROUTE (u32 *)0x0900000c +#define FPGA_SEVEN_LED (u32 *)0x09000014 + +extern void set_fpga(u32 *addr, u32 val); + +#ifdef DEBUG +void mcf5445x_pci_dumpregs(void); +#endif + +/* + * static void mcf5445x_conf_device(struct pci_dev *dev) + * + * Machine dependent Configure the given device. + * + * Parameters: + * + * dev - the pci device. + */ +void __init +mcf5445x_conf_device(struct pci_dev *dev) +{ + set_fpga(FPGA_PCI_IRQ_ENABLE, 0x0f); +} + +/* + * int mcf5445x_pci_config_read(unsigned int seg, unsigned int bus, + * unsigned int devfn, int reg, + * u32 *value) + * + * Read from PCI configuration space. + * + */ +int mcf5445x_pci_config_read(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 *value) +{ + u32 addr = MCF_PCI_PCICAR_BUSNUM(bus) | + MCF_PCI_PCICAR_DEVNUM(PCI_SLOT(devfn)) | + MCF_PCI_PCICAR_FUNCNUM(PCI_FUNC(devfn)) | + MCF_PCI_PCICAR_DWORD(reg) | + MCF_PCI_PCICAR_E; + + if ((bus > 255) || (devfn > 255) || (reg > 255)) { + *value = -1; + return -EINVAL; + } + + /* setup for config mode */ + MCF_PCI_PCICAR = addr; + __asm__ __volatile__("nop"); + + switch (len) { + case 1: + *value = *(volatile u8 *)(MCF5445X_PCI_CONFIG_BASE+(reg&3)); + break; + case 2: + *value = le16_to_cpu(*(volatile u16 *) + (MCF5445X_PCI_CONFIG_BASE + (reg&2))); + break; + case 4: + *value = le32_to_cpu(*(volatile u32 *) + (MCF5445X_PCI_CONFIG_BASE)); + break; + } + + /* clear config mode */ + MCF_PCI_PCICAR = ~MCF_PCI_PCICAR_E; + __asm__ __volatile__("nop"); + + return 0; +} + +/* + * int mcf5445x_pci_config_write(unsigned int seg, unsigned int bus, + * unsigned int devfn, int reg, + * u32 *value) + * + * Write to PCI configuration space + */ +int mcf5445x_pci_config_write(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 value) +{ + u32 addr = MCF_PCI_PCICAR_BUSNUM(bus) | + MCF_PCI_PCICAR_DEVNUM(PCI_SLOT(devfn)) | + MCF_PCI_PCICAR_FUNCNUM(PCI_FUNC(devfn)) | + MCF_PCI_PCICAR_DWORD(reg) | + MCF_PCI_PCICAR_E; + + if ((bus > 255) || (devfn > 255) || (reg > 255)) + return -EINVAL; + + /* setup for config mode */ + MCF_PCI_PCICAR = addr; + __asm__ __volatile__("nop"); + + switch (len) { + case 1: + *(volatile u8 *)(MCF5445X_PCI_CONFIG_BASE+(reg&3)) = (u8)value; + break; + case 2: + *(volatile u16 *)(MCF5445X_PCI_CONFIG_BASE+(reg&2)) = + cpu_to_le16((u16)value); + break; + case 4: + *(volatile u32 *)(MCF5445X_PCI_CONFIG_BASE) = + cpu_to_le32(value); + break; + } + + /* clear config mode */ + MCF_PCI_PCICAR = ~MCF_PCI_PCICAR_E; + __asm__ __volatile__("nop"); + + return 0; +} + +/* hardware operations */ +static struct pci_raw_ops mcf5445x_pci_ops = { + .read = mcf5445x_pci_config_read, + .write = mcf5445x_pci_config_write, +}; + +/* + * irqreturn_t mcf5445x_pci_interrupt( int irq, void *dev) + * + * PCI controller interrupt handler. + */ +static irqreturn_t +mcf5445x_pci_interrupt(int irq, void *dev) +{ + u32 status = MCF_PCI_PCIGSCR; +#ifdef DEBUG + printk(KERN_INFO "PCI: Controller irq status=0x%08x\n", status); +#endif + /* clear */ + MCF_PCI_PCIGSCR = status; + + return IRQ_HANDLED; +} + +/* + * irqreturn_t mcf5445x_pci_arb_interrupt( int irq, void *dev) + * + * PCI Arbiter interrupt handler. + */ +static irqreturn_t +mcf5445x_pci_arb_interrupt(int irq, void *dev) +{ + u32 status = MCF_PCIARB_PASR; +#ifdef DEBUG + printk(KERN_INFO "PCI: Arbiter irq status=0x%08x\n", status); +#endif + /* clear */ + MCF_PCIARB_PASR = status; + return IRQ_HANDLED; +} + +/* + * struct pci_bus_info *init_mcf5445x_pci(void) + * + * Machine specific initialisation: + * + * - Allocate and initialise a 'pci_bus_info' structure + * - Initialize hardware + * + * Result: pointer to 'pci_bus_info' structure. + */ +int __init +init_mcf5445x_pci(void) +{ + /* + * Initialize the PCI core + */ + + /* arbitration controller */ + MCF_PCIARB_PACR = MCF_PCIARB_PACR_INTMPRI | + MCF_PCIARB_PACR_EXTMPRI(0x0f) | + MCF_PCIARB_PACR_INTMINTEN | + MCF_PCIARB_PACR_EXTMINTEN(0x0f); + + /* pci pin assignment regs */ + MCF_GPIO_PAR_PCI = MCF_GPIO_PAR_PCI_GNT0 | + MCF_GPIO_PAR_PCI_GNT1 | + MCF_GPIO_PAR_PCI_GNT2 | + MCF_GPIO_PAR_PCI_GNT3_GNT3 | + MCF_GPIO_PAR_PCI_REQ0 | + MCF_GPIO_PAR_PCI_REQ1 | + MCF_GPIO_PAR_PCI_REQ2 | + MCF_GPIO_PAR_PCI_REQ3_REQ3; + + /* target control reg */ + MCF_PCI_PCITCR = MCF_PCI_PCITCR_P | + MCF_PCI_PCITCR_WCT(8); + + /* PCI MEM address */ + MCF_PCI_PCIIW0BTAR = 0xA007A000; + + /* PCI MEM address */ + MCF_PCI_PCIIW1BTAR = 0xA803A800; + + /* PCI IO address */ + MCF_PCI_PCIIW2BTAR = 0xAC03AC00; + + /* window control */ + MCF_PCI_PCIIWCR = MCF_PCI_PCIIWCR_WINCTRL0_ENABLE | + MCF_PCI_PCIIWCR_WINCTRL0_MEMREAD | + MCF_PCI_PCIIWCR_WINCTRL1_ENABLE | + MCF_PCI_PCIIWCR_WINCTRL1_MEMREAD | + MCF_PCI_PCIIWCR_WINCTRL2_ENABLE | + MCF_PCI_PCIIWCR_WINCTRL2_IO; + + /* initiator control reg */ + MCF_PCI_PCIICR = 0x00ff; + + /* type 0 - command */ + MCF_PCI_PCISCR = MCF_PCI_PCISCR_MW | /* mem write/inval */ + MCF_PCI_PCISCR_B | /* bus master enable */ + MCF_PCI_PCISCR_M; /* mem access enable */ + + /* type 0 - config reg */ + MCF_PCI_PCICR1 = MCF_PCI_PCICR1_CACHELINESIZE(8) | + MCF_PCI_PCICR1_LATTIMER(0xff); + + /* type 0 - config 2 reg */ + MCF_PCI_PCICR2 = 0; + + /* target control reg */ + MCF_PCI_PCITCR2 = MCF_PCI_PCITCR2_B0E | + MCF_PCI_PCITCR2_B4E; + + /* translate addresses from PCI[0] to CF[SDRAM] */ + MCF_PCI_PCITBATR0 = MCF_RAMBAR1 | MCF_PCI_PCITBATR0_EN; + MCF_PCI_PCITBATR4 = MCF_RAMBAR1 | MCF_PCI_PCITBATR4_EN; + + /* setup controller interrupt handlers */ + if (request_irq(55+128, mcf5445x_pci_interrupt, IRQF_SHARED, + "PCI Controller", NULL)) + printk(KERN_ERR "PCI: Unable to register controller irq\n"); + + if (request_irq (56+128, mcf5445x_pci_arb_interrupt, IRQF_SHARED, "PCI Arbiter", NULL)) + printk(KERN_ERR "PCI: Unable to register arbiter irq\n"); + + /* global control - clear reset bit */ + MCF_PCI_PCIGSCR = MCF_PCI_PCIGSCR_SEE | + MCF_PCI_PCIGSCR_PEE; + + /* let everything settle */ + udelay(1000); + + /* allocate bus ioport resource */ + if (request_resource(&ioport_resource, &pci_ioport_resource) < 0) + printk(KERN_ERR "PCI: Unable to alloc ioport resource\n"); + + /* allocate bus iomem resource */ + if (request_resource(&iomem_resource, &pci_iomem_resource) < 0) + printk(KERN_ERR "PCI: Unable to alloc iomem resource\n"); + + /* setup FPGA to route PCI to IRQ3(67), SW7 to IRQ7, SW6 to IRQ4 */ + set_fpga(FPGA_PCI_IRQ_ENABLE, 0x00000000); + set_fpga(FPGA_PCI_IRQ_ROUTE, 0x00000039); + set_fpga(FPGA_SEVEN_LED, 0x000000FF); + + raw_pci_ops = &mcf5445x_pci_ops; + + return 0; +} + +/* + * DEBUGGING + */ + +#ifdef DEBUG +struct regdump { + u32 addr; + char regname[16]; +}; + +struct regdump type0regs[] = { + { 0xfc0a8000, "PCIIDR" }, + { 0xfc0a8004, "PCISCR" }, + { 0xfc0a8008, "PCICCRIR" }, + { 0xfc0a800c, "PCICR1" }, + { 0xfc0a8010, "PCIBAR0" }, + { 0xfc0a8014, "PCIBAR1" }, + { 0xfc0a8018, "PCIBAR2" }, + { 0xfc0a801c, "PCIBAR3" }, + { 0xfc0a8020, "PCIBAR4" }, + { 0xfc0a8024, "PCIBAR5" }, + { 0xfc0a8028, "PCICCPR" }, + { 0xfc0a802c, "PCISID" }, + { 0xfc0a8030, "PCIERBAR" }, + { 0xfc0a8034, "PCICPR" }, + { 0xfc0a803c, "PCICR2" }, + { 0, "" } +}; + +struct regdump genregs[] = { + { 0xfc0a8060, "PCIGSCR" }, + { 0xfc0a8064, "PCITBATR0" }, + { 0xfc0a8068, "PCITBATR1" }, + { 0xfc0a806c, "PCITCR1" }, + { 0xfc0a8070, "PCIIW0BTAR" }, + { 0xfc0a8074, "PCIIW1BTAR" }, + { 0xfc0a8078, "PCIIW2BTAR" }, + { 0xfc0a8080, "PCIIWCR" }, + { 0xfc0a8084, "PCIICR" }, + { 0xfc0a8088, "PCIISR" }, + { 0xfc0a808c, "PCITCR2" }, + { 0xfc0a8090, "PCITBATR0" }, + { 0xfc0a8094, "PCITBATR1" }, + { 0xfc0a8098, "PCITBATR2" }, + { 0xfc0a809c, "PCITBATR3" }, + { 0xfc0a80a0, "PCITBATR4" }, + { 0xfc0a80a4, "PCITBATR5" }, + { 0xfc0a80a8, "PCIINTR" }, + { 0xfc0a80f8, "PCICAR" }, + { 0, "" } +}; + +struct regdump arbregs[] = { + { 0xfc0ac000, "PACR" }, + { 0xfc0ac004, "PASR" }, /* documentation error */ + { 0, "" } +}; + +/* + * void mcf5445x_pci_dumpregs() + * + * Dump out all the PCI registers + */ +void +mcf5445x_pci_dumpregs(void) +{ + struct regdump *reg; + + printk(KERN_INFO "*** MCF5445x PCI TARGET 0 REGISTERS ***\n"); + + reg = type0regs; + while (reg->addr) { + printk(KERN_INFO "0x%08x 0x%08x %s\n", reg->addr, + *((u32 *)reg->addr), reg->regname); + reg++; + } + + printk(KERN_INFO "\n*** MCF5445x PCI GENERAL REGISTERS ***\n"); + reg = genregs; + while (reg->addr) { + printk(KERN_INFO "0x%08x 0x%08x %s\n", reg->addr, + *((u32 *)reg->addr), reg->regname); + reg++; + } + printk(KERN_INFO "\n*** MCF5445x PCI ARBITER REGISTERS ***\n"); + reg = arbregs; + while (reg->addr) { + printk(KERN_INFO "0x%08x 0x%08x %s\n", reg->addr, + *((u32 *)reg->addr), reg->regname); + reg++; + } +} +#endif /* DEBUG */ --- /dev/null +++ b/arch/m68k/coldfire/muldi3.S @@ -0,0 +1,64 @@ +/* + * Coldfire muldi3 assembly verion + */ + +#include <linux/linkage.h> +.globl __muldi3 + +ENTRY(__muldi3) + linkw %fp,#0 + lea %sp@(-32),%sp + moveml %d2-%d7/%a2-%a3,%sp@ + moveal %fp@(8), %a2 + moveal %fp@(12), %a3 + moveal %fp@(16), %a0 + moveal %fp@(20),%a1 + movel %a3,%d2 + andil #65535,%d2 + movel %a3,%d3 + clrw %d3 + swap %d3 + movel %a1,%d0 + andil #65535,%d0 + movel %a1,%d1 + clrw %d1 + swap %d1 + movel %d2,%d7 + mulsl %d0,%d7 + movel %d2,%d4 + mulsl %d1,%d4 + movel %d3,%d2 + mulsl %d0,%d2 + mulsl %d1,%d3 + movel %d7,%d0 + clrw %d0 + swap %d0 + addl %d0,%d4 + addl %d2,%d4 + cmpl %d4,%d2 + blss 1f + addil #65536,%d3 +1: + movel %d4,%d0 + clrw %d0 + swap %d0 + movel %d3,%d5 + addl %d0,%d5 + movew %d4,%d6 + swap %d6 + movew %d7,%d6 + movel %d5,%d0 + movel %d6,%d1 + movel %a3,%d2 + movel %a0,%d3 + mulsl %d3,%d2 + movel %a2,%d3 + movel %a1,%d4 + mulsl %d4,%d3 + addl %d3,%d2 + movel %d2,%d0 + addl %d5,%d0 + moveml %sp@, %d2-%d7/%a2-%a3 + lea %sp@(32),%sp + unlk %fp + rts --- /dev/null +++ b/arch/m68k/coldfire/pci.c @@ -0,0 +1,245 @@ +/* + * linux/arch/m68k/coldfire/pci.c + * + * PCI initialization for Coldfire architectures. + * + * Currently Supported: + * M5445x + * + * Copyright (c) 2007 Freescale Semiconductor, Inc. + * Kurt Mahan <kmahan@freescale.com> + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> + +#include <asm/mcfsim.h> +#include <asm/pci.h> + +/* pci ops for reading/writing config */ +struct pci_raw_ops *raw_pci_ops; + +/* pci debug flag */ +static int debug_pci; + +#ifdef CONFIG_M54455 +extern int init_mcf5445x_pci(void); +extern void mcf5445x_conf_device(struct pci_dev *dev); +extern void mcf5445x_pci_dumpregs(void); + +extern struct resource pci_ioport_resource; +extern struct resource pci_iomem_resource; +#endif + +static int +pci_read(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + return raw_pci_ops->read(0, bus->number, devfn, where, size, value); +} + +static int +pci_write(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + return raw_pci_ops->write(0, bus->number, devfn, where, size, value); +} + +struct pci_ops pci_root_ops = { + .read = pci_read, + .write = pci_write, +}; + +/* + * pcibios_setup(char *) + * + * Initialize the pcibios based on cmd line params. + */ +char * __init +pcibios_setup(char *str) +{ + if (!strcmp(str, "debug")) { + debug_pci = 1; + return NULL; + } + return str; +} + +/* + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + * + * Why? Because some silly external IO cards only decode + * the low 10 bits of the IO address. The 0x00-0xff region + * is reserved for motherboard devices that decode all 16 + * bits, so it's ok to allocate at, say, 0x2800-0x28ff, + * but we want to try to avoid allocating at 0x2900-0x2bff + * which might have be mirrored at 0x0100-0x03ff.. + */ +void +pcibios_align_resource(void *data, struct resource *res, resource_size_t size, + resource_size_t align) +{ + struct pci_dev *dev = data; + + if (res->flags & IORESOURCE_IO) { + resource_size_t start = res->start; + + if (size > 0x100) + printk(KERN_ERR "PCI: I/O Region %s/%d too large" + " (%ld bytes)\n", pci_name(dev), + dev->resource - res, (long int)size); + + if (start & 0x300) { + start = (start + 0x3ff) & ~0x3ff; + res->start = start; + } + } +} + +/* + * Swizzle the device pin each time we cross a bridge + * and return the slot number. + */ +static u8 __devinit +pcibios_swizzle(struct pci_dev *dev, u8 *pin) +{ + return 0; +} + +/* + * Map a slot/pin to an IRQ. + */ +static int +pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + return 0x43; +} + +/* + * pcibios_update_irq(struct pci_dev *dev, int irq) + * + * Update a PCI interrupt. + */ +void __init +pcibios_update_irq(struct pci_dev *dev, int irq) +{ + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); +} + +/* + * pcibios_enable_device(struct pci_dev *dev, int mask) + * + * Enable a device on the PCI bus. + */ +int +pcibios_enable_device(struct pci_dev *dev, int mask) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for (idx = 0; idx < 6; idx++) { + r = &dev->resource[idx]; + if (!r->start && r->end) { + printk(KERN_ERR "PCI: Device %s not available because " + "of resource collisions\n", pci_name(dev)); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", + pci_name(dev), old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); +#ifdef CONFIG_M54455 + mcf5445x_conf_device(dev); +#endif + } + + return 0; +} + +/* + * pcibios_fixup_bus(struct pci_bus *bus) + */ +void __init +pcibios_fixup_bus(struct pci_bus *bus) +{ + struct pci_dev *dev = bus->self; + + if (!dev) { + /* Root bus. */ +#ifdef CONFIG_M54455 + bus->resource[0] = &pci_ioport_resource; + bus->resource[1] = &pci_iomem_resource; +#endif + } +} + +/* + * pcibios_init(void) + * + * Allocate/initialize low level pci bus/devices. + */ +static int __init +pcibios_init(void) +{ + struct pci_bus *bus; + + if (!raw_pci_ops) { + printk(KERN_WARNING "PCIBIOS: FATAL: NO PCI Hardware found\n"); + return 0; + } + + /* allocate and scan the (only) bus */ + bus = pci_scan_bus_parented(NULL, 0, &pci_root_ops, NULL); + + /* setup everything */ + if (bus) { + /* compute the bridge window sizes */ + pci_bus_size_bridges(bus); + + /* (re)assign device resources */ + pci_bus_assign_resources(bus); + + /* add the bus to the system */ + pci_bus_add_devices(bus); + + /* fixup irqs */ + pci_fixup_irqs(pcibios_swizzle, pcibios_map_irq); + } + + return 0; +} + +/* + * pci_init(void) + * + * Initialize the PCI Hardware. + */ +static int __init +pci_init(void) +{ +#if defined(CONFIG_M54455) + init_mcf5445x_pci(); +#endif + if (!raw_pci_ops) + printk(KERN_ERR "PCI: FATAL: NO PCI Detected\n"); + + return 0; +} + +/* low level hardware (first) */ +arch_initcall(pci_init); + +/* basic bios init (second) */ +subsys_initcall(pcibios_init); --- /dev/null +++ b/arch/m68k/coldfire/signal.c @@ -0,0 +1,868 @@ +/* + * linux/arch/m68k/kernel/signal.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +/* + * Derived from m68k/kernel/signal.c and the original authors are credited + * there. + * + * Coldfire support by: + * Matt Waddel Matt.Waddel@freescale.com + * Copyright Freescale Semiconductor, Inc 2007 + */ + +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/syscalls.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/stddef.h> +#include <linux/highuid.h> +#include <linux/personality.h> +#include <linux/tty.h> +#include <linux/binfmts.h> + +#include <asm/setup.h> +#include <asm/cf_uaccess.h> +#include <asm/cf_pgtable.h> +#include <asm/traps.h> +#include <asm/ucontext.h> + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs); + +const int frame_extra_sizes[16] = { + [1] = -1, + [2] = sizeof(((struct frame *)0)->un.fmt2), + [3] = sizeof(((struct frame *)0)->un.fmt3), + [4] = 0, + [5] = -1, + [6] = -1, + [7] = sizeof(((struct frame *)0)->un.fmt7), + [8] = -1, + [9] = sizeof(((struct frame *)0)->un.fmt9), + [10] = sizeof(((struct frame *)0)->un.fmta), + [11] = sizeof(((struct frame *)0)->un.fmtb), + [12] = -1, + [13] = -1, + [14] = -1, + [15] = -1, +}; + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage int do_sigsuspend(struct pt_regs *regs) +{ + old_sigset_t mask = regs->d3; + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs->d0 = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + return -EINTR; + } +} + +asmlinkage int +do_rt_sigsuspend(struct pt_regs *regs) +{ + sigset_t __user *unewset = (sigset_t __user *)regs->d1; + size_t sigsetsize = (size_t)regs->d2; + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs->d0 = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + return -EINTR; + } +} + +asmlinkage int +sys_sigaction(int sig, const struct old_sigaction __user *act, + struct old_sigaction __user *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (!access_ok(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +asmlinkage int +sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss) +{ + return do_sigaltstack(uss, uoss, rdusp()); +} + + +/* + * Do a signal return; undo the signal stack. + * + * Keep the return code on the stack quadword aligned! + * That makes the cache flush below easier. + */ + +struct sigframe +{ + char __user *pretcode; + int sig; + int code; + struct sigcontext __user *psc; + char retcode[16]; + unsigned long extramask[_NSIG_WORDS-1]; + struct sigcontext sc; +}; + +struct rt_sigframe +{ + char __user *pretcode; + int sig; + struct siginfo __user *pinfo; + void __user *puc; + char retcode[16]; + struct siginfo info; + struct ucontext uc; +}; + +#define FPCONTEXT_SIZE 216 +#define uc_fpstate uc_filler[0] +#define uc_formatvec uc_filler[FPCONTEXT_SIZE/4] +#define uc_extra uc_filler[FPCONTEXT_SIZE/4+1] + +#ifdef CONFIG_FPU +static unsigned char fpu_version; /* version num of fpu, set by setup_frame */ + +static inline int restore_fpu_state(struct sigcontext *sc) +{ + int err = 1; + + if (FPU_IS_EMU) { + /* restore registers */ + memcpy(current->thread.fpcntl, sc->sc_fpcntl, 12); + memcpy(current->thread.fp, sc->sc_fpregs, 24); + return 0; + } + + if (CPU_IS_060 ? sc->sc_fpstate[2] : sc->sc_fpstate[0]) { + /* Verify the frame format. */ + if (!CPU_IS_060 && (sc->sc_fpstate[0] != fpu_version)) + goto out; + if (CPU_IS_020_OR_030) { + if (m68k_fputype & FPU_68881 && + !(sc->sc_fpstate[1] == 0x18 || sc->sc_fpstate[1] == 0xb4)) + goto out; + if (m68k_fputype & FPU_68882 && + !(sc->sc_fpstate[1] == 0x38 || sc->sc_fpstate[1] == 0xd4)) + goto out; + } else if (CPU_IS_040) { + if (!(sc->sc_fpstate[1] == 0x00 || + sc->sc_fpstate[1] == 0x28 || + sc->sc_fpstate[1] == 0x60)) + goto out; + } else if (CPU_IS_060) { + if (!(sc->sc_fpstate[3] == 0x00 || + sc->sc_fpstate[3] == 0x60 || + sc->sc_fpstate[3] == 0xe0)) + goto out; + } else + goto out; + + } + err = 0; + +out: + return err; +} + +static inline int rt_restore_fpu_state(struct ucontext __user *uc) +{ + unsigned char fpstate[FPCONTEXT_SIZE]; + int context_size = CPU_IS_060 ? 8 : 0; + fpregset_t fpregs; + int err = 1; + + if (FPU_IS_EMU) { + /* restore fpu control register */ + if (__copy_from_user(current->thread.fpcntl, + uc->uc_mcontext.fpregs.f_fpcntl, 12)) + goto out; + /* restore all other fpu register */ + if (__copy_from_user(current->thread.fp, + uc->uc_mcontext.fpregs.f_fpregs, 96)) + goto out; + return 0; + } + + if (__get_user(*(long *)fpstate, (long __user *)&uc->uc_fpstate)) + goto out; + if (CPU_IS_060 ? fpstate[2] : fpstate[0]) { + if (!CPU_IS_060) + context_size = fpstate[1]; + /* Verify the frame format. */ + if (!CPU_IS_060 && (fpstate[0] != fpu_version)) + goto out; + if (CPU_IS_020_OR_030) { + if (m68k_fputype & FPU_68881 && + !(context_size == 0x18 || context_size == 0xb4)) + goto out; + if (m68k_fputype & FPU_68882 && + !(context_size == 0x38 || context_size == 0xd4)) + goto out; + } else if (CPU_IS_040) { + if (!(context_size == 0x00 || + context_size == 0x28 || + context_size == 0x60)) + goto out; + } else if (CPU_IS_060) { + if (!(fpstate[3] == 0x00 || + fpstate[3] == 0x60 || + fpstate[3] == 0xe0)) + goto out; + } else + goto out; + if (__copy_from_user(&fpregs, &uc->uc_mcontext.fpregs, + sizeof(fpregs))) + goto out; + } + if (context_size && + __copy_from_user(fpstate + 4, (long __user *)&uc->uc_fpstate + 1, + context_size)) + goto out; + err = 0; + +out: + return err; +} +#endif + +static inline int +restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, + void __user *fp, int *pd0) +{ + int fsize, formatvec; + struct sigcontext context; + int err = 0; + + /* get previous context */ + if (copy_from_user(&context, usc, sizeof(context))) + goto badframe; + + /* restore passed registers */ + regs->d1 = context.sc_d1; + regs->a0 = context.sc_a0; + regs->a1 = context.sc_a1; + regs->sr = (regs->sr & 0xff00) | (context.sc_sr & 0xff); + regs->pc = context.sc_pc; + regs->orig_d0 = -1; /* disable syscall checks */ + wrusp(context.sc_usp); + formatvec = context.sc_formatvec; + regs->format = formatvec >> 12; + regs->vector = formatvec & 0xfff; + +#ifdef CONFIG_FPU + err = restore_fpu_state(&context); +#endif + + fsize = frame_extra_sizes[regs->format]; + if (fsize < 0) { + /* + * user process trying to return with weird frame format + */ +#ifdef DEBUG + printk(KERN_DEBUG "user process returning with weird \ + frame format\n"); +#endif + goto badframe; + } + + /* OK. Make room on the supervisor stack for the extra junk, + * if necessary. + */ + + { + struct switch_stack *sw = (struct switch_stack *)regs - 1; + regs->d0 = context.sc_d0; +#define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack)) + __asm__ __volatile__ + (" movel %0,%/sp\n\t" + " bra ret_from_signal\n" + "4:\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 2b,4b\n" + ".previous" + : /* no outputs, it doesn't ever return */ + : "a" (sw), "d" (fsize), "d" (frame_offset/4-1), + "n" (frame_offset), "a" (fp) + : "a0"); +#undef frame_offset + /* + * If we ever get here an exception occurred while + * building the above stack-frame. + */ + goto badframe; + } + + *pd0 = context.sc_d0; + return err; + +badframe: + return 1; +} + +static inline int +rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw, + struct ucontext __user *uc, int *pd0) +{ + int fsize, temp; + greg_t __user *gregs = uc->uc_mcontext.gregs; + unsigned long usp; + int err; + + err = __get_user(temp, &uc->uc_mcontext.version); + if (temp != MCONTEXT_VERSION) + goto badframe; + /* restore passed registers */ + err |= __get_user(regs->d0, &gregs[0]); + err |= __get_user(regs->d1, &gregs[1]); + err |= __get_user(regs->d2, &gregs[2]); + err |= __get_user(regs->d3, &gregs[3]); + err |= __get_user(regs->d4, &gregs[4]); + err |= __get_user(regs->d5, &gregs[5]); + err |= __get_user(sw->d6, &gregs[6]); + err |= __get_user(sw->d7, &gregs[7]); + err |= __get_user(regs->a0, &gregs[8]); + err |= __get_user(regs->a1, &gregs[9]); + err |= __get_user(regs->a2, &gregs[10]); + err |= __get_user(sw->a3, &gregs[11]); + err |= __get_user(sw->a4, &gregs[12]); + err |= __get_user(sw->a5, &gregs[13]); + err |= __get_user(sw->a6, &gregs[14]); + err |= __get_user(usp, &gregs[15]); + wrusp(usp); + err |= __get_user(regs->pc, &gregs[16]); + err |= __get_user(temp, &gregs[17]); + regs->sr = (regs->sr & 0xff00) | (temp & 0xff); + regs->orig_d0 = -1; /* disable syscall checks */ + err |= __get_user(temp, &uc->uc_formatvec); + regs->format = temp >> 12; + regs->vector = temp & 0xfff; + +#ifdef CONFIG_FPU + err |= rt_restore_fpu_state(uc); +#endif + + if (do_sigaltstack(&uc->uc_stack, NULL, usp) == -EFAULT) + goto badframe; + + fsize = frame_extra_sizes[regs->format]; + if (fsize < 0) { + /* + * user process trying to return with weird frame format + */ +#ifdef DEBUG + printk(KERN_DEBUG "user process returning with weird \ + frame format\n"); +#endif + goto badframe; + } + + /* OK. Make room on the supervisor stack for the extra junk, + * if necessary. + */ + + { +#define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack)) + __asm__ __volatile__ + (" movel %0,%/sp\n\t" + " bra ret_from_signal\n" + "4:\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 2b,4b\n" + ".previous" + : /* no outputs, it doesn't ever return */ + : "a" (sw), "d" (fsize), "d" (frame_offset/4-1), + "n" (frame_offset), "a" (&uc->uc_extra) + : "a0"); +#undef frame_offset + /* + * If we ever get here an exception occurred while + * building the above stack-frame. + */ + goto badframe; + } + + *pd0 = regs->d0; + return err; + +badframe: + return 1; +} + +asmlinkage int do_sigreturn(unsigned long __unused) +{ + struct switch_stack *sw = (struct switch_stack *) &__unused; + struct pt_regs *regs = (struct pt_regs *) (sw + 1); + unsigned long usp = rdusp(); + struct sigframe __user *frame = (struct sigframe __user *)(usp - 4); + sigset_t set; + int d0; + + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__get_user(set.sig[0], &frame->sc.sc_mask) || + (_NSIG_WORDS > 1 && + __copy_from_user(&set.sig[1], &frame->extramask, + sizeof(frame->extramask)))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(regs, &frame->sc, frame + 1, &d0)) + goto badframe; + return d0; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage int do_rt_sigreturn(unsigned long __unused) +{ + struct switch_stack *sw = (struct switch_stack *) &__unused; + struct pt_regs *regs = (struct pt_regs *) (sw + 1); + unsigned long usp = rdusp(); + struct rt_sigframe __user *frame = + (struct rt_sigframe __user *)(usp - 4); + sigset_t set; + int d0; + + 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_ucontext(regs, sw, &frame->uc, &d0)) + goto badframe; + return d0; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +#ifdef CONFIG_FPU +/* + * Set up a signal frame. + */ + +static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs) +{ + if (FPU_IS_EMU) { + /* save registers */ + memcpy(sc->sc_fpcntl, current->thread.fpcntl, 12); + memcpy(sc->sc_fpregs, current->thread.fp, 24); + return; + } +} + +static inline int rt_save_fpu_state(struct ucontext __user *uc, + struct pt_regs *regs) +{ + int err = 0; + + if (FPU_IS_EMU) { + /* save fpu control register */ + err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpcntl, + current->thread.fpcntl, 12); + /* save all other fpu register */ + err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpregs, + current->thread.fp, 96); + return err; + } + + return err; +} +#endif + +static void setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, + unsigned long mask) +{ + sc->sc_mask = mask; + sc->sc_usp = rdusp(); + sc->sc_d0 = regs->d0; + sc->sc_d1 = regs->d1; + sc->sc_a0 = regs->a0; + sc->sc_a1 = regs->a1; + sc->sc_sr = regs->sr; + sc->sc_pc = regs->pc; + sc->sc_formatvec = regs->format << 12 | regs->vector; +#ifdef CONFIG_FPU + save_fpu_state(sc, regs); +#endif +} + +static inline int rt_setup_ucontext(struct ucontext __user *uc, + struct pt_regs *regs) +{ + struct switch_stack *sw = (struct switch_stack *)regs - 1; + greg_t __user *gregs = uc->uc_mcontext.gregs; + int err = 0; + + err |= __put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version); + err |= __put_user(regs->d0, &gregs[0]); + err |= __put_user(regs->d1, &gregs[1]); + err |= __put_user(regs->d2, &gregs[2]); + err |= __put_user(regs->d3, &gregs[3]); + err |= __put_user(regs->d4, &gregs[4]); + err |= __put_user(regs->d5, &gregs[5]); + err |= __put_user(sw->d6, &gregs[6]); + err |= __put_user(sw->d7, &gregs[7]); + err |= __put_user(regs->a0, &gregs[8]); + err |= __put_user(regs->a1, &gregs[9]); + err |= __put_user(regs->a2, &gregs[10]); + err |= __put_user(sw->a3, &gregs[11]); + err |= __put_user(sw->a4, &gregs[12]); + err |= __put_user(sw->a5, &gregs[13]); + err |= __put_user(sw->a6, &gregs[14]); + err |= __put_user(rdusp(), &gregs[15]); + err |= __put_user(regs->pc, &gregs[16]); + err |= __put_user(regs->sr, &gregs[17]); + err |= __put_user((regs->format << 12) | regs->vector, + &uc->uc_formatvec); +#ifdef CONFIG_FPU + err |= rt_save_fpu_state(uc, regs); +#endif + return err; +} + +extern void IcacheInvalidateCacheBlock(void *, unsigned long); +static inline void push_cache(unsigned long vaddr) +{ + IcacheInvalidateCacheBlock((void *)vaddr, 8); +} + +static inline void __user * +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 (!sas_ss_flags(usp)) + usp = current->sas_ss_sp + current->sas_ss_size; + } + return (void __user *)((usp - frame_size) & -8UL); +} + +static void setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs *regs) +{ + struct sigframe __user *frame; + int fsize = frame_extra_sizes[regs->format]; + struct sigcontext context; + int err = 0; + + if (fsize < 0) { +#ifdef DEBUG + printk(KERN_DEBUG "setup_frame: Unknown frame format %#x\n", + regs->format); +#endif + goto give_sigsegv; + } + + 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(regs->vector, &frame->code); + err |= __put_user(&frame->sc, &frame->psc); + + if (_NSIG_WORDS > 1) + err |= copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + + setup_sigcontext(&context, regs, set->sig[0]); + err |= copy_to_user(&frame->sc, &context, sizeof(context)); + + /* Set up to return from userspace. */ + err |= __put_user(frame->retcode, &frame->pretcode); + /* moveq #,d0; trap #0 */ + err |= __put_user(0x70004e40 + (__NR_sigreturn << 16), + (long __user *)(frame->retcode)); + + if (err) + goto give_sigsegv; + + push_cache((unsigned long) &frame->retcode); + + /* Set up registers for signal handler */ + wrusp((unsigned long) frame); + regs->pc = (unsigned long) ka->sa.sa_handler; + +adjust_stack: + /* Prepare to skip over the extra stuff in the exception frame. */ + if (regs->stkadj) { + struct pt_regs *tregs = + (struct pt_regs *)((ulong)regs + regs->stkadj); +#ifdef DEBUG + printk(KERN_DEBUG "Performing stackadjust=%04x\n", + regs->stkadj); +#endif + /* This must be copied with decreasing addresses to + handle overlaps. */ + tregs->vector = 0; + tregs->format = 0; + tregs->pc = regs->pc; + tregs->sr = regs->sr; + } + return; + +give_sigsegv: + force_sigsegv(sig, current); + goto adjust_stack; +} + +static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) +{ + struct rt_sigframe __user *frame; + int fsize = frame_extra_sizes[regs->format]; + int err = 0; + + if (fsize < 0) { +#ifdef DEBUG + printk(KERN_DEBUG "setup_frame: Unknown frame format %#x\n", + regs->format); +#endif + goto give_sigsegv; + } + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (fsize) { + err |= copy_to_user(&frame->uc.uc_extra, regs + 1, fsize); + regs->stkadj = fsize; + } + + 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(NULL, &frame->uc.uc_link); + err |= __put_user((void __user *)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_ucontext(&frame->uc, regs); + err |= copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + + /* Set up to return from userspace. */ + err |= __put_user(frame->retcode, &frame->pretcode); + + /* moveq #,d0; andi.l #,D0; trap #0 */ + err |= __put_user(0x70AD0280, (long *)(frame->retcode + 0)); + err |= __put_user(0x000000ff, (long *)(frame->retcode + 4)); + err |= __put_user(0x4e400000, (long *)(frame->retcode + 8)); + + if (err) + goto give_sigsegv; + + push_cache((unsigned long) &frame->retcode); + + /* Set up registers for signal handler */ + wrusp((unsigned long) frame); + regs->pc = (unsigned long) ka->sa.sa_handler; + +adjust_stack: + /* Prepare to skip over the extra stuff in the exception frame. */ + if (regs->stkadj) { + struct pt_regs *tregs = + (struct pt_regs *)((ulong)regs + regs->stkadj); +#ifdef DEBUG + printk(KERN_DEBUG "Performing stackadjust=%04x\n", + regs->stkadj); +#endif + /* This must be copied with decreasing addresses to + handle overlaps. */ + tregs->vector = 0; + tregs->format = 0; + tregs->pc = regs->pc; + tregs->sr = regs->sr; + } + return; + +give_sigsegv: + force_sigsegv(sig, current); + goto adjust_stack; +} + +static inline void +handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler) +{ + switch (regs->d0) { + case -ERESTARTNOHAND: + if (!has_handler) + goto do_restart; + regs->d0 = -EINTR; + break; + + case -ERESTARTSYS: + if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) { + regs->d0 = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: +do_restart: + regs->d0 = regs->orig_d0; + regs->pc -= 2; + break; + } +} + +/* + * OK, we're invoking a handler + */ +static void +handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *oldset, struct pt_regs *regs) +{ + /* are we from a system call? */ + if (regs->orig_d0 >= 0) + /* If so, check system call restarting.. */ + handle_restart(regs, ka, 1); + + /* set up the stack frame */ + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(sig, ka, info, oldset, regs); + else + setup_frame(sig, ka, oldset, regs); + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + 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); +} + +/* + * 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. + */ +asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs) +{ + siginfo_t info; + struct k_sigaction ka; + int signr; + + current->thread.esp0 = (unsigned long) regs; + + if (!oldset) + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + if (signr > 0) { + /* Whee! Actually deliver the signal. */ + handle_signal(signr, &ka, &info, oldset, regs); + return 1; + } + + /* Did we come from a system call? */ + if (regs->orig_d0 >= 0) + /* Restart the system call - no handlers present */ + handle_restart(regs, NULL, 0); + + return 0; +} --- /dev/null +++ b/arch/m68k/coldfire/traps.c @@ -0,0 +1,454 @@ +/* + * linux/arch/m68knommu/kernel/traps.c + * + * Copyright (C) 1993, 1994 by Hamish Macdonald + * + * 68040 fixes by Michael Rausch + * 68040 fixes by Martin Apel + * 68060 fixes by Roman Hodek + * 68060 fixes by Jesper Skov + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +/* + * Sets up all exception vectors + */ +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/a.out.h> +#include <linux/user.h> +#include <linux/string.h> +#include <linux/linkage.h> +#include <linux/init.h> +#include <linux/ptrace.h> +#include <linux/kallsyms.h> + +#include <asm/setup.h> +#include <asm/fpu.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/traps.h> +#include <asm/pgtable.h> +#include <asm/machdep.h> +#include <asm/siginfo.h> + +static char const * const vec_names[] = { + "RESET SP", "RESET PC", "BUS ERROR", "ADDRESS ERROR", + "ILLEGAL INSTRUCTION", "ZERO DIVIDE", "CHK", "TRAPcc", + "PRIVILEGE VIOLATION", "TRACE", "LINE 1010", "LINE 1111", + "UNASSIGNED RESERVED 12", "COPROCESSOR PROTOCOL VIOLATION", + "FORMAT ERROR", "UNINITIALIZED INTERRUPT", + "UNASSIGNED RESERVED 16", "UNASSIGNED RESERVED 17", + "UNASSIGNED RESERVED 18", "UNASSIGNED RESERVED 19", + "UNASSIGNED RESERVED 20", "UNASSIGNED RESERVED 21", + "UNASSIGNED RESERVED 22", "UNASSIGNED RESERVED 23", + "SPURIOUS INTERRUPT", "LEVEL 1 INT", "LEVEL 2 INT", "LEVEL 3 INT", + "LEVEL 4 INT", "LEVEL 5 INT", "LEVEL 6 INT", "LEVEL 7 INT", + "SYSCALL", "TRAP #1", "TRAP #2", "TRAP #3", + "TRAP #4", "TRAP #5", "TRAP #6", "TRAP #7", + "TRAP #8", "TRAP #9", "TRAP #10", "TRAP #11", + "TRAP #12", "TRAP #13", "TRAP #14", "TRAP #15", + "FPCP BSUN", "FPCP INEXACT", "FPCP DIV BY 0", "FPCP UNDERFLOW", + "FPCP OPERAND ERROR", "FPCP OVERFLOW", "FPCP SNAN", + "FPCP UNSUPPORTED OPERATION", + "MMU CONFIGURATION ERROR" +}; + +asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, + unsigned long error_code); +asmlinkage void trap_c(struct frame *fp); +extern void __init coldfire_trap_init(void); + +void __init trap_init(void) +{ + coldfire_trap_init(); +} + +/* The following table converts the FS encoding of a ColdFire + exception stack frame into the error_code value needed by + do_fault. */ + +static const unsigned char fs_err_code[] = { + 0, /* 0000 */ + 0, /* 0001 */ + 0, /* 0010 */ + 0, /* 0011 */ + 1, /* 0100 */ + 0, /* 0101 */ + 0, /* 0110 */ + 0, /* 0111 */ + 2, /* 1000 */ + 3, /* 1001 */ + 2, /* 1010 */ + 0, /* 1011 */ + 1, /* 1100 */ + 1, /* 1101 */ + 0, /* 1110 */ + 0 /* 1111 */ +}; + +#ifdef DEBUG +static const char *fs_err_msg[16] = { + "Normal", + "Reserved", + "Interrupt during debug service routine", + "Reserved", + "X Protection", + "TLB X miss (opword)", + "TLB X miss (ext. word)", + "IFP in emulator mode", + "W Protection", + "Write error", + "TLB W miss", + "Reserved", + "R Protection", + "R/RMW Protection", + "TLB R miss", + "OEP in emulator mode", +}; +#endif + +static inline void access_errorCF(struct frame *fp) +{ + unsigned long int mmusr, complainingAddress; + unsigned int err_code, fs; + int need_page_fault; + + mmusr = fp->ptregs.mmusr; + complainingAddress = fp->ptregs.mmuar; +#ifdef DEBUG + printk(KERN_DEBUG "pc %#lx, mmusr %#lx, complainingAddress %#lx\n", \ + fp->ptregs.pc, mmusr, complainingAddress); +#endif + + /* + * error_code: + * bit 0 == 0 means no page found, 1 means protection fault + * bit 1 == 0 means read, 1 means write + */ + + fs = (fp->ptregs.fs2 << 2) | fp->ptregs.fs1; + switch (fs) { + case 5: /* 0101 TLB opword X miss */ + need_page_fault = cf_tlb_miss(&fp->ptregs, 0, 0, 0); + complainingAddress = fp->ptregs.pc; + break; + case 6: /* 0110 TLB extension word X miss */ + need_page_fault = cf_tlb_miss(&fp->ptregs, 0, 0, 1); + complainingAddress = fp->ptregs.pc + sizeof(long); + break; + case 10: /* 1010 TLB W miss */ + need_page_fault = cf_tlb_miss(&fp->ptregs, 1, 1, 0); + break; + case 14: /* 1110 TLB R miss */ + need_page_fault = cf_tlb_miss(&fp->ptregs, 0, 1, 0); + break; + default: + /* 0000 Normal */ + /* 0001 Reserved */ + /* 0010 Interrupt during debug service routine */ + /* 0011 Reserved */ + /* 0100 X Protection */ + /* 0111 IFP in emulator mode */ + /* 1000 W Protection*/ + /* 1001 Write error*/ + /* 1011 Reserved*/ + /* 1100 R Protection*/ + /* 1101 R Protection*/ + /* 1111 OEP in emulator mode*/ + need_page_fault = 1; + break; + } + + if (need_page_fault) { + err_code = fs_err_code[fs]; + if ((fs == 13) && (mmusr & MMUSR_WF)) /* rd-mod-wr access */ + err_code |= 2; /* bit1 - write, bit0 - protection */ + do_page_fault(&fp->ptregs, complainingAddress, err_code); + } +} + +void die_if_kernel(char *str, struct pt_regs *fp, int nr) +{ + if (!(fp->sr & PS_S)) + return; + + console_verbose(); + printk(KERN_EMERG "%s: %08x\n", str, nr); + printk(KERN_EMERG "PC: [<%08lx>]", fp->pc); + print_symbol(" %s", fp->pc); + printk(KERN_EMERG "\nSR: %04x SP: %p a2: %08lx\n", + fp->sr, fp, fp->a2); + printk(KERN_EMERG "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", + fp->d0, fp->d1, fp->d2, fp->d3); + printk(KERN_EMERG "d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n", + fp->d4, fp->d5, fp->a0, fp->a1); + + printk(KERN_EMERG "Process %s (pid: %d, stackpage=%08lx)\n", + current->comm, current->pid, PAGE_SIZE+(unsigned long)current); + show_stack(NULL, (unsigned long *)fp); + do_exit(SIGSEGV); +} + +asmlinkage void buserr_c(struct frame *fp) +{ + unsigned int fs; + + /* Only set esp0 if coming from user mode */ + if (user_mode(&fp->ptregs)) + current->thread.esp0 = (unsigned long) fp; + + fs = (fp->ptregs.fs2 << 2) | fp->ptregs.fs1; +#if defined(DEBUG) + printk(KERN_DEBUG "*** Bus Error *** (%x)%s\n", fs, + fs_err_msg[fs & 0xf]); +#endif + switch (fs) { + case 0x5: + case 0x6: + case 0x7: + case 0x9: + case 0xa: + case 0xd: + case 0xe: + case 0xf: + access_errorCF(fp); + break; + default: + die_if_kernel("bad frame format", &fp->ptregs, 0); +#if defined(DEBUG) + printk(KERN_DEBUG "Unknown SIGSEGV - 4\n"); +#endif + force_sig(SIGSEGV, current); + } +} + + +int kstack_depth_to_print = 48; + +void show_stack(struct task_struct *task, unsigned long *stack) +{ + unsigned long *endstack, addr, symaddr; + extern char _start, _etext; + int i; + + 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 " "); + symaddr = *stack; + printk(KERN_EMERG " %08lx", *stack++); + if ((symaddr >= 0xc0000000) && (symaddr < 0xc1000000)) + print_symbol("(%s)", symaddr); + } + printk("\n"); + + printk(KERN_EMERG "Call Trace:"); + i = 0; + while (stack + 1 <= endstack) { + addr = *stack++; + /* + * 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 (((addr >= (unsigned long) &_start) && + (addr <= (unsigned long) &_etext))) { + if (i % 4 == 0) + printk("\n" KERN_EMERG " "); + printk(KERN_EMERG " [<%08lx>]", addr); + i++; + } + } + printk("\n"); +} + +void bad_super_trap(struct frame *fp) +{ + console_verbose(); + if (fp->ptregs.vector < 4*sizeof(vec_names)/sizeof(vec_names[0])) + printk(KERN_WARNING "*** %s *** FORMAT=%X\n", + vec_names[(fp->ptregs.vector) >> 2], + fp->ptregs.format); + else + printk(KERN_WARNING "*** Exception %d *** FORMAT=%X\n", + (fp->ptregs.vector) >> 2, + fp->ptregs.format); + printk(KERN_WARNING "Current process id is %d\n", current->pid); + die_if_kernel("BAD KERNEL TRAP", &fp->ptregs, 0); +} + +asmlinkage void trap_c(struct frame *fp) +{ + int sig; + siginfo_t info; + + if (fp->ptregs.sr & PS_S) { + if ((fp->ptregs.vector >> 2) == VEC_TRACE) { + /* traced a trapping instruction */ + current->ptrace |= PT_DTRACE; + } else + bad_super_trap(fp); + return; + } + + /* send the appropriate signal to the user program */ + switch ((fp->ptregs.vector) >> 2) { + case VEC_ADDRERR: + info.si_code = BUS_ADRALN; + sig = SIGBUS; + break; + case VEC_ILLEGAL: + case VEC_LINE10: + case VEC_LINE11: + info.si_code = ILL_ILLOPC; + sig = SIGILL; + break; + case VEC_PRIV: + info.si_code = ILL_PRVOPC; + sig = SIGILL; + break; + case VEC_COPROC: + info.si_code = ILL_COPROC; + sig = SIGILL; + break; + case VEC_TRAP1: /* gdbserver breakpoint */ + fp->ptregs.pc -= 2; + info.si_code = TRAP_TRACE; + sig = SIGTRAP; + break; + case VEC_TRAP2: + case VEC_TRAP3: + case VEC_TRAP4: + case VEC_TRAP5: + case VEC_TRAP6: + case VEC_TRAP7: + case VEC_TRAP8: + case VEC_TRAP9: + case VEC_TRAP10: + case VEC_TRAP11: + case VEC_TRAP12: + case VEC_TRAP13: + case VEC_TRAP14: + info.si_code = ILL_ILLTRP; + sig = SIGILL; + break; + case VEC_FPBRUC: + case VEC_FPOE: + case VEC_FPNAN: + info.si_code = FPE_FLTINV; + sig = SIGFPE; + break; + case VEC_FPIR: + info.si_code = FPE_FLTRES; + sig = SIGFPE; + break; + case VEC_FPDIVZ: + info.si_code = FPE_FLTDIV; + sig = SIGFPE; + break; + case VEC_FPUNDER: + info.si_code = FPE_FLTUND; + sig = SIGFPE; + break; + case VEC_FPOVER: + info.si_code = FPE_FLTOVF; + sig = SIGFPE; + break; + case VEC_ZERODIV: + info.si_code = FPE_INTDIV; + sig = SIGFPE; + break; + case VEC_CHK: + case VEC_TRAP: + info.si_code = FPE_INTOVF; + sig = SIGFPE; + break; + case VEC_TRACE: /* ptrace single step */ + info.si_code = TRAP_TRACE; + sig = SIGTRAP; + break; + case VEC_TRAP15: /* breakpoint */ + info.si_code = TRAP_BRKPT; + sig = SIGTRAP; + break; + default: + info.si_code = ILL_ILLOPC; + sig = SIGILL; + break; + } + info.si_signo = sig; + info.si_errno = 0; + switch (fp->ptregs.format) { + default: + info.si_addr = (void *) fp->ptregs.pc; + break; + case 2: + info.si_addr = (void *) fp->un.fmt2.iaddr; + break; + case 7: + info.si_addr = (void *) fp->un.fmt7.effaddr; + break; + case 9: + info.si_addr = (void *) fp->un.fmt9.iaddr; + break; + case 10: + info.si_addr = (void *) fp->un.fmta.daddr; + break; + case 11: + info.si_addr = (void *) fp->un.fmtb.daddr; + break; + } + force_sig_info(sig, &info, current); +} + +asmlinkage void set_esp0(unsigned long ssp) +{ + current->thread.esp0 = ssp; +} + +/* + * The architecture-independent backtrace generator + */ +void dump_stack(void) +{ + unsigned long stack; + + show_stack(current, &stack); +} +EXPORT_SYMBOL(dump_stack); + +#ifdef CONFIG_M68KFPU_EMU +asmlinkage void fpemu_signal(int signal, int code, void *addr) +{ + siginfo_t info; + + info.si_signo = signal; + info.si_errno = 0; + info.si_code = code; + info.si_addr = addr; + force_sig_info(signal, &info, current); +} +#endif --- /dev/null +++ b/arch/m68k/coldfire/vmlinux-cf.lds @@ -0,0 +1,92 @@ +/* ld script to make m68k Coldfire Linux kernel */ + +#include <asm-generic/vmlinux.lds.h> + +OUTPUT_FORMAT("elf32-m68k", "elf32-m68k", "elf32-m68k") +OUTPUT_ARCH(m68k) +ENTRY(_start) +jiffies = jiffies_64 + 4; +SECTIONS +{ + . = 0xC0020000; + _text = .; /* Text and read-only data */ + .text : { + *(.text.head) + TEXT_TEXT + SCHED_TEXT + LOCK_TEXT + *(.fixup) + *(.gnu.warning) + } :text = 0x4e75 + + _etext = .; /* End of text section */ + + . = ALIGN(16); + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + RODATA + + .data : { /* Data */ + DATA_DATA + CONSTRUCTORS + } + + .bss : { *(.bss) } /* BSS */ + + . = ALIGN(16); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } :data + + _edata = .; /* End of data section */ + + . = ALIGN(8192); /* Initrd */ + __init_begin = .; + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + .init.data : { *(.init.data) } + . = ALIGN(16); + __setup_start = .; + .init.setup : { *(.init.setup) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { + INITCALLS + } + __initcall_end = .; + __con_initcall_start = .; + .con_initcall.init : { *(.con_initcall.init) } + __con_initcall_end = .; + SECURITY_INIT +#ifdef CONFIG_BLK_DEV_INITRD + . = ALIGN(8192); + __initramfs_start = .; + .init.ramfs : { *(.init.ramfs) } + __initramfs_end = .; +#endif + . = ALIGN(8192); + __init_end = .; + + .data.init_task : { *(.data.init_task) } /* The initial task and kernel stack */ + + _end = . ; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.exit.text) + *(.exit.data) + *(.exitcall.exit) + } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +}