aboutsummaryrefslogtreecommitdiff
path: root/arch/m68knommu/platform/68360
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/m68knommu/platform/68360
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!
Diffstat (limited to 'arch/m68knommu/platform/68360')
-rw-r--r--arch/m68knommu/platform/68360/Makefile10
-rw-r--r--arch/m68knommu/platform/68360/commproc.c308
-rw-r--r--arch/m68knommu/platform/68360/config.c208
-rw-r--r--arch/m68knommu/platform/68360/entry.S184
-rw-r--r--arch/m68knommu/platform/68360/ints.c335
5 files changed, 1045 insertions, 0 deletions
diff --git a/arch/m68knommu/platform/68360/Makefile b/arch/m68knommu/platform/68360/Makefile
new file mode 100644
index 00000000000..cf5af73a578
--- /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 00000000000..6acb8d294cb
--- /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 00000000000..3db244625f0
--- /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 00000000000..f7bc80a60e0
--- /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 00000000000..ba184db1651
--- /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);
+}