linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary
@ 2003-02-17 13:43 Osamu Tomita
  2003-02-17 13:48 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (1/26) ALSA Osamu Tomita
                   ` (25 more replies)
  0 siblings, 26 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 13:43 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox, 'Christoph Hellwig'

This is patch's to support NEC PC-9800 subarchitecture
against 2.5.61.

Comments and test reports are wellcome.


Description:
 o alsa-pc98.patch (1/26)
   ALSA sound drivers for PC98.
 o apm.patch (2/26)
   APM support for PC98. Including PC98's BIOS bug fix.
 o arch-i386-mach-pc98.patch (3/26)
   Files under arch/i386/mach-pc9800 directory.
 o boot98.patch (4/26)
   Files under arch/i386/boot98 directory.
 o char_device.patch (5/26)
   Real time clock driver and printer driver for PC98.
 o console.patch (6/26)
   PC98 Standard console support (without japanese kanji character).
 o core-misc.patch (7/26)
   Small core patches for PC98.
 o core.patch (8/26)
   Core patches for PC98. Big changes using mach-* scheme.
 o dma.patch (9/26)
   DMA support for PC98.
 o floppy98-1.patch (10/26)
 o floppy98-2.patch (11/26)
   Driver for PC98 standard floppy disk drive.
 o fs.patch (12/26)
   FAT fs and partition table support for PC98.
 o ide.patch (13/26)
   PC98 standard IDE I/F support.
 o input.patch (14/26)
   Drivers for PC98 standard keyboard/mouse.
 o kanji.patch (15/26)
   japanese kanji character support for PC98 console.
 o network_card.patch (16/26)
   C-bus(PC98's legacy bus like ISA) network cards support.
 o parport.patch (17/26)
   Parallel port support.
 o pci.patch (18/26)
   Small changes for PCI support.
 o pcibios.patch (19/26)
   PCI BIOS function support using mach-* scheme.
 o pcmcia.patch (20/26)
   Small change for PCMCIA (16bits) support.
 o pnp.patch (21/26)
   Small change for Legacy bus PNP support.
 o reboot.patch (22/26)
   Support difference of machine reboot method, using mach-* scheme.
 o scsi.patch (23/26)
   SCSI host adapter support.
 o serial.patch (24/26)
   Serial port support for PC98.
 o smp.patch (25/26)
   SMP support for PC98.
 o video_card.patch (26/26)
   PC98 standard video card text mode driver.

Regards,
Osamu Tomita


^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (1/26) ALSA
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
@ 2003-02-17 13:48 ` Osamu Tomita
  2003-02-17 13:49 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (2/26) APM Osamu Tomita
                   ` (24 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 13:48 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox, Takashi Iwai

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (1/26).

ALSA sound drivers for PC98.

diff -Nru linux/sound/isa/Kconfig linux98/sound/isa/Kconfig
--- linux/sound/isa/Kconfig	2002-10-31 13:23:47.000000000 +0900
+++ linux98/sound/isa/Kconfig	2002-11-02 15:56:59.000000000 +0900
@@ -39,6 +39,12 @@
 	  Say 'Y' or 'M' to include support for CS4235,CS4236,CS4237B,CS4238B,CS4239
 	  chips from Cirrus Logic - Crystal Semiconductors.
 
+config SND_PC98_CS4232
+	tristate "NEC PC9800 CS4232 driver"
+	depends on SND
+	help
+	  Say 'Y' or 'M' to include support for NEC PC-9801/PC-9821 sound cards
+
 config SND_ES968
 	tristate "Generic ESS ES968 driver"
 	depends on SND && ISAPNP
diff -Nru linux-2.5.60/sound/isa/cs423x/Makefile linux98-2.5.60/sound/isa/cs423x/Makefile
--- linux-2.5.60/sound/isa/cs423x/Makefile	2003-02-11 03:38:51.000000000 +0900
+++ linux98-2.5.60/sound/isa/cs423x/Makefile	2003-02-11 10:26:12.000000000 +0900
@@ -8,6 +8,7 @@
 snd-cs4231-objs := cs4231.o
 snd-cs4232-objs := cs4232.o
 snd-cs4236-objs := cs4236.o
+snd-pc98-cs4232-objs := pc98.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_AZT2320) += snd-cs4231-lib.o
@@ -20,5 +21,6 @@
 obj-$(CONFIG_SND_INTERWAVE_STB) += snd-cs4231-lib.o
 obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-cs4231-lib.o
 obj-$(CONFIG_SND_WAVEFRONT) += snd-cs4231-lib.o
+obj-$(CONFIG_SND_PC98_CS4232) += snd-pc98-cs4232.o snd-cs4231-lib.o
 
 obj-m := $(sort $(obj-m))
diff -Nru linux/sound/isa/cs423x/pc98.c linux98/sound/isa/cs423x/pc98.c
--- linux/sound/isa/cs423x/pc98.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98/sound/isa/cs423x/pc98.c	2002-11-01 11:37:22.000000000 +0900
@@ -0,0 +1,466 @@
+/*
+ *  Driver for CS4232 on NEC PC9800 series
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *                   Osamu Tomita <tomita@cinet.co.jp>
+ *                   Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *   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 <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/cs4231.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include "sound_pc9800.h"
+
+#define chip_t cs4231_t
+
+MODULE_AUTHOR("Osamu Tomita <tomita@cinet.co.jp>");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DESCRIPTION("NEC PC9800 CS4232");
+MODULE_DEVICES("{{NEC,PC9800}}");
+
+#define IDENT "PC98-CS4232"
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+#if 0 /* NOT USED */
+static long cport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+#endif
+static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* PnP setup */
+static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
+static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,11,12,15 */
+static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 9,11,12,15 */
+static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
+static int pc98ii[SNDRV_CARDS];				/* PC98II */
+
+MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(index, "Index value for " IDENT " soundcard.");
+MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
+MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(id, "ID string for " IDENT " soundcard.");
+MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
+MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(enable, "Enable " IDENT " soundcard.");
+MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(port, "Port # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC);
+#if 0 /* NOT USED */
+MODULE_PARM(cport, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(cport, "Control port # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(cport, SNDRV_PORT12_DESC);
+#endif
+MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC);
+MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(fm_port, "FM port # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT12_DESC);
+MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(irq, "IRQ # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC);
+MODULE_PARM(mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC);
+MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(dma1, "DMA1 # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC);
+MODULE_PARM(dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(dma2, "DMA2 # for " IDENT " driver.");
+MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC);
+MODULE_PARM(pc98ii, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(pc98ii, "Roland MPU-PC98II support.");
+MODULE_PARM_SYNTAX(pc98ii, SNDRV_BOOLEAN_FALSE_DESC);
+
+
+static snd_card_t *snd_pc98_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+/*
+ * initialize MPU401-UART
+ */
+
+static int __init pc98_mpu401_init(int irq)
+{
+#include "pc9801_118_magic.h"
+#define outp118(reg,data) outb((reg),0x148e);outb((data),0x148f)
+#define WAIT118 outb(0x00,0x5f)
+	int	mpu_intr, count;
+#ifdef OOKUBO_ORIGINAL
+	int	err = 0;
+#endif /* OOKUBO_ORIGINAL */
+
+	switch (irq) {
+	case 3:
+		mpu_intr = 3;
+		break;
+	case 5:
+		mpu_intr = 2;
+		break;
+	case 6:
+		mpu_intr = 1;
+		break;
+	case 10:
+		mpu_intr = 0;
+		break;
+	default:
+		snd_printk(KERN_ERR IDENT ": Bad IRQ %d\n", irq);
+		return -EINVAL;
+	}
+
+	outp118(0x21, mpu_intr);
+	WAIT118;
+	outb(0x00, 0x148e);
+	if (inb(0x148f) & 0x08) {
+		snd_printk(KERN_INFO IDENT ": No MIDI daughter board found\n");
+		return 0;
+	}
+
+	outp118(0x20, 0x00);
+	outp118(0x05, 0x04);
+	for (count = 0; count < 35000; count ++)
+		WAIT118;
+	outb(0x05, 0x148e);
+	for (count = 0; count < 65000; count ++)
+		if (inb(0x148f) == 0x04)
+			goto set_mode_118;
+	snd_printk(KERN_ERR IDENT ": MIDI daughter board initalize failed at stage1\n\n");
+	return -EINVAL;
+
+ set_mode_118:
+	outp118(0x05, 0x0c);
+	outb(0xaa, 0x485);
+	outb(0x99, 0x485);
+	outb(0x2a, 0x485);
+	for (count = 0; count < sizeof(Data0485_99); count ++) {
+		outb(Data0485_99[count], 0x485);
+		WAIT118;
+	}
+
+	outb(0x00, 0x486);
+	outb(0xaa, 0x485);
+	outb(0x9e, 0x485);
+	outb(0x2a, 0x485);
+	for (count = 0; count < sizeof(Data0485_9E); count ++)
+		if (inb(0x485) != Data0485_9E[count]) {
+#ifdef OOKUBO_ORIGINAL
+			err = 1;
+#endif /* OOKUBO_ORIGINAL */
+			break;
+		}
+	outb(0x00, 0x486);
+	for (count = 0; count < 2000; count ++)
+		WAIT118;
+#ifdef OOKUBO_ORIGINAL
+	if (!err) {
+		outb(0xaa, 0x485);
+		outb(0x36, 0x485);
+		outb(0x28, 0x485);
+		for (count = 0; count < sizeof(Data0485_36); count ++)
+			outb(Data0485_36[count], 0x485);
+		outb(0x00, 0x486);
+		for (count = 0; count < 1500; count ++)
+			WAIT118;
+		outp118(0x05, inb(0x148f) | 0x08);
+		outb(0xff, 0x148c);
+		outp118(0x05, inb(0x148f) & 0xf7);
+		for (count = 0; count < 1500; count ++)
+			WAIT118;
+	}
+#endif /* OOKUBO_ORIGINAL */
+
+	outb(0xaa, 0x485);
+	outb(0xa9, 0x485);
+	outb(0x21, 0x485);
+	for (count = 0; count < sizeof(Data0485_A9); count ++) {
+		outb(Data0485_A9[count], 0x485);
+		WAIT118;
+	}
+
+	outb(0x00, 0x486);
+	outb(0xaa, 0x485);
+	outb(0x0c, 0x485);
+	outb(0x20, 0x485);
+	for (count = 0; count < sizeof(Data0485_0C); count ++) {
+		outb(Data0485_0C[count], 0x485);
+		WAIT118;
+	}
+
+	outb(0x00, 0x486);
+	outb(0xaa, 0x485);
+	outb(0x66, 0x485);
+	outb(0x20, 0x485);
+	for (count = 0; count < sizeof(Data0485_66); count ++) {
+		outb(Data0485_66[count], 0x485);
+		WAIT118;
+	}
+
+	outb(0x00, 0x486);
+	outb(0xaa, 0x485);
+	outb(0x60, 0x485);
+	outb(0x20, 0x485);
+	for (count = 0; count < sizeof(Data0485_60); count ++) {
+		outb(Data0485_60[count], 0x485);
+		WAIT118;
+	}
+
+	outb(0x00, 0x486);
+	outp118(0x05, 0x04);
+	outp118(0x05, 0x00);
+	for (count = 0; count < 35000; count ++)
+		WAIT118;
+	outb(0x05, 0x148e);
+	for (count = 0; count < 65000; count ++)
+		if (inb(0x148f) == 0x00)
+			goto end_mode_118;
+	snd_printk(KERN_ERR IDENT ": MIDI daughter board initalize failed at stage2\n");
+	return -EINVAL;
+
+ end_mode_118:
+	outb(0x3f, 0x148d);
+	snd_printk(KERN_INFO IDENT ": MIDI daughter board initalized\n");
+	return 0;
+}
+
+static int __init pc98_cs4231_chip_init(int dev)
+{
+	int intr_bits, intr_bits2, dma_bits;
+
+	switch (irq[dev]) {
+	case 3:
+		intr_bits = 0x08;
+		intr_bits2 = 0x03;
+		break;
+	case 5:
+		intr_bits = 0x10;
+		intr_bits2 = 0x08;
+		break;
+	case 10:
+		intr_bits = 0x18;
+		intr_bits2 = 0x02;
+		break;
+	case 12:
+		intr_bits = 0x20;
+		intr_bits2 = 0x00;
+		break;
+	default:
+		snd_printk(KERN_ERR IDENT ": Bad IRQ %d\n", irq[dev]);
+		return -EINVAL;
+	}
+
+	switch (dma1[dev]) {
+	case 0:
+		dma_bits = 0x01;
+		break;
+	case 1:
+		dma_bits = 0x02;
+		break;
+	case 3:
+		dma_bits = 0x03;
+		break;
+	default:
+		snd_printk(KERN_ERR IDENT ": Bad DMA %d\n", dma1[dev]);
+		return -EINVAL;
+	}
+
+	if (dma2[dev] >= 2) {
+		snd_printk(KERN_ERR IDENT ": Bad DMA %d\n", dma2[dev]);
+		return -EINVAL;
+	}
+	if (dma1[dev] != dma2[dev] && dma2[dev] >= 0)
+		intr_bits |= 0x04;
+
+	if (PC9800_SOUND_ID() == PC9800_SOUND_ID_118) {
+		/* Set up CanBe control registers. */
+		snd_printd(KERN_INFO "Setting up CanBe Sound System\n");
+		outb(inb(PC9800_SOUND_IO_ID) | 0x03, PC9800_SOUND_IO_ID);
+		outb(0x01, 0x0f4a);
+		outb(intr_bits2, 0x0f4b);
+	}
+
+	outb(intr_bits | dma_bits, 0xf40);
+	return 0;
+}
+
+
+static int __init snd_card_pc98_probe(int dev)
+{
+	snd_card_t *card;
+	snd_pcm_t *pcm = NULL;
+	cs4231_t *chip;
+	opl3_t *opl3;
+	int err;
+
+	if (port[dev] == SNDRV_AUTO_PORT) {
+		snd_printk(KERN_ERR IDENT ": specify port\n");
+		return -EINVAL;
+	}
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	if (mpu_port[dev] < 0 || mpu_irq[dev] < 0)
+		mpu_port[dev] = SNDRV_AUTO_PORT;
+	if (fm_port[dev] < 0)
+		fm_port[dev] = SNDRV_AUTO_PORT;
+
+	if ((err = pc98_cs4231_chip_init(dev)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_cs4231_create(card,
+				     port[dev],
+				     -1,
+				     irq[dev],
+				     dma1[dev],
+				     dma2[dev],
+				     CS4231_HW_DETECT,
+				     0,
+				     &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_cs4231_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (fm_port[dev] != SNDRV_AUTO_PORT) {
+		/* ??? */
+		outb(0x00, fm_port[dev] + 6);
+		inb(fm_port[dev] + 7);
+		/* Enable OPL-3 Function */
+		outb(inb(PC9800_SOUND_IO_ID) | 0x03, PC9800_SOUND_IO_ID);
+		if (snd_opl3_create(card,
+				    fm_port[dev], fm_port[dev] + 2,
+				    OPL3_HW_OPL3_PC98, 0, &opl3) < 0) {
+			printk(KERN_ERR IDENT ": OPL3 not detected\n");
+		} else {
+			if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+				snd_card_free(card);
+				return err;
+			}
+		}
+	}
+
+	if (mpu_port[dev] != SNDRV_AUTO_PORT) {
+		err = pc98_mpu401_init(mpu_irq[dev]);
+		if (! err) {
+			err = snd_mpu401_uart_new(card, 0,
+						  pc98ii[dev] ? MPU401_HW_PC98II : MPU401_HW_MPU401,
+						  mpu_port[dev], 0,
+						  mpu_irq[dev], SA_INTERRUPT, NULL);
+			if (err < 0)
+				snd_printk(KERN_INFO IDENT ": MPU401 not detected\n");
+		}
+	}
+
+	strcpy(card->driver, pcm->name);
+	strcpy(card->shortname, pcm->name);
+	sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i",
+		pcm->name,
+		chip->port,
+		irq[dev],
+		dma1[dev]);
+	if (dma1[dev] >= 0)
+		sprintf(card->longname + strlen(card->longname), "&%d", dma2[dev]);
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_pc98_cards[dev] = card;
+	return 0;
+}
+
+static int __init alsa_card_pc98_init(void)
+{
+	int dev, cards = 0;
+
+	for (dev = 0; dev < SNDRV_CARDS; dev++) {
+		if (!enable[dev])
+			continue;
+		if (snd_card_pc98_probe(dev) >= 0)
+			cards++;
+	}
+	if (!cards) {
+#ifdef MODULE
+		printk(KERN_ERR IDENT " soundcard not found or device busy\n");
+#endif
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_pc98_exit(void)
+{
+	int idx;
+
+	for (idx = 0; idx < SNDRV_CARDS; idx++)
+		snd_card_free(snd_pc98_cards[idx]);
+}
+
+module_init(alsa_card_pc98_init)
+module_exit(alsa_card_pc98_exit)
+
+#ifndef MODULE
+
+/* format is: snd-pc98-cs4232=enable,index,id,port,
+			 mpu_port,fm_port,
+			 irq,mpu_irq,dma1,dma2,pc98ii */
+
+static int __init alsa_card_pc98_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void)(get_option(&str,&enable[nr_dev]) == 2 &&
+	       get_option(&str,&index[nr_dev]) == 2 &&
+	       get_id(&str,&id[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,(int *)&fm_port[nr_dev]) == 2 &&
+	       get_option(&str,&irq[nr_dev]) == 2 &&
+	       get_option(&str,&mpu_irq[nr_dev]) == 2 &&
+	       get_option(&str,&dma1[nr_dev]) == 2 &&
+	       get_option(&str,&dma2[nr_dev]) == 2 &&
+	       get_option(&str,&pc98ii[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-pc98-cs4232=", alsa_card_pc98_setup);
+
+#endif /* ifndef MODULE */
diff -Nru linux/sound/isa/cs423x/pc9801_118_magic.h linux/sound/isa/cs423x/pc9801_118_magic.h
--- linux/sound/isa/cs423x/pc9801_118_magic.h	1970-01-01 09:00:00.000000000 +0100
+++ linux/sound/isa/cs423x/pc9801_118_magic.h	2002-10-28 15:44:12.000000000 +0100
@@ -0,0 +1,411 @@
+		static unsigned char	Data0485_A9[] = {
+		0x12, 0x03, 0x90, 0xc2, 0x2a, 0x75, 0x1e, 0x20,
+		0xe4, 0x12, 0x2b, 0x9b, 0x22, 0xa9, 0x16, 0x77,
+		0x33, 0xe9, 0x04, 0x54, 0x03, 0x44, 0xa8, 0xf5,
+		0x16, 0xc2, 0x2f, 0x22, 0xa9, 0x16, 0x77, 0x42,
+		0xe9, 0x04, 0x54, 0x03, 0x44, 0xa8, 0xf9, 0x77,
+		0xf8, 0x04, 0x54, 0x03, 0x44, 0xa8, 0xf5, 0x16,
+		0xc2, 0x2f, 0x22, 0x90, 0x25, 0x9f, 0x30, 0x04,
+		0x05, 0xc2, 0x04, 0x12, 0x1f, 0x62, 0x30, 0x00,
+		0x05, 0xc2, 0x00, 0x12, 0x15, 0xe6, 0x30, 0x01,
+		0x05, 0xc2, 0x01, 0x12, 0x29, 0xaf, 0x30, 0x02,
+		0x05, 0xc2, 0x02, 0x12, 0x29, 0xaf, 0x30, 0x05,
+		0x05, 0xc2, 0x05, 0x12, 0x16, 0x65, 0x30, 0x06,
+		0x08, 0xc2, 0x06, 0x12, 0x16, 0xb1, 0x12, 0x29,
+		0xaf, 0x30, 0x07, 0x08, 0xc2, 0x07, 0x12, 0x16,
+		0xe9, 0x12, 0x29, 0xaf, 0x22, 0x20, 0x97, 0x09,
+		0x53, 0xa8, 0xfb, 0x12, 0x04, 0x2c, 0x43, 0xa8,
+		0x04, 0x22, 0x71, 0xb8, 0x71, 0xb8, 0x71, 0xb8,
+		0x22, 0x20, 0x4b, 0x04, 0x75, 0x4e, 0x02, 0x22,
+		0xe5, 0x35, 0x24, 0xff, 0xf5, 0x35, 0xe5, 0x36,
+		0x34, 0xff, 0xf5, 0x36, 0x75, 0x4e, 0x02, 0x22,
+		0x10, 0x19, 0x02, 0x80, 0x08, 0x78, 0x00, 0xe2,
+		0x78, 0x07, 0xf2, 0x61, 0x9b, 0x78, 0x11, 0xe2,
+		0xc0, 0x01, 0xc0, 0xf0, 0xc0, 0xd0, 0xc0, 0x02,
+		0x71, 0x14, 0xe5, 0x30, 0xb4, 0x01, 0x02, 0x61,
+		0x93, 0x43, 0x08, 0x40, 0x12, 0x2a, 0x53, 0x61,
+		0x93, 0x79, 0x03, 0xe3, 0xa2, 0xe2, 0x92, 0x26,
+		0xa2, 0xe3, 0x92, 0x27, 0x22, 0xad, 0x2b, 0xbd,
+		0x04, 0x07, 0xf5, 0x72, 0x78, 0x27, 0x02, 0x11,
+		0x76, 0x02, 0x11, 0x30, 0x00, 0x00, 0x00, 0x12,
+		0x28, 0xba, 0x79, 0x01, 0xe3, 0x75, 0x21, 0x3f,
+		0x75, 0x49, 0x11, 0x75, 0x4c, 0x11, 0x31, 0xdc,
+		0x75, 0x1a, 0x80, 0x51, 0x72, 0x75, 0x81, 0xe3,
+		0x12, 0x25, 0xc9, 0x43, 0xa8, 0x01, 0x00, 0x53,
+		0xa8, 0xfe, 0x10, 0x50, 0x02, 0x80, 0x03, 0x12,
+		0x1a, 0x8d, 0xd1, 0x28, 0x12, 0x03, 0xd9, 0xd1,
+		0xf2, 0x12, 0x2d, 0xf0, 0xb0, 0x11, 0x92, 0xe0,
+		0xa2, 0x2a, 0xa0, 0xb5, 0x82, 0xe0, 0x50, 0x03,
+		0x79, 0x0f, 0xe3, 0x71, 0xca, 0x51, 0x1e, 0x91,
+		0xe4, 0x53, 0xa8, 0xfb, 0x10, 0x10, 0x02, 0x80,
+		0x26, 0xc2, 0x8e, 0xd2, 0xab, 0xa2, 0x1c, 0x40,
+		0x13, 0xa2, 0x1d, 0x50, 0x0a, 0x43, 0x08, 0x40,
+		0x12, 0x1a, 0x01, 0xd1, 0xd7, 0x80, 0x0b, 0x12,
+		0x26, 0x04, 0x61, 0x08, 0x43, 0x08, 0x40, 0x12,
+		0x1a, 0x01, 0xd2, 0x1f, 0x12, 0x17, 0x7f, 0x43,
+		0xa8, 0x04, 0x51, 0x1e, 0x91, 0xe4, 0x12, 0x13,
+		0x34, 0x80, 0x98, 0xa2, 0x17, 0x72, 0x16, 0x72,
+		0x15, 0x72, 0x2d, 0x50, 0x06, 0xfa, 0x12, 0x13,
+		0x66, 0x80, 0x25, 0xc2, 0x13, 0x30, 0x28, 0x05,
+		0x12, 0x02, 0xbe, 0x80, 0x1b, 0xb4, 0x10, 0x12,
+		0x78, 0x00, 0xf2, 0xe5, 0x30, 0xb4, 0x01, 0x06,
+		0x12, 0x03, 0x90, 0xd2, 0x19, 0x22, 0x12, 0x00,
+		0xdd, 0x22, 0x75, 0x30, 0x00, 0x12, 0x00, 0xa1,
+		0x22, 0x00, 0x00, 0x75, 0x1e, 0x00, 0x74, 0x0c,
+		0x12, 0x2b, 0x9b, 0x74, 0x40, 0x79, 0x05, 0xf3,
+		0x74, 0x49, 0x12, 0x2b, 0x9b, 0x74, 0x04, 0x79,
+		0x05, 0xf3, 0x75, 0x15, 0x04, 0x74, 0x10, 0x12,
+		0x2b, 0x9b, 0x74, 0x00, 0x79, 0x05, 0xf3, 0x74,
+		0x17, 0x12, 0x2b, 0x9b, 0x74, 0x00, 0x79, 0x05,
+		0xf3, 0x74, 0x1a, 0x12, 0x2b, 0x9b, 0x74, 0x00,
+		0x79, 0x05, 0xf3, 0x74, 0x0a, 0x12, 0x2b, 0x9b,
+		0x74, 0x20, 0x79, 0x05, 0xf3, 0x79, 0xe0, 0x77,
+		0x20, 0x22, 0xd0, 0x02, 0xd0, 0xd0, 0xd0, 0xf0,
+		0xd0, 0x01, 0xe5, 0x5f, 0xd0, 0xa8, 0x22, 0x00,
+		0x00, 0x90, 0x25, 0x9f, 0x75, 0x26, 0xff, 0x75,
+		0x27, 0xff, 0x75, 0x28, 0x03, 0x75, 0x13, 0xff,
+		0x75, 0x1f, 0x00, 0x75, 0x14, 0xff, 0x22, 0x79,
+		0x06, 0xe5, 0x29, 0x60, 0x0b, 0xe3, 0x30, 0xe1,
+		0xf8, 0xe5, 0x4f, 0x64, 0x80, 0x79, 0x07, 0xf3,
+		0x22, 0x10, 0x4c, 0x01, 0x22, 0x30, 0x4b, 0x0a,
+		0xc2, 0x4b, 0xe5, 0x4d, 0x64, 0x80, 0xf5, 0x4f,
+		0x80, 0x1d, 0xe5, 0x15, 0xa2, 0xe0, 0x82, 0xe6,
+		0x40, 0x02, 0x80, 0x35, 0x30, 0x4a, 0x04, 0xb1,
+		0xe6, 0x80, 0x0c, 0x30, 0x49, 0x04, 0x51, 0x2b,
+		0x80, 0x05, 0x30, 0x48, 0x24, 0x91, 0x7e, 0x79,
+		0x06, 0xe3, 0x30, 0xe0, 0x1a, 0x79, 0x06, 0xf3,
+		0xe5, 0x4e, 0x24, 0xff, 0x50, 0x04, 0xf5, 0x4e,
+		0x80, 0x0d, 0x79, 0x0f, 0xf3, 0x20, 0x2a, 0x07,
+		0x12, 0x2b, 0x32, 0x75, 0x29, 0x00, 0x22, 0x91,
+		0x1b, 0x22, 0x79, 0x0f, 0xe3, 0xc0, 0xa8, 0x75,
+		0xa8, 0x00, 0x30, 0x2b, 0x03, 0xd0, 0xa8, 0x22,
+		0x79, 0x0e, 0xf3, 0xd0, 0xa8, 0x22, 0x8a, 0xf0,
+		0xe5, 0x50, 0x10, 0xf3, 0x10, 0x23, 0x23, 0x23,
+		0x25, 0xf0, 0x12, 0x2c, 0xb8, 0xa2, 0xe7, 0x92,
+		0xe4, 0xc2, 0xe7, 0x80, 0x08, 0x23, 0x23, 0x23,
+		0x25, 0xf0, 0x12, 0x2c, 0x19, 0x25, 0x4f, 0x20,
+		0xd2, 0x04, 0xf5, 0x4f, 0x80, 0x0a, 0x40, 0x05,
+		0x75, 0x4f, 0x7f, 0x80, 0x03, 0x75, 0x4f, 0xff,
+		0xea, 0x12, 0x2c, 0x3c, 0x25, 0x50, 0x20, 0xe7,
+		0x05, 0xb4, 0x03, 0x07, 0x80, 0x0c, 0x75, 0x50,
+		0x00, 0x80, 0x09, 0x40, 0x05, 0x75, 0x50, 0x03,
+		0x80, 0x02, 0xf5, 0x50, 0x22, 0xe5, 0x4d, 0xc4,
+		0x54, 0x0c, 0x03, 0x03, 0xfa, 0x91, 0xa9, 0x71,
+		0xb8, 0xe5, 0x4d, 0xc4, 0x54, 0x03, 0xfa, 0x91,
+		0xa9, 0x71, 0xb8, 0xe5, 0x4d, 0x54, 0x0c, 0x03,
+		0x03, 0xfa, 0x91, 0xa9, 0x71, 0xb8, 0xe5, 0x4d,
+		0x54, 0x03, 0xfa, 0x91, 0xa9, 0x71, 0xb8, 0x22,
+		0x8a, 0xf0, 0xe5, 0x50, 0x23, 0x23, 0x25, 0xf0,
+		0x12, 0x2b, 0xf6, 0x25, 0x4f, 0x20, 0xd2, 0x04,
+		0xf5, 0x4f, 0x80, 0x0a, 0x40, 0x05, 0x75, 0x4f,
+		0x7f, 0x80, 0x03, 0x75, 0x4f, 0xff, 0xea, 0x12,
+		0x2c, 0x40, 0x25, 0x50, 0x20, 0xe7, 0x05, 0xb4,
+		0x05, 0x07, 0x80, 0x0c, 0x75, 0x50, 0x00, 0x80,
+		0x09, 0x40, 0x05, 0x75, 0x50, 0x05, 0x80, 0x02,
+		0xf5, 0x50, 0x22, 0x30, 0x26, 0x03, 0x12, 0x1e,
+		0xf5, 0x30, 0x27, 0x03, 0x12, 0x1f, 0x37, 0x30,
+		0x25, 0x09, 0x12, 0x1f, 0x4e, 0x30, 0x23, 0x03,
+		0x12, 0x1f, 0x1e, 0x10, 0x22, 0x02, 0x80, 0x0a,
+		0xe5, 0x3b, 0xb4, 0xff, 0x02, 0xc2, 0x20, 0x12,
+		0x1e, 0x79, 0x22, 0x78, 0x11, 0xe2, 0x20, 0xe0,
+		0x07, 0xc0, 0x01, 0x12, 0x28, 0xba, 0xd0, 0x01,
+		0x78, 0x00, 0xf2, 0x61, 0x9b, 0x12, 0x2b, 0x32,
+		0x12, 0x17, 0x7f, 0x78, 0x00, 0xf2, 0xaa, 0x35,
+		0xab, 0x36, 0xea, 0x24, 0xff, 0xfa, 0xeb, 0x34,
+		0xff, 0xfb, 0x50, 0x03, 0xd2, 0x10, 0x22, 0x75,
+		0x37, 0x01, 0x75, 0x38, 0x00, 0x75, 0x39, 0x00,
+		0x12, 0x04, 0x04, 0xd2, 0x8e, 0x22, 0xa8, 0x2b,
+		0xb8, 0x00, 0x02, 0x80, 0x03, 0x02, 0x11, 0xbd,
+		0xf5, 0x74, 0x78, 0x2a, 0x12, 0x11, 0xec, 0xe5,
+		0x74, 0x78, 0x29, 0x12, 0x11, 0xec, 0x22, 0xfa,
+		0xe5, 0x2b, 0x60, 0x01, 0x22, 0xea, 0x78, 0x2b,
+		0xf5, 0x75, 0x12, 0x11, 0xec, 0x22, 0x74, 0x10,
+		0x12, 0x2b, 0x9b, 0x74, 0x20, 0x78, 0x05, 0xf2,
+		0x74, 0x09, 0x12, 0x17, 0x75, 0xe5, 0x15, 0x44,
+		0x80, 0x79, 0x05, 0xf3, 0xf5, 0x15, 0x12, 0x17,
+		0x7f, 0x22, 0x12, 0x03, 0x84, 0x79, 0x0f, 0xe3,
+		0x78, 0x00, 0xf2, 0x12, 0x2b, 0x28, 0xe5, 0x81,
+		0x24, 0xfc, 0xf5, 0x81, 0x61, 0x93, 0xd2, 0x07,
+		0x78, 0x11, 0xe2, 0x44, 0x11, 0xf5, 0x4c, 0xc2,
+		0x0f, 0x12, 0x29, 0xa3, 0x61, 0x93, 0x02, 0x1b,
+		0x77, 0x00, 0xe1, 0x81, 0xe1, 0x9a, 0xd2, 0x2c,
+		0xa1, 0x0c, 0x20, 0x20, 0x02, 0xd2, 0x26, 0x02,
+		0x1e, 0x35, 0x02, 0x1e, 0x61, 0x02, 0x1d, 0x8f,
+		0xc2, 0x8e, 0x75, 0xa8, 0x9e, 0x22, 0x41, 0x49,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x02, 0x29, 0x91, 0x00, 0x00, 0x00, 0xa1, 0xbb,
+		0xa1, 0xc3, 0x02, 0x1e, 0x6b, 0xe5, 0x4d, 0xc4,
+		0x54, 0x0f, 0xfa, 0x91, 0x2f, 0x71, 0xb8, 0xe5,
+		0x4d, 0x54, 0x0f, 0xfa, 0x91, 0x2f, 0x71, 0xb8,
+		0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa1, 0xc6,
+		0x02, 0x1d, 0x8f, 0xc2, 0x8e, 0xd2, 0xab, 0xc2,
+		0x10, 0x79, 0x0f, 0xf3, 0x22, 0x00, 0x02, 0x2a,
+		0x84, 0x00, 0xe1, 0xbc, 0xe1, 0xc8, 0x02, 0x1e,
+		0x27, 0x00, 0x78, 0x00, 0xf2, 0x78, 0x0b, 0xe2,
+		0xf4, 0xf5, 0x4d, 0xd2, 0x4c, 0x61, 0x9b, 0x30,
+		0xb5, 0x02, 0xc2, 0x11, 0x22, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x79, 0xbd, 0xf1, 0x3d, 0x83,
+		0x22, 0xdd, 0xbd, 0xbd, 0xbd, 0x61, 0xbd, 0x8d,
+		0x7a, 0xbd, 0xbd, 0xbd, 0xbd, 0x30, 0xbd, 0xbd,
+		0xbd, 0x55, 0xbd, 0xbd, 0xbd, 0x52, 0xbd, 0xb6,
+		0xb6, 0xbd, 0xbd, 0xbd, 0xbd, 0x00, 0xbd, 0xbd,
+		0xbd, 0xe8, 0xda, 0xbd, 0xbd, 0xcf, 0xb9, 0xbd,
+		0xc4, 0xf1, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+		0xbd, 0x7b, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+		0xbd, 0x70, 0x6a, 0x57, 0x47, 0x34, 0xbd, 0xbd,
+		0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0x29, 0xbd,
+		0xbd, 0xbd, 0xb6, 0xb6, 0xbd, 0xbd, 0xbd, 0xbd,
+		0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0x2e, 0x25,
+		0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xfe, 0xf5,
+		0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0x19, 0xbd,
+		0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0x21, 0x8f,
+		0x09, 0xbd, 0xf9, 0x86, 0xbd, 0xbd, 0xbd, 0xd7,
+		0xbd, 0xa9, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0x9b,
+		0xd1, 0x9d, 0xbd, 0xae, 0xbd, 0xbd, 0xbd, 0xcb,
+		0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+		0xb6, 0xa5, 0xbd, 0xc5, 0xbd, 0xbd, 0xbd, 0xc3,
+		0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0x74, 0x10,
+		0x12, 0x2b, 0x9b, 0xe4, 0x78, 0x05, 0xf2, 0x74,
+		0x09, 0x12, 0x17, 0x75, 0xe5, 0x15, 0x54, 0x7f,
+		0x79, 0x05, 0xf3, 0xf5, 0x15, 0x12, 0x17, 0x7f,
+		0x22, 0x30, 0x51, 0x01, 0x22, 0x53, 0xa8, 0xfb,
+		0x12, 0x2d, 0xf0, 0x50, 0x22, 0x79, 0x03, 0xe3,
+		0x20, 0xe4, 0x1c, 0xaa, 0x35, 0xab, 0x36, 0xea,
+		0x24, 0xf0, 0xfa, 0xeb, 0x34, 0xff, 0xfb, 0x50,
+		0x0e, 0x10, 0x1f, 0x02, 0x80, 0x09, 0x20, 0x2a,
+		0x03, 0x12, 0x2b, 0x32, 0x12, 0x2d, 0xd6, 0x43,
+		0xa8, 0x04, 0x22, 0xa2, 0x1c, 0x72, 0x1d, 0x40,
+		0x07, 0x53, 0x08, 0xbf, 0x78, 0x00, 0xf2, 0x22,
+		0xb1, 0x1e, 0x22, 0x00, 0x79, 0x02, 0x12, 0x27,
+		0x3d, 0x02, 0x2d, 0x37, 0x14, 0x54, 0xf0, 0x60,
+		0x21, 0xe5, 0xf0, 0x24, 0xb6, 0xe5, 0xf0, 0x50,
+		0x16, 0x24, 0x8b, 0x50, 0x15, 0xe5, 0xf0, 0x24,
+		0x56, 0xe5, 0xf0, 0x50, 0x08, 0x24, 0x2f, 0x50,
+		0x09, 0xe5, 0xf0, 0x24, 0xd9, 0x24, 0xd5, 0x24,
+		0xf0, 0x22, 0x15, 0x81, 0x15, 0x81, 0xe9, 0x22,
+		0x78, 0x13, 0x74, 0x00, 0xf2, 0x75, 0x2e, 0x01,
+		0xd2, 0x6a, 0xc2, 0x69, 0xc2, 0x68, 0xc2, 0x6c,
+		0x90, 0x25, 0x9f, 0x75, 0xb8, 0x07, 0x41, 0xa4,
+		0xc0, 0x01, 0xc0, 0xf0, 0xc0, 0xd0, 0xc0, 0x02,
+		0xe5, 0x3d, 0x54, 0x7d, 0x03, 0x10, 0xe5, 0x05,
+		0x90, 0x28, 0x4b, 0x80, 0x03, 0x90, 0x2b, 0x7c,
+		0x73, 0xe5, 0x3d, 0x30, 0xe5, 0x07, 0x74, 0xfd,
+		0x78, 0x00, 0xf2, 0x61, 0x9b, 0x90, 0x1a, 0x97,
+		0x74, 0xb6, 0xc0, 0xe0, 0x74, 0x27, 0xc0, 0xe0,
+		0xc0, 0xa8, 0x02, 0x1b, 0xab, 0x90, 0x25, 0x9f,
+		0xd0, 0xa8, 0x22, 0x90, 0x27, 0xb6, 0xc0, 0x82,
+		0xc0, 0x83, 0xc0, 0xa8, 0x02, 0x1d, 0xa6, 0x90,
+		0x27, 0xb6, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0xa8,
+		0x02, 0x1e, 0x0a, 0xea, 0x24, 0xf0, 0xfa, 0xeb,
+		0x34, 0xff, 0xfb, 0x50, 0x2e, 0x20, 0x0b, 0x05,
+		0x85, 0x44, 0xe0, 0x80, 0x03, 0x75, 0xe0, 0x00,
+		0x30, 0xe1, 0x20, 0xe5, 0x35, 0x24, 0xff, 0xf5,
+		0x35, 0xe5, 0x36, 0x34, 0xff, 0xf5, 0x36, 0xc3,
+		0xe5, 0x36, 0x13, 0xf5, 0x36, 0xe5, 0x35, 0x13,
+		0xf5, 0x35, 0x75, 0x3a, 0x10, 0x12, 0x1a, 0x77,
+		0x02, 0x18, 0x77, 0x75, 0x3a, 0x00, 0x12, 0x1a,
+		0x77, 0x02, 0x18, 0x1b, 0x20, 0x4b, 0x04, 0x75,
+		0x4e, 0x03, 0x22, 0xe5, 0x35, 0x24, 0xff, 0xf5,
+		0x35, 0xe5, 0x36, 0x34, 0xff, 0xf5, 0x36, 0x75,
+		0x4e, 0x03, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x02, 0x2c,
+		0x70, 0xd2, 0x00, 0x78, 0x11, 0xe2, 0x44, 0x11,
+		0xf5, 0x3f, 0xc2, 0x08, 0x12, 0x29, 0xa3, 0x02,
+		0x23, 0x93, 0x21, 0x62, 0x61, 0x40, 0x01, 0x3a,
+		0x01, 0x73, 0x21, 0x76, 0x61, 0xa8, 0x21, 0x39,
+		0x21, 0x4a, 0x02, 0x2a, 0x7b, 0x79, 0x06, 0xf3,
+		0xc0, 0xd0, 0x12, 0x03, 0xd9, 0x78, 0x00, 0xf2,
+		0xd0, 0xd0, 0x22, 0x00, 0x00, 0x00, 0x00, 0x02,
+		0x2c, 0xb4, 0x78, 0x11, 0xe2, 0x44, 0x11, 0x54,
+		0x0f, 0xf8, 0xc4, 0x48, 0xd2, 0x05, 0xf5, 0x48,
+		0xc2, 0x0d, 0x31, 0xa3, 0x02, 0x23, 0x93, 0x20,
+		0x4b, 0x04, 0x75, 0x4e, 0x01, 0x22, 0xe5, 0x35,
+		0x24, 0xff, 0xf5, 0x35, 0xe5, 0x36, 0x34, 0xff,
+		0xf5, 0x36, 0x75, 0x4e, 0x01, 0x22, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x79, 0xd0, 0x77, 0x1b, 0x79, 0xd1, 0x77, 0x18,
+		0x79, 0xd2, 0x77, 0x77, 0x79, 0xd3, 0x77, 0x18,
+		0x22, 0x75, 0x29, 0x00, 0x75, 0x25, 0x00, 0x75,
+		0x34, 0x03, 0x75, 0x22, 0x00, 0x75, 0x23, 0x05,
+		0x75, 0x4f, 0x00, 0x75, 0x50, 0x00, 0x75, 0x30,
+		0x00, 0x79, 0xdc, 0x77, 0x03, 0xc2, 0x8e, 0x75,
+		0x17, 0xa8, 0x75, 0x16, 0xa8, 0x74, 0xaa, 0x79,
+		0x01, 0xf3, 0x79, 0xd7, 0x77, 0x74, 0x79, 0xd8,
+		0x77, 0xff, 0x79, 0xd9, 0x77, 0x07, 0x79, 0xda,
+		0x77, 0x00, 0x12, 0x25, 0x6f, 0x43, 0x08, 0x40,
+		0x71, 0x32, 0x79, 0x0e, 0xe3, 0x10, 0x51, 0x1c,
+		0x74, 0x06, 0x71, 0x9b, 0xe5, 0x11, 0x44, 0x80,
+		0x79, 0x05, 0xf3, 0xf5, 0x11, 0x74, 0x07, 0x71,
+		0x9b, 0xe5, 0x12, 0x44, 0x80, 0x79, 0x05, 0xf3,
+		0xf5, 0x12, 0x80, 0x18, 0x53, 0x27, 0xa0, 0x53,
+		0x28, 0x01, 0x75, 0x20, 0xf7, 0x12, 0x23, 0x4c,
+		0x75, 0x11, 0x80, 0x75, 0x12, 0x80, 0x12, 0x1f,
+		0xc0, 0x12, 0x21, 0xdc, 0x79, 0x06, 0xf3, 0x22,
+		0xd2, 0x02, 0x78, 0x11, 0xe2, 0x44, 0x11, 0xf5,
+		0x43, 0xc2, 0x0a, 0x12, 0x29, 0xa3, 0x02, 0x23,
+		0x93, 0x78, 0x11, 0xe2, 0x44, 0x11, 0xf5, 0x44,
+		0xc2, 0x0b, 0x12, 0x29, 0xa3, 0x02, 0x23, 0x93,
+		0x78, 0x00, 0xe2, 0x90, 0x25, 0x9f, 0x02, 0x23,
+		0x93, 0x78, 0x11, 0xe2, 0x75, 0x20, 0xf7, 0x75,
+		0x21, 0x3f, 0x75, 0x49, 0x11, 0x75, 0x4c, 0x11,
+		0x31, 0xa3, 0x02, 0x23, 0x93, 0x78, 0x11, 0xe2,
+		0x44, 0x11, 0x54, 0x0f, 0xf8, 0xc4, 0x48, 0xf8,
+		0xe5, 0x49, 0x45, 0x3f, 0x58, 0xf5, 0x49, 0xd2,
+		0x06, 0xc2, 0x0e, 0x31, 0xa3, 0x02, 0x23, 0x93,
+		0xc0, 0x01, 0x20, 0x2a, 0x04, 0x71, 0x32, 0xc2,
+		0x11, 0x11, 0x5e, 0xc2, 0x1f, 0xd0, 0x01, 0x02,
+		0x23, 0x9b, 0x12, 0x21, 0xdc, 0x78, 0x00, 0xf2,
+		0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0xda,
+		0xe7, 0x70, 0x2b, 0x20, 0x0a, 0x05, 0x85, 0x43,
+		0xe0, 0x80, 0x03, 0x75, 0xe0, 0x00, 0x30, 0xe1,
+		0x1d, 0x20, 0xe2, 0x1f, 0x74, 0xe0, 0xca, 0x74,
+		0x00, 0x71, 0x9b, 0xca, 0x79, 0x05, 0xf3, 0xf5,
+		0x09, 0xca, 0x74, 0x01, 0x71, 0x9b, 0xca, 0x79,
+		0x05, 0xf3, 0xf5, 0x0a, 0x80, 0x43, 0x12, 0x15,
+		0x3e, 0x80, 0x3e, 0xe5, 0x0b, 0xb4, 0x17, 0x02,
+		0x80, 0x0b, 0x50, 0x09, 0x74, 0x17, 0xc3, 0x95,
+		0x0b, 0x44, 0x60, 0x80, 0x02, 0x74, 0x60, 0xca,
+		0x74, 0x00, 0x71, 0x9b, 0xca, 0x79, 0x05, 0xf3,
+		0xf5, 0x09, 0xe5, 0x0c, 0xb4, 0x17, 0x02, 0x80,
+		0x0b, 0x50, 0x09, 0x74, 0x17, 0xc3, 0x95, 0x0c,
+		0x44, 0x60, 0x80, 0x02, 0x74, 0x60, 0xca, 0x74,
+		0x01, 0x71, 0x9b, 0xca, 0x79, 0x05, 0xf3, 0xf5,
+		0x0a, 0x22, 0xd2, 0x04, 0x78, 0x11, 0xe2, 0x44,
+		0x11, 0xf5, 0x46, 0xc2, 0x0c, 0x31, 0xa3, 0x02,
+		0x23, 0x93, 0xd2, 0x05, 0x78, 0x11, 0xe2, 0x44,
+		0x11, 0xf5, 0x48, 0xc2, 0x0d, 0x31, 0xa3, 0x02,
+		0x23, 0x93, 0xd2, 0x06, 0x78, 0x11, 0xe2, 0x44,
+		0x11, 0xf5, 0x49, 0xc2, 0x0e, 0x31, 0xa3, 0x02,
+		0x23, 0x93, 0x30, 0x1c, 0x21, 0x20, 0x4d, 0x1e,
+		0xe5, 0x29, 0x60, 0x1a, 0xc2, 0x1c, 0x12, 0x19,
+		0xec, 0x12, 0x13, 0xcf, 0xd2, 0x4d, 0x12, 0x17,
+		0x7f, 0x78, 0x00, 0xf2, 0x79, 0x06, 0xf3, 0x43,
+		0xa8, 0x04, 0x12, 0x24, 0x1b, 0x22, 0x12, 0x27,
+		0x24, 0x22, 0x78, 0x00, 0xe2, 0x90, 0x25, 0x9f,
+		0x02, 0x23, 0x93, 0x78, 0x00, 0xe2, 0xa2, 0xe7,
+		0x72, 0xe3, 0x92, 0xe7, 0x02, 0x1d, 0x85, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x79, 0x04, 0xe3, 0x54, 0x80, 0x70, 0xf9, 0x22,
+		0xe5, 0x29, 0x79, 0xde, 0xf7, 0x75, 0x29, 0x00,
+		0x70, 0x12, 0xe5, 0x15, 0x79, 0xdd, 0xf7, 0x12,
+		0x2d, 0xf0, 0x40, 0x08, 0x20, 0x1c, 0x07, 0x20,
+		0x1d, 0x04, 0x80, 0x02, 0x71, 0x32, 0x30, 0xb5,
+		0x0c, 0x79, 0x06, 0xf3, 0x20, 0x2a, 0x06, 0x79,
+		0xdd, 0xe7, 0x54, 0xfc, 0xf7, 0xd2, 0x2b, 0x12,
+		0x25, 0x6f, 0x22, 0x00, 0x00, 0x00, 0x00, 0xe5,
+		0x15, 0xa2, 0xe0, 0xb0, 0xe6, 0x40, 0x31, 0xa2,
+		0xe1, 0xb0, 0xe7, 0x40, 0x38, 0x10, 0x2b, 0x02,
+		0x80, 0x26, 0x79, 0xde, 0xe7, 0x70, 0x0b, 0x79,
+		0xdd, 0xe7, 0x20, 0xe0, 0x12, 0x20, 0xe1, 0x28,
+		0x80, 0x16, 0xf5, 0x29, 0x30, 0x4d, 0x11, 0x20,
+		0x4c, 0x0e, 0x12, 0x24, 0x1b, 0x80, 0x09, 0x43,
+		0x08, 0x40, 0x12, 0x13, 0xcf, 0x12, 0x17, 0x7f,
+		0xe5, 0x13, 0x20, 0xe4, 0x05, 0x12, 0x18, 0x1b,
+		0x80, 0x03, 0x12, 0x18, 0x77, 0xc2, 0x2b, 0x22,
+		0x12, 0x26, 0xd7, 0x12, 0x13, 0xb7, 0x22, 0x78,
+		0x04, 0x79, 0x00, 0xd9, 0xfe, 0xd8, 0xfa, 0x22,
+		0x00, 0x74, 0x09, 0x71, 0x9b, 0xe5, 0x15, 0x54,
+		0xfc, 0x79, 0x05, 0xf3, 0xf5, 0x15, 0x22, 0x78,
+		0x11, 0xe2, 0x44, 0x11, 0x54, 0x0f, 0xf8, 0xc4,
+		0x48, 0xf5, 0x46, 0xc2, 0x0c, 0xd2, 0x04, 0x31,
+		0xa3, 0x02, 0x23, 0x93, 0x12, 0x26, 0xd7, 0x12,
+		0x00, 0xb7, 0x22, 0x00, 0x79, 0x06, 0xf3, 0x74,
+		0x0a, 0x71, 0x9b, 0x79, 0xe0, 0xe7, 0x44, 0x02,
+		0xf7, 0x79, 0x05, 0xf3, 0x22, 0x74, 0x0a, 0x71,
+		0x9b, 0x79, 0xe0, 0xe7, 0x54, 0xfd, 0xf7, 0x79,
+		0x05, 0xf3, 0x22, 0x21, 0x59, 0x41, 0x23, 0x21,
+		0x59, 0x41, 0x33, 0x41, 0x43, 0x21, 0x59, 0x21,
+		0x59, 0x02, 0x25, 0x9f, 0x00, 0x74, 0x0d, 0x71,
+		0x9b, 0x74, 0x4d, 0x79, 0x05, 0xf3, 0xd2, 0x52,
+		0x22, 0x00, 0x53, 0x08, 0x40, 0x45, 0x08, 0x45,
+		0x1e, 0x79, 0x04, 0xf3, 0xf5, 0x08, 0x22, 0xd2,
+		0x01, 0x78, 0x11, 0xe2, 0x44, 0x11, 0xf5, 0x42,
+		0xc2, 0x09, 0x31, 0xa3, 0x02, 0x23, 0x93, 0x00,
+		0x00, 0x00, 0x00, 0x71, 0x6e, 0x74, 0x09, 0x12,
+		0x17, 0x75, 0xe5, 0x15, 0x44, 0x40, 0x79, 0x05,
+		0xf3, 0xf5, 0x15, 0x75, 0x3a, 0x00, 0x12, 0x1a,
+		0x77, 0x02, 0x18, 0x1b, 0xf5, 0x38, 0xe5, 0x37,
+		0x24, 0x01, 0xf5, 0x37, 0xe5, 0x38, 0x34, 0x00,
+		0xf5, 0x38, 0x40, 0x05, 0x75, 0x39, 0x00, 0x80,
+		0x03, 0x75, 0x39, 0x01, 0x12, 0x04, 0x04, 0xd2,
+		0x8e, 0x02, 0x03, 0x8d, 0x00, 0xb4, 0x0d, 0x03,
+		0x74, 0x14, 0x22, 0x04, 0x83, 0x22, 0x00, 0x02,
+		0xff, 0x01, 0x00, 0x05, 0xfe, 0xff, 0x00, 0x0a,
+		0xfc, 0xfe, 0x00, 0xc0, 0xf8, 0xfc, 0x00, 0x28,
+		0xf0, 0xf8, 0x00, 0x30, 0xe0, 0xd0, 0x01, 0x88,
+		0x04, 0x83, 0x22, 0x00, 0xff, 0xfe, 0xfd, 0xfc,
+		0xfc, 0xfb, 0xfa, 0xfe, 0xfd, 0xfb, 0xf9, 0xf7,
+		0xf7, 0xf5, 0xf3, 0xfc, 0xfa, 0xf6, 0xf2, 0xee,
+		0xee, 0xea, 0xe6, 0xf8, 0xf4, 0xec, 0xe4, 0xdc,
+		0xd4, 0xcc, 0xc4, 0x24, 0x21, 0x83, 0x22, 0x04,
+		0x83, 0x22, 0xff, 0x01, 0xff, 0x01, 0x00, 0x00,
+		0x00, 0x02, 0x22, 0x32, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+		0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0xff,
+		0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x83,
+		0x22, 0x8a, 0x01, 0x20, 0x01, 0x0b, 0xea, 0xf3,
+		0xf9, 0x8b, 0x7e, 0x6b, 0xd5, 0x01, 0x00, 0x01,
+		0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x01, 0x3a, 0x01, 0x38, 0x01, 0x4b, 0x01,
+		0x49, 0x01, 0x5c, 0x01, 0x5a, 0x01, 0x08, 0x08,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x01, 0x15, 0x24, 0x48, 0x83, 0x22, 0x04,
+		0x83, 0x22, 0x00, 0x01, 0x02, 0x03, 0x04, 0x06,
+		0x07, 0x08, 0x00, 0x03, 0x05, 0x07, 0x09, 0x0d,
+		0x0f, 0x81, 0x00, 0x06, 0x0a, 0x0e, 0x82, 0x8a,
+		0x8e, 0x22, 0x00, 0x0c, 0x84, 0x8c, 0x24, 0x2c,
+		0xa4, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0xaa, 0x35, 0xab, 0x36,
+		0x02, 0x27, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+		0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x25,
+		0x03, 0x03, 0x2b, 0x03, 0x00, 0x03, 0x00, 0x03,
+		0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+		0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+		0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x22,
+		0x00, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02,
+		0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02,
+		0x2b, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02,
+		0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02,
+		0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x01,
+		0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+		0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+		0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+		0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+		0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+		0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02,
+		0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02,
+		0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+		0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02,
+		0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02,
+		0x21, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00,
+		0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02,
+		0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x21,
+		0x01, 0x02, 0x21, 0x02, 0x02, 0x02, 0x00, 0x02,
+		0x02, 0x02, 0x02, 0x02, 0x02, 0x20, 0xb5, 0x05,
+		0x79, 0x0f, 0xf3, 0xc2, 0x11, 0x22, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5,
+		0x15, 0xa2, 0xe0, 0xb0, 0xe6, 0x50, 0x01, 0x22,
+		0xa2, 0xe1, 0xb0, 0xe7, 0x22, 0x02, 0x00};
+		static unsigned char	Data0485_0C[] = {
+		0x02, 0x27, 0x69};
+		static unsigned char	Data0485_66[] = {
+		0x02, 0x25, 0x47, 0x02, 0x25, 0x60};
+		static unsigned char	Data0485_60[] = {
+		0x02, 0x22, 0x7e};
+		static unsigned char	Data0485_99[] = {
+		0xc2, 0x53, 0x02, 0x12, 0x86};
+		static unsigned char	Data0485_9E[] = {
+		0x70, 0xf9, 0x22};
+#ifdef OOKUBO_ORIGINAL
+		static unsigned char	Data0485_36[] = {
+		0x78, 0x00, 0xf2, 0xc2, 0x53, 0x74, 0x86, 0xc0,
+		0xe0, 0x74, 0x12, 0xc0,	0xe0, 0x32};
+#endif /* OOKUBO_ORIGINAL */
diff -Nru linux/sound/isa/cs423x/sound_pc9800.h linux/sound/isa/cs423x/sound_pc9800.h
--- linux/sound/isa/cs423x/sound_pc9800.h	1970-01-01 09:00:00.000000000 +0100
+++ linux/sound/isa/cs423x/sound_pc9800.h	2002-10-28 15:45:00.000000000 +0100
@@ -0,0 +1,23 @@
+#ifndef _SOUND_PC9800_H_
+#define _SOUND_PC9800_H_
+
+#include <asm/io.h>
+
+#define PC9800_SOUND_IO_ID	0xa460
+
+/* Sound Functions ID. */
+#define PC9800_SOUND_ID()	((inb(PC9800_SOUND_IO_ID) >> 4) & 0x0f)
+
+#define PC9800_SOUND_ID_DO	0x0	/* PC-98DO+ Internal */
+#define PC9800_SOUND_ID_GS	0x1	/* PC-98GS Internal */
+#define PC9800_SOUND_ID_73	0x2	/* PC-9801-73 (base 0x18x) */
+#define PC9800_SOUND_ID_73A	0x3	/* PC-9801-73/76 (base 0x28x) */
+#define PC9800_SOUND_ID_86	0x4	/* PC-9801-86 and compatible (base 0x18x) */
+#define PC9800_SOUND_ID_86A	0x5	/* PC-9801-86 (base 0x28x) */
+#define PC9800_SOUND_ID_NF	0x6	/* PC-9821Nf/Np Internal */
+#define PC9800_SOUND_ID_XMATE	0x7	/* X-Mate Internal and compatible */
+#define PC9800_SOUND_ID_118	0x8	/* PC-9801-118 and compatible(CanBe Internal, etc.) */
+
+#define PC9800_SOUND_ID_UNKNOWN	0xf	/* Unknown (No Sound System or PC-9801-26) */
+
+#endif
diff -Nru linux-2.5.60/sound/core/Makefile linux98-2.5.60/sound/core/Makefile
--- linux-2.5.60/sound/core/Makefile	2003-02-11 03:38:46.000000000 +0900
+++ linux98-2.5.60/sound/core/Makefile	2003-02-11 10:26:12.000000000 +0900
@@ -93,6 +93,7 @@
 obj-$(CONFIG_SND_YMFPCI) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
 obj-$(CONFIG_SND_POWERMAC) += snd-pcm.o snd-timer.o snd.o
 obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-pcm.o snd-timer.o snd.o
+obj-$(CONFIG_SND_PC98_CS4232) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
 ifeq ($(CONFIG_SND_SB16_CSP),y)
   obj-$(CONFIG_SND_SB16) += snd-hwdep.o
   obj-$(CONFIG_SND_SBAWE) += snd-hwdep.o
diff -Nru linux-2.5.60/sound/drivers/mpu401/Makefile linux98-2.5.60/sound/drivers/mpu401/Makefile
--- linux-2.5.60/sound/drivers/mpu401/Makefile	2003-02-11 03:39:14.000000000 +0900
+++ linux98-2.5.60/sound/drivers/mpu401/Makefile	2003-02-11 10:26:12.000000000 +0900
@@ -37,5 +37,6 @@
 obj-$(CONFIG_SND_ALI5451) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_TRIDENT) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_YMFPCI) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_PC98_CS4232) += snd-mpu401-uart.o
 
 obj-m := $(sort $(obj-m))
diff -Nru linux-2.5.60/sound/drivers/opl3/Makefile linux98-2.5.60/sound/drivers/opl3/Makefile
--- linux-2.5.60/sound/drivers/opl3/Makefile	2003-02-11 03:37:56.000000000 +0900
+++ linux98-2.5.60/sound/drivers/opl3/Makefile	2003-02-11 10:26:12.000000000 +0900
@@ -24,6 +24,7 @@
 obj-$(CONFIG_SND_OPL3SA2) += $(OPL3_OBJS)
 obj-$(CONFIG_SND_AD1816A) += $(OPL3_OBJS)
 obj-$(CONFIG_SND_CS4232) += $(OPL3_OBJS)
+obj-$(CONFIG_SND_PC98_CS4232) += $(OPL3_OBJS)
 obj-$(CONFIG_SND_CS4236) += $(OPL3_OBJS)
 obj-$(CONFIG_SND_ES1688) += $(OPL3_OBJS)
 obj-$(CONFIG_SND_GUSEXTREME) += $(OPL3_OBJS)

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (2/26) APM
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
  2003-02-17 13:48 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (1/26) ALSA Osamu Tomita
@ 2003-02-17 13:49 ` Osamu Tomita
  2003-02-18 10:37   ` Christoph Hellwig
  2003-02-17 13:51 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (3/26) mach-pc9800 Osamu Tomita
                   ` (23 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 13:49 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (2/26).

APM support for PC98. Including PC98's BIOS bug fix.

diff -Nru linux-2.5.61/arch/i386/kernel/apm.c linux98-2.5.61/arch/i386/kernel/apm.c
--- linux-2.5.61/arch/i386/kernel/apm.c	2003-02-15 08:51:10.000000000 +0900
+++ linux98-2.5.61/arch/i386/kernel/apm.c	2003-02-15 13:40:49.000000000 +0900
@@ -226,6 +226,8 @@
 #include <asm/uaccess.h>
 #include <asm/desc.h>
 
+#include "io_ports.h"
+
 extern spinlock_t i8253_lock;
 extern unsigned long get_cmos_time(void);
 extern void machine_real_restart(unsigned char *, int);
@@ -621,6 +623,9 @@
 	__asm__ __volatile__(APM_DO_ZERO_SEGS
 		"pushl %%edi\n\t"
 		"pushl %%ebp\n\t"
+#ifdef CONFIG_X86_PC9800
+		"pushfl\n\t"
+#endif
 		"lcall *%%cs:apm_bios_entry\n\t"
 		"setc %%al\n\t"
 		"popl %%ebp\n\t"
@@ -682,6 +687,9 @@
 		__asm__ __volatile__(APM_DO_ZERO_SEGS
 			"pushl %%edi\n\t"
 			"pushl %%ebp\n\t"
+#ifdef CONFIG_X86_PC9800
+			"pushfl\n\t"
+#endif
 			"lcall *%%cs:apm_bios_entry\n\t"
 			"setc %%bl\n\t"
 			"popl %%ebp\n\t"
@@ -722,7 +730,7 @@
 
 	if (apm_bios_call_simple(APM_FUNC_VERSION, 0, *val, &eax))
 		return (eax >> 8) & 0xff;
-	*val = eax;
+	*val = pc98 ? ((eax & 0xff00) | ((eax & 0x00f0) >> 4)) : eax;
 	return APM_SUCCESS;
 }
 
@@ -1211,11 +1219,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
 }
diff -Nru linux/include/linux/apm_bios.h linux98/include/linux/apm_bios.h
--- linux/include/linux/apm_bios.h	2003-01-02 12:22:18.000000000 +0900
+++ linux98/include/linux/apm_bios.h	2003-01-04 13:20:28.000000000 +0900
@@ -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_X86_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

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (3/26) mach-pc9800
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
  2003-02-17 13:48 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (1/26) ALSA Osamu Tomita
  2003-02-17 13:49 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (2/26) APM Osamu Tomita
@ 2003-02-17 13:51 ` Osamu Tomita
  2003-02-17 14:48   ` Sam Ravnborg
  2003-02-18 10:39   ` Christoph Hellwig
  2003-02-17 13:54 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (4/26) boot Osamu Tomita
                   ` (22 subsequent siblings)
  25 siblings, 2 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 13:51 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (3/26).

Files under arch/i386/mach-pc9800 directory.
For fix difference of cascade IRQ number and APM BIOS version BUG.

diff -Nru linux-2.5.61/arch/i386/mach-pc9800/Makefile linux98-2.5.61/arch/i386/mach-pc9800/Makefile
--- linux-2.5.61/arch/i386/mach-pc9800/Makefile	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/arch/i386/mach-pc9800/Makefile	2003-02-16 17:19:03.000000000 +0900
@@ -0,0 +1,7 @@
+#
+# Makefile for the linux kernel.
+#
+
+EXTRA_CFLAGS	+= -I../kernel
+
+obj-y				:= setup.o topology.o
diff -Nru linux-2.5.61/arch/i386/mach-pc9800/setup.c linux98-2.5.61/arch/i386/mach-pc9800/setup.c
--- linux-2.5.61/arch/i386/mach-pc9800/setup.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/arch/i386/mach-pc9800/setup.c	2003-02-16 17:19:03.000000000 +0900
@@ -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 <linux/apm_bios.h>
+#include <asm/setup.h>
+#include <asm/arch_hooks.h>
+
+struct sys_desc_table_struct {
+	unsigned short length;
+	unsigned char table[0];
+};
+
+/**
+ * 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;
+	/* In PC-9800, APM BIOS version is written in BCD...?? */
+	APM_BIOS_INFO.version = (APM_BIOS_INFO.version & 0xff00)
+				| ((APM_BIOS_INFO.version & 0x00f0) >> 4);
+}
+
+/**
+ * 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 -Nru linux-2.5.61/arch/i386/mach-pc9800/topology.c linux98-2.5.61/arch/i386/mach-pc9800/topology.c
--- linux-2.5.61/arch/i386/mach-pc9800/topology.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/arch/i386/mach-pc9800/topology.c	2003-02-16 17:19:03.000000000 +0900
@@ -0,0 +1,68 @@
+/*
+ * arch/i386/mach-generic/topology.c - Populate driverfs with topology information
+ *
+ * Written by: Matthew Dobson, IBM Corporation
+ * Original Code: Paul Dorwin, IBM Corporation, Patrick Mochel, OSDL
+ *
+ * Copyright (C) 2002, IBM Corp.
+ *
+ * All rights reserved.          
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to <colpatch@us.ibm.com>
+ */
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <asm/cpu.h>
+
+struct i386_cpu cpu_devices[NR_CPUS];
+
+#ifdef CONFIG_NUMA
+#include <linux/mmzone.h>
+#include <asm/node.h>
+#include <asm/memblk.h>
+
+struct i386_node node_devices[MAX_NUMNODES];
+struct i386_memblk memblk_devices[MAX_NR_MEMBLKS];
+
+static int __init topology_init(void)
+{
+	int i;
+
+	for (i = 0; i < num_online_nodes(); i++)
+		arch_register_node(i);
+	for (i = 0; i < NR_CPUS; i++)
+		if (cpu_possible(i)) arch_register_cpu(i);
+	for (i = 0; i < num_online_memblks(); i++)
+		arch_register_memblk(i);
+	return 0;
+}
+
+#else /* !CONFIG_NUMA */
+
+static int __init topology_init(void)
+{
+	int i;
+
+	for (i = 0; i < NR_CPUS; i++)
+		if (cpu_possible(i)) arch_register_cpu(i);
+	return 0;
+}
+
+#endif /* CONFIG_NUMA */
+
+subsys_initcall(topology_init);

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (4/26) boot
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (2 preceding siblings ...)
  2003-02-17 13:51 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (3/26) mach-pc9800 Osamu Tomita
