NetBSD/sys/arch/cesfic/dev/zs.c
drochner e047259a1f Initial import of the known working kernel bits for the NetBSD/cesfic
port. cesfic is a VME board with one or two mc68040 processors. See
the README file for details.
The port is working well with a.out userland, there are some problems
with ELF still, like applications running out of memory where it is not
expected. Some parts, in particular the pmap (which was taken from hp300
four years ago), need updating, but this is easier done within the NetBSD
CVS tree.
2001-05-14 18:22:58 +00:00

515 lines
11 KiB
C

/* $NetBSD: zs.c,v 1.1 2001/05/14 18:23:07 drochner Exp $ */
/*-
* Copyright (c) 1996 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Gordon W. Ross.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Zilog Z8530 Dual UART driver (machine-dependent part)
*
* Runs two serial lines per chip using slave drivers.
* Plain tty/async lines use the zs_async slave.
*/
#include "opt_ddb.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/tty.h>
#include <sys/time.h>
#include <sys/syslog.h>
#include <dev/cons.h>
#include <dev/ic/z8530reg.h>
#include <machine/cpu.h>
#include <machine/z8530var.h>
#include <cesfic/dev/zsvar.h>
int zs_getc __P((void*));
void zs_putc __P((void*, int));
static struct zs_chanstate zs_conschan_store;
static int zs_hwflags[2][2];
int zs_major = 10;
int zssoftpending;
extern struct cfdriver zsc_cd;
u_char zs_init_reg[16] = {
0, /* 0: CMD (reset, etc.) */
0, /* 1: No interrupts yet. */
0x18 + ZSHARD_PRI, /* IVECT */
ZSWR3_RX_8 | ZSWR3_RX_ENABLE,
ZSWR4_CLK_X16 | ZSWR4_ONESB | ZSWR4_EVENP,
ZSWR5_TX_8 | ZSWR5_TX_ENABLE,
0, /* 6: TXSYNC/SYNCLO */
0, /* 7: RXSYNC/SYNCHI */
0, /* 8: alias for data port */
ZSWR9_MASTER_IE,
0, /*10: Misc. TX/RX control bits */
ZSWR11_TXCLK_BAUD | ZSWR11_RXCLK_BAUD,
11, /*12: BAUDLO (default=9600) */
0, /*13: BAUDHI (default=9600) */
ZSWR14_BAUD_ENA | ZSWR14_BAUD_FROM_PCLK,
ZSWR15_BREAK_IE | ZSWR15_DCD_IE,
};
static int zsc_print __P((void *, const char *));
int zscngetc __P((dev_t));
void zscnputc __P((dev_t, int));
static struct consdev zscons = { NULL, NULL,
zscngetc, zscnputc, nullcnpollc, 0, NODEV, 1 };
void
zs_config(zsc, base)
struct zsc_softc *zsc;
char *base;
{
struct zsc_attach_args zsc_args;
struct zs_chanstate *cs;
int zsc_unit, channel, s;
zsc_unit = zsc->zsc_dev.dv_unit;
printf(": Zilog 8530 SCC\n");
/*
* Initialize software state for each channel.
*/
for (channel = 0; channel < 2; channel++) {
zsc_args.channel = channel;
zsc_args.hwflags = zs_hwflags[zsc_unit][channel];
/*
* If we're the console, copy the channel state, and
* adjust the console channel pointer.
*/
if (zsc_args.hwflags & ZS_HWFLAG_CONSOLE) {
cs = &zs_conschan_store;
} else {
cs = malloc(sizeof(struct zs_chanstate),
M_DEVBUF, M_NOWAIT);
if(channel==0){
cs->cs_reg_csr = base+7;
cs->cs_reg_data = base+15;
} else {
cs->cs_reg_csr = base+3;
cs->cs_reg_data = base+11;
}
bcopy(zs_init_reg, cs->cs_creg, 16);
bcopy(zs_init_reg, cs->cs_preg, 16);
cs->cs_defspeed = 9600;
}
zsc->zsc_cs[channel] = cs;
cs->cs_defcflag = CREAD | CS8 | HUPCL;
/* Make these correspond to cs_defcflag (-crtscts) */
cs->cs_rr0_dcd = ZSRR0_DCD;
cs->cs_rr0_cts = 0;
cs->cs_wr5_dtr = ZSWR5_DTR | ZSWR5_RTS;
cs->cs_wr5_rts = 0;
cs->cs_channel = channel;
cs->cs_private = NULL;
cs->cs_ops = &zsops_null;
cs->cs_brg_clk = 4000000 / 16;
/*
* Clear the master interrupt enable.
* The INTENA is common to both channels,
* so just do it on the A channel.
*/
if (channel == 0) {
zs_write_reg(cs, 9, 0);
}
/*
* Look for a child driver for this channel.
* The child attach will setup the hardware.
*/
if (!config_found(&zsc->zsc_dev, (void *)&zsc_args, zsc_print)) {
/* No sub-driver. Just reset it. */
u_char reset = (channel == 0) ?
ZSWR9_A_RESET : ZSWR9_B_RESET;
s = splzs();
zs_write_reg(cs, 9, reset);
splx(s);
}
}
}
static int
zsc_print(aux, name)
void *aux;
const char *name;
{
struct zsc_attach_args *args = aux;
if (name != NULL)
printf("%s: ", name);
if (args->channel != -1)
printf(" channel %d", args->channel);
return UNCONF;
}
int
zshard(arg)
void *arg;
{
register struct zsc_softc *zsc;
register int unit, rval;
rval = 0;
for (unit = 0; unit < zsc_cd.cd_ndevs; unit++) {
zsc = zsc_cd.cd_devs[unit];
if (zsc == NULL)
continue;
rval |= zsc_intr_hard(zsc);
if ((zsc->zsc_cs[0]->cs_softreq) ||
(zsc->zsc_cs[1]->cs_softreq))
{
/* zsc_req_softint(zsc); */
/* We are at splzs here, so no need to lock. */
if (zssoftpending == 0) {
zssoftpending = 1;
setsoftzs();
}
}
}
return (rval);
}
void
softzs()
{
register struct zsc_softc *zsc;
register int unit;
/* This is not the only ISR on this IPL. */
if (zssoftpending == 0)
return;
/*
* The soft intr. bit will be set by zshard only if
* the variable zssoftpending is zero.
*/
zssoftpending = 0;
for (unit = 0; unit < zsc_cd.cd_ndevs; ++unit) {
zsc = zsc_cd.cd_devs[unit];
if (zsc == NULL)
continue;
(void) zsc_intr_soft(zsc);
}
return;
}
u_char
zs_read_reg(cs, reg)
struct zs_chanstate *cs;
u_char reg;
{
u_char val;
*cs->cs_reg_csr = reg;
ZS_DELAY();
val = *cs->cs_reg_csr;
ZS_DELAY();
return val;
}
void
zs_write_reg(cs, reg, val)
struct zs_chanstate *cs;
u_char reg, val;
{
*cs->cs_reg_csr = reg;
ZS_DELAY();
*cs->cs_reg_csr = val;
ZS_DELAY();
}
u_char zs_read_csr(cs)
struct zs_chanstate *cs;
{
register u_char val;
val = *cs->cs_reg_csr;
ZS_DELAY();
return val;
}
void zs_write_csr(cs, val)
struct zs_chanstate *cs;
u_char val;
{
*cs->cs_reg_csr = val;
ZS_DELAY();
}
u_char zs_read_data(cs)
struct zs_chanstate *cs;
{
register u_char val;
val = *cs->cs_reg_data;
ZS_DELAY();
return val;
}
void zs_write_data(cs, val)
struct zs_chanstate *cs;
u_char val;
{
*cs->cs_reg_data = val;
ZS_DELAY();
}
int
zs_set_speed(cs, bps)
struct zs_chanstate *cs;
int bps; /* bits per second */
{
int tconst, real_bps;
tconst = BPS_TO_TCONST(cs->cs_brg_clk, bps);
if (tconst < 0)
return (EINVAL);
/* Convert back to make sure we can do it. */
real_bps = TCONST_TO_BPS(cs->cs_brg_clk, tconst);
#if 0
/* XXX - Allow some tolerance here? */
if (real_bps != bps)
return (EINVAL);
#endif
cs->cs_preg[12] = tconst;
cs->cs_preg[13] = tconst >> 8;
return (0);
}
int
zs_set_modes(cs, cflag)
struct zs_chanstate *cs;
int cflag; /* bits per second */
{
int s;
/*
* Output hardware flow control on the chip is horrendous:
* if carrier detect drops, the receiver is disabled, and if
* CTS drops, the transmitter is stoped IN MID CHARACTER!
* Therefore, NEVER set the HFC bit, and instead use the
* status interrupt to detect CTS changes.
*/
s = splzs();
#if 0 /* XXX - See below. */
if (cflag & CLOCAL) {
cs->cs_rr0_dcd = 0;
cs->cs_preg[15] &= ~ZSWR15_DCD_IE;
} else {
/* XXX - Need to notice DCD change here... */
cs->cs_rr0_dcd = ZSRR0_DCD;
cs->cs_preg[15] |= ZSWR15_DCD_IE;
}
#endif /* XXX */
if (cflag & CRTSCTS) {
cs->cs_wr5_dtr = ZSWR5_DTR;
cs->cs_wr5_rts = ZSWR5_RTS;
cs->cs_rr0_cts = ZSRR0_CTS;
cs->cs_preg[15] |= ZSWR15_CTS_IE;
} else {
cs->cs_wr5_dtr = ZSWR5_DTR | ZSWR5_RTS;
cs->cs_wr5_rts = 0;
cs->cs_rr0_cts = 0;
cs->cs_preg[15] &= ~ZSWR15_CTS_IE;
}
splx(s);
/* Caller will stuff the pending registers. */
return (0);
}
/*
* Handle user request to enter kernel debugger.
*/
void
zs_abort(cs)
struct zs_chanstate *cs;
{
int rr0;
/* Wait for end of break to avoid PROM abort. */
/* XXX - Limit the wait? */
do {
rr0 = *cs->cs_reg_csr;
ZS_DELAY();
} while (rr0 & ZSRR0_BREAK);
#ifdef DDB
console_debugger();
#endif
}
/*
* Polled input char.
*/
int
zs_getc(arg)
void *arg;
{
register struct zs_chanstate *cs = arg;
register int s, c, rr0, stat;
s = splhigh();
top:
/* Wait for a character to arrive. */
do {
rr0 = *cs->cs_reg_csr;
ZS_DELAY();
} while ((rr0 & ZSRR0_RX_READY) == 0);
/* Read error register. */
stat = zs_read_reg(cs, 1) & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE);
if (stat) {
zs_write_csr(cs, ZSM_RESET_ERR);
goto top;
}
/* Read character. */
c = *cs->cs_reg_data;
ZS_DELAY();
splx(s);
return (c);
}
/*
* Polled output char.
*/
void
zs_putc(arg, c)
void *arg;
int c;
{
register struct zs_chanstate *cs = arg;
register int s, rr0;
s = splhigh();
/* Wait for transmitter to become ready. */
do {
rr0 = *cs->cs_reg_csr;
ZS_DELAY();
} while ((rr0 & ZSRR0_TX_READY) == 0);
*cs->cs_reg_data = c;
ZS_DELAY();
splx(s);
}
int zscngetc(dev)
dev_t dev;
{
register struct zs_chanstate *cs = &zs_conschan_store;
register int c;
c = zs_getc(cs);
return (c);
}
void zscnputc(dev, c)
dev_t dev;
int c;
{
register struct zs_chanstate *cs = &zs_conschan_store;
zs_putc(cs, c);
}
/*
* Common parts of console init.
*/
void
zs_cninit(base)
void *base;
{
struct zs_chanstate *cs;
/*
* Pointer to channel state. Later, the console channel
* state is copied into the softc, and the console channel
* pointer adjusted to point to the new copy.
*/
cs = &zs_conschan_store;
zs_hwflags[0][0] = ZS_HWFLAG_CONSOLE;
/* Setup temporary chanstate. */
cs->cs_reg_csr = base+7;
cs->cs_reg_data = base+15;
/* Initialize the pending registers. */
bcopy(zs_init_reg, cs->cs_preg, 16);
cs->cs_preg[5] |= (ZSWR5_DTR | ZSWR5_RTS);
/* XXX: Preserve BAUD rate from boot loader. */
/* XXX: Also, why reset the chip here? -gwr */
/* cs->cs_defspeed = zs_get_speed(cs); */
cs->cs_defspeed = 9600; /* XXX */
/* Clear the master interrupt enable. */
zs_write_reg(cs, 9, 0);
/* Reset the whole SCC chip. */
zs_write_reg(cs, 9, ZSWR9_HARD_RESET);
/* Copy "pending" to "current" and H/W. */
zs_loadchannelregs(cs);
/* Point the console at the SCC. */
cn_tab = &zscons;
}