#ifndef lint
static char sccsid[] = "@(#)mcp.c 1.1 92/07/30 SMI";
#endif
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
/*
* Sun MCP Multiprotocol Communication Processor
* Sun ALM-2 Asynchronous Line Multiplexer
*
* Common code for all MCP/ALM-2 modules, including boot-time autoconf,
* interrupt, and on-board DMA routines.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/termios.h>
#include <sys/buf.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/tty.h>
#include <sys/clist.h>
#include <sundev/mbvar.h>
#include <sundev/zsreg.h>
#include <sundev/mcpreg.h>
#include <sundev/mcpcom.h>
#include <sundev/mcpcmd.h>
#define DMADONE(x) ((x)->d_chip->d_ioaddr->csr)
int mcpdebug = 0;
#define printD if(mcpdebug)(void)printf
extern struct mcpcom mcpcom[];
extern struct mb_driver mcpdriver;
extern struct mcpops *mcp_proto[];
extern struct dma_chip dma_chip[];
extern struct mb_device *mcpinfo[];
extern int nmcpline;
extern int mcpsoftCAR[];
extern int mcpr_attach( /* struct mb_device *md; */);
int mcpprobe( /* caddr_t reg, int unit */ );
int mcpattach( /* struct mb_device *md */ );
static void cioattach(/* struct ciochip *cp, struct mcp_device *mcpaddr */);
int mcpintr( /* int unit */ );
static void mcp_dmaattach( /* struct mcp_device *mcp, int unit */ );
static void mcp_dmainit(/* struct dma_chip *dc, struct dma_device *base, char flag */ );
struct dma_chan *mcp_dmagetchan( /* int port, dir, unit, scc */ );
void mcp_dmastart(/* struct dma_chan *dma, char *addr, short len */);
u_short mcp_getwc( /* struct dma_chan *dma */ );
u_short mcp_dmahalt( /* struct dma_chan *dma */ );
int
mcpprobe(reg, unit)
caddr_t reg;
int unit;
{
register struct mcp_device *mcpaddr = (struct mcp_device *) reg;
register struct mcpcom *zs = &mcpcom[unit * 16];
register int i;
printD(" in mcpprobe, reg %x, unit %x \n", reg, unit);
if (peek((short *) &mcpaddr->reset_bid) == -1) {
printD("probe failed \n");
return (0);
}
/*
* Reset the board. Note that after reset, the board interrupts are
* diabled. Pick them up in mcpattach().
*/
if (poke((short *) &mcpaddr->reset_bid, 0) == -1)
return (0);
printD("devctl = %x\n", mcpaddr->devctl[1].ctr);
for (i = 0; i < 16; i++, zs++) {
zs->zs_unit = i;
zs->mc_unit = unit;
}
mcpcom[nmcpline + unit].mc_unit = unit; /* zs_unit doesn't matter to
* printer */
return (sizeof(struct mcp_device));
}
int
mcpattach(md)
register struct mb_device *md;
{
register struct mcpcom *zs = &mcpcom[md->md_unit * 16];
register struct mcp_device *mcpaddr = (struct mcp_device *) md->md_addr;
register struct mcpops *zso;
struct ciochip *cp = &mcpaddr->cio;
register int j, port;
register u_char vector;
mcpaddr->ivec = md->md_intr->v_vec;
*(md->md_intr->v_vptr) = (int) (md->md_unit * 16);
cioattach(cp, mcpaddr);
mcp_dmaattach(mcpaddr, md->md_unit);
mcpsoftCAR[md->md_unit] = md->md_flags;
/*
* initialize each port of MCP
* includes:
* disable xoff, clear one character buffer, set up links between
* data structures, and initialize dma channel and enable master
* interrupt and interrupt vector for SCC
* vector bits assignment:
* bit 0 -- select CIO or SCC (0 == SCC)
* bit 4 to 7 -- select SCC chip (0 to 8)
*/
for (port = 0, vector = 0; port < 16; port++, zs++) {
zs->mcp_addr = mcpaddr;
zs->zs_addr = &mcpaddr->sccs[port];
zs->zs_flags = 0;
zs->zs_rerror = 0;
mcpaddr->xbuf[port].xoff = DISABLE_XOFF;
if (port & 0x01) {
SCC_WRITE(&zs->mcp_addr->sccs[port], 9,
ZSWR9_MASTER_IE | ZSWR9_VECTOR_INCL_STAT);
SCC_WRITE(&mcpaddr->sccs[port], 2, vector);
vector += 0x10;
}
zs->mcp_txdma = mcp_dmagetchan(zs->zs_unit, TX_DIR,
md->md_unit, SCC_DMA);
zs->mcp_rxdma = mcp_dmagetchan(zs->zs_unit, RX_DIR,
md->md_unit, SCC_DMA);
/* attach driver of individual protocol */
for (j = 0; mcp_proto[j]; j++) {
zso = mcp_proto[j];
(*zso->mcpop_attach) (zs);
}
}
/* Initialize printer port */
(void) mcpr_attach(md);
/* enable master interrupt for MCP */
cp->portc_data = (cp->portc_data & 0xf) | MCP_IE;
}
static void
cioattach(cp, mcpaddr)
register struct ciochip *cp;
register struct mcp_device *mcpaddr;
{
register u_char select;
/* write a single zero to cio to clear the reset */
cp->cio_ctr = 0;
/* initialize cio port C. While here, detect RS232/449 interface */
CIO_WRITE(cp, CIO_MICR, MASTER_INT_ENABLE);
CIO_WRITE(cp, CIO_PC_DPPR, MCPRVMEINT | MCPRDIAG);
CIO_WRITE(cp, CIO_PC_DDR, PORT0_RS232_SEL | PORT1_RS232_SEL);
CIO_WRITE(cp, CIO_PC_SIOCR, ONES_CATCHER);
if (((select = cp->portc_data) & PORT0_RS232_SEL) == 0) {
(void) printf("***port 0 supports RS449 interface***\n");
mcpaddr->devctl[0].ctr &= EN_RS449_TX;
}
if ((select & PORT1_RS232_SEL) == 0) {
(void) printf("***port 1 supports RS449 interface***\n");
mcpaddr->devctl[1].ctr &= EN_RS449_TX;
}
cp->portc_data = (select & 0xf) & ~MCPRDIAG; /* Ensure diag off */
/* initialize port A of cio chip */
CIO_WRITE(cp, CIO_PA_MODE, BIT_PORT_MODE);
CIO_WRITE(cp, CIO_PA_DDR, ALL_INPUT);
CIO_WRITE(cp, CIO_PA_DPPR, FIFO_NON_INVERT);
CIO_WRITE(cp, CIO_PA_PP, FIFO_NOT_ONE);
CIO_WRITE(cp, CIO_PA_PM, FIFO_EMPTY_INTR_ONLY);
CIO_WRITE(cp, CIO_PA_CSR, PORT_INT_ENABLE);
/* initialize port B of cio chip */
CIO_WRITE(cp, CIO_PB_MODE, BIT_PORT_MODE);
CIO_WRITE(cp, CIO_PB_DDR, ALL_INPUT);
CIO_WRITE(cp, CIO_PB_SIOCR, EOP_ONE);
CIO_WRITE(cp, CIO_PB_DPPR, EOP_INVERT | MCPRSLCT | MCPRPE);
CIO_WRITE(cp, CIO_PB_PP, EOP_ONE);
CIO_WRITE(cp, CIO_PB_PM, EOP_ONE);
CIO_WRITE(cp, CIO_PB_CSR, PORT_INT_ENABLE);
CIO_WRITE(cp, CIO_MCCR, MASTER_ENABLE);
/*
* set vector for CIO
* vector bits assignment:
* bit 0 -- select CIO or SCC ( 1 == CIO)
* bit 4 -- select port A or B ( 0 == port A)
*/
CIO_WRITE(cp, CIO_PA_IVR, 0x01);
CIO_WRITE(cp, CIO_PB_IVR, 0x11);
}
/*
* Handle a level 4 interrupt
* unit is a multiple of 16
* i.e. unit=48 for mcp3
*/
int
mcpintr(unit)
register int unit; /* which mcp board */
{
register struct mcpcom *zs = &mcpcom[unit];
register struct mcp_device *mcp = zs->mcp_addr;
register struct ciochip *cio = &mcp->cio;
register int i;
register u_char dvector;
register u_char status;
register u_char mask;
register u_short data;
int loops = -1;
int bd = unit >> 4;
int ln = unit & 15;
do {
++loops;
/*
* If the vector corresponds to an action that requires
* us to empty the fifo first, and there is data in
* the FIFO, drain the data up to the next layer.
*/
dvector = mcp->devvector.ctr;
if ((dvector == MCP_NOINTR) ||
(dvector == CIO_PAD4_FIFO_E) ||
((dvector & 0x87) == SCC0_XSINT) || /* any XSINT */
((dvector & 0x87) == SCC0_SRINT) || /* any SRINT */
(dvector == CIO_PAD5_FIFO_HF))
while ((data = mcp->fifo[0]) != FIFO_EMPTY) {
++loops;
ln = data >> 8;
if (ln > 15) {
printf("mcp%d: unexpected data 0x%x from fifo (dvector=%x); probable hardware fault.\n",
bd, data, dvector);
continue;
}
zs = &mcpcom[unit + ln];
if (zs->mcp_ops) (*zs->mcp_ops->mcpop_rxchar) (zs, data & 0xff);
}
/*
* Process the interrupt vector.
*/
ln = (dvector >> 3) & 15; /* most common line encoding */
switch (dvector) {
case CIO_PBD0_TXEND:
case CIO_PBD1_TXEND:
case CIO_PBD2_TXEND:
case CIO_PBD3_TXEND:
zs = &mcpcom[unit + CIO_MCPBASE(dvector)];
/* reset & clear the source of interrupt */
CIO_DMARESET(cio, dvector);
CIO_WRITE(cio, CIO_PB_CSR, CIO_CLRIP);
status = DMADONE(zs->mcp_txdma);
for (i = 0; i < 4; zs++, i++) {
if (zs->zs_flags & MCP_WAIT_DMA) {
mask = 1 << zs->mcp_txdma->d_chan;
if (status & mask) {
zs->mcp_txdma->d_chip->d_mask &= ~mask;
zs->zs_flags &= ~MCP_WAIT_DMA;
if (zs->mcp_ops) (*zs->mcp_ops->mcpop_txend) (zs);
}
}
}
break;
case SCC0_XSINT:
case SCC1_XSINT:
case SCC2_XSINT:
case SCC3_XSINT:
case SCC4