@ 2003-02-17 13:54 ` Osamu Tomita
  2003-02-17 13:56 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (5/26) char device Osamu Tomita
                   ` (21 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 13:54 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (4/26).

Files under arch/i386/boot98 directory.
Because bootstrap code of PC98 has quite differcence from standard
i386 PC (AT compatibles). We use "boot98" directory instead of "boot".

diff -Nru linux-2.5.61/arch/i386/Makefile linux98-2.5.61/arch/i386/Makefile
--- linux-2.5.61/arch/i386/Makefile	2003-02-15 08:51:29.000000000 +0900
+++ linux98-2.5.61/arch/i386/Makefile	2003-02-16 17:19:03.000000000 +0900
@@ -65,6 +65,10 @@
 mflags-$(CONFIG_X86_NUMAQ)	:= -Iinclude/asm-i386/mach-numaq
 mcore-$(CONFIG_X86_NUMAQ)	:= mach-default
 
+# PC-9800 subarch support
+mflags-$(CONFIG_X86_PC9800)	:= -Iinclude/asm-i386/mach-pc9800
+mcore-$(CONFIG_X86_PC9800)	:= mach-pc9800
+
 # BIGSMP subarch support
 mflags-$(CONFIG_X86_BIGSMP)	:= -Iinclude/asm-i386/mach-bigsmp
 mcore-$(CONFIG_X86_BIGSMP)	:= mach-default
@@ -90,14 +94,18 @@
 CFLAGS += $(mflags-y)
 AFLAGS += $(mflags-y)
 
+ifeq ($(CONFIG_X86_PC9800),y)
+boot := arch/i386/boot98
+else
 boot := arch/i386/boot
+endif
 
 .PHONY: zImage bzImage compressed zlilo bzlilo zdisk bzdisk install
 
 all: bzImage
 
-BOOTIMAGE=arch/i386/boot/bzImage
-zImage zlilo zdisk: BOOTIMAGE=arch/i386/boot/zImage
+BOOTIMAGE=$(boot)/bzImage
+zImage zlilo zdisk: BOOTIMAGE=$(boot)/zImage
 
 zImage bzImage: vmlinux
 	$(Q)$(MAKE) $(build)=$(boot) $(BOOTIMAGE)
@@ -115,9 +123,10 @@
 
 archclean:
 	$(Q)$(MAKE) $(clean)=arch/i386/boot
+	$(Q)$(MAKE) $(clean)=arch/i386/boot98
 
 define archhelp
-  echo  '* bzImage	- Compressed kernel image (arch/$(ARCH)/boot/bzImage)'
+  echo  '* bzImage	- Compressed kernel image ($(boot)/bzImage)'
   echo  '  install	- Install kernel using'
   echo  '		   (your) ~/bin/installkernel or'
   echo  '		   (distribution) /sbin/installkernel or'
diff -Nru linux-2.5.61/arch/i386/boot98/bootsect.S linux98-2.5.61/arch/i386/boot98/bootsect.S
--- linux-2.5.61/arch/i386/boot98/bootsect.S	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/arch/i386/boot98/bootsect.S	2002-10-31 15:05:52.000000000 +0000
@@ -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 -Nru linux-2.5.61/arch/i386/boot98/compressed/head.S linux98-2.5.61/arch/i386/boot98/compressed/head.S
--- linux-2.5.61/arch/i386/boot98/compressed/head.S	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/arch/i386/boot98/compressed/head.S	2002-11-15 15:33:42.000000000 +0000
@@ -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 $(__BOOT_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 $(__BOOT_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 $(__BOOT_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 $(__BOOT_CS), $0x100000
+move_routine_end:
diff -Nru linux-2.5.61/arch/i386/boot98/compressed/Makefile linux98-2.5.61/arch/i386/boot98/compressed/Makefile
--- linux-2.5.61/arch/i386/boot98/compressed/Makefile	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/arch/i386/boot98/compressed/Makefile	2003-02-16 17:19:03.000000000 +0900
@@ -0,0 +1,25 @@
+#
+# 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
+
+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 -Nru linux-2.5.61/arch/i386/boot98/compressed/misc.c linux98-2.5.61/arch/i386/boot98/compressed/misc.c
--- linux-2.5.61/arch/i386/boot98/compressed/misc.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/arch/i386/boot98/compressed/misc.c	2002-11-15 15:33:42.000000000 +0000
@@ -0,0 +1,379 @@
+/*
+ * 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] , __BOOT_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;
+}
+
+/* We don't actually check for stack overflows this early. */
+__asm__(".globl mcount ; mcount: ret\n");
+
diff -Nru linux-2.5.61/arch/i386/boot98/compressed/vmlinux.scr linux98-2.5.61/arch/i386/boot98/compressed/vmlinux.scr
--- linux-2.5.61/arch/i386/boot98/compressed/vmlinux.scr	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/arch/i386/boot98/compressed/vmlinux.scr	2002-10-31 15:05:52.000000000 +0000
@@ -0,0 +1,9 @@
+SECTIONS
+{
+  .data : { 
+	input_len = .;
+	LONG(input_data_end - input_data) input_data = .; 
+	*(.data) 
+	input_data_end = .; 
+	}
+}
diff -Nru linux-2.5.61/arch/i386/boot98/install.sh linux98-2.5.61/arch/i386/boot98/install.sh
--- linux-2.5.61/arch/i386/boot98/install.sh	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/arch/i386/boot98/install.sh	2002-10-31 15:05:52.000000000 +0000
@@ -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 -Nru linux-2.5.61/arch/i386/boot98/Makefile linux98-2.5.61/arch/i386/boot98/Makefile
--- linux-2.5.61/arch/i386/boot98/Makefile	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/arch/i386/boot98/Makefile	2003-02-16 17:19:03.000000000 +0900
@@ -0,0 +1,76 @@
+#
+# 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
+
+# ---------------------------------------------------------------------------
+
+$(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   $@
+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)
+	@echo 'Kernel: $@ is ready'
+
+$(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
+	$(Q)$(MAKE) -f scripts/Makefile.build obj=$(obj)/compressed \
+					IMAGE_OFFSET=$(IMAGE_OFFSET) $@
+
+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)"
diff -Nru linux-2.5.61/arch/i386/boot98/setup.S linux98-2.5.61/arch/i386/boot98/setup.S
--- linux-2.5.61/arch/i386/boot98/setup.S	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/arch/i386/boot98/setup.S	2002-11-15 15:33:42.000000000 +0000
@@ -0,0 +1,961 @@
+/*
+ *	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
+ * (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92
+ *
+ * some changes and additional features by Christoph Niemann,
+ * March 1993/June 1994 (Christoph.Niemann@linux.org)
+ *
+ * add APM BIOS checking by Stephen Rothwell, May 1994
+ * (sfr@canb.auug.org.au)
+ *
+ * High load stuff, initrd support and position independency
+ * by Hans Lermen & Werner Almesberger, February 1996
+ * <lermen@elserv.ffm.fgan.de>, <almesber@lrc.epfl.ch>
+ *
+ * Video handling moved to video.S by Martin Mares, March 1996
+ * <mj@k332.feld.cvut.cz>
+ *
+ * Extended memory detection scheme retwiddled by orc@pell.chi.il.us (david
+ * parsons) to avoid loadlin confusion, July 1997
+ *
+ * Transcribed from Intel (as86) -> AT&T (gas) by Chris Noe, May 1999.
+ * <stiker@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 <michaelm@mjmm.org>
+ *
+ * New A20 code ported from SYSLINUX by H. Peter Anvin. AMD Elan bugfixes
+ * by Robert Schwebel, December 2001 <robert@schwebel.de>
+ *
+ * Heavily modified for NEC PC-9800 series by Kyoto University Microcomputer
+ * Club (KMC) Linux/98 project <seraphim@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,__BOOT_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	__BOOT_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: The intel manual says gdt should be sixteen bytes aligned for
+# efficiency reasons.  However, there are machines which are known not
+# to boot with misaligned GDTs, so alter this at your peril!  If you alter
+# GDT_ENTRY_BOOT_CS (in asm/segment.h) remember to leave at least two
+# empty GDT entries (one for NULL and one reserved).
+#
+# NOTE:	On some CPUs, the GDT must be 8 byte aligned.  This is
+# true for the Voyager Quad CPU card which will not boot without
+# This directive.  16 byte aligment is recommended by intel.
+#
+	.align 16
+gdt:
+	.fill GDT_ENTRY_BOOT_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)
+gdt_end:
+	.align	4
+	
+	.word	0				# alignment byte
+idt_48:
+	.word	0				# idt limit = 0
+	.word	0, 0				# idt base = 0L
+
+	.word	0				# alignment byte
+gdt_48:
+	.word	gdt_end - gdt - 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 -Nru linux-2.5.61/arch/i386/boot98/tools/build.c linux98-2.5.61/arch/i386/boot98/tools/build.c
--- linux-2.5.61/arch/i386/boot98/tools/build.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/arch/i386/boot98/tools/build.c	2002-10-31 15:05:52.000000000 +0000
@@ -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 -Nru linux-2.5.61/arch/i386/boot98/video.S linux98-2.5.61/arch/i386/boot98/video.S
--- linux-2.5.61/arch/i386/boot98/video.S	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/arch/i386/boot98/video.S	2002-10-31 15:05:52.000000000 +0000
@@ -0,0 +1,262 @@
+/*	video.S
+ *
+ *  Video mode setup, etc. for NEC PC-9800 series.
+ *
+ *  Copyright (C) 1997,98,99  Linux/98 project  <seraphim@kmc.kyoto-u.ac.jp>
+ *
+ *  Based on the video.S for IBM PC:
+ *	copyright (C) Martin Mares <mj@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

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (5/26) char device
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (3 preceding siblings ...)
  2003-02-17 13:54 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (4/26) boot Osamu Tomita
@ 2003-02-17 13:56 ` Osamu Tomita
  2003-02-18 10:45   ` Christoph Hellwig
  2003-02-17 14:03 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (6/26) console Osamu Tomita
                   ` (20 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 13:56 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (5/26).

Real time clock driver and printer driver for PC98.

diff -Nru linux-2.5.61/drivers/char/Kconfig linux98-2.5.61/drivers/char/Kconfig
--- linux-2.5.61/drivers/char/Kconfig	2003-02-15 08:51:08.000000000 +0900
+++ linux98-2.5.61/drivers/char/Kconfig	2003-02-16 17:19:03.000000000 +0900
@@ -575,6 +575,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 X86_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"
 
 
@@ -774,7 +785,7 @@
 
 config RTC
 	tristate "Enhanced Real Time Clock Support"
-	depends on !PPC32 && !PARISC && !IA64
+	depends on !PPC32 && !PARISC && !IA64 && !X86_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
@@ -833,6 +844,15 @@
 	bool "EFI Real Time Clock Services"
 	depends on IA64
 
+config RTC98
+	tristate "NEC PC-9800 Real Time Clock Support"
+	depends on X86_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 -Nru linux-2.5.60/drivers/char/Makefile linux98-2.5.60/drivers/char/Makefile
--- linux-2.5.60/drivers/char/Makefile	2003-02-11 03:38:54.000000000 +0900
+++ linux98-2.5.60/drivers/char/Makefile	2003-02-11 11:19:09.000000000 +0900
@@ -44,6 +44,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
@@ -51,6 +52,7 @@
 obj-$(CONFIG_APPLICOM) += applicom.o
 obj-$(CONFIG_SONYPI) += sonypi.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 -Nru linux-2.5.61/drivers/char/lp_old98.c linux98-2.5.61/drivers/char/lp_old98.c
--- linux-2.5.61/drivers/char/lp_old98.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/drivers/char/lp_old98.c	2003-02-17 19:10:26.000000000 +0900
@@ -0,0 +1,555 @@
+/*
+ *	linux/drivers/char/lp_old98.c
+ *
+ * printer port driver for ancient PC-9800s with no bidirectional port support
+ *
+ * Copyright (C)  1998,99  Kousuke Takai <tak@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/module.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/console.h>
+#include <linux/version.h>
+#include <linux/fs.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#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	= 0;
+static spinlock_t lp_old98_lock = SPIN_LOCK_UNLOCKED;
+
+
+#undef LP_OLD98_DEBUG
+
+#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
+	__const_udelay(lp.wait * 4);
+    
+	/* 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
+
+	__const_udelay(lp.wait * 4);
+
+	/* 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 int lp_old98_open(struct inode * inode, struct file * file)
+{
+	if (minor(inode->i_rdev) != 0)
+		return -ENXIO;
+
+	if (!try_module_get(THIS_MODULE))
+		return -EBUSY;
+
+	if (lp.flags & LP_BUSY)
+		return -EBUSY;
+
+	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 flags;
+
+		/* interrupts while check is fairly bad */
+		spin_lock_irqsave(&lp_old98_lock, flags);
+
+		if (!lp_old98_char(DC1)) {
+			spin_unlock_irqrestore(&lp_old98_lock, flags);
+			return -EBUSY;
+		}
+		count = (unsigned int)dc1_check > 10000 ? 10000 : dc1_check;
+		while (inb(LP_PORT_STATUS) & LP_MASK_nBUSY) {
+			if (--count == 0) {
+				spin_unlock_irqrestore(&lp_old98_lock, flags);
+				return -ENODEV;
+			}
+		}
+		spin_unlock_irqrestore(&lp_old98_lock, flags);
+	}
+
+	if ((lp.lp_buffer = kmalloc(LP_BUFFER_SIZE, GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	lp.flags |= LP_BUSY;
+
+#ifdef CONFIG_PC9800_OLDLP_CONSOLE
+	saved_console_flags = lp_old98_console.flags;
+	lp_old98_console.flags &= ~CON_ENABLED;
+#endif
+	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
+	module_put(THIS_MODULE);
+	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;
+}
+
+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 = 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 = 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 = 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		= no_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 mk_kdev(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 */
+
+static int __init lp_old98_init(void)
+{
+#ifdef	PC98_HW_H98
+	if (pc98_hw_flags & PC98_HW_H98)
+	    if (!request_region(LP_PORT_H98MODE, 1, "lp_old98") {
+		printk(KERN_ERR
+		       "lp_old98: I/O ports for H98 already occupied.\n");
+		return -EBUSY;
+	    }
+#endif
+	if (request_region(LP_PORT_DATA,   1, "lp_old98")) {
+	    if (request_region(LP_PORT_STATUS, 1, "lp_old98")) {
+		if (request_region(LP_PORT_STROBE, 1, "lp_old98")) {
+		    if (request_region(LP_PORT_EXTMODE, 1, "lp_old98")) {
+			if (register_chrdev(LP_MAJOR, "lp", &lp_old98_fops)) {
+			    printk(KERN_ERR
+				   "lp_old98: unable to get major %d\n",
+				   LP_MAJOR);
+			} else {
+#ifdef CONFIG_PC9800_OLDLP_CONSOLE
+			    register_console(&lp_old98_console);
+			    printk(KERN_INFO "lp_old98: console ready\n");
+#endif
+			    /*
+			     * rest are not needed by this driver,
+			     * but for locking out other printer drivers...
+			     */
+			    lp_old98_init_device();
+			    return 0;
+			}
+		    }
+		    release_region(LP_PORT_STROBE, 1);
+		}
+		release_region(LP_PORT_STATUS, 1);
+	    }
+	    release_region(LP_PORT_DATA, 1);
+	}
+#ifdef	PC98_HW_H98
+	if (pc98_hw_flags & PC98_HW_H98)
+	    release_region(LP_PORT_H98MODE, 1);
+#endif
+	printk(KERN_ERR "lp_old98: I/O ports already occupied, giving up.\n");
+	return -EBUSY;
+}
+
+static void __exit lp_old98_exit(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);
+}
+
+#ifndef MODULE
+static int __init lp_old98_setup(char *str)
+{
+        int ints[4];
+
+        str = get_options(str, ARRAY_SIZE(ints), ints);
+        if (ints[0] > 0)
+		dc1_check = ints[1];
+        return 1;
+}
+__setup("lp_old98_dc1_check=", lp_old98_setup);
+#endif
+
+MODULE_PARM(dc1_check, "i");
+MODULE_AUTHOR("Kousuke Takai <tak@kmc.kyoto-u.ac.jp>");
+MODULE_DESCRIPTION("PC-9800 old printer port driver");
+MODULE_LICENSE("GPL");
+
+module_init(lp_old98_init);
+module_exit(lp_old98_exit);
diff -Nru linux-2.5.61/drivers/char/upd4990a.c linux98-2.5.61/drivers/char/upd4990a.c
--- linux-2.5.61/drivers/char/upd4990a.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/drivers/char/upd4990a.c	2003-02-16 17:19:03.000000000 +0900
@@ -0,0 +1,429 @@
+/*
+ * 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 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 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		= no_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
+
+/*
+ *	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 -Nru linux-2.5.61/include/asm-i386/upd4990a.h linux98-2.5.61/include/asm-i386/upd4990a.h
--- linux-2.5.61/include/asm-i386/upd4990a.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/include/asm-i386/upd4990a.h	2003-02-16 17:19:03.000000000 +0900
@@ -0,0 +1,52 @@
+/*
+ *  Architecture dependent definitions
+ *  for NEC uPD4990A serial I/O real-time clock.
+ *
+ *  Copyright 2001  TAKAI Kousuke <tak@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 <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
diff -Nru linux-2.5.61/include/linux/upd4990a.h linux98-2.5.61/include/linux/upd4990a.h
--- linux-2.5.61/include/linux/upd4990a.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/include/linux/upd4990a.h	2002-10-31 15:06:03.000000000 +0000
@@ -0,0 +1,140 @@
+/*
+ *  Constant and architecture independent procedures
+ *  for NEC uPD4990A serial I/O real-time clock.
+ *
+ *  Copyright 2001  TAKAI Kousuke <tak@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 */

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (6/26) console
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (4 preceding siblings ...)
  2003-02-17 13:56 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (5/26) char device Osamu Tomita
@ 2003-02-17 14:03 ` Osamu Tomita
  2003-02-17 14:06 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (7/26) misc core Osamu Tomita
                   ` (19 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:03 UTC (permalink / raw)
  To: Linux Kernel Mailing List
  Cc: James simmons, Alan Cox, 'Christoph Hellwig'

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (6/26).

PC98 Standard console support (without japanese kanji character).

diff -Nru linux-2.5.60/drivers/char/Makefile linux98-2.5.60/drivers/char/Makefile
--- linux-2.5.60/drivers/char/Makefile	2003-02-11 03:38:54.000000000 +0900
+++ linux98-2.5.60/drivers/char/Makefile	2003-02-11 11:19:09.000000000 +0900
@@ -5,7 +5,11 @@
 #
 # This file contains the font map for the default (hardware) font
 #
+ifneq ($(CONFIG_X86_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
 
diff -Nru 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)
diff -Nru 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 -Nru linux/drivers/char/consolemap.c linux98/drivers/char/consolemap.c
--- linux/drivers/char/consolemap.c	2002-12-10 11:46:14.000000000 +0900
+++ linux98/drivers/char/consolemap.c	2002-12-16 11:27:23.000000000 +0900
@@ -23,7 +23,7 @@
 #include <linux/consolemap.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,
@@ -163,7 +163,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
@@ -177,7 +229,7 @@
 	u16 		**uni_pgdir[32];
 	unsigned long	refcount;
 	unsigned long	sum;
-	unsigned char	*inverse_translations[4];
+	unsigned char	*inverse_translations[5];
 	int		readonly;
 };
 
diff -Nru 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 -Nru linux/drivers/char/vt.c linux98/drivers/char/vt.c
--- linux/drivers/char/vt.c	2002-12-16 11:08:16.000000000 +0900
+++ linux98/drivers/char/vt.c	2002-12-20 14:52:06.000000000 +0900
@@ -107,6 +107,10 @@
 
 #include "console_macros.h"
 
+#ifdef CONFIG_X86_PC9800
+#include "console_pc9800.h"
+extern unsigned short translations[][256];
+#endif
 
 const struct consw *conswitchp;
 
@@ -301,7 +305,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) {
@@ -387,6 +391,8 @@
 {
 	attr = build_attr(currcons, color, intensity, blink, underline, reverse ^ decscnm);
 	video_erase_char = (build_attr(currcons, color, 1, blink, 0, decscnm) << 8) | ' ';
+	if (pc98 && decscnm)
+		video_erase_char |= 0x0400; /* reverse */
 }
 
 /* Note: inverting the screen twice should revert to the original state */
@@ -403,7 +411,7 @@
 	else {
 		u16 *q = p;
 		int cnt = count;
-		u16 a;
+		vram_char_t a;
 
 		if (!can_do_color) {
 			while (cnt--) {
@@ -437,7 +445,7 @@
 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) {
@@ -448,10 +456,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;
@@ -510,7 +523,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;
@@ -646,8 +659,12 @@
     complement_mask = 0;
     can_do_color = 0;
     sw->con_init(vc_cons[currcons].d, init);
-    if (!complement_mask)
-        complement_mask = can_do_color ? 0x7700 : 0x0800;
+    if (!complement_mask) {
+	if (pc98)
+        	complement_mask = 0x0400;
+	else
+        	complement_mask = can_do_color ? 0x7700 : 0x0800;
+    }
     s_complement_mask = complement_mask;
     video_size_row = video_num_columns<<1;
     screenbuf_size = video_num_lines*video_size_row;
@@ -679,7 +692,7 @@
 	    visual_init(currcons, 1);
 	    if (!*vc_cons[currcons].d->vc_uni_pagedir_loc)
 		con_set_default_unimap(currcons);
-	    q = (long)kmalloc(screenbuf_size, GFP_KERNEL);
+	    q = (long)kmalloc(screenbuf_size + (pc98 ? screenbuf_size : 0), GFP_KERNEL);
 	    if (!q) {
 		kfree((char *) p);
 		vc_cons[currcons].d = NULL;
@@ -732,7 +745,7 @@
 	if (new_cols == video_num_columns && new_rows == video_num_lines)
 		return 0;
 
-	newscreen = (unsigned short *) kmalloc(new_screen_size, GFP_USER);
+	newscreen = (unsigned short *) kmalloc(new_screen_size + (pc98 ? new_screen_size : 0), GFP_USER);
 	if (!newscreen)
 		return -ENOMEM;
 
@@ -1261,6 +1284,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) {
@@ -2427,9 +2454,17 @@
 		vc_cons[currcons].d->vc_palette[k++] = default_grn[j] ;
 		vc_cons[currcons].d->vc_palette[k++] = default_blu[j] ;
 	}
-	def_color       = 0x07;   /* white */
-	ulcolor		= 0x0f;   /* bold white */
-	halfcolor       = 0x08;   /* grey */
+	if (pc98) {
+		def_color	= 0x07;		/* white */
+		def_attr	= 0xE1;
+		ul_attr		= 0x08;		/* underline */
+		half_attr	= 0x00;		/* ignore half color */
+		bold_attr	= 0xC1;		/* yellow */
+	} else {
+		def_color       = 0x07;   /* white */
+		ulcolor		= 0x0f;   /* bold white */
+		halfcolor       = 0x08;   /* grey */
+	}
 	init_waitqueue_head(&vt_cons[currcons]->paste_wait);
 	reset_terminal(currcons, do_clear);
 }
@@ -2470,7 +2505,12 @@
 		vt_cons[currcons] = (struct vt_struct *)
 				alloc_bootmem(sizeof(struct vt_struct));
 		visual_init(currcons, 1);
+#if defined(CONFIG_X86_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);
@@ -2972,9 +3012,12 @@
 /* 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));
 	u16 c = w & 0xff;
 
+	if (pc98)
+		return ((u16)(w >> 16) & 0xff00) | c;
+
 	if (w & hi_font_mask)
 		c |= 0x100;
 	return c;
@@ -3036,8 +3079,10 @@
 EXPORT_SYMBOL(default_red);
 EXPORT_SYMBOL(default_grn);
 EXPORT_SYMBOL(default_blu);
+#ifndef CONFIG_X86_PC9800
 EXPORT_SYMBOL(video_font_height);
 EXPORT_SYMBOL(video_scan_lines);
+#endif
 EXPORT_SYMBOL(vc_cons_allocated);
 EXPORT_SYMBOL(update_region);
 EXPORT_SYMBOL(redraw_screen);
diff -Nru linux/drivers/char/vt_ioctl.c linux98/drivers/char/vt_ioctl.c
--- linux/drivers/char/vt_ioctl.c	2002-12-10 11:46:13.000000000 +0900
+++ linux98/drivers/char/vt_ioctl.c	2002-12-16 13:15:34.000000000 +0900
@@ -63,9 +63,11 @@
 asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on);
 #endif
 
+#ifndef CONFIG_X86_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_X86_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;
@@ -830,7 +843,9 @@
 		__get_user(clin, &vtconsize->v_clin);
 		__get_user(vcol, &vtconsize->v_vcol);
 		__get_user(ccol, &vtconsize->v_ccol);
+#ifndef CONFIG_X86_PC9800
 		vlin = vlin ? vlin : video_scan_lines;
+#endif
 		if (clin) {
 			if (ll) {
 				if (ll != vlin/clin)
@@ -849,10 +864,12 @@
 		if (clin > 32)
 			return -EINVAL;
 		    
+#ifndef CONFIG_X86_PC9800		    
 		if (vlin)
 			video_scan_lines = vlin;
 		if (clin)
 			video_font_height = clin;
+#endif
 	
 		for (i = 0; i < MAX_NR_CONSOLES; i++)
 			vc_resize(i, cc, ll);
@@ -1022,8 +1039,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_X86_PC9800
 	if (!in_interrupt())    /* Via keyboard.c:SAK() - akpm */
 		reset_palette(new_console) ;
+#endif
 }
 
 /*
diff -Nru linux-2.5.59/drivers/video/console/Kconfig linux98-2.5.59/drivers/video/console/Kconfig
--- linux-2.5.59/drivers/video/console/Kconfig	2003-01-17 13:22:14.000000000 +0900
+++ linux98-2.5.59/drivers/video/console/Kconfig	2003-01-17 13:43:44.000000000 +0900
@@ -6,7 +6,7 @@
 
 config VGA_CONSOLE
 	bool "VGA text console"
-	depends on !ARCH_ACORN && !ARCH_EBSA110 || !4xx && !8xx
+	depends on !X86_PC9800 && !ARCH_ACORN && !ARCH_EBSA110 || !4xx && !8xx
 	help
 	  Saying Y here will allow you to use Linux in text mode through a
 	  display that complies with the generic VGA standard. Virtually
@@ -97,6 +97,18 @@
 	  Say Y to build a console driver for Sun machines that uses the
 	  terminal emulation built into their console PROMS.
 
+config GDC_CONSOLE
+	bool "PC-9800 GDC text console"
+	depends on X86_PC9800
+	default y
+	help
+	  This enables support for PC-9800 standard text mode console.
+	  If use NEC PC-9801/PC-9821, Say Y.
+
+config GDC_32BITACCESS
+	bool "Enable 32-bit access to text video RAM"
+	depends on GDC_CONSOLE
+
 config DUMMY_CONSOLE
 	bool
 	depends on PROM_CONSOLE!=y || VGA_CONSOLE!=y || SGI_NEWPORT_CONSOLE!=y
diff -Nru linux/include/linux/console.h linux98/include/linux/console.h
--- linux/include/linux/console.h	2002-12-10 11:45:55.000000000 +0900
+++ linux98/include/linux/console.h	2002-12-16 11:27:23.000000000 +0900
@@ -17,6 +17,13 @@
 #include <linux/types.h>
 #include <linux/kdev_t.h>
 #include <linux/spinlock.h>
+#include <linux/config.h>
+
+#ifndef CONFIG_X86_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);
@@ -49,6 +56,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;
@@ -56,6 +64,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 -Nru linux/include/linux/console_struct.h linux98/include/linux/console_struct.h
--- linux/include/linux/console_struct.h	2002-12-10 11:45:40.000000000 +0900
+++ linux98/include/linux/console_struct.h	2002-12-16 13:25:55.000000000 +0900
@@ -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,10 +28,14 @@
 	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 */
+	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 */
 	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 */
 	struct console_font_op vc_font;		/* Current VC font set */
-	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 */
@@ -106,6 +113,10 @@
 #define CUR_HWMASK	0x0f
 #define CUR_SWMASK	0xfff0
 
+#ifndef CONFIG_X86_PC9800
 #define CUR_DEFAULT CUR_UNDERLINE
+#else
+#define CUR_DEFAULT CUR_BLOCK
+#endif
 
 #define CON_IS_VISIBLE(conp) (*conp->vc_display_fg == conp)
diff -Nru 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 -Nru 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 -Nru 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))

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (7/26) misc core
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (5 preceding siblings ...)
  2003-02-17 14:03 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (6/26) console Osamu Tomita
@ 2003-02-17 14:06 ` Osamu Tomita
  2003-02-17 14:07 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (8/26) core Osamu Tomita
                   ` (18 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:06 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (7/26).

Small core patches for PC98. I think these are small and clean.

diff -Nru linux/include/asm-i386/io.h linux98/include/asm-i386/io.h
--- linux/include/asm-i386/io.h	2002-10-12 13:22:45.000000000 +0900
+++ linux98/include/asm-i386/io.h	2002-10-12 19:25:19.000000000 +0900
@@ -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_X86_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 -Nru linux/include/asm-i386/irq.h linux98/include/asm-i386/irq.h
--- linux/include/asm-i386/irq.h	2002-09-21 00:20:16.000000000 +0900
+++ linux98/include/asm-i386/irq.h	2002-09-21 07:17:56.000000000 +0900
@@ -17,7 +17,11 @@
 
 static __inline__ int irq_cannonicalize(int irq)
 {
+#ifndef CONFIG_X86_PC9800
 	return ((irq == 2) ? 9 : irq);
+#else
+	return ((irq == 7) ? 11 : irq);
+#endif
 }
 
 extern void disable_irq(unsigned int);
diff -Nru linux-2.5.50/include/asm-i386/pc9800_sca.h linux98-2.5.50/include/asm-i386/pc9800_sca.h
--- linux-2.5.50/include/asm-i386/pc9800_sca.h	1970-01-01 01:00:00.000000000 +0100
+++ linux98-2.5.50/include/asm-i386/pc9800_sca.h	2002-10-31 15:06:16.000000000 +0000
@@ -0,0 +1,25 @@
+/*
+ *  System-common area definitions for NEC PC-9800 series
+ *
+ *  Copyright (C) 1999	TAKAI Kousuke <tak@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 -Nru linux/include/asm-i386/pc9800.h linux98/include/asm-i386/pc9800.h
--- linux/include/asm-i386/pc9800.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/include/asm-i386/pc9800.h	2002-08-17 21:50:18.000000000 +0900
@@ -0,0 +1,27 @@
+/*
+ *  PC-9800 machine types.
+ *
+ *  Copyright (C) 1999	TAKAI Kosuke <tak@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 -Nru linux-2.5.60/include/asm-i386/pgtable.h linux98-2.5.60/include/asm-i386/pgtable.h
--- linux-2.5.60/include/asm-i386/pgtable.h	2003-02-11 03:38:48.000000000 +0900
+++ linux98-2.5.60/include/asm-i386/pgtable.h	2003-02-11 12:56:40.000000000 +0900
@@ -49,7 +49,11 @@
 
 #endif
 
+#ifndef CONFIG_X86_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 -Nru linux-2.5.50/include/asm-i386/setup.h linux98-2.5.50/include/asm-i386/setup.h
--- linux-2.5.50/include/asm-i386/setup.h	2002-11-25 15:09:32.000000000 +0000
+++ linux98-2.5.50/include/asm-i386/setup.h	2002-10-31 15:06:16.000000000 +0000
@@ -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 -Nru linux/include/linux/kernel.h linux98/include/linux/kernel.h
--- linux/include/linux/kernel.h	2003-01-14 14:58:03.000000000 +0900
+++ linux98/include/linux/kernel.h	2003-01-14 23:11:42.000000000 +0900
@@ -224,4 +224,10 @@
 #define __FUNCTION__ (__func__)
 #endif
 
+#ifdef CONFIG_X86_PC9800
+#define pc98 1
+#else
+#define pc98 0
+#endif
+
 #endif

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (8/26) core
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (6 preceding siblings ...)
  2003-02-17 14:06 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (7/26) misc core Osamu Tomita
@ 2003-02-17 14:07 ` Osamu Tomita
  2003-02-17 14:58   ` Sam Ravnborg
  2003-02-17 14:08 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (9/26) DMA Osamu Tomita
                   ` (17 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:07 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (8/26).

Core patches for PC98. Big changes using mach-* scheme.
For fix differences of timer, IRQ, IO port assign and memory mappings.

diff -Nru linux-2.5.61/arch/i386/Kconfig linux98-2.5.61/arch/i386/Kconfig
--- linux-2.5.61/arch/i386/Kconfig	2003-02-15 08:51:18.000000000 +0900
+++ linux98-2.5.61/arch/i386/Kconfig	2003-02-15 13:44:30.000000000 +0900
@@ -75,6 +75,12 @@
 
 	  If you don't have one of these computers, you should say N here.
 
+config X86_PC9800
+	bool "PC-9800 (NEC)"
+	help
+	  To make kernel for NEC PC-9801/PC-9821 sub-architecture, say Y.
+	  If say Y, kernel works -ONLY- on PC-9800 architecture.
+
 config X86_BIGSMP
 	bool "Support for other sub-arch SMP systems with more than 8 CPUs"
 	help
@@ -1199,7 +1205,7 @@
 
 config EISA
 	bool "EISA support"
-	depends on ISA
+	depends on ISA && !X86_PC9800
 	---help---
 	  The Extended Industry Standard Architecture (EISA) bus was
 	  developed as an open alternative to the IBM MicroChannel bus.
@@ -1217,7 +1223,7 @@
 
 config MCA
 	bool "MCA support"
-	depends on !(X86_VISWS || X86_VOYAGER)
+	depends on !(X86_VISWS || X86_VOYAGER || X86_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
diff -Nru linux-2.5.54/arch/i386/kernel/apic.c linux98-2.5.54/arch/i386/kernel/apic.c
--- linux-2.5.54/arch/i386/kernel/apic.c	2003-01-02 12:23:30.000000000 +0900
+++ linux98-2.5.54/arch/i386/kernel/apic.c	2003-01-04 10:47:57.000000000 +0900
@@ -33,6 +33,7 @@
 #include <asm/arch_hooks.h>
 
 #include <mach_apic.h>
+#include <io_ports.h>
 
 void __init apic_intr_init(void)
 {
@@ -745,9 +746,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 -Nru linux-2.5.50/arch/i386/kernel/cpu/proc.c linux98-2.5.50-ac1/arch/i386/kernel/cpu/proc.c
--- linux-2.5.50/arch/i386/kernel/cpu/proc.c	2002-11-25 15:09:12.000000000 +0000
+++ linux98-2.5.50-ac1/arch/i386/kernel/cpu/proc.c	2002-11-17 00:19:07.000000000 +0000
@@ -83,7 +83,7 @@
 #endif
 	
 	/* 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 -Nru linux-2.5.61/arch/i386/kernel/i8259.c linux98-2.5.61/arch/i386/kernel/i8259.c
--- linux-2.5.61/arch/i386/kernel/i8259.c	2003-02-15 08:52:38.000000000 +0900
+++ linux98-2.5.61/arch/i386/kernel/i8259.c	2003-02-16 17:19:03.000000000 +0900
@@ -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_X86_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)
 {
@@ -369,11 +375,11 @@
 
 static void setup_timer(void)
 {
-	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 */
 }
 
 static int timer_resume(struct device *dev, u32 level)
@@ -440,5 +446,5 @@
 	 * original braindamaged IBM FERR coupling.
 	 */
 	if (boot_cpu_data.hard_math && !cpu_has_fpu)
-		setup_irq(13, &irq13);
+		setup_irq(FPU_IRQ, &fpu_irq);
 }
diff -Nru linux-2.5.61/arch/i386/kernel/setup.c linux98-2.5.61/arch/i386/kernel/setup.c
--- linux-2.5.61/arch/i386/kernel/setup.c	2003-02-15 08:51:44.000000000 +0900
+++ linux98-2.5.61/arch/i386/kernel/setup.c	2003-02-16 17:19:03.000000000 +0900
@@ -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"
 
 int disable_pse __initdata = 0;
 
@@ -49,7 +51,6 @@
  * Machine setup..
  */
 
-char ignore_irq13;		/* set if exception 16 works */
 /* cpu data as detected by the assembly code in head.S */
 struct cpuinfo_x86 new_cpu_data __initdata = { 0, 0, 0, 0, -1, 1, 0, 0, -1 };
 /* common cpu data for all cpus */
@@ -103,98 +104,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)
 {
@@ -827,11 +738,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;
@@ -912,6 +820,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 -Nru linux-2.5.60/arch/i386/kernel/time.c linux98-2.5.60/arch/i386/kernel/time.c
--- linux-2.5.60/arch/i386/kernel/time.c	2003-02-11 03:38:37.000000000 +0900
+++ linux98-2.5.60/arch/i386/kernel/time.c	2003-02-11 10:52:52.000000000 +0900
@@ -55,12 +55,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;
 int pit_latch_buggy;              /* extern */
 
@@ -137,69 +140,13 @@
 	write_sequnlock_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;
@@ -225,9 +172,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
@@ -241,14 +188,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
@@ -329,43 +276,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 -Nru linux-2.5.60/arch/i386/kernel/timers/timer_pit.c linux98-2.5.60/arch/i386/kernel/timers/timer_pit.c
--- linux-2.5.60/arch/i386/kernel/timers/timer_pit.c	2003-02-11 03:38:51.000000000 +0900
+++ linux98-2.5.60/arch/i386/kernel/timers/timer_pit.c	2003-02-11 11:15:22.000000000 +0900
@@ -16,6 +16,7 @@
 extern spinlock_t i8259A_lock;
 extern spinlock_t i8253_lock;
 #include "do_timer.h"
+#include "io_ports.h"
 
 static int init_pit(void)
 {
@@ -77,7 +78,8 @@
 {
 	int count;
 	unsigned long flags;
-	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;
 
 	/*
@@ -85,11 +87,17 @@
 	 */
 	unsigned long jiffies_t;
 
+	/* for support LATCH is not constant */
+	if (is_1st_boot) {
+		is_1st_boot = 0;
+		count_p = LATCH;
+	}
+
 	spin_lock_irqsave(&i8253_lock, flags);
 	/* 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 
@@ -97,13 +105,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 -Nru linux-2.5.61/arch/i386/kernel/timers/timer_tsc.c linux98-2.5.61/arch/i386/kernel/timers/timer_tsc.c
--- linux-2.5.61/arch/i386/kernel/timers/timer_tsc.c	2003-02-15 08:52:04.000000000 +0900
+++ linux98-2.5.61/arch/i386/kernel/timers/timer_tsc.c	2003-02-15 14:13:41.000000000 +0900
@@ -14,6 +14,9 @@
 /* processor.h for distable_tsc flag */
 #include <asm/processor.h>
 
+#include "io_ports.h"
+#include "calibrate_tsc.h"
+
 int tsc_disable __initdata = 0;
 
 extern spinlock_t i8253_lock;
@@ -22,8 +25,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) ).
@@ -64,7 +65,12 @@
 {
 	int count;
 	int countmp;
-	static int count1=0, count2=LATCH;
+	static int count1=0, count2, initialize = 1;
+
+	if (initialize) {
+		count2 = LATCH;
+		initialize = 0;
+	}
 	/*
 	 * It is important that these two operations happen almost at
 	 * the same time. We do the RDTSC stuff first, since it's
@@ -82,10 +88,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);
 
 	if (pit_latch_buggy) {
@@ -118,83 +124,9 @@
 	} while ((now-bclock) < loops);
 }
 
-/* ------ 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)
-
 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.
-	 *
-	 * Some devices need a delay here.
-	 */
-	outb(0xb0, 0x43);			/* binary, mode 0, LSB/MSB, Ch 2 */
-	outb_p(CALIBRATE_LATCH & 0xff, 0x42);	/* LSB of count */
-	outb_p(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;
+	return mach_calibrate_tsc();
 }
 
 
diff -Nru linux-2.5.61/arch/i386/kernel/traps.c linux98-2.5.61/arch/i386/kernel/traps.c
--- linux-2.5.61/arch/i386/kernel/traps.c	2003-02-15 08:51:19.000000000 +0900
+++ linux98-2.5.61/arch/i386/kernel/traps.c	2003-02-16 17:19:03.000000000 +0900
@@ -50,6 +50,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);
@@ -57,6 +59,9 @@
 struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 },
 		{ 0, 0 }, { 0, 0 } };
 
+/* Do we ignore FPU interrupts ? */
+char ignore_fpu_irq = 0;
+
 /*
  * The IDT has to be page-aligned to simplify the Pentium
  * F0 0F bug workaround.. We have a special link segment
@@ -384,8 +389,7 @@
 	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)
@@ -422,7 +426,7 @@
 
 static void default_do_nmi(struct pt_regs * regs)
 {
-	unsigned char reason = inb(0x61);
+	unsigned char reason = get_nmi_reason();
  
 	if (!(reason & 0xc0)) {
 #if CONFIG_X86_LOCAL_APIC
@@ -446,10 +450,7 @@
 	 * 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)
@@ -643,7 +644,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);
 }
 
@@ -700,7 +701,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 -Nru linux-2.5.56/arch/i386/kernel/vm86.c linux98-2.5.56/arch/i386/kernel/vm86.c
--- linux-2.5.56/arch/i386/kernel/vm86.c	2003-01-11 05:11:22.000000000 +0900
+++ linux98-2.5.56/arch/i386/kernel/vm86.c	2003-01-11 13:32:25.000000000 +0900
@@ -720,7 +720,7 @@
 void release_x86_irqs(struct task_struct *task)
 {
 	int i;
-	for (i=3; i<16; i++)
+	for (i = FIRST_VM86_IRQ; i <= LAST_VM86_IRQ; i++)
 	    if (vm86_irqs[i].tsk == task)
 		free_vm86_irq(i);
 }
@@ -730,7 +730,7 @@
 	int bit;
 	unsigned long flags;
 	
-	if ( (irqnumber<3) || (irqnumber>15) ) return 0;
+	if (invalid_vm86_irq(irqnumber)) return 0;
 	if (vm86_irqs[irqnumber].tsk != current) return 0;
 	spin_lock_irqsave(&irqbits_lock, flags);	
 	bit = irqbits & (1 << irqnumber);
@@ -755,7 +755,7 @@
 			int irq = irqnumber & 255;
 			if (!capable(CAP_SYS_ADMIN)) return -EPERM;
 			if (!((1 << sig) & ALLOWED_SIGS)) return -EPERM;
-			if ( (irq<3) || (irq>15) ) return -EPERM;
+			if (invalid_vm86_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;
@@ -764,7 +764,7 @@
 			return irq;
 		}
 		case  VM86_FREE_IRQ: {
-			if ( (irqnumber<3) || (irqnumber>15) ) return -EPERM;
+			if (invalid_vm86_irq(irqnumber)) return -EPERM;
 			if (!vm86_irqs[irqnumber].tsk) return 0;
 			if (vm86_irqs[irqnumber].tsk != current) return -EPERM;
 			free_vm86_irq(irqnumber);
This is patchset to support NEC PC-9800 subarchitecture
against 2.5.60 (6/34).

   PC98 support patch in 2.5.50-ac1 with minimum changes
   to apply 2.5.60. (include/*)

diff -Nru linux/include/asm-i386/mach-default/calibrate_tsc.h linux98/include/asm-i386/mach-default/calibrate_tsc.h
--- linux/include/asm-i386/mach-default/calibrate_tsc.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/include/asm-i386/mach-default/calibrate_tsc.h	2002-11-05 22:15:11.000000000 +0900
@@ -0,0 +1,90 @@
+/*
+ *  include/asm-i386/mach-default/calibrate_tsc.h
+ *
+ *  Machine specific calibrate_tsc() for generic.
+ *  Split out from timer_tsc.c by Osamu Tomita <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 mach_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.
+	 *
+	 * Some devices need a delay here.
+	 */
+	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 -Nru linux-2.5.53/include/asm-i386/mach-default/io_ports.h linux98-2.5.53/include/asm-i386/mach-default/io_ports.h
--- linux-2.5.53/include/asm-i386/mach-default/io_ports.h	1970-01-01 01:00:00.000000000 +0100
+++ linux98-2.5.53/include/asm-i386/mach-default/io_ports.h	2002-10-31 15:05:52.000000000 +0000
@@ -0,0 +1,30 @@
+/*
+ *  arch/i386/mach-generic/io_ports.h
+ *
+ *  Machine specific IO port address definition for generic.
+ *  Written by Osamu Tomita <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 -Nru linux-2.5.53/include/asm-i386/mach-default/irq_vectors.h linux98-2.5.53/include/asm-i386/mach-default/irq_vectors.h
--- linux-2.5.53/include/asm-i386/mach-default/irq_vectors.h	2002-11-25 15:09:13.000000000 +0000
+++ linux98-2.5.53/include/asm-i386/mach-default/irq_vectors.h	2002-10-31 15:05:52.000000000 +0000
@@ -82,4 +82,11 @@
 #define NR_IRQS 16
 #endif
 
+#define FPU_IRQ			13
+
+#define	FIRST_VM86_IRQ		3
+#define LAST_VM86_IRQ		15
+#define invalid_vm86_irq(irq)	((irq) < 3 || (irq) > 15)
+
+
 #endif /* _ASM_IRQ_VECTORS_H */
diff -Nru linux/include/asm-i386/mach-default/mach_resources.h linux98/include/asm-i386/mach-default/mach_resources.h
--- linux/include/asm-i386/mach-default/mach_resources.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/include/asm-i386/mach-default/mach_resources.h	2002-10-21 09:59:22.000000000 +0900
@@ -0,0 +1,113 @@
+/*
+ *  include/asm-i386/mach-default/mach_resources.h
+ *
+ *  Machine specific resource allocation for generic.
+ *  Split out from setup.c by Osamu Tomita <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 -Nru linux/include/asm-i386/mach-default/mach_time.h linux98/include/asm-i386/mach-default/mach_time.h
--- linux/include/asm-i386/mach-default/mach_time.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/include/asm-i386/mach-default/mach_time.h	2002-10-21 10:07:35.000000000 +0900
@@ -0,0 +1,122 @@
+/*
+ *  include/asm-i386/mach-default/mach_time.h
+ *
+ *  Machine specific set RTC function for generic.
+ *  Split out from time.c by Osamu Tomita <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 -Nru linux/include/asm-i386/mach-default/mach_traps.h linux98/include/asm-i386/mach-default/mach_traps.h
--- linux/include/asm-i386/mach-default/mach_traps.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/include/asm-i386/mach-default/mach_traps.h	2002-11-05 22:42:05.000000000 +0900
@@ -0,0 +1,29 @@
+/*
+ *  include/asm-i386/mach-default/mach_traps.h
+ *
+ *  Machine specific NMI handling for generic.
+ *  Split out from traps.c by Osamu Tomita <tomita@cinet.co.jp>
+ */
+#ifndef _MACH_TRAPS_H
+#define _MACH_TRAPS_H
+
+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 -Nru linux/include/asm-i386/mach-pc9800/calibrate_tsc.h linux98/include/asm-i386/mach-pc9800/calibrate_tsc.h
--- linux/include/asm-i386/mach-pc9800/calibrate_tsc.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/include/asm-i386/mach-pc9800/calibrate_tsc.h	2002-11-05 22:19:50.000000000 +0900
@@ -0,0 +1,71 @@
+/*
+ *  include/asm-i386/mach-pc9800/calibrate_tsc.h
+ *
+ *  Machine specific calibrate_tsc() for PC-9800.
+ *  Written by Osamu Tomita <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 mach_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 -Nru linux/include/asm-i386/mach-pc9800/do_timer.h linux98/include/asm-i386/mach-pc9800/do_timer.h
--- linux/include/asm-i386/mach-pc9800/do_timer.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/include/asm-i386/mach-pc9800/do_timer.h	2002-10-16 13:20:29.000000000 +0900
@@ -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 -Nru linux-2.5.53/include/asm-i386/mach-pc9800/io_ports.h linux98-2.5.53/include/asm-i386/mach-pc9800/io_ports.h
--- linux-2.5.53/include/asm-i386/mach-pc9800/io_ports.h	1970-01-01 01:00:00.000000000 +0100
+++ linux98-2.5.53/include/asm-i386/mach-pc9800/io_ports.h	2002-10-31 15:05:52.000000000 +0000
@@ -0,0 +1,30 @@
+/*
+ *  include/asm-i386/mach-pc9800/io_ports.h
+ *
+ *  Machine specific IO port address definition for PC-9800.
+ *  Written by Osamu Tomita <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 -Nru linux-2.5.53/include/asm-i386/mach-pc9800/irq_vectors.h linux98-2.5.53/include/asm-i386/mach-pc9800/irq_vectors.h
--- linux-2.5.53/include/asm-i386/mach-pc9800/irq_vectors.h	1970-01-01 01:00:00.000000000 +0100
+++ linux98-2.5.53/include/asm-i386/mach-pc9800/irq_vectors.h	2002-10-31 15:05:52.000000000 +0000
@@ -0,0 +1,93 @@
+/*
+ * This file should contain #defines for all of the interrupt vector
+ * numbers used by this architecture.
+ *
+ * In addition, there are some standard defines:
+ *
+ *	FIRST_EXTERNAL_VECTOR:
+ *		The first free place for external interrupts
+ *
+ *	SYSCALL_VECTOR:
+ *		The IRQ vector a syscall makes the user to kernel transition
+ *		under.
+ *
+ *	TIMER_IRQ:
+ *		The IRQ number the timer interrupt comes in at.
+ *
+ *	NR_IRQS:
+ *		The total number of interrupt vectors (including all the
+ *		architecture specific interrupts) needed.
+ *
+ */			
+#ifndef _ASM_IRQ_VECTORS_H
+#define _ASM_IRQ_VECTORS_H
+
+/*
+ * IDT vectors usable for external interrupt sources start
+ * at 0x20:
+ */
+#define FIRST_EXTERNAL_VECTOR	0x20
+
+#define SYSCALL_VECTOR		0x80
+
+/*
+ * Vectors 0x20-0x2f are used for ISA interrupts.
+ */
+
+/*
+ * Special IRQ vectors used by the SMP architecture, 0xf0-0xff
+ *
+ *  some of the following vectors are 'rare', they are merged
+ *  into a single vector (CALL_FUNCTION_VECTOR) to save vector space.
+ *  TLB, reschedule and local APIC vectors are performance-critical.
+ *
+ *  Vectors 0xf0-0xfa are free (reserved for future Linux use).
+ */
+#define SPURIOUS_APIC_VECTOR	0xff
+#define ERROR_APIC_VECTOR	0xfe
+#define INVALIDATE_TLB_VECTOR	0xfd
+#define RESCHEDULE_VECTOR	0xfc
+#define CALL_FUNCTION_VECTOR	0xfb
+
+#define THERMAL_APIC_VECTOR	0xf0
+/*
+ * Local APIC timer IRQ vector is on a different priority level,
+ * to work around the 'lost local interrupt if more than 2 IRQ
+ * sources per level' errata.
+ */
+#define LOCAL_TIMER_VECTOR	0xef
+
+/*
+ * First APIC vector available to drivers: (vectors 0x30-0xee)
+ * we start at 0x31 to spread out vectors evenly between priority
+ * levels. (0x80 is the syscall vector)
+ */
+#define FIRST_DEVICE_VECTOR	0x31
+#define FIRST_SYSTEM_VECTOR	0xef
+
+#define TIMER_IRQ 0
+
+/*
+ * 16 8259A IRQ's, 208 potential APIC interrupt sources.
+ * Right now the APIC is mostly only used for SMP.
+ * 256 vectors is an architectural limit. (we can have
+ * more than 256 devices theoretically, but they will
+ * have to use shared interrupts)
+ * Since vectors 0x00-0x1f are used/reserved for the CPU,
+ * the usable vector space is 0x20-0xff (224 vectors)
+ */
+#ifdef CONFIG_X86_IO_APIC
+#define NR_IRQS 224
+#else
+#define NR_IRQS 16
+#endif
+
+#define FPU_IRQ			8
+
+#define	FIRST_VM86_IRQ		2
+#define LAST_VM86_IRQ		15
+#define invalid_vm86_irq(irq)	((irq) < 2 || (irq) == 7 || (irq) > 15)
+
+#endif /* _ASM_IRQ_VECTORS_H */
+
+
diff -Nru linux/include/asm-i386/mach-pc9800/mach_resources.h linux98/include/asm-i386/mach-pc9800/mach_resources.h
--- linux/include/asm-i386/mach-pc9800/mach_resources.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/include/asm-i386/mach-pc9800/mach_resources.h	2002-10-26 17:35:19.000000000 +0900
@@ -0,0 +1,192 @@
+/*
+ *  include/asm-i386/mach-pc9800/mach_resources.h
+ *
+ *  Machine specific resource allocation for PC-9800.
+ *  Written by Osamu Tomita <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 -Nru linux/include/asm-i386/mach-pc9800/mach_time.h linux98/include/asm-i386/mach-pc9800/mach_time.h
--- linux/include/asm-i386/mach-pc9800/mach_time.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/include/asm-i386/mach-pc9800/mach_time.h	2002-10-21 11:23:06.000000000 +0900
@@ -0,0 +1,136 @@
+/*
+ *  include/asm-i386/mach-pc9800/mach_time.h
+ *
+ *  Machine specific set RTC function for PC-9800.
+ *  Written by Osamu Tomita <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 -Nru linux/include/asm-i386/mach-pc9800/mach_traps.h linux98/include/asm-i386/mach-pc9800/mach_traps.h
--- linux/include/asm-i386/mach-pc9800/mach_traps.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/include/asm-i386/mach-pc9800/mach_traps.h	2002-11-05 22:46:55.000000000 +0900
@@ -0,0 +1,27 @@
+/*
+ *  include/asm-i386/mach-pc9800/mach_traps.h
+ *
+ *  Machine specific NMI handling for PC-9800.
+ *  Written by Osamu Tomita <tomita@cinet.co.jp>
+ */
+#ifndef _MACH_TRAPS_H
+#define _MACH_TRAPS_H
+
+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) & 6) ? 0x80 : 0;
+}
+
+static inline void reassert_nmi(void)
+{
+	outb(0x09, 0x50);	/* disable NMI once */
+	outb(0x09, 0x52);	/* re-enable it */
+}
+
+#endif /* !_MACH_TRAPS_H */
diff -Nru linux-2.5.53/include/asm-i386/mach-pc9800/setup_arch_post.h linux98-2.5.53/include/asm-i386/mach-pc9800/setup_arch_post.h
--- linux-2.5.53/include/asm-i386/mach-pc9800/setup_arch_post.h	1970-01-01 01:00:00.000000000 +0100
+++ linux98-2.5.53/include/asm-i386/mach-pc9800/setup_arch_post.h	2002-10-31 15:05:52.000000000 +0000
@@ -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 -Nru linux-2.5.53/include/asm-i386/mach-pc9800/setup_arch_pre.h linux98-2.5.53/include/asm-i386/mach-pc9800/setup_arch_pre.h
--- linux-2.5.53/include/asm-i386/mach-pc9800/setup_arch_pre.h	1970-01-01 01:00:00.000000000 +0100
+++ linux98-2.5.53/include/asm-i386/mach-pc9800/setup_arch_pre.h	2002-10-31 15:05:52.000000000 +0000
@@ -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 -Nru linux-2.5.53/include/asm-i386/mach-visws/irq_vectors.h linux98-2.5.53/include/asm-i386/mach-visws/irq_vectors.h
--- linux-2.5.53/include/asm-i386/mach-visws/irq_vectors.h	2002-12-10 11:45:43.000000000 +0900
+++ linux98-2.5.53/include/asm-i386/mach-visws/irq_vectors.h	2002-12-16 09:15:54.000000000 +0900
@@ -61,4 +61,10 @@
 #define NR_IRQS 16
 #endif
 
