diff options
Diffstat (limited to 'arch/m68k')
101 files changed, 11942 insertions, 3208 deletions
diff --git a/arch/m68k/Makefile b/arch/m68k/Makefile index 3a5957b09..6821efbc1 100644 --- a/arch/m68k/Makefile +++ b/arch/m68k/Makefile @@ -24,7 +24,11 @@ ifneq ($(COMPILE_ARCH),$(ARCH)) CROSS_COMPILE = m68k-linux- endif +ifndef CONFIG_SUN3 LINKFLAGS = -T $(TOPDIR)/arch/m68k/vmlinux.lds +else +LINKFLAGS = -T $(TOPDIR)/arch/m68k/vmlinux-sun3.lds -N +endif # without -fno-strength-reduce the 53c7xx.c driver fails ;-( CFLAGS += -pipe -fno-strength-reduce -ffixed-a2 @@ -50,7 +54,11 @@ ifdef CONFIG_KGDB CFLAGS := $(subst -fomit-frame-pointer,,$(CFLAGS)) -g endif +ifndef CONFIG_SUN3 HEAD := arch/m68k/kernel/head.o +else +HEAD := arch/m68k/kernel/sun3-head.o +endif SUBDIRS += arch/m68k/kernel arch/m68k/mm arch/m68k/lib CORE_FILES := arch/m68k/kernel/kernel.o arch/m68k/mm/mm.o $(CORE_FILES) @@ -106,6 +114,11 @@ CORE_FILES := $(CORE_FILES) arch/m68k/sun3x/sun3x.o SUBDIRS := $(SUBDIRS) arch/m68k/sun3x endif +ifdef CONFIG_SUN3 +CORE_FILES := $(CORE_FILES) arch/m68k/sun3/sun3.o arch/m68k/sun3/prom/promlib.a +SUBDIRS := $(SUBDIRS) arch/m68k/sun3 arch/m68k/sun3/prom +endif + ifdef CONFIG_M68040 CORE_FILES := $(CORE_FILES) arch/m68k/fpsp040/fpsp.o SUBDIRS := $(SUBDIRS) arch/m68k/fpsp040 @@ -116,6 +129,11 @@ CORE_FILES := $(CORE_FILES) arch/m68k/ifpsp060/ifpsp.o SUBDIRS := $(SUBDIRS) arch/m68k/ifpsp060 endif +ifdef CONFIG_M68KFPU_EMU +CORE_FILES := $(CORE_FILES) arch/m68k/math-emu/mathemu.o +SUBDIRS := $(SUBDIRS) arch/m68k/math-emu +endif + lilo: vmlinux if [ -f $(INSTALL_PATH)/vmlinux ]; then mv -f $(INSTALL_PATH)/vmlinux $(INSTALL_PATH)/vmlinux.old; fi if [ -f $(INSTALL_PATH)/System.map ]; then mv -f $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi @@ -127,7 +145,7 @@ zImage compressed: vmlinux.gz vmlinux.gz: vmlinux -ifdef CONFIG_KGDB +ifndef CONFIG_KGDB cp vmlinux vmlinux.tmp $(STRIP) vmlinux.tmp gzip -9c vmlinux.tmp >vmlinux.gz @@ -136,8 +154,21 @@ else gzip -9c vmlinux >vmlinux.gz endif +bzImage: vmlinux.bz2 + +vmlinux.bz2: vmlinux + +ifndef CONFIG_KGDB + cp vmlinux vmlinux.tmp + $(STRIP) vmlinux.tmp + bzip2 -1c vmlinux.tmp >vmlinux.bz2 + rm vmlinux.tmp +else + bzip2 -1c vmlinux >vmlinux.bz2 +endif + archclean: - rm -f vmlinux.gz + rm -f vmlinux.gz vmlinux.bz2 rm -f arch/m68k/kernel/m68k_defs.h arch/m68k/kernel/m68k_defs.d archmrproper: diff --git a/arch/m68k/amiga/amiints.c b/arch/m68k/amiga/amiints.c index 4fb886456..209813dfb 100644 --- a/arch/m68k/amiga/amiints.c +++ b/arch/m68k/amiga/amiints.c @@ -19,6 +19,20 @@ * called again. * The whole interrupt handling for CIAs is moved to cia.c * /Roman Zippel + * + * 07/08/99: rewamp of the interrupt handling - we now have two types of + * interrupts, normal and fast handlers, fast handlers being + * marked with SA_INTERRUPT and runs with all other interrupts + * disabled. Normal interrupts disable their own source but + * run with all other interrupt sources enabled. + * PORTS and EXTER interrupts are always shared even if the + * drivers do not explicitly mark this when calling + * request_irq which they really should do. + * This is similar to the way interrupts are handled on all + * other architectures and makes a ton of sense besides + * having the advantage of making it easier to share + * drivers. + * /Jes */ #include <linux/types.h> @@ -70,7 +84,7 @@ static void ami_badint(int irq, void *dev_id, struct pt_regs *fp) * the amiga IRQ handling routines. */ -__initfunc(void amiga_init_IRQ(void)) +void __init amiga_init_IRQ(void) { int i; @@ -81,7 +95,7 @@ __initfunc(void amiga_init_IRQ(void)) } else { ami_irq_list[i] = new_irq_node(); ami_irq_list[i]->handler = ami_badint; - ami_irq_list[i]->flags = IRQ_FLG_STD; + ami_irq_list[i]->flags = 0; ami_irq_list[i]->dev_id = NULL; ami_irq_list[i]->devname = NULL; ami_irq_list[i]->next = NULL; @@ -103,7 +117,7 @@ __initfunc(void amiga_init_IRQ(void)) cia_init_IRQ(&ciab_base); } -static inline void amiga_insert_irq(irq_node_t **list, irq_node_t *node) +static inline int amiga_insert_irq(irq_node_t **list, irq_node_t *node) { unsigned long flags; irq_node_t *cur; @@ -117,19 +131,18 @@ static inline void amiga_insert_irq(irq_node_t **list, irq_node_t *node) 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) { + if (node->flags & SA_INTERRUPT) { + if (node->flags & SA_SHIRQ) + return -EBUSY; + /* + * There should never be more than one + */ + while (cur && cur->flags & SA_INTERRUPT) { list = &cur->next; cur = cur->next; } } else { - while (cur && !(cur->flags & IRQ_FLG_SLOW)) { + while (cur) { list = &cur->next; cur = cur->next; } @@ -139,6 +152,7 @@ static inline void amiga_insert_irq(irq_node_t **list, irq_node_t *node) *list = node; restore_flags(flags); + return 0; } static inline void amiga_delete_irq(irq_node_t **list, void *dev_id) @@ -168,13 +182,16 @@ static inline void amiga_delete_irq(irq_node_t **list, void *dev_id) * If the addition was successful, it returns 0. */ -int amiga_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), +int amiga_request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void *dev_id) { irq_node_t *node; + int error = 0; if (irq >= AMI_IRQS) { - printk ("%s: Unknown IRQ %d from %s\n", __FUNCTION__, irq, devname); + printk ("%s: Unknown IRQ %d from %s\n", __FUNCTION__, + irq, devname); return -ENXIO; } @@ -190,6 +207,11 @@ int amiga_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_r return cia_request_irq(&ciaa_base, irq - IRQ_AMIGA_CIAA, handler, flags, devname, dev_id); + /* + * IRQ_AMIGA_PORTS & IRQ_AMIGA_EXTER defaults to shared, + * we could add a check here for the SA_SHIRQ flag but all drivers + * should be aware of sharing anyway. + */ if (ami_servers[irq]) { if (!(node = new_irq_node())) return -ENOMEM; @@ -198,20 +220,8 @@ int amiga_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_r node->dev_id = dev_id; node->devname = devname; node->next = NULL; - amiga_insert_irq(&ami_irq_list[irq], node); + error = amiga_insert_irq(&ami_irq_list[irq], node); } else { - if (!(ami_irq_list[irq]->flags & IRQ_FLG_STD)) { - if (ami_irq_list[irq]->flags & IRQ_FLG_LOCK) { - printk("%s: IRQ %d from %s is not replaceable\n", - __FUNCTION__, irq, ami_irq_list[irq]->devname); - return -EBUSY; - } - if (!(flags & IRQ_FLG_REPLACE)) { - printk("%s: %s can't replace IRQ %d from %s\n", - __FUNCTION__, devname, irq, ami_irq_list[irq]->devname); - return -EBUSY; - } - } ami_irq_list[irq]->handler = handler; ami_irq_list[irq]->flags = flags; ami_irq_list[irq]->dev_id = dev_id; @@ -222,7 +232,7 @@ int amiga_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_r if (irq < IRQ_AMIGA_PORTS && !ami_ablecount[irq]) custom.intena = IF_SETCLR | ami_intena_vals[irq]; - return 0; + return error; } void amiga_free_irq(unsigned int irq, void *dev_id) @@ -255,7 +265,7 @@ void amiga_free_irq(unsigned int irq, void *dev_id) printk("%s: removing probably wrong IRQ %d from %s\n", __FUNCTION__, irq, ami_irq_list[irq]->devname); ami_irq_list[irq]->handler = ami_badint; - ami_irq_list[irq]->flags = IRQ_FLG_STD; + ami_irq_list[irq]->flags = 0; ami_irq_list[irq]->dev_id = NULL; ami_irq_list[irq]->devname = NULL; custom.intena = ami_intena_vals[irq]; @@ -345,37 +355,58 @@ inline void amiga_do_irq(int irq, struct pt_regs *fp) void amiga_do_irq_list(int irq, struct pt_regs *fp, struct irq_server *server) { irq_node_t *node, *slow_nodes; - unsigned short flags; + unsigned short flags, intena; kstat.irqs[0][SYS_IRQS + irq]++; if (server->count++) server->reentrance = 1; - /* serve first fast and normal handlers */ - for (node = ami_irq_list[irq]; - node && (!(node->flags & IRQ_FLG_SLOW)); - node = node->next) - node->handler(irq, node->dev_id, fp); - custom.intreq = ami_intena_vals[irq]; + + intena = ami_intena_vals[irq]; + custom.intreq = intena; + + /* serve first fast handlers - there can only be one of these */ + node = ami_irq_list[irq]; + + /* + * Timer interrupts show up like this + */ if (!node) { server->count--; return; } + + if (node && (node->flags & SA_INTERRUPT)) { + save_flags(flags); + cli(); + node->handler(irq, node->dev_id, fp); + restore_flags(flags); + + server->count--; + return; + } + + /* + * Disable the interrupt source in question and reenable all + * other interrupts. No interrupt handler should ever touch + * the intena flags directly! + */ + custom.intena = intena; save_flags(flags); - restore_flags((flags & ~0x0700) | (fp->sr & 0x0700)); - /* if slow handlers exists, serve them now */ + sti(); + slow_nodes = node; for (;;) { for (; node; node = node->next) node->handler(irq, node->dev_id, fp); - /* if reentrance occurred, serve slow handlers again */ - custom.intena = ami_intena_vals[irq]; + if (!server->reentrance) { server->count--; - custom.intena = IF_SETCLR | ami_intena_vals[irq]; + restore_flags(flags); + custom.intena = IF_SETCLR | intena; return; } + server->reentrance = 0; - custom.intena = IF_SETCLR | ami_intena_vals[irq]; node = slow_nodes; } } @@ -493,24 +524,13 @@ int amiga_get_irq_list(char *buf) for (i = 0; i < AMI_STD_IRQS; i++) { if (!(node = ami_irq_list[i])) continue; - if (node->flags & IRQ_FLG_STD) - continue; len += sprintf(buf+len, "ami %2d: %10u ", i, kstat.irqs[0][SYS_IRQS + i]); do { - if (ami_servers[i]) { - if (node->flags & IRQ_FLG_FAST) - len += sprintf(buf+len, "F "); - else if (node->flags & IRQ_FLG_SLOW) - len += sprintf(buf+len, "S "); - else - len += sprintf(buf+len, " "); - } else { - if (node->flags & IRQ_FLG_LOCK) - len += sprintf(buf+len, "L "); - else - len += sprintf(buf+len, " "); - } + if (node->flags & SA_INTERRUPT) + len += sprintf(buf+len, "F "); + else + len += sprintf(buf+len, " "); len += sprintf(buf+len, "%s\n", node->devname); if ((node = node->next)) len += sprintf(buf+len, " "); diff --git a/arch/m68k/amiga/amisound.c b/arch/m68k/amiga/amisound.c index 72d2091f5..170d28948 100644 --- a/arch/m68k/amiga/amisound.c +++ b/arch/m68k/amiga/amisound.c @@ -40,7 +40,7 @@ u_short amiga_audio_period = MAX_PERIOD; static u_long clock_constant; -__initfunc(void amiga_init_sound(void)) +void __init amiga_init_sound(void) { snd_data = amiga_chip_alloc(sizeof(sine_data)); if (!snd_data) { diff --git a/arch/m68k/amiga/chipram.c b/arch/m68k/amiga/chipram.c index 50d5a0de0..80bc3c408 100644 --- a/arch/m68k/amiga/chipram.c +++ b/arch/m68k/amiga/chipram.c @@ -1,8 +1,7 @@ /* ** linux/amiga/chipram.c ** -** Modified 03-May-94 by Geert Uytterhoeven -** (Geert.Uytterhoeven@cs.kuleuven.ac.be) +** Modified 03-May-94 by Geert Uytterhoeven <geert@linux-m68k.org> ** - 64-bit aligned allocations for full AGA compatibility */ @@ -33,7 +32,7 @@ unsigned long amiga_chip_avail( void ) } -__initfunc(void amiga_chip_init (void)) +void __init amiga_chip_init (void) { struct chip_desc *dp; diff --git a/arch/m68k/amiga/cia.c b/arch/m68k/amiga/cia.c index 8f27234b4..cda898699 100644 --- a/arch/m68k/amiga/cia.c +++ b/arch/m68k/amiga/cia.c @@ -94,20 +94,6 @@ int cia_request_irq(struct ciabase *base, unsigned int irq, { u_char mask; - if (!(base->irq_list[irq].flags & IRQ_FLG_STD)) { - if (base->irq_list[irq].flags & IRQ_FLG_LOCK) { - printk("%s: IRQ %i from %s is not replaceable\n", - __FUNCTION__, base->cia_irq + irq, - base->irq_list[irq].devname); - return -EBUSY; - } - if (!(flags & IRQ_FLG_REPLACE)) { - printk("%s: %s can't replace IRQ %i from %s\n", __FUNCTION__, - devname, base->cia_irq + irq, - base->irq_list[irq].devname); - return -EBUSY; - } - } base->irq_list[irq].handler = handler; base->irq_list[irq].flags = flags; base->irq_list[irq].dev_id = dev_id; @@ -128,7 +114,7 @@ void cia_free_irq(struct ciabase *base, unsigned int irq, void *dev_id) base->irq_list[irq].devname); base->irq_list[irq].handler = NULL; - base->irq_list[irq].flags = IRQ_FLG_STD; + base->irq_list[irq].flags = 0; cia_able_irq(base, 1 << irq); } @@ -153,14 +139,14 @@ static void cia_handler(int irq, void *dev_id, struct pt_regs *fp) amiga_do_irq_list(base->server_irq, fp, &base->server); } -__initfunc(void cia_init_IRQ(struct ciabase *base)) +void __init cia_init_IRQ(struct ciabase *base) { int i; /* init isr handlers */ for (i = 0; i < CIA_IRQS; i++) { base->irq_list[i].handler = NULL; - base->irq_list[i].flags = IRQ_FLG_STD; + base->irq_list[i].flags = 0; } /* clear any pending interrupt and turn off all interrupts */ @@ -168,7 +154,7 @@ __initfunc(void cia_init_IRQ(struct ciabase *base)) cia_able_irq(base, CIA_ICR_ALL); /* install CIA handler */ - request_irq(base->handler_irq, cia_handler, IRQ_FLG_LOCK, base->name, base); + request_irq(base->handler_irq, cia_handler, 0, base->name, base); custom.intena = IF_SETCLR | base->int_mask; } @@ -179,15 +165,10 @@ int cia_get_irq_list(struct ciabase *base, char *buf) j = base->cia_irq; for (i = 0; i < CIA_IRQS; i++) { - if (!(base->irq_list[i].flags & IRQ_FLG_STD)) { - len += sprintf(buf+len, "cia %2d: %10d ", j + i, - kstat.irqs[0][SYS_IRQS + j + i]); - if (base->irq_list[i].flags & IRQ_FLG_LOCK) - len += sprintf(buf+len, "L "); - else - len += sprintf(buf+len, " "); - len += sprintf(buf+len, "%s\n", base->irq_list[i].devname); - } + len += sprintf(buf+len, "cia %2d: %10d ", j + i, + kstat.irqs[0][SYS_IRQS + j + i]); + len += sprintf(buf+len, " "); + len += sprintf(buf+len, "%s\n", base->irq_list[i].devname); } return len; } diff --git a/arch/m68k/amiga/config.c b/arch/m68k/amiga/config.c index 37b5bde3c..95eaa76f3 100644 --- a/arch/m68k/amiga/config.c +++ b/arch/m68k/amiga/config.c @@ -173,7 +173,7 @@ int amiga_parse_bootinfo(const struct bi_record *record) * Identify builtin hardware */ -__initfunc(static void amiga_identify(void)) +static void __init amiga_identify(void) { /* Fill in some default values, if necessary */ if (amiga_eclock == 0) @@ -334,7 +334,7 @@ __initfunc(static void amiga_identify(void)) * Setup the Amiga configuration info */ -__initfunc(void config_amiga(void)) +void __init config_amiga(void) { amiga_debug_init(); amiga_identify(); @@ -446,8 +446,8 @@ __initfunc(void config_amiga(void)) static unsigned short jiffy_ticks; -__initfunc(static void amiga_sched_init(void (*timer_routine)(int, void *, - struct pt_regs *))) +static void __init amiga_sched_init(void (*timer_routine)(int, void *, + struct pt_regs *)) { jiffy_ticks = (amiga_eclock+HZ/2)/HZ; @@ -460,8 +460,7 @@ __initfunc(static void amiga_sched_init(void (*timer_routine)(int, void *, * Please don't change this to use ciaa, as it interferes with the * SCSI code. We'll have to take a look at this later */ - request_irq(IRQ_AMIGA_CIAB_TA, timer_routine, IRQ_FLG_LOCK, - "timer", NULL); + request_irq(IRQ_AMIGA_CIAB_TA, timer_routine, 0, "timer", NULL); /* start timer */ ciab.cra |= 0x11; } @@ -880,7 +879,7 @@ void amiga_serial_gets(struct console *co, char *s, int len) } #endif -__initfunc(static void amiga_debug_init(void)) +static void __init amiga_debug_init(void) { if (!strcmp( m68k_debug_device, "ser" )) { /* no initialization required (?) */ diff --git a/arch/m68k/apollo/config.c b/arch/m68k/apollo/config.c index 4c620f23e..e3b54b40e 100644 --- a/arch/m68k/apollo/config.c +++ b/arch/m68k/apollo/config.c @@ -1,4 +1,3 @@ -#include <stdarg.h> #include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> diff --git a/arch/m68k/apollo/dn_debug.c b/arch/m68k/apollo/dn_debug.c index cba26779a..734902fbf 100644 --- a/arch/m68k/apollo/dn_debug.c +++ b/arch/m68k/apollo/dn_debug.c @@ -1,4 +1,3 @@ -#include <stdarg.h> #define DN_DEBUG_BUFFER_BASE 0x82800000 #define DN_DEBUG_BUFFER_SIZE 8*1024*1024 diff --git a/arch/m68k/atari/ataints.c b/arch/m68k/atari/ataints.c index 0f6ddf111..f47ff3a7a 100644 --- a/arch/m68k/atari/ataints.c +++ b/arch/m68k/atari/ataints.c @@ -327,7 +327,7 @@ extern int atari_SCC_reset_done; * the atari IRQ handling routines. */ -__initfunc(void atari_init_IRQ(void)) +void __init atari_init_IRQ(void) { int i; diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c index 6de1fd686..8eeaa8985 100644 --- a/arch/m68k/atari/atakeyb.c +++ b/arch/m68k/atari/atakeyb.c @@ -762,7 +762,7 @@ void atari_kbd_leds (unsigned int leds) * Martin Rogge, 20 Aug 1995 */ -__initfunc(int atari_keyb_init(void)) +int __init atari_keyb_init(void) { /* setup key map */ memcpy(key_maps[0], ataplain_map, sizeof(plain_map)); diff --git a/arch/m68k/atari/config.c b/arch/m68k/atari/config.c index 5be79acaf..53f2c7509 100644 --- a/arch/m68k/atari/config.c +++ b/arch/m68k/atari/config.c @@ -111,8 +111,8 @@ extern void (*kd_mksound)(unsigned int, unsigned int); */ #if 0 -__initfunc(static int -hwreg_present_bywrite(volatile void *regp, unsigned char val)) +static int __init +hwreg_present_bywrite(volatile void *regp, unsigned char val) { int ret; long save_sp, save_vbr; @@ -150,7 +150,7 @@ hwreg_present_bywrite(volatile void *regp, unsigned char val)) * should be readable without trouble (from channel A!). */ -__initfunc(static int scc_test( volatile char *ctla )) +static int __init scc_test( volatile char *ctla ) { if (!hwreg_present( ctla )) return( 0 ); @@ -177,7 +177,7 @@ __initfunc(static int scc_test( volatile char *ctla )) * Parse an Atari-specific record in the bootinfo */ -__initfunc(int atari_parse_bootinfo(const struct bi_record *record)) +int __init atari_parse_bootinfo(const struct bi_record *record) { int unknown = 0; const u_long *data = record->data; @@ -197,7 +197,7 @@ __initfunc(int atari_parse_bootinfo(const struct bi_record *record)) /* Parse the Atari-specific switches= option. */ -__initfunc(void atari_switches_setup( const char *str, unsigned len )) +void __init atari_switches_setup( const char *str, unsigned len ) { char switches[len+1]; char *p; @@ -238,7 +238,7 @@ __initfunc(void atari_switches_setup( const char *str, unsigned len )) * Setup the Atari configuration info */ -__initfunc(void config_atari(void)) +void __init config_atari(void) { unsigned short tos_version; diff --git a/arch/m68k/atari/debug.c b/arch/m68k/atari/debug.c index 249e8208b..e14c97e42 100644 --- a/arch/m68k/atari/debug.c +++ b/arch/m68k/atari/debug.c @@ -168,7 +168,7 @@ int atari_midi_console_wait_key(struct console *co) * SCC serial ports. They're used by the debugging interface, kgdb, and the * serial console code. */ #ifndef CONFIG_SERIAL_CONSOLE -__initfunc(static void atari_init_mfp_port( int cflag )) +static void __init atari_init_mfp_port( int cflag ) #else void atari_init_mfp_port( int cflag ) #endif @@ -214,7 +214,7 @@ void atari_init_mfp_port( int cflag ) } while(0) #ifndef CONFIG_SERIAL_CONSOLE -__initfunc(static void atari_init_scc_port( int cflag )) +static void __init atari_init_scc_port( int cflag ) #else void atari_init_scc_port( int cflag ) #endif @@ -281,7 +281,7 @@ void atari_init_scc_port( int cflag ) } #ifndef CONFIG_SERIAL_CONSOLE -__initfunc(static void atari_init_midi_port( int cflag )) +static void __init atari_init_midi_port( int cflag ) #else void atari_init_midi_port( int cflag ) #endif @@ -309,7 +309,7 @@ void atari_init_midi_port( int cflag ) ACIA_RHTID : ACIA_RLTID); } -__initfunc(void atari_debug_init(void)) +void __init atari_debug_init(void) { #ifdef CONFIG_KGDB /* the m68k_debug_device is used by the GDB stub, do nothing here */ diff --git a/arch/m68k/atari/joystick.c b/arch/m68k/atari/joystick.c index ba5dbcd98..f8d965498 100644 --- a/arch/m68k/atari/joystick.c +++ b/arch/m68k/atari/joystick.c @@ -130,11 +130,12 @@ struct file_operations atari_joystick_fops = { release_joystick }; -__initfunc(int atari_joystick_init(void)) +int __init atari_joystick_init(void) { joystick[0].active = joystick[1].active = 0; joystick[0].ready = joystick[1].ready = 0; - joystick[0].wait = joystick[1].wait = NULL; + init_waitqueue_head(&joystick[0].wait); + init_waitqueue_head(&joystick[1].wait); if (register_chrdev(MAJOR_NR, "Joystick", &atari_joystick_fops)) printk("unable to get major %d for joystick devices\n", MAJOR_NR); diff --git a/arch/m68k/atari/stdma.c b/arch/m68k/atari/stdma.c index 205837ef6..1aff226be 100644 --- a/arch/m68k/atari/stdma.c +++ b/arch/m68k/atari/stdma.c @@ -29,6 +29,7 @@ #include <linux/types.h> +#include <linux/kdev_t.h> #include <linux/genhd.h> #include <linux/sched.h> #include <linux/init.h> @@ -171,7 +172,7 @@ int stdma_islocked(void) * */ -__initfunc(void stdma_init(void)) +void __init stdma_init(void) { stdma_isr = NULL; request_irq(IRQ_MFP_FDC, stdma_int, IRQ_TYPE_SLOW, diff --git a/arch/m68k/atari/stram.c b/arch/m68k/atari/stram.c index cade2173a..eabc6bc9c 100644 --- a/arch/m68k/atari/stram.c +++ b/arch/m68k/atari/stram.c @@ -200,7 +200,7 @@ static struct swap_info_struct *stram_swap_info; static int stram_swap_type; /* Semaphore for get_stram_region. */ -static struct semaphore stram_swap_sem = MUTEX; +static DECLARE_MUTEX(stram_swap_sem); /* major and minor device number of the ST-RAM device; for the major, we use * the same as Amiga z2ram, which is really similar and impossible on Atari, @@ -346,12 +346,11 @@ void __init atari_stram_reserve_pages(unsigned long start_mem) "swap=%08lx-%08lx\n", swap_start, swap_end ); /* reserve some amount of memory for maintainance of - * swapping itself: 1 page for the lockmap, and one page - * for each 2048 (PAGE_SIZE/2) swap pages. (2 bytes for - * each page) */ + * swapping itself: one page for each 2048 (PAGE_SIZE/2) + * swap pages. (2 bytes for each page) */ swap_data = start_mem; - start_mem += (((SWAP_NR(swap_end) + PAGE_SIZE/2 - 1) - >> (PAGE_SHIFT-1)) + 1) << PAGE_SHIFT; + start_mem += ((SWAP_NR(swap_end) + PAGE_SIZE/2 - 1) + >> (PAGE_SHIFT-1)) << PAGE_SHIFT; /* correct swap_start if necessary */ if (swap_start == swap_data) swap_start = start_mem; @@ -610,8 +609,7 @@ static int __init swap_init(unsigned long start_mem, unsigned long swap_data) p->flags = SWP_USED; p->swap_file = &fake_dentry[0]; p->swap_device = 0; - p->swap_lockmap = (unsigned char *)(swap_data); - p->swap_map = (unsigned short *)(swap_data + PAGE_SIZE); + p->swap_map = (unsigned short *)swap_data; p->cluster_nr = 0; p->next = -1; p->prio = 0x7ff0; /* a rather high priority, but not the higest @@ -624,9 +622,6 @@ static int __init swap_init(unsigned long start_mem, unsigned long swap_data) stram_open( &swap_inode, MAGIC_FILE_P ); p->max = SWAP_NR(swap_end); - /* initialize lockmap */ - memset( p->swap_lockmap, 0, PAGE_SIZE ); - /* initialize swap_map: set regions that are already allocated or belong * to kernel data space to SWAP_MAP_BAD, otherwise to free */ j = 0; /* # of free pages */ @@ -805,7 +800,7 @@ static void unswap_process(struct mm_struct * mm, unsigned long entry, /* * Go through process' page directory. */ - if (!mm || mm == &init_mm) + if (!mm) return; for (vma = mm->mmap; vma; vma = vma->vm_next) { pgd_t * pgd = pgd_offset(mm, vma->vm_start); diff --git a/arch/m68k/atari/time.c b/arch/m68k/atari/time.c index 0fd3795a5..08b861c55 100644 --- a/arch/m68k/atari/time.c +++ b/arch/m68k/atari/time.c @@ -17,8 +17,8 @@ #include <linux/init.h> -__initfunc(void -atari_sched_init(void (*timer_routine)(int, void *, struct pt_regs *))) +void __init +atari_sched_init(void (*timer_routine)(int, void *, struct pt_regs *)) { /* set Timer C data Register */ mfp.tim_dt_c = INT_TICKS; diff --git a/arch/m68k/bvme6000/config.c b/arch/m68k/bvme6000/config.c index fc1b5627f..dabb9bcf8 100644 --- a/arch/m68k/bvme6000/config.c +++ b/arch/m68k/bvme6000/config.c @@ -14,7 +14,6 @@ * for more details. */ -#include <stdarg.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/mm.h> @@ -104,7 +103,7 @@ static int bvme6000_get_hardware_list(char *buffer) } -__initfunc(void config_bvme6000(void)) +void __init config_bvme6000(void) { volatile PitRegsPtr pit = (PitRegsPtr)BVME_PIT_BASE; diff --git a/arch/m68k/bvme6000/rtc.c b/arch/m68k/bvme6000/rtc.c index cf05ab9aa..f1ad19b0c 100644 --- a/arch/m68k/bvme6000/rtc.c +++ b/arch/m68k/bvme6000/rtc.c @@ -164,7 +164,7 @@ static struct miscdevice rtc_dev= &rtc_fops }; -__initfunc(int rtc_DP8570A_init(void)) +int __init rtc_DP8570A_init(void) { if (!MACH_IS_BVME6000) return -ENODEV; diff --git a/arch/m68k/config.in b/arch/m68k/config.in index a8dacc904..54e8448d3 100644 --- a/arch/m68k/config.in +++ b/arch/m68k/config.in @@ -48,9 +48,6 @@ fi bool 'Sun3x support' CONFIG_SUN3X define_bool CONFIG_SUN3 n -if [ "$CONFIG_PCI" = "y" ]; then - bool 'Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC -fi bool 'Q40/Q60 support' CONFIG_Q40 comment 'Processor type' @@ -58,6 +55,13 @@ bool '68020 support' CONFIG_M68020 bool '68030 support' CONFIG_M68030 bool '68040 support' CONFIG_M68040 bool '68060 support' CONFIG_M68060 +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'Math emulation support' CONFIG_M68KFPU_EMU + if [ "$CONFIG_M68KFPU_EMU" = "y" ]; then + bool 'Math emulation extra precision' CONFIG_M68KFPU_EMU_EXTRAPREC + bool 'Math emulation only kernel' CONFIG_M68KFPU_EMU_ONLY + fi +fi bool 'Advanced configuration options' CONFIG_ADVANCED if [ "$CONFIG_ADVANCED" = "y" ]; then bool 'Use read-modify-write instructions' CONFIG_RMW_INSNS @@ -100,7 +104,7 @@ fi bool '/proc/hardware support' CONFIG_PROC_HARDWARE if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Parallel port support (EXPERIMENTAL, disables old lp driver!)' CONFIG_PARPORT + tristate 'Parallel port support (EXPERIMENTAL)' CONFIG_PARPORT if [ "$CONFIG_PARPORT" != "n" ]; then if [ "$CONFIG_AMIGA" != "n" ]; then dep_tristate ' Amiga builtin port' CONFIG_PARPORT_AMIGA $CONFIG_PARPORT @@ -118,6 +122,10 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then if [ "$CONFIG_ATARI" == "y" ]; then dep_tristate ' Atari builtin port' CONFIG_PARPORT_ATARI $CONFIG_PARPORT fi + dep_tristate 'Parallel printer support' CONFIG_PRINTER $CONFIG_PARPORT + if [ "$CONFIG_PRINTER" != "n" ]; then + bool ' Support IEEE1284 status readback' CONFIG_PRINTER_READBACK + fi fi @@ -190,8 +198,8 @@ if [ "$CONFIG_ATARI" = "y" ]; then fi fi if [ "$CONFIG_MAC" = "y" ]; then - bool 'MAC NCR5380 SCSI' CONFIG_MAC_SCSI - dep_tristate 'MAC NCR53c9[46] SCSI' CONFIG_SCSI_MAC_ESP $CONFIG_SCSI + bool 'Macintosh NCR5380 SCSI' CONFIG_MAC_SCSI + dep_tristate 'Macintosh NCR53c9[46] SCSI' CONFIG_SCSI_MAC_ESP $CONFIG_SCSI fi #dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI @@ -318,25 +326,23 @@ if [ "$CONFIG_ATARI" = "y" ]; then define_bool CONFIG_NVRAM y fi -if [ "$CONFIG_PARPORT" = "n" ]; then - tristate 'Parallel printer support' CONFIG_M68K_PRINTER - if [ "$CONFIG_ZORRO" = "y" ]; then - dep_tristate 'Multiface Card III parallel support' CONFIG_MULTIFACE_III_LP $CONFIG_M68K_PRINTER - fi -else - dep_tristate 'Parallel printer support' CONFIG_PRINTER $CONFIG_PARPORT - if [ "$CONFIG_PRINTER" != "n" ]; then - bool ' Support IEEE1284 status readback' CONFIG_PRINTER_READBACK - fi -fi if [ "$CONFIG_AMIGA" = "y" ]; then tristate 'Amiga mouse support' CONFIG_AMIGAMOUSE + if [ "$CONFIG_AMIGAMOUSE" != "n" ]; then + define_bool CONFIG_BUSMOUSE y + fi fi if [ "$CONFIG_ATARI" = "y" ]; then tristate 'Atari mouse support' CONFIG_ATARIMOUSE + if [ "$CONFIG_ATARIMOUSE" != "n" ]; then + define_bool CONFIG_BUSMOUSE y + fi fi if [ "$CONFIG_MAC" = "y" ]; then bool 'Mac ADB mouse support' CONFIG_ADBMOUSE + if [ "$CONFIG_ADBMOUSE" != "n" ]; then + define_bool CONFIG_BUSMOUSE y + fi fi if [ "$CONFIG_ATARI" = "y" ]; then tristate 'Atari MFP serial support' CONFIG_ATARI_MFPSER @@ -374,6 +380,9 @@ if [ "$CONFIG_SUN3X" = "y" ]; then if [ "$CONFIG_SUN3X_ZS" = "y" ]; then bool 'Sun keyboard support' CONFIG_SUN_KEYBOARD bool 'Sun mouse support' CONFIG_SUN_MOUSE + if [ "$CONFIG_SUN_MOUSE" != "n" ]; then + define_bool CONFIG_BUSMOUSE y + fi define_bool CONFIG_SBUS y define_bool CONFIG_SBUSCHAR y define_bool CONFIG_SUN_SERIAL y diff --git a/arch/m68k/hp300/config.c b/arch/m68k/hp300/config.c index 6808de799..d998d6ff9 100644 --- a/arch/m68k/hp300/config.c +++ b/arch/m68k/hp300/config.c @@ -31,7 +31,7 @@ extern int hp300_get_irq_list(char *buf); extern int hp300_keyb_init(void); #else /* Dummy function for when there is no keyboard. */ -__initfunc(int hp300_keyb_init(void)) +int __init hp300_keyb_init(void) { } #endif @@ -60,7 +60,7 @@ static void hp300_get_model(char *model) strcpy(model, "HP9000/300"); } -__initfunc(void config_hp300(void)) +void __init config_hp300(void) { mach_sched_init = hp300_sched_init; mach_keyb_init = hp300_keyb_init; diff --git a/arch/m68k/hp300/hil.c b/arch/m68k/hp300/hil.c index d7fe1867c..dec40562b 100644 --- a/arch/m68k/hp300/hil.c +++ b/arch/m68k/hp300/hil.c @@ -304,7 +304,7 @@ static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len) * Initialise HIL. */ -__initfunc(int hp300_keyb_init(void)) +int __init hp300_keyb_init(void) { unsigned char s, c, kbid; unsigned int n = 0; diff --git a/arch/m68k/hp300/ints.c b/arch/m68k/hp300/ints.c index 02538a475..497400575 100644 --- a/arch/m68k/hp300/ints.c +++ b/arch/m68k/hp300/ints.c @@ -15,13 +15,13 @@ #include <linux/sched.h> #include <linux/kernel_stat.h> #include <linux/interrupt.h> +#include <linux/spinlock.h> #include <asm/machdep.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/system.h> #include <asm/traps.h> #include <asm/ptrace.h> -#include <asm/spinlock.h> #include "ints.h" /* Each ipl has a linked list of interrupt service routines. @@ -153,7 +153,7 @@ int hp300_get_irq_list(char *buf) return 0; } -__initfunc(void hp300_init_IRQ(void)) +void __init hp300_init_IRQ(void) { spin_lock_init(&irqlist_lock); } diff --git a/arch/m68k/hp300/time.c b/arch/m68k/hp300/time.c index d29c3c5f4..d1aee933f 100644 --- a/arch/m68k/hp300/time.c +++ b/arch/m68k/hp300/time.c @@ -61,7 +61,7 @@ unsigned long hp300_gettimeoffset(void) return (USECS_PER_JIFFY * ticks) / INTVAL; } -__initfunc(void hp300_sched_init(void (*vector)(int, void *, struct pt_regs *))) +void __init hp300_sched_init(void (*vector)(int, void *, struct pt_regs *)) { writeb(0x1, CLOCKBASE + CLKCR2); /* select CR1 */ writeb(0x1, CLOCKBASE + CLKCR1); /* reset */ diff --git a/arch/m68k/kernel/Makefile b/arch/m68k/kernel/Makefile index 280a5b450..146fcb69b 100644 --- a/arch/m68k/kernel/Makefile +++ b/arch/m68k/kernel/Makefile @@ -10,11 +10,16 @@ .S.o: $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o +ifndef CONFIG_SUN3 all: head.o kernel.o +else +all: sun3-head.o kernel.o +endif + O_TARGET := kernel.o O_OBJS := entry.o process.o traps.o ints.o signal.o ptrace.o \ - setup.o sys_m68k.o time.o -OX_OBJS := m68k_ksyms.o + sys_m68k.o time.o semaphore.o +OX_OBJS := setup.o m68k_ksyms.o ifdef CONFIG_KGDB O_OBJS += kgdb.o @@ -26,6 +31,8 @@ endif head.o: head.S m68k_defs.h +sun3-head.o: sun3-head.S m68k_defs.h + m68k_defs.h: m68k_defs.c m68k_defs.head rm -f m68k_defs.d SUNPRO_DEPENDENCIES="m68k_defs.d m68k_defs.h" \ diff --git a/arch/m68k/kernel/bios32.c b/arch/m68k/kernel/bios32.c index b1322261a..55b762bbd 100644 --- a/arch/m68k/kernel/bios32.c +++ b/arch/m68k/kernel/bios32.c @@ -240,7 +240,7 @@ static unsigned int mem_base = FIRST_MEM_ADDR; /* Skip first 32M. */ * accesses. */ -__initfunc(static void disable_dev(struct pci_dev *dev)) +static void __init disable_dev(struct pci_dev *dev) { struct pci_bus *bus; unsigned short cmd; @@ -263,8 +263,8 @@ __initfunc(static void disable_dev(struct pci_dev *dev)) #define MAX(val1, val2) ( ((val1) > (val2)) ? val1 : val2) -__initfunc(static void layout_dev(struct pci_dev *dev, unsigned long pci_mem_base, - unsigned long pci_io_base)) +static void __init layout_dev(struct pci_dev *dev, unsigned long pci_mem_base, + unsigned long pci_io_base) { struct pci_bus *bus; unsigned short cmd; @@ -400,8 +400,8 @@ __initfunc(static void layout_dev(struct pci_dev *dev, unsigned long pci_mem_bas bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device, dev->class)); } -__initfunc(static void layout_bus(struct pci_bus *bus, unsigned long pci_mem_base, - unsigned long pci_io_base)) +static void __init layout_bus(struct pci_bus *bus, unsigned long pci_mem_base, + unsigned long pci_io_base) { struct pci_dev *dev; @@ -512,7 +512,7 @@ int pcibios_present(void) return 0; } -__initfunc(void pcibios_init(void)) +void __init pcibios_init(void) { printk("Linux/m68k PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV); @@ -531,7 +531,7 @@ __initfunc(void pcibios_init(void)) * of the PCI cards. */ -__initfunc(static inline void hades_fixup(void)) +static inline void __init hades_fixup(void) { char irq_tab[4] = { IRQ_TT_MFP_IO0, /* Slot 0. */ @@ -560,7 +560,7 @@ __initfunc(static inline void hades_fixup(void)) } } -__initfunc(void pcibios_fixup(void)) +void __init pcibios_fixup(void) { #if PCI_MODIFY unsigned long orig_mem_base, orig_io_base; @@ -587,11 +587,11 @@ __initfunc(void pcibios_fixup(void)) hades_fixup(); } -__initfunc(void pcibios_fixup_bus(struct pci_bus *bus)) +void __init pcibios_fixup_bus(struct pci_bus *bus) { } -__initfunc(char *pcibios_setup(char *str)) +char * __init pcibios_setup(char *str) { return str; } diff --git a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S index 61482c3a8..21acaa218 100644 --- a/arch/m68k/kernel/entry.S +++ b/arch/m68k/kernel/entry.S @@ -67,11 +67,21 @@ ENTRY(trap) ENTRY(reschedule) | save top of frame - movel %sp,%curptr@(TASK_TSS+TSS_ESP0) + movel %sp,%curptr@(TASK_THREAD+THREAD_ESP0) pea SYMBOL_NAME(ret_from_exception) jmp SYMBOL_NAME(schedule) + | After a fork we jump here directly from resume, + | so that %d1 contains the previous task + | Theoretically only needed on SMP, but let's watch + | what happens in schedule_tail() in future... +ENTRY(ret_from_fork) + movel %d1,%sp@- + jsr SYMBOL_NAME(schedule_tail) + addql #4,%sp + jra SYMBOL_NAME(ret_from_exception) + badsys: movel #-ENOSYS,PT_D0(%sp) jra SYMBOL_NAME(ret_from_exception) @@ -100,7 +110,7 @@ ENTRY(system_call) GET_CURRENT(%d0) | save top of frame - movel %sp,%curptr@(TASK_TSS+TSS_ESP0) + movel %sp,%curptr@(TASK_THREAD+THREAD_ESP0) cmpl #NR_syscalls,%d2 jcc badsys @@ -118,8 +128,10 @@ SYMBOL_NAME_LABEL(ret_from_exception) andw #ALLOWINT,%sr tstl %curptr@(TASK_NEEDRESCHED) jne SYMBOL_NAME(reschedule) +#if 0 cmpl #SYMBOL_NAME(task),%curptr | task[0] cannot have signals jeq 2f +#endif | check for delayed trace bclr #PF_DTRACE_BIT,%curptr@(TASK_FLAGS+PF_DTRACE_OFF) jne do_delayed_trace @@ -152,6 +164,36 @@ do_delayed_trace: addql #4,%sp jra 5b + +#if 0 +#if CONFIG_AMIGA +SYMBOL_NAME_LABEL(ami_inthandler) + addql #1,SYMBOL_NAME(local_irq_count) + SAVE_ALL_INT + GET_CURRENT(%d0) + + bfextu %sp@(PT_VECTOR){#4,#12},%d0 + movel %d0,%a0 + addql #1,%a0@(SYMBOL_NAME(kstat)+STAT_IRQ-VECOFF(VEC_SPUR)) + movel %a0@(SYMBOL_NAME(autoirq_list)-VECOFF(VEC_SPUR)),%a0 + +| amiga vector int handler get the req mask instead of irq vector + lea CUSTOMBASE,%a1 + movew %a1@(C_INTREQR),%d0 + andw %a1@(C_INTENAR),%d0 + +| prepare stack (push frame pointer, dev_id & req mask) + pea %sp@ + movel %a0@(IRQ_DEVID),%sp@- + movel %d0,%sp@- + pea %pc@(SYMBOL_NAME(ret_from_interrupt):w) + jbra @(IRQ_HANDLER,%a0)@(0) + +ENTRY(nmi_handler) + rte +#endif +#endif + /* ** This is the main interrupt handler, responsible for calling process_int() */ @@ -171,7 +213,7 @@ SYMBOL_NAME_LABEL(inthandler) jbeq 1f jbsr SYMBOL_NAME(floppy_hardint) jbra 3f -1: +1: #endif jbsr SYMBOL_NAME(process_int)| process the IRQ 3: addql #8,%sp | pop parameters off stack @@ -179,7 +221,7 @@ SYMBOL_NAME_LABEL(inthandler) SYMBOL_NAME_LABEL(ret_from_interrupt) subql #1,SYMBOL_NAME(local_irq_count) jeq 1f -2: +2: RESTORE_ALL 1: #if 1 @@ -265,25 +307,30 @@ SYMBOL_NAME_LABEL(resume) */ /* save sr */ - movew %sr,%a0@(TASK_TSS+TSS_SR) + movew %sr,%a0@(TASK_THREAD+THREAD_SR) /* save fs (sfc,%dfc) (may be pointing to kernel memory) */ movec %sfc,%d0 - movew %d0,%a0@(TASK_TSS+TSS_FS) + movew %d0,%a0@(TASK_THREAD+THREAD_FS) /* save usp */ /* it is better to use a movel here instead of a movew 8*) */ movec %usp,%d0 - movel %d0,%a0@(TASK_TSS+TSS_USP) + movel %d0,%a0@(TASK_THREAD+THREAD_USP) /* save non-scratch registers on stack */ SAVE_SWITCH_STACK /* save current kernel stack pointer */ - movel %sp,%a0@(TASK_TSS+TSS_KSP) + movel %sp,%a0@(TASK_THREAD+THREAD_KSP) /* save floating point context */ - fsave %a0@(TASK_TSS+TSS_FPSTATE) +#ifndef CONFIG_M68KFPU_EMU_ONLY +#ifdef CONFIG_M68KFPU_EMU + tstl SYMBOL_NAME(m68k_fputype) + jeq 3f +#endif + fsave %a0@(TASK_THREAD+THREAD_FPSTATE) #if defined(CONFIG_M68060) #if !defined(CPU_M68060_ONLY) @@ -291,127 +338,71 @@ SYMBOL_NAME_LABEL(resume) beqs 1f #endif /* The 060 FPU keeps status in bits 15-8 of the first longword */ - tstb %a0@(TASK_TSS+TSS_FPSTATE+2) + tstb %a0@(TASK_THREAD+THREAD_FPSTATE+2) jeq 3f #if !defined(CPU_M68060_ONLY) jra 2f #endif #endif /* CONFIG_M68060 */ #if !defined(CPU_M68060_ONLY) -1: tstb %a0@(TASK_TSS+TSS_FPSTATE) +1: tstb %a0@(TASK_THREAD+THREAD_FPSTATE) jeq 3f #endif -2: fmovemx %fp0-%fp7,%a0@(TASK_TSS+TSS_FPREG) - fmoveml %fpcr/%fpsr/%fpiar,%a0@(TASK_TSS+TSS_FPCNTL) +2: fmovemx %fp0-%fp7,%a0@(TASK_THREAD+THREAD_FPREG) + fmoveml %fpcr/%fpsr/%fpiar,%a0@(TASK_THREAD+THREAD_FPCNTL) 3: +#endif /* CONFIG_M68KFPU_EMU_ONLY */ /* Return previous task in %d1 */ movel %curptr,%d1 /* switch to new task (a1 contains new task) */ movel %a1,%curptr - /* Skip address space switching if they are the same. */ - movel %a0@(TASK_MM),%d0 - cmpl %a1@(TASK_MM),%d0 + /* restore floating point context */ +#ifndef CONFIG_M68KFPU_EMU_ONLY +#ifdef CONFIG_M68KFPU_EMU + tstl SYMBOL_NAME(m68k_fputype) jeq 4f - -#if defined(CPU_M68020_OR_M68030) && defined(CPU_M68040_OR_M68060) - /* 68040 or 68060 ? */ - tstl SYMBOL_NAME(m68k_is040or060) - bnes 1f #endif -#if defined(CPU_M68020_OR_M68030) - /* - * switch address space - */ - - /* flush MC68030/MC68020 caches (they are virtually addressed) */ - movec %cacr,%d0 - oriw #LFLUSH_I_AND_D,%d0 - movec %d0,%cacr - - /* switch the root pointer */ -#ifdef CPU_M68030_ONLY - .chip 68030 - pmovefd %a1@(TASK_TSS+TSS_CRP),%crp - .chip 68k - pflush #0,#4 -#else - pmove %a1@(TASK_TSS+TSS_CRP),%crp -#endif -#endif - -#if defined(CPU_M68020_OR_M68030) && defined(CPU_M68040_OR_M68060) - jra 2f /* skip m68040 stuff */ -1: -#endif -#if defined(CPU_M68040_OR_M68060) - /* - * switch address space - */ - .chip 68040 - - /* flush address translation cache (user entries) */ - pflushan - - /* switch the root pointer */ - movel %a1@(TASK_TSS+TSS_CRP+4),%d0 - movec %d0,%urp - -#if defined (CONFIG_M68060) - /* is it a '060 ? */ -#if !defined(CPU_M68060_ONLY) - btst #3,SYMBOL_NAME(m68k_cputype)+3 - beqs 2f -#endif - /* clear user entries in the branch cache */ - movec %cacr,%d0 - orl #0x00200000,%d0 - movec %d0,%cacr -#endif /* CONFIG_M68060 */ - .chip 68k -#endif /* CPU_M68040_OR_M68060 */ -2: -4: - /* restore floating point context */ - #if defined(CONFIG_M68060) #if !defined(CPU_M68060_ONLY) btst #3,SYMBOL_NAME(m68k_cputype)+3 beqs 1f #endif /* The 060 FPU keeps status in bits 15-8 of the first longword */ - tstb %a1@(TASK_TSS+TSS_FPSTATE+2) + tstb %a1@(TASK_THREAD+THREAD_FPSTATE+2) jeq 3f #if !defined(CPU_M68060_ONLY) jra 2f #endif #endif /* CONFIG_M68060 */ #if !defined(CPU_M68060_ONLY) -1: tstb %a1@(TASK_TSS+TSS_FPSTATE) +1: tstb %a1@(TASK_THREAD+THREAD_FPSTATE) jeq 3f #endif -2: fmovemx %a1@(TASK_TSS+TSS_FPREG),%fp0-%fp7 - fmoveml %a1@(TASK_TSS+TSS_FPCNTL),%fpcr/%fpsr/%fpiar -3: frestore %a1@(TASK_TSS+TSS_FPSTATE) +2: fmovemx %a1@(TASK_THREAD+THREAD_FPREG),%fp0-%fp7 + fmoveml %a1@(TASK_THREAD+THREAD_FPCNTL),%fpcr/%fpsr/%fpiar +3: frestore %a1@(TASK_THREAD+THREAD_FPSTATE) +4: +#endif /* CONFIG_M68KFPU_EMU_ONLY */ /* restore the kernel stack pointer */ - movel %a1@(TASK_TSS+TSS_KSP),%sp + movel %a1@(TASK_THREAD+THREAD_KSP),%sp /* restore non-scratch registers */ RESTORE_SWITCH_STACK /* restore user stack pointer */ - movel %a1@(TASK_TSS+TSS_USP),%a0 + movel %a1@(TASK_THREAD+THREAD_USP),%a0 movel %a0,%usp /* restore fs (sfc,%dfc) */ - movew %a1@(TASK_TSS+TSS_FS),%a0 + movew %a1@(TASK_THREAD+THREAD_FS),%a0 movec %a0,%sfc movec %a0,%dfc /* restore status register */ - movew %a1@(TASK_TSS+TSS_SR),%sr + movew %a1@(TASK_THREAD+THREAD_SR),%sr rts @@ -530,7 +521,7 @@ SYMBOL_NAME_LABEL(sys_call_table) .long SYMBOL_NAME(sys_ni_syscall) .long SYMBOL_NAME(sys_ni_syscall) /* iopl for i386 */ /* 110 */ .long SYMBOL_NAME(sys_vhangup) - .long SYMBOL_NAME(sys_idle) + .long SYMBOL_NAME(sys_ni_syscall) /* obsolete idle() syscall */ .long SYMBOL_NAME(sys_ni_syscall) /* vm86old for i386 */ .long SYMBOL_NAME(sys_wait4) .long SYMBOL_NAME(sys_swapoff) /* 115 */ diff --git a/arch/m68k/kernel/head.S b/arch/m68k/kernel/head.S index 361b10cb6..49285b86d 100644 --- a/arch/m68k/kernel/head.S +++ b/arch/m68k/kernel/head.S @@ -503,10 +503,18 @@ func_define putn,1 .macro puts string #if defined(CONSOLE) || defined(SERIAL_DEBUG) +/* The __INITDATA stuff is a no-op when ftrace or kgdb are turned on */ +#if defined(CONFIG_FTRACE) || defined(CONFIG_KGDB) + bra 1f +#endif __INITDATA .Lstr\@: .string "\string" __FINIT +#if defined(CONFIG_FTRACE) || defined(CONFIG_KGDB) + .align 2 +1: +#endif pea %pc@(.Lstr\@) func_call puts addql #4,%sp diff --git a/arch/m68k/kernel/ints.c b/arch/m68k/kernel/ints.c index 31931a920..9dc6427e6 100644 --- a/arch/m68k/kernel/ints.c +++ b/arch/m68k/kernel/ints.c @@ -86,14 +86,14 @@ void (*mach_free_irq) (unsigned int, void *) = dummy_free_irq; * the IRQ handling routines. */ -__initfunc(void init_IRQ(void)) +void __init init_IRQ(void) { int i; for (i = 0; i < SYS_IRQS; i++) { if (mach_default_handler) irq_list[i].handler = (*mach_default_handler)[i]; - irq_list[i].flags = IRQ_FLG_STD; + irq_list[i].flags = 0; irq_list[i].dev_id = NULL; irq_list[i].devname = default_names[i]; } @@ -144,6 +144,7 @@ int sys_request_irq(unsigned int irq, return -ENXIO; } +#if 0 if (!(irq_list[irq].flags & IRQ_FLG_STD)) { if (irq_list[irq].flags & IRQ_FLG_LOCK) { printk("%s: IRQ %d from %s is not replaceable\n", @@ -156,6 +157,8 @@ int sys_request_irq(unsigned int irq, return -EBUSY; } } +#endif + irq_list[irq].handler = handler; irq_list[irq].flags = flags; irq_list[irq].dev_id = dev_id; @@ -175,7 +178,7 @@ void sys_free_irq(unsigned int irq, void *dev_id) __FUNCTION__, irq, irq_list[irq].devname); irq_list[irq].handler = (*mach_default_handler)[irq]; - irq_list[irq].flags = IRQ_FLG_STD; + irq_list[irq].flags = 0; irq_list[irq].dev_id = NULL; irq_list[irq].devname = default_names[irq]; } @@ -250,9 +253,6 @@ int get_irq_list(char *buf) for (i = 0; i < SYS_IRQS; i++) { len += sprintf(buf+len, "auto %2d: %10u ", i, i ? kstat.irqs[0][i] : num_spurious); - if (irq_list[i].flags & IRQ_FLG_LOCK) - len += sprintf(buf+len, "L "); - else len += sprintf(buf+len, " "); len += sprintf(buf+len, "%s\n", irq_list[i].devname); } diff --git a/arch/m68k/kernel/kgdb.c b/arch/m68k/kernel/kgdb.c index cdbd250cd..be75defdb 100644 --- a/arch/m68k/kernel/kgdb.c +++ b/arch/m68k/kernel/kgdb.c @@ -189,6 +189,11 @@ #include <asm/amigahw.h> #include <asm/amigaints.h> #endif +#ifdef CONFIG_MAC +#include <linux/tty.h> +#include <asm/bootinfo.h> +#include <asm/macints.h> +#endif #undef DEBUG @@ -239,6 +244,15 @@ static unsigned char atari_scc_intr( void ); extern int amiga_ser_out( unsigned char c ); extern unsigned char amiga_ser_in( void ); #endif +#ifdef CONFIG_MAC +static unsigned char mac_scca_in( void ); +static unsigned char mac_scca_out( unsigned char c ); +static unsigned char mac_scca_intr( void ); +static unsigned char mac_sccb_in( void ); +static unsigned char mac_sccb_out( unsigned char c); +static unsigned char mac_sccb_intr( void ); +extern void mac_init_scc_port( int cflag, int port ); +#endif /************************* End of Prototypes **************************/ @@ -668,6 +682,31 @@ void kgdb_init(void) } #endif +#ifdef CONFIG_MAC + if (MACH_IS_MAC) { + if (!strcmp( m68k_debug_device, "ser" ) || + !strcmp( m68k_debug_device, "ser1" )) { + mac_init_scc_port( B9600|CS8, 0 ); + serial_in = mac_scca_in; + serial_out = mac_scca_out; + serial_intr = mac_scca_intr; + } else if (!strcmp( m68k_debug_device, "ser2" )) { + mac_init_scc_port( B9600|CS8, 1 ); + serial_in = mac_sccb_in; + serial_out = mac_sccb_out; + serial_intr = mac_sccb_intr; + } + } + if (!serial_in || !serial_out) { + if (*m68k_debug_device) + printk( "kgdb_init failed: no valid serial device!\n" ); + else + printk( "kgdb not enabled\n" ); + return; + } + request_irq(4, kgdb_intr, IRQ_TYPE_FAST, "kgdb", NULL); +#endif + #ifdef CONFIG_ATARI if (!serial_in || !serial_out) { if (*m68k_debug_device) @@ -781,8 +820,15 @@ __asm__ /* copy format/vector word */ " movew %a0@("FRAMEOFF_VECTOR"),%a1@("GDBOFF_VECTOR")\n" /* save FPU regs */ +#ifndef CONFIG_M68KFPU_EMU_ONLY +#ifdef CONFIG_M68KFPU_EMU + " tstl "SYMBOL_NAME_STR(m68k_fputype)"\n" + " jeq 1f\n" +#endif " fmovemx %fp0-%fp7,%a1@("GDBOFF_FP0")\n" " fmoveml %fpcr/%fpsr/%fpiar,%a1@("GDBOFF_FPCTL")\n" + "1:\n" +#endif /* CONFIG_M68KFPU_EMU_ONLY */ /* set stack to CPU frame */ " addl #"FRAMEOFF_SR",%a0\n" @@ -801,8 +847,15 @@ __asm__ /* after return, first restore FPU registers */ " movel #"SYMBOL_NAME_STR(kgdb_registers)",%a0\n" /* source */ +#ifndef CONFIG_M68KFPU_EMU_ONLY +#ifdef CONFIG_M68KFPU_EMU + " tstl "SYMBOL_NAME_STR(m68k_fputype)"\n" + " jeq 1f\n" +#endif " fmovemx %a0@("GDBOFF_FP0"),%fp0-%fp7\n" " fmoveml %a0@("GDBOFF_FPCTL"),%fpcr/%fpsr/%fpiar\n" + "1:\n" +#endif /* CONFIG_M68KFPU_EMU_ONLY */ /* set new stack pointer */ " movel %a0@("GDBOFF_A7"),%sp\n" " clrw %sp@-\n" /* fake format $0 frame */ @@ -849,8 +902,15 @@ __asm__ /* fake format 0 and vector 1 (translated to SIGINT) */ " movew #4,%a1@("GDBOFF_VECTOR")\n" /* save FPU regs */ +#ifndef CONFIG_M68KFPU_EMU_ONLY +#ifdef CONFIG_M68KFPU_EMU + " tstl "SYMBOL_NAME_STR(m68k_fputype)"\n" + " jeq 1f\n" +#endif " fmovemx %fp0-%fp7,%a1@("GDBOFF_FP0")\n" " fmoveml %fpcr/%fpsr/%fpiar,%a1@("GDBOFF_FPCTL")\n" + "1:\n" +#endif /* CONFIG_M68KFPU_EMU_ONLY */ /* pop off the CPU stack frame */ " addql #8,%sp\n" " movel %sp,%a1@("GDBOFF_A7")\n" /* save a7 now */ @@ -1192,3 +1252,111 @@ static unsigned char atari_scc_intr( void ) } #endif + +/* -------------------- Macintosh serial I/O -------------------- */ + +#ifdef CONFIG_MAC + +struct 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 SCC*)mac_bi_data.sccbase)) + +#define uSEC 1 +#define LONG_DELAY() \ + do { \ + int i; \ + for( i = 60*uSEC; i > 0; --i ) \ + barrier(); \ + } while(0) + +static unsigned char mac_sccb_out (unsigned char c) +{ + int i; + do { + LONG_DELAY(); + } while (!(scc.cha_b_ctrl & 0x04)); /* wait for tx buf empty */ + for( i = uSEC; i > 0; --i ) + barrier(); + scc.cha_b_data = c; +} + +static unsigned char mac_scca_out (unsigned char c) +{ + int i; + do { + LONG_DELAY(); + } while (!(scc.cha_a_ctrl & 0x04)); /* wait for tx buf empty */ + for( i = uSEC; i > 0; --i ) + barrier(); + scc.cha_a_data = c; +} + +static unsigned char mac_sccb_in( void ) +{ + do { + LONG_DELAY(); + } while( !(scc.cha_b_ctrl & 0x01) ); /* wait for rx buf filled */ + LONG_DELAY(); + return( scc.cha_b_data ); +} + +static unsigned char mac_scca_in( void ) + +{ + do { + LONG_DELAY(); + } while( !(scc.cha_a_ctrl & 0x01) ); /* wait for rx buf filled */ + LONG_DELAY(); + return( scc.cha_a_data ); +} + +static unsigned char mac_sccb_intr( void ) + +{ unsigned char c, stat; + + LONG_DELAY(); + scc.cha_b_ctrl = 1; /* RR1 */ + LONG_DELAY(); + stat = scc.cha_b_ctrl; + LONG_DELAY(); + c = scc.cha_b_data; + LONG_DELAY(); + if (stat & 0x30) { + scc.cha_b_ctrl = 0x30; /* error reset for overrun and parity */ + LONG_DELAY(); + } + scc.cha_b_ctrl = 0x38; /* reset highest IUS */ + LONG_DELAY(); + return( c ); +} + +static unsigned char mac_scca_intr( void ) + +{ unsigned char c, stat; + + LONG_DELAY(); + scc.cha_a_ctrl = 1; /* RR1 */ + LONG_DELAY(); + stat = scc.cha_a_ctrl; + LONG_DELAY(); + c = scc.cha_a_data; + LONG_DELAY(); + if (stat & 0x30) { + scc.cha_a_ctrl = 0x30; /* error reset for overrun and parity */ + LONG_DELAY(); + } + scc.cha_a_ctrl = 0x38; /* reset highest IUS */ + LONG_DELAY(); + return( c ); +} + +#endif diff --git a/arch/m68k/kernel/m68k_defs.c b/arch/m68k/kernel/m68k_defs.c index e2e6715e7..47cffcfa9 100644 --- a/arch/m68k/kernel/m68k_defs.c +++ b/arch/m68k/kernel/m68k_defs.c @@ -26,25 +26,34 @@ int main(void) DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); DEFINE(TASK_SIGPENDING, offsetof(struct task_struct, sigpending)); DEFINE(TASK_NEEDRESCHED, offsetof(struct task_struct, need_resched)); - DEFINE(TASK_TSS, offsetof(struct task_struct, tss)); + DEFINE(TASK_THREAD, offsetof(struct task_struct, thread)); DEFINE(TASK_MM, offsetof(struct task_struct, mm)); + DEFINE(TASK_ACTIVE_MM, offsetof(struct task_struct, active_mm)); /* offsets into the thread struct */ - DEFINE(TSS_KSP, offsetof(struct thread_struct, ksp)); - DEFINE(TSS_USP, offsetof(struct thread_struct, usp)); - DEFINE(TSS_SR, offsetof(struct thread_struct, sr)); - DEFINE(TSS_FS, offsetof(struct thread_struct, fs)); - DEFINE(TSS_CRP, offsetof(struct thread_struct, crp)); - DEFINE(TSS_ESP0, offsetof(struct thread_struct, esp0)); - DEFINE(TSS_FPREG, offsetof(struct thread_struct, fp)); - DEFINE(TSS_FPCNTL, offsetof(struct thread_struct, fpcntl)); - DEFINE(TSS_FPSTATE, offsetof(struct thread_struct, fpstate)); + DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp)); + DEFINE(THREAD_USP, offsetof(struct thread_struct, usp)); + DEFINE(THREAD_SR, offsetof(struct thread_struct, sr)); + DEFINE(THREAD_FS, offsetof(struct thread_struct, fs)); + DEFINE(THREAD_CRP, offsetof(struct thread_struct, crp)); + DEFINE(THREAD_ESP0, offsetof(struct thread_struct, esp0)); + DEFINE(THREAD_FPREG, offsetof(struct thread_struct, fp)); + DEFINE(THREAD_FPCNTL, offsetof(struct thread_struct, fpcntl)); + DEFINE(THREAD_FPSTATE, offsetof(struct thread_struct, fpstate)); /* offsets into the pt_regs */ DEFINE(PT_D0, offsetof(struct pt_regs, d0)); DEFINE(PT_ORIG_D0, offsetof(struct pt_regs, orig_d0)); + DEFINE(PT_D1, offsetof(struct pt_regs, d1)); + DEFINE(PT_D2, offsetof(struct pt_regs, d2)); + DEFINE(PT_D3, offsetof(struct pt_regs, d3)); + DEFINE(PT_D4, offsetof(struct pt_regs, d4)); + DEFINE(PT_D5, offsetof(struct pt_regs, d5)); + DEFINE(PT_A0, offsetof(struct pt_regs, a0)); + DEFINE(PT_A1, offsetof(struct pt_regs, a1)); + DEFINE(PT_A2, offsetof(struct pt_regs, a2)); + DEFINE(PT_PC, offsetof(struct pt_regs, pc)); DEFINE(PT_SR, offsetof(struct pt_regs, sr)); - /* bitfields are a bit difficult */ DEFINE(PT_VECTOR, offsetof(struct pt_regs, pc) + 4); @@ -69,6 +78,12 @@ int main(void) DEFINE(FBCON_FONT_DESC_DATA, offsetof(struct fbcon_font_desc, data)); DEFINE(FBCON_FONT_DESC_PREF, offsetof(struct fbcon_font_desc, pref)); + /* signal defines */ + DEFINE(SIGSEGV, SIGSEGV); + DEFINE(SEGV_MAPERR, SEGV_MAPERR); + DEFINE(SIGTRAP, SIGTRAP); + DEFINE(TRAP_TRACE, TRAP_TRACE); + /* offsets into the custom struct */ DEFINE(CUSTOMBASE, &custom); DEFINE(C_INTENAR, offsetof(struct CUSTOM, intenar)); diff --git a/arch/m68k/kernel/m68k_defs.h b/arch/m68k/kernel/m68k_defs.h index 4926a6dad..374e56e27 100644 --- a/arch/m68k/kernel/m68k_defs.h +++ b/arch/m68k/kernel/m68k_defs.h @@ -7,19 +7,29 @@ #define TASK_FLAGS 4 #define TASK_SIGPENDING 8 #define TASK_NEEDRESCHED 20 -#define TASK_TSS 470 -#define TASK_MM 622 -#define TSS_KSP 0 -#define TSS_USP 4 -#define TSS_SR 8 -#define TSS_FS 10 -#define TSS_CRP 12 -#define TSS_ESP0 20 -#define TSS_FPREG 24 -#define TSS_FPCNTL 120 -#define TSS_FPSTATE 132 +#define TASK_THREAD 482 +#define TASK_MM 634 +#define TASK_ACTIVE_MM 638 +#define THREAD_KSP 0 +#define THREAD_USP 4 +#define THREAD_SR 8 +#define THREAD_FS 10 +#define THREAD_CRP 12 +#define THREAD_ESP0 20 +#define THREAD_FPREG 24 +#define THREAD_FPCNTL 120 +#define THREAD_FPSTATE 132 #define PT_D0 32 #define PT_ORIG_D0 36 +#define PT_D1 0 +#define PT_D2 4 +#define PT_D3 8 +#define PT_D4 12 +#define PT_D5 16 +#define PT_A0 20 +#define PT_A1 24 +#define PT_A2 28 +#define PT_PC 46 #define PT_SR 44 #define PT_VECTOR 50 #define IRQ_HANDLER 0 @@ -35,6 +45,10 @@ #define FBCON_FONT_DESC_HEIGHT 12 #define FBCON_FONT_DESC_DATA 16 #define FBCON_FONT_DESC_PREF 20 +#define SIGSEGV 11 +#define SEGV_MAPERR 1 +#define SIGTRAP 5 +#define TRAP_TRACE 2 #define CUSTOMBASE -2132807680 #define C_INTENAR 28 #define C_INTREQR 30 diff --git a/arch/m68k/kernel/m68k_ksyms.c b/arch/m68k/kernel/m68k_ksyms.c index 5aae5953c..64af7cf7c 100644 --- a/arch/m68k/kernel/m68k_ksyms.c +++ b/arch/m68k/kernel/m68k_ksyms.c @@ -38,11 +38,14 @@ EXPORT_SYMBOL(mm_vtop); EXPORT_SYMBOL(mm_ptov); EXPORT_SYMBOL(mm_end_of_chunk); #endif -EXPORT_SYMBOL(mm_vtop_fallback); EXPORT_SYMBOL(m68k_realnum_memory); EXPORT_SYMBOL(m68k_memory); +#ifndef CONFIG_SUN3 +EXPORT_SYMBOL(mm_vtop_fallback); EXPORT_SYMBOL(__ioremap); EXPORT_SYMBOL(iounmap); +EXPORT_SYMBOL(kernel_set_cachemode); +#endif EXPORT_SYMBOL(m68k_debug_device); EXPORT_SYMBOL(dump_fpu); EXPORT_SYMBOL(dump_thread); @@ -53,10 +56,7 @@ EXPORT_SYMBOL(local_irq_count); EXPORT_SYMBOL(local_bh_count); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); -EXPORT_SYMBOL(kernel_set_cachemode); EXPORT_SYMBOL(kernel_thread); -EXPORT_SYMBOL(register_serial); -EXPORT_SYMBOL(unregister_serial); /* Networking helper routines. */ EXPORT_SYMBOL(csum_partial_copy); @@ -69,6 +69,7 @@ EXPORT_SYMBOL_NOVERS(__ashrdi3); EXPORT_SYMBOL_NOVERS(memcpy); EXPORT_SYMBOL_NOVERS(memset); EXPORT_SYMBOL_NOVERS(memcmp); +EXPORT_SYMBOL_NOVERS(memscan); EXPORT_SYMBOL_NOVERS(__down_failed); EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index 97f3bd151..ba94860f6 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -40,49 +40,64 @@ */ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; -struct mm_struct init_mm = INIT_MM; +struct mm_struct init_mm = INIT_MM(init_mm); union task_union init_task_union - __attribute__((section("init_task"), aligned(2*PAGE_SIZE))) - = { task: INIT_TASK }; +__attribute__((section("init_task"), aligned(KTHREAD_SIZE))) + = { task: INIT_TASK(init_task_union.task) }; + +asmlinkage void ret_from_fork(void); -asmlinkage void ret_from_exception(void); /* * The idle loop on an m68k.. */ -asmlinkage int sys_idle(void) +static void default_idle(void) { - if (current->pid != 0) - return -EPERM; - - /* endless idle loop with no priority at all */ - current->priority = 0; - current->counter = -100; - for (;;) { + while(1) { if (!current->need_resched) -#if defined(CONFIG_ATARI) && !defined(CONFIG_AMIGA) && !defined(CONFIG_MAC) +#if defined(MACH_ATARI_ONLY) && !defined(CONFIG_HADES) /* block out HSYNC on the atari (falcon) */ __asm__("stop #0x2200" : : : "cc"); -#else /* portable version */ +#else __asm__("stop #0x2000" : : : "cc"); -#endif /* machine compilation types */ +#endif schedule(); check_pgt_cache(); } } +void (*idle)(void) = default_idle; + +/* + * The idle thread. There's no useful work to be + * done, so just try to conserve power and have a + * low exit latency (ie sit in a loop waiting for + * somebody to say that they'd like to reschedule) + */ +void cpu_idle(void) +{ + /* endless idle loop with no priority at all */ + init_idle(); + current->priority = 0; + current->counter = -100; + idle(); +} + void machine_restart(char * __unused) { if (mach_reset) mach_reset(); + for (;;); } void machine_halt(void) { + if (mach_halt) + mach_halt(); + for (;;); } void machine_power_off(void) @@ -90,6 +105,10 @@ void machine_power_off(void) #if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF) apm_set_power_state(APM_STATE_OFF); #endif + + if (mach_power_off) + mach_power_off(); + for (;;); } void show_regs(struct pt_regs * regs) @@ -127,7 +146,7 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) "trap #0\n\t" /* Linux/m68k system call */ "tstl %0\n\t" /* child or parent */ "jne 1f\n\t" /* parent - jump */ - "lea %%sp@(-8192),%6\n\t" /* reload current */ + "lea %%sp@(%c7),%6\n\t" /* reload current */ "movel %3,%%sp@-\n\t" /* push argument */ "jsr %4@\n\t" /* call fn */ "movel %0,%%d1\n\t" /* pass exit value */ @@ -136,7 +155,8 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) "1:" : "=d" (retval) : "0" (__NR_clone), "i" (__NR_exit), - "r" (arg), "a" (fn), "d" (clone_arg), "r" (current) + "r" (arg), "a" (fn), "d" (clone_arg), "r" (current), + "i" (-KTHREAD_SIZE) : "d0", "d2"); pid = retval; } @@ -149,10 +169,11 @@ void flush_thread(void) { unsigned long zero = 0; set_fs(USER_DS); - current->tss.fs = __USER_DS; - asm volatile (".chip 68k/68881\n\t" - "frestore %0@\n\t" - ".chip 68k" : : "a" (&zero)); + current->thread.fs = __USER_DS; + if (!FPU_IS_EMU) + asm volatile (".chip 68k/68881\n\t" + "frestore %0@\n\t" + ".chip 68k" : : "a" (&zero)); } /* @@ -192,7 +213,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, struct switch_stack * childstack, *stack; unsigned long stack_offset, *retp; - stack_offset = 2*PAGE_SIZE - sizeof(struct pt_regs); + stack_offset = KTHREAD_SIZE - sizeof(struct pt_regs); childregs = (struct pt_regs *) ((unsigned long) p + stack_offset); *childregs = *regs; @@ -203,26 +224,28 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, childstack = ((struct switch_stack *) childregs) - 1; *childstack = *stack; - childstack->retpc = (unsigned long) ret_from_exception; + childstack->retpc = (unsigned long)ret_from_fork; - p->tss.usp = usp; - p->tss.ksp = (unsigned long)childstack; + p->thread.usp = usp; + p->thread.ksp = (unsigned long)childstack; /* * Must save the current SFC/DFC value, NOT the value when * the parent was last descheduled - RGH 10-08-96 */ - p->tss.fs = get_fs().seg; - - /* Copy the current fpu state */ - asm volatile ("fsave %0" : : "m" (p->tss.fpstate[0]) : "memory"); - - if (!CPU_IS_060 ? p->tss.fpstate[0] : p->tss.fpstate[2]) - asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t" - "fmoveml %/fpiar/%/fpcr/%/fpsr,%1" - : : "m" (p->tss.fp[0]), "m" (p->tss.fpcntl[0]) - : "memory"); - /* Restore the state in case the fpu was busy */ - asm volatile ("frestore %0" : : "m" (p->tss.fpstate[0])); + p->thread.fs = get_fs().seg; + + if (!FPU_IS_EMU) { + /* Copy the current fpu state */ + asm volatile ("fsave %0" : : "m" (p->thread.fpstate[0]) : "memory"); + + if (!CPU_IS_060 ? p->thread.fpstate[0] : p->thread.fpstate[2]) + asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t" + "fmoveml %/fpiar/%/fpcr/%/fpsr,%1" + : : "m" (p->thread.fp[0]), "m" (p->thread.fpcntl[0]) + : "memory"); + /* Restore the state in case the fpu was busy */ + asm volatile ("frestore %0" : : "m" (p->thread.fpstate[0])); + } return 0; } @@ -231,20 +254,34 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, int dump_fpu (struct pt_regs *regs, struct user_m68kfp_struct *fpu) { - char fpustate[216]; + char fpustate[216]; + + if (FPU_IS_EMU) { + int i; + + memcpy(fpu->fpcntl, current->thread.fpcntl, 12); + memcpy(fpu->fpregs, current->thread.fp, 96); + /* Convert internal fpu reg representation + * into long double format + */ + for (i = 0; i < 24; i += 3) + fpu->fpregs[i] = ((fpu->fpregs[i] & 0xffff0000) << 15) | + ((fpu->fpregs[i] & 0x0000ffff) << 16); + return 1; + } - /* First dump the fpu context to avoid protocol violation. */ - asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory"); - if (!CPU_IS_060 ? !fpustate[0] : !fpustate[2]) - return 0; + /* First dump the fpu context to avoid protocol violation. */ + asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory"); + if (!CPU_IS_060 ? !fpustate[0] : !fpustate[2]) + return 0; - asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0" + asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0" :: "m" (fpu->fpcntl[0]) : "memory"); - asm volatile ("fmovemx %/fp0-%/fp7,%0" + asm volatile ("fmovemx %/fp0-%/fp7,%0" :: "m" (fpu->fpregs[0]) : "memory"); - return 1; + return 1; } /* diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c index 86e225362..7db64849d 100644 --- a/arch/m68k/kernel/ptrace.c +++ b/arch/m68k/kernel/ptrace.c @@ -18,6 +18,7 @@ #include <linux/errno.h> #include <linux/ptrace.h> #include <linux/user.h> +#include <linux/config.h> #include <asm/uaccess.h> #include <asm/page.h> @@ -37,7 +38,7 @@ /* sets the trace bits. */ #define TRACE_BITS 0x8000 -/* Find the stack offset for a register, relative to tss.esp0. */ +/* Find the stack offset for a register, relative to thread.esp0. */ #define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg) #define SW_REG(reg) ((long)&((struct switch_stack *)0)->reg \ - sizeof(struct switch_stack)) @@ -60,9 +61,9 @@ static inline long get_reg(struct task_struct *task, int regno) unsigned long *addr; if (regno == PT_USP) - addr = &task->tss.usp; + addr = &task->thread.usp; else if (regno < sizeof(regoff)/sizeof(regoff[0])) - addr = (unsigned long *)(task->tss.esp0 + regoff[regno]); + addr = (unsigned long *)(task->thread.esp0 + regoff[regno]); else return 0; return *addr; @@ -77,9 +78,9 @@ static inline int put_reg(struct task_struct *task, int regno, unsigned long *addr; if (regno == PT_USP) - addr = &task->tss.usp; + addr = &task->thread.usp; else if (regno < sizeof(regoff)/sizeof(regoff[0])) - addr = (unsigned long *) (task->tss.esp0 + regoff[regno]); + addr = (unsigned long *) (task->thread.esp0 + regoff[regno]); else return -1; *addr = data; @@ -384,10 +385,17 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) tmp = get_reg(child, addr); if (addr == PT_SR) tmp >>= 16; - } - else if (addr >= 21 && addr < 49) - tmp = child->tss.fp[addr - 21]; - else + } else if (addr >= 21 && addr < 49) { + tmp = child->thread.fp[addr - 21]; +#ifdef CONFIG_M68KFPU_EMU + /* Convert internal fpu reg representation + * into long double format + */ + if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) + tmp = ((tmp & 0xffff0000) << 15) | + ((tmp & 0x0000ffff) << 16); +#endif + } else goto out; ret = put_user(tmp,(unsigned long *) data); goto out; @@ -423,7 +431,17 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) } if (addr >= 21 && addr < 48) { - child->tss.fp[addr - 21] = data; +#ifdef CONFIG_M68KFPU_EMU + /* Convert long double format + * into internal fpu reg representation + */ + if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) { + data = (unsigned long)data << 15; + data = (data & 0xffff0000) | + ((data & 0x0000ffff) >> 1); + } +#endif + child->thread.fp[addr - 21] = data; ret = 0; } goto out; @@ -544,7 +562,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_GETFPREGS: { /* Get the child FPU state. */ ret = 0; - if (copy_to_user((void *)data, &child->tss.fp, + if (copy_to_user((void *)data, &child->thread.fp, sizeof(struct user_m68kfp_struct))) ret = -EFAULT; goto out; @@ -552,7 +570,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_SETFPREGS: { /* Set the child FPU state. */ ret = 0; - if (copy_from_user(&child->tss.fp, (void *)data, + if (copy_from_user(&child->thread.fp, (void *)data, sizeof(struct user_m68kfp_struct))) ret = -EFAULT; goto out; diff --git a/arch/m68k/kernel/semaphore.c b/arch/m68k/kernel/semaphore.c new file mode 100644 index 000000000..d62b355e1 --- /dev/null +++ b/arch/m68k/kernel/semaphore.c @@ -0,0 +1,129 @@ +/* + * Generic semaphore code. Buyer beware. Do your own + * specific changes in <asm/semaphore-helper.h> + */ + +#include <linux/sched.h> +#include <asm/semaphore-helper.h> + +/* + * Semaphores are implemented using a two-way counter: + * The "count" variable is decremented for each process + * that tries to sleep, while the "waking" variable is + * incremented when the "up()" code goes to wake up waiting + * processes. + * + * Notably, the inline "up()" and "down()" functions can + * efficiently test if they need to do any extra work (up + * needs to do something only if count was negative before + * the increment operation. + * + * waking_non_zero() (from asm/semaphore.h) must execute + * atomically. + * + * When __up() is called, the count was negative before + * incrementing it, and we need to wake up somebody. + * + * This routine adds one to the count of processes that need to + * wake up and exit. ALL waiting processes actually wake up but + * only the one that gets to the "waking" field first will gate + * through and acquire the semaphore. The others will go back + * to sleep. + * + * Note that these functions are only called when there is + * contention on the lock, and as such all this is the + * "non-critical" part of the whole semaphore business. The + * critical part is the inline stuff in <asm/semaphore.h> + * where we want to avoid any extra jumps and calls. + */ +void __up(struct semaphore *sem) +{ + wake_one_more(sem); + wake_up(&sem->wait); +} + +/* + * Perform the "down" function. Return zero for semaphore acquired, + * return negative for signalled out of the function. + * + * If called from __down, the return is ignored and the wait loop is + * not interruptible. This means that a task waiting on a semaphore + * using "down()" cannot be killed until someone does an "up()" on + * the semaphore. + * + * If called from __down_interruptible, the return value gets checked + * upon return. If the return value is negative then the task continues + * with the negative value in the return register (it can be tested by + * the caller). + * + * Either form may be used in conjunction with "up()". + * + */ + +#define DOWN_VAR \ + struct task_struct *tsk = current; \ + wait_queue_t wait; \ + init_waitqueue_entry(&wait, tsk); + +#define DOWN_HEAD(task_state) \ + \ + \ + tsk->state = (task_state); \ + add_wait_queue(&sem->wait, &wait); \ + \ + /* \ + * Ok, we're set up. sem->count is known to be less than zero \ + * so we must wait. \ + * \ + * We can let go the lock for purposes of waiting. \ + * We re-acquire it after awaking so as to protect \ + * all semaphore operations. \ + * \ + * If "up()" is called before we call waking_non_zero() then \ + * we will catch it right away. If it is called later then \ + * we will have to go through a wakeup cycle to catch it. \ + * \ + * Multiple waiters contend for the semaphore lock to see \ + * who gets to gate through and who has to wait some more. \ + */ \ + for (;;) { + +#define DOWN_TAIL(task_state) \ + tsk->state = (task_state); \ + } \ + tsk->state = TASK_RUNNING; \ + remove_wait_queue(&sem->wait, &wait); + +void __down(struct semaphore * sem) +{ + DOWN_VAR + DOWN_HEAD(TASK_UNINTERRUPTIBLE) + if (waking_non_zero(sem)) + break; + schedule(); + DOWN_TAIL(TASK_UNINTERRUPTIBLE) +} + +int __down_interruptible(struct semaphore * sem) +{ + int ret = 0; + DOWN_VAR + DOWN_HEAD(TASK_INTERRUPTIBLE) + + ret = waking_non_zero_interruptible(sem, tsk); + if (ret) + { + if (ret == 1) + /* ret != 0 only if we get interrupted -arca */ + ret = 0; + break; + } + schedule(); + DOWN_TAIL(TASK_INTERRUPTIBLE) + return ret; +} + +int __down_trylock(struct semaphore * sem) +{ + return waking_non_zero_trylock(sem); +} diff --git a/arch/m68k/kernel/setup.c b/arch/m68k/kernel/setup.c index 39dcd9c15..adafc9879 100644 --- a/arch/m68k/kernel/setup.c +++ b/arch/m68k/kernel/setup.c @@ -19,6 +19,7 @@ #include <linux/errno.h> #include <linux/string.h> #include <linux/init.h> +#include <linux/module.h> #include <asm/bootinfo.h> #include <asm/setup.h> @@ -76,6 +77,8 @@ void (*mach_gettod) (int*, int*, int*, int*, int*, int*); int (*mach_hwclk) (int, struct hwclk_time*) = NULL; int (*mach_set_clock_mmss) (unsigned long) = NULL; void (*mach_reset)( void ); +void (*mach_halt)( void ) = NULL; +void (*mach_power_off)( void ) = NULL; long mach_max_dma_address = 0x00ffffff; /* default set to the lower 16MB */ #if defined(CONFIG_AMIGA_FLOPPY) || defined(CONFIG_ATARI_FLOPPY) || defined(CONFIG_BLK_DEV_FD) void (*mach_floppy_setup) (char *, int *) __initdata = NULL; @@ -100,6 +103,9 @@ long m68k_serial_console_init(long, long ); #ifdef CONFIG_HEARTBEAT void (*mach_heartbeat) (int) = NULL; #endif +#ifdef CONFIG_M68K_L2_CACHE +void (*mach_l2_flush) (int) = NULL; +#endif extern void base_trap_init(void); @@ -118,7 +124,7 @@ extern int q40_parse_bootinfo(const struct bi_record *); extern void config_amiga(void); extern void config_atari(void); extern void config_mac(void); -extern void config_sun3(void); +extern void config_sun3(unsigned long *, unsigned long *); extern void config_apollo(void); extern void config_mvme147(void); extern void config_mvme16x(void); @@ -127,10 +133,12 @@ extern void config_hp300(void); extern void config_q40(void); extern void config_sun3x(void); -#define MASK_256K 0xfffc0000 +extern void mac_debugging_short (int, short); +extern void mac_debugging_long (int, long); +#define MASK_256K 0xfffc0000 -__initfunc(static void m68k_parse_bootinfo(const struct bi_record *record)) +static void __init m68k_parse_bootinfo(const struct bi_record *record) { while (record->tag != BI_LAST) { int unknown = 0; @@ -190,8 +198,8 @@ __initfunc(static void m68k_parse_bootinfo(const struct bi_record *record)) #endif } -__initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, - unsigned long * memory_end_p)) +void __init setup_arch(char **cmdline_p, unsigned long * memory_start_p, + unsigned long * memory_end_p) { extern int _etext, _edata, _end; int i; @@ -205,18 +213,27 @@ __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, else if (CPU_IS_060) m68k_is040or060 = 6; +#ifndef CONFIG_SUN3 base_trap_init(); +#endif + /* FIXME: m68k_fputype is passed in by Penguin booter, which can + * be confused by software FPU emulation. BEWARE. + * We should really do our own FPU check at startup. + * [what do we do with buggy 68LC040s? if we have problems + * with them, we should add a test to check_bugs() below] */ +#ifndef CONFIG_M68KFPU_EMU_ONLY /* clear the fpu if we have one */ if (m68k_fputype & (FPU_68881|FPU_68882|FPU_68040|FPU_68060)) { volatile int zero = 0; asm __volatile__ ("frestore %0" : : "m" (zero)); } +#endif - init_task.mm->start_code = PAGE_OFFSET; - init_task.mm->end_code = (unsigned long) &_etext; - init_task.mm->end_data = (unsigned long) &_edata; - init_task.mm->brk = (unsigned long) &_end; + init_mm.start_code = PAGE_OFFSET; + init_mm.end_code = (unsigned long) &_etext; + init_mm.end_data = (unsigned long) &_edata; + init_mm.brk = (unsigned long) &_end; *cmdline_p = m68k_command_line; memcpy(saved_command_line, *cmdline_p, CL_SIZE); @@ -272,7 +289,7 @@ __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, #endif #ifdef CONFIG_SUN3 case MACH_SUN3: - config_sun3(); + config_sun3(memory_start_p, memory_end_p); break; #endif #ifdef CONFIG_APOLLO @@ -321,10 +338,12 @@ __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, } #endif +#ifndef CONFIG_SUN3 *memory_start_p = availmem; *memory_end_p = 0; for (i = 0; i < m68k_num_memory; i++) *memory_end_p += m68k_memory[i].size & MASK_256K; +#endif } int get_cpuinfo(char * buffer) @@ -354,6 +373,9 @@ int get_cpuinfo(char * buffer) clockfactor = 0; } +#ifdef CONFIG_M68KFPU_EMU_ONLY + fpu="none(soft float)"; +#else if (m68k_fputype & FPU_68881) fpu = "68881"; else if (m68k_fputype & FPU_68882) @@ -366,6 +388,7 @@ int get_cpuinfo(char * buffer) fpu = "Sun FPA"; else fpu = "none"; +#endif if (m68k_mmutype & MMU_68851) mmu = "68851"; @@ -452,6 +475,9 @@ void unregister_serial(int i) m68k_unregister_serial(i); #endif } +EXPORT_SYMBOL(register_serial); +EXPORT_SYMBOL(unregister_serial); + #ifdef CONFIG_SERIAL_CONSOLE long serial_console_init(long kmem_start, long kmem_end) { @@ -461,13 +487,15 @@ long serial_console_init(long kmem_start, long kmem_end) #endif #if defined(M68K_SERIAL) && defined(CONFIG_SERIAL_CONSOLE) return m68k_serial_console_init(kmem_start, kmem_end); +#else + return kmem_start; #endif } #endif #endif #if defined(CONFIG_AMIGA_FLOPPY) || defined(CONFIG_ATARI_FLOPPY) || defined(CONFIG_BLK_DEV_FD) -__initfunc(void floppy_setup(char *str, int *ints)) +void __init floppy_setup(char *str, int *ints) { if (mach_floppy_setup) mach_floppy_setup (str, ints); @@ -481,7 +509,7 @@ void floppy_eject(void) #endif /* for "kbd-reset" cmdline param */ -__initfunc(void kbd_reset_setup(char *str, int *ints)) +void __init kbd_reset_setup(char *str, int *ints) { } @@ -496,7 +524,7 @@ void arch_gettod(int *year, int *mon, int *day, int *hour, void check_bugs(void) { -#ifndef CONFIG_FPU_EMU +#ifndef CONFIG_M68KFPU_EMU if (m68k_fputype == 0) { printk( KERN_EMERG "*** YOU DO NOT HAVE A FLOATING POINT UNIT, " "WHICH IS REQUIRED BY LINUX/M68K ***\n" ); diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c index a2b9ce1d4..f35dd94c5 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c @@ -14,6 +14,10 @@ * 68060 fixes by Jesper Skov * * 1997-12-01 Modified for POSIX.1b signals by Andreas Schwab + * + * mathemu support by Roman Zippel + * (Note: fpstate in the signal context is completly ignored for the emulator + * and the internal floating point format is put on stack) */ /* @@ -156,6 +160,9 @@ sys_sigaltstack(const stack_t *uss, stack_t *uoss) /* * Do a signal return; undo the signal stack. + * + * Keep the return code on the stack quadword aligned! + * That makes the cache flush below easier. */ struct sigframe @@ -175,9 +182,9 @@ struct rt_sigframe int sig; struct siginfo *pinfo; void *puc; + char retcode[8]; struct siginfo info; struct ucontext uc; - char retcode[8]; }; @@ -187,6 +194,13 @@ static inline int restore_fpu_state(struct sigcontext *sc) { int err = 1; + if (FPU_IS_EMU) { + /* restore registers */ + memcpy(current->thread.fpcntl, sc->sc_fpcntl, 12); + memcpy(current->thread.fp, sc->sc_fpregs, 24); + return 0; + } + if (CPU_IS_060 ? sc->sc_fpstate[2] : sc->sc_fpstate[0]) { /* Verify the frame format. */ if (!CPU_IS_060 && (sc->sc_fpstate[0] != fpu_version)) @@ -239,6 +253,18 @@ static inline int rt_restore_fpu_state(struct ucontext *uc) fpregset_t fpregs; int err = 1; + if (FPU_IS_EMU) { + /* restore fpu control register */ + if (__copy_from_user(current->thread.fpcntl, + &uc->uc_mcontext.fpregs.f_pcr, 12)) + goto out; + /* restore all other fpu register */ + if (__copy_from_user(current->thread.fp, + uc->uc_mcontext.fpregs.f_fpregs, 96)) + goto out; + return 0; + } + if (__get_user(*(long *)fpstate, (long *)&uc->uc_fpstate)) goto out; if (CPU_IS_060 ? fpstate[2] : fpstate[0]) { @@ -478,7 +504,7 @@ asmlinkage int do_sigreturn(unsigned long __unused) struct switch_stack *sw = (struct switch_stack *) &__unused; struct pt_regs *regs = (struct pt_regs *) (sw + 1); unsigned long usp = rdusp(); - struct sigframe *frame = (struct sigframe *)(usp - 24); + struct sigframe *frame = (struct sigframe *)(usp - 4); sigset_t set; int d0; @@ -536,6 +562,13 @@ badframe: static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs) { + if (FPU_IS_EMU) { + /* save registers */ + memcpy(sc->sc_fpcntl, current->thread.fpcntl, 12); + memcpy(sc->sc_fpregs, current->thread.fp, 24); + return; + } + __asm__ volatile (".chip 68k/68881\n\t" "fsave %0\n\t" ".chip 68k" @@ -567,6 +600,16 @@ static inline int rt_save_fpu_state(struct ucontext *uc, struct pt_regs *regs) int context_size = CPU_IS_060 ? 8 : 0; int err = 0; + if (FPU_IS_EMU) { + /* save fpu control register */ + err |= copy_to_user(&uc->uc_mcontext.fpregs.f_pcr, + current->thread.fpcntl, 12); + /* save all other fpu register */ + err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpregs, + current->thread.fp, 96); + return err; + } + __asm__ volatile (".chip 68k/68881\n\t" "fsave %0\n\t" ".chip 68k" @@ -677,25 +720,6 @@ static inline void push_cache (unsigned long vaddr) "cpushl %%bc,(%0)\n\t" ".chip 68k" : : "a" (temp)); - if (((vaddr + 8) ^ vaddr) & ~15) { - if (((vaddr + 8) ^ vaddr) & PAGE_MASK) - __asm__ __volatile__ (".chip 68040\n\t" - "nop\n\t" - "ptestr (%1)\n\t" - "movec %%mmusr,%0\n\t" - ".chip 68k" - : "=r" (temp) - : "a" (vaddr + 8)); - - temp &= PAGE_MASK; - temp |= (vaddr + 8) & ~PAGE_MASK; - - __asm__ __volatile__ (".chip 68040\n\t" - "nop\n\t" - "cpushl %%bc,(%0)\n\t" - ".chip 68k" - : : "a" (temp)); - } } else if (CPU_IS_060) { unsigned long temp; @@ -708,18 +732,6 @@ static inline void push_cache (unsigned long vaddr) "cpushl %%bc,(%0)\n\t" ".chip 68k" : : "a" (temp)); - if (((vaddr + 8) ^ vaddr) & ~15) { - if (((vaddr + 8) ^ vaddr) & PAGE_MASK) - __asm__ __volatile__ (".chip 68060\n\t" - "plpar (%0)\n\t" - ".chip 68k" - : "=a" (temp) - : "0" (vaddr + 8)); - __asm__ __volatile__ (".chip 68060\n\t" - "cpushl %%bc,(%0)\n\t" - ".chip 68k" - : : "a" (temp)); - } } else { /* @@ -797,11 +809,9 @@ static void setup_frame (int sig, struct k_sigaction *ka, /* Set up to return from userspace. */ err |= __put_user(frame->retcode, &frame->pretcode); - /* addaw #20,sp */ - err |= __put_user(0xdefc0014, (long *)(frame->retcode + 0)); /* moveq #,d0; trap #0 */ err |= __put_user(0x70004e40 + (__NR_sigreturn << 16), - (long *)(frame->retcode + 4)); + (long *)(frame->retcode)); if (err) goto give_sigsegv; @@ -881,10 +891,10 @@ static void setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info, /* Set up to return from userspace. */ err |= __put_user(frame->retcode, &frame->pretcode); - /* movel #,d0; trap #0 */ - err |= __put_user(0x203c, (short *)(frame->retcode + 0)); - err |= __put_user(__NR_rt_sigreturn, (long *)(frame->retcode + 2)); - err |= __put_user(0x4e40, (short *)(frame->retcode + 6)); + /* moveq #,d0; notb d0; trap #0 */ + err |= __put_user(0x70004600 + ((__NR_rt_sigreturn ^ 0xff) << 16), + (long *)(frame->retcode + 0)); + err |= __put_user(0x4e40, (short *)(frame->retcode + 4)); if (err) goto give_sigsegv; @@ -964,11 +974,10 @@ handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, if (ka->sa.sa_flags & SA_ONESHOT) ka->sa.sa_handler = SIG_DFL; - if (!(ka->sa.sa_flags & SA_NODEFER)) { - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) sigaddset(¤t->blocked,sig); - recalc_sigpending(current); - } + recalc_sigpending(current); } /* @@ -985,7 +994,7 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs) siginfo_t info; struct k_sigaction *ka; - current->tss.esp0 = (unsigned long) regs; + current->thread.esp0 = (unsigned long) regs; if (!oldset) oldset = ¤t->blocked; @@ -1084,14 +1093,13 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs) case SIGQUIT: case SIGILL: case SIGTRAP: case SIGIOT: case SIGFPE: case SIGSEGV: - if (current->binfmt - && current->binfmt->core_dump - && current->binfmt->core_dump(signr, regs)) + if (do_coredump(signr, regs)) exit_code |= 0x80; /* FALLTHRU */ default: sigaddset(¤t->signal, signr); + recalc_sigpending(current); current->flags |= PF_SIGNALED; do_exit(exit_code); /* NOTREACHED */ diff --git a/arch/m68k/kernel/sun3-head.S b/arch/m68k/kernel/sun3-head.S new file mode 100644 index 000000000..fa17d83a9 --- /dev/null +++ b/arch/m68k/kernel/sun3-head.S @@ -0,0 +1,102 @@ +#include <linux/config.h> +#include <linux/linkage.h> + +#include <asm/page.h> +#include <asm/contregs.h> +#include <asm/sun3-head.h> + +PSL_HIGHIPL = 0x2700 +NBSG = 0x20000 +ICACHE_ONLY = 0x00000009 +CACHES_OFF = 0x00000008 | actually a clear and disable --m +#define MAS_STACK INT_STACK +ROOT_TABLE_SIZE = 128 +PAGESIZE = 8192 + +.globl SYMBOL_NAME(bootup_user_stack) +.globl SYMBOL_NAME(bootup_kernel_stack) +.globl SYMBOL_NAME(pg0) +.globl SYMBOL_NAME(empty_bad_page) +.globl SYMBOL_NAME(empty_bad_page_table) +.globl SYMBOL_NAME(empty_zero_page) +.globl SYMBOL_NAME(swapper_pg_dir) +.globl SYMBOL_NAME(kernel_pmd_table) +.global SYMBOL_NAME(m68k_pgtable_cachemode) +.global SYMBOL_NAME(kpt) +| todo: all these should be in bss! +SYMBOL_NAME(swapper_pg_dir): .skip 0x2000 +SYMBOL_NAME(pg0): .skip 0x2000 +SYMBOL_NAME(empty_bad_page): .skip 0x2000 +SYMBOL_NAME(empty_bad_page_table): .skip 0x2000 +SYMBOL_NAME(kernel_pmd_table): .skip 0x2000 +SYMBOL_NAME(empty_zero_page): .skip 0x2000 + +.globl SYMBOL_NAME(kernel_pg_dir) +.equ SYMBOL_NAME(kernel_pg_dir),SYMBOL_NAME(kernel_pmd_table) + + .section .head +ENTRY(_stext) +ENTRY(_start) + +/* Firstly, disable interrupts and set up function codes. */ + movew #PSL_HIGHIPL, %sr + moveq #FC_CONTROL, %d0 + movec %d0, %sfc + movec %d0, %dfc + +/* Make sure we're in context zero. */ + moveq #0, %d0 + movsb %d0, AC_CONTEXT + +/* Copy mappings for first megabyte of RAM to address 0xE000000. */ + lea (AC_SEGMAP+0),%a0 + lea (AC_SEGMAP+KERNBASE),%a1 + moveq #(0x140000/NBSG-1),%d0 +1: movsb %a0@,%d1 + movsb %d1,%a1@ + addl #NBSG,%a0 + addl #NBSG,%a1 + dbf %d0,1b + +/* Disable caches and jump to high code. */ + moveq #ICACHE_ONLY,%d0 | Cache disabled until we're ready to enable it + movc %d0, %cacr | is this the right value? (yes --m) + jmp 1f:l + +/* Following code executes at high addresses (0xE000xxx). */ +1: lea SYMBOL_NAME(init_task_union),%a2 | get initial thread... + lea %a2@(KTHREAD_SIZE),%sp | ...and its stack. + +/* copy bootinfo records from the loader to _end */ + lea SYMBOL_NAME(_end), %a1 + lea BI_START, %a0 + /* number of longs to copy */ + movel %a0@, %d0 +1: addl #4, %a0 + movel %a0@, %a1@ + addl #4, %a1 + dbf %d0, 1b + +/* Point MSP at an invalid page to trap if it's used. --m */ + movl #(PAGESIZE),%d0 + movc %d0,%msp + moveq #-1,%d0 + movsb %d0,(AC_SEGMAP+0x0) + + jbsr SYMBOL_NAME(sun3_init) + + jbsr SYMBOL_NAME(start_kernel) + trap #15 + + .data + .even +SYMBOL_NAME_LABEL(kpt) + .long 0 +SYMBOL_NAME_LABEL(availmem) + .long 0 +| todo: remove next two. --m +SYMBOL_NAME_LABEL(is_medusa) + .long 0 +SYMBOL_NAME_LABEL(m68k_pgtable_cachemode) + .long 0 + diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index 1f07014b2..e3a9e560b 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c @@ -47,6 +47,9 @@ asmlinkage void buserr(void); asmlinkage void trap(void); asmlinkage void inthandler(void); asmlinkage void nmihandler(void); +#ifdef CONFIG_M68KFPU_EMU +asmlinkage void fpu_emu(void); +#endif e_vector vectors[256] = { 0, 0, buserr, trap, trap, trap, trap, trap, @@ -65,12 +68,21 @@ asm(".text\n" __ALIGN_STR "\n" SYMBOL_NAME_STR(nmihandler) ": rte"); -__initfunc(void base_trap_init(void)) +void __init base_trap_init(void) { +#ifdef CONFIG_SUN3 + /* Keep the keyboard interrupt working with PROM for debugging. --m */ + e_vector *old_vbr; + __asm__ volatile ("movec %%vbr,%1\n\t" + "movec %0,%%vbr" + : "=&r" (old_vbr) : "r" ((void*)vectors)); + vectors[0x1E] = old_vbr[0x1E]; /* Copy int6 vector. */ +#else /* setup the exception vector table */ __asm__ volatile ("movec %0,%%vbr" : : "r" ((void*)vectors)); +#endif - if (CPU_IS_040) { + if (CPU_IS_040 && !FPU_IS_EMU) { /* set up FPSP entry points */ asmlinkage void dz_vec(void) asm ("dz"); asmlinkage void inex_vec(void) asm ("inex"); @@ -93,6 +105,12 @@ __initfunc(void base_trap_init(void)) vectors[VEC_FPUNSUP] = unsupp_vec; } if (CPU_IS_060) { + /* set up ISP entry points */ + asmlinkage void unimp_vec(void) asm ("_060_isp_unimp"); + + vectors[VEC_UNIMPII] = unimp_vec; + } + if (CPU_IS_060 && !FPU_IS_EMU) { /* set up IFPSP entry points */ asmlinkage void snan_vec(void) asm ("_060_fpsp_snan"); asmlinkage void operr_vec(void) asm ("_060_fpsp_operr"); @@ -104,8 +122,6 @@ __initfunc(void base_trap_init(void)) asmlinkage void unsupp_vec(void) asm ("_060_fpsp_unsupp"); asmlinkage void effadd_vec(void) asm ("_060_fpsp_effadd"); - asmlinkage void unimp_vec(void) asm ("_060_isp_unimp"); - vectors[VEC_FPNAN] = snan_vec; vectors[VEC_FPOE] = operr_vec; vectors[VEC_FPOVER] = ovfl_vec; @@ -115,14 +131,10 @@ __initfunc(void base_trap_init(void)) vectors[VEC_LINE11] = fline_vec; vectors[VEC_FPUNSUP] = unsupp_vec; vectors[VEC_UNIMPEA] = effadd_vec; - - /* set up ISP entry points */ - - vectors[VEC_UNIMPII] = unimp_vec; } } -__initfunc(void trap_init (void)) +void __init trap_init (void) { int i; @@ -133,16 +145,19 @@ __initfunc(void trap_init (void)) for (i = 64; i < 256; i++) vectors[i] = inthandler; +#ifdef CONFIG_M68KFPU_EMU + if (FPU_IS_EMU) + vectors[VEC_LINE11] = fpu_emu; +#endif + /* if running on an amiga, make the NMI interrupt do nothing */ if (MACH_IS_AMIGA) { vectors[VEC_INT7] = nmihandler; } -} - -void set_evector(int vecnum, void (*handler)(void)) -{ - if (vecnum >= 0 && vecnum <= 256) - vectors[vecnum] = handler; +#ifdef CONFIG_SUN3 + /* Moved from setup_arch() */ + base_trap_init(); +#endif } @@ -152,6 +167,7 @@ static inline void console_verbose(void) console_loglevel = 15; } + static char *vec_names[] = { "RESET SP", "RESET PC", "BUS ERROR", "ADDRESS ERROR", "ILLEGAL INSTRUCTION", "ZERO DIVIDE", "CHK", "TRAPcc", @@ -174,12 +190,17 @@ static char *vec_names[] = { "MMU CONFIGURATION ERROR" }; +#ifndef CONFIG_SUN3 static char *space_names[] = { "Space 0", "User Data", "User Program", "Space 3", "Space 4", "Super Data", "Super Program", "CPU" }; - - +#else +static char *space_names[] = { + "Space 0", "User Data", "User Program", "Control", + "Space 4", "Super Data", "Super Program", "CPU" + }; +#endif void die_if_kernel(char *,struct pt_regs *,int); asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, @@ -330,6 +351,17 @@ static inline void access_error040 (struct frame *fp) #endif errorcode = ((mmusr & MMU_R_040) ? 1 : 0) | ((ssw & RW_040) ? 0 : 2); +#ifdef CONFIG_FTRACE + { + unsigned long flags; + + save_flags(flags); + cli(); + do_ftrace(0xfa000000 | errorcode); + do_ftrace(mmusr); + restore_flags(flags); + } +#endif do_page_fault (&fp->ptregs, addr, errorcode); } else { printk ("68040 access error, ssw=%x\n", ssw); @@ -363,6 +395,132 @@ static inline void access_error040 (struct frame *fp) } #endif /* CONFIG_M68040 */ +#if defined(CONFIG_SUN3) +#include <asm/sun3mmu.h> + +extern int mmu_emu_handle_fault (unsigned long, int, int); + +/* sun3 version of bus_error030 */ + +extern inline void bus_error030 (struct frame *fp) +{ + unsigned char buserr_type = sun3_get_buserr (); + unsigned long addr, errorcode; + unsigned short ssw = fp->un.fmtb.ssw; + +#if DEBUG + if (ssw & (FC | FB)) + printk ("Instruction fault at %#010lx\n", + ssw & FC ? + fp->ptregs.format == 0xa ? fp->ptregs.pc + 2 : fp->un.fmtb.baddr - 2 + : + fp->ptregs.format == 0xa ? fp->ptregs.pc + 4 : fp->un.fmtb.baddr); + if (ssw & DF) + printk ("Data %s fault at %#010lx in %s (pc=%#lx)\n", + ssw & RW ? "read" : "write", + fp->un.fmtb.daddr, + space_names[ssw & DFC], fp->ptregs.pc); +#endif + + /* + * Check if this page should be demand-mapped. This needs to go before + * the testing for a bad kernel-space access (demand-mapping applies + * to kernel accesses too). + */ + + if ((ssw & DF) + && (buserr_type & (SUN3_BUSERR_PROTERR | SUN3_BUSERR_INVALID))) { + if (mmu_emu_handle_fault (fp->un.fmtb.daddr, ssw & RW, 0)) + return; + } + + /* Check for kernel-space pagefault (BAD). */ + if (fp->ptregs.sr & PS_S) { + /* kernel fault must be a data fault to user space */ + if (! ((ssw & DF) && ((ssw & DFC) == USER_DATA))) { + // try checking the kernel mappings before surrender + if (mmu_emu_handle_fault (fp->un.fmtb.daddr, ssw & RW, 1)) + return; + /* instruction fault or kernel data fault! */ + if (ssw & (FC | FB)) + printk ("Instruction fault at %#010lx\n", + fp->ptregs.pc); + if (ssw & DF) { + printk ("Data %s fault at %#010lx in %s (pc=%#lx)\n", + ssw & RW ? "read" : "write", + fp->un.fmtb.daddr, + space_names[ssw & DFC], fp->ptregs.pc); + } + printk ("BAD KERNEL BUSERR\n"); + + die_if_kernel("Oops", &fp->ptregs,0); + force_sig(SIGKILL, current); + return; + } + } else { + /* user fault */ + if (!(ssw & (FC | FB)) && !(ssw & DF)) + /* not an instruction fault or data fault! BAD */ + panic ("USER BUSERR w/o instruction or data fault"); + } + + + /* First handle the data fault, if any. */ + if (ssw & DF) { + addr = fp->un.fmtb.daddr; + +// errorcode bit 0: 0 -> no page 1 -> protection fault +// errorcode bit 1: 0 -> read fault 1 -> write fault + +// (buserr_type & SUN3_BUSERR_PROTERR) -> protection fault +// (buserr_type & SUN3_BUSERR_INVALID) -> invalid page fault + + if (buserr_type & SUN3_BUSERR_PROTERR) + errorcode = 0x01; + else if (buserr_type & SUN3_BUSERR_INVALID) + errorcode = 0x00; + else { + printk ("*** unexpected busfault type=%#04x\n", buserr_type); + printk ("invalid %s access at %#lx from pc %#lx\n", + !(ssw & RW) ? "write" : "read", addr, + fp->ptregs.pc); + die_if_kernel ("Oops", &fp->ptregs, buserr_type); + force_sig (SIGSEGV, current); + return; + } + +//todo: wtf is RM bit? --m + if (!(ssw & RW) || ssw & RM) + errorcode |= 0x02; + + /* Handle page fault. */ + do_page_fault (&fp->ptregs, addr, errorcode); + + /* Retry the data fault now. */ + return; + } + + /* Now handle the instruction fault. */ + + /* Get the fault address. */ + if (fp->ptregs.format == 0xA) + addr = fp->ptregs.pc + 4; + else + addr = fp->un.fmtb.baddr; + if (ssw & FC) + addr -= 2; + + if (buserr_type & SUN3_BUSERR_INVALID) { + if (!mmu_emu_handle_fault (fp->un.fmtb.daddr, 1, 0)) + do_page_fault (&fp->ptregs, addr, 0); + } else { +#ifdef DEBUG + printk ("protection fault on insn access (segv).\n"); +#endif + force_sig (SIGSEGV, current); + } +} +#else #if defined(CPU_M68020_OR_M68030) static inline void bus_error030 (struct frame *fp) { @@ -507,44 +665,6 @@ static inline void bus_error030 (struct frame *fp) else asm volatile ("ploadr %1,%0@" : /* no outputs */ : "a" (addr), "d" (ssw)); - -#if 0 - /* If this was a data fault due to an invalid page and a - prefetch is pending on the same page, simulate it (but - only if the page is now valid). Otherwise we'll get an - weird insn access. */ - if ((ssw & RB) && (mmusr & MMU_I)) - { - unsigned long iaddr; - - if ((fp->ptregs.format) == 0xB) - iaddr = fp->un.fmtb.baddr; - else - iaddr = fp->ptregs.pc + 4; - if (((addr ^ iaddr) & PAGE_MASK) == 0) - { - /* We only need to check the ATC as the entry has - already been set up above. */ - asm volatile ("ptestr #1,%1@,#0\n\t" - "pmove %/psr,%0@" - : : "a" (&temp), "a" (iaddr)); - mmusr = temp; -#ifdef DEBUG - printk ("prefetch iaddr=%#lx ssw=%#x mmusr=%#x\n", - iaddr, ssw, mmusr); -#endif - if (!(mmusr & MMU_I)) - { - unsigned short insn; - asm volatile ("movesw %1@,%0" - : "=r" (insn) - : "a" (iaddr)); - fp->un.fmtb.isb = insn; - fp->un.fmtb.ssw &= ~RB; - } - } - } -#endif } /* Now handle the instruction fault. */ @@ -598,43 +718,6 @@ static inline void bus_error030 (struct frame *fp) die_if_kernel("Oops",&fp->ptregs,mmusr); force_sig(SIGSEGV, current); return; - } else { -#if 0 /* stale ATC entry?? Ignore it */ - -#ifdef DEBUG - static volatile long tlong; -#endif - - printk ("weird insn access at %#lx from pc %#lx (ssw is %#x)\n", - addr, fp->ptregs.pc, ssw); - asm volatile ("ptestr #1,%1@,#0\n\t" - "pmove %/psr,%0@" - : /* no outputs */ - : "a" (&temp), "a" (addr)); - mmusr = temp; - - printk ("level 0 mmusr is %#x\n", mmusr); -#ifdef DEBUG - if (m68k_cputype & CPU_68030) { - asm volatile ("pmove %/tt0,%0@" - : /* no outputs */ - : "a" (&tlong)); - printk ("tt0 is %#lx, ", tlong); - asm volatile ("pmove %/tt1,%0@" - : /* no outputs */ - : "a" (&tlong)); - printk ("tt1 is %#lx\n", tlong); - } - -#endif - -#if DEBUG - printk("Unknown SIGSEGV - 3\n"); -#endif - die_if_kernel("Oops",&fp->ptregs,mmusr); - force_sig(SIGSEGV, current); - return; -#endif } create_atc_entry: @@ -643,12 +726,13 @@ create_atc_entry: : "a" (addr)); } #endif /* CPU_M68020_OR_M68030 */ +#endif /* !CONFIG_SUN3 */ asmlinkage void buserr_c(struct frame *fp) { /* Only set esp0 if coming from user mode */ if (user_mode(&fp->ptregs)) - current->tss.esp0 = (unsigned long) fp; + current->thread.esp0 = (unsigned long) fp; #if DEBUG printk ("*** Bus Error *** Format is %x\n", fp->ptregs.format); @@ -990,3 +1074,16 @@ asmlinkage void fpsp040_die(void) { do_exit(SIGSEGV); } + +#ifdef CONFIG_M68KFPU_EMU +asmlinkage void fpemu_signal(int signal, int code, void *addr) +{ + siginfo_t info; + + info.si_signo = signal; + info.si_errno = 0; + info.si_code = code; + info.si_addr = addr; + force_sig_info(signal, &info, current); +} +#endif diff --git a/arch/m68k/mac/Makefile b/arch/m68k/mac/Makefile index cfd63295a..10613c0a4 100644 --- a/arch/m68k/mac/Makefile +++ b/arch/m68k/mac/Makefile @@ -7,11 +7,9 @@ # # Note 2! The CFLAGS definitions are now in the main makefile... -EXTRA_CFLAGS := -Wa,-m68020 - O_TARGET := mac.o -O_OBJS := config.o bootparse.o macints.o via6522.o \ - mackeyb.o adb-bus.o macboing.o debug.o OX_OBJS := mac_ksyms.o +O_OBJS := config.o bootparse.o macints.o iop.o via.o oss.o psc.o \ + macboing.o debug.o misc.o include $(TOPDIR)/Rules.make diff --git a/arch/m68k/mac/adb-bus.c b/arch/m68k/mac/adb-bus.c index 5f0f792aa..850e9cb6f 100644 --- a/arch/m68k/mac/adb-bus.c +++ b/arch/m68k/mac/adb-bus.c @@ -13,7 +13,6 @@ * MSch (1/98) Integrated start of IIsi driver by Robert Thompson */ -#include <stdarg.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/miscdevice.h> @@ -2335,8 +2334,8 @@ static int adb_wait_reply(struct adbdev_state *state, struct file *file) int ret = 0; DECLARE_WAITQUEUE(wait,current); + __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&adb_wait, &wait); - current->state = TASK_INTERRUPTIBLE; while (!state->req.got_reply) { if (file->f_flags & O_NONBLOCK) { @@ -2350,7 +2349,7 @@ static int adb_wait_reply(struct adbdev_state *state, struct file *file) schedule(); } - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); remove_wait_queue(&adb_wait, &wait); return ret; @@ -2563,8 +2562,8 @@ static int adb_wait_reply(struct adbdev_state *state, struct file *file) printk("ADB request: wait_reply (blocking ... \n"); #endif + __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&adb_wait, &wait); - current->state = TASK_INTERRUPTIBLE; while (!state->req.got_reply) { if (file->f_flags & O_NONBLOCK) { @@ -2578,7 +2577,7 @@ static int adb_wait_reply(struct adbdev_state *state, struct file *file) schedule(); } - current->state = TASK_RUNNING; + __set_current_state(TASK_RUNNING); remove_wait_queue(&adb_wait, &wait); return ret; diff --git a/arch/m68k/mac/bootparse.c b/arch/m68k/mac/bootparse.c index ae5046c3e..f7600999b 100644 --- a/arch/m68k/mac/bootparse.c +++ b/arch/m68k/mac/bootparse.c @@ -1,6 +1,7 @@ #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> diff --git a/arch/m68k/mac/config.c b/arch/m68k/mac/config.c index 8e902d0bd..b5057f6e8 100644 --- a/arch/m68k/mac/config.c +++ b/arch/m68k/mac/config.c @@ -37,7 +37,20 @@ #include <asm/macints.h> #include <asm/machw.h> -#include "via6522.h" +#include <asm/mac_iop.h> +#include <asm/mac_via.h> +#include <asm/mac_oss.h> +#include <asm/mac_psc.h> + +/* Offset between Unix time (1970-based) and Mac time (1904-based) */ + +#define MAC_TIME_OFFSET 2082844800 + +/* + * hardware reset vector + */ + +static void (*rom_reset)(void); /* Mac bootinfo struct */ @@ -59,30 +72,21 @@ void *mac_env; /* Loaded by the boot asm */ unsigned long mac_orig_videoaddr; /* Mac specific keyboard functions */ -extern int mac_keyb_init(void); -extern int mac_kbdrate(struct kbd_repeat *k); -extern void mac_kbd_leds(unsigned int leds); - -/* Mac specific irq functions */ -extern void mac_init_IRQ (void); -extern void (*mac_handlers[]) (int, void *, struct pt_regs *); -extern int mac_request_irq (unsigned int irq, - void (*handler)(int, void *, struct pt_regs *), - unsigned long flags, const char *devname, - void *dev_id); -extern void mac_free_irq (unsigned int irq, void *dev_id); -extern void mac_enable_irq (unsigned int); -extern void mac_disable_irq (unsigned int); -static void mac_get_model(char *model); -/*static int mac_get_hardware_list(char *buffer);*/ -extern int mac_get_irq_list (char *); +extern int mackbd_init_hw(void); +extern void mackbd_leds(unsigned int leds); /* Mac specific timer functions */ extern unsigned long mac_gettimeoffset (void); static void mac_gettod (int *, int *, int *, int *, int *, int *); static int mac_hwclk (int, struct hwclk_time *); static int mac_set_clock_mmss (unsigned long); +extern void iop_preinit(void); +extern void iop_init(void); +extern void via_init(void); extern void via_init_clock(void (*func)(int, void *, struct pt_regs *)); +extern void via_flush_cache(void); +extern void oss_init(void); +extern void psc_init(void); extern void (*kd_mksound)(unsigned int, unsigned int); extern void mac_mksound(unsigned int, unsigned int); @@ -95,6 +99,18 @@ extern void nubus_sweep_video(void); extern void mac_debug_init(void); extern void mac_debugging_long(int, long); +/* poweroff functions */ +extern void via_poweroff(void); +extern void oss_poweroff(void); +extern void adb_poweroff(void); +extern void adb_hwreset(void); + +/* pram functions */ +extern __u32 via_read_time(void); +extern void via_write_time(__u32); +extern __u32 adb_read_time(void); +extern void adb_write_time(__u32); + #ifdef CONFIG_MAGIC_SYSRQ static char mac_sysrq_xlate[128] = "\000sdfghzxcv\000bqwer" /* 0x00 - 0x0f */ @@ -124,110 +140,201 @@ static void mac_sched_init(void (*vector)(int, void *, struct pt_regs *)) extern int console_loglevel; +/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. + * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 + * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. + * + * [For the Julian calendar (which was used in Russia before 1917, + * Britain & colonies before 1752, anywhere else before 1582, + * and is still in use by some communities) leave out the + * -year/100+year/400 terms, and add 10.] + * + * This algorithm was first published by Gauss (I think). + * + * WARNING: this function will overflow on 2106-02-07 06:28:16 on + * machines were long is 32-bit! (However, as time_t is signed, we + * will already get problems at other places on 2038-01-19 03:14:08) + */ +static unsigned long mktime(unsigned int year, unsigned int mon, + unsigned int day, unsigned int hour, + unsigned int min, unsigned int sec) +{ + if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ + mon += 12; /* Puts Feb last since it has leap day */ + year -= 1; + } + return ((( + (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + + year*365 - 719499 + )*24 + hour /* now have hours */ + )*60 + min /* now have minutes */ + )*60 + sec; /* finally seconds */ +} + /* - * This function translates the boot timeval into a proper date, to initialize - * the system time. + * 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 mac_gettod (int *yearp, int *monp, int *dayp, - int *hourp, int *minp, int *secp) +static void unmktime(unsigned long time, long offset, + int *yearp, int *monp, int *dayp, + int *hourp, int *minp, int *secp) { - unsigned long time; - int leap, oldleap, isleap; - int mon_days[14] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, -1 }; - - time = mac_bi_data.boottime - 60*mac_bi_data.gmtbias; /* seconds */ - - *minp = time / 60; - *secp = time - (*minp * 60); - time = *minp; /* minutes now */ - - *hourp = time / 60; - *minp = time - (*hourp * 60); - time = *hourp; /* hours now */ - - *dayp = time / 24; - *hourp = time - (*dayp * 24); - time = *dayp; /* days now ... */ - - /* for leap day calculation */ - *yearp = (time / 365) + 1970; /* approx. year */ - - /* leap year calculation - there's an easier way, I bet. And it's broken :-( */ - /* calculate leap days up to previous year */ - oldleap = (*yearp-1)/4 - (*yearp-1)/100 + (*yearp-1)/400; - /* calculate leap days incl. this year */ - leap = *yearp/4 - *yearp/100 + *yearp/400; - /* this year a leap year ?? */ - isleap = (leap != oldleap); - - /* adjust days: days, excluding past leap days since epoch */ - time -= oldleap - (1970/4 - 1970/100 + 1970/400); - - /* precise year, and day in year */ - *yearp = (time / 365); /* #years since epoch */ - *dayp = time - (*yearp * 365) + 1; /* #days this year (0: Jan 1) */ - *yearp += 70; /* add epoch :-) */ - time = *dayp; - - if (isleap) /* add leap day ?? */ - mon_days[2] += 1; - - /* count the months */ - for (*monp = 1; time > mon_days[*monp]; (*monp)++) - time -= mon_days[*monp]; + /* 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; +} - *dayp = time; +/* + * Return the boot time for use in initializing the kernel clock. + * + * I'd like to read the hardware clock here but many machines read + * the PRAM through ADB, and interrupts aren't initialized when this + * is called so ADB obviously won't work. + */ - return; +static void mac_gettod(int *yearp, int *monp, int *dayp, + int *hourp, int *minp, int *secp) +{ + /* Yes the GMT bias is backwards. It looks like Penguin is + screwing up the boottime it gives us... This works for me + in Canada/Eastern but it might be wrong everywhere else. */ + unmktime(mac_bi_data.boottime, -mac_bi_data.gmtbias * 60, + yearp, monp, dayp, hourp, minp, secp); + /* For some reason this is off by one */ + *monp = *monp + 1; } /* - * TBI: read and write hwclock + * Read/write the hardware clock. */ -static int mac_hwclk( int op, struct hwclk_time *t ) +static int mac_hwclk(int op, struct hwclk_time *t) { - return 0; + unsigned long now; + + if (!op) { /* read */ + if (macintosh_config->adb_type == MAC_ADB_II) { + now = via_read_time(); + } else if ((macintosh_config->adb_type == MAC_ADB_IISI) || + (macintosh_config->adb_type == MAC_ADB_CUDA)) { + now = adb_read_time(); + } else if (macintosh_config->adb_type == MAC_ADB_IOP) { + now = via_read_time(); + } else { + now = MAC_TIME_OFFSET; + } + + now -= MAC_TIME_OFFSET; + + t->wday = 0; + unmktime(now, 0, + &t->year, &t->mon, &t->day, + &t->hour, &t->min, &t->sec); + } else { /* write */ + now = mktime(t->year + 1900, t->mon + 1, t->day, + t->hour, t->min, t->sec) + MAC_TIME_OFFSET; + + 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_CUDA)) { + adb_write_time(now); + } else if (macintosh_config->adb_type == MAC_ADB_IOP) { + via_write_time(now); + } + } + return 0; } /* - * TBI: set minutes/seconds in hwclock + * Set minutes/seconds in the hardware clock */ static int mac_set_clock_mmss (unsigned long nowtime) { - short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60; + struct hwclk_time now; + + mac_hwclk(0, &now); + now.sec = nowtime % 60; + now.min = (nowtime / 60) % 60; + mac_hwclk(1, &now); - return 0; + return 0; } +#if 0 void mac_waitbut (void) { ; } +#endif extern struct consw fb_con; extern struct fb_info *mac_fb_init(long *); -extern void mac_video_setup(char *, int *); - -void (*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 */ -__initfunc(int mac_parse_bootinfo(const struct bi_record *record)) +int __init mac_parse_bootinfo(const struct bi_record *record) { int unknown = 0; const u_long *data = record->data; @@ -237,7 +344,7 @@ __initfunc(int mac_parse_bootinfo(const struct bi_record *record)) mac_bi_data.id = *data; break; case BI_MAC_VADDR: - mac_bi_data.videoaddr = VIDEOMEMBASE + (*data & ~VIDEOMEMMASK); + mac_bi_data.videoaddr = *data; break; case BI_MAC_VDEPTH: mac_bi_data.videodepth = *data; @@ -249,7 +356,8 @@ __initfunc(int mac_parse_bootinfo(const struct bi_record *record)) mac_bi_data.dimensions = *data; break; case BI_MAC_VLOGICAL: - mac_bi_data.videological = *data; + mac_bi_data.videological = VIDEOMEMBASE + (*data & ~VIDEOMEMMASK); + mac_orig_videoaddr = *data; break; case BI_MAC_SCCBASE: mac_bi_data.sccbase = *data; @@ -266,6 +374,9 @@ __initfunc(int mac_parse_bootinfo(const struct bi_record *record)) case BI_MAC_CPUID: mac_bi_data.cpuid = *data; break; + case BI_MAC_ROMBASE: + mac_bi_data.rombase = *data; + break; default: unknown = 1; } @@ -280,37 +391,29 @@ __initfunc(int mac_parse_bootinfo(const struct bi_record *record)) static void mac_cache_card_flush(int writeback) { - unsigned long flags; - save_flags(flags); + unsigned long cpu_flags; + save_flags(cpu_flags); cli(); - via_write(via2, vBufB, via_read(via2,vBufB)&~VIA2B_vMode32); - via_write(via2, vBufB, via_read(via2,vBufB)|VIA2B_vMode32); - restore_flags(flags); + via_flush_cache(); + restore_flags(cpu_flags); } -__initfunc(void config_mac(void)) +void __init config_mac(void) { if (!MACH_IS_MAC) { printk("ERROR: no Mac, but config_mac() called!! \n"); } - mac_debug_init(); - mach_sched_init = mac_sched_init; - mach_keyb_init = mac_keyb_init; - mach_kbdrate = mac_kbdrate; - mach_kbd_leds = mac_kbd_leds; + mach_keyb_init = mackbd_init_hw; + mach_kbd_leds = mackbd_leds; 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; -#if 1 - mach_default_handler = &mac_handlers; -#endif mach_get_model = mac_get_model; - mach_get_irq_list = mac_get_irq_list; mach_gettimeoffset = mac_gettimeoffset; mach_gettod = mac_gettod; mach_hwclk = mac_hwclk; @@ -319,11 +422,12 @@ __initfunc(void config_mac(void)) mach_mksound = mac_mksound; #endif mach_reset = mac_reset; + mach_halt = mac_poweroff; + mach_power_off = mac_poweroff; conswitchp = &dummy_con; mach_max_dma_address = 0xffffffff; #if 0 mach_debug_init = mac_debug_init; - mach_video_setup = mac_video_setup; #endif kd_mksound = mac_mksound; #ifdef CONFIG_MAGIC_SYSRQ @@ -346,18 +450,15 @@ __initfunc(void config_mac(void)) mac_identify(); mac_report_hardware(); - if( - /* Cache cards */ - macintosh_config->ident == MAC_MODEL_IICI|| - macintosh_config->ident == MAC_MODEL_IISI|| - macintosh_config->ident == MAC_MODEL_IICX|| - /* On board L2 cache */ - macintosh_config->ident == MAC_MODEL_IIFX) - { + /* 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; } - /* goes on forever if timers broken */ #ifdef MAC_DEBUG_SOUND + /* goes on forever if timers broken */ mac_mksound(1000,10); #endif @@ -365,7 +466,9 @@ __initfunc(void config_mac(void)) * Check for machine specific fixups. */ +#ifdef OLD_NUBUS_CODE nubus_sweep_video(); +#endif } @@ -386,6 +489,13 @@ struct mac_model *macintosh_config; static struct mac_model mac_data_table[]= { /* + * The default machine, in case we get an unsupported one + * We'll pretend to be a Macintosh II, that's pretty safe. + */ + + { MAC_MODEL_II, "Unknown", MAC_ADB_II, MAC_VIA_II, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + + /* * Original MacII hardware * */ @@ -403,10 +513,11 @@ static struct mac_model mac_data_table[]= * The IIfx apparently has different ADB hardware, and stuff * so zany nobody knows how to drive it. * Even so, with Marten's help we'll try to deal with it :-) + * CSA: see http://developer.apple.com/technotes/hw/hw_09.html */ { MAC_MODEL_IICI, "IIci", MAC_ADB_II, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_IIFX, "IIfx", MAC_ADB_II, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_IIFX, "IIfx", MAC_ADB_IOP, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_IOP, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_IISI, "IIsi", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_IIVI, "IIvi", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_IIVX, "IIvx", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, @@ -435,70 +546,72 @@ static struct mac_model mac_data_table[]= * confuse us. The 840AV has a SCSI location of its own (same as * the 660AV). */ - + { MAC_MODEL_Q605, "Quadra 605", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_Q610, "Quadra 610", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, { MAC_MODEL_Q630, "Quadra 630", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_QUADRA, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, { MAC_MODEL_Q650, "Quadra 650", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, /* The Q700 does have a NS Sonic */ - { MAC_MODEL_Q700, "Quadra 700", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_QUADRA2, MAC_ETHER_SONIC, MAC_NUBUS}, + { MAC_MODEL_Q700, "Quadra 700", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_QUADRA2, MAC_ETHER_SONIC, MAC_NUBUS}, { MAC_MODEL_Q800, "Quadra 800", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, { MAC_MODEL_Q840, "Quadra 840AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_MACE, MAC_NUBUS}, - /* These might have IOP problems */ - { MAC_MODEL_Q900, "Quadra 900", MAC_ADB_IISI, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_IOP, MAC_ETHER_SONIC, MAC_NUBUS}, - { MAC_MODEL_Q950, "Quadra 950", MAC_ADB_IISI, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_IOP, MAC_ETHER_SONIC, MAC_NUBUS}, + { MAC_MODEL_Q900, "Quadra 900", MAC_ADB_IOP, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_IOP, MAC_ETHER_SONIC, MAC_NUBUS}, + { MAC_MODEL_Q950, "Quadra 950", MAC_ADB_IOP, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_IOP, MAC_ETHER_SONIC, MAC_NUBUS}, /* * Performa - more LC type machines */ - { MAC_MODEL_P460, "Performa 460", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P475, "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P475F, "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P520, "Performa 520", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P550, "Performa 550", MAC_ADB_CUDA, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P575, "Performa 575", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P588, "Performa 588", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_TV, "TV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P600, "Performa 600", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, -#if 0 /* other sources seem to suggest the P630/Q630/LC630 is more like LCIII */ - { MAC_MODEL_P630, "Performa 630", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, -#endif + { MAC_MODEL_P460, "Performa 460", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_P475, "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_P475F, "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_P520, "Performa 520", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + + { MAC_MODEL_P550, "Performa 550", MAC_ADB_CUDA, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_P575, "Performa 575", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + /* These have the comm slot, and therefore the possibility of SONIC ethernet */ + { MAC_MODEL_P588, "Performa 588", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_QUADRA, MAC_SCC_II, MAC_ETHER_SONIC, MAC_NUBUS}, + { MAC_MODEL_TV, "TV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_P600, "Performa 600", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + /* * Centris - just guessing again; maybe like Quadra */ - { MAC_MODEL_C610, "Centris 610", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, - { MAC_MODEL_C650, "Centris 650", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, - { MAC_MODEL_C660, "Centris 660AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + /* The C610 may or may not have SONIC. We probe to make sure */ + { MAC_MODEL_C610, "Centris 610", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, + { MAC_MODEL_C650, "Centris 650", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, + { MAC_MODEL_C660, "Centris 660AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_MACE, MAC_NUBUS}, /* * Power books - seem similar to early Quadras ? (most have 030 though) */ - { MAC_MODEL_PB140, "PowerBook 140", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB145, "PowerBook 145", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB140, "PowerBook 140", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB145, "PowerBook 145", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, /* The PB150 has IDE, and IIci style VIA */ - { MAC_MODEL_PB150, "PowerBook 150", MAC_ADB_PB1, MAC_VIA_IIci, MAC_SCSI_QUADRA, MAC_IDE_PB, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB160, "PowerBook 160", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB165, "PowerBook 165", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB165C, "PowerBook 165c", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB170, "PowerBook 170", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB180, "PowerBook 180", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB180C, "PowerBook 180c", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB190, "PowerBook 190", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_PB, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB520, "PowerBook 520", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB150, "PowerBook 150", MAC_ADB_PB1, MAC_VIA_IIci, MAC_SCSI_NONE, MAC_IDE_PB, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB160, "PowerBook 160", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB165, "PowerBook 165", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB165C, "PowerBook 165c", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB170, "PowerBook 170", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB180, "PowerBook 180", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB180C, "PowerBook 180c", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB190, "PowerBook 190cs", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_PB, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + /* These have onboard SONIC */ + { MAC_MODEL_PB520, "PowerBook 520", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, /* * Power book Duos - similar to Power books, I hope */ - { MAC_MODEL_PB210, "PowerBook Duo 210", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB230, "PowerBook Duo 230", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB250, "PowerBook Duo 250", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB270C, "PowerBook Duo 270c", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB280, "PowerBook Duo 280", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB280C, "PowerBook Duo 280c", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + /* All of these might have onboard SONIC in the Dock but I'm not quite sure */ + { MAC_MODEL_PB210, "PowerBook Duo 210", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB230, "PowerBook Duo 230", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB250, "PowerBook Duo 250", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB270C, "PowerBook Duo 270c", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB280, "PowerBook Duo 280", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB280C, "PowerBook Duo 280c", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, /* * Other stuff ?? @@ -508,7 +621,7 @@ static struct mac_model mac_data_table[]= void mac_identify(void) { - struct mac_model *m=&mac_data_table[0]; + struct mac_model *m; /* Penguin data useful? */ int model = mac_bi_data.id; @@ -519,32 +632,23 @@ void mac_identify(void) printk ("No bootinfo model ID, using cpuid instead (hey, use Penguin!)\n"); } - printk ("Detected Macintosh model: %d \n", model); - - while(m->ident != -1) - { - if(m->ident == model) + macintosh_config = mac_data_table; + for (m = macintosh_config ; m->ident != -1 ; m++) { + if (m->ident == model) { + macintosh_config = m; break; - m++; - } - if(m->ident==-1) - { - printk("\nUnknown macintosh model %d, probably unsupported.\n", - model); - model = MAC_MODEL_Q800; - printk("Defaulting to: Quadra800, model id %d\n", model); - printk("Please report this case to linux-mac68k@wave.lm.com\n"); - m=&mac_data_table[0]; - while(m->ident != -1) - { - if(m->ident == model) - break; - m++; } - if(m->ident==-1) - panic("mac model config data corrupt!\n"); } + /* 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 ("Detected Macintosh model: %d \n", model); + /* * Report booter data: */ @@ -566,12 +670,6 @@ void mac_identify(void) #endif /* - * Save the pointer - */ - - macintosh_config=m; - - /* * TODO: set the various fields in macintosh_config->hw_present here! */ switch (macintosh_config->scsi_type) { @@ -592,7 +690,10 @@ void mac_identify(void) break; } - via_configure_base(); + iop_init(); + via_init(); + oss_init(); + psc_init(); } void mac_report_hardware(void) @@ -604,8 +705,133 @@ static void mac_get_model(char *str) { strcpy(str,"Macintosh "); strcat(str, macintosh_config->name); - if(mach_l2_flush && !(via_read(via2, vBufB)&VIA2B_vCDis)) - strcat(str, "(+L2 cache)"); +} + +/* + * The power switch - yes it's software! + */ + +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_poweroff(); + } else if (macintosh_config->adb_type == MAC_ADB_II) { + via_poweroff(); + } else { + adb_poweroff(); + } +} + +/* + * Not all Macs support software power down; for the rest, just + * try the ROM reset vector ... + */ +void mac_reset(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 (macintosh_config->adb_type == MAC_ADB_II) { + unsigned long cpu_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 ... + */ + printk("System halted.\n"); + while(1); + } else { + save_flags(cpu_flags); + cli(); + + rom_reset(); + + restore_flags(cpu_flags); + } + + /* We never make it this far... it usually panics above. */ + printk ("Restart failed. Please restart manually.\n"); + + /* XXX - delay do we need to spin here ? */ + while(1); /* Just in case .. */ + } else if (macintosh_config->adb_type == MAC_ADB_IISI + || macintosh_config->adb_type == MAC_ADB_CUDA) { + adb_hwreset(); + } 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; + cli(); /* 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 */ + sti(); /* sure, why not */ + printk ("030 Restart failed. Please restart manually.\n"); + while(1); + } else { + /* We never make it here... The above shoule handle all cases. */ + printk ("Restart failed. Please restart manually.\n"); + + /* XXX - delay do we need to spin here ? */ + while(1); /* Just in case .. */ + } } /* diff --git a/arch/m68k/mac/debug.c b/arch/m68k/mac/debug.c index 5aa7ce6cf..98cb2a695 100644 --- a/arch/m68k/mac/debug.c +++ b/arch/m68k/mac/debug.c @@ -243,7 +243,7 @@ void mac_scca_console_write (struct console *co, const char *str, } } -#ifdef CONFIG_SERIAL_CONSOLE +#if defined(CONFIG_SERIAL_CONSOLE) || defined(DEBUG_SERIAL) int mac_sccb_console_wait_key(struct console *co) { int i; @@ -305,7 +305,7 @@ int mac_scca_console_wait_key(struct console *co) } while(0) #ifndef CONFIG_SERIAL_CONSOLE -__initfunc(static void mac_init_scc_port( int cflag, int port )) +static void __init mac_init_scc_port( int cflag, int port ) #else void mac_init_scc_port( int cflag, int port ) #endif @@ -385,7 +385,17 @@ void mac_init_scc_port( int cflag, int port ) } #endif /* DEBUG_SERIAL */ -__initfunc(void mac_debug_init(void)) +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 CONFIG_KGDB /* the m68k_debug_device is used by the GDB stub, do nothing here */ diff --git a/arch/m68k/mac/iop.c b/arch/m68k/mac/iop.c new file mode 100644 index 000000000..ab07e3d48 --- /dev/null +++ b/arch/m68k/mac/iop.c @@ -0,0 +1,725 @@ +/* + * 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 _definately_ 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 e 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 <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 + +/* + * sneaky reuse of the PROC_MAC_VIA inode. It's not needed by via.c + * anymore so we'll use it to debut the IOPs. + */ + +int iop_get_proc_info(char *, char **, off_t, int, int); + +static struct proc_dir_entry proc_mac_iop = { + PROC_MAC_VIA, 7, "mac_iop", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_array_inode_operations, + &iop_get_proc_info +}; + +#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]; + +void 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; + ulong cpu_flags; + + save_flags(cpu_flags); + cli(); + + 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; + restore_flags(cpu_flags); + return &iop_msg_pool[i]; + } + } + + restore_flags(cpu_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 initalize 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_SCC]->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; + } + +#ifdef CONFIG_PROC_FS + proc_register(&proc_root, &proc_mac_iop); +#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) { + 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 + */ + +void 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 + } + +} + +#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; +} + +int iop_get_proc_info(char *buf, char **start, off_t pos, int count, int wr) +{ + 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 index a558d1684..6e37ceb0f 100644 --- a/arch/m68k/mac/mac_ksyms.c +++ b/arch/m68k/mac/mac_ksyms.c @@ -1,7 +1,8 @@ #include <linux/module.h> #include <asm/ptrace.h> #include <asm/traps.h> -/* Hook for mouse driver */ -extern void (*adb_mouse_interrupt_hook) (char *); -EXPORT_SYMBOL(adb_mouse_interrupt_hook); +/* 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 000000000..b3ce30b60 --- /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 index 1731c929c..557bcba27 100644 --- a/arch/m68k/mac/macboing.c +++ b/arch/m68k/mac/macboing.c @@ -91,14 +91,8 @@ static void mac_init_asc( void ) mac_special_bell = mac_quadra_start_bell; mac_asc_samplespersec = 22150; break; - case MAC_MODEL_Q650: - case MAC_MODEL_Q700: - case MAC_MODEL_Q800: - case MAC_MODEL_Q900: - case MAC_MODEL_Q950: - /* - * Currently not implemented! - */ + 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 @@ -126,6 +120,22 @@ static void mac_init_asc( void ) */ 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; } /* @@ -155,6 +165,12 @@ void mac_mksound( unsigned int freq, unsigned int length ) __u32 flags; int i; + if ( mac_special_bell == NULL ) + { + /* Do nothing */ + return; + } + if ( !mac_asc_inited ) mac_init_asc(); diff --git a/arch/m68k/mac/macints.c b/arch/m68k/mac/macints.c index 87e6692d4..0068effa5 100644 --- a/arch/m68k/mac/macints.c +++ b/arch/m68k/mac/macints.c @@ -7,8 +7,8 @@ * interrupts with exception vectors 0x19-0x1f). The following interrupt levels * are used: * 1 - VIA1 - * - slot 0: one second interrupt - * - slot 1: VBlank + * - 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) @@ -16,57 +16,103 @@ * - slot 6: timer 1 * - slot 7: status of IRQ; signals 'any enabled int.' * - * 2 - VIA2, RBV or OSS - * - slot 0: SCSI DRQ - * - slot 1: NUBUS IRQ - * - slot 3: SCSI IRQ + * 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 Macintohes: + * + * 3 - unused (?) + * + * 4 - SCC (slot number determined by reading RR3 on the SSC itself) + * - slot 0: SCC channel A + * - slot 1: 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): * - * 4 - SCC - * - subdivided into Channel B and Channel A interrupts + * 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 * - * 6 - Off switch (??) + * 4 - SCC IOP + * - slot 0: SCC channel A + * - slot 1: SCC channel B * - * 7 - Debug output + * 5 - ISM IOP (ADB?) * - * AV Macs only, handled by PSC: + * 6 - unused * - * 3 - MACE ethernet IRQ (DMA complete on level 4) + * For PSC Macintoshes (660AV, 840AV): * - * 5 - DSP ?? + * 3 - PSC level 3 + * - slot 0: MACE * - * Using the autovector irq numbers for Linux/m68k hardware interrupts without - * the IRQ_MACHSPEC bit set would interfere with the general m68k interrupt - * handling in kernel versions 2.0.x, so the following strategy is used: + * 4 - PSC level 4 + * - slot 1: SCC channel A interrupt + * - slot 2: SCC channel B interrupt + * - slot 3: MACE DMA * - * - mac_init_IRQ installs the low-level entry points for the via1 and via2 - * exception vectors and the corresponding handlers (C functions); these - * entry points just add the machspec bit and call the handlers proper. - * (in principle, the C functions can be installed as the exception vectors - * directly, as they are hardcoded anyway; that's the current method). + * 5 - PSC level 5 * - * - via[12]_irq determine what interrupt sources have triggered the interrupt, - * and call the corresponding device interrupt handlers. - * (currently, via1_irq and via2_irq just call via_irq, passing the via base - * address. RBV interrupts are handled by (you guessed it) rbv_irq). - * Some interrupt functions want to have the interrupt number passed, so - * via_irq and rbv_irq need to generate the 'fake' numbers from scratch. + * 6 - PSC level 6 * - * - for the request/free/enable/disable business, interrupt sources are - * numbered internally (suggestion: keep irq 0-7 unused :-). One bit in the - * irq number specifies the via# to use, i.e. via1 interrupts are 8-16, - * via2 interrupts 17-32, rbv interrupts ... - * The device interrupt table and the irq_enable bitmap is maintained by - * the machspec interrupt code; all device drivers should only use these - * functions ! + * Finally we have good 'ole level 7, the non-maskable interrupt: * - * - For future porting to version 2.1 (and removing of the machspec bit) it - * should be sufficient to use the same numbers (everything > 7 is assumed - * to be machspec, according to Jes!). + * 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. + * + * - 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. + * + * - Drivers should never try to request autovector interrupt numbers. It + * won't work. * * TODO: - * - integrate Nubus interrupts in request/free_irq * - * - + * o Perhaps build some intelligence into mac_SCC_handler(); we could check + * the SCC ourselves and only call the handler for the appopriate channel. */ #include <linux/config.h> @@ -82,124 +128,53 @@ #include <asm/traps.h> #include <asm/machw.h> #include <asm/macintosh.h> -#include "via6522.h" +#include <asm/mac_via.h> +#include <asm/mac_psc.h> #include <asm/macints.h> /* - * Interrupt handler and parameter types - */ -struct irqhandler { - void (*handler)(int, void *, struct pt_regs *); - void *dev_id; -}; - -struct irqparam { - unsigned long flags; - const char *devname; -}; - -struct irqflags { - unsigned int disabled; - unsigned int pending; -}; - -/* - * Array with irq's and their parameter data. - */ -static struct irqhandler via1_handler[8]; -static struct irqhandler via2_handler[8]; -static struct irqhandler rbv_handler[8]; -static struct irqhandler psc3_handler[8]; -static struct irqhandler scc_handler[8]; -static struct irqhandler psc5_handler[8]; -static struct irqhandler psc6_handler[8]; -static struct irqhandler nubus_handler[8]; - -static struct irqhandler *handler_table[8]; - -/* - * This array hold the rest of parameters of int handlers: type - * (slow,fast,prio) and the name of the handler. These values are only - * accessed from C - */ -static struct irqparam via1_param[8]; -static struct irqparam via2_param[8]; -static struct irqparam rbv_param[8]; -static struct irqparam psc3_param[8]; -static struct irqparam scc_param[8]; -static struct irqparam psc5_param[8]; -static struct irqparam psc6_param[8]; -static struct irqparam nubus_param[8]; - -static struct irqparam *param_table[8]; - -/* - * This array holds the 'disabled' and 'pending' software flags maintained - * by mac_{enable,disable}_irq and the generic via_irq function. - */ - -static struct irqflags irq_flags[8]; - -/* - * This array holds the pointers to the various VIA or other interrupt - * controllers, indexed by interrupt level + * VIA/RBV hooks */ -static volatile unsigned char *via_table[8]; +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); /* - * Arrays with irq statistics + * OSS hooks */ -static unsigned long via1_irqs[8]; -static unsigned long via2_irqs[8]; -static unsigned long rbv_irqs[8]; -static unsigned long psc3_irqs[8]; -static unsigned long scc_irqs[8]; -static unsigned long psc5_irqs[8]; -static unsigned long psc6_irqs[8]; -static unsigned long nubus_irqs[8]; -static unsigned long *mac_irqs[8]; +extern int oss_present; -/* - * Some special nutcases ... - */ - -static unsigned long mac_ide_irqs = 0; -static unsigned long nubus_stuck_events = 0; +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); /* - * VIA/RBV/OSS/PSC register base pointers + * PSC hooks */ -volatile unsigned char *via2_regp=(volatile unsigned char *)VIA2_BAS; -volatile unsigned char *rbv_regp=(volatile unsigned char *)VIA2_BAS_IIci; -volatile unsigned char *oss_regp=(volatile unsigned char *)OSS_BAS; -volatile unsigned char *psc_regp=(volatile unsigned char *)PSC_BAS; - -/* - * Flags to control via2 / rbv behaviour - */ - -static int via2_is_rbv = 0; -static int via2_is_oss = 0; -static int rbv_clear = 0; - -/* fake VIA2 to OSS bit mapping */ -static int oss_map[8] = {2, 7, 0, 1, 3, 4, 5}; +extern int psc_present; -void oss_irq(int irq, void *dev_id, struct pt_regs *regs); -static void oss_do_nubus(int irq, void *dev_id, struct pt_regs *regs); - -/* PSC ints */ -void psc_irq(int irq, void *dev_id, struct pt_regs *regs); +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); /* - * PSC hooks + * IOP hooks */ -extern void psc_init(void); +extern void iop_register_interrupts(void); /* * console_loglevel determines NMI handler function @@ -207,656 +182,317 @@ extern void psc_init(void); extern int console_loglevel; -/* - * ADB test hooks - */ -extern int in_keybinit; -void adb_queue_poll(void); - -/* Defined in entry.S; only increments 'num_spurious' */ -asmlinkage void bad_interrupt(void); +extern void mac_bang(int, void *, struct pt_regs *); -void nubus_wtf(int slot, void *via, struct pt_regs *regs); - -void mac_nmi_handler(int irq, void *dev_id, struct pt_regs *regs); -void mac_debug_handler(int irq, void *dev_id, struct pt_regs *regs); - -static void via_do_nubus(int slot, void *via, struct pt_regs *regs); +void mac_nmi_handler(int, void *, struct pt_regs *); +void mac_SCC_handler(int, void *, struct pt_regs *); /* #define DEBUG_MACINTS */ -#define DEBUG_SPURIOUS -#define DEBUG_NUBUS_SPURIOUS -#define DEBUG_NUBUS_INT - -/* #define DEBUG_VIA */ -#define DEBUG_VIA_NUBUS - void mac_init_IRQ(void) { - int i; - #ifdef DEBUG_MACINTS - printk("Mac interrupt stuff initializing ...\n"); + printk("mac_init_IRQ(): Setting things up...\n"); #endif - - via2_regp = (unsigned char *)VIA2_BAS; - rbv_regp = (unsigned char *)VIA2_BAS_IIci; - - /* initialize the hardwired (primary, autovector) IRQs */ - - /* level 1 IRQ: VIA1, always present */ - sys_request_irq(1, via1_irq, IRQ_FLG_LOCK, "via1", via1_irq); - - /* via2 or rbv?? */ - if (macintosh_config->via_type == MAC_VIA_IIci) { - /* - * A word of caution: the definitions here only affect interrupt - * handling, see via6522.c for yet another file to change - * base addresses and RBV flags - */ - - /* yes, this is messy - the IIfx deserves a class of his own */ - if (macintosh_config->ident == MAC_MODEL_IIFX) { - /* no real VIA2, the OSS seems _very_ different */ - via2_is_oss = 1; - /* IIfx has OSS, at a different base address than RBV */ - rbv_regp = (unsigned char *) OSS_BAS; - sys_request_irq(2, oss_irq, IRQ_FLG_LOCK, "oss", oss_irq); - } else { - /* VIA2 is part of the RBV: different base, other offsets */ - via2_is_rbv = 1; - - /* LC III weirdness: IFR seems to behave like VIA2 */ - /* FIXME: maybe also for LC II ?? */ - if (macintosh_config->ident == MAC_MODEL_LCIII) { - rbv_clear = 0x0; - } else { - rbv_clear = 0x80; - } - /* level 2 IRQ: RBV/OSS; we only care about RBV for now */ - sys_request_irq(2, rbv_irq, IRQ_FLG_LOCK, "rbv", rbv_irq); - } - } else - /* level 2 IRQ: VIA2 */ - sys_request_irq(2, via2_irq, IRQ_FLG_LOCK, "via2", via2_irq); - /* - * level 4 IRQ: SCC - use 'master' interrupt routine that calls the - * registered channel-specific interrupts in turn. - * Currently, one interrupt per channel is used, solely - * to pass the correct async_info as parameter! + * Register the handlers for the the master IRQ handlers + * at levels 1-7. Most of the work is done elsewhere. */ - sys_request_irq(4, mac_debug_handler, IRQ_FLG_STD, "INT4", mac_debug_handler); - - /* level 6 */ - sys_request_irq(6, mac_bang, IRQ_FLG_LOCK, "offswitch", mac_bang); - - /* level 7 (or NMI) : debug stuff */ - sys_request_irq(7, mac_nmi_handler, IRQ_FLG_STD, "NMI", mac_nmi_handler); - - /* initialize the handler tables for VIAs */ - for (i = 0; i < 8; i++) { - via1_handler[i].handler = mac_default_handler; - via1_handler[i].dev_id = NULL; - via1_param[i].flags = IRQ_FLG_STD; - via1_param[i].devname = NULL; - - via2_handler[i].handler = mac_default_handler; - via2_handler[i].dev_id = NULL; - via2_param[i].flags = IRQ_FLG_STD; - via2_param[i].devname = NULL; - - rbv_handler[i].handler = mac_default_handler; - rbv_handler[i].dev_id = NULL; - rbv_param[i].flags = IRQ_FLG_STD; - rbv_param[i].devname = NULL; - - scc_handler[i].handler = mac_default_handler; - scc_handler[i].dev_id = NULL; - scc_param[i].flags = IRQ_FLG_STD; - scc_param[i].devname = NULL; - - /* NUBUS interrupts routed through VIA2 slot 2 - special */ - nubus_handler[i].handler = nubus_wtf; - nubus_handler[i].dev_id = NULL; - nubus_param[i].flags = IRQ_FLG_STD; - nubus_param[i].devname = NULL; - - } - - /* initialize the handler tables (level 1 -> via_handler[0] !!!) */ - via_table[0] = via1_regp; - handler_table[0] = &via1_handler[0]; - param_table[0] = &via1_param[0]; - mac_irqs[0] = &via1_irqs[0]; - - if (via2_is_rbv || via2_is_oss) { - via_table[1] = rbv_regp; - handler_table[1] = &rbv_handler[0]; - param_table[1] = &rbv_param[0]; - mac_irqs[1] = &rbv_irqs[0]; + if (oss_present) { + oss_register_interrupts(); } else { - via_table[1] = via2_regp; - handler_table[1] = &via2_handler[0]; - param_table[1] = &via2_param[0]; - mac_irqs[1] = &via2_irqs[0]; - } - via_table[2] = NULL; - via_table[3] = NULL; - - handler_table[2] = &rbv_handler[0]; - handler_table[3] = &scc_handler[0]; - handler_table[4] = NULL; - handler_table[5] = NULL; - handler_table[6] = NULL; - handler_table[7] = &nubus_handler[0]; - - param_table[2] = &rbv_param[0]; - param_table[3] = &scc_param[0]; - param_table[7] = &nubus_param[0]; - - mac_irqs[2] = &rbv_irqs[0]; - mac_irqs[3] = &scc_irqs[0]; - mac_irqs[7] = &nubus_irqs[0]; - - /* - * Nubus Macs: turn off the Nubus dispatch interrupt for now - */ - - mac_turnoff_irq(IRQ_MAC_NUBUS); - - /* - * AV Macs: shutup the PSC ints - */ - if (macintosh_config->ident == MAC_MODEL_C660 - || macintosh_config->ident == MAC_MODEL_Q840) - { - psc_init(); - - handler_table[2] = &psc3_handler[0]; - /* handler_table[3] = &psc4_handler[0]; */ - handler_table[4] = &psc5_handler[0]; - handler_table[5] = &psc6_handler[0]; - - param_table[2] = &psc3_param[0]; - /* param_table[3] = &psc4_param[0]; */ - param_table[4] = &psc5_param[0]; - param_table[5] = &psc6_param[0]; - - mac_irqs[2] = &psc3_irqs[0]; - /* mac_irqs[3] = &psc4_irqs[0]; */ - mac_irqs[4] = &psc5_irqs[0]; - mac_irqs[5] = &psc6_irqs[0]; - - sys_request_irq(3, psc_irq, IRQ_FLG_STD, "PSC3", psc_irq); - sys_request_irq(4, psc_irq, IRQ_FLG_STD, "PSC4", psc_irq); - sys_request_irq(5, psc_irq, IRQ_FLG_STD, "PSC5", psc_irq); - sys_request_irq(6, psc_irq, IRQ_FLG_STD, "PSC6", psc_irq); + via_register_interrupts(); } - + if (psc_present) psc_register_interrupts(); + iop_register_interrupts(); + request_irq(7, mac_nmi_handler, IRQ_FLG_LOCK, "NMI", mac_nmi_handler); #ifdef DEBUG_MACINTS - printk("Mac interrupt init done!\n"); + printk("mac_init_IRQ(): Done!\n"); #endif } /* - * We have no machine specific interrupts on a macintoy - * Yet, we need to register/unregister interrupts ... :-) - * Currently unimplemented: Test for valid irq number, chained irqs, - * Nubus interrupts (use nubus_request_irq!). + * Routines to work with irq_node_t's on linked lists lifted from + * the Amiga code written by Roman Zippel. */ - -int mac_request_irq (unsigned int irq, void (*handler)(int, void *, struct pt_regs *), - unsigned long flags, const char *devname, void *dev_id) -{ - int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); - struct irqhandler *via_handler; - struct irqparam *via_param; - volatile unsigned char *via; - -#ifdef DEBUG_MACINTS - printk ("%s: IRQ %d on VIA%d[%d] requested from %s\n", - __FUNCTION__, irq, srcidx+1, irqidx, devname); -#endif - if (flags < IRQ_TYPE_SLOW || flags > IRQ_TYPE_PRIO) { - printk ("%s: Bad irq type %ld requested from %s\n", - __FUNCTION__, flags, devname); - return -EINVAL; - } - - /* figure out what VIA is handling this irq */ - if (irq < IRQ_IDX(IRQ_VIA1_1) || irq >= IRQ_IDX(IRQ_NUBUS_1)) { - /* non-via irqs unimplemented */ - printk ("%s: Bad irq source %d on VIA %d requested from %s\n", - __FUNCTION__, irq, srcidx, devname); - return -EINVAL; - } - - /* figure out if SCC pseudo-irq (redundant ??) */ - if (irq >= IRQ_IDX(IRQ_SCC) && irq < IRQ_IDX(IRQ_PSC5_0)) { - /* set specific SCC handler */ - scc_handler[irqidx].handler = handler; - scc_handler[irqidx].dev_id = dev_id; - scc_param[irqidx].flags = flags; - scc_param[irqidx].devname = devname; - /* and done! */ - return 0; - } - - /* - * code below: only for VIA irqs currently - * add similar hack for Nubus pseudo-irq here - hide nubus_request_irq - */ - via = (volatile unsigned char *) via_table[srcidx]; - if (!via) - return -EINVAL; - - via_handler = handler_table[srcidx]; - via_param = param_table[srcidx]; - - /* check for conflicts or possible replacement */ - - /* set the handler - no chained irqs yet !! */ - via_handler[irqidx].handler = handler; - via_handler[irqidx].dev_id = dev_id; - via_param[irqidx].flags = flags; - via_param[irqidx].devname = devname; - - /* and turn it on ... careful, that's VIA only ... */ - if (srcidx == SRC_VIA2 && via2_is_rbv) - via_write(via, rIER, via_read(via, rIER)|0x80|(1<<(irqidx))); - else if (srcidx == SRC_VIA2 && via2_is_oss) - via_write(oss_regp, oss_map[irqidx]+8, 2); - else - via_write(via, vIER, via_read(via, vIER)|0x80|(1<<(irqidx))); - - - if (irq == IRQ_IDX(IRQ_MAC_SCSI)) { - /* - * Set vPCR for SCSI interrupts. (what about RBV here?) - * 980429 MS: RBV is ok, OSS seems to be different - */ - if (!via2_is_oss) - if (macintosh_config->scsi_type == MAC_SCSI_OLD) { - /* CB2 (IRQ) indep. interrupt input, positive edge */ - /* CA2 (DRQ) indep. interrupt input, positive edge */ - via_write(via, vPCR, 0x66); - } else { - /* CB2 (IRQ) indep. interrupt input, negative edge */ - /* CA2 (DRQ) indep. interrupt input, negative edge */ - via_write(via, vPCR, 0x22); - } -#if 0 - else - /* CB2 (IRQ) indep. interrupt input, negative edge */ - /* CA2 (DRQ) indep. interrupt input, negative edge */ - via_write(via, vPCR, 0x22); -#endif - } - - return 0; -} - -void mac_free_irq (unsigned int irq, void *dev_id) +static inline void mac_insert_irq(irq_node_t **list, irq_node_t *node) { - unsigned long flags; - int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); - struct irqhandler *via_handler; - struct irqparam *via_param; - volatile unsigned char *via; + unsigned long cpu_flags; + irq_node_t *cur; -#ifdef DEBUG_MACINTS - printk ("%s: IRQ %d on VIA%d[%d] freed\n", - __FUNCTION__, irq, srcidx+1, irqidx); -#endif + if (!node->dev_id) + printk("%s: Warning: dev_id of %s is zero\n", + __FUNCTION__, node->devname); - /* figure out what VIA is handling this irq */ - if (irq < IRQ_IDX(IRQ_VIA1_1) || irq >= IRQ_IDX(IRQ_NUBUS_1)) { - /* non-via irqs unimplemented */ - return; - } - - save_flags(flags); + save_flags(cpu_flags); cli(); - /* figure out if SCC pseudo-irq */ - if (irq >= IRQ_IDX(IRQ_SCC) && irq < IRQ_IDX(IRQ_PSC5_0)) { - /* clear specific SCC handler */ - scc_handler[irqidx].handler = mac_default_handler; - scc_handler[irqidx].dev_id = NULL; - scc_param[irqidx].flags = IRQ_FLG_STD; - scc_param[irqidx].devname = NULL; - /* and done! */ - restore_flags(flags); - return; - } - - via = (volatile unsigned char *) via_table[srcidx]; - via_handler = handler_table[srcidx]; - via_param = param_table[srcidx]; + cur = *list; - if ( !via || (via_handler[irqidx].dev_id != dev_id) ) { - restore_flags(flags); - goto not_found; + 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; + } } - /* clear the handler - no chained irqs yet !! */ - via_handler[irqidx].handler = mac_default_handler; - via_handler[irqidx].dev_id = NULL; - via_param[irqidx].flags = IRQ_FLG_STD; - via_param[irqidx].devname = NULL; + node->next = cur; + *list = node; - /* and turn it off */ - if (srcidx == SRC_VIA2 && via2_is_rbv) - via_write(via, rIER, (via_read(via, rIER)&(1<<irqidx))); - else if (srcidx == SRC_VIA2 && via2_is_oss) - via_write(oss_regp, oss_map[irqidx]+8, 0); - else - via_write(via, vIER, (via_read(via, vIER)&(1<<irqidx))); + restore_flags(cpu_flags); +} - restore_flags(flags); - return; +static inline void mac_delete_irq(irq_node_t **list, void *dev_id) +{ + unsigned long cpu_flags; + irq_node_t *node; -not_found: - printk("%s: tried to remove invalid irq\n", __FUNCTION__); - return; + save_flags(cpu_flags); + cli(); + 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; + restore_flags(cpu_flags); + return; + } + } + restore_flags(cpu_flags); + printk ("%s: tried to remove invalid irq\n", __FUNCTION__); } /* - * {en,dis}able_irq have the usual semantics of temporary blocking the - * interrupt, but not loosing requests that happen between disabling and - * enabling. On Atari, this is done with the MFP mask registers. - * - * On the Mac, this isn't possible: there is no VIA mask register. - * Needs to be implemented in software, setting 'mask' bits in a separate - * struct for each interrupt controller. These mask bits need to be checked - * by the VIA interrupt routine which should ignore requests for masked IRQs - * (after possibly ack'ing them). + * Call all the handlers for a given interrupt. Fast handlers are called + * first followed by slow handlers. * - * On second thought: some of the IRQ sources _can_ be turned off via bits - * in the VIA output registers. Need to check this ... - * - * TBI: According to the VIA docs, clearing a bit in the IER has the effect of - * blocking generation of the interrupt, but the internal interrupt condition - * is preserved. So the IER might be used as mask register here, and turnon_irq - * would need to clear the interrupt bit in the IFR to prevent getting an - * interrupt at all. - * - * Implementation note: the irq no's here are the _machspec_ irqs, hence the - * hack with srcidx to figure out which VIA/RBV handles the interrupt. - * That's fundamentally different when it comes to the interrupt handlers - * proper: these get the interrupt level no. as argument, all information about - * which source triggered the int. is buried in the VIA IFR ... The int. level - * points us to the proper handler, so we could do a sanity check there ... + * This code taken from the original Amiga code written by Roman Zippel. */ -void mac_enable_irq (unsigned int irq) +void mac_do_irq_list(int irq, struct pt_regs *fp) { - int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); + irq_node_t *node, *slow_nodes, **list = NULL; + unsigned long cpu_flags; - irq_flags[srcidx].disabled &= ~(1<<irqidx); - /* - * Call handler here if irq_flags[srcidx].pending & 1<<irqidx ?? - * The structure of via_irq prevents this, sort of: it warns if - * no true events are pending. Maybe that's being changed ... - * Other problem: is it always possible to call an interrupt handler, - * or should that depend on the current interrupt level? - */ -} + kstat.irqs[0][irq]++; -void mac_disable_irq (unsigned int irq) -{ - int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); + if (irq < VIA1_SOURCE_BASE) { + list = &autoirq_list[irq]; + } else if (irq < NUM_MAC_SOURCES) { + list = &userirq_list[irq - VIA1_SOURCE_BASE]; + } + if (!list) return; + +#ifdef DEBUG_SPURIOUS + if (!*list && (console_loglevel > 7)) { + printk("mac_do_irq_list: spurious interrupt %d!\n", irq); + return; + } +#endif - irq_flags[srcidx].disabled |= (1<<irqidx); + /* serve first fast and normal handlers */ + for (node = *list; + node && (!(node->flags & IRQ_FLG_SLOW)); + node = node->next) + node->handler(irq, node->dev_id, fp); + if (!node) return; + save_flags(cpu_flags); + restore_flags((cpu_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); + } } /* - * In opposite to {en,dis}able_irq, requests between turn{off,on}_irq are not - * "stored". This is done with the VIA interrupt enable register on VIAs. + * 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) * - * Note: Using these functions on non-VIA/OSS/PSC ints will panic, or at least - * have undesired side effects. + * These routines are just dispatchers to the VIA/OSS/PSC routines. */ -void mac_turnon_irq( unsigned int irq ) +void mac_enable_irq (unsigned int irq) { - int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); - volatile unsigned char *via; - - via = (volatile unsigned char *) via_table[srcidx]; - if (!via) - return; - - if (srcidx == SRC_VIA2 && via2_is_rbv) /* RBV as VIA2 */ - via_write(via, rIER, via_read(via, rIER)|0x80|(1<<(irqidx))); - else if (srcidx == SRC_VIA2 && via2_is_oss) /* OSS */ - via_write(oss_regp, oss_map[irqidx]+8, 2); - else if (srcidx > SRC_VIA2) /* hope AVs have VIA2 */ - via_write(via, (0x104 + 0x10*srcidx), - via_read(via, (0x104 + 0x10*srcidx))|0x80|(1<<(irqidx))); - else /* VIA1+2 */ - via_write(via, vIER, via_read(via, vIER)|0x80|(1<<(irqidx))); - + switch(IRQ_SRC(irq)) { + 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); + } + break; + } } -void mac_turnoff_irq( unsigned int irq ) +void mac_disable_irq (unsigned int irq) { - int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); - volatile unsigned char *via; - - via = (volatile unsigned char *) via_table[srcidx]; - if (!via) - return; - - if (srcidx == SRC_VIA2 && via2_is_rbv) /* RBV as VIA2 */ - via_write(via, rIER, (via_read(via, rIER)&(1<<irqidx))); - else if (srcidx == SRC_VIA2 && via2_is_oss) /* OSS */ - via_write(oss_regp, oss_map[irqidx]+8, 0); - /* - * VIA2 is fixed. The stuff above VIA2 is for later - * macintoshes only. - */ - else if (srcidx > SRC_VIA2) - via_write(via, (0x104 + 0x10*srcidx), - via_read(via, (0x104 + 0x10*srcidx))|(1<<(irqidx))); - else /* VIA1+2 */ - via_write(via, vIER, (via_read(via, vIER)&(1<<irqidx))); + switch(IRQ_SRC(irq)) { + 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); + } + break; + } } -/* - * These functions currently only handle the software-maintained irq pending - * list for disabled irqs - manipulating the actual irq flags in the via would - * require clearing single bits in the via, such as (probably) - * via_write(via, vIFR, (via_read(via, vIFR)&(1<<irqidx))); - don't know if - * this has side-effects ... - */ - -void mac_clear_pending_irq( unsigned int irq ) +void mac_clear_irq( unsigned int irq ) { - int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); - - irq_flags[srcidx].pending &= ~(1<<irqidx); + 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; + } } -int mac_irq_pending( unsigned int irq ) +int mac_irq_pending( unsigned int irq ) { - int pending = 0; - volatile unsigned char *via; - - int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); - - pending = irq_flags[srcidx].pending & (1<<irqidx); - - via = (volatile unsigned char *) via_table[srcidx]; - if (!via) - return (pending); - - if (srcidx == SRC_VIA2 && via2_is_rbv) - pending |= via_read(via, rIFR)&(1<<irqidx); - else if (srcidx == SRC_VIA2 && via2_is_oss) - pending |= via_read(via, oIFR)&0x03&(1<<oss_map[irqidx]); - else if (srcidx > SRC_VIA2) - pending |= via_read(via, (0x100 + 0x10*srcidx))&(1<<irqidx); - else - pending |= via_read(via, vIFR)&(1<<irqidx); - - return (pending); + 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; } /* - * for /proc/interrupts: log interrupt stats broken down by - * autovector int first, then by actual interrupt source. + * Add an interrupt service routine to an interrupt source. + * Returns 0 on success. + * + * FIXME: You can register interrupts on nonexistant source (ie PSC4 on a + * non-PSC machine). We should return -EINVAL in those cases. */ - -int mac_get_irq_list (char *buf) + +int mac_request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long flags, const char *devname, void *dev_id) { - int i, len = 0; - int srcidx, irqidx; - - for (i = VIA1_SOURCE_BASE; i < NUM_MAC_SOURCES+8; ++i) { - /* XXX fixme: IRQ_SRC_MASK should cover VIA1 - Nubus */ - srcidx = ((i & IRQ_SRC_MASK)>>3) - 1; - irqidx = (i & IRQ_IDX_MASK); - - /* - * Not present: skip - */ - - if (mac_irqs[srcidx] == NULL) - continue; - - /* - * never used by VIAs, unused by others so far, counts - * the magic 'nothing pending' cases ... - */ - if (irqidx == 7 && mac_irqs[srcidx][irqidx]) { - len += sprintf(buf+len, "Level %01d: %10lu (spurious) \n", - srcidx, - mac_irqs[srcidx][irqidx]); - continue; - } + int vec, ret; - /* - * Nothing registered for this IPL: skip - */ - - if (handler_table[srcidx] == NULL) - continue; - - /* - * No handler installed: skip - */ - - if (handler_table[srcidx][irqidx].handler == mac_default_handler || - handler_table[srcidx][irqidx].handler == nubus_wtf) - continue; - - - if (i < VIA2_SOURCE_BASE) - len += sprintf(buf+len, "via1 %01d: %10lu ", - irqidx, - mac_irqs[srcidx][irqidx]); - else if (i < RBV_SOURCE_BASE) - len += sprintf(buf+len, "via2 %01d: %10lu ", - irqidx, - mac_irqs[srcidx][irqidx]); - else if (i < MAC_SCC_SOURCE_BASE) - len += sprintf(buf+len, "rbv %01d: %10lu ", - irqidx, - mac_irqs[srcidx][irqidx]); - else if (i < NUBUS_SOURCE_BASE) - len += sprintf(buf+len, "scc %01d: %10lu ", - irqidx, - mac_irqs[srcidx][irqidx]); - else /* Nubus */ - len += sprintf(buf+len, "nubus %01d: %10lu ", - irqidx, - mac_irqs[srcidx][irqidx]); - - len += sprintf(buf+len, "%s\n", - param_table[srcidx][irqidx].devname); +#ifdef DEBUG_MACINTS + printk ("%s: irq %d requested for %s\n", __FUNCTION__, irq, devname); +#endif + if (irq < VIA1_SOURCE_BASE) { + vec = VEC_SPUR + irq; + } else if (irq < NUM_MAC_SOURCES) { + vec = VEC_USER + irq - VIA1_SOURCE_BASE; + } else { + printk ("%s: unknown irq %d requested by %s\n", + __FUNCTION__, irq, devname); + return -EAGAIN; } - if (num_spurious) - len += sprintf(buf+len, "spurio.: %10u\n", num_spurious); - - /* - * XXX Fixme: Nubus sources are never logged above ... - */ - - len += sprintf(buf+len, "Nubus interrupts:\n"); - - for (i = 0; i < 7; i++) { - if (nubus_handler[i].handler == nubus_wtf) - continue; - len += sprintf(buf+len, "nubus %01X: %10lu ", - i+9, - nubus_irqs[i]); - len += sprintf(buf+len, "%s\n", - nubus_param[i].devname); + ret = sys_request_listirq(vec, handler, flags, devname, dev_id); + if (!ret) { + vectors[vec] = autoirq_listhandler; + mac_enable_irq(irq); } - len += sprintf(buf+len, "nubus spurious ints: %10lu\n", - nubus_irqs[7]); - len += sprintf(buf+len, "nubus stuck events : %10lu\n", - nubus_stuck_events); -#ifdef CONFIG_BLK_DEV_IDE - len += sprintf(buf+len, "nubus/IDE interrupt: %10lu\n", - mac_ide_irqs); -#endif - return len; + return ret; } + +/* + * Removes an interrupt service routine from an interrupt source. + */ -void via_scsi_clear(void) +void mac_free_irq(unsigned int irq, void *dev_id) { - volatile unsigned char deep_magic; - if (via2_is_rbv) { - via_write(rbv_regp, rIFR, (1<<3)|(1<<0)|0x80); - deep_magic = via_read(rbv_regp, rBufB); - } else if (via2_is_oss) { - /* nothing */ - /* via_write(oss_regp, 9, 0) */; - } else - deep_magic = via_read(via2_regp, vBufB); - mac_enable_irq( IRQ_IDX(IRQ_MAC_SCSI) ); -} - + irq_node_t **list = NULL; + int vec = 0; -void mac_default_handler(int irq, void *dev_id, struct pt_regs *regs) -{ -#ifdef DEBUG_SPURIOUS - if (console_loglevel > 6) - printk("Unexpected IRQ %d on device %p\n", irq, dev_id); +#ifdef DEBUG_MACINTS + printk ("%s: irq %d freed by %p\n", __FUNCTION__, irq, dev_id); #endif -} -static int num_debug[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + if (irq < VIA1_SOURCE_BASE) { + vec = VEC_SPUR + irq; + list = &autoirq_list[irq]; + } else if (irq < NUM_MAC_SOURCES) { + vec = VEC_USER + irq - VIA1_SOURCE_BASE; + list = &userirq_list[irq - VIA1_SOURCE_BASE]; + } + if (!list) return; -void 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]++; + sys_free_irq(vec, dev_id); + + /* If the list for this interrupt is */ + /* empty then disable the source. */ + + if (!*list) { + mac_disable_irq(irq); + vectors[vec] = bad_interrupt; } } -void scsi_mac_debug(void); -void scsi_mac_polled(void); - static int in_nmi = 0; static volatile int nmi_hold = 0; @@ -869,10 +505,6 @@ void mac_nmi_handler(int irq, void *dev_id, struct pt_regs *fp) */ in_nmi++; -#if 0 - scsi_mac_debug(); - printk("PC: %08lx\nSR: %04x SP: %p\n", fp->pc, fp->sr, fp); -#endif for (i=0; i<100; i++) udelay(1000); @@ -889,10 +521,6 @@ void mac_nmi_handler(int irq, void *dev_id, struct pt_regs *fp) while (nmi_hold == 1) udelay(1000); -#if 0 - scsi_mac_polled(); -#endif - if ( console_loglevel >= 8 ) { #if 0 show_state(); @@ -916,799 +544,12 @@ void mac_nmi_handler(int irq, void *dev_id, struct pt_regs *fp) } /* - * Unexpected via interrupt - */ - -void via_wtf(int slot, void *via, struct pt_regs *regs) -{ -#ifdef DEBUG_SPURIOUS - if (console_loglevel > 6) - printk("Unexpected nubus event %d on via %p\n",slot,via); -#endif -} - -/* - * The generic VIA interrupt routines (shamelessly stolen from Alan Cox's - * via6522.c :-), disable/pending masks added. - * The int *viaidx etc. is just to keep the prototype happy ... - */ - -static void via_irq(unsigned char *via, int *viaidx, struct pt_regs *regs) -{ - unsigned char events=(via_read(via, vIFR)&via_read(via,vIER))&0x7F; - int i; - int ct = 0; - struct irqhandler *via_handler = handler_table[*viaidx]; - struct irqparam *via_param = param_table[*viaidx]; - unsigned long *via_irqs = mac_irqs[*viaidx]; - - /* to be changed, possibly: for each non'masked', enabled IRQ, read - * flag bit, ack and call handler ... - * Currently: all pending irqs ack'ed en bloc. - * If ack for masked IRQ required: keep 'pending' info separate. - */ - - /* shouldn't we disable interrupts here ?? */ - - - /* - * Shouldnt happen - */ - - if(events==0) - { -#ifdef DEBUG_VIA - /* should go away; mostly missing timer ticks and ADB events */ - printk("via%d_irq: nothing pending, flags %x mask %x!\n", - *viaidx + 1, via_read(via, vIFR), via_read(via,vIER)); -#endif - via_irqs[7]++; - return; - } - -#ifdef DEBUG_VIA - /* - * limited verbosity for VIA interrupts - */ -#if 0 - if ( (*viaidx == 0 && events != 1<<6) /* timer int */ - || (*viaidx == 1 && events != 1<<3) ) /* SCSI IRQ */ -#else - if ( *viaidx == 0 && (events & 1<<2) ) -#endif - printk("via_irq: irq %d events %x !\n", (*viaidx)+1, events); -#endif - - do { - /* - * Clear the pending flag - */ - - via_write(via, vIFR, events); - - /* - * Now see what bits are raised - */ - - for(i=0;i<7;i++) - { - /* determine machspec. irq no. */ - int irq = ((*viaidx)+1)* 8 + i; - /* call corresponding handlers */ - if (events&(1<<i)) { - if (irq_flags[*viaidx].disabled & (1<<i)) { - if (!irq_flags[*viaidx].pending&(1<<i)) - via_irqs[i]++; - /* irq disabled -> mark pending */ - irq_flags[*viaidx].pending |= (1<<i); - } else { - via_irqs[i]++; - /* irq enabled -> call handler */ - (via_handler[i].handler)(irq, via, regs); - } - } - /* and call handlers for pending irqs - first ?? */ - if ( (irq_flags[*viaidx].pending & (1<<i)) - && !(irq_flags[*viaidx].disabled & (1<<i)) ) { - /* call handler for re-enabled irq */ - (via_handler[i].handler)(irq, via, regs); - /* and clear pending flag :-) */ - irq_flags[*viaidx].pending &= ~(1<<i); - } - } - - /* - * And done ... check for more punishment! - */ - - events=(via_read(via, vIFR)&via_read(via,vIER))&0x7F; - ct++; - if(events && ct>8) - { -#ifdef DEBUG_VIA - printk("via%d: stuck events %x\n", (*viaidx)+1, events); -#endif - break; - } - } - while(events); -#if 0 - scsi_mac_polled(); -#endif -} - -/* - * Caution: the following stuff is called from process_int as _autovector_ - * system interrupts. So irq is always in the range 0-7 :-( and the selection - * of the appropriate VIA is up to the irq handler here based on the autovec - * irq number. There's no information whatsoever about which source on the VIA - * triggered the int - and that's what the machspec irq no's are about. - * Broken design :-(((( - */ - -/* - * System interrupts - */ - -void via1_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - int srcidx = IRQ_IDX(irq) - 1; - via_irq((unsigned char *)via1_regp, &srcidx, regs); -} - - -/* - * Nubus / SCSI interrupts, VIA style (could be wrapped into via1_irq or - * via_irq directly by selecting the regp based on the irq!) - */ - -void via2_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - int srcidx = IRQ_IDX(irq) - 1; - via_irq((unsigned char *)via2_regp, &srcidx, regs); -} - -/* - * Nubus / SCSI interrupts; RBV style - * The RBV is different. RBV appears to stand for randomly broken - * VIA (or even real broken VIA). - */ - -void rbv_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - int srcidx = IRQ_IDX(irq) - 1; /* MUST be 1 !! */ - volatile unsigned char *via = rbv_regp; - unsigned char events=(via_read(via, rIFR)&via_read(via,rIER))&0x7F; - int i; - int ct = 0; - struct irqhandler *via_handler = handler_table[srcidx]; - struct irqparam *via_param = param_table[srcidx]; - - /* shouldn't we disable interrupts here ?? */ - - - /* - * Shouldnt happen - */ - - if(events==0) - { -#ifdef DEBUG_VIA - printk("rbv_irq: nothing pending, flags %x mask %x!\n", - via_read(via, rIFR), via_read(via,rIER)); -#endif - rbv_irqs[7]++; - return; - } - -#ifdef DEBUG_VIA - /* - * limited verbosity for RBV interrupts (add more if needed) - */ - if ( srcidx == 1 && events != 1<<3 ) /* SCSI IRQ */ - printk("rbv_irq: irq %d (%d) events %x !\n", irq, srcidx+1, events); -#endif - - /* to be changed, possibly: for each non'masked', enabled IRQ, read - * flag bit, ack and call handler ... - * Currently: all pending irqs ack'ed en bloc. - * If ack for masked IRQ required: keep 'pending' info separate. - */ - - do { - /* - * Clear the pending flag - */ - - via_write(via, rIFR, events | rbv_clear); - - /* - * Now see what bits are raised - */ - - for(i=0;i<7;i++) - { - /* determine machspec. irq no. */ - int irq = (srcidx+1)* 8 + i; - /* call corresponding handlers */ - if (events&(1<<i)) { - if (irq_flags[srcidx].disabled & (1<<i)) { - if (!irq_flags[srcidx].pending&(1<<i)) - rbv_irqs[i]++; - /* irq disabled -> mark pending */ - irq_flags[srcidx].pending |= (1<<i); - } else { - rbv_irqs[i]++; - /* irq enabled -> call handler */ - (via_handler[i].handler)(irq, via, regs); - } - } - /* and call handlers for pending irqs - first ?? */ - if ( (irq_flags[srcidx].pending & (1<<i)) - && !(irq_flags[srcidx].disabled & (1<<i)) ) { - /* call handler for re-enabled irq */ - (via_handler[i].handler)(irq, via, regs); - /* and clear pending flag :-) */ - irq_flags[srcidx].pending &= ~(1<<i); - } - } - - /* - * And done ... check for more punishment! - */ - - events=(via_read(via, rIFR)&via_read(via,rIER))&0x7F; - ct++; - if(events && ct>8) - { - printk("rbv: stuck events %x\n",events); - for(i=0;i<7;i++) - { - if(events&(1<<i)) - { - printk("rbv - bashing source %d\n", - i); - via_write(via, rIER, 1<<i); - via_write(via, rIFR, (1<<i) | rbv_clear); - } - } - break; - } - } - while(events); -#if 0 - scsi_mac_polled(); -#endif -} - -/* - * Nubus / SCSI interrupts; OSS style - * The OSS is even more different than the RBV. OSS appears to stand for - * Obscenely Screwed Silicon ... - * - * Latest NetBSD sources suggest the OSS should behave like a RBV, but - * that's probably true for the 0x203 offset (Nubus/ADB-SWIM IOP) at best - */ - -void oss_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - int srcidx = IRQ_IDX(irq) - 1; /* MUST be 1 !! */ - volatile unsigned char *via = oss_regp; - unsigned char events=(via_read(via, oIFR))&0x03; - unsigned char nub_ev=(via_read(via, nIFR))&0x4F; - unsigned char adb_ev; - int i; - int ct = 0; - struct irqhandler *via_handler = handler_table[srcidx]; - struct irqparam *via_param = param_table[srcidx]; - - /* shouldn't we disable interrupts here ?? */ - - adb_ev = nub_ev & 0x40; - nub_ev &= 0x3F; - - /* - * Shouldnt happen - */ - - if (events==0 && adb_ev==0 && nub_ev==0) - { - printk("oss_irq: nothing pending, flags %x %x!\n", - via_read(via, oIFR), via_read(via, nIFR)); - rbv_irqs[7]++; - return; - } - -#ifdef DEBUG_VIA - /* - * limited verbosity for RBV interrupts (add more if needed) - */ - if ( events != 1<<3 ) /* SCSI IRQ */ - printk("oss_irq: irq %d srcidx+1 %d events %x %x %x !\n", irq, srcidx+1, - events, adb_ev, nub_ev); -#endif - - /* - * OSS priorities: call ADB handler first if registered, other events, - * then Nubus - * ADB: yet to be implemented! - */ - - /* - * ADB: try to shutup the IOP - */ - if (adb_ev) { - printk("Hands off ! Don't press this button ever again !!!\n"); - via_write(via, 6, 0); - } - - do { - /* - * Clear the pending flags - * How exactly is that supposed to work ?? - */ - - /* - * Now see what bits are raised - */ - - for(i=0;i<7;i++) - { - /* HACK HACK: map to bit number in OSS register */ - int irqidx = oss_map[i]; - /* determine machspec. irq no. */ - int irq = (srcidx+1)* 8 + i; - /* call corresponding handlers */ - if ( (events&(1<<irqidx)) && /* bit set*/ - (via_read(via, irqidx+8)&0x7) ) { /* irq enabled */ - if (irq_flags[srcidx].disabled & (1<<i)) { - if (!irq_flags[srcidx].pending&(1<<i)) - rbv_irqs[i]++; - /* irq disabled -> mark pending */ - irq_flags[srcidx].pending |= (1<<i); - } else { - rbv_irqs[i]++; - /* irq enabled -> call handler */ - (via_handler[i].handler)(irq, via, regs); - } - } - /* and call handlers for pending irqs - first ?? */ - if ( (irq_flags[srcidx].pending & (1<<i)) - && !(irq_flags[srcidx].disabled & (1<<i)) ) { - /* call handler for re-enabled irq */ - (via_handler[i].handler)(irq, via, regs); - /* and clear pending flag :-) */ - irq_flags[srcidx].pending &= ~(1<<i); - } - } - - /* - * And done ... check for more punishment! - */ - - events=(via_read(via, oIFR)/*&via_read(via,rIER)*/)&0x03; - ct++; - if(events && ct>8) - { - printk("oss: stuck events %x\n",events); - for(i=0;i<7;i++) - { - if(events&(1<<i)) - { - printk("oss - bashing source %d\n", - i); - /* that should disable it */ - via_write(via, 8+i, 0); - } - } - break; - } - } - while(events); -#if 0 - scsi_mac_polled(); -#endif - - if (nub_ev) - oss_do_nubus(irq, via, regs); - -} - -/* - * Unexpected slot interrupt - */ - -void nubus_wtf(int slot, void *via, struct pt_regs *regs) -{ -#ifdef DEBUG_NUBUS_SPURIOUS - if (console_loglevel > 6) - printk("Unexpected interrupt on nubus slot %d\n",slot); -#endif -} - -/* * SCC master interrupt handler; sole purpose: pass the registered * async struct to the SCC handler proper. */ void mac_SCC_handler(int irq, void *dev_id, struct pt_regs *regs) { - int i; - /* 1+2: compatibility with PSC ! */ - for (i = 1; i < 3; i++) /* currently only these two used */ - if (scc_handler[i].handler != mac_default_handler) { - (scc_handler[i].handler)(i, scc_handler[i].dev_id, regs); - scc_irqs[i]++; - } -} - -/* - * PSC interrupt handler - */ - -void psc_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - int srcidx = IRQ_IDX(irq) - 1; - volatile unsigned char *via = psc_regp; - unsigned int pIFR = 0x100 + 0x10*srcidx; - unsigned int pIER = 0x104 + 0x10*srcidx; - unsigned char events=(via_read(via, pIFR)&via_read(via,pIER))&0xF; - int i; - int ct = 0; - struct irqhandler *via_handler = handler_table[srcidx]; - struct irqparam *via_param = param_table[srcidx]; - - /* shouldn't we disable interrupts here ?? */ - - - /* - * Shouldnt happen - */ - - if(events==0) - { -#ifdef DEBUG_VIA - printk("psc_irq: nothing pending, flags %x mask %x!\n", - via_read(via, pIFR), via_read(via,pIER)); -#endif - mac_irqs[srcidx][7]++; - return; - } - -#ifdef DEBUG_VIA - /* - * limited verbosity for PSC interrupts (add more if needed) - */ - if ( srcidx == 1 && events != 1<<3 && events != 1<<1 ) /* SCSI IRQ */ - printk("psc_irq: irq %d srcidx+1 %d events %x !\n", irq, srcidx+1, events); -#endif - - /* to be changed, possibly: for each non'masked', enabled IRQ, read - * flag bit, ack and call handler ... - * Currently: all pending irqs ack'ed en bloc. - * If ack for masked IRQ required: keep 'pending' info separate. - */ - - do { - /* - * Clear the pending flag - */ - - /* via_write(via, pIFR, events); */ - - /* - * Now see what bits are raised - */ - - for(i=0;i<7;i++) - { - /* determine machspec. irq no. */ - int irq = (srcidx+1)* 8 + i; - /* call corresponding handlers */ - if (events&(1<<i)) { - if (irq_flags[srcidx].disabled & (1<<i)) { - if (!irq_flags[srcidx].pending&(1<<i)) - mac_irqs[srcidx][i]++; - /* irq disabled -> mark pending */ - irq_flags[srcidx].pending |= (1<<i); - } else { - mac_irqs[srcidx][i]++; - /* irq enabled -> call handler */ - (via_handler[i].handler)(irq, via, regs); - } - } - /* and call handlers for pending irqs - first ?? */ - if ( (irq_flags[srcidx].pending & (1<<i)) - && !(irq_flags[srcidx].disabled & (1<<i)) ) { - /* call handler for re-enabled irq */ - (via_handler[i].handler)(irq, via, regs); - /* and clear pending flag :-) */ - irq_flags[srcidx].pending &= ~(1<<i); - } - } - - /* - * And done ... check for more punishment! - */ - - events=(via_read(via,pIFR)&via_read(via,pIER))&0x7F; - ct++; - if(events && ct>8) - { - printk("psc: stuck events %x\n",events); - for(i=0;i<7;i++) - { - if(events&(1<<i)) - { - printk("psc - bashing source %d\n", - i); - via_write(via, pIER, 1<<i); - /* via_write(via, pIFR, (1<<i)); */ - } - } - break; - } - } - while(events); -} - - -/* - * Nubus handling - * Caution: slot numbers are currently 'hardcoded' to the range 9-15! - * In general, the same request_irq() functions as above can be used if - * the interrupt numbers specifed in macints.h are used. - */ - -static int nubus_active=0; - -int nubus_request_irq(int slot, void *dev_id, void (*handler)(int,void *,struct pt_regs *)) -{ - slot-=9; -/* printk("Nubus request irq for slot %d\n",slot);*/ - if(nubus_handler[slot].handler!=nubus_wtf) - return -EBUSY; - nubus_handler[slot].handler=handler; - nubus_handler[slot].dev_id =dev_id; - nubus_param[slot].flags = IRQ_FLG_LOCK; - nubus_param[slot].devname = "nubus slot"; - - /* - * if no nubus int. was active previously: register the main nubus irq - * handler now! - */ - - if (!nubus_active && !via2_is_oss) { - request_irq(IRQ_MAC_NUBUS, via_do_nubus, IRQ_FLG_LOCK, - "nubus dispatch", via_do_nubus); - mac_turnon_irq(IRQ_MAC_NUBUS); - } - - nubus_active|=1<<slot; -/* printk("program slot %d\n",slot);*/ -/* printk("via2=%p\n",via2);*/ -#if 0 - via_write(via2, vDirA, - via_read(via2, vDirA)|(1<<slot)); - via_write(via2, vBufA, 0); -#endif - if (via2_is_oss) - via_write(oss_regp, slot, 2); - else if (!via2_is_rbv) { - /* Make sure the bit is an input */ - via_write(via2_regp, vDirA, - via_read(via2_regp, vDirA)&~(1<<slot)); - } -/* printk("nubus irq on\n");*/ - return 0; -} - -int nubus_free_irq(int slot) -{ - slot-=9; - nubus_active&=~(1<<slot); - nubus_handler[slot].handler=nubus_wtf; - nubus_handler[slot].dev_id = NULL; - nubus_param[slot].flags = IRQ_FLG_STD; - nubus_param[slot].devname = NULL; - - if (via2_is_rbv) - via_write(rbv_regp, rBufA, 1<<slot); - else if (via2_is_oss) - via_write(oss_regp, slot, 0); - else { - via_write(via2_regp, vDirA, - via_read(via2_regp, vDirA)|(1<<slot)); - via_write(via2_regp, vBufA, 1<<slot); - via_write(via2_regp, vDirA, - via_read(via2_regp, vDirA)&~(1<<slot)); - } - return 0; -} - -#ifdef CONFIG_BLK_DEV_MAC_IDE -/* - * IDE interrupt hook - */ -extern void (*mac_ide_intr_hook)(int, void *, struct pt_regs *); -extern int (*mac_ide_irq_p_hook)(void); -#endif - -/* - * Nubus dispatch handler - VIA/RBV style - */ -static void via_do_nubus(int slot, void *via, struct pt_regs *regs) -{ - unsigned char map, allints; - int i; - int ct=0; - int ide_pending = 0; - - /* lock the nubus interrupt */ - /* That's just 'clear Nubus IRQ bit in VIA2' BTW. Pretty obsolete ? */ - if (via2_is_rbv) - via_write(rbv_regp, rIFR, 0x82); - else - via_write(via2_regp, vIFR, 0x82); - -#ifdef CONFIG_BLK_DEV_MAC_IDE - /* IDE hack */ - if (mac_ide_intr_hook) { - /* 'slot' is lacking the machspec bit in 2.0 */ - /* need to pass proper dev_id = hwgroup here */ - mac_ide_intr_hook(IRQ_MAC_NUBUS, via, regs); - mac_ide_irqs++; - } -#endif - - while(1) - { - if (via2_is_rbv) - allints = ~via_read(rbv_regp, rBufA); - else - allints = ~via_read(via2_regp, vBufA); - -#ifdef CONFIG_BLK_DEV_MAC_IDE - if (mac_ide_irq_p_hook) - ide_pending = mac_ide_irq_p_hook(); -#endif - - if ( (map = (allints&nubus_active)) == 0 -#ifdef CONFIG_BLK_DEV_MAC_IDE - && !ide_pending -#endif - ) - { - if (ct == 0) { -#ifdef DEBUG_VIA_NUBUS - if (console_loglevel > 5) - printk("nubus_irq: nothing pending, map %x mask %x active %x\n", - allints, nubus_active, map); -#endif - nubus_irqs[7]++; - } - /* clear it */ - if (allints) - if (via2_is_rbv) - via_write(rbv_regp, rIFR, 0x02); - else - via_write(via2_regp, vIFR, 0x02); - break; - } - -#ifdef DEBUG_VIA_NUBUS - if (console_loglevel > 6) - printk("nubus_irq: map %x mask %x active %x\n", - allints, nubus_active, map); -#endif - -#ifdef CONFIG_BLK_DEV_MAC_IDE - if (mac_ide_intr_hook && ide_pending) { - mac_ide_intr_hook(IRQ_MAC_NUBUS, via, regs); - mac_ide_irqs++; - } -#endif - - if(ct++>2) - { - if (console_loglevel > 5) - printk("nubus stuck events - %x/%x/%x ide %x\n", - allints, nubus_active, map, ide_pending); - nubus_stuck_events++; - - return; - } - - for(i=0;i<7;i++) - { - if(map&(1<<i)) - { - nubus_irqs[i]++; - (nubus_handler[i].handler)(i+9, nubus_handler[i].dev_id, regs); - } - } - /* clear it */ - if (via2_is_rbv) - via_write(rbv_regp, rIFR, 0x02); - else - via_write(via2_regp, vIFR, 0x02); - - } - - /* And done */ -} - -/* - * Nubus dispatch handler - OSS style - */ -static void oss_do_nubus(int slot, void *via, struct pt_regs *regs) -{ - unsigned char map; - int i; - int ct=0; - -/* printk("nubus interrupt\n");*/ - -#if 0 - /* lock the nubus interrupt */ - if (via2_is_rbv) - via_write(rbv_regp, rIFR, 0x82); - else - via_write(via2_regp, vIFR, 0x82); -#endif - - /* IDE hack for Quadra: uses Nubus interrupt without any slot bit set */ -#ifdef CONFIG_BLK_DEV_MAC_IDE - if (mac_ide_intr_hook) - mac_ide_intr_hook(IRQ_MAC_NUBUS, via, regs); -#endif - - while(1) - { - /* pending events */ - map=(via_read(via, nIFR))&0x3F; - -#ifdef DEBUG_VIA_NUBUS - printk("nubus_irq: map %x mask %x\n", map, nubus_active); -#endif - if( (map = (map&nubus_active)) ==0 ) { - if (ct == 0) { -#ifdef CONFIG_BLK_DEV_MAC_IDE - if (!mac_ide_intr_hook) - printk("nubus_irq: nothing pending, map %x mask %x\n", - map, nubus_active); -#endif - nubus_irqs[7]++; - } - break; - } - - if(ct++>2) - { -#if 0 - printk("nubus stuck events - %d/%d\n", map, nubus_active); -#endif - return; - } - - for(i=0;i<7;i++) - { - if(map&(1<<i)) - { - nubus_irqs[i]++; - /* call handler */ - (nubus_handler[i].handler)((i+9), nubus_handler[i].dev_id, regs); - /* clear interrupt ?? */ -#if 0 - via_write(oss_regp, i, 0); -#endif - } - } - /* clear it */ -#if 0 - via_write(oss_regp, nIFR, map); -#endif - } - - /* And done */ + mac_do_irq_list(IRQ_SCCA, regs); + mac_do_irq_list(IRQ_SCCB, regs); } diff --git a/arch/m68k/mac/mackeyb.c b/arch/m68k/mac/mackeyb.c index a128a6314..0d70357dc 100644 --- a/arch/m68k/mac/mackeyb.c +++ b/arch/m68k/mac/mackeyb.c @@ -613,7 +613,7 @@ int mac_kbdrate(struct kbd_repeat *k) return 0; } -__initfunc(int mac_keyb_init(void)) +int __init mac_keyb_init(void) { static struct adb_request autopoll_req, confcod_req, mouse_req, readkey_req; volatile int ct; diff --git a/arch/m68k/mac/oss.c b/arch/m68k/mac/oss.c new file mode 100644 index 000000000..442d72802 --- /dev/null +++ b/arch/m68k/mac/oss.c @@ -0,0 +1,314 @@ +/* + * 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; + +void oss_irq(int, void *, struct pt_regs *); +void oss_nubus_irq(int, void *, struct pt_regs *); + +extern void via1_irq(int, void *, struct pt_regs *); +extern void mac_SCC_handler(int, void *, struct pt_regs *); +extern int console_loglevel; + +/* + * 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) +{ + request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK, + "OSS SCSI Dispatch", (void *) oss); + request_irq(OSS_IRQLEV_IOPSCC, mac_SCC_handler, IRQ_FLG_LOCK, + "SCC Dispatch", mac_SCC_handler); + request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK, + "Nubus Dispatch", (void *) oss); + request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK, + "OSS Sound Dispatch", (void *) oss); + request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK, + "VIA1 Dispatch", (void *) via1); +} + +/* + * Initialize OSS for Nubus access + */ + +void __init oss_nubus_init(void) +{ +} + +/* + * Turn off the power via the ROM control register + * + * FIXME: not sure how this is supposed to work exactly... + */ + +void oss_poweroff(void) +{ + oss->rom_ctrl = OSS_POWEROFF; + + /* We should never make it this far... */ + + printk ("It is now safe to switch off your machine.\n"); + while(1); +} + +/* + * Handle miscellaneous OSS interrupts. Right now that's just sound + * and SCSI; everything else is routed to its own autovector IRQ. + */ + +void 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; + +#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? */ + } +} + +/* + * Nubus IRQ handler, OSS style + * + * Unlike the VIA/RBV this is on its own autovector interupt level. + */ + +void 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; + +#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; + } + } +} + +/* + * 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 000000000..122659555 --- /dev/null +++ b/arch/m68k/mac/psc.c @@ -0,0 +1,203 @@ +/* + * 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/config.h> +#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; + +void psc_irq(int, void *, struct pt_regs *); + +extern int console_loglevel; + +/* + * 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) +{ + request_irq(3, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch", + (void *) 0x30); + request_irq(4, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch", + (void *) 0x40); + request_irq(5, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch", + (void *) 0x50); + request_irq(6, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch", + (void *) 0x60); +} + +/* + * PSC interrupt handler. It's a lot like the VIA interrupt handler. + */ + +void 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; + + irq -= VEC_SPUR; + 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; + + 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); + } + } +} + +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 000000000..2205b226c --- /dev/null +++ b/arch/m68k/mac/via.c @@ -0,0 +1,774 @@ +/* + * 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 <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 + * _consistantly_ 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); +void via1_irq(int, void *, struct pt_regs *); +void via2_irq(int, void *, struct pt_regs *); +void 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 void mac_bang(int, void *, struct pt_regs *); +extern void mac_SCC_handler(int, void *, struct pt_regs *); +extern int console_loglevel; +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("VIA1 at %p is a 6522 or clone\n", via1); + + printk("VIA2 at %p is ", via2); + if (rbv_present) { + printk("an RBV\n"); + } else if (oss_present) { + printk("an OSS\n"); + } else { + printk("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(void (*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) { + request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, + "Software IRQ", (void *) via1); + request_irq(IRQ_AUTO_6, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, + "VIA1 Dispatch", (void *) via1); + } else { + request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, + "VIA1 Dispatch", (void *) via1); +#if 0 /* interferes with serial on some machines */ + if (!psc_present) { + request_irq(IRQ_AUTO_6, mac_bang, IRQ_FLG_LOCK, + "Off Switch", mac_bang); + } +#endif + } + request_irq(IRQ_AUTO_2, via2_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, + "VIA2 Dispatch", (void *) via2); + if (!psc_present) { + request_irq(IRQ_AUTO_4, mac_SCC_handler, IRQ_FLG_LOCK, + "SCC Dispatch", mac_SCC_handler); + } + request_irq(IRQ_MAC_NUBUS, via_nubus_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, + "Nubus Dispatch", (void *) via2); +} + +/* + * Debugging dump, used in various places to see what's going on. + */ + +void via_debug_dump(void) +{ + printk("VIA1: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n", + (uint) via1[vDirA], (uint) via1[vDirB], (uint) via1[vACR]); + printk(" PCR = 0x%02X IFR = 0x%02X IER = 0x%02X\n", + (uint) via1[vPCR], (uint) via1[vIFR], (uint) via1[vIER]); + if (oss_present) { + printk("VIA2: <OSS>\n"); + } else if (rbv_present) { + printk("VIA2: IFR = 0x%02X IER = 0x%02X\n", + (uint) via2[rIFR], (uint) via2[rIER]); + printk(" SIFR = 0x%02X SIER = 0x%02X\n", + (uint) via2[rSIFR], (uint) via2[rSIER]); + } else { + printk("VIA2: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n", + (uint) via2[vDirA], (uint) via2[vDirB], + (uint) via2[vACR]); + printk(" 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; +} + +/* + * VIA-based power switch, for machines that support it. + */ + +void via_poweroff(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; + /* Otherwise it prints "It is now.." then shuts off */ + mdelay(1000); + } + + /* We should never make it this far... */ + printk ("It is now safe to switch off your machine.\n"); + while(1); +} + +/* + * Initialize VIA2 for Nubus access + */ + +void __init via_nubus_init(void) +{ + nubus_active = 0; + + /* unlock nubus transactions */ + + if (!rbv_present) { + /* set the line to be an output on non-RBV machines */ + via2[vDirB] |= 0x02; + } + via2[gBufB] |= 0x02; + + /* disable nubus slot interrupts. */ + if (rbv_present) { + via2[rSIER] = 0x7F; /* like VIA; bit 7=clr,set */ + } else { + via2[vBufA] = 0xFF; /* active low irqs, force high */ + via2[vDirA] = 0xFF; /* ddr to output. */ + } +} + +/* + * 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. + */ + +void via1_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int irq_bit, i; + unsigned char events, mask; + + irq -= VEC_SPUR; + + mask = via1[vIER] & 0x7F; + if (!(events = via1[vIFR] & mask)) return; + + 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 (!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); + } +} + +void via2_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int irq_bit, i; + unsigned char events, mask; + + irq -= VEC_SPUR; + + mask = via2[gIER] & 0x7F; + if (!(events = via2[gIFR] & mask)) return; + + 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; + } +} + +/* + * Dispatch Nubus interrupts. We are called as a secondary dispatch by the + * VIA2 dispatcher as a fast interrupt handler. + */ + +void 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; + + 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); + } + } +} + +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("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 */ + 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("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 */ + 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; +} + +void via_scsi_clear(void) +{ + volatile unsigned char deep_magic; + +#ifdef DEBUG_IRQUSE + printk("via_scsi_clear()\n"); +#endif + + /* We handle this in oss.c , but this gets called in mac_scsinew.c */ + if(oss_present) return; + + if (rbv_present) { + via2[rIFR] = (1<<3) | (1<<0) | rbv_clear; + deep_magic = via2[rBufB]; + } else { + deep_magic = via2[vBufB]; + } + mac_enable_irq(IRQ_MAC_SCSI); +} + +/* + * 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 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. + */ + +void via_pram_command(int command, __u8 *data) +{ + unsigned long cpu_flags; + int is_read; + + save_flags(cpu_flags); + cli(); + + /* 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; + + restore_flags(cpu_flags); +} + +/* + * 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. + */ + +__u32 via_read_time(void) +{ + union { + __u8 cdata[4]; + __u32 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%08X and 0x%08X\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; +} + +/* + * 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. + */ + +void via_write_time(__u32 time) +{ + union { + __u8 cdata[4]; + __u32 idata; + } data; + __u8 temp; + + /* Clear the write protect bit */ + + temp = 0x55; + via_pram_command(0x35, &temp); + + data.idata = time; + 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); +} diff --git a/arch/m68k/mac/via6522.c b/arch/m68k/mac/via6522.c deleted file mode 100644 index 05e6f44e4..000000000 --- a/arch/m68k/mac/via6522.c +++ /dev/null @@ -1,419 +0,0 @@ -/* - * 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. - */ - -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/delay.h> - -#include <asm/adb.h> -#include <asm/bootinfo.h> -#include <asm/macintosh.h> -#include <asm/macints.h> -#include "via6522.h" -#include <asm/mac_psc.h> - -volatile unsigned char *via1=(unsigned char *)VIABASE; -volatile unsigned char *via2=(unsigned char *)VIABASE2; -volatile unsigned char *psc=(unsigned char *)PSCBASE; - -volatile long *via_memory_bogon=(long *)&via_memory_bogon; - -unsigned char via1_clock, via1_datab; - -static int rbv=0; -static int oss=0; - -extern void adb_interrupt(int slot, void *via, struct pt_regs *regs); - -/* - * hardware reset vector - */ -static void (*rom_reset)(void); - -/* - * Timer defs. - */ -#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) - - -void via_configure_base(void) -{ - - switch(macintosh_config->via_type) - { - /* - * CI, SI, VX, LC - */ - case MAC_VIA_IIci: - via1=(void *)0x50F00000; - via2=(void *)0x50F26000; - rbv=1; - if (macintosh_config->ident == MAC_MODEL_IIFX) { - via2=(void *)0x50F1A000; - oss=1; - } - break; - /* - * Quadra and early MacIIs agree on the VIA locations - */ - case MAC_VIA_QUADRA: - case MAC_VIA_II: - via1=(void *)0x50F00000; - via2=(void *)0x50F02000; - break; - default: - } -} - - -void via_init_clock(void (*func)(int, void *, struct pt_regs *)) -{ - unsigned char c; - - via1_clock=via_read(via1, vACR); - via1_datab=via_read(via1, vBufB); - - /* - * Tell what MacOS left us with - */ - - printk("via_init: boot via1 acr=%X pcr=%X buf_a=%X dir_a=%X buf_b=%X dir_b=%X \n", - (int)via1_clock, (int)via_read(via1, vPCR), - (int)via_read(via1, vBufA), (int)via_read(via1, vDirA), - (int)via_read(via1, vBufB), (int)via_read(via1, vDirB)); - - if (rbv == 0) - printk("via_init: boot via2 acr=%X pcr=%X buf_a=%X dir_a=%X buf_b=%X dir_b=%X \n", - (int)via_read(via2, vACR), (int)via_read(via2, vPCR), - (int)via_read(via2, vBufA), (int)via_read(via2, vDirA), - (int)via_read(via2, vBufB), (int)via_read(via2, vDirB)); - - /* - * Shut it down - */ - - via_write(via1,vIER, 0x7F); - - /* - * Kill the timers - */ - - via_write(via1,vT1LL,0); - via_write(via1,vT1LH,0); - via_write(via1,vT1CL,0); - via_write(via1,vT1CH,0); - via_write(via1,vT2CL,0); - via_write(via1,vT2CH,0); - - /* - * Now do via2 - */ - - if(rbv==0) - { - via_write(via2,vT1LL,0); - via_write(via2,vT1LH,0); - via_write(via2,vT1CL,0); - via_write(via2,vT1CH,0); - via_write(via2,vT2CL,0); - via_write(via2,vT2CH,0); - via_write(via2,vIER, 0x7F); - } - else if (oss==0) - { - /* - * Init the RBV chip a bit - */ - - via_write(via2, rIER,0x7F); - } - - /* - * Disable the timer latches - */ - - c=via_read(via1,vACR); - via_write(via1,vACR,c&0x3F); - - if(rbv==0) - { - c=via_read(via2,vACR); - via_write(via2,vACR,c&0x3F); - } - - /* - * Now start the clock - we want 100Hz - */ - - via_write(via1,vACR,via_read(via1,vACR)|0x40); - - via_write(via1,vT1LL, MAC_CLOCK_LOW); - via_write(via1,vT1LH, MAC_CLOCK_HIGH); - via_write(via1,vT1CL, MAC_CLOCK_LOW); - via_write(via1,vT1CH, MAC_CLOCK_HIGH); - - /* - * And enable its interrupt - */ - - request_irq(IRQ_MAC_TIMER_1, func, IRQ_FLG_LOCK, "timer", func); - - /* - * SE/30: disable video int. - * XXX: testing for SE/30 VBL - */ - - if (macintosh_config->ident == MAC_MODEL_SE30) { - c = via_read(via1, vBufB); - via_write(via1, vBufB, c|(0x40)); - c = via_read(via1, vDirB); - via_write(via1, vDirB, c|(0x40)); - } - -#if 0 /* gone to mac_init_IRQ */ - /* - * Set vPCR for SCSI interrupts. - * - * That is: CA1 negative edge int., CA2 indep., positive edge int.; - * CB1 negative edge int., CB2 indep., positive edge int.. - */ - via_write(via2,vPCR, 0x66); -#endif - -} - -/* - * TBI: get time offset between scheduling timer ticks - */ -#define TICK_SIZE 10000 - -/* This is always executed with interrupts disabled. */ - -unsigned long mac_gettimeoffset (void) -{ - unsigned long ticks, offset = 0; - - /* read VIA1 timer 2 current value */ - ticks = via_read(via1, vT1CL) + (via_read(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 (via_read(via1, vIFR) & 0x40) - offset = TICK_SIZE; - - ticks = MAC_CLOCK_TICK - ticks; - ticks = ticks * 10000L / MAC_CLOCK_TICK; - - return ticks + offset; -} - -/* - * PSC (AV Macs; level 3-6): initialize interrupt enable registers - */ - -void psc_init(void) -{ - via_write(psc, pIER3, 0x01); - via_write(psc, pIER4, 0x09); - via_write(psc, pIER4, 0x86); - via_write(psc, pIER5, 0x03); - via_write(psc, pIER6, 0x07); -} - -/* - * The power switch - yes it's software! - */ - -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 (macintosh_config->adb_type == MAC_ADB_II) - { - if(rbv) { - via_write(via2, rBufB, via_read(via2, rBufB)&~0x04); - } else { - /* Direction of vDirB is output */ - via_write(via2,vDirB,via_read(via2,vDirB)|0x04); - /* Send a value of 0 on that line */ - via_write(via2,vBufB,via_read(via2,vBufB)&~0x04); - /* Otherwise it prints "It is now.." then shuts off */ - mdelay(1000); - } - - /* We should never make it this far... */ - printk ("It is now safe to switch off your machine.\n"); - - /* XXX - delay do we need to spin here ? */ - while(1); /* Just in case .. */ - } - - /* - * Initially discovered this technique in the Mach kernel of MkLinux in - * osfmk/src/mach_kernel/ppc/POWERMAC/cuda_power.c. Found equivalent LinuxPPC - * code in arch/ppc/kernel/setup.c, which also has a PMU technique for PowerBooks! - * --David Kilzer - */ - - else if (macintosh_config->adb_type == MAC_ADB_IISI - || macintosh_config->adb_type == MAC_ADB_CUDA) - { - struct adb_request req; - - /* - * Print our "safe" message before we send the request - * just in case the request never returns. - */ - - printk ("It is now safe to switch off your machine.\n"); - - adb_request (&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN); - - printk ("ADB powerdown request sent.\n"); - for (;;) - { - adb_poll(); - } - } -} - -/* - * Not all Macs support software power down; for the rest, just - * try the ROM reset vector ... - */ -void mac_reset(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 (macintosh_config->adb_type == MAC_ADB_II) - { - unsigned long flags; - unsigned long *reset_hook; - - /* 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 0 - /* testing, doesn't work on SE/30 either */ - reset_hook = (unsigned long *) (mac_bi_data.rombase + 0x4); - printk("ROM reset hook: %p\n", *reset_hook); - rom_reset = *reset_hook; -#endif - if (macintosh_config->ident == MAC_MODEL_SE30) { - /* - * MSch: Machines known to crash on ROM reset ... - */ - printk("System halted.\n"); - while(1); - } else { - save_flags(flags); - cli(); - - rom_reset(); - - restore_flags(flags); - } - - /* We never make it this far... it usually panics above. */ - printk ("Restart failed. Please restart manually.\n"); - - /* XXX - delay do we need to spin here ? */ - while(1); /* Just in case .. */ - } - - /* - * Initially discovered this technique in the Mach kernel of MkLinux in - * osfmk/src/mach_kernel/ppc/POWERMAC/cuda_power.c. Found equivalent LinuxPPC - * code in arch/ppc/kernel/setup.c, which also has a PMU technique! - * --David Kilzer - * - * I suspect the MAC_ADB_CUDA code might work with other ADB types of machines - * but have no way to test this myself. --DDK - */ - - else if (macintosh_config->adb_type == MAC_ADB_IISI - || macintosh_config->adb_type == MAC_ADB_CUDA) - { - struct adb_request req; - - adb_request (&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM); - - printk ("Restart failed. Please restart manually.\n"); - for (;;) - { - adb_poll(); - } - } -} - -/* - * Set up the keyboard - */ - -void via_setup_keyboard(void) -{ -#if 0 /* moved to adb */ - via1_func_tab.vector[2]=adb_interrupt; -#else - request_irq(IRQ_MAC_ADB, adb_interrupt, IRQ_FLG_LOCK, "adb interrupt", - adb_interrupt); -#endif -} - -/* - * Floppy hook - */ - -void via1_set_head(int head) -{ - if(head==0) - via_write(via1, vBufA, via_read(via1, vBufA)&~0x20); - else - via_write(via1, vBufA, via_read(via1, vBufA)|0x20); -} - -void nubus_init_via(void) -{ - if (rbv) { - if (oss==0) { - via_write(via2, rBufB, via_read(via2, rBufB)|0x02); - via_write(via2, rIER, 0x82); /* Interrupts on */ - } - } else { - /* Assert the nubus active */ - via_write(via2, vDirB, via_read(via2, vDirB)|0x02); - via_write(via2, vBufB, via_read(via2, vBufB)|0x02); - /* Make the nubus interrupt source register all output (disable) */ - /* via_write(via2, vDirA, 0xFF); */ - via_write(via2, vIER, 0x82); /* Interrupts on */ - } - - printk("nubus_init_via: via1 acr=%X datab=%X pcr=%X\n", - (int)via_read(via1, vACR), (int)via_read(via1, vBufB), - (int)via_read(via1, vPCR)); - - if (rbv==0) - printk("nubus_init_via: via2 acr=%X datab=%X pcr=%X\n", - (int)via_read(via2, vACR), (int)via_read(via2, vBufB), - (int)via_read(via2, vPCR)); -} diff --git a/arch/m68k/mac/via6522.h b/arch/m68k/mac/via6522.h deleted file mode 100644 index 91ba1d58e..000000000 --- a/arch/m68k/mac/via6522.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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. The picture - * is a bit incomplete as the Mac documentation doesnt cover this well - */ - -#ifndef _ASM_VIA6522_H_ -#define _ASM_VIA6522_H_ - -#define VIABASE 0x50F00000 -#define VIABASE2 0x50F02000 - -/* - * Not all of these are true post MacII I think - */ - -#define VIA1A_vSccWrReq 0x80 /* SCC write */ -#define VIA1A_vRev8 0x40 /* Revision 8 board ??? */ -#define VIA1A_vHeadSel 0x20 /* Head select for IWM */ -#define VIA1A_vOverlay 0x10 -#define VIA1A_vSync 0x08 -#define VIA1A_vVolume 0x07 /* Audio volume mask */ - -#define VIA1B_vSound 0x80 /* Audio on/off */ -#define VIA1B_vMystery 0x40 -#define VIA1B_vADBS2 0x20 /* ADB state 2 */ -#define VIA1B_vADBS1 0x10 /* ADB state 1 */ -#define VIA1B_vADBInt 0x08 /* ADB interrupt */ -#define VIA1B_vRTCEnb 0x04 /* Real time clock */ -#define VIA1B_vRTCClk 0x02 -#define VIA1B_vRTCData 0x01 - -/* - * VIA2 A register is the interrupt lines raised off the nubus - * slots. - */ - -#define VIA2A_vIRQE 0x20 -#define VIA2A_vIRQD 0x10 -#define VIA2A_vIRQC 0x08 -#define VIA2A_vIRQB 0x04 -#define VIA2A_vIRQA 0x02 -#define VIA2A_vIRQ9 0x01 - -/* - * Register B has the fun stuff in it - */ - -#define VIA2B_vMode32 0x08 /* 24/32bit switch - doubles as cache flush */ -#define VIA2B_vPower 0x04 /* Off switch */ -#define VIA2B_vBusLk 0x02 /* Nubus in use ?? */ -#define VIA2B_vCDis 0x01 /* Cache disable */ - -/* - * The 6522 via is a 2MHz part, and needs a delay. MacOS seems to - * execute MOV (Ax),(Ax) for this... Oh and we can't use udelay - * here... see we need the via to calibrate the udelay loop ... - */ - -extern volatile long *via_memory_bogon; - -extern __inline__ void via_write(volatile unsigned char *via,int reg, int v) -{ - *via_memory_bogon; - *via_memory_bogon; - *via_memory_bogon; - via[reg]=v; -} - -extern __inline__ int via_read(volatile unsigned char *via,int reg) -{ - *via_memory_bogon; - *via_memory_bogon; - *via_memory_bogon; - return (int)via[reg]; -} - -extern volatile unsigned char *via1,*via2; - -/* - * 6522 registers - see databook - */ - -#define vBufB 0x0000 -#define vBufA 0x0200 -#define vDirB 0x0400 -#define vDirA 0x0600 -#define vT1CL 0x0800 -#define vT1CH 0x0a00 -#define vT1LL 0x0c00 -#define vT1LH 0x0e00 -#define vT2CL 0x1000 -#define vT2CH 0x1200 -#define vSR 0x1400 -#define vACR 0x1600 -#define vPCR 0x1800 -#define vIFR 0x1a00 -#define vIER 0x1c00 -#define vANH 0x1e00 /* register A (no shake) */ - -#define rBufB 0x00 -#define rBufA 0x02 -/*#define rIFR 0x03*/ -#define rIFR 0x1A03 -#define rVideo 0x10 -#define rSlot 0x12 -/*#define rIER 0x13*/ -#define rIER 0x1C13 -/* -#define R_rIFR 0x03 -#define R_rIER 0x13 -#define W_rIFR 0x1A03 -#define W_rIER 0x1C13 -*/ -/* - * VIA interrupt - */ - -struct via_irq_tab -{ - void (*vector[8])(int, void *, struct pt_regs *); -}; - -extern void via1_irq(int, void *, struct pt_regs *); -extern void via2_irq(int, void *, struct pt_regs *); - -extern void via_setup_keyboard(void); - -#endif /* _ASM_VIA6522_H_ */ diff --git a/arch/m68k/math-emu/Makefile b/arch/m68k/math-emu/Makefile new file mode 100644 index 000000000..fe379cb2c --- /dev/null +++ b/arch/m68k/math-emu/Makefile @@ -0,0 +1,19 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.S.o: + $(CC) $(EXTRA_CFLAGS) -D__ASSEMBLY__ -traditional -c $< -o $*.o + +#EXTRA_CFLAGS=-DFPU_EMU_DEBUG + +O_TARGET := mathemu.o +O_OBJS := fp_entry.o fp_scan.o fp_util.o fp_move.o fp_movem.o \ + fp_cond.o fp_arith.o fp_log.o fp_trig.o + +include $(TOPDIR)/Rules.make diff --git a/arch/m68k/math-emu/fp_arith.c b/arch/m68k/math-emu/fp_arith.c new file mode 100644 index 000000000..c494b1da9 --- /dev/null +++ b/arch/m68k/math-emu/fp_arith.c @@ -0,0 +1,700 @@ +/* + + fp_arith.c: floating-point math routines for the Linux-m68k + floating point emulator. + + Copyright (c) 1998-1999 David Huggins-Daines. + + Somewhat based on the AlphaLinux floating point emulator, by David + Mosberger-Tang. + + You may copy, modify, and redistribute this file under the terms of + the GNU General Public License, version 2, or any later version, at + your convenience. + */ + +#include "fp_emu.h" +#include "multi_arith.h" +#include "fp_arith.h" + +const struct fp_ext fp_QNaN = +{ + 0, 0, 0x7fff, { ~0 } +}; + +const struct fp_ext fp_Inf = +{ + 0, 0, 0x7fff, { 0 } +}; + +/* let's start with the easy ones */ + +struct fp_ext * +fp_fabs(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fabs\n"); + + fp_monadic_check(dest, src); + + dest->sign = 0; + + return dest; +} + +struct fp_ext * +fp_fneg(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fneg\n"); + + fp_monadic_check(dest, src); + + dest->sign = !dest->sign; + + return dest; +} + +/* Now, the slightly harder ones */ + +/* fp_fadd: Implements the kernel of the FADD, FSADD, FDADD, FSUB, + FDSUB, and FCMP instructions. */ + +struct fp_ext * +fp_fadd(struct fp_ext *dest, struct fp_ext *src) +{ + int diff; + + dprint(PINSTR, "fadd\n"); + + fp_dyadic_check(dest, src); + + if (IS_INF(dest)) { + /* infinity - infinity == NaN */ + if (IS_INF(src) && (src->sign != dest->sign)) + fp_set_nan(dest); + return dest; + } + if (IS_INF(src)) { + fp_copy_ext(dest, src); + return dest; + } + + if (IS_ZERO(dest)) { + if (IS_ZERO(src)) { + if (src->sign != dest->sign) { + if (FPDATA->rnd == FPCR_ROUND_RM) + dest->sign = 1; + else + dest->sign = 0; + } + } else + fp_copy_ext(dest, src); + return dest; + } + + dest->lowmant = src->lowmant = 0; + + if ((diff = dest->exp - src->exp) > 0) + fp_denormalize(src, diff); + else if ((diff = -diff) > 0) + fp_denormalize(dest, diff); + + if (dest->sign == src->sign) { + if (fp_addmant(dest, src)) + if (!fp_addcarry(dest)) + return dest; + } else { + if (dest->mant.m64 < src->mant.m64) { + fp_submant(dest, src, dest); + dest->sign = !dest->sign; + } else + fp_submant(dest, dest, src); + } + + return dest; +} + +/* fp_fsub: Implementes the kernel of the FSUB, FSSUB, and FDSUB + instructions. + + Remember that the arguments are in assembler-syntax order! */ + +struct fp_ext * +fp_fsub(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fsub "); + + src->sign = !src->sign; + return fp_fadd(dest, src); +} + + +struct fp_ext * +fp_fcmp(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fcmp "); + + FPDATA->temp[1] = *dest; + src->sign = !src->sign; + return fp_fadd(&FPDATA->temp[1], src); +} + +struct fp_ext * +fp_ftst(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "ftst\n"); + + (void)dest; + + return src; +} + +struct fp_ext * +fp_fmul(struct fp_ext *dest, struct fp_ext *src) +{ + union fp_mant128 temp; + int exp; + + dprint(PINSTR, "fmul\n"); + + fp_dyadic_check(dest, src); + + /* calculate the correct sign now, as it's necessary for infinities */ + dest->sign = src->sign ^ dest->sign; + + /* Handle infinities */ + if (IS_INF(dest)) { + if (IS_ZERO(src)) + fp_set_nan(dest); + return dest; + } + if (IS_INF(src)) { + if (IS_ZERO(dest)) + fp_set_nan(dest); + else + fp_copy_ext(dest, src); + return dest; + } + + /* Of course, as we all know, zero * anything = zero. You may + not have known that it might be a positive or negative + zero... */ + if (IS_ZERO(dest) || IS_ZERO(src)) { + dest->exp = 0; + dest->mant.m64 = 0; + dest->lowmant = 0; + + return dest; + } + + exp = dest->exp + src->exp - 0x3ffe; + + /* shift up the mantissa for denormalized numbers, + so that the highest bit is set, this makes the + shift of the result below easier */ + if ((long)dest->mant.m32[0] >= 0) + exp -= fp_overnormalize(dest); + if ((long)src->mant.m32[0] >= 0) + exp -= fp_overnormalize(src); + + /* now, do a 64-bit multiply with expansion */ + fp_multiplymant(&temp, dest, src); + + /* normalize it back to 64 bits and stuff it back into the + destination struct */ + if ((long)temp.m32[0] > 0) { + exp--; + fp_putmant128(dest, &temp, 1); + } else + fp_putmant128(dest, &temp, 0); + + if (exp >= 0x7fff) { + fp_set_ovrflw(dest); + return dest; + } + dest->exp = exp; + if (exp < 0) { + fp_set_sr(FPSR_EXC_UNFL); + fp_denormalize(dest, -exp); + } + + return dest; +} + +/* fp_fdiv: Implements the "kernel" of the FDIV, FSDIV, FDDIV and + FSGLDIV instructions. + + Note that the order of the operands is counter-intuitive: instead + of src / dest, the result is actually dest / src. */ + +struct fp_ext * +fp_fdiv(struct fp_ext *dest, struct fp_ext *src) +{ + union fp_mant128 temp; + int exp; + + dprint(PINSTR, "fdiv\n"); + + fp_dyadic_check(dest, src); + + /* calculate the correct sign now, as it's necessary for infinities */ + dest->sign = src->sign ^ dest->sign; + + /* Handle infinities */ + if (IS_INF(dest)) { + /* infinity / infinity = NaN (quiet, as always) */ + if (IS_INF(src)) + fp_set_nan(dest); + /* infinity / anything else = infinity (with approprate sign) */ + return dest; + } + if (IS_INF(src)) { + /* anything / infinity = zero (with appropriate sign) */ + dest->exp = 0; + dest->mant.m64 = 0; + dest->lowmant = 0; + + return dest; + } + + /* zeroes */ + if (IS_ZERO(dest)) { + /* zero / zero = NaN */ + if (IS_ZERO(src)) + fp_set_nan(dest); + /* zero / anything else = zero */ + return dest; + } + if (IS_ZERO(src)) { + /* anything / zero = infinity (with appropriate sign) */ + fp_set_sr(FPSR_EXC_DZ); + dest->exp = 0x7fff; + dest->mant.m64 = 0; + + return dest; + } + + exp = dest->exp - src->exp + 0x3fff; + + /* shift up the mantissa for denormalized numbers, + so that the highest bit is set, this makes lots + of things below easier */ + if ((long)dest->mant.m32[0] >= 0) + exp -= fp_overnormalize(dest); + if ((long)src->mant.m32[0] >= 0) + exp -= fp_overnormalize(src); + + /* now, do the 64-bit divide */ + fp_dividemant(&temp, dest, src); + + /* normalize it back to 64 bits and stuff it back into the + destination struct */ + if (!temp.m32[0]) { + exp--; + fp_putmant128(dest, &temp, 32); + } else + fp_putmant128(dest, &temp, 31); + + if (exp >= 0x7fff) { + fp_set_ovrflw(dest); + return dest; + } + dest->exp = exp; + if (exp < 0) { + fp_set_sr(FPSR_EXC_UNFL); + fp_denormalize(dest, -exp); + } + + return dest; +} + +struct fp_ext * +fp_fsglmul(struct fp_ext *dest, struct fp_ext *src) +{ + int exp; + + dprint(PINSTR, "fsglmul\n"); + + fp_dyadic_check(dest, src); + + /* calculate the correct sign now, as it's necessary for infinities */ + dest->sign = src->sign ^ dest->sign; + + /* Handle infinities */ + if (IS_INF(dest)) { + if (IS_ZERO(src)) + fp_set_nan(dest); + return dest; + } + if (IS_INF(src)) { + if (IS_ZERO(dest)) + fp_set_nan(dest); + else + fp_copy_ext(dest, src); + return dest; + } + + /* Of course, as we all know, zero * anything = zero. You may + not have known that it might be a positive or negative + zero... */ + if (IS_ZERO(dest) || IS_ZERO(src)) { + dest->exp = 0; + dest->mant.m64 = 0; + dest->lowmant = 0; + + return dest; + } + + exp = dest->exp + src->exp - 0x3ffe; + + /* do a 32-bit multiply */ + fp_mul64(dest->mant.m32[0], dest->mant.m32[1], + dest->mant.m32[0] & 0xffffff00, + src->mant.m32[0] & 0xffffff00); + + if (exp >= 0x7fff) { + fp_set_ovrflw(dest); + return dest; + } + dest->exp = exp; + if (exp < 0) { + fp_set_sr(FPSR_EXC_UNFL); + fp_denormalize(dest, -exp); + } + + return dest; +} + +struct fp_ext * +fp_fsgldiv(struct fp_ext *dest, struct fp_ext *src) +{ + int exp; + unsigned long quot, rem; + + dprint(PINSTR, "fsgldiv\n"); + + fp_dyadic_check(dest, src); + + /* calculate the correct sign now, as it's necessary for infinities */ + dest->sign = src->sign ^ dest->sign; + + /* Handle infinities */ + if (IS_INF(dest)) { + /* infinity / infinity = NaN (quiet, as always) */ + if (IS_INF(src)) + fp_set_nan(dest); + /* infinity / anything else = infinity (with approprate sign) */ + return dest; + } + if (IS_INF(src)) { + /* anything / infinity = zero (with appropriate sign) */ + dest->exp = 0; + dest->mant.m64 = 0; + dest->lowmant = 0; + + return dest; + } + + /* zeroes */ + if (IS_ZERO(dest)) { + /* zero / zero = NaN */ + if (IS_ZERO(src)) + fp_set_nan(dest); + /* zero / anything else = zero */ + return dest; + } + if (IS_ZERO(src)) { + /* anything / zero = infinity (with appropriate sign) */ + fp_set_sr(FPSR_EXC_DZ); + dest->exp = 0x7fff; + dest->mant.m64 = 0; + + return dest; + } + + exp = dest->exp - src->exp + 0x3fff; + + dest->mant.m32[0] &= 0xffffff00; + src->mant.m32[0] &= 0xffffff00; + + /* do the 32-bit divide */ + if (dest->mant.m32[0] >= src->mant.m32[0]) { + fp_sub64(dest->mant, src->mant); + fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]); + dest->mant.m32[0] = 0x80000000 | (quot >> 1); + dest->mant.m32[1] = (quot & 1) | rem; /* only for rounding */ + } else { + fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]); + dest->mant.m32[0] = quot; + dest->mant.m32[1] = rem; /* only for rounding */ + exp--; + } + + if (exp >= 0x7fff) { + fp_set_ovrflw(dest); + return dest; + } + dest->exp = exp; + if (exp < 0) { + fp_set_sr(FPSR_EXC_UNFL); + fp_denormalize(dest, -exp); + } + + return dest; +} + +/* fp_roundint: Internal rounding function for use by several of these + emulated instructions. + + This one rounds off the fractional part using the rounding mode + specified. */ + +static void fp_roundint(struct fp_ext *dest, int mode) +{ + union fp_mant64 oldmant; + unsigned long mask; + + if (!fp_normalize_ext(dest)) + return; + + /* infinities and zeroes */ + if (IS_INF(dest) || IS_ZERO(dest)) + return; + + /* first truncate the lower bits */ + oldmant = dest->mant; + switch (dest->exp) { + case 0 ... 0x3ffe: + dest->mant.m64 = 0; + break; + case 0x3fff ... 0x401e: + dest->mant.m32[0] &= 0xffffffffU << (0x401e - dest->exp); + dest->mant.m32[1] = 0; + if (oldmant.m64 == dest->mant.m64) + return; + break; + case 0x401f ... 0x403e: + dest->mant.m32[1] &= 0xffffffffU << (0x403e - dest->exp); + if (oldmant.m32[1] == dest->mant.m32[1]) + return; + break; + default: + return; + } + fp_set_sr(FPSR_EXC_INEX2); + + /* We might want to normalize upwards here... however, since + we know that this is only called on the output of fp_fdiv, + or with the input to fp_fint or fp_fintrz, and the inputs + to all these functions are either normal or denormalized + (no subnormals allowed!), there's really no need. + + In the case of fp_fdiv, observe that 0x80000000 / 0xffff = + 0xffff8000, and the same holds for 128-bit / 64-bit. (i.e. the + smallest possible normal dividend and the largest possible normal + divisor will still produce a normal quotient, therefore, (normal + << 64) / normal is normal in all cases) */ + + switch (mode) { + case FPCR_ROUND_RN: + switch (dest->exp) { + case 0 ... 0x3ffd: + return; + case 0x3ffe: + /* As noted above, the input is always normal, so the + guard bit (bit 63) is always set. therefore, the + only case in which we will NOT round to 1.0 is when + the input is exactly 0.5. */ + if (oldmant.m64 == (1ULL << 63)) + return; + break; + case 0x3fff ... 0x401d: + mask = 1 << (0x401d - dest->exp); + if (!(oldmant.m32[0] & mask)) + return; + if (oldmant.m32[0] & (mask << 1)) + break; + if (!(oldmant.m32[0] << (dest->exp - 0x3ffd)) && + !oldmant.m32[1]) + return; + break; + case 0x401e: + if (!(oldmant.m32[1] >= 0)) + return; + if (oldmant.m32[0] & 1) + break; + if (!(oldmant.m32[1] << 1)) + return; + break; + case 0x401f ... 0x403d: + mask = 1 << (0x403d - dest->exp); + if (!(oldmant.m32[1] & mask)) + return; + if (oldmant.m32[1] & (mask << 1)) + break; + if (!(oldmant.m32[1] << (dest->exp - 0x401d))) + return; + break; + default: + return; + } + break; + case FPCR_ROUND_RZ: + return; + default: + if (dest->sign ^ (mode - FPCR_ROUND_RM)) + break; + return; + } + + switch (dest->exp) { + case 0 ... 0x3ffe: + dest->exp = 0x3fff; + dest->mant.m64 = 1ULL << 63; + break; + case 0x3fff ... 0x401e: + mask = 1 << (0x401e - dest->exp); + if (dest->mant.m32[0] += mask) + break; + dest->mant.m32[0] = 0x80000000; + dest->exp++; + break; + case 0x401f ... 0x403e: + mask = 1 << (0x403e - dest->exp); + if (dest->mant.m32[1] += mask) + break; + if (dest->mant.m32[0] += 1) + break; + dest->mant.m32[0] = 0x80000000; + dest->exp++; + break; + } +} + +/* modrem_kernel: Implementation of the FREM and FMOD instructions + (which are exactly the same, except for the rounding used on the + intermediate value) */ + +static struct fp_ext * +modrem_kernel(struct fp_ext *dest, struct fp_ext *src, int mode) +{ + struct fp_ext tmp; + + fp_dyadic_check(dest, src); + + /* Infinities and zeros */ + if (IS_INF(dest) || IS_ZERO(src)) { + fp_set_nan(dest); + return dest; + } + if (IS_ZERO(dest) || IS_INF(src)) + return dest; + + /* FIXME: there is almost certainly a smarter way to do this */ + fp_copy_ext(&tmp, dest); + fp_fdiv(&tmp, src); /* NOTE: src might be modified */ + fp_roundint(&tmp, mode); + fp_fmul(&tmp, src); + fp_fsub(dest, &tmp); + + /* set the quotient byte */ + fp_set_quotient((dest->mant.m64 & 0x7f) | (dest->sign << 7)); + return dest; +} + +/* fp_fmod: Implements the kernel of the FMOD instruction. + + Again, the argument order is backwards. The result, as defined in + the Motorola manuals, is: + + fmod(src,dest) = (dest - (src * floor(dest / src))) */ + +struct fp_ext * +fp_fmod(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fmod\n"); + return modrem_kernel(dest, src, FPCR_ROUND_RZ); +} + +/* fp_frem: Implements the kernel of the FREM instruction. + + frem(src,dest) = (dest - (src * round(dest / src))) + */ + +struct fp_ext * +fp_frem(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "frem\n"); + return modrem_kernel(dest, src, FPCR_ROUND_RN); +} + +struct fp_ext * +fp_fint(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fint\n"); + + fp_copy_ext(dest, src); + + fp_roundint(dest, FPDATA->rnd); + + return dest; +} + +struct fp_ext * +fp_fintrz(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fintrz\n"); + + fp_copy_ext(dest, src); + + fp_roundint(dest, FPCR_ROUND_RZ); + + return dest; +} + +struct fp_ext * +fp_fscale(struct fp_ext *dest, struct fp_ext *src) +{ + int scale, oldround; + + dprint(PINSTR, "fscale\n"); + + fp_dyadic_check(dest, src); + + /* Infinities */ + if (IS_INF(src)) { + fp_set_nan(dest); + return dest; + } + if (IS_INF(dest)) + return dest; + + /* zeroes */ + if (IS_ZERO(src) || IS_ZERO(dest)) + return dest; + + /* Source exponent out of range */ + if (src->exp >= 0x400c) { + fp_set_ovrflw(dest); + return dest; + } + + /* src must be rounded with round to zero. */ + oldround = FPDATA->rnd; + FPDATA->rnd = FPCR_ROUND_RZ; + scale = fp_conv_ext2long(src); + FPDATA->rnd = oldround; + + /* new exponent */ + scale += dest->exp; + + if (scale >= 0x7fff) { + fp_set_ovrflw(dest); + } else if (scale <= 0) { + fp_set_sr(FPSR_EXC_UNFL); + fp_denormalize(dest, -scale); + } else + dest->exp = scale; + + return dest; +} + diff --git a/arch/m68k/math-emu/fp_arith.h b/arch/m68k/math-emu/fp_arith.h new file mode 100644 index 000000000..2cc3f846c --- /dev/null +++ b/arch/m68k/math-emu/fp_arith.h @@ -0,0 +1,52 @@ +/* + + fp_arith.h: floating-point math routines for the Linux-m68k + floating point emulator. + + Copyright (c) 1998 David Huggins-Daines. + + Somewhat based on the AlphaLinux floating point emulator, by David + Mosberger-Tang. + + You may copy, modify, and redistribute this file under the terms of + the GNU General Public License, version 2, or any later version, at + your convenience. + + */ + +#ifndef FP_ARITH_H +#define FP_ARITH_H + +/* easy ones */ +struct fp_ext * +fp_fabs(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fneg(struct fp_ext *dest, struct fp_ext *src); + +/* straightforward arithmetic */ +struct fp_ext * +fp_fadd(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fsub(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fcmp(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_ftst(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fmul(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fdiv(struct fp_ext *dest, struct fp_ext *src); + +/* ones that do rounding and integer conversions */ +struct fp_ext * +fp_fmod(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_frem(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fint(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fintrz(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fscale(struct fp_ext *dest, struct fp_ext *src); + +#endif /* FP_ARITH__H */ diff --git a/arch/m68k/math-emu/fp_cond.S b/arch/m68k/math-emu/fp_cond.S new file mode 100644 index 000000000..d9981d6a8 --- /dev/null +++ b/arch/m68k/math-emu/fp_cond.S @@ -0,0 +1,334 @@ +/* + * fp_cond.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * 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 the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fp_emu.h" +#include "fp_decode.h" + + .globl fp_fscc, fp_fbccw, fp_fbccl + +#ifdef FPU_EMU_DEBUG +fp_fnop: + printf PDECODE,"fnop\n" + jra fp_end +#else +#define fp_fnop fp_end +#endif + +fp_fbccw: + tst.w %d2 + jeq fp_fnop + printf PDECODE,"fbccw " + fp_get_pc %a0 + lea (-2,%a0,%d2.w),%a0 + jra 1f + +fp_fbccl: + printf PDECODE,"fbccl " + fp_get_pc %a0 + move.l %d2,%d0 + swap %d0 + fp_get_instr_word %d0,fp_err_ua1 + lea (-2,%a0,%d0.l),%a0 +1: printf PDECODE,"%x",1,%a0 + move.l %d2,%d0 + swap %d0 + jsr fp_compute_cond + tst.l %d0 + jeq 1f + fp_put_pc %a0,1 +1: printf PDECODE,"\n" + jra fp_end + +fp_fdbcc: + printf PDECODE,"fdbcc " + fp_get_pc %a1 | calculate new pc + fp_get_instr_word %d0,fp_err_ua1 + add.w %d0,%a1 + fp_decode_addr_reg + printf PDECODE,"d%d,%x\n",2,%d0,%a1 + swap %d1 | test condition in %d1 + tst.w %d1 + jne 2f + move.l %d0,%d1 + jsr fp_get_data_reg + subq.w #1,%d0 + jcs 1f + fp_put_pc %a1,1 +1: jsr fp_put_data_reg +2: jra fp_end + +| set flags for decode macros for fs<cc> +do_fscc=1 +do_no_pc_mode=1 + +fp_fscc: + printf PDECODE,"fscc " + move.l %d2,%d0 + jsr fp_compute_cond + move.w %d0,%d1 + swap %d1 + + | decode addressing mode + fp_decode_addr_mode + + .long fp_data, fp_fdbcc + .long fp_indirect, fp_postinc + .long fp_predecr, fp_disp16 + .long fp_extmode0, fp_extmode1 + + | addressing mode: data register direct +fp_data: + fp_mode_data_direct + move.w %d0,%d1 | save register nr + jsr fp_get_data_reg + swap %d1 + move.b %d1,%d0 + swap %d1 + jsr fp_put_data_reg + printf PDECODE,"\n" + jra fp_end + +fp_indirect: + fp_mode_addr_indirect + jra fp_do_scc + +fp_postinc: + fp_mode_addr_indirect_postinc + jra fp_do_scc + +fp_predecr: + fp_mode_addr_indirect_predec + jra fp_do_scc + +fp_disp16: + fp_mode_addr_indirect_disp16 + jra fp_do_scc + +fp_extmode0: + fp_mode_addr_indirect_extmode0 + jra fp_do_scc + +fp_extmode1: + bfextu %d2{#13,#3},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fp_absolute_short, fp_absolute_long + .long fp_ill, fp_ill | NOTE: jump here to ftrap.x + .long fp_ill, fp_ill + .long fp_ill, fp_ill + +fp_absolute_short: + fp_mode_abs_short + jra fp_do_scc + +fp_absolute_long: + fp_mode_abs_long +| jra fp_do_scc + +fp_do_scc: + swap %d1 + putuser.b %d1,(%a0),fp_err_ua1,%a0 + printf PDECODE,"\n" + jra fp_end + + +#define tst_NAN btst #24,%d1 +#define tst_Z btst #26,%d1 +#define tst_N btst #27,%d1 + +fp_compute_cond: + move.l (FPD_FPSR,FPDATA),%d1 + btst #4,%d0 + jeq 1f + tst_NAN + jeq 1f + bset #15,%d1 + bset #7,%d1 + move.l %d1,(FPD_FPSR,FPDATA) +1: and.w #0xf,%d0 + jmp ([0f:w,%pc,%d0.w*4]) + + .align 4 +0: + .long fp_f , fp_eq , fp_ogt, fp_oge + .long fp_olt, fp_ole, fp_ogl, fp_or + .long fp_un , fp_ueq, fp_ugt, fp_uge + .long fp_ult, fp_ule, fp_ne , fp_t + +fp_f: + moveq #0,%d0 + rts + +fp_eq: + moveq #0,%d0 + tst_Z + jeq 1f + moveq #-1,%d0 +1: rts + +fp_ogt: + moveq #0,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + tst_N + jne 1f + moveq #-1,%d0 +1: rts + +fp_oge: + moveq #-1,%d0 + tst_Z + jne 2f + tst_NAN + jne 1f + tst_N + jeq 2f +1: moveq #0,%d0 +2: rts + +fp_olt: + moveq #0,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + tst_N + jeq 1f + moveq #-1,%d0 +1: rts + +fp_ole: + moveq #-1,%d0 + tst_Z + jne 2f + tst_NAN + jne 1f + tst_N + jne 2f +1: moveq #0,%d0 +2: rts + +fp_ogl: + moveq #0,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + moveq #-1,%d0 +1: rts + +fp_or: + moveq #0,%d0 + tst_NAN + jne 1f + moveq #-1,%d0 +1: rts + +fp_un: + moveq #0,%d0 + tst_NAN + jeq 1f + moveq #-1,%d0 + rts + +fp_ueq: + moveq #-1,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + moveq #0,%d0 +1: rts + +fp_ugt: + moveq #-1,%d0 + tst_NAN + jne 2f + tst_N + jne 1f + tst_Z + jeq 2f +1: moveq #0,%d0 +2: rts + +fp_uge: + moveq #-1,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + tst_N + jeq 1f + moveq #0,%d0 +1: rts + +fp_ult: + moveq #-1,%d0 + tst_NAN + jne 2f + tst_Z + jne 1f + tst_N + jne 2f +1: moveq #0,%d0 +2: rts + +fp_ule: + moveq #-1,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + tst_N + jne 1f + moveq #0,%d0 +1: rts + +fp_ne: + moveq #0,%d0 + tst_Z + jne 1f + moveq #-1,%d0 +1: rts + +fp_t: + moveq #-1,%d0 + rts diff --git a/arch/m68k/math-emu/fp_decode.h b/arch/m68k/math-emu/fp_decode.h new file mode 100644 index 000000000..233269415 --- /dev/null +++ b/arch/m68k/math-emu/fp_decode.h @@ -0,0 +1,417 @@ +/* + * fp_decode.h + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * 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 the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FP_DECODE_H +#define _FP_DECODE_H + +/* These macros do the dirty work of the instr decoding, several variables + * can be defined in the source file to modify the work of these macros, + * currently the following variables are used: + * ... + * The register usage: + * d0 - will contain source operand for data direct mode, + * otherwise scratch register + * d1 - upper 16bit are reserved for caller + * lower 16bit may contain further arguments, + * is destroyed during decoding + * d2 - contains first two instruction words, + * first word will be used for extension word + * a0 - will point to source/dest operand for any indirect mode + * otherwise scratch register + * a1 - scratch register + * a2 - base addr to the task structure + * + * the current implementation doesn't check for every disallowed + * addressing mode (e.g. pc relative modes as destination), as long + * as it only means a new addressing mode, which should not appear + * in a program and that doesn't crash the emulation, I think it's + * not a problem to allow these modes. + */ + +do_fmovem=0 +do_fmovem_cr=0 +do_no_pc_mode=0 +do_fscc=0 + +| first decoding of the instr type +| this seperates the conditional instr +.macro fp_decode_cond_instr_type + bfextu %d2{#8,#2},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: +| .long "f<op>","fscc/fdbcc" +| .long "fbccw","fbccl" +.endm + +| second decoding of the instr type +| this seperates most move instr +.macro fp_decode_move_instr_type + bfextu %d2{#16,#3},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: +| .long "f<op> fpx,fpx","invalid instr" +| .long "f<op> <ea>,fpx","fmove fpx,<ea>" +| .long "fmovem <ea>,fpcr","fmovem <ea>,fpx" +| .long "fmovem fpcr,<ea>","fmovem fpx,<ea>" +.endm + +| extract the source specifier, specifies +| either source fp register or data format +.macro fp_decode_sourcespec + bfextu %d2{#19,#3},%d0 +.endm + +| decode destination format for fmove reg,ea +.macro fp_decode_dest_format + bfextu %d2{#19,#3},%d0 +.endm + +| decode source register for fmove reg,ea +.macro fp_decode_src_reg + bfextu %d2{#22,#3},%d0 +.endm + +| extract the addressing mode +| it depends on the instr which of the modes is valid +.macro fp_decode_addr_mode + bfextu %d2{#10,#3},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: +| .long "data register direct","addr register direct" +| .long "addr register indirect" +| .long "addr register indirect postincrement" +| .long "addr register indirect predecrement" +| .long "addr register + index16" +| .long "extension mode1","extension mode2" +.endm + +| extract the register for the addressing mode +.macro fp_decode_addr_reg + bfextu %d2{#13,#3},%d0 +.endm + +| decode the 8bit diplacement from the brief extension word +.macro fp_decode_disp8 + move.b %d2,%d0 + ext.w %d0 +.endm + +| decode the index of the brief/full extension word +.macro fp_decode_index + bfextu %d2{#17,#3},%d0 | get the register nr + btst #15,%d2 | test for data/addr register + jne 1\@f + printf PDECODE,"d%d",1,%d0 + jsr fp_get_data_reg + jra 2\@f +1\@: printf PDECODE,"a%d",1,%d0 + jsr fp_get_addr_reg + move.l %a0,%d0 +2\@: +debug lea "'l'.w,%a0" + btst #11,%d2 | 16/32 bit size? + jne 3\@f +debug lea "'w'.w,%a0" + ext.l %d0 +3\@: printf PDECODE,":%c",1,%a0 + move.w %d2,%d1 | scale factor + rol.w #7,%d1 + and.w #3,%d1 +debug move.l "%d1,-(%sp)" +debug ext.l "%d1" + printf PDECODE,":%d",1,%d1 +debug move.l "(%sp)+,%d1" + lsl.l %d1,%d0 +.endm + +| decode the base displacement size +.macro fp_decode_basedisp + bfextu %d2{#26,#2},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: +| .long "reserved","null displacement" +| .long "word displacement","long displacement" +.endm + +.macro fp_decode_outerdisp + bfextu %d2{#30,#2},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: +| .long "no memory indirect action/reserved","null outer displacement" +| .long "word outer displacement","long outer displacement" +.endm + +| get the extension word and test for brief or full extension type +.macro fp_get_test_extword label + fp_get_instr_word %d2,fp_err_ua1 + btst #8,%d2 + jne \label +.endm + + +| test if %pc is the base register for the indirect addr mode +.macro fp_test_basereg_d16 label + btst #20,%d2 + jeq \label +.endm + +| test if %pc is the base register for one of the extended modes +.macro fp_test_basereg_ext label + btst #19,%d2 + jeq \label +.endm + +.macro fp_test_suppr_index label + btst #6,%d2 + jne \label +.endm + + +| addressing mode: data register direct +.macro fp_mode_data_direct + fp_decode_addr_reg + printf PDECODE,"d%d",1,%d0 +.endm + +| addressing mode: address register indirect +.macro fp_mode_addr_indirect + fp_decode_addr_reg + printf PDECODE,"(a%d)",1,%d0 + jsr fp_get_addr_reg +.endm + +| adjust stack for byte moves from/to stack +.macro fp_test_sp_byte_move + .if !do_fmovem + .if do_fscc + move.w #6,%d1 + .endif + cmp.w #7,%d0 + jne 1\@f + .if !do_fscc + cmp.w #6,%d1 + jne 1\@f + .endif + move.w #4,%d1 +1\@: + .endif +.endm + +| addressing mode: address register indirect with postincrement +.macro fp_mode_addr_indirect_postinc + fp_decode_addr_reg + printf PDECODE,"(a%d)+",1,%d0 + fp_test_sp_byte_move + jsr fp_get_addr_reg + move.l %a0,%a1 | save addr + .if do_fmovem + lea (%a0,%d1.w*4),%a0 + .if !do_fmovem_cr + lea (%a0,%d1.w*8),%a0 + .endif + .else + add.w (fp_datasize,%d1.w*2),%a0 + .endif + jsr fp_put_addr_reg + move.l %a1,%a0 +.endm + +| addressing mode: address register indirect with predecrement +.macro fp_mode_addr_indirect_predec + fp_decode_addr_reg + printf PDECODE,"-(a%d)",1,%d0 + fp_test_sp_byte_move + jsr fp_get_addr_reg + .if do_fmovem + .if !do_fmovem_cr + lea (-12,%a0),%a1 | setup to addr of 1st reg to move + neg.w %d1 + lea (%a0,%d1.w*4),%a0 + add.w %d1,%d1 + lea (%a0,%d1.w*4),%a0 + jsr fp_put_addr_reg + move.l %a1,%a0 + .else + neg.w %d1 + lea (%a0,%d1.w*4),%a0 + jsr fp_put_addr_reg + .endif + .else + sub.w (fp_datasize,%d1.w*2),%a0 + jsr fp_put_addr_reg + .endif +.endm + +| addressing mode: address register/programm counter indirect +| with 16bit displacement +.macro fp_mode_addr_indirect_disp16 + .if !do_no_pc_mode + fp_test_basereg_d16 1f + printf PDECODE,"pc" + fp_get_pc %a0 + jra 2f + .endif +1: fp_decode_addr_reg + printf PDECODE,"a%d",1,%d0 + jsr fp_get_addr_reg +2: fp_get_instr_word %a1,fp_err_ua1 + printf PDECODE,"@(%x)",1,%a1 + add.l %a1,%a0 +.endm + +| perform preindex (if I/IS == 0xx and xx != 00) +.macro fp_do_preindex + moveq #3,%d0 + and.w %d2,%d0 + jeq 1f + btst #2,%d2 + jne 1f + printf PDECODE,")@(" + getuser.l (%a1),%a1,fp_err_ua1,%a1 +debug jra "2f" +1: printf PDECODE,"," +2: +.endm + +| perform postindex (if I/IS == 1xx) +.macro fp_do_postindex + btst #2,%d2 + jeq 1f + printf PDECODE,")@(" + getuser.l (%a1),%a1,fp_err_ua1,%a1 +debug jra "2f" +1: printf PDECODE,"," +2: +.endm + +| all other indirect addressing modes will finally end up here +.macro fp_mode_addr_indirect_extmode0 + .if !do_no_pc_mode + fp_test_basereg_ext 1f + printf PDECODE,"pc" + fp_get_pc %a0 + jra 2f + .endif +1: fp_decode_addr_reg + printf PDECODE,"a%d",1,%d0 + jsr fp_get_addr_reg +2: move.l %a0,%a1 + swap %d2 + fp_get_test_extword 3f + | addressing mode: address register/programm counter indirect + | with index and 8bit displacement + fp_decode_disp8 +debug ext.l "%d0" + printf PDECODE,"@(%x,",1,%d0 + add.w %d0,%a1 + fp_decode_index + add.l %d0,%a1 + printf PDECODE,")" + jra 9f +3: | addressing mode: address register/programm counter memory indirect + | with base and/or outer displacement + btst #7,%d2 | base register suppressed? + jeq 1f + printf PDECODE,"!" + sub.l %a1,%a1 +1: printf PDECODE,"@(" + fp_decode_basedisp + + .long fp_ill,1f + .long 2f,3f + +#ifdef FPU_EMU_DEBUG +1: printf PDECODE,"0" | null base displacement + jra 1f +#endif +2: fp_get_instr_word %a0,fp_err_ua1 | 16bit base displacement + printf PDECODE,"%x:w",1,%a0 + jra 4f +3: fp_get_instr_long %a0,fp_err_ua1 | 32bit base displacement + printf PDECODE,"%x:l",1,%a0 +4: add.l %a0,%a1 +1: + fp_do_postindex + fp_test_suppr_index 1f + fp_decode_index + add.l %d0,%a1 +1: fp_do_preindex + + fp_decode_outerdisp + + .long 5f,1f + .long 2f,3f + +#ifdef FPU_EMU_DEBUG +1: printf PDECODE,"0" | null outer displacement + jra 1f +#endif +2: fp_get_instr_word %a0,fp_err_ua1 | 16bit outer displacement + printf PDECODE,"%x:w",1,%a0 + jra 4f +3: fp_get_instr_long %a0,fp_err_ua1 | 32bit outer displacement + printf PDECODE,"%x:l",1,%a0 +4: add.l %a0,%a1 +1: +5: printf PDECODE,")" +9: move.l %a1,%a0 + swap %d2 +.endm + +| get the absolute short address from user space +.macro fp_mode_abs_short + fp_get_instr_word %a0,fp_err_ua1 + printf PDECODE,"%x.w",1,%a0 +.endm + +| get the absolute long address from user space +.macro fp_mode_abs_long + fp_get_instr_long %a0,fp_err_ua1 + printf PDECODE,"%x.l",1,%a0 +.endm + +#endif /* _FP_DECODE_H */ diff --git a/arch/m68k/math-emu/fp_emu.h b/arch/m68k/math-emu/fp_emu.h new file mode 100644 index 000000000..9344b939b --- /dev/null +++ b/arch/m68k/math-emu/fp_emu.h @@ -0,0 +1,137 @@ +/* + * fp_emu.h + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * 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 the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FP_EMU_H +#define _FP_EMU_H + +#ifndef __ASSEMBLY__ + +#include <asm/math-emu.h> + +#define IS_INF(a) ((a)->exp == 0x7fff) +#define IS_ZERO(a) ((a)->mant.m64 == 0) + + +#define fp_set_sr(bit) ({ \ + FPDATA->fpsr |= 1 << (bit); \ +}) + +#define fp_set_quotient(quotient) ({ \ + FPDATA->fpsr &= 0xff00ffff; \ + FPDATA->fpsr |= ((quotient) & 0xff) << 16; \ +}) + +/* linkage for several useful functions */ + +/* Normalize the extended struct, return 0 for a NaN */ +#define fp_normalize_ext(fpreg) ({ \ + register struct fp_ext *reg asm ("a0") = fpreg; \ + register int res asm ("d0"); \ + \ + asm volatile ("jsr fp_conv_ext2ext" \ + : "=d" (res) : "a" (reg) \ + : "a1", "d1", "d2", "memory"); \ + res; \ +}) + +#define fp_copy_ext(dest, src) ({ \ + *dest = *src; \ +}) + +#define fp_monadic_check(dest, src) ({ \ + fp_copy_ext(dest, src); \ + if (!fp_normalize_ext(dest)) \ + return dest; \ +}) + +#define fp_dyadic_check(dest, src) ({ \ + if (!fp_normalize_ext(dest)) \ + return dest; \ + if (!fp_normalize_ext(src)) { \ + fp_copy_ext(dest, src); \ + return dest; \ + } \ +}) + +extern const struct fp_ext fp_QNaN; +extern const struct fp_ext fp_Inf; + +#define fp_set_nan(dest) ({ \ + fp_set_sr(FPSR_EXC_OPERR); \ + *dest = fp_QNaN; \ +}) + +/* TODO check rounding mode? */ +#define fp_set_ovrflw(dest) ({ \ + fp_set_sr(FPSR_EXC_OVFL); \ + dest->exp = 0x7fff; \ + dest->mant.m64 = 0; \ +}) + +#define fp_conv_ext2long(src) ({ \ + register struct fp_ext *__src asm ("a0") = src; \ + register int __res asm ("d0"); \ + \ + asm volatile ("jsr fp_conv_ext2long" \ + : "=d" (__res) : "a" (__src) \ + : "a1", "d1", "d2", "memory"); \ + __res; \ +}) + +#else /* __ASSEMBLY__ */ + +#include "../kernel/m68k_defs.h" +#include <asm/math-emu.h> + +/* + * set, reset or clear a bit in the fp status register + */ +.macro fp_set_sr bit + bset #(\bit&7),(FPD_FPSR+3-(\bit/8),FPDATA) +.endm + +.macro fp_clr_sr bit + bclr #(\bit&7),(FPD_FPSR+3-(\bit/8),FPDATA) +.endm + +.macro fp_tst_sr bit + btst #(\bit&7),(FPD_FPSR+3-(\bit/8),FPDATA) +.endm + +#endif /* __ASSEMBLY__ */ + +#endif /* _FP_EMU_H */ diff --git a/arch/m68k/math-emu/fp_entry.S b/arch/m68k/math-emu/fp_entry.S new file mode 100644 index 000000000..f2e699c8c --- /dev/null +++ b/arch/m68k/math-emu/fp_entry.S @@ -0,0 +1,324 @@ +/* + * fp_emu.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * 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 the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/linkage.h> +#include <asm/entry.h> + +#include "fp_emu.h" + + .globl SYMBOL_NAME(fpu_emu) + .globl fp_debugprint + .globl fp_err_ua1,fp_err_ua2 + + .text +SYMBOL_NAME_LABEL(fpu_emu) + SAVE_ALL_INT + GET_CURRENT(%d0) + +#if defined(CPU_M68020_OR_M68030) && defined(CPU_M68040_OR_M68060) + tst.l SYMBOL_NAME(m68k_is040or060) + jeq 1f +#endif +#if defined(CPU_M68040_OR_M68060) + move.l (FPS_PC2,%sp),(FPS_PC,%sp) +#endif +1: + | emulate the instruction + jsr fp_scan + +#if defined(CONFIG_M68060) +#if !defined(CPU_M68060_ONLY) + btst #3,SYMBOL_NAME(m68k_cputype)+3 + jeq 1f +#endif + btst #7,(FPS_SR,%sp) + jne fp_sendtrace060 +#endif +1: + | emulation successful? + tst.l %d0 + jeq SYMBOL_NAME(ret_from_exception) + + | send some signal to program here + + jra SYMBOL_NAME(ret_from_exception) + + | we jump here after an access error while trying to access + | user space, we correct stackpointer and send a SIGSEGV to + | the user process +fp_err_ua2: + addq.l #4,%sp +fp_err_ua1: + addq.l #4,%sp + move.l %a0,-(%sp) + pea SEGV_MAPERR + pea SIGSEGV + jsr SYMBOL_NAME(fpemu_signal) + add.w #12,%sp + jra SYMBOL_NAME(ret_from_exception) + +#if defined(CONFIG_M68060) + | send a trace signal if we are debugged + | it does not really belong here, but... +fp_sendtrace060: + move.l (FPS_PC,%sp),-(%sp) + pea TRAP_TRACE + pea SIGTRAP + jsr SYMBOL_NAME(fpemu_signal) + add.w #12,%sp + jra SYMBOL_NAME(ret_from_exception) +#endif + + .globl fp_get_data_reg, fp_put_data_reg + .globl fp_get_addr_reg, fp_put_addr_reg + + | Entry points to get/put a register. Some of them can be get/put + | directly, others are on the stack, as we read/write the stack + | directly here, these function may only be called from within + | instruction decoding, otherwise the stack pointer is incorrect + | and the stack gets corrupted. +fp_get_data_reg: + jmp ([0f:w,%pc,%d0.w*4]) + + .align 4 +0: + .long fp_get_d0, fp_get_d1 + .long fp_get_d2, fp_get_d3 + .long fp_get_d4, fp_get_d5 + .long fp_get_d6, fp_get_d7 + +fp_get_d0: + move.l (PT_D0+8,%sp),%d0 + printf PREGISTER,"{d0->%08x}",1,%d0 + rts + +fp_get_d1: + move.l (PT_D1+8,%sp),%d0 + printf PREGISTER,"{d1->%08x}",1,%d0 + rts + +fp_get_d2: + move.l (PT_D2+8,%sp),%d0 + printf PREGISTER,"{d2->%08x}",1,%d0 + rts + +fp_get_d3: + move.l %d3,%d0 + printf PREGISTER,"{d3->%08x}",1,%d0 + rts + +fp_get_d4: + move.l %d4,%d0 + printf PREGISTER,"{d4->%08x}",1,%d0 + rts + +fp_get_d5: + move.l %d5,%d0 + printf PREGISTER,"{d5->%08x}",1,%d0 + rts + +fp_get_d6: + move.l %d6,%d0 + printf PREGISTER,"{d6->%08x}",1,%d0 + rts + +fp_get_d7: + move.l %d7,%d0 + printf PREGISTER,"{d7->%08x}",1,%d0 + rts + +fp_put_data_reg: + jmp ([0f:w,%pc,%d1.w*4]) + + .align 4 +0: + .long fp_put_d0, fp_put_d1 + .long fp_put_d2, fp_put_d3 + .long fp_put_d4, fp_put_d5 + .long fp_put_d6, fp_put_d7 + +fp_put_d0: + printf PREGISTER,"{d0<-%08x}",1,%d0 + move.l %d0,(PT_D0+8,%sp) + rts + +fp_put_d1: + printf PREGISTER,"{d1<-%08x}",1,%d0 + move.l %d0,(PT_D1+8,%sp) + rts + +fp_put_d2: + printf PREGISTER,"{d2<-%08x}",1,%d0 + move.l %d0,(PT_D2+8,%sp) + rts + +fp_put_d3: + printf PREGISTER,"{d3<-%08x}",1,%d0 +| move.l %d0,%d3 + move.l %d0,(PT_D3+8,%sp) + rts + +fp_put_d4: + printf PREGISTER,"{d4<-%08x}",1,%d0 +| move.l %d0,%d4 + move.l %d0,(PT_D4+8,%sp) + rts + +fp_put_d5: + printf PREGISTER,"{d5<-%08x}",1,%d0 +| move.l %d0,%d5 + move.l %d0,(PT_D5+8,%sp) + rts + +fp_put_d6: + printf PREGISTER,"{d6<-%08x}",1,%d0 + move.l %d0,%d6 + rts + +fp_put_d7: + printf PREGISTER,"{d7<-%08x}",1,%d0 + move.l %d0,%d7 + rts + +fp_get_addr_reg: + jmp ([0f:w,%pc,%d0.w*4]) + + .align 4 +0: + .long fp_get_a0, fp_get_a1 + .long fp_get_a2, fp_get_a3 + .long fp_get_a4, fp_get_a5 + .long fp_get_a6, fp_get_a7 + +fp_get_a0: + move.l (PT_A0+8,%sp),%a0 + printf PREGISTER,"{a0->%08x}",1,%a0 + rts + +fp_get_a1: + move.l (PT_A1+8,%sp),%a0 + printf PREGISTER,"{a1->%08x}",1,%a0 + rts + +fp_get_a2: + move.l (PT_A2+8,%sp),%a0 + printf PREGISTER,"{a2->%08x}",1,%a0 + rts + +fp_get_a3: + move.l %a3,%a0 + printf PREGISTER,"{a3->%08x}",1,%a0 + rts + +fp_get_a4: + move.l %a4,%a0 + printf PREGISTER,"{a4->%08x}",1,%a0 + rts + +fp_get_a5: + move.l %a5,%a0 + printf PREGISTER,"{a5->%08x}",1,%a0 + rts + +fp_get_a6: + move.l %a6,%a0 + printf PREGISTER,"{a6->%08x}",1,%a0 + rts + +fp_get_a7: + move.l %usp,%a0 + printf PREGISTER,"{a7->%08x}",1,%a0 + rts + +fp_put_addr_reg: + jmp ([0f:w,%pc,%d0.w*4]) + + .align 4 +0: + .long fp_put_a0, fp_put_a1 + .long fp_put_a2, fp_put_a3 + .long fp_put_a4, fp_put_a5 + .long fp_put_a6, fp_put_a7 + +fp_put_a0: + printf PREGISTER,"{a0<-%08x}",1,%a0 + move.l %a0,(PT_A0+8,%sp) + rts + +fp_put_a1: + printf PREGISTER,"{a1<-%08x}",1,%a0 + move.l %a0,(PT_A1+8,%sp) + rts + +fp_put_a2: + printf PREGISTER,"{a2<-%08x}",1,%a0 + move.l %a0,(PT_A2+8,%sp) + rts + +fp_put_a3: + printf PREGISTER,"{a3<-%08x}",1,%a0 + move.l %a0,%a3 + rts + +fp_put_a4: + printf PREGISTER,"{a4<-%08x}",1,%a0 + move.l %a0,%a4 + rts + +fp_put_a5: + printf PREGISTER,"{a5<-%08x}",1,%a0 + move.l %a0,%a5 + rts + +fp_put_a6: + printf PREGISTER,"{a6<-%08x}",1,%a0 + move.l %a0,%a6 + rts + +fp_put_a7: + printf PREGISTER,"{a7<-%08x}",1,%a0 + move.l %a0,%usp + rts + + .data + .align 4 + +fp_debugprint: +| .long PMDECODE + .long PMINSTR+PMDECODE+PMCONV+PMNORM +| .long PMCONV+PMNORM+PMINSTR +| .long 0 diff --git a/arch/m68k/math-emu/fp_log.c b/arch/m68k/math-emu/fp_log.c new file mode 100644 index 000000000..8e1c96420 --- /dev/null +++ b/arch/m68k/math-emu/fp_log.c @@ -0,0 +1,142 @@ +/* + + fp_trig.c: floating-point math routines for the Linux-m68k + floating point emulator. + + Copyright (c) 1998-1999 David Huggins-Daines / Roman Zippel. + + I hereby give permission, free of charge, to copy, modify, and + redistribute this software, in source or binary form, provided that + the above copyright notice and the following disclaimer are included + in all such copies. + + THIS SOFTWARE IS PROVIDED "AS IS", WITH ABSOLUTELY NO WARRANTY, REAL + OR IMPLIED. + +*/ + +#include "fp_emu.h" + +struct fp_ext * +fp_fsqrt(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsqrt\n"); + + fp_monadic_check(dest, src); + + if (IS_ZERO(dest)) + return dest; + + if (dest->sign) { + fp_set_nan(dest); + return dest; + } + if (IS_INF(dest)) + return dest; + + return dest; +} + +struct fp_ext * +fp_fetoxm1(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fetoxm1\n"); + + fp_monadic_check(dest, src); + + if (IS_ZERO(dest)) + return dest; + + return dest; +} + +struct fp_ext * +fp_fetox(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fetox\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_ftwotox(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("ftwotox\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_ftentox(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("ftentox\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_flogn(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("flogn\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_flognp1(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("flognp1\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_flog10(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("flog10\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_flog2(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("flog2\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fgetexp(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fgetexp\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fgetman(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fgetman\n"); + + fp_monadic_check(dest, src); + + return dest; +} + diff --git a/arch/m68k/math-emu/fp_move.S b/arch/m68k/math-emu/fp_move.S new file mode 100644 index 000000000..45fb02bfe --- /dev/null +++ b/arch/m68k/math-emu/fp_move.S @@ -0,0 +1,244 @@ +/* + * fp_move.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * 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 the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fp_emu.h" +#include "fp_decode.h" + +do_no_pc_mode=1 + + .globl fp_fmove_fp2mem + +fp_fmove_fp2mem: + clr.b (2+FPD_FPSR,FPDATA) + fp_decode_dest_format + move.w %d0,%d1 | store data size twice in %d1 + swap %d1 | one can be trashed below + move.w %d0,%d1 +#ifdef FPU_EMU_DEBUG + lea 0f,%a0 + clr.l %d0 + move.b (%a0,%d1.w),%d0 + printf PDECODE,"fmove.%c ",1,%d0 + fp_decode_src_reg + printf PDECODE,"fp%d,",1,%d0 + + .data +0: .byte 'l','s','x','p','w','d','b','p' + .previous +#endif + + | encode addressing mode for dest + fp_decode_addr_mode + + .long fp_data, fp_ill + .long fp_indirect, fp_postinc + .long fp_predecr, fp_disp16 + .long fp_extmode0, fp_extmode1 + + | addressing mode: data register direct +fp_data: + fp_mode_data_direct + move.w %d0,%d1 + fp_decode_src_reg + fp_get_fp_reg + lea (FPD_TEMPFP1,FPDATA),%a1 + move.l (%a0)+,(%a1)+ + move.l (%a0)+,(%a1)+ + move.l (%a0),(%a1) + lea (-8,%a1),%a0 + swap %d1 + move.l %d1,%d2 + printf PDECODE,"\n" + jmp ([0f:w,%pc,%d1.w*4]) + + .align 4 +0: + .long fp_data_long, fp_data_single + .long fp_ill, fp_ill + .long fp_data_word, fp_ill + .long fp_data_byte, fp_ill + +fp_data_byte: + jsr fp_normalize_ext + jsr fp_conv_ext2byte + move.l %d0,%d1 + swap %d2 + move.w %d2,%d0 + jsr fp_get_data_reg + move.b %d1,%d0 + move.w %d2,%d1 + jsr fp_put_data_reg + jra fp_final + +fp_data_word: + jsr fp_normalize_ext + jsr fp_conv_ext2short + move.l %d0,%d1 + swap %d2 + move.w %d2,%d0 + jsr fp_get_data_reg + move.w %d1,%d0 + move.l %d2,%d1 + jsr fp_put_data_reg + jra fp_final + +fp_data_long: + jsr fp_normalize_ext + jsr fp_conv_ext2long + swap %d2 + move.w %d2,%d1 + jsr fp_put_data_reg + jra fp_final + +fp_data_single: + jsr fp_normalize_ext + jsr fp_conv_ext2single + swap %d2 + move.w %d2,%d1 + jsr fp_put_data_reg + jra fp_final + + | addressing mode: address register indirect +fp_indirect: + fp_mode_addr_indirect + jra fp_putdest + + | addressing mode: address register indirect with postincrement +fp_postinc: + fp_mode_addr_indirect_postinc + jra fp_putdest + + | addressing mode: address register indirect with predecrement +fp_predecr: + fp_mode_addr_indirect_predec + jra fp_putdest + + | addressing mode: address register indirect with 16bit displacement +fp_disp16: + fp_mode_addr_indirect_disp16 + jra fp_putdest + +fp_extmode0: + fp_mode_addr_indirect_extmode0 + jra fp_putdest + +fp_extmode1: + fp_decode_addr_reg + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fp_abs_short, fp_abs_long + .long fp_ill, fp_ill + .long fp_ill, fp_ill + .long fp_ill, fp_ill + +fp_abs_short: + fp_mode_abs_short + jra fp_putdest + +fp_abs_long: + fp_mode_abs_long + jra fp_putdest + +fp_putdest: + move.l %a0,%a1 + fp_decode_src_reg + move.l %d1,%d2 | save size + fp_get_fp_reg + printf PDECODE,"\n" + addq.l #8,%a0 + move.l (%a0),-(%sp) + move.l -(%a0),-(%sp) + move.l -(%a0),-(%sp) + move.l %sp,%a0 + jsr fp_normalize_ext + + swap %d2 + jmp ([0f:w,%pc,%d2.w*4]) + + .align 4 +0: + .long fp_format_long, fp_format_single + .long fp_format_extended, fp_format_packed + .long fp_format_word, fp_format_double + .long fp_format_byte, fp_format_packed + +fp_format_long: + jsr fp_conv_ext2long + putuser.l %d0,(%a1),fp_err_ua1,%a1 + jra fp_finish_move + +fp_format_single: + jsr fp_conv_ext2single + putuser.l %d0,(%a1),fp_err_ua1,%a1 + jra fp_finish_move + +fp_format_extended: + move.l (%a0)+,%d0 + lsl.w #1,%d0 + lsl.l #7,%d0 + lsl.l #8,%d0 + putuser.l %d0,(%a1)+,fp_err_ua1,%a1 + move.l (%a0)+,%d0 + putuser.l %d0,(%a1)+,fp_err_ua1,%a1 + move.l (%a0),%d0 + putuser.l %d0,(%a1),fp_err_ua1,%a1 + jra fp_finish_move + +fp_format_packed: + /* not supported yet */ + lea (12,%sp),%sp + jra fp_ill + +fp_format_word: + jsr fp_conv_ext2short + putuser.w %d0,(%a1),fp_err_ua1,%a1 + jra fp_finish_move + +fp_format_double: + jsr fp_conv_ext2double + jra fp_finish_move + +fp_format_byte: + jsr fp_conv_ext2byte + putuser.b %d0,(%a1),fp_err_ua1,%a1 +| jra fp_finish_move + +fp_finish_move: + lea (12,%sp),%sp + jra fp_final diff --git a/arch/m68k/math-emu/fp_movem.S b/arch/m68k/math-emu/fp_movem.S new file mode 100644 index 000000000..01058b33b --- /dev/null +++ b/arch/m68k/math-emu/fp_movem.S @@ -0,0 +1,368 @@ +/* + * fp_movem.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * 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 the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fp_emu.h" +#include "fp_decode.h" + +| set flags for decode macros for fmovem +do_fmovem=1 + + .globl fp_fmovem_fp, fp_fmovem_cr + +| %d1 contains the mask and count of the register list +| for other register usage see fp_decode.h + +fp_fmovem_fp: + printf PDECODE,"fmovem.x " + | get register list and count them + btst #11,%d2 + jne 1f + bfextu %d2{#24,#8},%d0 | static register list + jra 2f +1: bfextu %d2{#25,#3},%d0 | dynamic register list + jsr fp_get_data_reg +2: move.l %d0,%d1 + swap %d1 + jra 2f +1: addq.w #1,%d1 | count the # of registers in +2: lsr.b #1,%d0 | register list and keep it in %d1 + jcs 1b + jne 2b + printf PDECODE,"#%08x",1,%d1 +#ifdef FPU_EMU_DEBUG + btst #12,%d2 + jne 1f + printf PDECODE,"-" | decremental move + jra 2f +1: printf PDECODE,"+" | incremental move +2: btst #13,%d2 + jeq 1f + printf PDECODE,"->" | fpu -> cpu + jra 2f +1: printf PDECODE,"<-" | fpu <- cpu +2: +#endif + + | decode address mode + fp_decode_addr_mode + + .long fp_ill, fp_ill + .long fpr_indirect, fpr_postinc + .long fpr_predecr, fpr_disp16 + .long fpr_extmode0, fpr_extmode1 + + | addressing mode: address register indirect +fpr_indirect: + fp_mode_addr_indirect + jra fpr_do_movem + + | addressing mode: address register indirect with postincrement +fpr_postinc: + fp_mode_addr_indirect_postinc + jra fpr_do_movem + +fpr_predecr: + fp_mode_addr_indirect_predec + jra fpr_do_movem + + | addressing mode: address register/programm counter indirect + | with 16bit displacement +fpr_disp16: + fp_mode_addr_indirect_disp16 + jra fpr_do_movem + +fpr_extmode0: + fp_mode_addr_indirect_extmode0 + jra fpr_do_movem + +fpr_extmode1: + fp_decode_addr_reg + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fpr_absolute_short, fpr_absolute_long + .long fpr_disp16, fpr_extmode0 + .long fp_ill, fp_ill + .long fp_ill, fp_ill + +fpr_absolute_short: + fp_mode_abs_short + jra fpr_do_movem + +fpr_absolute_long: + fp_mode_abs_long +| jra fpr_do_movem + +fpr_do_movem: + swap %d1 | get fpu register list + lea (FPD_FPREG,FPDATA),%a1 + moveq #12,%d0 + btst #12,%d2 + jne 1f + lea (-12,%a1,%d0*8),%a1 + neg.l %d0 +1: btst #13,%d2 + jne 4f + | move register from memory into fpu + jra 3f +1: printf PMOVEM,"(%p>%p)",2,%a0,%a1 + getuser.l (%a0)+,%d2,fp_err_ua1,%a0 + lsr.l #8,%d2 + lsr.l #7,%d2 + lsr.w #1,%d2 + move.l %d2,(%a1)+ + getuser.l (%a0)+,%d2,fp_err_ua1,%a0 + move.l %d2,(%a1)+ + getuser.l (%a0),%d2,fp_err_ua1,%a0 + move.l %d2,(%a1) + subq.l #8,%a0 + subq.l #8,%a1 + add.l %d0,%a0 +2: add.l %d0,%a1 +3: lsl.b #1,%d1 + jcs 1b + jne 2b + jra 5f + | move register from fpu into memory +1: printf PMOVEM,"(%p>%p)",2,%a1,%a0 + move.l (%a1)+,%d2 + lsl.w #1,%d2 + lsl.l #7,%d2 + lsl.l #8,%d2 + putuser.l %d2,(%a0)+,fp_err_ua1,%a0 + move.l (%a1)+,%d2 + putuser.l %d2,(%a0)+,fp_err_ua1,%a0 + move.l (%a1),%d2 + putuser.l %d2,(%a0),fp_err_ua1,%a0 + subq.l #8,%a1 + subq.l #8,%a0 + add.l %d0,%a0 +2: add.l %d0,%a1 +4: lsl.b #1,%d1 + jcs 1b + jne 2b +5: + printf PDECODE,"\n" +#if 0 + lea (FPD_FPREG,FPDATA),%a0 + printf PMOVEM,"fp:" + printx PMOVEM,%a0@(0) + printx PMOVEM,%a0@(12) + printf PMOVEM,"\n " + printx PMOVEM,%a0@(24) + printx PMOVEM,%a0@(36) + printf PMOVEM,"\n " + printx PMOVEM,%a0@(48) + printx PMOVEM,%a0@(60) + printf PMOVEM,"\n " + printx PMOVEM,%a0@(72) + printx PMOVEM,%a0@(84) + printf PMOVEM,"\n" +#endif + jra fp_end + +| set flags for decode macros for fmovem control register +do_fmovem=1 +do_fmovem_cr=1 + +fp_fmovem_cr: + printf PDECODE,"fmovem.cr " + | get register list and count them + bfextu %d2{#19,#3},%d0 + move.l %d0,%d1 + swap %d1 + jra 2f +1: addq.w #1,%d1 +2: lsr.l #1,%d0 + jcs 1b + jne 2b + printf PDECODE,"#%08x",1,%d1 +#ifdef FPU_EMU_DEBUG + btst #13,%d2 + jeq 1f + printf PDECODE,"->" | fpu -> cpu + jra 2f +1: printf PDECODE,"<-" | fpu <- cpu +2: +#endif + + | decode address mode + fp_decode_addr_mode + + .long fpc_data, fpc_addr + .long fpc_indirect, fpc_postinc + .long fpc_predecr, fpc_disp16 + .long fpc_extmode0, fpc_extmode1 + +fpc_data: + fp_mode_data_direct + move.w %d0,%d1 + bfffo %d2{#19,#3},%d0 + sub.w #19,%d0 + lea (FPD_FPCR,FPDATA,%d0.w*4),%a1 + btst #13,%d2 + jne 1f + move.w %d1,%d0 + jsr fp_get_data_reg + move.l %d0,(%a1) + jra fpc_movem_fin +1: move.l (%a1),%d0 + jsr fp_put_data_reg + jra fpc_movem_fin + +fpc_addr: + fp_decode_addr_reg + printf PDECODE,"a%d",1,%d0 + btst #13,%d2 + jne 1f + jsr fp_get_addr_reg + move.l %a0,(FPD_FPIAR,FPDATA) + jra fpc_movem_fin +1: move.l (FPD_FPIAR,FPDATA),%a0 + jsr fp_put_addr_reg + jra fpc_movem_fin + +fpc_indirect: + fp_mode_addr_indirect + jra fpc_do_movem + +fpc_postinc: + fp_mode_addr_indirect_postinc + jra fpc_do_movem + +fpc_predecr: + fp_mode_addr_indirect_predec + jra fpc_do_movem + +fpc_disp16: + fp_mode_addr_indirect_disp16 + jra fpc_do_movem + +fpc_extmode0: + fp_mode_addr_indirect_extmode0 + jra fpc_do_movem + +fpc_extmode1: + fp_decode_addr_reg + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fpc_absolute_short, fpc_absolute_long + .long fpc_disp16, fpc_extmode0 + .long fpc_immediate, fp_ill + .long fp_ill, fp_ill + +fpc_absolute_short: + fp_mode_abs_short + jra fpc_do_movem + +fpc_absolute_long: + fp_mode_abs_long + jra fpc_do_movem + +fpc_immediate: + fp_get_pc %a0 + lea (%a0,%d1.w*4),%a1 + fp_put_pc %a1 + printf PDECODE,"#imm" +| jra fpc_do_movem +#if 0 + swap %d1 + lsl.l #5,%d1 + lea (FPD_FPCR,FPDATA),%a0 + jra 3f +1: move.l %d0,(%a0) +2: addq.l #4,%a0 +3: lsl.b #1,%d1 + jcs 1b + jne 2b + jra fpc_movem_fin +#endif + +fpc_do_movem: + swap %d1 | get fpu register list + lsl.l #5,%d1 + lea (FPD_FPCR,FPDATA),%a1 +1: btst #13,%d2 + jne 4f + + | move register from memory into fpu + jra 3f +1: printf PMOVEM,"(%p>%p)",2,%a0,%a1 + getuser.l (%a0)+,%d0,fp_err_ua1,%a0 + move.l %d0,(%a1) +2: addq.l #4,%a1 +3: lsl.b #1,%d1 + jcs 1b + jne 2b + jra fpc_movem_fin + + | move register from fpu into memory +1: printf PMOVEM,"(%p>%p)",2,%a1,%a0 + move.l (%a1),%d0 + putuser.l %d0,(%a0)+,fp_err_ua1,%a0 +2: addq.l #4,%a1 +4: lsl.b #1,%d1 + jcs 1b + jne 2b + +fpc_movem_fin: + and.l #0x0000fff0,(FPD_FPCR,FPDATA) + and.l #0x0ffffff8,(FPD_FPSR,FPDATA) + move.l (FPD_FPCR,FPDATA),%d0 + lsr.l #4,%d0 + moveq #3,%d1 + and.l %d0,%d1 + move.w %d1,(FPD_RND,FPDATA) + lsr.l #2,%d0 + moveq #3,%d1 + and.l %d0,%d1 + move.w %d1,(FPD_PREC,FPDATA) + printf PDECODE,"\n" +#if 0 + printf PMOVEM,"fpcr : %08x\n",1,FPDATA@(FPD_FPCR) + printf PMOVEM,"fpsr : %08x\n",1,FPDATA@(FPD_FPSR) + printf PMOVEM,"fpiar: %08x\n",1,FPDATA@(FPD_FPIAR) + clr.l %d0 + move.w (FPD_PREC,FPDATA),%d0 + printf PMOVEM,"prec : %04x\n",1,%d0 + move.w (FPD_RND,FPDATA),%d0 + printf PMOVEM,"rnd : %04x\n",1,%d0 +#endif + jra fp_end diff --git a/arch/m68k/math-emu/fp_scan.S b/arch/m68k/math-emu/fp_scan.S new file mode 100644 index 000000000..4f404914c --- /dev/null +++ b/arch/m68k/math-emu/fp_scan.S @@ -0,0 +1,478 @@ +/* + * fp_scan.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * 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 the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fp_emu.h" +#include "fp_decode.h" + + .globl fp_scan, fp_datasize + + .data + +| %d2 - first two instr words +| %d1 - operand size + +/* operand formats are: + + Long = 0, i.e. fmove.l + Single, i.e. fmove.s + Extended, i.e. fmove.x + Packed-BCD, i.e. fmove.p + Word, i.e. fmove.w + Double, i.e. fmove.d +*/ + + .text + +| On entry: +| FPDATA - base of emulated FPU registers + +fp_scan: +| normal fpu instruction? (this excludes fsave/frestore) + fp_get_pc %a0 + printf PDECODE,"%08x: ",1,%a0 + getuser.b (%a0),%d0,fp_err_ua1,%a0 +#if 1 + cmp.b #0xf2,%d0 | cpid = 1 +#else + cmp.b #0xfc,%d0 | cpid = 6 +#endif + jne fp_nonstd +| first two instruction words are kept in %d2 + getuser.l (%a0)+,%d2,fp_err_ua1,%a0 + fp_put_pc %a0 +fp_decode_cond: | seperate conditional instr + fp_decode_cond_instr_type + + .long fp_decode_move, fp_fscc + .long fp_fbccw, fp_fbccl + +fp_decode_move: | seperate move instr + fp_decode_move_instr_type + + .long fp_fgen_fp, fp_ill + .long fp_fgen_ea, fp_fmove_fp2mem + .long fp_fmovem_cr, fp_fmovem_cr + .long fp_fmovem_fp, fp_fmovem_fp + +| now all arithmetic instr and a few move instr are left +fp_fgen_fp: | source is a fpu register + clr.b (FPD_FPSR+2,FPDATA) | clear the exception byte + fp_decode_sourcespec + printf PDECODE,"f<op>.x fp%d",1,%d0 + fp_get_fp_reg + lea (FPD_TEMPFP1,FPDATA),%a1 | copy src into a temp location + move.l (%a0)+,(%a1)+ + move.l (%a0)+,(%a1)+ + move.l (%a0),(%a1) + lea (-8,%a1),%a0 + jra fp_getdest + +fp_fgen_ea: | source is <ea> + clr.b (FPD_FPSR+2,FPDATA) | clear the exception byte + | sort out fmovecr, keep data size in %d1 + fp_decode_sourcespec + cmp.w #7,%d0 + jeq fp_fmovecr + move.w %d0,%d1 | store data size twice in %d1 + swap %d1 | one can be trashed below + move.w %d0,%d1 +#ifdef FPU_EMU_DEBUG + lea 0f,%a0 + clr.l %d0 + move.b (%a0,%d1.w),%d0 + printf PDECODE,"f<op>.%c ",1,%d0 + + .data +0: .byte 'l','s','x','p','w','d','b',0 + .previous +#endif + +/* + fp_getsource, fp_getdest + + basically, we end up with a pointer to the source operand in + %a1, and a pointer to the destination operand in %a0. both + are, of course, 96-bit extended floating point numbers. +*/ + +fp_getsource: + | decode addressing mode for source + fp_decode_addr_mode + + .long fp_data, fp_ill + .long fp_indirect, fp_postinc + .long fp_predecr, fp_disp16 + .long fp_extmode0, fp_extmode1 + + | addressing mode: data register direct +fp_data: + fp_mode_data_direct + jsr fp_get_data_reg + lea (FPD_TEMPFP1,FPDATA),%a0 + jmp ([0f:w,%pc,%d1.w*4]) + + .align 4 +0: + .long fp_data_long, fp_data_single + .long fp_ill, fp_ill + .long fp_data_word, fp_ill + .long fp_data_byte, fp_ill + + | data types that fit in an integer data register +fp_data_byte: + extb.l %d0 + jra fp_data_long + +fp_data_word: + ext.l %d0 + +fp_data_long: + jsr fp_conv_long2ext + jra fp_getdest + +fp_data_single: + jsr fp_conv_single2ext + jra fp_getdest + + | addressing mode: address register indirect +fp_indirect: + fp_mode_addr_indirect + jra fp_fetchsource + + | addressing mode: address register indirect with postincrement +fp_postinc: + fp_mode_addr_indirect_postinc + jra fp_fetchsource + + | addressing mode: address register indirect with predecrement +fp_predecr: + fp_mode_addr_indirect_predec + jra fp_fetchsource + + | addressing mode: address register/programm counter indirect + | with 16bit displacement +fp_disp16: + fp_mode_addr_indirect_disp16 + jra fp_fetchsource + + | all other indirect addressing modes will finally end up here +fp_extmode0: + fp_mode_addr_indirect_extmode0 + jra fp_fetchsource + +| all pc relative addressing modes and immediate/absolute modes end up here +| the first ones are sent to fp_extmode0 or fp_disp16 +| and only the latter are handled here +fp_extmode1: + fp_decode_addr_reg + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fp_abs_short, fp_abs_long + .long fp_disp16, fp_extmode0 + .long fp_immediate, fp_ill + .long fp_ill, fp_ill + + | addressing mode: absolute short +fp_abs_short: + fp_mode_abs_short + jra fp_fetchsource + + | addressing mode: absolute long +fp_abs_long: + fp_mode_abs_long + jra fp_fetchsource + + | addressing mode: immediate data +fp_immediate: + printf PDECODE,"#" + fp_get_pc %a0 + move.w (fp_datasize,%d1.w*2),%d0 + addq.w #1,%d0 + and.w #-2,%d0 +#ifdef FPU_EMU_DEBUG + movem.l %d0/%d1,-(%sp) + movel %a0,%a1 + clr.l %d1 + jra 2f +1: getuser.b (%a1)+,%d1,fp_err_ua1,%a1 + printf PDECODE,"%02x",1,%d1 +2: dbra %d0,1b + movem.l (%sp)+,%d0/%d1 +#endif + lea (%a0,%d0.w),%a1 + fp_put_pc %a1 +| jra fp_fetchsource + +fp_fetchsource: + move.l %a0,%a1 + swap %d1 + lea (FPD_TEMPFP1,FPDATA),%a0 + jmp ([0f:w,%pc,%d1.w*4]) + + .align 4 +0: .long fp_long, fp_single + .long fp_ext, fp_pack + .long fp_word, fp_double + .long fp_byte, fp_ill + +fp_long: + getuser.l (%a1),%d0,fp_err_ua1,%a1 + jsr fp_conv_long2ext + jra fp_getdest + +fp_single: + getuser.l (%a1),%d0,fp_err_ua1,%a1 + jsr fp_conv_single2ext + jra fp_getdest + +fp_ext: + getuser.l (%a1)+,%d0,fp_err_ua1,%a1 + lsr.l #8,%d0 + lsr.l #7,%d0 + lsr.w #1,%d0 + move.l %d0,(%a0)+ + getuser.l (%a1)+,%d0,fp_err_ua1,%a1 + move.l %d0,(%a0)+ + getuser.l (%a1),%d0,fp_err_ua1,%a1 + move.l %d0,(%a0) + subq.l #8,%a0 + jra fp_getdest + +fp_pack: + /* not supported yet */ + jra fp_ill + +fp_word: + getuser.w (%a1),%d0,fp_err_ua1,%a1 + ext.l %d0 + jsr fp_conv_long2ext + jra fp_getdest + +fp_double: + jsr fp_conv_double2ext + jra fp_getdest + +fp_byte: + getuser.b (%a1),%d0,fp_err_ua1,%a1 + extb.l %d0 + jsr fp_conv_long2ext +| jra fp_getdest + +fp_getdest: + move.l %a0,%a1 + bfextu %d2{#22,#3},%d0 + printf PDECODE,",fp%d\n",1,%d0 + fp_get_fp_reg + movem.l %a0/%a1,-(%sp) + pea fp_finalrounding + bfextu %d2{#25,#7},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fp_fmove_mem2fp, fp_fint, fp_fsinh, fp_fintrz + .long fp_fsqrt, fp_ill, fp_flognp1, fp_ill + .long fp_fetoxm1, fp_ftanh, fp_fatan, fp_ill + .long fp_fasin, fp_fatanh, fp_fsin, fp_ftan + .long fp_fetox, fp_ftwotox, fp_ftentox, fp_ill + .long fp_flogn, fp_flog10, fp_flog2, fp_ill + .long fp_fabs, fp_fcosh, fp_fneg, fp_ill + .long fp_facos, fp_fcos, fp_fgetexp, fp_fgetman + .long fp_fdiv, fp_fmod, fp_fadd, fp_fmul + .long fpa_fsgldiv, fp_frem, fp_fscale, fpa_fsglmul + .long fp_fsub, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_fsincos0, fp_fsincos1, fp_fsincos2, fp_fsincos3 + .long fp_fsincos4, fp_fsincos5, fp_fsincos6, fp_fsincos7 + .long fp_fcmp, fp_ill, fp_ftst, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_fsmove, fp_fssqrt, fp_ill, fp_ill + .long fp_fdmove, fp_fdsqrt, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_fsabs, fp_ill, fp_fsneg, fp_ill + .long fp_fdabs, fp_ill, fp_fdneg, fp_ill + .long fp_fsdiv, fp_ill, fp_fsadd, fp_fsmul + .long fp_fddiv, fp_ill, fp_fdadd, fp_fdmul + .long fp_fssub, fp_ill, fp_ill, fp_ill + .long fp_fdsub, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + + | Instructions follow + + | Move an (emulated) ROM constant +fp_fmovecr: + bfextu %d2{#27,#5},%d0 + printf PINSTR,"fp_fmovecr #%d",1,%d0 + move.l %d0,%d1 + add.l %d0,%d0 + add.l %d1,%d0 + lea (fp_constants,%d0*4),%a0 + move.l #0x801cc0ff,%d0 + addq.l #1,%d1 + lsl.l %d1,%d0 + jcc 1f + fp_set_sr FPSR_EXC_INEX2 | INEX2 exception +1: moveq #-128,%d0 | continue with fmove + and.l %d0,%d2 + jra fp_getdest + + .data + .align 4 +fp_constants: + .long 0x00004000,0xc90fdaa2,0x2168c235 | pi + .extend 0,0,0,0,0,0,0,0,0,0 + .long 0x00003ffd,0x9a209a84,0xfbcff798 | log10(2) + .long 0x00004000,0xadf85458,0xa2bb4a9a | e + .long 0x00003fff,0xb8aa3b29,0x5c17f0bc | log2(e) + .long 0x00003ffd,0xde5bd8a9,0x37287195 | log10(e) + .long 0x00000000,0x00000000,0x00000000 | 0.0 + .long 0x00003ffe,0xb17217f7,0xd1cf79ac | 1n(2) + .long 0x00004000,0x935d8ddd,0xaaa8ac17 | 1n(10) + | read this as "1.0 * 2^0" - note the high bit in the mantissa + .long 0x00003fff,0x80000000,0x00000000 | 10^0 + .long 0x00004002,0xa0000000,0x00000000 | 10^1 + .long 0x00004005,0xc8000000,0x00000000 | 10^2 + .long 0x0000400c,0x9c400000,0x00000000 | 10^4 + .long 0x00004019,0xbebc2000,0x00000000 | 10^8 + .long 0x00004034,0x8e1bc9bf,0x04000000 | 10^16 + .long 0x00004069,0x9dc5ada8,0x2b70b59e | 10^32 + .long 0x000040d3,0xc2781f49,0xffcfa6d5 | 10^64 + .long 0x000041a8,0x93ba47c9,0x80e98ce0 | 10^128 + .long 0x00004351,0xaa7eebfb,0x9df9de8e | 10^256 + .long 0x000046a3,0xe319a0ae,0xa60e91c7 | 10^512 + .long 0x00004d48,0xc9767586,0x81750c17 | 10^1024 + .long 0x00005a92,0x9e8b3b5d,0xc53d5de5 | 10^2048 + .long 0x00007525,0xc4605202,0x8a20979b | 10^4096 + .previous + +fp_fmove_mem2fp: + printf PINSTR,"fmove %p,%p\n",2,%a0,%a1 + move.l (%a1)+,(%a0)+ + move.l (%a1)+,(%a0)+ + move.l (%a1),(%a0) + subq.l #8,%a0 + rts + +fpa_fsglmul: + move.l #fp_finalrounding_single_fast,(%sp) + jra fp_fsglmul + +fpa_fsgldiv: + move.l #fp_finalrounding_single_fast,(%sp) + jra fp_fsgldiv + +.macro fp_dosingleprec instr + printf PINSTR,"single " + move.l #fp_finalrounding_single,(%sp) + jra \instr +.endm + +.macro fp_dodoubleprec instr + printf PINSTR,"double " + move.l #fp_finalrounding_double,(%sp) + jra \instr +.endm + +fp_fsmove: + fp_dosingleprec fp_fmove_mem2fp + +fp_fssqrt: + fp_dosingleprec fp_fsqrt + +fp_fdmove: + fp_dodoubleprec fp_fmove_mem2fp + +fp_fdsqrt: + fp_dodoubleprec fp_fsqrt + +fp_fsabs: + fp_dosingleprec fp_fabs + +fp_fsneg: + fp_dosingleprec fp_fneg + +fp_fdabs: + fp_dodoubleprec fp_fabs + +fp_fdneg: + fp_dodoubleprec fp_fneg + +fp_fsdiv: + fp_dosingleprec fp_fdiv + +fp_fsadd: + fp_dosingleprec fp_fadd + +fp_fsmul: + fp_dosingleprec fp_fmul + +fp_fddiv: + fp_dodoubleprec fp_fdiv + +fp_fdadd: + fp_dodoubleprec fp_fadd + +fp_fdmul: + fp_dodoubleprec fp_fmul + +fp_fssub: + fp_dosingleprec fp_fsub + +fp_fdsub: + fp_dodoubleprec fp_fsub + +fp_nonstd: + fp_get_pc %a0 + getuser.l (%a0),%d0,fp_err_ua1,%a0 + printf ,"nonstd ((%08x)=%08x)\n",2,%a0,%d0 + moveq #-1,%d0 + rts + + .data + .align 4 + + | data sizes corresponding to the operand formats +fp_datasize: + .word 4, 4, 12, 12, 2, 8, 1, 0 diff --git a/arch/m68k/math-emu/fp_trig.c b/arch/m68k/math-emu/fp_trig.c new file mode 100644 index 000000000..6361d0784 --- /dev/null +++ b/arch/m68k/math-emu/fp_trig.c @@ -0,0 +1,183 @@ +/* + + fp_trig.c: floating-point math routines for the Linux-m68k + floating point emulator. + + Copyright (c) 1998-1999 David Huggins-Daines / Roman Zippel. + + I hereby give permission, free of charge, to copy, modify, and + redistribute this software, in source or binary form, provided that + the above copyright notice and the following disclaimer are included + in all such copies. + + THIS SOFTWARE IS PROVIDED "AS IS", WITH ABSOLUTELY NO WARRANTY, REAL + OR IMPLIED. + +*/ + +#include "fp_emu.h" +#include "fp_trig.h" + +struct fp_ext * +fp_fsin(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsin\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fcos(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fcos\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_ftan(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("ftan\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fasin(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fasin\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_facos(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("facos\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fatan(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fatan\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fsinh(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsinh\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fcosh(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fcosh\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_ftanh(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("ftanh\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fatanh(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fatanh\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fsincos0(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos0\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos1(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos1\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos2(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos2\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos3(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos3\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos4(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos4\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos5(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos5\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos6(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos6\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos7(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos7\n"); + + return dest; +} diff --git a/arch/m68k/math-emu/fp_trig.h b/arch/m68k/math-emu/fp_trig.h new file mode 100644 index 000000000..af8b247e9 --- /dev/null +++ b/arch/m68k/math-emu/fp_trig.h @@ -0,0 +1,32 @@ +/* + + fp_trig.h: floating-point math routines for the Linux-m68k + floating point emulator. + + Copyright (c) 1998 David Huggins-Daines. + + I hereby give permission, free of charge, to copy, modify, and + redistribute this software, in source or binary form, provided that + the above copyright notice and the following disclaimer are included + in all such copies. + + THIS SOFTWARE IS PROVIDED "AS IS", WITH ABSOLUTELY NO WARRANTY, REAL + OR IMPLIED. + +*/ + +#ifndef FP_TRIG_H +#define FP_TRIG_H + +#include "fp_emu.h" + +/* floating point trigonometric instructions: + + the arguments to these are in the "internal" extended format, that + is, an "exploded" version of the 96-bit extended fp format used by + the 68881. + + they return a status code, which should end up in %d0, if all goes + well. */ + +#endif /* FP_TRIG__H */ diff --git a/arch/m68k/math-emu/fp_util.S b/arch/m68k/math-emu/fp_util.S new file mode 100644 index 000000000..7356a4a55 --- /dev/null +++ b/arch/m68k/math-emu/fp_util.S @@ -0,0 +1,1454 @@ +/* + * fp_util.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * 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 the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fp_emu.h" + +/* + * Here are lots of conversion and normalization functions mainly + * used by fp_scan.S + * Note that these functions are optimized for "normal" numbers, + * these are handled first and exit as fast as possible, this is + * especially important for fp_normalize_ext/fp_conv_ext2ext, as + * it's called very often. + * The register usage is optimized for fp_scan.S and which register + * is currently at that time unused, be careful if you want change + * something here. %d0 and %d1 is always usable, sometimes %d2 (or + * only the lower half) most function have to return the %a0 + * unmodified, so that the caller can immediatly reuse it. + */ + + .globl fp_ill, fp_end + + | exits from fp_scan: + | illegal instruction +fp_ill: + printf ,"fp_illegal\n" + rts + | completed instruction +fp_end: + tst.l (TASK_MM-8,%a2) + jmi 1f + tst.l (TASK_MM-4,%a2) + jmi 1f + tst.l (TASK_MM,%a2) + jpl 2f +1: printf ,"oops:%p,%p,%p\n",3,%a2@(TASK_MM-8),%a2@(TASK_MM-4),%a2@(TASK_MM) +2: clr.l %d0 + rts + + .globl fp_conv_long2ext, fp_conv_single2ext + .globl fp_conv_double2ext, fp_conv_ext2ext + .globl fp_normalize_ext, fp_normalize_double + .globl fp_normalize_single, fp_normalize_single_fast + .globl fp_conv_ext2double, fp_conv_ext2single + .globl fp_conv_ext2long, fp_conv_ext2short + .globl fp_conv_ext2byte + .globl fp_finalrounding_single, fp_finalrounding_single_fast + .globl fp_finalrounding_double + .globl fp_finalrounding, fp_finaltest, fp_final + +/* + * First several conversion functions from a source operand + * into the extended format. Note, that only fp_conv_ext2ext + * normalizes the number and is always called after the other + * conversion functions, which only move the information into + * fp_ext structure. + */ + + | fp_conv_long2ext: + | + | args: %d0 = source (32-bit long) + | %a0 = destination (ptr to struct fp_ext) + +fp_conv_long2ext: + printf PCONV,"l2e: %p -> %p(",2,%d0,%a0 + clr.l %d1 | sign defaults to zero + tst.l %d0 + jeq fp_l2e_zero | is source zero? + jpl 1f | positive? + moveq #1,%d1 + neg.l %d0 +1: swap %d1 + move.w #0x3fff+31,%d1 + move.l %d1,(%a0)+ | set sign / exp + move.l %d0,(%a0)+ | set mantissa + clr.l (%a0) + subq.l #8,%a0 | restore %a0 + printx PCONV,%a0@ + printf PCONV,")\n" + rts + | source is zero +fp_l2e_zero: + clr.l (%a0)+ + clr.l (%a0)+ + clr.l (%a0) + subq.l #8,%a0 + printx PCONV,%a0@ + printf PCONV,")\n" + rts + + | fp_conv_single2ext + | args: %d0 = source (single-precision fp value) + | %a0 = dest (struct fp_ext *) + +fp_conv_single2ext: + printf PCONV,"s2e: %p -> %p(",2,%d0,%a0 + move.l %d0,%d1 + lsl.l #8,%d0 | shift mantissa + lsr.l #8,%d1 | exponent / sign + lsr.l #7,%d1 + lsr.w #8,%d1 + jeq fp_s2e_small | zero / denormal? + cmp.w #0xff,%d1 | NaN / Inf? + jeq fp_s2e_large + bset #31,%d0 | set explizit bit + add.w #0x3fff-0x7f,%d1 | re-bias the exponent. +9: move.l %d1,(%a0)+ | fp_ext.sign, fp_ext.exp + move.l %d0,(%a0)+ | high lword of fp_ext.mant + clr.l (%a0) | low lword = 0 + subq.l #8,%a0 + printx PCONV,%a0@ + printf PCONV,")\n" + rts + | zeros and denormalized +fp_s2e_small: + | exponent is zero, so explizit bit is already zero too + tst.l %d0 + jeq 9b + move.w #0x4000-0x7f,%d1 + jra 9b + | infinities and NAN +fp_s2e_large: + bclr #31,%d0 | clear explizit bit + move.w #0x7fff,%d1 + jra 9b + +fp_conv_double2ext: +#ifdef FPU_EMU_DEBUG + getuser.l %a1@(0),%d0,fp_err_ua2,%a1 + getuser.l %a1@(4),%d1,fp_err_ua2,%a1 + printf PCONV,"d2e: %p%p -> %p(",3,%d0,%d1,%a0 +#endif + getuser.l (%a1)+,%d0,fp_err_ua2,%a1 + move.l %d0,%d1 + lsl.l #8,%d0 | shift high mantissa + lsl.l #3,%d0 + lsr.l #8,%d1 | exponent / sign + lsr.l #7,%d1 + lsr.w #5,%d1 + jeq fp_d2e_small | zero / denormal? + cmp.w #0x7ff,%d1 | NaN / Inf? + jeq fp_d2e_large + bset #31,%d0 | set explizit bit + add.w #0x3fff-0x3ff,%d1 | re-bias the exponent. +9: move.l %d1,(%a0)+ | fp_ext.sign, fp_ext.exp + move.l %d0,(%a0)+ + getuser.l (%a1)+,%d0,fp_err_ua2,%a1 + move.l %d0,%d1 + lsl.l #8,%d0 + lsl.l #3,%d0 + move.l %d0,(%a0) + moveq #21,%d0 + lsr.l %d0,%d1 + or.l %d1,-(%a0) + subq.l #4,%a0 + printx PCONV,%a0@ + printf PCONV,")\n" + rts + | zeros and denormalized +fp_d2e_small: + | exponent is zero, so explizit bit is already zero too + tst.l %d0 + jeq 9b + move.w #0x4000-0x3ff,%d1 + jra 9b + | infinities and NAN +fp_d2e_large: + bclr #31,%d0 | clear explizit bit + move.w #0x7fff,%d1 + jra 9b + + | fp_conv_ext2ext: + | originally used to get longdouble from userspace, now it's + | called before arithmetic operations to make sure the number + | is normalized [maybe rename it?]. + | args: %a0 = dest (struct fp_ext *) + | returns 0 in %d0 for a NaN, otherwise 1 + +fp_conv_ext2ext: + printf PCONV,"e2e: %p(",1,%a0 + printx PCONV,%a0@ + printf PCONV,"), " + move.l (%a0)+,%d0 + cmp.w #0x7fff,%d0 | Inf / NaN? + jeq fp_e2e_large + move.l (%a0),%d0 + jpl fp_e2e_small | zero / denorm? + | The high bit is set, so normalization is irrelevant. +fp_e2e_checkround: + subq.l #4,%a0 +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC + move.b (%a0),%d0 + jne fp_e2e_round +#endif + printf PCONV,"%p(",1,%a0 + printx PCONV,%a0@ + printf PCONV,")\n" + moveq #1,%d0 + rts +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC +fp_e2e_round: + fp_set_sr FPSR_EXC_INEX2 + clr.b (%a0) + move.w (FPD_RND,FPDATA),%d2 + jne fp_e2e_roundother | %d2 == 0, round to nearest + tst.b %d0 | test guard bit + jpl 9f | zero is closer + btst #0,(11,%a0) | test lsb bit + jne fp_e2e_doroundup | round to infinity + lsl.b #1,%d0 | check low bits + jeq 9f | round to zero +fp_e2e_doroundup: + addq.l #1,(8,%a0) + jcc 9f + addq.l #1,(4,%a0) + jcc 9f + move.w #0x8000,(4,%a0) + addq.w #1,(2,%a0) +9: printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +fp_e2e_roundother: + subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 1f | %d2 > 2, round to +infinity + tst.b (1,%a0) | to -inf + jne fp_e2e_doroundup | negative, round to infinity + jra 9b | positive, round to zero +1: tst.b (1,%a0) | to +inf + jeq fp_e2e_doroundup | positive, round to infinity + jra 9b | negative, round to zero +#endif + | zeros and subnormals: + | try to normalize these anyway. +fp_e2e_small: + jne fp_e2e_small1 | high lword zero? + move.l (4,%a0),%d0 + jne fp_e2e_small2 +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC + clr.l %d0 + move.b (-4,%a0),%d0 + jne fp_e2e_small3 +#endif + | Genuine zero. + clr.w -(%a0) + subq.l #2,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + moveq #1,%d0 + rts + | definitely subnormal, need to shift all 64 bits +fp_e2e_small1: + bfffo %d0{#0,#32},%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 +1: move.w %d2,(%a0)+ + move.w %d1,%d2 + jeq fp_e2e_checkround + | fancy 64-bit double-shift begins here + lsl.l %d2,%d0 + move.l %d0,(%a0)+ + move.l (%a0),%d0 + move.l %d0,%d1 + lsl.l %d2,%d0 + move.l %d0,(%a0) + neg.w %d2 + and.w #0x1f,%d2 + lsr.l %d2,%d1 + or.l %d1,-(%a0) +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC +fp_e2e_extra1: + clr.l %d0 + move.b (-4,%a0),%d0 + neg.w %d2 + add.w #24,%d2 + jcc 1f + clr.b (-4,%a0) + lsl.l %d2,%d0 + or.l %d0,(4,%a0) + jra fp_e2e_checkround +1: addq.w #8,%d2 + lsl.l %d2,%d0 + move.b %d0,(-4,%a0) + lsr.l #8,%d0 + or.l %d0,(4,%a0) +#endif + jra fp_e2e_checkround + | pathologically small subnormal +fp_e2e_small2: + bfffo %d0{#0,#32},%d1 + add.w #32,%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Beyond pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 +1: move.w %d2,(%a0)+ + ext.l %d1 + jeq fp_e2e_checkround + clr.l (4,%a0) + sub.w #32,%d2 + jcs 1f + lsl.l %d1,%d0 | lower lword needs only to be shifted + move.l %d0,(%a0) | into the higher lword +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC + clr.l %d0 + move.b (-4,%a0),%d0 + clr.b (-4,%a0) + neg.w %d1 + add.w #32,%d1 + bfins %d0,(%a0){%d1,#8} +#endif + jra fp_e2e_checkround +1: neg.w %d1 | lower lword is splitted between + bfins %d0,(%a0){%d1,#32} | higher and lower lword +#ifndef CONFIG_M68KFPU_EMU_EXTRAPREC + jra fp_e2e_checkround +#else + move.w %d1,%d2 + jra fp_e2e_extra1 + | These are extremely small numbers, that will mostly end up as zero + | anyway, so this is only important for correct rounding. +fp_e2e_small3: + bfffo %d0{#24,#8},%d1 + add.w #40,%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 +1: move.w %d2,(%a0)+ + ext.l %d1 + jeq fp_e2e_checkround + cmp.w #8,%d1 + jcs 2f +1: clr.b (-4,%a0) + sub.w #64,%d1 + jcs 1f + add.w #24,%d1 + lsl.l %d1,%d0 + move.l %d0,(%a0) + jra fp_e2e_checkround +1: neg.w %d1 + bfins %d0,(%a0){%d1,#8} + jra fp_e2e_checkround +2: lsl.l %d1,%d0 + move.b %d0,(-4,%a0) + lsr.l #8,%d0 + move.b %d0,(7,%a0) + jra fp_e2e_checkround +#endif +1: move.l %d0,%d1 | lower lword is splitted between + lsl.l %d2,%d0 | higher and lower lword + move.l %d0,(%a0) + move.l %d1,%d0 + neg.w %d2 + add.w #32,%d2 + lsr.l %d2,%d0 + move.l %d0,-(%a0) + jra fp_e2e_checkround + | Infinities and NaNs +fp_e2e_large: + move.l (%a0)+,%d0 + jne 3f +1: tst.l (%a0) + jne 4f + moveq #1,%d0 +2: subq.l #8,%a0 + printf PCONV,"%p(",1,%a0 + printx PCONV,%a0@ + printf PCONV,")\n" + rts + | we have maybe a NaN, shift off the highest bit +3: lsl.l #1,%d0 + jeq 1b + | we have a NaN, clear the return value +4: clrl %d0 + jra 2b + + +/* + * Normalization functions. Call these on the output of general + * FP operators, and before any conversion into the destination + * formats. fp_normalize_ext has always to be called first, the + * following conversion functions expect an already normalized + * number. + */ + + | fp_normalize_ext: + | normalize an extended in extended (unpacked) format, basically + | it does the same as fp_conv_ext2ext, additionally it also does + | the necessary postprocessing checks. + | args: %a0 (struct fp_ext *) + | NOTE: it does _not_ modify %a0/%a1 and the upper word of %d2 + +fp_normalize_ext: + printf PNORM,"ne: %p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,"), " + move.l (%a0)+,%d0 + cmp.w #0x7fff,%d0 | Inf / NaN? + jeq fp_ne_large + move.l (%a0),%d0 + jpl fp_ne_small | zero / denorm? + | The high bit is set, so normalization is irrelevant. +fp_ne_checkround: + subq.l #4,%a0 +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC + move.b (%a0),%d0 + jne fp_ne_round +#endif + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC +fp_ne_round: + fp_set_sr FPSR_EXC_INEX2 + clr.b (%a0) + move.w (FPD_RND,FPDATA),%d2 + jne fp_ne_roundother | %d2 == 0, round to nearest + tst.b %d0 | test guard bit + jpl 9f | zero is closer + btst #0,(11,%a0) | test lsb bit + jne fp_ne_doroundup | round to infinity + lsl.b #1,%d0 | check low bits + jeq 9f | round to zero +fp_ne_doroundup: + addq.l #1,(8,%a0) + jcc 9f + addq.l #1,(4,%a0) + jcc 9f + addq.w #1,(2,%a0) + move.w #0x8000,(4,%a0) +9: printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +fp_ne_roundother: + subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 1f | %d2 > 2, round to +infinity + tst.b (1,%a0) | to -inf + jne fp_ne_doroundup | negative, round to infinity + jra 9b | positive, round to zero +1: tst.b (1,%a0) | to +inf + jeq fp_ne_doroundup | positive, round to infinity + jra 9b | negative, round to zero +#endif + | Zeros and subnormal numbers + | These are probably merely subnormal, rather than "denormalized" + | numbers, so we will try to make them normal again. +fp_ne_small: + jne fp_ne_small1 | high lword zero? + move.l (4,%a0),%d0 + jne fp_ne_small2 +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC + clr.l %d0 + move.b (-4,%a0),%d0 + jne fp_ne_small3 +#endif + | Genuine zero. + clr.w -(%a0) + subq.l #2,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + | Subnormal. +fp_ne_small1: + bfffo %d0{#0,#32},%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 + fp_set_sr FPSR_EXC_UNFL +1: move.w %d2,(%a0)+ + move.w %d1,%d2 + jeq fp_ne_checkround + | This is exactly the same 64-bit double shift as seen above. + lsl.l %d2,%d0 + move.l %d0,(%a0)+ + move.l (%a0),%d0 + move.l %d0,%d1 + lsl.l %d2,%d0 + move.l %d0,(%a0) + neg.w %d2 + and.w #0x1f,%d2 + lsr.l %d2,%d1 + or.l %d1,-(%a0) +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC +fp_ne_extra1: + clr.l %d0 + move.b (-4,%a0),%d0 + neg.w %d2 + add.w #24,%d2 + jcc 1f + clr.b (-4,%a0) + lsl.l %d2,%d0 + or.l %d0,(4,%a0) + jra fp_ne_checkround +1: addq.w #8,%d2 + lsl.l %d2,%d0 + move.b %d0,(-4,%a0) + lsr.l #8,%d0 + or.l %d0,(4,%a0) +#endif + jra fp_ne_checkround + | May or may not be subnormal, if so, only 32 bits to shift. +fp_ne_small2: + bfffo %d0{#0,#32},%d1 + add.w #32,%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Beyond pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 + fp_set_sr FPSR_EXC_UNFL +1: move.w %d2,(%a0)+ + ext.l %d1 + jeq fp_ne_checkround + clr.l (4,%a0) + sub.w #32,%d1 + jcs 1f + lsl.l %d1,%d0 | lower lword needs only to be shifted + move.l %d0,(%a0) | into the higher lword +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC + clr.l %d0 + move.b (-4,%a0),%d0 + clr.b (-4,%a0) + neg.w %d1 + add.w #32,%d1 + bfins %d0,(%a0){%d1,#8} +#endif + jra fp_ne_checkround +1: neg.w %d1 | lower lword is splitted between + bfins %d0,(%a0){%d1,#32} | higher and lower lword +#ifndef CONFIG_M68KFPU_EMU_EXTRAPREC + jra fp_ne_checkround +#else + move.w %d1,%d2 + jra fp_ne_extra1 + | These are extremely small numbers, that will mostly end up as zero + | anyway, so this is only important for correct rounding. +fp_ne_small3: + bfffo %d0{#24,#8},%d1 + add.w #40,%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 +1: move.w %d2,(%a0)+ + ext.l %d1 + jeq fp_ne_checkround + cmp.w #8,%d1 + jcs 2f +1: clr.b (-4,%a0) + sub.w #64,%d1 + jcs 1f + add.w #24,%d1 + lsl.l %d1,%d0 + move.l %d0,(%a0) + jra fp_ne_checkround +1: neg.w %d1 + bfins %d0,(%a0){%d1,#8} + jra fp_ne_checkround +2: lsl.l %d1,%d0 + move.b %d0,(-4,%a0) + lsr.l #8,%d0 + move.b %d0,(7,%a0) + jra fp_ne_checkround +#endif + | Infinities and NaNs, again, same as above. +fp_ne_large: + move.l (%a0)+,%d0 + jne 3f +1: tst.l (%a0) + jne 4f +2: subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + | we have maybe a NaN, shift off the highest bit +3: move.l %d0,%d1 + lsl.l #1,%d1 + jne 4f + clr.l (-4,%a0) + jra 1b + | we have a NaN, test if it is signaling +4: bset #30,%d0 + jne 2b + fp_set_sr FPSR_EXC_SNAN + move.l %d0,(-4,%a0) + jra 2b + + | these next two do rounding as per the IEEE standard. + | values for the rounding modes appear to be: + | 0: Round to nearest + | 1: Round to zero + | 2: Round to -Infinity + | 3: Round to +Infinity + | both functions expect that fp_normalize was already + | called (and extended argument is already normalized + | as far as possible), these are used if there is different + | rounding precision is selected and before converting + | into single/double + + | fp_normalize_double: + | normalize an extended with double (52-bit) precision + | args: %a0 (struct fp_ext *) + +fp_normalize_double: + printf PNORM,"nd: %p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,"), " + move.l (%a0)+,%d2 + tst.w %d2 + jeq fp_nd_zero | zero / denormalized + cmp.w #0x7fff,%d2 + jeq fp_nd_huge | NaN / infinitive. + sub.w #0x4000-0x3ff,%d2 | will the exponent fit? + jcs fp_nd_small | too small. + cmp.w #0x7fe,%d2 + jcc fp_nd_large | too big. + addq.l #4,%a0 + move.l (%a0),%d0 | low lword of mantissa + | now, round off the low 11 bits. +fp_nd_round: + moveq #21,%d1 + lsl.l %d1,%d0 | keep 11 low bits. + jne fp_nd_checkround | Are they non-zero? + | nothing to do here +9: subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + | Be careful with the X bit! It contains the lsb + | from the shift above, it is needed for round to nearest. +fp_nd_checkround: + fp_set_sr FPSR_EXC_INEX2 | INEX2 bit + and.w #0xf800,(2,%a0) | clear bits 0-10 + move.w (FPD_RND,FPDATA),%d2 | rounding mode + jne 2f | %d2 == 0, round to nearest + tst.l %d0 | test guard bit + jpl 9b | zero is closer + | here we test the X bit by adding it to %d2 + clr.w %d2 | first set z bit, addx only clears it + addx.w %d2,%d2 | test lsb bit + | IEEE754-specified "round to even" behaviour. If the guard + | bit is set, then the number is odd, so rounding works like + | in grade-school arithmetic (i.e. 1.5 rounds to 2.0) + | Otherwise, an equal distance rounds towards zero, so as not + | to produce an odd number. This is strange, but it is what + | the standard says. + jne fp_nd_doroundup | round to infinity + lsl.l #1,%d0 | check low bits + jeq 9b | round to zero +fp_nd_doroundup: + | round (the mantissa, that is) towards infinity + add.l #0x800,(%a0) + jcc 9b | no overflow, good. + addq.l #1,-(%a0) | extend to high lword + jcc 1f | no overflow, good. + | Yow! we have managed to overflow the mantissa. Since this + | only happens when %d1 was 0xfffff800, it is now zero, so + | reset the high bit, and increment the exponent. + move.w #0x8000,(%a0) + addq.w #1,-(%a0) + cmp.w #0x43ff,(%a0)+ | exponent now overflown? + jeq fp_nd_large | yes, so make it infinity. +1: subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +2: subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 3f | %d2 > 2, round to +infinity + | Round to +Inf or -Inf. High word of %d2 contains the + | sign of the number, by the way. + swap %d2 | to -inf + tst.b %d2 + jne fp_nd_doroundup | negative, round to infinity + jra 9b | positive, round to zero +3: swap %d2 | to +inf + tst.b %d2 + jeq fp_nd_doroundup | positive, round to infinity + jra 9b | negative, round to zero + | Exponent underflow. Try to make a denormal, and set it to + | the smallest possible fraction if this fails. +fp_nd_small: + fp_set_sr FPSR_EXC_UNFL | set UNFL bit + move.w #0x3c01,(-2,%a0) | 2**-1022 + neg.w %d2 | degree of underflow + cmp.w #32,%d2 | single or double shift? + jcc 1f + | Again, another 64-bit double shift. + move.l (%a0),%d0 + move.l %d0,%d1 + lsr.l %d2,%d0 + move.l %d0,(%a0)+ + move.l (%a0),%d0 + lsr.l %d2,%d0 + neg.w %d2 + add.w #32,%d2 + lsl.l %d2,%d1 + or.l %d1,%d0 + move.l (%a0),%d1 + move.l %d0,(%a0) + | Check to see if we shifted off any significant bits + lsl.l %d2,%d1 + jeq fp_nd_round | Nope, round. + bset #0,%d0 | Yes, so set the "sticky bit". + jra fp_nd_round | Now, round. + | Another 64-bit single shift and store +1: sub.w #32,%d2 + cmp.w #32,%d2 | Do we really need to shift? + jcc 2f | No, the number is too small. + move.l (%a0),%d0 + clr.l (%a0)+ + move.l %d0,%d1 + lsr.l %d2,%d0 + neg.w %d2 + add.w #32,%d2 + | Again, check to see if we shifted off any significant bits. + tst.l (%a0) + jeq 1f + bset #0,%d0 | Sticky bit. +1: move.l %d0,(%a0) + lsl.l %d2,%d1 + jeq fp_nd_round + bset #0,%d0 + jra fp_nd_round + | Sorry, the number is just too small. +2: clr.l (%a0)+ + clr.l (%a0) + moveq #1,%d0 | Smallest possible fraction, + jra fp_nd_round | round as desired. + | zero and denormalized +fp_nd_zero: + tst.l (%a0)+ + jne 1f + tst.l (%a0) + jne 1f + subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts | zero. nothing to do. + | These are not merely subnormal numbers, but true denormals, + | i.e. pathologically small (exponent is 2**-16383) numbers. + | It is clearly impossible for even a normal extended number + | with that exponent to fit into double precision, so just + | write these ones off as "too darn small". +1: fp_set_sr FPSR_EXC_UNFL | Set UNFL bit + clr.l (%a0) + clr.l -(%a0) + move.w #0x3c01,-(%a0) | i.e. 2**-1022 + addq.l #6,%a0 + moveq #1,%d0 + jra fp_nd_round | round. + | Exponent overflow. Just call it infinity. +fp_nd_large: + move.w #0x7ff,%d0 + and.w (6,%a0),%d0 + jeq 1f + fp_set_sr FPSR_EXC_INEX2 +1: fp_set_sr FPSR_EXC_OVFL + move.w (FPD_RND,FPDATA),%d2 + jne 3f | %d2 = 0 round to nearest +1: move.w #0x7fff,(-2,%a0) + clr.l (%a0)+ + clr.l (%a0) +2: subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +3: subq.w #2,%d2 + jcs 5f | %d2 < 2, round to zero + jhi 4f | %d2 > 2, round to +infinity + tst.b (-3,%a0) | to -inf + jne 1b + jra 5f +4: tst.b (-3,%a0) | to +inf + jeq 1b +5: move.w #0x43fe,(-2,%a0) + moveq #-1,%d0 + move.l %d0,(%a0)+ + move.w #0xf800,%d0 + move.l %d0,(%a0) + jra 2b + | Infinities or NaNs +fp_nd_huge: + subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + + | fp_normalize_single: + | normalize an extended with single (23-bit) precision + | args: %a0 (struct fp_ext *) + +fp_normalize_single: + printf PNORM,"ns: %p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,") " + addq.l #2,%a0 + move.w (%a0)+,%d2 + jeq fp_ns_zero | zero / denormalized + cmp.w #0x7fff,%d2 + jeq fp_ns_huge | NaN / infinitive. + sub.w #0x4000-0x7f,%d2 | will the exponent fit? + jcs fp_ns_small | too small. + cmp.w #0xfe,%d2 + jcc fp_ns_large | too big. + move.l (%a0)+,%d0 | get high lword of mantissa +fp_ns_round: + tst.l (%a0) | check the low lword + jeq 1f + | Set a sticky bit if it is non-zero. This should only + | affect the rounding in what would otherwise be equal- + | distance situations, which is what we want it to do. + bset #0,%d0 +1: clr.l (%a0) | zap it from memory. + | now, round off the low 8 bits of the hi lword. + tst.b %d0 | 8 low bits. + jne fp_ns_checkround | Are they non-zero? + | nothing to do here + subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +fp_ns_checkround: + fp_set_sr FPSR_EXC_INEX2 | INEX2 bit + clr.b -(%a0) | clear low byte of high lword + subq.l #3,%a0 + move.w (FPD_RND,FPDATA),%d2 | rounding mode + jne 2f | %d2 == 0, round to nearest + tst.b %d0 | test guard bit + jpl 9f | zero is closer + btst #8,%d0 | test lsb bit + | round to even behaviour, see above. + jne fp_ns_doroundup | round to infinity + lsl.b #1,%d0 | check low bits + jeq 9f | round to zero +fp_ns_doroundup: + | round (the mantissa, that is) towards infinity + add.l #0x100,(%a0) + jcc 9f | no overflow, good. + | Overflow. This means that the %d1 was 0xffffff00, so it + | is now zero. We will set the mantissa to reflect this, and + | increment the exponent (checking for overflow there too) + move.w #0x8000,(%a0) + addq.w #1,-(%a0) + cmp.w #0x407f,(%a0)+ | exponent now overflown? + jeq fp_ns_large | yes, so make it infinity. +9: subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + | check nondefault rounding modes +2: subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 3f | %d2 > 2, round to +infinity + tst.b (-3,%a0) | to -inf + jne fp_ns_doroundup | negative, round to infinity + jra 9b | positive, round to zero +3: tst.b (-3,%a0) | to +inf + jeq fp_ns_doroundup | positive, round to infinity + jra 9b | negative, round to zero + | Exponent underflow. Try to make a denormal, and set it to + | the smallest possible fraction if this fails. +fp_ns_small: + fp_set_sr FPSR_EXC_UNFL | set UNFL bit + move.w #0x3f81,(-2,%a0) | 2**-126 + neg.w %d2 | degree of underflow + cmp.w #32,%d2 | single or double shift? + jcc 2f + | a 32-bit shift. + move.l (%a0),%d0 + move.l %d0,%d1 + lsr.l %d2,%d0 + move.l %d0,(%a0)+ + | Check to see if we shifted off any significant bits. + neg.w %d2 + add.w #32,%d2 + lsl.l %d2,%d1 + jeq 1f + bset #0,%d0 | Sticky bit. + | Check the lower lword +1: tst.l (%a0) + jeq fp_ns_round + clr (%a0) + bset #0,%d0 | Sticky bit. + jra fp_ns_round + | Sorry, the number is just too small. +2: clr.l (%a0)+ + clr.l (%a0) + moveq #1,%d0 | Smallest possible fraction, + jra fp_ns_round | round as desired. + | Exponent overflow. Just call it infinity. +fp_ns_large: + tst.b (3,%a0) + jeq 1f + fp_set_sr FPSR_EXC_INEX2 +1: fp_set_sr FPSR_EXC_OVFL + move.w (FPD_RND,FPDATA),%d2 + jne 3f | %d2 = 0 round to nearest +1: move.w #0x7fff,(-2,%a0) + clr.l (%a0)+ + clr.l (%a0) +2: subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +3: subq.w #2,%d2 + jcs 5f | %d2 < 2, round to zero + jhi 4f | %d2 > 2, round to +infinity + tst.b (-3,%a0) | to -inf + jne 1b + jra 5f +4: tst.b (-3,%a0) | to +inf + jeq 1b +5: move.w #0x407e,(-2,%a0) + move.l #0xffffff00,(%a0)+ + clr.l (%a0) + jra 2b + | zero and denormalized +fp_ns_zero: + tst.l (%a0)+ + jne 1f + tst.l (%a0) + jne 1f + subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts | zero. nothing to do. + | These are not merely subnormal numbers, but true denormals, + | i.e. pathologically small (exponent is 2**-16383) numbers. + | It is clearly impossible for even a normal extended number + | with that exponent to fit into single precision, so just + | write these ones off as "too darn small". +1: fp_set_sr FPSR_EXC_UNFL | Set UNFL bit + clr.l (%a0) + clr.l -(%a0) + move.w #0x3f81,-(%a0) | i.e. 2**-126 + addq.l #6,%a0 + moveq #1,%d0 + jra fp_ns_round | round. + | Infinities or NaNs +fp_ns_huge: + subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + + | fp_normalize_single_fast: + | normalize an extended with single (23-bit) precision + | this is only used by fsgldiv/fsgdlmul, where the + | operand is not completly normalized. + | args: %a0 (struct fp_ext *) + +fp_normalize_single_fast: + printf PNORM,"nsf: %p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,") " + addq.l #2,%a0 + move.w (%a0)+,%d2 + cmp.w #0x7fff,%d2 + jeq fp_nsf_huge | NaN / infinitive. + move.l (%a0)+,%d0 | get high lword of mantissa +fp_nsf_round: + tst.l (%a0) | check the low lword + jeq 1f + | Set a sticky bit if it is non-zero. This should only + | affect the rounding in what would otherwise be equal- + | distance situations, which is what we want it to do. + bset #0,%d0 +1: clr.l (%a0) | zap it from memory. + | now, round off the low 8 bits of the hi lword. + tst.b %d0 | 8 low bits. + jne fp_nsf_checkround | Are they non-zero? + | nothing to do here + subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +fp_nsf_checkround: + fp_set_sr FPSR_EXC_INEX2 | INEX2 bit + clr.b -(%a0) | clear low byte of high lword + subq.l #3,%a0 + move.w (FPD_RND,FPDATA),%d2 | rounding mode + jne 2f | %d2 == 0, round to nearest + tst.b %d0 | test guard bit + jpl 9f | zero is closer + btst #8,%d0 | test lsb bit + | round to even behaviour, see above. + jne fp_nsf_doroundup | round to infinity + lsl.b #1,%d0 | check low bits + jeq 9f | round to zero +fp_nsf_doroundup: + | round (the mantissa, that is) towards infinity + add.l #0x100,(%a0) + jcc 9f | no overflow, good. + | Overflow. This means that the %d1 was 0xffffff00, so it + | is now zero. We will set the mantissa to reflect this, and + | increment the exponent (checking for overflow there too) + move.w #0x8000,(%a0) + addq.w #1,-(%a0) + cmp.w #0x407f,(%a0)+ | exponent now overflown? + jeq fp_nsf_large | yes, so make it infinity. +9: subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + | check nondefault rounding modes +2: subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 3f | %d2 > 2, round to +infinity + tst.b (-3,%a0) | to -inf + jne fp_nsf_doroundup | negative, round to infinity + jra 9b | positive, round to zero +3: tst.b (-3,%a0) | to +inf + jeq fp_nsf_doroundup | positive, round to infinity + jra 9b | negative, round to zero + | Exponent overflow. Just call it infinity. +fp_nsf_large: + tst.b (3,%a0) + jeq 1f + fp_set_sr FPSR_EXC_INEX2 +1: fp_set_sr FPSR_EXC_OVFL + move.w (FPD_RND,FPDATA),%d2 + jne 3f | %d2 = 0 round to nearest +1: move.w #0x7fff,(-2,%a0) + clr.l (%a0)+ + clr.l (%a0) +2: subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +3: subq.w #2,%d2 + jcs 5f | %d2 < 2, round to zero + jhi 4f | %d2 > 2, round to +infinity + tst.b (-3,%a0) | to -inf + jne 1b + jra 5f +4: tst.b (-3,%a0) | to +inf + jeq 1b +5: move.w #0x407e,(-2,%a0) + move.l #0xffffff00,(%a0)+ + clr.l (%a0) + jra 2b + | Infinities or NaNs +fp_nsf_huge: + subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + + | conv_ext2int (macro): + | Generates a subroutine that converts an extended value to an + | integer of a given size, again, with the appropriate type of + | rounding. + + | Macro arguments: + | s: size, as given in an assembly instruction. + | b: number of bits in that size. + + | Subroutine arguments: + | %a0: source (struct fp_ext *) + + | Returns the integer in %d0 (like it should) + +.macro conv_ext2int s,b + .set inf,(1<<(\b-1))-1 | i.e. MAXINT + printf PCONV,"e2i%d: %p(",2,#\b,%a0 + printx PCONV,%a0@ + printf PCONV,") " + addq.l #2,%a0 + move.w (%a0)+,%d2 | exponent + jeq fp_e2i_zero\b | zero / denorm (== 0, here) + cmp.w #0x7fff,%d2 + jeq fp_e2i_huge\b | Inf / NaN + sub.w #0x3ffe,%d2 + jcs fp_e2i_small\b + cmp.w #\b,%d2 + jhi fp_e2i_large\b + move.l (%a0),%d0 + move.l %d0,%d1 + lsl.l %d2,%d1 + jne fp_e2i_round\b + tst.l (4,%a0) + jne fp_e2i_round\b + neg.w %d2 + add.w #32,%d2 + lsr.l %d2,%d0 +9: tst.w (-4,%a0) + jne 1f + tst.\s %d0 + jmi fp_e2i_large\b + printf PCONV,"-> %p\n",1,%d0 + rts +1: neg.\s %d0 + jeq 1f + jpl fp_e2i_large\b +1: printf PCONV,"-> %p\n",1,%d0 + rts +fp_e2i_round\b: + fp_set_sr FPSR_EXC_INEX2 | INEX2 bit + neg.w %d2 + add.w #32,%d2 + .if \b>16 + jeq 5f + .endif + lsr.l %d2,%d0 + move.w (FPD_RND,FPDATA),%d2 | rounding mode + jne 2f | %d2 == 0, round to nearest + tst.l %d1 | test guard bit + jpl 9b | zero is closer + btst %d2,%d0 | test lsb bit (%d2 still 0) + jne fp_e2i_doroundup\b + lsl.l #1,%d1 | check low bits + jne fp_e2i_doroundup\b + tst.l (4,%a0) + jeq 9b +fp_e2i_doroundup\b: + addq.l #1,%d0 + jra 9b + | check nondefault rounding modes +2: subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 3f | %d2 > 2, round to +infinity + tst.w (-4,%a0) | to -inf + jne fp_e2i_doroundup\b | negative, round to infinity + jra 9b | positive, round to zero +3: tst.w (-4,%a0) | to +inf + jeq fp_e2i_doroundup\b | positive, round to infinity + jra 9b | negative, round to zero + | we are only want -2**127 get correctly rounded here, + | since the guard bit is in the lower lword. + | everything else ends up anyway as overflow. + .if \b>16 +5: move.w (FPD_RND,FPDATA),%d2 | rounding mode + jne 2b | %d2 == 0, round to nearest + move.l (4,%a0),%d1 | test guard bit + jpl 9b | zero is closer + lsl.l #1,%d1 | check low bits + jne fp_e2i_doroundup\b + jra 9b + .endif +fp_e2i_zero\b: + clr.l %d0 + tst.l (%a0)+ + jne 1f + tst.l (%a0) + jeq 3f +1: subq.l #4,%a0 + fp_clr_sr FPSR_EXC_UNFL | fp_normalize_ext has set this bit +fp_e2i_small\b: + fp_set_sr FPSR_EXC_INEX2 + clr.l %d0 + move.w (FPD_RND,FPDATA),%d2 | rounding mode + subq.w #2,%d2 + jcs 3f | %d2 < 2, round to nearest/zero + jhi 2f | %d2 > 2, round to +infinity + tst.w (-4,%a0) | to -inf + jeq 3f + subq.\s #1,%d0 + jra 3f +2: tst.w (-4,%a0) | to +inf + jne 3f + addq.\s #1,%d0 +3: printf PCONV,"-> %p\n",1,%d0 + rts +fp_e2i_large\b: + fp_set_sr FPSR_EXC_OPERR + move.\s #inf,%d0 + tst.w (-4,%a0) + jeq 1f + addq.\s #1,%d0 +1: printf PCONV,"-> %p\n",1,%d0 + rts +fp_e2i_huge\b: + move.\s (%a0),%d0 + tst.l (%a0) + jne 1f + tst.l (%a0) + jeq fp_e2i_large\b + | fp_normalize_ext has set this bit already + | and made the number nonsignaling +1: fp_tst_sr FPSR_EXC_SNAN + jne 1f + fp_set_sr FPSR_EXC_OPERR +1: printf PCONV,"-> %p\n",1,%d0 + rts +.endm + +fp_conv_ext2long: + conv_ext2int l,32 + +fp_conv_ext2short: + conv_ext2int w,16 + +fp_conv_ext2byte: + conv_ext2int b,8 + +fp_conv_ext2double: + jsr fp_normalize_double + printf PCONV,"e2d: %p(",1,%a0 + printx PCONV,%a0@ + printf PCONV,"), " + move.l (%a0)+,%d2 + cmp.w #0x7fff,%d2 + jne 1f + move.w #0x7ff,%d2 + move.l (%a0)+,%d0 + jra 2f +1: sub.w #0x3fff-0x3ff,%d2 + move.l (%a0)+,%d0 + jmi 2f + clr.w %d2 +2: lsl.w #5,%d2 + lsl.l #7,%d2 + lsl.l #8,%d2 + move.l %d0,%d1 + lsl.l #1,%d0 + lsr.l #4,%d0 + lsr.l #8,%d0 + or.l %d2,%d0 + putuser.l %d0,(%a1)+,fp_err_ua2,%a1 + moveq #21,%d0 + lsl.l %d0,%d1 + move.l (%a0),%d0 + lsr.l #4,%d0 + lsr.l #7,%d0 + or.l %d1,%d0 + putuser.l %d0,(%a1),fp_err_ua2,%a1 +#ifdef FPU_EMU_DEBUG + getuser.l %a1@(-4),%d0,fp_err_ua2,%a1 + getuser.l %a1@(0),%d1,fp_err_ua2,%a1 + printf PCONV,"%p(%08x%08x)\n",3,%a1,%d0,%d1 +#endif + rts + +fp_conv_ext2single: + jsr fp_normalize_single + printf PCONV,"e2s: %p(",1,%a0 + printx PCONV,%a0@ + printf PCONV,"), " + move.l (%a0)+,%d1 + cmp.w #0x7fff,%d1 + jne 1f + move.w #0xff,%d1 + move.l (%a0)+,%d0 + jra 2f +1: sub.w #0x3fff-0x7f,%d1 + move.l (%a0)+,%d0 + jmi 2f + clr.w %d1 +2: lsl.w #8,%d1 + lsl.l #7,%d1 + lsl.l #8,%d1 + bclr #31,%d0 + lsr.l #8,%d0 + or.l %d1,%d0 + printf PCONV,"%08x\n",1,%d0 + rts + + | special return addresses for instr that + | encode the rounding precision in the opcode + | (e.g. fsmove,fdmove) + +fp_finalrounding_single: + addq.l #8,%sp + jsr fp_normalize_ext + jsr fp_normalize_single + jra fp_finaltest + +fp_finalrounding_single_fast: + addq.l #8,%sp + jsr fp_normalize_ext + jsr fp_normalize_single_fast + jra fp_finaltest + +fp_finalrounding_double: + addq.l #8,%sp + jsr fp_normalize_ext + jsr fp_normalize_double + jra fp_finaltest + + | fp_finaltest: + | set the emulated status register based on the outcome of an + | emulated instruction. + +fp_finalrounding: + addq.l #8,%sp +| printf ,"f: %p\n",1,%a0 + jsr fp_normalize_ext + move.w (FPD_PREC,FPDATA),%d0 + subq.w #1,%d0 + jcs fp_finaltest + jne 1f + jsr fp_normalize_single + jra 2f +1: jsr fp_normalize_double +2:| printf ,"f: %p\n",1,%a0 +fp_finaltest: + | First, we do some of the obvious tests for the exception + | status byte and condition code bytes of fp_sr here, so that + | they do not have to be handled individually by every + | emulated instruction. + clr.l %d0 + addq.l #1,%a0 + tst.b (%a0)+ | sign + jeq 1f + bset #FPSR_CC_NEG-24,%d0 | N bit +1: cmp.w #0x7fff,(%a0)+ | exponent + jeq 2f + | test for zero + moveq #FPSR_CC_Z-24,%d1 + tst.l (%a0)+ + jne 9f + tst.l (%a0) + jne 9f + jra 8f + | infinitiv and NAN +2: moveq #FPSR_CC_NAN-24,%d1 + move.l (%a0)+,%d2 + lsl.l #1,%d2 | ignore high bit + jne 8f + tst.l (%a0) + jne 8f + moveq #FPSR_CC_INF-24,%d1 +8: bset %d1,%d0 +9: move.b %d0,(FPD_FPSR+0,FPDATA) | set condition test result + | move instructions enter here + | Here, we test things in the exception status byte, and set + | other things in the accrued exception byte accordingly. + | Emulated instructions can set various things in the former, + | as defined in fp_emu.h. +fp_final: + move.l (FPD_FPSR,FPDATA),%d0 +#if 0 + btst #FPSR_EXC_SNAN,%d0 | EXC_SNAN + jne 1f + btst #FPSR_EXC_OPERR,%d0 | EXC_OPERR + jeq 2f +1: bset #FPSR_AEXC_IOP,%d0 | set IOP bit +2: btst #FPSR_EXC_OVFL,%d0 | EXC_OVFL + jeq 1f + bset #FPSR_AEXC_OVFL,%d0 | set OVFL bit +1: btst #FPSR_EXC_UNFL,%d0 | EXC_UNFL + jeq 1f + btst #FPSR_EXC_INEX2,%d0 | EXC_INEX2 + jeq 1f + bset #FPSR_AEXC_UNFL,%d0 | set UNFL bit +1: btst #FPSR_EXC_DZ,%d0 | EXC_INEX1 + jeq 1f + bset #FPSR_AEXC_DZ,%d0 | set DZ bit +1: btst #FPSR_EXC_OVFL,%d0 | EXC_OVFL + jne 1f + btst #FPSR_EXC_INEX2,%d0 | EXC_INEX2 + jne 1f + btst #FPSR_EXC_INEX1,%d0 | EXC_INEX1 + jeq 2f +1: bset #FPSR_AEXC_INEX,%d0 | set INEX bit +2: move.l %d0,(FPD_FPSR,FPDATA) +#else + | same as above, greatly optimized, but untested (yet) + move.l %d0,%d2 + lsr.l #5,%d0 + move.l %d0,%d1 + lsr.l #4,%d1 + or.l %d0,%d1 + and.b #0x08,%d1 + move.l %d2,%d0 + lsr.l #6,%d0 + or.l %d1,%d0 + move.l %d2,%d1 + lsr.l #4,%d1 + or.b #0xdf,%d1 + and.b %d1,%d0 + move.l %d2,%d1 + lsr.l #7,%d1 + and.b #0x80,%d1 + or.b %d1,%d0 + and.b #0xf8,%d0 + or.b %d0,%d2 + move.l %d2,(FPD_FPSR,FPDATA) +#endif + move.b (FPD_FPSR+2,FPDATA),%d0 + and.b (FPD_FPCR+2,FPDATA),%d0 + jeq 1f + printf ,"send signal!!!\n" +1: jra fp_end diff --git a/arch/m68k/math-emu/multi_arith.h b/arch/m68k/math-emu/multi_arith.h new file mode 100644 index 000000000..2b9b9e2e1 --- /dev/null +++ b/arch/m68k/math-emu/multi_arith.h @@ -0,0 +1,822 @@ +/* multi_arith.h: multi-precision integer arithmetic functions, needed + to do extended-precision floating point. + + (c) 1998 David Huggins-Daines. + + Somewhat based on arch/alpha/math-emu/ieee-math.c, which is (c) + David Mosberger-Tang. + + You may copy, modify, and redistribute this file under the terms of + the GNU General Public License, version 2, or any later version, at + your convenience. */ + +/* Note: + + These are not general multi-precision math routines. Rather, they + implement the subset of integer arithmetic that we need in order to + multiply, divide, and normalize 128-bit unsigned mantissae. */ + +#ifndef MULTI_ARITH_H +#define MULTI_ARITH_H + +#if 0 /* old code... */ + +/* Unsigned only, because we don't need signs to multiply and divide. */ +typedef unsigned int int128[4]; + +/* Word order */ +enum { + MSW128, + NMSW128, + NLSW128, + LSW128 +}; + +/* big-endian */ +#define LO_WORD(ll) (((unsigned int *) &ll)[1]) +#define HI_WORD(ll) (((unsigned int *) &ll)[0]) + +/* Convenience functions to stuff various integer values into int128s */ + +extern inline void zero128(int128 a) +{ + a[LSW128] = a[NLSW128] = a[NMSW128] = a[MSW128] = 0; +} + +/* Human-readable word order in the arguments */ +extern inline void set128(unsigned int i3, + unsigned int i2, + unsigned int i1, + unsigned int i0, + int128 a) +{ + a[LSW128] = i0; + a[NLSW128] = i1; + a[NMSW128] = i2; + a[MSW128] = i3; +} + +/* Convenience functions (for testing as well) */ +extern inline void int64_to_128(unsigned long long src, + int128 dest) +{ + dest[LSW128] = (unsigned int) src; + dest[NLSW128] = src >> 32; + dest[NMSW128] = dest[MSW128] = 0; +} + +extern inline void int128_to_64(const int128 src, + unsigned long long *dest) +{ + *dest = src[LSW128] | (long long) src[NLSW128] << 32; +} + +extern inline void put_i128(const int128 a) +{ + printk("%08x %08x %08x %08x\n", a[MSW128], a[NMSW128], + a[NLSW128], a[LSW128]); +} + +/* Internal shifters: + + Note that these are only good for 0 < count < 32. + */ + +extern inline void _lsl128(unsigned int count, int128 a) +{ + a[MSW128] = (a[MSW128] << count) | (a[NMSW128] >> (32 - count)); + a[NMSW128] = (a[NMSW128] << count) | (a[NLSW128] >> (32 - count)); + a[NLSW128] = (a[NLSW128] << count) | (a[LSW128] >> (32 - count)); + a[LSW128] <<= count; +} + +extern inline void _lsr128(unsigned int count, int128 a) +{ + a[LSW128] = (a[LSW128] >> count) | (a[NLSW128] << (32 - count)); + a[NLSW128] = (a[NLSW128] >> count) | (a[NMSW128] << (32 - count)); + a[NMSW128] = (a[NMSW128] >> count) | (a[MSW128] << (32 - count)); + a[MSW128] >>= count; +} + +/* Should be faster, one would hope */ + +extern inline void lslone128(int128 a) +{ + asm volatile ("lsl.l #1,%0\n" + "roxl.l #1,%1\n" + "roxl.l #1,%2\n" + "roxl.l #1,%3\n" + : + "=d" (a[LSW128]), + "=d"(a[NLSW128]), + "=d"(a[NMSW128]), + "=d"(a[MSW128]) + : + "0"(a[LSW128]), + "1"(a[NLSW128]), + "2"(a[NMSW128]), + "3"(a[MSW128])); +} + +extern inline void lsrone128(int128 a) +{ + asm volatile ("lsr.l #1,%0\n" + "roxr.l #1,%1\n" + "roxr.l #1,%2\n" + "roxr.l #1,%3\n" + : + "=d" (a[MSW128]), + "=d"(a[NMSW128]), + "=d"(a[NLSW128]), + "=d"(a[LSW128]) + : + "0"(a[MSW128]), + "1"(a[NMSW128]), + "2"(a[NLSW128]), + "3"(a[LSW128])); +} + +/* Generalized 128-bit shifters: + + These bit-shift to a multiple of 32, then move whole longwords. */ + +extern inline void lsl128(unsigned int count, int128 a) +{ + int wordcount, i; + + if (count % 32) + _lsl128(count % 32, a); + + if (0 == (wordcount = count / 32)) + return; + + /* argh, gak, endian-sensitive */ + for (i = 0; i < 4 - wordcount; i++) { + a[i] = a[i + wordcount]; + } + for (i = 3; i >= 4 - wordcount; --i) { + a[i] = 0; + } +} + +extern inline void lsr128(unsigned int count, int128 a) +{ + int wordcount, i; + + if (count % 32) + _lsr128(count % 32, a); + + if (0 == (wordcount = count / 32)) + return; + + for (i = 3; i >= wordcount; --i) { + a[i] = a[i - wordcount]; + } + for (i = 0; i < wordcount; i++) { + a[i] = 0; + } +} + +extern inline int orl128(int a, int128 b) +{ + b[LSW128] |= a; +} + +extern inline int btsthi128(const int128 a) +{ + return a[MSW128] & 0x80000000; +} + +/* test bits (numbered from 0 = LSB) up to and including "top" */ +extern inline int bftestlo128(int top, const int128 a) +{ + int r = 0; + + if (top > 31) + r |= a[LSW128]; + if (top > 63) + r |= a[NLSW128]; + if (top > 95) + r |= a[NMSW128]; + + r |= a[3 - (top / 32)] & ((1 << (top % 32 + 1)) - 1); + + return (r != 0); +} + +/* Aargh. We need these because GCC is broken */ +/* FIXME: do them in assembly, for goodness' sake! */ +extern inline void mask64(int pos, unsigned long long *mask) +{ + *mask = 0; + + if (pos < 32) { + LO_WORD(*mask) = (1 << pos) - 1; + return; + } + LO_WORD(*mask) = -1; + HI_WORD(*mask) = (1 << (pos - 32)) - 1; +} + +extern inline void bset64(int pos, unsigned long long *dest) +{ + /* This conditional will be optimized away. Thanks, GCC! */ + if (pos < 32) + asm volatile ("bset %1,%0":"=m" + (LO_WORD(*dest)):"id"(pos)); + else + asm volatile ("bset %1,%0":"=m" + (HI_WORD(*dest)):"id"(pos - 32)); +} + +extern inline int btst64(int pos, unsigned long long dest) +{ + if (pos < 32) + return (0 != (LO_WORD(dest) & (1 << pos))); + else + return (0 != (HI_WORD(dest) & (1 << (pos - 32)))); +} + +extern inline void lsl64(int count, unsigned long long *dest) +{ + if (count < 32) { + HI_WORD(*dest) = (HI_WORD(*dest) << count) + | (LO_WORD(*dest) >> count); + LO_WORD(*dest) <<= count; + return; + } + count -= 32; + HI_WORD(*dest) = LO_WORD(*dest) << count; + LO_WORD(*dest) = 0; +} + +extern inline void lsr64(int count, unsigned long long *dest) +{ + if (count < 32) { + LO_WORD(*dest) = (LO_WORD(*dest) >> count) + | (HI_WORD(*dest) << (32 - count)); + HI_WORD(*dest) >>= count; + return; + } + count -= 32; + LO_WORD(*dest) = HI_WORD(*dest) >> count; + HI_WORD(*dest) = 0; +} +#endif + +extern inline void fp_denormalize(struct fp_ext *reg, unsigned int cnt) +{ + reg->exp += cnt; + + switch (cnt) { + case 0 ... 8: + reg->lowmant = reg->mant.m32[1] << (8 - cnt); + reg->mant.m32[1] = (reg->mant.m32[1] >> cnt) | + (reg->mant.m32[0] << (32 - cnt)); + reg->mant.m32[0] = reg->mant.m32[0] >> cnt; + break; + case 9 ... 32: + reg->lowmant = reg->mant.m32[1] >> (cnt - 8); + if (reg->mant.m32[1] << (40 - cnt)) + reg->lowmant |= 1; + reg->mant.m32[1] = (reg->mant.m32[1] >> cnt) | + (reg->mant.m32[0] << (32 - cnt)); + reg->mant.m32[0] = reg->mant.m32[0] >> cnt; + break; + case 33 ... 39: + asm volatile ("bfextu %1{%2,#8},%0" : "=d" (reg->lowmant) + : "m" (reg->mant.m32[0]), "d" (64 - cnt)); + if (reg->mant.m32[1] << (40 - cnt)) + reg->lowmant |= 1; + reg->mant.m32[1] = reg->mant.m32[0] >> (cnt - 32); + reg->mant.m32[0] = 0; + break; + case 40 ... 71: + reg->lowmant = reg->mant.m32[0] >> (cnt - 40); + if ((reg->mant.m32[0] << (72 - cnt)) || reg->mant.m32[1]) + reg->lowmant |= 1; + reg->mant.m32[1] = reg->mant.m32[0] >> (cnt - 32); + reg->mant.m32[0] = 0; + break; + default: + reg->lowmant = reg->mant.m32[0] || reg->mant.m32[1]; + reg->mant.m32[0] = 0; + reg->mant.m32[1] = 0; + break; + } +} + +extern inline int fp_overnormalize(struct fp_ext *reg) +{ + int shift; + + if (reg->mant.m32[0]) { + asm ("bfffo %1{#0,#32},%0" : "=d" (shift) : "dm" (reg->mant.m32[0])); + reg->mant.m32[0] = (reg->mant.m32[0] << shift) | (reg->mant.m32[1] >> (32 - shift)); + reg->mant.m32[1] = (reg->mant.m32[1] << shift); + } else { + asm ("bfffo %1{#0,#32},%0" : "=d" (shift) : "dm" (reg->mant.m32[1])); + reg->mant.m32[0] = (reg->mant.m32[1] << shift); + reg->mant.m32[1] = 0; + shift += 32; + } + + return shift; +} + +extern inline int fp_addmant(struct fp_ext *dest, struct fp_ext *src) +{ + int carry; + + /* we assume here, gcc only insert move and a clr instr */ + asm volatile ("add.b %1,%0" : "=d,=g" (dest->lowmant) + : "g,d" (src->lowmant), "0,0" (dest->lowmant)); + asm volatile ("addx.l %1,%0" : "=d" (dest->mant.m32[1]) + : "d" (src->mant.m32[1]), "0" (dest->mant.m32[1])); + asm volatile ("addx.l %1,%0" : "=d" (dest->mant.m32[0]) + : "d" (src->mant.m32[0]), "0" (dest->mant.m32[0])); + asm volatile ("addx.l %0,%0" : "=d" (carry) : "0" (0)); + + return carry; +} + +extern inline int fp_addcarry(struct fp_ext *reg) +{ + if (++reg->exp == 0x7fff) { + if (reg->mant.m64) + fp_set_sr(FPSR_EXC_INEX2); + reg->mant.m64 = 0; + fp_set_sr(FPSR_EXC_OVFL); + return 0; + } + reg->lowmant = (reg->mant.m32[1] << 7) | (reg->lowmant ? 1 : 0); + reg->mant.m32[1] = (reg->mant.m32[1] >> 1) | + (reg->mant.m32[0] << 31); + reg->mant.m32[0] = (reg->mant.m32[0] >> 1) | 0x80000000; + + return 1; +} + +extern inline void fp_submant(struct fp_ext *dest, struct fp_ext *src1, struct fp_ext *src2) +{ + /* we assume here, gcc only insert move and a clr instr */ + asm volatile ("sub.b %1,%0" : "=d,=g" (dest->lowmant) + : "g,d" (src2->lowmant), "0,0" (src1->lowmant)); + asm volatile ("subx.l %1,%0" : "=d" (dest->mant.m32[1]) + : "d" (src2->mant.m32[1]), "0" (src1->mant.m32[1])); + asm volatile ("subx.l %1,%0" : "=d" (dest->mant.m32[0]) + : "d" (src2->mant.m32[0]), "0" (src1->mant.m32[0])); +} + +#define fp_mul64(desth, destl, src1, src2) ({ \ + asm ("mulu.l %2,%1:%0" : "=d" (destl), "=d" (desth) \ + : "g" (src1), "0" (src2)); \ +}) +#define fp_div64(quot, rem, srch, srcl, div) \ + asm ("divu.l %2,%1:%0" : "=d" (quot), "=d" (rem) \ + : "dm" (div), "1" (srch), "0" (srcl)) +#define fp_add64(dest1, dest2, src1, src2) ({ \ + asm ("add.l %1,%0" : "=d,=dm" (dest2) \ + : "dm,d" (src2), "0,0" (dest2)); \ + asm ("addx.l %1,%0" : "=d" (dest1) \ + : "d" (src1), "0" (dest1)); \ +}) +#define fp_addx96(dest, src) ({ \ + /* we assume here, gcc only insert move and a clr instr */ \ + asm volatile ("add.l %1,%0" : "=d,=g" (dest->m32[2]) \ + : "g,d" (temp.m32[1]), "0,0" (dest->m32[2])); \ + asm volatile ("addx.l %1,%0" : "=d" (dest->m32[1]) \ + : "d" (temp.m32[0]), "0" (dest->m32[1])); \ + asm volatile ("addx.l %1,%0" : "=d" (dest->m32[0]) \ + : "d" (0), "0" (dest->m32[0])); \ +}) +#define fp_sub64(dest, src) ({ \ + asm ("sub.l %1,%0" : "=d,=dm" (dest.m32[1]) \ + : "dm,d" (src.m32[1]), "0,0" (dest.m32[1])); \ + asm ("subx.l %1,%0" : "=d" (dest.m32[0]) \ + : "d" (src.m32[0]), "0" (dest.m32[0])); \ +}) +#define fp_sub96c(dest, srch, srcm, srcl) ({ \ + char carry; \ + asm ("sub.l %1,%0" : "=d,=dm" (dest.m32[2]) \ + : "dm,d" (srcl), "0,0" (dest.m32[2])); \ + asm ("subx.l %1,%0" : "=d" (dest.m32[1]) \ + : "d" (srcm), "0" (dest.m32[1])); \ + asm ("subx.l %2,%1; scs %0" : "=d" (carry), "=d" (dest.m32[0]) \ + : "d" (srch), "1" (dest.m32[0])); \ + carry; \ +}) + +extern inline void fp_multiplymant(union fp_mant128 *dest, struct fp_ext *src1, struct fp_ext *src2) +{ + union fp_mant64 temp; + + fp_mul64(dest->m32[0], dest->m32[1], src1->mant.m32[0], src2->mant.m32[0]); + fp_mul64(dest->m32[2], dest->m32[3], src1->mant.m32[1], src2->mant.m32[1]); + + fp_mul64(temp.m32[0], temp.m32[1], src1->mant.m32[0], src2->mant.m32[1]); + fp_addx96(dest, temp); + + fp_mul64(temp.m32[0], temp.m32[1], src1->mant.m32[1], src2->mant.m32[0]); + fp_addx96(dest, temp); +} + +extern inline void fp_dividemant(union fp_mant128 *dest, struct fp_ext *src, struct fp_ext *div) +{ + union fp_mant128 tmp; + union fp_mant64 tmp64; + unsigned long *mantp = dest->m32; + unsigned long fix, rem, first, dummy; + int i; + + /* the algorithm below requires dest to be smaller than div, + but both have the high bit set */ + if (src->mant.m64 >= div->mant.m64) { + fp_sub64(src->mant, div->mant); + *mantp = 1; + } else + *mantp = 0; + mantp++; + + /* basic idea behind this algorithm: we can't divide two 64bit numbers + (AB/CD) directly, but we can calculate AB/C0, but this means this + quotient is off by C0/CD, so we have to multiply the first result + to fix the result, after that we have nearly the correct result + and only a few corrections are needed. */ + + /* C0/CD can be precalculated, but it's an 64bit division again, but + we can make it a bit easier, by dividing first through C so we get + 10/1D and now only a single shift and the value fits into 32bit. */ + fix = 0x80000000; + dummy = div->mant.m32[1] / div->mant.m32[0] + 1; + dummy = (dummy >> 1) | fix; + fp_div64(fix, dummy, fix, 0, dummy); + fix--; + + for (i = 0; i < 3; i++, mantp++) { + if (src->mant.m32[0] == div->mant.m32[0]) { + fp_div64(first, rem, 0, src->mant.m32[1], div->mant.m32[0]); + + fp_mul64(*mantp, dummy, first, fix); + *mantp += fix; + } else { + fp_div64(first, rem, src->mant.m32[0], src->mant.m32[1], div->mant.m32[0]); + + fp_mul64(*mantp, dummy, first, fix); + } + + fp_mul64(tmp.m32[0], tmp.m32[1], div->mant.m32[0], first - *mantp); + fp_add64(tmp.m32[0], tmp.m32[1], 0, rem); + tmp.m32[2] = 0; + + fp_mul64(tmp64.m32[0], tmp64.m32[1], *mantp, div->mant.m32[1]); + fp_sub96c(tmp, 0, tmp64.m32[0], tmp64.m32[1]); + + src->mant.m32[0] = tmp.m32[1]; + src->mant.m32[1] = tmp.m32[2]; + + while (!fp_sub96c(tmp, 0, div->mant.m32[0], div->mant.m32[1])) { + src->mant.m32[0] = tmp.m32[1]; + src->mant.m32[1] = tmp.m32[2]; + *mantp += 1; + } + } +} + +#if 0 +extern inline unsigned int fp_fls128(union fp_mant128 *src) +{ + unsigned long data; + unsigned int res, off; + + if ((data = src->m32[0])) + off = 0; + else if ((data = src->m32[1])) + off = 32; + else if ((data = src->m32[2])) + off = 64; + else if ((data = src->m32[3])) + off = 96; + else + return 128; + + asm ("bfffo %1{#0,#32},%0" : "=d" (res) : "dm" (data)); + return res + off; +} + +extern inline void fp_shiftmant128(union fp_mant128 *src, int shift) +{ + unsigned long sticky; + + switch (shift) { + case 0: + return; + case 1: + asm volatile ("lsl.l #1,%0" + : "=d" (src->m32[3]) : "0" (src->m32[3])); + asm volatile ("roxl.l #1,%0" + : "=d" (src->m32[2]) : "0" (src->m32[2])); + asm volatile ("roxl.l #1,%0" + : "=d" (src->m32[1]) : "0" (src->m32[1])); + asm volatile ("roxl.l #1,%0" + : "=d" (src->m32[0]) : "0" (src->m32[0])); + return; + case 2 ... 31: + src->m32[0] = (src->m32[0] << shift) | (src->m32[1] >> (32 - shift)); + src->m32[1] = (src->m32[1] << shift) | (src->m32[2] >> (32 - shift)); + src->m32[2] = (src->m32[2] << shift) | (src->m32[3] >> (32 - shift)); + src->m32[3] = (src->m32[3] << shift); + return; + case 32 ... 63: + shift -= 32; + src->m32[0] = (src->m32[1] << shift) | (src->m32[2] >> (32 - shift)); + src->m32[1] = (src->m32[2] << shift) | (src->m32[3] >> (32 - shift)); + src->m32[2] = (src->m32[3] << shift); + src->m32[3] = 0; + return; + case 64 ... 95: + shift -= 64; + src->m32[0] = (src->m32[2] << shift) | (src->m32[3] >> (32 - shift)); + src->m32[1] = (src->m32[3] << shift); + src->m32[2] = src->m32[3] = 0; + return; + case 96 ... 127: + shift -= 96; + src->m32[0] = (src->m32[3] << shift); + src->m32[1] = src->m32[2] = src->m32[3] = 0; + return; + case -31 ... -1: + shift = -shift; + sticky = 0; + if (src->m32[3] << (32 - shift)) + sticky = 1; + src->m32[3] = (src->m32[3] >> shift) | (src->m32[2] << (32 - shift)) | sticky; + src->m32[2] = (src->m32[2] >> shift) | (src->m32[1] << (32 - shift)); + src->m32[1] = (src->m32[1] >> shift) | (src->m32[0] << (32 - shift)); + src->m32[0] = (src->m32[0] >> shift); + return; + case -63 ... -32: + shift = -shift - 32; + sticky = 0; + if ((src->m32[2] << (32 - shift)) || src->m32[3]) + sticky = 1; + src->m32[3] = (src->m32[2] >> shift) | (src->m32[1] << (32 - shift)) | sticky; + src->m32[2] = (src->m32[1] >> shift) | (src->m32[0] << (32 - shift)); + src->m32[1] = (src->m32[0] >> shift); + src->m32[0] = 0; + return; + case -95 ... -64: + shift = -shift - 64; + sticky = 0; + if ((src->m32[1] << (32 - shift)) || src->m32[2] || src->m32[3]) + sticky = 1; + src->m32[3] = (src->m32[1] >> shift) | (src->m32[0] << (32 - shift)) | sticky; + src->m32[2] = (src->m32[0] >> shift); + src->m32[1] = src->m32[0] = 0; + return; + case -127 ... -96: + shift = -shift - 96; + sticky = 0; + if ((src->m32[0] << (32 - shift)) || src->m32[1] || src->m32[2] || src->m32[3]) + sticky = 1; + src->m32[3] = (src->m32[0] >> shift) | sticky; + src->m32[2] = src->m32[1] = src->m32[0] = 0; + return; + } + + if (shift < 0 && (src->m32[0] || src->m32[1] || src->m32[2] || src->m32[3])) + src->m32[3] = 1; + else + src->m32[3] = 0; + src->m32[2] = 0; + src->m32[1] = 0; + src->m32[0] = 0; +} +#endif + +extern inline void fp_putmant128(struct fp_ext *dest, union fp_mant128 *src, int shift) +{ + unsigned long tmp; + + switch (shift) { + case 0: + dest->mant.m64 = src->m64[0]; + dest->lowmant = src->m32[2] >> 24; + if (src->m32[3] || (src->m32[2] << 8)) + dest->lowmant |= 1; + break; + case 1: + asm volatile ("lsl.l #1,%0" + : "=d" (tmp) : "0" (src->m32[2])); + asm volatile ("roxl.l #1,%0" + : "=d" (dest->mant.m32[1]) : "0" (src->m32[1])); + asm volatile ("roxl.l #1,%0" + : "=d" (dest->mant.m32[0]) : "0" (src->m32[0])); + dest->lowmant = tmp >> 24; + if (src->m32[3] || (tmp << 8)) + dest->lowmant |= 1; + break; + case 31: + asm volatile ("lsr.l #1,%1; roxr.l #1,%0" + : "=d" (dest->mant.m32[0]) + : "d" (src->m32[0]), "0" (src->m32[1])); + asm volatile ("roxr.l #1,%0" + : "=d" (dest->mant.m32[1]) : "0" (src->m32[2])); + asm volatile ("roxr.l #1,%0" + : "=d" (tmp) : "0" (src->m32[3])); + dest->lowmant = tmp >> 24; + if (src->m32[3] << 7) + dest->lowmant |= 1; + break; + case 32: + dest->mant.m32[0] = src->m32[1]; + dest->mant.m32[1] = src->m32[2]; + dest->lowmant = src->m32[3] >> 24; + if (src->m32[3] << 8) + dest->lowmant |= 1; + break; + } +} + +#if 0 /* old code... */ +extern inline int fls(unsigned int a) +{ + int r; + + asm volatile ("bfffo %1{#0,#32},%0" + : "=d" (r) : "md" (a)); + return r; +} + +/* fls = "find last set" (cf. ffs(3)) */ +extern inline int fls128(const int128 a) +{ + if (a[MSW128]) + return fls(a[MSW128]); + if (a[NMSW128]) + return fls(a[NMSW128]) + 32; + /* XXX: it probably never gets beyond this point in actual + use, but that's indicative of a more general problem in the + algorithm (i.e. as per the actual 68881 implementation, we + really only need at most 67 bits of precision [plus + overflow]) so I'm not going to fix it. */ + if (a[NLSW128]) + return fls(a[NLSW128]) + 64; + if (a[LSW128]) + return fls(a[LSW128]) + 96; + else + return -1; +} + +extern inline int zerop128(const int128 a) +{ + return !(a[LSW128] | a[NLSW128] | a[NMSW128] | a[MSW128]); +} + +extern inline int nonzerop128(const int128 a) +{ + return (a[LSW128] | a[NLSW128] | a[NMSW128] | a[MSW128]); +} + +/* Addition and subtraction */ +/* Do these in "pure" assembly, because "extended" asm is unmanageable + here */ +extern inline void add128(const int128 a, int128 b) +{ + /* rotating carry flags */ + unsigned int carry[2]; + + carry[0] = a[LSW128] > (0xffffffff - b[LSW128]); + b[LSW128] += a[LSW128]; + + carry[1] = a[NLSW128] > (0xffffffff - b[NLSW128] - carry[0]); + b[NLSW128] = a[NLSW128] + b[NLSW128] + carry[0]; + + carry[0] = a[NMSW128] > (0xffffffff - b[NMSW128] - carry[1]); + b[NMSW128] = a[NMSW128] + b[NMSW128] + carry[1]; + + b[MSW128] = a[MSW128] + b[MSW128] + carry[0]; +} + +/* Note: assembler semantics: "b -= a" */ +extern inline void sub128(const int128 a, int128 b) +{ + /* rotating borrow flags */ + unsigned int borrow[2]; + + borrow[0] = b[LSW128] < a[LSW128]; + b[LSW128] -= a[LSW128]; + + borrow[1] = b[NLSW128] < a[NLSW128] + borrow[0]; + b[NLSW128] = b[NLSW128] - a[NLSW128] - borrow[0]; + + borrow[0] = b[NMSW128] < a[NMSW128] + borrow[1]; + b[NMSW128] = b[NMSW128] - a[NMSW128] - borrow[1]; + + b[MSW128] = b[MSW128] - a[MSW128] - borrow[0]; +} + +/* Poor man's 64-bit expanding multiply */ +extern inline void mul64(unsigned long long a, + unsigned long long b, + int128 c) +{ + unsigned long long acc; + int128 acc128; + + zero128(acc128); + zero128(c); + + /* first the low words */ + if (LO_WORD(a) && LO_WORD(b)) { + acc = (long long) LO_WORD(a) * LO_WORD(b); + c[NLSW128] = HI_WORD(acc); + c[LSW128] = LO_WORD(acc); + } + /* Next the high words */ + if (HI_WORD(a) && HI_WORD(b)) { + acc = (long long) HI_WORD(a) * HI_WORD(b); + c[MSW128] = HI_WORD(acc); + c[NMSW128] = LO_WORD(acc); + } + /* The middle words */ + if (LO_WORD(a) && HI_WORD(b)) { + acc = (long long) LO_WORD(a) * HI_WORD(b); + acc128[NMSW128] = HI_WORD(acc); + acc128[NLSW128] = LO_WORD(acc); + add128(acc128, c); + } + /* The first and last words */ + if (HI_WORD(a) && LO_WORD(b)) { + acc = (long long) HI_WORD(a) * LO_WORD(b); + acc128[NMSW128] = HI_WORD(acc); + acc128[NLSW128] = LO_WORD(acc); + add128(acc128, c); + } +} + +/* Note: unsigned */ +extern inline int cmp128(int128 a, int128 b) +{ + if (a[MSW128] < b[MSW128]) + return -1; + if (a[MSW128] > b[MSW128]) + return 1; + if (a[NMSW128] < b[NMSW128]) + return -1; + if (a[NMSW128] > b[NMSW128]) + return 1; + if (a[NLSW128] < b[NLSW128]) + return -1; + if (a[NLSW128] > b[NLSW128]) + return 1; + + return (signed) a[LSW128] - b[LSW128]; +} + +inline void div128(int128 a, int128 b, int128 c) +{ + int128 mask; + + /* Algorithm: + + Shift the divisor until it's at least as big as the + dividend, keeping track of the position to which we've + shifted it, i.e. the power of 2 which we've multiplied it + by. + + Then, for this power of 2 (the mask), and every one smaller + than it, subtract the mask from the dividend and add it to + the quotient until the dividend is smaller than the raised + divisor. At this point, divide the dividend and the mask + by 2 (i.e. shift one place to the right). Lather, rinse, + and repeat, until there are no more powers of 2 left. */ + + /* FIXME: needless to say, there's room for improvement here too. */ + + /* Shift up */ + /* XXX: since it just has to be "at least as big", we can + probably eliminate this horribly wasteful loop. I will + have to prove this first, though */ + set128(0, 0, 0, 1, mask); + while (cmp128(b, a) < 0 && !btsthi128(b)) { + lslone128(b); + lslone128(mask); + } + + /* Shift down */ + zero128(c); + do { + if (cmp128(a, b) >= 0) { + sub128(b, a); + add128(mask, c); + } + lsrone128(mask); + lsrone128(b); + } while (nonzerop128(mask)); + + /* The remainder is in a... */ +} +#endif + +#endif /* MULTI_ARITH_H */ diff --git a/arch/m68k/mm/Makefile b/arch/m68k/mm/Makefile index 54f0fae1a..fdc73c35d 100644 --- a/arch/m68k/mm/Makefile +++ b/arch/m68k/mm/Makefile @@ -8,6 +8,13 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := mm.o -O_OBJS := init.o fault.o memory.o kmap.o extable.o hwtest.o +O_OBJS := init.o fault.o extable.o hwtest.o + +ifndef CONFIG_SUN3 +O_OBJS += kmap.o memory.o motorola.o +else +O_OBJS += sun3mmu.o +endif + include $(TOPDIR)/Rules.make diff --git a/arch/m68k/mm/fault.c b/arch/m68k/mm/fault.c index ef1b855bd..c66db0652 100644 --- a/arch/m68k/mm/fault.c +++ b/arch/m68k/mm/fault.c @@ -36,7 +36,7 @@ asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, struct mm_struct *mm = current->mm; struct vm_area_struct * vma; unsigned long fixup; - int write; + int write, fault; #ifdef DEBUG printk ("regs->sr=%#x, regs->pc=%#lx, address=%#lx, %ld, %p\n", @@ -44,12 +44,11 @@ asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, current->mm->pgd); #endif - /* * If we're in an interrupt or have no user * context, we must not take the fault.. */ - if (in_interrupt() || mm == &init_mm) + if (in_interrupt() || !mm) goto no_context; down(&mm->mmap_sem); @@ -100,7 +99,10 @@ good_area: * make sure we exit gracefully rather than endlessly redo * the fault. */ - if (!handle_mm_fault(current, vma, address, write)) + fault = handle_mm_fault(current, vma, address, write); + if (fault < 0) + goto out_of_memory; + if (!fault) goto do_sigbus; /* There seems to be a missing invalidate somewhere in do_no_page. @@ -160,6 +162,13 @@ no_context: * We ran out of memory, or some other thing happened to us that made * us unable to handle the page fault gracefully. */ +out_of_memory: + up(&mm->mmap_sem); + printk("VM: killing process %s\n", current->comm); + if (error_code & 4) + do_exit(SIGKILL); + goto no_context; + do_sigbus: up(&mm->mmap_sem); diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index 91409cd28..c22dccfc5 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -2,6 +2,9 @@ * linux/arch/m68k/mm/init.c * * Copyright (C) 1995 Hamish Macdonald + * + * Contains common initialization routines, specific init code moved + * to motorola.c and sun3mmu.c */ #include <linux/config.h> @@ -28,9 +31,10 @@ #include <asm/atari_stram.h> #endif -#undef DEBUG +#ifdef CONFIG_SUN3 +void mmu_emu_reserve_pages(unsigned long max_page); +#endif -extern void die_if_kernel(char *,struct pt_regs *,long); extern void show_net_buffers(void); int do_check_pgt_cache(int low, int high) @@ -60,7 +64,7 @@ int do_check_pgt_cache(int low, int high) * ZERO_PAGE is a special page that is used for zero-initialized * data and COW. */ -static unsigned long empty_bad_page_table; +unsigned long empty_bad_page_table; pte_t *__bad_pagetable(void) { @@ -68,7 +72,7 @@ pte_t *__bad_pagetable(void) return (pte_t *)empty_bad_page_table; } -static unsigned long empty_bad_page; +unsigned long empty_bad_page; pte_t __bad_page(void) { @@ -94,12 +98,12 @@ void show_mem(void) reserved++; else if (PageSwapCache(mem_map+i)) cached++; - else if (!atomic_read(&mem_map[i].count)) + else if (!page_count(mem_map+i)) free++; - else if (atomic_read(&mem_map[i].count) == 1) + else if (page_count(mem_map+i) == 1) nonshared++; else - shared += atomic_read(&mem_map[i].count) - 1; + shared += page_count(mem_map+i) - 1; } printk("%d pages of RAM\n",total); printk("%d free pages\n",free); @@ -113,167 +117,6 @@ void show_mem(void) #endif } -#ifndef mm_cachebits -/* - * Bits to add to page descriptors for "normal" caching mode. - * For 68020/030 this is 0. - * For 68040, this is _PAGE_CACHE040 (cachable, copyback) - */ -unsigned long mm_cachebits = 0; -#endif - -__initfunc(static pte_t * kernel_page_table(unsigned long *memavailp)) -{ - pte_t *ptablep; - - ptablep = (pte_t *)*memavailp; - *memavailp += PAGE_SIZE; - - clear_page((unsigned long)ptablep); - flush_page_to_ram((unsigned long) ptablep); - flush_tlb_kernel_page((unsigned long) ptablep); - nocache_page ((unsigned long)ptablep); - - return ptablep; -} - -static pmd_t *last_pgtable __initdata = NULL; - -__initfunc(static pmd_t * kernel_ptr_table(unsigned long *memavailp)) -{ - if (!last_pgtable) { - unsigned long pmd, last; - int i; - - /* Find the last ptr table that was used in head.S and - * reuse the remaining space in that page for further - * ptr tables. - */ - last = (unsigned long)kernel_pg_dir; - for (i = 0; i < PTRS_PER_PGD; i++) { - if (!pgd_present(kernel_pg_dir[i])) - continue; - pmd = pgd_page(kernel_pg_dir[i]); - if (pmd > last) - last = pmd; - } - - last_pgtable = (pmd_t *)last; -#ifdef DEBUG - printk("kernel_ptr_init: %p\n", last_pgtable); -#endif - } - - if (((unsigned long)(last_pgtable + PTRS_PER_PMD) & ~PAGE_MASK) == 0) { - last_pgtable = (pmd_t *)*memavailp; - *memavailp += PAGE_SIZE; - - clear_page((unsigned long)last_pgtable); - flush_page_to_ram((unsigned long)last_pgtable); - flush_tlb_kernel_page((unsigned long)last_pgtable); - nocache_page((unsigned long)last_pgtable); - } else - last_pgtable += PTRS_PER_PMD; - - return last_pgtable; -} - -__initfunc(static unsigned long -map_chunk (unsigned long addr, long size, unsigned long *memavailp)) -{ -#define PTRTREESIZE (256*1024) -#define ROOTTREESIZE (32*1024*1024) - static unsigned long virtaddr = 0; - unsigned long physaddr; - pgd_t *pgd_dir; - pmd_t *pmd_dir; - pte_t *pte_dir; - - physaddr = (addr | m68k_supervisor_cachemode | - _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY); - if (CPU_IS_040_OR_060) - physaddr |= _PAGE_GLOBAL040; - - while (size > 0) { -#ifdef DEBUG - if (!(virtaddr & (PTRTREESIZE-1))) - printk ("\npa=%#lx va=%#lx ", physaddr & PAGE_MASK, - virtaddr); -#endif - pgd_dir = pgd_offset_k(virtaddr); - if (virtaddr && CPU_IS_020_OR_030) { - if (!(virtaddr & (ROOTTREESIZE-1)) && - size >= ROOTTREESIZE) { -#ifdef DEBUG - printk ("[very early term]"); -#endif - pgd_val(*pgd_dir) = physaddr; - size -= ROOTTREESIZE; - virtaddr += ROOTTREESIZE; - physaddr += ROOTTREESIZE; - continue; - } - } - if (!pgd_present(*pgd_dir)) { - pmd_dir = kernel_ptr_table(memavailp); -#ifdef DEBUG - printk ("[new pointer %p]", pmd_dir); -#endif - pgd_set(pgd_dir, pmd_dir); - } else - pmd_dir = pmd_offset(pgd_dir, virtaddr); - - if (CPU_IS_020_OR_030) { - if (virtaddr) { -#ifdef DEBUG - printk ("[early term]"); -#endif - pmd_dir->pmd[(virtaddr/PTRTREESIZE) & 15] = physaddr; - physaddr += PTRTREESIZE; - } else { - int i; -#ifdef DEBUG - printk ("[zero map]"); -#endif - pte_dir = (pte_t *)kernel_ptr_table(memavailp); - pmd_dir->pmd[0] = virt_to_phys(pte_dir) | - _PAGE_TABLE | _PAGE_ACCESSED; - pte_val(*pte_dir++) = 0; - physaddr += PAGE_SIZE; - for (i = 1; i < 64; physaddr += PAGE_SIZE, i++) - pte_val(*pte_dir++) = physaddr; - } - size -= PTRTREESIZE; - virtaddr += PTRTREESIZE; - } else { - if (!pmd_present(*pmd_dir)) { -#ifdef DEBUG - printk ("[new table]"); -#endif - pte_dir = kernel_page_table(memavailp); - pmd_set(pmd_dir, pte_dir); - } - pte_dir = pte_offset(pmd_dir, virtaddr); - - if (virtaddr) { - if (!pte_present(*pte_dir)) - pte_val(*pte_dir) = physaddr; - } else - pte_val(*pte_dir) = 0; - size -= PAGE_SIZE; - virtaddr += PAGE_SIZE; - physaddr += PAGE_SIZE; - } - - } -#ifdef DEBUG - printk("\n"); -#endif - - return virtaddr; -} - -extern unsigned long free_area_init(unsigned long, unsigned long); extern void init_pointer_table(unsigned long ptable); /* References to section boundaries */ @@ -281,121 +124,9 @@ extern void init_pointer_table(unsigned long ptable); extern char _text, _etext, _edata, __bss_start, _end; extern char __init_begin, __init_end; -/* - * paging_init() continues the virtual memory environment setup which - * was begun by the code in arch/head.S. - */ -__initfunc(unsigned long paging_init(unsigned long start_mem, - unsigned long end_mem)) -{ - int chunk; - unsigned long mem_avail = 0; - -#ifdef DEBUG - { - extern unsigned long availmem; - printk ("start of paging_init (%p, %lx, %lx, %lx)\n", - kernel_pg_dir, availmem, start_mem, end_mem); - } -#endif - - /* Fix the cache mode in the page descriptors for the 680[46]0. */ - if (CPU_IS_040_OR_060) { - int i; -#ifndef mm_cachebits - mm_cachebits = _PAGE_CACHE040; -#endif - for (i = 0; i < 16; i++) - pgprot_val(protection_map[i]) |= _PAGE_CACHE040; - } - /* Fix the PAGE_NONE value. */ - if (CPU_IS_040_OR_060) { - /* On the 680[46]0 we can use the _PAGE_SUPER bit. */ - pgprot_val(protection_map[0]) |= _PAGE_SUPER; - pgprot_val(protection_map[VM_SHARED]) |= _PAGE_SUPER; - } else { - /* Otherwise we must fake it. */ - pgprot_val(protection_map[0]) &= ~_PAGE_PRESENT; - pgprot_val(protection_map[0]) |= _PAGE_FAKE_SUPER; - pgprot_val(protection_map[VM_SHARED]) &= ~_PAGE_PRESENT; - pgprot_val(protection_map[VM_SHARED]) |= _PAGE_FAKE_SUPER; - } - - /* - * Map the physical memory available into the kernel virtual - * address space. It may allocate some memory for page - * tables and thus modify availmem. - */ - - for (chunk = 0; chunk < m68k_num_memory; chunk++) { - mem_avail = map_chunk (m68k_memory[chunk].addr, - m68k_memory[chunk].size, &start_mem); - - } - - flush_tlb_all(); -#ifdef DEBUG - printk ("memory available is %ldKB\n", mem_avail >> 10); - printk ("start_mem is %#lx\nvirtual_end is %#lx\n", - start_mem, end_mem); -#endif - - /* - * initialize the bad page table and bad page to point - * to a couple of allocated pages - */ - empty_bad_page_table = start_mem; - start_mem += PAGE_SIZE; - empty_bad_page = start_mem; - start_mem += PAGE_SIZE; - empty_zero_page = start_mem; - start_mem += PAGE_SIZE; - memset((void *)empty_zero_page, 0, PAGE_SIZE); - - /* - * allocate the "swapper" page directory and - * record in task 0 (swapper) tss - */ - init_mm.pgd = (pgd_t *)kernel_ptr_table(&start_mem); - memset (init_mm.pgd, 0, sizeof(pgd_t)*PTRS_PER_PGD); - - /* setup CPU root pointer for swapper task */ - task[0]->tss.crp[0] = 0x80000000 | _PAGE_TABLE; - task[0]->tss.crp[1] = virt_to_phys(init_mm.pgd); +extern pmd_t *zero_pgtable; -#ifdef DEBUG - printk ("task 0 pagedir at %p virt, %#lx phys\n", - swapper_pg_dir, task[0]->tss.crp[1]); -#endif - - if (CPU_IS_040_OR_060) - asm __volatile__ (".chip 68040\n\t" - "movec %0,%%urp\n\t" - ".chip 68k" - : /* no outputs */ - : "r" (task[0]->tss.crp[1])); - else - asm __volatile__ (".chip 68030\n\t" - "pmove %0,%%crp\n\t" - ".chip 68k" - : /* no outputs */ - : "m" (task[0]->tss.crp[0])); -#ifdef DEBUG - printk ("set crp\n"); -#endif - - /* - * Set up SFC/DFC registers (user data space) - */ - set_fs (USER_DS); - -#ifdef DEBUG - printk ("before free_area_init\n"); -#endif - return PAGE_ALIGN(free_area_init(start_mem, end_mem)); -} - -__initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) +void __init mem_init(unsigned long start_mem, unsigned long end_mem) { int codepages = 0; int datapages = 0; @@ -418,31 +149,36 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) atari_stram_reserve_pages( start_mem ); #endif - for (tmp = 0 ; tmp < end_mem ; tmp += PAGE_SIZE) { +#ifdef CONFIG_SUN3 + /* reserve rom pages */ + mmu_emu_reserve_pages(max_mapnr); +#endif + + for (tmp = PAGE_OFFSET ; tmp < end_mem ; tmp += PAGE_SIZE) { +#ifndef CONFIG_SUN3 if (virt_to_phys ((void *)tmp) >= mach_max_dma_address) clear_bit(PG_DMA, &mem_map[MAP_NR(tmp)].flags); +#endif if (PageReserved(mem_map+MAP_NR(tmp))) { if (tmp >= (unsigned long)&_text - && tmp < (unsigned long)&_edata) { - if (tmp < (unsigned long) &_etext) - codepages++; - else - datapages++; - } else if (tmp >= (unsigned long) &__init_begin - && tmp < (unsigned long) &__init_end) + && tmp < (unsigned long)&_etext) + codepages++; + else if (tmp >= (unsigned long) &__init_begin + && tmp < (unsigned long) &__init_end) initpages++; else datapages++; continue; } - atomic_set(&mem_map[MAP_NR(tmp)].count, 1); + set_page_count(mem_map+MAP_NR(tmp), 1); #ifdef CONFIG_BLK_DEV_INITRD if (!initrd_start || (tmp < (initrd_start & PAGE_MASK) || tmp >= initrd_end)) #endif free_page(tmp); } - + +#ifndef CONFIG_SUN3 /* insert pointer tables allocated so far into the tablelist */ init_pointer_table((unsigned long)kernel_pg_dir); for (i = 0; i < PTRS_PER_PGD; i++) { @@ -450,6 +186,11 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) init_pointer_table(pgd_page(kernel_pg_dir[i])); } + /* insert also pointer table that we used to unmap the zero page */ + if (zero_pgtable) + init_pointer_table((unsigned long)zero_pgtable); +#endif + printk("Memory: %luk/%luk available (%dk kernel code, %dk data, %dk init)\n", (unsigned long) nr_free_pages << (PAGE_SHIFT-10), max_mapnr << (PAGE_SHIFT-10), @@ -458,18 +199,6 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) initpages << (PAGE_SHIFT-10)); } -void free_initmem(void) -{ - unsigned long addr; - - addr = (unsigned long)&__init_begin; - for (; addr < (unsigned long)&__init_end; addr += PAGE_SIZE) { - mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved); - atomic_set(&mem_map[MAP_NR(addr)].count, 1); - free_page(addr); - } -} - void si_meminfo(struct sysinfo *val) { unsigned long i; @@ -483,9 +212,9 @@ void si_meminfo(struct sysinfo *val) if (PageReserved(mem_map+i)) continue; val->totalram++; - if (!atomic_read(&mem_map[i].count)) + if (!page_count(mem_map+i)) continue; - val->sharedram += atomic_read(&mem_map[i].count) - 1; + val->sharedram += page_count(mem_map+i) - 1; } val->totalram <<= PAGE_SHIFT; val->sharedram <<= PAGE_SHIFT; diff --git a/arch/m68k/mm/memory.c b/arch/m68k/mm/memory.c index a6d496571..0bf9691dd 100644 --- a/arch/m68k/mm/memory.c +++ b/arch/m68k/mm/memory.c @@ -98,7 +98,7 @@ static ptable_desc ptable_list = { &ptable_list, &ptable_list }; #define PTABLE_SIZE (PTRS_PER_PMD * sizeof(pmd_t)) -__initfunc(void init_pointer_table(unsigned long ptable)) +void __init init_pointer_table(unsigned long ptable) { ptable_desc *dp; unsigned long page = ptable & PAGE_MASK; @@ -227,31 +227,33 @@ static unsigned long transp_transl_matches( unsigned long regval, /* address match? */ base = regval & 0xff000000; mask = ~(regval << 8) & 0xff000000; - return ((vaddr ^ base) & mask) == 0; + return (((unsigned long)vaddr ^ base) & mask) == 0; } +#if DEBUG_INVALID_PTOV +int mm_inv_cnt = 5; +#endif + #ifndef CONFIG_SINGLE_MEMORY_CHUNK /* * The following two routines map from a physical address to a kernel * virtual address and vice versa. */ -unsigned long mm_vtop (unsigned long vaddr) +unsigned long mm_vtop(unsigned long vaddr) { int i=0; - unsigned long voff = vaddr; - unsigned long offset = 0; + unsigned long voff = (unsigned long)vaddr - PAGE_OFFSET; - do{ - if (voff < offset + m68k_memory[i].size) { + do { + if (voff < m68k_memory[i].size) { #ifdef DEBUGPV - printk ("VTOP(%lx)=%lx\n", vaddr, - m68k_memory[i].addr + voff - offset); + printk ("VTOP(%p)=%lx\n", vaddr, + m68k_memory[i].addr + voff); #endif - return m68k_memory[i].addr + voff - offset; - } else - offset += m68k_memory[i].size; - i++; - }while (i < m68k_num_memory); + return m68k_memory[i].addr + voff; + } + voff -= m68k_memory[i].size; + } while (++i < m68k_num_memory); return mm_vtop_fallback(vaddr); } @@ -259,7 +261,7 @@ unsigned long mm_vtop (unsigned long vaddr) /* Separate function to make the common case faster (needs to save less registers) */ -unsigned long mm_vtop_fallback (unsigned long vaddr) +unsigned long mm_vtop_fallback(unsigned long vaddr) { /* not in one of the memory chunks; test for applying transparent * translation */ @@ -272,13 +274,13 @@ unsigned long mm_vtop_fallback (unsigned long vaddr) ".chip 68k" : : "a" (&ttreg) ); if (transp_transl_matches( ttreg, vaddr )) - return vaddr; + return (unsigned long)vaddr; asm volatile( ".chip 68030\n\t" "pmove %/tt1,%0@\n\t" ".chip 68k" : : "a" (&ttreg) ); if (transp_transl_matches( ttreg, vaddr )) - return vaddr; + return (unsigned long)vaddr; } else if (CPU_IS_040_OR_060) { unsigned long ttreg; @@ -288,13 +290,13 @@ unsigned long mm_vtop_fallback (unsigned long vaddr) ".chip 68k" : "=d" (ttreg) ); if (transp_transl_matches( ttreg, vaddr )) - return vaddr; + return (unsigned long)vaddr; asm volatile( ".chip 68040\n\t" "movec %%dtt1,%0\n\t" ".chip 68k" : "=d" (ttreg) ); if (transp_transl_matches( ttreg, vaddr )) - return vaddr; + return (unsigned long)vaddr; } /* no match, too, so get the actual physical address from the MMU. */ @@ -306,11 +308,21 @@ unsigned long mm_vtop_fallback (unsigned long vaddr) set_fs (MAKE_MM_SEG(SUPER_DATA)); /* The PLPAR instruction causes an access error if the translation - * is not possible. We don't catch that here, so a bad kernel trap - * will be reported in this case. */ - asm volatile (".chip 68060\n\t" - "plpar (%0)\n\t" - ".chip 68k" + * is not possible. To catch this we use the same exception mechanism + * as for user space accesses in <asm/uaccess.h>. */ + asm volatile (".chip 68060\n" + "1: plpar (%0)\n" + ".chip 68k\n" + "2:\n" + ".section .fixup,\"ax\"\n" + " .even\n" + "3: lea -1,%0\n" + " jra 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b,3b\n" + ".previous" : "=a" (paddr) : "0" (vaddr)); set_fs (fs); @@ -332,12 +344,13 @@ unsigned long mm_vtop_fallback (unsigned long vaddr) set_fs (fs); if (mmusr & MMU_T_040) { - return (vaddr); /* Transparent translation */ + return (unsigned long)vaddr; /* Transparent translation */ } if (mmusr & MMU_R_040) - return (mmusr & PAGE_MASK) | (vaddr & (PAGE_SIZE-1)); + return (mmusr & PAGE_MASK) | ((unsigned long)vaddr & (PAGE_SIZE-1)); - panic ("VTOP040: bad virtual address %08lx (%lx)", vaddr, mmusr); + printk("VTOP040: bad virtual address %lx (%lx)", vaddr, mmusr); + return -1; } else { volatile unsigned short temp; unsigned short mmusr; @@ -350,46 +363,51 @@ unsigned long mm_vtop_fallback (unsigned long vaddr) mmusr = temp; if (mmusr & (MMU_I|MMU_B|MMU_L)) - panic ("VTOP030: bad virtual address %08lx (%x)", vaddr, mmusr); + printk("VTOP030: bad virtual address %lx (%x)\n", vaddr, mmusr); descaddr = phys_to_virt((unsigned long)descaddr); switch (mmusr & MMU_NUM) { case 1: - return (*descaddr & 0xfe000000) | (vaddr & 0x01ffffff); + return (*descaddr & 0xfe000000) | ((unsigned long)vaddr & 0x01ffffff); case 2: - return (*descaddr & 0xfffc0000) | (vaddr & 0x0003ffff); + return (*descaddr & 0xfffc0000) | ((unsigned long)vaddr & 0x0003ffff); case 3: - return (*descaddr & PAGE_MASK) | (vaddr & (PAGE_SIZE-1)); + return (*descaddr & PAGE_MASK) | ((unsigned long)vaddr & (PAGE_SIZE-1)); default: - panic ("VTOP: bad levels (%u) for virtual address %08lx", + printk("VTOP: bad levels (%u) for virtual address %lx\n", mmusr & MMU_NUM, vaddr); } } - panic ("VTOP: bad virtual address %08lx", vaddr); + printk("VTOP: bad virtual address %lx\n", vaddr); + return -1; } #ifndef CONFIG_SINGLE_MEMORY_CHUNK unsigned long mm_ptov (unsigned long paddr) { int i = 0; - unsigned long offset = 0; + unsigned long poff, voff = PAGE_OFFSET; - do{ - if (paddr >= m68k_memory[i].addr && - paddr < (m68k_memory[i].addr - + m68k_memory[i].size)) { + do { + poff = paddr - m68k_memory[i].addr; + if (poff < m68k_memory[i].size) { #ifdef DEBUGPV - printk ("PTOV(%lx)=%lx\n", paddr, - (paddr - m68k_memory[i].addr) + offset); + printk ("PTOV(%lx)=%lx\n", paddr, poff + voff); +#endif + return poff + voff; + } + voff += m68k_memory[i].size; + } while (++i < m68k_num_memory); + +#if DEBUG_INVALID_PTOV + if (mm_inv_cnt > 0) { + mm_inv_cnt--; + printk("Invalid use of phys_to_virt(0x%lx) at 0x%p!\n", + paddr, __builtin_return_address(0)); + } #endif - return (paddr - m68k_memory[i].addr) + offset; - } else - offset += m68k_memory[i].size; - i++; - }while (i < m68k_num_memory); - /* * assume that the kernel virtual address is the same as the * physical address. @@ -413,7 +431,7 @@ unsigned long mm_ptov (unsigned long paddr) if (MACH_IS_AMIGA && paddr < 16*1024*1024) return ZTWO_VADDR(paddr); #endif - return paddr; + return -1; } #endif @@ -473,14 +491,7 @@ unsigned long mm_ptov (unsigned long paddr) * this?). So we have to push first and then additionally to invalidate. */ -#ifdef CONFIG_M68K_L2_CACHE -/* - * Jes was worried about performance (urhh ???) so its optional - */ - -void (*mach_l2_flush)(int) = NULL; -#endif - + /* * cache_clear() semantics: Clear any cache entries for the area in question, * without writing back dirty entries first. This is useful if the data will diff --git a/arch/m68k/mm/motorola.c b/arch/m68k/mm/motorola.c new file mode 100644 index 000000000..7b463b895 --- /dev/null +++ b/arch/m68k/mm/motorola.c @@ -0,0 +1,299 @@ +/* + * linux/arch/m68k/motorola.c + * + * Routines specific to the Motorola MMU, originally from: + * linux/arch/m68k/init.c + * which are Copyright (C) 1995 Hamish Macdonald + * + * Moved 8/20/1999 Sam Creasey + */ + +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/swap.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/init.h> +#ifdef CONFIG_BLK_DEV_RAM +#include <linux/blk.h> +#endif + +#include <asm/setup.h> +#include <asm/uaccess.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/machdep.h> +#include <asm/io.h> +#ifdef CONFIG_ATARI +#include <asm/atari_stram.h> +#endif + +#undef DEBUG + +#ifndef mm_cachebits +/* + * Bits to add to page descriptors for "normal" caching mode. + * For 68020/030 this is 0. + * For 68040, this is _PAGE_CACHE040 (cachable, copyback) + */ +unsigned long mm_cachebits = 0; +#endif + +static pte_t * __init kernel_page_table(unsigned long *memavailp) +{ + pte_t *ptablep; + + ptablep = (pte_t *)*memavailp; + *memavailp += PAGE_SIZE; + + clear_page((unsigned long)ptablep); + flush_page_to_ram((unsigned long) ptablep); + flush_tlb_kernel_page((unsigned long) ptablep); + nocache_page ((unsigned long)ptablep); + + return ptablep; +} + +static pmd_t *last_pgtable __initdata = NULL; +pmd_t *zero_pgtable __initdata = NULL; + +static pmd_t * __init kernel_ptr_table(unsigned long *memavailp) +{ + if (!last_pgtable) { + unsigned long pmd, last; + int i; + + /* Find the last ptr table that was used in head.S and + * reuse the remaining space in that page for further + * ptr tables. + */ + last = (unsigned long)kernel_pg_dir; + for (i = 0; i < PTRS_PER_PGD; i++) { + if (!pgd_present(kernel_pg_dir[i])) + continue; + pmd = pgd_page(kernel_pg_dir[i]); + if (pmd > last) + last = pmd; + } + + last_pgtable = (pmd_t *)last; +#ifdef DEBUG + printk("kernel_ptr_init: %p\n", last_pgtable); +#endif + } + + if (((unsigned long)(last_pgtable + PTRS_PER_PMD) & ~PAGE_MASK) == 0) { + last_pgtable = (pmd_t *)*memavailp; + *memavailp += PAGE_SIZE; + + clear_page((unsigned long)last_pgtable); + flush_page_to_ram((unsigned long)last_pgtable); + flush_tlb_kernel_page((unsigned long)last_pgtable); + nocache_page((unsigned long)last_pgtable); + } else + last_pgtable += PTRS_PER_PMD; + + return last_pgtable; +} + +static unsigned long __init +map_chunk (unsigned long addr, long size, unsigned long *memavailp) +{ +#define PTRTREESIZE (256*1024) +#define ROOTTREESIZE (32*1024*1024) + static unsigned long virtaddr = PAGE_OFFSET; + unsigned long physaddr; + pgd_t *pgd_dir; + pmd_t *pmd_dir; + pte_t *pte_dir; + + physaddr = (addr | m68k_supervisor_cachemode | + _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY); + if (CPU_IS_040_OR_060) + physaddr |= _PAGE_GLOBAL040; + + while (size > 0) { +#ifdef DEBUG + if (!(virtaddr & (PTRTREESIZE-1))) + printk ("\npa=%#lx va=%#lx ", physaddr & PAGE_MASK, + virtaddr); +#endif + pgd_dir = pgd_offset_k(virtaddr); + if (virtaddr && CPU_IS_020_OR_030) { + if (!(virtaddr & (ROOTTREESIZE-1)) && + size >= ROOTTREESIZE) { +#ifdef DEBUG + printk ("[very early term]"); +#endif + pgd_val(*pgd_dir) = physaddr; + size -= ROOTTREESIZE; + virtaddr += ROOTTREESIZE; + physaddr += ROOTTREESIZE; + continue; + } + } + if (!pgd_present(*pgd_dir)) { + pmd_dir = kernel_ptr_table(memavailp); +#ifdef DEBUG + printk ("[new pointer %p]", pmd_dir); +#endif + pgd_set(pgd_dir, pmd_dir); + } else + pmd_dir = pmd_offset(pgd_dir, virtaddr); + + if (CPU_IS_020_OR_030) { + if (virtaddr) { +#ifdef DEBUG + printk ("[early term]"); +#endif + pmd_dir->pmd[(virtaddr/PTRTREESIZE) & 15] = physaddr; + physaddr += PTRTREESIZE; + } else { + int i; +#ifdef DEBUG + printk ("[zero map]"); +#endif + zero_pgtable = kernel_ptr_table(memavailp); + pte_dir = (pte_t *)zero_pgtable; + pmd_dir->pmd[0] = virt_to_phys(pte_dir) | + _PAGE_TABLE | _PAGE_ACCESSED; + pte_val(*pte_dir++) = 0; + physaddr += PAGE_SIZE; + for (i = 1; i < 64; physaddr += PAGE_SIZE, i++) + pte_val(*pte_dir++) = physaddr; + } + size -= PTRTREESIZE; + virtaddr += PTRTREESIZE; + } else { + if (!pmd_present(*pmd_dir)) { +#ifdef DEBUG + printk ("[new table]"); +#endif + pte_dir = kernel_page_table(memavailp); + pmd_set(pmd_dir, pte_dir); + } + pte_dir = pte_offset(pmd_dir, virtaddr); + + if (virtaddr) { + if (!pte_present(*pte_dir)) + pte_val(*pte_dir) = physaddr; + } else + pte_val(*pte_dir) = 0; + size -= PAGE_SIZE; + virtaddr += PAGE_SIZE; + physaddr += PAGE_SIZE; + } + + } +#ifdef DEBUG + printk("\n"); +#endif + + return virtaddr; +} + +extern unsigned long free_area_init(unsigned long, unsigned long); +extern unsigned long empty_bad_page_table; +extern unsigned long empty_bad_page; + +/* + * paging_init() continues the virtual memory environment setup which + * was begun by the code in arch/head.S. + */ +unsigned long __init paging_init(unsigned long start_mem, + unsigned long end_mem) +{ + int chunk; + unsigned long mem_avail = 0; + +#ifdef DEBUG + { + extern unsigned long availmem; + printk ("start of paging_init (%p, %lx, %lx, %lx)\n", + kernel_pg_dir, availmem, start_mem, end_mem); + } +#endif + + /* Fix the cache mode in the page descriptors for the 680[46]0. */ + if (CPU_IS_040_OR_060) { + int i; +#ifndef mm_cachebits + mm_cachebits = _PAGE_CACHE040; +#endif + for (i = 0; i < 16; i++) + pgprot_val(protection_map[i]) |= _PAGE_CACHE040; + } + /* Fix the PAGE_NONE value. */ + if (CPU_IS_040_OR_060) { + /* On the 680[46]0 we can use the _PAGE_SUPER bit. */ + pgprot_val(protection_map[0]) |= _PAGE_SUPER; + pgprot_val(protection_map[VM_SHARED]) |= _PAGE_SUPER; + } else { + /* Otherwise we must fake it. */ + pgprot_val(protection_map[0]) &= ~_PAGE_PRESENT; + pgprot_val(protection_map[0]) |= _PAGE_FAKE_SUPER; + pgprot_val(protection_map[VM_SHARED]) &= ~_PAGE_PRESENT; + pgprot_val(protection_map[VM_SHARED]) |= _PAGE_FAKE_SUPER; + } + + /* + * Map the physical memory available into the kernel virtual + * address space. It may allocate some memory for page + * tables and thus modify availmem. + */ + + for (chunk = 0; chunk < m68k_num_memory; chunk++) { + mem_avail = map_chunk (m68k_memory[chunk].addr, + m68k_memory[chunk].size, &start_mem); + + } + + flush_tlb_all(); +#ifdef DEBUG + printk ("memory available is %ldKB\n", mem_avail >> 10); + printk ("start_mem is %#lx\nvirtual_end is %#lx\n", + start_mem, end_mem); +#endif + + /* + * initialize the bad page table and bad page to point + * to a couple of allocated pages + */ + empty_bad_page_table = start_mem; + start_mem += PAGE_SIZE; + empty_bad_page = start_mem; + start_mem += PAGE_SIZE; + empty_zero_page = start_mem; + start_mem += PAGE_SIZE; + memset((void *)empty_zero_page, 0, PAGE_SIZE); + + /* + * Set up SFC/DFC registers (user data space) + */ + set_fs (USER_DS); + +#ifdef DEBUG + printk ("before free_area_init\n"); +#endif + return PAGE_ALIGN(free_area_init(start_mem, end_mem)); +} + +extern char __init_begin, __init_end; + +void free_initmem(void) +{ + unsigned long addr; + + addr = (unsigned long)&__init_begin; + for (; addr < (unsigned long)&__init_end; addr += PAGE_SIZE) { + mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved); + set_page_count(mem_map+MAP_NR(addr), 1); + free_page(addr); + } +} + + diff --git a/arch/m68k/mm/sun3mmu.c b/arch/m68k/mm/sun3mmu.c new file mode 100644 index 000000000..0d3277de0 --- /dev/null +++ b/arch/m68k/mm/sun3mmu.c @@ -0,0 +1,98 @@ +/* + * linux/arch/m68k/mm/sun3mmu.c + * + * Implementations of mm routines specific to the sun3 MMU. + * + * Moved here 8/20/1999 Sam Creasey + * + */ + +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/swap.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/init.h> +#ifdef CONFIG_BLK_DEV_RAM +#include <linux/blk.h> +#endif + +#include <asm/setup.h> +#include <asm/uaccess.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/machdep.h> +#include <asm/io.h> + +extern void mmu_emu_init (void); + +extern unsigned long free_area_init(unsigned long, unsigned long); + +const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n"; + +extern unsigned long empty_bad_page_table; +extern unsigned long empty_bad_page; + +void free_initmem(void) +{ +} +/* For the sun3 we try to follow the i386 paging_init() more closely */ +/* start_mem and end_mem have PAGE_OFFSET added already */ +/* now sets up tables using sun3 PTEs rather than i386 as before. --m */ +unsigned long __init paging_init(unsigned long start_mem, + unsigned long end_mem) +{ + pgd_t * pg_dir; + pte_t * pg_table; + int i; + unsigned long address; + +#ifdef TEST_VERIFY_AREA + wp_works_ok = 0; +#endif + start_mem = PAGE_ALIGN(start_mem); + empty_bad_page_table = start_mem; + start_mem += PAGE_SIZE; + empty_bad_page = start_mem; + start_mem += PAGE_SIZE; + empty_zero_page = start_mem; + start_mem += PAGE_SIZE; + memset((void *)empty_zero_page, 0, PAGE_SIZE); + + address = PAGE_OFFSET; + pg_dir = swapper_pg_dir; + memset (swapper_pg_dir, 0, sizeof (swapper_pg_dir)); + memset (kernel_pg_dir, 0, sizeof (kernel_pg_dir)); + + /* Map whole memory from PAGE_OFFSET (0x0E000000) */ + pg_dir += PAGE_OFFSET >> PGDIR_SHIFT; + + while (address < end_mem) { + pg_table = (pte_t *) __pa (start_mem); + start_mem += PTRS_PER_PTE * sizeof (pte_t); + pgd_val(*pg_dir) = (unsigned long) pg_table; + pg_dir++; + + /* now change pg_table to kernel virtual addresses */ + pg_table = (pte_t *) __va ((unsigned long) pg_table); + for (i=0; i<PTRS_PER_PTE; ++i, ++pg_table) { + pte_t pte = mk_pte (address, PAGE_INIT); + if (address >= end_mem) + pte_val (pte) = 0; + set_pte (pg_table, pte); + address += PAGE_SIZE; + } + } + + mmu_emu_init(); + + current->mm = NULL; + + return PAGE_ALIGN(free_area_init(start_mem, end_mem)); +} + + diff --git a/arch/m68k/mvme147/config.c b/arch/m68k/mvme147/config.c index 2fe61766a..774690012 100644 --- a/arch/m68k/mvme147/config.c +++ b/arch/m68k/mvme147/config.c @@ -13,7 +13,6 @@ * for more details. */ -#include <stdarg.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/mm.h> @@ -91,7 +90,7 @@ static int mvme147_get_hardware_list(char *buffer) } -__initfunc(void config_mvme147(void)) +void __init config_mvme147(void) { mach_sched_init = mvme147_sched_init; mach_keyb_init = mvme147_keyb_init; diff --git a/arch/m68k/mvme16x/config.c b/arch/m68k/mvme16x/config.c index db7eb3137..2f5d5689a 100644 --- a/arch/m68k/mvme16x/config.c +++ b/arch/m68k/mvme16x/config.c @@ -15,7 +15,6 @@ */ #include <linux/config.h> -#include <stdarg.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/mm.h> @@ -132,7 +131,7 @@ static int mvme16x_get_hardware_list(char *buffer) #define PccSCCTICR 0x1e #define PccSCCRICR 0x1f -__initfunc(void config_mvme16x(void)) +void __init config_mvme16x(void) { p_bdid p = (p_bdid)mvme_bdid_ptr; char id[40]; diff --git a/arch/m68k/mvme16x/rtc.c b/arch/m68k/mvme16x/rtc.c index b9485e54d..75efb8bd6 100644 --- a/arch/m68k/mvme16x/rtc.c +++ b/arch/m68k/mvme16x/rtc.c @@ -155,7 +155,7 @@ static struct miscdevice rtc_dev= &rtc_fops }; -__initfunc(int rtc_MK48T08_init(void)) +int __init rtc_MK48T08_init(void) { if (!MACH_IS_MVME16x) return -ENODEV; diff --git a/arch/m68k/q40/README b/arch/m68k/q40/README index 5433796cb..60134d9de 100644 --- a/arch/m68k/q40/README +++ b/arch/m68k/q40/README @@ -7,7 +7,7 @@ available from this place and ftp.uni-erlangen.de/linux/680x0/q40/ and mirrors. Hints to documentation usually refer to the linux source tree in -/usr/src/linux unless URL given. +/usr/src/linux/Documentation unless URL given. It seems IRQ unmasking can't be safely done on a Q40. Autoprobing is not yet implemented - do not try it! (See below) @@ -18,8 +18,8 @@ particular device drivers. The floppy imposes a very high interrupt load on the CPU, approx 30K/s. When something blocks interrupts (HD) it will loose some of them, so far this is not known to have caused any data loss. On hihgly loaded systems -it can make the floppy very slow. Other Q40 OS' simply poll the floppy -for this reason - something that can't be done in Linux. +it can make the floppy very slow or practicaly stop. Other Q40 OS' simply +poll the floppy for this reason - something that can't be done in Linux. Only possible cure is getting a 82072 contoler with fifo instead of the 8272A @@ -48,7 +48,7 @@ Debugging Upon startup the kernel will usually output "ABCQGHIJ" into the SRAM, preceded by the booter signature. This is a trace just in case something went wrong during earliest setup stages. -*Changed* to preserve SRAM contents by default, this is only done when +**Changed** to preserve SRAM contents by default, this is only done when requested - SRAM must start with '%LX$' signature to do this. '-d' option to 'lxx' loader enables this. @@ -92,12 +92,22 @@ Interrupts ========== q40 master chip handles only level triggered interrupts :-(( -further limitation is no disabling etc. Unless someone finds -some ingenious clue this means autoprobing will never work. -Parallel port interrupts cause most trouble.. - -IRQ sharing is not yet implemented. - +further limitation is no disabling etc. There is NO WAY to remove +an ISA irq request other than serve the HW specific control register, +the ISA irq lines are connected straight to the CPU ipl1 pin.. + +IRQ sharing is not yet implemented but this should be only a minor +problem.. + +Linux has some requirements wrt interrupt architecture, these are +to my knowledge: + (a) interrupt handler must not be reentered even when sti() is called + (b) working enable/disable_irq + +Luckily these requirements are only important for drivers shared +with other architectures - ide,serial,parallel, ethernet.. +q40ints.c now contains a trivial hack for (a), however (b) could +be only solved by driver-specific code Keyboard ======== diff --git a/arch/m68k/q40/config.c b/arch/m68k/q40/config.c index 0c6871f88..df0a57762 100644 --- a/arch/m68k/q40/config.c +++ b/arch/m68k/q40/config.c @@ -1,18 +1,17 @@ /* * arch/m68k/q40/config.c * + * Copyright (C) 1999 Richard Zidlicky + * * originally based on: * * linux/bvme/config.c * - * Copyright (C) 1993 Hamish Macdonald - * * This file is subject to the terms and conditions of the GNU General Public * License. See the file README.legal in the main directory of this archive * for more details. */ -#include <stdarg.h> #include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> @@ -24,6 +23,7 @@ #include <linux/init.h> #include <linux/major.h> +#include <asm/rtc.h> #include <asm/bootinfo.h> #include <asm/system.h> #include <asm/pgtable.h> @@ -135,12 +135,11 @@ int q40_kbdrate (struct kbd_repeat *k) void q40_reset() { - printk ("\n\n*******************************************\n" - "Called q40_reset : press the RESET button!! \n"); - printk( "*******************************************\n"); - - while(1) - ; + printk ("\n\n*******************************************\n" + "Called q40_reset : press the RESET button!! \n" + "*******************************************\n"); + + while(1) ; } static void q40_get_model(char *model) @@ -158,7 +157,7 @@ static int q40_get_hardware_list(char *buffer) } -__initfunc(void config_q40(void)) +void __init config_q40(void) { mach_sched_init = q40_sched_init; /* ok */ /*mach_kbdrate = q40_kbdrate;*/ /* unneeded ?*/ @@ -233,6 +232,9 @@ void q40_mksound(unsigned int hz, unsigned int ticks) } static void (*q40_timer_routine)(int, void *, struct pt_regs *); +static short rtc_oldsecs=0; +unsigned rtc_irq_flags=0; +unsigned rtc_irq_ctrl=0; static void q40_timer_int (int irq, void *dev_id, struct pt_regs *fp) { @@ -251,6 +253,14 @@ static void q40_timer_int (int irq, void *dev_id, struct pt_regs *fp) *DAC_LEFT=sval; *DAC_RIGHT=sval; } +#ifdef CONFIG_Q40RTC + if (rtc_irq_ctrl && (rtc_oldsecs != RTC_SECS)) + { + rtc_oldsecs = RTC_SECS; + rtc_irq_flags = RTC_UIE; + rtc_interrupt(); + } +#endif if (ql_ticks) return; #endif q40_timer_routine(irq, dev_id, fp); diff --git a/arch/m68k/q40/q40ints.c b/arch/m68k/q40/q40ints.c index e9b5a5b1f..c6625aad8 100644 --- a/arch/m68k/q40/q40ints.c +++ b/arch/m68k/q40/q40ints.c @@ -29,7 +29,7 @@ * 3,4,5,6,7,10,11,14,15 : ISA dev IRQs * 16-31: reserved * 32 : keyboard int - * 33 : frame int (50 Hz periodic timer) + * 33 : frame int (50/200 Hz periodic timer) * 34 : sample int (10/20 KHz periodic timer) * */ @@ -45,20 +45,21 @@ extern void (*q40_sys_default_handler[]) (int, void *, struct pt_regs *); /* ad static void q40_defhand (int irq, void *dev_id, struct pt_regs *fp); static void sys_default_handler(int lev, void *dev_id, struct pt_regs *regs); -/* - * This should ideally be 4 elements only, for speed. - */ #define DEVNAME_SIZE 24 -static struct { +static struct q40_irq_node { void (*handler)(int, void *, struct pt_regs *); unsigned long flags; void *dev_id; + /* struct q40_irq_node *next;*/ char devname[DEVNAME_SIZE]; unsigned count; + unsigned short state; } irq_tab[Q40_IRQ_MAX+1]; +short unsigned q40_ablecount[Q40_IRQ_MAX+1]; + /* * void q40_init_IRQ (void) * @@ -78,8 +79,11 @@ void q40_init_IRQ (void) irq_tab[i].handler = q40_defhand; irq_tab[i].flags = IRQ_FLG_STD; irq_tab[i].dev_id = NULL; + /* irq_tab[i].next = NULL;*/ irq_tab[i].devname[0] = 0; irq_tab[i].count = 0; + irq_tab[i].state =0; + q40_ablecount[i]=0; /* all enabled */ } /* setup handler for ISA ints */ @@ -108,23 +112,20 @@ int q40_request_irq(unsigned int irq, } /* test for ISA ints not implemented by HW */ - if (irq<15) + switch (irq) { - switch (irq){ - case 1: case 2: case 8: case 9: - case 12: case 13: - printk("%s: ISA IRQ %d from %s not implemented by HW\n", __FUNCTION__, irq, devname); - return -ENXIO; - default: - } + case 1: case 2: case 8: case 9: + case 12: case 13: + printk("%s: ISA IRQ %d from %s not implemented by HW\n", __FUNCTION__, irq, devname); + return -ENXIO; + case 11: + printk("warning IRQ 10 and 11 not distinguishable\n"); + irq=10; + default: } if (irq<Q40_IRQ_TIMER) { - if (irq==11) { - printk("warning IRQ 10 and 11 not distinguishable\n"); - irq=10; - } if (!(irq_tab[irq].flags & IRQ_FLG_STD)) { if (irq_tab[irq].flags & IRQ_FLG_LOCK) @@ -145,6 +146,7 @@ int q40_request_irq(unsigned int irq, irq_tab[irq].flags = flags; irq_tab[irq].dev_id = dev_id; strncpy(irq_tab[irq].devname,devname,DEVNAME_SIZE); + irq_tab[irq].state = 0; return 0; } else { @@ -163,30 +165,33 @@ void q40_free_irq(unsigned int irq, void *dev_id) } /* test for ISA ints not implemented by HW */ - if (irq<15) { - switch (irq){ + switch (irq) + { case 1: case 2: case 8: case 9: case 12: case 13: - printk("%s: ISA IRQ %d from %x illegal\n", __FUNCTION__, irq, (unsigned)dev_id); - return; + printk("%s: ISA IRQ %d from %x illegal\n", __FUNCTION__, irq, (unsigned)dev_id); + return; + case 11: irq=10; default: - } - } + } - if (irq<Q40_IRQ_TIMER){ - if (irq==11) irq=10; - if (irq_tab[irq].dev_id != dev_id) - printk("%s: Removing probably wrong IRQ %d from %s\n", - __FUNCTION__, irq, irq_tab[irq].devname); - - irq_tab[irq].handler = q40_defhand; - irq_tab[irq].flags = IRQ_FLG_STD; - irq_tab[irq].dev_id = NULL; - /* irq_tab[irq].devname = NULL; */ - } else { /* == Q40_IRQ_TIMER */ - sys_free_irq(4,dev_id); - sys_free_irq(6,dev_id); - } + if (irq<Q40_IRQ_TIMER) + { + if (irq_tab[irq].dev_id != dev_id) + printk("%s: Removing probably wrong IRQ %d from %s\n", + __FUNCTION__, irq, irq_tab[irq].devname); + + irq_tab[irq].handler = q40_defhand; + irq_tab[irq].flags = IRQ_FLG_STD; + irq_tab[irq].dev_id = NULL; + /* irq_tab[irq].devname = NULL; */ + /* do not reset state !! */ + } + else + { /* == Q40_IRQ_TIMER */ + sys_free_irq(4,dev_id); + sys_free_irq(6,dev_id); + } } #if 1 @@ -224,11 +229,16 @@ static struct IRQ_TABLE eirqs[]={ {0,0}}; -/* complaiun only this many times about spurious ints : */ +/* complain only this many times about spurious ints : */ static int ccleirq=60; /* ISA dev IRQ's*/ static int cclirq=60; /* internal */ -/* FIX: add IRQ_INPROGRESS,mask,unmask,probing.... */ +/* FIX: add shared ints,mask,unmask,probing.... */ + +/* this is an awfull hack.. */ +#define IRQ_INPROGRESS 1 +static int disabled=0; +/*static unsigned short saved_mask;*/ void q40_irq2_handler (int vec, void *devname, struct pt_regs *fp) { @@ -238,11 +248,6 @@ void q40_irq2_handler (int vec, void *devname, struct pt_regs *fp) unsigned mer; int irq,i; - /* - * more than 1 bit might be set, must handle atmost 1 int source, - * - handle only those with explicitly set handler - */ - if ((mir&IRQ_SER_MASK) || (mir&IRQ_EXT_MASK)) { @@ -257,9 +262,37 @@ void q40_irq2_handler (int vec, void *devname, struct pt_regs *fp) irq=eirqs[i].irq; irq_tab[irq].count++; if (irq_tab[irq].handler == q40_defhand ) - continue; /* ignore uninited INTs :-( */ - + { + printk("handler for IRQ %d not defined\n",irq); + continue; /* ignore uninited INTs :-( */ + } + + if ( irq_tab[irq].state & IRQ_INPROGRESS ) + { + /*printk("IRQ_INPROGRESS detected for irq %d, disabling - %s disabled\n",irq,disabled ? "already" : "not yet"); */ + + /*saved_mask = fp->sr;*/ + fp->sr = (fp->sr & (~0x700))+0x200; + disabled=1; + return; + } + irq_tab[irq].state |= IRQ_INPROGRESS; irq_tab[irq].handler(irq,irq_tab[irq].dev_id,fp); + + /* naively enable everything, if that fails than */ + /* this function will be reentered immediately thus */ + /* getting another chance to disable the IRQ */ + + irq_tab[irq].state &= ~IRQ_INPROGRESS; + if ( disabled ) + { + /*printk("reenabling irq %d\n",irq); */ + fp->sr = (fp->sr & (~0x700)); /*saved_mask; */ + disabled=0; + } + else if ( fp->sr &0x200 ) + printk("exiting irq handler: fp->sr &0x200 !!\n"); + return; } } @@ -279,7 +312,17 @@ void q40_irq2_handler (int vec, void *devname, struct pt_regs *fp) if (irq_tab[irq].handler == q40_defhand ) continue; /* ignore uninited INTs :-( */ + /* the INPROGRESS stuff should be completely useless*/ + /* for internal ints, nevertheless test it..*/ + if ( irq_tab[irq].state & IRQ_INPROGRESS ) + { + /*disable_irq(irq); + return;*/ + printk("rentering handler for IRQ %d !!\n",irq); + } irq_tab[irq].handler(irq,irq_tab[irq].dev_id,fp); + irq_tab[irq].state &= ~IRQ_INPROGRESS; + /*enable_irq(irq);*/ /* better not try luck !*/ return; } } @@ -327,19 +370,59 @@ static void sys_default_handler(int lev, void *dev_id, struct pt_regs *regs) sys_default_handler,sys_default_handler,sys_default_handler,sys_default_handler }; +int irq_disabled=0; void q40_enable_irq (unsigned int irq) { + /* enable ISA iqs */ + if ( irq>=0 && irq<=15 ) /* the moderately bad case */ + master_outb(1,EXT_ENABLE_REG); +#if 0 + unsigned long flags; + int i; + + if (irq>=10 && irq <= 15) + { + if ( !(--q40_ablecount[irq])) + for (i=10,irq_disabled=0; i<=15; i++) + { + irq_disabled |= (q40_ablecount[irq] !=0); + } + if ( !irq_disabled ) + { + save_flags(flags); + restore_flags(flags & (~0x700)); + } + } +#endif } void q40_disable_irq (unsigned int irq) { + /* disable ISA iqs : only do something if the driver has been + * verified to be Q40 "compatible" - right now only IDE + * Any driver should not attempt to sleep accross disable_irq !! + */ + + if ( irq>=10 && irq<=15 ) /* the moderately bad case */ + master_outb(0,EXT_ENABLE_REG); +#if 0 + unsigned long flags; + + if (irq>=10 && irq <= 15) + { + save_flags(flags); + restore_flags(flags | 0x200 ); + irq_disabled=1; + q40_ablecount[irq]++; + } +#endif } unsigned long q40_probe_irq_on (void) { - printk("sorry, irq probing not yet implemented - reconfigure the driver to avoid this\n"); - return 0; + printk("irq probing not working - reconfigure the driver to avoid this\n"); + return -1; } int q40_probe_irq_off (unsigned long irqs) { diff --git a/arch/m68k/sun3/Makefile b/arch/m68k/sun3/Makefile new file mode 100644 index 000000000..bf987e3d2 --- /dev/null +++ b/arch/m68k/sun3/Makefile @@ -0,0 +1,16 @@ +# +# Makefile for Linux arch/m68k/sun3 source directory +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -Wa,-m68020 -c $< -o $*.o + +O_TARGET := sun3.o +O_OBJS := config.o idprom.o mmu_emu.o sun3ints.o leds.o dvma.o sbus.o + +include $(TOPDIR)/Rules.make diff --git a/arch/m68k/sun3/config.c b/arch/m68k/sun3/config.c new file mode 100644 index 000000000..05fbdea8d --- /dev/null +++ b/arch/m68k/sun3/config.c @@ -0,0 +1,173 @@ +/* + * linux/arch/m68k/sun3/config.c + * + * Copyright (C) 1996,1997 Pekka Pietik{inen + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include <stdarg.h> + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/kd.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/init.h> + +#include <asm/oplib.h> +#include <asm/setup.h> +#include <asm/contregs.h> +#include <asm/movs.h> +#include <asm/pgtable.h> +#include <asm/sun3-head.h> +#include <asm/sun3mmu.h> +#include <asm/machdep.h> +#include <asm/intersil.h> +#include <asm/irq.h> +#include <asm/segment.h> + +extern char _text, _end; + +static int kernel_start, kernel_end; +char sun3_reserved_pmeg[SUN3_PMEGS_NUM]; + +static unsigned long sun3_gettimeoffset(void); +extern int sun3_get_irq_list (char *); +extern void sun3_sched_init(void (*handler)(int, void *, struct pt_regs *)); +extern void sun3_init_IRQ (void); +extern void (*sun3_default_handler[]) (int, void *, struct pt_regs *); +extern int sun3_request_irq (unsigned int irq, void (*handler)(int, void *, struct pt_regs *), + unsigned long flags, const char *devname, void *dev_id); +extern void sun3_free_irq (unsigned int irq, void *dev_id); +extern void sun3_enable_irq (unsigned int); +extern void sun3_disable_irq (unsigned int); +extern void sun3_enable_interrupts (void); +extern void sun3_disable_interrupts (void); +extern void sun3_get_model (unsigned char* model); +extern void idprom_init (void); +void sun3_gettod (int *yearp, int *monp, int *dayp, + int *hourp, int *minp, int *secp); + +extern unsigned long sun_serial_setup(unsigned long memory_start); +volatile char* clock_va; +extern unsigned char* sun3_intreg; + +void __init sun3_init(void) +{ + unsigned char enable_register; + int i; + + m68k_machtype= MACH_SUN3; + m68k_cputype = CPU_68020; + m68k_fputype = FPU_68881; /* mc68881 actually */ + m68k_mmutype = MMU_SUN3; + clock_va = (char *) 0xfe06000; /* dark */ + sun3_intreg = (unsigned char *) 0xfe0a000; /* magic */ + sun3_disable_interrupts(); + + prom_init((void *)LINUX_OPPROM_BEGVM); + + GET_CONTROL_BYTE(AC_SENABLE,enable_register); + enable_register |= 0x40; /* Enable FPU */ + SET_CONTROL_BYTE(AC_SENABLE,enable_register); + GET_CONTROL_BYTE(AC_SENABLE,enable_register); + + /* This code looks suspicious, because it doesn't subtract + memory belonging to the kernel from the available space */ + + + memset(sun3_reserved_pmeg, 0, sizeof(sun3_reserved_pmeg)); + + /* Reserve important PMEGS */ + /* FIXME: These should be probed instead of hardcoded */ + + for (i=0; i<8; i++) /* Kernel PMEGs */ + sun3_reserved_pmeg[i] = 1; + + sun3_reserved_pmeg[247] = 1; /* ROM mapping */ + sun3_reserved_pmeg[248] = 1; /* AMD Ethernet */ + sun3_reserved_pmeg[251] = 1; /* VB area */ + sun3_reserved_pmeg[254] = 1; /* main I/O */ + + sun3_reserved_pmeg[249] = 1; + sun3_reserved_pmeg[252] = 1; + sun3_reserved_pmeg[253] = 1; + set_fs(KERNEL_DS); +} + +/* Without this, Bad Things happen when something calls arch_reset. */ +static void sun3_reboot (void) +{ + prom_reboot ("vmlinux"); +} + +void __init config_sun3(unsigned long *start_mem_p, unsigned long *end_mem_p) +{ + printk("ARCH: SUN3\n"); + idprom_init(); + + /* Subtract kernel memory from available memory */ + + mach_sched_init = sun3_sched_init; + mach_init_IRQ = sun3_init_IRQ; + mach_default_handler = &sun3_default_handler; + mach_request_irq = sun3_request_irq; + mach_free_irq = sun3_free_irq; +// mach_keyb_init = sun3_keyb_init; + enable_irq = sun3_enable_irq; + disable_irq = sun3_disable_irq; + mach_get_irq_list = sun3_get_irq_list; + mach_gettod = sun3_gettod; + mach_reset = sun3_reboot; + mach_gettimeoffset = sun3_gettimeoffset; + mach_get_model = sun3_get_model; +#ifndef CONFIG_SERIAL_CONSOLE + conswitchp = &dummy_con; +#endif + kernel_start = 0x00000000; /* NOT &_text */ + kernel_end = ((((int)&_end) + 0x2000) & ~0x1fff) - 1; + + *start_mem_p = kernel_end + 1; +// PROM seems to want the last couple of physical pages. --m + *end_mem_p = *(romvec->pv_sun3mem) + PAGE_OFFSET - 2*PAGE_SIZE; + m68k_num_memory=1; + m68k_memory[0].size=*(romvec->pv_sun3mem); + + *start_mem_p = sun_serial_setup(*start_mem_p); +} + +void __init sun3_sched_init(void (*timer_routine)(int, void *, struct pt_regs *)) +{ + sun3_disable_interrupts(); + intersil_clock->cmd_reg=(INTERSIL_RUN|INTERSIL_INT_DISABLE|INTERSIL_24H_MODE); + intersil_clock->int_reg=INTERSIL_HZ_100_MASK; + intersil_clear(); + sun3_enable_irq(5); + intersil_clock->cmd_reg=(INTERSIL_RUN|INTERSIL_INT_ENABLE|INTERSIL_24H_MODE); + sun3_enable_interrupts(); + intersil_clear(); +} + +static unsigned long sun3_gettimeoffset(void) +{ + return 1; +} + +void sun3_gettod (int *yearp, int *monp, int *dayp, + int *hourp, int *minp, int *secp) +{ + struct intersil_dt* todintersil; + todintersil = (struct intersil_dt *) &intersil_clock->counter; + *secp = todintersil->second; + *minp = todintersil->minute; + *hourp = todintersil->hour; + *dayp = todintersil->day; + *monp = todintersil->month; + *yearp = todintersil->year+68; /* The base year for sun3 is 1968 */ +} + diff --git a/arch/m68k/sun3/dvma.c b/arch/m68k/sun3/dvma.c new file mode 100644 index 000000000..d12ff43ac --- /dev/null +++ b/arch/m68k/sun3/dvma.c @@ -0,0 +1,33 @@ + +/* dvma support routines */ + +#include <linux/kernel.h> +#include <linux/mm.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/sun3mmu.h> +#include <asm/dvma.h> + +unsigned long dvma_next_free = DVMA_START; + +/* get needed number of free dma pages, or panic if not enough */ + +void *sun3_dvma_malloc(int len) +{ + unsigned long vaddr; + + /* if the next free pages have been accessed, skip them */ + while((dvma_next_free < DVMA_END) && + (sun3_get_pte(dvma_next_free) & SUN3_PAGE_ACCESSED)) + dvma_next_free += SUN3_PTE_SIZE; + + if((dvma_next_free + len) > DVMA_END) + panic("sun3_dvma_malloc: out of dvma pages"); + + vaddr = dvma_next_free; + dvma_next_free = PAGE_ALIGN(dvma_next_free + len); + + return (void *)vaddr; +} + + diff --git a/arch/m68k/sun3/idprom.c b/arch/m68k/sun3/idprom.c new file mode 100644 index 000000000..3b2c99d45 --- /dev/null +++ b/arch/m68k/sun3/idprom.c @@ -0,0 +1,129 @@ +/* $Id: idprom.c,v 1.22 1996/11/13 05:09:25 davem Exp $ + * idprom.c: Routines to load the idprom into kernel addresses and + * interpret the data contained within. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Sun3/3x models added by David Monro (davidm@psrg.cs.usyd.edu.au) + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/string.h> + +#include <asm/oplib.h> +#include <asm/idprom.h> +#include <asm/machines.h> /* Fun with Sun released architectures. */ + +struct idprom *idprom; +static struct idprom idprom_buffer; + +/* Here is the master table of Sun machines which use some implementation + * of the Sparc CPU and have a meaningful IDPROM machtype value that we + * know about. See asm-sparc/machines.h for empirical constants. + */ +struct Sun_Machine_Models Sun_Machines[NUM_SUN_MACHINES] = { +/* First, Sun3's */ +{ "Sun 3/160 Series", (SM_SUN3 | SM_3_160) }, +{ "Sun 3/50", (SM_SUN3 | SM_3_50) }, +{ "Sun 3/260 Series", (SM_SUN3 | SM_3_260) }, +{ "Sun 3/110 Series", (SM_SUN3 | SM_3_110) }, +{ "Sun 3/60", (SM_SUN3 | SM_3_60) }, +{ "Sun 3/E", (SM_SUN3 | SM_3_E) }, +/* Now, Sun3x's */ +{ "Sun 3/460 Series", (SM_SUN3 | SM_3_460) }, +{ "Sun 3/80", (SM_SUN3 | SM_3_80) }, +/* Then, Sun4's */ +//{ "Sun 4/100 Series", (SM_SUN4 | SM_4_110) }, +//{ "Sun 4/200 Series", (SM_SUN4 | SM_4_260) }, +//{ "Sun 4/300 Series", (SM_SUN4 | SM_4_330) }, +//{ "Sun 4/400 Series", (SM_SUN4 | SM_4_470) }, +/* And now, Sun4c's */ +//{ "Sun4c SparcStation 1", (SM_SUN4C | SM_4C_SS1) }, +//{ "Sun4c SparcStation IPC", (SM_SUN4C | SM_4C_IPC) }, +//{ "Sun4c SparcStation 1+", (SM_SUN4C | SM_4C_SS1PLUS) }, +//{ "Sun4c SparcStation SLC", (SM_SUN4C | SM_4C_SLC) }, +//{ "Sun4c SparcStation 2", (SM_SUN4C | SM_4C_SS2) }, +//{ "Sun4c SparcStation ELC", (SM_SUN4C | SM_4C_ELC) }, +//{ "Sun4c SparcStation IPX", (SM_SUN4C | SM_4C_IPX) }, +/* Finally, early Sun4m's */ +//{ "Sun4m SparcSystem600", (SM_SUN4M | SM_4M_SS60) }, +//{ "Sun4m SparcStation10/20", (SM_SUN4M | SM_4M_SS50) }, +//{ "Sun4m SparcStation5", (SM_SUN4M | SM_4M_SS40) }, +/* One entry for the OBP arch's which are sun4d, sun4e, and newer sun4m's */ +//{ "Sun4M OBP based system", (SM_SUN4M_OBP | 0x0) } +}; + +static void __init display_system_type(unsigned char machtype) +{ + register int i; + + for (i = 0; i < NUM_SUN_MACHINES; i++) { + if(Sun_Machines[i].id_machtype == machtype) { + if (machtype != (SM_SUN4M_OBP | 0x00)) + printk("TYPE: %s\n", Sun_Machines[i].name); + else { +#if 0 + prom_getproperty(prom_root_node, "banner-name", + sysname, sizeof(sysname)); + printk("TYPE: %s\n", sysname); +#endif + } + return; + } + } + + prom_printf("IDPROM: Bogus id_machtype value, 0x%x\n", machtype); + prom_halt(); +} + +void sun3_get_model(unsigned char* model) +{ + register int i; + + for (i = 0; i < NUM_SUN_MACHINES; i++) { + if(Sun_Machines[i].id_machtype == idprom->id_machtype) { + strcpy(model, Sun_Machines[i].name); + return; + } + } +} + + + +/* Calculate the IDPROM checksum (xor of the data bytes). */ +static unsigned char __init calc_idprom_cksum(struct idprom *idprom) +{ + unsigned char cksum, i, *ptr = (unsigned char *)idprom; + + for (i = cksum = 0; i <= 0x0E; i++) + cksum ^= *ptr++; + + return cksum; +} + +/* Create a local IDPROM copy, verify integrity, and display information. */ +void __init idprom_init(void) +{ + prom_get_idprom((char *) &idprom_buffer, sizeof(idprom_buffer)); + + idprom = &idprom_buffer; + + if (idprom->id_format != 0x01) { + prom_printf("IDPROM: Unknown format type!\n"); + prom_halt(); + } + + if (idprom->id_cksum != calc_idprom_cksum(idprom)) { + prom_printf("IDPROM: Checksum failure (nvram=%x, calc=%x)!\n", + idprom->id_cksum, calc_idprom_cksum(idprom)); + prom_halt(); + } + + display_system_type(idprom->id_machtype); + + printk("Ethernet address: %x:%x:%x:%x:%x:%x\n", + idprom->id_ethaddr[0], idprom->id_ethaddr[1], + idprom->id_ethaddr[2], idprom->id_ethaddr[3], + idprom->id_ethaddr[4], idprom->id_ethaddr[5]); +} diff --git a/arch/m68k/sun3/leds.c b/arch/m68k/sun3/leds.c new file mode 100644 index 000000000..2128c4413 --- /dev/null +++ b/arch/m68k/sun3/leds.c @@ -0,0 +1,16 @@ +#include <asm/contregs.h> +#include <asm/sun3mmu.h> +#include <asm/io.h> +#include <asm/movs.h> + +#define FC_CONTROL 3 /* This should go somewhere else... */ + +void sun3_leds(unsigned char byte) +{ + unsigned char dfc; + + GET_DFC(dfc); + SET_DFC(FC_CONTROL); + SET_CONTROL_BYTE(AC_LEDS,byte); + SET_DFC(dfc); +} diff --git a/arch/m68k/sun3/mmu_emu.c b/arch/m68k/sun3/mmu_emu.c new file mode 100644 index 000000000..857aba4f1 --- /dev/null +++ b/arch/m68k/sun3/mmu_emu.c @@ -0,0 +1,445 @@ +/* +** Tablewalk MMU emulator +** +** by Toshiyasu Morita +** +** Started 1/16/98 @ 2:22 am +*/ + +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/kernel.h> +#include <linux/ptrace.h> +#include <linux/delay.h> + +#include <asm/setup.h> +#include <asm/traps.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/sun3mmu.h> +#include <asm/segment.h> +#include <asm/bitops.h> +#include <asm/oplib.h> +#include <asm/mmu_context.h> + +extern void prom_reboot (char *) __attribute__ ((__noreturn__)); + +#undef DEBUG_MMU_EMU + +/* +** Defines +*/ + +#define CONTEXTS_NUM 8 +#define SEGMAPS_PER_CONTEXT_NUM 2048 +#define PAGES_PER_SEGMENT 16 +#define PMEGS_NUM 256 +#define PMEG_MASK 0xFF + +/* +** Globals +*/ + +unsigned long vmalloc_end = 0; +unsigned long pmeg_vaddr[PMEGS_NUM]; +unsigned char pmeg_alloc[PMEGS_NUM]; +unsigned char pmeg_ctx[PMEGS_NUM]; + +/* pointers to the mm structs for each task in each + context. 0xffffffff is a marker for kernel context */ +struct mm_struct *ctx_alloc[CONTEXTS_NUM] = {0xffffffff, 0, 0, 0, 0, 0, 0, 0}; +/* has this context been mmdrop'd? */ +unsigned char ctx_live[CONTEXTS_NUM] = {1, 0, 0, 0, 0, 0, 0, 0}; +static unsigned char ctx_avail = CONTEXTS_NUM-1; +unsigned char ctx_next_to_die = 1; + +/* array of pages to be marked off for the rom when we do mem_init later */ +/* 256 pages lets the rom take up to 2mb of physical ram.. I really + hope it never wants mote than that. */ +unsigned long rom_pages[256]; + +/* Print a PTE value in symbolic form. For debugging. */ +void print_pte (pte_t pte) +{ +#if 0 + /* Verbose version. */ + unsigned long val = pte_val (pte); + printk (" pte=%lx [addr=%lx", + val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT); + if (val & SUN3_PAGE_VALID) printk (" valid"); + if (val & SUN3_PAGE_WRITEABLE) printk (" write"); + if (val & SUN3_PAGE_SYSTEM) printk (" sys"); + if (val & SUN3_PAGE_NOCACHE) printk (" nocache"); + if (val & SUN3_PAGE_ACCESSED) printk (" accessed"); + if (val & SUN3_PAGE_MODIFIED) printk (" modified"); + switch (val & SUN3_PAGE_TYPE_MASK) { + case SUN3_PAGE_TYPE_MEMORY: printk (" memory"); break; + case SUN3_PAGE_TYPE_IO: printk (" io"); break; + case SUN3_PAGE_TYPE_VME16: printk (" vme16"); break; + case SUN3_PAGE_TYPE_VME32: printk (" vme32"); break; + } + printk ("]\n"); +#else + /* Terse version. More likely to fit on a line. */ + unsigned long val = pte_val (pte); + char flags[7], *type; + + flags[0] = (val & SUN3_PAGE_VALID) ? 'v' : '-'; + flags[1] = (val & SUN3_PAGE_WRITEABLE) ? 'w' : '-'; + flags[2] = (val & SUN3_PAGE_SYSTEM) ? 's' : '-'; + flags[3] = (val & SUN3_PAGE_NOCACHE) ? 'x' : '-'; + flags[4] = (val & SUN3_PAGE_ACCESSED) ? 'a' : '-'; + flags[5] = (val & SUN3_PAGE_MODIFIED) ? 'm' : '-'; + flags[6] = '\0'; + + switch (val & SUN3_PAGE_TYPE_MASK) { + case SUN3_PAGE_TYPE_MEMORY: type = "memory"; break; + case SUN3_PAGE_TYPE_IO: type = "io" ; break; + case SUN3_PAGE_TYPE_VME16: type = "vme16" ; break; + case SUN3_PAGE_TYPE_VME32: type = "vme32" ; break; + default: type = "unknown?"; break; + } + + printk (" pte=%08lx [%07lx %s %s]\n", + val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT, flags, type); +#endif +} + +/* Print the PTE value for a given virtual address. For debugging. */ +void print_pte_vaddr (unsigned long vaddr) +{ + printk (" vaddr=%lx [%02lx]", vaddr, sun3_get_segmap (vaddr)); + print_pte (__pte (sun3_get_pte (vaddr))); +} + +/* + * Initialise the MMU emulator. + */ +void mmu_emu_init(void) +{ + unsigned long seg, num; + int i,j; + extern char _stext, _etext; + unsigned long page; + + memset(rom_pages, 0, sizeof(rom_pages)); + memset(pmeg_vaddr, 0, sizeof(pmeg_vaddr)); + memset(pmeg_alloc, 0, sizeof(pmeg_alloc)); + memset(pmeg_ctx, 0, sizeof(pmeg_ctx)); + +#ifdef DEBUG_MMU_EMU + printk ("mmu_emu_init: stext=%p etext=%p pmegs=%u\n", &_stext, + &_etext, (&_etext-&_stext+SUN3_PMEG_SIZE-1) >> + SUN3_PMEG_SIZE_BITS); +#endif + + /* mark the pmegs copied in sun3-head.S as used */ + for (i=0; i<10; ++i) + pmeg_alloc[i] = 2; + + /* I'm thinking that most of the top pmeg's are going to be + used for something, and we probably shouldn't risk it */ + for(num = 0xf0; num <= 0xff; num++) + pmeg_alloc[num] = 2; + + j = 0; + for (num=0, seg=0x0F800000; seg<0x10000000; seg+=16*PAGE_SIZE) { + if (sun3_get_segmap (seg) != SUN3_INVALID_PMEG) { +#ifdef DEBUG_MMU_EMU + printk ("mapped:"); + print_pte_vaddr (seg); +#endif + // the lowest mapping here is the end of our + // vmalloc region + if(!vmalloc_end) + vmalloc_end = seg; + + // mark the segmap alloc'd, and reserve any + // of the first 0xbff pages the hardware is + // already using... does any sun3 support > 24mb? + pmeg_alloc[sun3_get_segmap(seg)] = 2; + for(i = 0; i < SUN3_PMEG_SIZE; i += PAGE_SIZE) + { + page = (sun3_get_pte(seg+i) & + SUN3_PAGE_PGNUM_MASK); + + if((page) && (page < 0xbff)) { + rom_pages[j] = page; + j++; + } + } + } + } + + + /* blank everything below the kernel, and we've got the base + mapping to start all the contexts off with... */ + for(seg = 0; seg < PAGE_OFFSET; seg += SUN3_PMEG_SIZE) + sun3_put_segmap(seg, SUN3_INVALID_PMEG); + + set_fs(MAKE_MM_SEG(3)); + for(seg = 0; seg < 0x10000000; seg += SUN3_PMEG_SIZE) { + i = sun3_get_segmap(seg); + for(j = 1; j < CONTEXTS_NUM; j++) + (*(romvec->pv_setctxt))(j, (void *)seg, i); + } + set_fs(KERNEL_DS); + +} + +/* called during mem_init to create the needed holes in the mem + mappings */ +void mmu_emu_reserve_pages(unsigned long max_page) +{ + int i = 0; + + while(rom_pages[i] != 0) { + // don't tamper with pages that wound up after end_mem + if(rom_pages[i] < max_page) + set_bit(PG_reserved, &mem_map[rom_pages[i]].flags); + i++; + } +} + +/* erase the mappings for a dead context. Uses the pg_dir for hints + as the pmeg tables proved somewhat unreliable, and unmapping all of + TASK_SIZE was much slower and no more stable. */ +/* todo: find a better way to keep track of the pmegs used by a + context for when they're cleared */ +void clear_context(unsigned long context) +{ + unsigned char oldctx; + unsigned long i; + + if(!ctx_alloc[context]) + panic("clear_context: context not allocated\n"); + + oldctx = sun3_get_context(); + + sun3_put_context(context); + + /* ctx_live denotes if we're clearing a context with an active + mm, in which case we can use the pgd for clues and also should + mark that mm as lacking a context. if the context has been + mmdrop'd, then flush outright. */ + + if(!ctx_live[context]) { + for(i = 0; i < TASK_SIZE ; i += SUN3_PMEG_SIZE) + sun3_put_segmap(i, SUN3_INVALID_PMEG); + } else { + pgd_t *pgd; + + pgd = ctx_alloc[context]->pgd; + ctx_alloc[context]->context = SUN3_INVALID_CONTEXT; + for(i = 0; i < (TASK_SIZE>>PGDIR_SHIFT); i++, pgd++) + { + if(pgd_val(*pgd)) { + sun3_put_segmap(i<<PGDIR_SHIFT, + SUN3_INVALID_PMEG); + } + } + } + + for(i = 0; i < SUN3_INVALID_PMEG; i++) { + if((pmeg_ctx[i] == context) && (pmeg_alloc[i] != 2)) { + pmeg_ctx[i] = 0; + pmeg_alloc[i] = 0; + pmeg_vaddr[i] = 0; + } + } + + ctx_alloc[context] = (struct mm_struct *)0; + ctx_avail++; +} + +/* gets an empty context. if full, kills the next context listed to + die first */ +/* This context invalidation scheme is, well, totally arbitrary, I'm + sure it could be much more intellegent... but it gets the job done + for now without much overhead in making it's decision. */ +/* todo: come up with optimized scheme for flushing contexts */ +unsigned long get_free_context(struct mm_struct *mm) +{ + unsigned long new = 1; + + if(!ctx_avail) { + /* kill someone to get our context */ + new = ctx_next_to_die; + clear_context(new); + ctx_next_to_die = (ctx_next_to_die + 1) & 0x7; + if(!ctx_next_to_die) + ctx_next_to_die++; + } else { + while(new < CONTEXTS_NUM) { + if(ctx_alloc[new]) + new++; + else + break; + } + // check to make sure one was really free... + if(new == CONTEXTS_NUM) + panic("get_free_context: failed to find free context"); + } + + ctx_alloc[new] = mm; + ctx_live[new] = 1; + ctx_avail--; + + return new; +} + +/* + * Dynamically select a `spare' PMEG and use it to map virtual `vaddr' in + * `context'. Maintain internal PMEG management structures. This doesn't + * actually map the physical address, but does clear the old mappings. + */ +//todo: better allocation scheme? but is extra complexity worthwhile? +//todo: only clear old entries if necessary? how to tell? + +static inline void mmu_emu_map_pmeg (int context, int vaddr) +{ + static unsigned char curr_pmeg = 128; + int i; + + /* Round address to PMEG boundary. */ + vaddr &= ~SUN3_PMEG_MASK; + + /* Find a spare one. */ + while (pmeg_alloc[curr_pmeg] == 2) + ++curr_pmeg; + + +#ifdef DEBUG_MMU_EMU +printk("mmu_emu_map_pmeg: pmeg %x to context %d vaddr %x\n", + curr_pmeg, context, vaddr); +#endif + + /* Invalidate old mapping for the pmeg, if any */ + if (pmeg_alloc[curr_pmeg] == 1) { + sun3_put_context(pmeg_ctx[curr_pmeg]); + sun3_put_segmap (pmeg_vaddr[curr_pmeg], SUN3_INVALID_PMEG); + sun3_put_context(context); + } + + /* Update PMEG management structures. */ + // don't take pmeg's away from the kernel... + if(vaddr >= PAGE_OFFSET) { + /* map kernel pmegs into all contexts */ + unsigned char i; + + for(i = 0; i < CONTEXTS_NUM; i++) { + sun3_put_context(i); + sun3_put_segmap (vaddr, curr_pmeg); + } + sun3_put_context(context); + pmeg_alloc[curr_pmeg] = 2; + pmeg_ctx[curr_pmeg] = 0; + + } + else { + pmeg_alloc[curr_pmeg] = 1; + pmeg_ctx[curr_pmeg] = context; + sun3_put_segmap (vaddr, curr_pmeg); + + } + pmeg_vaddr[curr_pmeg] = vaddr; + + /* Set hardware mapping and clear the old PTE entries. */ + for (i=0; i<SUN3_PMEG_SIZE; i+=SUN3_PTE_SIZE) + sun3_put_pte (vaddr + i, SUN3_PAGE_SYSTEM); + + /* Consider a different one next time. */ + ++curr_pmeg; +} + +/* + * Handle a pagefault at virtual address `vaddr'; check if there should be a + * page there (specifically, whether the software pagetables indicate that + * there is). This is necessary due to the limited size of the second-level + * Sun3 hardware pagetables (256 groups of 16 pages). If there should be a + * mapping present, we select a `spare' PMEG and use it to create a mapping. + * `read_flag' is nonzero for a read fault; zero for a write. Returns nonzero + * if we successfully handled the fault. + */ +//todo: should we bump minor pagefault counter? if so, here or in caller? +//todo: possibly inline this into bus_error030 in <asm/buserror.h> ? + +// kernel_fault is set when a kernel page couldn't be demand mapped, +// and forces another try using the kernel page table. basically a +// hack so that vmalloc would work correctly. + +int mmu_emu_handle_fault (unsigned long vaddr, int read_flag, int kernel_fault) +{ + unsigned long segment, offset; + unsigned char context; + pte_t *pte; + pgd_t * crp; + + if(current->mm == NULL) { + crp = swapper_pg_dir; + context = 0; + } else { + context = current->mm->context; + if(kernel_fault) + crp = swapper_pg_dir; + else + crp = current->mm->pgd; + } + +#ifdef DEBUG_MMU_EMU + printk ("mmu_emu_handle_fault: vaddr=%lx type=%s crp=%p\n", + vaddr, read_flag ? "read" : "write", crp); +#endif + + segment = (vaddr >> SUN3_PMEG_SIZE_BITS) & 0x7FF; + offset = (vaddr >> SUN3_PTE_SIZE_BITS) & 0xF; + +#ifdef DEBUG_MMU_EMU + printk ("mmu_emu_handle_fault: segment=%lx offset=%lx\n", segment, offset); +#endif + + pte = (pte_t *) pgd_val (*(crp + segment)); + +//todo: next line should check for valid pmd properly. + if (!pte) { +// printk ("mmu_emu_handle_fault: invalid pmd\n"); + return 0; + } + + pte = (pte_t *) __va ((unsigned long)(pte + offset)); + + /* Make sure this is a valid page */ + if (!(pte_val (*pte) & SUN3_PAGE_VALID)) + return 0; + + /* Make sure there's a pmeg allocated for the page */ + if (sun3_get_segmap (vaddr&~SUN3_PMEG_MASK) == SUN3_INVALID_PMEG) + mmu_emu_map_pmeg (context, vaddr); + + /* Write the pte value to hardware MMU */ + sun3_put_pte (vaddr&PAGE_MASK, pte_val (*pte)); + + /* Update software copy of the pte value */ +// I'm not sure this is necessary. If this is required, we ought to simply +// copy this out when we reuse the PMEG or at some other convenient time. +// Doing it here is fairly meaningless, anyway, as we only know about the +// first access to a given page. --m + if (!read_flag) { + if (pte_val (*pte) & SUN3_PAGE_WRITEABLE) + pte_val (*pte) |= (SUN3_PAGE_ACCESSED + | SUN3_PAGE_MODIFIED); + else + return 0; /* Write-protect error. */ + } else + pte_val (*pte) |= SUN3_PAGE_ACCESSED; + +#ifdef DEBUG_MMU_EMU + printk ("seg:%d crp:%p ->", get_fs().seg, crp); + print_pte_vaddr (vaddr); + printk ("\n"); +#endif + + return 1; +} diff --git a/arch/m68k/sun3/prom/Makefile b/arch/m68k/sun3/prom/Makefile new file mode 100644 index 000000000..d1aeb0602 --- /dev/null +++ b/arch/m68k/sun3/prom/Makefile @@ -0,0 +1,22 @@ +# $Id: Makefile,v 1.5 1995/11/25 00:59:48 davem Exp $ +# Makefile for the Sun Boot PROM interface library under +# Linux. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +OBJS = init.o console.o printf.o misc.o +#bootstr.o init.o misc.o segment.o console.o printf.o +all: promlib.a + +promlib.a: $(OBJS) + $(AR) rcs promlib.a $(OBJS) + sync + +dep: + $(CPP) -M *.c > .depend + +include $(TOPDIR)/Rules.make diff --git a/arch/m68k/sun3/prom/console.c b/arch/m68k/sun3/prom/console.c new file mode 100644 index 000000000..cf5cdc24e --- /dev/null +++ b/arch/m68k/sun3/prom/console.c @@ -0,0 +1,175 @@ +/* $Id: console.c,v 1.10 1996/12/18 06:46:54 tridge Exp $ + * console.c: Routines that deal with sending and receiving IO + * to/from the current console device using the PROM. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <asm/openprom.h> +#include <asm/oplib.h> +#include <asm/system.h> +#include <linux/string.h> + +/* Non blocking get character from console input device, returns -1 + * if no input was taken. This can be used for polling. + */ +int +prom_nbgetchar(void) +{ + int i = -1; + unsigned long flags; + + save_flags(flags); cli(); + i = (*(romvec->pv_nbgetchar))(); + restore_flags(flags); + return i; /* Ugh, we could spin forever on unsupported proms ;( */ +} + +/* Non blocking put character to console device, returns -1 if + * unsuccessful. + */ +int +prom_nbputchar(char c) +{ + unsigned long flags; + int i = -1; + + save_flags(flags); cli(); + i = (*(romvec->pv_nbputchar))(c); + restore_flags(flags); + return i; /* Ugh, we could spin forever on unsupported proms ;( */ +} + +/* Blocking version of get character routine above. */ +char +prom_getchar(void) +{ + int character; + while((character = prom_nbgetchar()) == -1) ; + return (char) character; +} + +/* Blocking version of put character routine above. */ +void +prom_putchar(char c) +{ + while(prom_nbputchar(c) == -1) ; + return; +} + +/* Query for input device type */ +#if 0 +enum prom_input_device +prom_query_input_device() +{ + unsigned long flags; + int st_p; + char propb[64]; + char *p; + + switch(prom_vers) { + case PROM_V0: + case PROM_V2: + default: + switch(*romvec->pv_stdin) { + case PROMDEV_KBD: return PROMDEV_IKBD; + case PROMDEV_TTYA: return PROMDEV_ITTYA; + case PROMDEV_TTYB: return PROMDEV_ITTYB; + default: + return PROMDEV_I_UNK; + }; + case PROM_V3: + case PROM_P1275: + save_flags(flags); cli(); + st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdin); + __asm__ __volatile__("ld [%0], %%g6\n\t" : : + "r" (¤t_set[smp_processor_id()]) : + "memory"); + restore_flags(flags); + if(prom_node_has_property(st_p, "keyboard")) + return PROMDEV_IKBD; + prom_getproperty(st_p, "device_type", propb, sizeof(propb)); + if(strncmp(propb, "serial", sizeof("serial"))) + return PROMDEV_I_UNK; + prom_getproperty(prom_root_node, "stdin-path", propb, sizeof(propb)); + p = propb; + while(*p) p++; p -= 2; + if(p[0] == ':') { + if(p[1] == 'a') + return PROMDEV_ITTYA; + else if(p[1] == 'b') + return PROMDEV_ITTYB; + } + return PROMDEV_I_UNK; + case PROM_AP1000: + return PROMDEV_I_UNK; + }; +} +#endif + +/* Query for output device type */ + +#if 0 +enum prom_output_device +prom_query_output_device() +{ + unsigned long flags; + int st_p; + char propb[64]; + char *p; + int propl; + + switch(prom_vers) { + case PROM_V0: + switch(*romvec->pv_stdin) { + case PROMDEV_SCREEN: return PROMDEV_OSCREEN; + case PROMDEV_TTYA: return PROMDEV_OTTYA; + case PROMDEV_TTYB: return PROMDEV_OTTYB; + }; + break; + case PROM_V2: + case PROM_V3: + case PROM_P1275: + save_flags(flags); cli(); + st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdout); + __asm__ __volatile__("ld [%0], %%g6\n\t" : : + "r" (¤t_set[smp_processor_id()]) : + "memory"); + restore_flags(flags); + propl = prom_getproperty(st_p, "device_type", propb, sizeof(propb)); + if (propl >= 0 && propl == sizeof("display") && + strncmp("display", propb, sizeof("display")) == 0) + { + return PROMDEV_OSCREEN; + } + if(prom_vers == PROM_V3) { + if(strncmp("serial", propb, sizeof("serial"))) + return PROMDEV_O_UNK; + prom_getproperty(prom_root_node, "stdout-path", propb, sizeof(propb)); + p = propb; + while(*p) p++; p -= 2; + if(p[0]==':') { + if(p[1] == 'a') + return PROMDEV_OTTYA; + else if(p[1] == 'b') + return PROMDEV_OTTYB; + } + return PROMDEV_O_UNK; + } else { + /* This works on SS-2 (an early OpenFirmware) still. */ + switch(*romvec->pv_stdin) { + case PROMDEV_TTYA: return PROMDEV_OTTYA; + case PROMDEV_TTYB: return PROMDEV_OTTYB; + }; + } + break; + case PROM_AP1000: + return PROMDEV_I_UNK; + }; + return PROMDEV_O_UNK; +} +#endif diff --git a/arch/m68k/sun3/prom/init.c b/arch/m68k/sun3/prom/init.c new file mode 100644 index 000000000..3922eff55 --- /dev/null +++ b/arch/m68k/sun3/prom/init.c @@ -0,0 +1,89 @@ +/* $Id: init.c,v 1.9 1996/12/18 06:46:55 tridge Exp $ + * init.c: Initialize internal variables used by the PROM + * library functions. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/openprom.h> +#include <asm/oplib.h> + +struct linux_romvec *romvec; +enum prom_major_version prom_vers; +unsigned int prom_rev, prom_prev; + +/* The root node of the prom device tree. */ +int prom_root_node; + +/* Pointer to the device tree operations structure. */ +struct linux_nodeops *prom_nodeops; + +/* You must call prom_init() before you attempt to use any of the + * routines in the prom library. It returns 0 on success, 1 on + * failure. It gets passed the pointer to the PROM vector. + */ + +extern void prom_meminit(void); +extern void prom_ranges_init(void); + +void __init prom_init(struct linux_romvec *rp) +{ +#if CONFIG_AP1000 + extern struct linux_romvec *ap_prom_init(void); + rp = ap_prom_init(); +#endif + + romvec = rp; +#ifndef CONFIG_SUN3 + switch(romvec->pv_romvers) { + case 0: + prom_vers = PROM_V0; + break; + case 2: + prom_vers = PROM_V2; + break; + case 3: + prom_vers = PROM_V3; + break; + case 4: + prom_vers = PROM_P1275; + prom_printf("PROMLIB: Sun IEEE Prom not supported yet\n"); + prom_halt(); + break; + case 42: /* why not :-) */ + prom_vers = PROM_AP1000; + break; + + default: + prom_printf("PROMLIB: Bad PROM version %d\n", + romvec->pv_romvers); + prom_halt(); + break; + }; + + prom_rev = romvec->pv_plugin_revision; + prom_prev = romvec->pv_printrev; + prom_nodeops = romvec->pv_nodeops; + + prom_root_node = prom_getsibling(0); + if((prom_root_node == 0) || (prom_root_node == -1)) + prom_halt(); + + if((((unsigned long) prom_nodeops) == 0) || + (((unsigned long) prom_nodeops) == -1)) + prom_halt(); + + prom_meminit(); + + prom_ranges_init(); +#endif +// printk("PROMLIB: Sun Boot Prom Version %d Revision %d\n", +// romvec->pv_romvers, prom_rev); + + /* Initialization successful. */ + return; +} diff --git a/arch/m68k/sun3/prom/misc.c b/arch/m68k/sun3/prom/misc.c new file mode 100644 index 000000000..97036f37d --- /dev/null +++ b/arch/m68k/sun3/prom/misc.c @@ -0,0 +1,95 @@ +/* $Id: misc.c,v 1.15 1997/05/14 20:45:00 davem Exp $ + * misc.c: Miscellaneous prom functions that don't belong + * anywhere else. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <asm/sun3-head.h> +#include <asm/idprom.h> +#include <asm/openprom.h> +#include <asm/oplib.h> +#include <asm/movs.h> + +/* Reset and reboot the machine with the command 'bcommand'. */ +void +prom_reboot(char *bcommand) +{ + unsigned long flags; + save_flags(flags); cli(); + (*(romvec->pv_reboot))(bcommand); + restore_flags(flags); +} + +/* Drop into the prom, with the chance to continue with the 'go' + * prom command. + */ +void +prom_cmdline(void) +{ +} + +/* Drop into the prom, but completely terminate the program. + * No chance of continuing. + */ +void +prom_halt(void) +{ + unsigned long flags; +again: + save_flags(flags); cli(); + (*(romvec->pv_halt))(); + restore_flags(flags); + goto again; /* PROM is out to get me -DaveM */ +} + +typedef void (*sfunc_t)(void); + +/* Get the idprom and stuff it into buffer 'idbuf'. Returns the + * format type. 'num_bytes' is the number of bytes that your idbuf + * has space for. Returns 0xff on error. + */ +unsigned char +prom_get_idprom(char *idbuf, int num_bytes) +{ + int i, oldsfc; + GET_SFC(oldsfc); + SET_SFC(FC_CONTROL); + for(i=0;i<num_bytes; i++) + { + /* There is a problem with the GET_CONTROL_BYTE + macro; defining the extra variable + gets around it. + */ + int c; + GET_CONTROL_BYTE(SUN3_IDPROM_BASE + i, c); + idbuf[i] = c; + } + SET_SFC(oldsfc); + return idbuf[0]; +} + +/* Get the major prom version number. */ +int +prom_version(void) +{ + return romvec->pv_romvers; +} + +/* Get the prom plugin-revision. */ +int +prom_getrev(void) +{ + return prom_rev; +} + +/* Get the prom firmware print revision. */ +int +prom_getprev(void) +{ + return prom_prev; +} diff --git a/arch/m68k/sun3/prom/printf.c b/arch/m68k/sun3/prom/printf.c new file mode 100644 index 000000000..34aeda5f4 --- /dev/null +++ b/arch/m68k/sun3/prom/printf.c @@ -0,0 +1,61 @@ +/* $Id: printf.c,v 1.5 1996/04/04 16:31:07 tridge Exp $ + * printf.c: Internal prom library printf facility. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +/* This routine is internal to the prom library, no one else should know + * about or use it! It's simple and smelly anyway.... + */ + +#include <linux/config.h> +#include <linux/kernel.h> + +#include <asm/openprom.h> +#include <asm/oplib.h> + +#ifdef CONFIG_KGDB +extern int kgdb_initialized; +#endif + +static char ppbuf[1024]; + +void +prom_printf(char *fmt, ...) +{ + va_list args; + char ch, *bptr; + int i; + + va_start(args, fmt); + +#ifdef CONFIG_KGDB + ppbuf[0] = 'O'; + i = vsprintf(ppbuf + 1, fmt, args) + 1; +#else + i = vsprintf(ppbuf, fmt, args); +#endif + + bptr = ppbuf; + +#if CONFIG_AP1000 + ap_write(1,bptr,strlen(bptr)); +#else + +#ifdef CONFIG_KGDB + if (kgdb_initialized) { + printk("kgdb_initialized = %d\n", kgdb_initialized); + putpacket(bptr, 1); + } else +#else + while((ch = *(bptr++)) != 0) { + if(ch == '\n') + prom_putchar('\r'); + + prom_putchar(ch); + } +#endif +#endif + va_end(args); + return; +} diff --git a/arch/m68k/sun3/sbus.c b/arch/m68k/sun3/sbus.c new file mode 100644 index 000000000..ae2d99e2c --- /dev/null +++ b/arch/m68k/sun3/sbus.c @@ -0,0 +1,25 @@ +/* + * SBus helper functions + * + * Sun3 don't have a sbus, but many of the used devices are also + * used on Sparc machines with sbus. To avoid having a lot of + * duplicate code, we provide necessary glue stuff to make using + * of the sbus driver code possible. + * + * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de) + */ + +#include <linux/types.h> +#include <linux/init.h> + +void __init sbus_init(void) +{ + +} + +void *sparc_alloc_io (u32 address, void *virtual, int len, char *name, + u32 bus_type, int rdonly) +{ + return (void *)address; +} + diff --git a/arch/m68k/sun3/sun3ints.c b/arch/m68k/sun3/sun3ints.c new file mode 100644 index 000000000..687b51556 --- /dev/null +++ b/arch/m68k/sun3/sun3ints.c @@ -0,0 +1,132 @@ + /* + * linux/arch/m68k/sun3/sun3ints.c -- Sun-3 Linux interrupt handling code + * + * 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/kernel.h> +#include <linux/sched.h> +#include <linux/kernel_stat.h> +#include <linux/interrupt.h> +#include <asm/segment.h> +#include <asm/intersil.h> +#include <asm/oplib.h> +#include <asm/sun3ints.h> + +extern void sun3_leds (unsigned char); + +void sun3_disable_interrupts(void) +{ + sun3_disable_irq(0); +} + +void sun3_enable_interrupts(void) +{ + sun3_enable_irq(0); +} + +int led_pattern[8] = { + ~(0x80), ~(0x01), + ~(0x40), ~(0x02), + ~(0x20), ~(0x04), + ~(0x10), ~(0x08) +}; + +unsigned char* sun3_intreg; + +void sun3_init_IRQ(void) +{ +} + +void sun3_insert_irq(irq_node_t **list, irq_node_t *node) +{ +} + +void sun3_delete_irq(irq_node_t **list, void *dev_id) +{ +} + +void sun3_free_irq(unsigned int irq, void *dev_id) +{ +} + +void sun3_enable_irq(unsigned int irq) +{ + *sun3_intreg |= (1<<irq); +} + +void sun3_disable_irq(unsigned int irq) +{ + *sun3_intreg &= ~(1<<irq); +} + +inline void sun3_do_irq(int irq, struct pt_regs *fp) +{ + kstat.irqs[0][SYS_IRQS + irq]++; + *sun3_intreg &= ~(1<<irq); + *sun3_intreg |= (1<<irq); +} + +int sun3_get_irq_list(char *buf) +{ + return 0; +} + +static void sun3_int5(int irq, void *dev_id, struct pt_regs *fp) +{ + kstat.irqs[0][SYS_IRQS + irq]++; + *sun3_intreg &= ~(1<<irq); + intersil_clear(); + *sun3_intreg |= (1<<irq); + do_timer(fp); + if(!(kstat.irqs[0][SYS_IRQS + irq] % 20)) + sun3_leds(led_pattern[(kstat.irqs[0][SYS_IRQS+irq]%160) + /20]); +} + +static void sun3_int7(int irq, void *dev_id, struct pt_regs *fp) +{ + sun3_do_irq(irq,fp); + if(!(kstat.irqs[0][SYS_IRQS + irq] % 2000)) + sun3_leds(led_pattern[(kstat.irqs[0][SYS_IRQS+irq]%16000)/2000]); +} + +/* handle requested ints, excepting 5 and 7, which always do the same + thing */ +static void *dev_ids[SYS_IRQS]; +static void (*inthandler[SYS_IRQS])(int, void *, struct pt_regs *) = { + NULL, NULL, NULL, NULL, NULL, sun3_int5, NULL, sun3_int7 +}; + +static void sun3_inthandle(int irq, void *dev_id, struct pt_regs *fp) +{ + if(inthandler[irq] == NULL) + panic ("bad interrupt %d received (id %p)\n",irq, dev_id); + + kstat.irqs[0][SYS_IRQS + irq]++; + *sun3_intreg &= ~(1<<irq); + + inthandler[irq](irq, dev_ids[irq], fp); +} + +void (*sun3_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = { + sun3_inthandle, sun3_inthandle, sun3_inthandle, sun3_inthandle, + sun3_inthandle, sun3_int5, sun3_inthandle, sun3_int7 +}; + +int sun3_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), + unsigned long flags, const char *devname, void *dev_id) +{ + if(inthandler[irq] != NULL) + return -1; + + inthandler[irq] = handler; + dev_ids[irq] = dev_id; + + return 0; +} + diff --git a/arch/m68k/sun3x/config.c b/arch/m68k/sun3x/config.c index 5054cd888..6983e5850 100644 --- a/arch/m68k/sun3x/config.c +++ b/arch/m68k/sun3x/config.c @@ -39,7 +39,7 @@ void sun3x_reboot(void) asm ("reset"); } -__initfunc(int sun3x_keyb_init(void)) +int __init sun3x_keyb_init(void) { return 0; } @@ -75,7 +75,7 @@ void sun3x_disable_irq(unsigned int irq) *sun3x_intreg &= ~(1 << irq); } -__initfunc(void sun3x_init_IRQ(void)) +void __init sun3x_init_IRQ(void) { /* disable all interrupts initially */ *sun3x_intreg = 1; /* master enable only */ @@ -89,7 +89,7 @@ int sun3x_get_irq_list(char *buf) /* * Setup the sun3x configuration info */ -__initfunc(void config_sun3x(void)) +void __init config_sun3x(void) { mach_get_irq_list = sun3x_get_irq_list; mach_max_dma_address = 0xffffffff; /* we can DMA anywhere, whee */ diff --git a/arch/m68k/sun3x/sbus.c b/arch/m68k/sun3x/sbus.c index f1a89df65..b0f2e5a50 100644 --- a/arch/m68k/sun3x/sbus.c +++ b/arch/m68k/sun3x/sbus.c @@ -12,7 +12,7 @@ #include <linux/types.h> #include <linux/init.h> -__initfunc(void sbus_init(void)) +void __init sbus_init(void) { } diff --git a/arch/m68k/sun3x/time.c b/arch/m68k/sun3x/time.c index 73a9efd32..c6aa51b8a 100644 --- a/arch/m68k/sun3x/time.c +++ b/arch/m68k/sun3x/time.c @@ -72,7 +72,7 @@ static void sun3x_timer_tick(int irq, void *dev_id, struct pt_regs *regs) vector(irq, NULL, regs); } -__initfunc(void sun3x_sched_init(void (*vector)(int, void *, struct pt_regs *))) +void __init sun3x_sched_init(void (*vector)(int, void *, struct pt_regs *)) { sys_request_irq(5, sun3x_timer_tick, IRQ_FLG_STD, "timer tick", vector); diff --git a/arch/m68k/tools/amiga/dmesg.c b/arch/m68k/tools/amiga/dmesg.c index 9affff565..e892748e7 100644 --- a/arch/m68k/tools/amiga/dmesg.c +++ b/arch/m68k/tools/amiga/dmesg.c @@ -3,8 +3,7 @@ * in Chip RAM with the kernel command * line option `debug=mem'. * - * © Copyright 1996 by Geert Uytterhoeven - * (Geert.Uytterhoeven@cs.kuleuven.ac.be) + * © Copyright 1996 by Geert Uytterhoeven <geert@linux-m68k.org> * * * Usage: diff --git a/arch/m68k/vmlinux-sun3.lds b/arch/m68k/vmlinux-sun3.lds new file mode 100644 index 000000000..92ca61233 --- /dev/null +++ b/arch/m68k/vmlinux-sun3.lds @@ -0,0 +1,68 @@ +/* ld script to make m68k Linux kernel */ +OUTPUT_FORMAT("elf32-m68k", "elf32-m68k", "elf32-m68k") +OUTPUT_ARCH(m68k) +ENTRY(_start) +SECTIONS +{ + . = 0xE004000; + _text = .; /* Text and read-only data */ + .text : { + *(.head) + *(.text) + *(.fixup) + *(.text.lock) /* out-of-line lock text */ + *(.gnu.warning) + } = 0x4e75 + .kstrtab : { *(.kstrtab) } + + + _etext = .; /* End of text section */ + + .data : { /* Data */ + *(.rodata) + *(.data) + CONSTRUCTORS + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + *(__ex_table) + __stop___ex_table = .; + __start___ksymtab = .; /* Kernel symbol table */ + *(__ksymtab) + __stop___ksymtab = .; + } + /* End of data goes *here* so that freeing init code works properly. */ + _edata = .; + + . = ALIGN(8192); /* Init code and data */ +__init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(16); + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { *(.initcall.init) } + __initcall_end = .; + . = ALIGN(8192); + __init_end = .; + .init.task : { *(init_task) } + + + .bss : { *(.bss) } /* BSS */ + + _end = . ; + + .crap : { + /* Stabs debugging sections. */ + *(.stab) + *(.stabstr) + *(.stab.excl) + *(.stab.exclstr) + *(.stab.index) + *(.stab.indexstr) + *(.comment) + *(.note) + } + +} diff --git a/arch/m68k/vmlinux.lds b/arch/m68k/vmlinux.lds index b79c9dd87..5058d8d02 100644 --- a/arch/m68k/vmlinux.lds +++ b/arch/m68k/vmlinux.lds @@ -33,15 +33,22 @@ SECTIONS .bss : { *(.bss) } /* BSS */ - _edata = .; /* End of data section */ - . = ALIGN(16); .data.cacheline_aligned : { *(.data.cacheline_aligned) } + _edata = .; /* End of data section */ + . = ALIGN(4096); /* Init code and data */ __init_begin = .; .text.init : { *(.text.init) } .data.init : { *(.data.init) } + . = ALIGN(16); + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { *(.initcall.init) } + __initcall_end = .; . = ALIGN(8192); __init_end = .; |