19406Sbde/*- 26261Sjkh * cyclades cyclom-y serial driver 36261Sjkh * Andrew Herbert <andrew@werple.apana.org.au>, 17 August 1993 46261Sjkh * 56261Sjkh * Copyright (c) 1993 Andrew Herbert. 66261Sjkh * All rights reserved. 76261Sjkh * 86261Sjkh * Redistribution and use in source and binary forms, with or without 96261Sjkh * modification, are permitted provided that the following conditions 106261Sjkh * are met: 116261Sjkh * 1. Redistributions of source code must retain the above copyright 126261Sjkh * notice, this list of conditions and the following disclaimer. 136261Sjkh * 2. Redistributions in binary form must reproduce the above copyright 146261Sjkh * notice, this list of conditions and the following disclaimer in the 156261Sjkh * documentation and/or other materials provided with the distribution. 166261Sjkh * 3. The name Andrew Herbert may not be used to endorse or promote products 176261Sjkh * derived from this software without specific prior written permission. 186261Sjkh * 196261Sjkh * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED 206261Sjkh * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 216261Sjkh * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 226261Sjkh * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 236261Sjkh * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 246261Sjkh * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 256261Sjkh * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 266261Sjkh * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 276261Sjkh * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 286261Sjkh * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 296261Sjkh */ 306261Sjkh 31115703Sobrien#include <sys/cdefs.h> 32115703Sobrien__FBSDID("$FreeBSD$"); 33115703Sobrien 3431778Seivind#include "opt_compat.h" 3529047Sbde 366261Sjkh/* 379406Sbde * TODO: 389406Sbde * Atomic COR change. 399406Sbde * Consoles. 406261Sjkh */ 416261Sjkh 426261Sjkh/* 439406Sbde * Temporary compile-time configuration options. 446261Sjkh */ 459406Sbde#define RxFifoThreshold (CD1400_RX_FIFO_SIZE / 2) 469406Sbde /* Number of chars in the receiver FIFO before an 479406Sbde * an interrupt is generated. Should depend on 489406Sbde * line speed. Needs to be about 6 on a 486DX33 499406Sbde * for 4 active ports at 115200 bps. Why doesn't 509406Sbde * 10 work? 519406Sbde */ 529406Sbde#define PollMode /* Use polling-based irq service routine, not the 539406Sbde * hardware svcack lines. Must be defined for 549406Sbde * Cyclom-16Y boards. Less efficient for Cyclom-8Ys, 559406Sbde * and stops 4 * 115200 bps from working. 569406Sbde */ 579406Sbde#undef Smarts /* Enable slightly more CD1400 intelligence. Mainly 589406Sbde * the output CR/LF processing, plus we can avoid a 599406Sbde * few checks usually done in ttyinput(). 609406Sbde * 619406Sbde * XXX not fully implemented, and not particularly 629406Sbde * worthwhile. 639406Sbde */ 649406Sbde#undef CyDebug /* Include debugging code (not very expensive). */ 656261Sjkh 669406Sbde/* These will go away. */ 679406Sbde#undef SOFT_CTS_OFLOW 689406Sbde#define SOFT_HOTCHAR 696261Sjkh 706261Sjkh#include <sys/param.h> 716261Sjkh#include <sys/systm.h> 7276166Smarkm#include <sys/bus.h> 736261Sjkh#include <sys/conf.h> 7424131Sbde#include <sys/fcntl.h> 7538246Sbde#include <sys/interrupt.h> 766261Sjkh#include <sys/kernel.h> 7776166Smarkm#include <sys/lock.h> 789406Sbde#include <sys/malloc.h> 7971846Sbde#include <sys/mutex.h> 80136139Sphk#include <sys/serial.h> 816261Sjkh#include <sys/syslog.h> 8276166Smarkm#include <sys/tty.h> 8376166Smarkm 8447585Sbde#include <machine/psl.h> 856261Sjkh 86128800Sbde#include <dev/ic/cd1400.h> 876261Sjkh 88128800Sbde#include <dev/cy/cyreg.h> 89128800Sbde#include <dev/cy/cyvar.h> 90128800Sbde 91127885Sbde#define NCY 10 /* KLUDGE */ 9261011Speter 93136139Sphk#define NPORTS (NCY * CY_MAX_PORTS) 946261Sjkh 959406Sbde#define CY_MAX_PORTS (CD1400_NO_OF_CHANNELS * CY_MAX_CD1400s) 966261Sjkh 979406Sbde/* We encode the cyclom unit number (cyu) in spare bits in the IVR's. */ 989406Sbde#define CD1400_xIVR_CHAN_SHIFT 3 9929047Sbde#define CD1400_xIVR_CHAN 0x1F 1006261Sjkh 10141903Sbde/* 10241903Sbde * ETC states. com->etc may also contain a hardware ETC command value, 10341903Sbde * meaning that execution of that command is pending. 10441903Sbde */ 10541903Sbde#define ETC_NONE 0 /* we depend on bzero() setting this */ 10641903Sbde#define ETC_BREAK_STARTING 1 10741903Sbde#define ETC_BREAK_STARTED 2 10841903Sbde#define ETC_BREAK_ENDING 3 10941903Sbde#define ETC_BREAK_ENDED 4 11041903Sbde 1119406Sbde#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ 1126261Sjkh 11381580Sbde/* 1149406Sbde * com state bits. 1159406Sbde * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher 1169406Sbde * than the other bits so that they can be tested as a group without masking 1179406Sbde * off the low bits. 1189406Sbde * 1199406Sbde * The following com and tty flags correspond closely: 120136139Sphk * CS_BUSY = TS_BUSY (maintained by cystart(), cypoll() and 12151654Sphk * comstop()) 122136139Sphk * CS_TTGO = ~TS_TTSTOP (maintained by cyparam() and cystart()) 123136139Sphk * CS_CTS_OFLOW = CCTS_OFLOW (maintained by cyparam()) 124136139Sphk * CS_RTS_IFLOW = CRTS_IFLOW (maintained by cyparam()) 1259406Sbde * TS_FLUSH is not used. 1269406Sbde * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. 1279406Sbde * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). 1289406Sbde */ 1299406Sbde#define CS_BUSY 0x80 /* output in progress */ 1309406Sbde#define CS_TTGO 0x40 /* output not stopped by XOFF */ 1319406Sbde#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ 1329406Sbde#define CS_CHECKMSR 1 /* check of MSR scheduled */ 1339406Sbde#define CS_CTS_OFLOW 2 /* use CTS output flow control */ 1349406Sbde#define CS_ODONE 4 /* output completed */ 1359406Sbde#define CS_RTS_IFLOW 8 /* use RTS input flow control */ 13641388Sbde#define CSE_ODONE 1 /* output transmitted */ 1376261Sjkh 1389406Sbdestatic char const * const error_desc[] = { 1399406Sbde#define CE_OVERRUN 0 1409406Sbde "silo overflow", 1419406Sbde#define CE_INTERRUPT_BUF_OVERFLOW 1 1429406Sbde "interrupt-level buffer overflow", 1439406Sbde#define CE_TTY_BUF_OVERFLOW 2 1449406Sbde "tty-level buffer overflow", 1459406Sbde}; 1466261Sjkh 1479406Sbde#define CE_NTYPES 3 1489406Sbde#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) 1496261Sjkh 150122771Sbde#ifdef SMP 151136139Sphk#define COM_LOCK() mtx_lock_spin(&cy_lock) 152136139Sphk#define COM_UNLOCK() mtx_unlock_spin(&cy_lock) 153122771Sbde#else 154122771Sbde#define COM_LOCK() 155122771Sbde#define COM_UNLOCK() 156122771Sbde#endif 157122771Sbde 1589406Sbde/* types. XXX - should be elsewhere */ 1599406Sbdetypedef u_char bool_t; /* boolean */ 1609406Sbde 1619406Sbde/* queue of linear buffers */ 1629406Sbdestruct lbq { 1639406Sbde u_char *l_head; /* next char to process */ 1649406Sbde u_char *l_tail; /* one past the last char to process */ 1659406Sbde struct lbq *l_next; /* next in queue */ 1669406Sbde bool_t l_queued; /* nonzero if queued */ 1679406Sbde}; 1689406Sbde 1699406Sbde/* com device structure */ 1709406Sbdestruct com_s { 1719406Sbde u_char state; /* miscellaneous flag bits */ 17241903Sbde u_char etc; /* pending Embedded Transmit Command */ 17341388Sbde u_char extra_state; /* more flag bits, separate for order trick */ 17438303Sbde u_char gfrcr_image; /* copy of value read from GFRCR */ 17538303Sbde u_char mcr_dtr; /* MCR bit that is wired to DTR */ 1769406Sbde u_char mcr_image; /* copy of value written to MCR */ 17738303Sbde u_char mcr_rts; /* MCR bit that is wired to RTS */ 1789406Sbde int unit; /* unit number */ 1796261Sjkh 1809406Sbde /* 1819406Sbde * The high level of the driver never reads status registers directly 1829406Sbde * because there would be too many side effects to handle conveniently. 1839406Sbde * Instead, it reads copies of the registers stored here by the 1849406Sbde * interrupt handler. 1859406Sbde */ 1869406Sbde u_char last_modem_status; /* last MSR read by intr handler */ 1879406Sbde u_char prev_modem_status; /* last MSR handled by high level */ 1886261Sjkh 1899406Sbde u_char *ibuf; /* start of input buffer */ 1909406Sbde u_char *ibufend; /* end of input buffer */ 19143611Sbde u_char *ibufold; /* old input buffer, to be freed */ 1929406Sbde u_char *ihighwater; /* threshold in input buffer */ 1939406Sbde u_char *iptr; /* next free spot in input buffer */ 19443611Sbde int ibufsize; /* size of ibuf (not include error bytes) */ 19543611Sbde int ierroff; /* offset of error bytes in ibuf */ 1966261Sjkh 1979406Sbde struct lbq obufq; /* head of queue of output buffers */ 1989406Sbde struct lbq obufs[2]; /* output buffers */ 1996261Sjkh 20029047Sbde int cy_align; /* index for register alignment */ 2019406Sbde cy_addr cy_iobase; /* base address of this port's cyclom */ 2029406Sbde cy_addr iobase; /* base address of this port's cd1400 */ 20338303Sbde int mcr_rts_reg; /* cd1400 reg number of reg holding mcr_rts */ 2046261Sjkh 2059406Sbde struct tty *tp; /* cross reference */ 2069406Sbde 2079406Sbde u_long bytes_in; /* statistics */ 2089406Sbde u_long bytes_out; 2099406Sbde u_int delta_error_counts[CE_NTYPES]; 2109406Sbde u_long error_counts[CE_NTYPES]; 2119406Sbde 2129406Sbde u_int recv_exception; /* exception chars received */ 2139406Sbde u_int mdm; /* modem signal changes */ 2149406Sbde#ifdef CyDebug 215136139Sphk u_int start_count; /* no. of calls to cystart() */ 2169406Sbde u_int start_real; /* no. of calls that did something */ 2176261Sjkh#endif 21841293Sbde u_char car; /* CD1400 CAR shadow (if first unit in cd) */ 2199406Sbde u_char channel_control;/* CD1400 CCR control command shadow */ 2209406Sbde u_char cor[3]; /* CD1400 COR1-3 shadows */ 2219406Sbde u_char intr_enable; /* CD1400 SRER shadow */ 2226261Sjkh 2239406Sbde /* 2249406Sbde * Data area for output buffers. Someday we should build the output 2259406Sbde * buffer queue without copying data. 2269406Sbde */ 2279406Sbde u_char obuf1[256]; 2289406Sbde u_char obuf2[256]; 2299406Sbde}; 2309406Sbde 231127885Sbdedevclass_t cy_devclass; 232128800Sbdechar cy_driver_name[] = "cy"; 233127885Sbde 23492765Salfredstatic void cd1400_channel_cmd(struct com_s *com, int cmd); 23592765Salfredstatic void cd1400_channel_cmd_wait(struct com_s *com); 23693024Sbdestatic void cd_etc(struct com_s *com, int etc); 23793024Sbdestatic int cd_getreg(struct com_s *com, int reg); 23893024Sbdestatic void cd_setreg(struct com_s *com, int reg, int val); 239136139Sphkstatic void cyinput(struct com_s *com); 240136139Sphkstatic int cyparam(struct tty *tp, struct termios *t); 241136139Sphkstatic void cypoll(void *arg); 242136139Sphkstatic void cysettimeout(void); 243136139Sphkstatic int cysetwater(struct com_s *com, speed_t speed); 244136139Sphkstatic int cyspeed(speed_t speed, u_long cy_clock, int *prescaler_io); 245136139Sphkstatic void cystart(struct tty *tp); 24693024Sbdestatic void comstop(struct tty *tp, int rw); 247136139Sphkstatic timeout_t cywakeup; 24893024Sbdestatic void disc_optim(struct tty *tp, struct termios *t, 24993024Sbde struct com_s *com); 2509406Sbde 251136139Sphkstatic t_break_t cybreak; 252136139Sphkstatic t_modem_t cymodem; 253136139Sphkstatic t_open_t cyopen; 254136139Sphkstatic t_close_t cyclose; 255136139Sphk 2566261Sjkh#ifdef CyDebug 25793024Sbdevoid cystatus(int unit); 2586261Sjkh#endif 2599406Sbde 260136139Sphkstatic struct mtx cy_lock; 261136139Sphkstatic int cy_inited; 26212962Sbde 2639406Sbde/* table and macro for fast conversion from a unit number to its com struct */ 264136139Sphkstatic struct com_s *p_cy_addr[NPORTS]; 265136139Sphk#define cy_addr(unit) (p_cy_addr[unit]) 2669406Sbde 267136139Sphkstatic u_int cy_events; /* input chars + weighted output completions */ 268136139Sphkstatic void *cy_fast_ih; 269136139Sphkstatic void *cy_slow_ih; 270136139Sphkstatic int cy_timeout; 271136139Sphkstatic int cy_timeouts_until_log; 272136139Sphkstatic struct callout_handle cy_timeout_handle 273136139Sphk = CALLOUT_HANDLE_INITIALIZER(&cy_timeout_handle); 27412962Sbde 2756261Sjkh#ifdef CyDebug 2769406Sbdestatic u_int cd_inbs; 2779406Sbdestatic u_int cy_inbs; 2789406Sbdestatic u_int cd_outbs; 2799406Sbdestatic u_int cy_outbs; 2809406Sbdestatic u_int cy_svrr_probes; 2819406Sbdestatic u_int cy_timeouts; 2826261Sjkh#endif 2836261Sjkh 28429047Sbdestatic int cy_chip_offset[] = { 28531104Sbde 0x0000, 0x0400, 0x0800, 0x0c00, 0x0200, 0x0600, 0x0a00, 0x0e00, 28629047Sbde}; 2879406Sbdestatic int cy_nr_cd1400s[NCY]; 28818901Sdgstatic int cy_total_devices; 2899406Sbde#undef RxFifoThreshold 2909406Sbdestatic int volatile RxFifoThreshold = (CD1400_RX_FIFO_SIZE / 2); 2916261Sjkh 292128800Sbdeint 293136139Sphkcy_units(cy_addr cy_iobase, int cy_align) 29418901Sdg{ 29529047Sbde int cyu; 29629047Sbde u_char firmware_version; 29729047Sbde int i; 29829047Sbde cy_addr iobase; 29918901Sdg 30018679Sdg for (cyu = 0; cyu < CY_MAX_CD1400s; ++cyu) { 30129047Sbde iobase = cy_iobase + (cy_chip_offset[cyu] << cy_align); 3026261Sjkh 3039406Sbde /* wait for chip to become ready for new command */ 30411424Sdg for (i = 0; i < 10; i++) { 3059406Sbde DELAY(50); 30618901Sdg if (!cd_inb(iobase, CD1400_CCR, cy_align)) 3079406Sbde break; 3089406Sbde } 3096261Sjkh 3109406Sbde /* clear the GFRCR register */ 31118901Sdg cd_outb(iobase, CD1400_GFRCR, cy_align, 0); 3126261Sjkh 3139406Sbde /* issue a reset command */ 31418901Sdg cd_outb(iobase, CD1400_CCR, cy_align, 3159406Sbde CD1400_CCR_CMDRESET | CD1400_CCR_FULLRESET); 3166261Sjkh 31791314Sbde /* XXX bogus initialization to avoid a gcc bug/warning. */ 31891314Sbde firmware_version = 0; 31991314Sbde 3209406Sbde /* wait for the CD1400 to initialize itself */ 32111424Sdg for (i = 0; i < 200; i++) { 3229406Sbde DELAY(50); 3239406Sbde 3249406Sbde /* retrieve firmware version */ 32529047Sbde firmware_version = cd_inb(iobase, CD1400_GFRCR, 32629047Sbde cy_align); 32711424Sdg if ((firmware_version & 0xf0) == 0x40) 3289406Sbde break; 3299406Sbde } 3309406Sbde 3319406Sbde /* 3329406Sbde * Anything in the 0x40-0x4F range is fine. 3339406Sbde * If one CD1400 is bad then we don't support higher 3349406Sbde * numbered good ones on this board. 3359406Sbde */ 33611424Sdg if ((firmware_version & 0xf0) != 0x40) 3379406Sbde break; 3386261Sjkh } 33918901Sdg return (cyu); 3406261Sjkh} 3416261Sjkh 342123104Sbdevoid * 343136139Sphkcyattach_common(cy_addr cy_iobase, int cy_align) 34418901Sdg{ 34529047Sbde int adapter; 34629047Sbde int cyu; 34738303Sbde u_char firmware_version; 34829047Sbde cy_addr iobase; 34929047Sbde int ncyu; 35029047Sbde int unit; 351136139Sphk struct tty *tp; 35218901Sdg 353136139Sphk while (cy_inited != 2) 354136139Sphk if (atomic_cmpset_int(&cy_inited, 0, 1)) { 355136139Sphk mtx_init(&cy_lock, cy_driver_name, NULL, MTX_SPIN); 356136139Sphk atomic_store_rel_int(&cy_inited, 2); 357127883Sbde } 358127883Sbde 35918901Sdg adapter = cy_total_devices; 36018901Sdg if ((u_int)adapter >= NCY) { 36129047Sbde printf( 36229047Sbde "cy%d: can't attach adapter: insufficient cy devices configured\n", 36329047Sbde adapter); 364123104Sbde return (NULL); 36518901Sdg } 36618901Sdg ncyu = cy_units(cy_iobase, cy_align); 36718901Sdg if (ncyu == 0) 368123104Sbde return (NULL); 36918901Sdg cy_nr_cd1400s[adapter] = ncyu; 37018901Sdg cy_total_devices++; 37118901Sdg 37218901Sdg unit = adapter * CY_MAX_PORTS; 37318679Sdg for (cyu = 0; cyu < ncyu; ++cyu) { 3749406Sbde int cdu; 3756261Sjkh 37629047Sbde iobase = (cy_addr) (cy_iobase 37729047Sbde + (cy_chip_offset[cyu] << cy_align)); 37838303Sbde firmware_version = cd_inb(iobase, CD1400_GFRCR, cy_align); 37929047Sbde 3809406Sbde /* Set up a receive timeout period of than 1+ ms. */ 38138303Sbde cd_outb(iobase, CD1400_PPR, cy_align, 38238303Sbde howmany(CY_CLOCK(firmware_version) 38338303Sbde / CD1400_PPR_PRESCALER, 1000)); 38438303Sbde 3859406Sbde for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) { 3869406Sbde struct com_s *com; 3879406Sbde int s; 3886261Sjkh 389136139Sphk com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT | M_ZERO); 390136139Sphk if (com == NULL) 391136139Sphk break; 392136139Sphk com->unit = unit; 39338303Sbde com->gfrcr_image = firmware_version; 39438303Sbde if (CY_RTS_DTR_SWAPPED(firmware_version)) { 395136139Sphk com->mcr_dtr = CD1400_MSVR1_RTS; 396136139Sphk com->mcr_rts = CD1400_MSVR2_DTR; 39738303Sbde com->mcr_rts_reg = CD1400_MSVR2; 39838303Sbde } else { 399136139Sphk com->mcr_dtr = CD1400_MSVR2_DTR; 400136139Sphk com->mcr_rts = CD1400_MSVR1_RTS; 40138303Sbde com->mcr_rts_reg = CD1400_MSVR1; 40238303Sbde } 403136139Sphk com->obufs[0].l_head = com->obuf1; 404136139Sphk com->obufs[1].l_head = com->obuf2; 4056261Sjkh 40629047Sbde com->cy_align = cy_align; 4079406Sbde com->cy_iobase = cy_iobase; 408136139Sphk com->iobase = iobase; 40941293Sbde com->car = ~CD1400_CAR_CHAN; 4106261Sjkh 411136139Sphk tp = com->tp = ttyalloc(); 412136139Sphk tp->t_open = cyopen; 413136139Sphk tp->t_close = cyclose; 414136139Sphk tp->t_oproc = cystart; 415136139Sphk tp->t_stop = comstop; 416136139Sphk tp->t_param = cyparam; 417136139Sphk tp->t_break = cybreak; 418136139Sphk tp->t_modem = cymodem; 419136139Sphk tp->t_sc = com; 4206261Sjkh 421136139Sphk if (cysetwater(com, tp->t_init_in.c_ispeed) != 0) { 422136139Sphk free(com, M_DEVBUF); 423136139Sphk return (NULL); 424136139Sphk } 42512962Sbde 426136139Sphk s = spltty(); 427136139Sphk cy_addr(unit) = com; 428136139Sphk splx(s); 429136139Sphk 430136139Sphk if (cy_fast_ih == NULL) { 431151690Sru swi_add(&tty_intr_event, "cy", cypoll, NULL, SWI_TTY, 0, 432136139Sphk &cy_fast_ih); 433151690Sru swi_add(&clk_intr_event, "cy", cypoll, NULL, SWI_CLOCK, 0, 434136139Sphk &cy_slow_ih); 435136139Sphk } 436151383Sphk ttycreate(tp, TS_CALLOUT, "c%r%r", 437136139Sphk adapter, unit % CY_MAX_PORTS); 4389406Sbde } 4396261Sjkh } 4406261Sjkh 4419406Sbde /* ensure an edge for the next interrupt */ 44241309Sbde cy_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0); 4436261Sjkh 444136139Sphk return (cy_addr(adapter * CY_MAX_PORTS)); 4456261Sjkh} 4466261Sjkh 44712675Sjulianstatic int 448136139Sphkcyopen(struct tty *tp, struct cdev *dev) 4496261Sjkh{ 4509406Sbde struct com_s *com; 4519406Sbde int s; 4526261Sjkh 453136139Sphk com = tp->t_sc; 4549406Sbde s = spltty(); 4559406Sbde /* 4569406Sbde * We jump to this label after all non-interrupted sleeps to pick 4579406Sbde * up any changes of the device state. 4589406Sbde */ 45942045Sbde 460136139Sphk /* Encode per-board unit in LIVR for access in intr routines. */ 461136139Sphk cd_setreg(com, CD1400_LIVR, 462136139Sphk (com->unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT); 46342045Sbde 4649406Sbde /* 465136139Sphk * Flush fifos. This requires a full channel reset which 466136139Sphk * also disables the transmitter and receiver. Recover 467136139Sphk * from this. 4689406Sbde */ 469136139Sphk cd1400_channel_cmd(com, 470136139Sphk CD1400_CCR_CMDRESET | CD1400_CCR_CHANRESET); 471136139Sphk cd1400_channel_cmd(com, com->channel_control); 4726261Sjkh 473136139Sphk critical_enter(); 474136139Sphk COM_LOCK(); 475136139Sphk com->prev_modem_status = com->last_modem_status 476136139Sphk = cd_getreg(com, CD1400_MSVR2); 477136139Sphk cd_setreg(com, CD1400_SRER, 478136139Sphk com->intr_enable 479136139Sphk = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA); 480136139Sphk COM_UNLOCK(); 481136139Sphk critical_exit(); 482136139Sphk cysettimeout(); 4839406Sbde return (0); 4849406Sbde} 4856261Sjkh 486136139Sphk 4879406Sbdestatic void 488136139Sphkcyclose(struct tty *tp) 4899406Sbde{ 4909406Sbde cy_addr iobase; 491136139Sphk struct com_s *com; 4929406Sbde int s; 4939406Sbde int unit; 4946261Sjkh 495136139Sphk com = tp->t_sc; 4969406Sbde unit = com->unit; 4979406Sbde iobase = com->iobase; 4989406Sbde s = spltty(); 49941903Sbde /* XXX */ 50088088Sjhb critical_enter(); 50172262Sjhb COM_LOCK(); 50241903Sbde com->etc = ETC_NONE; 50341903Sbde cd_setreg(com, CD1400_COR2, com->cor[1] &= ~CD1400_COR2_ETC); 50472262Sjhb COM_UNLOCK(); 50588088Sjhb critical_exit(); 506136139Sphk cd_etc(com, CD1400_ETC_STOPBREAK); 50741903Sbde cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_FTF); 5086261Sjkh 5099406Sbde { 51088088Sjhb critical_enter(); 51172262Sjhb COM_LOCK(); 51241293Sbde cd_setreg(com, CD1400_SRER, com->intr_enable = 0); 51372262Sjhb COM_UNLOCK(); 51488088Sjhb critical_exit(); 5159406Sbde tp = com->tp; 51643314Sdillon if ((tp->t_cflag & HUPCL) 5179406Sbde /* 5189406Sbde * XXX we will miss any carrier drop between here and the 5199406Sbde * next open. Perhaps we should watch DCD even when the 5209406Sbde * port is closed; it is not sufficient to check it at 5219406Sbde * the next open because it might go up and down while 5229406Sbde * we're not watching. 5239406Sbde */ 524136139Sphk || (!tp->t_actout 525136139Sphk && !(com->prev_modem_status & CD1400_MSVR2_CD) 526136139Sphk && !(tp->t_init_in.c_cflag & CLOCAL)) 5279406Sbde || !(tp->t_state & TS_ISOPEN)) { 528136139Sphk (void)cymodem(tp, 0, SER_DTR); 5296261Sjkh 5309406Sbde /* Disable receiver (leave transmitter enabled). */ 5319406Sbde com->channel_control = CD1400_CCR_CMDCHANCTL 5329406Sbde | CD1400_CCR_XMTEN 5339406Sbde | CD1400_CCR_RCVDIS; 53441293Sbde cd1400_channel_cmd(com, com->channel_control); 5356261Sjkh 536131981Sphk ttydtrwaitstart(tp); 5379406Sbde } 5389406Sbde } 539136139Sphk tp->t_actout = FALSE; 540136139Sphk wakeup(&tp->t_actout); 5419406Sbde wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ 5429406Sbde splx(s); 5439406Sbde} 5449406Sbde 54565557Sjasone/* 54672262Sjhb * This function: 54772262Sjhb * a) needs to be called with COM_LOCK() held, and 54872262Sjhb * b) needs to return with COM_LOCK() held. 54965557Sjasone */ 55043611Sbdestatic void 551136139Sphkcyinput(struct com_s *com) 55243611Sbde{ 55343611Sbde u_char *buf; 55443611Sbde int incc; 55543611Sbde u_char line_status; 55643611Sbde int recv_data; 55743611Sbde struct tty *tp; 55843611Sbde 55943611Sbde buf = com->ibuf; 56043611Sbde tp = com->tp; 56143611Sbde if (!(tp->t_state & TS_ISOPEN)) { 562136139Sphk cy_events -= (com->iptr - com->ibuf); 56343611Sbde com->iptr = com->ibuf; 56443611Sbde return; 56543611Sbde } 56643611Sbde if (tp->t_state & TS_CAN_BYPASS_L_RINT) { 56743611Sbde /* 56843611Sbde * Avoid the grotesquely inefficient lineswitch routine 56943611Sbde * (ttyinput) in "raw" mode. It usually takes about 450 57043611Sbde * instructions (that's without canonical processing or echo!). 57143611Sbde * slinput is reasonably fast (usually 40 instructions plus 57243611Sbde * call overhead). 57343611Sbde */ 57465557Sjasone 57543611Sbde do { 57665557Sjasone /* 57765557Sjasone * This may look odd, but it is using save-and-enable 57865557Sjasone * semantics instead of the save-and-disable semantics 57965557Sjasone * that are used everywhere else. 58065557Sjasone */ 58172262Sjhb COM_UNLOCK(); 58288088Sjhb critical_exit(); 58343611Sbde incc = com->iptr - buf; 58443611Sbde if (tp->t_rawq.c_cc + incc > tp->t_ihiwat 58543611Sbde && (com->state & CS_RTS_IFLOW 58643611Sbde || tp->t_iflag & IXOFF) 58743611Sbde && !(tp->t_state & TS_TBLOCK)) 58843611Sbde ttyblock(tp); 58943611Sbde com->delta_error_counts[CE_TTY_BUF_OVERFLOW] 59043611Sbde += b_to_q((char *)buf, incc, &tp->t_rawq); 59143611Sbde buf += incc; 59243611Sbde tk_nin += incc; 59343611Sbde tk_rawcc += incc; 59443611Sbde tp->t_rawcc += incc; 59543611Sbde ttwakeup(tp); 59643611Sbde if (tp->t_state & TS_TTSTOP 59743611Sbde && (tp->t_iflag & IXANY 59843611Sbde || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { 59943611Sbde tp->t_state &= ~TS_TTSTOP; 60043611Sbde tp->t_lflag &= ~FLUSHO; 601136139Sphk cystart(tp); 60243611Sbde } 60388088Sjhb critical_enter(); 60472262Sjhb COM_LOCK(); 60543611Sbde } while (buf < com->iptr); 60643611Sbde } else { 60743611Sbde do { 60865557Sjasone /* 60965557Sjasone * This may look odd, but it is using save-and-enable 61065557Sjasone * semantics instead of the save-and-disable semantics 61165557Sjasone * that are used everywhere else. 61265557Sjasone */ 61372262Sjhb COM_UNLOCK(); 61488088Sjhb critical_exit(); 61543611Sbde line_status = buf[com->ierroff]; 61643611Sbde recv_data = *buf++; 61743611Sbde if (line_status 618136139Sphk & (CD1400_RDSR_BREAK | CD1400_RDSR_FE | CD1400_RDSR_OE | CD1400_RDSR_PE)) { 619136139Sphk if (line_status & CD1400_RDSR_BREAK) 62043611Sbde recv_data |= TTY_BI; 621136139Sphk if (line_status & CD1400_RDSR_FE) 62243611Sbde recv_data |= TTY_FE; 623136139Sphk if (line_status & CD1400_RDSR_OE) 62443611Sbde recv_data |= TTY_OE; 625136139Sphk if (line_status & CD1400_RDSR_PE) 62643611Sbde recv_data |= TTY_PE; 62743611Sbde } 628130077Sphk ttyld_rint(tp, recv_data); 62988088Sjhb critical_enter(); 63072262Sjhb COM_LOCK(); 63143611Sbde } while (buf < com->iptr); 63243611Sbde } 633136139Sphk cy_events -= (com->iptr - com->ibuf); 63443611Sbde com->iptr = com->ibuf; 63543611Sbde 63643611Sbde /* 63743611Sbde * There is now room for another low-level buffer full of input, 63843611Sbde * so enable RTS if it is now disabled and there is room in the 63943611Sbde * high-level buffer. 64043611Sbde */ 64143611Sbde if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & com->mcr_rts) && 64243611Sbde !(tp->t_state & TS_TBLOCK)) 64343611Sbde cd_setreg(com, com->mcr_rts_reg, 64443611Sbde com->mcr_image |= com->mcr_rts); 64543611Sbde} 64643611Sbde 647166901Spisoint 648136139Sphkcyintr(void *vcom) 6496261Sjkh{ 650123104Sbde struct com_s *basecom; 65129047Sbde int baseu; 65229047Sbde int cy_align; 65329047Sbde cy_addr cy_iobase; 65429047Sbde int cyu; 65529047Sbde cy_addr iobase; 65629047Sbde u_char status; 657123104Sbde int unit; 6586261Sjkh 65972262Sjhb COM_LOCK(); /* XXX could this be placed down lower in the loop? */ 66028921Sfsmp 661123104Sbde basecom = (struct com_s *)vcom; 662123104Sbde baseu = basecom->unit; 663123104Sbde cy_align = basecom->cy_align; 664123104Sbde cy_iobase = basecom->cy_iobase; 665123104Sbde unit = baseu / CY_MAX_PORTS; 6666261Sjkh 6679406Sbde /* check each CD1400 in turn */ 66818685Sdg for (cyu = 0; cyu < cy_nr_cd1400s[unit]; ++cyu) { 66929047Sbde iobase = (cy_addr) (cy_iobase 67029047Sbde + (cy_chip_offset[cyu] << cy_align)); 6719406Sbde /* poll to see if it has any work */ 67218901Sdg status = cd_inb(iobase, CD1400_SVRR, cy_align); 6739406Sbde if (status == 0) 674166901Spiso continue; // XXX - FILTER_STRAY? 6756261Sjkh#ifdef CyDebug 6769406Sbde ++cy_svrr_probes; 6776261Sjkh#endif 6789406Sbde /* service requests as appropriate, giving priority to RX */ 6799406Sbde if (status & CD1400_SVRR_RXRDY) { 6809406Sbde struct com_s *com; 6819406Sbde u_int count; 6829406Sbde u_char *ioptr; 6839406Sbde u_char line_status; 6849406Sbde u_char recv_data; 6859406Sbde u_char serv_type; 6869406Sbde#ifdef PollMode 6879406Sbde u_char save_rir; 6886261Sjkh#endif 6896261Sjkh 6909406Sbde#ifdef PollMode 69118901Sdg save_rir = cd_inb(iobase, CD1400_RIR, cy_align); 6926261Sjkh 6939406Sbde /* enter rx service */ 69418901Sdg cd_outb(iobase, CD1400_CAR, cy_align, save_rir); 695136139Sphk cy_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car 69641293Sbde = save_rir & CD1400_CAR_CHAN; 6976261Sjkh 69818901Sdg serv_type = cd_inb(iobase, CD1400_RIVR, cy_align); 699136139Sphk com = cy_addr(baseu 7009406Sbde + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) 7019406Sbde & CD1400_xIVR_CHAN)); 7026261Sjkh#else 7039406Sbde /* ack receive service */ 70441309Sbde serv_type = cy_inb(iobase, CY8_SVCACKR, cy_align); 7056261Sjkh 706136139Sphk com = cy_addr(baseu + 7079406Sbde + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) 7089406Sbde & CD1400_xIVR_CHAN)); 7096261Sjkh#endif 7106261Sjkh 7119406Sbde if (serv_type & CD1400_RIVR_EXCEPTION) { 7129406Sbde ++com->recv_exception; 71318901Sdg line_status = cd_inb(iobase, CD1400_RDSR, cy_align); 7149406Sbde /* break/unnattached error bits or real input? */ 71518901Sdg recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); 7169406Sbde#ifndef SOFT_HOTCHAR 7179406Sbde if (line_status & CD1400_RDSR_SPECIAL 718131134Sphk && com->tp->t_hotchar != 0) 719136139Sphk swi_sched(cy_fast_ih, 0); 72067551Sjhb 7216261Sjkh#endif 7229406Sbde#if 1 /* XXX "intelligent" PFO error handling would break O error handling */ 723136139Sphk if (line_status & (CD1400_RDSR_PE|CD1400_RDSR_FE|CD1400_RDSR_BREAK)) { 7249406Sbde /* 7259406Sbde Don't store PE if IGNPAR and BI if IGNBRK, 7269406Sbde this hack allows "raw" tty optimization 7279406Sbde works even if IGN* is set. 7289406Sbde */ 7299406Sbde if ( com->tp == NULL 7309406Sbde || !(com->tp->t_state & TS_ISOPEN) 731136139Sphk || ((line_status & (CD1400_RDSR_PE|CD1400_RDSR_FE)) 73243314Sdillon && (com->tp->t_iflag & IGNPAR)) 733136139Sphk || ((line_status & CD1400_RDSR_BREAK) 73443314Sdillon && (com->tp->t_iflag & IGNBRK))) 7359406Sbde goto cont; 736136139Sphk if ( (line_status & (CD1400_RDSR_PE|CD1400_RDSR_FE)) 7379406Sbde && (com->tp->t_state & TS_CAN_BYPASS_L_RINT) 738136139Sphk && ((line_status & CD1400_RDSR_FE) 739136139Sphk || ((line_status & CD1400_RDSR_PE) 74043314Sdillon && (com->tp->t_iflag & INPCK)))) 7419406Sbde recv_data = 0; 7429406Sbde } 7439406Sbde#endif /* 1 */ 7449406Sbde ++com->bytes_in; 7459406Sbde#ifdef SOFT_HOTCHAR 746131134Sphk if (com->tp->t_hotchar != 0 && recv_data == com->tp->t_hotchar) 747136139Sphk swi_sched(cy_fast_ih, 0); 7486261Sjkh#endif 7499406Sbde ioptr = com->iptr; 7509406Sbde if (ioptr >= com->ibufend) 7519406Sbde CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); 7529406Sbde else { 753136139Sphk if (com->tp != NULL && com->tp->t_do_timestamp) 754136139Sphk microtime(&com->tp->t_timestamp); 755136139Sphk ++cy_events; 7569406Sbde ioptr[0] = recv_data; 75743611Sbde ioptr[com->ierroff] = line_status; 7589406Sbde com->iptr = ++ioptr; 7599406Sbde if (ioptr == com->ihighwater 76038303Sbde && com->state & CS_RTS_IFLOW) 76138303Sbde cd_outb(iobase, com->mcr_rts_reg, 76238303Sbde cy_align, 76338303Sbde com->mcr_image &= 76438303Sbde ~com->mcr_rts); 765136139Sphk if (line_status & CD1400_RDSR_OE) 7669406Sbde CE_RECORD(com, CE_OVERRUN); 7679406Sbde } 7689406Sbde goto cont; 7699406Sbde } else { 7709406Sbde int ifree; 7716261Sjkh 77218901Sdg count = cd_inb(iobase, CD1400_RDCR, cy_align); 77334661Sdg if (!count) 77434661Sdg goto cont; 7759406Sbde com->bytes_in += count; 7769406Sbde ioptr = com->iptr; 7779406Sbde ifree = com->ibufend - ioptr; 7789406Sbde if (count > ifree) { 7799406Sbde count -= ifree; 780136139Sphk cy_events += ifree; 78117354Sbde if (ifree != 0) { 782136139Sphk if (com->tp != NULL && com->tp->t_do_timestamp) 783136139Sphk microtime(&com->tp->t_timestamp); 78417354Sbde do { 78517354Sbde recv_data = cd_inb(iobase, 78629047Sbde CD1400_RDSR, 78729047Sbde cy_align); 7889406Sbde#ifdef SOFT_HOTCHAR 789131134Sphk if (com->tp->t_hotchar != 0 79017354Sbde && recv_data 791131134Sphk == com->tp->t_hotchar) 792136139Sphk swi_sched(cy_fast_ih, 793122799Sbde 0); 7946261Sjkh#endif 79517354Sbde ioptr[0] = recv_data; 79643611Sbde ioptr[com->ierroff] = 0; 79717354Sbde ++ioptr; 79817354Sbde } while (--ifree != 0); 7999406Sbde } 8009406Sbde com->delta_error_counts 8019406Sbde [CE_INTERRUPT_BUF_OVERFLOW] += count; 8029406Sbde do { 80329047Sbde recv_data = cd_inb(iobase, CD1400_RDSR, 80429047Sbde cy_align); 8059406Sbde#ifdef SOFT_HOTCHAR 806131134Sphk if (com->tp->t_hotchar != 0 807131134Sphk && recv_data == com->tp->t_hotchar) 808136139Sphk swi_sched(cy_fast_ih, 0); 8096261Sjkh#endif 8109406Sbde } while (--count != 0); 8119406Sbde } else { 812136139Sphk if (com->tp != NULL && com->tp->t_do_timestamp) 813136139Sphk microtime(&com->tp->t_timestamp); 8149406Sbde if (ioptr <= com->ihighwater 8159406Sbde && ioptr + count > com->ihighwater 8169406Sbde && com->state & CS_RTS_IFLOW) 81738303Sbde cd_outb(iobase, com->mcr_rts_reg, 81838303Sbde cy_align, 81938303Sbde com->mcr_image 82038303Sbde &= ~com->mcr_rts); 821136139Sphk cy_events += count; 8229406Sbde do { 82329047Sbde recv_data = cd_inb(iobase, CD1400_RDSR, 82429047Sbde cy_align); 8259406Sbde#ifdef SOFT_HOTCHAR 826131134Sphk if (com->tp->t_hotchar != 0 827131134Sphk && recv_data == com->tp->t_hotchar) 828136139Sphk swi_sched(cy_fast_ih, 0); 8296261Sjkh#endif 8309406Sbde ioptr[0] = recv_data; 83143611Sbde ioptr[com->ierroff] = 0; 8329406Sbde ++ioptr; 8339406Sbde } while (--count != 0); 8346261Sjkh } 8359406Sbde com->iptr = ioptr; 8366261Sjkh } 8379406Sbdecont: 8386261Sjkh 8399406Sbde /* terminate service context */ 8406261Sjkh#ifdef PollMode 84118901Sdg cd_outb(iobase, CD1400_RIR, cy_align, 8429406Sbde save_rir 8439406Sbde & ~(CD1400_RIR_RDIREQ | CD1400_RIR_RBUSY)); 8446261Sjkh#else 84518901Sdg cd_outb(iobase, CD1400_EOSRR, cy_align, 0); 8466261Sjkh#endif 8479406Sbde } 8489406Sbde if (status & CD1400_SVRR_MDMCH) { 8499406Sbde struct com_s *com; 8509406Sbde u_char modem_status; 8516261Sjkh#ifdef PollMode 8529406Sbde u_char save_mir; 8536261Sjkh#else 8549406Sbde u_char vector; 8556261Sjkh#endif 8566261Sjkh 8576261Sjkh#ifdef PollMode 85818901Sdg save_mir = cd_inb(iobase, CD1400_MIR, cy_align); 8596261Sjkh 8609406Sbde /* enter modem service */ 86118901Sdg cd_outb(iobase, CD1400_CAR, cy_align, save_mir); 862136139Sphk cy_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car 86341293Sbde = save_mir & CD1400_CAR_CHAN; 8646261Sjkh 865136139Sphk com = cy_addr(baseu + cyu * CD1400_NO_OF_CHANNELS 8669406Sbde + (save_mir & CD1400_MIR_CHAN)); 8676261Sjkh#else 8689406Sbde /* ack modem service */ 86941309Sbde vector = cy_inb(iobase, CY8_SVCACKM, cy_align); 8706261Sjkh 871136139Sphk com = cy_addr(baseu 8729406Sbde + ((vector >> CD1400_xIVR_CHAN_SHIFT) 8739406Sbde & CD1400_xIVR_CHAN)); 8749406Sbde#endif 8759406Sbde ++com->mdm; 87618901Sdg modem_status = cd_inb(iobase, CD1400_MSVR2, cy_align); 8779406Sbde if (modem_status != com->last_modem_status) { 8789406Sbde /* 8799406Sbde * Schedule high level to handle DCD changes. Note 8809406Sbde * that we don't use the delta bits anywhere. Some 8819406Sbde * UARTs mess them up, and it's easy to remember the 8829406Sbde * previous bits and calculate the delta. 8839406Sbde */ 8849406Sbde com->last_modem_status = modem_status; 8859406Sbde if (!(com->state & CS_CHECKMSR)) { 886136139Sphk cy_events += LOTS_OF_EVENTS; 8879406Sbde com->state |= CS_CHECKMSR; 888136139Sphk swi_sched(cy_fast_ih, 0); 8899406Sbde } 8906261Sjkh 8919406Sbde#ifdef SOFT_CTS_OFLOW 8929406Sbde /* handle CTS change immediately for crisp flow ctl */ 8939406Sbde if (com->state & CS_CTS_OFLOW) { 894136139Sphk if (modem_status & CD1400_MSVR2_CTS) { 8959406Sbde com->state |= CS_ODEVREADY; 8969406Sbde if (com->state >= (CS_BUSY | CS_TTGO 8979406Sbde | CS_ODEVREADY) 8989406Sbde && !(com->intr_enable 8999406Sbde & CD1400_SRER_TXRDY)) 90029047Sbde cd_outb(iobase, CD1400_SRER, 90129047Sbde cy_align, 9029406Sbde com->intr_enable 90341388Sbde = com->intr_enable 90441388Sbde & ~CD1400_SRER_TXMPTY 90541388Sbde | CD1400_SRER_TXRDY); 9069406Sbde } else { 9079406Sbde com->state &= ~CS_ODEVREADY; 90829047Sbde if (com->intr_enable 90929047Sbde & CD1400_SRER_TXRDY) 91029047Sbde cd_outb(iobase, CD1400_SRER, 91129047Sbde cy_align, 9129406Sbde com->intr_enable 91341388Sbde = com->intr_enable 91441388Sbde & ~CD1400_SRER_TXRDY 91541388Sbde | CD1400_SRER_TXMPTY); 9169406Sbde } 9179406Sbde } 9186261Sjkh#endif 9199406Sbde } 9206261Sjkh 9219406Sbde /* terminate service context */ 9226261Sjkh#ifdef PollMode 92318901Sdg cd_outb(iobase, CD1400_MIR, cy_align, 9249406Sbde save_mir 9259406Sbde & ~(CD1400_MIR_RDIREQ | CD1400_MIR_RBUSY)); 9266261Sjkh#else 92718901Sdg cd_outb(iobase, CD1400_EOSRR, cy_align, 0); 9286261Sjkh#endif 9299406Sbde } 9309406Sbde if (status & CD1400_SVRR_TXRDY) { 9319406Sbde struct com_s *com; 9326261Sjkh#ifdef PollMode 9339406Sbde u_char save_tir; 9346261Sjkh#else 9359406Sbde u_char vector; 9366261Sjkh#endif 9376261Sjkh 9386261Sjkh#ifdef PollMode 93918901Sdg save_tir = cd_inb(iobase, CD1400_TIR, cy_align); 9409406Sbde 9419406Sbde /* enter tx service */ 94218901Sdg cd_outb(iobase, CD1400_CAR, cy_align, save_tir); 943136139Sphk cy_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car 94441293Sbde = save_tir & CD1400_CAR_CHAN; 94541293Sbde 946136139Sphk com = cy_addr(baseu 9479406Sbde + cyu * CD1400_NO_OF_CHANNELS 9489406Sbde + (save_tir & CD1400_TIR_CHAN)); 9496261Sjkh#else 9509406Sbde /* ack transmit service */ 95141309Sbde vector = cy_inb(iobase, CY8_SVCACKT, cy_align); 9526261Sjkh 953136139Sphk com = cy_addr(baseu 9549406Sbde + ((vector >> CD1400_xIVR_CHAN_SHIFT) 9559406Sbde & CD1400_xIVR_CHAN)); 9566261Sjkh#endif 9576261Sjkh 95841903Sbde if (com->etc != ETC_NONE) { 95941903Sbde if (com->intr_enable & CD1400_SRER_TXRDY) { 96041903Sbde /* 96141903Sbde * Here due to sloppy SRER_TXRDY 96241903Sbde * enabling. Ignore. Come back when 96341903Sbde * tx is empty. 96441903Sbde */ 96541903Sbde cd_outb(iobase, CD1400_SRER, cy_align, 96641903Sbde com->intr_enable 96743314Sdillon = (com->intr_enable 96843314Sdillon & ~CD1400_SRER_TXRDY) 96941903Sbde | CD1400_SRER_TXMPTY); 97041903Sbde goto terminate_tx_service; 97141903Sbde } 97241903Sbde switch (com->etc) { 97341903Sbde case CD1400_ETC_SENDBREAK: 97441903Sbde case CD1400_ETC_STOPBREAK: 97541903Sbde /* 97641903Sbde * Start the command. Come back on 97741903Sbde * next tx empty interrupt, hopefully 97841903Sbde * after command has been executed. 97941903Sbde */ 98041903Sbde cd_outb(iobase, CD1400_COR2, cy_align, 98141903Sbde com->cor[1] |= CD1400_COR2_ETC); 98241903Sbde cd_outb(iobase, CD1400_TDR, cy_align, 98341903Sbde CD1400_ETC_CMD); 98441903Sbde cd_outb(iobase, CD1400_TDR, cy_align, 98541903Sbde com->etc); 98641903Sbde if (com->etc == CD1400_ETC_SENDBREAK) 98741903Sbde com->etc = ETC_BREAK_STARTING; 98841903Sbde else 98941903Sbde com->etc = ETC_BREAK_ENDING; 99041903Sbde goto terminate_tx_service; 99141903Sbde case ETC_BREAK_STARTING: 99241903Sbde /* 99341903Sbde * BREAK is now on. Continue with 99441903Sbde * SRER_TXMPTY processing, hopefully 99541903Sbde * don't come back. 99641903Sbde */ 99741903Sbde com->etc = ETC_BREAK_STARTED; 99841903Sbde break; 99941903Sbde case ETC_BREAK_STARTED: 100041903Sbde /* 100141903Sbde * Came back due to sloppy SRER_TXMPTY 100241903Sbde * enabling. Hope again. 100341903Sbde */ 100441903Sbde break; 100541903Sbde case ETC_BREAK_ENDING: 100641903Sbde /* 100741903Sbde * BREAK is now off. Continue with 100841903Sbde * SRER_TXMPTY processing and don't 100941903Sbde * come back. The SWI handler will 101041903Sbde * restart tx interrupts if necessary. 101141903Sbde */ 101241903Sbde cd_outb(iobase, CD1400_COR2, cy_align, 101341903Sbde com->cor[1] 101441903Sbde &= ~CD1400_COR2_ETC); 101541903Sbde com->etc = ETC_BREAK_ENDED; 101641903Sbde if (!(com->state & CS_ODONE)) { 1017136139Sphk cy_events += LOTS_OF_EVENTS; 101841903Sbde com->state |= CS_ODONE; 1019136139Sphk swi_sched(cy_fast_ih, 0); 102041903Sbde } 102141903Sbde break; 102241903Sbde case ETC_BREAK_ENDED: 102341903Sbde /* 102441903Sbde * Shouldn't get here. Hope again. 102541903Sbde */ 102641903Sbde break; 102741903Sbde } 102841903Sbde } 102941388Sbde if (com->intr_enable & CD1400_SRER_TXMPTY) { 103041388Sbde if (!(com->extra_state & CSE_ODONE)) { 1031136139Sphk cy_events += LOTS_OF_EVENTS; 103241388Sbde com->extra_state |= CSE_ODONE; 1033136139Sphk swi_sched(cy_fast_ih, 0); 103441388Sbde } 103541388Sbde cd_outb(iobase, CD1400_SRER, cy_align, 103641388Sbde com->intr_enable 103741388Sbde &= ~CD1400_SRER_TXMPTY); 103841388Sbde goto terminate_tx_service; 103941388Sbde } 10409406Sbde if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { 10419406Sbde u_char *ioptr; 10429406Sbde u_int ocount; 10436261Sjkh 10449406Sbde ioptr = com->obufq.l_head; 10459406Sbde ocount = com->obufq.l_tail - ioptr; 10469406Sbde if (ocount > CD1400_TX_FIFO_SIZE) 10479406Sbde ocount = CD1400_TX_FIFO_SIZE; 10489406Sbde com->bytes_out += ocount; 10499406Sbde do 105029047Sbde cd_outb(iobase, CD1400_TDR, cy_align, 105129047Sbde *ioptr++); 10529406Sbde while (--ocount != 0); 10539406Sbde com->obufq.l_head = ioptr; 10549406Sbde if (ioptr >= com->obufq.l_tail) { 10559406Sbde struct lbq *qp; 10566261Sjkh 10579406Sbde qp = com->obufq.l_next; 10589406Sbde qp->l_queued = FALSE; 10599406Sbde qp = qp->l_next; 10609406Sbde if (qp != NULL) { 10619406Sbde com->obufq.l_head = qp->l_head; 10629406Sbde com->obufq.l_tail = qp->l_tail; 10639406Sbde com->obufq.l_next = qp; 10649406Sbde } else { 10659406Sbde /* output just completed */ 10669406Sbde com->state &= ~CS_BUSY; 106741388Sbde 106841388Sbde /* 106941388Sbde * The setting of CSE_ODONE may be 107041388Sbde * stale here. We currently only 107141388Sbde * use it when CS_BUSY is set, and 107241388Sbde * fixing it when we clear CS_BUSY 107341388Sbde * is easiest. 107441388Sbde */ 107541388Sbde if (com->extra_state & CSE_ODONE) { 1076136139Sphk cy_events -= LOTS_OF_EVENTS; 107741388Sbde com->extra_state &= ~CSE_ODONE; 107841388Sbde } 107941388Sbde 108018901Sdg cd_outb(iobase, CD1400_SRER, cy_align, 10819406Sbde com->intr_enable 108243314Sdillon = (com->intr_enable 108343314Sdillon & ~CD1400_SRER_TXRDY) 108441388Sbde | CD1400_SRER_TXMPTY); 10859406Sbde } 10869406Sbde if (!(com->state & CS_ODONE)) { 1087136139Sphk cy_events += LOTS_OF_EVENTS; 10889406Sbde com->state |= CS_ODONE; 108929047Sbde 109029047Sbde /* handle at high level ASAP */ 1091136139Sphk swi_sched(cy_fast_ih, 0); 10929406Sbde } 10939406Sbde } 10949406Sbde } 10956261Sjkh 10969406Sbde /* terminate service context */ 109741388Sbdeterminate_tx_service: 10986261Sjkh#ifdef PollMode 109918901Sdg cd_outb(iobase, CD1400_TIR, cy_align, 11009406Sbde save_tir 11019406Sbde & ~(CD1400_TIR_RDIREQ | CD1400_TIR_RBUSY)); 11026261Sjkh#else 110318901Sdg cd_outb(iobase, CD1400_EOSRR, cy_align, 0); 11046261Sjkh#endif 11059406Sbde } 11066261Sjkh } 11076261Sjkh 11089406Sbde /* ensure an edge for the next interrupt */ 110941309Sbde cy_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0); 11106261Sjkh 1111136139Sphk swi_sched(cy_slow_ih, SWI_DELAY); 111228921Sfsmp 111372262Sjhb COM_UNLOCK(); 1114166901Spiso return (FILTER_HANDLED); 11159406Sbde} 11166261Sjkh 1117136139Sphkstatic void 1118136139Sphkcybreak(struct tty *tp, int sig) 11196261Sjkh{ 11209406Sbde struct com_s *com; 11216261Sjkh 1122136139Sphk com = tp->t_sc; 1123136139Sphk if (sig) 112441903Sbde cd_etc(com, CD1400_ETC_SENDBREAK); 1125136139Sphk else 112641903Sbde cd_etc(com, CD1400_ETC_STOPBREAK); 11279406Sbde} 11286261Sjkh 112938246Sbdestatic void 1130136139Sphkcypoll(void *arg) 11319406Sbde{ 11329406Sbde int unit; 11336261Sjkh 11349406Sbde#ifdef CyDebug 11359406Sbde ++cy_timeouts; 11369406Sbde#endif 1137136139Sphk if (cy_events == 0) 11389406Sbde return; 11399406Sbderepeat: 1140136139Sphk for (unit = 0; unit < NPORTS; ++unit) { 11419406Sbde struct com_s *com; 11429406Sbde int incc; 11439406Sbde struct tty *tp; 11446261Sjkh 1145136139Sphk com = cy_addr(unit); 11469406Sbde if (com == NULL) 11479406Sbde continue; 11489406Sbde tp = com->tp; 11499406Sbde if (tp == NULL) { 11509406Sbde /* 11519406Sbde * XXX forget any events related to closed devices 11529406Sbde * (actually never opened devices) so that we don't 11539406Sbde * loop. 11549406Sbde */ 115588088Sjhb critical_enter(); 115672262Sjhb COM_LOCK(); 11579406Sbde incc = com->iptr - com->ibuf; 11589406Sbde com->iptr = com->ibuf; 11599406Sbde if (com->state & CS_CHECKMSR) { 11609406Sbde incc += LOTS_OF_EVENTS; 11619406Sbde com->state &= ~CS_CHECKMSR; 11629406Sbde } 1163136139Sphk cy_events -= incc; 116472262Sjhb COM_UNLOCK(); 116588088Sjhb critical_exit(); 11669406Sbde if (incc != 0) 11679406Sbde log(LOG_DEBUG, 1168136139Sphk "cy%d: %d events for device with no tp\n", 11699406Sbde unit, incc); 11709406Sbde continue; 11719406Sbde } 117243611Sbde if (com->iptr != com->ibuf) { 117388088Sjhb critical_enter(); 117472262Sjhb COM_LOCK(); 1175136139Sphk cyinput(com); 117672262Sjhb COM_UNLOCK(); 117788088Sjhb critical_exit(); 11789406Sbde } 11799406Sbde if (com->state & CS_CHECKMSR) { 11809406Sbde u_char delta_modem_status; 11816261Sjkh 118288088Sjhb critical_enter(); 118372262Sjhb COM_LOCK(); 1184136139Sphk cyinput(com); 11859406Sbde delta_modem_status = com->last_modem_status 11869406Sbde ^ com->prev_modem_status; 11879406Sbde com->prev_modem_status = com->last_modem_status; 1188136139Sphk cy_events -= LOTS_OF_EVENTS; 11899406Sbde com->state &= ~CS_CHECKMSR; 119072262Sjhb COM_UNLOCK(); 119188088Sjhb critical_exit(); 1192136139Sphk if (delta_modem_status & CD1400_MSVR2_CD) 1193130095Sphk ttyld_modem(tp, 1194136139Sphk com->prev_modem_status & CD1400_MSVR2_CD); 11959406Sbde } 119641388Sbde if (com->extra_state & CSE_ODONE) { 119788088Sjhb critical_enter(); 119872262Sjhb COM_LOCK(); 1199136139Sphk cy_events -= LOTS_OF_EVENTS; 120041388Sbde com->extra_state &= ~CSE_ODONE; 120172262Sjhb COM_UNLOCK(); 120288088Sjhb critical_exit(); 120341388Sbde if (!(com->state & CS_BUSY)) { 120441388Sbde tp->t_state &= ~TS_BUSY; 120541388Sbde ttwwakeup(com->tp); 120641388Sbde } 120741903Sbde if (com->etc != ETC_NONE) { 120841903Sbde if (com->etc == ETC_BREAK_ENDED) 120941903Sbde com->etc = ETC_NONE; 121041903Sbde wakeup(&com->etc); 121141903Sbde } 121241388Sbde } 121341903Sbde if (com->state & CS_ODONE) { 121488088Sjhb critical_enter(); 121572262Sjhb COM_LOCK(); 1216136139Sphk cy_events -= LOTS_OF_EVENTS; 121741903Sbde com->state &= ~CS_ODONE; 121872262Sjhb COM_UNLOCK(); 121988088Sjhb critical_exit(); 1220130077Sphk ttyld_start(tp); 122141903Sbde } 1222136139Sphk if (cy_events == 0) 12239406Sbde break; 12249406Sbde } 1225136139Sphk if (cy_events >= LOTS_OF_EVENTS) 12269406Sbde goto repeat; 12279406Sbde} 12286261Sjkh 12299406Sbdestatic int 1230136139Sphkcyparam(struct tty *tp, struct termios *t) 12319406Sbde{ 12329406Sbde int bits; 12339406Sbde int cflag; 12349406Sbde struct com_s *com; 12359406Sbde u_char cor_change; 123638303Sbde u_long cy_clock; 12379406Sbde int idivisor; 12389406Sbde int iflag; 12399406Sbde int iprescaler; 12409406Sbde int itimeout; 12419406Sbde int odivisor; 12429406Sbde int oprescaler; 12439406Sbde u_char opt; 12449406Sbde int s; 12456261Sjkh 1246136139Sphk com = tp->t_sc; 124738302Sbde 12489406Sbde /* check requested parameters */ 124938303Sbde cy_clock = CY_CLOCK(com->gfrcr_image); 1250136139Sphk idivisor = cyspeed(t->c_ispeed, cy_clock, &iprescaler); 1251120512Sbde if (idivisor <= 0) 12529406Sbde return (EINVAL); 1253136139Sphk odivisor = cyspeed(t->c_ospeed != 0 ? t->c_ospeed : tp->t_ospeed, 1254120512Sbde cy_clock, &oprescaler); 1255120512Sbde if (odivisor <= 0) 12569406Sbde return (EINVAL); 12576261Sjkh 12589406Sbde /* parameters are OK, convert them to the com struct and the device */ 125938303Sbde s = spltty(); 1260120512Sbde if (t->c_ospeed == 0) 1261136139Sphk (void)cymodem(tp, 0, SER_DTR); 12629406Sbde else 1263136139Sphk (void)cymodem(tp, SER_DTR, 0); 12646261Sjkh 1265136139Sphk (void) cysetwater(com, t->c_ispeed); 126643611Sbde 126743611Sbde /* XXX we don't actually change the speed atomically. */ 126843611Sbde 1269120512Sbde cd_setreg(com, CD1400_RBPR, idivisor); 1270120512Sbde cd_setreg(com, CD1400_RCOR, iprescaler); 1271120512Sbde cd_setreg(com, CD1400_TBPR, odivisor); 1272120512Sbde cd_setreg(com, CD1400_TCOR, oprescaler); 12736261Sjkh 12746261Sjkh /* 12756261Sjkh * channel control 12766261Sjkh * receiver enable 12776261Sjkh * transmitter enable (always set) 12786261Sjkh */ 12799406Sbde cflag = t->c_cflag; 12809406Sbde opt = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN 12819406Sbde | (cflag & CREAD ? CD1400_CCR_RCVEN : CD1400_CCR_RCVDIS); 12829406Sbde if (opt != com->channel_control) { 12839406Sbde com->channel_control = opt; 128441293Sbde cd1400_channel_cmd(com, opt); 12856261Sjkh } 12866261Sjkh 12876261Sjkh#ifdef Smarts 12886261Sjkh /* set special chars */ 12899406Sbde /* XXX if one is _POSIX_VDISABLE, can't use some others */ 12909406Sbde if (t->c_cc[VSTOP] != _POSIX_VDISABLE) 129141293Sbde cd_setreg(com, CD1400_SCHR1, t->c_cc[VSTOP]); 12929406Sbde if (t->c_cc[VSTART] != _POSIX_VDISABLE) 129341293Sbde cd_setreg(com, CD1400_SCHR2, t->c_cc[VSTART]); 12949406Sbde if (t->c_cc[VINTR] != _POSIX_VDISABLE) 129541293Sbde cd_setreg(com, CD1400_SCHR3, t->c_cc[VINTR]); 12969406Sbde if (t->c_cc[VSUSP] != _POSIX_VDISABLE) 129741293Sbde cd_setreg(com, CD1400_SCHR4, t->c_cc[VSUSP]); 12986261Sjkh#endif 12996261Sjkh 13006261Sjkh /* 13016261Sjkh * set channel option register 1 - 13026261Sjkh * parity mode 13036261Sjkh * stop bits 13046261Sjkh * char length 13056261Sjkh */ 13066261Sjkh opt = 0; 13076261Sjkh /* parity */ 13086261Sjkh if (cflag & PARENB) { 13099406Sbde if (cflag & PARODD) 13109406Sbde opt |= CD1400_COR1_PARODD; 13119406Sbde opt |= CD1400_COR1_PARNORMAL; 13126261Sjkh } 13139406Sbde iflag = t->c_iflag; 13146261Sjkh if (!(iflag & INPCK)) 13159406Sbde opt |= CD1400_COR1_NOINPCK; 13169406Sbde bits = 1 + 1; 13176261Sjkh /* stop bits */ 13189406Sbde if (cflag & CSTOPB) { 13199406Sbde ++bits; 13209406Sbde opt |= CD1400_COR1_STOP2; 13219406Sbde } 13226261Sjkh /* char length */ 13239406Sbde switch (cflag & CSIZE) { 13249406Sbde case CS5: 13259406Sbde bits += 5; 13269406Sbde opt |= CD1400_COR1_CS5; 13279406Sbde break; 13289406Sbde case CS6: 13299406Sbde bits += 6; 13309406Sbde opt |= CD1400_COR1_CS6; 13319406Sbde break; 13329406Sbde case CS7: 13339406Sbde bits += 7; 13349406Sbde opt |= CD1400_COR1_CS7; 13359406Sbde break; 13369406Sbde default: 13379406Sbde bits += 8; 13389406Sbde opt |= CD1400_COR1_CS8; 13399406Sbde break; 13406261Sjkh } 13419406Sbde cor_change = 0; 13429406Sbde if (opt != com->cor[0]) { 13439406Sbde cor_change |= CD1400_CCR_COR1; 134441293Sbde cd_setreg(com, CD1400_COR1, com->cor[0] = opt); 13459406Sbde } 13466261Sjkh 13476261Sjkh /* 13489406Sbde * Set receive time-out period, normally to max(one char time, 5 ms). 13499406Sbde */ 1350120512Sbde itimeout = (1000 * bits + t->c_ispeed - 1) / t->c_ispeed; 13519406Sbde#ifdef SOFT_HOTCHAR 13529406Sbde#define MIN_RTP 1 13539406Sbde#else 13549406Sbde#define MIN_RTP 5 13559406Sbde#endif 1356120512Sbde if (itimeout < MIN_RTP) 1357120512Sbde itimeout = MIN_RTP; 13589406Sbde if (!(t->c_lflag & ICANON) && t->c_cc[VMIN] != 0 && t->c_cc[VTIME] != 0 13599406Sbde && t->c_cc[VTIME] * 10 > itimeout) 13609406Sbde itimeout = t->c_cc[VTIME] * 10; 13619406Sbde if (itimeout > 255) 13629406Sbde itimeout = 255; 136341293Sbde cd_setreg(com, CD1400_RTPR, itimeout); 13649406Sbde 13659406Sbde /* 13666261Sjkh * set channel option register 2 - 13676261Sjkh * flow control 13686261Sjkh */ 13696261Sjkh opt = 0; 13706261Sjkh#ifdef Smarts 13716261Sjkh if (iflag & IXANY) 13729406Sbde opt |= CD1400_COR2_IXANY; 13736261Sjkh if (iflag & IXOFF) 13749406Sbde opt |= CD1400_COR2_IXOFF; 13756261Sjkh#endif 13769406Sbde#ifndef SOFT_CTS_OFLOW 13776261Sjkh if (cflag & CCTS_OFLOW) 13789406Sbde opt |= CD1400_COR2_CCTS_OFLOW; 13796261Sjkh#endif 138088088Sjhb critical_enter(); 138172262Sjhb COM_LOCK(); 13829406Sbde if (opt != com->cor[1]) { 13839406Sbde cor_change |= CD1400_CCR_COR2; 138441293Sbde cd_setreg(com, CD1400_COR2, com->cor[1] = opt); 13856261Sjkh } 138672262Sjhb COM_UNLOCK(); 138788088Sjhb critical_exit(); 13886261Sjkh 13896261Sjkh /* 13906261Sjkh * set channel option register 3 - 13916261Sjkh * receiver FIFO interrupt threshold 13926261Sjkh * flow control 13936261Sjkh */ 13949406Sbde opt = RxFifoThreshold; 13956261Sjkh#ifdef Smarts 13966261Sjkh if (t->c_lflag & ICANON) 13979406Sbde opt |= CD1400_COR3_SCD34; /* detect INTR & SUSP chars */ 13986261Sjkh if (iflag & IXOFF) 13999406Sbde /* detect and transparently handle START and STOP chars */ 14009406Sbde opt |= CD1400_COR3_FCT | CD1400_COR3_SCD12; 14016261Sjkh#endif 14029406Sbde if (opt != com->cor[2]) { 14039406Sbde cor_change |= CD1400_CCR_COR3; 140441293Sbde cd_setreg(com, CD1400_COR3, com->cor[2] = opt); 14056261Sjkh } 14066261Sjkh 14076261Sjkh /* notify the CD1400 if COR1-3 have changed */ 14089406Sbde if (cor_change) 140941293Sbde cd1400_channel_cmd(com, CD1400_CCR_CMDCORCHG | cor_change); 14106261Sjkh 14116261Sjkh /* 14126261Sjkh * set channel option register 4 - 14136261Sjkh * CR/NL processing 14146261Sjkh * break processing 14156261Sjkh * received exception processing 14166261Sjkh */ 14176261Sjkh opt = 0; 14186261Sjkh if (iflag & IGNCR) 14199406Sbde opt |= CD1400_COR4_IGNCR; 14206261Sjkh#ifdef Smarts 14216261Sjkh /* 14226261Sjkh * we need a new ttyinput() for this, as we don't want to 14236261Sjkh * have ICRNL && INLCR being done in both layers, or to have 14246261Sjkh * synchronisation problems 14256261Sjkh */ 14266261Sjkh if (iflag & ICRNL) 14279406Sbde opt |= CD1400_COR4_ICRNL; 14286261Sjkh if (iflag & INLCR) 14299406Sbde opt |= CD1400_COR4_INLCR; 14306261Sjkh#endif 14316261Sjkh if (iflag & IGNBRK) 143241905Sbde opt |= CD1400_COR4_IGNBRK | CD1400_COR4_NOBRKINT; 143341905Sbde /* 143441905Sbde * The `-ignbrk -brkint parmrk' case is not handled by the hardware, 143541905Sbde * so only tell the hardware about -brkint if -parmrk. 143641905Sbde */ 143741905Sbde if (!(iflag & (BRKINT | PARMRK))) 14389406Sbde opt |= CD1400_COR4_NOBRKINT; 14399406Sbde#if 0 14409406Sbde /* XXX using this "intelligence" breaks reporting of overruns. */ 14416261Sjkh if (iflag & IGNPAR) 14429406Sbde opt |= CD1400_COR4_PFO_DISCARD; 14436261Sjkh else { 14449406Sbde if (iflag & PARMRK) 14459406Sbde opt |= CD1400_COR4_PFO_ESC; 14469406Sbde else 14479406Sbde opt |= CD1400_COR4_PFO_NUL; 14489406Sbde } 14496261Sjkh#else 14509406Sbde opt |= CD1400_COR4_PFO_EXCEPTION; 14516261Sjkh#endif 145241293Sbde cd_setreg(com, CD1400_COR4, opt); 14536261Sjkh 14546261Sjkh /* 14556261Sjkh * set channel option register 5 - 14566261Sjkh */ 14576261Sjkh opt = 0; 14586261Sjkh if (iflag & ISTRIP) 14599406Sbde opt |= CD1400_COR5_ISTRIP; 14609406Sbde if (t->c_iflag & IEXTEN) 14619406Sbde /* enable LNEXT (e.g. ctrl-v quoting) handling */ 14629406Sbde opt |= CD1400_COR5_LNEXT; 14636261Sjkh#ifdef Smarts 14646261Sjkh if (t->c_oflag & ONLCR) 14659406Sbde opt |= CD1400_COR5_ONLCR; 14666261Sjkh if (t->c_oflag & OCRNL) 14679406Sbde opt |= CD1400_COR5_OCRNL; 14686261Sjkh#endif 146941293Sbde cd_setreg(com, CD1400_COR5, opt); 14706261Sjkh 14716261Sjkh /* 147220152Sbde * We always generate modem status change interrupts for CD changes. 147320152Sbde * Among other things, this is necessary to track TS_CARR_ON for 147420152Sbde * pstat to print even when the driver doesn't care. CD changes 147520152Sbde * should be rare so interrupts for them are not worth extra code to 147620152Sbde * avoid. We avoid interrupts for other modem status changes (except 147720152Sbde * for CTS changes when SOFT_CTS_OFLOW is configured) since this is 147820152Sbde * simplest and best. 14799406Sbde */ 148020152Sbde 14819406Sbde /* 14826261Sjkh * set modem change option register 1 14836261Sjkh * generate modem interrupts on which 1 -> 0 input transitions 14846261Sjkh * also controls auto-DTR output flow-control, which we don't use 14856261Sjkh */ 148620152Sbde opt = CD1400_MCOR1_CDzd; 14879406Sbde#ifdef SOFT_CTS_OFLOW 14889406Sbde if (cflag & CCTS_OFLOW) 14899406Sbde opt |= CD1400_MCOR1_CTSzd; 14909406Sbde#endif 149141293Sbde cd_setreg(com, CD1400_MCOR1, opt); 14926261Sjkh 14936261Sjkh /* 14946261Sjkh * set modem change option register 2 14956261Sjkh * generate modem interrupts on specific 0 -> 1 input transitions 14966261Sjkh */ 149720152Sbde opt = CD1400_MCOR2_CDod; 14989406Sbde#ifdef SOFT_CTS_OFLOW 14999406Sbde if (cflag & CCTS_OFLOW) 15009406Sbde opt |= CD1400_MCOR2_CTSod; 15019406Sbde#endif 150241293Sbde cd_setreg(com, CD1400_MCOR2, opt); 15036261Sjkh 15049406Sbde /* 15059406Sbde * XXX should have done this long ago, but there is too much state 15069406Sbde * to change all atomically. 15079406Sbde */ 150888088Sjhb critical_enter(); 150972262Sjhb COM_LOCK(); 15106261Sjkh 15119406Sbde com->state &= ~CS_TTGO; 15129406Sbde if (!(tp->t_state & TS_TTSTOP)) 15139406Sbde com->state |= CS_TTGO; 151419718Sbde if (cflag & CRTS_IFLOW) { 151519718Sbde com->state |= CS_RTS_IFLOW; 151619718Sbde /* 151719718Sbde * If CS_RTS_IFLOW just changed from off to on, the change 1518136139Sphk * needs to be propagated to CD1400_MSVR1_RTS. This isn't urgent, 1519136139Sphk * so do it later by calling cystart() instead of repeating 1520136139Sphk * a lot of code from cystart() here. 152119718Sbde */ 152219718Sbde } else if (com->state & CS_RTS_IFLOW) { 15239406Sbde com->state &= ~CS_RTS_IFLOW; 152419718Sbde /* 1525136139Sphk * CS_RTS_IFLOW just changed from on to off. Force CD1400_MSVR1_RTS 1526136139Sphk * on here, since cystart() won't do it later. 152719718Sbde */ 152841293Sbde cd_setreg(com, com->mcr_rts_reg, 152941293Sbde com->mcr_image |= com->mcr_rts); 153019718Sbde } 15316261Sjkh 15329406Sbde /* 15339406Sbde * Set up state to handle output flow control. 15349406Sbde * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? 15359406Sbde * Now has 10+ msec latency, while CTS flow has 50- usec latency. 15369406Sbde */ 15379406Sbde com->state |= CS_ODEVREADY; 15389406Sbde#ifdef SOFT_CTS_OFLOW 15399406Sbde com->state &= ~CS_CTS_OFLOW; 15409406Sbde if (cflag & CCTS_OFLOW) { 15419406Sbde com->state |= CS_CTS_OFLOW; 1542136139Sphk if (!(com->last_modem_status & CD1400_MSVR2_CTS)) 15439406Sbde com->state &= ~CS_ODEVREADY; 15449406Sbde } 15459406Sbde#endif 15469406Sbde /* XXX shouldn't call functions while intrs are disabled. */ 15479406Sbde disc_optim(tp, t, com); 15489406Sbde#if 0 15499406Sbde /* 1550136139Sphk * Recover from fiddling with CS_TTGO. We used to call cyintr1() 15519406Sbde * unconditionally, but that defeated the careful discarding of 1552136139Sphk * stale input in cyopen(). 15539406Sbde */ 15549406Sbde if (com->state >= (CS_BUSY | CS_TTGO)) 1555136139Sphk cyintr1(com); 15569406Sbde#endif 15579406Sbde if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { 15589406Sbde if (!(com->intr_enable & CD1400_SRER_TXRDY)) 155941293Sbde cd_setreg(com, CD1400_SRER, 156041388Sbde com->intr_enable 156143314Sdillon = (com->intr_enable & ~CD1400_SRER_TXMPTY) 156241388Sbde | CD1400_SRER_TXRDY); 15639406Sbde } else { 15649406Sbde if (com->intr_enable & CD1400_SRER_TXRDY) 156541293Sbde cd_setreg(com, CD1400_SRER, 156641388Sbde com->intr_enable 156743314Sdillon = (com->intr_enable & ~CD1400_SRER_TXRDY) 156841388Sbde | CD1400_SRER_TXMPTY); 15699406Sbde } 15706261Sjkh 157172262Sjhb COM_UNLOCK(); 157288088Sjhb critical_exit(); 15739406Sbde splx(s); 1574136139Sphk cystart(tp); 157543611Sbde if (com->ibufold != NULL) { 157643611Sbde free(com->ibufold, M_DEVBUF); 157743611Sbde com->ibufold = NULL; 157843611Sbde } 15799406Sbde return (0); 15809406Sbde} 15819406Sbde 158243611Sbdestatic int 1583136139Sphkcysetwater(struct com_s *com, speed_t speed) 158443611Sbde{ 158543611Sbde int cp4ticks; 158643611Sbde u_char *ibuf; 158743611Sbde int ibufsize; 158843611Sbde struct tty *tp; 158943611Sbde 159043611Sbde /* 159143611Sbde * Make the buffer size large enough to handle a softtty interrupt 159243611Sbde * latency of about 2 ticks without loss of throughput or data 159343611Sbde * (about 3 ticks if input flow control is not used or not honoured, 159443611Sbde * but a bit less for CS5-CS7 modes). 159543611Sbde */ 159643611Sbde cp4ticks = speed / 10 / hz * 4; 159743611Sbde for (ibufsize = 128; ibufsize < cp4ticks;) 159843611Sbde ibufsize <<= 1; 159943611Sbde if (ibufsize == com->ibufsize) { 160043611Sbde return (0); 160143611Sbde } 160243611Sbde 160343611Sbde /* 160443611Sbde * Allocate input buffer. The extra factor of 2 in the size is 160543611Sbde * to allow for an error byte for each input byte. 160643611Sbde */ 160743611Sbde ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT); 160843611Sbde if (ibuf == NULL) { 160943611Sbde return (ENOMEM); 161043611Sbde } 161143611Sbde 161243611Sbde /* Initialize non-critical variables. */ 161343611Sbde com->ibufold = com->ibuf; 161443611Sbde com->ibufsize = ibufsize; 161543611Sbde tp = com->tp; 161643611Sbde if (tp != NULL) { 161743611Sbde tp->t_ififosize = 2 * ibufsize; 161843611Sbde tp->t_ispeedwat = (speed_t)-1; 161943611Sbde tp->t_ospeedwat = (speed_t)-1; 162043611Sbde } 162143611Sbde 162243611Sbde /* 162343611Sbde * Read current input buffer, if any. Continue with interrupts 162443611Sbde * disabled. 162543611Sbde */ 162688088Sjhb critical_enter(); 162772262Sjhb COM_LOCK(); 162843611Sbde if (com->iptr != com->ibuf) 1629136139Sphk cyinput(com); 163043611Sbde 163143611Sbde /*- 163243611Sbde * Initialize critical variables, including input buffer watermarks. 163343611Sbde * The external device is asked to stop sending when the buffer 163443611Sbde * exactly reaches high water, or when the high level requests it. 163543611Sbde * The high level is notified immediately (rather than at a later 163643611Sbde * clock tick) when this watermark is reached. 163743611Sbde * The buffer size is chosen so the watermark should almost never 163843611Sbde * be reached. 163943611Sbde * The low watermark is invisibly 0 since the buffer is always 164043611Sbde * emptied all at once. 164143611Sbde */ 164243611Sbde com->iptr = com->ibuf = ibuf; 164343611Sbde com->ibufend = ibuf + ibufsize; 164443611Sbde com->ierroff = ibufsize; 164543611Sbde com->ihighwater = ibuf + 3 * ibufsize / 4; 164665557Sjasone 164772262Sjhb COM_UNLOCK(); 164888088Sjhb critical_exit(); 164943611Sbde return (0); 165043611Sbde} 165143611Sbde 16529406Sbdestatic void 1653136139Sphkcystart(struct tty *tp) 16546261Sjkh{ 16559406Sbde struct com_s *com; 16566261Sjkh int s; 16579406Sbde#ifdef CyDebug 16589406Sbde bool_t started; 16599406Sbde#endif 16606261Sjkh 1661136139Sphk com = tp->t_sc; 16629406Sbde s = spltty(); 16639406Sbde 16646261Sjkh#ifdef CyDebug 16659406Sbde ++com->start_count; 16669406Sbde started = FALSE; 16676261Sjkh#endif 16686261Sjkh 166988088Sjhb critical_enter(); 167072262Sjhb COM_LOCK(); 16719406Sbde if (tp->t_state & TS_TTSTOP) { 16729406Sbde com->state &= ~CS_TTGO; 16739406Sbde if (com->intr_enable & CD1400_SRER_TXRDY) 167441293Sbde cd_setreg(com, CD1400_SRER, 167541388Sbde com->intr_enable 167643314Sdillon = (com->intr_enable & ~CD1400_SRER_TXRDY) 167741388Sbde | CD1400_SRER_TXMPTY); 16789406Sbde } else { 16799406Sbde com->state |= CS_TTGO; 16809406Sbde if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) 16819406Sbde && !(com->intr_enable & CD1400_SRER_TXRDY)) 168241293Sbde cd_setreg(com, CD1400_SRER, 168341388Sbde com->intr_enable 168443314Sdillon = (com->intr_enable & ~CD1400_SRER_TXMPTY) 168541388Sbde | CD1400_SRER_TXRDY); 16869406Sbde } 16879406Sbde if (tp->t_state & TS_TBLOCK) { 168838303Sbde if (com->mcr_image & com->mcr_rts && com->state & CS_RTS_IFLOW) 16899406Sbde#if 0 1690136139Sphk outb(com->modem_ctl_port, com->mcr_image &= ~CD1400_MSVR1_RTS); 16919406Sbde#else 169241293Sbde cd_setreg(com, com->mcr_rts_reg, 169341293Sbde com->mcr_image &= ~com->mcr_rts); 16949406Sbde#endif 16959406Sbde } else { 169638303Sbde if (!(com->mcr_image & com->mcr_rts) 169738302Sbde && com->iptr < com->ihighwater 169819718Sbde && com->state & CS_RTS_IFLOW) 16999406Sbde#if 0 1700136139Sphk outb(com->modem_ctl_port, com->mcr_image |= CD1400_MSVR1_RTS); 17019406Sbde#else 170241293Sbde cd_setreg(com, com->mcr_rts_reg, 170341293Sbde com->mcr_image |= com->mcr_rts); 17049406Sbde#endif 17059406Sbde } 170672262Sjhb COM_UNLOCK(); 170788088Sjhb critical_exit(); 17089406Sbde if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { 170932045Sbde ttwwakeup(tp); 17109406Sbde splx(s); 17116261Sjkh return; 17129406Sbde } 17139406Sbde if (tp->t_outq.c_cc != 0) { 17149406Sbde struct lbq *qp; 17159406Sbde struct lbq *next; 17166261Sjkh 17179406Sbde if (!com->obufs[0].l_queued) { 17189406Sbde#ifdef CyDebug 17199406Sbde started = TRUE; 17209406Sbde#endif 17219406Sbde com->obufs[0].l_tail 17229406Sbde = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, 17239406Sbde sizeof com->obuf1); 17249406Sbde com->obufs[0].l_next = NULL; 17259406Sbde com->obufs[0].l_queued = TRUE; 172688088Sjhb critical_enter(); 172772262Sjhb COM_LOCK(); 17289406Sbde if (com->state & CS_BUSY) { 17299406Sbde qp = com->obufq.l_next; 17309406Sbde while ((next = qp->l_next) != NULL) 17319406Sbde qp = next; 17329406Sbde qp->l_next = &com->obufs[0]; 17339406Sbde } else { 17349406Sbde com->obufq.l_head = com->obufs[0].l_head; 17359406Sbde com->obufq.l_tail = com->obufs[0].l_tail; 17369406Sbde com->obufq.l_next = &com->obufs[0]; 17379406Sbde com->state |= CS_BUSY; 17389406Sbde if (com->state >= (CS_BUSY | CS_TTGO 17399406Sbde | CS_ODEVREADY)) 174041293Sbde cd_setreg(com, CD1400_SRER, 174141293Sbde com->intr_enable 174243314Sdillon = (com->intr_enable 174343314Sdillon & ~CD1400_SRER_TXMPTY) 174441388Sbde | CD1400_SRER_TXRDY); 17459406Sbde } 174672262Sjhb COM_UNLOCK(); 174788088Sjhb critical_exit(); 17489406Sbde } 17499406Sbde if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { 17509406Sbde#ifdef CyDebug 17519406Sbde started = TRUE; 17529406Sbde#endif 17539406Sbde com->obufs[1].l_tail 17549406Sbde = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, 17559406Sbde sizeof com->obuf2); 17569406Sbde com->obufs[1].l_next = NULL; 17579406Sbde com->obufs[1].l_queued = TRUE; 175888088Sjhb critical_enter(); 175972262Sjhb COM_LOCK(); 17609406Sbde if (com->state & CS_BUSY) { 17619406Sbde qp = com->obufq.l_next; 17629406Sbde while ((next = qp->l_next) != NULL) 17639406Sbde qp = next; 17649406Sbde qp->l_next = &com->obufs[1]; 17659406Sbde } else { 17669406Sbde com->obufq.l_head = com->obufs[1].l_head; 17679406Sbde com->obufq.l_tail = com->obufs[1].l_tail; 17689406Sbde com->obufq.l_next = &com->obufs[1]; 17699406Sbde com->state |= CS_BUSY; 17709406Sbde if (com->state >= (CS_BUSY | CS_TTGO 17719406Sbde | CS_ODEVREADY)) 177241293Sbde cd_setreg(com, CD1400_SRER, 177341293Sbde com->intr_enable 177443314Sdillon = (com->intr_enable 177543314Sdillon & ~CD1400_SRER_TXMPTY) 177641388Sbde | CD1400_SRER_TXRDY); 17779406Sbde } 177872262Sjhb COM_UNLOCK(); 177988088Sjhb critical_exit(); 17809406Sbde } 17819406Sbde tp->t_state |= TS_BUSY; 17829406Sbde } 17839406Sbde#ifdef CyDebug 17849406Sbde if (started) 17859406Sbde ++com->start_real; 17869406Sbde#endif 17879406Sbde#if 0 178888088Sjhb critical_enter(); 178972262Sjhb COM_LOCK(); 179012962Sbde if (com->state >= (CS_BUSY | CS_TTGO)) 1791136139Sphk cyintr1(com); /* fake interrupt to start output */ 179272262Sjhb COM_UNLOCK(); 179388088Sjhb critical_exit(); 17949406Sbde#endif 17959626Sbde ttwwakeup(tp); 17969406Sbde splx(s); 17979406Sbde} 17986261Sjkh 179912675Sjulianstatic void 1800136139Sphkcomstop(struct tty *tp, int rw) 18019406Sbde{ 18029406Sbde struct com_s *com; 180341903Sbde bool_t wakeup_etc; 18046261Sjkh 1805136139Sphk com = tp->t_sc; 180641903Sbde wakeup_etc = FALSE; 180788088Sjhb critical_enter(); 180872262Sjhb COM_LOCK(); 18099406Sbde if (rw & FWRITE) { 18109406Sbde com->obufs[0].l_queued = FALSE; 18119406Sbde com->obufs[1].l_queued = FALSE; 181241388Sbde if (com->extra_state & CSE_ODONE) { 1813136139Sphk cy_events -= LOTS_OF_EVENTS; 181441388Sbde com->extra_state &= ~CSE_ODONE; 181541903Sbde if (com->etc != ETC_NONE) { 181641903Sbde if (com->etc == ETC_BREAK_ENDED) 181741903Sbde com->etc = ETC_NONE; 181841903Sbde wakeup_etc = TRUE; 181941903Sbde } 182041388Sbde } 18219406Sbde com->tp->t_state &= ~TS_BUSY; 182241903Sbde if (com->state & CS_ODONE) 1823136139Sphk cy_events -= LOTS_OF_EVENTS; 182441903Sbde com->state &= ~(CS_ODONE | CS_BUSY); 18259406Sbde } 18269406Sbde if (rw & FREAD) { 182741908Sbde /* XXX no way to reset only input fifo. */ 1828136139Sphk cy_events -= (com->iptr - com->ibuf); 18299406Sbde com->iptr = com->ibuf; 18309406Sbde } 183172262Sjhb COM_UNLOCK(); 183288088Sjhb critical_exit(); 183341903Sbde if (wakeup_etc) 183441903Sbde wakeup(&com->etc); 183541908Sbde if (rw & FWRITE && com->etc == ETC_NONE) 183641908Sbde cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_FTF); 1837136139Sphk cystart(tp); 18389406Sbde} 18399406Sbde 18409406Sbdestatic int 1841136139Sphkcymodem(struct tty *tp, int sigon, int sigoff) 1842136139Sphk{ 18439406Sbde struct com_s *com; 18449406Sbde int mcr; 18459406Sbde int msr; 18469406Sbde 1847136139Sphk com = tp->t_sc; 1848136139Sphk if (sigon == 0 && sigoff == 0) { 1849136139Sphk sigon = 0; 18509406Sbde mcr = com->mcr_image; 185138303Sbde if (mcr & com->mcr_dtr) 1852136139Sphk sigon |= SER_DTR; 185338303Sbde if (mcr & com->mcr_rts) 18549406Sbde /* XXX wired on for Cyclom-8Ys */ 1855136139Sphk sigon |= SER_RTS; 185620152Sbde 185720152Sbde /* 185820152Sbde * We must read the modem status from the hardware because 185920152Sbde * we don't generate modem status change interrupts for all 186020152Sbde * changes, so com->prev_modem_status is not guaranteed to 186120152Sbde * be up to date. This is safe, unlike for sio, because 186220152Sbde * reading the status register doesn't clear pending modem 186320152Sbde * status change interrupts. 186420152Sbde */ 186541293Sbde msr = cd_getreg(com, CD1400_MSVR2); 186620152Sbde 1867136139Sphk if (msr & CD1400_MSVR2_CTS) 1868136139Sphk sigon |= SER_CTS; 1869136139Sphk if (msr & CD1400_MSVR2_CD) 1870136139Sphk sigon |= SER_DCD; 1871136139Sphk if (msr & CD1400_MSVR2_DSR) 1872136139Sphk sigon |= SER_DSR; 1873136139Sphk if (msr & CD1400_MSVR2_RI) 18749406Sbde /* XXX not connected except for Cyclom-16Y? */ 1875136139Sphk sigon |= SER_RI; 1876136139Sphk return (sigon); 18776261Sjkh } 1878136139Sphk mcr = com->mcr_image; 1879136139Sphk if (sigon & SER_DTR) 188038303Sbde mcr |= com->mcr_dtr; 1881136139Sphk if (sigoff & SER_DTR) 1882136139Sphk mcr &= ~com->mcr_dtr; 1883136139Sphk if (sigon & SER_RTS) 188438303Sbde mcr |= com->mcr_rts; 1885136139Sphk if (sigoff & SER_RTS) 1886136139Sphk mcr &= ~com->mcr_rts; 188788088Sjhb critical_enter(); 188872262Sjhb COM_LOCK(); 1889136139Sphk com->mcr_image = mcr; 1890136139Sphk cd_setreg(com, CD1400_MSVR1, mcr); 1891136139Sphk cd_setreg(com, CD1400_MSVR2, mcr); 189272262Sjhb COM_UNLOCK(); 189388088Sjhb critical_exit(); 18949406Sbde return (0); 18959406Sbde} 18966261Sjkh 18979406Sbdestatic void 1898136139Sphkcysettimeout() 18999406Sbde{ 19009406Sbde struct com_s *com; 19019406Sbde bool_t someopen; 19029406Sbde int unit; 19036261Sjkh 19049406Sbde /* 19059406Sbde * Set our timeout period to 1 second if no polled devices are open. 19069406Sbde * Otherwise set it to max(1/200, 1/hz). 19079406Sbde * Enable timeouts iff some device is open. 19089406Sbde */ 1909136139Sphk untimeout(cywakeup, (void *)NULL, cy_timeout_handle); 1910136139Sphk cy_timeout = hz; 19119406Sbde someopen = FALSE; 1912136139Sphk for (unit = 0; unit < NPORTS; ++unit) { 1913136139Sphk com = cy_addr(unit); 19149406Sbde if (com != NULL && com->tp != NULL 19159406Sbde && com->tp->t_state & TS_ISOPEN) { 19169406Sbde someopen = TRUE; 19179406Sbde } 19189406Sbde } 19199406Sbde if (someopen) { 1920136139Sphk cy_timeouts_until_log = hz / cy_timeout; 1921136139Sphk cy_timeout_handle = timeout(cywakeup, (void *)NULL, 1922136139Sphk cy_timeout); 19239406Sbde } else { 19249406Sbde /* Flush error messages, if any. */ 1925136139Sphk cy_timeouts_until_log = 1; 1926136139Sphk cywakeup((void *)NULL); 1927136139Sphk untimeout(cywakeup, (void *)NULL, cy_timeout_handle); 19289406Sbde } 19299406Sbde} 19306261Sjkh 19319406Sbdestatic void 1932136139Sphkcywakeup(void *chan) 19336261Sjkh{ 19349406Sbde struct com_s *com; 19359406Sbde int unit; 19366261Sjkh 1937136139Sphk cy_timeout_handle = timeout(cywakeup, (void *)NULL, cy_timeout); 19386261Sjkh 19399406Sbde /* 19409406Sbde * Check for and log errors, but not too often. 19419406Sbde */ 1942136139Sphk if (--cy_timeouts_until_log > 0) 19439406Sbde return; 1944136139Sphk cy_timeouts_until_log = hz / cy_timeout; 1945136139Sphk for (unit = 0; unit < NPORTS; ++unit) { 19469406Sbde int errnum; 19476261Sjkh 1948136139Sphk com = cy_addr(unit); 19499406Sbde if (com == NULL) 19509406Sbde continue; 19519406Sbde for (errnum = 0; errnum < CE_NTYPES; ++errnum) { 19529406Sbde u_int delta; 19539406Sbde u_long total; 19546261Sjkh 195588088Sjhb critical_enter(); 195672262Sjhb COM_LOCK(); 19579406Sbde delta = com->delta_error_counts[errnum]; 19589406Sbde com->delta_error_counts[errnum] = 0; 195972262Sjhb COM_UNLOCK(); 196088088Sjhb critical_exit(); 19619406Sbde if (delta == 0) 19629406Sbde continue; 19639406Sbde total = com->error_counts[errnum] += delta; 19649406Sbde log(LOG_ERR, "cy%d: %u more %s%s (total %lu)\n", 19659406Sbde unit, delta, error_desc[errnum], 19669406Sbde delta == 1 ? "" : "s", total); 19679406Sbde } 19689406Sbde } 19696261Sjkh} 19706261Sjkh 19719406Sbdestatic void 1972136139Sphkdisc_optim(struct tty *tp, struct termios *t, struct com_s *com) 19736712Spst{ 19749406Sbde#ifndef SOFT_HOTCHAR 19759406Sbde u_char opt; 19769406Sbde#endif 19776261Sjkh 1978131134Sphk ttyldoptim(tp); 19799406Sbde#ifndef SOFT_HOTCHAR 19809406Sbde opt = com->cor[2] & ~CD1400_COR3_SCD34; 1981131134Sphk if (com->tp->t_hotchar != 0) { 1982131134Sphk cd_setreg(com, CD1400_SCHR3, com->tp->t_hotchar); 1983131134Sphk cd_setreg(com, CD1400_SCHR4, com->tp->t_hotchar); 19849406Sbde opt |= CD1400_COR3_SCD34; 19859406Sbde } 19869406Sbde if (opt != com->cor[2]) { 198741293Sbde cd_setreg(com, CD1400_COR3, com->cor[2] = opt); 198841293Sbde cd1400_channel_cmd(com, CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3); 19899406Sbde } 19909406Sbde#endif 19916712Spst} 19926712Spst 19939406Sbde#ifdef Smarts 19949406Sbde/* standard line discipline input routine */ 19956261Sjkhint 1996136139Sphkcyinput(int c, struct tty *tp) 19976261Sjkh{ 19989406Sbde /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK 19999406Sbde * bits, as they are done by the CD1400. Hardly worth the effort, 2000136139Sphk * given that high-throughput session are raw anyhow. 20019406Sbde */ 20029406Sbde} 20039406Sbde#endif /* Smarts */ 20046261Sjkh 20059406Sbdestatic int 2006136139Sphkcyspeed(speed_t speed, u_long cy_clock, int *prescaler_io) 20079406Sbde{ 20089406Sbde int actual; 20099406Sbde int error; 20109406Sbde int divider; 20119406Sbde int prescaler; 20129406Sbde int prescaler_unit; 20136261Sjkh 20149406Sbde if (speed == 0) 20159406Sbde return (0); 20169406Sbde if (speed < 0 || speed > 150000) 20179406Sbde return (-1); 20186261Sjkh 20199406Sbde /* determine which prescaler to use */ 20209406Sbde for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; 20219406Sbde prescaler_unit--, prescaler >>= 2) { 202238303Sbde if (cy_clock / prescaler / speed > 63) 20239406Sbde break; 20249406Sbde } 20256261Sjkh 202638303Sbde divider = (cy_clock / prescaler * 2 / speed + 1) / 2; /* round off */ 20279406Sbde if (divider > 255) 20289406Sbde divider = 255; 202938303Sbde actual = cy_clock/prescaler/divider; 20306261Sjkh 203137959Sbde /* 10 times error in percent: */ 203237959Sbde error = ((actual - (long)speed) * 2000 / (long)speed + 1) / 2; 203337959Sbde 20349406Sbde /* 3.0% max error tolerance */ 20359406Sbde if (error < -30 || error > 30) 20369406Sbde return (-1); 20376261Sjkh 20389406Sbde *prescaler_io = prescaler_unit; 20399406Sbde return (divider); 20409406Sbde} 20416261Sjkh 20426261Sjkhstatic void 2043136139Sphkcd1400_channel_cmd(struct com_s *com, int cmd) 20446261Sjkh{ 204541940Sbde cd1400_channel_cmd_wait(com); 204641940Sbde cd_setreg(com, CD1400_CCR, cmd); 204741940Sbde cd1400_channel_cmd_wait(com); 204841940Sbde} 20496261Sjkh 205041940Sbdestatic void 2051136139Sphkcd1400_channel_cmd_wait(struct com_s *com) 205241940Sbde{ 205341940Sbde struct timeval start; 205441940Sbde struct timeval tv; 205541940Sbde long usec; 20568876Srgrimes 205741940Sbde if (cd_getreg(com, CD1400_CCR) == 0) 205841940Sbde return; 205941940Sbde microtime(&start); 206041940Sbde for (;;) { 206141940Sbde if (cd_getreg(com, CD1400_CCR) == 0) 206241940Sbde return; 206341940Sbde microtime(&tv); 206441940Sbde usec = 1000000 * (tv.tv_sec - start.tv_sec) + 206541940Sbde tv.tv_usec - start.tv_usec; 206641940Sbde if (usec >= 5000) { 206741940Sbde log(LOG_ERR, 206841940Sbde "cy%d: channel command timeout (%ld usec)\n", 206941940Sbde com->unit, usec); 207041940Sbde return; 207141940Sbde } 207241940Sbde } 20739406Sbde} 20746261Sjkh 207541903Sbdestatic void 2076136139Sphkcd_etc(struct com_s *com, int etc) 207741903Sbde{ 207865557Sjasone 207941903Sbde /* 208041903Sbde * We can't change the hardware's ETC state while there are any 208141903Sbde * characters in the tx fifo, since those characters would be 208241903Sbde * interpreted as commands! Unputting characters from the fifo 208341903Sbde * is difficult, so we wait up to 12 character times for the fifo 208441903Sbde * to drain. The command will be delayed for up to 2 character 208541903Sbde * times for the tx to become empty. Unputting characters from 208641903Sbde * the tx holding and shift registers is impossible, so we wait 208741903Sbde * for the tx to become empty so that the command is sure to be 208841903Sbde * executed soon after we issue it. 208941903Sbde */ 209088088Sjhb critical_enter(); 209172262Sjhb COM_LOCK(); 209265557Sjasone if (com->etc == etc) 209341903Sbde goto wait; 209443314Sdillon if ((etc == CD1400_ETC_SENDBREAK 209541903Sbde && (com->etc == ETC_BREAK_STARTING 209643314Sdillon || com->etc == ETC_BREAK_STARTED)) 209743314Sdillon || (etc == CD1400_ETC_STOPBREAK 209841903Sbde && (com->etc == ETC_BREAK_ENDING || com->etc == ETC_BREAK_ENDED 209943314Sdillon || com->etc == ETC_NONE))) { 210072262Sjhb COM_UNLOCK(); 210188088Sjhb critical_exit(); 210241903Sbde return; 210341903Sbde } 210441903Sbde com->etc = etc; 210541903Sbde cd_setreg(com, CD1400_SRER, 210641903Sbde com->intr_enable 210743314Sdillon = (com->intr_enable & ~CD1400_SRER_TXRDY) | CD1400_SRER_TXMPTY); 210841903Sbdewait: 210972262Sjhb COM_UNLOCK(); 211088088Sjhb critical_exit(); 211141903Sbde while (com->etc == etc 211241903Sbde && tsleep(&com->etc, TTIPRI | PCATCH, "cyetc", 0) == 0) 211341903Sbde continue; 211441903Sbde} 211541903Sbde 211641293Sbdestatic int 2117136139Sphkcd_getreg(struct com_s *com, int reg) 211841293Sbde{ 211941293Sbde struct com_s *basecom; 212041293Sbde u_char car; 212141293Sbde int cy_align; 212241293Sbde cy_addr iobase; 2123120504Sbde#ifdef SMP 2124120504Sbde int need_unlock; 2125120504Sbde#endif 212641293Sbde int val; 212741293Sbde 2128136139Sphk basecom = cy_addr(com->unit & ~(CD1400_NO_OF_CHANNELS - 1)); 212941293Sbde car = com->unit & CD1400_CAR_CHAN; 213041293Sbde cy_align = com->cy_align; 213141293Sbde iobase = com->iobase; 213288088Sjhb critical_enter(); 2133120504Sbde#ifdef SMP 2134120504Sbde need_unlock = 0; 2135136139Sphk if (!mtx_owned(&cy_lock)) { 213672262Sjhb COM_LOCK(); 2137120504Sbde need_unlock = 1; 2138120504Sbde } 2139120504Sbde#endif 214041293Sbde if (basecom->car != car) 214141293Sbde cd_outb(iobase, CD1400_CAR, cy_align, basecom->car = car); 214241293Sbde val = cd_inb(iobase, reg, cy_align); 2143120504Sbde#ifdef SMP 2144120504Sbde if (need_unlock) 214572262Sjhb COM_UNLOCK(); 2146120504Sbde#endif 214788088Sjhb critical_exit(); 214841293Sbde return (val); 214941293Sbde} 215041293Sbde 215141293Sbdestatic void 2152136139Sphkcd_setreg(struct com_s *com, int reg, int val) 215341293Sbde{ 215441293Sbde struct com_s *basecom; 215541293Sbde u_char car; 215641293Sbde int cy_align; 215741293Sbde cy_addr iobase; 2158120504Sbde#ifdef SMP 2159120504Sbde int need_unlock; 2160120504Sbde#endif 216141293Sbde 2162136139Sphk basecom = cy_addr(com->unit & ~(CD1400_NO_OF_CHANNELS - 1)); 216341293Sbde car = com->unit & CD1400_CAR_CHAN; 216441293Sbde cy_align = com->cy_align; 216541293Sbde iobase = com->iobase; 216688088Sjhb critical_enter(); 2167120504Sbde#ifdef SMP 2168120504Sbde need_unlock = 0; 2169136139Sphk if (!mtx_owned(&cy_lock)) { 217072262Sjhb COM_LOCK(); 2171120504Sbde need_unlock = 1; 2172120504Sbde } 2173120504Sbde#endif 217441293Sbde if (basecom->car != car) 217541293Sbde cd_outb(iobase, CD1400_CAR, cy_align, basecom->car = car); 217641293Sbde cd_outb(iobase, reg, cy_align, val); 2177120504Sbde#ifdef SMP 2178120504Sbde if (need_unlock) 217972262Sjhb COM_UNLOCK(); 2180120504Sbde#endif 218188088Sjhb critical_exit(); 218241293Sbde} 218341293Sbde 21846261Sjkh#ifdef CyDebug 21856261Sjkh/* useful in ddb */ 21866261Sjkhvoid 2187136139Sphkcystatus(int unit) 21886261Sjkh{ 21899406Sbde struct com_s *com; 21909406Sbde cy_addr iobase; 21919406Sbde u_int ocount; 21929406Sbde struct tty *tp; 21936261Sjkh 2194136139Sphk com = cy_addr(unit); 21956261Sjkh printf("info for channel %d\n", unit); 21966261Sjkh printf("------------------\n"); 21979406Sbde printf("total cyclom service probes:\t%d\n", cy_svrr_probes); 21989406Sbde printf("calls to upper layer:\t\t%d\n", cy_timeouts); 21999406Sbde if (com == NULL) 22009406Sbde return; 22019406Sbde iobase = com->iobase; 22026261Sjkh printf("\n"); 22039406Sbde printf("cd1400 base address:\\tt%p\n", iobase); 22049406Sbde printf("saved channel_control:\t\t0x%02x\n", com->channel_control); 22059406Sbde printf("saved cor1-3:\t\t\t0x%02x 0x%02x 0x%02x\n", 22069406Sbde com->cor[0], com->cor[1], com->cor[2]); 22079406Sbde printf("service request enable reg:\t0x%02x (0x%02x cached)\n", 220841293Sbde cd_getreg(com, CD1400_SRER), com->intr_enable); 22099406Sbde printf("service request register:\t0x%02x\n", 221018925Sdg cd_inb(iobase, CD1400_SVRR, com->cy_align)); 22119406Sbde printf("modem status:\t\t\t0x%02x (0x%02x cached)\n", 221241293Sbde cd_getreg(com, CD1400_MSVR2), com->prev_modem_status); 22139406Sbde printf("rx/tx/mdm interrupt registers:\t0x%02x 0x%02x 0x%02x\n", 221418925Sdg cd_inb(iobase, CD1400_RIR, com->cy_align), 221518925Sdg cd_inb(iobase, CD1400_TIR, com->cy_align), 221618925Sdg cd_inb(iobase, CD1400_MIR, com->cy_align)); 22176261Sjkh printf("\n"); 22189406Sbde printf("com state:\t\t\t0x%02x\n", com->state); 2219136139Sphk printf("calls to cystart():\t\t%d (%d useful)\n", 22209406Sbde com->start_count, com->start_real); 22219406Sbde printf("rx buffer chars free:\t\t%d\n", com->iptr - com->ibuf); 22229406Sbde ocount = 0; 22239406Sbde if (com->obufs[0].l_queued) 22249406Sbde ocount += com->obufs[0].l_tail - com->obufs[0].l_head; 22259406Sbde if (com->obufs[1].l_queued) 22269406Sbde ocount += com->obufs[1].l_tail - com->obufs[1].l_head; 22279406Sbde printf("tx buffer chars:\t\t%u\n", ocount); 22289406Sbde printf("received chars:\t\t\t%d\n", com->bytes_in); 22299406Sbde printf("received exceptions:\t\t%d\n", com->recv_exception); 22309406Sbde printf("modem signal deltas:\t\t%d\n", com->mdm); 22319406Sbde printf("transmitted chars:\t\t%d\n", com->bytes_out); 22329406Sbde printf("\n"); 22339406Sbde tp = com->tp; 22349406Sbde if (tp != NULL) { 22359406Sbde printf("tty state:\t\t\t0x%08x\n", tp->t_state); 223629047Sbde printf( 223729047Sbde "upper layer queue lengths:\t%d raw, %d canon, %d output\n", 22389406Sbde tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); 22399406Sbde } else 22406261Sjkh printf("tty state:\t\t\tclosed\n"); 22419406Sbde} 22429406Sbde#endif /* CyDebug */ 2243