+#define FPU_IRQ			13
+
+#define	FIRST_VM86_IRQ		3
+#define LAST_VM86_IRQ		15
+#define invalid_vm86_irq(irq)	((irq) < 3 || (irq) > 15)
+
 #endif /* _ASM_IRQ_VECTORS_H */
diff -Nru linux-2.5.53/include/asm-i386/mach-voyager/irq_vectors.h linux98-2.5.53/include/asm-i386/mach-voyager/irq_vectors.h
--- linux-2.5.53/include/asm-i386/mach-voyager/irq_vectors.h	2002-11-25 15:09:13.000000000 +0000
+++ linux98-2.5.53/include/asm-i386/mach-voyager/irq_vectors.h	2002-10-31 15:05:52.000000000 +0000
@@ -57,6 +57,12 @@
 
 #define NR_IRQS 224
 
+#define FPU_IRQ				13
+
+#define	FIRST_VM86_IRQ		3
+#define LAST_VM86_IRQ		15
+#define invalid_vm86_irq(irq)	((irq) < 3 || (irq) > 15)
+
 #ifndef __ASSEMBLY__
 extern asmlinkage void vic_cpi_interrupt(void);
 extern asmlinkage void vic_sys_interrupt(void);
diff -Nru linux-2.5.54/include/asm-i386/processor.h linux98-2.5.54/include/asm-i386/processor.h
--- linux-2.5.54/include/asm-i386/processor.h	2003-01-02 12:21:01.000000000 +0900
+++ linux98-2.5.54/include/asm-i386/processor.h	2003-01-04 10:47:57.000000000 +0900
@@ -92,7 +92,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 -Nru linux/include/asm-i386/timex.h linux98/include/asm-i386/timex.h
--- linux/include/asm-i386/timex.h	2002-02-14 18:09:15.000000000 +0900
+++ linux98/include/asm-i386/timex.h	2002-02-14 23:58:57.000000000 +0900
@@ -9,11 +9,15 @@
 #include <linux/config.h>
 #include <asm/msr.h>
 
