aboutsummaryrefslogtreecommitdiffstats
path: root/arch/m68knommu/platform/68360
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/m68knommu/platform/68360
Linux-2.6.12-rc2v2.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 000000000000..cf5af73a5789
--- /dev/null
+++ b/arch/m68knommu/platform/68360/Makefile
@@ -0,0 +1,10 @@
1#
2# Makefile for arch/m68knommu/platform/68360.
3#
4
5obj-y := config.o commproc.o entry.o ints.o
6
7extra-y := head.o
8
9$(obj)/head.o: $(obj)/head-$(MODEL).o
10 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 000000000000..6acb8d294cb6
--- /dev/null
+++ b/arch/m68knommu/platform/68360/commproc.c
@@ -0,0 +1,308 @@
1/*
2 * General Purpose functions for the global management of the
3 * Communication Processor Module.
4 *
5 * Copyright (c) 2000 Michael Leslie <mleslie@lineo.com>
6 * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
7 *
8 * In addition to the individual control of the communication
9 * channels, there are a few functions that globally affect the
10 * communication processor.
11 *
12 * Buffer descriptors must be allocated from the dual ported memory
13 * space. The allocator for that is here. When the communication
14 * process is reset, we reclaim the memory available. There is
15 * currently no deallocator for this memory.
16 * The amount of space available is platform dependent. On the
17 * MBX, the EPPC software loads additional microcode into the
18 * communication processor, and uses some of the DP ram for this
19 * purpose. Current, the first 512 bytes and the last 256 bytes of
20 * memory are used. Right now I am conservative and only use the
21 * memory that can never be used for microcode. If there are
22 * applications that require more DP ram, we can expand the boundaries
23 * but then we have to be careful of any downloaded microcode.
24 *
25 */
26
27/*
28 * Michael Leslie <mleslie@lineo.com>
29 * adapted Dan Malek's ppc8xx drivers to M68360
30 *
31 */
32
33#include <linux/errno.h>
34#include <linux/sched.h>
35#include <linux/kernel.h>
36#include <linux/param.h>
37#include <linux/string.h>
38#include <linux/mm.h>
39#include <linux/interrupt.h>
40#include <asm/irq.h>
41#include <asm/m68360.h>
42#include <asm/commproc.h>
43
44/* #include <asm/page.h> */
45/* #include <asm/pgtable.h> */
46extern void *_quicc_base;
47extern unsigned int system_clock;
48
49
50static uint dp_alloc_base; /* Starting offset in DP ram */
51static uint dp_alloc_top; /* Max offset + 1 */
52
53#if 0
54static void *host_buffer; /* One page of host buffer */
55static void *host_end; /* end + 1 */
56#endif
57
58/* struct cpm360_t *cpmp; */ /* Pointer to comm processor space */
59
60QUICC *pquicc;
61/* QUICC *quicc_dpram; */ /* mleslie - temporary; use extern pquicc elsewhere instead */
62
63
64/* CPM interrupt vector functions. */
65struct cpm_action {
66 void (*handler)(void *);
67 void *dev_id;
68};
69static struct cpm_action cpm_vecs[CPMVEC_NR];
70static void cpm_interrupt(int irq, void * dev, struct pt_regs * regs);
71static void cpm_error_interrupt(void *);
72
73/* prototypes: */
74void cpm_install_handler(int vec, void (*handler)(), void *dev_id);
75void m360_cpm_reset(void);
76
77
78
79
80void m360_cpm_reset()
81{
82/* pte_t *pte; */
83
84 pquicc = (struct quicc *)(_quicc_base); /* initialized in crt0_rXm.S */
85
86 /* Perform a CPM reset. */
87 pquicc->cp_cr = (SOFTWARE_RESET | CMD_FLAG);
88
89 /* Wait for CPM to become ready (should be 2 clocks). */
90 while (pquicc->cp_cr & CMD_FLAG);
91
92 /* On the recommendation of the 68360 manual, p. 7-60
93 * - Set sdma interrupt service mask to 7
94 * - Set sdma arbitration ID to 4
95 */
96 pquicc->sdma_sdcr = 0x0740;
97
98
99 /* Claim the DP memory for our use.
100 */
101 dp_alloc_base = CPM_DATAONLY_BASE;
102 dp_alloc_top = dp_alloc_base + CPM_DATAONLY_SIZE;
103
104
105 /* Set the host page for allocation.
106 */
107 /* host_buffer = host_page_addr; */
108 /* host_end = host_page_addr + PAGE_SIZE; */
109
110 /* pte = find_pte(&init_mm, host_page_addr); */
111 /* pte_val(*pte) |= _PAGE_NO_CACHE; */
112 /* flush_tlb_page(current->mm->mmap, host_buffer); */
113
114 /* Tell everyone where the comm processor resides.
115 */
116/* cpmp = (cpm360_t *)commproc; */
117}
118
119
120/* This is called during init_IRQ. We used to do it above, but this
121 * was too early since init_IRQ was not yet called.
122 */
123void
124cpm_interrupt_init(void)
125{
126 /* Initialize the CPM interrupt controller.
127 * NOTE THAT pquicc had better have been initialized!
128 * reference: MC68360UM p. 7-377
129 */
130 pquicc->intr_cicr =
131 (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) |
132 (CPM_INTERRUPT << 13) |
133 CICR_HP_MASK |
134 (CPM_VECTOR_BASE << 5) |
135 CICR_SPS;
136
137 /* mask all CPM interrupts from reaching the cpu32 core: */
138 pquicc->intr_cimr = 0;
139
140
141 /* mles - If I understand correctly, the 360 just pops over to the CPM
142 * specific vector, obviating the necessity to vector through the IRQ
143 * whose priority the CPM is set to. This needs a closer look, though.
144 */
145
146 /* Set our interrupt handler with the core CPU. */
147/* if (request_irq(CPM_INTERRUPT, cpm_interrupt, 0, "cpm", NULL) != 0) */
148/* panic("Could not allocate CPM IRQ!"); */
149
150 /* Install our own error handler.
151 */
152 /* I think we want to hold off on this one for the moment - mles */
153 /* cpm_install_handler(CPMVEC_ERROR, cpm_error_interrupt, NULL); */
154
155 /* master CPM interrupt enable */
156 /* pquicc->intr_cicr |= CICR_IEN; */ /* no such animal for 360 */
157}
158
159
160
161/* CPM interrupt controller interrupt.
162*/
163static void
164cpm_interrupt(int irq, void * dev, struct pt_regs * regs)
165{
166 /* uint vec; */
167
168 /* mles: Note that this stuff is currently being performed by
169 * M68360_do_irq(int vec, struct pt_regs *fp), in ../ints.c */
170
171 /* figure out the vector */
172 /* call that vector's handler */
173 /* clear the irq's bit in the service register */
174
175#if 0 /* old 860 stuff: */
176 /* Get the vector by setting the ACK bit and then reading
177 * the register.
178 */
179 ((volatile immap_t *)IMAP_ADDR)->im_cpic.cpic_civr = 1;
180 vec = ((volatile immap_t *)IMAP_ADDR)->im_cpic.cpic_civr;
181 vec >>= 11;
182
183
184 if (cpm_vecs[vec].handler != 0)
185 (*cpm_vecs[vec].handler)(cpm_vecs[vec].dev_id);
186 else
187 ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr &= ~(1 << vec);
188
189 /* After servicing the interrupt, we have to remove the status
190 * indicator.
191 */
192 ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cisr |= (1 << vec);
193#endif
194
195}
196
197/* The CPM can generate the error interrupt when there is a race condition
198 * between generating and masking interrupts. All we have to do is ACK it
199 * and return. This is a no-op function so we don't need any special
200 * tests in the interrupt handler.
201 */
202static void
203cpm_error_interrupt(void *dev)
204{
205}
206
207/* Install a CPM interrupt handler.
208*/
209void
210cpm_install_handler(int vec, void (*handler)(), void *dev_id)
211{
212
213 request_irq(vec, handler, IRQ_FLG_LOCK, "timer", dev_id);
214
215/* if (cpm_vecs[vec].handler != 0) */
216/* printk(KERN_INFO "CPM interrupt %x replacing %x\n", */
217/* (uint)handler, (uint)cpm_vecs[vec].handler); */
218/* cpm_vecs[vec].handler = handler; */
219/* cpm_vecs[vec].dev_id = dev_id; */
220
221 /* ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr |= (1 << vec); */
222/* pquicc->intr_cimr |= (1 << vec); */
223
224}
225
226/* Free a CPM interrupt handler.
227*/
228void
229cpm_free_handler(int vec)
230{
231 cpm_vecs[vec].handler = NULL;
232 cpm_vecs[vec].dev_id = NULL;
233 /* ((immap_t *)IMAP_ADDR)->im_cpic.cpic_cimr &= ~(1 << vec); */
234 pquicc->intr_cimr &= ~(1 << vec);
235}
236
237
238
239
240/* Allocate some memory from the dual ported ram. We may want to
241 * enforce alignment restrictions, but right now everyone is a good
242 * citizen.
243 */
244uint
245m360_cpm_dpalloc(uint size)
246{
247 uint retloc;
248
249 if ((dp_alloc_base + size) >= dp_alloc_top)
250 return(CPM_DP_NOSPACE);
251
252 retloc = dp_alloc_base;
253 dp_alloc_base += size;
254
255 return(retloc);
256}
257
258
259#if 0 /* mleslie - for now these are simply kmalloc'd */
260/* We also own one page of host buffer space for the allocation of
261 * UART "fifos" and the like.
262 */
263uint
264m360_cpm_hostalloc(uint size)
265{
266 uint retloc;
267
268 if ((host_buffer + size) >= host_end)
269 return(0);
270
271 retloc = host_buffer;
272 host_buffer += size;
273
274 return(retloc);
275}
276#endif
277
278
279/* Set a baud rate generator. This needs lots of work. There are
280 * four BRGs, any of which can be wired to any channel.
281 * The internal baud rate clock is the system clock divided by 16.
282 * This assumes the baudrate is 16x oversampled by the uart.
283 */
284/* #define BRG_INT_CLK (((bd_t *)__res)->bi_intfreq * 1000000) */
285#define BRG_INT_CLK system_clock
286#define BRG_UART_CLK (BRG_INT_CLK/16)
287
288void
289m360_cpm_setbrg(uint brg, uint rate)
290{
291 volatile uint *bp;
292
293 /* This is good enough to get SMCs running.....
294 */
295 /* bp = (uint *)&cpmp->cp_brgc1; */
296 bp = (volatile uint *)(&pquicc->brgc[0].l);
297 bp += brg;
298 *bp = ((BRG_UART_CLK / rate - 1) << 1) | CPM_BRG_EN;
299}
300
301
302/*
303 * Local variables:
304 * c-indent-level: 4
305 * c-basic-offset: 4
306 * tab-width: 4
307 * End:
308 */
diff --git a/arch/m68knommu/platform/68360/config.c b/arch/m68knommu/platform/68360/config.c
new file mode 100644
index 000000000000..3db244625f0f
--- /dev/null
+++ b/arch/m68knommu/platform/68360/config.c
@@ -0,0 +1,208 @@
1/*
2 * linux/arch/m68knommu/platform/68360/config.c
3 *
4 * Copyright (c) 2000 Michael Leslie <mleslie@lineo.com>
5 * Copyright (C) 1993 Hamish Macdonald
6 * Copyright (C) 1999 D. Jeff Dionne <jeff@uclinux.org>
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file COPYING in the main directory of this archive
10 * for more details.
11 */
12
13#include <stdarg.h>
14#include <linux/config.h>
15#include <linux/types.h>
16#include <linux/kernel.h>
17#include <linux/mm.h>
18#include <linux/tty.h>
19#include <linux/console.h>
20
21#include <asm/setup.h>
22#include <asm/system.h>
23#include <asm/pgtable.h>
24#include <asm/irq.h>
25#include <asm/machdep.h>
26#include <asm/m68360.h>
27
28#ifdef CONFIG_UCQUICC
29#include <asm/bootstd.h>
30#endif
31
32extern void m360_cpm_reset(void);
33
34// Mask to select if the PLL prescaler is enabled.
35#define MCU_PREEN ((unsigned short)(0x0001 << 13))
36
37#if defined(CONFIG_UCQUICC)
38#define OSCILLATOR (unsigned long int)33000000
39#endif
40
41unsigned long int system_clock;
42
43void M68360_init_IRQ(void);
44
45extern QUICC *pquicc;
46
47/* TODO DON"T Hard Code this */
48/* calculate properly using the right PLL and prescaller */
49// unsigned int system_clock = 33000000l;
50extern unsigned long int system_clock; //In kernel setup.c
51
52extern void config_M68360_irq(void);
53
54void BSP_sched_init(void (*timer_routine)(int, void *, struct pt_regs *))
55{
56 unsigned char prescaler;
57 unsigned short tgcr_save;
58 int return_value;
59
60#if 0
61 /* Restart mode, Enable int, 32KHz, Enable timer */
62 TCTL = TCTL_OM | TCTL_IRQEN | TCTL_CLKSOURCE_32KHZ | TCTL_TEN;
63 /* Set prescaler (Divide 32KHz by 32)*/
64 TPRER = 31;
65 /* Set compare register 32Khz / 32 / 10 = 100 */
66 TCMP = 10;
67
68 request_irq(IRQ_MACHSPEC | 1, timer_routine, IRQ_FLG_LOCK, "timer", NULL);
69#endif
70
71 /* General purpose quicc timers: MC68360UM p7-20 */
72
73 /* Set up timer 1 (in [1..4]) to do 100Hz */
74 tgcr_save = pquicc->timer_tgcr & 0xfff0;
75 pquicc->timer_tgcr = tgcr_save; /* stop and reset timer 1 */
76 /* pquicc->timer_tgcr |= 0x4444; */ /* halt timers when FREEZE (ie bdm freeze) */
77
78 prescaler = 8;
79 pquicc->timer_tmr1 = 0x001a | /* or=1, frr=1, iclk=01b */
80 (unsigned short)((prescaler - 1) << 8);
81
82 pquicc->timer_tcn1 = 0x0000; /* initial count */
83 /* calculate interval for 100Hz based on the _system_clock: */
84 pquicc->timer_trr1 = (system_clock/ prescaler) / HZ; /* reference count */
85
86 pquicc->timer_ter1 = 0x0003; /* clear timer events */
87
88 /* enable timer 1 interrupt in CIMR */
89// request_irq(IRQ_MACHSPEC | CPMVEC_TIMER1, timer_routine, IRQ_FLG_LOCK, "timer", NULL);
90 //return_value = request_irq( CPMVEC_TIMER1, timer_routine, IRQ_FLG_LOCK, "timer", NULL);
91 return_value = request_irq(CPMVEC_TIMER1 , timer_routine, IRQ_FLG_LOCK,
92 "Timer", NULL);
93
94 /* Start timer 1: */
95 tgcr_save = (pquicc->timer_tgcr & 0xfff0) | 0x0001;
96 pquicc->timer_tgcr = tgcr_save;
97}
98
99
100void BSP_tick(void)
101{
102 /* Reset Timer1 */
103 /* TSTAT &= 0; */
104
105 pquicc->timer_ter1 = 0x0002; /* clear timer event */
106}
107
108unsigned long BSP_gettimeoffset (void)
109{
110 return 0;
111}
112
113void BSP_gettod (int *yearp, int *monp, int *dayp,
114 int *hourp, int *minp, int *secp)
115{
116}
117
118int BSP_hwclk(int op, struct hwclk_time *t)
119{
120 if (!op) {
121 /* read */
122 } else {
123 /* write */
124 }
125 return 0;
126}
127
128int BSP_set_clock_mmss (unsigned long nowtime)
129{
130#if 0
131 short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
132
133 tod->second1 = real_seconds / 10;
134 tod->second2 = real_seconds % 10;
135 tod->minute1 = real_minutes / 10;
136 tod->minute2 = real_minutes % 10;
137#endif
138 return 0;
139}
140
141void BSP_reset (void)
142{
143 local_irq_disable();
144 asm volatile ("
145 moveal #_start, %a0;
146 moveb #0, 0xFFFFF300;
147 moveal 0(%a0), %sp;
148 moveal 4(%a0), %a0;
149 jmp (%a0);
150 ");
151}
152
153unsigned char *scc1_hwaddr;
154static int errno;
155
156#if defined (CONFIG_UCQUICC)
157_bsc0(char *, getserialnum)
158_bsc1(unsigned char *, gethwaddr, int, a)
159_bsc1(char *, getbenv, char *, a)
160#endif
161
162
163void config_BSP(char *command, int len)
164{
165 unsigned char *p;
166
167 m360_cpm_reset();
168
169 /* Calculate the real system clock value. */
170 {
171 unsigned int local_pllcr = (unsigned int)(pquicc->sim_pllcr);
172 if( local_pllcr & MCU_PREEN ) // If the prescaler is dividing by 128
173 {
174 int mf = (int)(pquicc->sim_pllcr & 0x0fff);
175 system_clock = (OSCILLATOR / 128) * (mf + 1);
176 }
177 else
178 {
179 int mf = (int)(pquicc->sim_pllcr & 0x0fff);
180 system_clock = (OSCILLATOR) * (mf + 1);
181 }
182 }
183
184 printk(KERN_INFO "\n68360 QUICC support (C) 2000 Lineo Inc.\n");
185
186#if defined(CONFIG_UCQUICC) && 0
187 printk(KERN_INFO "uCquicc serial string [%s]\n",getserialnum());
188 p = scc1_hwaddr = gethwaddr(0);
189 printk(KERN_INFO "uCquicc hwaddr %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
190 p[0], p[1], p[2], p[3], p[4], p[5]);
191
192 p = getbenv("APPEND");
193 if (p)
194 strcpy(p,command);
195 else
196 command[0] = 0;
197#else
198 scc1_hwaddr = "\00\01\02\03\04\05";
199#endif
200
201 mach_sched_init = BSP_sched_init;
202 mach_tick = BSP_tick;
203 mach_gettimeoffset = BSP_gettimeoffset;
204 mach_gettod = BSP_gettod;
205 mach_hwclk = NULL;
206 mach_set_clock_mmss = NULL;
207 mach_reset = BSP_reset;
208}
diff --git a/arch/m68knommu/platform/68360/entry.S b/arch/m68knommu/platform/68360/entry.S
new file mode 100644
index 000000000000..f7bc80a60e0f
--- /dev/null
+++ b/arch/m68knommu/platform/68360/entry.S
@@ -0,0 +1,184 @@
1/*
2 * linux/arch/m68knommu/platform/68360/entry.S
3 *
4 * Copyright (C) 1991, 1992 Linus Torvalds
5 * Copyright (C) 2001 SED Systems, a Division of Calian Ltd.
6 *
7 * This file is subject to the terms and conditions of the GNU General Public
8 * License. See the file README.legal in the main directory of this archive
9 * for more details.
10 *
11 * Linux/m68k support by Hamish Macdonald
12 * M68360 Port by SED Systems, and Lineo.
13 */
14
15#include <linux/config.h>
16#include <linux/sys.h>
17#include <linux/linkage.h>
18#include <asm/thread_info.h>
19#include <asm/unistd.h>
20#include <asm/errno.h>
21#include <asm/setup.h>
22#include <asm/segment.h>
23#include <asm/traps.h>
24#include <asm/asm-offsets.h>
25#include <asm/entry.h>
26
27.text
28
29.globl system_call
30.globl resume
31.globl ret_from_exception
32.globl ret_from_signal
33.globl sys_call_table
34.globl ret_from_interrupt
35.globl bad_interrupt
36.globl inthandler
37
38badsys:
39 movel #-ENOSYS,%sp@(PT_D0)
40 jra ret_from_exception
41
42do_trace:
43 movel #-ENOSYS,%sp@(PT_D0) /* needed for strace*/
44 subql #4,%sp
45 SAVE_SWITCH_STACK
46 jbsr syscall_trace
47 RESTORE_SWITCH_STACK
48 addql #4,%sp
49 movel %sp@(PT_ORIG_D0),%d1
50 movel #-ENOSYS,%d0
51 cmpl #NR_syscalls,%d1
52 jcc 1f
53 lsl #2,%d1
54 lea sys_call_table, %a0
55 jbsr %a0@(%d1)
56
571: movel %d0,%sp@(PT_D0) /* save the return value */
58 subql #4,%sp /* dummy return address */
59 SAVE_SWITCH_STACK
60 jbsr syscall_trace
61
62ret_from_signal:
63 RESTORE_SWITCH_STACK
64 addql #4,%sp
65 jra ret_from_exception
66
67ENTRY(system_call)
68 SAVE_ALL
69
70 /* save top of frame*/
71 pea %sp@
72 jbsr set_esp0
73 addql #4,%sp
74
75 btst #PF_TRACESYS_BIT,%a2@(TASK_FLAGS+PF_TRACESYS_OFF)
76 jne do_trace
77 cmpl #NR_syscalls,%d0
78 jcc badsys
79 lsl #2,%d0
80 lea sys_call_table,%a0
81 movel %a0@(%d0), %a0
82 jbsr %a0@
83 movel %d0,%sp@(PT_D0) /* save the return value*/
84
85ret_from_exception:
86 btst #5,%sp@(PT_SR) /* check if returning to kernel*/
87 jeq Luser_return /* if so, skip resched, signals*/
88
89Lkernel_return:
90 RESTORE_ALL
91
92Luser_return:
93 /* only allow interrupts when we are really the last one on the*/
94 /* kernel stack, otherwise stack overflow can occur during*/
95 /* heavy interrupt load*/
96 andw #ALLOWINT,%sr
97
98 movel %sp,%d1 /* get thread_info pointer */
99 andl #0xffffe000,%d1
100 movel %d1,%a2
101 move %a2@(TI_FLAGS),%d1 /* thread_info->flags */
102 andl #_TIF_WORK_MASK,%d1
103 jne Lwork_to_do
104 RESTORE_ALL
105
106Lwork_to_do:
107 movel %a2@(TI_FLAGS),%d1 /* thread_info->flags */
108 btst #TIF_NEED_RESCHED,%d1
109 jne reschedule
110
111Lsignal_return:
112 subql #4,%sp /* dummy return address*/
113 SAVE_SWITCH_STACK
114 pea %sp@(SWITCH_STACK_SIZE)
115 clrl %sp@-
116 bsrw do_signal
117 addql #8,%sp
118 RESTORE_SWITCH_STACK
119 addql #4,%sp
120Lreturn:
121 RESTORE_ALL
122
123/*
124 * This is the main interrupt handler, responsible for calling process_int()
125 */
126inthandler:
127 SAVE_ALL
128 addql #1,local_irq_count /* put exception # in d0*/
129 movew %sp@(PT_VECTOR), %d0
130 and.l #0x3ff, %d0
131 lsr.l #0x02, %d0
132
133 movel %sp,%sp@-
134 movel %d0,%sp@- /* put vector # on stack*/
135 jbsr process_int /* process the IRQ*/
1363: addql #8,%sp /* pop parameters off stack*/
137 bra ret_from_interrupt
138
139ret_from_interrupt:
140 subql #1,local_irq_count
141 jeq 1f
1422:
143 RESTORE_ALL
1441:
145 moveb %sp@(PT_SR), %d0
146 and #7, %d0
147 jhi 2b
148 /* check if we need to do software interrupts */
149
150 movel irq_stat+CPUSTAT_SOFTIRQ_PENDING,%d0
151 jeq ret_from_exception
152
153 pea ret_from_exception
154 jra do_softirq
155
156
157/*
158 * Handler for uninitialized and spurious interrupts.
159 */
160bad_interrupt:
161 addql #1,num_spurious
162 rte
163
164/*
165 * Beware - when entering resume, prev (the current task) is
166 * in a0, next (the new task) is in a1,so don't change these
167 * registers until their contents are no longer needed.
168 */
169ENTRY(resume)
170 movel %a0,%d1 /* save prev thread in d1 */
171 movew %sr,%a0@(TASK_THREAD+THREAD_SR) /* save sr */
172 movel %usp,%a2 /* save usp */
173 movel %a2,%a0@(TASK_THREAD+THREAD_USP)
174
175 SAVE_SWITCH_STACK
176 movel %sp,%a0@(TASK_THREAD+THREAD_KSP) /* save kernel stack */
177 movel %a1@(TASK_THREAD+THREAD_KSP),%sp /* restore new thread stack */
178 RESTORE_SWITCH_STACK
179
180 movel %a1@(TASK_THREAD+THREAD_USP),%a0 /* restore user stack */
181 movel %a0,%usp
182 movew %a1@(TASK_THREAD+THREAD_SR),%sr /* restore thread status reg */
183 rts
184
diff --git a/arch/m68knommu/platform/68360/ints.c b/arch/m68knommu/platform/68360/ints.c
new file mode 100644
index 000000000000..ba184db1651b
--- /dev/null
+++ b/arch/m68knommu/platform/68360/ints.c
@@ -0,0 +1,335 @@
1/*
2 * linux/arch/$(ARCH)/platform/$(PLATFORM)/ints.c
3 *
4 * This file is subject to the terms and conditions of the GNU General Public
5 * License. See the file COPYING in the main directory of this archive
6 * for more details.
7 *
8 * Copyright (c) 2000 Michael Leslie <mleslie@lineo.com>
9 * Copyright (c) 1996 Roman Zippel
10 * Copyright (c) 1999 D. Jeff Dionne <jeff@uclinux.org>
11 */
12
13#include <linux/config.h>
14#include <linux/module.h>
15#include <linux/types.h>
16#include <linux/kernel.h>
17#include <linux/sched.h>
18#include <linux/kernel_stat.h>
19#include <linux/errno.h>
20
21#include <asm/system.h>
22#include <asm/irq.h>
23#include <asm/traps.h>
24#include <asm/io.h>
25#include <asm/machdep.h>
26#include <asm/setup.h>
27#include <asm/m68360.h>
28
29/* from quicc/commproc.c: */
30extern QUICC *pquicc;
31extern void cpm_interrupt_init(void);
32
33#define INTERNAL_IRQS (96)
34
35/* assembler routines */
36asmlinkage void system_call(void);
37asmlinkage void buserr(void);
38asmlinkage void trap(void);
39asmlinkage irqreturn_t bad_interrupt(void);
40asmlinkage irqreturn_t inthandler(void);
41
42extern void *_ramvec[];
43
44/* The number of spurious interrupts */
45volatile unsigned int num_spurious;
46unsigned int local_irq_count[NR_CPUS];
47
48/* irq node variables for the 32 (potential) on chip sources */
49static irq_node_t int_irq_list[INTERNAL_IRQS];
50
51static short int_irq_ablecount[INTERNAL_IRQS];
52
53/*
54 * This function should be called during kernel startup to initialize
55 * IRQ handling routines.
56 */
57
58void init_IRQ(void)
59{
60 int i;
61 int vba = (CPM_VECTOR_BASE<<4);
62
63 /* set up the vectors */
64 _ramvec[2] = buserr;
65 _ramvec[3] = trap;
66 _ramvec[4] = trap;
67 _ramvec[5] = trap;
68 _ramvec[6] = trap;
69 _ramvec[7] = trap;
70 _ramvec[8] = trap;
71 _ramvec[9] = trap;
72 _ramvec[10] = trap;
73 _ramvec[11] = trap;
74 _ramvec[12] = trap;
75 _ramvec[13] = trap;
76 _ramvec[14] = trap;
77 _ramvec[15] = trap;
78
79 _ramvec[32] = system_call;
80 _ramvec[33] = trap;
81
82
83 cpm_interrupt_init();
84
85 /* set up CICR for vector base address and irq level */
86 /* irl = 4, hp = 1f - see MC68360UM p 7-377 */
87 pquicc->intr_cicr = 0x00e49f00 | vba;
88
89 /* CPM interrupt vectors: (p 7-376) */
90 _ramvec[vba+CPMVEC_ERROR] = bad_interrupt; /* Error */
91 _ramvec[vba+CPMVEC_PIO_PC11] = inthandler; /* pio - pc11 */
92 _ramvec[vba+CPMVEC_PIO_PC10] = inthandler; /* pio - pc10 */
93 _ramvec[vba+CPMVEC_SMC2] = inthandler; /* smc2/pip */
94 _ramvec[vba+CPMVEC_SMC1] = inthandler; /* smc1 */
95 _ramvec[vba+CPMVEC_SPI] = inthandler; /* spi */
96 _ramvec[vba+CPMVEC_PIO_PC9] = inthandler; /* pio - pc9 */
97 _ramvec[vba+CPMVEC_TIMER4] = inthandler; /* timer 4 */
98 _ramvec[vba+CPMVEC_RESERVED1] = inthandler; /* reserved */
99 _ramvec[vba+CPMVEC_PIO_PC8] = inthandler; /* pio - pc8 */
100 _ramvec[vba+CPMVEC_PIO_PC7] = inthandler; /* pio - pc7 */
101 _ramvec[vba+CPMVEC_PIO_PC6] = inthandler; /* pio - pc6 */
102 _ramvec[vba+CPMVEC_TIMER3] = inthandler; /* timer 3 */
103 _ramvec[vba+CPMVEC_RISCTIMER] = inthandler; /* reserved */
104 _ramvec[vba+CPMVEC_PIO_PC5] = inthandler; /* pio - pc5 */
105 _ramvec[vba+CPMVEC_PIO_PC4] = inthandler; /* pio - pc4 */
106 _ramvec[vba+CPMVEC_RESERVED2] = inthandler; /* reserved */
107 _ramvec[vba+CPMVEC_RISCTIMER] = inthandler; /* timer table */
108 _ramvec[vba+CPMVEC_TIMER2] = inthandler; /* timer 2 */
109 _ramvec[vba+CPMVEC_RESERVED3] = inthandler; /* reserved */
110 _ramvec[vba+CPMVEC_IDMA2] = inthandler; /* idma 2 */
111 _ramvec[vba+CPMVEC_IDMA1] = inthandler; /* idma 1 */
112 _ramvec[vba+CPMVEC_SDMA_CB_ERR] = inthandler; /* sdma channel bus error */
113 _ramvec[vba+CPMVEC_PIO_PC3] = inthandler; /* pio - pc3 */
114 _ramvec[vba+CPMVEC_PIO_PC2] = inthandler; /* pio - pc2 */
115 /* _ramvec[vba+CPMVEC_TIMER1] = cpm_isr_timer1; */ /* timer 1 */
116 _ramvec[vba+CPMVEC_TIMER1] = inthandler; /* timer 1 */
117 _ramvec[vba+CPMVEC_PIO_PC1] = inthandler; /* pio - pc1 */
118 _ramvec[vba+CPMVEC_SCC4] = inthandler; /* scc 4 */
119 _ramvec[vba+CPMVEC_SCC3] = inthandler; /* scc 3 */
120 _ramvec[vba+CPMVEC_SCC2] = inthandler; /* scc 2 */
121 _ramvec[vba+CPMVEC_SCC1] = inthandler; /* scc 1 */
122 _ramvec[vba+CPMVEC_PIO_PC0] = inthandler; /* pio - pc0 */
123
124
125 /* turn off all CPM interrupts */
126 pquicc->intr_cimr = 0x00000000;
127
128 /* initialize handlers */
129 for (i = 0; i < INTERNAL_IRQS; i++) {
130 int_irq_list[i].handler = NULL;
131 int_irq_list[i].flags = IRQ_FLG_STD;
132 int_irq_list[i].dev_id = NULL;
133 int_irq_list[i].devname = NULL;
134 }
135}
136
137#if 0
138void M68360_insert_irq(irq_node_t **list, irq_node_t *node)
139{
140 unsigned long flags;
141 irq_node_t *cur;
142
143 if (!node->dev_id)
144 printk(KERN_INFO "%s: Warning: dev_id of %s is zero\n",
145 __FUNCTION__, node->devname);
146
147 local_irq_save(flags);
148
149 cur = *list;
150
151 while (cur) {
152 list = &cur->next;
153 cur = cur->next;
154 }
155
156 node->next = cur;
157 *list = node;
158
159 local_irq_restore(flags);
160}
161
162void M68360_delete_irq(irq_node_t **list, void *dev_id)
163{
164 unsigned long flags;
165 irq_node_t *node;
166
167 local_irq_save(flags);
168
169 for (node = *list; node; list = &node->next, node = *list) {
170 if (node->dev_id == dev_id) {
171 *list = node->next;
172 /* Mark it as free. */
173 node->handler = NULL;
174 local_irq_restore(flags);
175 return;
176 }
177 }
178 local_irq_restore(flags);
179 printk (KERN_INFO "%s: tried to remove invalid irq\n", __FUNCTION__);
180}
181#endif
182
183int request_irq(
184 unsigned int irq,
185 irqreturn_t (*handler)(int, void *, struct pt_regs *),
186 unsigned long flags,
187 const char *devname,
188 void *dev_id)
189{
190 int mask = (1<<irq);
191
192 irq += (CPM_VECTOR_BASE<<4);
193
194 if (irq >= INTERNAL_IRQS) {
195 printk (KERN_ERR "%s: Unknown IRQ %d from %s\n", __FUNCTION__, irq, devname);
196 return -ENXIO;
197 }
198
199 if (!(int_irq_list[irq].flags & IRQ_FLG_STD)) {
200 if (int_irq_list[irq].flags & IRQ_FLG_LOCK) {
201 printk(KERN_ERR "%s: IRQ %d from %s is not replaceable\n",
202 __FUNCTION__, irq, int_irq_list[irq].devname);
203 return -EBUSY;
204 }
205 if (flags & IRQ_FLG_REPLACE) {
206 printk(KERN_ERR "%s: %s can't replace IRQ %d from %s\n",
207 __FUNCTION__, devname, irq, int_irq_list[irq].devname);
208 return -EBUSY;
209 }
210 }
211 int_irq_list[irq].handler = handler;
212 int_irq_list[irq].flags = flags;
213 int_irq_list[irq].dev_id = dev_id;
214 int_irq_list[irq].devname = devname;
215
216 /* enable in the CIMR */
217 if (!int_irq_ablecount[irq])
218 pquicc->intr_cimr |= mask;
219 /* *(volatile unsigned long *)0xfffff304 &= ~(1<<irq); */
220
221 return 0;
222}
223
224EXPORT_SYMBOL(request_irq);
225
226void free_irq(unsigned int irq, void *dev_id)
227{
228 if (irq >= INTERNAL_IRQS) {
229 printk (KERN_ERR "%s: Unknown IRQ %d\n", __FUNCTION__, irq);
230 return;
231 }
232
233 if (int_irq_list[irq].dev_id != dev_id)
234 printk(KERN_INFO "%s: removing probably wrong IRQ %d from %s\n",
235 __FUNCTION__, irq, int_irq_list[irq].devname);
236 int_irq_list[irq].handler = NULL;
237 int_irq_list[irq].flags = IRQ_FLG_STD;
238 int_irq_list[irq].dev_id = NULL;
239 int_irq_list[irq].devname = NULL;
240
241 *(volatile unsigned long *)0xfffff304 |= 1<<irq;
242}
243
244EXPORT_SYMBOL(free_irq);
245
246#if 0
247/*
248 * Enable/disable a particular machine specific interrupt source.
249 * Note that this may affect other interrupts in case of a shared interrupt.
250 * This function should only be called for a _very_ short time to change some
251 * internal data, that may not be changed by the interrupt at the same time.
252 * int_(enable|disable)_irq calls may also be nested.
253 */
254void M68360_enable_irq(unsigned int irq)
255{
256 if (irq >= INTERNAL_IRQS) {
257 printk(KERN_ERR "%s: Unknown IRQ %d\n", __FUNCTION__, irq);
258 return;
259 }
260
261 if (--int_irq_ablecount[irq])
262 return;
263
264 /* enable the interrupt */
265 *(volatile unsigned long *)0xfffff304 &= ~(1<<irq);
266}
267
268void M68360_disable_irq(unsigned int irq)
269{
270 if (irq >= INTERNAL_IRQS) {
271 printk(KERN_ERR "%s: Unknown IRQ %d\n", __FUNCTION__, irq);
272 return;
273 }
274
275 if (int_irq_ablecount[irq]++)
276 return;
277
278 /* disable the interrupt */
279 *(volatile unsigned long *)0xfffff304 |= 1<<irq;
280}
281#endif
282
283int show_interrupts(struct seq_file *p, void *v)
284{
285 int i = *(loff_t *) v;
286
287 if (i < NR_IRQS) {
288 if (int_irq_list[i].devname) {
289 seq_printf(p, "%3d: %10u ", i, kstat_cpu(0).irqs[i]);
290 if (int_irq_list[i].flags & IRQ_FLG_LOCK)
291 seq_printf(p, "L ");
292 else
293 seq_printf(p, " ");
294 seq_printf(p, "%s\n", int_irq_list[i].devname);
295 }
296 }
297 if (i == NR_IRQS)
298 seq_printf(p, " : %10u spurious\n", num_spurious);
299
300 return 0;
301}
302
303/* The 68k family did not have a good way to determine the source
304 * of interrupts until later in the family. The EC000 core does
305 * not provide the vector number on the stack, we vector everything
306 * into one vector and look in the blasted mask register...
307 * This code is designed to be fast, almost constant time, not clean!
308 */
309void process_int(int vec, struct pt_regs *fp)
310{
311 int irq;
312 int mask;
313
314 /* unsigned long pend = *(volatile unsigned long *)0xfffff30c; */
315
316 /* irq = vec + (CPM_VECTOR_BASE<<4); */
317 irq = vec;
318
319 /* unsigned long pend = *(volatile unsigned long *)pquicc->intr_cipr; */
320
321 /* Bugger all that weirdness. For the moment, I seem to know where I came from;
322 * vec is passed from a specific ISR, so I'll use it. */
323
324 if (int_irq_list[irq].handler) {
325 int_irq_list[irq].handler(irq , int_irq_list[irq].dev_id, fp);
326 kstat_cpu(0).irqs[irq]++;
327 pquicc->intr_cisr = (1 << vec); /* indicate that irq has been serviced */
328 } else {
329 printk(KERN_ERR "unregistered interrupt %d!\nTurning it off in the CIMR...\n", irq);
330 /* *(volatile unsigned long *)0xfffff304 |= mask; */
331 pquicc->intr_cimr &= ~(1 << vec);
332 num_spurious += 1;
333 }
334 return(IRQ_HANDLED);
335}