NetBSD/sys/arch/emips/ebus/dz_ebus.c
pooka 5f7e80a834 Add support for the Extensible MIPS ("eMIPS") platform. The
NetBSD/emips port runs on Xilinx and Beecube FPGA systems and the
Giano system simulator.

eMIPS is a platform developed at Microsoft Research for researching
reconfigurable computing.  eMIPS allows dynamic loading and scheduling
of application-specific circuits for the purpose of accelerating
computations based on the current workload.

NetBSD eMIPS support for NetBSD 4.x was written at Microsoft Research
by Alessandro Forin and Neil Pittman.  Microsoft Corporation has
donated full copyright to The NetBSD Foundation.

Platform support for eMIPS is the first part of Microsoft's
contribution.  The second part includes the hardware accelerator
framework and will be proposed on tech-kern soon.
2011-01-26 01:18:43 +00:00

794 lines
16 KiB
C

/* $NetBSD: dz_ebus.c,v 1.1 2011/01/26 01:18:50 pooka Exp $ */
/*-
* Copyright (c) 2010 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code was written by Alessandro Forin and Neil Pittman
* at Microsoft Research and contributed to The NetBSD Foundation
* by Microsoft Corporation.
*
* 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.
*
* 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.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: dz_ebus.c,v 1.1 2011/01/26 01:18:50 pooka Exp $");
#include "opt_ddb.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/callout.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/device.h>
#include <sys/kauth.h>
#include <machine/bus.h>
#include <machine/emipsreg.h>
#include <dev/cons.h>
#include <emips/ebus/ebusvar.h>
#include <emips/emips/cons.h>
//#include <emips/emips/machdep.h>
#include "ioconf.h" /* for dz_cd */
#define DZ_C2I(c) ((c)<<3) /* convert controller # to index */
#define DZ_I2C(c) ((c)>>3) /* convert minor to controller # */
#define DZ_PORT(u) ((u)&07) /* extract the port # */
struct dz_softc {
struct device sc_dev; /* Autoconf blaha */
struct evcnt sc_rintrcnt; /* recevive interrupt counts */
struct evcnt sc_tintrcnt; /* transmit interrupt counts */
struct _Usart *sc_dr; /* reg pointers */
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
int sc_consline; /* console line, or -1 */
int sc_rxint; /* Receive interrupt count XXX */
u_char sc_brk; /* Break asserted on some lines */
u_char sc_dsr; /* DSR set bits if no mdm ctrl */
struct dz_linestate {
struct dz_softc *dz_sc; /* backpointer to softc */
int dz_line; /* channel number */
struct tty * dz_tty; /* what we work on */
} sc_dz;
};
void dzrint(struct dz_softc *, uint32_t);
void dzxint(struct dz_softc *, uint32_t);
#ifndef TIOCM_BRK
#define TIOCM_BRK 0100000 /* no equivalent */
static void dzstart(struct tty *);
static int dzparam(struct tty *, struct termios *);
static unsigned dzmctl(struct dz_softc *sc, int line,
int bits, /* one of the TIOCM_xx */
int how); /* one of the DMSET/BIS.. */
#include <dev/dec/dzkbdvar.h>
#endif
dev_type_open(dzopen);
dev_type_close(dzclose);
dev_type_read(dzread);
dev_type_write(dzwrite);
dev_type_ioctl(dzioctl);
dev_type_stop(dzstop);
dev_type_tty(dztty);
dev_type_poll(dzpoll);
const struct cdevsw dz_cdevsw = {
dzopen, dzclose, dzread, dzwrite, dzioctl,
dzstop, dztty, dzpoll, nommap, ttykqfilter, D_TTY
};
int
dzopen(dev_t dev, int flag, int mode, struct lwp *l)
{
struct tty *tp;
int unit, line;
struct dz_softc *sc;
int s, error = 0;
unit = DZ_I2C(minor(dev));
line = DZ_PORT(minor(dev));
if (unit >= dz_cd.cd_ndevs || dz_cd.cd_devs[unit] == NULL)
return (ENXIO);
sc = (void *)dz_cd.cd_devs[unit];
if (line > 0) /* FIXME fo rmore than one line */
return ENXIO;
tp = sc->sc_dz.dz_tty;
if (tp == NULL)
return (ENODEV);
tp->t_oproc = dzstart;
tp->t_param = dzparam;
tp->t_dev = dev;
if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
return (EBUSY);
if ((tp->t_state & TS_ISOPEN) == 0) {
ttychars(tp);
if (tp->t_ispeed == 0) {
tp->t_iflag = TTYDEF_IFLAG;
tp->t_oflag = TTYDEF_OFLAG;
tp->t_cflag = TTYDEF_CFLAG;
tp->t_lflag = TTYDEF_LFLAG;
tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
}
(void) dzparam(tp, &tp->t_termios);
ttsetwater(tp);
}
/* we have no modem control but..*/
if (dzmctl(sc, line, TIOCM_DTR, DMBIS) & TIOCM_CD)
tp->t_state |= TS_CARR_ON;
s = spltty();
while (!(flag & O_NONBLOCK) && !(tp->t_cflag & CLOCAL) &&
!(tp->t_state & TS_CARR_ON)) {
tp->t_wopen++;
error = ttysleep(tp, &tp->t_rawcv, true, 0);
tp->t_wopen--;
if (error)
break;
}
(void) splx(s);
if (error)
return (error);
return ((*tp->t_linesw->l_open)(dev, tp));
}
int
dzclose(dev_t dev, int flag, int mode, struct lwp *l)
{
struct dz_softc *sc;
struct tty *tp;
int unit, line;
unit = DZ_I2C(minor(dev));
line = DZ_PORT(minor(dev));
sc = (void *)dz_cd.cd_devs[unit];
tp = sc->sc_dz.dz_tty;
(*tp->t_linesw->l_close)(tp, flag);
/* Make sure a BREAK state is not left enabled. */
(void) dzmctl(sc, line, TIOCM_BRK, DMBIC);
/* Do a hangup if so required. */
if ((tp->t_cflag & HUPCL) || tp->t_wopen || !(tp->t_state & TS_ISOPEN))
(void) dzmctl(sc, line, 0, DMSET);
return (ttyclose(tp));
}
int
dzread(dev_t dev, struct uio *uio, int flag)
{
struct tty *tp;
struct dz_softc *sc;
sc = (void *)dz_cd.cd_devs[DZ_I2C(minor(dev))];
tp = sc->sc_dz.dz_tty;
return ((*tp->t_linesw->l_read)(tp, uio, flag));
}
int
dzwrite(dev_t dev, struct uio *uio, int flag)
{
struct tty *tp;
struct dz_softc *sc;
sc = (void *)dz_cd.cd_devs[DZ_I2C(minor(dev))];
tp = sc->sc_dz.dz_tty;
return ((*tp->t_linesw->l_write)(tp, uio, flag));
}
/*ARGSUSED*/
int
dzioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
{
struct dz_softc *sc;
struct tty *tp;
int unit, line;
int error;
unit = DZ_I2C(minor(dev));
line = 0;
sc = (void *)dz_cd.cd_devs[unit];
tp = sc->sc_dz.dz_tty;
error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
if (error >= 0)
return (error);
error = ttioctl(tp, cmd, data, flag, l);
if (error >= 0)
return (error);
switch (cmd) {
case TIOCSBRK:
(void) dzmctl(sc, line, TIOCM_BRK, DMBIS);
break;
case TIOCCBRK:
(void) dzmctl(sc, line, TIOCM_BRK, DMBIC);
break;
case TIOCSDTR:
(void) dzmctl(sc, line, TIOCM_DTR, DMBIS);
break;
case TIOCCDTR:
(void) dzmctl(sc, line, TIOCM_DTR, DMBIC);
break;
case TIOCMSET:
(void) dzmctl(sc, line, *(int *)data, DMSET);
break;
case TIOCMBIS:
(void) dzmctl(sc, line, *(int *)data, DMBIS);
break;
case TIOCMBIC:
(void) dzmctl(sc, line, *(int *)data, DMBIC);
break;
case TIOCMGET:
*(int *)data = (dzmctl(sc, line, 0, DMGET) & ~TIOCM_BRK);
break;
default:
return (EPASSTHROUGH);
}
return (0);
}
/*ARGSUSED*/
void
dzstop(struct tty *tp, int flag)
{
if (tp->t_state & TS_BUSY)
if (!(tp->t_state & TS_TTSTOP))
tp->t_state |= TS_FLUSH;
}
struct tty *
dztty(dev_t dev)
{
struct dz_softc *sc = (void *)dz_cd.cd_devs[DZ_I2C(minor(dev))];
struct tty *tp = sc->sc_dz.dz_tty;
return (tp);
}
int
dzpoll( dev_t dev, int events, struct lwp *l)
{
struct tty *tp;
struct dz_softc *sc;
sc = (void *)dz_cd.cd_devs[DZ_I2C(minor(dev))];
tp = sc->sc_dz.dz_tty;
return ((*tp->t_linesw->l_poll)(tp, events, l));
}
void
dzstart(struct tty *tp)
{
struct dz_softc *sc;
struct clist *cl;
int unit, s;
unit = DZ_I2C(minor(tp->t_dev));
sc = (void *)dz_cd.cd_devs[unit];
s = spltty();
if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) {
splx(s);
return;
}
cl = &tp->t_outq;
ttypull(tp);
if (cl->c_cc == 0) {
splx(s);
return;
}
tp->t_state |= TS_BUSY;
/* was idle, get it started */
dzxint(sc,USI_TXRDY);
splx(s);
}
static int rclk = 25000000; /* BUGBUGBUGBUG */
static int
dzdivisor(int baudrate)
{
int act_baud, divisor, error;
if (baudrate <= 0)
return (0);
divisor = (rclk/8)/(baudrate);
divisor = (divisor/2) + (divisor&1);
if (divisor <= 0)
return (-1);
act_baud = rclk / (divisor * 16);
/* 10 times error in percent: */
error = ((act_baud - baudrate) * 2000 / baudrate + 1) >> 1;
/* 3.0% maximum error tolerance: */
if (error < -30 || error > 30)
return (-1);
return (divisor);
}
static int
dzparam(struct tty *tp, struct termios *t)
{
struct dz_softc *sc;
int cflag = t->c_cflag;
int unit, line;
int speed;
unsigned lpr;
int s;
struct _Usart *dzr;
unit = DZ_I2C(minor(tp->t_dev));
line = DZ_PORT(minor(tp->t_dev));
sc = (void *)dz_cd.cd_devs[unit];
/* check requested parameters */
if (t->c_ispeed != t->c_ospeed)
return (EINVAL);
speed = dzdivisor(t->c_ispeed);
if (speed < 0)
return (EINVAL);
tp->t_ispeed = t->c_ispeed;
tp->t_ospeed = t->c_ospeed;
tp->t_cflag = cflag;
{ static int didit=0;
if (!didit && t->c_ispeed != 38400)
printf("dzparam: c_ispeed %d ignored, keeping 38400\n",t->c_ispeed);
didit = 1;
}
speed = dzdivisor(38400);
if (speed == 0) {
(void) dzmctl(sc, line, 0, DMSET); /* hang up line */
return (0);
}
switch (cflag & CSIZE)
{
case CS5:
lpr = USC_BPC_5;
break;
case CS6:
lpr = USC_BPC_6;
break;
case CS7:
lpr = USC_BPC_7;
break;
default:
lpr = USC_BPC_8;
break;
}
if (cflag & CSTOPB)
lpr |= USC_2STOP;
if (cflag & PARENB) {
if (cflag & PARODD)
lpr |= USC_ODD;
else
lpr |= USC_EVEN;
} else
lpr |= USC_NONE;
s = spltty();
dzr = sc->sc_dr;
dzr->Baud = speed;
dzr->Control = USC_CLKDIV_4 | USC_TXEN | USC_RXEN | lpr;
#define USI_INTRS (USI_RXRDY|USI_RXBRK|USI_OVRE|USI_FRAME|USI_PARE)
dzr->IntrEnable = USI_INTRS;
(void) splx(s);
return (0);
}
static unsigned
dzmctl(struct dz_softc *sc, int line, int bits, int how)
{
unsigned mbits;
int s;
struct _Usart *dzr;
mbits = 0;
s = spltty();
dzr = sc->sc_dr;
/* we have no modem control bits (CD,RI,DTR,DSR,..) */
mbits |= TIOCM_CD;
mbits |= TIOCM_DTR;
if (dzr->ChannelStatus & USI_RXBRK)
mbits |= TIOCM_BRK;
switch (how)
{
case DMSET:
mbits = bits;
break;
case DMBIS:
mbits |= bits;
break;
case DMBIC:
mbits &= ~bits;
break;
case DMGET:
(void) splx(s);
return (mbits);
}
/* BUGBUG work in progress */
if (mbits & TIOCM_BRK) {
sc->sc_brk |= (1 << line);
dzr->Control |= USC_STTBRK;
} else {
sc->sc_brk &= ~(1 << line);
dzr->Control |= USC_STPBRK;
}
(void) splx(s);
return (mbits);
}
#if defined(DDB)
int dz_ddb = 0;
#endif
/* Receiver Interrupt */
void
dzrint(struct dz_softc *sc, uint32_t csr)
{
struct tty *tp;
int cc, mcc;
struct _Usart *dzr;
sc->sc_rxint++;
dzr = sc->sc_dr;
cc = dzr->RxData;
tp = sc->sc_dz.dz_tty;
if (csr & USI_RXBRK)
mcc = CNC_BREAK;
else
mcc = cc;
/* clear errors before we print or bail out */
if (csr & (USI_OVRE|USI_FRAME|USI_PARE))
dzr->Control = USC_RSTSTA;
if (!(tp->t_state & TS_ISOPEN)) {
wakeup(&tp->t_rawq);
return;
}
if (csr & USI_OVRE) {
log(LOG_WARNING, "%s: silo overflow, line %d\n",
sc->sc_dev.dv_xname, 0);
}
if (csr & USI_FRAME)
cc |= TTY_FE;
if (csr & USI_PARE)
cc |= TTY_PE;
#if defined(DDB)
/* ^P drops into DDB */
if (dz_ddb && (cc == 0x10))
Debugger();
#endif
(*tp->t_linesw->l_rint)(cc, tp);
}
/* Transmitter Interrupt */
void
dzxint(struct dz_softc *sc, uint32_t csr)
{
struct tty *tp;
struct clist *cl;
int ch;
struct _Usart *dzr;
dzr = sc->sc_dr;
tp = sc->sc_dz.dz_tty;
cl = &tp->t_outq;
tp->t_state &= ~TS_BUSY;
/* Just send out a char if we have one */
if (cl->c_cc) {
tp->t_state |= TS_BUSY;
ch = getc(cl);
dzr->TxData = ch;
dzr->IntrEnable = USI_TXRDY;
return;
}
/* Nothing to send; turn off intr */
dzr->IntrDisable = USI_TXRDY;
if (tp->t_state & TS_FLUSH)
tp->t_state &= ~TS_FLUSH;
else
ndflush (&tp->t_outq, cl->c_cc);
(*tp->t_linesw->l_start)(tp);
}
/* Machdep part of the driver
*/
int dz_ebus_match(struct device *, struct cfdata *, void *);
void dz_ebus_attach(struct device *, struct device *, void *);
int dz_ebus_intr(void *, void *);
void dz_ebus_cnsetup(paddr_t);
void dz_ebus_cninit(struct consdev*);
int dz_ebus_cngetc(dev_t);
void dz_ebus_cnputc(dev_t, int);
void dz_ebus_cnpollc(dev_t, int);
static int dz_ebus_getmajor(void);
CFATTACH_DECL(dz_ebus, sizeof(struct dz_softc),
dz_ebus_match, dz_ebus_attach, NULL, NULL);
struct consdev dz_ebus_consdev = {
NULL, dz_ebus_cninit, dz_ebus_cngetc, dz_ebus_cnputc,
dz_ebus_cnpollc, NULL, NULL, NULL, NODEV, CN_NORMAL,
};
/* Points to the console regs. Special mapping until VM is turned on.
*/
struct _Usart *dzcn;
int
dz_ebus_match(struct device *parent, struct cfdata *cf, void *aux)
{
struct ebus_attach_args *iba;
struct _Usart *us;
iba = aux;
if (strcmp(iba->ia_name, "dz") != 0)
return (0);
us = (struct _Usart *)iba->ia_vaddr;
if ((us == NULL) ||
(us->Tag != PMTTAG_USART))
return (0);
return (1);
}
void
dz_ebus_attach(struct device *parent, struct device *self, void *aux)
{
struct ebus_attach_args *iba;
struct dz_softc *sc;
iba = aux;
sc = (struct dz_softc *)self;
sc->sc_dr = (struct _Usart *)iba->ia_vaddr;
#if DEBUG
printf(" virt=%p ", (void *)sc->sc_dr);
#endif
printf(": neilsart 1 line");
ebus_intr_establish(parent, (void *)iba->ia_cookie, IPL_TTY,
dz_ebus_intr, sc);
sc->sc_rxint = sc->sc_brk = 0;
sc->sc_consline = 0;
/* Initialize our softc structure. Should be done in open? */
sc->sc_dz.dz_sc = sc;
sc->sc_dz.dz_line = 0;
sc->sc_dz.dz_tty = ttymalloc();
evcnt_attach_dynamic(&sc->sc_rintrcnt, EVCNT_TYPE_INTR, NULL,
sc->sc_dev.dv_xname, "rintr");
evcnt_attach_dynamic(&sc->sc_tintrcnt, EVCNT_TYPE_INTR, NULL,
sc->sc_dev.dv_xname, "tintr");
/* Initialize hw regs */
#if 0
DZ_WRITE_WORD(dr_csr, DZ_CSR_MSE | DZ_CSR_RXIE | DZ_CSR_TXIE);
DZ_WRITE_BYTE(dr_dtr, 0);
DZ_WRITE_BYTE(dr_break, 0);
#endif
/* Switch the console to virtual mode */
dzcn = sc->sc_dr;
/* And test it */
printf("\n");
}
static int
dz_ebus_getmajor(void)
{
extern const struct cdevsw dz_cdevsw;
static int cache = -1;
if (cache != -1)
return (cache);
return (cache = cdevsw_lookup_major(&dz_cdevsw));
}
int
dz_ebus_intr(void *cookie, void *f)
{
struct dz_softc *sc;
struct _Usart *dzr;
uint32_t csr;
sc = cookie;
dzr = sc->sc_dr;
#define USI_INTERRUPTS (USI_INTRS|USI_TXRDY)
for (; ((csr = (dzr->ChannelStatus & dzr->IntrMask)) & USI_INTERRUPTS) != 0;) {
if ((csr & USI_INTRS) != 0)
dzrint(sc, csr);
if ((csr & USI_TXRDY) != 0)
dzxint(sc, csr);
}
return (0);
}
void
dz_ebus_cnsetup(paddr_t addr)
{
dzcn = (struct _Usart *)addr;
#if 0
/* Initialize enough to xmit/recv via polling.
* Bootloader might or might not have done it.
*/
dzcn->Control = USC_RXEN|USC_TXEN|USC_BPC_8|USC_NONE|USC_1STOP|USC_CLKDIV_4;
dzcn->Baud = 0x29; /* 38400 */
#endif
/*
* Point the console at us
*/
cn_tab = &dz_ebus_consdev;
cn_tab->cn_pri = CN_NORMAL;/*CN_REMOTE?*/
cn_tab->cn_dev = makedev(dz_ebus_getmajor(), 0);
}
void dz_ebus_cninit(struct consdev *cn)
{
}
int
dz_ebus_cngetc(dev_t dev)
{
int c, s;
c = 0;
s = spltty();
while ((dzcn->ChannelStatus & USI_RXRDY) == 0)
DELAY(10);
c = dzcn->RxData;
splx(s);
if (c == 13) /* map cr->ln */
c = 10;
return (c);
}
int dzflipped = 0;
void
dz_ebus_cnputc(dev_t dev, int ch)
{
int timeout, s;
/* Don't hang the machine! */
timeout = 1 << 15;
s = spltty();
#if 1
/* Keep wired to hunt for a bug */
if (dzcn && (dzcn != (struct _Usart *)0xfff90000)) {
dzcn = (struct _Usart *)0xfff90000;
dzflipped++;
}
#endif
/* Wait until ready */
while ((dzcn->ChannelStatus & USI_TXRDY) == 0)
if (--timeout < 0)
break;
/* Put the character */
dzcn->TxData = ch;
splx(s);
}
/* Called before/after going into poll mode
*/
void
dz_ebus_cnpollc(dev_t dev, int on)
{
}