+#ifdef CONFIG_X86_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) * \
This is patchset to support NEC PC-9800 subarchitecture
against 2.5.60 (22/34).

Misc files for support PC98.

diff -Nru linux-2.5.60/kernel/timer.c linux98-2.5.60/kernel/timer.c
--- linux-2.5.60/kernel/timer.c	2003-02-11 03:38:50.000000000 +0900
+++ linux98-2.5.60/kernel/timer.c	2003-02-11 13:04:49.000000000 +0900
@@ -437,8 +437,13 @@
 /*
  * Timekeeping variables
  */
+#ifndef CONFIG_X86_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)));

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (9/26) DMA
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (7 preceding siblings ...)
  2003-02-17 14:07 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (8/26) core Osamu Tomita
@ 2003-02-17 14:08 ` Osamu Tomita
  2003-02-17 14:09 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (10/26) floppy #1 Osamu Tomita
                   ` (16 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:08 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (9/26).

DMA support for PC98.

diff -Nru linux/include/asm-i386/dma.h linux98/include/asm-i386/dma.h
--- linux/include/asm-i386/dma.h	2002-07-21 04:52:59.000000000 +0900
+++ linux98/include/asm-i386/dma.h	2002-08-17 22:15:06.000000000 +0900
@@ -10,6 +10,9 @@
 
 #include <linux/config.h>
 #include <linux/spinlock.h>	/* And spinlocks */
+#ifdef CONFIG_X86_PC9800
+#include <asm/pc9800_dma.h>
+#else /* !CONFIG_X86_PC9800 */
 #include <asm/io.h>		/* need byte IO */
 #include <linux/delay.h>
 
@@ -72,8 +75,10 @@
 
 #define MAX_DMA_CHANNELS	8
 
+#ifndef CONFIG_X86_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_X86_PC9800 */
+
 #endif /* _ASM_DMA_H */
diff -Nru linux/include/asm-i386/pc9800_dma.h linux98/include/asm-i386/pc9800_dma.h
--- linux/include/asm-i386/pc9800_dma.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/include/asm-i386/pc9800_dma.h	2002-08-17 21:15:01.000000000 +0900
@@ -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 -Nru linux/include/asm-i386/scatterlist.h linux98/include/asm-i386/scatterlist.h
--- linux/include/asm-i386/scatterlist.h	2002-04-15 04:18:52.000000000 +0900
+++ linux98/include/asm-i386/scatterlist.h	2002-04-17 10:37:22.000000000 +0900
@@ -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_X86_PC9800
+#define ISA_DMA_THRESHOLD (0xffffffff)
+#else
 #define ISA_DMA_THRESHOLD (0x00ffffff)
+#endif
 
 #endif /* !(_I386_SCATTERLIST_H) */
diff -Nru linux/kernel/dma.c linux98/kernel/dma.c
--- linux/kernel/dma.c	2002-08-11 10:41:22.000000000 +0900
+++ linux98/kernel/dma.c	2002-08-21 09:53:59.000000000 +0900
@@ -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_X86_PC9800
 	{ 1, "cascade" },
 	{ 0, 0 },
 	{ 0, 0 },
 	{ 0, 0 }
+#endif
 };
 
 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (10/26) floppy #1
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (8 preceding siblings ...)
  2003-02-17 14:08 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (9/26) DMA Osamu Tomita
@ 2003-02-17 14:09 ` Osamu Tomita
  2003-02-17 14:09 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (11/26) floppy #2 Osamu Tomita
                   ` (15 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:09 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (10/26).

Driver for PC98 standard floppy disk drive. 1 of 2.
floppy98.c is too big for sending via. LKML.
I separeate patch to floppy98-1.patch and floppy98-2.patch.

diff -Nru linux-2.5.61/drivers/block/Kconfig linux98-2.5.61/drivers/block/Kconfig
--- linux-2.5.61/drivers/block/Kconfig	2003-02-15 08:51:20.000000000 +0900
+++ linux98-2.5.61/drivers/block/Kconfig	2003-02-16 17:19:03.000000000 +0900
@@ -6,6 +6,7 @@
 
 config BLK_DEV_FD
 	tristate "Normal floppy disk support"
+	depends on !X86_PC9800
 	---help---
 	  If you want to use the floppy disk drive(s) of your PC under Linux,
 	  say Y. Information about this driver, especially important for IBM
@@ -27,6 +28,13 @@
 	tristate "Atari floppy support"
 	depends on ATARI
 
+config BLK_DEV_FD98
+	tristate "NEC PC-9800 floppy disk support"
+	depends on X86_PC9800
+	---help---
+	  If you want to use the floppy disk drive(s) of NEC PC-9801/PC-9821,
+	  say Y.
+
 config BLK_DEV_SWIM_IOP
 	bool "Macintosh IIfx/Quadra 900/Quadra 950 floppy support (EXPERIMENTAL)"
 	depends on MAC && EXPERIMENTAL
diff -Nru linux-2.5.61/drivers/block/Makefile linux98-2.5.61/drivers/block/Makefile
--- linux-2.5.61/drivers/block/Makefile	2003-02-15 08:52:27.000000000 +0900
+++ linux98-2.5.61/drivers/block/Makefile	2003-02-16 17:19:03.000000000 +0900
@@ -12,6 +12,7 @@
 
 obj-$(CONFIG_MAC_FLOPPY)	+= swim3.o
 obj-$(CONFIG_BLK_DEV_FD)	+= floppy.o
+obj-$(CONFIG_BLK_DEV_FD98)	+= floppy98.o
 obj-$(CONFIG_AMIGA_FLOPPY)	+= amiflop.o
 obj-$(CONFIG_ATARI_FLOPPY)	+= ataflop.o
 obj-$(CONFIG_BLK_DEV_SWIM_IOP)	+= swim_iop.o
diff -Nru linux-2.5.61/drivers/block/floppy98.c linux98-2.5.61/drivers/block/floppy98.c
--- linux-2.5.61/drivers/block/floppy98.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/drivers/block/floppy98.c	2003-02-16 20:34:27.000000000 +0900
@@ -0,0 +1,2240 @@
+/*
+ *  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 entropy@wintermute.wpi.edu (Lawrence Foard). Linus.
+ */
+
+/*
+ * Automatic floppy-detection and formatting written by Werner Almesberger
+ * (almesber@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 (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 <acme@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 <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>
+#include <linux/version.h>
+#define FDPATCHES
+#include <linux/fdreg.h>
+
+/*
+ * 1998/1/21 -- Richard Gooch <rgooch@atnf.csiro.au> -- devfs support
+ */
+
+
+#include <linux/fd.h>
+#define FLOPPY98_MOTOR_MASK 0x08
+
+#define FDPATCHES
+#include <linux/hdreg.h>
+#define FD98_STATUS	(0 + FD_IOPORT )
+#define FD98_DATA	(2 + FD_IOPORT )
+#define FD_MODE		(4 + FD_IOPORT )
+#define FD_MODE_CHANGE	0xbe
+#define FD_EMODE_CHANGE	0x4be
+
+#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;
+
+#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 DEVICE_NAME "floppy"
+
+#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(unsigned long);
+
+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 = TIMER_INITIALIZER(floppy_shutdown, 0, 0);
+
+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 & FLOPPY98_MOTOR_MASK)
+		floppy_grab_irq_and_dma();
+
+	if (olddor & FLOPPY98_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(FD98_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 = TIMER_INITIALIZER(NULL, 0, 0);
+
+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(FD98_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,FD98_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(FD98_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(FD98_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(unsigned long data)
+{
+	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(int drive)
+{
+	int type = ITYPE(UDRS->fd_device);
+	if (type) {
+		auto_detect_mode = 0;
+		_floppy = floppy_type + type;
+	} else if (auto_detect_mode == 0) {
+		auto_detect_mode = 1;
+		retry_auto_detect = 0;
+		_floppy = current_type[drive];
+	}
+#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();
+}
+

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (11/26) floppy #2
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (9 preceding siblings ...)
  2003-02-17 14:09 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (10/26) floppy #1 Osamu Tomita
@ 2003-02-17 14:09 ` Osamu Tomita
  2003-02-17 14:10 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (12/26) FS Osamu Tomita
                   ` (14 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:09 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (11/26).

Driver for PC98 standard floppy disk drive. 2 of 2.
floppy98.c is too big for sending via. LKML.
I separeate patch to floppy98-1.patch and floppy98-2.patch.

diff -Nru linux-2.5.61/drivers/block/floppy98.c linux98-2.5.61/drivers/block/floppy98.c
--- linux-2.5.61/drivers/block/floppy98.c	2003-02-16 20:42:53.000000000 +0900
+++ linux98-2.5.61/drivers/block/floppy98.c	2003-02-16 17:19:03.000000000 +0900
@@ -2238,3 +2238,2431 @@
 	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(drive);
+	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;
+}
+
+/*
+ * 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((long)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((long)current_req->rq_disk->private_data);
+
+	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; }
+	int drive;
+	int tmp;
+
+	lastredo = jiffies;
+	if (current_drive < N_DRIVE)
+		floppy_off(current_drive);
+
+	for (;;) {
+		if (!current_req) {
+			struct request *req;
+
+			spin_lock_irq(floppy_queue.queue_lock);
+			req = elv_next_request(&floppy_queue);
+			spin_unlock_irq(floppy_queue.queue_lock);
+			if (!req) {
+				do_floppy = NULL;
+				unlock_fdc();
+				return;
+			}
+			current_req = req;
+		}
+		drive = (long)current_req->rq_disk->private_data;
+		set_fdc(drive);
+		reschedule_timeout(current_reqD, "redo fd request", 0);
+
+		set_floppy(drive);
+		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((long)bdev->bd_disk->private_data, &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(drive);
+			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 = (long)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=(long)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 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};
+
+static void __init register_devfs_entries (int drive)
+{
+    int base_minor, i;
+
+    base_minor = (drive < 4) ? drive : (124 + drive);
+    if (UDP->cmos < NUMBER(default_drive_params)) {
+	i = 0;
+	do {
+	    char name[16];
+
+	    sprintf(name, "floppy/%d%s", drive, table[table_sup[UDP->cmos][i]]);
+	    devfs_register(NULL, name, DEVFS_FL_DEFAULT, FLOPPY_MAJOR,
+			    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;
+	return get_disk(disks[drive]);
+}
+
+int __init floppy_init(void)
+{
+	int i,unit,drive;
+	int err;
+
+	raw_cmd = NULL;
+	FDC1 = 0x90;
+
+	for (i=0; i<N_DRIVE; i++) {
+		disks[i] = alloc_disk(1);
+		if (!disks[i])
+			goto Enomem;
+	}
+
+	devfs_mk_dir (NULL, "floppy", NULL);
+	if (register_blkdev(FLOPPY_MAJOR,"fd",&floppy_fops)) {
+		printk("Unable to get major %d for floppy\n",FLOPPY_MAJOR);
+		err = -EBUSY;
+		goto out;
+	}
+
+	for (i=0; i<N_DRIVE; i++) {
+		disks[i]->major = FLOPPY_MAJOR;
+		disks[i]->first_minor = TOMINOR(i);
+		disks[i]->fops = &floppy_fops;
+		sprintf(disks[i]->disk_name, "fd%d", i);
+	}
+
+	blk_register_region(MKDEV(FLOPPY_MAJOR, 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++) {
+		init_timer(&motor_off_timer[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*)(long)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(FLOPPY_MAJOR, 0), 256);
+	unregister_blkdev(FLOPPY_MAJOR,"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 unregister_devfs_entries (int drive)
+{
+    int i;
+
+    if (UDP->cmos < NUMBER(default_drive_params)) {
+	i = 0;
+	do {
+	    devfs_remove("floppy/%d%s", drive, table[table_sup[UDP->cmos][i]]);
+	} while (table_sup[UDP->cmos][i++]);
+    }
+}
+
+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);
+	blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
+	unregister_blkdev(FLOPPY_MAJOR, "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]);
+			unregister_devfs_entries(drive);
+		}
+		put_disk(disks[drive]);
+	}
+	devfs_remove("floppy");
+
+	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

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (12/26) FS
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (10 preceding siblings ...)
  2003-02-17 14:09 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (11/26) floppy #2 Osamu Tomita
@ 2003-02-17 14:10 ` Osamu Tomita
  2003-02-17 14:11 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (13/26) IDE Osamu Tomita
                   ` (13 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:10 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (12/26).

FAT fs and partition table support for PC98.
FAT fs created by PC9800 MS-DOS has curious media descripter. (BUG?)

diff -Nru linux/fs/fat/inode.c linux98/fs/fat/inode.c
--- linux/fs/fat/inode.c	2003-01-02 12:21:53.000000000 +0900
+++ linux98/fs/fat/inode.c	2003-01-04 20:02:52.000000000 +0900
@@ -939,7 +939,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 -Nru linux/fs/partitions/Kconfig linux98/fs/partitions/Kconfig
--- linux/fs/partitions/Kconfig	2002-11-28 07:36:18.000000000 +0900
+++ linux98/fs/partitions/Kconfig	2002-12-12 14:27:58.000000000 +0900
@@ -177,6 +177,13 @@
 
 	  If unsure, say N.
 
+config NEC98_PARTITION
+	bool "NEC PC-9800 partition table support" if PARTITION_ADVANCED
+	default y if !PARTITION_ADVANCED && X86_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 SGI_PARTITION
 	bool "SGI partition support" if PARTITION_ADVANCED
 	default y if !PARTITION_ADVANCED && (SGI_IP22 || SGI_IP27)
diff -Nru linux-2.5.60/fs/partitions/Makefile linux98-2.5.60/fs/partitions/Makefile
--- linux-2.5.60/fs/partitions/Makefile	2003-02-11 03:38:28.000000000 +0900
+++ linux98-2.5.60/fs/partitions/Makefile	2003-02-11 12:50:18.000000000 +0900
@@ -16,3 +16,4 @@
 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
diff -Nru linux/fs/partitions/check.c linux98/fs/partitions/check.c
--- linux/fs/partitions/check.c	2003-01-09 13:04:25.000000000 +0900
+++ linux98/fs/partitions/check.c	2003-01-10 10:19:55.000000000 +0900
@@ -28,6 +28,7 @@
 #include "ldm.h"
 #include "mac.h"
 #include "msdos.h"
+#include "nec98.h"
 #include "osf.h"
 #include "sgi.h"
 #include "sun.h"
@@ -51,6 +52,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 -Nru linux/fs/partitions/msdos.c linux98/fs/partitions/msdos.c
--- linux/fs/partitions/msdos.c	2002-11-28 07:36:05.000000000 +0900
+++ linux98/fs/partitions/msdos.c	2002-12-12 14:36:18.000000000 +0900
@@ -219,7 +219,7 @@
  * Create devices for BSD partitions listed in a disklabel, under a
  * dos-like partition. See parse_extended() for more information.
  */
-static void
+void
 parse_bsd(struct parsed_partitions *state, struct block_device *bdev,
 		u32 offset, u32 size, int origin, char *flavour,
 		int max_partitions)
diff -Nru linux/fs/partitions/nec98.c linux98/fs/partitions/nec98.c
--- linux/fs/partitions/nec98.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98/fs/partitions/nec98.c	2003-02-13 23:55:09.000000000 +0900
@@ -0,0 +1,270 @@
+/*
+ *  NEC PC-9800 series partition supports
+ *
+ *  Copyright (C) 1999	Kyoto University Microcomputer Club
+ */
+
+#include <linux/config.h>
+#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)
+
+extern int pc98_bios_param(struct block_device *bdev, int *ip);
+
+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_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
+
+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 };
+
+		pc98_bios_param(bdev, 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_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;
+}
+\f
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -Nru linux/fs/partitions/nec98.h linux98/fs/partitions/nec98.h
--- linux/fs/partitions/nec98.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/fs/partitions/nec98.h	2002-07-26 11:10:08.000000000 +0900
@@ -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);

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (13/26) IDE
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (11 preceding siblings ...)
  2003-02-17 14:10 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (12/26) FS Osamu Tomita
@ 2003-02-17 14:11 ` Osamu Tomita
  2003-02-17 14:12 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (14/26) input Osamu Tomita
                   ` (12 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:11 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (13/26).

PC98 standard IDE I/F support.

diff -Nru linux-2.5.60/drivers/ide/Kconfig linux98-2.5.60/drivers/ide/Kconfig
--- linux-2.5.60/drivers/ide/Kconfig	2003-02-11 03:38:31.000000000 +0900
+++ linux98-2.5.60/drivers/ide/Kconfig	2003-02-11 12:54:00.000000000 +0900
@@ -1054,6 +1054,11 @@
 
 	  If unsure, say N.
 
+config BLK_DEV_IDE_PC9800
+	bool
+	depends on X86_PC9800
+	default y
+
 ##if [ "$CONFIG_IDE_TASKFILE_IO" = "y" ]; then
 ##  dep_mbool CONFIG_BLK_DEV_TF_DISK $CONFIG_BLK_DEV_IDEDISK
 ##else
diff -Nru linux/drivers/ide/ide-disk.c linux98/drivers/ide/ide-disk.c
--- linux/drivers/ide/ide-disk.c	2002-11-28 11:52:55.000000000 +0900
+++ linux98/drivers/ide/ide-disk.c	2002-11-28 13:23:54.000000000 +0900
@@ -1604,6 +1604,71 @@
 		blk_queue_max_sectors(&drive->queue, 2048);
 #endif
 
+#ifdef CONFIG_X86_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_X86_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;
@@ -1637,6 +1702,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_X86_PC9800 */
+
 	printk (KERN_INFO "%s: %ld sectors", drive->name, capacity);
 
 	/* Give size in megabytes (MB), not mebibytes (MiB). */
diff -Nru linux/drivers/ide/ide-probe.c linux98/drivers/ide/ide-probe.c
--- linux/drivers/ide/ide-probe.c	2002-12-16 11:08:10.000000000 +0900
+++ linux98/drivers/ide/ide-probe.c	2002-12-20 14:55:11.000000000 +0900
@@ -573,7 +573,7 @@
 
 	if (hwif->mmio == 2)
 		return 0;
-	addr_errs  = hwif_check_region(hwif, hwif->io_ports[IDE_DATA_OFFSET], 1);
+	addr_errs  = hwif_check_region(hwif, 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, hwif->io_ports[i], 1);
 	if (hwif->io_ports[IDE_CONTROL_OFFSET])
@@ -622,7 +622,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);
@@ -644,6 +646,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) {
@@ -973,7 +978,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_X86_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,
@@ -983,6 +988,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_X86_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 -Nru linux/drivers/ide/ide-proc.c linux98/drivers/ide/ide-proc.c
--- linux/drivers/ide/ide-proc.c	2002-09-16 11:18:30.000000000 +0900
+++ linux98/drivers/ide/ide-proc.c	2002-09-16 13:53:42.000000000 +0900
@@ -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_X86_PC9800
+		case ide_pc9800:	name = "pc9800";	break;
+#endif
 		default:		name = "(unknown)";	break;
 	}
 	len = sprintf(page, "%s\n", name);
diff -Nru linux/drivers/ide/ide.c linux98/drivers/ide/ide.c
--- linux/drivers/ide/ide.c	2003-01-09 13:03:59.000000000 +0900
+++ linux98/drivers/ide/ide.c	2003-01-10 10:27:16.000000000 +0900
@@ -547,7 +547,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);
 		}
 	}
 }
@@ -2011,6 +2012,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 -Nru linux/drivers/ide/legacy/Makefile linux98/drivers/ide/legacy/Makefile
--- linux/drivers/ide/legacy/Makefile	2002-12-16 11:07:47.000000000 +0900
+++ linux98/drivers/ide/legacy/Makefile	2002-12-17 09:42:08.000000000 +0900
@@ -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,6 +16,10 @@
 obj-$(CONFIG_BLK_DEV_IDECS)		+= ide-cs.o
 
 # Last of all
+ifneq ($(CONFIG_X86_PC9800),y)
 obj-$(CONFIG_BLK_DEV_HD)		+= hd.o
+else
+obj-$(CONFIG_BLK_DEV_HD)		+= hd98.o
+endif
 
 EXTRA_CFLAGS	:= -Idrivers/ide
