Discussion:
[RFC][Patchset 1/20] Support for PC-9800
Osamu Tomita
2002-11-02 17:33:45 UTC
Permalink
This patchset add support for NEC PC-9800 architecture, against 2.5.45.

PC-9800 is one of i386 sub-architecture, made by NEC Japan.
I update patchset agaist 2.5.45 and do some cleanups.

Best regards,
Osamu tomita sf.jp Linux/98 project
-

This is a part 1/20 of patchset. (APM)

Summary:
APM related modules
- adapted to BIOS spec. differences.

diffstat:
arch/i386/kernel/apm.c | 55 +++++++++++++++++++++++++++++++++++++++++------
include/linux/apm_bios.h | 24 ++++++++++++++++++++
2 files changed, 72 insertions(+), 7 deletions(-)

patch:
diff -urN linux/arch/i386/kernel/apm.c linux98/arch/i386/kernel/apm.c
--- linux/arch/i386/kernel/apm.c Thu Oct 31 13:23:02 2002
+++ linux98/arch/i386/kernel/apm.c Thu Oct 31 13:35:09 2002
@@ -227,6 +227,8 @@
#include <linux/sysrq.h>
+#include "io_ports.h"
+
extern rwlock_t xtime_lock;
extern spinlock_t i8253_lock;
extern unsigned long get_cmos_time(void);
@@ -377,6 +379,15 @@
#define DEFAULT_IDLE_PERIOD (100 / 3)
/*
+ * PC-9800 BIOS reports version by BCD format
+ */
+#ifdef CONFIG_PC9800
+#define BIOS_VERSION_HILO_FMT "%d.%02x"
+#else
+#define BIOS_VERSION_HILO_FMT "%d.%d"
+#endif
+
+/*
* Local variables
*/
static struct {
@@ -629,6 +640,9 @@
__asm__ __volatile__(APM_DO_ZERO_SEGS
"pushl %%edi\n\t"
"pushl %%ebp\n\t"
+#ifdef CONFIG_PC9800
+ "pushfl\n\t"
+#endif
"lcall *%%cs:apm_bios_entry\n\t"
"setc %%al\n\t"
"popl %%ebp\n\t"
@@ -690,6 +704,9 @@
__asm__ __volatile__(APM_DO_ZERO_SEGS
"pushl %%edi\n\t"
"pushl %%ebp\n\t"
+#ifdef CONFIG_PC9800
+ "pushfl\n\t"
+#endif
"lcall *%%cs:apm_bios_entry\n\t"
"setc %%bl\n\t"
"popl %%ebp\n\t"
@@ -961,7 +978,7 @@
/*
* This may be called on an SMP machine.
*/
-#ifdef CONFIG_SMP
+#if defined(CONFIG_SMP) && !defined(CONFIG_PC9800)
/* Some bioses don't like being called from CPU != 0 */
if (smp_processor_id() != 0) {
set_cpus_allowed(current, 1 << 0);
@@ -1241,11 +1258,11 @@
{
#ifdef INIT_TIMER_AFTER_SUSPEND
/* set the clock to 100 Hz */
- outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
+ outb_p(0x34, PIT_MODE); /* binary, mode 2, LSB/MSB, ch 0 */
udelay(10);
- outb_p(LATCH & 0xff , 0x40); /* LSB */
+ outb_p(LATCH & 0xff, PIT_CH0); /* LSB */
udelay(10);
- outb(LATCH >> 8 , 0x40); /* MSB */
+ outb(LATCH >> 8, PIT_CH0); /* MSB */
udelay(10);
#endif
}
@@ -1720,7 +1737,8 @@
-1: Unknown
8) min = minutes; sec = seconds */
- p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
+ p += sprintf(p, "%s " BIOS_VERSION_HILO_FMT + " 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
driver_version,
(apm_info.bios.version >> 8) & 0xff,
apm_info.bios.version & 0xff,
@@ -1744,6 +1762,8 @@
char * power_stat;
char * bat_stat;
+#if !defined(CONFIG_PC9800) || !defined(CONFIG_SMP)
+ /* Deamonize causes freeze on PC-9800 SMP box */
kapmd_running = 1;
daemonize();
@@ -1751,6 +1771,7 @@
strcpy(current->comm, "kapmd");
current->flags |= PF_IOTHREAD;
sigfillset(&current->blocked);
+#endif /* !CONFIG_PC9800 || !CONFIG_SMP */
#ifdef CONFIG_SMP
/* 2002/08/01 - WT
@@ -1780,6 +1801,17 @@
/* Fall back to an APM 1.0 connection. */
apm_info.connection_version = 0x100;
}
+#ifdef CONFIG_PC9800
+ else {
+ printk("PC-9801 APM BIOS BUG work around: "
+ "fix connection_version 0x%x to ",
+ apm_info.connection_version);
+ apm_info.connection_version =
+ (apm_info.connection_version & 0xff00)
+ | ((apm_info.connection_version & 0x00f0) >> 4);
+ printk("0x%x\n", apm_info.connection_version);
+ }
+#endif /* CONFIG_PC9800 */
}
}
@@ -1956,8 +1988,8 @@
printk(KERN_INFO "apm: BIOS not found.\n");
return -ENODEV;
}
- printk(KERN_INFO
- "apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n",
+ printk(KERN_INFO "apm: BIOS version " BIOS_VERSION_HILO_FMT
+ " Flags 0x%02x (Driver version %s)\n",
((apm_info.bios.version >> 8) & 0xff),
(apm_info.bios.version & 0xff),
apm_info.bios.flags,
@@ -1984,6 +2016,11 @@
if (apm_info.bios.version == 0x001)
apm_info.bios.version = 0x100;
+#ifdef CONFIG_PC9800
+ /* In PC-9800, APM BIOS version is written in BCD...?? */
+ apm_info.bios.version = (apm_info.bios.version & 0xff00)
+ | ((apm_info.bios.version & 0x00f0) >> 4);
+#endif
/* BIOS < 1.2 doesn't set cseg_16_len */
if (apm_info.bios.version < 0x102)
apm_info.bios.cseg_16_len = 0; /* 64k */
@@ -2067,7 +2104,11 @@
if (apm_proc)
SET_MODULE_OWNER(apm_proc);
+#if defined(CONFIG_PC9800) && defined(CONFIG_SMP)
+ apm(0);
+#else
kernel_thread(apm, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
+#endif
if (num_online_cpus() > 1 && !smp ) {
printk(KERN_NOTICE
diff -urN linux/include/linux/apm_bios.h linux98/include/linux/apm_bios.h
--- linux/include/linux/apm_bios.h Wed Aug 28 09:52:31 2002
+++ linux98/include/linux/apm_bios.h Wed Aug 28 13:34:09 2002
@@ -20,6 +20,7 @@
typedef unsigned short apm_eventinfo_t;
#ifdef __KERNEL__
+#include <linux/config.h>
#define APM_CS (GDT_ENTRY_APMBIOS_BASE * 8)
#define APM_CS_16 (APM_CS + 8)
@@ -60,6 +61,7 @@
/*
* The APM function codes
*/
+#ifndef CONFIG_PC9800
#define APM_FUNC_INST_CHECK 0x5300
#define APM_FUNC_REAL_CONN 0x5301
#define APM_FUNC_16BIT_CONN 0x5302
@@ -80,6 +82,28 @@
#define APM_FUNC_RESUME_TIMER 0x5311
#define APM_FUNC_RESUME_ON_RING 0x5312
#define APM_FUNC_TIMER 0x5313
+#else
+#define APM_FUNC_INST_CHECK 0x9a00
+#define APM_FUNC_REAL_CONN 0x9a01
+#define APM_FUNC_16BIT_CONN 0x9a02
+#define APM_FUNC_32BIT_CONN 0x9a03
+#define APM_FUNC_DISCONN 0x9a04
+#define APM_FUNC_IDLE 0x9a05
+#define APM_FUNC_BUSY 0x9a06
+#define APM_FUNC_SET_STATE 0x9a07
+#define APM_FUNC_ENABLE_PM 0x9a08
+#define APM_FUNC_RESTORE_BIOS 0x9a09
+#define APM_FUNC_GET_STATUS 0x9a3a
+#define APM_FUNC_GET_EVENT 0x9a0b
+#define APM_FUNC_GET_STATE 0x9a0c
+#define APM_FUNC_ENABLE_DEV_PM 0x9a0d
+#define APM_FUNC_VERSION 0x9a3e
+#define APM_FUNC_ENGAGE_PM 0x9a3f
+#define APM_FUNC_GET_CAP 0x9a10
+#define APM_FUNC_RESUME_TIMER 0x9a11
+#define APM_FUNC_RESUME_ON_RING 0x9a12
+#define APM_FUNC_TIMER 0x9a13
+#endif
/*
* Function code for APM_FUNC_RESUME_TIMER
Osamu Tomita
2002-11-02 17:44:46 UTC
Permalink
This is a part 2/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
boot related modules
- adapted to BIOS spec. differences.

diffstat:
arch/i386/boot98/Makefile | 90 +++
arch/i386/boot98/bootsect.S | 397 +++++++++++++
arch/i386/boot98/compressed/Makefile | 26 arch/i386/boot98/compressed/head.S | 128 ++++
arch/i386/boot98/compressed/misc.c | 375 ++++++++++++
arch/i386/boot98/compressed/vmlinux.scr | 9 arch/i386/boot98/install.sh | 40 +
arch/i386/boot98/setup.S | 950 ++++++++++++++++++++++++++++++++
arch/i386/boot98/tools/build.c | 188 ++++++
arch/i386/boot98/video.S | 262 ++++++++
10 files changed, 2465 insertions(+)

patch:
diff -urN linux/arch/i386/boot98/Makefile linux98/arch/i386/boot98/Makefile
--- linux/arch/i386/boot98/Makefile Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/boot98/Makefile Sat Oct 19 13:01:49 2002
@@ -0,0 +1,90 @@
+#
+# arch/i386/boot/Makefile
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License. See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1994 by Linus Torvalds
+#
+
+# ROOT_DEV specifies the default root-device when making the image.
+# This can be either FLOPPY, CURRENT, /dev/xxxx or empty, in which case
+# the default of FLOPPY is used by 'build'.
+
+ROOT_DEV := CURRENT
+
+# If you want to preset the SVGA mode, uncomment the next line and
+# set SVGA_MODE to whatever number you want.
+# Set it to -DSVGA_MODE=NORMAL_VGA if you just want the EGA/VGA mode.
+# The number is the same as you would ordinarily press at bootup.
+
+SVGA_MODE := -DSVGA_MODE=NORMAL_VGA
+
+# If you want the RAM disk device, define this to be the size in blocks.
+
+#RAMDISK := -DRAMDISK=512
+
+EXTRA_TARGETS := vmlinux.bin bootsect bootsect.o \
+ setup setup.o zImage bzImage
+
+subdir- := compressed
+
+host-progs := tools/build
+
+# Default
+
+boot: bzImage
+
+include $(TOPDIR)/Rules.make
+
+# ---------------------------------------------------------------------------
+
+$(obj)/zImage: IMAGE_OFFSET := 0x1000
+$(obj)/zImage: EXTRA_AFLAGS := -traditional $(SVGA_MODE) $(RAMDISK)
+$(obj)/bzImage: IMAGE_OFFSET := 0x100000
+$(obj)/bzImage: EXTRA_AFLAGS := -traditional $(SVGA_MODE) $(RAMDISK) -D__BIG_KERNEL__
+$(obj)/bzImage: BUILDFLAGS := -b
+
+quiet_cmd_image = BUILD $(echo_target)
+cmd_image = $(obj)/tools/build $(BUILDFLAGS) $(obj)/bootsect $(obj)/setup \
+ $(obj)/vmlinux.bin $(ROOT_DEV) > $@
+
+$(obj)/zImage $(obj)/bzImage: $(obj)/bootsect $(obj)/setup \
+ $(obj)/vmlinux.bin $(obj)/tools/build FORCE
+ $(call if_changed,image)
+
+$(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE
+ $(call if_changed,objcopy)
+
+LDFLAGS_bootsect := -Ttext 0x0 -s --oformat binary
+LDFLAGS_setup := -Ttext 0x0 -s --oformat binary -e begtext
+
+$(obj)/setup $(obj)/bootsect: %: %.o FORCE
+ $(call if_changed,ld)
+
+$(obj)/compressed/vmlinux: FORCE
+ +@$(call descend,$(obj)/compressed,IMAGE_OFFSET=$(IMAGE_OFFSET) \
+ $(obj)/compressed/vmlinux)
+
+
+zdisk: $(BOOTIMAGE)
+ dd bs=8192 if=$(BOOTIMAGE) of=/dev/fd0
+
+zlilo: $(BOOTIMAGE)
+ if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi
+ if [ -f $(INSTALL_PATH)/System.map ]; then mv $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi
+ cat $(BOOTIMAGE) > $(INSTALL_PATH)/vmlinuz
+ cp System.map $(INSTALL_PATH)/
+ if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
+
+install: $(BOOTIMAGE)
+ sh $(src)/install.sh $(KERNELRELEASE) $(BOOTIMAGE) System.map "$(INSTALL_PATH)"
+
+archhelp:
+ @echo '* bzImage - Compressed kernel image (arch/$(ARCH)/boot/bzImage)'
+ @echo ' install - Install kernel using'
+ @echo ' (your) ~/bin/installkernel or'
+ @echo ' (distribution) /sbin/installkernel or'
+ @echo ' install to $$(INSTALL_PATH) and run lilo'
+
diff -urN linux/arch/i386/boot98/bootsect.S linux98/arch/i386/boot98/bootsect.S
--- linux/arch/i386/boot98/bootsect.S Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/boot98/bootsect.S Wed May 22 09:13:46 2002
@@ -0,0 +1,397 @@
+/*
+ * bootsect.S - boot sector for NEC PC-9800 series
+ *
+ * Linux/98 project at Kyoto University Microcomputer Club (KMC)
+ * FUJITA Norimasa, TAKAI Kousuke 1997-1998
+ * rewritten by TAKAI Kousuke (as86 -> gas), Nov 1999
+ *
+ * Based on:
+ * bootsect.S Copyright (C) 1991, 1992 Linus Torvalds
+ * modified by Drew Eckhardt
+ * modified by Bruce Evans (bde)
+ *
+ * bootsect.S is loaded at 0x1FC00 or 0x1FE00 by the bios-startup routines,
+ * and moves itself out of the way to address 0x90000, and jumps there.
+ *
+ * It then loads 'setup' directly after itself (0x90200), and the system
+ * at 0x10000, using BIOS interrupts. + *
+ * NOTE! currently system is at most (8*65536-4096) bytes long. This should + * be no problem, even in the future. I want to keep it simple. This 508 kB
+ * kernel size should be enough, especially as this doesn't contain the
+ * buffer cache as in minix (and especially now that the kernel is + * compressed :-)
+ *
+ * The loader has been made as simple as possible, and continuous
+ * read errors will result in a unbreakable loop. Reboot by hand. It
+ * loads pretty fast by getting whole tracks at a time whenever possible.
+ */
+
+#include <linux/config.h> /* for CONFIG_ROOT_RDONLY */
+#include <asm/boot.h>
+
+SETUPSECTS = 4 /* default nr of setup-sectors */
+BOOTSEG = 0x1FC0 /* original address of boot-sector */
+INITSEG = DEF_INITSEG /* we move boot here - out of the way */
+SETUPSEG = DEF_SETUPSEG /* setup starts here */
+SYSSEG = DEF_SYSSEG /* system loaded at 0x10000 (65536) */
+SYSSIZE = DEF_SYSSIZE /* system size: # of 16-byte clicks */
+ /* to be loaded */
+ROOT_DEV = 0 /* ROOT_DEV is now written by "build" */
+SWAP_DEV = 0 /* SWAP_DEV is now written by "build" */
+
+#ifndef SVGA_MODE
+#define SVGA_MODE ASK_VGA
+#endif
+
+#ifndef RAMDISK
+#define RAMDISK 0
+#endif +
+#ifndef ROOT_RDONLY
+#define ROOT_RDONLY 1
+#endif
+
+/* normal/hireso text VRAM segments */
+#define NORMAL_TEXT 0xa000
+#define HIRESO_TEXT 0xe000
+
+/* bios work area addresses */
+#define EXPMMSZ 0x0401
+#define BIOS_FLAG 0x0501
+#define DISK_BOOT 0x0584
+
+.code16
+.text
+
+.global _start
+_start:
+
+#if 0 /* hook for debugger, harmless unless BIOS is fussy (old HP) */
+ int $0x3
+#endif
+ jmp real_start
+ .ascii "Linux 98"
+ .word 0
+real_start:
+ xorw %di, %di /* %di = 0 */
+ movw %di, %ss /* %ss = 0 */
+ movw $0x03F0, %sp
+ pushw %cx /* for hint */
+
+ movw $0x0A00, %ax /* normal mode defaults (80x25) */
+
+ testb $0x08, %ss:BIOS_FLAG /* check hi-reso bit */
+ jnz set_crt_mode
+/*
+ * Hi-Reso (high-resolution) machine.
+ *
+ * Some hi-reso machines have no RAMs on bank 8/A (0x080000 - 0x0BFFFF).
+ * On such machines we get two RAM banks from top of protect menory and
+ * map them on bank 8/A.
+ * These work-around must be done before moving myself on INITSEG (0x090000-).
+ */
+ movw $(HIRESO_TEXT >> 8), %cs:(vram + 1) /* text VRAM segment */
+
+ /* set memory window */
+ movb $0x08, %al
+ outb %al, $0x91 /* map native RAM (if any) */
+ movb $0x0A, %al
+ outb %al, $0x93
+
+ /* check bank ram A */
+ pushw $0xA500
+ popw %ds
+ movw (%di), %cx /* %si == 0 from entry */
+ notw %cx
+ movw %cx, (%di)
+
+ movw $0x43F, %dx /* cache flush for 486 and up. */
+ movb $0xA0, %al
+ outb %al, %dx
+
+ cmpw %cx, (%di)
+ je hireso_done
+
+ /* + * Write test failed; we have no native RAM on 080000h - 0BFFFFh.
+ * Take 256KB of RAM from top of protected memory.
+ */
+ movb %ss:EXPMMSZ, %al
+ subb $2, %al /* reduce 2 x 128KB */
+ movb %al, %ss:EXPMMSZ
+ addb %al, %al
+ addb $0x10, %al
+ outb %al, $0x91
+ addb $2, %al
+ outb %al, $0x93
+
+hireso_done:
+ movb $0x10, %al /* CRT mode 80x31, %ah still 0Ah */
+
+set_crt_mode:
+ int $0x18 /* set CRT mode */
+
+ movb $0x0C, %ah /* turn on text displaying */
+ int $0x18
+
+ xorw %dx, %dx /* position cursor to home */
+ movb $0x13, %ah
+ int $0x18
+
+ movb $0x11, %ah /* turn cursor displaying on */
+ int $0x18
+
+ /* move 1 kilobytes from [BOOTSEG:0000h] to [INITSEG:0000h] */
+ cld
+ xorw %si, %si
+ pushw $INITSEG
+ popw %es
+ movw $512, %cx /* %di == 0 from entry */
+ rep
+ cs
+ movsw
+
+ ljmp $INITSEG, $go
+
+go:
+ pushw %cs
+ popw %ds /* %ds = %cs */
+
+ popw %dx /* %dh = saved %ch passed from BIOS */
+ movb %ss:DISK_BOOT, %al
+ andb $0xf0, %al /* %al = Device Address */
+ movb $18, %ch /* 18 secs/track, 512 b/sec (1440 KB) */
+ cmpb $0x30, %al
+ je try512
+ cmpb $0x90, %al /* 1 MB I/F, 1 MB floppy */
+ je try1.2M
+ cmpb $0xf0, %al /* 640 KB I/F, 1 MB floppy */
+ je try1.2M
+ movb $9, %ch /* 9 secs/track, 512 b/sec ( 720 KB) */
+ cmpb $0x10, %al /* 1 MB I/F, 640 KB floppy */
+ je try512
+ cmpb $0x70, %al /* 640 KB I/F, 640 KB floppy */
+ jne error /* unknown device? */
+
+ /* XXX: Does it make sense to support 8 secs/track, 512 b/sec + (640 KB) floppy? */
+
+try512: movb $2, %cl /* 512 b/sec */
+lasttry:call tryload
+/*
+ * Display error message and halt
+ */
+error: movw $error_msg, %si
+ call print
+wait_reboot:
+ movb $0x0, %ah
+ int $0x18 /* wait keyboard input */
+1: movb $0, %al
+ outb %al, $0xF0 /* reset CPU */
+ jmp 1b /* just in case... */
+
+try1.2M:cmpb $2, %dh
+ je try2HC
+ movw $0x0803, %cx /* 8 secs/track, 1024 b/sec (1232 KB) */
+ call tryload
+ movb $15, %ch /* 15 secs/track, 512 b/sec (1200 KB) */
+ jmp try512
+try2HC: movw $0x0F02, %cx /* 15 secs/track, 512 b/sec (1200 KB) */
+ call tryload
+ movw $0x0803, %cx /* 8 secs/track, 1024 b/sec (1232 KB) */
+ jmp lasttry
+
+/*
+ * Try to load SETUP and SYSTEM provided geometry information in %cx.
+ * This routine *will not* return on successful load...
+ */
+tryload:
+ movw %cx, sectlen
+ movb %ss:DISK_BOOT, %al
+ movb $0x7, %ah /* recalibrate the drive */
+ int $0x1b
+ jc error /* recalibration should succeed */
+
+ /*
+ * Load SETUP into memory. It is assumed that SETUP fits into
+ * first cylinder (2 tracks, 9KB on 2DD, 15-18KB on 2HD).
+ */
+ movb $0, %bl
+ movb setup_sects, %bh
+ incb %bh
+ shlw %bx /* %bx = (setup_sects + 1) * 512 */
+ movw $128, %bp
+ shlw %cl, %bp /* %bp = <sector size> */
+ subw %bp, %bx /* length to load */
+ movw $0x0002, %dx /* head 0, sector 2 */
+ movb %cl, %ch /* `N' for sector address */
+ movb $0, %cl /* cylinder 0 */
+ pushw %cs
+ popw %es /* %es = %cs (= INITSEG) */
+ movb $0xd6, %ah /* read, multi-track, MFM */
+ int $0x1b /* load it! */
+ jc read_error
+
+ movw $loading_msg, %si
+ call print
+
+ movw $SYSSEG, %ax
+ movw %ax, %es /* %es = SYSSEG */
+
+/*
+ * This routine loads the system at address 0x10000, making sure
+ * no 64kB boundaries are crossed. We try to load it as fast as
+ * possible, loading whole tracks whenever we can.
+ *
+ * in: es - starting address segment (normally 0x1000)
+ */
+ movb %ch, %cl
+ addb $7, %cl /* %cl = log2 <sector_size> */
+ shrw %cl, %bx /* %bx = # of phys. sectors in SETUP */
+ addb %bl, %dl /* %dl = start sector # of SYSTEM */
+ decb %dl /* %dl is 0-based in below loop */
+
+rp_read_newseg:
+ xorw %bp, %bp /* = starting address within segment */
+#ifdef __BIG_KERNEL__
+ bootsect_kludge = 0x220 /* 0x200 (size of bootsector) + 0x20 (offset */
+ lcall *bootsect_kludge /* of bootsect_kludge in setup.S */
+#else
+ movw %es, %ax
+ subw $SYSSEG, %ax
+#endif
+ cmpw syssize, %ax
+ ja boot /* done! */
+
+rp_read:
+ movb sectors, %al
+ addb %al, %al
+ movb %al, %ch /* # of sectors on both surface */
+ subb %dl, %al /* # of sectors left on this track */
+ movb $0, %ah
+ shlw %cl, %ax /* # of bytes left on this track */
+ movw %ax, %bx /* transfer length */
+ addw %bp, %ax /* cross 64K boundary? */
+ jnc 1f /* ok. */
+ jz 1f /* also ok. */
+ /*
+ * Oops, we are crossing 64K boundary...
+ * Adjust transfer length to make transfer fit in the boundary.
+ *
+ * Note: sector size is assumed to be a measure of 65536.
+ */
+ xorw %bx, %bx
+ subw %bp, %bx
+1: pushw %dx
+ movw $dot_msg, %si /* give progress message */
+ call print
+ xchgw %ax, %dx
+ movb $0, %ah
+ divb sectors
+ xchgb %al, %ah
+ xchgw %ax, %dx /* %dh = head # / %dl = sector # */
+ incb %dl /* fix %dl to 1-based */
+ pushw %cx
+ movw cylinder, %cx
+ movb $0xd6, %ah /* read, multi-track, seek, MFM */
+ movb %ss:DISK_BOOT, %al
+ int $0x1b
+ popw %cx
+ popw %dx
+ jc read_error
+ movw %bx, %ax /* # of bytes just read */
+ shrw %cl, %ax /* %ax = # of sectors just read */
+ addb %al, %dl /* advance sector # */
+ cmpb %ch, %dl /* %ch = # of sectors/cylinder */
+ jb 2f
+ incb cylinder /* next cylinder */
+ xorb %dl, %dl /* sector 0 */
+2: addw %bx, %bp /* advance offset pointer */
+ jnc rp_read
+ /* offset pointer wrapped; advance segment pointer. */
+ movw %es, %ax
+ addw $0x1000, %ax
+ movw %ax, %es
+ jmp rp_read_newseg
+
+read_error:
+ ret
+
+boot: movw %cs, %ax /* = INITSEG */
+ /* movw %ax, %ds */
+ movw %ax, %ss
+ movw $0x4000, %sp /* 0x4000 is arbitrary value >=
+ * length of bootsect + length of
+ * setup + room for stack;
+ * PC-9800 never have BIOS workareas
+ * on high memory.
+ */
+/*
+ * After that we check which root-device to use. If the device is
+ * not defined, /dev/fd0 (2, 0) will be used.
+ */
+ cmpw $0, root_dev
+ jne 3f
+ movb $2, root_dev+1
+3:
+
+/*
+ * After that (everything loaded), we jump to the setup-routine
+ * loaded directly after the bootblock:
+ */
+ ljmp $SETUPSEG, $0
+
+/*
+ * Subroutine for print string on console.
+ * %cs:%si - pointer to message
+ */
+print:
+ pushaw
+ pushw %ds
+ pushw %es
+ pushw %cs
+ popw %ds
+ lesw curpos, %di /* %es:%di = current text VRAM addr. */
+1: xorw %ax, %ax
+ lodsb
+ testb %al, %al
+ jz 2f /* end of string */
+ stosw /* character code */
+ movb $0xE1, %es:0x2000-2(%di) /* character attribute */
+ jmp 1b
+2: movw %di, %dx
+ movb $0x13, %ah
+ int $0x18 /* move cursor to current point */
+ popw %es
+ popw %ds
+ popaw
+ ret
+
+loading_msg:
+ .string "Loading"
+dot_msg:
+ .string "."
+error_msg:
+ .string "Read Error!"
+
+ .org 490
+
+curpos: .word 160 /* current cursor position */
+vram: .word NORMAL_TEXT /* text VRAM segment */
+
+cylinder: .byte 0 /* current cylinder (lower byte) */
+sectlen: .byte 0 /* (log2 of <sector size>) - 7 */
+sectors: .byte 0x0F /* default is 2HD (15 sector/track) */
+
+# XXX: This is a fairly snug fit.
+
+.org 497
+setup_sects: .byte SETUPSECTS
+root_flags: .word ROOT_RDONLY
+syssize: .word SYSSIZE
+swap_dev: .word SWAP_DEV
+ram_size: .word RAMDISK
+vid_mode: .word SVGA_MODE
+root_dev: .word ROOT_DEV
+boot_flag: .word 0xAA55
diff -urN linux/arch/i386/boot98/compressed/Makefile linux98/arch/i386/boot98/compressed/Makefile
--- linux/arch/i386/boot98/compressed/Makefile Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/boot98/compressed/Makefile Sat Oct 19 13:02:26 2002
@@ -0,0 +1,26 @@
+#
+# linux/arch/i386/boot/compressed/Makefile
+#
+# create a compressed vmlinux image from the original vmlinux
+#
+
+EXTRA_TARGETS := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o
+EXTRA_AFLAGS := -traditional
+
+include $(TOPDIR)/Rules.make
+
+LDFLAGS_vmlinux := -Ttext $(IMAGE_OFFSET) -e startup_32
+
+$(obj)/vmlinux: $(obj)/head.o $(obj)/misc.o $(obj)/piggy.o FORCE
+ $(call if_changed,ld)
+
+$(obj)/vmlinux.bin: vmlinux FORCE
+ $(call if_changed,objcopy)
+
+$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE
+ $(call if_changed,gzip)
+
+LDFLAGS_piggy.o := -r --format binary --oformat elf32-i386 -T
+
+$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.gz FORCE
+ $(call if_changed,ld)
diff -urN linux/arch/i386/boot98/compressed/head.S linux98/arch/i386/boot98/compressed/head.S
--- linux/arch/i386/boot98/compressed/head.S Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/boot98/compressed/head.S Tue Oct 1 16:07:40 2002
@@ -0,0 +1,128 @@
+/*
+ * linux/boot/head.S
+ *
+ * Copyright (C) 1991, 1992, 1993 Linus Torvalds
+ */
+
+/*
+ * head.S contains the 32-bit startup code.
+ *
+ * NOTE!!! Startup happens at absolute address 0x00001000, which is also where
+ * the page directory will exist. The startup code will be overwritten by
+ * the page directory. [According to comments etc elsewhere on a compressed
+ * kernel it will end up at 0x1000 + 1Mb I hope so as I assume this. - AC]
+ *
+ * Page 0 is deliberately kept safe, since System Management Mode code in + * laptops may need to access the BIOS data stored there. This is also
+ * useful for future device drivers that either access the BIOS via VM86 + * mode.
+ */
+
+/*
+ * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
+ */
+.text
+
+#include <linux/linkage.h>
+#include <asm/segment.h>
+
+ .globl startup_32
+
+startup_32:
+ cld
+ cli
+ movl $(__KERNEL_DS),%eax
+ movl %eax,%ds
+ movl %eax,%es
+ movl %eax,%fs
+ movl %eax,%gs
+
+ lss stack_start,%esp
+ xorl %eax,%eax
+1: incl %eax # check that A20 really IS enabled
+ movl %eax,0x000000 # loop forever if it isn't
+ cmpl %eax,0x100000
+ je 1b
+
+/*
+ * Initialize eflags. Some BIOS's leave bits like NT set. This would
+ * confuse the debugger if this code is traced.
+ * XXX - best to initialize before switching to protected mode.
+ */
+ pushl $0
+ popfl
+/*
+ * Clear BSS
+ */
+ xorl %eax,%eax
+ movl $_edata,%edi
+ movl $_end,%ecx
+ subl %edi,%ecx
+ cld
+ rep
+ stosb
+/*
+ * Do the decompression, and jump to the new kernel..
+ */
+ subl $16,%esp # place for structure on the stack
+ movl %esp,%eax
+ pushl %esi # real mode pointer as second arg
+ pushl %eax # address of structure as first arg
+ call decompress_kernel
+ orl %eax,%eax + jnz 3f
+ popl %esi # discard address
+ popl %esi # real mode pointer
+ xorl %ebx,%ebx
+ ljmp $(__KERNEL_CS), $0x100000
+
+/*
+ * We come here, if we were loaded high.
+ * We need to move the move-in-place routine down to 0x1000
+ * and then start it with the buffer addresses in registers,
+ * which we got from the stack.
+ */
+3:
+ movl $move_routine_start,%esi
+ movl $0x1000,%edi
+ movl $move_routine_end,%ecx
+ subl %esi,%ecx
+ addl $3,%ecx
+ shrl $2,%ecx
+ cld
+ rep
+ movsl
+
+ popl %esi # discard the address
+ popl %ebx # real mode pointer
+ popl %esi # low_buffer_start
+ popl %ecx # lcount
+ popl %edx # high_buffer_start
+ popl %eax # hcount
+ movl $0x100000,%edi
+ cli # make sure we don't get interrupted
+ ljmp $(__KERNEL_CS), $0x1000 # and jump to the move routine
+
+/*
+ * Routine (template) for moving the decompressed kernel in place,
+ * if we were high loaded. This _must_ PIC-code !
+ */
+move_routine_start:
+ movl %ecx,%ebp
+ shrl $2,%ecx
+ rep
+ movsl
+ movl %ebp,%ecx
+ andl $3,%ecx
+ rep
+ movsb
+ movl %edx,%esi
+ movl %eax,%ecx # NOTE: rep movsb won't move if %ecx == 0
+ addl $3,%ecx
+ shrl $2,%ecx
+ rep
+ movsl
+ movl %ebx,%esi # Restore setup pointer
+ xorl %ebx,%ebx
+ ljmp $(__KERNEL_CS), $0x100000
+move_routine_end:
diff -urN linux/arch/i386/boot98/compressed/misc.c linux98/arch/i386/boot98/compressed/misc.c
--- linux/arch/i386/boot98/compressed/misc.c Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/boot98/compressed/misc.c Sat Oct 12 14:26:29 2002
@@ -0,0 +1,375 @@
+/*
+ * misc.c
+ * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux.
+ *
+ * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
+ * puts by Nick Holloway 1993, better puts by Martin Mares 1995
+ * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
+ */
+
+#include <linux/linkage.h>
+#include <linux/vmalloc.h>
+#include <linux/tty.h>
+#include <asm/io.h>
+#ifdef STANDARD_MEMORY_BIOS_CALL
+#undef STANDARD_MEMORY_BIOS_CALL
+#endif
+
+/*
+ * gzip declarations
+ */
+
+#define OF(args) args
+#define STATIC static
+
+#undef memset
+#undef memcpy
+
+/*
+ * Why do we do this? Don't ask me..
+ *
+ * Incomprehensible are the ways of bootloaders.
+ */
+static void* memset(void *, int, size_t);
+static void* memcpy(void *, __const void *, size_t);
+#define memzero(s, n) memset ((s), 0, (n))
+
+typedef unsigned char uch;
+typedef unsigned short ush;
+typedef unsigned long ulg;
+
+#define WSIZE 0x8000 /* Window size must be at least 32k, */
+ /* and a power of two */
+
+static uch *inbuf; /* input buffer */
+static uch window[WSIZE]; /* Sliding window buffer */
+
+static unsigned insize = 0; /* valid bytes in inbuf */
+static unsigned inptr = 0; /* index of next byte to be processed in inbuf */
+static unsigned outcnt = 0; /* bytes in output buffer */
+
+/* gzip flag byte */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */
+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
+#define RESERVED 0xC0 /* bit 6,7: reserved */
+
+#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf())
+
+/* Diagnostic functions */
+#ifdef DEBUG
+# define Assert(cond,msg) {if(!(cond)) error(msg);}
+# define Trace(x) fprintf x
+# define Tracev(x) {if (verbose) fprintf x ;}
+# define Tracevv(x) {if (verbose>1) fprintf x ;}
+# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
+#else
+# define Assert(cond,msg)
+# define Trace(x)
+# define Tracev(x)
+# define Tracevv(x)
+# define Tracec(c,x)
+# define Tracecv(c,x)
+#endif
+
+static int fill_inbuf(void);
+static void flush_window(void);
+static void error(char *m);
+static void gzip_mark(void **);
+static void gzip_release(void **);
+ +/*
+ * This is set up by the setup-routine at boot-time
+ */
+static unsigned char *real_mode; /* Pointer to real-mode data */
+
+#define EXT_MEM_K (*(unsigned short *)(real_mode + 0x2))
+#ifndef STANDARD_MEMORY_BIOS_CALL
+#define ALT_MEM_K (*(unsigned long *)(real_mode + 0x1e0))
+#endif
+#define SCREEN_INFO (*(struct screen_info *)(real_mode+0))
+
+extern char input_data[];
+extern int input_len;
+
+static long bytes_out = 0;
+static uch *output_data;
+static unsigned long output_ptr = 0;
+
+static void *malloc(int size);
+static void free(void *where);
+
+static void puts(const char *);
+
+extern int end;
+static long free_mem_ptr = (long)&end;
+static long free_mem_end_ptr;
+
+#define INPLACE_MOVE_ROUTINE 0x1000
+#define LOW_BUFFER_START 0x2000
+#define LOW_BUFFER_MAX 0x90000
+#define HEAP_SIZE 0x3000
+static unsigned int low_buffer_end, low_buffer_size;
+static int high_loaded =0;
+static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/;
+
+static char *vidmem = (char *)0xa0000;
+static int lines, cols;
+
+#ifdef CONFIG_X86_NUMAQ
+static void * xquad_portio = NULL;
+#endif
+
+#include "../../../../lib/inflate.c"
+
+static void *malloc(int size)
+{
+ void *p;
+
+ if (size <0) error("Malloc error\n");
+ if (free_mem_ptr <= 0) error("Memory error\n");
+
+ free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */
+
+ p = (void *)free_mem_ptr;
+ free_mem_ptr += size;
+
+ if (free_mem_ptr >= free_mem_end_ptr)
+ error("\nOut of memory\n");
+
+ return p;
+}
+
+static void free(void *where)
+{ /* Don't care */
+}
+
+static void gzip_mark(void **ptr)
+{
+ *ptr = (void *) free_mem_ptr;
+}
+
+static void gzip_release(void **ptr)
+{
+ free_mem_ptr = (long) *ptr;
+}
+ +static void scroll(void)
+{
+ int i;
+
+ memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 );
+ for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 )
+ vidmem[i] = ' ';
+}
+
+static void puts(const char *s)
+{
+ int x,y,pos;
+ char c;
+
+ x = SCREEN_INFO.orig_x;
+ y = SCREEN_INFO.orig_y;
+
+ while ( ( c = *s++ ) != '\0' ) {
+ if ( c == '\n' ) {
+ x = 0;
+ if ( ++y >= lines ) {
+ scroll();
+ y--;
+ }
+ } else {
+ vidmem [ ( x + cols * y ) * 2 ] = c; + if ( ++x >= cols ) {
+ x = 0;
+ if ( ++y >= lines ) {
+ scroll();
+ y--;
+ }
+ }
+ }
+ }
+
+ SCREEN_INFO.orig_x = x;
+ SCREEN_INFO.orig_y = y;
+
+ pos = x + cols * y; /* Update cursor position */
+ while (!(inb_p(0x60) & 4));
+ outb_p(0x49, 0x62);
+ outb_p(pos & 0xff, 0x60);
+ outb_p((pos >> 8) & 0xff, 0x60);
+}
+
+static void* memset(void* s, int c, size_t n)
+{
+ int i;
+ char *ss = (char*)s;
+
+ for (i=0;i<n;i++) ss[i] = c;
+ return s;
+}
+
+static void* memcpy(void* __dest, __const void* __src,
+ size_t __n)
+{
+ int i;
+ char *d = (char *)__dest, *s = (char *)__src;
+
+ for (i=0;i<__n;i++) d[i] = s[i];
+ return __dest;
+}
+
+/* ===========================================================================
+ * Fill the input buffer. This is called only when the buffer is empty
+ * and at least one byte is really needed.
+ */
+static int fill_inbuf(void)
+{
+ if (insize != 0) {
+ error("ran out of input data\n");
+ }
+
+ inbuf = input_data;
+ insize = input_len;
+ inptr = 1;
+ return inbuf[0];
+}
+
+/* ===========================================================================
+ * Write the output window window[0..outcnt-1] and update crc and bytes_out.
+ * (Used for the decompressed data only.)
+ */
+static void flush_window_low(void)
+{
+ ulg c = crc; /* temporary variable */
+ unsigned n;
+ uch *in, *out, ch;
+ + in = window;
+ out = &output_data[output_ptr]; + for (n = 0; n < outcnt; n++) {
+ ch = *out++ = *in++;
+ c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
+ }
+ crc = c;
+ bytes_out += (ulg)outcnt;
+ output_ptr += (ulg)outcnt;
+ outcnt = 0;
+}
+
+static void flush_window_high(void)
+{
+ ulg c = crc; /* temporary variable */
+ unsigned n;
+ uch *in, ch;
+ in = window;
+ for (n = 0; n < outcnt; n++) {
+ ch = *output_data++ = *in++;
+ if ((ulg)output_data == low_buffer_end) output_data=high_buffer_start;
+ c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
+ }
+ crc = c;
+ bytes_out += (ulg)outcnt;
+ outcnt = 0;
+}
+
+static void flush_window(void)
+{
+ if (high_loaded) flush_window_high();
+ else flush_window_low();
+}
+
+static void error(char *x)
+{
+ puts("\n\n");
+ puts(x);
+ puts("\n\n -- System halted");
+
+ while(1); /* Halt */
+}
+
+#define STACK_SIZE (4096)
+
+long user_stack [STACK_SIZE];
+
+struct {
+ long * a;
+ short b;
+ } stack_start = { & user_stack [STACK_SIZE] , __KERNEL_DS };
+
+static void setup_normal_output_buffer(void)
+{
+#ifdef STANDARD_MEMORY_BIOS_CALL
+ if (EXT_MEM_K < 1024) error("Less than 2MB of memory.\n");
+#else
+ if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < 1024) error("Less than 2MB of memory.\n");
+#endif
+ output_data = (char *)0x100000; /* Points to 1M */
+ free_mem_end_ptr = (long)real_mode;
+}
+
+struct moveparams {
+ uch *low_buffer_start; int lcount;
+ uch *high_buffer_start; int hcount;
+};
+
+static void setup_output_buffer_if_we_run_high(struct moveparams *mv)
+{
+ high_buffer_start = (uch *)(((ulg)&end) + HEAP_SIZE);
+#ifdef STANDARD_MEMORY_BIOS_CALL
+ if (EXT_MEM_K < (3*1024)) error("Less than 4MB of memory.\n");
+#else
+ if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < (3*1024)) error("Less than 4MB of memory.\n");
+#endif
+ mv->low_buffer_start = output_data = (char *)LOW_BUFFER_START;
+ low_buffer_end = ((unsigned int)real_mode > LOW_BUFFER_MAX
+ ? LOW_BUFFER_MAX : (unsigned int)real_mode) & ~0xfff;
+ low_buffer_size = low_buffer_end - LOW_BUFFER_START;
+ high_loaded = 1;
+ free_mem_end_ptr = (long)high_buffer_start;
+ if ( (0x100000 + low_buffer_size) > ((ulg)high_buffer_start)) {
+ high_buffer_start = (uch *)(0x100000 + low_buffer_size);
+ mv->hcount = 0; /* say: we need not to move high_buffer */
+ }
+ else mv->hcount = -1;
+ mv->high_buffer_start = high_buffer_start;
+}
+
+static void close_output_buffer_if_we_run_high(struct moveparams *mv)
+{
+ if (bytes_out > low_buffer_size) {
+ mv->lcount = low_buffer_size;
+ if (mv->hcount)
+ mv->hcount = bytes_out - low_buffer_size;
+ } else {
+ mv->lcount = bytes_out;
+ mv->hcount = 0;
+ }
+}
+
+
+asmlinkage int decompress_kernel(struct moveparams *mv, void *rmode)
+{
+ real_mode = rmode;
+
+ vidmem = (char *)(((unsigned int)SCREEN_INFO.orig_video_page) << 4);
+
+ lines = SCREEN_INFO.orig_video_lines;
+ cols = SCREEN_INFO.orig_video_cols;
+
+ if (free_mem_ptr < 0x100000) setup_normal_output_buffer();
+ else setup_output_buffer_if_we_run_high(mv);
+
+ makecrc();
+ puts("Uncompressing Linux... ");
+ gunzip();
+ puts("Ok, booting the kernel.\n");
+ if (high_loaded) close_output_buffer_if_we_run_high(mv);
+ return high_loaded;
+}
diff -urN linux/arch/i386/boot98/compressed/vmlinux.scr linux98/arch/i386/boot98/compressed/vmlinux.scr
--- linux/arch/i386/boot98/compressed/vmlinux.scr Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/boot98/compressed/vmlinux.scr Tue Oct 1 16:07:40 2002
@@ -0,0 +1,9 @@
+SECTIONS
+{
+ .data : { + input_len = .;
+ LONG(input_data_end - input_data) input_data = .; + *(.data) + input_data_end = .; + }
+}
diff -urN linux/arch/i386/boot98/install.sh linux98/arch/i386/boot98/install.sh
--- linux/arch/i386/boot98/install.sh Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/boot98/install.sh Tue Oct 1 16:07:35 2002
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# arch/i386/boot/install.sh
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License. See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1995 by Linus Torvalds
+#
+# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin
+#
+# "make install" script for i386 architecture
+#
+# Arguments:
+# $1 - kernel version
+# $2 - kernel image file
+# $3 - kernel map file
+# $4 - default install path (blank if root directory)
+#
+
+# User may have a custom install script
+
+if [ -x ~/bin/installkernel ]; then exec ~/bin/installkernel "$@"; fi
+if [ -x /sbin/installkernel ]; then exec /sbin/installkernel "$@"; fi
+
+# Default install - same as make zlilo
+
+if [ -f $4/vmlinuz ]; then
+ mv $4/vmlinuz $4/vmlinuz.old
+fi
+
+if [ -f $4/System.map ]; then
+ mv $4/System.map $4/System.old
+fi
+
+cat $2 > $4/vmlinuz
+cp $3 $4/System.map
+
+if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
diff -urN linux/arch/i386/boot98/setup.S linux98/arch/i386/boot98/setup.S
--- linux/arch/i386/boot98/setup.S Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/boot98/setup.S Fri Aug 30 17:06:33 2002
@@ -0,0 +1,950 @@
+/*
+ * setup.S Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * setup.s is responsible for getting the system data from the BIOS,
+ * and putting them into the appropriate places in system memory.
+ * both setup.s and system has been loaded by the bootblock.
+ *
+ * This code asks the bios for memory/disk/other parameters, and
+ * puts them in a "safe" place: 0x90000-0x901FF, ie where the
+ * boot-block used to be. It is then up to the protected mode
+ * system to read them from there before the area is overwritten
+ * for buffer-blocks.
+ *
+ * Move PS/2 aux init code to psaux.c
+ * (***@saifr00.cfsat.Honeywell.COM) 03Oct92
+ *
+ * some changes and additional features by Christoph Niemann,
+ * March 1993/June 1994 (***@linux.org)
+ *
+ * add APM BIOS checking by Stephen Rothwell, May 1994
+ * (***@canb.auug.org.au)
+ *
+ * High load stuff, initrd support and position independency
+ * by Hans Lermen & Werner Almesberger, February 1996
+ * <***@elserv.ffm.fgan.de>, <***@lrc.epfl.ch>
+ *
+ * Video handling moved to video.S by Martin Mares, March 1996
+ * <***@k332.feld.cvut.cz>
+ *
+ * Extended memory detection scheme retwiddled by ***@pell.chi.il.us (david
+ * parsons) to avoid loadlin confusion, July 1997
+ *
+ * Transcribed from Intel (as86) -> AT&T (gas) by Chris Noe, May 1999.
+ * <***@northlink.com>
+ *
+ * Fix to work around buggy BIOSes which dont use carry bit correctly
+ * and/or report extended memory in CX/DX for e801h memory size detection + * call. As a result the kernel got wrong figures. The int15/e801h docs
+ * from Ralf Brown interrupt list seem to indicate AX/BX should be used
+ * anyway. So to avoid breaking many machines (presumably there was a reason
+ * to orginally use CX/DX instead of AX/BX), we do a kludge to see
+ * if CX/DX have been changed in the e801 call and if so use AX/BX .
+ * Michael Miller, April 2001 <***@mjmm.org>
+ *
+ * New A20 code ported from SYSLINUX by H. Peter Anvin. AMD Elan bugfixes
+ * by Robert Schwebel, December 2001 <***@schwebel.de>
+ *
+ * Heavily modified for NEC PC-9800 series by Kyoto University Microcomputer
+ * Club (KMC) Linux/98 project <***@kmc.kyoto-u.ac.jp>, 1997-1999
+ */
+
+#include <linux/config.h>
+#include <asm/segment.h>
+#include <linux/version.h>
+#include <linux/compile.h>
+#include <asm/boot.h>
+#include <asm/e820.h>
+#include <asm/page.h>
+
+/* Signature words to ensure LILO loaded us right */
+#define SIG1 0xAA55
+#define SIG2 0x5A5A
+
+#define HIRESO_TEXT 0xe000
+#define NORMAL_TEXT 0xa000
+
+#define BIOS_FLAG2 0x0400
+#define BIOS_FLAG5 0x0458
+#define RDISK_EQUIP 0x0488
+#define BIOS_FLAG 0x0501
+#define KB_SHFT_STS 0x053a
+#define DISK_EQUIP 0x055c
+
+INITSEG = DEF_INITSEG # 0x9000, we move boot here, out of the way
+SYSSEG = DEF_SYSSEG # 0x1000, system loaded at 0x10000 (65536).
+SETUPSEG = DEF_SETUPSEG # 0x9020, this is the current segment
+ # ... and the former contents of CS
+
+DELTA_INITSEG = SETUPSEG - INITSEG # 0x0020
+
+.code16
+.globl begtext, begdata, begbss, endtext, enddata, endbss
+
+.text
+begtext:
+.data
+begdata:
+.bss
+begbss:
+.text
+
+start:
+ jmp trampoline
+
+# This is the setup header, and it must start at %cs:2 (old 0x9020:2)
+
+ .ascii "HdrS" # header signature
+ .word 0x0203 # header version number (>= 0x0105)
+ # or else old loadlin-1.5 will fail)
+realmode_swtch: .word 0, 0 # default_switch, SETUPSEG
+start_sys_seg: .word SYSSEG
+ .word kernel_version # pointing to kernel version string
+ # above section of header is compatible
+ # with loadlin-1.5 (header v1.5). Don't
+ # change it.
+
+type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin,
+ # Bootlin, SYSLX, bootsect...)
+ # See Documentation/i386/boot.txt for
+ # assigned ids
+
+# flags, unused bits must be zero (RFU) bit within loadflags
+loadflags:
+LOADED_HIGH = 1 # If set, the kernel is loaded high
+CAN_USE_HEAP = 0x80 # If set, the loader also has set
+ # heap_end_ptr to tell how much
+ # space behind setup.S can be used for
+ # heap purposes.
+ # Only the loader knows what is free
+#ifndef __BIG_KERNEL__
+ .byte 0
+#else
+ .byte LOADED_HIGH
+#endif
+
+setup_move_size: .word 0x8000 # size to move, when setup is not
+ # loaded at 0x90000. We will move setup + # to 0x90000 then just before jumping
+ # into the kernel. However, only the
+ # loader knows how much data behind
+ # us also needs to be loaded.
+
+code32_start: # here loaders can put a different
+ # start address for 32-bit code.
+#ifndef __BIG_KERNEL__
+ .long 0x1000 # 0x1000 = default for zImage
+#else
+ .long 0x100000 # 0x100000 = default for big kernel
+#endif
+
+ramdisk_image: .long 0 # address of loaded ramdisk image
+ # Here the loader puts the 32-bit
+ # address where it loaded the image.
+ # This only will be read by the kernel.
+
+ramdisk_size: .long 0 # its size in bytes
+
+bootsect_kludge:
+ .word bootsect_helper, SETUPSEG
+
+heap_end_ptr: .word modelist+1024 # (Header version 0x0201 or later)
+ # space from here (exclusive) down to
+ # end of setup code can be used by setup
+ # for local heap purposes.
+
+pad1: .word 0
+cmd_line_ptr: .long 0 # (Header version 0x0202 or later)
+ # If nonzero, a 32-bit pointer
+ # to the kernel command line.
+ # The command line should be
+ # located between the start of
+ # setup and the end of low
+ # memory (0xa0000), or it may
+ # get overwritten before it
+ # gets read. If this field is
+ # used, there is no longer
+ # anything magical about the
+ # 0x90000 segment; the setup
+ # can be located anywhere in
+ # low memory 0x10000 or higher.
+
+ramdisk_max: .long __MAXMEM-1 # (Header version 0x0203 or later)
+ # The highest safe address for
+ # the contents of an initrd
+
+trampoline: call start_of_setup
+ .space 1024
+# End of setup header #####################################################
+
+start_of_setup:
+# Set %ds = %cs, we know that SETUPSEG = %cs at this point
+ movw %cs, %ax # aka SETUPSEG
+ movw %ax, %ds
+# Check signature at end of setup
+ cmpw $SIG1, setup_sig1
+ jne bad_sig
+
+ cmpw $SIG2, setup_sig2
+ jne bad_sig
+
+ jmp good_sig1
+
+# Routine to print asciiz string at ds:si
+prtstr:
+ lodsb
+ andb %al, %al
+ jz fin
+
+ call prtchr
+ jmp prtstr
+
+fin: ret
+
+no_sig_mess: .string "No setup signature found ..."
+
+good_sig1:
+ jmp good_sig
+
+# We now have to find the rest of the setup code/data
+bad_sig:
+ movw %cs, %ax # SETUPSEG
+ subw $DELTA_INITSEG, %ax # INITSEG
+ movw %ax, %ds
+ xorb %bh, %bh
+ movb (497), %bl # get setup sect from bootsect
+ subw $4, %bx # LILO loads 4 sectors of setup
+ shlw $8, %bx # convert to words (1sect=2^8 words)
+ movw %bx, %cx
+ shrw $3, %bx # convert to segment
+ addw $SYSSEG, %bx
+ movw %bx, %cs:start_sys_seg
+# Move rest of setup code/data to here
+ movw $2048, %di # four sectors loaded by LILO
+ subw %si, %si
+ pushw %cs
+ popw %es
+ movw $SYSSEG, %ax
+ movw %ax, %ds
+ rep
+ movsw
+ movw %cs, %ax # aka SETUPSEG
+ movw %ax, %ds
+ cmpw $SIG1, setup_sig1
+ jne no_sig
+
+ cmpw $SIG2, setup_sig2
+ jne no_sig
+
+ jmp good_sig
+
+no_sig:
+ lea no_sig_mess, %si
+ call prtstr
+
+no_sig_loop:
+ hlt
+ jmp no_sig_loop
+
+good_sig:
+ movw %cs, %ax # aka SETUPSEG
+ subw $DELTA_INITSEG, %ax # aka INITSEG
+ movw %ax, %ds
+# Check if an old loader tries to load a big-kernel
+ testb $LOADED_HIGH, %cs:loadflags # Do we have a big kernel?
+ jz loader_ok # No, no danger for old loaders.
+
+ cmpb $0, %cs:type_of_loader # Do we have a loader that
+ # can deal with us?
+ jnz loader_ok # Yes, continue.
+
+ pushw %cs # No, we have an old loader,
+ popw %ds # die. + lea loader_panic_mess, %si
+ call prtstr
+
+ jmp no_sig_loop
+
+loader_panic_mess: .string "Wrong loader, giving up..."
+
+loader_ok:
+# Get memory size (extended mem, kB)
+
+# On PC-9800, memory size detection is done completely in 32-bit
+# kernel initialize code (kernel/setup.c).
+ pushw %es
+ xorl %eax, %eax
+ movw %ax, %es
+ movb %al, (E820NR) # PC-9800 has no E820
+ movb %es:(0x401), %al
+ shll $7, %eax
+ addw $1024, %ax
+ movw %ax, (2)
+ movl %eax, (0x1e0)
+ movw %es:(0x594), %ax
+ shll $10, %eax
+ addl %eax, (0x1e0)
+ popw %es
+
+# Check for video adapter and its parameters and allow the
+# user to browse video modes.
+ call video # NOTE: we need %ds pointing
+ # to bootsector
+
+# Get text video mode
+ movb $0x0B, %ah
+ int $0x18 # CRT mode sense
+ movw $(20 << 8) + 40, %cx
+ testb $0x10, %al
+ jnz 3f
+ movb $20, %ch
+ testb $0x01, %al
+ jnz 1f
+ movb $25, %ch
+ jmp 1f
+3: # If bit 4 was 1, it means either 1) 31 lines for hi-reso mode,
+ # or 2) 30 lines for PC-9821.
+ movb $31, %ch # hireso mode value
+ pushw $0
+ popw %es
+ testb $0x08, %es:BIOS_FLAG
+ jnz 1f
+ movb $30, %ch
+1: # Now we got # of rows in %ch
+ movb %ch, (14)
+
+ testb $0x02, %al
+ jnz 2f
+ movb $80, %cl
+2: # Now we got # of columns in %cl
+ movb %cl, (7)
+
+ # Next, get horizontal frequency if supported
+ movw $0x3100, %ax
+ int $0x18 # Call CRT bios
+ movb %al, (6) # If 31h is unsupported, %al remains 0
+
+# Get hd0-3 data...
+ pushw %ds # aka INITSEG
+ popw %es
+ xorw %ax, %ax
+ movw %ax, %ds
+ cld
+ movw $0x0080, %di
+ movb DISK_EQUIP+1, %ah
+ movb $0x80, %al
+
+get_hd_info:
+ shrb %ah
+ pushw %ax
+ jnc 1f
+ movb $0x84, %ah
+ int $0x1b
+ jnc 2f # Success
+1: xorw %cx, %cx # `0 cylinders' means no drive
+2: # Attention! Work area (drive_info) is arranged for PC-9800.
+ movw %cx, %ax # # of cylinders
+ stosw
+ movw %dx, %ax # # of sectors / # of heads
+ stosw
+ movw %bx, %ax # sector size in bytes
+ stosw
+ popw %ax
+ incb %al
+ cmpb $0x84, %al
+ jb get_hd_info
+
+# Get fd data...
+ movw DISK_EQUIP, %ax
+ andw $0xf00f, %ax
+ orb %al, %ah
+ movb RDISK_EQUIP, %al
+ notb %al
+ andb %al, %ah # ignore all `RAM drive'
+
+ movb $0x30, %al
+
+get_fd_info:
+ shrb %ah
+ pushw %ax
+ jnc 1f
+ movb $0xc4, %ah
+ int $0x1b
+ movb %ah, %al
+ andb $4, %al # 1.44MB support flag
+ shrb %al
+ addb $2, %al # %al = 2 (1.2MB) or 4 (1.44MB)
+ jmp 2f
+1: movb $0, %al # no drive
+2: stosb
+ popw %ax
+ incb %al
+ testb $0x04, %al
+ jz get_fd_info
+
+ addb $(0xb0 - 0x34), %al
+ jnc get_fd_info # check FDs on 640KB I/F
+
+ pushw %es
+ popw %ds # %ds got bootsector again
+#if 0
+ mov $0, (0x1ff) # default is no pointing device
+#endif
+
+#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
+# Then check for an APM BIOS...
+ # %ds points to the bootsector
+ movw $0, 0x40 # version = 0 means no APM BIOS
+ movw $0x09a00, %ax # APM BIOS installation check
+ xorw %bx, %bx
+ int $0x1f
+ jc done_apm_bios # Nope, no APM BIOS
+
+ cmpw $0x0504d, %bx # Check for "PM" signature
+ jne done_apm_bios # No signature, no APM BIOS
+
+ testb $0x02, %cl # Is 32 bit supported?
+ je done_apm_bios # No 32-bit, no (good) APM BIOS
+
+ movw $0x09a04, %ax # Disconnect first just in case
+ xorw %bx, %bx
+ int $0x1f # ignore return code
+ movw $0x09a03, %ax # 32 bit connect
+ xorl %ebx, %ebx
+ int $0x1f
+ jc no_32_apm_bios # Ack, error.
+
+ movw %ax, (66) # BIOS code segment
+ movl %ebx, (68) # BIOS entry point offset
+ movw %cx, (72) # BIOS 16 bit code segment
+ movw %dx, (74) # BIOS data segment
+ movl %esi, (78) # BIOS code segment length
+ movw %di, (82) # BIOS data segment length
+# Redo the installation check as the 32 bit connect
+# modifies the flags returned on some BIOSs
+ movw $0x09a00, %ax # APM BIOS installation check
+ xorw %bx, %bx
+ int $0x1f
+ jc apm_disconnect # error -> shouldn't happen
+
+ cmpw $0x0504d, %bx # check for "PM" signature
+ jne apm_disconnect # no sig -> shouldn't happen
+
+ movw %ax, (64) # record the APM BIOS version
+ movw %cx, (76) # and flags
+ jmp done_apm_bios
+
+apm_disconnect: # Tidy up
+ movw $0x09a04, %ax # Disconnect
+ xorw %bx, %bx
+ int $0x1f # ignore return code
+
+ jmp done_apm_bios
+
+no_32_apm_bios:
+ andw $0xfffd, (76) # remove 32 bit support bit
+done_apm_bios:
+#endif
+
+# Pass cursor position to kernel...
+ movw %cs:cursor_address, %ax
+ shrw %ax # cursor_address is 2 bytes unit
+ movb $80, %cl
+ divb %cl
+ xchgb %al, %ah # (0) = %al = X, (1) = %ah = Y
+ movw %ax, (0)
+
+#if 0
+ movw $msg_cpos, %si
+ call prtstr_cs
+ call prthex
+ call prtstr_cs
+ movw %ds, %ax
+ call prthex
+ call prtstr_cs
+ movb $0x11, %ah
+ int $0x18
+ movb $0, %ah
+ int $0x18
+ .section .rodata, "a"
+msg_cpos: .string "Cursor position: 0x"
+ .string ", %ds:0x"
+ .string "\r\n"
+ .previous
+#endif
+
+# Now we want to move to protected mode ...
+ cmpw $0, %cs:realmode_swtch
+ jz rmodeswtch_normal
+
+ lcall *%cs:realmode_swtch
+
+ jmp rmodeswtch_end
+
+rmodeswtch_normal:
+ pushw %cs
+ call default_switch
+
+rmodeswtch_end:
+# we get the code32 start address and modify the below 'jmpi'
+# (loader may have changed it)
+ movl %cs:code32_start, %eax
+ movl %eax, %cs:code32
+
+# Now we move the system to its rightful place ... but we check if we have a
+# big-kernel. In that case we *must* not move it ...
+ testb $LOADED_HIGH, %cs:loadflags
+ jz do_move0 # .. then we have a normal low
+ # loaded zImage
+ # .. or else we have a high
+ # loaded bzImage
+ jmp end_move # ... and we skip moving
+
+do_move0:
+ movw $0x100, %ax # start of destination segment
+ movw %cs, %bp # aka SETUPSEG
+ subw $DELTA_INITSEG, %bp # aka INITSEG
+ movw %cs:start_sys_seg, %bx # start of source segment
+ cld
+do_move:
+ movw %ax, %es # destination segment
+ incb %ah # instead of add ax,#0x100
+ movw %bx, %ds # source segment
+ addw $0x100, %bx
+ subw %di, %di
+ subw %si, %si
+ movw $0x800, %cx
+ rep
+ movsw
+ cmpw %bp, %bx # assume start_sys_seg > 0x200,
+ # so we will perhaps read one
+ # page more than needed, but
+ # never overwrite INITSEG
+ # because destination is a
+ # minimum one page below source
+ jb do_move
+
+end_move:
+# then we load the segment descriptors
+ movw %cs, %ax # aka SETUPSEG
+ movw %ax, %ds
+ +# Check whether we need to be downward compatible with version <=201
+ cmpl $0, cmd_line_ptr
+ jne end_move_self # loader uses version >=202 features
+ cmpb $0x20, type_of_loader
+ je end_move_self # bootsect loader, we know of it
+ +# Boot loader doesnt support boot protocol version 2.02.
+# If we have our code not at 0x90000, we need to move it there now.
+# We also then need to move the params behind it (commandline)
+# Because we would overwrite the code on the current IP, we move
+# it in two steps, jumping high after the first one.
+ movw %cs, %ax
+ cmpw $SETUPSEG, %ax
+ je end_move_self
+
+ cli # make sure we really have
+ # interrupts disabled !
+ # because after this the stack
+ # should not be used
+ subw $DELTA_INITSEG, %ax # aka INITSEG
+ movw %ss, %dx
+ cmpw %ax, %dx
+ jb move_self_1
+
+ addw $INITSEG, %dx
+ subw %ax, %dx # this will go into %ss after
+ # the move
+move_self_1:
+ movw %ax, %ds
+ movw $INITSEG, %ax # real INITSEG
+ movw %ax, %es
+ movw %cs:setup_move_size, %cx
+ std # we have to move up, so we use
+ # direction down because the
+ # areas may overlap
+ movw %cx, %di
+ decw %di
+ movw %di, %si
+ subw $move_self_here+0x200, %cx
+ rep
+ movsb
+ ljmp $SETUPSEG, $move_self_here
+
+move_self_here:
+ movw $move_self_here+0x200, %cx
+ rep
+ movsb
+ movw $SETUPSEG, %ax
+ movw %ax, %ds
+ movw %dx, %ss
+
+end_move_self: # now we are at the right place
+ lidt idt_48 # load idt with 0,0
+ xorl %eax, %eax # Compute gdt_base
+ movw %ds, %ax # (Convert %ds:gdt to a linear ptr)
+ shll $4, %eax
+ addl $gdt, %eax
+ movl %eax, (gdt_48+2)
+ lgdt gdt_48 # load gdt with whatever is
+ # appropriate
+
+# that was painless, now we enable A20
+
+ outb %al, $0xf2 # A20 on
+ movb $0x02, %al
+ outb %al, $0xf6 # also A20 on; making ITF's
+ # way our model
+
+ # PC-9800 seems to enable A20 at the moment of `outb';
+ # so we don't wait unlike IBM PCs (see ../setup.S).
+
+# enable DMA to access memory over 0x100000 (1MB).
+
+ movw $0x439, %dx
+ inb %dx, %al
+ andb $(~4), %al
+ outb %al, %dx
+
+# Set DMA to increment its bank address automatically at 16MB boundary.
+# Initial setting is 64KB boundary mode so that we can't run DMA crossing
+# physical address 0xXXXXFFFF.
+
+ movb $0x0c, %al
+ outb %al, $0x29 # ch. 0
+ movb $0x0d, %al
+ outb %al, $0x29 # ch. 1
+ movb $0x0e, %al
+ outb %al, $0x29 # ch. 2
+ movb $0x0f, %al
+ outb %al, $0x29 # ch. 3
+ movb $0x50, %al
+ outb %al, $0x11 # reinitialize DMAC
+
+# make sure any possible coprocessor is properly reset..
+ movb $0, %al
+ outb %al, $0xf8
+ outb %al, $0x5f # delay
+
+# well, that went ok, I hope. Now we mask all interrupts - the rest
+# is done in init_IRQ().
+ movb $0xFF, %al # mask all interrupts for now
+ outb %al, $0x0A
+ outb %al, $0x5f # delay
+
+ movb $0x7F, %al # mask all irq's but irq7 which
+ outb %al, $0x02 # is cascaded
+
+# Well, that certainly wasn't fun :-(. Hopefully it works, and we don't
+# need no steenking BIOS anyway (except for the initial loading :-).
+# The BIOS-routine wants lots of unnecessary data, and it's less
+# "interesting" anyway. This is how REAL programmers do it.
+#
+# Well, now's the time to actually move into protected mode. To make
+# things as simple as possible, we do no register set-up or anything,
+# we let the gnu-compiled 32-bit programs do that. We just jump to
+# absolute address 0x1000 (or the loader supplied one),
+# in 32-bit protected mode.
+#
+# Note that the short jump isn't strictly needed, although there are
+# reasons why it might be a good idea. It won't hurt in any case.
+ movw $1, %ax # protected mode (PE) bit
+ lmsw %ax # This is it!
+ jmp flush_instr
+
+flush_instr:
+ xorw %bx, %bx # Flag to indicate a boot
+ xorl %esi, %esi # Pointer to real-mode code
+ movw %cs, %si
+ subw $DELTA_INITSEG, %si
+ shll $4, %esi # Convert to 32-bit pointer
+# NOTE: For high loaded big kernels we need a
+# jmpi 0x100000,__KERNEL_CS
+#
+# but we yet haven't reloaded the CS register, so the default size +# of the target offset still is 16 bit.
+# However, using an operand prefix (0x66), the CPU will properly
+# take our 48 bit far pointer. (INTeL 80386 Programmer's Reference
+# Manual, Mixing 16-bit and 32-bit code, page 16-6)
+
+ .byte 0x66, 0xea # prefix + jmpi-opcode
+code32: .long 0x1000 # will be set to 0x100000
+ # for big kernels
+ .word __KERNEL_CS
+
+# Here's a bunch of information about your current kernel..
+kernel_version: .ascii UTS_RELEASE
+ .ascii " ("
+ .ascii LINUX_COMPILE_BY
+ .ascii "@"
+ .ascii LINUX_COMPILE_HOST
+ .ascii ") "
+ .ascii UTS_VERSION
+ .byte 0
+
+# This is the default real mode switch routine.
+# to be called just before protected mode transition
+default_switch:
+ cli # no interrupts allowed !
+ outb %al, $0x50 # disable NMI for bootup
+ # sequence
+ lret
+
+# This routine only gets called, if we get loaded by the simple
+# bootsect loader _and_ have a bzImage to load.
+# Because there is no place left in the 512 bytes of the boot sector,
+# we must emigrate to code space here.
+bootsect_helper:
+ cmpw $0, %cs:bootsect_es
+ jnz bootsect_second
+
+ movb $0x20, %cs:type_of_loader
+ movw %es, %ax
+ shrw $4, %ax
+ movb %ah, %cs:bootsect_src_base+2
+ movw %es, %ax
+ movw %ax, %cs:bootsect_es
+ subw $SYSSEG, %ax
+ lret # nothing else to do for now
+
+bootsect_second:
+ pushw %bx
+ pushw %cx
+ pushw %si
+ pushw %di
+ testw %bp, %bp # 64K full ?
+ jne bootsect_ex
+
+ xorw %cx, %cx # zero means full 64K
+ pushw %cs
+ popw %es
+ movw $bootsect_gdt, %bx
+ xorw %si, %si # source address
+ xorw %di, %di # destination address
+ movb $0x90, %ah
+ int $0x1f
+ jc bootsect_panic # this, if INT1F fails
+
+ movw %cs:bootsect_es, %es # we reset %es to always point
+ incb %cs:bootsect_dst_base+2 # to 0x10000
+bootsect_ex:
+ movb %cs:bootsect_dst_base+2, %ah
+ shlb $4, %ah # we now have the number of
+ # moved frames in %ax
+ xorb %al, %al
+ popw %di
+ popw %si
+ popw %cx
+ popw %bx
+ lret
+
+bootsect_gdt:
+ .word 0, 0, 0, 0
+ .word 0, 0, 0, 0
+
+bootsect_src:
+ .word 0xffff
+
+bootsect_src_base:
+ .byte 0x00, 0x00, 0x01 # base = 0x010000
+ .byte 0x93 # typbyte
+ .word 0 # limit16,base24 =0
+
+bootsect_dst:
+ .word 0xffff
+
+bootsect_dst_base:
+ .byte 0x00, 0x00, 0x10 # base = 0x100000
+ .byte 0x93 # typbyte
+ .word 0 # limit16,base24 =0
+ .word 0, 0, 0, 0 # BIOS CS
+ .word 0, 0, 0, 0 # BIOS DS
+
+bootsect_es:
+ .word 0
+
+bootsect_panic:
+ pushw %cs
+ popw %ds
+ cld
+ leaw bootsect_panic_mess, %si
+ call prtstr
+
+bootsect_panic_loop:
+ jmp bootsect_panic_loop
+
+bootsect_panic_mess:
+ .string "INT1F refuses to access high mem, giving up."
+
+# This routine prints one character (in %al) on console.
+# PC-9800 doesn't have BIOS-function to do it like IBM PC's INT 10h - 0Eh,
+# so we hardcode `prtchr' subroutine here.
+prtchr:
+ pushaw
+ pushw %es
+ cmpb $0, %cs:prtchr_initialized
+ jnz prtchr_ok
+ xorw %cx, %cx
+ movw %cx, %es
+ testb $0x8, %es:BIOS_FLAG
+ jz 1f
+ movb $(HIRESO_TEXT >> 8), %cs:cursor_address+3
+ movw $(80 * 31 * 2), %cs:max_cursor_offset
+1: pushw %ax
+ call get_cursor_position
+ movw %ax, %cs:cursor_address
+ popw %ax
+ movb $1, %cs:prtchr_initialized
+prtchr_ok:
+ lesw %cs:cursor_address, %di
+ movw $160, %bx
+ movb $0, %ah
+ cmpb $13, %al
+ je do_cr
+ cmpb $10, %al
+ je do_lf
+
+ # normal (printable) character
+ stosw
+ movb $0xe1, %es:0x2000-2(%di)
+ jmp 1f
+
+do_cr: movw %di, %ax
+ divb %bl # %al = Y, %ah = X * 2
+ mulb %bl
+ movw %ax, %dx
+ jmp 2f
+
+do_lf: addw %bx, %di
+1: movw %cs:max_cursor_offset, %cx
+ cmpw %cx, %di
+ movw %di, %dx
+ jb 2f
+ # cursor reaches bottom of screen; scroll it
+ subw %bx, %dx
+ xorw %di, %di
+ movw %bx, %si
+ cld
+ subw %bx, %cx
+ shrw %cx
+ pushw %cx
+ rep; es; movsw
+ movb $32, %al # clear bottom line characters
+ movb $80, %cl
+ rep; stosw
+ movw $0x2000, %di
+ popw %cx
+ leaw (%bx,%di), %si
+ rep; es; movsw
+ movb $0xe1, %al # clear bottom line attributes
+ movb $80, %cl
+ rep; stosw
+2: movw %dx, %cs:cursor_address
+ movb $0x13, %ah # move cursor to right position
+ int $0x18
+ popw %es
+ popaw
+ ret
+
+cursor_address:
+ .word 0
+ .word NORMAL_TEXT
+max_cursor_offset:
+ .word 80 * 25 * 2 # for normal 80x25 mode
+
+# putstr may called without running through start_of_setup (via bootsect_panic)
+# so we should initialize ourselves on demand.
+prtchr_initialized:
+ .byte 0
+
+# This routine queries GDC (graphic display controller) for current cursor
+# position. Cursor position is returned in %ax (CPU offset address).
+get_cursor_position:
+1: inb $0x60, %al
+ outb %al, $0x5f # delay
+ outb %al, $0x5f # delay
+ testb $0x04, %al # Is FIFO empty?
+ jz 1b # no -> wait until empty
+
+ movb $0xe0, %al # CSRR command
+ outb %al, $0x62 # command write
+ outb %al, $0x5f # delay
+ outb %al, $0x5f # delay
+
+2: inb $0x60, %al
+ outb %al, $0x5f # delay
+ outb %al, $0x5f # delay
+ testb $0x01, %al # Is DATA READY?
+ jz 2b # no -> wait until ready
+
+ inb $0x62, %al # read xAD (L)
+ outb %al, $0x5f # delay
+ outb %al, $0x5f # delay
+ movb %al, %ah
+ inb $0x62, %al # read xAD (H)
+ outb %al, $0x5f # delay
+ outb %al, $0x5f # delay
+ xchgb %al, %ah # correct byte order
+ pushw %ax
+ inb $0x62, %al # read yAD (L)
+ outb %al, $0x5f # delay
+ outb %al, $0x5f # delay
+ inb $0x62, %al # read yAD (M)
+ outb %al, $0x5f # delay
+ outb %al, $0x5f # delay
+ inb $0x62, %al # read yAD (H)
+ # yAD is not our interest,
+ # so discard it.
+ popw %ax
+ addw %ax, %ax # convert to CPU address
+ ret
+
+# Descriptor tables
+#
+# NOTE: if you think the GDT is large, you can make it smaller by just
+# defining the KERNEL_CS and KERNEL_DS entries and shifting the gdt
+# address down by GDT_ENTRY_KERNEL_CS*8. This puts bogus entries into
+# the GDT, but those wont be used so it's not a problem.
+#
+gdt:
+ .fill GDT_ENTRY_KERNEL_CS,8,0
+
+ .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
+ .word 0 # base address = 0
+ .word 0x9A00 # code read/exec
+ .word 0x00CF # granularity = 4096, 386
+ # (+5th nibble of limit)
+
+ .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
+ .word 0 # base address = 0
+ .word 0x9200 # data read/write
+ .word 0x00CF # granularity = 4096, 386
+ # (+5th nibble of limit)
+idt_48:
+ .word 0 # idt limit = 0
+ .word 0, 0 # idt base = 0L
+gdt_48:
+ .word GDT_ENTRY_KERNEL_CS*8 + 16 - 1 # gdt limit
+
+ .word 0, 0 # gdt base (filled in later)
+
+# Include video setup & detection code
+
+#include "video.S"
+
+# Setup signature -- must be last
+setup_sig1: .word SIG1
+setup_sig2: .word SIG2
+
+# After this point, there is some free space which is used by the video mode
+# handling code to store the temporary mode table (not used by the kernel).
+
+modelist:
+
+.text
+endtext:
+.data
+enddata:
+.bss
+endbss:
diff -urN linux/arch/i386/boot98/tools/build.c linux98/arch/i386/boot98/tools/build.c
--- linux/arch/i386/boot98/tools/build.c Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/boot98/tools/build.c Tue Oct 8 13:22:50 2002
@@ -0,0 +1,188 @@
+/*
+ * $Id: build.c,v 1.5 1997/05/19 12:29:58 mj Exp $
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1997 Martin Mares
+ */
+
+/*
+ * This file builds a disk-image from three different files:
+ *
+ * - bootsect: exactly 512 bytes of 8086 machine code, loads the rest
+ * - setup: 8086 machine code, sets up system parm
+ * - system: 80386 code for actual system
+ *
+ * It does some checking that all files are of the correct type, and
+ * just writes the result to stdout, removing headers and padding to
+ * the right amount. It also writes some system data to stderr.
+ */
+
+/*
+ * Changes by tytso to allow root device specification
+ * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
+ * Cross compiling fixes by Gertjan van Wingerde, July 1996
+ * Rewritten by Martin Mares, April 1997
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <asm/boot.h>
+
+typedef unsigned char byte;
+typedef unsigned short word;
+typedef unsigned long u32;
+
+#define DEFAULT_MAJOR_ROOT 0
+#define DEFAULT_MINOR_ROOT 0
+
+/* Minimal number of setup sectors (see also bootsect.S) */
+#define SETUP_SECTS 4
+
+byte buf[1024];
+int fd;
+int is_big_kernel;
+
+void die(const char * str, ...)
+{
+ va_list args;
+ va_start(args, str);
+ vfprintf(stderr, str, args);
+ fputc('\n', stderr);
+ exit(1);
+}
+
+void file_open(const char *name)
+{
+ if ((fd = open(name, O_RDONLY, 0)) < 0)
+ die("Unable to open `%s': %m", name);
+}
+
+void usage(void)
+{
+ die("Usage: build [-b] bootsect setup system [rootdev] [> image]");
+}
+
+int main(int argc, char ** argv)
+{
+ unsigned int i, c, sz, setup_sectors;
+ u32 sys_size;
+ byte major_root, minor_root;
+ struct stat sb;
+
+ if (argc > 2 && !strcmp(argv[1], "-b"))
+ {
+ is_big_kernel = 1;
+ argc--, argv++;
+ }
+ if ((argc < 4) || (argc > 5))
+ usage();
+ if (argc > 4) {
+ if (!strcmp(argv[4], "CURRENT")) {
+ if (stat("/", &sb)) {
+ perror("/");
+ die("Couldn't stat /");
+ }
+ major_root = major(sb.st_dev);
+ minor_root = minor(sb.st_dev);
+ } else if (strcmp(argv[4], "FLOPPY")) {
+ if (stat(argv[4], &sb)) {
+ perror(argv[4]);
+ die("Couldn't stat root device.");
+ }
+ major_root = major(sb.st_rdev);
+ minor_root = minor(sb.st_rdev);
+ } else {
+ major_root = 0;
+ minor_root = 0;
+ }
+ } else {
+ major_root = DEFAULT_MAJOR_ROOT;
+ minor_root = DEFAULT_MINOR_ROOT;
+ }
+ fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root);
+
+ file_open(argv[1]);
+ i = read(fd, buf, sizeof(buf));
+ fprintf(stderr,"Boot sector %d bytes.\n",i);
+ if (i != 512)
+ die("Boot block must be exactly 512 bytes");
+ if (buf[510] != 0x55 || buf[511] != 0xaa)
+ die("Boot block hasn't got boot flag (0xAA55)");
+ buf[508] = minor_root;
+ buf[509] = major_root;
+ if (write(1, buf, 512) != 512)
+ die("Write call failed");
+ close (fd);
+
+ file_open(argv[2]); /* Copy the setup code */
+ for (i=0 ; (c=read(fd, buf, sizeof(buf)))>0 ; i+=c )
+ if (write(1, buf, c) != c)
+ die("Write call failed");
+ if (c != 0)
+ die("read-error on `setup'");
+ close (fd);
+
+ setup_sectors = (i + 511) / 512; /* Pad unused space with zeros */
+ if (!(setup_sectors & 1))
+ setup_sectors++; /* setup_sectors must be odd on NEC PC-9800 */
+ fprintf(stderr, "Setup is %d bytes.\n", i);
+ memset(buf, 0, sizeof(buf));
+ while (i < setup_sectors * 512) {
+ c = setup_sectors * 512 - i;
+ if (c > sizeof(buf))
+ c = sizeof(buf);
+ if (write(1, buf, c) != c)
+ die("Write call failed");
+ i += c;
+ }
+
+ file_open(argv[3]);
+ if (fstat (fd, &sb))
+ die("Unable to stat `%s': %m", argv[3]);
+ sz = sb.st_size;
+ fprintf (stderr, "System is %d kB\n", sz/1024);
+ sys_size = (sz + 15) / 16;
+ /* 0x28000*16 = 2.5 MB, conservative estimate for the current maximum */
+ if (sys_size > (is_big_kernel ? 0x28000 : DEF_SYSSIZE))
+ die("System is too big. Try using %smodules.",
+ is_big_kernel ? "" : "bzImage or ");
+ if (sys_size > 0xefff)
+ fprintf(stderr,"warning: kernel is too big for standalone boot "
+ "from floppy\n");
+ while (sz > 0) {
+ int l, n;
+
+ l = (sz > sizeof(buf)) ? sizeof(buf) : sz;
+ if ((n=read(fd, buf, l)) != l) {
+ if (n < 0)
+ die("Error reading %s: %m", argv[3]);
+ else
+ die("%s: Unexpected EOF", argv[3]);
+ }
+ if (write(1, buf, l) != l)
+ die("Write failed");
+ sz -= l;
+ }
+ close(fd);
+
+ if (lseek(1, 497, SEEK_SET) != 497) /* Write sizes to the bootsector */
+ die("Output: seek failed");
+ buf[0] = setup_sectors;
+ if (write(1, buf, 1) != 1)
+ die("Write of setup sector count failed");
+ if (lseek(1, 500, SEEK_SET) != 500)
+ die("Output: seek failed");
+ buf[0] = (sys_size & 0xff);
+ buf[1] = ((sys_size >> 8) & 0xff);
+ if (write(1, buf, 2) != 2)
+ die("Write of image length failed");
+
+ return 0; /* Everything is OK */
+}
diff -urN linux/arch/i386/boot98/video.S linux98/arch/i386/boot98/video.S
--- linux/arch/i386/boot98/video.S Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/boot98/video.S Fri Aug 17 21:50:16 2001
@@ -0,0 +1,262 @@
+/* video.S
+ *
+ * Video mode setup, etc. for NEC PC-9800 series.
+ *
+ * Copyright (C) 1997,98,99 Linux/98 project <***@kmc.kyoto-u.ac.jp>
+ *
+ * Based on the video.S for IBM PC:
+ * copyright (C) Martin Mares <***@atrey.karlin.mff.cuni.cz>
+ */
+
+/* Positions of various video parameters passed to the kernel */
+/* (see also include/linux/tty.h) */
+#define PARAM_CURSOR_POS 0x00
+#define PARAM_VIDEO_PAGE 0x04
+#define PARAM_VIDEO_MODE 0x06
+#define PARAM_VIDEO_COLS 0x07
+#define PARAM_VIDEO_EGA_BX 0x0a
+#define PARAM_VIDEO_LINES 0x0e
+#define PARAM_HAVE_VGA 0x0f
+#define PARAM_FONT_POINTS 0x10
+
+#define PARAM_VIDEO98_COMPAT 0x0a
+#define PARAM_VIDEO98_HIRESO 0x0b
+#define PARAM_VIDEO98_MACHTYPE 0x0c
+#define PARAM_VIDEO98_LINES 0x0e
+#define PARAM_VIDEO98_COLS 0x0f
+
+# PARAM_LFB_* and PARAM_VESAPM_* are unused on PC-9800.
+
+# This is the main entry point called by setup.S
+# %ds *must* be pointing to the bootsector
+video: xorw %ax, %ax
+ movw %ax, %es # %es = 0
+
+ movb %es:BIOS_FLAG, %al
+ movb %al, PARAM_VIDEO_MODE
+
+ movb $0, PARAM_VIDEO98_HIRESO # 0 = normal
+ movw $NORMAL_TEXT, PARAM_VIDEO_PAGE
+ testb $0x8, %al
+ movw $(80 * 256 + 25), %ax
+ jz 1f
+ # hireso machine.
+ movb $1, PARAM_VIDEO98_HIRESO # !0 = hi-reso
+ movb $(HIRESO_TEXT >> 8), PARAM_VIDEO_PAGE + 1
+ movw $(80 * 256 + 31), %ax
+1: movw %ax, PARAM_VIDEO98_LINES # also sets VIDEO98_COLS
+
+ movb $0xc0, %ch # 400-line graphic mode
+ movb $0x42, %ah
+ int $0x18
+
+ movw $80, PARAM_VIDEO_COLS
+
+ movw $msg_probing, %si
+ call prtstr_cs
+
+# Check vendor from font pattern of `A'...
+
+1: inb $0x60, %al # wait V-sync
+ testb $0x20, %al
+ jnz 1b
+2: inb $0x60, %al
+ testb $0x20, %al
+ jz 2b
+
+ movb $0x00, %al # select font of `A'
+ outb %al, $0xa1
+ movb $0x41, %al
+ outb %al, $0xa3
+
+ movw $8, %cx
+ movw PARAM_VIDEO_PAGE, %ax
+ cmpw $NORMAL_TEXT, %ax
+ je 3f
+ movb $24, %cl # for hi-reso machine
+3: addw $0x400, %ax # %ax = CG window segment
+ pushw %ds
+ movw %ax, %ds
+ xorw %dx, %dx # get sum of `A' pattern...
+ xorw %si, %si
+4: lodsw
+ addw %ax, %dx
+ loop 4b
+ popw %ds
+
+ movw %dx, %ax
+ movw $msg_nec, %si
+ xorw %bx, %bx # vendor info will go into %bx
+ testb $8, %es:BIOS_FLAG
+ jnz check_hireso_vendor
+ cmpw $0xc7f8, %ax
+ je 5f
+ jmp 6f
+check_hireso_vendor:
+ cmpw $0x9639, %ax # XXX: NOT VERIFIED!!!
+ je 5f
+6: incw %bx # compatible machine
+ movw $msg_compat, %si
+5: movb %bl, PARAM_VIDEO98_COMPAT
+ call prtstr_cs
+
+ movw $msg_fontdata, %si
+ call prtstr_cs # " (CG sum of A = 0x"
+ movw %dx, %ax
+ call prthex
+ call prtstr_cs # ") PC-98"
+
+ movb $'0', %al
+ pushw %ds
+ pushw $0xf8e8
+ popw %ds
+ cmpw $0x2198, (0)
+ popw %ds
+ jne 7f
+ movb $'2', %al
+7: call prtchr
+ call prtstr_cs # "1 "
+
+ movb $0, PARAM_VIDEO98_MACHTYPE
+#if 0 /* XXX - This check is bogus? [0000:BIOS_FLAG2]-bit7 does NOT
+ indicate whether it is a note machine, but merely indicates
+ whether it has ``RAM drive''. */
+# check note machine
+ testb $0x80, %es:BIOS_FLAG2
+ jnz is_note
+ pushw %ds
+ pushw $0xfd80
+ popw %ds
+ movb (4), %al
+ popw %ds
+ cmpb $0x20, %al # EPSON note A
+ je epson_note
+ cmpb $0x22, %al # EPSON note W
+ je epson_note
+ cmpb $0x27, %al # EPSON note AE
+ je epson_note
+ cmpb $0x2a, %al # EPSON note WR
+ jne note_done
+epson_note:
+ movb $1, PARAM_VIDEO98_MACHTYPE
+ movw $msg_note, %si
+ call prtstr_cs
+note_done:
+#endif
+
+# print h98 ? (only NEC)
+ cmpb $0, PARAM_VIDEO98_COMPAT
+ jnz 8f # not NEC -> not H98
+
+ testb $0x80, %es:BIOS_FLAG5
+ jz 8f # have NESA bus -> H98
+ movw $msg_h98, %si
+ call prtstr_cs
+ orb $2, PARAM_VIDEO98_MACHTYPE
+8: testb $0x40, %es:BIOS_FLAG5
+ jz 9f
+ movw $msg_gs, %si
+ call prtstr_cs # only prints it :-)
+9:
+ movw $msg_normal, %si # "normal"
+ testb $0x8, %es:BIOS_FLAG
+ jz 1f
+ movw $msg_hireso, %si
+1: call prtstr_cs
+
+ movw $msg_sysclk, %si
+ call prtstr_cs
+ movb $'5', %al
+ testb $0x80, %es:BIOS_FLAG
+ jz 2f
+ movb $'8', %al
+2: call prtchr
+ call prtstr_cs
+
+#if 0
+ testb $0x40, %es:(0x45c)
+ jz no_30line # no 30-line support
+
+ movb %es:KB_SHFT_STS, %al
+ testb $0x01, %al # is SHIFT key pressed?
+ jz no_30line
+
+ testb $0x10, %al # is CTRL key pressed?
+ jnz line40
+
+ # switch to 30-line mode
+ movb $30, PARAM_VIDEO98_LINES
+ movw $msg_30line, %si
+ jmp 3f
+
+line40:
+ movb $37, PARAM_VIDEO98_LINES
+ movw $40, PARAM_VIDEO_LINES
+ movw $msg_40line, %si
+3: call prtstr_cs
+
+ movb $0x32, %bh
+ movw $0x300c, %ax
+ int $0x18 # switch video mode
+ movb $0x0c, %ah
+ int $0x18 # turn on text plane
+ movw %cs:cursor_address, %dx
+ movb $0x13, %ah
+ int $0x18 # move cursor to correct place
+ mov $0x11, %ah
+ int $0x18 # turn on text plane
+
+ call prtstr_cs # "Ok.\r\n"
+no_30line:
+#endif
+ ret
+
+prtstr_cs:
+ pushw %ds
+ pushw %cs
+ popw %ds
+ call prtstr
+ popw %ds
+ ret
+
+# prthex is for debugging purposes, and prints %ax in hexadecimal.
+prthex: pushw %cx
+ movw $4, %cx
+1: rolw $4, %ax
+ pushw %ax
+ andb $0xf, %al
+ cmpb $10, %al
+ sbbb $0x69, %al
+ das
+ call prtchr
+ popw %ax
+ loop 1b
+ popw %cx
+ ret
+
+msg_probing: .string "Probing machine: "
+
+msg_nec: .string "NEC"
+msg_compat: .string "compatible"
+
+msg_fontdata: .string " (CG sum of A = 0x"
+ .string ") PC-98"
+ .string "1 "
+
+msg_gs: .string "(GS) "
+msg_h98: .string "(H98) "
+
+msg_normal: .string "normal"
+msg_hireso: .string "Hi-reso"
+
+msg_sysclk: .string " mode, system clock "
+ .string "MHz\r\n"
+
+#if 0
+msg_40line: # cpp will concat following lines, so the assembler can deal.
+ .ascii "\
+Video mode will be adjusted to 37-line (so-called ``40-line'') mode later.\r\n\
+THIS MODE MAY DAMAGE YOUR MONITOR PHYSICALLY. USE AT YOUR OWN RISK.\r\n"
+msg_30line: .string "Switching video mode to 30-line (640x480) mode... "
+ .string "Ok.\r\n"
+#endif
Osamu Tomita
2002-11-02 17:48:08 UTC
Permalink
This is part 3/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
console display modules
- add jis-x201 charset("kana") support. (display only)
- add multi-byte char("kanji") support. (display only)

I hope this may be base for ather multi-byte languege support.

diffstat:
drivers/char/Makefile | 9 drivers/char/console_macros.h | 14 +
drivers/char/console_pc9800.h | 14 +
drivers/char/consolemap.c | 58 ++++-
drivers/char/pc9800.uni | 260 +++++++++++++++++++++++
drivers/char/vt.c | 453 +++++++++++++++++++++++++++++++++++------
drivers/char/vt_ioctl.c | 19 +
include/linux/console.h | 11 include/linux/console_struct.h | 27 ++
include/linux/consolemap.h | 1 include/linux/tty.h | 4 include/linux/vt.h | 1 include/linux/vt_buffer.h | 4
13 files changed, 812 insertions(+), 63 deletions(-)

patch:
diff -urN linux/drivers/char/Makefile linux98/drivers/char/Makefile
--- linux/drivers/char/Makefile Thu Oct 31 13:23:11 2002
+++ linux98/drivers/char/Makefile Thu Oct 31 15:14:04 2002
@@ -5,7 +5,11 @@
#
# This file contains the font map for the default (hardware) font
#
+ifneq ($(CONFIG_PC9800),y)
FONTMAPFILE = cp437.uni
+else
+FONTMAPFILE = pc9800.uni
+endif
obj-y += mem.o tty_io.o n_tty.o tty_ioctl.o pty.o misc.o random.o eventpoll.o
@@ -14,7 +18,8 @@
export-objs := busmouse.o vt.o generic_serial.o ip2main.o \
ite_gpio.o keyboard.o misc.o nvram.o random.o rtc.o \
- selection.o sonypi.o sysrq.o tty_io.o tty_ioctl.o eventpoll.o
+ selection.o sonypi.o sysrq.o tty_io.o tty_ioctl.o \
+ eventpoll.o upd4990a.o
obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o consolemap.o consolemap_deftbl.o selection.o keyboard.o
obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o
@@ -51,6 +56,7 @@
obj-$(CONFIG_PRINTER) += lp.o
obj-$(CONFIG_TIPAR) += tipar.o
+obj-$(CONFIG_PC9800_OLDLP)) += lp_old98.o
obj-$(CONFIG_BUSMOUSE) += busmouse.o
obj-$(CONFIG_DTLK) += dtlk.o
@@ -59,6 +65,7 @@
obj-$(CONFIG_SONYPI) += sonypi.o
obj-$(CONFIG_ATARIMOUSE) += atarimouse.o
obj-$(CONFIG_RTC) += rtc.o
+obj-$(CONFIG_RTC98) += upd4990a.o
obj-$(CONFIG_GEN_RTC) += genrtc.o
obj-$(CONFIG_EFI_RTC) += efirtc.o
ifeq ($(CONFIG_PPC),)
diff -urN linux/drivers/char/console_macros.h linux98/drivers/char/console_macros.h
--- linux/drivers/char/console_macros.h Sat Oct 19 13:01:17 2002
+++ linux98/drivers/char/console_macros.h Mon Oct 28 16:53:39 2002
@@ -55,6 +55,10 @@
#define s_reverse (vc_cons[currcons].d->vc_s_reverse)
#define ulcolor (vc_cons[currcons].d->vc_ulcolor)
#define halfcolor (vc_cons[currcons].d->vc_halfcolor)
+#define def_attr (vc_cons[currcons].d->vc_def_attr)
+#define ul_attr (vc_cons[currcons].d->vc_ul_attr)
+#define half_attr (vc_cons[currcons].d->vc_half_attr)
+#define bold_attr (vc_cons[currcons].d->vc_bold_attr)
#define tab_stop (vc_cons[currcons].d->vc_tab_stop)
#define palette (vc_cons[currcons].d->vc_palette)
#define bell_pitch (vc_cons[currcons].d->vc_bell_pitch)
@@ -64,6 +68,16 @@
#define complement_mask (vc_cons[currcons].d->vc_complement_mask)
#define s_complement_mask (vc_cons[currcons].d->vc_s_complement_mask)
#define hi_font_mask (vc_cons[currcons].d->vc_hi_font_mask)
+#define kanji_mode (vc_cons[currcons].d->vc_kanji_mode)
+#define s_kanji_mode (vc_cons[currcons].d->vc_s_kanji_mode)
+#define kanji_char1 (vc_cons[currcons].d->vc_kanji_char1)
+#define translate_ex (vc_cons[currcons].d->vc_translate_ex)
+#define G0_charset_ex (vc_cons[currcons].d->vc_G0_charset_ex)
+#define G1_charset_ex (vc_cons[currcons].d->vc_G1_charset_ex)
+#define saved_G0_ex (vc_cons[currcons].d->vc_saved_G0_ex)
+#define saved_G1_ex (vc_cons[currcons].d->vc_saved_G1_ex)
+#define kanji_jis_mode (vc_cons[currcons].d->vc_kanji_jis_mode)
+#define s_kanji_jis_mode (vc_cons[currcons].d->vc_s_kanji_jis_mode)
#define vcmode (vt_cons[currcons]->vc_mode)
diff -urN linux/drivers/char/console_pc9800.h linux98/drivers/char/console_pc9800.h
--- linux/drivers/char/console_pc9800.h Thu Jan 1 09:00:00 1970
+++ linux98/drivers/char/console_pc9800.h Mon Oct 28 11:48:10 2002
@@ -0,0 +1,14 @@
+#ifndef __CONSOLE_PC9800_H
+#define __CONSOLE_PC9800_H
+
+#define BLANK_ATTR 0x00E1
+
+#define JIS_CODE 0x01
+#define EUC_CODE 0x00
+#define SJIS_CODE 0x02
+#define JIS_CODE_ASCII 0x00
+#define JIS_CODE_78 0x01
+#define JIS_CODE_83 0x02
+#define JIS_CODE_90 0x03
+
+#endif /* __CONSOLE_PC9800_H */
diff -urN linux/drivers/char/consolemap.c linux98/drivers/char/consolemap.c
--- linux/drivers/char/consolemap.c Sat Oct 19 13:02:27 2002
+++ linux98/drivers/char/consolemap.c Mon Oct 21 13:18:03 2002
@@ -22,7 +22,7 @@
#include <linux/console_struct.h>
#include <linux/vt_kern.h>
-static unsigned short translations[][256] = {
+unsigned short translations[][256] = {
/* 8-bit Latin-1 mapped to Unicode -- trivial mapping */
{
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
@@ -162,7 +162,59 @@
0xf0e8, 0xf0e9, 0xf0ea, 0xf0eb, 0xf0ec, 0xf0ed, 0xf0ee, 0xf0ef,
0xf0f0, 0xf0f1, 0xf0f2, 0xf0f3, 0xf0f4, 0xf0f5, 0xf0f6, 0xf0f7,
0xf0f8, 0xf0f9, 0xf0fa, 0xf0fb, 0xf0fc, 0xf0fd, 0xf0fe, 0xf0ff
- }
+ },
+ /* JIS X0201 mapped to Unicode */
+ /* code marked with ** is not defined in JIS X0201.
+ So 0x00 - 0x1f are mapped to same to Laten1,
+ and others are mapped to PC-9800 internal font# directry */
+ {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+/* ** ** ** ** ** ** ** ** */
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+/* ** ** ** ** ** ** ** ** */
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+/* ** ** ** ** ** ** ** ** */
+ 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+/* ** ** ** ** ** ** ** ** */
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b, 0x00a5, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x203e, 0xf07f,
+/* ** */
+ 0xf080, 0xf081, 0xf082, 0xf083, 0xf084, 0xf085, 0xf086, 0xf087,
+/* ** ** ** ** ** ** ** ** */
+ 0xf088, 0xf089, 0xf08a, 0xf08b, 0xf08c, 0xf08d, 0xf08e, 0xf08f,
+/* ** ** ** ** ** ** ** ** */
+ 0xf090, 0xf091, 0xf092, 0xf093, 0xf094, 0xf095, 0xf096, 0xf097,
+/* ** ** ** ** ** ** ** ** */
+ 0xf098, 0xf099, 0xf09a, 0xf09b, 0xf09c, 0xf09d, 0xf09e, 0xf09f,
+/* ** ** ** ** ** ** ** ** */
+ 0xf0a0, 0xff61, 0xff62, 0xff63, 0xff64, 0xff65, 0xff66, 0xff67,
+/* ** */
+ 0xff68, 0xff69, 0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f,
+ 0xff70, 0xff71, 0xff72, 0xff73, 0xff74, 0xff75, 0xff76, 0xff77,
+ 0xff78, 0xff79, 0xff7a, 0xff7b, 0xff7c, 0xff7d, 0xff7e, 0xff7f,
+ 0xff80, 0xff81, 0xff82, 0xff83, 0xff84, 0xff85, 0xff86, 0xff87,
+ 0xff88, 0xff89, 0xff8a, 0xff8b, 0xff8c, 0xff8d, 0xff8e, 0xff8f,
+ 0xff90, 0xff91, 0xff92, 0xff93, 0xff94, 0xff95, 0xff96, 0xff97,
+ 0xff98, 0xff99, 0xff9a, 0xff9b, 0xff9c, 0xff9d, 0xff9e, 0xff9f,
+ 0xf0e0, 0xf0e1, 0xf0e2, 0xf0e3, 0xf0e4, 0xf0e5, 0xf0e6, 0xf0e7,
+/* ** ** ** ** ** ** ** ** */
+ 0xf0e8, 0xf0e9, 0xf0ea, 0xf0eb, 0xf0ec, 0xf0ed, 0xf0ee, 0xf0ef,
+/* ** ** ** ** ** ** ** ** */
+ 0xf0f0, 0xf0f1, 0xf0f2, 0xf0f3, 0xf0f4, 0xf0f5, 0xf0f6, 0xf0f7,
+/* ** ** ** ** ** ** ** ** */
+ 0xf0f8, 0xf0f9, 0xf0fa, 0xf0fb, 0xf0fc, 0xf0fd, 0xf0fe, 0xf0ff
+/* ** ** ** ** ** ** ** ** */
+ },
};
/* The standard kernel character-to-font mappings are not invertible
@@ -176,7 +228,7 @@
u16 **uni_pgdir[32];
unsigned long refcount;
unsigned long sum;
- unsigned char *inverse_translations[4];
+ unsigned char *inverse_translations[5];
int readonly;
};
diff -urN linux/drivers/char/pc9800.uni linux98/drivers/char/pc9800.uni
--- linux/drivers/char/pc9800.uni Thu Jan 1 09:00:00 1970
+++ linux98/drivers/char/pc9800.uni Fri Aug 17 21:50:17 2001
@@ -0,0 +1,260 @@
+#
+# Unicode table for PC-9800 console.
+# Copyright (C) 1998,2001 Linux/98 project (project Seraphim)
+# Kyoto University Microcomputer Club (KMC).
+#
+
+# Kore ha unicode wo 98 no ROM no font ni taio saseru tame no
+# map desu.
+
+# Characters for control codes.
+# PC-9800 uses 2-char sequences while Unicode uses 3-char for some codes.
+0x00
+0x01 U+2401 # SH / SOH
+0x02 U+2402 # SX / SOX
+0x03 U+2403 # EX / ETX
+0x04 U+2404 # ET / EOT
+0x05 U+2405 # EQ / ENQ
+0x06 U+2406 # AK / ACK
+0x07 U+2407 # BL / BEL
+0x08 U+2408 # BS
+0x09 U+2409 # HT
+0x0a U+240a # LF
+0x0b # HM / (VT)
+0x0c # CL / (FF)
+0x0d U+240d # CR
+0x0e # SO / (SS)
+0x0f U+240f # SI
+0x10 U+2410 # DE / DLE
+0x11 U+2411 # D1 / DC1
+0x12 U+2412 # D2 / DC2
+0x13 U+2413 # D3 / DC3
+0x14 U+2414 # D4 / DC4
+0x15 U+2415 # NK / NAK
+0x16 U+2416 # SN / SYN
+0x17 U+2417 # EB / ETB
+0x18 U+2418 # CN / CAN
+0x19 U+2419 # EM
+0x1a U+241a # SB / SUB
+0x1b U+241b # EC / ESC
+
+# arrow
+0x1c U+2192 U+ffeb # right
+0x1d U+2190 U+ffe9 # left
+0x1e U+2191 U+ffea # up
+0x1f U+2193 U+ffec # down
+
+#
+# The ASCII range is identity-mapped, but some of the characters also
+# have to act as substitutes, especially the upper-case characters.
+#
+0x20 U+0020
+0x21 U+0021
+# U+00a8 is Latin-1 Supplement DIAELESIS.
+0x22 U+0022 U+00a8
+0x23 U+0023
+0x24 U+0024
+0x25 U+0025
+0x26 U+0026
+0x26 U+2019 # General Punctuation "RIGHT SINGLE QUOTATION MARK"
+0x27 U+0027 U+2032
+0x28 U+0028
+0x29 U+0029
+0x2a U+002a
+0x2b U+002b
+# U+00b8 is Latin-1 Supplement CEDILLA.
+0x2c U+002c U+00b8
+# U+00b8 is Latin-1 Supplement SOFT HYPHEN.
+0x2d U+002d U+00ad
+0x2d U+2212 # Mathematical Operators "MINUS SIGN"
+0x2e U+002e
+0x2f U+002f
+0x2f U+2044 # General Punctuation "FRACTION SLASH"
+0x2f U+2215 # Mathematical Operators "DIVISION SLASH"
+0x30 U+0030
+0x31 U+0031
+0x32 U+0032
+0x33 U+0033
+0x34 U+0034
+0x35 U+0035
+0x36 U+0036
+0x37 U+0037
+0x38 U+0038
+0x39 U+0039
+0x3a U+003a
+0x3a U+003a # Mathematical Operators "RATIO"
+0x3b U+003b
+0x3c U+003c
+0x3d U+003d
+0x3e U+003e
+0x3f U+003f
+0x40 U+0040
+0x41 U+0041 U+00c0 U+00c1 U+00c2 U+00c3
+0x42 U+0042
+# U+00a9 is Latin-1 Supplement COPYRIGHT SIGN.
+0x43 U+0043 U+00a9
+0x44 U+0044
+0x45 U+0045 U+00c8 U+00ca U+00cb
+0x46 U+0046
+0x47 U+0047
+0x48 U+0048
+0x49 U+0049 U+00cc U+00cd U+00ce U+00cf
+0x4a U+004a
+# U+212a: Letterlike Symbols "KELVIN SIGN"
+0x4b U+004b U+212a
+0x4c U+004c
+0x4d U+004d
+0x4e U+004e
+0x4f U+004f U+00d2 U+00d3 U+00d4 U+00d5
+0x50 U+0050
+0x51 U+0051
+# U+00ae: Latin-1 Supplement "REGISTERED SIGN"
+0x52 U+0052 U+00ae
+0x53 U+0053
+0x54 U+0054
+0x55 U+0055 U+00d9 U+00da U+00db
+0x56 U+0056
+0x57 U+0057
+0x58 U+0058
+0x59 U+0059 U+00dd
+0x5a U+005a
+0x5b U+005b
+0x5c U+00a5 # Latin-1 Supplement "YEN SIGN"
+0x5d U+005d
+0x5e U+005e
+0x5f U+005f U+f804
+0x60 U+0060 U+2035
+0x61 U+0061 U+00e3
+0x62 U+0062
+0x63 U+0063
+0x64 U+0064
+0x65 U+0065
+0x66 U+0066
+0x67 U+0067
+0x68 U+0068
+0x69 U+0069
+0x6a U+006a
+0x6b U+006b
+0x6c U+006c
+0x6d U+006d
+0x6e U+006e
+0x6f U+006f U+00f5
+0x70 U+0070
+0x71 U+0071
+0x72 U+0072
+0x73 U+0073
+0x74 U+0074
+0x75 U+0075
+0x76 U+0076
+0x77 U+0077
+0x78 U+0078 U+00d7
+0x79 U+0079 U+00fd
+0x7a U+007a
+0x7b U+007b
+# U+00a6: Latin-1 Supplement "BROKEN (VERTICAL) BAR"
+0x7c U+007c U+00a6
+0x7d U+007d
+0x7e U+007e
+
+# kuhaku
+0x7f # U+2302
+
+# Block Elements.
+0x80 U+2581 # LOWER ONE EIGHTH BLOCK
+0x81 U+2582 # LOWER ONE QUARTER BLOCK
+0x82 U+2583 # LOWER THREE EIGHTHS BLOCK
+0x83 U+2584 # LOWER HALF BLOCK
+0x84 U+2585 # LOWER FIVE EIGHTHS BLOCK
+0x85 U+2586 # LOWER THREE QUARTERS BLOCK
+0x86 U+2587 # LOWER SEVEN EIGHTHS BLOCK
+0x87 U+2588 # FULL BLOCK
+0x88 U+258f # LEFT ONE EIGHTH BLOCK
+0x89 U+258e # LEFT ONE QUARTER BLOCK
+0x8a U+258d # LEFT THREE EIGHTHS BLOCK
+0x8b U+258c # LEFT HALF BLOCK
+0x8c U+258b # LEFT FIVE EIGHTHS BLOCK
+0x8d U+258a # LEFT THREE QUARTERS BLOCK
+0x8e U+2589 # LEFT SEVEN EIGHTHS BLOCK
+
+# Box Drawing.
+0x8f U+253c
+0x90 U+2534
+0x91 U+252c
+0x92 U+2524
+0x93 U+251c
+0x94 U+203e # General Punctuation "OVERLINE" (= "SPACING OVERSCORE")
+0x95 U+2500 # Box Drawing "BOX DRAWING LIGHT HORIZONTAL"
+0x96 U+2502 # Box Drawing "BOX DRAWING LIGHT VERTICAL"
+0x96 U+ffe8 # Halfwidth symbol variants "HALFWIDTH FORMS LIGHT VERTICAL"
+0x97 U+2595 # Block Elements "RIGHT ONE EIGHTH BLOCK"
+0x98 U+250c
+0x99 U+2510
+0x9a U+2514
+0x9b U+2518
+
+0x9c U+256d # "BOX DRAWING LIGHT ARC DOWN AND RIGHT"
+0x9d U+256e # "BOX DRAWING LIGHT ARC DOWN AND LEFT"
+0x9e U+2570 # "BOX DRAWING LIGHT ARC UP AND RIGHT"
+0x9f U+256f # "BOX DRAWING LIGHT ARC UP AND LEFT"
+
+0xa0 # another whitespace
+
+# Halfwidth CJK punctuation
+0xa1 - 0xa4 U+ff61 - U+ff64
+
+# Halfwidth Katakana variants
+0xa5 - 0xdf U+ff65 - U+ff9f
+0xa5 U+00b7 # Latin-1 Supplement "MIDDLE DOT"
+0xdf U+00b0 # Latin-1 Supplement "DEGREE SIGN"
+
+# Box Drawing
+0xe0 U+2550 # "BOX DRAWING DOUBLE HORIZONTAL"
+0xe1 U+255e # "BOX DRAWING VERTICAL SINGLE AND RIGHT DOUBLE"
+0xe2 U+256a # "BOX DRAWING VERTICAL SINGLE AND HORIZONTAL DOUBLE"
+0xe3 U+2561 # "BOX DRAWING VERTICAL SINGLE AND LEFT DOUBLE"
+
+# Geometric Shapes
+0xe4 U+25e2 # "BLACK LOWER RIGHT TRIANGLE"
+0xe5 U+25e3 # "BLACK LOWER LEFT TRIANGLE"
+0xe6 U+25e5 # "BLACK UPPER RIGHT TRIANGLE"
+0xe7 U+25e4 # "BLACK UPPER LEFT TRIANGLE"
+
+# Playing card symbols
+0xe8 U+2660 # "BLACK SPADE SUIT"
+0xe9 U+2665 # "BLACK HEART SUIT"
+0xea U+2666 # "BLACK DIAMOND SUIT"
+0xeb U+2663 # "BLACK CLUB SUIT"
+
+# Geometric Shapes
+0xec U+25cf # "BLACK CIRCLE"
+0xed U+25cb U+25ef # "WHITE CIRCLE", "LARGE CIRCLE"
+
+# Box Drawing
+0xee U+2571 # "BOX DRAWING LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT"
+0xef U+2572 # "BOX DRAWING LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT"
+0xf0 U+2573 # "BOX DRAWING LIGHT DIAGONAL CROSS"
+
+# CJK Unified Ideographs (XXX - should these be here?)
+0xf1 U+5186
+0xf2 U+5e74
+0xf3 U+6708
+0xf4 U+65e5
+0xf5 U+6642
+0xf6 U+5206
+0xf7 U+79d2
+
+# unassigned
+0xf8
+0xf9
+0xfa
+0xfb
+
+0xfc U+005c # "REVERSE SOLIDUS" / "BACKSLASH"
+0xfc U+2216 # Mathematical Operators "SET MINUS"
+
+# unassigned
+0xfd
+0xfe
+0xff
+
+# End of pc9800.uni
diff -urN linux/drivers/char/vt.c linux98/drivers/char/vt.c
--- linux/drivers/char/vt.c Sat Oct 19 13:02:29 2002
+++ linux98/drivers/char/vt.c Mon Oct 28 19:05:40 2002
@@ -108,6 +108,10 @@
#include "console_macros.h"
+#ifdef CONFIG_PC9800
+#include "console_pc9800.h"
+extern unsigned short translations[][256];
+#endif
const struct consw *conswitchp;
@@ -143,6 +147,10 @@
static void blank_screen(unsigned long dummy);
static void gotoxy(int currcons, int new_x, int new_y);
static void save_cur(int currcons);
+#ifdef CONFIG_PC9800
+static void save_cur_kanji(int currcons);
+static void restore_cur_kanji(int currcons);
+#endif
static void reset_terminal(int currcons, int do_clear);
static void con_flush_chars(struct tty_struct *tty);
static void set_vesa_blanking(unsigned long arg);
@@ -293,7 +301,7 @@
xx = nxx; yy = nyy;
}
for(;;) {
- u16 attrib = scr_readw(p) & 0xff00;
+ vram_char_t attrib = scr_readw(p) & 0xff00;
int startx = xx;
u16 *q = p;
while (xx < video_num_columns && count) {
@@ -379,6 +387,10 @@
{
attr = build_attr(currcons, color, intensity, blink, underline, reverse ^ decscnm);
video_erase_char = (build_attr(currcons, color, 1, blink, 0, decscnm) << 8) | ' ';
+#ifdef CONFIG_PC9800
+ if (decscnm)
+ video_erase_char |= 0x0400; /* reverse */
+#endif
}
/* Note: inverting the screen twice should revert to the original state */
@@ -395,7 +407,7 @@
else {
u16 *q = p;
int cnt = count;
- u16 a;
+ vram_char_t a;
if (!can_do_color) {
while (cnt--) {
@@ -425,11 +437,30 @@
do_update_region(currcons, (unsigned long) p, count);
}
+#ifdef CONFIG_PC9800
+/* can called form keyboard.c */
+void do_change_kanji_mode(int currcons, unsigned long mode)
+{
+ switch (mode) {
+ case 0:
+ kanji_mode = EUC_CODE;
+ break;
+ case 1:
+ kanji_mode = JIS_CODE;
+ break;
+ case 2:
+ kanji_mode = SJIS_CODE;
+ break;
+ }
+ kanji_char1 = 0;
+}
+#endif /* CONFIG_PC9800 */
+
/* used by selection: complement pointer position */
void complement_pos(int currcons, int offset)
{
static unsigned short *p;
- static unsigned short old;
+ static vram_char_t old;
static unsigned short oldx, oldy;
if (p) {
@@ -440,10 +471,15 @@
if (offset == -1)
p = NULL;
else {
- unsigned short new;
+ vram_char_t new;
p = screenpos(currcons, offset, 1);
old = scr_readw(p);
+#ifndef CONFIG_FB_EGC
new = old ^ complement_mask;
+#else
+ new = (old & 0xff0000ff) | ((old & 0xf000) >> 4)
+ | ((old & 0xf00) << 4);
+#endif
scr_writew(new, p);
if (DO_UPDATE) {
oldx = (offset >> 1) % video_num_columns;
@@ -502,7 +538,7 @@
static void add_softcursor(int currcons)
{
- int i = scr_readw((u16 *) pos);
+ vram_char_t i = scr_readw((u16 *) pos);
u32 type = cursor_type;
if (! (type & 0x10)) return;
@@ -639,7 +675,11 @@
can_do_color = 0;
sw->con_init(vc_cons[currcons].d, init);
if (!complement_mask)
+#ifndef CONFIG_PC9800
complement_mask = can_do_color ? 0x7700 : 0x0800;
+#else
+ complement_mask = 0x0400;
+#endif
s_complement_mask = complement_mask;
video_size_row = video_num_columns<<1;
screenbuf_size = video_num_lines*video_size_row;
@@ -671,7 +711,11 @@
visual_init(currcons, 1);
if (!*vc_cons[currcons].d->vc_uni_pagedir_loc)
con_set_default_unimap(currcons);
+#ifndef CONFIG_PC9800
q = (long)kmalloc(screenbuf_size, GFP_KERNEL);
+#else
+ q = (long)kmalloc(screenbuf_size * 2, GFP_KERNEL);
+#endif
if (!q) {
kfree((char *) p);
vc_cons[currcons].d = NULL;
@@ -713,7 +757,11 @@
(cc == video_num_columns && ll == video_num_lines))
newscreens[currcons] = NULL;
else {
+#ifndef CONFIG_PC9800
unsigned short *p = (unsigned short *) kmalloc(ss, GFP_USER);
+#else
+ unsigned short *p = (unsigned short *)kmalloc(ss * 2, GFP_USER);
+#endif
if (!p) {
for (i = first; i < currcons; i++)
if (newscreens[i])
@@ -1077,6 +1125,9 @@
translate = set_translate(charset == 0
? G0_charset
: G1_charset,currcons);
+#ifdef CONFIG_PC9800
+ translate_ex = (charset == 0 ? G0_charset_ex : G1_charset_ex);
+#endif
disp_ctrl = 0;
toggle_meta = 0;
break;
@@ -1085,6 +1136,9 @@
* chars < 32 be displayed as ROM chars.
*/
translate = set_translate(IBMPC_MAP,currcons);
+#ifdef CONFIG_PC9800
+ translate_ex = 0;
+#endif
disp_ctrl = 1;
toggle_meta = 0;
break;
@@ -1093,6 +1147,9 @@
* high bit before displaying as ROM char.
*/
translate = set_translate(IBMPC_MAP,currcons);
+#ifdef CONFIG_PC9800
+ translate_ex = 0;
+#endif
disp_ctrl = 1;
toggle_meta = 1;
break;
@@ -1253,6 +1310,10 @@
/* console_sem is held */
static void setterm_command(int currcons)
{
+ if (sw->con_setterm_command
+ && sw->con_setterm_command(vc_cons[currcons].d))
+ return;
+
switch(par[0]) {
case 1: /* set color for underline mode */
if (can_do_color && par[1] < 16) {
@@ -1302,6 +1363,22 @@
case 14: /* set vesa powerdown interval */
vesa_off_interval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
break;
+#ifdef CONFIG_PC9800
+ case 98:
+ if (par[1] < 10) /* change kanji mode */
+ do_change_kanji_mode(currcons, par[1]); /* 0208 */
+ else if (par[1] == 10) { /* save restore kanji mode */
+ switch (par[2]) {
+ case 1:
+ save_cur_kanji(currcons);
+ break;
+ case 2:
+ restore_cur_kanji(currcons);
+ break;
+ }
+ }
+ break;
+#endif /* CONFIG_PC9800 */
}
}
@@ -1379,8 +1456,26 @@
need_wrap = 0;
}
+#ifdef CONFIG_PC9800
+static void save_cur_kanji(int currcons)
+{
+ s_kanji_mode = kanji_mode;
+ s_kanji_jis_mode = kanji_jis_mode;
+}
+
+static void restore_cur_kanji(int currcons)
+{
+ kanji_mode = s_kanji_mode;
+ kanji_jis_mode = s_kanji_jis_mode;
+ kanji_char1 = 0;
+}
+#endif
+
enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
+#ifdef CONFIG_PC9800
+ ESsetJIS, ESsetJIS2,
+#endif
ESpalette };
/* console_sem is held (except via vc_init()) */
@@ -1390,9 +1485,18 @@
bottom = video_num_lines;
vc_state = ESnormal;
ques = 0;
+#ifndef CONFIG_PC9800
translate = set_translate(LAT1_MAP,currcons);
G0_charset = LAT1_MAP;
G1_charset = GRAF_MAP;
+#else
+ translate = set_translate(JP_MAP, currcons);
+ translate_ex = 0;
+ G0_charset = JP_MAP;
+ G0_charset_ex = 0;
+ G1_charset = GRAF_MAP;
+ G1_charset_ex = 0;
+#endif
charset = 0;
need_wrap = 0;
report_mouse = 0;
@@ -1434,6 +1538,12 @@
bell_pitch = DEFAULT_BELL_PITCH;
bell_duration = DEFAULT_BELL_DURATION;
+#ifdef CONFIG_PC9800
+ kanji_mode = EUC_CODE;
+ kanji_char1 = 0;
+ kanji_jis_mode = JIS_CODE_ASCII;
+#endif
+
gotoxy(currcons,0,0);
save_cur(currcons);
if (do_clear)
@@ -1476,11 +1586,17 @@
case 14:
charset = 1;
translate = set_translate(G1_charset,currcons);
+#ifdef CONFIG_PC9800
+ translate_ex = G1_charset_ex;
+#endif
disp_ctrl = 1;
return;
case 15:
charset = 0;
translate = set_translate(G0_charset,currcons);
+#ifdef CONFIG_PC9800
+ translate_ex = G0_charset_ex;
+#endif
disp_ctrl = 0;
return;
case 24: case 26:
@@ -1537,6 +1653,11 @@
case ')':
vc_state = ESsetG1;
return;
+#ifdef CONFIG_PC9800
+ case '$':
+ vc_state = ESsetJIS;
+ return;
+#endif
case '#':
vc_state = EShash;
return;
@@ -1786,8 +1907,25 @@
G0_charset = IBMPC_MAP;
else if (c == 'K')
G0_charset = USER_MAP;
- if (charset == 0)
+#ifdef CONFIG_PC9800
+ G0_charset_ex = 0;
+ if (c == 'J')
+ G0_charset = JP_MAP;
+ else if (c == 'I'){
+ G0_charset = JP_MAP;
+ G0_charset_ex = 1;
+ }
+#endif /* CONFIG_PC9800 */
+ if (charset == 0) {
translate = set_translate(G0_charset,currcons);
+#ifdef CONFIG_PC9800
+ translate_ex = G0_charset_ex;
+#endif
+ }
+#ifdef CONFIG_PC9800
+ kanji_jis_mode = JIS_CODE_ASCII;
+ kanji_char1 = 0;
+#endif
vc_state = ESnormal;
return;
case ESsetG1:
@@ -1799,10 +1937,51 @@
G1_charset = IBMPC_MAP;
else if (c == 'K')
G1_charset = USER_MAP;
- if (charset == 1)
+#ifdef CONFIG_PC9800
+ G1_charset_ex = 0;
+ if (c == 'J')
+ G1_charset = JP_MAP;
+ else if (c == 'I') {
+ G1_charset = JP_MAP;
+ G1_charset_ex = 1;
+ }
+#endif /* CONFIG_PC9800 */
+ if (charset == 1) {
translate = set_translate(G1_charset,currcons);
+#ifdef CONFIG_PC9800
+ translate_ex = G1_charset_ex;
+#endif
+ }
+#ifdef CONFIG_PC9800
+ kanji_jis_mode = JIS_CODE_ASCII;
+ kanji_char1 = 0;
+#endif
+ vc_state = ESnormal;
+ return;
+#ifdef CONFIG_PC9800
+ case ESsetJIS:
+ if (c == '@')
+ kanji_jis_mode = JIS_CODE_78;
+ else if (c == 'B')
+ kanji_jis_mode = JIS_CODE_83;
+ else if (c == '('){
+ vc_state = ESsetJIS2;
+ return;
+ } else {
vc_state = ESnormal;
return;
+ }
+ vc_state = ESnormal;
+ kanji_char1 = 0;
+ return;
+ case ESsetJIS2:
+ if (c == 'D'){
+ kanji_jis_mode = JIS_CODE_90;
+ kanji_char1 = 0;
+ }
+ vc_state = ESnormal;
+ return;
+#endif /* CONIFG_PC9800 */
default:
vc_state = ESnormal;
}
@@ -1834,7 +2013,7 @@
}
#endif
- int c, tc, ok, n = 0, draw_x = -1;
+ int c, tc = 0, ok, n = 0, draw_x = -1;
unsigned int currcons;
unsigned long draw_from = 0, draw_to = 0;
struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
@@ -1891,48 +2070,151 @@
hide_cursor(currcons);
while (!tty->stopped && count) {
+ int realkanji = 0;
+ int kanjioverrun = 0;
c = *buf;
buf++;
n++;
count--;
- if (utf) {
- /* Combine UTF-8 into Unicode */
- /* Incomplete characters silently ignored */
- if(c > 0x7f) {
- if (utf_count > 0 && (c & 0xc0) == 0x80) {
- utf_char = (utf_char << 6) | (c & 0x3f);
- utf_count--;
- if (utf_count == 0)
- tc = c = utf_char;
- else continue;
- } else {
- if ((c & 0xe0) == 0xc0) {
- utf_count = 1;
- utf_char = (c & 0x1f);
- } else if ((c & 0xf0) == 0xe0) {
- utf_count = 2;
- utf_char = (c & 0x0f);
- } else if ((c & 0xf8) == 0xf0) {
- utf_count = 3;
- utf_char = (c & 0x07);
- } else if ((c & 0xfc) == 0xf8) {
- utf_count = 4;
- utf_char = (c & 0x03);
- } else if ((c & 0xfe) == 0xfc) {
- utf_count = 5;
- utf_char = (c & 0x01);
- } else
- utf_count = 0;
- continue;
- }
- } else {
- tc = c;
- utf_count = 0;
- }
- } else { /* no utf */
- tc = translate[toggle_meta ? (c|0x80) : c];
- }
+#ifdef CONFIG_PC9800
+ if (vc_state == ESnormal && !disp_ctrl) {
+ switch (kanji_jis_mode) {
+ case JIS_CODE_78:
+ case JIS_CODE_83:
+ case JIS_CODE_90:
+ if (utf)
+ break;
+ if (c >= 127 || c <= 0x20) {
+ kanji_char1 = 0;
+ break;
+ }
+ if (kanji_char1) {
+ tc = (((unsigned int)kanji_char1) << 8) |
+ (((unsigned int)c) & 0x007f);
+ kanji_char1 = 0;
+ realkanji = 1;
+ } else {
+ kanji_char1 = ((unsigned int)c) & 0x007f;
+ continue;
+ } + break;
+ case JIS_CODE_ASCII:
+ default:
+ switch (kanji_mode) {
+ case SJIS_CODE:
+ if (kanji_char1) {
+ if ((0x40 <= c && c <= 0x7E) ||
+ (0x80 <= c && c <= 0xFC)) {
+ realkanji = 1;
+ /* SJIS to JIS */
+ kanji_char1 <<= 1; /* 81H-9FH --> 22H-3EH */
+ /* EOH-EFH --> C0H-DEH */
+ c -= 0x1f; /* 40H-7EH --> 21H-5FH */
+ /* 80H-9EH --> 61H-7FH */
+ /* 9FH-FCH --> 80H-DDH */
+ if (!(c & 0x80)) {
+ if (c < 0x61)
+ c++;
+ c += 0xde;
+ }
+ c &= 0xff;
+ c += 0xa1;
+ kanji_char1 += 0x1f;
+ tc = (kanji_char1 << 8) + c;
+ tc &= 0x7f7f;
+ kanji_char1 = 0;
+ }
+ } else {
+ if ((0x81 <= c && c <= 0x9f) ||
+ (0xE0 <= c && c <= 0xEF)) {
+ realkanji = 1;
+ kanji_char1 = c;
+ continue;
+ } else if (0xA1 <= c && c <= 0xDF) {
+ tc = (unsigned int)translations[JP_MAP][c];
+ goto hankana_skip;
+ }
+ }
+ break;
+ case EUC_CODE:
+ if (utf)
+ break;
+ if (c <= 0x7f) {
+ kanji_char1 = 0;
+ break;
+ }
+ if (kanji_char1) {
+ if (kanji_char1 == 0x8e) { /* SS2 */
+ /* realkanji ha tatenai */
+ tc = (unsigned int)translations[JP_MAP][c];
+ kanji_char1 = 0;
+ goto hankana_skip;
+ } else {
+ tc = (((unsigned int)kanji_char1) << 8) |
+ (((unsigned int)c) & 0x007f);
+ kanji_char1 = 0;
+ realkanji = 1;
+ }
+ } else {
+ kanji_char1 = (unsigned int)c;
+ continue;
+ }
+ break;
+ case JIS_CODE:
+ /* to be supported */
+ break;
+ } /* switch (kanji_mode) */
+ } /* switch (kanji_jis_mode) */
+ } /* if (vc_state == ESnormal) */
+
+#endif /* CONFIG_PC9800 */
+ if (!realkanji) {
+ if (utf) {
+ /* Combine UTF-8 into Unicode */
+ /* Incomplete characters silently ignored */
+ if(c > 0x7f) {
+ if (utf_count > 0 && (c & 0xc0) == 0x80) {
+ utf_char = (utf_char << 6) | (c & 0x3f);
+ utf_count--;
+ if (utf_count == 0)
+ tc = c = utf_char;
+ else continue;
+ } else {
+ if ((c & 0xe0) == 0xc0) {
+ utf_count = 1;
+ utf_char = (c & 0x1f);
+ } else if ((c & 0xf0) == 0xe0) {
+ utf_count = 2;
+ utf_char = (c & 0x0f);
+ } else if ((c & 0xf8) == 0xf0) {
+ utf_count = 3;
+ utf_char = (c & 0x07);
+ } else if ((c & 0xfc) == 0xf8) {
+ utf_count = 4;
+ utf_char = (c & 0x03);
+ } else if ((c & 0xfe) == 0xfc) {
+ utf_count = 5;
+ utf_char = (c & 0x01);
+ } else
+ utf_count = 0;
+ continue;
+ }
+ } else {
+ tc = c;
+ utf_count = 0;
+ }
+ } else { /* no utf */
+#ifndef CONFIG_PC9800
+ tc = translate[toggle_meta ? (c|0x80) : c];
+#else
+ tc = translate[(toggle_meta || translate_ex) ? (c | 0x80) : c];
+#endif
+ }
+ } /* if (!realkanji) */
+#ifdef CONFIG_PC9800
+ hankana_skip:
+#endif
/* If the original code was a control character we
* only allow a glyph to be displayed if the code is
@@ -1949,43 +2231,71 @@
: CTRL_ACTION) >> c) & 1)))
&& (c != 127 || disp_ctrl)
&& (c != 128+27);
+ ok |= realkanji;
if (vc_state == ESnormal && ok) {
- /* Now try to find out how to display it */
- tc = conv_uni_to_pc(vc_cons[currcons].d, tc);
- if ( tc == -4 ) {
+ if (!realkanji) {
+ /* Now try to find out how to display it */
+ tc = conv_uni_to_pc(vc_cons[currcons].d, tc);
+ if ( tc == -4 ) {
/* If we got -4 (not found) then see if we have
defined a replacement character (U+FFFD) */
- tc = conv_uni_to_pc(vc_cons[currcons].d, 0xfffd);
+ tc = conv_uni_to_pc(vc_cons[currcons].d, 0xfffd);
/* One reason for the -4 can be that we just
did a clear_unimap();
try at least to show something. */
- if (tc == -4)
- tc = c;
- } else if ( tc == -3 ) {
+ if (tc == -4)
+ tc = c;
+ } else if ( tc == -3 ) {
/* Bad hash table -- hope for the best */
- tc = c;
- }
- if (tc & ~charmask)
- continue; /* Conversion failed */
+ tc = c;
+ }
+ if (tc & ~charmask)
+ continue; /* Conversion failed */
+ } /* !realkanji */
if (need_wrap || decim)
FLUSH
if (need_wrap) {
cr(currcons);
lf(currcons);
+ if (kanjioverrun) {
+ x++;
+ pos += 2;
+ kanjioverrun = 0;
+ }
}
if (decim)
insert_char(currcons, 1);
+#ifndef CONFIG_PC9800
scr_writew(himask ?
((attr << 8) & ~himask) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) :
(attr << 8) + tc,
(u16 *) pos);
+#else /* CONFIG_PC9800 */
+ if (realkanji) {
+ tc = ((tc >> 8) & 0xff) | ((tc << 8) & 0xff00); + *((u16 *)pos) = (tc - 0x20) & 0xff7f;
+ *(pc9800_attr_offset((u16 *)pos)) = attr;
+ x ++;
+ pos += 2;
+ *((u16 *)pos) = (tc - 0x20) | 0x80;
+ *(pc9800_attr_offset((u16 *)pos)) = attr;
+ } else {
+ *((u16 *)pos) = tc & 0x00ff;
+ *(pc9800_attr_offset((u16 *)pos)) = attr;
+ }
+#endif /* !CONFIG_PC9800 */
if (DO_UPDATE && draw_x < 0) {
draw_x = x;
draw_from = pos;
+ if (realkanji) {
+ draw_x --;
+ draw_from -= 2;
+ }
}
+#ifndef CONFIG_PC9800
if (x == video_num_columns - 1) {
need_wrap = decawm;
draw_to = pos+2;
@@ -1993,6 +2303,16 @@
x++;
draw_to = (pos+=2);
}
+#else /* CONFIG_PC9800 */
+ if (x >= video_num_columns - 1) {
+ need_wrap = decawm;
+ kanjioverrun = x - video_num_columns + 1;
+ draw_to = pos + 2;
+ } else {
+ x++;
+ draw_to = (pos += 2);
+ }
+#endif /* !CONFIG_PC9800 */
continue;
}
FLUSH
@@ -2419,9 +2739,17 @@
vc_cons[currcons].d->vc_palette[k++] = default_grn[j] ;
vc_cons[currcons].d->vc_palette[k++] = default_blu[j] ;
}
+#ifndef CONFIG_PC9800
def_color = 0x07; /* white */
ulcolor = 0x0f; /* bold white */
halfcolor = 0x08; /* grey */
+#else
+ def_color = 0x07; /* white */
+ def_attr = 0xE1;
+ ul_attr = 0x08; /* underline */
+ half_attr = 0x00; /* ignore half color */
+ bold_attr = 0xC1; /* yellow */
+#endif
init_waitqueue_head(&vt_cons[currcons]->paste_wait);
reset_terminal(currcons, do_clear);
}
@@ -2462,7 +2790,12 @@
vt_cons[currcons] = (struct vt_struct *)
alloc_bootmem(sizeof(struct vt_struct));
visual_init(currcons, 1);
+#if defined CONFIG_PC9800 || defined CONFIG_FB
+ screenbuf
+ = (unsigned short *) alloc_bootmem(screenbuf_size * 2);
+#else
screenbuf = (unsigned short *) alloc_bootmem(screenbuf_size);
+#endif
kmalloced = 0;
vc_init(currcons, video_num_lines, video_num_columns, currcons || !sw->con_save_screen);
@@ -2955,12 +3288,16 @@
/* used by selection */
u16 screen_glyph(int currcons, int offset)
{
- u16 w = scr_readw(screenpos(currcons, offset, 1));
+ vram_char_t w = scr_readw(screenpos(currcons, offset, 1));
+#ifndef CONFIG_PC9800
u16 c = w & 0xff;
if (w & hi_font_mask)
c |= 0x100;
return c;
+#else
+ return ((u16)(w >> 16) & 0xff00) | ((u16)w & 0xff);
+#endif
}
/* used by vcs - note the word offset */
@@ -3019,8 +3356,10 @@
EXPORT_SYMBOL(default_red);
EXPORT_SYMBOL(default_grn);
EXPORT_SYMBOL(default_blu);
+#ifndef CONFIG_PC9800
EXPORT_SYMBOL(video_font_height);
EXPORT_SYMBOL(video_scan_lines);
+#endif
EXPORT_SYMBOL(vc_resize);
EXPORT_SYMBOL(fg_console);
EXPORT_SYMBOL(console_blank_hook);
diff -urN linux/drivers/char/vt_ioctl.c linux98/drivers/char/vt_ioctl.c
--- linux/drivers/char/vt_ioctl.c Sat Oct 12 13:22:14 2002
+++ linux98/drivers/char/vt_ioctl.c Sat Oct 12 14:18:52 2002
@@ -63,9 +63,11 @@
asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on);
#endif
+#ifndef CONFIG_PC9800
unsigned int video_font_height;
unsigned int default_font_height;
unsigned int video_scan_lines;
+#endif
/*
* these are the valid i/o ports we're allowed to change. they map all the
@@ -637,6 +639,17 @@
return 0;
}
+#ifdef CONFIG_PC9800
+ case VT_GDC_RESIZE:
+ {
+ if (!perm)
+ return -EPERM; +/* con_adjust_height(0);*/
+ update_screen(console);
+ return 0;
+ }
+#endif +
case VT_SETMODE:
{
struct vt_mode tmp;
@@ -828,7 +841,9 @@
__get_user(clin, &vtconsize->v_clin);
__get_user(vcol, &vtconsize->v_vcol);
__get_user(ccol, &vtconsize->v_ccol);
+#ifndef CONFIG_PC9800
vlin = vlin ? vlin : video_scan_lines;
+#endif
if ( clin )
{
if ( ll )
@@ -853,10 +868,12 @@
if ( clin > 32 )
return -EINVAL;
+#ifndef CONFIG_PC9800 if ( vlin )
video_scan_lines = vlin;
if ( clin )
video_font_height = clin;
+#endif

return vc_resize_all(ll, cc);
}
@@ -1024,8 +1041,10 @@
vt_cons[new_console]->vt_mode.frsig = 0;
vt_cons[new_console]->vt_pid = -1;
vt_cons[new_console]->vt_newvt = -1;
+#ifndef CONFIG_PC9800
if (!in_interrupt()) /* Via keyboard.c:SAK() - akpm */
reset_palette(new_console) ;
+#endif
}
/*
diff -urN linux/include/linux/console.h linux98/include/linux/console.h
--- linux/include/linux/console.h Sat Oct 19 13:01:51 2002
+++ linux98/include/linux/console.h Mon Oct 28 11:31:37 2002
@@ -17,6 +17,13 @@
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/spinlock.h>
+#include <linux/config.h>
+
+#ifndef CONFIG_PC9800
+typedef __u16 vram_char_t;
+#else
+typedef __u32 vram_char_t;
+#endif
struct vc_data;
struct console_font_op;
@@ -32,7 +39,7 @@
void (*con_init)(struct vc_data *, int);
void (*con_deinit)(struct vc_data *);
void (*con_clear)(struct vc_data *, int, int, int, int);
- void (*con_putc)(struct vc_data *, int, int, int);
+ void (*con_putc)(struct vc_data *, int, vram_char_t, int);
void (*con_putcs)(struct vc_data *, const unsigned short *, int, int, int);
void (*con_cursor)(struct vc_data *, int);
int (*con_scroll)(struct vc_data *, int, int, int, int);
@@ -48,6 +55,7 @@
void (*con_invert_region)(struct vc_data *, u16 *, int);
u16 *(*con_screen_pos)(struct vc_data *, int);
unsigned long (*con_getxy)(struct vc_data *, unsigned long, int *, int *);
+ int (*con_setterm_command)(struct vc_data *);
};
extern const struct consw *conswitchp;
@@ -55,6 +63,7 @@
extern const struct consw dummy_con; /* dummy console buffer */
extern const struct consw fb_con; /* frame buffer based console */
extern const struct consw vga_con; /* VGA text console */
+extern const struct consw gdc_con; /* PC-9800 GDC text console */
extern const struct consw newport_con; /* SGI Newport console */
extern const struct consw prom_con; /* SPARC PROM console */
diff -urN linux/include/linux/console_struct.h linux98/include/linux/console_struct.h
--- linux/include/linux/console_struct.h Sat Oct 19 13:01:07 2002
+++ linux98/include/linux/console_struct.h Mon Oct 28 16:45:49 2002
@@ -9,6 +9,9 @@
* to achieve effects such as fast scrolling by changing the origin.
*/
+#include <linux/config.h>
+#include <linux/console.h>
+
#define NPAR 16
struct vc_data {
@@ -25,9 +28,15 @@
unsigned char vc_s_color; /* Saved foreground & background */
unsigned char vc_ulcolor; /* Color for underline mode */
unsigned char vc_halfcolor; /* Color for half intensity mode */
+#ifdef CONFIG_GDC_CONSOLE
+ unsigned char vc_def_attr; /* Default attributes */
+ unsigned char vc_ul_attr; /* Attribute for underline mode */
+ unsigned char vc_half_attr; /* Attribute for half intensity mode */
+ unsigned char vc_bold_attr; /* Attribute for bold mode */
+#endif
unsigned short vc_complement_mask; /* [#] Xor mask for mouse pointer */
unsigned short vc_hi_font_mask; /* [#] Attribute set for upper 256 chars of font or 0 if not supported */
- unsigned short vc_video_erase_char; /* Background erase character */
+ vram_char_t vc_video_erase_char; /* Background erase character */
unsigned short vc_s_complement_mask; /* Saved mouse pointer mask */
unsigned int vc_x, vc_y; /* Cursor position */
unsigned int vc_top, vc_bottom; /* Scrolling region */
@@ -82,6 +91,18 @@
struct vc_data **vc_display_fg; /* [!] Ptr to var holding fg console for this display */
unsigned long vc_uni_pagedir;
unsigned long *vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */
+#ifdef CONFIG_PC9800
+ unsigned char vc_kanji_char1;
+ unsigned char vc_kanji_mode;
+ unsigned char vc_kanji_jis_mode;
+ unsigned char vc_s_kanji_mode;
+ unsigned char vc_s_kanji_jis_mode;
+ unsigned int vc_translate_ex;
+ unsigned char vc_G0_charset_ex;
+ unsigned char vc_G1_charset_ex;
+ unsigned char vc_saved_G0_ex;
+ unsigned char vc_saved_G1_ex;
+#endif /* CONFIG_PC9800 */
/* additional information is in vt_kern.h */
};
@@ -105,6 +126,10 @@
#define CUR_HWMASK 0x0f
#define CUR_SWMASK 0xfff0
+#ifndef CONFIG_PC9800
#define CUR_DEFAULT CUR_UNDERLINE
+#else
+#define CUR_DEFAULT CUR_BLOCK
+#endif
#define CON_IS_VISIBLE(conp) (*conp->vc_display_fg == conp)
diff -urN linux/include/linux/consolemap.h linux98/include/linux/consolemap.h
--- linux/include/linux/consolemap.h Sat Oct 19 13:02:34 2002
+++ linux98/include/linux/consolemap.h Mon Oct 21 14:19:31 2002
@@ -7,6 +7,7 @@
#define GRAF_MAP 1
#define IBMPC_MAP 2
#define USER_MAP 3
+#define JP_MAP 4
struct vc_data;
diff -urN linux/include/linux/tty.h linux98/include/linux/tty.h
--- linux/include/linux/tty.h Sat Oct 19 13:01:54 2002
+++ linux98/include/linux/tty.h Mon Oct 21 14:22:18 2002
@@ -123,6 +123,10 @@
#define VIDEO_TYPE_PMAC 0x60 /* PowerMacintosh frame buffer. */
+#define VIDEO_TYPE_98NORMAL 0xa4 /* NEC PC-9800 normal */
+#define VIDEO_TYPE_9840 0xa5 /* NEC PC-9800 normal 40 lines */
+#define VIDEO_TYPE_98HIRESO 0xa6 /* NEC PC-9800 hireso */
+
/*
* This character is the same as _POSIX_VDISABLE: it cannot be used as
* a c_cc[] character, but indicates that a particular special character
diff -urN linux/include/linux/vt.h linux98/include/linux/vt.h
--- linux/include/linux/vt.h Sat Oct 19 13:02:30 2002
+++ linux98/include/linux/vt.h Mon Oct 21 14:26:03 2002
@@ -50,5 +50,6 @@
#define VT_RESIZEX 0x560A /* set kernel's idea of screensize + more */
#define VT_LOCKSWITCH 0x560B /* disallow vt switching */
#define VT_UNLOCKSWITCH 0x560C /* allow vt switching */
+#define VT_GDC_RESIZE 0x5698
#endif /* _LINUX_VT_H */
diff -urN linux/include/linux/vt_buffer.h linux98/include/linux/vt_buffer.h
--- linux/include/linux/vt_buffer.h Sat Oct 19 13:02:24 2002
+++ linux98/include/linux/vt_buffer.h Mon Oct 21 14:28:40 2002
@@ -19,6 +19,10 @@
#include <asm/vga.h>
#endif
+#ifdef CONFIG_GDC_CONSOLE
+#include <asm/gdc.h>
+#endif
+
#ifndef VT_BUF_HAVE_RW
#define scr_writew(val, addr) (*(addr) = (val))
#define scr_readw(addr) (*(addr))
Osamu Tomita
2002-11-02 17:51:00 UTC
Permalink
This is a part 4/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
i386 core modules
- IO port address change
- IRQ number change
- adapted to hardware memory mapping.
- CLOCK_TICK_RATE constat to variable.
some PC-9800 can change base clock by hardware switch!

diffstat:
arch/i386/Kconfig | 30 ++
arch/i386/Makefile | 18 +
arch/i386/kernel/Makefile | 4 arch/i386/kernel/cpu/proc.c | 2 arch/i386/kernel/i8259.c | 100 ++++----
arch/i386/kernel/pc9800_debug.c | 362 ++++++++++++++++++++++++++++++++
arch/i386/kernel/reboot.c | 18 -
arch/i386/kernel/setup.c | 101 --------
arch/i386/kernel/time.c | 115 +---------
arch/i386/kernel/timers/timer_pit.c | 22 +
arch/i386/kernel/timers/timer_tsc.c | 88 -------
arch/i386/kernel/traps.c | 21 -
arch/i386/kernel/vm86.c | 21 +
arch/i386/mach-generic/calibrate_tsc.h | 88 +++++++
arch/i386/mach-generic/io_ports.h | 30 ++
arch/i386/mach-generic/mach_reboot.h | 30 ++
arch/i386/mach-generic/mach_resources.h | 113 +++++++++
arch/i386/mach-generic/mach_time.h | 122 ++++++++++
arch/i386/mach-generic/mach_traps.h | 31 ++
arch/i386/mach-pc9800/Makefile | 15 +
arch/i386/mach-pc9800/calibrate_tsc.h | 73 ++++++
arch/i386/mach-pc9800/do_timer.h | 80 +++++++
arch/i386/mach-pc9800/entry_arch.h | 1 arch/i386/mach-pc9800/io_ports.h | 30 ++
arch/i386/mach-pc9800/irq_vectors.h | 1 arch/i386/mach-pc9800/mach_apic.h | 1 arch/i386/mach-pc9800/mach_reboot.h | 21 +
arch/i386/mach-pc9800/mach_resources.h | 192 ++++++++++++++++
arch/i386/mach-pc9800/mach_time.h | 136 ++++++++++++
arch/i386/mach-pc9800/mach_traps.h | 29 ++
arch/i386/mach-pc9800/setup.c | 117 ++++++++++
arch/i386/mach-pc9800/setup_arch_post.h | 29 ++
arch/i386/mach-pc9800/setup_arch_pre.h | 36 +++
arch/i386/mach-pc9800/smpboot_hooks.h | 33 ++
arch/i386/mach-summit/calibrate_tsc.h | 1 arch/i386/mach-summit/io_ports.h | 1 arch/i386/mach-summit/mach_reboot.h | 1
arch/i386/mach-summit/mach_resources.h | 1 arch/i386/mach-summit/mach_time.h | 1 arch/i386/mach-summit/mach_traps.h | 1
arch/i386/mach-visws/calibrate_tsc.h | 1 arch/i386/mach-visws/io_ports.h | 1 arch/i386/mach-visws/mach_reboot.h | 1
arch/i386/mach-visws/mach_resources.h | 1 arch/i386/mach-visws/mach_time.h | 1 arch/i386/mach-visws/mach_traps.h | 1
arch/i386/mach-voyager/calibrate_tsc.h | 1 arch/i386/mach-voyager/io_ports.h | 1 arch/i386/mach-voyager/mach_reboot.h | 1
arch/i386/mach-voyager/mach_resources.h | 1 arch/i386/mach-voyager/mach_time.h | 1 arch/i386/mach-voyager/mach_traps.h | 1 52 files
changed, 1764 insertions(+), 364 deletions(-)

patch:
diff -urN linux/arch/i386/Makefile linux98/arch/i386/Makefile
--- linux/arch/i386/Makefile Thu Oct 31 13:23:02 2002
+++ linux98/arch/i386/Makefile Thu Oct 31 13:56:57 2002
@@ -48,8 +48,14 @@
ifdef CONFIG_VISWS
MACHINE := mach-visws
else
+ifdef CONFIG_PC9800
+MACHINE := mach-pc9800
+else
+ifndef MACHINE
MACHINE := mach-generic
endif
+endif
+endif
HEAD := arch/i386/kernel/head.o arch/i386/kernel/init_task.o
@@ -65,15 +71,20 @@
CFLAGS += -Iarch/i386/$(MACHINE)
AFLAGS += -Iarch/i386/$(MACHINE)
-makeboot = $(call descend,arch/i386/boot,$(1))
+ifndef CONFIG_PC9800
+ARCHDIR=arch/i386/boot
+else
+ARCHDIR=arch/i386/boot98
+endif
+makeboot = $(call descend,$(ARCHDIR),$(1))
.PHONY: zImage bzImage compressed zlilo bzlilo zdisk bzdisk install \
clean archclean archmrproper
all: bzImage
-BOOTIMAGE=arch/i386/boot/bzImage
-zImage zlilo zdisk: BOOTIMAGE=arch/i386/boot/zImage
+BOOTIMAGE=$(ARCHDIR)/bzImage
+zImage zlilo zdisk: BOOTIMAGE=$(ARCHDIR)/zImage
zImage bzImage: vmlinux
+@$(call makeboot,$(BOOTIMAGE))
@@ -91,5 +102,6 @@
archclean:
@$(MAKE) -f scripts/Makefile.clean obj=arch/i386/boot
+ @$(MAKE) -f scripts/Makefile.clean obj=arch/i386/boot98
archmrproper:
diff -urN linux/arch/i386/Kconfig linux98/arch/i386/Kconfig
--- linux/arch/i386/Kconfig Thu Oct 31 13:23:02 2002
+++ linux98/arch/i386/Kconfig Sat Nov 2 15:11:17 2002
@@ -964,6 +964,12 @@
# Visual Workstation support is utterly broken.
# If you want to see it working mail an VW540 to ***@infradead.org 8)
#bool 'SGI Visual Workstation support' CONFIG_VISWS
+config PC9800
+ bool "NEC PC-9800 architecture support"
+ help
+ To make kernel for NEC PC-9801/PC-9821 architecture, say Y.
+ If say Y, kernel works -ONLY- on PC-9800 architecture.
+
config X86_VISWS_APIC
bool
depends on VISWS
@@ -1056,7 +1062,7 @@
config EISA
bool "EISA support"
- depends on ISA
+ depends on ISA && !PC9800
---help---
The Extended Industry Standard Architecture (EISA) bus was
developed as an open alternative to the IBM MicroChannel bus.
@@ -1072,7 +1078,7 @@
config MCA
bool "MCA support"
- depends on !VISWS
+ depends on !(VISWS || PC9800)
help
MicroChannel Architecture is found in some IBM PS/2 machines and
laptops. It is a bus system similar to PCI or ISA. See
@@ -1420,6 +1426,7 @@
config VGA_CONSOLE
bool "VGA text console"
+ depends on !PC9800
help
Saying Y here will allow you to use Linux in text mode through a
display that complies with the generic VGA standard. Virtually
@@ -1433,6 +1440,7 @@
config VIDEO_SELECT
bool "Video mode selection support"
+ depends on !PC9800
---help---
This enables support for text mode selection on kernel startup. If
you want to take advantage of some high-resolution text mode your
@@ -1446,6 +1454,18 @@
Read the file <file:Documentation/svga.txt> for more information
about the Video mode selection support. If unsure, say N.
+config GDC_CONSOLE
+ bool "PC-9800 GDC text console"
+ depends on PC9800
+ default y
+ help
+ This enables support for PC-9800 standard text mode console.
+ If use PC-9801/PC-9821, Say Y.
+
+config GDC_32BITACCESS
+ bool "Enable 32-bit access to text video RAM"
+ depends on GDC_CONSOLE
+
if EXPERIMENTAL
config MDA_CONSOLE
@@ -1612,6 +1632,12 @@
symbolic stack backtraces. This increases the size of the kernel
somewhat, as all symbols have to be loaded into the kernel image.
+config PC9800_UCGLOG
+ bool "Save kernel messages into UCG-RAM"
+ depends on PC9800
+ help
+ This enables saving kernel messases into PC-9800's NVRAM.
+
config X86_EXTRA_IRQS
bool
depends on X86_LOCAL_APIC
diff -urN linux/arch/i386/kernel/Makefile linux98/arch/i386/kernel/Makefile
--- linux/arch/i386/kernel/Makefile Wed Oct 16 13:20:28 2002
+++ linux98/arch/i386/kernel/Makefile Wed Oct 16 14:27:17 2002
@@ -10,6 +10,10 @@
ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \
pci-dma.o i386_ksyms.o i387.o bluesmoke.o dmi_scan.o \
bootflag.o
+ifeq ($(CONFIG_PC9800),y)
+export-objs += pc9800_debug.o
+obj-$(CONFIG_PC9800) += pc9800_debug.o
+endif
obj-y += cpu/
obj-y += timers/
diff -urN linux/arch/i386/kernel/cpu/proc.c linux98/arch/i386/kernel/cpu/proc.c
--- linux/arch/i386/kernel/cpu/proc.c Mon Jun 17 11:31:35 2002
+++ linux98/arch/i386/kernel/cpu/proc.c Mon Jun 17 23:49:02 2002
@@ -76,7 +76,7 @@
seq_printf(m, "cache size\t: %d KB\n", c->x86_cache_size);

/* We use exception 16 if we have hardware math and we've either seen it or the CPU claims it is internal */
- fpu_exception = c->hard_math && (ignore_irq13 || cpu_has_fpu);
+ fpu_exception = c->hard_math && (ignore_fpu_irq || cpu_has_fpu);
seq_printf(m, "fdiv_bug\t: %s\n"
"hlt_bug\t\t: %s\n"
"f00f_bug\t: %s\n"
diff -urN linux/arch/i386/kernel/i8259.c linux98/arch/i386/kernel/i8259.c
--- linux/arch/i386/kernel/i8259.c Tue Oct 8 03:25:15 2002
+++ linux98/arch/i386/kernel/i8259.c Thu Oct 10 21:46:44 2002
@@ -25,6 +25,8 @@
#include <linux/irq.h>
+#include "io_ports.h"
+
/*
* This is the 'legacy' 8259A Programmable Interrupt Controller,
* present in the majority of PC/AT boxes.
@@ -74,8 +76,8 @@
static unsigned int cached_irq_mask = 0xffff;
#define __byte(x,y) (((unsigned char *)&(y))[x])
-#define cached_21 (__byte(0,cached_irq_mask))
-#define cached_A1 (__byte(1,cached_irq_mask))
+#define cached_master_mask (__byte(0,cached_irq_mask))
+#define cached_slave_mask (__byte(1,cached_irq_mask))
/*
* Not all IRQs can be routed through the IO-APIC, eg. on certain (older)
@@ -96,9 +98,9 @@
spin_lock_irqsave(&i8259A_lock, flags);
cached_irq_mask |= mask;
if (irq & 8)
- outb(cached_A1,0xA1);
+ outb(cached_slave_mask, PIC_SLAVE_IMR);
else
- outb(cached_21,0x21);
+ outb(cached_master_mask, PIC_MASTER_IMR);
spin_unlock_irqrestore(&i8259A_lock, flags);
}
@@ -110,9 +112,9 @@
spin_lock_irqsave(&i8259A_lock, flags);
cached_irq_mask &= mask;
if (irq & 8)
- outb(cached_A1,0xA1);
+ outb(cached_slave_mask, PIC_SLAVE_IMR);
else
- outb(cached_21,0x21);
+ outb(cached_master_mask, PIC_MASTER_IMR);
spin_unlock_irqrestore(&i8259A_lock, flags);
}
@@ -124,9 +126,9 @@
spin_lock_irqsave(&i8259A_lock, flags);
if (irq < 8)
- ret = inb(0x20) & mask;
+ ret = inb(PIC_MASTER_CMD) & mask;
else
- ret = inb(0xA0) & (mask >> 8);
+ ret = inb(PIC_SLAVE_CMD) & (mask >> 8);
spin_unlock_irqrestore(&i8259A_lock, flags);
return ret;
@@ -152,14 +154,14 @@
int irqmask = 1<<irq;
if (irq < 8) {
- outb(0x0B,0x20); /* ISR register */
- value = inb(0x20) & irqmask;
- outb(0x0A,0x20); /* back to the IRR register */
+ outb(0x0B,PIC_MASTER_CMD); /* ISR register */
+ value = inb(PIC_MASTER_CMD) & irqmask;
+ outb(0x0A,PIC_MASTER_CMD); /* back to the IRR register */
return value;
}
- outb(0x0B,0xA0); /* ISR register */
- value = inb(0xA0) & (irqmask >> 8);
- outb(0x0A,0xA0); /* back to the IRR register */
+ outb(0x0B,PIC_SLAVE_CMD); /* ISR register */
+ value = inb(PIC_SLAVE_CMD) & (irqmask >> 8);
+ outb(0x0A,PIC_SLAVE_CMD); /* back to the IRR register */
return value;
}
@@ -196,14 +198,14 @@
handle_real_irq:
if (irq & 8) {
- inb(0xA1); /* DUMMY - (do we need this?) */
- outb(cached_A1,0xA1);
- outb(0x60+(irq&7),0xA0);/* 'Specific EOI' to slave */
- outb(0x62,0x20); /* 'Specific EOI' to master-IRQ2 */
+ inb(PIC_SLAVE_IMR); /* DUMMY - (do we need this?) */
+ outb(cached_slave_mask, PIC_SLAVE_IMR);
+ outb(0x60+(irq&7),PIC_SLAVE_CMD);/* 'Specific EOI' to slave */
+ outb(0x60+PIC_CASCADE_IR,PIC_MASTER_CMD); /* 'Specific EOI' to master-IRQ2 */
} else {
- inb(0x21); /* DUMMY - (do we need this?) */
- outb(cached_21,0x21);
- outb(0x60+irq,0x20); /* 'Specific EOI' to master */
+ inb(PIC_MASTER_IMR); /* DUMMY - (do we need this?) */
+ outb(cached_master_mask, PIC_MASTER_IMR);
+ outb(0x60+irq,PIC_MASTER_CMD); /* 'Specific EOI to master */
}
spin_unlock_irqrestore(&i8259A_lock, flags);
return;
@@ -275,26 +277,24 @@
spin_lock_irqsave(&i8259A_lock, flags);
- outb(0xff, 0x21); /* mask all of 8259A-1 */
- outb(0xff, 0xA1); /* mask all of 8259A-2 */
+ outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */
+ outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-2 */
/*
* outb_p - this has to work on a wide range of PC hardware.
*/
- outb_p(0x11, 0x20); /* ICW1: select 8259A-1 init */
- outb_p(0x20 + 0, 0x21); /* ICW2: 8259A-1 IR0-7 mapped to 0x20-0x27 */
- outb_p(0x04, 0x21); /* 8259A-1 (the master) has a slave on IR2 */
- if (auto_eoi)
- outb_p(0x03, 0x21); /* master does Auto EOI */
- else
- outb_p(0x01, 0x21); /* master expects normal EOI */
-
- outb_p(0x11, 0xA0); /* ICW1: select 8259A-2 init */
- outb_p(0x20 + 8, 0xA1); /* ICW2: 8259A-2 IR0-7 mapped to 0x28-0x2f */
- outb_p(0x02, 0xA1); /* 8259A-2 is a slave on master's IR2 */
- outb_p(0x01, 0xA1); /* (slave's support for AEOI in flat mode
- is to be investigated) */
-
+ outb_p(0x11, PIC_MASTER_CMD); /* ICW1: select 8259A-1 init */
+ outb_p(0x20 + 0, PIC_MASTER_IMR); /* ICW2: 8259A-1 IR0-7 mapped to 0x20-0x27 */
+ outb_p(1U << PIC_CASCADE_IR, PIC_MASTER_IMR); /* 8259A-1 (the master) has a slave on IR2 */
+ if (auto_eoi) /* master does Auto EOI */
+ outb_p(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR);
+ else /* master expects normal EOI */
+ outb_p(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR);
+
+ outb_p(0x11, PIC_SLAVE_CMD); /* ICW1: select 8259A-2 init */
+ outb_p(0x20 + 8, PIC_SLAVE_IMR); /* ICW2: 8259A-2 IR0-7 mapped to 0x28-0x2f */
+ outb_p(PIC_CASCADE_IR, PIC_SLAVE_IMR); /* 8259A-2 is a slave on master's IR2 */
+ outb_p(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR); /* (slave's support for AEOI in flat mode is to be investigated) */
if (auto_eoi)
/*
* in AEOI mode we just have to mask the interrupt
@@ -306,8 +306,8 @@
udelay(100); /* wait for 8259A to initialize */
- outb(cached_21, 0x21); /* restore master IRQ mask */
- outb(cached_A1, 0xA1); /* restore slave IRQ mask */
+ outb(cached_master_mask, PIC_MASTER_IMR); /* restore master IRQ mask */
+ outb(cached_slave_mask, PIC_SLAVE_IMR); /* restore slave IRQ mask */
spin_unlock_irqrestore(&i8259A_lock, flags);
}
@@ -324,11 +324,17 @@
* be shot.
*/
+/*
+ * =PC9800NOTE= In NEC PC-9800, we use irq8 instead of irq13!
+ */
+
static void math_error_irq(int cpl, void *dev_id, struct pt_regs *regs)
{
extern void math_error(void *);
+#ifndef CONFIG_PC9800
outb(0,0xF0);
- if (ignore_irq13 || !boot_cpu_data.hard_math)
+#endif
+ if (ignore_fpu_irq || !boot_cpu_data.hard_math)
return;
math_error((void *)regs->eip);
}
@@ -337,7 +343,7 @@
* New motherboards sometimes make IRQ 13 be a PCI interrupt,
* so allow interrupt sharing.
*/
-static struct irqaction irq13 = { math_error_irq, 0, 0, "fpu", NULL, NULL };
+static struct irqaction fpu_irq = { math_error_irq, 0, 0, "fpu", NULL, NULL };
void __init init_ISA_irqs (void)
{
@@ -393,14 +399,18 @@
* Set the clock to HZ Hz, we already have a valid
* vector now:
*/
- outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
- outb_p(LATCH & 0xff , 0x40); /* LSB */
- outb(LATCH >> 8 , 0x40); /* MSB */
+ outb_p(0x34, PIT_MODE); /* binary, mode 2, LSB/MSB, ch 0 */
+ outb_p(LATCH & 0xff, PIT_CH0); /* LSB */
+ outb(LATCH >> 8 , PIT_CH0); /* MSB */
/*
* External FPU? Set up irq13 if so, for
* original braindamaged IBM FERR coupling.
*/
if (boot_cpu_data.hard_math && !cpu_has_fpu)
- setup_irq(13, &irq13);
+#ifndef CONFIG_PC9800
+ setup_irq(13, &fpu_irq);
+#else
+ setup_irq(8, &fpu_irq);
+#endif
}
diff -urN linux/arch/i386/kernel/pc9800_debug.c linux98/arch/i386/kernel/pc9800_debug.c
--- linux/arch/i386/kernel/pc9800_debug.c Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/kernel/pc9800_debug.c Wed May 1 16:47:15 2002
@@ -0,0 +1,362 @@
+/*
+ * linux/arch/i386/kernel/pc9800_debug.c
+ *
+ * Copyright (C) 1998 Linux/98 Project
+ *
+ * Revised by TAKAI Kousuke, Nov 1999.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <linux/console.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/pc9800_debug.h>
+#include <asm/pc9800.h>
+
+unsigned char __pc9800_beep_flag = 0x7;
+
+/* pc9800_beep_{on|off|toggle} are moved to <asm/pc9800_debug.h>. */
+
+/* Normal CG window begins at physical address 0xA4000,
+ but user-definable characters are on odd address only... */
+#define UCG_WINDOW (phys_to_virt (0xA4001))
+
+#define UCG_MAX_SIZE (UCG_LOG_END - UCG_LOG_START + 1)
+
+#define UCG_CHAR_NR(x) ((x) / 32)
+#define UCG_2ND_BYTE(x) ((UCG_CHAR_NR (x) & 0x1f) + 0x80)
+#define UCG_1ST_BYTE(x) ((UCG_CHAR_NR (x) >> 5) + 0x76 - 0x20 + 0x80)
+#define UCG_LR(x) (((x) & 16) << 1)
+#define UCG_LR_MASK (1U << 5)
+#define UCG_OFFSET(x) ((x) % 16)
+
+#ifdef CONFIG_PC9800_UCGLOG
+/*
+ * Notes for PC-9800 UCG-log facility:
+ *
+ * Official specification of PC-9800 says they can have user-definable
+ * character-generator (UCG) RAM for 188 characters, but actual
+ * implementations appear to have 256 characters (`188' seems to come
+ * from adapting it to 94x94 character set). Thus there is 2KB-odd of
+ * unused area in UCG-RAM (one character consists of 16*16 dots, or
+ * 32 bytes). This area is not touched and its content will be preserved
+ * around system reset. This facility uses this area for saving
+ * kernel messages.
+ *
+ * UCG-RAM layout:
+ *
+ * Page Char Description
+ * ---- ---- ----------------------------------------------------
+ * 76 00 Magic string and character count in first 16 bytes
+ * 00 (remaining 16 bytes)
+ * : Log messages 1/2
+ * 1f
+ * 20 (unused)
+ * 21
+ * : Officially used for user-definable characters
+ * 7e
+ * 7f (unused)
+ * 77 00
+ * : Log messages 2/2
+ * 1f
+ * 20 (unused)
+ * 21
+ * : Officially used for user-definable characters
+ * 7e
+ * 7f (unused)
+ *
+ * + Characters 20--7f on each page seem to be initialized by
+ * system firmware. + */
+
+
+/* UCG-RAM offsets. */
+#define UCG_LOG_MAGIC 0
+#define UCG_LOG_HEAD 12
+#define UCG_LOG_SIZE 14
+#define UCG_LOG_START 16
+#define UCG_LOG_END (32 * 32 * 2 - 1)
+
+#define UCG_MAGIC_STRING "Linux/98"
+
+static unsigned int ucg_log_head = UCG_LOG_START;
+static unsigned int ucg_log_size = 0;
+
+static void
+ucglog_write(struct console *console, const char *buf, unsigned int length)
+{
+ unsigned char *const cg_window = UCG_WINDOW;
+
+ /*
+ * Note that we are called with interrupt disabled
+ * (spin_lock_irqsave in kernel/printk.c).
+ */
+
+ if ((ucg_log_size += length) > UCG_MAX_SIZE)
+ ucg_log_size = UCG_MAX_SIZE;
+
+ outb(0x0b, 0x68); /* bitmap access mode */
+
+ while (length) {
+ unsigned char *p;
+ unsigned int count;
+ u8 lr;
+
+ outb(UCG_2ND_BYTE (ucg_log_head), 0xa1);
+ outb(UCG_1ST_BYTE (ucg_log_head), 0xa3);
+ lr = UCG_LR(ucg_log_head);
+ do {
+ outb(lr, 0xa5);
+ p = cg_window + UCG_OFFSET(ucg_log_head) * 2;
+ count = 16 - UCG_OFFSET(ucg_log_head);
+ if (count > length)
+ count = length;
+ length -= count;
+ ucg_log_head += count;
+ do {
+ *p = *buf++;
+ p += 2;
+ } while (--count);
+ } while (length && (lr ^= UCG_LR_MASK));
+ }
+
+ if (ucg_log_head > UCG_LOG_END)
+ ucg_log_head = UCG_LOG_START;
+ outb(UCG_2ND_BYTE(UCG_LOG_HEAD), 0xa1);
+ outb(UCG_1ST_BYTE(UCG_LOG_HEAD), 0xa3);
+ outb(UCG_LR(UCG_LOG_HEAD), 0xa5);
+ cg_window[(UCG_OFFSET(UCG_LOG_HEAD)) * 2] = ucg_log_head;
+ cg_window[(UCG_OFFSET(UCG_LOG_HEAD) + 1) * 2] = ucg_log_head >> 8;
+#if UCG_CHAR_NR(UCG_LOG_HEAD) != UCG_CHAR_NR(UCG_LOG_SIZE)
+ outb(UCG_2ND_BYTE(UCG_LOG_SIZE), 0xa1);
+ outb(UCG_1ST_BYTE(UCG_LOG_SIZE), 0xa3);
+#endif
+#if UCG_LR(UCG_LOG_HEAD) != UCG_LR(UCG_LOG_SIZE)
+ outb(UCG_LR(UCG_LOG_SIZE), 0xa5);
+#endif
+ cg_window[(UCG_OFFSET(UCG_LOG_SIZE)) * 2] = ucg_log_size;
+ cg_window[(UCG_OFFSET(UCG_LOG_SIZE) + 1) * 2] = ucg_log_size >> 8;
+
+ outb(0x0a, 0x68);
+}
+
+static struct console ucglog_console = {
+ name: "ucg",
+ write: ucglog_write,
+ setup: NULL,
+ flags: CON_PRINTBUFFER,
+ index: -1,
+};
+
+static int __init
+ucglog_init(void)
+{
+ unsigned long flags;
+ const u8 *p;
+ u8 *cg_window;
+ static const union {
+ struct {
+ char magic[12];
+ u16 start;
+ u16 size;
+ } s;
+ u8 bytes[16];
+ } ucg_init_data __initdata = { { UCG_MAGIC_STRING, 0, 0 } };
+
+ if (PC9800_HIGHRESO_P()) {
+ /* Not implemented (yet)... */
+ return 0;
+ }
+
+ save_flags(flags);
+ cli();
+ outb(0x0b, 0x68); /* bitmap access mode */
+ outb(UCG_2ND_BYTE(UCG_LOG_MAGIC), 0xa1);
+ outb(UCG_1ST_BYTE(UCG_LOG_MAGIC), 0xa3);
+ outb(UCG_LR(UCG_LOG_MAGIC), 0xa5);
+ for (cg_window = UCG_WINDOW, p = ucg_init_data.bytes;
+ p < (&ucg_init_data + 1)->bytes; cg_window += 2)
+ *cg_window = *p++;
+ outb(0x0a, 0x68);
+ restore_flags(flags);
+
+ register_console(&ucglog_console);
+ printk(KERN_INFO "UCG-RAM console driver installed\n");
+ return 0;
+}
+
+__initcall (ucglog_init);
+
+#endif /* CONFIG_PC9800_UCGLOG */
+
+/*
+#define CONFIG_PC9800_UCGSAVEARGS
+*/
+
+#ifdef CONFIG_PC9800_UCGSAVEARGS
+
+#define UCG_SAVEARGS_START (1 * 32)
+
+void
+ucg_saveargs(unsigned int n, ...)
+{
+ u8 *cg;
+ unsigned int count;
+ unsigned int addr;
+ unsigned long flags;
+ const u8 *p = (const u8 *) (&n - 1);
+
+ save_flags(flags);
+ cli();
+ outb(0x0b, 0x68); /* bitmap access mode */
+ outb(UCG_2ND_BYTE(UCG_SAVEARGS_START), 0xa1);
+ outb(UCG_1ST_BYTE(UCG_SAVEARGS_START), 0xa3);
+ outb(UCG_LR(UCG_SAVEARGS_START), 0xa5);
+ for (cg = UCG_WINDOW, count = 0; count < 4; count++)
+ cg[count * 2] = p[count];
+
+ addr = UCG_SAVEARGS_START + 4;
+ for (p += 8; n--; p += 4) {
+ if (UCG_OFFSET(addr) == 0) {
+ outb(UCG_2ND_BYTE(addr), 0xa1);
+ outb(UCG_1ST_BYTE(addr), 0xa3);
+ outb(UCG_LR(addr), 0xa5);
+ }
+ cg[(UCG_OFFSET(addr) + 0) * 2] = p[0];
+ cg[(UCG_OFFSET(addr) + 1) * 2] = p[1];
+ cg[(UCG_OFFSET(addr) + 2) * 2] = p[2];
+ cg[(UCG_OFFSET(addr) + 3) * 2] = p[3];
+ addr += 4;
+ }
+
+ outb(UCG_2ND_BYTE(0), 0xa1);
+ outb(UCG_1ST_BYTE(0), 0xa3);
+ outb(UCG_LR(0), 0xa5);
+
+ outb(0x0a, 0x68);
+ restore_flags(flags);
+}
+#endif
+
+#ifdef CONFIG_PC9800_ASSERT
+void
+__assert_fail(const char *base_file, const char *file, unsigned int line,
+ const char *function, void *return_address, const char *expr)
+{
+ panic("In function `%s' (called from [<%p>])\n" KERN_EMERG
+ "%s%s%s%s:%u: Assertion `%s' failed.",
+ function, return_address, file,
+ base_file == file ? "" : " (",
+ base_file == file ? "" : base_file,
+ base_file == file ? "" : ")",
+ line, expr);
+}
+
+void
+__invalid_kernel_pointer(const char *base_file, const char *file,
+ unsigned int line, const char *function,
+ void *return_address,
+ const char *expr, void *val)
+{
+ panic("In function `%s' (called from [<%p>])\n" KERN_EMERG
+ "%s%s%s%s:%u: Invalid kernel pointer `%s' (%p).",
+ function, return_address, file,
+ base_file == file ? "" : " (",
+ base_file == file ? "" : base_file,
+ base_file == file ? "" : ")",
+ line, expr, val);
+}
+
+#endif /* CONFIG_PC9800_ASSERT */
+
+unsigned char pc9800_saveregs_enabled;
+
+__asm__ (".text\n"
+ " .global __pc9800_saveregs\n"
+ "__pc9800_saveregs:\n"
+#if 1
+ " pushfl\n"
+ " cmpb $0,pc9800_saveregs_enabled\n"
+ " je 1f\n"
+ " pushl %edi\n" /* reverse order of PUSHA */
+ " pushl %esi\n"
+ " pushl %ebp\n"
+ " leal 20(%esp),%esi\n" /* original ESP */
+ " pushl %esi\n"
+ " pushl %ebx\n"
+ " pushl %edx\n"
+ " pushl %ecx\n"
+ " pushl %eax\n"
+ " movl $0xc0000780,%edi\n" /* save few words on stack */
+ " movl $20, %ecx\n"
+ " cld; rep; ss; movsl\n" /* EDI becomes 0xC00007D0 */
+ " subl $(20+1+1+8)*4,%esi\n" /* ESI points EAX on stack */
+ " movl $8,%ecx\n"
+ " rep; ss; movsl\n" /* save GP registers */
+ " ss; lodsl\n" /* EFLAGS */
+ " ss; movsl\n" /* save EIP */
+ " stosl\n" /* save EFLAGS */
+ " movl %cr3,%eax\n" /* save control registers */
+ " stosl\n"
+ " movl %cr0,%eax\n"
+ " stosl\n"
+ " popl %eax\n"
+ " popl %ecx\n"
+ " addl $4*4,%esp\n" /* discard EDX/EBX/ESP/EBP */
+ " popl %esi\n"
+ " popl %edi\n"
+ "1: popfl\n"
+#else
+ " cmpb $0,pc9800_saveregs_enabled\n"
+ " je 1f\n"
+ " pushl %eax\n"
+ " movl %eax,0xc00007d0\n"
+ " movl %ecx,0xc00007d4\n"
+ " movl %edx,0xc00007d8\n"
+ " movl %ebx,0xc00007dc\n"
+ " leal 8(%esp),%eax\n" /* original ESP */
+ " movl %eax,0xc00007e0\n"
+ " movl %ebp,0xc00007e4\n"
+ " movl %esi,0xc00007e8\n"
+ " movl %edi,0xc00007ec\n"
+ " movl 4(%esp),%eax\n" /* EIP as return address */
+ " movl %eax,0xc00007f0\n"
+ " pushfl\n"
+ " popl %eax\n"
+ " movl %eax,0xc00007f4\n"
+ " movl %cr3,%eax\n"
+ " movl %eax,0xc00007f8\n"
+ " movl %cr0,%eax\n"
+ " movl %eax,0xc00007fc\n"
+ " pushl %ecx\n"
+ " pushl %esi\n"
+ " pushl %edi\n"
+ " leal 20(%esp),%esi\n"
+ " movl $0xc0000780,%edi\n"
+ " movl $16,%ecx\n"
+ " cld; rep; ss; movsl\n"
+ " popl %edi\n"
+ " popl %esi\n"
+ " popl %ecx\n"
+ " popl %eax\n"
+ "1:\n"
+#endif
+ " ret");
+
+__asm__ (".weak mcount; mcount = __pc9800_saveregs");
+
+#if 0
+int
+test_mcount(void)
+{
+ printk("Calling mcount...\n");
+ pc9800_saveregs_enabled = 1;
+ mcount();
+}
+
+__initcall (test_mcount);
+#endif
diff -urN linux/arch/i386/kernel/reboot.c linux98/arch/i386/kernel/reboot.c
--- linux/arch/i386/kernel/reboot.c Sat Oct 19 13:01:20 2002
+++ linux98/arch/i386/kernel/reboot.c Sun Oct 20 14:59:44 2002
@@ -8,6 +8,7 @@
#include <linux/interrupt.h>
#include <linux/mc146818rtc.h>
#include <asm/uaccess.h>
+#include "mach_reboot.h"
/*
* Power off function, if any
@@ -125,15 +126,6 @@
0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */
};
-static inline void kb_wait(void)
-{
- int i;
-
- for (i=0; i<0x10000; i++)
- if ((inb_p(0x64) & 0x02) == 0)
- break;
-}
-
/*
* Switch to real mode and then execute the code
* specified by the code and length parameters.
@@ -264,13 +256,7 @@
/* rebooting needs to touch the page at absolute addr 0 */
*((unsigned short *)__va(0x472)) = reboot_mode;
for (;;) {
- int i;
- for (i=0; i<100; i++) {
- kb_wait();
- udelay(50);
- outb(0xfe,0x64); /* pulse reset low */
- udelay(50);
- }
+ mach_reboot();
/* That didn't work - force a triple fault.. */
__asm__ __volatile__("lidt %0": :"m" (no_idt));
__asm__ __volatile__("int3");
diff -urN linux/arch/i386/kernel/setup.c linux98/arch/i386/kernel/setup.c
--- linux/arch/i386/kernel/setup.c Thu Oct 31 13:23:02 2002
+++ linux98/arch/i386/kernel/setup.c Thu Oct 31 13:50:50 2002
@@ -20,6 +20,7 @@
* This file handles the architecture-dependent parts of initialization
*/
+#include <linux/config.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/tty.h>
@@ -40,6 +41,7 @@
#include <asm/setup.h>
#include <asm/arch_hooks.h>
#include "setup_arch_pre.h"
+#include "mach_resources.h"
static inline char * __init machine_specific_memory_setup(void);
@@ -47,7 +49,7 @@
* Machine setup..
*/
-char ignore_irq13; /* set if exception 16 works */
+char ignore_fpu_irq; /* set if exception 16 works */
struct cpuinfo_x86 boot_cpu_data = { 0, 0, 0, 0, -1, 1, 0, 0, -1 };
unsigned long mmu_cr4_features;
@@ -98,98 +100,8 @@
static char command_line[COMMAND_LINE_SIZE];
char saved_command_line[COMMAND_LINE_SIZE];
-struct resource standard_io_resources[] = {
- { "dma1", 0x00, 0x1f, IORESOURCE_BUSY },
- { "pic1", 0x20, 0x3f, IORESOURCE_BUSY },
- { "timer", 0x40, 0x5f, IORESOURCE_BUSY },
- { "keyboard", 0x60, 0x6f, IORESOURCE_BUSY },
- { "dma page reg", 0x80, 0x8f, IORESOURCE_BUSY },
- { "pic2", 0xa0, 0xbf, IORESOURCE_BUSY },
- { "dma2", 0xc0, 0xdf, IORESOURCE_BUSY },
- { "fpu", 0xf0, 0xff, IORESOURCE_BUSY }
-};
-#ifdef CONFIG_MELAN
-standard_io_resources[1] = { "pic1", 0x20, 0x21, IORESOURCE_BUSY };
-standard_io_resources[5] = { "pic2", 0xa0, 0xa1, IORESOURCE_BUSY };
-#endif
-
-#define STANDARD_IO_RESOURCES (sizeof(standard_io_resources)/sizeof(struct resource))
-
static struct resource code_resource = { "Kernel code", 0x100000, 0 };
static struct resource data_resource = { "Kernel data", 0, 0 };
-static struct resource vram_resource = { "Video RAM area", 0xa0000, 0xbffff, IORESOURCE_BUSY };
-
-/* System ROM resources */
-#define MAXROMS 6
-static struct resource rom_resources[MAXROMS] = {
- { "System ROM", 0xF0000, 0xFFFFF, IORESOURCE_BUSY },
- { "Video ROM", 0xc0000, 0xc7fff, IORESOURCE_BUSY }
-};
-
-#define romsignature(x) (*(unsigned short *)(x) == 0xaa55)
-
-static void __init probe_roms(void)
-{
- int roms = 1;
- unsigned long base;
- unsigned char *romstart;
-
- request_resource(&iomem_resource, rom_resources+0);
-
- /* Video ROM is standard at C000:0000 - C7FF:0000, check signature */
- for (base = 0xC0000; base < 0xE0000; base += 2048) {
- romstart = isa_bus_to_virt(base);
- if (!romsignature(romstart))
- continue;
- request_resource(&iomem_resource, rom_resources + roms);
- roms++;
- break;
- }
-
- /* Extension roms at C800:0000 - DFFF:0000 */
- for (base = 0xC8000; base < 0xE0000; base += 2048) {
- unsigned long length;
-
- romstart = isa_bus_to_virt(base);
- if (!romsignature(romstart))
- continue;
- length = romstart[2] * 512;
- if (length) {
- unsigned int i;
- unsigned char chksum;
-
- chksum = 0;
- for (i = 0; i < length; i++)
- chksum += romstart[i];
-
- /* Good checksum? */
- if (!chksum) {
- rom_resources[roms].start = base;
- rom_resources[roms].end = base + length - 1;
- rom_resources[roms].name = "Extension ROM";
- rom_resources[roms].flags = IORESOURCE_BUSY;
-
- request_resource(&iomem_resource, rom_resources + roms);
- roms++;
- if (roms >= MAXROMS)
- return;
- }
- }
- }
-
- /* Final check for motherboard extension rom at E000:0000 */
- base = 0xE0000;
- romstart = isa_bus_to_virt(base);
-
- if (romsignature(romstart)) {
- rom_resources[roms].start = base;
- rom_resources[roms].end = base + 65535;
- rom_resources[roms].name = "Extension ROM";
- rom_resources[roms].flags = IORESOURCE_BUSY;
-
- request_resource(&iomem_resource, rom_resources + roms);
- }
-}
static void __init limit_regions (unsigned long long size)
{
@@ -821,11 +733,8 @@
request_resource(res, &data_resource);
}
}
- request_resource(&iomem_resource, &vram_resource);
- /* request I/O space for devices used on all i[345]86 PCs */
- for (i = 0; i < STANDARD_IO_RESOURCES; i++)
- request_resource(&ioport_resource, standard_io_resources+i);
+ mach_request_resource( );
/* Tell the PCI layer not to allocate too close to the RAM area.. */
low_mem_size = ((max_low_pfn << PAGE_SHIFT) + 0xfffff) & ~0xfffff;
@@ -905,6 +814,8 @@
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
+#elif defined(CONFIG_GDC_CONSOLE)
+ conswitchp = &gdc_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
diff -urN linux/arch/i386/kernel/time.c linux98/arch/i386/kernel/time.c
--- linux/arch/i386/kernel/time.c Sat Oct 19 13:01:53 2002
+++ linux98/arch/i386/kernel/time.c Sun Oct 20 19:57:14 2002
@@ -54,12 +54,15 @@
#include <asm/processor.h>
#include <asm/timer.h>
-#include <linux/mc146818rtc.h>
+#include "mach_time.h"
+
#include <linux/timex.h>
#include <linux/config.h>
#include <asm/arch_hooks.h>
+#include "io_ports.h"
+
extern spinlock_t i8259A_lock;
#include "do_timer.h"
@@ -133,69 +136,13 @@
write_unlock_irq(&xtime_lock);
}
-/*
- * In order to set the CMOS clock precisely, set_rtc_mmss has to be
- * called 500 ms after the second nowtime has started, because when
- * nowtime is written into the registers of the CMOS clock, it will
- * jump to the next second precisely 500 ms later. Check the Motorola
- * MC146818A or Dallas DS12887 data sheet for details.
- *
- * BUG: This routine does not handle hour overflow properly; it just
- * sets the minutes. Usually you'll only notice that after reboot!
- */
static int set_rtc_mmss(unsigned long nowtime)
{
- int retval = 0;
- int real_seconds, real_minutes, cmos_minutes;
- unsigned char save_control, save_freq_select;
+ int retval;
/* gets recalled with irq locally disabled */
spin_lock(&rtc_lock);
- save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
- CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
-
- save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
- CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
-
- cmos_minutes = CMOS_READ(RTC_MINUTES);
- if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
- BCD_TO_BIN(cmos_minutes);
-
- /*
- * since we're only adjusting minutes and seconds,
- * don't interfere with hour overflow. This avoids
- * messing with unknown time zones but requires your
- * RTC not to be off by more than 15 minutes
- */
- real_seconds = nowtime % 60;
- real_minutes = nowtime / 60;
- if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
- real_minutes += 30; /* correct for half hour time zone */
- real_minutes %= 60;
-
- if (abs(real_minutes - cmos_minutes) < 30) {
- if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
- BIN_TO_BCD(real_seconds);
- BIN_TO_BCD(real_minutes);
- }
- CMOS_WRITE(real_seconds,RTC_SECONDS);
- CMOS_WRITE(real_minutes,RTC_MINUTES);
- } else {
- printk(KERN_WARNING
- "set_rtc_mmss: can't update from %d to %d\n",
- cmos_minutes, real_minutes);
- retval = -1;
- }
-
- /* The following flags have to be released exactly in this order,
- * otherwise the DS12887 (popular MC146818A clone with integrated
- * battery and quartz) will not reset the oscillator and will not
- * update precisely 500 ms later. You won't find this mentioned in
- * the Dallas Semiconductor data sheets, but who believes data
- * sheets anyway ... -- Markus Kuhn
- */
- CMOS_WRITE(save_control, RTC_CONTROL);
- CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+ retval = mach_set_rtc_mmss(nowtime);
spin_unlock(&rtc_lock);
return retval;
@@ -221,9 +168,9 @@
* on an 82489DX-based system.
*/
spin_lock(&i8259A_lock);
- outb(0x0c, 0x20);
+ outb(0x0c, PIC_MASTER_OCW3);
/* Ack the IRQ; AEOI will end it automatically. */
- inb(0x20);
+ inb(PIC_MASTER_POLL);
spin_unlock(&i8259A_lock);
}
#endif
@@ -237,14 +184,14 @@
*/
if ((time_status & STA_UNSYNC) == 0 &&
xtime.tv_sec > last_rtc_update + 660 &&
- (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 &&
- (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) {
+ (xtime.tv_nsec / 1000) >= TIME1 - ((unsigned) TICK_SIZE) / 2 &&
+ (xtime.tv_nsec / 1000) <= TIME2 + ((unsigned) TICK_SIZE) / 2) {
if (set_rtc_mmss(xtime.tv_sec) == 0)
last_rtc_update = xtime.tv_sec;
else
last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
}
- +
#ifdef CONFIG_MCA
if( MCA_bus ) {
/* The PS/2 uses level-triggered interrupts. You can't
@@ -289,43 +236,15 @@
/* not static: needed by APM */
unsigned long get_cmos_time(void)
{
- unsigned int year, mon, day, hour, min, sec;
- int i;
+ unsigned long retval;
spin_lock(&rtc_lock);
- /* The Linux interpretation of the CMOS clock register contents:
- * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
- * RTC registers show the second which has precisely just started.
- * Let's hope other operating systems interpret the RTC the same way.
- */
- /* read RTC exactly on falling edge of update flag */
- for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */
- if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
- break;
- for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */
- if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
- break;
- do { /* Isn't this overkill ? UIP above should guarantee consistency */
- sec = CMOS_READ(RTC_SECONDS);
- min = CMOS_READ(RTC_MINUTES);
- hour = CMOS_READ(RTC_HOURS);
- day = CMOS_READ(RTC_DAY_OF_MONTH);
- mon = CMOS_READ(RTC_MONTH);
- year = CMOS_READ(RTC_YEAR);
- } while (sec != CMOS_READ(RTC_SECONDS));
- if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
- {
- BCD_TO_BIN(sec);
- BCD_TO_BIN(min);
- BCD_TO_BIN(hour);
- BCD_TO_BIN(day);
- BCD_TO_BIN(mon);
- BCD_TO_BIN(year);
- }
+
+ retval = mach_get_cmos_time();
+
spin_unlock(&rtc_lock);
- if ((year += 1900) < 1970)
- year += 100;
- return mktime(year, mon, day, hour, min, sec);
+
+ return retval;
}
/* XXX this driverfs stuff should probably go elsewhere later -john */
diff -urN linux/arch/i386/kernel/timers/timer_pit.c linux98/arch/i386/kernel/timers/timer_pit.c
--- linux/arch/i386/kernel/timers/timer_pit.c Sat Oct 19 13:02:29 2002
+++ linux98/arch/i386/kernel/timers/timer_pit.c Mon Oct 21 11:39:53 2002
@@ -13,6 +13,7 @@
extern spinlock_t i8259A_lock;
extern spinlock_t i8253_lock;
#include "do_timer.h"
+#include "io_ports.h"
static int init_pit(void)
{
@@ -61,7 +62,8 @@
{
int count;
- static int count_p = LATCH; /* for the first call after boot */
+ static int count_p;
+ static int is_1st_boot = 1; /* for the first call after boot */
static unsigned long jiffies_p = 0;
/*
@@ -69,12 +71,18 @@
*/
unsigned long jiffies_t;
+ /* for support LATCH is not constant */
+ if (is_1st_boot) {
+ is_1st_boot = 0;
+ count_p = LATCH;
+ }
+
/* gets recalled with irq locally disabled */
spin_lock(&i8253_lock);
/* timer count may underflow right here */
- outb_p(0x00, 0x43); /* latch the count ASAP */
+ outb_p(0x00, PIT_MODE); /* latch the count ASAP */
- count = inb_p(0x40); /* read the latched count */
+ count = inb_p(PIT_CH0); /* read the latched count */
/*
* We do this guaranteed double memory access instead of a _p @@ -82,13 +90,13 @@
*/
jiffies_t = jiffies;
- count |= inb_p(0x40) << 8;
+ count |= inb_p(PIT_CH0) << 8;

/* VIA686a test code... reset the latch if count > max + 1 */
if (count > LATCH) {
- outb_p(0x34, 0x43);
- outb_p(LATCH & 0xff, 0x40);
- outb(LATCH >> 8, 0x40);
+ outb_p(0x34, PIT_MODE);
+ outb_p(LATCH & 0xff, PIT_CH0);
+ outb(LATCH >> 8, PIT_CH0);
count = LATCH - 1;
}

diff -urN linux/arch/i386/kernel/timers/timer_tsc.c linux98/arch/i386/kernel/timers/timer_tsc.c
--- linux/arch/i386/kernel/timers/timer_tsc.c Sat Oct 19 13:02:24 2002
+++ linux98/arch/i386/kernel/timers/timer_tsc.c Sun Oct 20 22:48:53 2002
@@ -12,6 +12,9 @@
#include <asm/timer.h>
#include <asm/io.h>
+#include "io_ports.h"
+#include "calibrate_tsc.h"
+
extern int x86_udelay_tsc;
extern spinlock_t i8253_lock;
@@ -19,8 +22,6 @@
/* Number of usecs that the last interrupt was delayed */
static int delay_at_last_interrupt;
-static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */
-
/* Cached *multiplier* to convert TSC counts to microseconds.
* (see the equation below).
* Equal to 2^32 * (1 / (clocks per usec) ).
@@ -77,10 +78,10 @@
rdtscl(last_tsc_low);
spin_lock(&i8253_lock);
- outb_p(0x00, 0x43); /* latch the count ASAP */
+ outb_p(0x00, PIT_MODE); /* latch the count ASAP */
- count = inb_p(0x40); /* read the latched count */
- count |= inb(0x40) << 8;
+ count = inb_p(PIT_CH0); /* read the latched count */
+ count |= inb(PIT_CH0) << 8;
spin_unlock(&i8253_lock);
count = ((LATCH-1) - count) * TICK_SIZE;
@@ -88,83 +89,6 @@
}
-/* ------ Calibrate the TSC ------- - * Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset().
- * Too much 64-bit arithmetic here to do this cleanly in C, and for
- * accuracy's sake we want to keep the overhead on the CTC speaker (channel 2)
- * output busy loop as low as possible. We avoid reading the CTC registers
- * directly because of the awkward 8-bit access mechanism of the 82C54
- * device.
- */
-
-#define CALIBRATE_LATCH (5 * LATCH)
-#define CALIBRATE_TIME (5 * 1000020/HZ)
-
-static unsigned long __init calibrate_tsc(void)
-{
- /* Set the Gate high, disable speaker */
- outb((inb(0x61) & ~0x02) | 0x01, 0x61);
-
- /*
- * Now let's take care of CTC channel 2
- *
- * Set the Gate high, program CTC channel 2 for mode 0,
- * (interrupt on terminal count mode), binary count,
- * load 5 * LATCH count, (LSB and MSB) to begin countdown.
- */
- outb(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */
- outb(CALIBRATE_LATCH & 0xff, 0x42); /* LSB of count */
- outb(CALIBRATE_LATCH >> 8, 0x42); /* MSB of count */
-
- {
- unsigned long startlow, starthigh;
- unsigned long endlow, endhigh;
- unsigned long count;
-
- rdtsc(startlow,starthigh);
- count = 0;
- do {
- count++;
- } while ((inb(0x61) & 0x20) == 0);
- rdtsc(endlow,endhigh);
-
- last_tsc_low = endlow;
-
- /* Error: ECTCNEVERSET */
- if (count <= 1)
- goto bad_ctc;
-
- /* 64-bit subtract - gcc just messes up with long longs */
- __asm__("subl %2,%0\n\t"
- "sbbl %3,%1"
- :"=a" (endlow), "=d" (endhigh)
- :"g" (startlow), "g" (starthigh),
- "0" (endlow), "1" (endhigh));
-
- /* Error: ECPUTOOFAST */
- if (endhigh)
- goto bad_ctc;
-
- /* Error: ECPUTOOSLOW */
- if (endlow <= CALIBRATE_TIME)
- goto bad_ctc;
-
- __asm__("divl %2"
- :"=a" (endlow), "=d" (endhigh)
- :"r" (endlow), "0" (0), "1" (CALIBRATE_TIME));
-
- return endlow;
- }
-
- /*
- * The CTC wasn't reliable: we got a hit on the very first read,
- * or the CPU was so fast/slow that the quotient wouldn't fit in
- * 32 bits..
- */
-bad_ctc:
- return 0;
-}
-
#ifdef CONFIG_CPU_FREQ
diff -urN linux/arch/i386/kernel/traps.c linux98/arch/i386/kernel/traps.c
--- linux/arch/i386/kernel/traps.c Sat Oct 19 13:01:16 2002
+++ linux98/arch/i386/kernel/traps.c Mon Oct 21 00:25:38 2002
@@ -49,6 +49,8 @@
#include <linux/irq.h>
#include <linux/module.h>
+#include "mach_traps.h"
+
asmlinkage int system_call(void);
asmlinkage void lcall7(void);
asmlinkage void lcall27(void);
@@ -449,11 +451,10 @@
printk("You probably have a hardware problem with your RAM chips\n");
/* Clear and disable the memory parity error line. */
- reason = (reason & 0xf) | 4;
- outb(reason, 0x61);
+ clear_mem_error(reason);
}
-static void io_check_error(unsigned char reason, struct pt_regs * regs)
+static inline void io_check_error(unsigned char reason, struct pt_regs * regs)
{
unsigned long i;
@@ -487,8 +488,9 @@
static void default_do_nmi(struct pt_regs * regs)
{
- unsigned char reason = inb(0x61);
+ unsigned char reason;
+ reason = get_nmi_reason();
if (!(reason & 0xc0)) {
#if CONFIG_X86_LOCAL_APIC
/*
@@ -506,15 +508,12 @@
if (reason & 0x80)
mem_parity_error(reason, regs);
if (reason & 0x40)
- io_check_error(reason, regs);
+ HANDLE_REASON_0X40(reason, regs);
/*
* Reassert NMI in case it became active meanwhile
* as it's edge-triggered.
*/
- outb(0x8f, 0x70);
- inb(0x71); /* dummy */
- outb(0x0f, 0x70);
- inb(0x71); /* dummy */
+ reassert_nmi();
}
static int dummy_nmi_callback(struct pt_regs * regs, int cpu)
@@ -697,7 +696,7 @@
asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code)
{
- ignore_irq13 = 1;
+ ignore_fpu_irq = 1;
math_error((void *)regs->eip);
}
@@ -754,7 +753,7 @@
{
if (cpu_has_xmm) {
/* Handle SIMD FPU exceptions on PIII+ processors. */
- ignore_irq13 = 1;
+ ignore_fpu_irq = 1;
simd_math_error((void *)regs->eip);
} else {
/*
diff -urN linux/arch/i386/kernel/vm86.c linux98/arch/i386/kernel/vm86.c
--- linux/arch/i386/kernel/vm86.c Sat Oct 12 13:21:31 2002
+++ linux98/arch/i386/kernel/vm86.c Sat Oct 12 16:09:20 2002
@@ -30,6 +30,7 @@
*
*/
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -732,10 +733,22 @@
free_vm86_irq(i);
}
+#ifndef CONFIG_PC9800
+# define ILLEGAL_IRQ(irq) ((irq) < 3 || (irq) > 15)
+# define FIRST_VM86_IRQ 3
+#else
+/*
+ * On PC-9800, slave PIC is wired master PIC's IR7,
+ * so that we don't allow vm86 to grab IRQ7.
+ */
+# define ILLEGAL_IRQ(irq) ((irq) < 2 || (irq) == 7 || (irq) > 15)
+# define FIRST_VM86_IRQ 2
+#endif
+
static inline void handle_irq_zombies(void)
{
int i;
- for (i=3; i<16; i++) {
+ for (i=FIRST_VM86_IRQ; i<16; i++) {
if (vm86_irqs[i].tsk) {
if (task_valid(vm86_irqs[i].tsk)) continue;
free_vm86_irq(i);
@@ -748,7 +761,7 @@
int bit;
unsigned long flags;

- if ( (irqnumber<3) || (irqnumber>15) ) return 0;
+ if (ILLEGAL_IRQ(irqnumber)) return 0;
if (vm86_irqs[irqnumber].tsk != current) return 0;
spin_lock_irqsave(&irqbits_lock, flags);
bit = irqbits & (1 << irqnumber);
@@ -774,7 +787,7 @@
handle_irq_zombies();
if (!capable(CAP_SYS_ADMIN)) return -EPERM;
if (!((1 << sig) & ALLOWED_SIGS)) return -EPERM;
- if ( (irq<3) || (irq>15) ) return -EPERM;
+ if (ILLEGAL_IRQ(irq)) return -EPERM;
if (vm86_irqs[irq].tsk) return -EPERM;
ret = request_irq(irq, &irq_handler, 0, VM86_IRQNAME, 0);
if (ret) return ret;
@@ -784,7 +797,7 @@
}
case VM86_FREE_IRQ: {
handle_irq_zombies();
- if ( (irqnumber<3) || (irqnumber>15) ) return -EPERM;
+ if (ILLEGAL_IRQ(irqnumber)) return -EPERM;
if (!vm86_irqs[irqnumber].tsk) return 0;
if (vm86_irqs[irqnumber].tsk != current) return -EPERM;
free_vm86_irq(irqnumber);
diff -urN linux/arch/i386/mach-generic/calibrate_tsc.h linux98/arch/i386/mach-generic/calibrate_tsc.h
--- linux/arch/i386/mach-generic/calibrate_tsc.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-generic/calibrate_tsc.h Mon Oct 21 09:30:16 2002
@@ -0,0 +1,88 @@
+/*
+ * arch/i386/mach-generic/calibrate_tsc.h
+ *
+ * Machine specific calibrate_tsc() for generic.
+ * Split out from timer_tsc.c by Osamu Tomita <***@cinet.co.jp>
+ */
+/* ------ Calibrate the TSC ------- + * Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset().
+ * Too much 64-bit arithmetic here to do this cleanly in C, and for
+ * accuracy's sake we want to keep the overhead on the CTC speaker (channel 2)
+ * output busy loop as low as possible. We avoid reading the CTC registers
+ * directly because of the awkward 8-bit access mechanism of the 82C54
+ * device.
+ */
+#ifndef _MACH_CALIBRATE_TSC_H
+#define _MACH_CALIBRATE_TSC_H
+
+#define CALIBRATE_LATCH (5 * LATCH)
+#define CALIBRATE_TIME (5 * 1000020/HZ)
+
+static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */
+
+static inline unsigned long calibrate_tsc(void)
+{
+ /* Set the Gate high, disable speaker */
+ outb((inb(0x61) & ~0x02) | 0x01, 0x61);
+
+ /*
+ * Now let's take care of CTC channel 2
+ *
+ * Set the Gate high, program CTC channel 2 for mode 0,
+ * (interrupt on terminal count mode), binary count,
+ * load 5 * LATCH count, (LSB and MSB) to begin countdown.
+ */
+ outb(0xb0, PIT_MODE); /* binary, mode 0, LSB/MSB, Ch 2 */
+ outb(CALIBRATE_LATCH & 0xff, PIT_CH2); /* LSB of count */
+ outb(CALIBRATE_LATCH >> 8, PIT_CH2); /* MSB of count */
+
+ {
+ unsigned long startlow, starthigh;
+ unsigned long endlow, endhigh;
+ unsigned long count;
+
+ rdtsc(startlow,starthigh);
+ count = 0;
+ do {
+ count++;
+ } while ((inb(0x61) & 0x20) == 0);
+ rdtsc(endlow,endhigh);
+
+ last_tsc_low = endlow;
+
+ /* Error: ECTCNEVERSET */
+ if (count <= 1)
+ goto bad_ctc;
+
+ /* 64-bit subtract - gcc just messes up with long longs */
+ __asm__("subl %2,%0\n\t"
+ "sbbl %3,%1"
+ :"=a" (endlow), "=d" (endhigh)
+ :"g" (startlow), "g" (starthigh),
+ "0" (endlow), "1" (endhigh));
+
+ /* Error: ECPUTOOFAST */
+ if (endhigh)
+ goto bad_ctc;
+
+ /* Error: ECPUTOOSLOW */
+ if (endlow <= CALIBRATE_TIME)
+ goto bad_ctc;
+
+ __asm__("divl %2"
+ :"=a" (endlow), "=d" (endhigh)
+ :"r" (endlow), "0" (0), "1" (CALIBRATE_TIME));
+
+ return endlow;
+ }
+
+ /*
+ * The CTC wasn't reliable: we got a hit on the very first read,
+ * or the CPU was so fast/slow that the quotient wouldn't fit in
+ * 32 bits..
+ */
+bad_ctc:
+ return 0;
+}
+
+#endif /* !_MACH_CALIBRATE_TSC_H */
diff -urN linux/arch/i386/mach-generic/io_ports.h linux98/arch/i386/mach-generic/io_ports.h
--- linux/arch/i386/mach-generic/io_ports.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-generic/io_ports.h Mon Oct 21 09:47:38 2002
@@ -0,0 +1,30 @@
+/*
+ * arch/i386/mach-generic/io_ports.h
+ *
+ * Machine specific IO port address definition for generic.
+ * Written by Osamu Tomita <***@cinet.co.jp>
+ */
+#ifndef _MACH_IO_PORTS_H
+#define _MACH_IO_PORTS_H
+
+/* i8253A PIT registers */
+#define PIT_MODE 0x43
+#define PIT_CH0 0x40
+#define PIT_CH2 0x42
+
+/* i8259A PIC registers */
+#define PIC_MASTER_CMD 0x20
+#define PIC_MASTER_IMR 0x21
+#define PIC_MASTER_ISR PIC_MASTER_CMD
+#define PIC_MASTER_POLL PIC_MASTER_ISR
+#define PIC_MASTER_OCW3 PIC_MASTER_ISR
+#define PIC_SLAVE_CMD 0xa0
+#define PIC_SLAVE_IMR 0xa1
+
+/* i8259A PIC related value */
+#define PIC_CASCADE_IR 2
+#define MASTER_ICW4_DEFAULT 0x01
+#define SLAVE_ICW4_DEFAULT 0x01
+#define PIC_ICW4_AEOI 2
+
+#endif /* !_MACH_IO_PORTS_H */
diff -urN linux/arch/i386/mach-generic/mach_reboot.h linux98/arch/i386/mach-generic/mach_reboot.h
--- linux/arch/i386/mach-generic/mach_reboot.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-generic/mach_reboot.h Mon Oct 21 09:53:44 2002
@@ -0,0 +1,30 @@
+/*
+ * arch/i386/mach-generic/mach_reboot.h
+ *
+ * Machine specific reboot functions for generic.
+ * Split out from reboot.c by Osamu Tomita <***@cinet.co.jp>
+ */
+#ifndef _MACH_REBOOT_H
+#define _MACH_REBOOT_H
+
+static inline void kb_wait(void)
+{
+ int i;
+
+ for (i = 0; i < 0x10000; i++)
+ if ((inb_p(0x64) & 0x02) == 0)
+ break;
+}
+
+static inline void mach_reboot(void)
+{
+ int i;
+ for (i = 0; i < 100; i++) {
+ kb_wait();
+ udelay(50);
+ outb(0xfe, 0x64); /* pulse reset low */
+ udelay(50);
+ }
+}
+
+#endif /* !_MACH_REBOOT_H */
diff -urN linux/arch/i386/mach-generic/mach_resources.h linux98/arch/i386/mach-generic/mach_resources.h
--- linux/arch/i386/mach-generic/mach_resources.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-generic/mach_resources.h Mon Oct 21 09:59:22 2002
@@ -0,0 +1,113 @@
+/*
+ * arch/i386/mach-generic/mach_resources.h
+ *
+ * Machine specific resource allocation for generic.
+ * Split out from setup.c by Osamu Tomita <***@cinet.co.jp>
+ */
+#ifndef _MACH_RESOURCES_H
+#define _MACH_RESOURCES_H
+
+struct resource standard_io_resources[] = {
+ { "dma1", 0x00, 0x1f, IORESOURCE_BUSY },
+ { "pic1", 0x20, 0x3f, IORESOURCE_BUSY },
+ { "timer", 0x40, 0x5f, IORESOURCE_BUSY },
+ { "keyboard", 0x60, 0x6f, IORESOURCE_BUSY },
+ { "dma page reg", 0x80, 0x8f, IORESOURCE_BUSY },
+ { "pic2", 0xa0, 0xbf, IORESOURCE_BUSY },
+ { "dma2", 0xc0, 0xdf, IORESOURCE_BUSY },
+ { "fpu", 0xf0, 0xff, IORESOURCE_BUSY }
+};
+#ifdef CONFIG_MELAN
+standard_io_resources[1] = { "pic1", 0x20, 0x21, IORESOURCE_BUSY };
+standard_io_resources[5] = { "pic2", 0xa0, 0xa1, IORESOURCE_BUSY };
+#endif
+
+#define STANDARD_IO_RESOURCES (sizeof(standard_io_resources)/sizeof(struct resource))
+
+static struct resource vram_resource = { "Video RAM area", 0xa0000, 0xbffff, IORESOURCE_BUSY };
+
+/* System ROM resources */
+#define MAXROMS 6
+static struct resource rom_resources[MAXROMS] = {
+ { "System ROM", 0xF0000, 0xFFFFF, IORESOURCE_BUSY },
+ { "Video ROM", 0xc0000, 0xc7fff, IORESOURCE_BUSY }
+};
+
+#define romsignature(x) (*(unsigned short *)(x) == 0xaa55)
+
+static inline void probe_roms(void)
+{
+ int roms = 1;
+ unsigned long base;
+ unsigned char *romstart;
+
+ request_resource(&iomem_resource, rom_resources+0);
+
+ /* Video ROM is standard at C000:0000 - C7FF:0000, check signature */
+ for (base = 0xC0000; base < 0xE0000; base += 2048) {
+ romstart = isa_bus_to_virt(base);
+ if (!romsignature(romstart))
+ continue;
+ request_resource(&iomem_resource, rom_resources + roms);
+ roms++;
+ break;
+ }
+
+ /* Extension roms at C800:0000 - DFFF:0000 */
+ for (base = 0xC8000; base < 0xE0000; base += 2048) {
+ unsigned long length;
+
+ romstart = isa_bus_to_virt(base);
+ if (!romsignature(romstart))
+ continue;
+ length = romstart[2] * 512;
+ if (length) {
+ unsigned int i;
+ unsigned char chksum;
+
+ chksum = 0;
+ for (i = 0; i < length; i++)
+ chksum += romstart[i];
+
+ /* Good checksum? */
+ if (!chksum) {
+ rom_resources[roms].start = base;
+ rom_resources[roms].end = base + length - 1;
+ rom_resources[roms].name = "Extension ROM";
+ rom_resources[roms].flags = IORESOURCE_BUSY;
+
+ request_resource(&iomem_resource, rom_resources + roms);
+ roms++;
+ if (roms >= MAXROMS)
+ return;
+ }
+ }
+ }
+
+ /* Final check for motherboard extension rom at E000:0000 */
+ base = 0xE0000;
+ romstart = isa_bus_to_virt(base);
+
+ if (romsignature(romstart)) {
+ rom_resources[roms].start = base;
+ rom_resources[roms].end = base + 65535;
+ rom_resources[roms].name = "Extension ROM";
+ rom_resources[roms].flags = IORESOURCE_BUSY;
+
+ request_resource(&iomem_resource, rom_resources + roms);
+ }
+}
+
+static inline void mach_request_resource(void)
+{
+ int i;
+
+ request_resource(&iomem_resource, &vram_resource);
+
+ /* request I/O space for devices used on all i[345]86 PCs */
+ for (i = 0; i < STANDARD_IO_RESOURCES; i++)
+ request_resource(&ioport_resource, standard_io_resources+i);
+
+}
+
+#endif /* !_MACH_RESOURCES_H */
diff -urN linux/arch/i386/mach-generic/mach_time.h linux98/arch/i386/mach-generic/mach_time.h
--- linux/arch/i386/mach-generic/mach_time.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-generic/mach_time.h Mon Oct 21 10:07:35 2002
@@ -0,0 +1,122 @@
+/*
+ * arch/i386/mach-generic/mach_time.h
+ *
+ * Machine specific set RTC function for generic.
+ * Split out from time.c by Osamu Tomita <***@cinet.co.jp>
+ */
+#ifndef _MACH_TIME_H
+#define _MACH_TIME_H
+
+#include <linux/mc146818rtc.h>
+
+/* for check timing call set_rtc_mmss() 500ms */
+/* used in arch/i386/time.c::do_timer_interrupt() */
+#define TIME1 500000
+#define TIME2 500000
+
+/*
+ * In order to set the CMOS clock precisely, set_rtc_mmss has to be
+ * called 500 ms after the second nowtime has started, because when
+ * nowtime is written into the registers of the CMOS clock, it will
+ * jump to the next second precisely 500 ms later. Check the Motorola
+ * MC146818A or Dallas DS12887 data sheet for details.
+ *
+ * BUG: This routine does not handle hour overflow properly; it just
+ * sets the minutes. Usually you'll only notice that after reboot!
+ */
+static inline int mach_set_rtc_mmss(unsigned long nowtime)
+{
+ int retval = 0;
+ int real_seconds, real_minutes, cmos_minutes;
+ unsigned char save_control, save_freq_select;
+
+ save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
+ CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
+
+ save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
+ CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
+
+ cmos_minutes = CMOS_READ(RTC_MINUTES);
+ if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+ BCD_TO_BIN(cmos_minutes);
+
+ /*
+ * since we're only adjusting minutes and seconds,
+ * don't interfere with hour overflow. This avoids
+ * messing with unknown time zones but requires your
+ * RTC not to be off by more than 15 minutes
+ */
+ real_seconds = nowtime % 60;
+ real_minutes = nowtime / 60;
+ if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
+ real_minutes += 30; /* correct for half hour time zone */
+ real_minutes %= 60;
+
+ if (abs(real_minutes - cmos_minutes) < 30) {
+ if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+ BIN_TO_BCD(real_seconds);
+ BIN_TO_BCD(real_minutes);
+ }
+ CMOS_WRITE(real_seconds,RTC_SECONDS);
+ CMOS_WRITE(real_minutes,RTC_MINUTES);
+ } else {
+ printk(KERN_WARNING
+ "set_rtc_mmss: can't update from %d to %d\n",
+ cmos_minutes, real_minutes);
+ retval = -1;
+ }
+
+ /* The following flags have to be released exactly in this order,
+ * otherwise the DS12887 (popular MC146818A clone with integrated
+ * battery and quartz) will not reset the oscillator and will not
+ * update precisely 500 ms later. You won't find this mentioned in
+ * the Dallas Semiconductor data sheets, but who believes data
+ * sheets anyway ... -- Markus Kuhn
+ */
+ CMOS_WRITE(save_control, RTC_CONTROL);
+ CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+
+ return retval;
+}
+
+static inline unsigned long mach_get_cmos_time(void)
+{
+ unsigned int year, mon, day, hour, min, sec;
+ int i;
+
+ /* The Linux interpretation of the CMOS clock register contents:
+ * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
+ * RTC registers show the second which has precisely just started.
+ * Let's hope other operating systems interpret the RTC the same way.
+ */
+ /* read RTC exactly on falling edge of update flag */
+ for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */
+ if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
+ break;
+ for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */
+ if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
+ break;
+ do { /* Isn't this overkill ? UIP above should guarantee consistency */
+ sec = CMOS_READ(RTC_SECONDS);
+ min = CMOS_READ(RTC_MINUTES);
+ hour = CMOS_READ(RTC_HOURS);
+ day = CMOS_READ(RTC_DAY_OF_MONTH);
+ mon = CMOS_READ(RTC_MONTH);
+ year = CMOS_READ(RTC_YEAR);
+ } while (sec != CMOS_READ(RTC_SECONDS));
+ if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+ {
+ BCD_TO_BIN(sec);
+ BCD_TO_BIN(min);
+ BCD_TO_BIN(hour);
+ BCD_TO_BIN(day);
+ BCD_TO_BIN(mon);
+ BCD_TO_BIN(year);
+ }
+ if ((year += 1900) < 1970)
+ year += 100;
+
+ return mktime(year, mon, day, hour, min, sec);
+}
+
+#endif /* !_MACH_TIME_H */
diff -urN linux/arch/i386/mach-generic/mach_traps.h linux98/arch/i386/mach-generic/mach_traps.h
--- linux/arch/i386/mach-generic/mach_traps.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-generic/mach_traps.h Mon Oct 21 10:17:02 2002
@@ -0,0 +1,31 @@
+/*
+ * arch/i386/mach-generic/mach_traps.h
+ *
+ * Machine specific NMI handling for generic.
+ * Split out from traps.c by Osamu Tomita <***@cinet.co.jp>
+ */
+#ifndef _MACH_TRAPS_H
+#define _MACH_TRAPS_H
+
+#define HANDLE_REASON_0X40(reason, regs) io_check_error(reason, regs)
+
+static inline void clear_mem_error(unsigned char reason)
+{
+ reason = (reason & 0xf) | 4;
+ outb(reason, 0x61);
+}
+
+static inline unsigned char get_nmi_reason(void)
+{
+ return inb(0x61);
+}
+
+static inline void reassert_nmi(void)
+{
+ outb(0x8f, 0x70);
+ inb(0x71); /* dummy */
+ outb(0x0f, 0x70);
+ inb(0x71); /* dummy */
+}
+
+#endif /* !_MACH_TRAPS_H */
diff -urN linux/arch/i386/mach-pc9800/Makefile linux98/arch/i386/mach-pc9800/Makefile
--- linux/arch/i386/mach-pc9800/Makefile Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-pc9800/Makefile Sat Sep 21 00:20:21 2002
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux kernel.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+EXTRA_CFLAGS += -I../kernel
+export-objs := +
+obj-y := setup.o
+
+include $(TOPDIR)/Rules.make
diff -urN linux/arch/i386/mach-pc9800/calibrate_tsc.h linux98/arch/i386/mach-pc9800/calibrate_tsc.h
--- linux/arch/i386/mach-pc9800/calibrate_tsc.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-pc9800/calibrate_tsc.h Mon Oct 21 11:01:05 2002
@@ -0,0 +1,73 @@
+/*
+ * arch/i386/mach-pc9800/calibrate_tsc.h
+ *
+ * Machine specific calibrate_tsc() for PC-9800.
+ * Written by Osamu Tomita <***@cinet.co.jp>
+ */
+
+/* ------ Calibrate the TSC ------- + * Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset().
+ * Too much 64-bit arithmetic here to do this cleanly in C.
+ * PC-9800:
+ * CTC cannot be used because some models (especially
+ * note-machines) may disable clock to speaker channel (#1)
+ * unless speaker is enabled. We use ARTIC instead.
+ */
+#ifndef _MACH_CALIBRATE_TSC_H
+#define _MACH_CALIBRATE_TSC_H
+
+#define CALIBRATE_LATCH (5 * 307200/HZ) /* 0.050sec * 307200Hz = 15360 */
+#define CALIBRATE_TIME (5 * 1000020/HZ)
+
+static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */
+
+static inline unsigned long calibrate_tsc(void)
+{
+
+ {
+ unsigned long startlow, starthigh;
+ unsigned long endlow, endhigh;
+ unsigned short count;
+
+ for (count = inw(0x5c); inw(0x5c) == count; )
+ ;
+ rdtsc(startlow,starthigh);
+ count = inw(0x5c);
+ while ((unsigned short)(inw(0x5c) - count) < CALIBRATE_LATCH)
+ ;
+ rdtsc(endlow,endhigh);
+
+ last_tsc_low = endlow;
+
+ /* 64-bit subtract - gcc just messes up with long longs */
+ __asm__("subl %2,%0\n\t"
+ "sbbl %3,%1"
+ :"=a" (endlow), "=d" (endhigh)
+ :"g" (startlow), "g" (starthigh),
+ "0" (endlow), "1" (endhigh));
+
+ /* Error: ECPUTOOFAST */
+ if (endhigh)
+ goto bad_ctc;
+
+ /* Error: ECPUTOOSLOW */
+ if (endlow <= CALIBRATE_TIME)
+ goto bad_ctc;
+
+ __asm__("divl %2"
+ :"=a" (endlow), "=d" (endhigh)
+ :"r" (endlow), "0" (0), "1" (CALIBRATE_TIME));
+
+ return endlow;
+ }
+
+ /*
+ * The CTC wasn't reliable: we got a hit on the very first read,
+ * or the CPU was so fast/slow that the quotient wouldn't fit in
+ * 32 bits..
+ */
+bad_ctc:
+ return 0;
+}
+
+#endif /* !_MACH_CALIBRATE_TSC_H */
diff -urN linux/arch/i386/mach-pc9800/do_timer.h linux98/arch/i386/mach-pc9800/do_timer.h
--- linux/arch/i386/mach-pc9800/do_timer.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-pc9800/do_timer.h Wed Oct 16 13:20:29 2002
@@ -0,0 +1,80 @@
+/* defines for inline arch setup functions */
+
+/**
+ * do_timer_interrupt_hook - hook into timer tick
+ * @regs: standard registers from interrupt
+ *
+ * Description:
+ * This hook is called immediately after the timer interrupt is ack'd.
+ * It's primary purpose is to allow architectures that don't possess
+ * individual per CPU clocks (like the CPU APICs supply) to broadcast the
+ * timer interrupt as a means of triggering reschedules etc.
+ **/
+
+static inline void do_timer_interrupt_hook(struct pt_regs *regs)
+{
+ do_timer(regs);
+/*
+ * In the SMP case we use the local APIC timer interrupt to do the
+ * profiling, except when we simulate SMP mode on a uniprocessor
+ * system, in that case we have to call the local interrupt handler.
+ */
+#ifndef CONFIG_X86_LOCAL_APIC
+ x86_do_profile(regs);
+#else
+ if (!using_apic_timer)
+ smp_local_timer_interrupt(regs);
+#endif
+}
+
+
+/* you can safely undefine this if you don't have the Neptune chipset */
+
+#define BUGGY_NEPTUN_TIMER
+
+/**
+ * do_timer_overflow - process a detected timer overflow condition
+ * @count: hardware timer interrupt count on overflow
+ *
+ * Description:
+ * This call is invoked when the jiffies count has not incremented but
+ * the hardware timer interrupt has. It means that a timer tick interrupt
+ * came along while the previous one was pending, thus a tick was missed
+ **/
+static inline int do_timer_overflow(int count)
+{
+ int i;
+
+ spin_lock(&i8259A_lock);
+ /*
+ * This is tricky when I/O APICs are used;
+ * see do_timer_interrupt().
+ */
+ i = inb(0x00);
+ spin_unlock(&i8259A_lock);
+
+ /* assumption about timer being IRQ0 */
+ if (i & 0x01) {
+ /*
+ * We cannot detect lost timer interrupts ... + * well, that's why we call them lost, don't we? :)
+ * [hmm, on the Pentium and Alpha we can ... sort of]
+ */
+ count -= LATCH;
+ } else {
+#ifdef BUGGY_NEPTUN_TIMER
+ /*
+ * for the Neptun bug we know that the 'latch'
+ * command doesnt latch the high and low value
+ * of the counter atomically. Thus we have to + * substract 256 from the counter + * ... funny, isnt it? :)
+ */
+
+ count -= 256;
+#else
+ printk("do_slow_gettimeoffset(): hardware timer problem?\n");
+#endif
+ }
+ return count;
+}
diff -urN linux/arch/i386/mach-pc9800/entry_arch.h linux98/arch/i386/mach-pc9800/entry_arch.h
--- linux/arch/i386/mach-pc9800/entry_arch.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-pc9800/entry_arch.h Sun Oct 20 17:42:49 2002
@@ -0,0 +1 @@
+#include "../mach-generic/entry_arch.h"
diff -urN linux/arch/i386/mach-pc9800/io_ports.h linux98/arch/i386/mach-pc9800/io_ports.h
--- linux/arch/i386/mach-pc9800/io_ports.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-pc9800/io_ports.h Mon Oct 21 11:03:30 2002
@@ -0,0 +1,30 @@
+/*
+ * arch/i386/mach-pc9800/io_ports.h
+ *
+ * Machine specific IO port address definition for PC-9800.
+ * Written by Osamu Tomita <***@cinet.co.jp>
+ */
+#ifndef _MACH_IO_PORTS_H
+#define _MACH_IO_PORTS_H
+
+/* i8253A PIT registers */
+#define PIT_MODE 0x77
+#define PIT_CH0 0x71
+#define PIT_CH2 0x75
+
+/* i8259A PIC registers */
+#define PIC_MASTER_CMD 0x00
+#define PIC_MASTER_IMR 0x02
+#define PIC_MASTER_ISR PIC_MASTER_CMD
+#define PIC_MASTER_POLL PIC_MASTER_ISR
+#define PIC_MASTER_OCW3 PIC_MASTER_ISR
+#define PIC_SLAVE_CMD 0x08
+#define PIC_SLAVE_IMR 0x0a
+
+/* i8259A PIC related values */
+#define PIC_CASCADE_IR 7
+#define MASTER_ICW4_DEFAULT 0x1d
+#define SLAVE_ICW4_DEFAULT 0x09
+#define PIC_ICW4_AEOI 0x02
+
+#endif /* !_MACH_IO_PORTS_H */
diff -urN linux/arch/i386/mach-pc9800/irq_vectors.h linux98/arch/i386/mach-pc9800/irq_vectors.h
--- linux/arch/i386/mach-pc9800/irq_vectors.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-pc9800/irq_vectors.h Sun Oct 20 17:45:10 2002
@@ -0,0 +1 @@
+#include "../mach-generic/irq_vectors.h"
diff -urN linux/arch/i386/mach-pc9800/mach_apic.h linux98/arch/i386/mach-pc9800/mach_apic.h
--- linux/arch/i386/mach-pc9800/mach_apic.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-pc9800/mach_apic.h Sun Oct 20 17:46:53 2002
@@ -0,0 +1 @@
+#include "../mach-generic/mach_apic.h"
diff -urN linux/arch/i386/mach-pc9800/mach_reboot.h linux98/arch/i386/mach-pc9800/mach_reboot.h
--- linux/arch/i386/mach-pc9800/mach_reboot.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-pc9800/mach_reboot.h Mon Oct 21 11:07:36 2002
@@ -0,0 +1,21 @@
+/*
+ * arch/i386/mach-pc9800/mach_reboot.h
+ *
+ * Machine specific reboot functions for PC-9800.
+ * Written by Osamu Tomita <***@cinet.co.jp>
+ */
+#ifndef _MACH_REBOOT_H
+#define _MACH_REBOOT_H
+
+#ifdef CMOS_WRITE
+#undef CMOS_WRITE
+#define CMOS_WRITE(a,b) do{}while(0)
+#endif
+
+static inline void mach_reboot(void)
+{
+ outb(0, 0xf0); /* signal CPU reset */
+ mdelay(1);
+}
+
+#endif /* !_MACH_REBOOT_H */
diff -urN linux/arch/i386/mach-pc9800/mach_resources.h linux98/arch/i386/mach-pc9800/mach_resources.h
--- linux/arch/i386/mach-pc9800/mach_resources.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-pc9800/mach_resources.h Sat Oct 26 17:35:19 2002
@@ -0,0 +1,192 @@
+/*
+ * arch/i386/mach-pc9800/mach_resources.h
+ *
+ * Machine specific resource allocation for PC-9800.
+ * Written by Osamu Tomita <***@cinet.co.jp>
+ */
+#ifndef _MACH_RESOURCES_H
+#define _MACH_RESOURCES_H
+
+static char str_pic1[] = "pic1";
+static char str_dma[] = "dma";
+static char str_pic2[] = "pic2";
+static char str_calender_clock[] = "calender clock";
+static char str_system[] = "system";
+static char str_nmi_control[] = "nmi control";
+static char str_kanji_rom[] = "kanji rom";
+static char str_keyboard[] = "keyboard";
+static char str_text_gdc[] = "text gdc";
+static char str_crtc[] = "crtc";
+static char str_timer[] = "timer";
+static char str_graphic_gdc[] = "graphic gdc";
+static char str_dma_ex_bank[] = "dma ex. bank";
+static char str_beep_freq[] = "beep freq.";
+static char str_mouse_pio[] = "mouse pio";
+struct resource standard_io_resources[] = {
+ { str_pic1, 0x00, 0x00, IORESOURCE_BUSY },
+ { str_dma, 0x01, 0x01, IORESOURCE_BUSY },
+ { str_pic1, 0x02, 0x02, IORESOURCE_BUSY },
+ { str_dma, 0x03, 0x03, IORESOURCE_BUSY },
+ { str_dma, 0x05, 0x05, IORESOURCE_BUSY },
+ { str_dma, 0x07, 0x07, IORESOURCE_BUSY },
+ { str_pic2, 0x08, 0x08, IORESOURCE_BUSY },
+ { str_dma, 0x09, 0x09, IORESOURCE_BUSY },
+ { str_pic2, 0x0a, 0x0a, IORESOURCE_BUSY },
+ { str_dma, 0x0b, 0x0b, IORESOURCE_BUSY },
+ { str_dma, 0x0d, 0x0d, IORESOURCE_BUSY },
+ { str_dma, 0x0f, 0x0f, IORESOURCE_BUSY },
+ { str_dma, 0x11, 0x11, IORESOURCE_BUSY },
+ { str_dma, 0x13, 0x13, IORESOURCE_BUSY },
+ { str_dma, 0x15, 0x15, IORESOURCE_BUSY },
+ { str_dma, 0x17, 0x17, IORESOURCE_BUSY },
+ { str_dma, 0x19, 0x19, IORESOURCE_BUSY },
+ { str_dma, 0x1b, 0x1b, IORESOURCE_BUSY },
+ { str_dma, 0x1d, 0x1d, IORESOURCE_BUSY },
+ { str_dma, 0x1f, 0x1f, IORESOURCE_BUSY },
+ { str_calender_clock, 0x20, 0x20, 0 },
+ { str_dma, 0x21, 0x21, IORESOURCE_BUSY },
+ { str_calender_clock, 0x22, 0x22, 0 },
+ { str_dma, 0x23, 0x23, IORESOURCE_BUSY },
+ { str_dma, 0x25, 0x25, IORESOURCE_BUSY },
+ { str_dma, 0x27, 0x27, IORESOURCE_BUSY },
+ { str_dma, 0x29, 0x29, IORESOURCE_BUSY },
+ { str_dma, 0x2b, 0x2b, IORESOURCE_BUSY },
+ { str_dma, 0x2d, 0x2d, IORESOURCE_BUSY },
+ { str_system, 0x31, 0x31, IORESOURCE_BUSY },
+ { str_system, 0x33, 0x33, IORESOURCE_BUSY },
+ { str_system, 0x35, 0x35, IORESOURCE_BUSY },
+ { str_system, 0x37, 0x37, IORESOURCE_BUSY },
+ { str_nmi_control, 0x50, 0x50, IORESOURCE_BUSY },
+ { str_nmi_control, 0x52, 0x52, IORESOURCE_BUSY },
+ { "time stamp", 0x5c, 0x5f, IORESOURCE_BUSY },
+ { str_kanji_rom, 0xa1, 0xa1, IORESOURCE_BUSY },
+ { str_kanji_rom, 0xa3, 0xa3, IORESOURCE_BUSY },
+ { str_kanji_rom, 0xa5, 0xa5, IORESOURCE_BUSY },
+ { str_kanji_rom, 0xa7, 0xa7, IORESOURCE_BUSY },
+ { str_kanji_rom, 0xa9, 0xa9, IORESOURCE_BUSY },
+ { str_keyboard, 0x41, 0x41, IORESOURCE_BUSY },
+ { str_keyboard, 0x43, 0x43, IORESOURCE_BUSY },
+ { str_text_gdc, 0x60, 0x60, IORESOURCE_BUSY },
+ { str_text_gdc, 0x62, 0x62, IORESOURCE_BUSY },
+ { str_text_gdc, 0x64, 0x64, IORESOURCE_BUSY },
+ { str_text_gdc, 0x66, 0x66, IORESOURCE_BUSY },
+ { str_text_gdc, 0x68, 0x68, IORESOURCE_BUSY },
+ { str_text_gdc, 0x6a, 0x6a, IORESOURCE_BUSY },
+ { str_text_gdc, 0x6c, 0x6c, IORESOURCE_BUSY },
+ { str_text_gdc, 0x6e, 0x6e, IORESOURCE_BUSY },
+ { str_crtc, 0x70, 0x70, IORESOURCE_BUSY },
+ { str_crtc, 0x72, 0x72, IORESOURCE_BUSY },
+ { str_crtc, 0x74, 0x74, IORESOURCE_BUSY },
+ { str_crtc, 0x74, 0x74, IORESOURCE_BUSY },
+ { str_crtc, 0x76, 0x76, IORESOURCE_BUSY },
+ { str_crtc, 0x78, 0x78, IORESOURCE_BUSY },
+ { str_crtc, 0x7a, 0x7a, IORESOURCE_BUSY },
+ { str_timer, 0x71, 0x71, IORESOURCE_BUSY },
+ { str_timer, 0x73, 0x73, IORESOURCE_BUSY },
+ { str_timer, 0x75, 0x75, IORESOURCE_BUSY },
+ { str_timer, 0x77, 0x77, IORESOURCE_BUSY },
+ { str_graphic_gdc, 0xa0, 0xa0, IORESOURCE_BUSY },
+ { str_graphic_gdc, 0xa2, 0xa2, IORESOURCE_BUSY },
+ { str_graphic_gdc, 0xa4, 0xa4, IORESOURCE_BUSY },
+ { str_graphic_gdc, 0xa6, 0xa6, IORESOURCE_BUSY },
+ { "cpu", 0xf0, 0xf7, IORESOURCE_BUSY },
+ { "fpu", 0xf8, 0xff, IORESOURCE_BUSY },
+ { str_dma_ex_bank, 0x0e05, 0x0e05, 0 },
+ { str_dma_ex_bank, 0x0e07, 0x0e07, 0 },
+ { str_dma_ex_bank, 0x0e09, 0x0e09, 0 },
+ { str_dma_ex_bank, 0x0e0b, 0x0e0b, 0 },
+ { str_beep_freq, 0x3fd9, 0x3fd9, IORESOURCE_BUSY },
+ { str_beep_freq, 0x3fdb, 0x3fdb, IORESOURCE_BUSY },
+ { str_beep_freq, 0x3fdd, 0x3fdd, IORESOURCE_BUSY },
+ { str_beep_freq, 0x3fdf, 0x3fdf, IORESOURCE_BUSY },
+ /* All PC-9800 have (exactly) one mouse interface. */
+ { str_mouse_pio, 0x7fd9, 0x7fd9, 0 },
+ { str_mouse_pio, 0x7fdb, 0x7fdb, 0 },
+ { str_mouse_pio, 0x7fdd, 0x7fdd, 0 },
+ { str_mouse_pio, 0x7fdf, 0x7fdf, 0 },
+ { "mouse timer", 0xbfdb, 0xbfdb, 0 },
+ { "mouse irq", 0x98d7, 0x98d7, 0 },
+};
+
+#define STANDARD_IO_RESOURCES (sizeof(standard_io_resources)/sizeof(struct resource))
+
+static struct resource tvram_resource = { "Text VRAM/CG window", 0xa0000, 0xa4fff, IORESOURCE_BUSY };
+static struct resource gvram_brg_resource = { "Graphic VRAM (B/R/G)", 0xa8000, 0xbffff, IORESOURCE_BUSY };
+static struct resource gvram_e_resource = { "Graphic VRAM (E)", 0xe0000, 0xe7fff, IORESOURCE_BUSY };
+
+/* System ROM resources */
+#define MAXROMS 6
+static struct resource rom_resources[MAXROMS] = {
+ { "System ROM", 0xe8000, 0xfffff, IORESOURCE_BUSY }
+};
+
+static inline void probe_roms(void)
+{
+ int roms = 1;
+ int i;
+ __u8 *xrom_id;
+
+ request_resource(&iomem_resource, rom_resources+0);
+
+ xrom_id = (__u8 *) isa_bus_to_virt(PC9800SCA_XROM_ID + 0x10);
+
+ for (i = 0; i < 16; i++) {
+ if (xrom_id[i] & 0x80) {
+ int j;
+
+ for (j = i + 1; j < 16 && (xrom_id[j] & 0x80); j++)
+ ;
+ rom_resources[roms].start = 0x0d0000 + i * 0x001000;
+ rom_resources[roms].end = 0x0d0000 + j * 0x001000 - 1;
+ rom_resources[roms].name = "Extension ROM";
+ rom_resources[roms].flags = IORESOURCE_BUSY;
+
+ request_resource(&iomem_resource,
+ rom_resources + roms);
+ if (++roms >= MAXROMS)
+ return;
+ }
+ }
+}
+
+static inline void mach_request_resource(void)
+{
+ int i;
+
+ if (PC9800_HIGHRESO_P()) {
+ tvram_resource.start = 0xe0000;
+ tvram_resource.end = 0xe4fff;
+ gvram_brg_resource.name = "Graphic VRAM";
+ gvram_brg_resource.start = 0xc0000;
+ gvram_brg_resource.end = 0xdffff;
+ }
+
+ request_resource(&iomem_resource, &tvram_resource);
+ request_resource(&iomem_resource, &gvram_brg_resource);
+ if (!PC9800_HIGHRESO_P())
+ request_resource(&iomem_resource, &gvram_e_resource);
+
+ for (i = 0; i < STANDARD_IO_RESOURCES; i++)
+ request_resource(&ioport_resource, standard_io_resources + i);
+
+ if (PC9800_HIGHRESO_P() || PC9800_9821_P()) {
+ static char graphics[] = "graphics";
+ static struct resource graphics_resources[] = {
+ { graphics, 0x9a0, 0x9a0, 0 },
+ { graphics, 0x9a2, 0x9a2, 0 },
+ { graphics, 0x9a4, 0x9a4, 0 },
+ { graphics, 0x9a6, 0x9a6, 0 },
+ { graphics, 0x9a8, 0x9a8, 0 },
+ { graphics, 0x9aa, 0x9aa, 0 },
+ { graphics, 0x9ac, 0x9ac, 0 },
+ { graphics, 0x9ae, 0x9ae, 0 },
+ };
+
+#define GRAPHICS_RESOURCES (sizeof(graphics_resources)/sizeof(struct resource))
+
+ for (i = 0; i < GRAPHICS_RESOURCES; i++)
+ request_resource(&ioport_resource, graphics_resources + i);
+ }
+}
+
+#endif /* !_MACH_RESOURCES_H */
diff -urN linux/arch/i386/mach-pc9800/mach_time.h linux98/arch/i386/mach-pc9800/mach_time.h
--- linux/arch/i386/mach-pc9800/mach_time.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-pc9800/mach_time.h Mon Oct 21 11:23:06 2002
@@ -0,0 +1,136 @@
+/*
+ * arch/i386/mach-pc9800/mach_time.h
+ *
+ * Machine specific set RTC function for PC-9800.
+ * Written by Osamu Tomita <***@cinet.co.jp>
+ */
+#ifndef _MACH_TIME_H
+#define _MACH_TIME_H
+
+#include <linux/upd4990a.h>
+
+/* for check timing call set_rtc_mmss() */
+/* used in arch/i386/time.c::do_timer_interrupt() */
+/*
+ * Because PC-9800's RTC (NEC uPD4990A) does not allow setting
+ * time partially, we always have to read-modify-write the
+ * entire time (including year) so that set_rtc_mmss() will
+ * take quite much time to execute. You may want to relax
+ * RTC resetting interval (currently ~11 minuts)...
+ */
+#define TIME1 1000000
+#define TIME2 0
+
+static inline int mach_set_rtc_mmss(unsigned long nowtime)
+{
+ int retval = 0;
+ int real_seconds, real_minutes, cmos_minutes;
+ struct upd4990a_raw_data data;
+
+ upd4990a_get_time(&data, 1);
+ cmos_minutes = (data.min >> 4) * 10 + (data.min & 0xf);
+
+ /*
+ * since we're only adjusting minutes and seconds,
+ * don't interfere with hour overflow. This avoids
+ * messing with unknown time zones but requires your
+ * RTC not to be off by more than 15 minutes
+ */
+ real_seconds = nowtime % 60;
+ real_minutes = nowtime / 60;
+ if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1)
+ real_minutes += 30; /* correct for half hour time zone */
+ real_minutes %= 60;
+
+ if (abs(real_minutes - cmos_minutes) < 30) {
+ u8 temp_seconds = (real_seconds / 10) * 16 + real_seconds % 10;
+ u8 temp_minutes = (real_minutes / 10) * 16 + real_minutes % 10;
+
+ if (data.sec != temp_seconds || data.min != temp_minutes) {
+ data.sec = temp_seconds;
+ data.min = temp_minutes;
+ upd4990a_set_time(&data, 1);
+ }
+ } else {
+ printk(KERN_WARNING
+ "set_rtc_mmss: can't update from %d to %d\n",
+ cmos_minutes, real_minutes);
+ retval = -1;
+ }
+
+ /* uPD4990A users' manual says we should issue Register Hold
+ * command after reading time, or future Time Read command
+ * may not work. When we have set the time, this also starts
+ * the clock.
+ */
+ upd4990a_serial_command(UPD4990A_REGISTER_HOLD);
+
+ return retval;
+}
+
+#define RTC_SANITY_CHECK
+
+static inline unsigned long mach_get_cmos_time(void)
+{
+ int i;
+ u8 prev, cur;
+ unsigned int year;
+#ifdef RTC_SANITY_CHECK
+ int retry_count;
+#endif
+
+ struct upd4990a_raw_data data;
+
+#ifdef RTC_SANITY_CHECK
+ retry_count = 0;
+ retry:
+#endif
+ /* Connect uPD4990A's DATA OUT pin to its 1Hz reference clock. */
+ upd4990a_serial_command(UPD4990A_REGISTER_HOLD);
+
+ /* Catch rising edge of reference clock. */
+ prev = ~UPD4990A_READ_DATA();
+ for (i = 0; i < 1800000; i++) { /* may take up to 1 second... */
+ __asm__ ("outb %%al,%0" : : "N" (0x5f)); /* 0.6usec delay */
+ cur = UPD4990A_READ_DATA();
+ if (!(prev & cur & 1))
+ break;
+ prev = ~cur;
+ }
+
+ upd4990a_get_time(&data, 0);
+
+#ifdef RTC_SANITY_CHECK
+# define BCD_VALID_P(x, hi) (((x) & 0x0f) <= 9 && (x) <= 0x ## hi)
+# define DATA ((const unsigned char *) &data)
+
+ if (!BCD_VALID_P(data.sec, 59) ||
+ !BCD_VALID_P(data.min, 59) ||
+ !BCD_VALID_P(data.hour, 23) ||
+ data.mday == 0 || !BCD_VALID_P(data.mday, 31) ||
+ data.wday > 6 ||
+ data.mon < 1 || 12 < data.mon ||
+ !BCD_VALID_P(data.year, 99)) {
+ printk(KERN_ERR "RTC clock data is invalid! "
+ "(%02X %02X %02X %02X %02X %02X) - ",
+ DATA[0], DATA[1], DATA[2], DATA[3], DATA[4], DATA[5]);
+ if (++retry_count < 3) {
+ printk("retrying (%d)\n", retry_count);
+ goto retry;
+ }
+ printk("giving up, continuing\n");
+ }
+
+# undef BCD_VALID_P
+# undef DATA
+#endif /* RTC_SANITY_CHECK */
+
+#define CVT(x) (((x) & 0xF) + ((x) >> 4) * 10)
+ if ((year = CVT(data.year) + 1900) < 1995)
+ year += 100;
+ return mktime(year, data.mon, CVT(data.mday),
+ CVT(data.hour), CVT(data.min), CVT(data.sec));
+#undef CVT
+}
+
+#endif /* !_MACH_TIME_H */
diff -urN linux/arch/i386/mach-pc9800/mach_traps.h linux98/arch/i386/mach-pc9800/mach_traps.h
--- linux/arch/i386/mach-pc9800/mach_traps.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-pc9800/mach_traps.h Mon Oct 21 11:26:12 2002
@@ -0,0 +1,29 @@
+/*
+ * arch/i386/mach-pc9800/mach_traps.h
+ *
+ * Machine specific NMI handling for PC-9800.
+ * Written by Osamu Tomita <***@cinet.co.jp>
+ */
+#ifndef _MACH_TRAPS_H
+#define _MACH_TRAPS_H
+
+#define HANDLE_REASON_0X40(reason, regs) mem_parity_error(reason, regs)
+
+static inline void clear_mem_error(unsigned char reason)
+{
+ outb(0x08, 0x37);
+ outb(0x09, 0x37);
+}
+
+static inline unsigned char get_nmi_reason(void)
+{
+ return inb(0x33) << 5;
+}
+
+static inline void reassert_nmi(void)
+{
+ outb(0x09, 0x50); /* disable NMI once */
+ outb(0x09, 0x52); /* re-enable it */
+}
+
+#endif /* !_MACH_TRAPS_H */
diff -urN linux/arch/i386/mach-pc9800/setup.c linux98/arch/i386/mach-pc9800/setup.c
--- linux/arch/i386/mach-pc9800/setup.c Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-pc9800/setup.c Sat Oct 26 17:08:45 2002
@@ -0,0 +1,117 @@
+/*
+ * Machine specific setup for generic
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <asm/setup.h>
+#include <asm/arch_hooks.h>
+
+struct sys_desc_table_struct {
+ unsigned short length;
+ unsigned char table[0];
+};
+
+/* Indicates PC-9800 architecture No:0 Yes:1 */
+extern int pc98;
+
+/**
+ * pre_intr_init_hook - initialisation prior to setting up interrupt vectors
+ *
+ * Description:
+ * Perform any necessary interrupt initialisation prior to setting up
+ * the "ordinary" interrupt call gates. For legacy reasons, the ISA
+ * interrupts should be initialised here if the machine emulates a PC
+ * in any way.
+ **/
+void __init pre_intr_init_hook(void)
+{
+ init_ISA_irqs();
+}
+
+/*
+ * IRQ7 is cascade interrupt to second interrupt controller
+ */
+static struct irqaction irq7 = { no_action, 0, 0, "cascade", NULL, NULL};
+
+/**
+ * intr_init_hook - post gate setup interrupt initialisation
+ *
+ * Description:
+ * Fill in any interrupts that may have been left out by the general
+ * init_IRQ() routine. interrupts having to do with the machine rather
+ * than the devices on the I/O bus (like APIC interrupts in intel MP
+ * systems) are started here.
+ **/
+void __init intr_init_hook(void)
+{
+#ifdef CONFIG_X86_LOCAL_APIC
+ apic_intr_init();
+#endif
+
+ setup_irq(7, &irq7);
+}
+
+/**
+ * pre_setup_arch_hook - hook called prior to any setup_arch() execution
+ *
+ * Description:
+ * generally used to activate any machine specific identification
+ * routines that may be needed before setup_arch() runs. On VISWS
+ * this is used to get the board revision and type.
+ **/
+void __init pre_setup_arch_hook(void)
+{
+ SYS_DESC_TABLE.length = 0;
+ MCA_bus = 0;
+ pc98 = 1;
+}
+
+/**
+ * trap_init_hook - initialise system specific traps
+ *
+ * Description:
+ * Called as the final act of trap_init(). Used in VISWS to initialise
+ * the various board specific APIC traps.
+ **/
+void __init trap_init_hook(void)
+{
+}
+
+static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL};
+
+/**
+ * time_init_hook - do any specific initialisations for the system timer.
+ *
+ * Description:
+ * Must plug the system timer interrupt source at HZ into the IRQ listed
+ * in irq_vectors.h:TIMER_IRQ
+ **/
+void __init time_init_hook(void)
+{
+ setup_irq(0, &irq0);
+}
+
+#ifdef CONFIG_MCA
+/**
+ * mca_nmi_hook - hook into MCA specific NMI chain
+ *
+ * Description:
+ * The MCA (Microchannel Arcitecture) has an NMI chain for NMI sources
+ * along the MCA bus. Use this to hook into that chain if you will need
+ * it.
+ **/
+void __init mca_nmi_hook(void)
+{
+ /* If I recall correctly, there's a whole bunch of other things that
+ * we can do to check for NMI problems, but that's all I know about
+ * at the moment.
+ */
+
+ printk("NMI generated from unknown source!\n");
+}
+#endif
diff -urN linux/arch/i386/mach-pc9800/setup_arch_post.h linux98/arch/i386/mach-pc9800/setup_arch_post.h
--- linux/arch/i386/mach-pc9800/setup_arch_post.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-pc9800/setup_arch_post.h Sat Sep 21 23:00:26 2002
@@ -0,0 +1,29 @@
+/**
+ * machine_specific_memory_setup - Hook for machine specific memory setup.
+ *
+ * Description:
+ * This is included late in kernel/setup.c so that it can make
+ * use of all of the static functions.
+ **/
+
+static inline char * __init machine_specific_memory_setup(void)
+{
+ char *who;
+ unsigned long low_mem_size, lower_high, higher_high;
+
+
+ who = "BIOS (common area)";
+
+ low_mem_size = ((*(unsigned char *)__va(PC9800SCA_BIOS_FLAG) & 7) + 1) << 17;
+ add_memory_region(0, low_mem_size, 1);
+ lower_high = (__u32) *(__u8 *) bus_to_virt(PC9800SCA_EXPMMSZ) << 17;
+ higher_high = (__u32) *(__u16 *) bus_to_virt(PC9800SCA_MMSZ16M) << 20;
+ if (lower_high != 0x00f00000UL) {
+ add_memory_region(HIGH_MEMORY, lower_high, 1);
+ add_memory_region(0x01000000UL, higher_high, 1);
+ }
+ else
+ add_memory_region(HIGH_MEMORY, lower_high + higher_high, 1);
+
+ return who;
+}
diff -urN linux/arch/i386/mach-pc9800/setup_arch_pre.h linux98/arch/i386/mach-pc9800/setup_arch_pre.h
--- linux/arch/i386/mach-pc9800/setup_arch_pre.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-pc9800/setup_arch_pre.h Sun Sep 22 07:58:29 2002
@@ -0,0 +1,36 @@
+/* Hook to call BIOS initialisation function */
+
+/* no action for generic */
+
+#define ARCH_SETUP arch_setup_pc9800();
+
+#include <linux/timex.h>
+#include <asm/io.h>
+#include <asm/pc9800.h>
+#include <asm/pc9800_sca.h>
+
+int CLOCK_TICK_RATE;
+unsigned long tick_usec; /* ACTHZ period (usec) */
+unsigned long tick_nsec; /* USER_HZ period (nsec) */
+unsigned char pc9800_misc_flags;
+/* (bit 0) 1:High Address Video ram exists 0:otherwise */
+
+#ifdef CONFIG_SMP
+#define MPC_TABLE_SIZE 512
+#define MPC_TABLE ((char *) (PARAM+0x400))
+char mpc_table[MPC_TABLE_SIZE];
+#endif
+
+static inline void arch_setup_pc9800(void)
+{
+ CLOCK_TICK_RATE = PC9800_8MHz_P() ? 1996800 : 2457600;
+ printk(KERN_DEBUG "CLOCK_TICK_RATE = %d\n", CLOCK_TICK_RATE);
+ tick_usec = TICK_USEC; /* ACTHZ period (usec) */
+ tick_nsec = TICK_NSEC(TICK_USEC); /* USER_HZ period (nsec) */
+
+ pc9800_misc_flags = PC9800_MISC_FLAGS;
+#ifdef CONFIG_SMP
+ if ((*(u32 *)(MPC_TABLE)) == 0x504d4350)
+ memcpy(mpc_table, MPC_TABLE, *(u16 *)(MPC_TABLE + 4));
+#endif /* CONFIG_SMP */
+}
diff -urN linux/arch/i386/mach-pc9800/smpboot_hooks.h linux98/arch/i386/mach-pc9800/smpboot_hooks.h
--- linux/arch/i386/mach-pc9800/smpboot_hooks.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-pc9800/smpboot_hooks.h Sun Sep 22 06:56:46 2002
@@ -0,0 +1,33 @@
+/* two abstractions specific to kernel/smpboot.c, mainly to cater to visws
+ * which needs to alter them. */
+
+static inline void smpboot_clear_io_apic_irqs(void)
+{
+ io_apic_irqs = 0;
+}
+
+static inline void smpboot_setup_warm_reset_vector(void)
+{
+ /*
+ * Install writable page 0 entry to set BIOS data area.
+ */
+ local_flush_tlb();
+
+ /*
+ * Paranoid: Set warm reset code and vector here back
+ * to default values.
+ */
+ outb(0x0f, 0x37); /* SHUT0 = 1 */
+
+ *((volatile long *) phys_to_virt(0x404)) = 0;
+}
+
+static inline void smpboot_setup_io_apic(void)
+{
+ /*
+ * Here we can be sure that there is an IO-APIC in the system. Let's
+ * go and set it up:
+ */
+ if (!skip_ioapic_setup && nr_ioapics)
+ setup_IO_APIC();
+}
diff -urN linux/arch/i386/mach-summit/calibrate_tsc.h linux98/arch/i386/mach-summit/calibrate_tsc.h
--- linux/arch/i386/mach-summit/calibrate_tsc.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-summit/calibrate_tsc.h Mon Oct 21 02:46:34 2002
@@ -0,0 +1 @@
+#include "../mach-generic/calibrate_tsc.h"
diff -urN linux/arch/i386/mach-summit/io_ports.h linux98/arch/i386/mach-summit/io_ports.h
--- linux/arch/i386/mach-summit/io_ports.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-summit/io_ports.h Sun Oct 20 18:08:54 2002
@@ -0,0 +1 @@
+#include "../mach-generic/io_ports.h"
diff -urN linux/arch/i386/mach-summit/mach_reboot.h linux98/arch/i386/mach-summit/mach_reboot.h
--- linux/arch/i386/mach-summit/mach_reboot.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-summit/mach_reboot.h Sun Oct 20 18:10:25 2002
@@ -0,0 +1 @@
+#include "../mach-generic/mach_reboot.h"
diff -urN linux/arch/i386/mach-summit/mach_resources.h linux98/arch/i386/mach-summit/mach_resources.h
--- linux/arch/i386/mach-summit/mach_resources.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-summit/mach_resources.h Sun Oct 20 18:11:27 2002
@@ -0,0 +1 @@
+#include "../mach-generic/mach_resources.h"
diff -urN linux/arch/i386/mach-summit/mach_time.h linux98/arch/i386/mach-summit/mach_time.h
--- linux/arch/i386/mach-summit/mach_time.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-summit/mach_time.h Sun Oct 20 20:00:44 2002
@@ -0,0 +1 @@
+#include "../mach-generic/mach_time.h"
diff -urN linux/arch/i386/mach-summit/mach_traps.h linux98/arch/i386/mach-summit/mach_traps.h
--- linux/arch/i386/mach-summit/mach_traps.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-summit/mach_traps.h Mon Oct 21 02:48:48 2002
@@ -0,0 +1 @@
+#include "../mach-generic/mach_traps.h"
diff -urN linux/arch/i386/mach-visws/calibrate_tsc.h linux98/arch/i386/mach-visws/calibrate_tsc.h
--- linux/arch/i386/mach-visws/calibrate_tsc.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-visws/calibrate_tsc.h Mon Oct 21 02:46:34 2002
@@ -0,0 +1 @@
+#include "../mach-generic/calibrate_tsc.h"
diff -urN linux/arch/i386/mach-visws/io_ports.h linux98/arch/i386/mach-visws/io_ports.h
--- linux/arch/i386/mach-visws/io_ports.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-visws/io_ports.h Sun Oct 20 18:08:54 2002
@@ -0,0 +1 @@
+#include "../mach-generic/io_ports.h"
diff -urN linux/arch/i386/mach-visws/mach_reboot.h linux98/arch/i386/mach-visws/mach_reboot.h
--- linux/arch/i386/mach-visws/mach_reboot.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-visws/mach_reboot.h Sun Oct 20 18:10:25 2002
@@ -0,0 +1 @@
+#include "../mach-generic/mach_reboot.h"
diff -urN linux/arch/i386/mach-visws/mach_resources.h linux98/arch/i386/mach-visws/mach_resources.h
--- linux/arch/i386/mach-visws/mach_resources.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-visws/mach_resources.h Sun Oct 20 18:11:27 2002
@@ -0,0 +1 @@
+#include "../mach-generic/mach_resources.h"
diff -urN linux/arch/i386/mach-visws/mach_time.h linux98/arch/i386/mach-visws/mach_time.h
--- linux/arch/i386/mach-visws/mach_time.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-visws/mach_time.h Sun Oct 20 20:00:44 2002
@@ -0,0 +1 @@
+#include "../mach-generic/mach_time.h"
diff -urN linux/arch/i386/mach-visws/mach_traps.h linux98/arch/i386/mach-visws/mach_traps.h
--- linux/arch/i386/mach-visws/mach_traps.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-visws/mach_traps.h Mon Oct 21 02:48:48 2002
@@ -0,0 +1 @@
+#include "../mach-generic/mach_traps.h"
diff -urN linux/arch/i386/mach-voyager/calibrate_tsc.h linux98/arch/i386/mach-voyager/calibrate_tsc.h
--- linux/arch/i386/mach-voyager/calibrate_tsc.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-voyager/calibrate_tsc.h Mon Oct 21 02:46:34 2002
@@ -0,0 +1 @@
+#include "../mach-generic/calibrate_tsc.h"
diff -urN linux/arch/i386/mach-voyager/io_ports.h linux98/arch/i386/mach-voyager/io_ports.h
--- linux/arch/i386/mach-voyager/io_ports.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-voyager/io_ports.h Sun Oct 20 18:08:54 2002
@@ -0,0 +1 @@
+#include "../mach-generic/io_ports.h"
diff -urN linux/arch/i386/mach-voyager/mach_reboot.h linux98/arch/i386/mach-voyager/mach_reboot.h
--- linux/arch/i386/mach-voyager/mach_reboot.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-voyager/mach_reboot.h Sun Oct 20 18:10:25 2002
@@ -0,0 +1 @@
+#include "../mach-generic/mach_reboot.h"
diff -urN linux/arch/i386/mach-voyager/mach_resources.h linux98/arch/i386/mach-voyager/mach_resources.h
--- linux/arch/i386/mach-voyager/mach_resources.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-voyager/mach_resources.h Sun Oct 20 18:11:27 2002
@@ -0,0 +1 @@
+#include "../mach-generic/mach_resources.h"
diff -urN linux/arch/i386/mach-voyager/mach_time.h linux98/arch/i386/mach-voyager/mach_time.h
--- linux/arch/i386/mach-voyager/mach_time.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-voyager/mach_time.h Sun Oct 20 20:00:44 2002
@@ -0,0 +1 @@
+#include "../mach-generic/mach_time.h"
diff -urN linux/arch/i386/mach-voyager/mach_traps.h linux98/arch/i386/mach-voyager/mach_traps.h
--- linux/arch/i386/mach-voyager/mach_traps.h Thu Jan 1 09:00:00 1970
+++ linux98/arch/i386/mach-voyager/mach_traps.h Mon Oct 21 02:48:48 2002
@@ -0,0 +1 @@
+#include "../mach-generic/mach_traps.h"
Osamu Tomita
2002-11-02 18:07:22 UTC
Permalink
This is a part 12/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
network card modules
- add support for PC-9800 legacy bus network cards.

diffstat:
drivers/net/3c509.c | 33 +++
drivers/net/8390.h | 3 drivers/net/Kconfig | 55 ++++-
drivers/net/Makefile | 4 drivers/net/Makefile.lib | 1 drivers/net/Space.c | 2 drivers/net/at1700.c | 115 +++++++++-
drivers/net/ne.c | 517 +++++++++++++++++++++++++++++++++++++++++++++--
drivers/net/ne2k_cbus.h | 467 ++++++++++++++++++++++++++++++++++++++++++
9 files changed, 1165 insertions(+), 32 deletions(-)

patch:
diff -Nru linux-2.5.42/drivers/net/3c509.c linux98-2.5.42/drivers/net/3c509.c
--- linux-2.5.42/drivers/net/3c509.c Wed Oct 16 12:27:09 2002
+++ linux98-2.5.42/drivers/net/3c509.c Sat Oct 19 10:49:31 2002
@@ -52,6 +52,10 @@
v1.19 16Oct2002 Zwane Mwaikambo <***@linuxpower.ca>
- Additional ethtool features
*/
+/*
+ FIXES for PC-9800:
+ Shu Iwanaga: 3c569B(PC-9801 C-bus) support
+*/
#define DRV_NAME "3c509"
#define DRV_VERSION "1.19"
@@ -168,7 +172,11 @@
struct pm_dev *pmdev;
#endif
};
+#ifdef CONFIG_PC9800
+static int id_port __initdata = 0x71d0;
+#else
static int id_port __initdata = 0x110; /* Start with 0x110 to avoid new sound cards.*/
+#endif
static struct net_device *el3_root_dev;
static ushort id_read_eeprom(int index);
@@ -389,6 +397,7 @@
no_pnp:
#endif /* __ISAPNP__ */
+#ifndef CONFIG_PC9800 /* Error for NEC C-bus */
/* Select an open I/O location at 0x1*0 to do contention select. */
for ( ; id_port < 0x200; id_port += 0x10) {
if (check_region(id_port, 1))
@@ -403,6 +412,7 @@
printk(" WARNING: No I/O port available for 3c509 activation.\n");
return -ENODEV;
}
+#endif /* CONFIG_PC9800 */
/* Next check for all ISA bus boards by sending the ID sequence to the
ID_PORT. We find cards past the first by setting the 'current_tag'
on cards as they are found. Cards with their tag set will not
@@ -458,7 +468,11 @@
{
unsigned int iobase = id_read_eeprom(8);
if_port = iobase >> 14;
+#ifndef CONFIG_PC9800
ioaddr = 0x200 + ((iobase & 0x1f) << 4);
+#else
+ ioaddr = 0x40d0 + ((iobase & 0x1f) << 8);
+#endif
}
irq = id_read_eeprom(9) >> 12;
@@ -482,7 +496,15 @@
outb(0xd0 + ++current_tag, id_port);
/* Activate the adaptor at the EEPROM location. */
+#ifndef CONFIG_PC9800
outb((ioaddr >> 4) | 0xe0, id_port);
+#else
+ outb((ioaddr >> 8) | 0xe0, id_port);
+ if (irq == 7)
+ irq = 6;
+ else if (irq == 15)
+ irq = 13;
+#endif
EL3WINDOW(0);
if (inw(ioaddr) != 0x6d50) {
@@ -1253,7 +1275,18 @@
outw(0x0001, ioaddr + 4);
/* Set the IRQ line. */
+#ifndef CONFIG_PC9800
outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
+#else
+ {
+ int irq = dev->irq;
+ if (irq == 6)
+ irq = 7;
+ else if (irq == 13)
+ irq = 15;
+ outw((irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
+ }
+#endif
/* Set the station address in window 2 each time opened. */
EL3WINDOW(2);
diff -Nru linux-2.5.42/drivers/net/8390.h linux98-2.5.42/drivers/net/8390.h
--- linux-2.5.42/drivers/net/8390.h Sat Oct 12 13:22:14 2002
+++ linux98-2.5.42/drivers/net/8390.h Tue Oct 15 23:03:22 2002
@@ -123,7 +123,8 @@
#define inb_p(port) in_8(port)
#define outb_p(val,port) out_8(port,val)
-#elif defined(CONFIG_ARM_ETHERH) || defined(CONFIG_ARM_ETHERH_MODULE)
+#elif defined(CONFIG_ARM_ETHERH) || defined(CONFIG_ARM_ETHERH_MODULE) || \
+ defined(CONFIG_NET_CBUS)
#define EI_SHIFT(x) (ei_local->reg_offset[x])
#else
#define EI_SHIFT(x) (x)
diff -Nru linux-2.5.42/drivers/net/Kconfig linux98-2.5.42/drivers/net/Kconfig
--- linux-2.5.42/drivers/net/Kconfig Thu Oct 31 13:23:20 2002
+++ linux98-2.5.42/drivers/net/Kconfig Sat Nov 2 15:28:46 2002
@@ -456,7 +456,7 @@
as <file:Documentation/networking/net-modules.txt>.
config EL3
- tristate "3c509/3c529 (MCA)/3c579 \"EtherLink III\" support"
+ tristate "3c509/3c529 (MCA)/3c569B (98)/3c579 \"EtherLink III\" support"
depends on NET_VENDOR_3COM && (ISA || EISA || MCA)
---help---
If you have a network (Ethernet) card belonging to the 3Com
@@ -705,7 +705,7 @@
source "drivers/net/tulip/Kconfig"
config AT1700
- tristate "AT1700/1720 support (EXPERIMENTAL)"
+ tristate "AT1700/1720/RE1000Plus(C-Bus) support (EXPERIMENTAL)"
depends on NET_ETHERNET && (ISA || MCA) && EXPERIMENTAL
---help---
If you have a network (Ethernet) card of this type, say Y and read
@@ -751,7 +751,7 @@
config NET_ISA
bool "Other ISA cards"
- depends on NET_ETHERNET && ISA
+ depends on NET_ETHERNET && ISA && !PC9800
---help---
If your network (Ethernet) card hasn't been mentioned yet and its
bus system (that's the way the cards talks to the other components
@@ -949,6 +949,55 @@
the Ethernet-HOWTO, available from
<http://www.linuxdoc.org/docs.html#howto>.
+config NET_CBUS
+ bool "NEC PC-9800 C-bus cards"
+ depends on NET_ETHERNET && ISA && PC9800
+ ---help---
+ If your network (Ethernet) card hasn't been mentioned yet and its
+ bus system (that's the way the cards talks to the other components
+ of your computer) is NEC PC-9800 C-Bus, say Y.
+
+config NE2K_CBUS
+ tristate "Most NE2000-based Ethernet support"
+ depends on NET_CBUS
+
+config NE2K_CBUS_EGY98
+ bool "Melco EGY-98 support"
+ depends on NE2K_CBUS
+
+config NE2K_CBUS_LGY98
+ bool "Melco LGY-98 support"
+ depends on NE2K_CBUS
+
+config NE2K_CBUS_ICM
+ bool "ICM IF-27xxET support"
+ depends on NE2K_CBUS
+
+config CONFIG_NE2K_CBUS_IOLA98
+ bool "I-O DATA LA-98 support"
+ depends on NE2K_CBUS
+
+config CONFIG_NE2K_CBUS_CNET98EL
+ bool "Contec C-NET(98)E/L support"
+ depends on NE2K_CBUS
+
+config CONFIG_NE2K_CBUS_CNET98EL_IO_BASE
+ hex "C-NET(98)E/L I/O base address (0xaaed or 0x55ed)"
+ depends on CONFIG_NE2K_CBUS_CNET98EL
+ default "0xaaed"
+
+config CONFIG_NE2K_CBUS_ATLA98
+ bool "Allied Telesis LA-98 Support"
+ depends on NE2K_CBUS
+
+config CONFIG_NE2K_CBUS_BDN
+ bool "ELECOM Laneed LD-BDN[123]A Support"
+ depends on NE2K_CBUS
+
+config CONFIG_NE2K_CBUS_NEC108
+ bool "NEC PC-9801-108 Support"
+ depends on NE2K_CBUS
+
config SKMC
tristate "SKnet MCA support"
depends on NET_ETHERNET && MCA
diff -Nru linux-2.5.42/drivers/net/Makefile linux98-2.5.42/drivers/net/Makefile
--- linux-2.5.42/drivers/net/Makefile Sat Oct 12 13:21:36 2002
+++ linux98-2.5.42/drivers/net/Makefile Tue Oct 15 23:03:22 2002
@@ -87,7 +87,11 @@
obj-$(CONFIG_ARM_ETHERH) += 8390.o
obj-$(CONFIG_WD80x3) += wd.o 8390.o
obj-$(CONFIG_EL2) += 3c503.o 8390.o
+ifneq ($(CONFIG_PC9800),y)
obj-$(CONFIG_NE2000) += ne.o 8390.o
+else
+obj-$(CONFIG_NE2K_CBUS) += ne.o 8390.o
+endif
obj-$(CONFIG_NE2_MCA) += ne2.o 8390.o
obj-$(CONFIG_HPLAN) += hp.o 8390.o
obj-$(CONFIG_HPLAN_PLUS) += hp-plus.o 8390.o
diff -Nru linux-2.5.42/drivers/net/Makefile.lib linux98-2.5.42/drivers/net/Makefile.lib
--- linux-2.5.42/drivers/net/Makefile.lib Sat Oct 12 13:22:18 2002
+++ linux98-2.5.42/drivers/net/Makefile.lib Tue Oct 15 23:03:22 2002
@@ -19,6 +19,7 @@
obj-$(CONFIG_MACMACE) += crc32.o
obj-$(CONFIG_MIPS_AU1000_ENET) += crc32.o
obj-$(CONFIG_NATSEMI) += crc32.o
+obj-$(CONFIG_NE2K_CBUS) += crc32.o
obj-$(CONFIG_PCMCIA_FMVJ18X) += crc32.o
obj-$(CONFIG_PCMCIA_SMC91C92) += crc32.o
obj-$(CONFIG_PCMCIA_XIRTULIP) += crc32.o
diff -Nru linux-2.5.42/drivers/net/Space.c linux98-2.5.42/drivers/net/Space.c
--- linux-2.5.42/drivers/net/Space.c Thu Oct 31 13:23:20 2002
+++ linux98-2.5.42/drivers/net/Space.c Thu Oct 31 15:17:29 2002
@@ -242,7 +242,7 @@
#ifdef CONFIG_E2100 /* Cabletron E21xx series. */
{e2100_probe, 0},
#endif
-#ifdef CONFIG_NE2000 /* ISA (use ne2k-pci for PCI cards) */
+#if defined(CONFIG_NE2000) || defined(CONFIG_NE2K_CBUS) /* ISA & PC-9800 CBUS (use ne2k-pci for PCI cards) */
{ne_probe, 0},
#endif
#ifdef CONFIG_LANCE /* ISA/VLB (use pcnet32 for PCI cards) */
diff -Nru linux-2.5.42/drivers/net/at1700.c linux98-2.5.42/drivers/net/at1700.c
--- linux-2.5.42/drivers/net/at1700.c Sat Oct 19 13:01:49 2002
+++ linux98-2.5.42/drivers/net/at1700.c Sat Oct 19 21:58:24 2002
@@ -34,6 +34,10 @@
only is it difficult to detect, it also moves around in I/O space in
response to inb()s from other device probes!
*/
+/*
+ 99/03/03 Allied Telesis RE1000 Plus support by T.Hagawa
+ 99/12/30 port to 2.3.35 by K.Takai
+*/
#include <linux/config.h>
#include <linux/module.h>
@@ -79,10 +83,17 @@
* ISA
*/
+#ifndef CONFIG_PC9800
static int at1700_probe_list[] __initdata = {
0x260, 0x280, 0x2a0, 0x240, 0x340, 0x320, 0x380, 0x300, 0
};
+#else /* CONFIG_PC9800 */
+static int at1700_probe_list[] __initdata = {
+ 0x1d6, 0x1d8, 0x1da, 0x1d4, 0xd4, 0xd2, 0xd8, 0xd0, 0
+};
+
+#endif /* CONFIG_PC9800 */
/*
* MCA
*/
@@ -125,6 +136,7 @@
/* Offsets from the base address. */
+#ifndef CONFIG_PC9800
#define STATUS 0
#define TX_STATUS 0
#define RX_STATUS 1
@@ -139,6 +151,7 @@
#define TX_START 10
#define COL16CNTL 11 /* Controll Reg for 16 collisions */
#define MODE13 13
+#define RX_CTRL 14
/* Configuration registers only on the '865A/B chips. */
#define EEPROM_Ctrl 16
#define EEPROM_Data 17
@@ -147,8 +160,39 @@
#define IOCONFIG 18 /* Either read the jumper, or move the I/O. */
#define IOCONFIG1 19
#define SAPROM 20 /* The station address PROM, if no EEPROM. */
+#define MODE24 24
#define RESET 31 /* Write to reset some parts of the chip. */
#define AT1700_IO_EXTENT 32
+#define PORT_OFFSET(o) (o)
+#else /* CONFIG_PC9800 */
+#define STATUS (0x0000)
+#define TX_STATUS (0x0000)
+#define RX_STATUS (0x0001)
+#define TX_INTR (0x0200)/* Bit-mapped interrupt enable registers. */
+#define RX_INTR (0x0201)
+#define TX_MODE (0x0400)
+#define RX_MODE (0x0401)
+#define CONFIG_0 (0x0600)/* Misc. configuration settings. */
+#define CONFIG_1 (0x0601)
+/* Run-time register bank 2 definitions. */
+#define DATAPORT (0x0800)/* Word-wide DMA or programmed-I/O dataport. */
+#define TX_START (0x0a00)
+#define COL16CNTL (0x0a01)/* Controll Reg for 16 collisions */
+#define MODE13 (0x0c01)
+#define RX_CTRL (0x0e00)
+/* Configuration registers only on the '865A/B chips. */
+#define EEPROM_Ctrl (0x1000)
+#define EEPROM_Data (0x1200)
+#define CARDSTATUS 16 /* FMV-18x Card Status */
+#define CARDSTATUS1 17 /* FMV-18x Card Status */
+#define IOCONFIG (0x1400)/* Either read the jumper, or move the I/O. */
+#define IOCONFIG1 (0x1600)
+#define SAPROM 20 /* The station address PROM, if no EEPROM. */
+#define MODE24 (0x1800)/* The station address PROM, if no EEPROM. */
+#define RESET (0x1e01)/* Write to reset some parts of the chip. */
+#define PORT_OFFSET(o) ({ int _o_ = (o); (_o_ & ~1) * 0x100 + (_o_ & 1); })
+#endif /* CONFIG_PC9800 */
+
#define TX_TIMEOUT 10
@@ -228,8 +272,20 @@
int slot, ret = -ENODEV;
struct net_local *lp;

+#ifndef CONFIG_PC9800
if (!request_region(ioaddr, AT1700_IO_EXTENT, dev->name))
return -EBUSY;
+#else
+ for (i = 0; i < 0x2000; i += 0x0200) {
+ if (!request_region(ioaddr + i, 2, dev->name)) {
+ while (i > 0) {
+ i -= 0x0200;
+ release_region(ioaddr + i, 2);
+ }
+ return -EBUSY;
+ }
+ }
+#endif
/* Resetting the chip doesn't reset the ISA interface, so don't bother.
That means we have to be careful with the register values we probe for.
@@ -320,10 +376,17 @@
/* Reset the internal state machines. */
outb(0, ioaddr + RESET);
- if (is_at1700)
+ if (is_at1700) {
+#ifndef CONFIG_PC9800
irq = at1700_irqmap[(read_eeprom(ioaddr, 12)&0x04)
| (read_eeprom(ioaddr, 0)>>14)];
- else {
+#else
+ {
+ char re1000plus_irqmap[4] = {3, 5, 6, 12};
+ irq = re1000plus_irqmap[inb(ioaddr + IOCONFIG1) >> 6];
+ }
+#endif
+ } else {
/* Check PnP mode for FMV-183/184/183A/184A. */
/* This PnP routine is very poor. IO and IRQ should be known. */
if (inb(ioaddr + CARDSTATUS1) & 0x20) {
@@ -395,18 +458,22 @@
/* Set the station address in bank zero. */
outb(0x00, ioaddr + CONFIG_1);
for (i = 0; i < 6; i++)
- outb(dev->dev_addr[i], ioaddr + 8 + i);
+ outb(dev->dev_addr[i], ioaddr + PORT_OFFSET(8 + i));
/* Switch to bank 1 and set the multicast table to accept none. */
outb(0x04, ioaddr + CONFIG_1);
for (i = 0; i < 8; i++)
- outb(0x00, ioaddr + 8 + i);
+ outb(0x00, ioaddr + PORT_OFFSET(8 + i));
/* Switch to bank 2 */
/* Lock our I/O address, and set manual processing mode for 16 collisions. */
outb(0x08, ioaddr + CONFIG_1);
+#ifndef CONFIG_PC9800
outb(dev->if_port, ioaddr + MODE13);
+#else
+ outb(0, ioaddr + MODE13);
+#endif
outb(0x00, ioaddr + COL16CNTL);
if (net_debug)
@@ -450,7 +517,12 @@
kfree(dev->priv);
dev->priv = NULL;
err_out:
+#ifndef CONFIG_PC9800
release_region(ioaddr, AT1700_IO_EXTENT);
+#else
+ for (i = 0; i < 0x2000; i += 0x0200)
+ release_region(ioaddr + i, 2);
+#endif
return ret;
}
@@ -462,7 +534,11 @@
#define EE_DATA_READ 0x80 /* EEPROM chip data out, in reg. 17. */
/* Delay between EEPROM clock transitions. */
+#ifndef CONFIG_PC9800
#define eeprom_delay() do { } while (0)
+#else
+#define eeprom_delay() __asm__ ("out%B0 %%al,%0" :: "N"(0x5f))
+#endif
/* The EEPROM commands include the alway-set leading bit. */
#define EE_WRITE_CMD (5 << 6)
@@ -545,12 +621,12 @@
inw (ioaddr + STATUS), inb (ioaddr + TX_STATUS) & 0x80
? "IRQ conflict" : "network cable problem");
printk ("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n",
- dev->name, inw (ioaddr + 0), inw (ioaddr + 2), inw (ioaddr + 4),
- inw (ioaddr + 6), inw (ioaddr + 8), inw (ioaddr + 10),
- inw (ioaddr + 12), inw (ioaddr + 14));
+ dev->name, inw(ioaddr + TX_STATUS), inw(ioaddr + TX_INTR), inw(ioaddr + TX_MODE),
+ inw(ioaddr + CONFIG_0), inw(ioaddr + DATAPORT), inw(ioaddr + TX_START),
+ inw(ioaddr + MODE13 - 1), inw(ioaddr + RX_CTRL));
lp->stats.tx_errors++;
/* ToDo: We should try to restart the adaptor... */
- outw (0xffff, ioaddr + 24);
+ outw(0xffff, ioaddr + MODE24);
outw (0xffff, ioaddr + TX_STATUS);
outb (0x5a, ioaddr + CONFIG_0);
outb (0xe8, ioaddr + CONFIG_1);
@@ -696,7 +772,7 @@
dev->name, inb(ioaddr + RX_MODE), status);
#ifndef final_version
if (status == 0) {
- outb(0x05, ioaddr + 14);
+ outb(0x05, ioaddr + RX_CTRL);
break;
}
#endif
@@ -716,7 +792,7 @@
dev->name, pkt_len);
/* Prime the FIFO and then flush the packet. */
inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
- outb(0x05, ioaddr + 14);
+ outb(0x05, ioaddr + RX_CTRL);
lp->stats.rx_errors++;
break;
}
@@ -726,7 +802,7 @@
dev->name, pkt_len);
/* Prime the FIFO and then flush the packet. */
inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
- outb(0x05, ioaddr + 14);
+ outb(0x05, ioaddr + RX_CTRL);
lp->stats.rx_dropped++;
break;
}
@@ -753,7 +829,7 @@
if ((inb(ioaddr + RX_MODE) & 0x40) == 0x40)
break;
inw(ioaddr + DATAPORT); /* dummy status read */
- outb(0x05, ioaddr + 14);
+ outb(0x05, ioaddr + RX_CTRL);
}
if (net_debug > 5)
@@ -843,7 +919,7 @@
/* Switch to bank 1 and set the multicast table. */
outw((saved_bank & ~0x0C00) | 0x0480, ioaddr + CONFIG_0);
for (i = 0; i < 8; i++)
- outb(mc_filter[i], ioaddr + 8 + i);
+ outb(mc_filter[i], ioaddr + PORT_OFFSET(8 + i));
memcpy(lp->mc_filter, mc_filter, sizeof(mc_filter));
outw(saved_bank, ioaddr + CONFIG_0);
}
@@ -853,7 +929,12 @@
#ifdef MODULE
static struct net_device dev_at1700;
+#ifndef CONFIG_PC9800
static int io = 0x260;
+#else
+static int io = 0xd0;
+#endif
+
static int irq;
MODULE_PARM(io, "i");
@@ -893,7 +974,15 @@
/* If we don't do this, we can't re-insmod it later. */
free_irq(dev_at1700.irq, NULL);
+#ifndef CONFIG_PC9800
release_region(dev_at1700.base_addr, AT1700_IO_EXTENT);
+#else
+ {
+ int i;
+ for (i = 0; i < 0x2000; i += 0x200)
+ release_region(dev_at1700.base_addr + i, 2);
+ }
+#endif
}
#endif /* MODULE */
MODULE_LICENSE("GPL");
diff -Nru linux-2.5.42/drivers/net/ne.c linux98-2.5.42/drivers/net/ne.c
--- linux-2.5.42/drivers/net/ne.c Sat Oct 12 13:22:07 2002
+++ linux98-2.5.42/drivers/net/ne.c Tue Oct 15 23:03:22 2002
@@ -54,6 +54,26 @@
#include <linux/etherdevice.h>
#include "8390.h"
+/* backword compatibility for kernel version 2.1.57 */
+#include <linux/version.h>
+#ifndef LINUX_VERSION_CODE
+#warning LINUX_VERSION_CODE is no defined!
+#endif
+#if LINUX_VERSION_CODE < 0x20200
+#ifdef CONFIG_PCI
+#undef CONFIG_PCI
+#endif
+#ifdef CONFIG_PC98
+#define CONFIG_PC9800
+#endif
+#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);})
+#endif
+
+#ifdef CONFIG_NET_CBUS
+#undef ei_debug
+#define ei_debug 9
+#endif /* CONFIG_NET_CBUS */
+
/* Some defines that people can play with if so inclined. */
/* Do we support clones that don't adhere to 14,15 of the SAprom ? */
@@ -69,11 +89,13 @@
/* #define PACKETBUF_MEMSIZE 0x40 */
/* A zero-terminated list of I/O addresses to be probed at boot. */
+#ifndef CONFIG_NET_CBUS
#ifndef MODULE
static unsigned int netcard_portlist[] __initdata = {
0x300, 0x280, 0x320, 0x340, 0x360, 0x380, 0
};
#endif
+#endif
static struct isapnp_device_id isapnp_clone_list[] __initdata = {
{ ISAPNP_CARD_ID('A','X','E',0x2011),
@@ -94,6 +116,7 @@
/* A list of bad clones that we none-the-less recognize. */
static struct { const char *name8, *name16; unsigned char SAprefix[4];}
bad_clone_list[] __initdata = {
+#ifndef CONFIG_NET_CBUS
{"DE100", "DE200", {0x00, 0xDE, 0x01,}},
{"DE120", "DE220", {0x00, 0x80, 0xc8,}},
{"DFI1000", "DFI2000", {'D', 'F', 'I',}}, /* Original, eh? */
@@ -108,26 +131,54 @@
{"PCM-4823", "PCM-4823", {0x00, 0xc0, 0x6c}}, /* Broken Advantech MoBo */
{"REALTEK", "RTL8019", {0x00, 0x00, 0xe8}}, /* no-name with Realtek chip */
{"LCS-8834", "LCS-8836", {0x04, 0x04, 0x37}}, /* ShinyNet (SET) */
+#else /* CONFIG_NET_CBUS */
+ {"LA/T-98?", "LA/T-98", {0x00,0xa0,0xb0}}, /* I/O Data */
+ {"EGY-98?", "EGY-98", {0x00,0x40,0x26}}, /* Melco EGY98 */
+ {"ICM?", "ICM-27xx-ET", {0x00,0x80,0xc8}}, /* ICM IF-27xx-ET */
+ {"CNET-98/EL?", "CNET(98)E/L", {0x00,0x80,0x4C}}, /* Contec CNET-98/EL */
+#endif
{0,}
};
#endif
/* ---- No user-serviceable parts below ---- */
+#define NE_SHIFT(x) EI_SHIFT(x)
#define NE_BASE (dev->base_addr)
-#define NE_CMD 0x00
-#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
-#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
+#define NE_CMD NE_SHIFT(0x00)
+#define NE_DATAPORT NE_SHIFT(0x10) /* NatSemi-defined port window offset. */
+#ifndef CONFIG_NET_CBUS
+#define NE_RESET NE_SHIFT(0x1f) /* Issue a read to reset, a write to clear. */
+#else
+#define NE_RESET NE_SHIFT(0x11) /* Issue a read to reset, a write to clear. */
+#endif
+
#define NE_IO_EXTENT 0x20
#define NE1SM_START_PG 0x20 /* First page of TX buffer */
#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */
#define NESM_START_PG 0x40 /* First page of TX buffer */
#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+#ifdef CONFIG_NE2K_CBUS_CNET98EL
+#ifndef CONFIG_NE2K_CBUS_CNET98EL_IO_BASE
+#warning CONFIG_NE2K_CBUS_CNET98EL_IO_BASE is not defined(config error?)
+#warning use 0xaaed as default
+#define CONFIG_NE2K_CBUS_CNET98EL_IO_BASE 0xaaed /* or 0x55ed */
+#endif
+#define CNET98EL_START_PG 0x00
+#define CNET98EL_STOP_PG 0x40
+#endif
+
+#ifdef CONFIG_NET_CBUS
+#include "ne2k_cbus.h"
+#endif
int ne_probe(struct net_device *dev);
static int ne_probe1(struct net_device *dev, int ioaddr);
static int ne_probe_isapnp(struct net_device *dev);
+#ifdef CONFIG_NET_CBUS
+static int ne_probe_cbus(struct net_device *dev, const struct ne2k_cbus_hwinfo *hw, int ioaddr);
+#endif
static int ne_open(struct net_device *dev);
static int ne_close(struct net_device *dev);
@@ -162,6 +213,8 @@
E2010 starts at 0x100 and ends at 0x4000.
E2010-x starts at 0x100 and ends at 0xffff. */
+#ifndef CONFIG_NET_CBUS
+
int __init ne_probe(struct net_device *dev)
{
unsigned int base_addr = dev->base_addr;
@@ -190,6 +243,95 @@
return -ENODEV;
}
+#else /* CONFIG_NET_CBUS */
+
+int __init ne_probe(struct net_device *dev)
+{
+ unsigned int base_addr = dev->base_addr;
+
+ SET_MODULE_OWNER(dev);
+
+ if (ei_debug > 2)
+ printk(KERN_DEBUG "ne_probe(): entered.\n");
+
+ /* If CONFIG_NET_CBUS,
+ we need dev->priv->reg_offset BEFORE to probe */
+ if (ne2k_cbus_init(dev) != 0) {
+ return -ENOMEM;
+ }
+
+ /* First check any supplied i/o locations. User knows best. <cough> */
+ if (base_addr > 0) {
+ int result;
+ const struct ne2k_cbus_hwinfo *hw = ne2k_cbus_get_hwinfo((int)(dev->mem_start & NE2K_CBUS_HARDWARE_TYPE_MASK));
+
+ if (ei_debug > 2)
+ printk(KERN_DEBUG "ne_probe(): call ne_probe_cbus(base_addr=0x%x)\n", base_addr);
+
+ result = ne_probe_cbus(dev, hw, base_addr);
+ if (result != 0)
+ ne2k_cbus_destroy(dev);
+
+ return result;
+ }
+
+ if (ei_debug > 2)
+ printk(KERN_DEBUG "ne_probe(): base_addr is not specified.\n");
+
+#ifndef MODULE
+ /* Last resort. The semi-risky C-Bus auto-probe. */
+ if (ei_debug > 2)
+ printk(KERN_DEBUG "ne_probe(): auto-probe start.\n");
+
+ {
+ const struct ne2k_cbus_hwinfo *hw = ne2k_cbus_get_hwinfo((int)(dev->mem_start & NE2K_CBUS_HARDWARE_TYPE_MASK));
+
+ if (hw && hw->hwtype) {
+ const unsigned short *plist;
+ for (plist = hw->portlist; *plist; plist++) {
+ const struct ne2k_cbus_region *rlist;
+ for (rlist = hw->regionlist; rlist->range; rlist++) {
+ if (check_region(*plist+rlist->start, rlist->range))
+ break;
+ }
+ if (rlist->range) {
+ /* check_region() failed */ + continue; /* try next base port */
+ }
+ /* check_region() succeeded */
+ if (ne_probe_cbus(dev,hw,*plist) == 0)
+ return 0;
+ }
+ }else{
+ for (hw = &ne2k_cbus_hwinfo_list[0]; hw->hwtype; hw++) {
+ const unsigned short *plist;
+ for(plist=hw->portlist; *plist; plist++){
+ const struct ne2k_cbus_region *rlist;
+
+ for (rlist = hw->regionlist; rlist->range; rlist++) {
+ if (check_region(*plist+rlist->start, rlist->range))
+ break;
+ }
+ if (rlist->range) {
+ /* check_region() failed */ + continue; /* try next base port */
+ }
+ /* check_region() succeeded */
+ if (ne_probe_cbus(dev,hw,*plist) == 0)
+ return 0;
+ }
+ }
+ }
+ }
+#endif
+
+ ne2k_cbus_destroy(dev);
+
+ return -ENODEV;
+}
+
+#endif /* CONFIG_NET_CBUS */
+
static int __init ne_probe_isapnp(struct net_device *dev)
{
int i;
@@ -231,42 +373,127 @@
return -ENODEV;
}
+#ifdef CONFIG_NET_CBUS
+static int __init ne_probe_cbus(struct net_device *dev, const struct ne2k_cbus_hwinfo *hw, int ioaddr)
+{
+ if (ei_debug > 2)
+ printk(KERN_DEBUG "ne_probe_cbus(): entered. (called from %p)\n",
+ __builtin_return_address(0));
+
+ if (hw && hw->hwtype) {
+ ne2k_cbus_set_hwtype(dev, hw, ioaddr);
+ return ne_probe1(dev, ioaddr);
+ } else {
+ /* auto detect */
+
+ printk(KERN_DEBUG "ne_probe_cbus(): try to determine hardware types.\n");
+ for (hw = &ne2k_cbus_hwinfo_list[0]; hw->hwtype; hw++) {
+ ne2k_cbus_set_hwtype(dev, hw, ioaddr);
+ if (ne_probe1(dev, ioaddr)==0)
+ return 0;
+ }
+ }
+ return ENODEV;
+}
+#endif /* CONFIG_NET_CBUS */
+
static int __init ne_probe1(struct net_device *dev, int ioaddr)
{
int i;
unsigned char SA_prom[32];
+#ifndef CONFIG_NET_CBUS /* if CONFIG_NET_CBUS, wordlength is always 2! */
int wordlength = 2;
+#endif
const char *name = NULL;
int start_page, stop_page;
+#ifndef CONFIG_NET_CBUS
int neX000, ctron, copam, bad_card;
+#else
+ int neX000, bad_card;
+#endif
int reg0, ret;
static unsigned version_printed;
+#ifdef CONFIG_NET_CBUS
+ const struct ne2k_cbus_hwinfo *hw = ne2k_cbus_get_hwinfo((int)(dev->mem_start & NE2K_CBUS_HARDWARE_TYPE_MASK));
+ struct ei_device *ei_local = (struct ei_device *)(dev->priv);
+#endif
+#ifdef CONFIG_NET_CBUS
+ if (ei_debug > 2) {
+ printk(KERN_DEBUG "ne_probe1(): entered\n"
+ "ioaddr=0x%x, hardware_type = %d(%s)\n"
+ "ei_local->reg_offset = \n"
+ "{ 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x, \n"
+ " 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x, 0x%04x, \n"
+ " 0x%04x, 0x%04x }\n",
+ ioaddr, hw->hwtype, hw->hwident,
+ ei_local->reg_offset[0], ei_local->reg_offset[1],
+ ei_local->reg_offset[2], ei_local->reg_offset[3],
+ ei_local->reg_offset[4], ei_local->reg_offset[5],
+ ei_local->reg_offset[6], ei_local->reg_offset[7],
+ ei_local->reg_offset[8], ei_local->reg_offset[9],
+ ei_local->reg_offset[10], ei_local->reg_offset[11],
+ ei_local->reg_offset[12], ei_local->reg_offset[13],
+ ei_local->reg_offset[14], ei_local->reg_offset[15],
+ ei_local->reg_offset[16], ei_local->reg_offset[17]);
+ }
+#endif
+
+#ifdef CONFIG_NE2K_CBUS_CNET98EL
+ if (hw->hwtype == NE2K_CBUS_HARDWARE_TYPE_CNET98EL) {
+ outb_p(0, CONFIG_NE2K_CBUS_CNET98EL_IO_BASE);
+ /* udelay(5000); */
+ outb_p(1, CONFIG_NE2K_CBUS_CNET98EL_IO_BASE);
+ /* udelay(5000); */
+ outb_p((ioaddr & 0xf000) >> 8 | 0x08 | 0x01, CONFIG_NE2K_CBUS_CNET98EL_IO_BASE + 2);
+ /* udelay(5000); */
+ }
+#endif
+
+#ifndef CONFIG_NET_CBUS
if (!request_region(ioaddr, NE_IO_EXTENT, dev->name))
return -EBUSY;
+#else /* CONFIG_NET_CBUS */
+ {
+ const struct ne2k_cbus_region *rlist;
+ for (rlist = hw->regionlist; rlist->range; rlist++) {
+ if (!request_region(ioaddr + rlist->start,
+ rlist->range, dev->name))
+ return -EBUSY;
+ }
+ }
+#endif /* !CONFIG_NET_CBUS */
- reg0 = inb_p(ioaddr);
+ reg0 = inb_p(ioaddr + NE_SHIFT(0));
if (reg0 == 0xFF) {
ret = -ENODEV;
goto err_out;
}
/* Do a preliminary verification that we have a 8390. */
+#ifdef CONFIG_NE2K_CBUS_CNET98EL
+ if (hw->hwtype != NE2K_CBUS_HARDWARE_TYPE_CNET98EL)
+#endif
{
int regd;
outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
- regd = inb_p(ioaddr + 0x0d);
- outb_p(0xff, ioaddr + 0x0d);
+ regd = inb_p(ioaddr + NE_SHIFT(0x0d));
+ outb_p(0xff, ioaddr + NE_SHIFT(0x0d));
outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
if (inb_p(ioaddr + EN0_COUNTER0) != 0) {
outb_p(reg0, ioaddr);
- outb_p(regd, ioaddr + 0x0d); /* Restore the old values. */
+ outb_p(regd, ioaddr + NE_SHIFT(0x0d)); /* Restore the old values. */
ret = -ENODEV;
goto err_out;
}
}
+#ifdef CONFIG_NET_CBUS
+ if (ei_debug > 2)
+ printk(KERN_DEBUG "ne_probe1(): 8390 verification passed.\n");
+#endif
+
if (ei_debug && version_printed++ == 0)
printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2);
@@ -285,6 +512,11 @@
{
unsigned long reset_start_time = jiffies;
+#ifdef CONFIG_NET_CBUS
+ /* derived from CNET98EL-patch for bad clones */
+ outb_p(E8390_NODMA | E8390_STOP, ioaddr+E8390_CMD);
+#endif
+
/* DON'T change these to inb_p/outb_p or reset will fail on clones. */
outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
@@ -303,15 +535,130 @@
outb_p(0xff, ioaddr + EN0_ISR); /* Ack all intr. */
}
+#ifdef CONFIG_NE2K_CBUS_ICM
+#if 0 /* obsoleted */
+ if (hw->hwtype == NE2K_CBUS_HARDWARE_TYPE_ICM) {
+ static const char pat[32] ="AbcdeFghijKlmnoPqrstUvwxyZ789012";
+ char buf[sizeof(pat)];
+ int maxwait = 200;
+
+ if (ei_debug > 2) {
+ printk(" [ICM-specific initialize...");
+ }
+
+ inb(ioaddr + EN0_ISR);
+ outb_p(E8390_RXOFF, ioaddr+EN0_RXCR);
+ outb_p(0x1| 0x40 | 0x8, ioaddr+EN0_DCFG); /* ENDCFG_WTS|ENDCFG_FT1|ENDCFG_LS */
+ outb_p(16384 / 256, ioaddr + EN0_STARTPG);
+ outb_p(32768 / 256, ioaddr + EN0_STOPPG);
+ ne2k_cbus_writemem(dev, ioaddr, 16384, pat, sizeof(pat));
+ while ((inb(ioaddr+EN0_ISR) & ENISR_RDC) != ENISR_RDC
+ && --maxwait)
+ ;
+ if (ei_debug > 2) {
+ printk("write pat...");
+ }
+ ne2k_cbus_readmem(dev, ioaddr, 16384, buf, sizeof(pat));
+ if (ei_debug>2) {
+ printk("read pat...");
+ }
+ if (memcmp(pat, buf, sizeof(pat))) {
+ if (ei_debug > 2) {
+ printk("compare failed.)");
+ }
+ printk(" memory failure\n");
+ return ENODEV;
+ }
+ if (ei_debug > 2) {
+ printk("compare ok...");
+ }
+ ne2k_cbus_readmem(dev, ioaddr, 0, SA_prom, 32);
+ outb(0xff, ioaddr+EN0_ISR);
+ printk("done)");
+ }
+ else
+#endif
+#endif /* CONFIG_NE2K_CBUS_ICM */
+#ifdef CONFIG_NE2K_CBUS_CNET98EL
+ if (hw->hwtype == NE2K_CBUS_HARDWARE_TYPE_CNET98EL) {
+ static const char pat[32] ="AbcdeFghijKlmnoPqrstUvwxyZ789012";
+ char buf[32];
+ int maxwait = 200;
+
+ if (ei_debug > 2) {
+ printk(" [CNET98EL-specific initialize...");
+ }
+ outb_p(E8390_NODMA | E8390_STOP, ioaddr+E8390_CMD); /* 0x20|0x1 */
+ i=inb(ioaddr);
+ if ((i & ~0x2) != (0x20 | 0x01))
+ return ENODEV;
+ if ((inb(ioaddr + 0x7) & 0x80) != 0x80)
+ return ENODEV;
+ outb_p(E8390_RXOFF, ioaddr+EN0_RXCR); /* out(ioaddr+0xc, 0x20) */
+ /* outb_p(ENDCFG_WTS|ENDCFG_FT1|ENDCFG_LS, ioaddr+EN0_DCFG); */
+ outb_p(ENDCFG_WTS|0x48, ioaddr+EN0_DCFG); /* 0x49 */
+ outb_p(CNET98EL_START_PG, ioaddr+EN0_STARTPG);
+ outb_p(CNET98EL_STOP_PG, ioaddr+EN0_STOPPG);
+ if (ei_debug > 2) {
+ printk("memory check");
+ }
+ for (i = 0; i < 65536; i += 1024) {
+ if (ei_debug > 2) {
+ printk(" %04x",i);
+ }
+ ne2k_cbus_writemem(dev,ioaddr, i, pat, 32);
+ while (((inb(ioaddr + EN0_ISR) & ENISR_RDC) != ENISR_RDC) && --maxwait)
+ ;
+ ne2k_cbus_readmem(dev, ioaddr, i, buf, 32);
+ if (memcmp(pat, buf, 32)) {
+ if (ei_debug > 2) {
+ printk(" failed.");
+ }
+ break;
+ }
+ }
+ if (i != 16384) {
+ if (ei_debug > 2) {
+ printk("] ");
+ }
+ printk("memory failure at %x\n", i);
+ return ENODEV;
+ }
+ if (ei_debug > 2) {
+ printk(" good...");
+ }
+ if (!dev->irq) {
+ if (ei_debug > 2) {
+ printk("] ");
+ }
+ printk("IRQ must be specified for C-NET(98)E/L. probe failed.\n");
+ return ENODEV;
+ }
+ outb((dev->irq>5) ? (dev->irq&4):(dev->irq>>1), ioaddr + (0x2 | 0x400));
+ outb(0x7e, ioaddr + (0x4 | 0x400));
+ ne2k_cbus_readmem(dev, ioaddr, 16384, SA_prom, 32);
+ outb(0xff, ioaddr + EN0_ISR);
+ if (ei_debug > 2) {
+ printk("done]");
+ }
+ } else
+#endif /* CONFIG_NE2K_CBUS_CNET98EL */
/* Read the 16 bytes of station address PROM.
We must first initialize registers, similar to NS8390_init(eifdev, 0).
We can't reliably read the SAPROM address without this.
(I learned the hard way!). */
{
- struct {unsigned char value, offset; } program_seq[] =
+ struct {unsigned char value; unsigned short offset;} program_seq[] = {
{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+#ifndef CONFIG_NET_CBUS
{0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */
+#else
+ /* NEC PC-9800: some board can only handle word-wide access? */
+ {0x48 | ENDCFG_WTS, EN0_DCFG}, /* Set word-wide (0x48) access. */
+ {16384 / 256, EN0_STARTPG},
+ {32768 / 256, EN0_STOPPG},
+#endif
{0x00, EN0_RCNTLO}, /* Clear the count regs. */
{0x00, EN0_RCNTHI},
{0x00, EN0_IMR}, /* Mask completion irq. */
@@ -325,17 +672,44 @@
{E8390_RREAD+E8390_START, E8390_CMD},
};
+#ifdef CONFIG_NET_CBUS
+ if (ei_debug > 2) {
+ printk(" [outb_p");
+ }
+#endif
+
for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
+#ifdef CONFIG_NET_CBUS
+ {
+ if (ei_debug > 2)
+ printk("(0x%x,0x%x)",program_seq[i].value, ioaddr + program_seq[i].offset);
+#endif
outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
-
- }
+#ifdef CONFIG_NET_CBUS
+ }
+ if (ei_debug > 2)
+ printk("]");
+#endif
+#ifndef CONFIG_NET_CBUS
for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
SA_prom[i] = inb(ioaddr + NE_DATAPORT);
SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
if (SA_prom[i] != SA_prom[i+1])
wordlength = 1;
}
+#else
+ insw(ioaddr + NE_DATAPORT, SA_prom, 32 >> 1);
+#endif
+ }
+
+ if (ei_debug > 2) {
+ printk("[SA_prom[]={ ");
+ for (i = 0; i < 32; i++) printk("%02x ", SA_prom[i]);
+ printk("}]");
+ }
+
+#ifndef CONFIG_NET_CBUS
if (wordlength == 2)
{
for (i = 0; i < 16; i++)
@@ -348,8 +722,24 @@
start_page = NE1SM_START_PG;
stop_page = NE1SM_STOP_PG;
}
+#else
+ for (i = 0; i < 16; i++)
+ SA_prom[i] = SA_prom[i + i];
+#ifdef CONFIG_NE2K_CBUS_CNET98EL
+ if (hw->hwtype == NE2K_CBUS_HARDWARE_TYPE_CNET98EL) {
+ start_page = CNET98EL_START_PG;
+ stop_page = CNET98EL_STOP_PG;
+ } else {
+#endif
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+#ifdef CONFIG_NE2K_CBUS_CNET98EL
+ }
+#endif
+#endif
neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
+#ifndef CONFIG_NET_CBUS
ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
copam = (SA_prom[14] == 0x49 && SA_prom[15] == 0x00);
@@ -363,6 +753,11 @@
start_page = 0x01;
stop_page = (wordlength == 2) ? 0x40 : 0x20;
}
+#else
+ if (neX000){
+ name = "NE2000-compat";
+ }
+#endif
else
{
#ifdef SUPPORT_NE_BAD_CLONES
@@ -374,12 +769,16 @@
SA_prom[1] == bad_clone_list[i].SAprefix[1] &&
SA_prom[2] == bad_clone_list[i].SAprefix[2])
{
+#ifndef CONFIG_NET_CBUS
if (wordlength == 2)
{
name = bad_clone_list[i].name16;
} else {
name = bad_clone_list[i].name8;
}
+#else
+ name = bad_clone_list[i].name16;
+#endif
break;
}
}
@@ -387,6 +786,12 @@
{
printk(" not found (invalid signature %2.2x %2.2x).\n",
SA_prom[14], SA_prom[15]);
+#ifdef CONFIG_NET_CBUS
+ if (ei_debug > 2) {
+ printk(KERN_DEBUG "PROM prefix is: %2.2x %2.2x %2.2x\n",
+ SA_prom[0], SA_prom[1], SA_prom[2]);
+ }
+#endif
ret = -ENXIO;
goto err_out;
}
@@ -409,10 +814,18 @@
dev->irq = probe_irq_off(cookie);
if (ei_debug > 2)
printk(" autoirq is %d\n", dev->irq);
- } else if (dev->irq == 2)
+ } else
+#ifndef CONFIG_PC9800
+ if (dev->irq == 2)
/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
or don't know which one to set. */
dev->irq = 9;
+#else
+ if (dev->irq == 7)
+ /* Fixup for users that don't know that IRQ 7 is really IRQ 11,
+ or don't know which one to set. */
+ dev->irq = 11;
+#endif
if (! dev->irq) {
printk(" failed to detect IRQ line.\n");
@@ -443,13 +856,22 @@
dev->dev_addr[i] = SA_prom[i];
}
+#ifndef CONFIG_NET_CBUS
printk("\n%s: %s found at %#x, using IRQ %d.\n",
dev->name, name, ioaddr, dev->irq);
+#else
+ printk("\n%s: %s found at %#x, hardware type %d(%s), using IRQ %d.\n",
+ dev->name, name, ioaddr, hw->hwtype, hw->hwident, dev->irq);
+#endif
ei_status.name = name;
ei_status.tx_start_page = start_page;
ei_status.stop_page = stop_page;
+#ifndef CONFIG_NET_CBUS
ei_status.word16 = (wordlength == 2);
+#else
+ ei_status.word16 = (2 == 2); /* wordlength is always 2 */
+#endif
ei_status.rx_start_page = start_page + TX_PAGES;
#ifdef PACKETBUF_MEMSIZE
@@ -468,10 +890,23 @@
return 0;
err_out_kfree:
+#ifndef CONFIG_NET_CBUS
kfree(dev->priv);
dev->priv = NULL;
+#else
+ ne2k_cbus_destroy(dev);
+#endif
err_out:
+#ifndef CONFIG_NET_CBUS
release_region(ioaddr, NE_IO_EXTENT);
+#else
+ {
+ const struct ne2k_cbus_region *rlist;
+ for (rlist = hw->regionlist; rlist->range; rlist++) {
+ release_region(ioaddr + rlist->start, rlist->range);
+ }
+ }
+#endif
return ret;
}
@@ -495,10 +930,18 @@
static void ne_reset_8390(struct net_device *dev)
{
unsigned long reset_start_time = jiffies;
+#ifdef CONFIG_NET_CBUS
+ struct ei_device *ei_local = (struct ei_device *)(dev->priv);
+#endif
if (ei_debug > 1)
printk(KERN_DEBUG "resetting the 8390 t=%ld...", jiffies);
+#ifdef CONFIG_NET_CBUS
+ /* derived from CNET98EL-patch for bad clones... */
+ outb_p(E8390_NODMA | E8390_STOP, NE_BASE + E8390_CMD); /* 0x20 | 0x1 */
+#endif
+
/* DON'T change these to inb_p/outb_p or reset will fail on clones. */
outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
@@ -521,6 +964,9 @@
static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
{
int nic_base = dev->base_addr;
+#ifdef CONFIG_NET_CBUS
+ struct ei_device *ei_local = (struct ei_device *)(dev->priv);
+#endif
/* This *shouldn't* happen. If it does, it's the last thing you'll see */
@@ -563,6 +1009,9 @@
#endif
int nic_base = dev->base_addr;
char *buf = skb->data;
+#ifdef CONFIG_NET_CBUS
+ struct ei_device *ei_local = (struct ei_device *)(dev->priv);
+#endif
/* This *shouldn't* happen. If it does, it's the last thing you'll see */
if (ei_status.dmaing)
@@ -573,6 +1022,15 @@
return;
}
ei_status.dmaing |= 0x01;
+
+#ifdef CONFIG_NET_CBUS
+ /* derived from ICM-patch */
+ /* round up count to a word */
+ if (count & 1) {
+ count++;
+ }
+#endif
+
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
outb_p(count >> 8, nic_base + EN0_RCNTHI);
@@ -630,6 +1088,9 @@
#ifdef NE_SANITY_CHECK
int retries = 0;
#endif
+#ifdef CONFIG_NET_CBUS
+ struct ei_device *ei_local = (struct ei_device *)(dev->priv);
+#endif
/* Round the count up for word writes. Do we need to do this?
What effect will an odd byte count have on the 8390?
@@ -730,16 +1191,25 @@
#ifdef MODULE
#define MAX_NE_CARDS 4 /* Max number of NE cards per module */
static struct net_device dev_ne[MAX_NE_CARDS];
-static int io[MAX_NE_CARDS];
-static int irq[MAX_NE_CARDS];
-static int bad[MAX_NE_CARDS]; /* 0xbad = bad sig or no reset ack */
+static int __initdata io[MAX_NE_CARDS];
+static int __initdata irq[MAX_NE_CARDS];
+static int __initdata bad[MAX_NE_CARDS]; /* 0xbad = bad sig or no reset ack */
+#ifdef CONFIG_PC9800
+static int __initdata hwtype[MAX_NE_CARDS] = { 0, }; /* board type */
+#endif
MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NE_CARDS) "i");
MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_NE_CARDS) "i");
MODULE_PARM(bad, "1-" __MODULE_STRING(MAX_NE_CARDS) "i");
+#ifdef CONFIG_PC9800
+MODULE_PARM(hwtype, "1-" __MODULE_STRING(MAX_NE_CARDS) "i");
+#endif
MODULE_PARM_DESC(io, "I/O base address(es),required");
MODULE_PARM_DESC(irq, "IRQ number(s)");
MODULE_PARM_DESC(bad, "Accept card(s) with bad signatures");
+#ifdef CONFIG_PC9800
+MODULE_PARM_DESC(hwtype, "Board type of PC-9800 C-Bus NIC");
+#endif
MODULE_DESCRIPTION("NE1000/NE2000 ISA/PnP Ethernet driver");
MODULE_LICENSE("GPL");
@@ -757,6 +1227,9 @@
dev->irq = irq[this_dev];
dev->mem_end = bad[this_dev];
dev->base_addr = io[this_dev];
+#ifdef CONFIG_PC9800
+ dev->mem_start = hwtype[this_dev];
+#endif
dev->init = ne_probe;
if (register_netdev(dev) == 0) {
found++;
@@ -781,14 +1254,30 @@
for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
struct net_device *dev = &dev_ne[this_dev];
if (dev->priv != NULL) {
+#ifndef CONFIG_NET_CBUS
void *priv = dev->priv;
+#endif
struct pci_dev *idev = (struct pci_dev *)ei_status.priv;
if (idev)
idev->deactivate(idev);
free_irq(dev->irq, dev);
+#ifndef CONFIG_NET_CBUS
release_region(dev->base_addr, NE_IO_EXTENT);
+#else /* CONFIG_NET_CBUS */
+ {
+ const struct ne2k_cbus_hwinfo *hw = ne2k_cbus_get_hwinfo((int)(dev->mem_start & NE2K_CBUS_HARDWARE_TYPE_MASK));
+ const struct ne2k_cbus_region *rlist;
+ for (rlist = hw->regionlist; rlist->range; rlist++) {
+ release_region(dev->base_addr + rlist->start, rlist->range);
+ }
+ }
+#endif /* !CONFIG_NET_CBUS */
unregister_netdev(dev);
+#ifndef CONFIG_NET_CBUS
kfree(priv);
+#else
+ ne2k_cbus_destroy(dev);
+#endif
}
}
}
diff -Nru linux-2.5.42/drivers/net/ne2k_cbus.h linux98-2.5.42/drivers/net/ne2k_cbus.h
--- linux-2.5.42/drivers/net/ne2k_cbus.h Thu Jan 1 09:00:00 1970
+++ linux98-2.5.42/drivers/net/ne2k_cbus.h Sat Oct 26 14:26:13 2002
@@ -0,0 +1,467 @@
+/* ne2k_cbus.h: + vender-specific information definition for NEC PC-9800
+ C-bus Ethernet Cards
+ Used in ne.c +
+ (C)1998,1999 KITAGWA Takurou & Linux/98 project
+*/
+
+#include <linux/config.h>
+
+/* Hardware type definition (derived from *BSD) */
+#define NE2K_CBUS_HARDWARE_TYPE_MASK 0xff
+
+/* 0: reserved for auto-detect */
+/* 1: (not tested)
+ Allied Telesis CentreCom LA-98-T */
+#define NE2K_CBUS_HARDWARE_TYPE_ATLA98 1
+/* 2: (not tested)
+ ELECOM Laneed
+ LD-BDN[123]A
+ PLANET SMART COM 98 EN-2298-C
+ MACNICA ME98 */
+#define NE2K_CBUS_HARDWARE_TYPE_BDN 2
+/* 3:
+ Melco EGY-98
+ Contec C-NET(98)E*A/L*A,C-NET(98)P */
+#define NE2K_CBUS_HARDWARE_TYPE_EGY98 3
+/* 4:
+ Melco LGY-98,IND-SP,IND-SS
+ MACNICA NE2098 */
+#define NE2K_CBUS_HARDWARE_TYPE_LGY98 4
+/* 5:
+ ICM DT-ET-25,DT-ET-T5,IF-2766ET,IF-2771ET
+ PLANET SMART COM 98 EN-2298-T,EN-2298P-T
+ D-Link DE-298PT,DE-298PCAT
+ ELECOM Laneed LD-98P */
+#define NE2K_CBUS_HARDWARE_TYPE_ICM 5
+/* 6: (reserved for SIC-98, which is not supported in this driver.) */
+/* 7: (unused in *BSD?)
+ <Original NE2000 compatible>
+ <for PCI/PCMCIA cards>
+*/
+#define NE2K_CBUS_HARDWARE_TYPE_NE2K 7
+/* 8: (not tested)
+ NEC PC-9801-108 */
+#define NE2K_CBUS_HARDWARE_TYPE_NEC108 8
+/* 9:
+ I-O DATA LA-98,LA/T-98 */
+#define NE2K_CBUS_HARDWARE_TYPE_IOLA98 9
+/* 10: (reserved for C-NET(98), which is not supported in this driver.) */
+/* 11:
+ Contec C-NET(98)E,L */
+#define NE2K_CBUS_HARDWARE_TYPE_CNET98EL 11
+
+#define NE2K_CBUS_HARDWARE_TYPE_MAX 11
+
+/* HARDWARE TYPE ID 12-31: reserved */
+
+struct ne2k_cbus_offsetinfo {
+ unsigned short skip;
+ unsigned short offset8; /* +0x8 - +0xf */
+ unsigned short offset10; /* +0x10 */
+ unsigned short offset1f; /* +0x1f */
+};
+
+struct ne2k_cbus_region {
+ unsigned short start;
+ short range;
+};
+
+struct ne2k_cbus_hwinfo {
+ const unsigned short hwtype;
+ const unsigned char *hwident;
+#ifndef MODULE
+ const unsigned short *portlist;
+#endif
+ const struct ne2k_cbus_offsetinfo *offsetinfo;
+ const struct ne2k_cbus_region *regionlist;
+};
+
+/* XXX */
+/* we can't use first __initdata with const? */
+static struct {} ne2k_cbus_dummy_for_initdata __attribute__((unused)) __initdata = {};
+
+#ifdef CONFIG_NE2K_CBUS_ATLA98
+#ifndef MODULE
+static const unsigned short atla98_portlist[] __initdata = {
+ 0xd0,
+ 0
+};
+#endif
+#define atla98_offsetinfo ne2k_offsetinfo
+#define atla98_regionlist ne2k_regionlist
+#endif /* CONFIG_NE2K_CBUS_ATLA98 */
+
+#ifdef CONFIG_NE2K_CBUS_BDN
+#ifndef MODULE
+static const unsigned short bdn_portlist[] __initdata = {
+ 0xd0,
+ 0
+};
+#endif
+static const struct ne2k_cbus_offsetinfo bdn_offsetinfo __initdata = {
+#if 0
+ /* comes from FreeBSD(98) ed98.h */
+ 0x1000, 0x8000, 0x100, 0xc200 /* ??? */
+#else
+ /* comes from NetBSD/pc98 if_ne_isa.c */
+ 0x1000, 0x8000, 0x100, 0x7f00 /* ??? */
+#endif
+};
+static const struct ne2k_cbus_region bdn_regionlist[] __initdata = {
+ {0x0,1},{0x1000,1},{0x2000,1},{0x3000,1},
+ {0x4000,1},{0x5000,1},{0x6000,1},{0x7000,1},
+ {0x8000,1},{0x9000,1},{0xa000,1},{0xb000,1},
+ {0xc000,1},{0xd000,1},{0xe000,1},{0xf000,1},{0x100,1},{0x7f00,1},
+ {0x0,0}
+};
+#endif /* CONFIG_NE2K_CBUS_BDN */
+
+#ifdef CONFIG_NE2K_CBUS_EGY98
+#ifndef MODULE
+static const unsigned short egy98_portlist[] __initdata = {
+ 0xd0,
+ 0
+};
+#endif
+static const struct ne2k_cbus_offsetinfo egy98_offsetinfo __initdata = {
+ 0x02, 0x100, 0x200, 0x300
+};
+static const struct ne2k_cbus_region egy98_regionlist[] __initdata = {
+ {0x0,1}, {0x2,1}, {0x4,1}, {0x6,1}, {0x8,1}, {0xa,1}, {0xc,1}, {0xe,1},
+ {0x100,1}, {0x102,1}, {0x104,1}, {0x106,1},
+ {0x108,1}, {0x10a,1}, {0x10c,1}, {0x10e,1},
+ {0x200,1}, {0x300,1},
+ {0x0,0}
+};
+#endif /* CONFIG_NE2K_CBUS_EGY98 */
+
+#ifdef CONFIG_NE2K_CBUS_LGY98
+#ifndef MODULE
+static const unsigned short lgy98_portlist[] __initdata = {
+ 0xd0, 0x10d0, 0x20d0, 0x30d0, 0x40d0, 0x50d0, 0x60d0, 0x70d0,
+ 0
+};
+#endif
+static const struct ne2k_cbus_offsetinfo lgy98_offsetinfo __initdata = {
+ 0x01, 0x08, 0x200, 0x300
+};
+static const struct ne2k_cbus_region lgy98_regionlist[] __initdata = {
+ {0x0,16}, {0x200,1}, {0x300,1},
+ {0x0,0}
+};
+#endif /* CONFIG_NE2K_CBUS_LGY98 */
+
+#ifdef CONFIG_NE2K_CBUS_ICM
+#ifndef MODULE
+static const unsigned short icm_portlist[] __initdata = {
+ /* ICM */
+ 0x56d0,
+ /* LD-98PT */
+ 0x46d0, 0x66d0, 0x76d0, 0x86d0, 0x96d0, 0xa6d0, 0xb6d0, 0xc6d0,
+ 0
+};
+#endif
+static const struct ne2k_cbus_offsetinfo icm_offsetinfo __initdata = {
+ 0x01, 0x08, 0x100, 0x10f
+};
+static const struct ne2k_cbus_region icm_regionlist[] __initdata = {
+ {0x0,16}, {0x100,16},
+ {0x0,0}
+};
+#endif /* CONFIG_NE2K_CBUS_ICM */
+
+#if defined(CONFIG_NE2K_CBUS_NE2K) && !defined(MODULE)
+static const unsigned short ne2k_portlist[] __initdata = {
+ 0xd0, 0x300, 0x280, 0x320, 0x340, 0x360, 0x380,
+ 0
+};
+#endif
+#if defined(CONFIG_NE2K_CBUS_NE2K) || defined(CONFIG_NE2K_CBUS_ATLA98)
+static const struct ne2k_cbus_offsetinfo ne2k_offsetinfo __initdata = {
+ 0x01, 0x08, 0x10, 0x1f
+};
+static const struct ne2k_cbus_region ne2k_regionlist[] __initdata = {
+ {0x0,32},
+ {0x0,0}
+};
+#endif
+
+#ifdef CONFIG_NE2K_CBUS_NEC108
+#ifndef MODULE
+static const unsigned short nec108_portlist[] __initdata = {
+ 0x770, 0x2770, 0x4770, 0x6770,
+ 0
+};
+#endif
+static const struct ne2k_cbus_offsetinfo nec108_offsetinfo __initdata = {
+ 0x02, 0x1000, 0x888, 0x88a
+};
+static const struct ne2k_cbus_region nec108_regionlist[] __initdata = {
+ {0x0,1}, {0x2,1}, {0x4,1}, {0x6,1}, {0x8,1}, {0xa,1}, {0xc,1}, {0xe,1},
+ {0x1000,1}, {0x1002,1}, {0x1004,1}, {0x1006,1},
+ {0x1008,1}, {0x100a,1}, {0x100c,1}, {0x100e,1},
+ {0x118,1}, {0x11a,1}, {0x11c,1}, {0x11e,1},
+ {0x0,0}
+};
+#endif
+
+#ifdef CONFIG_NE2K_CBUS_IOLA98
+#ifndef MODULE
+static const unsigned short iola98_portlist[] __initdata = {
+ 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
+ 0
+};
+#endif
+static const struct ne2k_cbus_offsetinfo iola98_offsetinfo __initdata = {
+ 0x1000, 0x8000, 0x100, 0xf100
+};
+static const struct ne2k_cbus_region iola98_regionlist[] __initdata = {
+ {0x0,1},{0x1000,1},{0x2000,1},{0x3000,1},
+ {0x4000,1},{0x5000,1},{0x6000,1},{0x7000,1},
+ {0x8000,1},{0x9000,1},{0xa000,1},{0xb000,1},
+ {0xc000,1},{0xd000,1},{0xe000,1},{0xf000,1},{0x100,1},{0xf100,1},
+ {0x0,0}
+};
+#endif /* CONFIG_NE2K_CBUS_IOLA98 */
+
+#ifdef CONFIG_NE2K_CBUS_CNET98EL
+#ifndef MODULE
+static const unsigned short cnet98el_portlist[] __initdata = {
+ 0x3d0, 0x13d0, 0x23d0, 0x33d0, 0x43d0, 0x53d0, 0x60d0, 0x70d0,
+ 0
+};
+#endif
+static const struct ne2k_cbus_offsetinfo cnet98el_offsetinfo __initdata = {
+ 0x01, 0x08, 0x40e, 0x400
+};
+static const struct ne2k_cbus_region cnet98el_regionlist[] __initdata = {
+ {0x0, 16}, {0x400, 16},
+ {0x0,0}
+};
+#endif
+
+
+/* port information table (for ne.c initialize/probe process) */
+
+static const struct ne2k_cbus_hwinfo ne2k_cbus_hwinfo_list[] __initdata = {
+#ifdef CONFIG_NE2K_CBUS_ATLA98
+/* NOT TESTED */
+ {
+ NE2K_CBUS_HARDWARE_TYPE_ATLA98,
+ "LA-98-T",
+#ifndef MODULE
+ atla98_portlist,
+#endif
+ &atla98_offsetinfo, atla98_regionlist
+ },
+#endif
+#ifdef CONFIG_NE2K_CBUS_BDN
+/* NOT TESTED */
+ {
+ NE2K_CBUS_HARDWARE_TYPE_BDN,
+ "LD-BDN[123]A",
+#ifndef MODULE
+ bdn_portlist,
+#endif
+ &bdn_offsetinfo, bdn_regionlist
+ },
+#endif
+#ifdef CONFIG_NE2K_CBUS_ICM
+ {
+ NE2K_CBUS_HARDWARE_TYPE_ICM,
+ "IF-27xxET",
+#ifndef MODULE
+ icm_portlist,
+#endif
+ &icm_offsetinfo, icm_regionlist
+ },
+#endif
+#ifdef CONFIG_NE2K_CBUS_NE2K
+ {
+ NE2K_CBUS_HARDWARE_TYPE_NE2K,
+ "NE2000 compat.",
+#ifndef MODULE
+ ne2k_portlist,
+#endif
+ &ne2k_offsetinfo, ne2k_regionlist
+ },
+#endif
+#ifdef CONFIG_NE2K_CBUS_NEC108
+/* NOT supported yet! */
+ {
+ NE2K_CBUS_HARDWARE_TYPE_NEC108,
+ "PC-9801-108",
+#ifndef MODULE
+ nec108_portlist,
+#endif
+ &nec108_offsetinfo, nec108_regionlist
+ },
+#endif
+#ifdef CONFIG_NE2K_CBUS_IOLA98
+ {
+ NE2K_CBUS_HARDWARE_TYPE_IOLA98,
+ "LA-98",
+#ifndef MODULE
+ iola98_portlist,
+#endif
+ &iola98_offsetinfo, iola98_regionlist
+ },
+#endif
+#ifdef CONFIG_NE2K_CBUS_CNET98EL
+ {
+ NE2K_CBUS_HARDWARE_TYPE_CNET98EL,
+ "C-NET(98)E/L",
+#ifndef MODULE
+ cnet98el_portlist,
+#endif
+ &cnet98el_offsetinfo, cnet98el_regionlist
+ },
+#endif
+/* NOTE: LGY98 must be probed before EGY98, or system stalled!? */
+#ifdef CONFIG_NE2K_CBUS_LGY98
+ {
+ NE2K_CBUS_HARDWARE_TYPE_LGY98,
+ "LGY-98",
+#ifndef MODULE
+ lgy98_portlist,
+#endif
+ &lgy98_offsetinfo, lgy98_regionlist
+ },
+#endif
+#ifdef CONFIG_NE2K_CBUS_EGY98
+ {
+ NE2K_CBUS_HARDWARE_TYPE_EGY98,
+ "EGY-98",
+#ifndef MODULE
+ egy98_portlist,
+#endif
+ &egy98_offsetinfo, egy98_regionlist
+ },
+#endif
+ {
+ 0,"unsupported hardware",
+#ifndef MODULE
+ NULL,
+#endif
+ NULL,NULL
+ }
+};
+
+static int __init ne2k_cbus_init(struct net_device *dev)
+{
+ struct ei_device *ei_local;
+ if(dev->priv == NULL) {
+ ei_local = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
+ if (ei_local == NULL)
+ return -ENOMEM;
+ memset(ei_local, 0, sizeof(struct ei_device));
+ ei_local->reg_offset = kmalloc(sizeof(typeof(*ei_local->reg_offset))*18, GFP_KERNEL);
+ if (ei_local->reg_offset == NULL){
+ kfree(ei_local);
+ return -ENOMEM;
+ }
+ spin_lock_init(&ei_local->page_lock);
+ dev->priv = ei_local;
+ }
+ return 0;
+}
+
+static void ne2k_cbus_destroy(struct net_device *dev)
+{
+ struct ei_device *ei_local = (struct ei_device *)(dev->priv);
+ if(ei_local != NULL){
+ if(ei_local->reg_offset)
+ kfree(ei_local->reg_offset);
+ kfree(dev->priv);
+ dev->priv=NULL;
+ }
+}
+
+static const struct ne2k_cbus_hwinfo * __init ne2k_cbus_get_hwinfo(int hwtype)
+{
+ const struct ne2k_cbus_hwinfo *hw;
+
+ for(hw=&ne2k_cbus_hwinfo_list[0]; hw->hwtype; hw++){
+ if(hw->hwtype==hwtype) break;
+ }
+ return hw;
+}
+
+static void __init ne2k_cbus_set_hwtype(struct net_device *dev, const struct ne2k_cbus_hwinfo *hw, int ioaddr)
+{
+ struct ei_device *ei_local=(struct ei_device *)(dev->priv);
+ int i;
+ int hwtype_old=(dev->mem_start & NE2K_CBUS_HARDWARE_TYPE_MASK);
+
+ if (!ei_local)
+ panic("Gieee! ei_local == NULL!! (from %p)",
+ __builtin_return_address(0));
+
+ dev->mem_start &= (~NE2K_CBUS_HARDWARE_TYPE_MASK);
+ dev->mem_start |= (hw->hwtype & NE2K_CBUS_HARDWARE_TYPE_MASK);
+
+ if(ei_debug>2){
+ printk(KERN_DEBUG "hwtype changed: %d -> %d\n",hwtype_old,(int)(dev->mem_start & NE2K_CBUS_HARDWARE_TYPE_MASK));
+ }
+
+ if(hw->offsetinfo){
+ for(i=0;i<8;i++){
+ ei_local->reg_offset[i] = hw->offsetinfo->skip * i;
+ }
+ for(i=8;i<16;i++){
+ ei_local->reg_offset[i] =
+ hw->offsetinfo->skip*(i-8) + hw->offsetinfo->offset8;
+ }
+#ifdef CONFIG_NE2K_CBUS_NEC108
+ if(hw->hwtype==NE2K_CBUS_HARDWARE_TYPE_NEC108){
+ int adj = (ioaddr & 0xf000) /2;
+ ei_local->reg_offset[16] = + (hw->offsetinfo->offset10 | adj) - ioaddr;
+ ei_local->reg_offset[17] = + (hw->offsetinfo->offset1f | adj) - ioaddr;
+ }else{
+#endif /* CONFIG_NE2K_CBUS_NEC108 */
+ ei_local->reg_offset[16] = hw->offsetinfo->offset10;
+ ei_local->reg_offset[17] = hw->offsetinfo->offset1f;
+#ifdef CONFIG_NE2K_CBUS_NEC108
+ }
+#endif
+ }else{
+ /* make dummmy offset list */
+ for(i=0;i<16;i++){
+ ei_local->reg_offset[i] = i;
+ }
+ ei_local->reg_offset[16] = 0x10;
+ ei_local->reg_offset[17] = 0x1f;
+ }
+}
+
+#if defined(CONFIG_NE2K_CBUS_ICM) || defined(CONFIG_NE2K_CBUS_CNET98EL)
+static void __init ne2k_cbus_readmem(struct net_device *dev, int ioaddr, unsigned short memaddr, char *buf, unsigned short len)
+{
+ struct ei_device *ei_local = (struct ei_device *)(dev->priv);
+ outb_p(E8390_NODMA | E8390_START, ioaddr+E8390_CMD);
+ outb_p( len & 0xff, ioaddr+EN0_RCNTLO);
+ outb_p( len >> 8 , ioaddr+EN0_RCNTHI);
+ outb_p( memaddr & 0xff, ioaddr+EN0_RSARLO);
+ outb_p( memaddr >> 8 , ioaddr+EN0_RSARHI);
+ outb_p(E8390_RREAD | E8390_START, ioaddr+E8390_CMD);
+ insw(ioaddr+NE_DATAPORT, buf, len>>1);
+}
+static void __init ne2k_cbus_writemem(struct net_device *dev, int ioaddr, unsigned short memaddr, const char *buf, unsigned short len)
+{
+ struct ei_device *ei_local = (struct ei_device *)(dev->priv);
+ outb_p(E8390_NODMA | E8390_START, ioaddr+E8390_CMD);
+ outb_p( ENISR_RDC, ioaddr+EN0_ISR);
+ outb_p( len & 0xff , ioaddr+EN0_RCNTLO);
+ outb_p( len >> 8 , ioaddr+EN0_RCNTHI);
+ outb_p( memaddr & 0xff , ioaddr+EN0_RSARLO);
+ outb_p( memaddr >> 8 , ioaddr+EN0_RSARHI);
+ outb_p(E8390_RWRITE | E8390_START, ioaddr+E8390_CMD);
+ outsw(ioaddr+NE_DATAPORT, buf, len>>1);
+}
+#endif
+
+/* End of ne2k_cbus.h */
Osamu Tomita
2002-11-02 18:16:32 UTC
Permalink
This is a part 15/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
legacy bus pnp module
- IO port address change.

diffstat:
drivers/pnp/isapnp/core.c | 5 +++++
1 files changed, 5 insertions(+)

patch:
diff -urN linux/drivers/pnp/isapnp/core.c linux98/drivers/pnp/isapnp/core.c
--- linux/drivers/pnp/isapnp/core.c Sat Oct 19 13:01:56 2002
+++ linux98/drivers/pnp/isapnp/core.c Sat Oct 19 15:37:24 2002
@@ -75,8 +75,13 @@
MODULE_PARM_DESC(isapnp_verbose, "ISA Plug & Play verbose mode");
MODULE_LICENSE("GPL");
+#ifndef CONFIG_PC9800
#define _PIDXR 0x279
#define _PNPWRP 0xa79
+#else
+#define _PIDXR 0x259
+#define _PNPWRP 0xa59
+#endif
/* short tags */
#define _STAG_PNPVERNO 0x01
Osamu Tomita
2002-11-02 18:21:58 UTC
Permalink
This is part 18/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
serial driver related modules.
- change IO port address and IRQ number.
- add new PNP device entry.

diffstat:
drivers/serial/8250_pnp.c | 7 +++++++
include/asm-i386/serial.h | 7 +++++++
2 files changed, 14 insertions(+)

patch:
diff -urN linux/drivers/serial/8250_pnp.c linux98/drivers/serial/8250_pnp.c
--- linux/drivers/serial/8250_pnp.c Sat Oct 19 13:01:08 2002
+++ linux98/drivers/serial/8250_pnp.c Sat Oct 19 16:20:53 2002
@@ -193,6 +193,8 @@
{ "MVX00A1", 0 },
/* PC Rider K56 Phone System PnP */
{ "MVX00F2", 0 },
+ /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */
+ { "nEC8241", 0 },
/* Pace 56 Voice Internal Plug & Play Modem */
{ "PMC2430", 0 },
/* Generic */
@@ -376,7 +378,12 @@
((port->min == 0x2f8) ||
(port->min == 0x3f8) ||
(port->min == 0x2e8) ||
+#ifndef CONFIG_PC9800
(port->min == 0x3e8)))
+#else
+ (port->min == 0x3e8) ||
+ (port->min == 0x8b0)))
+#endif
return 0;
}
diff -urN linux/include/asm-i386/serial.h linux98/include/asm-i386/serial.h
--- linux/include/asm-i386/serial.h Wed Oct 16 12:27:56 2002
+++ linux98/include/asm-i386/serial.h Fri Oct 18 10:12:09 2002
@@ -50,12 +50,19 @@
#define C_P(card,port) (((card)<<6|(port)<<3) + 1)
+#ifndef CONFIG_PC9800
#define STD_SERIAL_PORT_DEFNS \
/* UART CLK PORT IRQ FLAGS */ \
{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ \
{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ \
{ 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */ \
{ 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */
+#else
+#define STD_SERIAL_PORT_DEFNS \
+ /* UART CLK PORT IRQ FLAGS */ \
+ { 0, BASE_BAUD, 0x30, 4, STD_COM_FLAGS }, /* ttyS0 */ \
+ { 0, BASE_BAUD, 0x238, 5, STD_COM_FLAGS }, /* ttyS1 */
+#endif /* CONFIG_PC9800 */
#ifdef CONFIG_SERIAL_MANY_PORTS
Osamu Tomita
2002-11-02 17:56:52 UTC
Permalink
This is a part 7/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

floppy98.c is splited to 2 patches. Please apply this after floppy1.

Summary:
floppy driver modules

diffstat:
drivers/block/floppy98.c | 2303 ++++++++++++++++++++++++++++++++++++++++++++++
include/asm-i386/floppy.h | 11 include/linux/fdreg.h | 24 3 files changed, 2338 insertions(+)

patch:
diff -urN linux/drivers/block/floppy98.c linux98/drivers/block/floppy98.c
--- linux/drivers/block/floppy98.c Thu Oct 31 16:11:27 2002
+++ linux98/drivers/block/floppy98.c Thu Oct 31 16:09:02 2002
@@ -2335,3 +2335,2306 @@
return ret;
}
+/*
+ * Buffer read/write and support
+ * =============================
+ */
+
+static inline void end_request(struct request *req, int uptodate)
+{
+ if (end_that_request_first(req, uptodate, current_count_sectors))
+ return;
+ add_disk_randomness(req->rq_disk);
+ floppy_off((int)req->rq_disk->private_data);
+ blkdev_dequeue_request(req);
+ end_that_request_last(req);
+
+ /* We're done with the request */
+ current_req = NULL;
+}
+
+
+/* new request_done. Can handle physical sectors which are smaller than a
+ * logical buffer */
+static void request_done(int uptodate)
+{
+ struct request_queue *q = &floppy_queue;
+ struct request *req = current_req;
+ unsigned long flags;
+ int block;
+
+ probing = 0;
+ reschedule_timeout(MAXTIMEOUT, "request done %d", uptodate);
+
+ if (!req) {
+ printk("floppy.c: no request in request_done\n");
+ return;
+ }
+
+ if (uptodate){
+ /* maintain values for invalidation on geometry
+ * change */
+ block = current_count_sectors + req->sector;
+ INFBOUND(DRS->maxblock, block);
+ if (block > _floppy->sect)
+ DRS->maxtrack = 1;
+
+ /* unlock chained buffers */
+ spin_lock_irqsave(q->queue_lock, flags);
+ end_request(req, 1);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ } else {
+ if (rq_data_dir(req) == WRITE) {
+ /* record write error information */
+ DRWE->write_errors++;
+ if (DRWE->write_errors == 1) {
+ DRWE->first_error_sector = req->sector;
+ DRWE->first_error_generation = DRS->generation;
+ }
+ DRWE->last_error_sector = req->sector;
+ DRWE->last_error_generation = DRS->generation;
+ }
+ spin_lock_irqsave(q->queue_lock, flags);
+ end_request(req, 0);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ }
+}
+
+/* Interrupt handler evaluating the result of the r/w operation */
+static void rw_interrupt(void)
+{
+ int nr_sectors, ssize, eoc, heads;
+
+ if (R_HEAD >= 2) {
+ /* some Toshiba floppy controllers occasionnally seem to
+ * return bogus interrupts after read/write operations, which
+ * can be recognized by a bad head number (>= 2) */
+ return;
+ } +
+ if (!DRS->first_read_date)
+ DRS->first_read_date = jiffies;
+
+ nr_sectors = 0;
+ CODE2SIZE;
+
+ if (ST1 & ST1_EOC)
+ eoc = 1;
+ else
+ eoc = 0;
+
+ if (COMMAND & 0x80)
+ heads = 2;
+ else
+ heads = 1;
+
+ nr_sectors = (((R_TRACK-TRACK) * heads +
+ R_HEAD-HEAD) * SECT_PER_TRACK +
+ R_SECTOR-SECTOR + eoc) << SIZECODE >> 2;
+
+#ifdef FLOPPY_SANITY_CHECK
+ if (nr_sectors / ssize > + (in_sector_offset + current_count_sectors + ssize - 1) / ssize) {
+ DPRINT("long rw: %x instead of %lx\n",
+ nr_sectors, current_count_sectors);
+ printk("rs=%d s=%d\n", R_SECTOR, SECTOR);
+ printk("rh=%d h=%d\n", R_HEAD, HEAD);
+ printk("rt=%d t=%d\n", R_TRACK, TRACK);
+ printk("heads=%d eoc=%d\n", heads, eoc);
+ printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK,
+ fsector_t, ssize);
+ printk("in_sector_offset=%d\n", in_sector_offset);
+ }
+#endif
+
+ nr_sectors -= in_sector_offset;
+ INFBOUND(nr_sectors,0);
+ SUPBOUND(current_count_sectors, nr_sectors);
+
+ switch (interpret_errors()){
+ case 2:
+ cont->redo();
+ return;
+ case 1:
+ if (!current_count_sectors){
+ cont->error();
+ cont->redo();
+ return;
+ }
+ break;
+ case 0:
+ if (!current_count_sectors){
+ cont->redo();
+ return;
+ }
+ current_type[current_drive] = _floppy;
+ floppy_sizes[TOMINOR(current_drive) ]= _floppy->size;
+ break;
+ }
+
+ if (probing) {
+ if (DP->flags & FTD_MSG)
+ DPRINT("Auto-detected floppy type %s in fd%d\n",
+ _floppy->name,current_drive);
+ current_type[current_drive] = _floppy;
+ floppy_sizes[TOMINOR(current_drive)] = _floppy->size;
+ probing = 0;
+ }
+
+ if (CT(COMMAND) != FD_READ || + raw_cmd->kernel_data == current_req->buffer){
+ /* transfer directly from buffer */
+ cont->done(1);
+ } else if (CT(COMMAND) == FD_READ){
+ buffer_track = raw_cmd->track;
+ buffer_drive = current_drive;
+ INFBOUND(buffer_max, nr_sectors + fsector_t);
+ }
+ cont->redo();
+}
+
+/* Compute maximal contiguous buffer size. */
+static int buffer_chain_size(void)
+{
+ struct bio *bio;
+ struct bio_vec *bv;
+ int size, i;
+ char *base;
+
+ base = bio_data(current_req->bio);
+ size = 0;
+
+ rq_for_each_bio(bio, current_req) {
+ bio_for_each_segment(bv, bio, i) {
+ if (page_address(bv->bv_page) + bv->bv_offset != base + size)
+ break;
+
+ size += bv->bv_len;
+ }
+ }
+
+ return size >> 9;
+}
+
+/* Compute the maximal transfer size */
+static int transfer_size(int ssize, int max_sector, int max_size)
+{
+ SUPBOUND(max_sector, fsector_t + max_size);
+
+ /* alignment */
+ max_sector -= (max_sector % _floppy->sect) % ssize;
+
+ /* transfer size, beginning not aligned */
+ current_count_sectors = max_sector - fsector_t ;
+
+ return max_sector;
+}
+
+/*
+ * Move data from/to the track buffer to/from the buffer cache.
+ */
+static void copy_buffer(int ssize, int max_sector, int max_sector_2)
+{
+ int remaining; /* number of transferred 512-byte sectors */
+ struct bio_vec *bv;
+ struct bio *bio;
+ char *buffer, *dma_buffer;
+ int size, i;
+
+ max_sector = transfer_size(ssize,
+ minimum(max_sector, max_sector_2),
+ current_req->nr_sectors);
+
+ if (current_count_sectors <= 0 && CT(COMMAND) == FD_WRITE &&
+ buffer_max > fsector_t + current_req->nr_sectors)
+ current_count_sectors = minimum(buffer_max - fsector_t,
+ current_req->nr_sectors);
+
+ remaining = current_count_sectors << 9;
+#ifdef FLOPPY_SANITY_CHECK
+ if ((remaining >> 9) > current_req->nr_sectors &&
+ CT(COMMAND) == FD_WRITE){
+ DPRINT("in copy buffer\n");
+ printk("current_count_sectors=%ld\n", current_count_sectors);
+ printk("remaining=%d\n", remaining >> 9);
+ printk("current_req->nr_sectors=%ld\n",current_req->nr_sectors);
+ printk("current_req->current_nr_sectors=%u\n",
+ current_req->current_nr_sectors);
+ printk("max_sector=%d\n", max_sector);
+ printk("ssize=%d\n", ssize);
+ }
+#endif
+
+ buffer_max = maximum(max_sector, buffer_max);
+
+ dma_buffer = floppy_track_buffer + ((fsector_t - buffer_min) << 9);
+
+ size = current_req->current_nr_sectors << 9;
+
+ rq_for_each_bio(bio, current_req) {
+ bio_for_each_segment(bv, bio, i) {
+ if (!remaining)
+ break;
+
+ size = bv->bv_len;
+ SUPBOUND(size, remaining);
+
+ buffer = page_address(bv->bv_page) + bv->bv_offset;
+#ifdef FLOPPY_SANITY_CHECK
+ if (dma_buffer + size >
+ floppy_track_buffer + (max_buffer_sectors << 10) ||
+ dma_buffer < floppy_track_buffer){
+ DPRINT("buffer overrun in copy buffer %d\n",
+ (int) ((floppy_track_buffer - dma_buffer) >>9));
+ printk("fsector_t=%d buffer_min=%d\n",
+ fsector_t, buffer_min);
+ printk("current_count_sectors=%ld\n",
+ current_count_sectors);
+ if (CT(COMMAND) == FD_READ)
+ printk("read\n");
+ if (CT(COMMAND) == FD_READ)
+ printk("write\n");
+ break;
+ }
+ if (((unsigned long)buffer) % 512)
+ DPRINT("%p buffer not aligned\n", buffer);
+#endif
+ if (CT(COMMAND) == FD_READ)
+ memcpy(buffer, dma_buffer, size);
+ else
+ memcpy(dma_buffer, buffer, size);
+
+ remaining -= size;
+ dma_buffer += size;
+ }
+ }
+#ifdef FLOPPY_SANITY_CHECK
+ if (remaining){
+ if (remaining > 0)
+ max_sector -= remaining >> 9;
+ DPRINT("weirdness: remaining %d\n", remaining>>9);
+ }
+#endif
+}
+
+#if 0
+static inline int check_dma_crossing(char *start, + unsigned long length, char *message)
+{
+ if (CROSS_64KB(start, length)) {
+ printk("DMA xfer crosses 64KB boundary in %s %p-%p\n", + message, start, start+length);
+ return 1;
+ } else
+ return 0;
+}
+#endif
+
+/* work around a bug in pseudo DMA
+ * (on some FDCs) pseudo DMA does not stop when the CPU stops
+ * sending data. Hence we need a different way to signal the
+ * transfer length: We use SECT_PER_TRACK. Unfortunately, this
+ * does not work with MT, hence we can only transfer one head at
+ * a time
+ */
+static void virtualdmabug_workaround(void)
+{
+ int hard_sectors, end_sector;
+
+ if(CT(COMMAND) == FD_WRITE) {
+ COMMAND &= ~0x80; /* switch off multiple track mode */
+
+ hard_sectors = raw_cmd->length >> (7 + SIZECODE);
+ end_sector = SECTOR + hard_sectors - 1;
+#ifdef FLOPPY_SANITY_CHECK
+ if(end_sector > SECT_PER_TRACK) {
+ printk("too many sectors %d > %d\n",
+ end_sector, SECT_PER_TRACK);
+ return;
+ }
+#endif
+ SECT_PER_TRACK = end_sector; /* make sure SECT_PER_TRACK points
+ * to end of transfer */
+ }
+}
+
+/*
+ * Formulate a read/write request.
+ * this routine decides where to load the data (directly to buffer, or to
+ * tmp floppy area), how much data to load (the size of the buffer, the whole
+ * track, or a single sector)
+ * All floppy_track_buffer handling goes in here. If we ever add track buffer
+ * allocation on the fly, it should be done here. No other part should need
+ * modification.
+ */
+
+static int make_raw_rw_request(void)
+{
+ int aligned_sector_t;
+ int max_sector, max_size, tracksize, ssize;
+
+ if(max_buffer_sectors == 0) {
+ printk("VFS: Block I/O scheduled on unopened device\n");
+ return 0;
+ }
+
+ set_fdc(DRIVE(current_req->rq_dev));
+
+ raw_cmd = &default_raw_cmd;
+ raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK |
+ FD_RAW_NEED_SEEK;
+ raw_cmd->cmd_count = NR_RW;
+ if (rq_data_dir(current_req) == READ) {
+ raw_cmd->flags |= FD_RAW_READ;
+ COMMAND = FM_MODE(_floppy,FD_READ);
+ } else if (rq_data_dir(current_req) == WRITE){
+ raw_cmd->flags |= FD_RAW_WRITE;
+ COMMAND = FM_MODE(_floppy,FD_WRITE);
+ } else {
+ DPRINT("make_raw_rw_request: unknown command\n");
+ return 0;
+ }
+
+ max_sector = _floppy->sect * _floppy->head;
+
+ TRACK = (int)current_req->sector / max_sector;
+ fsector_t = (int)current_req->sector % max_sector;
+ if (_floppy->track && TRACK >= _floppy->track) {
+ if (current_req->current_nr_sectors & 1) {
+ current_count_sectors = 1;
+ return 1;
+ } else
+ return 0;
+ }
+ HEAD = fsector_t / _floppy->sect;
+
+ if (((_floppy->stretch & FD_SWAPSIDES) || TESTF(FD_NEED_TWADDLE)) &&
+ fsector_t < _floppy->sect)
+ max_sector = _floppy->sect;
+
+ /* 2M disks have phantom sectors on the first track */
+ if ((_floppy->rate & FD_2M) && (!TRACK) && (!HEAD)){
+ max_sector = 2 * _floppy->sect / 3;
+ if (fsector_t >= max_sector){
+ current_count_sectors = minimum(_floppy->sect - fsector_t,
+ current_req->nr_sectors);
+ return 1;
+ }
+ SIZECODE = 2;
+ } else
+ SIZECODE = FD_SIZECODE(_floppy);
+ raw_cmd->rate = _floppy->rate & 0x43;
+ if ((_floppy->rate & FD_2M) &&
+ (TRACK || HEAD) &&
+ raw_cmd->rate == 2)
+ raw_cmd->rate = 1;
+
+ if (SIZECODE)
+ SIZECODE2 = 0xff;
+ else
+ SIZECODE2 = 0x80;
+ raw_cmd->track = TRACK << STRETCH(_floppy);
+ DR_SELECT = UNIT(current_drive) + PH_HEAD(_floppy,HEAD);
+ GAP = _floppy->gap;
+ CODE2SIZE;
+ SECT_PER_TRACK = _floppy->sect << 2 >> SIZECODE;
+ SECTOR = ((fsector_t % _floppy->sect) << 2 >> SIZECODE) + 1;
+
+ /* tracksize describes the size which can be filled up with sectors
+ * of size ssize.
+ */
+ tracksize = _floppy->sect - _floppy->sect % ssize;
+ if (tracksize < _floppy->sect){
+ SECT_PER_TRACK ++;
+ if (tracksize <= fsector_t % _floppy->sect)
+ SECTOR--;
+
+ /* if we are beyond tracksize, fill up using smaller sectors */
+ while (tracksize <= fsector_t % _floppy->sect){
+ while(tracksize + ssize > _floppy->sect){
+ SIZECODE--;
+ ssize >>= 1;
+ }
+ SECTOR++; SECT_PER_TRACK ++;
+ tracksize += ssize;
+ }
+ max_sector = HEAD * _floppy->sect + tracksize;
+ } else if (!TRACK && !HEAD && !(_floppy->rate & FD_2M) && probing) {
+ max_sector = _floppy->sect;
+ } else if (!HEAD && CT(COMMAND) == FD_WRITE) {
+ /* for virtual DMA bug workaround */
+ max_sector = _floppy->sect;
+ }
+
+ in_sector_offset = (fsector_t % _floppy->sect) % ssize;
+ aligned_sector_t = fsector_t - in_sector_offset;
+ max_size = current_req->nr_sectors;
+ if ((raw_cmd->track == buffer_track) && + (current_drive == buffer_drive) &&
+ (fsector_t >= buffer_min) && (fsector_t < buffer_max)) {
+ /* data already in track buffer */
+ if (CT(COMMAND) == FD_READ) {
+ copy_buffer(1, max_sector, buffer_max);
+ return 1;
+ }
+ } else if (in_sector_offset || current_req->nr_sectors < ssize){
+ if (CT(COMMAND) == FD_WRITE){
+ if (fsector_t + current_req->nr_sectors > ssize &&
+ fsector_t + current_req->nr_sectors < ssize + ssize)
+ max_size = ssize + ssize;
+ else
+ max_size = ssize;
+ }
+ raw_cmd->flags &= ~FD_RAW_WRITE;
+ raw_cmd->flags |= FD_RAW_READ;
+ COMMAND = FM_MODE(_floppy,FD_READ);
+ } else if ((unsigned long)current_req->buffer < MAX_DMA_ADDRESS) {
+ unsigned long dma_limit;
+ int direct, indirect;
+
+ indirect= transfer_size(ssize,max_sector,max_buffer_sectors*2) -
+ fsector_t;
+
+ /*
+ * Do NOT use minimum() here---MAX_DMA_ADDRESS is 64 bits wide
+ * on a 64 bit machine!
+ */
+ max_size = buffer_chain_size();
+ dma_limit = (MAX_DMA_ADDRESS - ((unsigned long) current_req->buffer)) >> 9;
+ if ((unsigned long) max_size > dma_limit) {
+ max_size = dma_limit;
+ }
+ /* 64 kb boundaries */
+ if (CROSS_64KB(current_req->buffer, max_size << 9))
+ max_size = (K_64 - + ((unsigned long)current_req->buffer) % K_64)>>9;
+ direct = transfer_size(ssize,max_sector,max_size) - fsector_t;
+ /*
+ * We try to read tracks, but if we get too many errors, we
+ * go back to reading just one sector at a time.
+ *
+ * This means we should be able to read a sector even if there
+ * are other bad sectors on this track.
+ */
+ if (!direct ||
+ (indirect * 2 > direct * 3 &&
+ *errors < DP->max_errors.read_track &&
+ /*!TESTF(FD_NEED_TWADDLE) &&*/
+ ((!probing || (DP->read_track&(1<<DRS->probed_format)))))){
+ max_size = current_req->nr_sectors;
+ } else {
+ raw_cmd->kernel_data = current_req->buffer;
+ raw_cmd->length = current_count_sectors << 9;
+ if (raw_cmd->length == 0){
+ DPRINT("zero dma transfer attempted from make_raw_request\n");
+ DPRINT("indirect=%d direct=%d fsector_t=%d",
+ indirect, direct, fsector_t);
+ return 0;
+ }
+/* check_dma_crossing(raw_cmd->kernel_data, + raw_cmd->length,
+ "end of make_raw_request [1]");*/
+
+ virtualdmabug_workaround();
+ return 2;
+ }
+ }
+
+ if (CT(COMMAND) == FD_READ)
+ max_size = max_sector; /* unbounded */
+
+ /* claim buffer track if needed */
+ if (buffer_track != raw_cmd->track || /* bad track */
+ buffer_drive !=current_drive || /* bad drive */
+ fsector_t > buffer_max ||
+ fsector_t < buffer_min ||
+ ((CT(COMMAND) == FD_READ ||
+ (!in_sector_offset && current_req->nr_sectors >= ssize))&&
+ max_sector > 2 * max_buffer_sectors + buffer_min &&
+ max_size + fsector_t > 2 * max_buffer_sectors + buffer_min)
+ /* not enough space */){
+ buffer_track = -1;
+ buffer_drive = current_drive;
+ buffer_max = buffer_min = aligned_sector_t;
+ }
+ raw_cmd->kernel_data = floppy_track_buffer + + ((aligned_sector_t-buffer_min)<<9);
+
+ if (CT(COMMAND) == FD_WRITE){
+ /* copy write buffer to track buffer.
+ * if we get here, we know that the write
+ * is either aligned or the data already in the buffer
+ * (buffer will be overwritten) */
+#ifdef FLOPPY_SANITY_CHECK
+ if (in_sector_offset && buffer_track == -1)
+ DPRINT("internal error offset !=0 on write\n");
+#endif
+ buffer_track = raw_cmd->track;
+ buffer_drive = current_drive;
+ copy_buffer(ssize, max_sector, 2*max_buffer_sectors+buffer_min);
+ } else
+ transfer_size(ssize, max_sector,
+ 2*max_buffer_sectors+buffer_min-aligned_sector_t);
+
+ /* round up current_count_sectors to get dma xfer size */
+ raw_cmd->length = in_sector_offset+current_count_sectors;
+ raw_cmd->length = ((raw_cmd->length -1)|(ssize-1))+1;
+ raw_cmd->length <<= 9;
+#ifdef FLOPPY_SANITY_CHECK
+ /*check_dma_crossing(raw_cmd->kernel_data, raw_cmd->length, + "end of make_raw_request");*/
+ if ((raw_cmd->length < current_count_sectors << 9) ||
+ (raw_cmd->kernel_data != current_req->buffer &&
+ CT(COMMAND) == FD_WRITE &&
+ (aligned_sector_t + (raw_cmd->length >> 9) > buffer_max ||
+ aligned_sector_t < buffer_min)) ||
+ raw_cmd->length % (128 << SIZECODE) ||
+ raw_cmd->length <= 0 || current_count_sectors <= 0){
+ DPRINT("fractionary current count b=%lx s=%lx\n",
+ raw_cmd->length, current_count_sectors);
+ if (raw_cmd->kernel_data != current_req->buffer)
+ printk("addr=%d, length=%ld\n",
+ (int) ((raw_cmd->kernel_data - + floppy_track_buffer) >> 9),
+ current_count_sectors);
+ printk("st=%d ast=%d mse=%d msi=%d\n",
+ fsector_t, aligned_sector_t, max_sector, max_size);
+ printk("ssize=%x SIZECODE=%d\n", ssize, SIZECODE);
+ printk("command=%x SECTOR=%d HEAD=%d, TRACK=%d\n",
+ COMMAND, SECTOR, HEAD, TRACK);
+ printk("buffer drive=%d\n", buffer_drive);
+ printk("buffer track=%d\n", buffer_track);
+ printk("buffer_min=%d\n", buffer_min);
+ printk("buffer_max=%d\n", buffer_max);
+ return 0;
+ }
+
+ if (raw_cmd->kernel_data != current_req->buffer){
+ if (raw_cmd->kernel_data < floppy_track_buffer ||
+ current_count_sectors < 0 ||
+ raw_cmd->length < 0 ||
+ raw_cmd->kernel_data + raw_cmd->length >
+ floppy_track_buffer + (max_buffer_sectors << 10)){
+ DPRINT("buffer overrun in schedule dma\n");
+ printk("fsector_t=%d buffer_min=%d current_count=%ld\n",
+ fsector_t, buffer_min,
+ raw_cmd->length >> 9);
+ printk("current_count_sectors=%ld\n",
+ current_count_sectors);
+ if (CT(COMMAND) == FD_READ)
+ printk("read\n");
+ if (CT(COMMAND) == FD_READ)
+ printk("write\n");
+ return 0;
+ }
+ } else if (raw_cmd->length > current_req->nr_sectors << 9 ||
+ current_count_sectors > current_req->nr_sectors){
+ DPRINT("buffer overrun in direct transfer\n");
+ return 0;
+ } else if (raw_cmd->length < current_count_sectors << 9){
+ DPRINT("more sectors than bytes\n");
+ printk("bytes=%ld\n", raw_cmd->length >> 9);
+ printk("sectors=%ld\n", current_count_sectors);
+ }
+ if (raw_cmd->length == 0){
+ DPRINT("zero dma transfer attempted from make_raw_request\n");
+ return 0;
+ }
+#endif
+
+ virtualdmabug_workaround();
+ return 2;
+}
+
+static void redo_fd_request(void)
+{
+#define REPEAT {request_done(0); continue; }
+ kdev_t device;
+ int tmp;
+
+ lastredo = jiffies;
+ if (current_drive < N_DRIVE)
+ floppy_off(current_drive);
+
+ for (;;) {
+ if (!current_req) {
+ struct request *req = elv_next_request(&floppy_queue);
+ if (!req) {
+ do_floppy = NULL;
+ unlock_fdc();
+ return;
+ }
+ current_req = req;
+ }
+ device = current_req->rq_dev;
+ set_fdc(DRIVE(device));
+ reschedule_timeout(current_reqD, "redo fd request", 0);
+
+ set_floppy(device);
+ raw_cmd = & default_raw_cmd;
+ raw_cmd->flags = 0;
+ if (start_motor(redo_fd_request)) return;
+ disk_change(current_drive);
+ if (test_bit(current_drive, &fake_change) ||
+ TESTF(FD_DISK_CHANGED)){
+ DPRINT("disk absent or changed during operation\n");
+ REPEAT;
+ }
+ if (!_floppy) { /* Autodetection */
+ if (!probing){
+ DRS->probed_format = 0;
+ if (next_valid_format()){
+ DPRINT("no autodetectable formats\n");
+ _floppy = NULL;
+ REPEAT;
+ }
+ }
+ probing = 1;
+ _floppy = floppy_type+DP->autodetect[DRS->probed_format];
+ } else
+ probing = 0;
+ errors = & (current_req->errors);
+ tmp = make_raw_rw_request();
+ if (tmp < 2){
+ request_done(tmp);
+ continue;
+ }
+
+ if (TESTF(FD_NEED_TWADDLE))
+ twaddle();
+ schedule_bh( (void *)(void *) floppy_start);
+#ifdef DEBUGT
+ debugt("queue fd request");
+#endif
+ return;
+ }
+#undef REPEAT
+}
+
+static struct cont_t rw_cont={
+ rw_interrupt,
+ redo_fd_request,
+ bad_flp_intr,
+ request_done };
+
+static void process_fd_request(void)
+{
+ cont = &rw_cont;
+ schedule_bh( (void *)(void *) redo_fd_request);
+}
+
+static void do_fd_request(request_queue_t * q)
+{
+ if(max_buffer_sectors == 0) {
+ printk("VFS: do_fd_request called on non-open device\n");
+ return;
+ }
+
+ if (usage_count == 0) {
+ printk("warning: usage count=0, current_req=%p exiting\n", current_req);
+ printk("sect=%ld flags=%lx\n", (long)current_req->sector, current_req->flags);
+ return;
+ }
+ if (fdc_busy){
+ /* fdc busy, this new request will be treated when the
+ current one is done */
+ is_alive("do fd request, old request running");
+ return;
+ }
+ lock_fdc(MAXTIMEOUT,0);
+ process_fd_request();
+ is_alive("do fd request");
+}
+
+static struct cont_t poll_cont={
+ success_and_wakeup,
+ floppy_ready,
+ generic_failure,
+ generic_done };
+
+static int poll_drive(int interruptible, int flag)
+{
+ int ret;
+ /* no auto-sense, just clear dcl */
+ raw_cmd = &default_raw_cmd;
+ raw_cmd->flags= flag;
+ raw_cmd->track=0;
+ raw_cmd->cmd_count=0;
+ cont = &poll_cont;
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("setting NEWCHANGE in poll_drive\n");
+ }
+#endif
+ SETF(FD_DISK_NEWCHANGE);
+ WAIT(floppy_ready);
+ return ret;
+}
+
+/*
+ * User triggered reset
+ * ====================
+ */
+
+static void reset_intr(void)
+{
+ printk("weird, reset interrupt called\n");
+}
+
+static struct cont_t reset_cont={
+ reset_intr,
+ success_and_wakeup,
+ generic_failure,
+ generic_done };
+
+static int user_reset_fdc(int drive, int arg, int interruptible)
+{
+ int ret;
+
+ ret=0;
+ LOCK_FDC(drive,interruptible);
+ if (arg == FD_RESET_ALWAYS)
+ FDCS->reset=1;
+ if (FDCS->reset){
+ cont = &reset_cont;
+ WAIT(reset_fdc);
+ }
+ process_fd_request();
+ return ret;
+}
+
+/*
+ * Misc Ioctl's and support
+ * ========================
+ */
+static inline int fd_copyout(void *param, const void *address, unsigned long size)
+{
+ return copy_to_user(param,address, size) ? -EFAULT : 0;
+}
+
+static inline int fd_copyin(void *param, void *address, unsigned long size)
+{
+ return copy_from_user(address, param, size) ? -EFAULT : 0;
+}
+
+#define _COPYOUT(x) (copy_to_user((void *)param, &(x), sizeof(x)) ? -EFAULT : 0)
+#define _COPYIN(x) (copy_from_user(&(x), (void *)param, sizeof(x)) ? -EFAULT : 0)
+
+#define COPYOUT(x) ECALL(_COPYOUT(x))
+#define COPYIN(x) ECALL(_COPYIN(x))
+
+static inline const char *drive_name(int type, int drive)
+{
+ struct floppy_struct *floppy;
+
+ if (type)
+ floppy = floppy_type + type;
+ else {
+ if (UDP->native_format)
+ floppy = floppy_type + UDP->native_format;
+ else
+ return "(null)";
+ }
+ if (floppy->name)
+ return floppy->name;
+ else
+ return "(null)";
+}
+
+
+/* raw commands */
+static void raw_cmd_done(int flag)
+{
+ int i;
+
+ if (!flag) {
+ raw_cmd->flags |= FD_RAW_FAILURE;
+ raw_cmd->flags |= FD_RAW_HARDFAILURE;
+ } else {
+ raw_cmd->reply_count = inr;
+ if (raw_cmd->reply_count > MAX_REPLIES)
+ raw_cmd->reply_count=0;
+ for (i=0; i< raw_cmd->reply_count; i++)
+ raw_cmd->reply[i] = reply_buffer[i];
+
+ if (raw_cmd->flags & (FD_RAW_READ | FD_RAW_WRITE))
+ {
+ unsigned long flags;
+ flags=claim_dma_lock();
+ raw_cmd->length = fd_get_dma_residue();
+ release_dma_lock(flags);
+ }
+
+ if ((raw_cmd->flags & FD_RAW_SOFTFAILURE) &&
+ (!raw_cmd->reply_count || (raw_cmd->reply[0] & 0xc0)))
+ raw_cmd->flags |= FD_RAW_FAILURE;
+
+ if (disk_change(current_drive))
+ raw_cmd->flags |= FD_RAW_DISK_CHANGE;
+ else
+ raw_cmd->flags &= ~FD_RAW_DISK_CHANGE;
+ if (raw_cmd->flags & FD_RAW_NO_MOTOR_AFTER)
+ motor_off_callback(current_drive);
+
+ if (raw_cmd->next &&
+ (!(raw_cmd->flags & FD_RAW_FAILURE) ||
+ !(raw_cmd->flags & FD_RAW_STOP_IF_FAILURE)) &&
+ ((raw_cmd->flags & FD_RAW_FAILURE) ||
+ !(raw_cmd->flags &FD_RAW_STOP_IF_SUCCESS))) {
+ raw_cmd = raw_cmd->next;
+ return;
+ }
+ }
+ generic_done(flag);
+}
+
+
+static struct cont_t raw_cmd_cont={
+ success_and_wakeup,
+ floppy_start,
+ generic_failure,
+ raw_cmd_done
+};
+
+static inline int raw_cmd_copyout(int cmd, char *param,
+ struct floppy_raw_cmd *ptr)
+{
+ int ret;
+
+ while(ptr) {
+ COPYOUT(*ptr);
+ param += sizeof(struct floppy_raw_cmd);
+ if ((ptr->flags & FD_RAW_READ) && ptr->buffer_length){
+ if (ptr->length>=0 && ptr->length<=ptr->buffer_length)
+ ECALL(fd_copyout(ptr->data, + ptr->kernel_data,
+ ptr->buffer_length - + ptr->length));
+ }
+ ptr = ptr->next;
+ }
+ return 0;
+}
+
+
+static void raw_cmd_free(struct floppy_raw_cmd **ptr)
+{
+ struct floppy_raw_cmd *next,*this;
+
+ this = *ptr;
+ *ptr = 0;
+ while(this) {
+ if (this->buffer_length) {
+ fd_dma_mem_free((unsigned long)this->kernel_data,
+ this->buffer_length);
+ this->buffer_length = 0;
+ }
+ next = this->next;
+ kfree(this);
+ this = next;
+ }
+}
+
+
+static inline int raw_cmd_copyin(int cmd, char *param,
+ struct floppy_raw_cmd **rcmd)
+{
+ struct floppy_raw_cmd *ptr;
+ int ret;
+ int i;
+
+ *rcmd = 0;
+ while(1) {
+ ptr = (struct floppy_raw_cmd *) + kmalloc(sizeof(struct floppy_raw_cmd), GFP_USER);
+ if (!ptr)
+ return -ENOMEM;
+ *rcmd = ptr;
+ COPYIN(*ptr);
+ ptr->next = 0;
+ ptr->buffer_length = 0;
+ param += sizeof(struct floppy_raw_cmd);
+ if (ptr->cmd_count > 33)
+ /* the command may now also take up the space
+ * initially intended for the reply & the
+ * reply count. Needed for long 82078 commands
+ * such as RESTORE, which takes ... 17 command
+ * bytes. Murphy's law #137: When you reserve
+ * 16 bytes for a structure, you'll one day
+ * discover that you really need 17...
+ */
+ return -EINVAL;
+
+ for (i=0; i< 16; i++)
+ ptr->reply[i] = 0;
+ ptr->resultcode = 0;
+ ptr->kernel_data = 0;
+
+ if (ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
+ if (ptr->length <= 0)
+ return -EINVAL;
+ ptr->kernel_data =(char*)fd_dma_mem_alloc(ptr->length);
+ fallback_on_nodma_alloc(&ptr->kernel_data,
+ ptr->length);
+ if (!ptr->kernel_data)
+ return -ENOMEM;
+ ptr->buffer_length = ptr->length;
+ }
+ if (ptr->flags & FD_RAW_WRITE)
+ ECALL(fd_copyin(ptr->data, ptr->kernel_data, + ptr->length));
+ rcmd = & (ptr->next);
+ if (!(ptr->flags & FD_RAW_MORE))
+ return 0;
+ ptr->rate &= 0x43;
+ }
+}
+
+
+static int raw_cmd_ioctl(int cmd, void *param)
+{
+ int drive, ret, ret2;
+ struct floppy_raw_cmd *my_raw_cmd;
+
+ if (FDCS->rawcmd <= 1)
+ FDCS->rawcmd = 1;
+ for (drive= 0; drive < N_DRIVE; drive++){
+ if (FDC(drive) != fdc)
+ continue;
+ if (drive == current_drive){
+ if (UDRS->fd_ref > 1){
+ FDCS->rawcmd = 2;
+ break;
+ }
+ } else if (UDRS->fd_ref){
+ FDCS->rawcmd = 2;
+ break;
+ }
+ }
+
+ if (FDCS->reset)
+ return -EIO;
+
+ ret = raw_cmd_copyin(cmd, param, &my_raw_cmd);
+ if (ret) {
+ raw_cmd_free(&my_raw_cmd);
+ return ret;
+ }
+
+ raw_cmd = my_raw_cmd;
+ cont = &raw_cmd_cont;
+ ret=wait_til_done(floppy_start,1);
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("calling disk change from raw_cmd ioctl\n");
+ }
+#endif
+
+ if (ret != -EINTR && FDCS->reset)
+ ret = -EIO;
+
+ DRS->track = NO_TRACK;
+
+ ret2 = raw_cmd_copyout(cmd, param, my_raw_cmd);
+ if (!ret)
+ ret = ret2;
+ raw_cmd_free(&my_raw_cmd);
+ return ret;
+}
+
+static int invalidate_drive(struct block_device *bdev)
+{
+ /* invalidate the buffer track to force a reread */
+ set_bit(DRIVE(to_kdev_t(bdev->bd_dev)), &fake_change);
+ process_fd_request();
+ check_disk_change(bdev);
+ return 0;
+}
+
+
+static inline void clear_write_error(int drive)
+{
+ CLEARSTRUCT(UDRWE);
+}
+
+static inline int set_geometry(unsigned int cmd, struct floppy_struct *g,
+ int drive, int type, struct block_device *bdev)
+{
+ int cnt;
+
+ /* sanity checking for parameters.*/
+ if (g->sect <= 0 ||
+ g->head <= 0 ||
+ g->track <= 0 ||
+ g->track > UDP->tracks>>STRETCH(g) ||
+ /* check if reserved bits are set */
+ (g->stretch&~(FD_STRETCH|FD_SWAPSIDES)) != 0)
+ return -EINVAL;
+ if (type){
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ LOCK_FDC(drive,1);
+ for (cnt = 0; cnt < N_DRIVE; cnt++){
+ if (ITYPE(drive_state[cnt].fd_device) == type &&
+ drive_state[cnt].fd_ref)
+ set_bit(drive, &fake_change);
+ }
+ floppy_type[type] = *g;
+ floppy_type[type].name="user format";
+ for (cnt = type << 2; cnt < (type << 2) + 4; cnt++)
+ floppy_sizes[cnt]= floppy_sizes[cnt+0x80]=
+ floppy_type[type].size+1;
+ process_fd_request();
+ for (cnt = 0; cnt < N_DRIVE; cnt++){
+ if (ITYPE(drive_state[cnt].fd_device) == type &&
+ drive_state[cnt].fd_ref)
+ __check_disk_change(
+ MKDEV(FLOPPY_MAJOR,
+ drive_state[cnt].fd_device));
+ }
+ } else {
+ LOCK_FDC(drive,1);
+ if (cmd != FDDEFPRM)
+ /* notice a disk change immediately, else
+ * we lose our settings immediately*/
+ CALL(poll_drive(1, FD_RAW_NEED_DISK));
+ user_params[drive] = *g;
+ if (buffer_drive == drive)
+ SUPBOUND(buffer_max, user_params[drive].sect);
+ current_type[drive] = &user_params[drive];
+ floppy_sizes[drive] = user_params[drive].size;
+ if (cmd == FDDEFPRM)
+ DRS->keep_data = -1;
+ else
+ DRS->keep_data = 1;
+ /* invalidation. Invalidate only when needed, i.e.
+ * when there are already sectors in the buffer cache
+ * whose number will change. This is useful, because
+ * mtools often changes the geometry of the disk after
+ * looking at the boot block */
+ if (DRS->maxblock > user_params[drive].sect || DRS->maxtrack)
+ invalidate_drive(bdev);
+ else
+ process_fd_request();
+ }
+ return 0;
+}
+
+/* handle obsolete ioctl's */
+static int ioctl_table[]= {
+ FDCLRPRM,
+ FDSETPRM,
+ FDDEFPRM,
+ FDGETPRM,
+ FDMSGON,
+ FDMSGOFF,
+ FDFMTBEG,
+ FDFMTTRK,
+ FDFMTEND,
+ FDSETEMSGTRESH,
+ FDFLUSH,
+ FDSETMAXERRS,
+ FDGETMAXERRS,
+ FDGETDRVTYP,
+ FDSETDRVPRM,
+ FDGETDRVPRM,
+ FDGETDRVSTAT,
+ FDPOLLDRVSTAT,
+ FDRESET,
+ FDGETFDCSTAT,
+ FDWERRORCLR,
+ FDWERRORGET,
+ FDRAWCMD,
+ FDEJECT,
+ FDTWADDLE
+};
+
+static inline int normalize_ioctl(int *cmd, int *size)
+{
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(ioctl_table); i++) {
+ if ((*cmd & 0xffff) == (ioctl_table[i] & 0xffff)){
+ *size = _IOC_SIZE(*cmd);
+ *cmd = ioctl_table[i];
+ if (*size > _IOC_SIZE(*cmd)) {
+ printk("ioctl not yet supported\n");
+ return -EFAULT;
+ }
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int get_floppy_geometry(int drive, int type, struct floppy_struct **g)
+{
+ if (type)
+ *g = &floppy_type[type];
+ else {
+ LOCK_FDC(drive,0);
+ CALL(poll_drive(0,0));
+ process_fd_request();
+ *g = current_type[drive];
+ }
+ if (!*g)
+ return -ENODEV;
+ return 0;
+}
+
+static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long param)
+{
+#define FD_IOCTL_ALLOWED ((filp) && (filp)->private_data)
+#define OUT(c,x) case c: outparam = (const char *) (x); break
+#define IN(c,x,tag) case c: *(x) = inparam. tag ; return 0
+
+ int i,drive,type;
+ kdev_t device;
+ int ret;
+ int size;
+ union inparam {
+ struct floppy_struct g; /* geometry */
+ struct format_descr f;
+ struct floppy_max_errors max_errors;
+ struct floppy_drive_params dp;
+ } inparam; /* parameters coming from user space */
+ const char *outparam; /* parameters passed back to user space */
+
+ device = inode->i_rdev;
+ type = TYPE(device);
+ drive = DRIVE(device);
+
+ /* convert compatibility eject ioctls into floppy eject ioctl.
+ * We do this in order to provide a means to eject floppy disks before
+ * installing the new fdutils package */
+ if (cmd == CDROMEJECT || /* CD-ROM eject */
+ cmd == 0x6470 /* SunOS floppy eject */) {
+ DPRINT("obsolete eject ioctl\n");
+ DPRINT("please use floppycontrol --eject\n");
+ cmd = FDEJECT;
+ }
+
+ /* generic block device ioctls */
+ switch(cmd) {
+ /* the following have been inspired by the corresponding
+ * code for other block devices. */
+ struct floppy_struct *g;
+ case HDIO_GETGEO:
+ {
+ struct hd_geometry loc;
+ ECALL(get_floppy_geometry(drive, type, &g));
+ loc.heads = g->head;
+ loc.sectors = g->sect;
+ loc.cylinders = g->track;
+ loc.start = 0;
+ return _COPYOUT(loc);
+ }
+ }
+
+ /* convert the old style command into a new style command */
+ if ((cmd & 0xff00) == 0x0200) {
+ ECALL(normalize_ioctl(&cmd, &size));
+ } else
+ return -EINVAL;
+
+ /* permission checks */
+ if (((cmd & 0x40) && !FD_IOCTL_ALLOWED) ||
+ ((cmd & 0x80) && !capable(CAP_SYS_ADMIN)))
+ return -EPERM;
+
+ /* copyin */
+ CLEARSTRUCT(&inparam);
+ if (_IOC_DIR(cmd) & _IOC_WRITE)
+ ECALL(fd_copyin((void *)param, &inparam, size))
+
+ switch (cmd) {
+ case FDEJECT:
+ if (UDRS->fd_ref != 1)
+ /* somebody else has this drive open */
+ return -EBUSY;
+ LOCK_FDC(drive,1);
+
+ /* do the actual eject. Fails on
+ * non-Sparc architectures */
+ ret=fd_eject(UNIT(drive));
+
+ USETF(FD_DISK_CHANGED);
+ USETF(FD_VERIFY);
+ process_fd_request();
+ return ret;
+ case FDCLRPRM:
+ LOCK_FDC(drive,1);
+ current_type[drive] = NULL;
+ floppy_sizes[drive] = MAX_DISK_SIZE << 1;
+ UDRS->keep_data = 0;
+ return invalidate_drive(inode->i_bdev);
+ case FDSETPRM:
+ case FDDEFPRM:
+ return set_geometry(cmd, & inparam.g,
+ drive, type, inode->i_bdev);
+ case FDGETPRM:
+ ECALL(get_floppy_geometry(drive, type, + (struct floppy_struct**)
+ &outparam));
+ break;
+
+ case FDMSGON:
+ UDP->flags |= FTD_MSG;
+ return 0;
+ case FDMSGOFF:
+ UDP->flags &= ~FTD_MSG;
+ return 0;
+
+ case FDFMTBEG:
+ LOCK_FDC(drive,1);
+ CALL(poll_drive(1, FD_RAW_NEED_DISK));
+ ret = UDRS->flags;
+ if (ret & FD_VERIFY) {
+ CALL(poll_drive(1, FD_RAW_NEED_DISK));
+ ret = UDRS->flags;
+ }
+
+ if (ret & FD_VERIFY) {
+ CALL(poll_drive(1, FD_RAW_NEED_DISK));
+ ret = UDRS->flags;
+ }
+
+ if (ret & FD_VERIFY) {
+ CALL(poll_drive(1, FD_RAW_NEED_DISK));
+ ret = UDRS->flags;
+ }
+
+ if (ret & FD_VERIFY) {
+ CALL(poll_drive(1, FD_RAW_NEED_DISK));
+ ret = UDRS->flags;
+ }
+
+ if(ret & FD_VERIFY){
+ CALL(poll_drive(1, FD_RAW_NEED_DISK));
+ ret = UDRS->flags;
+ }
+ process_fd_request();
+ if (ret & FD_VERIFY)
+ return -ENODEV;
+ if (!(ret & FD_DISK_WRITABLE))
+ return -EROFS;
+ return 0;
+ case FDFMTTRK:
+ if (UDRS->fd_ref != 1)
+ return -EBUSY;
+ return do_format(device, &inparam.f);
+ case FDFMTEND:
+ case FDFLUSH:
+ LOCK_FDC(drive,1);
+ return invalidate_drive(inode->i_bdev);
+
+ case FDSETEMSGTRESH:
+ UDP->max_errors.reporting =
+ (unsigned short) (param & 0x0f);
+ return 0;
+ OUT(FDGETMAXERRS, &UDP->max_errors);
+ IN(FDSETMAXERRS, &UDP->max_errors, max_errors);
+
+ case FDGETDRVTYP:
+ outparam = drive_name(type,drive);
+ SUPBOUND(size,strlen(outparam)+1);
+ break;
+
+ IN(FDSETDRVPRM, UDP, dp);
+ OUT(FDGETDRVPRM, UDP);
+
+ case FDPOLLDRVSTAT:
+ LOCK_FDC(drive,1);
+ CALL(poll_drive(1, FD_RAW_NEED_DISK));
+ process_fd_request();
+ /* fall through */
+ OUT(FDGETDRVSTAT, UDRS);
+
+ case FDRESET:
+ return user_reset_fdc(drive, (int)param, 1);
+
+ OUT(FDGETFDCSTAT,UFDCS);
+
+ case FDWERRORCLR:
+ CLEARSTRUCT(UDRWE);
+ return 0;
+ OUT(FDWERRORGET,UDRWE);
+
+ case FDRAWCMD:
+ if (type)
+ return -EINVAL;
+ LOCK_FDC(drive,1);
+ set_floppy(device);
+ CALL(i = raw_cmd_ioctl(cmd,(void *) param));
+ process_fd_request();
+ return i;
+
+ case FDTWADDLE:
+ LOCK_FDC(drive,1);
+ twaddle();
+ process_fd_request();
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ return fd_copyout((void *)param, outparam, size);
+ else
+ return 0;
+#undef OUT
+#undef IN
+}
+
+static void __init config_types(void)
+{
+ int first=1;
+ int drive;
+ extern struct fd_info {
+ unsigned char dummy[4 * 6];
+ unsigned char fd_types[8];
+ } drive_info;
+
+ for (drive = 0; drive < 4; drive++)
+ UDP->cmos = drive_info.fd_types[drive];
+
+ /* XXX */
+ /* additional physical CMOS drive detection should go here */
+
+ for (drive=0; drive < N_DRIVE; drive++){
+ unsigned int type = UDP->cmos;
+ struct floppy_drive_params *params;
+ const char *name = NULL;
+ static char temparea[32];
+
+ if (type < NUMBER(default_drive_params)) {
+ params = &default_drive_params[type].params;
+ if (type) {
+ name = default_drive_params[type].name;
+ allowed_drive_mask |= 1 << drive;
+ }
+ } else {
+ params = &default_drive_params[0].params;
+ sprintf(temparea, "unknown type %d (usb?)", type);
+ name = temparea;
+ }
+ if (name) {
+ const char * prepend = ",";
+ if (first) {
+ prepend = KERN_INFO "Floppy drive(s):";
+ first = 0;
+ }
+ printk("%s fd%d is %s", prepend, drive, name);
+ register_devfs_entries (drive);
+ }
+ *UDP = *params;
+ }
+ if (!first)
+ printk("\n");
+}
+
+static int floppy_release(struct inode * inode, struct file * filp)
+{
+ int drive = DRIVE(inode->i_rdev);
+
+ if (UDRS->fd_ref < 0)
+ UDRS->fd_ref=0;
+ else if (!UDRS->fd_ref--) {
+ DPRINT("floppy_release with fd_ref == 0");
+ UDRS->fd_ref = 0;
+ }
+ floppy_release_irq_and_dma();
+ return 0;
+}
+
+/*
+ * floppy_open check for aliasing (/dev/fd0 can be the same as
+ * /dev/PS0 etc), and disallows simultaneous access to the same
+ * drive with different device numbers.
+ */
+#define RETERR(x) do{floppy_release(inode,filp); return -(x);}while(0)
+
+static int floppy_open(struct inode * inode, struct file * filp)
+{
+ int drive;
+ int old_dev;
+ int try;
+ char *tmp;
+
+#ifdef PC9800_DEBUG_FLOPPY
+ printk("floppy open: start\n");
+#endif
+ filp->private_data = (void*) 0;
+
+ drive = DRIVE(inode->i_rdev);
+#ifdef PC9800_DEBUG_FLOPPY
+ printk("floppy open: drive=%d, current_drive=%d, UDP->cmos=%d\n"
+ "floppy open: FDCS={spec1=%d, spec2=%d, dtr=%d, version=%d, dor=%d, address=%lu}\n",
+ drive, current_drive, UDP->cmos, FDCS->spec1, FDCS->spec2,
+ FDCS->dtr, FDCS->version, FDCS->dor, FDCS->address);
+ if (_floppy) {
+ printk("floppy open: _floppy={size=%d, sect=%d, head=%d, track=%d, spec1=%d}\n",
+ _floppy->size, _floppy->sect, _floppy->head,
+ _floppy->track, _floppy->spec1);
+ } else {
+ printk("floppy open: _floppy=NULL\n");
+ }
+#endif /* PC9800_DEBUG_FLOPPY */
+
+ if (drive >= N_DRIVE ||
+ !(allowed_drive_mask & (1 << drive)) ||
+ fdc_state[FDC(drive)].version == FDC_NONE)
+ return -ENXIO;
+
+ if (TYPE(inode->i_rdev) >= NUMBER(floppy_type))
+ return -ENXIO;
+ old_dev = UDRS->fd_device;
+ if (UDRS->fd_ref && old_dev != minor(inode->i_rdev))
+ return -EBUSY;
+
+ if (!UDRS->fd_ref && (UDP->flags & FD_BROKEN_DCL)){
+ USETF(FD_DISK_CHANGED);
+ USETF(FD_VERIFY);
+ }
+
+ if (UDRS->fd_ref == -1 ||
+ (UDRS->fd_ref && (filp->f_flags & O_EXCL)))
+ return -EBUSY;
+
+ if (floppy_grab_irq_and_dma())
+ return -EBUSY;
+
+ if (filp->f_flags & O_EXCL)
+ UDRS->fd_ref = -1;
+ else
+ UDRS->fd_ref++;
+
+ if (!floppy_track_buffer){
+ /* if opening an ED drive, reserve a big buffer,
+ * else reserve a small one */
+ if ((UDP->cmos == 6) || (UDP->cmos == 5))
+ try = 64; /* Only 48 actually useful */
+ else
+ try = 32; /* Only 24 actually useful */
+
+ tmp=(char *)fd_dma_mem_alloc(1024 * try);
+ if (!tmp && !floppy_track_buffer) {
+ try >>= 1; /* buffer only one side */
+ INFBOUND(try, 16);
+ tmp= (char *)fd_dma_mem_alloc(1024*try);
+ }
+ if (!tmp && !floppy_track_buffer) {
+ fallback_on_nodma_alloc(&tmp, 2048 * try);
+ }
+ if (!tmp && !floppy_track_buffer) {
+ DPRINT("Unable to allocate DMA memory\n");
+ RETERR(ENXIO);
+ }
+ if (floppy_track_buffer) {
+ if (tmp)
+ fd_dma_mem_free((unsigned long)tmp,try*1024);
+ } else {
+ buffer_min = buffer_max = -1;
+ floppy_track_buffer = tmp;
+ max_buffer_sectors = try;
+ }
+ }
+
+ UDRS->fd_device = minor(inode->i_rdev);
+ set_capacity(disks[drive], floppy_sizes[minor(inode->i_rdev)]);
+ if (old_dev != -1 && old_dev != minor(inode->i_rdev)) {
+ if (buffer_drive == drive)
+ buffer_track = -1;
+ /* umm, invalidate_buffers() in ->open?? --hch */
+ invalidate_buffers(mk_kdev(FLOPPY_MAJOR,old_dev));
+ }
+
+#ifdef PC9800_DEBUG_FLOPPY
+ printk("floppy open: floppy.c:%d passed\n", __LINE__);
+#endif
+
+
+ /* Allow ioctls if we have write-permissions even if read-only open.
+ * Needed so that programs such as fdrawcmd still can work on write
+ * protected disks */
+ if ((filp->f_mode & 2) || + (inode->i_sb && (permission(inode,2) == 0)))
+ filp->private_data = (void*) 8;
+
+ if (UFDCS->rawcmd == 1)
+ UFDCS->rawcmd = 2;
+
+#ifdef PC9800_DEBUG_FLOPPY
+ printk("floppy open: floppy.c:%d passed\n", __LINE__);
+#endif
+
+ if (filp->f_flags & O_NDELAY)
+ return 0;
+ if (filp->f_mode & 3) {
+ UDRS->last_checked = 0;
+ check_disk_change(inode->i_bdev);
+ if (UTESTF(FD_DISK_CHANGED))
+ RETERR(ENXIO);
+ }
+ if ((filp->f_mode & 2) && !(UTESTF(FD_DISK_WRITABLE)))
+ RETERR(EROFS);
+#ifdef PC9800_DEBUG_FLOPPY
+ printk("floppy open: end normally\n");
+#endif
+
+ return 0;
+#undef RETERR
+}
+
+/*
+ * Check if the disk has been changed or if a change has been faked.
+ */
+static int check_floppy_change(struct gendisk *disk)
+{
+ int drive = (int)disk->private_data;
+
+#ifdef PC9800_DEBUG_FLOPPY
+ printk("check_floppy_change: MINOR=%d\n", minor(dev));
+#endif
+
+ if (UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY))
+ return 1;
+
+ if (UDP->checkfreq < (int)(jiffies - UDRS->last_checked)) {
+ if(floppy_grab_irq_and_dma()) {
+ return 1;
+ }
+
+ lock_fdc(drive,0);
+ poll_drive(0,0);
+ process_fd_request();
+ floppy_release_irq_and_dma();
+ }
+
+ if (UTESTF(FD_DISK_CHANGED) ||
+ UTESTF(FD_VERIFY) ||
+ test_bit(drive, &fake_change) ||
+ (!ITYPE(UDRS->fd_device) && !current_type[drive]))
+ return 1;
+ return 0;
+}
+
+/*
+ * This implements "read block 0" for floppy_revalidate().
+ * Needed for format autodetection, checking whether there is
+ * a disk in the drive, and whether that disk is writable.
+ */
+
+static int floppy_rb0_complete(struct bio *bio, unsigned int bytes_done, int err)
+{
+ if (bio->bi_size)
+ return 1;
+
+ complete((struct completion*)bio->bi_private);
+ return 0;
+}
+
+static int __floppy_read_block_0(struct block_device *bdev)
+{
+ struct bio bio;
+ struct bio_vec bio_vec;
+ struct completion complete;
+ struct page *page;
+ size_t size;
+
+ page = alloc_page(GFP_NOIO);
+ if (!page) {
+ process_fd_request();
+ return -ENOMEM;
+ }
+
+ size = bdev->bd_block_size;
+ if (!size)
+ size = 1024;
+
+ bio_init(&bio);
+ bio.bi_io_vec = &bio_vec;
+ bio_vec.bv_page = page;
+ bio_vec.bv_len = size;
+ bio_vec.bv_offset = 0;
+ bio.bi_vcnt = 1;
+ bio.bi_idx = 0;
+ bio.bi_size = size;
+ bio.bi_bdev = bdev;
+ bio.bi_sector = 0;
+ init_completion(&complete);
+ bio.bi_private = &complete;
+ bio.bi_end_io = floppy_rb0_complete;
+
+ submit_bio(READ, &bio);
+ generic_unplug_device(bdev_get_queue(bdev));
+ process_fd_request();
+ wait_for_completion(&complete);
+
+ __free_page(page);
+
+ return 0;
+}
+
+static int floppy_read_block_0(struct gendisk *disk)
+{
+ struct block_device *bdev;
+ int ret;
+
+ bdev = bdget(MKDEV(disk->major, disk->first_minor));
+ if (!bdev) {
+ printk("No block device for %s\n", disk->disk_name);
+ BUG();
+ }
+ bdev->bd_disk = disk; /* ewww */
+ ret = __floppy_read_block_0(bdev);
+ atomic_dec(&bdev->bd_count);
+ return ret;
+}
+
+/* revalidate the floppy disk, i.e. trigger format autodetection by reading
+ * the bootblock (block 0). "Autodetection" is also needed to check whether
+ * there is a disk in the drive at all... Thus we also do it for fixed
+ * geometry formats */
+static int floppy_revalidate(struct gendisk *disk)
+{
+ int drive=(int)disk->private_data;
+#define NO_GEOM (!current_type[drive] && !ITYPE(UDRS->fd_device))
+ int cf;
+ int res = 0;
+
+ if (UTESTF(FD_DISK_CHANGED) ||
+ UTESTF(FD_VERIFY) ||
+ test_bit(drive, &fake_change) ||
+ NO_GEOM){
+ if(usage_count == 0) {
+ printk("VFS: revalidate called on non-open device.\n");
+ return -EFAULT;
+ }
+ lock_fdc(drive,0);
+ cf = UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY);
+ if (!(cf || test_bit(drive, &fake_change) || NO_GEOM)){
+ process_fd_request(); /*already done by another thread*/
+ return 0;
+ }
+ UDRS->maxblock = 0;
+ UDRS->maxtrack = 0;
+ if (buffer_drive == drive)
+ buffer_track = -1;
+ clear_bit(drive, &fake_change);
+ UCLEARF(FD_DISK_CHANGED);
+ if (cf)
+ UDRS->generation++;
+ if (NO_GEOM){
+ /* auto-sensing */
+ res = floppy_read_block_0(disk);
+ } else {
+ if (cf)
+ poll_drive(0, FD_RAW_NEED_DISK);
+ process_fd_request();
+ }
+ }
+ set_capacity(disk, floppy_sizes[UDRS->fd_device]);
+ return res;
+}
+
+static struct block_device_operations floppy_fops = {
+ .owner = THIS_MODULE,
+ .open = floppy_open,
+ .release = floppy_release,
+ .ioctl = fd_ioctl,
+ .media_changed = check_floppy_change,
+ .revalidate_disk= floppy_revalidate,
+};
+
+static void __init register_devfs_entries (int drive)
+{
+ int base_minor, i;
+ static char *table[] =
+ {"",
+#if 0
+ "d360", +#else
+ "h1232",
+#endif
+ "h1200", "u360", "u720", "h360", "h720",
+ "u1440", "u2880", "CompaQ", "h1440", "u1680", "h410",
+ "u820", "h1476", "u1722", "h420", "u830", "h1494", "u1743",
+ "h880", "u1040", "u1120", "h1600", "u1760", "u1920",
+ "u3200", "u3520", "u3840", "u1840", "u800", "u1600",
+ NULL
+ };
+ static int t360[] = {1,0}, t1200[] = {2,5,6,10,12,14,16,18,20,23,0},
+ t3in[] = {8,9,26,27,28, 7,11,15,19,24,25,29,31, 3,4,13,17,21,22,30,0};
+ static int *table_sup[] = + {NULL, t360, t1200, t3in+5+8, t3in+5, t3in, t3in};
+
+ base_minor = (drive < 4) ? drive : (124 + drive);
+ if (UDP->cmos < NUMBER(default_drive_params)) {
+ i = 0;
+ do {
+ char name[16];
+
+ sprintf (name, "%d%s", drive, table[table_sup[UDP->cmos][i]]);
+ devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, MAJOR_NR,
+ base_minor + (table_sup[UDP->cmos][i] << 2),
+ S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP |S_IWGRP,
+ &floppy_fops, NULL);
+ } while (table_sup[UDP->cmos][i++]);
+ }
+}
+
+/*
+ * Floppy Driver initialization
+ * =============================
+ */
+
+static inline char __init get_fdc_version(void)
+{
+ return FDC_8272A;
+}
+
+/* lilo configuration */
+
+static void __init floppy_set_flags(int *ints,int param, int param2)
+{
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
+ if (param)
+ default_drive_params[i].params.flags |= param2;
+ else
+ default_drive_params[i].params.flags &= ~param2;
+ }
+ DPRINT("%s flag 0x%x\n", param2 ? "Setting" : "Clearing", param);
+}
+
+static void __init daring(int *ints,int param, int param2)
+{
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
+ if (param){
+ default_drive_params[i].params.select_delay = 0;
+ default_drive_params[i].params.flags |= FD_SILENT_DCL_CLEAR;
+ } else {
+ default_drive_params[i].params.select_delay = 2*HZ/100;
+ default_drive_params[i].params.flags &= ~FD_SILENT_DCL_CLEAR;
+ }
+ }
+ DPRINT("Assuming %s floppy hardware\n", param ? "standard" : "broken");
+}
+
+static void __init set_cmos(int *ints, int dummy, int dummy2)
+{
+ int current_drive=0;
+
+ if (ints[0] != 2){
+ DPRINT("wrong number of parameters for CMOS\n");
+ return;
+ }
+ current_drive = ints[1];
+ if (current_drive < 0 || current_drive >= 8){
+ DPRINT("bad drive for set_cmos\n");
+ return;
+ }
+#if N_FDC > 1
+ if (current_drive >= 4 && !FDC2)
+ FDC2 = 0x370;
+#endif
+ DP->cmos = ints[2];
+ DPRINT("setting CMOS code to %d\n", ints[2]);
+}
+
+static struct param_table {
+ const char *name;
+ void (*fn)(int *ints, int param, int param2);
+ int *var;
+ int def_param;
+ int param2;
+} config_params[]={
+ { "allowed_drive_mask", 0, &allowed_drive_mask, 0xff, 0}, /* obsolete */
+ { "all_drives", 0, &allowed_drive_mask, 0xff, 0 }, /* obsolete */
+ { "irq", 0, &FLOPPY_IRQ, DEFAULT_FLOPPY_IRQ, 0 },
+ { "dma", 0, &FLOPPY_DMA, DEFAULT_FLOPPY_DMA, 0 },
+
+ { "daring", daring, 0, 1, 0},
+#if N_FDC > 1
+ { "two_fdc", 0, &FDC2, 0x370, 0 },
+ { "one_fdc", 0, &FDC2, 0, 0 },
+#endif
+ { "broken_dcl", floppy_set_flags, 0, 1, FD_BROKEN_DCL },
+ { "messages", floppy_set_flags, 0, 1, FTD_MSG },
+ { "silent_dcl_clear", floppy_set_flags, 0, 1, FD_SILENT_DCL_CLEAR },
+ { "debug", floppy_set_flags, 0, 1, FD_DEBUG },
+
+ { "nodma", 0, &can_use_virtual_dma, 1, 0 },
+ { "yesdma", 0, &can_use_virtual_dma, 0, 0 },
+
+ { "fifo_depth", 0, &fifo_depth, 0xa, 0 },
+ { "nofifo", 0, &no_fifo, 0x20, 0 },
+ { "usefifo", 0, &no_fifo, 0, 0 },
+
+ { "cmos", set_cmos, 0, 0, 0 },
+ { "slow", 0, &slow_floppy, 1, 0 },
+
+ { "unexpected_interrupts", 0, &print_unex, 1, 0 },
+ { "no_unexpected_interrupts", 0, &print_unex, 0, 0 },
+
+ EXTRA_FLOPPY_PARAMS
+};
+
+static int __init floppy_setup(char *str)
+{
+ int i;
+ int param;
+ int ints[11];
+
+ str = get_options(str,ARRAY_SIZE(ints),ints);
+ if (str) {
+ for (i=0; i< ARRAY_SIZE(config_params); i++){
+ if (strcmp(str,config_params[i].name) == 0){
+ if (ints[0])
+ param = ints[1];
+ else
+ param = config_params[i].def_param;
+ if (config_params[i].fn)
+ config_params[i].
+ fn(ints,param,
+ config_params[i].param2);
+ if (config_params[i].var) {
+ DPRINT("%s=%d\n", str, param);
+ *config_params[i].var = param;
+ }
+ return 1;
+ }
+ }
+ }
+ if (str) {
+ DPRINT("unknown floppy option [%s]\n", str);
+
+ DPRINT("allowed options are:");
+ for (i=0; i< ARRAY_SIZE(config_params); i++)
+ printk(" %s",config_params[i].name);
+ printk("\n");
+ } else
+ DPRINT("botched floppy option\n");
+ DPRINT("Read linux/Documentation/floppy.txt\n");
+ return 0;
+}
+
+static int have_no_fdc= -ENODEV;
+
+static struct platform_device floppy_device = {
+ .name = "floppy",
+ .id = 0,
+ .dev = {
+ .name = "Floppy Drive",
+ },
+};
+
+static struct gendisk *floppy_find(dev_t dev, int *part, void *data)
+{
+ int drive = (*part&3) | ((*part&0x80) >> 5);
+ if (drive >= N_DRIVE ||
+ !(allowed_drive_mask & (1 << drive)) ||
+ fdc_state[FDC(drive)].version == FDC_NONE)
+ return NULL;
+ *part = 0;
+ return get_disk(disks[drive]);
+}
+
+int __init floppy_init(void)
+{
+ int i,unit,drive;
+ int err;
+
+ raw_cmd = NULL;
+
+ for (i=0; i<N_DRIVE; i++) {
+ disks[i] = alloc_disk(1);
+ if (!disks[i])
+ goto Enomem;
+ }
+
+ devfs_handle = devfs_mk_dir (NULL, "floppy", NULL);
+ if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
+ printk("Unable to get major %d for floppy\n",MAJOR_NR);
+ err = -EBUSY;
+ goto out;
+ }
+
+ for (i=0; i<N_DRIVE; i++) {
+ disks[i]->major = MAJOR_NR;
+ disks[i]->first_minor = TOMINOR(i);
+ disks[i]->fops = &floppy_fops;
+ sprintf(disks[i]->disk_name, "fd%d", i);
+ }
+
+ blk_register_region(MKDEV(MAJOR_NR, 0), 256, THIS_MODULE,
+ floppy_find, NULL, NULL);
+
+ for (i=0; i<256; i++)
+ if (ITYPE(i))
+ floppy_sizes[i] = floppy_type[ITYPE(i)].size;
+ else
+ floppy_sizes[i] = MAX_DISK_SIZE << 1;
+
+ blk_init_queue(&floppy_queue, do_fd_request, &floppy_lock);
+ reschedule_timeout(MAXTIMEOUT, "floppy init", MAXTIMEOUT);
+ config_types();
+
+ for (i = 0; i < N_FDC; i++) {
+ fdc = i;
+ CLEARSTRUCT(FDCS);
+ FDCS->dtr = -1;
+ FDCS->dor = 0;
+ }
+
+ if ((fd_inb(FD_MODE_CHANGE) & 1) == 0)
+ FDC1 = 0xc8;
+
+ use_virtual_dma = can_use_virtual_dma & 1;
+ fdc_state[0].address = FDC1;
+ if (fdc_state[0].address == -1) {
+ err = -ENODEV;
+ goto out1;
+ }
+#if N_FDC > 1
+ fdc_state[1].address = FDC2;
+#endif
+
+ fdc = 0; /* reset fdc in case of unexpected interrupt */
+ if (floppy_grab_irq_and_dma()){
+ err = -EBUSY;
+ goto out1;
+ }
+
+ /* initialise drive state */
+ for (drive = 0; drive < N_DRIVE; drive++) {
+ CLEARSTRUCT(UDRS);
+ CLEARSTRUCT(UDRWE);
+ USETF(FD_DISK_NEWCHANGE);
+ USETF(FD_DISK_CHANGED);
+ USETF(FD_VERIFY);
+ UDRS->fd_device = -1;
+ floppy_track_buffer = NULL;
+ max_buffer_sectors = 0;
+ }
+
+ for (i = 0; i < N_FDC; i++) {
+ fdc = i;
+ FDCS->driver_version = FD_DRIVER_VERSION;
+ for (unit=0; unit<4; unit++)
+ FDCS->track[unit] = 0;
+ if (FDCS->address == -1)
+ continue;
+ FDCS->rawcmd = 2;
+ user_reset_fdc(-1, FD_RESET_ALWAYS, 0);
+
+ /* Try to determine the floppy controller type */
+ FDCS->version = get_fdc_version();
+ if (FDCS->version == FDC_NONE){
+ /* free ioports reserved by floppy_grab_irq_and_dma() */
+ release_region(FDCS->address, 1);
+ release_region(FDCS->address + 2, 1);
+ release_region(FDCS->address + 4, 1);
+ release_region(0xbe, 1);
+ release_region(0x4be, 1);
+ FDCS->address = -1;
+ continue;
+ }
+ if (can_use_virtual_dma == 2 && FDCS->version < FDC_82072A)
+ can_use_virtual_dma = 0;
+
+ have_no_fdc = 0;
+ /* Not all FDCs seem to be able to handle the version command
+ * properly, so force a reset for the standard FDC clones,
+ * to avoid interrupt garbage.
+ */
+ user_reset_fdc(-1,FD_RESET_ALWAYS,0);
+ }
+ fdc=0;
+ del_timer(&fd_timeout);
+ current_drive = 0;
+ floppy_release_irq_and_dma();
+#if 0 /* no message */
+ initialising=0;
+#endif
+ if (have_no_fdc) {
+ DPRINT("no floppy controllers found\n");
+ flush_scheduled_work();
+ if (usage_count)
+ floppy_release_irq_and_dma();
+ err = have_no_fdc;
+ goto out2;
+ }
+
+ for (drive = 0; drive < N_DRIVE; drive++) {
+ motor_off_timer[drive].data = drive;
+ motor_off_timer[drive].function = motor_off_callback;
+ if (!(allowed_drive_mask & (1 << drive)))
+ continue;
+ if (fdc_state[FDC(drive)].version == FDC_NONE)
+ continue;
+ /* to be cleaned up... */
+ disks[drive]->private_data = (void*)drive;
+ disks[drive]->queue = &floppy_queue;
+ add_disk(disks[drive]);
+ }
+
+ platform_device_register(&floppy_device);
+ return 0;
+
+out1:
+ del_timer(&fd_timeout);
+out2:
+ blk_unregister_region(MKDEV(MAJOR_NR, 0), 256);
+ unregister_blkdev(MAJOR_NR,"fd");
+ blk_cleanup_queue(&floppy_queue);
+out:
+ for (i=0; i<N_DRIVE; i++)
+ put_disk(disks[i]);
+ return err;
+
+Enomem:
+ while (i--)
+ put_disk(disks[i]);
+ return -ENOMEM;
+}
+
+static spinlock_t floppy_usage_lock = SPIN_LOCK_UNLOCKED;
+
+static int floppy_grab_irq_and_dma(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&floppy_usage_lock, flags);
+ if (usage_count++){
+ spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ MOD_INC_USE_COUNT;
+ if (fd_request_irq()) {
+ DPRINT("Unable to grab IRQ%d for the floppy driver\n",
+ FLOPPY_IRQ);
+ MOD_DEC_USE_COUNT;
+ spin_lock_irqsave(&floppy_usage_lock, flags);
+ usage_count--;
+ spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ return -1;
+ }
+ if (fd_request_dma()) {
+ DPRINT("Unable to grab DMA%d for the floppy driver\n",
+ FLOPPY_DMA);
+ fd_free_irq();
+ MOD_DEC_USE_COUNT;
+ spin_lock_irqsave(&floppy_usage_lock, flags);
+ usage_count--;
+ spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ return -1;
+ }
+
+ for (fdc=0; fdc< N_FDC; fdc++){
+ if (FDCS->address != -1){
+ static char floppy[] = "floppy";
+ if (!request_region(FDCS->address, 1, floppy))
+ goto cleanup0;
+
+ if (!request_region(FDCS->address + 2, 1, floppy)) {
+ release_region(FDCS->address, 1);
+ goto cleanup0;
+ }
+
+ if (!request_region(FDCS->address + 4, 1, floppy)) {
+ release_region(FDCS->address, 1);
+ release_region(FDCS->address + 2, 1);
+ goto cleanup0;
+ }
+
+ if (fdc == 0) { /* internal FDC */
+ if (request_region(0xbe, 1, "floppy mode change")) {
+ if (request_region(0x4be, 1, "floppy ex. mode change"))
+ continue;
+ else
+ DPRINT("Floppy io-port 0x4be in use\n");
+
+ release_region(0xbe, 1);
+ } else
+ DPRINT("Floppy io-port 0xbe in use\n");
+
+ release_region(FDCS->address, 1);
+ release_region(FDCS->address + 2, 1);
+ release_region(FDCS->address + 4, 1);
+ }
+
+ goto cleanup1;
+ }
+ }
+ for (fdc=0; fdc< N_FDC; fdc++){
+ if (FDCS->address != -1){
+ reset_fdc_info(1);
+ fd_outb(FDCS->dor, FD_MODE);
+ }
+ }
+ fdc = 0;
+ fd_outb((FDCS->dor & 8), FD_MODE);
+
+ for (fdc = 0; fdc < N_FDC; fdc++)
+ if (FDCS->address != -1)
+ fd_outb(FDCS->dor, FD_MODE);
+ /*
+ * The driver will try and free resources and relies on us
+ * to know if they were allocated or not.
+ */
+ fdc = 0;
+ irqdma_allocated = 1;
+ return 0;
+
+cleanup0:
+ DPRINT("Floppy io-port 0x%04lx in use\n", FDCS->address);
+cleanup1:
+ fd_free_irq();
+ fd_free_dma();
+ while(--fdc >= 0) {
+ release_region(FDCS->address, 1);
+ release_region(FDCS->address + 2, 1);
+ release_region(FDCS->address + 4, 1);
+ if (fdc == 0) {
+ release_region(0x00be, 1);
+ release_region(0x04be, 1);
+ }
+ }
+ MOD_DEC_USE_COUNT;
+ spin_lock_irqsave(&floppy_usage_lock, flags);
+ usage_count--;
+ spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ return -1;
+}
+
+static void floppy_release_irq_and_dma(void)
+{
+ int old_fdc;
+#ifdef FLOPPY_SANITY_CHECK
+ int drive;
+#endif
+ long tmpsize;
+ unsigned long tmpaddr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&floppy_usage_lock, flags);
+ if (--usage_count){
+ spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ if(irqdma_allocated)
+ {
+ fd_disable_dma();
+ fd_free_dma();
+ fd_free_irq();
+ irqdma_allocated=0;
+ }
+ fd_outb(0, FD_MODE);
+ floppy_enable_hlt();
+
+ if (floppy_track_buffer && max_buffer_sectors) {
+ tmpsize = max_buffer_sectors*1024;
+ tmpaddr = (unsigned long)floppy_track_buffer;
+ floppy_track_buffer = NULL;
+ max_buffer_sectors = 0;
+ buffer_min = buffer_max = -1;
+ fd_dma_mem_free(tmpaddr, tmpsize);
+ }
+
+#ifdef FLOPPY_SANITY_CHECK
+ for (drive=0; drive < N_FDC * 4; drive++)
+ if (timer_pending(motor_off_timer + drive))
+ printk("motor off timer %d still active\n", drive);
+
+ if (timer_pending(&fd_timeout))
+ printk("floppy timer still active:%s\n", timeout_message);
+ if (timer_pending(&fd_timer))
+ printk("auxiliary floppy timer still active\n");
+ if (floppy_work.pending)
+ printk("work still pending\n");
+#endif
+ old_fdc = fdc;
+ for (fdc = 0; fdc < N_FDC; fdc++)
+ if (FDCS->address != -1) {
+ release_region(FDCS->address, 1);
+ release_region(FDCS->address + 2, 1);
+ release_region(FDCS->address + 4, 1);
+ if (fdc == 0) {
+ release_region(0xbe, 1);
+ release_region(0x4be, 1);
+ }
+ }
+ fdc = old_fdc;
+ MOD_DEC_USE_COUNT;
+}
+
+
+#ifdef MODULE
+
+char *floppy;
+
+static void __init parse_floppy_cfg_string(char *cfg)
+{
+ char *ptr;
+
+ while(*cfg) {
+ for(ptr = cfg;*cfg && *cfg != ' ' && *cfg != '\t'; cfg++);
+ if (*cfg) {
+ *cfg = '\0';
+ cfg++;
+ }
+ if (*ptr)
+ floppy_setup(ptr);
+ }
+}
+
+int init_module(void)
+{
+ printk(KERN_INFO "inserting floppy driver for " UTS_RELEASE "\n");
+
+ if (floppy)
+ parse_floppy_cfg_string(floppy);
+ return floppy_init();
+}
+
+void cleanup_module(void)
+{
+ int drive;
+
+ platform_device_unregister(&floppy_device);
+ devfs_unregister (devfs_handle);
+ blk_unregister_region(MKDEV(MAJOR_NR, 0), 256);
+ unregister_blkdev(MAJOR_NR, "fd");
+ for (drive = 0; drive < N_DRIVE; drive++) {
+ if ((allowed_drive_mask & (1 << drive)) &&
+ fdc_state[FDC(drive)].version != FDC_NONE)
+ del_gendisk(disks[drive]);
+ put_disk(disks[drive]);
+ }
+
+ blk_cleanup_queue(&floppy_queue);
+ /* eject disk, if any */
+ fd_eject(0);
+}
+
+MODULE_PARM(floppy,"s");
+MODULE_PARM(FLOPPY_IRQ,"i");
+MODULE_PARM(FLOPPY_DMA,"i");
+MODULE_AUTHOR("Osamu Tomita");
+MODULE_SUPPORTED_DEVICE("fd");
+MODULE_LICENSE("GPL");
+
+#else
+
+__setup ("floppy=", floppy_setup);
+module_init(floppy_init)
+#endif
diff -urN linux/include/asm-i386/floppy.h linux98/include/asm-i386/floppy.h
--- linux/include/asm-i386/floppy.h Sat Jul 21 04:52:19 2001
+++ linux98/include/asm-i386/floppy.h Sun Aug 19 14:13:09 2001
@@ -11,6 +11,7 @@
#define __ASM_I386_FLOPPY_H
#include <linux/vmalloc.h>
+#include <linux/config.h>
/*
@@ -282,9 +283,14 @@
};
+#ifdef CONFIG_PC9800
+static int FDC1 = 0x90;
+#else /* !CONFIG_PC9800 */
static int FDC1 = 0x3f0;
+#endif /* CONFIG_PC9800 */
static int FDC2 = -1;
+#ifndef CONFIG_PC9800
/*
* Floppy types are stored in the rtc's CMOS RAM and so rtc_lock
* is needed to prevent corrupted CMOS RAM in case "insmod floppy"
@@ -307,11 +313,16 @@
spin_unlock_irqrestore(&rtc_lock, flags); \
val; \
})
+#endif /* !CONFIG_PC9800 */
#define N_FDC 2
#define N_DRIVE 8
+#ifdef CONFIG_PC9800
+#define FLOPPY_MOTOR_MASK 0x08
+#else
#define FLOPPY_MOTOR_MASK 0xf0
+#endif /* CONFIG_PC9800 */
#define AUTO_DMA
diff -urN linux/include/linux/fdreg.h linux98/include/linux/fdreg.h
--- linux/include/linux/fdreg.h Mon Apr 22 17:32:02 1996
+++ linux98/include/linux/fdreg.h Fri Aug 17 22:13:41 2001
@@ -6,10 +6,28 @@
* Handbook", Sanches and Canton.
*/
+#include <linux/config.h>
+
+#ifdef CONFIG_PC9800
+#define FDPATCHES
+#endif
+
#ifdef FDPATCHES
#define FD_IOPORT fdc_state[fdc].address
+#ifdef CONFIG_PC9800
+
+/* Fd controller regs. S&C, about page 340 */
+#define FD_STATUS (0 + FD_IOPORT )
+#define FD_DATA (2 + FD_IOPORT )
+
+#define FD_MODE (4 + FD_IOPORT )
+#define FD_MODE_CHANGE 0xbe
+#define FD_EMODE_CHANGE 0x4be
+
+#else /* !CONFIG_PC9800 */
+
/* Fd controller regs. S&C, about page 340 */
#define FD_STATUS (4 + FD_IOPORT )
#define FD_DATA (5 + FD_IOPORT )
@@ -23,8 +41,14 @@
/* Diskette Control Register (write)*/
#define FD_DCR (7 + FD_IOPORT )
+#endif /* CONFIG_PC9800 */
+
#else
+#ifdef CONFIG_PC9800
+#error FDPATCHES must be defined for NEC PC-9800
+#endif
+
#define FD_STATUS 0x3f4
#define FD_DATA 0x3f5
#define FD_DOR 0x3f2 /* Digital Output Register */
Osamu Tomita
2002-11-02 18:23:35 UTC
Permalink
This is a part 19/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
SMP related modules.
- add PC-9800 bus spec.

diffstat:
arch/i386/kernel/apic.c | 16 +++++++++++++---
arch/i386/kernel/io_apic.c | 25 +++++++++++++++++++++++--
arch/i386/kernel/mpparse.c | 35 ++++++++++++++++++++++++++++++++++-
arch/i386/kernel/smpboot.c | 14 ++++++++++++++
include/asm-i386/mpspec.h | 4 +++-
include/asm-i386/smpboot.h | 9 +++++++++
6 files changed, 96 insertions(+), 7 deletions(-)

patch:
diff -urN linux/arch/i386/kernel/apic.c linux98/arch/i386/kernel/apic.c
--- linux/arch/i386/kernel/apic.c Wed Oct 16 13:20:29 2002
+++ linux98/arch/i386/kernel/apic.c Wed Oct 16 15:40:20 2002
@@ -33,6 +33,8 @@
#include <asm/arch_hooks.h>
#include "mach_apic.h"
+#include "io_ports.h"
+
void __init apic_intr_init(void)
{
#ifdef CONFIG_SMP
@@ -135,9 +137,13 @@
* PIC mode, enable APIC mode in the IMCR, i.e.
* connect BSP's local APIC to INT and NMI lines.
*/
+#ifndef CONFIG_PC9800
printk("leaving PIC mode, enabling APIC mode.\n");
outb(0x70, 0x22);
outb(0x01, 0x23);
+#else
+ printk("On NEC98, Changing mode from PIC to APIC isNOT supported yet.\n");
+#endif
}
}
@@ -150,9 +156,13 @@
* interrupts, including IPIs, won't work beyond
* this point! The only exception are INIT IPIs.
*/
+#ifndef CONFIG_PC9800
printk("disabling APIC mode, entering PIC mode.\n");
outb(0x70, 0x22);
outb(0x00, 0x23);
+#else
+ printk("On NEC98, Changing mode from APIC to PIC isNOT supported yet.\n");
+#endif
}
}
@@ -759,9 +769,9 @@
spin_lock_irqsave(&i8253_lock, flags);
- outb_p(0x00, 0x43);
- count = inb_p(0x40);
- count |= inb_p(0x40) << 8;
+ outb_p(0x00, PIT_MODE);
+ count = inb_p(PIT_CH0);
+ count |= inb_p(PIT_CH0) << 8;
spin_unlock_irqrestore(&i8253_lock, flags);
diff -urN linux/arch/i386/kernel/io_apic.c linux98/arch/i386/kernel/io_apic.c
--- linux/arch/i386/kernel/io_apic.c Sat Oct 19 13:01:22 2002
+++ linux98/arch/i386/kernel/io_apic.c Sat Oct 19 16:22:51 2002
@@ -37,6 +37,8 @@
#include <asm/desc.h>
#include "mach_apic.h"
+#include "io_ports.h"
+
#undef APIC_LOCKUP_DEBUG
#define APIC_LOCKUP_DEBUG
@@ -354,7 +356,9 @@
if ((mp_bus_id_to_type[lbus] == MP_BUS_ISA ||
mp_bus_id_to_type[lbus] == MP_BUS_EISA ||
- mp_bus_id_to_type[lbus] == MP_BUS_MCA) &&
+ mp_bus_id_to_type[lbus] == MP_BUS_MCA ||
+ mp_bus_id_to_type[lbus] == MP_BUS_NEC98
+ ) &&
(mp_irqs[i].mpc_irqtype == type) &&
(mp_irqs[i].mpc_srcbusirq == irq))
@@ -448,6 +452,12 @@
#define default_MCA_trigger(idx) (1)
#define default_MCA_polarity(idx) (0)
+/* NEC98 interrupts are always polarity zero edge triggered,
+ * when listed as conforming in the MP table. */
+
+#define default_NEC98_trigger(idx) (0)
+#define default_NEC98_polarity(idx) (0)
+
static int __init MPBIOS_polarity(int idx)
{
int bus = mp_irqs[idx].mpc_srcbus;
@@ -482,6 +492,11 @@
polarity = default_MCA_polarity(idx);
break;
}
+ case MP_BUS_NEC98: /* NEC 98 pin */
+ {
+ polarity = default_NEC98_polarity(idx);
+ break;
+ }
default:
{
printk(KERN_WARNING "broken BIOS!!\n");
@@ -551,6 +566,11 @@
trigger = default_MCA_trigger(idx);
break;
}
+ case MP_BUS_NEC98: /* NEC 98 pin */
+ {
+ trigger = default_NEC98_trigger(idx);
+ break;
+ }
default:
{
printk(KERN_WARNING "broken BIOS!!\n");
@@ -612,6 +632,7 @@
case MP_BUS_ISA: /* ISA pin */
case MP_BUS_EISA:
case MP_BUS_MCA:
+ case MP_BUS_NEC98:
{
irq = mp_irqs[idx].mpc_srcbusirq;
break;
@@ -1718,7 +1739,7 @@
* Additionally, something is definitely wrong with irq9
* on PIIX4 boards.
*/
-#define PIC_IRQS (1<<2)
+#define PIC_IRQS (1 << PIC_CASCADE_IR)
void __init setup_IO_APIC(void)
{
diff -urN linux/arch/i386/kernel/mpparse.c linux98/arch/i386/kernel/mpparse.c
--- linux/arch/i386/kernel/mpparse.c Wed Oct 16 13:20:29 2002
+++ linux98/arch/i386/kernel/mpparse.c Wed Oct 16 15:40:20 2002
@@ -236,6 +236,8 @@
mp_current_pci_id++;
} else if (strncmp(str, BUSTYPE_MCA, sizeof(BUSTYPE_MCA)-1) == 0) {
mp_bus_id_to_type[m->mpc_busid] = MP_BUS_MCA;
+ } else if (strncmp(str, BUSTYPE_NEC98, sizeof(BUSTYPE_NEC98)-1) == 0) {
+ mp_bus_id_to_type[m->mpc_busid] = MP_BUS_NEC98;
} else {
printk("Unknown bustype %s - ignoring\n", str);
}
@@ -681,7 +683,12 @@
* Read the physical hardware table. Anything here will
* override the defaults.
*/
- if (!smp_read_mpc((void *)mpf->mpf_physptr)) {
+#ifndef CONFIG_PC9800
+ if (!smp_read_mpc((void *)mpf->mpf_physptr))
+#else
+ if (!smp_read_mpc(phys_to_virt(mpf->mpf_physptr)))
+#endif
+ {
smp_found_config = 0;
printk(KERN_ERR "BIOS bug, MP table errors detected!...\n");
printk(KERN_ERR "... disabling SMP support. (tell your hw vendor)\n");
@@ -735,8 +742,30 @@
printk("found SMP MP-table at %08lx\n",
virt_to_phys(mpf));
reserve_bootmem(virt_to_phys(mpf), PAGE_SIZE);
+#ifndef CONFIG_PC9800
if (mpf->mpf_physptr)
reserve_bootmem(mpf->mpf_physptr, PAGE_SIZE);
+#else
+ /*
+ * PC-9800's MPC table places on the very last of
+ * physical memory; so that simply reserving PAGE_SIZE
+ * from mpg->mpf_physptr yields BUG() in
+ * reserve_bootmem.
+ */
+ if (mpf->mpf_physptr) {
+ /*
+ * We cannot access to MPC table to compute
+ * table size yet, as only few megabytes from
+ * the bottom is mapped now.
+ */
+ unsigned long size = PAGE_SIZE;
+ unsigned long end = max_low_pfn * PAGE_SIZE;
+ if (mpf->mpf_physptr + size > end)
+ size = end - mpf->mpf_physptr;
+ reserve_bootmem(mpf->mpf_physptr, size);
+ }
+#endif
+
mpf_found = mpf;
return 1;
}
@@ -748,7 +777,9 @@
void __init find_smp_config (void)
{
+#ifndef CONFIG_PC9800
unsigned int address;
+#endif
/*
* FIXME: Linux assumes you have 640K of base ram..
@@ -762,6 +793,7 @@
smp_scan_config(639*0x400,0x400) ||
smp_scan_config(0xF0000,0x10000))
return;
+#ifndef CONFIG_PC9800 /* PC-9800 has no EBDA area? */
/*
* If it is an SMP machine we should know now, unless the
* configuration is in an EISA/MCA bus machine with an
@@ -784,6 +816,7 @@
smp_scan_config(address, 0x400);
if (smp_found_config)
printk(KERN_WARNING "WARNING: MP table in the EBDA can be UNSAFE, contact linux-***@vger.kernel.org if you experience SMP problems!\n");
+#endif
}
diff -urN linux/arch/i386/kernel/smpboot.c linux98/arch/i386/kernel/smpboot.c
--- linux/arch/i386/kernel/smpboot.c Wed Oct 16 13:20:29 2002
+++ linux98/arch/i386/kernel/smpboot.c Wed Oct 16 15:40:20 2002
@@ -821,13 +821,27 @@
nmi_low = *((volatile unsigned short *) TRAMPOLINE_LOW);
} +#ifndef CONFIG_PC9800
CMOS_WRITE(0xa, 0xf);
+#else
+ /* reset code is stored in 8255 on PC-9800. */
+ outb(0x0e, 0x37); /* SHUT0 = 0 */
+#endif
local_flush_tlb();
Dprintk("1.\n");
*((volatile unsigned short *) TRAMPOLINE_HIGH) = start_eip >> 4;
Dprintk("2.\n");
*((volatile unsigned short *) TRAMPOLINE_LOW) = start_eip & 0xf;
Dprintk("3.\n");
+#ifdef CONFIG_PC9800
+ /*
+ * On PC-9800, continuation on warm reset is done by loading
+ * %ss:%sp from 0x0000:0404 and executing 'lret', so:
+ */
+ /* 0x3f0 is on unused interrupt vector and should be safe... */
+ *((volatile unsigned long *) phys_to_virt(0x404)) = 0x000003f0;
+ Dprintk("4.\n");
+#endif
/*
* Be paranoid about clearing APIC errors.
diff -urN linux/include/asm-i386/mpspec.h linux98/include/asm-i386/mpspec.h
--- linux/include/asm-i386/mpspec.h Fri Apr 12 13:58:13 2002
+++ linux98/include/asm-i386/mpspec.h Fri Apr 12 14:02:42 2002
@@ -105,6 +105,7 @@
#define BUSTYPE_TC "TC"
#define BUSTYPE_VME "VME"
#define BUSTYPE_XPRESS "XPRESS"
+#define BUSTYPE_NEC98 "NEC98"
struct mpc_config_ioapic
{
@@ -195,7 +196,8 @@
MP_BUS_ISA = 1,
MP_BUS_EISA,
MP_BUS_PCI,
- MP_BUS_MCA
+ MP_BUS_MCA,
+ MP_BUS_NEC98
};
extern int mp_bus_id_to_type [MAX_MP_BUSSES];
extern int mp_bus_id_to_node [MAX_MP_BUSSES];
diff -urN linux/include/asm-i386/smpboot.h linux98/include/asm-i386/smpboot.h
--- linux/include/asm-i386/smpboot.h Sat Oct 12 13:22:19 2002
+++ linux98/include/asm-i386/smpboot.h Sat Oct 12 19:33:46 2002
@@ -13,8 +13,17 @@
#define TRAMPOLINE_LOW phys_to_virt(0x8)
#define TRAMPOLINE_HIGH phys_to_virt(0xa)
#else /* !CONFIG_CLUSTERED_APIC */
+ #ifndef CONFIG_PC9800
#define TRAMPOLINE_LOW phys_to_virt(0x467)
#define TRAMPOLINE_HIGH phys_to_virt(0x469)
+ #else /* CONFIG_PC9800 */
+ /*
+ * On PC-9800, continuation on warm reset is done by loading
+ * %ss:%sp from 0x0000:0404 and executing 'lret', so:
+ */
+ #define TRAMPOLINE_LOW phys_to_virt(0x4fa)
+ #define TRAMPOLINE_HIGH phys_to_virt(0x4fc)
+ #endif /* !CONFIG_PC9800 */
#endif /* CONFIG_CLUSTERED_APIC */
#ifdef CONFIG_CLUSTERED_APIC
Osamu Tomita
2002-11-02 18:20:07 UTC
Permalink
This is a part 17/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
SCSI driver related modules.
- replace bios_param function.
- add new driver. (pc980155)
- add new function sd_find_params_by_bdev() to get disk goemetry.
we need disk geometry to read partition table.

diffstat:
drivers/scsi/Kconfig | 7 +
drivers/scsi/Makefile | 3 drivers/scsi/pc980155.c | 262 ++++++++++++++++++++++++++++++++++++++++++++
drivers/scsi/pc980155.h | 47 +++++++
drivers/scsi/pc980155regs.h | 89 ++++++++++++++
drivers/scsi/scsi_scan.c | 1 drivers/scsi/scsi_syms.c | 5 drivers/scsi/scsicam.c | 68 +++++++++++
drivers/scsi/sd.c | 24 +++-
drivers/scsi/wd33c93.c | 57 ++++++---
drivers/scsi/wd33c93.h | 5 include/scsi/scsicam.h | 2 12 files changed, 548 insertions(+), 22 deletions(-)

patch:
diff -urN linux/drivers/scsi/Kconfig linux98/drivers/scsi/Kconfig
--- linux/drivers/scsi/Kconfig Thu Oct 31 13:23:25 2002
+++ linux98/drivers/scsi/Kconfig Thu Oct 31 19:29:18 2002
@@ -1746,6 +1746,13 @@
see the picture at
<http://amiga.multigraph.com/photos/oktagon.html>.
+config SCSI_PC980155
+ tristate "NEC PC-9801-55 SCSI support"
+ depends on PC9800 && SCSI
+ help
+ If you have the NEC PC-9801-55 SCSI interface card or compatibles
+ for NEC PC-9801/PC-9821, say Y.
+
# bool 'Cyberstorm Mk III SCSI support (EXPERIMENTAL)' CONFIG_CYBERSTORMIII_SCSI
# bool 'GVP Turbo 040/060 SCSI support (EXPERIMENTAL)' CONFIG_GVP_TURBO_SCSI
endmenu
diff -urN linux/drivers/scsi/Makefile linux98/drivers/scsi/Makefile
--- linux/drivers/scsi/Makefile Sat Oct 19 13:02:28 2002
+++ linux98/drivers/scsi/Makefile Tue Oct 29 15:45:44 2002
@@ -18,7 +18,7 @@
CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS
CFLAGS_seagate.o = -DARBITRATE -DPARITY -DSEAGATE_USE_ASM
-export-objs := scsi_syms.o 53c700.o
+export-objs := scsi_syms.o 53c700.o wd33c93.o
subdir-$(CONFIG_PCMCIA) += pcmcia
@@ -31,6 +31,7 @@
obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o
obj-$(CONFIG_A2091_SCSI) += a2091.o wd33c93.o
obj-$(CONFIG_GVP11_SCSI) += gvp11.o wd33c93.o
+obj-$(CONFIG_SCSI_PC980155) += pc980155.o wd33c93.o
obj-$(CONFIG_MVME147_SCSI) += mvme147.o wd33c93.o
obj-$(CONFIG_SGIWD93_SCSI) += sgiwd93.o wd33c93.o
obj-$(CONFIG_CYBERSTORM_SCSI) += NCR53C9x.o cyberstorm.o
diff -urN linux/drivers/scsi/pc980155.c linux98/drivers/scsi/pc980155.c
--- linux/drivers/scsi/pc980155.c Thu Jan 1 09:00:00 1970
+++ linux98/drivers/scsi/pc980155.c Sun Feb 3 12:08:14 2002
@@ -0,0 +1,262 @@
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/blk.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <linux/module.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "wd33c93.h"
+#include "pc980155.h"
+#include "pc980155regs.h"
+
+#define DEBUG
+
+#include<linux/stat.h>
+
+static inline void __print_debug_info(unsigned int);
+static inline void __print_debug_info(unsigned int a){}
+#define print_debug_info() __print_debug_info(base_io);
+
+#define NR_BASE_IOS 4
+static int nr_base_ios = NR_BASE_IOS;
+static unsigned int base_ios[NR_BASE_IOS] = {0xcc0, 0xcd0, 0xce0, 0xcf0};
+static unsigned int SASR;
+static unsigned int SCMD;
+static wd33c93_regs regs = {&SASR, &SCMD};
+
+static struct Scsi_Host *pc980155_host = NULL;
+
+static void pc980155_intr_handle(int irq, void *dev_id, struct pt_regs *regp);
+
+inline void pc980155_dma_enable(unsigned int base_io){
+ outb(0x01, REG_CWRITE);
+ WAIT();
+}
+inline void pc980155_dma_disable(unsigned int base_io){
+ outb(0x02, REG_CWRITE);
+ WAIT();
+}
+
+
+static void pc980155_intr_handle(int irq, void *dev_id, struct pt_regs *regp)
+{
+ wd33c93_intr(pc980155_host);
+}
+
+static int dma_setup(Scsi_Cmnd *sc, int dir_in){
+ /*
+ * sc->SCp.this_residual : transfer count
+ * sc->SCp.ptr : distination address (virtual address)
+ * dir_in : data direction (DATA_OUT_DIR:0 or DATA_IN_DIR:1)
+ *
+ * if success return 0
+ */
+
+ /*
+ * DMA WRITE MODE
+ * bit 7,6 01b single mode (this mode only)
+ * bit 5 inc/dec (default:0 = inc)
+ * bit 4 auto initialize (normaly:0 = off)
+ * bit 3,2 01b memory -> io
+ * 10b io -> memory
+ * 00b verify
+ * bit 1,0 channel
+ */
+ disable_dma(sc->host->dma_channel);
+ set_dma_mode(sc->host->dma_channel, 0x40 | (dir_in ? 0x04 : 0x08));
+ clear_dma_ff(sc->host->dma_channel);
+ set_dma_addr(sc->host->dma_channel, virt_to_phys(sc->SCp.ptr));
+ set_dma_count(sc->host->dma_channel, sc->SCp.this_residual);
+#if 0
+#ifdef DEBUG
+ printk("D%d(%x)D", sc->SCp.this_residual);
+#endif
+#endif
+ enable_dma(sc->host->dma_channel);
+
+ pc980155_dma_enable(sc->host->io_port);
+
+ return 0;
+}
+
+static void dma_stop(struct Scsi_Host *instance, Scsi_Cmnd *sc, int status){
+ /*
+ * instance: Hostadapter's instance
+ * sc: scsi command
+ * status: True if success
+ */
+
+ pc980155_dma_disable(sc->host->io_port);
+
+ disable_dma(sc->host->dma_channel);
+} +
+/* return non-zero on detection */
+static inline int pc980155_test_port(wd33c93_regs regs)
+{
+ /* Quick and dirty test for presence of the card. */
+ if (READ_AUX_STAT() == 0xff)
+ return 0;
+ return 1;
+}
+
+static inline int
+pc980155_getconfig(unsigned int base_io, wd33c93_regs regs,
+ unsigned char* irq, unsigned char* dma,
+ unsigned char* scsi_id)
+{
+ static unsigned char irqs[] = { 3, 5, 6, 9, 12, 13 };
+ unsigned char result;
+ + printk(KERN_DEBUG "PC-9801-55: base_io=%x SASR=%x SCMD=%x\n",
+ base_io, *regs.SASR, *regs.SCMD);
+ result = read_wd33c93(regs, WD_RESETINT);
+ printk(KERN_DEBUG "PC-9801-55: getting config (%x)\n", result);
+ *scsi_id = result & 0x07;
+ *irq = (result >> 3) & 0x07;
+ if (*irq > 5) {
+ printk(KERN_ERR "PC-9801-55 (base %#x): impossible IRQ (%d)"
+ " - other device here?\n", base_io, *irq);
+ return 0;
+ }
+
+ *irq = irqs[*irq];
+ result = inb(REG_STATRD);
+ WAIT();
+ *dma = result & 0x03;
+ if (*dma == 1) {
+ printk(KERN_ERR
+ "PC-9801-55 (base %#x): impossible DMA channl (%d)"
+ " - other device here?\n", base_io, *dma);
+ return 0;
+ }
+#ifdef DEBUG
+ printk("PC-9801-55: end of getconfig\n");
+#endif
+ return 1;
+}
+
+/* return non-zero on detection */
+int scsi_pc980155_detect(Scsi_Host_Template* tpnt)
+{
+ unsigned int base_io;
+ unsigned char irq, dma, scsi_id;
+ int i;
+#ifdef DEBUG
+ unsigned char debug;
+#endif
+ + for (i = 0; i < nr_base_ios; i++) {
+ base_io = base_ios[i];
+ SASR = REG_ADDRST;
+ SCMD = REG_CONTRL;
+
+ /* printk("PC-9801-55: SASR(%x = %x)\n", SASR, REG_ADDRST); */
+ if (check_region(base_io, 6))
+ continue;
+ if (! pc980155_test_port(regs))
+ continue;
+
+ if (!pc980155_getconfig(base_io, regs, &irq, &dma, &scsi_id))
+ continue;
+#ifdef DEBUG
+ printk("PC-9801-55: config: base io = %x, irq = %d, dma channel = %d, scsi id = %d\n",
+ base_io, irq, dma, scsi_id);
+#endif
+ if (request_irq(irq, pc980155_intr_handle, 0, "PC-9801-55",
+ NULL)) {
+ printk(KERN_ERR
+ "PC-9801-55: unable to allocate IRQ %d\n",
+ irq);
+ continue;
+ }
+ if (request_dma(dma, "PC-9801-55")) {
+ printk(KERN_ERR "PC-9801-55: "
+ "unable to allocate DMA channel %d\n", dma);
+ free_irq(irq, NULL);
+ continue;
+ }
+
+ request_region(base_io, 6, "PC-9801-55");
+ pc980155_host = scsi_register(tpnt, sizeof(struct WD33C93_hostdata));
+ pc980155_host->this_id = scsi_id;
+ pc980155_host->io_port = base_io;
+ pc980155_host->n_io_port = 6;
+ pc980155_host->irq = irq;
+ pc980155_host->dma_channel = dma;
+
+#ifdef DEBUG
+ printk("PC-9801-55: scsi host found at %x irq = %d, use dma channel %d.\n", base_io, irq, dma);
+ debug = read_aux_stat(regs);
+ printk("PC-9801-55: aux: %x ", debug);
+ debug = read_wd33c93(regs, 0x17);
+ printk("status: %x\n", debug);
+#endif
+
+ pc980155_int_enable(regs);
+ + wd33c93_init(pc980155_host, regs, dma_setup, dma_stop,
+ WD33C93_FS_12_15);
+ + return 1;
+ }
+
+ printk("PC-9801-55: not found\n");
+ return 0;
+}
+
+int pc980155_proc_info(char *buf, char **start, off_t off, int len,
+ int hostno, int in)
+{
+ /* NOT SUPPORTED YET! */
+
+ if (in) {
+ return -EPERM;
+ }
+ *start = buf;
+ return sprintf(buf, "Sorry, not supported yet.\n");
+}
+
+int pc980155_setup(char *str)
+{
+next:
+ if (!strncmp(str, "io:", 3)){
+ base_ios[0] = simple_strtoul(str+3,NULL,0);
+ nr_base_ios = 1;
+ while (*str > ' ' && *str != ',')
+ str++;
+ if (*str == ','){
+ str++;
+ goto next;
+ }
+ }
+ return 0;
+}
+
+int scsi_pc980155_release(struct Scsi_Host *pc980155_host)
+{
+#ifdef MODULE
+ pc980155_int_disable(regs);
+ release_region(pc980155_host->io_port, pc980155_host->n_io_port);
+ free_irq(pc980155_host->irq, NULL);
+ free_dma(pc980155_host->dma_channel);
+ wd33c93_release();
+#endif
+ return 1;
+}
+
+__setup("pc980155=", pc980155_setup);
+
+Scsi_Host_Template driver_template = SCSI_PC980155;
+
+#include "scsi_module.c"
diff -urN linux/drivers/scsi/pc980155.h linux98/drivers/scsi/pc980155.h
--- linux/drivers/scsi/pc980155.h Thu Jan 1 09:00:00 1970
+++ linux98/drivers/scsi/pc980155.h Sat Nov 2 17:51:18 2002
@@ -0,0 +1,47 @@
+/*
+ * PC-9801-55 SCSI host adapter driver
+ *
+ * Copyright (C) 1997-2000 Kyoto University Microcomputer Club
+ * (Linux/98 project)
+ */
+
+#ifndef _SCSI_PC9801_55_H
+#define _SCSI_PC9801_55_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <scsi/scsicam.h>
+
+int wd33c93_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int wd33c93_abort(Scsi_Cmnd *);
+int wd33c93_reset(Scsi_Cmnd *, unsigned int);
+int scsi_pc980155_detect(Scsi_Host_Template *);
+int scsi_pc980155_release(struct Scsi_Host *);
+int pc980155_proc_info(char *, char **, off_t, int, int, int);
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#define SCSI_PC980155 { .proc_name = "PC-9801-55", \
+ .name = "SCSI PC-9801-55", \
+ .proc_info = pc980155_proc_info, \
+ .detect = scsi_pc980155_detect, \
+ .release = scsi_pc980155_release, \
+ /* command: use queue command */ \
+ .queuecommand = wd33c93_queuecommand, \
+ /* .abort = wd33c93_abort, */ \
+ /* .reset = wd33c93_reset, */ \
+ .bios_param = pc9800_scsi_bios_param, \
+ .can_queue = CAN_QUEUE, \
+ .this_id = 7, \
+ .sg_tablesize = SG_ALL, \
+ .cmd_per_lun = CMD_PER_LUN, /* dont use link command */ \
+ .unchecked_isa_dma = 1, /* use dma **XXXX***/ \
+ .use_clustering = ENABLE_CLUSTERING }
+
+#endif /* _SCSI_PC9801_55_H */
diff -urN linux/drivers/scsi/pc980155regs.h linux98/drivers/scsi/pc980155regs.h
--- linux/drivers/scsi/pc980155regs.h Thu Jan 1 09:00:00 1970
+++ linux98/drivers/scsi/pc980155regs.h Mon Dec 3 18:44:10 2001
@@ -0,0 +1,89 @@
+#ifndef __PC980155REGS_H
+#define __PC980155REGS_H
+
+#include "wd33c93.h"
+
+#define REG_ADDRST (base_io+0)
+#define REG_CONTRL (base_io+2)
+#define REG_CWRITE (base_io+4)
+#define REG_STATRD (base_io+4)
+
+#define WD_MEMORYBANK 0x30
+#define WD_RESETINT 0x33
+
+#if 0
+#define WAIT() outb(0x00,0x5f)
+#else
+#define WAIT() do{}while(0)
+#endif
+
+static inline uchar read_wd33c93(const wd33c93_regs regs, uchar reg_num)
+{
+ uchar data;
+ outb(reg_num, *regs.SASR);
+ WAIT();
+ data = inb(*regs.SCMD);
+ WAIT();
+ return data;
+}
+
+static inline uchar read_aux_stat(const wd33c93_regs regs)
+{
+ uchar result;
+ result = inb(*regs.SASR);
+ WAIT();
+ /* printk("PC-9801-55: regp->SASR(%x) = %x\n", regp->SASR, result); */
+ return result;
+}
+#define READ_AUX_STAT() read_aux_stat(regs)
+
+static inline void write_wd33c93(const wd33c93_regs regs, uchar reg_num,
+ uchar value)
+{
+ outb(reg_num, *regs.SASR);
+ WAIT();
+ outb(value, *regs.SCMD);
+ WAIT();
+}
+
+
+#define write_wd33c93_cmd(regs,cmd) write_wd33c93(regs,WD_COMMAND,cmd)
+
+static inline void write_wd33c93_count(const wd33c93_regs regs,
+ unsigned long value)
+{
+ outb(WD_TRANSFER_COUNT_MSB, *regs.SASR);
+ WAIT();
+ outb((value >> 16) & 0xff, *regs.SCMD);
+ WAIT();
+ outb((value >> 8) & 0xff, *regs.SCMD);
+ WAIT();
+ outb( value & 0xff, *regs.SCMD);
+ WAIT();
+}
+
+
+static inline unsigned long read_wd33c93_count(const wd33c93_regs regs)
+{
+unsigned long value;
+
+ outb(WD_TRANSFER_COUNT_MSB, *regs.SASR);
+ value = inb(*regs.SCMD) << 16;
+ value |= inb(*regs.SCMD) << 8;
+ value |= inb(*regs.SCMD);
+ return value;
+}
+
+static inline void write_wd33c93_cdb(const wd33c93_regs regs, unsigned int len,
+ unsigned char cmnd[])
+{
+ int i;
+ outb(WD_CDB_1, *regs.SASR);
+ for (i=0; i<len; i++)
+ outb(cmnd[i], *regs.SCMD);
+}
+
+#define pc980155_int_enable(regs) write_wd33c93(regs, WD_MEMORYBANK, read_wd33c93(regs, WD_MEMORYBANK) | 0x04)
+#define pc980155_int_disable(regs) write_wd33c93(regs, WD_MEMORYBANK, read_wd33c93(regs, WD_MEMORYBANK) & ~0x04)
+
+#endif
diff -urN linux/drivers/scsi/scsi_scan.c linux98/drivers/scsi/scsi_scan.c
--- linux/drivers/scsi/scsi_scan.c Wed Aug 28 09:52:26 2002
+++ linux98/drivers/scsi/scsi_scan.c Wed Aug 28 13:09:39 2002
@@ -131,6 +131,7 @@
{"MITSUMI", "CD-R CR-2201CS", "6119", BLIST_NOLUN}, /* locks up */
{"RELISYS", "Scorpio", NULL, BLIST_NOLUN}, /* responds to all lun */
{"MICROTEK", "ScanMaker II", "5.61", BLIST_NOLUN}, /* responds to all lun */
+ {"NEC", "D3856", "0009", BLIST_NOLUN},
/*
* Other types of devices that have special flags.
diff -urN linux/drivers/scsi/scsi_syms.c linux98/drivers/scsi/scsi_syms.c
--- linux/drivers/scsi/scsi_syms.c Thu Oct 31 13:23:30 2002
+++ linux98/drivers/scsi/scsi_syms.c Sat Nov 2 17:10:31 2002
@@ -95,6 +95,11 @@
EXPORT_SYMBOL(scsi_devicelist);
EXPORT_SYMBOL(scsi_device_types);
+/* For PC-9800 architecture support */
+EXPORT_SYMBOL(pc9800_scsi_bios_param);
+extern struct scsi_device *sd_find_params_by_bdev(struct block_device *, char **, sector_t *);
+EXPORT_SYMBOL(sd_find_params_by_bdev);
+
/*
* Externalize timers so that HBAs can safely start/restart commands.
*/
diff -urN linux/drivers/scsi/scsicam.c linux98/drivers/scsi/scsicam.c
--- linux/drivers/scsi/scsicam.c Thu Oct 31 09:43:00 2002
+++ linux98/drivers/scsi/scsicam.c Sat Nov 2 22:19:46 2002
@@ -57,6 +57,11 @@
{
unsigned char *p;
int ret;
+ extern int pc98; /* whether PC-9800 or not. */
+
+ if (pc98)
+ if (!pc9800_scsi_bios_param(bdev, capacity, ip))
+ return 0;
p = scsi_bios_ptable(bdev);
if (!p)
@@ -240,3 +245,66 @@
*hds = (unsigned int) heads;
return (rv);
}
+
+#include <asm/pc9800.h>
+
+/* XXX - For now, we assume the first (i.e. having the least host_no)
+ real (i.e. non-emulated) host adapter shall be BIOS-controlled one.
+ We *SHOULD* invent another way. */
+
+static inline struct Scsi_Host *first_real_host(void)
+{
+ struct Scsi_Host *h = NULL;
+
+ while ((h = scsi_host_get_next(h)))
+ if (!h->hostt->emulated)
+ break;
+
+ return h;
+}
+
+extern struct scsi_device *sd_find_params_by_bdev(struct block_device *, char **, sector_t *);
+
+int pc9800_scsi_bios_param(struct block_device *bdev, sector_t capacity, int *ip)
+{
+ char *name;
+ struct scsi_device *device = sd_find_params_by_bdev(bdev, &name, NULL);
+
+ if (device && first_real_host() == device->host && device->id < 7
+ && __PC9800SCA_TEST_BIT(PC9800SCA_DISK_EQUIPS, device->id))
+ {
+ const u8 *p = (&__PC9800SCA(u8, PC9800SCA_SCSI_PARAMS)
+ + device->id * 4);
+
+ ip[0] = p[1]; /* # of heads */
+ ip[1] = p[0]; /* # of sectors/track */
+ ip[2] = *(u16 *)&p[2] & 0x0FFF; /* # of cylinders */
+ if (p[3] & (1 << 6)) { /* #-of-cylinders is 16-bit */
+ ip[2] |= (ip[0] & 0xF0) << 8;
+ ip[0] &= 0x0F;
+ }
+ printk(KERN_INFO "%s: "
+ "BIOS parameters CHS:%d/%d/%d, %u bytes %s sector\n",
+ name, ip[2], ip[0], ip[1], 256 << ((p[3] >> 4) & 3),
+ p[3] & 0x80 ? "hard" : "soft");
+ return 0;
+ }
+
+ /* Assume PC-9801-92 compatible parameters for HAs without BIOS. */
+ ip[0] = 8;
+ ip[1] = 32;
+ ip[2] = capacity / (8 * 32);
+ if (ip[2] > 65535) { /* if capacity >= 8GB */
+ /* Recent on-board adapters seem to use this parameter. */
+ ip[1] = 128;
+ ip[2] = capacity / (8 * 128);
+ if (ip[2] > 65535) { /* if capacity >= 32GB */
+ /* Clip the number of cylinders. Currently this
+ is the limit that we deal with. */
+ ip[2] = 65535;
+ }
+ }
+ printk(KERN_INFO "%s: BIOS parameters CHS:%d/%d/%d (assumed)\n",
+ name, ip[2], ip[0], ip[1]);
+ return 0;
+}
diff -urN linux/drivers/scsi/sd.c linux98/drivers/scsi/sd.c
--- linux/drivers/scsi/sd.c Thu Oct 31 13:23:30 2002
+++ linux98/drivers/scsi/sd.c Sat Nov 2 17:08:40 2002
@@ -193,6 +193,7 @@
case HDIO_GETGEO: /* Return BIOS disk parameters */
{
struct hd_geometry *loc = (struct hd_geometry *)arg;
+ extern int pc98;
if (!loc)
return -EINVAL;
@@ -208,7 +209,7 @@
/* override with calculated, extended default, or driver values */

- if (host->hostt->bios_param) {
+ if (!pc98 && host->hostt->bios_param) {
host->hostt->bios_param(sdp, bdev,
capacity, diskinfo);
} else
@@ -1315,6 +1316,27 @@
kfree(sdkp);
}
+struct scsi_device *sd_find_params_by_bdev(struct block_device *bdev, char **disk_name, sector_t *capacity)
+{
+ struct scsi_disk *sdkp;
+ int major = major(to_kdev_t(bdev->bd_dev));
+
+ spin_lock(&sd_devlist_lock);
+ list_for_each_entry(sdkp, &sd_devlist, list) {
+ if (sdkp->disk->major == major) {
+ if (capacity)
+ *capacity = sdkp->capacity;
+ if (disk_name)
+ *disk_name = sdkp->disk->disk_name;
+ spin_unlock(&sd_devlist_lock);
+ return sdkp->device;
+ }
+ }
+
+ spin_unlock(&sd_devlist_lock);
+ return NULL;
+}
+
/**
* init_sd - entry point for this driver (both when built in or when
* a module).
diff -urN linux/drivers/scsi/wd33c93.c linux98/drivers/scsi/wd33c93.c
--- linux/drivers/scsi/wd33c93.c Sat Oct 19 13:02:24 2002
+++ linux98/drivers/scsi/wd33c93.c Tue Oct 29 15:44:15 2002
@@ -84,6 +84,7 @@
#include <linux/init.h>
#include <asm/irq.h>
#include <linux/blk.h>
+#include <linux/spinlock.h>
#include "scsi.h"
#include "hosts.h"
@@ -173,7 +174,11 @@
MODULE_PARM(setup_strings, "s");
#endif
+static spinlock_t wd_lock = SPIN_LOCK_UNLOCKED;
+#if defined(CONFIG_SCSI_PC980155) || defined(CONFIG_SCSI_PC980155_MODULE)
+#include "pc980155regs.h"
+#else /* !CONFIG_SCSI_PC980155 */
static inline uchar read_wd33c93(const wd33c93_regs regs, uchar reg_num)
{
@@ -203,6 +208,7 @@
*regs.SCMD = cmd;
mb();
}
+#endif /* CONFIG_SCSI_PC980155 */
static inline uchar read_1_byte(const wd33c93_regs regs)
@@ -220,6 +226,7 @@
return x;
}
+#if !defined(CONFIG_SCSI_PC980155) && !defined(CONFIG_SCSI_PC980155_MODULE)
static void write_wd33c93_count(const wd33c93_regs regs, unsigned long value)
{
@@ -244,6 +251,7 @@
mb();
return value;
}
+#endif /* !CONFIG_SCSI_PC980155 */
/* The 33c93 needs to be told which direction a command transfers its
@@ -385,8 +393,7 @@
* sense data is not lost before REQUEST_SENSE executes.
*/
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&wd_lock, flags);
if (!(hostdata->input_Q) || (cmd->cmnd[0] == REQUEST_SENSE)) {
cmd->host_scribble = (uchar *)hostdata->input_Q;
@@ -407,7 +414,7 @@
DB(DB_QUEUE_COMMAND,printk(")Q-%ld ",cmd->pid))
- restore_flags(flags);
+ spin_unlock_irqrestore(&wd_lock, flags);
return 0;
}
@@ -428,7 +435,6 @@
struct WD33C93_hostdata *hostdata = (struct WD33C93_hostdata *)instance->hostdata;
const wd33c93_regs regs = hostdata->regs;
Scsi_Cmnd *cmd, *prev;
-int i;
DB(DB_EXECUTE,printk("EX("))
@@ -591,9 +597,16 @@
* (take advantage of auto-incrementing)
*/
- *regs.SASR = WD_CDB_1;
- for (i=0; i<cmd->cmd_len; i++)
- *regs.SCMD = cmd->cmnd[i];
+#if defined(CONFIG_SCSI_PC980155) || defined(CONFIG_SCSI_PC980155_MODULE)
+ write_wd33c93_cdb(regs, cmd->cmd_len, cmd->cmnd);
+#else /* !CONFIG_SCSI_PC980155 */
+ {
+ int i;
+ *regs.SASR = WD_CDB_1;
+ for (i = 0; i < cmd->cmd_len; i++)
+ *regs.SCMD = cmd->cmnd[i];
+ }
+#endif /* CONFIG_SCSI_PC980155 */
/* The wd33c93 only knows about Group 0, 1, and 5 commands when
* it's doing a 'select-and-transfer'. To be safe, we write the
@@ -765,7 +778,7 @@
if (!(asr & ASR_INT) || (asr & ASR_BSY))
return;
- save_flags(flags);
+ local_save_flags(flags);
#ifdef PROC_STATISTICS
hostdata->int_cnt++;
@@ -831,7 +844,7 @@
* is here...
*/
- restore_flags(flags);
+ local_irq_restore(flags);
/* We are not connected to a target - check to see if there
* are commands waiting to be executed.
@@ -1085,7 +1098,7 @@
write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
hostdata->state = S_CONNECTED;
}
- restore_flags(flags);
+ local_irq_restore(flags);
break;
@@ -1117,7 +1130,7 @@
/* We are no longer connected to a target - check to see if
* there are commands waiting to be executed.
*/
- restore_flags(flags);
+ local_irq_restore(flags);
wd33c93_execute(instance);
}
else {
@@ -1200,7 +1213,7 @@
* there are commands waiting to be executed.
*/
/* look above for comments on scsi_done() */
- restore_flags(flags);
+ local_irq_restore(flags);
wd33c93_execute(instance);
break;
@@ -1228,7 +1241,7 @@
else
cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
cmd->scsi_done(cmd);
- restore_flags(flags);
+ local_irq_restore(flags);
break;
case S_PRE_TMP_DISC:
case S_RUNNING_LEVEL2:
@@ -1693,7 +1706,7 @@
return 1;
}
-__setup("wd33c93", wd33c93_setup);
+__setup("wd33c93=", wd33c93_setup);
/* check_setup_args() returns index if key found, 0 if not
@@ -1831,10 +1844,9 @@
{ unsigned long flags;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&wd_lock, flags);
reset_wd33c93(instance);
- restore_flags(flags);
+ spin_unlock_irqrestore(&wd_lock, flags);
}
printk("wd33c93-%d: chip=%s/%d no_sync=0x%x no_dma=%d",instance->host_no,
@@ -1929,8 +1941,7 @@
return len;
}
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&wd_lock, flags);
bp = buf;
*bp = '\0';
if (hd->proc & PR_VERSION) {
@@ -2005,7 +2016,7 @@
}
}
strcat(bp,"\n");
- restore_flags(flags);
+ spin_unlock_irqrestore(&wd_lock, flags);
*start = buf;
if (stop) {
stop = 0;
@@ -2034,4 +2045,10 @@
MOD_DEC_USE_COUNT;
}
+EXPORT_SYMBOL(wd33c93_reset);
+EXPORT_SYMBOL(wd33c93_init);
+EXPORT_SYMBOL(wd33c93_release);
+EXPORT_SYMBOL(wd33c93_abort);
+EXPORT_SYMBOL(wd33c93_queuecommand);
+EXPORT_SYMBOL(wd33c93_intr);
MODULE_LICENSE("GPL");
diff -urN linux/drivers/scsi/wd33c93.h linux98/drivers/scsi/wd33c93.h
--- linux/drivers/scsi/wd33c93.h Sat Oct 12 13:21:35 2002
+++ linux98/drivers/scsi/wd33c93.h Sat Oct 12 14:18:53 2002
@@ -186,8 +186,13 @@
/* This is what the 3393 chip looks like to us */
typedef struct {
+#if defined(CONFIG_SCSI_PC980155) || defined(CONFIG_SCSI_PC980155_MODULE)
+ volatile unsigned int *SASR;
+ volatile unsigned int *SCMD;
+#else
volatile unsigned char *SASR;
volatile unsigned char *SCMD;
+#endif
} wd33c93_regs;
diff -urN linux/include/scsi/scsicam.h linux98/include/scsi/scsicam.h
--- linux/include/scsi/scsicam.h Thu Oct 31 13:23:43 2002
+++ linux98/include/scsi/scsicam.h Fri Nov 1 14:37:33 2002
@@ -16,4 +16,6 @@
extern int scsi_partsize(unsigned char *buf, unsigned long capacity,
unsigned int *cyls, unsigned int *hds, unsigned int *secs);
extern unsigned char *scsi_bios_ptable(struct block_device *bdev);
+extern int pc9800_scsi_bios_param(struct block_device *bdev, sector_t capacity,
+ int *ip);
#endif /* def SCSICAM_H */
Osamu Tomita
2002-11-02 18:00:47 UTC
Permalink
This is a part 9/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
IDE driver modules
- IO port address change.
- add support PC-9800 specific feature. (shared IRQ, serialize)

diffstat:
drivers/ide/Kconfig | 5 drivers/ide/ide-disk.c | 67 +++
drivers/ide/ide-geometry.c | 2 drivers/ide/ide-probe.c | 23 -
drivers/ide/ide-proc.c | 3 drivers/ide/ide.c | 14 drivers/ide/legacy/Makefile | 5 drivers/ide/legacy/hd98.c | 904
++++++++++++++++++++++++++++++++++++++++++++
drivers/ide/legacy/pc9800.c | 82 +++
include/asm-i386/ide.h | 18 include/linux/hdreg.h | 19 include/linux/ide.h | 2 12 files changed, 1138 insertions(+), 6
deletions(-)

patch:
diff -urN linux/drivers/ide/Kconfig linux98/drivers/ide/Kconfig
--- linux/drivers/ide/Kconfig Thu Oct 31 13:23:12 2002
+++ linux98/drivers/ide/Kconfig Sat Nov 2 18:23:53 2002
@@ -998,6 +998,11 @@
If unsure, say N.
+config BLK_DEV_IDE_PC9800
+ bool
+ depends on PC9800
+ default y
+
##if [ "$CONFIG_IDE_TASKFILE_IO" = "y" ]; then
## dep_mbool CONFIG_BLK_DEV_TF_DISK $CONFIG_BLK_DEV_IDEDISK
##else
diff -urN linux/drivers/ide/ide-disk.c linux98/drivers/ide/ide-disk.c
--- linux/drivers/ide/ide-disk.c Thu Oct 31 13:23:12 2002
+++ linux98/drivers/ide/ide-disk.c Thu Oct 31 16:45:25 2002
@@ -1596,6 +1596,71 @@
blk_queue_max_sectors(&drive->queue, 2048);
#endif
+#ifdef CONFIG_PC9800
+ /* XXX - need more checks */
+ if (!drive->nobios && !drive->scsi && !drive->removable) {
+ /* PC-9800's BIOS do pack drive numbers to be continuous,
+ so extra work is needed here. */
+
+ /* drive information passed from boot/setup.S */
+ struct drive_info_struct {
+ u16 cyl;
+ u8 sect, head;
+ u16 ssize;
+ } __attribute__ ((packed));
+ extern struct drive_info_struct drive_info[];
+
+ /* this pointer must be advanced only when *DRIVE is
+ really hard disk. */
+ static struct drive_info_struct *info = drive_info;
+
+ if (info < &drive_info[4] && info->cyl) {
+ drive->cyl = drive->bios_cyl = info->cyl;
+ drive->head = drive->bios_head = info->head;
+ drive->sect = drive->bios_sect = info->sect;
+ ++info;
+ }
+ }
+
+ /* =PC98 MEMO=
+ physical capacity =< 65535*8*17 sect. : H/S=8/17 (fixed)
+ physical capacity > 65535*8*17 sect. : use physical geometry
+ (65535*8*17 = 8912760 sectors)
+ */
+ printk("%s: CHS: physical %d/%d/%d, logical %d/%d/%d, BIOS %d/%d/%d\n",
+ drive->name,
+ id->cyls, id->heads, id->sectors,
+ id->cur_cyls, id->cur_heads, id->cur_sectors,
+ drive->bios_cyl, drive->bios_head,drive->bios_sect);
+ if (!drive->cyl || !drive->head || !drive->sect) {
+ drive->cyl = drive->bios_cyl = id->cyls;
+ drive->head = drive->bios_head = id->heads;
+ drive->sect = drive->bios_sect = id->sectors;
+ printk("%s: not BIOS-supported device.\n",drive->name);
+ }
+ /* calculate drive capacity, and select LBA if possible */
+ init_idedisk_capacity(drive);
+
+ /*
+ * if possible, give fdisk access to more of the drive,
+ * by correcting bios_cyls:
+ */
+ capacity = idedisk_capacity(drive);
+ if (capacity < 8912760 &&
+ (drive->head != 8 || drive->sect != 17)) {
+ drive->head = drive->bios_head = 8;
+ drive->sect = drive->bios_sect = 17;
+ drive->cyl = drive->bios_cyl =
+ capacity / (drive->bios_head * drive->bios_sect);
+ printk("%s: Fixing Geometry :: CHS=%d/%d/%d to CHS=%d/%d/%d\n",
+ drive->name,
+ id->cur_cyls,id->cur_heads,id->cur_sectors,
+ drive->bios_cyl,drive->bios_head,drive->bios_sect);
+ id->cur_cyls = drive->bios_cyl;
+ id->cur_heads = drive->bios_head;
+ id->cur_sectors = drive->bios_sect;
+ }
+#else /* !CONFIG_PC9800 */
/* Extract geometry if we did not already have one for the drive */
if (!drive->cyl || !drive->head || !drive->sect) {
drive->cyl = drive->bios_cyl = id->cyls;
@@ -1629,6 +1694,8 @@
if ((capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head)) &&
(!drive->forced_geom) && drive->bios_sect && drive->bios_head)
drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head;
+#endif /* CONFIG_PC9800 */
+
printk (KERN_INFO "%s: %ld sectors", drive->name, capacity);
/* Give size in megabytes (MB), not mebibytes (MiB). */
diff -urN linux/drivers/ide/ide-geometry.c linux98/drivers/ide/ide-geometry.c
--- linux/drivers/ide/ide-geometry.c Sat Oct 19 13:02:28 2002
+++ linux98/drivers/ide/ide-geometry.c Sat Oct 19 15:08:56 2002
@@ -6,6 +6,7 @@
#include <linux/mc146818rtc.h>
#include <asm/io.h>
+#ifndef CONFIG_PC9800
/*
* We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc
* controller that is BIOS compatible with ST-506, and thus showing up in our
@@ -81,6 +82,7 @@
}
#endif
}
+#endif /* !CONFIG_PC9800 */
extern unsigned long current_capacity (ide_drive_t *);
diff -urN linux/drivers/ide/ide-probe.c linux98/drivers/ide/ide-probe.c
--- linux/drivers/ide/ide-probe.c Thu Oct 31 13:23:12 2002
+++ linux98/drivers/ide/ide-probe.c Thu Oct 31 16:45:25 2002
@@ -55,6 +55,11 @@
#include <asm/io.h>
/*
+ * Whether PC-9800 architecture or not.
+ */
+extern int pc98;
+
+/*
* CompactFlash cards and their brethern pretend to be removable
* hard disks, except:
* (1) they never have a slave unit, and
@@ -554,7 +559,7 @@
if (hwif->mmio == 2)
return 0;
- addr_errs = hwif_check_region(hwif->io_ports[IDE_DATA_OFFSET], 1);
+ addr_errs = hwif_check_region(hwif->io_ports[IDE_DATA_OFFSET], pc98 ? 2 : 1);
for (i = IDE_ERROR_OFFSET; i <= IDE_STATUS_OFFSET; i++)
addr_errs += hwif_check_region(hwif->io_ports[i], 1);
if (hwif->io_ports[IDE_CONTROL_OFFSET])
@@ -603,7 +608,9 @@
}
for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++)
- hwif_request_region(hwif->io_ports[i], 1, hwif->name);
+ hwif_request_region(hwif->io_ports[i],
+ (pc98 && i == IDE_DATA_OFFSET) ? 2 : 1,
+ hwif->name);
}
//EXPORT_SYMBOL(hwif_register);
@@ -620,7 +627,7 @@
if (hwif->noprobe)
return;
-#ifdef CONFIG_BLK_DEV_IDE
+#if !defined(CONFIG_PC9800) && defined(CONFIG_BLK_DEV_IDE)
if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA) {
extern void probe_cmos_for_drives(ide_hwif_t *);
probe_cmos_for_drives(hwif);
@@ -631,6 +638,9 @@
#if CONFIG_BLK_DEV_PDC4030
(hwif->chipset != ide_pdc4030 || hwif->channel == 0) &&
#endif /* CONFIG_BLK_DEV_PDC4030 */
+#if CONFIG_BLK_DEV_IDE_PC9800
+ (hwif->chipset != ide_pc9800 || !hwif->mate->present) &&
+#endif
(hwif_check_regions(hwif))) {
u16 msgout = 0;
for (unit = 0; unit < MAX_DRIVES; ++unit) {
@@ -950,7 +960,7 @@
/* all CPUs; safe now that hwif->hwgroup is set up */
spin_unlock_irqrestore(&ide_lock, flags);
-#if !defined(__mc68000__) && !defined(CONFIG_APUS) && !defined(__sparc__)
+#if !defined(__mc68000__) && !defined(CONFIG_APUS) && !defined(__sparc__) && !defined(CONFIG_PC9800)
printk("%s at 0x%03lx-0x%03lx,0x%03lx on irq %d", hwif->name,
hwif->io_ports[IDE_DATA_OFFSET],
hwif->io_ports[IDE_DATA_OFFSET]+7,
@@ -960,6 +970,11 @@
hwif->io_ports[IDE_DATA_OFFSET],
hwif->io_ports[IDE_DATA_OFFSET]+7,
hwif->io_ports[IDE_CONTROL_OFFSET], __irq_itoa(hwif->irq));
+#elif defined(CONFIG_PC9800)
+ printk("%s at 0x%03lx-0x%03lx,0x%03lx on irq %d", hwif->name,
+ hwif->io_ports[IDE_DATA_OFFSET],
+ hwif->io_ports[IDE_DATA_OFFSET]+15,
+ hwif->io_ports[IDE_CONTROL_OFFSET], hwif->irq);
#else
printk("%s at %x on irq 0x%08x", hwif->name,
hwif->io_ports[IDE_DATA_OFFSET], hwif->irq);
diff -urN linux/drivers/ide/ide-proc.c linux98/drivers/ide/ide-proc.c
--- linux/drivers/ide/ide-proc.c Mon Sep 16 11:18:30 2002
+++ linux98/drivers/ide/ide-proc.c Mon Sep 16 13:53:42 2002
@@ -365,6 +365,9 @@
case ide_cy82c693: name = "cy82c693"; break;
case ide_4drives: name = "4drives"; break;
case ide_pmac: name = "mac-io"; break;
+#ifdef CONFIG_PC9800
+ case ide_pc9800: name = "pc9800"; break;
+#endif
default: name = "(unknown)"; break;
}
len = sprintf(page, "%s\n", name);
diff -urN linux/drivers/ide/ide.c linux98/drivers/ide/ide.c
--- linux/drivers/ide/ide.c Thu Oct 31 13:23:13 2002
+++ linux98/drivers/ide/ide.c Thu Oct 31 16:45:25 2002
@@ -189,6 +189,11 @@
static int ide_intr_lock;
#endif /* __mc68000__ || CONFIG_APUS */
+/*
+ * Whether PC-9800 architecture or not.
+ */
+extern int pc98;
+
#ifdef CONFIG_IDEDMA_AUTO
int noautodma = 0;
#else
@@ -1657,7 +1662,8 @@
}
for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
if (hwif->io_ports[i]) {
- hwif_release_region(hwif->io_ports[i], 1);
+ hwif_release_region(hwif->io_ports[i],
+ (pc98 && i == IDE_DATA_OFFSET) ? 2 : 1);
}
}
}
@@ -3009,6 +3015,12 @@
}
#endif /* CONFIG_BLK_DEV_IDEPCI */
+#ifdef CONFIG_BLK_DEV_IDE_PC9800
+ {
+ extern void ide_probe_for_pc9800(void);
+ ide_probe_for_pc9800();
+ }
+#endif
#ifdef CONFIG_ETRAX_IDE
{
extern void init_e100_ide(void);
diff -urN linux/drivers/ide/legacy/Makefile linux98/drivers/ide/legacy/Makefile
--- linux/drivers/ide/legacy/Makefile Sat Oct 12 13:21:34 2002
+++ linux98/drivers/ide/legacy/Makefile Sun Oct 13 11:07:36 2002
@@ -2,6 +2,7 @@
obj-$(CONFIG_BLK_DEV_ALI14XX) += ali14xx.o
obj-$(CONFIG_BLK_DEV_DTC2278) += dtc2278.o
obj-$(CONFIG_BLK_DEV_HT6560B) += ht6560b.o
+obj-$(CONFIG_BLK_DEV_IDE_PC9800) += pc9800.o
obj-$(CONFIG_BLK_DEV_PDC4030) += pdc4030.o
obj-$(CONFIG_BLK_DEV_QD65XX) += qd65xx.o
obj-$(CONFIG_BLK_DEV_UMC8672) += umc8672.o
@@ -15,7 +16,11 @@
obj-$(CONFIG_BLK_DEV_IDECS) += ide-cs.o
# Last of all
+ifneq ($(CONFIG_PC9800),y)
obj-$(CONFIG_BLK_DEV_HD) += hd.o
+else
+obj-$(CONFIG_BLK_DEV_HD) += hd98.o
+endif
EXTRA_CFLAGS := -Idrivers/ide
diff -urN linux/drivers/ide/legacy/hd98.c linux98/drivers/ide/legacy/hd98.c
--- linux/drivers/ide/legacy/hd98.c Thu Jan 1 09:00:00 1970
+++ linux98/drivers/ide/legacy/hd98.c Sat Oct 26 15:42:09 2002
@@ -0,0 +1,904 @@
+/*
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * This is the low-level hd interrupt support. It traverses the
+ * request-list, using interrupts to jump between functions. As
+ * all the functions are called within interrupts, we may not
+ * sleep. Special care is recommended.
+ *
+ * modified by Drew Eckhardt to check nr of hd's from the CMOS.
+ *
+ * Thanks to Branko Lankester, ***@fwi.uva.nl, who found a bug
+ * in the early extended-partition checks and added DM partitions
+ *
+ * IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
+ * and general streamlining by Mark Lord.
+ *
+ * Removed 99% of above. Use Mark's ide driver for those options.
+ * This is now a lightweight ST-506 driver. (Paul Gortmaker)
+ *
+ * Modified 1995 Russell King for ARM processor.
+ *
+ * Bugfix: max_sectors must be <= 255 or the wheels tend to come
+ * off in a hurry once you queue things up - Paul G. 02/2001
+ */
+
+/* Uncomment the following if you want verbose error reports. */
+/* #define VERBOSE_ERRORS */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/genhd.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/mc146818rtc.h> /* CMOS defines */
+#include <linux/init.h>
+#include <linux/blkpg.h>
+#include <linux/hdreg.h>
+
+#define REALLY_SLOW_IO
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#define MAJOR_NR HD_MAJOR
+#define DEVICE_NR(device) (minor(device)>>6)
+#include <linux/blk.h>
+
+#include "io_ports.h"
+
+#ifdef __arm__
+#undef HD_IRQ
+#endif
+#include <asm/irq.h>
+#ifdef __arm__
+#define HD_IRQ IRQ_HARDDISK
+#endif
+
+/* Hd controller regster ports */
+
+#define HD_DATA 0x640 /* _CTL when writing */
+#define HD_ERROR 0x642 /* see err-bits */
+#define HD_NSECTOR 0x644 /* nr of sectors to read/write */
+#define HD_SECTOR 0x646 /* starting sector */
+#define HD_LCYL 0x648 /* starting cylinder */
+#define HD_HCYL 0x64a /* high byte of starting cyl */
+#define HD_CURRENT 0x64c /* 101dhhhh , d=drive, hhhh=head */
+#define HD_STATUS 0x64e /* see status-bits */
+#define HD_FEATURE HD_ERROR /* same io address, read=error, write=feature */
+#define HD_PRECOMP HD_FEATURE /* obsolete use of this port - predates IDE */
+#define HD_COMMAND HD_STATUS /* same io address, read=status, write=cmd */
+
+#define HD_CMD 0x74c /* used for resets */
+#define HD_ALTSTATUS 0x74c /* same as HD_STATUS but doesn't clear irq */
+
+/* Bits of HD_STATUS */
+#define ERR_STAT 0x01
+#define INDEX_STAT 0x02
+#define ECC_STAT 0x04 /* Corrected error */
+#define DRQ_STAT 0x08
+#define SEEK_STAT 0x10
+#define SERVICE_STAT SEEK_STAT
+#define WRERR_STAT 0x20
+#define READY_STAT 0x40
+#define BUSY_STAT 0x80
+
+/* Bits for HD_ERROR */
+#define MARK_ERR 0x01 /* Bad address mark */
+#define TRK0_ERR 0x02 /* couldn't find track 0 */
+#define ABRT_ERR 0x04 /* Command aborted */
+#define MCR_ERR 0x08 /* media change request */
+#define ID_ERR 0x10 /* ID field not found */
+#define MC_ERR 0x20 /* media changed */
+#define ECC_ERR 0x40 /* Uncorrectable ECC error */
+#define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */
+#define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */
+
+static spinlock_t hd_lock = SPIN_LOCK_UNLOCKED;
+
+#define TIMEOUT_VALUE (6*HZ)
+#define HD_DELAY 0
+
+#define MAX_ERRORS 16 /* Max read/write errors/sector */
+#define RESET_FREQ 8 /* Reset controller every 8th retry */
+#define RECAL_FREQ 4 /* Recalibrate every 4th retry */
+#define MAX_HD 2
+
+#define STAT_OK (READY_STAT|SEEK_STAT)
+#define OK_STATUS(s) (((s)&(STAT_OK|(BUSY_STAT|WRERR_STAT|ERR_STAT)))==STAT_OK)
+
+static void recal_intr(void);
+static void bad_rw_intr(void);
+
+static char recalibrate[MAX_HD];
+static char special_op[MAX_HD];
+
+static int reset;
+static int hd_error;
+
+#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
+
+/*
+ * This struct defines the HD's and their types.
+ */
+struct hd_i_struct {
+ unsigned int head,sect,cyl,wpcom,lzone,ctl;
+};
+
+#ifdef HD_TYPE
+struct hd_i_struct hd_info[] = { HD_TYPE };
+static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct)));
+#else
+struct hd_i_struct hd_info[MAX_HD];
+static int NR_HD;
+#endif
+
+static struct gendisk *hd_gendisk[MAX_HD];
+
+static struct timer_list device_timer;
+
+#define TIMEOUT_VALUE (6*HZ)
+
+#define SET_TIMER \
+ do { \
+ mod_timer(&device_timer, jiffies + TIMEOUT_VALUE); \
+ } while (0)
+
+static void (*do_hd)(void) = NULL;
+#define SET_HANDLER(x) \
+if ((do_hd = (x)) != NULL) \
+ SET_TIMER; \
+else \
+ del_timer(&device_timer);
+
+
+#if (HD_DELAY > 0)
+unsigned long last_req;
+
+unsigned long read_timer(void)
+{
+ extern spinlock_t i8253_lock;
+ unsigned long t, flags;
+ int i;
+
+ spin_lock_irqsave(&i8253_lock, flags);
+ t = jiffies * 11932;
+ outb_p(0, PIT_MODE);
+ i = inb_p(PIT_CH0);
+ i |= inb(PIT_CH0) << 8;
+ spin_unlock_irqrestore(&i8253_lock, flags);
+ return(t - i);
+}
+#endif
+
+void __init hd_setup(char *str, int *ints)
+{
+ int hdind = 0;
+
+ if (ints[0] != 3)
+ return;
+ if (hd_info[0].head != 0)
+ hdind=1;
+ hd_info[hdind].head = ints[2];
+ hd_info[hdind].sect = ints[3];
+ hd_info[hdind].cyl = ints[1];
+ hd_info[hdind].wpcom = 0;
+ hd_info[hdind].lzone = ints[1];
+ hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
+ NR_HD = hdind+1;
+}
+
+static void dump_status (const char *msg, unsigned int stat)
+{
+ char devc;
+
+ devc = !blk_queue_empty(QUEUE) ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?';
+#ifdef VERBOSE_ERRORS
+ printk("hd%c: %s: status=0x%02x { ", devc, msg, stat & 0xff);
+ if (stat & BUSY_STAT) printk("Busy ");
+ if (stat & READY_STAT) printk("DriveReady ");
+ if (stat & WRERR_STAT) printk("WriteFault ");
+ if (stat & SEEK_STAT) printk("SeekComplete ");
+ if (stat & DRQ_STAT) printk("DataRequest ");
+ if (stat & ECC_STAT) printk("CorrectedError ");
+ if (stat & INDEX_STAT) printk("Index ");
+ if (stat & ERR_STAT) printk("Error ");
+ printk("}\n");
+ if ((stat & ERR_STAT) == 0) {
+ hd_error = 0;
+ } else {
+ hd_error = inb(HD_ERROR);
+ printk("hd%c: %s: error=0x%02x { ", devc, msg, hd_error & 0xff);
+ if (hd_error & BBD_ERR) printk("BadSector ");
+ if (hd_error & ECC_ERR) printk("UncorrectableError ");
+ if (hd_error & ID_ERR) printk("SectorIdNotFound ");
+ if (hd_error & ABRT_ERR) printk("DriveStatusError ");
+ if (hd_error & TRK0_ERR) printk("TrackZeroNotFound ");
+ if (hd_error & MARK_ERR) printk("AddrMarkNotFound ");
+ printk("}");
+ if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
+ printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL),
+ inb(HD_CURRENT) & 0xf, inb(HD_SECTOR));
+ if (!blk_queue_empty(QUEUE))
+ printk(", sector=%ld", CURRENT->sector);
+ }
+ printk("\n");
+ }
+#else
+ printk("hd%c: %s: status=0x%02x.\n", devc, msg, stat & 0xff);
+ if ((stat & ERR_STAT) == 0) {
+ hd_error = 0;
+ } else {
+ hd_error = inb(HD_ERROR);
+ printk("hd%c: %s: error=0x%02x.\n", devc, msg, hd_error & 0xff);
+ }
+#endif
+}
+
+void check_status(void)
+{
+ int i = inb(HD_STATUS);
+
+ if (!OK_STATUS(i)) {
+ dump_status("check_status", i);
+ bad_rw_intr();
+ }
+}
+
+static int controller_busy(void)
+{
+ int retries = 100000;
+ unsigned char status;
+
+ do {
+ status = inb(HD_STATUS);
+ } while ((status & BUSY_STAT) && --retries);
+ return status;
+}
+
+static int status_ok(void)
+{
+ unsigned char status = inb(HD_STATUS);
+
+ if (status & BUSY_STAT)
+ return 1; /* Ancient, but does it make sense??? */
+ if (status & WRERR_STAT)
+ return 0;
+ if (!(status & READY_STAT))
+ return 0;
+ if (!(status & SEEK_STAT))
+ return 0;
+ return 1;
+}
+
+static int controller_ready(unsigned int drive, unsigned int head)
+{
+ int retry = 100;
+
+ do {
+ if (controller_busy() & BUSY_STAT)
+ return 0;
+ outb(0xA0 | (drive<<4) | head, HD_CURRENT);
+ if (status_ok())
+ return 1;
+ } while (--retry);
+ return 0;
+}
+
+static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
+ unsigned int head,unsigned int cyl,unsigned int cmd,
+ void (*intr_addr)(void))
+{
+ unsigned short port;
+
+#if (HD_DELAY > 0)
+ while (read_timer() - last_req < HD_DELAY)
+ /* nothing */;
+#endif
+ if (reset)
+ return;
+ if (!controller_ready(drive, head)) {
+ reset = 1;
+ return;
+ }
+ SET_HANDLER(intr_addr);
+ outb(hd_info[drive].ctl,HD_CMD);
+ port=HD_DATA + 2;
+ outb(hd_info[drive].wpcom>>2, port); port += 2;
+ outb(nsect, port); port += 2;
+ outb(sect, port); port += 2;
+ outb(cyl, port); port += 2;
+ outb(cyl>>8, port); port += 2;
+ outb(0xA0|(drive<<4)|head, port); port += 2;
+ outb(cmd, port);
+}
+
+static void hd_request (void);
+
+static int drive_busy(void)
+{
+ unsigned int i;
+ unsigned char c;
+
+ for (i = 0; i < 500000 ; i++) {
+ c = inb(HD_STATUS);
+ if ((c & (BUSY_STAT | READY_STAT | SEEK_STAT)) == STAT_OK)
+ return 0;
+ }
+ dump_status("reset timed out", c);
+ return 1;
+}
+
+static void reset_controller(void)
+{
+ int i;
+
+ outb(4,HD_CMD);
+ for(i = 0; i < 1000; i++) barrier();
+ outb(hd_info[0].ctl & 0x0f,HD_CMD);
+ for(i = 0; i < 1000; i++) barrier();
+ if (drive_busy())
+ printk("hd: controller still busy\n");
+ else if ((hd_error = inb(HD_ERROR)) != 1)
+ printk("hd: controller reset failed: %02x\n",hd_error);
+}
+
+static void reset_hd(void)
+{
+ static int i;
+
+repeat:
+ if (reset) {
+ reset = 0;
+ i = -1;
+ reset_controller();
+ } else {
+ check_status();
+ if (reset)
+ goto repeat;
+ }
+ if (++i < NR_HD) {
+ special_op[i] = recalibrate[i] = 1;
+ hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
+ hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
+ if (reset)
+ goto repeat;
+ } else
+ hd_request();
+}
+
+/*
+ * Ok, don't know what to do with the unexpected interrupts: on some machines
+ * doing a reset and a retry seems to result in an eternal loop. Right now I
+ * ignore it, and just set the timeout.
+ *
+ * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
+ * drive enters "idle", "standby", or "sleep" mode, so if the status looks
+ * "good", we just ignore the interrupt completely.
+ */
+void unexpected_hd_interrupt(void)
+{
+ unsigned int stat = inb(HD_STATUS);
+
+ if (stat & (BUSY_STAT|DRQ_STAT|ECC_STAT|ERR_STAT)) {
+ dump_status ("unexpected interrupt", stat);
+ SET_TIMER;
+ }
+}
+
+/*
+ * bad_rw_intr() now tries to be a bit smarter and does things
+ * according to the error returned by the controller.
+ * -Mika Liljeberg (***@cs.Helsinki.FI)
+ */
+static void bad_rw_intr(void)
+{
+ int dev;
+
+ if (blk_queue_empty(QUEUE))
+ return;
+ dev = DEVICE_NR(CURRENT->rq_dev);
+ if (++CURRENT->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
+ end_request(CURRENT, 0);
+ special_op[dev] = recalibrate[dev] = 1;
+ } else if (CURRENT->errors % RESET_FREQ == 0)
+ reset = 1;
+ else if ((hd_error & TRK0_ERR) || CURRENT->errors % RECAL_FREQ == 0)
+ special_op[dev] = recalibrate[dev] = 1;
+ /* Otherwise just retry */
+}
+
+static inline int wait_DRQ(void)
+{
+ int retries = 100000, stat;
+
+ while (--retries > 0)
+ if ((stat = inb(HD_STATUS)) & DRQ_STAT)
+ return 0;
+ dump_status("wait_DRQ", stat);
+ return -1;
+}
+
+static void read_intr(void)
+{
+ int i, retries = 100000;
+
+ do {
+ i = (unsigned) inb(HD_STATUS);
+ if (i & BUSY_STAT)
+ continue;
+ if (!OK_STATUS(i))
+ break;
+ if (i & DRQ_STAT)
+ goto ok_to_read;
+ } while (--retries > 0);
+ dump_status("read_intr", i);
+ bad_rw_intr();
+ hd_request();
+ return;
+ok_to_read:
+ insw(HD_DATA,CURRENT->buffer,256);
+ CURRENT->sector++;
+ CURRENT->buffer += 512;
+ CURRENT->errors = 0;
+ i = --CURRENT->nr_sectors;
+ --CURRENT->current_nr_sectors;
+#ifdef DEBUG
+ printk("hd%c: read: sector %ld, remaining = %ld, buffer=0x%08lx\n",
+ dev+'a', CURRENT->sector, CURRENT->nr_sectors,
+ (unsigned long) CURRENT->buffer+512);
+#endif
+ if (CURRENT->current_nr_sectors <= 0)
+ end_request(CURRENT, 1);
+ if (i > 0) {
+ SET_HANDLER(&read_intr);
+ return;
+ }
+ (void) inb(HD_STATUS);
+#if (HD_DELAY > 0)
+ last_req = read_timer();
+#endif
+ if (!blk_queue_empty(QUEUE))
+ hd_request();
+ return;
+}
+
+static void write_intr(void)
+{
+ int i;
+ int retries = 100000;
+
+ do {
+ i = (unsigned) inb(HD_STATUS);
+ if (i & BUSY_STAT)
+ continue;
+ if (!OK_STATUS(i))
+ break;
+ if ((CURRENT->nr_sectors <= 1) || (i & DRQ_STAT))
+ goto ok_to_write;
+ } while (--retries > 0);
+ dump_status("write_intr", i);
+ bad_rw_intr();
+ hd_request();
+ return;
+ok_to_write:
+ CURRENT->sector++;
+ i = --CURRENT->nr_sectors;
+ --CURRENT->current_nr_sectors;
+ CURRENT->buffer += 512;
+ if (!i || (CURRENT->bio && !SUBSECTOR(i)))
+ end_request(CURRENT, 1);
+ if (i > 0) {
+ SET_HANDLER(&write_intr);
+ outsw(HD_DATA,CURRENT->buffer,256);
+ local_irq_enable();
+ } else {
+#if (HD_DELAY > 0)
+ last_req = read_timer();
+#endif
+ hd_request();
+ }
+ return;
+}
+
+static void recal_intr(void)
+{
+ check_status();
+#if (HD_DELAY > 0)
+ last_req = read_timer();
+#endif
+ hd_request();
+}
+
+/*
+ * This is another of the error-routines I don't know what to do with. The
+ * best idea seems to just set reset, and start all over again.
+ */
+static void hd_times_out(unsigned long dummy)
+{
+ unsigned int dev;
+
+ do_hd = NULL;
+
+ if (blk_queue_empty(QUEUE))
+ return;
+
+ disable_irq(HD_IRQ);
+ local_irq_enable();
+ reset = 1;
+ dev = DEVICE_NR(CURRENT->rq_dev);
+ printk("hd%c: timeout\n", dev+'a');
+ if (++CURRENT->errors >= MAX_ERRORS) {
+#ifdef DEBUG
+ printk("hd%c: too many errors\n", dev+'a');
+#endif
+ end_request(CURRENT, 0);
+ }
+ local_irq_disable();
+ hd_request();
+ enable_irq(HD_IRQ);
+}
+
+int do_special_op (unsigned int dev)
+{
+ if (recalibrate[dev]) {
+ recalibrate[dev] = 0;
+ hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
+ return reset;
+ }
+ if (hd_info[dev].head > 16) {
+ printk ("hd%c: cannot handle device with more than 16 heads - giving up\n", dev+'a');
+ end_request(CURRENT, 0);
+ }
+ special_op[dev] = 0;
+ return 1;
+}
+
+/*
+ * The driver enables interrupts as much as possible. In order to do this,
+ * (a) the device-interrupt is disabled before entering hd_request(),
+ * and (b) the timeout-interrupt is disabled before the sti().
+ *
+ * Interrupts are still masked (by default) whenever we are exchanging
+ * data/cmds with a drive, because some drives seem to have very poor
+ * tolerance for latency during I/O. The IDE driver has support to unmask
+ * interrupts for non-broken hardware, so use that driver if required.
+ */
+static void hd_request(void)
+{
+ unsigned int dev, block, nsect, sec, track, head, cyl;
+
+ if (do_hd)
+ return;
+repeat:
+ del_timer(&device_timer);
+ local_irq_enable();
+
+ if (blk_queue_empty(QUEUE)) {
+ do_hd = NULL;
+ return;
+ }
+
+ if (reset) {
+ local_irq_disable();
+ reset_hd();
+ return;
+ }
+ dev = DEVICE_NR(CURRENT->rq_dev);
+ block = CURRENT->sector;
+ nsect = CURRENT->nr_sectors;
+ if (dev >= NR_HD) {
+ printk("hd: bad disk number: %d\n", dev);
+ end_request(CURRENT, 0);
+ goto repeat;
+ }
+ if (block >= get_capacity(hd_gendisk[dev]) ||
+ ((block+nsect) > get_capacity(hd_gendisk[dev]))) {
+ printk("%s: bad access: block=%d, count=%d\n",
+ hd_gendisk[dev]->disk_name, block, nsect);
+ end_request(CURRENT, 0);
+ goto repeat;
+ }
+
+ if (special_op[dev]) {
+ if (do_special_op(dev))
+ goto repeat;
+ return;
+ }
+ sec = block % hd_info[dev].sect + 1;
+ track = block / hd_info[dev].sect;
+ head = track % hd_info[dev].head;
+ cyl = track / hd_info[dev].head;
+#ifdef DEBUG
+ printk("hd%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx\n",
+ dev+'a', (CURRENT->cmd == READ)?"read":"writ",
+ cyl, head, sec, nsect, (unsigned long) CURRENT->buffer);
+#endif
+ if(CURRENT->flags & REQ_CMD) {
+ switch (rq_data_dir(CURRENT)) {
+ case READ:
+ hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
+ if (reset)
+ goto repeat;
+ break;
+ case WRITE:
+ hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
+ if (reset)
+ goto repeat;
+ if (wait_DRQ()) {
+ bad_rw_intr();
+ goto repeat;
+ }
+ outsw(HD_DATA,CURRENT->buffer,256);
+ break;
+ default:
+ printk("unknown hd-command\n");
+ end_request(CURRENT, 0);
+ break;
+ }
+ }
+}
+
+static void do_hd_request (request_queue_t * q)
+{
+ disable_irq(HD_IRQ);
+ hd_request();
+ enable_irq(HD_IRQ);
+}
+
+static int hd_ioctl(struct inode * inode, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct hd_geometry *loc = (struct hd_geometry *) arg;
+ int dev;
+
+ if ((!inode) || kdev_none(inode->i_rdev))
+ return -EINVAL;
+ dev = DEVICE_NR(inode->i_rdev);
+ if (dev >= NR_HD)
+ return -EINVAL;
+ switch (cmd) {
+ case HDIO_GETGEO:
+ {
+ struct hd_geometry g; + if (!loc) return -EINVAL;
+ g.heads = hd_info[dev].head;
+ g.sectors = hd_info[dev].sect;
+ g.cylinders = hd_info[dev].cyl;
+ g.start = get_start_sect(inode->i_bdev);
+ return copy_to_user(loc, &g, sizeof g) ? -EFAULT : 0; + }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int hd_open(struct inode * inode, struct file * filp)
+{
+ int target = DEVICE_NR(inode->i_rdev);
+ if (target >= NR_HD)
+ return -ENODEV;
+ return 0;
+}
+
+/*
+ * Releasing a block device means we sync() it, so that it can safely
+ * be forgotten about...
+ */
+
+extern struct block_device_operations hd_fops;
+
+static void hd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ void (*handler)(void) = do_hd;
+
+ do_hd = NULL;
+ del_timer(&device_timer);
+ if (!handler)
+ handler = unexpected_hd_interrupt;
+ handler();
+ local_irq_enable();
+}
+
+static struct block_device_operations hd_fops = {
+ .open = hd_open,
+ .ioctl = hd_ioctl,
+};
+
+/*
+ * This is the hard disk IRQ description. The SA_INTERRUPT in sa_flags
+ * means we run the IRQ-handler with interrupts disabled: this is bad for
+ * interrupt latency, but anything else has led to problems on some
+ * machines.
+ *
+ * We enable interrupts in some of the routines after making sure it's
+ * safe.
+ */
+
+static int __init hd_init(void)
+{
+ int drive;
+ if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
+ printk("hd: unable to get major %d for hard disk\n",MAJOR_NR);
+ return -1;
+ }
+ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_hd_request, &hd_lock);
+ blk_queue_max_sectors(BLK_DEFAULT_QUEUE(MAJOR_NR), 255);
+ init_timer(&device_timer);
+ device_timer.function = hd_times_out;
+ blk_queue_hardsect_size(QUEUE, 512);
+
+#ifdef __i386__
+ if (!NR_HD) {
+ extern struct drive_info drive_info;
+ unsigned char *BIOS = (unsigned char *) &drive_info;
+ unsigned long flags;
+#ifndef CONFIG_PC9800
+ int cmos_disks;
+#endif
+
+ for (drive=0 ; drive<2 ; drive++) {
+ hd_info[drive].cyl = *(unsigned short *) BIOS;
+ hd_info[drive].head = *(3+BIOS);
+ hd_info[drive].sect = *(2+BIOS);
+ hd_info[drive].wpcom = 0;
+ hd_info[drive].ctl = *(3+BIOS) > 8 ? 8 : 0;
+ hd_info[drive].lzone = *(unsigned short *) BIOS;
+ if (hd_info[drive].cyl && NR_HD == drive)
+ NR_HD++;
+ BIOS += 6;
+ }
+
+ }
+#endif /* __i386__ */
+#ifdef __arm__
+ if (!NR_HD) {
+ /* We don't know anything about the drive. This means
+ * that you *MUST* specify the drive parameters to the
+ * kernel yourself.
+ */
+ printk("hd: no drives specified - use hd=cyl,head,sectors"
+ " on kernel command line\n");
+ }
+#endif
+ if (!NR_HD)
+ goto out;
+
+ for (drive=0 ; drive < NR_HD ; drive++) {
+ struct gendisk *disk = alloc_disk();
+ if (!disk)
+ goto Enomem;
+ disk->major = MAJOR_NR;
+ disk->first_minor = drive << 6;
+ disk->minor_shift = 6;
+ disk->fops = &hd_fops;
+ sprintf(disk->disk_name, "hd%c", 'a'+drive);
+ hd_gendisk[drive] = disk;
+ }
+ for (drive=0 ; drive < NR_HD ; drive++) {
+ sector_t size = hd_info[drive].head *
+ hd_info[drive].sect * hd_info[drive].cyl;
+ set_capacity(hd_gendisk[drive], size);
+ printk ("%s: %ldMB, CHS=%d/%d/%d\n",
+ hd_gendisk[drive]->disk_name,
+ size / 2048, hd_info[drive].cyl,
+ hd_info[drive].head, hd_info[drive].sect);
+ }
+
+ if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd", NULL)) {
+ printk("hd: unable to get IRQ%d for the hard disk driver\n",
+ HD_IRQ);
+ goto out1;
+ }
+
+ if (!request_region(HD_DATA, 2, "hd(data)")) {
+ printk(KERN_WARNING "hd: port 0x%x busy\n", HD_DATA);
+ NR_HD = 0;
+ free_irq(HD_IRQ, NULL);
+ return;
+ }
+
+ if (!request_region(HD_DATA + 2, 1, "hd"))
+ {
+ printk(KERN_WARNING "hd: port 0x%x busy\n", HD_DATA);
+ goto out2;
+ }
+
+ if (!request_region(HD_DATA + 4, 1, "hd"))
+ {
+ printk(KERN_WARNING "hd: port 0x%x busy\n", HD_DATA);
+ goto out3;
+ }
+
+ if (!request_region(HD_DATA + 6, 1, "hd"))
+ {
+ printk(KERN_WARNING "hd: port 0x%x busy\n", HD_DATA);
+ goto out4;
+ }
+
+ if (!request_region(HD_DATA + 8, 1, "hd"))
+ {
+ printk(KERN_WARNING "hd: port 0x%x busy\n", HD_DATA);
+ goto out5;
+ }
+
+ if (!request_region(HD_DATA + 10, 1, "hd"))
+ {
+ printk(KERN_WARNING "hd: port 0x%x busy\n", HD_DATA);
+ goto out6;
+ }
+
+ if (!request_region(HD_DATA + 12, 1, "hd"))
+ {
+ printk(KERN_WARNING "hd: port 0x%x busy\n", HD_DATA);
+ goto out7;
+ }
+
+ if (!request_region(HD_CMD, 1, "hd(cmd)"))
+ {
+ printk(KERN_WARNING "hd: port 0x%x busy\n", HD_CMD);
+ goto out8;
+ }
+
+ if (!request_region(HD_CMD + 2, 1, "hd(cmd)"))
+ {
+ printk(KERN_WARNING "hd: port 0x%x busy\n", HD_CMD);
+ goto out9;
+ }
+
+ for(drive=0; drive < NR_HD; drive++) {
+ struct hd_i_struct *p = hd_info + drive;
+ set_capacity(hd_gendisk[drive], p->head * p->sect * p->cyl);
+ add_disk(hd_gendisk[drive]);
+ }
+ return 0;
+
+out9:
+ release_region(HD_CMD, 1);
+out8:
+ release_region(HD_DATA + 12, 1);
+out7:
+ release_region(HD_DATA + 10, 1);
+out6:
+ release_region(HD_DATA + 8, 1);
+out5:
+ release_region(HD_DATA + 6, 1);
+out4:
+ release_region(HD_DATA + 4, 1);
+out3:
+ release_region(HD_DATA + 2, 1);
+out2:
+ release_region(HD_DATA, 2);
+ free_irq(HD_IRQ, NULL);
+out1:
+ for (drive = 0; drive < NR_HD; drive++)
+ put_disk(hd_gendisk[drive]);
+ NR_HD = 0;
+out:
+ del_timer(&device_timer);
+ unregister_blkdev(MAJOR_NR,"hd");
+ blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+ return -1;
+Enomem:
+ while (drive--)
+ put_disk(hd_gendisk[drive]);
+ goto out;
+}
+
+static int parse_hd_setup (char *line) {
+ int ints[6];
+
+ (void) get_options(line, ARRAY_SIZE(ints), ints);
+ hd_setup(NULL, ints);
+
+ return 1;
+}
+__setup("hd=", parse_hd_setup);
+
+module_init(hd_init);
diff -urN linux/drivers/ide/legacy/pc9800.c linux98/drivers/ide/legacy/pc9800.c
--- linux/drivers/ide/legacy/pc9800.c Thu Jan 1 09:00:00 1970
+++ linux98/drivers/ide/legacy/pc9800.c Tue Oct 8 17:06:39 2002
@@ -0,0 +1,82 @@
+/*
+ * ide_pc9800.c
+ *
+ * Copyright (C) 1997-2000 Linux/98 project,
+ * Kyoto University Microcomputer Club.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/pc9800.h>
+
+#define PC9800_IDE_BANKSELECT 0x432
+
+#define DEBUG
+
+static void
+pc9800_select(ide_drive_t *drive)
+{
+#ifdef DEBUG
+ byte old;
+
+ /* Too noisy: */
+ /* printk(KERN_DEBUG "pc9800_select(%s)\n", drive->name); */
+
+ outb(0x80, PC9800_IDE_BANKSELECT);
+ old = inb(PC9800_IDE_BANKSELECT);
+ if (old != HWIF(drive)->index)
+ printk(KERN_DEBUG "ide-pc9800: switching bank #%d -> #%d\n",
+ old, HWIF(drive)->index);
+#endif
+ outb(HWIF(drive)->index, PC9800_IDE_BANKSELECT);
+}
+
+void __init
+ide_probe_for_pc9800(void)
+{
+ byte tmp;
+
+ if (!PC9800_9821_P() /* || !PC9821_IDEIF_DOUBLE_P() */)
+ return;
+
+ if (check_region(PC9800_IDE_BANKSELECT, 1)) {
+ printk(KERN_ERR
+ "ide: bank select port (%#x) is already occupied!\n",
+ PC9800_IDE_BANKSELECT);
+ return;
+ }
+
+ /* Do actual probing. */
+ if ((tmp = inb(PC9800_IDE_BANKSELECT)) == (byte) ~0
+ || (outb(tmp ^ 1, PC9800_IDE_BANKSELECT),
+ /* Next outb is dummy for reading status. */
+ outb(0x80, PC9800_IDE_BANKSELECT),
+ inb(PC9800_IDE_BANKSELECT) != (tmp ^ 1))) {
+ printk(KERN_INFO
+ "ide: pc9800 type bank selecting port not found\n");
+ return;
+ }
+ /* Restore original value, just in case. */
+ outb(tmp, PC9800_IDE_BANKSELECT);
+
+ request_region(PC9800_IDE_BANKSELECT, 1, "ide0/1 bank");
+
+ /* These ports are probably used by IDE I/F. */
+ request_region(0x430, 1, "ide");
+ request_region(0x435, 1, "ide");
+
+ if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET] == HD_DATA
+ && ide_hwifs[1].io_ports[IDE_DATA_OFFSET] == HD_DATA) {
+ ide_hwifs[0].chipset = ide_pc9800;
+ ide_hwifs[0].mate = &ide_hwifs[1];
+ ide_hwifs[0].selectproc = pc9800_select;
+ ide_hwifs[1].chipset = ide_pc9800;
+ ide_hwifs[1].mate = &ide_hwifs[0];
+ ide_hwifs[1].selectproc = pc9800_select;
+ }
+}
diff -urN linux/include/asm-i386/ide.h linux98/include/asm-i386/ide.h
--- linux/include/asm-i386/ide.h Sat Oct 12 13:21:31 2002
+++ linux98/include/asm-i386/ide.h Sun Oct 13 23:17:54 2002
@@ -26,6 +26,9 @@
static __inline__ int ide_default_irq(ide_ioreg_t base)
{
switch (base) {
+#ifdef CONFIG_PC9800
+ case 0x640: return 9;
+#endif /* CONFIG_PC9800 */
case 0x1f0: return 14;
case 0x170: return 15;
case 0x1e8: return 11;
@@ -39,7 +42,11 @@
static __inline__ ide_ioreg_t ide_default_io_base(int index)
{
+#ifndef CONFIG_PC9800
static unsigned long ata_io_base[MAX_HWIFS] = { 0x1f0, 0x170, 0x1e8, 0x168, 0x1e0, 0x160 };
+#else /* CONFIG_PC9800 */
+ static unsigned long ata_io_base[MAX_HWIFS] = { 0x640, 0x640, 0, 0, 0, 0 };
+#endif /* !CONFIG_PC9800 */
return ata_io_base[index];
}
@@ -48,13 +55,24 @@
{
ide_ioreg_t reg = data_port;
int i;
+#ifdef CONFIG_PC9800
+ ide_ioreg_t increment = data_port == 0x640 ? 2 : 1;
+#endif
for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
hw->io_ports[i] = reg;
+#ifndef CONFIG_PC9800
reg += 1;
+#else
+ reg += increment;
+#endif
}
if (ctrl_port) {
hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
+#ifdef CONFIG_PC9800
+ } else if (data_port == 0x640) {
+ hw->io_ports[IDE_CONTROL_OFFSET] = 0x74c;
+#endif
} else {
hw->io_ports[IDE_CONTROL_OFFSET] = hw->io_ports[IDE_DATA_OFFSET] + 0x206;
}
diff -urN linux/include/linux/hdreg.h linux98/include/linux/hdreg.h
--- linux/include/linux/hdreg.h Sat Oct 12 13:22:07 2002
+++ linux98/include/linux/hdreg.h Sat Oct 12 19:38:02 2002
@@ -5,11 +5,13 @@
* This file contains some defines for the AT-hd-controller.
* Various sources.
*/
+#include <linux/config.h>
/* ide.c has its own port definitions in "ide.h" */
#define HD_IRQ 14
+#ifndef CONFIG_PC9800
/* Hd controller regs. Ref: IBM AT Bios-listing */
#define HD_DATA 0x1f0 /* _CTL when writing */
#define HD_ERROR 0x1f1 /* see err-bits */
@@ -25,6 +27,23 @@
#define HD_CMD 0x3f6 /* used for resets */
#define HD_ALTSTATUS 0x3f6 /* same as HD_STATUS but doesn't clear irq */
+#else /* CONFIG_PC9800 */
+/* Hd controller regs. for NEC PC-9800 */
+#define HD_DATA 0x640 /* _CTL when writing */
+#define HD_ERROR 0x642 /* see err-bits */
+#define HD_NSECTOR 0x644 /* nr of sectors to read/write */
+#define HD_SECTOR 0x646 /* starting sector */
+#define HD_LCYL 0x648 /* starting cylinder */
+#define HD_HCYL 0x64a /* high byte of starting cyl */
+#define HD_CURRENT 0x64c /* 101dhhhh , d=drive, hhhh=head */
+#define HD_STATUS 0x64e /* see status-bits */
+#define HD_FEATURE HD_ERROR /* same io address, read=error, write=feature */
+#define HD_PRECOMP HD_FEATURE /* obsolete use of this port - predates IDE */
+#define HD_COMMAND HD_STATUS /* same io address, read=status, write=cmd */
+
+#define HD_CMD 0x74c /* used for resets */
+#define HD_ALTSTATUS 0x74c /* same as HD_STATUS but doesn't clear irq */
+#endif /* CONFIG_PC9800 */
/* remainder is shared between hd.c, ide.c, ide-cd.c, and the hdparm utility */
diff -urN linux/include/linux/ide.h linux98/include/linux/ide.h
--- linux/include/linux/ide.h Sat Oct 12 13:21:41 2002
+++ linux98/include/linux/ide.h Sat Oct 12 14:18:54 2002
@@ -286,7 +286,7 @@
ide_qd65xx, ide_umc8672, ide_ht6560b,
ide_pdc4030, ide_rz1000, ide_trm290,
ide_cmd646, ide_cy82c693, ide_4drives,
- ide_pmac, ide_etrax100, ide_acorn
+ ide_pmac, ide_etrax100, ide_acorn, ide_pc9800
} hwif_chipset_t;
typedef struct ide_io_ops_s {
Osamu Tomita
2002-11-02 17:53:07 UTC
Permalink
This is a part 5/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
core modules (continued)

diffstat:
include/asm-i386/bugs.h | 2 include/asm-i386/dma.h | 7 +
include/asm-i386/io.h | 6 +
include/asm-i386/irq.h | 4 include/asm-i386/pc9800.h | 27 ++++
include/asm-i386/pc9800_debug.h | 152 +++++++++++++++++++++++++
include/asm-i386/pc9800_dma.h | 238 ++++++++++++++++++++++++++++++++++++++++
include/asm-i386/pc9800_sca.h | 25 ++++
include/asm-i386/pgtable.h | 4 include/asm-i386/processor.h | 2 include/asm-i386/scatterlist.h | 6 +
include/asm-i386/setup.h | 1 include/asm-i386/timex.h | 4 13 files changed, 477 insertions(+), 1 deletion(-)

patch:
diff -urN linux/include/asm-i386/bugs.h linux98/include/asm-i386/bugs.h
--- linux/include/asm-i386/bugs.h Sun Jun 9 14:27:38 2002
+++ linux98/include/asm-i386/bugs.h Mon Jun 10 20:49:15 2002
@@ -34,6 +34,7 @@
__setup("no-hlt", no_halt);
+#ifndef CONFIG_PC9800
static int __init mca_pentium(char *s)
{
mca_pentium_flag = 1;
@@ -41,6 +42,7 @@
}
__setup("mca-pentium", mca_pentium);
+#endif
static int __init no_387(char *s)
{
diff -urN linux/include/asm-i386/dma.h linux98/include/asm-i386/dma.h
--- linux/include/asm-i386/dma.h Sat Jul 21 04:52:59 2001
+++ linux98/include/asm-i386/dma.h Fri Aug 17 22:15:06 2001
@@ -10,6 +10,9 @@
#include <linux/config.h>
#include <linux/spinlock.h> /* And spinlocks */
+#ifdef CONFIG_PC9800
+#include <asm/pc9800_dma.h>
+#else /* !CONFIG_PC9800 */
#include <asm/io.h> /* need byte IO */
#include <linux/delay.h>
@@ -72,8 +75,10 @@
#define MAX_DMA_CHANNELS 8
+#ifndef CONFIG_PC9800
/* The maximum address that we can perform a DMA transfer to on this platform */
#define MAX_DMA_ADDRESS (PAGE_OFFSET+0x1000000)
+#endif
/* 8237 DMA controllers */
#define IO_DMA1_BASE 0x00 /* 8 bit slave DMA, channels 0..3 */
@@ -295,4 +300,6 @@
#define isa_dma_bridge_buggy (0)
#endif
+#endif /* CONFIG_PC9800 */
+
#endif /* _ASM_DMA_H */
diff -urN linux/include/asm-i386/io.h linux98/include/asm-i386/io.h
--- linux/include/asm-i386/io.h Sat Oct 12 13:22:45 2002
+++ linux98/include/asm-i386/io.h Sat Oct 12 19:25:19 2002
@@ -27,6 +27,8 @@
* Linus
*/
+#include <linux/config.h>
+
/*
* Bit simplified and optimized by Jan Hubicka
* Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999.
@@ -288,7 +290,11 @@
#ifdef SLOW_IO_BY_JUMPING
#define __SLOW_DOWN_IO "jmp 1f; 1: jmp 1f; 1:"
#else
+#ifndef CONFIG_PC9800
#define __SLOW_DOWN_IO "outb %%al,$0x80;"
+#else
+#define __SLOW_DOWN_IO "outb %%al,$0x5f;"
+#endif
#endif
static inline void slow_down_io(void) {
diff -urN linux/include/asm-i386/irq.h linux98/include/asm-i386/irq.h
--- linux/include/asm-i386/irq.h Sat Sep 21 00:20:16 2002
+++ linux98/include/asm-i386/irq.h Sat Sep 21 07:17:56 2002
@@ -17,7 +17,11 @@
static __inline__ int irq_cannonicalize(int irq)
{
+#ifndef CONFIG_PC9800
return ((irq == 2) ? 9 : irq);
+#else
+ return ((irq == 7) ? 11 : irq);
+#endif
}
extern void disable_irq(unsigned int);
diff -urN linux/include/asm-i386/pc9800.h linux98/include/asm-i386/pc9800.h
--- linux/include/asm-i386/pc9800.h Thu Jan 1 09:00:00 1970
+++ linux98/include/asm-i386/pc9800.h Fri Aug 17 21:50:18 2001
@@ -0,0 +1,27 @@
+/*
+ * PC-9800 machine types.
+ *
+ * Copyright (C) 1999 TAKAI Kosuke <***@kmc.kyoto-u.ac.jp>
+ * (Linux/98 Project)
+ */
+
+#ifndef _ASM_PC9800_H_
+#define _ASM_PC9800_H_
+
+#include <asm/pc9800_sca.h>
+#include <asm/types.h>
+
+#define __PC9800SCA(type, pa) (*(type *) phys_to_virt(pa))
+#define __PC9800SCA_TEST_BIT(pa, n) \
+ ((__PC9800SCA(u8, pa) & (1U << (n))) != 0)
+
+#define PC9800_HIGHRESO_P() __PC9800SCA_TEST_BIT(PC9800SCA_BIOS_FLAG, 3)
+#define PC9800_8MHz_P() __PC9800SCA_TEST_BIT(PC9800SCA_BIOS_FLAG, 7)
+
+ /* 0x2198 is 98 21 on memory... */
+#define PC9800_9821_P() (__PC9800SCA(u16, PC9821SCA_ROM_ID) == 0x2198)
+
+/* Note PC9821_...() are valid only when PC9800_9821_P() was true. */
+#define PC9821_IDEIF_DOUBLE_P() __PC9800SCA_TEST_BIT(PC9821SCA_ROM_FLAG4, 4)
+
+#endif
diff -urN linux/include/asm-i386/pc9800_debug.h linux98/include/asm-i386/pc9800_debug.h
--- linux/include/asm-i386/pc9800_debug.h Thu Jan 1 09:00:00 1970
+++ linux98/include/asm-i386/pc9800_debug.h Fri Aug 17 22:20:21 2001
@@ -0,0 +1,152 @@
+/*
+ * linux/include/asm-i386/pc9800_debug.h: Defines for debug routines
+ *
+ * Written by Linux/98 Project, 1998.
+ *
+ * Revised by TAKAI Kousuke, Nov 1999.
+ */
+
+#ifndef _ASM_PC9800_DEBUG_H
+#define _ASM_PC9800_DEBUG_H
+
+extern unsigned char __pc9800_beep_flag;
+
+/* Don't depend on <asm/io.h> ... */
+static __inline__ void pc9800_beep_on(void)
+{
+ __asm__ ("out%B0 %0,%1" : : "a"((char) 0x6), "N"(0x37));
+ __pc9800_beep_flag = 0x6;
+}
+
+static __inline__ void pc9800_beep_off(void)
+{
+ __asm__ ("out%B0 %0,%1" : : "a"((char) 0x7), "N"(0x37));
+ __pc9800_beep_flag = 0x7;
+}
+
+static __inline__ void pc9800_beep_toggle(void)
+{
+ __pc9800_beep_flag ^= 1;
+ __asm__ ("out%B0 %0,%1" : : "a"(__pc9800_beep_flag), "N"(0x37));
+}
+
+static __inline__ void pc9800_udelay(unsigned int __usec)
+{
+ if ((__usec = __usec * 10 / 6) > 0)
+ do
+ __asm__ ("out%B0 %%al,%0" : : "N"(0x5F));
+ while (--__usec);
+}
+
+# define assertk(expr) ({ \
+ if (!(expr)) { \
+ __pc9800_saveregs(); \
+ __assert_fail(__BASE_FILE__, __FILE__, __LINE__, \
+ __PRETTY_FUNCTION__, \
+ __builtin_return_address (0), #expr); \
+ } \
+})
+# define check_kernel_pointer(expr) ({ \
+ void *__p = (expr); \
+ if ((unsigned long) __p < PAGE_OFFSET) { \
+ __pc9800_saveregs(); \
+ __invalid_kernel_pointer \
+ (__BASE_FILE__, __FILE__, __LINE__, \
+ __PRETTY_FUNCTION__, \
+ __builtin_return_address(0), #expr, __p); \
+ } \
+})
+
+extern void __assert_fail(const char *, const char *, unsigned int,
+ const char *, void *, const char *)
+ __attribute__ ((__noreturn__));
+extern void __invalid_kernel_pointer(const char *, const char *, unsigned int,
+ const char *, void *,
+ const char *, void *)
+ __attribute__ ((__noreturn__));
+extern void __pc9800_saveregs(void);
+
+extern void ucg_saveargs(unsigned int, ...);
+
+#ifdef NEED_UNMAP_PHYSPAGE
+
+#include <linux/mm.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+
+/*
+ * unmap_physpage (addr)
+ */
+static __inline__ void
+unmap_physpage(unsigned long addr)
+{
+ pgd_t *pgd = pgd_offset_k(addr);
+ pmd_t *pmd;
+ pte_t *pte;
+
+ if (pgd_none(*pgd))
+ panic("%s: No pgd?!?", __BASE_FILE__);
+ pmd = pmd_offset(pgd, addr);
+ if (pmd_none(*pmd))
+ panic("%s: No pmd?!?", __BASE_FILE__);
+ if (pmd_val(*pmd) & _PAGE_PSE) {
+ int i;
+ unsigned long paddr = pmd_val(*pmd) & PAGE_MASK;
+
+ pte = (pte_t *) __get_free_page(GFP_KERNEL);
+ set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte)));
+ for (i = 0; i < PTRS_PER_PTE; pte++, i++)
+ *pte = mk_pte_phys(paddr + i * PAGE_SIZE,
+ PAGE_KERNEL);
+ }
+ pte = pte_offset(pmd, addr);
+
+ set_pte(pte, pte_modify(*pte, PAGE_NONE));
+ __flush_tlb_one(addr);
+}
+#endif /* NEED_UNMAP_PHYSPAGE */
+
+#ifdef NEED_KERNEL_POINTER_CHECKER
+# /* no dep */ include <asm/uaccess.h>
+
+/*
+ * KERNEL_POINTER_VALIDP(PTR) validates kernel pointer PTR.
+ * If PTR points vaild memory address for kernel internal use,
+ * return nonzero; otherwise return zero.
+ *
+ * Note PTR is invalid if PTR points user area.
+ */
+#define KERNEL_POINTER_VALIDP(PTR) ({ \
+ const int *_ptr = (const int *) (PTR); \
+ int _dummy; \
+ (unsigned long) _ptr >= PAGE_OFFSET \
+ && !__get_user(_dummy, _ptr); \
+})
+
+/*
+ * Similar, but validates for a trivial string in kernel.
+ * Here `trivial' means that the string has no non-ASCII characters
+ * and is shorter than 80 characters.
+ *
+ * Note this is intended for checking various `name' (I/O
+ * resources and so on).
+ */
+#define KERNEL_POINTER_TRIVIAL_STRING_P(PTR) ({ \
+ const char *_ptr = (const char *) (PTR); \
+ char _dummy; \
+ (unsigned long) _ptr >= PAGE_OFFSET \
+ && ({ int _result; \
+ while (!(_result = __get_user(_dummy, _ptr)) \
+ && _dummy) \
+ _ptr++; \
+ !_result; }); })
+
+#endif /* NEED_VALIDATE_KERNEL_POINTER */
+
+#endif /* _ASM_PC9800_DEBUG_H */
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -urN linux/include/asm-i386/pc9800_dma.h linux98/include/asm-i386/pc9800_dma.h
--- linux/include/asm-i386/pc9800_dma.h Thu Jan 1 09:00:00 1970
+++ linux98/include/asm-i386/pc9800_dma.h Fri Aug 17 22:15:01 2001
@@ -0,0 +1,238 @@
+/* $Id: dma.h,v 1.7 1992/12/14 00:29:34 root Exp root $
+ * linux/include/asm/dma.h: Defines for using and allocating dma channels.
+ * Written by Hennus Bergman, 1992.
+ * High DMA channel support & info by Hannu Savolainen
+ * and John Boyd, Nov. 1992.
+ */
+
+#ifndef _ASM_PC9800_DMA_H
+#define _ASM_PC9800_DMA_H
+
+#include <linux/config.h>
+#include <asm/io.h> /* need byte IO */
+#include <linux/delay.h>
+
+
+#ifdef HAVE_REALLY_SLOW_DMA_CONTROLLER
+#define dma_outb outb_p
+#else
+#define dma_outb outb
+#endif
+
+#define dma_inb inb
+
+/*
+ * NOTES about DMA transfers:
+ *
+ * controller 1: channels 0-3, byte operations, ports 00-1F
+ * controller 2: channels 4-7, word operations, ports C0-DF
+ *
+ * - ALL registers are 8 bits only, regardless of transfer size
+ * - channel 4 is not used - cascades 1 into 2.
+ * - channels 0-3 are byte - addresses/counts are for physical bytes
+ * - channels 5-7 are word - addresses/counts are for physical words
+ * - transfers must not cross physical 64K (0-3) or 128K (5-7) boundaries
+ * - transfer count loaded to registers is 1 less than actual count
+ * - controller 2 offsets are all even (2x offsets for controller 1)
+ * - page registers for 5-7 don't use data bit 0, represent 128K pages
+ * - page registers for 0-3 use bit 0, represent 64K pages
+ *
+ * DMA transfers are limited to the lower 16MB of _physical_ memory. + * Note that addresses loaded into registers must be _physical_ addresses,
+ * not logical addresses (which may differ if paging is active).
+ *
+ * Address mapping for channels 0-3:
+ *
+ * A23 ... A16 A15 ... A8 A7 ... A0 (Physical addresses)
+ * | ... | | ... | | ... |
+ * | ... | | ... | | ... |
+ * | ... | | ... | | ... |
+ * P7 ... P0 A7 ... A0 A7 ... A0 + * | Page | Addr MSB | Addr LSB | (DMA registers)
+ *
+ * Address mapping for channels 5-7:
+ *
+ * A23 ... A17 A16 A15 ... A9 A8 A7 ... A1 A0 (Physical addresses)
+ * | ... | \ \ ... \ \ \ ... \ \
+ * | ... | \ \ ... \ \ \ ... \ (not used)
+ * | ... | \ \ ... \ \ \ ... \
+ * P7 ... P1 (0) A7 A6 ... A0 A7 A6 ... A0 + * | Page | Addr MSB | Addr LSB | (DMA registers)
+ *
+ * Again, channels 5-7 transfer _physical_ words (16 bits), so addresses
+ * and counts _must_ be word-aligned (the lowest address bit is _ignored_ at
+ * the hardware level, so odd-byte transfers aren't possible).
+ *
+ * Transfer count (_not # bytes_) is limited to 64K, represented as actual
+ * count - 1 : 64K => 0xFFFF, 1 => 0x0000. Thus, count is always 1 or more,
+ * and up to 128K bytes may be transferred on channels 5-7 in one operation. + *
+ */
+
+#define MAX_DMA_CHANNELS 4
+
+/* The maximum address that we can perform a DMA transfer to on this platform */
+#define MAX_DMA_ADDRESS (~0UL)
+
+/* 8237 DMA controllers */
+#define IO_DMA_BASE 0x01
+
+/* DMA controller registers */
+#define DMA_CMD_REG ((IO_DMA_BASE)+0x10) /* command register (w) */
+#define DMA_STAT_REG ((IO_DMA_BASE)+0x10) /* status register (r) */
+#define DMA_REQ_REG ((IO_DMA_BASE)+0x12) /* request register (w) */
+#define DMA_MASK_REG ((IO_DMA_BASE)+0x14) /* single-channel mask (w) */
+#define DMA_MODE_REG ((IO_DMA_BASE)+0x16) /* mode register (w) */
+#define DMA_CLEAR_FF_REG ((IO_DMA_BASE)+0x18) /* clear pointer flip-flop (w) */
+#define DMA_TEMP_REG ((IO_DMA_BASE)+0x1A) /* Temporary Register (r) */
+#define DMA_RESET_REG ((IO_DMA_BASE)+0x1A) /* Master Clear (w) */
+#define DMA_CLR_MASK_REG ((IO_DMA_BASE)+0x1C) /* Clear Mask */
+#define DMA_MASK_ALL_REG ((IO_DMA_BASE)+0x1E) /* all-channels mask (w) */
+
+#define DMA_PAGE_0 0x27 /* DMA page registers */
+#define DMA_PAGE_1 0x21
+#define DMA_PAGE_2 0x23
+#define DMA_PAGE_3 0x25
+
+#define DMA_Ex_PAGE_0 0xe05 /* DMA Extended page reg base */
+#define DMA_Ex_PAGE_1 0xe07
+#define DMA_Ex_PAGE_2 0xe09
+#define DMA_Ex_PAGE_3 0xe0b
+
+#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */
+#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */
+#define DMA_AUTOINIT 0x10
+
+extern spinlock_t dma_spin_lock;
+
+static __inline__ unsigned long claim_dma_lock(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dma_spin_lock, flags);
+ return flags;
+}
+
+static __inline__ void release_dma_lock(unsigned long flags)
+{
+ spin_unlock_irqrestore(&dma_spin_lock, flags);
+}
+
+/* enable/disable a specific DMA channel */
+static __inline__ void enable_dma(unsigned int dmanr)
+{
+ dma_outb(dmanr, DMA_MASK_REG);
+}
+
+static __inline__ void disable_dma(unsigned int dmanr)
+{
+ dma_outb(dmanr | 4, DMA_MASK_REG);
+}
+
+/* Clear the 'DMA Pointer Flip Flop'.
+ * Write 0 for LSB/MSB, 1 for MSB/LSB access.
+ * Use this once to initialize the FF to a known state.
+ * After that, keep track of it. :-)
+ * --- In order to do that, the DMA routines below should ---
+ * --- only be used while holding the DMA lock ! ---
+ */
+static __inline__ void clear_dma_ff(unsigned int dmanr)
+{
+ dma_outb(0, DMA_CLEAR_FF_REG);
+}
+
+/* set mode (above) for a specific DMA channel */
+static __inline__ void set_dma_mode(unsigned int dmanr, char mode)
+{
+ dma_outb(mode | dmanr, DMA_MODE_REG);
+}
+
+/* Set only the page register bits of the transfer address.
+ * This is used for successive transfers when we know the contents of
+ * the lower 16 bits of the DMA current address register, but a 64k boundary
+ * may have been crossed.
+ */
+static __inline__ void set_dma_page(unsigned int dmanr, unsigned int pagenr)
+{
+ unsigned char low=pagenr&0xff;
+ unsigned char hi=pagenr>>8;
+
+ switch(dmanr) {
+ case 0:
+ dma_outb(low, DMA_PAGE_0);
+ dma_outb(hi, DMA_Ex_PAGE_0);
+ break;
+ case 1:
+ dma_outb(low, DMA_PAGE_1);
+ dma_outb(hi, DMA_Ex_PAGE_1);
+ break;
+ case 2:
+ dma_outb(low, DMA_PAGE_2);
+ dma_outb(hi, DMA_Ex_PAGE_2);
+ break;
+ case 3:
+ dma_outb(low, DMA_PAGE_3);
+ dma_outb(hi, DMA_Ex_PAGE_3);
+ break;
+ }
+}
+
+/* Set transfer address & page bits for specific DMA channel.
+ * Assumes dma flipflop is clear.
+ */
+static __inline__ void set_dma_addr(unsigned int dmanr, unsigned int a)
+{
+ set_dma_page(dmanr, a>>16);
+ dma_outb( a & 0xff, ((dmanr&3)<<2) + IO_DMA_BASE );
+ dma_outb( (a>>8) & 0xff, ((dmanr&3)<<2) + IO_DMA_BASE );
+}
+
+
+/* Set transfer size (max 64k for DMA1..3, 128k for DMA5..7) for
+ * a specific DMA channel.
+ * You must ensure the parameters are valid.
+ * NOTE: from a manual: "the number of transfers is one more
+ * than the initial word count"! This is taken into account.
+ * Assumes dma flip-flop is clear.
+ * NOTE 2: "count" represents _bytes_ and must be even for channels 5-7.
+ */
+static __inline__ void set_dma_count(unsigned int dmanr, unsigned int count)
+{
+ count--;
+ dma_outb( count & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA_BASE );
+ dma_outb( (count>>8) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA_BASE );
+}
+
+
+/* Get DMA residue count. After a DMA transfer, this
+ * should return zero. Reading this while a DMA transfer is
+ * still in progress will return unpredictable results.
+ * If called before the channel has been used, it may return 1.
+ * Otherwise, it returns the number of _bytes_ left to transfer.
+ *
+ * Assumes DMA flip-flop is clear.
+ */
+static __inline__ int get_dma_residue(unsigned int dmanr)
+{
+ /* using short to get 16-bit wrap around */
+ unsigned short count;
+
+ count = 1 + dma_inb(((dmanr&3)<<2) + 2 + IO_DMA_BASE);
+ count += dma_inb(((dmanr&3)<<2) + 2 + IO_DMA_BASE) << 8;
+
+ return count;
+}
+
+
+/* These are in kernel/dma.c: */
+extern int request_dma(unsigned int dmanr, const char * device_id); /* reserve a DMA channel */
+extern void free_dma(unsigned int dmanr); /* release it again */
+
+/* From PCI */
+
+#ifdef CONFIG_PCI
+extern int isa_dma_bridge_buggy;
+#else
+#define isa_dma_bridge_buggy (0)
+#endif
+
+#endif /* _ASM_PC9800_DMA_H */
diff -urN linux/include/asm-i386/pc9800_sca.h linux98/include/asm-i386/pc9800_sca.h
--- linux/include/asm-i386/pc9800_sca.h Thu Jan 1 09:00:00 1970
+++ linux98/include/asm-i386/pc9800_sca.h Fri Aug 17 21:50:18 2001
@@ -0,0 +1,25 @@
+/*
+ * System-common area definitions for NEC PC-9800 series
+ *
+ * Copyright (C) 1999 TAKAI Kousuke <***@kmc.kyoto-u.ac.jp>,
+ * Kyoto University Microcomputer Club.
+ */
+
+#ifndef _ASM_I386_PC9800SCA_H_
+#define _ASM_I386_PC9800SCA_H_
+
+#define PC9800SCA_EXPMMSZ (0x0401) /* B */
+#define PC9800SCA_SCSI_PARAMS (0x0460) /* 8 * 4B */
+#define PC9800SCA_DISK_EQUIPS (0x0482) /* B */
+#define PC9800SCA_XROM_ID (0x04C0) /* 52B */
+#define PC9800SCA_BIOS_FLAG (0x0501) /* B */
+#define PC9800SCA_MMSZ16M (0x0594) /* W */
+
+/* PC-9821 have additional system common area in their BIOS-ROM segment. */
+
+#define PC9821SCA__BASE (0xF8E8 << 4)
+#define PC9821SCA_ROM_ID (PC9821SCA__BASE + 0x00)
+#define PC9821SCA_ROM_FLAG4 (PC9821SCA__BASE + 0x05)
+#define PC9821SCA_RSFLAGS (PC9821SCA__BASE + 0x11) /* B */
+
+#endif /* !_ASM_I386_PC9800SCA_H_ */
diff -urN linux/include/asm-i386/pgtable.h linux98/include/asm-i386/pgtable.h
--- linux/include/asm-i386/pgtable.h Mon Apr 15 04:18:55 2002
+++ linux98/include/asm-i386/pgtable.h Wed Apr 17 10:37:22 2002
@@ -58,7 +58,11 @@
#endif
#endif
+#ifndef CONFIG_PC9800
#define __beep() asm("movb $0x3,%al; outb %al,$0x61")
+#else
+#define __beep() asm("movb $0x6,%al; outb %al,$0x37")
+#endif
#define PMD_SIZE (1UL << PMD_SHIFT)
#define PMD_MASK (~(PMD_SIZE-1))
diff -urN linux/include/asm-i386/processor.h linux98/include/asm-i386/processor.h
--- linux/include/asm-i386/processor.h Sat Sep 21 00:20:15 2002
+++ linux98/include/asm-i386/processor.h Sun Sep 22 09:16:38 2002
@@ -87,7 +87,7 @@
#define current_cpu_data boot_cpu_data
#endif
-extern char ignore_irq13;
+extern char ignore_fpu_irq;
extern void identify_cpu(struct cpuinfo_x86 *);
extern void print_cpu_info(struct cpuinfo_x86 *);
diff -urN linux/include/asm-i386/scatterlist.h linux98/include/asm-i386/scatterlist.h
--- linux/include/asm-i386/scatterlist.h Mon Apr 15 04:18:52 2002
+++ linux98/include/asm-i386/scatterlist.h Wed Apr 17 10:37:22 2002
@@ -1,6 +1,8 @@
#ifndef _I386_SCATTERLIST_H
#define _I386_SCATTERLIST_H
+#include <linux/config.h>
+
struct scatterlist {
struct page *page;
unsigned int offset;
@@ -8,6 +10,10 @@
unsigned int length;
};
+#ifdef CONFIG_PC9800
+#define ISA_DMA_THRESHOLD (0xffffffff)
+#else
#define ISA_DMA_THRESHOLD (0x00ffffff)
+#endif
#endif /* !(_I386_SCATTERLIST_H) */
diff -urN linux/include/asm-i386/setup.h linux98/include/asm-i386/setup.h
--- linux/include/asm-i386/setup.h Sat Oct 19 13:02:01 2002
+++ linux98/include/asm-i386/setup.h Mon Oct 21 15:32:08 2002
@@ -28,6 +28,7 @@
#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+0x40))
#define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80))
#define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0xa0))
+#define PC9800_MISC_FLAGS (*(unsigned char *)(PARAM+0x1AF))
#define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2))
#define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8))
#define VIDEO_MODE (*(unsigned short *) (PARAM+0x1FA))
diff -urN linux/include/asm-i386/timex.h linux98/include/asm-i386/timex.h
--- linux/include/asm-i386/timex.h Thu Feb 14 18:09:15 2002
+++ linux98/include/asm-i386/timex.h Thu Feb 14 23:58:57 2002
@@ -9,11 +9,15 @@
#include <linux/config.h>
#include <asm/msr.h>
+#ifdef CONFIG_PC9800
+ extern int CLOCK_TICK_RATE;
+#else
#ifdef CONFIG_MELAN
# define CLOCK_TICK_RATE 1189200 /* AMD Elan has different frequency! */
#else
# define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
#endif
+#endif
#define CLOCK_TICK_FACTOR 20 /* Factor of both 1000000 and CLOCK_TICK_RATE */
#define FINETUNE ((((((long)LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) * \
Osamu Tomita
2002-11-02 17:58:49 UTC
Permalink
This is part 8/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
file system modules
- add support PC-9800 partition table.
- small hack for access FAT partition made by PC-9800 DOS.

diffstat:
fs/fat/inode.c | 5 fs/partitions/Kconfig | 11 +
fs/partitions/Makefile | 1 fs/partitions/check.c | 4 fs/partitions/msdos.c | 15 ++
fs/partitions/nec98.c | 277 +++++++++++++++++++++++++++++++++++++++++++++++++
fs/partitions/nec98.h | 10 +
7 files changed, 322 insertions(+), 1 deletion(-)

patch:
diff -urN linux/fs/fat/inode.c linux98/fs/fat/inode.c
--- linux/fs/fat/inode.c Thu Oct 31 13:23:37 2002
+++ linux98/fs/fat/inode.c Thu Oct 31 16:41:16 2002
@@ -49,6 +49,7 @@
#define FAT_HASH_MASK (FAT_HASH_SIZE-1)
static struct list_head fat_inode_hashtable[FAT_HASH_SIZE];
spinlock_t fat_inode_lock = SPIN_LOCK_UNLOCKED;
+extern int pc98; /* whether PC-9801 architecutre or not */
void fat_hash_init(void)
{
@@ -928,7 +929,9 @@
error = first;
goto out_fail;
}
- if (FAT_FIRST_ENT(sb, media) != first) {
+ if (FAT_FIRST_ENT(sb, media) != first
+ && (!pc98 || media != 0xf8 || (first & 0xff) != 0xfe))
+ {
if (!silent) {
printk(KERN_ERR "FAT: invalid first entry of FAT "
"(0x%x != 0x%x)\n",
diff -urN linux/fs/partitions/Kconfig linux98/fs/partitions/Kconfig
--- linux/fs/partitions/Kconfig Thu Oct 31 13:23:38 2002
+++ linux98/fs/partitions/Kconfig Thu Oct 31 16:27:06 2002
@@ -177,6 +177,17 @@
If unsure, say N.
+config NEC98_PARTITION
+ bool "NEC PC-9800 partition table support" if PARTITION_ADVANCED
+ default y if !PARTITION_ADVANCED && PC9800
+ help
+ Say Y here if you would like to be able to read the hard disk
+ partition table format used by NEC PC-9800 machines.
+
+config NEC98_BSD_DISKLABEL
+ bool "BSD disklabel support for NEC PC-9800 type partitions"
+ depends on NEC98_PARTITION
+
config SGI_PARTITION
bool "SGI partition support" if PARTITION_ADVANCED
default y if !PARTITION_ADVANCED && (SGI_IP22 || SGI_IP27)
diff -urN linux/fs/partitions/Makefile linux98/fs/partitions/Makefile
--- linux/fs/partitions/Makefile Sat Oct 19 13:01:50 2002
+++ linux98/fs/partitions/Makefile Sat Oct 19 17:00:39 2002
@@ -18,6 +18,7 @@
obj-$(CONFIG_ULTRIX_PARTITION) += ultrix.o
obj-$(CONFIG_IBM_PARTITION) += ibm.o
obj-$(CONFIG_EFI_PARTITION) += efi.o
+obj-$(CONFIG_NEC98_PARTITION) += nec98.o msdos.o
include $(TOPDIR)/Rules.make
diff -urN linux/fs/partitions/check.c linux98/fs/partitions/check.c
--- linux/fs/partitions/check.c Thu Oct 31 13:23:38 2002
+++ linux98/fs/partitions/check.c Thu Oct 31 16:16:41 2002
@@ -29,6 +29,7 @@
#include "ldm.h"
#include "mac.h"
#include "msdos.h"
+#include "nec98.h"
#include "osf.h"
#include "sgi.h"
#include "sun.h"
@@ -52,6 +53,9 @@
#ifdef CONFIG_LDM_PARTITION
ldm_partition, /* this must come before msdos */
#endif
+#ifdef CONFIG_NEC98_PARTITION
+ nec98_partition, /* must be come before `msdos_partition' */
+#endif
#ifdef CONFIG_MSDOS_PARTITION
msdos_partition,
#endif
diff -urN linux/fs/partitions/msdos.c linux98/fs/partitions/msdos.c
--- linux/fs/partitions/msdos.c Sat Oct 12 13:22:10 2002
+++ linux98/fs/partitions/msdos.c Sun Oct 13 22:23:58 2002
@@ -20,6 +20,11 @@
*/
#include <linux/config.h>
+
+#ifdef CONFIG_NEC98_BSD_DISKLABEL
+# define CONFIG_BSD_DISKLABEL
+#endif
+
#include <linux/buffer_head.h> /* for invalidate_bdev() */
#ifdef CONFIG_BLK_DEV_IDE
@@ -37,6 +42,7 @@
#include "msdos.h"
#include "efi.h"
+#if !defined(CONFIG_PC9800) || defined(CONFIG_MSDOS_PARTITION)
/*
* Many architectures don't like unaligned accesses, which is
* frequently the case with the nr_sects and start_sect partition
@@ -171,6 +177,7 @@
done:
put_dev_sector(sect);
}
+#endif /* !defined(CONFIG_PC9800) || defined(CONFIG_MSDOS_PARTITION) */
/* ***@bpgc.com: Solaris has a nasty indicator: 0x82 which also
indicates linux swap. Be careful before believing this is Solaris. */
@@ -179,6 +186,7 @@
parse_solaris_x86(struct parsed_partitions *state, struct block_device *bdev,
u32 offset, u32 size, int origin)
{
+#if !defined(CONFIG_PC9800) || defined(CONFIG_MSDOS_PARTITION)
#ifdef CONFIG_SOLARIS_X86_PARTITION
Sector sect;
struct solaris_x86_vtoc *v;
@@ -212,6 +220,7 @@
put_dev_sector(sect);
printk(" >\n");
#endif
+#endif /* !defined(CONFIG_PC9800) || defined(CONFIG_MSDOS_PARTITION) */
}
#ifdef CONFIG_BSD_DISKLABEL
@@ -219,7 +228,11 @@
* Create devices for BSD partitions listed in a disklabel, under a
* dos-like partition. See parse_extended() for more information.
*/
+#ifndef CONFIG_NEC98_BSD_DISKLABEL
static void
+#else
+void
+#endif
parse_bsd(struct parsed_partitions *state, struct block_device *bdev,
u32 offset, u32 size, int origin, char *flavour,
int max_partitions)
@@ -292,6 +305,7 @@
#endif
}
+#if !defined(CONFIG_PC9800) || defined(CONFIG_MSDOS_PARTITION)
/*
* Create devices for Unixware partitions listed in a disklabel, under a
* dos-like partition. See parse_extended() for more information.
@@ -461,3 +475,4 @@
put_dev_sector(sect);
return 1;
}
+#endif /* !defined(CONFIG_PC9800) || defined(CONFIG_MSDOS_PARTITION) */
diff -urN linux/fs/partitions/nec98.c linux98/fs/partitions/nec98.c
--- linux/fs/partitions/nec98.c Thu Jan 1 09:00:00 1970
+++ linux98/fs/partitions/nec98.c Sat Nov 2 17:12:51 2002
@@ -0,0 +1,277 @@
+/*
+ * NEC PC-9800 series partition supports
+ *
+ * Copyright (C) 1999 Kyoto University Microcomputer Club
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_NEC98_BSD_DISKLABEL
+#define CONFIG_BSD_DISKLABEL
+#endif
+
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/blk.h>
+#include <linux/major.h>
+
+#include "check.h"
+#include "nec98.h"
+
+/* #ifdef CONFIG_BLK_DEV_IDEDISK */
+#include <linux/ide.h>
+/* #endif */
+
+/* #ifdef CONFIG_BLK_DEV_SD */
+#include "../../drivers/scsi/scsi.h"
+#include "../../drivers/scsi/hosts.h"
+#include <scsi/scsicam.h>
+/* #endif */
+
+struct nec98_partition {
+ __u8 mid; /* 0x80 - active */
+ __u8 sid; /* 0x80 - bootable */
+ __u16 pad1; /* dummy for padding */
+ __u8 ipl_sector; /* IPL sector */
+ __u8 ipl_head; /* IPL head */
+ __u16 ipl_cyl; /* IPL cylinder */
+ __u8 sector; /* starting sector */
+ __u8 head; /* starting head */
+ __u16 cyl; /* starting cylinder */
+ __u8 end_sector; /* end sector */
+ __u8 end_head; /* end head */
+ __u16 end_cyl; /* end cylinder */
+ unsigned char name[16];
+} __attribute__((__packed__));
+
+#define NEC98_BSD_PARTITION_MID 0x14
+#define NEC98_BSD_PARTITION_SID 0x44
+#define MID_SID_16(mid, sid) (((mid) & 0xFF) | (((sid) & 0xFF) << 8))
+#define NEC98_BSD_PARTITION_MID_SID \
+ MID_SID_16(NEC98_BSD_PARTITION_MID, NEC98_BSD_PARTITION_SID)
+#define NEC98_VALID_PTABLE_ENTRY(P) \
+ (!(P)->pad1 && (P)->cyl <= (P)->end_cyl)
+
+static inline int
+is_valid_nec98_partition_table(const struct nec98_partition *ptable,
+ __u8 nsectors, __u8 nheads)
+{
+ int i;
+ int valid = 0;
+
+ for (i = 0; i < 16; i++) {
+ if (!*(__u16 *)&ptable[i])
+ continue; /* empty slot */
+ if (ptable[i].pad1 /* `pad1' contains junk */
+ || ptable[i].ipl_sector >= nsectors
+ || ptable[i].sector >= nsectors
+ || ptable[i].end_sector >= nsectors
+ || ptable[i].ipl_head >= nheads
+ || ptable[i].head >= nheads
+ || ptable[i].end_head >= nheads
+ || ptable[i].cyl > ptable[i].end_cyl)
+ return 0;
+ valid = 1; /* We have a valid partition. */
+ }
+ /* If no valid PC-9800-style partitions found,
+ the disk may have other type of partition table. */
+ return valid;
+}
+
+#ifdef CONFIG_NEC98_BSD_DISKLABEL
+extern void parse_bsd(struct parsed_partitions *state,
+ struct block_device *bdev,
+ u32 offset, u32 size, int origin, char *flavour,
+ int max_partitions);
+#endif
+
+extern struct scsi_device *sd_find_params_by_bdev(struct block_device *, char **, sector_t *);
+
+int nec98_partition(struct parsed_partitions *state, struct block_device *bdev)
+{
+ unsigned int nr;
+ int g_head, g_sect;
+ Sector sect;
+ const struct nec98_partition *part;
+ unsigned char *data;
+ int sector_size = bdev_hardsect_size(bdev);
+ int major = major(to_kdev_t(bdev->bd_dev));
+ int minor = minor(to_kdev_t(bdev->bd_dev));
+
+ switch (major) {
+#if defined CONFIG_BLK_DEV_HD_ONLY
+ case HD_MAJOR:
+ {
+ extern struct hd_i_struct hd_info[2];
+
+ g_head = hd_info[minor >> 6].head;
+ g_sect = hd_info[minor >> 6].sect;
+ break;
+ }
+#endif /* CONFIG_BLK_DEV_HD_ONLY */
+#if defined CONFIG_BLK_DEV_SD || defined CONFIG_BLK_DEV_SD_MODULE
+ case SCSI_DISK0_MAJOR:
+ case SCSI_DISK1_MAJOR:
+ case SCSI_DISK2_MAJOR:
+ case SCSI_DISK3_MAJOR:
+ case SCSI_DISK4_MAJOR:
+ case SCSI_DISK5_MAJOR:
+ case SCSI_DISK6_MAJOR:
+ case SCSI_DISK7_MAJOR:
+ {
+ int diskinfo[3] = { 0, 0, 0 };
+ sector_t capacity;
+
+ (void)sd_find_params_by_bdev(bdev, NULL, &capacity);
+ scsicam_bios_param(bdev, capacity, diskinfo);
+
+ if ((g_head = diskinfo[0]) <= 0)
+ g_head = 8;
+ if ((g_sect = diskinfo[1]) <= 0)
+ g_sect = 17;
+ break;
+ }
+#endif /* CONFIG_BLK_DEV_SD(_MODULE) */
+#if defined CONFIG_BLK_DEV_IDEDISK || defined CONFIG_BLK_DEV_IDEDISK_MODULE
+ case IDE0_MAJOR:
+ case IDE1_MAJOR:
+ case IDE2_MAJOR:
+ case IDE3_MAJOR:
+ case IDE4_MAJOR:
+ case IDE5_MAJOR:
+ case IDE6_MAJOR:
+ case IDE7_MAJOR:
+ case IDE8_MAJOR:
+ case IDE9_MAJOR:
+ {
+ ide_drive_t *drive;
+ unsigned int h;
+
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ if (hwif->present && major == hwif->major) {
+ unsigned unit = minor >> PARTN_BITS;
+ if (unit < MAX_DRIVES) {
+ drive = &hwif->drives[unit];
+ if (drive->present) {
+ g_head = drive->head;
+ g_sect = drive->sect;
+ goto found;
+ }
+ }
+ break;
+ }
+ }
+ }
+#endif /* CONFIG_BLK_DEV_IDEDISK(_MODULE) */
+ default:
+ printk(" unsupported disk (major = %u)\n", major);
+ return 0;
+ }
+
+ found:
+ data = read_dev_sector(bdev, 0, &sect);
+ if (!data) {
+ if (warn_no_part)
+ printk(" unable to read partition table\n");
+ return -1;
+ }
+
+ /* magic(?) check */
+ if (*(__u16 *)(data + sector_size - 2) != NEC98_PTABLE_MAGIC) {
+ put_dev_sector(sect);
+ return 0;
+ }
+
+ put_dev_sector(sect);
+ data = read_dev_sector(bdev, 1, &sect);
+ if (!data) {
+ if (warn_no_part)
+ printk(" unable to read partition table\n");
+ return -1;
+ }
+
+ if (!is_valid_nec98_partition_table((struct nec98_partition *)data,
+ g_sect, g_head)) {
+#if 0
+ if (warn_no_part)
+ printk(" partition table consistency check failed"
+ " (not PC-9800 disk?)\n");
+#endif
+ put_dev_sector(sect);
+ return 0;
+ }
+
+ part = (const struct nec98_partition *)data;
+ for (nr = 0; nr < 16; nr++, part++) {
+ unsigned int start_sect, end_sect;
+
+ if (part->mid == 0 || part->sid == 0)
+ continue;
+
+ if (nr)
+ printk(" ");
+
+ { /* Print partition name. Fdisk98 might put NUL
+ characters in partition name... */
+
+ int j;
+ unsigned char *p;
+ unsigned char buf[sizeof (part->name) * 2 + 1];
+
+ for (p = buf, j = 0; j < sizeof (part->name); j++, p++)
+ if ((*p = part->name[j]) < ' ') {
+ *p++ = '^';
+ *p = part->name[j] + '@';
+ }
+
+ *p = 0;
+ printk(" <%s>", buf);
+ }
+ start_sect = (part->cyl * g_head + part->head) * g_sect
+ + part->sector;
+ end_sect = (part->end_cyl + 1) * g_head * g_sect;
+ if (end_sect <= start_sect) {
+ printk(" (invalid partition info)\n");
+ continue;
+ }
+
+ put_partition(state, nr + 1, start_sect, end_sect - start_sect);
+#ifdef CONFIG_NEC98_BSD_DISKLABEL
+ if ((*(__u16 *)&part->mid & 0x7F7F)
+ == NEC98_BSD_PARTITION_MID_SID) {
+ printk("!");
+ /* NEC98_BSD_PARTITION_MID_SID is not valid SYSIND for
+ IBM PC's MS-DOS partition table, so we simply pass
+ it to bsd_disklabel_partition;
+ it will just print `<bsd: ... >'. */
+ parse_bsd(state, bdev, start_sect,
+ end_sect - start_sect, nr + 1,
+ "bsd98", BSD_MAXPARTITIONS);
+ }
+#endif
+ { /* Pretty size printing. */
+ /* XXX sector size? */
+ unsigned int psize = (end_sect - start_sect) / 2;
+ int unit_char = 'K';
+
+ if (psize > 99999) {
+ psize >>= 10;
+ unit_char = 'M';
+ }
+ printk(" %5d%cB (%5d-%5d)\n", + psize, unit_char, part->cyl, part->end_cyl);
+ }
+ }
+
+ put_dev_sector(sect);
+
+ return nr ? 1 : 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -urN linux/fs/partitions/nec98.h linux98/fs/partitions/nec98.h
--- linux/fs/partitions/nec98.h Thu Jan 1 09:00:00 1970
+++ linux98/fs/partitions/nec98.h Fri Jul 26 11:10:08 2002
@@ -0,0 +1,10 @@
+/*
+ * NEC PC-9800 series partition supports
+ *
+ * Copyright (C) 1998-2000 Kyoto University Microcomputer Club
+ */
+
+#define NEC98_PTABLE_MAGIC 0xAA55
+
+extern int nec98_partition(struct parsed_partitions *state,
+ struct block_device *bdev);
Osamu Tomita
2002-11-02 18:10:42 UTC
Permalink
This is a part 13/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
parallel drivers
- add new driver for support legacy PC-9800 printer port.
- IO address change (parport_pc)
- IRQ number change (parport_pc)

diffstat:
drivers/char/Kconfig | 21 +
drivers/char/lp_old98.c | 577 +++++++++++++++++++++++++++++++++++++++++++
drivers/parport/parport_pc.c | 68 ++++-
include/linux/parport_pc.h | 10 4 files changed, 671 insertions(+), 5 deletions(-)

patch:
diff -urN linux/drivers/parport/parport_pc.c linux98/drivers/parport/parport_pc.c
--- linux/drivers/parport/parport_pc.c Sat Oct 19 13:01:52 2002
+++ linux98/drivers/parport/parport_pc.c Sat Oct 26 17:01:09 2002
@@ -85,6 +85,8 @@
#define DPRINTK(stuff...)
#endif
+/* Indicates PC-9800 architecture No:0 Yes:1 */
+extern int pc98;
#define NR_SUPERIOS 3
static struct superio_struct { /* For Super-IO chips autodetection */
@@ -332,7 +334,10 @@
unsigned char parport_pc_read_status(struct parport *p)
{
- return inb (STATUS (p));
+ if (pc98 && p->base == 0x40)
+ return ((inb(0x42) & 0x04) << 5) | PARPORT_STATUS_ERROR;
+ else
+ return inb (STATUS (p));
}
void parport_pc_disable_irq(struct parport *p)
@@ -1644,6 +1649,8 @@
{
unsigned char r, w;
+ if (pc98 && pb->base == 0x40)
+ return PARPORT_MODE_PCSPP;
/*
* first clear an eventually pending EPP timeout * I (***@ife.ee.ethz.ch) have an SMSC chipset
@@ -1777,6 +1784,9 @@
{
int ok = 0;
+ if (pc98 && pb->base == 0x40)
+ return 0; /* never support */
+
clear_epp_timeout(pb);
/* try to tri-state the buffer */
@@ -1908,6 +1918,9 @@
config & 0x80 ? "Level" : "Pulses");
configb = inb (CONFIGB (pb));
+ if (pc98 && (CONFIGB(pb) == 0x14d) && ((configb & 0x38) == 0x30))
+ configb = (configb & ~0x38) | 0x28; /* IRQ 14 */
+
printk (KERN_DEBUG "0x%lx: ECP port cfgA=0x%02x cfgB=0x%02x\n",
pb->base, config, configb);
printk (KERN_DEBUG "0x%lx: ECP settings irq=", pb->base);
@@ -2048,6 +2061,9 @@
ECR_WRITE (pb, ECR_CNF << 5); /* Configuration MODE */
intrLine = (inb (CONFIGB (pb)) >> 3) & 0x07;
+ if (pc98 && (CONFIGB(pb) == 0x14d) && (intrLine == 6))
+ intrLine = 5; /* IRQ 14 */
+
irq = lookup[intrLine];
ECR_WRITE (pb, oecr);
@@ -2212,7 +2228,14 @@
struct parport tmp;
struct parport *p = &tmp;
int probedirq = PARPORT_IRQ_NONE;
- if (check_region(base, 3)) return NULL;
+ if (pc98 && base == 0x40) {
+ int i;
+ for (i = 0; i < 8; i += 2)
+ if (check_region(base + i, 1)) return NULL;
+ } else {
+ if (check_region(base, 3)) return NULL;
+ }
+
priv = kmalloc (sizeof (struct parport_pc_private), GFP_KERNEL);
if (!priv) {
printk (KERN_DEBUG "parport (0x%lx): no memory!\n", base);
@@ -2245,7 +2268,7 @@
if (base_hi && !check_region(base_hi,3))
parport_ECR_present(p);
- if (base != 0x3bc) {
+ if (!pc98 && base != 0x3bc) {
if (!check_region(base+0x3, 5)) {
if (!parport_EPP_supported(p))
parport_ECPEPP_supported(p);
@@ -2343,7 +2366,12 @@
printk(KERN_INFO "%s: irq %d detected\n", p->name, probedirq);
parport_proc_register(p);
- request_region (p->base, 3, p->name);
+ if (pc98 && p->base == 0x40) {
+ int i;
+ for (i = 0; i < 8; i += 2)
+ request_region(p->base + i, 1, p->name);
+ } else
+ request_region (p->base, 3, p->name);
if (p->size > 3)
request_region (p->base + 3, p->size - 3, p->name);
if (p->modes & PARPORT_MODE_ECP)
@@ -2413,7 +2441,13 @@
free_dma(p->dma);
if (p->irq != PARPORT_IRQ_NONE)
free_irq(p->irq, p);
- release_region(p->base, 3);
+ if (pc98 && p->base == 0x40) {
+ int i;
+ for (i = 0; i < 8; i += 2)
+ release_region(p->base + i, 1);
+ } else
+ release_region(p->base, 3);
+
if (p->size > 3)
release_region(p->base + 3, p->size - 3);
if (p->modes & PARPORT_MODE_ECP)
@@ -2994,6 +3028,30 @@
{
int count = 0;
+ if (pc98) {
+ /* Set default resource settings for old style parport */
+ int base = 0x40;
+ int base_hi = 0;
+ int irq = PARPORT_IRQ_NONE;
+ int dma = PARPORT_DMA_NONE;
+
+ /* Check PC9800 old style parport */
+ outb(inb(0x149) & ~0x10, 0x149); /* disable IEEE1284 */
+ if (!(inb(0x149) & 0x10)) { /* IEEE1284 disabled ? */
+ outb(inb(0x149) | 0x10, 0x149); /* enable IEEE1284 */
+ if (inb(0x149) & 0x10) { /* IEEE1284 enabled ? */
+ /* Set default settings for IEEE1284 parport */
+ base = 0x140;
+ base_hi = 0x14c;
+ irq = 14;
+ /* dma = PARPORT_DMA_NONE; */
+ }
+ }
+
+ if (parport_pc_probe_port(base, base_hi, irq, dma, NULL))
+ count++;
+ }
+
if (parport_pc_probe_port(0x3bc, 0x7bc, autoirq, autodma, NULL))
count++;
if (parport_pc_probe_port(0x378, 0x778, autoirq, autodma, NULL))
diff -urN linux/include/linux/parport_pc.h linux98/include/linux/parport_pc.h
--- linux/include/linux/parport_pc.h Tue Jun 12 11:15:27 2001
+++ linux98/include/linux/parport_pc.h Sun Aug 19 14:13:09 2001
@@ -119,6 +119,11 @@
#endif
ctr = (ctr & ~mask) ^ val;
ctr &= priv->ctr_writable; /* only write writable bits. */
+#ifdef CONFIG_PC9800
+ if (p->base == 0x40 && ((priv->ctr) ^ ctr) & 0x01)
+ outb(0x0e | ((ctr & 0x01) ^ 0x01), 0x46);
+ else
+#endif /* CONFIG_PC9800 */
outb (ctr, CONTROL (p));
priv->ctr = ctr; /* Update soft copy */
return ctr;
@@ -191,6 +196,11 @@
extern __inline__ unsigned char parport_pc_read_status(struct parport *p)
{
+#ifdef CONFIG_PC9800
+ if (p->base == 0x40)
+ return ((inb(0x42) & 0x04) << 5) | PARPORT_STATUS_ERROR;
+ else
+#endif /* CONFIG_PC9800 */
return inb(STATUS(p));
}
diff -urN linux/drivers/char/Kconfig linux98/drivers/char/Kconfig
--- linux/drivers/char/Kconfig Thu Oct 31 13:23:11 2002
+++ linux98/drivers/char/Kconfig Thu Oct 31 19:17:02 2002
@@ -574,6 +574,17 @@
console. This driver allows each pSeries partition to have a console
which is accessed via the HMC.
+config PC9800_OLDLP
+ tristate "NEC PC-9800 old-style printer port support"
+ depends on PC9800 && !PARPORT
+ ---help---
+ If you intend to attach a printer to the parallel port of NEC PC-9801
+ /PC-9821 with OLD compatibility mode, Say Y.
+
+config PC9800_OLDLP_CONSOLE
+ bool "Support for console on line printer"
+ depends on PC9800_OLDLP
+
source "drivers/i2c/Kconfig"
@@ -1053,6 +1064,7 @@
config RTC
tristate "Enhanced Real Time Clock Support"
+ depends on !PC9800
---help---
If you say Y here and create a character special file /dev/rtc with
major number 10 and minor number 135 using mknod ("man mknod"), you
@@ -1111,6 +1123,15 @@
bool "EFI Real Time Clock Services"
depends on IA64
+config RTC98
+ tristate "NEC PC-9800 Real Time Clock Support"
+ depends on PC9800
+ default y
+ ---help---
+ If you say Y here and create a character special file /dev/rtc with
+ major number 10 and minor number 135 using mknod ("man mknod"), you
+ will get access to the real time clock (or hardware clock) built
+
config H8
bool "Tadpole ANA H8 Support (OBSOLETE)"
depends on OBSOLETE && ALPHA_BOOK1
diff -urN linux/drivers/char/lp_old98.c linux98/drivers/char/lp_old98.c
--- linux/drivers/char/lp_old98.c Thu Jan 1 09:00:00 1970
+++ linux98/drivers/char/lp_old98.c Sat Oct 26 17:13:23 2002
@@ -0,0 +1,577 @@
+/*
+ * linux/drivers/char/lp_old98.c
+ *
+ * printer port driver for ancient PC-9800s with no bidirectional port support
+ *
+ * Copyright (C) 1998,99 Kousuke Takai <***@kmc.kyoto-u.ac.jp>,
+ * Kyoto University Microcomputer Club
+ *
+ * This driver is based on and has compatibility with `lp.c',
+ * generic PC printer port driver.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/console.h>
+#include <linux/version.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#if !defined(CONFIG_PC9800) && !defined(CONFIG_PC98)
+#error This driver works only for NEC PC-9800 series
+#endif
+
+#if LINUX_VERSION_CODE < 0x20200
+# define LP_STATS
+#endif
+
+#if LINUX_VERSION_CODE >= 0x2030b
+# define CONFIG_RESOURCE98
+#endif
+
+#include <linux/lp.h>
+
+/*
+ * I/O port numbers
+ */
+#define LP_PORT_DATA 0x40
+#define LP_PORT_STATUS (LP_PORT_DATA+2)
+#define LP_PORT_STROBE (LP_PORT_DATA+4)
+#define LP_PORT_CONTROL (LP_PORT_DATA+6)
+
+#define LP_PORT_H98MODE 0x0448
+#define LP_PORT_EXTMODE 0x0149
+
+/*
+ * bit mask for I/O
+ */
+#define LP_MASK_nBUSY (1 << 2)
+#define LP_MASK_nSTROBE (1 << 7)
+
+#define LP_CONTROL_ASSERT_STROBE (0x0e)
+#define LP_CONTROL_NEGATE_STROBE (0x0f)
+
+/*
+ * Acceptable maximum value for non-privileged user for LPCHARS ioctl.
+ */
+#define LP_CHARS_NOPRIV_MAX 65535
+
+#define DC1 '\x11'
+#define DC3 '\x13'
+
+/* PC-9800s have at least and at most one old-style printer port. */
+static struct lp_struct lp = {
+ /* Following `TAG: INITIALIZER' notations are GNU CC extension. */
+ flags: LP_EXIST | LP_ABORTOPEN,
+ chars: LP_INIT_CHAR,
+ time: LP_INIT_TIME,
+ wait: LP_INIT_WAIT,
+};
+
+static int dc1_check = 1000;
+
+#undef LP_OLD98_DEBUG
+
+#ifndef __udelay_val
+# define __udelay_val current_cpu_data.loops_per_sec
+#endif
+
+static inline void nanodelay(unsigned long nanosecs) /* Evil ? */
+{
+ if( nanosecs ) {
+ nanosecs *= (unsigned long)((1ULL << 40) / 1000000000ULL);
+ __asm__("mul%L2 %2"
+ : "=d"(nanosecs) : "a"(nanosecs), "g"(__udelay_val));
+ __delay(nanosecs >> 8);
+ }
+}
+
+#ifdef CONFIG_PC9800_OLDLP_CONSOLE
+static struct console lp_old98_console; /* defined later */
+static __typeof__(lp_old98_console.flags) saved_console_flags;
+#endif
+
+static DECLARE_WAIT_QUEUE_HEAD (lp_old98_waitq);
+
+static void lp_old98_timer_function(unsigned long data);
+
+static void lp_old98_timer_function(unsigned long data)
+{
+ if (inb(LP_PORT_STATUS) & LP_MASK_nBUSY)
+ wake_up_interruptible(&lp_old98_waitq);
+ else {
+ struct timer_list *t = (struct timer_list *) data;
+
+ t->expires = jiffies + 1;
+ add_timer(t);
+ }
+}
+
+static inline int
+lp_old98_wait_ready(void)
+{
+ struct timer_list timer;
+
+ init_timer(&timer);
+ timer.function = lp_old98_timer_function;
+ timer.expires = jiffies + 1;
+ timer.data = (unsigned long) &timer;
+ add_timer(&timer);
+ interruptible_sleep_on(&lp_old98_waitq);
+ del_timer(&timer);
+ return signal_pending(current);
+}
+
+static inline int lp_old98_char(char lpchar)
+{
+ unsigned long count = 0;
+#ifdef LP_STATS
+ int tmp;
+#endif
+
+ while( !(inb(LP_PORT_STATUS) & LP_MASK_nBUSY) ) {
+ count++;
+ if (count >= lp.chars)
+ return 0;
+ }
+
+ outb(lpchar, LP_PORT_DATA);
+
+#ifdef LP_STATS
+ /*
+ * Update lp statsistics here (and between next two outb()'s).
+ * Time to compute it is part of storobe delay.
+ */
+ if( count > lp.stats.maxwait ) {
+#ifdef LP_OLD98_DEBUG
+ printk(KERN_DEBUG "lp_old98: success after %d counts.\n",
+ count);
+#endif
+ lp.stats.maxwait = count;
+ }
+ count *= 256;
+ tmp = count - lp.stats.meanwait;
+ if( tmp < 0 )
+ tmp = -tmp;
+#endif
+ nanodelay(lp.wait);
+ + /* negate PSTB# (activate strobe) */
+ outb(LP_CONTROL_ASSERT_STROBE, LP_PORT_CONTROL);
+
+#ifdef LP_STATS
+ lp.stats.meanwait = (255 * lp.stats.meanwait + count + 128) / 256;
+ lp.stats.mdev = (127 * lp.stats.mdev + tmp + 64) / 128;
+ lp.stats.chars++;
+#endif
+
+ nanodelay(lp.wait);
+
+ /* assert PSTB# (deactivate strobe) */
+ outb(LP_CONTROL_NEGATE_STROBE, LP_PORT_CONTROL);
+
+ return 1;
+}
+
+#if LINUX_VERSION_CODE < 0x20200
+static long lp_old98_write(struct inode * inode, struct file * file,
+ const char * buf, unsigned long count)
+#else
+static ssize_t lp_old98_write(struct file * file,
+ const char * buf, size_t count,
+ loff_t *dummy)
+#endif +{
+ unsigned long total_bytes_written = 0;
+
+ if (!access_ok(VERIFY_READ, buf, count))
+ return -EFAULT;
+
+#ifdef LP_STATS
+ if( jiffies - lp.lastcall > lp.time )
+ lp.runchars = 0;
+ lp.lastcall = jiffies;
+#endif
+
+ do {
+ unsigned long bytes_written = 0;
+ unsigned long copy_size
+ = (count < LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
+
+ if (__copy_from_user(lp.lp_buffer, buf, copy_size))
+ return -EFAULT;
+
+ while( bytes_written < copy_size ) {
+ if( lp_old98_char(lp.lp_buffer[bytes_written]) )
+ bytes_written++;
+ else {
+#ifdef LP_STATS
+ int rc = lp.runchars + bytes_written;
+
+ if( rc > lp.stats.maxrun )
+ lp.stats.maxrun = rc;
+
+ lp.stats.sleeps++;
+#endif
+#ifdef LP_OLD98_DEBUG
+ printk(KERN_DEBUG
+ "lp_old98: sleeping at %d characters"
+ " for %d jiffies\n",
+ lp.runchars, lp.time);
+ lp.runchars = 0;
+#endif
+ if (lp_old98_wait_ready())
+ return ((total_bytes_written
+ + bytes_written)
+ ? : -EINTR);
+ }
+ }
+ total_bytes_written += bytes_written;
+ buf += bytes_written;
+#ifdef LP_STATS
+ lp.runchars += bytes_written;
+#endif
+ count -= bytes_written;
+ } while( count > 0 );
+
+ return total_bytes_written;
+}
+
+static long long lp_old98_llseek(struct file * file,
+ long long offset, int whence)
+{
+ return -ESPIPE; /* cannot seek like pipe */
+}
+
+static int lp_old98_open(struct inode * inode, struct file * file)
+{
+ if( MINOR(inode->i_rdev) != 0 )
+ return -ENXIO;
+ if( lp.flags & LP_BUSY )
+ return -EBUSY;
+
+ if ((lp.lp_buffer = kmalloc(LP_BUFFER_SIZE, GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ if (dc1_check && (lp.flags & LP_ABORTOPEN)
+ && !(file->f_flags & O_NONBLOCK) ) {
+ /*
+ * Check whether printer is on-line.
+ * PC-9800's old style port have only BUSY# as status input,
+ * so that it is impossible to distinguish that the printer is
+ * ready and that the printer is off-line or not connected
+ * (in both case BUSY# is in the same state). So:
+ *
+ * (1) output DC1 (0x11) to printer port and do strobe.
+ * (2) watch BUSY# line for a while. If BUSY# is pulled
+ * down, the printer will be ready. Otherwise,
+ * it will be off-line (or not connected, or power-off,
+ * ...).
+ *
+ * The source of this procedure:
+ * Terumasa KODAKA, Kazufumi SHIMIZU, Yu HAYAMI:
+ * `PC-9801 Super Technique', Ascii, 1992.
+ */
+ int count;
+ unsigned long eflags;
+
+ save_flags(eflags);
+ cli(); /* interrupts while check is fairly bad */
+
+ if (!lp_old98_char(DC1)) {
+ restore_flags(eflags);
+ return -EBUSY;
+ }
+ count = (unsigned int)dc1_check > 10000 ? 10000 : dc1_check;
+ while( inb(LP_PORT_STATUS) & LP_MASK_nBUSY )
+ if( --count == 0 ) {
+ restore_flags(eflags);
+ return -ENODEV;
+ }
+ restore_flags(eflags);
+ }
+
+ lp.flags |= LP_BUSY;
+
+#ifdef CONFIG_PC9800_OLDLP_CONSOLE
+ saved_console_flags = lp_old98_console.flags;
+ lp_old98_console.flags &= ~CON_ENABLED;
+#endif
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int lp_old98_release(struct inode * inode, struct file * file)
+{
+ kfree(lp.lp_buffer);
+ lp.lp_buffer = NULL;
+ lp.flags &= ~LP_BUSY;
+#ifdef CONFIG_PC9800_OLDLP_CONSOLE
+ lp_old98_console.flags = saved_console_flags;
+#endif
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int lp_old98_init_device(void)
+{
+ unsigned char data;
+
+ if( (data = inb(LP_PORT_EXTMODE)) != 0xFF && (data & 0x10) ) {
+ printk(KERN_INFO
+ "lp_old98: shutting down extended parallel port mode...\n");
+ outb(data & ~0x10, LP_PORT_EXTMODE);
+ }
+#ifdef PC98_HW_H98
+ if( (pc98_hw_flags & PC98_HW_H98)
+ && ((data = inb(LP_PORT_H98MODE)) & 0x01) ) {
+ printk(KERN_INFO
+ "lp_old98: shutting down H98 full centronics mode...\n");
+ outb(data & ~0x01, LP_PORT_H98MODE);
+ }
+#endif
+ return 0;
+}
+
+/*
+ * Many use of `put_user' macro enlarge code size...
+ */
+static /* not inline */ int lp_old98_put_user(int val, int *addr)
+{
+ return put_user(val, addr);
+}
+
+static int lp_old98_ioctl(struct inode *inode, struct file *file,
+ unsigned int command, unsigned long arg)
+{
+ int retval = 0;
+
+ switch ( command ) {
+ case LPTIME:
+ lp.time = arg * HZ/100;
+ break;
+ case LPCHAR:
+ lp.chars = arg;
+ break;
+ case LPABORT:
+ if( arg )
+ lp.flags |= LP_ABORT;
+ else
+ lp.flags &= ~LP_ABORT;
+ break;
+ case LPABORTOPEN:
+ if( arg )
+ lp.flags |= LP_ABORTOPEN;
+ else
+ lp.flags &= ~LP_ABORTOPEN;
+ break;
+ case LPCAREFUL:
+ /* do nothing */
+ break;
+ case LPWAIT:
+ lp.wait = arg;
+ break;
+ case LPGETIRQ:
+ retval = lp_old98_put_user(0, (int *)arg);
+ break;
+ case LPGETSTATUS:
+ /*
+ * convert PC-9800's status to IBM PC's one, so that tunelp(8)
+ * works in the same way on this driver.
+ */
+ retval = lp_old98_put_user((inb(LP_PORT_STATUS)
+ & LP_MASK_nBUSY)
+ ? (LP_PBUSY | LP_PERRORP)
+ : LP_PERRORP,
+ (int *)arg);
+ break;
+ case LPRESET:
+ retval = lp_old98_init_device();
+ break;
+#ifdef LP_STATS
+ case LPGETSTATS:
+ if( copy_to_user((struct lp_stats *)arg, &lp.stats,
+ sizeof(struct lp_stats)) )
+ retval = -EFAULT;
+ else if (suser())
+ memset(&lp.stats, 0, sizeof(struct lp_stats));
+ break;
+#endif
+ case LPGETFLAGS:
+ retval = lp_old98_put_user(lp.flags, (int *)arg);
+ break;
+ case LPSETIRQ: + default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+static struct file_operations lp_old98_fops = {
+ owner: THIS_MODULE,
+ llseek: lp_old98_llseek,
+ read: NULL,
+ write: lp_old98_write,
+ ioctl: lp_old98_ioctl,
+ open: lp_old98_open,
+ release:lp_old98_release,
+};
+
+/*
+ * Support for console on lp_old98
+ */
+#ifdef CONFIG_PC9800_OLDLP_CONSOLE
+
+static inline void io_delay(void)
+{
+ unsigned char dummy; /* actually not output */
+
+ asm volatile ("out%B0 %0,%1" : "=a"(dummy) : "N"(0x5f));
+}
+
+static void lp_old98_console_write(struct console *console,
+ const char *s, unsigned int count)
+{
+ int i;
+ static unsigned int timeout_run = 0;
+
+ while (count) {
+ /* wait approx 1.2 seconds */
+ for (i = 2000000;
+ !(inb(LP_PORT_STATUS) & LP_MASK_nBUSY);
+ io_delay())
+ if (!--i) {
+ if (++timeout_run >= 10)
+ /* disable forever... */
+ console->flags &= ~CON_ENABLED;
+ return;
+ }
+
+ timeout_run = 0;
+
+ if (*s == '\n') {
+ outb('\r', LP_PORT_DATA);
+ io_delay();
+ io_delay();
+ outb(LP_CONTROL_ASSERT_STROBE, LP_PORT_CONTROL);
+ io_delay();
+ io_delay();
+ outb(LP_CONTROL_NEGATE_STROBE, LP_PORT_CONTROL);
+ io_delay();
+ io_delay();
+ for (i = 1000000;
+ !(inb(LP_PORT_STATUS) & LP_MASK_nBUSY);
+ io_delay())
+ if (!--i)
+ return;
+ }
+
+ outb(*s++, LP_PORT_DATA);
+ io_delay();
+ io_delay();
+ outb(LP_CONTROL_ASSERT_STROBE, LP_PORT_CONTROL);
+ io_delay();
+ io_delay();
+ outb(LP_CONTROL_NEGATE_STROBE, LP_PORT_CONTROL);
+ io_delay();
+ io_delay();
+
+ --count;
+ }
+}
+
+static kdev_t lp_old98_console_device(struct console *console)
+{
+ return MKDEV(LP_MAJOR, 0);
+}
+
+static struct console lp_old98_console = {
+ name: "lp_old98",
+ write: lp_old98_console_write,
+ device: lp_old98_console_device,
+ flags: CON_PRINTBUFFER,
+ index: -1,
+};
+
+#endif /* console on lp_old98 */
+
+#ifdef MODULE
+#define lp_old98_init init_module
+#endif
+
+int __init lp_old98_init(void)
+{
+ if (check_region(LP_PORT_DATA, 1) || check_region(LP_PORT_STATUS, 1)
+ || check_region(LP_PORT_STROBE, 1)
+#ifdef PC98_HW_H98
+ || ((pc98_hw_flags & PC98_HW_H98)
+ && check_region(LP_PORT_H98MODE, 1))
+#endif
+ || check_region(LP_PORT_EXTMODE, 1)) {
+ printk(KERN_ERR
+ "lp_old98: I/O ports already occupied, giving up.\n");
+ return -EBUSY;
+ }
+ if (register_chrdev(LP_MAJOR, "lp", &lp_old98_fops)) {
+ printk(KERN_ERR "lp_old98: unable to get major %d\n",
+ LP_MAJOR);
+ return -EBUSY;
+ }
+
+#ifdef CONFIG_PC9800_OLDLP_CONSOLE
+ register_console(&lp_old98_console);
+ printk(KERN_INFO "lp_old98: console ready\n");
+#endif
+
+ request_region(LP_PORT_DATA, 1, "lp_old98");
+ request_region(LP_PORT_STATUS, 1, "lp_old98");
+ request_region(LP_PORT_STROBE, 1, "lp_old98");
+
+ /*
+ * rest are not needed by this driver,
+ * but for locking out other printer drivers...
+ */
+#ifdef PC98_HW_H98
+ if( pc98_hw_flags & PC98_HW_H98 )
+ request_region(LP_PORT_H98MODE, 1, "lp_old98");
+#endif
+ request_region(LP_PORT_EXTMODE, 1, "lp_old98");
+ lp_old98_init_device();
+
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+#ifdef CONFIG_PC9800_OLDLP_CONSOLE
+ unregister_console(&lp_old98_console);
+#endif
+ unregister_chrdev(LP_MAJOR, "lp");
+
+ release_region(LP_PORT_DATA, 1);
+ release_region(LP_PORT_STATUS, 1);
+ release_region(LP_PORT_STROBE, 1);
+#ifdef PC98_HW_H98
+ if( pc98_hw_flags & PC98_HW_H98 )
+ release_region(LP_PORT_H98MODE, 1);
+#endif
+ release_region(LP_PORT_EXTMODE, 1);
+}
+
+MODULE_PARM(dc1_check, "1i");
+MODULE_AUTHOR("Kousuke Takai <***@kmc.kyoto-u.ac.jp>");
+
+#endif
Osamu Tomita
2002-11-02 18:14:38 UTC
Permalink
This is a part 14/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
PCI related modules
- adapted to IRQ number differences.
- adapted to PCI BIOS function number differences.
- add some entry to PCI name database.
- add quirks for legacy bus.
- IO address limitation change.

diffstat:
arch/i386/pci/irq.c | 27 +++++++++++++++++++++++++++
arch/i386/pci/pcbios.c | 17 +++++++++++++++++
drivers/pci/pci.ids | 16 ++++++++++------
drivers/pci/quirks.c | 7 +++++++
drivers/pcmcia/yenta.c | 6 ++++++
include/asm-i386/pci.h | 4 ++++
include/linux/pci_ids.h | 20 +++++++++++++++++++-
7 files changed, 90 insertions(+), 7 deletions(-)

patch:
diff -urN linux/arch/i386/pci/irq.c linux98/arch/i386/pci/irq.c
--- linux/arch/i386/pci/irq.c Sat Oct 12 13:22:46 2002
+++ linux98/arch/i386/pci/irq.c Sat Oct 12 14:18:52 2002
@@ -5,6 +5,7 @@
*/
#include <linux/config.h>
+#include <linux/pci_ids.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/pci.h>
@@ -25,6 +26,7 @@
static struct irq_routing_table *pirq_table;
+#ifndef CONFIG_PC9800
/*
* Never use: 0, 1, 2 (timer, keyboard, and cascade)
* Avoid using: 13, 14 and 15 (FP error and IDE).
@@ -36,6 +38,20 @@
1000000, 1000000, 1000000, 1000, 1000, 0, 1000, 1000,
0, 0, 0, 0, 1000, 100000, 100000, 100000
};
+#else
+/*
+ * Never use: 0, 1, 2, 7 (timer, keyboard, CRT VSYNC and cascade)
+ * Avoid using: 8, 9 and 15 (FP error and IDE).
+ * Penalize: 4, 5, 11, 12, 13, 14 (known ISA uses: serial, floppy, sound, mouse
+ * and parallel)
+ */
+unsigned int pcibios_irq_mask = 0xff78;
+
+static int pirq_penalty[16] = {
+ 1000000, 1000000, 1000000, 0, 1000, 1000, 0, 1000000,
+ 100000, 100000, 0, 1000, 1000, 1000, 1000, 100000
+};
+#endif
struct irq_router {
char *name;
@@ -612,6 +628,17 @@
r->set(pirq_router_dev, dev, pirq, 11);
}
+#ifdef CONFIG_PC9800
+ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) {
+ if (pci_find_device(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82439TX, NULL) != NULL) {
+ if (mask & 0x0040) {
+ mask &= 0x0040; /* assign IRQ 6 only */
+ printk("pci-irq: Use IRQ6 for CardBus controller\n");
+ }
+ }
+ }
+#endif
/*
* Find the best IRQ to assign: use the one
* reported by the device if possible.
diff -urN linux/arch/i386/pci/pcbios.c linux98/arch/i386/pci/pcbios.c
--- linux/arch/i386/pci/pcbios.c Sun Jun 9 14:27:00 2002
+++ linux98/arch/i386/pci/pcbios.c Mon Jun 10 20:49:14 2002
@@ -7,6 +7,7 @@
#include "pci.h"
+#ifndef CONFIG_PC9800
#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX
#define PCIBIOS_PCI_BIOS_PRESENT 0xb101
#define PCIBIOS_FIND_PCI_DEVICE 0xb102
@@ -20,6 +21,22 @@
#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d
#define PCIBIOS_GET_ROUTING_OPTIONS 0xb10e
#define PCIBIOS_SET_PCI_HW_INT 0xb10f
+#else /* CONFIG_PC9800 */
+#define PCIBIOS_PCI_FUNCTION_ID 0xccXX
+#define PCIBIOS_PCI_BIOS_PRESENT 0xcc81
+#define PCIBIOS_FIND_PCI_DEVICE 0xcc82
+#define PCIBIOS_FIND_PCI_CLASS_CODE 0xcc83
+/* PCIBIOS_GENERATE_SPECIAL_CYCLE 0xcc86 (not supported by bios) */
+#define PCIBIOS_READ_CONFIG_BYTE 0xcc88
+#define PCIBIOS_READ_CONFIG_WORD 0xcc89
+#define PCIBIOS_READ_CONFIG_DWORD 0xcc8a
+#define PCIBIOS_WRITE_CONFIG_BYTE 0xcc8b
+#define PCIBIOS_WRITE_CONFIG_WORD 0xcc8c
+#define PCIBIOS_WRITE_CONFIG_DWORD 0xcc8d
+#define PCIBIOS_GET_ROUTING_OPTIONS 0xcc8e /* PCI 2.1 only */
+#define PCIBIOS_SET_PCI_HW_INT 0xcc8f /* PCI 2.1 only */
+/* Note: PC-9800 confirms PCI 2.1 on only few models */
+#endif /* CONFIG_PC9800 */
/* BIOS32 signature: "_32_" */
#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24))
diff -urN linux/drivers/pcmcia/yenta.c linux98/drivers/pcmcia/yenta.c
--- linux/drivers/pcmcia/yenta.c Sat Oct 12 13:22:10 2002
+++ linux98/drivers/pcmcia/yenta.c Sun Oct 13 17:29:24 2002
@@ -3,6 +3,7 @@
*
* (C) Copyright 1999, 2000 Linus Torvalds
*/
+#include <linux/config.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/sched.h>
@@ -505,6 +506,7 @@
add_timer(&socket->poll_timer);
}
+#ifndef CONFIG_PC9800
/*
* Only probe "regular" interrupts, don't
* touch dangerous spots like the mouse irq,
@@ -515,6 +517,10 @@
* Default to 11, 10, 9, 7, 6, 5, 4, 3.
*/
static u32 isa_interrupts = 0x0ef8;
+#else
+/* Default to 12, 10, 6, 5, 3. */
+static u32 isa_interrupts = 0x1468;
+#endif
static unsigned int yenta_probe_irq(pci_socket_t *socket, u32 isa_irq_mask)
{
diff -urN linux/drivers/pci/pci.ids linux98/drivers/pci/pci.ids
--- linux/drivers/pci/pci.ids Tue Oct 8 10:56:11 2002
+++ linux98/drivers/pci/pci.ids Tue Oct 8 11:01:40 2002
@@ -497,8 +497,8 @@
1011 500b DE500B Fast Ethernet
1014 0001 10/100 EtherJet Cardbus
1025 0315 ALN315 Fast Ethernet
- 1033 800c PC-9821-CS01
- 1033 800d PC-9821NR-B06
+ 1033 800c PC-9821-CS01 100BASE-TX Interface Card
+ 1033 800d PC-9821NR-B06 100BASE-TX Interface Card
108d 0016 Rapidfire 2327 10/100 Ethernet
108d 0017 GoCard 2250 Ethernet 10/100 Cardbus
10b8 2005 SMC8032DT Extreme Ethernet 10/100
@@ -1049,17 +1049,21 @@
0003 ATM Controller
0004 R4000 PCI Bridge
0005 PCI to 486-like bus Bridge
- 0006 GUI Accelerator
+ 0006 PC-9800 Graphic Accelerator
0007 PCI to UX-Bus Bridge
- 0008 GUI Accelerator
- 0009 GUI Accelerator for W98
+ 0008 PC-9800 Graphic Accelerator
+ 0009 PCI to PC9800 Core-Graph Bridge
+ 0016 PCI to VL Bridge
001a [Nile II]
0021 Vrc4373 [Nile I]
0029 PowerVR PCX1
002a PowerVR 3D
+ 002c Star Alpha 2
+ 002d PCI to C-bus Bridge
0035 USB
1179 0001 USB
12ee 7000 Root Hub
+ 003b PCI to C-bus Bridge
003e NAPCCARD Cardbus Controller
0046 PowerVR PCX2 [midas]
005a Vrc5074 [Nile 4]
@@ -3485,7 +3489,7 @@
5811 FW323
dead 0800 FireWire Host Bus Adapter
11c2 Sand Microelectronics
-11c3 NEC Corp
+11c3 NEC Corporation
11c4 Document Technologies, Inc
11c5 Shiva Corporation
11c6 Dainippon Screen Mfg. Co. Ltd
diff -urN linux/drivers/pci/quirks.c linux98/drivers/pci/quirks.c
--- linux/drivers/pci/quirks.c Tue Sep 10 02:35:00 2002
+++ linux98/drivers/pci/quirks.c Tue Sep 10 09:07:50 2002
@@ -54,7 +54,11 @@
{
if (!isa_dma_bridge_buggy) {
isa_dma_bridge_buggy=1;
+#ifndef CONFIG_PC9800
printk(KERN_INFO "Activating ISA DMA hang workarounds.\n");
+#else
+ printk(KERN_INFO "Activating C-bus DMA hang workarounds.\n");
+#endif
}
}
@@ -491,6 +495,9 @@
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0, quirk_isa_dma_hangs },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596, quirk_isa_dma_hangs },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, quirk_isa_dma_hangs },
+#ifdef CONFIG_PC9800
+ { PCI_FIXUP_FINAL, PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_CBUS_1, quirk_isa_dma_hangs },
+#endif
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_868, quirk_s3_64M },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_968, quirk_s3_64M },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, quirk_triton }, diff -urN linux/include/asm-i386/pci.h
linux98/include/asm-i386/pci.h
--- linux/include/asm-i386/pci.h Sun Jun 9 14:29:24 2002
+++ linux98/include/asm-i386/pci.h Mon Jun 10 20:49:15 2002
@@ -17,7 +17,11 @@
#endif
extern unsigned long pci_mem_start;
+#ifndef CONFIG_PC9800
#define PCIBIOS_MIN_IO 0x1000
+#else
+#define PCIBIOS_MIN_IO 0x4000
+#endif
#define PCIBIOS_MIN_MEM (pci_mem_start)
void pcibios_config_init(void);
* Status Register Bits
diff -urN linux/include/linux/pci_ids.h linux98/include/linux/pci_ids.h
--- linux/include/linux/pci_ids.h Sat Oct 12 13:21:42 2002
+++ linux98/include/linux/pci_ids.h Sat Oct 12 19:56:04 2002
@@ -456,10 +456,26 @@
#define PCI_DEVICE_ID_MIRO_36050 0x5601
#define PCI_VENDOR_ID_NEC 0x1033
-#define PCI_DEVICE_ID_NEC_PCX2 0x0046
+#define PCI_DEVICE_ID_NEC_CBUS_1 0x0001 /* PCI-Cbus Bridge */
+#define PCI_DEVICE_ID_NEC_LOCAL 0x0002 /* Local Bridge */
+#define PCI_DEVICE_ID_NEC_ATM 0x0003 /* ATM LAN Controller */
+#define PCI_DEVICE_ID_NEC_R4000 0x0004 /* R4000 Bridge */
+#define PCI_DEVICE_ID_NEC_486 0x0005 /* 486 Like Peripheral Bus Bridge */
+#define PCI_DEVICE_ID_NEC_ACCEL_1 0x0006 /* Graphic Accelerator */
+#define PCI_DEVICE_ID_NEC_UXBUS 0x0007 /* UX-Bus Bridge */
+#define PCI_DEVICE_ID_NEC_ACCEL_2 0x0006 /* Graphic Accelerator */
+#define PCI_DEVICE_ID_NEC_GRAPH 0x0009 /* PCI-CoreGraph Bridge */
+#define PCI_DEVICE_ID_NEC_VL 0x0016 /* PCI-VL Bridge */
+#define PCI_DEVICE_ID_NEC_STARALPHA2 0x002c /* STAR ALPHA2 */
+#define PCI_DEVICE_ID_NEC_CBUS_2 0x002d /* PCI-Cbus Bridge */
+#define PCI_DEVICE_ID_NEC_USB 0x0035 /* PCI-USB Host */
+#define PCI_DEVICE_ID_NEC_CBUS_3 0x003b
+#define PCI_DEVICE_ID_NEC_PCX2 0x0046 /* PowerVR */
#define PCI_DEVICE_ID_NEC_NILE4 0x005a
#define PCI_DEVICE_ID_NEC_VRC5476 0x009b
#define PCI_DEVICE_ID_NEC_VRC5477_AC97 0x00a6
+#define PCI_DEVICE_ID_NEC_PC9821CS01 0x800c /* PC-9821-CS01 */
+#define PCI_DEVICE_ID_NEC_PC9821NRB06 0x800d /* PC-9821NR-B06 */
#define PCI_VENDOR_ID_FD 0x1036
#define PCI_DEVICE_ID_FD_36C70 0x0000
@@ -1232,6 +1248,8 @@
#define PCI_DEVICE_ID_ATT_L56XMF 0x0440
#define PCI_DEVICE_ID_ATT_VENUS_MODEM 0x480
+#define PCI_VENDOR_ID_NEC2 0x11c3 /* NEC (2nd) */
+
#define PCI_VENDOR_ID_SPECIALIX 0x11cb
#define PCI_DEVICE_ID_SPECIALIX_IO8 0x2000
#define PCI_DEVICE_ID_SPECIALIX_XIO 0x4000
Osamu Tomita
2002-11-02 18:25:09 UTC
Permalink
This is a part 20/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
New driver support for PC-9800 standard text console.

diffstat:
drivers/video/Makefile | 1 drivers/video/gdccon.c | 834 +++++++++++++++++++++++++++++++++++++++++++++++++
include/asm-i386/gdc.h | 217 ++++++++++++
3 files changed, 1052 insertions(+)

patch:
diff -urN linux/drivers/video/Makefile linux98/drivers/video/Makefile
--- linux/drivers/video/Makefile Sat Oct 19 13:01:12 2002
+++ linux98/drivers/video/Makefile Mon Oct 28 23:47:29 2002
@@ -19,6 +19,7 @@
obj-$(CONFIG_PROM_CONSOLE) += promcon.o promcon_tbl.o
obj-$(CONFIG_STI_CONSOLE) += sticon.o sticon-bmode.o sticore.o
obj-$(CONFIG_VGA_CONSOLE) += vgacon.o
+obj-$(CONFIG_GDC_CONSOLE) += gdccon.o
obj-$(CONFIG_MDA_CONSOLE) += mdacon.o
obj-$(CONFIG_FONT_SUN8x16) += font_sun8x16.o
diff -urN linux/drivers/video/gdccon.c linux98/drivers/video/gdccon.c
--- linux/drivers/video/gdccon.c Thu Jan 1 09:00:00 1970
+++ linux98/drivers/video/gdccon.c Mon Oct 28 17:53:54 2002
@@ -0,0 +1,834 @@
+/*
+ * linux/drivers/video/gdccon.c
+ * Low level GDC based console driver for NEC PC-9800 series
+ *
+ * Created 24 Dec 1998 by Linux/98 project
+ *
+ * based on:
+ * linux/drivers/video/vgacon.c in Linux 2.1.131 by Geert Uytterhoeven
+ * linux/char/gdc.c in Linux/98 2.1.57 by Linux/98 project
+ * linux/char/console.c in Linux/98 2.1.57 by Linux/98 project
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/console_struct.h>
+#include <linux/string.h>
+#include <linux/kd.h>
+#include <linux/slab.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+#include <linux/spinlock.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/pc9800.h>
+
+static spinlock_t gdc_lock = SPIN_LOCK_UNLOCKED;
+
+static char str_gdc_master[] = "GDC (master)";
+static char str_gdc_slave[] = "GDC (slave)";
+static char str_crtc[] = "crtc";
+static struct resource gdc_console_resources[] = {
+ {str_gdc_master, 0x60, 0x60, 0},
+ {str_gdc_master, 0x62, 0x62, 0},
+ {str_gdc_master, 0x64, 0x64, 0},
+ {str_gdc_master, 0x66, 0x66, 0},
+ {str_gdc_master, 0x68, 0x68, 0},
+ {str_gdc_master, 0x6a, 0x6a, 0},
+ {str_gdc_master, 0x6c, 0x6c, 0},
+ {str_gdc_master, 0x6e, 0x6e, 0},
+ {str_crtc, 0x70, 0x70, 0},
+ {str_crtc, 0x72, 0x72, 0},
+ {str_crtc, 0x74, 0x74, 0},
+ {str_crtc, 0x76, 0x76, 0},
+ {str_crtc, 0x78, 0x78, 0},
+ {str_crtc, 0x7a, 0x7a, 0},
+ {str_gdc_slave, 0xa0, 0xa0, 0},
+ {str_gdc_slave, 0xa2, 0xa2, 0},
+ {str_gdc_slave, 0xa4, 0xa4, 0},
+ {str_gdc_slave, 0xa6, 0xa6, 0},
+};
+
+#define GDC_CONSOLE_RESOURCES (sizeof(gdc_console_resources)/sizeof(struct resource))
+
+#define BLANK 0x0020
+#define BLANK_ATTR 0x00e1
+
+/* GDC/GGDC port# */
+#define GDC_COMMAND 0x62
+#define GDC_PARAM 0x60
+#define GDC_STAT 0x60
+#define GDC_DATA 0x62
+
+#define MODE_FF1 (0x0068) /* mode F/F register 1 */
+
+#define MODE_FF1_ATR_SEL (0x00) /* 0: vertical line 1: 8001 graphic */
+#define MODE_FF1_GRAPHIC_MODE (0x02) /* 0: color 1: mono */
+#define MODE_FF1_COLUMN_WIDTH (0x04) /* 0: 80col 1: 40col */
+#define MODE_FF1_FONT_SEL (0x06) /* 0: 6x8 1: 7x13 */
+#define MODE_FF1_GRP_MODE (0x08) /* 0: display odd-y raster 1: not */
+#define MODE_FF1_KAC_MODE (0x0a) /* 0: code access 1: dot access */
+#define MODE_FF1_NVMW_PERMIT (0x0c) /* 0: protect 1: permit */
+#define MODE_FF1_DISP_ENABLE (0x0e) /* 0: enable 1: disable */
+
+#define GGDC_COMMAND 0xa2
+#define GGDC_PARAM 0xa0
+#define GGDC_STAT 0xa0
+#define GGDC_DATA 0xa2
+
+/* GDC status */
+#define GDC_DATA_READY (1 << 0)
+#define GDC_FIFO_FULL (1 << 1)
+#define GDC_FIFO_EMPTY (1 << 2)
+#define GGDC_FIFO_EMPTY GDC_FIFO_EMPTY
+#define GDC_DRAWING (1 << 3)
+#define GDC_DMA_EXECUTE (1 << 4) /* nonsense on 98 */
+#define GDC_VERTICAL_SYNC (1 << 5)
+#define GDC_HORIZONTAL_BLANK (1 << 6)
+#define GDC_LIGHTPEN_DETECT (1 << 7) /* nonsense on 98 */
+
+#define ATTR_G (1U << 7)
+#define ATTR_R (1U << 6)
+#define ATTR_B (1U << 5)
+#define ATTR_GRAPHIC (1U << 4)
+#define ATTR_VERTBAR ATTR_GRAPHIC /* vertical bar */
+#define ATTR_UNDERLINE (1U << 3)
+#define ATTR_REVERSE (1U << 2)
+#define ATTR_BLINK (1U << 1)
+#define ATTR_NOSECRET (1U << 0)
+#define AMASK_NOCOLOR (ATTR_GRAPHIC | ATTR_UNDERLINE | ATTR_REVERSE \
+ | ATTR_BLINK | ATTR_NOSECRET)
+
+/*
+ * Interface used by the world
+ */
+static const char *gdccon_startup(void);
+static void gdccon_init(struct vc_data *c, int init);
+static void gdccon_deinit(struct vc_data *c);
+static void gdccon_cursor(struct vc_data *c, int mode);
+static int gdccon_switch(struct vc_data *c);
+static int gdccon_blank(struct vc_data *c, int blank);
+static int gdccon_scrolldelta(struct vc_data *c, int lines);
+static int gdccon_set_origin(struct vc_data *c);
+static void gdccon_save_screen(struct vc_data *c);
+static int gdccon_scroll(struct vc_data *c, int t, int b, int dir, int lines);
+static u8 gdccon_build_attr(struct vc_data *c, u8 color, u8 intensity, u8 blink, u8 underline, u8 reverse);
+static void gdccon_invert_region(struct vc_data *c, u16 *p, int count);
+static unsigned long gdccon_uni_pagedir[2];
+
+/* Description of the hardware situation */
+static unsigned long gdc_vram_base; /* Base of video memory */
+static unsigned long gdc_vram_end; /* End of video memory */
+static unsigned int gdc_video_num_columns = 80;
+ /* Number of text columns */
+static unsigned int gdc_video_num_lines = 25;
+ /* Number of text lines */
+static int gdc_can_do_color = 1; /* Do we support colors? */
+static unsigned char gdc_video_type; /* Card type */
+static unsigned char gdc_hardscroll_enabled;
+static unsigned char gdc_hardscroll_user_enable = 1;
+static int gdc_vesa_blanked = 0;
+static unsigned int gdc_rolled_over = 0;
+
+#define DISP_FREQ_AUTO 0
+#define DISP_FREQ_25k 1
+#define DISP_FREQ_31k 2
+
+static unsigned int gdc_disp_freq = DISP_FREQ_AUTO;
+
+#define gdc_attr_offset(x) ((typeof(x))((unsigned long)(x)+0x2000))
+
+#define gdc_outb(val, port) outb_p((val), (port))
+#define gdc_inb(port) inb_p(port)
+
+#define __gdc_write_command(cmd) gdc_outb((cmd), GDC_COMMAND)
+#define __gdc_write_param(param) gdc_outb((param), GDC_PARAM)
+
+static const char * __init gdccon_startup(void)
+{
+ const char *display_desc = NULL;
+ unsigned long hdots = gdc_video_num_lines * 16;
+ int i;
+
+ while (!(inb_p(GDC_STAT) & GDC_FIFO_EMPTY));
+ while (!(inb_p(GGDC_STAT) & GDC_FIFO_EMPTY));
+ spin_lock_irq(&gdc_lock);
+ outb_p(0x0c, GDC_COMMAND); /* STOP */
+ outb_p(0x0c, GGDC_COMMAND); /* STOP */
+ if (PC9800_9821_P() && gdc_disp_freq == DISP_FREQ_AUTO) {
+ if (gdc_video_num_lines >= 30 || (inb(0x9a8) & 0x01)) {
+ gdc_disp_freq = DISP_FREQ_31k;
+ }
+ }
+
+ if (PC9800_9821_P() && gdc_disp_freq == DISP_FREQ_31k) {
+ outb_p(0x01, 0x9a8); /* 31.47KHz */
+ outb_p(0x0e, GDC_COMMAND); /* SYNC, DE deny */
+ outb_p(0x00, GDC_PARAM); /* CHR, F, I, D, G, S = 0 */
+ outb_p(0x4e, GDC_PARAM); /* C/R = 78 (80 chars) */
+ outb_p(0x4b, GDC_PARAM); /* VSL = 2(3) ; HS = 11 */
+ outb_p(0x0c, GDC_PARAM); /* HFP = 3 ; VSH = 0(VS=2) */
+ outb_p(0x03, GDC_PARAM); /* DS, PH = 0 ; HBP = 3 */
+ outb_p(0x06, GDC_PARAM); /* VH, VL = 0 ; VFP = 6 */
+ outb_p(hdots & 0xff, GDC_PARAM); /* LFL */
+ outb_p(0x94 | ((hdots >> 8) & 0x03), GDC_PARAM);
+ /* VBP = 37 ; LFH */
+ outb_p(0x47, GDC_COMMAND); /* PITCH */
+ outb_p(0x50, GDC_PARAM);
+
+ outb_p(0x70, GDC_COMMAND); /* SCROLL */
+ outb_p(0x00, GDC_PARAM);
+ outb_p(0x00, GDC_PARAM);
+ outb_p((hdots << 4) & 0xf0, GDC_PARAM); /* SL1=592 (0x250) */
+ outb_p((hdots >> 4) & 0x3f, GDC_PARAM);
+
+ outb_p(0x0e, GGDC_COMMAND); /* SYNC, DE deny */
+ outb_p(0x00, GGDC_PARAM); /* CHR, F, I, D, G, S = 0 */
+ outb_p(0x4e, GGDC_PARAM); /* C/R = 78 (80 chars) */
+ outb_p(0x4b, GGDC_PARAM); /* VSL = 2(3) ; HS = 11 */
+ outb_p(0x0c, GGDC_PARAM); /* HFP = 3 ; VSH = 0(VS=2) */
+ outb_p(0x03, GGDC_PARAM); /* DS, PH = 0 ; HBP = 3 */
+ outb_p(0x06, GGDC_PARAM); /* VH, VL = 0 ; VFP = 6 */
+ outb_p(hdots & 0xff, GGDC_PARAM); /* LFL */
+ outb_p(0x94 | ((hdots >> 8) & 0x03), GGDC_PARAM);
+ /* VBP = 37 ; LFH */
+ } else {
+ outb_p(0x00, 0x9a8); /* 24.83 KHz */
+ outb_p(0x0e, GDC_COMMAND); /* SYNC, DE deny */
+ outb_p(0x00, GDC_PARAM); /* CHR, F, I, D, G, S = 0 */
+ outb_p(0x4e, GDC_PARAM); /* C/R = 78 (80 chars) */
+ outb_p(0x07, GDC_PARAM); /* VSL = 0(3) ; HS = 7 */
+ outb_p(0x25, GDC_PARAM); /* HFP = 9 ; VSH = 1(VS=8) */
+ outb_p(0x07, GDC_PARAM); /* DS, PH = 0 ; HBP = 7 */
+ outb_p(0x07, GDC_PARAM); /* VH, VL = 0 ; VFP = 7 */
+ outb_p(hdots & 0xff, GDC_PARAM); /* LFL */
+ outb_p(0x64 | ((hdots >> 8) & 0x03), GDC_PARAM);
+ /* VBP = 25 ; LFH */
+ outb_p(0x47, GDC_COMMAND); /* PITCH */
+ outb_p(0x50, GDC_PARAM);
+
+ outb_p(0x70, GDC_COMMAND); /* SCROLL */
+ outb_p(0x00, GDC_PARAM);
+ outb_p(0x00, GDC_PARAM);
+ outb_p((hdots << 4) & 0xf0, GDC_PARAM); /* SL1=592 (0x250) */
+ outb_p((hdots >> 4) & 0x3f, GDC_PARAM);
+
+ outb_p(0x0e, GGDC_COMMAND); /* SYNC */
+ outb_p(0x00, GGDC_PARAM);
+ outb_p(0x4e, GGDC_PARAM);
+ outb_p(0x07, GGDC_PARAM);
+ outb_p(0x25, GGDC_PARAM);
+ outb_p(0x07, GGDC_PARAM);
+ outb_p(0x07, GGDC_PARAM);
+ outb_p(hdots & 0xff, GGDC_PARAM); /* LFL */
+ outb_p(0x64 | ((hdots >> 8) & 0x03), GGDC_PARAM);
+ /* VBP = 25 ; LFH */
+ }
+
+ outb_p(0x47, GGDC_COMMAND); /* PITCH */ + outb_p(0x28, GGDC_PARAM);
+
+ outb_p(0x0d, GDC_COMMAND); /* START */
+ outb_p(0x0d, GGDC_COMMAND); /* START */
+ spin_unlock_irq(&gdc_lock);
+
+ gdc_vram_base = (unsigned long)phys_to_virt(0xa0000);
+ /* Last few bytes of text VRAM area are for NVRAM. */
+ gdc_vram_end = gdc_vram_base + 0x1fe0;
+
+ if (!PC9800_HIGHRESO_P()) {
+ gdc_video_type = VIDEO_TYPE_98NORMAL;
+ display_desc = "NEC PC-9800 Normal";
+ } else {
+ gdc_video_type = VIDEO_TYPE_98HIRESO;
+ display_desc = "NEC PC-9800 High Resolution";
+ }
+
+ gdc_hardscroll_enabled = gdc_hardscroll_user_enable;
+
+ for (i = 0; i < GDC_CONSOLE_RESOURCES; i++)
+ request_resource(&ioport_resource, gdc_console_resources + i);
+
+ return display_desc;
+}
+
+static void gdccon_init(struct vc_data *c, int init)
+{
+ unsigned long p;
+
+ /* We cannot be loaded as a module, therefore init is always 1 */
+ c->vc_can_do_color = gdc_can_do_color;
+ c->vc_cols = gdc_video_num_columns;
+ c->vc_rows = gdc_video_num_lines;
+ c->vc_complement_mask = ATTR_REVERSE << 8;
+ p = *c->vc_uni_pagedir_loc;
+ if (c->vc_uni_pagedir_loc == &c->vc_uni_pagedir
+ || !--c->vc_uni_pagedir_loc[1])
+ con_free_unimap(c->vc_num);
+
+ c->vc_uni_pagedir_loc = gdccon_uni_pagedir;
+ gdccon_uni_pagedir[1]++;
+ if (!gdccon_uni_pagedir[0] && p)
+ con_set_default_unimap(c->vc_num);
+}
+
+static inline void gdc_set_mem_top(struct vc_data *c)
+{
+ unsigned long flags;
+ unsigned long origin = (c->vc_visible_origin - gdc_vram_base) / 2;
+
+ spin_lock_irqsave(&gdc_lock, flags);
+ while (!(inb_p(GDC_STAT) & GDC_FIFO_EMPTY));
+ __gdc_write_command(0x70); /* SCROLL */
+ __gdc_write_param(origin); /* SAD1 (L) */
+ __gdc_write_param((origin >> 8) & 0x1f); /* SAD1 (H) */
+ spin_unlock_irqrestore(&gdc_lock, flags);
+}
+
+static void gdccon_deinit(struct vc_data *c)
+{
+ /* When closing the last console, reset video origin */
+ if (!--gdccon_uni_pagedir[1]) {
+ c->vc_visible_origin = gdc_vram_base;
+ gdc_set_mem_top(c);
+ con_free_unimap(c->vc_num);
+ }
+
+ c->vc_uni_pagedir_loc = &c->vc_uni_pagedir;
+ con_set_default_unimap(c->vc_num);
+}
+
+#if 0
+/* Translate ANSI terminal color code to GDC color code. */
+#define BGR_TO_GRB(bgr) ((((bgr) & 4) >> 2) | (((bgr) & 3) << 1))
+#else
+#define RGB_TO_GRB(rgb) ((((rgb) & 4) >> 1) | (((rgb) & 2) << 1) | ((rgb) & 1))
+#endif
+
+static const u8 gdccon_color_table[] = {
+#define C(color) ((RGB_TO_GRB (color) << 5) | ATTR_NOSECRET)
+ C(0), C(1), C(2), C(3), C(4), C(5), C(6), C(7)
+#undef C
+};
+
+static u8 gdccon_build_attr(struct vc_data *c, u8 color, u8 intensity, u8 blink, u8 underline, u8 reverse)
+{
+ u8 attr = gdccon_color_table[color & 0x07];
+
+ if (!gdc_can_do_color)
+ attr = (intensity == 0 ? 0x61
+ : intensity == 2 ? 0xe1 : 0xa1);
+
+ if (underline)
+ attr |= 0x08;
+
+ /* ignore intensity */
+#if 0
+ if(intensity == 0)
+ ;
+ else if (intensity == 2)
+ attr |= 0x10; /* virtical line */
+#else
+ if (intensity == 0) {
+ if (attr == c->vc_def_attr)
+ attr = c->vc_half_attr;
+ else
+ attr |= c->vc_half_attr & AMASK_NOCOLOR;
+ } else if (intensity == 2) {
+ if (attr == c->vc_def_attr)
+ attr = c->vc_bold_attr;
+ else
+ attr |= c->vc_bold_attr & AMASK_NOCOLOR;
+ }
+#endif
+ if (reverse)
+ attr |= ATTR_REVERSE;
+
+ if ((color & 0x07) == 0) { /* foreground color == black */
+ /* Fake background color by reversed character
+ as GDC cannot set background color. */
+ attr |= gdccon_color_table[(color >> 4) & 0x07];
+ attr ^= ATTR_REVERSE;
+ }
+
+ if (blink)
+ attr |= ATTR_BLINK;
+
+ return attr;
+}
+
+static void gdccon_invert_region(struct vc_data *c, u16 *p, int count)
+{
+ while (count--) {
+ *((u16 *)(gdc_attr_offset(p))) ^= ATTR_REVERSE;
+ p++;
+ }
+}
+
+static u8 gdc_csrform_lr = 15; /* Lines/Row */
+static u16 gdc_csrform_bl_bd = ((12 << 6) /* BLinking Rate */
+ | (0 << 5)); /* Blinking Disable */
+
+static inline void gdc_hide_cursor(void)
+{
+ __gdc_write_command(0x4b); /* CSRFORM */
+ __gdc_write_param(gdc_csrform_lr); /* CS = 0, CE = 0, L/R = ? */
+}
+
+static inline void gdc_show_cursor(int cursor_start, int cursor_finish)
+{
+ __gdc_write_command(0x4b); /* CSRFORM */
+ __gdc_write_param(0x80 | gdc_csrform_lr); /* CS = 1 */
+ __gdc_write_param(cursor_start | gdc_csrform_bl_bd);
+ __gdc_write_param((cursor_finish << 3) | (gdc_csrform_bl_bd >> 8));
+}
+
+static void gdccon_cursor(struct vc_data *c, int mode)
+{
+ unsigned long flags;
+ u16 ead;
+
+ if (c->vc_origin != c->vc_visible_origin)
+ gdccon_scrolldelta(c, 0);
+
+ spin_lock_irqsave(&gdc_lock, flags);
+ while (!(inb_p(GDC_STAT) & GDC_FIFO_EMPTY));
+ spin_unlock_irqrestore(&gdc_lock, flags);
+ switch (mode) {
+ case CM_ERASE:
+ gdc_hide_cursor();
+ break;
+
+ case CM_MOVE:
+ case CM_DRAW:
+ switch (c->vc_cursor_type & 0x0f) {
+ case CUR_UNDERLINE:
+ gdc_show_cursor(14, 15); /* XXX font height */
+ break;
+
+ case CUR_TWO_THIRDS:
+ gdc_show_cursor(5, 15); /* XXX */
+ break;
+
+ case CUR_LOWER_THIRD:
+ gdc_show_cursor(11, 15); /* XXX */
+ break;
+
+ case CUR_LOWER_HALF:
+ gdc_show_cursor(8, 15); /* XXX */
+ break;
+
+ case CUR_NONE:
+ gdc_hide_cursor();
+ break;
+
+ default:
+ gdc_show_cursor(0, 15); /* XXX */
+ break;
+ }
+
+ spin_lock_irqsave(&gdc_lock, flags);
+ __gdc_write_command(0x49); /* CSRW */
+ ead = (c->vc_pos - gdc_vram_base) >> 1;
+ __gdc_write_param(ead);
+ __gdc_write_param((ead >> 8) & 0x1f);
+ spin_unlock_irqrestore(&gdc_lock, flags);
+ break;
+ }
+
+}
+
+static int gdccon_switch(struct vc_data *c)
+{
+ /*
+ * We need to save screen size here as it's the only way
+ * we can spot the screen has been resized and we need to
+ * set size of freshly allocated screens ourselves.
+ */
+ gdc_video_num_columns = c->vc_cols;
+ gdc_video_num_lines = c->vc_rows;
+ if (c->vc_origin != (unsigned long)c->vc_screenbuf
+ && gdc_vram_base <= c->vc_origin && c->vc_origin < gdc_vram_end) {
+ _scr_memcpyw_to((u16 *)c->vc_origin,
+ (u16 *)c->vc_screenbuf,
+ c->vc_screenbuf_size);
+ _scr_memcpyw_to((u16 *)gdc_attr_offset(c->vc_origin),
+ (u16 *)((char *)c->vc_screenbuf
+ + c->vc_screenbuf_size),
+ c->vc_screenbuf_size);
+ } else
+ printk(KERN_WARNING
+ "gdccon: switch (vc #%d) called on origin=%lx\n",
+ c->vc_num, c->vc_origin);
+
+ return 0; /* Redrawing not needed */
+}
+
+static int gdccon_set_palette(struct vc_data *c, unsigned char *table)
+{
+ return -EINVAL;
+}
+
+#define RELAY0 0x01
+#define RELAY0_GDC 0x00
+#define RELAY0_ACCEL 0x01
+#define RELAY1 0x02
+#define RELAY1_INTERNAL 0x00
+#define RELAY1_EXTERNAL 0x02
+#define IO_RELAY 0x0fac
+#define IO_DPMS 0x09a2
+static unsigned char relay_mode = RELAY0_GDC | RELAY1_INTERNAL;
+
+static void gdc_vesa_blank(int mode)
+{
+ unsigned char stat;
+
+ spin_lock_irq(&gdc_lock);
+
+ relay_mode = inb_p(IO_RELAY);
+ if ((relay_mode & (RELAY0 | RELAY1)) != (RELAY0_GDC | RELAY1_INTERNAL)) {
+#ifdef CONFIG_DONTTOUCHRELAY
+ spin_unlock_irq(&gdc_lock);
+ return;
+#else
+ outb_p((relay_mode & ~(RELAY0 | RELAY1)) |
+ RELAY0_GDC | RELAY1_INTERNAL , IO_RELAY);
+#endif
+ }
+
+ if (mode & VESA_VSYNC_SUSPEND) {
+ stat = inb_p(IO_DPMS);
+ outb_p(stat | 0x80, IO_DPMS);
+ }
+ if (mode & VESA_HSYNC_SUSPEND) {
+ stat = inb_p(IO_DPMS);
+ outb_p(stat | 0x40, IO_DPMS);
+ }
+
+ spin_unlock_irq(&gdc_lock);
+}
+
+static void gdc_vesa_unblank(void)
+{
+ unsigned char stat;
+
+#ifdef CONFIG_DONTTOUCHRELAY
+ if (relay_mode & (RELAY0 | RELAY1))
+ return;
+#endif
+
+ spin_lock_irq(&gdc_lock);
+
+ stat = inb_p(0x09a2);
+ outb_p(stat & ~0xc0, IO_DPMS);
+ if (relay_mode & (RELAY0 | RELAY1))
+ outb_p(relay_mode, IO_RELAY);
+
+ spin_unlock_irq(&gdc_lock);
+}
+
+static int gdccon_blank(struct vc_data *c, int blank)
+{
+ switch (blank) {
+ case 0: /* Unblank */
+ if (gdc_vesa_blanked) {
+ gdc_vesa_unblank();
+ gdc_vesa_blanked = 0;
+ }
+
+ outb(MODE_FF1_DISP_ENABLE | 1, MODE_FF1);
+
+ /* Tell console.c that it need not to restore the screen */
+ return 0;
+
+ case 1: /* Normal blanking */
+ /* Disable displaying */
+ outb(MODE_FF1_DISP_ENABLE | 0, MODE_FF1);
+
+ /* Tell console.c that it need not to reset origin */
+ return 0;
+
+ case -1: /* Entering graphic mode */
+ return 1;
+
+ default: /* VESA blanking */
+ if (gdc_video_type == VIDEO_TYPE_98NORMAL
+ || gdc_video_type == VIDEO_TYPE_9840
+ || gdc_video_type == VIDEO_TYPE_98HIRESO) {
+ gdc_vesa_blank(blank - 1);
+ gdc_vesa_blanked = blank;
+ }
+
+ return 0;
+ }
+}
+
+static int gdccon_font_op(struct vc_data *c, struct console_font_op *op)
+{
+ return -ENOSYS;
+}
+
+static int gdccon_scrolldelta(struct vc_data *c, int lines)
+{
+ if (!lines) /* Turn scrollback off */
+ c->vc_visible_origin = c->vc_origin;
+ else {
+ int vram_size = gdc_vram_end - gdc_vram_base;
+ int margin = c->vc_size_row /* * 4 */;
+ int ul, we, p, st;
+
+ if (gdc_rolled_over > c->vc_scr_end - gdc_vram_base + margin) {
+ ul = c->vc_scr_end - gdc_vram_base;
+ we = gdc_rolled_over + c->vc_size_row;
+ } else {
+ ul = 0;
+ we = vram_size;
+ }
+
+ p = (c->vc_visible_origin - gdc_vram_base - ul + we)
+ % we + lines * c->vc_size_row;
+ st = (c->vc_origin - gdc_vram_base - ul + we) % we;
+ if (p < margin)
+ p = 0;
+
+ if (p > st - margin)
+ p = st;
+ c->vc_visible_origin = gdc_vram_base + (p + ul) % we;
+ }
+
+ gdc_set_mem_top(c);
+ return 1;
+}
+
+static int gdccon_set_origin(struct vc_data *c)
+{
+ c->vc_origin = c->vc_visible_origin = gdc_vram_base;
+ gdc_set_mem_top(c);
+ gdc_rolled_over = 0;
+ return 1;
+}
+
+static void gdccon_save_screen(struct vc_data *c)
+{
+ static int gdc_bootup_console = 0;
+
+ if (!gdc_bootup_console) {
+ /* This is a gross hack, but here is the only place we can
+ * set bootup console parameters without messing up generic
+ * console initialization routines.
+ */
+ gdc_bootup_console = 1;
+ c->vc_x = ORIG_X;
+ c->vc_y = ORIG_Y;
+ }
+
+ _scr_memcpyw_from((u16 *)c->vc_screenbuf,
+ (u16 *)c->vc_origin, c->vc_screenbuf_size);
+ _scr_memcpyw_from((u16 *)((char *)c->vc_screenbuf + c->vc_screenbuf_size), (u16 *)gdc_attr_offset(c->vc_origin), c->vc_screenbuf_size);
+}
+
+static int gdccon_scroll(struct vc_data *c, int t, int b, int dir, int lines)
+{
+ unsigned long oldo;
+ unsigned int delta;
+
+ if (t || b != c->vc_rows)
+ return 0;
+
+ if (c->vc_origin != c->vc_visible_origin)
+ gdccon_scrolldelta(c, 0);
+
+ if (!gdc_hardscroll_enabled || lines >= c->vc_rows / 2)
+ return 0;
+
+ oldo = c->vc_origin;
+ delta = lines * c->vc_size_row;
+ if (dir == SM_UP) {
+ if (c->vc_scr_end + delta >= gdc_vram_end) {
+ _scr_memcpyw((u16 *)gdc_vram_base,
+ (u16 *)(oldo + delta),
+ c->vc_screenbuf_size - delta);
+ _scr_memcpyw((u16 *)gdc_attr_offset(gdc_vram_base),
+ (u16 *)gdc_attr_offset(oldo + delta),
+ c->vc_screenbuf_size - delta);
+ c->vc_origin = gdc_vram_base;
+ gdc_rolled_over = oldo - gdc_vram_base;
+ } else
+ c->vc_origin += delta;
+
+ _scr_memsetw((u16 *)(c->vc_origin + c->vc_screenbuf_size - delta), c->vc_video_erase_char & 0xff, delta);
+ _scr_memsetw((u16 *)gdc_attr_offset(c->vc_origin + c->vc_screenbuf_size - delta), c->vc_video_erase_char >> 8, delta);
+ } else {
+ if (oldo - delta < gdc_vram_base) {
+ _scr_memmovew((u16 *)(gdc_vram_end - c->vc_screenbuf_size + delta), (u16 *)oldo, c->vc_screenbuf_size - delta);
+ _scr_memmovew((u16 *)gdc_attr_offset(gdc_vram_end - c->vc_screenbuf_size + delta), (u16 *)gdc_attr_offset(oldo), c->vc_screenbuf_size -
delta);
+ c->vc_origin = gdc_vram_end - c->vc_screenbuf_size;
+ gdc_rolled_over = 0;
+ } else
+ c->vc_origin -= delta;
+
+ c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
+ _scr_memsetw((u16 *)(c->vc_origin), c->vc_video_erase_char & 0xff, delta);
+ _scr_memsetw((u16 *)gdc_attr_offset(c->vc_origin), c->vc_video_erase_char >> 8, delta);
+ }
+
+ c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
+ c->vc_visible_origin = c->vc_origin;
+ gdc_set_mem_top(c);
+ c->vc_pos = (c->vc_pos - oldo) + c->vc_origin;
+ return 1;
+}
+
+static int gdccon_setterm_command(struct vc_data *c)
+{
+ switch (c->vc_par[0]) {
+ case 1: /* set attr for underline mode */
+ if (c->vc_npar < 2) {
+ if (c->vc_par[1] < 16)
+ c->vc_ul_attr = gdccon_color_table[color_table[c->vc_par[1]] & 7];
+ } else {
+ if (c->vc_par[2] < 256)
+ c->vc_ul_attr = c->vc_par[2];
+ }
+
+ if (c->vc_underline)
+ goto update_attr;
+
+ return 1;
+
+ case 2: /* set attr for half intensity mode */
+ if (c->vc_npar < 2) {
+ if (c->vc_par[1] < 16)
+ c->vc_half_attr = gdccon_color_table[color_table[c->vc_par[1]] & 7];
+ }
+ else {
+ if (c->vc_par[2] < 256)
+ c->vc_half_attr = c->vc_par[2];
+ }
+
+ if (c->vc_intensity == 0)
+ goto update_attr;
+
+ return 1;
+
+ case 3: /* set color for bold mode */
+ if (c->vc_npar < 2) {
+ if (c->vc_par[1] < 16)
+ c->vc_bold_attr = gdccon_color_table[color_table[c->vc_par[1]] & 7];
+ } else {
+ if (c->vc_par[2] < 256)
+ c->vc_bold_attr = c->vc_par[2];
+ }
+
+ if (c->vc_intensity == 2)
+ goto update_attr;
+
+ return 1;
+ }
+
+ return 0;
+
+update_attr:
+ c->vc_attr = gdccon_build_attr(c,
+ c->vc_color, c->vc_intensity,
+ c->vc_blink, c->vc_underline,
+ c->vc_reverse);
+ return 1;
+}
+
+/*
+ * The console `switch' structure for the GDC based console
+ */
+
+static int gdccon_dummy(struct vc_data *c)
+{
+ return 0;
+}
+
+#define DUMMY (void *) gdccon_dummy
+
+const struct consw gdc_con = {
+ .con_startup = gdccon_startup,
+ .con_init = gdccon_init,
+ .con_deinit = gdccon_deinit,
+ .con_clear = DUMMY,
+ .con_putc = DUMMY,
+ .con_putcs = DUMMY,
+ .con_cursor = gdccon_cursor,
+ .con_scroll = gdccon_scroll,
+ .con_bmove = DUMMY,
+ .con_switch = gdccon_switch,
+ .con_blank = gdccon_blank,
+ .con_font_op = gdccon_font_op,
+ .con_set_palette = gdccon_set_palette,
+ .con_scrolldelta = gdccon_scrolldelta,
+ .con_set_origin = gdccon_set_origin,
+ .con_save_screen = gdccon_save_screen,
+ .con_build_attr = gdccon_build_attr,
+ .con_invert_region = gdccon_invert_region,
+ .con_setterm_command = gdccon_setterm_command
+};
+
+static int __init gdc_setup(char *str)
+{
+ unsigned long tmp_ulong;
+ char *opt, *orig_opt, *endp;
+
+ while ((opt = strsep(&str, ",")) != NULL) {
+ int force = 0;
+
+ orig_opt = opt;
+ if (!strncmp(opt, "force", 5)) {
+ force = 1;
+ opt += 5;
+ }
+
+ if (!strcmp(opt, "mono"))
+ gdc_can_do_color = 0;
+ else if ((tmp_ulong = simple_strtoul(opt, &endp, 0)) > 0) {
+ if (!strcmp(endp, "lines")
+ || (!strcmp(endp, "linesforce") && (force == 1))) {
+ if (!force
+ && (tmp_ulong < 20
+ || (!PC9800_9821_P()
+ && 25 < tmp_ulong)
+ || 37 < tmp_ulong))
+ printk(KERN_ERR
+ "gdccon: %d is out of bound"
+ " for number of lines\n",
+ (int)tmp_ulong);
+ else
+ gdc_video_num_lines = tmp_ulong;
+ } else if (!strcmp(endp, "kHz")) {
+ if (tmp_ulong == 24 || tmp_ulong == 25)
+ gdc_disp_freq = DISP_FREQ_25k;
+ else
+ printk(KERN_ERR "gdccon: `%s' ignored\n",
+ orig_opt);
+ } else
+ printk(KERN_ERR "gdccon: unknown option `%s'\n",
+ orig_opt);
+ } else
+ printk(KERN_ERR "gdccon: unknown option `%s'\n",
+ orig_opt);
+ }
+
+ return 1; +}
+
+__setup("gdccon=", gdc_setup);
+
+/*
+ * We will follow Linus's indenting style...
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -urN linux/include/asm-i386/gdc.h linux98/include/asm-i386/gdc.h
--- linux/include/asm-i386/gdc.h Thu Jan 1 09:00:00 1970
+++ linux98/include/asm-i386/gdc.h Mon Oct 28 21:44:40 2002
@@ -0,0 +1,217 @@
+/*
+ * gdc.h - macro & inline functions for accessing GDC text-VRAM
+ *
+ * Copyright (C) 1997-2002 Osamu Tomita <***@cinet.co.jp>
+ * KITAGAWA Takurou,
+ * UGAWA Tomoharu,
+ * TAKAI Kosuke
+ * (Linux/98 Project)
+ */
+#ifndef _LINUX_ASM_GDC_H_
+#define _LINUX_ASM_GDC_H_
+
+#include <linux/config.h>
+
+#define PC9800_VRAM_ATTR_OFFSET 0x2000
+
+#define GDC_MAP_MEM(x) (unsigned long)phys_to_virt(x)
+
+#define gdc_readb(x) (*(x))
+#define gdc_writeb(x,y) (*(y) = (x))
+
+extern int fbcon_softback_size;
+
+#ifdef CONFIG_FB_EGC
+#define pc9800_attr_offset(p) \
+ ((u16 *)((u32)(p) + \
+ (((u32)(p) >= (u32)(vc_cons[currcons].d->vc_screenbuf) \
+ && (u32)(p) < (u32)(vc_cons[currcons].d->vc_screenbuf) \
+ + vc_cons[currcons].d->vc_screenbuf_size) ? \
+ vc_cons[currcons].d->vc_screenbuf_size : fbcon_softback_size)))
+#else
+#define pc9800_attr_offset(p) \
+ ((u16 *)((u32)(p) + \
+ (((u32)(p) >= (u32)(__va(0xa0000)) \
+ && (u32)(p) < (u32)(__va(0xa2000))) ? \
+ 0x2000 : vc_cons[currcons].d->vc_screenbuf_size)))
+#endif
+
+#define VT_BUF_HAVE_RW
+#define scr_writew(val, p) \
+ { \
+ *((u16 *)(p)) = (u16)(((val) >> 16) & 0xff00) \
+ | (u16)((val) & 0xff); \
+ *(pc9800_attr_offset(p)) = (u16)((val) >> 8); \
+ }
+
+#define scr_readw(p) \
+ ( \
+ (*((u16 *)(p)) & 0xff) | ((*((u16 *)(p)) & 0xff00) << 16) \
+ | ((*(pc9800_attr_offset(p)) & 0xff) << 8) \
+ )
+
+#define VT_BUF_HAVE_MEMSETW
+extern inline void
+_scr_memsetw(u16 *s, u16 c, unsigned int count)
+{
+#ifdef CONFIG_GDC_32BITACCESS
+ __asm__ __volatile__ ("shr%L1 %1
+ jz 2f
+" /* cld kernel code now assumes DF = 0 any time */ "\
+ test%L0 %3,%0
+ jz 1f
+ stos%W2
+ dec%L1 %1
+1: shr%L1 %1
+ rep
+ stos%L2
+ jnc 2f
+ stos%W2
+ rep
+ stos%W2
+2:"
+ : "=D"(s), "=c"(count)
+ : "a"((((u32) c) << 16) | c), "g"(2),
+ "0"(s), "1"(count));
+#else
+ __asm__ __volatile__ ("rep\n\tstosw"
+ : "=D"(s), "=c"(count)
+ : "0"(s), "1"(count / 2), "a"(c));
+#endif
+}
+
+#define scr_memsetw(s, c, count) \
+ { \
+ _scr_memsetw((s), (u16)(((c) >> 16) & 0xff00) | (u16)((c) & 0xff), \
+ (count)); \
+ _scr_memsetw(pc9800_attr_offset(s), ((u16)(c)) >> 8, (count)); \
+ }
+
+#define VT_BUF_HAVE_MEMCPYW
+extern inline void
+_scr_memcpyw(u16 *d, u16 *s, unsigned int count)
+{
+#if 1 /* def CONFIG_GDC_32BITACCESS */
+ __asm__ __volatile__ ("shr%L2 %2
+ jz 2f
+" /* cld */ "\
+ test%L0 %3,%0
+ jz 1f
+ movs%W0
+ dec%L2 %2
+1: shr%L2 %2
+ rep
+ movs%L0
+ jnc 2f
+ movs%W0
+2:"
+ : "=D"(d), "=S"(s), "=c"(count)
+ : "g"(2), "0"(d), "1"(s), "2"(count));
+#else
+ __asm__ __volatile__ ("rep\n\tmovsw"
+ : "=D"(d), "=S"(s), "=c"(count)
+ : "0"(d), "1"(s), "2"(count / 2));
+#endif
+}
+
+#define scr_memcpyw(d, s, count) \
+ { \
+ _scr_memcpyw((d), (s), (count)); \
+ _scr_memcpyw(pc9800_attr_offset(d), pc9800_attr_offset(s), (count)); \
+ }
+
+extern inline void
+_scr_memrcpyw(u16 *d, u16 *s, unsigned int count)
+{
+#if 1 /* def CONFIG_GDC_32BITACCESS */
+ u16 tmp;
+
+ __asm__ __volatile__ ("shr%L3 %3
+ jz 2f
+ std
+ lea%L1 -4(%1,%3,2),%1
+ lea%L2 -4(%2,%3,2),%2
+ test%L1 %4,%1
+ jz 1f
+ mov%W0 2(%2),%0
+ sub%L2 %4,%2
+ dec%L3 %3
+ mov%W0 %0,2(%1)
+ sub%L1 %4,%1
+1: shr%L3 %3
+ rep
+ movs%L0
+ jnc 3f
+ mov%W0 2(%2),%0
+ mov%W0 %0,2(%1)
+3: cld
+2:"
+ : "=r"(tmp), "=D"(d), "=S"(s), "=c"(count)
+ : "g"(2), "1"(d), "2"(s), "3"(count));
+#else
+ __asm__ __volatile__ ("std\n\trep\n\tmovsw\n\tcld"
+ : "=D"(d), "=S"(s), "=c"(count)
+ : "0"((void *) d + count - 2),
+ "1"((void *) s + count - 2), "2"(count / 2));
+#endif
+}
+
+#define VT_BUF_HAVE_MEMMOVEW
+extern inline void
+_scr_memmovew(u16 *d, u16 *s, unsigned int count)
+{
+ if (d > s)
+ _scr_memrcpyw(d, s, count);
+ else
+ _scr_memcpyw(d, s, count);
+}
+
+#define scr_memmovew(d, s, count) \
+ { \
+ _scr_memmovew((d), (s), (count)); \
+ _scr_memmovew(pc9800_attr_offset(d), pc9800_attr_offset(s), (count)); \
+ }
+
+#define VT_BUF_HAVE_MEMCPYF
+extern inline void
+_scr_memcpyw_from(u16 *d, u16 *s, unsigned int count)
+{
+#ifdef CONFIG_GDC_32BITACCESS
+ /* VRAM is quite slow, so we align source pointer (%esi)
+ to double-word alignment. */
+ __asm__ __volatile__ ("shr%L2 %2
+ jz 2f
+" /* cld */ "\
+ test%L0 %3,%0
+ jz 1f
+ movs%W0
+ dec%L2 %2
+1: shr%L2 %2
+ rep
+ movs%L0
+ jnc 2f
+ movs%W0
+2:"
+ : "=D"(d), "=S"(s), "=c"(count)
+ : "g"(2), "0"(d), "1"(s), "2"(count));
+#else
+ __asm__ __volatile__ ("rep\n\tmovsw"
+ : "=D"(d), "=S"(s), "=c"(count)
+ : "0"(d), "1"(s), "2"(count / 2));
+#endif
+}
+
+#define scr_memcpyw_from(d, s, count) \
+ { \
+ _scr_memcpyw_from((d), (s), (count)); \
+ _scr_memcpyw_from(pc9800_attr_offset(d), pc9800_attr_offset(s), \
+ (count)); \
+ }
+
+#ifdef CONFIG_GDC_32BITACCESS
+# define _scr_memcpyw_to _scr_memcpyw
+#else
+# define _scr_memcpyw_to _scr_memcpyw_from
+#endif
+
+#endif /* _LINUX_ASM_GDC_H_ */
Osamu Tomita
2002-11-02 17:55:07 UTC
Permalink
This is a part 6/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

floppy98.c is splited to 2 patches. Please apply this before floppy2.

Summary:
floppy driver modules
- add PC-9800 standard FDD support.
based on floppy.c, enhanced media check and auto detect.

diffstat:
drivers/block/Makefile | 4 drivers/block/floppy98.c | 2337 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 2341 insertions(+)

patch:
diff -urN linux/drivers/block/Makefile linux98/drivers/block/Makefile
--- linux/drivers/block/Makefile Wed Oct 16 13:20:33 2002
+++ linux98/drivers/block/Makefile Wed Oct 16 14:56:42 2002
@@ -14,7 +14,11 @@
obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o deadline-iosched.o
obj-$(CONFIG_MAC_FLOPPY) += swim3.o
+ifneq ($(CONFIG_PC9800),y)
obj-$(CONFIG_BLK_DEV_FD) += floppy.o
+else
+obj-$(CONFIG_BLK_DEV_FD) += floppy98.o
+endif
obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o
obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o
obj-$(CONFIG_BLK_DEV_SWIM_IOP) += swim_iop.o
diff -urN linux/drivers/block/floppy98.c linux98/drivers/block/floppy98.c
--- linux/drivers/block/floppy98.c Thu Jan 1 09:00:00 1970
+++ linux98/drivers/block/floppy98.c Thu Oct 31 16:11:27 2002
@@ -0,0 +1,2337 @@
+/*
+ * linux/drivers/block/floppy.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1993, 1994 Alain Knaff
+ * Copyright (C) 1998 Alan Cox
+ */
+/*
+ * 02.12.91 - Changed to static variables to indicate need for reset
+ * and recalibrate. This makes some things easier (output_byte reset
+ * checking etc), and means less interrupt jumping in case of errors,
+ * so the code is hopefully easier to understand.
+ */
+
+/*
+ * This file is certainly a mess. I've tried my best to get it working,
+ * but I don't like programming floppies, and I have only one anyway.
+ * Urgel. I should check for more errors, and do more graceful error
+ * recovery. Seems there are problems with several drives. I've tried to
+ * correct them. No promises.
+ */
+
+/*
+ * As with hd.c, all routines within this file can (and will) be called
+ * by interrupts, so extreme caution is needed. A hardware interrupt
+ * handler may not sleep, or a kernel panic will happen. Thus I cannot
+ * call "floppy-on" directly, but have to set a special timer interrupt
+ * etc.
+ */
+
+/*
+ * 28.02.92 - made track-buffering routines, based on the routines written
+ * by ***@wintermute.wpi.edu (Lawrence Foard). Linus.
+ */
+
+/*
+ * Automatic floppy-detection and formatting written by Werner Almesberger
+ * (***@nessie.cs.id.ethz.ch), who also corrected some problems with
+ * the floppy-change signal detection.
+ */
+
+/*
+ * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed
+ * FDC data overrun bug, added some preliminary stuff for vertical
+ * recording support.
+ *
+ * 1992/9/17: Added DMA allocation & DMA functions. -- hhb.
+ *
+ * TODO: Errors are still not counted properly.
+ */
+
+/* 1992/9/20
+ * Modifications for ``Sector Shifting'' by Rob Hooft (***@chem.ruu.nl)
+ * modeled after the freeware MS-DOS program fdformat/88 V1.8 by
+ * Christoph H. Hochst\"atter.
+ * I have fixed the shift values to the ones I always use. Maybe a new
+ * ioctl() should be created to be able to modify them.
+ * There is a bug in the driver that makes it impossible to format a
+ * floppy as the first thing after bootup.
+ */
+
+/*
+ * 1993/4/29 -- Linus -- cleaned up the timer handling in the kernel, and
+ * this helped the floppy driver as well. Much cleaner, and still seems to
+ * work.
+ */
+
+/* 1994/6/24 --bbroad-- added the floppy table entries and made
+ * minor modifications to allow 2.88 floppies to be run.
+ */
+
+/* 1994/7/13 -- Paul Vojta -- modified the probing code to allow three or more
+ * disk types.
+ */
+
+/*
+ * 1994/8/8 -- Alain Knaff -- Switched to fdpatch driver: Support for bigger
+ * format bug fixes, but unfortunately some new bugs too...
+ */
+
+/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write
+ * errors to allow safe writing by specialized programs.
+ */
+
+/* 1995/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks
+ * by defining bit 1 of the "stretch" parameter to mean put sectors on the
+ * opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's
+ * drives are "upside-down").
+ */
+
+/*
+ * 1995/8/26 -- Andreas Busse -- added Mips support.
+ */
+
+/*
+ * 1995/10/18 -- Ralf Baechle -- Portability cleanup; move machine dependent
+ * features to asm/floppy.h.
+ */
+
+/*
+ * 1998/05/07 -- Russell King -- More portability cleanups; moved definition of
+ * interrupt and dma channel to asm/floppy.h. Cleaned up some formatting &
+ * use of '0' for NULL.
+ */
+ +/*
+ * 1998/06/07 -- Alan Cox -- Merged the 2.0.34 fixes for resource allocation
+ * failures.
+ */
+
+/*
+ * 1998/09/20 -- David Weinehall -- Added slow-down code for buggy PS/2-drives.
+ */
+
+/*
+ * 1999/01/19 -- N.Fujita & Linux/98 Project -- Added code for NEC PC-9800
+ * series.
+ */
+
+/*
+ * 1999/08/13 -- Paul Slootman -- floppy stopped working on Alpha after 24
+ * days, 6 hours, 32 minutes and 32 seconds (i.e. MAXINT jiffies; ints were
+ * being used to store jiffies, which are unsigned longs).
+ */
+
+/*
+ * 2000/08/28 -- Arnaldo Carvalho de Melo <***@conectiva.com.br>
+ * - get rid of check_region
+ * - s/suser/capable/
+ */
+
+/*
+ * 2001/08/26 -- Paul Gortmaker - fix insmod oops on machines with no
+ * floppy controller (lingering task on list after module is gone... boom.)
+ */
+
+/*
+ * 2002/02/07 -- Anton Altaparmakov - Fix io ports reservation to correct range
+ * (0x3f2-0x3f5, 0x3f7). This fix is a bit of a hack but the proper fix
+ * requires many non-obvious changes in arch dependent code.
+ */
+
+/*
+ * 2002/10/12 -- Osamu Tomita <***@cinet.co.jp>
+ * split code from floppy.c
+ * support NEC PC-9800 only
+ */
+
+#define FLOPPY_SANITY_CHECK
+#undef FLOPPY_SILENT_DCL_CLEAR
+
+/*
+#define PC9800_DEBUG_FLOPPY
+#define PC9800_DEBUG_FLOPPY2
+*/
+
+#define REALLY_SLOW_IO
+
+#define DEBUGT 2
+#define DCL_DEBUG /* debug disk change line */
+
+/* do print messages for unexpected interrupts */
+static int print_unex=1;
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#define FDPATCHES
+#include <linux/fdreg.h>
+
+/*
+ * 1998/1/21 -- Richard Gooch <***@atnf.csiro.au> -- devfs support
+ */
+
+
+#include <linux/fd.h>
+#include <linux/hdreg.h>
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/bio.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/mc146818rtc.h> /* CMOS defines */
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/device.h>
+#include <linux/buffer_head.h> /* for invalidate_buffers() */
+
+/*
+ * PS/2 floppies have much slower step rates than regular floppies.
+ * It's been recommended that take about 1/4 of the default speed
+ * in some more extreme cases.
+ */
+static int slow_floppy;
+
+#include <asm/dma.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#ifndef DEFAULT_FLOPPY_IRQ
+# define DEFAULT_FLOPPY_IRQ 11
+#endif
+#ifndef DEFAULT_FLOPPY_DMA
+# define DEFAULT_FLOPPY_DMA 2
+#endif
+
+static int FLOPPY_IRQ=DEFAULT_FLOPPY_IRQ;
+static int FLOPPY_DMA=DEFAULT_FLOPPY_DMA;
+static int can_use_virtual_dma=2;
+static int auto_detect_mode = 0;
+static int retry_auto_detect = 0;
+#define FD_AFTER_RESET_DELAY 1000
+
+/* =======
+ * can use virtual DMA:
+ * 0 = use of virtual DMA disallowed by config
+ * 1 = use of virtual DMA prescribed by config
+ * 2 = no virtual DMA preference configured. By default try hard DMA,
+ * but fall back on virtual DMA when not enough memory available
+ */
+
+static int use_virtual_dma;
+/* =======
+ * use virtual DMA
+ * 0 using hard DMA
+ * 1 using virtual DMA
+ * This variable is set to virtual when a DMA mem problem arises, and
+ * reset back in floppy_grab_irq_and_dma.
+ * It is not safe to reset it in other circumstances, because the floppy
+ * driver may have several buffers in use at once, and we do currently not
+ * record each buffers capabilities
+ */
+
+static spinlock_t floppy_lock = SPIN_LOCK_UNLOCKED;
+
+static unsigned short virtual_dma_port=0x3f0;
+void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs);
+static int set_mode(char mask, char data);
+static void register_devfs_entries (int drive) __init;
+static devfs_handle_t devfs_handle;
+
+#define K_64 0x10000 /* 64KB */
+
+/* the following is the mask of allowed drives. By default units 2 and
+ * 3 of both floppy controllers are disabled, because switching on the
+ * motor of these drives causes system hangs on some PCI computers. drive
+ * 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if
+ * a drive is allowed.
+ *
+ * NOTE: This must come before we include the arch floppy header because
+ * some ports reference this variable from there. -DaveM
+ */
+
+static int allowed_drive_mask = 0x0f;
+
+#include <asm/floppy.h>
+
+static int irqdma_allocated;
+
+#define LOCAL_END_REQUEST
+#define MAJOR_NR FLOPPY_MAJOR
+#define DEVICE_NAME "floppy"
+#define DEVICE_NR(device) ( (minor(device) & 3) | ((minor(device) & 0x80 ) >> 5 ))
+#include <linux/blk.h>
+#include <linux/blkpg.h>
+#include <linux/cdrom.h> /* for the compatibility eject ioctl */
+#include <linux/completion.h>
+
+static struct request *current_req;
+static struct request_queue floppy_queue;
+
+#ifndef fd_get_dma_residue
+#define fd_get_dma_residue() get_dma_residue(FLOPPY_DMA)
+#endif
+
+/* Dma Memory related stuff */
+
+#ifndef fd_dma_mem_free
+#define fd_dma_mem_free(addr, size) free_pages(addr, get_order(size))
+#endif
+
+#ifndef fd_dma_mem_alloc
+#define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,get_order(size))
+#endif
+
+static inline void fallback_on_nodma_alloc(char **addr, size_t l)
+{
+#ifdef FLOPPY_CAN_FALLBACK_ON_NODMA
+ if (*addr)
+ return; /* we have the memory */
+ if (can_use_virtual_dma != 2)
+ return; /* no fallback allowed */
+ printk("DMA memory shortage. Temporarily falling back on virtual DMA\n");
+ *addr = (char *) nodma_mem_alloc(l);
+#else
+ return;
+#endif
+}
+
+/* End dma memory related stuff */
+
+static unsigned long fake_change;
+static int initialising=1;
+
+static inline int TYPE(kdev_t x) {
+ return (minor(x)>>2) & 0x1f;
+}
+static inline int DRIVE(kdev_t x) {
+ return (minor(x)&0x03) | ((minor(x)&0x80) >> 5);
+}
+#define ITYPE(x) (((x)>>2) & 0x1f)
+#define TOMINOR(x) ((x & 3) | ((x & 4) << 5))
+#define UNIT(x) ((x) & 0x03) /* drive on fdc */
+#define FDC(x) (((x) & 0x04) >> 2) /* fdc of drive */
+#define REVDRIVE(fdc, unit) ((unit) + ((fdc) << 2))
+ /* reverse mapping from unit and fdc to drive */
+#define DP (&drive_params[current_drive])
+#define DRS (&drive_state[current_drive])
+#define DRWE (&write_errors[current_drive])
+#define FDCS (&fdc_state[fdc])
+#define CLEARF(x) (clear_bit(x##_BIT, &DRS->flags))
+#define SETF(x) (set_bit(x##_BIT, &DRS->flags))
+#define TESTF(x) (test_bit(x##_BIT, &DRS->flags))
+
+#define UDP (&drive_params[drive])
+#define UDRS (&drive_state[drive])
+#define UDRWE (&write_errors[drive])
+#define UFDCS (&fdc_state[FDC(drive)])
+#define UCLEARF(x) (clear_bit(x##_BIT, &UDRS->flags))
+#define USETF(x) (set_bit(x##_BIT, &UDRS->flags))
+#define UTESTF(x) (test_bit(x##_BIT, &UDRS->flags))
+
+#define DPRINT(format, args...) printk(DEVICE_NAME "%d: " format, current_drive , ## args)
+
+#define PH_HEAD(floppy,head) (((((floppy)->stretch & 2) >>1) ^ head) << 2)
+#define STRETCH(floppy) ((floppy)->stretch & FD_STRETCH)
+
+#define CLEARSTRUCT(x) memset((x), 0, sizeof(*(x)))
+
+/* read/write */
+#define COMMAND raw_cmd->cmd[0]
+#define DR_SELECT raw_cmd->cmd[1]
+#define TRACK raw_cmd->cmd[2]
+#define HEAD raw_cmd->cmd[3]
+#define SECTOR raw_cmd->cmd[4]
+#define SIZECODE raw_cmd->cmd[5]
+#define SECT_PER_TRACK raw_cmd->cmd[6]
+#define GAP raw_cmd->cmd[7]
+#define SIZECODE2 raw_cmd->cmd[8]
+#define NR_RW 9
+
+/* format */
+#define F_SIZECODE raw_cmd->cmd[2]
+#define F_SECT_PER_TRACK raw_cmd->cmd[3]
+#define F_GAP raw_cmd->cmd[4]
+#define F_FILL raw_cmd->cmd[5]
+#define NR_F 6
+
+/*
+ * Maximum disk size (in kilobytes). This default is used whenever the
+ * current disk size is unknown.
+ * [Now it is rather a minimum]
+ */
+#define MAX_DISK_SIZE 4 /* 3984*/
+
+
+/*
+ * globals used by 'result()'
+ */
+#define MAX_REPLIES 16
+static unsigned char reply_buffer[MAX_REPLIES];
+static int inr; /* size of reply buffer, when called from interrupt */
+#define ST0 (reply_buffer[0])
+#define ST1 (reply_buffer[1])
+#define ST2 (reply_buffer[2])
+#define ST3 (reply_buffer[0]) /* result of GETSTATUS */
+#define R_TRACK (reply_buffer[3])
+#define R_HEAD (reply_buffer[4])
+#define R_SECTOR (reply_buffer[5])
+#define R_SIZECODE (reply_buffer[6])
+
+#define SEL_DLY (2*HZ/100)
+
+/*
+ * this struct defines the different floppy drive types.
+ */
+static struct {
+ struct floppy_drive_params params;
+ const char *name; /* name printed while booting */
+} default_drive_params[]= {
+/* NOTE: the time values in jiffies should be in msec!
+ CMOS drive type
+ | Maximum data rate supported by drive type
+ | | Head load time, msec
+ | | | Head unload time, msec (not used)
+ | | | | Step rate interval, usec
+ | | | | | Time needed for spinup time (jiffies)
+ | | | | | | Timeout for spinning down (jiffies)
+ | | | | | | | Spindown offset (where disk stops)
+ | | | | | | | | Select delay
+ | | | | | | | | | RPS
+ | | | | | | | | | | Max number of tracks
+ | | | | | | | | | | | Interrupt timeout
+ | | | | | | | | | | | | Max nonintlv. sectors
+ | | | | | | | | | | | | | -Max Errors- flags */
+{{0, 500, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0,
+ 0, { 7, 4, 8, 2, 1, 5, 3,10}, 3*HZ/2, 0 }, "unknown" },
+
+{{1, 300, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0,
+ 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
+
+{{2, 500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0,
+ 0, { 2, 6, 4, 0, 0, 0, 0, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/
+
+{{3, 250, 16, 16, 3000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
+ 0, { 4, 6, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/
+
+{{4, 500, 16, 16, 4000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
+ 0, { 7,10, 2, 4, 6, 0, 0, 0}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
+
+{{5, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
+ 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
+
+{{6, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
+ 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M" } /*3 1/2 ED*/
+/* | --autodetected formats--- | | |
+ * read_track | | Name printed when booting
+ * | Native format
+ * Frequency of disk change checks */
+};
+
+static struct floppy_drive_params drive_params[N_DRIVE];
+static struct floppy_drive_struct drive_state[N_DRIVE];
+static struct floppy_write_errors write_errors[N_DRIVE];
+static struct timer_list motor_off_timer[N_DRIVE];
+static struct gendisk *disks[N_DRIVE];
+static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;
+
+/*
+ * This struct defines the different floppy types.
+ *
+ * Bit 0 of 'stretch' tells if the tracks need to be doubled for some
+ * types (e.g. 360kB diskette in 1.2MB drive, etc.). Bit 1 of 'stretch'
+ * tells if the disk is in Commodore 1581 format, which means side 0 sectors
+ * are located on side 1 of the disk but with a side 0 ID, and vice-versa.
+ * This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the
+ * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical
+ * side 0 is on physical side 0 (but with the misnamed sector IDs).
+ * 'stretch' should probably be renamed to something more general, like
+ * 'options'. Other parameters should be self-explanatory (see also
+ * setfdprm(8)).
+ */
+/*
+ Size
+ | Sectors per track
+ | | Head
+ | | | Tracks
+ | | | | Stretch
+ | | | | | Gap 1 size
+ | | | | | | Data rate, | 0x40 for perp
+ | | | | | | | Spec1 (stepping rate, head unload
+ | | | | | | | | /fmt gap (gap2) */
+static struct floppy_struct floppy_type[32] = {
+ { 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* 0 no testing */
+#if 0
+ { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360" }, /* 1 360KB PC */
+#else
+ { 2464,16,2,77,0,0x35,0x48,0xDF,0x74,"d360" }, /* 1 1.25MB 98 */
+#endif
+ { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" }, /* 2 1.2MB AT */
+ { 720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360" }, /* 3 360KB SS 3.5" */
+ { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720" }, /* 4 720KB 3.5" */
+ { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360" }, /* 5 360KB AT */
+ { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" }, /* 6 720KB AT */
+ { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" }, /* 7 1.44MB 3.5" */
+ { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" }, /* 8 2.88MB 3.5" */
+ { 6240,39,2,80,0,0x1B,0x43,0xAF,0x28,"E3120" }, /* 9 3.12MB 3.5" */
+
+ { 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 1.44MB 5.25" */
+ { 3360,21,2,80,0,0x1C,0x00,0xCF,0x0C,"H1680" }, /* 11 1.68MB 3.5" */
+ { 820,10,2,41,1,0x25,0x01,0xDF,0x2E,"h410" }, /* 12 410KB 5.25" */
+ { 1640,10,2,82,0,0x25,0x02,0xDF,0x2E,"H820" }, /* 13 820KB 3.5" */
+ { 2952,18,2,82,0,0x25,0x00,0xDF,0x02,"h1476" }, /* 14 1.48MB 5.25" */
+ { 3444,21,2,82,0,0x25,0x00,0xDF,0x0C,"H1722" }, /* 15 1.72MB 3.5" */
+ { 840,10,2,42,1,0x25,0x01,0xDF,0x2E,"h420" }, /* 16 420KB 5.25" */
+ { 1660,10,2,83,0,0x25,0x02,0xDF,0x2E,"H830" }, /* 17 830KB 3.5" */
+ { 2988,18,2,83,0,0x25,0x00,0xDF,0x02,"h1494" }, /* 18 1.49MB 5.25" */
+ { 3486,21,2,83,0,0x25,0x00,0xDF,0x0C,"H1743" }, /* 19 1.74 MB 3.5" */
+
+ { 1760,11,2,80,0,0x1C,0x09,0xCF,0x00,"h880" }, /* 20 880KB 5.25" */
+ { 2080,13,2,80,0,0x1C,0x01,0xCF,0x00,"D1040" }, /* 21 1.04MB 3.5" */
+ { 2240,14,2,80,0,0x1C,0x19,0xCF,0x00,"D1120" }, /* 22 1.12MB 3.5" */
+ { 3200,20,2,80,0,0x1C,0x20,0xCF,0x2C,"h1600" }, /* 23 1.6MB 5.25" */
+ { 3520,22,2,80,0,0x1C,0x08,0xCF,0x2e,"H1760" }, /* 24 1.76MB 3.5" */
+ { 3840,24,2,80,0,0x1C,0x20,0xCF,0x00,"H1920" }, /* 25 1.92MB 3.5" */
+ { 6400,40,2,80,0,0x25,0x5B,0xCF,0x00,"E3200" }, /* 26 3.20MB 3.5" */
+ { 7040,44,2,80,0,0x25,0x5B,0xCF,0x00,"E3520" }, /* 27 3.52MB 3.5" */
+ { 7680,48,2,80,0,0x25,0x63,0xCF,0x00,"E3840" }, /* 28 3.84MB 3.5" */
+
+ { 3680,23,2,80,0,0x1C,0x10,0xCF,0x00,"H1840" }, /* 29 1.84MB 3.5" */
+ { 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"D800" }, /* 30 800KB 3.5" */
+ { 3200,20,2,80,0,0x1C,0x00,0xCF,0x2C,"H1600" }, /* 31 1.6MB 3.5" */
+};
+
+#define NUMBER(x) (sizeof(x) / sizeof(*(x)))
+#define SECTSIZE (_FD_SECTSIZE(*floppy))
+
+/* Auto-detection: Disk type used until the next media change occurs. */
+static struct floppy_struct *current_type[N_DRIVE];
+
+/*
+ * User-provided type information. current_type points to
+ * the respective entry of this array.
+ */
+static struct floppy_struct user_params[N_DRIVE];
+
+static sector_t floppy_sizes[256];
+
+/*
+ * The driver is trying to determine the correct media format
+ * while probing is set. rw_interrupt() clears it after a
+ * successful access.
+ */
+static int probing;
+
+/* Synchronization of FDC access. */
+#define FD_COMMAND_NONE -1
+#define FD_COMMAND_ERROR 2
+#define FD_COMMAND_OKAY 3
+
+static volatile int command_status = FD_COMMAND_NONE;
+static unsigned long fdc_busy;
+static DECLARE_WAIT_QUEUE_HEAD(fdc_wait);
+static DECLARE_WAIT_QUEUE_HEAD(command_done);
+
+#define NO_SIGNAL (!interruptible || !signal_pending(current))
+#define CALL(x) if ((x) == -EINTR) return -EINTR
+#define ECALL(x) if ((ret = (x))) return ret;
+#define _WAIT(x,i) CALL(ret=wait_til_done((x),i))
+#define WAIT(x) _WAIT((x),interruptible)
+#define IWAIT(x) _WAIT((x),1)
+
+/* Errors during formatting are counted here. */
+static int format_errors;
+
+/* Format request descriptor. */
+static struct format_descr format_req;
+
+/*
+ * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps
+ * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
+ * H is head unload time (1=16ms, 2=32ms, etc)
+ */
+
+/*
+ * Track buffer
+ * Because these are written to by the DMA controller, they must
+ * not contain a 64k byte boundary crossing, or data will be
+ * corrupted/lost.
+ */
+static char *floppy_track_buffer;
+static int max_buffer_sectors;
+
+static int *errors;
+typedef void (*done_f)(int);
+static struct cont_t {
+ void (*interrupt)(void); /* this is called after the interrupt of the
+ * main command */
+ void (*redo)(void); /* this is called to retry the operation */
+ void (*error)(void); /* this is called to tally an error */
+ done_f done; /* this is called to say if the operation has + * succeeded/failed */
+} *cont;
+
+static void floppy_ready(void);
+static void floppy_start(void);
+static void process_fd_request(void);
+static void recalibrate_floppy(void);
+static void floppy_shutdown(void);
+
+static int floppy_grab_irq_and_dma(void);
+static void floppy_release_irq_and_dma(void);
+
+/*
+ * The "reset" variable should be tested whenever an interrupt is scheduled,
+ * after the commands have been sent. This is to ensure that the driver doesn't
+ * get wedged when the interrupt doesn't come because of a failed command.
+ * reset doesn't need to be tested before sending commands, because
+ * output_byte is automatically disabled when reset is set.
+ */
+#define CHECK_RESET { if (FDCS->reset){ reset_fdc(); return; } }
+static void reset_fdc(void);
+
+/*
+ * These are global variables, as that's the easiest way to give
+ * information to interrupts. They are the data used for the current
+ * request.
+ */
+#define NO_TRACK -1
+#define NEED_1_RECAL -2
+#define NEED_2_RECAL -3
+
+static int usage_count;
+
+/* buffer related variables */
+static int buffer_track = -1;
+static int buffer_drive = -1;
+static int buffer_min = -1;
+static int buffer_max = -1;
+
+/* fdc related variables, should end up in a struct */
+static struct floppy_fdc_state fdc_state[N_FDC];
+static int fdc; /* current fdc */
+
+static struct floppy_struct *_floppy = floppy_type;
+static unsigned char current_drive;
+static long current_count_sectors;
+static unsigned char fsector_t; /* sector in track */
+static unsigned char in_sector_offset; /* offset within physical sector,
+ * expressed in units of 512 bytes */
+
+#ifndef fd_eject
+static inline int fd_eject(int drive)
+{
+ return -EINVAL;
+}
+#endif
+
+#ifdef DEBUGT
+static long unsigned debugtimer;
+#endif
+
+/*
+ * Debugging
+ * =========
+ */
+static inline void set_debugt(void)
+{
+#ifdef DEBUGT
+ debugtimer = jiffies;
+#endif
+}
+
+static inline void debugt(const char *message)
+{
+#ifdef DEBUGT
+ if (DP->flags & DEBUGT)
+ printk("%s dtime=%lu\n", message, jiffies-debugtimer);
+#endif
+}
+
+typedef void (*timeout_fn)(unsigned long);
+static struct timer_list fd_timeout ={ function: (timeout_fn) floppy_shutdown };
+
+static const char *timeout_message;
+
+#ifdef FLOPPY_SANITY_CHECK
+static void is_alive(const char *message)
+{
+ /* this routine checks whether the floppy driver is "alive" */
+ if (fdc_busy && command_status < 2 && !timer_pending(&fd_timeout)){
+ DPRINT("timeout handler died: %s\n",message);
+ }
+}
+#endif
+
+static void (*do_floppy)(void) = NULL;
+
+#ifdef FLOPPY_SANITY_CHECK
+
+#define OLOGSIZE 20
+
+static void (*lasthandler)(void);
+static unsigned long interruptjiffies;
+static unsigned long resultjiffies;
+static int resultsize;
+static unsigned long lastredo;
+
+static struct output_log {
+ unsigned char data;
+ unsigned char status;
+ unsigned long jiffies;
+} output_log[OLOGSIZE];
+
+static int output_log_pos;
+#endif
+
+#define current_reqD -1
+#define MAXTIMEOUT -2
+
+static void reschedule_timeout(int drive, const char *message, int marg)
+{
+ if (drive == current_reqD)
+ drive = current_drive;
+ del_timer(&fd_timeout);
+ if (drive < 0 || drive > N_DRIVE) {
+ fd_timeout.expires = jiffies + 20UL*HZ;
+ drive=0;
+ } else
+ fd_timeout.expires = jiffies + UDP->timeout;
+ add_timer(&fd_timeout);
+ if (UDP->flags & FD_DEBUG){
+ DPRINT("reschedule timeout ");
+ printk(message, marg);
+ printk("\n");
+ }
+ timeout_message = message;
+}
+
+static int maximum(int a, int b)
+{
+ if (a > b)
+ return a;
+ else
+ return b;
+}
+#define INFBOUND(a,b) (a)=maximum((a),(b));
+
+static int minimum(int a, int b)
+{
+ if (a < b)
+ return a;
+ else
+ return b;
+}
+#define SUPBOUND(a,b) (a)=minimum((a),(b));
+
+
+/*
+ * Bottom half floppy driver.
+ * ==========================
+ *
+ * This part of the file contains the code talking directly to the hardware,
+ * and also the main service loop (seek-configure-spinup-command)
+ */
+
+/*
+ * disk change.
+ * This routine is responsible for maintaining the FD_DISK_CHANGE flag,
+ * and the last_checked date.
+ *
+ * last_checked is the date of the last check which showed 'no disk change'
+ * FD_DISK_CHANGE is set under two conditions:
+ * 1. The floppy has been changed after some i/o to that floppy already
+ * took place.
+ * 2. No floppy disk is in the drive. This is done in order to ensure that
+ * requests are quickly flushed in case there is no disk in the drive. It
+ * follows that FD_DISK_CHANGE can only be cleared if there is a disk in
+ * the drive.
+ *
+ * For 1., maxblock is observed. Maxblock is 0 if no i/o has taken place yet.
+ * For 2., FD_DISK_NEWCHANGE is watched. FD_DISK_NEWCHANGE is cleared on
+ * each seek. If a disk is present, the disk change line should also be
+ * cleared on each seek. Thus, if FD_DISK_NEWCHANGE is clear, but the disk
+ * change line is set, this means either that no disk is in the drive, or
+ * that it has been removed since the last seek.
+ *
+ * This means that we really have a third possibility too:
+ * The floppy has been changed after the last seek.
+ */
+
+static int disk_change(int drive)
+{
+ return UTESTF(FD_DISK_CHANGED);
+}
+
+static int set_mode(char mask, char data)
+{
+ register unsigned char newdor, olddor;
+
+ olddor = FDCS->dor;
+ newdor = (olddor & mask) | data;
+ if (newdor != olddor) {
+ FDCS->dor = newdor;
+ fd_outb(newdor, FD_MODE);
+ }
+
+ if (newdor & FLOPPY_MOTOR_MASK)
+ floppy_grab_irq_and_dma();
+
+ if (olddor & FLOPPY_MOTOR_MASK)
+ floppy_release_irq_and_dma();
+
+ return olddor;
+}
+
+static void twaddle(void)
+{
+ if (DP->select_delay)
+ return;
+
+ fd_outb(FDCS->dor & 0xf7, FD_MODE);
+ fd_outb(FDCS->dor, FD_MODE);
+ DRS->select_date = jiffies;
+}
+
+/* reset all driver information about the current fdc. This is needed after
+ * a reset, and after a raw command. */
+static void reset_fdc_info(int mode)
+{
+ int drive;
+
+ FDCS->spec1 = FDCS->spec2 = -1;
+ FDCS->need_configure = 1;
+ FDCS->perp_mode = 1;
+ FDCS->rawcmd = 0;
+ for (drive = 0; drive < N_DRIVE; drive++)
+ if (FDC(drive) == fdc &&
+ (mode || UDRS->track != NEED_1_RECAL))
+ UDRS->track = NEED_2_RECAL;
+}
+
+/* selects the fdc and drive, and enables the fdc's input/dma. */
+static void set_fdc(int drive)
+{
+ fdc = 0;
+ current_drive = drive;
+ set_mode(~0, 0x10);
+ if (FDCS->rawcmd == 2)
+ reset_fdc_info(1);
+
+ if (fd_inb(FD_STATUS) != STATUS_READY)
+ FDCS->reset = 1;
+}
+
+/* locks the driver */
+static int _lock_fdc(int drive, int interruptible, int line)
+{
+ if (!usage_count){
+ printk(KERN_ERR "Trying to lock fdc while usage count=0 at line %d\n", line);
+ return -1;
+ }
+ if(floppy_grab_irq_and_dma()==-1)
+ return -EBUSY;
+
+ if (test_and_set_bit(0, &fdc_busy)) {
+ DECLARE_WAITQUEUE(wait, current);
+ add_wait_queue(&fdc_wait, &wait);
+
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (!test_and_set_bit(0, &fdc_busy))
+ break;
+
+ schedule();
+
+ if (!NO_SIGNAL) {
+ remove_wait_queue(&fdc_wait, &wait);
+ return -EINTR;
+ }
+ }
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&fdc_wait, &wait);
+ }
+ command_status = FD_COMMAND_NONE;
+
+ reschedule_timeout(drive, "lock fdc", 0);
+ set_fdc(drive);
+ return 0;
+}
+
+#define lock_fdc(drive,interruptible) _lock_fdc(drive,interruptible, __LINE__)
+
+#define LOCK_FDC(drive,interruptible) \
+if (lock_fdc(drive,interruptible)) return -EINTR;
+
+
+/* unlocks the driver */
+static inline void unlock_fdc(void)
+{
+ raw_cmd = 0;
+ if (!fdc_busy)
+ DPRINT("FDC access conflict!\n");
+
+ if (do_floppy)
+ DPRINT("device interrupt still active at FDC release: %p!\n",
+ do_floppy);
+ command_status = FD_COMMAND_NONE;
+ del_timer(&fd_timeout);
+ cont = NULL;
+ clear_bit(0, &fdc_busy);
+ floppy_release_irq_and_dma();
+ wake_up(&fdc_wait);
+}
+
+#ifndef CONFIG_PC9800_MOTOR_OFF /* tomita */
+
+/* switches the motor off after a given timeout */
+static void motor_off_callback(unsigned long nr)
+{
+ printk(KERN_DEBUG "fdc%lu: turn off motor\n", nr);
+}
+
+/* schedules motor off */
+static void floppy_off(unsigned int drive)
+{
+}
+
+#else /* CONFIG_PC9800_MOTOR_OFF */
+
+/* switches the motor off after a given timeout */
+static void motor_off_callback(unsigned long fdc)
+{
+ printk(KERN_DEBUG "fdc%u: turn off motor\n", (unsigned int) fdc);
+
+ fd_outb(0, FD_MODE); /* MTON = 0 */
+}
+
+static struct timer_list motor_off_timer[N_FDC] = {
+ { data: 0, function: motor_off_callback },
+#if N_FDC > 1
+ { data: 1, function: motor_off_callback },
+#endif
+#if N_FDC > 2
+# error "N_FDC > 2; please fix initializer for motor_off_timer[]"
+#endif
+};
+
+/* schedules motor off */
+static void floppy_off(unsigned int drive)
+{
+ unsigned long volatile delta;
+ register int fdc = FDC(drive);
+
+ if (!(FDCS->dor & (0x10 << UNIT(drive))))
+ return;
+
+ del_timer(motor_off_timer + fdc);
+
+#if 0
+ /* make spindle stop in a position which minimizes spinup time
+ * next time */
+ if (UDP->rps){
+ delta = jiffies - UDRS->first_read_date + HZ -
+ UDP->spindown_offset;
+ delta = ((delta * UDP->rps) % HZ) / UDP->rps;
+ motor_off_timer[drive].expires = jiffies + UDP->spindown - delta;
+ }
+#else
+ if (UDP->rps)
+ motor_off_timer[drive].expires = jiffies + UDP->spindown;
+#endif
+
+ add_timer(motor_off_timer + fdc);
+}
+
+#endif /* CONFIG_PC9800_MOTOR_OFF */
+
+/*
+ * cycle through all N_DRIVE floppy drives, for disk change testing.
+ * stopping at current drive. This is done before any long operation, to
+ * be sure to have up to date disk change information.
+ */
+static void scandrives(void)
+{
+ int i, drive, saved_drive;
+
+ if (DP->select_delay)
+ return;
+
+ saved_drive = current_drive;
+ for (i=0; i < N_DRIVE; i++){
+ drive = (saved_drive + i + 1) % N_DRIVE;
+ if (UDRS->fd_ref == 0 || UDP->select_delay != 0)
+ continue; /* skip closed drives */
+ set_fdc(drive);
+ }
+ set_fdc(saved_drive);
+}
+
+static void empty(void)
+{
+}
+
+static DECLARE_WORK(floppy_work, NULL, NULL);
+
+static void schedule_bh( void (*handler)(void*) )
+{
+ PREPARE_WORK(&floppy_work, handler, NULL);
+ schedule_work(&floppy_work);
+}
+
+static struct timer_list fd_timer;
+
+static void cancel_activity(void)
+{
+ do_floppy = NULL;
+ PREPARE_WORK(&floppy_work, (void*)(void*)empty, NULL);
+ del_timer(&fd_timer);
+}
+
+/* this function makes sure that the disk stays in the drive during the
+ * transfer */
+static void fd_watchdog(void)
+{
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("calling disk change from watchdog\n");
+ }
+#endif
+
+ if (disk_change(current_drive)){
+ DPRINT("disk removed during i/o\n");
+ cancel_activity();
+ cont->done(0);
+ reset_fdc();
+ } else {
+ del_timer(&fd_timer);
+ fd_timer.function = (timeout_fn) fd_watchdog;
+ fd_timer.expires = jiffies + HZ / 10;
+ add_timer(&fd_timer);
+ }
+}
+
+static void main_command_interrupt(void)
+{
+ del_timer(&fd_timer);
+ cont->interrupt();
+}
+
+/* waits for a delay (spinup or select) to pass */
+static int fd_wait_for_completion(unsigned long delay, timeout_fn function)
+{
+ if (FDCS->reset){
+ reset_fdc(); /* do the reset during sleep to win time
+ * if we don't need to sleep, it's a good
+ * occasion anyways */
+ return 1;
+ }
+
+ if ((signed) (jiffies - delay) < 0){
+ del_timer(&fd_timer);
+ fd_timer.function = function;
+ fd_timer.expires = delay;
+ add_timer(&fd_timer);
+ return 1;
+ }
+ return 0;
+}
+
+static spinlock_t floppy_hlt_lock = SPIN_LOCK_UNLOCKED;
+static int hlt_disabled;
+static void floppy_disable_hlt(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&floppy_hlt_lock, flags);
+ if (!hlt_disabled) {
+ hlt_disabled=1;
+#ifdef HAVE_DISABLE_HLT
+ disable_hlt();
+#endif
+ }
+ spin_unlock_irqrestore(&floppy_hlt_lock, flags);
+}
+
+static void floppy_enable_hlt(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&floppy_hlt_lock, flags);
+ if (hlt_disabled){
+ hlt_disabled=0;
+#ifdef HAVE_DISABLE_HLT
+ enable_hlt();
+#endif
+ }
+ spin_unlock_irqrestore(&floppy_hlt_lock, flags);
+}
+
+
+static void setup_DMA(void)
+{
+ unsigned long f;
+
+#ifdef FLOPPY_SANITY_CHECK
+ if (raw_cmd->length == 0){
+ int i;
+
+ printk("zero dma transfer size:");
+ for (i=0; i < raw_cmd->cmd_count; i++)
+ printk("%x,", raw_cmd->cmd[i]);
+ printk("\n");
+ cont->done(0);
+ FDCS->reset = 1;
+ return;
+ }
+ if (((unsigned long) raw_cmd->kernel_data) % 512){
+ printk("non aligned address: %p\n", raw_cmd->kernel_data);
+ cont->done(0);
+ FDCS->reset=1;
+ return;
+ }
+#endif
+ f=claim_dma_lock();
+ fd_disable_dma();
+#ifdef fd_dma_setup
+ if (fd_dma_setup(raw_cmd->kernel_data, raw_cmd->length, + (raw_cmd->flags & FD_RAW_READ)?
+ DMA_MODE_READ : DMA_MODE_WRITE,
+ FDCS->address) < 0) {
+ release_dma_lock(f);
+ cont->done(0);
+ FDCS->reset=1;
+ return;
+ }
+ release_dma_lock(f);
+#else
+ fd_clear_dma_ff();
+ fd_cacheflush(raw_cmd->kernel_data, raw_cmd->length);
+ fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ)?
+ DMA_MODE_READ : DMA_MODE_WRITE);
+ fd_set_dma_addr(raw_cmd->kernel_data);
+ fd_set_dma_count(raw_cmd->length);
+ virtual_dma_port = FDCS->address;
+ fd_enable_dma();
+ release_dma_lock(f);
+#endif
+ floppy_disable_hlt();
+}
+
+static void show_floppy(void);
+
+/* waits until the fdc becomes ready */
+
+#ifdef PC9800_DEBUG_FLOPPY
+#define READY_DELAY 10000000
+#else
+#define READY_DELAY 100000
+#endif
+
+static int wait_til_ready(void)
+{
+ int counter, status;
+ if (FDCS->reset)
+ return -1;
+ for (counter = 0; counter < READY_DELAY; counter++) {
+ status = fd_inb(FD_STATUS);
+ if (status & STATUS_READY)
+ return status;
+ }
+ if (!initialising) {
+ DPRINT("Getstatus times out (%x) on fdc %d\n",
+ status, fdc);
+ show_floppy();
+ }
+ FDCS->reset = 1;
+ return -1;
+}
+
+/* sends a command byte to the fdc */
+static int output_byte(char byte)
+{
+ int status;
+
+ if ((status = wait_til_ready()) < 0)
+ return -1;
+ if ((status & (STATUS_READY|STATUS_DIR|STATUS_DMA)) == STATUS_READY){
+ fd_outb(byte,FD_DATA);
+#ifdef FLOPPY_SANITY_CHECK
+ output_log[output_log_pos].data = byte;
+ output_log[output_log_pos].status = status;
+ output_log[output_log_pos].jiffies = jiffies;
+ output_log_pos = (output_log_pos + 1) % OLOGSIZE;
+#endif
+ return 0;
+ }
+ FDCS->reset = 1;
+ if (!initialising) {
+ DPRINT("Unable to send byte %x to FDC. Fdc=%x Status=%x\n",
+ byte, fdc, status);
+ show_floppy();
+ }
+ return -1;
+}
+#define LAST_OUT(x) if (output_byte(x)<0){ reset_fdc();return;}
+
+/* gets the response from the fdc */
+static int result(void)
+{
+ int i, status=0;
+
+ for(i=0; i < MAX_REPLIES; i++) {
+ if ((status = wait_til_ready()) < 0)
+ break;
+ status &= STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA;
+ if ((status & ~STATUS_BUSY) == STATUS_READY){
+#ifdef FLOPPY_SANITY_CHECK
+ resultjiffies = jiffies;
+ resultsize = i;
+#endif
+ return i;
+ }
+ if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY))
+ reply_buffer[i] = fd_inb(FD_DATA);
+ else
+ break;
+ }
+ if (!initialising) {
+ DPRINT("get result error. Fdc=%d Last status=%x Read bytes=%d\n",
+ fdc, status, i);
+ show_floppy();
+ }
+ FDCS->reset = 1;
+ return -1;
+}
+
+static int fifo_depth = 0xa;
+static int no_fifo;
+
+#define NOMINAL_DTR 500
+
+/* Issue a "SPECIFY" command to set the step rate time, head unload time,
+ * head load time, and DMA disable flag to values needed by floppy.
+ *
+ * The value "dtr" is the data transfer rate in Kbps. It is needed
+ * to account for the data rate-based scaling done by the 82072 and 82077
+ * FDC types. This parameter is ignored for other types of FDCs (i.e.
+ * 8272a).
+ *
+ * Note that changing the data transfer rate has a (probably deleterious)
+ * effect on the parameters subject to scaling for 82072/82077 FDCs, so
+ * fdc_specify is called again after each data transfer rate
+ * change.
+ *
+ * srt: 1000 to 16000 in microseconds
+ * hut: 16 to 240 milliseconds
+ * hlt: 2 to 254 milliseconds
+ *
+ * These values are rounded up to the next highest available delay time.
+ */
+static void fdc_specify(void)
+{
+ output_byte(FD_SPECIFY);
+ output_byte(FDCS->spec1 = 0xdf);
+ output_byte(FDCS->spec2 = 0x24);
+}
+
+static void tell_sector(void)
+{
+ printk(": track %d, head %d, sector %d, size %d",
+ R_TRACK, R_HEAD, R_SECTOR, R_SIZECODE);
+} /* tell_sector */
+
+static int auto_detect_mode_pc9800(void)
+{
+#ifdef PC9800_DEBUG_FLOPPY
+ printk("auto_detect_mode_pc9800: retry_auto_detect=%d\n",
+ retry_auto_detect);
+#endif
+ if (retry_auto_detect > 4) {
+ retry_auto_detect = 0; + return 1;
+ }
+
+ switch ((int)(_floppy - floppy_type)) {
+ case 2:
+ _floppy = floppy_type + 4;
+ break;
+
+ case 4:
+ case 6:
+ _floppy = floppy_type + 7;
+ break;
+
+ case 7:
+ case 10:
+ _floppy = floppy_type + 2;
+ break;
+
+ default:
+ _floppy = floppy_type + 7;
+ }
+
+ retry_auto_detect++;
+ return 0;
+}
+
+static void access_mode_change_pc9800(void);
+
+/*
+ * OK, this error interpreting routine is called after a
+ * DMA read/write has succeeded
+ * or failed, so we check the results, and copy any buffers.
+ * hhb: Added better error reporting.
+ * ak: Made this into a separate routine.
+ */
+static int interpret_errors(void)
+{
+ char bad;
+
+ if (inr!=7) {
+ DPRINT("-- FDC reply error");
+ FDCS->reset = 1;
+ return 1;
+ }
+
+ /* check IC to find cause of interrupt */
+ switch (ST0 & ST0_INTR) {
+ case 0x40: /* error occurred during command execution */
+ if (ST1 & ST1_EOC)
+ return 0; /* occurs with pseudo-DMA */
+ bad = 1;
+ if (ST1 & ST1_WP) {
+ DPRINT("Drive is write protected\n");
+ CLEARF(FD_DISK_WRITABLE);
+ cont->done(0);
+ bad = 2;
+ } else if (ST1 & ST1_ND) {
+ SETF(FD_NEED_TWADDLE);
+ } else if (ST1 & ST1_OR) {
+ if (DP->flags & FTD_MSG)
+ DPRINT("Over/Underrun - retrying\n");
+ bad = 0;
+ }else if (*errors >= DP->max_errors.reporting){
+ if (ST0 & ST0_ECE) {
+ printk("Recalibrate failed!");
+ } else if (ST2 & ST2_CRC) {
+ printk("data CRC error");
+ tell_sector();
+ } else if (ST1 & ST1_CRC) {
+ printk("CRC error");
+ tell_sector();
+ } else if ((ST1 & (ST1_MAM|ST1_ND)) || (ST2 & ST2_MAM)) {
+ if (auto_detect_mode) {
+ bad = (char)auto_detect_mode_pc9800();
+ access_mode_change_pc9800();
+ }
+
+ if (bad) {
+ printk("floppy error: MA: _floppy - floppy_type=%d\n", (int)(_floppy - floppy_type));
+ printk("bad=%d\n", (int)bad);
+ if (!probing) {
+ printk("sector not found");
+ tell_sector();
+ } else
+ printk("probe failed...");
+ }
+ } else if (ST2 & ST2_WC) { /* seek error */
+ printk("wrong cylinder");
+ } else if (ST2 & ST2_BC) { /* cylinder marked as bad */
+ printk("bad cylinder");
+ } else {
+ printk("unknown error. ST[0..2] are: 0x%x 0x%x 0x%x", ST0, ST1, ST2);
+ tell_sector();
+ }
+ printk("\n");
+
+ }
+ if (ST2 & ST2_WC || ST2 & ST2_BC)
+ /* wrong cylinder => recal */
+ DRS->track = NEED_2_RECAL;
+ return bad;
+ case 0x80: /* invalid command given */
+ DPRINT("Invalid FDC command given!\n");
+ cont->done(0);
+ return 2;
+ case 0xc0:
+ SETF(FD_DISK_CHANGED);
+ SETF(FD_DISK_WRITABLE);
+ DPRINT("Abnormal termination caused by polling\n");
+ cont->error();
+ return 2;
+ default: /* (0) Normal command termination */
+ auto_detect_mode = 0;
+ return 0;
+ }
+}
+
+/*
+ * This routine is called when everything should be correctly set up
+ * for the transfer (i.e. floppy motor is on, the correct floppy is
+ * selected, and the head is sitting on the right track).
+ */
+static void setup_rw_floppy(void)
+{
+ int i,r, flags,dflags;
+ unsigned long ready_date;
+ timeout_fn function;
+
+ access_mode_change_pc9800();
+ flags = raw_cmd->flags;
+ if (flags & (FD_RAW_READ | FD_RAW_WRITE))
+ flags |= FD_RAW_INTR;
+
+ if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)){
+ ready_date = DRS->spinup_date + DP->spinup;
+ /* If spinup will take a long time, rerun scandrives
+ * again just before spinup completion. Beware that
+ * after scandrives, we must again wait for selection.
+ */
+ if ((signed) (ready_date - jiffies) > DP->select_delay){
+ ready_date -= DP->select_delay;
+ function = (timeout_fn) floppy_start;
+ } else
+ function = (timeout_fn) setup_rw_floppy;
+
+ /* wait until the floppy is spinning fast enough */
+ if (fd_wait_for_completion(ready_date,function))
+ return;
+ }
+ dflags = DRS->flags;
+
+ if ((flags & FD_RAW_READ) || (flags & FD_RAW_WRITE))
+ setup_DMA();
+
+ if (flags & FD_RAW_INTR)
+ do_floppy = main_command_interrupt;
+
+ r=0;
+ for (i=0; i< raw_cmd->cmd_count; i++)
+ r|=output_byte(raw_cmd->cmd[i]);
+
+#ifdef DEBUGT
+ debugt("rw_command: ");
+#endif
+ if (r){
+ cont->error();
+ reset_fdc();
+ return;
+ }
+
+ if (!(flags & FD_RAW_INTR)){
+ inr = result();
+ cont->interrupt();
+ } else if (flags & FD_RAW_NEED_DISK)
+ fd_watchdog();
+}
+
+static int blind_seek;
+
+/*
+ * This is the routine called after every seek (or recalibrate) interrupt
+ * from the floppy controller.
+ */
+static void seek_interrupt(void)
+{
+#ifdef DEBUGT
+ debugt("seek interrupt:");
+#endif
+ if (inr != 2 || (ST0 & 0xF8) != 0x20) {
+ DRS->track = NEED_2_RECAL;
+ cont->error();
+ cont->redo();
+ return;
+ }
+ if (DRS->track >= 0 && DRS->track != ST1 && !blind_seek){
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("clearing NEWCHANGE flag because of effective seek\n");
+ DPRINT("jiffies=%lu\n", jiffies);
+ }
+#endif
+ CLEARF(FD_DISK_NEWCHANGE); /* effective seek */
+ CLEARF(FD_DISK_CHANGED); /* effective seek */
+ DRS->select_date = jiffies;
+ }
+ DRS->track = ST1;
+ floppy_ready();
+}
+
+static void check_wp(void)
+{
+ if (TESTF(FD_VERIFY)) {
+ /* check write protection */
+ output_byte(FD_GETSTATUS);
+ output_byte(UNIT(current_drive));
+ if (result() != 1){
+ FDCS->reset = 1;
+ return;
+ }
+ CLEARF(FD_VERIFY);
+ CLEARF(FD_NEED_TWADDLE);
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("checking whether disk is write protected\n");
+ DPRINT("wp=%x\n",ST3 & 0x40);
+ }
+#endif
+ if (!(ST3 & 0x40))
+ SETF(FD_DISK_WRITABLE);
+ else
+ CLEARF(FD_DISK_WRITABLE);
+ }
+}
+
+static void seek_floppy(void)
+{
+ int track;
+
+ blind_seek=0;
+
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("calling disk change from seek\n");
+ }
+#endif
+
+ if (!TESTF(FD_DISK_NEWCHANGE) &&
+ disk_change(current_drive) &&
+ (raw_cmd->flags & FD_RAW_NEED_DISK)){
+ /* the media changed flag should be cleared after the seek.
+ * If it isn't, this means that there is really no disk in
+ * the drive.
+ */
+ SETF(FD_DISK_CHANGED);
+ cont->done(0);
+ cont->redo();
+ return;
+ }
+ if (DRS->track <= NEED_1_RECAL){
+ recalibrate_floppy();
+ return;
+ } else if (TESTF(FD_DISK_NEWCHANGE) &&
+ (raw_cmd->flags & FD_RAW_NEED_DISK) &&
+ (DRS->track <= NO_TRACK || DRS->track == raw_cmd->track)) {
+ /* we seek to clear the media-changed condition. Does anybody
+ * know a more elegant way, which works on all drives? */
+ if (raw_cmd->track)
+ track = raw_cmd->track - 1;
+ else {
+ if (DP->flags & FD_SILENT_DCL_CLEAR){
+ blind_seek = 1;
+ raw_cmd->flags |= FD_RAW_NEED_SEEK;
+ }
+ track = 1;
+ }
+ } else {
+ check_wp();
+ if (raw_cmd->track != DRS->track &&
+ (raw_cmd->flags & FD_RAW_NEED_SEEK))
+ track = raw_cmd->track;
+ else {
+ setup_rw_floppy();
+ return;
+ }
+ }
+
+ do_floppy = seek_interrupt;
+ output_byte(FD_SEEK);
+ output_byte(UNIT(current_drive));
+ LAST_OUT(track);
+#ifdef DEBUGT
+ debugt("seek command:");
+#endif
+}
+
+static void recal_interrupt(void)
+{
+#ifdef DEBUGT
+ debugt("recal interrupt:");
+#endif
+ if (inr !=2)
+ FDCS->reset = 1;
+ else if (ST0 & ST0_ECE) {
+ switch(DRS->track){
+ case NEED_1_RECAL:
+#ifdef DEBUGT
+ debugt("recal interrupt need 1 recal:");
+#endif
+ /* after a second recalibrate, we still haven't
+ * reached track 0. Probably no drive. Raise an
+ * error, as failing immediately might upset
+ * computers possessed by the Devil :-) */
+ cont->error();
+ cont->redo();
+ return;
+ case NEED_2_RECAL:
+#ifdef DEBUGT
+ debugt("recal interrupt need 2 recal:");
+#endif
+ /* If we already did a recalibrate,
+ * and we are not at track 0, this
+ * means we have moved. (The only way
+ * not to move at recalibration is to
+ * be already at track 0.) Clear the
+ * new change flag */
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("clearing NEWCHANGE flag because of second recalibrate\n");
+ }
+#endif
+
+ CLEARF(FD_DISK_NEWCHANGE);
+ DRS->select_date = jiffies;
+ /* fall through */
+ default:
+#ifdef DEBUGT
+ debugt("recal interrupt default:");
+#endif
+ /* Recalibrate moves the head by at
+ * most 80 steps. If after one
+ * recalibrate we don't have reached
+ * track 0, this might mean that we
+ * started beyond track 80. Try
+ * again. */
+ DRS->track = NEED_1_RECAL;
+ break;
+ }
+ } else
+ DRS->track = ST1;
+ floppy_ready();
+}
+
+static void print_result(char *message, int inr)
+{
+ int i;
+
+ DPRINT("%s ", message);
+ if (inr >= 0)
+ for (i=0; i<inr; i++)
+ printk("repl[%d]=%x ", i, reply_buffer[i]);
+ printk("\n");
+}
+
+/* interrupt handler. Note that this can be called externally on the Sparc */
+void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ void (*handler)(void) = do_floppy;
+ int do_print;
+ unsigned long f;
+
+ lasthandler = handler;
+ interruptjiffies = jiffies;
+
+ f=claim_dma_lock();
+ fd_disable_dma();
+ release_dma_lock(f);
+
+ floppy_enable_hlt();
+ do_floppy = NULL;
+ if (fdc >= N_FDC || FDCS->address == -1){
+ /* we don't even know which FDC is the culprit */
+ printk("DOR0=%x\n", fdc_state[0].dor);
+ printk("floppy interrupt on bizarre fdc %d\n",fdc);
+ printk("handler=%p\n", handler);
+ is_alive("bizarre fdc");
+ return;
+ }
+
+ FDCS->reset = 0;
+ /* We have to clear the reset flag here, because apparently on boxes
+ * with level triggered interrupts (PS/2, Sparc, ...), it is needed to
+ * emit SENSEI's to clear the interrupt line. And FDCS->reset blocks the
+ * emission of the SENSEI's.
+ * It is OK to emit floppy commands because we are in an interrupt
+ * handler here, and thus we have to fear no interference of other
+ * activity.
+ */
+
+ do_print = !handler && !initialising;
+
+ inr = result();
+ if (inr && do_print)
+ print_result("unexpected interrupt", inr);
+ if (inr == 0){
+ do {
+ output_byte(FD_SENSEI);
+ inr = result();
+ if ((ST0 & ST0_INTR) == 0xC0) {
+ int drive = ST0 & ST0_DS;
+
+ /* Attention Interrupt. */
+ if (ST0 & ST0_NR) {
+#ifdef PC9800_DEBUG_FLOPPY
+ if (do_print)
+ printk(KERN_DEBUG
+ "floppy debug: floppy ejected (drive %d)\n",
+ drive);
+#endif
+ USETF(FD_DISK_CHANGED);
+ USETF(FD_VERIFY);
+ } else {
+#ifdef PC9800_DEBUG_FLOPPY
+ if (do_print)
+ printk(KERN_DEBUG
+ "floppy debug: floppy inserted (drive %d)\n",
+ drive);
+#endif
+ }
+ } /* Attention Interrupt */
+#ifdef PC9800_DEBUG_FLOPPY
+ else {
+ printk(KERN_DEBUG
+ "floppy debug : unknown interrupt\n");
+ }
+#endif
+ } while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2);
+ }
+ if (handler) {
+ schedule_bh( (void *)(void *) handler);
+ } else {
+#if 0
+ FDCS->reset = 1;
+#endif
+ }
+ is_alive("normal interrupt end");
+}
+
+static void recalibrate_floppy(void)
+{
+#ifdef DEBUGT
+ debugt("recalibrate floppy:");
+#endif
+ do_floppy = recal_interrupt;
+ output_byte(FD_RECALIBRATE);
+ LAST_OUT(UNIT(current_drive));
+}
+
+/*
+ * Must do 4 FD_SENSEIs after reset because of ``drive polling''.
+ */
+static void reset_interrupt(void)
+{
+#ifdef PC9800_DEBUG_FLOPPY
+ printk("floppy debug: reset interrupt\n");
+#endif
+#ifdef DEBUGT
+ debugt("reset interrupt:");
+#endif
+ result(); /* get the status ready for set_fdc */
+ if (FDCS->reset) {
+ printk("reset set in interrupt, calling %p\n", cont->error);
+ cont->error(); /* a reset just after a reset. BAD! */
+ }
+ cont->redo();
+}
+
+/*
+ * reset is done by pulling bit 2 of DOR low for a while (old FDCs),
+ * or by setting the self clearing bit 7 of STATUS (newer FDCs)
+ */
+static void reset_fdc(void)
+{
+ unsigned long flags;
+
+#ifdef PC9800_DEBUG_FLOPPY
+ printk("floppy debug: reset_fdc\n");
+#endif
+
+ do_floppy = reset_interrupt;
+ FDCS->reset = 0;
+ reset_fdc_info(0);
+
+ /* Pseudo-DMA may intercept 'reset finished' interrupt. */
+ /* Irrelevant for systems with true DMA (i386). */
+
+ flags=claim_dma_lock();
+ fd_disable_dma();
+ release_dma_lock(flags);
+
+ fd_outb(FDCS->dor | 0x80, FD_MODE);
+ udelay(FD_RESET_DELAY);
+ fd_outb(FDCS->dor, FD_MODE);
+ udelay(FD_AFTER_RESET_DELAY);
+}
+
+static void show_floppy(void)
+{
+ int i;
+
+ printk("\n");
+ printk("floppy driver state\n");
+ printk("-------------------\n");
+ printk("now=%lu last interrupt=%lu diff=%lu last called handler=%p\n",
+ jiffies, interruptjiffies, jiffies-interruptjiffies, lasthandler);
+
+
+#ifdef FLOPPY_SANITY_CHECK
+ printk("timeout_message=%s\n", timeout_message);
+ printk("last output bytes:\n");
+ for (i=0; i < OLOGSIZE; i++)
+ printk("%2x %2x %lu\n",
+ output_log[(i+output_log_pos) % OLOGSIZE].data,
+ output_log[(i+output_log_pos) % OLOGSIZE].status,
+ output_log[(i+output_log_pos) % OLOGSIZE].jiffies);
+ printk("last result at %lu\n", resultjiffies);
+ printk("last redo_fd_request at %lu\n", lastredo);
+ for (i=0; i<resultsize; i++){
+ printk("%2x ", reply_buffer[i]);
+ }
+ printk("\n");
+#endif
+
+ printk("status=%x\n", fd_inb(FD_STATUS));
+ printk("fdc_busy=%lu\n", fdc_busy);
+ if (do_floppy)
+ printk("do_floppy=%p\n", do_floppy);
+ if (floppy_work.pending)
+ printk("floppy_work.func=%p\n", floppy_work.func);
+ if (timer_pending(&fd_timer))
+ printk("fd_timer.function=%p\n", fd_timer.function);
+ if (timer_pending(&fd_timeout)){
+ printk("timer_function=%p\n",fd_timeout.function);
+ printk("expires=%lu\n",fd_timeout.expires-jiffies);
+ printk("now=%lu\n",jiffies);
+ }
+ printk("cont=%p\n", cont);
+ printk("current_req=%p\n", current_req);
+ printk("command_status=%d\n", command_status);
+ printk("\n");
+}
+
+static void floppy_shutdown(void)
+{
+ unsigned long flags;
+
+ if (!initialising)
+ show_floppy();
+ cancel_activity();
+
+ floppy_enable_hlt();
+
+ flags=claim_dma_lock();
+ fd_disable_dma();
+ release_dma_lock(flags);
+
+ /* avoid dma going to a random drive after shutdown */
+
+ if (!initialising)
+ DPRINT("floppy timeout called\n");
+ FDCS->reset = 1;
+ if (cont){
+ cont->done(0);
+ cont->redo(); /* this will recall reset when needed */
+ } else {
+ printk("no cont in shutdown!\n");
+ process_fd_request();
+ }
+ is_alive("floppy shutdown");
+}
+/*typedef void (*timeout_fn)(unsigned long);*/
+
+static void access_mode_change_pc9800(void)
+{
+ static int access_mode, mode_change_now, old_mode, new_set = 1;
+#ifdef PC9800_DEBUG_FLOPPY2
+ printk("enter access_mode_change\n");
+#endif
+ access_mode = mode_change_now = 0;
+ if (DP->cmos==4) {
+ switch ((int)(_floppy - &floppy_type[0])) {
+ case 1:
+ case 2:
+ new_set = 1;
+ access_mode = 2;
+ break;
+
+ case 4:
+ case 6:
+ new_set = 1;
+ access_mode = 3;
+ break;
+
+ case 7:
+ case 10:
+ new_set = 1;
+ access_mode = 1;
+ break;
+
+ default:
+ access_mode = 1;
+ break;
+ }
+
+ old_mode = fd_inb(FD_MODE_CHANGE) & 3;
+
+ switch (access_mode) {
+ case 1:
+ if ((old_mode & 2) == 0) {
+ fd_outb(old_mode | 2, FD_MODE_CHANGE);
+ mode_change_now = 1;
+ } else {
+ fd_outb(current_drive << 5, FD_EMODE_CHANGE);
+ if (fd_inb(FD_EMODE_CHANGE) == 0xff)
+ return;
+ }
+
+ fd_outb((current_drive << 5) | 0x11, FD_EMODE_CHANGE);
+ mode_change_now = 1;
+ break;
+
+ case 2:
+ if ((old_mode & 2) == 0) {
+ fd_outb(old_mode | 2, FD_MODE_CHANGE);
+ mode_change_now = 1;
+ } else {
+ fd_outb(current_drive << 5, FD_EMODE_CHANGE);
+ if ((fd_inb(FD_EMODE_CHANGE) & 1) == 0)
+ return;
+ fd_outb((current_drive << 5) | 0x10, FD_EMODE_CHANGE);
+ mode_change_now = 1;
+ }
+
+ break;
+
+ case 3:
+ if ((old_mode & 2) == 0)
+ return;
+ fd_outb(current_drive << 5, FD_EMODE_CHANGE);
+ if (fd_inb(FD_EMODE_CHANGE) & 1)
+ fd_outb((current_drive << 5) | 0x10, FD_EMODE_CHANGE);
+ fd_outb(old_mode & 0xfd, FD_MODE_CHANGE);
+ mode_change_now = 1;
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ switch ((int)(_floppy - &floppy_type[0])) {
+ case 1:
+ case 2:
+ new_set = 1;
+ access_mode = 2;
+ break;
+
+ case 4:
+ case 6:
+ new_set = 1;
+ access_mode = 3;
+ break;
+
+ default:
+ switch (DP->cmos) {
+ case 2:
+ access_mode = 2;
+ break;
+
+ case 3:
+ access_mode = 3;
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+ }
+
+ old_mode = fd_inb(FD_MODE_CHANGE) & 3;
+
+ switch (access_mode) {
+ case 2:
+ if ((old_mode & 2) == 0) {
+ fd_outb(old_mode | 2, FD_MODE_CHANGE);
+ mode_change_now = 1;
+ }
+
+ break;
+
+ case 3:
+ if (old_mode & 2) {
+ fd_outb(old_mode & 0xfd, FD_MODE_CHANGE);
+ mode_change_now = 1;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+#ifdef PC9800_DEBUG_FLOPPY2
+ printk("floppy debug: DP->cmos=%d\n", DP->cmos);
+ printk("floppy debug: mode_change_now=%d\n", mode_change_now);
+ printk("floppy debug: access_mode=%d\n", access_mode);
+ printk("floppy debug: old_mode=%d\n", old_mode);
+ printk("floppy debug: _floppy - &floppy_type[0]=%d\n", (int)(_floppy - &floppy_type[0]));
+#endif /* PC9800_DEBUG_FLOPPY2 */
+ if(mode_change_now)
+ reset_fdc();
+}
+
+/* start motor, check media-changed condition and write protection */
+static int start_motor(void (*function)(void) )
+{
+ access_mode_change_pc9800();
+ set_mode(~0, 0x8);
+
+ /* wait_for_completion also schedules reset if needed. */
+ return(fd_wait_for_completion(DRS->select_date+DP->select_delay,
+ (timeout_fn) function));
+}
+
+static void floppy_ready(void)
+{
+ CHECK_RESET;
+ if (start_motor(floppy_ready)) return;
+
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("calling disk change from floppy_ready\n");
+ }
+#endif
+ if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
+ disk_change(current_drive) &&
+ !DP->select_delay)
+ twaddle(); /* this clears the dcl on certain drive/controller
+ * combinations */
+
+#ifdef fd_chose_dma_mode
+ if ((raw_cmd->flags & FD_RAW_READ) || + (raw_cmd->flags & FD_RAW_WRITE))
+ {
+ unsigned long flags = claim_dma_lock();
+ fd_chose_dma_mode(raw_cmd->kernel_data,
+ raw_cmd->length);
+ release_dma_lock(flags);
+ }
+#endif
+
+#if 0
+ access_mode_change_pc9800();
+#endif
+ if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)){
+ fdc_specify(); /* must be done here because of hut, hlt ... */
+ seek_floppy();
+ } else {
+ if ((raw_cmd->flags & FD_RAW_READ) || + (raw_cmd->flags & FD_RAW_WRITE))
+ fdc_specify();
+ setup_rw_floppy();
+ }
+}
+
+static void floppy_start(void)
+{
+ reschedule_timeout(current_reqD, "floppy start", 0);
+
+ scandrives();
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("setting NEWCHANGE in floppy_start\n");
+ }
+#endif
+ SETF(FD_DISK_NEWCHANGE);
+ floppy_ready();
+}
+
+/*
+ * ========================================================================
+ * here ends the bottom half. Exported routines are:
+ * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
+ * start_motor, reset_fdc, reset_fdc_info, interpret_errors.
+ * Initialization also uses output_byte, result, set_dor, floppy_interrupt
+ * and set_dor.
+ * ========================================================================
+ */
+/*
+ * General purpose continuations.
+ * ==============================
+ */
+
+static void do_wakeup(void)
+{
+ reschedule_timeout(MAXTIMEOUT, "do wakeup", 0);
+ cont = 0;
+ command_status += 2;
+ wake_up(&command_done);
+}
+
+static struct cont_t wakeup_cont={
+ empty,
+ do_wakeup,
+ empty,
+ (done_f)empty
+};
+
+
+static struct cont_t intr_cont={
+ empty,
+ process_fd_request,
+ empty,
+ (done_f) empty
+};
+
+static int wait_til_done(void (*handler)(void), int interruptible)
+{
+ int ret;
+
+ schedule_bh((void *)(void *)handler);
+
+ if (command_status < 2 && NO_SIGNAL) {
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(&command_done, &wait);
+ for (;;) {
+ set_current_state(interruptible?
+ TASK_INTERRUPTIBLE:
+ TASK_UNINTERRUPTIBLE);
+
+ if (command_status >= 2 || !NO_SIGNAL)
+ break;
+
+ is_alive("wait_til_done");
+
+ schedule();
+ }
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&command_done, &wait);
+ }
+
+ if (command_status < 2){
+ cancel_activity();
+ cont = &intr_cont;
+ reset_fdc();
+ return -EINTR;
+ }
+
+#ifdef PC9800_DEBUG_FLOPPY
+ if (command_status != FD_COMMAND_OKAY)
+ printk("floppy check: wait_til_done out:%d\n", command_status);
+#endif
+ if (FDCS->reset)
+ command_status = FD_COMMAND_ERROR;
+ if (command_status == FD_COMMAND_OKAY)
+ ret=0;
+ else
+ ret=-EIO;
+ command_status = FD_COMMAND_NONE;
+ return ret;
+}
+
+static void generic_done(int result)
+{
+ command_status = result;
+ cont = &wakeup_cont;
+}
+
+static void generic_success(void)
+{
+ cont->done(1);
+}
+
+static void generic_failure(void)
+{
+ cont->done(0);
+}
+
+static void success_and_wakeup(void)
+{
+ generic_success();
+ cont->redo();
+}
+
+
+/*
+ * formatting and rw support.
+ * ==========================
+ */
+
+static int next_valid_format(void)
+{
+ int probed_format;
+
+ probed_format = DRS->probed_format;
+ while(1){
+ if (probed_format >= 8 ||
+ !DP->autodetect[probed_format]){
+ DRS->probed_format = 0;
+ return 1;
+ }
+ if (floppy_type[DP->autodetect[probed_format]].sect){
+ DRS->probed_format = probed_format;
+ return 0;
+ }
+ probed_format++;
+ }
+}
+
+static void bad_flp_intr(void)
+{
+ if (probing){
+ DRS->probed_format++;
+ if (!next_valid_format())
+ return;
+ }
+ (*errors)++;
+ INFBOUND(DRWE->badness, *errors);
+ if (*errors > DP->max_errors.abort)
+ cont->done(0);
+ if (*errors > DP->max_errors.reset)
+ FDCS->reset = 1;
+ else if (*errors > DP->max_errors.recal)
+ DRS->track = NEED_2_RECAL;
+}
+
+static void set_floppy(kdev_t device)
+{
+ if (TYPE(device)) {
+ auto_detect_mode = 0;
+ _floppy = TYPE(device) + floppy_type;
+ } else if (auto_detect_mode == 0) {
+ auto_detect_mode = 1;
+ retry_auto_detect = 0;
+ _floppy = current_type[DRIVE(device)];
+ }
+#ifdef PC9800_DEBUG_FLOPPY2
+ printk("set_floppy: set floppy type=%d\n", (int)(_floppy - floppy_type));
+#endif
+}
+
+/*
+ * formatting support.
+ * ===================
+ */
+static void format_interrupt(void)
+{
+ switch (interpret_errors()){
+ case 1:
+ cont->error();
+ case 2:
+ break;
+ case 0:
+ cont->done(1);
+ }
+ cont->redo();
+}
+
+#define CODE2SIZE (ssize = ((1 << SIZECODE) + 3) >> 2)
+#define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80) >>1))
+#define CT(x) ((x) | 0xc0)
+static void setup_format_params(int track)
+{
+ struct fparm {
+ unsigned char track,head,sect,size;
+ } *here = (struct fparm *)floppy_track_buffer;
+ int il,n;
+ int count,head_shift,track_shift;
+
+ raw_cmd = &default_raw_cmd;
+ raw_cmd->track = track;
+
+ raw_cmd->flags = FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN |
+ FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK;
+ raw_cmd->rate = _floppy->rate & 0x43;
+ raw_cmd->cmd_count = NR_F;
+ COMMAND = FM_MODE(_floppy,FD_FORMAT);
+ DR_SELECT = UNIT(current_drive) + PH_HEAD(_floppy,format_req.head);
+ F_SIZECODE = FD_SIZECODE(_floppy);
+ F_SECT_PER_TRACK = _floppy->sect << 2 >> F_SIZECODE;
+ F_GAP = _floppy->fmt_gap;
+ F_FILL = FD_FILL_BYTE;
+
+ raw_cmd->kernel_data = floppy_track_buffer;
+ raw_cmd->length = 4 * F_SECT_PER_TRACK;
+
+ /* allow for about 30ms for data transport per track */
+ head_shift = (F_SECT_PER_TRACK + 5) / 6;
+
+ /* a ``cylinder'' is two tracks plus a little stepping time */
+ track_shift = 2 * head_shift + 3;
+
+ /* position of logical sector 1 on this track */
+ n = (track_shift * format_req.track + head_shift * format_req.head)
+ % F_SECT_PER_TRACK;
+
+ /* determine interleave */
+ il = 1;
+ if (_floppy->fmt_gap < 0x22)
+ il++;
+
+ /* initialize field */
+ for (count = 0; count < F_SECT_PER_TRACK; ++count) {
+ here[count].track = format_req.track;
+ here[count].head = format_req.head;
+ here[count].sect = 0;
+ here[count].size = F_SIZECODE;
+ }
+ /* place logical sectors */
+ for (count = 1; count <= F_SECT_PER_TRACK; ++count) {
+ here[n].sect = count;
+ n = (n+il) % F_SECT_PER_TRACK;
+ if (here[n].sect) { /* sector busy, find next free sector */
+ ++n;
+ if (n>= F_SECT_PER_TRACK) {
+ n-=F_SECT_PER_TRACK;
+ while (here[n].sect) ++n;
+ }
+ }
+ }
+}
+
+static void redo_format(void)
+{
+ buffer_track = -1;
+ setup_format_params(format_req.track << STRETCH(_floppy));
+ floppy_start();
+#ifdef DEBUGT
+ debugt("queue format request");
+#endif
+}
+
+static struct cont_t format_cont={
+ format_interrupt,
+ redo_format,
+ bad_flp_intr,
+ generic_done };
+
+static int do_format(kdev_t device, struct format_descr *tmp_format_req)
+{
+ int ret;
+ int drive=DRIVE(device);
+
+ LOCK_FDC(drive,1);
+ set_floppy(device);
+ if (!_floppy ||
+ _floppy->track > DP->tracks ||
+ tmp_format_req->track >= _floppy->track ||
+ tmp_format_req->head >= _floppy->head ||
+ (_floppy->sect << 2) % (1 << FD_SIZECODE(_floppy)) ||
+ !_floppy->fmt_gap) {
+ process_fd_request();
+ return -EINVAL;
+ }
+ format_req = *tmp_format_req;
+ format_errors = 0;
+ cont = &format_cont;
+ errors = &format_errors;
+ IWAIT(redo_format);
+ process_fd_request();
+ return ret;
+}
+
Osamu Tomita
2002-11-02 18:02:52 UTC
Permalink
This is a part 10/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
input modules
- add new driver for PC-9800 standard keyboard and mouse.
- add few changes for PC-9800 hardware spec.

diffstat:
drivers/char/keyboard.c | 4 drivers/input/keyboard/98kbd.c | 356 +++++++++++++++++++++++++++++++++++++++
drivers/input/keyboard/Kconfig | 12 +
drivers/input/keyboard/Makefile | 1 drivers/input/misc/pcspkr.c | 24 ++
drivers/input/mouse/98busmouse.c | 201 ++++++++++++++++++++++
drivers/input/mouse/Kconfig | 12 +
drivers/input/mouse/Makefile | 1 drivers/input/serio/98kbd-io.c | 181 +++++++++++++++++++
drivers/input/serio/Kconfig | 12 +
drivers/input/serio/Makefile | 1 include/linux/kbd_kern.h | 5 include/linux/keyboard.h | 1 include/linux/serio.h
| 1 14 files changed, 810 insertions(+), 2 deletions(-)

patch:
diff -urN linux/drivers/input/keyboard/Kconfig linux98/drivers/input/keyboard/Kconfig
--- linux/drivers/input/keyboard/Kconfig Thu Oct 31 13:23:13 2002
+++ linux98/drivers/input/keyboard/Kconfig Thu Oct 31 17:16:37 2002
@@ -88,3 +88,15 @@
The module will be called amikbd.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
+config KEYBOARD_98KBD
+ tristate "NEC PC-9800 Keyboard support"
+ depends on PC9800 && INPUT && INPUT_KEYBOARD && SERIO
+ help
+ Say Y here if you want to use the NEC PC-9801/PC-9821 keyboard (or
+ compatible) on your system. +
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called xtkbd.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
diff -urN linux/drivers/input/keyboard/Makefile linux98/drivers/input/keyboard/Makefile
--- linux/drivers/input/keyboard/Makefile Sat Oct 12 13:21:42 2002
+++ linux98/drivers/input/keyboard/Makefile Sun Oct 13 11:27:15 2002
@@ -10,6 +10,7 @@
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
+obj-$(CONFIG_KEYBOARD_98KBD) += 98kbd.o
# The global Rules.make.
diff -urN linux/drivers/input/keyboard/98kbd.c linux98/drivers/input/keyboard/98kbd.c
--- linux/drivers/input/keyboard/98kbd.c Thu Jan 1 09:00:00 1970
+++ linux98/drivers/input/keyboard/98kbd.c Sun Oct 27 07:45:43 2002
@@ -0,0 +1,356 @@
+/*
+ * drivers/input/keyboard/98kbd.c
+ *
+ * PC-9801 keyboard driver for Linux
+ *
+ * Based on atkbd.c and xtkbd.c written by Vojtech Pavlik
+ *
+ * Copyright (c) 2002 Osamu Tomita
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version.
+ * + * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * + * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * + */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+
+#include <asm/io.h>
+#include <asm/pc9800.h>
+
+MODULE_AUTHOR("Osamu Tomita <***@cinet.co.jp>");
+MODULE_DESCRIPTION("PC-9801 keyboard driver");
+MODULE_LICENSE("GPL");
+
+#define KBD98_KEY 0x7f
+#define KBD98_RELEASE 0x80
+
+static unsigned char kbd98_keycode[256] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 43, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 41, 26, 28, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 27, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 53, 12, 57,184,109,104,110,111,103,105,106,108,102,107,
+ 74, 98, 71, 72, 73, 55, 75, 76, 77, 78, 79, 80, 81,117, 82,124,
+ 83,185, 87, 88, 85, 89, 90, 0, 0, 0, 0, 0, 0, 0,102, 0,
+ 99,133, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 0, 0, 0, 0,
+ 54, 58, 42, 56, 29
+};
+
+struct jis_kbd_conv {
+ unsigned char scancode;
+ struct {
+ unsigned char shift;
+ unsigned char keycode;
+ } emul[2];
+};
+
+static struct jis_kbd_conv kbd98_jis[] = {
+ {0x02, {{0, 3}, {1, 40}}},
+ {0x06, {{0, 7}, {1, 8}}},
+ {0x07, {{0, 8}, {0, 40}}},
+ {0x08, {{0, 9}, {1, 10}}},
+ {0x09, {{0, 10}, {1, 11}}},
+ {0x0a, {{0, 11}, {1, 255}}},
+ {0x0b, {{0, 12}, {0, 13}}},
+ {0x0c, {{1, 7}, {0, 41}}},
+ {0x1a, {{1, 3}, {1, 41}}},
+ {0x26, {{0, 39}, {1, 13}}},
+ {0x27, {{1, 39}, {1, 9}}},
+ {0x33, {{0, 255}, {1, 12}}},
+ {0xff, {{0, 255}, {1, 255}}} /* terminater */
+};
+
+#define KBD98_CMD_SETEXKEY 0x1095 /* Enable/Disable Windows, Appli key */
+#define KBD98_CMD_SETRATE 0x109c /* Set typematic rate */
+#define KBD98_CMD_SETLEDS 0x109d /* Set keyboard leds */
+#define KBD98_CMD_GETLEDS 0x119d /* Get keyboard leds */
+#define KBD98_CMD_GETID 0x019f
+
+#define KBD98_RET_ACK 0xfa
+#define KBD98_RET_NAK 0xfc /* Command NACK, send the cmd again */
+
+#define KBD98_KEY_JIS_EMUL 254
+#define KBD98_KEY_NULL 255
+
+static char *kbd98_name = "PC-9801 Keyboard";
+
+struct kbd98 {
+ unsigned char keycode[256];
+ struct input_dev dev;
+ struct serio *serio;
+ char phys[32];
+ unsigned char cmdbuf[4];
+ unsigned char cmdcnt;
+ signed char ack;
+ unsigned char shift;
+ struct jis_kbd_conv jis[16];
+};
+
+void kbd98_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
+{
+ struct kbd98 *kbd98 = serio->private;
+ unsigned char scancode, keycode;
+ int press, i;
+
+ switch (data) {
+ case KBD98_RET_ACK:
+ kbd98->ack = 1;
+ return;
+ case KBD98_RET_NAK:
+ kbd98->ack = -1;
+ return;
+ }
+
+ if (kbd98->cmdcnt) {
+ kbd98->cmdbuf[--kbd98->cmdcnt] = data;
+ return;
+ }
+
+ scancode = data & KBD98_KEY;
+ keycode = kbd98->keycode[scancode];
+ press = !(data & KBD98_RELEASE);
+ if (keycode == KEY_RIGHTSHIFT)
+ kbd98->shift = press;
+
+ switch (keycode) {
+ case KEY_2:
+ case KEY_6:
+ case KEY_7:
+ case KEY_8:
+ case KEY_9:
+ case KEY_0:
+ case KEY_MINUS:
+ case KEY_EQUAL:
+ case KEY_GRAVE:
+ case KEY_SEMICOLON:
+ case KEY_APOSTROPHE:
+ /* emulation: JIS keyboard to US101 keyboard */
+ i = 0;
+ while (kbd98->jis[i].scancode != 0xff) {
+ if (scancode == kbd98->jis[i].scancode)
+ break;
+ i ++;
+ }
+
+ keycode = kbd98->jis[i].emul[kbd98->shift].keycode;
+ if (keycode == KBD98_KEY_NULL)
+ return;
+
+ if (kbd98->jis[i].emul[kbd98->shift].shift != kbd98->shift && press) {
+ input_report_key(&kbd98->dev, KEY_RIGHTSHIFT, !(kbd98->shift));
+ }
+
+ input_report_key(&kbd98->dev, keycode, press);
+ if (kbd98->jis[i].emul[kbd98->shift].shift != kbd98->shift && !press) {
+ input_report_key(&kbd98->dev, KEY_RIGHTSHIFT, kbd98->shift);
+ }
+
+ input_sync(&kbd98->dev);
+ return;
+
+ case KBD98_KEY_NULL:
+ return;
+
+ case 0:
+ printk(KERN_WARNING "kbd98.c: Unknown key (scancode %#x) %s.\n",
+ data & KBD98_KEY, data & KBD98_RELEASE ? "released" : "pressed");
+ return;
+
+ default:
+ input_report_key(&kbd98->dev, keycode, press);
+ input_sync(&kbd98->dev);
+ }
+}
+
+/*
+ * kbd98_sendbyte() sends a byte to the keyboard, and waits for
+ * acknowledge. It doesn't handle resends according to the keyboard
+ * protocol specs, because if these are needed, the keyboard needs
+ * replacement anyway, and they only make a mess in the protocol.
+ */
+
+static int kbd98_sendbyte(struct kbd98 *kbd98, unsigned char byte)
+{
+ int timeout = 10000; /* 100 msec */
+ kbd98->ack = 0;
+
+ if (serio_write(kbd98->serio, byte))
+ return -1;
+
+ while (!kbd98->ack && timeout--) udelay(10);
+
+ return -(kbd98->ack <= 0);
+}
+
+/*
+ * kbd98_command() sends a command, and its parameters to the keyboard,
+ * then waits for the response and puts it in the param array.
+ */
+
+static int kbd98_command(struct kbd98 *kbd98, unsigned char *param, int command)
+{
+ int timeout = 50000; /* 500 msec */
+ int send = (command >> 12) & 0xf;
+ int receive = (command >> 8) & 0xf;
+ int i;
+
+ kbd98->cmdcnt = receive;
+
+ if (command & 0xff)
+ if (kbd98_sendbyte(kbd98, command & 0xff))
+ return (kbd98->cmdcnt = 0) - 1;
+
+ for (i = 0; i < send; i++)
+ if (kbd98_sendbyte(kbd98, param[i]))
+ return (kbd98->cmdcnt = 0) - 1;
+
+ while (kbd98->cmdcnt && timeout--) udelay(10);
+
+ if (param)
+ for (i = 0; i < receive; i++)
+ param[i] = kbd98->cmdbuf[(receive - 1) - i];
+
+ if (kbd98->cmdcnt) + return (kbd98->cmdcnt = 0) - 1;
+
+ return 0;
+}
+
+/*
+ * Event callback from the input module. Events that change the state of
+ * the hardware are processed here.
+ */
+
+static int kbd98_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct kbd98 *kbd98 = dev->private;
+ char param[2];
+
+ switch (type) {
+
+ case EV_LED:
+
+ if (__PC9800SCA_TEST_BIT(0x481, 3)) {
+ /* 98note with Num Lock key */
+ /* keep Num Lock status */
+ *param = 0x60;
+ if (kbd98_command(kbd98, param,
+ KBD98_CMD_GETLEDS))
+ printk(KERN_DEBUG
+ "kbd98: Get keyboard LED"
+ " status Error\n");
+
+ *param &= 1;
+ } else {
+ /* desktop PC-9801 */
+ *param = 1; /* Allways set Num Lock */
+ }
+
+ *param |= 0x70
+ | (test_bit(LED_CAPSL, dev->led) ? 4 : 0)
+ | (test_bit(LED_KANA, dev->led) ? 8 : 0);
+ kbd98_command(kbd98, param, KBD98_CMD_SETLEDS);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+void kbd98_connect(struct serio *serio, struct serio_dev *dev)
+{
+ struct kbd98 *kbd98;
+ int i;
+
+ if ((serio->type & SERIO_TYPE) != SERIO_PC9800)
+ return;
+
+ if (!(kbd98 = kmalloc(sizeof(struct kbd98), GFP_KERNEL)))
+ return;
+
+ memset(kbd98, 0, sizeof(struct kbd98));
+
+ kbd98->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
+ kbd98->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_KANA);
+
+ kbd98->serio = serio;
+
+ init_input_dev(&kbd98->dev);
+ kbd98->dev.keycode = kbd98->keycode;
+ kbd98->dev.keycodesize = sizeof(unsigned char);
+ kbd98->dev.keycodemax = ARRAY_SIZE(kbd98_keycode);
+ kbd98->dev.event = kbd98_event;
+ kbd98->dev.private = kbd98;
+
+ serio->private = kbd98;
+
+ if (serio_open(serio, dev)) {
+ kfree(kbd98);
+ return;
+ }
+
+ memcpy(kbd98->jis, kbd98_jis, sizeof(kbd98_jis));
+ memcpy(kbd98->keycode, kbd98_keycode, sizeof(kbd98->keycode));
+ for (i = 0; i < 255; i++)
+ set_bit(kbd98->keycode[i], kbd98->dev.keybit);
+ clear_bit(0, kbd98->dev.keybit);
+
+ sprintf(kbd98->phys, "%s/input0", serio->phys);
+
+ kbd98->dev.name = kbd98_name;
+ kbd98->dev.phys = kbd98->phys;
+ kbd98->dev.id.bustype = BUS_XTKBD;
+ kbd98->dev.id.vendor = 0x0002;
+ kbd98->dev.id.product = 0x0001;
+ kbd98->dev.id.version = 0x0100;
+
+ input_register_device(&kbd98->dev);
+
+ printk(KERN_INFO "input: %s on %s\n", kbd98_name, serio->phys);
+}
+
+void kbd98_disconnect(struct serio *serio)
+{
+ struct kbd98 *kbd98 = serio->private;
+ input_unregister_device(&kbd98->dev);
+ serio_close(serio);
+ kfree(kbd98);
+}
+
+struct serio_dev kbd98_dev = {
+ .interrupt = kbd98_interrupt,
+ .connect = kbd98_connect,
+ .disconnect = kbd98_disconnect
+};
+
+int __init kbd98_init(void)
+{
+ serio_register_device(&kbd98_dev);
+ return 0;
+}
+
+void __exit kbd98_exit(void)
+{
+ serio_unregister_device(&kbd98_dev);
+}
+
+module_init(kbd98_init);
+module_exit(kbd98_exit);
diff -urN linux/drivers/input/misc/pcspkr.c linux98/drivers/input/misc/pcspkr.c
--- linux/drivers/input/misc/pcspkr.c Mon Sep 16 11:18:31 2002
+++ linux98/drivers/input/misc/pcspkr.c Mon Sep 16 16:04:05 2002
@@ -12,6 +12,7 @@
* the Free Software Foundation
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -23,7 +24,11 @@
MODULE_LICENSE("GPL");
static char pcspkr_name[] = "PC Speaker";
+#ifndef CONFIG_PC9800
static char pcspkr_phys[] = "isa0061/input0";
+#else
+static char pcspkr_phys[] = "isa3fdb/input0";
+#endif
static struct input_dev pcspkr_dev;
spinlock_t i8253_beep_lock = SPIN_LOCK_UNLOCKED;
@@ -43,11 +48,16 @@
} if (value > 20 && value < 32767)
+#ifndef CONFIG_PC9800
count = 1193182 / value;
+#else
+ count = CLOCK_TICK_RATE / value;
+#endif

spin_lock_irqsave(&i8253_beep_lock, flags);
if (count) {
+#ifndef CONFIG_PC9800
/* enable counter 2 */
outb_p(inb_p(0x61) | 3, 0x61);
/* set command for counter 2, 2 byte write */
@@ -55,9 +65,23 @@
/* select desired HZ */
outb_p(count & 0xff, 0x42);
outb((count >> 8) & 0xff, 0x42);
+#else /* CONFIG_PC9800 */
+ outb(0x76, 0x3fdf);
+ outb(0, 0x5f);
+ outb(count & 0xff, 0x3fdb);
+ outb(0, 0x5f);
+ outb((count >> 8) & 0xff, 0x3fdb);
+ /* beep on */
+ outb(6, 0x37);
+#endif /* !CONFIG_PC9800 */
} else {
/* disable counter 2 */
+#ifndef CONFIG_PC9800
outb(inb_p(0x61) & 0xFC, 0x61);
+#else
+ /* beep off */
+ outb(7, 0x37);
+#endif
}
spin_unlock_irqrestore(&i8253_beep_lock, flags);
diff -urN linux/drivers/input/mouse/98busmouse.c linux98/drivers/input/mouse/98busmouse.c
--- linux/drivers/input/mouse/98busmouse.c Thu Jan 1 09:00:00 1970
+++ linux98/drivers/input/mouse/98busmouse.c Sat Oct 26 18:36:15 2002
@@ -0,0 +1,201 @@
+/*
+ *
+ * Copyright (c) 2002 Osamu Tomita
+ *
+ * Based on the work of:
+ * James Banks Matthew Dillon
+ * David Giller Nathan Laredo
+ * Linus Torvalds Johan Myreen
+ * Cliff Matthews Philip Blundell
+ * Russell King Vojtech Pavlik
+ */
+
+/*
+ * NEC PC-9801 Bus Mouse Driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version.
+ * + * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * + * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * + */
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/input.h>
+
+MODULE_AUTHOR("Osamu Tomita <***@cinet.co.jp>");
+MODULE_DESCRIPTION("PC-9801 busmouse driver");
+MODULE_LICENSE("GPL");
+
+#define PC98BM_BASE 0x7fd9
+#define PC98BM_DATA_PORT PC98BM_BASE + 0
+/* PC98BM_SIGNATURE_PORT does not exist */
+#define PC98BM_CONTROL_PORT PC98BM_BASE + 4
+/* PC98BM_INTERRUPT_PORT does not exist */
+#define PC98BM_CONFIG_PORT PC98BM_BASE + 6
+
+#define PC98BM_ENABLE_IRQ 0x00
+#define PC98BM_DISABLE_IRQ 0x10
+#define PC98BM_READ_X_LOW 0x80
+#define PC98BM_READ_X_HIGH 0xa0
+#define PC98BM_READ_Y_LOW 0xc0
+#define PC98BM_READ_Y_HIGH 0xe0
+
+#define PC98BM_DEFAULT_MODE 0x93
+/* PC98BM_CONFIG_BYTE is not used */
+/* PC98BM_SIGNATURE_BYTE is not used */
+
+#define PC98BM_TIMER_PORT 0xbfdb
+#define PC98BM_DEFAULT_TIMER_VAL 0x00
+
+#define PC98BM_IRQ 13
+
+MODULE_PARM(pc98bm_irq, "i");
+
+static int pc98bm_irq = PC98BM_IRQ;
+static int pc98bm_used = 0;
+
+static void pc98bm_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static int pc98bm_open(struct input_dev *dev)
+{
+ if (pc98bm_used++)
+ return 0;
+ if (request_irq(pc98bm_irq, pc98bm_interrupt, 0, "98busmouse", NULL)) {
+ pc98bm_used--;
+ printk(KERN_ERR "98busmouse.c: Can't allocate irq %d\n", pc98bm_irq);
+ return -EBUSY;
+ }
+ outb(PC98BM_ENABLE_IRQ, PC98BM_CONTROL_PORT);
+ return 0;
+}
+
+static void pc98bm_close(struct input_dev *dev)
+{
+ if (--pc98bm_used)
+ return;
+ outb(PC98BM_DISABLE_IRQ, PC98BM_CONTROL_PORT);
+ free_irq(pc98bm_irq, NULL);
+}
+
+static struct input_dev pc98bm_dev = {
+ .evbit = { BIT(EV_KEY) | BIT(EV_REL) },
+ .keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT) },
+ .relbit = { BIT(REL_X) | BIT(REL_Y) },
+ .open = pc98bm_open,
+ .close = pc98bm_close,
+ .name = "PC-9801 bus mouse",
+ .phys = "isa7fd9/input0",
+ .id = {
+ .bustype = BUS_ISA,
+ .vendor = 0x0004,
+ .product = 0x0001,
+ .version = 0x0100,
+ },
+};
+
+static void pc98bm_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ char dx, dy;
+ unsigned char buttons;
+
+ outb(PC98BM_READ_X_LOW, PC98BM_CONTROL_PORT);
+ dx = (inb(PC98BM_DATA_PORT) & 0xf);
+ outb(PC98BM_READ_X_HIGH, PC98BM_CONTROL_PORT);
+ dx |= (inb(PC98BM_DATA_PORT) & 0xf) << 4;
+ outb(PC98BM_READ_Y_LOW, PC98BM_CONTROL_PORT);
+ dy = (inb(PC98BM_DATA_PORT) & 0xf);
+ outb(PC98BM_READ_Y_HIGH, PC98BM_CONTROL_PORT);
+ buttons = inb(PC98BM_DATA_PORT);
+ dy |= (buttons & 0xf) << 4;
+ buttons = ~buttons >> 5;
+
+ input_report_rel(&pc98bm_dev, REL_X, dx);
+ input_report_rel(&pc98bm_dev, REL_Y, dy);
+ input_report_key(&pc98bm_dev, BTN_RIGHT, buttons & 1);
+ input_report_key(&pc98bm_dev, BTN_MIDDLE, buttons & 2);
+ input_report_key(&pc98bm_dev, BTN_LEFT, buttons & 4);
+ input_sync(&pc98bm_dev);
+
+ outb(PC98BM_ENABLE_IRQ, PC98BM_CONTROL_PORT);
+}
+
+#ifndef MODULE
+static int __init pc98bm_setup(char *str)
+{
+ int ints[4];
+ str = get_options(str, ARRAY_SIZE(ints), ints);
+ if (ints[0] > 0) pc98bm_irq = ints[1];
+ return 1;
+}
+__setup("pc98bm_irq=", pc98bm_setup);
+#endif
+
+static int __init pc98bm_init(void)
+{
+ int i;
+
+ for (i = 0; i <= 6; i += 2) {
+ if (!request_region(PC98BM_BASE + i, 1, "98busmouse")) {
+ printk(KERN_ERR "98busmouse.c: Can't allocate ports at %#x\n", PC98BM_BASE + i);
+ while (i > 0) {
+ i -= 2;
+ release_region(PC98BM_BASE + i, 1);
+ }
+
+ return -EBUSY;
+ }
+
+ }
+
+ if (!request_region(PC98BM_TIMER_PORT, 1, "98busmouse")) {
+ printk(KERN_ERR "98busmouse.c: Can't allocate ports at %#x\n", PC98BM_TIMER_PORT);
+ for (i = 0; i <= 6; i += 2)
+ release_region(PC98BM_BASE + i, 1);
+
+ return -EBUSY;
+ }
+
+ outb(PC98BM_DEFAULT_MODE, PC98BM_CONFIG_PORT);
+ outb(PC98BM_DISABLE_IRQ, PC98BM_CONTROL_PORT);
+
+ outb(PC98BM_DEFAULT_TIMER_VAL, PC98BM_TIMER_PORT);
+
+ input_register_device(&pc98bm_dev);
+
+ printk(KERN_INFO "input: PC-9801 bus mouse at %#x irq %d\n", PC98BM_BASE, pc98bm_irq);
+
+ return 0;
+}
+
+static void __exit pc98bm_exit(void)
+{
+ int i;
+
+ input_unregister_device(&pc98bm_dev);
+ for (i = 0; i <= 6; i += 2)
+ release_region(PC98BM_BASE + i, 1);
+
+ release_region(PC98BM_TIMER_PORT, 1);
+}
+
+module_init(pc98bm_init);
+module_exit(pc98bm_exit);
diff -urN linux/drivers/input/mouse/Kconfig linux98/drivers/input/mouse/Kconfig
--- linux/drivers/input/mouse/Kconfig Thu Oct 31 13:23:13 2002
+++ linux98/drivers/input/mouse/Kconfig Thu Oct 31 17:24:14 2002
@@ -119,3 +119,15 @@
The module will be called rpcmouse.o. If you want to compile it as a
module, say M here and read <file.:Documentation/modules.txt>.
+config MOUSE_PC9800
+ tristate "NEC PC-9800 busmouse"
+ depends on PC9800 && INPUT && INPUT_MOUSE && ISA
+ help
+ Say Y here if you have NEC PC-9801/PC-9821 computer and want its
+ native mouse supported.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called logibm.o. If you want to compile it as a
+ module, say M here and read <file.:Documentation/modules.txt>.
+
diff -urN linux/drivers/input/mouse/Makefile linux98/drivers/input/mouse/Makefile
--- linux/drivers/input/mouse/Makefile Sat Oct 19 13:01:17 2002
+++ linux98/drivers/input/mouse/Makefile Thu Oct 31 17:25:12 2002
@@ -10,6 +10,7 @@
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
+obj-$(CONFIG_MOUSE_PC9800) += 98busmouse.o
obj-$(CONFIG_MOUSE_PS2) += psmouse.o
obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
diff -urN linux/drivers/input/serio/Kconfig linux98/drivers/input/serio/Kconfig
--- linux/drivers/input/serio/Kconfig Thu Oct 31 13:23:13 2002
+++ linux98/drivers/input/serio/Kconfig Thu Oct 31 17:34:00 2002
@@ -103,3 +103,15 @@
tristate "Intel SA1111 keyboard controller"
depends on SA1111 && SERIO
+config SERIO_98KBD
+ tristate "NEC PC-9800 keyboard controller"
+ depends on PC9800 && SERIO
+ help
+ Say Y here if you have the NEC PC-9801/PC-9821 and want to use its
+ standard keyboard connected to its keyboard controller.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called rpckbd.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
diff -urN linux/drivers/input/serio/Makefile linux98/drivers/input/serio/Makefile
--- linux/drivers/input/serio/Makefile Sat Oct 19 13:01:09 2002
+++ linux98/drivers/input/serio/Makefile Thu Oct 24 15:50:55 2002
@@ -17,6 +17,7 @@
obj-$(CONFIG_SERIO_SA1111) += sa1111ps2.o
obj-$(CONFIG_SERIO_AMBAKMI) += ambakmi.o
obj-$(CONFIG_SERIO_Q40KBD) += q40kbd.o
+obj-$(CONFIG_SERIO_98KBD) += 98kbd-io.o
# The global Rules.make.
diff -urN linux/drivers/input/serio/98kbd-io.c linux98/drivers/input/serio/98kbd-io.c
--- linux/drivers/input/serio/98kbd-io.c Thu Jan 1 09:00:00 1970
+++ linux98/drivers/input/serio/98kbd-io.c Thu Oct 24 16:29:57 2002
@@ -0,0 +1,181 @@
+/*
+ * NEC PC-9801 keyboard controller driver for Linux
+ *
+ * Copyright (c) 1999-2002 Osamu Tomita <***@cinet.co.jp>
+ * Based on i8042.c written by Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <asm/io.h>
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/sched.h>
+
+MODULE_AUTHOR("Osamu Tomita <***@cinet.co.jp>");
+MODULE_DESCRIPTION("NEC PC-9801 keyboard controller driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Names.
+ */
+
+#define KBD98_PHYS_DESC "isa0041/serio0"
+
+/*
+ * IRQs.
+ */
+
+#define KBD98_IRQ 1
+
+/*
+ * Register numbers.
+ */
+
+#define KBD98_COMMAND_REG 0x43
+#define KBD98_STATUS_REG 0x43
+#define KBD98_DATA_REG 0x41
+
+spinlock_t kbd98io_lock = SPIN_LOCK_UNLOCKED;
+
+static struct serio kbd98_port;
+extern struct pt_regs *kbd_pt_regs;
+
+static void kbd98io_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+/*
+ * kbd98_flush() flushes all data that may be in the keyboard buffers
+ */
+
+static int kbd98_flush(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&kbd98io_lock, flags);
+
+ while (inb(KBD98_STATUS_REG) & 0x02) /* RxRDY */
+ inb(KBD98_DATA_REG);
+
+ if (inb(KBD98_STATUS_REG) & 0x38)
+ printk("98kbd-io: Keyboard error!\n");
+
+ spin_unlock_irqrestore(&kbd98io_lock, flags);
+
+ return 0;
+}
+
+/*
+ * kbd98_write() sends a byte out through the keyboard interface.
+ */
+
+static int kbd98_write(struct serio *port, unsigned char c)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&kbd98io_lock, flags);
+
+ outb(0, 0x5f); /* wait */
+ outb(0x17, KBD98_COMMAND_REG); /* enable send command */
+ outb(0, 0x5f); /* wait */
+ outb(c, KBD98_DATA_REG);
+ outb(0, 0x5f); /* wait */
+ outb(0x16, KBD98_COMMAND_REG); /* disable send command */
+ outb(0, 0x5f); /* wait */
+
+ spin_unlock_irqrestore(&kbd98io_lock, flags);
+
+ return 0;
+}
+
+/*
+ * kbd98_open() is called when a port is open by the higher layer.
+ * It allocates the interrupt and enables in in the chip.
+ */
+
+static int kbd98_open(struct serio *port)
+{
+ kbd98_flush();
+
+ if (request_irq(KBD98_IRQ, kbd98io_interrupt, 0, "kbd98", NULL)) {
+ printk(KERN_ERR "98kbd-io.c: Can't get irq %d for %s, unregistering the port.\n", KBD98_IRQ, "KBD");
+ serio_unregister_port(port);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void kbd98_close(struct serio *port)
+{
+ free_irq(KBD98_IRQ, NULL);
+
+ kbd98_flush();
+}
+
+/*
+ * Structures for registering the devices in the serio.c module.
+ */
+
+static struct serio kbd98_port =
+{
+ .type = SERIO_PC9800,
+ .write = kbd98_write,
+ .open = kbd98_open,
+ .close = kbd98_close,
+ .driver = NULL,
+ .name = "PC-9801 Kbd Port",
+ .phys = KBD98_PHYS_DESC,
+};
+
+/*
+ * kbd98io_interrupt() is the most important function in this driver -
+ * it handles the interrupts from keyboard, and sends incoming bytes
+ * to the upper layers.
+ */
+
+static void kbd98io_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ unsigned char data;
+
+#ifdef CONFIG_VT
+ kbd_pt_regs = regs;
+#endif
+
+ spin_lock_irqsave(&kbd98io_lock, flags);
+
+ data = inb(KBD98_DATA_REG);
+ spin_unlock_irqrestore(&kbd98io_lock, flags);
+ serio_interrupt(&kbd98_port, data, 0);
+
+}
+
+int __init kbd98io_init(void)
+{
+ serio_register_port(&kbd98_port);
+
+ printk(KERN_INFO "serio: PC-9801 %s port at %#lx,%#lx irq %d\n",
+ "KBD",
+ (unsigned long) KBD98_DATA_REG,
+ (unsigned long) KBD98_COMMAND_REG,
+ KBD98_IRQ);
+
+ return 0;
+}
+
+void __exit kbd98io_exit(void)
+{
+ serio_unregister_port(&kbd98_port);
+}
+
+module_init(kbd98io_init);
+module_exit(kbd98io_exit);
diff -urN linux/drivers/char/keyboard.c linux98/drivers/char/keyboard.c
--- linux/drivers/char/keyboard.c Sat Oct 19 13:01:49 2002
+++ linux98/drivers/char/keyboard.c Sun Oct 27 09:12:29 2002
@@ -58,7 +58,11 @@
* Some laptops take the 789uiojklm,. keys as number pad when NumLock is on.
* This seems a good reason to start with NumLock off.
*/
+#ifndef CONFIG_PC9800
#define KBD_DEFLEDS 0
+#else
+#define KBD_DEFLEDS (1 << VC_NUMLOCK)
+#endif
#endif
#ifndef KBD_DEFLOCK
diff -urN linux/include/linux/kbd_kern.h linux98/include/linux/kbd_kern.h
--- linux/include/linux/kbd_kern.h Sat Oct 19 13:02:28 2002
+++ linux98/include/linux/kbd_kern.h Sun Oct 27 10:23:23 2002
@@ -43,11 +43,12 @@
#define LED_SHOW_IOCTL 1 /* only change leds upon ioctl */
#define LED_SHOW_MEM 2 /* `heartbeat': peek into memory */
- unsigned char ledflagstate:3; /* flags, not lights */
- unsigned char default_ledflagstate:3;
+ unsigned char ledflagstate:4; /* flags, not lights */
+ unsigned char default_ledflagstate:4;
#define VC_SCROLLOCK 0 /* scroll-lock mode */
#define VC_NUMLOCK 1 /* numeric lock mode */
#define VC_CAPSLOCK 2 /* capslock mode */
+#define VC_KANALOCK 3 /* kanalock mode */
unsigned char kbdmode:2; /* one 2-bit value */
#define VC_XLATE 0 /* translate keycodes using keymap */
diff -urN linux/include/linux/keyboard.h linux98/include/linux/keyboard.h
--- linux/include/linux/keyboard.h Sat Oct 19 13:01:13 2002
+++ linux98/include/linux/keyboard.h Mon Oct 21 15:59:48 2002
@@ -9,6 +9,7 @@
#define KG_ALT 3
#define KG_ALTGR 1
#define KG_SHIFTL 4
+#define KG_KANASHIFT 4
#define KG_SHIFTR 5
#define KG_CTRLL 6
#define KG_CTRLR 7
diff -urN linux/include/linux/serio.h linux98/include/linux/serio.h
--- linux/include/linux/serio.h Sat Oct 19 13:01:58 2002
+++ linux98/include/linux/serio.h Thu Oct 24 09:39:09 2002
@@ -97,6 +97,7 @@
#define SERIO_8042 0x01000000UL
#define SERIO_RS232 0x02000000UL
#define SERIO_HIL_MLC 0x03000000UL
+#define SERIO_PC9800 0x04000000UL
#define SERIO_PROTO 0xFFUL
#define SERIO_MSC 0x01
Osamu Tomita
2002-11-02 18:04:34 UTC
Permalink
This is a part 11/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.44.

Summary:
- add PC-9800 special features.
- CLOCK_TICK is not constant.

diffstat:
init/main.c | 3 +++
kernel/dma.c | 3 +++
kernel/ksyms.c | 4 ++++
kernel/timer.c | 5 +++++
4 files changed, 15 insertions(+)

patch:
diff -urN linux/init/main.c linux98/init/main.c
--- linux/init/main.c Thu Oct 31 13:23:44 2002
+++ linux98/init/main.c Thu Oct 31 17:36:24 2002
@@ -100,6 +100,9 @@
char *execute_command;
+/* Indicates PC-9800 architecture No:0 Yes:1 */
+int pc98 = 0;
+
/* Setup configured maximum number of CPUs to activate */
static unsigned int max_cpus = UINT_MAX;
diff -urN linux/kernel/dma.c linux98/kernel/dma.c
--- linux/kernel/dma.c Sun Aug 11 10:41:22 2002
+++ linux98/kernel/dma.c Wed Aug 21 09:53:59 2002
@@ -9,6 +9,7 @@
* [It also happened to remove the sizeof(char *) == sizeof(int)
* assumption introduced because of those /proc/dma patches. -- Hennus]
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -62,10 +63,12 @@
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
+#ifndef CONFIG_PC9800
{ 1, "cascade" },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 }
+#endif
};
diff -urN linux/kernel/ksyms.c linux98/kernel/ksyms.c
--- linux/kernel/ksyms.c Sat Oct 19 13:01:08 2002
+++ linux98/kernel/ksyms.c Sat Oct 26 16:57:37 2002
@@ -599,5 +599,9 @@
EXPORT_SYMBOL(__per_cpu_offset);
#endif
+/* Indicates PC-9800 architecture No:0 Yes:1 */
+extern int pc98;
+EXPORT_SYMBOL(pc98);
+
/* debug */
EXPORT_SYMBOL(dump_stack);
diff -urN linux/kernel/timer.c linux98/kernel/timer.c
--- linux/kernel/timer.c Thu Oct 31 13:23:44 2002
+++ linux98/kernel/timer.c Thu Oct 31 17:36:24 2002
@@ -400,8 +400,13 @@
/*
* Timekeeping variables
*/
+#ifndef CONFIG_PC9800
unsigned long tick_usec = TICK_USEC; /* ACTHZ period (usec) */
unsigned long tick_nsec = TICK_NSEC(TICK_USEC); /* USER_HZ period (nsec) */
+#else
+extern unsigned long tick_usec; /* ACTHZ period (usec) */
+extern unsigned long tick_nsec; /* USER_HZ period (nsec) */
+#endif
/* The current time */
struct timespec xtime __attribute__ ((aligned (16)));
Osamu Tomita
2002-11-02 18:18:17 UTC
Permalink
This is part 16/20 of patchset for add support NEC PC-9800 architecture,
against 2.5.45.

Summary:
PC-9800 standard real time clock driver.(add new files)

diffstat:
drivers/char/upd4990a.c | 438 ++++++++++++++++++++++++++++++++++++++++++++
include/asm-i386/upd4990a.h | 58 +++++
include/linux/upd4990a.h | 140 ++++++++++++++
3 files changed, 636 insertions(+)

patch:
diff -urN linux/drivers/char/upd4990a.c linux98/drivers/char/upd4990a.c
--- linux/drivers/char/upd4990a.c Thu Jan 1 09:00:00 1970
+++ linux98/drivers/char/upd4990a.c Fri Aug 17 21:50:17 2001
@@ -0,0 +1,438 @@
+/*
+ * NEC PC-9800 Real Time Clock interface for Linux
+ *
+ * Copyright (C) 1997-2001 Linux/98 project,
+ * Kyoto University Microcomputer Club.
+ *
+ * Based on:
+ * drivers/char/rtc.c by Paul Gortmaker
+ *
+ * Changes:
+ * 2001-02-09 Call check_region on rtc_init and do not request I/O 0033h.
+ * Call del_timer and release_region on rtc_exit. -- tak
+ * 2001-07-14 Rewrite <linux/upd4990a.h> and split to <linux/upd4990a.h>
+ * and <asm-i386/upd4990a.h>.
+ * Introduce a lot of spin_lock/unlock (&rtc_lock).
+ */
+
+#define RTC98_VERSION "1.2"
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/rtc.h>
+#include <linux/upd4990a.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#define BCD_TO_BINARY(val) (((val) >> 4) * 10 + ((val) & 0xF))
+#define BINARY_TO_BCD(val) ((((val) / 10) << 4) | ((val) % 10))
+
+/*
+ * We sponge a minor off of the misc major. No need slurping
+ * up another valuable major dev number for this. If you add
+ * an ioctl, make sure you don't conflict with SPARC's RTC
+ * ioctls.
+ */
+
+static struct fasync_struct *rtc_async_queue;
+
+static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
+
+static struct timer_list rtc_uie_timer;
+static u8 old_refclk;
+
+static long long rtc_llseek(struct file *file, loff_t offset, int origin);
+
+static int rtc_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+
+static int rtc_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+
+/*
+ * Bits in rtc_status. (5 bits of room for future expansion)
+ */
+
+#define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */
+#define RTC_TIMER_ON 0x02 /* not used */
+#define RTC_UIE_TIMER_ON 0x04 /* UIE emulation timer is active */
+
+/*
+ * rtc_status is never changed by rtc_interrupt, and ioctl/open/close is
+ * protected by the big kernel lock. However, ioctl can still disable the timer
+ * in rtc_status and then with del_timer after the interrupt has read
+ * rtc_status but before mod_timer is called, which would then reenable the
+ * timer (but you would need to have an awful timing before you'd trip on it)
+ */
+static unsigned char rtc_status; /* bitmapped status byte. */
+static unsigned long rtc_irq_data; /* our output to the world */
+
+static const unsigned char days_in_mo[] = +{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+extern spinlock_t rtc_lock; /* defined in arch/i386/kernel/time.c */
+
+static void rtc_uie_intr(unsigned long data)
+{
+ u8 refclk, tmp;
+
+ /* Kernel timer does del_timer internally before calling
+ each timer entry, so this is unnecessary.
+ del_timer(&rtc_uie_timer); */
+ spin_lock(&rtc_lock);
+
+ /* Detect rising edge of 1Hz reference clock. */
+ refclk = UPD4990A_READ_DATA();
+ tmp = old_refclk & refclk;
+ old_refclk = ~refclk;
+ if (!(tmp & 1))
+ rtc_irq_data += 0x100;
+
+ spin_unlock(&rtc_lock);
+
+ if (!(tmp & 1)) {
+ /* Now do the rest of the actions */
+ wake_up_interruptible(&rtc_wait);
+ kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
+ }
+
+ rtc_uie_timer.expires = jiffies + 1;
+ add_timer(&rtc_uie_timer);
+}
+
+/*
+ * Now all the various file operations that we export.
+ */
+
+static long long rtc_llseek(struct file *file, loff_t offset, int origin)
+{
+ return -ESPIPE;
+}
+
+static ssize_t rtc_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ unsigned long data;
+ ssize_t retval = 0;
+
+ if (count < sizeof(unsigned long))
+ return -EINVAL;
+
+ add_wait_queue(&rtc_wait, &wait);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ do {
+ /* First make it right. Then make it fast. Putting this whole
+ * block within the parentheses of a while would be too
+ * confusing. And no, xchg() is not the answer. */
+ spin_lock_irq(&rtc_lock);
+ data = rtc_irq_data;
+ rtc_irq_data = 0;
+ spin_unlock_irq(&rtc_lock);
+
+ if (data != 0)
+ break;
+
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ goto out;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ goto out;
+ }
+ schedule();
+ } while (1);
+
+ retval = put_user(data, (unsigned long *)buf);
+ if (!retval)
+ retval = sizeof(unsigned long); + out:
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&rtc_wait, &wait);
+
+ return retval;
+}
+
+static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct rtc_time wtime; + struct upd4990a_raw_data raw;
+
+ switch (cmd) {
+ case RTC_UIE_OFF: /* Mask ints from RTC updates. */
+ spin_lock_irq(&rtc_lock);
+ if (rtc_status & RTC_UIE_TIMER_ON) {
+ rtc_status &= ~RTC_UIE_TIMER_ON;
+ del_timer(&rtc_uie_timer);
+ }
+ spin_unlock_irq(&rtc_lock);
+ return 0;
+
+ case RTC_UIE_ON: /* Allow ints for RTC updates. */
+ spin_lock_irq(&rtc_lock);
+ rtc_irq_data = 0;
+ if (!(rtc_status & RTC_UIE_TIMER_ON)){
+ rtc_status |= RTC_UIE_TIMER_ON;
+ rtc_uie_timer.expires = jiffies + 1;
+ add_timer(&rtc_uie_timer);
+ }
+ /* Just in case... */
+ upd4990a_serial_command(UPD4990A_REGISTER_HOLD);
+ old_refclk = ~UPD4990A_READ_DATA();
+ spin_unlock_irq(&rtc_lock);
+ return 0;
+
+ case RTC_RD_TIME: /* Read the time/date from RTC */
+ spin_lock_irq(&rtc_lock);
+ upd4990a_get_time(&raw, 0);
+ spin_unlock_irq(&rtc_lock);
+
+ wtime.tm_sec = BCD_TO_BINARY(raw.sec);
+ wtime.tm_min = BCD_TO_BINARY(raw.min);
+ wtime.tm_hour = BCD_TO_BINARY(raw.hour);
+ wtime.tm_mday = BCD_TO_BINARY(raw.mday);
+ wtime.tm_mon = raw.mon - 1; /* convert to 0-base */
+ wtime.tm_wday = raw.wday;
+
+ /*
+ * Account for differences between how the RTC uses the values
+ * and how they are defined in a struct rtc_time;
+ */
+ if ((wtime.tm_year = BCD_TO_BINARY(raw.year)) < 95)
+ wtime.tm_year += 100;
+
+ wtime.tm_isdst = 0;
+ break;
+
+ case RTC_SET_TIME: /* Set the RTC */
+ {
+ int leap_yr;
+
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+
+ if (copy_from_user(&wtime, (struct rtc_time *) arg,
+ sizeof (struct rtc_time)))
+ return -EFAULT;
+
+ /* Valid year is 1995 - 2094, inclusive. */
+ if (wtime.tm_year < 95 || wtime.tm_year > 194)
+ return -EINVAL;
+
+ if (wtime.tm_mon > 11 || wtime.tm_mday == 0)
+ return -EINVAL;
+
+ /* For acceptable year domain (1995 - 2094),
+ this IS sufficient. */
+ leap_yr = !(wtime.tm_year % 4);
+
+ if (wtime.tm_mday > (days_in_mo[wtime.tm_mon]
+ + (wtime.tm_mon == 2 && leap_yr)))
+ return -EINVAL;
+
+ if (wtime.tm_hour >= 24
+ || wtime.tm_min >= 60 || wtime.tm_sec >= 60)
+ return -EINVAL;
+
+ if (wtime.tm_wday > 6)
+ return -EINVAL;
+
+ raw.sec = BINARY_TO_BCD(wtime.tm_sec);
+ raw.min = BINARY_TO_BCD(wtime.tm_min);
+ raw.hour = BINARY_TO_BCD(wtime.tm_hour);
+ raw.mday = BINARY_TO_BCD(wtime.tm_mday);
+ raw.mon = wtime.tm_mon + 1;
+ raw.wday = wtime.tm_wday;
+ raw.year = BINARY_TO_BCD(wtime.tm_year % 100);
+
+ spin_lock_irq(&rtc_lock);
+ upd4990a_set_time(&raw, 0);
+ spin_unlock_irq(&rtc_lock);
+
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+ return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
+}
+
+/*
+ * We enforce only one user at a time here with the open/close.
+ * Also clear the previous interrupt data on an open, and clean
+ * up things on a close.
+ */
+
+static int rtc_open(struct inode *inode, struct file *file)
+{
+ spin_lock_irq(&rtc_lock);
+
+ if(rtc_status & RTC_IS_OPEN)
+ goto out_busy;
+
+ rtc_status |= RTC_IS_OPEN;
+
+ rtc_irq_data = 0;
+ spin_unlock_irq(&rtc_lock);
+ return 0;
+
+ out_busy:
+ spin_unlock_irq(&rtc_lock);
+ return -EBUSY;
+}
+
+static int rtc_fasync(int fd, struct file *filp, int on)
+{
+ return fasync_helper(fd, filp, on, &rtc_async_queue);
+}
+
+static int rtc_release(struct inode *inode, struct file *file)
+{
+ del_timer(&rtc_uie_timer);
+
+ if (file->f_flags & FASYNC)
+ rtc_fasync(-1, file, 0);
+
+ rtc_irq_data = 0;
+
+ /* No need for locking -- nobody else can do anything until this rmw is
+ * committed, and no timer is running. */
+ rtc_status &= ~(RTC_IS_OPEN | RTC_UIE_TIMER_ON);
+ return 0;
+}
+
+static unsigned int rtc_poll(struct file *file, poll_table *wait)
+{
+ unsigned long l;
+
+ poll_wait(file, &rtc_wait, wait);
+
+ spin_lock_irq(&rtc_lock);
+ l = rtc_irq_data;
+ spin_unlock_irq(&rtc_lock);
+
+ if (l != 0)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+/*
+ * The various file operations we support.
+ */
+
+static struct file_operations rtc_fops = {
+ owner: THIS_MODULE,
+ llseek: rtc_llseek,
+ read: rtc_read,
+ poll: rtc_poll,
+ ioctl: rtc_ioctl,
+ open: rtc_open,
+ release: rtc_release,
+ fasync: rtc_fasync,
+};
+
+static struct miscdevice rtc_dev=
+{
+ RTC_MINOR,
+ "rtc",
+ &rtc_fops
+};
+
+static int __init rtc_init(void)
+{
+ if (!request_region(UPD4990A_IO, 1, "rtc")) {
+ printk(KERN_ERR "upd4990a: could not acquire I/O port %#x\n",
+ UPD4990A_IO);
+ return -EBUSY;
+ }
+
+#if 0
+ printk(KERN_INFO "\xB6\xDA\xDD\xC0\xDE \xC4\xDE\xB9\xB2 Driver\n"); /* Calender Clock Driver */
+#else
+ printk(KERN_INFO
+ "Real Time Clock driver for NEC PC-9800 v" RTC98_VERSION "\n");
+#endif
+ misc_register(&rtc_dev);
+ create_proc_read_entry("driver/rtc", 0, NULL, rtc_read_proc, NULL);
+
+ init_timer(&rtc_uie_timer);
+ rtc_uie_timer.function = rtc_uie_intr;
+
+ return 0;
+}
+
+module_init (rtc_init);
+
+#ifdef MODULE
+static void __exit rtc_exit(void)
+{
+ del_timer(&rtc_uie_timer);
+ release_region(UPD4990A_IO, 1);
+ remove_proc_entry("driver/rtc", NULL);
+ misc_deregister(&rtc_dev);
+}
+
+module_exit (rtc_exit);
+#endif
+
+EXPORT_NO_SYMBOLS;
+
+/*
+ * Info exported via "/proc/driver/rtc".
+ */
+
+static inline int rtc_get_status(char *buf)
+{
+ char *p;
+ unsigned int year;
+ struct upd4990a_raw_data data;
+
+ p = buf;
+
+ upd4990a_get_time(&data, 0);
+
+ /*
+ * There is no way to tell if the luser has the RTC set for local
+ * time or for Universal Standard Time (GMT). Probably local though.
+ */
+ if ((year = BCD_TO_BINARY(data.year) + 1900) < 1995)
+ year += 100;
+ p += sprintf(p,
+ "rtc_time\t: %02d:%02d:%02d\n"
+ "rtc_date\t: %04d-%02d-%02d\n",
+ BCD_TO_BINARY(data.hour), BCD_TO_BINARY(data.min),
+ BCD_TO_BINARY(data.sec),
+ year, data.mon, BCD_TO_BINARY(data.mday));
+
+ return p - buf;
+}
+
+static int rtc_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = rtc_get_status(page);
+
+ if (len <= off + count)
+ *eof = 1;
+ *start = page + off;
+ len -= off;
+ if (len > count)
+ len = count;
+ if (len < 0)
+ len = 0;
+ return len;
+}
diff -urN linux/include/asm-i386/upd4990a.h linux98/include/asm-i386/upd4990a.h
--- linux/include/asm-i386/upd4990a.h Thu Jan 1 09:00:00 1970
+++ linux98/include/asm-i386/upd4990a.h Fri Aug 17 22:15:17 2001
@@ -0,0 +1,58 @@
+/*
+ * Architecture dependent definitions
+ * for NEC uPD4990A serial I/O real-time clock.
+ *
+ * Copyright 2001 TAKAI Kousuke <***@kmc.kyoto-u.ac.jp>
+ * Kyoto University Microcomputer Club (KMC).
+ *
+ * References:
+ * uPD4990A serial I/O real-time clock users' manual (Japanese)
+ * No. S12828JJ4V0UM00 (4th revision), NEC Corporation, 1999.
+ */
+
+#ifndef _ASM_I386_uPD4990A_H
+#define _ASM_I386_uPD4990A_H
+
+#include <linux/config.h>
+
+#ifdef CONFIG_PC9800
+
+#include <asm/io.h>
+
+#define UPD4990A_IO (0x0020)
+#define UPD4990A_IO_DATAOUT (0x0033)
+
+#define UPD4990A_OUTPUT_DATA_CLK(data, clk) \
+ outb((((data) & 1) << 5) | (((clk) & 1) << 4) \
+ | UPD4990A_PAR_SERIAL_MODE, UPD4990A_IO)
+
+#define UPD4990A_OUTPUT_CLK(clk) UPD4990A_OUTPUT_DATA_CLK(0, (clk))
+
+#define UPD4990A_OUTPUT_STROBE(stb) \
+ outb(((stb) << 3) | UPD4990A_PAR_SERIAL_MODE, UPD4990A_IO)
+
+/*
+ * Note: udelay() is *not* usable for UPD4990A_DELAY because
+ * the Linux kernel reads uPD4990A to set up system clock
+ * before calibrating delay...
+ */
+#define UPD4990A_DELAY(usec) \
+ do { \
+ if (__builtin_constant_p((usec)) && (usec) < 5) \
+ __asm__ (".rept %c1\n\toutb %%al,%0\n\t.endr" \
+ : : "N" (0x5F), \
+ "i" (((usec) * 10 + 5) / 6)); \
+ else { \
+ int _count = ((usec) * 10 + 5) / 6; \
+ __asm__ volatile ("1: outb %%al,%1\n\tloop 1b" \
+ : "=c" (_count) \
+ : "N" (0x5F), "0" (_count)); \
+ } \
+ } while (0)
+
+/* Caller should ignore all bits except bit0 */
+#define UPD4990A_READ_DATA() inb(UPD4990A_IO_DATAOUT)
+
+#endif /* CONFIG_PC9800 */
+
+#endif
diff -urN linux/include/linux/upd4990a.h linux98/include/linux/upd4990a.h
--- linux/include/linux/upd4990a.h Thu Jan 1 09:00:00 1970
+++ linux98/include/linux/upd4990a.h Fri Aug 17 22:15:48 2001
@@ -0,0 +1,140 @@
+/*
+ * Constant and architecture independent procedures
+ * for NEC uPD4990A serial I/O real-time clock.
+ *
+ * Copyright 2001 TAKAI Kousuke <***@kmc.kyoto-u.ac.jp>
+ * Kyoto University Microcomputer Club (KMC).
+ *
+ * References:
+ * uPD4990A serial I/O real-time clock users' manual (Japanese)
+ * No. S12828JJ4V0UM00 (4th revision), NEC Corporation, 1999.
+ */
+
+#ifndef _LINUX_uPD4990A_H
+#define _LINUX_uPD4990A_H
+
+#include <asm/byteorder.h>
+
+#include <asm/upd4990a.h>
+
+/* Serial commands (4 bits) */
+#define UPD4990A_REGISTER_HOLD (0x0)
+#define UPD4990A_REGISTER_SHIFT (0x1)
+#define UPD4990A_TIME_SET_AND_COUNTER_HOLD (0x2)
+#define UPD4990A_TIME_READ (0x3)
+#define UPD4990A_TP_64HZ (0x4)
+#define UPD4990A_TP_256HZ (0x5)
+#define UPD4990A_TP_2048HZ (0x6)
+#define UPD4990A_TP_4096HZ (0x7)
+#define UPD4990A_TP_1S (0x8)
+#define UPD4990A_TP_10S (0x9)
+#define UPD4990A_TP_30S (0xA)
+#define UPD4990A_TP_60S (0xB)
+#define UPD4990A_INTERRUPT_RESET (0xC)
+#define UPD4990A_INTERRUPT_TIMER_START (0xD)
+#define UPD4990A_INTERRUPT_TIMER_STOP (0xE)
+#define UPD4990A_TEST_MODE_SET (0xF)
+
+/* Parallel commands (3 bits)
+ 0-6 are same with serial commands. */
+#define UPD4990A_PAR_SERIAL_MODE 7
+
+#ifndef UPD4990A_DELAY
+# include <linux/delay.h>
+# define UPD4990A_DELAY(usec) udelay((usec))
+#endif
+#ifndef UPD4990A_OUTPUT_DATA
+# define UPD4990A_OUTPUT_DATA(bit) \
+ do { \
+ UPD4990A_OUTPUT_DATA_CLK((bit), 0); \
+ UPD4990A_DELAY(1); /* t-DSU */ \
+ UPD4990A_OUTPUT_DATA_CLK((bit), 1); \
+ UPD4990A_DELAY(1); /* t-DHLD */ \
+ } while (0)
+#endif
+
+static __inline__ void upd4990a_serial_command(int command)
+{
+ UPD4990A_OUTPUT_DATA(command >> 0);
+ UPD4990A_OUTPUT_DATA(command >> 1);
+ UPD4990A_OUTPUT_DATA(command >> 2);
+ UPD4990A_OUTPUT_DATA(command >> 3);
+ UPD4990A_DELAY(1); /* t-HLD */
+ UPD4990A_OUTPUT_STROBE(1);
+ UPD4990A_DELAY(1); /* t-STB & t-d1 */
+ UPD4990A_OUTPUT_STROBE(0);
+ /* 19 microseconds extra delay is needed
+ iff previous mode is TIME READ command */
+}
+
+struct upd4990a_raw_data {
+ u8 sec; /* BCD */
+ u8 min; /* BCD */
+ u8 hour; /* BCD */
+ u8 mday; /* BCD */
+#if defined __LITTLE_ENDIAN_BITFIELD
+ unsigned wday :4; /* 0-6 */
+ unsigned mon :4; /* 1-based */
+#elif defined __BIG_ENDIAN_BITFIELD
+ unsigned mon :4; /* 1-based */
+ unsigned wday :4; /* 0-6 */
+#else
+# error Unknown bitfield endian!
+#endif
+ u8 year; /* BCD */
+};
+
+static __inline__ void upd4990a_get_time(struct upd4990a_raw_data *buf,
+ int leave_register_hold)
+{
+ int byte;
+
+ upd4990a_serial_command(UPD4990A_TIME_READ);
+ upd4990a_serial_command(UPD4990A_REGISTER_SHIFT);
+ UPD4990A_DELAY(19); /* t-d2 - t-d1 */
+
+ for (byte = 0; byte < 6; byte++) {
+ u8 tmp;
+ int bit;
+
+ for (tmp = 0, bit = 0; bit < 8; bit++) {
+ tmp = (tmp | (UPD4990A_READ_DATA() << 8)) >> 1;
+ UPD4990A_OUTPUT_CLK(1);
+ UPD4990A_DELAY(1);
+ UPD4990A_OUTPUT_CLK(0);
+ UPD4990A_DELAY(1);
+ }
+ ((u8 *) buf)[byte] = tmp;
+ }
+
+ /* The uPD4990A users' manual says that we should issue `Register
+ Hold' command after each data retrieval, or next `Time Read'
+ command may not work correctly. */
+ if (!leave_register_hold)
+ upd4990a_serial_command(UPD4990A_REGISTER_HOLD);
+}
+
+static __inline__ void upd4990a_set_time(const struct upd4990a_raw_data *data,
+ int time_set_only)
+{
+ int byte;
+
+ if (!time_set_only)
+ upd4990a_serial_command(UPD4990A_REGISTER_SHIFT);
+
+ for (byte = 0; byte < 6; byte++) {
+ int bit;
+ u8 tmp = ((const u8 *) data)[byte];
+
+ for (bit = 0; bit < 8; bit++, tmp >>= 1)
+ UPD4990A_OUTPUT_DATA(tmp);
+ }
+
+ upd4990a_serial_command(UPD4990A_TIME_SET_AND_COUNTER_HOLD);
+
+ /* Release counter hold and start the clock. */
+ if (!time_set_only)
+ upd4990a_serial_command(UPD4990A_REGISTER_HOLD);
+}
+
+#endif /* _LINUX_uPD4990A_H */
Osamu Tomita
2002-11-03 00:05:20 UTC
Permalink
My bad! My mailer has broken some patches.
Please get patchset this URL.
http://downloads.sourceforge.jp/linux98/1480/linux98-2.5.45.patch.tar.bz2

Regards,
Osamu Tomita

Continue reading on narkive:
Loading...