Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/arch/m68knommu/platform/68360/Makefile b/arch/m68knommu/platform/68360/Makefile
new file mode 100644
index 0000000..cf5af73
--- /dev/null
+++ b/arch/m68knommu/platform/68360/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for arch/m68knommu/platform/68360.
+#
+
+obj-y := config.o commproc.o entry.o ints.o
+
+extra-y := head.o
+
+$(obj)/head.o: $(obj)/head-$(MODEL).o
+	ln -sf head-$(MODEL).o $(obj)/head.o
diff --git a/arch/m68knommu/platform/68360/commproc.c b/arch/m68knommu/platform/68360/commproc.c
new file mode 100644
index 0000000..6acb8d2
--- /dev/null
+++ b/arch/m68knommu/platform/68360/commproc.c
@@ -0,0 +1,308 @@
+/*
+ * General Purpose functions for the global management of the
+ * Communication Processor Module.
+ *
+ * Copyright (c) 2000 Michael Leslie <mleslie@lineo.com>
+ * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
+ *
+ * In addition to the individual control of the communication
+ * channels, there are a few functions that globally affect the
+ * communication processor.
+ *
+ * Buffer descriptors must be allocated from the dual ported memory
+ * space.  The allocator for that is here.  When the communication
+ * process is reset, we reclaim the memory available.  There is
+ * currently no deallocator for this memory.
+ * The amount of space available is platform dependent.  On the
+ * MBX, the EPPC software loads additional microcode into the
+ * communication processor, and uses some of the DP ram for this
+ * purpose.  Current, the first 512 bytes and the last 256 bytes of
+ * memory are used.  Right now I am conservative and only use the
+ * memory that can never be used for microcode.  If there are
+ * applications that require more DP ram, we can expand the boundaries
+ * but then we have to be careful of any downloaded microcode.
+ *
+ */
+
+/*
+ * Michael Leslie <mleslie@lineo.com>
+ * adapted Dan Malek's ppc8xx drivers to M68360
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <asm/m68360.h>
+#include <asm/commproc.h>
+
+/* #include <asm/page.h> */
+/* #include <asm/pgtable.h> */
+extern void *_quicc_base;
+extern unsigned int system_clock;
+
+
+static uint dp_alloc_base;	/* Starting offset in DP ram */
+static uint dp_alloc_top;	/* Max offset + 1 */
+
+#if 0
+static	void	*host_buffer;	/* One page of host buffer */
+static	void	*host_end;	    /* end + 1 */
+#endif
+
+/* struct  cpm360_t *cpmp; */         /* Pointer to comm processor space */
+
+QUICC  *pquicc;
+/* QUICC  *quicc_dpram; */ /* mleslie - temporary; use extern pquicc elsewhere instead */
+
+
+/* CPM interrupt vector functions. */
+struct	cpm_action {
+	void	(*handler)(void *);
+	void	*dev_id;
+};
+static	struct	cpm_action cpm_vecs[CPMVEC_NR];
+static	void	cpm_interrupt(int irq, void * dev, struct pt_regs * regs);
+static	void	cpm_error_interrupt(void *);
+
+/* prototypes: */
+void cpm_install_handler(int vec, void (*handler)(), void *dev_id);
+void m360_cpm_reset(void);
+
+
+
+
+void m360_cpm_reset()
+{
+/* 	pte_t		   *pte; */
+
+	pquicc = (struct quicc *)(_quicc_base); /* initialized in crt0_rXm.S */
+
+	/* Perform a CPM reset. */
+	pquicc->cp_cr = (SOFTWARE_RESET | CMD_FLAG);
+
+	/* Wait for CPM to become ready (should be 2 clocks). */
+	while (pquicc->cp_cr & CMD_FLAG);
+
+	/* On the recommendation of the 68360 manual, p. 7-60
+	 * - Set sdma interrupt service mask to 7
+	 * - Set sdma arbitration ID to 4
+	 */
+	pquicc->sdma_sdcr = 0x0740;
+
+
+	/* Claim the DP memory for our use.
+	 */
+	dp_alloc_base = CPM_DATAONLY_BASE;
+	dp_alloc_top = dp_alloc_base + CPM_DATAONLY_SIZE;
+
+
+	/* Set the host page for allocation.
+	 */
+	/* 	host_buffer = host_page_addr; */
+	/* 	host_end = host_page_addr + PAGE_SIZE; */
+
+	/* 	pte = find_pte(&init_mm, host_page_addr); */
+	/* 	pte_val(*pte) |= _PAGE_NO_CACHE; */
+	/* 	flush_tlb_page(current->mm->mmap, host_buffer); */
+	
+	/* Tell everyone where the comm processor resides.
+	*/
+/* 	cpmp = (cpm360_t *)commproc; */
+}
+
+
+/* This is called during init_IRQ.  We used to do it above, but this
+ * was too early since init_IRQ was not yet called.
+ */
+void
+cpm_interrupt_init(void)
+{
+	/* Initialize the CPM interrupt controller.
+	 * NOTE THAT pquicc had better have been initialized!
+	 * reference: MC68360UM p. 7-377
+	 */
+	pquicc->intr_cicr =
+		(CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) |
+		(CPM_INTERRUPT << 13) |
+		CICR_HP_MASK |
+		(CPM_VECTOR_BASE << 5) |
+		CICR_SPS;
+
+	/* mask all CPM interrupts from reaching the cpu32 core: */
+	pquicc->intr_cimr = 0;
+
+
+	/* mles - If I understand correctly, the 360 just pops over to the CPM
+	 * specific vector, obviating the necessity to vector through the IRQ
+	 * whose priority the CPM is set to. This needs a closer look, though.
+	 */
+
+	/* Set our interrupt handler with the core CPU. */
+/* 	if (request_irq(CPM_INTERRUPT, cpm_interrupt, 0, "cpm", NULL) != 0) */
+/* 		panic("Could not allocate CPM IRQ!"); */
+
+	/* Install our own error handler.
+	 */
+	/* I think we want to hold off on this one for the moment - mles */
+	/* cpm_install_handler(CPMVEC_ERROR, cpm_error_interrupt, NULL); */
+
+	/* master CPM interrupt enable */
+	/* pquicc->intr_cicr |= CICR_IEN; */ /* no such animal for 360 */
+}
+
+
+
+/* CPM interrupt controller interrupt.
+*/
+static	void
+cpm_interrupt(int irq, void * dev, struct pt_regs * regs)
+{
+	/* uint	vec; */
+
+	/* mles: Note that this stuff is currently being performed by
+	 * M68360_do_irq(int vec, struct pt_regs *fp), in ../ints.c  */
+
+	/* figure out the vector */
+	/* call that vector's handler */
+	/* clear the irq's bit in the service register */
+
+#if 0 /* old 860 stuff: */
+	/* Get the vector by setting the ACK bit and then reading
+	 * the register.
+	 */
+	((volatile immap_t *)IMAP_ADDR)->im_cpic.cpic_civr = 1;
+	vec = ((volatile immap_t *)IMAP_ADDR)->im_cpic.cpic_civr;
+	vec >>= 11;
+
+
+	if (cpm_vecs[vec].handler != 0)
+		(*cpm_vecs[vec].handler)(cpm_vecs[vec].dev_id);
+	else
+		((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr &= ~(1 << vec);
+
+	/* After servicing the interrupt, we have to remove the status
+	 * indicator.
+	 */
+	((immap_t *)IMAP_ADDR)->im_cpic.cpic_cisr |= (1 << vec);
+#endif
+	
+}
+
+/* The CPM can generate the error interrupt when there is a race condition
+ * between generating and masking interrupts.  All we have to do is ACK it
+ * and return.  This is a no-op function so we don't need any special
+ * tests in the interrupt handler.
+ */
+static	void
+cpm_error_interrupt(void *dev)
+{
+}
+
+/* Install a CPM interrupt handler.
+*/
+void
+cpm_install_handler(int vec, void (*handler)(), void *dev_id)
+{
+
+	request_irq(vec, handler, IRQ_FLG_LOCK, "timer", dev_id);
+
+/* 	if (cpm_vecs[vec].handler != 0) */
+/* 		printk(KERN_INFO "CPM interrupt %x replacing %x\n", */
+/* 			(uint)handler, (uint)cpm_vecs[vec].handler); */
+/* 	cpm_vecs[vec].handler = handler; */
+/* 	cpm_vecs[vec].dev_id = dev_id; */
+
+	/*              ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr |= (1 << vec); */
+/* 	pquicc->intr_cimr |= (1 << vec); */
+
+}
+
+/* Free a CPM interrupt handler.
+*/
+void
+cpm_free_handler(int vec)
+{
+	cpm_vecs[vec].handler = NULL;
+	cpm_vecs[vec].dev_id = NULL;
+	/* ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr &= ~(1 << vec); */
+	pquicc->intr_cimr &= ~(1 << vec);
+}
+
+
+
+
+/* Allocate some memory from the dual ported ram.  We may want to
+ * enforce alignment restrictions, but right now everyone is a good
+ * citizen.
+ */
+uint
+m360_cpm_dpalloc(uint size)
+{
+        uint    retloc;
+
+        if ((dp_alloc_base + size) >= dp_alloc_top)
+                return(CPM_DP_NOSPACE);
+
+        retloc = dp_alloc_base;
+        dp_alloc_base += size;
+
+        return(retloc);
+}
+
+
+#if 0 /* mleslie - for now these are simply kmalloc'd */
+/* We also own one page of host buffer space for the allocation of
+ * UART "fifos" and the like.
+ */
+uint
+m360_cpm_hostalloc(uint size)
+{
+	uint	retloc;
+
+	if ((host_buffer + size) >= host_end)
+		return(0);
+
+	retloc = host_buffer;
+	host_buffer += size;
+
+	return(retloc);
+}
+#endif
+
+
+/* Set a baud rate generator.  This needs lots of work.  There are
+ * four BRGs, any of which can be wired to any channel.
+ * The internal baud rate clock is the system clock divided by 16.
+ * This assumes the baudrate is 16x oversampled by the uart.
+ */
+/* #define BRG_INT_CLK	(((bd_t *)__res)->bi_intfreq * 1000000) */
+#define BRG_INT_CLK		system_clock
+#define BRG_UART_CLK	(BRG_INT_CLK/16)
+
+void
+m360_cpm_setbrg(uint brg, uint rate)
+{
+	volatile uint	*bp;
+
+	/* This is good enough to get SMCs running.....
+	 */
+	/* bp = (uint *)&cpmp->cp_brgc1; */
+	bp = (volatile uint *)(&pquicc->brgc[0].l);
+	bp += brg;
+	*bp = ((BRG_UART_CLK / rate - 1) << 1) | CPM_BRG_EN;
+}
+
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/arch/m68knommu/platform/68360/config.c b/arch/m68knommu/platform/68360/config.c
new file mode 100644
index 0000000..3db2446
--- /dev/null
+++ b/arch/m68knommu/platform/68360/config.c
@@ -0,0 +1,208 @@
+/*
+ *  linux/arch/m68knommu/platform/68360/config.c
+ *
+ *  Copyright (c) 2000 Michael Leslie <mleslie@lineo.com>
+ *  Copyright (C) 1993 Hamish Macdonald
+ *  Copyright (C) 1999 D. Jeff Dionne <jeff@uclinux.org>
+ *
+ * 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 <stdarg.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+
+#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+#include <asm/machdep.h>
+#include <asm/m68360.h>
+
+#ifdef CONFIG_UCQUICC
+#include <asm/bootstd.h>
+#endif
+
+extern void m360_cpm_reset(void);
+
+// Mask to select if the PLL prescaler is enabled.
+#define MCU_PREEN   ((unsigned short)(0x0001 << 13))
+
+#if defined(CONFIG_UCQUICC)
+#define OSCILLATOR  (unsigned long int)33000000
+#endif
+
+unsigned long int system_clock;
+
+void M68360_init_IRQ(void);
+
+extern QUICC *pquicc;
+
+/* TODO  DON"T Hard Code this */
+/* calculate properly using the right PLL and prescaller */
+// unsigned int system_clock = 33000000l;
+extern unsigned long int system_clock; //In kernel setup.c
+
+extern void config_M68360_irq(void);
+
+void BSP_sched_init(void (*timer_routine)(int, void *, struct pt_regs *))
+{
+  unsigned char prescaler;
+  unsigned short tgcr_save;
+  int return_value;
+
+#if 0
+  /* Restart mode, Enable int, 32KHz, Enable timer */
+  TCTL = TCTL_OM | TCTL_IRQEN | TCTL_CLKSOURCE_32KHZ | TCTL_TEN;
+  /* Set prescaler (Divide 32KHz by 32)*/
+  TPRER = 31;
+  /* Set compare register  32Khz / 32 / 10 = 100 */
+  TCMP = 10;                                                              
+
+  request_irq(IRQ_MACHSPEC | 1, timer_routine, IRQ_FLG_LOCK, "timer", NULL);
+#endif
+
+  /* General purpose quicc timers: MC68360UM p7-20 */
+
+  /* Set up timer 1 (in [1..4]) to do 100Hz */
+  tgcr_save = pquicc->timer_tgcr & 0xfff0;
+  pquicc->timer_tgcr  = tgcr_save; /* stop and reset timer 1 */
+  /* pquicc->timer_tgcr |= 0x4444; */ /* halt timers when FREEZE (ie bdm freeze) */
+
+  prescaler = 8;
+  pquicc->timer_tmr1 = 0x001a | /* or=1, frr=1, iclk=01b */
+                           (unsigned short)((prescaler - 1) << 8);
+    
+  pquicc->timer_tcn1 = 0x0000; /* initial count */
+  /* calculate interval for 100Hz based on the _system_clock: */
+  pquicc->timer_trr1 = (system_clock/ prescaler) / HZ; /* reference count */
+
+  pquicc->timer_ter1 = 0x0003; /* clear timer events */
+
+  /* enable timer 1 interrupt in CIMR */
+//  request_irq(IRQ_MACHSPEC | CPMVEC_TIMER1, timer_routine, IRQ_FLG_LOCK, "timer", NULL);
+  //return_value = request_irq( CPMVEC_TIMER1, timer_routine, IRQ_FLG_LOCK, "timer", NULL);
+  return_value = request_irq(CPMVEC_TIMER1 , timer_routine, IRQ_FLG_LOCK,
+          "Timer", NULL);
+
+  /* Start timer 1: */
+  tgcr_save = (pquicc->timer_tgcr & 0xfff0) | 0x0001;
+  pquicc->timer_tgcr  = tgcr_save;
+}
+
+
+void BSP_tick(void)
+{
+  /* Reset Timer1 */
+  /* TSTAT &= 0; */
+
+  pquicc->timer_ter1 = 0x0002; /* clear timer event */  
+}
+
+unsigned long BSP_gettimeoffset (void)
+{
+  return 0;
+}
+
+void BSP_gettod (int *yearp, int *monp, int *dayp,
+		   int *hourp, int *minp, int *secp)
+{
+}
+
+int BSP_hwclk(int op, struct hwclk_time *t)
+{
+  if (!op) {
+    /* read */
+  } else {
+    /* write */
+  }
+  return 0;
+}
+
+int BSP_set_clock_mmss (unsigned long nowtime)
+{
+#if 0
+  short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
+
+  tod->second1 = real_seconds / 10;
+  tod->second2 = real_seconds % 10;
+  tod->minute1 = real_minutes / 10;
+  tod->minute2 = real_minutes % 10;
+#endif
+  return 0;
+}
+
+void BSP_reset (void)
+{
+  local_irq_disable();
+  asm volatile ("
+    moveal #_start, %a0;
+    moveb #0, 0xFFFFF300;
+    moveal 0(%a0), %sp;
+    moveal 4(%a0), %a0;
+    jmp (%a0);
+    ");
+}
+
+unsigned char *scc1_hwaddr;
+static int errno;
+
+#if defined (CONFIG_UCQUICC)
+_bsc0(char *, getserialnum)
+_bsc1(unsigned char *, gethwaddr, int, a)
+_bsc1(char *, getbenv, char *, a)
+#endif
+
+
+void config_BSP(char *command, int len)
+{
+  unsigned char *p;
+
+  m360_cpm_reset();
+
+  /* Calculate the real system clock value. */
+  {
+     unsigned int local_pllcr = (unsigned int)(pquicc->sim_pllcr);
+     if( local_pllcr & MCU_PREEN ) // If the prescaler is dividing by 128
+     {
+         int mf = (int)(pquicc->sim_pllcr & 0x0fff);
+         system_clock = (OSCILLATOR / 128) * (mf + 1);
+     }
+     else
+     {
+         int mf = (int)(pquicc->sim_pllcr & 0x0fff);
+         system_clock = (OSCILLATOR) * (mf + 1);
+     }
+  }
+
+  printk(KERN_INFO "\n68360 QUICC support (C) 2000 Lineo Inc.\n");
+
+#if defined(CONFIG_UCQUICC) && 0
+  printk(KERN_INFO "uCquicc serial string [%s]\n",getserialnum());
+  p = scc1_hwaddr = gethwaddr(0);
+  printk(KERN_INFO "uCquicc hwaddr %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
+         p[0], p[1], p[2], p[3], p[4], p[5]);
+
+  p = getbenv("APPEND");
+  if (p)
+    strcpy(p,command);
+  else
+    command[0] = 0;
+#else
+  scc1_hwaddr = "\00\01\02\03\04\05";
+#endif
+ 
+  mach_sched_init      = BSP_sched_init;
+  mach_tick            = BSP_tick;
+  mach_gettimeoffset   = BSP_gettimeoffset;
+  mach_gettod          = BSP_gettod;
+  mach_hwclk           = NULL;
+  mach_set_clock_mmss  = NULL;
+  mach_reset           = BSP_reset;
+}
diff --git a/arch/m68knommu/platform/68360/entry.S b/arch/m68knommu/platform/68360/entry.S
new file mode 100644
index 0000000..f7bc80a
--- /dev/null
+++ b/arch/m68knommu/platform/68360/entry.S
@@ -0,0 +1,184 @@
+/*
+ *  linux/arch/m68knommu/platform/68360/entry.S
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *  Copyright (C) 2001 SED Systems, a Division of Calian Ltd.
+ *
+ * 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
+ * M68360 Port by SED Systems, and Lineo.
+ */
+
+#include <linux/config.h>
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/thread_info.h>
+#include <asm/unistd.h>
+#include <asm/errno.h>
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/traps.h>
+#include <asm/asm-offsets.h>
+#include <asm/entry.h>
+
+.text
+
+.globl system_call
+.globl resume
+.globl ret_from_exception
+.globl ret_from_signal
+.globl sys_call_table
+.globl ret_from_interrupt
+.globl bad_interrupt
+.globl inthandler
+
+badsys:
+	movel	#-ENOSYS,%sp@(PT_D0)
+	jra	ret_from_exception
+
+do_trace:
+	movel	#-ENOSYS,%sp@(PT_D0)	/* needed for strace*/
+	subql	#4,%sp
+	SAVE_SWITCH_STACK
+	jbsr	syscall_trace
+	RESTORE_SWITCH_STACK
+	addql	#4,%sp
+	movel	%sp@(PT_ORIG_D0),%d1
+	movel	#-ENOSYS,%d0
+	cmpl	#NR_syscalls,%d1
+	jcc	1f
+	lsl	#2,%d1
+	lea	sys_call_table, %a0
+	jbsr	%a0@(%d1)
+
+1:	movel	%d0,%sp@(PT_D0)		/* save the return value */
+	subql	#4,%sp			/* dummy return address */
+	SAVE_SWITCH_STACK
+	jbsr	syscall_trace
+
+ret_from_signal:
+	RESTORE_SWITCH_STACK
+	addql	#4,%sp
+	jra	ret_from_exception
+
+ENTRY(system_call)
+	SAVE_ALL
+
+	/* save top of frame*/
+	pea	%sp@
+	jbsr	set_esp0
+	addql	#4,%sp
+
+	btst	#PF_TRACESYS_BIT,%a2@(TASK_FLAGS+PF_TRACESYS_OFF)
+	jne	do_trace
+	cmpl	#NR_syscalls,%d0
+	jcc	badsys
+	lsl	#2,%d0
+	lea	sys_call_table,%a0
+	movel	%a0@(%d0), %a0
+	jbsr	%a0@
+	movel	%d0,%sp@(PT_D0)		/* save the return value*/
+
+ret_from_exception:
+	btst	#5,%sp@(PT_SR)		/* check if returning to kernel*/
+	jeq	Luser_return		/* if so, skip resched, signals*/
+
+Lkernel_return:
+	RESTORE_ALL
+
+Luser_return:
+	/* only allow interrupts when we are really the last one on the*/
+	/* kernel stack, otherwise stack overflow can occur during*/
+	/* heavy interrupt load*/
+	andw	#ALLOWINT,%sr
+
+	movel	%sp,%d1			/* get thread_info pointer */
+	andl	#0xffffe000,%d1
+	movel	%d1,%a2
+	move	%a2@(TI_FLAGS),%d1	/* thread_info->flags */
+	andl	#_TIF_WORK_MASK,%d1
+	jne	Lwork_to_do
+	RESTORE_ALL
+
+Lwork_to_do:
+	movel	%a2@(TI_FLAGS),%d1	/* thread_info->flags */
+	btst	#TIF_NEED_RESCHED,%d1
+	jne	reschedule
+
+Lsignal_return:
+	subql	#4,%sp			/* dummy return address*/
+	SAVE_SWITCH_STACK
+	pea	%sp@(SWITCH_STACK_SIZE)
+	clrl	%sp@-
+	bsrw	do_signal
+	addql	#8,%sp
+	RESTORE_SWITCH_STACK
+	addql	#4,%sp
+Lreturn:
+	RESTORE_ALL
+
+/*
+ * This is the main interrupt handler, responsible for calling process_int()
+ */
+inthandler:
+	SAVE_ALL
+	addql	#1,local_irq_count	/*  put exception # in d0*/
+	movew	%sp@(PT_VECTOR), %d0
+	and.l	#0x3ff, %d0
+	lsr.l   #0x02,  %d0
+
+	movel	%sp,%sp@-
+	movel	%d0,%sp@- 		/*  put vector # on stack*/
+	jbsr	process_int		/*  process the IRQ*/
+3:     	addql	#8,%sp			/*  pop parameters off stack*/
+	bra	ret_from_interrupt
+
+ret_from_interrupt:
+	subql	#1,local_irq_count
+	jeq	1f
+2:
+	RESTORE_ALL
+1:
+	moveb	%sp@(PT_SR), %d0
+	and	#7, %d0
+	jhi	2b
+	/* check if we need to do software interrupts */
+
+	movel	irq_stat+CPUSTAT_SOFTIRQ_PENDING,%d0
+	jeq	ret_from_exception
+
+	pea	ret_from_exception
+	jra	do_softirq
+
+
+/*
+ * Handler for uninitialized and spurious interrupts.
+ */
+bad_interrupt:
+	addql	#1,num_spurious
+	rte
+
+/*
+ * 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.
+ */
+ENTRY(resume)
+	movel	%a0,%d1				/* save prev thread in d1 */
+	movew	%sr,%a0@(TASK_THREAD+THREAD_SR)	/* save sr */
+	movel	%usp,%a2			/* save usp */
+	movel	%a2,%a0@(TASK_THREAD+THREAD_USP)
+
+	SAVE_SWITCH_STACK
+	movel	%sp,%a0@(TASK_THREAD+THREAD_KSP) /* save kernel stack */
+	movel	%a1@(TASK_THREAD+THREAD_KSP),%sp /* restore new thread stack */
+	RESTORE_SWITCH_STACK
+
+	movel	%a1@(TASK_THREAD+THREAD_USP),%a0 /* restore user stack */
+	movel	%a0,%usp
+	movew	%a1@(TASK_THREAD+THREAD_SR),%sr	/* restore thread status reg */
+	rts
+
diff --git a/arch/m68knommu/platform/68360/ints.c b/arch/m68knommu/platform/68360/ints.c
new file mode 100644
index 0000000..ba184db
--- /dev/null
+++ b/arch/m68knommu/platform/68360/ints.c
@@ -0,0 +1,335 @@
+/*
+ * linux/arch/$(ARCH)/platform/$(PLATFORM)/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.
+ *
+ * Copyright (c) 2000  Michael Leslie <mleslie@lineo.com>
+ * Copyright (c) 1996 Roman Zippel
+ * Copyright (c) 1999 D. Jeff Dionne <jeff@uclinux.org>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/traps.h>
+#include <asm/io.h>
+#include <asm/machdep.h>
+#include <asm/setup.h>
+#include <asm/m68360.h>
+
+/* from quicc/commproc.c: */
+extern QUICC *pquicc;
+extern void cpm_interrupt_init(void);
+
+#define INTERNAL_IRQS (96)
+
+/* assembler routines */
+asmlinkage void system_call(void);
+asmlinkage void buserr(void);
+asmlinkage void trap(void);
+asmlinkage irqreturn_t bad_interrupt(void);
+asmlinkage irqreturn_t inthandler(void);
+
+extern void *_ramvec[];
+
+/* The number of spurious interrupts */
+volatile unsigned int num_spurious;
+unsigned int local_irq_count[NR_CPUS];
+
+/* irq node variables for the 32 (potential) on chip sources */
+static irq_node_t int_irq_list[INTERNAL_IRQS];
+
+static short int_irq_ablecount[INTERNAL_IRQS];
+
+/*
+ * This function should be called during kernel startup to initialize
+ * IRQ handling routines.
+ */
+
+void init_IRQ(void)
+{
+	int i;
+	int vba = (CPM_VECTOR_BASE<<4);
+
+	/* set up the vectors */
+	_ramvec[2] = buserr;
+	_ramvec[3] = trap;
+	_ramvec[4] = trap;
+	_ramvec[5] = trap;
+	_ramvec[6] = trap;
+	_ramvec[7] = trap;
+	_ramvec[8] = trap;
+	_ramvec[9] = trap;
+	_ramvec[10] = trap;
+	_ramvec[11] = trap;
+	_ramvec[12] = trap;
+	_ramvec[13] = trap;
+	_ramvec[14] = trap;
+	_ramvec[15] = trap;
+
+	_ramvec[32] = system_call;
+	_ramvec[33] = trap;
+
+
+	cpm_interrupt_init();
+
+	/* set up CICR for vector base address and irq level */
+	/* irl = 4, hp = 1f - see MC68360UM p 7-377 */
+	pquicc->intr_cicr = 0x00e49f00 | vba;
+
+	/* CPM interrupt vectors: (p 7-376) */
+	_ramvec[vba+CPMVEC_ERROR]       = bad_interrupt; /* Error */
+	_ramvec[vba+CPMVEC_PIO_PC11]    = inthandler;   /* pio - pc11 */
+	_ramvec[vba+CPMVEC_PIO_PC10]    = inthandler;   /* pio - pc10 */
+	_ramvec[vba+CPMVEC_SMC2]        = inthandler;   /* smc2/pip */
+	_ramvec[vba+CPMVEC_SMC1]        = inthandler;   /* smc1 */
+	_ramvec[vba+CPMVEC_SPI]         = inthandler;   /* spi */
+	_ramvec[vba+CPMVEC_PIO_PC9]     = inthandler;   /* pio - pc9 */
+	_ramvec[vba+CPMVEC_TIMER4]      = inthandler;   /* timer 4 */
+	_ramvec[vba+CPMVEC_RESERVED1]   = inthandler;   /* reserved */
+	_ramvec[vba+CPMVEC_PIO_PC8]     = inthandler;   /* pio - pc8 */
+	_ramvec[vba+CPMVEC_PIO_PC7]     = inthandler;  /* pio - pc7 */
+	_ramvec[vba+CPMVEC_PIO_PC6]     = inthandler;  /* pio - pc6 */
+	_ramvec[vba+CPMVEC_TIMER3]      = inthandler;  /* timer 3 */
+	_ramvec[vba+CPMVEC_RISCTIMER]   = inthandler;  /* reserved */
+	_ramvec[vba+CPMVEC_PIO_PC5]     = inthandler;  /* pio - pc5 */
+	_ramvec[vba+CPMVEC_PIO_PC4]     = inthandler;  /* pio - pc4 */
+	_ramvec[vba+CPMVEC_RESERVED2]   = inthandler;  /* reserved */
+	_ramvec[vba+CPMVEC_RISCTIMER]   = inthandler;  /* timer table */
+	_ramvec[vba+CPMVEC_TIMER2]      = inthandler;  /* timer 2 */
+	_ramvec[vba+CPMVEC_RESERVED3]   = inthandler;  /* reserved */
+	_ramvec[vba+CPMVEC_IDMA2]       = inthandler;  /* idma 2 */
+	_ramvec[vba+CPMVEC_IDMA1]       = inthandler;  /* idma 1 */
+	_ramvec[vba+CPMVEC_SDMA_CB_ERR] = inthandler;  /* sdma channel bus error */
+	_ramvec[vba+CPMVEC_PIO_PC3]     = inthandler;  /* pio - pc3 */
+	_ramvec[vba+CPMVEC_PIO_PC2]     = inthandler;  /* pio - pc2 */
+	/* _ramvec[vba+CPMVEC_TIMER1]      = cpm_isr_timer1; */  /* timer 1 */
+	_ramvec[vba+CPMVEC_TIMER1]      = inthandler;  /* timer 1 */
+	_ramvec[vba+CPMVEC_PIO_PC1]     = inthandler;  /* pio - pc1 */
+	_ramvec[vba+CPMVEC_SCC4]        = inthandler;  /* scc 4 */
+	_ramvec[vba+CPMVEC_SCC3]        = inthandler;  /* scc 3 */
+	_ramvec[vba+CPMVEC_SCC2]        = inthandler;  /* scc 2 */
+	_ramvec[vba+CPMVEC_SCC1]        = inthandler;  /* scc 1 */
+	_ramvec[vba+CPMVEC_PIO_PC0]     = inthandler;  /* pio - pc0 */
+
+
+	/* turn off all CPM interrupts */
+	pquicc->intr_cimr = 0x00000000;
+
+	/* initialize handlers */
+	for (i = 0; i < INTERNAL_IRQS; i++) {
+		int_irq_list[i].handler = NULL;
+		int_irq_list[i].flags   = IRQ_FLG_STD;
+		int_irq_list[i].dev_id  = NULL;
+		int_irq_list[i].devname = NULL;
+	}
+}
+
+#if 0
+void M68360_insert_irq(irq_node_t **list, irq_node_t *node)
+{
+	unsigned long flags;
+	irq_node_t *cur;
+
+	if (!node->dev_id)
+		printk(KERN_INFO "%s: Warning: dev_id of %s is zero\n",
+		       __FUNCTION__, node->devname);
+
+	local_irq_save(flags);
+
+	cur = *list;
+
+	while (cur) {
+		list = &cur->next;
+		cur = cur->next;
+	}
+
+	node->next = cur;
+	*list = node;
+
+	local_irq_restore(flags);
+}
+
+void M68360_delete_irq(irq_node_t **list, void *dev_id)
+{
+	unsigned long flags;
+	irq_node_t *node;
+
+	local_irq_save(flags);
+
+	for (node = *list; node; list = &node->next, node = *list) {
+		if (node->dev_id == dev_id) {
+			*list = node->next;
+			/* Mark it as free. */
+			node->handler = NULL;
+			local_irq_restore(flags);
+			return;
+		}
+	}
+	local_irq_restore(flags);
+	printk (KERN_INFO "%s: tried to remove invalid irq\n", __FUNCTION__);
+}
+#endif
+
+int request_irq(
+	unsigned int irq,
+	irqreturn_t (*handler)(int, void *, struct pt_regs *),
+	unsigned long flags,
+	const char *devname,
+	void *dev_id)
+{
+	int mask = (1<<irq);
+
+	irq += (CPM_VECTOR_BASE<<4);
+
+	if (irq >= INTERNAL_IRQS) {
+		printk (KERN_ERR "%s: Unknown IRQ %d from %s\n", __FUNCTION__, irq, devname);
+		return -ENXIO;
+	}
+
+	if (!(int_irq_list[irq].flags & IRQ_FLG_STD)) {
+		if (int_irq_list[irq].flags & IRQ_FLG_LOCK) {
+			printk(KERN_ERR "%s: IRQ %d from %s is not replaceable\n",
+			       __FUNCTION__, irq, int_irq_list[irq].devname);
+			return -EBUSY;
+		}
+		if (flags & IRQ_FLG_REPLACE) {
+			printk(KERN_ERR "%s: %s can't replace IRQ %d from %s\n",
+			       __FUNCTION__, devname, irq, int_irq_list[irq].devname);
+			return -EBUSY;
+		}
+	}
+	int_irq_list[irq].handler = handler;
+	int_irq_list[irq].flags   = flags;
+	int_irq_list[irq].dev_id  = dev_id;
+	int_irq_list[irq].devname = devname;
+
+	/* enable in the CIMR */
+	if (!int_irq_ablecount[irq])
+		pquicc->intr_cimr |= mask;
+	/*      *(volatile unsigned long *)0xfffff304 &= ~(1<<irq); */
+
+	return 0;
+}
+
+EXPORT_SYMBOL(request_irq);
+
+void free_irq(unsigned int irq, void *dev_id)
+{
+	if (irq >= INTERNAL_IRQS) {
+		printk (KERN_ERR "%s: Unknown IRQ %d\n", __FUNCTION__, irq);
+		return;
+	}
+
+	if (int_irq_list[irq].dev_id != dev_id)
+		printk(KERN_INFO "%s: removing probably wrong IRQ %d from %s\n",
+		       __FUNCTION__, irq, int_irq_list[irq].devname);
+	int_irq_list[irq].handler = NULL;
+	int_irq_list[irq].flags   = IRQ_FLG_STD;
+	int_irq_list[irq].dev_id  = NULL;
+	int_irq_list[irq].devname = NULL;
+
+	*(volatile unsigned long *)0xfffff304 |= 1<<irq;
+}
+
+EXPORT_SYMBOL(free_irq);
+
+#if 0
+/*
+ * Enable/disable a particular machine specific interrupt source.
+ * Note that this may affect other interrupts in case of a shared interrupt.
+ * This function should only be called for a _very_ short time to change some
+ * internal data, that may not be changed by the interrupt at the same time.
+ * int_(enable|disable)_irq calls may also be nested.
+ */
+void M68360_enable_irq(unsigned int irq)
+{
+	if (irq >= INTERNAL_IRQS) {
+		printk(KERN_ERR "%s: Unknown IRQ %d\n", __FUNCTION__, irq);
+		return;
+	}
+
+	if (--int_irq_ablecount[irq])
+		return;
+
+	/* enable the interrupt */
+	*(volatile unsigned long *)0xfffff304 &= ~(1<<irq);
+}
+
+void M68360_disable_irq(unsigned int irq)
+{
+	if (irq >= INTERNAL_IRQS) {
+		printk(KERN_ERR "%s: Unknown IRQ %d\n", __FUNCTION__, irq);
+		return;
+	}
+
+	if (int_irq_ablecount[irq]++)
+		return;
+
+	/* disable the interrupt */
+	*(volatile unsigned long *)0xfffff304 |= 1<<irq;
+}
+#endif
+
+int show_interrupts(struct seq_file *p, void *v)
+{
+	int i = *(loff_t *) v;
+
+	if (i < NR_IRQS) {
+		if (int_irq_list[i].devname) {
+			seq_printf(p, "%3d: %10u ", i, kstat_cpu(0).irqs[i]);
+			if (int_irq_list[i].flags & IRQ_FLG_LOCK)
+				seq_printf(p, "L ");
+			else
+				seq_printf(p, "  ");
+			seq_printf(p, "%s\n", int_irq_list[i].devname);
+		}
+	}
+	if (i == NR_IRQS)
+		seq_printf(p, "   : %10u   spurious\n", num_spurious);
+
+	return 0;
+}
+
+/* The 68k family did not have a good way to determine the source
+ * of interrupts until later in the family.  The EC000 core does
+ * not provide the vector number on the stack, we vector everything
+ * into one vector and look in the blasted mask register...
+ * This code is designed to be fast, almost constant time, not clean!
+ */
+void process_int(int vec, struct pt_regs *fp)
+{
+	int irq;
+	int mask;
+
+	/* unsigned long pend = *(volatile unsigned long *)0xfffff30c; */
+
+	/* irq = vec + (CPM_VECTOR_BASE<<4); */
+	irq = vec;
+
+	/* unsigned long pend = *(volatile unsigned long *)pquicc->intr_cipr; */
+
+	/* Bugger all that weirdness. For the moment, I seem to know where I came from;
+	 * vec is passed from a specific ISR, so I'll use it. */
+
+	if (int_irq_list[irq].handler) {
+		int_irq_list[irq].handler(irq , int_irq_list[irq].dev_id, fp);
+		kstat_cpu(0).irqs[irq]++;
+		pquicc->intr_cisr = (1 << vec); /* indicate that irq has been serviced */
+	} else {
+		printk(KERN_ERR "unregistered interrupt %d!\nTurning it off in the CIMR...\n", irq);
+		/* *(volatile unsigned long *)0xfffff304 |= mask; */
+		pquicc->intr_cimr &= ~(1 << vec);
+		num_spurious += 1;
+	}
+	return(IRQ_HANDLED);
+}