diff -Nru linux/drivers/ide/legacy/hd98.c linux98/drivers/ide/legacy/hd98.c
--- linux/drivers/ide/legacy/hd98.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98/drivers/ide/legacy/hd98.c	2002-10-26 15:42:09.000000000 +0900
@@ -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, lankeste@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 (liljeber@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_X86_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 -Nru linux/drivers/ide/legacy/pc9800.c linux98/drivers/ide/legacy/pc9800.c
--- linux/drivers/ide/legacy/pc9800.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98/drivers/ide/legacy/pc9800.c	2002-10-08 17:06:39.000000000 +0900
@@ -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 -Nru linux/include/asm-i386/ide.h linux98/include/asm-i386/ide.h
--- linux/include/asm-i386/ide.h	2002-10-12 13:21:31.000000000 +0900
+++ linux98/include/asm-i386/ide.h	2002-10-13 23:17:54.000000000 +0900
@@ -26,6 +26,9 @@
 static __inline__ int ide_default_irq(ide_ioreg_t base)
 {
 	switch (base) {
+#ifdef CONFIG_X86_PC9800
+		case 0x640: return 9;
+#endif /* CONFIG_X86_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_X86_PC9800
 	static unsigned long ata_io_base[MAX_HWIFS] = { 0x1f0, 0x170, 0x1e8, 0x168, 0x1e0, 0x160 };
+#else /* CONFIG_X86_PC9800 */
+	static unsigned long ata_io_base[MAX_HWIFS] = { 0x640, 0x640, 0, 0, 0, 0 };
+#endif /* !CONFIG_X86_PC9800 */
 
 	return ata_io_base[index];
 }
@@ -48,13 +55,24 @@
 {
 	ide_ioreg_t reg = data_port;
 	int i;
+#ifdef CONFIG_X86_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_X86_PC9800
 		reg += 1;
+#else
+		reg += increment;
+#endif
 	}
 	if (ctrl_port) {
 		hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
+#ifdef CONFIG_X86_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 -Nru linux/include/linux/hdreg.h linux98/include/linux/hdreg.h
--- linux/include/linux/hdreg.h	2002-10-12 13:22:07.000000000 +0900
+++ linux98/include/linux/hdreg.h	2002-10-12 19:38:02.000000000 +0900
@@ -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_X86_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_X86_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_X86_PC9800 */
 
 /* remainder is shared between hd.c, ide.c, ide-cd.c, and the hdparm utility */
 
diff -Nru linux/include/linux/ide.h linux98/include/linux/ide.h
--- linux/include/linux/ide.h	2002-11-11 15:46:21.000000000 +0900
+++ linux98/include/linux/ide.h	2002-11-11 16:43:43.000000000 +0900
@@ -294,7 +294,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 {

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (14/26) input
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (12 preceding siblings ...)
  2003-02-17 14:11 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (13/26) IDE Osamu Tomita
@ 2003-02-17 14:12 ` Osamu Tomita
  2003-03-14  8:22   ` Vojtech Pavlik
  2003-02-17 14:13 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (15/26) kanji Osamu Tomita
                   ` (11 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:12 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Vojtech Pavlik, Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (14/26).

Drivers for PC98 standard keyboard/mouse.

diff -Nru linux/drivers/input/keyboard/98kbd.c linux98/drivers/input/keyboard/98kbd.c
--- linux/drivers/input/keyboard/98kbd.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98/drivers/input/keyboard/98kbd.c	2002-11-15 15:57:45.000000000 +0000
@@ -0,0 +1,379 @@
+/*
+ *  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 <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	253
+#define KBD98_KEY_UNKNOWN	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 {
+		unsigned char scancode;
+		unsigned char keycode;
+	} emul;
+	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 (kbd98->emul.scancode != KBD98_KEY_UNKNOWN
+	    && scancode != kbd98->emul.scancode) {
+		input_report_key(&kbd98->dev, kbd98->emul.keycode, 0);
+		kbd98->emul.scancode = KBD98_KEY_UNKNOWN;
+	}
+
+	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 (press) {
+				kbd98->emul.scancode = scancode;
+				kbd98->emul.keycode = keycode;
+				if (kbd98->jis[i].emul[kbd98->shift].shift
+								!= kbd98->shift)
+					input_report_key(&kbd98->dev,
+							KEY_RIGHTSHIFT,
+							!(kbd98->shift));
+			}
+
+			input_report_key(&kbd98->dev, keycode, press);
+			if (!press) {
+				if (kbd98->jis[i].emul[kbd98->shift].shift
+								!= kbd98->shift)
+					input_report_key(&kbd98->dev,
+							KEY_RIGHTSHIFT,
+							kbd98->shift);
+				kbd98->emul.scancode = KBD98_KEY_UNKNOWN;
+			}
+
+			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->emul.scancode = KBD98_KEY_UNKNOWN;
+	
+	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 -Nru linux-2.5.61/drivers/input/keyboard/Kconfig linux98-2.5.61/drivers/input/keyboard/Kconfig
--- linux-2.5.61/drivers/input/keyboard/Kconfig	2003-02-15 08:52:39.000000000 +0900
+++ linux98-2.5.61/drivers/input/keyboard/Kconfig	2003-02-16 17:19:03.000000000 +0900
@@ -90,3 +90,15 @@
 	  The module will be called amikbd. 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 X86_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 -Nru linux-2.5.52/drivers/input/keyboard/Makefile linux98-2.5.52/drivers/input/keyboard/Makefile
--- linux-2.5.52/drivers/input/keyboard/Makefile	2002-12-16 11:07:54.000000000 +0900
+++ linux98-2.5.52/drivers/input/keyboard/Makefile	2002-12-16 21:28:54.000000000 +0900
@@ -10,3 +10,4 @@
 obj-$(CONFIG_KEYBOARD_XTKBD)		+= xtkbd.o
 obj-$(CONFIG_KEYBOARD_AMIGA)		+= amikbd.o
 obj-$(CONFIG_KEYBOARD_NEWTON)		+= newtonkbd.o
+obj-$(CONFIG_KEYBOARD_98KBD)		+= 98kbd.o
diff -Nru linux/drivers/input/mouse/98busmouse.c linux98/drivers/input/mouse/98busmouse.c
--- linux/drivers/input/mouse/98busmouse.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98/drivers/input/mouse/98busmouse.c	2003-02-16 17:19:03.000000000 +0900
@@ -0,0 +1,202 @@
+/*
+ *
+ *  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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Osamu Tomita <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 -Nru linux-2.5.60/drivers/input/mouse/Kconfig linux98-2.5.60/drivers/input/mouse/Kconfig
--- linux-2.5.60/drivers/input/mouse/Kconfig	2003-02-11 03:38:50.000000000 +0900
+++ linux98-2.5.60/drivers/input/mouse/Kconfig	2003-02-16 17:19:03.000000000 +0900
@@ -121,3 +121,15 @@
 	  The module will be called rpcmouse. 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 X86_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 -Nru linux-2.5.52/drivers/input/mouse/Makefile linux98-2.5.52/drivers/input/mouse/Makefile
--- linux-2.5.52/drivers/input/mouse/Makefile	2002-12-16 11:07:49.000000000 +0900
+++ linux98-2.5.52/drivers/input/mouse/Makefile	2002-12-16 21:34:16.000000000 +0900
@@ -10,5 +10,6 @@
 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 -Nru linux-2.5.61/drivers/input/serio/98kbd-io.c linux98-2.5.61/drivers/input/serio/98kbd-io.c
--- linux-2.5.61/drivers/input/serio/98kbd-io.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/drivers/input/serio/98kbd-io.c	2003-02-16 17:19:03.000000000 +0900
@@ -0,0 +1,178 @@
+/*
+ *  NEC PC-9801 keyboard controller driver for Linux
+ *
+ *  Copyright (c) 1999-2002 Osamu Tomita <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 <linux/config.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/sched.h>
+
+#include <asm/io.h>
+
+MODULE_AUTHOR("Osamu Tomita <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;
+
+	spin_lock_irqsave(&kbd98io_lock, flags);
+
+	data = inb(KBD98_DATA_REG);
+	spin_unlock_irqrestore(&kbd98io_lock, flags);
+	serio_interrupt(&kbd98_port, data, 0, regs);
+
+}
+
+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 -Nru linux-2.5.61/drivers/input/serio/Kconfig linux98-2.5.61/drivers/input/serio/Kconfig
--- linux-2.5.61/drivers/input/serio/Kconfig	2003-02-15 08:51:47.000000000 +0900
+++ linux98-2.5.61/drivers/input/serio/Kconfig	2003-02-16 17:19:03.000000000 +0900
@@ -107,3 +107,15 @@
 	tristate "Intel SA1111 keyboard controller"
 	depends on SA1111 && SERIO
 
+config SERIO_98KBD
+	tristate "NEC PC-9800 keyboard controller"
+	depends on X86_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 -Nru linux-2.5.60/drivers/input/serio/Makefile linux98-2.5.60/drivers/input/serio/Makefile
--- linux-2.5.60/drivers/input/serio/Makefile	2003-02-11 03:37:57.000000000 +0900
+++ linux98-2.5.60/drivers/input/serio/Makefile	2003-02-11 09:47:18.000000000 +0900
@@ -13,3 +13,4 @@
 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

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (15/26) kanji
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (13 preceding siblings ...)
  2003-02-17 14:12 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (14/26) input Osamu Tomita
@ 2003-02-17 14:13 ` Osamu Tomita
  2003-02-17 14:15 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC Osamu Tomita
                   ` (10 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:13 UTC (permalink / raw)
  To: Linux Kernel Mailing List
  Cc: Alan Cox, James simmons, 'Christoph Hellwig'

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (15/26).

Add japanese kanji character support to PC98 console.

diff -Nru 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
@@ -64,6 +64,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 -Nru linux/drivers/char/vt.c linux98/drivers/char/vt.c
--- linux/drivers/char/vt.c	2002-12-16 11:08:16.000000000 +0900
+++ linux98/drivers/char/vt.c	2002-12-20 14:52:06.000000000 +0900
@@ -151,6 +151,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_KANJI
+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);
@@ -433,6 +437,25 @@
 		do_update_region(currcons, (unsigned long) p, count);
 }
 
+#ifdef CONFIG_KANJI
+/* 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_KANJI */
+
 /* used by selection: complement pointer position */
 void complement_pos(int currcons, int offset)
 {
@@ -1085,6 +1108,9 @@
 				translate = set_translate(charset == 0
 						? G0_charset
 						: G1_charset,currcons);
+#ifdef CONFIG_KANJI
+				translate_ex = (charset == 0 ? G0_charset_ex : G1_charset_ex);
+#endif
 				disp_ctrl = 0;
 				toggle_meta = 0;
 				break;
@@ -1093,6 +1119,9 @@
 				  * chars < 32 be displayed as ROM chars.
 				  */
 				translate = set_translate(IBMPC_MAP,currcons);
+#ifdef CONFIG_KANJI
+				translate_ex = 0;
+#endif
 				disp_ctrl = 1;
 				toggle_meta = 0;
 				break;
@@ -1101,6 +1130,9 @@
 				  * high bit before displaying as ROM char.
 				  */
 				translate = set_translate(IBMPC_MAP,currcons);
+#ifdef CONFIG_KANJI
+				translate_ex = 0;
+#endif
 				disp_ctrl = 1;
 				toggle_meta = 1;
 				break;
@@ -1310,6 +1342,22 @@
 		case 14: /* set vesa powerdown interval */
 			vesa_off_interval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
 			break;
+#ifdef CONFIG_KANJI
+		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_KANJI */
 	}
 }
 
@@ -1387,8 +1435,26 @@
 	need_wrap = 0;
 }
 
+#ifdef CONFIG_KANJI
+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_KANJI
+	ESsetJIS, ESsetJIS2,
+#endif
 	ESpalette };
 
 /* console_sem is held (except via vc_init()) */
@@ -1398,9 +1464,18 @@
 	bottom		= video_num_lines;
 	vc_state	= ESnormal;
 	ques		= 0;
+#ifndef CONFIG_KANJI
 	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;
@@ -1442,6 +1517,12 @@
 	bell_pitch = DEFAULT_BELL_PITCH;
 	bell_duration = DEFAULT_BELL_DURATION;
 
+#ifdef CONFIG_KANJI
+	kanji_mode = EUC_CODE;
+	kanji_char1 = 0;
+	kanji_jis_mode = JIS_CODE_ASCII;
+#endif
+
 	gotoxy(currcons,0,0);
 	save_cur(currcons);
 	if (do_clear)
@@ -1484,11 +1565,17 @@
 	case 14:
 		charset = 1;
 		translate = set_translate(G1_charset,currcons);
+#ifdef CONFIG_KANJI
+		translate_ex = G1_charset_ex;
+#endif
 		disp_ctrl = 1;
 		return;
 	case 15:
 		charset = 0;
 		translate = set_translate(G0_charset,currcons);
+#ifdef CONFIG_KANJI
+		translate_ex = G0_charset_ex;
+#endif
 		disp_ctrl = 0;
 		return;
 	case 24: case 26:
@@ -1545,6 +1632,11 @@
 		case ')':
 			vc_state = ESsetG1;
 			return;
+#ifdef CONFIG_KANJI
+		case '$':
+			vc_state = ESsetJIS;
+			return;
+#endif
 		case '#':
 			vc_state = EShash;
 			return;
@@ -1794,8 +1886,25 @@
 			G0_charset = IBMPC_MAP;
 		else if (c == 'K')
 			G0_charset = USER_MAP;
-		if (charset == 0)
+#ifdef CONFIG_KANJI
+		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_KANJI */
+		if (charset == 0) {
 			translate = set_translate(G0_charset,currcons);
+#ifdef CONFIG_KANJI
+			translate_ex = G0_charset_ex;
+#endif
+		}
+#ifdef CONFIG_KANJI
+		kanji_jis_mode = JIS_CODE_ASCII;
+		kanji_char1 = 0;
+#endif
 		vc_state = ESnormal;
 		return;
 	case ESsetG1:
@@ -1807,10 +1916,51 @@
 			G1_charset = IBMPC_MAP;
 		else if (c == 'K')
 			G1_charset = USER_MAP;
-		if (charset == 1)
+#ifdef CONFIG_KANJI
+		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_KANJI */
+		if (charset == 1) {
 			translate = set_translate(G1_charset,currcons);
+#ifdef CONFIG_KANJI
+			translate_ex = G1_charset_ex;
+#endif
+		}
+#ifdef CONFIG_KANJI
+		kanji_jis_mode = JIS_CODE_ASCII;
+		kanji_char1 = 0;
+#endif
+		vc_state = ESnormal;
+		return;
+#ifdef CONFIG_KANJI
+	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_KANJI */
 	default:
 		vc_state = ESnormal;
 	}
@@ -1842,7 +1992,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;
@@ -1899,48 +2049,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_KANJI
+		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_KANJI */
+		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_KANJI
+			  tc = translate[toggle_meta ? (c|0x80) : c];
+#else
+			  tc = translate[(toggle_meta || translate_ex) ? (c | 0x80) : c];
+#endif
+			}
+		} /* if (!realkanji) */
+#ifdef CONFIG_KANJI
+	hankana_skip:
+#endif
 
                 /* If the original code was a control character we
                  * only allow a glyph to be displayed if the code is
@@ -1957,43 +2210,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_KANJI
 			scr_writew(himask ?
 				     ((attr << 8) & ~himask) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) :
 				     (attr << 8) + tc,
 				   (u16 *) pos);
+#else /* CONFIG_KANJI */
+			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_KANJI */
 			if (DO_UPDATE && draw_x < 0) {
 				draw_x = x;
 				draw_from = pos;
+				if (realkanji) {
+					draw_x --;
+					draw_from -= 2;
+				}
 			}
+#ifndef CONFIG_KANJI
 			if (x == video_num_columns - 1) {
 				need_wrap = decawm;
 				draw_to = pos+2;
@@ -2001,6 +2282,16 @@
 				x++;
 				draw_to = (pos+=2);
 			}
+#else /* CONFIG_KANJI */
+			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_KANJI */
 			continue;
 		}
 		FLUSH
diff -Nru linux-2.5.61/drivers/video/console/Kconfig linux98-2.5.61/drivers/video/console/Kconfig
--- linux-2.5.61/drivers/video/console/Kconfig	2003-02-15 08:51:21.000000000 +0900
+++ linux98-2.5.61/drivers/video/console/Kconfig	2003-02-16 14:48:08.000000000 +0900
@@ -221,5 +221,9 @@
 	bool "Mini 4x6 font"
 	depends on !SPARC32 && !SPARC64 && FONTS
 
+config KANJI
+	bool "Japanese Kanji support"
+	depends on X86_PC9800
+
 endmenu
 
diff -Nru linux/include/linux/console_struct.h linux98/include/linux/console_struct.h
--- linux/include/linux/console_struct.h	2002-12-10 11:45:40.000000000 +0900
+++ linux98/include/linux/console_struct.h	2002-12-16 13:25:55.000000000 +0900
@@ -83,6 +83,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_KANJI
+	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_KANJI */
 	/* additional information is in vt_kern.h */
 };
 
diff -Nru 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;
 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (14 preceding siblings ...)
  2003-02-17 14:13 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (15/26) kanji Osamu Tomita
@ 2003-02-17 14:15 ` Osamu Tomita
  2003-02-17 17:02   ` Jeff Garzik
  2003-02-17 14:16 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (17/26) parport Osamu Tomita
                   ` (9 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:15 UTC (permalink / raw)
  To: Linux Kernel Mailing List
  Cc: Jeff Garzik, Alan Cox, 'Christoph Hellwig'

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (16/26).

C-bus(PC98's legacy bus like ISA) network cards support.

diff -Nru linux-2.5.61/drivers/net/3c509.c linux98-2.5.61/drivers/net/3c509.c
--- linux-2.5.61/drivers/net/3c509.c	2003-02-15 08:51:09.000000000 +0900
+++ linux98-2.5.61/drivers/net/3c509.c	2003-02-15 14:38:08.000000000 +0900
@@ -56,6 +56,10 @@
 		v1.19b 08Nov2002 Marc Zyngier <maz@wild-wind.fr.eu.org>
 		    - Introduce driver model for EISA cards.
 */
+/*
+  FIXES for PC-9800:
+  Shu Iwanaga: 3c569B(PC-9801 C-bus) support
+*/
 
 #define DRV_NAME	"3c509"
 #define DRV_VERSION	"1.19b"
@@ -257,7 +261,7 @@
 };
 #endif /* CONFIG_MCA */
 
-#ifdef __ISAPNP__
+#if defined(__ISAPNP__) && !defined(CONFIG_X86_PC9800)
 static struct isapnp_device_id el3_isapnp_adapters[] __initdata = {
 	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
 		ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5090),
@@ -350,7 +354,7 @@
 		if (lp->pmdev)
 			pm_unregister(lp->pmdev);
 #endif
-#ifdef __ISAPNP__
+#if defined(__ISAPNP__) && !defined(CONFIG_X86_PC9800)
 		if (lp->type == EL3_PNP)
 			pnp_device_detach(to_pnp_dev(lp->dev));
 #endif
@@ -368,12 +372,12 @@
 	int ioaddr, irq, if_port;
 	u16 phys_addr[3];
 	static int current_tag;
-#ifdef __ISAPNP__
+#if defined(__ISAPNP__) && !defined(CONFIG_X86_PC9800)
 	static int pnp_cards;
 	struct pnp_dev *idev = NULL;
 #endif /* __ISAPNP__ */
 
-#ifdef __ISAPNP__
+#if defined(__ISAPNP__) && !defined(CONFIG_X86_PC9800)
 	if (nopnp == 1)
 		goto no_pnp;
 
@@ -421,6 +425,9 @@
 no_pnp:
 #endif /* __ISAPNP__ */
 
+#ifdef CONFIG_X86_PC9800
+	id_port = 0x71d0;
+#else
 	/* 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))
@@ -435,6 +442,7 @@
 		printk(" WARNING: No I/O port available for 3c509 activation.\n");
 		return -ENODEV;
 	}
+#endif /* CONFIG_X86_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
@@ -465,7 +473,7 @@
 		phys_addr[i] = htons(id_read_eeprom(i));
 	}
 
-#ifdef __ISAPNP__
+#if defined(__ISAPNP__) && !defined(CONFIG_X86_PC9800)
 	if (nopnp == 0) {
 		/* The ISA PnP 3c509 cards respond to the ID sequence.
 		   This check is needed in order not to register them twice. */
@@ -490,9 +498,19 @@
 	{
 		unsigned int iobase = id_read_eeprom(8);
 		if_port = iobase >> 14;
+#ifdef CONFIG_X86_PC9800
+		ioaddr = 0x40d0 + ((iobase & 0x1f) << 8);
+#else
 		ioaddr = 0x200 + ((iobase & 0x1f) << 4);
+#endif
 	}
 	irq = id_read_eeprom(9) >> 12;
+#ifdef CONFIG_X86_PC9800
+	if (irq == 7)
+		irq = 6;
+	else if (irq == 15)
+		irq = 13;
+#endif
 
 	if (!(dev = init_etherdev(NULL, sizeof(struct el3_private))))
 			return -ENOMEM;
@@ -522,7 +540,11 @@
 	outb(0xd0 + ++current_tag, id_port);
 
 	/* Activate the adaptor at the EEPROM location. */
+#ifdef CONFIG_X86_PC9800
+	outb((ioaddr >> 8) | 0xe0, id_port);
+#else
 	outb((ioaddr >> 4) | 0xe0, id_port);
+#endif
 
 	EL3WINDOW(0);
 	if (inw(ioaddr) != 0x6d50) {
@@ -534,7 +556,7 @@
 	/* Free the interrupt so that some other card can use it. */
 	outw(0x0f00, ioaddr + WN0_IRQ);
 
-#ifdef __ISAPNP__	
+#if defined(__ISAPNP__) && !defined(CONFIG_X86_PC9800)
  found:							/* PNP jumps here... */
 #endif /* __ISAPNP__ */
 
@@ -543,7 +565,7 @@
 	dev->irq = irq;
 	dev->if_port = if_port;
 	lp = dev->priv;
-#ifdef __ISAPNP__
+#if defined(__ISAPNP__) && !defined(CONFIG_X86_PC9800)
 	lp->dev = &idev->dev;
 #endif
 
@@ -1388,6 +1410,12 @@
 	outw(0x0001, ioaddr + 4);
 
 	/* Set the IRQ line. */
+#ifdef CONFIG_X86_PC9800
+	if (dev->irq == 6)
+		dev->irq = 7;
+	else if (dev->irq == 13)
+		dev->irq = 15;
+#endif
 	outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
 
 	/* Set the station address in window 2 each time opened. */
@@ -1550,7 +1578,7 @@
 MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
 MODULE_PARM_DESC(xcvr,"tranceiver(s) (0=internal, 1=external)");
 MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt");
-#ifdef __ISAPNP__
+#if defined(__ISAPNP__) && !defined(CONFIG_X86_PC9800)
 MODULE_PARM(nopnp, "i");
 MODULE_PARM_DESC(nopnp, "disable ISA PnP support (0-1)");
 MODULE_DEVICE_TABLE(isapnp, el3_isapnp_adapters);
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	2002-10-12 13:22:14.000000000 +0900
+++ linux98-2.5.42/drivers/net/8390.h	2002-10-15 23:03:22.000000000 +0900
@@ -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.61/drivers/net/Kconfig linux98-2.5.61/drivers/net/Kconfig
--- linux-2.5.61/drivers/net/Kconfig	2003-02-15 08:52:31.000000000 +0900
+++ linux98-2.5.61/drivers/net/Kconfig	2003-02-15 14:24:25.000000000 +0900
@@ -662,7 +662,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
@@ -931,7 +931,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
@@ -977,7 +977,7 @@
 
 config NET_ISA
 	bool "Other ISA cards"
-	depends on NET_ETHERNET && ISA
+	depends on NET_ETHERNET && ISA && !X86_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
@@ -1175,6 +1175,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 && X86_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 NE2K_CBUS_IOLA98
+	bool "I-O DATA LA-98 support"
+	depends on NE2K_CBUS
+
+config NE2K_CBUS_CNET98EL
+	bool "Contec C-NET(98)E/L support"
+	depends on NE2K_CBUS
+
+config NE2K_CBUS_CNET98EL_IO_BASE
+	hex "C-NET(98)E/L I/O base address (0xaaed or 0x55ed)"
+	depends on NE2K_CBUS_CNET98EL
+	default "0xaaed"
+
+config NE2K_CBUS_ATLA98
+	bool "Allied Telesis LA-98 Support"
+	depends on NE2K_CBUS
+
+config NE2K_CBUS_BDN
+	bool "ELECOM Laneed LD-BDN[123]A Support"
+	depends on NE2K_CBUS
+
+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.61/drivers/net/Makefile linux98-2.5.61/drivers/net/Makefile
--- linux-2.5.61/drivers/net/Makefile	2003-02-15 08:51:25.000000000 +0900
+++ linux98-2.5.61/drivers/net/Makefile	2003-02-15 14:24:25.000000000 +0900
@@ -86,6 +86,7 @@
 obj-$(CONFIG_WD80x3) += wd.o 8390.o
 obj-$(CONFIG_EL2) += 3c503.o 8390.o
 obj-$(CONFIG_NE2000) += ne.o 8390.o
+obj-$(CONFIG_NE2K_CBUS) += ne.o 8390.o
 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	2002-10-12 13:22:18.000000000 +0900
+++ linux98-2.5.42/drivers/net/Makefile.lib	2002-10-15 23:03:22.000000000 +0900
@@ -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.61/drivers/net/Space.c linux98-2.5.61/drivers/net/Space.c
--- linux-2.5.61/drivers/net/Space.c	2003-02-15 08:51:12.000000000 +0900
+++ linux98-2.5.61/drivers/net/Space.c	2003-02-15 14:24:25.000000000 +0900
@@ -233,7 +233,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.57/drivers/net/at1700.c linux98-2.5.57/drivers/net/at1700.c
--- linux-2.5.57/drivers/net/at1700.c	2003-01-14 09:31:51.000000000 +0900
+++ linux98-2.5.57/drivers/net/at1700.c	2003-01-14 09:55:53.000000000 +0900
@@ -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/errno.h>
@@ -76,10 +80,17 @@
  *	ISA
  */
 
+#ifndef CONFIG_X86_PC9800
 static int at1700_probe_list[] __initdata = {
 	0x260, 0x280, 0x2a0, 0x240, 0x340, 0x320, 0x380, 0x300, 0
 };
 
+#else /* CONFIG_X86_PC9800 */
+static int at1700_probe_list[] __initdata = {
+	0x1d6, 0x1d8, 0x1da, 0x1d4, 0xd4, 0xd2, 0xd8, 0xd0, 0
+};
+
+#endif /* CONFIG_X86_PC9800 */
 /*
  *	MCA
  */
@@ -122,6 +133,7 @@
 
 
 /* Offsets from the base address. */
+#ifndef CONFIG_X86_PC9800
 #define STATUS			0
 #define TX_STATUS		0
 #define RX_STATUS		1
@@ -136,6 +148,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
@@ -144,8 +157,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_X86_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_X86_PC9800 */
+
 
 #define TX_TIMEOUT		10
 
@@ -225,8 +269,20 @@
 	int slot, ret = -ENODEV;
 	struct net_local *lp;
 	
+#ifndef CONFIG_X86_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.
@@ -317,10 +373,17 @@
 		/* Reset the internal state machines. */
 	outb(0, ioaddr + RESET);
 
-	if (is_at1700)
+	if (is_at1700) {
+#ifndef CONFIG_X86_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) {
@@ -392,18 +455,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_X86_PC9800
 	outb(dev->if_port, ioaddr + MODE13);
+#else
+	outb(0, ioaddr + MODE13);
+#endif
 	outb(0x00, ioaddr + COL16CNTL);
 
 	if (net_debug)
@@ -447,7 +514,12 @@
 	kfree(dev->priv);
 	dev->priv = NULL;
 err_out:
+#ifndef CONFIG_X86_PC9800
 	release_region(ioaddr, AT1700_IO_EXTENT);
+#else
+	for (i = 0; i < 0x2000; i += 0x0200)
+		release_region(ioaddr + i, 2);
+#endif
 	return ret;
 }
 
@@ -459,7 +531,11 @@
 #define EE_DATA_READ	0x80	/* EEPROM chip data out, in reg. 17. */
 
 /* Delay between EEPROM clock transitions. */
+#ifndef CONFIG_X86_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)
@@ -542,12 +618,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);
@@ -704,7 +780,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
@@ -724,7 +800,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;
 			}
@@ -734,7 +810,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;
 			}
@@ -761,7 +837,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)
@@ -851,7 +927,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);
 	}
@@ -861,7 +937,12 @@
 
 #ifdef MODULE
 static struct net_device dev_at1700;
+#ifndef CONFIG_X86_PC9800
 static int io = 0x260;
+#else
+static int io = 0xd0;
+#endif
+
 static int irq;
 
 MODULE_PARM(io, "i");
@@ -901,7 +982,15 @@
 
 	/* If we don't do this, we can't re-insmod it later. */
 	free_irq(dev_at1700.irq, NULL);
+#ifndef CONFIG_X86_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.61/drivers/net/ne.c linux98-2.5.61/drivers/net/ne.c
--- linux-2.5.61/drivers/net/ne.c	2003-02-15 08:51:42.000000000 +0900
+++ linux98-2.5.61/drivers/net/ne.c	2003-02-16 11:49:42.000000000 +0900
@@ -109,6 +109,10 @@
     {"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) */
+    {"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 */
     {0,}
 };
 #endif
@@ -116,9 +120,10 @@
 /* ---- No user-serviceable parts below ---- */
 
 #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	 	EI_SHIFT(0x00)
+#define NE_DATAPORT	EI_SHIFT(0x10)	/* NatSemi-defined port window offset. */
+#define NE_RESET	EI_SHIFT(0x1f) /* Issue a read to reset, a write to clear. */
+
 #define NE_IO_EXTENT	0x20
 
 #define NE1SM_START_PG	0x20	/* First page of TX buffer */
@@ -126,9 +131,15 @@
 #define NESM_START_PG	0x40	/* First page of TX buffer */
 #define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
 
+#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);
+#ifndef CONFIG_NET_CBUS
 static int ne_probe_isapnp(struct net_device *dev);
+#endif
 
 static int ne_open(struct net_device *dev);
 static int ne_close(struct net_device *dev);
@@ -163,6 +174,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;
@@ -236,6 +249,116 @@
 	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;
+}
+
+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;
@@ -243,30 +366,62 @@
 	int wordlength = 2;
 	const char *name = NULL;
 	int start_page, stop_page;
-	int neX000, ctron, copam, bad_card;
+	int neX000, bad_card;
+#ifndef CONFIG_NET_CBUS
+	int ctron, copam;
+#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_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 + EI_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 + EI_SHIFT(0x0d));
+		outb_p(0xff, ioaddr + EI_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 + EI_SHIFT(0x0d));	/* Restore the old values. */
 			ret = -ENODEV;
 			goto err_out;
 		}
@@ -290,6 +445,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);
 
@@ -308,15 +468,86 @@
 		outb_p(0xff, ioaddr + EN0_ISR);		/* Ack all intr. */
 	}
 
+#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. */
@@ -332,29 +563,42 @@
 
 		for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
 			outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
-
-	}
+#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 (wordlength == 2)
 	{
 		for (i = 0; i < 16; i++)
 			SA_prom[i] = SA_prom[i+i];
+#ifndef CONFIG_NET_CBUS
 		/* We must set the 8390 for word mode. */
 		outb_p(0x49, ioaddr + EN0_DCFG);
+#endif
 		start_page = NESM_START_PG;
 		stop_page = NESM_STOP_PG;
+#ifdef CONFIG_NE2K_CBUS_CNET98EL
+		if (hw->hwtype == NE2K_CBUS_HARDWARE_TYPE_CNET98EL) {
+			start_page = CNET98EL_START_PG;
+			stop_page = CNET98EL_STOP_PG;
+		}
+#endif
 	} else {
 		start_page = NE1SM_START_PG;
 		stop_page = NE1SM_STOP_PG;
 	}
 
 	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);
 
@@ -368,6 +612,11 @@
 		start_page = 0x01;
 		stop_page = (wordlength == 2) ? 0x40 : 0x20;
 	}
+#else
+	if (neX000) {
+		name = "C-Bus-NE2K-compat";
+	}
+#endif
 	else
 	{
 #ifdef SUPPORT_NE_BAD_CLONES
@@ -414,10 +663,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_X86_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");
@@ -448,8 +705,13 @@
 		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;
@@ -473,10 +735,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;
 }
 
@@ -500,10 +775,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);
 
@@ -526,6 +809,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 */
 
@@ -568,6 +854,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)
@@ -578,6 +867,12 @@
 		return;
 	}
 	ei_status.dmaing |= 0x01;
+
+#ifdef CONFIG_NET_CBUS
+	/* round up count to a word (derived from ICM-patch) */
+	count = (count + 1) & ~1;
+#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);
@@ -635,6 +930,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?
@@ -738,13 +1036,22 @@
 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 */
+#ifdef CONFIG_NET_CBUS
+static int 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_NET_CBUS
+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_NET_CBUS
+MODULE_PARM_DESC(hwtype, "Board type of PC-9800 C-Bus NIC");
+#endif
 MODULE_DESCRIPTION("NE1000/NE2000 ISA/PnP Ethernet driver");
 MODULE_LICENSE("GPL");
 
@@ -762,6 +1069,9 @@
 		dev->irq = irq[this_dev];
 		dev->mem_end = bad[this_dev];
 		dev->base_addr = io[this_dev];
