2003-02-17 13:35:13

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (0/26) summary

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


2003-02-17 13:43:10

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (3/26) mach-pc9800

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 <[email protected]>
+ */
+#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);

2003-02-17 13:40:46

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (1/26) ALSA

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 <[email protected]>
+ * Osamu Tomita <[email protected]>
+ * Takashi Iwai <[email protected]>
+ *
+ *
+ * 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 <[email protected]>");
+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)

2003-02-17 13:41:22

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (2/26) APM

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

2003-02-17 13:58:57

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (7/26) misc core

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 <[email protected]>,
+ * 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 <[email protected]>
+ * (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

2003-02-17 13:51:07

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (5/26) char device

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 <[email protected]>,
+ * 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 <[email protected]>");
+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 <[email protected]>
+ * 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 <[email protected]>
+ * 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 */

2003-02-17 13:57:04

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (6/26) console

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

2003-02-17 14:01:53

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (9/26) DMA

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


2003-02-17 13:48:45

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (4/26) boot

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
+ * ([email protected]) 03Oct92
+ *
+ * some changes and additional features by Christoph Niemann,
+ * March 1993/June 1994 ([email protected])
+ *
+ * add APM BIOS checking by Stephen Rothwell, May 1994
+ * ([email protected])
+ *
+ * High load stuff, initrd support and position independency
+ * by Hans Lermen & Werner Almesberger, February 1996
+ * <[email protected]>, <[email protected]>
+ *
+ * Video handling moved to video.S by Martin Mares, March 1996
+ * <[email protected]>
+ *
+ * Extended memory detection scheme retwiddled by [email protected] (david
+ * parsons) to avoid loadlin confusion, July 1997
+ *
+ * Transcribed from Intel (as86) -> AT&T (gas) by Chris Noe, May 1999.
+ * <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * New A20 code ported from SYSLINUX by H. Peter Anvin. AMD Elan bugfixes
+ * by Robert Schwebel, December 2001 <[email protected]>
+ *
+ * Heavily modified for NEC PC-9800 series by Kyoto University Microcomputer
+ * Club (KMC) Linux/98 project <[email protected]>, 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 <[email protected]>
+ *
+ * Based on the video.S for IBM PC:
+ * copyright (C) Martin Mares <[email protected]>
+ */
+
+/* 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

2003-02-17 14:01:08

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (8/26) core

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 <[email protected]>
+ */
+/* ------ 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 <[email protected]>
+ */
+#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 <[email protected]>
+ */
+#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 <[email protected]>
+ */
+#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 <[email protected]>
+ */
+#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 <[email protected]>
+ */
+
+/* ------ 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 <[email protected]>
+ */
+#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 <[email protected]>
+ */
+#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 <[email protected]>
+ */
+#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 <[email protected]>
+ */
+#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)));

2003-02-17 14:04:18

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (10/26) floppy #1

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 [email protected] (Lawrence Foard). Linus.
+ */
+
+/*
+ * Automatic floppy-detection and formatting written by Werner Almesberger
+ * ([email protected]), 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 ([email protected])
+ * 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 <[email protected]>
+ * - 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 <[email protected]>
+ * 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 <[email protected]> -- 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();
+}
+

2003-02-17 14:29:49

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC

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 <[email protected]>
- 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 */

2003-02-17 14:10:44

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (11/26) floppy #2

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

2003-02-17 14:40:36

by Sam Ravnborg

[permalink] [raw]
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (3/26) mach-pc9800

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

2003-02-17 14:40:37

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (18/26) PCI

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

2003-02-17 14:36:47

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (14/26) input

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 <[email protected]>");
+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 <[email protected]>");
+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 <[email protected]>
+ * 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 <[email protected]>");
+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

2003-02-17 14:13:11

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (22/26) reboot

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 <[email protected]>
+ */
+#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 <[email protected]>
+ */
+#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 */

2003-02-17 14:38:20

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (15/26) kanji

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;

2003-02-17 14:07:27

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (12/26) FS

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

2003-02-17 14:15:41

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (23/26) SCSI

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;


2003-02-17 14:18:24

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (25/26) SMP

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 [email protected] 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 [email protected] 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();
+}

2003-02-17 14:27:38

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (19/26) PCI BIOS

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 */

2003-02-17 14:13:10

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (21/26) PNP

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

2003-02-17 14:29:47

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (20/26) PCMCIA

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;

