Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id ; Wed, 12 Feb 2003 09:12:17 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id ; Wed, 12 Feb 2003 09:11:18 -0500 Received: from chii.cinet.co.jp ([61.197.228.217]:48512 "EHLO yuzuki.cinet.co.jp") by vger.kernel.org with ESMTP id ; Wed, 12 Feb 2003 09:08:44 -0500 Date: Wed, 12 Feb 2003 23:17:15 +0900 From: Osamu Tomita To: Linux Kernel Mailing List Cc: Alan Cox Subject: [PATCHSET] PC-9800 subarch. support for 2.5.60 (32/34) serial Message-ID: <20030212141715.GG1551@yuzuki.cinet.co.jp> References: <20030212131737.GA1551@yuzuki.cinet.co.jp> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20030212131737.GA1551@yuzuki.cinet.co.jp> User-Agent: Mutt/1.4i Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 32604 Lines: 1221 This is patchset to support NEC PC-9800 subarchitecture against 2.5.60 (32/34). Serial port driver. - Osamu Tomita 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(CONFIG_SERIAL98_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include + +#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 "); +MODULE_DESCRIPTION("PC-9801 standard serial port driver Version 0.1alpha"); +MODULE_LICENSE("GPL"); diff -Nru linux/include/linux/serial_core.h linux98/include/linux/serial_core.h --- linux/include/linux/serial_core.h 2003-01-09 13:03:55.000000000 +0900 +++ linux98/include/linux/serial_core.h 2003-01-10 10:53:38.000000000 +0900 @@ -58,6 +58,14 @@ /* NEC v850. */ #define PORT_NB85E_UART 40 +/* NEC PC-9800 */ +#define PORT_8251_PC98 41 +#define PORT_19K_PC98 42 +#define PORT_FIFO_PC98 43 +#define PORT_VFAST_PC98 44 +#define PORT_PC9861 45 +#define PORT_PC9801_101 46 + #ifdef __KERNEL__ - 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/