+#ifdef CONFIG_NET_CBUS
+		dev->mem_start = hwtype[this_dev];
+#endif
 		dev->init = ne_probe;
 		if (register_netdev(dev) == 0) {
 			found++;
@@ -791,9 +1101,23 @@
 			if (idev)
 				pnp_device_detach(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.50/drivers/net/ne2k_cbus.h linux98-2.5.50/drivers/net/ne2k_cbus.h
--- linux-2.5.42/drivers/net/ne2k_cbus.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.42/drivers/net/ne2k_cbus.h	2002-12-15 10:56:15.000000000 +0900
@@ -0,0 +1,481 @@
+/* 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>
+
+#undef NE_RESET
+#define NE_RESET EI_SHIFT(0x11) /* Issue a read to reset, a write to clear. */
+
+#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
+
+/* 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:
+   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;
+};
+
+#ifdef CONFIG_NE2K_CBUS_ATLA98
+#ifndef MODULE
+static 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 unsigned short bdn_portlist[] __initdata = {
+	0xd0,
+	0
+};
+#endif
+static 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 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 unsigned short egy98_portlist[] __initdata = {
+	0xd0,
+	0
+};
+#endif
+static struct ne2k_cbus_offsetinfo egy98_offsetinfo __initdata = {
+	0x02, 0x100, 0x200, 0x300
+};
+static 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 unsigned short lgy98_portlist[] __initdata = {
+	0xd0, 0x10d0, 0x20d0, 0x30d0, 0x40d0, 0x50d0, 0x60d0, 0x70d0,
+	0
+};
+#endif
+static struct ne2k_cbus_offsetinfo lgy98_offsetinfo __initdata = {
+	0x01, 0x08, 0x200, 0x300
+};
+static 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 unsigned short icm_portlist[] __initdata = {
+	/* ICM */
+	0x56d0,
+	/* LD-98PT */
+	0x46d0, 0x66d0, 0x76d0, 0x86d0, 0x96d0, 0xa6d0, 0xb6d0, 0xc6d0,
+	0
+};
+#endif
+static struct ne2k_cbus_offsetinfo icm_offsetinfo __initdata = {
+	0x01, 0x08, 0x100, 0x10f
+};
+static 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 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 struct ne2k_cbus_offsetinfo ne2k_offsetinfo __initdata = {
+	0x01, 0x08, 0x10, 0x1f
+};
+static struct ne2k_cbus_region ne2k_regionlist[] __initdata = {
+	{0x0, 32},
+	{0x0, 0}
+};
+#endif
+
+#ifdef CONFIG_NE2K_CBUS_NEC108
+#ifndef MODULE
+static unsigned short nec108_portlist[] __initdata = {
+	0x770, 0x2770, 0x4770, 0x6770,
+	0
+};
+#endif
+static struct ne2k_cbus_offsetinfo nec108_offsetinfo __initdata = {
+	0x02, 0x1000, 0x888, 0x88a
+};
+static 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},
+	{0x888, 1}, {0x88a, 1}, {0x88c, 1}, {0x88e, 1},
+	{0x0, 0}
+};
+#endif
+
+#ifdef CONFIG_NE2K_CBUS_IOLA98
+#ifndef MODULE
+static unsigned short iola98_portlist[] __initdata = {
+	0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
+	0
+};
+#endif
+static struct ne2k_cbus_offsetinfo iola98_offsetinfo __initdata = {
+	0x1000, 0x8000, 0x100, 0xf100
+};
+static 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 unsigned short cnet98el_portlist[] __initdata = {
+	0x3d0, 0x13d0, 0x23d0, 0x33d0, 0x43d0, 0x53d0, 0x60d0, 0x70d0,
+	0
+};
+#endif
+static struct ne2k_cbus_offsetinfo cnet98el_offsetinfo __initdata = {
+	0x01, 0x08, 0x40e, 0x400
+};
+static struct ne2k_cbus_region cnet98el_regionlist[] __initdata = {
+	{0x0, 16}, {0x400, 16},
+	{0x0, 0}
+};
+#endif
+
+
+/* port information table (for ne.c initialize/probe process) */
+
+static 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
+	{
+		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
+
+static int ne_probe_cbus(struct net_device *dev, const struct ne2k_cbus_hwinfo *hw, int ioaddr);
+/* End of ne2k_cbus.h */

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (17/26) parport
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (15 preceding siblings ...)
  2003-02-17 14:15 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC Osamu Tomita
@ 2003-02-17 14:16 ` Osamu Tomita
  2003-02-17 14:17 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (18/26) PCI Osamu Tomita
                   ` (8 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:16 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (17/26).

Parallel port support.

diff -Nru linux/drivers/parport/parport_pc.c linux98/drivers/parport/parport_pc.c
--- linux/drivers/parport/parport_pc.c	2002-12-16 11:08:22.000000000 +0900
+++ linux98/drivers/parport/parport_pc.c	2002-12-22 20:51:23.000000000 +0900
@@ -332,7 +332,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 +1647,8 @@
 {
 	unsigned char r, w;
 
+	if (pc98 && pb->base == 0x40)
+		return PARPORT_MODE_PCSPP;
 	/*
 	 * first clear an eventually pending EPP timeout 
 	 * I (sailer@ife.ee.ethz.ch) have an SMSC chipset
@@ -1777,6 +1782,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 +1916,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 +2059,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 +2226,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 +2266,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 +2364,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 +2439,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)
@@ -2996,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 -Nru linux/include/linux/parport_pc.h linux98/include/linux/parport_pc.h
--- linux/include/linux/parport_pc.h	2002-06-12 11:15:27.000000000 +0900
+++ linux98/include/linux/parport_pc.h	2002-08-19 14:13:09.000000000 +0900
@@ -119,6 +119,11 @@
 #endif
 	ctr = (ctr & ~mask) ^ val;
 	ctr &= priv->ctr_writable; /* only write writable bits. */
+#ifdef CONFIG_X86_PC9800
+	if (p->base == 0x40 && ((priv->ctr) ^ ctr) & 0x01)
+		outb(0x0e | ((ctr & 0x01) ^ 0x01), 0x46);
+	else
+#endif /* CONFIG_X86_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_X86_PC9800
+	if (p->base == 0x40)
+		return ((inb(0x42) & 0x04) << 5) | PARPORT_STATUS_ERROR;
+	else
+#endif /* CONFIG_X86_PC9800 */
 	return inb(STATUS(p));
 }
 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (18/26) PCI
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (16 preceding siblings ...)
  2003-02-17 14:16 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (17/26) parport Osamu Tomita
@ 2003-02-17 14:17 ` Osamu Tomita
  2003-02-17 14:18 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (19/26) PCI BIOS Osamu Tomita
                   ` (7 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:17 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (18/26).

Small changes for PCI support.
Fix for difference of IRQ numbers and IO addresses.

diff -Nru linux/arch/i386/pci/irq.c linux98/arch/i386/pci/irq.c
--- linux/arch/i386/pci/irq.c	2002-10-12 13:22:46.000000000 +0900
+++ linux98/arch/i386/pci/irq.c	2002-10-12 14:18:52.000000000 +0900
@@ -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_X86_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_X86_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 -Nru linux-2.5.61/drivers/pci/quirks.c linux98-2.5.61/drivers/pci/quirks.c
--- linux-2.5.61/drivers/pci/quirks.c	2003-02-15 08:51:07.000000000 +0900
+++ linux98-2.5.61/drivers/pci/quirks.c	2003-02-16 17:19:03.000000000 +0900
@@ -551,6 +551,8 @@
 	{ 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 },
 	{ PCI_FIXUP_FINAL,	PCI_VENDOR_ID_NEC,	PCI_DEVICE_ID_NEC_CBUS_1,	quirk_isa_dma_hangs },
+	{ PCI_FIXUP_FINAL,	PCI_VENDOR_ID_NEC,	PCI_DEVICE_ID_NEC_CBUS_2,	quirk_isa_dma_hangs },
+	{ PCI_FIXUP_FINAL,	PCI_VENDOR_ID_NEC,	PCI_DEVICE_ID_NEC_CBUS_3,	quirk_isa_dma_hangs },
 	{ 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 -Nru linux/drivers/pcmcia/yenta.c linux98/drivers/pcmcia/yenta.c
--- linux/drivers/pcmcia/yenta.c	2002-11-18 13:29:48.000000000 +0900
+++ linux98/drivers/pcmcia/yenta.c	2002-11-19 11:02:09.000000000 +0900
@@ -8,6 +8,7 @@
  * 	Dynamically adjust the size of the bridge resource
  * 	
  */
+#include <linux/config.h>
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/sched.h>
@@ -510,6 +511,7 @@
 	add_timer(&socket->poll_timer);
 }
 
+#ifndef CONFIG_X86_PC9800
 /*
  * Only probe "regular" interrupts, don't
  * touch dangerous spots like the mouse irq,
@@ -520,6 +522,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 -Nru linux/include/asm-i386/pci.h linux98/include/asm-i386/pci.h
--- linux/include/asm-i386/pci.h	2002-06-09 14:29:24.000000000 +0900
+++ linux98/include/asm-i386/pci.h	2002-06-10 20:49:15.000000000 +0900
@@ -17,7 +17,11 @@
 #endif
 
 extern unsigned long pci_mem_start;
+#ifndef CONFIG_X86_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);

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (19/26) PCI BIOS
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (17 preceding siblings ...)
  2003-02-17 14:17 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (18/26) PCI Osamu Tomita
@ 2003-02-17 14:18 ` Osamu Tomita
  2003-02-17 14:18 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (20/26) PCMCIA Osamu Tomita
                   ` (6 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:18 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (19/26).

PCI BIOS function support using mach-* scheme.

diff -Nru linux-2.5.53/arch/i386/pci/pcbios.c linux98-2.5.53/arch/i386/pci/pcbios.c
--- linux-2.5.53/arch/i386/pci/pcbios.c	2002-11-25 15:09:13.000000000 +0000
+++ linux98-2.5.53/arch/i386/pci/pcbios.c	2002-10-31 15:05:49.000000000 +0000
@@ -5,22 +5,9 @@
 #include <linux/pci.h>
 #include <linux/init.h>
 #include "pci.h"
+#include "pci-functions.h"
 
 
-#define PCIBIOS_PCI_FUNCTION_ID 	0xb1XX
-#define PCIBIOS_PCI_BIOS_PRESENT 	0xb101
-#define PCIBIOS_FIND_PCI_DEVICE		0xb102
-#define PCIBIOS_FIND_PCI_CLASS_CODE	0xb103
-#define PCIBIOS_GENERATE_SPECIAL_CYCLE	0xb106
-#define PCIBIOS_READ_CONFIG_BYTE	0xb108
-#define PCIBIOS_READ_CONFIG_WORD	0xb109
-#define PCIBIOS_READ_CONFIG_DWORD	0xb10a
-#define PCIBIOS_WRITE_CONFIG_BYTE	0xb10b
-#define PCIBIOS_WRITE_CONFIG_WORD	0xb10c
-#define PCIBIOS_WRITE_CONFIG_DWORD	0xb10d
-#define PCIBIOS_GET_ROUTING_OPTIONS	0xb10e
-#define PCIBIOS_SET_PCI_HW_INT		0xb10f
-
 /* BIOS32 signature: "_32_" */
 #define BIOS32_SIGNATURE	(('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24))
 
diff -Nru linux-2.5.53/include/asm-i386/mach-default/pci-functions.h linux98-2.5.53/include/asm-i386/mach-default/pci-functions.h
--- linux-2.5.53/include/asm-i386/mach-default/pci-functions.h	1970-01-01 01:00:00.000000000 +0100
+++ linux98-2.5.53/include/asm-i386/mach-default/pci-functions.h	2002-10-31 15:05:52.000000000 +0000
@@ -0,0 +1,19 @@
+/*
+ *	PCI BIOS function numbering for conventional PCI BIOS 
+ *	systems
+ */
+
+#define PCIBIOS_PCI_FUNCTION_ID 	0xb1XX
+#define PCIBIOS_PCI_BIOS_PRESENT 	0xb101
+#define PCIBIOS_FIND_PCI_DEVICE		0xb102
+#define PCIBIOS_FIND_PCI_CLASS_CODE	0xb103
+#define PCIBIOS_GENERATE_SPECIAL_CYCLE	0xb106
+#define PCIBIOS_READ_CONFIG_BYTE	0xb108
+#define PCIBIOS_READ_CONFIG_WORD	0xb109
+#define PCIBIOS_READ_CONFIG_DWORD	0xb10a
+#define PCIBIOS_WRITE_CONFIG_BYTE	0xb10b
+#define PCIBIOS_WRITE_CONFIG_WORD	0xb10c
+#define PCIBIOS_WRITE_CONFIG_DWORD	0xb10d
+#define PCIBIOS_GET_ROUTING_OPTIONS	0xb10e
+#define PCIBIOS_SET_PCI_HW_INT		0xb10f
+
diff -Nru linux-2.5.53/include/asm-i386/mach-pc9800/pci-functions.h linux98-2.5.53/include/asm-i386/mach-pc9800/pci-functions.h
--- linux-2.5.53/include/asm-i386/mach-pc9800/pci-functions.h	1970-01-01 01:00:00.000000000 +0100
+++ linux98-2.5.53/include/asm-i386/mach-pc9800/pci-functions.h	2002-10-31 15:05:52.000000000 +0000
@@ -0,0 +1,20 @@
+/*
+ *	PCI BIOS function codes for the PC9800. Different from
+ *	standard PC systems
+ */
+
+/* Note: PC-9800 confirms PCI 2.1 on only few models */
+
+#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 */

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (20/26) PCMCIA
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (18 preceding siblings ...)
  2003-02-17 14:18 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (19/26) PCI BIOS Osamu Tomita
@ 2003-02-17 14:18 ` Osamu Tomita
  2003-02-17 14:19 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (21/26) PNP Osamu Tomita
                   ` (5 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:18 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (20/26).

Small change for PCMCIA (16bits) support.
For fix usable IRQ number.

diff -Nru linux-2.5.50-ac1/drivers/pcmcia/i82365.c linux98-2.5.50-ac1/drivers/pcmcia/i82365.c
--- linux-2.5.50-ac1/drivers/pcmcia/i82365.c	2002-11-28 07:36:18.000000000 +0900
+++ linux98-2.5.50-ac1/drivers/pcmcia/i82365.c	2002-12-12 16:40:13.000000000 +0900
@@ -187,7 +187,11 @@
 };
 
 /* Default ISA interrupt mask */
+#ifndef CONFIG_X86_PC9800
 #define I365_MASK	0xdeb8	/* irq 15,14,12,11,10,9,7,5,4,3 */
+#else
+#define I365_MASK	0xd668	/* irq 15,14,12,10,9,6,5,3 */
+#endif
 
 #ifdef CONFIG_ISA
 static int grab_irq;

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (21/26) PNP
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (19 preceding siblings ...)
  2003-02-17 14:18 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (20/26) PCMCIA Osamu Tomita
@ 2003-02-17 14:19 ` Osamu Tomita
  2003-02-17 14:20 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (22/26) reboot Osamu Tomita
                   ` (4 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:19 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (21/26).

Small change for Legacy bus PNP support.
For fix IO port address.

diff -Nru linux/drivers/pnp/isapnp/core.c linux98/drivers/pnp/isapnp/core.c
--- linux/drivers/pnp/isapnp/core.c	2003-01-02 12:22:18.000000000 +0900
+++ linux98/drivers/pnp/isapnp/core.c	2003-01-04 16:40:40.000000000 +0900
@@ -72,8 +72,13 @@
 MODULE_PARM_DESC(isapnp_verbose, "ISA Plug & Play verbose mode");
 MODULE_LICENSE("GPL");
 
+#ifndef CONFIG_X86_PC9800
 #define _PIDXR		0x279
 #define _PNPWRP		0xa79
+#else
+#define _PIDXR		0x259
+#define _PNPWRP		0xa59
+#endif
 
 /* short tags */
 #define _STAG_PNPVERNO		0x01

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (22/26) reboot
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (20 preceding siblings ...)
  2003-02-17 14:19 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (21/26) PNP Osamu Tomita
@ 2003-02-17 14:20 ` Osamu Tomita
  2003-02-17 14:21 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (23/26) SCSI Osamu Tomita
                   ` (3 subsequent siblings)
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:20 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (22/26).

Support difference of machine reboot method, using mach-* scheme.

diff -Nru linux-2.5.53/arch/i386/kernel/reboot.c linux98-2.5.53/arch/i386/kernel/reboot.c
--- linux-2.5.53/arch/i386/kernel/reboot.c	2002-12-24 14:19:53.000000000 +0900
+++ linux98-2.5.53/arch/i386/kernel/reboot.c	2002-12-26 10:46:08.000000000 +0900
@@ -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 -Nru linux-2.5.53/include/asm-i386/mach-default/mach_reboot.h linux98-2.5.53/include/asm-i386/mach-default/mach_reboot.h
--- linux-2.5.53/include/asm-i386/mach-default/mach_reboot.h	1970-01-01 01:00:00.000000000 +0100
+++ linux98-2.5.53/include/asm-i386/mach-default/mach_reboot.h	2002-10-31 15:05:52.000000000 +0000
@@ -0,0 +1,30 @@
+/*
+ *  include/asm-i386/mach-default/mach_reboot.h
+ *
+ *  Machine specific reboot functions for generic.
+ *  Split out from reboot.c by Osamu Tomita <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 -Nru linux-2.5.53/include/asm-i386/mach-pc9800/mach_reboot.h linux98-2.5.53/include/asm-i386/mach-pc9800/mach_reboot.h
--- linux-2.5.53/include/asm-i386/mach-pc9800/mach_reboot.h	1970-01-01 01:00:00.000000000 +0100
+++ linux98-2.5.53/include/asm-i386/mach-pc9800/mach_reboot.h	2002-10-31 15:05:52.000000000 +0000
@@ -0,0 +1,21 @@
+/*
+ *  include/asm-i386/mach-pc9800/mach_reboot.h
+ *
+ *  Machine specific reboot functions for PC-9800.
+ *  Written by Osamu Tomita <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 */

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (23/26) SCSI
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (21 preceding siblings ...)
  2003-02-17 14:20 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (22/26) reboot Osamu Tomita
@ 2003-02-17 14:21 ` Osamu Tomita
  2003-02-18 10:54   ` 'Christoph Hellwig'
  2003-02-17 14:24 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (24/26) serial Osamu Tomita
                   ` (2 subsequent siblings)
  25 siblings, 1 reply; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:21 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox, 'Christoph Hellwig'

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (23/26).

SCSI host adapter support.
 - BIOS parameter change for PC98.
 - Add pc980155 driver for old PC98.
 - wd33c93.c compile fix.

diff -Nru linux-2.5.60/drivers/scsi/Kconfig linux98-2.5.60/drivers/scsi/Kconfig
--- linux-2.5.60/drivers/scsi/Kconfig	2003-02-11 03:38:53.000000000 +0900
+++ linux98-2.5.60/drivers/scsi/Kconfig	2003-02-11 13:27:06.000000000 +0900
@@ -1729,6 +1729,13 @@
 	  see the picture at
 	  <http://amiga.multigraph.com/photos/oktagon.html>.
 
+config SCSI_PC980155
+	tristate "NEC PC-9801-55 SCSI support"
+	depends on X86_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 -Nru linux-2.5.60/drivers/scsi/Makefile linux98-2.5.60/drivers/scsi/Makefile
--- linux-2.5.60/drivers/scsi/Makefile	2003-02-11 03:38:49.000000000 +0900
+++ linux98-2.5.60/drivers/scsi/Makefile	2003-02-13 23:40:04.000000000 +0900
@@ -29,6 +29,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 -Nru linux/drivers/scsi/pc980155.c linux98/drivers/scsi/pc980155.c
--- linux/drivers/scsi/pc980155.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98/drivers/scsi/pc980155.c	2003-02-17 21:05:25.000000000 +0900
@@ -0,0 +1,264 @@
+#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 <linux/interrupt.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->device->host->dma_channel);
+  set_dma_mode(sc->device->host->dma_channel, 0x40 | (dir_in ? 0x04 : 0x08));
+  clear_dma_ff(sc->device->host->dma_channel);
+  set_dma_addr(sc->device->host->dma_channel, virt_to_phys(sc->SCp.ptr));
+  set_dma_count(sc->device->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->device->host->dma_channel);
+
+  pc980155_dma_enable(sc->device->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->device->host->io_port);
+
+  disable_dma(sc->device->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 (!request_region(base_io, 6, "PC-9801-55"))
+			continue;
+		if (!pc980155_test_port(regs)) {
+			release_region(base_io, 6);
+			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;
+		}
+
+		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 -Nru linux/drivers/scsi/pc980155.h linux98/drivers/scsi/pc980155.h
--- linux/drivers/scsi/pc980155.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/drivers/scsi/pc980155.h	2002-12-15 12:05:05.000000000 +0900
@@ -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 =		scsicam_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 -Nru linux/drivers/scsi/pc980155regs.h linux98/drivers/scsi/pc980155regs.h
--- linux/drivers/scsi/pc980155regs.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/drivers/scsi/pc980155regs.h	2001-12-03 18:44:10.000000000 +0900
@@ -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 -Nru linux/drivers/scsi/scsi_scan.c linux98/drivers/scsi/scsi_scan.c
--- linux/drivers/scsi/scsi_scan.c	2002-12-24 14:21:04.000000000 +0900
+++ linux98/drivers/scsi/scsi_scan.c	2002-12-26 14:28:56.000000000 +0900
@@ -128,6 +128,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 -Nru linux/drivers/scsi/sd.c linux98/drivers/scsi/sd.c
--- linux/drivers/scsi/sd.c	2003-02-11 03:38:49.000000000 +0900
+++ linux98/drivers/scsi/sd.c	2003-02-14 00:24:33.000000000 +0900
@@ -46,6 +46,7 @@
 #include <linux/blk.h>
 #include <linux/blkpg.h>
 #include <asm/uaccess.h>
+#include <asm/pc9800.h>
 
 #include "scsi.h"
 #include "hosts.h"
@@ -467,6 +468,61 @@
 	return 0;
 }
 
+
+
+/* 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;
+}
+
+int pc98_bios_param(struct block_device *bdev, int *ip)
+{
+	struct scsi_disk *sdkp = scsi_disk(bdev->bd_disk);
+	struct scsi_device *sdp = sdkp->device;
+
+	if (sdp && first_real_host() == sdp->host && sdp->id < 7
+	    && __PC9800SCA_TEST_BIT(PC9800SCA_DISK_EQUIPS, sdp->id))
+	{
+		const u8 *p = (&__PC9800SCA(u8, PC9800SCA_SCSI_PARAMS)
+			       + sdp->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;
+		}
+		return 0;
+	}
+
+	/* Assume PC-9801-92 compatible parameters for HAs without BIOS.  */
+	ip[0] = 8;
+	ip[1] = 32;
+	ip[2] = sdkp->capacity / (8 * 32);
+	if (ip[2] > 65535) {	/* if capacity >= 8GB */
+		/* Recent on-board adapters seem to use this parameter.  */
+		ip[1] = 128;
+		ip[2] = sdkp->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;
+		}
+	}
+	return 0;
+}
+
 static int sd_hdio_getgeo(struct block_device *bdev, struct hd_geometry *loc)
 {
 	struct scsi_disk *sdkp = scsi_disk(bdev->bd_disk);
@@ -480,7 +536,9 @@
        	diskinfo[2] = sdkp->capacity >> 11;
 	
 	/* override with calculated, extended default, or driver values */
-	if (host->hostt->bios_param)
+	if (pc98)
+		pc98_bios_param(bdev, diskinfo);
+	else if (host->hostt->bios_param)
 		host->hostt->bios_param(sdp, bdev, sdkp->capacity, diskinfo);
 	else
 		scsicam_bios_param(bdev, sdkp->capacity, diskinfo);
diff -Nru linux-2.5.61/drivers/scsi/wd33c93.c linux98-2.5.61/drivers/scsi/wd33c93.c
--- linux-2.5.61/drivers/scsi/wd33c93.c	2003-02-15 08:52:04.000000000 +0900
+++ linux98-2.5.61/drivers/scsi/wd33c93.c	2003-02-17 21:51:42.000000000 +0900
@@ -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
@@ -316,9 +324,10 @@
    struct WD33C93_hostdata *hostdata;
    Scsi_Cmnd *tmp;
 
-   hostdata = (struct WD33C93_hostdata *)cmd->host->hostdata;
+   hostdata = (struct WD33C93_hostdata *)cmd->device->host->hostdata;
 
-DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld( ",cmd->target,cmd->cmnd[0],cmd->pid))
+DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld( ",cmd->device->id,
+				cmd->cmnd[0],cmd->pid))
 
 /* Set up a few fields in the Scsi_Cmnd structure for our own use:
  *  - host_scribble is the pointer to the next cmd in the input queue
@@ -401,7 +410,7 @@
  * Go see if any of them are runnable!
  */
 
-   wd33c93_execute(cmd->host);
+   wd33c93_execute(cmd->device->host);
 
 DB(DB_QUEUE_COMMAND,printk(")Q-%ld ",cmd->pid))
 
@@ -426,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("))
 
@@ -445,7 +453,7 @@
    cmd = (Scsi_Cmnd *)hostdata->input_Q;
    prev = 0;
    while (cmd) {
-      if (!(hostdata->busy[cmd->target] & (1 << cmd->lun)))
+      if (!(hostdata->busy[cmd->device->id] & (1 << cmd->device->lun)))
          break;
       prev = cmd;
       cmd = (Scsi_Cmnd *)cmd->host_scribble;
@@ -468,7 +476,7 @@
       hostdata->input_Q = (Scsi_Cmnd *)cmd->host_scribble;
 
 #ifdef PROC_STATISTICS
-   hostdata->cmd_cnt[cmd->target]++;
+   hostdata->cmd_cnt[cmd->device->id]++;
 #endif
 
    /*
@@ -476,9 +484,9 @@
     */
 
    if (is_dir_out(cmd))
-      write_wd33c93(regs, WD_DESTINATION_ID, cmd->target);
+      write_wd33c93(regs, WD_DESTINATION_ID, cmd->device->id);
    else
-      write_wd33c93(regs, WD_DESTINATION_ID, cmd->target | DSTID_DPD);
+      write_wd33c93(regs, WD_DESTINATION_ID, cmd->device->id | DSTID_DPD);
 
 /* Now we need to figure out whether or not this command is a good
  * candidate for disconnect/reselect. We guess to the best of our
@@ -516,7 +524,8 @@
       goto no;
    for (prev=(Scsi_Cmnd *)hostdata->input_Q; prev;
          prev=(Scsi_Cmnd *)prev->host_scribble) {
-      if ((prev->target != cmd->target) || (prev->lun != cmd->lun)) {
+      if ((prev->device->id != cmd->device->id)
+		|| (prev->device->lun != cmd->device->lun)) {
          for (prev=(Scsi_Cmnd *)hostdata->input_Q; prev;
                prev=(Scsi_Cmnd *)prev->host_scribble)
             prev->SCp.phase = 1;
@@ -529,19 +538,20 @@
    cmd->SCp.phase = 1;
 
 #ifdef PROC_STATISTICS
-   hostdata->disc_allowed_cnt[cmd->target]++;
+   hostdata->disc_allowed_cnt[cmd->device->id]++;
 #endif
 
 no:
 
    write_wd33c93(regs, WD_SOURCE_ID, ((cmd->SCp.phase)?SRCID_ER:0));
 
-   write_wd33c93(regs, WD_TARGET_LUN, cmd->lun);
-   write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER,hostdata->sync_xfer[cmd->target]);
-   hostdata->busy[cmd->target] |= (1 << cmd->lun);
+   write_wd33c93(regs, WD_TARGET_LUN, cmd->device->lun);
+   write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER,
+			hostdata->sync_xfer[cmd->device->id]);
+   hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
 
    if ((hostdata->level2 == L2_NONE) ||
-       (hostdata->sync_stat[cmd->target] == SS_UNSET)) {
+       (hostdata->sync_stat[cmd->device->id] == SS_UNSET)) {
 
          /*
           * Do a 'Select-With-ATN' command. This will end with
@@ -565,8 +575,8 @@
  * later, but at that time we'll negotiate for async by specifying a
  * sync fifo depth of 0.
  */
-      if (hostdata->sync_stat[cmd->target] == SS_UNSET)
-            hostdata->sync_stat[cmd->target] = SS_FIRST;
+      if (hostdata->sync_stat[cmd->device->id] == SS_UNSET)
+            hostdata->sync_stat[cmd->device->id] = SS_FIRST;
       hostdata->state = S_SELECTING;
       write_wd33c93_count(regs, 0); /* guarantee a DATA_PHASE interrupt */
       write_wd33c93_cmd(regs, WD_CMD_SEL_ATN);
@@ -589,9 +599,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
@@ -677,7 +694,7 @@
 struct WD33C93_hostdata *hostdata;
 unsigned long length;
 
-   hostdata = (struct WD33C93_hostdata *)cmd->host->hostdata;
+   hostdata = (struct WD33C93_hostdata *)cmd->device->host->hostdata;
 
 /* Normally, you'd expect 'this_residual' to be non-zero here.
  * In a series of scatter-gather transfers, however, this
@@ -695,7 +712,8 @@
 		     cmd->SCp.buffer->offset;
       }
 
-   write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER,hostdata->sync_xfer[cmd->target]);
+   write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER,
+			hostdata->sync_xfer[cmd->device->id]);
 
 /* 'hostdata->no_dma' is TRUE if we don't even want to try DMA.
  * Update 'this_residual' and 'ptr' after 'transfer_pio()' returns.
@@ -792,7 +810,7 @@
 
    if (hostdata->dma == D_DMA_RUNNING) {
 DB(DB_TRANSFER,printk("[%p/%d:",cmd->SCp.ptr,cmd->SCp.this_residual))
-      hostdata->dma_stop(cmd->host, cmd, 1);
+      hostdata->dma_stop(cmd->device->host, cmd, 1);
       hostdata->dma = D_DMA_OFF;
       length = cmd->SCp.this_residual;
       cmd->SCp.this_residual = read_wd33c93_count(regs);
@@ -815,7 +833,7 @@
             }
 
          cmd->result = DID_NO_CONNECT << 16;
-         hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+         hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
          hostdata->state = S_UNCONNECTED;
          cmd->scsi_done(cmd);
 
@@ -849,16 +867,16 @@
 
       /* construct an IDENTIFY message with correct disconnect bit */
 
-         hostdata->outgoing_msg[0] = (0x80 | 0x00 | cmd->lun);
+         hostdata->outgoing_msg[0] = (0x80 | 0x00 | cmd->device->lun);
          if (cmd->SCp.phase)
             hostdata->outgoing_msg[0] |= 0x40;
 
-         if (hostdata->sync_stat[cmd->target] == SS_FIRST) {
+         if (hostdata->sync_stat[cmd->device->id] == SS_FIRST) {
 #ifdef SYNC_DEBUG
 printk(" sending SDTR ");
 #endif
 
-            hostdata->sync_stat[cmd->target] = SS_WAITING;
+            hostdata->sync_stat[cmd->device->id] = SS_WAITING;
 
 /* Tack on a 2nd message to ask about synchronous transfers. If we've
  * been asked to do only asynchronous transfers on this device, we
@@ -869,7 +887,7 @@
             hostdata->outgoing_msg[1] = EXTENDED_MESSAGE;
             hostdata->outgoing_msg[2] = 3;
             hostdata->outgoing_msg[3] = EXTENDED_SDTR;
-            if (hostdata->no_sync & (1 << cmd->target)) {
+            if (hostdata->no_sync & (1 << cmd->device->id)) {
                hostdata->outgoing_msg[4] = hostdata->default_sx_per/4;
                hostdata->outgoing_msg[5] = 0;
                }
@@ -995,8 +1013,8 @@
 #ifdef SYNC_DEBUG
 printk("-REJ-");
 #endif
-               if (hostdata->sync_stat[cmd->target] == SS_WAITING)
-                  hostdata->sync_stat[cmd->target] = SS_SET;
+               if (hostdata->sync_stat[cmd->device->id] == SS_WAITING)
+                  hostdata->sync_stat[cmd->device->id] = SS_SET;
                write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
                hostdata->state = S_CONNECTED;
                break;
@@ -1017,7 +1035,7 @@
                   switch (ucp[2]) {   /* what's the EXTENDED code? */
                      case EXTENDED_SDTR:
                         id = calc_sync_xfer(ucp[3],ucp[4]);
-                        if (hostdata->sync_stat[cmd->target] != SS_WAITING) {
+                        if (hostdata->sync_stat[cmd->device->id] != SS_WAITING) {
 
 /* A device has sent an unsolicited SDTR message; rather than go
  * through the effort of decoding it and then figuring out what
@@ -1035,16 +1053,16 @@
                            hostdata->outgoing_msg[3] = hostdata->default_sx_per/4;
                            hostdata->outgoing_msg[4] = 0;
                            hostdata->outgoing_len = 5;
-                           hostdata->sync_xfer[cmd->target] =
+                           hostdata->sync_xfer[cmd->device->id] =
                                        calc_sync_xfer(hostdata->default_sx_per/4,0);
                            }
                         else {
-                           hostdata->sync_xfer[cmd->target] = id;
+                           hostdata->sync_xfer[cmd->device->id] = id;
                            }
 #ifdef SYNC_DEBUG
-printk("sync_xfer=%02x",hostdata->sync_xfer[cmd->target]);
+printk("sync_xfer=%02x",hostdata->sync_xfer[cmd->device->id]);
 #endif
-                        hostdata->sync_stat[cmd->target] = SS_SET;
+                        hostdata->sync_stat[cmd->device->id] = SS_SET;
                         write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
                         hostdata->state = S_CONNECTED;
                         break;
@@ -1107,7 +1125,7 @@
             lun = read_wd33c93(regs, WD_TARGET_LUN);
 DB(DB_INTR,printk(":%d.%d",cmd->SCp.Status,lun))
             hostdata->connected = NULL;
-            hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+            hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
             hostdata->state = S_UNCONNECTED;
             if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE)
                cmd->SCp.Status = lun;
@@ -1195,7 +1213,7 @@
             }
 DB(DB_INTR,printk("UNEXP_DISC-%ld",cmd->pid))
          hostdata->connected = NULL;
-         hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+         hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
          hostdata->state = S_UNCONNECTED;
          if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
             cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
@@ -1227,7 +1245,7 @@
          switch (hostdata->state) {
             case S_PRE_CMP_DISC:
                hostdata->connected = NULL;
-               hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+               hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
                hostdata->state = S_UNCONNECTED;
 DB(DB_INTR,printk(":%d",cmd->SCp.Status))
                if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
@@ -1244,7 +1262,7 @@
                hostdata->state = S_UNCONNECTED;
 
 #ifdef PROC_STATISTICS
-               hostdata->disc_done_cnt[cmd->target]++;
+               hostdata->disc_done_cnt[cmd->device->id]++;
 #endif
 
                break;
@@ -1278,7 +1296,7 @@
             if (hostdata->selecting) {
                cmd = (Scsi_Cmnd *)hostdata->selecting;
                hostdata->selecting = NULL;
-               hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+               hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
                cmd->host_scribble = (uchar *)hostdata->input_Q;
                hostdata->input_Q = cmd;
                }
@@ -1288,7 +1306,7 @@
 
             if (cmd) {
                if (phs == 0x00) {
-                  hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+                  hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
                   cmd->host_scribble = (uchar *)hostdata->input_Q;
                   hostdata->input_Q = cmd;
                   }
@@ -1364,7 +1382,7 @@
          cmd = (Scsi_Cmnd *)hostdata->disconnected_Q;
          patch = NULL;
          while (cmd) {
-            if (id == cmd->target && lun == cmd->lun)
+            if (id == cmd->device->id && lun == cmd->device->lun)
                break;
             patch = cmd;
             cmd = (Scsi_Cmnd *)cmd->host_scribble;
@@ -1392,9 +1410,9 @@
     */
 
          if (is_dir_out(cmd))
-            write_wd33c93(regs, WD_DESTINATION_ID, cmd->target);
+            write_wd33c93(regs, WD_DESTINATION_ID, cmd->device->id);
          else
-            write_wd33c93(regs, WD_DESTINATION_ID, cmd->target | DSTID_DPD);
+            write_wd33c93(regs, WD_DESTINATION_ID, cmd->device->id | DSTID_DPD);
          if (hostdata->level2 >= L2_RESELECT) {
             write_wd33c93_count(regs, 0);  /* we want a DATA_PHASE interrupt */
             write_wd33c93(regs, WD_COMMAND_PHASE, 0x45);
@@ -1467,7 +1485,7 @@
 struct WD33C93_hostdata *hostdata;
 int i;
 
-   instance = SCpnt->host;
+   instance = SCpnt->device->host;
    hostdata = (struct WD33C93_hostdata *)instance->hostdata;
 
    printk("scsi%d: reset. ", instance->host_no);
@@ -1503,9 +1521,9 @@
 wd33c93_regs regs;
 Scsi_Cmnd *tmp, *prev;
 
-   disable_irq(cmd->host->irq);
+   disable_irq(cmd->device->host->irq);
 
-   instance = cmd->host;
+   instance = cmd->device->host;
    hostdata = (struct WD33C93_hostdata *)instance->hostdata;
    regs = hostdata->regs;
 
@@ -1526,7 +1544,7 @@
          cmd->result = DID_ABORT << 16;
          printk("scsi%d: Abort - removing command %ld from input_Q. ",
            instance->host_no, cmd->pid);
-    enable_irq(cmd->host->irq);
+    enable_irq(cmd->device->host->irq);
          cmd->scsi_done(cmd);
          return SCSI_ABORT_SUCCESS;
          }
@@ -1591,7 +1609,7 @@
       sr = read_wd33c93(regs, WD_SCSI_STATUS);
       printk("asr=%02x, sr=%02x.",asr,sr);
 
-      hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+      hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
       hostdata->connected = NULL;
       hostdata->state = S_UNCONNECTED;
       cmd->result = DID_ABORT << 16;
@@ -1599,7 +1617,7 @@
 /*      sti();*/
       wd33c93_execute (instance);
 
-      enable_irq(cmd->host->irq);
+      enable_irq(cmd->device->host->irq);
       cmd->scsi_done(cmd);
       return SCSI_ABORT_SUCCESS;
       }
@@ -1616,7 +1634,7 @@
          printk("scsi%d: Abort - command %ld found on disconnected_Q - ",
                  instance->host_no, cmd->pid);
          printk("returning ABORT_SNOOZE. ");
-    enable_irq(cmd->host->irq);
+    enable_irq(cmd->device->host->irq);
          return SCSI_ABORT_SNOOZE;
          }
       tmp = (Scsi_Cmnd *)tmp->host_scribble;
@@ -1635,7 +1653,7 @@
 /*   sti();*/
    wd33c93_execute (instance);
 
-   enable_irq(cmd->host->irq);
+   enable_irq(cmd->device->host->irq);
    printk("scsi%d: warning : SCSI command probably completed successfully"
       "         before abortion. ", instance->host_no);
    return SCSI_ABORT_NOT_RUNNING;
@@ -1703,7 +1721,7 @@
    return 1;
 }
 
-__setup("wd33c93", wd33c93_setup);
+__setup("wd33c93=", wd33c93_setup);
 
 
 /* check_setup_args() returns index if key found, 0 if not
@@ -1984,7 +2002,7 @@
       if (hd->connected) {
          cmd = (Scsi_Cmnd *)hd->connected;
          sprintf(tbuf," %ld-%d:%d(%02x)",
-               cmd->pid, cmd->target, cmd->lun, cmd->cmnd[0]);
+               cmd->pid, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
          strcat(bp,tbuf);
          }
       }
@@ -1993,7 +2011,7 @@
       cmd = (Scsi_Cmnd *)hd->input_Q;
       while (cmd) {
          sprintf(tbuf," %ld-%d:%d(%02x)",
-               cmd->pid, cmd->target, cmd->lun, cmd->cmnd[0]);
+               cmd->pid, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
          strcat(bp,tbuf);
          cmd = (Scsi_Cmnd *)cmd->host_scribble;
          }
@@ -2003,7 +2021,7 @@
       cmd = (Scsi_Cmnd *)hd->disconnected_Q;
       while (cmd) {
          sprintf(tbuf," %ld-%d:%d(%02x)",
-               cmd->pid, cmd->target, cmd->lun, cmd->cmnd[0]);
+               cmd->pid, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
          strcat(bp,tbuf);
          cmd = (Scsi_Cmnd *)cmd->host_scribble;
          }
@@ -2037,4 +2055,10 @@
 {
 }
 
+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 -Nru linux/drivers/scsi/wd33c93.h linux98/drivers/scsi/wd33c93.h
--- linux/drivers/scsi/wd33c93.h	2002-10-12 13:21:35.000000000 +0900
+++ linux98/drivers/scsi/wd33c93.h	2002-10-12 14:18:53.000000000 +0900
@@ -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;
 
 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (24/26) serial
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (22 preceding siblings ...)
  2003-02-17 14:21 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (23/26) SCSI Osamu Tomita
@ 2003-02-17 14:24 ` Osamu Tomita
  2003-02-17 14:25 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (25/26) SMP Osamu Tomita
  2003-02-17 14:26 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (26/26) video card Osamu Tomita
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:24 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (24/26).

Serial port support for PC98.
- Patch for onboard modem.
- New driver for 1st COM port.
  PC98 has 2 COM ports. 1st port uses 8251 upper compatible chip
  and 2nd port uses 16550A compatible chip.

diff -Nru linux/drivers/serial/8250_pnp.c linux98/drivers/serial/8250_pnp.c
--- linux/drivers/serial/8250_pnp.c	2002-12-11 13:10:07.000000000 +0900
+++ linux98/drivers/serial/8250_pnp.c	2002-12-11 13:16:51.000000000 +0900
@@ -188,6 +188,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 */
@@ -373,6 +375,9 @@
 			    ((port->min == 0x2f8) ||
 			     (port->min == 0x3f8) ||
 			     (port->min == 0x2e8) ||
+#ifdef CONFIG_X86_PC9800
+			     (port->min == 0x8b0) ||
+#endif
 			     (port->min == 0x3e8)))
 				return 0;
 	}
diff -Nru linux/drivers/serial/Kconfig linux98/drivers/serial/Kconfig
--- linux/drivers/serial/Kconfig	2003-01-14 14:59:16.000000000 +0900
+++ linux98/drivers/serial/Kconfig	2003-01-17 13:07:46.000000000 +0900
@@ -372,14 +372,25 @@
 	bool "Use NEC V850E on-chip UART for console"
 	depends on V850E_NB85E_UART
 
+config SERIAL98
+	tristate "PC-9800 8251-based primary serial port support"
+	depends on X86_PC9800
+	help
+	  If you want to use standard primary serial ports on PC-9800, 
+	  say Y.  Otherwise, say N.
+
+config SERIAL98_CONSOLE
+        bool "Support for console on PC-9800 standard serial port"
+        depends on SERIAL98=y
+
 config SERIAL_CORE
 	tristate
-	default m if SERIAL_AMBA!=y && SERIAL_CLPS711X!=y && SERIAL_21285!=y && !SERIAL_SA1100 && !SERIAL_ANAKIN && !SERIAL_UART00 && SERIAL_8250!=y && SERIAL_MUX!=y && !SERIAL_ROCKETPORT && !SERIAL_SUNCORE && !V850E_NB85E_UART && (SERIAL_AMBA=m || SERIAL_CLPS711X=m || SERIAL_21285=m || SERIAL_8250=m || SERIAL_MUX=m)
-	default y if SERIAL_AMBA=y || SERIAL_CLPS711X=y || SERIAL_21285=y || SERIAL_SA1100 || SERIAL_ANAKIN || SERIAL_UART00 || SERIAL_8250=y || SERIAL_MUX=y || SERIAL_ROCKETPORT || SERIAL_SUNCORE || V850E_NB85E_UART
+	default m if SERIAL_AMBA!=y && SERIAL_CLPS711X!=y && SERIAL_21285!=y && !SERIAL_SA1100 && !SERIAL_ANAKIN && !SERIAL_UART00 && SERIAL_8250!=y && SERIAL_MUX!=y && !SERIAL_ROCKETPORT && !SERIAL_SUNCORE && !V850E_NB85E_UART && (SERIAL_AMBA=m || SERIAL_CLPS711X=m || SERIAL_21285=m || SERIAL_8250=m || SERIAL_MUX=m || SERIAL98=m)
+	default y if SERIAL_AMBA=y || SERIAL_CLPS711X=y || SERIAL_21285=y || SERIAL_SA1100 || SERIAL_ANAKIN || SERIAL_UART00 || SERIAL_8250=y || SERIAL_MUX=y || SERIAL_ROCKETPORT || SERIAL_SUNCORE || V850E_NB85E_UART || SERIAL98=y
 
 config SERIAL_CORE_CONSOLE
 	bool
-	depends on SERIAL_AMBA_CONSOLE || SERIAL_CLPS711X_CONSOLE || SERIAL_21285_CONSOLE || SERIAL_SA1100_CONSOLE || SERIAL_ANAKIN_CONSOLE || SERIAL_UART00_CONSOLE || SERIAL_8250_CONSOLE || SERIAL_MUX_CONSOLE || SERIAL_SUNCORE || V850E_NB85E_UART_CONSOLE
+	depends on SERIAL_AMBA_CONSOLE || SERIAL_CLPS711X_CONSOLE || SERIAL_21285_CONSOLE || SERIAL_SA1100_CONSOLE || SERIAL_ANAKIN_CONSOLE || SERIAL_UART00_CONSOLE || SERIAL_8250_CONSOLE || SERIAL_MUX_CONSOLE || SERIAL_SUNCORE || V850E_NB85E_UART_CONSOLE || SERIAL98_CONSOLE
 	default y
 
 config SERIAL_68328
diff -Nru linux-2.5.60/drivers/serial/Makefile linux98-2.5.60/drivers/serial/Makefile
--- linux-2.5.60/drivers/serial/Makefile	2003-02-11 03:38:32.000000000 +0900
+++ linux98-2.5.60/drivers/serial/Makefile	2003-02-11 13:32:49.000000000 +0900
@@ -27,3 +27,4 @@
 obj-$(CONFIG_SERIAL_68360) += 68360serial.o
 obj-$(CONFIG_SERIAL_COLDFIRE) += mcfserial.o
 obj-$(CONFIG_V850E_NB85E_UART) += nb85e_uart.o
+obj-$(CONFIG_SERIAL98) += serial98.o
diff -Nru linux/drivers/serial/serial98.c linux98/drivers/serial/serial98.c
--- linux/drivers/serial/serial98.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98/drivers/serial/serial98.c	2003-01-17 21:16:43.000000000 +0900
@@ -0,0 +1,1125 @@
+/*
+ *  linux/drivers/serial/serial98.c
+ *
+ *  Driver for NEC PC-9801/PC-9821 standard serial ports
+ *
+ *  Based on drivers/serial/8250.c, by Russell King.
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright (C) 2002 Osamu Tomita <tomita@cinet.co.jp>
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial_reg.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pc9800.h>
+#include <asm/pc9800_sca.h>
+
+#if defined(CONFIG_SERIAL98_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+
+#define SERIAL98_NR		1
+#define SERIAL98_ISR_PASS_LIMIT	256
+#define SERIAL98_EXT		0x434
+
+//#define RX_8251F		0x130	/* In: Receive buffer */
+//#define TX_8251F		0x130	/* Out: Transmit buffer */
+//#define LSR_8251F		0x132	/* In: Line Status Register */
+//#define MSR_8251F		0x134	/* In: Modem Status Register */
+#define IIR_8251F		0x136	/* In: Interrupt ID Register */
+#define FCR_8251F		0x138	/* I/O: FIFO Control Register */
+#define VFAST_8251F		0x13a	/* I/O: VFAST mode Register */
+
+#define CMD_8251F		0x32	/* Out: 8251 Command Resister */
+#define IER2_8251F		0x34	/* I/O: Interrupt Enable Register */
+#define IER1_8251F		0x35	/* I/O: Interrupt Enable Register */
+#define IER1_CTL		0x37	/* Out: Interrupt Enable Register */
+#define DIS_RXR_INT		0x00	/* disable RxRDY Interrupt */
+#define ENA_RXR_INT		0x01	/* enable RxRDY Interrupt */
+#define DIS_TXE_INT		0x02	/* disable TxEMPTY Interrupt */
+#define ENA_TXE_INT		0x03	/* enable TxEMPTY Interrupt */
+#define DIS_TXR_INT		0x04	/* disable TxRDY Interrupt */
+#define ENA_TXR_INT		0x05	/* enable TxRDY Interrupt */
+
+#define CMD_RESET		0x40	/* Reset Command */
+#define CMD_RTS			0x20	/* Set RTS line */
+#define CMD_CLR_ERR		0x10	/* Clear error flag */
+#define CMD_BREAK		0x08	/* Send Break */
+#define CMD_RXE			0x04	/* Enable receive */
+#define CMD_DTR			0x02	/* Set DTR line */
+#define CMD_TXE			0x01	/* Enable send */
+#define CMD_DUMMY		0x00	/* Dummy Command */
+
+#define VFAST_ENABLE		0x80	/* V.Fast mode Enable */
+
+/* Interrupt masks */
+#define INTR_8251_TXRE		0x04
+#define INTR_8251_TXEE		0x02
+#define INTR_8251_RXRE		0x01
+/* I/O Port */
+//#define PORT_8251_DATA	0
+//#define PORT_8251_CMD		2
+//#define PORT_8251_MOD		2
+//#define PORT_8251_STS		2
+/* status read */
+#define STAT_8251_TXRDY		0x01
+#define STAT_8251_RXRDY		0x02
+#define STAT_8251_TXEMP		0x04
+#define STAT_8251_PER		0x08
+#define STAT_8251_OER		0x10
+#define STAT_8251_FER		0x20
+#define STAT_8251_BRK		0x40
+#define STAT_8251_DSR		0x80
+#if 1
+#define STAT_8251F_TXEMP	0x01
+#define STAT_8251F_TXRDY	0x02
+#define STAT_8251F_RXRDY	0x04
+#define STAT_8251F_DSR		0x08
+#define STAT_8251F_OER		0x10
+#define STAT_8251F_PER		0x20
+#define STAT_8251F_FER		0x40
+#define STAT_8251F_BRK		0x80
+#else
+#define STAT_8251F_TXEMP	0x01
+#define STAT_8251F_TEMT		0x01
+#define STAT_8251F_TXRDY	0x02
+#define STAT_8251F_THRE		0x02
+#define STAT_8251F_RXRDY	0x04
+#define STAT_8251F_DSR		0x04
+#define STAT_8251F_PER		0x08
+#define STAT_8251F_OER		0x10
+#define STAT_8251F_FER		0x20
+#define STAT_8251F_BRK		0x40
+#endif
+
+/*
+ * We wrap our port structure around the generic uart_port.
+ */
+struct serial98_port {
+	struct uart_port	port;
+	unsigned int		type;
+	unsigned int		ext;
+	unsigned int		lsr_break_flag;
+	unsigned char		cmd;
+	unsigned char		mode;
+	unsigned char		msr;
+	unsigned char		ier;
+	unsigned char		rxchk;
+	unsigned char		txemp;
+	unsigned char		txrdy;
+	unsigned char		rxrdy;
+	unsigned char		brk;
+	unsigned char		fe;
+	unsigned char		oe;
+	unsigned char		pe;
+	unsigned char		dr;
+};
+
+#ifdef CONFIG_SERIAL98_CONSOLE
+static void
+serial98_console_write(struct console *co, const char *s, unsigned int count);
+static kdev_t serial98_console_device(struct console *co);
+static int __init serial98_console_setup(struct console *co, char *options);
+
+static struct console serial98_console = {
+	.name		= "ttyS",
+	.write		= serial98_console_write,
+	.device		= serial98_console_device,
+	.setup		= serial98_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+};
+
+#define SERIAL98_CONSOLE	&serial98_console
+#else
+#define SERIAL98_CONSOLE	NULL
+#endif
+
+static struct uart_driver serial98_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "serial98",
+	.dev_name		= "ttyS%d",
+	.major			= TTY_MAJOR,
+	.minor			= 64,
+	.nr			= SERIAL98_NR,
+	.cons			= SERIAL98_CONSOLE,
+};
+
+static int serial98_clk;
+static char type_str[48];
+
+#define PORT98 ((struct serial98_port *)port)
+#define PORT (PORT98->port)
+
+static void serial98_fifo_enable(struct uart_port *port, int enable)
+{
+	unsigned char fcr;
+
+	if (PORT.type == PORT_FIFO_PC98 || PORT.type == PORT_VFAST_PC98) {
+		fcr = inb(FCR_8251F);
+		if (enable)
+			fcr |= UART_FCR_ENABLE_FIFO;
+		else
+			fcr &= ~UART_FCR_ENABLE_FIFO;
+		outb(fcr, FCR_8251F);
+	}
+
+	if (!enable)
+		return;
+
+	outb(0, 0x5f);	/* wait */
+	outb(0, 0x5f);
+	outb(0, 0x5f);
+	outb(0, 0x5f);
+}
+
+static void serial98_cmd_out(struct uart_port *port, unsigned char cmd)
+{
+	serial98_fifo_enable(port, 0);
+	outb(cmd, CMD_8251F);
+	serial98_fifo_enable(port, 1);
+}
+
+static void serial98_mode_set(struct uart_port *port)
+{
+	serial98_cmd_out(port, CMD_DUMMY);
+	serial98_cmd_out(port, CMD_DUMMY);
+	serial98_cmd_out(port, CMD_DUMMY);
+	serial98_cmd_out(port, CMD_RESET);
+	serial98_cmd_out(port, PORT98->mode);
+}
+
+static unsigned char serial98_msr_in(struct uart_port *port)
+{
+	unsigned long flags;
+	unsigned int ms, st;
+	unsigned int tmp;
+
+	spin_lock_irqsave(&PORT.lock, flags);
+	if (PORT.type == PORT_FIFO_PC98 || PORT.type == PORT_VFAST_PC98) {
+		PORT98->msr = inb(PORT.iobase + 4);
+	} else {
+		ms = inb(0x33);
+		st = inb(0x32);
+		tmp = 0;
+		if(!(ms & 0x20))
+			tmp |= UART_MSR_DCD;
+		if(!(ms & 0x80)) {
+			tmp |= UART_MSR_RI;
+			PORT98->msr |= UART_MSR_RI;
+		}
+		if(!(ms & 0x40))
+			tmp |= UART_MSR_CTS;
+		if(st & 0x80)
+			tmp |= UART_MSR_DSR;
+		PORT98->msr = ((PORT98->msr ^ tmp) >> 4) | tmp;
+	}
+
+	spin_unlock_irqrestore(&PORT.lock, flags);
+	return PORT98->msr;
+}
+
+static void serial98_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+	unsigned int ier = inb(IER1_8251F);
+
+	ier &= ~(INTR_8251_TXRE | INTR_8251_TXEE);
+	outb(ier, IER1_8251F);
+}
+
+static void serial98_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+	unsigned int ier = inb(IER1_8251F);
+
+	ier |= INTR_8251_TXRE | INTR_8251_TXEE;
+	outb(ier, IER1_8251F);
+}
+
+static void serial98_stop_rx(struct uart_port *port)
+{
+	PORT.read_status_mask &= ~PORT98->dr;
+	outb(DIS_RXR_INT, IER1_CTL);
+}
+
+static void serial98_enable_ms(struct uart_port *port)
+{
+	outb(PORT98->ier | 0x80, IER2_8251F);
+}
+
+static void serial98_rx_chars(struct uart_port *port, int *status,
+				struct pt_regs *regs)
+{
+	struct tty_struct *tty = PORT.info->tty;
+	unsigned char ch;
+	int max_count = 256;
+
+	do {
+		if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
+			tty->flip.work.func((void *)tty);
+			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+				return; // if TTY_DONT_FLIP is set
+		}
+		ch = inb(PORT.iobase);
+		*tty->flip.char_buf_ptr = ch;
+		*tty->flip.flag_buf_ptr = TTY_NORMAL;
+		PORT.icount.rx++;
+
+		if (unlikely(*status & (PORT98->brk | PORT98->pe |
+				       PORT98->fe | PORT98->oe))) {
+			/*
+			 * For statistics only
+			 */
+			if (*status & PORT98->brk) {
+				*status &= ~(PORT98->fe | PORT98->pe);
+				PORT.icount.brk++;
+				/*
+				 * We do the SysRQ and SAK checking
+				 * here because otherwise the break
+				 * may get masked by ignore_status_mask
+				 * or read_status_mask.
+				 */
+				if (uart_handle_break(&PORT))
+					goto ignore_char;
+			} else if (*status & PORT98->pe)
+				PORT.icount.parity++;
+			else if (*status & PORT98->fe)
+				PORT.icount.frame++;
+			if (*status & PORT98->oe)
+				PORT.icount.overrun++;
+
+			/*
+			 * Mask off conditions which should be ingored.
+			 */
+			*status &= PORT.read_status_mask;
+
+#ifdef CONFIG_SERIAL98_CONSOLE
+			if (PORT.line == PORT.cons->index) {
+				/* Recover the break flag from console xmit */
+				*status |= PORT98->lsr_break_flag;
+				PORT98->lsr_break_flag = 0;
+			}
+#endif
+			if (*status & PORT98->brk) {
+				*tty->flip.flag_buf_ptr = TTY_BREAK;
+			} else if (*status & PORT98->pe)
+				*tty->flip.flag_buf_ptr = TTY_PARITY;
+			else if (*status & PORT98->fe)
+				*tty->flip.flag_buf_ptr = TTY_FRAME;
+		}
+		if (uart_handle_sysrq_char(&PORT, ch, regs))
+			goto ignore_char;
+		if ((*status & PORT.ignore_status_mask) == 0) {
+			tty->flip.flag_buf_ptr++;
+			tty->flip.char_buf_ptr++;
+			tty->flip.count++;
+		}
+		if ((*status & PORT98->oe) &&
+		    tty->flip.count < TTY_FLIPBUF_SIZE) {
+			/*
+			 * Overrun is special, since it's reported
+			 * immediately, and doesn't affect the current
+			 * character.
+			 */
+			*tty->flip.flag_buf_ptr = TTY_OVERRUN;
+			tty->flip.flag_buf_ptr++;
+			tty->flip.char_buf_ptr++;
+			tty->flip.count++;
+		}
+	ignore_char:
+		*status = inb(PORT.iobase + 2);
+	} while ((*status & PORT98->rxchk) && (max_count-- > 0));
+	tty_flip_buffer_push(tty);
+}
+
+static void serial98_tx_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit = &PORT.info->xmit;
+	int count;
+
+	if (PORT.x_char) {
+		outb(PORT.x_char, PORT.iobase);
+		PORT.icount.tx++;
+		PORT.x_char = 0;
+		return;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&PORT)) {
+		serial98_stop_tx(port, 0);
+		return;
+	}
+
+	count = PORT.fifosize;
+	do {
+		outb(xmit->buf[xmit->tail], PORT.iobase);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		PORT.icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&PORT);
+
+	if (uart_circ_empty(xmit))
+		serial98_stop_tx(&PORT, 0);
+}
+
+static void serial98_modem_status(struct uart_port *port)
+{
+	int status;
+
+	status = serial98_msr_in(port);
+
+	if ((status & UART_MSR_ANY_DELTA) == 0)
+		return;
+
+	if (status & UART_MSR_TERI)
+		PORT.icount.rng++;
+	if (status & UART_MSR_DDSR)
+		PORT.icount.dsr++;
+	if (status & UART_MSR_DDCD)
+		uart_handle_dcd_change(&PORT, status & UART_MSR_DCD);
+	if (status & UART_MSR_DCTS)
+		uart_handle_cts_change(&PORT, status & UART_MSR_CTS);
+
+	wake_up_interruptible(&PORT.info->delta_msr_wait);
+}
+
+static void serial98_int(int irq, void *port, struct pt_regs *regs)
+{
+	unsigned int status;
+
+	spin_lock(&PORT.lock);
+	status = inb(PORT.iobase + 2);
+	if (status & PORT98->rxrdy) {
+		serial98_rx_chars(port, &status, regs);
+	}
+	serial98_modem_status(port);
+	if (status & PORT98->txrdy) {
+		serial98_tx_chars(port);
+	}
+	spin_unlock(&PORT.lock);
+}
+
+static unsigned int serial98_tx_empty(struct uart_port *port)
+{
+	unsigned long flags;
+	unsigned int ret = 0;
+
+	spin_lock_irqsave(&PORT.lock, flags);
+	if (inb(PORT.iobase + 2) & PORT98->txemp)
+			ret = TIOCSER_TEMT;
+
+	spin_unlock_irqrestore(&PORT.lock, flags);
+	return ret;
+}
+
+static unsigned int serial98_get_mctrl(struct uart_port *port)
+{
+	unsigned char status;
+	unsigned int ret = 0;
+
+	status = serial98_msr_in(port);
+	if (status & UART_MSR_DCD)
+		ret |= TIOCM_CAR;
+	if (status & UART_MSR_RI)
+		ret |= TIOCM_RNG;
+	if (status & UART_MSR_DSR)
+		ret |= TIOCM_DSR;
+	if (status & UART_MSR_CTS)
+		ret |= TIOCM_CTS;
+	return ret;
+}
+
+static void serial98_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	PORT98->cmd &= 0xdd;
+	if (mctrl & TIOCM_RTS)
+		PORT98->cmd |= CMD_RTS;
+
+	if (mctrl & TIOCM_DTR)
+		PORT98->cmd |= CMD_DTR;
+
+	serial98_cmd_out(port, PORT98->cmd);
+}
+
+static void serial98_break_ctl(struct uart_port *port, int break_state)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&PORT.lock, flags);
+	if (break_state == -1)
+		PORT98->cmd |= CMD_BREAK;
+	else
+		PORT98->cmd &= ~CMD_BREAK;
+
+	serial98_cmd_out(port, PORT98->cmd);
+	spin_unlock_irqrestore(&PORT.lock, flags);
+}
+
+static int serial98_startup(struct uart_port *port)
+{
+	int retval;
+
+	if (PORT.type == PORT_8251_PC98) {
+		/* Wake up UART */
+		PORT98->mode = 0xfc;
+		serial98_mode_set(port);
+		outb(DIS_RXR_INT, IER1_CTL);
+		outb(DIS_TXE_INT, IER1_CTL);
+		outb(DIS_TXR_INT, IER1_CTL);
+		PORT98->mode = 0;
+		serial98_mode_set(port);
+	}
+
+	/*
+	 * Clear the FIFO buffers and disable them.
+	 * (they will be reeanbled in set_termios())
+	 */
+	if (PORT.type == PORT_FIFO_PC98 || PORT.type == PORT_VFAST_PC98) {
+		outb(UART_FCR_ENABLE_FIFO, FCR_8251F);
+		outb((UART_FCR_ENABLE_FIFO
+			| UART_FCR_CLEAR_RCVR
+			| UART_FCR_CLEAR_XMIT), FCR_8251F);
+		outb(0, FCR_8251F);
+	}
+
+	/* Clear the interrupt registers. */
+	inb(0x30);
+	inb(0x32);
+	if (PORT.type == PORT_FIFO_PC98 || PORT.type == PORT_VFAST_PC98) {
+		inb(PORT.iobase);
+		inb(PORT.iobase + 2);
+		inb(PORT.iobase + 4);
+		inb(PORT.iobase + 6);
+	}
+
+	/* Allocate the IRQ */
+	retval = request_irq(PORT.irq, serial98_int, 0,
+				serial98_reg.driver_name, port);
+	if (retval)
+		return retval;
+
+	/*
+	 * Now, initialize the UART
+	 */
+	PORT98->mode = 0x4e;
+	serial98_mode_set(port);
+	PORT98->cmd = 0x15;
+	serial98_cmd_out(port, PORT98->cmd);
+	PORT98->cmd = 0x05;
+
+	/*
+	 * Finally, enable interrupts
+	 */
+	outb(0x00, IER2_8251F);
+	outb(ENA_RXR_INT, IER1_CTL);
+
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	inb(0x30);
+	inb(0x32);
+	if (PORT.type == PORT_FIFO_PC98 || PORT.type == PORT_VFAST_PC98) {
+		inb(PORT.iobase);
+		inb(PORT.iobase + 2);
+		inb(PORT.iobase + 4);
+		inb(PORT.iobase + 6);
+	}
+
+	return 0;
+}
+
+static void serial98_shutdown(struct uart_port *port)
+{
+	unsigned long flags;
+
+	/*
+	 * disable all interrupts
+	 */
+	spin_lock_irqsave(&PORT.lock, flags);
+	if (PORT.type == PORT_VFAST_PC98)
+		outb(0, VFAST_8251F);		/* V.FAST mode off */
+
+	/* disnable all modem status interrupt */
+	outb(0x80, IER2_8251F);
+
+	/* disnable TX/RX interrupt */
+	outb(0x00, IER2_8251F);
+	outb(DIS_RXR_INT, IER1_CTL);
+	outb(DIS_TXE_INT, IER1_CTL);
+	outb(DIS_TXR_INT, IER1_CTL);
+	PORT98->ier = 0;
+
+	spin_unlock_irqrestore(&PORT.lock, flags);
+
+	/*
+	 * Free the interrupt
+	 */
+	free_irq(PORT.irq, port);
+
+	/* disable break condition and disable the port */
+	serial98_mode_set(port);
+
+	/* disable FIFO's */	
+	if (PORT.type == PORT_FIFO_PC98 || PORT.type == PORT_VFAST_PC98) {
+		outb((UART_FCR_ENABLE_FIFO
+			| UART_FCR_CLEAR_RCVR
+			| UART_FCR_CLEAR_XMIT), FCR_8251F);
+		outb(0, FCR_8251F);
+	}
+
+	inb(PORT.iobase);
+}
+
+static void
+serial98_set_termios(struct uart_port *port, struct termios *termios,
+		       struct termios *old)
+{
+	unsigned char stopbit, cval, fcr = 0, ier = 0;
+	unsigned long flags;
+	unsigned int quot;
+
+	stopbit = 0x80;
+	switch (termios->c_cflag & CSIZE) {
+		case CS5:
+			cval = 0x42;
+			stopbit = 0xc0;
+			break;
+		case CS6:
+			cval = 0x46;
+			break;
+		case CS7:
+			cval = 0x4a;
+			break;
+		default:
+		case CS8:
+			cval = 0x4e;
+			break;
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		cval ^= stopbit;
+	if (termios->c_cflag & PARENB)
+		cval |= 0x10;
+	if (!(termios->c_cflag & PARODD))
+		cval |= 0x20;
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	quot = uart_get_divisor(port, termios, old);
+
+	if (PORT.type == PORT_FIFO_PC98 || PORT.type == PORT_VFAST_PC98) {
+		if ((PORT.uartclk / quot) < (2400 * 16))
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+		else
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+	}
+
+	/*
+	 * Ok, we're now changing the port state.  Do it with
+	 * interrupts disabled.
+	 */
+	spin_lock_irqsave(&PORT.lock, flags);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, quot);
+
+	PORT.read_status_mask = PORT98->oe | PORT98->txemp | PORT98->dr;
+	if (termios->c_iflag & INPCK)
+		PORT.read_status_mask |= PORT98->fe | PORT98->pe;
+
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		PORT.read_status_mask |= PORT98->brk;
+	/*
+	 * Characteres to ignore
+	 */
+	PORT.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		PORT.ignore_status_mask |= PORT98->fe | PORT98->pe;
+
+	if (termios->c_iflag & IGNBRK) {
+		PORT.ignore_status_mask |= PORT98->brk;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			PORT.ignore_status_mask |= PORT98->oe;
+	}
+
+	/*
+	 * ignore all characters if CREAD is not set
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		PORT.ignore_status_mask |= PORT98->dr;
+
+	/*
+	 * CTS flow control flag and modem status interrupts
+	 */
+	if (PORT.flags & UPF_HARDPPS_CD)
+		ier |= 0x80;	/* enable modem status interrupt */
+	if (termios->c_cflag & CRTSCTS) {
+		ier |= 0x08;	/* enable CTS interrupt */
+		ier |= 0x80;	/* enable modem status interrupt */
+	}
+	if (!(termios->c_cflag & CLOCAL)) {
+		ier |= 0x20;	/* enable CD interrupt */
+		ier |= 0x80;	/* enable modem status interrupt */
+	}
+	PORT98->ier = ier;
+
+	PORT98->mode = cval;
+	serial98_mode_set(port);
+	if (PORT.type == PORT_VFAST_PC98 && quot <= 48) {
+		quot /= 4;
+		if (quot < 1)
+			quot = 1;
+		outb(quot | VFAST_ENABLE, VFAST_8251F);
+	} else {
+		quot /= 3;
+		if (quot < 1)
+			quot = 1;
+		if (PORT.type == PORT_VFAST_PC98)
+			outb(0, VFAST_8251F);		/* V.FAST mode off */
+		outb(0xb6, 0x77);
+		outb(quot & 0xff, 0x75);		/* LS of divisor */
+		outb(quot >> 8, 0x75);			/* MS of divisor */
+	}
+
+	if (fcr & UART_FCR_ENABLE_FIFO) {
+		outb(UART_FCR_ENABLE_FIFO, FCR_8251F);
+		outb(fcr, FCR_8251F);
+	}
+
+	/* enable RX/TX */
+	PORT98->cmd = 0x15;
+	serial98_cmd_out(port, PORT98->cmd);
+	PORT98->cmd = 0x05;
+	/* enable interrupts */
+	outb(0x00, IER2_8251F);
+	outb(ENA_RXR_INT, IER1_CTL);
+	spin_unlock_irqrestore(&PORT.lock, flags);
+}
+
+static const char *serial98_type(struct uart_port *port)
+{
+	char *p;
+
+	switch (PORT.type) {
+		case PORT_8251_PC98:
+			p = "PC98 onboard legacy 8251";
+			break;
+		case PORT_19K_PC98:
+			p =  "PC98 onboard max 19200bps";
+			break;
+		case PORT_FIFO_PC98:
+			p = "PC98 onboard with FIFO";
+			break;
+		case PORT_VFAST_PC98:
+			p = "PC98 onboard V.FAST";
+			break;
+		case PORT_PC9861:
+			p = "PC-9861K RS-232C ext. board";
+			break;
+		case PORT_PC9801_101:
+			p = "PC-9801-101 RS-232C ext. board";
+			break;
+		default:
+			return NULL;
+	}
+
+	sprintf(type_str, "%s  Clock %dMHz", p, serial98_clk);
+	return type_str;
+}
+
+/* Release the region(s) being used by 'port' */
+static void serial98_release_port(struct uart_port *port)
+{
+	switch (PORT.type) {
+		case PORT_VFAST_PC98:
+			release_region(PORT.iobase + 0xa, 1);
+		case PORT_FIFO_PC98:
+			release_region(PORT.iobase + 8, 1);
+			release_region(PORT.iobase + 6, 1);
+			release_region(PORT.iobase + 4, 1);
+			release_region(PORT.iobase + 2, 1);
+			release_region(PORT.iobase, 1);
+		case PORT_19K_PC98:
+			release_region(SERIAL98_EXT, 1);
+			release_region(0x34, 1);
+		case PORT_8251_PC98:
+			release_region(0x32, 1);
+			release_region(0x30, 1);
+	}
+}
+
+/* Request the region(s) being used by 'port' */
+#define REQ_REGION98(base) (request_region((base), 1, serial98_reg.driver_name))
+static int serial98_request_region(unsigned int type)
+{
+	if (!REQ_REGION98(0x30))
+		return -EBUSY;
+	if (REQ_REGION98(0x32)) {
+		if (type == PORT_8251_PC98)
+			return 0;
+		if (REQ_REGION98(0x34)) {
+			if (REQ_REGION98(SERIAL98_EXT)) {
+				unsigned long base;
+
+				if (type == PORT_19K_PC98)
+					return 0;
+				for (base = 0x130; base <= 0x138; base += 2) {
+					if (!REQ_REGION98(base)) {
+						base -= 2;
+						goto err;
+					}
+				}
+				if (type == PORT_FIFO_PC98)
+					return 0;
+				if (type == PORT_VFAST_PC98) {
+					if (REQ_REGION98(0x13a))
+						return 0;
+				}
+				err:
+				while (base >= 0x130) {
+					release_region(base, 1);
+					base -= 2;
+				}
+				release_region(SERIAL98_EXT, 1);
+			}
+			release_region(0x34, 1);
+		}
+		release_region(0x32, 1);
+	}
+	release_region(0x30, 1);
+	return -EBUSY;
+}
+
+static int serial98_request_port(struct uart_port *port)
+{
+	return serial98_request_region(PORT.type);
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void serial98_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE)
+		PORT.type = PORT98->type;
+}
+
+/*
+ * verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int serial98_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	switch (ser->type) {
+		case PORT_VFAST_PC98:
+		case PORT_FIFO_PC98:
+		case PORT_19K_PC98:
+		case PORT_8251_PC98:
+		/* not implemented yet
+		case PORT_PC9861:
+		case PORT_PC9801_101:
+		*/
+		case PORT_UNKNOWN:
+			break;
+		default:
+			return -EINVAL;
+	}
+	if (ser->irq < 0 || ser->irq >= NR_IRQS)
+		return -EINVAL;
+	if (ser->baud_base < 9600)
+		return -EINVAL;
+	return 0;
+}
+
+static struct uart_ops serial98_ops = {
+	.tx_empty	= serial98_tx_empty,
+	.set_mctrl	= serial98_set_mctrl,
+	.get_mctrl	= serial98_get_mctrl,
+	.stop_tx	= serial98_stop_tx,
+	.start_tx	= serial98_start_tx,
+	.stop_rx	= serial98_stop_rx,
+	.enable_ms	= serial98_enable_ms,
+	.break_ctl	= serial98_break_ctl,
+	.startup	= serial98_startup,
+	.shutdown	= serial98_shutdown,
+	.set_termios	= serial98_set_termios,
+	.type		= serial98_type,
+	.release_port	= serial98_release_port,
+	.request_port	= serial98_request_port,
+	.config_port	= serial98_config_port,
+	.verify_port	= serial98_verify_port,
+};
+
+static struct serial98_port serial98_ports[SERIAL98_NR] = {
+	{
+		.port =	{
+				.iobase		= 0x30,
+				.iotype		= SERIAL_IO_PORT,
+				.irq		= 4,
+				.fifosize	= 1,
+				.ops		= &serial98_ops,
+				.flags		= ASYNC_BOOT_AUTOCONF,
+				.line		= 0,
+			},
+		.rxchk = STAT_8251_RXRDY,
+		.txemp = STAT_8251_TXEMP,
+		.txrdy = STAT_8251_TXRDY,
+		.rxrdy = STAT_8251_RXRDY,
+		.brk = STAT_8251_BRK,
+		.fe = STAT_8251_FER,
+		.oe = STAT_8251_OER,
+		.pe = STAT_8251_PER,
+		.dr = STAT_8251_DSR,
+	},
+};
+
+#ifdef CONFIG_SERIAL98_CONSOLE
+
+#define BOTH_EMPTY (PORT98->txemp | PORT98->txrdy)
+
+/*
+ *	Wait for transmitter & holding register to empty
+ */
+static inline void wait_for_xmitr(struct uart_port *port)
+{
+	unsigned int status, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	do {
+		status = inb(PORT.iobase + 2);
+
+		if (status & PORT98->brk)
+			PORT98->lsr_break_flag = PORT98->brk;
+
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while ((status & BOTH_EMPTY) != BOTH_EMPTY);
+
+	/* Wait up to 1s for flow control if necessary */
+	if (PORT.flags & UPF_CONS_FLOW) {
+		tmout = 1000000;
+		while (--tmout &&
+		       ((serial98_msr_in(port) & UART_MSR_CTS) == 0))
+			udelay(1);
+	}
+}
+
+/*
+ *	Print a string to the serial port trying not to disturb
+ *	any possible real use of the port...
+ *
+ *	The console_lock must be held when we get here.
+ */
+static void
+serial98_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_port *port = (struct uart_port *)&serial98_ports[co->index];
+	unsigned int ier1, ier2;
+	int i;
+
+	/*
+	 *	First save the UER then disable the interrupts
+	 */
+	ier1 = inb(IER1_8251F);
+	ier2 = inb(IER2_8251F);
+	/* disnable all modem status interrupt */
+	outb(0x80, IER2_8251F);
+
+	/* disnable TX/RX interrupt */
+	outb(0x00, IER2_8251F);
+	outb(DIS_RXR_INT, IER1_CTL);
+	outb(DIS_TXE_INT, IER1_CTL);
+	outb(DIS_TXR_INT, IER1_CTL);
+
+	/*
+	 *	Now, do each character
+	 */
+	for (i = 0; i < count; i++, s++) {
+		wait_for_xmitr(port);
+
+		/*
+		 *	Send the character out.
+		 *	If a LF, also do CR...
+		 */
+		outb(*s, PORT.iobase);
+		if (*s == 10) {
+			wait_for_xmitr(port);
+			outb(13, PORT.iobase);
+		}
+	}
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore the IER
+	 */
+	wait_for_xmitr(port);
+
+	/* restore TX/RX interrupt */
+	outb(0x00, IER2_8251F);
+	if (ier1 & 0x01)
+		outb(ENA_RXR_INT, IER1_CTL);
+	if (ier1 & 0x02)
+		outb(ENA_TXE_INT, IER1_CTL);
+	if (ier1 & 0x04)
+		outb(ENA_TXR_INT, IER1_CTL);
+
+	/* restore modem status interrupt */
+	outb(ier2, IER2_8251F);
+}
+
+static kdev_t serial98_console_device(struct console *co)
+{
+	return mk_kdev(TTY_MAJOR, 64 + co->index);
+}
+
+static int __init serial98_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	/*
+	 * Check whether an invalid uart number has been specified, and
+	 * if so, search for the first available port that does have
+	 * console support.
+	 */
+	if (co->index >= SERIAL98_NR)
+		co->index = 0;
+	port = &serial98_ports[co->index].port;
+
+	/*
+	 * Temporary fix.
+	 */
+	spin_lock_init(&port->lock);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+void __init serial98_console_init(void)
+{
+	register_console(&serial98_console);
+}
+
+#endif /* CONFIG_SERIAL98_CONSOLE */
+
+
+static int __init serial98_init(void)
+{
+	int ret;
+	unsigned char iir1, iir2;
+
+	if (PC9800_8MHz_P()) {
+		serial98_clk = 8;
+		serial98_ports[0].port.uartclk = 374400 * 16;
+	} else {
+		serial98_clk = 5;
+		serial98_ports[0].port.uartclk = 460800 * 16;
+	}
+
+	printk(KERN_INFO "serial98: PC-9801 standard serial port driver Version 0.1alpha\n");
+	serial98_ports[0].type = PORT_8251_PC98;
+	/* Check FIFO exist */
+	iir1 = inb(IIR_8251F);
+	iir2 = inb(IIR_8251F);
+	if ((iir1 & 0x40) != (iir2 & 0x40) && (iir1 & 0x20) == (iir2 & 0x20)) {
+		serial98_ports[0].port.iobase = 0x130;
+		serial98_ports[0].port.fifosize = 16;
+		serial98_ports[0].rxchk = STAT_8251F_DSR;
+		serial98_ports[0].txemp = STAT_8251F_TXEMP;
+		serial98_ports[0].txrdy = STAT_8251F_TXRDY;
+		serial98_ports[0].rxrdy = STAT_8251F_RXRDY;
+		serial98_ports[0].brk = STAT_8251F_BRK;
+		serial98_ports[0].fe = STAT_8251F_FER;
+		serial98_ports[0].oe = STAT_8251F_OER;
+		serial98_ports[0].pe = STAT_8251F_PER;
+		serial98_ports[0].dr = STAT_8251F_DSR;
+
+		if (*(unsigned char*)__va(PC9821SCA_RSFLAGS) & 0x10)
+			serial98_ports[0].type = PORT_VFAST_PC98;
+		else {
+			outb(serial98_ports[0].ext | 0x40, SERIAL98_EXT);
+			serial98_ports[0].port.uartclk *= 4;
+			serial98_ports[0].type = PORT_FIFO_PC98;
+		}
+	} else if ((serial98_ports[0].ext = inb(SERIAL98_EXT)) != 0xff) {
+		outb(serial98_ports[0].ext | 0x40, SERIAL98_EXT);
+		if (inb(SERIAL98_EXT) == (serial98_ports[0].ext | 0x40)) {
+			serial98_ports[0].port.uartclk *= 4;
+			serial98_ports[0].type = PORT_19K_PC98;
+		} else {
+			serial98_ops.enable_ms = NULL;
+			outb(serial98_ports[0].ext, SERIAL98_EXT);
+		}
+	}
+
+	if (serial98_request_region(serial98_ports[0].type))
+		return -EBUSY;
+
+	ret = uart_register_driver(&serial98_reg);
+	if (ret == 0) {
+		int i;
+
+		for (i = 0; i < SERIAL98_NR; i++) {
+			uart_add_one_port(&serial98_reg,
+					(struct uart_port *)&serial98_ports[i]);
+		}
+	}
+
+	return ret;
+}
+
+static void __exit serial98_exit(void)
+{
+	int i;
+
+	if (serial98_ports[0].type == PORT_19K_PC98
+			|| serial98_ports[0].type == PORT_FIFO_PC98)
+		outb(serial98_ports[0].ext, SERIAL98_EXT);
+
+	for (i = 0; i < SERIAL98_NR; i++) {
+		uart_remove_one_port(&serial98_reg,
+					(struct uart_port *)&serial98_ports[i]);
+	}
+
+	uart_unregister_driver(&serial98_reg);
+}
+
+module_init(serial98_init);
+module_exit(serial98_exit);
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Osamu Tomita <tomita@cinet.co.jp>");
+MODULE_DESCRIPTION("PC-9801 standard serial port driver Version 0.1alpha");
+MODULE_LICENSE("GPL");
diff -Nru linux/include/asm-i386/serial.h linux98/include/asm-i386/serial.h
--- linux/include/asm-i386/serial.h	2003-02-15 08:52:44.000000000 +0900
+++ linux98/include/asm-i386/serial.h	2003-02-16 17:19:03.000000000 +0900
@@ -50,12 +50,19 @@
 
 #define C_P(card,port) (((card)<<6|(port)<<3) + 1)
 
