lkml.org 
[lkml]   [2003]   [Feb]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH] PC-9800 additional for 2.5.61-ac1 (17/21) serial
    This is additional patch to support NEC PC-9800 subarchitecture
    against 2.5.61-ac1. (17/21)

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

    diff -Nru linux/drivers/serial/8250_pnp.c linux98/drivers/serial/8250_pnp.c
    --- linux/drivers/serial/8250_pnp.c 2002-12-11 13:10:07.000000000 +0900
    +++ linux98/drivers/serial/8250_pnp.c 2002-12-11 13:16:51.000000000 +0900
    @@ -188,6 +188,8 @@
    { "MVX00A1", 0 },
    /* PC Rider K56 Phone System PnP */
    { "MVX00F2", 0 },
    + /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */
    + { "nEC8241", 0 },
    /* Pace 56 Voice Internal Plug & Play Modem */
    { "PMC2430", 0 },
    /* Generic */
    @@ -373,6 +375,9 @@
    ((port->min == 0x2f8) ||
    (port->min == 0x3f8) ||
    (port->min == 0x2e8) ||
    +#ifdef CONFIG_X86_PC9800
    + (port->min == 0x8b0) ||
    +#endif
    (port->min == 0x3e8)))
    return 0;
    }
    diff -Nru linux/drivers/serial/Kconfig linux98/drivers/serial/Kconfig
    --- linux/drivers/serial/Kconfig 2003-01-14 14:59:16.000000000 +0900
    +++ linux98/drivers/serial/Kconfig 2003-01-17 13:07:46.000000000 +0900
    @@ -372,14 +372,25 @@
    bool "Use NEC V850E on-chip UART for console"
    depends on V850E_NB85E_UART

    +config SERIAL98
    + tristate "PC-9800 8251-based primary serial port support"
    + depends on X86_PC9800
    + help
    + If you want to use standard primary serial ports on PC-9800,
    + say Y. Otherwise, say N.
    +
    +config SERIAL98_CONSOLE
    + bool "Support for console on PC-9800 standard serial port"
    + depends on SERIAL98=y
    +
    config SERIAL_CORE
    tristate
    - default m if SERIAL_AMBA!=y && SERIAL_CLPS711X!=y && SERIAL_21285!=y && !SERIAL_SA1100 && !SERIAL_ANAKIN && !SERIAL_UART00 && SERIAL_8250!=y && SERIAL_MUX!=y && !SERIAL_ROCKETPORT && !SERIAL_SUNCORE && !V850E_NB85E_UART && (SERIAL_AMBA=m || SERIAL_CLPS711X=m || SERIAL_21285=m || SERIAL_8250=m || SERIAL_MUX=m)
    - default y if SERIAL_AMBA=y || SERIAL_CLPS711X=y || SERIAL_21285=y || SERIAL_SA1100 || SERIAL_ANAKIN || SERIAL_UART00 || SERIAL_8250=y || SERIAL_MUX=y || SERIAL_ROCKETPORT || SERIAL_SUNCORE || V850E_NB85E_UART
    + default m if SERIAL_AMBA!=y && SERIAL_CLPS711X!=y && SERIAL_21285!=y && !SERIAL_SA1100 && !SERIAL_ANAKIN && !SERIAL_UART00 && SERIAL_8250!=y && SERIAL_MUX!=y && !SERIAL_ROCKETPORT && !SERIAL_SUNCORE && !V850E_NB85E_UART && (SERIAL_AMBA=m || SERIAL_CLPS711X=m || SERIAL_21285=m || SERIAL_8250=m || SERIAL_MUX=m || SERIAL98=m)
    + default y if SERIAL_AMBA=y || SERIAL_CLPS711X=y || SERIAL_21285=y || SERIAL_SA1100 || SERIAL_ANAKIN || SERIAL_UART00 || SERIAL_8250=y || SERIAL_MUX=y || SERIAL_ROCKETPORT || SERIAL_SUNCORE || V850E_NB85E_UART || SERIAL98=y

    config SERIAL_CORE_CONSOLE
    bool
    - depends on SERIAL_AMBA_CONSOLE || SERIAL_CLPS711X_CONSOLE || SERIAL_21285_CONSOLE || SERIAL_SA1100_CONSOLE || SERIAL_ANAKIN_CONSOLE || SERIAL_UART00_CONSOLE || SERIAL_8250_CONSOLE || SERIAL_MUX_CONSOLE || SERIAL_SUNCORE || V850E_NB85E_UART_CONSOLE
    + depends on SERIAL_AMBA_CONSOLE || SERIAL_CLPS711X_CONSOLE || SERIAL_21285_CONSOLE || SERIAL_SA1100_CONSOLE || SERIAL_ANAKIN_CONSOLE || SERIAL_UART00_CONSOLE || SERIAL_8250_CONSOLE || SERIAL_MUX_CONSOLE || SERIAL_SUNCORE || V850E_NB85E_UART_CONSOLE || SERIAL98_CONSOLE
    default y

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

    +/* NEC PC-9800 */
    +#define PORT_8251_PC98 41
    +#define PORT_19K_PC98 42
    +#define PORT_FIFO_PC98 43
    +#define PORT_VFAST_PC98 44
    +#define PORT_PC9861 45
    +#define PORT_PC9801_101 46
    +

    #ifdef __KERNEL__

    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/
    \
     
     \ /
      Last update: 2005-03-22 13:33    [from the cache]
    ©2003-2014 Jasper Spaans. hosted at Digital Ocean