1 | /* |
2 | * linux/arch/m68k/amiga/config.c |
3 | * |
4 | * Copyright (C) 1993 Hamish Macdonald |
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 | /* |
12 | * Miscellaneous Amiga stuff |
13 | */ |
14 | |
15 | #include <linux/types.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/mm.h> |
18 | #include <linux/seq_file.h> |
19 | #include <linux/tty.h> |
20 | #include <linux/clocksource.h> |
21 | #include <linux/console.h> |
22 | #include <linux/rtc.h> |
23 | #include <linux/init.h> |
24 | #include <linux/vt_kern.h> |
25 | #include <linux/delay.h> |
26 | #include <linux/interrupt.h> |
27 | #include <linux/zorro.h> |
28 | #include <linux/module.h> |
29 | #include <linux/keyboard.h> |
30 | |
31 | #include <asm/bootinfo.h> |
32 | #include <asm/bootinfo-amiga.h> |
33 | #include <asm/byteorder.h> |
34 | #include <asm/setup.h> |
35 | #include <asm/amigahw.h> |
36 | #include <asm/amigaints.h> |
37 | #include <asm/irq.h> |
38 | #include <asm/machdep.h> |
39 | #include <asm/io.h> |
40 | #include <asm/config.h> |
41 | |
42 | #include "amiga.h" |
43 | |
44 | static unsigned long amiga_model; |
45 | |
46 | unsigned long amiga_eclock; |
47 | EXPORT_SYMBOL(amiga_eclock); |
48 | |
49 | unsigned long amiga_colorclock; |
50 | EXPORT_SYMBOL(amiga_colorclock); |
51 | |
52 | unsigned long amiga_chipset; |
53 | EXPORT_SYMBOL(amiga_chipset); |
54 | |
55 | unsigned char amiga_vblank; |
56 | EXPORT_SYMBOL(amiga_vblank); |
57 | |
58 | static unsigned char amiga_psfreq; |
59 | |
60 | struct amiga_hw_present amiga_hw_present; |
61 | EXPORT_SYMBOL(amiga_hw_present); |
62 | |
63 | static char s_a500[] __initdata = "A500" ; |
64 | static char s_a500p[] __initdata = "A500+" ; |
65 | static char s_a600[] __initdata = "A600" ; |
66 | static char s_a1000[] __initdata = "A1000" ; |
67 | static char s_a1200[] __initdata = "A1200" ; |
68 | static char s_a2000[] __initdata = "A2000" ; |
69 | static char s_a2500[] __initdata = "A2500" ; |
70 | static char s_a3000[] __initdata = "A3000" ; |
71 | static char s_a3000t[] __initdata = "A3000T" ; |
72 | static char s_a3000p[] __initdata = "A3000+" ; |
73 | static char s_a4000[] __initdata = "A4000" ; |
74 | static char s_a4000t[] __initdata = "A4000T" ; |
75 | static char s_cdtv[] __initdata = "CDTV" ; |
76 | static char s_cd32[] __initdata = "CD32" ; |
77 | static char s_draco[] __initdata = "Draco" ; |
78 | static char *amiga_models[] __initdata = { |
79 | [AMI_500-AMI_500] = s_a500, |
80 | [AMI_500PLUS-AMI_500] = s_a500p, |
81 | [AMI_600-AMI_500] = s_a600, |
82 | [AMI_1000-AMI_500] = s_a1000, |
83 | [AMI_1200-AMI_500] = s_a1200, |
84 | [AMI_2000-AMI_500] = s_a2000, |
85 | [AMI_2500-AMI_500] = s_a2500, |
86 | [AMI_3000-AMI_500] = s_a3000, |
87 | [AMI_3000T-AMI_500] = s_a3000t, |
88 | [AMI_3000PLUS-AMI_500] = s_a3000p, |
89 | [AMI_4000-AMI_500] = s_a4000, |
90 | [AMI_4000T-AMI_500] = s_a4000t, |
91 | [AMI_CDTV-AMI_500] = s_cdtv, |
92 | [AMI_CD32-AMI_500] = s_cd32, |
93 | [AMI_DRACO-AMI_500] = s_draco, |
94 | }; |
95 | |
96 | static char amiga_model_name[13] = "Amiga " ; |
97 | |
98 | static void amiga_sched_init(void); |
99 | static void amiga_get_model(char *model); |
100 | static void amiga_get_hardware_list(struct seq_file *m); |
101 | static void amiga_reset(void); |
102 | static void amiga_mem_console_write(struct console *co, const char *b, |
103 | unsigned int count); |
104 | #ifdef CONFIG_HEARTBEAT |
105 | static void amiga_heartbeat(int on); |
106 | #endif |
107 | |
108 | static struct console amiga_console_driver = { |
109 | .name = "debug" , |
110 | .flags = CON_PRINTBUFFER, |
111 | .index = -1, |
112 | }; |
113 | |
114 | |
115 | /* |
116 | * Motherboard Resources present in all Amiga models |
117 | */ |
118 | |
119 | static struct { |
120 | struct resource _ciab, _ciaa, _custom, _kickstart; |
121 | } mb_resources = { |
122 | ._ciab = { |
123 | .name = "CIA B" , .start = 0x00bfd000, .end = 0x00bfdfff |
124 | }, |
125 | ._ciaa = { |
126 | .name = "CIA A" , .start = 0x00bfe000, .end = 0x00bfefff |
127 | }, |
128 | ._custom = { |
129 | .name = "Custom I/O" , .start = 0x00dff000, .end = 0x00dfffff |
130 | }, |
131 | ._kickstart = { |
132 | .name = "Kickstart ROM" , .start = 0x00f80000, .end = 0x00ffffff |
133 | } |
134 | }; |
135 | |
136 | static struct resource ram_resource[NUM_MEMINFO]; |
137 | |
138 | |
139 | /* |
140 | * Parse an Amiga-specific record in the bootinfo |
141 | */ |
142 | |
143 | int __init amiga_parse_bootinfo(const struct bi_record *record) |
144 | { |
145 | int unknown = 0; |
146 | const void *data = record->data; |
147 | |
148 | switch (be16_to_cpu(record->tag)) { |
149 | case BI_AMIGA_MODEL: |
150 | amiga_model = be32_to_cpup(p: data); |
151 | break; |
152 | |
153 | case BI_AMIGA_ECLOCK: |
154 | amiga_eclock = be32_to_cpup(p: data); |
155 | break; |
156 | |
157 | case BI_AMIGA_CHIPSET: |
158 | amiga_chipset = be32_to_cpup(p: data); |
159 | break; |
160 | |
161 | case BI_AMIGA_CHIP_SIZE: |
162 | amiga_chip_size = be32_to_cpup(p: data); |
163 | break; |
164 | |
165 | case BI_AMIGA_VBLANK: |
166 | amiga_vblank = *(const __u8 *)data; |
167 | break; |
168 | |
169 | case BI_AMIGA_PSFREQ: |
170 | amiga_psfreq = *(const __u8 *)data; |
171 | break; |
172 | |
173 | case BI_AMIGA_AUTOCON: |
174 | #ifdef CONFIG_ZORRO |
175 | if (zorro_num_autocon < ZORRO_NUM_AUTO) { |
176 | const struct ConfigDev *cd = data; |
177 | struct zorro_dev_init *dev = &zorro_autocon_init[zorro_num_autocon++]; |
178 | dev->rom = cd->cd_Rom; |
179 | dev->slotaddr = be16_to_cpu(cd->cd_SlotAddr); |
180 | dev->slotsize = be16_to_cpu(cd->cd_SlotSize); |
181 | dev->boardaddr = be32_to_cpu(cd->cd_BoardAddr); |
182 | dev->boardsize = be32_to_cpu(cd->cd_BoardSize); |
183 | } else |
184 | pr_warn("amiga_parse_bootinfo: too many AutoConfig devices\n" ); |
185 | #endif /* CONFIG_ZORRO */ |
186 | break; |
187 | |
188 | case BI_AMIGA_SERPER: |
189 | /* serial port period: ignored here */ |
190 | break; |
191 | |
192 | default: |
193 | unknown = 1; |
194 | } |
195 | return unknown; |
196 | } |
197 | |
198 | /* |
199 | * Identify builtin hardware |
200 | */ |
201 | |
202 | static void __init amiga_identify(void) |
203 | { |
204 | /* Fill in some default values, if necessary */ |
205 | if (amiga_eclock == 0) |
206 | amiga_eclock = 709379; |
207 | |
208 | memset(&amiga_hw_present, 0, sizeof(amiga_hw_present)); |
209 | |
210 | pr_info("Amiga hardware found: " ); |
211 | if (amiga_model >= AMI_500 && amiga_model <= AMI_DRACO) { |
212 | pr_cont("[%s] " , amiga_models[amiga_model-AMI_500]); |
213 | strcat(amiga_model_name, amiga_models[amiga_model-AMI_500]); |
214 | } |
215 | |
216 | switch (amiga_model) { |
217 | case AMI_UNKNOWN: |
218 | break; |
219 | |
220 | case AMI_600: |
221 | case AMI_1200: |
222 | AMIGAHW_SET(A1200_IDE); |
223 | AMIGAHW_SET(PCMCIA); |
224 | fallthrough; |
225 | case AMI_500: |
226 | case AMI_500PLUS: |
227 | case AMI_1000: |
228 | case AMI_2000: |
229 | case AMI_2500: |
230 | AMIGAHW_SET(A2000_CLK); /* Is this correct for all models? */ |
231 | break; |
232 | |
233 | case AMI_3000: |
234 | case AMI_3000T: |
235 | AMIGAHW_SET(AMBER_FF); |
236 | AMIGAHW_SET(MAGIC_REKICK); |
237 | fallthrough; |
238 | case AMI_3000PLUS: |
239 | AMIGAHW_SET(A3000_SCSI); |
240 | AMIGAHW_SET(A3000_CLK); |
241 | AMIGAHW_SET(ZORRO3); |
242 | break; |
243 | |
244 | case AMI_4000T: |
245 | AMIGAHW_SET(A4000_SCSI); |
246 | fallthrough; |
247 | case AMI_4000: |
248 | AMIGAHW_SET(A4000_IDE); |
249 | AMIGAHW_SET(A3000_CLK); |
250 | AMIGAHW_SET(ZORRO3); |
251 | break; |
252 | |
253 | case AMI_CDTV: |
254 | case AMI_CD32: |
255 | AMIGAHW_SET(CD_ROM); |
256 | AMIGAHW_SET(A2000_CLK); /* Is this correct? */ |
257 | break; |
258 | |
259 | case AMI_DRACO: |
260 | panic(fmt: "No support for Draco yet" ); |
261 | |
262 | default: |
263 | panic(fmt: "Unknown Amiga Model" ); |
264 | } |
265 | |
266 | AMIGAHW_SET(AMI_VIDEO); |
267 | AMIGAHW_SET(AMI_BLITTER); |
268 | AMIGAHW_SET(AMI_AUDIO); |
269 | AMIGAHW_SET(AMI_FLOPPY); |
270 | AMIGAHW_SET(AMI_KEYBOARD); |
271 | AMIGAHW_SET(AMI_MOUSE); |
272 | AMIGAHW_SET(AMI_SERIAL); |
273 | AMIGAHW_SET(AMI_PARALLEL); |
274 | AMIGAHW_SET(CHIP_RAM); |
275 | AMIGAHW_SET(PAULA); |
276 | |
277 | switch (amiga_chipset) { |
278 | case CS_OCS: |
279 | case CS_ECS: |
280 | case CS_AGA: |
281 | switch (amiga_custom.deniseid & 0xf) { |
282 | case 0x0c: |
283 | AMIGAHW_SET(DENISE_HR); |
284 | break; |
285 | case 0x08: |
286 | AMIGAHW_SET(LISA); |
287 | break; |
288 | default: |
289 | AMIGAHW_SET(DENISE); |
290 | break; |
291 | } |
292 | break; |
293 | } |
294 | switch ((amiga_custom.vposr>>8) & 0x7f) { |
295 | case 0x00: |
296 | AMIGAHW_SET(AGNUS_PAL); |
297 | break; |
298 | case 0x10: |
299 | AMIGAHW_SET(AGNUS_NTSC); |
300 | break; |
301 | case 0x20: |
302 | case 0x21: |
303 | AMIGAHW_SET(AGNUS_HR_PAL); |
304 | break; |
305 | case 0x30: |
306 | case 0x31: |
307 | AMIGAHW_SET(AGNUS_HR_NTSC); |
308 | break; |
309 | case 0x22: |
310 | case 0x23: |
311 | AMIGAHW_SET(ALICE_PAL); |
312 | break; |
313 | case 0x32: |
314 | case 0x33: |
315 | AMIGAHW_SET(ALICE_NTSC); |
316 | break; |
317 | } |
318 | AMIGAHW_SET(ZORRO); |
319 | |
320 | #define AMIGAHW_ANNOUNCE(name, str) \ |
321 | if (AMIGAHW_PRESENT(name)) \ |
322 | pr_cont(str) |
323 | |
324 | AMIGAHW_ANNOUNCE(AMI_VIDEO, "VIDEO " ); |
325 | AMIGAHW_ANNOUNCE(AMI_BLITTER, "BLITTER " ); |
326 | AMIGAHW_ANNOUNCE(AMBER_FF, "AMBER_FF " ); |
327 | AMIGAHW_ANNOUNCE(AMI_AUDIO, "AUDIO " ); |
328 | AMIGAHW_ANNOUNCE(AMI_FLOPPY, "FLOPPY " ); |
329 | AMIGAHW_ANNOUNCE(A3000_SCSI, "A3000_SCSI " ); |
330 | AMIGAHW_ANNOUNCE(A4000_SCSI, "A4000_SCSI " ); |
331 | AMIGAHW_ANNOUNCE(A1200_IDE, "A1200_IDE " ); |
332 | AMIGAHW_ANNOUNCE(A4000_IDE, "A4000_IDE " ); |
333 | AMIGAHW_ANNOUNCE(CD_ROM, "CD_ROM " ); |
334 | AMIGAHW_ANNOUNCE(AMI_KEYBOARD, "KEYBOARD " ); |
335 | AMIGAHW_ANNOUNCE(AMI_MOUSE, "MOUSE " ); |
336 | AMIGAHW_ANNOUNCE(AMI_SERIAL, "SERIAL " ); |
337 | AMIGAHW_ANNOUNCE(AMI_PARALLEL, "PARALLEL " ); |
338 | AMIGAHW_ANNOUNCE(A2000_CLK, "A2000_CLK " ); |
339 | AMIGAHW_ANNOUNCE(A3000_CLK, "A3000_CLK " ); |
340 | AMIGAHW_ANNOUNCE(CHIP_RAM, "CHIP_RAM " ); |
341 | AMIGAHW_ANNOUNCE(PAULA, "PAULA " ); |
342 | AMIGAHW_ANNOUNCE(DENISE, "DENISE " ); |
343 | AMIGAHW_ANNOUNCE(DENISE_HR, "DENISE_HR " ); |
344 | AMIGAHW_ANNOUNCE(LISA, "LISA " ); |
345 | AMIGAHW_ANNOUNCE(AGNUS_PAL, "AGNUS_PAL " ); |
346 | AMIGAHW_ANNOUNCE(AGNUS_NTSC, "AGNUS_NTSC " ); |
347 | AMIGAHW_ANNOUNCE(AGNUS_HR_PAL, "AGNUS_HR_PAL " ); |
348 | AMIGAHW_ANNOUNCE(AGNUS_HR_NTSC, "AGNUS_HR_NTSC " ); |
349 | AMIGAHW_ANNOUNCE(ALICE_PAL, "ALICE_PAL " ); |
350 | AMIGAHW_ANNOUNCE(ALICE_NTSC, "ALICE_NTSC " ); |
351 | AMIGAHW_ANNOUNCE(MAGIC_REKICK, "MAGIC_REKICK " ); |
352 | AMIGAHW_ANNOUNCE(PCMCIA, "PCMCIA " ); |
353 | if (AMIGAHW_PRESENT(ZORRO)) |
354 | pr_cont("ZORRO%s " , AMIGAHW_PRESENT(ZORRO3) ? "3" : "" ); |
355 | pr_cont("\n" ); |
356 | |
357 | #undef AMIGAHW_ANNOUNCE |
358 | } |
359 | |
360 | |
361 | static unsigned long amiga_random_get_entropy(void) |
362 | { |
363 | /* VPOSR/VHPOSR provide at least 17 bits of data changing at 1.79 MHz */ |
364 | return *(unsigned long *)&amiga_custom.vposr; |
365 | } |
366 | |
367 | |
368 | /* |
369 | * Setup the Amiga configuration info |
370 | */ |
371 | |
372 | void __init config_amiga(void) |
373 | { |
374 | int i; |
375 | |
376 | amiga_identify(); |
377 | |
378 | /* Yuk, we don't have PCI memory */ |
379 | iomem_resource.name = "Memory" ; |
380 | for (i = 0; i < 4; i++) |
381 | request_resource(root: &iomem_resource, new: &((struct resource *)&mb_resources)[i]); |
382 | |
383 | mach_sched_init = amiga_sched_init; |
384 | mach_init_IRQ = amiga_init_IRQ; |
385 | mach_get_model = amiga_get_model; |
386 | mach_get_hardware_list = amiga_get_hardware_list; |
387 | mach_reset = amiga_reset; |
388 | #if IS_ENABLED(CONFIG_INPUT_M68K_BEEP) |
389 | mach_beep = amiga_mksound; |
390 | #endif |
391 | |
392 | #ifdef CONFIG_HEARTBEAT |
393 | mach_heartbeat = amiga_heartbeat; |
394 | #endif |
395 | |
396 | mach_random_get_entropy = amiga_random_get_entropy; |
397 | |
398 | /* Fill in the clock value (based on the 700 kHz E-Clock) */ |
399 | amiga_colorclock = 5*amiga_eclock; /* 3.5 MHz */ |
400 | |
401 | /* clear all DMA bits */ |
402 | amiga_custom.dmacon = DMAF_ALL; |
403 | /* ensure that the DMA master bit is set */ |
404 | amiga_custom.dmacon = DMAF_SETCLR | DMAF_MASTER; |
405 | |
406 | /* don't use Z2 RAM as system memory on Z3 capable machines */ |
407 | if (AMIGAHW_PRESENT(ZORRO3)) { |
408 | int i, j; |
409 | u32 disabled_z2mem = 0; |
410 | |
411 | for (i = 0; i < m68k_num_memory; i++) { |
412 | if (m68k_memory[i].addr < 16*1024*1024) { |
413 | if (i == 0) { |
414 | /* don't cut off the branch we're sitting on */ |
415 | pr_warn("Warning: kernel runs in Zorro II memory\n" ); |
416 | continue; |
417 | } |
418 | disabled_z2mem += m68k_memory[i].size; |
419 | m68k_num_memory--; |
420 | for (j = i; j < m68k_num_memory; j++) |
421 | m68k_memory[j] = m68k_memory[j+1]; |
422 | i--; |
423 | } |
424 | } |
425 | if (disabled_z2mem) |
426 | pr_info("%dK of Zorro II memory will not be used as system memory\n" , |
427 | disabled_z2mem>>10); |
428 | } |
429 | |
430 | /* request all RAM */ |
431 | for (i = 0; i < m68k_num_memory; i++) { |
432 | ram_resource[i].name = |
433 | (m68k_memory[i].addr >= 0x01000000) ? "32-bit Fast RAM" : |
434 | (m68k_memory[i].addr < 0x00c00000) ? "16-bit Fast RAM" : |
435 | "16-bit Slow RAM" ; |
436 | ram_resource[i].start = m68k_memory[i].addr; |
437 | ram_resource[i].end = m68k_memory[i].addr+m68k_memory[i].size-1; |
438 | request_resource(&iomem_resource, &ram_resource[i]); |
439 | } |
440 | |
441 | /* initialize chipram allocator */ |
442 | amiga_chip_init(); |
443 | |
444 | /* our beloved beeper */ |
445 | if (AMIGAHW_PRESENT(AMI_AUDIO)) |
446 | amiga_init_sound(); |
447 | |
448 | /* |
449 | * if it is an A3000, set the magic bit that forces |
450 | * a hard rekick |
451 | */ |
452 | if (AMIGAHW_PRESENT(MAGIC_REKICK)) |
453 | *(unsigned char *)ZTWO_VADDR(0xde0002) |= 0x80; |
454 | } |
455 | |
456 | static u64 amiga_read_clk(struct clocksource *cs); |
457 | |
458 | static struct clocksource amiga_clk = { |
459 | .name = "ciab" , |
460 | .rating = 250, |
461 | .read = amiga_read_clk, |
462 | .mask = CLOCKSOURCE_MASK(32), |
463 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
464 | }; |
465 | |
466 | static unsigned short jiffy_ticks; |
467 | static u32 clk_total, clk_offset; |
468 | |
469 | static irqreturn_t ciab_timer_handler(int irq, void *dev_id) |
470 | { |
471 | clk_total += jiffy_ticks; |
472 | clk_offset = 0; |
473 | legacy_timer_tick(ticks: 1); |
474 | timer_heartbeat(); |
475 | |
476 | return IRQ_HANDLED; |
477 | } |
478 | |
479 | static void __init amiga_sched_init(void) |
480 | { |
481 | static struct resource sched_res = { |
482 | .name = "timer" , .start = 0x00bfd400, .end = 0x00bfd5ff, |
483 | }; |
484 | jiffy_ticks = DIV_ROUND_CLOSEST(amiga_eclock, HZ); |
485 | |
486 | if (request_resource(root: &mb_resources._ciab, new: &sched_res)) |
487 | pr_warn("Cannot allocate ciab.ta{lo,hi}\n" ); |
488 | ciab.cra &= 0xC0; /* turn off timer A, continuous mode, from Eclk */ |
489 | ciab.talo = jiffy_ticks % 256; |
490 | ciab.tahi = jiffy_ticks / 256; |
491 | |
492 | /* install interrupt service routine for CIAB Timer A |
493 | * |
494 | * Please don't change this to use ciaa, as it interferes with the |
495 | * SCSI code. We'll have to take a look at this later |
496 | */ |
497 | if (request_irq(IRQ_AMIGA_CIAB_TA, ciab_timer_handler, IRQF_TIMER, |
498 | "timer" , NULL)) |
499 | pr_err("Couldn't register timer interrupt\n" ); |
500 | /* start timer */ |
501 | ciab.cra |= 0x11; |
502 | |
503 | clocksource_register_hz(cs: &amiga_clk, hz: amiga_eclock); |
504 | } |
505 | |
506 | static u64 amiga_read_clk(struct clocksource *cs) |
507 | { |
508 | unsigned short hi, lo, hi2; |
509 | unsigned long flags; |
510 | u32 ticks; |
511 | |
512 | local_irq_save(flags); |
513 | |
514 | /* read CIA B timer A current value */ |
515 | hi = ciab.tahi; |
516 | lo = ciab.talo; |
517 | hi2 = ciab.tahi; |
518 | |
519 | if (hi != hi2) { |
520 | lo = ciab.talo; |
521 | hi = hi2; |
522 | } |
523 | |
524 | ticks = hi << 8 | lo; |
525 | |
526 | if (ticks > jiffy_ticks / 2) |
527 | /* check for pending interrupt */ |
528 | if (cia_set_irq(&ciab_base, 0) & CIA_ICR_TA) |
529 | clk_offset = jiffy_ticks; |
530 | |
531 | ticks = jiffy_ticks - ticks; |
532 | ticks += clk_offset + clk_total; |
533 | |
534 | local_irq_restore(flags); |
535 | |
536 | return ticks; |
537 | } |
538 | |
539 | static void amiga_reset(void) __noreturn; |
540 | |
541 | static void amiga_reset(void) |
542 | { |
543 | unsigned long jmp_addr040 = virt_to_phys(address: &&jmp_addr_label040); |
544 | unsigned long jmp_addr = virt_to_phys(address: &&jmp_addr_label); |
545 | |
546 | local_irq_disable(); |
547 | if (CPU_IS_040_OR_060) |
548 | /* Setup transparent translation registers for mapping |
549 | * of 16 MB kernel segment before disabling translation |
550 | */ |
551 | asm volatile ("\n" |
552 | " move.l %0,%%d0\n" |
553 | " and.l #0xff000000,%%d0\n" |
554 | " or.w #0xe020,%%d0\n" /* map 16 MB, enable, cacheable */ |
555 | " .chip 68040\n" |
556 | " movec %%d0,%%itt0\n" |
557 | " movec %%d0,%%dtt0\n" |
558 | " .chip 68k\n" |
559 | " jmp %0@\n" |
560 | : /* no outputs */ |
561 | : "a" (jmp_addr040) |
562 | : "d0" ); |
563 | else |
564 | /* for 680[23]0, just disable translation and jump to the physical |
565 | * address of the label |
566 | */ |
567 | asm volatile ("\n" |
568 | " pmove %%tc,%@\n" |
569 | " bclr #7,%@\n" |
570 | " pmove %@,%%tc\n" |
571 | " jmp %0@\n" |
572 | : /* no outputs */ |
573 | : "a" (jmp_addr)); |
574 | jmp_addr_label040: |
575 | /* disable translation on '040 now */ |
576 | asm volatile ("\n" |
577 | " moveq #0,%%d0\n" |
578 | " .chip 68040\n" |
579 | " movec %%d0,%%tc\n" /* disable MMU */ |
580 | " .chip 68k\n" |
581 | : /* no outputs */ |
582 | : /* no inputs */ |
583 | : "d0" ); |
584 | |
585 | jmp_addr_label: |
586 | /* pickup reset address from AmigaOS ROM, reset devices and jump |
587 | * to reset address |
588 | */ |
589 | asm volatile ("\n" |
590 | " move.w #0x2700,%sr\n" |
591 | " lea 0x01000000,%a0\n" |
592 | " sub.l %a0@(-0x14),%a0\n" |
593 | " move.l %a0@(4),%a0\n" |
594 | " subq.l #2,%a0\n" |
595 | " jra 1f\n" |
596 | /* align on a longword boundary */ |
597 | " " __ALIGN_STR "\n" |
598 | "1:\n" |
599 | " reset\n" |
600 | " jmp %a0@" ); |
601 | |
602 | for (;;) |
603 | ; |
604 | } |
605 | |
606 | |
607 | /* |
608 | * Debugging |
609 | */ |
610 | |
611 | #define SAVEKMSG_MAXMEM 128*1024 |
612 | |
613 | #define SAVEKMSG_MAGIC1 0x53415645 /* 'SAVE' */ |
614 | #define SAVEKMSG_MAGIC2 0x4B4D5347 /* 'KMSG' */ |
615 | |
616 | struct savekmsg { |
617 | unsigned long magic1; /* SAVEKMSG_MAGIC1 */ |
618 | unsigned long magic2; /* SAVEKMSG_MAGIC2 */ |
619 | unsigned long magicptr; /* address of magic1 */ |
620 | unsigned long size; |
621 | char data[]; |
622 | }; |
623 | |
624 | static struct savekmsg *savekmsg; |
625 | |
626 | static void amiga_mem_console_write(struct console *co, const char *s, |
627 | unsigned int count) |
628 | { |
629 | if (savekmsg->size + count <= SAVEKMSG_MAXMEM-sizeof(struct savekmsg)) { |
630 | memcpy(savekmsg->data + savekmsg->size, s, count); |
631 | savekmsg->size += count; |
632 | } |
633 | } |
634 | |
635 | static int __init amiga_savekmsg_setup(char *arg) |
636 | { |
637 | bool registered; |
638 | |
639 | if (!MACH_IS_AMIGA || strcmp(arg, "mem" )) |
640 | return 0; |
641 | |
642 | if (amiga_chip_size < SAVEKMSG_MAXMEM) { |
643 | pr_err("Not enough chipram for debugging\n" ); |
644 | return -ENOMEM; |
645 | } |
646 | |
647 | /* Just steal the block, the chipram allocator isn't functional yet */ |
648 | amiga_chip_size -= SAVEKMSG_MAXMEM; |
649 | savekmsg = ZTWO_VADDR(CHIP_PHYSADDR + amiga_chip_size); |
650 | savekmsg->magic1 = SAVEKMSG_MAGIC1; |
651 | savekmsg->magic2 = SAVEKMSG_MAGIC2; |
652 | savekmsg->magicptr = ZTWO_PADDR(savekmsg); |
653 | savekmsg->size = 0; |
654 | |
655 | registered = !!amiga_console_driver.write; |
656 | amiga_console_driver.write = amiga_mem_console_write; |
657 | if (!registered) |
658 | register_console(&amiga_console_driver); |
659 | return 0; |
660 | } |
661 | |
662 | early_param("debug" , amiga_savekmsg_setup); |
663 | |
664 | static void amiga_serial_putc(char c) |
665 | { |
666 | amiga_custom.serdat = (unsigned char)c | 0x100; |
667 | while (!(amiga_custom.serdatr & 0x2000)) |
668 | ; |
669 | } |
670 | |
671 | static void amiga_serial_console_write(struct console *co, const char *s, |
672 | unsigned int count) |
673 | { |
674 | while (count--) { |
675 | if (*s == '\n') |
676 | amiga_serial_putc(c: '\r'); |
677 | amiga_serial_putc(c: *s++); |
678 | } |
679 | } |
680 | |
681 | #if 0 |
682 | void amiga_serial_puts(const char *s) |
683 | { |
684 | amiga_serial_console_write(NULL, s, strlen(s)); |
685 | } |
686 | |
687 | int amiga_serial_console_wait_key(struct console *co) |
688 | { |
689 | int ch; |
690 | |
691 | while (!(amiga_custom.intreqr & IF_RBF)) |
692 | barrier(); |
693 | ch = amiga_custom.serdatr & 0xff; |
694 | /* clear the interrupt, so that another character can be read */ |
695 | amiga_custom.intreq = IF_RBF; |
696 | return ch; |
697 | } |
698 | |
699 | void amiga_serial_gets(struct console *co, char *s, int len) |
700 | { |
701 | int ch, cnt = 0; |
702 | |
703 | while (1) { |
704 | ch = amiga_serial_console_wait_key(co); |
705 | |
706 | /* Check for backspace. */ |
707 | if (ch == 8 || ch == 127) { |
708 | if (cnt == 0) { |
709 | amiga_serial_putc('\007'); |
710 | continue; |
711 | } |
712 | cnt--; |
713 | amiga_serial_puts("\010 \010" ); |
714 | continue; |
715 | } |
716 | |
717 | /* Check for enter. */ |
718 | if (ch == 10 || ch == 13) |
719 | break; |
720 | |
721 | /* See if line is too long. */ |
722 | if (cnt >= len + 1) { |
723 | amiga_serial_putc(7); |
724 | cnt--; |
725 | continue; |
726 | } |
727 | |
728 | /* Store and echo character. */ |
729 | s[cnt++] = ch; |
730 | amiga_serial_putc(ch); |
731 | } |
732 | /* Print enter. */ |
733 | amiga_serial_puts("\r\n" ); |
734 | s[cnt] = 0; |
735 | } |
736 | #endif |
737 | |
738 | static int __init amiga_debug_setup(char *arg) |
739 | { |
740 | bool registered; |
741 | |
742 | if (!MACH_IS_AMIGA || strcmp(arg, "ser" )) |
743 | return 0; |
744 | |
745 | /* no initialization required (?) */ |
746 | registered = !!amiga_console_driver.write; |
747 | amiga_console_driver.write = amiga_serial_console_write; |
748 | if (!registered) |
749 | register_console(&amiga_console_driver); |
750 | return 0; |
751 | } |
752 | |
753 | early_param("debug" , amiga_debug_setup); |
754 | |
755 | #ifdef CONFIG_HEARTBEAT |
756 | static void amiga_heartbeat(int on) |
757 | { |
758 | if (on) |
759 | ciaa.pra &= ~2; |
760 | else |
761 | ciaa.pra |= 2; |
762 | } |
763 | #endif |
764 | |
765 | /* |
766 | * Amiga specific parts of /proc |
767 | */ |
768 | |
769 | static void amiga_get_model(char *model) |
770 | { |
771 | strcpy(p: model, q: amiga_model_name); |
772 | } |
773 | |
774 | |
775 | static void amiga_get_hardware_list(struct seq_file *m) |
776 | { |
777 | if (AMIGAHW_PRESENT(CHIP_RAM)) |
778 | seq_printf(m, "Chip RAM:\t%ldK\n" , amiga_chip_size>>10); |
779 | seq_printf(m, fmt: "PS Freq:\t%dHz\nEClock Freq:\t%ldHz\n" , |
780 | amiga_psfreq, amiga_eclock); |
781 | if (AMIGAHW_PRESENT(AMI_VIDEO)) { |
782 | char *type; |
783 | switch (amiga_chipset) { |
784 | case CS_OCS: |
785 | type = "OCS" ; |
786 | break; |
787 | case CS_ECS: |
788 | type = "ECS" ; |
789 | break; |
790 | case CS_AGA: |
791 | type = "AGA" ; |
792 | break; |
793 | default: |
794 | type = "Old or Unknown" ; |
795 | break; |
796 | } |
797 | seq_printf(m, fmt: "Graphics:\t%s\n" , type); |
798 | } |
799 | |
800 | #define AMIGAHW_ANNOUNCE(name, str) \ |
801 | if (AMIGAHW_PRESENT(name)) \ |
802 | seq_printf (m, "\t%s\n", str) |
803 | |
804 | seq_puts(m, s: "Detected hardware:\n" ); |
805 | AMIGAHW_ANNOUNCE(AMI_VIDEO, "Amiga Video" ); |
806 | AMIGAHW_ANNOUNCE(AMI_BLITTER, "Blitter" ); |
807 | AMIGAHW_ANNOUNCE(AMBER_FF, "Amber Flicker Fixer" ); |
808 | AMIGAHW_ANNOUNCE(AMI_AUDIO, "Amiga Audio" ); |
809 | AMIGAHW_ANNOUNCE(AMI_FLOPPY, "Floppy Controller" ); |
810 | AMIGAHW_ANNOUNCE(A3000_SCSI, "SCSI Controller WD33C93 (A3000 style)" ); |
811 | AMIGAHW_ANNOUNCE(A4000_SCSI, "SCSI Controller NCR53C710 (A4000T style)" ); |
812 | AMIGAHW_ANNOUNCE(A1200_IDE, "IDE Interface (A1200 style)" ); |
813 | AMIGAHW_ANNOUNCE(A4000_IDE, "IDE Interface (A4000 style)" ); |
814 | AMIGAHW_ANNOUNCE(CD_ROM, "Internal CD ROM drive" ); |
815 | AMIGAHW_ANNOUNCE(AMI_KEYBOARD, "Keyboard" ); |
816 | AMIGAHW_ANNOUNCE(AMI_MOUSE, "Mouse Port" ); |
817 | AMIGAHW_ANNOUNCE(AMI_SERIAL, "Serial Port" ); |
818 | AMIGAHW_ANNOUNCE(AMI_PARALLEL, "Parallel Port" ); |
819 | AMIGAHW_ANNOUNCE(A2000_CLK, "Hardware Clock (A2000 style)" ); |
820 | AMIGAHW_ANNOUNCE(A3000_CLK, "Hardware Clock (A3000 style)" ); |
821 | AMIGAHW_ANNOUNCE(CHIP_RAM, "Chip RAM" ); |
822 | AMIGAHW_ANNOUNCE(PAULA, "Paula 8364" ); |
823 | AMIGAHW_ANNOUNCE(DENISE, "Denise 8362" ); |
824 | AMIGAHW_ANNOUNCE(DENISE_HR, "Denise 8373" ); |
825 | AMIGAHW_ANNOUNCE(LISA, "Lisa 8375" ); |
826 | AMIGAHW_ANNOUNCE(AGNUS_PAL, "Normal/Fat PAL Agnus 8367/8371" ); |
827 | AMIGAHW_ANNOUNCE(AGNUS_NTSC, "Normal/Fat NTSC Agnus 8361/8370" ); |
828 | AMIGAHW_ANNOUNCE(AGNUS_HR_PAL, "Fat Hires PAL Agnus 8372" ); |
829 | AMIGAHW_ANNOUNCE(AGNUS_HR_NTSC, "Fat Hires NTSC Agnus 8372" ); |
830 | AMIGAHW_ANNOUNCE(ALICE_PAL, "PAL Alice 8374" ); |
831 | AMIGAHW_ANNOUNCE(ALICE_NTSC, "NTSC Alice 8374" ); |
832 | AMIGAHW_ANNOUNCE(MAGIC_REKICK, "Magic Hard Rekick" ); |
833 | AMIGAHW_ANNOUNCE(PCMCIA, "PCMCIA Slot" ); |
834 | #ifdef CONFIG_ZORRO |
835 | if (AMIGAHW_PRESENT(ZORRO)) |
836 | seq_printf(m, "\tZorro II%s AutoConfig: %d Expansion " |
837 | "Device%s\n" , |
838 | AMIGAHW_PRESENT(ZORRO3) ? "I" : "" , |
839 | zorro_num_autocon, zorro_num_autocon == 1 ? "" : "s" ); |
840 | #endif /* CONFIG_ZORRO */ |
841 | |
842 | #undef AMIGAHW_ANNOUNCE |
843 | } |
844 | |
845 | /* |
846 | * The Amiga keyboard driver needs key_maps, but we cannot export it in |
847 | * drivers/char/defkeymap.c, as it is autogenerated |
848 | */ |
849 | #ifdef CONFIG_HW_CONSOLE |
850 | EXPORT_SYMBOL_GPL(key_maps); |
851 | #endif |
852 | |