116359Sasami/*- 216359Sasami * Copyright (c) 1991 The Regents of the University of California. 316359Sasami * All rights reserved. 416359Sasami * 516359Sasami * Redistribution and use in source and binary forms, with or without 616359Sasami * modification, are permitted provided that the following conditions 716359Sasami * are met: 816359Sasami * 1. Redistributions of source code must retain the above copyright 916359Sasami * notice, this list of conditions and the following disclaimer. 1016359Sasami * 2. Redistributions in binary form must reproduce the above copyright 1116359Sasami * notice, this list of conditions and the following disclaimer in the 1216359Sasami * documentation and/or other materials provided with the distribution. 1316359Sasami * 4. Neither the name of the University nor the names of its contributors 1416359Sasami * may be used to endorse or promote products derived from this software 1516359Sasami * without specific prior written permission. 1616359Sasami * 1716359Sasami * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1816359Sasami * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1916359Sasami * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2016359Sasami * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2116359Sasami * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2216359Sasami * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2316359Sasami * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2416359Sasami * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2516359Sasami * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2616359Sasami * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2716359Sasami * SUCH DAMAGE. 2816359Sasami * 2950477Speter * $FreeBSD$ 3016359Sasami * from: @(#)com.c 7.5 (Berkeley) 5/16/91 3145816Skato * from: i386/isa sio.c,v 1.234 3216359Sasami */ 3316359Sasami 3431778Seivind#include "opt_compat.h" 35131939Smarcel#include "opt_gdb.h" 36131939Smarcel#include "opt_kdb.h" 3746871Skato#include "opt_sio.h" 3816359Sasami 3916359Sasami/* 4016359Sasami * Serial driver, based on 386BSD-0.1 com driver. 4116359Sasami * Mostly rewritten to use pseudo-DMA. 4216359Sasami * Works for National Semiconductor NS8250-NS16550AF UARTs. 4316359Sasami * COM driver, based on HP dca driver. 4416359Sasami * 45150750Snyan * Changes for PC Card integration: 46150750Snyan * - Added PC Card driver table and handlers 4716359Sasami */ 4816359Sasami/*=============================================================== 4916359Sasami * 386BSD(98),FreeBSD-1.1x(98) com driver. 5016359Sasami * ----- 5116359Sasami * modified for PC9801 by M.Ishii 5216359Sasami * Kyoto University Microcomputer Club (KMC) 5316359Sasami * Chou "TEFUTEFU" Hirotomi 5416359Sasami * Kyoto Univ. the faculty of medicine 5516359Sasami *=============================================================== 5616359Sasami * FreeBSD-2.0.1(98) sio driver. 5716359Sasami * ----- 5816359Sasami * modified for pc98 Internal i8251 and MICRO CORE MC16550II 5916359Sasami * T.Koike(hfc01340@niftyserve.or.jp) 6016359Sasami * implement kernel device configuration 6116359Sasami * aizu@orient.center.nitech.ac.jp 6216359Sasami * 6316359Sasami * Notes. 6416359Sasami * ----- 6516359Sasami * PC98 localization based on 386BSD(98) com driver. Using its PC98 local 6616359Sasami * functions. 6716359Sasami * This driver is under debugging,has bugs. 6816359Sasami */ 6916359Sasami/* 7016359Sasami * modified for AIWA B98-01 7116359Sasami * by T.Hatanou <hatanou@yasuda.comm.waseda.ac.jp> last update: 15 Sep.1995 7216359Sasami */ 7342262Skato/* 7442262Skato * Modified by Y.Takahashi of Kogakuin University. 7542262Skato */ 7654174Snyan/* 7754174Snyan * modified for 8251(FIFO) by Seigo TANIMURA <tanimura@FreeBSD.org> 7854174Snyan */ 7942262Skato 8016359Sasami#include <sys/param.h> 8176212Skato#include <sys/systm.h> 8265877Skato#include <sys/bus.h> 8316359Sasami#include <sys/conf.h> 8424132Sbde#include <sys/fcntl.h> 8538297Skato#include <sys/interrupt.h> 86131939Smarcel#include <sys/kdb.h> 8716359Sasami#include <sys/kernel.h> 88114216Skan#include <sys/limits.h> 8976212Skato#include <sys/lock.h> 9076212Skato#include <sys/malloc.h> 9176212Skato#include <sys/module.h> 9276212Skato#include <sys/mutex.h> 9376212Skato#include <sys/proc.h> 9476212Skato#include <sys/reboot.h> 95131125Snyan#include <sys/serial.h> 9676212Skato#include <sys/sysctl.h> 9716359Sasami#include <sys/syslog.h> 9876212Skato#include <sys/tty.h> 9945783Skato#include <machine/bus.h> 10045783Skato#include <sys/rman.h> 10145226Skato#include <sys/timepps.h> 10293934Snyan#include <sys/uio.h> 103119525Snyan#include <sys/cons.h> 10416359Sasami 10545783Skato#include <isa/isavar.h> 10616359Sasami 10745783Skato#include <machine/resource.h> 10845783Skato 10985302Simp#include <dev/sio/sioreg.h> 11086912Snyan#include <dev/sio/siovar.h> 11145783Skato 11286912Snyan#ifdef PC98 113146049Snyan#include <pc98/cbus/cbus.h> 11486912Snyan#include <pc98/pc98/pc98_machdep.h> 11586912Snyan#endif 11686912Snyan 11716359Sasami#ifdef COM_ESP 11877962Snyan#include <dev/ic/esp.h> 11916359Sasami#endif 12077962Snyan#include <dev/ic/ns16550.h> 12142265Skato#ifdef PC98 12277962Snyan#include <dev/ic/i8251.h> 123182835Snyan#include <dev/ic/i8255.h> 12477962Snyan#include <dev/ic/rsa.h> 12542265Skato#endif 12616359Sasami 12716359Sasami#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ 12816359Sasami 129128796Snyan/* 130128796Snyan * Meaning of flags: 131128796Snyan * 132128796Snyan * 0x00000001 shared IRQs 133128796Snyan * 0x00000002 disable FIFO 134128796Snyan * 0x00000008 recover sooner from lost output interrupts 135128796Snyan * 0x00000010 device is potential system console 136128796Snyan * 0x00000020 device is forced to become system console 137128796Snyan * 0x00000040 device is reserved for low-level IO 138128796Snyan * 0x00000080 use this port for remote kernel debugging 139128796Snyan * 0x0000??00 minor number of master port 140128796Snyan * 0x00010000 PPS timestamping on CTS instead of DCD 141128796Snyan * 0x00080000 IIR_TXRDY bug 142128796Snyan * 0x00400000 If no comconsole found then mark as a comconsole 143128796Snyan * 0x1?000000 interface type 144128796Snyan */ 145128796Snyan 14616359Sasami#ifdef COM_MULTIPORT 14716359Sasami/* checks in flags for multiport and which is multiport "master chip" 14816359Sasami * for a given card 14916359Sasami */ 15045783Skato#define COM_ISMULTIPORT(flags) ((flags) & 0x01) 15145783Skato#define COM_MPMASTER(flags) (((flags) >> 8) & 0x0ff) 152128796Snyan#ifndef PC98 15345783Skato#define COM_NOTAST4(flags) ((flags) & 0x04) 154128796Snyan#endif 155104134Snyan#else 156104134Snyan#define COM_ISMULTIPORT(flags) (0) 15716359Sasami#endif /* COM_MULTIPORT */ 15816359Sasami 159120809Snyan#define COM_C_IIR_TXRDYBUG 0x80000 16045783Skato#define COM_CONSOLE(flags) ((flags) & 0x10) 161120809Snyan#define COM_DEBUGGER(flags) ((flags) & 0x80) 162128796Snyan#ifndef PC98 163120809Snyan#define COM_FIFOSIZE(flags) (((flags) & 0xff000000) >> 24) 164128796Snyan#endif 16545783Skato#define COM_FORCECONSOLE(flags) ((flags) & 0x20) 166120809Snyan#define COM_IIR_TXRDYBUG(flags) ((flags) & COM_C_IIR_TXRDYBUG) 16745783Skato#define COM_LLCONSOLE(flags) ((flags) & 0x40) 16845783Skato#define COM_LOSESOUTINTS(flags) ((flags) & 0x08) 169120809Snyan#define COM_NOFIFO(flags) ((flags) & 0x02) 170128796Snyan#ifndef PC98 171120809Snyan#define COM_NOSCR(flags) ((flags) & 0x100000) 172128796Snyan#endif 173112032Snyan#define COM_PPSCTS(flags) ((flags) & 0x10000) 174128796Snyan#ifndef PC98 175120809Snyan#define COM_ST16650A(flags) ((flags) & 0x20000) 176120809Snyan#define COM_TI16754(flags) ((flags) & 0x200000) 177128796Snyan#endif 17816359Sasami 17960472Snyan#define sio_getreg(com, off) \ 18060472Snyan (bus_space_read_1((com)->bst, (com)->bsh, (off))) 18160472Snyan#define sio_setreg(com, off, value) \ 18260472Snyan (bus_space_write_1((com)->bst, (com)->bsh, (off), (value))) 18360472Snyan 18416359Sasami/* 18516359Sasami * com state bits. 18616359Sasami * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher 18716359Sasami * than the other bits so that they can be tested as a group without masking 18816359Sasami * off the low bits. 18916359Sasami * 19016359Sasami * The following com and tty flags correspond closely: 19116359Sasami * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and 19251654Sphk * comstop()) 19316359Sasami * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) 19416359Sasami * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) 19516359Sasami * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) 19616359Sasami * TS_FLUSH is not used. 19716359Sasami * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. 19816359Sasami * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). 19916359Sasami */ 20016359Sasami#define CS_BUSY 0x80 /* output in progress */ 20116359Sasami#define CS_TTGO 0x40 /* output not stopped by XOFF */ 20216359Sasami#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ 20316359Sasami#define CS_CHECKMSR 1 /* check of MSR scheduled */ 20416359Sasami#define CS_CTS_OFLOW 2 /* use CTS output flow control */ 20516359Sasami#define CS_ODONE 4 /* output completed */ 20616359Sasami#define CS_RTS_IFLOW 8 /* use RTS input flow control */ 20725026Skato#define CSE_BUSYCHECK 1 /* siobusycheck() scheduled */ 20816359Sasami 20916359Sasamistatic char const * const error_desc[] = { 21016359Sasami#define CE_OVERRUN 0 21116359Sasami "silo overflow", 21216359Sasami#define CE_INTERRUPT_BUF_OVERFLOW 1 21316359Sasami "interrupt-level buffer overflow", 21416359Sasami#define CE_TTY_BUF_OVERFLOW 2 21516359Sasami "tty-level buffer overflow", 21616359Sasami}; 21716359Sasami 21816359Sasami#define CE_NTYPES 3 21916359Sasami#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) 22016359Sasami 22116359Sasami/* types. XXX - should be elsewhere */ 22216359Sasamitypedef u_int Port_t; /* hardware port */ 22316359Sasamitypedef u_char bool_t; /* boolean */ 22416359Sasami 22516359Sasami/* queue of linear buffers */ 22616359Sasamistruct lbq { 22716359Sasami u_char *l_head; /* next char to process */ 22816359Sasami u_char *l_tail; /* one past the last char to process */ 22916359Sasami struct lbq *l_next; /* next in queue */ 23016359Sasami bool_t l_queued; /* nonzero if queued */ 23116359Sasami}; 23216359Sasami 23316359Sasami/* com device structure */ 23416359Sasamistruct com_s { 23516359Sasami u_char state; /* miscellaneous flag bits */ 23616359Sasami u_char cfcr_image; /* copy of value written to CFCR */ 23716359Sasami#ifdef COM_ESP 23816359Sasami bool_t esp; /* is this unit a hayes esp board? */ 23916359Sasami#endif 24025026Skato u_char extra_state; /* more flag bits, separate for order trick */ 24116359Sasami u_char fifo_image; /* copy of value written to FIFO */ 24216359Sasami bool_t hasfifo; /* nonzero for 16550 UARTs */ 24316359Sasami bool_t loses_outints; /* nonzero if device loses output interrupts */ 24416359Sasami u_char mcr_image; /* copy of value written to MCR */ 24516359Sasami#ifdef COM_MULTIPORT 24616359Sasami bool_t multiport; /* is this unit part of a multiport device? */ 24716359Sasami#endif /* COM_MULTIPORT */ 24816359Sasami bool_t no_irq; /* nonzero if irq is not attached */ 24916359Sasami bool_t gone; /* hardware disappeared */ 25016359Sasami bool_t poll; /* nonzero if polling is required */ 25116359Sasami bool_t poll_output; /* nonzero if polling for output is required */ 252120809Snyan bool_t st16650a; /* nonzero if Startech 16650A compatible */ 25316359Sasami int unit; /* unit number */ 254120809Snyan u_int flags; /* copy of device flags */ 25516359Sasami u_int tx_fifo_size; 25616359Sasami 25716359Sasami /* 25816359Sasami * The high level of the driver never reads status registers directly 25916359Sasami * because there would be too many side effects to handle conveniently. 26016359Sasami * Instead, it reads copies of the registers stored here by the 26116359Sasami * interrupt handler. 26216359Sasami */ 26316359Sasami u_char last_modem_status; /* last MSR read by intr handler */ 26416359Sasami u_char prev_modem_status; /* last MSR handled by high level */ 26516359Sasami 26616359Sasami u_char *ibuf; /* start of input buffer */ 26716359Sasami u_char *ibufend; /* end of input buffer */ 26843663Skato u_char *ibufold; /* old input buffer, to be freed */ 26916359Sasami u_char *ihighwater; /* threshold in input buffer */ 27016359Sasami u_char *iptr; /* next free spot in input buffer */ 27143663Skato int ibufsize; /* size of ibuf (not include error bytes) */ 27243663Skato int ierroff; /* offset of error bytes in ibuf */ 27316359Sasami 27416359Sasami struct lbq obufq; /* head of queue of output buffers */ 27516359Sasami struct lbq obufs[2]; /* output buffers */ 27616359Sasami 27760472Snyan bus_space_tag_t bst; 27860472Snyan bus_space_handle_t bsh; 27960472Snyan 28016359Sasami#ifdef PC98 28116359Sasami Port_t cmd_port; 28216359Sasami Port_t sts_port; 28316359Sasami Port_t in_modem_port; 28416359Sasami Port_t intr_ctrl_port; 285108533Sschweikh Port_t rsabase; /* Iobase address of an I/O-DATA RSA board. */ 28616359Sasami int intr_enable; 28716359Sasami int pc98_prev_modem_status; 28816359Sasami int pc98_modem_delta; 28916359Sasami int modem_car_chg_timer; 29016359Sasami int pc98_prev_siocmd; 29116359Sasami int pc98_prev_siomod; 29216359Sasami int modem_checking; 29316359Sasami int pc98_if_type; 29454174Snyan 29554174Snyan bool_t pc98_8251fifo; 29654174Snyan bool_t pc98_8251fifo_enable; 29716359Sasami#endif /* PC98 */ 29816359Sasami Port_t data_port; /* i/o ports */ 29916359Sasami#ifdef COM_ESP 30016359Sasami Port_t esp_port; 30116359Sasami#endif 302120809Snyan Port_t int_ctl_port; 30316359Sasami Port_t int_id_port; 30416359Sasami Port_t modem_ctl_port; 30516359Sasami Port_t line_status_port; 30616359Sasami Port_t modem_status_port; 30716359Sasami 30816359Sasami struct tty *tp; /* cross reference */ 30916359Sasami 31045226Skato struct pps_state pps; 311112032Snyan int pps_bit; 312225203Srwatson#ifdef KDB 313119525Snyan int alt_brk_state; 314119525Snyan#endif 31516359Sasami 31616359Sasami u_long bytes_in; /* statistics */ 31716359Sasami u_long bytes_out; 31816359Sasami u_int delta_error_counts[CE_NTYPES]; 31916359Sasami u_long error_counts[CE_NTYPES]; 32016359Sasami 32190011Snyan u_long rclk; 32290011Snyan 32351202Snyan struct resource *irqres; 32451202Snyan struct resource *ioportres; 325118353Snyan int ioportrid; 326118353Snyan void *cookie; 32751202Snyan 32816359Sasami /* 32942262Skato * Data area for output buffers. Someday we should build the output 33042262Skato * buffer queue without copying data. 33142262Skato */ 33243663Skato#ifdef PC98 33343663Skato int obufsize; 33442262Skato u_char *obuf1; 33542262Skato u_char *obuf2; 33642262Skato#else 33716359Sasami u_char obuf1[256]; 33816359Sasami u_char obuf2[256]; 33942262Skato#endif 34016359Sasami}; 34116359Sasami 34216359Sasami#ifdef COM_ESP 34392793Skatostatic int espattach(struct com_s *com, Port_t esp_port); 34416359Sasami#endif 34545783Skato 346131403Snyanstatic void combreak(struct tty *tp, int sig); 34720129Sasamistatic timeout_t siobusycheck; 34892793Skatostatic u_int siodivisor(u_long rclk, speed_t speed); 349136550Snyanstatic void comclose(struct tty *tp); 350136550Snyanstatic int comopen(struct tty *tp, struct cdev *dev); 35192793Skatostatic void sioinput(struct com_s *com); 35292793Skatostatic void siointr1(struct com_s *com); 353166901Spisostatic int siointr(void *arg); 354131125Snyanstatic int commodem(struct tty *tp, int sigon, int sigoff); 35592793Skatostatic int comparam(struct tty *tp, struct termios *t); 35692793Skatostatic void siopoll(void *); 35792793Skatostatic void siosettimeout(void); 35892793Skatostatic int siosetwater(struct com_s *com, speed_t speed); 35992793Skatostatic void comstart(struct tty *tp); 36092793Skatostatic void comstop(struct tty *tp, int rw); 36116359Sasamistatic timeout_t comwakeup; 36216359Sasami 36386912Snyanchar sio_driver_name[] = "sio"; 36471713Snyanstatic struct mtx sio_lock; 36571713Snyanstatic int sio_inited; 36616359Sasami 36716359Sasami/* table and macro for fast conversion from a unit number to its com struct */ 36886912Snyandevclass_t sio_devclass; 36945783Skato#define com_addr(unit) ((struct com_s *) \ 37086912Snyan devclass_get_softc(sio_devclass, unit)) /* XXX */ 37116359Sasami 37245783Skatoint comconsole = -1; 37326478Skatostatic volatile speed_t comdefaultrate = CONSPEED; 37490011Snyanstatic u_long comdefaultrclk = DEFAULT_RCLK; 37590011SnyanSYSCTL_ULONG(_machdep, OID_AUTO, conrclk, CTLFLAG_RW, &comdefaultrclk, 0, ""); 37698401Sn_hibmastatic speed_t gdbdefaultrate = GDBSPEED; 37798401Sn_hibmaSYSCTL_UINT(_machdep, OID_AUTO, gdbspeed, CTLFLAG_RW, 37898401Sn_hibma &gdbdefaultrate, GDBSPEED, ""); 37916359Sasamistatic u_int com_events; /* input chars + weighted output completions */ 38024655Skatostatic Port_t siocniobase; 38198431Snyanstatic int siocnunit = -1; 38272431Skatostatic void *sio_slow_ih; 38372431Skatostatic void *sio_fast_ih; 38416359Sasamistatic int sio_timeout; 38516359Sasamistatic int sio_timeouts_until_log; 38629715Skatostatic struct callout_handle sio_timeout_handle 38729715Skato = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); 38853373Snyanstatic int sio_numunits; 38916359Sasami 39016359Sasami#ifdef PC98 39116359Sasamistruct siodev { 39216359Sasami short if_type; 39316359Sasami short irq; 39416359Sasami Port_t cmd, sts, ctrl, mod; 39542262Skato}; 39616359Sasamistatic int sysclock; 39742262Skato 39816359Sasami#define COM_INT_DISABLE {int previpri; previpri=spltty(); 39916359Sasami#define COM_INT_ENABLE splx(previpri);} 40016359Sasami#define IEN_TxFLAG IEN_Tx 40116359Sasami 40216359Sasami#define COM_CARRIER_DETECT_EMULATE 0 40316359Sasami#define PC98_CHECK_MODEM_INTERVAL (hz/10) 40416359Sasami#define DCD_OFF_TOLERANCE 2 40516359Sasami#define DCD_ON_RECOGNITION 2 40653373Snyan#define IS_8251(if_type) (!(if_type & 0x10)) 40742262Skato#define COM1_EXT_CLOCK 0x40000 40816359Sasami 409130585Sphkstatic void commint(struct cdev *dev); 41092793Skatostatic void com_tiocm_bis(struct com_s *com, int msr); 41192793Skatostatic void com_tiocm_bic(struct com_s *com, int msr); 41292793Skatostatic int com_tiocm_get(struct com_s *com); 41392793Skatostatic int com_tiocm_get_delta(struct com_s *com); 414130585Sphkstatic void pc98_msrint_start(struct cdev *dev); 41592793Skatostatic void com_cflag_and_speed_set(struct com_s *com, int cflag, int speed); 41692793Skatostatic int pc98_ttspeedtab(struct com_s *com, int speed, u_int *divisor); 41792793Skatostatic int pc98_get_modem_status(struct com_s *com); 41816359Sasamistatic timeout_t pc98_check_msr; 41992793Skatostatic void pc98_set_baud_rate(struct com_s *com, u_int count); 42092793Skatostatic void pc98_i8251_reset(struct com_s *com, int mode, int command); 42192793Skatostatic void pc98_disable_i8251_interrupt(struct com_s *com, int mod); 42292793Skatostatic void pc98_enable_i8251_interrupt(struct com_s *com, int mod); 42392793Skatostatic int pc98_check_i8251_interrupt(struct com_s *com); 42492793Skatostatic int pc98_i8251_get_cmd(struct com_s *com); 42592793Skatostatic int pc98_i8251_get_mod(struct com_s *com); 42692793Skatostatic void pc98_i8251_set_cmd(struct com_s *com, int x); 42792793Skatostatic void pc98_i8251_or_cmd(struct com_s *com, int x); 42892793Skatostatic void pc98_i8251_clear_cmd(struct com_s *com, int x); 42992793Skatostatic void pc98_i8251_clear_or_cmd(struct com_s *com, int clr, int x); 43092793Skatostatic int pc98_check_if_type(device_t dev, struct siodev *iod); 43192793Skatostatic int pc98_check_8251vfast(void); 43292793Skatostatic int pc98_check_8251fifo(void); 43392793Skatostatic void pc98_check_sysclock(void); 43492793Skatostatic void pc98_set_ioport(struct com_s *com); 43516359Sasami 43616359Sasami#define com_int_Tx_disable(com) \ 43716359Sasami pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP) 43816359Sasami#define com_int_Tx_enable(com) \ 43916359Sasami pc98_enable_i8251_interrupt(com,IEN_TxFLAG) 44016359Sasami#define com_int_Rx_disable(com) \ 44116359Sasami pc98_disable_i8251_interrupt(com,IEN_Rx) 44216359Sasami#define com_int_Rx_enable(com) \ 44316359Sasami pc98_enable_i8251_interrupt(com,IEN_Rx) 44416359Sasami#define com_int_TxRx_disable(com) \ 44516359Sasami pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP|IEN_Rx) 44616359Sasami#define com_int_TxRx_enable(com) \ 44716359Sasami pc98_enable_i8251_interrupt(com,IEN_TxFLAG|IEN_Rx) 44816359Sasami#define com_send_break_on(com) \ 449131125Snyan (IS_8251((com)->pc98_if_type) ? \ 450131125Snyan pc98_i8251_or_cmd((com), CMD8251_SBRK) : \ 451131125Snyan sio_setreg((com), com_cfcr, (com)->cfcr_image |= CFCR_SBREAK)) 45216359Sasami#define com_send_break_off(com) \ 453131125Snyan (IS_8251((com)->pc98_if_type) ? \ 454131125Snyan pc98_i8251_clear_cmd((com), CMD8251_SBRK) : \ 455131125Snyan sio_setreg((com), com_cfcr, (com)->cfcr_image &= ~CFCR_SBREAK)) 45616359Sasami 45742262Skatostatic struct speedtab pc98speedtab[] = { /* internal RS232C interface */ 45843539Skato { 0, 0, }, 45943539Skato { 50, 50, }, 46043539Skato { 75, 75, }, 46143539Skato { 150, 150, }, 46243539Skato { 200, 200, }, 46343539Skato { 300, 300, }, 46443539Skato { 600, 600, }, 46543539Skato { 1200, 1200, }, 46643539Skato { 2400, 2400, }, 46743539Skato { 4800, 4800, }, 46843539Skato { 9600, 9600, }, 46943539Skato { 19200, 19200, }, 47043539Skato { 38400, 38400, }, 47143539Skato { 51200, 51200, }, 47243539Skato { 76800, 76800, }, 47343539Skato { 20800, 20800, }, 47443539Skato { 31200, 31200, }, 47543539Skato { 41600, 41600, }, 47643539Skato { 62400, 62400, }, 47743539Skato { -1, -1 } 47816359Sasami}; 47942262Skatostatic struct speedtab pc98fast_speedtab[] = { 48090011Snyan { 9600, 0x80 | (DEFAULT_RCLK / (16 * (9600))), }, 48190011Snyan { 19200, 0x80 | (DEFAULT_RCLK / (16 * (19200))), }, 48290011Snyan { 38400, 0x80 | (DEFAULT_RCLK / (16 * (38400))), }, 48390011Snyan { 57600, 0x80 | (DEFAULT_RCLK / (16 * (57600))), }, 48490011Snyan { 115200, 0x80 | (DEFAULT_RCLK / (16 * (115200))), }, 48543539Skato { -1, -1 } 48642262Skato}; 48742262Skatostatic struct speedtab comspeedtab_pio9032b[] = { 48843539Skato { 300, 6, }, 48943539Skato { 600, 5, }, 49043539Skato { 1200, 4, }, 49143539Skato { 2400, 3, }, 49243539Skato { 4800, 2, }, 49343539Skato { 9600, 1, }, 49443539Skato { 19200, 0, }, 49543539Skato { 38400, 7, }, 49643539Skato { -1, -1 } 49716359Sasami}; 49842262Skatostatic struct speedtab comspeedtab_b98_01[] = { 49943539Skato { 75, 11, }, 50043539Skato { 150, 10, }, 50143539Skato { 300, 9, }, 50243539Skato { 600, 8, }, 50343539Skato { 1200, 7, }, 50443539Skato { 2400, 6, }, 50543539Skato { 4800, 5, }, 50643539Skato { 9600, 4, }, 50743539Skato { 19200, 3, }, 50843539Skato { 38400, 2, }, 50943539Skato { 76800, 1, }, 51043539Skato { 153600, 0, }, 51143539Skato { -1, -1 } 51216359Sasami}; 51390011Snyanstatic struct speedtab comspeedtab_ind[] = { 51443539Skato { 300, 1536, }, 51543539Skato { 600, 768, }, 51643539Skato { 1200, 384, }, 51743539Skato { 2400, 192, }, 51843539Skato { 4800, 96, }, 51943539Skato { 9600, 48, }, 52043539Skato { 19200, 24, }, 52143539Skato { 38400, 12, }, 52243539Skato { 57600, 8, }, 52343539Skato { 115200, 4, }, 52443539Skato { 153600, 3, }, 52543539Skato { 230400, 2, }, 52643539Skato { 460800, 1, }, 52743539Skato { -1, -1 } 52842262Skato}; 52916359Sasami 53042262Skatostruct { 53142262Skato char *name; 53242262Skato short port_table[7]; 53342262Skato short irr_mask; 53442262Skato struct speedtab *speedtab; 53542262Skato short check_irq; 53642262Skato} if_8251_type[] = { 53742262Skato /* COM_IF_INTERNAL */ 53842262Skato { " (internal)", {0x30, 0x32, 0x32, 0x33, 0x35, -1, -1}, 53942262Skato -1, pc98speedtab, 1 }, 54042262Skato /* COM_IF_PC9861K_1 */ 54142262Skato { " (PC9861K)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, -1, -1}, 54242262Skato 3, NULL, 1 }, 54342262Skato /* COM_IF_PC9861K_2 */ 54442262Skato { " (PC9861K)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, -1, -1}, 54542262Skato 3, NULL, 1 }, 54642262Skato /* COM_IF_IND_SS_1 */ 54742262Skato { " (IND-SS)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, 0xb3, -1}, 54890011Snyan 3, comspeedtab_ind, 1 }, 54942262Skato /* COM_IF_IND_SS_2 */ 55042262Skato { " (IND-SS)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, 0xbb, -1}, 55190011Snyan 3, comspeedtab_ind, 1 }, 55242262Skato /* COM_IF_PIO9032B_1 */ 55342262Skato { " (PIO9032B)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, 0xb8, -1}, 55442262Skato 7, comspeedtab_pio9032b, 1 }, 55542262Skato /* COM_IF_PIO9032B_2 */ 55642262Skato { " (PIO9032B)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, 0xba, -1}, 55742262Skato 7, comspeedtab_pio9032b, 1 }, 55842262Skato /* COM_IF_B98_01_1 */ 55942262Skato { " (B98-01)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, 0xd1, 0xd3}, 56042262Skato 7, comspeedtab_b98_01, 0 }, 56142262Skato /* COM_IF_B98_01_2 */ 56242262Skato { " (B98-01)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, 0xd5, 0xd7}, 56342262Skato 7, comspeedtab_b98_01, 0 }, 56442262Skato}; 56542262Skato#define PC98SIO_data_port(type) (if_8251_type[type].port_table[0]) 56642262Skato#define PC98SIO_cmd_port(type) (if_8251_type[type].port_table[1]) 56742262Skato#define PC98SIO_sts_port(type) (if_8251_type[type].port_table[2]) 56842262Skato#define PC98SIO_in_modem_port(type) (if_8251_type[type].port_table[3]) 56942262Skato#define PC98SIO_intr_ctrl_port(type) (if_8251_type[type].port_table[4]) 57042262Skato#define PC98SIO_baud_rate_port(type) (if_8251_type[type].port_table[5]) 57142262Skato#define PC98SIO_func_port(type) (if_8251_type[type].port_table[6]) 57242262Skato 57354174Snyan#define I8251F_data 0x130 57454174Snyan#define I8251F_lsr 0x132 57554174Snyan#define I8251F_msr 0x134 57654174Snyan#define I8251F_iir 0x136 57754174Snyan#define I8251F_fcr 0x138 57854174Snyan#define I8251F_div 0x13a 57954174Snyan 58054174Snyan 58160472Snyanstatic bus_addr_t port_table_0[] = 58260472Snyan {0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007}; 58360472Snyanstatic bus_addr_t port_table_1[] = 58460472Snyan {0x000, 0x002, 0x004, 0x006, 0x008, 0x00a, 0x00c, 0x00e}; 58560472Snyanstatic bus_addr_t port_table_8[] = 58660472Snyan {0x000, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700}; 58761897Snyanstatic bus_addr_t port_table_rsa[] = { 58861897Snyan 0x008, 0x009, 0x00a, 0x00b, 0x00c, 0x00d, 0x00e, 0x00f, 58961897Snyan 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007 59061897Snyan}; 59160472Snyan 59242262Skatostruct { 59360472Snyan char *name; 59460472Snyan short irr_read; 59560472Snyan short irr_write; 59661897Snyan bus_addr_t *iat; 59761897Snyan bus_size_t iatsz; 59890011Snyan u_long rclk; 59942262Skato} if_16550a_type[] = { 60042262Skato /* COM_IF_RSA98 */ 60190011Snyan {" (RSA-98)", -1, -1, port_table_0, IO_COMSIZE, DEFAULT_RCLK}, 60242262Skato /* COM_IF_NS16550 */ 60390011Snyan {"", -1, -1, port_table_0, IO_COMSIZE, DEFAULT_RCLK}, 60442262Skato /* COM_IF_SECOND_CCU */ 60590011Snyan {"", -1, -1, port_table_0, IO_COMSIZE, DEFAULT_RCLK}, 60642262Skato /* COM_IF_MC16550II */ 60761897Snyan {" (MC16550II)", -1, 0x1000, port_table_8, IO_COMSIZE, 60890011Snyan DEFAULT_RCLK * 4}, 60942262Skato /* COM_IF_MCRS98 */ 61090011Snyan {" (MC-RS98)", -1, 0x1000, port_table_8, IO_COMSIZE, DEFAULT_RCLK * 4}, 61142262Skato /* COM_IF_RSB3000 */ 61290011Snyan {" (RSB-3000)", 0xbf, -1, port_table_1, IO_COMSIZE, DEFAULT_RCLK * 10}, 61342262Skato /* COM_IF_RSB384 */ 61490011Snyan {" (RSB-384)", 0xbf, -1, port_table_1, IO_COMSIZE, DEFAULT_RCLK * 10}, 61542262Skato /* COM_IF_MODEM_CARD */ 61690011Snyan {"", -1, -1, port_table_0, IO_COMSIZE, DEFAULT_RCLK}, 61742262Skato /* COM_IF_RSA98III */ 61890011Snyan {" (RSA-98III)", -1, -1, port_table_rsa, 16, DEFAULT_RCLK * 8}, 61942262Skato /* COM_IF_ESP98 */ 62090011Snyan {" (ESP98)", -1, -1, port_table_1, IO_COMSIZE, DEFAULT_RCLK * 4}, 62142262Skato}; 62242262Skato#endif /* PC98 */ 62342262Skato 624131939Smarcel#ifdef GDB 625131939Smarcelstatic Port_t siogdbiobase = 0; 626131939Smarcel#endif 627131939Smarcel 62816359Sasami#ifdef COM_ESP 62942265Skato#ifdef PC98 63042265Skato 63116359Sasami/* XXX configure this properly. */ 63286912Snyan/* XXX quite broken for new-bus. */ 63342262Skatostatic Port_t likely_com_ports[] = { 0, 0xb0, 0xb1, 0 }; 63442262Skatostatic Port_t likely_esp_ports[] = { 0xc0d0, 0 }; 63542265Skato 63642265Skato#define ESP98_CMD1 (ESP_CMD1 * 0x100) 63742265Skato#define ESP98_CMD2 (ESP_CMD2 * 0x100) 63842265Skato#define ESP98_STATUS1 (ESP_STATUS1 * 0x100) 63942265Skato#define ESP98_STATUS2 (ESP_STATUS2 * 0x100) 64042265Skato 64142265Skato#else /* PC98 */ 64242265Skato 64342265Skato/* XXX configure this properly. */ 64416359Sasamistatic Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; 64516359Sasamistatic Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; 64642265Skato 64742262Skato#endif /* PC98 */ 64816359Sasami#endif 64916359Sasami 65026439Skato/* 65126439Skato * handle sysctl read/write requests for console speed 65226439Skato * 65326439Skato * In addition to setting comdefaultrate for I/O through /dev/console, 65426439Skato * also set the initial and lock values for the /dev/ttyXX device 65526439Skato * if there is one associated with the console. Finally, if the /dev/tty 65626439Skato * device has already been open, change the speed on the open running port 65726439Skato * itself. 65826439Skato */ 65926439Skato 66026439Skatostatic int 66162573Sphksysctl_machdep_comdefaultrate(SYSCTL_HANDLER_ARGS) 66226439Skato{ 66326439Skato int error, s; 66426439Skato speed_t newspeed; 66526439Skato struct com_s *com; 66626439Skato struct tty *tp; 66726439Skato 66826439Skato newspeed = comdefaultrate; 66926439Skato 67026439Skato error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req); 67126439Skato if (error || !req->newptr) 67226439Skato return (error); 67326439Skato 67426439Skato comdefaultrate = newspeed; 67526439Skato 67626439Skato if (comconsole < 0) /* serial console not selected? */ 67726439Skato return (0); 67826439Skato 67926439Skato com = com_addr(comconsole); 68057928Skato if (com == NULL) 68126439Skato return (ENXIO); 682135517Snyan 683135374Sphk tp = com->tp; 684135374Sphk if (tp == NULL) 685135374Sphk return (ENXIO); 68626439Skato 68726439Skato /* 68826439Skato * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX 68926439Skato * (note, the lock rates really are boolean -- if non-zero, disallow 69026439Skato * speed changes) 69126439Skato */ 692135374Sphk tp->t_init_in.c_ispeed = tp->t_init_in.c_ospeed = 693135374Sphk tp->t_lock_in.c_ispeed = tp->t_lock_in.c_ospeed = 694135374Sphk tp->t_init_out.c_ispeed = tp->t_init_out.c_ospeed = 695135374Sphk tp->t_lock_out.c_ispeed = tp->t_lock_out.c_ospeed = comdefaultrate; 69626439Skato 697135374Sphk if (tp->t_state & TS_ISOPEN) { 69826439Skato tp->t_termios.c_ispeed = 69926439Skato tp->t_termios.c_ospeed = comdefaultrate; 70026439Skato s = spltty(); 70126439Skato error = comparam(tp, &tp->t_termios); 70226439Skato splx(s); 70326439Skato } 70426439Skato return error; 70526439Skato} 70626439Skato 70726439SkatoSYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW, 70826439Skato 0, 0, sysctl_machdep_comdefaultrate, "I", ""); 709153506SnyanTUNABLE_INT("machdep.conspeed", __DEVOLATILE(int *, &comdefaultrate)); 71026439Skato 71116359Sasami/* 71286912Snyan * Unload the driver and clear the table. 71386912Snyan * XXX this is mostly wrong. 71416359Sasami * XXX TODO: 71516359Sasami * This is usually called when the card is ejected, but 716105093Snyan * can be caused by a kldunload of a controller driver. 71716359Sasami * The idea is to reset the driver's view of the device 71816359Sasami * and ensure that any driver entry points such as 71916359Sasami * read and write do not hang. 72016359Sasami */ 72186912Snyanint 722136550Snyansiodetach(device_t dev) 72316359Sasami{ 72416359Sasami struct com_s *com; 72516359Sasami 72652831Snyan com = (struct com_s *) device_get_softc(dev); 72757928Skato if (com == NULL) { 72852831Snyan device_printf(dev, "NULL com in siounload\n"); 72954407Skato return (0); 73037138Skato } 731120809Snyan com->gone = TRUE; 732132599Snyan if (com->tp) 733136550Snyan ttyfree(com->tp); 73454407Skato if (com->irqres) { 73554407Skato bus_teardown_intr(dev, com->irqres, com->cookie); 73654407Skato bus_release_resource(dev, SYS_RES_IRQ, 0, com->irqres); 73754407Skato } 73854407Skato if (com->ioportres) 739118353Snyan bus_release_resource(dev, SYS_RES_IOPORT, com->ioportrid, 740118353Snyan com->ioportres); 741136550Snyan if (com->ibuf != NULL) 742136550Snyan free(com->ibuf, M_DEVBUF); 74391986Snyan#ifdef PC98 744136550Snyan if (com->obuf1 != NULL) 745136550Snyan free(com->obuf1, M_DEVBUF); 74691986Snyan#endif 747136550Snyan 748136550Snyan device_set_softc(dev, NULL); 749136550Snyan free(com, M_DEVBUF); 75053986Snyan return (0); 75116359Sasami} 75216359Sasami 75386912Snyanint 75490011Snyansioprobe(dev, xrid, rclk, noprobe) 75558888Skato device_t dev; 75658888Skato int xrid; 75790011Snyan u_long rclk; 75886912Snyan int noprobe; 75916359Sasami{ 76053373Snyan#if 0 76116359Sasami static bool_t already_init; 76253373Snyan device_t xdev; 76353373Snyan#endif 76460472Snyan struct com_s *com; 76590011Snyan u_int divisor; 76616359Sasami bool_t failures[10]; 76716359Sasami int fn; 76845783Skato device_t idev; 76916359Sasami Port_t iobase; 77036564Skato intrmask_t irqmap[4]; 77136564Skato intrmask_t irqs; 77216359Sasami u_char mcr_image; 77316359Sasami int result; 77454255Skato u_long xirq; 77551056Skato u_int flags = device_get_flags(dev); 77651202Snyan int rid; 77751202Snyan struct resource *port; 77816359Sasami#ifdef PC98 77916359Sasami int tmp; 78042262Skato struct siodev iod; 78116359Sasami#endif 78216359Sasami 78360472Snyan#ifdef PC98 78460472Snyan iod.if_type = GET_IFTYPE(flags); 78560472Snyan if ((iod.if_type < 0 || iod.if_type > COM_IF_END1) && 78660472Snyan (iod.if_type < 0x10 || iod.if_type > COM_IF_END2)) 78771713Snyan return ENXIO; 78860472Snyan#endif 78960472Snyan 79058888Skato rid = xrid; 79151202Snyan#ifdef PC98 79260472Snyan if (IS_8251(iod.if_type)) { 793127135Snjl port = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 794127135Snjl RF_ACTIVE); 79573022Snyan } else if (iod.if_type == COM_IF_MODEM_CARD || 79673022Snyan iod.if_type == COM_IF_RSA98III || 79761897Snyan isa_get_vendorid(dev)) { 79861897Snyan port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 79971713Snyan if_16550a_type[iod.if_type & 0x0f].iatsz, RF_ACTIVE); 80060472Snyan } else { 80160472Snyan port = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, 80271713Snyan if_16550a_type[iod.if_type & 0x0f].iat, 80371713Snyan if_16550a_type[iod.if_type & 0x0f].iatsz, RF_ACTIVE); 80460472Snyan } 80551202Snyan#else 80651202Snyan port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 80751202Snyan 0, ~0, IO_COMSIZE, RF_ACTIVE); 80851202Snyan#endif 80951202Snyan if (!port) 81057928Skato return (ENXIO); 81160472Snyan#ifdef PC98 81260472Snyan if (!IS_8251(iod.if_type)) { 81360472Snyan if (isa_load_resourcev(port, 81471713Snyan if_16550a_type[iod.if_type & 0x0f].iat, 81571713Snyan if_16550a_type[iod.if_type & 0x0f].iatsz) != 0) { 81671713Snyan bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 81771713Snyan return ENXIO; 81860472Snyan } 81960472Snyan } 82060472Snyan#endif 82151202Snyan 82286912Snyan com = malloc(sizeof(*com), M_DEVBUF, M_NOWAIT | M_ZERO); 823124791Snyan if (com == NULL) { 824124791Snyan bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 82586912Snyan return (ENOMEM); 826124791Snyan } 82786912Snyan device_set_softc(dev, com); 82860472Snyan com->bst = rman_get_bustag(port); 82960472Snyan com->bsh = rman_get_bushandle(port); 83090011Snyan#ifdef PC98 83190011Snyan if (!IS_8251(iod.if_type) && rclk == 0) 83290011Snyan rclk = if_16550a_type[iod.if_type & 0x0f].rclk; 83390011Snyan#else 83490011Snyan if (rclk == 0) 83590011Snyan rclk = DEFAULT_RCLK; 83690011Snyan#endif 83790011Snyan com->rclk = rclk; 83860472Snyan 83986912Snyan while (sio_inited != 2) 84086912Snyan if (atomic_cmpset_int(&sio_inited, 0, 1)) { 84193818Sjhb mtx_init(&sio_lock, sio_driver_name, NULL, 84293818Sjhb (comconsole != -1) ? 84386912Snyan MTX_SPIN | MTX_QUIET : MTX_SPIN); 84486912Snyan atomic_store_rel_int(&sio_inited, 2); 84586912Snyan } 84671713Snyan 84753373Snyan#if 0 84853373Snyan /* 84953373Snyan * XXX this is broken - when we are first called, there are no 85053373Snyan * previously configured IO ports. We could hard code 85153373Snyan * 0x3f8, 0x2f8, 0x3e8, 0x2e8 etc but that's probably worse. 85253373Snyan * This code has been doing nothing since the conversion since 85353373Snyan * "count" is zero the first time around. 85453373Snyan */ 85516359Sasami if (!already_init) { 85616359Sasami /* 85716359Sasami * Turn off MCR_IENABLE for all likely serial ports. An unused 85816359Sasami * port with its MCR_IENABLE gate open will inhibit interrupts 85916359Sasami * from any used port that shares the interrupt vector. 86016359Sasami * XXX the gate enable is elsewhere for some multiports. 86116359Sasami */ 86245783Skato device_t *devs; 86353373Snyan int count, i, xioport; 86453373Snyan#ifdef PC98 86553373Snyan int xiftype; 86653373Snyan#endif 86745783Skato 86845783Skato devclass_get_devices(sio_devclass, &devs, &count); 86916359Sasami#ifdef PC98 87045783Skato for (i = 0; i < count; i++) { 87145783Skato xdev = devs[i]; 87253373Snyan xioport = bus_get_resource_start(xdev, SYS_RES_IOPORT, 0); 87353373Snyan xiftype = GET_IFTYPE(device_get_flags(xdev)); 87453373Snyan if (device_is_enabled(xdev) && xioport > 0) { 87553373Snyan if (IS_8251(xiftype)) 87660472Snyan outb((xioport & 0xff00) | PC98SIO_cmd_port(xiftype & 0x0f), 0xf2); 87760472Snyan else 87861897Snyan outb(xioport + if_16550a_type[xiftype & 0x0f].iat[com_mcr], 0); 87951202Snyan } 88045783Skato } 88142262Skato#else 88245783Skato for (i = 0; i < count; i++) { 88345783Skato xdev = devs[i]; 88454255Skato if (device_is_enabled(xdev) && 88554255Skato bus_get_resource(xdev, SYS_RES_IOPORT, 0, &xioport, 88654255Skato NULL) == 0) 88753373Snyan outb(xioport + com_mcr, 0); 88845783Skato } 88942262Skato#endif 89045783Skato free(devs, M_TEMP); 89116359Sasami already_init = TRUE; 89216359Sasami } 89353373Snyan#endif 89416359Sasami 89545783Skato if (COM_LLCONSOLE(flags)) { 89645783Skato printf("sio%d: reserved for low-level i/o\n", 89745783Skato device_get_unit(dev)); 89856793Skato bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 89986912Snyan device_set_softc(dev, NULL); 90086912Snyan free(com, M_DEVBUF); 90145783Skato return (ENXIO); 90232546Skato } 90332546Skato 90416359Sasami#ifdef PC98 90532691Skato DELAY(10); 90642262Skato 90716359Sasami /* 90816359Sasami * If the port is i8251 UART (internal, B98_01) 90916359Sasami */ 91059493Snyan if (pc98_check_if_type(dev, &iod) == -1) { 91159493Snyan bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 91286912Snyan device_set_softc(dev, NULL); 91386912Snyan free(com, M_DEVBUF); 91457928Skato return (ENXIO); 91559493Snyan } 91642262Skato if (iod.irq > 0) 91753884Snyan bus_set_resource(dev, SYS_RES_IRQ, 0, iod.irq, 1); 91842262Skato if (IS_8251(iod.if_type)) { 91916359Sasami outb(iod.cmd, 0); 92016359Sasami DELAY(10); 92116359Sasami outb(iod.cmd, 0); 92216359Sasami DELAY(10); 92316359Sasami outb(iod.cmd, 0); 92416359Sasami DELAY(10); 92516359Sasami outb(iod.cmd, CMD8251_RESET); 92616359Sasami DELAY(1000); /* for a while...*/ 92716359Sasami outb(iod.cmd, 0xf2); /* MODE (dummy) */ 92816359Sasami DELAY(10); 92916359Sasami outb(iod.cmd, 0x01); /* CMD (dummy) */ 93016359Sasami DELAY(1000); /* for a while...*/ 93116359Sasami if (( inb(iod.sts) & STS8251_TxEMP ) == 0 ) { 93257928Skato result = (ENXIO); 93316359Sasami } 93442262Skato if (if_8251_type[iod.if_type & 0x0f].check_irq) { 93542262Skato COM_INT_DISABLE 93642262Skato tmp = ( inb( iod.ctrl ) & ~(IEN_Rx|IEN_TxEMP|IEN_Tx)); 93742262Skato outb( iod.ctrl, tmp|IEN_TxEMP ); 93842262Skato DELAY(10); 93945830Skato result = isa_irq_pending() ? 0 : ENXIO; 94042262Skato outb( iod.ctrl, tmp ); 94142262Skato COM_INT_ENABLE 94242262Skato } else { 94342262Skato /* 94442262Skato * B98_01 doesn't activate TxEMP interrupt line 94542262Skato * when being reset, so we can't check irq pending. 94642262Skato */ 94745830Skato result = 0; 94816359Sasami } 94951202Snyan bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 95086912Snyan if (result) { 95186912Snyan device_set_softc(dev, NULL); 95286912Snyan free(com, M_DEVBUF); 95386912Snyan } 95445830Skato return result; 95516359Sasami } 95616359Sasami#endif /* PC98 */ 95716359Sasami /* 95816359Sasami * If the device is on a multiport card and has an AST/4 95916359Sasami * compatible interrupt control register, initialize this 96016359Sasami * register and prepare to leave MCR_IENABLE clear in the mcr. 96116359Sasami * Otherwise, prepare to set MCR_IENABLE in the mcr. 96216359Sasami * Point idev to the device struct giving the correct id_irq. 96316359Sasami * This is the struct for the master device if there is one. 96416359Sasami */ 96516359Sasami idev = dev; 96616359Sasami mcr_image = MCR_IENABLE; 96753373Snyan#ifdef COM_MULTIPORT 96857291Skato if (COM_ISMULTIPORT(flags)) { 96960472Snyan#ifndef PC98 97054255Skato Port_t xiobase; 97154255Skato u_long io; 97260472Snyan#endif 97354255Skato 97453373Snyan idev = devclass_get_device(sio_devclass, COM_MPMASTER(flags)); 97553373Snyan if (idev == NULL) { 97653373Snyan printf("sio%d: master device %d not configured\n", 97753373Snyan device_get_unit(dev), COM_MPMASTER(flags)); 97853373Snyan idev = dev; 97953373Snyan } 98053373Snyan#ifndef PC98 98157291Skato if (!COM_NOTAST4(flags)) { 98257291Skato if (bus_get_resource(idev, SYS_RES_IOPORT, 0, &io, 98357291Skato NULL) == 0) { 98457291Skato xiobase = io; 98557291Skato if (bus_get_resource(idev, SYS_RES_IRQ, 0, 98657291Skato NULL, NULL) == 0) 98757291Skato outb(xiobase + com_scr, 0x80); 98857291Skato else 98957291Skato outb(xiobase + com_scr, 0); 99057291Skato } 99157291Skato mcr_image = 0; 99253373Snyan } 99353373Snyan#endif 99453373Snyan } 99553373Snyan#endif /* COM_MULTIPORT */ 99654255Skato if (bus_get_resource(idev, SYS_RES_IRQ, 0, NULL, NULL) != 0) 99753373Snyan mcr_image = 0; 99853373Snyan 99953373Snyan bzero(failures, sizeof failures); 100053373Snyan iobase = rman_get_start(port); 100153373Snyan 100242262Skato#ifdef PC98 100342262Skato if (iod.if_type == COM_IF_RSA98III) { 100442262Skato mcr_image = 0; 100553373Snyan 100660472Snyan outb(iobase + rsa_msr, 0x04); 100760472Snyan outb(iobase + rsa_frr, 0x00); 100860472Snyan if ((inb(iobase + rsa_srr) & 0x36) != 0x36) { 100959493Snyan bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 101086912Snyan device_set_softc(dev, NULL); 101186912Snyan free(com, M_DEVBUF); 101259493Snyan return (ENXIO); 101359493Snyan } 101460472Snyan outb(iobase + rsa_ier, 0x00); 101560472Snyan outb(iobase + rsa_frr, 0x00); 101660472Snyan outb(iobase + rsa_tivsr, 0x00); 101760472Snyan outb(iobase + rsa_tcr, 0x00); 101842262Skato } 101916359Sasami 102042262Skato tmp = if_16550a_type[iod.if_type & 0x0f].irr_write; 102142262Skato if (tmp != -1) { 102242262Skato /* MC16550II */ 102360472Snyan int irqout; 102445783Skato switch (isa_get_irq(idev)) { 102545783Skato case 3: irqout = 4; break; 102645783Skato case 5: irqout = 5; break; 102745783Skato case 6: irqout = 6; break; 102845783Skato case 12: irqout = 7; break; 102942262Skato default: 103045783Skato printf("sio%d: irq configuration error\n", 103145783Skato device_get_unit(dev)); 103259493Snyan bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 103386912Snyan device_set_softc(dev, NULL); 103486912Snyan free(com, M_DEVBUF); 103559493Snyan return (ENXIO); 103642262Skato } 103760472Snyan outb((iobase & 0x00ff) | tmp, irqout); 103816359Sasami } 103916359Sasami#endif 104016359Sasami 104116359Sasami /* 104216359Sasami * We don't want to get actual interrupts, just masked ones. 104316359Sasami * Interrupts from this line should already be masked in the ICU, 104416359Sasami * but mask them in the processor as well in case there are some 104516359Sasami * (misconfigured) shared interrupts. 104616359Sasami */ 104772200Sbmilekic mtx_lock_spin(&sio_lock); 104816359Sasami/* EXTRA DELAY? */ 104916359Sasami 105016359Sasami /* 105116359Sasami * Initialize the speed and the word size and wait long enough to 105216359Sasami * drain the maximum of 16 bytes of junk in device output queues. 105316359Sasami * The speed is undefined after a master reset and must be set 105416359Sasami * before relying on anything related to output. There may be 105516359Sasami * junk after a (very fast) soft reboot and (apparently) after 105616359Sasami * master reset. 105716359Sasami * XXX what about the UART bug avoided by waiting in comparam()? 105816359Sasami * We don't want to to wait long enough to drain at 2 bps. 105916359Sasami */ 106024655Skato if (iobase == siocniobase) 106124655Skato DELAY((16 + 1) * 1000000 / (comdefaultrate / 10)); 106224655Skato else { 106360472Snyan sio_setreg(com, com_cfcr, CFCR_DLAB | CFCR_8BITS); 106490011Snyan divisor = siodivisor(rclk, SIO_TEST_SPEED); 106590011Snyan sio_setreg(com, com_dlbl, divisor & 0xff); 106690011Snyan sio_setreg(com, com_dlbh, divisor >> 8); 106760472Snyan sio_setreg(com, com_cfcr, CFCR_8BITS); 106826439Skato DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10)); 106924655Skato } 107016359Sasami 107116359Sasami /* 1072172568Skevlo * Enable the interrupt gate and disable device interrupts. This 107316359Sasami * should leave the device driving the interrupt line low and 107416359Sasami * guarantee an edge trigger if an interrupt can be generated. 107516359Sasami */ 107616359Sasami/* EXTRA DELAY? */ 107760472Snyan sio_setreg(com, com_mcr, mcr_image); 107860472Snyan sio_setreg(com, com_ier, 0); 107936564Skato DELAY(1000); /* XXX */ 108036564Skato irqmap[0] = isa_irq_pending(); 108116359Sasami 108216359Sasami /* 108316359Sasami * Attempt to set loopback mode so that we can send a null byte 108416359Sasami * without annoying any external device. 108516359Sasami */ 108616359Sasami/* EXTRA DELAY? */ 108760472Snyan sio_setreg(com, com_mcr, mcr_image | MCR_LOOPBACK); 108816359Sasami 108916359Sasami /* 109016359Sasami * Attempt to generate an output interrupt. On 8250's, setting 109116359Sasami * IER_ETXRDY generates an interrupt independent of the current 109216359Sasami * setting and independent of whether the THR is empty. On 16450's, 109316359Sasami * setting IER_ETXRDY generates an interrupt independent of the 109416359Sasami * current setting. On 16550A's, setting IER_ETXRDY only 109516359Sasami * generates an interrupt when IER_ETXRDY is not already set. 109616359Sasami */ 109760472Snyan sio_setreg(com, com_ier, IER_ETXRDY); 109842262Skato#ifdef PC98 109945783Skato if (iod.if_type == COM_IF_RSA98III) 110060472Snyan outb(iobase + rsa_ier, 0x04); 110160472Snyan#endif 110216359Sasami 110316359Sasami /* 110416359Sasami * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate 110516359Sasami * an interrupt. They'd better generate one for actually doing 110616359Sasami * output. Loopback may be broken on the same incompatibles but 110716359Sasami * it's unlikely to do more than allow the null byte out. 110816359Sasami */ 110960472Snyan sio_setreg(com, com_data, 0); 1110120809Snyan if (iobase == siocniobase) 1111120809Snyan DELAY((1 + 2) * 1000000 / (comdefaultrate / 10)); 1112120809Snyan else 1113120809Snyan DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10)); 111416359Sasami 111516359Sasami /* 111616359Sasami * Turn off loopback mode so that the interrupt gate works again 111716359Sasami * (MCR_IENABLE was hidden). This should leave the device driving 111816359Sasami * an interrupt line high. It doesn't matter if the interrupt 111916359Sasami * line oscillates while we are not looking at it, since interrupts 112016359Sasami * are disabled. 112116359Sasami */ 112216359Sasami/* EXTRA DELAY? */ 112360472Snyan sio_setreg(com, com_mcr, mcr_image); 112493147Snyan 112593147Snyan /* 112693147Snyan * It seems my Xircom CBEM56G Cardbus modem wants to be reset 112793147Snyan * to 8 bits *again*, or else probe test 0 will fail. 112893147Snyan * gwk@sgi.com, 4/19/2001 112993147Snyan */ 113093147Snyan sio_setreg(com, com_cfcr, CFCR_8BITS); 113116359Sasami 113251202Snyan /* 1133120809Snyan * Some PCMCIA cards (Palido 321s, DC-1S, ...) have the "TXRDY bug", 1134120809Snyan * so we probe for a buggy IIR_TXRDY implementation even in the 1135120809Snyan * noprobe case. We don't probe for it in the !noprobe case because 1136120809Snyan * noprobe is always set for PCMCIA cards and the problem is not 1137120809Snyan * known to affect any other cards. 113832332Skato */ 113986912Snyan if (noprobe) { 1140120809Snyan /* Read IIR a few times. */ 114153373Snyan for (fn = 0; fn < 2; fn ++) { 114253373Snyan DELAY(10000); 114360472Snyan failures[6] = sio_getreg(com, com_iir); 114453373Snyan } 1145120809Snyan 1146120809Snyan /* IIR_TXRDY should be clear. Is it? */ 114753373Snyan result = 0; 114853373Snyan if (failures[6] & IIR_TXRDY) { 1149120809Snyan /* 1150120809Snyan * No. We seem to have the bug. Does our fix for 1151120809Snyan * it work? 1152120809Snyan */ 115360472Snyan sio_setreg(com, com_ier, 0); 115460472Snyan if (sio_getreg(com, com_iir) & IIR_NOPEND) { 1155120809Snyan /* Yes. We discovered the TXRDY bug! */ 115653373Snyan SET_FLAG(dev, COM_C_IIR_TXRDYBUG); 115753373Snyan } else { 1158120809Snyan /* No. Just fail. XXX */ 115953373Snyan result = ENXIO; 116083539Snyan sio_setreg(com, com_mcr, 0); 116153373Snyan } 116232332Skato } else { 1163120809Snyan /* Yes. No bug. */ 116453373Snyan CLR_FLAG(dev, COM_C_IIR_TXRDYBUG); 116532332Skato } 116683539Snyan sio_setreg(com, com_ier, 0); 116760472Snyan sio_setreg(com, com_cfcr, CFCR_8BITS); 116872200Sbmilekic mtx_unlock_spin(&sio_lock); 116953373Snyan bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 1170129001Snyan if (iobase == siocniobase) 117186912Snyan result = 0; 117286912Snyan if (result != 0) { 117386912Snyan device_set_softc(dev, NULL); 117486912Snyan free(com, M_DEVBUF); 117586912Snyan } 117686912Snyan return (result); 117752831Snyan } 117853373Snyan 117916359Sasami /* 118016359Sasami * Check that 118116359Sasami * o the CFCR, IER and MCR in UART hold the values written to them 118216359Sasami * (the values happen to be all distinct - this is good for 118316359Sasami * avoiding false positive tests from bus echoes). 118416359Sasami * o an output interrupt is generated and its vector is correct. 118516359Sasami * o the interrupt goes away when the IIR in the UART is read. 118616359Sasami */ 118716359Sasami/* EXTRA DELAY? */ 118860472Snyan failures[0] = sio_getreg(com, com_cfcr) - CFCR_8BITS; 118960472Snyan failures[1] = sio_getreg(com, com_ier) - IER_ETXRDY; 119060472Snyan failures[2] = sio_getreg(com, com_mcr) - mcr_image; 119117256Sasami DELAY(10000); /* Some internal modems need this time */ 119236564Skato irqmap[1] = isa_irq_pending(); 119360472Snyan failures[4] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_TXRDY; 119442262Skato#ifdef PC98 119545783Skato if (iod.if_type == COM_IF_RSA98III) 119660472Snyan inb(iobase + rsa_srr); 119742262Skato#endif 119816359Sasami DELAY(1000); /* XXX */ 119936564Skato irqmap[2] = isa_irq_pending(); 120060472Snyan failures[6] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND; 120142262Skato#ifdef PC98 120245783Skato if (iod.if_type == COM_IF_RSA98III) 120360472Snyan inb(iobase + rsa_srr); 120442262Skato#endif 120516359Sasami 120616359Sasami /* 120716359Sasami * Turn off all device interrupts and check that they go off properly. 120816359Sasami * Leave MCR_IENABLE alone. For ports without a master port, it gates 120916359Sasami * the OUT2 output of the UART to 121016359Sasami * the ICU input. Closing the gate would give a floating ICU input 121145816Skato * (unless there is another device driving it) and spurious interrupts. 121216359Sasami * (On the system that this was first tested on, the input floats high 121316359Sasami * and gives a (masked) interrupt as soon as the gate is closed.) 121416359Sasami */ 121560472Snyan sio_setreg(com, com_ier, 0); 121660472Snyan sio_setreg(com, com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ 121760472Snyan failures[7] = sio_getreg(com, com_ier); 121842262Skato#ifdef PC98 121945783Skato if (iod.if_type == COM_IF_RSA98III) 122060472Snyan outb(iobase + rsa_ier, 0x00); 122142262Skato#endif 122216359Sasami DELAY(1000); /* XXX */ 122336564Skato irqmap[3] = isa_irq_pending(); 122460472Snyan failures[9] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND; 122542262Skato#ifdef PC98 122642262Skato if (iod.if_type == COM_IF_RSA98III) { 122760472Snyan inb(iobase + rsa_srr); 122860472Snyan outb(iobase + rsa_frr, 0x00); 122942262Skato } 123042262Skato#endif 123116359Sasami 123272200Sbmilekic mtx_unlock_spin(&sio_lock); 123316359Sasami 123436662Skato irqs = irqmap[1] & ~irqmap[0]; 123554255Skato if (bus_get_resource(idev, SYS_RES_IRQ, 0, &xirq, NULL) == 0 && 123689485Snyan ((1 << xirq) & irqs) == 0) { 123736564Skato printf( 123854255Skato "sio%d: configured irq %ld not in bitmap of probed irqs %#x\n", 123953373Snyan device_get_unit(dev), xirq, irqs); 124089485Snyan printf( 124189485Snyan "sio%d: port may not be enabled\n", 124289485Snyan device_get_unit(dev)); 124389485Snyan } 124436564Skato if (bootverbose) 124536662Skato printf("sio%d: irq maps: %#x %#x %#x %#x\n", 124645783Skato device_get_unit(dev), 124745783Skato irqmap[0], irqmap[1], irqmap[2], irqmap[3]); 124836564Skato 1249129001Snyan result = 0; 1250129001Snyan for (fn = 0; fn < sizeof failures; ++fn) 125116359Sasami if (failures[fn]) { 125260472Snyan sio_setreg(com, com_mcr, 0); 125345783Skato result = ENXIO; 125436564Skato if (bootverbose) { 125536564Skato printf("sio%d: probe failed test(s):", 125645783Skato device_get_unit(dev)); 125736564Skato for (fn = 0; fn < sizeof failures; ++fn) 125836564Skato if (failures[fn]) 125936564Skato printf(" %d", fn); 126036564Skato printf("\n"); 126136564Skato } 126236564Skato break; 126316359Sasami } 126451202Snyan bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 1265129001Snyan if (iobase == siocniobase) 126686912Snyan result = 0; 126786912Snyan if (result != 0) { 126886912Snyan device_set_softc(dev, NULL); 126986912Snyan free(com, M_DEVBUF); 127086912Snyan } 127186912Snyan return (result); 127216359Sasami} 127316359Sasami 127416359Sasami#ifdef COM_ESP 127516359Sasamistatic int 127646871Skatoespattach(com, esp_port) 127716359Sasami struct com_s *com; 127816359Sasami Port_t esp_port; 127916359Sasami{ 128016359Sasami u_char dips; 128116359Sasami u_char val; 128216359Sasami 128316359Sasami /* 128416359Sasami * Check the ESP-specific I/O port to see if we're an ESP 128516359Sasami * card. If not, return failure immediately. 128616359Sasami */ 128716359Sasami if ((inb(esp_port) & 0xf3) == 0) { 128816359Sasami printf(" port 0x%x is not an ESP board?\n", esp_port); 128916359Sasami return (0); 129016359Sasami } 129116359Sasami 129216359Sasami /* 129316359Sasami * We've got something that claims to be a Hayes ESP card. 129416359Sasami * Let's hope so. 129516359Sasami */ 129616359Sasami 129716359Sasami /* Get the dip-switch configuration */ 129842265Skato#ifdef PC98 129942265Skato outb(esp_port + ESP98_CMD1, ESP_GETDIPS); 130042265Skato dips = inb(esp_port + ESP98_STATUS1); 130142265Skato#else 130216359Sasami outb(esp_port + ESP_CMD1, ESP_GETDIPS); 130316359Sasami dips = inb(esp_port + ESP_STATUS1); 130442265Skato#endif 130516359Sasami 130616359Sasami /* 130716359Sasami * Bits 0,1 of dips say which COM port we are. 130816359Sasami */ 130942262Skato#ifdef PC98 131060472Snyan if ((rman_get_start(com->ioportres) & 0xff) == 131160472Snyan likely_com_ports[dips & 0x03]) 131242262Skato#else 131360472Snyan if (rman_get_start(com->ioportres) == likely_com_ports[dips & 0x03]) 131442262Skato#endif 131516359Sasami printf(" : ESP"); 131616359Sasami else { 131716359Sasami printf(" esp_port has com %d\n", dips & 0x03); 131816359Sasami return (0); 131916359Sasami } 132016359Sasami 132116359Sasami /* 132216359Sasami * Check for ESP version 2.0 or later: bits 4,5,6 = 010. 132316359Sasami */ 132442265Skato#ifdef PC98 132542265Skato outb(esp_port + ESP98_CMD1, ESP_GETTEST); 132642265Skato val = inb(esp_port + ESP98_STATUS1); /* clear reg 1 */ 132742265Skato val = inb(esp_port + ESP98_STATUS2); 132842265Skato#else 132916359Sasami outb(esp_port + ESP_CMD1, ESP_GETTEST); 133016359Sasami val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ 133116359Sasami val = inb(esp_port + ESP_STATUS2); 133242265Skato#endif 133316359Sasami if ((val & 0x70) < 0x20) { 133416359Sasami printf("-old (%o)", val & 0x70); 133516359Sasami return (0); 133616359Sasami } 133716359Sasami 133816359Sasami /* 133916359Sasami * Check for ability to emulate 16550: bit 7 == 1 134016359Sasami */ 134116359Sasami if ((dips & 0x80) == 0) { 134216359Sasami printf(" slave"); 134316359Sasami return (0); 134416359Sasami } 134516359Sasami 134616359Sasami /* 134716359Sasami * Okay, we seem to be a Hayes ESP card. Whee. 134816359Sasami */ 134916359Sasami com->esp = TRUE; 135016359Sasami com->esp_port = esp_port; 135116359Sasami return (1); 135216359Sasami} 135316359Sasami#endif /* COM_ESP */ 135416359Sasami 135586912Snyanint 135690011Snyansioattach(dev, xrid, rclk) 135745783Skato device_t dev; 135858888Skato int xrid; 135990011Snyan u_long rclk; 136016359Sasami{ 136116359Sasami struct com_s *com; 136216359Sasami#ifdef COM_ESP 136316359Sasami Port_t *espp; 136416359Sasami#endif 136516359Sasami Port_t iobase; 136616359Sasami int unit; 136753373Snyan u_int flags; 136851202Snyan int rid; 136951202Snyan struct resource *port; 137053373Snyan int ret; 1371136550Snyan int error; 1372135374Sphk struct tty *tp; 137342262Skato#ifdef PC98 137445783Skato u_char *obuf; 137543663Skato u_long obufsize; 137660472Snyan int if_type = GET_IFTYPE(device_get_flags(dev)); 137742262Skato#endif 137816359Sasami 137958888Skato rid = xrid; 138042262Skato#ifdef PC98 138160472Snyan if (IS_8251(if_type)) { 1382127135Snjl port = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 1383127135Snjl RF_ACTIVE); 138473022Snyan } else if (if_type == COM_IF_MODEM_CARD || 138573022Snyan if_type == COM_IF_RSA98III || 138661897Snyan isa_get_vendorid(dev)) { 138761897Snyan port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 138871713Snyan if_16550a_type[if_type & 0x0f].iatsz, RF_ACTIVE); 138960472Snyan } else { 139060472Snyan port = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, 139171713Snyan if_16550a_type[if_type & 0x0f].iat, 139271713Snyan if_16550a_type[if_type & 0x0f].iatsz, RF_ACTIVE); 139360472Snyan } 139451202Snyan#else 139551202Snyan port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 139651202Snyan 0, ~0, IO_COMSIZE, RF_ACTIVE); 139751202Snyan#endif 139851202Snyan if (!port) 139957928Skato return (ENXIO); 140060472Snyan#ifdef PC98 140160472Snyan if (!IS_8251(if_type)) { 140260472Snyan if (isa_load_resourcev(port, 140371713Snyan if_16550a_type[if_type & 0x0f].iat, 140471713Snyan if_16550a_type[if_type & 0x0f].iatsz) != 0) { 140571713Snyan bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 140671713Snyan return ENXIO; 140760472Snyan } 140860472Snyan } 140960472Snyan#endif 141051202Snyan 141151202Snyan iobase = rman_get_start(port); 141245783Skato unit = device_get_unit(dev); 141345783Skato com = device_get_softc(dev); 141453373Snyan flags = device_get_flags(dev); 141553373Snyan 141653373Snyan if (unit >= sio_numunits) 141753373Snyan sio_numunits = unit + 1; 141853373Snyan 141945783Skato#ifdef PC98 142043663Skato obufsize = 256; 142160472Snyan if (if_type == COM_IF_RSA98III) 142243663Skato obufsize = 2048; 142359493Snyan if ((obuf = malloc(obufsize * 2, M_DEVBUF, M_NOWAIT)) == NULL) { 142459493Snyan bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 142556512Skato return ENXIO; 142659493Snyan } 142745783Skato bzero(obuf, obufsize * 2); 142842262Skato#endif 142916359Sasami 143016359Sasami /* 143116359Sasami * sioprobe() has initialized the device registers as follows: 143216359Sasami * o cfcr = CFCR_8BITS. 143316359Sasami * It is most important that CFCR_DLAB is off, so that the 143416359Sasami * data port is not hidden when we enable interrupts. 143516359Sasami * o ier = 0. 143616359Sasami * Interrupts are only enabled when the line is open. 143716359Sasami * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible 143816359Sasami * interrupt control register or the config specifies no irq. 143916359Sasami * Keeping MCR_DTR and MCR_RTS off might stop the external 144016359Sasami * device from sending before we are ready. 144116359Sasami */ 144216359Sasami bzero(com, sizeof *com); 144316359Sasami com->unit = unit; 144451202Snyan com->ioportres = port; 1445118353Snyan com->ioportrid = rid; 144660472Snyan com->bst = rman_get_bustag(port); 144760472Snyan com->bsh = rman_get_bushandle(port); 144816359Sasami com->cfcr_image = CFCR_8BITS; 144945783Skato com->loses_outints = COM_LOSESOUTINTS(flags) != 0; 145057291Skato com->no_irq = bus_get_resource(dev, SYS_RES_IRQ, 0, NULL, NULL) != 0; 145116359Sasami com->tx_fifo_size = 1; 145260472Snyan#ifdef PC98 145360472Snyan com->obufsize = obufsize; 145460472Snyan com->obuf1 = obuf; 145560472Snyan com->obuf2 = obuf + obufsize; 145660472Snyan#endif 145716359Sasami com->obufs[0].l_head = com->obuf1; 145816359Sasami com->obufs[1].l_head = com->obuf2; 145916359Sasami 146016359Sasami#ifdef PC98 146160472Snyan com->pc98_if_type = if_type; 146254174Snyan 146360472Snyan if (IS_8251(if_type)) { 146460472Snyan pc98_set_ioport(com); 146554174Snyan 146660472Snyan if (if_type == COM_IF_INTERNAL && pc98_check_8251fifo()) { 146760472Snyan com->pc98_8251fifo = 1; 146860472Snyan com->pc98_8251fifo_enable = 0; 146960472Snyan } 147060472Snyan } else { 147161897Snyan bus_addr_t *iat = if_16550a_type[if_type & 0x0f].iat; 147260472Snyan 147360472Snyan com->data_port = iobase + iat[com_data]; 1474120809Snyan com->int_ctl_port = iobase + iat[com_ier]; 147560472Snyan com->int_id_port = iobase + iat[com_iir]; 147660472Snyan com->modem_ctl_port = iobase + iat[com_mcr]; 147742262Skato com->mcr_image = inb(com->modem_ctl_port); 147860472Snyan com->line_status_port = iobase + iat[com_lsr]; 147960472Snyan com->modem_status_port = iobase + iat[com_msr]; 148042262Skato } 148116359Sasami#else /* not PC98 */ 148216359Sasami com->data_port = iobase + com_data; 1483120809Snyan com->int_ctl_port = iobase + com_ier; 148416359Sasami com->int_id_port = iobase + com_iir; 148516359Sasami com->modem_ctl_port = iobase + com_mcr; 148616359Sasami com->mcr_image = inb(com->modem_ctl_port); 148716359Sasami com->line_status_port = iobase + com_lsr; 148816359Sasami com->modem_status_port = iobase + com_msr; 148916359Sasami#endif 1490136550Snyan 1491135374Sphk tp = com->tp = ttyalloc(); 1492135374Sphk tp->t_oproc = comstart; 1493135374Sphk tp->t_param = comparam; 1494135374Sphk tp->t_stop = comstop; 1495135374Sphk tp->t_modem = commodem; 1496135374Sphk tp->t_break = combreak; 1497136550Snyan tp->t_close = comclose; 1498136550Snyan tp->t_open = comopen; 1499135374Sphk tp->t_sc = com; 150016359Sasami 150190011Snyan#ifdef PC98 150290011Snyan if (!IS_8251(if_type) && rclk == 0) 150390011Snyan rclk = if_16550a_type[if_type & 0x0f].rclk; 150490011Snyan#else 150590011Snyan if (rclk == 0) 150690011Snyan rclk = DEFAULT_RCLK; 150790011Snyan#endif 150890011Snyan com->rclk = rclk; 150990011Snyan 1510136550Snyan if (unit == comconsole) 1511136550Snyan ttyconsolemode(tp, comdefaultrate); 1512136550Snyan error = siosetwater(com, tp->t_init_in.c_ispeed); 1513136550Snyan mtx_unlock_spin(&sio_lock); 1514136550Snyan if (error) { 151556793Skato /* 151656793Skato * Leave i/o resources allocated if this is a `cn'-level 151756793Skato * console, so that other devices can't snarf them. 151856793Skato */ 151956793Skato if (iobase != siocniobase) 152056793Skato bus_release_resource(dev, SYS_RES_IOPORT, rid, port); 152156793Skato return (ENOMEM); 152243663Skato } 152316359Sasami 152416359Sasami /* attempt to determine UART type */ 152516359Sasami printf("sio%d: type", unit); 152616359Sasami 152716359Sasami#ifndef PC98 1528104134Snyan if (!COM_ISMULTIPORT(flags) && 1529104134Snyan !COM_IIR_TXRDYBUG(flags) && !COM_NOSCR(flags)) { 153016359Sasami u_char scr; 153116359Sasami u_char scr1; 153216359Sasami u_char scr2; 153316359Sasami 153460472Snyan scr = sio_getreg(com, com_scr); 153560472Snyan sio_setreg(com, com_scr, 0xa5); 153660472Snyan scr1 = sio_getreg(com, com_scr); 153760472Snyan sio_setreg(com, com_scr, 0x5a); 153860472Snyan scr2 = sio_getreg(com, com_scr); 153960472Snyan sio_setreg(com, com_scr, scr); 154016359Sasami if (scr1 != 0xa5 || scr2 != 0x5a) { 154189485Snyan printf(" 8250 or not responding"); 154216359Sasami goto determined_type; 154316359Sasami } 154416359Sasami } 154516359Sasami#endif /* !PC98 */ 154616359Sasami#ifdef PC98 154742262Skato if (IS_8251(com->pc98_if_type)) { 154854174Snyan if (com->pc98_8251fifo && !COM_NOFIFO(flags)) 154954174Snyan com->tx_fifo_size = 16; 155042262Skato com_int_TxRx_disable( com ); 1551135374Sphk com_cflag_and_speed_set( com, tp->t_init_in.c_cflag, comdefaultrate ); 155242262Skato com_tiocm_bic( com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE ); 155342262Skato com_send_break_off( com ); 155454174Snyan 155554174Snyan if (com->pc98_if_type == COM_IF_INTERNAL) { 155654174Snyan printf(" (internal%s%s)", 155754174Snyan com->pc98_8251fifo ? " fifo" : "", 155854174Snyan PC98SIO_baud_rate_port(com->pc98_if_type) != -1 ? 155954174Snyan " v-fast" : ""); 156054174Snyan } else { 156154174Snyan printf(" 8251%s", if_8251_type[com->pc98_if_type & 0x0f].name); 156254174Snyan } 156316359Sasami } else { 156416359Sasami#endif /* PC98 */ 156560472Snyan sio_setreg(com, com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); 156616359Sasami DELAY(100); 156716359Sasami switch (inb(com->int_id_port) & IIR_FIFO_MASK) { 156816359Sasami case FIFO_RX_LOW: 156916359Sasami printf(" 16450"); 157016359Sasami break; 157116359Sasami case FIFO_RX_MEDL: 157216359Sasami printf(" 16450?"); 157316359Sasami break; 157416359Sasami case FIFO_RX_MEDH: 157516359Sasami printf(" 16550?"); 157616359Sasami break; 157716359Sasami case FIFO_RX_HIGH: 157845783Skato if (COM_NOFIFO(flags)) { 157925924Skato printf(" 16550A fifo disabled"); 1580120809Snyan break; 158126381Skato } 1582120809Snyan com->hasfifo = TRUE; 158342262Skato#ifdef PC98 158442262Skato if (com->pc98_if_type == COM_IF_RSA98III) { 158542262Skato com->tx_fifo_size = 2048; 158660472Snyan com->rsabase = iobase; 158742262Skato outb(com->rsabase + rsa_ier, 0x00); 158842262Skato outb(com->rsabase + rsa_frr, 0x00); 158942262Skato } 1590120809Snyan#else 1591120809Snyan if (COM_ST16650A(flags)) { 1592120809Snyan printf(" ST16650A"); 1593120809Snyan com->st16650a = TRUE; 1594120809Snyan com->tx_fifo_size = 32; 1595120809Snyan break; 1596120809Snyan } 1597120809Snyan if (COM_TI16754(flags)) { 1598120809Snyan printf(" TI16754"); 1599120809Snyan com->tx_fifo_size = 64; 1600120809Snyan break; 1601120809Snyan } 160242262Skato#endif 1603120809Snyan printf(" 16550A"); 160426381Skato#ifdef COM_ESP 160542262Skato#ifdef PC98 160642262Skato if (com->pc98_if_type == COM_IF_ESP98) 160742262Skato#endif 160826381Skato for (espp = likely_esp_ports; *espp != 0; espp++) 160946871Skato if (espattach(com, *espp)) { 161026381Skato com->tx_fifo_size = 1024; 161126381Skato break; 161225924Skato } 1613123847Sbde if (com->esp) 1614120809Snyan break; 161516359Sasami#endif 1616120809Snyan#ifdef PC98 1617120809Snyan com->tx_fifo_size = 16; 1618120809Snyan#else 1619120809Snyan com->tx_fifo_size = COM_FIFOSIZE(flags); 1620120809Snyan if (com->tx_fifo_size == 0) 1621120809Snyan com->tx_fifo_size = 16; 1622120809Snyan else 1623120809Snyan printf(" lookalike with %u bytes FIFO", 1624120809Snyan com->tx_fifo_size); 1625120809Snyan#endif 162616359Sasami break; 162716359Sasami } 162826381Skato 162942262Skato#ifdef PC98 163042262Skato if (com->pc98_if_type == COM_IF_RSB3000) { 163142262Skato /* Set RSB-2000/3000 Extended Buffer mode. */ 163242262Skato u_char lcr; 163360472Snyan lcr = sio_getreg(com, com_cfcr); 163460472Snyan sio_setreg(com, com_cfcr, lcr | CFCR_DLAB); 163560472Snyan sio_setreg(com, com_emr, EMR_EXBUFF | EMR_EFMODE); 163660472Snyan sio_setreg(com, com_cfcr, lcr); 163742262Skato } 163842262Skato#endif 163942262Skato 164016359Sasami#ifdef COM_ESP 1641123847Sbde if (com->esp) { 164222120Skato /* 164322120Skato * Set 16550 compatibility mode. 164422120Skato * We don't use the ESP_MODE_SCALE bit to increase the 164522120Skato * fifo trigger levels because we can't handle large 164622120Skato * bursts of input. 164722120Skato * XXX flow control should be set in comparam(), not here. 164822120Skato */ 164942265Skato#ifdef PC98 165042265Skato outb(com->esp_port + ESP98_CMD1, ESP_SETMODE); 165142265Skato outb(com->esp_port + ESP98_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); 165242265Skato#else 165316359Sasami outb(com->esp_port + ESP_CMD1, ESP_SETMODE); 165422120Skato outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); 165542265Skato#endif 165616359Sasami 165716359Sasami /* Set RTS/CTS flow control. */ 165842265Skato#ifdef PC98 165942265Skato outb(com->esp_port + ESP98_CMD1, ESP_SETFLOWTYPE); 166042265Skato outb(com->esp_port + ESP98_CMD2, ESP_FLOW_RTS); 166142265Skato outb(com->esp_port + ESP98_CMD2, ESP_FLOW_CTS); 166242265Skato#else 166316359Sasami outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); 166416359Sasami outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); 166516359Sasami outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); 166642265Skato#endif 166716359Sasami 166816359Sasami /* Set flow-control levels. */ 166942265Skato#ifdef PC98 167042265Skato outb(com->esp_port + ESP98_CMD1, ESP_SETRXFLOW); 167142265Skato outb(com->esp_port + ESP98_CMD2, HIBYTE(768)); 167242265Skato outb(com->esp_port + ESP98_CMD2, LOBYTE(768)); 167342265Skato outb(com->esp_port + ESP98_CMD2, HIBYTE(512)); 167442265Skato outb(com->esp_port + ESP98_CMD2, LOBYTE(512)); 167542265Skato#else 167616359Sasami outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); 167716359Sasami outb(com->esp_port + ESP_CMD2, HIBYTE(768)); 167816359Sasami outb(com->esp_port + ESP_CMD2, LOBYTE(768)); 167916359Sasami outb(com->esp_port + ESP_CMD2, HIBYTE(512)); 168016359Sasami outb(com->esp_port + ESP_CMD2, LOBYTE(512)); 168142265Skato#endif 168242265Skato 168342262Skato#ifdef PC98 168442262Skato /* Set UART clock prescaler. */ 168542265Skato outb(com->esp_port + ESP98_CMD1, ESP_SETCLOCK); 168642265Skato outb(com->esp_port + ESP98_CMD2, 2); /* 4 times */ 168742262Skato#endif 168816359Sasami } 168916359Sasami#endif /* COM_ESP */ 169060472Snyan sio_setreg(com, com_fifo, 0); 169142262Skato#ifdef PC98 169242262Skato printf("%s", if_16550a_type[com->pc98_if_type & 0x0f].name); 169342262Skato#else 169443338Skatodetermined_type: ; 169542262Skato#endif 169616359Sasami 169716359Sasami#ifdef COM_MULTIPORT 169845783Skato if (COM_ISMULTIPORT(flags)) { 169953373Snyan device_t masterdev; 170053373Snyan 170116359Sasami com->multiport = TRUE; 170216359Sasami printf(" (multiport"); 170345783Skato if (unit == COM_MPMASTER(flags)) 170416359Sasami printf(" master"); 170516359Sasami printf(")"); 170657291Skato masterdev = devclass_get_device(sio_devclass, 170757291Skato COM_MPMASTER(flags)); 170857291Skato com->no_irq = (masterdev == NULL || bus_get_resource(masterdev, 170957291Skato SYS_RES_IRQ, 0, NULL, NULL) != 0); 171043663Skato } 171116359Sasami#endif /* COM_MULTIPORT */ 171216359Sasami#ifdef PC98 171316359Sasami } 171416359Sasami#endif 171526439Skato if (unit == comconsole) 171626439Skato printf(", console"); 171753373Snyan if (COM_IIR_TXRDYBUG(flags)) 1718120809Snyan printf(" with a buggy IIR_TXRDY implementation"); 171916359Sasami printf("\n"); 172016359Sasami 172167551Sjhb if (sio_fast_ih == NULL) { 1722151690Sru swi_add(&tty_intr_event, "sio", siopoll, NULL, SWI_TTY, 0, 172372431Skato &sio_fast_ih); 1724151690Sru swi_add(&clk_intr_event, "sio", siopoll, NULL, SWI_CLOCK, 0, 172572431Skato &sio_slow_ih); 172638603Skato } 1727136550Snyan 172851202Snyan com->flags = flags; 172945226Skato com->pps.ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; 1730136550Snyan tp->t_pps = &com->pps; 1731112032Snyan 1732112032Snyan if (COM_PPSCTS(flags)) 1733112032Snyan com->pps_bit = MSR_CTS; 1734112032Snyan else 1735112032Snyan com->pps_bit = MSR_DCD; 173645226Skato pps_init(&com->pps); 173745783Skato 173851202Snyan rid = 0; 1739135517Snyan com->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 174053373Snyan if (com->irqres) { 1741155921Sjhb ret = bus_setup_intr(dev, com->irqres, 1742166901Spiso INTR_TYPE_TTY, 1743166901Spiso siointr, NULL, com, &com->cookie); 174454255Skato if (ret) { 1745155921Sjhb ret = bus_setup_intr(dev, 174654255Skato com->irqres, INTR_TYPE_TTY, 1747166901Spiso NULL, (driver_intr_t *)siointr, 1748166901Spiso com, &com->cookie); 174954255Skato if (ret == 0) 175083539Snyan device_printf(dev, "unable to activate interrupt in fast mode - using normal mode\n"); 175154255Skato } 175253373Snyan if (ret) 175353373Snyan device_printf(dev, "could not activate interrupt\n"); 1754225203Srwatson#if defined(KDB) 175583539Snyan /* 175683539Snyan * Enable interrupts for early break-to-debugger support 175783539Snyan * on the console. 175883539Snyan */ 175983539Snyan if (ret == 0 && unit == comconsole) 176083539Snyan outb(siocniobase + com_ier, IER_ERXRDY | IER_ERLS | 176183539Snyan IER_EMSC); 176283539Snyan#endif 176353373Snyan } 176445783Skato 1765136550Snyan /* We're ready, open the doors... */ 1766151383Sphk ttycreate(tp, TS_CALLOUT, "d%r", unit); 1767136550Snyan 176845783Skato return (0); 176916359Sasami} 177016359Sasami 177116359Sasamistatic int 1772136550Snyancomopen(struct tty *tp, struct cdev *dev) 1773130924Snyan{ 1774130924Snyan struct com_s *com; 1775136550Snyan int i; 1776130924Snyan 1777136478Sphk com = tp->t_sc; 1778136478Sphk com->poll = com->no_irq; 1779136478Sphk com->poll_output = com->loses_outints; 178016359Sasami#ifdef PC98 1781136478Sphk if (IS_8251(com->pc98_if_type)) { 1782136478Sphk com_tiocm_bis(com, TIOCM_DTR|TIOCM_RTS); 1783136478Sphk pc98_msrint_start(dev); 1784136478Sphk if (com->pc98_8251fifo) { 1785136550Snyan com->pc98_8251fifo_enable = 1; 1786182835Snyan outb(I8251F_fcr, 1787182835Snyan FIFO_ENABLE | FIFO_XMT_RST | FIFO_RCV_RST); 178816359Sasami } 1789136478Sphk } 179016359Sasami#endif 1791136478Sphk if (com->hasfifo) { 179216359Sasami /* 1793136478Sphk * (Re)enable and drain fifos. 1794136478Sphk * 1795136478Sphk * Certain SMC chips cause problems if the fifos 1796136478Sphk * are enabled while input is ready. Turn off the 1797136478Sphk * fifo if necessary to clear the input. We test 1798136478Sphk * the input ready bit after enabling the fifos 1799136478Sphk * since we've already enabled them in comparam() 1800136478Sphk * and to handle races between enabling and fresh 1801136478Sphk * input. 180216359Sasami */ 1803136478Sphk for (i = 0; i < 500; i++) { 1804136478Sphk sio_setreg(com, com_fifo, 1805182835Snyan FIFO_RCV_RST | FIFO_XMT_RST | com->fifo_image); 1806136478Sphk#ifdef PC98 1807136478Sphk if (com->pc98_if_type == COM_IF_RSA98III) 1808136478Sphk outb(com->rsabase + rsa_frr , 0x00); 1809136478Sphk#endif 181016359Sasami /* 1811136478Sphk * XXX the delays are for superstitious 1812136478Sphk * historical reasons. It must be less than 1813136478Sphk * the character time at the maximum 1814136478Sphk * supported speed (87 usec at 115200 bps 1815136478Sphk * 8N1). Otherwise we might loop endlessly 1816136478Sphk * if data is streaming in. We used to use 1817136478Sphk * delays of 100. That usually worked 1818136478Sphk * because DELAY(100) used to usually delay 1819136478Sphk * for about 85 usec instead of 100. 182016359Sasami */ 1821136478Sphk DELAY(50); 182242262Skato#ifdef PC98 1823136478Sphk if (com->pc98_if_type == COM_IF_RSA98III ? 1824136478Sphk !(inb(com->rsabase + rsa_srr) & 0x08) : 1825136478Sphk !(inb(com->line_status_port) & LSR_RXRDY)) 1826136478Sphk break; 182760472Snyan#else 1828136478Sphk if (!(inb(com->line_status_port) & LSR_RXRDY)) 1829136478Sphk break; 183042262Skato#endif 1831136478Sphk sio_setreg(com, com_fifo, 0); 1832136478Sphk DELAY(50); 1833136478Sphk (void) inb(com->data_port); 183416359Sasami } 1835136550Snyan if (i == 500) 1836136478Sphk return (EIO); 1837136478Sphk } 183816359Sasami 1839136478Sphk mtx_lock_spin(&sio_lock); 184016359Sasami#ifdef PC98 1841136478Sphk if (IS_8251(com->pc98_if_type)) { 1842136550Snyan com_tiocm_bis(com, TIOCM_LE); 1843136550Snyan com->pc98_prev_modem_status = pc98_get_modem_status(com); 1844136550Snyan com_int_Rx_enable(com); 1845136478Sphk } else { 184616359Sasami#endif 1847136478Sphk (void) inb(com->line_status_port); 1848136478Sphk (void) inb(com->data_port); 1849136478Sphk com->prev_modem_status = com->last_modem_status 1850136478Sphk = inb(com->modem_status_port); 1851136478Sphk outb(com->int_ctl_port, 1852136478Sphk IER_ERXRDY | IER_ERLS | IER_EMSC 1853136478Sphk | (COM_IIR_TXRDYBUG(com->flags) ? 0 : IER_ETXRDY)); 185416359Sasami#ifdef PC98 1855136478Sphk if (com->pc98_if_type == COM_IF_RSA98III) { 1856136478Sphk outb(com->rsabase + rsa_ier, 0x1d); 1857136478Sphk outb(com->int_ctl_port, IER_ERLS | IER_EMSC); 1858136478Sphk } 185916359Sasami#endif 186042262Skato#ifdef PC98 1861136478Sphk } 186242262Skato#endif 1863136478Sphk mtx_unlock_spin(&sio_lock); 1864136478Sphk siosettimeout(); 1865136550Snyan /* XXX: should be generic ? */ 186616359Sasami#ifdef PC98 1867136478Sphk if ((IS_8251(com->pc98_if_type) && 1868136550Snyan (pc98_get_modem_status(com) & TIOCM_CAR)) || 1869136478Sphk (!IS_8251(com->pc98_if_type) && 1870136550Snyan (com->prev_modem_status & MSR_DCD)) || 1871136478Sphk ISCALLOUT(dev)) 1872136478Sphk ttyld_modem(tp, 1); 187316359Sasami#else 1874136478Sphk if (com->prev_modem_status & MSR_DCD || ISCALLOUT(dev)) 1875136478Sphk ttyld_modem(tp, 1); 187616359Sasami#endif 1877130924Snyan return (0); 1878130924Snyan} 1879130924Snyan 1880136478Sphkstatic void 1881136550Snyancomclose(tp) 1882136550Snyan struct tty *tp; 188316359Sasami{ 1884136550Snyan int s; 188516359Sasami struct com_s *com; 188616359Sasami 1887136550Snyan s = spltty(); 1888136478Sphk com = tp->t_sc; 188916359Sasami com->poll = FALSE; 189016359Sasami com->poll_output = FALSE; 189116359Sasami#ifdef PC98 1892131125Snyan com_send_break_off(com); 1893131125Snyan#else 1894131125Snyan sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); 189516359Sasami#endif 189683539Snyan 1897225203Srwatson#if defined(KDB) 189883539Snyan /* 189983539Snyan * Leave interrupts enabled and don't clear DTR if this is the 190083539Snyan * console. This allows us to detect break-to-debugger events 190183539Snyan * while the console device is closed. 190283539Snyan */ 190383539Snyan if (com->unit != comconsole) 190483539Snyan#endif 190516359Sasami { 190616359Sasami#ifdef PC98 190783539Snyan int tmp; 190842262Skato if (IS_8251(com->pc98_if_type)) 190916359Sasami com_int_TxRx_disable(com); 191016359Sasami else 191160472Snyan sio_setreg(com, com_ier, 0); 191260472Snyan if (com->pc98_if_type == COM_IF_RSA98III) 191342262Skato outb(com->rsabase + rsa_ier, 0x00); 191442262Skato if (IS_8251(com->pc98_if_type)) 191516359Sasami tmp = pc98_get_modem_status(com) & TIOCM_CAR; 191616359Sasami else 191716359Sasami tmp = com->prev_modem_status & MSR_DCD; 191883539Snyan#else 191983539Snyan sio_setreg(com, com_ier, 0); 192016359Sasami#endif 192116359Sasami if (tp->t_cflag & HUPCL 192216359Sasami /* 192316359Sasami * XXX we will miss any carrier drop between here and the 192416359Sasami * next open. Perhaps we should watch DCD even when the 192516359Sasami * port is closed; it is not sufficient to check it at 192616359Sasami * the next open because it might go up and down while 192716359Sasami * we're not watching. 192816359Sasami */ 1929136478Sphk || (!tp->t_actout 193016359Sasami#ifdef PC98 193160472Snyan && !(tmp) 193216359Sasami#else 193346766Skato && !(com->prev_modem_status & MSR_DCD) 193416359Sasami#endif 1935135374Sphk && !(tp->t_init_in.c_cflag & CLOCAL)) 193616359Sasami || !(tp->t_state & TS_ISOPEN)) { 193716359Sasami#ifdef PC98 193842262Skato if (IS_8251(com->pc98_if_type)) 193942262Skato com_tiocm_bic(com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE); 194016359Sasami else 194116359Sasami#endif 1942131125Snyan (void)commodem(tp, 0, SER_DTR); 1943131981Sphk ttydtrwaitstart(tp); 194416359Sasami } 194516359Sasami#ifdef PC98 194616359Sasami else { 194742262Skato if (IS_8251(com->pc98_if_type)) 194860472Snyan com_tiocm_bic(com, TIOCM_LE); 194916359Sasami } 195016359Sasami#endif 195116359Sasami } 195254174Snyan#ifdef PC98 195354174Snyan if (com->pc98_8251fifo) { 195454174Snyan if (com->pc98_8251fifo_enable) 1955182835Snyan outb(I8251F_fcr, FIFO_XMT_RST | FIFO_RCV_RST); 195654174Snyan com->pc98_8251fifo_enable = 0; 195754174Snyan } 195854174Snyan#endif 195916359Sasami if (com->hasfifo) { 196016359Sasami /* 196116359Sasami * Disable fifos so that they are off after controlled 196216359Sasami * reboots. Some BIOSes fail to detect 16550s when the 196316359Sasami * fifos are enabled. 196416359Sasami */ 196560472Snyan sio_setreg(com, com_fifo, 0); 196616359Sasami } 1967136478Sphk tp->t_actout = FALSE; 1968136478Sphk wakeup(&tp->t_actout); 196916359Sasami wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ 1970136550Snyan siosettimeout(); 197116359Sasami splx(s); 197216359Sasami} 197316359Sasami 197416359Sasamistatic void 197520129Sasamisiobusycheck(chan) 197620129Sasami void *chan; 197720129Sasami{ 197820129Sasami struct com_s *com; 197920129Sasami int s; 198020129Sasami 198120129Sasami com = (struct com_s *)chan; 198220129Sasami 198320129Sasami /* 198420129Sasami * Clear TS_BUSY if low-level output is complete. 198520129Sasami * spl locking is sufficient because siointr1() does not set CS_BUSY. 198625026Skato * If siointr1() clears CS_BUSY after we look at it, then we'll get 198720129Sasami * called again. Reading the line status port outside of siointr1() 198820129Sasami * is safe because CS_BUSY is clear so there are no output interrupts 198920129Sasami * to lose. 199020129Sasami */ 199120129Sasami s = spltty(); 199220129Sasami if (com->state & CS_BUSY) 199325026Skato com->extra_state &= ~CSE_BUSYCHECK; /* False alarm. */ 199423447Skato#ifdef PC98 199543539Skato else if ((IS_8251(com->pc98_if_type) && 199654174Snyan ((com->pc98_8251fifo_enable && 1997182835Snyan (inb(I8251F_lsr) & (FLSR_TxRDY | FLSR_TxEMP)) 1998182835Snyan == (FLSR_TxRDY | FLSR_TxEMP)) || 199954174Snyan (!com->pc98_8251fifo_enable && 200054174Snyan (inb(com->sts_port) & (STS8251_TxRDY | STS8251_TxEMP)) 200154174Snyan == (STS8251_TxRDY | STS8251_TxEMP)))) || 200254174Snyan ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) 200354174Snyan == (LSR_TSRE | LSR_TXRDY))) { 200423447Skato#else 200520129Sasami else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) 200620129Sasami == (LSR_TSRE | LSR_TXRDY)) { 200723447Skato#endif 200820129Sasami com->tp->t_state &= ~TS_BUSY; 200920129Sasami ttwwakeup(com->tp); 201025026Skato com->extra_state &= ~CSE_BUSYCHECK; 201120129Sasami } else 201220129Sasami timeout(siobusycheck, com, hz / 100); 201320129Sasami splx(s); 201420129Sasami} 201520129Sasami 201690011Snyanstatic u_int 201790011Snyansiodivisor(rclk, speed) 201890011Snyan u_long rclk; 201990011Snyan speed_t speed; 202090011Snyan{ 202190011Snyan long actual_speed; 202290011Snyan u_int divisor; 202390011Snyan int error; 202490011Snyan 2025120809Snyan if (speed == 0) 202690011Snyan return (0); 2027120809Snyan#if UINT_MAX > (ULONG_MAX - 1) / 8 2028120809Snyan if (speed > (ULONG_MAX - 1) / 8) 2029120809Snyan return (0); 2030120809Snyan#endif 203190011Snyan divisor = (rclk / (8UL * speed) + 1) / 2; 203290011Snyan if (divisor == 0 || divisor >= 65536) 203390011Snyan return (0); 203490011Snyan actual_speed = rclk / (16UL * divisor); 203590011Snyan 203690011Snyan /* 10 times error in percent: */ 203790011Snyan error = ((actual_speed - (long)speed) * 2000 / (long)speed + 1) / 2; 203890011Snyan 203990011Snyan /* 3.0% maximum error tolerance: */ 204090011Snyan if (error < -30 || error > 30) 204190011Snyan return (0); 204290011Snyan 204390011Snyan return (divisor); 204490011Snyan} 204590011Snyan 204665568Skato/* 204771713Snyan * Call this function with the sio_lock mutex held. It will return with the 204871713Snyan * lock still held. 204965568Skato */ 205040565Sbdestatic void 205143663Skatosioinput(com) 205243663Skato struct com_s *com; 205343663Skato{ 205443663Skato u_char *buf; 205543663Skato int incc; 205643663Skato u_char line_status; 205743663Skato int recv_data; 205843663Skato struct tty *tp; 205943663Skato 206043663Skato buf = com->ibuf; 206143663Skato tp = com->tp; 206243663Skato if (!(tp->t_state & TS_ISOPEN) || !(tp->t_cflag & CREAD)) { 206343663Skato com_events -= (com->iptr - com->ibuf); 206443663Skato com->iptr = com->ibuf; 206543663Skato return; 206643663Skato } 206743663Skato if (tp->t_state & TS_CAN_BYPASS_L_RINT) { 206843663Skato /* 206943663Skato * Avoid the grotesquely inefficient lineswitch routine 207043663Skato * (ttyinput) in "raw" mode. It usually takes about 450 207143663Skato * instructions (that's without canonical processing or echo!). 207243663Skato * slinput is reasonably fast (usually 40 instructions plus 207343663Skato * call overhead). 207443663Skato */ 207543663Skato do { 207665568Skato /* 207765568Skato * This may look odd, but it is using save-and-enable 207865568Skato * semantics instead of the save-and-disable semantics 207965568Skato * that are used everywhere else. 208065568Skato */ 208172200Sbmilekic mtx_unlock_spin(&sio_lock); 208243663Skato incc = com->iptr - buf; 208343663Skato if (tp->t_rawq.c_cc + incc > tp->t_ihiwat 208443663Skato && (com->state & CS_RTS_IFLOW 208543663Skato || tp->t_iflag & IXOFF) 208643663Skato && !(tp->t_state & TS_TBLOCK)) 208743663Skato ttyblock(tp); 208843663Skato com->delta_error_counts[CE_TTY_BUF_OVERFLOW] 208943663Skato += b_to_q((char *)buf, incc, &tp->t_rawq); 209043663Skato buf += incc; 209143663Skato tk_nin += incc; 209243663Skato tk_rawcc += incc; 209343663Skato tp->t_rawcc += incc; 209443663Skato ttwakeup(tp); 209543663Skato if (tp->t_state & TS_TTSTOP 209643663Skato && (tp->t_iflag & IXANY 209743663Skato || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { 209843663Skato tp->t_state &= ~TS_TTSTOP; 209943663Skato tp->t_lflag &= ~FLUSHO; 210043663Skato comstart(tp); 210143663Skato } 210272200Sbmilekic mtx_lock_spin(&sio_lock); 210343663Skato } while (buf < com->iptr); 210443663Skato } else { 210543663Skato do { 210665568Skato /* 210765568Skato * This may look odd, but it is using save-and-enable 210865568Skato * semantics instead of the save-and-disable semantics 210965568Skato * that are used everywhere else. 211065568Skato */ 211172200Sbmilekic mtx_unlock_spin(&sio_lock); 211243663Skato line_status = buf[com->ierroff]; 211343663Skato recv_data = *buf++; 211443663Skato if (line_status 211543663Skato & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { 211643663Skato if (line_status & LSR_BI) 211743663Skato recv_data |= TTY_BI; 211843663Skato if (line_status & LSR_FE) 211943663Skato recv_data |= TTY_FE; 212043663Skato if (line_status & LSR_OE) 212143663Skato recv_data |= TTY_OE; 212243663Skato if (line_status & LSR_PE) 212343663Skato recv_data |= TTY_PE; 212443663Skato } 2125130077Sphk ttyld_rint(tp, recv_data); 212672200Sbmilekic mtx_lock_spin(&sio_lock); 212743663Skato } while (buf < com->iptr); 212843663Skato } 212943663Skato com_events -= (com->iptr - com->ibuf); 213043663Skato com->iptr = com->ibuf; 213143663Skato 213243663Skato /* 213343663Skato * There is now room for another low-level buffer full of input, 213443663Skato * so enable RTS if it is now disabled and there is room in the 213543663Skato * high-level buffer. 213643663Skato */ 213743663Skato#ifdef PC98 213853884Snyan if (IS_8251(com->pc98_if_type)) { 213953884Snyan if ((com->state & CS_RTS_IFLOW) && 214053884Snyan !(com_tiocm_get(com) & TIOCM_RTS) && 214153884Snyan !(tp->t_state & TS_TBLOCK)) 214243663Skato com_tiocm_bis(com, TIOCM_RTS); 214353884Snyan } else { 214453884Snyan if ((com->state & CS_RTS_IFLOW) && 214553884Snyan !(com->mcr_image & MCR_RTS) && 214653884Snyan !(tp->t_state & TS_TBLOCK)) 214743663Skato outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 214853884Snyan } 214943663Skato#else 215043663Skato if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && 215143663Skato !(tp->t_state & TS_TBLOCK)) 215243663Skato outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 215343663Skato#endif 215443663Skato} 215543663Skato 2156166901Spisostatic int 215745783Skatosiointr(arg) 215845783Skato void *arg; 215916359Sasami{ 216046871Skato struct com_s *com; 2161104778Snyan#if defined(PC98) && defined(COM_MULTIPORT) 216242262Skato u_char rsa_buf_status; 216342262Skato#endif 216416359Sasami 216571713Snyan#ifndef COM_MULTIPORT 216671713Snyan com = (struct com_s *)arg; 216771713Snyan 216872200Sbmilekic mtx_lock_spin(&sio_lock); 216971713Snyan siointr1(com); 217072200Sbmilekic mtx_unlock_spin(&sio_lock); 217171713Snyan#else /* COM_MULTIPORT */ 217271713Snyan bool_t possibly_more_intrs; 217371713Snyan int unit; 217471713Snyan 217516359Sasami /* 217616359Sasami * Loop until there is no activity on any port. This is necessary 217716359Sasami * to get an interrupt edge more than to avoid another interrupt. 217816359Sasami * If the IRQ signal is just an OR of the IRQ signals from several 217916359Sasami * devices, then the edge from one may be lost because another is 218016359Sasami * on. 218116359Sasami */ 218272200Sbmilekic mtx_lock_spin(&sio_lock); 218316359Sasami do { 218416359Sasami possibly_more_intrs = FALSE; 218553373Snyan for (unit = 0; unit < sio_numunits; ++unit) { 218616359Sasami com = com_addr(unit); 218728939Skato /* 218829010Skato * XXX COM_LOCK(); 218928939Skato * would it work here, or be counter-productive? 219028939Skato */ 219116359Sasami#ifdef PC98 219216359Sasami if (com != NULL 219316359Sasami && !com->gone 219460472Snyan && IS_8251(com->pc98_if_type)) { 219516359Sasami siointr1(com); 219660472Snyan } else if (com != NULL 219716359Sasami && !com->gone 219842262Skato && com->pc98_if_type == COM_IF_RSA98III) { 219960472Snyan rsa_buf_status = 220060472Snyan inb(com->rsabase + rsa_srr) & 0xc9; 220160472Snyan if ((rsa_buf_status & 0xc8) 220260472Snyan || !(rsa_buf_status & 0x01)) { 220360472Snyan siointr1(com); 220460472Snyan if (rsa_buf_status != 220560472Snyan (inb(com->rsabase + rsa_srr) & 0xc9)) 220660472Snyan possibly_more_intrs = TRUE; 220760472Snyan } 220842262Skato } else 220942262Skato#endif 221042262Skato if (com != NULL 221142262Skato && !com->gone 221216359Sasami && (inb(com->int_id_port) & IIR_IMASK) 221316359Sasami != IIR_NOPEND) { 221416359Sasami siointr1(com); 221516359Sasami possibly_more_intrs = TRUE; 221616359Sasami } 221729010Skato /* XXX COM_UNLOCK(); */ 221816359Sasami } 221916359Sasami } while (possibly_more_intrs); 222072200Sbmilekic mtx_unlock_spin(&sio_lock); 222116359Sasami#endif /* COM_MULTIPORT */ 2222166901Spiso return (FILTER_HANDLED); 222316359Sasami} 222416359Sasami 2225122872Sbdestatic struct timespec siots[8]; 222693934Snyanstatic int siotso; 222793934Snyanstatic int volatile siotsunit = -1; 222893934Snyan 222993934Snyanstatic int 223093934Snyansysctl_siots(SYSCTL_HANDLER_ARGS) 223193934Snyan{ 223293934Snyan char buf[128]; 223393934Snyan long long delta; 223493934Snyan size_t len; 2235122872Sbde int error, i, tso; 223693934Snyan 2237122872Sbde for (i = 1, tso = siotso; i < tso; i++) { 223893934Snyan delta = (long long)(siots[i].tv_sec - siots[i - 1].tv_sec) * 223993934Snyan 1000000000 + 224093934Snyan (siots[i].tv_nsec - siots[i - 1].tv_nsec); 224193934Snyan len = sprintf(buf, "%lld\n", delta); 224293934Snyan if (delta >= 110000) 224393934Snyan len += sprintf(buf + len - 1, ": *** %ld.%09ld\n", 2244122872Sbde (long)siots[i].tv_sec, siots[i].tv_nsec) - 1; 2245122872Sbde if (i == tso - 1) 224693934Snyan buf[len - 1] = '\0'; 224793934Snyan error = SYSCTL_OUT(req, buf, len); 224893934Snyan if (error != 0) 224993934Snyan return (error); 225093934Snyan } 225193934Snyan return (0); 225293934Snyan} 225393934Snyan 225493934SnyanSYSCTL_PROC(_machdep, OID_AUTO, siots, CTLTYPE_STRING | CTLFLAG_RD, 225593934Snyan 0, 0, sysctl_siots, "A", "sio timestamps"); 225693934Snyan 225716359Sasamistatic void 225816359Sasamisiointr1(com) 225916359Sasami struct com_s *com; 226016359Sasami{ 2261120809Snyan u_char int_ctl; 2262120809Snyan u_char int_ctl_new; 226316359Sasami u_char line_status; 226416359Sasami u_char modem_status; 226516359Sasami u_char *ioptr; 226616359Sasami u_char recv_data; 226716359Sasami#ifdef PC98 226860472Snyan u_char tmp = 0; 226942262Skato u_char rsa_buf_status = 0; 227060472Snyan int rsa_tx_fifo_size = 0; 227116359Sasami#endif /* PC98 */ 2272225203Srwatson#if defined(KDB) 2273178766Speter int kdb_brk; 227416359Sasami 2275178766Speteragain: 2276178766Speter#endif 2277178766Speter 2278120809Snyan if (COM_IIR_TXRDYBUG(com->flags)) { 2279120809Snyan int_ctl = inb(com->int_ctl_port); 2280120809Snyan int_ctl_new = int_ctl; 2281120809Snyan } else { 2282120809Snyan int_ctl = 0; 2283120809Snyan int_ctl_new = 0; 2284120809Snyan } 228532332Skato 228633378Skato while (!com->gone) { 228716359Sasami#ifdef PC98 228816359Sasamistatus_read:; 228916359Sasami if (IS_8251(com->pc98_if_type)) { 229054174Snyan if (com->pc98_8251fifo_enable) 229154174Snyan tmp = inb(I8251F_lsr); 229254174Snyan else 229354174Snyan tmp = inb(com->sts_port); 229416359Sasamimore_intr: 229516359Sasami line_status = 0; 229654174Snyan if (com->pc98_8251fifo_enable) { 2297182835Snyan if (tmp & FLSR_TxRDY) line_status |= LSR_TXRDY; 2298182835Snyan if (tmp & FLSR_RxRDY) line_status |= LSR_RXRDY; 2299182835Snyan if (tmp & FLSR_TxEMP) line_status |= LSR_TSRE; 2300182835Snyan if (tmp & FLSR_PE) line_status |= LSR_PE; 2301182835Snyan if (tmp & FLSR_OE) line_status |= LSR_OE; 2302182835Snyan if (tmp & FLSR_BI) line_status |= LSR_BI; 230354174Snyan } else { 230454174Snyan if (tmp & STS8251_TxRDY) line_status |= LSR_TXRDY; 230554174Snyan if (tmp & STS8251_RxRDY) line_status |= LSR_RXRDY; 230654174Snyan if (tmp & STS8251_TxEMP) line_status |= LSR_TSRE; 230754174Snyan if (tmp & STS8251_PE) line_status |= LSR_PE; 230854174Snyan if (tmp & STS8251_OE) line_status |= LSR_OE; 230954174Snyan if (tmp & STS8251_FE) line_status |= LSR_FE; 2310182835Snyan if (tmp & STS8251_BI) line_status |= LSR_BI; 231154174Snyan } 231246460Skato } else { 231316359Sasami#endif /* PC98 */ 231445226Skato if (com->pps.ppsparam.mode & PPS_CAPTUREBOTH) { 231545226Skato modem_status = inb(com->modem_status_port); 2316112032Snyan if ((modem_status ^ com->last_modem_status) & 2317112032Snyan com->pps_bit) { 231895523Sphk pps_capture(&com->pps); 2319112032Snyan pps_event(&com->pps, 2320112032Snyan (modem_status & com->pps_bit) ? 232151202Snyan PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); 232245226Skato } 232345226Skato } 232416359Sasami line_status = inb(com->line_status_port); 232542262Skato#ifdef PC98 232646460Skato } 232742262Skato if (com->pc98_if_type == COM_IF_RSA98III) 232842262Skato rsa_buf_status = inb(com->rsabase + rsa_srr); 232942262Skato#endif /* PC98 */ 233016359Sasami 233116359Sasami /* input event? (check first to help avoid overruns) */ 233242262Skato#ifndef PC98 233316359Sasami while (line_status & LSR_RCV_MASK) { 233442262Skato#else 233542262Skato while ((line_status & LSR_RCV_MASK) 233642262Skato || (com->pc98_if_type == COM_IF_RSA98III 233742262Skato && (rsa_buf_status & 0x08))) { 233842262Skato#endif /* PC98 */ 233916359Sasami /* break/unnattached error bits or real input? */ 234016359Sasami#ifdef PC98 234142262Skato if (IS_8251(com->pc98_if_type)) { 234254174Snyan if (com->pc98_8251fifo_enable) { 234354174Snyan recv_data = inb(I8251F_data); 2344182835Snyan if (tmp & 2345182835Snyan (FLSR_PE | FLSR_OE | FLSR_BI)) { 234654174Snyan pc98_i8251_or_cmd(com, CMD8251_ER); 234716359Sasami recv_data = 0; 234854174Snyan } 234954174Snyan } else { 235054174Snyan recv_data = inb(com->data_port); 235154174Snyan if (tmp & (STS8251_PE | STS8251_OE | 2352182835Snyan STS8251_FE | STS8251_BI)) { 235354174Snyan pc98_i8251_or_cmd(com, CMD8251_ER); 235454174Snyan recv_data = 0; 235554174Snyan } 235616359Sasami } 235760472Snyan } else if (com->pc98_if_type == COM_IF_RSA98III) { 235854174Snyan if (!(rsa_buf_status & 0x08)) 235954174Snyan recv_data = 0; 236054174Snyan else 236154174Snyan recv_data = inb(com->data_port); 236242262Skato } else 236342262Skato#endif 236416359Sasami if (!(line_status & LSR_RXRDY)) 236516359Sasami recv_data = 0; 236616359Sasami else 236716359Sasami recv_data = inb(com->data_port); 2368131939Smarcel#ifdef KDB 2369119525Snyan if (com->unit == comconsole && 2370178766Speter (kdb_brk = kdb_alt_break(recv_data, 2371178766Speter &com->alt_brk_state)) != 0) { 2372178766Speter goto again; 2373178766Speter } 2374131939Smarcel#endif /* KDB */ 237520129Sasami if (line_status & (LSR_BI | LSR_FE | LSR_PE)) { 237620129Sasami /* 237720129Sasami * Don't store BI if IGNBRK or FE/PE if IGNPAR. 237820129Sasami * Otherwise, push the work to a higher level 237920129Sasami * (to handle PARMRK) if we're bypassing. 238020129Sasami * Otherwise, convert BI/FE and PE+INPCK to 0. 238120129Sasami * 238220129Sasami * This makes bypassing work right in the 238320129Sasami * usual "raw" case (IGNBRK set, and IGNPAR 238420129Sasami * and INPCK clear). 238520129Sasami * 238620129Sasami * Note: BI together with FE/PE means just BI. 238720129Sasami */ 238820129Sasami if (line_status & LSR_BI) { 2389225203Srwatson#if defined(KDB) 239020129Sasami if (com->unit == comconsole) { 2391174898Srwatson kdb_enter(KDB_WHY_BREAK, 2392174898Srwatson "Line break on console"); 239320129Sasami goto cont; 239420129Sasami } 239520129Sasami#endif 239620129Sasami if (com->tp == NULL 239720129Sasami || com->tp->t_iflag & IGNBRK) 239820129Sasami goto cont; 239920129Sasami } else { 240020129Sasami if (com->tp == NULL 240120129Sasami || com->tp->t_iflag & IGNPAR) 240220129Sasami goto cont; 240316359Sasami } 240420129Sasami if (com->tp->t_state & TS_CAN_BYPASS_L_RINT 240520129Sasami && (line_status & (LSR_BI | LSR_FE) 240620129Sasami || com->tp->t_iflag & INPCK)) 240716359Sasami recv_data = 0; 240816359Sasami } 240916359Sasami ++com->bytes_in; 2410131237Snyan if (com->tp != NULL && 2411131237Snyan com->tp->t_hotchar != 0 && recv_data == com->tp->t_hotchar) 241288900Sjhb swi_sched(sio_fast_ih, 0); 241316359Sasami ioptr = com->iptr; 241416359Sasami if (ioptr >= com->ibufend) 241516359Sasami CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); 241616359Sasami else { 2417136550Snyan if (com->tp != NULL && com->tp->t_do_timestamp) 2418136550Snyan microtime(&com->tp->t_timestamp); 241916359Sasami ++com_events; 242072431Skato swi_sched(sio_slow_ih, SWI_DELAY); 242116359Sasami#if 0 /* for testing input latency vs efficiency */ 242216359Sasamiif (com->iptr - com->ibuf == 8) 242388900Sjhb swi_sched(sio_fast_ih, 0); 242416359Sasami#endif 242516359Sasami ioptr[0] = recv_data; 242643663Skato ioptr[com->ierroff] = line_status; 242716359Sasami com->iptr = ++ioptr; 242816359Sasami if (ioptr == com->ihighwater 242916359Sasami && com->state & CS_RTS_IFLOW) 243016359Sasami#ifdef PC98 243153884Snyan IS_8251(com->pc98_if_type) ? 243253884Snyan com_tiocm_bic(com, TIOCM_RTS) : 243316359Sasami#endif 243416359Sasami outb(com->modem_ctl_port, 243516359Sasami com->mcr_image &= ~MCR_RTS); 243616359Sasami if (line_status & LSR_OE) 243716359Sasami CE_RECORD(com, CE_OVERRUN); 243816359Sasami } 243916359Sasamicont: 2440122872Sbde if (line_status & LSR_TXRDY 2441122872Sbde && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) 2442122872Sbde goto txrdy; 2443122872Sbde 244416359Sasami /* 244516359Sasami * "& 0x7F" is to avoid the gcc-1.40 generating a slow 244616359Sasami * jump from the top of the loop to here 244716359Sasami */ 244816359Sasami#ifdef PC98 244942262Skato if (IS_8251(com->pc98_if_type)) 245016359Sasami goto status_read; 245116359Sasami else 245216359Sasami#endif 245316359Sasami line_status = inb(com->line_status_port) & 0x7F; 245442262Skato#ifdef PC98 245542262Skato if (com->pc98_if_type == COM_IF_RSA98III) 245642262Skato rsa_buf_status = inb(com->rsabase + rsa_srr); 245742262Skato#endif /* PC98 */ 245816359Sasami } 245916359Sasami 246016359Sasami /* modem status change? (always check before doing output) */ 246116359Sasami#ifdef PC98 246242262Skato if (!IS_8251(com->pc98_if_type)) { 246316359Sasami#endif 246416359Sasami modem_status = inb(com->modem_status_port); 246516359Sasami if (modem_status != com->last_modem_status) { 246616359Sasami /* 246716359Sasami * Schedule high level to handle DCD changes. Note 246816359Sasami * that we don't use the delta bits anywhere. Some 246916359Sasami * UARTs mess them up, and it's easy to remember the 247016359Sasami * previous bits and calculate the delta. 247116359Sasami */ 247216359Sasami com->last_modem_status = modem_status; 247316359Sasami if (!(com->state & CS_CHECKMSR)) { 247416359Sasami com_events += LOTS_OF_EVENTS; 247516359Sasami com->state |= CS_CHECKMSR; 247688900Sjhb swi_sched(sio_fast_ih, 0); 247716359Sasami } 247816359Sasami 247916359Sasami /* handle CTS change immediately for crisp flow ctl */ 248016359Sasami if (com->state & CS_CTS_OFLOW) { 248116359Sasami if (modem_status & MSR_CTS) 248216359Sasami com->state |= CS_ODEVREADY; 248316359Sasami else 248416359Sasami com->state &= ~CS_ODEVREADY; 248516359Sasami } 248616359Sasami } 248716359Sasami#ifdef PC98 248816359Sasami } 248916359Sasami#endif 249016359Sasami 2491122872Sbdetxrdy: 249216359Sasami /* output queued and everything ready? */ 249342262Skato#ifndef PC98 249416359Sasami if (line_status & LSR_TXRDY 249516359Sasami && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { 249642262Skato#else 249742262Skato if (((com->pc98_if_type == COM_IF_RSA98III) 249842262Skato ? (rsa_buf_status & 0x02) 249942262Skato : (line_status & LSR_TXRDY)) 250042262Skato && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { 250142262Skato#endif 250254174Snyan#ifdef PC98 250354174Snyan Port_t tmp_data_port; 250454174Snyan 250554174Snyan if (IS_8251(com->pc98_if_type) && 250654174Snyan com->pc98_8251fifo_enable) 250754174Snyan tmp_data_port = I8251F_data; 250854174Snyan else 250954174Snyan tmp_data_port = com->data_port; 251054174Snyan#endif 251154174Snyan 251216359Sasami ioptr = com->obufq.l_head; 251393934Snyan if (com->tx_fifo_size > 1 && com->unit != siotsunit) { 251416359Sasami u_int ocount; 251516359Sasami 251616359Sasami ocount = com->obufq.l_tail - ioptr; 251742262Skato#ifdef PC98 251842262Skato if (com->pc98_if_type == COM_IF_RSA98III) { 251942262Skato rsa_buf_status = inb(com->rsabase + rsa_srr); 252042262Skato rsa_tx_fifo_size = 1024; 252142262Skato if (!(rsa_buf_status & 0x01)) 252254174Snyan rsa_tx_fifo_size = 2048; 252342262Skato if (ocount > rsa_tx_fifo_size) 252454174Snyan ocount = rsa_tx_fifo_size; 252542262Skato } else 252642262Skato#endif 252716359Sasami if (ocount > com->tx_fifo_size) 252816359Sasami ocount = com->tx_fifo_size; 252916359Sasami com->bytes_out += ocount; 253016359Sasami do 253154174Snyan#ifdef PC98 253254174Snyan outb(tmp_data_port, *ioptr++); 253354174Snyan#else 253416359Sasami outb(com->data_port, *ioptr++); 253554174Snyan#endif 253616359Sasami while (--ocount != 0); 253716359Sasami } else { 253854174Snyan#ifdef PC98 253954174Snyan outb(tmp_data_port, *ioptr++); 254054174Snyan#else 254116359Sasami outb(com->data_port, *ioptr++); 254254174Snyan#endif 254316359Sasami ++com->bytes_out; 2544122872Sbde if (com->unit == siotsunit 2545122872Sbde && siotso < sizeof siots / sizeof siots[0]) 2546122872Sbde nanouptime(&siots[siotso++]); 254716359Sasami } 254816359Sasami#ifdef PC98 254942262Skato if (IS_8251(com->pc98_if_type)) 255042262Skato if (!(pc98_check_i8251_interrupt(com) & IEN_TxFLAG)) 255160472Snyan com_int_Tx_enable(com); 255216359Sasami#endif 255316359Sasami com->obufq.l_head = ioptr; 2554120809Snyan if (COM_IIR_TXRDYBUG(com->flags)) 255532332Skato int_ctl_new = int_ctl | IER_ETXRDY; 255616359Sasami if (ioptr >= com->obufq.l_tail) { 255716359Sasami struct lbq *qp; 255817256Sasami 255916359Sasami qp = com->obufq.l_next; 256016359Sasami qp->l_queued = FALSE; 256116359Sasami qp = qp->l_next; 256216359Sasami if (qp != NULL) { 256316359Sasami com->obufq.l_head = qp->l_head; 256416359Sasami com->obufq.l_tail = qp->l_tail; 256516359Sasami com->obufq.l_next = qp; 256616359Sasami } else { 256716359Sasami /* output just completed */ 2568120809Snyan if (COM_IIR_TXRDYBUG(com->flags)) 2569120809Snyan int_ctl_new = int_ctl 2570120809Snyan & ~IER_ETXRDY; 257116359Sasami com->state &= ~CS_BUSY; 257217256Sasami#if defined(PC98) 257360472Snyan if (IS_8251(com->pc98_if_type) && 257460472Snyan pc98_check_i8251_interrupt(com) & IEN_TxFLAG) 257542262Skato com_int_Tx_disable(com); 257617256Sasami#endif 257716359Sasami } 257816359Sasami if (!(com->state & CS_ODONE)) { 257916359Sasami com_events += LOTS_OF_EVENTS; 258016359Sasami com->state |= CS_ODONE; 258167551Sjhb /* handle at high level ASAP */ 258288900Sjhb swi_sched(sio_fast_ih, 0); 258316359Sasami } 258416359Sasami } 258543663Skato#ifdef PC98 2586120809Snyan if (COM_IIR_TXRDYBUG(com->flags) 2587120809Snyan && int_ctl != int_ctl_new) { 258842262Skato if (com->pc98_if_type == COM_IF_RSA98III) { 258953373Snyan int_ctl_new &= ~(IER_ETXRDY | IER_ERXRDY); 2590120809Snyan outb(com->int_ctl_port, int_ctl_new); 259153373Snyan outb(com->rsabase + rsa_ier, 0x1d); 259242262Skato } else 2593120809Snyan outb(com->int_ctl_port, int_ctl_new); 2594120809Snyan } 2595120809Snyan#else 2596120809Snyan if (COM_IIR_TXRDYBUG(com->flags) 2597120809Snyan && int_ctl != int_ctl_new) 2598120809Snyan outb(com->int_ctl_port, int_ctl_new); 259943663Skato#endif 260016359Sasami } 260116359Sasami#ifdef PC98 260216359Sasami else if (line_status & LSR_TXRDY) { 260342262Skato if (IS_8251(com->pc98_if_type)) 260460472Snyan if (pc98_check_i8251_interrupt(com) & IEN_TxFLAG) 260542262Skato com_int_Tx_disable(com); 260616359Sasami } 260754174Snyan if (IS_8251(com->pc98_if_type)) { 260854174Snyan if (com->pc98_8251fifo_enable) { 2609182835Snyan if ((tmp = inb(I8251F_lsr)) & FLSR_RxRDY) 261054174Snyan goto more_intr; 261154174Snyan } else { 261254174Snyan if ((tmp = inb(com->sts_port)) & STS8251_RxRDY) 261354174Snyan goto more_intr; 261454174Snyan } 261554174Snyan } 261616359Sasami#endif 261716359Sasami 261816359Sasami /* finished? */ 261916359Sasami#ifndef COM_MULTIPORT 262016359Sasami#ifdef PC98 262142262Skato if (IS_8251(com->pc98_if_type)) 262216359Sasami return; 262316359Sasami#endif 262416359Sasami if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) 262516359Sasami#endif /* COM_MULTIPORT */ 262616359Sasami return; 262716359Sasami } 262816359Sasami} 262916359Sasami 263065568Skato/* software interrupt handler for SWI_TTY */ 263138297Skatostatic void 263267580Sjhbsiopoll(void *dummy) 263316359Sasami{ 263416359Sasami int unit; 263516359Sasami 263616359Sasami if (com_events == 0) 263716359Sasami return; 263816359Sasamirepeat: 263953373Snyan for (unit = 0; unit < sio_numunits; ++unit) { 264016359Sasami struct com_s *com; 264116359Sasami int incc; 264216359Sasami struct tty *tp; 264316359Sasami 264416359Sasami com = com_addr(unit); 264516359Sasami if (com == NULL) 264616359Sasami continue; 264716359Sasami tp = com->tp; 264837025Skato if (tp == NULL || com->gone) { 264916359Sasami /* 265037025Skato * Discard any events related to never-opened or 265137025Skato * going-away devices. 265216359Sasami */ 265372200Sbmilekic mtx_lock_spin(&sio_lock); 265416359Sasami incc = com->iptr - com->ibuf; 265516359Sasami com->iptr = com->ibuf; 265616359Sasami if (com->state & CS_CHECKMSR) { 265716359Sasami incc += LOTS_OF_EVENTS; 265816359Sasami com->state &= ~CS_CHECKMSR; 265916359Sasami } 266016359Sasami com_events -= incc; 266172200Sbmilekic mtx_unlock_spin(&sio_lock); 266216359Sasami continue; 266316359Sasami } 266443663Skato if (com->iptr != com->ibuf) { 266572200Sbmilekic mtx_lock_spin(&sio_lock); 266643663Skato sioinput(com); 266772200Sbmilekic mtx_unlock_spin(&sio_lock); 266816359Sasami } 266916359Sasami if (com->state & CS_CHECKMSR) { 267016359Sasami u_char delta_modem_status; 267116359Sasami 267216359Sasami#ifdef PC98 267342262Skato if (!IS_8251(com->pc98_if_type)) { 267416359Sasami#endif 267572200Sbmilekic mtx_lock_spin(&sio_lock); 267616359Sasami delta_modem_status = com->last_modem_status 267716359Sasami ^ com->prev_modem_status; 267816359Sasami com->prev_modem_status = com->last_modem_status; 267916359Sasami com_events -= LOTS_OF_EVENTS; 268016359Sasami com->state &= ~CS_CHECKMSR; 268172200Sbmilekic mtx_unlock_spin(&sio_lock); 268216359Sasami if (delta_modem_status & MSR_DCD) 2683130095Sphk ttyld_modem(tp, 2684130095Sphk com->prev_modem_status & MSR_DCD); 268516359Sasami#ifdef PC98 268616359Sasami } 268716359Sasami#endif 268816359Sasami } 268916359Sasami if (com->state & CS_ODONE) { 269072200Sbmilekic mtx_lock_spin(&sio_lock); 269116359Sasami com_events -= LOTS_OF_EVENTS; 269216359Sasami com->state &= ~CS_ODONE; 269372200Sbmilekic mtx_unlock_spin(&sio_lock); 269425026Skato if (!(com->state & CS_BUSY) 269525026Skato && !(com->extra_state & CSE_BUSYCHECK)) { 269620129Sasami timeout(siobusycheck, com, hz / 100); 269725026Skato com->extra_state |= CSE_BUSYCHECK; 269825026Skato } 2699130077Sphk ttyld_start(tp); 270016359Sasami } 270116359Sasami if (com_events == 0) 270216359Sasami break; 270316359Sasami } 270416359Sasami if (com_events >= LOTS_OF_EVENTS) 270516359Sasami goto repeat; 270616359Sasami} 270716359Sasami 2708131403Snyanstatic void 2709131125Snyancombreak(tp, sig) 2710131125Snyan struct tty *tp; 2711131125Snyan int sig; 2712131125Snyan{ 2713131125Snyan struct com_s *com; 2714131125Snyan 2715135374Sphk com = tp->t_sc; 2716131125Snyan 2717131125Snyan#ifdef PC98 2718131125Snyan if (sig) 2719131125Snyan com_send_break_on(com); 2720131125Snyan else 2721131125Snyan com_send_break_off(com); 2722131125Snyan#else 2723131125Snyan if (sig) 2724131125Snyan sio_setreg(com, com_cfcr, com->cfcr_image |= CFCR_SBREAK); 2725131125Snyan else 2726131125Snyan sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); 2727131125Snyan#endif 2728131125Snyan} 2729131125Snyan 2730131125Snyanstatic int 273116359Sasamicomparam(tp, t) 273216359Sasami struct tty *tp; 273316359Sasami struct termios *t; 273416359Sasami{ 273516359Sasami u_int cfcr; 273616359Sasami int cflag; 273716359Sasami struct com_s *com; 273890011Snyan u_int divisor; 273918846Sasami u_char dlbh; 274018846Sasami u_char dlbl; 2741120809Snyan u_char efr_flowbits; 274216359Sasami int s; 274316359Sasami#ifdef PC98 274442262Skato u_char param = 0; 274516359Sasami#endif 274616359Sasami 2747135374Sphk com = tp->t_sc; 274890011Snyan if (com == NULL) 274990011Snyan return (ENODEV); 275060472Snyan 275190011Snyan#ifdef PC98 275260472Snyan cfcr = 0; 275390011Snyan 275442262Skato if (IS_8251(com->pc98_if_type)) { 275591865Snyan if (pc98_ttspeedtab(com, t->c_ospeed, &divisor) != 0) 275690011Snyan return (EINVAL); 275742262Skato } else { 275890011Snyan#endif 275916359Sasami /* check requested parameters */ 2760120809Snyan if (t->c_ispeed != (t->c_ospeed != 0 ? t->c_ospeed : tp->t_ospeed)) 2761120809Snyan return (EINVAL); 2762120809Snyan divisor = siodivisor(com->rclk, t->c_ispeed); 2763120809Snyan if (divisor == 0) 2764120809Snyan return (EINVAL); 276590011Snyan#ifdef PC98 276690011Snyan } 276742262Skato#endif 276816359Sasami 276916359Sasami /* parameters are OK, convert them to the com struct and the device */ 277016359Sasami s = spltty(); 277116359Sasami#ifdef PC98 277242262Skato if (IS_8251(com->pc98_if_type)) { 2773120809Snyan if (t->c_ospeed == 0) 277460472Snyan com_tiocm_bic(com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE); 277532089Skato else 277660472Snyan com_tiocm_bis(com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE); 277760472Snyan } else 277816359Sasami#endif 2779120809Snyan if (t->c_ospeed == 0) 2780131125Snyan (void)commodem(tp, 0, SER_DTR); /* hang up line */ 278116359Sasami else 2782131125Snyan (void)commodem(tp, SER_DTR, 0); 278316359Sasami cflag = t->c_cflag; 278416359Sasami#ifdef PC98 278542262Skato if (!IS_8251(com->pc98_if_type)) { 278616359Sasami#endif 278716359Sasami switch (cflag & CSIZE) { 278816359Sasami case CS5: 278916359Sasami cfcr = CFCR_5BITS; 279016359Sasami break; 279116359Sasami case CS6: 279216359Sasami cfcr = CFCR_6BITS; 279316359Sasami break; 279416359Sasami case CS7: 279516359Sasami cfcr = CFCR_7BITS; 279616359Sasami break; 279716359Sasami default: 279816359Sasami cfcr = CFCR_8BITS; 279916359Sasami break; 280016359Sasami } 280116359Sasami if (cflag & PARENB) { 280216359Sasami cfcr |= CFCR_PENAB; 280316359Sasami if (!(cflag & PARODD)) 280416359Sasami cfcr |= CFCR_PEVEN; 280516359Sasami } 280616359Sasami if (cflag & CSTOPB) 280716359Sasami cfcr |= CFCR_STOPB; 280816359Sasami 2809120809Snyan if (com->hasfifo) { 281016359Sasami /* 281116359Sasami * Use a fifo trigger level low enough so that the input 281216359Sasami * latency from the fifo is less than about 16 msec and 281316359Sasami * the total latency is less than about 30 msec. These 281416359Sasami * latencies are reasonable for humans. Serial comms 281516359Sasami * protocols shouldn't expect anything better since modem 281616359Sasami * latencies are larger. 281788955Snyan * 2818120809Snyan * The fifo trigger level cannot be set at RX_HIGH for high 2819120809Snyan * speed connections without further work on reducing 2820120809Snyan * interrupt disablement times in other parts of the system, 2821120809Snyan * without producing silo overflow errors. 282216359Sasami */ 282393934Snyan com->fifo_image = com->unit == siotsunit ? 0 2824120809Snyan : t->c_ispeed <= 4800 282588955Snyan ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_MEDH; 282622120Skato#ifdef COM_ESP 282722120Skato /* 282822120Skato * The Hayes ESP card needs the fifo DMA mode bit set 282922120Skato * in compatibility mode. If not, it will interrupt 283022120Skato * for each character received. 283122120Skato */ 283222120Skato if (com->esp) 283322120Skato com->fifo_image |= FIFO_DMA_MODE; 283422120Skato#endif 283560472Snyan sio_setreg(com, com_fifo, com->fifo_image); 283616359Sasami } 283716359Sasami#ifdef PC98 283816359Sasami } 283916359Sasami#endif 284016359Sasami 284165611Skato /* 284265611Skato * This returns with interrupts disabled so that we can complete 284365611Skato * the speed change atomically. Keeping interrupts disabled is 284465611Skato * especially important while com_data is hidden. 284565611Skato */ 284643663Skato (void) siosetwater(com, t->c_ispeed); 284716359Sasami 284816359Sasami#ifdef PC98 284942262Skato if (IS_8251(com->pc98_if_type)) 285060472Snyan com_cflag_and_speed_set(com, cflag, t->c_ospeed); 285142262Skato else { 285216359Sasami#endif 2853120809Snyan sio_setreg(com, com_cfcr, cfcr | CFCR_DLAB); 2854120809Snyan /* 2855120809Snyan * Only set the divisor registers if they would change, since on 2856120809Snyan * some 16550 incompatibles (UMC8669F), setting them while input 2857120809Snyan * is arriving loses sync until data stops arriving. 2858120809Snyan */ 2859120809Snyan dlbl = divisor & 0xFF; 2860120809Snyan if (sio_getreg(com, com_dlbl) != dlbl) 2861120809Snyan sio_setreg(com, com_dlbl, dlbl); 2862120809Snyan dlbh = divisor >> 8; 2863120809Snyan if (sio_getreg(com, com_dlbh) != dlbh) 2864120809Snyan sio_setreg(com, com_dlbh, dlbh); 286542262Skato#ifdef PC98 286642262Skato } 286742262Skato#endif 286827479Skato 2869120809Snyan efr_flowbits = 0; 287032546Skato 287132546Skato if (cflag & CRTS_IFLOW) { 287220127Sasami com->state |= CS_RTS_IFLOW; 2873120809Snyan efr_flowbits |= EFR_AUTORTS; 287420127Sasami /* 287520127Sasami * If CS_RTS_IFLOW just changed from off to on, the change 287620127Sasami * needs to be propagated to MCR_RTS. This isn't urgent, 287720127Sasami * so do it later by calling comstart() instead of repeating 287820127Sasami * a lot of code from comstart() here. 287920127Sasami */ 288020127Sasami } else if (com->state & CS_RTS_IFLOW) { 288116359Sasami com->state &= ~CS_RTS_IFLOW; 288220127Sasami /* 288320127Sasami * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS 288420127Sasami * on here, since comstart() won't do it later. 288520127Sasami */ 288623447Skato#ifdef PC98 288742262Skato if (IS_8251(com->pc98_if_type)) 288823447Skato com_tiocm_bis(com, TIOCM_RTS); 288923447Skato else 289060472Snyan outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 289160472Snyan#else 289220127Sasami outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 289360472Snyan#endif 289420127Sasami } 289516359Sasami 289616359Sasami /* 289716359Sasami * Set up state to handle output flow control. 289816359Sasami * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? 289916359Sasami * Now has 10+ msec latency, while CTS flow has 50- usec latency. 290016359Sasami */ 290116359Sasami com->state |= CS_ODEVREADY; 290216359Sasami com->state &= ~CS_CTS_OFLOW; 290342262Skato#ifdef PC98 290442262Skato if (com->pc98_if_type == COM_IF_RSA98III) { 290542262Skato param = inb(com->rsabase + rsa_msr); 290642262Skato outb(com->rsabase + rsa_msr, param & 0x14); 290742262Skato } 290842262Skato#endif 290916359Sasami if (cflag & CCTS_OFLOW) { 291016359Sasami com->state |= CS_CTS_OFLOW; 2911120809Snyan efr_flowbits |= EFR_AUTOCTS; 291216359Sasami#ifdef PC98 291342262Skato if (IS_8251(com->pc98_if_type)) { 291416359Sasami if (!(pc98_get_modem_status(com) & TIOCM_CTS)) 291516359Sasami com->state &= ~CS_ODEVREADY; 2916120809Snyan } else if (com->pc98_if_type == COM_IF_RSA98III) { 2917120809Snyan /* Set automatic flow control mode */ 2918120809Snyan outb(com->rsabase + rsa_msr, param | 0x08); 2919120809Snyan } else 292016359Sasami#endif 292116359Sasami if (!(com->last_modem_status & MSR_CTS)) 292232546Skato com->state &= ~CS_ODEVREADY; 292316359Sasami } 292427479Skato 292542262Skato#ifdef PC98 292660472Snyan if (!IS_8251(com->pc98_if_type)) 2927120809Snyan sio_setreg(com, com_cfcr, com->cfcr_image = cfcr); 2928120809Snyan#else 2929120809Snyan if (com->st16650a) { 2930120809Snyan sio_setreg(com, com_lcr, LCR_EFR_ENABLE); 2931120809Snyan sio_setreg(com, com_efr, 2932120809Snyan (sio_getreg(com, com_efr) 2933120809Snyan & ~(EFR_AUTOCTS | EFR_AUTORTS)) | efr_flowbits); 2934120809Snyan } 2935120809Snyan sio_setreg(com, com_cfcr, com->cfcr_image = cfcr); 293642262Skato#endif 293727479Skato 293816359Sasami /* XXX shouldn't call functions while intrs are disabled. */ 2939131134Sphk ttyldoptim(tp); 294016359Sasami 294172200Sbmilekic mtx_unlock_spin(&sio_lock); 294216359Sasami splx(s); 294320127Sasami comstart(tp); 294443663Skato if (com->ibufold != NULL) { 294543663Skato free(com->ibufold, M_DEVBUF); 294643663Skato com->ibufold = NULL; 294743663Skato } 294816359Sasami return (0); 294916359Sasami} 295016359Sasami 295165611Skato/* 295271713Snyan * This function must be called with the sio_lock mutex released and will 295371713Snyan * return with it obtained. 295465611Skato */ 295543663Skatostatic int 295643663Skatosiosetwater(com, speed) 295743663Skato struct com_s *com; 295843663Skato speed_t speed; 295943663Skato{ 296043663Skato int cp4ticks; 296143663Skato u_char *ibuf; 296243663Skato int ibufsize; 296343663Skato struct tty *tp; 296443663Skato 296543663Skato /* 296643663Skato * Make the buffer size large enough to handle a softtty interrupt 296743663Skato * latency of about 2 ticks without loss of throughput or data 296843663Skato * (about 3 ticks if input flow control is not used or not honoured, 296943663Skato * but a bit less for CS5-CS7 modes). 297043663Skato */ 297143663Skato cp4ticks = speed / 10 / hz * 4; 297243663Skato for (ibufsize = 128; ibufsize < cp4ticks;) 297343663Skato ibufsize <<= 1; 297443663Skato#ifdef PC98 297543663Skato if (com->pc98_if_type == COM_IF_RSA98III) 297643663Skato ibufsize = 2048; 297743663Skato#endif 297865611Skato if (ibufsize == com->ibufsize) { 297972200Sbmilekic mtx_lock_spin(&sio_lock); 298043663Skato return (0); 298165611Skato } 298243663Skato 298343663Skato /* 298443663Skato * Allocate input buffer. The extra factor of 2 in the size is 298543663Skato * to allow for an error byte for each input byte. 298643663Skato */ 298743663Skato ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT); 298865611Skato if (ibuf == NULL) { 298972200Sbmilekic mtx_lock_spin(&sio_lock); 299043663Skato return (ENOMEM); 299165611Skato } 299243663Skato 299343663Skato /* Initialize non-critical variables. */ 299443663Skato com->ibufold = com->ibuf; 299543663Skato com->ibufsize = ibufsize; 299643663Skato tp = com->tp; 299743663Skato if (tp != NULL) { 299843663Skato tp->t_ififosize = 2 * ibufsize; 299943663Skato tp->t_ispeedwat = (speed_t)-1; 300043663Skato tp->t_ospeedwat = (speed_t)-1; 300143663Skato } 300243663Skato 300343663Skato /* 300443663Skato * Read current input buffer, if any. Continue with interrupts 300543663Skato * disabled. 300643663Skato */ 300772200Sbmilekic mtx_lock_spin(&sio_lock); 300843663Skato if (com->iptr != com->ibuf) 300943663Skato sioinput(com); 301043663Skato 301143663Skato /*- 301243663Skato * Initialize critical variables, including input buffer watermarks. 301343663Skato * The external device is asked to stop sending when the buffer 301443663Skato * exactly reaches high water, or when the high level requests it. 301543663Skato * The high level is notified immediately (rather than at a later 301643663Skato * clock tick) when this watermark is reached. 301743663Skato * The buffer size is chosen so the watermark should almost never 301843663Skato * be reached. 301943663Skato * The low watermark is invisibly 0 since the buffer is always 302043663Skato * emptied all at once. 302143663Skato */ 302243663Skato com->iptr = com->ibuf = ibuf; 302343663Skato com->ibufend = ibuf + ibufsize; 302443663Skato com->ierroff = ibufsize; 302543663Skato com->ihighwater = ibuf + 3 * ibufsize / 4; 302643663Skato return (0); 302743663Skato} 302843663Skato 302916359Sasamistatic void 303016359Sasamicomstart(tp) 303116359Sasami struct tty *tp; 303216359Sasami{ 303316359Sasami struct com_s *com; 303416359Sasami int s; 303516359Sasami 3036135374Sphk com = tp->t_sc; 303757928Skato if (com == NULL) 303857928Skato return; 303916359Sasami s = spltty(); 304072200Sbmilekic mtx_lock_spin(&sio_lock); 304116359Sasami if (tp->t_state & TS_TTSTOP) 304216359Sasami com->state &= ~CS_TTGO; 304316359Sasami else 304416359Sasami com->state |= CS_TTGO; 304516359Sasami if (tp->t_state & TS_TBLOCK) { 304616359Sasami#ifdef PC98 304753884Snyan if (IS_8251(com->pc98_if_type)) { 304853884Snyan if ((com_tiocm_get(com) & TIOCM_RTS) && 304953884Snyan (com->state & CS_RTS_IFLOW)) 305053884Snyan com_tiocm_bic(com, TIOCM_RTS); 305153884Snyan } else { 305253884Snyan if ((com->mcr_image & MCR_RTS) && 305353884Snyan (com->state & CS_RTS_IFLOW)) 305453884Snyan outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); 305553884Snyan } 305616359Sasami#else 305716359Sasami if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) 305853884Snyan outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); 305916359Sasami#endif 306016359Sasami } else { 306116359Sasami#ifdef PC98 306253884Snyan if (IS_8251(com->pc98_if_type)) { 306353884Snyan if (!(com_tiocm_get(com) & TIOCM_RTS) && 306453884Snyan com->iptr < com->ihighwater && 306553884Snyan com->state & CS_RTS_IFLOW) 306653884Snyan com_tiocm_bis(com, TIOCM_RTS); 306753884Snyan } else { 306853884Snyan if (!(com->mcr_image & MCR_RTS) && 306953884Snyan com->iptr < com->ihighwater && 307053884Snyan com->state & CS_RTS_IFLOW) 307153884Snyan outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 307253884Snyan } 307316359Sasami#else 307420127Sasami if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater 307520127Sasami && com->state & CS_RTS_IFLOW) 307653884Snyan outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); 307716359Sasami#endif 307816359Sasami } 307972200Sbmilekic mtx_unlock_spin(&sio_lock); 308016359Sasami if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { 308132089Skato ttwwakeup(tp); 308216359Sasami splx(s); 308316359Sasami return; 308416359Sasami } 308516359Sasami if (tp->t_outq.c_cc != 0) { 308616359Sasami struct lbq *qp; 308716359Sasami struct lbq *next; 308816359Sasami 308916359Sasami if (!com->obufs[0].l_queued) { 309016359Sasami com->obufs[0].l_tail 309116359Sasami = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, 309260472Snyan#ifdef PC98 309360472Snyan com->obufsize); 309460472Snyan#else 309516359Sasami sizeof com->obuf1); 309642262Skato#endif 309716359Sasami com->obufs[0].l_next = NULL; 309816359Sasami com->obufs[0].l_queued = TRUE; 309972200Sbmilekic mtx_lock_spin(&sio_lock); 310016359Sasami if (com->state & CS_BUSY) { 310116359Sasami qp = com->obufq.l_next; 310216359Sasami while ((next = qp->l_next) != NULL) 310316359Sasami qp = next; 310416359Sasami qp->l_next = &com->obufs[0]; 310516359Sasami } else { 310616359Sasami com->obufq.l_head = com->obufs[0].l_head; 310716359Sasami com->obufq.l_tail = com->obufs[0].l_tail; 310816359Sasami com->obufq.l_next = &com->obufs[0]; 310916359Sasami com->state |= CS_BUSY; 311016359Sasami } 311172200Sbmilekic mtx_unlock_spin(&sio_lock); 311216359Sasami } 311316359Sasami if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { 311416359Sasami com->obufs[1].l_tail 311516359Sasami = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, 311660472Snyan#ifdef PC98 311760472Snyan com->obufsize); 311860472Snyan#else 311916359Sasami sizeof com->obuf2); 312042262Skato#endif 312116359Sasami com->obufs[1].l_next = NULL; 312216359Sasami com->obufs[1].l_queued = TRUE; 312372200Sbmilekic mtx_lock_spin(&sio_lock); 312416359Sasami if (com->state & CS_BUSY) { 312516359Sasami qp = com->obufq.l_next; 312616359Sasami while ((next = qp->l_next) != NULL) 312716359Sasami qp = next; 312816359Sasami qp->l_next = &com->obufs[1]; 312916359Sasami } else { 313016359Sasami com->obufq.l_head = com->obufs[1].l_head; 313116359Sasami com->obufq.l_tail = com->obufs[1].l_tail; 313216359Sasami com->obufq.l_next = &com->obufs[1]; 313316359Sasami com->state |= CS_BUSY; 313416359Sasami } 313572200Sbmilekic mtx_unlock_spin(&sio_lock); 313616359Sasami } 313716359Sasami tp->t_state |= TS_BUSY; 313816359Sasami } 313972200Sbmilekic mtx_lock_spin(&sio_lock); 314016359Sasami if (com->state >= (CS_BUSY | CS_TTGO)) 314116359Sasami siointr1(com); /* fake interrupt to start output */ 314272200Sbmilekic mtx_unlock_spin(&sio_lock); 314316359Sasami ttwwakeup(tp); 314416359Sasami splx(s); 314516359Sasami} 314616359Sasami 314716359Sasamistatic void 314851654Sphkcomstop(tp, rw) 314916359Sasami struct tty *tp; 315016359Sasami int rw; 315116359Sasami{ 315216359Sasami struct com_s *com; 315342262Skato#ifdef PC98 315442262Skato int rsa98_tmp = 0; 315542262Skato#endif 315616359Sasami 3157135374Sphk com = tp->t_sc; 315857928Skato if (com == NULL || com->gone) 315916359Sasami return; 316072200Sbmilekic mtx_lock_spin(&sio_lock); 316160472Snyan if (rw & FWRITE) { 316242262Skato#ifdef PC98 316360472Snyan if (!IS_8251(com->pc98_if_type)) { 316442262Skato#endif 316520129Sasami if (com->hasfifo) 316622120Skato#ifdef COM_ESP 316722120Skato /* XXX avoid h/w bug. */ 316822120Skato if (!com->esp) 316922120Skato#endif 317060472Snyan sio_setreg(com, com_fifo, 317160472Snyan FIFO_XMT_RST | com->fifo_image); 317242262Skato#ifdef PC98 317360472Snyan if (com->pc98_if_type == COM_IF_RSA98III) 317460472Snyan for (rsa98_tmp = 0; rsa98_tmp < 2048; rsa98_tmp++) 317560472Snyan sio_setreg(com, com_fifo, 317660472Snyan FIFO_XMT_RST | com->fifo_image); 317760472Snyan } 317842262Skato#endif 317916359Sasami com->obufs[0].l_queued = FALSE; 318016359Sasami com->obufs[1].l_queued = FALSE; 318116359Sasami if (com->state & CS_ODONE) 318216359Sasami com_events -= LOTS_OF_EVENTS; 318316359Sasami com->state &= ~(CS_ODONE | CS_BUSY); 318416359Sasami com->tp->t_state &= ~TS_BUSY; 318516359Sasami } 318616359Sasami if (rw & FREAD) { 318760472Snyan#ifdef PC98 318860472Snyan if (!IS_8251(com->pc98_if_type)) { 318960472Snyan if (com->pc98_if_type == COM_IF_RSA98III) 319060472Snyan for (rsa98_tmp = 0; rsa98_tmp < 2048; rsa98_tmp++) 319160472Snyan sio_getreg(com, com_data); 319260472Snyan#endif 319320129Sasami if (com->hasfifo) 319422120Skato#ifdef COM_ESP 319522120Skato /* XXX avoid h/w bug. */ 319622120Skato if (!com->esp) 319722120Skato#endif 319860472Snyan sio_setreg(com, com_fifo, 319960472Snyan FIFO_RCV_RST | com->fifo_image); 320042262Skato#ifdef PC98 320160472Snyan } 320242262Skato#endif 320316359Sasami com_events -= (com->iptr - com->ibuf); 320416359Sasami com->iptr = com->ibuf; 320516359Sasami } 320672200Sbmilekic mtx_unlock_spin(&sio_lock); 320716359Sasami comstart(tp); 320816359Sasami} 320916359Sasami 321016359Sasamistatic int 3211136550Snyancommodem(struct tty *tp, int sigon, int sigoff) 3212131125Snyan{ 321316359Sasami struct com_s *com; 3214131125Snyan int bitand, bitor, msr; 3215131125Snyan#ifdef PC98 3216131125Snyan int clr, set; 3217131125Snyan#endif 321816359Sasami 3219135374Sphk com = tp->t_sc; 3220131125Snyan if (com->gone) 3221131125Snyan return(0); 3222131125Snyan if (sigon != 0 || sigoff != 0) { 3223131125Snyan#ifdef PC98 3224131125Snyan if (IS_8251(com->pc98_if_type)) { 3225131125Snyan bitand = bitor = 0; 3226131125Snyan clr = set = 0; 3227131125Snyan if (sigoff & SER_DTR) { 3228131125Snyan bitand |= TIOCM_DTR; 3229131125Snyan clr |= CMD8251_DTR; 3230131125Snyan } 3231131125Snyan if (sigoff & SER_RTS) { 3232131125Snyan bitand |= TIOCM_RTS; 3233131125Snyan clr |= CMD8251_RxEN | CMD8251_RTS; 3234131125Snyan } 3235131125Snyan if (sigon & SER_DTR) { 3236131125Snyan bitor |= TIOCM_DTR; 3237131125Snyan set |= CMD8251_TxEN | CMD8251_RxEN | 3238131125Snyan CMD8251_DTR; 3239131125Snyan } 3240131125Snyan if (sigon & SER_RTS) { 3241131125Snyan bitor |= TIOCM_RTS; 3242131125Snyan set |= CMD8251_TxEN | CMD8251_RxEN | 3243131125Snyan CMD8251_RTS; 3244131125Snyan } 3245131125Snyan bitand = ~bitand; 3246131125Snyan mtx_lock_spin(&sio_lock); 3247131125Snyan com->pc98_prev_modem_status &= bitand; 3248131125Snyan com->pc98_prev_modem_status |= bitor; 3249131125Snyan pc98_i8251_clear_or_cmd(com, clr, set); 3250131125Snyan mtx_unlock_spin(&sio_lock); 3251131125Snyan return (0); 3252131125Snyan } else { 3253131125Snyan#endif 3254131125Snyan bitand = bitor = 0; 3255131125Snyan if (sigoff & SER_DTR) 3256131125Snyan bitand |= MCR_DTR; 3257131125Snyan if (sigoff & SER_RTS) 3258131125Snyan bitand |= MCR_RTS; 3259131125Snyan if (sigon & SER_DTR) 3260131125Snyan bitor |= MCR_DTR; 3261131125Snyan if (sigon & SER_RTS) 3262131125Snyan bitor |= MCR_RTS; 3263131125Snyan bitand = ~bitand; 3264131125Snyan mtx_lock_spin(&sio_lock); 3265131125Snyan com->mcr_image &= bitand; 3266131125Snyan com->mcr_image |= bitor; 3267131125Snyan outb(com->modem_ctl_port, com->mcr_image); 3268131125Snyan mtx_unlock_spin(&sio_lock); 3269131125Snyan return (0); 3270131125Snyan#ifdef PC98 3271131125Snyan } 3272131125Snyan#endif 3273131125Snyan } else { 3274131125Snyan#ifdef PC98 3275131125Snyan if (IS_8251(com->pc98_if_type)) 3276131125Snyan return (com_tiocm_get(com)); 3277131125Snyan else { 3278131125Snyan#endif 3279131125Snyan bitor = 0; 3280131125Snyan if (com->mcr_image & MCR_DTR) 3281131125Snyan bitor |= SER_DTR; 3282131125Snyan if (com->mcr_image & MCR_RTS) 3283131125Snyan bitor |= SER_RTS; 328416359Sasami msr = com->prev_modem_status; 328516359Sasami if (msr & MSR_CTS) 3286131125Snyan bitor |= SER_CTS; 328716359Sasami if (msr & MSR_DCD) 3288131125Snyan bitor |= SER_DCD; 328916359Sasami if (msr & MSR_DSR) 3290131125Snyan bitor |= SER_DSR; 3291131125Snyan if (msr & MSR_DSR) 3292131125Snyan bitor |= SER_DSR; 329316359Sasami if (msr & (MSR_RI | MSR_TERI)) 3294131125Snyan bitor |= SER_RI; 3295131125Snyan return (bitor); 3296131125Snyan#ifdef PC98 3297131125Snyan } 3298131125Snyan#endif 329916359Sasami } 330016359Sasami} 330116359Sasami 330216359Sasamistatic void 330316359Sasamisiosettimeout() 330416359Sasami{ 330516359Sasami struct com_s *com; 330616359Sasami bool_t someopen; 330716359Sasami int unit; 330816359Sasami 330916359Sasami /* 331016359Sasami * Set our timeout period to 1 second if no polled devices are open. 331116359Sasami * Otherwise set it to max(1/200, 1/hz). 331216359Sasami * Enable timeouts iff some device is open. 331316359Sasami */ 331429715Skato untimeout(comwakeup, (void *)NULL, sio_timeout_handle); 331516359Sasami sio_timeout = hz; 331616359Sasami someopen = FALSE; 331753373Snyan for (unit = 0; unit < sio_numunits; ++unit) { 331816359Sasami com = com_addr(unit); 331916359Sasami if (com != NULL && com->tp != NULL 332016359Sasami && com->tp->t_state & TS_ISOPEN && !com->gone) { 332116359Sasami someopen = TRUE; 332216359Sasami if (com->poll || com->poll_output) { 332316359Sasami sio_timeout = hz > 200 ? hz / 200 : 1; 332416359Sasami break; 332516359Sasami } 332616359Sasami } 332716359Sasami } 332816359Sasami if (someopen) { 332916359Sasami sio_timeouts_until_log = hz / sio_timeout; 333029715Skato sio_timeout_handle = timeout(comwakeup, (void *)NULL, 333129715Skato sio_timeout); 333216359Sasami } else { 333316359Sasami /* Flush error messages, if any. */ 333416359Sasami sio_timeouts_until_log = 1; 333516359Sasami comwakeup((void *)NULL); 333629715Skato untimeout(comwakeup, (void *)NULL, sio_timeout_handle); 333716359Sasami } 333816359Sasami} 333916359Sasami 334016359Sasamistatic void 334116359Sasamicomwakeup(chan) 334216359Sasami void *chan; 334316359Sasami{ 334416359Sasami struct com_s *com; 334516359Sasami int unit; 334616359Sasami 334729715Skato sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); 334816359Sasami 334916359Sasami /* 335016359Sasami * Recover from lost output interrupts. 335116359Sasami * Poll any lines that don't use interrupts. 335216359Sasami */ 335353373Snyan for (unit = 0; unit < sio_numunits; ++unit) { 335416359Sasami com = com_addr(unit); 335516359Sasami if (com != NULL && !com->gone 335616359Sasami && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { 335772200Sbmilekic mtx_lock_spin(&sio_lock); 335816359Sasami siointr1(com); 335972200Sbmilekic mtx_unlock_spin(&sio_lock); 336016359Sasami } 336116359Sasami } 336216359Sasami 336316359Sasami /* 336416359Sasami * Check for and log errors, but not too often. 336516359Sasami */ 336616359Sasami if (--sio_timeouts_until_log > 0) 336716359Sasami return; 336816359Sasami sio_timeouts_until_log = hz / sio_timeout; 336953373Snyan for (unit = 0; unit < sio_numunits; ++unit) { 337016359Sasami int errnum; 337116359Sasami 337216359Sasami com = com_addr(unit); 337316359Sasami if (com == NULL) 337416359Sasami continue; 337516359Sasami if (com->gone) 337616359Sasami continue; 337716359Sasami for (errnum = 0; errnum < CE_NTYPES; ++errnum) { 337816359Sasami u_int delta; 337916359Sasami u_long total; 338016359Sasami 338172200Sbmilekic mtx_lock_spin(&sio_lock); 338216359Sasami delta = com->delta_error_counts[errnum]; 338316359Sasami com->delta_error_counts[errnum] = 0; 338472200Sbmilekic mtx_unlock_spin(&sio_lock); 338516359Sasami if (delta == 0) 338616359Sasami continue; 338716359Sasami total = com->error_counts[errnum] += delta; 338816359Sasami log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", 338916359Sasami unit, delta, error_desc[errnum], 339016359Sasami delta == 1 ? "" : "s", total); 339116359Sasami } 339216359Sasami } 339316359Sasami} 339416359Sasami 339516359Sasami#ifdef PC98 339616359Sasami/* commint is called when modem control line changes */ 339716359Sasamistatic void 3398130585Sphkcommint(struct cdev *dev) 339916359Sasami{ 340016359Sasami register struct tty *tp; 340116359Sasami int stat,delta; 340216359Sasami struct com_s *com; 340316359Sasami 3404135374Sphk com = dev->si_drv1; 340516359Sasami tp = com->tp; 340616359Sasami 340716359Sasami stat = com_tiocm_get(com); 340816359Sasami delta = com_tiocm_get_delta(com); 340916359Sasami 341016359Sasami if (com->state & CS_CTS_OFLOW) { 341116359Sasami if (stat & TIOCM_CTS) 341216359Sasami com->state |= CS_ODEVREADY; 341316359Sasami else 341416359Sasami com->state &= ~CS_ODEVREADY; 341516359Sasami } 3416136478Sphk if ((delta & TIOCM_CAR) && (ISCALLOUT(dev)) == 0) { 341716359Sasami if (stat & TIOCM_CAR ) 3418130077Sphk (void)ttyld_modem(tp, 1); 3419130077Sphk else if (ttyld_modem(tp, 0) == 0) { 342016359Sasami /* negate DTR, RTS */ 342116359Sasami com_tiocm_bic(com, (tp->t_cflag & HUPCL) ? 342216359Sasami TIOCM_DTR|TIOCM_RTS|TIOCM_LE : TIOCM_LE ); 342316359Sasami /* disable IENABLE */ 342416359Sasami com_int_TxRx_disable( com ); 342516359Sasami } 342616359Sasami } 342716359Sasami} 342816359Sasami#endif 342916359Sasami 343016359Sasami/* 343116359Sasami * Following are all routines needed for SIO to act as console 343216359Sasami */ 343316359Sasamistruct siocnstate { 343416359Sasami u_char dlbl; 343516359Sasami u_char dlbh; 343616359Sasami u_char ier; 343716359Sasami u_char cfcr; 343816359Sasami u_char mcr; 343916359Sasami}; 344016359Sasami 3441120491Sphk/* 3442120491Sphk * This is a function in order to not replicate "ttyd%d" more 3443120491Sphk * places than absolutely necessary. 3444120491Sphk */ 3445120491Sphkstatic void 3446120491Sphksiocnset(struct consdev *cd, int unit) 3447120491Sphk{ 3448120491Sphk 3449120491Sphk cd->cn_unit = unit; 3450120491Sphk sprintf(cd->cn_name, "ttyd%d", unit); 3451120491Sphk} 3452120491Sphk 345392793Skatostatic speed_t siocngetspeed(Port_t, u_long rclk); 345492793Skatostatic void siocnclose(struct siocnstate *sp, Port_t iobase); 345592793Skatostatic void siocnopen(struct siocnstate *sp, Port_t iobase, int speed); 345692793Skatostatic void siocntxwait(Port_t iobase); 345716359Sasami 3458158957Sphkstatic cn_probe_t sio_cnprobe; 3459158957Sphkstatic cn_init_t sio_cninit; 3460158957Sphkstatic cn_term_t sio_cnterm; 3461158957Sphkstatic cn_getc_t sio_cngetc; 3462158957Sphkstatic cn_putc_t sio_cnputc; 3463228631Savgstatic cn_grab_t sio_cngrab; 3464228631Savgstatic cn_ungrab_t sio_cnungrab; 346542405Skato 3466158957SphkCONSOLE_DRIVER(sio); 346751202Snyan 346816359Sasamistatic void 346945783Skatosiocntxwait(iobase) 347045783Skato Port_t iobase; 347116359Sasami{ 347216359Sasami int timo; 347316359Sasami 347416359Sasami /* 347516359Sasami * Wait for any pending transmission to finish. Required to avoid 347616359Sasami * the UART lockup bug when the speed is changed, and for normal 347716359Sasami * transmits. 347816359Sasami */ 347916359Sasami timo = 100000; 348045783Skato while ((inb(iobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) 348116359Sasami != (LSR_TSRE | LSR_TXRDY) && --timo != 0) 348216359Sasami ; 348316359Sasami} 348416359Sasami 348526439Skato/* 348626439Skato * Read the serial port specified and try to figure out what speed 348726439Skato * it's currently running at. We're assuming the serial port has 348826439Skato * been initialized and is basicly idle. This routine is only intended 348926439Skato * to be run at system startup. 349026439Skato * 349126439Skato * If the value read from the serial port doesn't make sense, return 0. 349226439Skato */ 349326439Skato 349426439Skatostatic speed_t 349590011Snyansiocngetspeed(iobase, rclk) 349690011Snyan Port_t iobase; 349790011Snyan u_long rclk; 349826439Skato{ 349990011Snyan u_int divisor; 350026439Skato u_char dlbh; 350126439Skato u_char dlbl; 350226439Skato u_char cfcr; 350326439Skato 350426439Skato cfcr = inb(iobase + com_cfcr); 350526439Skato outb(iobase + com_cfcr, CFCR_DLAB | cfcr); 350626439Skato 350726439Skato dlbl = inb(iobase + com_dlbl); 350826439Skato dlbh = inb(iobase + com_dlbh); 350926439Skato 351026439Skato outb(iobase + com_cfcr, cfcr); 351126439Skato 351290011Snyan divisor = dlbh << 8 | dlbl; 351326439Skato 351490011Snyan /* XXX there should be more sanity checking. */ 351590011Snyan if (divisor == 0) 351690011Snyan return (CONSPEED); 351790011Snyan return (rclk / (16UL * divisor)); 351826439Skato} 351926439Skato 352016359Sasamistatic void 352145783Skatosiocnopen(sp, iobase, speed) 352216359Sasami struct siocnstate *sp; 352345783Skato Port_t iobase; 352445783Skato int speed; 352516359Sasami{ 352690011Snyan u_int divisor; 352718846Sasami u_char dlbh; 352818846Sasami u_char dlbl; 352916359Sasami 353016359Sasami /* 353116359Sasami * Save all the device control registers except the fifo register 353220129Sasami * and set our default ones (cs8 -parenb speed=comdefaultrate). 353316359Sasami * We can't save the fifo register since it is read-only. 353416359Sasami */ 353516359Sasami sp->ier = inb(iobase + com_ier); 353616359Sasami outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ 353745783Skato siocntxwait(iobase); 353816359Sasami sp->cfcr = inb(iobase + com_cfcr); 353918846Sasami outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); 354016359Sasami sp->dlbl = inb(iobase + com_dlbl); 354116359Sasami sp->dlbh = inb(iobase + com_dlbh); 354218846Sasami /* 354318846Sasami * Only set the divisor registers if they would change, since on 354418846Sasami * some 16550 incompatibles (Startech), setting them clears the 354518846Sasami * data input register. This also reduces the effects of the 354618846Sasami * UMC8669F bug. 354718846Sasami */ 354890011Snyan divisor = siodivisor(comdefaultrclk, speed); 354918846Sasami dlbl = divisor & 0xFF; 355018846Sasami if (sp->dlbl != dlbl) 355118846Sasami outb(iobase + com_dlbl, dlbl); 355290011Snyan dlbh = divisor >> 8; 355318846Sasami if (sp->dlbh != dlbh) 355418846Sasami outb(iobase + com_dlbh, dlbh); 355516359Sasami outb(iobase + com_cfcr, CFCR_8BITS); 355616359Sasami sp->mcr = inb(iobase + com_mcr); 355716359Sasami /* 355816359Sasami * We don't want interrupts, but must be careful not to "disable" 355916359Sasami * them by clearing the MCR_IENABLE bit, since that might cause 356016359Sasami * an interrupt by floating the IRQ line. 356116359Sasami */ 356216359Sasami outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); 356316359Sasami} 356416359Sasami 356516359Sasamistatic void 356645783Skatosiocnclose(sp, iobase) 356716359Sasami struct siocnstate *sp; 356845783Skato Port_t iobase; 356916359Sasami{ 357016359Sasami /* 357116359Sasami * Restore the device control registers. 357216359Sasami */ 357345783Skato siocntxwait(iobase); 357418846Sasami outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); 357518846Sasami if (sp->dlbl != inb(iobase + com_dlbl)) 357618846Sasami outb(iobase + com_dlbl, sp->dlbl); 357718846Sasami if (sp->dlbh != inb(iobase + com_dlbh)) 357818846Sasami outb(iobase + com_dlbh, sp->dlbh); 357916359Sasami outb(iobase + com_cfcr, sp->cfcr); 358016359Sasami /* 358116359Sasami * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. 358216359Sasami */ 358316359Sasami outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); 358416359Sasami outb(iobase + com_ier, sp->ier); 358516359Sasami} 358616359Sasami 358751202Snyanstatic void 3588158957Sphksio_cnprobe(cp) 358916359Sasami struct consdev *cp; 359016359Sasami{ 359132089Skato speed_t boot_speed; 359232089Skato u_char cfcr; 359390011Snyan u_int divisor; 3594129001Snyan int s, unit; 359524655Skato struct siocnstate sp; 359616359Sasami 359724655Skato /* 359824655Skato * Find our first enabled console, if any. If it is a high-level 359924655Skato * console device, then initialize it and return successfully. 360024655Skato * If it is a low-level console device, then initialize it and 360124655Skato * return unsuccessfully. It must be initialized in both cases 360224655Skato * for early use by console drivers and debuggers. Initializing 360324655Skato * the hardware is not necessary in all cases, since the i/o 360424655Skato * routines initialize it on the fly, but it is necessary if 360524655Skato * input might arrive while the hardware is switched back to an 360624655Skato * uninitialized state. We can't handle multiple console devices 360724655Skato * yet because our low-level routines don't take a device arg. 360824655Skato * We trust the user to set the console flags properly so that we 360924655Skato * don't need to probe. 361024655Skato */ 361124655Skato cp->cn_pri = CN_DEAD; 361245783Skato 3613129001Snyan for (unit = 0; unit < 16; unit++) { /* XXX need to know how many */ 3614129001Snyan int flags; 3615120809Snyan 3616129001Snyan if (resource_disabled("sio", unit)) 3617129001Snyan continue; 3618129001Snyan if (resource_int_value("sio", unit, "flags", &flags)) 3619129001Snyan continue; 3620129001Snyan if (COM_CONSOLE(flags) || COM_DEBUGGER(flags)) { 3621129001Snyan int port; 3622129001Snyan Port_t iobase; 3623129001Snyan 3624129001Snyan if (resource_int_value("sio", unit, "port", &port)) 3625128795Snyan continue; 3626129001Snyan iobase = port; 3627129001Snyan s = spltty(); 3628176657Snyan if ((boothowto & RB_SERIAL) && COM_CONSOLE(flags)) { 3629129001Snyan boot_speed = 3630129001Snyan siocngetspeed(iobase, comdefaultrclk); 3631129001Snyan if (boot_speed) 3632129001Snyan comdefaultrate = boot_speed; 363326439Skato } 363432089Skato 363532089Skato /* 363632089Skato * Initialize the divisor latch. We can't rely on 363732089Skato * siocnopen() to do this the first time, since it 363832089Skato * avoids writing to the latch if the latch appears 363932089Skato * to have the correct value. Also, if we didn't 364032089Skato * just read the speed from the hardware, then we 364132089Skato * need to set the speed in hardware so that 364232089Skato * switching it later is null. 364332089Skato */ 3644129001Snyan cfcr = inb(iobase + com_cfcr); 3645129001Snyan outb(iobase + com_cfcr, CFCR_DLAB | cfcr); 3646129001Snyan divisor = siodivisor(comdefaultrclk, comdefaultrate); 3647129001Snyan outb(iobase + com_dlbl, divisor & 0xff); 3648129001Snyan outb(iobase + com_dlbh, divisor >> 8); 3649129001Snyan outb(iobase + com_cfcr, cfcr); 365032089Skato 3651129001Snyan siocnopen(&sp, iobase, comdefaultrate); 365246766Skato 3653129001Snyan splx(s); 3654129001Snyan if (COM_CONSOLE(flags) && !COM_LLCONSOLE(flags)) { 3655129001Snyan siocnset(cp, unit); 3656129001Snyan cp->cn_pri = COM_FORCECONSOLE(flags) 3657129001Snyan || boothowto & RB_SERIAL 3658129001Snyan ? CN_REMOTE : CN_NORMAL; 3659129001Snyan siocniobase = iobase; 3660129001Snyan siocnunit = unit; 3661129001Snyan } 3662131939Smarcel#ifdef GDB 3663131939Smarcel if (COM_DEBUGGER(flags)) 3664129001Snyan siogdbiobase = iobase; 366546766Skato#endif 366624655Skato } 366745783Skato } 366816359Sasami} 366916359Sasami 367066250Skatostatic void 3671158957Sphksio_cninit(cp) 367266250Skato struct consdev *cp; 367366250Skato{ 3674120491Sphk comconsole = cp->cn_unit; 367566250Skato} 367666250Skato 367786912Snyanstatic void 3678158957Sphksio_cnterm(cp) 367986912Snyan struct consdev *cp; 368086912Snyan{ 368186912Snyan comconsole = -1; 368286912Snyan} 368386912Snyan 3684228631Savgstatic void 3685228631Savgsio_cngrab(struct consdev *cp) 3686228631Savg{ 3687228631Savg} 3688228631Savg 3689228631Savgstatic void 3690228631Savgsio_cnungrab(struct consdev *cp) 3691228631Savg{ 3692228631Savg} 3693228631Savg 369451202Snyanstatic int 3695158957Sphksio_cngetc(struct consdev *cd) 369616359Sasami{ 369716359Sasami int c; 369816359Sasami Port_t iobase; 369916359Sasami int s; 370016359Sasami struct siocnstate sp; 370198401Sn_hibma speed_t speed; 370216359Sasami 3703131939Smarcel if (cd != NULL && cd->cn_unit == siocnunit) { 370498401Sn_hibma iobase = siocniobase; 370598401Sn_hibma speed = comdefaultrate; 370698401Sn_hibma } else { 3707131939Smarcel#ifdef GDB 370846766Skato iobase = siogdbiobase; 370998401Sn_hibma speed = gdbdefaultrate; 3710131939Smarcel#else 3711131939Smarcel return (-1); 3712131939Smarcel#endif 371398401Sn_hibma } 371416359Sasami s = spltty(); 371598401Sn_hibma siocnopen(&sp, iobase, speed); 371616359Sasami if (inb(iobase + com_lsr) & LSR_RXRDY) 371716359Sasami c = inb(iobase + com_data); 371816359Sasami else 371918846Sasami c = -1; 372045783Skato siocnclose(&sp, iobase); 372116359Sasami splx(s); 372216359Sasami return (c); 372316359Sasami} 372416359Sasami 3725104134Snyanstatic void 3726158957Sphksio_cnputc(struct consdev *cd, int c) 372716359Sasami{ 372888955Snyan int need_unlock; 372916359Sasami int s; 373016359Sasami struct siocnstate sp; 373146766Skato Port_t iobase; 373298431Snyan speed_t speed; 373316359Sasami 3734131939Smarcel if (cd != NULL && cd->cn_unit == siocnunit) { 373598401Sn_hibma iobase = siocniobase; 373698401Sn_hibma speed = comdefaultrate; 373798401Sn_hibma } else { 3738131939Smarcel#ifdef GDB 373946766Skato iobase = siogdbiobase; 374098401Sn_hibma speed = gdbdefaultrate; 3741131939Smarcel#else 3742131939Smarcel return; 3743131939Smarcel#endif 374498401Sn_hibma } 374516359Sasami s = spltty(); 374688955Snyan need_unlock = 0; 3747141678Snyan if (!kdb_active && sio_inited == 2 && !mtx_owned(&sio_lock)) { 3748141678Snyan mtx_lock_spin(&sio_lock); 3749141678Snyan need_unlock = 1; 375088955Snyan } 375198401Sn_hibma siocnopen(&sp, iobase, speed); 375246766Skato siocntxwait(iobase); 375346766Skato outb(iobase + com_data, c); 375446766Skato siocnclose(&sp, iobase); 3755141678Snyan if (need_unlock) 3756141678Snyan mtx_unlock_spin(&sio_lock); 375716359Sasami splx(s); 375816359Sasami} 375916359Sasami 3760131939Smarcel/* 3761131939Smarcel * Remote gdb(1) support. 3762131939Smarcel */ 3763131939Smarcel 3764131939Smarcel#if defined(GDB) 3765131939Smarcel 3766131939Smarcel#include <gdb/gdb.h> 3767131939Smarcel 3768131939Smarcelstatic gdb_probe_f siogdbprobe; 3769131939Smarcelstatic gdb_init_f siogdbinit; 3770131939Smarcelstatic gdb_term_f siogdbterm; 3771131939Smarcelstatic gdb_getc_f siogdbgetc; 3772131939Smarcelstatic gdb_putc_f siogdbputc; 3773131939Smarcel 3774158957SphkGDB_DBGPORT(sio, siogdbprobe, siogdbinit, siogdbterm, siogdbgetc, siogdbputc); 3775131939Smarcel 3776131939Smarcelstatic int 3777131939Smarcelsiogdbprobe(void) 377845783Skato{ 3779131939Smarcel return ((siogdbiobase != 0) ? 0 : -1); 3780131939Smarcel} 378116359Sasami 3782131939Smarcelstatic void 3783131939Smarcelsiogdbinit(void) 3784131939Smarcel{ 3785131939Smarcel} 378698431Snyan 3787131939Smarcelstatic void 3788131939Smarcelsiogdbterm(void) 3789131939Smarcel{ 379045783Skato} 379145783Skato 3792131939Smarcelstatic void 3793131939Smarcelsiogdbputc(int c) 379445783Skato{ 3795158969Snyan sio_cnputc(NULL, c); 3796131939Smarcel} 379745783Skato 3798131939Smarcelstatic int 3799131939Smarcelsiogdbgetc(void) 3800131939Smarcel{ 3801158969Snyan return (sio_cngetc(NULL)); 380245783Skato} 3803131939Smarcel 380445816Skato#endif 380545783Skato 380616359Sasami#ifdef PC98 380716359Sasami/* 380816359Sasami * pc98 local function 380916359Sasami */ 381016359Sasamistatic void 381116359Sasamicom_tiocm_bis(struct com_s *com, int msr) 381216359Sasami{ 381316359Sasami int s; 381416359Sasami int tmp = 0; 381516359Sasami 381616359Sasami s=spltty(); 381716359Sasami com->pc98_prev_modem_status |= ( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); 381816359Sasami tmp |= CMD8251_TxEN|CMD8251_RxEN; 381916359Sasami if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; 382016359Sasami if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; 382116359Sasami 382216359Sasami pc98_i8251_or_cmd( com, tmp ); 382316359Sasami splx(s); 382416359Sasami} 382516359Sasami 382616359Sasamistatic void 382716359Sasamicom_tiocm_bic(struct com_s *com, int msr) 382816359Sasami{ 382916359Sasami int s; 383016359Sasami int tmp = msr; 383116359Sasami 383216359Sasami s=spltty(); 383316359Sasami com->pc98_prev_modem_status &= ~( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) ); 383416359Sasami if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR; 383516359Sasami if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS; 383616359Sasami 383716359Sasami pc98_i8251_clear_cmd( com, tmp ); 383816359Sasami splx(s); 383916359Sasami} 384016359Sasami 384116359Sasamistatic int 384216359Sasamicom_tiocm_get(struct com_s *com) 384316359Sasami{ 384416359Sasami return( com->pc98_prev_modem_status ); 384516359Sasami} 384616359Sasami 384716359Sasamistatic int 384816359Sasamicom_tiocm_get_delta(struct com_s *com) 384916359Sasami{ 385016359Sasami int tmp; 385116359Sasami 385216359Sasami tmp = com->pc98_modem_delta; 385316359Sasami com->pc98_modem_delta = 0; 385416359Sasami return( tmp ); 385516359Sasami} 385616359Sasami 385716359Sasami/* convert to TIOCM_?? ( ioctl.h ) */ 385816359Sasamistatic int 385916359Sasamipc98_get_modem_status(struct com_s *com) 386016359Sasami{ 386116359Sasami register int msr; 386216359Sasami 386316359Sasami msr = com->pc98_prev_modem_status 386416359Sasami & ~(TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS); 386554174Snyan if (com->pc98_8251fifo_enable) { 386654174Snyan int stat2; 386754174Snyan 386854174Snyan stat2 = inb(I8251F_msr); 3869182835Snyan if ( stat2 & MSR_DCD ) msr |= TIOCM_CAR; 3870182835Snyan if ( stat2 & MSR_RI ) msr |= TIOCM_RI; 3871182835Snyan if ( stat2 & MSR_DSR ) msr |= TIOCM_DSR; 3872182835Snyan if ( stat2 & MSR_CTS ) msr |= TIOCM_CTS; 387316359Sasami#if COM_CARRIER_DETECT_EMULATE 387454174Snyan if ( msr & (TIOCM_DSR|TIOCM_CTS) ) { 387554174Snyan msr |= TIOCM_CAR; 387654174Snyan } 387754174Snyan#endif 387854174Snyan } else { 387954174Snyan int stat, stat2; 388054174Snyan 388154174Snyan stat = inb(com->sts_port); 388254174Snyan stat2 = inb(com->in_modem_port); 388354174Snyan if ( !(stat2 & CICSCD_CD) ) msr |= TIOCM_CAR; 388454174Snyan if ( !(stat2 & CICSCD_CI) ) msr |= TIOCM_RI; 388554174Snyan if ( stat & STS8251_DSR ) msr |= TIOCM_DSR; 388654174Snyan if ( !(stat2 & CICSCD_CS) ) msr |= TIOCM_CTS; 388754174Snyan#if COM_CARRIER_DETECT_EMULATE 388854174Snyan if ( msr & (TIOCM_DSR|TIOCM_CTS) ) { 388954174Snyan msr |= TIOCM_CAR; 389054174Snyan } 389154174Snyan#endif 389216359Sasami } 389316359Sasami return(msr); 389416359Sasami} 389516359Sasami 389616359Sasamistatic void 389716359Sasamipc98_check_msr(void* chan) 389816359Sasami{ 389916359Sasami int msr, delta; 390016359Sasami int s; 390116359Sasami register struct tty *tp; 390216359Sasami struct com_s *com; 3903130585Sphk struct cdev *dev; 390416359Sasami 3905130585Sphk dev=(struct cdev *)chan; 3906135374Sphk com = dev->si_drv1; 3907135374Sphk tp = dev->si_tty; 390816359Sasami 390916359Sasami s = spltty(); 391016359Sasami msr = pc98_get_modem_status(com); 391116359Sasami /* make change flag */ 391216359Sasami delta = msr ^ com->pc98_prev_modem_status; 391316359Sasami if ( delta & TIOCM_CAR ) { 391416359Sasami if ( com->modem_car_chg_timer ) { 391516359Sasami if ( -- com->modem_car_chg_timer ) 391616359Sasami msr ^= TIOCM_CAR; 391716359Sasami } else { 391843539Skato if ((com->modem_car_chg_timer = (msr & TIOCM_CAR) ? 391943539Skato DCD_ON_RECOGNITION : DCD_OFF_TOLERANCE) != 0) 392016359Sasami msr ^= TIOCM_CAR; 392116359Sasami } 392216359Sasami } else 392316359Sasami com->modem_car_chg_timer = 0; 392416359Sasami delta = ( msr ^ com->pc98_prev_modem_status ) & 392516359Sasami (TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS); 392616359Sasami com->pc98_prev_modem_status = msr; 392716359Sasami delta = ( com->pc98_modem_delta |= delta ); 392816359Sasami splx(s); 392916359Sasami if ( com->modem_checking || (tp->t_state & (TS_ISOPEN)) ) { 393016359Sasami if ( delta ) { 393116359Sasami commint(dev); 393216359Sasami } 393316359Sasami timeout(pc98_check_msr, (caddr_t)dev, 393416359Sasami PC98_CHECK_MODEM_INTERVAL); 393516359Sasami } else { 393616359Sasami com->modem_checking = 0; 393716359Sasami } 393816359Sasami} 393916359Sasami 394016359Sasamistatic void 3941130585Sphkpc98_msrint_start(struct cdev *dev) 394216359Sasami{ 394316359Sasami struct com_s *com; 394416359Sasami int s = spltty(); 394516359Sasami 3946135374Sphk com = dev->si_drv1; 394716359Sasami /* modem control line check routine envoke interval is 1/10 sec */ 394816359Sasami if ( com->modem_checking == 0 ) { 394916359Sasami com->pc98_prev_modem_status = pc98_get_modem_status(com); 395016359Sasami com->pc98_modem_delta = 0; 395116359Sasami timeout(pc98_check_msr, (caddr_t)dev, 395216359Sasami PC98_CHECK_MODEM_INTERVAL); 395316359Sasami com->modem_checking = 1; 395416359Sasami } 395516359Sasami splx(s); 395616359Sasami} 395716359Sasami 395816359Sasamistatic void 395916359Sasamipc98_disable_i8251_interrupt(struct com_s *com, int mod) 396016359Sasami{ 396116359Sasami /* disable interrupt */ 396216359Sasami register int tmp; 396316359Sasami 396416359Sasami mod |= ~(IEN_Tx|IEN_TxEMP|IEN_Rx); 396516359Sasami COM_INT_DISABLE 396616359Sasami tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx); 396716359Sasami outb( com->intr_ctrl_port, (com->intr_enable&=~mod) | tmp ); 396816359Sasami COM_INT_ENABLE 396916359Sasami} 397016359Sasami 397116359Sasamistatic void 397216359Sasamipc98_enable_i8251_interrupt(struct com_s *com, int mod) 397316359Sasami{ 397416359Sasami register int tmp; 397516359Sasami 397616359Sasami COM_INT_DISABLE 397716359Sasami tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx); 397816359Sasami outb( com->intr_ctrl_port, (com->intr_enable|=mod) | tmp ); 397916359Sasami COM_INT_ENABLE 398016359Sasami} 398116359Sasami 398216359Sasamistatic int 398316359Sasamipc98_check_i8251_interrupt(struct com_s *com) 398416359Sasami{ 398516359Sasami return ( com->intr_enable & 0x07 ); 398616359Sasami} 398716359Sasami 398816359Sasamistatic void 398916359Sasamipc98_i8251_clear_cmd(struct com_s *com, int x) 399016359Sasami{ 399116359Sasami int tmp; 399216359Sasami 399316359Sasami COM_INT_DISABLE 399416359Sasami tmp = com->pc98_prev_siocmd & ~(x); 399554174Snyan if (com->pc98_8251fifo_enable) 399654174Snyan outb(I8251F_fcr, 0); 399716359Sasami outb(com->cmd_port, tmp); 399816359Sasami com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); 399954174Snyan if (com->pc98_8251fifo_enable) 4000182835Snyan outb(I8251F_fcr, FIFO_ENABLE); 400116359Sasami COM_INT_ENABLE 400216359Sasami} 400316359Sasami 400416359Sasamistatic void 400516359Sasamipc98_i8251_or_cmd(struct com_s *com, int x) 400616359Sasami{ 400716359Sasami int tmp; 400816359Sasami 400916359Sasami COM_INT_DISABLE 401054174Snyan if (com->pc98_8251fifo_enable) 401154174Snyan outb(I8251F_fcr, 0); 401216359Sasami tmp = com->pc98_prev_siocmd | (x); 401316359Sasami outb(com->cmd_port, tmp); 401416359Sasami com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); 401554174Snyan if (com->pc98_8251fifo_enable) 4016182835Snyan outb(I8251F_fcr, FIFO_ENABLE); 401716359Sasami COM_INT_ENABLE 401816359Sasami} 401916359Sasami 402016359Sasamistatic void 402116359Sasamipc98_i8251_set_cmd(struct com_s *com, int x) 402216359Sasami{ 402316359Sasami int tmp; 402416359Sasami 402516359Sasami COM_INT_DISABLE 402654174Snyan if (com->pc98_8251fifo_enable) 402754174Snyan outb(I8251F_fcr, 0); 402816359Sasami tmp = (x); 402916359Sasami outb(com->cmd_port, tmp); 403016359Sasami com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); 403154174Snyan if (com->pc98_8251fifo_enable) 4032182835Snyan outb(I8251F_fcr, FIFO_ENABLE); 403316359Sasami COM_INT_ENABLE 403416359Sasami} 403516359Sasami 403616359Sasamistatic void 403716359Sasamipc98_i8251_clear_or_cmd(struct com_s *com, int clr, int x) 403816359Sasami{ 403916359Sasami int tmp; 404016359Sasami COM_INT_DISABLE 404154174Snyan if (com->pc98_8251fifo_enable) 404254174Snyan outb(I8251F_fcr, 0); 404316359Sasami tmp = com->pc98_prev_siocmd & ~(clr); 404416359Sasami tmp |= (x); 404516359Sasami outb(com->cmd_port, tmp); 404616359Sasami com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH); 404754174Snyan if (com->pc98_8251fifo_enable) 4048182835Snyan outb(I8251F_fcr, FIFO_ENABLE); 404916359Sasami COM_INT_ENABLE 405016359Sasami} 405116359Sasami 405216359Sasamistatic int 405316359Sasamipc98_i8251_get_cmd(struct com_s *com) 405416359Sasami{ 405516359Sasami return com->pc98_prev_siocmd; 405616359Sasami} 405716359Sasami 405816359Sasamistatic int 405916359Sasamipc98_i8251_get_mod(struct com_s *com) 406016359Sasami{ 406116359Sasami return com->pc98_prev_siomod; 406216359Sasami} 406316359Sasami 406416359Sasamistatic void 406516359Sasamipc98_i8251_reset(struct com_s *com, int mode, int command) 406616359Sasami{ 406754174Snyan if (com->pc98_8251fifo_enable) 406854174Snyan outb(I8251F_fcr, 0); 406916359Sasami outb(com->cmd_port, 0); /* dummy */ 407016359Sasami DELAY(2); 407116359Sasami outb(com->cmd_port, 0); /* dummy */ 407216359Sasami DELAY(2); 407316359Sasami outb(com->cmd_port, 0); /* dummy */ 407416359Sasami DELAY(2); 407516359Sasami outb(com->cmd_port, CMD8251_RESET); /* internal reset */ 407616359Sasami DELAY(2); 407716359Sasami outb(com->cmd_port, mode ); /* mode register */ 407816359Sasami com->pc98_prev_siomod = mode; 407916359Sasami DELAY(2); 408016359Sasami pc98_i8251_set_cmd( com, (command|CMD8251_ER) ); 408154174Snyan DELAY(10); 408254174Snyan if (com->pc98_8251fifo_enable) 4083182835Snyan outb(I8251F_fcr, FIFO_ENABLE | FIFO_XMT_RST | FIFO_RCV_RST); 408416359Sasami} 408516359Sasami 408616359Sasamistatic void 408716359Sasamipc98_check_sysclock(void) 408816359Sasami{ 408916359Sasami /* get system clock from port */ 409016359Sasami if ( pc98_machine_type & M_8M ) { 409116359Sasami /* 8 MHz system & H98 */ 409216359Sasami sysclock = 8; 409316359Sasami } else { 409416359Sasami /* 5 MHz system */ 409516359Sasami sysclock = 5; 409616359Sasami } 409716359Sasami} 409816359Sasami 409916359Sasamistatic void 410016359Sasamicom_cflag_and_speed_set( struct com_s *com, int cflag, int speed) 410116359Sasami{ 410291865Snyan int cfcr=0; 410317256Sasami int previnterrupt; 4104150127Snyan int tmp; 410591865Snyan u_int count; 410616359Sasami 410791865Snyan if (pc98_ttspeedtab(com, speed, &count) != 0) 410891865Snyan return; 410916359Sasami 411016359Sasami previnterrupt = pc98_check_i8251_interrupt(com); 411116359Sasami pc98_disable_i8251_interrupt( com, IEN_Tx|IEN_TxEMP|IEN_Rx ); 411216359Sasami 411316359Sasami switch ( cflag&CSIZE ) { 411416359Sasami case CS5: 411516359Sasami cfcr = MOD8251_5BITS; break; 411616359Sasami case CS6: 411716359Sasami cfcr = MOD8251_6BITS; break; 411816359Sasami case CS7: 411916359Sasami cfcr = MOD8251_7BITS; break; 412016359Sasami case CS8: 412116359Sasami cfcr = MOD8251_8BITS; break; 412216359Sasami } 412316359Sasami if ( cflag&PARENB ) { 412416359Sasami if ( cflag&PARODD ) 4125182835Snyan cfcr |= MOD8251_PENAB; 412616359Sasami else 4127182835Snyan cfcr |= MOD8251_PENAB | MOD8251_PEVEN; 4128182835Snyan } 412916359Sasami 413016359Sasami if ( cflag&CSTOPB ) 413116359Sasami cfcr |= MOD8251_STOP2; 413216359Sasami else 413316359Sasami cfcr |= MOD8251_STOP1; 413416359Sasami 413516359Sasami if ( count & 0x10000 ) 4136182835Snyan cfcr |= MOD8251_CLKx1; 413716359Sasami else 4138182835Snyan cfcr |= MOD8251_CLKx16; 413916359Sasami 4140150127Snyan while (!((tmp = inb(com->sts_port)) & STS8251_TxEMP)) 4141150127Snyan ; 4142150127Snyan 414316359Sasami /* set baud rate from ospeed */ 414416359Sasami pc98_set_baud_rate( com, count ); 414516359Sasami 414616359Sasami if ( cfcr != pc98_i8251_get_mod(com) ) 414716359Sasami pc98_i8251_reset(com, cfcr, pc98_i8251_get_cmd(com) ); 414816359Sasami 414916359Sasami pc98_enable_i8251_interrupt( com, previnterrupt ); 415016359Sasami} 415116359Sasami 415216359Sasamistatic int 415391865Snyanpc98_ttspeedtab(struct com_s *com, int speed, u_int *divisor) 415416359Sasami{ 415542262Skato int if_type, effect_sp, count = -1, mod; 415616359Sasami 415742262Skato if_type = com->pc98_if_type & 0x0f; 415842262Skato 415942262Skato switch (com->pc98_if_type) { 416042262Skato case COM_IF_INTERNAL: 416142262Skato if (PC98SIO_baud_rate_port(if_type) != -1) { 416242262Skato count = ttspeedtab(speed, if_8251_type[if_type].speedtab); 416342262Skato if (count > 0) { 416442262Skato count |= COM1_EXT_CLOCK; 416542262Skato break; 416642262Skato } 416742262Skato } 416842262Skato 416942262Skato /* for *1CLK asynchronous! mode, TEFUTEFU */ 417042262Skato mod = (sysclock == 5) ? 2457600 : 1996800; 417142262Skato effect_sp = ttspeedtab( speed, pc98speedtab ); 417242262Skato if ( effect_sp < 0 ) /* XXX */ 417342262Skato effect_sp = ttspeedtab( (speed - 1), pc98speedtab ); 417442262Skato if ( effect_sp <= 0 ) 417542262Skato return effect_sp; 417642262Skato if ( effect_sp == speed ) 417742262Skato mod /= 16; 417842262Skato if ( mod % effect_sp ) 417942262Skato return(-1); 418042262Skato count = mod / effect_sp; 418142262Skato if ( count > 65535 ) 418242262Skato return(-1); 418342262Skato if ( effect_sp != speed ) 418442262Skato count |= 0x10000; 418542262Skato break; 418642262Skato case COM_IF_PC9861K_1: 418742262Skato case COM_IF_PC9861K_2: 418842262Skato count = 1; 418942262Skato break; 419042262Skato case COM_IF_IND_SS_1: 419142262Skato case COM_IF_IND_SS_2: 419242262Skato case COM_IF_PIO9032B_1: 419342262Skato case COM_IF_PIO9032B_2: 419442262Skato count = ttspeedtab( speed, if_8251_type[if_type].speedtab ); 419542262Skato break; 419642262Skato case COM_IF_B98_01_1: 419742262Skato case COM_IF_B98_01_2: 419842262Skato count = ttspeedtab( speed, if_8251_type[if_type].speedtab ); 419942262Skato#ifdef B98_01_OLD 420042262Skato if (count == 0 || count == 1) { 420142262Skato count += 4; 420242262Skato count |= 0x20000; /* x1 mode for 76800 and 153600 */ 420342262Skato } 420416359Sasami#endif 420542262Skato break; 420616359Sasami } 420742262Skato 420891865Snyan if (count < 0) 420991865Snyan return count; 421091865Snyan 421191865Snyan *divisor = (u_int) count; 421291865Snyan return 0; 421316359Sasami} 421416359Sasami 421516359Sasamistatic void 421691865Snyanpc98_set_baud_rate( struct com_s *com, u_int count ) 421716359Sasami{ 421842262Skato int if_type, io, s; 421916359Sasami 422042262Skato if_type = com->pc98_if_type & 0x0f; 422160472Snyan io = rman_get_start(com->ioportres) & 0xff00; 422242262Skato 422342262Skato switch (com->pc98_if_type) { 422442262Skato case COM_IF_INTERNAL: 422542262Skato if (PC98SIO_baud_rate_port(if_type) != -1) { 422642262Skato if (count & COM1_EXT_CLOCK) { 422742262Skato outb((Port_t)PC98SIO_baud_rate_port(if_type), count & 0xff); 422842262Skato break; 422942262Skato } else { 423042262Skato outb((Port_t)PC98SIO_baud_rate_port(if_type), 0x09); 423142262Skato } 423242262Skato } 423342262Skato 423491865Snyan if (count == 0) 423542262Skato return; 423691865Snyan 423742262Skato /* set i8253 */ 423842262Skato s = splclock(); 423942262Skato if (count != 3) 424016359Sasami outb( 0x77, 0xb6 ); 424142262Skato else 424242262Skato outb( 0x77, 0xb4 ); 424342262Skato outb( 0x5f, 0); 424442262Skato outb( 0x75, count & 0xff ); 424542262Skato outb( 0x5f, 0); 424642262Skato outb( 0x75, (count >> 8) & 0xff ); 424742262Skato splx(s); 424842262Skato break; 424942262Skato case COM_IF_IND_SS_1: 425042262Skato case COM_IF_IND_SS_2: 425142262Skato outb(io | PC98SIO_intr_ctrl_port(if_type), 0); 425242262Skato outb(io | PC98SIO_baud_rate_port(if_type), 0); 425342262Skato outb(io | PC98SIO_baud_rate_port(if_type), 0xc0); 425442262Skato outb(io | PC98SIO_baud_rate_port(if_type), (count >> 8) | 0x80); 425542262Skato outb(io | PC98SIO_baud_rate_port(if_type), count & 0xff); 425642262Skato break; 425742262Skato case COM_IF_PIO9032B_1: 425842262Skato case COM_IF_PIO9032B_2: 425942262Skato outb(io | PC98SIO_baud_rate_port(if_type), count); 426042262Skato break; 426142262Skato case COM_IF_B98_01_1: 426242262Skato case COM_IF_B98_01_2: 426342262Skato outb(io | PC98SIO_baud_rate_port(if_type), count & 0x0f); 426416359Sasami#ifdef B98_01_OLD 426542262Skato /* 426642262Skato * Some old B98_01 board should be controlled 426742262Skato * in different way, but this hasn't been tested yet. 426842262Skato */ 426942262Skato outb(io | PC98SIO_func_port(if_type), 427042262Skato (count & 0x20000) ? 0xf0 : 0xf2); 427116359Sasami#endif 427242262Skato break; 427316359Sasami } 427416359Sasami} 427516359Sasamistatic int 427645783Skatopc98_check_if_type(device_t dev, struct siodev *iod) 427716359Sasami{ 427842262Skato int irr, io, if_type, tmp; 427916359Sasami static short irq_tab[2][8] = { 428016359Sasami { 3, 5, 6, 9, 10, 12, 13, -1}, 428116359Sasami { 3, 10, 12, 13, 5, 6, 9, -1} 428216359Sasami }; 428342262Skato 428460472Snyan if_type = iod->if_type & 0x0f; 428516359Sasami iod->irq = 0; 428645783Skato io = isa_get_port(dev) & 0xff00; 428742262Skato 428842262Skato if (IS_8251(iod->if_type)) { 428942262Skato if (PC98SIO_func_port(if_type) != -1) { 429042262Skato outb(io | PC98SIO_func_port(if_type), 0xf2); 429142262Skato tmp = ttspeedtab(9600, if_8251_type[if_type].speedtab); 429242262Skato if (tmp != -1 && PC98SIO_baud_rate_port(if_type) != -1) 429342262Skato outb(io | PC98SIO_baud_rate_port(if_type), tmp); 429442262Skato } 429542262Skato 429642262Skato iod->cmd = io | PC98SIO_cmd_port(if_type); 429742262Skato iod->sts = io | PC98SIO_sts_port(if_type); 429842262Skato iod->mod = io | PC98SIO_in_modem_port(if_type); 429942262Skato iod->ctrl = io | PC98SIO_intr_ctrl_port(if_type); 430042262Skato 430142262Skato if (iod->if_type == COM_IF_INTERNAL) { 430242262Skato iod->irq = 4; 430342262Skato 430454174Snyan if (pc98_check_8251vfast()) { 430554174Snyan PC98SIO_baud_rate_port(if_type) = I8251F_div; 430642262Skato if_8251_type[if_type].speedtab = pc98fast_speedtab; 430742262Skato } 430842262Skato } else { 430942262Skato tmp = inb( iod->mod ) & if_8251_type[if_type].irr_mask; 431045783Skato if ((isa_get_port(dev) & 0xff) == IO_COM2) 431142262Skato iod->irq = irq_tab[0][tmp]; 431242262Skato else 431342262Skato iod->irq = irq_tab[1][tmp]; 431442262Skato } 431542262Skato } else { 431642262Skato irr = if_16550a_type[if_type].irr_read; 431742262Skato#ifdef COM_MULTIPORT 431851056Skato if (!COM_ISMULTIPORT(device_get_flags(dev)) || 431951056Skato device_get_unit(dev) == COM_MPMASTER(device_get_flags(dev))) 432016359Sasami#endif 432142262Skato if (irr != -1) { 432242262Skato tmp = inb(io | irr); 432345783Skato if (isa_get_port(dev) & 0x01) /* XXX depend on RSB-384 */ 432442262Skato iod->irq = irq_tab[1][tmp >> 3]; 432542262Skato else 432642262Skato iod->irq = irq_tab[0][tmp & 0x07]; 432742262Skato } 4328171380Smjacob iod->cmd = 0; 4329171380Smjacob iod->sts = 0; 4330171380Smjacob iod->mod = 0; 4331171380Smjacob iod->ctrl = 0; 433216359Sasami } 433342262Skato if ( iod->irq == -1 ) return -1; 433416359Sasami 433516359Sasami return 0; 433616359Sasami} 433760472Snyanstatic void 433854174Snyanpc98_set_ioport(struct com_s *com) 433916359Sasami{ 434054174Snyan int if_type = com->pc98_if_type & 0x0f; 434160472Snyan Port_t io = rman_get_start(com->ioportres) & 0xff00; 434216359Sasami 434360472Snyan pc98_check_sysclock(); 434460472Snyan com->data_port = io | PC98SIO_data_port(if_type); 434560472Snyan com->cmd_port = io | PC98SIO_cmd_port(if_type); 434660472Snyan com->sts_port = io | PC98SIO_sts_port(if_type); 434760472Snyan com->in_modem_port = io | PC98SIO_in_modem_port(if_type); 434860472Snyan com->intr_ctrl_port = io | PC98SIO_intr_ctrl_port(if_type); 434916359Sasami} 435054174Snyanstatic int 435154174Snyanpc98_check_8251vfast(void) 435254174Snyan{ 435354174Snyan int i; 435454174Snyan 435554174Snyan outb(I8251F_div, 0x8c); 435654174Snyan DELAY(10); 435754174Snyan for (i = 0; i < 100; i++) { 435854174Snyan if ((inb(I8251F_div) & 0x80) != 0) { 435954174Snyan i = 0; 436054174Snyan break; 436154174Snyan } 436254174Snyan DELAY(1); 436354174Snyan } 436454174Snyan outb(I8251F_div, 0); 436554174Snyan DELAY(10); 436654174Snyan for (; i < 100; i++) { 436754174Snyan if ((inb(I8251F_div) & 0x80) == 0) 436854174Snyan return 1; 436954174Snyan DELAY(1); 437054174Snyan } 437154174Snyan 437254174Snyan return 0; 437354174Snyan} 437454174Snyanstatic int 437554174Snyanpc98_check_8251fifo(void) 437654174Snyan{ 437754174Snyan u_char tmp1, tmp2; 437854174Snyan 437954174Snyan tmp1 = inb(I8251F_iir); 438054174Snyan DELAY(10); 438154174Snyan tmp2 = inb(I8251F_iir); 438254174Snyan if (((tmp1 ^ tmp2) & 0x40) != 0 && ((tmp1 | tmp2) & 0x20) == 0) 438354174Snyan return 1; 438454174Snyan 438554174Snyan return 0; 438654174Snyan} 438716359Sasami#endif /* PC98 defined */ 4388