svn commit: r184056 - in user/nyan/pc98/sys: conf dev/ic dev/uart modules/uart pc98/conf pc98/pc98

Takahashi Yoshihiro nyan at FreeBSD.org
Sun Oct 19 09:42:10 UTC 2008


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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <machine/bus.h>
+#include <machine/timerreg.h>
+
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_cpu.h>
+#include <dev/uart/uart_bus.h>
+#include <dev/uart/uart_dev_i8251.h>
+
+#include <dev/ic/i8251.h>
+#include <dev/ic/i8255.h>
+
+#include <pc98/pc98/pc98_machdep.h>
+
+#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 ***


More information about the svn-src-user mailing list