File: [local] / sys / arch / mvme88k / dev / vx.c (download)
Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:04:38 2008 UTC (16 years, 5 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD Changes since 1.1: +0 -0 lines
Import of OpenBSD 4.2 release kernel tree with initial code to support
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO
Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)
|
/* $OpenBSD: vx.c,v 1.38 2006/07/28 21:46:02 miod Exp $ */
/*
* Copyright (c) 1999 Steve Murphree, Jr.
* All rights reserved.
*
* 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. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*/
/* This card lives in D16 space */
#define __BUS_SPACE_RESTRICT_D16__
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/proc.h>
#include <sys/tty.h>
#include <sys/uio.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/device.h>
#include <sys/syslog.h>
#include <machine/autoconf.h>
#include <machine/conf.h>
#include <machine/cpu.h>
#include <machine/psl.h>
#include <dev/cons.h>
#include <mvme88k/dev/vme.h>
#include <mvme88k/dev/vxreg.h>
#define splvx() spltty()
struct vx_info {
struct tty *tty;
u_char vx_swflags;
int vx_linestatus;
int open;
int waiting;
u_char vx_speed;
u_char read_pending;
struct wring *wringp;
struct rring *rringp;
};
struct vxsoftc {
struct device sc_dev;
struct vx_info sc_info[NVXPORTS];
struct vxreg *vx_reg;
vaddr_t board_vaddr;
struct channel *channel;
char channel_number;
struct packet sc_bppwait_pkt;
void *sc_bppwait_pktp;
struct intrhand sc_ih;
int sc_vec;
struct envelope *elist_head, *elist_tail;
struct packet *plist_head, *plist_tail;
};
int vxmatch(struct device *, void *, void *);
void vxattach(struct device *, struct device *, void *);
struct cfattach vx_ca = {
sizeof(struct vxsoftc), vxmatch, vxattach
};
struct cfdriver vx_cd = {
NULL, "vx", DV_TTY
};
int bpp_send(struct vxsoftc *, void *, int);
void ccode(struct vxsoftc *, int, char);
int create_channels(struct vxsoftc *);
void create_free_queue(struct vxsoftc *);
short dtr_ctl(struct vxsoftc *, int, int);
int env_isvalid(struct envelope *);
struct envelope *find_status_packet(struct vxsoftc *, struct packet *);
short flush_ctl(struct vxsoftc *, int, int);
struct envelope *get_cmd_tail(struct vxsoftc *);
void *get_free_envelope(struct vxsoftc *);
void *get_free_packet(struct vxsoftc *);
struct envelope *get_next_envelope(struct vxsoftc *, struct envelope *);
struct packet *get_packet(struct vxsoftc *, struct envelope *);
struct envelope *get_status_head(struct vxsoftc *);
void put_free_envelope(struct vxsoftc *, void *);
void put_free_packet(struct vxsoftc *, void *);
void read_chars(struct vxsoftc *, int);
void read_wakeup(struct vxsoftc *, int);
short rts_ctl(struct vxsoftc *, int, int);
void set_status_head(struct vxsoftc *, void *);
void vx_break(struct vxsoftc *, int);
int vx_ccparam(struct vxsoftc *, struct termios *, int);
int vx_event(struct vxsoftc *, struct packet *);
void vx_frame(struct vxsoftc *, int);
int vx_init(struct vxsoftc *);
int vx_intr(void *);
int vx_mctl(dev_t, int, int);
void vx_overflow(struct vxsoftc *, int, long *, u_char *);
int vx_param(struct tty *, struct termios *);
int vx_poll(struct vxsoftc *, struct packet *);
void vxputc(struct vxsoftc *, int, u_char);
int vx_sintr(struct vxsoftc *);
void vxstart(struct tty *tp);
u_short vxtspeed(int);
void vx_unblock(struct tty *);
/* flags for bpp_send() */
#define NOWAIT 0
#define WAIT 1
#define VX_UNIT(x) (minor(x) / NVXPORTS)
#define VX_PORT(x) (minor(x) % NVXPORTS)
/*
* Convert dual-ported physical addresse to host virtual address, and the
* opposite.
*/
#define VIRTUAL(addr) (((addr) & 0xffff) + sc->board_vaddr)
#define PHYSICAL(addr) \
(((addr - sc->board_vaddr) & 0xffff) | LOCAL_DPMEM_ADDRESS)
#if 0
#define LO(x) (u_short)((unsigned long)x & 0x0000FFFF)
#define HI(x) (u_short)((unsigned long)x >> 16)
#else
#define LO(x) (u_short)(x)
#define HI(x) (LOCAL_DPMEM_ADDRESS >> 16)
#endif
struct tty *
vxtty(dev_t dev)
{
int unit, port;
struct vxsoftc *sc;
unit = VX_UNIT(dev);
if (unit >= vx_cd.cd_ndevs ||
(sc = (struct vxsoftc *)vx_cd.cd_devs[unit]) == NULL) {
return (NULL);
}
port = VX_PORT(dev);
return sc->sc_info[port].tty;
}
int
vxmatch(struct device *parent, void *self, void *aux)
{
struct vxreg *vx_reg;
struct confargs *ca = aux;
bus_space_tag_t iot = ca->ca_iot;
bus_space_handle_t ioh;
int rc;
if (bus_space_map(iot, ca->ca_paddr, 0x10000, 0, &ioh) != 0)
return 0;
vx_reg = (struct vxreg *)bus_space_vaddr(iot, ioh);
rc = badaddr((vaddr_t)&vx_reg->ipc_cr, 1);
bus_space_unmap(iot, ioh, 0x10000);
return rc == 0;
}
void
vxattach(struct device *parent, struct device *self, void *aux)
{
struct vxsoftc *sc = (struct vxsoftc *)self;
struct confargs *ca = aux;
bus_space_tag_t iot = ca->ca_iot;
bus_space_handle_t ioh;
if (ca->ca_vec < 0) {
printf(": no more interrupts!\n");
return;
}
if (ca->ca_ipl < 0)
ca->ca_ipl = IPL_TTY;
if (bus_space_map(iot, ca->ca_paddr, 0x10000, 0, &ioh) != 0) {
printf(": can't map registers!\n");
return;
}
/* set up dual port memory and registers and init */
sc->board_vaddr = (vaddr_t)bus_space_vaddr(iot, ioh);
sc->vx_reg = (struct vxreg *)sc->board_vaddr;
sc->channel = (struct channel *)(sc->board_vaddr + 0x0100);
sc->sc_vec = ca->ca_vec;
printf("\n");
if (create_channels(sc) != 0) {
printf("%s: failed to create channel %d\n",
sc->sc_dev.dv_xname, sc->channel->channel_number);
return;
}
if (vx_init(sc) != 0) {
printf("%s: failed to initialize\n", sc->sc_dev.dv_xname);
return;
}
/* enable interrupts */
sc->sc_ih.ih_fn = vx_intr;
sc->sc_ih.ih_arg = sc;
sc->sc_ih.ih_wantframe = 0;
sc->sc_ih.ih_ipl = IPL_TTY;
vmeintr_establish(ca->ca_vec, &sc->sc_ih, self->dv_xname);
}
short
dtr_ctl(struct vxsoftc *sc, int port, int on)
{
struct packet pkt;
bzero(&pkt, sizeof(struct packet));
pkt.command = CMD_IOCTL;
pkt.ioctl_cmd_l = IOCTL_TCXONC;
pkt.command_pipe_number = sc->channel_number;
pkt.status_pipe_number = sc->channel_number;
pkt.device_number = port;
if (on) {
pkt.ioctl_arg_l = 6; /* assert DTR */
} else {
pkt.ioctl_arg_l = 7; /* negate DTR */
}
return bpp_send(sc, &pkt, WAIT);
}
short
rts_ctl(struct vxsoftc *sc, int port, int on)
{
struct packet pkt;
bzero(&pkt, sizeof(struct packet));
pkt.command = CMD_IOCTL;
pkt.ioctl_cmd_l = IOCTL_TCXONC;
pkt.command_pipe_number = sc->channel_number;
pkt.status_pipe_number = sc->channel_number;
pkt.device_number = port;
if (on) {
pkt.ioctl_arg_l = 4; /* assert RTS */
} else {
pkt.ioctl_arg_l = 5; /* negate RTS */
}
return bpp_send(sc, &pkt, WAIT);
}
#if 0
short
flush_ctl(struct vxsoftc *sc, int port, int which)
{
struct packet pkt;
bzero(&pkt, sizeof(struct packet));
pkt.command = CMD_IOCTL;
pkt.ioctl_cmd_l = IOCTL_TCFLSH;
pkt.command_pipe_number = sc->channel_number;
pkt.status_pipe_number = sc->channel_number;
pkt.device_number = port;
pkt.ioctl_arg_l = which; /* 0=input, 1=output, 2=both */
return bpp_send(sc, &pkt, WAIT);
}
#endif
int
vx_mctl(dev_t dev, int bits, int how)
{
int s, unit, port;
struct vxsoftc *sc;
struct vx_info *vxt;
u_char msvr;
unit = VX_UNIT(dev);
port = VX_PORT(dev);
sc = (struct vxsoftc *)vx_cd.cd_devs[unit];
vxt = &sc->sc_info[port];
s = splvx();
switch (how) {
case DMSET:
if (bits & TIOCM_RTS) {
rts_ctl(sc, port, 1);
vxt->vx_linestatus |= TIOCM_RTS;
} else {
rts_ctl(sc, port, 0);
vxt->vx_linestatus &= ~TIOCM_RTS;
}
if (bits & TIOCM_DTR) {
dtr_ctl(sc, port, 1);
vxt->vx_linestatus |= TIOCM_DTR;
} else {
dtr_ctl(sc, port, 0);
vxt->vx_linestatus &= ~TIOCM_DTR;
}
break;
case DMBIC:
if (bits & TIOCM_RTS) {
rts_ctl(sc, port, 0);
vxt->vx_linestatus &= ~TIOCM_RTS;
}
if (bits & TIOCM_DTR) {
dtr_ctl(sc, port, 0);
vxt->vx_linestatus &= ~TIOCM_DTR;
}
break;
case DMBIS:
if (bits & TIOCM_RTS) {
rts_ctl(sc, port, 1);
vxt->vx_linestatus |= TIOCM_RTS;
}
if (bits & TIOCM_DTR) {
dtr_ctl(sc, port, 1);
vxt->vx_linestatus |= TIOCM_DTR;
}
break;
case DMGET:
bits = 0;
msvr = vxt->vx_linestatus;
if (msvr & TIOCM_DSR) {
bits |= TIOCM_DSR;
}
if (msvr & TIOCM_CD) {
bits |= TIOCM_CD;
}
if (msvr & TIOCM_CTS) {
bits |= TIOCM_CTS;
}
if (msvr & TIOCM_DTR) {
bits |= TIOCM_DTR;
}
if (msvr & TIOCM_RTS) {
bits |= TIOCM_RTS;
}
break;
}
splx(s);
#if 0
bits = 0;
bits |= TIOCM_DTR;
bits |= TIOCM_RTS;
bits |= TIOCM_CTS;
bits |= TIOCM_CD;
bits |= TIOCM_DSR;
#endif
return (bits);
}
int
vxopen(dev_t dev, int flag, int mode, struct proc *p)
{
int s, unit, port, error;
struct vx_info *vxt;
struct vxsoftc *sc;
struct tty *tp;
struct packet opkt;
u_short code;
unit = VX_UNIT(dev);
port = VX_PORT(dev);
if (unit >= vx_cd.cd_ndevs ||
(sc = (struct vxsoftc *) vx_cd.cd_devs[unit]) == NULL) {
return (ENODEV);
}
vxt = &sc->sc_info[port];
#if 0
flush_ctl(sc, port, 2);
#endif
bzero(&opkt, sizeof(struct packet));
opkt.command_pipe_number = sc->channel_number;
opkt.status_pipe_number = sc->channel_number;
opkt.command = CMD_OPEN;
opkt.device_number = port;
if ((error = bpp_send(sc, &opkt, WAIT)) != 0) {
#ifdef DEBUG_VXT
printf("unit %d, port %d, ", unit, port);
printf("error = %d\n", error);
#endif
return (ENXIO);
}
code = opkt.event_code;
s = splvx();
if (vxt->tty) {
tp = vxt->tty;
} else {
tp = vxt->tty = ttymalloc();
}
/* set line status */
tp->t_state |= TS_CARR_ON;
if (code & E_DCD) {
tp->t_state |= TS_CARR_ON;
vxt->vx_linestatus |= TIOCM_CD;
}
if (code & E_DSR) {
vxt->vx_linestatus |= TIOCM_DSR;
}
if (code & E_CTS) {
vxt->vx_linestatus |= TIOCM_CTS;
}
tp->t_oproc = vxstart;
tp->t_param = vx_param;
tp->t_dev = dev;
if ((tp->t_state & TS_ISOPEN) == 0) {
tp->t_state |= TS_WOPEN;
ttychars(tp);
if (tp->t_ispeed == 0) {
/*
* only when cleared do we reset to defaults.
*/
tp->t_iflag = TTYDEF_IFLAG;
tp->t_oflag = TTYDEF_OFLAG;
tp->t_lflag = TTYDEF_LFLAG;
tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
tp->t_cflag = TTYDEF_CFLAG;
}
/*
* do these all the time
*/
if (vxt->vx_swflags & TIOCFLAG_CLOCAL)
tp->t_cflag |= CLOCAL;
if (vxt->vx_swflags & TIOCFLAG_CRTSCTS)
tp->t_cflag |= CRTSCTS;
if (vxt->vx_swflags & TIOCFLAG_MDMBUF)
tp->t_cflag |= MDMBUF;
vx_param(tp, &tp->t_termios);
ttsetwater(tp);
(void)vx_mctl(dev, TIOCM_DTR | TIOCM_RTS, DMSET);
tp->t_state |= TS_CARR_ON;
} else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
splx(s);
return (EBUSY);
}
/*
* Reset the tty pointer, as there could have been a dialout
* use of the tty with a dialin open waiting.
*/
tp->t_dev = dev;
vxt->open = 1;
splx(s);
return (*linesw[tp->t_line].l_open)(dev, tp);
}
int
vx_param(struct tty *tp, struct termios *t)
{
int unit, port;
struct vxsoftc *sc;
dev_t dev;
dev = tp->t_dev;
unit = VX_UNIT(dev);
if (unit >= vx_cd.cd_ndevs ||
(sc = (struct vxsoftc *) vx_cd.cd_devs[unit]) == NULL) {
return (ENODEV);
}
port = VX_PORT(dev);
tp->t_ispeed = t->c_ispeed;
tp->t_ospeed = t->c_ospeed;
tp->t_cflag = t->c_cflag;
vx_ccparam(sc, t, port);
vx_unblock(tp);
return 0;
}
int
vxclose(dev_t dev, int flag, int mode, struct proc *p)
{
int unit, port;
struct tty *tp;
struct vx_info *vxt;
struct vxsoftc *sc;
int s;
struct packet cpkt;
unit = VX_UNIT(dev);
if (unit >= vx_cd.cd_ndevs ||
(sc = (struct vxsoftc *) vx_cd.cd_devs[unit]) == NULL) {
return (ENODEV);
}
port = VX_PORT(dev);
vxt = &sc->sc_info[port];
#if 0
flush_ctl(sc, port, 2); /* flush both input and output */
#endif
tp = vxt->tty;
(*linesw[tp->t_line].l_close)(tp, flag);
s = splvx();
if ((tp->t_cflag & HUPCL) != 0) {
rts_ctl(sc, port, 0);
dtr_ctl(sc, port, 0);
}
bzero(&cpkt, sizeof(struct packet));
cpkt.command_pipe_number = sc->channel_number;
cpkt.status_pipe_number = sc->channel_number;
cpkt.command = CMD_CLOSE;
cpkt.device_number = port;
bpp_send(sc, &cpkt, NOWAIT);
vxt->open = 0;
splx(s);
ttyclose(tp);
return (0);
}
void
read_wakeup(struct vxsoftc *sc, int port)
{
struct packet rwp;
struct vx_info *volatile vxt;
vxt = &sc->sc_info[port];
/*
* If we already have a read_wakeup paket
* for this port, do nothing.
*/
if (vxt->read_pending != 0)
return;
else
vxt->read_pending = 1;
bzero(&rwp, sizeof(struct packet));
rwp.command_pipe_number = sc->channel_number;
rwp.status_pipe_number = sc->channel_number;
rwp.command = CMD_READW;
rwp.device_number = port;
/*
* Do not wait. Characters will be transferred
* to (*linesw[tp->t_line].l_rint)(c, tp); by
* vx_intr() (IPC will notify via interrupt)
*/
bpp_send(sc, &rwp, NOWAIT);
}
int
vxread(dev_t dev, struct uio *uio, int flag)
{
int unit, port;
struct tty *tp;
struct vx_info *volatile vxt;
struct vxsoftc *volatile sc;
unit = VX_UNIT(dev);
if (unit >= vx_cd.cd_ndevs ||
(sc = (struct vxsoftc *) vx_cd.cd_devs[unit]) == NULL) {
return (ENODEV);
}
port = VX_PORT(dev);
vxt = &sc->sc_info[port];
tp = vxt->tty;
if (!tp)
return ENXIO;
return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
}
int
vxwrite(dev_t dev, struct uio *uio, int flag)
{
int unit, port;
struct tty *tp;
struct vx_info *vxt;
struct vxsoftc *sc;
struct wring *wp;
struct packet wwp;
u_short get, put;
unit = VX_UNIT(dev);
if (unit >= vx_cd.cd_ndevs ||
(sc = (struct vxsoftc *) vx_cd.cd_devs[unit]) == NULL) {
return (ENODEV);
}
port = VX_PORT(dev);
vxt = &sc->sc_info[port];
tp = vxt->tty;
if (!tp)
return ENXIO;
wp = sc->sc_info[port].wringp;
get = wp->get;
put = wp->put;
if ((put + 1) == get) {
bzero(&wwp, sizeof(struct packet));
wwp.command_pipe_number = sc->channel_number;
wwp.status_pipe_number = sc->channel_number;
wwp.command = CMD_WRITEW;
wwp.device_number = port;
if (bpp_send(sc, &wwp, WAIT))
return (ENXIO);
}
return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
}
int
vxioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
int error;
int unit, port;
struct tty *tp;
struct vx_info *vxt;
struct vxsoftc *sc;
unit = VX_UNIT(dev);
if (unit >= vx_cd.cd_ndevs ||
(sc = (struct vxsoftc *) vx_cd.cd_devs[unit]) == NULL) {
return (ENODEV);
}
port = VX_PORT(dev);
vxt = &sc->sc_info[port];
tp = vxt->tty;
if (!tp)
return ENXIO;
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
if (error >= 0)
return (error);
error = ttioctl(tp, cmd, data, flag, p);
if (error >= 0)
return (error);
switch (cmd) {
case TIOCSBRK:
/* */
break;
case TIOCCBRK:
/* */
break;
case TIOCSDTR:
vx_mctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIS);
break;
case TIOCCDTR:
vx_mctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIC);
break;
case TIOCMSET:
vx_mctl(dev, *(int *)data, DMSET);
break;
case TIOCMBIS:
vx_mctl(dev, *(int *)data, DMBIS);
break;
case TIOCMBIC:
vx_mctl(dev, *(int *)data, DMBIC);
break;
case TIOCMGET:
*(int *)data = vx_mctl(dev, 0, DMGET);
break;
case TIOCGFLAGS:
*(int *)data = vxt->vx_swflags;
break;
case TIOCSFLAGS:
error = suser(p, 0);
if (error != 0)
return (EPERM);
vxt->vx_swflags = *(int *)data;
vxt->vx_swflags &= /* only allow valid flags */
(TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | TIOCFLAG_CRTSCTS);
break;
default:
return (ENOTTY);
}
return 0;
}
int
vxstop(struct tty *tp, int flag)
{
int s;
s = splvx();
if (tp->t_state & TS_BUSY) {
if ((tp->t_state & TS_TTSTOP) == 0)
tp->t_state |= TS_FLUSH;
}
splx(s);
return 0;
}
void
vxputc(struct vxsoftc *sc, int port, u_char c)
{
struct wring *wp;
wp = sc->sc_info[port].wringp;
wp->data[wp->put++ & (WRING_BUF_SIZE - 1)] = c;
wp->put &= (WRING_BUF_SIZE - 1);
}
u_short
vxtspeed(int speed)
{
switch (speed) {
case B0:
return VB0;
case B50:
return VB50;
case B75:
return VB75;
case B110:
return VB110;
case B134:
return VB134;
case B150:
return VB150;
case B200:
return VB200;
case B300:
return VB300;
case B600:
return VB600;
case B1200:
return VB1200;
case B1800:
return VB1800;
case B2400:
return VB2400;
case B4800:
return VB4800;
case B9600:
return VB9600;
case B19200:
return VB19200;
case B38400:
return VB38400;
default:
return VB9600;
}
}
int
vx_ccparam(struct vxsoftc *sc, struct termios *par, int port)
{
int imask = 0, s;
int cflag;
struct packet pkt;
if (par->c_ospeed == 0) {
s = splvx();
/* disconnect, drop RTS DTR stop receiver */
rts_ctl(sc, port, 0);
dtr_ctl(sc, port, 0);
splx(s);
return (0xff);
}
bzero(&pkt, sizeof(struct packet));
pkt.command = CMD_IOCTL;
pkt.ioctl_cmd_l = IOCTL_TCGETA;
pkt.command_pipe_number = sc->channel_number;
pkt.status_pipe_number = sc->channel_number;
pkt.device_number = port;
if (bpp_send(sc, &pkt, WAIT))
return 0xff;
cflag = pkt.pb.tio.c_cflag;
cflag |= vxtspeed(par->c_ospeed);
switch (par->c_cflag & CSIZE) {
case CS5:
cflag |= VCS5;
imask = 0x1F;
break;
case CS6:
cflag |= VCS6;
imask = 0x3F;
break;
case CS7:
cflag |= VCS7;
imask = 0x7F;
break;
default:
cflag |= VCS8;
imask = 0xFF;
}
if (par->c_cflag & PARENB) cflag |= VPARENB;
else cflag &= ~VPARENB;
if (par->c_cflag & PARODD) cflag |= VPARODD;
else cflag &= ~VPARODD;
if (par->c_cflag & CREAD) cflag |= VCREAD;
else cflag &= ~VCREAD;
if (par->c_cflag & CLOCAL) cflag |= VCLOCAL;
else cflag &= ~VCLOCAL;
if (par->c_cflag & HUPCL) cflag |= VHUPCL;
else cflag &= ~VHUPCL;
pkt.command = CMD_IOCTL;
pkt.ioctl_cmd_l = IOCTL_TCSETA;
pkt.command_pipe_number = sc->channel_number;
pkt.status_pipe_number = sc->channel_number;
pkt.device_number = port;
pkt.pb.tio.c_cflag = cflag;
if (bpp_send(sc, &pkt, WAIT))
return 0xff;
return imask;
}
void
vx_unblock(struct tty *tp)
{
tp->t_state &= ~TS_FLUSH;
if (tp->t_outq.c_cc != 0)
vxstart(tp);
}
void
vxstart(struct tty *tp)
{
dev_t dev;
struct vxsoftc *sc;
struct wring *wp;
int cc, port, unit, s, cnt, i;
u_short get, put;
char buffer[256];
dev = tp->t_dev;
port = VX_PORT(dev);
unit = VX_UNIT(dev);
if (unit >= vx_cd.cd_ndevs ||
(sc = (struct vxsoftc *) vx_cd.cd_devs[unit]) == NULL) {
return;
}
if ((tp->t_state & TS_ISOPEN) == 0)
return;
s = splvx();
if ((tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP | TS_FLUSH)) == 0) {
tp->t_state |= TS_BUSY;
wp = sc->sc_info[port].wringp;
get = wp->get;
put = wp->put;
cc = tp->t_outq.c_cc;
while (cc > 0) {
cnt = min(sizeof buffer, cc);
cnt = q_to_b(&tp->t_outq, buffer, cnt);
buffer[cnt] = 0;
for (i = 0; i < cnt; i++) {
vxputc(sc, port, buffer[i]);
}
cc -= cnt;
}
tp->t_state &= ~TS_BUSY;
}
splx(s);
}
void
read_chars(struct vxsoftc *sc, int port)
{
/*
* This routine is called by vx_intr() when there are
* characters in the read ring. It will process one
* cooked line, put the chars in the line disipline ring,
* and then return. The characters may then
* be read by vxread.
*/
struct vx_info *vxt;
struct rring *rp;
struct tty *tp;
u_short get, put;
int frame_count, i, open;
char c;
vxt = &sc->sc_info[port];
tp = vxt->tty;
rp = vxt->rringp;
open = vxt->open;
get = rp->get;
put = rp->put;
#ifdef DEBUG_VXT
printf("read_chars() get=%d, put=%d ", get, put);
printf("open = %d ring at 0x%x\n", open, rp);
#endif
while (get != put) {
frame_count = rp->data[rp->get++ & (RRING_BUF_SIZE - 1)];
rp->get &= (RRING_BUF_SIZE - 1);
for (i = 0; i < frame_count; i++) {
c = rp->data[rp->get++ & (RRING_BUF_SIZE - 1)];
rp->get &= (RRING_BUF_SIZE - 1);
if (open)
(*linesw[tp->t_line].l_rint)(c, tp);
}
c = rp->data[rp->get++ & (RRING_BUF_SIZE - 1)];
rp->get &= (RRING_BUF_SIZE - 1);
if (!(c & DELIMITER)) {
vx_frame(sc, port);
break;
} else {
break;
}
get = rp->get;
put = rp->put;
}
vxt->read_pending = 0;
read_wakeup(sc, port);
}
void
ccode(struct vxsoftc *sc, int port, char c)
{
struct vx_info *vxt;
struct tty *tp;
tp = vxt->tty;
(*linesw[tp->t_line].l_rint)(c, tp);
}
int
vx_intr(void *arg)
{
struct vxsoftc *sc = arg;
struct envelope *envp, *next_envp;
struct packet *pktp, pkt;
int valid;
short cmd;
u_char port;
while (env_isvalid(get_status_head(sc))) {
pktp = get_packet(sc, get_status_head(sc));
valid = env_isvalid(get_status_head(sc));
cmd = pktp->command;
port = pktp->device_number;
/*
* If we are waiting on this packet, store the info
* so bpp_send can process the packet
*/
if (sc->sc_bppwait_pktp == pktp)
d16_bcopy(pktp, &sc->sc_bppwait_pkt, sizeof(struct packet));
d16_bcopy(pktp, &pkt, sizeof(struct packet));
next_envp = get_next_envelope(sc, get_status_head(sc));
envp = get_status_head(sc);
/* return envelope and packet to the free queues */
put_free_envelope(sc, envp);
put_free_packet(sc, pktp);
/* mark new status pipe head pointer */
set_status_head(sc, next_envp);
/* if it was valid, process packet */
switch (cmd) {
case CMD_READW:
#ifdef DEBUG_VXT
printf("READW Packet\n");
#endif
read_chars(sc, port);
break;
case CMD_WRITEW:
#ifdef DEBUG_VXT
printf("WRITEW Packet\n"); /* Still don't know XXXsmurph */
#endif
break;
case CMD_EVENT:
#ifdef DEBUG_VXT
printf("EVENT Packet\n");
#endif
vx_event(sc, &pkt);
break;
case CMD_PROCESSED:
#ifdef DEBUG_VXT
printf("CMD_PROCESSED Packet\n");
#endif
break;
default:
#ifdef DEBUG_VXT
printf("Other packet 0x%x\n", cmd);
#endif
break;
}
}
return 1;
}
int
vx_event(struct vxsoftc *sc, struct packet *evntp)
{
u_short code = evntp->event_code;
struct packet evnt;
struct vx_info *vxt;
vxt = &sc->sc_info[evntp->device_number];
if (code & E_INTR) {
ccode(sc, evntp->device_number, CINTR);
}
if (code & E_QUIT) {
ccode(sc, evntp->device_number, CQUIT);
}
if (code & E_HUP) {
rts_ctl(sc, evntp->device_number, 0);
dtr_ctl(sc, evntp->device_number, 0);
}
if (code & E_DCD) {
vxt->vx_linestatus |= TIOCM_CD;
}
if (code & E_DSR) {
vxt->vx_linestatus |= TIOCM_DSR;
}
if (code & E_CTS) {
vxt->vx_linestatus |= TIOCM_CTS;
}
if (code & E_LOST_DCD) {
vxt->vx_linestatus &= ~TIOCM_CD;
}
if (code & E_LOST_DSR) {
vxt->vx_linestatus &= ~TIOCM_DSR;
}
if (code & E_LOST_CTS) {
vxt->vx_linestatus &= ~TIOCM_CTS;
}
if (code & E_PR_FAULT) {
/* do something... */
}
if (code & E_PR_POUT) {
/* do something... */
}
if (code & E_PR_SELECT) {
/* do something... */
}
if (code & E_SWITCH) {
/* do something... */
}
if (code & E_BREAK) {
vx_break(sc, evntp->device_number);
}
/* send an event packet back to the device */
bzero(&evnt, sizeof(struct packet));
evnt.command = CMD_EVENT;
evnt.device_number = evntp->device_number;
evnt.command_pipe_number = sc->channel_number;
/* return status on same channel */
evnt.status_pipe_number = sc->channel_number;
/* send packet to the firmware */
bpp_send(sc, &evnt, NOWAIT);
return 1;
}
void
vx_overflow(struct vxsoftc *sc, int port, long *ptime, u_char *msg)
{
log(LOG_WARNING, "%s port %d: overrun\n",
sc->sc_dev.dv_xname, port);
}
void
vx_frame(struct vxsoftc *sc, int port)
{
log(LOG_WARNING, "%s port %d: frame error\n",
sc->sc_dev.dv_xname, port);
}
void
vx_break(struct vxsoftc *sc, int port)
{
/*
* No need to check for a ddb break, as the console can never be on
* this hardware.
*/
log(LOG_WARNING, "%s port %d: break detected\n",
sc->sc_dev.dv_xname, port);
}
/*
* Initialization and Buffered Pipe Protocol (BPP) code
*/
void
create_free_queue(struct vxsoftc *sc)
{
int i;
struct envelope *envp, env;
struct packet *pktp, pkt;
envp = (struct envelope *)VIRTUAL(ENVELOPE_AREA);
sc->elist_head = envp;
for (i = 1; i <= NENVELOPES; i++) {
bzero(&env, sizeof(struct envelope));
if (i == NENVELOPES)
env.link = NULL;
else
env.link = ENVELOPE_AREA + i * sizeof(struct envelope);
env.packet_ptr = NULL;
env.valid_flag = 0;
d16_bcopy(&env, envp, sizeof(struct envelope));
envp++;
}
sc->elist_tail = --envp;
pktp = (struct packet *)VIRTUAL(PACKET_AREA);
sc->plist_head = pktp;
for (i = 1; i <= NPACKETS; i++) {
bzero(&pkt, sizeof(struct packet));
if (i == NPACKETS)
pkt.link = NULL;
else
pkt.link = PACKET_AREA + i * sizeof(struct packet);
d16_bcopy(&pkt, pktp, sizeof(struct packet));
pktp++;
}
sc->plist_tail = --pktp;
}
void *
get_free_envelope(struct vxsoftc *sc)
{
struct envelope *envp;
paddr_t link;
envp = sc->elist_head;
/* pick envelope next pointer from the envelope itself */
d16_bcopy((const void *)&envp->link, &link, sizeof link);
sc->elist_head = (struct envelope *)VIRTUAL(link);
d16_bzero(envp, sizeof(struct envelope));
return envp;
}
void
put_free_envelope(struct vxsoftc *sc, void *ep)
{
struct envelope *envp = (struct envelope *)ep;
u_long link;
#if 0
d16_bzero(envp, sizeof(struct envelope));
#endif
/* put envelope next pointer in the envelope itself */
link = PHYSICAL((vaddr_t)envp);
d16_bzero((void *)&envp->link, sizeof envp->link);
d16_bcopy(&link, (void *)&sc->elist_tail->link, sizeof link);
sc->elist_tail = envp;
}
void *
get_free_packet(struct vxsoftc *sc)
{
struct packet *pktp;
paddr_t link;
pktp = sc->plist_head;
/* pick packet next pointer from the packet itself */
d16_bcopy((const void *)&pktp->link, &link, sizeof link);
sc->plist_head = (struct packet *)VIRTUAL(link);
d16_bzero(pktp, sizeof(struct packet));
return pktp;
}
void
put_free_packet(struct vxsoftc *sc, void *pp)
{
struct packet *pktp = (struct packet *)pp;
u_long link;
#if 0
d16_bzero(pktp, sizeof(struct packet));
#endif
pktp->command = CMD_PROCESSED;
/* put packet next pointer in the packet itself */
link = PHYSICAL((vaddr_t)pktp);
d16_bzero((void *)&pktp->link, sizeof pktp->link);
d16_bcopy(&link, (void *)&sc->plist_tail->link, sizeof link);
sc->plist_tail = pktp;
}
/*
* This is the nitty gritty. All the rest if this code
* was hell to come by. Getting this right from the
* Moto manual took *time*!
*/
int
create_channels(struct vxsoftc *sc)
{
u_long envp;
u_short status;
u_short tas;
struct vxreg *ipc_csr = sc->vx_reg;
/* wait for busy bit to clear */
while ((ipc_csr->ipc_cr & IPC_CR_BUSY))
;
create_free_queue(sc);
/* set up channel header. we only want one */
tas = ipc_csr->ipc_tas;
while (!(tas & IPC_TAS_VALID_STATUS)) {
envp = PHYSICAL((vaddr_t)get_free_envelope(sc));
sc->channel->command_pipe_head_ptr_h = HI(envp);
sc->channel->command_pipe_head_ptr_l = LO(envp);
sc->channel->command_pipe_tail_ptr_h =
sc->channel->command_pipe_head_ptr_h;
sc->channel->command_pipe_tail_ptr_l =
sc->channel->command_pipe_head_ptr_l;
envp = PHYSICAL((vaddr_t)get_free_envelope(sc));
sc->channel->status_pipe_head_ptr_h = HI(envp);
sc->channel->status_pipe_head_ptr_l = LO(envp);
sc->channel->status_pipe_tail_ptr_h =
sc->channel->status_pipe_head_ptr_h;
sc->channel->status_pipe_tail_ptr_l =
sc->channel->status_pipe_head_ptr_l;
sc->channel->interrupt_level = IPL_TTY;
sc->channel->interrupt_vec = sc->sc_vec;
sc->channel->channel_priority = 0;
sc->channel->channel_number = 0;
sc->channel->valid = 1;
sc->channel->address_modifier = 0x8d; /* A32/D16 supervisor data access */
sc->channel->datasize = 0; /* 32 bit data mode */
/* loop until TAS bit is zero */
while ((ipc_csr->ipc_tas & IPC_TAS_TAS))
;
ipc_csr->ipc_tas |= IPC_TAS_TAS;
/* load address of channel header */
ipc_csr->ipc_addrh = HI(CHANNEL_H);
ipc_csr->ipc_addrl = LO(CHANNEL_H);
/* load address modifier reg (supervisor data access) */
ipc_csr->ipc_amr = 0x8d;
/* load tas with create channel command */
ipc_csr->ipc_tas |= IPC_CSR_CREATE;
/* set vaild command bit */
ipc_csr->ipc_tas |= IPC_TAS_VALID_CMD;
/* notify IPC of the CSR command */
ipc_csr->ipc_cr |= IPC_CR_ATTEN;
/* loop until IPC sets valid status bit */
delay(5000);
tas = ipc_csr->ipc_tas;
}
/* save the status */
status = ipc_csr->ipc_sr;
/* set COMMAND COMPLETE bit */
ipc_csr->ipc_tas |= IPC_TAS_COMPLETE;
/* notify IPC that we are through */
ipc_csr->ipc_cr |= IPC_CR_ATTEN;
/* check and see if the channel was created */
if (status == 0 && sc->channel->valid) {
sc->channel_number = sc->channel->channel_number;
#ifdef DEBUG_VXT
printf("%s: created channel %d\n", sc->sc_dev.dv_xname,
sc->channel->channel_number);
#endif
return 0;
} else {
switch (status) {
case 0x0000:
printf("%s: channel not valid\n",
sc->sc_dev.dv_xname);
break;
case 0xFFFF:
printf("%s: invalid CSR command\n",
sc->sc_dev.dv_xname);
break;
case 0xC000:
printf("%s: could not read channel structure\n",
sc->sc_dev.dv_xname);
break;
case 0x8000:
printf("%s: could not write channel structure\n",
sc->sc_dev.dv_xname);
break;
default:
printf("%s: unknown IPC CSR command error 0x%x\n",
sc->sc_dev.dv_xname, status);
break;
}
return 1;
}
}
struct envelope *
get_next_envelope(struct vxsoftc *sc, struct envelope *thisenv)
{
paddr_t ptr;
d16_bcopy((const void*)&thisenv->link, &ptr, sizeof ptr);
return (struct envelope *)VIRTUAL(ptr);
}
int
env_isvalid(struct envelope *thisenv)
{
return (int)thisenv->valid_flag;
}
struct envelope *
get_cmd_tail(struct vxsoftc *sc)
{
paddr_t retaddr;
retaddr = sc->channel->command_pipe_tail_ptr_h << 16;
retaddr += sc->channel->command_pipe_tail_ptr_l;
return (struct envelope *)VIRTUAL(retaddr);
}
struct envelope *
get_status_head(struct vxsoftc *sc)
{
paddr_t retaddr;
retaddr = sc->channel->status_pipe_head_ptr_h << 16;
retaddr += sc->channel->status_pipe_head_ptr_l;
return (struct envelope *)VIRTUAL(retaddr);
}
void
set_status_head(struct vxsoftc *sc, void *envp)
{
paddr_t ptr;
ptr = PHYSICAL((vaddr_t)envp);
sc->channel->status_pipe_head_ptr_h = HI(ptr);
sc->channel->status_pipe_head_ptr_l = LO(ptr);
}
struct packet *
get_packet(struct vxsoftc *sc, struct envelope *thisenv)
{
paddr_t baseaddr;
if (thisenv == NULL)
return NULL;
/*
* packet ptr returned on status pipe is only last two bytes
* so we must supply the full address based on the board address.
* This also works for all envelopes because every address is an
* offset to the board address.
*/
d16_bcopy((const void *)&thisenv->packet_ptr, &baseaddr, sizeof baseaddr);
return (struct packet *)VIRTUAL(baseaddr);
}
/*
* Send a command via BPP
*/
int
bpp_send(struct vxsoftc *sc, void *pkt, int wait_flag)
{
struct envelope *envp;
struct packet *pktp;
paddr_t ptr;
int tmo;
/* load up packet in dual port mem */
pktp = get_free_packet(sc);
d16_bcopy(pkt, pktp, sizeof(struct packet));
envp = get_cmd_tail(sc);
ptr = PHYSICAL((vaddr_t)get_free_envelope(sc)); /* put a NULL env on the tail */
sc->channel->command_pipe_tail_ptr_h = HI(ptr);
sc->channel->command_pipe_tail_ptr_l = LO(ptr);
d16_bcopy(&ptr, (void *)&envp->link, sizeof envp->link);
ptr = PHYSICAL((vaddr_t)pktp);
d16_bcopy(&ptr, (void *)&envp->packet_ptr, sizeof envp->packet_ptr);
envp->valid_flag = 1;
sc->vx_reg->ipc_cr |= IPC_CR_ATTEN;
/* wait for a packet to return */
if (wait_flag == NOWAIT)
return 0;
tmo = 0;
while (pktp->command != CMD_PROCESSED) {
#ifdef DEBUG_VXT
printf("Polling for packet 0x%x in envelope 0x%x...\n", pktp, envp);
#endif
vx_intr(sc);
if (++tmo > 20) {
printf("%s: bpp_send pkt %x env %x timed out %d\n",
sc->sc_dev.dv_xname, pktp, envp, pktp->command);
return ETIMEDOUT;
}
delay(5000);
}
d16_bcopy(pktp, pkt, sizeof(struct packet));
return pktp->error_l;
}
/*
* BPP commands
*/
int
vx_init(struct vxsoftc *sc)
{
int i;
int error;
struct init_info *infp, inf;
paddr_t wringp, rringp;
struct packet init;
struct packet evnt;
/* init wait queue */
d16_bzero(&sc->sc_bppwait_pkt, sizeof(struct packet));
sc->sc_bppwait_pktp = NULL;
/* set up init_info array */
wringp = WRING_AREA;
rringp = RRING_AREA;
infp = (struct init_info *)VIRTUAL(INIT_INFO_AREA);
for (i = 0; i < NVXPORTS; i++) {
bzero(&inf, sizeof(struct init_info));
inf.write_ring_ptr_h = HI(wringp);
inf.write_ring_ptr_l = LO(wringp);
sc->sc_info[i].wringp = (struct wring *)VIRTUAL(wringp);
inf.read_ring_ptr_h = HI(rringp);
inf.read_ring_ptr_l = LO(rringp);
sc->sc_info[i].rringp = (struct rring *)VIRTUAL(rringp);
#ifdef DEBUG_VXT
printf("write at 0x%8x, read at 0x%8x\n", wringp, rringp);
#endif
inf.write_ring_size = WRING_DATA_SIZE;
inf.read_ring_size = RRING_DATA_SIZE;
inf.def_termio.c_iflag = VBRKINT;
inf.def_termio.c_oflag = 0;
inf.def_termio.c_cflag = (VB9600 | VCS8);
inf.def_termio.c_lflag = VISIG; /* enable signal processing */
inf.def_termio.c_line = 1; /* raw line discipline */
inf.def_termio.c_cc[0] = CINTR;
inf.def_termio.c_cc[1] = CQUIT;
inf.def_termio.c_cc[2] = CERASE;
inf.def_termio.c_cc[3] = CKILL;
inf.def_termio.c_cc[4] = CEOF;
inf.def_termio.c_cc[5] = CEOL;
d16_bcopy(&inf, infp, sizeof(struct init_info));
wringp += sizeof(struct wring);
rringp += sizeof(struct rring);
infp++;
}
/* set up init_packet */
bzero(&init, sizeof(struct packet));
init.command = CMD_INIT;
init.command_pipe_number = sc->channel_number;
/* return status on the same channel */
init.status_pipe_number = sc->channel_number;
init.interrupt_level = IPL_TTY;
init.interrupt_vec = sc->sc_vec;
init.init_info_ptr_h = HI(INIT_INFO_AREA);
init.init_info_ptr_l = LO(INIT_INFO_AREA);
/* send packet to the firmware and wait for completion */
if ((error = bpp_send(sc, &init, WAIT)) != 0)
return error;
/* send one event packet to each device */
for (i = 0; i < NVXPORTS; i++) {
bzero(&evnt, sizeof(struct packet));
evnt.command = CMD_EVENT;
evnt.device_number = i;
evnt.command_pipe_number = sc->channel_number;
/* return status on same channel */
evnt.status_pipe_number = sc->channel_number;
/* send packet to the firmware */
bpp_send(sc, &evnt, NOWAIT);
}
return 0;
}