1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-22 11:17:19 +00:00
freebsd/sys/i386/isa/cronyx.c
Bruce Evans dd1df1935f Moved some definitions of initialized data nearer to the start of
the file so that this compiles without forward declarations of that
data.  (It is impossible to forward-declare static data in Gnu C.
Declaring it as static is correct, but causes bogus warnings from
gcc -Wredundant-decls.  Declaring it as extern works, but causes
correct warnings from gcc -pedantic and is undefined in ANSI C.
We usually declare it as extern.  Here it was once really extern,
but botched staticization left it as static here and apparently-
extern in a header file.)
----------------------------------------------------------------------
another system, such as NetBSD, CVS:   then name the system in this
line, otherwise delete it.  CVS: Reviewed by:  CVS:   Before
committing changes please have someone check your work and CVS:
include their name here. If the change is trivial and you have not
else; i.e., CVS:   they sent us a patch or a new module, then
include their name/email CVS:   address here. If this is your work
then delete this line.  CVS:
----------------------------------------------------------------------
----------------------------------------------------------------------
1997-07-20 10:07:55 +00:00

1030 lines
26 KiB
C

/*
* Low-level subroutines for Cronyx-Sigma adapter.
*
* Copyright (C) 1994-95 Cronyx Ltd.
* Author: Serge Vakulenko, <vak@cronyx.ru>
*
* This software is distributed with NO WARRANTIES, not even the implied
* warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Authors grant any other persons or organisations permission to use
* or modify this software as long as this message is kept with the software,
* all derivative works or modified versions.
*
* Version 1.6, Wed May 31 16:03:20 MSD 1995
*/
#if defined (MSDOS) || defined (__MSDOS__)
# include <string.h>
# include <dos.h>
# define inb(port) inportb(port)
# define inw(port) inport(port)
# define outb(port,b) outportb(port,b)
# define outw(port,w) outport(port,w)
# define vtophys(a) (((unsigned long)(a)>>12 & 0xffff0) +\
((unsigned)(a) & 0xffff))
# include "cronyx.h"
# include "cxreg.h"
#else
# include <sys/param.h>
# include <sys/systm.h>
# include <sys/socket.h>
# include <net/if.h>
# include <vm/vm.h>
# include <vm/vm_param.h>
# include <vm/pmap.h>
# ifndef __FreeBSD__
# include <machine/inline.h>
# endif
# include <machine/cronyx.h>
# include <i386/isa/cxreg.h>
#endif
#define DMA_MASK 0xd4 /* DMA mask register */
#define DMA_MASK_CLEAR 0x04 /* DMA clear mask */
#define DMA_MODE 0xd6 /* DMA mode register */
#define DMA_MODE_MASTER 0xc0 /* DMA master mode */
#define BYTE *(unsigned char*)&
static unsigned char irqmask [] = {
BCR0_IRQ_DIS, BCR0_IRQ_DIS, BCR0_IRQ_DIS, BCR0_IRQ_3,
BCR0_IRQ_DIS, BCR0_IRQ_5, BCR0_IRQ_DIS, BCR0_IRQ_7,
BCR0_IRQ_DIS, BCR0_IRQ_DIS, BCR0_IRQ_10, BCR0_IRQ_11,
BCR0_IRQ_12, BCR0_IRQ_DIS, BCR0_IRQ_DIS, BCR0_IRQ_15,
};
static unsigned char dmamask [] = {
BCR0_DMA_DIS, BCR0_DMA_DIS, BCR0_DMA_DIS, BCR0_DMA_DIS,
BCR0_DMA_DIS, BCR0_DMA_5, BCR0_DMA_6, BCR0_DMA_7,
};
static long cx_rxbaud = CX_SPEED_DFLT; /* receiver baud rate */
static long cx_txbaud = CX_SPEED_DFLT; /* transmitter baud rate */
static int cx_univ_mode = M_ASYNC; /* univ. chan. mode: async or sync */
static int cx_sync_mode = M_HDLC; /* sync. chan. mode: HDLC, Bisync or X.21 */
static int cx_iftype = 0; /* univ. chan. interface: upper/lower */
static cx_chan_opt_t chan_opt_dflt = { /* mode-independent options */
{ /* cor4 */
7, /* FIFO threshold, odd is better */
0,
0, /* don't detect 1 to 0 on CTS */
1, /* detect 1 to 0 on CD */
0, /* detect 1 to 0 on DSR */
},
{ /* cor5 */
0, /* receive flow control FIFO threshold */
0,
0, /* don't detect 0 to 1 on CTS */
1, /* detect 0 to 1 on CD */
0, /* detect 0 to 1 on DSR */
},
{ /* rcor */
0, /* dummy clock source */
ENCOD_NRZ, /* NRZ mode */
0, /* disable DPLL */
0,
0, /* transmit line value */
},
{ /* tcor */
0,
0, /* local loopback mode */
0,
1, /* external 1x clock mode */
0,
0, /* dummy transmit clock source */
},
};
static cx_opt_async_t opt_async_dflt = { /* default async options */
{ /* cor1 */
8-1, /* 8-bit char length */
0, /* don't ignore parity */
PARM_NOPAR, /* no parity */
PAR_EVEN, /* even parity */
},
{ /* cor2 */
0, /* disable automatic DSR */
1, /* enable automatic CTS */
0, /* disable automatic RTS */
0, /* no remote loopback */
0,
0, /* disable embedded cmds */
0, /* disable XON/XOFF */
0, /* disable XANY */
},
{ /* cor3 */
STOPB_1, /* 1 stop bit */
0,
0, /* disable special char detection */
FLOWCC_PASS, /* pass flow ctl chars to the host */
0, /* range detect disable */
0, /* disable extended spec. char detect */
},
{ /* cor6 */
PERR_INTR, /* generate exception on parity errors */
BRK_INTR, /* generate exception on break condition */
0, /* don't translate NL to CR on input */
0, /* don't translate CR to NL on input */
0, /* don't discard CR on input */
},
{ /* cor7 */
0, /* don't translate CR to NL on output */
0, /* don't translate NL to CR on output */
0,
0, /* don't process flow ctl err chars */
0, /* disable LNext option */
0, /* don't strip 8 bit on input */
},
0, 0, 0, 0, 0, 0, 0, /* clear schr1-4, scrl, scrh, lnxt */
};
static cx_opt_hdlc_t opt_hdlc_dflt = { /* default hdlc options */
{ /* cor1 */
2, /* 2 inter-frame flags */
0, /* no-address mode */
CLRDET_DISABLE, /* disable clear detect */
AFLO_1OCT, /* 1-byte address field length */
},
{ /* cor2 */
0, /* disable automatic DSR */
0, /* disable automatic CTS */
0, /* disable automatic RTS */
0,
CRC_INVERT, /* use CRC V.41 */
0,
FCS_NOTPASS, /* don't pass received CRC to the host */
0,
},
{ /* cor3 */
0, /* 0 pad characters sent */
IDLE_FLAG, /* idle in flag */
0, /* enable FCS */
FCSP_ONES, /* FCS preset to all ones (V.41) */
SYNC_AA, /* use AAh as sync char */
0, /* disable pad characters */
},
0, 0, 0, 0, /* clear rfar1-4 */
POLY_V41, /* use V.41 CRC polynomial */
};
static cx_opt_bisync_t opt_bisync_dflt = { /* default bisync options */
{ /* cor1 */
8-1, /* 8-bit char length */
0, /* don't ignore parity */
PARM_NOPAR, /* no parity */
PAR_EVEN, /* even parity */
},
{ /* cor2 */
3-2, /* send three SYN chars */
CRC_DONT_INVERT,/* don't invert CRC (CRC-16) */
0, /* use ASCII, not EBCDIC */
0, /* disable bcc append */
BCC_CRC16, /* user CRC16, not LRC */
},
{ /* cor3 */
0, /* send 0 pad chars */
IDLE_FLAG, /* idle in SYN */
0, /* enable FCS */
FCSP_ZEROS, /* FCS preset to all zeros (CRC-16) */
PAD_AA, /* use AAh as pad char */
0, /* disable pad characters */
},
{ /* cor6 */
10, /* DLE - disable special termination char */
},
POLY_16, /* use CRC-16 polynomial */
};
static cx_opt_x21_t opt_x21_dflt = { /* default x21 options */
{ /* cor1 */
8-1, /* 8-bit char length */
0, /* don't ignore parity */
PARM_NOPAR, /* no parity */
PAR_EVEN, /* even parity */
},
{ /* cor2 */
0,
0, /* disable embedded transmitter cmds */
0,
},
{ /* cor3 */
0,
0, /* disable special character detect */
0, /* don't treat SYN as special condition */
0, /* disable steady state detect */
X21SYN_2, /* 2 SYN chars on receive are required */
},
{ /* cor6 */
16, /* SYN - standard SYN character */
},
0, 0, 0, /* clear schr1-3 */
};
static int cx_probe_chip (int base);
static void cx_setup_chip (cx_chip_t *c);
static void cx_init_board (cx_board_t *b, int num, int port, int irq, int dma,
int chain, int rev, int osc, int rev2, int osc2);
static void cx_reinit_board (cx_board_t *b);
/*
* Wait for CCR to clear.
*/
void cx_cmd (int base, int cmd)
{
unsigned short port = CCR(base);
unsigned short count;
/* Wait 10 msec for the previous command to complete. */
for (count=0; inb(port) && count<20000; ++count)
continue;
/* Issue the command. */
outb (port, cmd);
/* Wait 10 msec for the command to complete. */
for (count=0; inb(port) && count<20000; ++count)
continue;
}
/*
* Reset the chip.
*/
static int cx_reset (unsigned short port)
{
int count;
/* Wait up to 10 msec for revision code to appear after reset. */
for (count=0; count<20000; ++count)
if (inb(GFRCR(port)) != 0)
break;
cx_cmd (port, CCR_RSTALL);
/* Firmware revision code should clear imediately. */
/* Wait up to 10 msec for revision code to appear again. */
for (count=0; count<20000; ++count)
if (inb(GFRCR(port)) != 0)
return (1);
/* Reset failed. */
return (0);
}
/*
* Check if the CD2400 board is present at the given base port.
*/
static int cx_probe_chained_board (int port, int *c0, int *c1)
{
int rev, i;
/* Read and check the board revision code. */
rev = inb (BSR(port));
*c0 = *c1 = 0;
switch (rev & BSR_VAR_MASK) {
case CRONYX_100: *c0 = 1; break;
case CRONYX_400: *c1 = 1; break;
case CRONYX_500: *c0 = *c1 = 1; break;
case CRONYX_410: *c0 = 1; break;
case CRONYX_810: *c0 = *c1 = 1; break;
case CRONYX_410s: *c0 = 1; break;
case CRONYX_810s: *c0 = *c1 = 1; break;
case CRONYX_440: *c0 = 1; break;
case CRONYX_840: *c0 = *c1 = 1; break;
case CRONYX_401: *c0 = 1; break;
case CRONYX_801: *c0 = *c1 = 1; break;
case CRONYX_401s: *c0 = 1; break;
case CRONYX_801s: *c0 = *c1 = 1; break;
case CRONYX_404: *c0 = 1; break;
case CRONYX_703: *c0 = *c1 = 1; break;
default: return (0); /* invalid variant code */
}
switch (rev & BSR_OSC_MASK) {
case BSR_OSC_20: /* 20 MHz */
case BSR_OSC_18432: /* 18.432 MHz */
break;
default:
return (0); /* oscillator frequency does not match */
}
for (i=2; i<0x10; i+=2)
if ((inb (BSR(port)+i) & BSR_REV_MASK) != (rev & BSR_REV_MASK))
return (0); /* status changed? */
return (1);
}
/*
* Check if the CD2400 board is present at the given base port.
*/
int
cx_probe_board (int port)
{
int c0, c1, c2=0, c3=0, result;
if (! cx_probe_chained_board (port, &c0, &c1))
return (0); /* no board detected */
if (! (inb (BSR(port)) & BSR_NOCHAIN)) { /* chained board attached */
if (! cx_probe_chained_board (port + 0x10, &c2, &c3))
return (0); /* invalid chained board? */
if (! (inb (BSR(port+0x10)) & BSR_NOCHAIN))
return (0); /* invalid chained board flag? */
}
/* Turn off the reset bit. */
outb (BCR0(port), BCR0_NORESET);
if (c2 || c3)
outb (BCR0(port + 0x10), BCR0_NORESET);
result = 1;
if (c0 && ! cx_probe_chip (CS0(port)))
result = 0; /* no CD2400 chip here */
else if (c1 && ! cx_probe_chip (CS1(port)))
result = 0; /* no second CD2400 chip */
else if (c2 && ! cx_probe_chip (CS0(port + 0x10)))
result = 0; /* no CD2400 chip on the slave board */
else if (c3 && ! cx_probe_chip (CS1(port + 0x10)))
result = 0; /* no second CD2400 chip on the slave board */
/* Reset the controller. */
outb (BCR0(port), 0);
if (c2 || c3)
outb (BCR0(port + 0x10), 0);
/* Yes, we really have valid CD2400 board. */
return (result);
}
/*
* Check if the CD2400 chip is present at the given base port.
*/
static int cx_probe_chip (int base)
{
int rev, newrev, count;
/* Wait up to 10 msec for revision code to appear after reset. */
for (count=0; inb(GFRCR(base))==0; ++count)
if (count >= 20000)
return (0); /* reset failed */
/* Read and check the global firmware revision code. */
rev = inb (GFRCR(base));
if (rev<REVCL_MIN || rev>REVCL_MAX)
return (0); /* CD2400 revision does not match */
/* Reset the chip. */
if (! cx_reset (base))
return (0);
/* Read and check the new global firmware revision code. */
newrev = inb (GFRCR(base));
if (newrev != rev)
return (0); /* revision changed */
/* Yes, we really have CD2400 chip here. */
return (1);
}
/*
* Probe and initialize the board structure.
*/
void cx_init (cx_board_t *b, int num, int port, int irq, int dma)
{
int rev, chain, rev2;
rev = inb (BSR(port));
chain = !(rev & BSR_NOCHAIN);
rev2 = chain ? inb (BSR(port+0x10)) : 0;
cx_init_board (b, num, port, irq, dma, chain,
(rev & BSR_VAR_MASK), (rev & BSR_OSC_MASK),
(rev2 & BSR_VAR_MASK), (rev2 & BSR_OSC_MASK));
}
/*
* Initialize the board structure, given the type of the board.
*/
static void
cx_init_board (cx_board_t *b, int num, int port, int irq, int dma,
int chain, int rev, int osc, int rev2, int osc2)
{
cx_chan_t *c;
int i, c0, c1;
/* Initialize board structure. */
b->port = port;
b->num = num;
b->irq = irq;
b->dma = dma;
b->if0type = b->if8type = cx_iftype;
/* Set channels 0 and 8 mode, set DMA and IRQ. */
b->bcr0 = b->bcr0b = BCR0_NORESET | dmamask[b->dma] | irqmask[b->irq];
/* Clear DTR[0..3] and DTR[8..12]. */
b->bcr1 = b->bcr1b = 0;
/* Initialize chip structures. */
for (i=0; i<NCHIP; ++i) {
b->chip[i].num = i;
b->chip[i].board = b;
}
b->chip[0].port = CS0(port);
b->chip[1].port = CS1(port);
b->chip[2].port = CS0(port+0x10);
b->chip[3].port = CS1(port+0x10);
/*------------------ Master board -------------------*/
/* Read and check the board revision code. */
c0 = c1 = 0;
b->name[0] = 0;
switch (rev) {
case CRONYX_100: strcpy (b->name, "100"); c0 = 1; break;
case CRONYX_400: strcpy (b->name, "400"); c1 = 1; break;
case CRONYX_500: strcpy (b->name, "500"); c0 = c1 = 1; break;
case CRONYX_410: strcpy (b->name, "410"); c0 = 1; break;
case CRONYX_810: strcpy (b->name, "810"); c0 = c1 = 1; break;
case CRONYX_410s: strcpy (b->name, "410s"); c0 = 1; break;
case CRONYX_810s: strcpy (b->name, "810s"); c0 = c1 = 1; break;
case CRONYX_440: strcpy (b->name, "440"); c0 = 1; break;
case CRONYX_840: strcpy (b->name, "840"); c0 = c1 = 1; break;
case CRONYX_401: strcpy (b->name, "401"); c0 = 1; break;
case CRONYX_801: strcpy (b->name, "801"); c0 = c1 = 1; break;
case CRONYX_401s: strcpy (b->name, "401s"); c0 = 1; break;
case CRONYX_801s: strcpy (b->name, "801s"); c0 = c1 = 1; break;
case CRONYX_404: strcpy (b->name, "404"); c0 = 1; break;
case CRONYX_703: strcpy (b->name, "703"); c0 = c1 = 1; break;
}
switch (osc) {
default:
case BSR_OSC_20: /* 20 MHz */
b->chip[0].oscfreq = b->chip[1].oscfreq = 20000000L;
strcat (b->name, "a");
break;
case BSR_OSC_18432: /* 18.432 MHz */
b->chip[0].oscfreq = b->chip[1].oscfreq = 18432000L;
strcat (b->name, "b");
break;
}
if (! c0)
b->chip[0].port = 0;
if (! c1)
b->chip[1].port = 0;
/*------------------ Slave board -------------------*/
if (! chain) {
b->chip[2].oscfreq = b->chip[3].oscfreq = 0L;
b->chip[2].port = b->chip[3].port = 0;
} else {
/* Read and check the board revision code. */
c0 = c1 = 0;
strcat (b->name, "/");
switch (rev2) {
case CRONYX_100: strcat(b->name,"100"); c0=1; break;
case CRONYX_400: strcat(b->name,"400"); c1=1; break;
case CRONYX_500: strcat(b->name,"500"); c0=c1=1; break;
case CRONYX_410: strcat(b->name,"410"); c0=1; break;
case CRONYX_810: strcat(b->name,"810"); c0=c1=1; break;
case CRONYX_410s: strcat(b->name,"410s"); c0=1; break;
case CRONYX_810s: strcat(b->name,"810s"); c0=c1=1; break;
case CRONYX_440: strcat(b->name,"440"); c0=1; break;
case CRONYX_840: strcat(b->name,"840"); c0=c1=1; break;
case CRONYX_401: strcat(b->name,"401"); c0=1; break;
case CRONYX_801: strcat(b->name,"801"); c0=c1=1; break;
case CRONYX_401s: strcat(b->name,"401s"); c0=1; break;
case CRONYX_801s: strcat(b->name,"801s"); c0=c1=1; break;
case CRONYX_404: strcat(b->name,"404"); c0=1; break;
case CRONYX_703: strcat(b->name,"703"); c0=c1=1; break;
}
switch (osc2) {
default:
case BSR_OSC_20: /* 20 MHz */
b->chip[2].oscfreq = b->chip[3].oscfreq = 20000000L;
strcat (b->name, "a");
break;
case BSR_OSC_18432: /* 18.432 MHz */
b->chip[2].oscfreq = b->chip[3].oscfreq = 18432000L;
strcat (b->name, "b");
break;
}
if (! c0)
b->chip[2].port = 0;
if (! c1)
b->chip[3].port = 0;
}
/* Initialize channel structures. */
for (i=0; i<NCHAN; ++i) {
cx_chan_t *c = b->chan + i;
c->num = i;
c->board = b;
c->chip = b->chip + i*NCHIP/NCHAN;
c->stat = b->stat + i;
c->type = T_NONE;
}
/*------------------ Master board -------------------*/
switch (rev) {
case CRONYX_400:
break;
case CRONYX_100:
case CRONYX_500:
b->chan[0].type = T_UNIV_RS232;
break;
case CRONYX_410:
case CRONYX_810:
b->chan[0].type = T_UNIV_V35;
for (i=1; i<4; ++i)
b->chan[i].type = T_UNIV_RS232;
break;
case CRONYX_410s:
case CRONYX_810s:
b->chan[0].type = T_UNIV_V35;
for (i=1; i<4; ++i)
b->chan[i].type = T_SYNC_RS232;
break;
case CRONYX_440:
case CRONYX_840:
b->chan[0].type = T_UNIV_V35;
for (i=1; i<4; ++i)
b->chan[i].type = T_SYNC_V35;
break;
case CRONYX_401:
case CRONYX_801:
b->chan[0].type = T_UNIV_RS449;
for (i=1; i<4; ++i)
b->chan[i].type = T_UNIV_RS232;
break;
case CRONYX_401s:
case CRONYX_801s:
b->chan[0].type = T_UNIV_RS449;
for (i=1; i<4; ++i)
b->chan[i].type = T_SYNC_RS232;
break;
case CRONYX_404:
b->chan[0].type = T_UNIV_RS449;
for (i=1; i<4; ++i)
b->chan[i].type = T_SYNC_RS449;
break;
case CRONYX_703:
b->chan[0].type = T_UNIV_RS449;
for (i=1; i<3; ++i)
b->chan[i].type = T_SYNC_RS449;
break;
}
/* If the second controller is present,
* then we have 4..7 channels in async. mode */
if (b->chip[1].port)
for (i=4; i<8; ++i)
b->chan[i].type = T_UNIV_RS232;
/*------------------ Slave board -------------------*/
if (chain) {
switch (rev2) {
case CRONYX_400:
break;
case CRONYX_100:
case CRONYX_500:
b->chan[8].type = T_UNIV_RS232;
break;
case CRONYX_410:
case CRONYX_810:
b->chan[8].type = T_UNIV_V35;
for (i=9; i<12; ++i)
b->chan[i].type = T_UNIV_RS232;
break;
case CRONYX_410s:
case CRONYX_810s:
b->chan[8].type = T_UNIV_V35;
for (i=9; i<12; ++i)
b->chan[i].type = T_SYNC_RS232;
break;
case CRONYX_440:
case CRONYX_840:
b->chan[8].type = T_UNIV_V35;
for (i=9; i<12; ++i)
b->chan[i].type = T_SYNC_V35;
break;
case CRONYX_401:
case CRONYX_801:
b->chan[8].type = T_UNIV_RS449;
for (i=9; i<12; ++i)
b->chan[i].type = T_UNIV_RS232;
break;
case CRONYX_401s:
case CRONYX_801s:
b->chan[8].type = T_UNIV_RS449;
for (i=9; i<12; ++i)
b->chan[i].type = T_UNIV_RS232;
break;
case CRONYX_404:
b->chan[8].type = T_UNIV_RS449;
for (i=9; i<12; ++i)
b->chan[i].type = T_SYNC_RS449;
break;
case CRONYX_703:
b->chan[8].type = T_UNIV_RS449;
for (i=9; i<11; ++i)
b->chan[i].type = T_SYNC_RS449;
break;
}
/* If the second controller is present,
* then we have 4..7 channels in async. mode */
if (b->chip[3].port)
for (i=12; i<16; ++i)
b->chan[i].type = T_UNIV_RS232;
}
b->nuniv = b->nsync = b->nasync = 0;
for (c=b->chan; c<b->chan+NCHAN; ++c)
switch (c->type) {
case T_ASYNC: ++b->nasync; break;
case T_UNIV_RS232:
case T_UNIV_RS449:
case T_UNIV_V35: ++b->nuniv; break;
case T_SYNC_RS232:
case T_SYNC_V35:
case T_SYNC_RS449: ++b->nsync; break;
}
cx_reinit_board (b);
}
/*
* Reinitialize all channels, using new options and baud rate.
*/
static void
cx_reinit_board (cx_board_t *b)
{
cx_chan_t *c;
b->if0type = b->if8type = cx_iftype;
for (c=b->chan; c<b->chan+NCHAN; ++c) {
switch (c->type) {
default:
case T_NONE:
continue;
case T_UNIV_RS232:
case T_UNIV_RS449:
case T_UNIV_V35:
c->mode = (cx_univ_mode == M_ASYNC) ?
M_ASYNC : cx_sync_mode;
break;
case T_SYNC_RS232:
case T_SYNC_V35:
case T_SYNC_RS449:
c->mode = cx_sync_mode;
break;
case T_ASYNC:
c->mode = M_ASYNC;
break;
}
c->rxbaud = cx_rxbaud;
c->txbaud = cx_txbaud;
c->opt = chan_opt_dflt;
c->aopt = opt_async_dflt;
c->hopt = opt_hdlc_dflt;
c->bopt = opt_bisync_dflt;
c->xopt = opt_x21_dflt;
}
}
/*
* Set up the board.
*/
void cx_setup_board (cx_board_t *b)
{
int i;
/* Disable DMA channel. */
outb (DMA_MASK, (b->dma & 3) | DMA_MASK_CLEAR);
/* Reset the controller. */
outb (BCR0(b->port), 0);
if (b->chip[2].port || b->chip[3].port)
outb (BCR0(b->port+0x10), 0);
/*
* Set channels 0 and 8 to RS232 async. mode.
* Enable DMA and IRQ.
*/
outb (BCR0(b->port), b->bcr0);
if (b->chip[2].port || b->chip[3].port)
outb (BCR0(b->port+0x10), b->bcr0b);
/* Clear DTR[0..3] and DTR[8..12]. */
outw (BCR1(b->port), b->bcr1);
if (b->chip[2].port || b->chip[3].port)
outw (BCR1(b->port+0x10), b->bcr1b);
/* Initialize all controllers. */
for (i=0; i<NCHIP; ++i)
if (b->chip[i].port)
cx_setup_chip (b->chip + i);
/* Set up DMA channel to master mode. */
outb (DMA_MODE, (b->dma & 3) | DMA_MODE_MASTER);
/* Enable DMA channel. */
outb (DMA_MASK, b->dma & 3);
/* Initialize all channels. */
for (i=0; i<NCHAN; ++i)
if (b->chan[i].type != T_NONE)
cx_setup_chan (b->chan + i);
}
/*
* Initialize the board.
*/
static void cx_setup_chip (cx_chip_t *c)
{
/* Reset the chip. */
cx_reset (c->port);
/*
* Set all interrupt level registers to the same value.
* This enables the internal CD2400 priority scheme.
*/
outb (RPILR(c->port), BRD_INTR_LEVEL);
outb (TPILR(c->port), BRD_INTR_LEVEL);
outb (MPILR(c->port), BRD_INTR_LEVEL);
/* Set bus error count to zero. */
outb (BERCNT(c->port), 0);
/* Set 16-bit DMA mode. */
outb (DMR(c->port), 0);
/* Set timer period register to 1 msec (approximately). */
outb (TPR(c->port), 10);
}
/*
* Initialize the CD2400 channel.
*/
void cx_setup_chan (cx_chan_t *c)
{
unsigned short port = c->chip->port;
int clock, period;
if (c->num == 0) {
c->board->bcr0 &= ~BCR0_UMASK;
if (c->mode != M_ASYNC)
c->board->bcr0 |= BCR0_UM_SYNC;
if (c->board->if0type &&
(c->type==T_UNIV_RS449 || c->type==T_UNIV_V35))
c->board->bcr0 |= BCR0_UI_RS449;
outb (BCR0(c->board->port), c->board->bcr0);
} else if (c->num == 8) {
c->board->bcr0b &= ~BCR0_UMASK;
if (c->mode != M_ASYNC)
c->board->bcr0b |= BCR0_UM_SYNC;
if (c->board->if8type &&
(c->type==T_UNIV_RS449 || c->type==T_UNIV_V35))
c->board->bcr0b |= BCR0_UI_RS449;
outb (BCR0(c->board->port+0x10), c->board->bcr0b);
}
/* set current channel number */
outb (CAR(port), c->num & 3);
/* reset the channel */
cx_cmd (port, CCR_CLRCH);
/* set LIVR to contain the board and channel numbers */
outb (LIVR(port), c->board->num << 6 | c->num << 2);
/* clear DTR, RTS, set TXCout/DTR pin */
outb (MSVR_RTS(port), 0);
outb (MSVR_DTR(port), c->mode==M_ASYNC ? 0 : MSV_TXCOUT);
switch (c->mode) { /* initialize the channel mode */
case M_ASYNC:
/* set receiver timeout register */
outw (RTPR(port), 10); /* 10 msec, see TPR */
outb (CMR(port), CMR_RXDMA | CMR_TXDMA | CMR_ASYNC);
outb (COR1(port), BYTE c->aopt.cor1);
outb (COR2(port), BYTE c->aopt.cor2);
outb (COR3(port), BYTE c->aopt.cor3);
outb (COR6(port), BYTE c->aopt.cor6);
outb (COR7(port), BYTE c->aopt.cor7);
outb (SCHR1(port), c->aopt.schr1);
outb (SCHR2(port), c->aopt.schr2);
outb (SCHR3(port), c->aopt.schr3);
outb (SCHR4(port), c->aopt.schr4);
outb (SCRL(port), c->aopt.scrl);
outb (SCRH(port), c->aopt.scrh);
outb (LNXT(port), c->aopt.lnxt);
break;
case M_HDLC:
outb (CMR(port), CMR_RXDMA | CMR_TXDMA | CMR_HDLC);
outb (COR1(port), BYTE c->hopt.cor1);
outb (COR2(port), BYTE c->hopt.cor2);
outb (COR3(port), BYTE c->hopt.cor3);
outb (RFAR1(port), c->hopt.rfar1);
outb (RFAR2(port), c->hopt.rfar2);
outb (RFAR3(port), c->hopt.rfar3);
outb (RFAR4(port), c->hopt.rfar4);
outb (CPSR(port), c->hopt.cpsr);
break;
case M_BISYNC:
outb (CMR(port), CMR_RXDMA | CMR_TXDMA | CMR_BISYNC);
outb (COR1(port), BYTE c->bopt.cor1);
outb (COR2(port), BYTE c->bopt.cor2);
outb (COR3(port), BYTE c->bopt.cor3);
outb (COR6(port), BYTE c->bopt.cor6);
outb (CPSR(port), c->bopt.cpsr);
break;
case M_X21:
outb (CMR(port), CMR_RXDMA | CMR_TXDMA | CMR_X21);
outb (COR1(port), BYTE c->xopt.cor1);
outb (COR2(port), BYTE c->xopt.cor2);
outb (COR3(port), BYTE c->xopt.cor3);
outb (COR6(port), BYTE c->xopt.cor6);
outb (SCHR1(port), c->xopt.schr1);
outb (SCHR2(port), c->xopt.schr2);
outb (SCHR3(port), c->xopt.schr3);
break;
}
/* set mode-independent options */
outb (COR4(port), BYTE c->opt.cor4);
outb (COR5(port), BYTE c->opt.cor5);
/* set up receiver clock values */
if (c->mode == M_ASYNC || c->opt.rcor.dpll) {
cx_clock (c->chip->oscfreq, c->rxbaud, &clock, &period);
c->opt.rcor.clk = clock;
} else {
c->opt.rcor.clk = CLK_EXT;
period = 1;
}
outb (RCOR(port), BYTE c->opt.rcor);
outb (RBPR(port), period);
/* set up transmitter clock values */
if (c->mode == M_ASYNC || !c->opt.tcor.ext1x) {
unsigned ext1x = c->opt.tcor.ext1x;
c->opt.tcor.ext1x = 0;
cx_clock (c->chip->oscfreq, c->txbaud, &clock, &period);
c->opt.tcor.clk = clock;
c->opt.tcor.ext1x = ext1x;
} else {
c->opt.tcor.clk = CLK_EXT;
period = 1;
}
outb (TCOR(port), BYTE c->opt.tcor);
outb (TBPR(port), period);
/* set receiver A buffer physical address */
c->arphys = vtophys (c->arbuf);
outw (ARBADRU(port), (unsigned short) (c->arphys>>16));
outw (ARBADRL(port), (unsigned short) c->arphys);
/* set receiver B buffer physical address */
c->brphys = vtophys (c->brbuf);
outw (BRBADRU(port), (unsigned short) (c->brphys>>16));
outw (BRBADRL(port), (unsigned short) c->brphys);
/* set transmitter A buffer physical address */
c->atphys = vtophys (c->atbuf);
outw (ATBADRU(port), (unsigned short) (c->atphys>>16));
outw (ATBADRL(port), (unsigned short) c->atphys);
/* set transmitter B buffer physical address */
c->btphys = vtophys (c->btbuf);
outw (BTBADRU(port), (unsigned short) (c->btphys>>16));
outw (BTBADRL(port), (unsigned short) c->btphys);
c->dtr = 0;
c->rts = 0;
}
/*
* Control DTR signal for the channel.
* Turn it on/off.
*/
void cx_chan_dtr (cx_chan_t *c, int on)
{
c->dtr = on ? 1 : 0;
if (c->mode == M_ASYNC) {
outb (CAR(c->chip->port), c->num & 3);
outb (MSVR_DTR(c->chip->port), on ? MSV_DTR : 0);
return;
}
switch (c->num) {
default:
/* Channels 4..7 and 12..15 in syncronous mode
* have no DTR signal. */
break;
case 1: case 2: case 3:
if (c->type == T_UNIV_RS232)
break;
case 0:
if (on)
c->board->bcr1 |= 0x100 << c->num;
else
c->board->bcr1 &= ~(0x100 << c->num);
outw (BCR1(c->board->port), c->board->bcr1);
break;
case 9: case 10: case 11:
if (c->type == T_UNIV_RS232)
break;
case 8:
if (on)
c->board->bcr1b |= 0x100 << (c->num & 3);
else
c->board->bcr1b &= ~(0x100 << (c->num & 3));
outw (BCR1(c->board->port+0x10), c->board->bcr1b);
break;
}
}
/*
* Control RTS signal for the channel.
* Turn it on/off.
*/
void
cx_chan_rts (cx_chan_t *c, int on)
{
c->rts = on ? 1 : 0;
outb (CAR(c->chip->port), c->num & 3);
outb (MSVR_RTS(c->chip->port), on ? MSV_RTS : 0);
}
/*
* Get the state of CARRIER signal of the channel.
*/
int
cx_chan_cd (cx_chan_t *c)
{
unsigned char sigval;
if (c->mode == M_ASYNC) {
outb (CAR(c->chip->port), c->num & 3);
return (inb (MSVR(c->chip->port)) & MSV_CD ? 1 : 0);
}
/*
* Channels 4..7 and 12..15 don't have CD signal available.
*/
switch (c->num) {
default:
return (1);
case 1: case 2: case 3:
if (c->type == T_UNIV_RS232)
return (1);
case 0:
sigval = inw (BSR(c->board->port)) >> 8;
break;
case 9: case 10: case 11:
if (c->type == T_UNIV_RS232)
return (1);
case 8:
sigval = inw (BSR(c->board->port+0x10)) >> 8;
break;
}
return (~sigval >> 4 >> (c->num & 3) & 1);
}
/*
* Compute CD2400 clock values.
*/
void cx_clock (long hz, long ba, int *clk, int *div)
{
static short clocktab[] = { 8, 32, 128, 512, 2048, 0 };
for (*clk=0; clocktab[*clk]; ++*clk) {
long c = ba * clocktab[*clk];
if (hz <= c*256) {
*div = (2 * hz + c) / (2 * c) - 1;
return;
}
}
/* Incorrect baud rate. Return some meaningful values. */
*clk = 0;
*div = 255;
}