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)®list, ®len) != 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)®list, ®len) != 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