diff options
| author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
| commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
| tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/m68k/atari | |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/m68k/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 @@ | |||
| 1 | # | ||
| 2 | # Makefile for Linux arch/m68k/atari source directory | ||
| 3 | # | ||
| 4 | |||
| 5 | obj-y := config.o time.o debug.o ataints.o stdma.o \ | ||
| 6 | atasound.o stram.o atari_ksyms.o | ||
| 7 | |||
| 8 | ifeq ($(CONFIG_PCI),y) | ||
| 9 | obj-$(CONFIG_HADES) += hades-pci.o | ||
| 10 | 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 @@ | |||
| 1 | /* | ||
| 2 | * arch/m68k/atari/ataints.c -- Atari Linux interrupt handling code | ||
| 3 | * | ||
| 4 | * 5/2/94 Roman Hodek: | ||
| 5 | * Added support for TT interrupts; setup for TT SCU (may someone has | ||
| 6 | * twiddled there and we won't get the right interrupts :-() | ||
| 7 | * | ||
| 8 | * Major change: The device-independent code in m68k/ints.c didn't know | ||
| 9 | * about non-autovec ints yet. It hardcoded the number of possible ints to | ||
| 10 | * 7 (IRQ1...IRQ7). But the Atari has lots of non-autovec ints! I made the | ||
| 11 | * number of possible ints a constant defined in interrupt.h, which is | ||
| 12 | * 47 for the Atari. So we can call request_irq() for all Atari interrupts | ||
| 13 | * just the normal way. Additionally, all vectors >= 48 are initialized to | ||
| 14 | * call trap() instead of inthandler(). This must be changed here, too. | ||
| 15 | * | ||
| 16 | * 1995-07-16 Lars Brinkhoff <f93labr@dd.chalmers.se>: | ||
| 17 | * Corrected a bug in atari_add_isr() which rejected all SCC | ||
| 18 | * interrupt sources if there were no TT MFP! | ||
| 19 | * | ||
| 20 | * 12/13/95: New interface functions atari_level_triggered_int() and | ||
| 21 | * atari_register_vme_int() as support for level triggered VME interrupts. | ||
| 22 | * | ||
| 23 | * 02/12/96: (Roman) | ||
| 24 | * Total rewrite of Atari interrupt handling, for new scheme see comments | ||
| 25 | * below. | ||
| 26 | * | ||
| 27 | * 1996-09-03 lars brinkhoff <f93labr@dd.chalmers.se>: | ||
| 28 | * Added new function atari_unregister_vme_int(), and | ||
| 29 | * modified atari_register_vme_int() as well as IS_VALID_INTNO() | ||
| 30 | * to work with it. | ||
| 31 | * | ||
| 32 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 33 | * License. See the file COPYING in the main directory of this archive | ||
| 34 | * for more details. | ||
| 35 | * | ||
| 36 | */ | ||
| 37 | |||
| 38 | #include <linux/types.h> | ||
| 39 | #include <linux/kernel.h> | ||
| 40 | #include <linux/kernel_stat.h> | ||
| 41 | #include <linux/init.h> | ||
| 42 | #include <linux/seq_file.h> | ||
| 43 | |||
| 44 | #include <asm/system.h> | ||
| 45 | #include <asm/traps.h> | ||
| 46 | |||
| 47 | #include <asm/atarihw.h> | ||
| 48 | #include <asm/atariints.h> | ||
| 49 | #include <asm/atari_stdma.h> | ||
| 50 | #include <asm/irq.h> | ||
| 51 | #include <asm/entry.h> | ||
| 52 | |||
| 53 | |||
| 54 | /* | ||
| 55 | * Atari interrupt handling scheme: | ||
| 56 | * -------------------------------- | ||
| 57 | * | ||
| 58 | * All interrupt source have an internal number (defined in | ||
| 59 | * <asm/atariints.h>): Autovector interrupts are 1..7, then follow ST-MFP, | ||
| 60 | * TT-MFP, SCC, and finally VME interrupts. Vector numbers for the latter can | ||
| 61 | * be allocated by atari_register_vme_int(). | ||
| 62 | * | ||
| 63 | * Each interrupt can be of three types: | ||
| 64 | * | ||
| 65 | * - SLOW: The handler runs with all interrupts enabled, except the one it | ||
| 66 | * was called by (to avoid reentering). This should be the usual method. | ||
| 67 | * But it is currently possible only for MFP ints, since only the MFP | ||
| 68 | * offers an easy way to mask interrupts. | ||
| 69 | * | ||
| 70 | * - FAST: The handler runs with all interrupts disabled. This should be used | ||
| 71 | * only for really fast handlers, that just do actions immediately | ||
| 72 | * necessary, and let the rest do a bottom half or task queue. | ||
| 73 | * | ||
| 74 | * - PRIORITIZED: The handler can be interrupted by higher-level ints | ||
| 75 | * (greater IPL, no MFP priorities!). This is the method of choice for ints | ||
| 76 | * which should be slow, but are not from a MFP. | ||
| 77 | * | ||
| 78 | * The feature of more than one handler for one int source is still there, but | ||
| 79 | * only applicable if all handers are of the same type. To not slow down | ||
| 80 | * processing of ints with only one handler by the chaining feature, the list | ||
| 81 | * calling function atari_call_irq_list() is only plugged in at the time the | ||
| 82 | * second handler is registered. | ||
| 83 | * | ||
| 84 | * Implementation notes: For fast-as-possible int handling, there are separate | ||
| 85 | * entry points for each type (slow/fast/prio). The assembler handler calls | ||
| 86 | * the irq directly in the usual case, no C wrapper is involved. In case of | ||
| 87 | * multiple handlers, atari_call_irq_list() is registered as handler and calls | ||
| 88 | * in turn the real irq's. To ease access from assembler level to the irq | ||
| 89 | * function pointer and accompanying data, these two are stored in a separate | ||
| 90 | * array, irq_handler[]. The rest of data (type, name) are put into a second | ||
| 91 | * array, irq_param, that is accessed from C only. For each slow interrupt (32 | ||
| 92 | * in all) there are separate handler functions, which makes it possible to | ||
| 93 | * hard-code the MFP register address and value, are necessary to mask the | ||
| 94 | * int. If there'd be only one generic function, lots of calculations would be | ||
| 95 | * needed to determine MFP register and int mask from the vector number :-( | ||
| 96 | * | ||
| 97 | * Furthermore, slow ints may not lower the IPL below its previous value | ||
| 98 | * (before the int happened). This is needed so that an int of class PRIO, on | ||
| 99 | * that this int may be stacked, cannot be reentered. This feature is | ||
| 100 | * implemented as follows: If the stack frame format is 1 (throwaway), the int | ||
| 101 | * is not stacked, and the IPL is anded with 0xfbff, resulting in a new level | ||
| 102 | * 2, which still blocks the HSYNC, but no interrupts of interest. If the | ||
| 103 | * frame format is 0, the int is nested, and the old IPL value can be found in | ||
| 104 | * the sr copy in the frame. | ||
| 105 | */ | ||
| 106 | |||
| 107 | |||
| 108 | #define NUM_INT_SOURCES (8 + NUM_ATARI_SOURCES) | ||
| 109 | |||
| 110 | typedef void (*asm_irq_handler)(void); | ||
| 111 | |||
| 112 | struct irqhandler { | ||
| 113 | irqreturn_t (*handler)(int, void *, struct pt_regs *); | ||
| 114 | void *dev_id; | ||
| 115 | }; | ||
| 116 | |||
| 117 | struct irqparam { | ||
| 118 | unsigned long flags; | ||
| 119 | const char *devname; | ||
| 120 | }; | ||
| 121 | |||
| 122 | /* | ||
| 123 | * Array with irq's and their parameter data. This array is accessed from low | ||
| 124 | * level assembler code, so an element size of 8 allows usage of index scaling | ||
| 125 | * addressing mode. | ||
| 126 | */ | ||
| 127 | static struct irqhandler irq_handler[NUM_INT_SOURCES]; | ||
| 128 | |||
| 129 | /* | ||
| 130 | * This array hold the rest of parameters of int handlers: type | ||
| 131 | * (slow,fast,prio) and the name of the handler. These values are only | ||
| 132 | * accessed from C | ||
| 133 | */ | ||
| 134 | static struct irqparam irq_param[NUM_INT_SOURCES]; | ||
| 135 | |||
| 136 | /* | ||
| 137 | * Bitmap for free interrupt vector numbers | ||
| 138 | * (new vectors starting from 0x70 can be allocated by | ||
| 139 | * atari_register_vme_int()) | ||
| 140 | */ | ||
| 141 | static int free_vme_vec_bitmap; | ||
| 142 | |||
| 143 | /* check for valid int number (complex, sigh...) */ | ||
| 144 | #define IS_VALID_INTNO(n) \ | ||
| 145 | ((n) > 0 && \ | ||
| 146 | /* autovec and ST-MFP ok anyway */ \ | ||
| 147 | (((n) < TTMFP_SOURCE_BASE) || \ | ||
| 148 | /* TT-MFP ok if present */ \ | ||
| 149 | ((n) >= TTMFP_SOURCE_BASE && (n) < SCC_SOURCE_BASE && \ | ||
| 150 | ATARIHW_PRESENT(TT_MFP)) || \ | ||
| 151 | /* SCC ok if present and number even */ \ | ||
| 152 | ((n) >= SCC_SOURCE_BASE && (n) < VME_SOURCE_BASE && \ | ||
| 153 | !((n) & 1) && ATARIHW_PRESENT(SCC)) || \ | ||
| 154 | /* greater numbers ok if they are registered VME vectors */ \ | ||
| 155 | ((n) >= VME_SOURCE_BASE && (n) < VME_SOURCE_BASE + VME_MAX_SOURCES && \ | ||
| 156 | free_vme_vec_bitmap & (1 << ((n) - VME_SOURCE_BASE))))) | ||
| 157 | |||
| 158 | |||
| 159 | /* | ||
| 160 | * Here start the assembler entry points for interrupts | ||
| 161 | */ | ||
| 162 | |||
| 163 | #define IRQ_NAME(nr) atari_slow_irq_##nr##_handler(void) | ||
| 164 | |||
| 165 | #define BUILD_SLOW_IRQ(n) \ | ||
| 166 | asmlinkage void IRQ_NAME(n); \ | ||
| 167 | /* Dummy function to allow asm with operands. */ \ | ||
| 168 | void atari_slow_irq_##n##_dummy (void) { \ | ||
| 169 | __asm__ (__ALIGN_STR "\n" \ | ||
| 170 | "atari_slow_irq_" #n "_handler:\t" \ | ||
| 171 | " addl %6,%5\n" /* preempt_count() += HARDIRQ_OFFSET */ \ | ||
| 172 | SAVE_ALL_INT "\n" \ | ||
| 173 | GET_CURRENT(%%d0) "\n" \ | ||
| 174 | " andb #~(1<<(%c3&7)),%a4:w\n" /* mask this interrupt */ \ | ||
| 175 | /* get old IPL from stack frame */ \ | ||
| 176 | " bfextu %%sp@(%c2){#5,#3},%%d0\n" \ | ||
| 177 | " movew %%sr,%%d1\n" \ | ||
| 178 | " bfins %%d0,%%d1{#21,#3}\n" \ | ||
| 179 | " movew %%d1,%%sr\n" /* set IPL = previous value */ \ | ||
| 180 | " addql #1,%a0\n" \ | ||
| 181 | " lea %a1,%%a0\n" \ | ||
| 182 | " pea %%sp@\n" /* push addr of frame */ \ | ||
| 183 | " movel %%a0@(4),%%sp@-\n" /* push handler data */ \ | ||
| 184 | " pea (%c3+8)\n" /* push int number */ \ | ||
| 185 | " movel %%a0@,%%a0\n" \ | ||
| 186 | " jbsr %%a0@\n" /* call the handler */ \ | ||
| 187 | " addql #8,%%sp\n" \ | ||
| 188 | " addql #4,%%sp\n" \ | ||
| 189 | " orw #0x0600,%%sr\n" \ | ||
| 190 | " andw #0xfeff,%%sr\n" /* set IPL = 6 again */ \ | ||
| 191 | " orb #(1<<(%c3&7)),%a4:w\n" /* now unmask the int again */ \ | ||
| 192 | " jbra ret_from_interrupt\n" \ | ||
| 193 | : : "i" (&kstat_cpu(0).irqs[n+8]), "i" (&irq_handler[n+8]), \ | ||
| 194 | "n" (PT_OFF_SR), "n" (n), \ | ||
| 195 | "i" (n & 8 ? (n & 16 ? &tt_mfp.int_mk_a : &mfp.int_mk_a) \ | ||
| 196 | : (n & 16 ? &tt_mfp.int_mk_b : &mfp.int_mk_b)), \ | ||
| 197 | "m" (preempt_count()), "di" (HARDIRQ_OFFSET) \ | ||
| 198 | ); \ | ||
| 199 | for (;;); /* fake noreturn */ \ | ||
| 200 | } | ||
| 201 | |||
| 202 | BUILD_SLOW_IRQ(0); | ||
| 203 | BUILD_SLOW_IRQ(1); | ||
| 204 | BUILD_SLOW_IRQ(2); | ||
| 205 | BUILD_SLOW_IRQ(3); | ||
| 206 | BUILD_SLOW_IRQ(4); | ||
| 207 | BUILD_SLOW_IRQ(5); | ||
| 208 | BUILD_SLOW_IRQ(6); | ||
| 209 | BUILD_SLOW_IRQ(7); | ||
| 210 | BUILD_SLOW_IRQ(8); | ||
| 211 | BUILD_SLOW_IRQ(9); | ||
| 212 | BUILD_SLOW_IRQ(10); | ||
| 213 | BUILD_SLOW_IRQ(11); | ||
| 214 | BUILD_SLOW_IRQ(12); | ||
| 215 | BUILD_SLOW_IRQ(13); | ||
| 216 | BUILD_SLOW_IRQ(14); | ||
| 217 | BUILD_SLOW_IRQ(15); | ||
| 218 | BUILD_SLOW_IRQ(16); | ||
| 219 | BUILD_SLOW_IRQ(17); | ||
| 220 | BUILD_SLOW_IRQ(18); | ||
| 221 | BUILD_SLOW_IRQ(19); | ||
| 222 | BUILD_SLOW_IRQ(20); | ||
| 223 | BUILD_SLOW_IRQ(21); | ||
| 224 | BUILD_SLOW_IRQ(22); | ||
| 225 | BUILD_SLOW_IRQ(23); | ||
| 226 | BUILD_SLOW_IRQ(24); | ||
| 227 | BUILD_SLOW_IRQ(25); | ||
| 228 | BUILD_SLOW_IRQ(26); | ||
| 229 | BUILD_SLOW_IRQ(27); | ||
| 230 | BUILD_SLOW_IRQ(28); | ||
| 231 | BUILD_SLOW_IRQ(29); | ||
| 232 | BUILD_SLOW_IRQ(30); | ||
| 233 | BUILD_SLOW_IRQ(31); | ||
| 234 | |||
| 235 | asm_irq_handler slow_handlers[32] = { | ||
| 236 | [0] = atari_slow_irq_0_handler, | ||
| 237 | [1] = atari_slow_irq_1_handler, | ||
| 238 | [2] = atari_slow_irq_2_handler, | ||
| 239 | [3] = atari_slow_irq_3_handler, | ||
| 240 | [4] = atari_slow_irq_4_handler, | ||
| 241 | [5] = atari_slow_irq_5_handler, | ||
| 242 | [6] = atari_slow_irq_6_handler, | ||
| 243 | [7] = atari_slow_irq_7_handler, | ||
| 244 | [8] = atari_slow_irq_8_handler, | ||
| 245 | [9] = atari_slow_irq_9_handler, | ||
| 246 | [10] = atari_slow_irq_10_handler, | ||
| 247 | [11] = atari_slow_irq_11_handler, | ||
| 248 | [12] = atari_slow_irq_12_handler, | ||
| 249 | [13] = atari_slow_irq_13_handler, | ||
| 250 | [14] = atari_slow_irq_14_handler, | ||
| 251 | [15] = atari_slow_irq_15_handler, | ||
| 252 | [16] = atari_slow_irq_16_handler, | ||
| 253 | [17] = atari_slow_irq_17_handler, | ||
| 254 | [18] = atari_slow_irq_18_handler, | ||
| 255 | [19] = atari_slow_irq_19_handler, | ||
| 256 | [20] = atari_slow_irq_20_handler, | ||
| 257 | [21] = atari_slow_irq_21_handler, | ||
| 258 | [22] = atari_slow_irq_22_handler, | ||
| 259 | [23] = atari_slow_irq_23_handler, | ||
| 260 | [24] = atari_slow_irq_24_handler, | ||
| 261 | [25] = atari_slow_irq_25_handler, | ||
| 262 | [26] = atari_slow_irq_26_handler, | ||
| 263 | [27] = atari_slow_irq_27_handler, | ||
| 264 | [28] = atari_slow_irq_28_handler, | ||
| 265 | [29] = atari_slow_irq_29_handler, | ||
| 266 | [30] = atari_slow_irq_30_handler, | ||
| 267 | [31] = atari_slow_irq_31_handler | ||
| 268 | }; | ||
| 269 | |||
| 270 | asmlinkage void atari_fast_irq_handler( void ); | ||
| 271 | asmlinkage void atari_prio_irq_handler( void ); | ||
| 272 | |||
| 273 | /* Dummy function to allow asm with operands. */ | ||
| 274 | void atari_fast_prio_irq_dummy (void) { | ||
| 275 | __asm__ (__ALIGN_STR "\n" | ||
| 276 | "atari_fast_irq_handler:\n\t" | ||
| 277 | "orw #0x700,%%sr\n" /* disable all interrupts */ | ||
| 278 | "atari_prio_irq_handler:\n\t" | ||
| 279 | "addl %3,%2\n\t" /* preempt_count() += HARDIRQ_OFFSET */ | ||
| 280 | SAVE_ALL_INT "\n\t" | ||
| 281 | GET_CURRENT(%%d0) "\n\t" | ||
| 282 | /* get vector number from stack frame and convert to source */ | ||
| 283 | "bfextu %%sp@(%c1){#4,#10},%%d0\n\t" | ||
| 284 | "subw #(0x40-8),%%d0\n\t" | ||
| 285 | "jpl 1f\n\t" | ||
| 286 | "addw #(0x40-8-0x18),%%d0\n" | ||
| 287 | "1:\tlea %a0,%%a0\n\t" | ||
| 288 | "addql #1,%%a0@(%%d0:l:4)\n\t" | ||
| 289 | "lea irq_handler,%%a0\n\t" | ||
| 290 | "lea %%a0@(%%d0:l:8),%%a0\n\t" | ||
| 291 | "pea %%sp@\n\t" /* push frame address */ | ||
| 292 | "movel %%a0@(4),%%sp@-\n\t" /* push handler data */ | ||
| 293 | "movel %%d0,%%sp@-\n\t" /* push int number */ | ||
| 294 | "movel %%a0@,%%a0\n\t" | ||
| 295 | "jsr %%a0@\n\t" /* and call the handler */ | ||
| 296 | "addql #8,%%sp\n\t" | ||
| 297 | "addql #4,%%sp\n\t" | ||
| 298 | "jbra ret_from_interrupt" | ||
| 299 | : : "i" (&kstat_cpu(0).irqs), "n" (PT_OFF_FORMATVEC), | ||
| 300 | "m" (preempt_count()), "di" (HARDIRQ_OFFSET) | ||
| 301 | ); | ||
| 302 | for (;;); | ||
| 303 | } | ||
| 304 | |||
| 305 | /* GK: | ||
| 306 | * HBL IRQ handler for Falcon. Nobody needs it :-) | ||
| 307 | * ++andreas: raise ipl to disable further HBLANK interrupts. | ||
| 308 | */ | ||
| 309 | asmlinkage void falcon_hblhandler(void); | ||
| 310 | asm(".text\n" | ||
| 311 | __ALIGN_STR "\n\t" | ||
| 312 | "falcon_hblhandler:\n\t" | ||
| 313 | "orw #0x200,%sp@\n\t" /* set saved ipl to 2 */ | ||
| 314 | "rte"); | ||
| 315 | |||
| 316 | /* Defined in entry.S; only increments 'num_spurious' */ | ||
| 317 | asmlinkage void bad_interrupt(void); | ||
| 318 | |||
| 319 | extern void atari_microwire_cmd( int cmd ); | ||
| 320 | |||
| 321 | extern int atari_SCC_reset_done; | ||
| 322 | |||
| 323 | /* | ||
| 324 | * void atari_init_IRQ (void) | ||
| 325 | * | ||
| 326 | * Parameters: None | ||
| 327 | * | ||
| 328 | * Returns: Nothing | ||
| 329 | * | ||
| 330 | * This function should be called during kernel startup to initialize | ||
| 331 | * the atari IRQ handling routines. | ||
| 332 | */ | ||
| 333 | |||
| 334 | void __init atari_init_IRQ(void) | ||
| 335 | { | ||
| 336 | int i; | ||
| 337 | |||
| 338 | /* initialize the vector table */ | ||
| 339 | for (i = 0; i < NUM_INT_SOURCES; ++i) { | ||
| 340 | vectors[IRQ_SOURCE_TO_VECTOR(i)] = bad_interrupt; | ||
| 341 | } | ||
| 342 | |||
| 343 | /* Initialize the MFP(s) */ | ||
| 344 | |||
| 345 | #ifdef ATARI_USE_SOFTWARE_EOI | ||
| 346 | mfp.vec_adr = 0x48; /* Software EOI-Mode */ | ||
| 347 | #else | ||
| 348 | mfp.vec_adr = 0x40; /* Automatic EOI-Mode */ | ||
| 349 | #endif | ||
| 350 | mfp.int_en_a = 0x00; /* turn off MFP-Ints */ | ||
| 351 | mfp.int_en_b = 0x00; | ||
| 352 | mfp.int_mk_a = 0xff; /* no Masking */ | ||
| 353 | mfp.int_mk_b = 0xff; | ||
| 354 | |||
| 355 | if (ATARIHW_PRESENT(TT_MFP)) { | ||
| 356 | #ifdef ATARI_USE_SOFTWARE_EOI | ||
| 357 | tt_mfp.vec_adr = 0x58; /* Software EOI-Mode */ | ||
| 358 | #else | ||
| 359 | tt_mfp.vec_adr = 0x50; /* Automatic EOI-Mode */ | ||
| 360 | #endif | ||
| 361 | tt_mfp.int_en_a = 0x00; /* turn off MFP-Ints */ | ||
| 362 | tt_mfp.int_en_b = 0x00; | ||
| 363 | tt_mfp.int_mk_a = 0xff; /* no Masking */ | ||
| 364 | tt_mfp.int_mk_b = 0xff; | ||
| 365 | } | ||
| 366 | |||
| 367 | if (ATARIHW_PRESENT(SCC) && !atari_SCC_reset_done) { | ||
| 368 | scc.cha_a_ctrl = 9; | ||
| 369 | MFPDELAY(); | ||
| 370 | scc.cha_a_ctrl = (char) 0xc0; /* hardware reset */ | ||
| 371 | } | ||
| 372 | |||
| 373 | if (ATARIHW_PRESENT(SCU)) { | ||
| 374 | /* init the SCU if present */ | ||
| 375 | tt_scu.sys_mask = 0x10; /* enable VBL (for the cursor) and | ||
| 376 | * disable HSYNC interrupts (who | ||
| 377 | * needs them?) MFP and SCC are | ||
| 378 | * enabled in VME mask | ||
| 379 | */ | ||
| 380 | tt_scu.vme_mask = 0x60; /* enable MFP and SCC ints */ | ||
| 381 | } | ||
| 382 | else { | ||
| 383 | /* If no SCU and no Hades, the HSYNC interrupt needs to be | ||
| 384 | * disabled this way. (Else _inthandler in kernel/sys_call.S | ||
| 385 | * gets overruns) | ||
| 386 | */ | ||
| 387 | |||
| 388 | if (!MACH_IS_HADES) | ||
| 389 | vectors[VEC_INT2] = falcon_hblhandler; | ||
| 390 | } | ||
| 391 | |||
| 392 | if (ATARIHW_PRESENT(PCM_8BIT) && ATARIHW_PRESENT(MICROWIRE)) { | ||
| 393 | /* Initialize the LM1992 Sound Controller to enable | ||
| 394 | the PSG sound. This is misplaced here, it should | ||
| 395 | be in an atasound_init(), that doesn't exist yet. */ | ||
| 396 | atari_microwire_cmd(MW_LM1992_PSG_HIGH); | ||
| 397 | } | ||
| 398 | |||
| 399 | stdma_init(); | ||
| 400 | |||
| 401 | /* Initialize the PSG: all sounds off, both ports output */ | ||
| 402 | sound_ym.rd_data_reg_sel = 7; | ||
| 403 | sound_ym.wd_data = 0xff; | ||
| 404 | } | ||
| 405 | |||
| 406 | |||
| 407 | static irqreturn_t atari_call_irq_list( int irq, void *dev_id, struct pt_regs *fp ) | ||
| 408 | { | ||
| 409 | irq_node_t *node; | ||
| 410 | |||
| 411 | for (node = (irq_node_t *)dev_id; node; node = node->next) | ||
| 412 | node->handler(irq, node->dev_id, fp); | ||
| 413 | return IRQ_HANDLED; | ||
| 414 | } | ||
| 415 | |||
| 416 | |||
| 417 | /* | ||
| 418 | * atari_request_irq : add an interrupt service routine for a particular | ||
| 419 | * machine specific interrupt source. | ||
| 420 | * If the addition was successful, it returns 0. | ||
| 421 | */ | ||
| 422 | |||
| 423 | int atari_request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), | ||
| 424 | unsigned long flags, const char *devname, void *dev_id) | ||
| 425 | { | ||
| 426 | int vector; | ||
| 427 | unsigned long oflags = flags; | ||
| 428 | |||
| 429 | /* | ||
| 430 | * The following is a hack to make some PCI card drivers work, | ||
| 431 | * which set the SA_SHIRQ flag. | ||
| 432 | */ | ||
| 433 | |||
| 434 | flags &= ~SA_SHIRQ; | ||
| 435 | |||
| 436 | if (flags == SA_INTERRUPT) { | ||
| 437 | printk ("%s: SA_INTERRUPT changed to IRQ_TYPE_SLOW for %s\n", | ||
| 438 | __FUNCTION__, devname); | ||
| 439 | flags = IRQ_TYPE_SLOW; | ||
| 440 | } | ||
| 441 | if (flags < IRQ_TYPE_SLOW || flags > IRQ_TYPE_PRIO) { | ||
| 442 | printk ("%s: Bad irq type 0x%lx <0x%lx> requested from %s\n", | ||
| 443 | __FUNCTION__, flags, oflags, devname); | ||
| 444 | return -EINVAL; | ||
| 445 | } | ||
| 446 | if (!IS_VALID_INTNO(irq)) { | ||
| 447 | printk ("%s: Unknown irq %d requested from %s\n", | ||
| 448 | __FUNCTION__, irq, devname); | ||
| 449 | return -ENXIO; | ||
| 450 | } | ||
| 451 | vector = IRQ_SOURCE_TO_VECTOR(irq); | ||
| 452 | |||
| 453 | /* | ||
| 454 | * Check type/source combination: slow ints are (currently) | ||
| 455 | * only possible for MFP-interrupts. | ||
| 456 | */ | ||
| 457 | if (flags == IRQ_TYPE_SLOW && | ||
| 458 | (irq < STMFP_SOURCE_BASE || irq >= SCC_SOURCE_BASE)) { | ||
| 459 | printk ("%s: Slow irq requested for non-MFP source %d from %s\n", | ||
| 460 | __FUNCTION__, irq, devname); | ||
| 461 | return -EINVAL; | ||
| 462 | } | ||
| 463 | |||
| 464 | if (vectors[vector] == bad_interrupt) { | ||
| 465 | /* int has no handler yet */ | ||
| 466 | irq_handler[irq].handler = handler; | ||
| 467 | irq_handler[irq].dev_id = dev_id; | ||
| 468 | irq_param[irq].flags = flags; | ||
| 469 | irq_param[irq].devname = devname; | ||
| 470 | vectors[vector] = | ||
| 471 | (flags == IRQ_TYPE_SLOW) ? slow_handlers[irq-STMFP_SOURCE_BASE] : | ||
| 472 | (flags == IRQ_TYPE_FAST) ? atari_fast_irq_handler : | ||
| 473 | atari_prio_irq_handler; | ||
| 474 | /* If MFP int, also enable and umask it */ | ||
| 475 | atari_turnon_irq(irq); | ||
| 476 | atari_enable_irq(irq); | ||
| 477 | |||
| 478 | return 0; | ||
| 479 | } | ||
| 480 | else if (irq_param[irq].flags == flags) { | ||
| 481 | /* old handler is of same type -> handlers can be chained */ | ||
| 482 | irq_node_t *node; | ||
| 483 | unsigned long flags; | ||
| 484 | |||
| 485 | local_irq_save(flags); | ||
| 486 | |||
| 487 | if (irq_handler[irq].handler != atari_call_irq_list) { | ||
| 488 | /* Only one handler yet, make a node for this first one */ | ||
| 489 | if (!(node = new_irq_node())) | ||
| 490 | return -ENOMEM; | ||
| 491 | node->handler = irq_handler[irq].handler; | ||
| 492 | node->dev_id = irq_handler[irq].dev_id; | ||
| 493 | node->devname = irq_param[irq].devname; | ||
| 494 | node->next = NULL; | ||
| 495 | |||
| 496 | irq_handler[irq].handler = atari_call_irq_list; | ||
| 497 | irq_handler[irq].dev_id = node; | ||
| 498 | irq_param[irq].devname = "chained"; | ||
| 499 | } | ||
| 500 | |||
| 501 | if (!(node = new_irq_node())) | ||
| 502 | return -ENOMEM; | ||
| 503 | node->handler = handler; | ||
| 504 | node->dev_id = dev_id; | ||
| 505 | node->devname = devname; | ||
| 506 | /* new handlers are put in front of the queue */ | ||
| 507 | node->next = irq_handler[irq].dev_id; | ||
| 508 | irq_handler[irq].dev_id = node; | ||
| 509 | |||
| 510 | local_irq_restore(flags); | ||
| 511 | return 0; | ||
| 512 | } else { | ||
| 513 | printk ("%s: Irq %d allocated by other type int (call from %s)\n", | ||
| 514 | __FUNCTION__, irq, devname); | ||
| 515 | return -EBUSY; | ||
| 516 | } | ||
| 517 | } | ||
| 518 | |||
| 519 | void atari_free_irq(unsigned int irq, void *dev_id) | ||
| 520 | { | ||
| 521 | unsigned long flags; | ||
| 522 | int vector; | ||
| 523 | irq_node_t **list, *node; | ||
| 524 | |||
| 525 | if (!IS_VALID_INTNO(irq)) { | ||
| 526 | printk("%s: Unknown irq %d\n", __FUNCTION__, irq); | ||
| 527 | return; | ||
| 528 | } | ||
| 529 | |||
| 530 | vector = IRQ_SOURCE_TO_VECTOR(irq); | ||
| 531 | if (vectors[vector] == bad_interrupt) | ||
| 532 | goto not_found; | ||
| 533 | |||
| 534 | local_irq_save(flags); | ||
| 535 | |||
| 536 | if (irq_handler[irq].handler != atari_call_irq_list) { | ||
| 537 | /* It's the only handler for the interrupt */ | ||
| 538 | if (irq_handler[irq].dev_id != dev_id) { | ||
| 539 | local_irq_restore(flags); | ||
| 540 | goto not_found; | ||
| 541 | } | ||
| 542 | irq_handler[irq].handler = NULL; | ||
| 543 | irq_handler[irq].dev_id = NULL; | ||
| 544 | irq_param[irq].devname = NULL; | ||
| 545 | vectors[vector] = bad_interrupt; | ||
| 546 | /* If MFP int, also disable it */ | ||
| 547 | atari_disable_irq(irq); | ||
| 548 | atari_turnoff_irq(irq); | ||
| 549 | |||
| 550 | local_irq_restore(flags); | ||
| 551 | return; | ||
| 552 | } | ||
| 553 | |||
| 554 | /* The interrupt is chained, find the irq on the list */ | ||
| 555 | for(list = (irq_node_t **)&irq_handler[irq].dev_id; *list; list = &(*list)->next) { | ||
| 556 | if ((*list)->dev_id == dev_id) break; | ||
| 557 | } | ||
| 558 | if (!*list) { | ||
| 559 | local_irq_restore(flags); | ||
| 560 | goto not_found; | ||
| 561 | } | ||
| 562 | |||
| 563 | (*list)->handler = NULL; /* Mark it as free for reallocation */ | ||
| 564 | *list = (*list)->next; | ||
| 565 | |||
| 566 | /* If there's now only one handler, unchain the interrupt, i.e. plug in | ||
| 567 | * the handler directly again and omit atari_call_irq_list */ | ||
| 568 | node = (irq_node_t *)irq_handler[irq].dev_id; | ||
| 569 | if (node && !node->next) { | ||
| 570 | irq_handler[irq].handler = node->handler; | ||
| 571 | irq_handler[irq].dev_id = node->dev_id; | ||
| 572 | irq_param[irq].devname = node->devname; | ||
| 573 | node->handler = NULL; /* Mark it as free for reallocation */ | ||
| 574 | } | ||
| 575 | |||
| 576 | local_irq_restore(flags); | ||
| 577 | return; | ||
| 578 | |||
| 579 | not_found: | ||
| 580 | printk("%s: tried to remove invalid irq\n", __FUNCTION__); | ||
| 581 | return; | ||
| 582 | } | ||
| 583 | |||
| 584 | |||
| 585 | /* | ||
| 586 | * atari_register_vme_int() returns the number of a free interrupt vector for | ||
| 587 | * hardware with a programmable int vector (probably a VME board). | ||
| 588 | */ | ||
| 589 | |||
| 590 | unsigned long atari_register_vme_int(void) | ||
| 591 | { | ||
| 592 | int i; | ||
| 593 | |||
| 594 | for(i = 0; i < 32; i++) | ||
| 595 | if((free_vme_vec_bitmap & (1 << i)) == 0) | ||
| 596 | break; | ||
| 597 | |||
| 598 | if(i == 16) | ||
| 599 | return 0; | ||
| 600 | |||
| 601 | free_vme_vec_bitmap |= 1 << i; | ||
| 602 | return (VME_SOURCE_BASE + i); | ||
| 603 | } | ||
| 604 | |||
| 605 | |||
| 606 | void atari_unregister_vme_int(unsigned long irq) | ||
| 607 | { | ||
| 608 | if(irq >= VME_SOURCE_BASE && irq < VME_SOURCE_BASE + VME_MAX_SOURCES) { | ||
| 609 | irq -= VME_SOURCE_BASE; | ||
| 610 | free_vme_vec_bitmap &= ~(1 << irq); | ||
| 611 | } | ||
| 612 | } | ||
| 613 | |||
| 614 | |||
| 615 | int show_atari_interrupts(struct seq_file *p, void *v) | ||
| 616 | { | ||
| 617 | int i; | ||
| 618 | |||
| 619 | for (i = 0; i < NUM_INT_SOURCES; ++i) { | ||
| 620 | if (vectors[IRQ_SOURCE_TO_VECTOR(i)] == bad_interrupt) | ||
| 621 | continue; | ||
| 622 | if (i < STMFP_SOURCE_BASE) | ||
| 623 | seq_printf(p, "auto %2d: %10u ", | ||
| 624 | i, kstat_cpu(0).irqs[i]); | ||
| 625 | else | ||
| 626 | seq_printf(p, "vec $%02x: %10u ", | ||
| 627 | IRQ_SOURCE_TO_VECTOR(i), | ||
| 628 | kstat_cpu(0).irqs[i]); | ||
| 629 | |||
| 630 | if (irq_handler[i].handler != atari_call_irq_list) { | ||
| 631 | seq_printf(p, "%s\n", irq_param[i].devname); | ||
| 632 | } | ||
| 633 | else { | ||
| 634 | irq_node_t *n; | ||
| 635 | for( n = (irq_node_t *)irq_handler[i].dev_id; n; n = n->next ) { | ||
| 636 | seq_printf(p, "%s\n", n->devname); | ||
| 637 | if (n->next) | ||
| 638 | seq_puts(p, " " ); | ||
| 639 | } | ||
| 640 | } | ||
| 641 | } | ||
| 642 | if (num_spurious) | ||
| 643 | seq_printf(p, "spurio.: %10u\n", num_spurious); | ||
| 644 | |||
| 645 | return 0; | ||
| 646 | } | ||
| 647 | |||
| 648 | |||
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 @@ | |||
| 1 | #include <linux/module.h> | ||
| 2 | |||
| 3 | #include <asm/ptrace.h> | ||
| 4 | #include <asm/traps.h> | ||
| 5 | #include <asm/atarihw.h> | ||
| 6 | #include <asm/atariints.h> | ||
| 7 | #include <asm/atarikb.h> | ||
| 8 | #include <asm/atari_joystick.h> | ||
| 9 | #include <asm/atari_stdma.h> | ||
| 10 | #include <asm/atari_stram.h> | ||
| 11 | |||
| 12 | extern void atari_microwire_cmd( int cmd ); | ||
| 13 | extern int atari_MFP_init_done; | ||
| 14 | extern int atari_SCC_init_done; | ||
| 15 | extern int atari_SCC_reset_done; | ||
| 16 | |||
| 17 | EXPORT_SYMBOL(atari_mch_cookie); | ||
| 18 | EXPORT_SYMBOL(atari_mch_type); | ||
| 19 | EXPORT_SYMBOL(atari_hw_present); | ||
| 20 | EXPORT_SYMBOL(atari_switches); | ||
| 21 | EXPORT_SYMBOL(atari_dont_touch_floppy_select); | ||
| 22 | EXPORT_SYMBOL(atari_register_vme_int); | ||
| 23 | EXPORT_SYMBOL(atari_unregister_vme_int); | ||
| 24 | EXPORT_SYMBOL(stdma_lock); | ||
| 25 | EXPORT_SYMBOL(stdma_release); | ||
| 26 | EXPORT_SYMBOL(stdma_others_waiting); | ||
| 27 | EXPORT_SYMBOL(stdma_islocked); | ||
| 28 | EXPORT_SYMBOL(atari_stram_alloc); | ||
| 29 | EXPORT_SYMBOL(atari_stram_free); | ||
| 30 | |||
| 31 | EXPORT_SYMBOL(atari_MFP_init_done); | ||
| 32 | EXPORT_SYMBOL(atari_SCC_init_done); | ||
| 33 | EXPORT_SYMBOL(atari_SCC_reset_done); | ||
| 34 | |||
| 35 | 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 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/m68k/atari/atasound.c | ||
| 3 | * | ||
| 4 | * ++Geert: Moved almost all stuff to linux/drivers/sound/ | ||
| 5 | * | ||
| 6 | * The author of atari_nosound, atari_mksound and atari_microwire_cmd is | ||
| 7 | * unknown. (++roman: That's me... :-) | ||
| 8 | * | ||
| 9 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 10 | * License. See the file COPYING in the main directory of this archive | ||
| 11 | * for more details. | ||
| 12 | * | ||
| 13 | * 1998-05-31 ++andreas: atari_mksound rewritten to always use the envelope, | ||
| 14 | * no timer, atari_nosound removed. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | |||
| 19 | #include <linux/sched.h> | ||
| 20 | #include <linux/timer.h> | ||
| 21 | #include <linux/major.h> | ||
| 22 | #include <linux/fcntl.h> | ||
| 23 | #include <linux/errno.h> | ||
| 24 | #include <linux/mm.h> | ||
| 25 | |||
| 26 | #include <asm/atarihw.h> | ||
| 27 | #include <asm/system.h> | ||
| 28 | #include <asm/irq.h> | ||
| 29 | #include <asm/pgtable.h> | ||
| 30 | #include <asm/atariints.h> | ||
| 31 | |||
| 32 | |||
| 33 | /* | ||
| 34 | * stuff from the old atasound.c | ||
| 35 | */ | ||
| 36 | |||
| 37 | void atari_microwire_cmd (int cmd) | ||
| 38 | { | ||
| 39 | tt_microwire.mask = 0x7ff; | ||
| 40 | tt_microwire.data = MW_LM1992_ADDR | cmd; | ||
| 41 | |||
| 42 | /* Busy wait for data being completely sent :-( */ | ||
| 43 | while( tt_microwire.mask != 0x7ff) | ||
| 44 | ; | ||
| 45 | } | ||
| 46 | |||
| 47 | |||
| 48 | /* PSG base frequency */ | ||
| 49 | #define PSG_FREQ 125000 | ||
| 50 | /* PSG envelope base frequency times 10 */ | ||
| 51 | #define PSG_ENV_FREQ_10 78125 | ||
| 52 | |||
| 53 | void atari_mksound (unsigned int hz, unsigned int ticks) | ||
| 54 | { | ||
| 55 | /* Generates sound of some frequency for some number of clock | ||
| 56 | ticks. */ | ||
| 57 | unsigned long flags; | ||
| 58 | unsigned char tmp; | ||
| 59 | int period; | ||
| 60 | |||
| 61 | local_irq_save(flags); | ||
| 62 | |||
| 63 | |||
| 64 | /* Disable generator A in mixer control. */ | ||
| 65 | sound_ym.rd_data_reg_sel = 7; | ||
| 66 | tmp = sound_ym.rd_data_reg_sel; | ||
| 67 | tmp |= 011; | ||
| 68 | sound_ym.wd_data = tmp; | ||
| 69 | |||
| 70 | if (hz) { | ||
| 71 | /* Convert from frequency value to PSG period value (base | ||
| 72 | frequency 125 kHz). */ | ||
| 73 | |||
| 74 | period = PSG_FREQ / hz; | ||
| 75 | |||
| 76 | if (period > 0xfff) period = 0xfff; | ||
| 77 | |||
| 78 | /* Set generator A frequency to hz. */ | ||
| 79 | sound_ym.rd_data_reg_sel = 0; | ||
| 80 | sound_ym.wd_data = period & 0xff; | ||
| 81 | sound_ym.rd_data_reg_sel = 1; | ||
| 82 | sound_ym.wd_data = (period >> 8) & 0xf; | ||
| 83 | if (ticks) { | ||
| 84 | /* Set length of envelope (max 8 sec). */ | ||
| 85 | int length = (ticks * PSG_ENV_FREQ_10) / HZ / 10; | ||
| 86 | |||
| 87 | if (length > 0xffff) length = 0xffff; | ||
| 88 | sound_ym.rd_data_reg_sel = 11; | ||
| 89 | sound_ym.wd_data = length & 0xff; | ||
| 90 | sound_ym.rd_data_reg_sel = 12; | ||
| 91 | sound_ym.wd_data = length >> 8; | ||
| 92 | /* Envelope form: max -> min single. */ | ||
| 93 | sound_ym.rd_data_reg_sel = 13; | ||
| 94 | sound_ym.wd_data = 0; | ||
| 95 | /* Use envelope for generator A. */ | ||
| 96 | sound_ym.rd_data_reg_sel = 8; | ||
| 97 | sound_ym.wd_data = 0x10; | ||
| 98 | } else { | ||
| 99 | /* Set generator A level to maximum, no envelope. */ | ||
| 100 | sound_ym.rd_data_reg_sel = 8; | ||
| 101 | sound_ym.wd_data = 15; | ||
| 102 | } | ||
| 103 | /* Turn on generator A in mixer control. */ | ||
| 104 | sound_ym.rd_data_reg_sel = 7; | ||
| 105 | tmp &= ~1; | ||
| 106 | sound_ym.wd_data = tmp; | ||
| 107 | } | ||
| 108 | local_irq_restore(flags); | ||
| 109 | } | ||
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 @@ | |||
| 1 | /* | ||
| 2 | * Minor numbers for the sound driver. | ||
| 3 | * | ||
| 4 | * Unfortunately Creative called the codec chip of SB as a DSP. For this | ||
| 5 | * reason the /dev/dsp is reserved for digitized audio use. There is a | ||
| 6 | * device for true DSP processors but it will be called something else. | ||
| 7 | * In v3.0 it's /dev/sndproc but this could be a temporary solution. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #define SND_NDEVS 256 /* Number of supported devices */ | ||
| 11 | #define SND_DEV_CTL 0 /* Control port /dev/mixer */ | ||
| 12 | #define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM | ||
| 13 | synthesizer and MIDI output) */ | ||
| 14 | #define SND_DEV_MIDIN 2 /* Raw midi access */ | ||
| 15 | #define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ | ||
| 16 | #define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ | ||
| 17 | #define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ | ||
| 18 | #define SND_DEV_STATUS 6 /* /dev/sndstat */ | ||
| 19 | /* #7 not in use now. Was in 2.4. Free for use after v3.0. */ | ||
| 20 | #define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */ | ||
| 21 | #define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ | ||
| 22 | #define SND_DEV_PSS SND_DEV_SNDPROC | ||
| 23 | |||
| 24 | #define DSP_DEFAULT_SPEED 8000 | ||
| 25 | |||
| 26 | #define ON 1 | ||
| 27 | #define OFF 0 | ||
| 28 | |||
| 29 | #define MAX_AUDIO_DEV 5 | ||
| 30 | #define MAX_MIXER_DEV 2 | ||
| 31 | #define MAX_SYNTH_DEV 3 | ||
| 32 | #define MAX_MIDI_DEV 6 | ||
| 33 | #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 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/m68k/atari/config.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 1994 Bjoern Brauel | ||
| 5 | * | ||
| 6 | * 5/2/94 Roman Hodek: | ||
| 7 | * Added setting of time_adj to get a better clock. | ||
| 8 | * | ||
| 9 | * 5/14/94 Roman Hodek: | ||
| 10 | * gettod() for TT | ||
| 11 | * | ||
| 12 | * 5/15/94 Roman Hodek: | ||
| 13 | * hard_reset_now() for Atari (and others?) | ||
| 14 | * | ||
| 15 | * 94/12/30 Andreas Schwab: | ||
| 16 | * atari_sched_init fixed to get precise clock. | ||
| 17 | * | ||
| 18 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 19 | * License. See the file COPYING in the main directory of this archive | ||
| 20 | * for more details. | ||
| 21 | */ | ||
| 22 | |||
| 23 | /* | ||
| 24 | * Miscellaneous atari stuff | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <linux/config.h> | ||
| 28 | #include <linux/types.h> | ||
| 29 | #include <linux/mm.h> | ||
| 30 | #include <linux/console.h> | ||
| 31 | #include <linux/init.h> | ||
| 32 | #include <linux/delay.h> | ||
| 33 | #include <linux/ioport.h> | ||
| 34 | #include <linux/vt_kern.h> | ||
| 35 | |||
| 36 | #include <asm/bootinfo.h> | ||
| 37 | #include <asm/setup.h> | ||
| 38 | #include <asm/atarihw.h> | ||
| 39 | #include <asm/atariints.h> | ||
| 40 | #include <asm/atari_stram.h> | ||
| 41 | #include <asm/system.h> | ||
| 42 | #include <asm/machdep.h> | ||
| 43 | #include <asm/hwtest.h> | ||
| 44 | #include <asm/io.h> | ||
| 45 | |||
| 46 | u_long atari_mch_cookie; | ||
| 47 | u_long atari_mch_type; | ||
| 48 | struct atari_hw_present atari_hw_present; | ||
| 49 | u_long atari_switches; | ||
| 50 | int atari_dont_touch_floppy_select; | ||
| 51 | int atari_rtc_year_offset; | ||
| 52 | |||
| 53 | /* local function prototypes */ | ||
| 54 | static void atari_reset( void ); | ||
| 55 | #ifdef CONFIG_ATARI_FLOPPY | ||
| 56 | extern void atari_floppy_setup(char *, int *); | ||
| 57 | #endif | ||
| 58 | static void atari_get_model(char *model); | ||
| 59 | static int atari_get_hardware_list(char *buffer); | ||
| 60 | |||
| 61 | /* atari specific irq functions */ | ||
| 62 | extern void atari_init_IRQ (void); | ||
| 63 | extern int atari_request_irq (unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), | ||
| 64 | unsigned long flags, const char *devname, void *dev_id); | ||
| 65 | extern void atari_free_irq (unsigned int irq, void *dev_id); | ||
| 66 | extern void atari_enable_irq (unsigned int); | ||
| 67 | extern void atari_disable_irq (unsigned int); | ||
| 68 | extern int show_atari_interrupts (struct seq_file *, void *); | ||
| 69 | extern void atari_mksound( unsigned int count, unsigned int ticks ); | ||
| 70 | #ifdef CONFIG_HEARTBEAT | ||
| 71 | static void atari_heartbeat( int on ); | ||
| 72 | #endif | ||
| 73 | |||
| 74 | /* atari specific timer functions (in time.c) */ | ||
| 75 | extern void atari_sched_init(irqreturn_t (*)(int, void *, struct pt_regs *)); | ||
| 76 | extern unsigned long atari_gettimeoffset (void); | ||
| 77 | extern int atari_mste_hwclk (int, struct rtc_time *); | ||
| 78 | extern int atari_tt_hwclk (int, struct rtc_time *); | ||
| 79 | extern int atari_mste_set_clock_mmss (unsigned long); | ||
| 80 | extern int atari_tt_set_clock_mmss (unsigned long); | ||
| 81 | |||
| 82 | /* atari specific debug functions (in debug.c) */ | ||
| 83 | extern void atari_debug_init(void); | ||
| 84 | |||
| 85 | |||
| 86 | /* I've moved hwreg_present() and hwreg_present_bywrite() out into | ||
| 87 | * mm/hwtest.c, to avoid having multiple copies of the same routine | ||
| 88 | * in the kernel [I wanted them in hp300 and they were already used | ||
| 89 | * in the nubus code. NB: I don't have an Atari so this might (just | ||
| 90 | * conceivably) break something. | ||
| 91 | * I've preserved the #if 0 version of hwreg_present_bywrite() here | ||
| 92 | * for posterity. | ||
| 93 | * -- Peter Maydell <pmaydell@chiark.greenend.org.uk>, 05/1998 | ||
| 94 | */ | ||
| 95 | |||
| 96 | #if 0 | ||
| 97 | static int __init | ||
| 98 | hwreg_present_bywrite(volatile void *regp, unsigned char val) | ||
| 99 | { | ||
| 100 | int ret; | ||
| 101 | long save_sp, save_vbr; | ||
| 102 | static long tmp_vectors[3] = { [2] = (long)&&after_test }; | ||
| 103 | |||
| 104 | __asm__ __volatile__ | ||
| 105 | ( "movec %/vbr,%2\n\t" /* save vbr value */ | ||
| 106 | "movec %4,%/vbr\n\t" /* set up temporary vectors */ | ||
| 107 | "movel %/sp,%1\n\t" /* save sp */ | ||
| 108 | "moveq #0,%0\n\t" /* assume not present */ | ||
| 109 | "moveb %5,%3@\n\t" /* write the hardware reg */ | ||
| 110 | "cmpb %3@,%5\n\t" /* compare it */ | ||
| 111 | "seq %0" /* comes here only if reg */ | ||
| 112 | /* is present */ | ||
| 113 | : "=d&" (ret), "=r&" (save_sp), "=r&" (save_vbr) | ||
| 114 | : "a" (regp), "r" (tmp_vectors), "d" (val) | ||
| 115 | ); | ||
| 116 | after_test: | ||
| 117 | __asm__ __volatile__ | ||
| 118 | ( "movel %0,%/sp\n\t" /* restore sp */ | ||
| 119 | "movec %1,%/vbr" /* restore vbr */ | ||
| 120 | : : "r" (save_sp), "r" (save_vbr) : "sp" | ||
| 121 | ); | ||
| 122 | |||
| 123 | return( ret ); | ||
| 124 | } | ||
| 125 | #endif | ||
| 126 | |||
| 127 | |||
| 128 | /* ++roman: This is a more elaborate test for an SCC chip, since the plain | ||
| 129 | * Medusa board generates DTACK at the SCC's standard addresses, but a SCC | ||
| 130 | * board in the Medusa is possible. Also, the addresses where the ST_ESCC | ||
| 131 | * resides generate DTACK without the chip, too. | ||
| 132 | * The method is to write values into the interrupt vector register, that | ||
| 133 | * should be readable without trouble (from channel A!). | ||
| 134 | */ | ||
| 135 | |||
| 136 | static int __init scc_test( volatile char *ctla ) | ||
| 137 | { | ||
| 138 | if (!hwreg_present( ctla )) | ||
| 139 | return( 0 ); | ||
| 140 | MFPDELAY(); | ||
| 141 | |||
| 142 | *ctla = 2; MFPDELAY(); | ||
| 143 | *ctla = 0x40; MFPDELAY(); | ||
| 144 | |||
| 145 | *ctla = 2; MFPDELAY(); | ||
| 146 | if (*ctla != 0x40) return( 0 ); | ||
| 147 | MFPDELAY(); | ||
| 148 | |||
| 149 | *ctla = 2; MFPDELAY(); | ||
| 150 | *ctla = 0x60; MFPDELAY(); | ||
| 151 | |||
| 152 | *ctla = 2; MFPDELAY(); | ||
| 153 | if (*ctla != 0x60) return( 0 ); | ||
| 154 | |||
| 155 | return( 1 ); | ||
| 156 | } | ||
| 157 | |||
| 158 | |||
| 159 | /* | ||
| 160 | * Parse an Atari-specific record in the bootinfo | ||
| 161 | */ | ||
| 162 | |||
| 163 | int __init atari_parse_bootinfo(const struct bi_record *record) | ||
| 164 | { | ||
| 165 | int unknown = 0; | ||
| 166 | const u_long *data = record->data; | ||
| 167 | |||
| 168 | switch (record->tag) { | ||
| 169 | case BI_ATARI_MCH_COOKIE: | ||
| 170 | atari_mch_cookie = *data; | ||
| 171 | break; | ||
| 172 | case BI_ATARI_MCH_TYPE: | ||
| 173 | atari_mch_type = *data; | ||
| 174 | break; | ||
| 175 | default: | ||
| 176 | unknown = 1; | ||
| 177 | } | ||
| 178 | return(unknown); | ||
| 179 | } | ||
| 180 | |||
| 181 | |||
| 182 | /* Parse the Atari-specific switches= option. */ | ||
| 183 | void __init atari_switches_setup( const char *str, unsigned len ) | ||
| 184 | { | ||
| 185 | char switches[len+1]; | ||
| 186 | char *p; | ||
| 187 | int ovsc_shift; | ||
| 188 | char *args = switches; | ||
| 189 | |||
| 190 | /* copy string to local array, strsep works destructively... */ | ||
| 191 | strlcpy( switches, str, sizeof(switches) ); | ||
| 192 | atari_switches = 0; | ||
| 193 | |||
| 194 | /* parse the options */ | ||
| 195 | while ((p = strsep(&args, ",")) != NULL) { | ||
| 196 | if (!*p) continue; | ||
| 197 | ovsc_shift = 0; | ||
| 198 | if (strncmp( p, "ov_", 3 ) == 0) { | ||
| 199 | p += 3; | ||
| 200 | ovsc_shift = ATARI_SWITCH_OVSC_SHIFT; | ||
| 201 | } | ||
| 202 | |||
| 203 | if (strcmp( p, "ikbd" ) == 0) { | ||
| 204 | /* RTS line of IKBD ACIA */ | ||
| 205 | atari_switches |= ATARI_SWITCH_IKBD << ovsc_shift; | ||
| 206 | } | ||
| 207 | else if (strcmp( p, "midi" ) == 0) { | ||
| 208 | /* RTS line of MIDI ACIA */ | ||
| 209 | atari_switches |= ATARI_SWITCH_MIDI << ovsc_shift; | ||
| 210 | } | ||
| 211 | else if (strcmp( p, "snd6" ) == 0) { | ||
| 212 | atari_switches |= ATARI_SWITCH_SND6 << ovsc_shift; | ||
| 213 | } | ||
| 214 | else if (strcmp( p, "snd7" ) == 0) { | ||
| 215 | atari_switches |= ATARI_SWITCH_SND7 << ovsc_shift; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | |||
| 221 | /* | ||
| 222 | * Setup the Atari configuration info | ||
| 223 | */ | ||
| 224 | |||
| 225 | void __init config_atari(void) | ||
| 226 | { | ||
| 227 | unsigned short tos_version; | ||
| 228 | |||
| 229 | memset(&atari_hw_present, 0, sizeof(atari_hw_present)); | ||
| 230 | |||
| 231 | atari_debug_init(); | ||
| 232 | |||
| 233 | ioport_resource.end = 0xFFFFFFFF; /* Change size of I/O space from 64KB | ||
| 234 | to 4GB. */ | ||
| 235 | |||
| 236 | mach_sched_init = atari_sched_init; | ||
| 237 | mach_init_IRQ = atari_init_IRQ; | ||
| 238 | mach_request_irq = atari_request_irq; | ||
| 239 | mach_free_irq = atari_free_irq; | ||
| 240 | enable_irq = atari_enable_irq; | ||
| 241 | disable_irq = atari_disable_irq; | ||
| 242 | mach_get_model = atari_get_model; | ||
| 243 | mach_get_hardware_list = atari_get_hardware_list; | ||
| 244 | mach_get_irq_list = show_atari_interrupts; | ||
| 245 | mach_gettimeoffset = atari_gettimeoffset; | ||
| 246 | mach_reset = atari_reset; | ||
| 247 | #ifdef CONFIG_ATARI_FLOPPY | ||
| 248 | mach_floppy_setup = atari_floppy_setup; | ||
| 249 | #endif | ||
| 250 | #ifdef CONFIG_DUMMY_CONSOLE | ||
| 251 | conswitchp = &dummy_con; | ||
| 252 | #endif | ||
| 253 | mach_max_dma_address = 0xffffff; | ||
| 254 | #if defined(CONFIG_INPUT_M68K_BEEP) || defined(CONFIG_INPUT_M68K_BEEP_MODULE) | ||
| 255 | mach_beep = atari_mksound; | ||
| 256 | #endif | ||
| 257 | #ifdef CONFIG_HEARTBEAT | ||
| 258 | mach_heartbeat = atari_heartbeat; | ||
| 259 | #endif | ||
| 260 | |||
| 261 | /* Set switches as requested by the user */ | ||
| 262 | if (atari_switches & ATARI_SWITCH_IKBD) | ||
| 263 | acia.key_ctrl = ACIA_DIV64 | ACIA_D8N1S | ACIA_RHTID; | ||
| 264 | if (atari_switches & ATARI_SWITCH_MIDI) | ||
| 265 | acia.mid_ctrl = ACIA_DIV16 | ACIA_D8N1S | ACIA_RHTID; | ||
| 266 | if (atari_switches & (ATARI_SWITCH_SND6|ATARI_SWITCH_SND7)) { | ||
| 267 | sound_ym.rd_data_reg_sel = 14; | ||
| 268 | sound_ym.wd_data = sound_ym.rd_data_reg_sel | | ||
| 269 | ((atari_switches&ATARI_SWITCH_SND6) ? 0x40 : 0) | | ||
| 270 | ((atari_switches&ATARI_SWITCH_SND7) ? 0x80 : 0); | ||
| 271 | } | ||
| 272 | |||
| 273 | /* ++bjoern: | ||
| 274 | * Determine hardware present | ||
| 275 | */ | ||
| 276 | |||
| 277 | printk( "Atari hardware found: " ); | ||
| 278 | if (MACH_IS_MEDUSA || MACH_IS_HADES) { | ||
| 279 | /* There's no Atari video hardware on the Medusa, but all the | ||
| 280 | * addresses below generate a DTACK so no bus error occurs! */ | ||
| 281 | } | ||
| 282 | else if (hwreg_present( f030_xreg )) { | ||
| 283 | ATARIHW_SET(VIDEL_SHIFTER); | ||
| 284 | printk( "VIDEL " ); | ||
| 285 | /* This is a temporary hack: If there is Falcon video | ||
| 286 | * hardware, we assume that the ST-DMA serves SCSI instead of | ||
| 287 | * ACSI. In the future, there should be a better method for | ||
| 288 | * this... | ||
| 289 | */ | ||
| 290 | ATARIHW_SET(ST_SCSI); | ||
| 291 | printk( "STDMA-SCSI " ); | ||
| 292 | } | ||
| 293 | else if (hwreg_present( tt_palette )) { | ||
| 294 | ATARIHW_SET(TT_SHIFTER); | ||
| 295 | printk( "TT_SHIFTER " ); | ||
| 296 | } | ||
| 297 | else if (hwreg_present( &shifter.bas_hi )) { | ||
| 298 | if (hwreg_present( &shifter.bas_lo ) && | ||
| 299 | (shifter.bas_lo = 0x0aau, shifter.bas_lo == 0x0aau)) { | ||
| 300 | ATARIHW_SET(EXTD_SHIFTER); | ||
| 301 | printk( "EXTD_SHIFTER " ); | ||
| 302 | } | ||
| 303 | else { | ||
| 304 | ATARIHW_SET(STND_SHIFTER); | ||
| 305 | printk( "STND_SHIFTER " ); | ||
| 306 | } | ||
| 307 | } | ||
| 308 | if (hwreg_present( &mfp.par_dt_reg )) { | ||
| 309 | ATARIHW_SET(ST_MFP); | ||
| 310 | printk( "ST_MFP " ); | ||
| 311 | } | ||
| 312 | if (hwreg_present( &tt_mfp.par_dt_reg )) { | ||
| 313 | ATARIHW_SET(TT_MFP); | ||
| 314 | printk( "TT_MFP " ); | ||
| 315 | } | ||
| 316 | if (hwreg_present( &tt_scsi_dma.dma_addr_hi )) { | ||
| 317 | ATARIHW_SET(SCSI_DMA); | ||
| 318 | printk( "TT_SCSI_DMA " ); | ||
| 319 | } | ||
| 320 | if (!MACH_IS_HADES && hwreg_present( &st_dma.dma_hi )) { | ||
| 321 | ATARIHW_SET(STND_DMA); | ||
| 322 | printk( "STND_DMA " ); | ||
| 323 | } | ||
| 324 | if (MACH_IS_MEDUSA || /* The ST-DMA address registers aren't readable | ||
| 325 | * on all Medusas, so the test below may fail */ | ||
| 326 | (hwreg_present( &st_dma.dma_vhi ) && | ||
| 327 | (st_dma.dma_vhi = 0x55) && (st_dma.dma_hi = 0xaa) && | ||
| 328 | st_dma.dma_vhi == 0x55 && st_dma.dma_hi == 0xaa && | ||
| 329 | (st_dma.dma_vhi = 0xaa) && (st_dma.dma_hi = 0x55) && | ||
| 330 | st_dma.dma_vhi == 0xaa && st_dma.dma_hi == 0x55)) { | ||
| 331 | ATARIHW_SET(EXTD_DMA); | ||
| 332 | printk( "EXTD_DMA " ); | ||
| 333 | } | ||
| 334 | if (hwreg_present( &tt_scsi.scsi_data )) { | ||
| 335 | ATARIHW_SET(TT_SCSI); | ||
| 336 | printk( "TT_SCSI " ); | ||
| 337 | } | ||
| 338 | if (hwreg_present( &sound_ym.rd_data_reg_sel )) { | ||
| 339 | ATARIHW_SET(YM_2149); | ||
| 340 | printk( "YM2149 " ); | ||
| 341 | } | ||
| 342 | if (!MACH_IS_MEDUSA && !MACH_IS_HADES && | ||
| 343 | hwreg_present( &tt_dmasnd.ctrl )) { | ||
| 344 | ATARIHW_SET(PCM_8BIT); | ||
| 345 | printk( "PCM " ); | ||
| 346 | } | ||
| 347 | if (!MACH_IS_HADES && hwreg_present( &falcon_codec.unused5 )) { | ||
| 348 | ATARIHW_SET(CODEC); | ||
| 349 | printk( "CODEC " ); | ||
| 350 | } | ||
| 351 | if (hwreg_present( &dsp56k_host_interface.icr )) { | ||
| 352 | ATARIHW_SET(DSP56K); | ||
| 353 | printk( "DSP56K " ); | ||
| 354 | } | ||
| 355 | if (hwreg_present( &tt_scc_dma.dma_ctrl ) && | ||
| 356 | #if 0 | ||
| 357 | /* This test sucks! Who knows some better? */ | ||
| 358 | (tt_scc_dma.dma_ctrl = 0x01, (tt_scc_dma.dma_ctrl & 1) == 1) && | ||
| 359 | (tt_scc_dma.dma_ctrl = 0x00, (tt_scc_dma.dma_ctrl & 1) == 0) | ||
| 360 | #else | ||
| 361 | !MACH_IS_MEDUSA && !MACH_IS_HADES | ||
| 362 | #endif | ||
| 363 | ) { | ||
| 364 | ATARIHW_SET(SCC_DMA); | ||
| 365 | printk( "SCC_DMA " ); | ||
| 366 | } | ||
| 367 | if (scc_test( &scc.cha_a_ctrl )) { | ||
| 368 | ATARIHW_SET(SCC); | ||
| 369 | printk( "SCC " ); | ||
| 370 | } | ||
| 371 | if (scc_test( &st_escc.cha_b_ctrl )) { | ||
| 372 | ATARIHW_SET( ST_ESCC ); | ||
| 373 | printk( "ST_ESCC " ); | ||
| 374 | } | ||
| 375 | if (MACH_IS_HADES) | ||
| 376 | { | ||
| 377 | ATARIHW_SET( VME ); | ||
| 378 | printk( "VME " ); | ||
| 379 | } | ||
| 380 | else if (hwreg_present( &tt_scu.sys_mask )) { | ||
| 381 | ATARIHW_SET(SCU); | ||
| 382 | /* Assume a VME bus if there's a SCU */ | ||
| 383 | ATARIHW_SET( VME ); | ||
| 384 | printk( "VME SCU " ); | ||
| 385 | } | ||
| 386 | if (hwreg_present( (void *)(0xffff9210) )) { | ||
| 387 | ATARIHW_SET(ANALOG_JOY); | ||
| 388 | printk( "ANALOG_JOY " ); | ||
| 389 | } | ||
| 390 | if (!MACH_IS_HADES && hwreg_present( blitter.halftone )) { | ||
| 391 | ATARIHW_SET(BLITTER); | ||
| 392 | printk( "BLITTER " ); | ||
| 393 | } | ||
| 394 | if (hwreg_present((void *)0xfff00039)) { | ||
| 395 | ATARIHW_SET(IDE); | ||
| 396 | printk( "IDE " ); | ||
| 397 | } | ||
| 398 | #if 1 /* This maybe wrong */ | ||
| 399 | if (!MACH_IS_MEDUSA && !MACH_IS_HADES && | ||
| 400 | hwreg_present( &tt_microwire.data ) && | ||
| 401 | hwreg_present( &tt_microwire.mask ) && | ||
| 402 | (tt_microwire.mask = 0x7ff, | ||
| 403 | udelay(1), | ||
| 404 | tt_microwire.data = MW_LM1992_PSG_HIGH | MW_LM1992_ADDR, | ||
| 405 | udelay(1), | ||
| 406 | tt_microwire.data != 0)) { | ||
| 407 | ATARIHW_SET(MICROWIRE); | ||
| 408 | while (tt_microwire.mask != 0x7ff) ; | ||
| 409 | printk( "MICROWIRE " ); | ||
| 410 | } | ||
| 411 | #endif | ||
| 412 | if (hwreg_present( &tt_rtc.regsel )) { | ||
| 413 | ATARIHW_SET(TT_CLK); | ||
| 414 | printk( "TT_CLK " ); | ||
| 415 | mach_hwclk = atari_tt_hwclk; | ||
| 416 | mach_set_clock_mmss = atari_tt_set_clock_mmss; | ||
| 417 | } | ||
| 418 | if (!MACH_IS_HADES && hwreg_present( &mste_rtc.sec_ones)) { | ||
| 419 | ATARIHW_SET(MSTE_CLK); | ||
| 420 | printk( "MSTE_CLK "); | ||
| 421 | mach_hwclk = atari_mste_hwclk; | ||
| 422 | mach_set_clock_mmss = atari_mste_set_clock_mmss; | ||
| 423 | } | ||
| 424 | if (!MACH_IS_MEDUSA && !MACH_IS_HADES && | ||
| 425 | hwreg_present( &dma_wd.fdc_speed ) && | ||
| 426 | hwreg_write( &dma_wd.fdc_speed, 0 )) { | ||
| 427 | ATARIHW_SET(FDCSPEED); | ||
| 428 | printk( "FDC_SPEED "); | ||
| 429 | } | ||
| 430 | if (!MACH_IS_HADES && !ATARIHW_PRESENT(ST_SCSI)) { | ||
| 431 | ATARIHW_SET(ACSI); | ||
| 432 | printk( "ACSI " ); | ||
| 433 | } | ||
| 434 | printk("\n"); | ||
| 435 | |||
| 436 | if (CPU_IS_040_OR_060) | ||
| 437 | /* Now it seems to be safe to turn of the tt0 transparent | ||
| 438 | * translation (the one that must not be turned off in | ||
| 439 | * head.S...) | ||
| 440 | */ | ||
| 441 | __asm__ volatile ("moveq #0,%/d0\n\t" | ||
| 442 | ".chip 68040\n\t" | ||
| 443 | "movec %%d0,%%itt0\n\t" | ||
| 444 | "movec %%d0,%%dtt0\n\t" | ||
| 445 | ".chip 68k" | ||
| 446 | : /* no outputs */ | ||
| 447 | : /* no inputs */ | ||
| 448 | : "d0"); | ||
| 449 | |||
| 450 | /* allocator for memory that must reside in st-ram */ | ||
| 451 | atari_stram_init (); | ||
| 452 | |||
| 453 | /* Set up a mapping for the VMEbus address region: | ||
| 454 | * | ||
| 455 | * VME is either at phys. 0xfexxxxxx (TT) or 0xa00000..0xdfffff | ||
| 456 | * (MegaSTE) In both cases, the whole 16 MB chunk is mapped at | ||
| 457 | * 0xfe000000 virt., because this can be done with a single | ||
| 458 | * transparent translation. On the 68040, lots of often unused | ||
| 459 | * page tables would be needed otherwise. On a MegaSTE or similar, | ||
| 460 | * the highest byte is stripped off by hardware due to the 24 bit | ||
| 461 | * design of the bus. | ||
| 462 | */ | ||
| 463 | |||
| 464 | if (CPU_IS_020_OR_030) { | ||
| 465 | unsigned long tt1_val; | ||
| 466 | tt1_val = 0xfe008543; /* Translate 0xfexxxxxx, enable, cache | ||
| 467 | * inhibit, read and write, FDC mask = 3, | ||
| 468 | * FDC val = 4 -> Supervisor only */ | ||
| 469 | __asm__ __volatile__ ( ".chip 68030\n\t" | ||
| 470 | "pmove %0@,%/tt1\n\t" | ||
| 471 | ".chip 68k" | ||
| 472 | : : "a" (&tt1_val) ); | ||
| 473 | } | ||
| 474 | else { | ||
| 475 | __asm__ __volatile__ | ||
| 476 | ( "movel %0,%/d0\n\t" | ||
| 477 | ".chip 68040\n\t" | ||
| 478 | "movec %%d0,%%itt1\n\t" | ||
| 479 | "movec %%d0,%%dtt1\n\t" | ||
| 480 | ".chip 68k" | ||
| 481 | : | ||
| 482 | : "g" (0xfe00a040) /* Translate 0xfexxxxxx, enable, | ||
| 483 | * supervisor only, non-cacheable/ | ||
| 484 | * serialized, writable */ | ||
| 485 | : "d0" ); | ||
| 486 | |||
| 487 | } | ||
| 488 | |||
| 489 | /* Fetch tos version at Physical 2 */ | ||
| 490 | /* We my not be able to access this address if the kernel is | ||
| 491 | loaded to st ram, since the first page is unmapped. On the | ||
| 492 | Medusa this is always the case and there is nothing we can do | ||
| 493 | about this, so we just assume the smaller offset. For the TT | ||
| 494 | we use the fact that in head.S we have set up a mapping | ||
| 495 | 0xFFxxxxxx -> 0x00xxxxxx, so that the first 16MB is accessible | ||
| 496 | in the last 16MB of the address space. */ | ||
| 497 | tos_version = (MACH_IS_MEDUSA || MACH_IS_HADES) ? | ||
| 498 | 0xfff : *(unsigned short *)0xff000002; | ||
| 499 | atari_rtc_year_offset = (tos_version < 0x306) ? 70 : 68; | ||
| 500 | } | ||
| 501 | |||
| 502 | #ifdef CONFIG_HEARTBEAT | ||
| 503 | static void atari_heartbeat( int on ) | ||
| 504 | { | ||
| 505 | unsigned char tmp; | ||
| 506 | unsigned long flags; | ||
| 507 | |||
| 508 | if (atari_dont_touch_floppy_select) | ||
| 509 | return; | ||
| 510 | |||
| 511 | local_irq_save(flags); | ||
| 512 | sound_ym.rd_data_reg_sel = 14; /* Select PSG Port A */ | ||
| 513 | tmp = sound_ym.rd_data_reg_sel; | ||
| 514 | sound_ym.wd_data = on ? (tmp & ~0x02) : (tmp | 0x02); | ||
| 515 | local_irq_restore(flags); | ||
| 516 | } | ||
| 517 | #endif | ||
| 518 | |||
| 519 | /* ++roman: | ||
| 520 | * | ||
| 521 | * This function does a reset on machines that lack the ability to | ||
| 522 | * assert the processor's _RESET signal somehow via hardware. It is | ||
| 523 | * based on the fact that you can find the initial SP and PC values | ||
| 524 | * after a reset at physical addresses 0 and 4. This works pretty well | ||
| 525 | * for Atari machines, since the lowest 8 bytes of physical memory are | ||
| 526 | * really ROM (mapped by hardware). For other 680x0 machines: don't | ||
| 527 | * know if it works... | ||
| 528 | * | ||
| 529 | * To get the values at addresses 0 and 4, the MMU better is turned | ||
| 530 | * off first. After that, we have to jump into physical address space | ||
| 531 | * (the PC before the pmove statement points to the virtual address of | ||
| 532 | * the code). Getting that physical address is not hard, but the code | ||
| 533 | * becomes a bit complex since I've tried to ensure that the jump | ||
| 534 | * statement after the pmove is in the cache already (otherwise the | ||
| 535 | * processor can't fetch it!). For that, the code first jumps to the | ||
| 536 | * jump statement with the (virtual) address of the pmove section in | ||
| 537 | * an address register . The jump statement is surely in the cache | ||
| 538 | * now. After that, that physical address of the reset code is loaded | ||
| 539 | * into the same address register, pmove is done and the same jump | ||
| 540 | * statements goes to the reset code. Since there are not many | ||
| 541 | * statements between the two jumps, I hope it stays in the cache. | ||
| 542 | * | ||
| 543 | * The C code makes heavy use of the GCC features that you can get the | ||
| 544 | * address of a C label. No hope to compile this with another compiler | ||
| 545 | * than GCC! | ||
| 546 | */ | ||
| 547 | |||
| 548 | /* ++andreas: no need for complicated code, just depend on prefetch */ | ||
| 549 | |||
| 550 | static void atari_reset (void) | ||
| 551 | { | ||
| 552 | long tc_val = 0; | ||
| 553 | long reset_addr; | ||
| 554 | |||
| 555 | /* On the Medusa, phys. 0x4 may contain garbage because it's no | ||
| 556 | ROM. See above for explanation why we cannot use PTOV(4). */ | ||
| 557 | reset_addr = MACH_IS_HADES ? 0x7fe00030 : | ||
| 558 | MACH_IS_MEDUSA || MACH_IS_AB40 ? 0xe00030 : | ||
| 559 | *(unsigned long *) 0xff000004; | ||
| 560 | |||
| 561 | /* reset ACIA for switch off OverScan, if it's active */ | ||
| 562 | if (atari_switches & ATARI_SWITCH_OVSC_IKBD) | ||
| 563 | acia.key_ctrl = ACIA_RESET; | ||
| 564 | if (atari_switches & ATARI_SWITCH_OVSC_MIDI) | ||
| 565 | acia.mid_ctrl = ACIA_RESET; | ||
| 566 | |||
| 567 | /* processor independent: turn off interrupts and reset the VBR; | ||
| 568 | * the caches must be left enabled, else prefetching the final jump | ||
| 569 | * instruction doesn't work. */ | ||
| 570 | local_irq_disable(); | ||
| 571 | __asm__ __volatile__ | ||
| 572 | ("moveq #0,%/d0\n\t" | ||
| 573 | "movec %/d0,%/vbr" | ||
| 574 | : : : "d0" ); | ||
| 575 | |||
| 576 | if (CPU_IS_040_OR_060) { | ||
| 577 | unsigned long jmp_addr040 = virt_to_phys(&&jmp_addr_label040); | ||
| 578 | if (CPU_IS_060) { | ||
| 579 | /* 68060: clear PCR to turn off superscalar operation */ | ||
| 580 | __asm__ __volatile__ | ||
| 581 | ("moveq #0,%/d0\n\t" | ||
| 582 | ".chip 68060\n\t" | ||
| 583 | "movec %%d0,%%pcr\n\t" | ||
| 584 | ".chip 68k" | ||
| 585 | : : : "d0" ); | ||
| 586 | } | ||
| 587 | |||
| 588 | __asm__ __volatile__ | ||
| 589 | ("movel %0,%/d0\n\t" | ||
| 590 | "andl #0xff000000,%/d0\n\t" | ||
| 591 | "orw #0xe020,%/d0\n\t" /* map 16 MB, enable, cacheable */ | ||
| 592 | ".chip 68040\n\t" | ||
| 593 | "movec %%d0,%%itt0\n\t" | ||
| 594 | "movec %%d0,%%dtt0\n\t" | ||
| 595 | ".chip 68k\n\t" | ||
| 596 | "jmp %0@\n\t" | ||
| 597 | : /* no outputs */ | ||
| 598 | : "a" (jmp_addr040) | ||
| 599 | : "d0" ); | ||
| 600 | jmp_addr_label040: | ||
| 601 | __asm__ __volatile__ | ||
| 602 | ("moveq #0,%/d0\n\t" | ||
| 603 | "nop\n\t" | ||
| 604 | ".chip 68040\n\t" | ||
| 605 | "cinva %%bc\n\t" | ||
| 606 | "nop\n\t" | ||
| 607 | "pflusha\n\t" | ||
| 608 | "nop\n\t" | ||
| 609 | "movec %%d0,%%tc\n\t" | ||
| 610 | "nop\n\t" | ||
| 611 | /* the following setup of transparent translations is needed on the | ||
| 612 | * Afterburner040 to successfully reboot. Other machines shouldn't | ||
| 613 | * care about a different tt regs setup, they also didn't care in | ||
| 614 | * the past that the regs weren't turned off. */ | ||
| 615 | "movel #0xffc000,%%d0\n\t" /* whole insn space cacheable */ | ||
| 616 | "movec %%d0,%%itt0\n\t" | ||
| 617 | "movec %%d0,%%itt1\n\t" | ||
| 618 | "orw #0x40,%/d0\n\t" /* whole data space non-cacheable/ser. */ | ||
| 619 | "movec %%d0,%%dtt0\n\t" | ||
| 620 | "movec %%d0,%%dtt1\n\t" | ||
| 621 | ".chip 68k\n\t" | ||
| 622 | "jmp %0@" | ||
| 623 | : /* no outputs */ | ||
| 624 | : "a" (reset_addr) | ||
| 625 | : "d0"); | ||
| 626 | } | ||
| 627 | else | ||
| 628 | __asm__ __volatile__ | ||
| 629 | ("pmove %0@,%/tc\n\t" | ||
| 630 | "jmp %1@" | ||
| 631 | : /* no outputs */ | ||
| 632 | : "a" (&tc_val), "a" (reset_addr)); | ||
| 633 | } | ||
| 634 | |||
| 635 | |||
| 636 | static void atari_get_model(char *model) | ||
| 637 | { | ||
| 638 | strcpy(model, "Atari "); | ||
| 639 | switch (atari_mch_cookie >> 16) { | ||
| 640 | case ATARI_MCH_ST: | ||
| 641 | if (ATARIHW_PRESENT(MSTE_CLK)) | ||
| 642 | strcat (model, "Mega ST"); | ||
| 643 | else | ||
| 644 | strcat (model, "ST"); | ||
| 645 | break; | ||
| 646 | case ATARI_MCH_STE: | ||
| 647 | if (MACH_IS_MSTE) | ||
| 648 | strcat (model, "Mega STE"); | ||
| 649 | else | ||
| 650 | strcat (model, "STE"); | ||
| 651 | break; | ||
| 652 | case ATARI_MCH_TT: | ||
| 653 | if (MACH_IS_MEDUSA) | ||
| 654 | /* Medusa has TT _MCH cookie */ | ||
| 655 | strcat (model, "Medusa"); | ||
| 656 | else if (MACH_IS_HADES) | ||
| 657 | strcat(model, "Hades"); | ||
| 658 | else | ||
| 659 | strcat (model, "TT"); | ||
| 660 | break; | ||
| 661 | case ATARI_MCH_FALCON: | ||
| 662 | strcat (model, "Falcon"); | ||
| 663 | if (MACH_IS_AB40) | ||
| 664 | strcat (model, " (with Afterburner040)"); | ||
| 665 | break; | ||
| 666 | default: | ||
| 667 | sprintf (model + strlen (model), "(unknown mach cookie 0x%lx)", | ||
| 668 | atari_mch_cookie); | ||
| 669 | break; | ||
| 670 | } | ||
| 671 | } | ||
| 672 | |||
| 673 | |||
| 674 | static int atari_get_hardware_list(char *buffer) | ||
| 675 | { | ||
| 676 | int len = 0, i; | ||
| 677 | |||
| 678 | for (i = 0; i < m68k_num_memory; i++) | ||
| 679 | len += sprintf (buffer+len, "\t%3ld MB at 0x%08lx (%s)\n", | ||
| 680 | m68k_memory[i].size >> 20, m68k_memory[i].addr, | ||
| 681 | (m68k_memory[i].addr & 0xff000000 ? | ||
| 682 | "alternate RAM" : "ST-RAM")); | ||
| 683 | |||
| 684 | #define ATARIHW_ANNOUNCE(name,str) \ | ||
| 685 | if (ATARIHW_PRESENT(name)) \ | ||
| 686 | len += sprintf (buffer + len, "\t%s\n", str) | ||
| 687 | |||
| 688 | len += sprintf (buffer + len, "Detected hardware:\n"); | ||
| 689 | ATARIHW_ANNOUNCE(STND_SHIFTER, "ST Shifter"); | ||
| 690 | ATARIHW_ANNOUNCE(EXTD_SHIFTER, "STe Shifter"); | ||
| 691 | ATARIHW_ANNOUNCE(TT_SHIFTER, "TT Shifter"); | ||
| 692 | ATARIHW_ANNOUNCE(VIDEL_SHIFTER, "Falcon Shifter"); | ||
| 693 | ATARIHW_ANNOUNCE(YM_2149, "Programmable Sound Generator"); | ||
| 694 | ATARIHW_ANNOUNCE(PCM_8BIT, "PCM 8 Bit Sound"); | ||
| 695 | ATARIHW_ANNOUNCE(CODEC, "CODEC Sound"); | ||
| 696 | ATARIHW_ANNOUNCE(TT_SCSI, "SCSI Controller NCR5380 (TT style)"); | ||
| 697 | ATARIHW_ANNOUNCE(ST_SCSI, "SCSI Controller NCR5380 (Falcon style)"); | ||
| 698 | ATARIHW_ANNOUNCE(ACSI, "ACSI Interface"); | ||
| 699 | ATARIHW_ANNOUNCE(IDE, "IDE Interface"); | ||
| 700 | ATARIHW_ANNOUNCE(FDCSPEED, "8/16 Mhz Switch for FDC"); | ||
| 701 | ATARIHW_ANNOUNCE(ST_MFP, "Multi Function Peripheral MFP 68901"); | ||
| 702 | ATARIHW_ANNOUNCE(TT_MFP, "Second Multi Function Peripheral MFP 68901"); | ||
| 703 | ATARIHW_ANNOUNCE(SCC, "Serial Communications Controller SCC 8530"); | ||
| 704 | ATARIHW_ANNOUNCE(ST_ESCC, "Extended Serial Communications Controller SCC 85230"); | ||
| 705 | ATARIHW_ANNOUNCE(ANALOG_JOY, "Paddle Interface"); | ||
| 706 | ATARIHW_ANNOUNCE(MICROWIRE, "MICROWIRE(tm) Interface"); | ||
| 707 | ATARIHW_ANNOUNCE(STND_DMA, "DMA Controller (24 bit)"); | ||
| 708 | ATARIHW_ANNOUNCE(EXTD_DMA, "DMA Controller (32 bit)"); | ||
| 709 | ATARIHW_ANNOUNCE(SCSI_DMA, "DMA Controller for NCR5380"); | ||
| 710 | ATARIHW_ANNOUNCE(SCC_DMA, "DMA Controller for SCC"); | ||
| 711 | ATARIHW_ANNOUNCE(TT_CLK, "Clock Chip MC146818A"); | ||
| 712 | ATARIHW_ANNOUNCE(MSTE_CLK, "Clock Chip RP5C15"); | ||
| 713 | ATARIHW_ANNOUNCE(SCU, "System Control Unit"); | ||
| 714 | ATARIHW_ANNOUNCE(BLITTER, "Blitter"); | ||
| 715 | ATARIHW_ANNOUNCE(VME, "VME Bus"); | ||
| 716 | ATARIHW_ANNOUNCE(DSP56K, "DSP56001 processor"); | ||
| 717 | |||
| 718 | return(len); | ||
| 719 | } | ||
| 720 | |||
| 721 | /* | ||
| 722 | * Local variables: | ||
| 723 | * c-indent-level: 4 | ||
| 724 | * tab-width: 8 | ||
| 725 | * End: | ||
| 726 | */ | ||
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 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/m68k/atari/debug.c | ||
| 3 | * | ||
| 4 | * Atari debugging and serial console stuff | ||
| 5 | * | ||
| 6 | * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek | ||
| 7 | * | ||
| 8 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 9 | * License. See the file COPYING in the main directory of this archive | ||
| 10 | * for more details. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/config.h> | ||
| 14 | #include <linux/types.h> | ||
| 15 | #include <linux/tty.h> | ||
| 16 | #include <linux/console.h> | ||
| 17 | #include <linux/init.h> | ||
| 18 | #include <linux/delay.h> | ||
| 19 | |||
| 20 | #include <asm/atarihw.h> | ||
| 21 | #include <asm/atariints.h> | ||
| 22 | |||
| 23 | extern char m68k_debug_device[]; | ||
| 24 | |||
| 25 | /* Flag that Modem1 port is already initialized and used */ | ||
| 26 | int atari_MFP_init_done; | ||
| 27 | /* Flag that Modem1 port is already initialized and used */ | ||
| 28 | int atari_SCC_init_done; | ||
| 29 | /* Can be set somewhere, if a SCC master reset has already be done and should | ||
| 30 | * not be repeated; used by kgdb */ | ||
| 31 | int atari_SCC_reset_done; | ||
| 32 | |||
| 33 | static struct console atari_console_driver = { | ||
| 34 | .name = "debug", | ||
| 35 | .flags = CON_PRINTBUFFER, | ||
| 36 | .index = -1, | ||
| 37 | }; | ||
| 38 | |||
| 39 | |||
| 40 | static inline void ata_mfp_out (char c) | ||
| 41 | { | ||
| 42 | while (!(mfp.trn_stat & 0x80)) /* wait for tx buf empty */ | ||
| 43 | barrier (); | ||
| 44 | mfp.usart_dta = c; | ||
| 45 | } | ||
| 46 | |||
| 47 | void atari_mfp_console_write (struct console *co, const char *str, | ||
| 48 | unsigned int count) | ||
| 49 | { | ||
| 50 | while (count--) { | ||
| 51 | if (*str == '\n') | ||
| 52 | ata_mfp_out( '\r' ); | ||
| 53 | ata_mfp_out( *str++ ); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | static inline void ata_scc_out (char c) | ||
| 58 | { | ||
| 59 | do { | ||
| 60 | MFPDELAY(); | ||
| 61 | } while (!(scc.cha_b_ctrl & 0x04)); /* wait for tx buf empty */ | ||
| 62 | MFPDELAY(); | ||
| 63 | scc.cha_b_data = c; | ||
| 64 | } | ||
| 65 | |||
| 66 | void atari_scc_console_write (struct console *co, const char *str, | ||
| 67 | unsigned int count) | ||
| 68 | { | ||
| 69 | while (count--) { | ||
| 70 | if (*str == '\n') | ||
| 71 | ata_scc_out( '\r' ); | ||
| 72 | ata_scc_out( *str++ ); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | static inline void ata_midi_out (char c) | ||
| 77 | { | ||
| 78 | while (!(acia.mid_ctrl & ACIA_TDRE)) /* wait for tx buf empty */ | ||
| 79 | barrier (); | ||
| 80 | acia.mid_data = c; | ||
| 81 | } | ||
| 82 | |||
| 83 | void atari_midi_console_write (struct console *co, const char *str, | ||
| 84 | unsigned int count) | ||
| 85 | { | ||
| 86 | while (count--) { | ||
| 87 | if (*str == '\n') | ||
| 88 | ata_midi_out( '\r' ); | ||
| 89 | ata_midi_out( *str++ ); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | static int ata_par_out (char c) | ||
| 94 | { | ||
| 95 | unsigned char tmp; | ||
| 96 | /* This a some-seconds timeout in case no printer is connected */ | ||
| 97 | unsigned long i = loops_per_jiffy > 1 ? loops_per_jiffy : 10000000/HZ; | ||
| 98 | |||
| 99 | while( (mfp.par_dt_reg & 1) && --i ) /* wait for BUSY == L */ | ||
| 100 | ; | ||
| 101 | if (!i) return( 0 ); | ||
| 102 | |||
| 103 | sound_ym.rd_data_reg_sel = 15; /* select port B */ | ||
| 104 | sound_ym.wd_data = c; /* put char onto port */ | ||
| 105 | sound_ym.rd_data_reg_sel = 14; /* select port A */ | ||
| 106 | tmp = sound_ym.rd_data_reg_sel; | ||
| 107 | sound_ym.wd_data = tmp & ~0x20; /* set strobe L */ | ||
| 108 | MFPDELAY(); /* wait a bit */ | ||
| 109 | sound_ym.wd_data = tmp | 0x20; /* set strobe H */ | ||
| 110 | return( 1 ); | ||
| 111 | } | ||
| 112 | |||
| 113 | static void atari_par_console_write (struct console *co, const char *str, | ||
| 114 | unsigned int count) | ||
| 115 | { | ||
| 116 | static int printer_present = 1; | ||
| 117 | |||
| 118 | if (!printer_present) | ||
| 119 | return; | ||
| 120 | |||
| 121 | while (count--) { | ||
| 122 | if (*str == '\n') | ||
| 123 | if (!ata_par_out( '\r' )) { | ||
| 124 | printer_present = 0; | ||
| 125 | return; | ||
| 126 | } | ||
| 127 | if (!ata_par_out( *str++ )) { | ||
| 128 | printer_present = 0; | ||
| 129 | return; | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | #ifdef CONFIG_SERIAL_CONSOLE | ||
| 135 | int atari_mfp_console_wait_key(struct console *co) | ||
| 136 | { | ||
| 137 | while( !(mfp.rcv_stat & 0x80) ) /* wait for rx buf filled */ | ||
| 138 | barrier(); | ||
| 139 | return( mfp.usart_dta ); | ||
| 140 | } | ||
| 141 | |||
| 142 | int atari_scc_console_wait_key(struct console *co) | ||
| 143 | { | ||
| 144 | do { | ||
| 145 | MFPDELAY(); | ||
| 146 | } while( !(scc.cha_b_ctrl & 0x01) ); /* wait for rx buf filled */ | ||
| 147 | MFPDELAY(); | ||
| 148 | return( scc.cha_b_data ); | ||
| 149 | } | ||
| 150 | |||
| 151 | int atari_midi_console_wait_key(struct console *co) | ||
| 152 | { | ||
| 153 | while( !(acia.mid_ctrl & ACIA_RDRF) ) /* wait for rx buf filled */ | ||
| 154 | barrier(); | ||
| 155 | return( acia.mid_data ); | ||
| 156 | } | ||
| 157 | #endif | ||
| 158 | |||
| 159 | /* The following two functions do a quick'n'dirty initialization of the MFP or | ||
| 160 | * SCC serial ports. They're used by the debugging interface, kgdb, and the | ||
| 161 | * serial console code. */ | ||
| 162 | #ifndef CONFIG_SERIAL_CONSOLE | ||
| 163 | static void __init atari_init_mfp_port( int cflag ) | ||
| 164 | #else | ||
| 165 | void atari_init_mfp_port( int cflag ) | ||
| 166 | #endif | ||
| 167 | { | ||
| 168 | /* timer values for 1200...115200 bps; > 38400 select 110, 134, or 150 | ||
| 169 | * bps, resp., and work only correct if there's a RSVE or RSSPEED */ | ||
| 170 | static int baud_table[9] = { 16, 11, 8, 4, 2, 1, 175, 143, 128 }; | ||
| 171 | int baud = cflag & CBAUD; | ||
| 172 | int parity = (cflag & PARENB) ? ((cflag & PARODD) ? 0x04 : 0x06) : 0; | ||
| 173 | int csize = ((cflag & CSIZE) == CS7) ? 0x20 : 0x00; | ||
| 174 | |||
| 175 | if (cflag & CBAUDEX) | ||
| 176 | baud += B38400; | ||
| 177 | if (baud < B1200 || baud > B38400+2) | ||
| 178 | baud = B9600; /* use default 9600bps for non-implemented rates */ | ||
| 179 | baud -= B1200; /* baud_table[] starts at 1200bps */ | ||
| 180 | |||
| 181 | mfp.trn_stat &= ~0x01; /* disable TX */ | ||
| 182 | mfp.usart_ctr = parity | csize | 0x88; /* 1:16 clk mode, 1 stop bit */ | ||
| 183 | mfp.tim_ct_cd &= 0x70; /* stop timer D */ | ||
| 184 | mfp.tim_dt_d = baud_table[baud]; | ||
| 185 | mfp.tim_ct_cd |= 0x01; /* start timer D, 1:4 */ | ||
| 186 | mfp.trn_stat |= 0x01; /* enable TX */ | ||
| 187 | |||
| 188 | atari_MFP_init_done = 1; | ||
| 189 | } | ||
| 190 | |||
| 191 | #define SCC_WRITE(reg,val) \ | ||
| 192 | do { \ | ||
| 193 | scc.cha_b_ctrl = (reg); \ | ||
| 194 | MFPDELAY(); \ | ||
| 195 | scc.cha_b_ctrl = (val); \ | ||
| 196 | MFPDELAY(); \ | ||
| 197 | } while(0) | ||
| 198 | |||
| 199 | /* loops_per_jiffy isn't initialized yet, so we can't use udelay(). This does a | ||
| 200 | * delay of ~ 60us. */ | ||
| 201 | #define LONG_DELAY() \ | ||
| 202 | do { \ | ||
| 203 | int i; \ | ||
| 204 | for( i = 100; i > 0; --i ) \ | ||
| 205 | MFPDELAY(); \ | ||
| 206 | } while(0) | ||
| 207 | |||
| 208 | #ifndef CONFIG_SERIAL_CONSOLE | ||
| 209 | static void __init atari_init_scc_port( int cflag ) | ||
| 210 | #else | ||
| 211 | void atari_init_scc_port( int cflag ) | ||
| 212 | #endif | ||
| 213 | { | ||
| 214 | extern int atari_SCC_reset_done; | ||
| 215 | static int clksrc_table[9] = | ||
| 216 | /* reg 11: 0x50 = BRG, 0x00 = RTxC, 0x28 = TRxC */ | ||
| 217 | { 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x00, 0x00 }; | ||
| 218 | static int brgsrc_table[9] = | ||
| 219 | /* reg 14: 0 = RTxC, 2 = PCLK */ | ||
| 220 | { 2, 2, 2, 2, 2, 2, 0, 2, 2 }; | ||
| 221 | static int clkmode_table[9] = | ||
| 222 | /* reg 4: 0x40 = x16, 0x80 = x32, 0xc0 = x64 */ | ||
| 223 | { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x80 }; | ||
| 224 | static int div_table[9] = | ||
| 225 | /* reg12 (BRG low) */ | ||
| 226 | { 208, 138, 103, 50, 24, 11, 1, 0, 0 }; | ||
| 227 | |||
| 228 | int baud = cflag & CBAUD; | ||
| 229 | int clksrc, clkmode, div, reg3, reg5; | ||
| 230 | |||
| 231 | if (cflag & CBAUDEX) | ||
| 232 | baud += B38400; | ||
| 233 | if (baud < B1200 || baud > B38400+2) | ||
| 234 | baud = B9600; /* use default 9600bps for non-implemented rates */ | ||
| 235 | baud -= B1200; /* tables starts at 1200bps */ | ||
| 236 | |||
| 237 | clksrc = clksrc_table[baud]; | ||
| 238 | clkmode = clkmode_table[baud]; | ||
| 239 | div = div_table[baud]; | ||
| 240 | if (ATARIHW_PRESENT(TT_MFP) && baud >= 6) { | ||
| 241 | /* special treatment for TT, where rates >= 38400 are done via TRxC */ | ||
| 242 | clksrc = 0x28; /* TRxC */ | ||
| 243 | clkmode = baud == 6 ? 0xc0 : | ||
| 244 | baud == 7 ? 0x80 : /* really 76800bps */ | ||
| 245 | 0x40; /* really 153600bps */ | ||
| 246 | div = 0; | ||
| 247 | } | ||
| 248 | |||
| 249 | reg3 = (cflag & CSIZE) == CS8 ? 0xc0 : 0x40; | ||
| 250 | reg5 = (cflag & CSIZE) == CS8 ? 0x60 : 0x20 | 0x82 /* assert DTR/RTS */; | ||
| 251 | |||
| 252 | (void)scc.cha_b_ctrl; /* reset reg pointer */ | ||
| 253 | SCC_WRITE( 9, 0xc0 ); /* reset */ | ||
| 254 | LONG_DELAY(); /* extra delay after WR9 access */ | ||
| 255 | SCC_WRITE( 4, (cflag & PARENB) ? ((cflag & PARODD) ? 0x01 : 0x03) : 0 | | ||
| 256 | 0x04 /* 1 stopbit */ | | ||
| 257 | clkmode ); | ||
| 258 | SCC_WRITE( 3, reg3 ); | ||
| 259 | SCC_WRITE( 5, reg5 ); | ||
| 260 | SCC_WRITE( 9, 0 ); /* no interrupts */ | ||
| 261 | LONG_DELAY(); /* extra delay after WR9 access */ | ||
| 262 | SCC_WRITE( 10, 0 ); /* NRZ mode */ | ||
| 263 | SCC_WRITE( 11, clksrc ); /* main clock source */ | ||
| 264 | SCC_WRITE( 12, div ); /* BRG value */ | ||
| 265 | SCC_WRITE( 13, 0 ); /* BRG high byte */ | ||
| 266 | SCC_WRITE( 14, brgsrc_table[baud] ); | ||
| 267 | SCC_WRITE( 14, brgsrc_table[baud] | (div ? 1 : 0) ); | ||
| 268 | SCC_WRITE( 3, reg3 | 1 ); | ||
| 269 | SCC_WRITE( 5, reg5 | 8 ); | ||
| 270 | |||
| 271 | atari_SCC_reset_done = 1; | ||
| 272 | atari_SCC_init_done = 1; | ||
| 273 | } | ||
| 274 | |||
| 275 | #ifndef CONFIG_SERIAL_CONSOLE | ||
| 276 | static void __init atari_init_midi_port( int cflag ) | ||
| 277 | #else | ||
| 278 | void atari_init_midi_port( int cflag ) | ||
| 279 | #endif | ||
| 280 | { | ||
| 281 | int baud = cflag & CBAUD; | ||
| 282 | int csize = ((cflag & CSIZE) == CS8) ? 0x10 : 0x00; | ||
| 283 | /* warning 7N1 isn't possible! (instead 7O2 is used...) */ | ||
| 284 | int parity = (cflag & PARENB) ? ((cflag & PARODD) ? 0x0c : 0x08) : 0x04; | ||
| 285 | int div; | ||
| 286 | |||
| 287 | /* 4800 selects 7812.5, 115200 selects 500000, all other (incl. 9600 as | ||
| 288 | * default) the standard MIDI speed 31250. */ | ||
| 289 | if (cflag & CBAUDEX) | ||
| 290 | baud += B38400; | ||
| 291 | if (baud == B4800) | ||
| 292 | div = ACIA_DIV64; /* really 7812.5 bps */ | ||
| 293 | else if (baud == B38400+2 /* 115200 */) | ||
| 294 | div = ACIA_DIV1; /* really 500 kbps (does that work??) */ | ||
| 295 | else | ||
| 296 | div = ACIA_DIV16; /* 31250 bps, standard for MIDI */ | ||
| 297 | |||
| 298 | /* RTS low, ints disabled */ | ||
| 299 | acia.mid_ctrl = div | csize | parity | | ||
| 300 | ((atari_switches & ATARI_SWITCH_MIDI) ? | ||
| 301 | ACIA_RHTID : ACIA_RLTID); | ||
| 302 | } | ||
| 303 | |||
| 304 | void __init atari_debug_init(void) | ||
| 305 | { | ||
| 306 | if (!strcmp( m68k_debug_device, "ser" )) { | ||
| 307 | /* defaults to ser2 for a Falcon and ser1 otherwise */ | ||
| 308 | strcpy( m68k_debug_device, MACH_IS_FALCON ? "ser2" : "ser1" ); | ||
| 309 | |||
| 310 | } | ||
| 311 | |||
| 312 | if (!strcmp( m68k_debug_device, "ser1" )) { | ||
| 313 | /* ST-MFP Modem1 serial port */ | ||
| 314 | atari_init_mfp_port( B9600|CS8 ); | ||
| 315 | atari_console_driver.write = atari_mfp_console_write; | ||
| 316 | } | ||
| 317 | else if (!strcmp( m68k_debug_device, "ser2" )) { | ||
| 318 | /* SCC Modem2 serial port */ | ||
| 319 | atari_init_scc_port( B9600|CS8 ); | ||
| 320 | atari_console_driver.write = atari_scc_console_write; | ||
| 321 | } | ||
| 322 | else if (!strcmp( m68k_debug_device, "midi" )) { | ||
| 323 | /* MIDI port */ | ||
| 324 | atari_init_midi_port( B9600|CS8 ); | ||
| 325 | atari_console_driver.write = atari_midi_console_write; | ||
| 326 | } | ||
| 327 | else if (!strcmp( m68k_debug_device, "par" )) { | ||
| 328 | /* parallel printer */ | ||
| 329 | atari_turnoff_irq( IRQ_MFP_BUSY ); /* avoid ints */ | ||
| 330 | sound_ym.rd_data_reg_sel = 7; /* select mixer control */ | ||
| 331 | sound_ym.wd_data = 0xff; /* sound off, ports are output */ | ||
| 332 | sound_ym.rd_data_reg_sel = 15; /* select port B */ | ||
| 333 | sound_ym.wd_data = 0; /* no char */ | ||
| 334 | sound_ym.rd_data_reg_sel = 14; /* select port A */ | ||
| 335 | sound_ym.wd_data = sound_ym.rd_data_reg_sel | 0x20; /* strobe H */ | ||
| 336 | atari_console_driver.write = atari_par_console_write; | ||
| 337 | } | ||
| 338 | if (atari_console_driver.write) | ||
| 339 | register_console(&atari_console_driver); | ||
| 340 | } | ||
| 341 | |||
| 342 | /* | ||
| 343 | * Local variables: | ||
| 344 | * c-indent-level: 4 | ||
| 345 | * tab-width: 8 | ||
| 346 | * End: | ||
| 347 | */ | ||
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 @@ | |||
| 1 | /* | ||
| 2 | * hades-pci.c - Hardware specific PCI BIOS functions the Hades Atari clone. | ||
| 3 | * | ||
| 4 | * Written by Wout Klaren. | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/config.h> | ||
| 8 | #include <linux/init.h> | ||
| 9 | #include <linux/kernel.h> | ||
| 10 | #include <asm/io.h> | ||
| 11 | |||
| 12 | #if 0 | ||
| 13 | # define DBG_DEVS(args) printk args | ||
| 14 | #else | ||
| 15 | # define DBG_DEVS(args) | ||
| 16 | #endif | ||
| 17 | |||
| 18 | #if defined(CONFIG_PCI) && defined(CONFIG_HADES) | ||
| 19 | |||
| 20 | #include <linux/slab.h> | ||
| 21 | #include <linux/mm.h> | ||
| 22 | #include <linux/pci.h> | ||
| 23 | |||
| 24 | #include <asm/atarihw.h> | ||
| 25 | #include <asm/atariints.h> | ||
| 26 | #include <asm/byteorder.h> | ||
| 27 | #include <asm/pci.h> | ||
| 28 | |||
| 29 | #define HADES_MEM_BASE 0x80000000 | ||
| 30 | #define HADES_MEM_SIZE 0x20000000 | ||
| 31 | #define HADES_CONFIG_BASE 0xA0000000 | ||
| 32 | #define HADES_CONFIG_SIZE 0x10000000 | ||
| 33 | #define HADES_IO_BASE 0xB0000000 | ||
| 34 | #define HADES_IO_SIZE 0x10000000 | ||
| 35 | #define HADES_VIRT_IO_SIZE 0x00010000 /* Only 64k is remapped and actually used. */ | ||
| 36 | |||
| 37 | #define N_SLOTS 4 /* Number of PCI slots. */ | ||
| 38 | |||
| 39 | static const char pci_mem_name[] = "PCI memory space"; | ||
| 40 | static const char pci_io_name[] = "PCI I/O space"; | ||
| 41 | static const char pci_config_name[] = "PCI config space"; | ||
| 42 | |||
| 43 | static struct resource config_space = { | ||
| 44 | .name = pci_config_name, | ||
| 45 | .start = HADES_CONFIG_BASE, | ||
| 46 | .end = HADES_CONFIG_BASE + HADES_CONFIG_SIZE - 1 | ||
| 47 | }; | ||
| 48 | static struct resource io_space = { | ||
| 49 | .name = pci_io_name, | ||
| 50 | .start = HADES_IO_BASE, | ||
| 51 | .end = HADES_IO_BASE + HADES_IO_SIZE - 1 | ||
| 52 | }; | ||
| 53 | |||
| 54 | static const unsigned long pci_conf_base_phys[] = { | ||
| 55 | 0xA0080000, 0xA0040000, 0xA0020000, 0xA0010000 | ||
| 56 | }; | ||
| 57 | static unsigned long pci_conf_base_virt[N_SLOTS]; | ||
| 58 | static unsigned long pci_io_base_virt; | ||
| 59 | |||
| 60 | /* | ||
| 61 | * static void *mk_conf_addr(unsigned char bus, unsigned char device_fn, | ||
| 62 | * unsigned char where) | ||
| 63 | * | ||
| 64 | * Calculate the address of the PCI configuration area of the given | ||
| 65 | * device. | ||
| 66 | * | ||
| 67 | * BUG: boards with multiple functions are probably not correctly | ||
| 68 | * supported. | ||
| 69 | */ | ||
| 70 | |||
| 71 | static void *mk_conf_addr(struct pci_dev *dev, int where) | ||
| 72 | { | ||
| 73 | int device = dev->devfn >> 3, function = dev->devfn & 7; | ||
| 74 | void *result; | ||
| 75 | |||
| 76 | DBG_DEVS(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p)\n", | ||
| 77 | dev->bus->number, dev->devfn, where, pci_addr)); | ||
| 78 | |||
| 79 | if (device > 3) | ||
| 80 | { | ||
| 81 | DBG_DEVS(("mk_conf_addr: device (%d) > 3, returning NULL\n", device)); | ||
| 82 | return NULL; | ||
| 83 | } | ||
| 84 | |||
| 85 | if (dev->bus->number != 0) | ||
| 86 | { | ||
| 87 | DBG_DEVS(("mk_conf_addr: bus (%d) > 0, returning NULL\n", device)); | ||
| 88 | return NULL; | ||
| 89 | } | ||
| 90 | |||
| 91 | result = (void *) (pci_conf_base_virt[device] | (function << 8) | (where)); | ||
| 92 | DBG_DEVS(("mk_conf_addr: returning pci_addr 0x%lx\n", (unsigned long) result)); | ||
| 93 | return result; | ||
| 94 | } | ||
| 95 | |||
| 96 | static int hades_read_config_byte(struct pci_dev *dev, int where, u8 *value) | ||
| 97 | { | ||
| 98 | volatile unsigned char *pci_addr; | ||
| 99 | |||
| 100 | *value = 0xff; | ||
| 101 | |||
| 102 | if ((pci_addr = (unsigned char *) mk_conf_addr(dev, where)) == NULL) | ||
| 103 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
| 104 | |||
| 105 | *value = *pci_addr; | ||
| 106 | |||
| 107 | return PCIBIOS_SUCCESSFUL; | ||
| 108 | } | ||
| 109 | |||
| 110 | static int hades_read_config_word(struct pci_dev *dev, int where, u16 *value) | ||
| 111 | { | ||
| 112 | volatile unsigned short *pci_addr; | ||
| 113 | |||
| 114 | *value = 0xffff; | ||
| 115 | |||
| 116 | if (where & 0x1) | ||
| 117 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
| 118 | |||
| 119 | if ((pci_addr = (unsigned short *) mk_conf_addr(dev, where)) == NULL) | ||
| 120 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
| 121 | |||
| 122 | *value = le16_to_cpu(*pci_addr); | ||
| 123 | |||
| 124 | return PCIBIOS_SUCCESSFUL; | ||
| 125 | } | ||
| 126 | |||
| 127 | static int hades_read_config_dword(struct pci_dev *dev, int where, u32 *value) | ||
| 128 | { | ||
| 129 | volatile unsigned int *pci_addr; | ||
| 130 | unsigned char header_type; | ||
| 131 | int result; | ||
| 132 | |||
| 133 | *value = 0xffffffff; | ||
| 134 | |||
| 135 | if (where & 0x3) | ||
| 136 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
| 137 | |||
| 138 | if ((pci_addr = (unsigned int *) mk_conf_addr(dev, where)) == NULL) | ||
| 139 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
| 140 | |||
| 141 | *value = le32_to_cpu(*pci_addr); | ||
| 142 | |||
| 143 | /* | ||
| 144 | * Check if the value is an address on the bus. If true, add the | ||
| 145 | * base address of the PCI memory or PCI I/O area on the Hades. | ||
| 146 | */ | ||
| 147 | |||
| 148 | if ((result = hades_read_config_byte(dev, PCI_HEADER_TYPE, | ||
| 149 | &header_type)) != PCIBIOS_SUCCESSFUL) | ||
| 150 | return result; | ||
| 151 | |||
| 152 | if (((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_1)) || | ||
| 153 | ((header_type != PCI_HEADER_TYPE_BRIDGE) && ((where >= PCI_BASE_ADDRESS_2) && | ||
| 154 | (where <= PCI_BASE_ADDRESS_5)))) | ||
| 155 | { | ||
| 156 | if ((*value & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) | ||
| 157 | { | ||
| 158 | /* | ||
| 159 | * Base address register that contains an I/O address. If the | ||
| 160 | * address is valid on the Hades (0 <= *value < HADES_VIRT_IO_SIZE), | ||
| 161 | * add 'pci_io_base_virt' to the value. | ||
| 162 | */ | ||
| 163 | |||
| 164 | if (*value < HADES_VIRT_IO_SIZE) | ||
| 165 | *value += pci_io_base_virt; | ||
| 166 | } | ||
| 167 | else | ||
| 168 | { | ||
| 169 | /* | ||
| 170 | * Base address register that contains an memory address. If the | ||
| 171 | * address is valid on the Hades (0 <= *value < HADES_MEM_SIZE), | ||
| 172 | * add HADES_MEM_BASE to the value. | ||
| 173 | */ | ||
| 174 | |||
| 175 | if (*value == 0) | ||
| 176 | { | ||
| 177 | /* | ||
| 178 | * Base address is 0. Test if this base | ||
| 179 | * address register is used. | ||
| 180 | */ | ||
| 181 | |||
| 182 | *pci_addr = 0xffffffff; | ||
| 183 | if (*pci_addr != 0) | ||
| 184 | { | ||
| 185 | *pci_addr = *value; | ||
| 186 | if (*value < HADES_MEM_SIZE) | ||
| 187 | *value += HADES_MEM_BASE; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | else | ||
| 191 | { | ||
| 192 | if (*value < HADES_MEM_SIZE) | ||
| 193 | *value += HADES_MEM_BASE; | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | return PCIBIOS_SUCCESSFUL; | ||
| 199 | } | ||
| 200 | |||
| 201 | static int hades_write_config_byte(struct pci_dev *dev, int where, u8 value) | ||
| 202 | { | ||
| 203 | volatile unsigned char *pci_addr; | ||
| 204 | |||
| 205 | if ((pci_addr = (unsigned char *) mk_conf_addr(dev, where)) == NULL) | ||
| 206 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
| 207 | |||
| 208 | *pci_addr = value; | ||
| 209 | |||
| 210 | return PCIBIOS_SUCCESSFUL; | ||
| 211 | } | ||
| 212 | |||
| 213 | static int hades_write_config_word(struct pci_dev *dev, int where, u16 value) | ||
| 214 | { | ||
| 215 | volatile unsigned short *pci_addr; | ||
| 216 | |||
| 217 | if ((pci_addr = (unsigned short *) mk_conf_addr(dev, where)) == NULL) | ||
| 218 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
| 219 | |||
| 220 | *pci_addr = cpu_to_le16(value); | ||
| 221 | |||
| 222 | return PCIBIOS_SUCCESSFUL; | ||
| 223 | } | ||
| 224 | |||
| 225 | static int hades_write_config_dword(struct pci_dev *dev, int where, u32 value) | ||
| 226 | { | ||
| 227 | volatile unsigned int *pci_addr; | ||
| 228 | unsigned char header_type; | ||
| 229 | int result; | ||
| 230 | |||
| 231 | if ((pci_addr = (unsigned int *) mk_conf_addr(dev, where)) == NULL) | ||
| 232 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
| 233 | |||
| 234 | /* | ||
| 235 | * Check if the value is an address on the bus. If true, subtract the | ||
| 236 | * base address of the PCI memory or PCI I/O area on the Hades. | ||
| 237 | */ | ||
| 238 | |||
| 239 | if ((result = hades_read_config_byte(dev, PCI_HEADER_TYPE, | ||
| 240 | &header_type)) != PCIBIOS_SUCCESSFUL) | ||
| 241 | return result; | ||
| 242 | |||
| 243 | if (((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_1)) || | ||
| 244 | ((header_type != PCI_HEADER_TYPE_BRIDGE) && ((where >= PCI_BASE_ADDRESS_2) && | ||
| 245 | (where <= PCI_BASE_ADDRESS_5)))) | ||
| 246 | { | ||
| 247 | if ((value & PCI_BASE_ADDRESS_SPACE) == | ||
| 248 | PCI_BASE_ADDRESS_SPACE_IO) | ||
| 249 | { | ||
| 250 | /* | ||
| 251 | * I/O address. Check if the address is valid address on | ||
| 252 | * the Hades (pci_io_base_virt <= value < pci_io_base_virt + | ||
| 253 | * HADES_VIRT_IO_SIZE) or if the value is 0xffffffff. If not | ||
| 254 | * true do not write the base address register. If it is a | ||
| 255 | * valid base address subtract 'pci_io_base_virt' from the value. | ||
| 256 | */ | ||
| 257 | |||
| 258 | if ((value >= pci_io_base_virt) && (value < (pci_io_base_virt + | ||
| 259 | HADES_VIRT_IO_SIZE))) | ||
| 260 | value -= pci_io_base_virt; | ||
| 261 | else | ||
| 262 | { | ||
| 263 | if (value != 0xffffffff) | ||
| 264 | return PCIBIOS_SET_FAILED; | ||
| 265 | } | ||
| 266 | } | ||
| 267 | else | ||
| 268 | { | ||
| 269 | /* | ||
| 270 | * Memory address. Check if the address is valid address on | ||
| 271 | * the Hades (HADES_MEM_BASE <= value < HADES_MEM_BASE + HADES_MEM_SIZE) or | ||
| 272 | * if the value is 0xffffffff. If not true do not write | ||
| 273 | * the base address register. If it is a valid base address | ||
| 274 | * subtract HADES_MEM_BASE from the value. | ||
| 275 | */ | ||
| 276 | |||
| 277 | if ((value >= HADES_MEM_BASE) && (value < (HADES_MEM_BASE + HADES_MEM_SIZE))) | ||
| 278 | value -= HADES_MEM_BASE; | ||
| 279 | else | ||
| 280 | { | ||
| 281 | if (value != 0xffffffff) | ||
| 282 | return PCIBIOS_SET_FAILED; | ||
| 283 | } | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | *pci_addr = cpu_to_le32(value); | ||
| 288 | |||
| 289 | return PCIBIOS_SUCCESSFUL; | ||
| 290 | } | ||
| 291 | |||
| 292 | /* | ||
| 293 | * static inline void hades_fixup(void) | ||
| 294 | * | ||
| 295 | * Assign IRQ numbers as used by Linux to the interrupt pins | ||
| 296 | * of the PCI cards. | ||
| 297 | */ | ||
| 298 | |||
| 299 | static void __init hades_fixup(int pci_modify) | ||
| 300 | { | ||
| 301 | char irq_tab[4] = { | ||
| 302 | [0] = IRQ_TT_MFP_IO0, /* Slot 0. */ | ||
| 303 | [1] = IRQ_TT_MFP_IO1, /* Slot 1. */ | ||
| 304 | [2] = IRQ_TT_MFP_SCC, /* Slot 2. */ | ||
| 305 | [3] = IRQ_TT_MFP_SCSIDMA /* Slot 3. */ | ||
| 306 | }; | ||
| 307 | struct pci_dev *dev = NULL; | ||
| 308 | unsigned char slot; | ||
| 309 | |||
| 310 | /* | ||
| 311 | * Go through all devices, fixing up irqs as we see fit: | ||
| 312 | */ | ||
| 313 | |||
| 314 | while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) | ||
| 315 | { | ||
| 316 | if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) | ||
| 317 | { | ||
| 318 | slot = PCI_SLOT(dev->devfn); /* Determine slot number. */ | ||
| 319 | dev->irq = irq_tab[slot]; | ||
| 320 | if (pci_modify) | ||
| 321 | pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); | ||
| 322 | } | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | /* | ||
| 327 | * static void hades_conf_device(struct pci_dev *dev) | ||
| 328 | * | ||
| 329 | * Machine dependent Configure the given device. | ||
| 330 | * | ||
| 331 | * Parameters: | ||
| 332 | * | ||
| 333 | * dev - the pci device. | ||
| 334 | */ | ||
| 335 | |||
| 336 | static void __init hades_conf_device(struct pci_dev *dev) | ||
| 337 | { | ||
| 338 | pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0); | ||
| 339 | } | ||
| 340 | |||
| 341 | static struct pci_ops hades_pci_ops = { | ||
| 342 | .read_byte = hades_read_config_byte, | ||
| 343 | .read_word = hades_read_config_word, | ||
| 344 | .read_dword = hades_read_config_dword, | ||
| 345 | .write_byte = hades_write_config_byte, | ||
| 346 | .write_word = hades_write_config_word, | ||
| 347 | .write_dword = hades_write_config_dword | ||
| 348 | }; | ||
| 349 | |||
| 350 | /* | ||
| 351 | * struct pci_bus_info *init_hades_pci(void) | ||
| 352 | * | ||
| 353 | * Machine specific initialisation: | ||
| 354 | * | ||
| 355 | * - Allocate and initialise a 'pci_bus_info' structure | ||
| 356 | * - Initialise hardware | ||
| 357 | * | ||
| 358 | * Result: pointer to 'pci_bus_info' structure. | ||
| 359 | */ | ||
| 360 | |||
| 361 | struct pci_bus_info * __init init_hades_pci(void) | ||
| 362 | { | ||
| 363 | struct pci_bus_info *bus; | ||
| 364 | int i; | ||
| 365 | |||
| 366 | /* | ||
| 367 | * Remap I/O and configuration space. | ||
| 368 | */ | ||
| 369 | |||
| 370 | pci_io_base_virt = (unsigned long) ioremap(HADES_IO_BASE, HADES_VIRT_IO_SIZE); | ||
| 371 | |||
| 372 | for (i = 0; i < N_SLOTS; i++) | ||
| 373 | pci_conf_base_virt[i] = (unsigned long) ioremap(pci_conf_base_phys[i], 0x10000); | ||
| 374 | |||
| 375 | /* | ||
| 376 | * Allocate memory for bus info structure. | ||
| 377 | */ | ||
| 378 | |||
| 379 | bus = kmalloc(sizeof(struct pci_bus_info), GFP_KERNEL); | ||
| 380 | if (!bus) | ||
| 381 | return NULL; | ||
| 382 | memset(bus, 0, sizeof(struct pci_bus_info)); | ||
| 383 | |||
| 384 | /* | ||
| 385 | * Claim resources. The m68k has no separate I/O space, both | ||
| 386 | * PCI memory space and PCI I/O space are in memory space. Therefore | ||
| 387 | * the I/O resources are requested in memory space as well. | ||
| 388 | */ | ||
| 389 | |||
| 390 | if (request_resource(&iomem_resource, &config_space) != 0) | ||
| 391 | { | ||
| 392 | kfree(bus); | ||
| 393 | return NULL; | ||
| 394 | } | ||
| 395 | |||
| 396 | if (request_resource(&iomem_resource, &io_space) != 0) | ||
| 397 | { | ||
| 398 | release_resource(&config_space); | ||
| 399 | kfree(bus); | ||
| 400 | return NULL; | ||
| 401 | } | ||
| 402 | |||
| 403 | bus->mem_space.start = HADES_MEM_BASE; | ||
| 404 | bus->mem_space.end = HADES_MEM_BASE + HADES_MEM_SIZE - 1; | ||
| 405 | bus->mem_space.name = pci_mem_name; | ||
| 406 | #if 1 | ||
| 407 | if (request_resource(&iomem_resource, &bus->mem_space) != 0) | ||
| 408 | { | ||
| 409 | release_resource(&io_space); | ||
| 410 | release_resource(&config_space); | ||
| 411 | kfree(bus); | ||
| 412 | return NULL; | ||
| 413 | } | ||
| 414 | #endif | ||
| 415 | bus->io_space.start = pci_io_base_virt; | ||
| 416 | bus->io_space.end = pci_io_base_virt + HADES_VIRT_IO_SIZE - 1; | ||
| 417 | bus->io_space.name = pci_io_name; | ||
| 418 | #if 1 | ||
| 419 | if (request_resource(&ioport_resource, &bus->io_space) != 0) | ||
| 420 | { | ||
| 421 | release_resource(&bus->mem_space); | ||
| 422 | release_resource(&io_space); | ||
| 423 | release_resource(&config_space); | ||
| 424 | kfree(bus); | ||
| 425 | return NULL; | ||
| 426 | } | ||
| 427 | #endif | ||
| 428 | /* | ||
| 429 | * Set hardware dependent functions. | ||
| 430 | */ | ||
| 431 | |||
| 432 | bus->m68k_pci_ops = &hades_pci_ops; | ||
| 433 | bus->fixup = hades_fixup; | ||
| 434 | bus->conf_device = hades_conf_device; | ||
| 435 | |||
| 436 | /* | ||
| 437 | * Select high to low edge for PCI interrupts. | ||
| 438 | */ | ||
| 439 | |||
| 440 | tt_mfp.active_edge &= ~0x27; | ||
| 441 | |||
| 442 | return bus; | ||
| 443 | } | ||
| 444 | #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 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/m68k/atari/stmda.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 1994 Roman Hodek | ||
| 5 | * | ||
| 6 | * | ||
| 7 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 8 | * License. See the file COPYING in the main directory of this archive | ||
| 9 | * for more details. | ||
| 10 | */ | ||
| 11 | |||
| 12 | |||
| 13 | /* This file contains some function for controlling the access to the */ | ||
| 14 | /* ST-DMA chip that may be shared between devices. Currently we have: */ | ||
| 15 | /* TT: Floppy and ACSI bus */ | ||
| 16 | /* Falcon: Floppy and SCSI */ | ||
| 17 | /* */ | ||
| 18 | /* The controlling functions set up a wait queue for access to the */ | ||
| 19 | /* ST-DMA chip. Callers to stdma_lock() that cannot granted access are */ | ||
| 20 | /* put onto a queue and waked up later if the owner calls */ | ||
| 21 | /* stdma_release(). Additionally, the caller gives his interrupt */ | ||
| 22 | /* service routine to stdma_lock(). */ | ||
| 23 | /* */ | ||
| 24 | /* On the Falcon, the IDE bus uses just the ACSI/Floppy interrupt, but */ | ||
| 25 | /* not the ST-DMA chip itself. So falhd.c needs not to lock the */ | ||
| 26 | /* chip. The interrupt is routed to falhd.c if IDE is configured, the */ | ||
| 27 | /* model is a Falcon and the interrupt was caused by the HD controller */ | ||
| 28 | /* (can be determined by looking at its status register). */ | ||
| 29 | |||
| 30 | |||
| 31 | #include <linux/types.h> | ||
| 32 | #include <linux/kdev_t.h> | ||
| 33 | #include <linux/genhd.h> | ||
| 34 | #include <linux/sched.h> | ||
| 35 | #include <linux/init.h> | ||
| 36 | #include <linux/interrupt.h> | ||
| 37 | #include <linux/wait.h> | ||
| 38 | |||
| 39 | #include <asm/atari_stdma.h> | ||
| 40 | #include <asm/atariints.h> | ||
| 41 | #include <asm/atarihw.h> | ||
| 42 | #include <asm/io.h> | ||
| 43 | #include <asm/irq.h> | ||
| 44 | |||
| 45 | static int stdma_locked; /* the semaphore */ | ||
| 46 | /* int func to be called */ | ||
| 47 | static irqreturn_t (*stdma_isr)(int, void *, struct pt_regs *); | ||
| 48 | static void *stdma_isr_data; /* data passed to isr */ | ||
| 49 | static DECLARE_WAIT_QUEUE_HEAD(stdma_wait); /* wait queue for ST-DMA */ | ||
| 50 | |||
| 51 | |||
| 52 | |||
| 53 | |||
| 54 | /***************************** Prototypes *****************************/ | ||
| 55 | |||
| 56 | static irqreturn_t stdma_int (int irq, void *dummy, struct pt_regs *fp); | ||
| 57 | |||
| 58 | /************************* End of Prototypes **************************/ | ||
| 59 | |||
| 60 | |||
| 61 | |||
| 62 | /* | ||
| 63 | * Function: void stdma_lock( isrfunc isr, void *data ) | ||
| 64 | * | ||
| 65 | * Purpose: Tries to get a lock on the ST-DMA chip that is used by more | ||
| 66 | * then one device driver. Waits on stdma_wait until lock is free. | ||
| 67 | * stdma_lock() may not be called from an interrupt! You have to | ||
| 68 | * get the lock in your main routine and release it when your | ||
| 69 | * request is finished. | ||
| 70 | * | ||
| 71 | * Inputs: A interrupt function that is called until the lock is | ||
| 72 | * released. | ||
| 73 | * | ||
| 74 | * Returns: nothing | ||
| 75 | * | ||
| 76 | */ | ||
| 77 | |||
| 78 | void stdma_lock(irqreturn_t (*handler)(int, void *, struct pt_regs *), | ||
| 79 | void *data) | ||
| 80 | { | ||
| 81 | unsigned long flags; | ||
| 82 | |||
| 83 | local_irq_save(flags); /* protect lock */ | ||
| 84 | |||
| 85 | /* Since the DMA is used for file system purposes, we | ||
| 86 | have to sleep uninterruptible (there may be locked | ||
| 87 | buffers) */ | ||
| 88 | wait_event(stdma_wait, !stdma_locked); | ||
| 89 | |||
| 90 | stdma_locked = 1; | ||
| 91 | stdma_isr = handler; | ||
| 92 | stdma_isr_data = data; | ||
| 93 | local_irq_restore(flags); | ||
| 94 | } | ||
| 95 | |||
| 96 | |||
| 97 | /* | ||
| 98 | * Function: void stdma_release( void ) | ||
| 99 | * | ||
| 100 | * Purpose: Releases the lock on the ST-DMA chip. | ||
| 101 | * | ||
| 102 | * Inputs: none | ||
| 103 | * | ||
| 104 | * Returns: nothing | ||
| 105 | * | ||
| 106 | */ | ||
| 107 | |||
| 108 | void stdma_release(void) | ||
| 109 | { | ||
| 110 | unsigned long flags; | ||
| 111 | |||
| 112 | local_irq_save(flags); | ||
| 113 | |||
| 114 | stdma_locked = 0; | ||
| 115 | stdma_isr = NULL; | ||
| 116 | stdma_isr_data = NULL; | ||
| 117 | wake_up(&stdma_wait); | ||
| 118 | |||
| 119 | local_irq_restore(flags); | ||
| 120 | } | ||
| 121 | |||
| 122 | |||
| 123 | /* | ||
| 124 | * Function: int stdma_others_waiting( void ) | ||
| 125 | * | ||
| 126 | * Purpose: Check if someone waits for the ST-DMA lock. | ||
| 127 | * | ||
| 128 | * Inputs: none | ||
| 129 | * | ||
| 130 | * Returns: 0 if no one is waiting, != 0 otherwise | ||
| 131 | * | ||
| 132 | */ | ||
| 133 | |||
| 134 | int stdma_others_waiting(void) | ||
| 135 | { | ||
| 136 | return waitqueue_active(&stdma_wait); | ||
| 137 | } | ||
| 138 | |||
| 139 | |||
| 140 | /* | ||
| 141 | * Function: int stdma_islocked( void ) | ||
| 142 | * | ||
| 143 | * Purpose: Check if the ST-DMA is currently locked. | ||
| 144 | * Note: Returned status is only valid if ints are disabled while calling and | ||
| 145 | * as long as they remain disabled. | ||
| 146 | * If called with ints enabled, status can change only from locked to | ||
| 147 | * unlocked, because ints may not lock the ST-DMA. | ||
| 148 | * | ||
| 149 | * Inputs: none | ||
| 150 | * | ||
| 151 | * Returns: != 0 if locked, 0 otherwise | ||
| 152 | * | ||
| 153 | */ | ||
| 154 | |||
| 155 | int stdma_islocked(void) | ||
| 156 | { | ||
| 157 | return stdma_locked; | ||
| 158 | } | ||
| 159 | |||
| 160 | |||
| 161 | /* | ||
| 162 | * Function: void stdma_init( void ) | ||
| 163 | * | ||
| 164 | * Purpose: Initialize the ST-DMA chip access controlling. | ||
| 165 | * It sets up the interrupt and its service routine. The int is registered | ||
| 166 | * as slow int, client devices have to live with that (no problem | ||
| 167 | * currently). | ||
| 168 | * | ||
| 169 | * Inputs: none | ||
| 170 | * | ||
| 171 | * Return: nothing | ||
| 172 | * | ||
| 173 | */ | ||
| 174 | |||
| 175 | void __init stdma_init(void) | ||
| 176 | { | ||
| 177 | stdma_isr = NULL; | ||
| 178 | request_irq(IRQ_MFP_FDC, stdma_int, IRQ_TYPE_SLOW, | ||
| 179 | "ST-DMA: floppy/ACSI/IDE/Falcon-SCSI", stdma_int); | ||
| 180 | } | ||
| 181 | |||
| 182 | |||
| 183 | /* | ||
| 184 | * Function: void stdma_int() | ||
| 185 | * | ||
| 186 | * Purpose: The interrupt routine for the ST-DMA. It calls the isr | ||
| 187 | * registered by stdma_lock(). | ||
| 188 | * | ||
| 189 | */ | ||
| 190 | |||
| 191 | static irqreturn_t stdma_int(int irq, void *dummy, struct pt_regs *fp) | ||
| 192 | { | ||
| 193 | if (stdma_isr) | ||
| 194 | (*stdma_isr)(irq, stdma_isr_data, fp); | ||
| 195 | return IRQ_HANDLED; | ||
| 196 | } | ||
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 @@ | |||
| 1 | /* | ||
| 2 | * arch/m68k/atari/stram.c: Functions for ST-RAM allocations | ||
| 3 | * | ||
| 4 | * Copyright 1994-97 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> | ||
| 5 | * | ||
| 6 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 7 | * License. See the file COPYING in the main directory of this archive | ||
| 8 | * for more details. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/config.h> | ||
| 12 | #include <linux/types.h> | ||
| 13 | #include <linux/kernel.h> | ||
| 14 | #include <linux/mm.h> | ||
| 15 | #include <linux/kdev_t.h> | ||
| 16 | #include <linux/major.h> | ||
| 17 | #include <linux/init.h> | ||
| 18 | #include <linux/swap.h> | ||
| 19 | #include <linux/slab.h> | ||
| 20 | #include <linux/vmalloc.h> | ||
| 21 | #include <linux/pagemap.h> | ||
| 22 | #include <linux/shm.h> | ||
| 23 | #include <linux/bootmem.h> | ||
| 24 | #include <linux/mount.h> | ||
| 25 | #include <linux/blkdev.h> | ||
| 26 | |||
| 27 | #include <asm/setup.h> | ||
| 28 | #include <asm/machdep.h> | ||
| 29 | #include <asm/page.h> | ||
| 30 | #include <asm/pgtable.h> | ||
| 31 | #include <asm/atarihw.h> | ||
| 32 | #include <asm/atari_stram.h> | ||
| 33 | #include <asm/io.h> | ||
| 34 | #include <asm/semaphore.h> | ||
| 35 | |||
| 36 | #include <linux/swapops.h> | ||
| 37 | |||
| 38 | #undef DEBUG | ||
| 39 | |||
| 40 | #ifdef DEBUG | ||
| 41 | #define DPRINTK(fmt,args...) printk( fmt, ##args ) | ||
| 42 | #else | ||
| 43 | #define DPRINTK(fmt,args...) | ||
| 44 | #endif | ||
| 45 | |||
| 46 | #if defined(CONFIG_PROC_FS) && defined(CONFIG_STRAM_PROC) | ||
| 47 | /* abbrev for the && above... */ | ||
| 48 | #define DO_PROC | ||
| 49 | #include <linux/proc_fs.h> | ||
| 50 | #endif | ||
| 51 | |||
| 52 | /* Pre-swapping comments: | ||
| 53 | * | ||
| 54 | * ++roman: | ||
| 55 | * | ||
| 56 | * New version of ST-Ram buffer allocation. Instead of using the | ||
| 57 | * 1 MB - 4 KB that remain when the ST-Ram chunk starts at $1000 | ||
| 58 | * (1 MB granularity!), such buffers are reserved like this: | ||
| 59 | * | ||
| 60 | * - If the kernel resides in ST-Ram anyway, we can take the buffer | ||
| 61 | * from behind the current kernel data space the normal way | ||
| 62 | * (incrementing start_mem). | ||
| 63 | * | ||
| 64 | * - If the kernel is in TT-Ram, stram_init() initializes start and | ||
| 65 | * end of the available region. Buffers are allocated from there | ||
| 66 | * and mem_init() later marks the such used pages as reserved. | ||
| 67 | * Since each TT-Ram chunk is at least 4 MB in size, I hope there | ||
| 68 | * won't be an overrun of the ST-Ram region by normal kernel data | ||
| 69 | * space. | ||
| 70 | * | ||
| 71 | * For that, ST-Ram may only be allocated while kernel initialization | ||
| 72 | * is going on, or exactly: before mem_init() is called. There is also | ||
| 73 | * no provision now for freeing ST-Ram buffers. It seems that isn't | ||
| 74 | * really needed. | ||
| 75 | * | ||
| 76 | */ | ||
| 77 | |||
| 78 | /* | ||
| 79 | * New Nov 1997: Use ST-RAM as swap space! | ||
| 80 | * | ||
| 81 | * In the past, there were often problems with modules that require ST-RAM | ||
| 82 | * buffers. Such drivers have to use __get_dma_pages(), which unfortunately | ||
| 83 | * often isn't very successful in allocating more than 1 page :-( [1] The net | ||
| 84 | * result was that most of the time you couldn't insmod such modules (ataflop, | ||
| 85 | * ACSI, SCSI on Falcon, Atari internal framebuffer, not to speak of acsi_slm, | ||
| 86 | * which needs a 1 MB buffer... :-). | ||
| 87 | * | ||
| 88 | * To overcome this limitation, ST-RAM can now be turned into a very | ||
| 89 | * high-speed swap space. If a request for an ST-RAM buffer comes, the kernel | ||
| 90 | * now tries to unswap some pages on that swap device to make some free (and | ||
| 91 | * contiguous) space. This works much better in comparison to | ||
| 92 | * __get_dma_pages(), since used swap pages can be selectively freed by either | ||
| 93 | * moving them to somewhere else in swap space, or by reading them back into | ||
| 94 | * system memory. Ok, there operation of unswapping isn't really cheap (for | ||
| 95 | * each page, one has to go through the page tables of all processes), but it | ||
| 96 | * doesn't happen that often (only when allocation ST-RAM, i.e. when loading a | ||
| 97 | * module that needs ST-RAM). But it at least makes it possible to load such | ||
| 98 | * modules! | ||
| 99 | * | ||
| 100 | * It could also be that overall system performance increases a bit due to | ||
| 101 | * ST-RAM swapping, since slow ST-RAM isn't used anymore for holding data or | ||
| 102 | * executing code in. It's then just a (very fast, compared to disk) back | ||
| 103 | * storage for not-so-often needed data. (But this effect must be compared | ||
| 104 | * with the loss of total memory...) Don't know if the effect is already | ||
| 105 | * visible on a TT, where the speed difference between ST- and TT-RAM isn't | ||
| 106 | * that dramatic, but it should on machines where TT-RAM is really much faster | ||
| 107 | * (e.g. Afterburner). | ||
| 108 | * | ||
| 109 | * [1]: __get_free_pages() does a fine job if you only want one page, but if | ||
| 110 | * you want more (contiguous) pages, it can give you such a block only if | ||
| 111 | * there's already a free one. The algorithm can't try to free buffers or swap | ||
| 112 | * out something in order to make more free space, since all that page-freeing | ||
| 113 | * mechanisms work "target-less", i.e. they just free something, but not in a | ||
| 114 | * specific place. I.e., __get_free_pages() can't do anything to free | ||
| 115 | * *adjacent* pages :-( This situation becomes even worse for DMA memory, | ||
| 116 | * since the freeing algorithms are also blind to DMA capability of pages. | ||
| 117 | */ | ||
| 118 | |||
| 119 | /* 1998-10-20: ++andreas | ||
| 120 | unswap_by_move disabled because it does not handle swapped shm pages. | ||
| 121 | */ | ||
| 122 | |||
| 123 | /* 2000-05-01: ++andreas | ||
| 124 | Integrated with bootmem. Remove all traces of unswap_by_move. | ||
| 125 | */ | ||
| 126 | |||
| 127 | #ifdef CONFIG_STRAM_SWAP | ||
| 128 | #define ALIGN_IF_SWAP(x) PAGE_ALIGN(x) | ||
| 129 | #else | ||
| 130 | #define ALIGN_IF_SWAP(x) (x) | ||
| 131 | #endif | ||
| 132 | |||
| 133 | /* get index of swap page at address 'addr' */ | ||
| 134 | #define SWAP_NR(addr) (((addr) - swap_start) >> PAGE_SHIFT) | ||
| 135 | |||
| 136 | /* get address of swap page #'nr' */ | ||
| 137 | #define SWAP_ADDR(nr) (swap_start + ((nr) << PAGE_SHIFT)) | ||
| 138 | |||
| 139 | /* get number of pages for 'n' bytes (already page-aligned) */ | ||
| 140 | #define N_PAGES(n) ((n) >> PAGE_SHIFT) | ||
| 141 | |||
| 142 | /* The following two numbers define the maximum fraction of ST-RAM in total | ||
| 143 | * memory, below that the kernel would automatically use ST-RAM as swap | ||
| 144 | * space. This decision can be overridden with stram_swap= */ | ||
| 145 | #define MAX_STRAM_FRACTION_NOM 1 | ||
| 146 | #define MAX_STRAM_FRACTION_DENOM 3 | ||
| 147 | |||
| 148 | /* Start and end (virtual) of ST-RAM */ | ||
| 149 | static void *stram_start, *stram_end; | ||
| 150 | |||
| 151 | /* set after memory_init() executed and allocations via start_mem aren't | ||
| 152 | * possible anymore */ | ||
| 153 | static int mem_init_done; | ||
| 154 | |||
| 155 | /* set if kernel is in ST-RAM */ | ||
| 156 | static int kernel_in_stram; | ||
| 157 | |||
| 158 | typedef struct stram_block { | ||
| 159 | struct stram_block *next; | ||
| 160 | void *start; | ||
| 161 | unsigned long size; | ||
| 162 | unsigned flags; | ||
| 163 | const char *owner; | ||
| 164 | } BLOCK; | ||
| 165 | |||
| 166 | /* values for flags field */ | ||
| 167 | #define BLOCK_FREE 0x01 /* free structure in the BLOCKs pool */ | ||
| 168 | #define BLOCK_KMALLOCED 0x02 /* structure allocated by kmalloc() */ | ||
| 169 | #define BLOCK_GFP 0x08 /* block allocated with __get_dma_pages() */ | ||
| 170 | #define BLOCK_INSWAP 0x10 /* block allocated in swap space */ | ||
| 171 | |||
| 172 | /* list of allocated blocks */ | ||
| 173 | static BLOCK *alloc_list; | ||
| 174 | |||
| 175 | /* We can't always use kmalloc() to allocate BLOCK structures, since | ||
| 176 | * stram_alloc() can be called rather early. So we need some pool of | ||
| 177 | * statically allocated structures. 20 of them is more than enough, so in most | ||
| 178 | * cases we never should need to call kmalloc(). */ | ||
| 179 | #define N_STATIC_BLOCKS 20 | ||
| 180 | static BLOCK static_blocks[N_STATIC_BLOCKS]; | ||
| 181 | |||
| 182 | #ifdef CONFIG_STRAM_SWAP | ||
| 183 | /* max. number of bytes to use for swapping | ||
| 184 | * 0 = no ST-RAM swapping | ||
| 185 | * -1 = do swapping (to whole ST-RAM) if it's less than MAX_STRAM_FRACTION of | ||
| 186 | * total memory | ||
| 187 | */ | ||
| 188 | static int max_swap_size = -1; | ||
| 189 | |||
| 190 | /* start and end of swapping area */ | ||
| 191 | static void *swap_start, *swap_end; | ||
| 192 | |||
| 193 | /* The ST-RAM's swap info structure */ | ||
| 194 | static struct swap_info_struct *stram_swap_info; | ||
| 195 | |||
| 196 | /* The ST-RAM's swap type */ | ||
| 197 | static int stram_swap_type; | ||
| 198 | |||
| 199 | /* Semaphore for get_stram_region. */ | ||
| 200 | static DECLARE_MUTEX(stram_swap_sem); | ||
| 201 | |||
| 202 | /* major and minor device number of the ST-RAM device; for the major, we use | ||
| 203 | * the same as Amiga z2ram, which is really similar and impossible on Atari, | ||
| 204 | * and for the minor a relatively odd number to avoid the user creating and | ||
| 205 | * using that device. */ | ||
| 206 | #define STRAM_MAJOR Z2RAM_MAJOR | ||
| 207 | #define STRAM_MINOR 13 | ||
| 208 | |||
| 209 | /* Some impossible pointer value */ | ||
| 210 | #define MAGIC_FILE_P (struct file *)0xffffdead | ||
| 211 | |||
| 212 | #ifdef DO_PROC | ||
| 213 | static unsigned stat_swap_read; | ||
| 214 | static unsigned stat_swap_write; | ||
| 215 | static unsigned stat_swap_force; | ||
| 216 | #endif /* DO_PROC */ | ||
| 217 | |||
| 218 | #endif /* CONFIG_STRAM_SWAP */ | ||
| 219 | |||
| 220 | /***************************** Prototypes *****************************/ | ||
| 221 | |||
| 222 | #ifdef CONFIG_STRAM_SWAP | ||
| 223 | static int swap_init(void *start_mem, void *swap_data); | ||
| 224 | static void *get_stram_region( unsigned long n_pages ); | ||
| 225 | static void free_stram_region( unsigned long offset, unsigned long n_pages | ||
| 226 | ); | ||
| 227 | static int in_some_region(void *addr); | ||
| 228 | static unsigned long find_free_region( unsigned long n_pages, unsigned long | ||
| 229 | *total_free, unsigned long | ||
| 230 | *region_free ); | ||
| 231 | static void do_stram_request(request_queue_t *); | ||
| 232 | static int stram_open( struct inode *inode, struct file *filp ); | ||
| 233 | static int stram_release( struct inode *inode, struct file *filp ); | ||
| 234 | static void reserve_region(void *start, void *end); | ||
| 235 | #endif | ||
| 236 | static BLOCK *add_region( void *addr, unsigned long size ); | ||
| 237 | static BLOCK *find_region( void *addr ); | ||
| 238 | static int remove_region( BLOCK *block ); | ||
| 239 | |||
| 240 | /************************* End of Prototypes **************************/ | ||
| 241 | |||
| 242 | |||
| 243 | /* ------------------------------------------------------------------------ */ | ||
| 244 | /* Public Interface */ | ||
| 245 | /* ------------------------------------------------------------------------ */ | ||
| 246 | |||
| 247 | /* | ||
| 248 | * This init function is called very early by atari/config.c | ||
| 249 | * It initializes some internal variables needed for stram_alloc() | ||
| 250 | */ | ||
| 251 | void __init atari_stram_init(void) | ||
| 252 | { | ||
| 253 | int i; | ||
| 254 | |||
| 255 | /* initialize static blocks */ | ||
| 256 | for( i = 0; i < N_STATIC_BLOCKS; ++i ) | ||
| 257 | static_blocks[i].flags = BLOCK_FREE; | ||
| 258 | |||
| 259 | /* determine whether kernel code resides in ST-RAM (then ST-RAM is the | ||
| 260 | * first memory block at virtual 0x0) */ | ||
| 261 | stram_start = phys_to_virt(0); | ||
| 262 | kernel_in_stram = (stram_start == 0); | ||
| 263 | |||
| 264 | for( i = 0; i < m68k_num_memory; ++i ) { | ||
| 265 | if (m68k_memory[i].addr == 0) { | ||
| 266 | /* skip first 2kB or page (supervisor-only!) */ | ||
| 267 | stram_end = stram_start + m68k_memory[i].size; | ||
| 268 | return; | ||
| 269 | } | ||
| 270 | } | ||
| 271 | /* Should never come here! (There is always ST-Ram!) */ | ||
| 272 | panic( "atari_stram_init: no ST-RAM found!" ); | ||
| 273 | } | ||
| 274 | |||
| 275 | |||
| 276 | /* | ||
| 277 | * This function is called from setup_arch() to reserve the pages needed for | ||
| 278 | * ST-RAM management. | ||
| 279 | */ | ||
| 280 | void __init atari_stram_reserve_pages(void *start_mem) | ||
| 281 | { | ||
| 282 | #ifdef CONFIG_STRAM_SWAP | ||
| 283 | /* if max_swap_size is negative (i.e. no stram_swap= option given), | ||
| 284 | * determine at run time whether to use ST-RAM swapping */ | ||
| 285 | if (max_swap_size < 0) | ||
| 286 | /* Use swapping if ST-RAM doesn't make up more than MAX_STRAM_FRACTION | ||
| 287 | * of total memory. In that case, the max. size is set to 16 MB, | ||
| 288 | * because ST-RAM can never be bigger than that. | ||
| 289 | * Also, never use swapping on a Hades, there's no separate ST-RAM in | ||
| 290 | * that machine. */ | ||
| 291 | max_swap_size = | ||
| 292 | (!MACH_IS_HADES && | ||
| 293 | (N_PAGES(stram_end-stram_start)*MAX_STRAM_FRACTION_DENOM <= | ||
| 294 | ((unsigned long)high_memory>>PAGE_SHIFT)*MAX_STRAM_FRACTION_NOM)) ? 16*1024*1024 : 0; | ||
| 295 | DPRINTK( "atari_stram_reserve_pages: max_swap_size = %d\n", max_swap_size ); | ||
| 296 | #endif | ||
| 297 | |||
| 298 | /* always reserve first page of ST-RAM, the first 2 kB are | ||
| 299 | * supervisor-only! */ | ||
| 300 | if (!kernel_in_stram) | ||
| 301 | reserve_bootmem (0, PAGE_SIZE); | ||
| 302 | |||
| 303 | #ifdef CONFIG_STRAM_SWAP | ||
| 304 | { | ||
| 305 | void *swap_data; | ||
| 306 | |||
| 307 | start_mem = (void *) PAGE_ALIGN ((unsigned long) start_mem); | ||
| 308 | /* determine first page to use as swap: if the kernel is | ||
| 309 | in TT-RAM, this is the first page of (usable) ST-RAM; | ||
| 310 | otherwise just use the end of kernel data (= start_mem) */ | ||
| 311 | swap_start = !kernel_in_stram ? stram_start + PAGE_SIZE : start_mem; | ||
| 312 | /* decrement by one page, rest of kernel assumes that first swap page | ||
| 313 | * is always reserved and maybe doesn't handle swp_entry == 0 | ||
| 314 | * correctly */ | ||
| 315 | swap_start -= PAGE_SIZE; | ||
| 316 | swap_end = stram_end; | ||
| 317 | if (swap_end-swap_start > max_swap_size) | ||
| 318 | swap_end = swap_start + max_swap_size; | ||
| 319 | DPRINTK( "atari_stram_reserve_pages: swapping enabled; " | ||
| 320 | "swap=%p-%p\n", swap_start, swap_end); | ||
| 321 | |||
| 322 | /* reserve some amount of memory for maintainance of | ||
| 323 | * swapping itself: one page for each 2048 (PAGE_SIZE/2) | ||
| 324 | * swap pages. (2 bytes for each page) */ | ||
| 325 | swap_data = start_mem; | ||
| 326 | start_mem += ((SWAP_NR(swap_end) + PAGE_SIZE/2 - 1) | ||
| 327 | >> (PAGE_SHIFT-1)) << PAGE_SHIFT; | ||
| 328 | /* correct swap_start if necessary */ | ||
| 329 | if (swap_start + PAGE_SIZE == swap_data) | ||
| 330 | swap_start = start_mem - PAGE_SIZE; | ||
| 331 | |||
| 332 | if (!swap_init( start_mem, swap_data )) { | ||
| 333 | printk( KERN_ERR "ST-RAM swap space initialization failed\n" ); | ||
| 334 | max_swap_size = 0; | ||
| 335 | return; | ||
| 336 | } | ||
| 337 | /* reserve region for swapping meta-data */ | ||
| 338 | reserve_region(swap_data, start_mem); | ||
| 339 | /* reserve swapping area itself */ | ||
| 340 | reserve_region(swap_start + PAGE_SIZE, swap_end); | ||
| 341 | |||
| 342 | /* | ||
| 343 | * If the whole ST-RAM is used for swapping, there are no allocatable | ||
| 344 | * dma pages left. But unfortunately, some shared parts of the kernel | ||
| 345 | * (particularly the SCSI mid-level) call __get_dma_pages() | ||
| 346 | * unconditionally :-( These calls then fail, and scsi.c even doesn't | ||
| 347 | * check for NULL return values and just crashes. The quick fix for | ||
| 348 | * this (instead of doing much clean up work in the SCSI code) is to | ||
| 349 | * pretend all pages are DMA-able by setting mach_max_dma_address to | ||
| 350 | * ULONG_MAX. This doesn't change any functionality so far, since | ||
| 351 | * get_dma_pages() shouldn't be used on Atari anyway anymore (better | ||
| 352 | * use atari_stram_alloc()), and the Atari SCSI drivers don't need DMA | ||
| 353 | * memory. But unfortunately there's now no kind of warning (even not | ||
| 354 | * a NULL return value) if you use get_dma_pages() nevertheless :-( | ||
| 355 | * You just will get non-DMA-able memory... | ||
| 356 | */ | ||
| 357 | mach_max_dma_address = 0xffffffff; | ||
| 358 | } | ||
| 359 | #endif | ||
| 360 | } | ||
| 361 | |||
| 362 | void atari_stram_mem_init_hook (void) | ||
| 363 | { | ||
| 364 | mem_init_done = 1; | ||
| 365 | } | ||
| 366 | |||
| 367 | |||
| 368 | /* | ||
| 369 | * This is main public interface: somehow allocate a ST-RAM block | ||
| 370 | * There are three strategies: | ||
| 371 | * | ||
| 372 | * - If we're before mem_init(), we have to make a static allocation. The | ||
| 373 | * region is taken in the kernel data area (if the kernel is in ST-RAM) or | ||
| 374 | * from the start of ST-RAM (if the kernel is in TT-RAM) and added to the | ||
| 375 | * rsvd_stram_* region. The ST-RAM is somewhere in the middle of kernel | ||
| 376 | * address space in the latter case. | ||
| 377 | * | ||
| 378 | * - If mem_init() already has been called and ST-RAM swapping is enabled, | ||
| 379 | * try to get the memory from the (pseudo) swap-space, either free already | ||
| 380 | * or by moving some other pages out of the swap. | ||
| 381 | * | ||
| 382 | * - If mem_init() already has been called, and ST-RAM swapping is not | ||
| 383 | * enabled, the only possibility is to try with __get_dma_pages(). This has | ||
| 384 | * the disadvantage that it's very hard to get more than 1 page, and it is | ||
| 385 | * likely to fail :-( | ||
| 386 | * | ||
| 387 | */ | ||
| 388 | void *atari_stram_alloc(long size, const char *owner) | ||
| 389 | { | ||
| 390 | void *addr = NULL; | ||
| 391 | BLOCK *block; | ||
| 392 | int flags; | ||
| 393 | |||
| 394 | DPRINTK("atari_stram_alloc(size=%08lx,owner=%s)\n", size, owner); | ||
| 395 | |||
| 396 | size = ALIGN_IF_SWAP(size); | ||
| 397 | DPRINTK( "atari_stram_alloc: rounded size = %08lx\n", size ); | ||
| 398 | #ifdef CONFIG_STRAM_SWAP | ||
| 399 | if (max_swap_size) { | ||
| 400 | /* If swapping is active: make some free space in the swap | ||
| 401 | "device". */ | ||
| 402 | DPRINTK( "atari_stram_alloc: after mem_init, swapping ok, " | ||
| 403 | "calling get_region\n" ); | ||
| 404 | addr = get_stram_region( N_PAGES(size) ); | ||
| 405 | flags = BLOCK_INSWAP; | ||
| 406 | } | ||
| 407 | else | ||
| 408 | #endif | ||
| 409 | if (!mem_init_done) | ||
| 410 | return alloc_bootmem_low(size); | ||
| 411 | else { | ||
| 412 | /* After mem_init() and no swapping: can only resort to | ||
| 413 | * __get_dma_pages() */ | ||
| 414 | addr = (void *)__get_dma_pages(GFP_KERNEL, get_order(size)); | ||
| 415 | flags = BLOCK_GFP; | ||
| 416 | DPRINTK( "atari_stram_alloc: after mem_init, swapping off, " | ||
| 417 | "get_pages=%p\n", addr ); | ||
| 418 | } | ||
| 419 | |||
| 420 | if (addr) { | ||
| 421 | if (!(block = add_region( addr, size ))) { | ||
| 422 | /* out of memory for BLOCK structure :-( */ | ||
| 423 | DPRINTK( "atari_stram_alloc: out of mem for BLOCK -- " | ||
| 424 | "freeing again\n" ); | ||
| 425 | #ifdef CONFIG_STRAM_SWAP | ||
| 426 | if (flags == BLOCK_INSWAP) | ||
| 427 | free_stram_region( SWAP_NR(addr), N_PAGES(size) ); | ||
| 428 | else | ||
| 429 | #endif | ||
| 430 | free_pages((unsigned long)addr, get_order(size)); | ||
| 431 | return( NULL ); | ||
| 432 | } | ||
| 433 | block->owner = owner; | ||
| 434 | block->flags |= flags; | ||
| 435 | } | ||
| 436 | return( addr ); | ||
| 437 | } | ||
| 438 | |||
| 439 | void atari_stram_free( void *addr ) | ||
| 440 | |||
| 441 | { | ||
| 442 | BLOCK *block; | ||
| 443 | |||
| 444 | DPRINTK( "atari_stram_free(addr=%p)\n", addr ); | ||
| 445 | |||
| 446 | if (!(block = find_region( addr ))) { | ||
| 447 | printk( KERN_ERR "Attempt to free non-allocated ST-RAM block at %p " | ||
| 448 | "from %p\n", addr, __builtin_return_address(0) ); | ||
| 449 | return; | ||
| 450 | } | ||
| 451 | DPRINTK( "atari_stram_free: found block (%p): size=%08lx, owner=%s, " | ||
| 452 | "flags=%02x\n", block, block->size, block->owner, block->flags ); | ||
| 453 | |||
| 454 | #ifdef CONFIG_STRAM_SWAP | ||
| 455 | if (!max_swap_size) { | ||
| 456 | #endif | ||
| 457 | if (block->flags & BLOCK_GFP) { | ||
| 458 | DPRINTK("atari_stram_free: is kmalloced, order_size=%d\n", | ||
| 459 | get_order(block->size)); | ||
| 460 | free_pages((unsigned long)addr, get_order(block->size)); | ||
| 461 | } | ||
| 462 | else | ||
| 463 | goto fail; | ||
| 464 | #ifdef CONFIG_STRAM_SWAP | ||
| 465 | } | ||
| 466 | else if (block->flags & BLOCK_INSWAP) { | ||
| 467 | DPRINTK( "atari_stram_free: is swap-alloced\n" ); | ||
| 468 | free_stram_region( SWAP_NR(block->start), N_PAGES(block->size) ); | ||
| 469 | } | ||
| 470 | else | ||
| 471 | goto fail; | ||
| 472 | #endif | ||
| 473 | remove_region( block ); | ||
| 474 | return; | ||
| 475 | |||
| 476 | fail: | ||
| 477 | printk( KERN_ERR "atari_stram_free: cannot free block at %p " | ||
| 478 | "(called from %p)\n", addr, __builtin_return_address(0) ); | ||
| 479 | } | ||
| 480 | |||
| 481 | |||
| 482 | #ifdef CONFIG_STRAM_SWAP | ||
| 483 | |||
| 484 | |||
| 485 | /* ------------------------------------------------------------------------ */ | ||
| 486 | /* Main Swapping Functions */ | ||
| 487 | /* ------------------------------------------------------------------------ */ | ||
| 488 | |||
| 489 | |||
| 490 | /* | ||
| 491 | * Initialize ST-RAM swap device | ||
| 492 | * (lots copied and modified from sys_swapon() in mm/swapfile.c) | ||
| 493 | */ | ||
| 494 | static int __init swap_init(void *start_mem, void *swap_data) | ||
| 495 | { | ||
| 496 | static struct dentry fake_dentry; | ||
| 497 | static struct vfsmount fake_vfsmnt; | ||
| 498 | struct swap_info_struct *p; | ||
| 499 | struct inode swap_inode; | ||
| 500 | unsigned int type; | ||
| 501 | void *addr; | ||
| 502 | int i, j, k, prev; | ||
| 503 | |||
| 504 | DPRINTK("swap_init(start_mem=%p, swap_data=%p)\n", | ||
| 505 | start_mem, swap_data); | ||
| 506 | |||
| 507 | /* need at least one page for swapping to (and this also isn't very | ||
| 508 | * much... :-) */ | ||
| 509 | if (swap_end - swap_start < 2*PAGE_SIZE) { | ||
| 510 | printk( KERN_WARNING "stram_swap_init: swap space too small\n" ); | ||
| 511 | return( 0 ); | ||
| 512 | } | ||
| 513 | |||
| 514 | /* find free slot in swap_info */ | ||
| 515 | for( p = swap_info, type = 0; type < nr_swapfiles; type++, p++ ) | ||
| 516 | if (!(p->flags & SWP_USED)) | ||
| 517 | break; | ||
| 518 | if (type >= MAX_SWAPFILES) { | ||
| 519 | printk( KERN_WARNING "stram_swap_init: max. number of " | ||
| 520 | "swap devices exhausted\n" ); | ||
| 521 | return( 0 ); | ||
| 522 | } | ||
| 523 | if (type >= nr_swapfiles) | ||
| 524 | nr_swapfiles = type+1; | ||
| 525 | |||
| 526 | stram_swap_info = p; | ||
| 527 | stram_swap_type = type; | ||
| 528 | |||
| 529 | /* fake some dir cache entries to give us some name in /dev/swaps */ | ||
| 530 | fake_dentry.d_parent = &fake_dentry; | ||
| 531 | fake_dentry.d_name.name = "stram (internal)"; | ||
| 532 | fake_dentry.d_name.len = 16; | ||
| 533 | fake_vfsmnt.mnt_parent = &fake_vfsmnt; | ||
| 534 | |||
| 535 | p->flags = SWP_USED; | ||
| 536 | p->swap_file = &fake_dentry; | ||
| 537 | p->swap_vfsmnt = &fake_vfsmnt; | ||
| 538 | p->swap_map = swap_data; | ||
| 539 | p->cluster_nr = 0; | ||
| 540 | p->next = -1; | ||
| 541 | p->prio = 0x7ff0; /* a rather high priority, but not the higest | ||
| 542 | * to give the user a chance to override */ | ||
| 543 | |||
| 544 | /* call stram_open() directly, avoids at least the overhead in | ||
| 545 | * constructing a dummy file structure... */ | ||
| 546 | swap_inode.i_rdev = MKDEV( STRAM_MAJOR, STRAM_MINOR ); | ||
| 547 | stram_open( &swap_inode, MAGIC_FILE_P ); | ||
| 548 | p->max = SWAP_NR(swap_end); | ||
| 549 | |||
| 550 | /* initialize swap_map: set regions that are already allocated or belong | ||
| 551 | * to kernel data space to SWAP_MAP_BAD, otherwise to free */ | ||
| 552 | j = 0; /* # of free pages */ | ||
| 553 | k = 0; /* # of already allocated pages (from pre-mem_init stram_alloc()) */ | ||
| 554 | p->lowest_bit = 0; | ||
| 555 | p->highest_bit = 0; | ||
| 556 | for( i = 1, addr = SWAP_ADDR(1); i < p->max; | ||
| 557 | i++, addr += PAGE_SIZE ) { | ||
| 558 | if (in_some_region( addr )) { | ||
| 559 | p->swap_map[i] = SWAP_MAP_BAD; | ||
| 560 | ++k; | ||
| 561 | } | ||
| 562 | else if (kernel_in_stram && addr < start_mem ) { | ||
| 563 | p->swap_map[i] = SWAP_MAP_BAD; | ||
| 564 | } | ||
| 565 | else { | ||
| 566 | p->swap_map[i] = 0; | ||
| 567 | ++j; | ||
| 568 | if (!p->lowest_bit) p->lowest_bit = i; | ||
| 569 | p->highest_bit = i; | ||
| 570 | } | ||
| 571 | } | ||
| 572 | /* first page always reserved (and doesn't really belong to swap space) */ | ||
| 573 | p->swap_map[0] = SWAP_MAP_BAD; | ||
| 574 | |||
| 575 | /* now swapping to this device ok */ | ||
| 576 | p->pages = j + k; | ||
| 577 | swap_list_lock(); | ||
| 578 | nr_swap_pages += j; | ||
| 579 | p->flags = SWP_WRITEOK; | ||
| 580 | |||
| 581 | /* insert swap space into swap_list */ | ||
| 582 | prev = -1; | ||
| 583 | for (i = swap_list.head; i >= 0; i = swap_info[i].next) { | ||
| 584 | if (p->prio >= swap_info[i].prio) { | ||
| 585 | break; | ||
| 586 | } | ||
| 587 | prev = i; | ||
| 588 | } | ||
| 589 | p->next = i; | ||
| 590 | if (prev < 0) { | ||
| 591 | swap_list.head = swap_list.next = p - swap_info; | ||
| 592 | } else { | ||
| 593 | swap_info[prev].next = p - swap_info; | ||
| 594 | } | ||
| 595 | swap_list_unlock(); | ||
| 596 | |||
| 597 | printk( KERN_INFO "Using %dk (%d pages) of ST-RAM as swap space.\n", | ||
| 598 | p->pages << 2, p->pages ); | ||
| 599 | return( 1 ); | ||
| 600 | } | ||
| 601 | |||
| 602 | |||
| 603 | /* | ||
| 604 | * The swap entry has been read in advance, and we return 1 to indicate | ||
| 605 | * that the page has been used or is no longer needed. | ||
| 606 | * | ||
| 607 | * Always set the resulting pte to be nowrite (the same as COW pages | ||
| 608 | * after one process has exited). We don't know just how many PTEs will | ||
| 609 | * share this swap entry, so be cautious and let do_wp_page work out | ||
| 610 | * what to do if a write is requested later. | ||
| 611 | */ | ||
| 612 | static inline void unswap_pte(struct vm_area_struct * vma, unsigned long | ||
| 613 | address, pte_t *dir, swp_entry_t entry, | ||
| 614 | struct page *page) | ||
| 615 | { | ||
| 616 | pte_t pte = *dir; | ||
| 617 | |||
| 618 | if (pte_none(pte)) | ||
| 619 | return; | ||
| 620 | if (pte_present(pte)) { | ||
| 621 | /* If this entry is swap-cached, then page must already | ||
| 622 | hold the right address for any copies in physical | ||
| 623 | memory */ | ||
| 624 | if (pte_page(pte) != page) | ||
| 625 | return; | ||
| 626 | /* We will be removing the swap cache in a moment, so... */ | ||
| 627 | set_pte(dir, pte_mkdirty(pte)); | ||
| 628 | return; | ||
| 629 | } | ||
| 630 | if (pte_val(pte) != entry.val) | ||
| 631 | return; | ||
| 632 | |||
| 633 | DPRINTK("unswap_pte: replacing entry %08lx by new page %p", | ||
| 634 | entry.val, page); | ||
| 635 | set_pte(dir, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); | ||
| 636 | swap_free(entry); | ||
| 637 | get_page(page); | ||
| 638 | inc_mm_counter(vma->vm_mm, rss); | ||
| 639 | } | ||
| 640 | |||
| 641 | static inline void unswap_pmd(struct vm_area_struct * vma, pmd_t *dir, | ||
| 642 | unsigned long address, unsigned long size, | ||
| 643 | unsigned long offset, swp_entry_t entry, | ||
| 644 | struct page *page) | ||
| 645 | { | ||
| 646 | pte_t * pte; | ||
| 647 | unsigned long end; | ||
| 648 | |||
| 649 | if (pmd_none(*dir)) | ||
| 650 | return; | ||
| 651 | if (pmd_bad(*dir)) { | ||
| 652 | pmd_ERROR(*dir); | ||
| 653 | pmd_clear(dir); | ||
| 654 | return; | ||
| 655 | } | ||
| 656 | pte = pte_offset_kernel(dir, address); | ||
| 657 | offset += address & PMD_MASK; | ||
| 658 | address &= ~PMD_MASK; | ||
| 659 | end = address + size; | ||
| 660 | if (end > PMD_SIZE) | ||
| 661 | end = PMD_SIZE; | ||
| 662 | do { | ||
| 663 | unswap_pte(vma, offset+address-vma->vm_start, pte, entry, page); | ||
| 664 | address += PAGE_SIZE; | ||
| 665 | pte++; | ||
| 666 | } while (address < end); | ||
| 667 | } | ||
| 668 | |||
| 669 | static inline void unswap_pgd(struct vm_area_struct * vma, pgd_t *dir, | ||
| 670 | unsigned long address, unsigned long size, | ||
| 671 | swp_entry_t entry, struct page *page) | ||
| 672 | { | ||
| 673 | pmd_t * pmd; | ||
| 674 | unsigned long offset, end; | ||
| 675 | |||
| 676 | if (pgd_none(*dir)) | ||
| 677 | return; | ||
| 678 | if (pgd_bad(*dir)) { | ||
| 679 | pgd_ERROR(*dir); | ||
| 680 | pgd_clear(dir); | ||
| 681 | return; | ||
| 682 | } | ||
| 683 | pmd = pmd_offset(dir, address); | ||
| 684 | offset = address & PGDIR_MASK; | ||
| 685 | address &= ~PGDIR_MASK; | ||
| 686 | end = address + size; | ||
| 687 | if (end > PGDIR_SIZE) | ||
| 688 | end = PGDIR_SIZE; | ||
| 689 | do { | ||
| 690 | unswap_pmd(vma, pmd, address, end - address, offset, entry, | ||
| 691 | page); | ||
| 692 | address = (address + PMD_SIZE) & PMD_MASK; | ||
| 693 | pmd++; | ||
| 694 | } while (address < end); | ||
| 695 | } | ||
| 696 | |||
| 697 | static void unswap_vma(struct vm_area_struct * vma, pgd_t *pgdir, | ||
| 698 | swp_entry_t entry, struct page *page) | ||
| 699 | { | ||
| 700 | unsigned long start = vma->vm_start, end = vma->vm_end; | ||
| 701 | |||
| 702 | do { | ||
| 703 | unswap_pgd(vma, pgdir, start, end - start, entry, page); | ||
| 704 | start = (start + PGDIR_SIZE) & PGDIR_MASK; | ||
| 705 | pgdir++; | ||
| 706 | } while (start < end); | ||
| 707 | } | ||
| 708 | |||
| 709 | static void unswap_process(struct mm_struct * mm, swp_entry_t entry, | ||
| 710 | struct page *page) | ||
| 711 | { | ||
| 712 | struct vm_area_struct* vma; | ||
| 713 | |||
| 714 | /* | ||
| 715 | * Go through process' page directory. | ||
| 716 | */ | ||
| 717 | if (!mm) | ||
| 718 | return; | ||
| 719 | for (vma = mm->mmap; vma; vma = vma->vm_next) { | ||
| 720 | pgd_t * pgd = pgd_offset(mm, vma->vm_start); | ||
| 721 | unswap_vma(vma, pgd, entry, page); | ||
| 722 | } | ||
| 723 | } | ||
| 724 | |||
| 725 | |||
| 726 | static int unswap_by_read(unsigned short *map, unsigned long max, | ||
| 727 | unsigned long start, unsigned long n_pages) | ||
| 728 | { | ||
| 729 | struct task_struct *p; | ||
| 730 | struct page *page; | ||
| 731 | swp_entry_t entry; | ||
| 732 | unsigned long i; | ||
| 733 | |||
| 734 | DPRINTK( "unswapping %lu..%lu by reading in\n", | ||
| 735 | start, start+n_pages-1 ); | ||
| 736 | |||
| 737 | for( i = start; i < start+n_pages; ++i ) { | ||
| 738 | if (map[i] == SWAP_MAP_BAD) { | ||
| 739 | printk( KERN_ERR "get_stram_region: page %lu already " | ||
| 740 | "reserved??\n", i ); | ||
| 741 | continue; | ||
| 742 | } | ||
| 743 | |||
| 744 | if (map[i]) { | ||
| 745 | entry = swp_entry(stram_swap_type, i); | ||
| 746 | DPRINTK("unswap: map[i=%lu]=%u nr_swap=%ld\n", | ||
| 747 | i, map[i], nr_swap_pages); | ||
| 748 | |||
| 749 | swap_device_lock(stram_swap_info); | ||
| 750 | map[i]++; | ||
| 751 | swap_device_unlock(stram_swap_info); | ||
| 752 | /* Get a page for the entry, using the existing | ||
| 753 | swap cache page if there is one. Otherwise, | ||
| 754 | get a clean page and read the swap into it. */ | ||
| 755 | page = read_swap_cache_async(entry, NULL, 0); | ||
| 756 | if (!page) { | ||
| 757 | swap_free(entry); | ||
| 758 | return -ENOMEM; | ||
| 759 | } | ||
| 760 | read_lock(&tasklist_lock); | ||
| 761 | for_each_process(p) | ||
| 762 | unswap_process(p->mm, entry, page); | ||
| 763 | read_unlock(&tasklist_lock); | ||
| 764 | shmem_unuse(entry, page); | ||
| 765 | /* Now get rid of the extra reference to the | ||
| 766 | temporary page we've been using. */ | ||
| 767 | if (PageSwapCache(page)) | ||
| 768 | delete_from_swap_cache(page); | ||
| 769 | __free_page(page); | ||
| 770 | #ifdef DO_PROC | ||
| 771 | stat_swap_force++; | ||
| 772 | #endif | ||
| 773 | } | ||
| 774 | |||
| 775 | DPRINTK( "unswap: map[i=%lu]=%u nr_swap=%ld\n", | ||
| 776 | i, map[i], nr_swap_pages ); | ||
| 777 | swap_list_lock(); | ||
| 778 | swap_device_lock(stram_swap_info); | ||
| 779 | map[i] = SWAP_MAP_BAD; | ||
| 780 | if (stram_swap_info->lowest_bit == i) | ||
| 781 | stram_swap_info->lowest_bit++; | ||
| 782 | if (stram_swap_info->highest_bit == i) | ||
| 783 | stram_swap_info->highest_bit--; | ||
| 784 | --nr_swap_pages; | ||
| 785 | swap_device_unlock(stram_swap_info); | ||
| 786 | swap_list_unlock(); | ||
| 787 | } | ||
| 788 | |||
| 789 | return 0; | ||
| 790 | } | ||
| 791 | |||
| 792 | /* | ||
| 793 | * reserve a region in ST-RAM swap space for an allocation | ||
| 794 | */ | ||
| 795 | static void *get_stram_region( unsigned long n_pages ) | ||
| 796 | { | ||
| 797 | unsigned short *map = stram_swap_info->swap_map; | ||
| 798 | unsigned long max = stram_swap_info->max; | ||
| 799 | unsigned long start, total_free, region_free; | ||
| 800 | int err; | ||
| 801 | void *ret = NULL; | ||
| 802 | |||
| 803 | DPRINTK( "get_stram_region(n_pages=%lu)\n", n_pages ); | ||
| 804 | |||
| 805 | down(&stram_swap_sem); | ||
| 806 | |||
| 807 | /* disallow writing to the swap device now */ | ||
| 808 | stram_swap_info->flags = SWP_USED; | ||
| 809 | |||
| 810 | /* find a region of n_pages pages in the swap space including as much free | ||
| 811 | * pages as possible (and excluding any already-reserved pages). */ | ||
| 812 | if (!(start = find_free_region( n_pages, &total_free, ®ion_free ))) | ||
| 813 | goto end; | ||
| 814 | DPRINTK( "get_stram_region: region starts at %lu, has %lu free pages\n", | ||
| 815 | start, region_free ); | ||
| 816 | |||
| 817 | err = unswap_by_read(map, max, start, n_pages); | ||
| 818 | if (err) | ||
| 819 | goto end; | ||
| 820 | |||
| 821 | ret = SWAP_ADDR(start); | ||
| 822 | end: | ||
| 823 | /* allow using swap device again */ | ||
| 824 | stram_swap_info->flags = SWP_WRITEOK; | ||
| 825 | up(&stram_swap_sem); | ||
| 826 | DPRINTK( "get_stram_region: returning %p\n", ret ); | ||
| 827 | return( ret ); | ||
| 828 | } | ||
| 829 | |||
| 830 | |||
| 831 | /* | ||
| 832 | * free a reserved region in ST-RAM swap space | ||
| 833 | */ | ||
| 834 | static void free_stram_region( unsigned long offset, unsigned long n_pages ) | ||
| 835 | { | ||
| 836 | unsigned short *map = stram_swap_info->swap_map; | ||
| 837 | |||
| 838 | DPRINTK( "free_stram_region(offset=%lu,n_pages=%lu)\n", offset, n_pages ); | ||
| 839 | |||
| 840 | if (offset < 1 || offset + n_pages > stram_swap_info->max) { | ||
| 841 | printk( KERN_ERR "free_stram_region: Trying to free non-ST-RAM\n" ); | ||
| 842 | return; | ||
| 843 | } | ||
| 844 | |||
| 845 | swap_list_lock(); | ||
| 846 | swap_device_lock(stram_swap_info); | ||
| 847 | /* un-reserve the freed pages */ | ||
| 848 | for( ; n_pages > 0; ++offset, --n_pages ) { | ||
| 849 | if (map[offset] != SWAP_MAP_BAD) | ||
| 850 | printk( KERN_ERR "free_stram_region: Swap page %lu was not " | ||
| 851 | "reserved\n", offset ); | ||
| 852 | map[offset] = 0; | ||
| 853 | } | ||
| 854 | |||
| 855 | /* update swapping meta-data */ | ||
| 856 | if (offset < stram_swap_info->lowest_bit) | ||
| 857 | stram_swap_info->lowest_bit = offset; | ||
| 858 | if (offset+n_pages-1 > stram_swap_info->highest_bit) | ||
| 859 | stram_swap_info->highest_bit = offset+n_pages-1; | ||
| 860 | if (stram_swap_info->prio > swap_info[swap_list.next].prio) | ||
| 861 | swap_list.next = swap_list.head; | ||
| 862 | nr_swap_pages += n_pages; | ||
| 863 | swap_device_unlock(stram_swap_info); | ||
| 864 | swap_list_unlock(); | ||
| 865 | } | ||
| 866 | |||
| 867 | |||
| 868 | /* ------------------------------------------------------------------------ */ | ||
| 869 | /* Utility Functions for Swapping */ | ||
| 870 | /* ------------------------------------------------------------------------ */ | ||
| 871 | |||
| 872 | |||
| 873 | /* is addr in some of the allocated regions? */ | ||
| 874 | static int in_some_region(void *addr) | ||
| 875 | { | ||
| 876 | BLOCK *p; | ||
| 877 | |||
| 878 | for( p = alloc_list; p; p = p->next ) { | ||
| 879 | if (p->start <= addr && addr < p->start + p->size) | ||
| 880 | return( 1 ); | ||
| 881 | } | ||
| 882 | return( 0 ); | ||
| 883 | } | ||
| 884 | |||
| 885 | |||
| 886 | static unsigned long find_free_region(unsigned long n_pages, | ||
| 887 | unsigned long *total_free, | ||
| 888 | unsigned long *region_free) | ||
| 889 | { | ||
| 890 | unsigned short *map = stram_swap_info->swap_map; | ||
| 891 | unsigned long max = stram_swap_info->max; | ||
| 892 | unsigned long head, tail, max_start; | ||
| 893 | long nfree, max_free; | ||
| 894 | |||
| 895 | /* first scan the swap space for a suitable place for the allocation */ | ||
| 896 | head = 1; | ||
| 897 | max_start = 0; | ||
| 898 | max_free = -1; | ||
| 899 | *total_free = 0; | ||
| 900 | |||
| 901 | start_over: | ||
| 902 | /* increment tail until final window size reached, and count free pages */ | ||
| 903 | nfree = 0; | ||
| 904 | for( tail = head; tail-head < n_pages && tail < max; ++tail ) { | ||
| 905 | if (map[tail] == SWAP_MAP_BAD) { | ||
| 906 | head = tail+1; | ||
| 907 | goto start_over; | ||
| 908 | } | ||
| 909 | if (!map[tail]) { | ||
| 910 | ++nfree; | ||
| 911 | ++*total_free; | ||
| 912 | } | ||
| 913 | } | ||
| 914 | if (tail-head < n_pages) | ||
| 915 | goto out; | ||
| 916 | if (nfree > max_free) { | ||
| 917 | max_start = head; | ||
| 918 | max_free = nfree; | ||
| 919 | if (max_free >= n_pages) | ||
| 920 | /* don't need more free pages... :-) */ | ||
| 921 | goto out; | ||
| 922 | } | ||
| 923 | |||
| 924 | /* now shift the window and look for the area where as much pages as | ||
| 925 | * possible are free */ | ||
| 926 | while( tail < max ) { | ||
| 927 | nfree -= (map[head++] == 0); | ||
| 928 | if (map[tail] == SWAP_MAP_BAD) { | ||
| 929 | head = tail+1; | ||
| 930 | goto start_over; | ||
| 931 | } | ||
| 932 | if (!map[tail]) { | ||
| 933 | ++nfree; | ||
| 934 | ++*total_free; | ||
| 935 | } | ||
| 936 | ++tail; | ||
| 937 | if (nfree > max_free) { | ||
| 938 | max_start = head; | ||
| 939 | max_free = nfree; | ||
| 940 | if (max_free >= n_pages) | ||
| 941 | /* don't need more free pages... :-) */ | ||
| 942 | goto out; | ||
| 943 | } | ||
| 944 | } | ||
| 945 | |||
| 946 | out: | ||
| 947 | if (max_free < 0) { | ||
| 948 | printk( KERN_NOTICE "get_stram_region: ST-RAM too full or fragmented " | ||
| 949 | "-- can't allocate %lu pages\n", n_pages ); | ||
| 950 | return( 0 ); | ||
| 951 | } | ||
| 952 | |||
| 953 | *region_free = max_free; | ||
| 954 | return( max_start ); | ||
| 955 | } | ||
| 956 | |||
| 957 | |||
| 958 | /* setup parameters from command line */ | ||
| 959 | void __init stram_swap_setup(char *str, int *ints) | ||
| 960 | { | ||
| 961 | if (ints[0] >= 1) | ||
| 962 | max_swap_size = ((ints[1] < 0 ? 0 : ints[1]) * 1024) & PAGE_MASK; | ||
| 963 | } | ||
| 964 | |||
| 965 | |||
| 966 | /* ------------------------------------------------------------------------ */ | ||
| 967 | /* ST-RAM device */ | ||
| 968 | /* ------------------------------------------------------------------------ */ | ||
| 969 | |||
| 970 | static int refcnt; | ||
| 971 | |||
| 972 | static void do_stram_request(request_queue_t *q) | ||
| 973 | { | ||
| 974 | struct request *req; | ||
| 975 | |||
| 976 | while ((req = elv_next_request(q)) != NULL) { | ||
| 977 | void *start = swap_start + (req->sector << 9); | ||
| 978 | unsigned long len = req->current_nr_sectors << 9; | ||
| 979 | if ((start + len) > swap_end) { | ||
| 980 | printk( KERN_ERR "stram: bad access beyond end of device: " | ||
| 981 | "block=%ld, count=%d\n", | ||
| 982 | req->sector, | ||
| 983 | req->current_nr_sectors ); | ||
| 984 | end_request(req, 0); | ||
| 985 | continue; | ||
| 986 | } | ||
| 987 | |||
| 988 | if (req->cmd == READ) { | ||
| 989 | memcpy(req->buffer, start, len); | ||
| 990 | #ifdef DO_PROC | ||
| 991 | stat_swap_read += N_PAGES(len); | ||
| 992 | #endif | ||
| 993 | } | ||
| 994 | else { | ||
| 995 | memcpy(start, req->buffer, len); | ||
| 996 | #ifdef DO_PROC | ||
| 997 | stat_swap_write += N_PAGES(len); | ||
| 998 | #endif | ||
| 999 | } | ||
| 1000 | end_request(req, 1); | ||
| 1001 | } | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | |||
| 1005 | static int stram_open( struct inode *inode, struct file *filp ) | ||
| 1006 | { | ||
| 1007 | if (filp != MAGIC_FILE_P) { | ||
| 1008 | printk( KERN_NOTICE "Only kernel can open ST-RAM device\n" ); | ||
| 1009 | return( -EPERM ); | ||
| 1010 | } | ||
| 1011 | if (refcnt) | ||
| 1012 | return( -EBUSY ); | ||
| 1013 | ++refcnt; | ||
| 1014 | return( 0 ); | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | static int stram_release( struct inode *inode, struct file *filp ) | ||
| 1018 | { | ||
| 1019 | if (filp != MAGIC_FILE_P) { | ||
| 1020 | printk( KERN_NOTICE "Only kernel can close ST-RAM device\n" ); | ||
| 1021 | return( -EPERM ); | ||
| 1022 | } | ||
| 1023 | if (refcnt > 0) | ||
| 1024 | --refcnt; | ||
| 1025 | return( 0 ); | ||
| 1026 | } | ||
| 1027 | |||
| 1028 | |||
| 1029 | static struct block_device_operations stram_fops = { | ||
| 1030 | .open = stram_open, | ||
| 1031 | .release = stram_release, | ||
| 1032 | }; | ||
| 1033 | |||
| 1034 | static struct gendisk *stram_disk; | ||
| 1035 | static struct request_queue *stram_queue; | ||
| 1036 | static DEFINE_SPINLOCK(stram_lock); | ||
| 1037 | |||
| 1038 | int __init stram_device_init(void) | ||
| 1039 | { | ||
| 1040 | if (!MACH_IS_ATARI) | ||
| 1041 | /* no point in initializing this, I hope */ | ||
| 1042 | return -ENXIO; | ||
| 1043 | |||
| 1044 | if (!max_swap_size) | ||
| 1045 | /* swapping not enabled */ | ||
| 1046 | return -ENXIO; | ||
| 1047 | stram_disk = alloc_disk(1); | ||
| 1048 | if (!stram_disk) | ||
| 1049 | return -ENOMEM; | ||
| 1050 | |||
| 1051 | if (register_blkdev(STRAM_MAJOR, "stram")) { | ||
| 1052 | put_disk(stram_disk); | ||
| 1053 | return -ENXIO; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | stram_queue = blk_init_queue(do_stram_request, &stram_lock); | ||
| 1057 | if (!stram_queue) { | ||
| 1058 | unregister_blkdev(STRAM_MAJOR, "stram"); | ||
| 1059 | put_disk(stram_disk); | ||
| 1060 | return -ENOMEM; | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | stram_disk->major = STRAM_MAJOR; | ||
| 1064 | stram_disk->first_minor = STRAM_MINOR; | ||
| 1065 | stram_disk->fops = &stram_fops; | ||
| 1066 | stram_disk->queue = stram_queue; | ||
| 1067 | sprintf(stram_disk->disk_name, "stram"); | ||
| 1068 | set_capacity(stram_disk, (swap_end - swap_start)/512); | ||
| 1069 | add_disk(stram_disk); | ||
| 1070 | return 0; | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | |||
| 1074 | |||
| 1075 | /* ------------------------------------------------------------------------ */ | ||
| 1076 | /* Misc Utility Functions */ | ||
| 1077 | /* ------------------------------------------------------------------------ */ | ||
| 1078 | |||
| 1079 | /* reserve a range of pages */ | ||
| 1080 | static void reserve_region(void *start, void *end) | ||
| 1081 | { | ||
| 1082 | reserve_bootmem (virt_to_phys(start), end - start); | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | #endif /* CONFIG_STRAM_SWAP */ | ||
| 1086 | |||
| 1087 | |||
| 1088 | /* ------------------------------------------------------------------------ */ | ||
| 1089 | /* Region Management */ | ||
| 1090 | /* ------------------------------------------------------------------------ */ | ||
| 1091 | |||
| 1092 | |||
| 1093 | /* insert a region into the alloced list (sorted) */ | ||
| 1094 | static BLOCK *add_region( void *addr, unsigned long size ) | ||
| 1095 | { | ||
| 1096 | BLOCK **p, *n = NULL; | ||
| 1097 | int i; | ||
| 1098 | |||
| 1099 | for( i = 0; i < N_STATIC_BLOCKS; ++i ) { | ||
| 1100 | if (static_blocks[i].flags & BLOCK_FREE) { | ||
| 1101 | n = &static_blocks[i]; | ||
| 1102 | n->flags = 0; | ||
| 1103 | break; | ||
| 1104 | } | ||
| 1105 | } | ||
| 1106 | if (!n && mem_init_done) { | ||
| 1107 | /* if statics block pool exhausted and we can call kmalloc() already | ||
| 1108 | * (after mem_init()), try that */ | ||
| 1109 | n = kmalloc( sizeof(BLOCK), GFP_KERNEL ); | ||
| 1110 | if (n) | ||
| 1111 | n->flags = BLOCK_KMALLOCED; | ||
| 1112 | } | ||
| 1113 | if (!n) { | ||
| 1114 | printk( KERN_ERR "Out of memory for ST-RAM descriptor blocks\n" ); | ||
| 1115 | return( NULL ); | ||
| 1116 | } | ||
| 1117 | n->start = addr; | ||
| 1118 | n->size = size; | ||
| 1119 | |||
| 1120 | for( p = &alloc_list; *p; p = &((*p)->next) ) | ||
| 1121 | if ((*p)->start > addr) break; | ||
| 1122 | n->next = *p; | ||
| 1123 | *p = n; | ||
| 1124 | |||
| 1125 | return( n ); | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | |||
| 1129 | /* find a region (by start addr) in the alloced list */ | ||
| 1130 | static BLOCK *find_region( void *addr ) | ||
| 1131 | { | ||
| 1132 | BLOCK *p; | ||
| 1133 | |||
| 1134 | for( p = alloc_list; p; p = p->next ) { | ||
| 1135 | if (p->start == addr) | ||
| 1136 | return( p ); | ||
| 1137 | if (p->start > addr) | ||
| 1138 | break; | ||
| 1139 | } | ||
| 1140 | return( NULL ); | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | |||
| 1144 | /* remove a block from the alloced list */ | ||
| 1145 | static int remove_region( BLOCK *block ) | ||
| 1146 | { | ||
| 1147 | BLOCK **p; | ||
| 1148 | |||
| 1149 | for( p = &alloc_list; *p; p = &((*p)->next) ) | ||
| 1150 | if (*p == block) break; | ||
| 1151 | if (!*p) | ||
| 1152 | return( 0 ); | ||
| 1153 | |||
| 1154 | *p = block->next; | ||
| 1155 | if (block->flags & BLOCK_KMALLOCED) | ||
| 1156 | kfree( block ); | ||
| 1157 | else | ||
| 1158 | block->flags |= BLOCK_FREE; | ||
| 1159 | return( 1 ); | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | |||
| 1163 | |||
| 1164 | /* ------------------------------------------------------------------------ */ | ||
| 1165 | /* /proc statistics file stuff */ | ||
| 1166 | /* ------------------------------------------------------------------------ */ | ||
| 1167 | |||
| 1168 | #ifdef DO_PROC | ||
| 1169 | |||
| 1170 | #define PRINT_PROC(fmt,args...) len += sprintf( buf+len, fmt, ##args ) | ||
| 1171 | |||
| 1172 | int get_stram_list( char *buf ) | ||
| 1173 | { | ||
| 1174 | int len = 0; | ||
| 1175 | BLOCK *p; | ||
| 1176 | #ifdef CONFIG_STRAM_SWAP | ||
| 1177 | int i; | ||
| 1178 | unsigned short *map = stram_swap_info->swap_map; | ||
| 1179 | unsigned long max = stram_swap_info->max; | ||
| 1180 | unsigned free = 0, used = 0, rsvd = 0; | ||
| 1181 | #endif | ||
| 1182 | |||
| 1183 | #ifdef CONFIG_STRAM_SWAP | ||
| 1184 | if (max_swap_size) { | ||
| 1185 | for( i = 1; i < max; ++i ) { | ||
| 1186 | if (!map[i]) | ||
| 1187 | ++free; | ||
| 1188 | else if (map[i] == SWAP_MAP_BAD) | ||
| 1189 | ++rsvd; | ||
| 1190 | else | ||
| 1191 | ++used; | ||
| 1192 | } | ||
| 1193 | PRINT_PROC( | ||
| 1194 | "Total ST-RAM: %8u kB\n" | ||
| 1195 | "Total ST-RAM swap: %8lu kB\n" | ||
| 1196 | "Free swap: %8u kB\n" | ||
| 1197 | "Used swap: %8u kB\n" | ||
| 1198 | "Allocated swap: %8u kB\n" | ||
| 1199 | "Swap Reads: %8u\n" | ||
| 1200 | "Swap Writes: %8u\n" | ||
| 1201 | "Swap Forced Reads: %8u\n", | ||
| 1202 | (stram_end - stram_start) >> 10, | ||
| 1203 | (max-1) << (PAGE_SHIFT-10), | ||
| 1204 | free << (PAGE_SHIFT-10), | ||
| 1205 | used << (PAGE_SHIFT-10), | ||
| 1206 | rsvd << (PAGE_SHIFT-10), | ||
| 1207 | stat_swap_read, | ||
| 1208 | stat_swap_write, | ||
| 1209 | stat_swap_force ); | ||
| 1210 | } | ||
| 1211 | else { | ||
| 1212 | #endif | ||
| 1213 | PRINT_PROC( "ST-RAM swapping disabled\n" ); | ||
| 1214 | PRINT_PROC("Total ST-RAM: %8u kB\n", | ||
| 1215 | (stram_end - stram_start) >> 10); | ||
| 1216 | #ifdef CONFIG_STRAM_SWAP | ||
| 1217 | } | ||
| 1218 | #endif | ||
| 1219 | |||
| 1220 | PRINT_PROC( "Allocated regions:\n" ); | ||
| 1221 | for( p = alloc_list; p; p = p->next ) { | ||
| 1222 | if (len + 50 >= PAGE_SIZE) | ||
| 1223 | break; | ||
| 1224 | PRINT_PROC("0x%08lx-0x%08lx: %s (", | ||
| 1225 | virt_to_phys(p->start), | ||
| 1226 | virt_to_phys(p->start+p->size-1), | ||
| 1227 | p->owner); | ||
| 1228 | if (p->flags & BLOCK_GFP) | ||
| 1229 | PRINT_PROC( "page-alloced)\n" ); | ||
| 1230 | else if (p->flags & BLOCK_INSWAP) | ||
| 1231 | PRINT_PROC( "in swap)\n" ); | ||
| 1232 | else | ||
| 1233 | PRINT_PROC( "??)\n" ); | ||
| 1234 | } | ||
| 1235 | |||
| 1236 | return( len ); | ||
| 1237 | } | ||
| 1238 | |||
| 1239 | #endif | ||
| 1240 | |||
| 1241 | |||
| 1242 | /* | ||
| 1243 | * Local variables: | ||
| 1244 | * c-indent-level: 4 | ||
| 1245 | * tab-width: 4 | ||
| 1246 | * End: | ||
| 1247 | */ | ||
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 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/m68k/atari/time.c | ||
| 3 | * | ||
| 4 | * Atari time and real time clock stuff | ||
| 5 | * | ||
| 6 | * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek | ||
| 7 | * | ||
| 8 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 9 | * License. See the file COPYING in the main directory of this archive | ||
| 10 | * for more details. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/types.h> | ||
| 14 | #include <linux/mc146818rtc.h> | ||
| 15 | #include <linux/interrupt.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/rtc.h> | ||
| 18 | #include <linux/bcd.h> | ||
| 19 | |||
| 20 | #include <asm/atariints.h> | ||
| 21 | |||
| 22 | void __init | ||
| 23 | atari_sched_init(irqreturn_t (*timer_routine)(int, void *, struct pt_regs *)) | ||
| 24 | { | ||
| 25 | /* set Timer C data Register */ | ||
| 26 | mfp.tim_dt_c = INT_TICKS; | ||
| 27 | /* start timer C, div = 1:100 */ | ||
| 28 | mfp.tim_ct_cd = (mfp.tim_ct_cd & 15) | 0x60; | ||
| 29 | /* install interrupt service routine for MFP Timer C */ | ||
| 30 | request_irq(IRQ_MFP_TIMC, timer_routine, IRQ_TYPE_SLOW, | ||
| 31 | "timer", timer_routine); | ||
| 32 | } | ||
| 33 | |||
| 34 | /* ++andreas: gettimeoffset fixed to check for pending interrupt */ | ||
| 35 | |||
| 36 | #define TICK_SIZE 10000 | ||
| 37 | |||
| 38 | /* This is always executed with interrupts disabled. */ | ||
| 39 | unsigned long atari_gettimeoffset (void) | ||
| 40 | { | ||
| 41 | unsigned long ticks, offset = 0; | ||
| 42 | |||
| 43 | /* read MFP timer C current value */ | ||
| 44 | ticks = mfp.tim_dt_c; | ||
| 45 | /* The probability of underflow is less than 2% */ | ||
| 46 | if (ticks > INT_TICKS - INT_TICKS / 50) | ||
| 47 | /* Check for pending timer interrupt */ | ||
| 48 | if (mfp.int_pn_b & (1 << 5)) | ||
| 49 | offset = TICK_SIZE; | ||
| 50 | |||
| 51 | ticks = INT_TICKS - ticks; | ||
| 52 | ticks = ticks * 10000L / INT_TICKS; | ||
| 53 | |||
| 54 | return ticks + offset; | ||
| 55 | } | ||
| 56 | |||
| 57 | |||
| 58 | static void mste_read(struct MSTE_RTC *val) | ||
| 59 | { | ||
| 60 | #define COPY(v) val->v=(mste_rtc.v & 0xf) | ||
| 61 | do { | ||
| 62 | COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ; | ||
| 63 | COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ; | ||
| 64 | COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ; | ||
| 65 | COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ; | ||
| 66 | COPY(year_tens) ; | ||
| 67 | /* prevent from reading the clock while it changed */ | ||
| 68 | } while (val->sec_ones != (mste_rtc.sec_ones & 0xf)); | ||
| 69 | #undef COPY | ||
| 70 | } | ||
| 71 | |||
| 72 | static void mste_write(struct MSTE_RTC *val) | ||
| 73 | { | ||
| 74 | #define COPY(v) mste_rtc.v=val->v | ||
| 75 | do { | ||
| 76 | COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ; | ||
| 77 | COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ; | ||
| 78 | COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ; | ||
| 79 | COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ; | ||
| 80 | COPY(year_tens) ; | ||
| 81 | /* prevent from writing the clock while it changed */ | ||
| 82 | } while (val->sec_ones != (mste_rtc.sec_ones & 0xf)); | ||
| 83 | #undef COPY | ||
| 84 | } | ||
| 85 | |||
| 86 | #define RTC_READ(reg) \ | ||
| 87 | ({ unsigned char __val; \ | ||
| 88 | (void) atari_writeb(reg,&tt_rtc.regsel); \ | ||
| 89 | __val = tt_rtc.data; \ | ||
| 90 | __val; \ | ||
| 91 | }) | ||
| 92 | |||
| 93 | #define RTC_WRITE(reg,val) \ | ||
| 94 | do { \ | ||
| 95 | atari_writeb(reg,&tt_rtc.regsel); \ | ||
| 96 | tt_rtc.data = (val); \ | ||
| 97 | } while(0) | ||
| 98 | |||
| 99 | |||
| 100 | #define HWCLK_POLL_INTERVAL 5 | ||
| 101 | |||
| 102 | int atari_mste_hwclk( int op, struct rtc_time *t ) | ||
| 103 | { | ||
| 104 | int hour, year; | ||
| 105 | int hr24=0; | ||
| 106 | struct MSTE_RTC val; | ||
| 107 | |||
| 108 | mste_rtc.mode=(mste_rtc.mode | 1); | ||
| 109 | hr24=mste_rtc.mon_tens & 1; | ||
| 110 | mste_rtc.mode=(mste_rtc.mode & ~1); | ||
| 111 | |||
| 112 | if (op) { | ||
| 113 | /* write: prepare values */ | ||
| 114 | |||
| 115 | val.sec_ones = t->tm_sec % 10; | ||
| 116 | val.sec_tens = t->tm_sec / 10; | ||
| 117 | val.min_ones = t->tm_min % 10; | ||
| 118 | val.min_tens = t->tm_min / 10; | ||
| 119 | hour = t->tm_hour; | ||
| 120 | if (!hr24) { | ||
| 121 | if (hour > 11) | ||
| 122 | hour += 20 - 12; | ||
| 123 | if (hour == 0 || hour == 20) | ||
| 124 | hour += 12; | ||
| 125 | } | ||
| 126 | val.hr_ones = hour % 10; | ||
| 127 | val.hr_tens = hour / 10; | ||
| 128 | val.day_ones = t->tm_mday % 10; | ||
| 129 | val.day_tens = t->tm_mday / 10; | ||
| 130 | val.mon_ones = (t->tm_mon+1) % 10; | ||
| 131 | val.mon_tens = (t->tm_mon+1) / 10; | ||
| 132 | year = t->tm_year - 80; | ||
| 133 | val.year_ones = year % 10; | ||
| 134 | val.year_tens = year / 10; | ||
| 135 | val.weekday = t->tm_wday; | ||
| 136 | mste_write(&val); | ||
| 137 | mste_rtc.mode=(mste_rtc.mode | 1); | ||
| 138 | val.year_ones = (year % 4); /* leap year register */ | ||
| 139 | mste_rtc.mode=(mste_rtc.mode & ~1); | ||
| 140 | } | ||
| 141 | else { | ||
| 142 | mste_read(&val); | ||
| 143 | t->tm_sec = val.sec_ones + val.sec_tens * 10; | ||
| 144 | t->tm_min = val.min_ones + val.min_tens * 10; | ||
| 145 | hour = val.hr_ones + val.hr_tens * 10; | ||
| 146 | if (!hr24) { | ||
| 147 | if (hour == 12 || hour == 12 + 20) | ||
| 148 | hour -= 12; | ||
| 149 | if (hour >= 20) | ||
| 150 | hour += 12 - 20; | ||
| 151 | } | ||
| 152 | t->tm_hour = hour; | ||
| 153 | t->tm_mday = val.day_ones + val.day_tens * 10; | ||
| 154 | t->tm_mon = val.mon_ones + val.mon_tens * 10 - 1; | ||
| 155 | t->tm_year = val.year_ones + val.year_tens * 10 + 80; | ||
| 156 | t->tm_wday = val.weekday; | ||
| 157 | } | ||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | int atari_tt_hwclk( int op, struct rtc_time *t ) | ||
| 162 | { | ||
| 163 | int sec=0, min=0, hour=0, day=0, mon=0, year=0, wday=0; | ||
| 164 | unsigned long flags; | ||
| 165 | unsigned char ctrl; | ||
| 166 | int pm = 0; | ||
| 167 | |||
| 168 | ctrl = RTC_READ(RTC_CONTROL); /* control registers are | ||
| 169 | * independent from the UIP */ | ||
| 170 | |||
| 171 | if (op) { | ||
| 172 | /* write: prepare values */ | ||
| 173 | |||
| 174 | sec = t->tm_sec; | ||
| 175 | min = t->tm_min; | ||
| 176 | hour = t->tm_hour; | ||
| 177 | day = t->tm_mday; | ||
| 178 | mon = t->tm_mon + 1; | ||
| 179 | year = t->tm_year - atari_rtc_year_offset; | ||
| 180 | wday = t->tm_wday + (t->tm_wday >= 0); | ||
| 181 | |||
| 182 | if (!(ctrl & RTC_24H)) { | ||
| 183 | if (hour > 11) { | ||
| 184 | pm = 0x80; | ||
| 185 | if (hour != 12) | ||
| 186 | hour -= 12; | ||
| 187 | } | ||
| 188 | else if (hour == 0) | ||
| 189 | hour = 12; | ||
| 190 | } | ||
| 191 | |||
| 192 | if (!(ctrl & RTC_DM_BINARY)) { | ||
| 193 | BIN_TO_BCD(sec); | ||
| 194 | BIN_TO_BCD(min); | ||
| 195 | BIN_TO_BCD(hour); | ||
| 196 | BIN_TO_BCD(day); | ||
| 197 | BIN_TO_BCD(mon); | ||
| 198 | BIN_TO_BCD(year); | ||
| 199 | if (wday >= 0) BIN_TO_BCD(wday); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | /* Reading/writing the clock registers is a bit critical due to | ||
| 204 | * the regular update cycle of the RTC. While an update is in | ||
| 205 | * progress, registers 0..9 shouldn't be touched. | ||
| 206 | * The problem is solved like that: If an update is currently in | ||
| 207 | * progress (the UIP bit is set), the process sleeps for a while | ||
| 208 | * (50ms). This really should be enough, since the update cycle | ||
| 209 | * normally needs 2 ms. | ||
| 210 | * If the UIP bit reads as 0, we have at least 244 usecs until the | ||
| 211 | * update starts. This should be enough... But to be sure, | ||
| 212 | * additionally the RTC_SET bit is set to prevent an update cycle. | ||
| 213 | */ | ||
| 214 | |||
| 215 | while( RTC_READ(RTC_FREQ_SELECT) & RTC_UIP ) { | ||
| 216 | current->state = TASK_INTERRUPTIBLE; | ||
| 217 | schedule_timeout(HWCLK_POLL_INTERVAL); | ||
| 218 | } | ||
| 219 | |||
| 220 | local_irq_save(flags); | ||
| 221 | RTC_WRITE( RTC_CONTROL, ctrl | RTC_SET ); | ||
| 222 | if (!op) { | ||
| 223 | sec = RTC_READ( RTC_SECONDS ); | ||
| 224 | min = RTC_READ( RTC_MINUTES ); | ||
| 225 | hour = RTC_READ( RTC_HOURS ); | ||
| 226 | day = RTC_READ( RTC_DAY_OF_MONTH ); | ||
| 227 | mon = RTC_READ( RTC_MONTH ); | ||
| 228 | year = RTC_READ( RTC_YEAR ); | ||
| 229 | wday = RTC_READ( RTC_DAY_OF_WEEK ); | ||
| 230 | } | ||
| 231 | else { | ||
| 232 | RTC_WRITE( RTC_SECONDS, sec ); | ||
| 233 | RTC_WRITE( RTC_MINUTES, min ); | ||
| 234 | RTC_WRITE( RTC_HOURS, hour + pm); | ||
| 235 | RTC_WRITE( RTC_DAY_OF_MONTH, day ); | ||
| 236 | RTC_WRITE( RTC_MONTH, mon ); | ||
| 237 | RTC_WRITE( RTC_YEAR, year ); | ||
| 238 | if (wday >= 0) RTC_WRITE( RTC_DAY_OF_WEEK, wday ); | ||
| 239 | } | ||
| 240 | RTC_WRITE( RTC_CONTROL, ctrl & ~RTC_SET ); | ||
| 241 | local_irq_restore(flags); | ||
| 242 | |||
| 243 | if (!op) { | ||
| 244 | /* read: adjust values */ | ||
| 245 | |||
| 246 | if (hour & 0x80) { | ||
| 247 | hour &= ~0x80; | ||
| 248 | pm = 1; | ||
| 249 | } | ||
| 250 | |||
| 251 | if (!(ctrl & RTC_DM_BINARY)) { | ||
| 252 | BCD_TO_BIN(sec); | ||
| 253 | BCD_TO_BIN(min); | ||
| 254 | BCD_TO_BIN(hour); | ||
| 255 | BCD_TO_BIN(day); | ||
| 256 | BCD_TO_BIN(mon); | ||
| 257 | BCD_TO_BIN(year); | ||
| 258 | BCD_TO_BIN(wday); | ||
| 259 | } | ||
| 260 | |||
| 261 | if (!(ctrl & RTC_24H)) { | ||
| 262 | if (!pm && hour == 12) | ||
| 263 | hour = 0; | ||
| 264 | else if (pm && hour != 12) | ||
| 265 | hour += 12; | ||
| 266 | } | ||
| 267 | |||
| 268 | t->tm_sec = sec; | ||
| 269 | t->tm_min = min; | ||
| 270 | t->tm_hour = hour; | ||
| 271 | t->tm_mday = day; | ||
| 272 | t->tm_mon = mon - 1; | ||
| 273 | t->tm_year = year + atari_rtc_year_offset; | ||
| 274 | t->tm_wday = wday - 1; | ||
| 275 | } | ||
| 276 | |||
| 277 | return( 0 ); | ||
| 278 | } | ||
| 279 | |||
| 280 | |||
| 281 | int atari_mste_set_clock_mmss (unsigned long nowtime) | ||
| 282 | { | ||
| 283 | short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60; | ||
| 284 | struct MSTE_RTC val; | ||
| 285 | unsigned char rtc_minutes; | ||
| 286 | |||
| 287 | mste_read(&val); | ||
| 288 | rtc_minutes= val.min_ones + val.min_tens * 10; | ||
| 289 | if ((rtc_minutes < real_minutes | ||
| 290 | ? real_minutes - rtc_minutes | ||
| 291 | : rtc_minutes - real_minutes) < 30) | ||
| 292 | { | ||
| 293 | val.sec_ones = real_seconds % 10; | ||
| 294 | val.sec_tens = real_seconds / 10; | ||
| 295 | val.min_ones = real_minutes % 10; | ||
| 296 | val.min_tens = real_minutes / 10; | ||
| 297 | mste_write(&val); | ||
| 298 | } | ||
| 299 | else | ||
| 300 | return -1; | ||
| 301 | return 0; | ||
| 302 | } | ||
| 303 | |||
| 304 | int atari_tt_set_clock_mmss (unsigned long nowtime) | ||
| 305 | { | ||
| 306 | int retval = 0; | ||
| 307 | short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60; | ||
| 308 | unsigned char save_control, save_freq_select, rtc_minutes; | ||
| 309 | |||
| 310 | save_control = RTC_READ (RTC_CONTROL); /* tell the clock it's being set */ | ||
| 311 | RTC_WRITE (RTC_CONTROL, save_control | RTC_SET); | ||
| 312 | |||
| 313 | save_freq_select = RTC_READ (RTC_FREQ_SELECT); /* stop and reset prescaler */ | ||
| 314 | RTC_WRITE (RTC_FREQ_SELECT, save_freq_select | RTC_DIV_RESET2); | ||
| 315 | |||
| 316 | rtc_minutes = RTC_READ (RTC_MINUTES); | ||
| 317 | if (!(save_control & RTC_DM_BINARY)) | ||
| 318 | BCD_TO_BIN (rtc_minutes); | ||
| 319 | |||
| 320 | /* Since we're only adjusting minutes and seconds, don't interfere | ||
| 321 | with hour overflow. This avoids messing with unknown time zones | ||
| 322 | but requires your RTC not to be off by more than 30 minutes. */ | ||
| 323 | if ((rtc_minutes < real_minutes | ||
| 324 | ? real_minutes - rtc_minutes | ||
| 325 | : rtc_minutes - real_minutes) < 30) | ||
| 326 | { | ||
| 327 | if (!(save_control & RTC_DM_BINARY)) | ||
| 328 | { | ||
| 329 | BIN_TO_BCD (real_seconds); | ||
| 330 | BIN_TO_BCD (real_minutes); | ||
| 331 | } | ||
| 332 | RTC_WRITE (RTC_SECONDS, real_seconds); | ||
| 333 | RTC_WRITE (RTC_MINUTES, real_minutes); | ||
| 334 | } | ||
| 335 | else | ||
| 336 | retval = -1; | ||
| 337 | |||
| 338 | RTC_WRITE (RTC_FREQ_SELECT, save_freq_select); | ||
| 339 | RTC_WRITE (RTC_CONTROL, save_control); | ||
| 340 | return retval; | ||
| 341 | } | ||
| 342 | |||
| 343 | /* | ||
| 344 | * Local variables: | ||
| 345 | * c-indent-level: 4 | ||
| 346 | * tab-width: 8 | ||
| 347 | * End: | ||
| 348 | */ | ||
