aboutsummaryrefslogtreecommitdiffstats
path: root/arch/m68k/q40
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/m68k/q40
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/m68k/q40')
-rw-r--r--arch/m68k/q40/Makefile5
-rw-r--r--arch/m68k/q40/README138
-rw-r--r--arch/m68k/q40/config.c365
-rw-r--r--arch/m68k/q40/q40ints.c476
4 files changed, 984 insertions, 0 deletions
diff --git a/arch/m68k/q40/Makefile b/arch/m68k/q40/Makefile
new file mode 100644
index 000000000000..27eb42796afa
--- /dev/null
+++ b/arch/m68k/q40/Makefile
@@ -0,0 +1,5 @@
1#
2# Makefile for Linux arch/m68k/q40 source directory
3#
4
5obj-y := config.o q40ints.o
diff --git a/arch/m68k/q40/README b/arch/m68k/q40/README
new file mode 100644
index 000000000000..6bdbf4879570
--- /dev/null
+++ b/arch/m68k/q40/README
@@ -0,0 +1,138 @@
1Linux for the Q40
2=================
3
4You may try http://www.geocities.com/SiliconValley/Bay/2602/ for
5some up to date information. Booter and other tools will be also
6available from this place or ftp.uni-erlangen.de/linux/680x0/q40/
7and mirrors.
8
9Hints to documentation usually refer to the linux source tree in
10/usr/src/linux/Documentation unless URL given.
11
12It seems IRQ unmasking can't be safely done on a Q40. IRQ probing
13is not implemented - do not try it! (See below)
14
15For a list of kernel command-line options read the documentation for the
16particular device drivers.
17
18The floppy imposes a very high interrupt load on the CPU, approx 30K/s.
19When something blocks interrupts (HD) it will lose some of them, so far
20this is not known to have caused any data loss. On highly loaded systems
21it can make the floppy very slow or practically stop. Other Q40 OS' simply
22poll the floppy for this reason - something that can't be done in Linux.
23Only possible cure is getting a 82072 controller with fifo instead of
24the 8272A.
25
26drivers used by the Q40, apart from the very obvious (console etc.):
27 drivers/char/q40_keyb.c # use PC keymaps for national keyboards
28 serial.c # normal PC driver - any speed
29 lp.c # printer driver
30 genrtc.c # RTC
31 char/joystick/* # most of this should work, not
32 # in default config.in
33 block/q40ide.c # startup for ide
34 ide* # see Documentation/ide.txt
35 floppy.c # normal PC driver, DMA emu in asm/floppy.h
36 # and arch/m68k/kernel/entry.S
37 # see drivers/block/README.fd
38 net/ne.c
39 video/q40fb.c
40 parport/*
41 sound/dmasound_core.c
42 dmasound_q40.c
43
44Various other PC drivers can be enabled simply by adding them to
45arch/m68k/config.in, especially 8 bit devices should be without any
46problems. For cards using 16bit io/mem more care is required, like
47checking byte order issues, hacking memcpy_*_io etc.
48
49
50Debugging
51=========
52
53Upon startup the kernel will usually output "ABCQGHIJ" into the SRAM,
54preceded by the booter signature. This is a trace just in case something
55went wrong during earliest setup stages of head.S.
56**Changed** to preserve SRAM contents by default, this is only done when
57requested - SRAM must start with '%LX$' signature to do this. '-d' option
58to 'lxx' loader enables this.
59
60SRAM can also be used as additional console device, use debug=mem.
61This will save kernel startup msgs into SRAM, the screen will display
62only the penguin - and shell prompt if it gets that far..
63Unfortunately only 2000 bytes are available.
64
65Serial console works and can also be used for debugging, see loader_txt
66
67Most problems seem to be caused by fawlty or badly configured io-cards or
68hard drives anyway.
69Make sure to configure the parallel port as SPP and remove IRQ/DMA jumpers
70for first testing. The Q40 does not support DMA and may have trouble with
71parallel ports version of interrupts.
72
73
74Q40 Hardware Description
75========================
76
77This is just an overview, see asm-m68k/* for details ask if you have any
78questions.
79
80The Q40 consists of a 68040@40 MHz, 1MB video RAM, up to 32MB RAM, AT-style
81keyboard interface, 1 Programmable LED, 2x8bit DACs and up to 1MB ROM, 1MB
82shadow ROM.
83The Q60 has any of 68060 or 68LC060 and up to 128 MB RAM.
84
85Most interfacing like floppy, IDE, serial and parallel ports is done via ISA
86slots. The ISA io and mem range is mapped (sparse&byteswapped!) into separate
87regions of the memory.
88The main interrupt register IIRQ_REG will indicate whether an IRQ was internal
89or from some ISA devices, EIRQ_REG can distinguish up to 8 ISA IRQs.
90
91The Q40 custom chip is programmable to provide 2 periodic timers:
92 - 50 or 200 Hz - level 2, !!THIS CANT BE DISABLED!!
93 - 10 or 20 KHz - level 4, used for dma-sound
94
95Linux uses the 200 Hz interrupt for timer and beep by default.
96
97
98Interrupts
99==========
100
101q40 master chip handles only a subset of level triggered interrupts.
102
103Linux has some requirements wrt interrupt architecture, these are
104to my knowledge:
105 (a) interrupt handler must not be reentered even when sti() is called
106 from within handler
107 (b) working enable/disable_irq
108
109Luckily these requirements are only important for drivers shared
110with other architectures - ide,serial,parallel, ethernet.
111q40ints.c now contains a trivial hack for (a), (b) is more difficult
112because only irq's 4-15 can be disabled - and only all of them at once.
113Thus disable_irq() can effectively block the machine if the driver goes
114asleep.
115One thing to keep in mind when hacking around the interrupt code is
116that there is no way to find out which IRQ caused a request, [EI]IRQ_REG
117displays current state of the various IRQ lines.
118
119Keyboard
120========
121
122q40 receives AT make/break codes from the keyboard, these are translated to
123the PC scancodes x86 Linux uses. So by theory every national keyboard should
124work just by loading the appropriate x86 keytable - see any national-HOWTO.
125
126Unfortunately the AT->PC translation isn't quite trivial and even worse, my
127documentation of it is absolutely minimal - thus some exotic keys may not
128behave exactly as expected.
129
130There is still hope that it can be fixed completely though. If you encounter
131problems, email me ideally this:
132 - exact keypress/release sequence
133 - 'showkey -s' run on q40, non-X session
134 - 'showkey -s' run on a PC, non-X session
135 - AT codes as displayed by the q40 debugging ROM
136btw if the showkey output from PC and Q40 doesn't differ then you have some
137classic configuration problem - don't send me anything in this case
138
diff --git a/arch/m68k/q40/config.c b/arch/m68k/q40/config.c
new file mode 100644
index 000000000000..02b626bae4ae
--- /dev/null
+++ b/arch/m68k/q40/config.c
@@ -0,0 +1,365 @@
1/*
2 * arch/m68k/q40/config.c
3 *
4 * Copyright (C) 1999 Richard Zidlicky
5 *
6 * originally based on:
7 *
8 * linux/bvme/config.c
9 *
10 * This file is subject to the terms and conditions of the GNU General Public
11 * License. See the file README.legal in the main directory of this archive
12 * for more details.
13 */
14
15#include <linux/config.h>
16#include <linux/types.h>
17#include <linux/kernel.h>
18#include <linux/mm.h>
19#include <linux/tty.h>
20#include <linux/console.h>
21#include <linux/linkage.h>
22#include <linux/init.h>
23#include <linux/major.h>
24#include <linux/serial_reg.h>
25#include <linux/rtc.h>
26#include <linux/vt_kern.h>
27
28#include <asm/io.h>
29#include <asm/rtc.h>
30#include <asm/bootinfo.h>
31#include <asm/system.h>
32#include <asm/pgtable.h>
33#include <asm/setup.h>
34#include <asm/irq.h>
35#include <asm/traps.h>
36#include <asm/machdep.h>
37#include <asm/q40_master.h>
38
39extern void floppy_setup(char *str, int *ints);
40
41extern irqreturn_t q40_process_int (int level, struct pt_regs *regs);
42extern irqreturn_t (*q40_default_handler[]) (int, void *, struct pt_regs *); /* added just for debugging */
43extern void q40_init_IRQ (void);
44extern void q40_free_irq (unsigned int, void *);
45extern int show_q40_interrupts (struct seq_file *, void *);
46extern void q40_enable_irq (unsigned int);
47extern void q40_disable_irq (unsigned int);
48static void q40_get_model(char *model);
49static int q40_get_hardware_list(char *buffer);
50extern int q40_request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void *dev_id);
51extern void q40_sched_init(irqreturn_t (*handler)(int, void *, struct pt_regs *));
52
53extern unsigned long q40_gettimeoffset (void);
54extern int q40_hwclk (int, struct rtc_time *);
55extern unsigned int q40_get_ss (void);
56extern int q40_set_clock_mmss (unsigned long);
57static int q40_get_rtc_pll(struct rtc_pll_info *pll);
58static int q40_set_rtc_pll(struct rtc_pll_info *pll);
59extern void q40_reset (void);
60void q40_halt(void);
61extern void q40_waitbut(void);
62void q40_set_vectors (void);
63
64extern void q40_mksound(unsigned int /*freq*/, unsigned int /*ticks*/ );
65
66extern char m68k_debug_device[];
67static void q40_mem_console_write(struct console *co, const char *b,
68 unsigned int count);
69
70extern int ql_ticks;
71
72static struct console q40_console_driver = {
73 .name = "debug",
74 .flags = CON_PRINTBUFFER,
75 .index = -1,
76};
77
78
79/* early debugging function:*/
80extern char *q40_mem_cptr; /*=(char *)0xff020000;*/
81static int _cpleft;
82
83static void q40_mem_console_write(struct console *co, const char *s,
84 unsigned int count)
85{
86 char *p=(char *)s;
87
88 if (count<_cpleft)
89 while (count-- >0){
90 *q40_mem_cptr=*p++;
91 q40_mem_cptr+=4;
92 _cpleft--;
93 }
94}
95#if 0
96void printq40(char *str)
97{
98 int l=strlen(str);
99 char *p=q40_mem_cptr;
100
101 while (l-- >0 && _cpleft-- >0)
102 {
103 *p=*str++;
104 p+=4;
105 }
106 q40_mem_cptr=p;
107}
108#endif
109
110static int halted=0;
111
112#ifdef CONFIG_HEARTBEAT
113static void q40_heartbeat(int on)
114{
115 if (halted) return;
116
117 if (on)
118 Q40_LED_ON();
119 else
120 Q40_LED_OFF();
121}
122#endif
123
124void q40_reset(void)
125{
126 halted=1;
127 printk ("\n\n*******************************************\n"
128 "Called q40_reset : press the RESET button!! \n"
129 "*******************************************\n");
130 Q40_LED_ON();
131 while(1) ;
132}
133void q40_halt(void)
134{
135 halted=1;
136 printk ("\n\n*******************\n"
137 " Called q40_halt\n"
138 "*******************\n");
139 Q40_LED_ON();
140 while(1) ;
141}
142
143static void q40_get_model(char *model)
144{
145 sprintf(model, "Q40");
146}
147
148/* No hardware options on Q40? */
149
150static int q40_get_hardware_list(char *buffer)
151{
152 *buffer = '\0';
153 return 0;
154}
155
156static unsigned int serports[]={0x3f8,0x2f8,0x3e8,0x2e8,0};
157void q40_disable_irqs(void)
158{
159 unsigned i,j;
160
161 j=0;
162 while((i=serports[j++])) outb(0,i+UART_IER);
163 master_outb(0,EXT_ENABLE_REG);
164 master_outb(0,KEY_IRQ_ENABLE_REG);
165}
166
167void __init config_q40(void)
168{
169 mach_sched_init = q40_sched_init;
170
171 mach_init_IRQ = q40_init_IRQ;
172 mach_gettimeoffset = q40_gettimeoffset;
173 mach_hwclk = q40_hwclk;
174 mach_get_ss = q40_get_ss;
175 mach_get_rtc_pll = q40_get_rtc_pll;
176 mach_set_rtc_pll = q40_set_rtc_pll;
177 mach_set_clock_mmss = q40_set_clock_mmss;
178
179 mach_reset = q40_reset;
180 mach_free_irq = q40_free_irq;
181 mach_process_int = q40_process_int;
182 mach_get_irq_list = show_q40_interrupts;
183 mach_request_irq = q40_request_irq;
184 enable_irq = q40_enable_irq;
185 disable_irq = q40_disable_irq;
186 mach_default_handler = &q40_default_handler;
187 mach_get_model = q40_get_model;
188 mach_get_hardware_list = q40_get_hardware_list;
189
190#if defined(CONFIG_INPUT_M68K_BEEP) || defined(CONFIG_INPUT_M68K_BEEP_MODULE)
191 mach_beep = q40_mksound;
192#endif
193#ifdef CONFIG_HEARTBEAT
194 mach_heartbeat = q40_heartbeat;
195#endif
196 mach_halt = q40_halt;
197#ifdef CONFIG_DUMMY_CONSOLE
198 conswitchp = &dummy_con;
199#endif
200
201 /* disable a few things that SMSQ might have left enabled */
202 q40_disable_irqs();
203
204 /* no DMA at all, but ide-scsi requires it.. make sure
205 * all physical RAM fits into the boundary - otherwise
206 * allocator may play costly and useless tricks */
207 mach_max_dma_address = 1024*1024*1024;
208
209 /* useful for early debugging stages - writes kernel messages into SRAM */
210 if (!strncmp( m68k_debug_device,"mem",3 ))
211 {
212 /*printk("using NVRAM debug, q40_mem_cptr=%p\n",q40_mem_cptr);*/
213 _cpleft=2000-((long)q40_mem_cptr-0xff020000)/4;
214 q40_console_driver.write = q40_mem_console_write;
215 register_console(&q40_console_driver);
216 }
217}
218
219
220int q40_parse_bootinfo(const struct bi_record *rec)
221{
222 return 1;
223}
224
225
226static inline unsigned char bcd2bin (unsigned char b)
227{
228 return ((b>>4)*10 + (b&15));
229}
230
231static inline unsigned char bin2bcd (unsigned char b)
232{
233 return (((b/10)*16) + (b%10));
234}
235
236
237unsigned long q40_gettimeoffset (void)
238{
239 return 5000*(ql_ticks!=0);
240}
241
242
243/*
244 * Looks like op is non-zero for setting the clock, and zero for
245 * reading the clock.
246 *
247 * struct hwclk_time {
248 * unsigned sec; 0..59
249 * unsigned min; 0..59
250 * unsigned hour; 0..23
251 * unsigned day; 1..31
252 * unsigned mon; 0..11
253 * unsigned year; 00...
254 * int wday; 0..6, 0 is Sunday, -1 means unknown/don't set
255 * };
256 */
257
258int q40_hwclk(int op, struct rtc_time *t)
259{
260 if (op)
261 { /* Write.... */
262 Q40_RTC_CTRL |= Q40_RTC_WRITE;
263
264 Q40_RTC_SECS = bin2bcd(t->tm_sec);
265 Q40_RTC_MINS = bin2bcd(t->tm_min);
266 Q40_RTC_HOUR = bin2bcd(t->tm_hour);
267 Q40_RTC_DATE = bin2bcd(t->tm_mday);
268 Q40_RTC_MNTH = bin2bcd(t->tm_mon + 1);
269 Q40_RTC_YEAR = bin2bcd(t->tm_year%100);
270 if (t->tm_wday >= 0)
271 Q40_RTC_DOW = bin2bcd(t->tm_wday+1);
272
273 Q40_RTC_CTRL &= ~(Q40_RTC_WRITE);
274 }
275 else
276 { /* Read.... */
277 Q40_RTC_CTRL |= Q40_RTC_READ;
278
279 t->tm_year = bcd2bin (Q40_RTC_YEAR);
280 t->tm_mon = bcd2bin (Q40_RTC_MNTH)-1;
281 t->tm_mday = bcd2bin (Q40_RTC_DATE);
282 t->tm_hour = bcd2bin (Q40_RTC_HOUR);
283 t->tm_min = bcd2bin (Q40_RTC_MINS);
284 t->tm_sec = bcd2bin (Q40_RTC_SECS);
285
286 Q40_RTC_CTRL &= ~(Q40_RTC_READ);
287
288 if (t->tm_year < 70)
289 t->tm_year += 100;
290 t->tm_wday = bcd2bin(Q40_RTC_DOW)-1;
291
292 }
293
294 return 0;
295}
296
297unsigned int q40_get_ss(void)
298{
299 return bcd2bin(Q40_RTC_SECS);
300}
301
302/*
303 * Set the minutes and seconds from seconds value 'nowtime'. Fail if
304 * clock is out by > 30 minutes. Logic lifted from atari code.
305 */
306
307int q40_set_clock_mmss (unsigned long nowtime)
308{
309 int retval = 0;
310 short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
311
312 int rtc_minutes;
313
314
315 rtc_minutes = bcd2bin (Q40_RTC_MINS);
316
317 if ((rtc_minutes < real_minutes
318 ? real_minutes - rtc_minutes
319 : rtc_minutes - real_minutes) < 30)
320 {
321 Q40_RTC_CTRL |= Q40_RTC_WRITE;
322 Q40_RTC_MINS = bin2bcd(real_minutes);
323 Q40_RTC_SECS = bin2bcd(real_seconds);
324 Q40_RTC_CTRL &= ~(Q40_RTC_WRITE);
325 }
326 else
327 retval = -1;
328
329
330 return retval;
331}
332
333
334/* get and set PLL calibration of RTC clock */
335#define Q40_RTC_PLL_MASK ((1<<5)-1)
336#define Q40_RTC_PLL_SIGN (1<<5)
337
338static int q40_get_rtc_pll(struct rtc_pll_info *pll)
339{
340 int tmp=Q40_RTC_CTRL;
341 pll->pll_value = tmp & Q40_RTC_PLL_MASK;
342 if (tmp & Q40_RTC_PLL_SIGN)
343 pll->pll_value = -pll->pll_value;
344 pll->pll_max=31;
345 pll->pll_min=-31;
346 pll->pll_posmult=512;
347 pll->pll_negmult=256;
348 pll->pll_clock=125829120;
349 return 0;
350}
351
352static int q40_set_rtc_pll(struct rtc_pll_info *pll)
353{
354 if (!pll->pll_ctrl){
355 /* the docs are a bit unclear so I am doublesetting */
356 /* RTC_WRITE here ... */
357 int tmp = (pll->pll_value & 31) | (pll->pll_value<0 ? 32 : 0) |
358 Q40_RTC_WRITE;
359 Q40_RTC_CTRL |= Q40_RTC_WRITE;
360 Q40_RTC_CTRL = tmp;
361 Q40_RTC_CTRL &= ~(Q40_RTC_WRITE);
362 return 0;
363 } else
364 return -EINVAL;
365}
diff --git a/arch/m68k/q40/q40ints.c b/arch/m68k/q40/q40ints.c
new file mode 100644
index 000000000000..f8ecc2664fe6
--- /dev/null
+++ b/arch/m68k/q40/q40ints.c
@@ -0,0 +1,476 @@
1/*
2 * arch/m68k/q40/q40ints.c
3 *
4 * Copyright (C) 1999,2001 Richard Zidlicky
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive
8 * for more details.
9 *
10 * .. used to be loosely based on bvme6000ints.c
11 *
12 */
13
14#include <linux/types.h>
15#include <linux/kernel.h>
16#include <linux/errno.h>
17#include <linux/string.h>
18#include <linux/sched.h>
19#include <linux/seq_file.h>
20#include <linux/interrupt.h>
21#include <linux/hardirq.h>
22
23#include <asm/rtc.h>
24#include <asm/ptrace.h>
25#include <asm/system.h>
26#include <asm/irq.h>
27#include <asm/traps.h>
28
29#include <asm/q40_master.h>
30#include <asm/q40ints.h>
31
32/*
33 * Q40 IRQs are defined as follows:
34 * 3,4,5,6,7,10,11,14,15 : ISA dev IRQs
35 * 16-31: reserved
36 * 32 : keyboard int
37 * 33 : frame int (50/200 Hz periodic timer)
38 * 34 : sample int (10/20 KHz periodic timer)
39 *
40*/
41
42extern int ints_inited;
43
44
45irqreturn_t q40_irq2_handler (int, void *, struct pt_regs *fp);
46
47
48static irqreturn_t q40_defhand (int irq, void *dev_id, struct pt_regs *fp);
49static irqreturn_t default_handler(int lev, void *dev_id, struct pt_regs *regs);
50
51
52#define DEVNAME_SIZE 24
53
54static struct q40_irq_node {
55 irqreturn_t (*handler)(int, void *, struct pt_regs *);
56 unsigned long flags;
57 void *dev_id;
58 /* struct q40_irq_node *next;*/
59 char devname[DEVNAME_SIZE];
60 unsigned count;
61 unsigned short state;
62} irq_tab[Q40_IRQ_MAX+1];
63
64short unsigned q40_ablecount[Q40_IRQ_MAX+1];
65
66/*
67 * void q40_init_IRQ (void)
68 *
69 * Parameters: None
70 *
71 * Returns: Nothing
72 *
73 * This function is called during kernel startup to initialize
74 * the q40 IRQ handling routines.
75 */
76
77static int disabled=0;
78
79void q40_init_IRQ (void)
80{
81 int i;
82
83 disabled=0;
84 for (i = 0; i <= Q40_IRQ_MAX; i++) {
85 irq_tab[i].handler = q40_defhand;
86 irq_tab[i].flags = 0;
87 irq_tab[i].dev_id = NULL;
88 /* irq_tab[i].next = NULL;*/
89 irq_tab[i].devname[0] = 0;
90 irq_tab[i].count = 0;
91 irq_tab[i].state =0;
92 q40_ablecount[i]=0; /* all enabled */
93 }
94
95 /* setup handler for ISA ints */
96 cpu_request_irq(IRQ2, q40_irq2_handler, 0, "q40 ISA and master chip",
97 NULL);
98
99 /* now enable some ints.. */
100 master_outb(1,EXT_ENABLE_REG); /* ISA IRQ 5-15 */
101
102 /* make sure keyboard IRQ is disabled */
103 master_outb(0,KEY_IRQ_ENABLE_REG);
104}
105
106int q40_request_irq(unsigned int irq,
107 irqreturn_t (*handler)(int, void *, struct pt_regs *),
108 unsigned long flags, const char *devname, void *dev_id)
109{
110 /*printk("q40_request_irq %d, %s\n",irq,devname);*/
111
112 if (irq > Q40_IRQ_MAX || (irq>15 && irq<32)) {
113 printk("%s: Incorrect IRQ %d from %s\n", __FUNCTION__, irq, devname);
114 return -ENXIO;
115 }
116
117 /* test for ISA ints not implemented by HW */
118 switch (irq)
119 {
120 case 1: case 2: case 8: case 9:
121 case 12: case 13:
122 printk("%s: ISA IRQ %d from %s not implemented by HW\n", __FUNCTION__, irq, devname);
123 return -ENXIO;
124 case 11:
125 printk("warning IRQ 10 and 11 not distinguishable\n");
126 irq=10;
127 default:
128 ;
129 }
130
131 if (irq<Q40_IRQ_SAMPLE)
132 {
133 if (irq_tab[irq].dev_id != NULL)
134 {
135 printk("%s: IRQ %d from %s is not replaceable\n",
136 __FUNCTION__, irq, irq_tab[irq].devname);
137 return -EBUSY;
138 }
139 /*printk("IRQ %d set to handler %p\n",irq,handler);*/
140 if (dev_id==NULL)
141 {
142 printk("WARNING: dev_id == NULL in request_irq\n");
143 dev_id=(void*)1;
144 }
145 irq_tab[irq].handler = handler;
146 irq_tab[irq].flags = flags;
147 irq_tab[irq].dev_id = dev_id;
148 strlcpy(irq_tab[irq].devname,devname,sizeof(irq_tab[irq].devname));
149 irq_tab[irq].state = 0;
150 return 0;
151 }
152 else {
153 /* Q40_IRQ_SAMPLE :somewhat special actions required here ..*/
154 cpu_request_irq(4, handler, flags, devname, dev_id);
155 cpu_request_irq(6, handler, flags, devname, dev_id);
156 return 0;
157 }
158}
159
160void q40_free_irq(unsigned int irq, void *dev_id)
161{
162 if (irq > Q40_IRQ_MAX || (irq>15 && irq<32)) {
163 printk("%s: Incorrect IRQ %d, dev_id %x \n", __FUNCTION__, irq, (unsigned)dev_id);
164 return;
165 }
166
167 /* test for ISA ints not implemented by HW */
168 switch (irq)
169 {
170 case 1: case 2: case 8: case 9:
171 case 12: case 13:
172 printk("%s: ISA IRQ %d from %x invalid\n", __FUNCTION__, irq, (unsigned)dev_id);
173 return;
174 case 11: irq=10;
175 default:
176 ;
177 }
178
179 if (irq<Q40_IRQ_SAMPLE)
180 {
181 if (irq_tab[irq].dev_id != dev_id)
182 printk("%s: Removing probably wrong IRQ %d from %s\n",
183 __FUNCTION__, irq, irq_tab[irq].devname);
184
185 irq_tab[irq].handler = q40_defhand;
186 irq_tab[irq].flags = 0;
187 irq_tab[irq].dev_id = NULL;
188 /* irq_tab[irq].devname = NULL; */
189 /* do not reset state !! */
190 }
191 else
192 { /* == Q40_IRQ_SAMPLE */
193 cpu_free_irq(4, dev_id);
194 cpu_free_irq(6, dev_id);
195 }
196}
197
198
199irqreturn_t q40_process_int (int level, struct pt_regs *fp)
200{
201 printk("unexpected interrupt vec=%x, pc=%lx, d0=%lx, d0_orig=%lx, d1=%lx, d2=%lx\n",
202 level, fp->pc, fp->d0, fp->orig_d0, fp->d1, fp->d2);
203 printk("\tIIRQ_REG = %x, EIRQ_REG = %x\n",master_inb(IIRQ_REG),master_inb(EIRQ_REG));
204 return IRQ_HANDLED;
205}
206
207/*
208 * this stuff doesn't really belong here..
209*/
210
211int ql_ticks; /* 200Hz ticks since last jiffie */
212static int sound_ticks;
213
214#define SVOL 45
215
216void q40_mksound(unsigned int hz, unsigned int ticks)
217{
218 /* for now ignore hz, except that hz==0 switches off sound */
219 /* simply alternate the ampl (128-SVOL)-(128+SVOL)-..-.. at 200Hz */
220 if (hz==0)
221 {
222 if (sound_ticks)
223 sound_ticks=1;
224
225 *DAC_LEFT=128;
226 *DAC_RIGHT=128;
227
228 return;
229 }
230 /* sound itself is done in q40_timer_int */
231 if (sound_ticks == 0) sound_ticks=1000; /* pretty long beep */
232 sound_ticks=ticks<<1;
233}
234
235static irqreturn_t (*q40_timer_routine)(int, void *, struct pt_regs *);
236
237static irqreturn_t q40_timer_int (int irq, void * dev, struct pt_regs * regs)
238{
239 ql_ticks = ql_ticks ? 0 : 1;
240 if (sound_ticks)
241 {
242 unsigned char sval=(sound_ticks & 1) ? 128-SVOL : 128+SVOL;
243 sound_ticks--;
244 *DAC_LEFT=sval;
245 *DAC_RIGHT=sval;
246 }
247
248 if (!ql_ticks)
249 q40_timer_routine(irq, dev, regs);
250 return IRQ_HANDLED;
251}
252
253void q40_sched_init (irqreturn_t (*timer_routine)(int, void *, struct pt_regs *))
254{
255 int timer_irq;
256
257 q40_timer_routine = timer_routine;
258 timer_irq=Q40_IRQ_FRAME;
259
260 if (request_irq(timer_irq, q40_timer_int, 0,
261 "timer", q40_timer_int))
262 panic ("Couldn't register timer int");
263
264 master_outb(-1,FRAME_CLEAR_REG);
265 master_outb( 1,FRAME_RATE_REG);
266}
267
268
269/*
270 * tables to translate bits into IRQ numbers
271 * it is a good idea to order the entries by priority
272 *
273*/
274
275struct IRQ_TABLE{ unsigned mask; int irq ;};
276#if 0
277static struct IRQ_TABLE iirqs[]={
278 {Q40_IRQ_FRAME_MASK,Q40_IRQ_FRAME},
279 {Q40_IRQ_KEYB_MASK,Q40_IRQ_KEYBOARD},
280 {0,0}};
281#endif
282static struct IRQ_TABLE eirqs[] = {
283 { .mask = Q40_IRQ3_MASK, .irq = 3 }, /* ser 1 */
284 { .mask = Q40_IRQ4_MASK, .irq = 4 }, /* ser 2 */
285 { .mask = Q40_IRQ14_MASK, .irq = 14 }, /* IDE 1 */
286 { .mask = Q40_IRQ15_MASK, .irq = 15 }, /* IDE 2 */
287 { .mask = Q40_IRQ6_MASK, .irq = 6 }, /* floppy, handled elsewhere */
288 { .mask = Q40_IRQ7_MASK, .irq = 7 }, /* par */
289 { .mask = Q40_IRQ5_MASK, .irq = 5 },
290 { .mask = Q40_IRQ10_MASK, .irq = 10 },
291 {0,0}
292};
293
294/* complain only this many times about spurious ints : */
295static int ccleirq=60; /* ISA dev IRQ's*/
296/*static int cclirq=60;*/ /* internal */
297
298/* FIXME: add shared ints,mask,unmask,probing.... */
299
300#define IRQ_INPROGRESS 1
301/*static unsigned short saved_mask;*/
302//static int do_tint=0;
303
304#define DEBUG_Q40INT
305/*#define IP_USE_DISABLE *//* would be nice, but crashes ???? */
306
307static int mext_disabled=0; /* ext irq disabled by master chip? */
308static int aliased_irq=0; /* how many times inside handler ?*/
309
310
311/* got level 2 interrupt, dispatch to ISA or keyboard/timer IRQs */
312irqreturn_t q40_irq2_handler (int vec, void *devname, struct pt_regs *fp)
313{
314 unsigned mir, mer;
315 int irq,i;
316
317//repeat:
318 mir=master_inb(IIRQ_REG);
319 if (mir&Q40_IRQ_FRAME_MASK) {
320 irq_tab[Q40_IRQ_FRAME].count++;
321 irq_tab[Q40_IRQ_FRAME].handler(Q40_IRQ_FRAME,irq_tab[Q40_IRQ_FRAME].dev_id,fp);
322 master_outb(-1,FRAME_CLEAR_REG);
323 }
324 if ((mir&Q40_IRQ_SER_MASK) || (mir&Q40_IRQ_EXT_MASK)) {
325 mer=master_inb(EIRQ_REG);
326 for (i=0; eirqs[i].mask; i++) {
327 if (mer&(eirqs[i].mask)) {
328 irq=eirqs[i].irq;
329/*
330 * There is a little mess wrt which IRQ really caused this irq request. The
331 * main problem is that IIRQ_REG and EIRQ_REG reflect the state when they
332 * are read - which is long after the request came in. In theory IRQs should
333 * not just go away but they occassionally do
334 */
335 if (irq>4 && irq<=15 && mext_disabled) {
336 /*aliased_irq++;*/
337 goto iirq;
338 }
339 if (irq_tab[irq].handler == q40_defhand ) {
340 printk("handler for IRQ %d not defined\n",irq);
341 continue; /* ignore uninited INTs :-( */
342 }
343 if ( irq_tab[irq].state & IRQ_INPROGRESS ) {
344 /* some handlers do local_irq_enable() for irq latency reasons, */
345 /* however reentering an active irq handler is not permitted */
346#ifdef IP_USE_DISABLE
347 /* in theory this is the better way to do it because it still */
348 /* lets through eg the serial irqs, unfortunately it crashes */
349 disable_irq(irq);
350 disabled=1;
351#else
352 /*printk("IRQ_INPROGRESS detected for irq %d, disabling - %s disabled\n",irq,disabled ? "already" : "not yet"); */
353 fp->sr = (((fp->sr) & (~0x700))+0x200);
354 disabled=1;
355#endif
356 goto iirq;
357 }
358 irq_tab[irq].count++;
359 irq_tab[irq].state |= IRQ_INPROGRESS;
360 irq_tab[irq].handler(irq,irq_tab[irq].dev_id,fp);
361 irq_tab[irq].state &= ~IRQ_INPROGRESS;
362
363 /* naively enable everything, if that fails than */
364 /* this function will be reentered immediately thus */
365 /* getting another chance to disable the IRQ */
366
367 if ( disabled ) {
368#ifdef IP_USE_DISABLE
369 if (irq>4){
370 disabled=0;
371 enable_irq(irq);}
372#else
373 disabled=0;
374 /*printk("reenabling irq %d\n",irq); */
375#endif
376 }
377// used to do 'goto repeat;' here, this delayed bh processing too long
378 return IRQ_HANDLED;
379 }
380 }
381 if (mer && ccleirq>0 && !aliased_irq)
382 printk("ISA interrupt from unknown source? EIRQ_REG = %x\n",mer),ccleirq--;
383 }
384 iirq:
385 mir=master_inb(IIRQ_REG);
386 /* should test whether keyboard irq is really enabled, doing it in defhand */
387 if (mir&Q40_IRQ_KEYB_MASK) {
388 irq_tab[Q40_IRQ_KEYBOARD].count++;
389 irq_tab[Q40_IRQ_KEYBOARD].handler(Q40_IRQ_KEYBOARD,irq_tab[Q40_IRQ_KEYBOARD].dev_id,fp);
390 }
391 return IRQ_HANDLED;
392}
393
394int show_q40_interrupts (struct seq_file *p, void *v)
395{
396 int i;
397
398 for (i = 0; i <= Q40_IRQ_MAX; i++) {
399 if (irq_tab[i].count)
400 seq_printf(p, "%sIRQ %02d: %8d %s%s\n",
401 (i<=15) ? "ISA-" : " " ,
402 i, irq_tab[i].count,
403 irq_tab[i].devname[0] ? irq_tab[i].devname : "?",
404 irq_tab[i].handler == q40_defhand ?
405 " (now unassigned)" : "");
406 }
407 return 0;
408}
409
410
411static irqreturn_t q40_defhand (int irq, void *dev_id, struct pt_regs *fp)
412{
413 if (irq!=Q40_IRQ_KEYBOARD)
414 printk ("Unknown q40 interrupt %d\n", irq);
415 else master_outb(-1,KEYBOARD_UNLOCK_REG);
416 return IRQ_NONE;
417}
418static irqreturn_t default_handler(int lev, void *dev_id, struct pt_regs *regs)
419{
420 printk ("Uninitialised interrupt level %d\n", lev);
421 return IRQ_NONE;
422}
423
424irqreturn_t (*q40_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = {
425 [0] = default_handler,
426 [1] = default_handler,
427 [2] = default_handler,
428 [3] = default_handler,
429 [4] = default_handler,
430 [5] = default_handler,
431 [6] = default_handler,
432 [7] = default_handler
433};
434
435
436void q40_enable_irq (unsigned int irq)
437{
438 if ( irq>=5 && irq<=15 )
439 {
440 mext_disabled--;
441 if (mext_disabled>0)
442 printk("q40_enable_irq : nested disable/enable\n");
443 if (mext_disabled==0)
444 master_outb(1,EXT_ENABLE_REG);
445 }
446}
447
448
449void q40_disable_irq (unsigned int irq)
450{
451 /* disable ISA iqs : only do something if the driver has been
452 * verified to be Q40 "compatible" - right now IDE, NE2K
453 * Any driver should not attempt to sleep across disable_irq !!
454 */
455
456 if ( irq>=5 && irq<=15 ) {
457 master_outb(0,EXT_ENABLE_REG);
458 mext_disabled++;
459 if (mext_disabled>1) printk("disable_irq nesting count %d\n",mext_disabled);
460 }
461}
462
463unsigned long q40_probe_irq_on (void)
464{
465 printk("irq probing not working - reconfigure the driver to avoid this\n");
466 return -1;
467}
468int q40_probe_irq_off (unsigned long irqs)
469{
470 return -1;
471}
472/*
473 * Local variables:
474 * compile-command: "m68k-linux-gcc -D__KERNEL__ -I/home/rz/lx/linux-2.2.6/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -ffixed-a2 -m68040 -c -o q40ints.o q40ints.c"
475 * End:
476 */