mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-30 21:44:27 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			4485 lines
		
	
	
		
			113 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			4485 lines
		
	
	
		
			113 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| 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) }
 | |
| +}
 |