diff options
Diffstat (limited to 'arch/m68k/mac')
-rw-r--r-- | arch/m68k/mac/Makefile | 6 | ||||
-rw-r--r-- | arch/m68k/mac/baboon.c | 126 | ||||
-rw-r--r-- | arch/m68k/mac/bootparse.c | 122 | ||||
-rw-r--r-- | arch/m68k/mac/config.c | 902 | ||||
-rw-r--r-- | arch/m68k/mac/debug.c | 398 | ||||
-rw-r--r-- | arch/m68k/mac/iop.c | 714 | ||||
-rw-r--r-- | arch/m68k/mac/mac_ksyms.c | 8 | ||||
-rw-r--r-- | arch/m68k/mac/mac_penguin.S | 75 | ||||
-rw-r--r-- | arch/m68k/mac/macboing.c | 309 | ||||
-rw-r--r-- | arch/m68k/mac/macints.c | 760 | ||||
-rw-r--r-- | arch/m68k/mac/misc.c | 651 | ||||
-rw-r--r-- | arch/m68k/mac/oss.c | 301 | ||||
-rw-r--r-- | arch/m68k/mac/psc.c | 197 | ||||
-rw-r--r-- | arch/m68k/mac/via.c | 619 |
14 files changed, 5188 insertions, 0 deletions
diff --git a/arch/m68k/mac/Makefile b/arch/m68k/mac/Makefile new file mode 100644 index 000000000000..995a09d912f5 --- /dev/null +++ b/arch/m68k/mac/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | # | ||
2 | # Makefile for Linux arch/m68k/mac source directory | ||
3 | # | ||
4 | |||
5 | obj-y := config.o bootparse.o macints.o iop.o via.o oss.o psc.o \ | ||
6 | baboon.o macboing.o debug.o misc.o mac_ksyms.o | ||
diff --git a/arch/m68k/mac/baboon.c b/arch/m68k/mac/baboon.c new file mode 100644 index 000000000000..b19b7dd9bd21 --- /dev/null +++ b/arch/m68k/mac/baboon.c | |||
@@ -0,0 +1,126 @@ | |||
1 | /* | ||
2 | * Baboon Custom IC Management | ||
3 | * | ||
4 | * The Baboon custom IC controls the IDE, PCMCIA and media bay on the | ||
5 | * PowerBook 190. It multiplexes multiple interrupt sources onto the | ||
6 | * Nubus slot $C interrupt. | ||
7 | */ | ||
8 | |||
9 | #include <linux/types.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/mm.h> | ||
12 | #include <linux/delay.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/ide.h> | ||
15 | |||
16 | #include <asm/traps.h> | ||
17 | #include <asm/bootinfo.h> | ||
18 | #include <asm/macintosh.h> | ||
19 | #include <asm/macints.h> | ||
20 | #include <asm/mac_baboon.h> | ||
21 | |||
22 | /* #define DEBUG_BABOON */ | ||
23 | /* #define DEBUG_IRQS */ | ||
24 | |||
25 | int baboon_present,baboon_active; | ||
26 | volatile struct baboon *baboon; | ||
27 | |||
28 | irqreturn_t baboon_irq(int, void *, struct pt_regs *); | ||
29 | |||
30 | #if 0 | ||
31 | extern int macide_ack_intr(struct ata_channel *); | ||
32 | #endif | ||
33 | |||
34 | /* | ||
35 | * Baboon initialization. | ||
36 | */ | ||
37 | |||
38 | void __init baboon_init(void) | ||
39 | { | ||
40 | if (macintosh_config->ident != MAC_MODEL_PB190) { | ||
41 | baboon = NULL; | ||
42 | baboon_present = 0; | ||
43 | return; | ||
44 | } | ||
45 | |||
46 | baboon = (struct baboon *) BABOON_BASE; | ||
47 | baboon_present = 1; | ||
48 | baboon_active = 0; | ||
49 | |||
50 | printk("Baboon detected at %p\n", baboon); | ||
51 | } | ||
52 | |||
53 | /* | ||
54 | * Register the Baboon interrupt dispatcher on nubus slot $C. | ||
55 | */ | ||
56 | |||
57 | void __init baboon_register_interrupts(void) | ||
58 | { | ||
59 | request_irq(IRQ_NUBUS_C, baboon_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, | ||
60 | "baboon", (void *) baboon); | ||
61 | } | ||
62 | |||
63 | /* | ||
64 | * Baboon interrupt handler. This works a lot like a VIA. | ||
65 | */ | ||
66 | |||
67 | irqreturn_t baboon_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
68 | { | ||
69 | int irq_bit,i; | ||
70 | unsigned char events; | ||
71 | |||
72 | #ifdef DEBUG_IRQS | ||
73 | printk("baboon_irq: mb_control %02X mb_ifr %02X mb_status %02X active %02X\n", | ||
74 | (uint) baboon->mb_control, (uint) baboon->mb_ifr, | ||
75 | (uint) baboon->mb_status, baboon_active); | ||
76 | #endif | ||
77 | |||
78 | if (!(events = baboon->mb_ifr & 0x07)) | ||
79 | return IRQ_NONE; | ||
80 | |||
81 | for (i = 0, irq_bit = 1 ; i < 3 ; i++, irq_bit <<= 1) { | ||
82 | if (events & irq_bit/* & baboon_active*/) { | ||
83 | baboon_active &= ~irq_bit; | ||
84 | mac_do_irq_list(IRQ_BABOON_0 + i, regs); | ||
85 | baboon_active |= irq_bit; | ||
86 | baboon->mb_ifr &= ~irq_bit; | ||
87 | } | ||
88 | } | ||
89 | #if 0 | ||
90 | if (baboon->mb_ifr & 0x02) macide_ack_intr(NULL); | ||
91 | /* for now we need to smash all interrupts */ | ||
92 | baboon->mb_ifr &= ~events; | ||
93 | #endif | ||
94 | return IRQ_HANDLED; | ||
95 | } | ||
96 | |||
97 | void baboon_irq_enable(int irq) { | ||
98 | int irq_idx = IRQ_IDX(irq); | ||
99 | |||
100 | #ifdef DEBUG_IRQUSE | ||
101 | printk("baboon_irq_enable(%d)\n", irq); | ||
102 | #endif | ||
103 | baboon_active |= (1 << irq_idx); | ||
104 | } | ||
105 | |||
106 | void baboon_irq_disable(int irq) { | ||
107 | int irq_idx = IRQ_IDX(irq); | ||
108 | |||
109 | #ifdef DEBUG_IRQUSE | ||
110 | printk("baboon_irq_disable(%d)\n", irq); | ||
111 | #endif | ||
112 | baboon_active &= ~(1 << irq_idx); | ||
113 | } | ||
114 | |||
115 | void baboon_irq_clear(int irq) { | ||
116 | int irq_idx = IRQ_IDX(irq); | ||
117 | |||
118 | baboon->mb_ifr &= ~(1 << irq_idx); | ||
119 | } | ||
120 | |||
121 | int baboon_irq_pending(int irq) | ||
122 | { | ||
123 | int irq_idx = IRQ_IDX(irq); | ||
124 | |||
125 | return baboon->mb_ifr & (1 << irq_idx); | ||
126 | } | ||
diff --git a/arch/m68k/mac/bootparse.c b/arch/m68k/mac/bootparse.c new file mode 100644 index 000000000000..36d223609823 --- /dev/null +++ b/arch/m68k/mac/bootparse.c | |||
@@ -0,0 +1,122 @@ | |||
1 | #include <linux/string.h> | ||
2 | #include <linux/kernel.h> | ||
3 | #include <linux/sched.h> | ||
4 | #include <asm/irq.h> | ||
5 | #include <asm/setup.h> | ||
6 | #include <asm/bootinfo.h> | ||
7 | #include <asm/macintosh.h> | ||
8 | |||
9 | /* | ||
10 | * Booter vars | ||
11 | */ | ||
12 | |||
13 | int boothowto; | ||
14 | int _boothowto; | ||
15 | |||
16 | /* | ||
17 | * Called early to parse the environment (passed to us from the booter) | ||
18 | * into a bootinfo struct. Will die as soon as we have our own booter | ||
19 | */ | ||
20 | |||
21 | #define atol(x) simple_strtoul(x,NULL,0) | ||
22 | |||
23 | void parse_booter(char *env) | ||
24 | { | ||
25 | char *name; | ||
26 | char *value; | ||
27 | #if 0 | ||
28 | while(0 && *env) | ||
29 | #else | ||
30 | while(*env) | ||
31 | #endif | ||
32 | { | ||
33 | name=env; | ||
34 | value=name; | ||
35 | while(*value!='='&&*value) | ||
36 | value++; | ||
37 | if(*value=='=') | ||
38 | *value++=0; | ||
39 | env=value; | ||
40 | while(*env) | ||
41 | env++; | ||
42 | env++; | ||
43 | #if 0 | ||
44 | if(strcmp(name,"VIDEO_ADDR")==0) | ||
45 | mac_mch.videoaddr=atol(value); | ||
46 | if(strcmp(name,"ROW_BYTES")==0) | ||
47 | mac_mch.videorow=atol(value); | ||
48 | if(strcmp(name,"SCREEN_DEPTH")==0) | ||
49 | mac_mch.videodepth=atol(value); | ||
50 | if(strcmp(name,"DIMENSIONS")==0) | ||
51 | mac_mch.dimensions=atol(value); | ||
52 | #endif | ||
53 | if(strcmp(name,"BOOTTIME")==0) | ||
54 | mac_bi_data.boottime=atol(value); | ||
55 | if(strcmp(name,"GMTBIAS")==0) | ||
56 | mac_bi_data.gmtbias=atol(value); | ||
57 | if(strcmp(name,"BOOTERVER")==0) | ||
58 | mac_bi_data.bootver=atol(value); | ||
59 | if(strcmp(name,"MACOS_VIDEO")==0) | ||
60 | mac_bi_data.videological=atol(value); | ||
61 | if(strcmp(name,"MACOS_SCC")==0) | ||
62 | mac_bi_data.sccbase=atol(value); | ||
63 | if(strcmp(name,"MACHINEID")==0) | ||
64 | mac_bi_data.id=atol(value); | ||
65 | if(strcmp(name,"MEMSIZE")==0) | ||
66 | mac_bi_data.memsize=atol(value); | ||
67 | if(strcmp(name,"SERIAL_MODEM_FLAGS")==0) | ||
68 | mac_bi_data.serialmf=atol(value); | ||
69 | if(strcmp(name,"SERIAL_MODEM_HSKICLK")==0) | ||
70 | mac_bi_data.serialhsk=atol(value); | ||
71 | if(strcmp(name,"SERIAL_MODEM_GPICLK")==0) | ||
72 | mac_bi_data.serialgpi=atol(value); | ||
73 | if(strcmp(name,"SERIAL_PRINT_FLAGS")==0) | ||
74 | mac_bi_data.printmf=atol(value); | ||
75 | if(strcmp(name,"SERIAL_PRINT_HSKICLK")==0) | ||
76 | mac_bi_data.printhsk=atol(value); | ||
77 | if(strcmp(name,"SERIAL_PRINT_GPICLK")==0) | ||
78 | mac_bi_data.printgpi=atol(value); | ||
79 | if(strcmp(name,"PROCESSOR")==0) | ||
80 | mac_bi_data.cpuid=atol(value); | ||
81 | if(strcmp(name,"ROMBASE")==0) | ||
82 | mac_bi_data.rombase=atol(value); | ||
83 | if(strcmp(name,"TIMEDBRA")==0) | ||
84 | mac_bi_data.timedbra=atol(value); | ||
85 | if(strcmp(name,"ADBDELAY")==0) | ||
86 | mac_bi_data.adbdelay=atol(value); | ||
87 | } | ||
88 | #if 0 /* XXX: TODO with m68k_mach_* */ | ||
89 | /* Fill in the base stuff */ | ||
90 | boot_info.machtype=MACH_MAC; | ||
91 | /* Read this from the macinfo we got ! */ | ||
92 | /* boot_info.cputype=CPU_68020|FPUB_68881;*/ | ||
93 | /* boot_info.memory[0].addr=0;*/ | ||
94 | /* boot_info.memory[0].size=((mac_bi_data.id>>7)&31)<<20;*/ | ||
95 | boot_info.num_memory=1; /* On a MacII */ | ||
96 | boot_info.ramdisk_size=0; /* For now */ | ||
97 | *boot_info.command_line=0; | ||
98 | #endif | ||
99 | } | ||
100 | |||
101 | |||
102 | void print_booter(char *env) | ||
103 | { | ||
104 | char *name; | ||
105 | char *value; | ||
106 | while(*env) | ||
107 | { | ||
108 | name=env; | ||
109 | value=name; | ||
110 | while(*value!='='&&*value) | ||
111 | value++; | ||
112 | if(*value=='=') | ||
113 | *value++=0; | ||
114 | env=value; | ||
115 | while(*env) | ||
116 | env++; | ||
117 | env++; | ||
118 | printk("%s=%s\n", name,value); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | |||
diff --git a/arch/m68k/mac/config.c b/arch/m68k/mac/config.c new file mode 100644 index 000000000000..cd19cbb213e8 --- /dev/null +++ b/arch/m68k/mac/config.c | |||
@@ -0,0 +1,902 @@ | |||
1 | /* | ||
2 | * linux/arch/m68k/mac/config.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 | |||
9 | /* | ||
10 | * Miscellaneous linux stuff | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include <linux/tty.h> | ||
18 | #include <linux/console.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | /* keyb */ | ||
21 | #include <linux/random.h> | ||
22 | #include <linux/delay.h> | ||
23 | /* keyb */ | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/vt_kern.h> | ||
26 | |||
27 | #define BOOTINFO_COMPAT_1_0 | ||
28 | #include <asm/setup.h> | ||
29 | #include <asm/bootinfo.h> | ||
30 | |||
31 | #include <asm/system.h> | ||
32 | #include <asm/io.h> | ||
33 | #include <asm/irq.h> | ||
34 | #include <asm/pgtable.h> | ||
35 | #include <asm/rtc.h> | ||
36 | #include <asm/machdep.h> | ||
37 | |||
38 | #include <asm/macintosh.h> | ||
39 | #include <asm/macints.h> | ||
40 | #include <asm/machw.h> | ||
41 | |||
42 | #include <asm/mac_iop.h> | ||
43 | #include <asm/mac_via.h> | ||
44 | #include <asm/mac_oss.h> | ||
45 | #include <asm/mac_psc.h> | ||
46 | |||
47 | /* Mac bootinfo struct */ | ||
48 | |||
49 | struct mac_booter_data mac_bi_data; | ||
50 | int mac_bisize = sizeof mac_bi_data; | ||
51 | |||
52 | struct mac_hw_present mac_hw_present; | ||
53 | |||
54 | /* New m68k bootinfo stuff and videobase */ | ||
55 | |||
56 | extern int m68k_num_memory; | ||
57 | extern struct mem_info m68k_memory[NUM_MEMINFO]; | ||
58 | |||
59 | extern struct mem_info m68k_ramdisk; | ||
60 | |||
61 | extern char m68k_command_line[CL_SIZE]; | ||
62 | |||
63 | void *mac_env; /* Loaded by the boot asm */ | ||
64 | |||
65 | /* The phys. video addr. - might be bogus on some machines */ | ||
66 | unsigned long mac_orig_videoaddr; | ||
67 | |||
68 | /* Mac specific timer functions */ | ||
69 | extern unsigned long mac_gettimeoffset (void); | ||
70 | extern int mac_hwclk (int, struct rtc_time *); | ||
71 | extern int mac_set_clock_mmss (unsigned long); | ||
72 | extern int show_mac_interrupts(struct seq_file *, void *); | ||
73 | extern void iop_preinit(void); | ||
74 | extern void iop_init(void); | ||
75 | extern void via_init(void); | ||
76 | extern void via_init_clock(irqreturn_t (*func)(int, void *, struct pt_regs *)); | ||
77 | extern void via_flush_cache(void); | ||
78 | extern void oss_init(void); | ||
79 | extern void psc_init(void); | ||
80 | extern void baboon_init(void); | ||
81 | |||
82 | extern void mac_mksound(unsigned int, unsigned int); | ||
83 | |||
84 | extern void nubus_sweep_video(void); | ||
85 | |||
86 | /* Mac specific debug functions (in debug.c) */ | ||
87 | extern void mac_debug_init(void); | ||
88 | extern void mac_debugging_long(int, long); | ||
89 | |||
90 | static void mac_get_model(char *str); | ||
91 | |||
92 | void mac_bang(int irq, void *vector, struct pt_regs *p) | ||
93 | { | ||
94 | printk(KERN_INFO "Resetting ...\n"); | ||
95 | mac_reset(); | ||
96 | } | ||
97 | |||
98 | static void mac_sched_init(irqreturn_t (*vector)(int, void *, struct pt_regs *)) | ||
99 | { | ||
100 | via_init_clock(vector); | ||
101 | } | ||
102 | |||
103 | #if 0 | ||
104 | void mac_waitbut (void) | ||
105 | { | ||
106 | ; | ||
107 | } | ||
108 | #endif | ||
109 | |||
110 | extern irqreturn_t mac_default_handler(int, void *, struct pt_regs *); | ||
111 | |||
112 | irqreturn_t (*mac_handlers[8])(int, void *, struct pt_regs *)= | ||
113 | { | ||
114 | mac_default_handler, | ||
115 | mac_default_handler, | ||
116 | mac_default_handler, | ||
117 | mac_default_handler, | ||
118 | mac_default_handler, | ||
119 | mac_default_handler, | ||
120 | mac_default_handler, | ||
121 | mac_default_handler | ||
122 | }; | ||
123 | |||
124 | /* | ||
125 | * Parse a Macintosh-specific record in the bootinfo | ||
126 | */ | ||
127 | |||
128 | int __init mac_parse_bootinfo(const struct bi_record *record) | ||
129 | { | ||
130 | int unknown = 0; | ||
131 | const u_long *data = record->data; | ||
132 | |||
133 | switch (record->tag) { | ||
134 | case BI_MAC_MODEL: | ||
135 | mac_bi_data.id = *data; | ||
136 | break; | ||
137 | case BI_MAC_VADDR: | ||
138 | mac_bi_data.videoaddr = *data; | ||
139 | break; | ||
140 | case BI_MAC_VDEPTH: | ||
141 | mac_bi_data.videodepth = *data; | ||
142 | break; | ||
143 | case BI_MAC_VROW: | ||
144 | mac_bi_data.videorow = *data; | ||
145 | break; | ||
146 | case BI_MAC_VDIM: | ||
147 | mac_bi_data.dimensions = *data; | ||
148 | break; | ||
149 | case BI_MAC_VLOGICAL: | ||
150 | mac_bi_data.videological = VIDEOMEMBASE + (*data & ~VIDEOMEMMASK); | ||
151 | mac_orig_videoaddr = *data; | ||
152 | break; | ||
153 | case BI_MAC_SCCBASE: | ||
154 | mac_bi_data.sccbase = *data; | ||
155 | break; | ||
156 | case BI_MAC_BTIME: | ||
157 | mac_bi_data.boottime = *data; | ||
158 | break; | ||
159 | case BI_MAC_GMTBIAS: | ||
160 | mac_bi_data.gmtbias = *data; | ||
161 | break; | ||
162 | case BI_MAC_MEMSIZE: | ||
163 | mac_bi_data.memsize = *data; | ||
164 | break; | ||
165 | case BI_MAC_CPUID: | ||
166 | mac_bi_data.cpuid = *data; | ||
167 | break; | ||
168 | case BI_MAC_ROMBASE: | ||
169 | mac_bi_data.rombase = *data; | ||
170 | break; | ||
171 | default: | ||
172 | unknown = 1; | ||
173 | } | ||
174 | return(unknown); | ||
175 | } | ||
176 | |||
177 | /* | ||
178 | * Flip into 24bit mode for an instant - flushes the L2 cache card. We | ||
179 | * have to disable interrupts for this. Our IRQ handlers will crap | ||
180 | * themselves if they take an IRQ in 24bit mode! | ||
181 | */ | ||
182 | |||
183 | static void mac_cache_card_flush(int writeback) | ||
184 | { | ||
185 | unsigned long flags; | ||
186 | local_irq_save(flags); | ||
187 | via_flush_cache(); | ||
188 | local_irq_restore(flags); | ||
189 | } | ||
190 | |||
191 | void __init config_mac(void) | ||
192 | { | ||
193 | if (!MACH_IS_MAC) { | ||
194 | printk(KERN_ERR "ERROR: no Mac, but config_mac() called!! \n"); | ||
195 | } | ||
196 | |||
197 | mach_sched_init = mac_sched_init; | ||
198 | mach_init_IRQ = mac_init_IRQ; | ||
199 | mach_request_irq = mac_request_irq; | ||
200 | mach_free_irq = mac_free_irq; | ||
201 | enable_irq = mac_enable_irq; | ||
202 | disable_irq = mac_disable_irq; | ||
203 | mach_get_model = mac_get_model; | ||
204 | mach_default_handler = &mac_handlers; | ||
205 | mach_get_irq_list = show_mac_interrupts; | ||
206 | mach_gettimeoffset = mac_gettimeoffset; | ||
207 | #warning move to adb/via init | ||
208 | #if 0 | ||
209 | mach_hwclk = mac_hwclk; | ||
210 | #endif | ||
211 | mach_set_clock_mmss = mac_set_clock_mmss; | ||
212 | mach_reset = mac_reset; | ||
213 | mach_halt = mac_poweroff; | ||
214 | mach_power_off = mac_poweroff; | ||
215 | #ifdef CONFIG_DUMMY_CONSOLE | ||
216 | conswitchp = &dummy_con; | ||
217 | #endif | ||
218 | mach_max_dma_address = 0xffffffff; | ||
219 | #if 0 | ||
220 | mach_debug_init = mac_debug_init; | ||
221 | #endif | ||
222 | #if defined(CONFIG_INPUT_M68K_BEEP) || defined(CONFIG_INPUT_M68K_BEEP_MODULE) | ||
223 | mach_beep = mac_mksound; | ||
224 | #endif | ||
225 | #ifdef CONFIG_HEARTBEAT | ||
226 | #if 0 | ||
227 | mach_heartbeat = mac_heartbeat; | ||
228 | mach_heartbeat_irq = IRQ_MAC_TIMER; | ||
229 | #endif | ||
230 | #endif | ||
231 | |||
232 | /* | ||
233 | * Determine hardware present | ||
234 | */ | ||
235 | |||
236 | mac_identify(); | ||
237 | mac_report_hardware(); | ||
238 | |||
239 | /* AFAIK only the IIci takes a cache card. The IIfx has onboard | ||
240 | cache ... someone needs to figure out how to tell if it's on or | ||
241 | not. */ | ||
242 | |||
243 | if (macintosh_config->ident == MAC_MODEL_IICI | ||
244 | || macintosh_config->ident == MAC_MODEL_IIFX) { | ||
245 | mach_l2_flush = mac_cache_card_flush; | ||
246 | } | ||
247 | |||
248 | /* | ||
249 | * Check for machine specific fixups. | ||
250 | */ | ||
251 | |||
252 | #ifdef OLD_NUBUS_CODE | ||
253 | nubus_sweep_video(); | ||
254 | #endif | ||
255 | } | ||
256 | |||
257 | |||
258 | /* | ||
259 | * Macintosh Table: hardcoded model configuration data. | ||
260 | * | ||
261 | * Much of this was defined by Alan, based on who knows what docs. | ||
262 | * I've added a lot more, and some of that was pure guesswork based | ||
263 | * on hardware pages present on the Mac web site. Possibly wildly | ||
264 | * inaccurate, so look here if a new Mac model won't run. Example: if | ||
265 | * a Mac crashes immediately after the VIA1 registers have been dumped | ||
266 | * to the screen, it probably died attempting to read DirB on a RBV. | ||
267 | * Meaning it should have MAC_VIA_IIci here :-) | ||
268 | */ | ||
269 | |||
270 | struct mac_model *macintosh_config; | ||
271 | EXPORT_SYMBOL(macintosh_config); | ||
272 | |||
273 | static struct mac_model mac_data_table[]= | ||
274 | { | ||
275 | /* | ||
276 | * We'll pretend to be a Macintosh II, that's pretty safe. | ||
277 | */ | ||
278 | |||
279 | { | ||
280 | .ident = MAC_MODEL_II, | ||
281 | .name = "Unknown", | ||
282 | .adb_type = MAC_ADB_II, | ||
283 | .via_type = MAC_VIA_II, | ||
284 | .scsi_type = MAC_SCSI_OLD, | ||
285 | .scc_type = MAC_SCC_II, | ||
286 | .nubus_type = MAC_NUBUS | ||
287 | }, | ||
288 | |||
289 | /* | ||
290 | * Original MacII hardware | ||
291 | * | ||
292 | */ | ||
293 | |||
294 | { | ||
295 | .ident = MAC_MODEL_II, | ||
296 | .name = "II", | ||
297 | .adb_type = MAC_ADB_II, | ||
298 | .via_type = MAC_VIA_II, | ||
299 | .scsi_type = MAC_SCSI_OLD, | ||
300 | .scc_type = MAC_SCC_II, | ||
301 | .nubus_type = MAC_NUBUS | ||
302 | }, { | ||
303 | .ident = MAC_MODEL_IIX, | ||
304 | .name = "IIx", | ||
305 | .adb_type = MAC_ADB_II, | ||
306 | .via_type = MAC_VIA_II, | ||
307 | .scsi_type = MAC_SCSI_OLD, | ||
308 | .scc_type = MAC_SCC_II, | ||
309 | .nubus_type = MAC_NUBUS | ||
310 | }, { | ||
311 | .ident = MAC_MODEL_IICX, | ||
312 | .name = "IIcx", | ||
313 | .adb_type = MAC_ADB_II, | ||
314 | .via_type = MAC_VIA_II, | ||
315 | .scsi_type = MAC_SCSI_OLD, | ||
316 | .scc_type = MAC_SCC_II, | ||
317 | .nubus_type = MAC_NUBUS | ||
318 | }, { | ||
319 | .ident = MAC_MODEL_SE30, | ||
320 | .name = "SE/30", | ||
321 | .adb_type = MAC_ADB_II, | ||
322 | .via_type = MAC_VIA_II, | ||
323 | .scsi_type = MAC_SCSI_OLD, | ||
324 | .scc_type = MAC_SCC_II, | ||
325 | .nubus_type = MAC_NUBUS | ||
326 | }, | ||
327 | |||
328 | /* | ||
329 | * Weirdified MacII hardware - all subtley different. Gee thanks | ||
330 | * Apple. All these boxes seem to have VIA2 in a different place to | ||
331 | * the MacII (+1A000 rather than +4000) | ||
332 | * CSA: see http://developer.apple.com/technotes/hw/hw_09.html | ||
333 | */ | ||
334 | |||
335 | { | ||
336 | .ident = MAC_MODEL_IICI, | ||
337 | .name = "IIci", | ||
338 | .adb_type = MAC_ADB_II, | ||
339 | .via_type = MAC_VIA_IIci, | ||
340 | .scsi_type = MAC_SCSI_OLD, | ||
341 | .scc_type = MAC_SCC_II, | ||
342 | .nubus_type = MAC_NUBUS | ||
343 | }, { | ||
344 | .ident = MAC_MODEL_IIFX, | ||
345 | .name = "IIfx", | ||
346 | .adb_type = MAC_ADB_IOP, | ||
347 | .via_type = MAC_VIA_IIci, | ||
348 | .scsi_type = MAC_SCSI_OLD, | ||
349 | .scc_type = MAC_SCC_IOP, | ||
350 | .nubus_type = MAC_NUBUS | ||
351 | }, { | ||
352 | .ident = MAC_MODEL_IISI, | ||
353 | .name = "IIsi", | ||
354 | .adb_type = MAC_ADB_IISI, | ||
355 | .via_type = MAC_VIA_IIci, | ||
356 | .scsi_type = MAC_SCSI_OLD, | ||
357 | .scc_type = MAC_SCC_II, | ||
358 | .nubus_type = MAC_NUBUS | ||
359 | }, { | ||
360 | .ident = MAC_MODEL_IIVI, | ||
361 | .name = "IIvi", | ||
362 | .adb_type = MAC_ADB_IISI, | ||
363 | .via_type = MAC_VIA_IIci, | ||
364 | .scsi_type = MAC_SCSI_OLD, | ||
365 | .scc_type = MAC_SCC_II, | ||
366 | .nubus_type = MAC_NUBUS | ||
367 | }, { | ||
368 | .ident = MAC_MODEL_IIVX, | ||
369 | .name = "IIvx", | ||
370 | .adb_type = MAC_ADB_IISI, | ||
371 | .via_type = MAC_VIA_IIci, | ||
372 | .scsi_type = MAC_SCSI_OLD, | ||
373 | .scc_type = MAC_SCC_II, | ||
374 | .nubus_type = MAC_NUBUS | ||
375 | }, | ||
376 | |||
377 | /* | ||
378 | * Classic models (guessing: similar to SE/30 ?? Nope, similar to LC ...) | ||
379 | */ | ||
380 | |||
381 | { | ||
382 | .ident = MAC_MODEL_CLII, | ||
383 | .name = "Classic II", | ||
384 | .adb_type = MAC_ADB_IISI, | ||
385 | .via_type = MAC_VIA_IIci, | ||
386 | .scsi_type = MAC_SCSI_OLD, | ||
387 | .scc_type = MAC_SCC_II, | ||
388 | .nubus_type = MAC_NUBUS | ||
389 | }, { | ||
390 | .ident = MAC_MODEL_CCL, | ||
391 | .name = "Color Classic", | ||
392 | .adb_type = MAC_ADB_CUDA, | ||
393 | .via_type = MAC_VIA_IIci, | ||
394 | .scsi_type = MAC_SCSI_OLD, | ||
395 | .scc_type = MAC_SCC_II, | ||
396 | .nubus_type = MAC_NUBUS}, | ||
397 | |||
398 | /* | ||
399 | * Some Mac LC machines. Basically the same as the IIci, ADB like IIsi | ||
400 | */ | ||
401 | |||
402 | { | ||
403 | .ident = MAC_MODEL_LC, | ||
404 | .name = "LC", | ||
405 | .adb_type = MAC_ADB_IISI, | ||
406 | .via_type = MAC_VIA_IIci, | ||
407 | .scsi_type = MAC_SCSI_OLD, | ||
408 | .scc_type = MAC_SCC_II, | ||
409 | .nubus_type = MAC_NUBUS | ||
410 | }, { | ||
411 | .ident = MAC_MODEL_LCII, | ||
412 | .name = "LC II", | ||
413 | .adb_type = MAC_ADB_IISI, | ||
414 | .via_type = MAC_VIA_IIci, | ||
415 | .scsi_type = MAC_SCSI_OLD, | ||
416 | .scc_type = MAC_SCC_II, | ||
417 | .nubus_type = MAC_NUBUS | ||
418 | }, { | ||
419 | .ident = MAC_MODEL_LCIII, | ||
420 | .name = "LC III", | ||
421 | .adb_type = MAC_ADB_IISI, | ||
422 | .via_type = MAC_VIA_IIci, | ||
423 | .scsi_type = MAC_SCSI_OLD, | ||
424 | .scc_type = MAC_SCC_II, | ||
425 | .nubus_type = MAC_NUBUS | ||
426 | }, | ||
427 | |||
428 | /* | ||
429 | * Quadra. Video is at 0xF9000000, via is like a MacII. We label it differently | ||
430 | * as some of the stuff connected to VIA2 seems different. Better SCSI chip and | ||
431 | * onboard ethernet using a NatSemi SONIC except the 660AV and 840AV which use an | ||
432 | * AMD 79C940 (MACE). | ||
433 | * The 700, 900 and 950 have some I/O chips in the wrong place to | ||
434 | * confuse us. The 840AV has a SCSI location of its own (same as | ||
435 | * the 660AV). | ||
436 | */ | ||
437 | |||
438 | { | ||
439 | .ident = MAC_MODEL_Q605, | ||
440 | .name = "Quadra 605", | ||
441 | .adb_type = MAC_ADB_CUDA, | ||
442 | .via_type = MAC_VIA_QUADRA, | ||
443 | .scsi_type = MAC_SCSI_QUADRA, | ||
444 | .scc_type = MAC_SCC_QUADRA, | ||
445 | .nubus_type = MAC_NUBUS | ||
446 | }, { | ||
447 | .ident = MAC_MODEL_Q605_ACC, | ||
448 | .name = "Quadra 605", | ||
449 | .adb_type = MAC_ADB_CUDA, | ||
450 | .via_type = MAC_VIA_QUADRA, | ||
451 | .scsi_type = MAC_SCSI_QUADRA, | ||
452 | .scc_type = MAC_SCC_QUADRA, | ||
453 | .nubus_type = MAC_NUBUS | ||
454 | }, { | ||
455 | .ident = MAC_MODEL_Q610, | ||
456 | .name = "Quadra 610", | ||
457 | .adb_type = MAC_ADB_II, | ||
458 | .via_type = MAC_VIA_QUADRA, | ||
459 | .scsi_type = MAC_SCSI_QUADRA, | ||
460 | .scc_type = MAC_SCC_QUADRA, | ||
461 | .ether_type = MAC_ETHER_SONIC, | ||
462 | .nubus_type = MAC_NUBUS | ||
463 | }, { | ||
464 | .ident = MAC_MODEL_Q630, | ||
465 | .name = "Quadra 630", | ||
466 | .adb_type = MAC_ADB_CUDA, | ||
467 | .via_type = MAC_VIA_QUADRA, | ||
468 | .scsi_type = MAC_SCSI_QUADRA, | ||
469 | .ide_type = MAC_IDE_QUADRA, | ||
470 | .scc_type = MAC_SCC_QUADRA, | ||
471 | .ether_type = MAC_ETHER_SONIC, | ||
472 | .nubus_type = MAC_NUBUS | ||
473 | }, { | ||
474 | .ident = MAC_MODEL_Q650, | ||
475 | .name = "Quadra 650", | ||
476 | .adb_type = MAC_ADB_II, | ||
477 | .via_type = MAC_VIA_QUADRA, | ||
478 | .scsi_type = MAC_SCSI_QUADRA, | ||
479 | .scc_type = MAC_SCC_QUADRA, | ||
480 | .ether_type = MAC_ETHER_SONIC, | ||
481 | .nubus_type = MAC_NUBUS | ||
482 | }, | ||
483 | /* The Q700 does have a NS Sonic */ | ||
484 | { | ||
485 | .ident = MAC_MODEL_Q700, | ||
486 | .name = "Quadra 700", | ||
487 | .adb_type = MAC_ADB_II, | ||
488 | .via_type = MAC_VIA_QUADRA, | ||
489 | .scsi_type = MAC_SCSI_QUADRA2, | ||
490 | .scc_type = MAC_SCC_QUADRA, | ||
491 | .ether_type = MAC_ETHER_SONIC, | ||
492 | .nubus_type = MAC_NUBUS | ||
493 | }, { | ||
494 | .ident = MAC_MODEL_Q800, | ||
495 | .name = "Quadra 800", | ||
496 | .adb_type = MAC_ADB_II, | ||
497 | .via_type = MAC_VIA_QUADRA, | ||
498 | .scsi_type = MAC_SCSI_QUADRA, | ||
499 | .scc_type = MAC_SCC_QUADRA, | ||
500 | .ether_type = MAC_ETHER_SONIC, | ||
501 | .nubus_type = MAC_NUBUS | ||
502 | }, { | ||
503 | .ident = MAC_MODEL_Q840, | ||
504 | .name = "Quadra 840AV", | ||
505 | .adb_type = MAC_ADB_CUDA, | ||
506 | .via_type = MAC_VIA_QUADRA, | ||
507 | .scsi_type = MAC_SCSI_QUADRA3, | ||
508 | .scc_type = MAC_SCC_PSC, | ||
509 | .ether_type = MAC_ETHER_MACE, | ||
510 | .nubus_type = MAC_NUBUS | ||
511 | }, { | ||
512 | .ident = MAC_MODEL_Q900, | ||
513 | .name = "Quadra 900", | ||
514 | .adb_type = MAC_ADB_IOP, | ||
515 | .via_type = MAC_VIA_QUADRA, | ||
516 | .scsi_type = MAC_SCSI_QUADRA2, | ||
517 | .scc_type = MAC_SCC_IOP, | ||
518 | .ether_type = MAC_ETHER_SONIC, | ||
519 | .nubus_type = MAC_NUBUS | ||
520 | }, { | ||
521 | .ident = MAC_MODEL_Q950, | ||
522 | .name = "Quadra 950", | ||
523 | .adb_type = MAC_ADB_IOP, | ||
524 | .via_type = MAC_VIA_QUADRA, | ||
525 | .scsi_type = MAC_SCSI_QUADRA2, | ||
526 | .scc_type = MAC_SCC_IOP, | ||
527 | .ether_type = MAC_ETHER_SONIC, | ||
528 | .nubus_type = MAC_NUBUS | ||
529 | }, | ||
530 | |||
531 | /* | ||
532 | * Performa - more LC type machines | ||
533 | */ | ||
534 | |||
535 | { | ||
536 | .ident = MAC_MODEL_P460, | ||
537 | .name = "Performa 460", | ||
538 | .adb_type = MAC_ADB_IISI, | ||
539 | .via_type = MAC_VIA_IIci, | ||
540 | .scsi_type = MAC_SCSI_OLD, | ||
541 | .scc_type = MAC_SCC_II, | ||
542 | .nubus_type = MAC_NUBUS | ||
543 | }, { | ||
544 | .ident = MAC_MODEL_P475, | ||
545 | .name = "Performa 475", | ||
546 | .adb_type = MAC_ADB_CUDA, | ||
547 | .via_type = MAC_VIA_QUADRA, | ||
548 | .scsi_type = MAC_SCSI_QUADRA, | ||
549 | .scc_type = MAC_SCC_II, | ||
550 | .nubus_type = MAC_NUBUS | ||
551 | }, { | ||
552 | .ident = MAC_MODEL_P475F, | ||
553 | .name = "Performa 475", | ||
554 | .adb_type = MAC_ADB_CUDA, | ||
555 | .via_type = MAC_VIA_QUADRA, | ||
556 | .scsi_type = MAC_SCSI_QUADRA, | ||
557 | .scc_type = MAC_SCC_II, | ||
558 | .nubus_type = MAC_NUBUS | ||
559 | }, { | ||
560 | .ident = MAC_MODEL_P520, | ||
561 | .name = "Performa 520", | ||
562 | .adb_type = MAC_ADB_CUDA, | ||
563 | .via_type = MAC_VIA_IIci, | ||
564 | .scsi_type = MAC_SCSI_OLD, | ||
565 | .scc_type = MAC_SCC_II, | ||
566 | .nubus_type = MAC_NUBUS | ||
567 | }, { | ||
568 | .ident = MAC_MODEL_P550, | ||
569 | .name = "Performa 550", | ||
570 | .adb_type = MAC_ADB_CUDA, | ||
571 | .via_type = MAC_VIA_IIci, | ||
572 | .scsi_type = MAC_SCSI_OLD, | ||
573 | .scc_type = MAC_SCC_II, | ||
574 | .nubus_type = MAC_NUBUS | ||
575 | }, | ||
576 | /* These have the comm slot, and therefore the possibility of SONIC ethernet */ | ||
577 | { | ||
578 | .ident = MAC_MODEL_P575, | ||
579 | .name = "Performa 575", | ||
580 | .adb_type = MAC_ADB_CUDA, | ||
581 | .via_type = MAC_VIA_QUADRA, | ||
582 | .scsi_type = MAC_SCSI_QUADRA, | ||
583 | .scc_type = MAC_SCC_II, | ||
584 | .ether_type = MAC_ETHER_SONIC, | ||
585 | .nubus_type = MAC_NUBUS | ||
586 | }, { | ||
587 | .ident = MAC_MODEL_P588, | ||
588 | .name = "Performa 588", | ||
589 | .adb_type = MAC_ADB_CUDA, | ||
590 | .via_type = MAC_VIA_QUADRA, | ||
591 | .scsi_type = MAC_SCSI_QUADRA, | ||
592 | .ide_type = MAC_IDE_QUADRA, | ||
593 | .scc_type = MAC_SCC_II, | ||
594 | .ether_type = MAC_ETHER_SONIC, | ||
595 | .nubus_type = MAC_NUBUS | ||
596 | }, { | ||
597 | .ident = MAC_MODEL_TV, | ||
598 | .name = "TV", | ||
599 | .adb_type = MAC_ADB_CUDA, | ||
600 | .via_type = MAC_VIA_QUADRA, | ||
601 | .scsi_type = MAC_SCSI_OLD, | ||
602 | .scc_type = MAC_SCC_II, | ||
603 | .nubus_type = MAC_NUBUS | ||
604 | }, { | ||
605 | .ident = MAC_MODEL_P600, | ||
606 | .name = "Performa 600", | ||
607 | .adb_type = MAC_ADB_IISI, | ||
608 | .via_type = MAC_VIA_IIci, | ||
609 | .scsi_type = MAC_SCSI_OLD, | ||
610 | .scc_type = MAC_SCC_II, | ||
611 | .nubus_type = MAC_NUBUS | ||
612 | }, | ||
613 | |||
614 | /* | ||
615 | * Centris - just guessing again; maybe like Quadra | ||
616 | */ | ||
617 | |||
618 | /* The C610 may or may not have SONIC. We probe to make sure */ | ||
619 | { | ||
620 | .ident = MAC_MODEL_C610, | ||
621 | .name = "Centris 610", | ||
622 | .adb_type = MAC_ADB_II, | ||
623 | .via_type = MAC_VIA_QUADRA, | ||
624 | .scsi_type = MAC_SCSI_QUADRA, | ||
625 | .scc_type = MAC_SCC_QUADRA, | ||
626 | .ether_type = MAC_ETHER_SONIC, | ||
627 | .nubus_type = MAC_NUBUS | ||
628 | }, { | ||
629 | .ident = MAC_MODEL_C650, | ||
630 | .name = "Centris 650", | ||
631 | .adb_type = MAC_ADB_II, | ||
632 | .via_type = MAC_VIA_QUADRA, | ||
633 | .scsi_type = MAC_SCSI_QUADRA, | ||
634 | .scc_type = MAC_SCC_QUADRA, | ||
635 | .ether_type = MAC_ETHER_SONIC, | ||
636 | .nubus_type = MAC_NUBUS | ||
637 | }, { | ||
638 | .ident = MAC_MODEL_C660, | ||
639 | .name = "Centris 660AV", | ||
640 | .adb_type = MAC_ADB_CUDA, | ||
641 | .via_type = MAC_VIA_QUADRA, | ||
642 | .scsi_type = MAC_SCSI_QUADRA3, | ||
643 | .scc_type = MAC_SCC_PSC, | ||
644 | .ether_type = MAC_ETHER_MACE, | ||
645 | .nubus_type = MAC_NUBUS | ||
646 | }, | ||
647 | |||
648 | /* | ||
649 | * The PowerBooks all the same "Combo" custom IC for SCSI and SCC | ||
650 | * and a PMU (in two variations?) for ADB. Most of them use the | ||
651 | * Quadra-style VIAs. A few models also have IDE from hell. | ||
652 | */ | ||
653 | |||
654 | { | ||
655 | .ident = MAC_MODEL_PB140, | ||
656 | .name = "PowerBook 140", | ||
657 | .adb_type = MAC_ADB_PB1, | ||
658 | .via_type = MAC_VIA_QUADRA, | ||
659 | .scsi_type = MAC_SCSI_OLD, | ||
660 | .scc_type = MAC_SCC_QUADRA, | ||
661 | .nubus_type = MAC_NUBUS | ||
662 | }, { | ||
663 | .ident = MAC_MODEL_PB145, | ||
664 | .name = "PowerBook 145", | ||
665 | .adb_type = MAC_ADB_PB1, | ||
666 | .via_type = MAC_VIA_QUADRA, | ||
667 | .scsi_type = MAC_SCSI_OLD, | ||
668 | .scc_type = MAC_SCC_QUADRA, | ||
669 | .nubus_type = MAC_NUBUS | ||
670 | }, { | ||
671 | .ident = MAC_MODEL_PB150, | ||
672 | .name = "PowerBook 150", | ||
673 | .adb_type = MAC_ADB_PB1, | ||
674 | .via_type = MAC_VIA_IIci, | ||
675 | .scsi_type = MAC_SCSI_OLD, | ||
676 | .ide_type = MAC_IDE_PB, | ||
677 | .scc_type = MAC_SCC_QUADRA, | ||
678 | .nubus_type = MAC_NUBUS | ||
679 | }, { | ||
680 | .ident = MAC_MODEL_PB160, | ||
681 | .name = "PowerBook 160", | ||
682 | .adb_type = MAC_ADB_PB1, | ||
683 | .via_type = MAC_VIA_QUADRA, | ||
684 | .scsi_type = MAC_SCSI_OLD, | ||
685 | .scc_type = MAC_SCC_QUADRA, | ||
686 | .nubus_type = MAC_NUBUS | ||
687 | }, { | ||
688 | .ident = MAC_MODEL_PB165, | ||
689 | .name = "PowerBook 165", | ||
690 | .adb_type = MAC_ADB_PB1, | ||
691 | .via_type = MAC_VIA_QUADRA, | ||
692 | .scsi_type = MAC_SCSI_OLD, | ||
693 | .scc_type = MAC_SCC_QUADRA, | ||
694 | .nubus_type = MAC_NUBUS | ||
695 | }, { | ||
696 | .ident = MAC_MODEL_PB165C, | ||
697 | .name = "PowerBook 165c", | ||
698 | .adb_type = MAC_ADB_PB1, | ||
699 | .via_type = MAC_VIA_QUADRA, | ||
700 | .scsi_type = MAC_SCSI_OLD, | ||
701 | .scc_type = MAC_SCC_QUADRA, | ||
702 | .nubus_type = MAC_NUBUS | ||
703 | }, { | ||
704 | .ident = MAC_MODEL_PB170, | ||
705 | .name = "PowerBook 170", | ||
706 | .adb_type = MAC_ADB_PB1, | ||
707 | .via_type = MAC_VIA_QUADRA, | ||
708 | .scsi_type = MAC_SCSI_OLD, | ||
709 | .scc_type = MAC_SCC_QUADRA, | ||
710 | .nubus_type = MAC_NUBUS | ||
711 | }, { | ||
712 | .ident = MAC_MODEL_PB180, | ||
713 | .name = "PowerBook 180", | ||
714 | .adb_type = MAC_ADB_PB1, | ||
715 | .via_type = MAC_VIA_QUADRA, | ||
716 | .scsi_type = MAC_SCSI_OLD, | ||
717 | .scc_type = MAC_SCC_QUADRA, | ||
718 | .nubus_type = MAC_NUBUS | ||
719 | }, { | ||
720 | .ident = MAC_MODEL_PB180C, | ||
721 | .name = "PowerBook 180c", | ||
722 | .adb_type = MAC_ADB_PB1, | ||
723 | .via_type = MAC_VIA_QUADRA, | ||
724 | .scsi_type = MAC_SCSI_OLD, | ||
725 | .scc_type = MAC_SCC_QUADRA, | ||
726 | .nubus_type = MAC_NUBUS | ||
727 | }, { | ||
728 | .ident = MAC_MODEL_PB190, | ||
729 | .name = "PowerBook 190", | ||
730 | .adb_type = MAC_ADB_PB2, | ||
731 | .via_type = MAC_VIA_QUADRA, | ||
732 | .scsi_type = MAC_SCSI_OLD, | ||
733 | .ide_type = MAC_IDE_BABOON, | ||
734 | .scc_type = MAC_SCC_QUADRA, | ||
735 | .nubus_type = MAC_NUBUS | ||
736 | }, { | ||
737 | .ident = MAC_MODEL_PB520, | ||
738 | .name = "PowerBook 520", | ||
739 | .adb_type = MAC_ADB_PB2, | ||
740 | .via_type = MAC_VIA_QUADRA, | ||
741 | .scsi_type = MAC_SCSI_OLD, | ||
742 | .scc_type = MAC_SCC_QUADRA, | ||
743 | .ether_type = MAC_ETHER_SONIC, | ||
744 | .nubus_type = MAC_NUBUS | ||
745 | }, | ||
746 | |||
747 | /* | ||
748 | * PowerBook Duos are pretty much like normal PowerBooks | ||
749 | * All of these probably have onboard SONIC in the Dock which | ||
750 | * means we'll have to probe for it eventually. | ||
751 | * | ||
752 | * Are these reallly MAC_VIA_IIci? The developer notes for the | ||
753 | * Duos show pretty much the same custom parts as in most of | ||
754 | * the other PowerBooks which would imply MAC_VIA_QUADRA. | ||
755 | */ | ||
756 | |||
757 | { | ||
758 | .ident = MAC_MODEL_PB210, | ||
759 | .name = "PowerBook Duo 210", | ||
760 | .adb_type = MAC_ADB_PB2, | ||
761 | .via_type = MAC_VIA_IIci, | ||
762 | .scsi_type = MAC_SCSI_OLD, | ||
763 | .scc_type = MAC_SCC_QUADRA, | ||
764 | .nubus_type = MAC_NUBUS | ||
765 | }, { | ||
766 | .ident = MAC_MODEL_PB230, | ||
767 | .name = "PowerBook Duo 230", | ||
768 | .adb_type = MAC_ADB_PB2, | ||
769 | .via_type = MAC_VIA_IIci, | ||
770 | .scsi_type = MAC_SCSI_OLD, | ||
771 | .scc_type = MAC_SCC_QUADRA, | ||
772 | .nubus_type = MAC_NUBUS | ||
773 | }, { | ||
774 | .ident = MAC_MODEL_PB250, | ||
775 | .name = "PowerBook Duo 250", | ||
776 | .adb_type = MAC_ADB_PB2, | ||
777 | .via_type = MAC_VIA_IIci, | ||
778 | .scsi_type = MAC_SCSI_OLD, | ||
779 | .scc_type = MAC_SCC_QUADRA, | ||
780 | .nubus_type = MAC_NUBUS | ||
781 | }, { | ||
782 | .ident = MAC_MODEL_PB270C, | ||
783 | .name = "PowerBook Duo 270c", | ||
784 | .adb_type = MAC_ADB_PB2, | ||
785 | .via_type = MAC_VIA_IIci, | ||
786 | .scsi_type = MAC_SCSI_OLD, | ||
787 | .scc_type = MAC_SCC_QUADRA, | ||
788 | .nubus_type = MAC_NUBUS | ||
789 | }, { | ||
790 | .ident = MAC_MODEL_PB280, | ||
791 | .name = "PowerBook Duo 280", | ||
792 | .adb_type = MAC_ADB_PB2, | ||
793 | .via_type = MAC_VIA_IIci, | ||
794 | .scsi_type = MAC_SCSI_OLD, | ||
795 | .scc_type = MAC_SCC_QUADRA, | ||
796 | .nubus_type = MAC_NUBUS | ||
797 | }, { | ||
798 | .ident = MAC_MODEL_PB280C, | ||
799 | .name = "PowerBook Duo 280c", | ||
800 | .adb_type = MAC_ADB_PB2, | ||
801 | .via_type = MAC_VIA_IIci, | ||
802 | .scsi_type = MAC_SCSI_OLD, | ||
803 | .scc_type = MAC_SCC_QUADRA, | ||
804 | .nubus_type = MAC_NUBUS | ||
805 | }, | ||
806 | |||
807 | /* | ||
808 | * Other stuff ?? | ||
809 | */ | ||
810 | { | ||
811 | .ident = -1 | ||
812 | } | ||
813 | }; | ||
814 | |||
815 | void mac_identify(void) | ||
816 | { | ||
817 | struct mac_model *m; | ||
818 | |||
819 | /* Penguin data useful? */ | ||
820 | int model = mac_bi_data.id; | ||
821 | if (!model) { | ||
822 | /* no bootinfo model id -> NetBSD booter was used! */ | ||
823 | /* XXX FIXME: breaks for model > 31 */ | ||
824 | model=(mac_bi_data.cpuid>>2)&63; | ||
825 | printk (KERN_WARNING "No bootinfo model ID, using cpuid instead (hey, use Penguin!)\n"); | ||
826 | } | ||
827 | |||
828 | macintosh_config = mac_data_table; | ||
829 | for (m = macintosh_config ; m->ident != -1 ; m++) { | ||
830 | if (m->ident == model) { | ||
831 | macintosh_config = m; | ||
832 | break; | ||
833 | } | ||
834 | } | ||
835 | |||
836 | /* We need to pre-init the IOPs, if any. Otherwise */ | ||
837 | /* the serial console won't work if the user had */ | ||
838 | /* the serial ports set to "Faster" mode in MacOS. */ | ||
839 | |||
840 | iop_preinit(); | ||
841 | mac_debug_init(); | ||
842 | |||
843 | printk (KERN_INFO "Detected Macintosh model: %d \n", model); | ||
844 | |||
845 | /* | ||
846 | * Report booter data: | ||
847 | */ | ||
848 | printk (KERN_DEBUG " Penguin bootinfo data:\n"); | ||
849 | printk (KERN_DEBUG " Video: addr 0x%lx row 0x%lx depth %lx dimensions %ld x %ld\n", | ||
850 | mac_bi_data.videoaddr, mac_bi_data.videorow, | ||
851 | mac_bi_data.videodepth, mac_bi_data.dimensions & 0xFFFF, | ||
852 | mac_bi_data.dimensions >> 16); | ||
853 | printk (KERN_DEBUG " Videological 0x%lx phys. 0x%lx, SCC at 0x%lx \n", | ||
854 | mac_bi_data.videological, mac_orig_videoaddr, | ||
855 | mac_bi_data.sccbase); | ||
856 | printk (KERN_DEBUG " Boottime: 0x%lx GMTBias: 0x%lx \n", | ||
857 | mac_bi_data.boottime, mac_bi_data.gmtbias); | ||
858 | printk (KERN_DEBUG " Machine ID: %ld CPUid: 0x%lx memory size: 0x%lx \n", | ||
859 | mac_bi_data.id, mac_bi_data.cpuid, mac_bi_data.memsize); | ||
860 | #if 0 | ||
861 | printk ("Ramdisk: addr 0x%lx size 0x%lx\n", | ||
862 | m68k_ramdisk.addr, m68k_ramdisk.size); | ||
863 | #endif | ||
864 | |||
865 | /* | ||
866 | * TODO: set the various fields in macintosh_config->hw_present here! | ||
867 | */ | ||
868 | switch (macintosh_config->scsi_type) { | ||
869 | case MAC_SCSI_OLD: | ||
870 | MACHW_SET(MAC_SCSI_80); | ||
871 | break; | ||
872 | case MAC_SCSI_QUADRA: | ||
873 | case MAC_SCSI_QUADRA2: | ||
874 | case MAC_SCSI_QUADRA3: | ||
875 | MACHW_SET(MAC_SCSI_96); | ||
876 | if ((macintosh_config->ident == MAC_MODEL_Q900) || | ||
877 | (macintosh_config->ident == MAC_MODEL_Q950)) | ||
878 | MACHW_SET(MAC_SCSI_96_2); | ||
879 | break; | ||
880 | default: | ||
881 | printk(KERN_WARNING "config.c: wtf: unknown scsi, using 53c80\n"); | ||
882 | MACHW_SET(MAC_SCSI_80); | ||
883 | break; | ||
884 | |||
885 | } | ||
886 | iop_init(); | ||
887 | via_init(); | ||
888 | oss_init(); | ||
889 | psc_init(); | ||
890 | baboon_init(); | ||
891 | } | ||
892 | |||
893 | void mac_report_hardware(void) | ||
894 | { | ||
895 | printk(KERN_INFO "Apple Macintosh %s\n", macintosh_config->name); | ||
896 | } | ||
897 | |||
898 | static void mac_get_model(char *str) | ||
899 | { | ||
900 | strcpy(str,"Macintosh "); | ||
901 | strcat(str, macintosh_config->name); | ||
902 | } | ||
diff --git a/arch/m68k/mac/debug.c b/arch/m68k/mac/debug.c new file mode 100644 index 000000000000..cc62ed61cda2 --- /dev/null +++ b/arch/m68k/mac/debug.c | |||
@@ -0,0 +1,398 @@ | |||
1 | /* | ||
2 | * linux/arch/m68k/mac/debug.c | ||
3 | * | ||
4 | * Shamelessly stolen (SCC code and general framework) from: | ||
5 | * | ||
6 | * linux/arch/m68k/atari/debug.c | ||
7 | * | ||
8 | * Atari debugging and serial console stuff | ||
9 | * | ||
10 | * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek | ||
11 | * | ||
12 | * This file is subject to the terms and conditions of the GNU General Public | ||
13 | * License. See the file COPYING in the main directory of this archive | ||
14 | * for more details. | ||
15 | */ | ||
16 | |||
17 | #include <linux/config.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/tty.h> | ||
21 | #include <linux/console.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/delay.h> | ||
24 | |||
25 | #define BOOTINFO_COMPAT_1_0 | ||
26 | #include <asm/setup.h> | ||
27 | #include <asm/bootinfo.h> | ||
28 | #include <asm/machw.h> | ||
29 | #include <asm/macints.h> | ||
30 | |||
31 | extern char m68k_debug_device[]; | ||
32 | |||
33 | extern struct compat_bootinfo compat_boot_info; | ||
34 | |||
35 | extern unsigned long mac_videobase; | ||
36 | extern unsigned long mac_videodepth; | ||
37 | extern unsigned long mac_rowbytes; | ||
38 | |||
39 | extern void mac_serial_print(const char *); | ||
40 | |||
41 | #define DEBUG_HEADS | ||
42 | #undef DEBUG_SCREEN | ||
43 | #define DEBUG_SERIAL | ||
44 | |||
45 | /* | ||
46 | * These two auxiliary debug functions should go away ASAP. Only usage: | ||
47 | * before the console output is up (after head.S come some other crucial | ||
48 | * setup routines :-) it permits writing 'data' to the screen as bit patterns | ||
49 | * (good luck reading those). Helped to figure that the bootinfo contained | ||
50 | * garbage data on the amount and size of memory chunks ... | ||
51 | * | ||
52 | * The 'pos' argument now simply means 'linefeed after print' ... | ||
53 | */ | ||
54 | |||
55 | #ifdef DEBUG_SCREEN | ||
56 | static int peng=0, line=0; | ||
57 | #endif | ||
58 | |||
59 | void mac_debugging_short(int pos, short num) | ||
60 | { | ||
61 | #ifdef DEBUG_SCREEN | ||
62 | unsigned char *pengoffset; | ||
63 | unsigned char *pptr; | ||
64 | int i; | ||
65 | #endif | ||
66 | |||
67 | #ifdef DEBUG_SERIAL | ||
68 | printk("debug: %d !\n", num); | ||
69 | #endif | ||
70 | |||
71 | #ifdef DEBUG_SCREEN | ||
72 | if (!MACH_IS_MAC) { | ||
73 | /* printk("debug: %d !\n", num); */ | ||
74 | return; | ||
75 | } | ||
76 | |||
77 | /* calculate current offset */ | ||
78 | pengoffset=(unsigned char *)(mac_videobase+(150+line*2)*mac_rowbytes) | ||
79 | +80*peng; | ||
80 | |||
81 | pptr=pengoffset; | ||
82 | |||
83 | for(i=0;i<8*sizeof(short);i++) /* # of bits */ | ||
84 | { | ||
85 | /* value mask for bit i, reverse order */ | ||
86 | *pptr++ = (num & ( 1 << (8*sizeof(short)-i-1) ) ? 0xFF : 0x00); | ||
87 | } | ||
88 | |||
89 | peng++; | ||
90 | |||
91 | if (pos) { | ||
92 | line++; | ||
93 | peng = 0; | ||
94 | } | ||
95 | #endif | ||
96 | } | ||
97 | |||
98 | void mac_debugging_long(int pos, long addr) | ||
99 | { | ||
100 | #ifdef DEBUG_SCREEN | ||
101 | unsigned char *pengoffset; | ||
102 | unsigned char *pptr; | ||
103 | int i; | ||
104 | #endif | ||
105 | |||
106 | #ifdef DEBUG_SERIAL | ||
107 | printk("debug: #%ld !\n", addr); | ||
108 | #endif | ||
109 | |||
110 | #ifdef DEBUG_SCREEN | ||
111 | if (!MACH_IS_MAC) { | ||
112 | /* printk("debug: #%ld !\n", addr); */ | ||
113 | return; | ||
114 | } | ||
115 | |||
116 | pengoffset=(unsigned char *)(mac_videobase+(150+line*2)*mac_rowbytes) | ||
117 | +80*peng; | ||
118 | |||
119 | pptr=pengoffset; | ||
120 | |||
121 | for(i=0;i<8*sizeof(long);i++) /* # of bits */ | ||
122 | { | ||
123 | *pptr++ = (addr & ( 1 << (8*sizeof(long)-i-1) ) ? 0xFF : 0x00); | ||
124 | } | ||
125 | |||
126 | peng++; | ||
127 | |||
128 | if (pos) { | ||
129 | line++; | ||
130 | peng = 0; | ||
131 | } | ||
132 | #endif | ||
133 | } | ||
134 | |||
135 | #ifdef DEBUG_SERIAL | ||
136 | /* | ||
137 | * TODO: serial debug code | ||
138 | */ | ||
139 | |||
140 | struct mac_SCC | ||
141 | { | ||
142 | u_char cha_b_ctrl; | ||
143 | u_char char_dummy1; | ||
144 | u_char cha_a_ctrl; | ||
145 | u_char char_dummy2; | ||
146 | u_char cha_b_data; | ||
147 | u_char char_dummy3; | ||
148 | u_char cha_a_data; | ||
149 | }; | ||
150 | |||
151 | # define scc (*((volatile struct mac_SCC*)mac_bi_data.sccbase)) | ||
152 | |||
153 | /* Flag that serial port is already initialized and used */ | ||
154 | int mac_SCC_init_done; | ||
155 | /* Can be set somewhere, if a SCC master reset has already be done and should | ||
156 | * not be repeated; used by kgdb */ | ||
157 | int mac_SCC_reset_done; | ||
158 | |||
159 | static int scc_port = -1; | ||
160 | |||
161 | static struct console mac_console_driver = { | ||
162 | .name = "debug", | ||
163 | .flags = CON_PRINTBUFFER, | ||
164 | .index = -1, | ||
165 | }; | ||
166 | |||
167 | /* | ||
168 | * Crude hack to get console output to the screen before the framebuffer | ||
169 | * is initialized (happens a lot later in 2.1!). | ||
170 | * We just use the console routines declared in head.S, this will interfere | ||
171 | * with regular framebuffer console output and should be used exclusively | ||
172 | * to debug kernel problems manifesting before framebuffer init (aka WSOD) | ||
173 | * | ||
174 | * To keep this hack from interfering with the regular console driver, either | ||
175 | * deregister this driver before/on framebuffer console init, or silence this | ||
176 | * function after the fbcon driver is running (will lose console messages!?). | ||
177 | * To debug real early bugs, need to write a 'mac_register_console_hack()' | ||
178 | * that is called from start_kernel() before setup_arch() and just registers | ||
179 | * this driver if Mac. | ||
180 | */ | ||
181 | |||
182 | void mac_debug_console_write (struct console *co, const char *str, | ||
183 | unsigned int count) | ||
184 | { | ||
185 | mac_serial_print(str); | ||
186 | } | ||
187 | |||
188 | |||
189 | |||
190 | /* Mac: loops_per_jiffy min. 19000 ^= .5 us; MFPDELAY was 0.6 us*/ | ||
191 | |||
192 | #define uSEC 1 | ||
193 | |||
194 | static inline void mac_sccb_out (char c) | ||
195 | { | ||
196 | int i; | ||
197 | do { | ||
198 | for( i = uSEC; i > 0; --i ) | ||
199 | barrier(); | ||
200 | } while (!(scc.cha_b_ctrl & 0x04)); /* wait for tx buf empty */ | ||
201 | for( i = uSEC; i > 0; --i ) | ||
202 | barrier(); | ||
203 | scc.cha_b_data = c; | ||
204 | } | ||
205 | |||
206 | static inline void mac_scca_out (char c) | ||
207 | { | ||
208 | int i; | ||
209 | do { | ||
210 | for( i = uSEC; i > 0; --i ) | ||
211 | barrier(); | ||
212 | } while (!(scc.cha_a_ctrl & 0x04)); /* wait for tx buf empty */ | ||
213 | for( i = uSEC; i > 0; --i ) | ||
214 | barrier(); | ||
215 | scc.cha_a_data = c; | ||
216 | } | ||
217 | |||
218 | void mac_sccb_console_write (struct console *co, const char *str, | ||
219 | unsigned int count) | ||
220 | { | ||
221 | while (count--) { | ||
222 | if (*str == '\n') | ||
223 | mac_sccb_out( '\r' ); | ||
224 | mac_sccb_out( *str++ ); | ||
225 | } | ||
226 | } | ||
227 | |||
228 | void mac_scca_console_write (struct console *co, const char *str, | ||
229 | unsigned int count) | ||
230 | { | ||
231 | while (count--) { | ||
232 | if (*str == '\n') | ||
233 | mac_scca_out( '\r' ); | ||
234 | mac_scca_out( *str++ ); | ||
235 | } | ||
236 | } | ||
237 | |||
238 | |||
239 | /* The following two functions do a quick'n'dirty initialization of the MFP or | ||
240 | * SCC serial ports. They're used by the debugging interface, kgdb, and the | ||
241 | * serial console code. */ | ||
242 | #define SCCB_WRITE(reg,val) \ | ||
243 | do { \ | ||
244 | int i; \ | ||
245 | scc.cha_b_ctrl = (reg); \ | ||
246 | for( i = uSEC; i > 0; --i ) \ | ||
247 | barrier(); \ | ||
248 | scc.cha_b_ctrl = (val); \ | ||
249 | for( i = uSEC; i > 0; --i ) \ | ||
250 | barrier(); \ | ||
251 | } while(0) | ||
252 | |||
253 | #define SCCA_WRITE(reg,val) \ | ||
254 | do { \ | ||
255 | int i; \ | ||
256 | scc.cha_a_ctrl = (reg); \ | ||
257 | for( i = uSEC; i > 0; --i ) \ | ||
258 | barrier(); \ | ||
259 | scc.cha_a_ctrl = (val); \ | ||
260 | for( i = uSEC; i > 0; --i ) \ | ||
261 | barrier(); \ | ||
262 | } while(0) | ||
263 | |||
264 | /* loops_per_jiffy isn't initialized yet, so we can't use udelay(). This does a | ||
265 | * delay of ~ 60us. */ | ||
266 | /* Mac: loops_per_jiffy min. 19000 ^= .5 us; MFPDELAY was 0.6 us*/ | ||
267 | #define LONG_DELAY() \ | ||
268 | do { \ | ||
269 | int i; \ | ||
270 | for( i = 60*uSEC; i > 0; --i ) \ | ||
271 | barrier(); \ | ||
272 | } while(0) | ||
273 | |||
274 | #ifndef CONFIG_SERIAL_CONSOLE | ||
275 | static void __init mac_init_scc_port( int cflag, int port ) | ||
276 | #else | ||
277 | void mac_init_scc_port( int cflag, int port ) | ||
278 | #endif | ||
279 | { | ||
280 | extern int mac_SCC_reset_done; | ||
281 | |||
282 | /* | ||
283 | * baud rates: 1200, 1800, 2400, 4800, 9600, 19.2k, 38.4k, 57.6k, 115.2k | ||
284 | */ | ||
285 | |||
286 | static int clksrc_table[9] = | ||
287 | /* reg 11: 0x50 = BRG, 0x00 = RTxC, 0x28 = TRxC */ | ||
288 | { 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x00, 0x00 }; | ||
289 | static int clkmode_table[9] = | ||
290 | /* reg 4: 0x40 = x16, 0x80 = x32, 0xc0 = x64 */ | ||
291 | { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x80 }; | ||
292 | static int div_table[9] = | ||
293 | /* reg12 (BRG low) */ | ||
294 | { 94, 62, 46, 22, 10, 4, 1, 0, 0 }; | ||
295 | |||
296 | int baud = cflag & CBAUD; | ||
297 | int clksrc, clkmode, div, reg3, reg5; | ||
298 | |||
299 | if (cflag & CBAUDEX) | ||
300 | baud += B38400; | ||
301 | if (baud < B1200 || baud > B38400+2) | ||
302 | baud = B9600; /* use default 9600bps for non-implemented rates */ | ||
303 | baud -= B1200; /* tables starts at 1200bps */ | ||
304 | |||
305 | clksrc = clksrc_table[baud]; | ||
306 | clkmode = clkmode_table[baud]; | ||
307 | div = div_table[baud]; | ||
308 | |||
309 | reg3 = (((cflag & CSIZE) == CS8) ? 0xc0 : 0x40); | ||
310 | reg5 = (((cflag & CSIZE) == CS8) ? 0x60 : 0x20) | 0x82 /* assert DTR/RTS */; | ||
311 | |||
312 | if (port == 1) { | ||
313 | (void)scc.cha_b_ctrl; /* reset reg pointer */ | ||
314 | SCCB_WRITE( 9, 0xc0 ); /* reset */ | ||
315 | LONG_DELAY(); /* extra delay after WR9 access */ | ||
316 | SCCB_WRITE( 4, (cflag & PARENB) ? ((cflag & PARODD) ? 0x01 : 0x03) : 0 | | ||
317 | 0x04 /* 1 stopbit */ | | ||
318 | clkmode ); | ||
319 | SCCB_WRITE( 3, reg3 ); | ||
320 | SCCB_WRITE( 5, reg5 ); | ||
321 | SCCB_WRITE( 9, 0 ); /* no interrupts */ | ||
322 | LONG_DELAY(); /* extra delay after WR9 access */ | ||
323 | SCCB_WRITE( 10, 0 ); /* NRZ mode */ | ||
324 | SCCB_WRITE( 11, clksrc ); /* main clock source */ | ||
325 | SCCB_WRITE( 12, div ); /* BRG value */ | ||
326 | SCCB_WRITE( 13, 0 ); /* BRG high byte */ | ||
327 | SCCB_WRITE( 14, 1 ); | ||
328 | SCCB_WRITE( 3, reg3 | 1 ); | ||
329 | SCCB_WRITE( 5, reg5 | 8 ); | ||
330 | } else if (port == 0) { | ||
331 | (void)scc.cha_a_ctrl; /* reset reg pointer */ | ||
332 | SCCA_WRITE( 9, 0xc0 ); /* reset */ | ||
333 | LONG_DELAY(); /* extra delay after WR9 access */ | ||
334 | SCCA_WRITE( 4, (cflag & PARENB) ? ((cflag & PARODD) ? 0x01 : 0x03) : 0 | | ||
335 | 0x04 /* 1 stopbit */ | | ||
336 | clkmode ); | ||
337 | SCCA_WRITE( 3, reg3 ); | ||
338 | SCCA_WRITE( 5, reg5 ); | ||
339 | SCCA_WRITE( 9, 0 ); /* no interrupts */ | ||
340 | LONG_DELAY(); /* extra delay after WR9 access */ | ||
341 | SCCA_WRITE( 10, 0 ); /* NRZ mode */ | ||
342 | SCCA_WRITE( 11, clksrc ); /* main clock source */ | ||
343 | SCCA_WRITE( 12, div ); /* BRG value */ | ||
344 | SCCA_WRITE( 13, 0 ); /* BRG high byte */ | ||
345 | SCCA_WRITE( 14, 1 ); | ||
346 | SCCA_WRITE( 3, reg3 | 1 ); | ||
347 | SCCA_WRITE( 5, reg5 | 8 ); | ||
348 | } | ||
349 | |||
350 | mac_SCC_reset_done = 1; | ||
351 | mac_SCC_init_done = 1; | ||
352 | } | ||
353 | #endif /* DEBUG_SERIAL */ | ||
354 | |||
355 | void mac_init_scca_port( int cflag ) | ||
356 | { | ||
357 | mac_init_scc_port(cflag, 0); | ||
358 | } | ||
359 | |||
360 | void mac_init_sccb_port( int cflag ) | ||
361 | { | ||
362 | mac_init_scc_port(cflag, 1); | ||
363 | } | ||
364 | |||
365 | void __init mac_debug_init(void) | ||
366 | { | ||
367 | #ifdef DEBUG_SERIAL | ||
368 | if ( !strcmp( m68k_debug_device, "ser" ) | ||
369 | || !strcmp( m68k_debug_device, "ser1" )) { | ||
370 | /* Mac modem port */ | ||
371 | mac_init_scc_port( B9600|CS8, 0 ); | ||
372 | mac_console_driver.write = mac_scca_console_write; | ||
373 | scc_port = 0; | ||
374 | } | ||
375 | else if (!strcmp( m68k_debug_device, "ser2" )) { | ||
376 | /* Mac printer port */ | ||
377 | mac_init_scc_port( B9600|CS8, 1 ); | ||
378 | mac_console_driver.write = mac_sccb_console_write; | ||
379 | scc_port = 1; | ||
380 | } | ||
381 | #endif | ||
382 | #ifdef DEBUG_HEADS | ||
383 | if ( !strcmp( m68k_debug_device, "scn" ) | ||
384 | || !strcmp( m68k_debug_device, "con" )) { | ||
385 | /* display, using head.S console routines */ | ||
386 | mac_console_driver.write = mac_debug_console_write; | ||
387 | } | ||
388 | #endif | ||
389 | if (mac_console_driver.write) | ||
390 | register_console(&mac_console_driver); | ||
391 | } | ||
392 | |||
393 | /* | ||
394 | * Local variables: | ||
395 | * c-indent-level: 4 | ||
396 | * tab-width: 8 | ||
397 | * End: | ||
398 | */ | ||
diff --git a/arch/m68k/mac/iop.c b/arch/m68k/mac/iop.c new file mode 100644 index 000000000000..d889ba80ccdc --- /dev/null +++ b/arch/m68k/mac/iop.c | |||
@@ -0,0 +1,714 @@ | |||
1 | /* | ||
2 | * I/O Processor (IOP) management | ||
3 | * Written and (C) 1999 by Joshua M. Thompson (funaho@jurai.org) | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions | ||
7 | * are met: | ||
8 | * 1. Redistributions of source code must retain the above copyright | ||
9 | * notice and this list of conditions. | ||
10 | * 2. Redistributions in binary form must reproduce the above copyright | ||
11 | * notice and this list of conditions in the documentation and/or other | ||
12 | * materials provided with the distribution. | ||
13 | */ | ||
14 | |||
15 | /* | ||
16 | * The IOP chips are used in the IIfx and some Quadras (900, 950) to manage | ||
17 | * serial and ADB. They are actually a 6502 processor and some glue logic. | ||
18 | * | ||
19 | * 990429 (jmt) - Initial implementation, just enough to knock the SCC IOP | ||
20 | * into compatible mode so nobody has to fiddle with the | ||
21 | * Serial Switch control panel anymore. | ||
22 | * 990603 (jmt) - Added code to grab the correct ISM IOP interrupt for OSS | ||
23 | * and non-OSS machines (at least I hope it's correct on a | ||
24 | * non-OSS machine -- someone with a Q900 or Q950 needs to | ||
25 | * check this.) | ||
26 | * 990605 (jmt) - Rearranged things a bit wrt IOP detection; iop_present is | ||
27 | * gone, IOP base addresses are now in an array and the | ||
28 | * globally-visible functions take an IOP number instead of an | ||
29 | * an actual base address. | ||
30 | * 990610 (jmt) - Finished the message passing framework and it seems to work. | ||
31 | * Sending _definitely_ works; my adb-bus.c mods can send | ||
32 | * messages and receive the MSG_COMPLETED status back from the | ||
33 | * IOP. The trick now is figuring out the message formats. | ||
34 | * 990611 (jmt) - More cleanups. Fixed problem where unclaimed messages on a | ||
35 | * receive channel were never properly acknowledged. Bracketed | ||
36 | * the remaining debug printk's with #ifdef's and disabled | ||
37 | * debugging. I can now type on the console. | ||
38 | * 990612 (jmt) - Copyright notice added. Reworked the way replies are handled. | ||
39 | * It turns out that replies are placed back in the send buffer | ||
40 | * for that channel; messages on the receive channels are always | ||
41 | * unsolicited messages from the IOP (and our replies to them | ||
42 | * should go back in the receive channel.) Also added tracking | ||
43 | * of device names to the listener functions ala the interrupt | ||
44 | * handlers. | ||
45 | * 990729 (jmt) - Added passing of pt_regs structure to IOP handlers. This is | ||
46 | * used by the new unified ADB driver. | ||
47 | * | ||
48 | * TODO: | ||
49 | * | ||
50 | * o Something should be periodically checking iop_alive() to make sure the | ||
51 | * IOP hasn't died. | ||
52 | * o Some of the IOP manager routines need better error checking and | ||
53 | * return codes. Nothing major, just prettying up. | ||
54 | */ | ||
55 | |||
56 | /* | ||
57 | * ----------------------- | ||
58 | * IOP Message Passing 101 | ||
59 | * ----------------------- | ||
60 | * | ||
61 | * The host talks to the IOPs using a rather simple message-passing scheme via | ||
62 | * a shared memory area in the IOP RAM. Each IOP has seven "channels"; each | ||
63 | * channel is conneced to a specific software driver on the IOP. For example | ||
64 | * on the SCC IOP there is one channel for each serial port. Each channel has | ||
65 | * an incoming and and outgoing message queue with a depth of one. | ||
66 | * | ||
67 | * A message is 32 bytes plus a state byte for the channel (MSG_IDLE, MSG_NEW, | ||
68 | * MSG_RCVD, MSG_COMPLETE). To send a message you copy the message into the | ||
69 | * buffer, set the state to MSG_NEW and signal the IOP by setting the IRQ flag | ||
70 | * in the IOP control to 1. The IOP will move the state to MSG_RCVD when it | ||
71 | * receives the message and then to MSG_COMPLETE when the message processing | ||
72 | * has completed. It is the host's responsibility at that point to read the | ||
73 | * reply back out of the send channel buffer and reset the channel state back | ||
74 | * to MSG_IDLE. | ||
75 | * | ||
76 | * To receive message from the IOP the same procedure is used except the roles | ||
77 | * are reversed. That is, the IOP puts message in the channel with a state of | ||
78 | * MSG_NEW, and the host receives the message and move its state to MSG_RCVD | ||
79 | * and then to MSG_COMPLETE when processing is completed and the reply (if any) | ||
80 | * has been placed back in the receive channel. The IOP will then reset the | ||
81 | * channel state to MSG_IDLE. | ||
82 | * | ||
83 | * Two sets of host interrupts are provided, INT0 and INT1. Both appear on one | ||
84 | * interrupt level; they are distinguished by a pair of bits in the IOP status | ||
85 | * register. The IOP will raise INT0 when one or more messages in the send | ||
86 | * channels have gone to the MSG_COMPLETE state and it will raise INT1 when one | ||
87 | * or more messages on the receive channels have gone to the MSG_NEW state. | ||
88 | * | ||
89 | * Since each channel handles only one message we have to implement a small | ||
90 | * interrupt-driven queue on our end. Messages to be sent are placed on the | ||
91 | * queue for sending and contain a pointer to an optional callback function. | ||
92 | * The handler for a message is called when the message state goes to | ||
93 | * MSG_COMPLETE. | ||
94 | * | ||
95 | * For receiving message we maintain a list of handler functions to call when | ||
96 | * a message is received on that IOP/channel combination. The handlers are | ||
97 | * called much like an interrupt handler and are passed a copy of the message | ||
98 | * from the IOP. The message state will be in MSG_RCVD while the handler runs; | ||
99 | * it is the handler's responsibility to call iop_complete_message() when | ||
100 | * finished; this function moves the message state to MSG_COMPLETE and signals | ||
101 | * the IOP. This two-step process is provided to allow the handler to defer | ||
102 | * message processing to a bottom-half handler if the processing will take | ||
103 | * a signifigant amount of time (handlers are called at interrupt time so they | ||
104 | * should execute quickly.) | ||
105 | */ | ||
106 | |||
107 | #include <linux/config.h> | ||
108 | #include <linux/types.h> | ||
109 | #include <linux/kernel.h> | ||
110 | #include <linux/mm.h> | ||
111 | #include <linux/delay.h> | ||
112 | #include <linux/init.h> | ||
113 | #include <linux/proc_fs.h> | ||
114 | #include <linux/interrupt.h> | ||
115 | |||
116 | #include <asm/bootinfo.h> | ||
117 | #include <asm/macintosh.h> | ||
118 | #include <asm/macints.h> | ||
119 | #include <asm/mac_iop.h> | ||
120 | #include <asm/mac_oss.h> | ||
121 | |||
122 | /*#define DEBUG_IOP*/ | ||
123 | |||
124 | /* Set to nonezero if the IOPs are present. Set by iop_init() */ | ||
125 | |||
126 | int iop_scc_present,iop_ism_present; | ||
127 | |||
128 | #ifdef CONFIG_PROC_FS | ||
129 | static int iop_get_proc_info(char *, char **, off_t, int); | ||
130 | #endif /* CONFIG_PROC_FS */ | ||
131 | |||
132 | /* structure for tracking channel listeners */ | ||
133 | |||
134 | struct listener { | ||
135 | const char *devname; | ||
136 | void (*handler)(struct iop_msg *, struct pt_regs *); | ||
137 | }; | ||
138 | |||
139 | /* | ||
140 | * IOP structures for the two IOPs | ||
141 | * | ||
142 | * The SCC IOP controls both serial ports (A and B) as its two functions. | ||
143 | * The ISM IOP controls the SWIM (floppy drive) and ADB. | ||
144 | */ | ||
145 | |||
146 | static volatile struct mac_iop *iop_base[NUM_IOPS]; | ||
147 | |||
148 | /* | ||
149 | * IOP message queues | ||
150 | */ | ||
151 | |||
152 | static struct iop_msg iop_msg_pool[NUM_IOP_MSGS]; | ||
153 | static struct iop_msg *iop_send_queue[NUM_IOPS][NUM_IOP_CHAN]; | ||
154 | static struct listener iop_listeners[NUM_IOPS][NUM_IOP_CHAN]; | ||
155 | |||
156 | irqreturn_t iop_ism_irq(int, void *, struct pt_regs *); | ||
157 | |||
158 | extern void oss_irq_enable(int); | ||
159 | |||
160 | /* | ||
161 | * Private access functions | ||
162 | */ | ||
163 | |||
164 | static __inline__ void iop_loadaddr(volatile struct mac_iop *iop, __u16 addr) | ||
165 | { | ||
166 | iop->ram_addr_lo = addr; | ||
167 | iop->ram_addr_hi = addr >> 8; | ||
168 | } | ||
169 | |||
170 | static __inline__ __u8 iop_readb(volatile struct mac_iop *iop, __u16 addr) | ||
171 | { | ||
172 | iop->ram_addr_lo = addr; | ||
173 | iop->ram_addr_hi = addr >> 8; | ||
174 | return iop->ram_data; | ||
175 | } | ||
176 | |||
177 | static __inline__ void iop_writeb(volatile struct mac_iop *iop, __u16 addr, __u8 data) | ||
178 | { | ||
179 | iop->ram_addr_lo = addr; | ||
180 | iop->ram_addr_hi = addr >> 8; | ||
181 | iop->ram_data = data; | ||
182 | } | ||
183 | |||
184 | static __inline__ void iop_stop(volatile struct mac_iop *iop) | ||
185 | { | ||
186 | iop->status_ctrl &= ~IOP_RUN; | ||
187 | } | ||
188 | |||
189 | static __inline__ void iop_start(volatile struct mac_iop *iop) | ||
190 | { | ||
191 | iop->status_ctrl = IOP_RUN | IOP_AUTOINC; | ||
192 | } | ||
193 | |||
194 | static __inline__ void iop_bypass(volatile struct mac_iop *iop) | ||
195 | { | ||
196 | iop->status_ctrl |= IOP_BYPASS; | ||
197 | } | ||
198 | |||
199 | static __inline__ void iop_interrupt(volatile struct mac_iop *iop) | ||
200 | { | ||
201 | iop->status_ctrl |= IOP_IRQ; | ||
202 | } | ||
203 | |||
204 | static int iop_alive(volatile struct mac_iop *iop) | ||
205 | { | ||
206 | int retval; | ||
207 | |||
208 | retval = (iop_readb(iop, IOP_ADDR_ALIVE) == 0xFF); | ||
209 | iop_writeb(iop, IOP_ADDR_ALIVE, 0); | ||
210 | return retval; | ||
211 | } | ||
212 | |||
213 | static struct iop_msg *iop_alloc_msg(void) | ||
214 | { | ||
215 | int i; | ||
216 | unsigned long flags; | ||
217 | |||
218 | local_irq_save(flags); | ||
219 | |||
220 | for (i = 0 ; i < NUM_IOP_MSGS ; i++) { | ||
221 | if (iop_msg_pool[i].status == IOP_MSGSTATUS_UNUSED) { | ||
222 | iop_msg_pool[i].status = IOP_MSGSTATUS_WAITING; | ||
223 | local_irq_restore(flags); | ||
224 | return &iop_msg_pool[i]; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | local_irq_restore(flags); | ||
229 | return NULL; | ||
230 | } | ||
231 | |||
232 | static void iop_free_msg(struct iop_msg *msg) | ||
233 | { | ||
234 | msg->status = IOP_MSGSTATUS_UNUSED; | ||
235 | } | ||
236 | |||
237 | /* | ||
238 | * This is called by the startup code before anything else. Its purpose | ||
239 | * is to find and initialize the IOPs early in the boot sequence, so that | ||
240 | * the serial IOP can be placed into bypass mode _before_ we try to | ||
241 | * initialize the serial console. | ||
242 | */ | ||
243 | |||
244 | void __init iop_preinit(void) | ||
245 | { | ||
246 | if (macintosh_config->scc_type == MAC_SCC_IOP) { | ||
247 | if (macintosh_config->ident == MAC_MODEL_IIFX) { | ||
248 | iop_base[IOP_NUM_SCC] = (struct mac_iop *) SCC_IOP_BASE_IIFX; | ||
249 | } else { | ||
250 | iop_base[IOP_NUM_SCC] = (struct mac_iop *) SCC_IOP_BASE_QUADRA; | ||
251 | } | ||
252 | iop_base[IOP_NUM_SCC]->status_ctrl = 0x87; | ||
253 | iop_scc_present = 1; | ||
254 | } else { | ||
255 | iop_base[IOP_NUM_SCC] = NULL; | ||
256 | iop_scc_present = 0; | ||
257 | } | ||
258 | if (macintosh_config->adb_type == MAC_ADB_IOP) { | ||
259 | if (macintosh_config->ident == MAC_MODEL_IIFX) { | ||
260 | iop_base[IOP_NUM_ISM] = (struct mac_iop *) ISM_IOP_BASE_IIFX; | ||
261 | } else { | ||
262 | iop_base[IOP_NUM_ISM] = (struct mac_iop *) ISM_IOP_BASE_QUADRA; | ||
263 | } | ||
264 | iop_base[IOP_NUM_ISM]->status_ctrl = 0; | ||
265 | iop_ism_present = 1; | ||
266 | } else { | ||
267 | iop_base[IOP_NUM_ISM] = NULL; | ||
268 | iop_ism_present = 0; | ||
269 | } | ||
270 | } | ||
271 | |||
272 | /* | ||
273 | * Initialize the IOPs, if present. | ||
274 | */ | ||
275 | |||
276 | void __init iop_init(void) | ||
277 | { | ||
278 | int i; | ||
279 | |||
280 | if (iop_scc_present) { | ||
281 | printk("IOP: detected SCC IOP at %p\n", iop_base[IOP_NUM_SCC]); | ||
282 | } | ||
283 | if (iop_ism_present) { | ||
284 | printk("IOP: detected ISM IOP at %p\n", iop_base[IOP_NUM_ISM]); | ||
285 | iop_start(iop_base[IOP_NUM_ISM]); | ||
286 | iop_alive(iop_base[IOP_NUM_ISM]); /* clears the alive flag */ | ||
287 | } | ||
288 | |||
289 | /* Make the whole pool available and empty the queues */ | ||
290 | |||
291 | for (i = 0 ; i < NUM_IOP_MSGS ; i++) { | ||
292 | iop_msg_pool[i].status = IOP_MSGSTATUS_UNUSED; | ||
293 | } | ||
294 | |||
295 | for (i = 0 ; i < NUM_IOP_CHAN ; i++) { | ||
296 | iop_send_queue[IOP_NUM_SCC][i] = 0; | ||
297 | iop_send_queue[IOP_NUM_ISM][i] = 0; | ||
298 | iop_listeners[IOP_NUM_SCC][i].devname = NULL; | ||
299 | iop_listeners[IOP_NUM_SCC][i].handler = NULL; | ||
300 | iop_listeners[IOP_NUM_ISM][i].devname = NULL; | ||
301 | iop_listeners[IOP_NUM_ISM][i].handler = NULL; | ||
302 | } | ||
303 | |||
304 | #if 0 /* Crashing in 2.4 now, not yet sure why. --jmt */ | ||
305 | #ifdef CONFIG_PROC_FS | ||
306 | create_proc_info_entry("mac_iop", 0, &proc_root, iop_get_proc_info); | ||
307 | #endif | ||
308 | #endif | ||
309 | } | ||
310 | |||
311 | /* | ||
312 | * Register the interrupt handler for the IOPs. | ||
313 | * TODO: might be wrong for non-OSS machines. Anyone? | ||
314 | */ | ||
315 | |||
316 | void __init iop_register_interrupts(void) | ||
317 | { | ||
318 | if (iop_ism_present) { | ||
319 | if (oss_present) { | ||
320 | cpu_request_irq(OSS_IRQLEV_IOPISM, iop_ism_irq, | ||
321 | IRQ_FLG_LOCK, "ISM IOP", | ||
322 | (void *) IOP_NUM_ISM); | ||
323 | oss_irq_enable(IRQ_MAC_ADB); | ||
324 | } else { | ||
325 | request_irq(IRQ_VIA2_0, iop_ism_irq, | ||
326 | IRQ_FLG_LOCK|IRQ_FLG_FAST, "ISM IOP", | ||
327 | (void *) IOP_NUM_ISM); | ||
328 | } | ||
329 | if (!iop_alive(iop_base[IOP_NUM_ISM])) { | ||
330 | printk("IOP: oh my god, they killed the ISM IOP!\n"); | ||
331 | } else { | ||
332 | printk("IOP: the ISM IOP seems to be alive.\n"); | ||
333 | } | ||
334 | } | ||
335 | } | ||
336 | |||
337 | /* | ||
338 | * Register or unregister a listener for a specific IOP and channel | ||
339 | * | ||
340 | * If the handler pointer is NULL the current listener (if any) is | ||
341 | * unregistered. Otherwise the new listener is registered provided | ||
342 | * there is no existing listener registered. | ||
343 | */ | ||
344 | |||
345 | int iop_listen(uint iop_num, uint chan, | ||
346 | void (*handler)(struct iop_msg *, struct pt_regs *), | ||
347 | const char *devname) | ||
348 | { | ||
349 | if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return -EINVAL; | ||
350 | if (chan >= NUM_IOP_CHAN) return -EINVAL; | ||
351 | if (iop_listeners[iop_num][chan].handler && handler) return -EINVAL; | ||
352 | iop_listeners[iop_num][chan].devname = devname; | ||
353 | iop_listeners[iop_num][chan].handler = handler; | ||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | /* | ||
358 | * Complete reception of a message, which just means copying the reply | ||
359 | * into the buffer, setting the channel state to MSG_COMPLETE and | ||
360 | * notifying the IOP. | ||
361 | */ | ||
362 | |||
363 | void iop_complete_message(struct iop_msg *msg) | ||
364 | { | ||
365 | int iop_num = msg->iop_num; | ||
366 | int chan = msg->channel; | ||
367 | int i,offset; | ||
368 | |||
369 | #ifdef DEBUG_IOP | ||
370 | printk("iop_complete(%p): iop %d chan %d\n", msg, msg->iop_num, msg->channel); | ||
371 | #endif | ||
372 | |||
373 | offset = IOP_ADDR_RECV_MSG + (msg->channel * IOP_MSG_LEN); | ||
374 | |||
375 | for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) { | ||
376 | iop_writeb(iop_base[iop_num], offset, msg->reply[i]); | ||
377 | } | ||
378 | |||
379 | iop_writeb(iop_base[iop_num], | ||
380 | IOP_ADDR_RECV_STATE + chan, IOP_MSG_COMPLETE); | ||
381 | iop_interrupt(iop_base[msg->iop_num]); | ||
382 | |||
383 | iop_free_msg(msg); | ||
384 | } | ||
385 | |||
386 | /* | ||
387 | * Actually put a message into a send channel buffer | ||
388 | */ | ||
389 | |||
390 | static void iop_do_send(struct iop_msg *msg) | ||
391 | { | ||
392 | volatile struct mac_iop *iop = iop_base[msg->iop_num]; | ||
393 | int i,offset; | ||
394 | |||
395 | offset = IOP_ADDR_SEND_MSG + (msg->channel * IOP_MSG_LEN); | ||
396 | |||
397 | for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) { | ||
398 | iop_writeb(iop, offset, msg->message[i]); | ||
399 | } | ||
400 | |||
401 | iop_writeb(iop, IOP_ADDR_SEND_STATE + msg->channel, IOP_MSG_NEW); | ||
402 | |||
403 | iop_interrupt(iop); | ||
404 | } | ||
405 | |||
406 | /* | ||
407 | * Handle sending a message on a channel that | ||
408 | * has gone into the IOP_MSG_COMPLETE state. | ||
409 | */ | ||
410 | |||
411 | static void iop_handle_send(uint iop_num, uint chan, struct pt_regs *regs) | ||
412 | { | ||
413 | volatile struct mac_iop *iop = iop_base[iop_num]; | ||
414 | struct iop_msg *msg,*msg2; | ||
415 | int i,offset; | ||
416 | |||
417 | #ifdef DEBUG_IOP | ||
418 | printk("iop_handle_send: iop %d channel %d\n", iop_num, chan); | ||
419 | #endif | ||
420 | |||
421 | iop_writeb(iop, IOP_ADDR_SEND_STATE + chan, IOP_MSG_IDLE); | ||
422 | |||
423 | if (!(msg = iop_send_queue[iop_num][chan])) return; | ||
424 | |||
425 | msg->status = IOP_MSGSTATUS_COMPLETE; | ||
426 | offset = IOP_ADDR_SEND_MSG + (chan * IOP_MSG_LEN); | ||
427 | for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) { | ||
428 | msg->reply[i] = iop_readb(iop, offset); | ||
429 | } | ||
430 | if (msg->handler) (*msg->handler)(msg, regs); | ||
431 | msg2 = msg; | ||
432 | msg = msg->next; | ||
433 | iop_free_msg(msg2); | ||
434 | |||
435 | iop_send_queue[iop_num][chan] = msg; | ||
436 | if (msg) iop_do_send(msg); | ||
437 | } | ||
438 | |||
439 | /* | ||
440 | * Handle reception of a message on a channel that has | ||
441 | * gone into the IOP_MSG_NEW state. | ||
442 | */ | ||
443 | |||
444 | static void iop_handle_recv(uint iop_num, uint chan, struct pt_regs *regs) | ||
445 | { | ||
446 | volatile struct mac_iop *iop = iop_base[iop_num]; | ||
447 | int i,offset; | ||
448 | struct iop_msg *msg; | ||
449 | |||
450 | #ifdef DEBUG_IOP | ||
451 | printk("iop_handle_recv: iop %d channel %d\n", iop_num, chan); | ||
452 | #endif | ||
453 | |||
454 | msg = iop_alloc_msg(); | ||
455 | msg->iop_num = iop_num; | ||
456 | msg->channel = chan; | ||
457 | msg->status = IOP_MSGSTATUS_UNSOL; | ||
458 | msg->handler = iop_listeners[iop_num][chan].handler; | ||
459 | |||
460 | offset = IOP_ADDR_RECV_MSG + (chan * IOP_MSG_LEN); | ||
461 | |||
462 | for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) { | ||
463 | msg->message[i] = iop_readb(iop, offset); | ||
464 | } | ||
465 | |||
466 | iop_writeb(iop, IOP_ADDR_RECV_STATE + chan, IOP_MSG_RCVD); | ||
467 | |||
468 | /* If there is a listener, call it now. Otherwise complete */ | ||
469 | /* the message ourselves to avoid possible stalls. */ | ||
470 | |||
471 | if (msg->handler) { | ||
472 | (*msg->handler)(msg, regs); | ||
473 | } else { | ||
474 | #ifdef DEBUG_IOP | ||
475 | printk("iop_handle_recv: unclaimed message on iop %d channel %d\n", iop_num, chan); | ||
476 | printk("iop_handle_recv:"); | ||
477 | for (i = 0 ; i < IOP_MSG_LEN ; i++) { | ||
478 | printk(" %02X", (uint) msg->message[i]); | ||
479 | } | ||
480 | printk("\n"); | ||
481 | #endif | ||
482 | iop_complete_message(msg); | ||
483 | } | ||
484 | } | ||
485 | |||
486 | /* | ||
487 | * Send a message | ||
488 | * | ||
489 | * The message is placed at the end of the send queue. Afterwards if the | ||
490 | * channel is idle we force an immediate send of the next message in the | ||
491 | * queue. | ||
492 | */ | ||
493 | |||
494 | int iop_send_message(uint iop_num, uint chan, void *privdata, | ||
495 | uint msg_len, __u8 *msg_data, | ||
496 | void (*handler)(struct iop_msg *, struct pt_regs *)) | ||
497 | { | ||
498 | struct iop_msg *msg, *q; | ||
499 | |||
500 | if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return -EINVAL; | ||
501 | if (chan >= NUM_IOP_CHAN) return -EINVAL; | ||
502 | if (msg_len > IOP_MSG_LEN) return -EINVAL; | ||
503 | |||
504 | msg = iop_alloc_msg(); | ||
505 | if (!msg) return -ENOMEM; | ||
506 | |||
507 | msg->next = NULL; | ||
508 | msg->status = IOP_MSGSTATUS_WAITING; | ||
509 | msg->iop_num = iop_num; | ||
510 | msg->channel = chan; | ||
511 | msg->caller_priv = privdata; | ||
512 | memcpy(msg->message, msg_data, msg_len); | ||
513 | msg->handler = handler; | ||
514 | |||
515 | if (!(q = iop_send_queue[iop_num][chan])) { | ||
516 | iop_send_queue[iop_num][chan] = msg; | ||
517 | } else { | ||
518 | while (q->next) q = q->next; | ||
519 | q->next = msg; | ||
520 | } | ||
521 | |||
522 | if (iop_readb(iop_base[iop_num], | ||
523 | IOP_ADDR_SEND_STATE + chan) == IOP_MSG_IDLE) { | ||
524 | iop_do_send(msg); | ||
525 | } | ||
526 | |||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | /* | ||
531 | * Upload code to the shared RAM of an IOP. | ||
532 | */ | ||
533 | |||
534 | void iop_upload_code(uint iop_num, __u8 *code_start, | ||
535 | uint code_len, __u16 shared_ram_start) | ||
536 | { | ||
537 | if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return; | ||
538 | |||
539 | iop_loadaddr(iop_base[iop_num], shared_ram_start); | ||
540 | |||
541 | while (code_len--) { | ||
542 | iop_base[iop_num]->ram_data = *code_start++; | ||
543 | } | ||
544 | } | ||
545 | |||
546 | /* | ||
547 | * Download code from the shared RAM of an IOP. | ||
548 | */ | ||
549 | |||
550 | void iop_download_code(uint iop_num, __u8 *code_start, | ||
551 | uint code_len, __u16 shared_ram_start) | ||
552 | { | ||
553 | if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return; | ||
554 | |||
555 | iop_loadaddr(iop_base[iop_num], shared_ram_start); | ||
556 | |||
557 | while (code_len--) { | ||
558 | *code_start++ = iop_base[iop_num]->ram_data; | ||
559 | } | ||
560 | } | ||
561 | |||
562 | /* | ||
563 | * Compare the code in the shared RAM of an IOP with a copy in system memory | ||
564 | * and return 0 on match or the first nonmatching system memory address on | ||
565 | * failure. | ||
566 | */ | ||
567 | |||
568 | __u8 *iop_compare_code(uint iop_num, __u8 *code_start, | ||
569 | uint code_len, __u16 shared_ram_start) | ||
570 | { | ||
571 | if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return code_start; | ||
572 | |||
573 | iop_loadaddr(iop_base[iop_num], shared_ram_start); | ||
574 | |||
575 | while (code_len--) { | ||
576 | if (*code_start != iop_base[iop_num]->ram_data) { | ||
577 | return code_start; | ||
578 | } | ||
579 | code_start++; | ||
580 | } | ||
581 | return (__u8 *) 0; | ||
582 | } | ||
583 | |||
584 | /* | ||
585 | * Handle an ISM IOP interrupt | ||
586 | */ | ||
587 | |||
588 | irqreturn_t iop_ism_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
589 | { | ||
590 | uint iop_num = (uint) dev_id; | ||
591 | volatile struct mac_iop *iop = iop_base[iop_num]; | ||
592 | int i,state; | ||
593 | |||
594 | #ifdef DEBUG_IOP | ||
595 | printk("iop_ism_irq: status = %02X\n", (uint) iop->status_ctrl); | ||
596 | #endif | ||
597 | |||
598 | /* INT0 indicates a state change on an outgoing message channel */ | ||
599 | |||
600 | if (iop->status_ctrl & IOP_INT0) { | ||
601 | iop->status_ctrl = IOP_INT0 | IOP_RUN | IOP_AUTOINC; | ||
602 | #ifdef DEBUG_IOP | ||
603 | printk("iop_ism_irq: new status = %02X, send states", | ||
604 | (uint) iop->status_ctrl); | ||
605 | #endif | ||
606 | for (i = 0 ; i < NUM_IOP_CHAN ; i++) { | ||
607 | state = iop_readb(iop, IOP_ADDR_SEND_STATE + i); | ||
608 | #ifdef DEBUG_IOP | ||
609 | printk(" %02X", state); | ||
610 | #endif | ||
611 | if (state == IOP_MSG_COMPLETE) { | ||
612 | iop_handle_send(iop_num, i, regs); | ||
613 | } | ||
614 | } | ||
615 | #ifdef DEBUG_IOP | ||
616 | printk("\n"); | ||
617 | #endif | ||
618 | } | ||
619 | |||
620 | if (iop->status_ctrl & IOP_INT1) { /* INT1 for incoming msgs */ | ||
621 | iop->status_ctrl = IOP_INT1 | IOP_RUN | IOP_AUTOINC; | ||
622 | #ifdef DEBUG_IOP | ||
623 | printk("iop_ism_irq: new status = %02X, recv states", | ||
624 | (uint) iop->status_ctrl); | ||
625 | #endif | ||
626 | for (i = 0 ; i < NUM_IOP_CHAN ; i++) { | ||
627 | state = iop_readb(iop, IOP_ADDR_RECV_STATE + i); | ||
628 | #ifdef DEBUG_IOP | ||
629 | printk(" %02X", state); | ||
630 | #endif | ||
631 | if (state == IOP_MSG_NEW) { | ||
632 | iop_handle_recv(iop_num, i, regs); | ||
633 | } | ||
634 | } | ||
635 | #ifdef DEBUG_IOP | ||
636 | printk("\n"); | ||
637 | #endif | ||
638 | } | ||
639 | return IRQ_HANDLED; | ||
640 | } | ||
641 | |||
642 | #ifdef CONFIG_PROC_FS | ||
643 | |||
644 | char *iop_chan_state(int state) | ||
645 | { | ||
646 | switch(state) { | ||
647 | case IOP_MSG_IDLE : return "idle "; | ||
648 | case IOP_MSG_NEW : return "new "; | ||
649 | case IOP_MSG_RCVD : return "received "; | ||
650 | case IOP_MSG_COMPLETE : return "completed "; | ||
651 | default : return "unknown "; | ||
652 | } | ||
653 | } | ||
654 | |||
655 | int iop_dump_one_iop(char *buf, int iop_num, char *iop_name) | ||
656 | { | ||
657 | int i,len = 0; | ||
658 | volatile struct mac_iop *iop = iop_base[iop_num]; | ||
659 | |||
660 | len += sprintf(buf+len, "%s IOP channel states:\n\n", iop_name); | ||
661 | len += sprintf(buf+len, "## send_state recv_state device\n"); | ||
662 | len += sprintf(buf+len, "------------------------------------------------\n"); | ||
663 | for (i = 0 ; i < NUM_IOP_CHAN ; i++) { | ||
664 | len += sprintf(buf+len, "%2d %10s %10s %s\n", i, | ||
665 | iop_chan_state(iop_readb(iop, IOP_ADDR_SEND_STATE+i)), | ||
666 | iop_chan_state(iop_readb(iop, IOP_ADDR_RECV_STATE+i)), | ||
667 | iop_listeners[iop_num][i].handler? | ||
668 | iop_listeners[iop_num][i].devname : ""); | ||
669 | |||
670 | } | ||
671 | len += sprintf(buf+len, "\n"); | ||
672 | return len; | ||
673 | } | ||
674 | |||
675 | static int iop_get_proc_info(char *buf, char **start, off_t pos, int count) | ||
676 | { | ||
677 | int len, cnt; | ||
678 | |||
679 | cnt = 0; | ||
680 | len = sprintf(buf, "IOPs detected:\n\n"); | ||
681 | |||
682 | if (iop_scc_present) { | ||
683 | len += sprintf(buf+len, "SCC IOP (%p): status %02X\n", | ||
684 | iop_base[IOP_NUM_SCC], | ||
685 | (uint) iop_base[IOP_NUM_SCC]->status_ctrl); | ||
686 | } | ||
687 | if (iop_ism_present) { | ||
688 | len += sprintf(buf+len, "ISM IOP (%p): status %02X\n\n", | ||
689 | iop_base[IOP_NUM_ISM], | ||
690 | (uint) iop_base[IOP_NUM_ISM]->status_ctrl); | ||
691 | } | ||
692 | |||
693 | if (iop_scc_present) { | ||
694 | len += iop_dump_one_iop(buf+len, IOP_NUM_SCC, "SCC"); | ||
695 | |||
696 | } | ||
697 | |||
698 | if (iop_ism_present) { | ||
699 | len += iop_dump_one_iop(buf+len, IOP_NUM_ISM, "ISM"); | ||
700 | |||
701 | } | ||
702 | |||
703 | if (len >= pos) { | ||
704 | if (!*start) { | ||
705 | *start = buf + pos; | ||
706 | cnt = len - pos; | ||
707 | } else { | ||
708 | cnt += len; | ||
709 | } | ||
710 | } | ||
711 | return (count > cnt) ? cnt : count; | ||
712 | } | ||
713 | |||
714 | #endif /* CONFIG_PROC_FS */ | ||
diff --git a/arch/m68k/mac/mac_ksyms.c b/arch/m68k/mac/mac_ksyms.c new file mode 100644 index 000000000000..6e37ceb0f3b5 --- /dev/null +++ b/arch/m68k/mac/mac_ksyms.c | |||
@@ -0,0 +1,8 @@ | |||
1 | #include <linux/module.h> | ||
2 | #include <asm/ptrace.h> | ||
3 | #include <asm/traps.h> | ||
4 | |||
5 | /* Says whether we're using A/UX interrupts or not */ | ||
6 | extern int via_alt_mapping; | ||
7 | |||
8 | EXPORT_SYMBOL(via_alt_mapping); | ||
diff --git a/arch/m68k/mac/mac_penguin.S b/arch/m68k/mac/mac_penguin.S new file mode 100644 index 000000000000..b3ce30b6071d --- /dev/null +++ b/arch/m68k/mac/mac_penguin.S | |||
@@ -0,0 +1,75 @@ | |||
1 | .byte \ | ||
2 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
3 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
4 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
5 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
6 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
7 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
8 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
9 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
10 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
11 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
12 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
13 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0xF0,0x00,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
14 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
15 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
16 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0xFF,0xFF,0x0F,0xF0,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
17 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0x00,0xFF,0xFF,0x0F,0xFF,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
18 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0x0F,0xFF,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
19 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xFF,0x00,0x0F,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
20 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
21 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
22 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
23 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
24 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0xFF,0x00,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
25 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x0F,0xF0,0x00,0x00,0xFF,0xF0,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
26 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x0F,0xF0,0xFF,0xFF,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
27 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xF0,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
28 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x0F,0xFF,0x00,0xFF,0xF0,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
29 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
30 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
31 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
32 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
33 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
34 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
35 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,\ | ||
36 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,\ | ||
37 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,\ | ||
38 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,\ | ||
39 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,\ | ||
40 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,\ | ||
41 | 0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,\ | ||
42 | 0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,\ | ||
43 | 0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,\ | ||
44 | 0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,\ | ||
45 | 0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,\ | ||
46 | 0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ | ||
47 | 0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ | ||
48 | 0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ | ||
49 | 0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ | ||
50 | 0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ | ||
51 | 0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ | ||
52 | 0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ | ||
53 | 0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ | ||
54 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ | ||
55 | 0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,\ | ||
56 | 0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,\ | ||
57 | 0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0xF0,0x00,0x00,\ | ||
58 | 0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0xF0,0x00,0x00,\ | ||
59 | 0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,\ | ||
60 | 0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,\ | ||
61 | 0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,\ | ||
62 | 0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,\ | ||
63 | 0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,\ | ||
64 | 0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,\ | ||
65 | 0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
66 | 0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ | ||
67 | 0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0xF0,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,\ | ||
68 | 0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,\ | ||
69 | 0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,\ | ||
70 | 0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xFF,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,\ | ||
71 | 0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,\ | ||
72 | 0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,\ | ||
73 | 0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,\ | ||
74 | 0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,\ | ||
75 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00 | ||
diff --git a/arch/m68k/mac/macboing.c b/arch/m68k/mac/macboing.c new file mode 100644 index 000000000000..44c5cd2ad6a8 --- /dev/null +++ b/arch/m68k/mac/macboing.c | |||
@@ -0,0 +1,309 @@ | |||
1 | /* | ||
2 | * Mac bong noise generator. Note - we ought to put a boingy noise | ||
3 | * here 8) | ||
4 | * | ||
5 | * ---------------------------------------------------------------------- | ||
6 | * 16.11.98: | ||
7 | * rewrote some functions, added support for Enhanced ASC (Quadras) | ||
8 | * after the NetBSD asc.c console bell patch by Colin Wood/Frederick Bruck | ||
9 | * Juergen Mellinger (juergen.mellinger@t-online.de) | ||
10 | */ | ||
11 | |||
12 | #include <linux/sched.h> | ||
13 | #include <linux/timer.h> | ||
14 | |||
15 | #include <asm/macintosh.h> | ||
16 | #include <asm/mac_asc.h> | ||
17 | |||
18 | static int mac_asc_inited; | ||
19 | /* | ||
20 | * dumb triangular wave table | ||
21 | */ | ||
22 | static __u8 mac_asc_wave_tab[ 0x800 ]; | ||
23 | |||
24 | /* | ||
25 | * Alan's original sine table; needs interpolating to 0x800 | ||
26 | * (hint: interpolate or hardwire [0 -> Pi/2[, it's symmetric) | ||
27 | */ | ||
28 | static const signed char sine_data[] = { | ||
29 | 0, 39, 75, 103, 121, 127, 121, 103, 75, 39, | ||
30 | 0, -39, -75, -103, -121, -127, -121, -103, -75, -39 | ||
31 | }; | ||
32 | |||
33 | /* | ||
34 | * where the ASC hides ... | ||
35 | */ | ||
36 | static volatile __u8* mac_asc_regs = ( void* )0x50F14000; | ||
37 | |||
38 | /* | ||
39 | * sample rate; is this a good default value? | ||
40 | */ | ||
41 | static unsigned long mac_asc_samplespersec = 11050; | ||
42 | static int mac_bell_duration; | ||
43 | static unsigned long mac_bell_phase; /* 0..2*Pi -> 0..0x800 (wavetable size) */ | ||
44 | static unsigned long mac_bell_phasepersample; | ||
45 | |||
46 | /* | ||
47 | * some function protos | ||
48 | */ | ||
49 | static void mac_init_asc( void ); | ||
50 | static void mac_nosound( unsigned long ); | ||
51 | static void mac_quadra_start_bell( unsigned int, unsigned int, unsigned int ); | ||
52 | static void mac_quadra_ring_bell( unsigned long ); | ||
53 | static void mac_av_start_bell( unsigned int, unsigned int, unsigned int ); | ||
54 | static void ( *mac_special_bell )( unsigned int, unsigned int, unsigned int ); | ||
55 | |||
56 | /* | ||
57 | * our timer to start/continue/stop the bell | ||
58 | */ | ||
59 | static struct timer_list mac_sound_timer = | ||
60 | TIMER_INITIALIZER(mac_nosound, 0, 0); | ||
61 | |||
62 | /* | ||
63 | * Sort of initialize the sound chip (called from mac_mksound on the first | ||
64 | * beep). | ||
65 | */ | ||
66 | static void mac_init_asc( void ) | ||
67 | { | ||
68 | int i; | ||
69 | |||
70 | /* | ||
71 | * do some machine specific initialization | ||
72 | * BTW: | ||
73 | * the NetBSD Quadra patch identifies the Enhanced Apple Sound Chip via | ||
74 | * mac_asc_regs[ 0x800 ] & 0xF0 != 0 | ||
75 | * this makes no sense here, because we have to set the default sample | ||
76 | * rate anyway if we want correct frequencies | ||
77 | */ | ||
78 | switch ( macintosh_config->ident ) | ||
79 | { | ||
80 | case MAC_MODEL_IIFX: | ||
81 | /* | ||
82 | * The IIfx is always special ... | ||
83 | */ | ||
84 | mac_asc_regs = ( void* )0x50010000; | ||
85 | break; | ||
86 | /* | ||
87 | * not sure about how correct this list is | ||
88 | * machines with the EASC enhanced apple sound chip | ||
89 | */ | ||
90 | case MAC_MODEL_Q630: | ||
91 | case MAC_MODEL_P475: | ||
92 | mac_special_bell = mac_quadra_start_bell; | ||
93 | mac_asc_samplespersec = 22150; | ||
94 | break; | ||
95 | case MAC_MODEL_C660: | ||
96 | case MAC_MODEL_Q840: | ||
97 | /* | ||
98 | * The Quadra 660AV and 840AV use the "Singer" custom ASIC for sound I/O. | ||
99 | * It appears to be similar to the "AWACS" custom ASIC in the Power Mac | ||
100 | * [678]100. Because Singer and AWACS may have a similar hardware | ||
101 | * interface, this would imply that the code in drivers/sound/dmasound.c | ||
102 | * for AWACS could be used as a basis for Singer support. All we have to | ||
103 | * do is figure out how to do DMA on the 660AV/840AV through the PSC and | ||
104 | * figure out where the Singer hardware sits in memory. (I'd look in the | ||
105 | * vicinity of the AWACS location in a Power Mac [678]100 first, or the | ||
106 | * current location of the Apple Sound Chip--ASC--in other Macs.) The | ||
107 | * Power Mac [678]100 info can be found in MkLinux Mach kernel sources. | ||
108 | * | ||
109 | * Quoted from Apple's Tech Info Library, article number 16405: | ||
110 | * "Among desktop Macintosh computers, only the 660AV, 840AV, and Power | ||
111 | * Macintosh models have 16-bit audio input and output capability | ||
112 | * because of the AT&T DSP3210 hardware circuitry and the 16-bit Singer | ||
113 | * codec circuitry in the AVs. The Audio Waveform Amplifier and | ||
114 | * Converter (AWAC) chip in the Power Macintosh performs the same | ||
115 | * 16-bit I/O functionality. The PowerBook 500 series computers | ||
116 | * support 16-bit stereo output, but only mono input." | ||
117 | * | ||
118 | * http://til.info.apple.com/techinfo.nsf/artnum/n16405 | ||
119 | * | ||
120 | * --David Kilzer | ||
121 | */ | ||
122 | mac_special_bell = mac_av_start_bell; | ||
123 | break; | ||
124 | case MAC_MODEL_Q650: | ||
125 | case MAC_MODEL_Q700: | ||
126 | case MAC_MODEL_Q800: | ||
127 | case MAC_MODEL_Q900: | ||
128 | case MAC_MODEL_Q950: | ||
129 | /* | ||
130 | * Currently not implemented! | ||
131 | */ | ||
132 | mac_special_bell = NULL; | ||
133 | break; | ||
134 | default: | ||
135 | /* | ||
136 | * Every switch needs a default | ||
137 | */ | ||
138 | mac_special_bell = NULL; | ||
139 | break; | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * init the wave table with a simple triangular wave | ||
144 | * A sine wave would sure be nicer here ... | ||
145 | */ | ||
146 | for ( i = 0; i < 0x400; i++ ) | ||
147 | { | ||
148 | mac_asc_wave_tab[ i ] = i / 4; | ||
149 | mac_asc_wave_tab[ i + 0x400 ] = 0xFF - i / 4; | ||
150 | } | ||
151 | mac_asc_inited = 1; | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * Called to make noise; current single entry to the boing driver. | ||
156 | * Does the job for simple ASC, calls other routines else. | ||
157 | * XXX Fixme: | ||
158 | * Should be split into asc_mksound, easc_mksound, av_mksound and | ||
159 | * function pointer set in mac_init_asc which would be called at | ||
160 | * init time. | ||
161 | * _This_ is rather ugly ... | ||
162 | */ | ||
163 | void mac_mksound( unsigned int freq, unsigned int length ) | ||
164 | { | ||
165 | __u32 cfreq = ( freq << 5 ) / 468; | ||
166 | __u32 flags; | ||
167 | int i; | ||
168 | |||
169 | if ( mac_special_bell == NULL ) | ||
170 | { | ||
171 | /* Do nothing */ | ||
172 | return; | ||
173 | } | ||
174 | |||
175 | if ( !mac_asc_inited ) | ||
176 | mac_init_asc(); | ||
177 | |||
178 | if ( mac_special_bell ) | ||
179 | { | ||
180 | mac_special_bell( freq, length, 128 ); | ||
181 | return; | ||
182 | } | ||
183 | |||
184 | if ( freq < 20 || freq > 20000 || length == 0 ) | ||
185 | { | ||
186 | mac_nosound( 0 ); | ||
187 | return; | ||
188 | } | ||
189 | |||
190 | local_irq_save(flags); | ||
191 | |||
192 | del_timer( &mac_sound_timer ); | ||
193 | |||
194 | for ( i = 0; i < 0x800; i++ ) | ||
195 | mac_asc_regs[ i ] = 0; | ||
196 | for ( i = 0; i < 0x800; i++ ) | ||
197 | mac_asc_regs[ i ] = mac_asc_wave_tab[ i ]; | ||
198 | |||
199 | for ( i = 0; i < 8; i++ ) | ||
200 | *( __u32* )( ( __u32 )mac_asc_regs + ASC_CONTROL + 0x814 + 8 * i ) = cfreq; | ||
201 | |||
202 | mac_asc_regs[ 0x807 ] = 0; | ||
203 | mac_asc_regs[ ASC_VOLUME ] = 128; | ||
204 | mac_asc_regs[ 0x805 ] = 0; | ||
205 | mac_asc_regs[ 0x80F ] = 0; | ||
206 | mac_asc_regs[ ASC_MODE ] = ASC_MODE_SAMPLE; | ||
207 | mac_asc_regs[ ASC_ENABLE ] = ASC_ENABLE_SAMPLE; | ||
208 | |||
209 | mac_sound_timer.expires = jiffies + length; | ||
210 | add_timer( &mac_sound_timer ); | ||
211 | |||
212 | local_irq_restore(flags); | ||
213 | } | ||
214 | |||
215 | /* | ||
216 | * regular ASC: stop whining .. | ||
217 | */ | ||
218 | static void mac_nosound( unsigned long ignored ) | ||
219 | { | ||
220 | mac_asc_regs[ ASC_ENABLE ] = 0; | ||
221 | } | ||
222 | |||
223 | /* | ||
224 | * EASC entry; init EASC, don't load wavetable, schedule 'start whining'. | ||
225 | */ | ||
226 | static void mac_quadra_start_bell( unsigned int freq, unsigned int length, unsigned int volume ) | ||
227 | { | ||
228 | __u32 flags; | ||
229 | |||
230 | /* if the bell is already ringing, ring longer */ | ||
231 | if ( mac_bell_duration > 0 ) | ||
232 | { | ||
233 | mac_bell_duration += length; | ||
234 | return; | ||
235 | } | ||
236 | |||
237 | mac_bell_duration = length; | ||
238 | mac_bell_phase = 0; | ||
239 | mac_bell_phasepersample = ( freq * sizeof( mac_asc_wave_tab ) ) / mac_asc_samplespersec; | ||
240 | /* this is reasonably big for small frequencies */ | ||
241 | |||
242 | local_irq_save(flags); | ||
243 | |||
244 | /* set the volume */ | ||
245 | mac_asc_regs[ 0x806 ] = volume; | ||
246 | |||
247 | /* set up the ASC registers */ | ||
248 | if ( mac_asc_regs[ 0x801 ] != 1 ) | ||
249 | { | ||
250 | /* select mono mode */ | ||
251 | mac_asc_regs[ 0x807 ] = 0; | ||
252 | /* select sampled sound mode */ | ||
253 | mac_asc_regs[ 0x802 ] = 0; | ||
254 | /* ??? */ | ||
255 | mac_asc_regs[ 0x801 ] = 1; | ||
256 | mac_asc_regs[ 0x803 ] |= 0x80; | ||
257 | mac_asc_regs[ 0x803 ] &= 0x7F; | ||
258 | } | ||
259 | |||
260 | mac_sound_timer.function = mac_quadra_ring_bell; | ||
261 | mac_sound_timer.expires = jiffies + 1; | ||
262 | add_timer( &mac_sound_timer ); | ||
263 | |||
264 | local_irq_restore(flags); | ||
265 | } | ||
266 | |||
267 | /* | ||
268 | * EASC 'start/continue whining'; I'm not sure why the above function didn't | ||
269 | * already load the wave table, or at least call this one... | ||
270 | * This piece keeps reloading the wave table until done. | ||
271 | */ | ||
272 | static void mac_quadra_ring_bell( unsigned long ignored ) | ||
273 | { | ||
274 | int i, count = mac_asc_samplespersec / HZ; | ||
275 | __u32 flags; | ||
276 | |||
277 | /* | ||
278 | * we neither want a sound buffer overflow nor underflow, so we need to match | ||
279 | * the number of samples per timer interrupt as exactly as possible. | ||
280 | * using the asc interrupt will give better results in the future | ||
281 | * ...and the possibility to use a real sample (a boingy noise, maybe...) | ||
282 | */ | ||
283 | |||
284 | local_irq_save(flags); | ||
285 | |||
286 | del_timer( &mac_sound_timer ); | ||
287 | |||
288 | if ( mac_bell_duration-- > 0 ) | ||
289 | { | ||
290 | for ( i = 0; i < count; i++ ) | ||
291 | { | ||
292 | mac_bell_phase += mac_bell_phasepersample; | ||
293 | mac_asc_regs[ 0 ] = mac_asc_wave_tab[ mac_bell_phase & ( sizeof( mac_asc_wave_tab ) - 1 ) ]; | ||
294 | } | ||
295 | mac_sound_timer.expires = jiffies + 1; | ||
296 | add_timer( &mac_sound_timer ); | ||
297 | } | ||
298 | else | ||
299 | mac_asc_regs[ 0x801 ] = 0; | ||
300 | |||
301 | local_irq_restore(flags); | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * AV code - please fill in. | ||
306 | */ | ||
307 | static void mac_av_start_bell( unsigned int freq, unsigned int length, unsigned int volume ) | ||
308 | { | ||
309 | } | ||
diff --git a/arch/m68k/mac/macints.c b/arch/m68k/mac/macints.c new file mode 100644 index 000000000000..1809601ad903 --- /dev/null +++ b/arch/m68k/mac/macints.c | |||
@@ -0,0 +1,760 @@ | |||
1 | /* | ||
2 | * Macintosh interrupts | ||
3 | * | ||
4 | * General design: | ||
5 | * In contrary to the Amiga and Atari platforms, the Mac hardware seems to | ||
6 | * exclusively use the autovector interrupts (the 'generic level0-level7' | ||
7 | * interrupts with exception vectors 0x19-0x1f). The following interrupt levels | ||
8 | * are used: | ||
9 | * 1 - VIA1 | ||
10 | * - slot 0: one second interrupt (CA2) | ||
11 | * - slot 1: VBlank (CA1) | ||
12 | * - slot 2: ADB data ready (SR full) | ||
13 | * - slot 3: ADB data (CB2) | ||
14 | * - slot 4: ADB clock (CB1) | ||
15 | * - slot 5: timer 2 | ||
16 | * - slot 6: timer 1 | ||
17 | * - slot 7: status of IRQ; signals 'any enabled int.' | ||
18 | * | ||
19 | * 2 - VIA2 or RBV | ||
20 | * - slot 0: SCSI DRQ (CA2) | ||
21 | * - slot 1: NUBUS IRQ (CA1) need to read port A to find which | ||
22 | * - slot 2: /EXP IRQ (only on IIci) | ||
23 | * - slot 3: SCSI IRQ (CB2) | ||
24 | * - slot 4: ASC IRQ (CB1) | ||
25 | * - slot 5: timer 2 (not on IIci) | ||
26 | * - slot 6: timer 1 (not on IIci) | ||
27 | * - slot 7: status of IRQ; signals 'any enabled int.' | ||
28 | * | ||
29 | * 2 - OSS (IIfx only?) | ||
30 | * - slot 0: SCSI interrupt | ||
31 | * - slot 1: Sound interrupt | ||
32 | * | ||
33 | * Levels 3-6 vary by machine type. For VIA or RBV Macintoshes: | ||
34 | * | ||
35 | * 3 - unused (?) | ||
36 | * | ||
37 | * 4 - SCC (slot number determined by reading RR3 on the SSC itself) | ||
38 | * - slot 1: SCC channel A | ||
39 | * - slot 2: SCC channel B | ||
40 | * | ||
41 | * 5 - unused (?) | ||
42 | * [serial errors or special conditions seem to raise level 6 | ||
43 | * interrupts on some models (LC4xx?)] | ||
44 | * | ||
45 | * 6 - off switch (?) | ||
46 | * | ||
47 | * For OSS Macintoshes (IIfx only at this point): | ||
48 | * | ||
49 | * 3 - Nubus interrupt | ||
50 | * - slot 0: Slot $9 | ||
51 | * - slot 1: Slot $A | ||
52 | * - slot 2: Slot $B | ||
53 | * - slot 3: Slot $C | ||
54 | * - slot 4: Slot $D | ||
55 | * - slot 5: Slot $E | ||
56 | * | ||
57 | * 4 - SCC IOP | ||
58 | * - slot 1: SCC channel A | ||
59 | * - slot 2: SCC channel B | ||
60 | * | ||
61 | * 5 - ISM IOP (ADB?) | ||
62 | * | ||
63 | * 6 - unused | ||
64 | * | ||
65 | * For PSC Macintoshes (660AV, 840AV): | ||
66 | * | ||
67 | * 3 - PSC level 3 | ||
68 | * - slot 0: MACE | ||
69 | * | ||
70 | * 4 - PSC level 4 | ||
71 | * - slot 1: SCC channel A interrupt | ||
72 | * - slot 2: SCC channel B interrupt | ||
73 | * - slot 3: MACE DMA | ||
74 | * | ||
75 | * 5 - PSC level 5 | ||
76 | * | ||
77 | * 6 - PSC level 6 | ||
78 | * | ||
79 | * Finally we have good 'ole level 7, the non-maskable interrupt: | ||
80 | * | ||
81 | * 7 - NMI (programmer's switch on the back of some Macs) | ||
82 | * Also RAM parity error on models which support it (IIc, IIfx?) | ||
83 | * | ||
84 | * The current interrupt logic looks something like this: | ||
85 | * | ||
86 | * - We install dispatchers for the autovector interrupts (1-7). These | ||
87 | * dispatchers are responsible for querying the hardware (the | ||
88 | * VIA/RBV/OSS/PSC chips) to determine the actual interrupt source. Using | ||
89 | * this information a machspec interrupt number is generated by placing the | ||
90 | * index of the interrupt hardware into the low three bits and the original | ||
91 | * autovector interrupt number in the upper 5 bits. The handlers for the | ||
92 | * resulting machspec interrupt are then called. | ||
93 | * | ||
94 | * - Nubus is a special case because its interrupts are hidden behind two | ||
95 | * layers of hardware. Nubus interrupts come in as index 1 on VIA #2, | ||
96 | * which translates to IRQ number 17. In this spot we install _another_ | ||
97 | * dispatcher. This dispatcher finds the interrupting slot number (9-F) and | ||
98 | * then forms a new machspec interrupt number as above with the slot number | ||
99 | * minus 9 in the low three bits and the pseudo-level 7 in the upper five | ||
100 | * bits. The handlers for this new machspec interrupt number are then | ||
101 | * called. This puts Nubus interrupts into the range 56-62. | ||
102 | * | ||
103 | * - The Baboon interrupts (used on some PowerBooks) are an even more special | ||
104 | * case. They're hidden behind the Nubus slot $C interrupt thus adding a | ||
105 | * third layer of indirection. Why oh why did the Apple engineers do that? | ||
106 | * | ||
107 | * - We support "fast" and "slow" handlers, just like the Amiga port. The | ||
108 | * fast handlers are called first and with all interrupts disabled. They | ||
109 | * are expected to execute quickly (hence the name). The slow handlers are | ||
110 | * called last with interrupts enabled and the interrupt level restored. | ||
111 | * They must therefore be reentrant. | ||
112 | * | ||
113 | * TODO: | ||
114 | * | ||
115 | */ | ||
116 | |||
117 | #include <linux/types.h> | ||
118 | #include <linux/kernel.h> | ||
119 | #include <linux/sched.h> | ||
120 | #include <linux/kernel_stat.h> | ||
121 | #include <linux/interrupt.h> /* for intr_count */ | ||
122 | #include <linux/delay.h> | ||
123 | #include <linux/seq_file.h> | ||
124 | |||
125 | #include <asm/system.h> | ||
126 | #include <asm/irq.h> | ||
127 | #include <asm/traps.h> | ||
128 | #include <asm/bootinfo.h> | ||
129 | #include <asm/machw.h> | ||
130 | #include <asm/macintosh.h> | ||
131 | #include <asm/mac_via.h> | ||
132 | #include <asm/mac_psc.h> | ||
133 | #include <asm/hwtest.h> | ||
134 | #include <asm/errno.h> | ||
135 | #include <asm/macints.h> | ||
136 | |||
137 | #define DEBUG_SPURIOUS | ||
138 | #define SHUTUP_SONIC | ||
139 | |||
140 | /* | ||
141 | * The mac_irq_list array is an array of linked lists of irq_node_t nodes. | ||
142 | * Each node contains one handler to be called whenever the interrupt | ||
143 | * occurs, with fast handlers listed before slow handlers. | ||
144 | */ | ||
145 | |||
146 | irq_node_t *mac_irq_list[NUM_MAC_SOURCES]; | ||
147 | |||
148 | /* SCC interrupt mask */ | ||
149 | |||
150 | static int scc_mask; | ||
151 | |||
152 | /* | ||
153 | * VIA/RBV hooks | ||
154 | */ | ||
155 | |||
156 | extern void via_init(void); | ||
157 | extern void via_register_interrupts(void); | ||
158 | extern void via_irq_enable(int); | ||
159 | extern void via_irq_disable(int); | ||
160 | extern void via_irq_clear(int); | ||
161 | extern int via_irq_pending(int); | ||
162 | |||
163 | /* | ||
164 | * OSS hooks | ||
165 | */ | ||
166 | |||
167 | extern int oss_present; | ||
168 | |||
169 | extern void oss_init(void); | ||
170 | extern void oss_register_interrupts(void); | ||
171 | extern void oss_irq_enable(int); | ||
172 | extern void oss_irq_disable(int); | ||
173 | extern void oss_irq_clear(int); | ||
174 | extern int oss_irq_pending(int); | ||
175 | |||
176 | /* | ||
177 | * PSC hooks | ||
178 | */ | ||
179 | |||
180 | extern int psc_present; | ||
181 | |||
182 | extern void psc_init(void); | ||
183 | extern void psc_register_interrupts(void); | ||
184 | extern void psc_irq_enable(int); | ||
185 | extern void psc_irq_disable(int); | ||
186 | extern void psc_irq_clear(int); | ||
187 | extern int psc_irq_pending(int); | ||
188 | |||
189 | /* | ||
190 | * IOP hooks | ||
191 | */ | ||
192 | |||
193 | extern void iop_register_interrupts(void); | ||
194 | |||
195 | /* | ||
196 | * Baboon hooks | ||
197 | */ | ||
198 | |||
199 | extern int baboon_present; | ||
200 | |||
201 | extern void baboon_init(void); | ||
202 | extern void baboon_register_interrupts(void); | ||
203 | extern void baboon_irq_enable(int); | ||
204 | extern void baboon_irq_disable(int); | ||
205 | extern void baboon_irq_clear(int); | ||
206 | extern int baboon_irq_pending(int); | ||
207 | |||
208 | /* | ||
209 | * SCC interrupt routines | ||
210 | */ | ||
211 | |||
212 | static void scc_irq_enable(int); | ||
213 | static void scc_irq_disable(int); | ||
214 | |||
215 | /* | ||
216 | * console_loglevel determines NMI handler function | ||
217 | */ | ||
218 | |||
219 | extern irqreturn_t mac_bang(int, void *, struct pt_regs *); | ||
220 | irqreturn_t mac_nmi_handler(int, void *, struct pt_regs *); | ||
221 | irqreturn_t mac_debug_handler(int, void *, struct pt_regs *); | ||
222 | |||
223 | /* #define DEBUG_MACINTS */ | ||
224 | |||
225 | void mac_init_IRQ(void) | ||
226 | { | ||
227 | int i; | ||
228 | |||
229 | #ifdef DEBUG_MACINTS | ||
230 | printk("mac_init_IRQ(): Setting things up...\n"); | ||
231 | #endif | ||
232 | /* Initialize the IRQ handler lists. Initially each list is empty, */ | ||
233 | |||
234 | for (i = 0; i < NUM_MAC_SOURCES; i++) { | ||
235 | mac_irq_list[i] = NULL; | ||
236 | } | ||
237 | |||
238 | scc_mask = 0; | ||
239 | |||
240 | /* Make sure the SONIC interrupt is cleared or things get ugly */ | ||
241 | #ifdef SHUTUP_SONIC | ||
242 | printk("Killing onboard sonic... "); | ||
243 | /* This address should hopefully be mapped already */ | ||
244 | if (hwreg_present((void*)(0x50f0a000))) { | ||
245 | *(long *)(0x50f0a014) = 0x7fffL; | ||
246 | *(long *)(0x50f0a010) = 0L; | ||
247 | } | ||
248 | printk("Done.\n"); | ||
249 | #endif /* SHUTUP_SONIC */ | ||
250 | |||
251 | /* | ||
252 | * Now register the handlers for the master IRQ handlers | ||
253 | * at levels 1-7. Most of the work is done elsewhere. | ||
254 | */ | ||
255 | |||
256 | if (oss_present) { | ||
257 | oss_register_interrupts(); | ||
258 | } else { | ||
259 | via_register_interrupts(); | ||
260 | } | ||
261 | if (psc_present) psc_register_interrupts(); | ||
262 | if (baboon_present) baboon_register_interrupts(); | ||
263 | iop_register_interrupts(); | ||
264 | cpu_request_irq(7, mac_nmi_handler, IRQ_FLG_LOCK, "NMI", | ||
265 | mac_nmi_handler); | ||
266 | #ifdef DEBUG_MACINTS | ||
267 | printk("mac_init_IRQ(): Done!\n"); | ||
268 | #endif | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * Routines to work with irq_node_t's on linked lists lifted from | ||
273 | * the Amiga code written by Roman Zippel. | ||
274 | */ | ||
275 | |||
276 | static inline void mac_insert_irq(irq_node_t **list, irq_node_t *node) | ||
277 | { | ||
278 | unsigned long flags; | ||
279 | irq_node_t *cur; | ||
280 | |||
281 | if (!node->dev_id) | ||
282 | printk("%s: Warning: dev_id of %s is zero\n", | ||
283 | __FUNCTION__, node->devname); | ||
284 | |||
285 | local_irq_save(flags); | ||
286 | |||
287 | cur = *list; | ||
288 | |||
289 | if (node->flags & IRQ_FLG_FAST) { | ||
290 | node->flags &= ~IRQ_FLG_SLOW; | ||
291 | while (cur && cur->flags & IRQ_FLG_FAST) { | ||
292 | list = &cur->next; | ||
293 | cur = cur->next; | ||
294 | } | ||
295 | } else if (node->flags & IRQ_FLG_SLOW) { | ||
296 | while (cur) { | ||
297 | list = &cur->next; | ||
298 | cur = cur->next; | ||
299 | } | ||
300 | } else { | ||
301 | while (cur && !(cur->flags & IRQ_FLG_SLOW)) { | ||
302 | list = &cur->next; | ||
303 | cur = cur->next; | ||
304 | } | ||
305 | } | ||
306 | |||
307 | node->next = cur; | ||
308 | *list = node; | ||
309 | |||
310 | local_irq_restore(flags); | ||
311 | } | ||
312 | |||
313 | static inline void mac_delete_irq(irq_node_t **list, void *dev_id) | ||
314 | { | ||
315 | unsigned long flags; | ||
316 | irq_node_t *node; | ||
317 | |||
318 | local_irq_save(flags); | ||
319 | |||
320 | for (node = *list; node; list = &node->next, node = *list) { | ||
321 | if (node->dev_id == dev_id) { | ||
322 | *list = node->next; | ||
323 | /* Mark it as free. */ | ||
324 | node->handler = NULL; | ||
325 | local_irq_restore(flags); | ||
326 | return; | ||
327 | } | ||
328 | } | ||
329 | local_irq_restore(flags); | ||
330 | printk ("%s: tried to remove invalid irq\n", __FUNCTION__); | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * Call all the handlers for a given interrupt. Fast handlers are called | ||
335 | * first followed by slow handlers. | ||
336 | * | ||
337 | * This code taken from the original Amiga code written by Roman Zippel. | ||
338 | */ | ||
339 | |||
340 | void mac_do_irq_list(int irq, struct pt_regs *fp) | ||
341 | { | ||
342 | irq_node_t *node, *slow_nodes; | ||
343 | unsigned long flags; | ||
344 | |||
345 | kstat_cpu(0).irqs[irq]++; | ||
346 | |||
347 | #ifdef DEBUG_SPURIOUS | ||
348 | if (!mac_irq_list[irq] && (console_loglevel > 7)) { | ||
349 | printk("mac_do_irq_list: spurious interrupt %d!\n", irq); | ||
350 | return; | ||
351 | } | ||
352 | #endif | ||
353 | |||
354 | /* serve first fast and normal handlers */ | ||
355 | for (node = mac_irq_list[irq]; | ||
356 | node && (!(node->flags & IRQ_FLG_SLOW)); | ||
357 | node = node->next) | ||
358 | node->handler(irq, node->dev_id, fp); | ||
359 | if (!node) return; | ||
360 | local_save_flags(flags); | ||
361 | local_irq_restore((flags & ~0x0700) | (fp->sr & 0x0700)); | ||
362 | /* if slow handlers exists, serve them now */ | ||
363 | slow_nodes = node; | ||
364 | for (; node; node = node->next) { | ||
365 | node->handler(irq, node->dev_id, fp); | ||
366 | } | ||
367 | } | ||
368 | |||
369 | /* | ||
370 | * mac_enable_irq - enable an interrupt source | ||
371 | * mac_disable_irq - disable an interrupt source | ||
372 | * mac_clear_irq - clears a pending interrupt | ||
373 | * mac_pending_irq - Returns the pending status of an IRQ (nonzero = pending) | ||
374 | * | ||
375 | * These routines are just dispatchers to the VIA/OSS/PSC routines. | ||
376 | */ | ||
377 | |||
378 | void mac_enable_irq (unsigned int irq) | ||
379 | { | ||
380 | int irq_src = IRQ_SRC(irq); | ||
381 | |||
382 | switch(irq_src) { | ||
383 | case 1: via_irq_enable(irq); | ||
384 | break; | ||
385 | case 2: | ||
386 | case 7: if (oss_present) { | ||
387 | oss_irq_enable(irq); | ||
388 | } else { | ||
389 | via_irq_enable(irq); | ||
390 | } | ||
391 | break; | ||
392 | case 3: | ||
393 | case 4: | ||
394 | case 5: | ||
395 | case 6: if (psc_present) { | ||
396 | psc_irq_enable(irq); | ||
397 | } else if (oss_present) { | ||
398 | oss_irq_enable(irq); | ||
399 | } else if (irq_src == 4) { | ||
400 | scc_irq_enable(irq); | ||
401 | } | ||
402 | break; | ||
403 | case 8: if (baboon_present) { | ||
404 | baboon_irq_enable(irq); | ||
405 | } | ||
406 | break; | ||
407 | } | ||
408 | } | ||
409 | |||
410 | void mac_disable_irq (unsigned int irq) | ||
411 | { | ||
412 | int irq_src = IRQ_SRC(irq); | ||
413 | |||
414 | switch(irq_src) { | ||
415 | case 1: via_irq_disable(irq); | ||
416 | break; | ||
417 | case 2: | ||
418 | case 7: if (oss_present) { | ||
419 | oss_irq_disable(irq); | ||
420 | } else { | ||
421 | via_irq_disable(irq); | ||
422 | } | ||
423 | break; | ||
424 | case 3: | ||
425 | case 4: | ||
426 | case 5: | ||
427 | case 6: if (psc_present) { | ||
428 | psc_irq_disable(irq); | ||
429 | } else if (oss_present) { | ||
430 | oss_irq_disable(irq); | ||
431 | } else if (irq_src == 4) { | ||
432 | scc_irq_disable(irq); | ||
433 | } | ||
434 | break; | ||
435 | case 8: if (baboon_present) { | ||
436 | baboon_irq_disable(irq); | ||
437 | } | ||
438 | break; | ||
439 | } | ||
440 | } | ||
441 | |||
442 | void mac_clear_irq( unsigned int irq ) | ||
443 | { | ||
444 | switch(IRQ_SRC(irq)) { | ||
445 | case 1: via_irq_clear(irq); | ||
446 | break; | ||
447 | case 2: | ||
448 | case 7: if (oss_present) { | ||
449 | oss_irq_clear(irq); | ||
450 | } else { | ||
451 | via_irq_clear(irq); | ||
452 | } | ||
453 | break; | ||
454 | case 3: | ||
455 | case 4: | ||
456 | case 5: | ||
457 | case 6: if (psc_present) { | ||
458 | psc_irq_clear(irq); | ||
459 | } else if (oss_present) { | ||
460 | oss_irq_clear(irq); | ||
461 | } | ||
462 | break; | ||
463 | case 8: if (baboon_present) { | ||
464 | baboon_irq_clear(irq); | ||
465 | } | ||
466 | break; | ||
467 | } | ||
468 | } | ||
469 | |||
470 | int mac_irq_pending( unsigned int irq ) | ||
471 | { | ||
472 | switch(IRQ_SRC(irq)) { | ||
473 | case 1: return via_irq_pending(irq); | ||
474 | case 2: | ||
475 | case 7: if (oss_present) { | ||
476 | return oss_irq_pending(irq); | ||
477 | } else { | ||
478 | return via_irq_pending(irq); | ||
479 | } | ||
480 | case 3: | ||
481 | case 4: | ||
482 | case 5: | ||
483 | case 6: if (psc_present) { | ||
484 | return psc_irq_pending(irq); | ||
485 | } else if (oss_present) { | ||
486 | return oss_irq_pending(irq); | ||
487 | } | ||
488 | } | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | /* | ||
493 | * Add an interrupt service routine to an interrupt source. | ||
494 | * Returns 0 on success. | ||
495 | * | ||
496 | * FIXME: You can register interrupts on nonexistent source (ie PSC4 on a | ||
497 | * non-PSC machine). We should return -EINVAL in those cases. | ||
498 | */ | ||
499 | |||
500 | int mac_request_irq(unsigned int irq, | ||
501 | irqreturn_t (*handler)(int, void *, struct pt_regs *), | ||
502 | unsigned long flags, const char *devname, void *dev_id) | ||
503 | { | ||
504 | irq_node_t *node; | ||
505 | |||
506 | #ifdef DEBUG_MACINTS | ||
507 | printk ("%s: irq %d requested for %s\n", __FUNCTION__, irq, devname); | ||
508 | #endif | ||
509 | |||
510 | if (irq < VIA1_SOURCE_BASE) { | ||
511 | return cpu_request_irq(irq, handler, flags, devname, dev_id); | ||
512 | } | ||
513 | |||
514 | if (irq >= NUM_MAC_SOURCES) { | ||
515 | printk ("%s: unknown irq %d requested by %s\n", | ||
516 | __FUNCTION__, irq, devname); | ||
517 | } | ||
518 | |||
519 | /* Get a node and stick it onto the right list */ | ||
520 | |||
521 | if (!(node = new_irq_node())) return -ENOMEM; | ||
522 | |||
523 | node->handler = handler; | ||
524 | node->flags = flags; | ||
525 | node->dev_id = dev_id; | ||
526 | node->devname = devname; | ||
527 | node->next = NULL; | ||
528 | mac_insert_irq(&mac_irq_list[irq], node); | ||
529 | |||
530 | /* Now enable the IRQ source */ | ||
531 | |||
532 | mac_enable_irq(irq); | ||
533 | |||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | /* | ||
538 | * Removes an interrupt service routine from an interrupt source. | ||
539 | */ | ||
540 | |||
541 | void mac_free_irq(unsigned int irq, void *dev_id) | ||
542 | { | ||
543 | #ifdef DEBUG_MACINTS | ||
544 | printk ("%s: irq %d freed by %p\n", __FUNCTION__, irq, dev_id); | ||
545 | #endif | ||
546 | |||
547 | if (irq < VIA1_SOURCE_BASE) { | ||
548 | cpu_free_irq(irq, dev_id); | ||
549 | return; | ||
550 | } | ||
551 | |||
552 | if (irq >= NUM_MAC_SOURCES) { | ||
553 | printk ("%s: unknown irq %d freed\n", | ||
554 | __FUNCTION__, irq); | ||
555 | return; | ||
556 | } | ||
557 | |||
558 | mac_delete_irq(&mac_irq_list[irq], dev_id); | ||
559 | |||
560 | /* If the list for this interrupt is */ | ||
561 | /* empty then disable the source. */ | ||
562 | |||
563 | if (!mac_irq_list[irq]) { | ||
564 | mac_disable_irq(irq); | ||
565 | } | ||
566 | } | ||
567 | |||
568 | /* | ||
569 | * Generate a pretty listing for /proc/interrupts | ||
570 | * | ||
571 | * By the time we're called the autovector interrupt list has already been | ||
572 | * generated, so we just need to do the machspec interrupts. | ||
573 | * | ||
574 | * 990506 (jmt) - rewritten to handle chained machspec interrupt handlers. | ||
575 | * Also removed display of num_spurious it is already | ||
576 | * displayed for us as autovector irq 0. | ||
577 | */ | ||
578 | |||
579 | int show_mac_interrupts(struct seq_file *p, void *v) | ||
580 | { | ||
581 | int i; | ||
582 | irq_node_t *node; | ||
583 | char *base; | ||
584 | |||
585 | /* Don't do Nubus interrupts in this loop; we do them separately */ | ||
586 | /* below so that we can print slot numbers instead of IRQ numbers */ | ||
587 | |||
588 | for (i = VIA1_SOURCE_BASE ; i < NUM_MAC_SOURCES ; ++i) { | ||
589 | |||
590 | /* Nonexistant interrupt or nothing registered; skip it. */ | ||
591 | |||
592 | if ((node = mac_irq_list[i]) == NULL) continue; | ||
593 | if (node->flags & IRQ_FLG_STD) continue; | ||
594 | |||
595 | base = ""; | ||
596 | switch(IRQ_SRC(i)) { | ||
597 | case 1: base = "via1"; | ||
598 | break; | ||
599 | case 2: if (oss_present) { | ||
600 | base = "oss"; | ||
601 | } else { | ||
602 | base = "via2"; | ||
603 | } | ||
604 | break; | ||
605 | case 3: | ||
606 | case 4: | ||
607 | case 5: | ||
608 | case 6: if (psc_present) { | ||
609 | base = "psc"; | ||
610 | } else if (oss_present) { | ||
611 | base = "oss"; | ||
612 | } else { | ||
613 | if (IRQ_SRC(i) == 4) base = "scc"; | ||
614 | } | ||
615 | break; | ||
616 | case 7: base = "nbus"; | ||
617 | break; | ||
618 | case 8: base = "bbn"; | ||
619 | break; | ||
620 | } | ||
621 | seq_printf(p, "%4s %2d: %10u ", base, i, kstat_cpu(0).irqs[i]); | ||
622 | |||
623 | do { | ||
624 | if (node->flags & IRQ_FLG_FAST) { | ||
625 | seq_puts(p, "F "); | ||
626 | } else if (node->flags & IRQ_FLG_SLOW) { | ||
627 | seq_puts(p, "S "); | ||
628 | } else { | ||
629 | seq_puts(p, " "); | ||
630 | } | ||
631 | seq_printf(p, "%s\n", node->devname); | ||
632 | if ((node = node->next)) { | ||
633 | seq_puts(p, " "); | ||
634 | } | ||
635 | } while(node); | ||
636 | |||
637 | } | ||
638 | return 0; | ||
639 | } | ||
640 | |||
641 | void mac_default_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
642 | { | ||
643 | #ifdef DEBUG_SPURIOUS | ||
644 | printk("Unexpected IRQ %d on device %p\n", irq, dev_id); | ||
645 | #endif | ||
646 | } | ||
647 | |||
648 | static int num_debug[8]; | ||
649 | |||
650 | irqreturn_t mac_debug_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
651 | { | ||
652 | if (num_debug[irq] < 10) { | ||
653 | printk("DEBUG: Unexpected IRQ %d\n", irq); | ||
654 | num_debug[irq]++; | ||
655 | } | ||
656 | return IRQ_HANDLED; | ||
657 | } | ||
658 | |||
659 | static int in_nmi; | ||
660 | static volatile int nmi_hold; | ||
661 | |||
662 | irqreturn_t mac_nmi_handler(int irq, void *dev_id, struct pt_regs *fp) | ||
663 | { | ||
664 | int i; | ||
665 | /* | ||
666 | * generate debug output on NMI switch if 'debug' kernel option given | ||
667 | * (only works with Penguin!) | ||
668 | */ | ||
669 | |||
670 | in_nmi++; | ||
671 | for (i=0; i<100; i++) | ||
672 | udelay(1000); | ||
673 | |||
674 | if (in_nmi == 1) { | ||
675 | nmi_hold = 1; | ||
676 | printk("... pausing, press NMI to resume ..."); | ||
677 | } else { | ||
678 | printk(" ok!\n"); | ||
679 | nmi_hold = 0; | ||
680 | } | ||
681 | |||
682 | barrier(); | ||
683 | |||
684 | while (nmi_hold == 1) | ||
685 | udelay(1000); | ||
686 | |||
687 | if ( console_loglevel >= 8 ) { | ||
688 | #if 0 | ||
689 | show_state(); | ||
690 | printk("PC: %08lx\nSR: %04x SP: %p\n", fp->pc, fp->sr, fp); | ||
691 | printk("d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", | ||
692 | fp->d0, fp->d1, fp->d2, fp->d3); | ||
693 | printk("d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n", | ||
694 | fp->d4, fp->d5, fp->a0, fp->a1); | ||
695 | |||
696 | if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page) | ||
697 | printk("Corrupted stack page\n"); | ||
698 | printk("Process %s (pid: %d, stackpage=%08lx)\n", | ||
699 | current->comm, current->pid, current->kernel_stack_page); | ||
700 | if (intr_count == 1) | ||
701 | dump_stack((struct frame *)fp); | ||
702 | #else | ||
703 | /* printk("NMI "); */ | ||
704 | #endif | ||
705 | } | ||
706 | in_nmi--; | ||
707 | return IRQ_HANDLED; | ||
708 | } | ||
709 | |||
710 | /* | ||
711 | * Simple routines for masking and unmasking | ||
712 | * SCC interrupts in cases where this can't be | ||
713 | * done in hardware (only the PSC can do that.) | ||
714 | */ | ||
715 | |||
716 | static void scc_irq_enable(int irq) { | ||
717 | int irq_idx = IRQ_IDX(irq); | ||
718 | |||
719 | scc_mask |= (1 << irq_idx); | ||
720 | } | ||
721 | |||
722 | static void scc_irq_disable(int irq) { | ||
723 | int irq_idx = IRQ_IDX(irq); | ||
724 | |||
725 | scc_mask &= ~(1 << irq_idx); | ||
726 | } | ||
727 | |||
728 | /* | ||
729 | * SCC master interrupt handler. We have to do a bit of magic here | ||
730 | * to figure out what channel gave us the interrupt; putting this | ||
731 | * here is cleaner than hacking it into drivers/char/macserial.c. | ||
732 | */ | ||
733 | |||
734 | void mac_scc_dispatch(int irq, void *dev_id, struct pt_regs *regs) | ||
735 | { | ||
736 | volatile unsigned char *scc = (unsigned char *) mac_bi_data.sccbase + 2; | ||
737 | unsigned char reg; | ||
738 | unsigned long flags; | ||
739 | |||
740 | /* Read RR3 from the chip. Always do this on channel A */ | ||
741 | /* This must be an atomic operation so disable irqs. */ | ||
742 | |||
743 | local_irq_save(flags); | ||
744 | *scc = 3; | ||
745 | reg = *scc; | ||
746 | local_irq_restore(flags); | ||
747 | |||
748 | /* Now dispatch. Bits 0-2 are for channel B and */ | ||
749 | /* bits 3-5 are for channel A. We can safely */ | ||
750 | /* ignore the remaining bits here. */ | ||
751 | /* */ | ||
752 | /* Note that we're ignoring scc_mask for now. */ | ||
753 | /* If we actually mask the ints then we tend to */ | ||
754 | /* get hammered by very persistent SCC irqs, */ | ||
755 | /* and since they're autovector interrupts they */ | ||
756 | /* pretty much kill the system. */ | ||
757 | |||
758 | if (reg & 0x38) mac_do_irq_list(IRQ_SCCA, regs); | ||
759 | if (reg & 0x07) mac_do_irq_list(IRQ_SCCB, regs); | ||
760 | } | ||
diff --git a/arch/m68k/mac/misc.c b/arch/m68k/mac/misc.c new file mode 100644 index 000000000000..5b80d7cd954a --- /dev/null +++ b/arch/m68k/mac/misc.c | |||
@@ -0,0 +1,651 @@ | |||
1 | /* | ||
2 | * Miscellaneous Mac68K-specific stuff | ||
3 | */ | ||
4 | |||
5 | #include <linux/config.h> | ||
6 | #include <linux/types.h> | ||
7 | #include <linux/errno.h> | ||
8 | #include <linux/miscdevice.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/delay.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/time.h> | ||
14 | #include <linux/rtc.h> | ||
15 | #include <linux/mm.h> | ||
16 | |||
17 | #include <linux/adb.h> | ||
18 | #include <linux/cuda.h> | ||
19 | #include <linux/pmu.h> | ||
20 | |||
21 | #include <asm/uaccess.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/rtc.h> | ||
24 | #include <asm/system.h> | ||
25 | #include <asm/segment.h> | ||
26 | #include <asm/setup.h> | ||
27 | #include <asm/macintosh.h> | ||
28 | #include <asm/mac_via.h> | ||
29 | #include <asm/mac_oss.h> | ||
30 | |||
31 | #define BOOTINFO_COMPAT_1_0 | ||
32 | #include <asm/bootinfo.h> | ||
33 | #include <asm/machdep.h> | ||
34 | |||
35 | /* Offset between Unix time (1970-based) and Mac time (1904-based) */ | ||
36 | |||
37 | #define RTC_OFFSET 2082844800 | ||
38 | |||
39 | extern struct mac_booter_data mac_bi_data; | ||
40 | static void (*rom_reset)(void); | ||
41 | |||
42 | #ifdef CONFIG_ADB | ||
43 | /* | ||
44 | * Return the current time as the number of seconds since January 1, 1904. | ||
45 | */ | ||
46 | |||
47 | static long adb_read_time(void) | ||
48 | { | ||
49 | volatile struct adb_request req; | ||
50 | long time; | ||
51 | |||
52 | adb_request((struct adb_request *) &req, NULL, | ||
53 | ADBREQ_RAW|ADBREQ_SYNC, | ||
54 | 2, CUDA_PACKET, CUDA_GET_TIME); | ||
55 | |||
56 | time = (req.reply[3] << 24) | (req.reply[4] << 16) | ||
57 | | (req.reply[5] << 8) | req.reply[6]; | ||
58 | return time - RTC_OFFSET; | ||
59 | } | ||
60 | |||
61 | /* | ||
62 | * Set the current system time | ||
63 | */ | ||
64 | |||
65 | static void adb_write_time(long data) | ||
66 | { | ||
67 | volatile struct adb_request req; | ||
68 | |||
69 | data += RTC_OFFSET; | ||
70 | |||
71 | adb_request((struct adb_request *) &req, NULL, | ||
72 | ADBREQ_RAW|ADBREQ_SYNC, | ||
73 | 6, CUDA_PACKET, CUDA_SET_TIME, | ||
74 | (data >> 24) & 0xFF, (data >> 16) & 0xFF, | ||
75 | (data >> 8) & 0xFF, data & 0xFF); | ||
76 | } | ||
77 | |||
78 | /* | ||
79 | * Get a byte from the NVRAM | ||
80 | */ | ||
81 | |||
82 | static __u8 adb_read_pram(int offset) | ||
83 | { | ||
84 | volatile struct adb_request req; | ||
85 | |||
86 | adb_request((struct adb_request *) &req, NULL, | ||
87 | ADBREQ_RAW|ADBREQ_SYNC, | ||
88 | 4, CUDA_PACKET, CUDA_GET_PRAM, | ||
89 | (offset >> 8) & 0xFF, offset & 0xFF); | ||
90 | return req.reply[3]; | ||
91 | } | ||
92 | |||
93 | /* | ||
94 | * Write a byte to the NVRAM | ||
95 | */ | ||
96 | |||
97 | static void adb_write_pram(int offset, __u8 data) | ||
98 | { | ||
99 | volatile struct adb_request req; | ||
100 | |||
101 | adb_request((struct adb_request *) &req, NULL, | ||
102 | ADBREQ_RAW|ADBREQ_SYNC, | ||
103 | 5, CUDA_PACKET, CUDA_SET_PRAM, | ||
104 | (offset >> 8) & 0xFF, offset & 0xFF, | ||
105 | data); | ||
106 | } | ||
107 | #endif /* CONFIG_ADB */ | ||
108 | |||
109 | /* | ||
110 | * VIA PRAM/RTC access routines | ||
111 | * | ||
112 | * Must be called with interrupts disabled and | ||
113 | * the RTC should be enabled. | ||
114 | */ | ||
115 | |||
116 | static __u8 via_pram_readbyte(void) | ||
117 | { | ||
118 | int i,reg; | ||
119 | __u8 data; | ||
120 | |||
121 | reg = via1[vBufB] & ~VIA1B_vRTCClk; | ||
122 | |||
123 | /* Set the RTC data line to be an input. */ | ||
124 | |||
125 | via1[vDirB] &= ~VIA1B_vRTCData; | ||
126 | |||
127 | /* The bits of the byte come out in MSB order */ | ||
128 | |||
129 | data = 0; | ||
130 | for (i = 0 ; i < 8 ; i++) { | ||
131 | via1[vBufB] = reg; | ||
132 | via1[vBufB] = reg | VIA1B_vRTCClk; | ||
133 | data = (data << 1) | (via1[vBufB] & VIA1B_vRTCData); | ||
134 | } | ||
135 | |||
136 | /* Return RTC data line to output state */ | ||
137 | |||
138 | via1[vDirB] |= VIA1B_vRTCData; | ||
139 | |||
140 | return data; | ||
141 | } | ||
142 | |||
143 | static void via_pram_writebyte(__u8 data) | ||
144 | { | ||
145 | int i,reg,bit; | ||
146 | |||
147 | reg = via1[vBufB] & ~(VIA1B_vRTCClk | VIA1B_vRTCData); | ||
148 | |||
149 | /* The bits of the byte go in in MSB order */ | ||
150 | |||
151 | for (i = 0 ; i < 8 ; i++) { | ||
152 | bit = data & 0x80? 1 : 0; | ||
153 | data <<= 1; | ||
154 | via1[vBufB] = reg | bit; | ||
155 | via1[vBufB] = reg | bit | VIA1B_vRTCClk; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | /* | ||
160 | * Execute a VIA PRAM/RTC command. For read commands | ||
161 | * data should point to a one-byte buffer for the | ||
162 | * resulting data. For write commands it should point | ||
163 | * to the data byte to for the command. | ||
164 | * | ||
165 | * This function disables all interrupts while running. | ||
166 | */ | ||
167 | |||
168 | static void via_pram_command(int command, __u8 *data) | ||
169 | { | ||
170 | unsigned long flags; | ||
171 | int is_read; | ||
172 | |||
173 | local_irq_save(flags); | ||
174 | |||
175 | /* Enable the RTC and make sure the strobe line is high */ | ||
176 | |||
177 | via1[vBufB] = (via1[vBufB] | VIA1B_vRTCClk) & ~VIA1B_vRTCEnb; | ||
178 | |||
179 | if (command & 0xFF00) { /* extended (two-byte) command */ | ||
180 | via_pram_writebyte((command & 0xFF00) >> 8); | ||
181 | via_pram_writebyte(command & 0xFF); | ||
182 | is_read = command & 0x8000; | ||
183 | } else { /* one-byte command */ | ||
184 | via_pram_writebyte(command); | ||
185 | is_read = command & 0x80; | ||
186 | } | ||
187 | if (is_read) { | ||
188 | *data = via_pram_readbyte(); | ||
189 | } else { | ||
190 | via_pram_writebyte(*data); | ||
191 | } | ||
192 | |||
193 | /* All done, disable the RTC */ | ||
194 | |||
195 | via1[vBufB] |= VIA1B_vRTCEnb; | ||
196 | |||
197 | local_irq_restore(flags); | ||
198 | } | ||
199 | |||
200 | static __u8 via_read_pram(int offset) | ||
201 | { | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static void via_write_pram(int offset, __u8 data) | ||
206 | { | ||
207 | } | ||
208 | |||
209 | /* | ||
210 | * Return the current time in seconds since January 1, 1904. | ||
211 | * | ||
212 | * This only works on machines with the VIA-based PRAM/RTC, which | ||
213 | * is basically any machine with Mac II-style ADB. | ||
214 | */ | ||
215 | |||
216 | static long via_read_time(void) | ||
217 | { | ||
218 | union { | ||
219 | __u8 cdata[4]; | ||
220 | long idata; | ||
221 | } result, last_result; | ||
222 | int ct; | ||
223 | |||
224 | /* | ||
225 | * The NetBSD guys say to loop until you get the same reading | ||
226 | * twice in a row. | ||
227 | */ | ||
228 | |||
229 | ct = 0; | ||
230 | do { | ||
231 | if (++ct > 10) { | ||
232 | printk("via_read_time: couldn't get valid time, " | ||
233 | "last read = 0x%08lx and 0x%08lx\n", | ||
234 | last_result.idata, result.idata); | ||
235 | break; | ||
236 | } | ||
237 | |||
238 | last_result.idata = result.idata; | ||
239 | result.idata = 0; | ||
240 | |||
241 | via_pram_command(0x81, &result.cdata[3]); | ||
242 | via_pram_command(0x85, &result.cdata[2]); | ||
243 | via_pram_command(0x89, &result.cdata[1]); | ||
244 | via_pram_command(0x8D, &result.cdata[0]); | ||
245 | } while (result.idata != last_result.idata); | ||
246 | |||
247 | return result.idata - RTC_OFFSET; | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * Set the current time to a number of seconds since January 1, 1904. | ||
252 | * | ||
253 | * This only works on machines with the VIA-based PRAM/RTC, which | ||
254 | * is basically any machine with Mac II-style ADB. | ||
255 | */ | ||
256 | |||
257 | static void via_write_time(long time) | ||
258 | { | ||
259 | union { | ||
260 | __u8 cdata[4]; | ||
261 | long idata; | ||
262 | } data; | ||
263 | __u8 temp; | ||
264 | |||
265 | /* Clear the write protect bit */ | ||
266 | |||
267 | temp = 0x55; | ||
268 | via_pram_command(0x35, &temp); | ||
269 | |||
270 | data.idata = time + RTC_OFFSET; | ||
271 | via_pram_command(0x01, &data.cdata[3]); | ||
272 | via_pram_command(0x05, &data.cdata[2]); | ||
273 | via_pram_command(0x09, &data.cdata[1]); | ||
274 | via_pram_command(0x0D, &data.cdata[0]); | ||
275 | |||
276 | /* Set the write protect bit */ | ||
277 | |||
278 | temp = 0xD5; | ||
279 | via_pram_command(0x35, &temp); | ||
280 | } | ||
281 | |||
282 | static void via_shutdown(void) | ||
283 | { | ||
284 | if (rbv_present) { | ||
285 | via2[rBufB] &= ~0x04; | ||
286 | } else { | ||
287 | /* Direction of vDirB is output */ | ||
288 | via2[vDirB] |= 0x04; | ||
289 | /* Send a value of 0 on that line */ | ||
290 | via2[vBufB] &= ~0x04; | ||
291 | mdelay(1000); | ||
292 | } | ||
293 | } | ||
294 | |||
295 | /* | ||
296 | * FIXME: not sure how this is supposed to work exactly... | ||
297 | */ | ||
298 | |||
299 | static void oss_shutdown(void) | ||
300 | { | ||
301 | oss->rom_ctrl = OSS_POWEROFF; | ||
302 | } | ||
303 | |||
304 | #ifdef CONFIG_ADB_CUDA | ||
305 | |||
306 | static void cuda_restart(void) | ||
307 | { | ||
308 | adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, | ||
309 | 2, CUDA_PACKET, CUDA_RESET_SYSTEM); | ||
310 | } | ||
311 | |||
312 | static void cuda_shutdown(void) | ||
313 | { | ||
314 | adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, | ||
315 | 2, CUDA_PACKET, CUDA_POWERDOWN); | ||
316 | } | ||
317 | |||
318 | #endif /* CONFIG_ADB_CUDA */ | ||
319 | |||
320 | #ifdef CONFIG_ADB_PMU | ||
321 | |||
322 | void pmu_restart(void) | ||
323 | { | ||
324 | adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, | ||
325 | 3, PMU_PACKET, PMU_SET_INTR_MASK, | ||
326 | PMU_INT_ADB|PMU_INT_TICK); | ||
327 | |||
328 | adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, | ||
329 | 2, PMU_PACKET, PMU_RESET); | ||
330 | } | ||
331 | |||
332 | void pmu_shutdown(void) | ||
333 | { | ||
334 | adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, | ||
335 | 3, PMU_PACKET, PMU_SET_INTR_MASK, | ||
336 | PMU_INT_ADB|PMU_INT_TICK); | ||
337 | |||
338 | adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, | ||
339 | 6, PMU_PACKET, PMU_SHUTDOWN, | ||
340 | 'M', 'A', 'T', 'T'); | ||
341 | } | ||
342 | |||
343 | #endif /* CONFIG_ADB_PMU */ | ||
344 | |||
345 | /* | ||
346 | *------------------------------------------------------------------- | ||
347 | * Below this point are the generic routines; they'll dispatch to the | ||
348 | * correct routine for the hardware on which we're running. | ||
349 | *------------------------------------------------------------------- | ||
350 | */ | ||
351 | |||
352 | void mac_pram_read(int offset, __u8 *buffer, int len) | ||
353 | { | ||
354 | __u8 (*func)(int) = NULL; | ||
355 | int i; | ||
356 | |||
357 | if (macintosh_config->adb_type == MAC_ADB_IISI || | ||
358 | macintosh_config->adb_type == MAC_ADB_PB1 || | ||
359 | macintosh_config->adb_type == MAC_ADB_PB2 || | ||
360 | macintosh_config->adb_type == MAC_ADB_CUDA) { | ||
361 | #ifdef CONFIG_ADB | ||
362 | func = adb_read_pram; | ||
363 | #else | ||
364 | return; | ||
365 | #endif | ||
366 | } else { | ||
367 | func = via_read_pram; | ||
368 | } | ||
369 | for (i = 0 ; i < len ; i++) { | ||
370 | buffer[i] = (*func)(offset++); | ||
371 | } | ||
372 | } | ||
373 | |||
374 | void mac_pram_write(int offset, __u8 *buffer, int len) | ||
375 | { | ||
376 | void (*func)(int, __u8) = NULL; | ||
377 | int i; | ||
378 | |||
379 | if (macintosh_config->adb_type == MAC_ADB_IISI || | ||
380 | macintosh_config->adb_type == MAC_ADB_PB1 || | ||
381 | macintosh_config->adb_type == MAC_ADB_PB2 || | ||
382 | macintosh_config->adb_type == MAC_ADB_CUDA) { | ||
383 | #ifdef CONFIG_ADB | ||
384 | func = adb_write_pram; | ||
385 | #else | ||
386 | return; | ||
387 | #endif | ||
388 | } else { | ||
389 | func = via_write_pram; | ||
390 | } | ||
391 | for (i = 0 ; i < len ; i++) { | ||
392 | (*func)(offset++, buffer[i]); | ||
393 | } | ||
394 | } | ||
395 | |||
396 | void mac_poweroff(void) | ||
397 | { | ||
398 | /* | ||
399 | * MAC_ADB_IISI may need to be moved up here if it doesn't actually | ||
400 | * work using the ADB packet method. --David Kilzer | ||
401 | */ | ||
402 | |||
403 | if (oss_present) { | ||
404 | oss_shutdown(); | ||
405 | } else if (macintosh_config->adb_type == MAC_ADB_II) { | ||
406 | via_shutdown(); | ||
407 | #ifdef CONFIG_ADB_CUDA | ||
408 | } else if (macintosh_config->adb_type == MAC_ADB_CUDA) { | ||
409 | cuda_shutdown(); | ||
410 | #endif | ||
411 | #ifdef CONFIG_ADB_PMU | ||
412 | } else if (macintosh_config->adb_type == MAC_ADB_PB1 | ||
413 | || macintosh_config->adb_type == MAC_ADB_PB2) { | ||
414 | pmu_shutdown(); | ||
415 | #endif | ||
416 | } | ||
417 | local_irq_enable(); | ||
418 | printk("It is now safe to turn off your Macintosh.\n"); | ||
419 | while(1); | ||
420 | } | ||
421 | |||
422 | void mac_reset(void) | ||
423 | { | ||
424 | if (macintosh_config->adb_type == MAC_ADB_II) { | ||
425 | unsigned long flags; | ||
426 | |||
427 | /* need ROMBASE in booter */ | ||
428 | /* indeed, plus need to MAP THE ROM !! */ | ||
429 | |||
430 | if (mac_bi_data.rombase == 0) | ||
431 | mac_bi_data.rombase = 0x40800000; | ||
432 | |||
433 | /* works on some */ | ||
434 | rom_reset = (void *) (mac_bi_data.rombase + 0xa); | ||
435 | |||
436 | if (macintosh_config->ident == MAC_MODEL_SE30) { | ||
437 | /* | ||
438 | * MSch: Machines known to crash on ROM reset ... | ||
439 | */ | ||
440 | } else { | ||
441 | local_irq_save(flags); | ||
442 | |||
443 | rom_reset(); | ||
444 | |||
445 | local_irq_restore(flags); | ||
446 | } | ||
447 | #ifdef CONFIG_ADB_CUDA | ||
448 | } else if (macintosh_config->adb_type == MAC_ADB_CUDA) { | ||
449 | cuda_restart(); | ||
450 | #endif | ||
451 | #ifdef CONFIG_ADB_PMU | ||
452 | } else if (macintosh_config->adb_type == MAC_ADB_PB1 | ||
453 | || macintosh_config->adb_type == MAC_ADB_PB2) { | ||
454 | pmu_restart(); | ||
455 | #endif | ||
456 | } else if (CPU_IS_030) { | ||
457 | |||
458 | /* 030-specific reset routine. The idea is general, but the | ||
459 | * specific registers to reset are '030-specific. Until I | ||
460 | * have a non-030 machine, I can't test anything else. | ||
461 | * -- C. Scott Ananian <cananian@alumni.princeton.edu> | ||
462 | */ | ||
463 | |||
464 | unsigned long rombase = 0x40000000; | ||
465 | |||
466 | /* make a 1-to-1 mapping, using the transparent tran. reg. */ | ||
467 | unsigned long virt = (unsigned long) mac_reset; | ||
468 | unsigned long phys = virt_to_phys(mac_reset); | ||
469 | unsigned long offset = phys-virt; | ||
470 | local_irq_disable(); /* lets not screw this up, ok? */ | ||
471 | __asm__ __volatile__(".chip 68030\n\t" | ||
472 | "pmove %0,%/tt0\n\t" | ||
473 | ".chip 68k" | ||
474 | : : "m" ((phys&0xFF000000)|0x8777)); | ||
475 | /* Now jump to physical address so we can disable MMU */ | ||
476 | __asm__ __volatile__( | ||
477 | ".chip 68030\n\t" | ||
478 | "lea %/pc@(1f),%/a0\n\t" | ||
479 | "addl %0,%/a0\n\t"/* fixup target address and stack ptr */ | ||
480 | "addl %0,%/sp\n\t" | ||
481 | "pflusha\n\t" | ||
482 | "jmp %/a0@\n\t" /* jump into physical memory */ | ||
483 | "0:.long 0\n\t" /* a constant zero. */ | ||
484 | /* OK. Now reset everything and jump to reset vector. */ | ||
485 | "1:\n\t" | ||
486 | "lea %/pc@(0b),%/a0\n\t" | ||
487 | "pmove %/a0@, %/tc\n\t" /* disable mmu */ | ||
488 | "pmove %/a0@, %/tt0\n\t" /* disable tt0 */ | ||
489 | "pmove %/a0@, %/tt1\n\t" /* disable tt1 */ | ||
490 | "movel #0, %/a0\n\t" | ||
491 | "movec %/a0, %/vbr\n\t" /* clear vector base register */ | ||
492 | "movec %/a0, %/cacr\n\t" /* disable caches */ | ||
493 | "movel #0x0808,%/a0\n\t" | ||
494 | "movec %/a0, %/cacr\n\t" /* flush i&d caches */ | ||
495 | "movew #0x2700,%/sr\n\t" /* set up status register */ | ||
496 | "movel %1@(0x0),%/a0\n\t"/* load interrupt stack pointer */ | ||
497 | "movec %/a0, %/isp\n\t" | ||
498 | "movel %1@(0x4),%/a0\n\t" /* load reset vector */ | ||
499 | "reset\n\t" /* reset external devices */ | ||
500 | "jmp %/a0@\n\t" /* jump to the reset vector */ | ||
501 | ".chip 68k" | ||
502 | : : "r" (offset), "a" (rombase) : "a0"); | ||
503 | } | ||
504 | |||
505 | /* should never get here */ | ||
506 | local_irq_enable(); | ||
507 | printk ("Restart failed. Please restart manually.\n"); | ||
508 | while(1); | ||
509 | } | ||
510 | |||
511 | /* | ||
512 | * This function translates seconds since 1970 into a proper date. | ||
513 | * | ||
514 | * Algorithm cribbed from glibc2.1, __offtime(). | ||
515 | */ | ||
516 | #define SECS_PER_MINUTE (60) | ||
517 | #define SECS_PER_HOUR (SECS_PER_MINUTE * 60) | ||
518 | #define SECS_PER_DAY (SECS_PER_HOUR * 24) | ||
519 | |||
520 | static void unmktime(unsigned long time, long offset, | ||
521 | int *yearp, int *monp, int *dayp, | ||
522 | int *hourp, int *minp, int *secp) | ||
523 | { | ||
524 | /* How many days come before each month (0-12). */ | ||
525 | static const unsigned short int __mon_yday[2][13] = | ||
526 | { | ||
527 | /* Normal years. */ | ||
528 | { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, | ||
529 | /* Leap years. */ | ||
530 | { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } | ||
531 | }; | ||
532 | long int days, rem, y, wday, yday; | ||
533 | const unsigned short int *ip; | ||
534 | |||
535 | days = time / SECS_PER_DAY; | ||
536 | rem = time % SECS_PER_DAY; | ||
537 | rem += offset; | ||
538 | while (rem < 0) { | ||
539 | rem += SECS_PER_DAY; | ||
540 | --days; | ||
541 | } | ||
542 | while (rem >= SECS_PER_DAY) { | ||
543 | rem -= SECS_PER_DAY; | ||
544 | ++days; | ||
545 | } | ||
546 | *hourp = rem / SECS_PER_HOUR; | ||
547 | rem %= SECS_PER_HOUR; | ||
548 | *minp = rem / SECS_PER_MINUTE; | ||
549 | *secp = rem % SECS_PER_MINUTE; | ||
550 | /* January 1, 1970 was a Thursday. */ | ||
551 | wday = (4 + days) % 7; /* Day in the week. Not currently used */ | ||
552 | if (wday < 0) wday += 7; | ||
553 | y = 1970; | ||
554 | |||
555 | #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) | ||
556 | #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) | ||
557 | #define __isleap(year) \ | ||
558 | ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) | ||
559 | |||
560 | while (days < 0 || days >= (__isleap (y) ? 366 : 365)) | ||
561 | { | ||
562 | /* Guess a corrected year, assuming 365 days per year. */ | ||
563 | long int yg = y + days / 365 - (days % 365 < 0); | ||
564 | |||
565 | /* Adjust DAYS and Y to match the guessed year. */ | ||
566 | days -= ((yg - y) * 365 | ||
567 | + LEAPS_THRU_END_OF (yg - 1) | ||
568 | - LEAPS_THRU_END_OF (y - 1)); | ||
569 | y = yg; | ||
570 | } | ||
571 | *yearp = y - 1900; | ||
572 | yday = days; /* day in the year. Not currently used. */ | ||
573 | ip = __mon_yday[__isleap(y)]; | ||
574 | for (y = 11; days < (long int) ip[y]; --y) | ||
575 | continue; | ||
576 | days -= ip[y]; | ||
577 | *monp = y; | ||
578 | *dayp = days + 1; /* day in the month */ | ||
579 | return; | ||
580 | } | ||
581 | |||
582 | /* | ||
583 | * Read/write the hardware clock. | ||
584 | */ | ||
585 | |||
586 | int mac_hwclk(int op, struct rtc_time *t) | ||
587 | { | ||
588 | unsigned long now; | ||
589 | |||
590 | if (!op) { /* read */ | ||
591 | if (macintosh_config->adb_type == MAC_ADB_II) { | ||
592 | now = via_read_time(); | ||
593 | } else | ||
594 | #ifdef CONFIG_ADB | ||
595 | if ((macintosh_config->adb_type == MAC_ADB_IISI) || | ||
596 | (macintosh_config->adb_type == MAC_ADB_PB1) || | ||
597 | (macintosh_config->adb_type == MAC_ADB_PB2) || | ||
598 | (macintosh_config->adb_type == MAC_ADB_CUDA)) { | ||
599 | now = adb_read_time(); | ||
600 | } else | ||
601 | #endif | ||
602 | if (macintosh_config->adb_type == MAC_ADB_IOP) { | ||
603 | now = via_read_time(); | ||
604 | } else { | ||
605 | now = 0; | ||
606 | } | ||
607 | |||
608 | t->tm_wday = 0; | ||
609 | unmktime(now, 0, | ||
610 | &t->tm_year, &t->tm_mon, &t->tm_mday, | ||
611 | &t->tm_hour, &t->tm_min, &t->tm_sec); | ||
612 | printk("mac_hwclk: read %04d-%02d-%-2d %02d:%02d:%02d\n", | ||
613 | t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); | ||
614 | } else { /* write */ | ||
615 | printk("mac_hwclk: tried to write %04d-%02d-%-2d %02d:%02d:%02d\n", | ||
616 | t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); | ||
617 | |||
618 | #if 0 /* it trashes my rtc */ | ||
619 | now = mktime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, | ||
620 | t->tm_hour, t->tm_min, t->tm_sec); | ||
621 | |||
622 | if (macintosh_config->adb_type == MAC_ADB_II) { | ||
623 | via_write_time(now); | ||
624 | } else if ((macintosh_config->adb_type == MAC_ADB_IISI) || | ||
625 | (macintosh_config->adb_type == MAC_ADB_PB1) || | ||
626 | (macintosh_config->adb_type == MAC_ADB_PB2) || | ||
627 | (macintosh_config->adb_type == MAC_ADB_CUDA)) { | ||
628 | adb_write_time(now); | ||
629 | } else if (macintosh_config->adb_type == MAC_ADB_IOP) { | ||
630 | via_write_time(now); | ||
631 | } | ||
632 | #endif | ||
633 | } | ||
634 | return 0; | ||
635 | } | ||
636 | |||
637 | /* | ||
638 | * Set minutes/seconds in the hardware clock | ||
639 | */ | ||
640 | |||
641 | int mac_set_clock_mmss (unsigned long nowtime) | ||
642 | { | ||
643 | struct rtc_time now; | ||
644 | |||
645 | mac_hwclk(0, &now); | ||
646 | now.tm_sec = nowtime % 60; | ||
647 | now.tm_min = (nowtime / 60) % 60; | ||
648 | mac_hwclk(1, &now); | ||
649 | |||
650 | return 0; | ||
651 | } | ||
diff --git a/arch/m68k/mac/oss.c b/arch/m68k/mac/oss.c new file mode 100644 index 000000000000..333547692724 --- /dev/null +++ b/arch/m68k/mac/oss.c | |||
@@ -0,0 +1,301 @@ | |||
1 | /* | ||
2 | * OSS handling | ||
3 | * Written by Joshua M. Thompson (funaho@jurai.org) | ||
4 | * | ||
5 | * | ||
6 | * This chip is used in the IIfx in place of VIA #2. It acts like a fancy | ||
7 | * VIA chip with prorammable interrupt levels. | ||
8 | * | ||
9 | * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some | ||
10 | * recent insights into OSS operational details. | ||
11 | * 990610 (jmt) - Now taking fulll advantage of the OSS. Interrupts are mapped | ||
12 | * to mostly match the A/UX interrupt scheme supported on the | ||
13 | * VIA side. Also added support for enabling the ISM irq again | ||
14 | * since we now have a functional IOP manager. | ||
15 | */ | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/mm.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/init.h> | ||
22 | |||
23 | #include <asm/bootinfo.h> | ||
24 | #include <asm/machw.h> | ||
25 | #include <asm/macintosh.h> | ||
26 | #include <asm/macints.h> | ||
27 | #include <asm/mac_via.h> | ||
28 | #include <asm/mac_oss.h> | ||
29 | |||
30 | int oss_present; | ||
31 | volatile struct mac_oss *oss; | ||
32 | |||
33 | irqreturn_t oss_irq(int, void *, struct pt_regs *); | ||
34 | irqreturn_t oss_nubus_irq(int, void *, struct pt_regs *); | ||
35 | |||
36 | extern irqreturn_t via1_irq(int, void *, struct pt_regs *); | ||
37 | extern irqreturn_t mac_scc_dispatch(int, void *, struct pt_regs *); | ||
38 | |||
39 | /* | ||
40 | * Initialize the OSS | ||
41 | * | ||
42 | * The OSS "detection" code is actually in via_init() which is always called | ||
43 | * before us. Thus we can count on oss_present being valid on entry. | ||
44 | */ | ||
45 | |||
46 | void __init oss_init(void) | ||
47 | { | ||
48 | int i; | ||
49 | |||
50 | if (!oss_present) return; | ||
51 | |||
52 | oss = (struct mac_oss *) OSS_BASE; | ||
53 | |||
54 | /* Disable all interrupts. Unlike a VIA it looks like we */ | ||
55 | /* do this by setting the source's interrupt level to zero. */ | ||
56 | |||
57 | for (i = 0; i <= OSS_NUM_SOURCES; i++) { | ||
58 | oss->irq_level[i] = OSS_IRQLEV_DISABLED; | ||
59 | } | ||
60 | /* If we disable VIA1 here, we never really handle it... */ | ||
61 | oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1; | ||
62 | } | ||
63 | |||
64 | /* | ||
65 | * Register the OSS and NuBus interrupt dispatchers. | ||
66 | */ | ||
67 | |||
68 | void __init oss_register_interrupts(void) | ||
69 | { | ||
70 | cpu_request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK, | ||
71 | "scsi", (void *) oss); | ||
72 | cpu_request_irq(OSS_IRQLEV_IOPSCC, mac_scc_dispatch, IRQ_FLG_LOCK, | ||
73 | "scc", mac_scc_dispatch); | ||
74 | cpu_request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK, | ||
75 | "nubus", (void *) oss); | ||
76 | cpu_request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK, | ||
77 | "sound", (void *) oss); | ||
78 | cpu_request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK, | ||
79 | "via1", (void *) via1); | ||
80 | } | ||
81 | |||
82 | /* | ||
83 | * Initialize OSS for Nubus access | ||
84 | */ | ||
85 | |||
86 | void __init oss_nubus_init(void) | ||
87 | { | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * Handle miscellaneous OSS interrupts. Right now that's just sound | ||
92 | * and SCSI; everything else is routed to its own autovector IRQ. | ||
93 | */ | ||
94 | |||
95 | irqreturn_t oss_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
96 | { | ||
97 | int events; | ||
98 | |||
99 | events = oss->irq_pending & (OSS_IP_SOUND|OSS_IP_SCSI); | ||
100 | if (!events) | ||
101 | return IRQ_NONE; | ||
102 | |||
103 | #ifdef DEBUG_IRQS | ||
104 | if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) { | ||
105 | printk("oss_irq: irq %d events = 0x%04X\n", irq, | ||
106 | (int) oss->irq_pending); | ||
107 | } | ||
108 | #endif | ||
109 | /* FIXME: how do you clear a pending IRQ? */ | ||
110 | |||
111 | if (events & OSS_IP_SOUND) { | ||
112 | /* FIXME: call sound handler */ | ||
113 | oss->irq_pending &= ~OSS_IP_SOUND; | ||
114 | } else if (events & OSS_IP_SCSI) { | ||
115 | oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED; | ||
116 | mac_do_irq_list(IRQ_MAC_SCSI, regs); | ||
117 | oss->irq_pending &= ~OSS_IP_SCSI; | ||
118 | oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI; | ||
119 | } else { | ||
120 | /* FIXME: error check here? */ | ||
121 | } | ||
122 | return IRQ_HANDLED; | ||
123 | } | ||
124 | |||
125 | /* | ||
126 | * Nubus IRQ handler, OSS style | ||
127 | * | ||
128 | * Unlike the VIA/RBV this is on its own autovector interrupt level. | ||
129 | */ | ||
130 | |||
131 | irqreturn_t oss_nubus_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
132 | { | ||
133 | int events, irq_bit, i; | ||
134 | |||
135 | events = oss->irq_pending & OSS_IP_NUBUS; | ||
136 | if (!events) | ||
137 | return IRQ_NONE; | ||
138 | |||
139 | #ifdef DEBUG_NUBUS_INT | ||
140 | if (console_loglevel > 7) { | ||
141 | printk("oss_nubus_irq: events = 0x%04X\n", events); | ||
142 | } | ||
143 | #endif | ||
144 | /* There are only six slots on the OSS, not seven */ | ||
145 | |||
146 | for (i = 0, irq_bit = 1 ; i < 6 ; i++, irq_bit <<= 1) { | ||
147 | if (events & irq_bit) { | ||
148 | oss->irq_level[i] = OSS_IRQLEV_DISABLED; | ||
149 | mac_do_irq_list(NUBUS_SOURCE_BASE + i, regs); | ||
150 | oss->irq_pending &= ~irq_bit; | ||
151 | oss->irq_level[i] = OSS_IRQLEV_NUBUS; | ||
152 | } | ||
153 | } | ||
154 | return IRQ_HANDLED; | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * Enable an OSS interrupt | ||
159 | * | ||
160 | * It looks messy but it's rather straightforward. The switch() statement | ||
161 | * just maps the machspec interrupt numbers to the right OSS interrupt | ||
162 | * source (if the OSS handles that interrupt) and then sets the interrupt | ||
163 | * level for that source to nonzero, thus enabling the interrupt. | ||
164 | */ | ||
165 | |||
166 | void oss_irq_enable(int irq) { | ||
167 | #ifdef DEBUG_IRQUSE | ||
168 | printk("oss_irq_enable(%d)\n", irq); | ||
169 | #endif | ||
170 | switch(irq) { | ||
171 | case IRQ_SCC: | ||
172 | case IRQ_SCCA: | ||
173 | case IRQ_SCCB: | ||
174 | oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC; | ||
175 | break; | ||
176 | case IRQ_MAC_ADB: | ||
177 | oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM; | ||
178 | break; | ||
179 | case IRQ_MAC_SCSI: | ||
180 | oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI; | ||
181 | break; | ||
182 | case IRQ_NUBUS_9: | ||
183 | case IRQ_NUBUS_A: | ||
184 | case IRQ_NUBUS_B: | ||
185 | case IRQ_NUBUS_C: | ||
186 | case IRQ_NUBUS_D: | ||
187 | case IRQ_NUBUS_E: | ||
188 | irq -= NUBUS_SOURCE_BASE; | ||
189 | oss->irq_level[irq] = OSS_IRQLEV_NUBUS; | ||
190 | break; | ||
191 | #ifdef DEBUG_IRQUSE | ||
192 | default: | ||
193 | printk("%s unknown irq %d\n",__FUNCTION__, irq); | ||
194 | break; | ||
195 | #endif | ||
196 | } | ||
197 | } | ||
198 | |||
199 | /* | ||
200 | * Disable an OSS interrupt | ||
201 | * | ||
202 | * Same as above except we set the source's interrupt level to zero, | ||
203 | * to disable the interrupt. | ||
204 | */ | ||
205 | |||
206 | void oss_irq_disable(int irq) { | ||
207 | #ifdef DEBUG_IRQUSE | ||
208 | printk("oss_irq_disable(%d)\n", irq); | ||
209 | #endif | ||
210 | switch(irq) { | ||
211 | case IRQ_SCC: | ||
212 | case IRQ_SCCA: | ||
213 | case IRQ_SCCB: | ||
214 | oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_DISABLED; | ||
215 | break; | ||
216 | case IRQ_MAC_ADB: | ||
217 | oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_DISABLED; | ||
218 | break; | ||
219 | case IRQ_MAC_SCSI: | ||
220 | oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED; | ||
221 | break; | ||
222 | case IRQ_NUBUS_9: | ||
223 | case IRQ_NUBUS_A: | ||
224 | case IRQ_NUBUS_B: | ||
225 | case IRQ_NUBUS_C: | ||
226 | case IRQ_NUBUS_D: | ||
227 | case IRQ_NUBUS_E: | ||
228 | irq -= NUBUS_SOURCE_BASE; | ||
229 | oss->irq_level[irq] = OSS_IRQLEV_DISABLED; | ||
230 | break; | ||
231 | #ifdef DEBUG_IRQUSE | ||
232 | default: | ||
233 | printk("%s unknown irq %d\n", __FUNCTION__, irq); | ||
234 | break; | ||
235 | #endif | ||
236 | } | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * Clear an OSS interrupt | ||
241 | * | ||
242 | * Not sure if this works or not but it's the only method I could | ||
243 | * think of based on the contents of the mac_oss structure. | ||
244 | */ | ||
245 | |||
246 | void oss_irq_clear(int irq) { | ||
247 | /* FIXME: how to do this on OSS? */ | ||
248 | switch(irq) { | ||
249 | case IRQ_SCC: | ||
250 | case IRQ_SCCA: | ||
251 | case IRQ_SCCB: | ||
252 | oss->irq_pending &= ~OSS_IP_IOPSCC; | ||
253 | break; | ||
254 | case IRQ_MAC_ADB: | ||
255 | oss->irq_pending &= ~OSS_IP_IOPISM; | ||
256 | break; | ||
257 | case IRQ_MAC_SCSI: | ||
258 | oss->irq_pending &= ~OSS_IP_SCSI; | ||
259 | break; | ||
260 | case IRQ_NUBUS_9: | ||
261 | case IRQ_NUBUS_A: | ||
262 | case IRQ_NUBUS_B: | ||
263 | case IRQ_NUBUS_C: | ||
264 | case IRQ_NUBUS_D: | ||
265 | case IRQ_NUBUS_E: | ||
266 | irq -= NUBUS_SOURCE_BASE; | ||
267 | oss->irq_pending &= ~(1 << irq); | ||
268 | break; | ||
269 | } | ||
270 | } | ||
271 | |||
272 | /* | ||
273 | * Check to see if a specific OSS interrupt is pending | ||
274 | */ | ||
275 | |||
276 | int oss_irq_pending(int irq) | ||
277 | { | ||
278 | switch(irq) { | ||
279 | case IRQ_SCC: | ||
280 | case IRQ_SCCA: | ||
281 | case IRQ_SCCB: | ||
282 | return oss->irq_pending & OSS_IP_IOPSCC; | ||
283 | break; | ||
284 | case IRQ_MAC_ADB: | ||
285 | return oss->irq_pending & OSS_IP_IOPISM; | ||
286 | break; | ||
287 | case IRQ_MAC_SCSI: | ||
288 | return oss->irq_pending & OSS_IP_SCSI; | ||
289 | break; | ||
290 | case IRQ_NUBUS_9: | ||
291 | case IRQ_NUBUS_A: | ||
292 | case IRQ_NUBUS_B: | ||
293 | case IRQ_NUBUS_C: | ||
294 | case IRQ_NUBUS_D: | ||
295 | case IRQ_NUBUS_E: | ||
296 | irq -= NUBUS_SOURCE_BASE; | ||
297 | return oss->irq_pending & (1 << irq); | ||
298 | break; | ||
299 | } | ||
300 | return 0; | ||
301 | } | ||
diff --git a/arch/m68k/mac/psc.c b/arch/m68k/mac/psc.c new file mode 100644 index 000000000000..e72384e43a1e --- /dev/null +++ b/arch/m68k/mac/psc.c | |||
@@ -0,0 +1,197 @@ | |||
1 | /* | ||
2 | * Apple Peripheral System Controller (PSC) | ||
3 | * | ||
4 | * The PSC is used on the AV Macs to control IO functions not handled | ||
5 | * by the VIAs (Ethernet, DSP, SCC). | ||
6 | * | ||
7 | * TO DO: | ||
8 | * | ||
9 | * Try to figure out what's going on in pIFR5 and pIFR6. There seem to be | ||
10 | * persisant interrupt conditions in those registers and I have no idea what | ||
11 | * they are. Granted it doesn't affect since we're not enabling any interrupts | ||
12 | * on those levels at the moment, but it would be nice to know. I have a feeling | ||
13 | * they aren't actually interrupt lines but data lines (to the DSP?) | ||
14 | */ | ||
15 | |||
16 | #include <linux/types.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/init.h> | ||
21 | |||
22 | #include <asm/traps.h> | ||
23 | #include <asm/bootinfo.h> | ||
24 | #include <asm/macintosh.h> | ||
25 | #include <asm/macints.h> | ||
26 | #include <asm/mac_psc.h> | ||
27 | |||
28 | #define DEBUG_PSC | ||
29 | |||
30 | int psc_present; | ||
31 | volatile __u8 *psc; | ||
32 | |||
33 | irqreturn_t psc_irq(int, void *, struct pt_regs *); | ||
34 | |||
35 | /* | ||
36 | * Debugging dump, used in various places to see what's going on. | ||
37 | */ | ||
38 | |||
39 | void psc_debug_dump(void) | ||
40 | { | ||
41 | int i; | ||
42 | |||
43 | if (!psc_present) return; | ||
44 | for (i = 0x30 ; i < 0x70 ; i += 0x10) { | ||
45 | printk("PSC #%d: IFR = 0x%02X IER = 0x%02X\n", | ||
46 | i >> 4, | ||
47 | (int) psc_read_byte(pIFRbase + i), | ||
48 | (int) psc_read_byte(pIERbase + i)); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | /* | ||
53 | * Try to kill all DMA channels on the PSC. Not sure how this his | ||
54 | * supposed to work; this is code lifted from macmace.c and then | ||
55 | * expanded to cover what I think are the other 7 channels. | ||
56 | */ | ||
57 | |||
58 | void psc_dma_die_die_die(void) | ||
59 | { | ||
60 | int i; | ||
61 | |||
62 | printk("Killing all PSC DMA channels..."); | ||
63 | for (i = 0 ; i < 9 ; i++) { | ||
64 | psc_write_word(PSC_CTL_BASE + (i << 4), 0x8800); | ||
65 | psc_write_word(PSC_CTL_BASE + (i << 4), 0x1000); | ||
66 | psc_write_word(PSC_CMD_BASE + (i << 5), 0x1100); | ||
67 | psc_write_word(PSC_CMD_BASE + (i << 5) + 0x10, 0x1100); | ||
68 | } | ||
69 | printk("done!\n"); | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * Initialize the PSC. For now this just involves shutting down all | ||
74 | * interrupt sources using the IERs. | ||
75 | */ | ||
76 | |||
77 | void __init psc_init(void) | ||
78 | { | ||
79 | int i; | ||
80 | |||
81 | if (macintosh_config->ident != MAC_MODEL_C660 | ||
82 | && macintosh_config->ident != MAC_MODEL_Q840) | ||
83 | { | ||
84 | psc = NULL; | ||
85 | psc_present = 0; | ||
86 | return; | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * The PSC is always at the same spot, but using psc | ||
91 | * keeps things consisant with the psc_xxxx functions. | ||
92 | */ | ||
93 | |||
94 | psc = (void *) PSC_BASE; | ||
95 | psc_present = 1; | ||
96 | |||
97 | printk("PSC detected at %p\n", psc); | ||
98 | |||
99 | psc_dma_die_die_die(); | ||
100 | |||
101 | #ifdef DEBUG_PSC | ||
102 | psc_debug_dump(); | ||
103 | #endif | ||
104 | /* | ||
105 | * Mask and clear all possible interrupts | ||
106 | */ | ||
107 | |||
108 | for (i = 0x30 ; i < 0x70 ; i += 0x10) { | ||
109 | psc_write_byte(pIERbase + i, 0x0F); | ||
110 | psc_write_byte(pIFRbase + i, 0x0F); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | /* | ||
115 | * Register the PSC interrupt dispatchers for autovector interrupts 3-6. | ||
116 | */ | ||
117 | |||
118 | void __init psc_register_interrupts(void) | ||
119 | { | ||
120 | cpu_request_irq(3, psc_irq, IRQ_FLG_LOCK, "psc3", (void *) 0x30); | ||
121 | cpu_request_irq(4, psc_irq, IRQ_FLG_LOCK, "psc4", (void *) 0x40); | ||
122 | cpu_request_irq(5, psc_irq, IRQ_FLG_LOCK, "psc5", (void *) 0x50); | ||
123 | cpu_request_irq(6, psc_irq, IRQ_FLG_LOCK, "psc6", (void *) 0x60); | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * PSC interrupt handler. It's a lot like the VIA interrupt handler. | ||
128 | */ | ||
129 | |||
130 | irqreturn_t psc_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
131 | { | ||
132 | int pIFR = pIFRbase + ((int) dev_id); | ||
133 | int pIER = pIERbase + ((int) dev_id); | ||
134 | int base_irq; | ||
135 | int irq_bit,i; | ||
136 | unsigned char events; | ||
137 | |||
138 | base_irq = irq << 3; | ||
139 | |||
140 | #ifdef DEBUG_IRQS | ||
141 | printk("psc_irq: irq %d pIFR = 0x%02X pIER = 0x%02X\n", | ||
142 | irq, (int) psc_read_byte(pIFR), (int) psc_read_byte(pIER)); | ||
143 | #endif | ||
144 | |||
145 | events = psc_read_byte(pIFR) & psc_read_byte(pIER) & 0xF; | ||
146 | if (!events) | ||
147 | return IRQ_NONE; | ||
148 | |||
149 | for (i = 0, irq_bit = 1 ; i < 4 ; i++, irq_bit <<= 1) { | ||
150 | if (events & irq_bit) { | ||
151 | psc_write_byte(pIER, irq_bit); | ||
152 | mac_do_irq_list(base_irq + i, regs); | ||
153 | psc_write_byte(pIFR, irq_bit); | ||
154 | psc_write_byte(pIER, irq_bit | 0x80); | ||
155 | } | ||
156 | } | ||
157 | return IRQ_HANDLED; | ||
158 | } | ||
159 | |||
160 | void psc_irq_enable(int irq) { | ||
161 | int irq_src = IRQ_SRC(irq); | ||
162 | int irq_idx = IRQ_IDX(irq); | ||
163 | int pIER = pIERbase + (irq_src << 4); | ||
164 | |||
165 | #ifdef DEBUG_IRQUSE | ||
166 | printk("psc_irq_enable(%d)\n", irq); | ||
167 | #endif | ||
168 | psc_write_byte(pIER, (1 << irq_idx) | 0x80); | ||
169 | } | ||
170 | |||
171 | void psc_irq_disable(int irq) { | ||
172 | int irq_src = IRQ_SRC(irq); | ||
173 | int irq_idx = IRQ_IDX(irq); | ||
174 | int pIER = pIERbase + (irq_src << 4); | ||
175 | |||
176 | #ifdef DEBUG_IRQUSE | ||
177 | printk("psc_irq_disable(%d)\n", irq); | ||
178 | #endif | ||
179 | psc_write_byte(pIER, 1 << irq_idx); | ||
180 | } | ||
181 | |||
182 | void psc_irq_clear(int irq) { | ||
183 | int irq_src = IRQ_SRC(irq); | ||
184 | int irq_idx = IRQ_IDX(irq); | ||
185 | int pIFR = pIERbase + (irq_src << 4); | ||
186 | |||
187 | psc_write_byte(pIFR, 1 << irq_idx); | ||
188 | } | ||
189 | |||
190 | int psc_irq_pending(int irq) | ||
191 | { | ||
192 | int irq_src = IRQ_SRC(irq); | ||
193 | int irq_idx = IRQ_IDX(irq); | ||
194 | int pIFR = pIERbase + (irq_src << 4); | ||
195 | |||
196 | return psc_read_byte(pIFR) & (1 << irq_idx); | ||
197 | } | ||
diff --git a/arch/m68k/mac/via.c b/arch/m68k/mac/via.c new file mode 100644 index 000000000000..cd528bf7b43f --- /dev/null +++ b/arch/m68k/mac/via.c | |||
@@ -0,0 +1,619 @@ | |||
1 | /* | ||
2 | * 6522 Versatile Interface Adapter (VIA) | ||
3 | * | ||
4 | * There are two of these on the Mac II. Some IRQ's are vectored | ||
5 | * via them as are assorted bits and bobs - eg RTC, ADB. | ||
6 | * | ||
7 | * CSA: Motorola seems to have removed documentation on the 6522 from | ||
8 | * their web site; try | ||
9 | * http://nerini.drf.com/vectrex/other/text/chips/6522/ | ||
10 | * http://www.zymurgy.net/classic/vic20/vicdet1.htm | ||
11 | * and | ||
12 | * http://193.23.168.87/mikro_laborversuche/via_iobaustein/via6522_1.html | ||
13 | * for info. A full-text web search on 6522 AND VIA will probably also | ||
14 | * net some usefulness. <cananian@alumni.princeton.edu> 20apr1999 | ||
15 | * | ||
16 | * PRAM/RTC access algorithms are from the NetBSD RTC toolkit version 1.08b | ||
17 | * by Erik Vogan and adapted to Linux by Joshua M. Thompson (funaho@jurai.org) | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <linux/types.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/mm.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/ide.h> | ||
27 | |||
28 | #include <asm/traps.h> | ||
29 | #include <asm/bootinfo.h> | ||
30 | #include <asm/macintosh.h> | ||
31 | #include <asm/macints.h> | ||
32 | #include <asm/machw.h> | ||
33 | #include <asm/mac_via.h> | ||
34 | #include <asm/mac_psc.h> | ||
35 | |||
36 | volatile __u8 *via1, *via2; | ||
37 | #if 0 | ||
38 | /* See note in mac_via.h about how this is possibly not useful */ | ||
39 | volatile long *via_memory_bogon=(long *)&via_memory_bogon; | ||
40 | #endif | ||
41 | int rbv_present,via_alt_mapping; | ||
42 | __u8 rbv_clear; | ||
43 | |||
44 | /* | ||
45 | * Globals for accessing the VIA chip registers without having to | ||
46 | * check if we're hitting a real VIA or an RBV. Normally you could | ||
47 | * just hit the combined register (ie, vIER|rIER) but that seems to | ||
48 | * break on AV Macs...probably because they actually decode more than | ||
49 | * eight address bits. Why can't Apple engineers at least be | ||
50 | * _consistently_ lazy? - 1999-05-21 (jmt) | ||
51 | */ | ||
52 | |||
53 | static int gIER,gIFR,gBufA,gBufB; | ||
54 | |||
55 | /* | ||
56 | * Timer defs. | ||
57 | */ | ||
58 | |||
59 | #define TICK_SIZE 10000 | ||
60 | #define MAC_CLOCK_TICK (783300/HZ) /* ticks per HZ */ | ||
61 | #define MAC_CLOCK_LOW (MAC_CLOCK_TICK&0xFF) | ||
62 | #define MAC_CLOCK_HIGH (MAC_CLOCK_TICK>>8) | ||
63 | |||
64 | static int nubus_active; | ||
65 | |||
66 | void via_debug_dump(void); | ||
67 | irqreturn_t via1_irq(int, void *, struct pt_regs *); | ||
68 | irqreturn_t via2_irq(int, void *, struct pt_regs *); | ||
69 | irqreturn_t via_nubus_irq(int, void *, struct pt_regs *); | ||
70 | void via_irq_enable(int irq); | ||
71 | void via_irq_disable(int irq); | ||
72 | void via_irq_clear(int irq); | ||
73 | |||
74 | extern irqreturn_t mac_bang(int, void *, struct pt_regs *); | ||
75 | extern irqreturn_t mac_scc_dispatch(int, void *, struct pt_regs *); | ||
76 | extern int oss_present; | ||
77 | |||
78 | /* | ||
79 | * Initialize the VIAs | ||
80 | * | ||
81 | * First we figure out where they actually _are_ as well as what type of | ||
82 | * VIA we have for VIA2 (it could be a real VIA or an RBV or even an OSS.) | ||
83 | * Then we pretty much clear them out and disable all IRQ sources. | ||
84 | * | ||
85 | * Note: the OSS is actually "detected" here and not in oss_init(). It just | ||
86 | * seems more logical to do it here since via_init() needs to know | ||
87 | * these things anyways. | ||
88 | */ | ||
89 | |||
90 | void __init via_init(void) | ||
91 | { | ||
92 | switch(macintosh_config->via_type) { | ||
93 | |||
94 | /* IIci, IIsi, IIvx, IIvi (P6xx), LC series */ | ||
95 | |||
96 | case MAC_VIA_IIci: | ||
97 | via1 = (void *) VIA1_BASE; | ||
98 | if (macintosh_config->ident == MAC_MODEL_IIFX) { | ||
99 | via2 = NULL; | ||
100 | rbv_present = 0; | ||
101 | oss_present = 1; | ||
102 | } else { | ||
103 | via2 = (void *) RBV_BASE; | ||
104 | rbv_present = 1; | ||
105 | oss_present = 0; | ||
106 | } | ||
107 | if (macintosh_config->ident == MAC_MODEL_LCIII) { | ||
108 | rbv_clear = 0x00; | ||
109 | } else { | ||
110 | /* on most RBVs (& unlike the VIAs), you */ | ||
111 | /* need to set bit 7 when you write to IFR */ | ||
112 | /* in order for your clear to occur. */ | ||
113 | rbv_clear = 0x80; | ||
114 | } | ||
115 | gIER = rIER; | ||
116 | gIFR = rIFR; | ||
117 | gBufA = rSIFR; | ||
118 | gBufB = rBufB; | ||
119 | break; | ||
120 | |||
121 | /* Quadra and early MacIIs agree on the VIA locations */ | ||
122 | |||
123 | case MAC_VIA_QUADRA: | ||
124 | case MAC_VIA_II: | ||
125 | via1 = (void *) VIA1_BASE; | ||
126 | via2 = (void *) VIA2_BASE; | ||
127 | rbv_present = 0; | ||
128 | oss_present = 0; | ||
129 | rbv_clear = 0x00; | ||
130 | gIER = vIER; | ||
131 | gIFR = vIFR; | ||
132 | gBufA = vBufA; | ||
133 | gBufB = vBufB; | ||
134 | break; | ||
135 | default: | ||
136 | panic("UNKNOWN VIA TYPE"); | ||
137 | } | ||
138 | |||
139 | printk(KERN_INFO "VIA1 at %p is a 6522 or clone\n", via1); | ||
140 | |||
141 | printk(KERN_INFO "VIA2 at %p is ", via2); | ||
142 | if (rbv_present) { | ||
143 | printk(KERN_INFO "an RBV\n"); | ||
144 | } else if (oss_present) { | ||
145 | printk(KERN_INFO "an OSS\n"); | ||
146 | } else { | ||
147 | printk(KERN_INFO "a 6522 or clone\n"); | ||
148 | } | ||
149 | |||
150 | #ifdef DEBUG_VIA | ||
151 | via_debug_dump(); | ||
152 | #endif | ||
153 | |||
154 | /* | ||
155 | * Shut down all IRQ sources, reset the timers, and | ||
156 | * kill the timer latch on VIA1. | ||
157 | */ | ||
158 | |||
159 | via1[vIER] = 0x7F; | ||
160 | via1[vIFR] = 0x7F; | ||
161 | via1[vT1LL] = 0; | ||
162 | via1[vT1LH] = 0; | ||
163 | via1[vT1CL] = 0; | ||
164 | via1[vT1CH] = 0; | ||
165 | via1[vT2CL] = 0; | ||
166 | via1[vT2CH] = 0; | ||
167 | via1[vACR] &= 0x3F; | ||
168 | |||
169 | /* | ||
170 | * SE/30: disable video IRQ | ||
171 | * XXX: testing for SE/30 VBL | ||
172 | */ | ||
173 | |||
174 | if (macintosh_config->ident == MAC_MODEL_SE30) { | ||
175 | via1[vDirB] |= 0x40; | ||
176 | via1[vBufB] |= 0x40; | ||
177 | } | ||
178 | |||
179 | /* | ||
180 | * Set the RTC bits to a known state: all lines to outputs and | ||
181 | * RTC disabled (yes that's 0 to enable and 1 to disable). | ||
182 | */ | ||
183 | |||
184 | via1[vDirB] |= (VIA1B_vRTCEnb | VIA1B_vRTCClk | VIA1B_vRTCData); | ||
185 | via1[vBufB] |= (VIA1B_vRTCEnb | VIA1B_vRTCClk); | ||
186 | |||
187 | /* Everything below this point is VIA2/RBV only... */ | ||
188 | |||
189 | if (oss_present) return; | ||
190 | |||
191 | #if 1 | ||
192 | /* Some machines support an alternate IRQ mapping that spreads */ | ||
193 | /* Ethernet and Sound out to their own autolevel IRQs and moves */ | ||
194 | /* VIA1 to level 6. A/UX uses this mapping and we do too. Note */ | ||
195 | /* that the IIfx emulates this alternate mapping using the OSS. */ | ||
196 | |||
197 | switch(macintosh_config->ident) { | ||
198 | case MAC_MODEL_C610: | ||
199 | case MAC_MODEL_Q610: | ||
200 | case MAC_MODEL_C650: | ||
201 | case MAC_MODEL_Q650: | ||
202 | case MAC_MODEL_Q700: | ||
203 | case MAC_MODEL_Q800: | ||
204 | case MAC_MODEL_Q900: | ||
205 | case MAC_MODEL_Q950: | ||
206 | via_alt_mapping = 1; | ||
207 | via1[vDirB] |= 0x40; | ||
208 | via1[vBufB] &= ~0x40; | ||
209 | break; | ||
210 | default: | ||
211 | via_alt_mapping = 0; | ||
212 | break; | ||
213 | } | ||
214 | #else | ||
215 | /* The alernate IRQ mapping seems to just not work. Anyone with a */ | ||
216 | /* supported machine is welcome to take a stab at fixing it. It */ | ||
217 | /* _should_ work on the following Quadras: 610,650,700,800,900,950 */ | ||
218 | /* - 1999-06-12 (jmt) */ | ||
219 | |||
220 | via_alt_mapping = 0; | ||
221 | #endif | ||
222 | |||
223 | /* | ||
224 | * Now initialize VIA2. For RBV we just kill all interrupts; | ||
225 | * for a regular VIA we also reset the timers and stuff. | ||
226 | */ | ||
227 | |||
228 | via2[gIER] = 0x7F; | ||
229 | via2[gIFR] = 0x7F | rbv_clear; | ||
230 | if (!rbv_present) { | ||
231 | via2[vT1LL] = 0; | ||
232 | via2[vT1LH] = 0; | ||
233 | via2[vT1CL] = 0; | ||
234 | via2[vT1CH] = 0; | ||
235 | via2[vT2CL] = 0; | ||
236 | via2[vT2CH] = 0; | ||
237 | via2[vACR] &= 0x3F; | ||
238 | } | ||
239 | } | ||
240 | |||
241 | /* | ||
242 | * Start the 100 Hz clock | ||
243 | */ | ||
244 | |||
245 | void __init via_init_clock(irqreturn_t (*func)(int, void *, struct pt_regs *)) | ||
246 | { | ||
247 | via1[vACR] |= 0x40; | ||
248 | via1[vT1LL] = MAC_CLOCK_LOW; | ||
249 | via1[vT1LH] = MAC_CLOCK_HIGH; | ||
250 | via1[vT1CL] = MAC_CLOCK_LOW; | ||
251 | via1[vT1CH] = MAC_CLOCK_HIGH; | ||
252 | |||
253 | request_irq(IRQ_MAC_TIMER_1, func, IRQ_FLG_LOCK, "timer", func); | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * Register the interrupt dispatchers for VIA or RBV machines only. | ||
258 | */ | ||
259 | |||
260 | void __init via_register_interrupts(void) | ||
261 | { | ||
262 | if (via_alt_mapping) { | ||
263 | cpu_request_irq(IRQ_AUTO_1, via1_irq, | ||
264 | IRQ_FLG_LOCK|IRQ_FLG_FAST, "software", | ||
265 | (void *) via1); | ||
266 | cpu_request_irq(IRQ_AUTO_6, via1_irq, | ||
267 | IRQ_FLG_LOCK|IRQ_FLG_FAST, "via1", | ||
268 | (void *) via1); | ||
269 | } else { | ||
270 | cpu_request_irq(IRQ_AUTO_1, via1_irq, | ||
271 | IRQ_FLG_LOCK|IRQ_FLG_FAST, "via1", | ||
272 | (void *) via1); | ||
273 | #if 0 /* interferes with serial on some machines */ | ||
274 | if (!psc_present) { | ||
275 | cpu_request_irq(IRQ_AUTO_6, mac_bang, IRQ_FLG_LOCK, | ||
276 | "Off Switch", mac_bang); | ||
277 | } | ||
278 | #endif | ||
279 | } | ||
280 | cpu_request_irq(IRQ_AUTO_2, via2_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, | ||
281 | "via2", (void *) via2); | ||
282 | if (!psc_present) { | ||
283 | cpu_request_irq(IRQ_AUTO_4, mac_scc_dispatch, IRQ_FLG_LOCK, | ||
284 | "scc", mac_scc_dispatch); | ||
285 | } | ||
286 | request_irq(IRQ_MAC_NUBUS, via_nubus_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, | ||
287 | "nubus", (void *) via2); | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * Debugging dump, used in various places to see what's going on. | ||
292 | */ | ||
293 | |||
294 | void via_debug_dump(void) | ||
295 | { | ||
296 | printk(KERN_DEBUG "VIA1: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n", | ||
297 | (uint) via1[vDirA], (uint) via1[vDirB], (uint) via1[vACR]); | ||
298 | printk(KERN_DEBUG " PCR = 0x%02X IFR = 0x%02X IER = 0x%02X\n", | ||
299 | (uint) via1[vPCR], (uint) via1[vIFR], (uint) via1[vIER]); | ||
300 | if (oss_present) { | ||
301 | printk(KERN_DEBUG "VIA2: <OSS>\n"); | ||
302 | } else if (rbv_present) { | ||
303 | printk(KERN_DEBUG "VIA2: IFR = 0x%02X IER = 0x%02X\n", | ||
304 | (uint) via2[rIFR], (uint) via2[rIER]); | ||
305 | printk(KERN_DEBUG " SIFR = 0x%02X SIER = 0x%02X\n", | ||
306 | (uint) via2[rSIFR], (uint) via2[rSIER]); | ||
307 | } else { | ||
308 | printk(KERN_DEBUG "VIA2: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n", | ||
309 | (uint) via2[vDirA], (uint) via2[vDirB], | ||
310 | (uint) via2[vACR]); | ||
311 | printk(KERN_DEBUG " PCR = 0x%02X IFR = 0x%02X IER = 0x%02X\n", | ||
312 | (uint) via2[vPCR], | ||
313 | (uint) via2[vIFR], (uint) via2[vIER]); | ||
314 | } | ||
315 | } | ||
316 | |||
317 | /* | ||
318 | * This is always executed with interrupts disabled. | ||
319 | * | ||
320 | * TBI: get time offset between scheduling timer ticks | ||
321 | */ | ||
322 | |||
323 | unsigned long mac_gettimeoffset (void) | ||
324 | { | ||
325 | unsigned long ticks, offset = 0; | ||
326 | |||
327 | /* read VIA1 timer 2 current value */ | ||
328 | ticks = via1[vT1CL] | (via1[vT1CH] << 8); | ||
329 | /* The probability of underflow is less than 2% */ | ||
330 | if (ticks > MAC_CLOCK_TICK - MAC_CLOCK_TICK / 50) | ||
331 | /* Check for pending timer interrupt in VIA1 IFR */ | ||
332 | if (via1[vIFR] & 0x40) offset = TICK_SIZE; | ||
333 | |||
334 | ticks = MAC_CLOCK_TICK - ticks; | ||
335 | ticks = ticks * 10000L / MAC_CLOCK_TICK; | ||
336 | |||
337 | return ticks + offset; | ||
338 | } | ||
339 | |||
340 | /* | ||
341 | * Flush the L2 cache on Macs that have it by flipping | ||
342 | * the system into 24-bit mode for an instant. | ||
343 | */ | ||
344 | |||
345 | void via_flush_cache(void) | ||
346 | { | ||
347 | via2[gBufB] &= ~VIA2B_vMode32; | ||
348 | via2[gBufB] |= VIA2B_vMode32; | ||
349 | } | ||
350 | |||
351 | /* | ||
352 | * Return the status of the L2 cache on a IIci | ||
353 | */ | ||
354 | |||
355 | int via_get_cache_disable(void) | ||
356 | { | ||
357 | /* Safeguard against being called accidentally */ | ||
358 | if (!via2) { | ||
359 | printk(KERN_ERR "via_get_cache_disable called on a non-VIA machine!\n"); | ||
360 | return 1; | ||
361 | } | ||
362 | |||
363 | return (int) via2[gBufB] & VIA2B_vCDis; | ||
364 | } | ||
365 | |||
366 | /* | ||
367 | * Initialize VIA2 for Nubus access | ||
368 | */ | ||
369 | |||
370 | void __init via_nubus_init(void) | ||
371 | { | ||
372 | /* don't set nubus_active = 0 here, it kills the Baboon */ | ||
373 | /* interrupt that we've already registered. */ | ||
374 | |||
375 | /* unlock nubus transactions */ | ||
376 | |||
377 | if (!rbv_present) { | ||
378 | /* set the line to be an output on non-RBV machines */ | ||
379 | if ((macintosh_config->adb_type != MAC_ADB_PB1) && | ||
380 | (macintosh_config->adb_type != MAC_ADB_PB2)) { | ||
381 | via2[vDirB] |= 0x02; | ||
382 | } | ||
383 | } | ||
384 | |||
385 | /* this seems to be an ADB bit on PMU machines */ | ||
386 | /* according to MkLinux. -- jmt */ | ||
387 | |||
388 | if ((macintosh_config->adb_type != MAC_ADB_PB1) && | ||
389 | (macintosh_config->adb_type != MAC_ADB_PB2)) { | ||
390 | via2[gBufB] |= 0x02; | ||
391 | } | ||
392 | |||
393 | /* disable nubus slot interrupts. */ | ||
394 | if (rbv_present) { | ||
395 | via2[rSIER] = 0x7F; | ||
396 | via2[rSIER] = nubus_active | 0x80; | ||
397 | } else { | ||
398 | /* These are ADB bits on PMU */ | ||
399 | if ((macintosh_config->adb_type != MAC_ADB_PB1) && | ||
400 | (macintosh_config->adb_type != MAC_ADB_PB2)) { | ||
401 | switch(macintosh_config->ident) | ||
402 | { | ||
403 | case MAC_MODEL_II: | ||
404 | case MAC_MODEL_IIX: | ||
405 | case MAC_MODEL_IICX: | ||
406 | case MAC_MODEL_SE30: | ||
407 | via2[vBufA] |= 0x3F; | ||
408 | via2[vDirA] = ~nubus_active | 0xc0; | ||
409 | break; | ||
410 | default: | ||
411 | via2[vBufA] = 0xFF; | ||
412 | via2[vDirA] = ~nubus_active; | ||
413 | } | ||
414 | } | ||
415 | } | ||
416 | } | ||
417 | |||
418 | /* | ||
419 | * The generic VIA interrupt routines (shamelessly stolen from Alan Cox's | ||
420 | * via6522.c :-), disable/pending masks added. | ||
421 | * | ||
422 | * The new interrupt architecture in macints.c takes care of a lot of the | ||
423 | * gruntwork for us, including tallying the interrupts and calling the | ||
424 | * handlers on the linked list. All we need to do here is basically generate | ||
425 | * the machspec interrupt number after clearing the interrupt. | ||
426 | */ | ||
427 | |||
428 | irqreturn_t via1_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
429 | { | ||
430 | int irq_bit, i; | ||
431 | unsigned char events, mask; | ||
432 | |||
433 | mask = via1[vIER] & 0x7F; | ||
434 | if (!(events = via1[vIFR] & mask)) | ||
435 | return IRQ_NONE; | ||
436 | |||
437 | for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1) | ||
438 | if (events & irq_bit) { | ||
439 | via1[vIER] = irq_bit; | ||
440 | mac_do_irq_list(VIA1_SOURCE_BASE + i, regs); | ||
441 | via1[vIFR] = irq_bit; | ||
442 | via1[vIER] = irq_bit | 0x80; | ||
443 | } | ||
444 | |||
445 | #if 0 /* freakin' pmu is doing weird stuff */ | ||
446 | if (!oss_present) { | ||
447 | /* This (still) seems to be necessary to get IDE | ||
448 | working. However, if you enable VBL interrupts, | ||
449 | you're screwed... */ | ||
450 | /* FIXME: should we check the SLOTIRQ bit before | ||
451 | pulling this stunt? */ | ||
452 | /* No, it won't be set. that's why we're doing this. */ | ||
453 | via_irq_disable(IRQ_MAC_NUBUS); | ||
454 | via_irq_clear(IRQ_MAC_NUBUS); | ||
455 | mac_do_irq_list(IRQ_MAC_NUBUS, regs); | ||
456 | via_irq_enable(IRQ_MAC_NUBUS); | ||
457 | } | ||
458 | #endif | ||
459 | return IRQ_HANDLED; | ||
460 | } | ||
461 | |||
462 | irqreturn_t via2_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
463 | { | ||
464 | int irq_bit, i; | ||
465 | unsigned char events, mask; | ||
466 | |||
467 | mask = via2[gIER] & 0x7F; | ||
468 | if (!(events = via2[gIFR] & mask)) | ||
469 | return IRQ_NONE; | ||
470 | |||
471 | for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1) | ||
472 | if (events & irq_bit) { | ||
473 | via2[gIER] = irq_bit; | ||
474 | mac_do_irq_list(VIA2_SOURCE_BASE + i, regs); | ||
475 | via2[gIFR] = irq_bit | rbv_clear; | ||
476 | via2[gIER] = irq_bit | 0x80; | ||
477 | } | ||
478 | return IRQ_HANDLED; | ||
479 | } | ||
480 | |||
481 | /* | ||
482 | * Dispatch Nubus interrupts. We are called as a secondary dispatch by the | ||
483 | * VIA2 dispatcher as a fast interrupt handler. | ||
484 | */ | ||
485 | |||
486 | irqreturn_t via_nubus_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
487 | { | ||
488 | int irq_bit, i; | ||
489 | unsigned char events; | ||
490 | |||
491 | if (!(events = ~via2[gBufA] & nubus_active)) | ||
492 | return IRQ_NONE; | ||
493 | |||
494 | for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1) { | ||
495 | if (events & irq_bit) { | ||
496 | via_irq_disable(NUBUS_SOURCE_BASE + i); | ||
497 | mac_do_irq_list(NUBUS_SOURCE_BASE + i, regs); | ||
498 | via_irq_enable(NUBUS_SOURCE_BASE + i); | ||
499 | } | ||
500 | } | ||
501 | return IRQ_HANDLED; | ||
502 | } | ||
503 | |||
504 | void via_irq_enable(int irq) { | ||
505 | int irq_src = IRQ_SRC(irq); | ||
506 | int irq_idx = IRQ_IDX(irq); | ||
507 | int irq_bit = 1 << irq_idx; | ||
508 | |||
509 | #ifdef DEBUG_IRQUSE | ||
510 | printk(KERN_DEBUG "via_irq_enable(%d)\n", irq); | ||
511 | #endif | ||
512 | |||
513 | if (irq_src == 1) { | ||
514 | via1[vIER] = irq_bit | 0x80; | ||
515 | } else if (irq_src == 2) { | ||
516 | /* | ||
517 | * Set vPCR for SCSI interrupts (but not on RBV) | ||
518 | */ | ||
519 | if ((irq_idx == 0) && !rbv_present) { | ||
520 | if (macintosh_config->scsi_type == MAC_SCSI_OLD) { | ||
521 | /* CB2 (IRQ) indep. input, positive edge */ | ||
522 | /* CA2 (DRQ) indep. input, positive edge */ | ||
523 | via2[vPCR] = 0x66; | ||
524 | } else { | ||
525 | /* CB2 (IRQ) indep. input, negative edge */ | ||
526 | /* CA2 (DRQ) indep. input, negative edge */ | ||
527 | via2[vPCR] = 0x22; | ||
528 | } | ||
529 | } | ||
530 | via2[gIER] = irq_bit | 0x80; | ||
531 | } else if (irq_src == 7) { | ||
532 | if (rbv_present) { | ||
533 | /* enable the slot interrupt. SIER works like IER. */ | ||
534 | via2[rSIER] = IER_SET_BIT(irq_idx); | ||
535 | } else { | ||
536 | /* Make sure the bit is an input, to enable the irq */ | ||
537 | /* But not on PowerBooks, that's ADB... */ | ||
538 | if ((macintosh_config->adb_type != MAC_ADB_PB1) && | ||
539 | (macintosh_config->adb_type != MAC_ADB_PB2)) { | ||
540 | switch(macintosh_config->ident) | ||
541 | { | ||
542 | case MAC_MODEL_II: | ||
543 | case MAC_MODEL_IIX: | ||
544 | case MAC_MODEL_IICX: | ||
545 | case MAC_MODEL_SE30: | ||
546 | via2[vDirA] &= (~irq_bit | 0xc0); | ||
547 | break; | ||
548 | default: | ||
549 | via2[vDirA] &= ~irq_bit; | ||
550 | } | ||
551 | } | ||
552 | } | ||
553 | nubus_active |= irq_bit; | ||
554 | } | ||
555 | } | ||
556 | |||
557 | void via_irq_disable(int irq) { | ||
558 | int irq_src = IRQ_SRC(irq); | ||
559 | int irq_idx = IRQ_IDX(irq); | ||
560 | int irq_bit = 1 << irq_idx; | ||
561 | |||
562 | #ifdef DEBUG_IRQUSE | ||
563 | printk(KERN_DEBUG "via_irq_disable(%d)\n", irq); | ||
564 | #endif | ||
565 | |||
566 | if (irq_src == 1) { | ||
567 | via1[vIER] = irq_bit; | ||
568 | } else if (irq_src == 2) { | ||
569 | via2[gIER] = irq_bit; | ||
570 | } else if (irq_src == 7) { | ||
571 | if (rbv_present) { | ||
572 | /* disable the slot interrupt. SIER works like IER. */ | ||
573 | via2[rSIER] = IER_CLR_BIT(irq_idx); | ||
574 | } else { | ||
575 | /* disable the nubus irq by changing dir to output */ | ||
576 | /* except on PMU */ | ||
577 | if ((macintosh_config->adb_type != MAC_ADB_PB1) && | ||
578 | (macintosh_config->adb_type != MAC_ADB_PB2)) { | ||
579 | via2[vDirA] |= irq_bit; | ||
580 | } | ||
581 | } | ||
582 | nubus_active &= ~irq_bit; | ||
583 | } | ||
584 | } | ||
585 | |||
586 | void via_irq_clear(int irq) { | ||
587 | int irq_src = IRQ_SRC(irq); | ||
588 | int irq_idx = IRQ_IDX(irq); | ||
589 | int irq_bit = 1 << irq_idx; | ||
590 | |||
591 | if (irq_src == 1) { | ||
592 | via1[vIFR] = irq_bit; | ||
593 | } else if (irq_src == 2) { | ||
594 | via2[gIFR] = irq_bit | rbv_clear; | ||
595 | } else if (irq_src == 7) { | ||
596 | /* FIXME: hmm.. */ | ||
597 | } | ||
598 | } | ||
599 | |||
600 | /* | ||
601 | * Returns nonzero if an interrupt is pending on the given | ||
602 | * VIA/IRQ combination. | ||
603 | */ | ||
604 | |||
605 | int via_irq_pending(int irq) | ||
606 | { | ||
607 | int irq_src = IRQ_SRC(irq); | ||
608 | int irq_idx = IRQ_IDX(irq); | ||
609 | int irq_bit = 1 << irq_idx; | ||
610 | |||
611 | if (irq_src == 1) { | ||
612 | return via1[vIFR] & irq_bit; | ||
613 | } else if (irq_src == 2) { | ||
614 | return via2[gIFR] & irq_bit; | ||
615 | } else if (irq_src == 7) { | ||
616 | return ~via2[gBufA] & irq_bit; | ||
617 | } | ||
618 | return 0; | ||
619 | } | ||