+#ifndef CONFIG_X86_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_X86_PC9800 */
 
 
 #ifdef CONFIG_SERIAL_MANY_PORTS
diff -Nru linux/include/linux/serial_core.h linux98/include/linux/serial_core.h
--- linux/include/linux/serial_core.h	2003-01-09 13:03:55.000000000 +0900
+++ linux98/include/linux/serial_core.h	2003-01-10 10:53:38.000000000 +0900
@@ -58,6 +58,14 @@
 /* NEC v850.  */
 #define PORT_NB85E_UART	40
 
+/* NEC PC-9800 */
+#define PORT_8251_PC98	41
+#define PORT_19K_PC98	42
+#define PORT_FIFO_PC98	43
+#define PORT_VFAST_PC98	44
+#define PORT_PC9861	45
+#define PORT_PC9801_101	46
+
 
 #ifdef __KERNEL__
 

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (25/26) SMP
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (23 preceding siblings ...)
  2003-02-17 14:24 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (24/26) serial Osamu Tomita
@ 2003-02-17 14:25 ` Osamu Tomita
  2003-02-17 14:26 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (26/26) video card Osamu Tomita
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:25 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (25/26).

SMP support for PC98.

diff -Nru linux-2.5.61/arch/i386/kernel/io_apic.c linux98-2.5.61/arch/i386/kernel/io_apic.c
--- linux-2.5.61/arch/i386/kernel/io_apic.c	2003-02-15 08:51:26.000000000 +0900
+++ linux98-2.5.61/arch/i386/kernel/io_apic.c	2003-02-15 13:23:49.000000000 +0900
@@ -37,6 +37,7 @@
 #include <asm/desc.h>
 
 #include <mach_apic.h>
+#include <io_ports.h>
 
 #undef APIC_LOCKUP_DEBUG
 
@@ -668,7 +669,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))
 
@@ -762,6 +765,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;
@@ -796,6 +805,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");
@@ -865,6 +879,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");
@@ -926,6 +945,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;
@@ -2034,7 +2054,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 -Nru linux-2.5.61/arch/i386/kernel/mpparse.c linux98-2.5.61/arch/i386/kernel/mpparse.c
--- linux-2.5.61/arch/i386/kernel/mpparse.c	2003-02-15 08:51:26.000000000 +0900
+++ linux98-2.5.61/arch/i386/kernel/mpparse.c	2003-02-16 17:19:03.000000000 +0900
@@ -33,6 +33,7 @@
 
 #include <mach_apic.h>
 #include <mach_mpparse.h>
+#include <bios_ebda.h>
 
 /* Have we found an MP table */
 int smp_found_config;
@@ -209,6 +210,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);
 	}
@@ -651,7 +654,8 @@
 		 * Read the physical hardware table.  Anything here will
 		 * override the defaults.
 		 */
-		if (!smp_read_mpc((void *)mpf->mpf_physptr)) {
+		if (!smp_read_mpc(pc98 ? phys_to_virt(mpf->mpf_physptr)
+					: (void *)mpf->mpf_physptr)) {
 			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");
@@ -705,8 +709,23 @@
 			printk("found SMP MP-table at %08lx\n",
 						virt_to_phys(mpf));
 			reserve_bootmem(virt_to_phys(mpf), PAGE_SIZE);
-			if (mpf->mpf_physptr)
-				reserve_bootmem(mpf->mpf_physptr, PAGE_SIZE);
+			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.
+				 * 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.
+				 */
+				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);
+			}
+
 			mpf_found = mpf;
 			return 1;
 		}
@@ -749,11 +768,12 @@
 	 * MP1.4 SPEC states to only scan first 1K of 4K EBDA.
 	 */
 
-	address = *(unsigned short *)phys_to_virt(0x40E);
-	address <<= 4;
-	smp_scan_config(address, 0x400);
-	if (smp_found_config)
-		printk(KERN_WARNING "WARNING: MP table in the EBDA can be UNSAFE, contact linux-smp@vger.kernel.org if you experience SMP problems!\n");
+	address = get_bios_ebda();
+	if (address) {
+		smp_scan_config(address, 0x400);
+		if (smp_found_config)
+			printk(KERN_WARNING "WARNING: MP table in the EBDA can be UNSAFE, contact linux-smp@vger.kernel.org if you experience SMP problems!\n");
+	}
 }
 
 
diff -Nru linux-2.5.61/arch/i386/kernel/smpboot.c linux98-2.5.61/arch/i386/kernel/smpboot.c
--- linux-2.5.61/arch/i386/kernel/smpboot.c	2003-02-15 08:51:44.000000000 +0900
+++ linux98-2.5.61/arch/i386/kernel/smpboot.c	2003-02-15 15:04:28.000000000 +0900
@@ -823,13 +823,27 @@
 
 	store_NMI_vector(&nmi_high, &nmi_low);
 
+#ifndef CONFIG_X86_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_X86_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
 
 	/*
 	 * Starting actual IPI sequence...
diff -Nru linux/include/asm-i386/mach-default/bios_ebda.h linux98/include/asm-i386/mach-default/bios_ebda.h
--- linux/include/asm-i386/mach-default/bios_ebda.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/include/asm-i386/mach-default/bios_ebda.h	2002-12-18 22:40:38.000000000 +0900
@@ -0,0 +1,15 @@
+#ifndef _MACH_BIOS_EBDA_H
+#define _MACH_BIOS_EBDA_H
+
+/*
+ * there is a real-mode segmented pointer pointing to the
+ * 4K EBDA area at 0x40E.
+ */
+static inline unsigned int get_bios_ebda(void)
+{
+	unsigned int address = *(unsigned short *)phys_to_virt(0x40E);
+	address <<= 4;
+	return address;	/* 0 means none */
+}
+
+#endif /* _MACH_BIOS_EBDA_H */
diff -Nru linux/include/asm-i386/mach-pc9800/bios_ebda.h linux98/include/asm-i386/mach-pc9800/bios_ebda.h
--- linux/include/asm-i386/mach-pc9800/bios_ebda.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/include/asm-i386/mach-pc9800/bios_ebda.h	2002-12-18 22:49:59.000000000 +0900
@@ -0,0 +1,14 @@
+#ifndef _MACH_BIOS_EBDA_H
+#define _MACH_BIOS_EBDA_H
+
+/*
+ * PC-9800 has no EBDA.
+ * Its BIOS uses 0x40E for other purpose,
+ * Not pointer to 4K EBDA area.
+ */
+static inline unsigned int get_bios_ebda(void)
+{
+	return 0;	/* 0 means none */
+}
+
+#endif /* _MACH_BIOS_EBDA_H */
diff -Nru linux/include/asm-i386/mach-pc9800/mach_wakecpu.h linux98/include/asm-i386/mach-pc9800/mach_wakecpu.h
--- linux/include/asm-i386/mach-pc9800/mach_wakecpu.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/include/asm-i386/mach-pc9800/mach_wakecpu.h	2003-01-10 11:40:16.000000000 +0900
@@ -0,0 +1,45 @@
+#ifndef __ASM_MACH_WAKECPU_H
+#define __ASM_MACH_WAKECPU_H
+
+/* 
+ * This file copes with machines that wakeup secondary CPUs by the
+ * INIT, INIT, STARTUP sequence.
+ */
+
+#define WAKE_SECONDARY_VIA_INIT
+
+/*
+ * 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)
+
+#define boot_cpu_apicid boot_cpu_physical_apicid
+
+static inline void wait_for_init_deassert(atomic_t *deassert)
+{
+	while (!atomic_read(deassert));
+	return;
+}
+
+/* Nothing to do for most platforms, since cleared by the INIT cycle */
+static inline void smp_callin_clear_local_apic(void)
+{
+}
+
+static inline void store_NMI_vector(unsigned short *high, unsigned short *low)
+{
+}
+
+static inline void restore_NMI_vector(unsigned short *high, unsigned short *low)
+{
+}
+
+#if APIC_DEBUG
+ #define inquire_remote_apic(apicid) __inquire_remote_apic(apicid)
+#else
+ #define inquire_remote_apic(apicid) {}
+#endif
+
+#endif /* __ASM_MACH_WAKECPU_H */
diff -Nru linux/include/asm-i386/mach-pc9800/smpboot_hooks.h linux98/include/asm-i386/mach-pc9800/smpboot_hooks.h
--- linux/include/asm-i386/mach-pc9800/smpboot_hooks.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98/include/asm-i386/mach-pc9800/smpboot_hooks.h	2002-09-22 06:56:46.000000000 +0900
@@ -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();
+}

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCHSET] PC-9800 subarch. support for 2.5.61 (26/26) video card
  2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
                   ` (24 preceding siblings ...)
  2003-02-17 14:25 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (25/26) SMP Osamu Tomita
