Home | History | Annotate | Download | only in io
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
     23 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
     24 /*	  All Rights Reserved					*/
     25 
     26 /*
     27  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     28  * Use is subject to license terms.
     29  */
     30 
     31 
     32 /*
     33  * Serial I/O driver for 8250/16450/16550A/16650/16750 chips.
     34  */
     35 
     36 #include <sys/param.h>
     37 #include <sys/types.h>
     38 #include <sys/signal.h>
     39 #include <sys/stream.h>
     40 #include <sys/termio.h>
     41 #include <sys/errno.h>
     42 #include <sys/file.h>
     43 #include <sys/cmn_err.h>
     44 #include <sys/stropts.h>
     45 #include <sys/strsubr.h>
     46 #include <sys/strtty.h>
     47 #include <sys/debug.h>
     48 #include <sys/kbio.h>
     49 #include <sys/cred.h>
     50 #include <sys/stat.h>
     51 #include <sys/consdev.h>
     52 #include <sys/mkdev.h>
     53 #include <sys/kmem.h>
     54 #include <sys/cred.h>
     55 #include <sys/strsun.h>
     56 #ifdef DEBUG
     57 #include <sys/promif.h>
     58 #endif
     59 #include <sys/modctl.h>
     60 #include <sys/ddi.h>
     61 #include <sys/sunddi.h>
     62 #include <sys/pci.h>
     63 #include <sys/asy.h>
     64 #include <sys/policy.h>
     65 
     66 /*
     67  * set the RX FIFO trigger_level to half the RX FIFO size for now
     68  * we may want to make this configurable later.
     69  */
     70 static	int asy_trig_level = FIFO_TRIG_8;
     71 
     72 int asy_drain_check = 15000000;		/* tunable: exit drain check time */
     73 int asy_min_dtr_low = 500000;		/* tunable: minimum DTR down time */
     74 int asy_min_utbrk = 100000;		/* tunable: minumum untimed brk time */
     75 
     76 int asymaxchip = ASY16750;	/* tunable: limit chip support we look for */
     77 
     78 /*
     79  * Just in case someone has a chip with broken loopback mode, we provide a
     80  * means to disable the loopback test. By default, we only loopback test
     81  * UARTs which look like they have FIFOs bigger than 16 bytes.
     82  * Set to 0 to suppress test, or to 2 to enable test on any size FIFO.
     83  */
     84 int asy_fifo_test = 1;		/* tunable: set to 0, 1, or 2 */
     85 
     86 /*
     87  * Allow ability to switch off testing of the scratch register.
     88  * Some UART emulators might not have it. This will also disable the test
     89  * for Exar/Startech ST16C650, as that requires use of the SCR register.
     90  */
     91 int asy_scr_test = 1;		/* tunable: set to 0 to disable SCR reg test */
     92 
     93 /*
     94  * As we don't yet support on-chip flow control, it's a bad idea to put a
     95  * large number of characters in the TX FIFO, since if other end tells us
     96  * to stop transmitting, we can only stop filling the TX FIFO, but it will
     97  * still carry on draining by itself, so remote end still gets what's left
     98  * in the FIFO.
     99  */
    100 int asy_max_tx_fifo = 16;	/* tunable: max fill of TX FIFO */
    101 
    102 #define	async_stopc	async_ttycommon.t_stopc
    103 #define	async_startc	async_ttycommon.t_startc
    104 
    105 #define	ASY_INIT	1
    106 #define	ASY_NOINIT	0
    107 
    108 /* enum value for sw and hw flow control action */
    109 typedef enum {
    110 	FLOW_CHECK,
    111 	FLOW_STOP,
    112 	FLOW_START
    113 } async_flowc_action;
    114 
    115 #ifdef DEBUG
    116 #define	ASY_DEBUG_INIT	0x0001	/* Output msgs during driver initialization. */
    117 #define	ASY_DEBUG_INPUT	0x0002	/* Report characters received during int. */
    118 #define	ASY_DEBUG_EOT	0x0004	/* Output msgs when wait for xmit to finish. */
    119 #define	ASY_DEBUG_CLOSE	0x0008	/* Output msgs when driver open/close called */
    120 #define	ASY_DEBUG_HFLOW	0x0010	/* Output msgs when H/W flowcontrol is active */
    121 #define	ASY_DEBUG_PROCS	0x0020	/* Output each proc name as it is entered. */
    122 #define	ASY_DEBUG_STATE	0x0040	/* Output value of Interrupt Service Reg. */
    123 #define	ASY_DEBUG_INTR	0x0080	/* Output value of Interrupt Service Reg. */
    124 #define	ASY_DEBUG_OUT	0x0100	/* Output msgs about output events. */
    125 #define	ASY_DEBUG_BUSY	0x0200	/* Output msgs when xmit is enabled/disabled */
    126 #define	ASY_DEBUG_MODEM	0x0400	/* Output msgs about modem status & control. */
    127 #define	ASY_DEBUG_MODM2	0x0800	/* Output msgs about modem status & control. */
    128 #define	ASY_DEBUG_IOCTL	0x1000	/* Output msgs about ioctl messages. */
    129 #define	ASY_DEBUG_CHIP	0x2000	/* Output msgs about chip identification. */
    130 #define	ASY_DEBUG_SFLOW	0x4000	/* Output msgs when S/W flowcontrol is active */
    131 #define	ASY_DEBUG(x) (debug & (x))
    132 static	int debug  = 0;
    133 #else
    134 #define	ASY_DEBUG(x) B_FALSE
    135 #endif
    136 
    137 /* pnpISA compressed device ids */
    138 #define	pnpMTS0219 0xb6930219	/* Multitech MT5634ZTX modem */
    139 
    140 /*
    141  * PPS (Pulse Per Second) support.
    142  */
    143 void ddi_hardpps();
    144 /*
    145  * This is protected by the asy_excl_hi of the port on which PPS event
    146  * handling is enabled.  Note that only one port should have this enabled at
    147  * any one time.  Enabling PPS handling on multiple ports will result in
    148  * unpredictable (but benign) results.
    149  */
    150 static struct ppsclockev asy_ppsev;
    151 
    152 #ifdef PPSCLOCKLED
    153 /* XXX Use these to observe PPS latencies and jitter on a scope */
    154 #define	LED_ON
    155 #define	LED_OFF
    156 #else
    157 #define	LED_ON
    158 #define	LED_OFF
    159 #endif
    160 
    161 static	int max_asy_instance = -1;
    162 
    163 static	uint_t	asysoftintr(caddr_t intarg);
    164 static	uint_t	asyintr(caddr_t argasy);
    165 
    166 static boolean_t abort_charseq_recognize(uchar_t ch);
    167 
    168 /* The async interrupt entry points */
    169 static void	async_txint(struct asycom *asy);
    170 static void	async_rxint(struct asycom *asy, uchar_t lsr);
    171 static void	async_msint(struct asycom *asy);
    172 static void	async_softint(struct asycom *asy);
    173 
    174 static void	async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp);
    175 static void	async_reioctl(void *unit);
    176 static void	async_iocdata(queue_t *q, mblk_t *mp);
    177 static void	async_restart(void *arg);
    178 static void	async_start(struct asyncline *async);
    179 static void	async_nstart(struct asyncline *async, int mode);
    180 static void	async_resume(struct asyncline *async);
    181 static void	asy_program(struct asycom *asy, int mode);
    182 static void	asyinit(struct asycom *asy);
    183 static void	asy_waiteot(struct asycom *asy);
    184 static void	asyputchar(cons_polledio_arg_t, uchar_t c);
    185 static int	asygetchar(cons_polledio_arg_t);
    186 static boolean_t	asyischar(cons_polledio_arg_t);
    187 
    188 static int	asymctl(struct asycom *, int, int);
    189 static int	asytodm(int, int);
    190 static int	dmtoasy(int);
    191 /*PRINTFLIKE2*/
    192 static void	asyerror(int level, const char *fmt, ...) __KPRINTFLIKE(2);
    193 static void	asy_parse_mode(dev_info_t *devi, struct asycom *asy);
    194 static void	asy_soft_state_free(struct asycom *);
    195 static char	*asy_hw_name(struct asycom *asy);
    196 static void	async_hold_utbrk(void *arg);
    197 static void	async_resume_utbrk(struct asyncline *async);
    198 static void	async_dtr_free(struct asyncline *async);
    199 static int	asy_identify_chip(dev_info_t *devi, struct asycom *asy);
    200 static void	asy_reset_fifo(struct asycom *asy, uchar_t flags);
    201 static int	asy_getproperty(dev_info_t *devi, struct asycom *asy,
    202 		    const char *property);
    203 static boolean_t	async_flowcontrol_sw_input(struct asycom *asy,
    204 			    async_flowc_action onoff, int type);
    205 static void	async_flowcontrol_sw_output(struct asycom *asy,
    206 		    async_flowc_action onoff);
    207 static void	async_flowcontrol_hw_input(struct asycom *asy,
    208 		    async_flowc_action onoff, int type);
    209 static void	async_flowcontrol_hw_output(struct asycom *asy,
    210 		    async_flowc_action onoff);
    211 
    212 #define	GET_PROP(devi, pname, pflag, pval, plen) \
    213 		(ddi_prop_op(DDI_DEV_T_ANY, (devi), PROP_LEN_AND_VAL_BUF, \
    214 		(pflag), (pname), (caddr_t)(pval), (plen)))
    215 
    216 static ddi_iblock_cookie_t asy_soft_iblock;
    217 ddi_softintr_t asy_softintr_id;
    218 static	int asy_addedsoft = 0;
    219 int	asysoftpend;	/* soft interrupt pending */
    220 kmutex_t asy_soft_lock;	/* lock protecting asysoftpend */
    221 kmutex_t asy_glob_lock; /* lock protecting global data manipulation */
    222 void *asy_soft_state;
    223 
    224 /* Standard COM port I/O addresses */
    225 static const int standard_com_ports[] = {
    226 	COM1_IOADDR, COM2_IOADDR, COM3_IOADDR, COM4_IOADDR
    227 };
    228 
    229 static int *com_ports;
    230 static uint_t num_com_ports;
    231 
    232 #ifdef	DEBUG
    233 /*
    234  * Set this to true to make the driver pretend to do a suspend.  Useful
    235  * for debugging suspend/resume code with a serial debugger.
    236  */
    237 boolean_t	asy_nosuspend = B_FALSE;
    238 #endif
    239 
    240 
    241 /*
    242  * Baud rate table. Indexed by #defines found in sys/termios.h
    243  */
    244 ushort_t asyspdtab[] = {
    245 	0,	/* 0 baud rate */
    246 	0x900,	/* 50 baud rate */
    247 	0x600,	/* 75 baud rate */
    248 	0x417,	/* 110 baud rate (%0.026) */
    249 	0x359,	/* 134 baud rate (%0.058) */
    250 	0x300,	/* 150 baud rate */
    251 	0x240,	/* 200 baud rate */
    252 	0x180,	/* 300 baud rate */
    253 	0x0c0,	/* 600 baud rate */
    254 	0x060,	/* 1200 baud rate */
    255 	0x040,	/* 1800 baud rate */
    256 	0x030,	/* 2400 baud rate */
    257 	0x018,	/* 4800 baud rate */
    258 	0x00c,	/* 9600 baud rate */
    259 	0x006,	/* 19200 baud rate */
    260 	0x003,	/* 38400 baud rate */
    261 
    262 	0x002,	/* 57600 baud rate */
    263 	0x0,	/* 76800 baud rate not supported */
    264 	0x001,	/* 115200 baud rate */
    265 	0x0,	/* 153600 baud rate not supported */
    266 	0x0,	/* 0x8002 (SMC chip) 230400 baud rate not supported */
    267 	0x0,	/* 307200 baud rate not supported */
    268 	0x0,	/* 0x8001 (SMC chip) 460800 baud rate not supported */
    269 	0x0,	/* unused */
    270 	0x0,	/* unused */
    271 	0x0,	/* unused */
    272 	0x0,	/* unused */
    273 	0x0,	/* unused */
    274 	0x0,	/* unused */
    275 	0x0,	/* unused */
    276 	0x0,	/* unused */
    277 	0x0,	/* unused */
    278 };
    279 
    280 static int asyrsrv(queue_t *q);
    281 static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr);
    282 static int asyclose(queue_t *q, int flag, cred_t *credp);
    283 static int asywputdo(queue_t *q, mblk_t *mp, boolean_t);
    284 static int asywput(queue_t *q, mblk_t *mp);
    285 
    286 struct module_info asy_info = {
    287 	0,
    288 	"asy",
    289 	0,
    290 	INFPSZ,
    291 	4096,
    292 	128
    293 };
    294 
    295 static struct qinit asy_rint = {
    296 	putq,
    297 	asyrsrv,
    298 	asyopen,
    299 	asyclose,
    300 	NULL,
    301 	&asy_info,
    302 	NULL
    303 };
    304 
    305 static struct qinit asy_wint = {
    306 	asywput,
    307 	NULL,
    308 	NULL,
    309 	NULL,
    310 	NULL,
    311 	&asy_info,
    312 	NULL
    313 };
    314 
    315 struct streamtab asy_str_info = {
    316 	&asy_rint,
    317 	&asy_wint,
    318 	NULL,
    319 	NULL
    320 };
    321 
    322 static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
    323 		void **result);
    324 static int asyprobe(dev_info_t *);
    325 static int asyattach(dev_info_t *, ddi_attach_cmd_t);
    326 static int asydetach(dev_info_t *, ddi_detach_cmd_t);
    327 static int asyquiesce(dev_info_t *);
    328 
    329 static 	struct cb_ops cb_asy_ops = {
    330 	nodev,			/* cb_open */
    331 	nodev,			/* cb_close */
    332 	nodev,			/* cb_strategy */
    333 	nodev,			/* cb_print */
    334 	nodev,			/* cb_dump */
    335 	nodev,			/* cb_read */
    336 	nodev,			/* cb_write */
    337 	nodev,			/* cb_ioctl */
    338 	nodev,			/* cb_devmap */
    339 	nodev,			/* cb_mmap */
    340 	nodev,			/* cb_segmap */
    341 	nochpoll,		/* cb_chpoll */
    342 	ddi_prop_op,		/* cb_prop_op */
    343 	&asy_str_info,		/* cb_stream */
    344 	D_MP			/* cb_flag */
    345 };
    346 
    347 struct dev_ops asy_ops = {
    348 	DEVO_REV,		/* devo_rev */
    349 	0,			/* devo_refcnt */
    350 	asyinfo,		/* devo_getinfo */
    351 	nulldev,		/* devo_identify */
    352 	asyprobe,		/* devo_probe */
    353 	asyattach,		/* devo_attach */
    354 	asydetach,		/* devo_detach */
    355 	nodev,			/* devo_reset */
    356 	&cb_asy_ops,		/* devo_cb_ops */
    357 	NULL,			/* devo_bus_ops */
    358 	NULL,			/* power */
    359 	asyquiesce,		/* quiesce */
    360 };
    361 
    362 static struct modldrv modldrv = {
    363 	&mod_driverops, /* Type of module.  This one is a driver */
    364 	"ASY driver",
    365 	&asy_ops,	/* driver ops */
    366 };
    367 
    368 static struct modlinkage modlinkage = {
    369 	MODREV_1,
    370 	(void *)&modldrv,
    371 	NULL
    372 };
    373 
    374 int
    375 _init(void)
    376 {
    377 	int i;
    378 
    379 	i = ddi_soft_state_init(&asy_soft_state, sizeof (struct asycom), 2);
    380 	if (i == 0) {
    381 		mutex_init(&asy_glob_lock, NULL, MUTEX_DRIVER, NULL);
    382 		if ((i = mod_install(&modlinkage)) != 0) {
    383 			mutex_destroy(&asy_glob_lock);
    384 			ddi_soft_state_fini(&asy_soft_state);
    385 		} else {
    386 			DEBUGCONT2(ASY_DEBUG_INIT, "%s, debug = %x\n",
    387 			    modldrv.drv_linkinfo, debug);
    388 		}
    389 	}
    390 	return (i);
    391 }
    392 
    393 int
    394 _fini(void)
    395 {
    396 	int i;
    397 
    398 	if ((i = mod_remove(&modlinkage)) == 0) {
    399 		DEBUGCONT1(ASY_DEBUG_INIT, "%s unloading\n",
    400 		    modldrv.drv_linkinfo);
    401 		ASSERT(max_asy_instance == -1);
    402 		mutex_destroy(&asy_glob_lock);
    403 		if (asy_addedsoft)
    404 			ddi_remove_softintr(asy_softintr_id);
    405 		asy_addedsoft = 0;
    406 		/* free "motherboard-serial-ports" property if allocated */
    407 		if (com_ports != NULL && com_ports != (int *)standard_com_ports)
    408 			ddi_prop_free(com_ports);
    409 		com_ports = NULL;
    410 		mutex_destroy(&asy_soft_lock);
    411 		ddi_soft_state_fini(&asy_soft_state);
    412 	}
    413 	return (i);
    414 }
    415 
    416 int
    417 _info(struct modinfo *modinfop)
    418 {
    419 	return (mod_info(&modlinkage, modinfop));
    420 }
    421 
    422 void
    423 async_put_suspq(struct asycom *asy, mblk_t *mp)
    424 {
    425 	struct asyncline *async = asy->asy_priv;
    426 
    427 	ASSERT(mutex_owned(&asy->asy_excl));
    428 
    429 	if (async->async_suspqf == NULL)
    430 		async->async_suspqf = mp;
    431 	else
    432 		async->async_suspqb->b_next = mp;
    433 
    434 	async->async_suspqb = mp;
    435 }
    436 
    437 static mblk_t *
    438 async_get_suspq(struct asycom *asy)
    439 {
    440 	struct asyncline *async = asy->asy_priv;
    441 	mblk_t *mp;
    442 
    443 	ASSERT(mutex_owned(&asy->asy_excl));
    444 
    445 	if ((mp = async->async_suspqf) != NULL) {
    446 		async->async_suspqf = mp->b_next;
    447 		mp->b_next = NULL;
    448 	} else {
    449 		async->async_suspqb = NULL;
    450 	}
    451 	return (mp);
    452 }
    453 
    454 static void
    455 async_process_suspq(struct asycom *asy)
    456 {
    457 	struct asyncline *async = asy->asy_priv;
    458 	mblk_t *mp;
    459 
    460 	ASSERT(mutex_owned(&asy->asy_excl));
    461 
    462 	while ((mp = async_get_suspq(asy)) != NULL) {
    463 		queue_t *q;
    464 
    465 		q = async->async_ttycommon.t_writeq;
    466 		ASSERT(q != NULL);
    467 		mutex_exit(&asy->asy_excl);
    468 		(void) asywputdo(q, mp, B_FALSE);
    469 		mutex_enter(&asy->asy_excl);
    470 	}
    471 	async->async_flags &= ~ASYNC_DDI_SUSPENDED;
    472 	cv_broadcast(&async->async_flags_cv);
    473 }
    474 
    475 static int
    476 asy_get_bus_type(dev_info_t *devinfo)
    477 {
    478 	char	parent_type[16];
    479 	int	parentlen;
    480 
    481 	parentlen = sizeof (parent_type);
    482 
    483 	if (ddi_prop_op(DDI_DEV_T_ANY, devinfo, PROP_LEN_AND_VAL_BUF, 0,
    484 	    "device_type", (caddr_t)parent_type, &parentlen)
    485 	    != DDI_PROP_SUCCESS && ddi_prop_op(DDI_DEV_T_ANY, devinfo,
    486 	    PROP_LEN_AND_VAL_BUF, 0, "bus-type", (caddr_t)parent_type,
    487 	    &parentlen) != DDI_PROP_SUCCESS) {
    488 			cmn_err(CE_WARN,
    489 			    "asy: can't figure out device type for"
    490 			    " parent \"%s\"",
    491 			    ddi_get_name(ddi_get_parent(devinfo)));
    492 			return (ASY_BUS_UNKNOWN);
    493 	}
    494 	if (strcmp(parent_type, "isa") == 0)
    495 		return (ASY_BUS_ISA);
    496 	else if (strcmp(parent_type, "pci") == 0)
    497 		return (ASY_BUS_PCI);
    498 	else
    499 		return (ASY_BUS_UNKNOWN);
    500 }
    501 
    502 static int
    503 asy_get_io_regnum_pci(dev_info_t *devi, struct asycom *asy)
    504 {
    505 	int reglen, nregs;
    506 	int regnum, i;
    507 	uint64_t size;
    508 	struct pci_phys_spec *reglist;
    509 
    510 	if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
    511 	    "reg", (caddr_t)&reglist, &reglen) != DDI_PROP_SUCCESS) {
    512 		cmn_err(CE_WARN, "asy_get_io_regnum_pci: reg property"
    513 		    " not found in devices property list");
    514 		return (-1);
    515 	}
    516 
    517 	/*
    518 	 * PCI devices are assumed to not have broken FIFOs;
    519 	 * Agere/Lucent Venus PCI modem chipsets are an example
    520 	 */
    521 	if (asy)
    522 		asy->asy_flags2 |= ASY2_NO_LOOPBACK;
    523 
    524 	regnum = -1;
    525 	nregs = reglen / sizeof (*reglist);
    526 	for (i = 0; i < nregs; i++) {
    527 		switch (reglist[i].pci_phys_hi & PCI_ADDR_MASK) {
    528 		case PCI_ADDR_IO:		/* I/O bus reg property */
    529 			if (regnum == -1) /* use only the first one */
    530 				regnum = i;
    531 			break;
    532 
    533 		default:
    534 			break;
    535 		}
    536 	}
    537 
    538 	/* check for valid count of registers */
    539 	if (regnum >= 0) {
    540 		size = ((uint64_t)reglist[regnum].pci_size_low) |
    541 		    ((uint64_t)reglist[regnum].pci_size_hi) << 32;
    542 		if (size < 8)
    543 			regnum = -1;
    544 	}
    545 	kmem_free(reglist, reglen);
    546 	return (regnum);
    547 }
    548 
    549 static int
    550 asy_get_io_regnum_isa(dev_info_t *devi, struct asycom *asy)
    551 {
    552 	int reglen, nregs;
    553 	int regnum, i;
    554 	struct {
    555 		uint_t bustype;
    556 		int base;
    557 		int size;
    558 	} *reglist;
    559 
    560 	if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
    561 	    "reg", (caddr_t)&reglist, &reglen) != DDI_PROP_SUCCESS) {
    562 		cmn_err(CE_WARN, "asy_get_io_regnum: reg property not found "
    563 		    "in devices property list");
    564 		return (-1);
    565 	}
    566 
    567 	regnum = -1;
    568 	nregs = reglen / sizeof (*reglist);
    569 	for (i = 0; i < nregs; i++) {
    570 		switch (reglist[i].bustype) {
    571 		case 1:			/* I/O bus reg property */
    572 			if (regnum == -1) /* only use the first one */
    573 				regnum = i;
    574 			break;
    575 
    576 		case pnpMTS0219:	/* Multitech MT5634ZTX modem */
    577 			/* Venus chipset can't do loopback test */
    578 			if (asy)
    579 				asy->asy_flags2 |= ASY2_NO_LOOPBACK;
    580 			break;
    581 
    582 		default:
    583 			break;
    584 		}
    585 	}
    586 
    587 	/* check for valid count of registers */
    588 	if ((regnum < 0) || (reglist[regnum].size < 8))
    589 		regnum = -1;
    590 	kmem_free(reglist, reglen);
    591 	return (regnum);
    592 }
    593 
    594 static int
    595 asy_get_io_regnum(dev_info_t *devinfo, struct asycom *asy)
    596 {
    597 	switch (asy_get_bus_type(devinfo)) {
    598 	case ASY_BUS_ISA:
    599 		return (asy_get_io_regnum_isa(devinfo, asy));
    600 	case ASY_BUS_PCI:
    601 		return (asy_get_io_regnum_pci(devinfo, asy));
    602 	default:
    603 		return (-1);
    604 	}
    605 }
    606 
    607 static int
    608 asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd)
    609 {
    610 	int instance;
    611 	struct asycom *asy;
    612 	struct asyncline *async;
    613 
    614 	instance = ddi_get_instance(devi);	/* find out which unit */
    615 
    616 	asy = ddi_get_soft_state(asy_soft_state, instance);
    617 	if (asy == NULL)
    618 		return (DDI_FAILURE);
    619 	async = asy->asy_priv;
    620 
    621 	switch (cmd) {
    622 	case DDI_DETACH:
    623 		DEBUGNOTE2(ASY_DEBUG_INIT, "asy%d: %s shutdown.",
    624 		    instance, asy_hw_name(asy));
    625 
    626 		/* cancel DTR hold timeout */
    627 		if (async->async_dtrtid != 0) {
    628 			(void) untimeout(async->async_dtrtid);
    629 			async->async_dtrtid = 0;
    630 		}
    631 
    632 		/* remove all minor device node(s) for this device */
    633 		ddi_remove_minor_node(devi, NULL);
    634 
    635 		mutex_destroy(&asy->asy_excl);
    636 		mutex_destroy(&asy->asy_excl_hi);
    637 		cv_destroy(&async->async_flags_cv);
    638 		ddi_remove_intr(devi, 0, asy->asy_iblock);
    639 		ddi_regs_map_free(&asy->asy_iohandle);
    640 		asy_soft_state_free(asy);
    641 		DEBUGNOTE1(ASY_DEBUG_INIT, "asy%d: shutdown complete",
    642 		    instance);
    643 		break;
    644 	case DDI_SUSPEND:
    645 		{
    646 		unsigned i;
    647 		uchar_t lsr;
    648 
    649 #ifdef	DEBUG
    650 		if (asy_nosuspend)
    651 			return (DDI_SUCCESS);
    652 #endif
    653 		mutex_enter(&asy->asy_excl);
    654 
    655 		ASSERT(async->async_ops >= 0);
    656 		while (async->async_ops > 0)
    657 			cv_wait(&async->async_ops_cv, &asy->asy_excl);
    658 
    659 		async->async_flags |= ASYNC_DDI_SUSPENDED;
    660 
    661 		/* Wait for timed break and delay to complete */
    662 		while ((async->async_flags & (ASYNC_BREAK|ASYNC_DELAY))) {
    663 			if (cv_wait_sig(&async->async_flags_cv, &asy->asy_excl)
    664 			    == 0) {
    665 				async_process_suspq(asy);
    666 				mutex_exit(&asy->asy_excl);
    667 				return (DDI_FAILURE);
    668 			}
    669 		}
    670 
    671 		/* Clear untimed break */
    672 		if (async->async_flags & ASYNC_OUT_SUSPEND)
    673 			async_resume_utbrk(async);
    674 
    675 		mutex_exit(&asy->asy_excl);
    676 
    677 		mutex_enter(&asy->asy_soft_sr);
    678 		mutex_enter(&asy->asy_excl);
    679 		if (async->async_wbufcid != 0) {
    680 			bufcall_id_t bcid = async->async_wbufcid;
    681 			async->async_wbufcid = 0;
    682 			async->async_flags |= ASYNC_RESUME_BUFCALL;
    683 			mutex_exit(&asy->asy_excl);
    684 			unbufcall(bcid);
    685 			mutex_enter(&asy->asy_excl);
    686 		}
    687 		mutex_enter(&asy->asy_excl_hi);
    688 
    689 		/* Disable interrupts from chip */
    690 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
    691 		asy->asy_flags |= ASY_DDI_SUSPENDED;
    692 
    693 		/* Process remaining RX characters and RX errors, if any */
    694 		lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
    695 		async_rxint(asy, lsr);
    696 
    697 		/* Wait for TX to drain */
    698 		for (i = 1000; i > 0; i--) {
    699 			lsr = ddi_get8(asy->asy_iohandle,
    700 			    asy->asy_ioaddr + LSR);
    701 			if ((lsr & (XSRE | XHRE)) == (XSRE | XHRE))
    702 				break;
    703 			delay(drv_usectohz(10000));
    704 		}
    705 		if (i == 0)
    706 			cmn_err(CE_WARN,
    707 			    "asy: transmitter wasn't drained before "
    708 			    "driver was suspended");
    709 
    710 		mutex_exit(&asy->asy_excl_hi);
    711 		mutex_exit(&asy->asy_excl);
    712 		mutex_exit(&asy->asy_soft_sr);
    713 		break;
    714 	}
    715 	default:
    716 		return (DDI_FAILURE);
    717 	}
    718 
    719 	return (DDI_SUCCESS);
    720 }
    721 
    722 /*
    723  * asyprobe
    724  * We don't bother probing for the hardware, as since Solaris 2.6, device
    725  * nodes are only created for auto-detected hardware or nodes explicitly
    726  * created by the user, e.g. via the DCA. However, we should check the
    727  * device node is at least vaguely usable, i.e. we have a block of 8 i/o
    728  * ports. This prevents attempting to attach to bogus serial ports which
    729  * some BIOSs still partially report when they are disabled in the BIOS.
    730  */
    731 static int
    732 asyprobe(dev_info_t *devi)
    733 {
    734 	return ((asy_get_io_regnum(devi, NULL) < 0) ?
    735 	    DDI_PROBE_FAILURE : DDI_PROBE_DONTCARE);
    736 }
    737 
    738 static int
    739 asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
    740 {
    741 	int instance;
    742 	int mcr;
    743 	int ret;
    744 	int regnum = 0;
    745 	int i;
    746 	struct asycom *asy;
    747 	char name[ASY_MINOR_LEN];
    748 	int status;
    749 	static ddi_device_acc_attr_t ioattr = {
    750 		DDI_DEVICE_ATTR_V0,
    751 		DDI_NEVERSWAP_ACC,
    752 		DDI_STRICTORDER_ACC,
    753 	};
    754 
    755 	instance = ddi_get_instance(devi);	/* find out which unit */
    756 
    757 	switch (cmd) {
    758 	case DDI_ATTACH:
    759 		break;
    760 	case DDI_RESUME:
    761 	{
    762 		struct asyncline *async;
    763 
    764 #ifdef	DEBUG
    765 		if (asy_nosuspend)
    766 			return (DDI_SUCCESS);
    767 #endif
    768 		asy = ddi_get_soft_state(asy_soft_state, instance);
    769 		if (asy == NULL)
    770 			return (DDI_FAILURE);
    771 
    772 		mutex_enter(&asy->asy_soft_sr);
    773 		mutex_enter(&asy->asy_excl);
    774 		mutex_enter(&asy->asy_excl_hi);
    775 
    776 		async = asy->asy_priv;
    777 		/* Disable interrupts */
    778 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
    779 		if (asy_identify_chip(devi, asy) != DDI_SUCCESS) {
    780 			mutex_exit(&asy->asy_excl_hi);
    781 			mutex_exit(&asy->asy_excl);
    782 			mutex_exit(&asy->asy_soft_sr);
    783 			cmn_err(CE_WARN, "Cannot identify UART chip at %p\n",
    784 			    (void *)asy->asy_ioaddr);
    785 			return (DDI_FAILURE);
    786 		}
    787 		asy->asy_flags &= ~ASY_DDI_SUSPENDED;
    788 		if (async->async_flags & ASYNC_ISOPEN) {
    789 			asy_program(asy, ASY_INIT);
    790 			/* Kick off output */
    791 			if (async->async_ocnt > 0) {
    792 				async_resume(async);
    793 			} else {
    794 				mutex_exit(&asy->asy_excl_hi);
    795 				if (async->async_xmitblk)
    796 					freeb(async->async_xmitblk);
    797 				async->async_xmitblk = NULL;
    798 				async_start(async);
    799 				mutex_enter(&asy->asy_excl_hi);
    800 			}
    801 			ASYSETSOFT(asy);
    802 		}
    803 		mutex_exit(&asy->asy_excl_hi);
    804 		mutex_exit(&asy->asy_excl);
    805 		mutex_exit(&asy->asy_soft_sr);
    806 
    807 		mutex_enter(&asy->asy_excl);
    808 		if (async->async_flags & ASYNC_RESUME_BUFCALL) {
    809 			async->async_wbufcid = bufcall(async->async_wbufcds,
    810 			    BPRI_HI, (void (*)(void *)) async_reioctl,
    811 			    (void *)(intptr_t)async->async_common->asy_unit);
    812 			async->async_flags &= ~ASYNC_RESUME_BUFCALL;
    813 		}
    814 		async_process_suspq(asy);
    815 		mutex_exit(&asy->asy_excl);
    816 		return (DDI_SUCCESS);
    817 	}
    818 	default:
    819 		return (DDI_FAILURE);
    820 	}
    821 
    822 	ret = ddi_soft_state_zalloc(asy_soft_state, instance);
    823 	if (ret != DDI_SUCCESS)
    824 		return (DDI_FAILURE);
    825 	asy = ddi_get_soft_state(asy_soft_state, instance);
    826 	ASSERT(asy != NULL);	/* can't fail - we only just allocated it */
    827 	asy->asy_unit = instance;
    828 	mutex_enter(&asy_glob_lock);
    829 	if (instance > max_asy_instance)
    830 		max_asy_instance = instance;
    831 	mutex_exit(&asy_glob_lock);
    832 
    833 	regnum = asy_get_io_regnum(devi, asy);
    834 
    835 	if (regnum < 0 ||
    836 	    ddi_regs_map_setup(devi, regnum, (caddr_t *)&asy->asy_ioaddr,
    837 	    (offset_t)0, (offset_t)0, &ioattr, &asy->asy_iohandle)
    838 	    != DDI_SUCCESS) {
    839 		cmn_err(CE_WARN, "asy%d: could not map UART registers @ %p",
    840 		    instance, (void *)asy->asy_ioaddr);
    841 
    842 		asy_soft_state_free(asy);
    843 		return (DDI_FAILURE);
    844 	}
    845 
    846 	DEBUGCONT2(ASY_DEBUG_INIT, "asy%dattach: UART @ %p\n",
    847 	    instance, (void *)asy->asy_ioaddr);
    848 
    849 	mutex_enter(&asy_glob_lock);
    850 	if (com_ports == NULL) {	/* need to initialize com_ports */
    851 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, devi, 0,
    852 		    "motherboard-serial-ports", &com_ports, &num_com_ports) !=
    853 		    DDI_PROP_SUCCESS) {
    854 			/* Use our built-in COM[1234] values */
    855 			com_ports = (int *)standard_com_ports;
    856 			num_com_ports = sizeof (standard_com_ports) /
    857 			    sizeof (standard_com_ports[0]);
    858 		}
    859 		if (num_com_ports > 10) {
    860 			/* We run out of single digits for device properties */
    861 			num_com_ports = 10;
    862 			cmn_err(CE_WARN,
    863 			    "More than %d motherboard-serial-ports",
    864 			    num_com_ports);
    865 		}
    866 	}
    867 	mutex_exit(&asy_glob_lock);
    868 
    869 	/*
    870 	 * Lookup the i/o address to see if this is a standard COM port
    871 	 * in which case we assign it the correct tty[a-d] to match the
    872 	 * COM port number, or some other i/o address in which case it
    873 	 * will be assigned /dev/term/[0123...] in some rather arbitrary
    874 	 * fashion.
    875 	 */
    876 
    877 	for (i = 0; i < num_com_ports; i++) {
    878 		if (asy->asy_ioaddr == (uint8_t *)(uintptr_t)com_ports[i]) {
    879 			asy->asy_com_port = i + 1;
    880 			break;
    881 		}
    882 	}
    883 
    884 	/*
    885 	 * It appears that there was async hardware that on reset
    886 	 * did not clear ICR.  Hence when we get to
    887 	 * ddi_get_iblock_cookie below, this hardware would cause
    888 	 * the system to hang if there was input available.
    889 	 */
    890 
    891 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0x00);
    892 
    893 	/* establish default usage */
    894 	asy->asy_mcr |= RTS|DTR;		/* do use RTS/DTR after open */
    895 	asy->asy_lcr = STOP1|BITS8;		/* default to 1 stop 8 bits */
    896 	asy->asy_bidx = B9600;			/* default to 9600  */
    897 #ifdef DEBUG
    898 	asy->asy_msint_cnt = 0;			/* # of times in async_msint */
    899 #endif
    900 	mcr = 0;				/* don't enable until open */
    901 
    902 	if (asy->asy_com_port != 0) {
    903 		/*
    904 		 * For motherboard ports, emulate tty eeprom properties.
    905 		 * Actually, we can't tell if a port is motherboard or not,
    906 		 * so for "motherboard ports", read standard DOS COM ports.
    907 		 */
    908 		switch (asy_getproperty(devi, asy, "ignore-cd")) {
    909 		case 0:				/* *-ignore-cd=False */
    910 			DEBUGCONT1(ASY_DEBUG_MODEM,
    911 			    "asy%dattach: clear ASY_IGNORE_CD\n", instance);
    912 			asy->asy_flags &= ~ASY_IGNORE_CD; /* wait for cd */
    913 			break;
    914 		case 1:				/* *-ignore-cd=True */
    915 			/*FALLTHRU*/
    916 		default:			/* *-ignore-cd not defined */
    917 			/*
    918 			 * We set rather silly defaults of soft carrier on
    919 			 * and DTR/RTS raised here because it might be that
    920 			 * one of the motherboard ports is the system console.
    921 			 */
    922 			DEBUGCONT1(ASY_DEBUG_MODEM,
    923 			    "asy%dattach: set ASY_IGNORE_CD, set RTS & DTR\n",
    924 			    instance);
    925 			mcr = asy->asy_mcr;		/* rts/dtr on */
    926 			asy->asy_flags |= ASY_IGNORE_CD;	/* ignore cd */
    927 			break;
    928 		}
    929 
    930 		/* Property for not raising DTR/RTS */
    931 		switch (asy_getproperty(devi, asy, "rts-dtr-off")) {
    932 		case 0:				/* *-rts-dtr-off=False */
    933 			asy->asy_flags |= ASY_RTS_DTR_OFF;	/* OFF */
    934 			mcr = asy->asy_mcr;		/* rts/dtr on */
    935 			DEBUGCONT1(ASY_DEBUG_MODEM, "asy%dattach: "
    936 			    "ASY_RTS_DTR_OFF set and DTR & RTS set\n",
    937 			    instance);
    938 			break;
    939 		case 1:				/* *-rts-dtr-off=True */
    940 			/*FALLTHRU*/
    941 		default:			/* *-rts-dtr-off undefined */
    942 			break;
    943 		}
    944 
    945 		/* Parse property for tty modes */
    946 		asy_parse_mode(devi, asy);
    947 	} else {
    948 		DEBUGCONT1(ASY_DEBUG_MODEM,
    949 		    "asy%dattach: clear ASY_IGNORE_CD, clear RTS & DTR\n",
    950 		    instance);
    951 		asy->asy_flags &= ~ASY_IGNORE_CD;	/* wait for cd */
    952 	}
    953 
    954 	/*
    955 	 * Initialize the port with default settings.
    956 	 */
    957 
    958 	asy->asy_fifo_buf = 1;
    959 	asy->asy_use_fifo = FIFO_OFF;
    960 
    961 	/*
    962 	 * Get icookie for mutexes initialization
    963 	 */
    964 	if ((ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) !=
    965 	    DDI_SUCCESS) ||
    966 	    (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_MED,
    967 	    &asy_soft_iblock) != DDI_SUCCESS)) {
    968 		ddi_regs_map_free(&asy->asy_iohandle);
    969 		cmn_err(CE_CONT,
    970 		    "asy%d: could not hook interrupt for UART @ %p\n",
    971 		    instance, (void *)asy->asy_ioaddr);
    972 		asy_soft_state_free(asy);
    973 		return (DDI_FAILURE);
    974 	}
    975 
    976 	/*
    977 	 * Initialize mutexes before accessing the hardware
    978 	 */
    979 	mutex_init(&asy->asy_excl, NULL, MUTEX_DRIVER, asy_soft_iblock);
    980 	mutex_init(&asy->asy_excl_hi, NULL, MUTEX_DRIVER,
    981 	    (void *)asy->asy_iblock);
    982 	mutex_init(&asy->asy_soft_sr, NULL, MUTEX_DRIVER, asy_soft_iblock);
    983 
    984 	mutex_enter(&asy->asy_excl);
    985 	mutex_enter(&asy->asy_excl_hi);
    986 
    987 	if (asy_identify_chip(devi, asy) != DDI_SUCCESS) {
    988 		mutex_exit(&asy->asy_excl_hi);
    989 		mutex_exit(&asy->asy_excl);
    990 		mutex_destroy(&asy->asy_excl);
    991 		mutex_destroy(&asy->asy_excl_hi);
    992 		mutex_destroy(&asy->asy_soft_sr);
    993 		ddi_regs_map_free(&asy->asy_iohandle);
    994 		cmn_err(CE_CONT, "Cannot identify UART chip at %p\n",
    995 		    (void *)asy->asy_ioaddr);
    996 		asy_soft_state_free(asy);
    997 		return (DDI_FAILURE);
    998 	}
    999 
   1000 	/* disable all interrupts */
   1001 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
   1002 	/* select baud rate generator */
   1003 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, DLAB);
   1004 	/* Set the baud rate to 9600 */
   1005 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLL),
   1006 	    asyspdtab[asy->asy_bidx] & 0xff);
   1007 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLH),
   1008 	    (asyspdtab[asy->asy_bidx] >> 8) & 0xff);
   1009 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, asy->asy_lcr);
   1010 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr);
   1011 
   1012 	mutex_exit(&asy->asy_excl_hi);
   1013