diff options
Diffstat (limited to 'arch/m68k/atari')
-rw-r--r-- | arch/m68k/atari/Makefile | 10 | ||||
-rw-r--r-- | arch/m68k/atari/ataints.c | 648 | ||||
-rw-r--r-- | arch/m68k/atari/atari_ksyms.c | 35 | ||||
-rw-r--r-- | arch/m68k/atari/atasound.c | 109 | ||||
-rw-r--r-- | arch/m68k/atari/atasound.h | 33 | ||||
-rw-r--r-- | arch/m68k/atari/config.c | 726 | ||||
-rw-r--r-- | arch/m68k/atari/debug.c | 347 | ||||
-rw-r--r-- | arch/m68k/atari/hades-pci.c | 444 | ||||
-rw-r--r-- | arch/m68k/atari/stdma.c | 196 | ||||
-rw-r--r-- | arch/m68k/atari/stram.c | 1247 | ||||
-rw-r--r-- | arch/m68k/atari/time.c | 348 |
11 files changed, 4143 insertions, 0 deletions
diff --git a/arch/m68k/atari/Makefile b/arch/m68k/atari/Makefile new file mode 100644 index 00000000000..8cb6236b39d --- /dev/null +++ b/arch/m68k/atari/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for Linux arch/m68k/atari source directory +# + +obj-y := config.o time.o debug.o ataints.o stdma.o \ + atasound.o stram.o atari_ksyms.o + +ifeq ($(CONFIG_PCI),y) +obj-$(CONFIG_HADES) += hades-pci.o +endif diff --git a/arch/m68k/atari/ataints.c b/arch/m68k/atari/ataints.c new file mode 100644 index 00000000000..076f4791784 --- /dev/null +++ b/arch/m68k/atari/ataints.c @@ -0,0 +1,648 @@ +/* + * arch/m68k/atari/ataints.c -- Atari Linux interrupt handling code + * + * 5/2/94 Roman Hodek: + * Added support for TT interrupts; setup for TT SCU (may someone has + * twiddled there and we won't get the right interrupts :-() + * + * Major change: The device-independent code in m68k/ints.c didn't know + * about non-autovec ints yet. It hardcoded the number of possible ints to + * 7 (IRQ1...IRQ7). But the Atari has lots of non-autovec ints! I made the + * number of possible ints a constant defined in interrupt.h, which is + * 47 for the Atari. So we can call request_irq() for all Atari interrupts + * just the normal way. Additionally, all vectors >= 48 are initialized to + * call trap() instead of inthandler(). This must be changed here, too. + * + * 1995-07-16 Lars Brinkhoff <f93labr@dd.chalmers.se>: + * Corrected a bug in atari_add_isr() which rejected all SCC + * interrupt sources if there were no TT MFP! + * + * 12/13/95: New interface functions atari_level_triggered_int() and + * atari_register_vme_int() as support for level triggered VME interrupts. + * + * 02/12/96: (Roman) + * Total rewrite of Atari interrupt handling, for new scheme see comments + * below. + * + * 1996-09-03 lars brinkhoff <f93labr@dd.chalmers.se>: + * Added new function atari_unregister_vme_int(), and + * modified atari_register_vme_int() as well as IS_VALID_INTNO() + * to work with it. + * + * 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/types.h> +#include <linux/kernel.h> +#include <linux/kernel_stat.h> +#include <linux/init.h> +#include <linux/seq_file.h> + +#include <asm/system.h> +#include <asm/traps.h> + +#include <asm/atarihw.h> +#include <asm/atariints.h> +#include <asm/atari_stdma.h> +#include <asm/irq.h> +#include <asm/entry.h> + + +/* + * Atari interrupt handling scheme: + * -------------------------------- + * + * All interrupt source have an internal number (defined in + * <asm/atariints.h>): Autovector interrupts are 1..7, then follow ST-MFP, + * TT-MFP, SCC, and finally VME interrupts. Vector numbers for the latter can + * be allocated by atari_register_vme_int(). + * + * Each interrupt can be of three types: + * + * - SLOW: The handler runs with all interrupts enabled, except the one it + * was called by (to avoid reentering). This should be the usual method. + * But it is currently possible only for MFP ints, since only the MFP + * offers an easy way to mask interrupts. + * + * - FAST: The handler runs with all interrupts disabled. This should be used + * only for really fast handlers, that just do actions immediately + * necessary, and let the rest do a bottom half or task queue. + * + * - PRIORITIZED: The handler can be interrupted by higher-level ints + * (greater IPL, no MFP priorities!). This is the method of choice for ints + * which should be slow, but are not from a MFP. + * + * The feature of more than one handler for one int source is still there, but + * only applicable if all handers are of the same type. To not slow down + * processing of ints with only one handler by the chaining feature, the list + * calling function atari_call_irq_list() is only plugged in at the time the + * second handler is registered. + * + * Implementation notes: For fast-as-possible int handling, there are separate + * entry points for each type (slow/fast/prio). The assembler handler calls + * the irq directly in the usual case, no C wrapper is involved. In case of + * multiple handlers, atari_call_irq_list() is registered as handler and calls + * in turn the real irq's. To ease access from assembler level to the irq + * function pointer and accompanying data, these two are stored in a separate + * array, irq_handler[]. The rest of data (type, name) are put into a second + * array, irq_param, that is accessed from C only. For each slow interrupt (32 + * in all) there are separate handler functions, which makes it possible to + * hard-code the MFP register address and value, are necessary to mask the + * int. If there'd be only one generic function, lots of calculations would be + * needed to determine MFP register and int mask from the vector number :-( + * + * Furthermore, slow ints may not lower the IPL below its previous value + * (before the int happened). This is needed so that an int of class PRIO, on + * that this int may be stacked, cannot be reentered. This feature is + * implemented as follows: If the stack frame format is 1 (throwaway), the int + * is not stacked, and the IPL is anded with 0xfbff, resulting in a new level + * 2, which still blocks the HSYNC, but no interrupts of interest. If the + * frame format is 0, the int is nested, and the old IPL value can be found in + * the sr copy in the frame. + */ + + +#define NUM_INT_SOURCES (8 + NUM_ATARI_SOURCES) + +typedef void (*asm_irq_handler)(void); + +struct irqhandler { + irqreturn_t (*handler)(int, void *, struct pt_regs *); + void *dev_id; +}; + +struct irqparam { + unsigned long flags; + const char *devname; +}; + +/* + * Array with irq's and their parameter data. This array is accessed from low + * level assembler code, so an element size of 8 allows usage of index scaling + * addressing mode. + */ +static struct irqhandler irq_handler[NUM_INT_SOURCES]; + +/* + * 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 irq_param[NUM_INT_SOURCES]; + +/* + * Bitmap for free interrupt vector numbers + * (new vectors starting from 0x70 can be allocated by + * atari_register_vme_int()) + */ +static int free_vme_vec_bitmap; + +/* check for valid int number (complex, sigh...) */ +#define IS_VALID_INTNO(n) \ + ((n) > 0 && \ + /* autovec and ST-MFP ok anyway */ \ + (((n) < TTMFP_SOURCE_BASE) || \ + /* TT-MFP ok if present */ \ + ((n) >= TTMFP_SOURCE_BASE && (n) < SCC_SOURCE_BASE && \ + ATARIHW_PRESENT(TT_MFP)) || \ + /* SCC ok if present and number even */ \ + ((n) >= SCC_SOURCE_BASE && (n) < VME_SOURCE_BASE && \ + !((n) & 1) && ATARIHW_PRESENT(SCC)) || \ + /* greater numbers ok if they are registered VME vectors */ \ + ((n) >= VME_SOURCE_BASE && (n) < VME_SOURCE_BASE + VME_MAX_SOURCES && \ + free_vme_vec_bitmap & (1 << ((n) - VME_SOURCE_BASE))))) + + +/* + * Here start the assembler entry points for interrupts + */ + +#define IRQ_NAME(nr) atari_slow_irq_##nr##_handler(void) + +#define BUILD_SLOW_IRQ(n) \ +asmlinkage void IRQ_NAME(n); \ +/* Dummy function to allow asm with operands. */ \ +void atari_slow_irq_##n##_dummy (void) { \ +__asm__ (__ALIGN_STR "\n" \ +"atari_slow_irq_" #n "_handler:\t" \ +" addl %6,%5\n" /* preempt_count() += HARDIRQ_OFFSET */ \ + SAVE_ALL_INT "\n" \ + GET_CURRENT(%%d0) "\n" \ +" andb #~(1<<(%c3&7)),%a4:w\n" /* mask this interrupt */ \ + /* get old IPL from stack frame */ \ +" bfextu %%sp@(%c2){#5,#3},%%d0\n" \ +" movew %%sr,%%d1\n" \ +" bfins %%d0,%%d1{#21,#3}\n" \ +" movew %%d1,%%sr\n" /* set IPL = previous value */ \ +" addql #1,%a0\n" \ +" lea %a1,%%a0\n" \ +" pea %%sp@\n" /* push addr of frame */ \ +" movel %%a0@(4),%%sp@-\n" /* push handler data */ \ +" pea (%c3+8)\n" /* push int number */ \ +" movel %%a0@,%%a0\n" \ +" jbsr %%a0@\n" /* call the handler */ \ +" addql #8,%%sp\n" \ +" addql #4,%%sp\n" \ +" orw #0x0600,%%sr\n" \ +" andw #0xfeff,%%sr\n" /* set IPL = 6 again */ \ +" orb #(1<<(%c3&7)),%a4:w\n" /* now unmask the int again */ \ +" jbra ret_from_interrupt\n" \ + : : "i" (&kstat_cpu(0).irqs[n+8]), "i" (&irq_handler[n+8]), \ + "n" (PT_OFF_SR), "n" (n), \ + "i" (n & 8 ? (n & 16 ? &tt_mfp.int_mk_a : &mfp.int_mk_a) \ + : (n & 16 ? &tt_mfp.int_mk_b : &mfp.int_mk_b)), \ + "m" (preempt_count()), "di" (HARDIRQ_OFFSET) \ +); \ + for (;;); /* fake noreturn */ \ +} + +BUILD_SLOW_IRQ(0); +BUILD_SLOW_IRQ(1); +BUILD_SLOW_IRQ(2); +BUILD_SLOW_IRQ(3); +BUILD_SLOW_IRQ(4); +BUILD_SLOW_IRQ(5); +BUILD_SLOW_IRQ(6); +BUILD_SLOW_IRQ(7); +BUILD_SLOW_IRQ(8); +BUILD_SLOW_IRQ(9); +BUILD_SLOW_IRQ(10); +BUILD_SLOW_IRQ(11); +BUILD_SLOW_IRQ(12); +BUILD_SLOW_IRQ(13); +BUILD_SLOW_IRQ(14); +BUILD_SLOW_IRQ(15); +BUILD_SLOW_IRQ(16); +BUILD_SLOW_IRQ(17); +BUILD_SLOW_IRQ(18); +BUILD_SLOW_IRQ(19); +BUILD_SLOW_IRQ(20); +BUILD_SLOW_IRQ(21); +BUILD_SLOW_IRQ(22); +BUILD_SLOW_IRQ(23); +BUILD_SLOW_IRQ(24); +BUILD_SLOW_IRQ(25); +BUILD_SLOW_IRQ(26); +BUILD_SLOW_IRQ(27); +BUILD_SLOW_IRQ(28); +BUILD_SLOW_IRQ(29); +BUILD_SLOW_IRQ(30); +BUILD_SLOW_IRQ(31); + +asm_irq_handler slow_handlers[32] = { + [0] = atari_slow_irq_0_handler, + [1] = atari_slow_irq_1_handler, + [2] = atari_slow_irq_2_handler, + [3] = atari_slow_irq_3_handler, + [4] = atari_slow_irq_4_handler, + [5] = atari_slow_irq_5_handler, + [6] = atari_slow_irq_6_handler, + [7] = atari_slow_irq_7_handler, + [8] = atari_slow_irq_8_handler, + [9] = atari_slow_irq_9_handler, + [10] = atari_slow_irq_10_handler, + [11] = atari_slow_irq_11_handler, + [12] = atari_slow_irq_12_handler, + [13] = atari_slow_irq_13_handler, + [14] = atari_slow_irq_14_handler, + [15] = atari_slow_irq_15_handler, + [16] = atari_slow_irq_16_handler, + [17] = atari_slow_irq_17_handler, + [18] = atari_slow_irq_18_handler, + [19] = atari_slow_irq_19_handler, + [20] = atari_slow_irq_20_handler, + [21] = atari_slow_irq_21_handler, + [22] = atari_slow_irq_22_handler, + [23] = atari_slow_irq_23_handler, + [24] = atari_slow_irq_24_handler, + [25] = atari_slow_irq_25_handler, + [26] = atari_slow_irq_26_handler, + [27] = atari_slow_irq_27_handler, + [28] = atari_slow_irq_28_handler, + [29] = atari_slow_irq_29_handler, + [30] = atari_slow_irq_30_handler, + [31] = atari_slow_irq_31_handler +}; + +asmlinkage void atari_fast_irq_handler( void ); +asmlinkage void atari_prio_irq_handler( void ); + +/* Dummy function to allow asm with operands. */ +void atari_fast_prio_irq_dummy (void) { +__asm__ (__ALIGN_STR "\n" +"atari_fast_irq_handler:\n\t" + "orw #0x700,%%sr\n" /* disable all interrupts */ +"atari_prio_irq_handler:\n\t" + "addl %3,%2\n\t" /* preempt_count() += HARDIRQ_OFFSET */ + SAVE_ALL_INT "\n\t" + GET_CURRENT(%%d0) "\n\t" + /* get vector number from stack frame and convert to source */ + "bfextu %%sp@(%c1){#4,#10},%%d0\n\t" + "subw #(0x40-8),%%d0\n\t" + "jpl 1f\n\t" + "addw #(0x40-8-0x18),%%d0\n" + "1:\tlea %a0,%%a0\n\t" + "addql #1,%%a0@(%%d0:l:4)\n\t" + "lea irq_handler,%%a0\n\t" + "lea %%a0@(%%d0:l:8),%%a0\n\t" + "pea %%sp@\n\t" /* push frame address */ + "movel %%a0@(4),%%sp@-\n\t" /* push handler data */ + "movel %%d0,%%sp@-\n\t" /* push int number */ + "movel %%a0@,%%a0\n\t" + "jsr %%a0@\n\t" /* and call the handler */ + "addql #8,%%sp\n\t" + "addql #4,%%sp\n\t" + "jbra ret_from_interrupt" + : : "i" (&kstat_cpu(0).irqs), "n" (PT_OFF_FORMATVEC), + "m" (preempt_count()), "di" (HARDIRQ_OFFSET) +); + for (;;); +} + +/* GK: + * HBL IRQ handler for Falcon. Nobody needs it :-) + * ++andreas: raise ipl to disable further HBLANK interrupts. + */ +asmlinkage void falcon_hblhandler(void); +asm(".text\n" +__ALIGN_STR "\n\t" +"falcon_hblhandler:\n\t" + "orw #0x200,%sp@\n\t" /* set saved ipl to 2 */ + "rte"); + +/* Defined in entry.S; only increments 'num_spurious' */ +asmlinkage void bad_interrupt(void); + +extern void atari_microwire_cmd( int cmd ); + +extern int atari_SCC_reset_done; + +/* + * void atari_init_IRQ (void) + * + * Parameters: None + * + * Returns: Nothing + * + * This function should be called during kernel startup to initialize + * the atari IRQ handling routines. + */ + +void __init atari_init_IRQ(void) +{ + int i; + + /* initialize the vector table */ + for (i = 0; i < NUM_INT_SOURCES; ++i) { + vectors[IRQ_SOURCE_TO_VECTOR(i)] = bad_interrupt; + } + + /* Initialize the MFP(s) */ + +#ifdef ATARI_USE_SOFTWARE_EOI + mfp.vec_adr = 0x48; /* Software EOI-Mode */ +#else + mfp.vec_adr = 0x40; /* Automatic EOI-Mode */ +#endif + mfp.int_en_a = 0x00; /* turn off MFP-Ints */ + mfp.int_en_b = 0x00; + mfp.int_mk_a = 0xff; /* no Masking */ + mfp.int_mk_b = 0xff; + + if (ATARIHW_PRESENT(TT_MFP)) { +#ifdef ATARI_USE_SOFTWARE_EOI + tt_mfp.vec_adr = 0x58; /* Software EOI-Mode */ +#else + tt_mfp.vec_adr = 0x50; /* Automatic EOI-Mode */ +#endif + tt_mfp.int_en_a = 0x00; /* turn off MFP-Ints */ + tt_mfp.int_en_b = 0x00; + tt_mfp.int_mk_a = 0xff; /* no Masking */ + tt_mfp.int_mk_b = 0xff; + } + + if (ATARIHW_PRESENT(SCC) && !atari_SCC_reset_done) { + scc.cha_a_ctrl = 9; + MFPDELAY(); + scc.cha_a_ctrl = (char) 0xc0; /* hardware reset */ + } + + if (ATARIHW_PRESENT(SCU)) { + /* init the SCU if present */ + tt_scu.sys_mask = 0x10; /* enable VBL (for the cursor) and + * disable HSYNC interrupts (who + * needs them?) MFP and SCC are + * enabled in VME mask + */ + tt_scu.vme_mask = 0x60; /* enable MFP and SCC ints */ + } + else { + /* If no SCU and no Hades, the HSYNC interrupt needs to be + * disabled this way. (Else _inthandler in kernel/sys_call.S + * gets overruns) + */ + + if (!MACH_IS_HADES) + vectors[VEC_INT2] = falcon_hblhandler; + } + + if (ATARIHW_PRESENT(PCM_8BIT) && ATARIHW_PRESENT(MICROWIRE)) { + /* Initialize the LM1992 Sound Controller to enable + the PSG sound. This is misplaced here, it should + be in an atasound_init(), that doesn't exist yet. */ + atari_microwire_cmd(MW_LM1992_PSG_HIGH); + } + + stdma_init(); + + /* Initialize the PSG: all sounds off, both ports output */ + sound_ym.rd_data_reg_sel = 7; + sound_ym.wd_data = 0xff; +} + + +static irqreturn_t atari_call_irq_list( int irq, void *dev_id, struct pt_regs *fp ) +{ + irq_node_t *node; + + for (node = (irq_node_t *)dev_id; node; node = node->next) + node->handler(irq, node->dev_id, fp); + return IRQ_HANDLED; +} + + +/* + * atari_request_irq : add an interrupt service routine for a particular + * machine specific interrupt source. + * If the addition was successful, it returns 0. + */ + +int atari_request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), + unsigned long flags, const char *devname, void *dev_id) +{ + int vector; + unsigned long oflags = flags; + + /* + * The following is a hack to make some PCI card drivers work, + * which set the SA_SHIRQ flag. + */ + + flags &= ~SA_SHIRQ; + + if (flags == SA_INTERRUPT) { + printk ("%s: SA_INTERRUPT changed to IRQ_TYPE_SLOW for %s\n", + __FUNCTION__, devname); + flags = IRQ_TYPE_SLOW; + } + if (flags < IRQ_TYPE_SLOW || flags > IRQ_TYPE_PRIO) { + printk ("%s: Bad irq type 0x%lx <0x%lx> requested from %s\n", + __FUNCTION__, flags, oflags, devname); + return -EINVAL; + } + if (!IS_VALID_INTNO(irq)) { + printk ("%s: Unknown irq %d requested from %s\n", + __FUNCTION__, irq, devname); + return -ENXIO; + } + vector = IRQ_SOURCE_TO_VECTOR(irq); + + /* + * Check type/source combination: slow ints are (currently) + * only possible for MFP-interrupts. + */ + if (flags == IRQ_TYPE_SLOW && + (irq < STMFP_SOURCE_BASE || irq >= SCC_SOURCE_BASE)) { + printk ("%s: Slow irq requested for non-MFP source %d from %s\n", + __FUNCTION__, irq, devname); + return -EINVAL; + } + + if (vectors[vector] == bad_interrupt) { + /* int has no handler yet */ + irq_handler[irq].handler = handler; + irq_handler[irq].dev_id = dev_id; + irq_param[irq].flags = flags; + irq_param[irq].devname = devname; + vectors[vector] = + (flags == IRQ_TYPE_SLOW) ? slow_handlers[irq-STMFP_SOURCE_BASE] : + (flags == IRQ_TYPE_FAST) ? atari_fast_irq_handler : + atari_prio_irq_handler; + /* If MFP int, also enable and umask it */ + atari_turnon_irq(irq); + atari_enable_irq(irq); + + return 0; + } + else if (irq_param[irq].flags == flags) { + /* old handler is of same type -> handlers can be chained */ + irq_node_t *node; + unsigned long flags; + + local_irq_save(flags); + + if (irq_handler[irq].handler != atari_call_irq_list) { + /* Only one handler yet, make a node for this first one */ + if (!(node = new_irq_node())) + return -ENOMEM; + node->handler = irq_handler[irq].handler; + node->dev_id = irq_handler[irq].dev_id; + node->devname = irq_param[irq].devname; + node->next = NULL; + + irq_handler[irq].handler = atari_call_irq_list; + irq_handler[irq].dev_id = node; + irq_param[irq].devname = "chained"; + } + + if (!(node = new_irq_node())) + return -ENOMEM; + node->handler = handler; + node->dev_id = dev_id; + node->devname = devname; + /* new handlers are put in front of the queue */ + node->next = irq_handler[irq].dev_id; + irq_handler[irq].dev_id = node; + + local_irq_restore(flags); + return 0; + } else { + printk ("%s: Irq %d allocated by other type int (call from %s)\n", + __FUNCTION__, irq, devname); + return -EBUSY; + } +} + +void atari_free_irq(unsigned int irq, void *dev_id) +{ + unsigned long flags; + int vector; + irq_node_t **list, *node; + + if (!IS_VALID_INTNO(irq)) { + printk("%s: Unknown irq %d\n", __FUNCTION__, irq); + return; + } + + vector = IRQ_SOURCE_TO_VECTOR(irq); + if (vectors[vector] == bad_interrupt) + goto not_found; + + local_irq_save(flags); + + if (irq_handler[irq].handler != atari_call_irq_list) { + /* It's the only handler for the interrupt */ + if (irq_handler[irq].dev_id != dev_id) { + local_irq_restore(flags); + goto not_found; + } + irq_handler[irq].handler = NULL; + irq_handler[irq].dev_id = NULL; + irq_param[irq].devname = NULL; + vectors[vector] = bad_interrupt; + /* If MFP int, also disable it */ + atari_disable_irq(irq); + atari_turnoff_irq(irq); + + local_irq_restore(flags); + return; + } + + /* The interrupt is chained, find the irq on the list */ + for(list = (irq_node_t **)&irq_handler[irq].dev_id; *list; list = &(*list)->next) { + if ((*list)->dev_id == dev_id) break; + } + if (!*list) { + local_irq_restore(flags); + goto not_found; + } + + (*list)->handler = NULL; /* Mark it as free for reallocation */ + *list = (*list)->next; + + /* If there's now only one handler, unchain the interrupt, i.e. plug in + * the handler directly again and omit atari_call_irq_list */ + node = (irq_node_t *)irq_handler[irq].dev_id; + if (node && !node->next) { + irq_handler[irq].handler = node->handler; + irq_handler[irq].dev_id = node->dev_id; + irq_param[irq].devname = node->devname; + node->handler = NULL; /* Mark it as free for reallocation */ + } + + local_irq_restore(flags); + return; + +not_found: + printk("%s: tried to remove invalid irq\n", __FUNCTION__); + return; +} + + +/* + * atari_register_vme_int() returns the number of a free interrupt vector for + * hardware with a programmable int vector (probably a VME board). + */ + +unsigned long atari_register_vme_int(void) +{ + int i; + + for(i = 0; i < 32; i++) + if((free_vme_vec_bitmap & (1 << i)) == 0) + break; + + if(i == 16) + return 0; + + free_vme_vec_bitmap |= 1 << i; + return (VME_SOURCE_BASE + i); +} + + +void atari_unregister_vme_int(unsigned long irq) +{ + if(irq >= VME_SOURCE_BASE && irq < VME_SOURCE_BASE + VME_MAX_SOURCES) { + irq -= VME_SOURCE_BASE; + free_vme_vec_bitmap &= ~(1 << irq); + } +} + + +int show_atari_interrupts(struct seq_file *p, void *v) +{ + int i; + + for (i = 0; i < NUM_INT_SOURCES; ++i) { + if (vectors[IRQ_SOURCE_TO_VECTOR(i)] == bad_interrupt) + continue; + if (i < STMFP_SOURCE_BASE) + seq_printf(p, "auto %2d: %10u ", + i, kstat_cpu(0).irqs[i]); + else + seq_printf(p, "vec $%02x: %10u ", + IRQ_SOURCE_TO_VECTOR(i), + kstat_cpu(0).irqs[i]); + + if (irq_handler[i].handler != atari_call_irq_list) { + seq_printf(p, "%s\n", irq_param[i].devname); + } + else { + irq_node_t *n; + for( n = (irq_node_t *)irq_handler[i].dev_id; n; n = n->next ) { + seq_printf(p, "%s\n", n->devname); + if (n->next) + seq_puts(p, " " ); + } + } + } + if (num_spurious) + seq_printf(p, "spurio.: %10u\n", num_spurious); + + return 0; +} + + diff --git a/arch/m68k/atari/atari_ksyms.c b/arch/m68k/atari/atari_ksyms.c new file mode 100644 index 00000000000..a0475715153 --- /dev/null +++ b/arch/m68k/atari/atari_ksyms.c @@ -0,0 +1,35 @@ +#include <linux/module.h> + +#include <asm/ptrace.h> +#include <asm/traps.h> +#include <asm/atarihw.h> +#include <asm/atariints.h> +#include <asm/atarikb.h> +#include <asm/atari_joystick.h> +#include <asm/atari_stdma.h> +#include <asm/atari_stram.h> + +extern void atari_microwire_cmd( int cmd ); +extern int atari_MFP_init_done; +extern int atari_SCC_init_done; +extern int atari_SCC_reset_done; + +EXPORT_SYMBOL(atari_mch_cookie); +EXPORT_SYMBOL(atari_mch_type); +EXPORT_SYMBOL(atari_hw_present); +EXPORT_SYMBOL(atari_switches); +EXPORT_SYMBOL(atari_dont_touch_floppy_select); +EXPORT_SYMBOL(atari_register_vme_int); +EXPORT_SYMBOL(atari_unregister_vme_int); +EXPORT_SYMBOL(stdma_lock); +EXPORT_SYMBOL(stdma_release); +EXPORT_SYMBOL(stdma_others_waiting); +EXPORT_SYMBOL(stdma_islocked); +EXPORT_SYMBOL(atari_stram_alloc); +EXPORT_SYMBOL(atari_stram_free); + +EXPORT_SYMBOL(atari_MFP_init_done); +EXPORT_SYMBOL(atari_SCC_init_done); +EXPORT_SYMBOL(atari_SCC_reset_done); + +EXPORT_SYMBOL(atari_microwire_cmd); diff --git a/arch/m68k/atari/atasound.c b/arch/m68k/atari/atasound.c new file mode 100644 index 00000000000..ee04250eb56 --- /dev/null +++ b/arch/m68k/atari/atasound.c @@ -0,0 +1,109 @@ +/* + * linux/arch/m68k/atari/atasound.c + * + * ++Geert: Moved almost all stuff to linux/drivers/sound/ + * + * The author of atari_nosound, atari_mksound and atari_microwire_cmd is + * unknown. (++roman: That's me... :-) + * + * 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. + * + * 1998-05-31 ++andreas: atari_mksound rewritten to always use the envelope, + * no timer, atari_nosound removed. + * + */ + + +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/major.h> +#include <linux/fcntl.h> +#include <linux/errno.h> +#include <linux/mm.h> + +#include <asm/atarihw.h> +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/pgtable.h> +#include <asm/atariints.h> + + +/* + * stuff from the old atasound.c + */ + +void atari_microwire_cmd (int cmd) +{ + tt_microwire.mask = 0x7ff; + tt_microwire.data = MW_LM1992_ADDR | cmd; + + /* Busy wait for data being completely sent :-( */ + while( tt_microwire.mask != 0x7ff) + ; +} + + +/* PSG base frequency */ +#define PSG_FREQ 125000 +/* PSG envelope base frequency times 10 */ +#define PSG_ENV_FREQ_10 78125 + +void atari_mksound (unsigned int hz, unsigned int ticks) +{ + /* Generates sound of some frequency for some number of clock + ticks. */ + unsigned long flags; + unsigned char tmp; + int period; + + local_irq_save(flags); + + + /* Disable generator A in mixer control. */ + sound_ym.rd_data_reg_sel = 7; + tmp = sound_ym.rd_data_reg_sel; + tmp |= 011; + sound_ym.wd_data = tmp; + + if (hz) { + /* Convert from frequency value to PSG period value (base + frequency 125 kHz). */ + + period = PSG_FREQ / hz; + + if (period > 0xfff) period = 0xfff; + + /* Set generator A frequency to hz. */ + sound_ym.rd_data_reg_sel = 0; + sound_ym.wd_data = period & 0xff; + sound_ym.rd_data_reg_sel = 1; + sound_ym.wd_data = (period >> 8) & 0xf; + if (ticks) { + /* Set length of envelope (max 8 sec). */ + int length = (ticks * PSG_ENV_FREQ_10) / HZ / 10; + + if (length > 0xffff) length = 0xffff; + sound_ym.rd_data_reg_sel = 11; + sound_ym.wd_data = length & 0xff; + sound_ym.rd_data_reg_sel = 12; + sound_ym.wd_data = length >> 8; + /* Envelope form: max -> min single. */ + sound_ym.rd_data_reg_sel = 13; + sound_ym.wd_data = 0; + /* Use envelope for generator A. */ + sound_ym.rd_data_reg_sel = 8; + sound_ym.wd_data = 0x10; + } else { + /* Set generator A level to maximum, no envelope. */ + sound_ym.rd_data_reg_sel = 8; + sound_ym.wd_data = 15; + } + /* Turn on generator A in mixer control. */ + sound_ym.rd_data_reg_sel = 7; + tmp &= ~1; + sound_ym.wd_data = tmp; + } + local_irq_restore(flags); +} diff --git a/arch/m68k/atari/atasound.h b/arch/m68k/atari/atasound.h new file mode 100644 index 00000000000..1362762b8c0 --- /dev/null +++ b/arch/m68k/atari/atasound.h @@ -0,0 +1,33 @@ +/* + * Minor numbers for the sound driver. + * + * Unfortunately Creative called the codec chip of SB as a DSP. For this + * reason the /dev/dsp is reserved for digitized audio use. There is a + * device for true DSP processors but it will be called something else. + * In v3.0 it's /dev/sndproc but this could be a temporary solution. + */ + +#define SND_NDEVS 256 /* Number of supported devices */ +#define SND_DEV_CTL 0 /* Control port /dev/mixer */ +#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM + synthesizer and MIDI output) */ +#define SND_DEV_MIDIN 2 /* Raw midi access */ +#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ +#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ +#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ +#define SND_DEV_STATUS 6 /* /dev/sndstat */ +/* #7 not in use now. Was in 2.4. Free for use after v3.0. */ +#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */ +#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ +#define SND_DEV_PSS SND_DEV_SNDPROC + +#define DSP_DEFAULT_SPEED 8000 + +#define ON 1 +#define OFF 0 + +#define MAX_AUDIO_DEV 5 +#define MAX_MIXER_DEV 2 +#define MAX_SYNTH_DEV 3 +#define MAX_MIDI_DEV 6 +#define MAX_TIMER_DEV 3 diff --git a/arch/m68k/atari/config.c b/arch/m68k/atari/config.c new file mode 100644 index 00000000000..9261d2deeaf --- /dev/null +++ b/arch/m68k/atari/config.c @@ -0,0 +1,726 @@ +/* + * linux/arch/m68k/atari/config.c + * + * Copyright (C) 1994 Bjoern Brauel + * + * 5/2/94 Roman Hodek: + * Added setting of time_adj to get a better clock. + * + * 5/14/94 Roman Hodek: + * gettod() for TT + * + * 5/15/94 Roman Hodek: + * hard_reset_now() for Atari (and others?) + * + * 94/12/30 Andreas Schwab: + * atari_sched_init fixed to get precise clock. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +/* + * Miscellaneous atari stuff + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/vt_kern.h> + +#include <asm/bootinfo.h> +#include <asm/setup.h> +#include <asm/atarihw.h> +#include <asm/atariints.h> +#include <asm/atari_stram.h> +#include <asm/system.h> +#include <asm/machdep.h> +#include <asm/hwtest.h> +#include <asm/io.h> + +u_long atari_mch_cookie; +u_long atari_mch_type; +struct atari_hw_present atari_hw_present; +u_long atari_switches; +int atari_dont_touch_floppy_select; +int atari_rtc_year_offset; + +/* local function prototypes */ +static void atari_reset( void ); +#ifdef CONFIG_ATARI_FLOPPY +extern void atari_floppy_setup(char *, int *); +#endif +static void atari_get_model(char *model); +static int atari_get_hardware_list(char *buffer); + +/* atari specific irq functions */ +extern void atari_init_IRQ (void); +extern int atari_request_irq (unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), + unsigned long flags, const char *devname, void *dev_id); +extern void atari_free_irq (unsigned int irq, void *dev_id); +extern void atari_enable_irq (unsigned int); +extern void atari_disable_irq (unsigned int); +extern int show_atari_interrupts (struct seq_file *, void *); +extern void atari_mksound( unsigned int count, unsigned int ticks ); +#ifdef CONFIG_HEARTBEAT +static void atari_heartbeat( int on ); +#endif + +/* atari specific timer functions (in time.c) */ +extern void atari_sched_init(irqreturn_t (*)(int, void *, struct pt_regs *)); +extern unsigned long atari_gettimeoffset (void); +extern int atari_mste_hwclk (int, struct rtc_time *); +extern int atari_tt_hwclk (int, struct rtc_time *); +extern int atari_mste_set_clock_mmss (unsigned long); +extern int atari_tt_set_clock_mmss (unsigned long); + +/* atari specific debug functions (in debug.c) */ +extern void atari_debug_init(void); + + +/* I've moved hwreg_present() and hwreg_present_bywrite() out into + * mm/hwtest.c, to avoid having multiple copies of the same routine + * in the kernel [I wanted them in hp300 and they were already used + * in the nubus code. NB: I don't have an Atari so this might (just + * conceivably) break something. + * I've preserved the #if 0 version of hwreg_present_bywrite() here + * for posterity. + * -- Peter Maydell <pmaydell@chiark.greenend.org.uk>, 05/1998 + */ + +#if 0 +static int __init +hwreg_present_bywrite(volatile void *regp, unsigned char val) +{ + int ret; + long save_sp, save_vbr; + static long tmp_vectors[3] = { [2] = (long)&&after_test }; + + __asm__ __volatile__ + ( "movec %/vbr,%2\n\t" /* save vbr value */ + "movec %4,%/vbr\n\t" /* set up temporary vectors */ + "movel %/sp,%1\n\t" /* save sp */ + "moveq #0,%0\n\t" /* assume not present */ + "moveb %5,%3@\n\t" /* write the hardware reg */ + "cmpb %3@,%5\n\t" /* compare it */ + "seq %0" /* comes here only if reg */ + /* is present */ + : "=d&" (ret), "=r&" (save_sp), "=r&" (save_vbr) + : "a" (regp), "r" (tmp_vectors), "d" (val) + ); + after_test: + __asm__ __volatile__ + ( "movel %0,%/sp\n\t" /* restore sp */ + "movec %1,%/vbr" /* restore vbr */ + : : "r" (save_sp), "r" (save_vbr) : "sp" + ); + + return( ret ); +} +#endif + + +/* ++roman: This is a more elaborate test for an SCC chip, since the plain + * Medusa board generates DTACK at the SCC's standard addresses, but a SCC + * board in the Medusa is possible. Also, the addresses where the ST_ESCC + * resides generate DTACK without the chip, too. + * The method is to write values into the interrupt vector register, that + * should be readable without trouble (from channel A!). + */ + +static int __init scc_test( volatile char *ctla ) +{ + if (!hwreg_present( ctla )) + return( 0 ); + MFPDELAY(); + + *ctla = 2; MFPDELAY(); + *ctla = 0x40; MFPDELAY(); + + *ctla = 2; MFPDELAY(); + if (*ctla != 0x40) return( 0 ); + MFPDELAY(); + + *ctla = 2; MFPDELAY(); + *ctla = 0x60; MFPDELAY(); + + *ctla = 2; MFPDELAY(); + if (*ctla != 0x60) return( 0 ); + + return( 1 ); +} + + + /* + * Parse an Atari-specific record in the bootinfo + */ + +int __init atari_parse_bootinfo(const struct bi_record *record) +{ + int unknown = 0; + const u_long *data = record->data; + + switch (record->tag) { + case BI_ATARI_MCH_COOKIE: + atari_mch_cookie = *data; + break; + case BI_ATARI_MCH_TYPE: + atari_mch_type = *data; + break; + default: + unknown = 1; + } + return(unknown); +} + + +/* Parse the Atari-specific switches= option. */ +void __init atari_switches_setup( const char *str, unsigned len ) +{ + char switches[len+1]; + char *p; + int ovsc_shift; + char *args = switches; + + /* copy string to local array, strsep works destructively... */ + strlcpy( switches, str, sizeof(switches) ); + atari_switches = 0; + + /* parse the options */ + while ((p = strsep(&args, ",")) != NULL) { + if (!*p) continue; + ovsc_shift = 0; + if (strncmp( p, "ov_", 3 ) == 0) { + p += 3; + ovsc_shift = ATARI_SWITCH_OVSC_SHIFT; + } + + if (strcmp( p, "ikbd" ) == 0) { + /* RTS line of IKBD ACIA */ + atari_switches |= ATARI_SWITCH_IKBD << ovsc_shift; + } + else if (strcmp( p, "midi" ) == 0) { + /* RTS line of MIDI ACIA */ + atari_switches |= ATARI_SWITCH_MIDI << ovsc_shift; + } + else if (strcmp( p, "snd6" ) == 0) { + atari_switches |= ATARI_SWITCH_SND6 << ovsc_shift; + } + else if (strcmp( p, "snd7" ) == 0) { + atari_switches |= ATARI_SWITCH_SND7 << ovsc_shift; + } + } +} + + + /* + * Setup the Atari configuration info + */ + +void __init config_atari(void) +{ + unsigned short tos_version; + + memset(&atari_hw_present, 0, sizeof(atari_hw_present)); + + atari_debug_init(); + + ioport_resource.end = 0xFFFFFFFF; /* Change size of I/O space from 64KB + to 4GB. */ + + mach_sched_init = atari_sched_init; + mach_init_IRQ = atari_init_IRQ; + mach_request_irq = atari_request_irq; + mach_free_irq = atari_free_irq; + enable_irq = atari_enable_irq; + disable_irq = atari_disable_irq; + mach_get_model = atari_get_model; + mach_get_hardware_list = atari_get_hardware_list; + mach_get_irq_list = show_atari_interrupts; + mach_gettimeoffset = atari_gettimeoffset; + mach_reset = atari_reset; +#ifdef CONFIG_ATARI_FLOPPY + mach_floppy_setup = atari_floppy_setup; +#endif +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + mach_max_dma_address = 0xffffff; +#if defined(CONFIG_INPUT_M68K_BEEP) || defined(CONFIG_INPUT_M68K_BEEP_MODULE) + mach_beep = atari_mksound; +#endif +#ifdef CONFIG_HEARTBEAT + mach_heartbeat = atari_heartbeat; +#endif + + /* Set switches as requested by the user */ + if (atari_switches & ATARI_SWITCH_IKBD) + acia.key_ctrl = ACIA_DIV64 | ACIA_D8N1S | ACIA_RHTID; + if (atari_switches & ATARI_SWITCH_MIDI) + acia.mid_ctrl = ACIA_DIV16 | ACIA_D8N1S | ACIA_RHTID; + if (atari_switches & (ATARI_SWITCH_SND6|ATARI_SWITCH_SND7)) { + sound_ym.rd_data_reg_sel = 14; + sound_ym.wd_data = sound_ym.rd_data_reg_sel | + ((atari_switches&ATARI_SWITCH_SND6) ? 0x40 : 0) | + ((atari_switches&ATARI_SWITCH_SND7) ? 0x80 : 0); + } + + /* ++bjoern: + * Determine hardware present + */ + + printk( "Atari hardware found: " ); + if (MACH_IS_MEDUSA || MACH_IS_HADES) { + /* There's no Atari video hardware on the Medusa, but all the + * addresses below generate a DTACK so no bus error occurs! */ + } + else if (hwreg_present( f030_xreg )) { + ATARIHW_SET(VIDEL_SHIFTER); + printk( "VIDEL " ); + /* This is a temporary hack: If there is Falcon video + * hardware, we assume that the ST-DMA serves SCSI instead of + * ACSI. In the future, there should be a better method for + * this... + */ + ATARIHW_SET(ST_SCSI); + printk( "STDMA-SCSI " ); + } + else if (hwreg_present( tt_palette )) { + ATARIHW_SET(TT_SHIFTER); + printk( "TT_SHIFTER " ); + } + else if (hwreg_present( &shifter.bas_hi )) { + if (hwreg_present( &shifter.bas_lo ) && + (shifter.bas_lo = 0x0aau, shifter.bas_lo == 0x0aau)) { + ATARIHW_SET(EXTD_SHIFTER); + printk( "EXTD_SHIFTER " ); + } + else { + ATARIHW_SET(STND_SHIFTER); + printk( "STND_SHIFTER " ); + } + } + if (hwreg_present( &mfp.par_dt_reg )) { + ATARIHW_SET(ST_MFP); + printk( "ST_MFP " ); + } + if (hwreg_present( &tt_mfp.par_dt_reg )) { + ATARIHW_SET(TT_MFP); + printk( "TT_MFP " ); + } + if (hwreg_present( &tt_scsi_dma.dma_addr_hi )) { + ATARIHW_SET(SCSI_DMA); + printk( "TT_SCSI_DMA " ); + } + if (!MACH_IS_HADES && hwreg_present( &st_dma.dma_hi )) { + ATARIHW_SET(STND_DMA); + printk( "STND_DMA " ); + } + if (MACH_IS_MEDUSA || /* The ST-DMA address registers aren't readable + * on all Medusas, so the test below may fail */ + (hwreg_present( &st_dma.dma_vhi ) && + (st_dma.dma_vhi = 0x55) && (st_dma.dma_hi = 0xaa) && + st_dma.dma_vhi == 0x55 && st_dma.dma_hi == 0xaa && + (st_dma.dma_vhi = 0xaa) && (st_dma.dma_hi = 0x55) && + st_dma.dma_vhi == 0xaa && st_dma.dma_hi == 0x55)) { + ATARIHW_SET(EXTD_DMA); + printk( "EXTD_DMA " ); + } + if (hwreg_present( &tt_scsi.scsi_data )) { + ATARIHW_SET(TT_SCSI); + printk( "TT_SCSI " ); + } + if (hwreg_present( &sound_ym.rd_data_reg_sel )) { + ATARIHW_SET(YM_2149); + printk( "YM2149 " ); + } + if (!MACH_IS_MEDUSA && !MACH_IS_HADES && + hwreg_present( &tt_dmasnd.ctrl )) { + ATARIHW_SET(PCM_8BIT); + printk( "PCM " ); + } + if (!MACH_IS_HADES && hwreg_present( &falcon_codec.unused5 )) { + ATARIHW_SET(CODEC); + printk( "CODEC " ); + } + if (hwreg_present( &dsp56k_host_interface.icr )) { + ATARIHW_SET(DSP56K); + printk( "DSP56K " ); + } + if (hwreg_present( &tt_scc_dma.dma_ctrl ) && +#if 0 + /* This test sucks! Who knows some better? */ + (tt_scc_dma.dma_ctrl = 0x01, (tt_scc_dma.dma_ctrl & 1) == 1) && + (tt_scc_dma.dma_ctrl = 0x00, (tt_scc_dma.dma_ctrl & 1) == 0) +#else + !MACH_IS_MEDUSA && !MACH_IS_HADES +#endif + ) { + ATARIHW_SET(SCC_DMA); + printk( "SCC_DMA " ); + } + if (scc_test( &scc.cha_a_ctrl )) { + ATARIHW_SET(SCC); + printk( "SCC " ); + } + if (scc_test( &st_escc.cha_b_ctrl )) { + ATARIHW_SET( ST_ESCC ); + printk( "ST_ESCC " ); + } + if (MACH_IS_HADES) + { + ATARIHW_SET( VME ); + printk( "VME " ); + } + else if (hwreg_present( &tt_scu.sys_mask )) { + ATARIHW_SET(SCU); + /* Assume a VME bus if there's a SCU */ + ATARIHW_SET( VME ); + printk( "VME SCU " ); + } + if (hwreg_present( (void *)(0xffff9210) )) { + ATARIHW_SET(ANALOG_JOY); + printk( "ANALOG_JOY " ); + } + if (!MACH_IS_HADES && hwreg_present( blitter.halftone )) { + ATARIHW_SET(BLITTER); + printk( "BLITTER " ); + } + if (hwreg_present((void *)0xfff00039)) { + ATARIHW_SET(IDE); + printk( "IDE " ); + } +#if 1 /* This maybe wrong */ + if (!MACH_IS_MEDUSA && !MACH_IS_HADES && + hwreg_present( &tt_microwire.data ) && + hwreg_present( &tt_microwire.mask ) && + (tt_microwire.mask = 0x7ff, + udelay(1), + tt_microwire.data = MW_LM1992_PSG_HIGH | MW_LM1992_ADDR, + udelay(1), + tt_microwire.data != 0)) { + ATARIHW_SET(MICROWIRE); + while (tt_microwire.mask != 0x7ff) ; + printk( "MICROWIRE " ); + } +#endif + if (hwreg_present( &tt_rtc.regsel )) { + ATARIHW_SET(TT_CLK); + printk( "TT_CLK " ); + mach_hwclk = atari_tt_hwclk; + mach_set_clock_mmss = atari_tt_set_clock_mmss; + } + if (!MACH_IS_HADES && hwreg_present( &mste_rtc.sec_ones)) { + ATARIHW_SET(MSTE_CLK); + printk( "MSTE_CLK "); + mach_hwclk = atari_mste_hwclk; + mach_set_clock_mmss = atari_mste_set_clock_mmss; + } + if (!MACH_IS_MEDUSA && !MACH_IS_HADES && + hwreg_present( &dma_wd.fdc_speed ) && + hwreg_write( &dma_wd.fdc_speed, 0 )) { + ATARIHW_SET(FDCSPEED); + printk( "FDC_SPEED "); + } + if (!MACH_IS_HADES && !ATARIHW_PRESENT(ST_SCSI)) { + ATARIHW_SET(ACSI); + printk( "ACSI " ); + } + printk("\n"); + + if (CPU_IS_040_OR_060) + /* Now it seems to be safe to turn of the tt0 transparent + * translation (the one that must not be turned off in + * head.S...) + */ + __asm__ volatile ("moveq #0,%/d0\n\t" + ".chip 68040\n\t" + "movec %%d0,%%itt0\n\t" + "movec %%d0,%%dtt0\n\t" + ".chip 68k" + : /* no outputs */ + : /* no inputs */ + : "d0"); + + /* allocator for memory that must reside in st-ram */ + atari_stram_init (); + + /* Set up a mapping for the VMEbus address region: + * + * VME is either at phys. 0xfexxxxxx (TT) or 0xa00000..0xdfffff + * (MegaSTE) In both cases, the whole 16 MB chunk is mapped at + * 0xfe000000 virt., because this can be done with a single + * transparent translation. On the 68040, lots of often unused + * page tables would be needed otherwise. On a MegaSTE or similar, + * the highest byte is stripped off by hardware due to the 24 bit + * design of the bus. + */ + + if (CPU_IS_020_OR_030) { + unsigned long tt1_val; + tt1_val = 0xfe008543; /* Translate 0xfexxxxxx, enable, cache + * inhibit, read and write, FDC mask = 3, + * FDC val = 4 -> Supervisor only */ + __asm__ __volatile__ ( ".chip 68030\n\t" + "pmove %0@,%/tt1\n\t" + ".chip 68k" + : : "a" (&tt1_val) ); + } + else { + __asm__ __volatile__ + ( "movel %0,%/d0\n\t" + ".chip 68040\n\t" + "movec %%d0,%%itt1\n\t" + "movec %%d0,%%dtt1\n\t" + ".chip 68k" + : + : "g" (0xfe00a040) /* Translate 0xfexxxxxx, enable, + * supervisor only, non-cacheable/ + * serialized, writable */ + : "d0" ); + + } + + /* Fetch tos version at Physical 2 */ + /* We my not be able to access this address if the kernel is + loaded to st ram, since the first page is unmapped. On the + Medusa this is always the case and there is nothing we can do + about this, so we just assume the smaller offset. For the TT + we use the fact that in head.S we have set up a mapping + 0xFFxxxxxx -> 0x00xxxxxx, so that the first 16MB is accessible + in the last 16MB of the address space. */ + tos_version = (MACH_IS_MEDUSA || MACH_IS_HADES) ? + 0xfff : *(unsigned short *)0xff000002; + atari_rtc_year_offset = (tos_version < 0x306) ? 70 : 68; +} + +#ifdef CONFIG_HEARTBEAT +static void atari_heartbeat( int on ) +{ + unsigned char tmp; + unsigned long flags; + + if (atari_dont_touch_floppy_select) + return; + + local_irq_save(flags); + sound_ym.rd_data_reg_sel = 14; /* Select PSG Port A */ + tmp = sound_ym.rd_data_reg_sel; + sound_ym.wd_data = on ? (tmp & ~0x02) : (tmp | 0x02); + local_irq_restore(flags); +} +#endif + +/* ++roman: + * + * This function does a reset on machines that lack the ability to + * assert the processor's _RESET signal somehow via hardware. It is + * based on the fact that you can find the initial SP and PC values + * after a reset at physical addresses 0 and 4. This works pretty well + * for Atari machines, since the lowest 8 bytes of physical memory are + * really ROM (mapped by hardware). For other 680x0 machines: don't + * know if it works... + * + * To get the values at addresses 0 and 4, the MMU better is turned + * off first. After that, we have to jump into physical address space + * (the PC before the pmove statement points to the virtual address of + * the code). Getting that physical address is not hard, but the code + * becomes a bit complex since I've tried to ensure that the jump + * statement after the pmove is in the cache already (otherwise the + * processor can't fetch it!). For that, the code first jumps to the + * jump statement with the (virtual) address of the pmove section in + * an address register . The jump statement is surely in the cache + * now. After that, that physical address of the reset code is loaded + * into the same address register, pmove is done and the same jump + * statements goes to the reset code. Since there are not many + * statements between the two jumps, I hope it stays in the cache. + * + * The C code makes heavy use of the GCC features that you can get the + * address of a C label. No hope to compile this with another compiler + * than GCC! + */ + +/* ++andreas: no need for complicated code, just depend on prefetch */ + +static void atari_reset (void) +{ + long tc_val = 0; + long reset_addr; + + /* On the Medusa, phys. 0x4 may contain garbage because it's no + ROM. See above for explanation why we cannot use PTOV(4). */ + reset_addr = MACH_IS_HADES ? 0x7fe00030 : + MACH_IS_MEDUSA || MACH_IS_AB40 ? 0xe00030 : + *(unsigned long *) 0xff000004; + + /* reset ACIA for switch off OverScan, if it's active */ + if (atari_switches & ATARI_SWITCH_OVSC_IKBD) + acia.key_ctrl = ACIA_RESET; + if (atari_switches & ATARI_SWITCH_OVSC_MIDI) + acia.mid_ctrl = ACIA_RESET; + + /* processor independent: turn off interrupts and reset the VBR; + * the caches must be left enabled, else prefetching the final jump + * instruction doesn't work. */ + local_irq_disable(); + __asm__ __volatile__ + ("moveq #0,%/d0\n\t" + "movec %/d0,%/vbr" + : : : "d0" ); + + if (CPU_IS_040_OR_060) { + unsigned long jmp_addr040 = virt_to_phys(&&jmp_addr_label040); + if (CPU_IS_060) { + /* 68060: clear PCR to turn off superscalar operation */ + __asm__ __volatile__ + ("moveq #0,%/d0\n\t" + ".chip 68060\n\t" + "movec %%d0,%%pcr\n\t" + ".chip 68k" + : : : "d0" ); + } + + __asm__ __volatile__ + ("movel %0,%/d0\n\t" + "andl #0xff000000,%/d0\n\t" + "orw #0xe020,%/d0\n\t" /* map 16 MB, enable, cacheable */ + ".chip 68040\n\t" + "movec %%d0,%%itt0\n\t" + "movec %%d0,%%dtt0\n\t" + ".chip 68k\n\t" + "jmp %0@\n\t" + : /* no outputs */ + : "a" (jmp_addr040) + : "d0" ); + jmp_addr_label040: + __asm__ __volatile__ + ("moveq #0,%/d0\n\t" + "nop\n\t" + ".chip 68040\n\t" + "cinva %%bc\n\t" + "nop\n\t" + "pflusha\n\t" + "nop\n\t" + "movec %%d0,%%tc\n\t" + "nop\n\t" + /* the following setup of transparent translations is needed on the + * Afterburner040 to successfully reboot. Other machines shouldn't + * care about a different tt regs setup, they also didn't care in + * the past that the regs weren't turned off. */ + "movel #0xffc000,%%d0\n\t" /* whole insn space cacheable */ + "movec %%d0,%%itt0\n\t" + "movec %%d0,%%itt1\n\t" + "orw #0x40,%/d0\n\t" /* whole data space non-cacheable/ser. */ + "movec %%d0,%%dtt0\n\t" + "movec %%d0,%%dtt1\n\t" + ".chip 68k\n\t" + "jmp %0@" + : /* no outputs */ + : "a" (reset_addr) + : "d0"); + } + else + __asm__ __volatile__ + ("pmove %0@,%/tc\n\t" + "jmp %1@" + : /* no outputs */ + : "a" (&tc_val), "a" (reset_addr)); +} + + +static void atari_get_model(char *model) +{ + strcpy(model, "Atari "); + switch (atari_mch_cookie >> 16) { + case ATARI_MCH_ST: + if (ATARIHW_PRESENT(MSTE_CLK)) + strcat (model, "Mega ST"); + else + strcat (model, "ST"); + break; + case ATARI_MCH_STE: + if (MACH_IS_MSTE) + strcat (model, "Mega STE"); + else + strcat (model, "STE"); + break; + case ATARI_MCH_TT: + if (MACH_IS_MEDUSA) + /* Medusa has TT _MCH cookie */ + strcat (model, "Medusa"); + else if (MACH_IS_HADES) + strcat(model, "Hades"); + else + strcat (model, "TT"); + break; + case ATARI_MCH_FALCON: + strcat (model, "Falcon"); + if (MACH_IS_AB40) + strcat (model, " (with Afterburner040)"); + break; + default: + sprintf (model + strlen (model), "(unknown mach cookie 0x%lx)", + atari_mch_cookie); + break; + } +} + + +static int atari_get_hardware_list(char *buffer) +{ + int len = 0, i; + + for (i = 0; i < m68k_num_memory; i++) + len += sprintf (buffer+len, "\t%3ld MB at 0x%08lx (%s)\n", + m68k_memory[i].size >> 20, m68k_memory[i].addr, + (m68k_memory[i].addr & 0xff000000 ? + "alternate RAM" : "ST-RAM")); + +#define ATARIHW_ANNOUNCE(name,str) \ + if (ATARIHW_PRESENT(name)) \ + len += sprintf (buffer + len, "\t%s\n", str) + + len += sprintf (buffer + len, "Detected hardware:\n"); + ATARIHW_ANNOUNCE(STND_SHIFTER, "ST Shifter"); + ATARIHW_ANNOUNCE(EXTD_SHIFTER, "STe Shifter"); + ATARIHW_ANNOUNCE(TT_SHIFTER, "TT Shifter"); + ATARIHW_ANNOUNCE(VIDEL_SHIFTER, "Falcon Shifter"); + ATARIHW_ANNOUNCE(YM_2149, "Programmable Sound Generator"); + ATARIHW_ANNOUNCE(PCM_8BIT, "PCM 8 Bit Sound"); + ATARIHW_ANNOUNCE(CODEC, "CODEC Sound"); + ATARIHW_ANNOUNCE(TT_SCSI, "SCSI Controller NCR5380 (TT style)"); + ATARIHW_ANNOUNCE(ST_SCSI, "SCSI Controller NCR5380 (Falcon style)"); + ATARIHW_ANNOUNCE(ACSI, "ACSI Interface"); + ATARIHW_ANNOUNCE(IDE, "IDE Interface"); + ATARIHW_ANNOUNCE(FDCSPEED, "8/16 Mhz Switch for FDC"); + ATARIHW_ANNOUNCE(ST_MFP, "Multi Function Peripheral MFP 68901"); + ATARIHW_ANNOUNCE(TT_MFP, "Second Multi Function Peripheral MFP 68901"); + ATARIHW_ANNOUNCE(SCC, "Serial Communications Controller SCC 8530"); + ATARIHW_ANNOUNCE(ST_ESCC, "Extended Serial Communications Controller SCC 85230"); + ATARIHW_ANNOUNCE(ANALOG_JOY, "Paddle Interface"); + ATARIHW_ANNOUNCE(MICROWIRE, "MICROWIRE(tm) Interface"); + ATARIHW_ANNOUNCE(STND_DMA, "DMA Controller (24 bit)"); + ATARIHW_ANNOUNCE(EXTD_DMA, "DMA Controller (32 bit)"); + ATARIHW_ANNOUNCE(SCSI_DMA, "DMA Controller for NCR5380"); + ATARIHW_ANNOUNCE(SCC_DMA, "DMA Controller for SCC"); + ATARIHW_ANNOUNCE(TT_CLK, "Clock Chip MC146818A"); + ATARIHW_ANNOUNCE(MSTE_CLK, "Clock Chip RP5C15"); + ATARIHW_ANNOUNCE(SCU, "System Control Unit"); + ATARIHW_ANNOUNCE(BLITTER, "Blitter"); + ATARIHW_ANNOUNCE(VME, "VME Bus"); + ATARIHW_ANNOUNCE(DSP56K, "DSP56001 processor"); + + return(len); +} + +/* + * Local variables: + * c-indent-level: 4 + * tab-width: 8 + * End: + */ diff --git a/arch/m68k/atari/debug.c b/arch/m68k/atari/debug.c new file mode 100644 index 00000000000..ace05f79d96 --- /dev/null +++ b/arch/m68k/atari/debug.c @@ -0,0 +1,347 @@ +/* + * linux/arch/m68k/atari/debug.c + * + * Atari debugging and serial console stuff + * + * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/delay.h> + +#include <asm/atarihw.h> +#include <asm/atariints.h> + +extern char m68k_debug_device[]; + +/* Flag that Modem1 port is already initialized and used */ +int atari_MFP_init_done; +/* Flag that Modem1 port is already initialized and used */ +int atari_SCC_init_done; +/* Can be set somewhere, if a SCC master reset has already be done and should + * not be repeated; used by kgdb */ +int atari_SCC_reset_done; + +static struct console atari_console_driver = { + .name = "debug", + .flags = CON_PRINTBUFFER, + .index = -1, +}; + + +static inline void ata_mfp_out (char c) +{ + while (!(mfp.trn_stat & 0x80)) /* wait for tx buf empty */ + barrier (); + mfp.usart_dta = c; +} + +void atari_mfp_console_write (struct console *co, const char *str, + unsigned int count) +{ + while (count--) { + if (*str == '\n') + ata_mfp_out( '\r' ); + ata_mfp_out( *str++ ); + } +} + +static inline void ata_scc_out (char c) +{ + do { + MFPDELAY(); + } while (!(scc.cha_b_ctrl & 0x04)); /* wait for tx buf empty */ + MFPDELAY(); + scc.cha_b_data = c; +} + +void atari_scc_console_write (struct console *co, const char *str, + unsigned int count) +{ + while (count--) { + if (*str == '\n') + ata_scc_out( '\r' ); + ata_scc_out( *str++ ); + } +} + +static inline void ata_midi_out (char c) +{ + while (!(acia.mid_ctrl & ACIA_TDRE)) /* wait for tx buf empty */ + barrier (); + acia.mid_data = c; +} + +void atari_midi_console_write (struct console *co, const char *str, + unsigned int count) +{ + while (count--) { + if (*str == '\n') + ata_midi_out( '\r' ); + ata_midi_out( *str++ ); + } +} + +static int ata_par_out (char c) +{ + unsigned char tmp; + /* This a some-seconds timeout in case no printer is connected */ + unsigned long i = loops_per_jiffy > 1 ? loops_per_jiffy : 10000000/HZ; + + while( (mfp.par_dt_reg & 1) && --i ) /* wait for BUSY == L */ + ; + if (!i) return( 0 ); + + sound_ym.rd_data_reg_sel = 15; /* select port B */ + sound_ym.wd_data = c; /* put char onto port */ + sound_ym.rd_data_reg_sel = 14; /* select port A */ + tmp = sound_ym.rd_data_reg_sel; + sound_ym.wd_data = tmp & ~0x20; /* set strobe L */ + MFPDELAY(); /* wait a bit */ + sound_ym.wd_data = tmp | 0x20; /* set strobe H */ + return( 1 ); +} + +static void atari_par_console_write (struct console *co, const char *str, + unsigned int count) +{ + static int printer_present = 1; + + if (!printer_present) + return; + + while (count--) { + if (*str == '\n') + if (!ata_par_out( '\r' )) { + printer_present = 0; + return; + } + if (!ata_par_out( *str++ )) { + printer_present = 0; + return; + } + } +} + +#ifdef CONFIG_SERIAL_CONSOLE +int atari_mfp_console_wait_key(struct console *co) +{ + while( !(mfp.rcv_stat & 0x80) ) /* wait for rx buf filled */ + barrier(); + return( mfp.usart_dta ); +} + +int atari_scc_console_wait_key(struct console *co) +{ + do { + MFPDELAY(); + } while( !(scc.cha_b_ctrl & 0x01) ); /* wait for rx buf filled */ + MFPDELAY(); + return( scc.cha_b_data ); +} + +int atari_midi_console_wait_key(struct console *co) +{ + while( !(acia.mid_ctrl & ACIA_RDRF) ) /* wait for rx buf filled */ + barrier(); + return( acia.mid_data ); +} +#endif + +/* The following two functions do a quick'n'dirty initialization of the MFP or + * SCC serial ports. They're used by the debugging interface, kgdb, and the + * serial console code. */ +#ifndef CONFIG_SERIAL_CONSOLE +static void __init atari_init_mfp_port( int cflag ) +#else +void atari_init_mfp_port( int cflag ) +#endif +{ + /* timer values for 1200...115200 bps; > 38400 select 110, 134, or 150 + * bps, resp., and work only correct if there's a RSVE or RSSPEED */ + static int baud_table[9] = { 16, 11, 8, 4, 2, 1, 175, 143, 128 }; + int baud = cflag & CBAUD; + int parity = (cflag & PARENB) ? ((cflag & PARODD) ? 0x04 : 0x06) : 0; + int csize = ((cflag & CSIZE) == CS7) ? 0x20 : 0x00; + + if (cflag & CBAUDEX) + baud += B38400; + if (baud < B1200 || baud > B38400+2) + baud = B9600; /* use default 9600bps for non-implemented rates */ + baud -= B1200; /* baud_table[] starts at 1200bps */ + + mfp.trn_stat &= ~0x01; /* disable TX */ + mfp.usart_ctr = parity | csize | 0x88; /* 1:16 clk mode, 1 stop bit */ + mfp.tim_ct_cd &= 0x70; /* stop timer D */ + mfp.tim_dt_d = baud_table[baud]; + mfp.tim_ct_cd |= 0x01; /* start timer D, 1:4 */ + mfp.trn_stat |= 0x01; /* enable TX */ + + atari_MFP_init_done = 1; +} + +#define SCC_WRITE(reg,val) \ + do { \ + scc.cha_b_ctrl = (reg); \ + MFPDELAY(); \ + scc.cha_b_ctrl = (val); \ + MFPDELAY(); \ + } while(0) + +/* loops_per_jiffy isn't initialized yet, so we can't use udelay(). This does a + * delay of ~ 60us. */ +#define LONG_DELAY() \ + do { \ + int i; \ + for( i = 100; i > 0; --i ) \ + MFPDELAY(); \ + } while(0) + +#ifndef CONFIG_SERIAL_CONSOLE +static void __init atari_init_scc_port( int cflag ) +#else +void atari_init_scc_port( int cflag ) +#endif +{ + extern int atari_SCC_reset_done; + static int clksrc_table[9] = + /* reg 11: 0x50 = BRG, 0x00 = RTxC, 0x28 = TRxC */ + { 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x00, 0x00 }; + static int brgsrc_table[9] = + /* reg 14: 0 = RTxC, 2 = PCLK */ + { 2, 2, 2, 2, 2, 2, 0, 2, 2 }; + static int clkmode_table[9] = + /* reg 4: 0x40 = x16, 0x80 = x32, 0xc0 = x64 */ + { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x80 }; + static int div_table[9] = + /* reg12 (BRG low) */ + { 208, 138, 103, 50, 24, 11, 1, 0, 0 }; + + int baud = cflag & CBAUD; + int clksrc, clkmode, div, reg3, reg5; + + if (cflag & CBAUDEX) + baud += B38400; + if (baud < B1200 || baud > B38400+2) + baud = B9600; /* use default 9600bps for non-implemented rates */ + baud -= B1200; /* tables starts at 1200bps */ + + clksrc = clksrc_table[baud]; + clkmode = clkmode_table[baud]; + div = div_table[baud]; + if (ATARIHW_PRESENT(TT_MFP) && baud >= 6) { + /* special treatment for TT, where rates >= 38400 are done via TRxC */ + clksrc = 0x28; /* TRxC */ + clkmode = baud == 6 ? 0xc0 : + baud == 7 ? 0x80 : /* really 76800bps */ + 0x40; /* really 153600bps */ + div = 0; + } + + reg3 = (cflag & CSIZE) == CS8 ? 0xc0 : 0x40; + reg5 = (cflag & CSIZE) == CS8 ? 0x60 : 0x20 | 0x82 /* assert DTR/RTS */; + + (void)scc.cha_b_ctrl; /* reset reg pointer */ + SCC_WRITE( 9, 0xc0 ); /* reset */ + LONG_DELAY(); /* extra delay after WR9 access */ + SCC_WRITE( 4, (cflag & PARENB) ? ((cflag & PARODD) ? 0x01 : 0x03) : 0 | + 0x04 /* 1 stopbit */ | + clkmode ); + SCC_WRITE( 3, reg3 ); + SCC_WRITE( 5, reg5 ); + SCC_WRITE( 9, 0 ); /* no interrupts */ + LONG_DELAY(); /* extra delay after WR9 access */ + SCC_WRITE( 10, 0 ); /* NRZ mode */ + SCC_WRITE( 11, clksrc ); /* main clock source */ + SCC_WRITE( 12, div ); /* BRG value */ + SCC_WRITE( 13, 0 ); /* BRG high byte */ + SCC_WRITE( 14, brgsrc_table[baud] ); + SCC_WRITE( 14, brgsrc_table[baud] | (div ? 1 : 0) ); + SCC_WRITE( 3, reg3 | 1 ); + SCC_WRITE( 5, reg5 | 8 ); + + atari_SCC_reset_done = 1; + atari_SCC_init_done = 1; +} + +#ifndef CONFIG_SERIAL_CONSOLE +static void __init atari_init_midi_port( int cflag ) +#else +void atari_init_midi_port( int cflag ) +#endif +{ + int baud = cflag & CBAUD; + int csize = ((cflag & CSIZE) == CS8) ? 0x10 : 0x00; + /* warning 7N1 isn't possible! (instead 7O2 is used...) */ + int parity = (cflag & PARENB) ? ((cflag & PARODD) ? 0x0c : 0x08) : 0x04; + int div; + + /* 4800 selects 7812.5, 115200 selects 500000, all other (incl. 9600 as + * default) the standard MIDI speed 31250. */ + if (cflag & CBAUDEX) + baud += B38400; + if (baud == B4800) + div = ACIA_DIV64; /* really 7812.5 bps */ + else if (baud == B38400+2 /* 115200 */) + div = ACIA_DIV1; /* really 500 kbps (does that work??) */ + else + div = ACIA_DIV16; /* 31250 bps, standard for MIDI */ + + /* RTS low, ints disabled */ + acia.mid_ctrl = div | csize | parity | + ((atari_switches & ATARI_SWITCH_MIDI) ? + ACIA_RHTID : ACIA_RLTID); +} + +void __init atari_debug_init(void) +{ + if (!strcmp( m68k_debug_device, "ser" )) { + /* defaults to ser2 for a Falcon and ser1 otherwise */ + strcpy( m68k_debug_device, MACH_IS_FALCON ? "ser2" : "ser1" ); + + } + + if (!strcmp( m68k_debug_device, "ser1" )) { + /* ST-MFP Modem1 serial port */ + atari_init_mfp_port( B9600|CS8 ); + atari_console_driver.write = atari_mfp_console_write; + } + else if (!strcmp( m68k_debug_device, "ser2" )) { + /* SCC Modem2 serial port */ + atari_init_scc_port( B9600|CS8 ); + atari_console_driver.write = atari_scc_console_write; + } + else if (!strcmp( m68k_debug_device, "midi" )) { + /* MIDI port */ + atari_init_midi_port( B9600|CS8 ); + atari_console_driver.write = atari_midi_console_write; + } + else if (!strcmp( m68k_debug_device, "par" )) { + /* parallel printer */ + atari_turnoff_irq( IRQ_MFP_BUSY ); /* avoid ints */ + sound_ym.rd_data_reg_sel = 7; /* select mixer control */ + sound_ym.wd_data = 0xff; /* sound off, ports are output */ + sound_ym.rd_data_reg_sel = 15; /* select port B */ + sound_ym.wd_data = 0; /* no char */ + sound_ym.rd_data_reg_sel = 14; /* select port A */ + sound_ym.wd_data = sound_ym.rd_data_reg_sel | 0x20; /* strobe H */ + atari_console_driver.write = atari_par_console_write; + } + if (atari_console_driver.write) + register_console(&atari_console_driver); +} + +/* + * Local variables: + * c-indent-level: 4 + * tab-width: 8 + * End: + */ diff --git a/arch/m68k/atari/hades-pci.c b/arch/m68k/atari/hades-pci.c new file mode 100644 index 00000000000..8888debf71e --- /dev/null +++ b/arch/m68k/atari/hades-pci.c @@ -0,0 +1,444 @@ +/* + * hades-pci.c - Hardware specific PCI BIOS functions the Hades Atari clone. + * + * Written by Wout Klaren. + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <asm/io.h> + +#if 0 +# define DBG_DEVS(args) printk args +#else +# define DBG_DEVS(args) +#endif + +#if defined(CONFIG_PCI) && defined(CONFIG_HADES) + +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/pci.h> + +#include <asm/atarihw.h> +#include <asm/atariints.h> +#include <asm/byteorder.h> +#include <asm/pci.h> + +#define HADES_MEM_BASE 0x80000000 +#define HADES_MEM_SIZE 0x20000000 +#define HADES_CONFIG_BASE 0xA0000000 +#define HADES_CONFIG_SIZE 0x10000000 +#define HADES_IO_BASE 0xB0000000 +#define HADES_IO_SIZE 0x10000000 +#define HADES_VIRT_IO_SIZE 0x00010000 /* Only 64k is remapped and actually used. */ + +#define N_SLOTS 4 /* Number of PCI slots. */ + +static const char pci_mem_name[] = "PCI memory space"; +static const char pci_io_name[] = "PCI I/O space"; +static const char pci_config_name[] = "PCI config space"; + +static struct resource config_space = { + .name = pci_config_name, + .start = HADES_CONFIG_BASE, + .end = HADES_CONFIG_BASE + HADES_CONFIG_SIZE - 1 +}; +static struct resource io_space = { + .name = pci_io_name, + .start = HADES_IO_BASE, + .end = HADES_IO_BASE + HADES_IO_SIZE - 1 +}; + +static const unsigned long pci_conf_base_phys[] = { + 0xA0080000, 0xA0040000, 0xA0020000, 0xA0010000 +}; +static unsigned long pci_conf_base_virt[N_SLOTS]; +static unsigned long pci_io_base_virt; + +/* + * static void *mk_conf_addr(unsigned char bus, unsigned char device_fn, + * unsigned char where) + * + * Calculate the address of the PCI configuration area of the given + * device. + * + * BUG: boards with multiple functions are probably not correctly + * supported. + */ + +static void *mk_conf_addr(struct pci_dev *dev, int where) +{ + int device = dev->devfn >> 3, function = dev->devfn & 7; + void *result; + + DBG_DEVS(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p)\n", + dev->bus->number, dev->devfn, where, pci_addr)); + + if (device > 3) + { + DBG_DEVS(("mk_conf_addr: device (%d) > 3, returning NULL\n", device)); + return NULL; + } + + if (dev->bus->number != 0) + { + DBG_DEVS(("mk_conf_addr: bus (%d) > 0, returning NULL\n", device)); + return NULL; + } + + result = (void *) (pci_conf_base_virt[device] | (function << 8) | (where)); + DBG_DEVS(("mk_conf_addr: returning pci_addr 0x%lx\n", (unsigned long) result)); + return result; +} + +static int hades_read_config_byte(struct pci_dev *dev, int where, u8 *value) +{ + volatile unsigned char *pci_addr; + + *value = 0xff; + + if ((pci_addr = (unsigned char *) mk_conf_addr(dev, where)) == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + *value = *pci_addr; + + return PCIBIOS_SUCCESSFUL; +} + +static int hades_read_config_word(struct pci_dev *dev, int where, u16 *value) +{ + volatile unsigned short *pci_addr; + + *value = 0xffff; + + if (where & 0x1) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if ((pci_addr = (unsigned short *) mk_conf_addr(dev, where)) == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + *value = le16_to_cpu(*pci_addr); + + return PCIBIOS_SUCCESSFUL; +} + +static int hades_read_config_dword(struct pci_dev *dev, int where, u32 *value) +{ + volatile unsigned int *pci_addr; + unsigned char header_type; + int result; + + *value = 0xffffffff; + + if (where & 0x3) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if ((pci_addr = (unsigned int *) mk_conf_addr(dev, where)) == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + *value = le32_to_cpu(*pci_addr); + + /* + * Check if the value is an address on the bus. If true, add the + * base address of the PCI memory or PCI I/O area on the Hades. + */ + + if ((result = hades_read_config_byte(dev, PCI_HEADER_TYPE, + &header_type)) != PCIBIOS_SUCCESSFUL) + return result; + + if (((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_1)) || + ((header_type != PCI_HEADER_TYPE_BRIDGE) && ((where >= PCI_BASE_ADDRESS_2) && + (where <= PCI_BASE_ADDRESS_5)))) + { + if ((*value & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) + { + /* + * Base address register that contains an I/O address. If the + * address is valid on the Hades (0 <= *value < HADES_VIRT_IO_SIZE), + * add 'pci_io_base_virt' to the value. + */ + + if (*value < HADES_VIRT_IO_SIZE) + *value += pci_io_base_virt; + } + else + { + /* + * Base address register that contains an memory address. If the + * address is valid on the Hades (0 <= *value < HADES_MEM_SIZE), + * add HADES_MEM_BASE to the value. + */ + + if (*value == 0) + { + /* + * Base address is 0. Test if this base + * address register is used. + */ + + *pci_addr = 0xffffffff; + if (*pci_addr != 0) + { + *pci_addr = *value; + if (*value < HADES_MEM_SIZE) + *value += HADES_MEM_BASE; + } + } + else + { + if (*value < HADES_MEM_SIZE) + *value += HADES_MEM_BASE; + } + } + } + + return PCIBIOS_SUCCESSFUL; +} + +static int hades_write_config_byte(struct pci_dev *dev, int where, u8 value) +{ + volatile unsigned char *pci_addr; + + if ((pci_addr = (unsigned char *) mk_conf_addr(dev, where)) == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + *pci_addr = value; + + return PCIBIOS_SUCCESSFUL; +} + +static int hades_write_config_word(struct pci_dev *dev, int where, u16 value) +{ + volatile unsigned short *pci_addr; + + if ((pci_addr = (unsigned short *) mk_conf_addr(dev, where)) == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + *pci_addr = cpu_to_le16(value); + + return PCIBIOS_SUCCESSFUL; +} + +static int hades_write_config_dword(struct pci_dev *dev, int where, u32 value) +{ + volatile unsigned int *pci_addr; + unsigned char header_type; + int result; + + if ((pci_addr = (unsigned int *) mk_conf_addr(dev, where)) == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * Check if the value is an address on the bus. If true, subtract the + * base address of the PCI memory or PCI I/O area on the Hades. + */ + + if ((result = hades_read_config_byte(dev, PCI_HEADER_TYPE, + &header_type)) != PCIBIOS_SUCCESSFUL) + return result; + + if (((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_1)) || + ((header_type != PCI_HEADER_TYPE_BRIDGE) && ((where >= PCI_BASE_ADDRESS_2) && + (where <= PCI_BASE_ADDRESS_5)))) + { + if ((value & PCI_BASE_ADDRESS_SPACE) == + PCI_BASE_ADDRESS_SPACE_IO) + { + /* + * I/O address. Check if the address is valid address on + * the Hades (pci_io_base_virt <= value < pci_io_base_virt + + * HADES_VIRT_IO_SIZE) or if the value is 0xffffffff. If not + * true do not write the base address register. If it is a + * valid base address subtract 'pci_io_base_virt' from the value. + */ + + if ((value >= pci_io_base_virt) && (value < (pci_io_base_virt + + HADES_VIRT_IO_SIZE))) + value -= pci_io_base_virt; + else + { + if (value != 0xffffffff) + return PCIBIOS_SET_FAILED; + } + } + else + { + /* + * Memory address. Check if the address is valid address on + * the Hades (HADES_MEM_BASE <= value < HADES_MEM_BASE + HADES_MEM_SIZE) or + * if the value is 0xffffffff. If not true do not write + * the base address register. If it is a valid base address + * subtract HADES_MEM_BASE from the value. + */ + + if ((value >= HADES_MEM_BASE) && (value < (HADES_MEM_BASE + HADES_MEM_SIZE))) + value -= HADES_MEM_BASE; + else + { + if (value != 0xffffffff) + return PCIBIOS_SET_FAILED; + } + } + } + + *pci_addr = cpu_to_le32(value); + + return PCIBIOS_SUCCESSFUL; +} + +/* + * static inline void hades_fixup(void) + * + * Assign IRQ numbers as used by Linux to the interrupt pins + * of the PCI cards. + */ + +static void __init hades_fixup(int pci_modify) +{ + char irq_tab[4] = { + [0] = IRQ_TT_MFP_IO0, /* Slot 0. */ + [1] = IRQ_TT_MFP_IO1, /* Slot 1. */ + [2] = IRQ_TT_MFP_SCC, /* Slot 2. */ + [3] = IRQ_TT_MFP_SCSIDMA /* Slot 3. */ + }; + struct pci_dev *dev = NULL; + unsigned char slot; + + /* + * Go through all devices, fixing up irqs as we see fit: + */ + + while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) + { + if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) + { + slot = PCI_SLOT(dev->devfn); /* Determine slot number. */ + dev->irq = irq_tab[slot]; + if (pci_modify) + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + } + } +} + +/* + * static void hades_conf_device(struct pci_dev *dev) + * + * Machine dependent Configure the given device. + * + * Parameters: + * + * dev - the pci device. + */ + +static void __init hades_conf_device(struct pci_dev *dev) +{ + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0); +} + +static struct pci_ops hades_pci_ops = { + .read_byte = hades_read_config_byte, + .read_word = hades_read_config_word, + .read_dword = hades_read_config_dword, + .write_byte = hades_write_config_byte, + .write_word = hades_write_config_word, + .write_dword = hades_write_config_dword +}; + +/* + * struct pci_bus_info *init_hades_pci(void) + * + * Machine specific initialisation: + * + * - Allocate and initialise a 'pci_bus_info' structure + * - Initialise hardware + * + * Result: pointer to 'pci_bus_info' structure. + */ + +struct pci_bus_info * __init init_hades_pci(void) +{ + struct pci_bus_info *bus; + int i; + + /* + * Remap I/O and configuration space. + */ + + pci_io_base_virt = (unsigned long) ioremap(HADES_IO_BASE, HADES_VIRT_IO_SIZE); + + for (i = 0; i < N_SLOTS; i++) + pci_conf_base_virt[i] = (unsigned long) ioremap(pci_conf_base_phys[i], 0x10000); + + /* + * Allocate memory for bus info structure. + */ + + bus = kmalloc(sizeof(struct pci_bus_info), GFP_KERNEL); + if (!bus) + return NULL; + memset(bus, 0, sizeof(struct pci_bus_info)); + + /* + * Claim resources. The m68k has no separate I/O space, both + * PCI memory space and PCI I/O space are in memory space. Therefore + * the I/O resources are requested in memory space as well. + */ + + if (request_resource(&iomem_resource, &config_space) != 0) + { + kfree(bus); + return NULL; + } + + if (request_resource(&iomem_resource, &io_space) != 0) + { + release_resource(&config_space); + kfree(bus); + return NULL; + } + + bus->mem_space.start = HADES_MEM_BASE; + bus->mem_space.end = HADES_MEM_BASE + HADES_MEM_SIZE - 1; + bus->mem_space.name = pci_mem_name; +#if 1 + if (request_resource(&iomem_resource, &bus->mem_space) != 0) + { + release_resource(&io_space); + release_resource(&config_space); + kfree(bus); + return NULL; + } +#endif + bus->io_space.start = pci_io_base_virt; + bus->io_space.end = pci_io_base_virt + HADES_VIRT_IO_SIZE - 1; + bus->io_space.name = pci_io_name; +#if 1 + if (request_resource(&ioport_resource, &bus->io_space) != 0) + { + release_resource(&bus->mem_space); + release_resource(&io_space); + release_resource(&config_space); + kfree(bus); + return NULL; + } +#endif + /* + * Set hardware dependent functions. + */ + + bus->m68k_pci_ops = &hades_pci_ops; + bus->fixup = hades_fixup; + bus->conf_device = hades_conf_device; + + /* + * Select high to low edge for PCI interrupts. + */ + + tt_mfp.active_edge &= ~0x27; + + return bus; +} +#endif diff --git a/arch/m68k/atari/stdma.c b/arch/m68k/atari/stdma.c new file mode 100644 index 00000000000..288f5e6a124 --- /dev/null +++ b/arch/m68k/atari/stdma.c @@ -0,0 +1,196 @@ +/* + * linux/arch/m68k/atari/stmda.c + * + * Copyright (C) 1994 Roman Hodek + * + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + + +/* This file contains some function for controlling the access to the */ +/* ST-DMA chip that may be shared between devices. Currently we have: */ +/* TT: Floppy and ACSI bus */ +/* Falcon: Floppy and SCSI */ +/* */ +/* The controlling functions set up a wait queue for access to the */ +/* ST-DMA chip. Callers to stdma_lock() that cannot granted access are */ +/* put onto a queue and waked up later if the owner calls */ +/* stdma_release(). Additionally, the caller gives his interrupt */ +/* service routine to stdma_lock(). */ +/* */ +/* On the Falcon, the IDE bus uses just the ACSI/Floppy interrupt, but */ +/* not the ST-DMA chip itself. So falhd.c needs not to lock the */ +/* chip. The interrupt is routed to falhd.c if IDE is configured, the */ +/* model is a Falcon and the interrupt was caused by the HD controller */ +/* (can be determined by looking at its status register). */ + + +#include <linux/types.h> +#include <linux/kdev_t.h> +#include <linux/genhd.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/wait.h> + +#include <asm/atari_stdma.h> +#include <asm/atariints.h> +#include <asm/atarihw.h> +#include <asm/io.h> +#include <asm/irq.h> + +static int stdma_locked; /* the semaphore */ + /* int func to be called */ +static irqreturn_t (*stdma_isr)(int, void *, struct pt_regs *); +static void *stdma_isr_data; /* data passed to isr */ +static DECLARE_WAIT_QUEUE_HEAD(stdma_wait); /* wait queue for ST-DMA */ + + + + +/***************************** Prototypes *****************************/ + +static irqreturn_t stdma_int (int irq, void *dummy, struct pt_regs *fp); + +/************************* End of Prototypes **************************/ + + + +/* + * Function: void stdma_lock( isrfunc isr, void *data ) + * + * Purpose: Tries to get a lock on the ST-DMA chip that is used by more + * then one device driver. Waits on stdma_wait until lock is free. + * stdma_lock() may not be called from an interrupt! You have to + * get the lock in your main routine and release it when your + * request is finished. + * + * Inputs: A interrupt function that is called until the lock is + * released. + * + * Returns: nothing + * + */ + +void stdma_lock(irqreturn_t (*handler)(int, void *, struct pt_regs *), + void *data) +{ + unsigned long flags; + + local_irq_save(flags); /* protect lock */ + + /* Since the DMA is used for file system purposes, we + have to sleep uninterruptible (there may be locked + buffers) */ + wait_event(stdma_wait, !stdma_locked); + + stdma_locked = 1; + stdma_isr = handler; + stdma_isr_data = data; + local_irq_restore(flags); +} + + +/* + * Function: void stdma_release( void ) + * + * Purpose: Releases the lock on the ST-DMA chip. + * + * Inputs: none + * + * Returns: nothing + * + */ + +void stdma_release(void) +{ + unsigned long flags; + + local_irq_save(flags); + + stdma_locked = 0; + stdma_isr = NULL; + stdma_isr_data = NULL; + wake_up(&stdma_wait); + + local_irq_restore(flags); +} + + +/* + * Function: int stdma_others_waiting( void ) + * + * Purpose: Check if someone waits for the ST-DMA lock. + * + * Inputs: none + * + * Returns: 0 if no one is waiting, != 0 otherwise + * + */ + +int stdma_others_waiting(void) +{ + return waitqueue_active(&stdma_wait); +} + + +/* + * Function: int stdma_islocked( void ) + * + * Purpose: Check if the ST-DMA is currently locked. + * Note: Returned status is only valid if ints are disabled while calling and + * as long as they remain disabled. + * If called with ints enabled, status can change only from locked to + * unlocked, because ints may not lock the ST-DMA. + * + * Inputs: none + * + * Returns: != 0 if locked, 0 otherwise + * + */ + +int stdma_islocked(void) +{ + return stdma_locked; +} + + +/* + * Function: void stdma_init( void ) + * + * Purpose: Initialize the ST-DMA chip access controlling. + * It sets up the interrupt and its service routine. The int is registered + * as slow int, client devices have to live with that (no problem + * currently). + * + * Inputs: none + * + * Return: nothing + * + */ + +void __init stdma_init(void) +{ + stdma_isr = NULL; + request_irq(IRQ_MFP_FDC, stdma_int, IRQ_TYPE_SLOW, + "ST-DMA: floppy/ACSI/IDE/Falcon-SCSI", stdma_int); +} + + +/* + * Function: void stdma_int() + * + * Purpose: The interrupt routine for the ST-DMA. It calls the isr + * registered by stdma_lock(). + * + */ + +static irqreturn_t stdma_int(int irq, void *dummy, struct pt_regs *fp) +{ + if (stdma_isr) + (*stdma_isr)(irq, stdma_isr_data, fp); + return IRQ_HANDLED; +} diff --git a/arch/m68k/atari/stram.c b/arch/m68k/atari/stram.c new file mode 100644 index 00000000000..5a3c106b40c --- /dev/null +++ b/arch/m68k/atari/stram.c @@ -0,0 +1,1247 @@ +/* + * arch/m68k/atari/stram.c: Functions for ST-RAM allocations + * + * Copyright 1994-97 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> + * + * 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/mm.h> +#include <linux/kdev_t.h> +#include <linux/major.h> +#include <linux/init.h> +#include <linux/swap.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/pagemap.h> +#include <linux/shm.h> +#include <linux/bootmem.h> +#include <linux/mount.h> +#include <linux/blkdev.h> + +#include <asm/setup.h> +#include <asm/machdep.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/atarihw.h> +#include <asm/atari_stram.h> +#include <asm/io.h> +#include <asm/semaphore.h> + +#include <linux/swapops.h> + +#undef DEBUG + +#ifdef DEBUG +#define DPRINTK(fmt,args...) printk( fmt, ##args ) +#else +#define DPRINTK(fmt,args...) +#endif + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_STRAM_PROC) +/* abbrev for the && above... */ +#define DO_PROC +#include <linux/proc_fs.h> +#endif + +/* Pre-swapping comments: + * + * ++roman: + * + * New version of ST-Ram buffer allocation. Instead of using the + * 1 MB - 4 KB that remain when the ST-Ram chunk starts at $1000 + * (1 MB granularity!), such buffers are reserved like this: + * + * - If the kernel resides in ST-Ram anyway, we can take the buffer + * from behind the current kernel data space the normal way + * (incrementing start_mem). + * + * - If the kernel is in TT-Ram, stram_init() initializes start and + * end of the available region. Buffers are allocated from there + * and mem_init() later marks the such used pages as reserved. + * Since each TT-Ram chunk is at least 4 MB in size, I hope there + * won't be an overrun of the ST-Ram region by normal kernel data + * space. + * + * For that, ST-Ram may only be allocated while kernel initialization + * is going on, or exactly: before mem_init() is called. There is also + * no provision now for freeing ST-Ram buffers. It seems that isn't + * really needed. + * + */ + +/* + * New Nov 1997: Use ST-RAM as swap space! + * + * In the past, there were often problems with modules that require ST-RAM + * buffers. Such drivers have to use __get_dma_pages(), which unfortunately + * often isn't very successful in allocating more than 1 page :-( [1] The net + * result was that most of the time you couldn't insmod such modules (ataflop, + * ACSI, SCSI on Falcon, Atari internal framebuffer, not to speak of acsi_slm, + * which needs a 1 MB buffer... :-). + * + * To overcome this limitation, ST-RAM can now be turned into a very + * high-speed swap space. If a request for an ST-RAM buffer comes, the kernel + * now tries to unswap some pages on that swap device to make some free (and + * contiguous) space. This works much better in comparison to + * __get_dma_pages(), since used swap pages can be selectively freed by either + * moving them to somewhere else in swap space, or by reading them back into + * system memory. Ok, there operation of unswapping isn't really cheap (for + * each page, one has to go through the page tables of all processes), but it + * doesn't happen that often (only when allocation ST-RAM, i.e. when loading a + * module that needs ST-RAM). But it at least makes it possible to load such + * modules! + * + * It could also be that overall system performance increases a bit due to + * ST-RAM swapping, since slow ST-RAM isn't used anymore for holding data or + * executing code in. It's then just a (very fast, compared to disk) back + * storage for not-so-often needed data. (But this effect must be compared + * with the loss of total memory...) Don't know if the effect is already + * visible on a TT, where the speed difference between ST- and TT-RAM isn't + * that dramatic, but it should on machines where TT-RAM is really much faster + * (e.g. Afterburner). + * + * [1]: __get_free_pages() does a fine job if you only want one page, but if + * you want more (contiguous) pages, it can give you such a block only if + * there's already a free one. The algorithm can't try to free buffers or swap + * out something in order to make more free space, since all that page-freeing + * mechanisms work "target-less", i.e. they just free something, but not in a + * specific place. I.e., __get_free_pages() can't do anything to free + * *adjacent* pages :-( This situation becomes even worse for DMA memory, + * since the freeing algorithms are also blind to DMA capability of pages. + */ + +/* 1998-10-20: ++andreas + unswap_by_move disabled because it does not handle swapped shm pages. +*/ + +/* 2000-05-01: ++andreas + Integrated with bootmem. Remove all traces of unswap_by_move. +*/ + +#ifdef CONFIG_STRAM_SWAP +#define ALIGN_IF_SWAP(x) PAGE_ALIGN(x) +#else +#define ALIGN_IF_SWAP(x) (x) +#endif + +/* get index of swap page at address 'addr' */ +#define SWAP_NR(addr) (((addr) - swap_start) >> PAGE_SHIFT) + +/* get address of swap page #'nr' */ +#define SWAP_ADDR(nr) (swap_start + ((nr) << PAGE_SHIFT)) + +/* get number of pages for 'n' bytes (already page-aligned) */ +#define N_PAGES(n) ((n) >> PAGE_SHIFT) + +/* The following two numbers define the maximum fraction of ST-RAM in total + * memory, below that the kernel would automatically use ST-RAM as swap + * space. This decision can be overridden with stram_swap= */ +#define MAX_STRAM_FRACTION_NOM 1 +#define MAX_STRAM_FRACTION_DENOM 3 + +/* Start and end (virtual) of ST-RAM */ +static void *stram_start, *stram_end; + +/* set after memory_init() executed and allocations via start_mem aren't + * possible anymore */ +static int mem_init_done; + +/* set if kernel is in ST-RAM */ +static int kernel_in_stram; + +typedef struct stram_block { + struct stram_block *next; + void *start; + unsigned long size; + unsigned flags; + const char *owner; +} BLOCK; + +/* values for flags field */ +#define BLOCK_FREE 0x01 /* free structure in the BLOCKs pool */ +#define BLOCK_KMALLOCED 0x02 /* structure allocated by kmalloc() */ +#define BLOCK_GFP 0x08 /* block allocated with __get_dma_pages() */ +#define BLOCK_INSWAP 0x10 /* block allocated in swap space */ + +/* list of allocated blocks */ +static BLOCK *alloc_list; + +/* We can't always use kmalloc() to allocate BLOCK structures, since + * stram_alloc() can be called rather early. So we need some pool of + * statically allocated structures. 20 of them is more than enough, so in most + * cases we never should need to call kmalloc(). */ +#define N_STATIC_BLOCKS 20 +static BLOCK static_blocks[N_STATIC_BLOCKS]; + +#ifdef CONFIG_STRAM_SWAP +/* max. number of bytes to use for swapping + * 0 = no ST-RAM swapping + * -1 = do swapping (to whole ST-RAM) if it's less than MAX_STRAM_FRACTION of + * total memory + */ +static int max_swap_size = -1; + +/* start and end of swapping area */ +static void *swap_start, *swap_end; + +/* The ST-RAM's swap info structure */ +static struct swap_info_struct *stram_swap_info; + +/* The ST-RAM's swap type */ +static int stram_swap_type; + +/* Semaphore for get_stram_region. */ +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, + * and for the minor a relatively odd number to avoid the user creating and + * using that device. */ +#define STRAM_MAJOR Z2RAM_MAJOR +#define STRAM_MINOR 13 + +/* Some impossible pointer value */ +#define MAGIC_FILE_P (struct file *)0xffffdead + +#ifdef DO_PROC +static unsigned stat_swap_read; +static unsigned stat_swap_write; +static unsigned stat_swap_force; +#endif /* DO_PROC */ + +#endif /* CONFIG_STRAM_SWAP */ + +/***************************** Prototypes *****************************/ + +#ifdef CONFIG_STRAM_SWAP +static int swap_init(void *start_mem, void *swap_data); +static void *get_stram_region( unsigned long n_pages ); +static void free_stram_region( unsigned long offset, unsigned long n_pages + ); +static int in_some_region(void *addr); +static unsigned long find_free_region( unsigned long n_pages, unsigned long + *total_free, unsigned long + *region_free ); +static void do_stram_request(request_queue_t *); +static int stram_open( struct inode *inode, struct file *filp ); +static int stram_release( struct inode *inode, struct file *filp ); +static void reserve_region(void *start, void *end); +#endif +static BLOCK *add_region( void *addr, unsigned long size ); +static BLOCK *find_region( void *addr ); +static int remove_region( BLOCK *block ); + +/************************* End of Prototypes **************************/ + + +/* ------------------------------------------------------------------------ */ +/* Public Interface */ +/* ------------------------------------------------------------------------ */ + +/* + * This init function is called very early by atari/config.c + * It initializes some internal variables needed for stram_alloc() + */ +void __init atari_stram_init(void) +{ + int i; + + /* initialize static blocks */ + for( i = 0; i < N_STATIC_BLOCKS; ++i ) + static_blocks[i].flags = BLOCK_FREE; + + /* determine whether kernel code resides in ST-RAM (then ST-RAM is the + * first memory block at virtual 0x0) */ + stram_start = phys_to_virt(0); + kernel_in_stram = (stram_start == 0); + + for( i = 0; i < m68k_num_memory; ++i ) { + if (m68k_memory[i].addr == 0) { + /* skip first 2kB or page (supervisor-only!) */ + stram_end = stram_start + m68k_memory[i].size; + return; + } + } + /* Should never come here! (There is always ST-Ram!) */ + panic( "atari_stram_init: no ST-RAM found!" ); +} + + +/* + * This function is called from setup_arch() to reserve the pages needed for + * ST-RAM management. + */ +void __init atari_stram_reserve_pages(void *start_mem) +{ +#ifdef CONFIG_STRAM_SWAP + /* if max_swap_size is negative (i.e. no stram_swap= option given), + * determine at run time whether to use ST-RAM swapping */ + if (max_swap_size < 0) + /* Use swapping if ST-RAM doesn't make up more than MAX_STRAM_FRACTION + * of total memory. In that case, the max. size is set to 16 MB, + * because ST-RAM can never be bigger than that. + * Also, never use swapping on a Hades, there's no separate ST-RAM in + * that machine. */ + max_swap_size = + (!MACH_IS_HADES && + (N_PAGES(stram_end-stram_start)*MAX_STRAM_FRACTION_DENOM <= + ((unsigned long)high_memory>>PAGE_SHIFT)*MAX_STRAM_FRACTION_NOM)) ? 16*1024*1024 : 0; + DPRINTK( "atari_stram_reserve_pages: max_swap_size = %d\n", max_swap_size ); +#endif + + /* always reserve first page of ST-RAM, the first 2 kB are + * supervisor-only! */ + if (!kernel_in_stram) + reserve_bootmem (0, PAGE_SIZE); + +#ifdef CONFIG_STRAM_SWAP + { + void *swap_data; + + start_mem = (void *) PAGE_ALIGN ((unsigned long) start_mem); + /* determine first page to use as swap: if the kernel is + in TT-RAM, this is the first page of (usable) ST-RAM; + otherwise just use the end of kernel data (= start_mem) */ + swap_start = !kernel_in_stram ? stram_start + PAGE_SIZE : start_mem; + /* decrement by one page, rest of kernel assumes that first swap page + * is always reserved and maybe doesn't handle swp_entry == 0 + * correctly */ + swap_start -= PAGE_SIZE; + swap_end = stram_end; + if (swap_end-swap_start > max_swap_size) + swap_end = swap_start + max_swap_size; + DPRINTK( "atari_stram_reserve_pages: swapping enabled; " + "swap=%p-%p\n", swap_start, swap_end); + + /* reserve some amount of memory for maintainance of + * 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)) << PAGE_SHIFT; + /* correct swap_start if necessary */ + if (swap_start + PAGE_SIZE == swap_data) + swap_start = start_mem - PAGE_SIZE; + + if (!swap_init( start_mem, swap_data )) { + printk( KERN_ERR "ST-RAM swap space initialization failed\n" ); + max_swap_size = 0; + return; + } + /* reserve region for swapping meta-data */ + reserve_region(swap_data, start_mem); + /* reserve swapping area itself */ + reserve_region(swap_start + PAGE_SIZE, swap_end); + + /* + * If the whole ST-RAM is used for swapping, there are no allocatable + * dma pages left. But unfortunately, some shared parts of the kernel + * (particularly the SCSI mid-level) call __get_dma_pages() + * unconditionally :-( These calls then fail, and scsi.c even doesn't + * check for NULL return values and just crashes. The quick fix for + * this (instead of doing much clean up work in the SCSI code) is to + * pretend all pages are DMA-able by setting mach_max_dma_address to + * ULONG_MAX. This doesn't change any functionality so far, since + * get_dma_pages() shouldn't be used on Atari anyway anymore (better + * use atari_stram_alloc()), and the Atari SCSI drivers don't need DMA + * memory. But unfortunately there's now no kind of warning (even not + * a NULL return value) if you use get_dma_pages() nevertheless :-( + * You just will get non-DMA-able memory... + */ + mach_max_dma_address = 0xffffffff; + } +#endif +} + +void atari_stram_mem_init_hook (void) +{ + mem_init_done = 1; +} + + +/* + * This is main public interface: somehow allocate a ST-RAM block + * There are three strategies: + * + * - If we're before mem_init(), we have to make a static allocation. The + * region is taken in the kernel data area (if the kernel is in ST-RAM) or + * from the start of ST-RAM (if the kernel is in TT-RAM) and added to the + * rsvd_stram_* region. The ST-RAM is somewhere in the middle of kernel + * address space in the latter case. + * + * - If mem_init() already has been called and ST-RAM swapping is enabled, + * try to get the memory from the (pseudo) swap-space, either free already + * or by moving some other pages out of the swap. + * + * - If mem_init() already has been called, and ST-RAM swapping is not + * enabled, the only possibility is to try with __get_dma_pages(). This has + * the disadvantage that it's very hard to get more than 1 page, and it is + * likely to fail :-( + * + */ +void *atari_stram_alloc(long size, const char *owner) +{ + void *addr = NULL; + BLOCK *block; + int flags; + + DPRINTK("atari_stram_alloc(size=%08lx,owner=%s)\n", size, owner); + + size = ALIGN_IF_SWAP(size); + DPRINTK( "atari_stram_alloc: rounded size = %08lx\n", size ); +#ifdef CONFIG_STRAM_SWAP + if (max_swap_size) { + /* If swapping is active: make some free space in the swap + "device". */ + DPRINTK( "atari_stram_alloc: after mem_init, swapping ok, " + "calling get_region\n" ); + addr = get_stram_region( N_PAGES(size) ); + flags = BLOCK_INSWAP; + } + else +#endif + if (!mem_init_done) + return alloc_bootmem_low(size); + else { + /* After mem_init() and no swapping: can only resort to + * __get_dma_pages() */ + addr = (void *)__get_dma_pages(GFP_KERNEL, get_order(size)); + flags = BLOCK_GFP; + DPRINTK( "atari_stram_alloc: after mem_init, swapping off, " + "get_pages=%p\n", addr ); + } + + if (addr) { + if (!(block = add_region( addr, size ))) { + /* out of memory for BLOCK structure :-( */ + DPRINTK( "atari_stram_alloc: out of mem for BLOCK -- " + "freeing again\n" ); +#ifdef CONFIG_STRAM_SWAP + if (flags == BLOCK_INSWAP) + free_stram_region( SWAP_NR(addr), N_PAGES(size) ); + else +#endif + free_pages((unsigned long)addr, get_order(size)); + return( NULL ); + } + block->owner = owner; + block->flags |= flags; + } + return( addr ); +} + +void atari_stram_free( void *addr ) + +{ + BLOCK *block; + + DPRINTK( "atari_stram_free(addr=%p)\n", addr ); + + if (!(block = find_region( addr ))) { + printk( KERN_ERR "Attempt to free non-allocated ST-RAM block at %p " + "from %p\n", addr, __builtin_return_address(0) ); + return; + } + DPRINTK( "atari_stram_free: found block (%p): size=%08lx, owner=%s, " + "flags=%02x\n", block, block->size, block->owner, block->flags ); + +#ifdef CONFIG_STRAM_SWAP + if (!max_swap_size) { +#endif + if (block->flags & BLOCK_GFP) { + DPRINTK("atari_stram_free: is kmalloced, order_size=%d\n", + get_order(block->size)); + free_pages((unsigned long)addr, get_order(block->size)); + } + else + goto fail; +#ifdef CONFIG_STRAM_SWAP + } + else if (block->flags & BLOCK_INSWAP) { + DPRINTK( "atari_stram_free: is swap-alloced\n" ); + free_stram_region( SWAP_NR(block->start), N_PAGES(block->size) ); + } + else + goto fail; +#endif + remove_region( block ); + return; + + fail: + printk( KERN_ERR "atari_stram_free: cannot free block at %p " + "(called from %p)\n", addr, __builtin_return_address(0) ); +} + + +#ifdef CONFIG_STRAM_SWAP + + +/* ------------------------------------------------------------------------ */ +/* Main Swapping Functions */ +/* ------------------------------------------------------------------------ */ + + +/* + * Initialize ST-RAM swap device + * (lots copied and modified from sys_swapon() in mm/swapfile.c) + */ +static int __init swap_init(void *start_mem, void *swap_data) +{ + static struct dentry fake_dentry; + static struct vfsmount fake_vfsmnt; + struct swap_info_struct *p; + struct inode swap_inode; + unsigned int type; + void *addr; + int i, j, k, prev; + + DPRINTK("swap_init(start_mem=%p, swap_data=%p)\n", + start_mem, swap_data); + + /* need at least one page for swapping to (and this also isn't very + * much... :-) */ + if (swap_end - swap_start < 2*PAGE_SIZE) { + printk( KERN_WARNING "stram_swap_init: swap space too small\n" ); + return( 0 ); + } + + /* find free slot in swap_info */ + for( p = swap_info, type = 0; type < nr_swapfiles; type++, p++ ) + if (!(p->flags & SWP_USED)) + break; + if (type >= MAX_SWAPFILES) { + printk( KERN_WARNING "stram_swap_init: max. number of " + "swap devices exhausted\n" ); + return( 0 ); + } + if (type >= nr_swapfiles) + nr_swapfiles = type+1; + + stram_swap_info = p; + stram_swap_type = type; + + /* fake some dir cache entries to give us some name in /dev/swaps */ + fake_dentry.d_parent = &fake_dentry; + fake_dentry.d_name.name = "stram (internal)"; + fake_dentry.d_name.len = 16; + fake_vfsmnt.mnt_parent = &fake_vfsmnt; + + p->flags = SWP_USED; + p->swap_file = &fake_dentry; + p->swap_vfsmnt = &fake_vfsmnt; + p->swap_map = swap_data; + p->cluster_nr = 0; + p->next = -1; + p->prio = 0x7ff0; /* a rather high priority, but not the higest + * to give the user a chance to override */ + + /* call stram_open() directly, avoids at least the overhead in + * constructing a dummy file structure... */ + swap_inode.i_rdev = MKDEV( STRAM_MAJOR, STRAM_MINOR ); + stram_open( &swap_inode, MAGIC_FILE_P ); + p->max = SWAP_NR(swap_end); + + /* 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 */ + k = 0; /* # of already allocated pages (from pre-mem_init stram_alloc()) */ + p->lowest_bit = 0; + p->highest_bit = 0; + for( i = 1, addr = SWAP_ADDR(1); i < p->max; + i++, addr += PAGE_SIZE ) { + if (in_some_region( addr )) { + p->swap_map[i] = SWAP_MAP_BAD; + ++k; + } + else if (kernel_in_stram && addr < start_mem ) { + p->swap_map[i] = SWAP_MAP_BAD; + } + else { + p->swap_map[i] = 0; + ++j; + if (!p->lowest_bit) p->lowest_bit = i; + p->highest_bit = i; + } + } + /* first page always reserved (and doesn't really belong to swap space) */ + p->swap_map[0] = SWAP_MAP_BAD; + + /* now swapping to this device ok */ + p->pages = j + k; + swap_list_lock(); + nr_swap_pages += j; + p->flags = SWP_WRITEOK; + + /* insert swap space into swap_list */ + prev = -1; + for (i = swap_list.head; i >= 0; i = swap_info[i].next) { + if (p->prio >= swap_info[i].prio) { + break; + } + prev = i; + } + p->next = i; + if (prev < 0) { + swap_list.head = swap_list.next = p - swap_info; + } else { + swap_info[prev].next = p - swap_info; + } + swap_list_unlock(); + + printk( KERN_INFO "Using %dk (%d pages) of ST-RAM as swap space.\n", + p->pages << 2, p->pages ); + return( 1 ); +} + + +/* + * The swap entry has been read in advance, and we return 1 to indicate + * that the page has been used or is no longer needed. + * + * Always set the resulting pte to be nowrite (the same as COW pages + * after one process has exited). We don't know just how many PTEs will + * share this swap entry, so be cautious and let do_wp_page work out + * what to do if a write is requested later. + */ +static inline void unswap_pte(struct vm_area_struct * vma, unsigned long + address, pte_t *dir, swp_entry_t entry, + struct page *page) +{ + pte_t pte = *dir; + + if (pte_none(pte)) + return; + if (pte_present(pte)) { + /* If this entry is swap-cached, then page must already + hold the right address for any copies in physical + memory */ + if (pte_page(pte) != page) + return; + /* We will be removing the swap cache in a moment, so... */ + set_pte(dir, pte_mkdirty(pte)); + return; + } + if (pte_val(pte) != entry.val) + return; + + DPRINTK("unswap_pte: replacing entry %08lx by new page %p", + entry.val, page); + set_pte(dir, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); + swap_free(entry); + get_page(page); + inc_mm_counter(vma->vm_mm, rss); +} + +static inline void unswap_pmd(struct vm_area_struct * vma, pmd_t *dir, + unsigned long address, unsigned long size, + unsigned long offset, swp_entry_t entry, + struct page *page) +{ + pte_t * pte; + unsigned long end; + + if (pmd_none(*dir)) + return; + if (pmd_bad(*dir)) { + pmd_ERROR(*dir); + pmd_clear(dir); + return; + } + pte = pte_offset_kernel(dir, address); + offset += address & PMD_MASK; + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + do { + unswap_pte(vma, offset+address-vma->vm_start, pte, entry, page); + address += PAGE_SIZE; + pte++; + } while (address < end); +} + +static inline void unswap_pgd(struct vm_area_struct * vma, pgd_t *dir, + unsigned long address, unsigned long size, + swp_entry_t entry, struct page *page) +{ + pmd_t * pmd; + unsigned long offset, end; + + if (pgd_none(*dir)) + return; + if (pgd_bad(*dir)) { + pgd_ERROR(*dir); + pgd_clear(dir); + return; + } + pmd = pmd_offset(dir, address); + offset = address & PGDIR_MASK; + address &= ~PGDIR_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + do { + unswap_pmd(vma, pmd, address, end - address, offset, entry, + page); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address < end); +} + +static void unswap_vma(struct vm_area_struct * vma, pgd_t *pgdir, + swp_entry_t entry, struct page *page) +{ + unsigned long start = vma->vm_start, end = vma->vm_end; + + do { + unswap_pgd(vma, pgdir, start, end - start, entry, page); + start = (start + PGDIR_SIZE) & PGDIR_MASK; + pgdir++; + } while (start < end); +} + +static void unswap_process(struct mm_struct * mm, swp_entry_t entry, + struct page *page) +{ + struct vm_area_struct* vma; + + /* + * Go through process' page directory. + */ + if (!mm) + return; + for (vma = mm->mmap; vma; vma = vma->vm_next) { + pgd_t * pgd = pgd_offset(mm, vma->vm_start); + unswap_vma(vma, pgd, entry, page); + } +} + + +static int unswap_by_read(unsigned short *map, unsigned long max, + unsigned long start, unsigned long n_pages) +{ + struct task_struct *p; + struct page *page; + swp_entry_t entry; + unsigned long i; + + DPRINTK( "unswapping %lu..%lu by reading in\n", + start, start+n_pages-1 ); + + for( i = start; i < start+n_pages; ++i ) { + if (map[i] == SWAP_MAP_BAD) { + printk( KERN_ERR "get_stram_region: page %lu already " + "reserved??\n", i ); + continue; + } + + if (map[i]) { + entry = swp_entry(stram_swap_type, i); + DPRINTK("unswap: map[i=%lu]=%u nr_swap=%ld\n", + i, map[i], nr_swap_pages); + + swap_device_lock(stram_swap_info); + map[i]++; + swap_device_unlock(stram_swap_info); + /* Get a page for the entry, using the existing + swap cache page if there is one. Otherwise, + get a clean page and read the swap into it. */ + page = read_swap_cache_async(entry, NULL, 0); + if (!page) { + swap_free(entry); + return -ENOMEM; + } + read_lock(&tasklist_lock); + for_each_process(p) + unswap_process(p->mm, entry, page); + read_unlock(&tasklist_lock); + shmem_unuse(entry, page); + /* Now get rid of the extra reference to the + temporary page we've been using. */ + if (PageSwapCache(page)) + delete_from_swap_cache(page); + __free_page(page); + #ifdef DO_PROC + stat_swap_force++; + #endif + } + + DPRINTK( "unswap: map[i=%lu]=%u nr_swap=%ld\n", + i, map[i], nr_swap_pages ); + swap_list_lock(); + swap_device_lock(stram_swap_info); + map[i] = SWAP_MAP_BAD; + if (stram_swap_info->lowest_bit == i) + stram_swap_info->lowest_bit++; + if (stram_swap_info->highest_bit == i) + stram_swap_info->highest_bit--; + --nr_swap_pages; + swap_device_unlock(stram_swap_info); + swap_list_unlock(); + } + + return 0; +} + +/* + * reserve a region in ST-RAM swap space for an allocation + */ +static void *get_stram_region( unsigned long n_pages ) +{ + unsigned short *map = stram_swap_info->swap_map; + unsigned long max = stram_swap_info->max; + unsigned long start, total_free, region_free; + int err; + void *ret = NULL; + + DPRINTK( "get_stram_region(n_pages=%lu)\n", n_pages ); + + down(&stram_swap_sem); + + /* disallow writing to the swap device now */ + stram_swap_info->flags = SWP_USED; + + /* find a region of n_pages pages in the swap space including as much free + * pages as possible (and excluding any already-reserved pages). */ + if (!(start = find_free_region( n_pages, &total_free, ®ion_free ))) + goto end; + DPRINTK( "get_stram_region: region starts at %lu, has %lu free pages\n", + start, region_free ); + + err = unswap_by_read(map, max, start, n_pages); + if (err) + goto end; + + ret = SWAP_ADDR(start); + end: + /* allow using swap device again */ + stram_swap_info->flags = SWP_WRITEOK; + up(&stram_swap_sem); + DPRINTK( "get_stram_region: returning %p\n", ret ); + return( ret ); +} + + +/* + * free a reserved region in ST-RAM swap space + */ +static void free_stram_region( unsigned long offset, unsigned long n_pages ) +{ + unsigned short *map = stram_swap_info->swap_map; + + DPRINTK( "free_stram_region(offset=%lu,n_pages=%lu)\n", offset, n_pages ); + + if (offset < 1 || offset + n_pages > stram_swap_info->max) { + printk( KERN_ERR "free_stram_region: Trying to free non-ST-RAM\n" ); + return; + } + + swap_list_lock(); + swap_device_lock(stram_swap_info); + /* un-reserve the freed pages */ + for( ; n_pages > 0; ++offset, --n_pages ) { + if (map[offset] != SWAP_MAP_BAD) + printk( KERN_ERR "free_stram_region: Swap page %lu was not " + "reserved\n", offset ); + map[offset] = 0; + } + + /* update swapping meta-data */ + if (offset < stram_swap_info->lowest_bit) + stram_swap_info->lowest_bit = offset; + if (offset+n_pages-1 > stram_swap_info->highest_bit) + stram_swap_info->highest_bit = offset+n_pages-1; + if (stram_swap_info->prio > swap_info[swap_list.next].prio) + swap_list.next = swap_list.head; + nr_swap_pages += n_pages; + swap_device_unlock(stram_swap_info); + swap_list_unlock(); +} + + +/* ------------------------------------------------------------------------ */ +/* Utility Functions for Swapping */ +/* ------------------------------------------------------------------------ */ + + +/* is addr in some of the allocated regions? */ +static int in_some_region(void *addr) +{ + BLOCK *p; + + for( p = alloc_list; p; p = p->next ) { + if (p->start <= addr && addr < p->start + p->size) + return( 1 ); + } + return( 0 ); +} + + +static unsigned long find_free_region(unsigned long n_pages, + unsigned long *total_free, + unsigned long *region_free) +{ + unsigned short *map = stram_swap_info->swap_map; + unsigned long max = stram_swap_info->max; + unsigned long head, tail, max_start; + long nfree, max_free; + + /* first scan the swap space for a suitable place for the allocation */ + head = 1; + max_start = 0; + max_free = -1; + *total_free = 0; + + start_over: + /* increment tail until final window size reached, and count free pages */ + nfree = 0; + for( tail = head; tail-head < n_pages && tail < max; ++tail ) { + if (map[tail] == SWAP_MAP_BAD) { + head = tail+1; + goto start_over; + } + if (!map[tail]) { + ++nfree; + ++*total_free; + } + } + if (tail-head < n_pages) + goto out; + if (nfree > max_free) { + max_start = head; + max_free = nfree; + if (max_free >= n_pages) + /* don't need more free pages... :-) */ + goto out; + } + + /* now shift the window and look for the area where as much pages as + * possible are free */ + while( tail < max ) { + nfree -= (map[head++] == 0); + if (map[tail] == SWAP_MAP_BAD) { + head = tail+1; + goto start_over; + } + if (!map[tail]) { + ++nfree; + ++*total_free; + } + ++tail; + if (nfree > max_free) { + max_start = head; + max_free = nfree; + if (max_free >= n_pages) + /* don't need more free pages... :-) */ + goto out; + } + } + + out: + if (max_free < 0) { + printk( KERN_NOTICE "get_stram_region: ST-RAM too full or fragmented " + "-- can't allocate %lu pages\n", n_pages ); + return( 0 ); + } + + *region_free = max_free; + return( max_start ); +} + + +/* setup parameters from command line */ +void __init stram_swap_setup(char *str, int *ints) +{ + if (ints[0] >= 1) + max_swap_size = ((ints[1] < 0 ? 0 : ints[1]) * 1024) & PAGE_MASK; +} + + +/* ------------------------------------------------------------------------ */ +/* ST-RAM device */ +/* ------------------------------------------------------------------------ */ + +static int refcnt; + +static void do_stram_request(request_queue_t *q) +{ + struct request *req; + + while ((req = elv_next_request(q)) != NULL) { + void *start = swap_start + (req->sector << 9); + unsigned long len = req->current_nr_sectors << 9; + if ((start + len) > swap_end) { + printk( KERN_ERR "stram: bad access beyond end of device: " + "block=%ld, count=%d\n", + req->sector, + req->current_nr_sectors ); + end_request(req, 0); + continue; + } + + if (req->cmd == READ) { + memcpy(req->buffer, start, len); +#ifdef DO_PROC + stat_swap_read += N_PAGES(len); +#endif + } + else { + memcpy(start, req->buffer, len); +#ifdef DO_PROC + stat_swap_write += N_PAGES(len); +#endif + } + end_request(req, 1); + } +} + + +static int stram_open( struct inode *inode, struct file *filp ) +{ + if (filp != MAGIC_FILE_P) { + printk( KERN_NOTICE "Only kernel can open ST-RAM device\n" ); + return( -EPERM ); + } + if (refcnt) + return( -EBUSY ); + ++refcnt; + return( 0 ); +} + +static int stram_release( struct inode *inode, struct file *filp ) +{ + if (filp != MAGIC_FILE_P) { + printk( KERN_NOTICE "Only kernel can close ST-RAM device\n" ); + return( -EPERM ); + } + if (refcnt > 0) + --refcnt; + return( 0 ); +} + + +static struct block_device_operations stram_fops = { + .open = stram_open, + .release = stram_release, +}; + +static struct gendisk *stram_disk; +static struct request_queue *stram_queue; +static DEFINE_SPINLOCK(stram_lock); + +int __init stram_device_init(void) +{ + if (!MACH_IS_ATARI) + /* no point in initializing this, I hope */ + return -ENXIO; + + if (!max_swap_size) + /* swapping not enabled */ + return -ENXIO; + stram_disk = alloc_disk(1); + if (!stram_disk) + return -ENOMEM; + + if (register_blkdev(STRAM_MAJOR, "stram")) { + put_disk(stram_disk); + return -ENXIO; + } + + stram_queue = blk_init_queue(do_stram_request, &stram_lock); + if (!stram_queue) { + unregister_blkdev(STRAM_MAJOR, "stram"); + put_disk(stram_disk); + return -ENOMEM; + } + + stram_disk->major = STRAM_MAJOR; + stram_disk->first_minor = STRAM_MINOR; + stram_disk->fops = &stram_fops; + stram_disk->queue = stram_queue; + sprintf(stram_disk->disk_name, "stram"); + set_capacity(stram_disk, (swap_end - swap_start)/512); + add_disk(stram_disk); + return 0; +} + + + +/* ------------------------------------------------------------------------ */ +/* Misc Utility Functions */ +/* ------------------------------------------------------------------------ */ + +/* reserve a range of pages */ +static void reserve_region(void *start, void *end) +{ + reserve_bootmem (virt_to_phys(start), end - start); +} + +#endif /* CONFIG_STRAM_SWAP */ + + +/* ------------------------------------------------------------------------ */ +/* Region Management */ +/* ------------------------------------------------------------------------ */ + + +/* insert a region into the alloced list (sorted) */ +static BLOCK *add_region( void *addr, unsigned long size ) +{ + BLOCK **p, *n = NULL; + int i; + + for( i = 0; i < N_STATIC_BLOCKS; ++i ) { + if (static_blocks[i].flags & BLOCK_FREE) { + n = &static_blocks[i]; + n->flags = 0; + break; + } + } + if (!n && mem_init_done) { + /* if statics block pool exhausted and we can call kmalloc() already + * (after mem_init()), try that */ + n = kmalloc( sizeof(BLOCK), GFP_KERNEL ); + if (n) + n->flags = BLOCK_KMALLOCED; + } + if (!n) { + printk( KERN_ERR "Out of memory for ST-RAM descriptor blocks\n" ); + return( NULL ); + } + n->start = addr; + n->size = size; + + for( p = &alloc_list; *p; p = &((*p)->next) ) + if ((*p)->start > addr) break; + n->next = *p; + *p = n; + + return( n ); +} + + +/* find a region (by start addr) in the alloced list */ +static BLOCK *find_region( void *addr ) +{ + BLOCK *p; + + for( p = alloc_list; p; p = p->next ) { + if (p->start == addr) + return( p ); + if (p->start > addr) + break; + } + return( NULL ); +} + + +/* remove a block from the alloced list */ +static int remove_region( BLOCK *block ) +{ + BLOCK **p; + + for( p = &alloc_list; *p; p = &((*p)->next) ) + if (*p == block) break; + if (!*p) + return( 0 ); + + *p = block->next; + if (block->flags & BLOCK_KMALLOCED) + kfree( block ); + else + block->flags |= BLOCK_FREE; + return( 1 ); +} + + + +/* ------------------------------------------------------------------------ */ +/* /proc statistics file stuff */ +/* ------------------------------------------------------------------------ */ + +#ifdef DO_PROC + +#define PRINT_PROC(fmt,args...) len += sprintf( buf+len, fmt, ##args ) + +int get_stram_list( char *buf ) +{ + int len = 0; + BLOCK *p; +#ifdef CONFIG_STRAM_SWAP + int i; + unsigned short *map = stram_swap_info->swap_map; + unsigned long max = stram_swap_info->max; + unsigned free = 0, used = 0, rsvd = 0; +#endif + +#ifdef CONFIG_STRAM_SWAP + if (max_swap_size) { + for( i = 1; i < max; ++i ) { + if (!map[i]) + ++free; + else if (map[i] == SWAP_MAP_BAD) + ++rsvd; + else + ++used; + } + PRINT_PROC( + "Total ST-RAM: %8u kB\n" + "Total ST-RAM swap: %8lu kB\n" + "Free swap: %8u kB\n" + "Used swap: %8u kB\n" + "Allocated swap: %8u kB\n" + "Swap Reads: %8u\n" + "Swap Writes: %8u\n" + "Swap Forced Reads: %8u\n", + (stram_end - stram_start) >> 10, + (max-1) << (PAGE_SHIFT-10), + free << (PAGE_SHIFT-10), + used << (PAGE_SHIFT-10), + rsvd << (PAGE_SHIFT-10), + stat_swap_read, + stat_swap_write, + stat_swap_force ); + } + else { +#endif + PRINT_PROC( "ST-RAM swapping disabled\n" ); + PRINT_PROC("Total ST-RAM: %8u kB\n", + (stram_end - stram_start) >> 10); +#ifdef CONFIG_STRAM_SWAP + } +#endif + + PRINT_PROC( "Allocated regions:\n" ); + for( p = alloc_list; p; p = p->next ) { + if (len + 50 >= PAGE_SIZE) + break; + PRINT_PROC("0x%08lx-0x%08lx: %s (", + virt_to_phys(p->start), + virt_to_phys(p->start+p->size-1), + p->owner); + if (p->flags & BLOCK_GFP) + PRINT_PROC( "page-alloced)\n" ); + else if (p->flags & BLOCK_INSWAP) + PRINT_PROC( "in swap)\n" ); + else + PRINT_PROC( "??)\n" ); + } + + return( len ); +} + +#endif + + +/* + * Local variables: + * c-indent-level: 4 + * tab-width: 4 + * End: + */ diff --git a/arch/m68k/atari/time.c b/arch/m68k/atari/time.c new file mode 100644 index 00000000000..6df7fb60dfe --- /dev/null +++ b/arch/m68k/atari/time.c @@ -0,0 +1,348 @@ +/* + * linux/arch/m68k/atari/time.c + * + * Atari time and real time clock stuff + * + * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include <linux/types.h> +#include <linux/mc146818rtc.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/rtc.h> +#include <linux/bcd.h> + +#include <asm/atariints.h> + +void __init +atari_sched_init(irqreturn_t (*timer_routine)(int, void *, struct pt_regs *)) +{ + /* set Timer C data Register */ + mfp.tim_dt_c = INT_TICKS; + /* start timer C, div = 1:100 */ + mfp.tim_ct_cd = (mfp.tim_ct_cd & 15) | 0x60; + /* install interrupt service routine for MFP Timer C */ + request_irq(IRQ_MFP_TIMC, timer_routine, IRQ_TYPE_SLOW, + "timer", timer_routine); +} + +/* ++andreas: gettimeoffset fixed to check for pending interrupt */ + +#define TICK_SIZE 10000 + +/* This is always executed with interrupts disabled. */ +unsigned long atari_gettimeoffset (void) +{ + unsigned long ticks, offset = 0; + + /* read MFP timer C current value */ + ticks = mfp.tim_dt_c; + /* The probability of underflow is less than 2% */ + if (ticks > INT_TICKS - INT_TICKS / 50) + /* Check for pending timer interrupt */ + if (mfp.int_pn_b & (1 << 5)) + offset = TICK_SIZE; + + ticks = INT_TICKS - ticks; + ticks = ticks * 10000L / INT_TICKS; + + return ticks + offset; +} + + +static void mste_read(struct MSTE_RTC *val) +{ +#define COPY(v) val->v=(mste_rtc.v & 0xf) + do { + COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ; + COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ; + COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ; + COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ; + COPY(year_tens) ; + /* prevent from reading the clock while it changed */ + } while (val->sec_ones != (mste_rtc.sec_ones & 0xf)); +#undef COPY +} + +static void mste_write(struct MSTE_RTC *val) +{ +#define COPY(v) mste_rtc.v=val->v + do { + COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ; + COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ; + COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ; + COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ; + COPY(year_tens) ; + /* prevent from writing the clock while it changed */ + } while (val->sec_ones != (mste_rtc.sec_ones & 0xf)); +#undef COPY +} + +#define RTC_READ(reg) \ + ({ unsigned char __val; \ + (void) atari_writeb(reg,&tt_rtc.regsel); \ + __val = tt_rtc.data; \ + __val; \ + }) + +#define RTC_WRITE(reg,val) \ + do { \ + atari_writeb(reg,&tt_rtc.regsel); \ + tt_rtc.data = (val); \ + } while(0) + + +#define HWCLK_POLL_INTERVAL 5 + +int atari_mste_hwclk( int op, struct rtc_time *t ) +{ + int hour, year; + int hr24=0; + struct MSTE_RTC val; + + mste_rtc.mode=(mste_rtc.mode | 1); + hr24=mste_rtc.mon_tens & 1; + mste_rtc.mode=(mste_rtc.mode & ~1); + + if (op) { + /* write: prepare values */ + + val.sec_ones = t->tm_sec % 10; + val.sec_tens = t->tm_sec / 10; + val.min_ones = t->tm_min % 10; + val.min_tens = t->tm_min / 10; + hour = t->tm_hour; + if (!hr24) { + if (hour > 11) + hour += 20 - 12; + if (hour == 0 || hour == 20) + hour += 12; + } + val.hr_ones = hour % 10; + val.hr_tens = hour / 10; + val.day_ones = t->tm_mday % 10; + val.day_tens = t->tm_mday / 10; + val.mon_ones = (t->tm_mon+1) % 10; + val.mon_tens = (t->tm_mon+1) / 10; + year = t->tm_year - 80; + val.year_ones = year % 10; + val.year_tens = year / 10; + val.weekday = t->tm_wday; + mste_write(&val); + mste_rtc.mode=(mste_rtc.mode | 1); + val.year_ones = (year % 4); /* leap year register */ + mste_rtc.mode=(mste_rtc.mode & ~1); + } + else { + mste_read(&val); + t->tm_sec = val.sec_ones + val.sec_tens * 10; + t->tm_min = val.min_ones + val.min_tens * 10; + hour = val.hr_ones + val.hr_tens * 10; + if (!hr24) { + if (hour == 12 || hour == 12 + 20) + hour -= 12; + if (hour >= 20) + hour += 12 - 20; + } + t->tm_hour = hour; + t->tm_mday = val.day_ones + val.day_tens * 10; + t->tm_mon = val.mon_ones + val.mon_tens * 10 - 1; + t->tm_year = val.year_ones + val.year_tens * 10 + 80; + t->tm_wday = val.weekday; + } + return 0; +} + +int atari_tt_hwclk( int op, struct rtc_time *t ) +{ + int sec=0, min=0, hour=0, day=0, mon=0, year=0, wday=0; + unsigned long flags; + unsigned char ctrl; + int pm = 0; + + ctrl = RTC_READ(RTC_CONTROL); /* control registers are + * independent from the UIP */ + + if (op) { + /* write: prepare values */ + + sec = t->tm_sec; + min = t->tm_min; + hour = t->tm_hour; + day = t->tm_mday; + mon = t->tm_mon + 1; + year = t->tm_year - atari_rtc_year_offset; + wday = t->tm_wday + (t->tm_wday >= 0); + + if (!(ctrl & RTC_24H)) { + if (hour > 11) { + pm = 0x80; + if (hour != 12) + hour -= 12; + } + else if (hour == 0) + hour = 12; + } + + if (!(ctrl & RTC_DM_BINARY)) { + BIN_TO_BCD(sec); + BIN_TO_BCD(min); + BIN_TO_BCD(hour); + BIN_TO_BCD(day); + BIN_TO_BCD(mon); + BIN_TO_BCD(year); + if (wday >= 0) BIN_TO_BCD(wday); + } + } + + /* Reading/writing the clock registers is a bit critical due to + * the regular update cycle of the RTC. While an update is in + * progress, registers 0..9 shouldn't be touched. + * The problem is solved like that: If an update is currently in + * progress (the UIP bit is set), the process sleeps for a while + * (50ms). This really should be enough, since the update cycle + * normally needs 2 ms. + * If the UIP bit reads as 0, we have at least 244 usecs until the + * update starts. This should be enough... But to be sure, + * additionally the RTC_SET bit is set to prevent an update cycle. + */ + + while( RTC_READ(RTC_FREQ_SELECT) & RTC_UIP ) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HWCLK_POLL_INTERVAL); + } + + local_irq_save(flags); + RTC_WRITE( RTC_CONTROL, ctrl | RTC_SET ); + if (!op) { + sec = RTC_READ( RTC_SECONDS ); + min = RTC_READ( RTC_MINUTES ); + hour = RTC_READ( RTC_HOURS ); + day = RTC_READ( RTC_DAY_OF_MONTH ); + mon = RTC_READ( RTC_MONTH ); + year = RTC_READ( RTC_YEAR ); + wday = RTC_READ( RTC_DAY_OF_WEEK ); + } + else { + RTC_WRITE( RTC_SECONDS, sec ); + RTC_WRITE( RTC_MINUTES, min ); + RTC_WRITE( RTC_HOURS, hour + pm); + RTC_WRITE( RTC_DAY_OF_MONTH, day ); + RTC_WRITE( RTC_MONTH, mon ); + RTC_WRITE( RTC_YEAR, year ); + if (wday >= 0) RTC_WRITE( RTC_DAY_OF_WEEK, wday ); + } + RTC_WRITE( RTC_CONTROL, ctrl & ~RTC_SET ); + local_irq_restore(flags); + + if (!op) { + /* read: adjust values */ + + if (hour & 0x80) { + hour &= ~0x80; + pm = 1; + } + + if (!(ctrl & RTC_DM_BINARY)) { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + BCD_TO_BIN(wday); + } + + if (!(ctrl & RTC_24H)) { + if (!pm && hour == 12) + hour = 0; + else if (pm && hour != 12) + hour += 12; + } + + t->tm_sec = sec; + t->tm_min = min; + t->tm_hour = hour; + t->tm_mday = day; + t->tm_mon = mon - 1; + t->tm_year = year + atari_rtc_year_offset; + t->tm_wday = wday - 1; + } + + return( 0 ); +} + + +int atari_mste_set_clock_mmss (unsigned long nowtime) +{ + short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60; + struct MSTE_RTC val; + unsigned char rtc_minutes; + + mste_read(&val); + rtc_minutes= val.min_ones + val.min_tens * 10; + if ((rtc_minutes < real_minutes + ? real_minutes - rtc_minutes + : rtc_minutes - real_minutes) < 30) + { + val.sec_ones = real_seconds % 10; + val.sec_tens = real_seconds / 10; + val.min_ones = real_minutes % 10; + val.min_tens = real_minutes / 10; + mste_write(&val); + } + else + return -1; + return 0; +} + +int atari_tt_set_clock_mmss (unsigned long nowtime) +{ + int retval = 0; + short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60; + unsigned char save_control, save_freq_select, rtc_minutes; + + save_control = RTC_READ (RTC_CONTROL); /* tell the clock it's being set */ + RTC_WRITE (RTC_CONTROL, save_control | RTC_SET); + + save_freq_select = RTC_READ (RTC_FREQ_SELECT); /* stop and reset prescaler */ + RTC_WRITE (RTC_FREQ_SELECT, save_freq_select | RTC_DIV_RESET2); + + rtc_minutes = RTC_READ (RTC_MINUTES); + if (!(save_control & RTC_DM_BINARY)) + BCD_TO_BIN (rtc_minutes); + + /* Since we're only adjusting minutes and seconds, don't interfere + with hour overflow. This avoids messing with unknown time zones + but requires your RTC not to be off by more than 30 minutes. */ + if ((rtc_minutes < real_minutes + ? real_minutes - rtc_minutes + : rtc_minutes - real_minutes) < 30) + { + if (!(save_control & RTC_DM_BINARY)) + { + BIN_TO_BCD (real_seconds); + BIN_TO_BCD (real_minutes); + } + RTC_WRITE (RTC_SECONDS, real_seconds); + RTC_WRITE (RTC_MINUTES, real_minutes); + } + else + retval = -1; + + RTC_WRITE (RTC_FREQ_SELECT, save_freq_select); + RTC_WRITE (RTC_CONTROL, save_control); + return retval; +} + +/* + * Local variables: + * c-indent-level: 4 + * tab-width: 8 + * End: + */ |