diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-17 00:20:36 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-17 00:20:36 +0200 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/m68k/mac | |
download | linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/m68k/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 @@ +# +# Makefile for Linux arch/m68k/mac source directory +# + +obj-y := config.o bootparse.o macints.o iop.o via.o oss.o psc.o \ + 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 @@ +/* + * Baboon Custom IC Management + * + * The Baboon custom IC controls the IDE, PCMCIA and media bay on the + * PowerBook 190. It multiplexes multiple interrupt sources onto the + * Nubus slot $C interrupt. + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/ide.h> + +#include <asm/traps.h> +#include <asm/bootinfo.h> +#include <asm/macintosh.h> +#include <asm/macints.h> +#include <asm/mac_baboon.h> + +/* #define DEBUG_BABOON */ +/* #define DEBUG_IRQS */ + +int baboon_present,baboon_active; +volatile struct baboon *baboon; + +irqreturn_t baboon_irq(int, void *, struct pt_regs *); + +#if 0 +extern int macide_ack_intr(struct ata_channel *); +#endif + +/* + * Baboon initialization. + */ + +void __init baboon_init(void) +{ + if (macintosh_config->ident != MAC_MODEL_PB190) { + baboon = NULL; + baboon_present = 0; + return; + } + + baboon = (struct baboon *) BABOON_BASE; + baboon_present = 1; + baboon_active = 0; + + printk("Baboon detected at %p\n", baboon); +} + +/* + * Register the Baboon interrupt dispatcher on nubus slot $C. + */ + +void __init baboon_register_interrupts(void) +{ + request_irq(IRQ_NUBUS_C, baboon_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, + "baboon", (void *) baboon); +} + +/* + * Baboon interrupt handler. This works a lot like a VIA. + */ + +irqreturn_t baboon_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int irq_bit,i; + unsigned char events; + +#ifdef DEBUG_IRQS + printk("baboon_irq: mb_control %02X mb_ifr %02X mb_status %02X active %02X\n", + (uint) baboon->mb_control, (uint) baboon->mb_ifr, + (uint) baboon->mb_status, baboon_active); +#endif + + if (!(events = baboon->mb_ifr & 0x07)) + return IRQ_NONE; + + for (i = 0, irq_bit = 1 ; i < 3 ; i++, irq_bit <<= 1) { + if (events & irq_bit/* & baboon_active*/) { + baboon_active &= ~irq_bit; + mac_do_irq_list(IRQ_BABOON_0 + i, regs); + baboon_active |= irq_bit; + baboon->mb_ifr &= ~irq_bit; + } + } +#if 0 + if (baboon->mb_ifr & 0x02) macide_ack_intr(NULL); + /* for now we need to smash all interrupts */ + baboon->mb_ifr &= ~events; +#endif + return IRQ_HANDLED; +} + +void baboon_irq_enable(int irq) { + int irq_idx = IRQ_IDX(irq); + +#ifdef DEBUG_IRQUSE + printk("baboon_irq_enable(%d)\n", irq); +#endif + baboon_active |= (1 << irq_idx); +} + +void baboon_irq_disable(int irq) { + int irq_idx = IRQ_IDX(irq); + +#ifdef DEBUG_IRQUSE + printk("baboon_irq_disable(%d)\n", irq); +#endif + baboon_active &= ~(1 << irq_idx); +} + +void baboon_irq_clear(int irq) { + int irq_idx = IRQ_IDX(irq); + + baboon->mb_ifr &= ~(1 << irq_idx); +} + +int baboon_irq_pending(int irq) +{ + int irq_idx = IRQ_IDX(irq); + + return baboon->mb_ifr & (1 << irq_idx); +} 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 @@ +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <asm/irq.h> +#include <asm/setup.h> +#include <asm/bootinfo.h> +#include <asm/macintosh.h> + +/* + * Booter vars + */ + +int boothowto; +int _boothowto; + +/* + * Called early to parse the environment (passed to us from the booter) + * into a bootinfo struct. Will die as soon as we have our own booter + */ + +#define atol(x) simple_strtoul(x,NULL,0) + +void parse_booter(char *env) +{ + char *name; + char *value; +#if 0 + while(0 && *env) +#else + while(*env) +#endif + { + name=env; + value=name; + while(*value!='='&&*value) + value++; + if(*value=='=') + *value++=0; + env=value; + while(*env) + env++; + env++; +#if 0 + if(strcmp(name,"VIDEO_ADDR")==0) + mac_mch.videoaddr=atol(value); + if(strcmp(name,"ROW_BYTES")==0) + mac_mch.videorow=atol(value); + if(strcmp(name,"SCREEN_DEPTH")==0) + mac_mch.videodepth=atol(value); + if(strcmp(name,"DIMENSIONS")==0) + mac_mch.dimensions=atol(value); +#endif + if(strcmp(name,"BOOTTIME")==0) + mac_bi_data.boottime=atol(value); + if(strcmp(name,"GMTBIAS")==0) + mac_bi_data.gmtbias=atol(value); + if(strcmp(name,"BOOTERVER")==0) + mac_bi_data.bootver=atol(value); + if(strcmp(name,"MACOS_VIDEO")==0) + mac_bi_data.videological=atol(value); + if(strcmp(name,"MACOS_SCC")==0) + mac_bi_data.sccbase=atol(value); + if(strcmp(name,"MACHINEID")==0) + mac_bi_data.id=atol(value); + if(strcmp(name,"MEMSIZE")==0) + mac_bi_data.memsize=atol(value); + if(strcmp(name,"SERIAL_MODEM_FLAGS")==0) + mac_bi_data.serialmf=atol(value); + if(strcmp(name,"SERIAL_MODEM_HSKICLK")==0) + mac_bi_data.serialhsk=atol(value); + if(strcmp(name,"SERIAL_MODEM_GPICLK")==0) + mac_bi_data.serialgpi=atol(value); + if(strcmp(name,"SERIAL_PRINT_FLAGS")==0) + mac_bi_data.printmf=atol(value); + if(strcmp(name,"SERIAL_PRINT_HSKICLK")==0) + mac_bi_data.printhsk=atol(value); + if(strcmp(name,"SERIAL_PRINT_GPICLK")==0) + mac_bi_data.printgpi=atol(value); + if(strcmp(name,"PROCESSOR")==0) + mac_bi_data.cpuid=atol(value); + if(strcmp(name,"ROMBASE")==0) + mac_bi_data.rombase=atol(value); + if(strcmp(name,"TIMEDBRA")==0) + mac_bi_data.timedbra=atol(value); + if(strcmp(name,"ADBDELAY")==0) + mac_bi_data.adbdelay=atol(value); + } +#if 0 /* XXX: TODO with m68k_mach_* */ + /* Fill in the base stuff */ + boot_info.machtype=MACH_MAC; + /* Read this from the macinfo we got ! */ +/* boot_info.cputype=CPU_68020|FPUB_68881;*/ +/* boot_info.memory[0].addr=0;*/ +/* boot_info.memory[0].size=((mac_bi_data.id>>7)&31)<<20;*/ + boot_info.num_memory=1; /* On a MacII */ + boot_info.ramdisk_size=0; /* For now */ + *boot_info.command_line=0; +#endif + } + + +void print_booter(char *env) +{ + char *name; + char *value; + while(*env) + { + name=env; + value=name; + while(*value!='='&&*value) + value++; + if(*value=='=') + *value++=0; + env=value; + while(*env) + env++; + env++; + printk("%s=%s\n", name,value); + } + } + + 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 @@ +/* + * linux/arch/m68k/mac/config.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +/* + * Miscellaneous linux stuff + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/interrupt.h> +/* keyb */ +#include <linux/random.h> +#include <linux/delay.h> +/* keyb */ +#include <linux/init.h> +#include <linux/vt_kern.h> + +#define BOOTINFO_COMPAT_1_0 +#include <asm/setup.h> +#include <asm/bootinfo.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/pgtable.h> +#include <asm/rtc.h> +#include <asm/machdep.h> + +#include <asm/macintosh.h> +#include <asm/macints.h> +#include <asm/machw.h> + +#include <asm/mac_iop.h> +#include <asm/mac_via.h> +#include <asm/mac_oss.h> +#include <asm/mac_psc.h> + +/* Mac bootinfo struct */ + +struct mac_booter_data mac_bi_data; +int mac_bisize = sizeof mac_bi_data; + +struct mac_hw_present mac_hw_present; + +/* New m68k bootinfo stuff and videobase */ + +extern int m68k_num_memory; +extern struct mem_info m68k_memory[NUM_MEMINFO]; + +extern struct mem_info m68k_ramdisk; + +extern char m68k_command_line[CL_SIZE]; + +void *mac_env; /* Loaded by the boot asm */ + +/* The phys. video addr. - might be bogus on some machines */ +unsigned long mac_orig_videoaddr; + +/* Mac specific timer functions */ +extern unsigned long mac_gettimeoffset (void); +extern int mac_hwclk (int, struct rtc_time *); +extern int mac_set_clock_mmss (unsigned long); +extern int show_mac_interrupts(struct seq_file *, void *); +extern void iop_preinit(void); +extern void iop_init(void); +extern void via_init(void); +extern void via_init_clock(irqreturn_t (*func)(int, void *, struct pt_regs *)); +extern void via_flush_cache(void); +extern void oss_init(void); +extern void psc_init(void); +extern void baboon_init(void); + +extern void mac_mksound(unsigned int, unsigned int); + +extern void nubus_sweep_video(void); + +/* Mac specific debug functions (in debug.c) */ +extern void mac_debug_init(void); +extern void mac_debugging_long(int, long); + +static void mac_get_model(char *str); + +void mac_bang(int irq, void *vector, struct pt_regs *p) +{ + printk(KERN_INFO "Resetting ...\n"); + mac_reset(); +} + +static void mac_sched_init(irqreturn_t (*vector)(int, void *, struct pt_regs *)) +{ + via_init_clock(vector); +} + +#if 0 +void mac_waitbut (void) +{ + ; +} +#endif + +extern irqreturn_t mac_default_handler(int, void *, struct pt_regs *); + +irqreturn_t (*mac_handlers[8])(int, void *, struct pt_regs *)= +{ + mac_default_handler, + mac_default_handler, + mac_default_handler, + mac_default_handler, + mac_default_handler, + mac_default_handler, + mac_default_handler, + mac_default_handler +}; + +/* + * Parse a Macintosh-specific record in the bootinfo + */ + +int __init mac_parse_bootinfo(const struct bi_record *record) +{ + int unknown = 0; + const u_long *data = record->data; + + switch (record->tag) { + case BI_MAC_MODEL: + mac_bi_data.id = *data; + break; + case BI_MAC_VADDR: + mac_bi_data.videoaddr = *data; + break; + case BI_MAC_VDEPTH: + mac_bi_data.videodepth = *data; + break; + case BI_MAC_VROW: + mac_bi_data.videorow = *data; + break; + case BI_MAC_VDIM: + mac_bi_data.dimensions = *data; + break; + case BI_MAC_VLOGICAL: + mac_bi_data.videological = VIDEOMEMBASE + (*data & ~VIDEOMEMMASK); + mac_orig_videoaddr = *data; + break; + case BI_MAC_SCCBASE: + mac_bi_data.sccbase = *data; + break; + case BI_MAC_BTIME: + mac_bi_data.boottime = *data; + break; + case BI_MAC_GMTBIAS: + mac_bi_data.gmtbias = *data; + break; + case BI_MAC_MEMSIZE: + mac_bi_data.memsize = *data; + break; + case BI_MAC_CPUID: + mac_bi_data.cpuid = *data; + break; + case BI_MAC_ROMBASE: + mac_bi_data.rombase = *data; + break; + default: + unknown = 1; + } + return(unknown); +} + +/* + * Flip into 24bit mode for an instant - flushes the L2 cache card. We + * have to disable interrupts for this. Our IRQ handlers will crap + * themselves if they take an IRQ in 24bit mode! + */ + +static void mac_cache_card_flush(int writeback) +{ + unsigned long flags; + local_irq_save(flags); + via_flush_cache(); + local_irq_restore(flags); +} + +void __init config_mac(void) +{ + if (!MACH_IS_MAC) { + printk(KERN_ERR "ERROR: no Mac, but config_mac() called!! \n"); + } + + mach_sched_init = mac_sched_init; + mach_init_IRQ = mac_init_IRQ; + mach_request_irq = mac_request_irq; + mach_free_irq = mac_free_irq; + enable_irq = mac_enable_irq; + disable_irq = mac_disable_irq; + mach_get_model = mac_get_model; + mach_default_handler = &mac_handlers; + mach_get_irq_list = show_mac_interrupts; + mach_gettimeoffset = mac_gettimeoffset; +#warning move to adb/via init +#if 0 + mach_hwclk = mac_hwclk; +#endif + mach_set_clock_mmss = mac_set_clock_mmss; + mach_reset = mac_reset; + mach_halt = mac_poweroff; + mach_power_off = mac_poweroff; +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + mach_max_dma_address = 0xffffffff; +#if 0 + mach_debug_init = mac_debug_init; +#endif +#if defined(CONFIG_INPUT_M68K_BEEP) || defined(CONFIG_INPUT_M68K_BEEP_MODULE) + mach_beep = mac_mksound; +#endif +#ifdef CONFIG_HEARTBEAT +#if 0 + mach_heartbeat = mac_heartbeat; + mach_heartbeat_irq = IRQ_MAC_TIMER; +#endif +#endif + + /* + * Determine hardware present + */ + + mac_identify(); + mac_report_hardware(); + + /* AFAIK only the IIci takes a cache card. The IIfx has onboard + cache ... someone needs to figure out how to tell if it's on or + not. */ + + if (macintosh_config->ident == MAC_MODEL_IICI + || macintosh_config->ident == MAC_MODEL_IIFX) { + mach_l2_flush = mac_cache_card_flush; + } + + /* + * Check for machine specific fixups. + */ + +#ifdef OLD_NUBUS_CODE + nubus_sweep_video(); +#endif +} + + +/* + * Macintosh Table: hardcoded model configuration data. + * + * Much of this was defined by Alan, based on who knows what docs. + * I've added a lot more, and some of that was pure guesswork based + * on hardware pages present on the Mac web site. Possibly wildly + * inaccurate, so look here if a new Mac model won't run. Example: if + * a Mac crashes immediately after the VIA1 registers have been dumped + * to the screen, it probably died attempting to read DirB on a RBV. + * Meaning it should have MAC_VIA_IIci here :-) + */ + +struct mac_model *macintosh_config; +EXPORT_SYMBOL(macintosh_config); + +static struct mac_model mac_data_table[]= +{ + /* + * We'll pretend to be a Macintosh II, that's pretty safe. + */ + + { + .ident = MAC_MODEL_II, + .name = "Unknown", + .adb_type = MAC_ADB_II, + .via_type = MAC_VIA_II, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, + + /* + * Original MacII hardware + * + */ + + { + .ident = MAC_MODEL_II, + .name = "II", + .adb_type = MAC_ADB_II, + .via_type = MAC_VIA_II, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_IIX, + .name = "IIx", + .adb_type = MAC_ADB_II, + .via_type = MAC_VIA_II, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_IICX, + .name = "IIcx", + .adb_type = MAC_ADB_II, + .via_type = MAC_VIA_II, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_SE30, + .name = "SE/30", + .adb_type = MAC_ADB_II, + .via_type = MAC_VIA_II, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, + + /* + * Weirdified MacII hardware - all subtley different. Gee thanks + * Apple. All these boxes seem to have VIA2 in a different place to + * the MacII (+1A000 rather than +4000) + * CSA: see http://developer.apple.com/technotes/hw/hw_09.html + */ + + { + .ident = MAC_MODEL_IICI, + .name = "IIci", + .adb_type = MAC_ADB_II, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_IIFX, + .name = "IIfx", + .adb_type = MAC_ADB_IOP, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_IOP, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_IISI, + .name = "IIsi", + .adb_type = MAC_ADB_IISI, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_IIVI, + .name = "IIvi", + .adb_type = MAC_ADB_IISI, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_IIVX, + .name = "IIvx", + .adb_type = MAC_ADB_IISI, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, + + /* + * Classic models (guessing: similar to SE/30 ?? Nope, similar to LC ...) + */ + + { + .ident = MAC_MODEL_CLII, + .name = "Classic II", + .adb_type = MAC_ADB_IISI, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_CCL, + .name = "Color Classic", + .adb_type = MAC_ADB_CUDA, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS}, + + /* + * Some Mac LC machines. Basically the same as the IIci, ADB like IIsi + */ + + { + .ident = MAC_MODEL_LC, + .name = "LC", + .adb_type = MAC_ADB_IISI, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_LCII, + .name = "LC II", + .adb_type = MAC_ADB_IISI, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_LCIII, + .name = "LC III", + .adb_type = MAC_ADB_IISI, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, + + /* + * Quadra. Video is at 0xF9000000, via is like a MacII. We label it differently + * as some of the stuff connected to VIA2 seems different. Better SCSI chip and + * onboard ethernet using a NatSemi SONIC except the 660AV and 840AV which use an + * AMD 79C940 (MACE). + * The 700, 900 and 950 have some I/O chips in the wrong place to + * confuse us. The 840AV has a SCSI location of its own (same as + * the 660AV). + */ + + { + .ident = MAC_MODEL_Q605, + .name = "Quadra 605", + .adb_type = MAC_ADB_CUDA, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_Q605_ACC, + .name = "Quadra 605", + .adb_type = MAC_ADB_CUDA, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_Q610, + .name = "Quadra 610", + .adb_type = MAC_ADB_II, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA, + .scc_type = MAC_SCC_QUADRA, + .ether_type = MAC_ETHER_SONIC, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_Q630, + .name = "Quadra 630", + .adb_type = MAC_ADB_CUDA, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA, + .ide_type = MAC_IDE_QUADRA, + .scc_type = MAC_SCC_QUADRA, + .ether_type = MAC_ETHER_SONIC, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_Q650, + .name = "Quadra 650", + .adb_type = MAC_ADB_II, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA, + .scc_type = MAC_SCC_QUADRA, + .ether_type = MAC_ETHER_SONIC, + .nubus_type = MAC_NUBUS + }, + /* The Q700 does have a NS Sonic */ + { + .ident = MAC_MODEL_Q700, + .name = "Quadra 700", + .adb_type = MAC_ADB_II, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA2, + .scc_type = MAC_SCC_QUADRA, + .ether_type = MAC_ETHER_SONIC, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_Q800, + .name = "Quadra 800", + .adb_type = MAC_ADB_II, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA, + .scc_type = MAC_SCC_QUADRA, + .ether_type = MAC_ETHER_SONIC, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_Q840, + .name = "Quadra 840AV", + .adb_type = MAC_ADB_CUDA, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA3, + .scc_type = MAC_SCC_PSC, + .ether_type = MAC_ETHER_MACE, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_Q900, + .name = "Quadra 900", + .adb_type = MAC_ADB_IOP, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA2, + .scc_type = MAC_SCC_IOP, + .ether_type = MAC_ETHER_SONIC, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_Q950, + .name = "Quadra 950", + .adb_type = MAC_ADB_IOP, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA2, + .scc_type = MAC_SCC_IOP, + .ether_type = MAC_ETHER_SONIC, + .nubus_type = MAC_NUBUS + }, + + /* + * Performa - more LC type machines + */ + + { + .ident = MAC_MODEL_P460, + .name = "Performa 460", + .adb_type = MAC_ADB_IISI, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_P475, + .name = "Performa 475", + .adb_type = MAC_ADB_CUDA, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_P475F, + .name = "Performa 475", + .adb_type = MAC_ADB_CUDA, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_P520, + .name = "Performa 520", + .adb_type = MAC_ADB_CUDA, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_P550, + .name = "Performa 550", + .adb_type = MAC_ADB_CUDA, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, + /* These have the comm slot, and therefore the possibility of SONIC ethernet */ + { + .ident = MAC_MODEL_P575, + .name = "Performa 575", + .adb_type = MAC_ADB_CUDA, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA, + .scc_type = MAC_SCC_II, + .ether_type = MAC_ETHER_SONIC, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_P588, + .name = "Performa 588", + .adb_type = MAC_ADB_CUDA, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA, + .ide_type = MAC_IDE_QUADRA, + .scc_type = MAC_SCC_II, + .ether_type = MAC_ETHER_SONIC, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_TV, + .name = "TV", + .adb_type = MAC_ADB_CUDA, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_P600, + .name = "Performa 600", + .adb_type = MAC_ADB_IISI, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_II, + .nubus_type = MAC_NUBUS + }, + + /* + * Centris - just guessing again; maybe like Quadra + */ + + /* The C610 may or may not have SONIC. We probe to make sure */ + { + .ident = MAC_MODEL_C610, + .name = "Centris 610", + .adb_type = MAC_ADB_II, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA, + .scc_type = MAC_SCC_QUADRA, + .ether_type = MAC_ETHER_SONIC, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_C650, + .name = "Centris 650", + .adb_type = MAC_ADB_II, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA, + .scc_type = MAC_SCC_QUADRA, + .ether_type = MAC_ETHER_SONIC, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_C660, + .name = "Centris 660AV", + .adb_type = MAC_ADB_CUDA, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_QUADRA3, + .scc_type = MAC_SCC_PSC, + .ether_type = MAC_ETHER_MACE, + .nubus_type = MAC_NUBUS + }, + + /* + * The PowerBooks all the same "Combo" custom IC for SCSI and SCC + * and a PMU (in two variations?) for ADB. Most of them use the + * Quadra-style VIAs. A few models also have IDE from hell. + */ + + { + .ident = MAC_MODEL_PB140, + .name = "PowerBook 140", + .adb_type = MAC_ADB_PB1, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_PB145, + .name = "PowerBook 145", + .adb_type = MAC_ADB_PB1, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_PB150, + .name = "PowerBook 150", + .adb_type = MAC_ADB_PB1, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .ide_type = MAC_IDE_PB, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_PB160, + .name = "PowerBook 160", + .adb_type = MAC_ADB_PB1, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_PB165, + .name = "PowerBook 165", + .adb_type = MAC_ADB_PB1, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_PB165C, + .name = "PowerBook 165c", + .adb_type = MAC_ADB_PB1, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_PB170, + .name = "PowerBook 170", + .adb_type = MAC_ADB_PB1, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_PB180, + .name = "PowerBook 180", + .adb_type = MAC_ADB_PB1, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_PB180C, + .name = "PowerBook 180c", + .adb_type = MAC_ADB_PB1, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_PB190, + .name = "PowerBook 190", + .adb_type = MAC_ADB_PB2, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_OLD, + .ide_type = MAC_IDE_BABOON, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_PB520, + .name = "PowerBook 520", + .adb_type = MAC_ADB_PB2, + .via_type = MAC_VIA_QUADRA, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_QUADRA, + .ether_type = MAC_ETHER_SONIC, + .nubus_type = MAC_NUBUS + }, + + /* + * PowerBook Duos are pretty much like normal PowerBooks + * All of these probably have onboard SONIC in the Dock which + * means we'll have to probe for it eventually. + * + * Are these reallly MAC_VIA_IIci? The developer notes for the + * Duos show pretty much the same custom parts as in most of + * the other PowerBooks which would imply MAC_VIA_QUADRA. + */ + + { + .ident = MAC_MODEL_PB210, + .name = "PowerBook Duo 210", + .adb_type = MAC_ADB_PB2, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_PB230, + .name = "PowerBook Duo 230", + .adb_type = MAC_ADB_PB2, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_PB250, + .name = "PowerBook Duo 250", + .adb_type = MAC_ADB_PB2, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_PB270C, + .name = "PowerBook Duo 270c", + .adb_type = MAC_ADB_PB2, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_PB280, + .name = "PowerBook Duo 280", + .adb_type = MAC_ADB_PB2, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, { + .ident = MAC_MODEL_PB280C, + .name = "PowerBook Duo 280c", + .adb_type = MAC_ADB_PB2, + .via_type = MAC_VIA_IIci, + .scsi_type = MAC_SCSI_OLD, + .scc_type = MAC_SCC_QUADRA, + .nubus_type = MAC_NUBUS + }, + + /* + * Other stuff ?? + */ + { + .ident = -1 + } +}; + +void mac_identify(void) +{ + struct mac_model *m; + + /* Penguin data useful? */ + int model = mac_bi_data.id; + if (!model) { + /* no bootinfo model id -> NetBSD booter was used! */ + /* XXX FIXME: breaks for model > 31 */ + model=(mac_bi_data.cpuid>>2)&63; + printk (KERN_WARNING "No bootinfo model ID, using cpuid instead (hey, use Penguin!)\n"); + } + + macintosh_config = mac_data_table; + for (m = macintosh_config ; m->ident != -1 ; m++) { + if (m->ident == model) { + macintosh_config = m; + break; + } + } + + /* We need to pre-init the IOPs, if any. Otherwise */ + /* the serial console won't work if the user had */ + /* the serial ports set to "Faster" mode in MacOS. */ + + iop_preinit(); + mac_debug_init(); + + printk (KERN_INFO "Detected Macintosh model: %d \n", model); + + /* + * Report booter data: + */ + printk (KERN_DEBUG " Penguin bootinfo data:\n"); + printk (KERN_DEBUG " Video: addr 0x%lx row 0x%lx depth %lx dimensions %ld x %ld\n", + mac_bi_data.videoaddr, mac_bi_data.videorow, + mac_bi_data.videodepth, mac_bi_data.dimensions & 0xFFFF, + mac_bi_data.dimensions >> 16); + printk (KERN_DEBUG " Videological 0x%lx phys. 0x%lx, SCC at 0x%lx \n", + mac_bi_data.videological, mac_orig_videoaddr, + mac_bi_data.sccbase); + printk (KERN_DEBUG " Boottime: 0x%lx GMTBias: 0x%lx \n", + mac_bi_data.boottime, mac_bi_data.gmtbias); + printk (KERN_DEBUG " Machine ID: %ld CPUid: 0x%lx memory size: 0x%lx \n", + mac_bi_data.id, mac_bi_data.cpuid, mac_bi_data.memsize); +#if 0 + printk ("Ramdisk: addr 0x%lx size 0x%lx\n", + m68k_ramdisk.addr, m68k_ramdisk.size); +#endif + + /* + * TODO: set the various fields in macintosh_config->hw_present here! + */ + switch (macintosh_config->scsi_type) { + case MAC_SCSI_OLD: + MACHW_SET(MAC_SCSI_80); + break; + case MAC_SCSI_QUADRA: + case MAC_SCSI_QUADRA2: + case MAC_SCSI_QUADRA3: + MACHW_SET(MAC_SCSI_96); + if ((macintosh_config->ident == MAC_MODEL_Q900) || + (macintosh_config->ident == MAC_MODEL_Q950)) + MACHW_SET(MAC_SCSI_96_2); + break; + default: + printk(KERN_WARNING "config.c: wtf: unknown scsi, using 53c80\n"); + MACHW_SET(MAC_SCSI_80); + break; + + } + iop_init(); + via_init(); + oss_init(); + psc_init(); + baboon_init(); +} + +void mac_report_hardware(void) +{ + printk(KERN_INFO "Apple Macintosh %s\n", macintosh_config->name); +} + +static void mac_get_model(char *str) +{ + strcpy(str,"Macintosh "); + strcat(str, macintosh_config->name); +} 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 @@ +/* + * linux/arch/m68k/mac/debug.c + * + * Shamelessly stolen (SCC code and general framework) from: + * + * linux/arch/m68k/atari/debug.c + * + * Atari debugging and serial console stuff + * + * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/delay.h> + +#define BOOTINFO_COMPAT_1_0 +#include <asm/setup.h> +#include <asm/bootinfo.h> +#include <asm/machw.h> +#include <asm/macints.h> + +extern char m68k_debug_device[]; + +extern struct compat_bootinfo compat_boot_info; + +extern unsigned long mac_videobase; +extern unsigned long mac_videodepth; +extern unsigned long mac_rowbytes; + +extern void mac_serial_print(const char *); + +#define DEBUG_HEADS +#undef DEBUG_SCREEN +#define DEBUG_SERIAL + +/* + * These two auxiliary debug functions should go away ASAP. Only usage: + * before the console output is up (after head.S come some other crucial + * setup routines :-) it permits writing 'data' to the screen as bit patterns + * (good luck reading those). Helped to figure that the bootinfo contained + * garbage data on the amount and size of memory chunks ... + * + * The 'pos' argument now simply means 'linefeed after print' ... + */ + +#ifdef DEBUG_SCREEN +static int peng=0, line=0; +#endif + +void mac_debugging_short(int pos, short num) +{ +#ifdef DEBUG_SCREEN + unsigned char *pengoffset; + unsigned char *pptr; + int i; +#endif + +#ifdef DEBUG_SERIAL + printk("debug: %d !\n", num); +#endif + +#ifdef DEBUG_SCREEN + if (!MACH_IS_MAC) { + /* printk("debug: %d !\n", num); */ + return; + } + + /* calculate current offset */ + pengoffset=(unsigned char *)(mac_videobase+(150+line*2)*mac_rowbytes) + +80*peng; + + pptr=pengoffset; + + for(i=0;i<8*sizeof(short);i++) /* # of bits */ + { + /* value mask for bit i, reverse order */ + *pptr++ = (num & ( 1 << (8*sizeof(short)-i-1) ) ? 0xFF : 0x00); + } + + peng++; + + if (pos) { + line++; + peng = 0; + } +#endif +} + +void mac_debugging_long(int pos, long addr) +{ +#ifdef DEBUG_SCREEN + unsigned char *pengoffset; + unsigned char *pptr; + int i; +#endif + +#ifdef DEBUG_SERIAL + printk("debug: #%ld !\n", addr); +#endif + +#ifdef DEBUG_SCREEN + if (!MACH_IS_MAC) { + /* printk("debug: #%ld !\n", addr); */ + return; + } + + pengoffset=(unsigned char *)(mac_videobase+(150+line*2)*mac_rowbytes) + +80*peng; + + pptr=pengoffset; + + for(i=0;i<8*sizeof(long);i++) /* # of bits */ + { + *pptr++ = (addr & ( 1 << (8*sizeof(long)-i-1) ) ? 0xFF : 0x00); + } + + peng++; + + if (pos) { + line++; + peng = 0; + } +#endif +} + +#ifdef DEBUG_SERIAL +/* + * TODO: serial debug code + */ + +struct mac_SCC + { + u_char cha_b_ctrl; + u_char char_dummy1; + u_char cha_a_ctrl; + u_char char_dummy2; + u_char cha_b_data; + u_char char_dummy3; + u_char cha_a_data; + }; + +# define scc (*((volatile struct mac_SCC*)mac_bi_data.sccbase)) + +/* Flag that serial port is already initialized and used */ +int mac_SCC_init_done; +/* Can be set somewhere, if a SCC master reset has already be done and should + * not be repeated; used by kgdb */ +int mac_SCC_reset_done; + +static int scc_port = -1; + +static struct console mac_console_driver = { + .name = "debug", + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +/* + * Crude hack to get console output to the screen before the framebuffer + * is initialized (happens a lot later in 2.1!). + * We just use the console routines declared in head.S, this will interfere + * with regular framebuffer console output and should be used exclusively + * to debug kernel problems manifesting before framebuffer init (aka WSOD) + * + * To keep this hack from interfering with the regular console driver, either + * deregister this driver before/on framebuffer console init, or silence this + * function after the fbcon driver is running (will lose console messages!?). + * To debug real early bugs, need to write a 'mac_register_console_hack()' + * that is called from start_kernel() before setup_arch() and just registers + * this driver if Mac. + */ + +void mac_debug_console_write (struct console *co, const char *str, + unsigned int count) +{ + mac_serial_print(str); +} + + + +/* Mac: loops_per_jiffy min. 19000 ^= .5 us; MFPDELAY was 0.6 us*/ + +#define uSEC 1 + +static inline void mac_sccb_out (char c) +{ + int i; + do { + for( i = uSEC; i > 0; --i ) + barrier(); + } while (!(scc.cha_b_ctrl & 0x04)); /* wait for tx buf empty */ + for( i = uSEC; i > 0; --i ) + barrier(); + scc.cha_b_data = c; +} + +static inline void mac_scca_out (char c) +{ + int i; + do { + for( i = uSEC; i > 0; --i ) + barrier(); + } while (!(scc.cha_a_ctrl & 0x04)); /* wait for tx buf empty */ + for( i = uSEC; i > 0; --i ) + barrier(); + scc.cha_a_data = c; +} + +void mac_sccb_console_write (struct console *co, const char *str, + unsigned int count) +{ + while (count--) { + if (*str == '\n') + mac_sccb_out( '\r' ); + mac_sccb_out( *str++ ); + } +} + +void mac_scca_console_write (struct console *co, const char *str, + unsigned int count) +{ + while (count--) { + if (*str == '\n') + mac_scca_out( '\r' ); + mac_scca_out( *str++ ); + } +} + + +/* The following two functions do a quick'n'dirty initialization of the MFP or + * SCC serial ports. They're used by the debugging interface, kgdb, and the + * serial console code. */ +#define SCCB_WRITE(reg,val) \ + do { \ + int i; \ + scc.cha_b_ctrl = (reg); \ + for( i = uSEC; i > 0; --i ) \ + barrier(); \ + scc.cha_b_ctrl = (val); \ + for( i = uSEC; i > 0; --i ) \ + barrier(); \ + } while(0) + +#define SCCA_WRITE(reg,val) \ + do { \ + int i; \ + scc.cha_a_ctrl = (reg); \ + for( i = uSEC; i > 0; --i ) \ + barrier(); \ + scc.cha_a_ctrl = (val); \ + for( i = uSEC; i > 0; --i ) \ + barrier(); \ + } while(0) + +/* loops_per_jiffy isn't initialized yet, so we can't use udelay(). This does a + * delay of ~ 60us. */ +/* Mac: loops_per_jiffy min. 19000 ^= .5 us; MFPDELAY was 0.6 us*/ +#define LONG_DELAY() \ + do { \ + int i; \ + for( i = 60*uSEC; i > 0; --i ) \ + barrier(); \ + } while(0) + +#ifndef CONFIG_SERIAL_CONSOLE +static void __init mac_init_scc_port( int cflag, int port ) +#else +void mac_init_scc_port( int cflag, int port ) +#endif +{ + extern int mac_SCC_reset_done; + + /* + * baud rates: 1200, 1800, 2400, 4800, 9600, 19.2k, 38.4k, 57.6k, 115.2k + */ + + static int clksrc_table[9] = + /* reg 11: 0x50 = BRG, 0x00 = RTxC, 0x28 = TRxC */ + { 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x00, 0x00 }; + static int clkmode_table[9] = + /* reg 4: 0x40 = x16, 0x80 = x32, 0xc0 = x64 */ + { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x80 }; + static int div_table[9] = + /* reg12 (BRG low) */ + { 94, 62, 46, 22, 10, 4, 1, 0, 0 }; + + int baud = cflag & CBAUD; + int clksrc, clkmode, div, reg3, reg5; + + if (cflag & CBAUDEX) + baud += B38400; + if (baud < B1200 || baud > B38400+2) + baud = B9600; /* use default 9600bps for non-implemented rates */ + baud -= B1200; /* tables starts at 1200bps */ + + clksrc = clksrc_table[baud]; + clkmode = clkmode_table[baud]; + div = div_table[baud]; + + reg3 = (((cflag & CSIZE) == CS8) ? 0xc0 : 0x40); + reg5 = (((cflag & CSIZE) == CS8) ? 0x60 : 0x20) | 0x82 /* assert DTR/RTS */; + + if (port == 1) { + (void)scc.cha_b_ctrl; /* reset reg pointer */ + SCCB_WRITE( 9, 0xc0 ); /* reset */ + LONG_DELAY(); /* extra delay after WR9 access */ + SCCB_WRITE( 4, (cflag & PARENB) ? ((cflag & PARODD) ? 0x01 : 0x03) : 0 | + 0x04 /* 1 stopbit */ | + clkmode ); + SCCB_WRITE( 3, reg3 ); + SCCB_WRITE( 5, reg5 ); + SCCB_WRITE( 9, 0 ); /* no interrupts */ + LONG_DELAY(); /* extra delay after WR9 access */ + SCCB_WRITE( 10, 0 ); /* NRZ mode */ + SCCB_WRITE( 11, clksrc ); /* main clock source */ + SCCB_WRITE( 12, div ); /* BRG value */ + SCCB_WRITE( 13, 0 ); /* BRG high byte */ + SCCB_WRITE( 14, 1 ); + SCCB_WRITE( 3, reg3 | 1 ); + SCCB_WRITE( 5, reg5 | 8 ); + } else if (port == 0) { + (void)scc.cha_a_ctrl; /* reset reg pointer */ + SCCA_WRITE( 9, 0xc0 ); /* reset */ + LONG_DELAY(); /* extra delay after WR9 access */ + SCCA_WRITE( 4, (cflag & PARENB) ? ((cflag & PARODD) ? 0x01 : 0x03) : 0 | + 0x04 /* 1 stopbit */ | + clkmode ); + SCCA_WRITE( 3, reg3 ); + SCCA_WRITE( 5, reg5 ); + SCCA_WRITE( 9, 0 ); /* no interrupts */ + LONG_DELAY(); /* extra delay after WR9 access */ + SCCA_WRITE( 10, 0 ); /* NRZ mode */ + SCCA_WRITE( 11, clksrc ); /* main clock source */ + SCCA_WRITE( 12, div ); /* BRG value */ + SCCA_WRITE( 13, 0 ); /* BRG high byte */ + SCCA_WRITE( 14, 1 ); + SCCA_WRITE( 3, reg3 | 1 ); + SCCA_WRITE( 5, reg5 | 8 ); + } + + mac_SCC_reset_done = 1; + mac_SCC_init_done = 1; +} +#endif /* DEBUG_SERIAL */ + +void mac_init_scca_port( int cflag ) +{ + mac_init_scc_port(cflag, 0); +} + +void mac_init_sccb_port( int cflag ) +{ + mac_init_scc_port(cflag, 1); +} + +void __init mac_debug_init(void) +{ +#ifdef DEBUG_SERIAL + if ( !strcmp( m68k_debug_device, "ser" ) + || !strcmp( m68k_debug_device, "ser1" )) { + /* Mac modem port */ + mac_init_scc_port( B9600|CS8, 0 ); + mac_console_driver.write = mac_scca_console_write; + scc_port = 0; + } + else if (!strcmp( m68k_debug_device, "ser2" )) { + /* Mac printer port */ + mac_init_scc_port( B9600|CS8, 1 ); + mac_console_driver.write = mac_sccb_console_write; + scc_port = 1; + } +#endif +#ifdef DEBUG_HEADS + if ( !strcmp( m68k_debug_device, "scn" ) + || !strcmp( m68k_debug_device, "con" )) { + /* display, using head.S console routines */ + mac_console_driver.write = mac_debug_console_write; + } +#endif + if (mac_console_driver.write) + register_console(&mac_console_driver); +} + +/* + * Local variables: + * c-indent-level: 4 + * tab-width: 8 + * End: + */ 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 @@ +/* + * I/O Processor (IOP) management + * Written and (C) 1999 by Joshua M. Thompson (funaho@jurai.org) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice and this list of conditions. + * 2. Redistributions in binary form must reproduce the above copyright + * notice and this list of conditions in the documentation and/or other + * materials provided with the distribution. + */ + +/* + * The IOP chips are used in the IIfx and some Quadras (900, 950) to manage + * serial and ADB. They are actually a 6502 processor and some glue logic. + * + * 990429 (jmt) - Initial implementation, just enough to knock the SCC IOP + * into compatible mode so nobody has to fiddle with the + * Serial Switch control panel anymore. + * 990603 (jmt) - Added code to grab the correct ISM IOP interrupt for OSS + * and non-OSS machines (at least I hope it's correct on a + * non-OSS machine -- someone with a Q900 or Q950 needs to + * check this.) + * 990605 (jmt) - Rearranged things a bit wrt IOP detection; iop_present is + * gone, IOP base addresses are now in an array and the + * globally-visible functions take an IOP number instead of an + * an actual base address. + * 990610 (jmt) - Finished the message passing framework and it seems to work. + * Sending _definitely_ works; my adb-bus.c mods can send + * messages and receive the MSG_COMPLETED status back from the + * IOP. The trick now is figuring out the message formats. + * 990611 (jmt) - More cleanups. Fixed problem where unclaimed messages on a + * receive channel were never properly acknowledged. Bracketed + * the remaining debug printk's with #ifdef's and disabled + * debugging. I can now type on the console. + * 990612 (jmt) - Copyright notice added. Reworked the way replies are handled. + * It turns out that replies are placed back in the send buffer + * for that channel; messages on the receive channels are always + * unsolicited messages from the IOP (and our replies to them + * should go back in the receive channel.) Also added tracking + * of device names to the listener functions ala the interrupt + * handlers. + * 990729 (jmt) - Added passing of pt_regs structure to IOP handlers. This is + * used by the new unified ADB driver. + * + * TODO: + * + * o Something should be periodically checking iop_alive() to make sure the + * IOP hasn't died. + * o Some of the IOP manager routines need better error checking and + * return codes. Nothing major, just prettying up. + */ + +/* + * ----------------------- + * IOP Message Passing 101 + * ----------------------- + * + * The host talks to the IOPs using a rather simple message-passing scheme via + * a shared memory area in the IOP RAM. Each IOP has seven "channels"; each + * channel is conneced to a specific software driver on the IOP. For example + * on the SCC IOP there is one channel for each serial port. Each channel has + * an incoming and and outgoing message queue with a depth of one. + * + * A message is 32 bytes plus a state byte for the channel (MSG_IDLE, MSG_NEW, + * MSG_RCVD, MSG_COMPLETE). To send a message you copy the message into the + * buffer, set the state to MSG_NEW and signal the IOP by setting the IRQ flag + * in the IOP control to 1. The IOP will move the state to MSG_RCVD when it + * receives the message and then to MSG_COMPLETE when the message processing + * has completed. It is the host's responsibility at that point to read the + * reply back out of the send channel buffer and reset the channel state back + * to MSG_IDLE. + * + * To receive message from the IOP the same procedure is used except the roles + * are reversed. That is, the IOP puts message in the channel with a state of + * MSG_NEW, and the host receives the message and move its state to MSG_RCVD + * and then to MSG_COMPLETE when processing is completed and the reply (if any) + * has been placed back in the receive channel. The IOP will then reset the + * channel state to MSG_IDLE. + * + * Two sets of host interrupts are provided, INT0 and INT1. Both appear on one + * interrupt level; they are distinguished by a pair of bits in the IOP status + * register. The IOP will raise INT0 when one or more messages in the send + * channels have gone to the MSG_COMPLETE state and it will raise INT1 when one + * or more messages on the receive channels have gone to the MSG_NEW state. + * + * Since each channel handles only one message we have to implement a small + * interrupt-driven queue on our end. Messages to be sent are placed on the + * queue for sending and contain a pointer to an optional callback function. + * The handler for a message is called when the message state goes to + * MSG_COMPLETE. + * + * For receiving message we maintain a list of handler functions to call when + * a message is received on that IOP/channel combination. The handlers are + * called much like an interrupt handler and are passed a copy of the message + * from the IOP. The message state will be in MSG_RCVD while the handler runs; + * it is the handler's responsibility to call iop_complete_message() when + * finished; this function moves the message state to MSG_COMPLETE and signals + * the IOP. This two-step process is provided to allow the handler to defer + * message processing to a bottom-half handler if the processing will take + * a signifigant amount of time (handlers are called at interrupt time so they + * should execute quickly.) + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/proc_fs.h> +#include <linux/interrupt.h> + +#include <asm/bootinfo.h> +#include <asm/macintosh.h> +#include <asm/macints.h> +#include <asm/mac_iop.h> +#include <asm/mac_oss.h> + +/*#define DEBUG_IOP*/ + +/* Set to nonezero if the IOPs are present. Set by iop_init() */ + +int iop_scc_present,iop_ism_present; + +#ifdef CONFIG_PROC_FS +static int iop_get_proc_info(char *, char **, off_t, int); +#endif /* CONFIG_PROC_FS */ + +/* structure for tracking channel listeners */ + +struct listener { + const char *devname; + void (*handler)(struct iop_msg *, struct pt_regs *); +}; + +/* + * IOP structures for the two IOPs + * + * The SCC IOP controls both serial ports (A and B) as its two functions. + * The ISM IOP controls the SWIM (floppy drive) and ADB. + */ + +static volatile struct mac_iop *iop_base[NUM_IOPS]; + +/* + * IOP message queues + */ + +static struct iop_msg iop_msg_pool[NUM_IOP_MSGS]; +static struct iop_msg *iop_send_queue[NUM_IOPS][NUM_IOP_CHAN]; +static struct listener iop_listeners[NUM_IOPS][NUM_IOP_CHAN]; + +irqreturn_t iop_ism_irq(int, void *, struct pt_regs *); + +extern void oss_irq_enable(int); + +/* + * Private access functions + */ + +static __inline__ void iop_loadaddr(volatile struct mac_iop *iop, __u16 addr) +{ + iop->ram_addr_lo = addr; + iop->ram_addr_hi = addr >> 8; +} + +static __inline__ __u8 iop_readb(volatile struct mac_iop *iop, __u16 addr) +{ + iop->ram_addr_lo = addr; + iop->ram_addr_hi = addr >> 8; + return iop->ram_data; +} + +static __inline__ void iop_writeb(volatile struct mac_iop *iop, __u16 addr, __u8 data) +{ + iop->ram_addr_lo = addr; + iop->ram_addr_hi = addr >> 8; + iop->ram_data = data; +} + +static __inline__ void iop_stop(volatile struct mac_iop *iop) +{ + iop->status_ctrl &= ~IOP_RUN; +} + +static __inline__ void iop_start(volatile struct mac_iop *iop) +{ + iop->status_ctrl = IOP_RUN | IOP_AUTOINC; +} + +static __inline__ void iop_bypass(volatile struct mac_iop *iop) +{ + iop->status_ctrl |= IOP_BYPASS; +} + +static __inline__ void iop_interrupt(volatile struct mac_iop *iop) +{ + iop->status_ctrl |= IOP_IRQ; +} + +static int iop_alive(volatile struct mac_iop *iop) +{ + int retval; + + retval = (iop_readb(iop, IOP_ADDR_ALIVE) == 0xFF); + iop_writeb(iop, IOP_ADDR_ALIVE, 0); + return retval; +} + +static struct iop_msg *iop_alloc_msg(void) +{ + int i; + unsigned long flags; + + local_irq_save(flags); + + for (i = 0 ; i < NUM_IOP_MSGS ; i++) { + if (iop_msg_pool[i].status == IOP_MSGSTATUS_UNUSED) { + iop_msg_pool[i].status = IOP_MSGSTATUS_WAITING; + local_irq_restore(flags); + return &iop_msg_pool[i]; + } + } + + local_irq_restore(flags); + return NULL; +} + +static void iop_free_msg(struct iop_msg *msg) +{ + msg->status = IOP_MSGSTATUS_UNUSED; +} + +/* + * This is called by the startup code before anything else. Its purpose + * is to find and initialize the IOPs early in the boot sequence, so that + * the serial IOP can be placed into bypass mode _before_ we try to + * initialize the serial console. + */ + +void __init iop_preinit(void) +{ + if (macintosh_config->scc_type == MAC_SCC_IOP) { + if (macintosh_config->ident == MAC_MODEL_IIFX) { + iop_base[IOP_NUM_SCC] = (struct mac_iop *) SCC_IOP_BASE_IIFX; + } else { + iop_base[IOP_NUM_SCC] = (struct mac_iop *) SCC_IOP_BASE_QUADRA; + } + iop_base[IOP_NUM_SCC]->status_ctrl = 0x87; + iop_scc_present = 1; + } else { + iop_base[IOP_NUM_SCC] = NULL; + iop_scc_present = 0; + } + if (macintosh_config->adb_type == MAC_ADB_IOP) { + if (macintosh_config->ident == MAC_MODEL_IIFX) { + iop_base[IOP_NUM_ISM] = (struct mac_iop *) ISM_IOP_BASE_IIFX; + } else { + iop_base[IOP_NUM_ISM] = (struct mac_iop *) ISM_IOP_BASE_QUADRA; + } + iop_base[IOP_NUM_ISM]->status_ctrl = 0; + iop_ism_present = 1; + } else { + iop_base[IOP_NUM_ISM] = NULL; + iop_ism_present = 0; + } +} + +/* + * Initialize the IOPs, if present. + */ + +void __init iop_init(void) +{ + int i; + + if (iop_scc_present) { + printk("IOP: detected SCC IOP at %p\n", iop_base[IOP_NUM_SCC]); + } + if (iop_ism_present) { + printk("IOP: detected ISM IOP at %p\n", iop_base[IOP_NUM_ISM]); + iop_start(iop_base[IOP_NUM_ISM]); + iop_alive(iop_base[IOP_NUM_ISM]); /* clears the alive flag */ + } + + /* Make the whole pool available and empty the queues */ + + for (i = 0 ; i < NUM_IOP_MSGS ; i++) { + iop_msg_pool[i].status = IOP_MSGSTATUS_UNUSED; + } + + for (i = 0 ; i < NUM_IOP_CHAN ; i++) { + iop_send_queue[IOP_NUM_SCC][i] = 0; + iop_send_queue[IOP_NUM_ISM][i] = 0; + iop_listeners[IOP_NUM_SCC][i].devname = NULL; + iop_listeners[IOP_NUM_SCC][i].handler = NULL; + iop_listeners[IOP_NUM_ISM][i].devname = NULL; + iop_listeners[IOP_NUM_ISM][i].handler = NULL; + } + +#if 0 /* Crashing in 2.4 now, not yet sure why. --jmt */ +#ifdef CONFIG_PROC_FS + create_proc_info_entry("mac_iop", 0, &proc_root, iop_get_proc_info); +#endif +#endif +} + +/* + * Register the interrupt handler for the IOPs. + * TODO: might be wrong for non-OSS machines. Anyone? + */ + +void __init iop_register_interrupts(void) +{ + if (iop_ism_present) { + if (oss_present) { + cpu_request_irq(OSS_IRQLEV_IOPISM, iop_ism_irq, + IRQ_FLG_LOCK, "ISM IOP", + (void *) IOP_NUM_ISM); + oss_irq_enable(IRQ_MAC_ADB); + } else { + request_irq(IRQ_VIA2_0, iop_ism_irq, + IRQ_FLG_LOCK|IRQ_FLG_FAST, "ISM IOP", + (void *) IOP_NUM_ISM); + } + if (!iop_alive(iop_base[IOP_NUM_ISM])) { + printk("IOP: oh my god, they killed the ISM IOP!\n"); + } else { + printk("IOP: the ISM IOP seems to be alive.\n"); + } + } +} + +/* + * Register or unregister a listener for a specific IOP and channel + * + * If the handler pointer is NULL the current listener (if any) is + * unregistered. Otherwise the new listener is registered provided + * there is no existing listener registered. + */ + +int iop_listen(uint iop_num, uint chan, + void (*handler)(struct iop_msg *, struct pt_regs *), + const char *devname) +{ + if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return -EINVAL; + if (chan >= NUM_IOP_CHAN) return -EINVAL; + if (iop_listeners[iop_num][chan].handler && handler) return -EINVAL; + iop_listeners[iop_num][chan].devname = devname; + iop_listeners[iop_num][chan].handler = handler; + return 0; +} + +/* + * Complete reception of a message, which just means copying the reply + * into the buffer, setting the channel state to MSG_COMPLETE and + * notifying the IOP. + */ + +void iop_complete_message(struct iop_msg *msg) +{ + int iop_num = msg->iop_num; + int chan = msg->channel; + int i,offset; + +#ifdef DEBUG_IOP + printk("iop_complete(%p): iop %d chan %d\n", msg, msg->iop_num, msg->channel); +#endif + + offset = IOP_ADDR_RECV_MSG + (msg->channel * IOP_MSG_LEN); + + for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) { + iop_writeb(iop_base[iop_num], offset, msg->reply[i]); + } + + iop_writeb(iop_base[iop_num], + IOP_ADDR_RECV_STATE + chan, IOP_MSG_COMPLETE); + iop_interrupt(iop_base[msg->iop_num]); + + iop_free_msg(msg); +} + +/* + * Actually put a message into a send channel buffer + */ + +static void iop_do_send(struct iop_msg *msg) +{ + volatile struct mac_iop *iop = iop_base[msg->iop_num]; + int i,offset; + + offset = IOP_ADDR_SEND_MSG + (msg->channel * IOP_MSG_LEN); + + for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) { + iop_writeb(iop, offset, msg->message[i]); + } + + iop_writeb(iop, IOP_ADDR_SEND_STATE + msg->channel, IOP_MSG_NEW); + + iop_interrupt(iop); +} + +/* + * Handle sending a message on a channel that + * has gone into the IOP_MSG_COMPLETE state. + */ + +static void iop_handle_send(uint iop_num, uint chan, struct pt_regs *regs) +{ + volatile struct mac_iop *iop = iop_base[iop_num]; + struct iop_msg *msg,*msg2; + int i,offset; + +#ifdef DEBUG_IOP + printk("iop_handle_send: iop %d channel %d\n", iop_num, chan); +#endif + + iop_writeb(iop, IOP_ADDR_SEND_STATE + chan, IOP_MSG_IDLE); + + if (!(msg = iop_send_queue[iop_num][chan])) return; + + msg->status = IOP_MSGSTATUS_COMPLETE; + offset = IOP_ADDR_SEND_MSG + (chan * IOP_MSG_LEN); + for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) { + msg->reply[i] = iop_readb(iop, offset); + } + if (msg->handler) (*msg->handler)(msg, regs); + msg2 = msg; + msg = msg->next; + iop_free_msg(msg2); + + iop_send_queue[iop_num][chan] = msg; + if (msg) iop_do_send(msg); +} + +/* + * Handle reception of a message on a channel that has + * gone into the IOP_MSG_NEW state. + */ + +static void iop_handle_recv(uint iop_num, uint chan, struct pt_regs *regs) +{ + volatile struct mac_iop *iop = iop_base[iop_num]; + int i,offset; + struct iop_msg *msg; + +#ifdef DEBUG_IOP + printk("iop_handle_recv: iop %d channel %d\n", iop_num, chan); +#endif + + msg = iop_alloc_msg(); + msg->iop_num = iop_num; + msg->channel = chan; + msg->status = IOP_MSGSTATUS_UNSOL; + msg->handler = iop_listeners[iop_num][chan].handler; + + offset = IOP_ADDR_RECV_MSG + (chan * IOP_MSG_LEN); + + for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) { + msg->message[i] = iop_readb(iop, offset); + } + + iop_writeb(iop, IOP_ADDR_RECV_STATE + chan, IOP_MSG_RCVD); + + /* If there is a listener, call it now. Otherwise complete */ + /* the message ourselves to avoid possible stalls. */ + + if (msg->handler) { + (*msg->handler)(msg, regs); + } else { +#ifdef DEBUG_IOP + printk("iop_handle_recv: unclaimed message on iop %d channel %d\n", iop_num, chan); + printk("iop_handle_recv:"); + for (i = 0 ; i < IOP_MSG_LEN ; i++) { + printk(" %02X", (uint) msg->message[i]); + } + printk("\n"); +#endif + iop_complete_message(msg); + } +} + +/* + * Send a message + * + * The message is placed at the end of the send queue. Afterwards if the + * channel is idle we force an immediate send of the next message in the + * queue. + */ + +int iop_send_message(uint iop_num, uint chan, void *privdata, + uint msg_len, __u8 *msg_data, + void (*handler)(struct iop_msg *, struct pt_regs *)) +{ + struct iop_msg *msg, *q; + + if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return -EINVAL; + if (chan >= NUM_IOP_CHAN) return -EINVAL; + if (msg_len > IOP_MSG_LEN) return -EINVAL; + + msg = iop_alloc_msg(); + if (!msg) return -ENOMEM; + + msg->next = NULL; + msg->status = IOP_MSGSTATUS_WAITING; + msg->iop_num = iop_num; + msg->channel = chan; + msg->caller_priv = privdata; + memcpy(msg->message, msg_data, msg_len); + msg->handler = handler; + + if (!(q = iop_send_queue[iop_num][chan])) { + iop_send_queue[iop_num][chan] = msg; + } else { + while (q->next) q = q->next; + q->next = msg; + } + + if (iop_readb(iop_base[iop_num], + IOP_ADDR_SEND_STATE + chan) == IOP_MSG_IDLE) { + iop_do_send(msg); + } + + return 0; +} + +/* + * Upload code to the shared RAM of an IOP. + */ + +void iop_upload_code(uint iop_num, __u8 *code_start, + uint code_len, __u16 shared_ram_start) +{ + if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return; + + iop_loadaddr(iop_base[iop_num], shared_ram_start); + + while (code_len--) { + iop_base[iop_num]->ram_data = *code_start++; + } +} + +/* + * Download code from the shared RAM of an IOP. + */ + +void iop_download_code(uint iop_num, __u8 *code_start, + uint code_len, __u16 shared_ram_start) +{ + if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return; + + iop_loadaddr(iop_base[iop_num], shared_ram_start); + + while (code_len--) { + *code_start++ = iop_base[iop_num]->ram_data; + } +} + +/* + * Compare the code in the shared RAM of an IOP with a copy in system memory + * and return 0 on match or the first nonmatching system memory address on + * failure. + */ + +__u8 *iop_compare_code(uint iop_num, __u8 *code_start, + uint code_len, __u16 shared_ram_start) +{ + if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return code_start; + + iop_loadaddr(iop_base[iop_num], shared_ram_start); + + while (code_len--) { + if (*code_start != iop_base[iop_num]->ram_data) { + return code_start; + } + code_start++; + } + return (__u8 *) 0; +} + +/* + * Handle an ISM IOP interrupt + */ + +irqreturn_t iop_ism_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + uint iop_num = (uint) dev_id; + volatile struct mac_iop *iop = iop_base[iop_num]; + int i,state; + +#ifdef DEBUG_IOP + printk("iop_ism_irq: status = %02X\n", (uint) iop->status_ctrl); +#endif + + /* INT0 indicates a state change on an outgoing message channel */ + + if (iop->status_ctrl & IOP_INT0) { + iop->status_ctrl = IOP_INT0 | IOP_RUN | IOP_AUTOINC; +#ifdef DEBUG_IOP + printk("iop_ism_irq: new status = %02X, send states", + (uint) iop->status_ctrl); +#endif + for (i = 0 ; i < NUM_IOP_CHAN ; i++) { + state = iop_readb(iop, IOP_ADDR_SEND_STATE + i); +#ifdef DEBUG_IOP + printk(" %02X", state); +#endif + if (state == IOP_MSG_COMPLETE) { + iop_handle_send(iop_num, i, regs); + } + } +#ifdef DEBUG_IOP + printk("\n"); +#endif + } + + if (iop->status_ctrl & IOP_INT1) { /* INT1 for incoming msgs */ + iop->status_ctrl = IOP_INT1 | IOP_RUN | IOP_AUTOINC; +#ifdef DEBUG_IOP + printk("iop_ism_irq: new status = %02X, recv states", + (uint) iop->status_ctrl); +#endif + for (i = 0 ; i < NUM_IOP_CHAN ; i++) { + state = iop_readb(iop, IOP_ADDR_RECV_STATE + i); +#ifdef DEBUG_IOP + printk(" %02X", state); +#endif + if (state == IOP_MSG_NEW) { + iop_handle_recv(iop_num, i, regs); + } + } +#ifdef DEBUG_IOP + printk("\n"); +#endif + } + return IRQ_HANDLED; +} + +#ifdef CONFIG_PROC_FS + +char *iop_chan_state(int state) +{ + switch(state) { + case IOP_MSG_IDLE : return "idle "; + case IOP_MSG_NEW : return "new "; + case IOP_MSG_RCVD : return "received "; + case IOP_MSG_COMPLETE : return "completed "; + default : return "unknown "; + } +} + +int iop_dump_one_iop(char *buf, int iop_num, char *iop_name) +{ + int i,len = 0; + volatile struct mac_iop *iop = iop_base[iop_num]; + + len += sprintf(buf+len, "%s IOP channel states:\n\n", iop_name); + len += sprintf(buf+len, "## send_state recv_state device\n"); + len += sprintf(buf+len, "------------------------------------------------\n"); + for (i = 0 ; i < NUM_IOP_CHAN ; i++) { + len += sprintf(buf+len, "%2d %10s %10s %s\n", i, + iop_chan_state(iop_readb(iop, IOP_ADDR_SEND_STATE+i)), + iop_chan_state(iop_readb(iop, IOP_ADDR_RECV_STATE+i)), + iop_listeners[iop_num][i].handler? + iop_listeners[iop_num][i].devname : ""); + + } + len += sprintf(buf+len, "\n"); + return len; +} + +static int iop_get_proc_info(char *buf, char **start, off_t pos, int count) +{ + int len, cnt; + + cnt = 0; + len = sprintf(buf, "IOPs detected:\n\n"); + + if (iop_scc_present) { + len += sprintf(buf+len, "SCC IOP (%p): status %02X\n", + iop_base[IOP_NUM_SCC], + (uint) iop_base[IOP_NUM_SCC]->status_ctrl); + } + if (iop_ism_present) { + len += sprintf(buf+len, "ISM IOP (%p): status %02X\n\n", + iop_base[IOP_NUM_ISM], + (uint) iop_base[IOP_NUM_ISM]->status_ctrl); + } + + if (iop_scc_present) { + len += iop_dump_one_iop(buf+len, IOP_NUM_SCC, "SCC"); + + } + + if (iop_ism_present) { + len += iop_dump_one_iop(buf+len, IOP_NUM_ISM, "ISM"); + + } + + if (len >= pos) { + if (!*start) { + *start = buf + pos; + cnt = len - pos; + } else { + cnt += len; + } + } + return (count > cnt) ? cnt : count; +} + +#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 @@ +#include <linux/module.h> +#include <asm/ptrace.h> +#include <asm/traps.h> + +/* Says whether we're using A/UX interrupts or not */ +extern int via_alt_mapping; + +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 @@ +.byte \ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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,\ +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 @@ +/* + * Mac bong noise generator. Note - we ought to put a boingy noise + * here 8) + * + * ---------------------------------------------------------------------- + * 16.11.98: + * rewrote some functions, added support for Enhanced ASC (Quadras) + * after the NetBSD asc.c console bell patch by Colin Wood/Frederick Bruck + * Juergen Mellinger (juergen.mellinger@t-online.de) + */ + +#include <linux/sched.h> +#include <linux/timer.h> + +#include <asm/macintosh.h> +#include <asm/mac_asc.h> + +static int mac_asc_inited; +/* + * dumb triangular wave table + */ +static __u8 mac_asc_wave_tab[ 0x800 ]; + +/* + * Alan's original sine table; needs interpolating to 0x800 + * (hint: interpolate or hardwire [0 -> Pi/2[, it's symmetric) + */ +static const signed char sine_data[] = { + 0, 39, 75, 103, 121, 127, 121, 103, 75, 39, + 0, -39, -75, -103, -121, -127, -121, -103, -75, -39 +}; + +/* + * where the ASC hides ... + */ +static volatile __u8* mac_asc_regs = ( void* )0x50F14000; + +/* + * sample rate; is this a good default value? + */ +static unsigned long mac_asc_samplespersec = 11050; +static int mac_bell_duration; +static unsigned long mac_bell_phase; /* 0..2*Pi -> 0..0x800 (wavetable size) */ +static unsigned long mac_bell_phasepersample; + +/* + * some function protos + */ +static void mac_init_asc( void ); +static void mac_nosound( unsigned long ); +static void mac_quadra_start_bell( unsigned int, unsigned int, unsigned int ); +static void mac_quadra_ring_bell( unsigned long ); +static void mac_av_start_bell( unsigned int, unsigned int, unsigned int ); +static void ( *mac_special_bell )( unsigned int, unsigned int, unsigned int ); + +/* + * our timer to start/continue/stop the bell + */ +static struct timer_list mac_sound_timer = + TIMER_INITIALIZER(mac_nosound, 0, 0); + +/* + * Sort of initialize the sound chip (called from mac_mksound on the first + * beep). + */ +static void mac_init_asc( void ) +{ + int i; + + /* + * do some machine specific initialization + * BTW: + * the NetBSD Quadra patch identifies the Enhanced Apple Sound Chip via + * mac_asc_regs[ 0x800 ] & 0xF0 != 0 + * this makes no sense here, because we have to set the default sample + * rate anyway if we want correct frequencies + */ + switch ( macintosh_config->ident ) + { + case MAC_MODEL_IIFX: + /* + * The IIfx is always special ... + */ + mac_asc_regs = ( void* )0x50010000; + break; + /* + * not sure about how correct this list is + * machines with the EASC enhanced apple sound chip + */ + case MAC_MODEL_Q630: + case MAC_MODEL_P475: + mac_special_bell = mac_quadra_start_bell; + mac_asc_samplespersec = 22150; + break; + case MAC_MODEL_C660: + case MAC_MODEL_Q840: + /* + * The Quadra 660AV and 840AV use the "Singer" custom ASIC for sound I/O. + * It appears to be similar to the "AWACS" custom ASIC in the Power Mac + * [678]100. Because Singer and AWACS may have a similar hardware + * interface, this would imply that the code in drivers/sound/dmasound.c + * for AWACS could be used as a basis for Singer support. All we have to + * do is figure out how to do DMA on the 660AV/840AV through the PSC and + * figure out where the Singer hardware sits in memory. (I'd look in the + * vicinity of the AWACS location in a Power Mac [678]100 first, or the + * current location of the Apple Sound Chip--ASC--in other Macs.) The + * Power Mac [678]100 info can be found in MkLinux Mach kernel sources. + * + * Quoted from Apple's Tech Info Library, article number 16405: + * "Among desktop Macintosh computers, only the 660AV, 840AV, and Power + * Macintosh models have 16-bit audio input and output capability + * because of the AT&T DSP3210 hardware circuitry and the 16-bit Singer + * codec circuitry in the AVs. The Audio Waveform Amplifier and + * Converter (AWAC) chip in the Power Macintosh performs the same + * 16-bit I/O functionality. The PowerBook 500 series computers + * support 16-bit stereo output, but only mono input." + * + * http://til.info.apple.com/techinfo.nsf/artnum/n16405 + * + * --David Kilzer + */ + mac_special_bell = mac_av_start_bell; + break; + case MAC_MODEL_Q650: + case MAC_MODEL_Q700: + case MAC_MODEL_Q800: + case MAC_MODEL_Q900: + case MAC_MODEL_Q950: + /* + * Currently not implemented! + */ + mac_special_bell = NULL; + break; + default: + /* + * Every switch needs a default + */ + mac_special_bell = NULL; + break; + } + + /* + * init the wave table with a simple triangular wave + * A sine wave would sure be nicer here ... + */ + for ( i = 0; i < 0x400; i++ ) + { + mac_asc_wave_tab[ i ] = i / 4; + mac_asc_wave_tab[ i + 0x400 ] = 0xFF - i / 4; + } + mac_asc_inited = 1; +} + +/* + * Called to make noise; current single entry to the boing driver. + * Does the job for simple ASC, calls other routines else. + * XXX Fixme: + * Should be split into asc_mksound, easc_mksound, av_mksound and + * function pointer set in mac_init_asc which would be called at + * init time. + * _This_ is rather ugly ... + */ +void mac_mksound( unsigned int freq, unsigned int length ) +{ + __u32 cfreq = ( freq << 5 ) / 468; + __u32 flags; + int i; + + if ( mac_special_bell == NULL ) + { + /* Do nothing */ + return; + } + + if ( !mac_asc_inited ) + mac_init_asc(); + + if ( mac_special_bell ) + { + mac_special_bell( freq, length, 128 ); + return; + } + + if ( freq < 20 || freq > 20000 || length == 0 ) + { + mac_nosound( 0 ); + return; + } + + local_irq_save(flags); + + del_timer( &mac_sound_timer ); + + for ( i = 0; i < 0x800; i++ ) + mac_asc_regs[ i ] = 0; + for ( i = 0; i < 0x800; i++ ) + mac_asc_regs[ i ] = mac_asc_wave_tab[ i ]; + + for ( i = 0; i < 8; i++ ) + *( __u32* )( ( __u32 )mac_asc_regs + ASC_CONTROL + 0x814 + 8 * i ) = cfreq; + + mac_asc_regs[ 0x807 ] = 0; + mac_asc_regs[ ASC_VOLUME ] = 128; + mac_asc_regs[ 0x805 ] = 0; + mac_asc_regs[ 0x80F ] = 0; + mac_asc_regs[ ASC_MODE ] = ASC_MODE_SAMPLE; + mac_asc_regs[ ASC_ENABLE ] = ASC_ENABLE_SAMPLE; + + mac_sound_timer.expires = jiffies + length; + add_timer( &mac_sound_timer ); + + local_irq_restore(flags); +} + +/* + * regular ASC: stop whining .. + */ +static void mac_nosound( unsigned long ignored ) +{ + mac_asc_regs[ ASC_ENABLE ] = 0; +} + +/* + * EASC entry; init EASC, don't load wavetable, schedule 'start whining'. + */ +static void mac_quadra_start_bell( unsigned int freq, unsigned int length, unsigned int volume ) +{ + __u32 flags; + + /* if the bell is already ringing, ring longer */ + if ( mac_bell_duration > 0 ) + { + mac_bell_duration += length; + return; + } + + mac_bell_duration = length; + mac_bell_phase = 0; + mac_bell_phasepersample = ( freq * sizeof( mac_asc_wave_tab ) ) / mac_asc_samplespersec; + /* this is reasonably big for small frequencies */ + + local_irq_save(flags); + + /* set the volume */ + mac_asc_regs[ 0x806 ] = volume; + + /* set up the ASC registers */ + if ( mac_asc_regs[ 0x801 ] != 1 ) + { + /* select mono mode */ + mac_asc_regs[ 0x807 ] = 0; + /* select sampled sound mode */ + mac_asc_regs[ 0x802 ] = 0; + /* ??? */ + mac_asc_regs[ 0x801 ] = 1; + mac_asc_regs[ 0x803 ] |= 0x80; + mac_asc_regs[ 0x803 ] &= 0x7F; + } + + mac_sound_timer.function = mac_quadra_ring_bell; + mac_sound_timer.expires = jiffies + 1; + add_timer( &mac_sound_timer ); + + local_irq_restore(flags); +} + +/* + * EASC 'start/continue whining'; I'm not sure why the above function didn't + * already load the wave table, or at least call this one... + * This piece keeps reloading the wave table until done. + */ +static void mac_quadra_ring_bell( unsigned long ignored ) +{ + int i, count = mac_asc_samplespersec / HZ; + __u32 flags; + + /* + * we neither want a sound buffer overflow nor underflow, so we need to match + * the number of samples per timer interrupt as exactly as possible. + * using the asc interrupt will give better results in the future + * ...and the possibility to use a real sample (a boingy noise, maybe...) + */ + + local_irq_save(flags); + + del_timer( &mac_sound_timer ); + + if ( mac_bell_duration-- > 0 ) + { + for ( i = 0; i < count; i++ ) + { + mac_bell_phase += mac_bell_phasepersample; + mac_asc_regs[ 0 ] = mac_asc_wave_tab[ mac_bell_phase & ( sizeof( mac_asc_wave_tab ) - 1 ) ]; + } + mac_sound_timer.expires = jiffies + 1; + add_timer( &mac_sound_timer ); + } + else + mac_asc_regs[ 0x801 ] = 0; + + local_irq_restore(flags); +} + +/* + * AV code - please fill in. + */ +static void mac_av_start_bell( unsigned int freq, unsigned int length, unsigned int volume ) +{ +} 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 @@ +/* + * Macintosh interrupts + * + * General design: + * In contrary to the Amiga and Atari platforms, the Mac hardware seems to + * exclusively use the autovector interrupts (the 'generic level0-level7' + * interrupts with exception vectors 0x19-0x1f). The following interrupt levels + * are used: + * 1 - VIA1 + * - slot 0: one second interrupt (CA2) + * - slot 1: VBlank (CA1) + * - slot 2: ADB data ready (SR full) + * - slot 3: ADB data (CB2) + * - slot 4: ADB clock (CB1) + * - slot 5: timer 2 + * - slot 6: timer 1 + * - slot 7: status of IRQ; signals 'any enabled int.' + * + * 2 - VIA2 or RBV + * - slot 0: SCSI DRQ (CA2) + * - slot 1: NUBUS IRQ (CA1) need to read port A to find which + * - slot 2: /EXP IRQ (only on IIci) + * - slot 3: SCSI IRQ (CB2) + * - slot 4: ASC IRQ (CB1) + * - slot 5: timer 2 (not on IIci) + * - slot 6: timer 1 (not on IIci) + * - slot 7: status of IRQ; signals 'any enabled int.' + * + * 2 - OSS (IIfx only?) + * - slot 0: SCSI interrupt + * - slot 1: Sound interrupt + * + * Levels 3-6 vary by machine type. For VIA or RBV Macintoshes: + * + * 3 - unused (?) + * + * 4 - SCC (slot number determined by reading RR3 on the SSC itself) + * - slot 1: SCC channel A + * - slot 2: SCC channel B + * + * 5 - unused (?) + * [serial errors or special conditions seem to raise level 6 + * interrupts on some models (LC4xx?)] + * + * 6 - off switch (?) + * + * For OSS Macintoshes (IIfx only at this point): + * + * 3 - Nubus interrupt + * - slot 0: Slot $9 + * - slot 1: Slot $A + * - slot 2: Slot $B + * - slot 3: Slot $C + * - slot 4: Slot $D + * - slot 5: Slot $E + * + * 4 - SCC IOP + * - slot 1: SCC channel A + * - slot 2: SCC channel B + * + * 5 - ISM IOP (ADB?) + * + * 6 - unused + * + * For PSC Macintoshes (660AV, 840AV): + * + * 3 - PSC level 3 + * - slot 0: MACE + * + * 4 - PSC level 4 + * - slot 1: SCC channel A interrupt + * - slot 2: SCC channel B interrupt + * - slot 3: MACE DMA + * + * 5 - PSC level 5 + * + * 6 - PSC level 6 + * + * Finally we have good 'ole level 7, the non-maskable interrupt: + * + * 7 - NMI (programmer's switch on the back of some Macs) + * Also RAM parity error on models which support it (IIc, IIfx?) + * + * The current interrupt logic looks something like this: + * + * - We install dispatchers for the autovector interrupts (1-7). These + * dispatchers are responsible for querying the hardware (the + * VIA/RBV/OSS/PSC chips) to determine the actual interrupt source. Using + * this information a machspec interrupt number is generated by placing the + * index of the interrupt hardware into the low three bits and the original + * autovector interrupt number in the upper 5 bits. The handlers for the + * resulting machspec interrupt are then called. + * + * - Nubus is a special case because its interrupts are hidden behind two + * layers of hardware. Nubus interrupts come in as index 1 on VIA #2, + * which translates to IRQ number 17. In this spot we install _another_ + * dispatcher. This dispatcher finds the interrupting slot number (9-F) and + * then forms a new machspec interrupt number as above with the slot number + * minus 9 in the low three bits and the pseudo-level 7 in the upper five + * bits. The handlers for this new machspec interrupt number are then + * called. This puts Nubus interrupts into the range 56-62. + * + * - The Baboon interrupts (used on some PowerBooks) are an even more special + * case. They're hidden behind the Nubus slot $C interrupt thus adding a + * third layer of indirection. Why oh why did the Apple engineers do that? + * + * - We support "fast" and "slow" handlers, just like the Amiga port. The + * fast handlers are called first and with all interrupts disabled. They + * are expected to execute quickly (hence the name). The slow handlers are + * called last with interrupts enabled and the interrupt level restored. + * They must therefore be reentrant. + * + * TODO: + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/kernel_stat.h> +#include <linux/interrupt.h> /* for intr_count */ +#include <linux/delay.h> +#include <linux/seq_file.h> + +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/traps.h> +#include <asm/bootinfo.h> +#include <asm/machw.h> +#include <asm/macintosh.h> +#include <asm/mac_via.h> +#include <asm/mac_psc.h> +#include <asm/hwtest.h> +#include <asm/errno.h> +#include <asm/macints.h> + +#define DEBUG_SPURIOUS +#define SHUTUP_SONIC + +/* + * The mac_irq_list array is an array of linked lists of irq_node_t nodes. + * Each node contains one handler to be called whenever the interrupt + * occurs, with fast handlers listed before slow handlers. + */ + +irq_node_t *mac_irq_list[NUM_MAC_SOURCES]; + +/* SCC interrupt mask */ + +static int scc_mask; + +/* + * VIA/RBV hooks + */ + +extern void via_init(void); +extern void via_register_interrupts(void); +extern void via_irq_enable(int); +extern void via_irq_disable(int); +extern void via_irq_clear(int); +extern int via_irq_pending(int); + +/* + * OSS hooks + */ + +extern int oss_present; + +extern void oss_init(void); +extern void oss_register_interrupts(void); +extern void oss_irq_enable(int); +extern void oss_irq_disable(int); +extern void oss_irq_clear(int); +extern int oss_irq_pending(int); + +/* + * PSC hooks + */ + +extern int psc_present; + +extern void psc_init(void); +extern void psc_register_interrupts(void); +extern void psc_irq_enable(int); +extern void psc_irq_disable(int); +extern void psc_irq_clear(int); +extern int psc_irq_pending(int); + +/* + * IOP hooks + */ + +extern void iop_register_interrupts(void); + +/* + * Baboon hooks + */ + +extern int baboon_present; + +extern void baboon_init(void); +extern void baboon_register_interrupts(void); +extern void baboon_irq_enable(int); +extern void baboon_irq_disable(int); +extern void baboon_irq_clear(int); +extern int baboon_irq_pending(int); + +/* + * SCC interrupt routines + */ + +static void scc_irq_enable(int); +static void scc_irq_disable(int); + +/* + * console_loglevel determines NMI handler function + */ + +extern irqreturn_t mac_bang(int, void *, struct pt_regs *); +irqreturn_t mac_nmi_handler(int, void *, struct pt_regs *); +irqreturn_t mac_debug_handler(int, void *, struct pt_regs *); + +/* #define DEBUG_MACINTS */ + +void mac_init_IRQ(void) +{ + int i; + +#ifdef DEBUG_MACINTS + printk("mac_init_IRQ(): Setting things up...\n"); +#endif + /* Initialize the IRQ handler lists. Initially each list is empty, */ + + for (i = 0; i < NUM_MAC_SOURCES; i++) { + mac_irq_list[i] = NULL; + } + + scc_mask = 0; + + /* Make sure the SONIC interrupt is cleared or things get ugly */ +#ifdef SHUTUP_SONIC + printk("Killing onboard sonic... "); + /* This address should hopefully be mapped already */ + if (hwreg_present((void*)(0x50f0a000))) { + *(long *)(0x50f0a014) = 0x7fffL; + *(long *)(0x50f0a010) = 0L; + } + printk("Done.\n"); +#endif /* SHUTUP_SONIC */ + + /* + * Now register the handlers for the master IRQ handlers + * at levels 1-7. Most of the work is done elsewhere. + */ + + if (oss_present) { + oss_register_interrupts(); + } else { + via_register_interrupts(); + } + if (psc_present) psc_register_interrupts(); + if (baboon_present) baboon_register_interrupts(); + iop_register_interrupts(); + cpu_request_irq(7, mac_nmi_handler, IRQ_FLG_LOCK, "NMI", + mac_nmi_handler); +#ifdef DEBUG_MACINTS + printk("mac_init_IRQ(): Done!\n"); +#endif +} + +/* + * Routines to work with irq_node_t's on linked lists lifted from + * the Amiga code written by Roman Zippel. + */ + +static inline void mac_insert_irq(irq_node_t **list, irq_node_t *node) +{ + unsigned long flags; + irq_node_t *cur; + + if (!node->dev_id) + printk("%s: Warning: dev_id of %s is zero\n", + __FUNCTION__, node->devname); + + local_irq_save(flags); + + cur = *list; + + if (node->flags & IRQ_FLG_FAST) { + node->flags &= ~IRQ_FLG_SLOW; + while (cur && cur->flags & IRQ_FLG_FAST) { + list = &cur->next; + cur = cur->next; + } + } else if (node->flags & IRQ_FLG_SLOW) { + while (cur) { + list = &cur->next; + cur = cur->next; + } + } else { + while (cur && !(cur->flags & IRQ_FLG_SLOW)) { + list = &cur->next; + cur = cur->next; + } + } + + node->next = cur; + *list = node; + + local_irq_restore(flags); +} + +static inline void mac_delete_irq(irq_node_t **list, void *dev_id) +{ + unsigned long flags; + irq_node_t *node; + + local_irq_save(flags); + + for (node = *list; node; list = &node->next, node = *list) { + if (node->dev_id == dev_id) { + *list = node->next; + /* Mark it as free. */ + node->handler = NULL; + local_irq_restore(flags); + return; + } + } + local_irq_restore(flags); + printk ("%s: tried to remove invalid irq\n", __FUNCTION__); +} + +/* + * Call all the handlers for a given interrupt. Fast handlers are called + * first followed by slow handlers. + * + * This code taken from the original Amiga code written by Roman Zippel. + */ + +void mac_do_irq_list(int irq, struct pt_regs *fp) +{ + irq_node_t *node, *slow_nodes; + unsigned long flags; + + kstat_cpu(0).irqs[irq]++; + +#ifdef DEBUG_SPURIOUS + if (!mac_irq_list[irq] && (console_loglevel > 7)) { + printk("mac_do_irq_list: spurious interrupt %d!\n", irq); + return; + } +#endif + + /* serve first fast and normal handlers */ + for (node = mac_irq_list[irq]; + node && (!(node->flags & IRQ_FLG_SLOW)); + node = node->next) + node->handler(irq, node->dev_id, fp); + if (!node) return; + local_save_flags(flags); + local_irq_restore((flags & ~0x0700) | (fp->sr & 0x0700)); + /* if slow handlers exists, serve them now */ + slow_nodes = node; + for (; node; node = node->next) { + node->handler(irq, node->dev_id, fp); + } +} + +/* + * mac_enable_irq - enable an interrupt source + * mac_disable_irq - disable an interrupt source + * mac_clear_irq - clears a pending interrupt + * mac_pending_irq - Returns the pending status of an IRQ (nonzero = pending) + * + * These routines are just dispatchers to the VIA/OSS/PSC routines. + */ + +void mac_enable_irq (unsigned int irq) +{ + int irq_src = IRQ_SRC(irq); + + switch(irq_src) { + case 1: via_irq_enable(irq); + break; + case 2: + case 7: if (oss_present) { + oss_irq_enable(irq); + } else { + via_irq_enable(irq); + } + break; + case 3: + case 4: + case 5: + case 6: if (psc_present) { + psc_irq_enable(irq); + } else if (oss_present) { + oss_irq_enable(irq); + } else if (irq_src == 4) { + scc_irq_enable(irq); + } + break; + case 8: if (baboon_present) { + baboon_irq_enable(irq); + } + break; + } +} + +void mac_disable_irq (unsigned int irq) +{ + int irq_src = IRQ_SRC(irq); + + switch(irq_src) { + case 1: via_irq_disable(irq); + break; + case 2: + case 7: if (oss_present) { + oss_irq_disable(irq); + } else { + via_irq_disable(irq); + } + break; + case 3: + case 4: + case 5: + case 6: if (psc_present) { + psc_irq_disable(irq); + } else if (oss_present) { + oss_irq_disable(irq); + } else if (irq_src == 4) { + scc_irq_disable(irq); + } + break; + case 8: if (baboon_present) { + baboon_irq_disable(irq); + } + break; + } +} + +void mac_clear_irq( unsigned int irq ) +{ + switch(IRQ_SRC(irq)) { + case 1: via_irq_clear(irq); + break; + case 2: + case 7: if (oss_present) { + oss_irq_clear(irq); + } else { + via_irq_clear(irq); + } + break; + case 3: + case 4: + case 5: + case 6: if (psc_present) { + psc_irq_clear(irq); + } else if (oss_present) { + oss_irq_clear(irq); + } + break; + case 8: if (baboon_present) { + baboon_irq_clear(irq); + } + break; + } +} + +int mac_irq_pending( unsigned int irq ) +{ + switch(IRQ_SRC(irq)) { + case 1: return via_irq_pending(irq); + case 2: + case 7: if (oss_present) { + return oss_irq_pending(irq); + } else { + return via_irq_pending(irq); + } + case 3: + case 4: + case 5: + case 6: if (psc_present) { + return psc_irq_pending(irq); + } else if (oss_present) { + return oss_irq_pending(irq); + } + } + return 0; +} + +/* + * Add an interrupt service routine to an interrupt source. + * Returns 0 on success. + * + * FIXME: You can register interrupts on nonexistent source (ie PSC4 on a + * non-PSC machine). We should return -EINVAL in those cases. + */ + +int mac_request_irq(unsigned int irq, + irqreturn_t (*handler)(int, void *, struct pt_regs *), + unsigned long flags, const char *devname, void *dev_id) +{ + irq_node_t *node; + +#ifdef DEBUG_MACINTS + printk ("%s: irq %d requested for %s\n", __FUNCTION__, irq, devname); +#endif + + if (irq < VIA1_SOURCE_BASE) { + return cpu_request_irq(irq, handler, flags, devname, dev_id); + } + + if (irq >= NUM_MAC_SOURCES) { + printk ("%s: unknown irq %d requested by %s\n", + __FUNCTION__, irq, devname); + } + + /* Get a node and stick it onto the right list */ + + if (!(node = new_irq_node())) return -ENOMEM; + + node->handler = handler; + node->flags = flags; + node->dev_id = dev_id; + node->devname = devname; + node->next = NULL; + mac_insert_irq(&mac_irq_list[irq], node); + + /* Now enable the IRQ source */ + + mac_enable_irq(irq); + + return 0; +} + +/* + * Removes an interrupt service routine from an interrupt source. + */ + +void mac_free_irq(unsigned int irq, void *dev_id) +{ +#ifdef DEBUG_MACINTS + printk ("%s: irq %d freed by %p\n", __FUNCTION__, irq, dev_id); +#endif + + if (irq < VIA1_SOURCE_BASE) { + cpu_free_irq(irq, dev_id); + return; + } + + if (irq >= NUM_MAC_SOURCES) { + printk ("%s: unknown irq %d freed\n", + __FUNCTION__, irq); + return; + } + + mac_delete_irq(&mac_irq_list[irq], dev_id); + + /* If the list for this interrupt is */ + /* empty then disable the source. */ + + if (!mac_irq_list[irq]) { + mac_disable_irq(irq); + } +} + +/* + * Generate a pretty listing for /proc/interrupts + * + * By the time we're called the autovector interrupt list has already been + * generated, so we just need to do the machspec interrupts. + * + * 990506 (jmt) - rewritten to handle chained machspec interrupt handlers. + * Also removed display of num_spurious it is already + * displayed for us as autovector irq 0. + */ + +int show_mac_interrupts(struct seq_file *p, void *v) +{ + int i; + irq_node_t *node; + char *base; + + /* Don't do Nubus interrupts in this loop; we do them separately */ + /* below so that we can print slot numbers instead of IRQ numbers */ + + for (i = VIA1_SOURCE_BASE ; i < NUM_MAC_SOURCES ; ++i) { + + /* Nonexistant interrupt or nothing registered; skip it. */ + + if ((node = mac_irq_list[i]) == NULL) continue; + if (node->flags & IRQ_FLG_STD) continue; + + base = ""; + switch(IRQ_SRC(i)) { + case 1: base = "via1"; + break; + case 2: if (oss_present) { + base = "oss"; + } else { + base = "via2"; + } + break; + case 3: + case 4: + case 5: + case 6: if (psc_present) { + base = "psc"; + } else if (oss_present) { + base = "oss"; + } else { + if (IRQ_SRC(i) == 4) base = "scc"; + } + break; + case 7: base = "nbus"; + break; + case 8: base = "bbn"; + break; + } + seq_printf(p, "%4s %2d: %10u ", base, i, kstat_cpu(0).irqs[i]); + + do { + if (node->flags & IRQ_FLG_FAST) { + seq_puts(p, "F "); + } else if (node->flags & IRQ_FLG_SLOW) { + seq_puts(p, "S "); + } else { + seq_puts(p, " "); + } + seq_printf(p, "%s\n", node->devname); + if ((node = node->next)) { + seq_puts(p, " "); + } + } while(node); + + } + return 0; +} + +void mac_default_handler(int irq, void *dev_id, struct pt_regs *regs) +{ +#ifdef DEBUG_SPURIOUS + printk("Unexpected IRQ %d on device %p\n", irq, dev_id); +#endif +} + +static int num_debug[8]; + +irqreturn_t mac_debug_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + if (num_debug[irq] < 10) { + printk("DEBUG: Unexpected IRQ %d\n", irq); + num_debug[irq]++; + } + return IRQ_HANDLED; +} + +static int in_nmi; +static volatile int nmi_hold; + +irqreturn_t mac_nmi_handler(int irq, void *dev_id, struct pt_regs *fp) +{ + int i; + /* + * generate debug output on NMI switch if 'debug' kernel option given + * (only works with Penguin!) + */ + + in_nmi++; + for (i=0; i<100; i++) + udelay(1000); + + if (in_nmi == 1) { + nmi_hold = 1; + printk("... pausing, press NMI to resume ..."); + } else { + printk(" ok!\n"); + nmi_hold = 0; + } + + barrier(); + + while (nmi_hold == 1) + udelay(1000); + + if ( console_loglevel >= 8 ) { +#if 0 + show_state(); + printk("PC: %08lx\nSR: %04x SP: %p\n", fp->pc, fp->sr, fp); + printk("d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", + fp->d0, fp->d1, fp->d2, fp->d3); + printk("d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n", + fp->d4, fp->d5, fp->a0, fp->a1); + + if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page) + printk("Corrupted stack page\n"); + printk("Process %s (pid: %d, stackpage=%08lx)\n", + current->comm, current->pid, current->kernel_stack_page); + if (intr_count == 1) + dump_stack((struct frame *)fp); +#else + /* printk("NMI "); */ +#endif + } + in_nmi--; + return IRQ_HANDLED; +} + +/* + * Simple routines for masking and unmasking + * SCC interrupts in cases where this can't be + * done in hardware (only the PSC can do that.) + */ + +static void scc_irq_enable(int irq) { + int irq_idx = IRQ_IDX(irq); + + scc_mask |= (1 << irq_idx); +} + +static void scc_irq_disable(int irq) { + int irq_idx = IRQ_IDX(irq); + + scc_mask &= ~(1 << irq_idx); +} + +/* + * SCC master interrupt handler. We have to do a bit of magic here + * to figure out what channel gave us the interrupt; putting this + * here is cleaner than hacking it into drivers/char/macserial.c. + */ + +void mac_scc_dispatch(int irq, void *dev_id, struct pt_regs *regs) +{ + volatile unsigned char *scc = (unsigned char *) mac_bi_data.sccbase + 2; + unsigned char reg; + unsigned long flags; + + /* Read RR3 from the chip. Always do this on channel A */ + /* This must be an atomic operation so disable irqs. */ + + local_irq_save(flags); + *scc = 3; + reg = *scc; + local_irq_restore(flags); + + /* Now dispatch. Bits 0-2 are for channel B and */ + /* bits 3-5 are for channel A. We can safely */ + /* ignore the remaining bits here. */ + /* */ + /* Note that we're ignoring scc_mask for now. */ + /* If we actually mask the ints then we tend to */ + /* get hammered by very persistent SCC irqs, */ + /* and since they're autovector interrupts they */ + /* pretty much kill the system. */ + + if (reg & 0x38) mac_do_irq_list(IRQ_SCCA, regs); + if (reg & 0x07) mac_do_irq_list(IRQ_SCCB, regs); +} 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 @@ +/* + * Miscellaneous Mac68K-specific stuff + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/miscdevice.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/rtc.h> +#include <linux/mm.h> + +#include <linux/adb.h> +#include <linux/cuda.h> +#include <linux/pmu.h> + +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/rtc.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/setup.h> +#include <asm/macintosh.h> +#include <asm/mac_via.h> +#include <asm/mac_oss.h> + +#define BOOTINFO_COMPAT_1_0 +#include <asm/bootinfo.h> +#include <asm/machdep.h> + +/* Offset between Unix time (1970-based) and Mac time (1904-based) */ + +#define RTC_OFFSET 2082844800 + +extern struct mac_booter_data mac_bi_data; +static void (*rom_reset)(void); + +#ifdef CONFIG_ADB +/* + * Return the current time as the number of seconds since January 1, 1904. + */ + +static long adb_read_time(void) +{ + volatile struct adb_request req; + long time; + + adb_request((struct adb_request *) &req, NULL, + ADBREQ_RAW|ADBREQ_SYNC, + 2, CUDA_PACKET, CUDA_GET_TIME); + + time = (req.reply[3] << 24) | (req.reply[4] << 16) + | (req.reply[5] << 8) | req.reply[6]; + return time - RTC_OFFSET; +} + +/* + * Set the current system time + */ + +static void adb_write_time(long data) +{ + volatile struct adb_request req; + + data += RTC_OFFSET; + + adb_request((struct adb_request *) &req, NULL, + ADBREQ_RAW|ADBREQ_SYNC, + 6, CUDA_PACKET, CUDA_SET_TIME, + (data >> 24) & 0xFF, (data >> 16) & 0xFF, + (data >> 8) & 0xFF, data & 0xFF); +} + +/* + * Get a byte from the NVRAM + */ + +static __u8 adb_read_pram(int offset) +{ + volatile struct adb_request req; + + adb_request((struct adb_request *) &req, NULL, + ADBREQ_RAW|ADBREQ_SYNC, + 4, CUDA_PACKET, CUDA_GET_PRAM, + (offset >> 8) & 0xFF, offset & 0xFF); + return req.reply[3]; +} + +/* + * Write a byte to the NVRAM + */ + +static void adb_write_pram(int offset, __u8 data) +{ + volatile struct adb_request req; + + adb_request((struct adb_request *) &req, NULL, + ADBREQ_RAW|ADBREQ_SYNC, + 5, CUDA_PACKET, CUDA_SET_PRAM, + (offset >> 8) & 0xFF, offset & 0xFF, + data); +} +#endif /* CONFIG_ADB */ + +/* + * VIA PRAM/RTC access routines + * + * Must be called with interrupts disabled and + * the RTC should be enabled. + */ + +static __u8 via_pram_readbyte(void) +{ + int i,reg; + __u8 data; + + reg = via1[vBufB] & ~VIA1B_vRTCClk; + + /* Set the RTC data line to be an input. */ + + via1[vDirB] &= ~VIA1B_vRTCData; + + /* The bits of the byte come out in MSB order */ + + data = 0; + for (i = 0 ; i < 8 ; i++) { + via1[vBufB] = reg; + via1[vBufB] = reg | VIA1B_vRTCClk; + data = (data << 1) | (via1[vBufB] & VIA1B_vRTCData); + } + + /* Return RTC data line to output state */ + + via1[vDirB] |= VIA1B_vRTCData; + + return data; +} + +static void via_pram_writebyte(__u8 data) +{ + int i,reg,bit; + + reg = via1[vBufB] & ~(VIA1B_vRTCClk | VIA1B_vRTCData); + + /* The bits of the byte go in in MSB order */ + + for (i = 0 ; i < 8 ; i++) { + bit = data & 0x80? 1 : 0; + data <<= 1; + via1[vBufB] = reg | bit; + via1[vBufB] = reg | bit | VIA1B_vRTCClk; + } +} + +/* + * Execute a VIA PRAM/RTC command. For read commands + * data should point to a one-byte buffer for the + * resulting data. For write commands it should point + * to the data byte to for the command. + * + * This function disables all interrupts while running. + */ + +static void via_pram_command(int command, __u8 *data) +{ + unsigned long flags; + int is_read; + + local_irq_save(flags); + + /* Enable the RTC and make sure the strobe line is high */ + + via1[vBufB] = (via1[vBufB] | VIA1B_vRTCClk) & ~VIA1B_vRTCEnb; + + if (command & 0xFF00) { /* extended (two-byte) command */ + via_pram_writebyte((command & 0xFF00) >> 8); + via_pram_writebyte(command & 0xFF); + is_read = command & 0x8000; + } else { /* one-byte command */ + via_pram_writebyte(command); + is_read = command & 0x80; + } + if (is_read) { + *data = via_pram_readbyte(); + } else { + via_pram_writebyte(*data); + } + + /* All done, disable the RTC */ + + via1[vBufB] |= VIA1B_vRTCEnb; + + local_irq_restore(flags); +} + +static __u8 via_read_pram(int offset) +{ + return 0; +} + +static void via_write_pram(int offset, __u8 data) +{ +} + +/* + * Return the current time in seconds since January 1, 1904. + * + * This only works on machines with the VIA-based PRAM/RTC, which + * is basically any machine with Mac II-style ADB. + */ + +static long via_read_time(void) +{ + union { + __u8 cdata[4]; + long idata; + } result, last_result; + int ct; + + /* + * The NetBSD guys say to loop until you get the same reading + * twice in a row. + */ + + ct = 0; + do { + if (++ct > 10) { + printk("via_read_time: couldn't get valid time, " + "last read = 0x%08lx and 0x%08lx\n", + last_result.idata, result.idata); + break; + } + + last_result.idata = result.idata; + result.idata = 0; + + via_pram_command(0x81, &result.cdata[3]); + via_pram_command(0x85, &result.cdata[2]); + via_pram_command(0x89, &result.cdata[1]); + via_pram_command(0x8D, &result.cdata[0]); + } while (result.idata != last_result.idata); + + return result.idata - RTC_OFFSET; +} + +/* + * Set the current time to a number of seconds since January 1, 1904. + * + * This only works on machines with the VIA-based PRAM/RTC, which + * is basically any machine with Mac II-style ADB. + */ + +static void via_write_time(long time) +{ + union { + __u8 cdata[4]; + long idata; + } data; + __u8 temp; + + /* Clear the write protect bit */ + + temp = 0x55; + via_pram_command(0x35, &temp); + + data.idata = time + RTC_OFFSET; + via_pram_command(0x01, &data.cdata[3]); + via_pram_command(0x05, &data.cdata[2]); + via_pram_command(0x09, &data.cdata[1]); + via_pram_command(0x0D, &data.cdata[0]); + + /* Set the write protect bit */ + + temp = 0xD5; + via_pram_command(0x35, &temp); +} + +static void via_shutdown(void) +{ + if (rbv_present) { + via2[rBufB] &= ~0x04; + } else { + /* Direction of vDirB is output */ + via2[vDirB] |= 0x04; + /* Send a value of 0 on that line */ + via2[vBufB] &= ~0x04; + mdelay(1000); + } +} + +/* + * FIXME: not sure how this is supposed to work exactly... + */ + +static void oss_shutdown(void) +{ + oss->rom_ctrl = OSS_POWEROFF; +} + +#ifdef CONFIG_ADB_CUDA + +static void cuda_restart(void) +{ + adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, + 2, CUDA_PACKET, CUDA_RESET_SYSTEM); +} + +static void cuda_shutdown(void) +{ + adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, + 2, CUDA_PACKET, CUDA_POWERDOWN); +} + +#endif /* CONFIG_ADB_CUDA */ + +#ifdef CONFIG_ADB_PMU + +void pmu_restart(void) +{ + adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, + 3, PMU_PACKET, PMU_SET_INTR_MASK, + PMU_INT_ADB|PMU_INT_TICK); + + adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, + 2, PMU_PACKET, PMU_RESET); +} + +void pmu_shutdown(void) +{ + adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, + 3, PMU_PACKET, PMU_SET_INTR_MASK, + PMU_INT_ADB|PMU_INT_TICK); + + adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, + 6, PMU_PACKET, PMU_SHUTDOWN, + 'M', 'A', 'T', 'T'); +} + +#endif /* CONFIG_ADB_PMU */ + +/* + *------------------------------------------------------------------- + * Below this point are the generic routines; they'll dispatch to the + * correct routine for the hardware on which we're running. + *------------------------------------------------------------------- + */ + +void mac_pram_read(int offset, __u8 *buffer, int len) +{ + __u8 (*func)(int) = NULL; + int i; + + if (macintosh_config->adb_type == MAC_ADB_IISI || + macintosh_config->adb_type == MAC_ADB_PB1 || + macintosh_config->adb_type == MAC_ADB_PB2 || + macintosh_config->adb_type == MAC_ADB_CUDA) { +#ifdef CONFIG_ADB + func = adb_read_pram; +#else + return; +#endif + } else { + func = via_read_pram; + } + for (i = 0 ; i < len ; i++) { + buffer[i] = (*func)(offset++); + } +} + +void mac_pram_write(int offset, __u8 *buffer, int len) +{ + void (*func)(int, __u8) = NULL; + int i; + + if (macintosh_config->adb_type == MAC_ADB_IISI || + macintosh_config->adb_type == MAC_ADB_PB1 || + macintosh_config->adb_type == MAC_ADB_PB2 || + macintosh_config->adb_type == MAC_ADB_CUDA) { +#ifdef CONFIG_ADB + func = adb_write_pram; +#else + return; +#endif + } else { + func = via_write_pram; + } + for (i = 0 ; i < len ; i++) { + (*func)(offset++, buffer[i]); + } +} + +void mac_poweroff(void) +{ + /* + * MAC_ADB_IISI may need to be moved up here if it doesn't actually + * work using the ADB packet method. --David Kilzer + */ + + if (oss_present) { + oss_shutdown(); + } else if (macintosh_config->adb_type == MAC_ADB_II) { + via_shutdown(); +#ifdef CONFIG_ADB_CUDA + } else if (macintosh_config->adb_type == MAC_ADB_CUDA) { + cuda_shutdown(); +#endif +#ifdef CONFIG_ADB_PMU + } else if (macintosh_config->adb_type == MAC_ADB_PB1 + || macintosh_config->adb_type == MAC_ADB_PB2) { + pmu_shutdown(); +#endif + } + local_irq_enable(); + printk("It is now safe to turn off your Macintosh.\n"); + while(1); +} + +void mac_reset(void) +{ + if (macintosh_config->adb_type == MAC_ADB_II) { + unsigned long flags; + + /* need ROMBASE in booter */ + /* indeed, plus need to MAP THE ROM !! */ + + if (mac_bi_data.rombase == 0) + mac_bi_data.rombase = 0x40800000; + + /* works on some */ + rom_reset = (void *) (mac_bi_data.rombase + 0xa); + + if (macintosh_config->ident == MAC_MODEL_SE30) { + /* + * MSch: Machines known to crash on ROM reset ... + */ + } else { + local_irq_save(flags); + + rom_reset(); + + local_irq_restore(flags); + } +#ifdef CONFIG_ADB_CUDA + } else if (macintosh_config->adb_type == MAC_ADB_CUDA) { + cuda_restart(); +#endif +#ifdef CONFIG_ADB_PMU + } else if (macintosh_config->adb_type == MAC_ADB_PB1 + || macintosh_config->adb_type == MAC_ADB_PB2) { + pmu_restart(); +#endif + } else if (CPU_IS_030) { + + /* 030-specific reset routine. The idea is general, but the + * specific registers to reset are '030-specific. Until I + * have a non-030 machine, I can't test anything else. + * -- C. Scott Ananian <cananian@alumni.princeton.edu> + */ + + unsigned long rombase = 0x40000000; + + /* make a 1-to-1 mapping, using the transparent tran. reg. */ + unsigned long virt = (unsigned long) mac_reset; + unsigned long phys = virt_to_phys(mac_reset); + unsigned long offset = phys-virt; + local_irq_disable(); /* lets not screw this up, ok? */ + __asm__ __volatile__(".chip 68030\n\t" + "pmove %0,%/tt0\n\t" + ".chip 68k" + : : "m" ((phys&0xFF000000)|0x8777)); + /* Now jump to physical address so we can disable MMU */ + __asm__ __volatile__( + ".chip 68030\n\t" + "lea %/pc@(1f),%/a0\n\t" + "addl %0,%/a0\n\t"/* fixup target address and stack ptr */ + "addl %0,%/sp\n\t" + "pflusha\n\t" + "jmp %/a0@\n\t" /* jump into physical memory */ + "0:.long 0\n\t" /* a constant zero. */ + /* OK. Now reset everything and jump to reset vector. */ + "1:\n\t" + "lea %/pc@(0b),%/a0\n\t" + "pmove %/a0@, %/tc\n\t" /* disable mmu */ + "pmove %/a0@, %/tt0\n\t" /* disable tt0 */ + "pmove %/a0@, %/tt1\n\t" /* disable tt1 */ + "movel #0, %/a0\n\t" + "movec %/a0, %/vbr\n\t" /* clear vector base register */ + "movec %/a0, %/cacr\n\t" /* disable caches */ + "movel #0x0808,%/a0\n\t" + "movec %/a0, %/cacr\n\t" /* flush i&d caches */ + "movew #0x2700,%/sr\n\t" /* set up status register */ + "movel %1@(0x0),%/a0\n\t"/* load interrupt stack pointer */ + "movec %/a0, %/isp\n\t" + "movel %1@(0x4),%/a0\n\t" /* load reset vector */ + "reset\n\t" /* reset external devices */ + "jmp %/a0@\n\t" /* jump to the reset vector */ + ".chip 68k" + : : "r" (offset), "a" (rombase) : "a0"); + } + + /* should never get here */ + local_irq_enable(); + printk ("Restart failed. Please restart manually.\n"); + while(1); +} + +/* + * This function translates seconds since 1970 into a proper date. + * + * Algorithm cribbed from glibc2.1, __offtime(). + */ +#define SECS_PER_MINUTE (60) +#define SECS_PER_HOUR (SECS_PER_MINUTE * 60) +#define SECS_PER_DAY (SECS_PER_HOUR * 24) + +static void unmktime(unsigned long time, long offset, + int *yearp, int *monp, int *dayp, + int *hourp, int *minp, int *secp) +{ + /* How many days come before each month (0-12). */ + static const unsigned short int __mon_yday[2][13] = + { + /* Normal years. */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* Leap years. */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } + }; + long int days, rem, y, wday, yday; + const unsigned short int *ip; + + days = time / SECS_PER_DAY; + rem = time % SECS_PER_DAY; + rem += offset; + while (rem < 0) { + rem += SECS_PER_DAY; + --days; + } + while (rem >= SECS_PER_DAY) { + rem -= SECS_PER_DAY; + ++days; + } + *hourp = rem / SECS_PER_HOUR; + rem %= SECS_PER_HOUR; + *minp = rem / SECS_PER_MINUTE; + *secp = rem % SECS_PER_MINUTE; + /* January 1, 1970 was a Thursday. */ + wday = (4 + days) % 7; /* Day in the week. Not currently used */ + if (wday < 0) wday += 7; + y = 1970; + +#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) +#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) +#define __isleap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) + + while (days < 0 || days >= (__isleap (y) ? 366 : 365)) + { + /* Guess a corrected year, assuming 365 days per year. */ + long int yg = y + days / 365 - (days % 365 < 0); + + /* Adjust DAYS and Y to match the guessed year. */ + days -= ((yg - y) * 365 + + LEAPS_THRU_END_OF (yg - 1) + - LEAPS_THRU_END_OF (y - 1)); + y = yg; + } + *yearp = y - 1900; + yday = days; /* day in the year. Not currently used. */ + ip = __mon_yday[__isleap(y)]; + for (y = 11; days < (long int) ip[y]; --y) + continue; + days -= ip[y]; + *monp = y; + *dayp = days + 1; /* day in the month */ + return; +} + +/* + * Read/write the hardware clock. + */ + +int mac_hwclk(int op, struct rtc_time *t) +{ + unsigned long now; + + if (!op) { /* read */ + if (macintosh_config->adb_type == MAC_ADB_II) { + now = via_read_time(); + } else +#ifdef CONFIG_ADB + if ((macintosh_config->adb_type == MAC_ADB_IISI) || + (macintosh_config->adb_type == MAC_ADB_PB1) || + (macintosh_config->adb_type == MAC_ADB_PB2) || + (macintosh_config->adb_type == MAC_ADB_CUDA)) { + now = adb_read_time(); + } else +#endif + if (macintosh_config->adb_type == MAC_ADB_IOP) { + now = via_read_time(); + } else { + now = 0; + } + + t->tm_wday = 0; + unmktime(now, 0, + &t->tm_year, &t->tm_mon, &t->tm_mday, + &t->tm_hour, &t->tm_min, &t->tm_sec); + printk("mac_hwclk: read %04d-%02d-%-2d %02d:%02d:%02d\n", + t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); + } else { /* write */ + printk("mac_hwclk: tried to write %04d-%02d-%-2d %02d:%02d:%02d\n", + t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); + +#if 0 /* it trashes my rtc */ + now = mktime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + + if (macintosh_config->adb_type == MAC_ADB_II) { + via_write_time(now); + } else if ((macintosh_config->adb_type == MAC_ADB_IISI) || + (macintosh_config->adb_type == MAC_ADB_PB1) || + (macintosh_config->adb_type == MAC_ADB_PB2) || + (macintosh_config->adb_type == MAC_ADB_CUDA)) { + adb_write_time(now); + } else if (macintosh_config->adb_type == MAC_ADB_IOP) { + via_write_time(now); + } +#endif + } + return 0; +} + +/* + * Set minutes/seconds in the hardware clock + */ + +int mac_set_clock_mmss (unsigned long nowtime) +{ + struct rtc_time now; + + mac_hwclk(0, &now); + now.tm_sec = nowtime % 60; + now.tm_min = (nowtime / 60) % 60; + mac_hwclk(1, &now); + + return 0; +} 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 @@ +/* + * OSS handling + * Written by Joshua M. Thompson (funaho@jurai.org) + * + * + * This chip is used in the IIfx in place of VIA #2. It acts like a fancy + * VIA chip with prorammable interrupt levels. + * + * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some + * recent insights into OSS operational details. + * 990610 (jmt) - Now taking fulll advantage of the OSS. Interrupts are mapped + * to mostly match the A/UX interrupt scheme supported on the + * VIA side. Also added support for enabling the ISM irq again + * since we now have a functional IOP manager. + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/init.h> + +#include <asm/bootinfo.h> +#include <asm/machw.h> +#include <asm/macintosh.h> +#include <asm/macints.h> +#include <asm/mac_via.h> +#include <asm/mac_oss.h> + +int oss_present; +volatile struct mac_oss *oss; + +irqreturn_t oss_irq(int, void *, struct pt_regs *); +irqreturn_t oss_nubus_irq(int, void *, struct pt_regs *); + +extern irqreturn_t via1_irq(int, void *, struct pt_regs *); +extern irqreturn_t mac_scc_dispatch(int, void *, struct pt_regs *); + +/* + * Initialize the OSS + * + * The OSS "detection" code is actually in via_init() which is always called + * before us. Thus we can count on oss_present being valid on entry. + */ + +void __init oss_init(void) +{ + int i; + + if (!oss_present) return; + + oss = (struct mac_oss *) OSS_BASE; + + /* Disable all interrupts. Unlike a VIA it looks like we */ + /* do this by setting the source's interrupt level to zero. */ + + for (i = 0; i <= OSS_NUM_SOURCES; i++) { + oss->irq_level[i] = OSS_IRQLEV_DISABLED; + } + /* If we disable VIA1 here, we never really handle it... */ + oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1; +} + +/* + * Register the OSS and NuBus interrupt dispatchers. + */ + +void __init oss_register_interrupts(void) +{ + cpu_request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK, + "scsi", (void *) oss); + cpu_request_irq(OSS_IRQLEV_IOPSCC, mac_scc_dispatch, IRQ_FLG_LOCK, + "scc", mac_scc_dispatch); + cpu_request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK, + "nubus", (void *) oss); + cpu_request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK, + "sound", (void *) oss); + cpu_request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK, + "via1", (void *) via1); +} + +/* + * Initialize OSS for Nubus access + */ + +void __init oss_nubus_init(void) +{ +} + +/* + * Handle miscellaneous OSS interrupts. Right now that's just sound + * and SCSI; everything else is routed to its own autovector IRQ. + */ + +irqreturn_t oss_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int events; + + events = oss->irq_pending & (OSS_IP_SOUND|OSS_IP_SCSI); + if (!events) + return IRQ_NONE; + +#ifdef DEBUG_IRQS + if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) { + printk("oss_irq: irq %d events = 0x%04X\n", irq, + (int) oss->irq_pending); + } +#endif + /* FIXME: how do you clear a pending IRQ? */ + + if (events & OSS_IP_SOUND) { + /* FIXME: call sound handler */ + oss->irq_pending &= ~OSS_IP_SOUND; + } else if (events & OSS_IP_SCSI) { + oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED; + mac_do_irq_list(IRQ_MAC_SCSI, regs); + oss->irq_pending &= ~OSS_IP_SCSI; + oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI; + } else { + /* FIXME: error check here? */ + } + return IRQ_HANDLED; +} + +/* + * Nubus IRQ handler, OSS style + * + * Unlike the VIA/RBV this is on its own autovector interrupt level. + */ + +irqreturn_t oss_nubus_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int events, irq_bit, i; + + events = oss->irq_pending & OSS_IP_NUBUS; + if (!events) + return IRQ_NONE; + +#ifdef DEBUG_NUBUS_INT + if (console_loglevel > 7) { + printk("oss_nubus_irq: events = 0x%04X\n", events); + } +#endif + /* There are only six slots on the OSS, not seven */ + + for (i = 0, irq_bit = 1 ; i < 6 ; i++, irq_bit <<= 1) { + if (events & irq_bit) { + oss->irq_level[i] = OSS_IRQLEV_DISABLED; + mac_do_irq_list(NUBUS_SOURCE_BASE + i, regs); + oss->irq_pending &= ~irq_bit; + oss->irq_level[i] = OSS_IRQLEV_NUBUS; + } + } + return IRQ_HANDLED; +} + +/* + * Enable an OSS interrupt + * + * It looks messy but it's rather straightforward. The switch() statement + * just maps the machspec interrupt numbers to the right OSS interrupt + * source (if the OSS handles that interrupt) and then sets the interrupt + * level for that source to nonzero, thus enabling the interrupt. + */ + +void oss_irq_enable(int irq) { +#ifdef DEBUG_IRQUSE + printk("oss_irq_enable(%d)\n", irq); +#endif + switch(irq) { + case IRQ_SCC: + case IRQ_SCCA: + case IRQ_SCCB: + oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC; + break; + case IRQ_MAC_ADB: + oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM; + break; + case IRQ_MAC_SCSI: + oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI; + break; + case IRQ_NUBUS_9: + case IRQ_NUBUS_A: + case IRQ_NUBUS_B: + case IRQ_NUBUS_C: + case IRQ_NUBUS_D: + case IRQ_NUBUS_E: + irq -= NUBUS_SOURCE_BASE; + oss->irq_level[irq] = OSS_IRQLEV_NUBUS; + break; +#ifdef DEBUG_IRQUSE + default: + printk("%s unknown irq %d\n",__FUNCTION__, irq); + break; +#endif + } +} + +/* + * Disable an OSS interrupt + * + * Same as above except we set the source's interrupt level to zero, + * to disable the interrupt. + */ + +void oss_irq_disable(int irq) { +#ifdef DEBUG_IRQUSE + printk("oss_irq_disable(%d)\n", irq); +#endif + switch(irq) { + case IRQ_SCC: + case IRQ_SCCA: + case IRQ_SCCB: + oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_DISABLED; + break; + case IRQ_MAC_ADB: + oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_DISABLED; + break; + case IRQ_MAC_SCSI: + oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED; + break; + case IRQ_NUBUS_9: + case IRQ_NUBUS_A: + case IRQ_NUBUS_B: + case IRQ_NUBUS_C: + case IRQ_NUBUS_D: + case IRQ_NUBUS_E: + irq -= NUBUS_SOURCE_BASE; + oss->irq_level[irq] = OSS_IRQLEV_DISABLED; + break; +#ifdef DEBUG_IRQUSE + default: + printk("%s unknown irq %d\n", __FUNCTION__, irq); + break; +#endif + } +} + +/* + * Clear an OSS interrupt + * + * Not sure if this works or not but it's the only method I could + * think of based on the contents of the mac_oss structure. + */ + +void oss_irq_clear(int irq) { + /* FIXME: how to do this on OSS? */ + switch(irq) { + case IRQ_SCC: + case IRQ_SCCA: + case IRQ_SCCB: + oss->irq_pending &= ~OSS_IP_IOPSCC; + break; + case IRQ_MAC_ADB: + oss->irq_pending &= ~OSS_IP_IOPISM; + break; + case IRQ_MAC_SCSI: + oss->irq_pending &= ~OSS_IP_SCSI; + break; + case IRQ_NUBUS_9: + case IRQ_NUBUS_A: + case IRQ_NUBUS_B: + case IRQ_NUBUS_C: + case IRQ_NUBUS_D: + case IRQ_NUBUS_E: + irq -= NUBUS_SOURCE_BASE; + oss->irq_pending &= ~(1 << irq); + break; + } +} + +/* + * Check to see if a specific OSS interrupt is pending + */ + +int oss_irq_pending(int irq) +{ + switch(irq) { + case IRQ_SCC: + case IRQ_SCCA: + case IRQ_SCCB: + return oss->irq_pending & OSS_IP_IOPSCC; + break; + case IRQ_MAC_ADB: + return oss->irq_pending & OSS_IP_IOPISM; + break; + case IRQ_MAC_SCSI: + return oss->irq_pending & OSS_IP_SCSI; + break; + case IRQ_NUBUS_9: + case IRQ_NUBUS_A: + case IRQ_NUBUS_B: + case IRQ_NUBUS_C: + case IRQ_NUBUS_D: + case IRQ_NUBUS_E: + irq -= NUBUS_SOURCE_BASE; + return oss->irq_pending & (1 << irq); + break; + } + return 0; +} 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 @@ +/* + * Apple Peripheral System Controller (PSC) + * + * The PSC is used on the AV Macs to control IO functions not handled + * by the VIAs (Ethernet, DSP, SCC). + * + * TO DO: + * + * Try to figure out what's going on in pIFR5 and pIFR6. There seem to be + * persisant interrupt conditions in those registers and I have no idea what + * they are. Granted it doesn't affect since we're not enabling any interrupts + * on those levels at the moment, but it would be nice to know. I have a feeling + * they aren't actually interrupt lines but data lines (to the DSP?) + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/init.h> + +#include <asm/traps.h> +#include <asm/bootinfo.h> +#include <asm/macintosh.h> +#include <asm/macints.h> +#include <asm/mac_psc.h> + +#define DEBUG_PSC + +int psc_present; +volatile __u8 *psc; + +irqreturn_t psc_irq(int, void *, struct pt_regs *); + +/* + * Debugging dump, used in various places to see what's going on. + */ + +void psc_debug_dump(void) +{ + int i; + + if (!psc_present) return; + for (i = 0x30 ; i < 0x70 ; i += 0x10) { + printk("PSC #%d: IFR = 0x%02X IER = 0x%02X\n", + i >> 4, + (int) psc_read_byte(pIFRbase + i), + (int) psc_read_byte(pIERbase + i)); + } +} + +/* + * Try to kill all DMA channels on the PSC. Not sure how this his + * supposed to work; this is code lifted from macmace.c and then + * expanded to cover what I think are the other 7 channels. + */ + +void psc_dma_die_die_die(void) +{ + int i; + + printk("Killing all PSC DMA channels..."); + for (i = 0 ; i < 9 ; i++) { + psc_write_word(PSC_CTL_BASE + (i << 4), 0x8800); + psc_write_word(PSC_CTL_BASE + (i << 4), 0x1000); + psc_write_word(PSC_CMD_BASE + (i << 5), 0x1100); + psc_write_word(PSC_CMD_BASE + (i << 5) + 0x10, 0x1100); + } + printk("done!\n"); +} + +/* + * Initialize the PSC. For now this just involves shutting down all + * interrupt sources using the IERs. + */ + +void __init psc_init(void) +{ + int i; + + if (macintosh_config->ident != MAC_MODEL_C660 + && macintosh_config->ident != MAC_MODEL_Q840) + { + psc = NULL; + psc_present = 0; + return; + } + + /* + * The PSC is always at the same spot, but using psc + * keeps things consisant with the psc_xxxx functions. + */ + + psc = (void *) PSC_BASE; + psc_present = 1; + + printk("PSC detected at %p\n", psc); + + psc_dma_die_die_die(); + +#ifdef DEBUG_PSC + psc_debug_dump(); +#endif + /* + * Mask and clear all possible interrupts + */ + + for (i = 0x30 ; i < 0x70 ; i += 0x10) { + psc_write_byte(pIERbase + i, 0x0F); + psc_write_byte(pIFRbase + i, 0x0F); + } +} + +/* + * Register the PSC interrupt dispatchers for autovector interrupts 3-6. + */ + +void __init psc_register_interrupts(void) +{ + cpu_request_irq(3, psc_irq, IRQ_FLG_LOCK, "psc3", (void *) 0x30); + cpu_request_irq(4, psc_irq, IRQ_FLG_LOCK, "psc4", (void *) 0x40); + cpu_request_irq(5, psc_irq, IRQ_FLG_LOCK, "psc5", (void *) 0x50); + cpu_request_irq(6, psc_irq, IRQ_FLG_LOCK, "psc6", (void *) 0x60); +} + +/* + * PSC interrupt handler. It's a lot like the VIA interrupt handler. + */ + +irqreturn_t psc_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int pIFR = pIFRbase + ((int) dev_id); + int pIER = pIERbase + ((int) dev_id); + int base_irq; + int irq_bit,i; + unsigned char events; + + base_irq = irq << 3; + +#ifdef DEBUG_IRQS + printk("psc_irq: irq %d pIFR = 0x%02X pIER = 0x%02X\n", + irq, (int) psc_read_byte(pIFR), (int) psc_read_byte(pIER)); +#endif + + events = psc_read_byte(pIFR) & psc_read_byte(pIER) & 0xF; + if (!events) + return IRQ_NONE; + + for (i = 0, irq_bit = 1 ; i < 4 ; i++, irq_bit <<= 1) { + if (events & irq_bit) { + psc_write_byte(pIER, irq_bit); + mac_do_irq_list(base_irq + i, regs); + psc_write_byte(pIFR, irq_bit); + psc_write_byte(pIER, irq_bit | 0x80); + } + } + return IRQ_HANDLED; +} + +void psc_irq_enable(int irq) { + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int pIER = pIERbase + (irq_src << 4); + +#ifdef DEBUG_IRQUSE + printk("psc_irq_enable(%d)\n", irq); +#endif + psc_write_byte(pIER, (1 << irq_idx) | 0x80); +} + +void psc_irq_disable(int irq) { + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int pIER = pIERbase + (irq_src << 4); + +#ifdef DEBUG_IRQUSE + printk("psc_irq_disable(%d)\n", irq); +#endif + psc_write_byte(pIER, 1 << irq_idx); +} + +void psc_irq_clear(int irq) { + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int pIFR = pIERbase + (irq_src << 4); + + psc_write_byte(pIFR, 1 << irq_idx); +} + +int psc_irq_pending(int irq) +{ + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int pIFR = pIERbase + (irq_src << 4); + + return psc_read_byte(pIFR) & (1 << irq_idx); +} 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 @@ +/* + * 6522 Versatile Interface Adapter (VIA) + * + * There are two of these on the Mac II. Some IRQ's are vectored + * via them as are assorted bits and bobs - eg RTC, ADB. + * + * CSA: Motorola seems to have removed documentation on the 6522 from + * their web site; try + * http://nerini.drf.com/vectrex/other/text/chips/6522/ + * http://www.zymurgy.net/classic/vic20/vicdet1.htm + * and + * http://193.23.168.87/mikro_laborversuche/via_iobaustein/via6522_1.html + * for info. A full-text web search on 6522 AND VIA will probably also + * net some usefulness. <cananian@alumni.princeton.edu> 20apr1999 + * + * PRAM/RTC access algorithms are from the NetBSD RTC toolkit version 1.08b + * by Erik Vogan and adapted to Linux by Joshua M. Thompson (funaho@jurai.org) + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/ide.h> + +#include <asm/traps.h> +#include <asm/bootinfo.h> +#include <asm/macintosh.h> +#include <asm/macints.h> +#include <asm/machw.h> +#include <asm/mac_via.h> +#include <asm/mac_psc.h> + +volatile __u8 *via1, *via2; +#if 0 +/* See note in mac_via.h about how this is possibly not useful */ +volatile long *via_memory_bogon=(long *)&via_memory_bogon; +#endif +int rbv_present,via_alt_mapping; +__u8 rbv_clear; + +/* + * Globals for accessing the VIA chip registers without having to + * check if we're hitting a real VIA or an RBV. Normally you could + * just hit the combined register (ie, vIER|rIER) but that seems to + * break on AV Macs...probably because they actually decode more than + * eight address bits. Why can't Apple engineers at least be + * _consistently_ lazy? - 1999-05-21 (jmt) + */ + +static int gIER,gIFR,gBufA,gBufB; + +/* + * Timer defs. + */ + +#define TICK_SIZE 10000 +#define MAC_CLOCK_TICK (783300/HZ) /* ticks per HZ */ +#define MAC_CLOCK_LOW (MAC_CLOCK_TICK&0xFF) +#define MAC_CLOCK_HIGH (MAC_CLOCK_TICK>>8) + +static int nubus_active; + +void via_debug_dump(void); +irqreturn_t via1_irq(int, void *, struct pt_regs *); +irqreturn_t via2_irq(int, void *, struct pt_regs *); +irqreturn_t via_nubus_irq(int, void *, struct pt_regs *); +void via_irq_enable(int irq); +void via_irq_disable(int irq); +void via_irq_clear(int irq); + +extern irqreturn_t mac_bang(int, void *, struct pt_regs *); +extern irqreturn_t mac_scc_dispatch(int, void *, struct pt_regs *); +extern int oss_present; + +/* + * Initialize the VIAs + * + * First we figure out where they actually _are_ as well as what type of + * VIA we have for VIA2 (it could be a real VIA or an RBV or even an OSS.) + * Then we pretty much clear them out and disable all IRQ sources. + * + * Note: the OSS is actually "detected" here and not in oss_init(). It just + * seems more logical to do it here since via_init() needs to know + * these things anyways. + */ + +void __init via_init(void) +{ + switch(macintosh_config->via_type) { + + /* IIci, IIsi, IIvx, IIvi (P6xx), LC series */ + + case MAC_VIA_IIci: + via1 = (void *) VIA1_BASE; + if (macintosh_config->ident == MAC_MODEL_IIFX) { + via2 = NULL; + rbv_present = 0; + oss_present = 1; + } else { + via2 = (void *) RBV_BASE; + rbv_present = 1; + oss_present = 0; + } + if (macintosh_config->ident == MAC_MODEL_LCIII) { + rbv_clear = 0x00; + } else { + /* on most RBVs (& unlike the VIAs), you */ + /* need to set bit 7 when you write to IFR */ + /* in order for your clear to occur. */ + rbv_clear = 0x80; + } + gIER = rIER; + gIFR = rIFR; + gBufA = rSIFR; + gBufB = rBufB; + break; + + /* Quadra and early MacIIs agree on the VIA locations */ + + case MAC_VIA_QUADRA: + case MAC_VIA_II: + via1 = (void *) VIA1_BASE; + via2 = (void *) VIA2_BASE; + rbv_present = 0; + oss_present = 0; + rbv_clear = 0x00; + gIER = vIER; + gIFR = vIFR; + gBufA = vBufA; + gBufB = vBufB; + break; + default: + panic("UNKNOWN VIA TYPE"); + } + + printk(KERN_INFO "VIA1 at %p is a 6522 or clone\n", via1); + + printk(KERN_INFO "VIA2 at %p is ", via2); + if (rbv_present) { + printk(KERN_INFO "an RBV\n"); + } else if (oss_present) { + printk(KERN_INFO "an OSS\n"); + } else { + printk(KERN_INFO "a 6522 or clone\n"); + } + +#ifdef DEBUG_VIA + via_debug_dump(); +#endif + + /* + * Shut down all IRQ sources, reset the timers, and + * kill the timer latch on VIA1. + */ + + via1[vIER] = 0x7F; + via1[vIFR] = 0x7F; + via1[vT1LL] = 0; + via1[vT1LH] = 0; + via1[vT1CL] = 0; + via1[vT1CH] = 0; + via1[vT2CL] = 0; + via1[vT2CH] = 0; + via1[vACR] &= 0x3F; + + /* + * SE/30: disable video IRQ + * XXX: testing for SE/30 VBL + */ + + if (macintosh_config->ident == MAC_MODEL_SE30) { + via1[vDirB] |= 0x40; + via1[vBufB] |= 0x40; + } + + /* + * Set the RTC bits to a known state: all lines to outputs and + * RTC disabled (yes that's 0 to enable and 1 to disable). + */ + + via1[vDirB] |= (VIA1B_vRTCEnb | VIA1B_vRTCClk | VIA1B_vRTCData); + via1[vBufB] |= (VIA1B_vRTCEnb | VIA1B_vRTCClk); + + /* Everything below this point is VIA2/RBV only... */ + + if (oss_present) return; + +#if 1 + /* Some machines support an alternate IRQ mapping that spreads */ + /* Ethernet and Sound out to their own autolevel IRQs and moves */ + /* VIA1 to level 6. A/UX uses this mapping and we do too. Note */ + /* that the IIfx emulates this alternate mapping using the OSS. */ + + switch(macintosh_config->ident) { + case MAC_MODEL_C610: + case MAC_MODEL_Q610: + case MAC_MODEL_C650: + case MAC_MODEL_Q650: + case MAC_MODEL_Q700: + case MAC_MODEL_Q800: + case MAC_MODEL_Q900: + case MAC_MODEL_Q950: + via_alt_mapping = 1; + via1[vDirB] |= 0x40; + via1[vBufB] &= ~0x40; + break; + default: + via_alt_mapping = 0; + break; + } +#else + /* The alernate IRQ mapping seems to just not work. Anyone with a */ + /* supported machine is welcome to take a stab at fixing it. It */ + /* _should_ work on the following Quadras: 610,650,700,800,900,950 */ + /* - 1999-06-12 (jmt) */ + + via_alt_mapping = 0; +#endif + + /* + * Now initialize VIA2. For RBV we just kill all interrupts; + * for a regular VIA we also reset the timers and stuff. + */ + + via2[gIER] = 0x7F; + via2[gIFR] = 0x7F | rbv_clear; + if (!rbv_present) { + via2[vT1LL] = 0; + via2[vT1LH] = 0; + via2[vT1CL] = 0; + via2[vT1CH] = 0; + via2[vT2CL] = 0; + via2[vT2CH] = 0; + via2[vACR] &= 0x3F; + } +} + +/* + * Start the 100 Hz clock + */ + +void __init via_init_clock(irqreturn_t (*func)(int, void *, struct pt_regs *)) +{ + via1[vACR] |= 0x40; + via1[vT1LL] = MAC_CLOCK_LOW; + via1[vT1LH] = MAC_CLOCK_HIGH; + via1[vT1CL] = MAC_CLOCK_LOW; + via1[vT1CH] = MAC_CLOCK_HIGH; + + request_irq(IRQ_MAC_TIMER_1, func, IRQ_FLG_LOCK, "timer", func); +} + +/* + * Register the interrupt dispatchers for VIA or RBV machines only. + */ + +void __init via_register_interrupts(void) +{ + if (via_alt_mapping) { + cpu_request_irq(IRQ_AUTO_1, via1_irq, + IRQ_FLG_LOCK|IRQ_FLG_FAST, "software", + (void *) via1); + cpu_request_irq(IRQ_AUTO_6, via1_irq, + IRQ_FLG_LOCK|IRQ_FLG_FAST, "via1", + (void *) via1); + } else { + cpu_request_irq(IRQ_AUTO_1, via1_irq, + IRQ_FLG_LOCK|IRQ_FLG_FAST, "via1", + (void *) via1); +#if 0 /* interferes with serial on some machines */ + if (!psc_present) { + cpu_request_irq(IRQ_AUTO_6, mac_bang, IRQ_FLG_LOCK, + "Off Switch", mac_bang); + } +#endif + } + cpu_request_irq(IRQ_AUTO_2, via2_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, + "via2", (void *) via2); + if (!psc_present) { + cpu_request_irq(IRQ_AUTO_4, mac_scc_dispatch, IRQ_FLG_LOCK, + "scc", mac_scc_dispatch); + } + request_irq(IRQ_MAC_NUBUS, via_nubus_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, + "nubus", (void *) via2); +} + +/* + * Debugging dump, used in various places to see what's going on. + */ + +void via_debug_dump(void) +{ + printk(KERN_DEBUG "VIA1: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n", + (uint) via1[vDirA], (uint) via1[vDirB], (uint) via1[vACR]); + printk(KERN_DEBUG " PCR = 0x%02X IFR = 0x%02X IER = 0x%02X\n", + (uint) via1[vPCR], (uint) via1[vIFR], (uint) via1[vIER]); + if (oss_present) { + printk(KERN_DEBUG "VIA2: <OSS>\n"); + } else if (rbv_present) { + printk(KERN_DEBUG "VIA2: IFR = 0x%02X IER = 0x%02X\n", + (uint) via2[rIFR], (uint) via2[rIER]); + printk(KERN_DEBUG " SIFR = 0x%02X SIER = 0x%02X\n", + (uint) via2[rSIFR], (uint) via2[rSIER]); + } else { + printk(KERN_DEBUG "VIA2: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n", + (uint) via2[vDirA], (uint) via2[vDirB], + (uint) via2[vACR]); + printk(KERN_DEBUG " PCR = 0x%02X IFR = 0x%02X IER = 0x%02X\n", + (uint) via2[vPCR], + (uint) via2[vIFR], (uint) via2[vIER]); + } +} + +/* + * This is always executed with interrupts disabled. + * + * TBI: get time offset between scheduling timer ticks + */ + +unsigned long mac_gettimeoffset (void) +{ + unsigned long ticks, offset = 0; + + /* read VIA1 timer 2 current value */ + ticks = via1[vT1CL] | (via1[vT1CH] << 8); + /* The probability of underflow is less than 2% */ + if (ticks > MAC_CLOCK_TICK - MAC_CLOCK_TICK / 50) + /* Check for pending timer interrupt in VIA1 IFR */ + if (via1[vIFR] & 0x40) offset = TICK_SIZE; + + ticks = MAC_CLOCK_TICK - ticks; + ticks = ticks * 10000L / MAC_CLOCK_TICK; + + return ticks + offset; +} + +/* + * Flush the L2 cache on Macs that have it by flipping + * the system into 24-bit mode for an instant. + */ + +void via_flush_cache(void) +{ + via2[gBufB] &= ~VIA2B_vMode32; + via2[gBufB] |= VIA2B_vMode32; +} + +/* + * Return the status of the L2 cache on a IIci + */ + +int via_get_cache_disable(void) +{ + /* Safeguard against being called accidentally */ + if (!via2) { + printk(KERN_ERR "via_get_cache_disable called on a non-VIA machine!\n"); + return 1; + } + + return (int) via2[gBufB] & VIA2B_vCDis; +} + +/* + * Initialize VIA2 for Nubus access + */ + +void __init via_nubus_init(void) +{ + /* don't set nubus_active = 0 here, it kills the Baboon */ + /* interrupt that we've already registered. */ + + /* unlock nubus transactions */ + + if (!rbv_present) { + /* set the line to be an output on non-RBV machines */ + if ((macintosh_config->adb_type != MAC_ADB_PB1) && + (macintosh_config->adb_type != MAC_ADB_PB2)) { + via2[vDirB] |= 0x02; + } + } + + /* this seems to be an ADB bit on PMU machines */ + /* according to MkLinux. -- jmt */ + + if ((macintosh_config->adb_type != MAC_ADB_PB1) && + (macintosh_config->adb_type != MAC_ADB_PB2)) { + via2[gBufB] |= 0x02; + } + + /* disable nubus slot interrupts. */ + if (rbv_present) { + via2[rSIER] = 0x7F; + via2[rSIER] = nubus_active | 0x80; + } else { + /* These are ADB bits on PMU */ + if ((macintosh_config->adb_type != MAC_ADB_PB1) && + (macintosh_config->adb_type != MAC_ADB_PB2)) { + switch(macintosh_config->ident) + { + case MAC_MODEL_II: + case MAC_MODEL_IIX: + case MAC_MODEL_IICX: + case MAC_MODEL_SE30: + via2[vBufA] |= 0x3F; + via2[vDirA] = ~nubus_active | 0xc0; + break; + default: + via2[vBufA] = 0xFF; + via2[vDirA] = ~nubus_active; + } + } + } +} + +/* + * The generic VIA interrupt routines (shamelessly stolen from Alan Cox's + * via6522.c :-), disable/pending masks added. + * + * The new interrupt architecture in macints.c takes care of a lot of the + * gruntwork for us, including tallying the interrupts and calling the + * handlers on the linked list. All we need to do here is basically generate + * the machspec interrupt number after clearing the interrupt. + */ + +irqreturn_t via1_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int irq_bit, i; + unsigned char events, mask; + + mask = via1[vIER] & 0x7F; + if (!(events = via1[vIFR] & mask)) + return IRQ_NONE; + + for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1) + if (events & irq_bit) { + via1[vIER] = irq_bit; + mac_do_irq_list(VIA1_SOURCE_BASE + i, regs); + via1[vIFR] = irq_bit; + via1[vIER] = irq_bit | 0x80; + } + +#if 0 /* freakin' pmu is doing weird stuff */ + if (!oss_present) { + /* This (still) seems to be necessary to get IDE + working. However, if you enable VBL interrupts, + you're screwed... */ + /* FIXME: should we check the SLOTIRQ bit before + pulling this stunt? */ + /* No, it won't be set. that's why we're doing this. */ + via_irq_disable(IRQ_MAC_NUBUS); + via_irq_clear(IRQ_MAC_NUBUS); + mac_do_irq_list(IRQ_MAC_NUBUS, regs); + via_irq_enable(IRQ_MAC_NUBUS); + } +#endif + return IRQ_HANDLED; +} + +irqreturn_t via2_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int irq_bit, i; + unsigned char events, mask; + + mask = via2[gIER] & 0x7F; + if (!(events = via2[gIFR] & mask)) + return IRQ_NONE; + + for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1) + if (events & irq_bit) { + via2[gIER] = irq_bit; + mac_do_irq_list(VIA2_SOURCE_BASE + i, regs); + via2[gIFR] = irq_bit | rbv_clear; + via2[gIER] = irq_bit | 0x80; + } + return IRQ_HANDLED; +} + +/* + * Dispatch Nubus interrupts. We are called as a secondary dispatch by the + * VIA2 dispatcher as a fast interrupt handler. + */ + +irqreturn_t via_nubus_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int irq_bit, i; + unsigned char events; + + if (!(events = ~via2[gBufA] & nubus_active)) + return IRQ_NONE; + + for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1) { + if (events & irq_bit) { + via_irq_disable(NUBUS_SOURCE_BASE + i); + mac_do_irq_list(NUBUS_SOURCE_BASE + i, regs); + via_irq_enable(NUBUS_SOURCE_BASE + i); + } + } + return IRQ_HANDLED; +} + +void via_irq_enable(int irq) { + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int irq_bit = 1 << irq_idx; + +#ifdef DEBUG_IRQUSE + printk(KERN_DEBUG "via_irq_enable(%d)\n", irq); +#endif + + if (irq_src == 1) { + via1[vIER] = irq_bit | 0x80; + } else if (irq_src == 2) { + /* + * Set vPCR for SCSI interrupts (but not on RBV) + */ + if ((irq_idx == 0) && !rbv_present) { + if (macintosh_config->scsi_type == MAC_SCSI_OLD) { + /* CB2 (IRQ) indep. input, positive edge */ + /* CA2 (DRQ) indep. input, positive edge */ + via2[vPCR] = 0x66; + } else { + /* CB2 (IRQ) indep. input, negative edge */ + /* CA2 (DRQ) indep. input, negative edge */ + via2[vPCR] = 0x22; + } + } + via2[gIER] = irq_bit | 0x80; + } else if (irq_src == 7) { + if (rbv_present) { + /* enable the slot interrupt. SIER works like IER. */ + via2[rSIER] = IER_SET_BIT(irq_idx); + } else { + /* Make sure the bit is an input, to enable the irq */ + /* But not on PowerBooks, that's ADB... */ + if ((macintosh_config->adb_type != MAC_ADB_PB1) && + (macintosh_config->adb_type != MAC_ADB_PB2)) { + switch(macintosh_config->ident) + { + case MAC_MODEL_II: + case MAC_MODEL_IIX: + case MAC_MODEL_IICX: + case MAC_MODEL_SE30: + via2[vDirA] &= (~irq_bit | 0xc0); + break; + default: + via2[vDirA] &= ~irq_bit; + } + } + } + nubus_active |= irq_bit; + } +} + +void via_irq_disable(int irq) { + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int irq_bit = 1 << irq_idx; + +#ifdef DEBUG_IRQUSE + printk(KERN_DEBUG "via_irq_disable(%d)\n", irq); +#endif + + if (irq_src == 1) { + via1[vIER] = irq_bit; + } else if (irq_src == 2) { + via2[gIER] = irq_bit; + } else if (irq_src == 7) { + if (rbv_present) { + /* disable the slot interrupt. SIER works like IER. */ + via2[rSIER] = IER_CLR_BIT(irq_idx); + } else { + /* disable the nubus irq by changing dir to output */ + /* except on PMU */ + if ((macintosh_config->adb_type != MAC_ADB_PB1) && + (macintosh_config->adb_type != MAC_ADB_PB2)) { + via2[vDirA] |= irq_bit; + } + } + nubus_active &= ~irq_bit; + } +} + +void via_irq_clear(int irq) { + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int irq_bit = 1 << irq_idx; + + if (irq_src == 1) { + via1[vIFR] = irq_bit; + } else if (irq_src == 2) { + via2[gIFR] = irq_bit | rbv_clear; + } else if (irq_src == 7) { + /* FIXME: hmm.. */ + } +} + +/* + * Returns nonzero if an interrupt is pending on the given + * VIA/IRQ combination. + */ + +int via_irq_pending(int irq) +{ + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int irq_bit = 1 << irq_idx; + + if (irq_src == 1) { + return via1[vIFR] & irq_bit; + } else if (irq_src == 2) { + return via2[gIFR] & irq_bit; + } else if (irq_src == 7) { + return ~via2[gBufA] & irq_bit; + } + return 0; +} |