From owner-svn-src-user@FreeBSD.ORG Sun Oct 19 09:42:10 2008 Return-Path: Delivered-To: svn-src-user@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 69AE41065687; Sun, 19 Oct 2008 09:42:10 +0000 (UTC) (envelope-from nyan@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 56EF08FC08; Sun, 19 Oct 2008 09:42:10 +0000 (UTC) (envelope-from nyan@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id m9J9gA7G039548; Sun, 19 Oct 2008 09:42:10 GMT (envelope-from nyan@svn.freebsd.org) Received: (from nyan@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id m9J9gAsS039541; Sun, 19 Oct 2008 09:42:10 GMT (envelope-from nyan@svn.freebsd.org) Message-Id: <200810190942.m9J9gAsS039541@svn.freebsd.org> From: Takahashi Yoshihiro Date: Sun, 19 Oct 2008 09:42:10 +0000 (UTC) To: src-committers@freebsd.org, svn-src-user@freebsd.org X-SVN-Group: user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r184056 - in user/nyan/pc98/sys: conf dev/ic dev/uart modules/uart pc98/conf pc98/pc98 X-BeenThere: svn-src-user@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the experimental " user" src tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 19 Oct 2008 09:42:10 -0000 Author: nyan Date: Sun Oct 19 09:42:09 2008 New Revision: 184056 URL: http://svn.freebsd.org/changeset/base/184056 Log: Add an experimental i8251 support. It does not work yet. Added: user/nyan/pc98/sys/dev/uart/uart_dev_i8251.c (contents, props changed) user/nyan/pc98/sys/dev/uart/uart_dev_i8251.h (contents, props changed) Modified: user/nyan/pc98/sys/conf/files user/nyan/pc98/sys/dev/ic/i8251.h user/nyan/pc98/sys/dev/uart/uart.h user/nyan/pc98/sys/dev/uart/uart_core.c user/nyan/pc98/sys/dev/uart/uart_cpu_pc98.c user/nyan/pc98/sys/dev/uart/uart_subr.c user/nyan/pc98/sys/modules/uart/Makefile user/nyan/pc98/sys/pc98/conf/DEFAULTS user/nyan/pc98/sys/pc98/conf/GENERIC user/nyan/pc98/sys/pc98/conf/GENERIC.hints user/nyan/pc98/sys/pc98/pc98/busiosubr.c Modified: user/nyan/pc98/sys/conf/files ============================================================================== --- user/nyan/pc98/sys/conf/files Sun Oct 19 09:10:44 2008 (r184055) +++ user/nyan/pc98/sys/conf/files Sun Oct 19 09:42:09 2008 (r184056) @@ -1297,6 +1297,7 @@ dev/uart/uart_bus_puc.c optional uart p dev/uart/uart_bus_scc.c optional uart scc dev/uart/uart_core.c optional uart dev/uart/uart_dbg.c optional uart gdb +dev/uart/uart_dev_i8251.c optional uart uart_i8251 dev/uart/uart_dev_ns8250.c optional uart uart_ns8250 dev/uart/uart_dev_quicc.c optional uart quicc dev/uart/uart_dev_sab82532.c optional uart uart_sab82532 Modified: user/nyan/pc98/sys/dev/ic/i8251.h ============================================================================== --- user/nyan/pc98/sys/dev/ic/i8251.h Sun Oct 19 09:10:44 2008 (r184055) +++ user/nyan/pc98/sys/dev/ic/i8251.h Sun Oct 19 09:42:09 2008 (r184056) @@ -69,6 +69,7 @@ #define STS8251_FE 0x20 /* framing error */ #define STS8251_BI 0x40 /* break detect */ #define STS8251_DSR 0x80 /* DSR is asserted */ +#define STS8251_RCV_ERR 0x78 /* i8251F line status register */ #define FLSR_TxEMP 0x01 /* transmit buffer EMPTY */ @@ -77,6 +78,7 @@ #define FLSR_OE 0x10 /* overrun error */ #define FLSR_PE 0x20 /* perity error */ #define FLSR_BI 0x80 /* break detect */ +#define FLSR_RCV_ERR 0xb0 /* i8251F modem status register */ #define MSR_DCD 0x80 /* Current Data Carrier Detect */ Modified: user/nyan/pc98/sys/dev/uart/uart.h ============================================================================== --- user/nyan/pc98/sys/dev/uart/uart.h Sun Oct 19 09:10:44 2008 (r184055) +++ user/nyan/pc98/sys/dev/uart/uart.h Sun Oct 19 09:42:09 2008 (r184056) @@ -38,6 +38,9 @@ struct uart_bas { bus_space_tag_t bst; bus_space_handle_t bsh; +#ifdef PC98 + u_int type; +#endif u_int chan; u_int rclk; u_int regshft; @@ -64,6 +67,9 @@ struct uart_bas { */ struct uart_class; +#ifdef PC98 +extern struct uart_class uart_i8251_class __attribute__((weak)); +#endif extern struct uart_class uart_ns8250_class __attribute__((weak)); extern struct uart_class uart_quicc_class __attribute__((weak)); extern struct uart_class uart_sab82532_class __attribute__((weak)); Modified: user/nyan/pc98/sys/dev/uart/uart_core.c ============================================================================== --- user/nyan/pc98/sys/dev/uart/uart_core.c Sun Oct 19 09:10:44 2008 (r184055) +++ user/nyan/pc98/sys/dev/uart/uart_core.c Sun Oct 19 09:42:09 2008 (r184056) @@ -376,6 +376,9 @@ uart_bus_probe(device_t dev, int regshft */ sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres); sc->sc_bas.bst = rman_get_bustag(sc->sc_rres); +#ifdef PC98 + sc->sc_bas.type = 0; +#endif sc->sc_bas.chan = chan; sc->sc_bas.regshft = regshft; sc->sc_bas.rclk = (rclk == 0) ? sc->sc_class->uc_rclk : rclk; Modified: user/nyan/pc98/sys/dev/uart/uart_cpu_pc98.c ============================================================================== --- user/nyan/pc98/sys/dev/uart/uart_cpu_pc98.c Sun Oct 19 09:10:44 2008 (r184055) +++ user/nyan/pc98/sys/dev/uart/uart_cpu_pc98.c Sun Oct 19 09:42:09 2008 (r184056) @@ -44,6 +44,7 @@ static struct { u_long iobase; struct uart_class *class; } uart_pc98_devs[] = { + { 0x30, &uart_i8251_class }, { 0x238, &uart_ns8250_class }, { 0, NULL } }; @@ -76,7 +77,7 @@ uart_cpu_getdev(int devtype, struct uart struct uart_class *class; unsigned int i, ivar; - class = &uart_ns8250_class; + class = &uart_ns8250_class; /* Default is ns8250 class. */ if (class == NULL) return (ENXIO); @@ -112,6 +113,7 @@ uart_cpu_getdev(int devtype, struct uart continue; di->ops = uart_getops(class); + di->bas.type = 0; di->bas.chan = 0; di->bas.bst = uart_bus_space_io; if (bus_space_map(di->bas.bst, ivar, uart_getrange(class), 0, Added: user/nyan/pc98/sys/dev/uart/uart_dev_i8251.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/nyan/pc98/sys/dev/uart/uart_dev_i8251.c Sun Oct 19 09:42:09 2008 (r184056) @@ -0,0 +1,1261 @@ +/*- + * Copyright (c) 2008 TAKAHASHI Yoshihiro + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include "uart_if.h" + +/* + * I/O address table + */ +/* Internal i8251 / i8251F */ +static bus_addr_t i8251_iat[] = + { 0, 2, 2, 2, 3, 5, 0, 0, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a }; +/* PC-9861K */ +static bus_addr_t pc9861k_ext1_iat[] = { 1, 3, 3, 3, 0, 0 }; +static bus_addr_t pc9861k_ext2_iat[] = { 7, 9, 9, 9, 0, 0 }; +/* IND-SS / IND-SP */ +static bus_addr_t indss_ext1_iat[] = { 1, 3, 3, 3, 0, 0, 3 }; +static bus_addr_t indss_ext2_iat[] = { 7, 9, 9, 9, 0, 0, 9 }; +/* PIO-9032B */ +static bus_addr_t pio9032b_ext1_iat[] = { 1, 3, 3, 3, 0, 0, 8 }; +static bus_addr_t pio9032b_ext2_iat[] = { 7, 9, 9, 9, 0, 0, 8 }; +/* B98-01 */ +static bus_addr_t b9801_ext1_iat[] = { 1, 3, 3, 3, 0, 0, 0x21, 0x23 }; +static bus_addr_t b9801_ext2_iat[] = { 7, 9, 9, 9, 0, 0, 0x23, 0x25 }; + +/* + * Baudrate table + */ +static struct i8251_speedtab sptab_vfast[] = { + { 9600, 12, }, + { 14400, 8, }, + { 19200, 6, }, + { 28800, 4, }, + { 38400, 3, }, + { 57600, 2, }, + { 115200, 1, }, + { -1, -1 } +}; +static struct i8251_speedtab sptab_pio9032b[] = { + { 300, 6, }, + { 600, 5, }, + { 1200, 4, }, + { 2400, 3, }, + { 4800, 2, }, + { 9600, 1, }, + { 19200, 0, }, + { 38400, 7, }, + { -1, -1 } +}; +static struct i8251_speedtab sptab_b9801[] = { + { 75, 11, }, + { 150, 10, }, + { 300, 9, }, + { 600, 8, }, + { 1200, 7, }, + { 2400, 6, }, + { 4800, 5, }, + { 9600, 4, }, + { 19200, 3, }, + { 38400, 2, }, + { 76800, 1, }, + { 153600, 0, }, + { -1, -1 } +}; + +/* + * Hardware specific table + */ +struct i8251_hw_table i8251_hw[] = { + { "internal", i8251_iat, BUS_SPACE_IAT_SZ(i8251_iat), + sptab_vfast }, + { "PC-9861K", pc9861k_ext1_iat, BUS_SPACE_IAT_SZ(pc9861k_ext1_iat), + NULL }, + { "PC-9861K", pc9861k_ext2_iat, BUS_SPACE_IAT_SZ(pc9861k_ext2_iat), + NULL }, + { "IND-SS / IND-SP", indss_ext1_iat, BUS_SPACE_IAT_SZ(indss_ext1_iat), + NULL }, + { "IND-SS / IND-SP", indss_ext2_iat, BUS_SPACE_IAT_SZ(indss_ext2_iat), + NULL }, + { "PIO-9032B", pio9032b_ext1_iat, BUS_SPACE_IAT_SZ(pio9032b_ext1_iat), + sptab_pio9032b }, + { "PIO-9032B", pio9032b_ext2_iat, BUS_SPACE_IAT_SZ(pio9032b_ext2_iat), + sptab_pio9032b }, + { "B98-01", b9801_ext1_iat, BUS_SPACE_IAT_SZ(b9801_ext1_iat), + sptab_b9801 }, + { "B98-01", b9801_ext2_iat, BUS_SPACE_IAT_SZ(b9801_ext2_iat), + sptab_b9801 }, +}; + + +static void +i8251_probe_fifo(struct uart_bas *bas) +{ + u_int8_t t1, t2; + + t1 = uart_getreg(bas, serf_iir); + DELAY(10); + t2 = uart_getreg(bas, serf_iir); + + if ((t1 & IIR_FIFO_CK1) == (t2 & IIR_FIFO_CK1)) + return; + if ((t1 & IIR_FIFO_CK2) != 0 || (t2 & IIR_FIFO_CK2) != 0) + return; + +#ifndef I8251_DISABLE_FIFO + SET_TYPE(bas, COM_SUB_I8251F); +#endif +} + +static void +i8251_probe_vfast(struct uart_bas *bas) +{ + + uart_setreg(bas, serf_div, 0); + if (uart_getreg(bas, serf_div) & 0x80) + return; + +#ifndef I8251_DISABLE_FIFO + SET_TYPE(bas, COM_SUB_I8251VFAST); +#endif +} + +static void +i8251_reset(struct uart_bas *bas, u_int8_t mode, int force) +{ + + if (force) { + uart_setreg(bas, seri_cmd, 0); + DELAY(30); + uart_setreg(bas, seri_cmd, 0); + DELAY(30); + uart_setreg(bas, seri_cmd, 0); + DELAY(30); + } + + uart_setreg(bas, seri_cmd, CMD8251_RESET); + DELAY(100); + uart_setreg(bas, seri_mod, mode); + DELAY(100); +} + +static __inline void +i8251_write_cmd(struct uart_bas *bas, u_int8_t cmd) +{ + + if (GET_SUBTYPE(bas) & COM_SUB_I8251F) { + uart_setreg(bas, serf_fcr, 0); + uart_setreg(bas, seri_cmd, cmd); + uart_setreg(bas, serf_fcr, I8251F_DEF_FIFO); + } else + uart_setreg(bas, seri_cmd, cmd); +} + +static __inline void +i8251_init_func(struct uart_bas *bas) +{ + + uart_setreg(bas, seri_func, 0xf2); +} + +static __inline void +i8251_enable_fifo(struct uart_bas *bas) +{ + + if (GET_SUBTYPE(bas) & COM_SUB_I8251F) + uart_setreg(bas, serf_fcr, + I8251F_DEF_FIFO | FIFO_XMT_RST | FIFO_RCV_RST); +} + +static __inline void +i8251_disable_fifo(struct uart_bas *bas) +{ + + if (GET_SUBTYPE(bas) & COM_SUB_I8251F) + uart_setreg(bas, serf_fcr, 0); +} + +static __inline void +i8251_data_putc(struct uart_bas *bas, u_int8_t c) +{ + + if (GET_SUBTYPE(bas) & COM_SUB_I8251F) + uart_setreg(bas, serf_data, c); + else + uart_setreg(bas, seri_data, c); +} + +static __inline u_int8_t +i8251_data_getc(struct uart_bas *bas) +{ + u_int8_t c; + + if (GET_SUBTYPE(bas) & COM_SUB_I8251F) + c = uart_getreg(bas, serf_data); + else + c = uart_getreg(bas, seri_data); + + return (c); +} + +static __inline int +i8251_check_rxready(struct uart_bas *bas) +{ + + if (GET_SUBTYPE(bas) & COM_SUB_I8251F) + return (uart_getreg(bas, serf_lsr) & FLSR_RxRDY); + else + return (uart_getreg(bas, seri_lsr) & STS8251_RxRDY); +} + +static __inline int +i8251_check_txready(struct uart_bas *bas) +{ + + if (GET_SUBTYPE(bas) & COM_SUB_I8251F) + return (uart_getreg(bas, serf_lsr) & FLSR_TxRDY); + else + return (uart_getreg(bas, seri_lsr) & STS8251_TxRDY); +} + +static __inline int +i8251_check_txempty(struct uart_bas *bas) +{ + + if (GET_SUBTYPE(bas) & COM_SUB_I8251F) + return (uart_getreg(bas, serf_lsr) & FLSR_TxEMP); + else + return (uart_getreg(bas, seri_lsr) & STS8251_TxEMP); +} + +static u_int8_t +i8251_read_lsr(struct uart_bas *bas) +{ + u_int8_t stat, lsr; + + if (GET_SUBTYPE(bas) & COM_SUB_I8251F) { + stat = uart_getreg(bas, serf_lsr); + lsr = 0; + if (stat & FLSR_TxEMP) + lsr |= STS8251_TxEMP; + if (stat & FLSR_TxRDY) + lsr |= STS8251_TxRDY; + if (stat & FLSR_RxRDY) + lsr |= STS8251_RxRDY; + if (stat & FLSR_OE) + lsr |= STS8251_OE; + if (stat & FLSR_PE) + lsr |= STS8251_PE; + if (stat & FLSR_BI) + lsr |= STS8251_BI; + } else { + lsr = uart_getreg(bas, seri_lsr); + } + + return (lsr); +} + +static u_int8_t +i8251_read_msr(struct uart_bas *bas) +{ + static u_int8_t msr_translate_tbl[] = { + 0, + MSR_DCD, + MSR_CTS, + MSR_DCD | MSR_CTS, + MSR_RI, + MSR_RI | MSR_DCD, + MSR_RI | MSR_CTS, + MSR_RI | MSR_CTS | MSR_DCD + }; + u_int8_t stat, msr; + + if (GET_SUBTYPE(bas) & COM_SUB_I8251F) + return (uart_getreg(bas, serf_msr)); + + stat = (uart_getreg(bas, seri_msr) ^ 0xff) >> 5; + msr = msr_translate_tbl[stat]; + + stat = uart_getreg(bas, seri_lsr); + if (stat & STS8251_DSR) + msr |= MSR_DSR; + + return (msr); +} + +static void +i8251_set_icr(struct uart_bas *bas, u_int8_t icr) +{ + u_int8_t tmp; + + tmp = 0; + if (GET_IFTYPE(bas) == COM_IF_INTERNAL) { + tmp = uart_getreg(bas, seri_icr); + tmp &= ~(IEN_Rx | IEN_TxEMP | IEN_Tx); + } + tmp |= icr & (IEN_Rx | IEN_TxEMP | IEN_Tx); + + uart_setreg(bas, seri_icr, tmp); +} + + +/* + * Clear pending interrupts. THRE is cleared by reading IIR. Data + * that may have been received gets lost here. + */ +static void +i8251_clrint(struct uart_bas *bas) +{ + u_int8_t iir; + + if (GET_SUBTYPE(bas) & COM_SUB_I8251F) { + iir = uart_getreg(bas, serf_iir); + while ((iir & IIR_NOPEND) == 0) { + iir &= IIR_IMASK; + if (iir == IIR_RLS) + (void)uart_getreg(bas, serf_lsr); + else if (iir == IIR_RXRDY || iir == IIR_RXTOUT) + (void)uart_getreg(bas, serf_data); + else if (iir == IIR_MLSC) + (void)uart_getreg(bas, serf_msr); + iir = uart_getreg(bas, serf_iir); + } + } else { + if (uart_getreg(bas, seri_lsr) & STS8251_RxRDY) + (void)uart_getreg(bas, seri_data); + } +} + +static int +i8251_drain(struct uart_bas *bas, int what) +{ + int delay, limit; + + delay = 100; + + if (what & UART_DRAIN_TRANSMITTER) { + /* + * Pick an arbitrary high limit to avoid getting stuck in + * an infinite loop when the hardware is broken. Make the + * limit high enough to handle large FIFOs. + */ + limit = 10*256; + while ((uart_getreg(bas, seri_lsr) & STS8251_TxEMP) == 0 && + --limit) + DELAY(delay); + if (limit == 0) { + /* printf("i8251: transmitter appears stuck... "); */ + return (EIO); + } + } + + if (what & UART_DRAIN_RECEIVER) { + /* + * Pick an arbitrary high limit to avoid getting stuck in + * an infinite loop when the hardware is broken. Make the + * limit high enough to handle large FIFOs and integrated + * UARTs. + */ + limit=10*1024; + while ((uart_getreg(bas, seri_lsr) & STS8251_RxRDY) && + --limit) { + (void)uart_getreg(bas, seri_data); + DELAY(delay << 2); + } + if (limit == 0) { + /* printf("i8251: receiver appears broken... "); */ + return (EIO); + } + } + + return (0); +} + +/* + * We can only flush UARTs with FIFOs. UARTs without FIFOs should be + * drained. WARNING: this function clobbers the FIFO setting! + */ +static void +i8251_flush(struct uart_bas *bas, int what) +{ + u_int8_t fcr; + + fcr = FIFO_ENABLE; + if (what & UART_FLUSH_TRANSMITTER) + fcr |= FIFO_XMT_RST; + if (what & UART_FLUSH_RECEIVER) + fcr |= FIFO_RCV_RST; + uart_setreg(bas, serf_fcr, fcr); +} + +static int +i8251_divisor(int rclk, int baudrate) +{ + int actual_baud, divisor; + int error; + + if (baudrate == 0) + return (0); + + divisor = (rclk / (baudrate << 3) + 1) >> 1; + if (divisor == 0 || divisor >= 65536) + return (0); + actual_baud = rclk / (divisor << 4); + + /* 10 times error in percent: */ + error = ((actual_baud - baudrate) * 2000 / baudrate + 1) >> 1; + + /* 3.0% maximum error tolerance: */ + if (error < -30 || error > 30) + return (0); + + return (divisor); +} + +static int +i8251_ttspeedtab(int speed, struct i8251_speedtab *table) +{ + + if (table == NULL) + return (-1); + + for (; table->sp_speed != -1; table++) + if (table->sp_speed == speed) + return (table->sp_code); + + return (-1); +} + +static int +i8251_set_baudrate(struct uart_bas *bas, int baudrate) +{ + int type, divisor, rclk; + + if (baudrate == 0) + return (EINVAL); + + type = GET_IFTYPE(bas); + + switch (type) { + case COM_IF_INTERNAL: + if (GET_SUBTYPE(bas) & COM_SUB_I8251VFAST) { + divisor = i8251_ttspeedtab(baudrate, + i8251_hw[type].sp_tab); + if (divisor != -1) { + divisor |= 0x80; + uart_setreg(bas, serf_div, divisor); + return (0); + } else { + /* Set compatible mode. */ + uart_setreg(bas, serf_div, 0); + } + } + + /* Check system clock. */ + if (pc98_machine_type & M_8M) + rclk = 1996800; /* 8 MHz system */ + else + rclk = 2457600; /* 5 MHz system */ + + divisor = i8251_divisor(rclk, baudrate); + if (divisor < 2) + return (EINVAL); + if (divisor == 3) + outb(TIMER_MODE, + TIMER_SEL2 | TIMER_16BIT | TIMER_RATEGEN); + else + outb(TIMER_MODE, + TIMER_SEL2 | TIMER_16BIT | TIMER_SQWAVE); + DELAY(10); + outb(TIMER_CNTR2, divisor & 0xff); + DELAY(10); + outb(TIMER_CNTR2, (divisor >> 8) & 0xff); + break; + case COM_IF_PC9861K_1: + case COM_IF_PC9861K_2: + /* Cannot set a baudrate. */ + break; + case COM_IF_IND_SS_1: + case COM_IF_IND_SS_2: + divisor = i8251_divisor(460800 * 16, baudrate); + if (divisor == 0) + return (EINVAL); + divisor |= 0x8000; +#if 0 + i8251_reset(bas, I8251_DEF_MODE, 1); +#else + uart_setreg(bas, seri_icr, 0); + uart_setreg(bas, seri_div, 0); +#endif + uart_setreg(bas, seri_cmd, CMD8251_RESET | CMD8251_EH); + uart_setreg(bas, seri_div, (divisor >> 8) & 0xff); + uart_setreg(bas, seri_div, divisor & 0xff); + break; + case COM_IF_PIO9032B_1: + case COM_IF_PIO9032B_2: + case COM_IF_B98_01_1: + case COM_IF_B98_01_2: + divisor = i8251_ttspeedtab(baudrate, i8251_hw[type].sp_tab); + if (divisor == -1) + return (EINVAL); + uart_setreg(bas, seri_div, divisor); + break; + } + + return (0); +} + +static int +i8251_get_baudrate(struct uart_bas *bas) +{ + static int vfast_translate_tbl[] = { + 0, 115200, 57600, 38400, 28800, 0, 19200, 0, + 14400, 0, 0, 0, 9600, 0, 0, 0 + }; + int divisor, rclk; + + switch (GET_IFTYPE(bas)) { + case COM_IF_INTERNAL: + if (GET_SUBTYPE(bas) & COM_SUB_I8251VFAST) { + divisor = uart_getreg(bas, serf_div); + if (divisor & 0x80) + return (vfast_translate_tbl[divisor & 0x0f]); + } + + /* Check system clock */ + if (pc98_machine_type & M_8M) + rclk = 1996800; /* 8 MHz system */ + else + rclk = 2457600; /* 5 MHz system */ + + /* XXX Always set mode3 */ + outb(TIMER_MODE, TIMER_SEL2 | TIMER_16BIT | TIMER_SQWAVE); + DELAY(10); + divisor = inb(TIMER_CNTR2); + DELAY(10); + divisor |= inb(TIMER_CNTR2) << 8; + if (divisor != 0) + return (rclk / divisor / 16); + break; + case COM_IF_PC9861K_1: + case COM_IF_PC9861K_2: + case COM_IF_IND_SS_1: + case COM_IF_IND_SS_2: + case COM_IF_PIO9032B_1: + case COM_IF_PIO9032B_2: + case COM_IF_B98_01_1: + case COM_IF_B98_01_2: + break; + } + + return (0); +} + +static int +i8251_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, + int parity) +{ + u_int8_t mod; + int error; + + mod = 0; + if (databits >= 8) + mod |= MOD8251_8BITS; + else if (databits == 7) + mod |= MOD8251_7BITS; + else if (databits == 6) + mod |= MOD8251_6BITS; + else + mod |= MOD8251_5BITS; + if (stopbits >= 2) + mod |= MOD8251_STOP2; + else if (stopbits == 1) + mod |= MOD8251_STOP1; + if (parity == UART_PARITY_ODD) + mod |= MOD8251_PENAB; + else if (parity == UART_PARITY_EVEN) + mod |= MOD8251_PENAB | MOD8251_PEVEN; + + /* Set baudrate. */ + if (baudrate > 0) { + if ((error = i8251_set_baudrate(bas, baudrate)) != 0) + return (error); + } + + /* Set mode and command register. */ + i8251_disable_fifo(bas); + i8251_reset(bas, mod, 1); + uart_setreg(bas, seri_cmd, I8251_DEF_CMD | CMD8251_ER); + i8251_enable_fifo(bas); + + return (0); +} + +/* + * Low-level UART interface. + */ +static int i8251_probe(struct uart_bas *bas); +static void i8251_init(struct uart_bas *bas, int, int, int, int); +static void i8251_term(struct uart_bas *bas); +static void i8251_putc(struct uart_bas *bas, int); +static int i8251_rxready(struct uart_bas *bas); +static int i8251_getc(struct uart_bas *bas, struct mtx *); + +static struct uart_ops uart_i8251_ops = { + .probe = i8251_probe, + .init = i8251_init, + .term = i8251_term, + .putc = i8251_putc, + .rxready = i8251_rxready, + .getc = i8251_getc, +}; + +static int +i8251_probe(struct uart_bas *bas) +{ + int error; + u_int type; + u_int8_t lsr; + + type = GET_IFTYPE(bas); + + /* Load I/O address table. */ + error = bus_space_map_load(bas->bst, bas->bsh, + i8251_hw[type].iatsz, i8251_hw[type].iat, 0); + if (error) + return (error); + + /* Probe i8251F and V-FAST. */ + if (type == COM_IF_INTERNAL) { + i8251_probe_fifo(bas); + i8251_probe_vfast(bas); + } + + /* + * Clear fifo advanced mode, because line status register has + * no response under the i8251F mode. + */ + i8251_disable_fifo(bas); + + /* Reset i8251. */ + i8251_reset(bas, I8251_DEF_MODE, 1); + + /* Initialize function regsiter for B98-01. */ + if (type == COM_IF_B98_01_1 || type == COM_IF_B98_01_2) + i8251_init_func(bas); + + /* Disable transmit. */ + uart_setreg(bas, seri_cmd, CMD8251_DTR | CMD8251_RTS); + DELAY(100); + + /* Check tx buffer empty. */ + uart_setreg(bas, seri_cmd, CMD8251_DTR | CMD8251_RTS); + lsr = uart_getreg(bas, seri_lsr); + if ((lsr & STS8251_TxRDY) == 0) + return (ENXIO); + + /* Write 2 bytes. */ + uart_setreg(bas, seri_data, ' '); + DELAY(100); + uart_setreg(bas, seri_data, ' '); + DELAY(100); + + /* Check tx buffer non empty. */ + lsr = uart_getreg(bas, seri_lsr); + if ((lsr & STS8251_TxRDY) != 0) + return (ENXIO); + + /* Clear tx buffer. */ + i8251_reset(bas, I8251_DEF_MODE, 0); + + return (0); +} + +static void +i8251_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, + int parity) +{ + + /* Disable the FIFO (if present). */ + i8251_disable_fifo(bas); + + /* Reset i8251. */ + i8251_reset(bas, I8251_DEF_MODE, 1); + + i8251_param(bas, baudrate, databits, stopbits, parity); + + /* Disable the FIFO again. */ + i8251_disable_fifo(bas); + + /* Disable all interrupt sources. */ + i8251_set_icr(bas, 0); + + /* Set RTS & DTR. */ + uart_setreg(bas, seri_cmd, I8251_DEF_CMD); + + i8251_drain(bas, UART_DRAIN_RECEIVER | UART_DRAIN_TRANSMITTER); + + /* Reset and enable FIFO. */ + i8251_enable_fifo(bas); +} + +static void +i8251_term(struct uart_bas *bas) +{ + + /* Clear DTR & RTS. */ + i8251_write_cmd(bas, I8251_DEF_CMD & ~(CMD8251_DTR | CMD8251_RTS)); +} + +static void +i8251_putc(struct uart_bas *bas, int c) +{ + int limit; + + limit = 250000; + while (i8251_check_txready(bas) == 0 && --limit) + DELAY(4); + i8251_data_putc(bas, c); + limit = 250000; + while (i8251_check_txempty(bas) == 0 && --limit) + DELAY(4); +} + +static int +i8251_rxready(struct uart_bas *bas) +{ + + return (i8251_check_rxready(bas) != 0 ? 1 : 0); +} + +static int +i8251_getc(struct uart_bas *bas, struct mtx *hwmtx) +{ + int c; + + uart_lock(hwmtx); + + while (i8251_check_rxready(bas) == 0) { + uart_unlock(hwmtx); + DELAY(4); + uart_lock(hwmtx); + } + + c = i8251_data_getc(bas); + + uart_unlock(hwmtx); + + return (c); +} + +/* + * High-level UART interface. + */ +struct i8251_softc { + struct uart_softc base; + u_int8_t icr; + u_int8_t cmd; + u_int8_t msr; + struct callout_handle timeout_msr; +}; + +static void i8251_enable_msrintr(struct uart_softc *); +static void i8251_disable_msrintr(struct uart_softc *); + +static int i8251_bus_attach(struct uart_softc *); +static int i8251_bus_detach(struct uart_softc *); +static int i8251_bus_flush(struct uart_softc *, int); +static int i8251_bus_getsig(struct uart_softc *); +static int i8251_bus_ioctl(struct uart_softc *, int, intptr_t); +static int i8251_bus_ipend(struct uart_softc *); +static int i8251_bus_param(struct uart_softc *, int, int, int, int); +static int i8251_bus_probe(struct uart_softc *); +static int i8251_bus_receive(struct uart_softc *); +static int i8251_bus_setsig(struct uart_softc *, int); +static int i8251_bus_transmit(struct uart_softc *); + +static kobj_method_t i8251_methods[] = { + KOBJMETHOD(uart_attach, i8251_bus_attach), + KOBJMETHOD(uart_detach, i8251_bus_detach), + KOBJMETHOD(uart_flush, i8251_bus_flush), + KOBJMETHOD(uart_getsig, i8251_bus_getsig), + KOBJMETHOD(uart_ioctl, i8251_bus_ioctl), + KOBJMETHOD(uart_ipend, i8251_bus_ipend), + KOBJMETHOD(uart_param, i8251_bus_param), + KOBJMETHOD(uart_probe, i8251_bus_probe), + KOBJMETHOD(uart_receive, i8251_bus_receive), + KOBJMETHOD(uart_setsig, i8251_bus_setsig), + KOBJMETHOD(uart_transmit, i8251_bus_transmit), + { 0, 0 } +}; + +struct uart_class uart_i8251_class = { + "i8251", + i8251_methods, + sizeof(struct i8251_softc), + .uc_ops = &uart_i8251_ops, + .uc_range = 1, + .uc_rclk = 0 +}; + +#define SIGCHG(c, i, s, d) \ + if (c) { \ + i |= (i & s) ? s : s | d; \ + } else { \ + i = (i & s) ? (i & ~s) | d : i; \ + } + +static int +i8251_bus_attach(struct uart_softc *sc) +{ + struct i8251_softc *i8251; + struct uart_bas *bas; + int type, error; + + i8251 = (struct i8251_softc *)sc; + bas = &sc->sc_bas; + type = GET_IFTYPE(bas); + + /* Load I/O address table. */ + error = bus_space_map_load(bas->bst, bas->bsh, + i8251_hw[type].iatsz, i8251_hw[type].iat, 0); + if (error) + return (error); + + i8251_bus_flush(sc, UART_FLUSH_RECEIVER | UART_FLUSH_TRANSMITTER); + + i8251->cmd = I8251_DEF_CMD; + if (i8251->cmd & CMD8251_DTR) + sc->sc_hwsig |= SER_DTR; + if (i8251->cmd & CMD8251_RTS) + sc->sc_hwsig |= SER_RTS; + i8251_bus_getsig(sc); + + i8251_clrint(bas); + i8251->icr = IEN_Rx; + i8251_set_icr(bas, i8251->icr); + + i8251_enable_msrintr(sc); + + return (0); +} + +static int +i8251_bus_detach(struct uart_softc *sc) +{ + struct uart_bas *bas; + + bas = &sc->sc_bas; + + i8251_disable_msrintr(sc); + + i8251_set_icr(bas, 0); + i8251_clrint(bas); + + return (0); +} + +static int +i8251_bus_flush(struct uart_softc *sc, int what) +{ + struct i8251_softc *i8251; + struct uart_bas *bas; + int error; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***