10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
54525Skchow * Common Development and Distribution License (the "License").
64525Skchow * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*7656SSherry.Moore@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate * Serial I/O driver for Z8530 chips
280Sstevel@tonic-gate */
290Sstevel@tonic-gate
300Sstevel@tonic-gate
310Sstevel@tonic-gate #include <sys/types.h>
320Sstevel@tonic-gate #include <sys/param.h>
330Sstevel@tonic-gate #include <sys/systm.h>
340Sstevel@tonic-gate #include <sys/sysmacros.h>
350Sstevel@tonic-gate #include <sys/stropts.h>
360Sstevel@tonic-gate #include <sys/stream.h>
370Sstevel@tonic-gate #include <sys/stat.h>
380Sstevel@tonic-gate #include <sys/mkdev.h>
390Sstevel@tonic-gate #include <sys/cmn_err.h>
400Sstevel@tonic-gate #include <sys/errno.h>
410Sstevel@tonic-gate #include <sys/kmem.h>
420Sstevel@tonic-gate #include <sys/zsdev.h>
430Sstevel@tonic-gate #include <sys/debug.h>
440Sstevel@tonic-gate #include <sys/machsystm.h>
450Sstevel@tonic-gate
460Sstevel@tonic-gate #include <sys/conf.h>
470Sstevel@tonic-gate #include <sys/sunddi.h>
480Sstevel@tonic-gate #include <sys/errno.h>
490Sstevel@tonic-gate
500Sstevel@tonic-gate #define ZS_TRACING
510Sstevel@tonic-gate #ifdef ZS_TRACING
520Sstevel@tonic-gate #include <sys/vtrace.h>
530Sstevel@tonic-gate
540Sstevel@tonic-gate /*
550Sstevel@tonic-gate * Temp tracepoint definitions
560Sstevel@tonic-gate */
570Sstevel@tonic-gate #define TR_FAC_ZS 51
580Sstevel@tonic-gate
590Sstevel@tonic-gate #define TR_ZS_H_INT_START 1
600Sstevel@tonic-gate #define TR_ZS_H_INT_END 2
610Sstevel@tonic-gate #define TR_ZS_INT_START 3
620Sstevel@tonic-gate #define TR_ZS_INT_END 4
630Sstevel@tonic-gate
640Sstevel@tonic-gate #define TR_FAC_ZS_INT 52
650Sstevel@tonic-gate #define TR_READ_START 1
660Sstevel@tonic-gate #define TR_READ_END 2
670Sstevel@tonic-gate
680Sstevel@tonic-gate #endif /* ZSH_TRACING */
690Sstevel@tonic-gate
700Sstevel@tonic-gate #define KIOIP KSTAT_INTR_PTR(zs->intrstats)
710Sstevel@tonic-gate
720Sstevel@tonic-gate #ifndef MAXZS
730Sstevel@tonic-gate #define MAXZS 4
740Sstevel@tonic-gate #endif
750Sstevel@tonic-gate int maxzs = MAXZS;
760Sstevel@tonic-gate
770Sstevel@tonic-gate int nzs = 0;
780Sstevel@tonic-gate
790Sstevel@tonic-gate struct zscom *zscom;
800Sstevel@tonic-gate struct zscom *zscurr;
810Sstevel@tonic-gate struct zscom *zslast;
820Sstevel@tonic-gate struct zs_prog *zs_prog;
830Sstevel@tonic-gate char *zssoftCAR;
840Sstevel@tonic-gate int zs_watchdog_count = 10; /* countdown to determine if tx hung */
850Sstevel@tonic-gate
860Sstevel@tonic-gate int zs_drain_check = 15000000; /* tunable: exit drain check time */
870Sstevel@tonic-gate
880Sstevel@tonic-gate #ifdef ZS_DEBUG
890Sstevel@tonic-gate char zs_h_log[ZS_H_LOG_MAX +10];
900Sstevel@tonic-gate int zs_h_log_n = 0;
910Sstevel@tonic-gate #define zs_h_log_add(c) \
920Sstevel@tonic-gate { \
930Sstevel@tonic-gate if (zs_h_log_n >= ZS_H_LOG_MAX) \
940Sstevel@tonic-gate zs_h_log_n = 0; \
950Sstevel@tonic-gate zs_h_log[zs_h_log_n++] = c; \
960Sstevel@tonic-gate zs_h_log[zs_h_log_n] = '\0'; \
970Sstevel@tonic-gate }
980Sstevel@tonic-gate
990Sstevel@tonic-gate #else /* NO_ZS_DEBUG */
1000Sstevel@tonic-gate #define zs_h_log_add(c)
1010Sstevel@tonic-gate #endif /* ZS_DEBUG */
1020Sstevel@tonic-gate
1030Sstevel@tonic-gate
1040Sstevel@tonic-gate /*
1050Sstevel@tonic-gate * Driver data
1060Sstevel@tonic-gate */
1070Sstevel@tonic-gate
1080Sstevel@tonic-gate #define GETPROP(dip, str, defval) \
1090Sstevel@tonic-gate ddi_getprop(DDI_DEV_T_ANY, (dip), DDI_PROP_DONTPASS, (str), (defval))
1100Sstevel@tonic-gate
1110Sstevel@tonic-gate int zs_usec_delay = 1;
1120Sstevel@tonic-gate int zssoftpend;
1130Sstevel@tonic-gate ddi_softintr_t zs_softintr_id;
1140Sstevel@tonic-gate time_t default_dtrlow = 3; /* hold dtr low nearly this long on close */
1150Sstevel@tonic-gate static ddi_iblock_cookie_t zs_iblock;
1160Sstevel@tonic-gate static ddi_iblock_cookie_t zs_hi_iblock;
1170Sstevel@tonic-gate static int zs_addedsoft = 0;
1180Sstevel@tonic-gate
1190Sstevel@tonic-gate
1200Sstevel@tonic-gate /*
1210Sstevel@tonic-gate * Driver information for auto-configuration stuff.
1220Sstevel@tonic-gate */
1230Sstevel@tonic-gate
1240Sstevel@tonic-gate static int zsprobe(dev_info_t *dev);
1250Sstevel@tonic-gate static int zsattach(dev_info_t *dev, ddi_attach_cmd_t cmd);
1260Sstevel@tonic-gate static int zsdetach(dev_info_t *dev, ddi_detach_cmd_t cmd);
1270Sstevel@tonic-gate void zsopinit(struct zscom *zs, struct zsops *zso);
1280Sstevel@tonic-gate
1290Sstevel@tonic-gate static void zsnull_intr(struct zscom *zs);
1300Sstevel@tonic-gate static int zsnull_softint(struct zscom *zs);
1310Sstevel@tonic-gate static int zsnull_suspend(struct zscom *zs);
1320Sstevel@tonic-gate static int zsnull_resume(struct zscom *zs);
1330Sstevel@tonic-gate
1340Sstevel@tonic-gate struct zsops zsops_null = {
1350Sstevel@tonic-gate zsnull_intr,
1360Sstevel@tonic-gate zsnull_intr,
1370Sstevel@tonic-gate zsnull_intr,
1380Sstevel@tonic-gate zsnull_intr,
1390Sstevel@tonic-gate zsnull_softint,
1400Sstevel@tonic-gate zsnull_suspend,
1410Sstevel@tonic-gate zsnull_resume
1420Sstevel@tonic-gate };
1430Sstevel@tonic-gate
1440Sstevel@tonic-gate extern struct streamtab asynctab; /* default -- from zs_async.c */
1450Sstevel@tonic-gate
1460Sstevel@tonic-gate uint_t zs_high_intr(caddr_t argzs);
1470Sstevel@tonic-gate uint_t zsintr(caddr_t intarg);
1480Sstevel@tonic-gate
1490Sstevel@tonic-gate extern int zsc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
1500Sstevel@tonic-gate void **result);
1510Sstevel@tonic-gate
1520Sstevel@tonic-gate extern int ddi_create_internal_pathname(dev_info_t *dip, char *name,
1530Sstevel@tonic-gate int spec_type, minor_t minor_num);
1540Sstevel@tonic-gate
1550Sstevel@tonic-gate extern struct streamtab zsstab;
1560Sstevel@tonic-gate int zssoftpend; /* soft interrupt pending */
1570Sstevel@tonic-gate kmutex_t zs_soft_lock; /* adapt.lock,to use to protect data */
1580Sstevel@tonic-gate /* common to sev. streams or ports */
1590Sstevel@tonic-gate kmutex_t zs_curr_lock; /* lock protecting zscurr */
1600Sstevel@tonic-gate
1610Sstevel@tonic-gate extern kcondvar_t lbolt_cv;
1620Sstevel@tonic-gate
1630Sstevel@tonic-gate /*
1640Sstevel@tonic-gate * curently the only spin lock level 12 for all ocasions
1650Sstevel@tonic-gate */
1660Sstevel@tonic-gate
1670Sstevel@tonic-gate #define ZSS_CONF_FLAG (D_NEW | D_MP)
1680Sstevel@tonic-gate
1690Sstevel@tonic-gate static struct cb_ops cb_zs_ops = {
1700Sstevel@tonic-gate nulldev, /* cb_open */
1710Sstevel@tonic-gate nulldev, /* cb_close */
1720Sstevel@tonic-gate nodev, /* cb_strategy */
1730Sstevel@tonic-gate nodev, /* cb_print */
1740Sstevel@tonic-gate nodev, /* cb_dump */
1750Sstevel@tonic-gate nodev, /* cb_read */
1760Sstevel@tonic-gate nodev, /* cb_write */
1770Sstevel@tonic-gate nodev, /* cb_ioctl */
1780Sstevel@tonic-gate nodev, /* cb_devmap */
1790Sstevel@tonic-gate nodev, /* cb_mmap */
1800Sstevel@tonic-gate nodev, /* cb_segmap */
1810Sstevel@tonic-gate nochpoll, /* cb_chpoll */
1820Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */
1830Sstevel@tonic-gate &asynctab, /* cb_stream */
1840Sstevel@tonic-gate (int)(ZSS_CONF_FLAG) /* cb_flag */
1850Sstevel@tonic-gate };
1860Sstevel@tonic-gate
1870Sstevel@tonic-gate struct dev_ops zs_ops = {
1880Sstevel@tonic-gate DEVO_REV, /* devo_rev */
1890Sstevel@tonic-gate 0, /* devo_refcnt */
1900Sstevel@tonic-gate zsc_info, /* devo_getinfo */
1910Sstevel@tonic-gate nulldev, /* devo_identify */
1920Sstevel@tonic-gate zsprobe, /* devo_probe */
1930Sstevel@tonic-gate zsattach, /* devo_attach */
1940Sstevel@tonic-gate zsdetach, /* devo_detach */
1950Sstevel@tonic-gate nodev, /* devo_reset */
1960Sstevel@tonic-gate &cb_zs_ops, /* devo_cb_ops */
1970Sstevel@tonic-gate (struct bus_ops *)NULL, /* devo_bus_ops */
198*7656SSherry.Moore@Sun.COM ddi_power, /* devo_power */
199*7656SSherry.Moore@Sun.COM ddi_quiesce_not_supported, /* devo_quiesce */
2000Sstevel@tonic-gate };
2010Sstevel@tonic-gate
2020Sstevel@tonic-gate
2030Sstevel@tonic-gate /*
2040Sstevel@tonic-gate * This is the loadable module wrapper.
2050Sstevel@tonic-gate */
2060Sstevel@tonic-gate
2070Sstevel@tonic-gate #include <sys/modctl.h>
2080Sstevel@tonic-gate
2090Sstevel@tonic-gate /*
2100Sstevel@tonic-gate * Module linkage information for the kernel.
2110Sstevel@tonic-gate */
2120Sstevel@tonic-gate
2130Sstevel@tonic-gate extern struct mod_ops mod_driverops;
2140Sstevel@tonic-gate
2150Sstevel@tonic-gate static struct modldrv modldrv = {
2160Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */
217*7656SSherry.Moore@Sun.COM "Z8530 serial driver",
2180Sstevel@tonic-gate &zs_ops, /* driver ops */
2190Sstevel@tonic-gate };
2200Sstevel@tonic-gate
2210Sstevel@tonic-gate static struct modlinkage modlinkage = {
2220Sstevel@tonic-gate MODREV_1, (void *)&modldrv, NULL
2230Sstevel@tonic-gate };
2240Sstevel@tonic-gate
2250Sstevel@tonic-gate int
_init(void)2260Sstevel@tonic-gate _init(void)
2270Sstevel@tonic-gate {
2280Sstevel@tonic-gate return (mod_install(&modlinkage));
2290Sstevel@tonic-gate }
2300Sstevel@tonic-gate
2310Sstevel@tonic-gate int
_fini(void)2320Sstevel@tonic-gate _fini(void)
2330Sstevel@tonic-gate {
2340Sstevel@tonic-gate return (EBUSY);
2350Sstevel@tonic-gate }
2360Sstevel@tonic-gate
2370Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2380Sstevel@tonic-gate _info(struct modinfo *modinfop)
2390Sstevel@tonic-gate {
2400Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop));
2410Sstevel@tonic-gate }
2420Sstevel@tonic-gate
2430Sstevel@tonic-gate static int
zsprobe(dev_info_t * dev)2440Sstevel@tonic-gate zsprobe(dev_info_t *dev)
2450Sstevel@tonic-gate {
2460Sstevel@tonic-gate struct zscc_device *zsaddr;
2470Sstevel@tonic-gate int rval;
2480Sstevel@tonic-gate auto char c;
2490Sstevel@tonic-gate
2500Sstevel@tonic-gate rval = DDI_PROBE_FAILURE;
2510Sstevel@tonic-gate
2520Sstevel@tonic-gate /*
2530Sstevel@tonic-gate * temporarily map in registers
2540Sstevel@tonic-gate */
2550Sstevel@tonic-gate if (ddi_map_regs(dev, 0, (caddr_t *)&zsaddr, 0, 0)) {
2560Sstevel@tonic-gate cmn_err(CE_WARN, "zsprobe: unable to map registers");
2570Sstevel@tonic-gate return (rval);
2580Sstevel@tonic-gate }
2590Sstevel@tonic-gate
2600Sstevel@tonic-gate /*
2610Sstevel@tonic-gate * NON-DDI Compliant call
2620Sstevel@tonic-gate */
2630Sstevel@tonic-gate mon_clock_stop();
2640Sstevel@tonic-gate
2650Sstevel@tonic-gate /*
2660Sstevel@tonic-gate * get in sync with the chip
2670Sstevel@tonic-gate */
2680Sstevel@tonic-gate
2690Sstevel@tonic-gate if (ddi_peek8(dev, (char *)&zsaddr->zscc_control, &c) != DDI_SUCCESS) {
2700Sstevel@tonic-gate goto out;
2710Sstevel@tonic-gate }
2720Sstevel@tonic-gate drv_usecwait(2);
2730Sstevel@tonic-gate
2740Sstevel@tonic-gate /*
2750Sstevel@tonic-gate * The traditional test for the presence of an 8530 has been to write
2760Sstevel@tonic-gate * a 15 (octal 017) to its control register address, then read it back.
2770Sstevel@tonic-gate * A Z8530 will respond to this with the contents of Read-Register 15.
2780Sstevel@tonic-gate * If this address were memory, or something else, we would expect to
2790Sstevel@tonic-gate * see the 15 again. Normally, the contents of RR15 will be entirely
2800Sstevel@tonic-gate * different. A Z8530 does not use the D0 and D2 bits of register 15,
2810Sstevel@tonic-gate * so they should equal zero. Compatable chips should do the same.
2820Sstevel@tonic-gate * Beware of "enhanced" SCC's that do not guarantee this.
2830Sstevel@tonic-gate */
2840Sstevel@tonic-gate if (ddi_poke8(dev, (char *)&zsaddr->zscc_control, '\017')
2850Sstevel@tonic-gate != DDI_SUCCESS) {
2860Sstevel@tonic-gate goto out;
2870Sstevel@tonic-gate }
2880Sstevel@tonic-gate drv_usecwait(2);
2890Sstevel@tonic-gate if (ddi_peek8(dev, (char *)&zsaddr->zscc_control, &c) != DDI_SUCCESS) {
2900Sstevel@tonic-gate goto out;
2910Sstevel@tonic-gate }
2920Sstevel@tonic-gate drv_usecwait(2);
2930Sstevel@tonic-gate if (c & 5) {
2940Sstevel@tonic-gate goto out;
2950Sstevel@tonic-gate }
2960Sstevel@tonic-gate
2970Sstevel@tonic-gate rval = DDI_PROBE_SUCCESS;
2980Sstevel@tonic-gate
2990Sstevel@tonic-gate out:
3000Sstevel@tonic-gate /*
3010Sstevel@tonic-gate * NON-DDI Compliant call
3020Sstevel@tonic-gate */
3030Sstevel@tonic-gate mon_clock_start();
3040Sstevel@tonic-gate
3050Sstevel@tonic-gate ddi_unmap_regs(dev, 0, (caddr_t *)&zsaddr, 0, 0);
3060Sstevel@tonic-gate return (rval);
3070Sstevel@tonic-gate }
3080Sstevel@tonic-gate
3090Sstevel@tonic-gate /*ARGSUSED*/
3100Sstevel@tonic-gate static int
zsattach(dev_info_t * dev,ddi_attach_cmd_t cmd)3110Sstevel@tonic-gate zsattach(dev_info_t *dev, ddi_attach_cmd_t cmd)
3120Sstevel@tonic-gate {
3130Sstevel@tonic-gate struct zscom *zs;
3140Sstevel@tonic-gate int loops, i;
3150Sstevel@tonic-gate uint_t s;
3160Sstevel@tonic-gate int rtsdtr_bits = 0;
3170Sstevel@tonic-gate char softcd;
3180Sstevel@tonic-gate uchar_t rr;
3190Sstevel@tonic-gate short speed[2];
3200Sstevel@tonic-gate int current_chip = ddi_get_instance(dev);
3210Sstevel@tonic-gate struct zscc_device *tmpzs; /* for mapping purposes */
3220Sstevel@tonic-gate char name[16];
3230Sstevel@tonic-gate int keyboard_prop;
3240Sstevel@tonic-gate
3250Sstevel@tonic-gate switch (cmd) {
3260Sstevel@tonic-gate case DDI_ATTACH:
3270Sstevel@tonic-gate break;
3280Sstevel@tonic-gate
3290Sstevel@tonic-gate case DDI_RESUME:
3300Sstevel@tonic-gate zs = &zscom[current_chip*2];
3310Sstevel@tonic-gate /*
3320Sstevel@tonic-gate * Try to resume first channel
3330Sstevel@tonic-gate */
3340Sstevel@tonic-gate if (!zs->zs_resume || (zs->zs_resume)(zs) != DDI_SUCCESS)
3350Sstevel@tonic-gate return (DDI_FAILURE);
3360Sstevel@tonic-gate /*
3370Sstevel@tonic-gate * And the second channel
3380Sstevel@tonic-gate */
3390Sstevel@tonic-gate zs++;
3400Sstevel@tonic-gate if (!zs->zs_resume || (zs->zs_resume)(zs) != DDI_SUCCESS) {
3410Sstevel@tonic-gate zs--;
3420Sstevel@tonic-gate if (!zs->zs_suspend ||
3430Sstevel@tonic-gate (zs->zs_suspend)(zs) != DDI_SUCCESS)
3440Sstevel@tonic-gate cmn_err(CE_WARN,
3450Sstevel@tonic-gate "zs: inconsistent suspend/resume state");
3460Sstevel@tonic-gate return (DDI_FAILURE);
3470Sstevel@tonic-gate }
3480Sstevel@tonic-gate return (DDI_SUCCESS);
3490Sstevel@tonic-gate
3500Sstevel@tonic-gate default:
3510Sstevel@tonic-gate return (DDI_FAILURE);
3520Sstevel@tonic-gate }
3530Sstevel@tonic-gate
3540Sstevel@tonic-gate if (zscom == NULL) {
3550Sstevel@tonic-gate mutex_init(&zs_soft_lock, NULL, MUTEX_DRIVER, (void *)ZS_PL);
3560Sstevel@tonic-gate mutex_init(&zs_curr_lock, NULL, MUTEX_DRIVER, (void *)ZS_PL_HI);
3570Sstevel@tonic-gate zscom = kmem_zalloc(maxzs * sizeof (struct zscom), KM_SLEEP);
3580Sstevel@tonic-gate zs_prog = kmem_zalloc(maxzs * sizeof (struct zs_prog),
3590Sstevel@tonic-gate KM_SLEEP);
3600Sstevel@tonic-gate zssoftCAR = kmem_zalloc(maxzs, KM_SLEEP);
3610Sstevel@tonic-gate /* don't set nzs until arrays are allocated */
3620Sstevel@tonic-gate membar_producer();
3630Sstevel@tonic-gate nzs = maxzs;
3640Sstevel@tonic-gate zscurr = &zscom[(current_chip*2) + 1];
3650Sstevel@tonic-gate zslast = &zscom[current_chip*2];
3660Sstevel@tonic-gate i = GETPROP(dev, "zs-usec-delay", 0);
3670Sstevel@tonic-gate zs_usec_delay = (i <= 0) ? 1 : i;
3680Sstevel@tonic-gate }
3690Sstevel@tonic-gate
3700Sstevel@tonic-gate if (2 * current_chip >= maxzs) {
3710Sstevel@tonic-gate cmn_err(CE_WARN,
3720Sstevel@tonic-gate "zs: unable to allocate resources for chip %d.",
3730Sstevel@tonic-gate current_chip);
3740Sstevel@tonic-gate cmn_err(CE_WARN, "Change zs:maxzs in /etc/system");
3750Sstevel@tonic-gate return (DDI_FAILURE);
3760Sstevel@tonic-gate }
3770Sstevel@tonic-gate zs = &zscom[current_chip*2];
3780Sstevel@tonic-gate
3790Sstevel@tonic-gate /*
3800Sstevel@tonic-gate * map in the device registers
3810Sstevel@tonic-gate */
3820Sstevel@tonic-gate if (ddi_map_regs(dev, 0, (caddr_t *)&zs->zs_addr, 0, 0)) {
3830Sstevel@tonic-gate cmn_err(CE_WARN, "zs%d: unable to map registers\n",
3840Sstevel@tonic-gate current_chip);
3850Sstevel@tonic-gate return (DDI_FAILURE);
3860Sstevel@tonic-gate }
3870Sstevel@tonic-gate
3880Sstevel@tonic-gate tmpzs = zs->zs_addr;
3890Sstevel@tonic-gate
3900Sstevel@tonic-gate /*
3910Sstevel@tonic-gate * Non-DDI compliant Sun-Ness specfic call(s)
3920Sstevel@tonic-gate */
3930Sstevel@tonic-gate
3940Sstevel@tonic-gate /*
3950Sstevel@tonic-gate * Stop the monitor's polling interrupt.
3960Sstevel@tonic-gate *
3970Sstevel@tonic-gate * I know that this is not exactly obvious. On all sunmon PROM
3980Sstevel@tonic-gate * machines, the PROM has can have a high level periodic clock
3990Sstevel@tonic-gate * interrupt going at this time. It uses this periodic interrupt
4000Sstevel@tonic-gate * to poll the console tty or kbd uart to check for things like
4010Sstevel@tonic-gate * BREAK or L1-A (abort). While we're probing this device out we
4020Sstevel@tonic-gate * have to shut that off so the PROM won't get confused by what
4030Sstevel@tonic-gate * we're doing to the zs. This has caused some pretty funny bugs
4040Sstevel@tonic-gate * in its time.
4050Sstevel@tonic-gate *
4060Sstevel@tonic-gate * For OPENPROM machines, the prom takes level12 interrupts directly,
4070Sstevel@tonic-gate * but we call this routine anyway (I forget why).
4080Sstevel@tonic-gate */
4090Sstevel@tonic-gate mon_clock_stop();
4100Sstevel@tonic-gate
4110Sstevel@tonic-gate /*
4120Sstevel@tonic-gate * Go critical to keep uart from urking.
4130Sstevel@tonic-gate */
4140Sstevel@tonic-gate s = ddi_enter_critical();
4150Sstevel@tonic-gate
4160Sstevel@tonic-gate /*
4170Sstevel@tonic-gate * We are about to issue a full reset to this chip.
4180Sstevel@tonic-gate * First, now that interrupts are blocked, we will delay up to a
4190Sstevel@tonic-gate * half-second, checking both channels for any stray activity.
4200Sstevel@tonic-gate * Next we will preserve the time constants from both channels,
4210Sstevel@tonic-gate * so that they can be restored after the reset. This is especially
4220Sstevel@tonic-gate * important for the console device. Finally, do the reset and
4230Sstevel@tonic-gate * follow it with an extended recovery while the chip settles down.
4240Sstevel@tonic-gate */
4250Sstevel@tonic-gate for (loops = 0; loops++ <= 500; DELAY(1000)) {
4260Sstevel@tonic-gate SCC_READA(1, rr);
4270Sstevel@tonic-gate if ((rr & ZSRR1_ALL_SENT) == 0) continue;
4280Sstevel@tonic-gate SCC_READB(1, rr);
4290Sstevel@tonic-gate if ((rr & ZSRR1_ALL_SENT) == 0) continue;
4300Sstevel@tonic-gate SCC_READA(0, rr);
4310Sstevel@tonic-gate if ((rr & ZSRR0_TX_READY) == 0) continue;
4320Sstevel@tonic-gate SCC_READB(0, rr);
4330Sstevel@tonic-gate if ((rr & ZSRR0_TX_READY) != 0) break;
4340Sstevel@tonic-gate }
4350Sstevel@tonic-gate
4360Sstevel@tonic-gate SCC_READA(12, speed[0]);
4370Sstevel@tonic-gate SCC_READA(13, rr);
4380Sstevel@tonic-gate speed[0] |= rr << 8;
4390Sstevel@tonic-gate SCC_READB(12, speed[1]);
4400Sstevel@tonic-gate SCC_READB(13, rr);
4410Sstevel@tonic-gate speed[1] |= rr << 8;
4420Sstevel@tonic-gate
4430Sstevel@tonic-gate SCC_WRITE(9, ZSWR9_RESET_WORLD);
4440Sstevel@tonic-gate DELAY(10);
4450Sstevel@tonic-gate
4460Sstevel@tonic-gate /*
4470Sstevel@tonic-gate * Set up the other components of the zscom structs for this chip.
4480Sstevel@tonic-gate */
4490Sstevel@tonic-gate for (i = 0; i < 2; i++) {
4500Sstevel@tonic-gate /*
4510Sstevel@tonic-gate * Property for ignoring DCD.
4520Sstevel@tonic-gate * We switch between 'a' and 'b' ports for this device.
4530Sstevel@tonic-gate */
4544525Skchow static char prop[] = "port-a-ignore-cd";
4550Sstevel@tonic-gate
4560Sstevel@tonic-gate /*
4570Sstevel@tonic-gate * For this channel, set the hardware address, allocate the
4580Sstevel@tonic-gate * high-level mutex, and update the zscurr pointer.
4590Sstevel@tonic-gate * The high-level lock is shared by both channels because
4600Sstevel@tonic-gate * 8530 register addressing is non-atomic and asymetrical.
4610Sstevel@tonic-gate * Multiple threads crossing paths during this operation
4620Sstevel@tonic-gate * could trash the chip, and thus, possibly the system console.
4630Sstevel@tonic-gate */
4640Sstevel@tonic-gate if (i == 0) { /* port A */
4650Sstevel@tonic-gate zs->zs_addr = (struct zscc_device *)
4660Sstevel@tonic-gate ((uintptr_t)tmpzs | ZSOFF);
4670Sstevel@tonic-gate (zs+1)->zs_excl_hi = zs->zs_excl_hi = &zs_curr_lock;
4680Sstevel@tonic-gate } else { /* port B */
4690Sstevel@tonic-gate zs++;
4700Sstevel@tonic-gate zs->zs_addr = (struct zscc_device *)
4710Sstevel@tonic-gate ((uintptr_t)tmpzs & ~ZSOFF);
4720Sstevel@tonic-gate zscurr = zs;
4730Sstevel@tonic-gate }
4740Sstevel@tonic-gate zs->zs_unit = current_chip * 2 + i;
4750Sstevel@tonic-gate zs->zs_dip = dev;
4760Sstevel@tonic-gate zs->zs_excl = kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
4770Sstevel@tonic-gate mutex_init(zs->zs_excl, NULL, MUTEX_DRIVER, (void *)ZS_PL);
4780Sstevel@tonic-gate zs->zs_ocexcl = kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
4790Sstevel@tonic-gate mutex_init(zs->zs_ocexcl, NULL, MUTEX_DRIVER, (void *)ZS_PL);
4800Sstevel@tonic-gate
4810Sstevel@tonic-gate zsopinit(zs, &zsops_null);
4820Sstevel@tonic-gate
4830Sstevel@tonic-gate prop[5] = 'a' + i;
4840Sstevel@tonic-gate softcd = GETPROP((dev_info_t *)(dev), prop, 0);
4850Sstevel@tonic-gate zssoftCAR[zs->zs_unit] = softcd;
4860Sstevel@tonic-gate if (softcd)
4870Sstevel@tonic-gate rtsdtr_bits = ZSWR5_RTS | ZSWR5_DTR;
4880Sstevel@tonic-gate
4890Sstevel@tonic-gate keyboard_prop = GETPROP((dev_info_t *)(zs->zs_dip),
4900Sstevel@tonic-gate "keyboard", 0);
4910Sstevel@tonic-gate
4920Sstevel@tonic-gate mutex_enter(&zs_curr_lock);
4930Sstevel@tonic-gate
4940Sstevel@tonic-gate /*
4950Sstevel@tonic-gate * Set up the default asynch modes
4960Sstevel@tonic-gate * so the monitor will still work
4970Sstevel@tonic-gate */
4980Sstevel@tonic-gate SCC_WRITE(4, ZSWR4_PARITY_EVEN | ZSWR4_1_STOP | ZSWR4_X16_CLK);
4990Sstevel@tonic-gate SCC_WRITE(3, ZSWR3_RX_8);
5000Sstevel@tonic-gate SCC_WRITE(11, ZSWR11_TXCLK_BAUD | ZSWR11_RXCLK_BAUD);
5010Sstevel@tonic-gate SCC_WRITE(12, (speed[i] & 0xff));
5020Sstevel@tonic-gate SCC_WRITE(13, (speed[i] >> 8) & 0xff);
5030Sstevel@tonic-gate SCC_WRITE(14, ZSWR14_BAUD_FROM_PCLK);
5040Sstevel@tonic-gate SCC_WRITE(3, ZSWR3_RX_8 | ZSWR3_RX_ENABLE);
5050Sstevel@tonic-gate SCC_WRITE(5, ZSWR5_TX_ENABLE | ZSWR5_TX_8 | rtsdtr_bits);
5060Sstevel@tonic-gate SCC_WRITE(14, ZSWR14_BAUD_ENA | ZSWR14_BAUD_FROM_PCLK);
5070Sstevel@tonic-gate
5080Sstevel@tonic-gate /*
5090Sstevel@tonic-gate * The SYNC pin on the second SCC (keyboard & mouse) may not
5100Sstevel@tonic-gate * be connected and noise creates transitions on this line.
5110Sstevel@tonic-gate * This floods the system with interrupts, unless the
5120Sstevel@tonic-gate * Sync/Hunt Interrupt Enable is cleared. So write
5130Sstevel@tonic-gate * register 15 with everything we need but that one.
5140Sstevel@tonic-gate */
5150Sstevel@tonic-gate if (keyboard_prop) {
5160Sstevel@tonic-gate SCC_WRITE(15, ZSR15_BREAK | ZSR15_TX_UNDER |
5170Sstevel@tonic-gate ZSR15_CTS | ZSR15_CD);
5180Sstevel@tonic-gate }
5190Sstevel@tonic-gate
5200Sstevel@tonic-gate SCC_WRITE0(ZSWR0_RESET_ERRORS);
5210Sstevel@tonic-gate SCC_WRITE0(ZSWR0_RESET_STATUS);
5220Sstevel@tonic-gate mutex_exit(&zs_curr_lock);
5230Sstevel@tonic-gate
5240Sstevel@tonic-gate zs->zs_dtrlow = gethrestime_sec() - default_dtrlow;
5250Sstevel@tonic-gate cv_init(&zs->zs_flags_cv, NULL, CV_DEFAULT, NULL);
5260Sstevel@tonic-gate zsa_init(zs);
5270Sstevel@tonic-gate }
5280Sstevel@tonic-gate
5290Sstevel@tonic-gate mutex_enter(&zs_curr_lock);
5300Sstevel@tonic-gate SCC_WRITE(9, ZSWR9_MASTER_IE | ZSWR9_VECTOR_INCL_STAT);
5310Sstevel@tonic-gate DELAY(4000);
5320Sstevel@tonic-gate mutex_exit(&zs_curr_lock);
5330Sstevel@tonic-gate
5340Sstevel@tonic-gate /*
5350Sstevel@tonic-gate * Two levels of interrupt - chip interrupts at a high level (12),
5360Sstevel@tonic-gate * (which is seen below as zs_high_intr), and then as a secondary
5370Sstevel@tonic-gate * stage soft interrupt as seen in zsintr below.
5380Sstevel@tonic-gate *
5390Sstevel@tonic-gate * Because zs_high_intr does a window save, as well as calls to
5400Sstevel@tonic-gate * other functions, we cannot install it as a "fast" interrupt
5410Sstevel@tonic-gate * that would execute right out of the trap window. Too bad...
5420Sstevel@tonic-gate */
5430Sstevel@tonic-gate if (ddi_add_intr(dev, (uint_t)0, &zs_hi_iblock,
5440Sstevel@tonic-gate (ddi_idevice_cookie_t *)0, zs_high_intr,
5450Sstevel@tonic-gate (caddr_t)0) != DDI_SUCCESS) {
5460Sstevel@tonic-gate cmn_err(CE_PANIC, "cannot set high level zs interrupt");
5470Sstevel@tonic-gate /*NOTREACHED*/
5480Sstevel@tonic-gate }
5490Sstevel@tonic-gate
5500Sstevel@tonic-gate if (zs_addedsoft == 0) {
5510Sstevel@tonic-gate if (ddi_add_softintr(dev, DDI_SOFTINT_HIGH, &zs_softintr_id,
5520Sstevel@tonic-gate &zs_iblock, (ddi_idevice_cookie_t *)0,
5530Sstevel@tonic-gate zsintr, (caddr_t)0) != DDI_SUCCESS) {
5540Sstevel@tonic-gate cmn_err(CE_PANIC,
5554525Skchow "cannot set second stage zs interrupt");
5560Sstevel@tonic-gate /*NOTREACHED*/
5570Sstevel@tonic-gate }
5580Sstevel@tonic-gate
5590Sstevel@tonic-gate zs_addedsoft++; /* we only need one zsintr! */
5600Sstevel@tonic-gate }
5610Sstevel@tonic-gate
5620Sstevel@tonic-gate if (zs > zslast)
5630Sstevel@tonic-gate zslast = zs;
5640Sstevel@tonic-gate
5650Sstevel@tonic-gate (void) ddi_exit_critical(s);
5660Sstevel@tonic-gate
5670Sstevel@tonic-gate /*
5680Sstevel@tonic-gate * Non-DDI compliant Sun-Ness specific call
5690Sstevel@tonic-gate */
5700Sstevel@tonic-gate mon_clock_start(); /* re-enable monitor's polling interrupt */
5710Sstevel@tonic-gate
5720Sstevel@tonic-gate if (!GETPROP(zs->zs_dip, "keyboard", 0)) {
5730Sstevel@tonic-gate static char *serial_line = DDI_NT_SERIAL_MB;
5740Sstevel@tonic-gate static char *dial_out = DDI_NT_SERIAL_MB_DO;
5750Sstevel@tonic-gate
5760Sstevel@tonic-gate /*
5770Sstevel@tonic-gate * Export names for channel a or b consconfig match...
5780Sstevel@tonic-gate * The names exported to the filesystem include the
5790Sstevel@tonic-gate * designated tty'a' type name and may not match the PROM
5800Sstevel@tonic-gate * pathname.
5810Sstevel@tonic-gate * Note the special name "obp-console-name" used in these calls.
5820Sstevel@tonic-gate * This keeps the ports and devlinks programs from seeing these
5830Sstevel@tonic-gate * names. (But allows ddi_pathname_to_dev_t to see them.)
5840Sstevel@tonic-gate * We don't need to do this if the instance number is zero,
5850Sstevel@tonic-gate * because we'll create them below, in this case.
5860Sstevel@tonic-gate */
5870Sstevel@tonic-gate
5880Sstevel@tonic-gate if (ddi_get_instance(dev) != 0) {
5890Sstevel@tonic-gate
5900Sstevel@tonic-gate /*
5910Sstevel@tonic-gate * Select a node type unused by ddi/devfs
5920Sstevel@tonic-gate */
5930Sstevel@tonic-gate static char *obp_type = "obp-console-name";
5940Sstevel@tonic-gate
5950Sstevel@tonic-gate (void) strcpy(name, "a");
5960Sstevel@tonic-gate if (ddi_create_minor_node(dev, name, S_IFCHR,
5970Sstevel@tonic-gate ddi_get_instance(dev) * 2,
5980Sstevel@tonic-gate obp_type, NULL) == DDI_FAILURE) {
5990Sstevel@tonic-gate ddi_remove_minor_node(dev, NULL);
6000Sstevel@tonic-gate return (DDI_FAILURE);
6010Sstevel@tonic-gate }
6020Sstevel@tonic-gate (void) strcpy(name, "b");
6030Sstevel@tonic-gate if (ddi_create_minor_node(dev, name, S_IFCHR,
6040Sstevel@tonic-gate (ddi_get_instance(dev) * 2) + 1,
6050Sstevel@tonic-gate obp_type, NULL) == DDI_FAILURE) {
6060Sstevel@tonic-gate ddi_remove_minor_node(dev, NULL);
6070Sstevel@tonic-gate return (DDI_FAILURE);
6080Sstevel@tonic-gate }
6090Sstevel@tonic-gate }
6100Sstevel@tonic-gate
6110Sstevel@tonic-gate /*
6120Sstevel@tonic-gate * Export normal device names...
6130Sstevel@tonic-gate */
6140Sstevel@tonic-gate (void) sprintf(name, "%c", (ddi_get_instance(dev) + 'a'));
6150Sstevel@tonic-gate if (ddi_create_minor_node(dev, name, S_IFCHR,
6160Sstevel@tonic-gate ddi_get_instance(dev) * 2,
6170Sstevel@tonic-gate serial_line, NULL) == DDI_FAILURE) {
6180Sstevel@tonic-gate ddi_remove_minor_node(dev, NULL);
6190Sstevel@tonic-gate return (DDI_FAILURE);
6200Sstevel@tonic-gate }
6210Sstevel@tonic-gate (void) sprintf(name, "%c", (ddi_get_instance(dev) + 'b'));
6220Sstevel@tonic-gate if (ddi_create_minor_node(dev, name, S_IFCHR,
6230Sstevel@tonic-gate (ddi_get_instance(dev) * 2) + 1,
6240Sstevel@tonic-gate serial_line, NULL) == DDI_FAILURE) {
6250Sstevel@tonic-gate ddi_remove_minor_node(dev, NULL);
6260Sstevel@tonic-gate return (DDI_FAILURE);
6270Sstevel@tonic-gate }
6280Sstevel@tonic-gate (void) sprintf(name, "%c,cu", (ddi_get_instance(dev) + 'a'));
6290Sstevel@tonic-gate if (ddi_create_minor_node(dev, name, S_IFCHR,
6300Sstevel@tonic-gate (ddi_get_instance(dev) * 2) | OUTLINE,
6310Sstevel@tonic-gate dial_out, NULL) == DDI_FAILURE) {
6320Sstevel@tonic-gate ddi_remove_minor_node(dev, NULL);
6330Sstevel@tonic-gate return (DDI_FAILURE);
6340Sstevel@tonic-gate }
6350Sstevel@tonic-gate (void) sprintf(name, "%c,cu", (ddi_get_instance(dev) + 'b'));
6360Sstevel@tonic-gate if (ddi_create_minor_node(dev, name, S_IFCHR,
6370Sstevel@tonic-gate ((ddi_get_instance(dev) * 2) + 1) | OUTLINE,
6380Sstevel@tonic-gate dial_out, NULL) == DDI_FAILURE) {
6390Sstevel@tonic-gate ddi_remove_minor_node(dev, NULL);
6400Sstevel@tonic-gate return (DDI_FAILURE);
6410Sstevel@tonic-gate }
6420Sstevel@tonic-gate } else {
6430Sstevel@tonic-gate
6440Sstevel@tonic-gate /*
6450Sstevel@tonic-gate * Create keyboard and mouse nodes which devfs doesn't see
6460Sstevel@tonic-gate */
6470Sstevel@tonic-gate
6480Sstevel@tonic-gate /*
6490Sstevel@tonic-gate * This set of minor nodes is for use with the consconfig_dacf
6500Sstevel@tonic-gate * module for the sun4u platforms. (See PSARC/1998/212)
6510Sstevel@tonic-gate */
6520Sstevel@tonic-gate if (ddi_create_internal_pathname(dev, "keyboard", S_IFCHR,
6530Sstevel@tonic-gate ddi_get_instance(dev) * 2) == DDI_FAILURE) {
6540Sstevel@tonic-gate ddi_remove_minor_node(dev, NULL);
6550Sstevel@tonic-gate return (DDI_FAILURE);
6560Sstevel@tonic-gate }
6570Sstevel@tonic-gate
6580Sstevel@tonic-gate if (ddi_create_internal_pathname(dev, "mouse", S_IFCHR,
6590Sstevel@tonic-gate (ddi_get_instance(dev) * 2) + 1) == DDI_FAILURE) {
6600Sstevel@tonic-gate ddi_remove_minor_node(dev, NULL);
6610Sstevel@tonic-gate return (DDI_FAILURE);
6620Sstevel@tonic-gate }
6630Sstevel@tonic-gate
6640Sstevel@tonic-gate /*
6650Sstevel@tonic-gate * These minor nodes are for use with pre-sun4u platforms.
6660Sstevel@tonic-gate * Either one set or the other will be opened by consconfig.
6670Sstevel@tonic-gate */
6680Sstevel@tonic-gate (void) strcpy(name, "a");
6690Sstevel@tonic-gate if (ddi_create_internal_pathname(dev, name, S_IFCHR,
6704525Skchow ddi_get_instance(dev) * 2) == DDI_FAILURE) {
6710Sstevel@tonic-gate ddi_remove_minor_node(dev, NULL);
6720Sstevel@tonic-gate return (DDI_FAILURE);
6730Sstevel@tonic-gate }
6740Sstevel@tonic-gate
6750Sstevel@tonic-gate (void) strcpy(name, "b");
6760Sstevel@tonic-gate if (ddi_create_internal_pathname(dev, name, S_IFCHR,
6774525Skchow (ddi_get_instance(dev) * 2) + 1) == DDI_FAILURE) {
6780Sstevel@tonic-gate ddi_remove_minor_node(dev, NULL);
6790Sstevel@tonic-gate return (DDI_FAILURE);
6800Sstevel@tonic-gate }
6810Sstevel@tonic-gate
6820Sstevel@tonic-gate }
6830Sstevel@tonic-gate
6840Sstevel@tonic-gate ddi_report_dev(dev);
6850Sstevel@tonic-gate /*
6860Sstevel@tonic-gate * Initialize power management bookkeeping; components are
6870Sstevel@tonic-gate * created idle.
6880Sstevel@tonic-gate */
6890Sstevel@tonic-gate if (pm_create_components(dev, 3) == DDI_SUCCESS) {
6900Sstevel@tonic-gate (void) pm_busy_component(dev, 0);
6910Sstevel@tonic-gate pm_set_normal_power(dev, 0, 1);
6920Sstevel@tonic-gate pm_set_normal_power(dev, 1, 1);
6930Sstevel@tonic-gate pm_set_normal_power(dev, 2, 1);
6940Sstevel@tonic-gate } else {
6950Sstevel@tonic-gate return (DDI_FAILURE);
6960Sstevel@tonic-gate }
6970Sstevel@tonic-gate
6980Sstevel@tonic-gate (void) sprintf(name, "zsc%d", current_chip);
6990Sstevel@tonic-gate zs->intrstats = kstat_create("zs", current_chip, name, "controller",
7004525Skchow KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT);
7010Sstevel@tonic-gate if (zs->intrstats) {
7020Sstevel@tonic-gate kstat_install(zs->intrstats);
7030Sstevel@tonic-gate }
7040Sstevel@tonic-gate
7050Sstevel@tonic-gate return (DDI_SUCCESS);
7060Sstevel@tonic-gate }
7070Sstevel@tonic-gate
7080Sstevel@tonic-gate static int
zsdetach(dev_info_t * dev,ddi_detach_cmd_t cmd)7090Sstevel@tonic-gate zsdetach(dev_info_t *dev, ddi_detach_cmd_t cmd)
7100Sstevel@tonic-gate {
7110Sstevel@tonic-gate struct zscom *zs;
7120Sstevel@tonic-gate int current_chip = ddi_get_instance(dev);
7130Sstevel@tonic-gate
7140Sstevel@tonic-gate switch (cmd) {
7150Sstevel@tonic-gate case DDI_DETACH:
7160Sstevel@tonic-gate return (DDI_FAILURE);
7170Sstevel@tonic-gate
7180Sstevel@tonic-gate case DDI_SUSPEND:
7190Sstevel@tonic-gate zs = &zscom[current_chip*2];
7200Sstevel@tonic-gate /*
7210Sstevel@tonic-gate * Try to suspend first channel
7220Sstevel@tonic-gate */
7230Sstevel@tonic-gate if (!zs->zs_suspend || (zs->zs_suspend)(zs) != DDI_SUCCESS)
7240Sstevel@tonic-gate return (DDI_FAILURE);
7250Sstevel@tonic-gate /*
7260Sstevel@tonic-gate * And the second channel
7270Sstevel@tonic-gate */
7280Sstevel@tonic-gate zs++;
7290Sstevel@tonic-gate if (!zs->zs_suspend || (zs->zs_suspend)(zs) != DDI_SUCCESS) {
7300Sstevel@tonic-gate zs--;
7310Sstevel@tonic-gate if (!zs->zs_resume ||
7320Sstevel@tonic-gate (zs->zs_resume)(zs) != DDI_SUCCESS)
7330Sstevel@tonic-gate cmn_err(CE_WARN,
7340Sstevel@tonic-gate "zs: inconsistent suspend/resume state");
7350Sstevel@tonic-gate return (DDI_FAILURE);
7360Sstevel@tonic-gate }
7370Sstevel@tonic-gate return (DDI_SUCCESS);
7380Sstevel@tonic-gate
7390Sstevel@tonic-gate default:
7400Sstevel@tonic-gate return (DDI_FAILURE);
7410Sstevel@tonic-gate }
7420Sstevel@tonic-gate }
7430Sstevel@tonic-gate
7440Sstevel@tonic-gate /*
7450Sstevel@tonic-gate * SCC High Level Interrupt Handler
7460Sstevel@tonic-gate *
7470Sstevel@tonic-gate * This routine fields the level 12 interrupts generated by the 8530 chips.
7480Sstevel@tonic-gate * When the SCC interrupts the conditions that triggered it are available
7490Sstevel@tonic-gate * for reference in Read Register 3 of the A channel (RR3A). We process
7500Sstevel@tonic-gate * all the pending interrupts before returning. The maximum interrupts
7510Sstevel@tonic-gate * that will be processed before returning is set to 6, which is twice
7520Sstevel@tonic-gate * the size of RX-FIFO.
7530Sstevel@tonic-gate * We keep a pointer to the B side of the most recently interrupting chip
7540Sstevel@tonic-gate * in zscurr.
7550Sstevel@tonic-gate */
7560Sstevel@tonic-gate
7570Sstevel@tonic-gate /*
7580Sstevel@tonic-gate * 'argzs' actually 'struct zscom *argzs'
7590Sstevel@tonic-gate */
7600Sstevel@tonic-gate
7610Sstevel@tonic-gate #define ZSRR3_INT_PENDING (ZSRR3_IP_B_STAT | ZSRR3_IP_B_TX | ZSRR3_IP_B_RX |\
7620Sstevel@tonic-gate ZSRR3_IP_A_STAT | ZSRR3_IP_A_TX | ZSRR3_IP_A_RX)
7630Sstevel@tonic-gate
7640Sstevel@tonic-gate #define ZSRR1_ANY_ERRORS (ZSRR1_PE | ZSRR1_DO | ZSRR1_FE | ZSRR1_RXEOF)
7650Sstevel@tonic-gate #define ZS_HIGH_INTR_LOOPLIMIT 6
7660Sstevel@tonic-gate
7670Sstevel@tonic-gate /*ARGSUSED*/
7680Sstevel@tonic-gate uint_t
zs_high_intr(caddr_t argzs)7690Sstevel@tonic-gate zs_high_intr(caddr_t argzs)
7700Sstevel@tonic-gate {
7710Sstevel@tonic-gate struct zscom *zs;
7720Sstevel@tonic-gate uchar_t stat, isource, count;
7730Sstevel@tonic-gate int unit;
7740Sstevel@tonic-gate
7750Sstevel@tonic-gate TRACE_0(TR_FAC_ZS, TR_ZS_H_INT_START, "zs_h_int start");
7760Sstevel@tonic-gate mutex_enter(&zs_curr_lock);
7770Sstevel@tonic-gate zs = zscurr; /* Points at Channel B */
7780Sstevel@tonic-gate
7790Sstevel@tonic-gate ZSNEXTPOLL(zs, zscurr);
7800Sstevel@tonic-gate
7810Sstevel@tonic-gate SCC_READA(3, isource);
7820Sstevel@tonic-gate start_zs_h:
7830Sstevel@tonic-gate count = ZS_HIGH_INTR_LOOPLIMIT;
7840Sstevel@tonic-gate while ((isource & ZSRR3_INT_PENDING) && (count--)) {
7850Sstevel@tonic-gate if (isource & ZSRR3_IP_B_STAT)
7860Sstevel@tonic-gate (zs->zs_xsint)(zs);
7870Sstevel@tonic-gate else {
7880Sstevel@tonic-gate if (isource & ZSRR3_IP_B_TX)
7890Sstevel@tonic-gate (zs->zs_txint)(zs);
7900Sstevel@tonic-gate if (isource & ZSRR3_IP_B_RX) {
7910Sstevel@tonic-gate SCC_READ(1, stat);
7920Sstevel@tonic-gate if (stat & ZSRR1_ANY_ERRORS)
7930Sstevel@tonic-gate (zs->zs_srint)(zs);
7940Sstevel@tonic-gate else if ((SCC_READ0()) & ZSRR0_RX_READY)
7950Sstevel@tonic-gate (zs->zs_rxint)(zs);
7960Sstevel@tonic-gate }
7970Sstevel@tonic-gate }
7980Sstevel@tonic-gate
7990Sstevel@tonic-gate zs -= 1;
8000Sstevel@tonic-gate if (isource & ZSRR3_IP_A_STAT)
8010Sstevel@tonic-gate (zs->zs_xsint)(zs);
8020Sstevel@tonic-gate else {
8030Sstevel@tonic-gate if (isource & ZSRR3_IP_A_TX)
8040Sstevel@tonic-gate (zs->zs_txint)(zs);
8050Sstevel@tonic-gate if (isource & ZSRR3_IP_A_RX) {
8060Sstevel@tonic-gate SCC_READ(1, stat);
8070Sstevel@tonic-gate if (stat & ZSRR1_ANY_ERRORS)
8080Sstevel@tonic-gate (zs->zs_srint)(zs);
8090Sstevel@tonic-gate else if ((SCC_READ0()) & ZSRR0_RX_READY)
8100Sstevel@tonic-gate (zs->zs_rxint)(zs);
8110Sstevel@tonic-gate }
8120Sstevel@tonic-gate }
8130Sstevel@tonic-gate
8140Sstevel@tonic-gate zs = zscurr;
8150Sstevel@tonic-gate SCC_READA(3, isource);
8160Sstevel@tonic-gate }
8170Sstevel@tonic-gate
8180Sstevel@tonic-gate if (count == ZS_HIGH_INTR_LOOPLIMIT) {
8190Sstevel@tonic-gate unit = (nzs >> 1) - 1;
8200Sstevel@tonic-gate while (unit--) {
8210Sstevel@tonic-gate zs += 2; /* Always Channel B */
8220Sstevel@tonic-gate if (zs > zslast)
8230Sstevel@tonic-gate zs = &zscom[1];
8240Sstevel@tonic-gate if (!zs->zs_ops)
8250Sstevel@tonic-gate continue;
8260Sstevel@tonic-gate SCC_READA(3, isource);
8270Sstevel@tonic-gate if (isource & ZSRR3_INT_PENDING) {
8280Sstevel@tonic-gate zscurr = zs; /* update zscurr and */
8290Sstevel@tonic-gate goto start_zs_h;
8300Sstevel@tonic-gate }
8310Sstevel@tonic-gate }
8320Sstevel@tonic-gate if (zs->intrstats) {
8330Sstevel@tonic-gate KIOIP->intrs[KSTAT_INTR_HARD]++;
8340Sstevel@tonic-gate }
8350Sstevel@tonic-gate mutex_exit(&zs_curr_lock);
8360Sstevel@tonic-gate TRACE_0(TR_FAC_ZS, TR_ZS_H_INT_END, "zs_h_int end");
8370Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); /* Must not be for us. */
8380Sstevel@tonic-gate }
8390Sstevel@tonic-gate if (zs->intrstats) {
8400Sstevel@tonic-gate KIOIP->intrs[KSTAT_INTR_HARD]++;
8410Sstevel@tonic-gate }
8420Sstevel@tonic-gate mutex_exit(&zs_curr_lock); /* we're done with zscurr */
8430Sstevel@tonic-gate TRACE_0(TR_FAC_ZS, TR_ZS_H_INT_END, "zs_h_int end");
8440Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
8450Sstevel@tonic-gate }
8460Sstevel@tonic-gate
8470Sstevel@tonic-gate /*
8480Sstevel@tonic-gate * Handle a second-stage interrupt.
8490Sstevel@tonic-gate */
8500Sstevel@tonic-gate /*ARGSUSED*/
8510Sstevel@tonic-gate uint_t
zsintr(caddr_t intarg)8520Sstevel@tonic-gate zsintr(caddr_t intarg)
8530Sstevel@tonic-gate {
8540Sstevel@tonic-gate struct zscom *zs;
8550Sstevel@tonic-gate int rv;
8560Sstevel@tonic-gate
8570Sstevel@tonic-gate /*
8580Sstevel@tonic-gate * Test and clear soft interrupt.
8590Sstevel@tonic-gate */
8600Sstevel@tonic-gate TRACE_0(TR_FAC_ZS, TR_ZS_INT_START,
8610Sstevel@tonic-gate "zs_int start");
8620Sstevel@tonic-gate
8630Sstevel@tonic-gate mutex_enter(&zs_curr_lock);
8640Sstevel@tonic-gate rv = zssoftpend;
8650Sstevel@tonic-gate if (rv != 0) {
8660Sstevel@tonic-gate zssoftpend = 0;
8670Sstevel@tonic-gate }
8680Sstevel@tonic-gate mutex_exit(&zs_curr_lock);
8690Sstevel@tonic-gate
8700Sstevel@tonic-gate if (rv) {
8710Sstevel@tonic-gate for (zs = &zscom[0]; zs <= zslast; zs++) {
8720Sstevel@tonic-gate if (zs->zs_flags & ZS_NEEDSOFT) {
8730Sstevel@tonic-gate zs->zs_flags &= ~ZS_NEEDSOFT;
8740Sstevel@tonic-gate (*zs->zs_ops->zsop_softint)(zs);
8750Sstevel@tonic-gate if (zs->intrstats) {
8760Sstevel@tonic-gate KIOIP->intrs[KSTAT_INTR_SOFT]++;
8770Sstevel@tonic-gate }
8780Sstevel@tonic-gate }
8790Sstevel@tonic-gate }
8800Sstevel@tonic-gate }
8810Sstevel@tonic-gate TRACE_0(TR_FAC_ZS, TR_ZS_INT_END,
8820Sstevel@tonic-gate "zs_int end");
8830Sstevel@tonic-gate return (rv);
8840Sstevel@tonic-gate }
8850Sstevel@tonic-gate
8860Sstevel@tonic-gate void
setzssoft(void)8870Sstevel@tonic-gate setzssoft(void)
8880Sstevel@tonic-gate {
8890Sstevel@tonic-gate ddi_trigger_softintr(zs_softintr_id);
8900Sstevel@tonic-gate }
8910Sstevel@tonic-gate
8920Sstevel@tonic-gate /*
8930Sstevel@tonic-gate * Install a new ops vector into low level vector routine addresses
8940Sstevel@tonic-gate */
8950Sstevel@tonic-gate void
zsopinit(struct zscom * zs,struct zsops * zso)8960Sstevel@tonic-gate zsopinit(struct zscom *zs, struct zsops *zso)
8970Sstevel@tonic-gate {
8980Sstevel@tonic-gate zs->zs_txint = zso->zsop_txint;
8990Sstevel@tonic-gate zs->zs_xsint = zso->zsop_xsint;
9000Sstevel@tonic-gate zs->zs_rxint = zso->zsop_rxint;
9010Sstevel@tonic-gate zs->zs_srint = zso->zsop_srint;
9020Sstevel@tonic-gate zs->zs_suspend = zso->zsop_suspend;
9030Sstevel@tonic-gate zs->zs_resume = zso->zsop_resume;
9040Sstevel@tonic-gate zs->zs_ops = zso;
9050Sstevel@tonic-gate zs->zs_flags = 0;
9060Sstevel@tonic-gate }
9070Sstevel@tonic-gate
9080Sstevel@tonic-gate /*
9090Sstevel@tonic-gate * Set or get the modem control status.
9100Sstevel@tonic-gate *
9110Sstevel@tonic-gate * This routine relies on the fact that the bits of interest in RR0 (CD and
9120Sstevel@tonic-gate * CTS) do not overlap the bits of interest in WR5 (RTS and DTR). Thus, they
9130Sstevel@tonic-gate * can be combined into a single 'int' without harm.
9140Sstevel@tonic-gate */
9150Sstevel@tonic-gate int
zsmctl(struct zscom * zs,int bits,int how)9160Sstevel@tonic-gate zsmctl(struct zscom *zs, int bits, int how)
9170Sstevel@tonic-gate {
9180Sstevel@tonic-gate int mbits, obits;
9190Sstevel@tonic-gate time_t now, held;
9200Sstevel@tonic-gate
9210Sstevel@tonic-gate ASSERT(mutex_owned(zs->zs_excl_hi));
9220Sstevel@tonic-gate ASSERT(mutex_owned(zs->zs_excl));
9230Sstevel@tonic-gate
9240Sstevel@tonic-gate again:
9250Sstevel@tonic-gate mbits = zs->zs_wreg[5] & (ZSWR5_RTS|ZSWR5_DTR);
9260Sstevel@tonic-gate SCC_WRITE0(ZSWR0_RESET_STATUS);
9270Sstevel@tonic-gate mbits |= SCC_READ0() & (ZSRR0_CD|ZSRR0_CTS);
9280Sstevel@tonic-gate ZSDELAY();
9290Sstevel@tonic-gate obits = mbits;
9300Sstevel@tonic-gate
9310Sstevel@tonic-gate switch (how) {
9320Sstevel@tonic-gate
9330Sstevel@tonic-gate case DMSET:
9340Sstevel@tonic-gate mbits = bits;
9350Sstevel@tonic-gate break;
9360Sstevel@tonic-gate
9370Sstevel@tonic-gate case DMBIS:
9380Sstevel@tonic-gate mbits |= bits;
9390Sstevel@tonic-gate break;
9400Sstevel@tonic-gate
9410Sstevel@tonic-gate case DMBIC:
9420Sstevel@tonic-gate mbits &= ~bits;
9430Sstevel@tonic-gate break;
9440Sstevel@tonic-gate
9450Sstevel@tonic-gate case DMGET:
9460Sstevel@tonic-gate return (mbits);
9470Sstevel@tonic-gate }
9480Sstevel@tonic-gate
9490Sstevel@tonic-gate now = gethrestime_sec();
9500Sstevel@tonic-gate held = now - zs->zs_dtrlow;
9510Sstevel@tonic-gate
9520Sstevel@tonic-gate /*
9530Sstevel@tonic-gate * if DTR is going low, stash current time away
9540Sstevel@tonic-gate */
9550Sstevel@tonic-gate if (~mbits & obits & ZSWR5_DTR)
9560Sstevel@tonic-gate zs->zs_dtrlow = now;
9570Sstevel@tonic-gate
9580Sstevel@tonic-gate /*
9590Sstevel@tonic-gate * if DTR is going high, sleep until it has been low a bit
9600Sstevel@tonic-gate */
9610Sstevel@tonic-gate if ((mbits & ~obits & ZSWR5_DTR) && (held < default_dtrlow)) {
9620Sstevel@tonic-gate mutex_exit(zs->zs_excl_hi);
9630Sstevel@tonic-gate cv_wait(&lbolt_cv, zs->zs_excl);
9640Sstevel@tonic-gate if (zs->zs_suspended)
9650Sstevel@tonic-gate (void) ddi_dev_is_needed(zs->zs_dip, 0, 1);
9660Sstevel@tonic-gate mutex_enter(zs->zs_excl_hi);
9670Sstevel@tonic-gate goto again;
9680Sstevel@tonic-gate }
9690Sstevel@tonic-gate
9700Sstevel@tonic-gate zs->zs_wreg[5] &= ~(ZSWR5_RTS|ZSWR5_DTR);
9710Sstevel@tonic-gate SCC_BIS(5, mbits & (ZSWR5_RTS|ZSWR5_DTR));
9720Sstevel@tonic-gate return (mbits);
9730Sstevel@tonic-gate }
9740Sstevel@tonic-gate
9750Sstevel@tonic-gate /*
9760Sstevel@tonic-gate * Program the Z8530 registers.
9770Sstevel@tonic-gate */
9780Sstevel@tonic-gate void
zs_program(struct zs_prog * zspp)9790Sstevel@tonic-gate zs_program(struct zs_prog *zspp)
9800Sstevel@tonic-gate {
9810Sstevel@tonic-gate struct zscom *zs = zspp->zs;
9820Sstevel@tonic-gate int loops;
9830Sstevel@tonic-gate uchar_t c;
9840Sstevel@tonic-gate uchar_t wr10 = 0, wr14 = 0;
9850Sstevel@tonic-gate
9860Sstevel@tonic-gate ASSERT(mutex_owned(zs->zs_excl));
9870Sstevel@tonic-gate ASSERT(mutex_owned(zs->zs_excl_hi));
9880Sstevel@tonic-gate
9890Sstevel@tonic-gate /*
9900Sstevel@tonic-gate * There are some special cases to account for before reprogramming.
9910Sstevel@tonic-gate * We might be transmitting, so delay 100,000 usec (worst case at 110
9920Sstevel@tonic-gate * baud) for this to finish, then disable the receiver until later,
9930Sstevel@tonic-gate * reset the External Status Change latches and the error bits, and
9940Sstevel@tonic-gate * drain the receive FIFO.
9950Sstevel@tonic-gate * XXX: Doing any kind of reset (WR9) here causes trouble!
9960Sstevel@tonic-gate */
9970Sstevel@tonic-gate if (zspp->flags & ZSP_SYNC) {
9980Sstevel@tonic-gate SCC_WRITE(7, SDLCFLAG);
9990Sstevel@tonic-gate wr10 = ZSWR10_PRESET_ONES;
10000Sstevel@tonic-gate if (zspp->flags & ZSP_NRZI)
10010Sstevel@tonic-gate wr10 |= ZSWR10_NRZI;
10020Sstevel@tonic-gate SCC_WRITE(10, wr10);
10030Sstevel@tonic-gate } else {
10040Sstevel@tonic-gate for (loops = 1000; loops > 0; --loops) {
10050Sstevel@tonic-gate SCC_READ(1, c);
10060Sstevel@tonic-gate if (c & ZSRR1_ALL_SENT)
10070Sstevel@tonic-gate break;
10080Sstevel@tonic-gate DELAY(100);
10090Sstevel@tonic-gate }
10100Sstevel@tonic-gate SCC_WRITE(3, 0);
10110Sstevel@tonic-gate SCC_WRITE0(ZSWR0_RESET_STATUS);
10120Sstevel@tonic-gate SCC_WRITE0(ZSWR0_RESET_ERRORS);
10130Sstevel@tonic-gate c = SCC_READDATA(); /* Empty the FIFO */
10140Sstevel@tonic-gate c = SCC_READDATA();
10150Sstevel@tonic-gate c = SCC_READDATA();
10160Sstevel@tonic-gate }
10170Sstevel@tonic-gate
10180Sstevel@tonic-gate /*
10190Sstevel@tonic-gate * Programming the SCC is done in three phases.
10200Sstevel@tonic-gate * Phase one sets operating modes:
10210Sstevel@tonic-gate */
10220Sstevel@tonic-gate SCC_WRITE(4, zspp->wr4);
10230Sstevel@tonic-gate SCC_WRITE(11, zspp->wr11);
10240Sstevel@tonic-gate SCC_WRITE(12, zspp->wr12);
10250Sstevel@tonic-gate SCC_WRITE(13, zspp->wr13);
10260Sstevel@tonic-gate if (zspp->flags & ZSP_PLL) {
10270Sstevel@tonic-gate SCC_WRITE(14, ZSWR14_DPLL_SRC_BAUD);
10280Sstevel@tonic-gate SCC_WRITE(14, ZSWR14_DPLL_NRZI);
10290Sstevel@tonic-gate } else
10300Sstevel@tonic-gate SCC_WRITE(14, ZSWR14_DPLL_DISABLE);
10310Sstevel@tonic-gate
10320Sstevel@tonic-gate /*
10330Sstevel@tonic-gate * Phase two enables special hardware functions:
10340Sstevel@tonic-gate */
10350Sstevel@tonic-gate wr14 = ZSWR14_BAUD_FROM_PCLK | ZSWR14_BAUD_ENA;
10360Sstevel@tonic-gate if (zspp->flags & ZSP_LOOP)
10370Sstevel@tonic-gate wr14 |= ZSWR14_LOCAL_LOOPBACK;
10380Sstevel@tonic-gate if (zspp->flags & ZSP_ECHO)
10390Sstevel@tonic-gate wr14 |= ZSWR14_AUTO_ECHO;
10400Sstevel@tonic-gate SCC_WRITE(14, wr14);
10410Sstevel@tonic-gate SCC_WRITE(3, zspp->wr3);
10420Sstevel@tonic-gate SCC_WRITE(5, zspp->wr5);
10430Sstevel@tonic-gate
10440Sstevel@tonic-gate SCC_WRITE0(ZSWR0_RESET_TXCRC);
10450Sstevel@tonic-gate
10460Sstevel@tonic-gate if (zspp->flags & ZSP_PARITY_SPECIAL) {
10470Sstevel@tonic-gate SCC_WRITE(1, ZSWR1_PARITY_SPECIAL);
10480Sstevel@tonic-gate } else {
10490Sstevel@tonic-gate SCC_WRITE(1, 0);
10500Sstevel@tonic-gate }
10510Sstevel@tonic-gate
10520Sstevel@tonic-gate /*
10530Sstevel@tonic-gate * Phase three enables interrupt sources:
10540Sstevel@tonic-gate */
10550Sstevel@tonic-gate SCC_WRITE(15, zspp->wr15);
10560Sstevel@tonic-gate SCC_WRITE0(ZSWR0_RESET_STATUS);
10570Sstevel@tonic-gate SCC_WRITE0(ZSWR0_RESET_ERRORS);
10580Sstevel@tonic-gate SCC_BIS(1, ZSWR1_INIT);
10590Sstevel@tonic-gate }
10600Sstevel@tonic-gate
10610Sstevel@tonic-gate static void
zsnull_intr(struct zscom * zs)10620Sstevel@tonic-gate zsnull_intr(struct zscom *zs)
10630Sstevel@tonic-gate {
10640Sstevel@tonic-gate short c;
10650Sstevel@tonic-gate
10660Sstevel@tonic-gate SCC_WRITE0(ZSWR0_RESET_TXINT);
10670Sstevel@tonic-gate SCC_WRITE0(ZSWR0_RESET_STATUS);
10680Sstevel@tonic-gate c = SCC_READDATA();
10690Sstevel@tonic-gate ZSDELAY();
10700Sstevel@tonic-gate #ifdef lint
10710Sstevel@tonic-gate c = c;
10720Sstevel@tonic-gate #endif /* lint */
10730Sstevel@tonic-gate SCC_WRITE0(ZSWR0_RESET_ERRORS);
10740Sstevel@tonic-gate }
10750Sstevel@tonic-gate
10760Sstevel@tonic-gate static int
zsnull_softint(struct zscom * zs)10770Sstevel@tonic-gate zsnull_softint(struct zscom *zs)
10780Sstevel@tonic-gate {
10790Sstevel@tonic-gate cmn_err(CE_WARN, "zs%d: unexpected soft int\n", zs->zs_unit);
10800Sstevel@tonic-gate return (0);
10810Sstevel@tonic-gate }
10820Sstevel@tonic-gate
10830Sstevel@tonic-gate /*
10840Sstevel@tonic-gate * These will be called on suspend/resume for un-opened zs ports.
10850Sstevel@tonic-gate */
10860Sstevel@tonic-gate static int
zsnull_suspend(struct zscom * zs)10870Sstevel@tonic-gate zsnull_suspend(struct zscom *zs)
10880Sstevel@tonic-gate {
10890Sstevel@tonic-gate struct zs_prog *zspp = &zs_prog[zs->zs_unit];
10900Sstevel@tonic-gate
10910Sstevel@tonic-gate /*
10920Sstevel@tonic-gate * Get a copy of the current registers
10930Sstevel@tonic-gate */
10940Sstevel@tonic-gate mutex_enter(zs->zs_excl);
10950Sstevel@tonic-gate mutex_enter(zs->zs_excl_hi);
10960Sstevel@tonic-gate zspp->zs = zs;
10970Sstevel@tonic-gate zspp->flags = 0;
10980Sstevel@tonic-gate zspp->wr3 = zs->zs_wreg[3];
10990Sstevel@tonic-gate zspp->wr4 = zs->zs_wreg[4];
11000Sstevel@tonic-gate zspp->wr5 = zs->zs_wreg[5];
11010Sstevel@tonic-gate zspp->wr11 = zs->zs_wreg[11];
11020Sstevel@tonic-gate zspp->wr12 = zs->zs_wreg[12];
11030Sstevel@tonic-gate zspp->wr13 = zs->zs_wreg[13];
11040Sstevel@tonic-gate zspp->wr15 = zs->zs_wreg[15];
11050Sstevel@tonic-gate mutex_exit(zs->zs_excl_hi);
11060Sstevel@tonic-gate mutex_exit(zs->zs_excl);
11070Sstevel@tonic-gate
11080Sstevel@tonic-gate return (DDI_SUCCESS);
11090Sstevel@tonic-gate }
11100Sstevel@tonic-gate
11110Sstevel@tonic-gate static int
zsnull_resume(struct zscom * zs)11120Sstevel@tonic-gate zsnull_resume(struct zscom *zs)
11130Sstevel@tonic-gate {
11140Sstevel@tonic-gate struct zs_prog *zspp = &zs_prog[zs->zs_unit];
11150Sstevel@tonic-gate
11160Sstevel@tonic-gate /*
11170Sstevel@tonic-gate * Restore registers
11180Sstevel@tonic-gate */
11190Sstevel@tonic-gate mutex_enter(zs->zs_excl);
11200Sstevel@tonic-gate mutex_enter(zs->zs_excl_hi);
11210Sstevel@tonic-gate zs_program(zspp);
11220Sstevel@tonic-gate SCC_WRITE(9, ZSWR9_MASTER_IE);
11230Sstevel@tonic-gate DELAY(4000);
11240Sstevel@tonic-gate mutex_exit(zs->zs_excl_hi);
11250Sstevel@tonic-gate mutex_exit(zs->zs_excl);
11260Sstevel@tonic-gate return (DDI_SUCCESS);
11270Sstevel@tonic-gate }
1128