#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/stat.h>
#include <sys/mkdev.h>
#include <sys/cmn_err.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/zsdev.h>
#include <sys/debug.h>
#include <sys/machsystm.h>
#include <sys/conf.h>
#include <sys/sunddi.h>
#include <sys/errno.h>
#define ZS_TRACING
#ifdef ZS_TRACING
#include <sys/vtrace.h>
#define TR_FAC_ZS 51
#define TR_ZS_H_INT_START 1
#define TR_ZS_H_INT_END 2
#define TR_ZS_INT_START 3
#define TR_ZS_INT_END 4
#define TR_FAC_ZS_INT 52
#define TR_READ_START 1
#define TR_READ_END 2
#endif
#define KIOIP KSTAT_INTR_PTR(zs->intrstats)
#ifndef MAXZS
#define MAXZS 4
#endif
int maxzs = MAXZS;
int nzs = 0;
struct zscom *zscom;
struct zscom *zscurr;
struct zscom *zslast;
struct zs_prog *zs_prog;
char *zssoftCAR;
int zs_watchdog_count = 10;
int zs_drain_check = 15000000;
#ifdef ZS_DEBUG
char zs_h_log[ZS_H_LOG_MAX +10];
int zs_h_log_n = 0;
#define zs_h_log_add(c) \
{ \
if (zs_h_log_n >= ZS_H_LOG_MAX) \
zs_h_log_n = 0; \
zs_h_log[zs_h_log_n++] = c; \
zs_h_log[zs_h_log_n] = '\0'; \
}
#else
#define zs_h_log_add(c)
#endif
#define GETPROP(dip, str, defval) \
ddi_getprop(DDI_DEV_T_ANY, (dip), DDI_PROP_DONTPASS, (str), (defval))
int zs_usec_delay = 1;
int zssoftpend;
ddi_softintr_t zs_softintr_id;
time_t default_dtrlow = 3;
static ddi_iblock_cookie_t zs_iblock;
static ddi_iblock_cookie_t zs_hi_iblock;
static int zs_addedsoft = 0;
static int zsprobe(dev_info_t *dev);
static int zsattach(dev_info_t *dev, ddi_attach_cmd_t cmd);
static int zsdetach(dev_info_t *dev, ddi_detach_cmd_t cmd);
void zsopinit(struct zscom *zs, struct zsops *zso);
static void zsnull_intr(struct zscom *zs);
static int zsnull_softint(struct zscom *zs);
static int zsnull_suspend(struct zscom *zs);
static int zsnull_resume(struct zscom *zs);
struct zsops zsops_null = {
zsnull_intr,
zsnull_intr,
zsnull_intr,
zsnull_intr,
zsnull_softint,
zsnull_suspend,
zsnull_resume
};
extern struct streamtab asynctab;
uint_t zs_high_intr(caddr_t argzs);
uint_t zsintr(caddr_t intarg);
extern int zsc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
void **result);
extern int ddi_create_internal_pathname(dev_info_t *dip, char *name,
int spec_type, minor_t minor_num);
extern struct streamtab zsstab;
int zssoftpend;
kmutex_t zs_soft_lock;
kmutex_t zs_curr_lock;
extern kcondvar_t lbolt_cv;
#define ZSS_CONF_FLAG (D_NEW | D_MP)
static struct cb_ops cb_zs_ops = {
nulldev,
nulldev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
&asynctab,
(int)(ZSS_CONF_FLAG)
};
struct dev_ops zs_ops = {
DEVO_REV,
0,
zsc_info,
nulldev,
zsprobe,
zsattach,
zsdetach,
nodev,
&cb_zs_ops,
(struct bus_ops *)NULL,
ddi_power,
ddi_quiesce_not_supported,
};
#include <sys/modctl.h>
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
&mod_driverops,
"Z8530 serial driver",
&zs_ops,
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modldrv, NULL
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (EBUSY);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
zsprobe(dev_info_t *dev)
{
struct zscc_device *zsaddr;
int rval;
auto char c;
rval = DDI_PROBE_FAILURE;
if (ddi_map_regs(dev, 0, (caddr_t *)&zsaddr, 0, 0)) {
cmn_err(CE_WARN, "zsprobe: unable to map registers");
return (rval);
}
mon_clock_stop();
if (ddi_peek8(dev, (char *)&zsaddr->zscc_control, &c) != DDI_SUCCESS) {
goto out;
}
drv_usecwait(2);
if (ddi_poke8(dev, (char *)&zsaddr->zscc_control, '\017')
!= DDI_SUCCESS) {
goto out;
}
drv_usecwait(2);
if (ddi_peek8(dev, (char *)&zsaddr->zscc_control, &c) != DDI_SUCCESS) {
goto out;
}
drv_usecwait(2);
if (c & 5) {
goto out;
}
rval = DDI_PROBE_SUCCESS;
out:
mon_clock_start();
ddi_unmap_regs(dev, 0, (caddr_t *)&zsaddr, 0, 0);
return (rval);
}
static int
zsattach(dev_info_t *dev, ddi_attach_cmd_t cmd)
{
struct zscom *zs;
int loops, i;
uint_t s;
int rtsdtr_bits = 0;
char softcd;
uchar_t rr;
short speed[2];
int current_chip = ddi_get_instance(dev);
struct zscc_device *tmpzs;
char name[16];
int keyboard_prop;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
zs = &zscom[current_chip*2];
if (!zs->zs_resume || (zs->zs_resume)(zs) != DDI_SUCCESS)
return (DDI_FAILURE);
zs++;
if (!zs->zs_resume || (zs->zs_resume)(zs) != DDI_SUCCESS) {
zs--;
if (!zs->zs_suspend ||
(zs->zs_suspend)(zs) != DDI_SUCCESS)
cmn_err(CE_WARN,
"zs: inconsistent suspend/resume state");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (zscom == NULL) {
mutex_init(&zs_soft_lock, NULL, MUTEX_DRIVER, (void *)ZS_PL);
mutex_init(&zs_curr_lock, NULL, MUTEX_DRIVER, (void *)ZS_PL_HI);
zscom = kmem_zalloc(maxzs * sizeof (struct zscom), KM_SLEEP);
zs_prog = kmem_zalloc(maxzs * sizeof (struct zs_prog),
KM_SLEEP);
zssoftCAR = kmem_zalloc(maxzs, KM_SLEEP);
membar_producer();
nzs = maxzs;
zscurr = &zscom[(current_chip*2) + 1];
zslast = &zscom[current_chip*2];
i = GETPROP(dev, "zs-usec-delay", 0);
zs_usec_delay = (i <= 0) ? 1 : i;
}
if (2 * current_chip >= maxzs) {
cmn_err(CE_WARN,
"zs: unable to allocate resources for chip %d.",
current_chip);
cmn_err(CE_WARN, "Change zs:maxzs in /etc/system");
return (DDI_FAILURE);
}
zs = &zscom[current_chip*2];
if (ddi_map_regs(dev, 0, (caddr_t *)&zs->zs_addr, 0, 0)) {
cmn_err(CE_WARN, "zs%d: unable to map registers\n",
current_chip);
return (DDI_FAILURE);
}
tmpzs = zs->zs_addr;
mon_clock_stop();
s = ddi_enter_critical();
for (loops = 0; loops++ <= 500; DELAY(1000)) {
SCC_READA(1, rr);
if ((rr & ZSRR1_ALL_SENT) == 0) continue;
SCC_READB(1, rr);
if ((rr & ZSRR1_ALL_SENT) == 0) continue;
SCC_READA(0, rr);
if ((rr & ZSRR0_TX_READY) == 0) continue;
SCC_READB(0, rr);
if ((rr & ZSRR0_TX_READY) != 0) break;
}
SCC_READA(12, speed[0]);
SCC_READA(13, rr);
speed[0] |= rr << 8;
SCC_READB(12, speed[1]);
SCC_READB(13, rr);
speed[1] |= rr << 8;
SCC_WRITE(9, ZSWR9_RESET_WORLD);
DELAY(10);
for (i = 0; i < 2; i++) {
static char prop[] = "port-a-ignore-cd";
if (i == 0) {
zs->zs_addr = (struct zscc_device *)
((uintptr_t)tmpzs | ZSOFF);
(zs+1)->zs_excl_hi = zs->zs_excl_hi = &zs_curr_lock;
} else {
zs++;
zs->zs_addr = (struct zscc_device *)
((uintptr_t)tmpzs & ~ZSOFF);
zscurr = zs;
}
zs->zs_unit = current_chip * 2 + i;
zs->zs_dip = dev;
zs->zs_excl = kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
mutex_init(zs->zs_excl, NULL, MUTEX_DRIVER, (void *)ZS_PL);
zs->zs_ocexcl = kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
mutex_init(zs->zs_ocexcl, NULL, MUTEX_DRIVER, (void *)ZS_PL);
zsopinit(zs, &zsops_null);
prop[5] = 'a' + i;
softcd = GETPROP((dev_info_t *)(dev), prop, 0);
zssoftCAR[zs->zs_unit] = softcd;
if (softcd)
rtsdtr_bits = ZSWR5_RTS | ZSWR5_DTR;
keyboard_prop = GETPROP((dev_info_t *)(zs->zs_dip),
"keyboard", 0);
mutex_enter(&zs_curr_lock);
SCC_WRITE(4, ZSWR4_PARITY_EVEN | ZSWR4_1_STOP | ZSWR4_X16_CLK);
SCC_WRITE(3, ZSWR3_RX_8);
SCC_WRITE(11, ZSWR11_TXCLK_BAUD | ZSWR11_RXCLK_BAUD);
SCC_WRITE(12, (speed[i] & 0xff));
SCC_WRITE(13, (speed[i] >> 8) & 0xff);
SCC_WRITE(14, ZSWR14_BAUD_FROM_PCLK);
SCC_WRITE(3, ZSWR3_RX_8 | ZSWR3_RX_ENABLE);
SCC_WRITE(5, ZSWR5_TX_ENABLE | ZSWR5_TX_8 | rtsdtr_bits);
SCC_WRITE(14, ZSWR14_BAUD_ENA | ZSWR14_BAUD_FROM_PCLK);
if (keyboard_prop) {
SCC_WRITE(15, ZSR15_BREAK | ZSR15_TX_UNDER |
ZSR15_CTS | ZSR15_CD);
}
SCC_WRITE0(ZSWR0_RESET_ERRORS);
SCC_WRITE0(ZSWR0_RESET_STATUS);
mutex_exit(&zs_curr_lock);
zs->zs_dtrlow = gethrestime_sec() - default_dtrlow;
cv_init(&zs->zs_flags_cv, NULL, CV_DEFAULT, NULL);
zsa_init(zs);
}
mutex_enter(&zs_curr_lock);
SCC_WRITE(9, ZSWR9_MASTER_IE | ZSWR9_VECTOR_INCL_STAT);
DELAY(4000);
mutex_exit(&zs_curr_lock);
if (ddi_add_intr(dev, (uint_t)0, &zs_hi_iblock,
(ddi_idevice_cookie_t *)0, zs_high_intr,
(caddr_t)0) != DDI_SUCCESS) {
cmn_err(CE_PANIC, "cannot set high level zs interrupt");
}
if (zs_addedsoft == 0) {
if (ddi_add_softintr(dev, DDI_SOFTINT_HIGH, &zs_softintr_id,
&zs_iblock, (ddi_idevice_cookie_t *)0,
zsintr, (caddr_t)0) != DDI_SUCCESS) {
cmn_err(CE_PANIC,
"cannot set second stage zs interrupt");
}
zs_addedsoft++;
}
if (zs > zslast)
zslast = zs;
(void) ddi_exit_critical(s);
mon_clock_start();
if (!GETPROP(zs->zs_dip, "keyboard", 0)) {
static char *serial_line = DDI_NT_SERIAL_MB;
static char *dial_out = DDI_NT_SERIAL_MB_DO;
if (ddi_get_instance(dev) != 0) {
static char *obp_type = "obp-console-name";
(void) strcpy(name, "a");
if (ddi_create_minor_node(dev, name, S_IFCHR,
ddi_get_instance(dev) * 2,
obp_type, 0) == DDI_FAILURE) {
ddi_remove_minor_node(dev, NULL);
return (DDI_FAILURE);
}
(void) strcpy(name, "b");
if (ddi_create_minor_node(dev, name, S_IFCHR,
(ddi_get_instance(dev) * 2) + 1,
obp_type, 0) == DDI_FAILURE) {
ddi_remove_minor_node(dev, NULL);
return (DDI_FAILURE);
}
}
(void) sprintf(name, "%c", (ddi_get_instance(dev) + 'a'));
if (ddi_create_minor_node(dev, name, S_IFCHR,
ddi_get_instance(dev) * 2,
serial_line, 0) == DDI_FAILURE) {
ddi_remove_minor_node(dev, NULL);
return (DDI_FAILURE);
}
(void) sprintf(name, "%c", (ddi_get_instance(dev) + 'b'));
if (ddi_create_minor_node(dev, name, S_IFCHR,
(ddi_get_instance(dev) * 2) + 1,
serial_line, 0) == DDI_FAILURE) {
ddi_remove_minor_node(dev, NULL);
return (DDI_FAILURE);
}
(void) sprintf(name, "%c,cu", (ddi_get_instance(dev) + 'a'));
if (ddi_create_minor_node(dev, name, S_IFCHR,
(ddi_get_instance(dev) * 2) | OUTLINE,
dial_out, 0) == DDI_FAILURE) {
ddi_remove_minor_node(dev, NULL);
return (DDI_FAILURE);
}
(void) sprintf(name, "%c,cu", (ddi_get_instance(dev) + 'b'));
if (ddi_create_minor_node(dev, name, S_IFCHR,
((ddi_get_instance(dev) * 2) + 1) | OUTLINE,
dial_out, 0) == DDI_FAILURE) {
ddi_remove_minor_node(dev, NULL);
return (DDI_FAILURE);
}
} else {
if (ddi_create_internal_pathname(dev, "keyboard", S_IFCHR,
ddi_get_instance(dev) * 2) == DDI_FAILURE) {
ddi_remove_minor_node(dev, NULL);
return (DDI_FAILURE);
}
if (ddi_create_internal_pathname(dev, "mouse", S_IFCHR,
(ddi_get_instance(dev) * 2) + 1) == DDI_FAILURE) {
ddi_remove_minor_node(dev, NULL);
return (DDI_FAILURE);
}
(void) strcpy(name, "a");
if (ddi_create_internal_pathname(dev, name, S_IFCHR,
ddi_get_instance(dev) * 2) == DDI_FAILURE) {
ddi_remove_minor_node(dev, NULL);
return (DDI_FAILURE);
}
(void) strcpy(name, "b");
if (ddi_create_internal_pathname(dev, name, S_IFCHR,
(ddi_get_instance(dev) * 2) + 1) == DDI_FAILURE) {
ddi_remove_minor_node(dev, NULL);
return (DDI_FAILURE);
}
}
ddi_report_dev(dev);
if (pm_create_components(dev, 3) == DDI_SUCCESS) {
(void) pm_busy_component(dev, 0);
pm_set_normal_power(dev, 0, 1);
pm_set_normal_power(dev, 1, 1);
pm_set_normal_power(dev, 2, 1);
} else {
return (DDI_FAILURE);
}
(void) sprintf(name, "zsc%d", current_chip);
zs->intrstats = kstat_create("zs", current_chip, name, "controller",
KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT);
if (zs->intrstats) {
kstat_install(zs->intrstats);
}
return (DDI_SUCCESS);
}
static int
zsdetach(dev_info_t *dev, ddi_detach_cmd_t cmd)
{
struct zscom *zs;
int current_chip = ddi_get_instance(dev);
switch (cmd) {
case DDI_DETACH:
return (DDI_FAILURE);
case DDI_SUSPEND:
zs = &zscom[current_chip*2];
if (!zs->zs_suspend || (zs->zs_suspend)(zs) != DDI_SUCCESS)
return (DDI_FAILURE);
zs++;
if (!zs->zs_suspend || (zs->zs_suspend)(zs) != DDI_SUCCESS) {
zs--;
if (!zs->zs_resume ||
(zs->zs_resume)(zs) != DDI_SUCCESS)
cmn_err(CE_WARN,
"zs: inconsistent suspend/resume state");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
#define ZSRR3_INT_PENDING (ZSRR3_IP_B_STAT | ZSRR3_IP_B_TX | ZSRR3_IP_B_RX |\
ZSRR3_IP_A_STAT | ZSRR3_IP_A_TX | ZSRR3_IP_A_RX)
#define ZSRR1_ANY_ERRORS (ZSRR1_PE | ZSRR1_DO | ZSRR1_FE | ZSRR1_RXEOF)
#define ZS_HIGH_INTR_LOOPLIMIT 6
uint_t
zs_high_intr(caddr_t argzs)
{
struct zscom *zs;
uchar_t stat, isource, count;
int unit;
TRACE_0(TR_FAC_ZS, TR_ZS_H_INT_START, "zs_h_int start");
mutex_enter(&zs_curr_lock);
zs = zscurr;
ZSNEXTPOLL(zs, zscurr);
SCC_READA(3, isource);
start_zs_h:
count = ZS_HIGH_INTR_LOOPLIMIT;
while ((isource & ZSRR3_INT_PENDING) && (count--)) {
if (isource & ZSRR3_IP_B_STAT)
(zs->zs_xsint)(zs);
else {
if (isource & ZSRR3_IP_B_TX)
(zs->zs_txint)(zs);
if (isource & ZSRR3_IP_B_RX) {
SCC_READ(1, stat);
if (stat & ZSRR1_ANY_ERRORS)
(zs->zs_srint)(zs);
else if ((SCC_READ0()) & ZSRR0_RX_READY)
(zs->zs_rxint)(zs);
}
}
zs -= 1;
if (isource & ZSRR3_IP_A_STAT)
(zs->zs_xsint)(zs);
else {
if (isource & ZSRR3_IP_A_TX)
(zs->zs_txint)(zs);
if (isource & ZSRR3_IP_A_RX) {
SCC_READ(1, stat);
if (stat & ZSRR1_ANY_ERRORS)
(zs->zs_srint)(zs);
else if ((SCC_READ0()) & ZSRR0_RX_READY)
(zs->zs_rxint)(zs);
}
}
zs = zscurr;
SCC_READA(3, isource);
}
if (count == ZS_HIGH_INTR_LOOPLIMIT) {
unit = (nzs >> 1) - 1;
while (unit--) {
zs += 2;
if (zs > zslast)
zs = &zscom[1];
if (!zs->zs_ops)
continue;
SCC_READA(3, isource);
if (isource & ZSRR3_INT_PENDING) {
zscurr = zs;
goto start_zs_h;
}
}
if (zs->intrstats) {
KIOIP->intrs[KSTAT_INTR_HARD]++;
}
mutex_exit(&zs_curr_lock);
TRACE_0(TR_FAC_ZS, TR_ZS_H_INT_END, "zs_h_int end");
return (DDI_INTR_UNCLAIMED);
}
if (zs->intrstats) {
KIOIP->intrs[KSTAT_INTR_HARD]++;
}
mutex_exit(&zs_curr_lock);
TRACE_0(TR_FAC_ZS, TR_ZS_H_INT_END, "zs_h_int end");
return (DDI_INTR_CLAIMED);
}
uint_t
zsintr(caddr_t intarg)
{
struct zscom *zs;
int rv;
TRACE_0(TR_FAC_ZS, TR_ZS_INT_START,
"zs_int start");
mutex_enter(&zs_curr_lock);
rv = zssoftpend;
if (rv != 0) {
zssoftpend = 0;
}
mutex_exit(&zs_curr_lock);
if (rv) {
for (zs = &zscom[0]; zs <= zslast; zs++) {
if (zs->zs_flags & ZS_NEEDSOFT) {
zs->zs_flags &= ~ZS_NEEDSOFT;
(void) (*zs->zs_ops->zsop_softint)(zs);
if (zs->intrstats) {
KIOIP->intrs[KSTAT_INTR_SOFT]++;
}
}
}
}
TRACE_0(TR_FAC_ZS, TR_ZS_INT_END,
"zs_int end");
return (rv);
}
void
setzssoft(void)
{
ddi_trigger_softintr(zs_softintr_id);
}
void
zsopinit(struct zscom *zs, struct zsops *zso)
{
zs->zs_txint = zso->zsop_txint;
zs->zs_xsint = zso->zsop_xsint;
zs->zs_rxint = zso->zsop_rxint;
zs->zs_srint = zso->zsop_srint;
zs->zs_suspend = zso->zsop_suspend;
zs->zs_resume = zso->zsop_resume;
zs->zs_ops = zso;
zs->zs_flags = 0;
}
int
zsmctl(struct zscom *zs, int bits, int how)
{
int mbits, obits;
time_t now, held;
ASSERT(mutex_owned(zs->zs_excl_hi));
ASSERT(mutex_owned(zs->zs_excl));
again:
mbits = zs->zs_wreg[5] & (ZSWR5_RTS|ZSWR5_DTR);
SCC_WRITE0(ZSWR0_RESET_STATUS);
mbits |= SCC_READ0() & (ZSRR0_CD|ZSRR0_CTS);
ZSDELAY();
obits = mbits;
switch (how) {
case DMSET:
mbits = bits;
break;
case DMBIS:
mbits |= bits;
break;
case DMBIC:
mbits &= ~bits;
break;
case DMGET:
return (mbits);
}
now = gethrestime_sec();
held = now - zs->zs_dtrlow;
if (~mbits & obits & ZSWR5_DTR)
zs->zs_dtrlow = now;
if ((mbits & ~obits & ZSWR5_DTR) && (held < default_dtrlow)) {
mutex_exit(zs->zs_excl_hi);
cv_wait(&lbolt_cv, zs->zs_excl);
if (zs->zs_suspended)
(void) ddi_dev_is_needed(zs->zs_dip, 0, 1);
mutex_enter(zs->zs_excl_hi);
goto again;
}
zs->zs_wreg[5] &= ~(ZSWR5_RTS|ZSWR5_DTR);
SCC_BIS(5, mbits & (ZSWR5_RTS|ZSWR5_DTR));
return (mbits);
}
void
zs_program(struct zs_prog *zspp)
{
struct zscom *zs = zspp->zs;
int loops;
uchar_t c;
uchar_t wr10 = 0, wr14 = 0;
ASSERT(mutex_owned(zs->zs_excl));
ASSERT(mutex_owned(zs->zs_excl_hi));
if (zspp->flags & ZSP_SYNC) {
SCC_WRITE(7, SDLCFLAG);
wr10 = ZSWR10_PRESET_ONES;
if (zspp->flags & ZSP_NRZI)
wr10 |= ZSWR10_NRZI;
SCC_WRITE(10, wr10);
} else {
for (loops = 1000; loops > 0; --loops) {
SCC_READ(1, c);
if (c & ZSRR1_ALL_SENT)
break;
DELAY(100);
}
SCC_WRITE(3, 0);
SCC_WRITE0(ZSWR0_RESET_STATUS);
SCC_WRITE0(ZSWR0_RESET_ERRORS);
c = SCC_READDATA();
c = SCC_READDATA();
c = SCC_READDATA();
}
SCC_WRITE(4, zspp->wr4);
SCC_WRITE(11, zspp->wr11);
SCC_WRITE(12, zspp->wr12);
SCC_WRITE(13, zspp->wr13);
if (zspp->flags & ZSP_PLL) {
SCC_WRITE(14, ZSWR14_DPLL_SRC_BAUD);
SCC_WRITE(14, ZSWR14_DPLL_NRZI);
} else
SCC_WRITE(14, ZSWR14_DPLL_DISABLE);
wr14 = ZSWR14_BAUD_FROM_PCLK | ZSWR14_BAUD_ENA;
if (zspp->flags & ZSP_LOOP)
wr14 |= ZSWR14_LOCAL_LOOPBACK;
if (zspp->flags & ZSP_ECHO)
wr14 |= ZSWR14_AUTO_ECHO;
SCC_WRITE(14, wr14);
SCC_WRITE(3, zspp->wr3);
SCC_WRITE(5, zspp->wr5);
SCC_WRITE0(ZSWR0_RESET_TXCRC);
if (zspp->flags & ZSP_PARITY_SPECIAL) {
SCC_WRITE(1, ZSWR1_PARITY_SPECIAL);
} else {
SCC_WRITE(1, 0);
}
SCC_WRITE(15, zspp->wr15);
SCC_WRITE0(ZSWR0_RESET_STATUS);
SCC_WRITE0(ZSWR0_RESET_ERRORS);
SCC_BIS(1, ZSWR1_INIT);
}
static void
zsnull_intr(struct zscom *zs)
{
short c;
SCC_WRITE0(ZSWR0_RESET_TXINT);
SCC_WRITE0(ZSWR0_RESET_STATUS);
c = SCC_READDATA();
ZSDELAY();
#ifdef lint
c = c;
#endif
SCC_WRITE0(ZSWR0_RESET_ERRORS);
}
static int
zsnull_softint(struct zscom *zs)
{
cmn_err(CE_WARN, "zs%d: unexpected soft int\n", zs->zs_unit);
return (0);
}
static int
zsnull_suspend(struct zscom *zs)
{
struct zs_prog *zspp = &zs_prog[zs->zs_unit];
mutex_enter(zs->zs_excl);
mutex_enter(zs->zs_excl_hi);
zspp->zs = zs;
zspp->flags = 0;
zspp->wr3 = zs->zs_wreg[3];
zspp->wr4 = zs->zs_wreg[4];
zspp->wr5 = zs->zs_wreg[5];
zspp->wr11 = zs->zs_wreg[11];
zspp->wr12 = zs->zs_wreg[12];
zspp->wr13 = zs->zs_wreg[13];
zspp->wr15 = zs->zs_wreg[15];
mutex_exit(zs->zs_excl_hi);
mutex_exit(zs->zs_excl);
return (DDI_SUCCESS);
}
static int
zsnull_resume(struct zscom *zs)
{
struct zs_prog *zspp = &zs_prog[zs->zs_unit];
mutex_enter(zs->zs_excl);
mutex_enter(zs->zs_excl_hi);
zs_program(zspp);
SCC_WRITE(9, ZSWR9_MASTER_IE);
DELAY(4000);
mutex_exit(zs->zs_excl_hi);
mutex_exit(zs->zs_excl);
return (DDI_SUCCESS);
}