2003-02-17 14:07:27

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (13/26) IDE

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, [email protected], 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 ([email protected])
+ */
+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 {

2003-02-17 14:19:52

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (26/26) video card

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);
+
+/*
+ * 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 <[email protected]>
+ * 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_ */

2003-02-17 14:10:43

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (17/26) parport

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 ([email protected]) 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));
}

2003-02-17 14:18:23

by Osamu Tomita

[permalink] [raw]
Subject: [PATCHSET] PC-9800 subarch. support for 2.5.61 (24/26) serial

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 <[email protected]>
+ *
+ * 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 <[email protected]>");
+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__

2003-02-17 14:49:43

by Sam Ravnborg

[permalink] [raw]
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (8/26) core

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

2003-02-17 15:26:39

by Osamu Tomita

[permalink] [raw]
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (3/26) mach-pc9800

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

2003-02-17 16:53:20

by Jeff Garzik

[permalink] [raw]
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC

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

2003-02-17 17:59:32

by Brian Gerst

[permalink] [raw]
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC

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


2003-02-17 18:23:56

by Jeff Garzik

[permalink] [raw]
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC

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



Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC

In article <[email protected]> (at Mon, 17 Feb 2003 13:06:36 -0500), Brian Gerst <[email protected]> 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

2003-02-18 00:45:19

by Osamu Tomita

[permalink] [raw]
Subject: RE: [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC

Thanks for the comment.

-----Original Message-----
From: Jeff Garzik
To: Osamu Tomita
Cc: Linux Kernel Mailing List; Alan Cox; 'Christoph Hellwig'
Sent: 2003/02/18 2:02
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC

>> -#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?
PC98 has other PNP cards. So I think this is best way.
But most PNP cards for PC98 has hardware switch or jumper pin to
disable PNP feature. We can use them without PNP support.
Your idea is not bad.....

Thanks,
Osamu Tomita

2003-02-18 01:27:40

by Osamu Tomita

[permalink] [raw]
Subject: RE: [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC

Hi. Thanks for the comment.

-----Original Message-----
From: YOSHIFUJI Hideaki / $B5HF#1QL@(J
To: [email protected]
Cc: [email protected]; [email protected]; [email protected];
[email protected]; [email protected]
Sent: 2003/02/18 4:02
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (16/26) NIC

In article <[email protected]> (at Mon, 17 Feb 2003 13:06:36
-0500), Brian Gerst <[email protected]> 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.
C-Bus PNP feature is compatible with ISA PNP except for control port
address.
By changing them from 0x259/0x279 to 0xa59/0xa79, PNP works fine.

Thanks,
Osamu Tomita

2003-02-18 10:29:08

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (3/26) mach-pc9800

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

Are there NUMA PC98 machines?

2003-02-18 10:27:42

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (2/26) APM

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.

2003-02-18 10:35:49

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (5/26) char device

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

2003-02-18 10:44:57

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (23/26) SCSI

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?

2003-02-19 02:34:33

by Osamu Tomita

[permalink] [raw]
Subject: RE: [PATCHSET] PC-9800 subarch. support for 2.5.61 (2/26) APM

-----Original Message-----
From: Christoph Hellwig
To: Osamu Tomita
Cc: Linux Kernel Mailing List; Alan Cox
Sent: 2003/02/18 19:37
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (2/26) APM

> 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.

Of course, I never want impact on other architecture.
io_ports.h is in 'core patch (8/26)'.

>>
>> "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?
>
I think "#ifndef"s are clear and readable rather than macro definition
at this situation.

> +#ifndef CONFIG_X86_PC9800
>
> Once again please always use #ifdef instead of #ifndef where possible.
I see.

Thanks,
Osamu Tomita

2003-02-20 05:27:50

by Osamu Tomita

[permalink] [raw]
Subject: RE: [PATCHSET] PC-9800 subarch. support for 2.5.61 (23/26) SCSI

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

2003-03-14 08:12:27

by Vojtech Pavlik

[permalink] [raw]
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (14/26) input

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

2003-03-15 05:59:23

by Osamu Tomita

[permalink] [raw]
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (14/26) input

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

2003-03-15 07:50:43

by Vojtech Pavlik

[permalink] [raw]
Subject: Re: [PATCHSET] PC-9800 subarch. support for 2.5.61 (14/26) input

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