@ 2003-02-17 14:26 ` Osamu Tomita
  25 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 14:26 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: Alan Cox, James simmons

This is patchset to support NEC PC-9800 subarchitecture
against 2.5.61 (26/26).

PC98 standard video card text mode driver.

diff -Nru linux-2.5.60/drivers/video/console/Makefile linux98-2.5.60/drivers/video/console/Makefile
--- linux-2.5.60/drivers/video/console/Makefile	2003-02-11 03:38:30.000000000 +0900
+++ linux98-2.5.60/drivers/video/console/Makefile	2003-02-11 09:47:18.000000000 +0900
@@ -19,6 +19,7 @@
 # Each configuration option enables a list of files.
 
 obj-$(CONFIG_DUMMY_CONSOLE)       += dummycon.o
+obj-$(CONFIG_GDC_CONSOLE)         += gdccon.o
 obj-$(CONFIG_SGI_NEWPORT_CONSOLE) += newport_con.o
 obj-$(CONFIG_PROM_CONSOLE)        += promcon.o promcon_tbl.o
 obj-$(CONFIG_STI_CONSOLE)         += sticon.o sticore.o
diff -Nru linux-2.5.61/drivers/video/console/gdccon.c linux98-2.5.61/drivers/video/console/gdccon.c
--- linux-2.5.61/drivers/video/console/gdccon.c	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/drivers/video/console/gdccon.c	2003-02-16 17:19:03.000000000 +0900
@@ -0,0 +1,833 @@
+/*
+ * 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/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);
+\f
+/*
+ * We will follow Linus's indenting style...
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -Nru linux-2.5.61/include/asm-i386/gdc.h linux98-2.5.61/include/asm-i386/gdc.h
--- linux-2.5.61/include/asm-i386/gdc.h	1970-01-01 09:00:00.000000000 +0900
+++ linux98-2.5.61/include/asm-i386/gdc.h	2003-02-16 17:19:03.000000000 +0900
@@ -0,0 +1,231 @@
+/*
+ *  gdc.h - macro & inline functions for accessing GDC text-VRAM
+ *
+ *  Copyright (C) 1997-2002   Osamu Tomita <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\n\t"
+	"jz 2f\n\t"
+ /*	"cld\n\t"	kernel code now assumes DF = 0 any time */
+	"test%L0 %3,%0\n\t"
+	"jz 1f\n\t"
+	"stos%W2\n\t"
+	"dec%L1 %1\n"
+"1:	shr%L1 %1\n\t"
+	"rep\n\t"
+	"stos%L2\n\t"
+	"jnc 2f\n\t"
+	"stos%W2\n\t"
+	"rep\n\t"
+	"stos%W2\n"
+"2:"
+			      : "=D"(s), "=c"(count)
+			      : "a"((((u32) c) << 16) | c), "g"(2),
+			        "0"(s), "1"(count));
+#else
+	__asm__ __volatile__ (
+	"rep\n\t"
+	"stosw"
+			      : "=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\n\t"
+	"jz 2f\n\t"
+ /*	"cld\n\t"	*/
+	"test%L0 %3,%0\n\t"
+	"jz 1f\n\t"
+	"movs%W0\n\t"
+	"dec%L2 %2\n"
+"1:	shr%L2 %2\n\t"
+	"rep\n\t"
+	"movs%L0\n\t"
+	"jnc 2f\n\t"
+	"movs%W0\n"
+"2:"
+			      : "=D"(d), "=S"(s), "=c"(count)
+			      : "g"(2), "0"(d), "1"(s), "2"(count));
+#else
+	__asm__ __volatile__ (
+	"rep\n\t"
+	"movsw"
+			      : "=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\n\t"
+	"jz 2f\n\t"
+	"std\n\t"
+	"lea%L1 -4(%1,%3,2),%1\n\t"
+	"lea%L2 -4(%2,%3,2),%2\n\t"
+	"test%L1 %4,%1\n\t"
+	"jz 1f\n\t"
+	"mov%W0 2(%2),%0\n\t"
+	"sub%L2 %4,%2\n\t"
+	"dec%L3 %3\n\t"
+	"mov%W0 %0,2(%1)\n\t"
+	"sub%L1 %4,%1\n"
+"1:	shr%L3 %3\n\t"
+	"rep\n\t"
+	"movs%L0\n\t"
+	"jnc 3f\n\t"
+	"mov%W0 2(%2),%0\n\t"
+	"mov%W0 %0,2(%1)\n"
+"3:	cld\n"
+"2:"
+			      : "=r"(tmp), "=D"(d), "=S"(s), "=c"(count)
+			      : "g"(2), "1"(d), "2"(s), "3"(count));
+#else
+	__asm__ __volatile__ (
+	"std\n\t"
+	"rep\n\t"
+	"movsw\n\t"
+	"cld"
+			      : "=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\n\t"
+	"jz 2f\n\t"
+ /*	"cld\n\t"	*/
+	"test%L0 %3,%0\n\t"
+	"jz 1f\n\t"
+	"movs%W0\n\t"
+	"dec%L2 %2\n"
+"1:	shr%L2 %2\n\t"
+	"rep\n\t"
+	"movs%L0\n\t"
+	"jnc 2f\n\t"
+	"movs%W0\n"
+"2:"
+			      : "=D"(d), "=S"(s), "=c"(count)
+			      : "g"(2), "0"(d), "1"(s), "2"(count));
+#else
+	__asm__ __volatile__ (
+	"rep\n\t"
+	"movsw"
+			      : "=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_ */

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (3/26) mach-pc9800
  2003-02-17 13:51 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (3/26) mach-pc9800 Osamu Tomita
@ 2003-02-17 14:48   ` Sam Ravnborg
  2003-02-17 15:35     ` Osamu Tomita
  2003-02-18 10:39   ` Christoph Hellwig
  1 sibling, 1 reply; 42+ messages in thread
From: Sam Ravnborg @ 2003-02-17 14:48 UTC (permalink / raw)
  To: Osamu Tomita; +Cc: Linux Kernel Mailing List, Alan Cox

On Mon, Feb 17, 2003 at 10:51:37PM +0900, Osamu Tomita wrote:
> This is patchset to support NEC PC-9800 subarchitecture
> against 2.5.61 (3/26).
> 
> diff -Nru linux-2.5.61/arch/i386/mach-pc9800/Makefile linux98-2.5.61/arch/i386/mach-pc9800/Makefile
> --- linux-2.5.61/arch/i386/mach-pc9800/Makefile	1970-01-01 09:00:00.000000000 +0900
> +++ linux98-2.5.61/arch/i386/mach-pc9800/Makefile	2003-02-16 17:19:03.000000000 +0900
> @@ -0,0 +1,7 @@
> +#
> +# Makefile for the linux kernel.
> +#
> +
> +EXTRA_CFLAGS	+= -I../kernel

Is this really needed to make it compile?
Seems to be inherited from other Makefiles,
and I doubt it is needed.

	Sam


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (8/26) core
  2003-02-17 14:07 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (8/26) core Osamu Tomita
@ 2003-02-17 14:58   ` Sam Ravnborg
  0 siblings, 0 replies; 42+ messages in thread
From: Sam Ravnborg @ 2003-02-17 14:58 UTC (permalink / raw)
  To: Osamu Tomita; +Cc: Linux Kernel Mailing List, Alan Cox

On Mon, Feb 17, 2003 at 11:07:22PM +0900, Osamu Tomita wrote:
> This is patchset to support NEC PC-9800 subarchitecture
> against 2.5.61 (8/26).

Browsing the code resulted in a few comments:
this code does at least the following:
1) introducing more sensible names for some constants
2) Moving functionality to mach-default
3) Adding PC9800 functionality

I suggest splitting up the patch in more sensible chunks, and get 1) and 2) applied first.

	Sam

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (3/26) mach-pc9800
  2003-02-17 14:48   ` Sam Ravnborg
@ 2003-02-17 15:35     ` Osamu Tomita
  0 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-17 15:35 UTC (permalink / raw)
  To: Sam Ravnborg; +Cc: Linux Kernel Mailing List, Alan Cox

Sam Ravnborg wrote:
> 
> On Mon, Feb 17, 2003 at 10:51:37PM +0900, Osamu Tomita wrote:
> > This is patchset to support NEC PC-9800 subarchitecture
> > against 2.5.61 (3/26).
> >
> > diff -Nru linux-2.5.61/arch/i386/mach-pc9800/Makefile linux98-2.5.61/arch/i386/mach-pc9800/Makefile
> > --- linux-2.5.61/arch/i386/mach-pc9800/Makefile       1970-01-01 09:00:00.000000000 +0900
> > +++ linux98-2.5.61/arch/i386/mach-pc9800/Makefile     2003-02-16 17:19:03.000000000 +0900
> > @@ -0,0 +1,7 @@
> > +#
> > +# Makefile for the linux kernel.
> > +#
> > +
> > +EXTRA_CFLAGS += -I../kernel
> 
> Is this really needed to make it compile?
> Seems to be inherited from other Makefiles,
> and I doubt it is needed.
Thanks for the comment.
It is copy of mach-defaults/Makefile at all.
I think this is no need now too. But I guess, .c files in mach-* 
were placed in ../kernel originaly, so new comming file may be need
this in future??? Not sure.
-- 
Osamu Tomita

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC
  2003-02-17 14:15 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC Osamu Tomita
@ 2003-02-17 17:02   ` Jeff Garzik
  2003-02-17 18:06     ` Brian Gerst
  0 siblings, 1 reply; 42+ messages in thread
From: Jeff Garzik @ 2003-02-17 17:02 UTC (permalink / raw)
  To: Osamu Tomita
  Cc: Linux Kernel Mailing List, Alan Cox, 'Christoph Hellwig'

 > -#ifdef __ISAPNP__	
 > +#if defined(__ISAPNP__) && !defined(CONFIG_X86_PC9800)


I am curious, does PC9800 support ISAPNP at all?

Perhaps a dumb question, but I wonder if the above ifdef can be 
simplified by turning off ISAPNP on PC9800?


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC
  2003-02-17 17:02   ` Jeff Garzik
@ 2003-02-17 18:06     ` Brian Gerst
  2003-02-17 18:33       ` Jeff Garzik
  2003-02-17 19:02       ` YOSHIFUJI Hideaki / 吉藤英明
  0 siblings, 2 replies; 42+ messages in thread
From: Brian Gerst @ 2003-02-17 18:06 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: Osamu Tomita, Linux Kernel Mailing List, Alan Cox,
	'Christoph Hellwig'

Jeff Garzik wrote:
>  > -#ifdef __ISAPNP__   
>  > +#if defined(__ISAPNP__) && !defined(CONFIG_X86_PC9800)
> 
> 
> I am curious, does PC9800 support ISAPNP at all?
> 
> Perhaps a dumb question, but I wonder if the above ifdef can be 
> simplified by turning off ISAPNP on PC9800?

As long as the machine has ISA expansion slots, ISAPNP is possible.  It 
is a property of the card, not the system.

--
				Brian Gerst



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC
  2003-02-17 18:06     ` Brian Gerst
@ 2003-02-17 18:33       ` Jeff Garzik
  2003-02-17 19:02       ` YOSHIFUJI Hideaki / 吉藤英明
  1 sibling, 0 replies; 42+ messages in thread
From: Jeff Garzik @ 2003-02-17 18:33 UTC (permalink / raw)
  To: Brian Gerst
  Cc: Osamu Tomita, Linux Kernel Mailing List, Alan Cox,
	'Christoph Hellwig'

Brian Gerst wrote:
> Jeff Garzik wrote:
> 
>>  > -#ifdef __ISAPNP__    > +#if defined(__ISAPNP__) && 
>> !defined(CONFIG_X86_PC9800)
>>
>>
>> I am curious, does PC9800 support ISAPNP at all?
>>
>> Perhaps a dumb question, but I wonder if the above ifdef can be 
>> simplified by turning off ISAPNP on PC9800?
> 
> 
> As long as the machine has ISA expansion slots, ISAPNP is possible.  It 
> is a property of the card, not the system.


I know that, and that still didn't answer my question :)

	Jeff




^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC
  2003-02-17 18:06     ` Brian Gerst
  2003-02-17 18:33       ` Jeff Garzik
@ 2003-02-17 19:02       ` YOSHIFUJI Hideaki / 吉藤英明
  1 sibling, 0 replies; 42+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2003-02-17 19:02 UTC (permalink / raw)
  To: bgerst; +Cc: jgarzik, tomita, linux-kernel, alan, hch

In article <3E5124AC.80505@didntduck.org> (at Mon, 17 Feb 2003 13:06:36 -0500), Brian Gerst <bgerst@didntduck.org> says:

> >  > -#ifdef __ISAPNP__   
> >  > +#if defined(__ISAPNP__) && !defined(CONFIG_X86_PC9800)
:
> > Perhaps a dumb question, but I wonder if the above ifdef can be 
> > simplified by turning off ISAPNP on PC9800?
> 
> As long as the machine has ISA expansion slots, ISAPNP is possible.  It 
> is a property of the card, not the system.

Some C-bus (this is the name of the bus of the PC-9800 expansion slots) 
cards seems PnP-capable while PC-9800 series have no ISA slots as I remember.  
I don't know if Linux ISAPNP suport work well with them.

--yoshfuji

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (2/26) APM
  2003-02-17 13:49 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (2/26) APM Osamu Tomita
@ 2003-02-18 10:37   ` Christoph Hellwig
  0 siblings, 0 replies; 42+ messages in thread
From: Christoph Hellwig @ 2003-02-18 10:37 UTC (permalink / raw)
  To: Osamu Tomita; +Cc: Linux Kernel Mailing List, Alan Cox

On Mon, Feb 17, 2003 at 10:49:55PM +0900, Osamu Tomita wrote:
> +#include "io_ports.h"

Isn't this introduced in a later patch?  Please make sure your patchkit
never breaks the compile of the existing subarches when applied in order.

>  		"pushl %%edi\n\t"
>  		"pushl %%ebp\n\t"
> +#ifdef CONFIG_X86_PC9800
> +		"pushfl\n\t"
> +#endif
>  		"lcall *%%cs:apm_bios_entry\n\t"
>  		"setc %%al\n\t"
>  		"popl %%ebp\n\t"
> @@ -682,6 +687,9 @@
>  		__asm__ __volatile__(APM_DO_ZERO_SEGS
>  			"pushl %%edi\n\t"
>  			"pushl %%ebp\n\t"
> +#ifdef CONFIG_X86_PC9800
> +			"pushfl\n\t"
> +#endif

Maybe add a

#ifdef CONFIG_X86_PC9800
#define COND_PUSHFL	"pushfl\n\t"
#else
#define COND_PUSHFL	"pushfl\n\t"
#endif

to the top of this file and then use it?

> +#ifndef CONFIG_X86_PC9800

Once again please always use #ifdef instead of #ifndef where possible.


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (3/26) mach-pc9800
  2003-02-17 13:51 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (3/26) mach-pc9800 Osamu Tomita
  2003-02-17 14:48   ` Sam Ravnborg
@ 2003-02-18 10:39   ` Christoph Hellwig
  1 sibling, 0 replies; 42+ messages in thread
From: Christoph Hellwig @ 2003-02-18 10:39 UTC (permalink / raw)
  To: Osamu Tomita; +Cc: Linux Kernel Mailing List, Alan Cox

> +#ifdef CONFIG_NUMA
> +#include <linux/mmzone.h>
> +#include <asm/node.h>
> +#include <asm/memblk.h>

Are there NUMA PC98 machines?


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (5/26) char device
  2003-02-17 13:56 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (5/26) char device Osamu Tomita
@ 2003-02-18 10:45   ` Christoph Hellwig
  0 siblings, 0 replies; 42+ messages in thread
From: Christoph Hellwig @ 2003-02-18 10:45 UTC (permalink / raw)
  To: Osamu Tomita; +Cc: Linux Kernel Mailing List, Alan Cox

> +static struct lp_struct lp = {
> +	/* Following `TAG: INITIALIZER' notations are GNU CC extension. */

This comment doesn't make sense anymore :)

> +	.flags	= LP_EXIST | LP_ABORTOPEN,
> +	.chars	= LP_INIT_CHAR,
> +	.time	= LP_INIT_TIME,
> +	.wait	= LP_INIT_WAIT,
> +};
> +
> +static	int	dc1_check	= 0;
> +static spinlock_t lp_old98_lock = SPIN_LOCK_UNLOCKED;
> +
> +
> +#undef LP_OLD98_DEBUG
> +
> +#ifdef CONFIG_PC9800_OLDLP_CONSOLE
> +static struct console lp_old98_console;		/* defined later */
> +static __typeof__(lp_old98_console.flags) saved_console_flags;

Please directly use the actual type here.

> +#endif
> +
> +static DECLARE_WAIT_QUEUE_HEAD (lp_old98_waitq);
> +
> +static void lp_old98_timer_function(unsigned long data);

This prototype is superflous.

> +	__const_udelay(lp.wait * 4);

Why do you use __const_udelay instead of udelay?

> +#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    

Do you really need that compat code?  I don't think it makes much sense to
keep 2.0 code around in 2.5.

> +static int lp_old98_open(struct inode * inode, struct file * file)
> +{
> +	if (minor(inode->i_rdev) != 0)
> +		return -ENXIO;
> +
> +	if (!try_module_get(THIS_MODULE))
> +		return -EBUSY;

This is broken - the upper layer does this for you swhen you set the owner fild
of struct file_operations.

> +	module_put(THIS_MODULE);

Dito.

> +static struct file_operations lp_old98_fops = {
> +	.owner		= THIS_MODULE,

See, you already set it..

> +	.llseek		= no_llseek,
> +	.read		= NULL,

Remove this line.

> +	if (request_region(LP_PORT_DATA,   1, "lp_old98")) {
> +	    if (request_region(LP_PORT_STATUS, 1, "lp_old98")) {
> +		if (request_region(LP_PORT_STROBE, 1, "lp_old98")) {
> +		    if (request_region(LP_PORT_EXTMODE, 1, "lp_old98")) {
> +			if (register_chrdev(LP_MAJOR, "lp", &lp_old98_fops)) {

Using gotos for error handling here might make the code quite a bit more
readable :)


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (23/26) SCSI
  2003-02-17 14:21 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (23/26) SCSI Osamu Tomita
@ 2003-02-18 10:54   ` 'Christoph Hellwig'
  0 siblings, 0 replies; 42+ messages in thread
From: 'Christoph Hellwig' @ 2003-02-18 10:54 UTC (permalink / raw)
  To: Osamu Tomita
  Cc: Linux Kernel Mailing List, Alan Cox, 'Christoph Hellwig'

On Mon, Feb 17, 2003 at 11:21:23PM +0900, Osamu Tomita wrote:
> diff -Nru linux/drivers/scsi/pc980155.c linux98/drivers/scsi/pc980155.c
> --- linux/drivers/scsi/pc980155.c	1970-01-01 09:00:00.000000000 +0900
> +++ linux98/drivers/scsi/pc980155.c	2003-02-17 21:05:25.000000000 +0900
> @@ -0,0 +1,264 @@
> +#include <linux/kernel.h>
> +#include <linux/types.h>

no copyright statement?

> +#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 <linux/interrupt.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>

I doj't think you need this.

> +
> +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);

Please remove unused declarations.

> +
> +#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();
> +}

This whole file wants to be reformatted to follow Documentation/CodingStyle.

> +			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;
> +		}

You seems to sometimes miss some cleanup on failures.  cleanups gotos might
help :)

> +
> +		pc980155_host = scsi_register(tpnt, sizeof(struct WD33C93_hostdata));

This can return NULL and you need to handle that.

> +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");
> +}

So if it's not supported don't implement this entry point :)

> +
> +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;

This might be easier to read when rewritten into a while loop..

> +}
> +
> +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

no need for the ifdef module here.

> +    return 1;
> +}
> +
> +__setup("pc980155=", pc980155_setup);
> +
> +Scsi_Host_Template driver_template = SCSI_PC980155;

Please don't use the  SCSI_PC980155 macro - declare the struct members here
directly.

> +int pc98_bios_param(struct block_device *bdev, int *ip)

This should _not_ be in sd.c but the lowlevel driver.

> +static spinlock_t wd_lock = SPIN_LOCK_UNLOCKED;

Where is this used?


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (14/26) input
  2003-02-17 14:12 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (14/26) input Osamu Tomita
@ 2003-03-14  8:22   ` Vojtech Pavlik
  2003-03-15  6:09     ` Osamu Tomita
  0 siblings, 1 reply; 42+ messages in thread
From: Vojtech Pavlik @ 2003-03-14  8:22 UTC (permalink / raw)
  To: Osamu Tomita; +Cc: Linux Kernel Mailing List, Vojtech Pavlik, Alan Cox

On Mon, Feb 17, 2003 at 11:12:23PM +0900, Osamu Tomita wrote:

> This is patchset to support NEC PC-9800 subarchitecture
> against 2.5.61 (14/26).
> 
> Drivers for PC98 standard keyboard/mouse.

Thanks, applied.

-- 
Vojtech Pavlik
SuSE Labs

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (14/26) input
  2003-03-14  8:22   ` Vojtech Pavlik
@ 2003-03-15  6:09     ` Osamu Tomita
  2003-03-15  8:01       ` Vojtech Pavlik
  0 siblings, 1 reply; 42+ messages in thread
From: Osamu Tomita @ 2003-03-15  6:09 UTC (permalink / raw)
  To: Vojtech Pavlik; +Cc: Linux Kernel Mailing List, Alan Cox

Vojtech Pavlik wrote:
> 
> On Mon, Feb 17, 2003 at 11:12:23PM +0900, Osamu Tomita wrote:
> 
> > This is patchset to support NEC PC-9800 subarchitecture
> > against 2.5.61 (14/26).
> >
> > Drivers for PC98 standard keyboard/mouse.
> 
> Thanks, applied.
Thanks.
Please check my patch agaist 2.5.64 I posted. That has update
(only one line) to syncronize parameter change of interrupt
function.

Regards,
Osamu Tomita

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (14/26) input
  2003-03-15  6:09     ` Osamu Tomita
@ 2003-03-15  8:01       ` Vojtech Pavlik
  0 siblings, 0 replies; 42+ messages in thread
From: Vojtech Pavlik @ 2003-03-15  8:01 UTC (permalink / raw)
  To: Osamu Tomita; +Cc: Vojtech Pavlik, Linux Kernel Mailing List, Alan Cox

On Sat, Mar 15, 2003 at 03:09:20PM +0900, Osamu Tomita wrote:
> Vojtech Pavlik wrote:
> > 
> > On Mon, Feb 17, 2003 at 11:12:23PM +0900, Osamu Tomita wrote:
> > 
> > > This is patchset to support NEC PC-9800 subarchitecture
> > > against 2.5.61 (14/26).
> > >
> > > Drivers for PC98 standard keyboard/mouse.
> > 
> > Thanks, applied.
> Thanks.
> Please check my patch agaist 2.5.64 I posted. That has update
> (only one line) to syncronize parameter change of interrupt
> function.

Already there, too.

-- 
Vojtech Pavlik
SuSE Labs

^ permalink raw reply	[flat|nested] 42+ messages in thread

* RE: [PATCHSET] PC-9800 subarch. support for 2.5.61 (23/26) SCSI
@ 2003-02-20  5:37 Osamu Tomita
  0 siblings, 0 replies; 42+ messages in thread
From: Osamu Tomita @ 2003-02-20  5:37 UTC (permalink / raw)
  To: ''Christoph Hellwig' '
  Cc: 'Linux Kernel Mailing List ', 'Alan Cox '

Thanks for many advices about pc980155 driver.
I'm trying to cleanup it.

>> +int pc98_bios_param(struct block_device *bdev, int *ip)
> 
> This should _not_ be in sd.c but the lowlevel driver.
There is a specification for PC98 SCSI HA. So we can use this
function commonly across all HA (except for bad clones). I think, 
this is better than patching many drivers by same way. Is this a
bad idea?

>> +static spinlock_t wd_lock = SPIN_LOCK_UNLOCKED;
> 
> Where is this used?
I forgot to remove it.

Thanks,
Osamu Tomita


^ permalink raw reply	[flat|nested] 42+ messages in thread

end of thread, other threads:[~2003-03-15  7:50 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-02-17 13:43 [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary Osamu Tomita
2003-02-17 13:48 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (1/26) ALSA Osamu Tomita
2003-02-17 13:49 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (2/26) APM Osamu Tomita
2003-02-18 10:37   ` Christoph Hellwig
2003-02-17 13:51 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (3/26) mach-pc9800 Osamu Tomita
2003-02-17 14:48   ` Sam Ravnborg
2003-02-17 15:35     ` Osamu Tomita
2003-02-18 10:39   ` Christoph Hellwig
2003-02-17 13:54 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (4/26) boot Osamu Tomita
2003-02-17 13:56 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (5/26) char device Osamu Tomita
2003-02-18 10:45   ` Christoph Hellwig
2003-02-17 14:03 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (6/26) console Osamu Tomita
2003-02-17 14:06 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (7/26) misc core Osamu Tomita
2003-02-17 14:07 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (8/26) core Osamu Tomita
2003-02-17 14:58   ` Sam Ravnborg
2003-02-17 14:08 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (9/26) DMA Osamu Tomita
2003-02-17 14:09 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (10/26) floppy #1 Osamu Tomita
2003-02-17 14:09 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (11/26) floppy #2 Osamu Tomita
2003-02-17 14:10 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (12/26) FS Osamu Tomita
2003-02-17 14:11 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (13/26) IDE Osamu Tomita
2003-02-17 14:12 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (14/26) input Osamu Tomita
2003-03-14  8:22   ` Vojtech Pavlik
2003-03-15  6:09     ` Osamu Tomita
2003-03-15  8:01       ` Vojtech Pavlik
2003-02-17 14:13 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (15/26) kanji Osamu Tomita
2003-02-17 14:15 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC Osamu Tomita
2003-02-17 17:02   ` Jeff Garzik
2003-02-17 18:06     ` Brian Gerst
2003-02-17 18:33       ` Jeff Garzik
2003-02-17 19:02       ` YOSHIFUJI Hideaki / 吉藤英明
2003-02-17 14:16 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (17/26) parport Osamu Tomita
2003-02-17 14:17 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (18/26) PCI Osamu Tomita
2003-02-17 14:18 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (19/26) PCI BIOS Osamu Tomita
2003-02-17 14:18 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (20/26) PCMCIA Osamu Tomita
2003-02-17 14:19 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (21/26) PNP Osamu Tomita
2003-02-17 14:20 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (22/26) reboot Osamu Tomita
2003-02-17 14:21 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (23/26) SCSI Osamu Tomita
2003-02-18 10:54   ` 'Christoph Hellwig'
2003-02-17 14:24 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (24/26) serial Osamu Tomita
2003-02-17 14:25 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (25/26) SMP Osamu Tomita
2003-02-17 14:26 ` [PATCHSET] PC-9800 subarch. support for 2.5.61 (26/26) video card Osamu Tomita
2003-02-20  5:37 [PATCHSET] PC-9800 subarch. support for 2.5.61 (23/26) SCSI Osamu Tomita

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).