Commit b1d53f0c authored by Linus Torvalds's avatar Linus Torvalds

This removes the files orphaned by the earlier PC9800 removal

parent bffc8f71
#
# 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
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) $(build)=$(obj)/compressed IMAGE_OFFSET=$(IMAGE_OFFSET) $@
# Set this if you want to pass append arguments to the zdisk/fdimage kernel
FDARGS =
$(obj)/mtools.conf: $(src)/mtools.conf.in
sed -e 's|@OBJ@|$(obj)|g' < $< > $@
# This requires write access to /dev/fd0
zdisk: $(BOOTIMAGE) $(obj)/mtools.conf
MTOOLSRC=$(obj)/mtools.conf mformat a: ; sync
syslinux /dev/fd0 ; sync
echo 'default linux $(FDARGS)' | \
MTOOLSRC=$(src)/mtools.conf mcopy - a:syslinux.cfg
MTOOLSRC=$(obj)/mtools.conf mcopy $(BOOTIMAGE) a:linux ; sync
# These require being root or having syslinux 2.02 or higher installed
fdimage fdimage144: $(BOOTIMAGE) $(obj)/mtools.conf
dd if=/dev/zero of=$(obj)/fdimage bs=1024 count=1440
MTOOLSRC=$(obj)/mtools.conf mformat v: ; sync
syslinux $(obj)/fdimage ; sync
echo 'default linux $(FDARGS)' | \
MTOOLSRC=$(obj)/mtools.conf mcopy - v:syslinux.cfg
MTOOLSRC=$(obj)/mtools.conf mcopy $(BOOTIMAGE) v:linux ; sync
fdimage288: $(BOOTIMAGE) $(obj)/mtools.conf
dd if=/dev/zero of=$(obj)/fdimage bs=1024 count=2880
MTOOLSRC=$(obj)/mtools.conf mformat w: ; sync
syslinux $(obj)/fdimage ; sync
echo 'default linux $(FDARGS)' | \
MTOOLSRC=$(obj)/mtools.conf mcopy - w:syslinux.cfg
MTOOLSRC=$(obj)/mtools.conf mcopy $(BOOTIMAGE) w:linux ; sync
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)"
/*
* 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
#!/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
#
# mtools configuration file for "make (b)zdisk"
#
# Actual floppy drive
drive a:
file="/dev/fd0"
# 1.44 MB floppy disk image
drive v:
file="@OBJ@/fdimage" cylinders=80 heads=2 sectors=18 filter
# 2.88 MB floppy disk image (mostly for virtual uses)
drive w:
file="@OBJ@/fdimage" cylinders=80 heads=2 sectors=36 filter
/*
* setup.S Copyright (C) 1991, 1992 Linus Torvalds
*
* setup.s is responsible for getting the system data from the BIOS,
* and putting them into the appropriate places in system memory.
* both setup.s and system has been loaded by the bootblock.
*
* This code asks the bios for memory/disk/other parameters, and
* puts them in a "safe" place: 0x90000-0x901FF, ie where the
* boot-block used to be. It is then up to the protected mode
* system to read them from there before the area is overwritten
* for buffer-blocks.
*
* Move PS/2 aux init code to psaux.c
* (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92
*
* some changes and additional features by Christoph Niemann,
* March 1993/June 1994 (Christoph.Niemann@linux.org)
*
* add APM BIOS checking by Stephen Rothwell, May 1994
* (sfr@canb.auug.org.au)
*
* High load stuff, initrd support and position independency
* by Hans Lermen & Werner Almesberger, February 1996
* <lermen@elserv.ffm.fgan.de>, <almesber@lrc.epfl.ch>
*
* Video handling moved to video.S by Martin Mares, March 1996
* <mj@k332.feld.cvut.cz>
*
* Extended memory detection scheme retwiddled by orc@pell.chi.il.us (david
* parsons) to avoid loadlin confusion, July 1997
*
* Transcribed from Intel (as86) -> AT&T (gas) by Chris Noe, May 1999.
* <stiker@northlink.com>
*
* Fix to work around buggy BIOSes which dont use carry bit correctly
* and/or report extended memory in CX/DX for e801h memory size detection
* call. As a result the kernel got wrong figures. The int15/e801h docs
* from Ralf Brown interrupt list seem to indicate AX/BX should be used
* anyway. So to avoid breaking many machines (presumably there was a reason
* to orginally use CX/DX instead of AX/BX), we do a kludge to see
* if CX/DX have been changed in the e801 call and if so use AX/BX .
* Michael Miller, April 2001 <michaelm@mjmm.org>
*
* New A20 code ported from SYSLINUX by H. Peter Anvin. AMD Elan bugfixes
* by Robert Schwebel, December 2001 <robert@schwebel.de>
*
* Heavily modified for NEC PC-9800 series by Kyoto University Microcomputer
* Club (KMC) Linux/98 project <seraphim@kmc.kyoto-u.ac.jp>, 1997-1999
*/
#include <linux/config.h>
#include <asm/segment.h>
#include <linux/version.h>
#include <linux/compile.h>
#include <asm/boot.h>
#include <asm/e820.h>
#include <asm/page.h>
/* Signature words to ensure LILO loaded us right */
#define SIG1 0xAA55
#define SIG2 0x5A5A
#define HIRESO_TEXT 0xe000
#define NORMAL_TEXT 0xa000
#define BIOS_FLAG2 0x0400
#define BIOS_FLAG5 0x0458
#define RDISK_EQUIP 0x0488
#define BIOS_FLAG 0x0501
#define KB_SHFT_STS 0x053a
#define DISK_EQUIP 0x055c
INITSEG = DEF_INITSEG # 0x9000, we move boot here, out of the way
SYSSEG = DEF_SYSSEG # 0x1000, system loaded at 0x10000 (65536).
SETUPSEG = DEF_SETUPSEG # 0x9020, this is the current segment
# ... and the former contents of CS
DELTA_INITSEG = SETUPSEG - INITSEG # 0x0020
.code16
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
start:
jmp trampoline
# This is the setup header, and it must start at %cs:2 (old 0x9020:2)
.ascii "HdrS" # header signature
.word 0x0203 # header version number (>= 0x0105)
# or else old loadlin-1.5 will fail)
realmode_swtch: .word 0, 0 # default_switch, SETUPSEG
start_sys_seg: .word SYSSEG
.word kernel_version # pointing to kernel version string
# above section of header is compatible
# with loadlin-1.5 (header v1.5). Don't
# change it.
type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin,
# Bootlin, SYSLX, bootsect...)
# See Documentation/i386/boot.txt for
# assigned ids
# flags, unused bits must be zero (RFU) bit within loadflags
loadflags:
LOADED_HIGH = 1 # If set, the kernel is loaded high
CAN_USE_HEAP = 0x80 # If set, the loader also has set
# heap_end_ptr to tell how much
# space behind setup.S can be used for
# heap purposes.
# Only the loader knows what is free
#ifndef __BIG_KERNEL__
.byte 0
#else
.byte LOADED_HIGH
#endif
setup_move_size: .word 0x8000 # size to move, when setup is not
# loaded at 0x90000. We will move setup
# to 0x90000 then just before jumping
# into the kernel. However, only the
# loader knows how much data behind
# us also needs to be loaded.
code32_start: # here loaders can put a different
# start address for 32-bit code.
#ifndef __BIG_KERNEL__
.long 0x1000 # 0x1000 = default for zImage
#else
.long 0x100000 # 0x100000 = default for big kernel
#endif
ramdisk_image: .long 0 # address of loaded ramdisk image
# Here the loader puts the 32-bit
# address where it loaded the image.
# This only will be read by the kernel.
ramdisk_size: .long 0 # its size in bytes
bootsect_kludge:
.long 0 # obsolete
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 does not 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 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:
/* video.S
*
* Video mode setup, etc. for NEC PC-9800 series.
*
* Copyright (C) 1997,98,99 Linux/98 project <seraphim@kmc.kyoto-u.ac.jp>
*
* Based on the video.S for IBM PC:
* copyright (C) Martin Mares <mj@atrey.karlin.mff.cuni.cz>
*/
/* Positions of various video parameters passed to the kernel */
/* (see also include/linux/tty.h) */
#define PARAM_CURSOR_POS 0x00
#define PARAM_VIDEO_PAGE 0x04
#define PARAM_VIDEO_MODE 0x06
#define PARAM_VIDEO_COLS 0x07
#define PARAM_VIDEO_EGA_BX 0x0a
#define PARAM_VIDEO_LINES 0x0e
#define PARAM_HAVE_VGA 0x0f
#define PARAM_FONT_POINTS 0x10
#define PARAM_VIDEO98_COMPAT 0x0a
#define PARAM_VIDEO98_HIRESO 0x0b
#define PARAM_VIDEO98_MACHTYPE 0x0c
#define PARAM_VIDEO98_LINES 0x0e
#define PARAM_VIDEO98_COLS 0x0f
# PARAM_LFB_* and PARAM_VESAPM_* are unused on PC-9800.
# This is the main entry point called by setup.S
# %ds *must* be pointing to the bootsector
video: xorw %ax, %ax
movw %ax, %es # %es = 0
movb %es:BIOS_FLAG, %al
movb %al, PARAM_VIDEO_MODE
movb $0, PARAM_VIDEO98_HIRESO # 0 = normal
movw $NORMAL_TEXT, PARAM_VIDEO_PAGE
testb $0x8, %al
movw $(80 * 256 + 25), %ax
jz 1f
# hireso machine.
movb $1, PARAM_VIDEO98_HIRESO # !0 = hi-reso
movb $(HIRESO_TEXT >> 8), PARAM_VIDEO_PAGE + 1
movw $(80 * 256 + 31), %ax
1: movw %ax, PARAM_VIDEO98_LINES # also sets VIDEO98_COLS
movb $0xc0, %ch # 400-line graphic mode
movb $0x42, %ah
int $0x18
movw $80, PARAM_VIDEO_COLS
movw $msg_probing, %si
call prtstr_cs
# Check vendor from font pattern of `A'...
1: inb $0x60, %al # wait V-sync
testb $0x20, %al
jnz 1b
2: inb $0x60, %al
testb $0x20, %al
jz 2b
movb $0x00, %al # select font of `A'
outb %al, $0xa1
movb $0x41, %al
outb %al, $0xa3
movw $8, %cx
movw PARAM_VIDEO_PAGE, %ax
cmpw $NORMAL_TEXT, %ax
je 3f
movb $24, %cl # for hi-reso machine
3: addw $0x400, %ax # %ax = CG window segment
pushw %ds
movw %ax, %ds
xorw %dx, %dx # get sum of `A' pattern...
xorw %si, %si
4: lodsw
addw %ax, %dx
loop 4b
popw %ds
movw %dx, %ax
movw $msg_nec, %si
xorw %bx, %bx # vendor info will go into %bx
testb $8, %es:BIOS_FLAG
jnz check_hireso_vendor
cmpw $0xc7f8, %ax
je 5f
jmp 6f
check_hireso_vendor:
cmpw $0x9639, %ax # XXX: NOT VERIFIED!!!
je 5f
6: incw %bx # compatible machine
movw $msg_compat, %si
5: movb %bl, PARAM_VIDEO98_COMPAT
call prtstr_cs
movw $msg_fontdata, %si
call prtstr_cs # " (CG sum of A = 0x"
movw %dx, %ax
call prthex
call prtstr_cs # ") PC-98"
movb $'0', %al
pushw %ds
pushw $0xf8e8
popw %ds
cmpw $0x2198, (0)
popw %ds
jne 7f
movb $'2', %al
7: call prtchr
call prtstr_cs # "1 "
movb $0, PARAM_VIDEO98_MACHTYPE
#if 0 /* XXX - This check is bogus? [0000:BIOS_FLAG2]-bit7 does NOT
indicate whether it is a note machine, but merely indicates
whether it has ``RAM drive''. */
# check note machine
testb $0x80, %es:BIOS_FLAG2
jnz is_note
pushw %ds
pushw $0xfd80
popw %ds
movb (4), %al
popw %ds
cmpb $0x20, %al # EPSON note A
je epson_note
cmpb $0x22, %al # EPSON note W
je epson_note
cmpb $0x27, %al # EPSON note AE
je epson_note
cmpb $0x2a, %al # EPSON note WR
jne note_done
epson_note:
movb $1, PARAM_VIDEO98_MACHTYPE
movw $msg_note, %si
call prtstr_cs
note_done:
#endif
# print h98 ? (only NEC)
cmpb $0, PARAM_VIDEO98_COMPAT
jnz 8f # not NEC -> not H98
testb $0x80, %es:BIOS_FLAG5
jz 8f # have NESA bus -> H98
movw $msg_h98, %si
call prtstr_cs
orb $2, PARAM_VIDEO98_MACHTYPE
8: testb $0x40, %es:BIOS_FLAG5
jz 9f
movw $msg_gs, %si
call prtstr_cs # only prints it :-)
9:
movw $msg_normal, %si # "normal"
testb $0x8, %es:BIOS_FLAG
jz 1f
movw $msg_hireso, %si
1: call prtstr_cs
movw $msg_sysclk, %si
call prtstr_cs
movb $'5', %al
testb $0x80, %es:BIOS_FLAG
jz 2f
movb $'8', %al
2: call prtchr
call prtstr_cs
#if 0
testb $0x40, %es:(0x45c)
jz no_30line # no 30-line support
movb %es:KB_SHFT_STS, %al
testb $0x01, %al # is SHIFT key pressed?
jz no_30line
testb $0x10, %al # is CTRL key pressed?
jnz line40
# switch to 30-line mode
movb $30, PARAM_VIDEO98_LINES
movw $msg_30line, %si
jmp 3f
line40:
movb $37, PARAM_VIDEO98_LINES
movw $40, PARAM_VIDEO_LINES
movw $msg_40line, %si
3: call prtstr_cs
movb $0x32, %bh
movw $0x300c, %ax
int $0x18 # switch video mode
movb $0x0c, %ah
int $0x18 # turn on text plane
movw %cs:cursor_address, %dx
movb $0x13, %ah
int $0x18 # move cursor to correct place
mov $0x11, %ah
int $0x18 # turn on text plane
call prtstr_cs # "Ok.\r\n"
no_30line:
#endif
ret
prtstr_cs:
pushw %ds
pushw %cs
popw %ds
call prtstr
popw %ds
ret
# prthex is for debugging purposes, and prints %ax in hexadecimal.
prthex: pushw %cx
movw $4, %cx
1: rolw $4, %ax
pushw %ax
andb $0xf, %al
cmpb $10, %al
sbbb $0x69, %al
das
call prtchr
popw %ax
loop 1b
popw %cx
ret
msg_probing: .string "Probing machine: "
msg_nec: .string "NEC"
msg_compat: .string "compatible"
msg_fontdata: .string " (CG sum of A = 0x"
.string ") PC-98"
.string "1 "
msg_gs: .string "(GS) "
msg_h98: .string "(H98) "
msg_normal: .string "normal"
msg_hireso: .string "Hi-reso"
msg_sysclk: .string " mode, system clock "
.string "MHz\r\n"
#if 0
msg_40line: # cpp will concat following lines, so the assembler can deal.
.ascii "\
Video mode will be adjusted to 37-line (so-called ``40-line'') mode later.\r\n\
THIS MODE MAY DAMAGE YOUR MONITOR PHYSICALLY. USE AT YOUR OWN RISK.\r\n"
msg_30line: .string "Switching video mode to 30-line (640x480) mode... "
.string "Ok.\r\n"
#endif
#
# Makefile for the linux kernel.
#
obj-y := setup.o topology.o std_resources.o
/*
* Machine specific setup for pc9800
*/
#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 Architecture) 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
/*
* Machine specific resource allocation for PC-9800.
* Written by Osamu Tomita <tomita@cinet.co.jp>
*/
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/std_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 }
};
void __init probe_roms(void)
{
int i;
__u8 *xrom_id;
int roms = 1;
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;
}
}
}
void __init request_graphics_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);
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);
}
}
void __init request_standard_io_resources(void)
{
int i;
for (i = 0; i < STANDARD_IO_RESOURCES; i++)
request_resource(&ioport_resource, standard_io_resources+i);
}
/*
* arch/i386/mach-pc9800/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.
*
* Modify for PC-9800 by Osamu Tomita <tomita@cinet.co.jp>
*
*/
#include <linux/init.h>
#include <linux/smp.h>
#include <asm/cpu.h>
struct i386_cpu cpu_devices[NR_CPUS];
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;
}
subsys_initcall(topology_init);
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* linux/drivers/char/lp_old98.c
*
* printer port driver for ancient PC-9800s with no bidirectional port support
*
* Copyright (C) 1998,99 Kousuke Takai <tak@kmc.kyoto-u.ac.jp>,
* Kyoto University Microcomputer Club
*
* This driver is based on and has compatibility with `lp.c',
* generic PC printer port driver.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/console.h>
#include <linux/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 = {
.flags = LP_EXIST | LP_ABORTOPEN,
.chars = LP_INIT_CHAR,
.time = LP_INIT_TIME,
.wait = LP_INIT_WAIT,
};
static int dc1_check;
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 short saved_console_flags;
#endif
static DECLARE_WAIT_QUEUE_HEAD (lp_old98_waitq);
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
ndelay(lp.wait);
/* negate PSTB# (activate strobe) */
outb(LP_CONTROL_ASSERT_STROBE, LP_PORT_CONTROL);
#ifdef LP_STATS
lp.stats.meanwait = (255 * lp.stats.meanwait + count + 128) / 256;
lp.stats.mdev = (127 * lp.stats.mdev + tmp + 64) / 128;
lp.stats.chars ++;
#endif
ndelay(lp.wait);
/* assert PSTB# (deactivate strobe) */
outb(LP_CONTROL_NEGATE_STROBE, LP_PORT_CONTROL);
return 1;
}
static ssize_t lp_old98_write(struct file * file,
const char * buf, size_t count,
loff_t *dummy)
{
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 (iminor(inode) != 0)
return -ENXIO;
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
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,
.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 struct console lp_old98_console = {
.name = "lp_old98",
.write = lp_old98_console_write,
.flags = CON_PRINTBUFFER,
.index = -1,
};
#endif /* console on lp_old98 */
static int __init lp_old98_init(void)
{
char *errmsg = "I/O ports already occupied, giving up.";
#ifdef PC98_HW_H98
if (pc98_hw_flags & PC98_HW_H98)
if (!request_region(LP_PORT_H98MODE, 1, "lp_old98")
goto err1;
#endif
if (!request_region(LP_PORT_DATA, 1, "lp_old98"))
goto err2;
if (!request_region(LP_PORT_STATUS, 1, "lp_old98"))
goto err3;
if (!request_region(LP_PORT_STROBE, 1, "lp_old98"))
goto err4;
if (!request_region(LP_PORT_EXTMODE, 1, "lp_old98"))
goto err5;
if (!register_chrdev(LP_MAJOR, "lp", &lp_old98_fops)) {
#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;
} else
errmsg = "unable to register device";
release_region(LP_PORT_EXTMODE, 1);
err5:
release_region(LP_PORT_STROBE, 1);
err4:
release_region(LP_PORT_STATUS, 1);
err3:
release_region(LP_PORT_DATA, 1);
err2:
#ifdef PC98_HW_H98
if (pc98_hw_flags & PC98_HW_H98)
release_region(LP_PORT_H98MODE, 1);
err1:
#endif
printk(KERN_ERR "lp_old98: %s\n", errmsg);
return -EBUSY;
}
static void __exit lp_old98_exit(void)
{
#ifdef CONFIG_PC9800_OLDLP_CONSOLE
unregister_console(&lp_old98_console);
#endif
unregister_chrdev(LP_MAJOR, "lp");
release_region(LP_PORT_DATA, 1);
release_region(LP_PORT_STATUS, 1);
release_region(LP_PORT_STROBE, 1);
#ifdef PC98_HW_H98
if (pc98_hw_flags & PC98_HW_H98)
release_region(LP_PORT_H98MODE, 1);
#endif
release_region(LP_PORT_EXTMODE, 1);
}
#ifndef MODULE
static int __init lp_old98_setup(char *str)
{
int ints[4];
str = get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] > 0)
dc1_check = ints[1];
return 1;
}
__setup("lp_old98_dc1_check=", lp_old98_setup);
#endif
MODULE_PARM(dc1_check, "i");
MODULE_AUTHOR("Kousuke Takai <tak@kmc.kyoto-u.ac.jp>");
MODULE_DESCRIPTION("PC-9800 old printer port driver");
MODULE_LICENSE("GPL");
module_init(lp_old98_init);
module_exit(lp_old98_exit);
/*
* 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/bcd.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>
/*
* 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 = BCD2BIN(raw.sec);
wtime.tm_min = BCD2BIN(raw.min);
wtime.tm_hour = BCD2BIN(raw.hour);
wtime.tm_mday = BCD2BIN(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 = BCD2BIN(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 = BIN2BCD(wtime.tm_sec);
raw.min = BIN2BCD(wtime.tm_min);
raw.hour = BIN2BCD(wtime.tm_hour);
raw.mday = BIN2BCD(wtime.tm_mday);
raw.mon = wtime.tm_mon + 1;
raw.wday = wtime.tm_wday;
raw.year = BIN2BCD(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,
.read = rtc_read,
.poll = rtc_poll,
.ioctl = rtc_ioctl,
.open = rtc_open,
.release = rtc_release,
.fasync = rtc_fasync,
};
static struct miscdevice rtc_dev=
{
.minor = RTC_MINOR,
.name = "rtc",
.fops = &rtc_fops,
};
static int __init rtc_init(void)
{
int err = 0;
if (!request_region(UPD4990A_IO, 1, "rtc")) {
printk(KERN_ERR "upd4990a: could not acquire I/O port %#x\n",
UPD4990A_IO);
return -EBUSY;
}
err = misc_register(&rtc_dev);
if (err) {
printk(KERN_ERR "upd4990a: can't misc_register() on minor=%d\n",
RTC_MINOR);
release_region(UPD4990A_IO, 1);
return err;
}
#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
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);
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);
/*
* 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 = BCD2BIN(data.year) + 1900) < 1995)
year += 100;
p += sprintf(p,
"rtc_time\t: %02d:%02d:%02d\n"
"rtc_date\t: %04d-%02d-%02d\n",
BCD2BIN(data.hour), BCD2BIN(data.min),
BCD2BIN(data.sec),
year, data.mon, BCD2BIN(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;
}
/*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* This is the low-level hd interrupt support. It traverses the
* request-list, using interrupts to jump between functions. As
* all the functions are called within interrupts, we may not
* sleep. Special care is recommended.
*
* modified by Drew Eckhardt to check nr of hd's from the CMOS.
*
* Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
* in the early extended-partition checks and added DM partitions
*
* IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
* and general streamlining by Mark Lord.
*
* Removed 99% of above. Use Mark's ide driver for those options.
* This is now a lightweight ST-506 driver. (Paul Gortmaker)
*
* Modified 1995 Russell King for ARM processor.
*
* Bugfix: max_sectors must be <= 255 or the wheels tend to come
* off in a hurry once you queue things up - Paul G. 02/2001
*/
/* Uncomment the following if you want verbose error reports. */
/* #define VERBOSE_ERRORS */
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/genhd.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/mc146818rtc.h> /* CMOS defines */
#include <linux/init.h>
#include <linux/blkpg.h>
#include <linux/hdreg.h>
#define REALLY_SLOW_IO
#include <asm/system.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#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;
static struct request_queue *hd_queue;
#define CURRENT elv_next_request(hd_queue)
#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 int reset;
static int hd_error;
/*
* This struct defines the HD's and their types.
*/
struct hd_i_struct {
unsigned int head,sect,cyl,wpcom,lzone,ctl;
int unit;
int recalibrate;
int special_op;
};
#ifdef HD_TYPE
static struct hd_i_struct hd_info[] = { HD_TYPE };
static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct)));
#else
static 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 *name = CURRENT ?
CURRENT->rq_dev->bd_disk->disk_name :
"hd?";
#ifdef VERBOSE_ERRORS
printk("%s: %s: status=0x%02x { ", name, 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("%s: %s: error=0x%02x { ", name, 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 (CURRENT)
printk(", sector=%ld", CURRENT->sector);
}
printk("\n");
}
#else
printk("%s: %s: status=0x%02x.\n", name, msg, stat & 0xff);
if ((stat & ERR_STAT) == 0) {
hd_error = 0;
} else {
hd_error = inb(HD_ERROR);
printk("%s: %s: error=0x%02x.\n", name, 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(struct hd_i_struct *disk,
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(disk->unit, head)) {
reset = 1;
return;
}
SET_HANDLER(intr_addr);
outb(disk->ctl,HD_CMD);
port=HD_DATA + 2;
outb(disk->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|(disk->unit<<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) {
struct hd_i_struct *disk = &hd_info[i];
disk->special_op = disk->recalibrate = 1;
hd_out(disk, disk->sect, disk->sect, disk->head-1,
disk->cyl, WIN_SPECIFY, &reset_hd);
if (reset)
goto repeat;
} else
hd_request();
}
/*
* Ok, don't know what to do with the unexpected interrupts: on some machines
* doing a reset and a retry seems to result in an eternal loop. Right now I
* ignore it, and just set the timeout.
*
* On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
* drive enters "idle", "standby", or "sleep" mode, so if the status looks
* "good", we just ignore the interrupt completely.
*/
void unexpected_hd_interrupt(void)
{
unsigned int stat = inb(HD_STATUS);
if (stat & (BUSY_STAT|DRQ_STAT|ECC_STAT|ERR_STAT)) {
dump_status ("unexpected interrupt", stat);
SET_TIMER;
}
}
/*
* bad_rw_intr() now tries to be a bit smarter and does things
* according to the error returned by the controller.
* -Mika Liljeberg (liljeber@cs.Helsinki.FI)
*/
static void bad_rw_intr(void)
{
struct request *req = CURRENT;
struct hd_i_struct *disk;
if (!req)
return;
disk = req->rq_disk->private_data;
if (++req->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
end_request(req, 0);
disk->special_op = disk->recalibrate = 1;
} else if (req->errors % RESET_FREQ == 0)
reset = 1;
else if ((hd_error & TRK0_ERR) || req->errors % RECAL_FREQ == 0)
disk->special_op = disk->recalibrate = 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;
struct request *req;
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:
req = CURRENT;
insw(HD_DATA,req->buffer,256);
req->sector++;
req->buffer += 512;
req->errors = 0;
i = --req->nr_sectors;
--req->current_nr_sectors;
#ifdef DEBUG
printk("%s: read: sector %ld, remaining = %ld, buffer=%p\n",
req->rq_disk->disk_name, req->sector, req->nr_sectors,
req->buffer+512);
#endif
if (req->current_nr_sectors <= 0)
end_request(req, 1);
if (i > 0) {
SET_HANDLER(&read_intr);
return;
}
(void) inb(HD_STATUS);
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
if (CURRENT)
hd_request();
return;
}
static void write_intr(void)
{
int i;
int retries = 100000;
struct request *req = CURRENT;
do {
i = (unsigned) inb(HD_STATUS);
if (i & BUSY_STAT)
continue;
if (!OK_STATUS(i))
break;
if ((req->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:
req->sector++;
i = --req->nr_sectors;
--req->current_nr_sectors;
req->buffer += 512;
if (!i || (req->bio && req->current_nr_sectors < 1))
end_request(req, 1);
if (i > 0) {
SET_HANDLER(&write_intr);
outsw(HD_DATA,req->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)
{
do_hd = NULL;
if (!CURRENT)
return;
disable_irq(HD_IRQ);
local_irq_enable();
reset = 1;
printk("%s: timeout\n", CURRENT->rq_disk->disk_name);
if (++CURRENT->errors >= MAX_ERRORS) {
#ifdef DEBUG
printk("%s: too many errors\n", CURRENT->rq_disk->disk_name);
#endif
end_request(CURRENT, 0);
}
local_irq_disable();
hd_request();
enable_irq(HD_IRQ);
}
int do_special_op(struct hd_i_struct *disk, struct request *req)
{
if (disk->recalibrate) {
disk->recalibrate = 0;
hd_out(disk, disk->sect,0,0,0,WIN_RESTORE,&recal_intr);
return reset;
}
if (disk->head > 16) {
printk ("%s: cannot handle device with more than 16 heads - giving up\n", req->rq_disk->disk_name);
end_request(req, 0);
}
disk->special_op = 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 block, nsect, sec, track, head, cyl;
struct hd_i_struct *disk;
struct request *req;
if (do_hd)
return;
repeat:
del_timer(&device_timer);
local_irq_enable();
if (!CURRENT) {
do_hd = NULL;
return;
}
req = CURRENT;
if (reset) {
local_irq_disable();
reset_hd();
return;
}
disk = req->rq_disk->private_data;
block = req->sector;
nsect = req->nr_sectors;
if (block >= get_capacity(req->rq_disk) ||
((block+nsect) > get_capacity(req->rq_disk))) {
printk("%s: bad access: block=%d, count=%d\n",
req->rq_disk->disk_name, block, nsect);
end_request(req, 0);
goto repeat;
}
if (disk->special_op) {
if (do_special_op(disk, req))
goto repeat;
return;
}
sec = block % disk->sect + 1;
track = block / disk->sect;
head = track % disk->head;
cyl = track / disk->head;
#ifdef DEBUG
printk("%s: %sing: CHS=%d/%d/%d, sectors=%d, buffer=%p\n",
req->rq_disk->disk_name, (req->cmd == READ)?"read":"writ",
cyl, head, sec, nsect, req->buffer);
#endif
if (req->flags & REQ_CMD) {
switch (rq_data_dir(req)) {
case READ:
hd_out(disk,nsect,sec,head,cyl,WIN_READ,&read_intr);
if (reset)
goto repeat;
break;
case WRITE:
hd_out(disk,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
if (reset)
goto repeat;
if (wait_DRQ()) {
bad_rw_intr();
goto repeat;
}
outsw(HD_DATA,req->buffer,256);
break;
default:
printk("unknown hd-command\n");
end_request(req, 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_i_struct *disk = inode->i_bdev->bd_disk->private_data;
struct hd_geometry *loc = (struct hd_geometry *) arg;
struct hd_geometry g;
if (cmd != HDIO_GETGEO)
return -EINVAL;
if (!loc)
return -EINVAL;
g.heads = disk->head;
g.sectors = disk->sect;
g.cylinders = disk->cyl;
g.start = get_start_sect(inode->i_bdev);
return copy_to_user(loc, &g, sizeof g) ? -EFAULT : 0;
}
/*
* Releasing a block device means we sync() it, so that it can safely
* be forgotten about...
*/
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 = {
.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(HD_MAJOR,"hd")) {
printk("hd: unable to get major %d for hard disk\n",HD_MAJOR);
return -1;
}
hd_queue = blk_init_queue(do_hd_request, &hd_lock);
if (!hd_queue) {
unregister_blkdev(HD_MAJOR,"hd");
return -1;
}
blk_queue_max_sectors(hd_queue, 255);
init_timer(&device_timer);
device_timer.function = hd_times_out;
blk_queue_hardsect_size(hd_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(64);
struct hd_i_struct *p = &hd_info[drive];
if (!disk)
goto Enomem;
disk->major = HD_MAJOR;
disk->first_minor = drive << 6;
disk->fops = &hd_fops;
sprintf(disk->disk_name, "hd%c", 'a'+drive);
disk->private_data = p;
set_capacity(disk, p->head * p->sect * p->cyl);
disk->queue = hd_queue;
p->unit = drive;
hd_gendisk[drive] = disk;
printk ("%s: %luMB, CHS=%d/%d/%d\n",
disk->disk_name, (unsigned long)get_capacity(disk)/2048,
p->cyl, p->head, p->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++)
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(HD_MAJOR,"hd");
blk_cleanup_queue(hd_queue);
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);
/*
* 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
#undef PC9800_IDE_DEBUG
static void pc9800_select(ide_drive_t *drive)
{
#ifdef PC9800_IDE_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)
{
u8 saved_bank;
if (!PC9800_9821_P() /* || !PC9821_IDEIF_DOUBLE_P() */)
return;
if (!request_region(PC9800_IDE_BANKSELECT, 1, "ide0/1 bank")) {
printk(KERN_ERR
"ide: bank select port (%#x) is already occupied!\n",
PC9800_IDE_BANKSELECT);
return;
}
/* Do actual probing. */
if ((saved_bank = inb(PC9800_IDE_BANKSELECT)) == (u8) ~0
|| (outb(saved_bank ^ 1, PC9800_IDE_BANKSELECT),
/* Next outb is dummy for reading status. */
outb(0x80, PC9800_IDE_BANKSELECT),
inb(PC9800_IDE_BANKSELECT) != (saved_bank ^ 1))) {
printk(KERN_INFO
"ide: pc9800 type bank selecting port not found\n");
release_region(PC9800_IDE_BANKSELECT, 1);
return;
}
/* Restore original value, just in case. */
outb(saved_bank, PC9800_IDE_BANKSELECT);
/* These ports are reseved by IDE I/F. */
if (!request_region(0x430, 1, "ide") ||
!request_region(0x435, 1, "ide")) {
printk(KERN_WARNING
"ide: IO port 0x430 and 0x435 are reserved for IDE"
" the card using these ports may not work\n");
}
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;
}
}
/*
* drivers/input/keyboard/98kbd.c
*
* PC-9801 keyboard driver for Linux
*
* Based on atkbd.c and xtkbd.c written by Vojtech Pavlik
*
* Copyright (c) 2002 Osamu Tomita
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <asm/io.h>
#include <asm/pc9800.h>
MODULE_AUTHOR("Osamu Tomita <tomita@cinet.co.jp>");
MODULE_DESCRIPTION("PC-9801 keyboard driver");
MODULE_LICENSE("GPL");
#define KBD98_KEY 0x7f
#define KBD98_RELEASE 0x80
static unsigned char kbd98_keycode[256] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 43, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 41, 26, 28, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40, 27, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, 12, 57, 92,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,121,
83, 94, 87, 88,183,184,185, 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];
};
irqreturn_t kbd98_interrupt(struct serio *serio, unsigned char data,
unsigned int flags, struct pt_regs *regs)
{
struct kbd98 *kbd98 = serio->private;
unsigned char scancode, keycode;
int press, i;
switch (data) {
case KBD98_RET_ACK:
kbd98->ack = 1;
goto out;
case KBD98_RET_NAK:
kbd98->ack = -1;
goto out;
}
if (kbd98->cmdcnt) {
kbd98->cmdbuf[--kbd98->cmdcnt] = data;
goto out;
}
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)
break;
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);
break;
case KEY_CAPSLOCK:
input_report_key(&kbd98->dev, keycode, 1);
input_sync(&kbd98->dev);
input_report_key(&kbd98->dev, keycode, 0);
input_sync(&kbd98->dev);
break;
case KBD98_KEY_NULL:
break;
case 0:
printk(KERN_WARNING "kbd98.c: Unknown key (scancode %#x) %s.\n",
data & KBD98_KEY, data & KBD98_RELEASE ? "released" : "pressed");
break;
default:
input_report_key(&kbd98->dev, keycode, press);
input_sync(&kbd98->dev);
break;
}
out:
return IRQ_HANDLED;
}
/*
* 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; /* Always 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);
/*
* PC-9800 Speaker beeper driver for Linux
*
* Copyright (c) 2002 Osamu Tomita
* Copyright (c) 2002 Vojtech Pavlik
* Copyright (c) 1992 Orest Zborowski
*
*/
/*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <asm/8253pit.h>
#include <asm/io.h>
MODULE_AUTHOR("Osamu Tomita <tomita@cinet.co.jp>");
MODULE_DESCRIPTION("PC-9800 Speaker beeper driver");
MODULE_LICENSE("GPL");
static char spkr98_name[] = "PC-9801 Speaker";
static char spkr98_phys[] = "isa3fdb/input0";
static struct input_dev spkr98_dev;
spinlock_t i8253_beep_lock = SPIN_LOCK_UNLOCKED;
static int spkr98_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
unsigned int count = 0;
unsigned long flags;
if (type != EV_SND)
return -1;
switch (code) {
case SND_BELL: if (value) value = 1000;
case SND_TONE: break;
default: return -1;
}
if (value > 20 && value < 32767)
count = PIT_TICK_RATE / value;
spin_lock_irqsave(&i8253_beep_lock, flags);
if (count) {
outb(0x76, 0x3fdf);
outb(0, 0x5f);
outb(count & 0xff, 0x3fdb);
outb(0, 0x5f);
outb((count >> 8) & 0xff, 0x3fdb);
/* beep on */
outb(6, 0x37);
} else {
/* beep off */
outb(7, 0x37);
}
spin_unlock_irqrestore(&i8253_beep_lock, flags);
return 0;
}
static int __init spkr98_init(void)
{
spkr98_dev.evbit[0] = BIT(EV_SND);
spkr98_dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
spkr98_dev.event = spkr98_event;
spkr98_dev.name = spkr98_name;
spkr98_dev.phys = spkr98_phys;
spkr98_dev.id.bustype = BUS_ISA;
spkr98_dev.id.vendor = 0x001f;
spkr98_dev.id.product = 0x0001;
spkr98_dev.id.version = 0x0100;
input_register_device(&spkr98_dev);
printk(KERN_INFO "input: %s\n", spkr98_name);
return 0;
}
static void __exit spkr98_exit(void)
{
input_unregister_device(&spkr98_dev);
}
module_init(spkr98_init);
module_exit(spkr98_exit);
/*
*
* 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/moduleparam.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/irq.h>
MODULE_AUTHOR("Osamu Tomita <tomita@cinet.co.jp>");
MODULE_DESCRIPTION("PC-9801 busmouse driver");
MODULE_LICENSE("GPL");
#define PC98BM_BASE 0x7fd9
#define PC98BM_DATA_PORT PC98BM_BASE + 0
/* PC98BM_SIGNATURE_PORT does not exist */
#define PC98BM_CONTROL_PORT PC98BM_BASE + 4
/* PC98BM_INTERRUPT_PORT does not exist */
#define PC98BM_CONFIG_PORT PC98BM_BASE + 6
#define PC98BM_ENABLE_IRQ 0x00
#define PC98BM_DISABLE_IRQ 0x10
#define PC98BM_READ_X_LOW 0x80
#define PC98BM_READ_X_HIGH 0xa0
#define PC98BM_READ_Y_LOW 0xc0
#define PC98BM_READ_Y_HIGH 0xe0
#define PC98BM_DEFAULT_MODE 0x93
/* PC98BM_CONFIG_BYTE is not used */
/* PC98BM_SIGNATURE_BYTE is not used */
#define PC98BM_TIMER_PORT 0xbfdb
#define PC98BM_DEFAULT_TIMER_VAL 0x00
#define PC98BM_IRQ 13
static int pc98bm_irq = PC98BM_IRQ;
module_param_named(irq, pc98bm_irq, uint, 0);
MODULE_PARM_DESC(irq, "IRQ number (13=default)");
__obsolete_setup("pc98bm_irq=");
static int pc98bm_used = 0;
static irqreturn_t 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 irqreturn_t 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);
return IRQ_HANDLED;
}
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);
/*
* NEC PC-9801 keyboard controller driver for Linux
*
* Copyright (c) 1999-2002 Osamu Tomita <tomita@cinet.co.jp>
* Based on i8042.c written by Vojtech Pavlik
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/config.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/sched.h>
#include <asm/io.h>
MODULE_AUTHOR("Osamu Tomita <tomita@cinet.co.jp>");
MODULE_DESCRIPTION("NEC PC-9801 keyboard controller driver");
MODULE_LICENSE("GPL");
/*
* Names.
*/
#define KBD98_PHYS_DESC "isa0041/serio0"
/*
* IRQs.
*/
#define KBD98_IRQ 1
/*
* Register numbers.
*/
#define KBD98_COMMAND_REG 0x43
#define KBD98_STATUS_REG 0x43
#define KBD98_DATA_REG 0x41
spinlock_t kbd98io_lock = SPIN_LOCK_UNLOCKED;
static struct serio kbd98_port;
extern struct pt_regs *kbd_pt_regs;
static irqreturn_t 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 irqreturn_t 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);
return IRQ_HANDLED;
}
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);
/*
ne2k_cbus.c: A driver for the NE2000 like ethernet on NEC PC-9800.
This is a copy of the 2.5.66 Linux ISA NE2000 driver "ne.c"
(Donald Becker/Paul Gortmaker) with the NEC PC-9800 specific
changes added by Osamu Tomita.
From ne.c:
-----------
Copyright 1993 United States Government as represented by the
Director, National Security Agency.
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
-----------
*/
/* Routines for the NatSemi-based designs (NE[12]000). */
static const char version[] =
"ne2k_cbus.c:v1.0 3/24/03 Osamu Tomita\n";
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/isapnp.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <asm/system.h>
#include <asm/io.h>
#include "8390.h"
/* Some defines that people can play with if so inclined. */
/* Do we support clones that don't adhere to 14,15 of the SAprom ? */
#define SUPPORT_NE_BAD_CLONES
/* Do we perform extra sanity checks on stuff ? */
/* #define NE_SANITY_CHECK */
/* Do we implement the read before write bugfix ? */
/* #define NE_RW_BUGFIX */
/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
/* #define PACKETBUF_MEMSIZE 0x40 */
#ifdef SUPPORT_NE_BAD_CLONES
/* A list of bad clones that we none-the-less recognize. */
static struct { const char *name8, *name16; unsigned char SAprefix[4];}
bad_clone_list[] __initdata = {
{"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
/* ---- No user-serviceable parts below ---- */
#define NE_BASE (dev->base_addr)
#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 */
#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */
#define NESM_START_PG 0x40 /* First page of TX buffer */
#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
#include "ne2k_cbus.h"
static int ne_probe1(struct net_device *dev, int ioaddr);
static int ne_open(struct net_device *dev);
static int ne_close(struct net_device *dev);
static void ne_reset_8390(struct net_device *dev);
static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
int ring_page);
static void ne_block_input(struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset);
static void ne_block_output(struct net_device *dev, const int count,
const unsigned char *buf, const int start_page);
/* Probe for various non-shared-memory ethercards.
NEx000-clone boards have a Station Address PROM (SAPROM) in the packet
buffer memory space. NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of
the SAPROM, while other supposed NE2000 clones must be detected by their
SA prefix.
Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
mode results in doubled values, which can be detected and compensated for.
The probe is also responsible for initializing the card and filling
in the 'dev' and 'ei_status' structures.
We use the minimum memory size for some ethercard product lines, iff we can't
distinguish models. You can increase the packet buffer size by setting
PACKETBUF_MEMSIZE. Reported Cabletron packet buffer locations are:
E1010 starts at 0x100 and ends at 0x2000.
E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
E2010 starts at 0x100 and ends at 0x4000.
E2010-x starts at 0x100 and ends at 0xffff. */
static int __init do_ne_probe(struct net_device *dev)
{
unsigned int base_addr = dev->base_addr;
int irq = dev->irq;
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, irq);
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++)
if (ne_probe_cbus(dev, hw, *plist, irq) == 0)
return 0;
} else {
for (hw = &ne2k_cbus_hwinfo_list[0]; hw->hwtype; hw++) {
const unsigned short *plist;
for (plist = hw->portlist; *plist; plist++)
if (ne_probe_cbus(dev, hw, *plist, irq) == 0)
return 0;
}
}
}
#endif
ne2k_cbus_destroy(dev);
return -ENODEV;
}
static void cleanup_card(struct net_device *dev)
{
const struct ne2k_cbus_region *rlist;
const struct ne2k_cbus_hwinfo *hw = ne2k_cbus_get_hwinfo((int)(dev->mem_start & NE2K_CBUS_HARDWARE_TYPE_MASK));
free_irq(dev->irq, dev);
for (rlist = hw->regionlist; rlist->range; rlist++) {
release_region(dev->base_addr + rlist->start,
rlist->range);
}
ne2k_cbus_destroy(dev);
}
struct net_device * __init ne_probe(int unit)
{
struct net_device *dev = alloc_ei_netdev();
int err;
if (!dev)
return ERR_PTR(-ENOMEM);
sprintf(dev->name, "eth%d", unit);
netdev_boot_setup_check(dev);
err = do_ne_probe(dev);
if (err)
goto out;
err = register_netdev(dev);
if (err)
goto out1;
return dev;
out1:
cleanup_card(dev);
out:
free_netdev(dev);
return ERR_PTR(err);
}
static int __init ne_probe_cbus(struct net_device *dev, const struct ne2k_cbus_hwinfo *hw, int ioaddr, int irq)
{
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);
dev->irq = irq;
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);
dev->irq = irq;
if (ne_probe1(dev, ioaddr) == 0)
return 0;
}
}
return -ENODEV;
}
static int __init ne_probe1(struct net_device *dev, int ioaddr)
{
int i;
unsigned char SA_prom[32];
int wordlength = 2;
const char *name = NULL;
int start_page, stop_page;
int neX000, bad_card;
int reg0, ret;
static unsigned version_printed;
const struct ne2k_cbus_region *rlist;
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);
#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
for (rlist = hw->regionlist; rlist->range; rlist++)
if (!request_region(ioaddr + rlist->start,
rlist->range, dev->name)) {
ret = -EBUSY;
goto err_out;
}
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 + 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 + EI_SHIFT(0x0d)); /* Restore the old values. */
ret = -ENODEV;
goto err_out;
}
}
if (ei_debug && version_printed++ == 0)
printk(KERN_INFO "%s", version);
printk(KERN_INFO "NE*000 ethercard probe at %#3x:", ioaddr);
/* A user with a poor card that fails to ack the reset, or that
does not have a valid 0x57,0x57 signature can still use this
without having to recompile. Specifying an i/o address along
with an otherwise unused dev->mem_end value of "0xBAD" will
cause the driver to skip these parts of the probe. */
bad_card = ((dev->base_addr != 0) && (dev->mem_end == 0xbad));
/* Reset card. Who knows what dain-bramaged state it was left in. */
{
unsigned long reset_start_time = jiffies;
/* derived from CNET98EL-patch for bad clones */
outb_p(E8390_NODMA | E8390_STOP, ioaddr + E8390_CMD);
/* DON'T change these to inb_p/outb_p or reset will fail on clones. */
outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
while ((inb_p(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
if (jiffies - reset_start_time > 2*HZ/100) {
if (bad_card) {
printk(" (warning: no reset ack)");
break;
} else {
printk(" not found (no reset ack).\n");
ret = -ENODEV;
goto err_out;
}
}
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 */
ret = -ENODEV;
i = inb(ioaddr);
if ((i & ~0x2) != (0x20 | 0x01))
goto err_out;
if ((inb(ioaddr + 0x7) & 0x80) != 0x80)
goto err_out;
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);
goto err_out;
}
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");
goto err_out;
}
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; unsigned short offset;} program_seq[] =
{
{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
/* 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},
{0x00, EN0_RCNTLO}, /* Clear the count regs. */
{0x00, EN0_RCNTHI},
{0x00, EN0_IMR}, /* Mask completion irq. */
{0xFF, EN0_ISR},
{E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */
{E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
{32, EN0_RCNTLO},
{0x00, EN0_RCNTHI},
{0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
{0x00, EN0_RSARHI},
{E8390_RREAD+E8390_START, E8390_CMD},
};
for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
insw(ioaddr + NE_DATAPORT, SA_prom, 32 >> 1);
}
if (wordlength == 2)
{
for (i = 0; i < 16; i++)
SA_prom[i] = SA_prom[i+i];
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);
if (neX000) {
name = "C-Bus-NE2K-compat";
}
else
{
#ifdef SUPPORT_NE_BAD_CLONES
/* Ack! Well, there might be a *bad* NE*000 clone there.
Check for total bogus addresses. */
for (i = 0; bad_clone_list[i].name8; i++)
{
if (SA_prom[0] == bad_clone_list[i].SAprefix[0] &&
SA_prom[1] == bad_clone_list[i].SAprefix[1] &&
SA_prom[2] == bad_clone_list[i].SAprefix[2])
{
if (wordlength == 2)
{
name = bad_clone_list[i].name16;
} else {
name = bad_clone_list[i].name8;
}
break;
}
}
if (bad_clone_list[i].name8 == NULL)
{
printk(" not found (invalid signature %2.2x %2.2x).\n",
SA_prom[14], SA_prom[15]);
ret = -ENXIO;
goto err_out;
}
#else
printk(" not found.\n");
ret = -ENXIO;
goto err_out;
#endif
}
if (dev->irq < 2)
{
unsigned long cookie = probe_irq_on();
outb_p(0x50, ioaddr + EN0_IMR); /* Enable one interrupt. */
outb_p(0x00, ioaddr + EN0_RCNTLO);
outb_p(0x00, ioaddr + EN0_RCNTHI);
outb_p(E8390_RREAD+E8390_START, ioaddr); /* Trigger it... */
mdelay(10); /* wait 10ms for interrupt to propagate */
outb_p(0x00, ioaddr + EN0_IMR); /* Mask it again. */
dev->irq = probe_irq_off(cookie);
if (ei_debug > 2)
printk(" autoirq is %d\n", dev->irq);
} 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;
if (! dev->irq) {
printk(" failed to detect IRQ line.\n");
ret = -EAGAIN;
goto err_out;
}
/* Snarf the interrupt now. There's no point in waiting since we cannot
share and the board will usually be enabled. */
ret = request_irq(dev->irq, ei_interrupt, 0, name, dev);
if (ret) {
printk (" unable to get IRQ %d (errno=%d).\n", dev->irq, ret);
goto err_out_kfree;
}
dev->base_addr = ioaddr;
for(i = 0; i < ETHER_ADDR_LEN; i++) {
printk(" %2.2x", SA_prom[i]);
dev->dev_addr[i] = SA_prom[i];
}
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);
ei_status.name = name;
ei_status.tx_start_page = start_page;
ei_status.stop_page = stop_page;
ei_status.word16 = (wordlength == 2);
ei_status.rx_start_page = start_page + TX_PAGES;
#ifdef PACKETBUF_MEMSIZE
/* Allow the packet buffer size to be overridden by know-it-alls. */
ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
#endif
ei_status.reset_8390 = &ne_reset_8390;
ei_status.block_input = &ne_block_input;
ei_status.block_output = &ne_block_output;
ei_status.get_8390_hdr = &ne_get_8390_hdr;
ei_status.priv = 0;
dev->open = &ne_open;
dev->stop = &ne_close;
#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = ei_poll;
#endif
NS8390_init(dev, 0);
return 0;
err_out_kfree:
ne2k_cbus_destroy(dev);
err_out:
while (rlist > hw->regionlist) {
rlist --;
release_region(ioaddr + rlist->start, rlist->range);
}
return ret;
}
static int ne_open(struct net_device *dev)
{
ei_open(dev);
return 0;
}
static int ne_close(struct net_device *dev)
{
if (ei_debug > 1)
printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name);
ei_close(dev);
return 0;
}
/* Hard reset the card. This used to pause for the same period that a
8390 reset command required, but that shouldn't be necessary. */
static void ne_reset_8390(struct net_device *dev)
{
unsigned long reset_start_time = jiffies;
struct ei_device *ei_local = (struct ei_device *)(dev->priv);
if (ei_debug > 1)
printk(KERN_DEBUG "resetting the 8390 t=%ld...", jiffies);
/* derived from CNET98EL-patch for bad clones... */
outb_p(E8390_NODMA | E8390_STOP, NE_BASE + E8390_CMD); /* 0x20 | 0x1 */
/* 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);
ei_status.txing = 0;
ei_status.dmaing = 0;
/* This check _should_not_ be necessary, omit eventually. */
while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
if (jiffies - reset_start_time > 2*HZ/100) {
printk(KERN_WARNING "%s: ne_reset_8390() did not complete.\n", dev->name);
break;
}
outb_p(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */
}
/* Grab the 8390 specific header. Similar to the block_input routine, but
we don't need to be concerned with ring wrap as the header will be at
the start of a page, so we optimize accordingly. */
static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
{
int nic_base = dev->base_addr;
struct ei_device *ei_local = (struct ei_device *)(dev->priv);
/* This *shouldn't* happen. If it does, it's the last thing you'll see */
if (ei_status.dmaing)
{
printk(KERN_EMERG "%s: DMAing conflict in ne_get_8390_hdr "
"[DMAstat:%d][irqlock:%d].\n",
dev->name, ei_status.dmaing, ei_status.irqlock);
return;
}
ei_status.dmaing |= 0x01;
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
outb_p(0, nic_base + EN0_RCNTHI);
outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */
outb_p(ring_page, nic_base + EN0_RSARHI);
outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
if (ei_status.word16)
insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
else
insb(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr));
outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
ei_status.dmaing &= ~0x01;
le16_to_cpus(&hdr->count);
}
/* Block input and output, similar to the Crynwr packet driver. If you
are porting to a new ethercard, look at the packet driver source for hints.
The NEx000 doesn't share the on-board packet memory -- you have to put
the packet out through the "remote DMA" dataport using outb. */
static void ne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
{
#ifdef NE_SANITY_CHECK
int xfer_count = count;
#endif
int nic_base = dev->base_addr;
char *buf = skb->data;
struct ei_device *ei_local = (struct ei_device *)(dev->priv);
/* This *shouldn't* happen. If it does, it's the last thing you'll see */
if (ei_status.dmaing)
{
printk(KERN_EMERG "%s: DMAing conflict in ne_block_input "
"[DMAstat:%d][irqlock:%d].\n",
dev->name, ei_status.dmaing, ei_status.irqlock);
return;
}
ei_status.dmaing |= 0x01;
/* round up count to a word (derived from ICM-patch) */
count = (count + 1) & ~1;
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);
outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
if (ei_status.word16)
{
insw(NE_BASE + NE_DATAPORT,buf,count>>1);
if (count & 0x01)
{
buf[count-1] = inb(NE_BASE + NE_DATAPORT);
#ifdef NE_SANITY_CHECK
xfer_count++;
#endif
}
} else {
insb(NE_BASE + NE_DATAPORT, buf, count);
}
#ifdef NE_SANITY_CHECK
/* This was for the ALPHA version only, but enough people have
been encountering problems so it is still here. If you see
this message you either 1) have a slightly incompatible clone
or 2) have noise/speed problems with your bus. */
if (ei_debug > 1)
{
/* DMA termination address check... */
int addr, tries = 20;
do {
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
-- it's broken for Rx on some cards! */
int high = inb_p(nic_base + EN0_RSARHI);
int low = inb_p(nic_base + EN0_RSARLO);
addr = (high << 8) + low;
if (((ring_offset + xfer_count) & 0xff) == low)
break;
} while (--tries > 0);
if (tries <= 0)
printk(KERN_WARNING "%s: RX transfer address mismatch,"
"%#4.4x (expected) vs. %#4.4x (actual).\n",
dev->name, ring_offset + xfer_count, addr);
}
#endif
outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
ei_status.dmaing &= ~0x01;
}
static void ne_block_output(struct net_device *dev, int count,
const unsigned char *buf, const int start_page)
{
int nic_base = NE_BASE;
unsigned long dma_start;
#ifdef NE_SANITY_CHECK
int retries = 0;
#endif
struct ei_device *ei_local = (struct ei_device *)(dev->priv);
/* Round the count up for word writes. Do we need to do this?
What effect will an odd byte count have on the 8390?
I should check someday. */
if (ei_status.word16 && (count & 0x01))
count++;
/* This *shouldn't* happen. If it does, it's the last thing you'll see */
if (ei_status.dmaing)
{
printk(KERN_EMERG "%s: DMAing conflict in ne_block_output."
"[DMAstat:%d][irqlock:%d]\n",
dev->name, ei_status.dmaing, ei_status.irqlock);
return;
}
ei_status.dmaing |= 0x01;
/* We should already be in page 0, but to be safe... */
outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
#ifdef NE_SANITY_CHECK
retry:
#endif
#ifdef NE8390_RW_BUGFIX
/* Handle the read-before-write bug the same way as the
Crynwr packet driver -- the NatSemi method doesn't work.
Actually this doesn't always work either, but if you have
problems with your NEx000 this is better than nothing! */
outb_p(0x42, nic_base + EN0_RCNTLO);
outb_p(0x00, nic_base + EN0_RCNTHI);
outb_p(0x42, nic_base + EN0_RSARLO);
outb_p(0x00, nic_base + EN0_RSARHI);
outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
/* Make certain that the dummy read has occurred. */
udelay(6);
#endif
outb_p(ENISR_RDC, nic_base + EN0_ISR);
/* Now the normal output. */
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
outb_p(count >> 8, nic_base + EN0_RCNTHI);
outb_p(0x00, nic_base + EN0_RSARLO);
outb_p(start_page, nic_base + EN0_RSARHI);
outb_p(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
if (ei_status.word16) {
outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
} else {
outsb(NE_BASE + NE_DATAPORT, buf, count);
}
dma_start = jiffies;
#ifdef NE_SANITY_CHECK
/* This was for the ALPHA version only, but enough people have
been encountering problems so it is still here. */
if (ei_debug > 1)
{
/* DMA termination address check... */
int addr, tries = 20;
do {
int high = inb_p(nic_base + EN0_RSARHI);
int low = inb_p(nic_base + EN0_RSARLO);
addr = (high << 8) + low;
if ((start_page << 8) + count == addr)
break;
} while (--tries > 0);
if (tries <= 0)
{
printk(KERN_WARNING "%s: Tx packet transfer address mismatch,"
"%#4.4x (expected) vs. %#4.4x (actual).\n",
dev->name, (start_page << 8) + count, addr);
if (retries++ == 0)
goto retry;
}
}
#endif
while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
if (jiffies - dma_start > 2*HZ/100) { /* 20ms */
printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name);
ne_reset_8390(dev);
NS8390_init(dev,1);
break;
}
outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
ei_status.dmaing &= ~0x01;
return;
}
#ifdef MODULE
#define MAX_NE_CARDS 4 /* Max number of NE cards per module */
static struct net_device *dev_ne[MAX_NE_CARDS];
static int io[MAX_NE_CARDS];
static int irq[MAX_NE_CARDS];
static int bad[MAX_NE_CARDS]; /* 0xbad = bad sig or no reset ack */
static int hwtype[MAX_NE_CARDS] = { 0, }; /* board type */
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");
MODULE_PARM(hwtype, "1-" __MODULE_STRING(MAX_NE_CARDS) "i");
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");
MODULE_PARM_DESC(hwtype, "Board type of PC-9800 C-Bus NIC");
MODULE_DESCRIPTION("NE1000/NE2000 PC-9800 C-bus Ethernet driver");
MODULE_LICENSE("GPL");
/* This is set up so that no ISA autoprobe takes place. We can't guarantee
that the ne2k probe is the last 8390 based probe to take place (as it
is at boot) and so the probe will get confused by any other 8390 cards.
ISA device autoprobes on a running machine are not recommended anyway. */
int init_module(void)
{
int this_dev, found = 0;
for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
struct net_device *dev = alloc_ei_netdev();
if (!dev)
break;
dev->irq = irq[this_dev];
dev->mem_end = bad[this_dev];
dev->base_addr = io[this_dev];
dev->mem_start = hwtype[this_dev];
if (do_ne_probe(dev) == 0) {
if (register_netdev(dev) == 0) {
dev_ne[found++] = dev;
continue;
}
cleanup_card(dev);
}
free_netdev(dev);
if (found)
break;
if (io[this_dev] != 0)
printk(KERN_WARNING "ne2k_cbus: No NE*000 card found at i/o = %#x\n", io[this_dev]);
else
printk(KERN_NOTICE "ne.c: You must supply \"io=0xNNN\" value(s) for ISA cards.\n");
return -ENXIO;
}
if (found)
return 0;
return -ENODEV;
}
void cleanup_module(void)
{
int this_dev;
for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
struct net_device *dev = dev_ne[this_dev];
if (dev) {
unregister_netdev(dev);
cleanup_card(dev);
free_netdev(dev);
}
}
}
#endif /* MODULE */
/* 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, int irq);
/* End of ne2k_cbus.h */
/*
*
* drivers/scsi/pc980155.c
*
* PC-9801-55 SCSI host adapter driver
*
* Copyright (C) 1997-2003 Kyoto University Microcomputer Club
* (Linux/98 project)
* Tomoharu Ugawa <ohirune@kmc.gr.jp>
*
*/
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <asm/dma.h>
#include "scsi.h"
#include "hosts.h"
#include "wd33c93.h"
#include "pc980155.h"
extern int pc98_bios_param(struct scsi_device *, struct block_device *,
sector_t, int *);
static int scsi_pc980155_detect(Scsi_Host_Template *);
static int scsi_pc980155_release(struct Scsi_Host *);
#ifndef CMD_PER_LUN
#define CMD_PER_LUN 2
#endif
#ifndef CAN_QUEUE
#define CAN_QUEUE 16
#endif
#undef PC_9801_55_DEBUG
#undef PC_9801_55_DEBUG_VERBOSE
#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 wd33c93_regs init_regs;
static int io;
static struct Scsi_Host *pc980155_host = NULL;
static void pc980155_intr_handle(int irq, void *dev_id, struct pt_regs *regp);
static inline void pc980155_dma_enable(unsigned int base_io)
{
outb(0x01, REG_CWRITE);
}
static inline void pc980155_dma_disable(unsigned int base_io)
{
outb(0x02, REG_CWRITE);
}
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);
#ifdef PC_9801_55_DEBUG
printk("D%d(%x)D", sc->device->host->dma_channel,
sc->SCp.this_residual);
#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 (inb(regs.SASR) == 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_pc980155_resetint(regs);
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);
*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 PC_9801_55_DEBUG
printk("PC-9801-55: end of getconfig\n");
#endif
return 1;
}
/* return non-zero on detection */
static int scsi_pc980155_detect(Scsi_Host_Template* tpnt)
{
unsigned int base_io;
unsigned char irq, dma, scsi_id;
int i;
#ifdef PC_9801_55_DEBUG
unsigned char debug;
#endif
if (io) {
base_ios[0] = io;
nr_base_ios = 1;
}
for (i = 0; i < nr_base_ios; i++) {
base_io = base_ios[i];
init_regs.SASR = REG_ADDRST;
init_regs.SCMD = REG_CONTRL;
#ifdef PC_9801_55_DEBUG
printk("PC-9801-55: SASR(%x = %x)\n", SASR, REG_ADDRST);
#endif
if (!request_region(base_io, 6, "PC-9801-55"))
continue;
if (pc980155_test_port(init_regs) &&
pc980155_getconfig(base_io, init_regs,
&irq, &dma, &scsi_id))
goto found;
release_region(base_io, 6);
}
printk("PC-9801-55: not found\n");
return 0;
found:
#ifdef PC_9801_55_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);
goto err1;
}
if (request_dma(dma, "PC-9801-55")) {
printk(KERN_ERR "PC-9801-55: unable to allocate DMA channel %d\n", dma);
goto err2;
}
pc980155_host = scsi_register(tpnt, sizeof(struct WD33C93_hostdata));
if (pc980155_host) {
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;
printk("PC-9801-55: scsi host found at %x irq = %d, use dma channel %d.\n", base_io, irq, dma);
pc980155_int_enable(init_regs);
wd33c93_init(pc980155_host, init_regs, dma_setup, dma_stop,
WD33C93_FS_12_15);
return 1;
}
printk(KERN_ERR "PC-9801-55: failed to register device\n");
err2:
free_irq(irq, NULL);
err1:
release_region(base_io, 6);
return 0;
}
static int scsi_pc980155_release(struct Scsi_Host *shost)
{
struct WD33C93_hostdata *hostdata
= (struct WD33C93_hostdata *)shost->hostdata;
pc980155_int_disable(hostdata->regs);
release_region(shost->io_port, shost->n_io_port);
free_irq(shost->irq, NULL);
free_dma(shost->dma_channel);
wd33c93_release();
return 1;
}
static int pc980155_bus_reset(Scsi_Cmnd *cmd)
{
struct WD33C93_hostdata *hostdata
= (struct WD33C93_hostdata *)cmd->device->host->hostdata;
pc980155_int_disable(hostdata->regs);
pc980155_assert_bus_reset(hostdata->regs);
udelay(50);
pc980155_negate_bus_reset(hostdata->regs);
(void) inb(hostdata->regs.SASR);
(void) read_pc980155(hostdata->regs, WD_SCSI_STATUS);
pc980155_int_enable(hostdata->regs);
wd33c93_host_reset(cmd);
return SUCCESS;
}
#ifndef MODULE
static int __init pc980155_setup(char *str)
{
int ints[4];
str = get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] > 0)
io = ints[1];
return 1;
}
__setup("pc980155_io=", pc980155_setup);
#endif
MODULE_PARM(io, "i");
MODULE_AUTHOR("Tomoharu Ugawa <ohirune@kmc.gr.jp>");
MODULE_DESCRIPTION("PC-9801-55 SCSI host adapter driver");
MODULE_LICENSE("GPL");
static Scsi_Host_Template driver_template = {
.proc_info = wd33c93_proc_info,
.name = "SCSI PC-9801-55",
.detect = scsi_pc980155_detect,
.release = scsi_pc980155_release,
.queuecommand = wd33c93_queuecommand,
.eh_abort_handler = wd33c93_abort,
.eh_bus_reset_handler = pc980155_bus_reset,
.eh_host_reset_handler = wd33c93_host_reset,
.bios_param = pc98_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,
.proc_name = "PC_9801_55",
};
#include "scsi_module.c"
/*
*
* drivers/scsi/pc980155.h
*
* PC-9801-55 SCSI host adapter driver
*
* Copyright (C) 1997-2003 Kyoto University Microcomputer Club
* (Linux/98 project)
* Tomoharu Ugawa <ohirune@kmc.gr.jp>
*
*/
#ifndef __PC980155_H
#define __PC980155_H
#include "wd33c93.h"
#define REG_ADDRST (base_io)
#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
static inline uchar read_pc980155(const wd33c93_regs regs, uchar reg_num)
{
outb(reg_num, regs.SASR);
return (uchar)inb(regs.SCMD);
}
static inline void write_memorybank(const wd33c93_regs regs, uchar value)
{
outb(WD_MEMORYBANK, regs.SASR);
outb(value, regs.SCMD);
}
#define read_pc980155_resetint(regs) \
read_pc980155((regs), WD_RESETINT)
#define pc980155_int_enable(regs) \
write_memorybank((regs), read_pc980155((regs), WD_MEMORYBANK) | 0x04)
#define pc980155_int_disable(regs) \
write_memorybank((regs), read_pc980155((regs), WD_MEMORYBANK) & ~0x04)
#define pc980155_assert_bus_reset(regs) \
write_memorybank((regs), read_pc980155((regs), WD_MEMORYBANK) | 0x02)
#define pc980155_negate_bus_reset(regs) \
write_memorybank((regs), read_pc980155((regs), WD_MEMORYBANK) & ~0x02)
#endif /* __PC980155_H */
/*
* Copyright (C) 2003 Osamu Tomita <tomita@cinet.co.jp>
*
* PC9801 BIOS geometry handling.
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <asm/pc9800.h>
#include "scsi.h"
#include "hosts.h"
static int pc98_first_bios_param(struct scsi_device *sdev, int *ip)
{
const u8 *p = (&__PC9800SCA(u8, PC9800SCA_SCSI_PARAMS) + sdev->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;
}
int pc98_bios_param(struct scsi_device *sdev, struct block_device *bdev,
sector_t capacity, int *ip)
{
struct Scsi_Host *first_real = first_real_host();
/*
* XXX
* XXX This needs to become a sysfs attribute that's set
* XXX by code that knows which host is the first one.
* XXX
* XXX Currently we support only one host on with a
* XXX PC98ish HBA.
* XXX
*/
if (1 || sdev->host == first_real && sdev->id < 7 &&
__PC9800SCA_TEST_BIT(PC9800SCA_DISK_EQUIPS, sdev->id))
return pc98_first_bios_param(sdev, ip);
/* Assume PC-9801-92 compatible parameters for HAs without BIOS. */
ip[0] = 8;
ip[1] = 32;
ip[2] = capacity / (8 * 32);
if (ip[2] > 65535) { /* if capacity >= 8GB */
/* Recent on-board adapters seem to use this parameter. */
ip[1] = 128;
ip[2] = capacity / (8 * 128);
if (ip[2] > 65535) { /* if capacity >= 32GB */
/* Clip the number of cylinders. Currently
this is the limit that we deal with. */
ip[2] = 65535;
}
}
return 0;
}
EXPORT_SYMBOL(pc98_bios_param);
/*
* linux/drivers/serial/serial98.c
*
* Driver for NEC PC-9801/PC-9821 standard serial ports
*
* Based on drivers/serial/8250.c, by Russell King.
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
*
* Copyright (C) 2002 Osamu Tomita <tomita@cinet.co.jp>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/serial_reg.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/pc9800.h>
#include <asm/pc9800_sca.h>
#if defined(CONFIG_SERIAL98_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/serial_core.h>
#define SERIAL98_NR 1
#define SERIAL98_ISR_PASS_LIMIT 256
#define SERIAL98_EXT 0x434
//#define RX_8251F 0x130 /* In: Receive buffer */
//#define TX_8251F 0x130 /* Out: Transmit buffer */
//#define LSR_8251F 0x132 /* In: Line Status Register */
//#define MSR_8251F 0x134 /* In: Modem Status Register */
#define IIR_8251F 0x136 /* In: Interrupt ID Register */
#define FCR_8251F 0x138 /* I/O: FIFO Control Register */
#define VFAST_8251F 0x13a /* I/O: VFAST mode Register */
#define CMD_8251F 0x32 /* Out: 8251 Command Resister */
#define IER2_8251F 0x34 /* I/O: Interrupt Enable Register */
#define IER1_8251F 0x35 /* I/O: Interrupt Enable Register */
#define IER1_CTL 0x37 /* Out: Interrupt Enable Register */
#define DIS_RXR_INT 0x00 /* disable RxRDY Interrupt */
#define ENA_RXR_INT 0x01 /* enable RxRDY Interrupt */
#define DIS_TXE_INT 0x02 /* disable TxEMPTY Interrupt */
#define ENA_TXE_INT 0x03 /* enable TxEMPTY Interrupt */
#define DIS_TXR_INT 0x04 /* disable TxRDY Interrupt */
#define ENA_TXR_INT 0x05 /* enable TxRDY Interrupt */
#define CMD_RESET 0x40 /* Reset Command */
#define CMD_RTS 0x20 /* Set RTS line */
#define CMD_CLR_ERR 0x10 /* Clear error flag */
#define CMD_BREAK 0x08 /* Send Break */
#define CMD_RXE 0x04 /* Enable receive */
#define CMD_DTR 0x02 /* Set DTR line */
#define CMD_TXE 0x01 /* Enable send */
#define CMD_DUMMY 0x00 /* Dummy Command */
#define VFAST_ENABLE 0x80 /* V.Fast mode Enable */
/* Interrupt masks */
#define INTR_8251_TXRE 0x04
#define INTR_8251_TXEE 0x02
#define INTR_8251_RXRE 0x01
/* I/O Port */
//#define PORT_8251_DATA 0
//#define PORT_8251_CMD 2
//#define PORT_8251_MOD 2
//#define PORT_8251_STS 2
/* status read */
#define STAT_8251_TXRDY 0x01
#define STAT_8251_RXRDY 0x02
#define STAT_8251_TXEMP 0x04
#define STAT_8251_PER 0x08
#define STAT_8251_OER 0x10
#define STAT_8251_FER 0x20
#define STAT_8251_BRK 0x40
#define STAT_8251_DSR 0x80
#if 1
#define STAT_8251F_TXEMP 0x01
#define STAT_8251F_TXRDY 0x02
#define STAT_8251F_RXRDY 0x04
#define STAT_8251F_DSR 0x08
#define STAT_8251F_OER 0x10
#define STAT_8251F_PER 0x20
#define STAT_8251F_FER 0x40
#define STAT_8251F_BRK 0x80
#else
#define STAT_8251F_TXEMP 0x01
#define STAT_8251F_TEMT 0x01
#define STAT_8251F_TXRDY 0x02
#define STAT_8251F_THRE 0x02
#define STAT_8251F_RXRDY 0x04
#define STAT_8251F_DSR 0x04
#define STAT_8251F_PER 0x08
#define STAT_8251F_OER 0x10
#define STAT_8251F_FER 0x20
#define STAT_8251F_BRK 0x40
#endif
/*
* We wrap our port structure around the generic uart_port.
*/
struct serial98_port {
struct uart_port port;
unsigned int type;
unsigned int ext;
unsigned int lsr_break_flag;
unsigned char cmd;
unsigned char mode;
unsigned char msr;
unsigned char ier;
unsigned char rxchk;
unsigned char txemp;
unsigned char txrdy;
unsigned char rxrdy;
unsigned char brk;
unsigned char fe;
unsigned char oe;
unsigned char pe;
unsigned char dr;
};
#ifdef CONFIG_SERIAL98_CONSOLE
static void
serial98_console_write(struct console *co, const char *s, unsigned int count);
static int __init serial98_console_setup(struct console *co, char *options);
extern struct uart_driver serial98_reg;
static struct console serial98_console = {
.name = "ttyS",
.write = serial98_console_write,
.device = uart_console_device,
.setup = serial98_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &serial98_reg,
};
#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",
.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 baud, 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.
*/
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
quot = uart_get_divisor(port, baud);
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, baud);
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;
/*
* Characters 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 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);
MODULE_AUTHOR("Osamu Tomita <tomita@cinet.co.jp>");
MODULE_DESCRIPTION("PC-9801 standard serial port driver Version 0.1alpha");
MODULE_LICENSE("GPL");
/*
* 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/hdreg.h>
#include "check.h"
#include "nec98.h"
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;
}
int nec98_partition(struct parsed_partitions *state, struct block_device *bdev)
{
unsigned int nr;
struct hd_geometry geo;
Sector sect;
const struct nec98_partition *part;
unsigned char *data;
int sector_size = bdev_hardsect_size(bdev);
if (ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)&geo) != 0) {
printk(" unsupported disk (%s)\n", bdev->bd_disk->disk_name);
return 0;
}
#ifdef NEC98_PARTITION_DEBUG
printk("ioctl_by_bdev head=%d sect=%d\n", geo.heads, geo.sectors);
#endif
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,
geo.sectors, geo.heads)) {
#ifdef NEC98_PARTITION_DEBUG
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 * geo.heads + part->head) * geo.sectors
+ part->sector;
end_sect = (part->end_cyl + 1) * geo.heads * geo.sectors;
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:
*/
/*
* 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);
/*
* include/asm-i386/mach-pc9800/apm.h
*
* Machine specific APM BIOS functions for NEC PC9800.
* Split out from apm.c by Osamu Tomita <tomita@cinet.co.jp>
*/
#ifndef _ASM_APM_H
#define _ASM_APM_H
#include <linux/apm_bios.h>
#ifdef APM_ZERO_SEGS
# define APM_DO_ZERO_SEGS \
"pushl %%ds\n\t" \
"pushl %%es\n\t" \
"xorl %%edx, %%edx\n\t" \
"mov %%dx, %%ds\n\t" \
"mov %%dx, %%es\n\t" \
"mov %%dx, %%fs\n\t" \
"mov %%dx, %%gs\n\t"
# define APM_DO_POP_SEGS \
"popl %%es\n\t" \
"popl %%ds\n\t"
#else
# define APM_DO_ZERO_SEGS
# define APM_DO_POP_SEGS
#endif
static inline void apm_bios_call_asm(u32 func, u32 ebx_in, u32 ecx_in,
u32 *eax, u32 *ebx, u32 *ecx,
u32 *edx, u32 *esi)
{
/*
* N.B. We do NOT need a cld after the BIOS call
* because we always save and restore the flags.
*/
__asm__ __volatile__(APM_DO_ZERO_SEGS
"pushl %%edi\n\t"
"pushl %%ebp\n\t"
"pushfl\n\t"
"lcall *%%cs:apm_bios_entry\n\t"
"setc %%al\n\t"
"popl %%ebp\n\t"
"popl %%edi\n\t"
APM_DO_POP_SEGS
: "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx),
"=S" (*esi)
: "a" (func), "b" (ebx_in), "c" (ecx_in)
: "memory", "cc");
}
static inline u8 apm_bios_call_simple_asm(u32 func, u32 ebx_in,
u32 ecx_in, u32 *eax)
{
int cx, dx, si;
u8 error;
/*
* N.B. We do NOT need a cld after the BIOS call
* because we always save and restore the flags.
*/
__asm__ __volatile__(APM_DO_ZERO_SEGS
"pushl %%edi\n\t"
"pushl %%ebp\n\t"
"pushfl\n\t"
"lcall *%%cs:apm_bios_entry\n\t"
"setc %%bl\n\t"
"popl %%ebp\n\t"
"popl %%edi\n\t"
APM_DO_POP_SEGS
: "=a" (*eax), "=b" (error), "=c" (cx), "=d" (dx),
"=S" (si)
: "a" (func), "b" (ebx_in), "c" (ecx_in)
: "memory", "cc");
if (func == APM_FUNC_VERSION)
*eax = (*eax & 0xff00) | ((*eax & 0x00f0) >> 4);
return error;
}
#endif /* _ASM_APM_H */
#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 */
/* defines for inline arch setup functions */
#include <asm/apic.h>
/**
* 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 doesn't 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;
}
/*
* arch/i386/mach-pc9800/io_ports.h
*
* Machine specific IO port address definition for PC-9800.
* Written by Osamu Tomita <tomita@cinet.co.jp>
*/
#ifndef _MACH_IO_PORTS_H
#define _MACH_IO_PORTS_H
/* i8253A PIT registers */
#define PIT_MODE 0x77
#define PIT_CH0 0x71
#define PIT_CH2 0x75
/* i8259A PIC registers */
#define PIC_MASTER_CMD 0x00
#define PIC_MASTER_IMR 0x02
#define PIC_MASTER_ISR PIC_MASTER_CMD
#define PIC_MASTER_POLL PIC_MASTER_ISR
#define PIC_MASTER_OCW3 PIC_MASTER_ISR
#define PIC_SLAVE_CMD 0x08
#define PIC_SLAVE_IMR 0x0a
/* i8259A PIC related values */
#define PIC_CASCADE_IR 7
#define MASTER_ICW4_DEFAULT 0x1d
#define SLAVE_ICW4_DEFAULT 0x09
#define PIC_ICW4_AEOI 0x02
#endif /* !_MACH_IO_PORTS_H */
/*
* 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.
*
* NR_IRQ_VECTORS:
* The total number of IO APIC vector inputs
*
*/
#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 NR_VECTORS 256
#define NR_IRQ_VECTORS NR_IRQS
#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 */
/*
* arch/i386/mach-pc9800/mach_reboot.h
*
* Machine specific reboot functions for PC-9800.
* Written by Osamu Tomita <tomita@cinet.co.jp>
*/
#ifndef _MACH_REBOOT_H
#define _MACH_REBOOT_H
#ifdef CMOS_WRITE
#undef CMOS_WRITE
#define CMOS_WRITE(a,b) do{}while(0)
#endif
static inline void mach_reboot(void)
{
outb(0, 0xf0); /* signal CPU reset */
mdelay(1);
}
#endif /* !_MACH_REBOOT_H */
/*
* include/asm-i386/mach-pc9800/mach_time.h
*
* Machine specific set RTC function for PC-9800.
* Written by Osamu Tomita <tomita@cinet.co.jp>
*/
#ifndef _MACH_TIME_H
#define _MACH_TIME_H
#include <linux/bcd.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 USEC_AFTER 1000000
#define USEC_BEFORE 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 = BCD2BIN(data.min);
/*
* 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;
}
static inline unsigned long mach_get_cmos_time(void)
{
int i;
u8 prev, cur;
unsigned int year;
struct upd4990a_raw_data data;
/* 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);
if ((year = BCD2BIN(data.year) + 1900) < 1995)
year += 100;
return mktime(year, data.mon, BCD2BIN(data.mday), BCD2BIN(data.hour),
BCD2BIN(data.min), BCD2BIN(data.sec));
}
#endif /* !_MACH_TIME_H */
/*
* include/asm-i386/mach-pc9800/mach_timer.h
*
* Machine specific calibrate_tsc() for PC-9800.
* Written by Osamu Tomita <tomita@cinet.co.jp>
*/
/* ------ Calibrate the TSC -------
* 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_TIMER_H
#define _MACH_TIMER_H
#define CALIBRATE_LATCH (5 * 307200/HZ) /* 0.050sec * 307200Hz = 15360 */
static inline void mach_prepare_counter(void)
{
/* ARTIC can't be stopped nor reset. So we wait roundup. */
while (inw(0x5c));
}
static inline void mach_countup(unsigned long *count)
{
do {
*count = inw(0x5c);
} while (*count < CALIBRATE_LATCH);
}
#endif /* !_MACH_TIMER_H */
/*
* include/asm-i386/mach-pc9800/mach_traps.h
*
* Machine specific NMI handling for PC-9800.
* Written by Osamu Tomita <tomita@cinet.co.jp>
*/
#ifndef _MACH_TRAPS_H
#define _MACH_TRAPS_H
static inline void clear_mem_error(unsigned char reason)
{
outb(0x08, 0x37);
outb(0x09, 0x37);
}
static inline unsigned char get_nmi_reason(void)
{
return (inb(0x33) & 6) ? 0x80 : 0;
}
static inline void reassert_nmi(void)
{
outb(0x09, 0x50); /* disable NMI once */
outb(0x09, 0x52); /* re-enable it */
}
#endif /* !_MACH_TRAPS_H */
#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 */
/*
* PCI BIOS function codes for the PC9800. Different to
* 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 */
/**
* 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;
}
/* 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;
extern unsigned long tick_usec; /* ACTHZ period (usec) */
extern 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; /* USER_HZ period (usec) */
tick_nsec = TICK_NSEC; /* ACTHZ 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 */
}
/* 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(unsigned long start_eip)
{
/* reset code is stored in 8255 on PC-9800. */
outb(0x0e, 0x37); /* SHUT0 = 0 */
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");
/*
* 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");
}
static inline void smpboot_restore_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();
}
/*
* PC-9800 machine types.
*
* Copyright (C) 1999 TAKAI Kosuke <tak@kmc.kyoto-u.ac.jp>
* (Linux/98 Project)
*/
#ifndef _ASM_PC9800_H_
#define _ASM_PC9800_H_
#include <asm/pc9800_sca.h>
#include <asm/types.h>
#define __PC9800SCA(type, pa) (*(type *) phys_to_virt(pa))
#define __PC9800SCA_TEST_BIT(pa, n) \
((__PC9800SCA(u8, pa) & (1U << (n))) != 0)
#define PC9800_HIGHRESO_P() __PC9800SCA_TEST_BIT(PC9800SCA_BIOS_FLAG, 3)
#define PC9800_8MHz_P() __PC9800SCA_TEST_BIT(PC9800SCA_BIOS_FLAG, 7)
/* 0x2198 is 98 21 on memory... */
#define PC9800_9821_P() (__PC9800SCA(u16, PC9821SCA_ROM_ID) == 0x2198)
/* Note PC9821_...() are valid only when PC9800_9821_P() was true. */
#define PC9821_IDEIF_DOUBLE_P() __PC9800SCA_TEST_BIT(PC9821SCA_ROM_FLAG4, 4)
#endif
/*
* System-common area definitions for NEC PC-9800 series
*
* Copyright (C) 1999 TAKAI Kousuke <tak@kmc.kyoto-u.ac.jp>,
* Kyoto University Microcomputer Club.
*/
#ifndef _ASM_I386_PC9800SCA_H_
#define _ASM_I386_PC9800SCA_H_
#define PC9800SCA_EXPMMSZ (0x0401) /* B */
#define PC9800SCA_SCSI_PARAMS (0x0460) /* 8 * 4B */
#define PC9800SCA_DISK_EQUIPS (0x0482) /* B */
#define PC9800SCA_XROM_ID (0x04C0) /* 52B */
#define PC9800SCA_BIOS_FLAG (0x0501) /* B */
#define PC9800SCA_MMSZ16M (0x0594) /* W */
/* PC-9821 have additional system common area in their BIOS-ROM segment. */
#define PC9821SCA__BASE (0xF8E8 << 4)
#define PC9821SCA_ROM_ID (PC9821SCA__BASE + 0x00)
#define PC9821SCA_ROM_FLAG4 (PC9821SCA__BASE + 0x05)
#define PC9821SCA_RSFLAGS (PC9821SCA__BASE + 0x11) /* B */
#endif /* !_ASM_I386_PC9800SCA_H_ */
/*
* Driver for CS4232 on NEC PC9800 series
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
* Osamu Tomita <tomita@cinet.co.jp>
* Takashi Iwai <tiwai@suse.de>
* Hideaki Okubo <okubo@msh.biglobe.ne.jp>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/cs4231.h>
#include <sound/mpu401.h>
#include <sound/opl3.h>
#include <sound/initval.h>
#include "sound_pc9800.h"
#define chip_t cs4231_t
MODULE_AUTHOR("Osamu Tomita <tomita@cinet.co.jp>");
MODULE_LICENSE("GPL");
MODULE_CLASSES("{sound}");
MODULE_DESCRIPTION("NEC PC9800 CS4232");
MODULE_DEVICES("{{NEC,PC9800}}");
#define IDENT "PC98-CS4232"
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
#if 0 /* NOT USED */
static long cport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
#endif
static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* PnP setup */
static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */
static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 9,11,12,15 */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */
static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */
static int pc98ii[SNDRV_CARDS]; /* PC98II */
static int boot_devs;
module_param_array(index, int, boot_devs, 0444);
MODULE_PARM_DESC(index, "Index value for " IDENT " soundcard.");
MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
module_param_array(id, charp, boot_devs, 0444);
MODULE_PARM_DESC(id, "ID string for " IDENT " soundcard.");
MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
module_param_array(enable, bool, boot_devs, 0444);
MODULE_PARM_DESC(enable, "Enable " IDENT " soundcard.");
MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
module_param_array(port, long, boot_devs, 0444);
MODULE_PARM_DESC(port, "Port # for " IDENT " driver.");
MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC);
#if 0 /* NOT USED */
module_param_array(cport, long, boot_devs, 0444);
MODULE_PARM_DESC(cport, "Control port # for " IDENT " driver.");
MODULE_PARM_SYNTAX(cport, SNDRV_PORT12_DESC);
#endif
module_param_array(mpu_port, long, boot_devs, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " IDENT " driver.");
MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC);
module_param_array(fm_port, long, boot_devs, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for " IDENT " driver.");
MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT12_DESC);
module_param_array(irq, int, boot_devs, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " IDENT " driver.");
MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC);
module_param_array(mpu_irq, int, boot_devs, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " IDENT " driver.");
MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC);
module_param_array(dma1, int, boot_devs, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for " IDENT " driver.");
MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC);
module_param_array(dma2, int, boot_devs, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for " IDENT " driver.");
MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC);
module_param_array(pc98ii, bool, boot_devs, 0444);
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 initialize 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 initialize failed at stage2\n");
return -EINVAL;
end_mode_118:
outb(0x3f, 0x148d);
snd_printk(KERN_INFO IDENT ": MIDI daughter board initialized\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;
}
outb(dma1[dev], 0x29); /* dma1 boundary 64KB */
if (dma1[dev] != dma2[dev] && dma2[dev] >= 0) {
outb(0, 0x5f); /* wait */
outb(dma2[dev], 0x29); /* dma2 boundary 64KB */
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 ((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] > 0 && 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] > 0 && 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 (dma2[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)
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 */
#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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment