/*
* cy.c
*
* Driver for Cyclades Cyclom-8/16/32 multiport serial cards
* (currently not tested with Cyclom-32 cards)
*
* Timo Rossi, 1996
*
* Supports both ISA and PCI Cyclom cards
*
* Lots of debug output can be enabled by defining CY_DEBUG
* Some debugging counters (number of receive/transmit interrupts etc.)
* can be enabled by defining CY_DEBUG1
*/
/* wait until the chip is ready for command */
DELAY(1000);
if (bus_space_read_1(tag, bsh, chip +
((CD1400_CCR << 1) << bustype)) != 0) {
#ifdef CY_DEBUG
printf("not ready for command\n");
#endif
break;
}
/* clear the firmware version reg. */
bus_space_write_1(tag, bsh, chip +
((CD1400_GFRCR << 1) << bustype), 0);
/*
* On Cyclom-16 references to non-existent chip 4
* actually access chip 0 (address line 9 not decoded).
* Here we check if the clearing of chip 4 GFRCR actually
* cleared chip 0 GFRCR. In that case we have a 16 port card.
*/
if (cy_chip == 4 &&
bus_space_read_1(tag, bsh, /* off for chip 0 (0) + */
((CD1400_GFRCR << 1) << bustype)) == 0)
break;
/* wait for the chip to initialize itself */
for (i = 0; i < 200; i++) {
DELAY(50);
firmware_ver = bus_space_read_1(tag, bsh, chip +
((CD1400_GFRCR << 1) << bustype));
if ((firmware_ver & 0xf0) == 0x40) /* found a CD1400 */
break;
}
#ifdef CY_DEBUG
printf("firmware version 0x%x\n", firmware_ver);
#endif
if ((firmware_ver & 0xf0) != 0x40)
break;
/* firmware version OK, CD1400 found */
sc->sc_nchips++;
}
static struct cy_port *
cy_getport(dev_t dev)
{
int i, j, k, u = CY_UNIT(dev);
struct cy_softc *sc;
for (i = 0, j = 0; i < cy_cd.cd_ndevs; i++) {
k = j;
sc = device_lookup_private(&cy_cd, i);
if (sc == NULL)
continue;
if (sc->sc_nchannels == 0)
continue;
j += sc->sc_nchannels;
if (j > u)
return (&sc->sc_ports[u - k]);
}
return (NULL);
}
/*
* open routine. returns zero if successful, else error code
*/
int
cyopen(dev_t dev, int flag, int mode, struct lwp *l)
{
struct cy_softc *sc;
struct cy_port *cy;
struct tty *tp;
int s, error;
/*
* Allocate input ring buffer if we don't already have one
*/
if (cy->cy_ibuf == NULL) {
cy->cy_ibuf = malloc(CY_IBUF_SIZE, M_DEVBUF, M_WAITOK);
cy->cy_ibuf_end = cy->cy_ibuf + CY_IBUF_SIZE;
}
/* mark the ring buffer as empty */
cy->cy_ibuf_rd_ptr = cy->cy_ibuf_wr_ptr = cy->cy_ibuf;
/* select CD1400 channel */
cd_write_reg(sc, cy->cy_chip, CD1400_CAR,
cy->cy_port_num & CD1400_CAR_CHAN);
/* reset the channel */
cd1400_channel_cmd(sc, cy, CD1400_CCR_CMDRESET);
/* encode unit (port) number in LIVR */
/* there is just enough space for 5 bits (32 ports) */
cd_write_reg(sc, cy->cy_chip, CD1400_LIVR,
cy->cy_port_num << 3);
cy->cy_channel_control = 0;
/* hmm... need spltty() here? */
if (cy_open == 0) {
cy_open = 1;
callout_reset(&cy_poll_callout, 1, cy_poll, NULL);
}
/* this sets parameters and raises DTR */
cyparam(tp, &tp->t_termios);
ttsetwater(tp);
/* raise RTS too */
cy_modem_control(sc, cy, TIOCM_RTS, DMBIS);
/* wait for carrier if necessary */
if (!ISSET(flag, O_NONBLOCK)) {
ttylock(tp);
while (!ISSET(tp->t_cflag, CLOCAL) &&
!ISSET(tp->t_state, TS_CARR_ON)) {
tp->t_wopen++;
error = ttysleep(tp, &tp->t_rawcv, true, 0);
tp->t_wopen--;
if (error != 0) {
ttyunlock(tp);
return error;
}
}
ttyunlock(tp);
}
return (*tp->t_linesw->l_open) (dev, tp);
}
/*
* close routine. returns zero if successful, else error code
*/
int
cyclose(dev_t dev, int flag, int mode, struct lwp *l)
{
struct cy_softc *sc;
struct cy_port *cy;
struct tty *tp;
int s;
(*tp->t_linesw->l_close) (tp, flag);
s = spltty();
if (ISSET(tp->t_cflag, HUPCL) &&
!ISSET(cy->cy_openflags, TIOCFLAG_SOFTCAR)) {
/*
* drop DTR and RTS (should we wait for output buffer to
* become empty first?)
*/
cy_modem_control(sc, cy, 0, DMSET);
}
/*
* XXX should we disable modem change and
* receive interrupts here or somewhere ?
*/
CLR(tp->t_state, TS_BUSY | TS_FLUSH);
splx(s);
ttyclose(tp);
return 0;
}
/*
* Read routine
*/
int
cyread(dev_t dev, struct uio *uio, int flag)
{
struct cy_port *cy;
struct tty *tp;
/*
* use the CD1400 automatic CTS flow control if CRTSCTS is set
*
* CD1400_COR2_ETC is used because breaks are generated with
* embedded transmit commands
*/
cd_write_reg(sc, cy->cy_chip, CD1400_COR2,
CD1400_COR2_ETC |
(ISSET(t->c_cflag, CRTSCTS) ? CD1400_COR2_CCTS_OFLOW : 0));
/*
* set receive timeout to approx. 2ms
* could use more complex logic here...
* (but is it actually needed or even useful?)
*/
cd_write_reg(sc, cy->cy_chip, CD1400_RTPR, 2);
/*
* should do anything else here?
* XXX check MDMBUF handshaking like in com.c?
*/
splx(s);
return 0;
}
/*
* set/get modem line status
*
* bits can be: TIOCM_DTR, TIOCM_RTS, TIOCM_CTS, TIOCM_CD, TIOCM_RI, TIOCM_DSR
*/
int
cy_modem_control(struct cy_softc *sc, struct cy_port *cy, int bits, int howto)
{
struct tty *tp = cy->cy_tty;
int s, msvr;
/* Does not manipulate RTS if it is used for flow control. */
switch (howto) {
case DMGET:
bits = 0;
if (cy->cy_channel_control & CD1400_CCR_RCVEN)
bits |= TIOCM_LE;
msvr = cd_read_reg(sc, cy->cy_chip, CD1400_MSVR2);
if (cy->cy_clock == CY_CLOCK_60) {
if (cd_read_reg(sc, cy->cy_chip, CD1400_MSVR1) &
CD1400_MSVR1_RTS)
bits |= TIOCM_DTR;
if (msvr & CD1400_MSVR2_DTR)
bits |= TIOCM_RTS;
} else {
if (cd_read_reg(sc, cy->cy_chip, CD1400_MSVR1) &
CD1400_MSVR1_RTS)
bits |= TIOCM_RTS;
if (msvr & CD1400_MSVR2_DTR)
bits |= TIOCM_DTR;
}
if (msvr & CD1400_MSVR2_CTS)
bits |= TIOCM_CTS;
if (msvr & CD1400_MSVR2_CD)
bits |= TIOCM_CD;
/* Not connected on some Cyclom-Y boards? */
if (msvr & CD1400_MSVR2_DSR)
bits |= TIOCM_DSR;
/* Not connected on some Cyclom-8Y boards? */
if (msvr & CD1400_MSVR2_RI)
bits |= TIOCM_RI;
break;
case DMSET: /* replace old values with new ones */
if (cy->cy_clock == CY_CLOCK_60) {
if (!ISSET(tp->t_cflag, CRTSCTS))
cd_write_reg(sc, cy->cy_chip, CD1400_MSVR2,
((bits & TIOCM_RTS) ? CD1400_MSVR2_DTR : 0));
cd_write_reg(sc, cy->cy_chip, CD1400_MSVR1,
((bits & TIOCM_DTR) ? CD1400_MSVR1_RTS : 0));
} else {
if (!ISSET(tp->t_cflag, CRTSCTS))
cd_write_reg(sc, cy->cy_chip, CD1400_MSVR1,
((bits & TIOCM_RTS) ? CD1400_MSVR1_RTS : 0));
cd_write_reg(sc, cy->cy_chip, CD1400_MSVR2,
((bits & TIOCM_DTR) ? CD1400_MSVR2_DTR : 0));
}
break;
case DMBIS: /* set bits */
if (cy->cy_clock == CY_CLOCK_60) {
if (!ISSET(tp->t_cflag, CRTSCTS) &&
(bits & TIOCM_RTS) != 0)
cd_write_reg(sc, cy->cy_chip, CD1400_MSVR2,
CD1400_MSVR2_DTR);
if (bits & TIOCM_DTR)
cd_write_reg(sc, cy->cy_chip, CD1400_MSVR1,
CD1400_MSVR1_RTS);
} else {
if (!ISSET(tp->t_cflag, CRTSCTS) &&
(bits & TIOCM_RTS) != 0)
cd_write_reg(sc, cy->cy_chip, CD1400_MSVR1,
CD1400_MSVR1_RTS);
if (bits & TIOCM_DTR)
cd_write_reg(sc, cy->cy_chip, CD1400_MSVR2,
CD1400_MSVR2_DTR);
}
break;
/*
* Upper-level handler loop (called from timer interrupt?)
* This routine is common for multiple cards
*/
void
cy_poll(void *arg)
{
int card, port;
struct cy_softc *sc;
struct cy_port *cy;
struct tty *tp;
static int counter = 0;
#ifdef CY_DEBUG1
int did_something;
#endif
int s = spltty();
s = spltty(); /* really necessary? */
if ((cy->cy_ibuf_rd_ptr += 2) ==
cy->cy_ibuf_end)
cy->cy_ibuf_rd_ptr = cy->cy_ibuf;
splx(s);
#ifdef CY_DEBUG1
did_something = 1;
#endif
}
/*
* If we don't have any received data in ibuf and
* CRTSCTS is on and RTS is turned off, it is time to
* turn RTS back on
*/
if (ISSET(tp->t_cflag, CRTSCTS)) {
/*
* we can't use cy_modem_control() here as it
* doesn't change RTS if RTSCTS is on
*/
cd_write_reg(sc, cy->cy_chip, CD1400_CAR,
port & CD1400_CAR_CHAN);
/*
* hardware interrupt routine
*/
int
cy_intr(void *arg)
{
struct cy_softc *sc = arg;
struct cy_port *cy;
int cy_chip, stat;
int int_serviced = 0;
/*
* Check interrupt status of each CD1400 chip on this card
* (multiple cards cannot share the same interrupt)
*/
for (cy_chip = 0; cy_chip < sc->sc_nchips; cy_chip++) {
stat = cd_read_reg(sc, cy_chip, CD1400_SVRR);
if (stat == 0)
continue;
if (buf_p == cy->cy_ibuf_rd_ptr) {
if (buf_p == cy->cy_ibuf)
buf_p = cy->cy_ibuf_end;
buf_p -= 2;
cy->cy_ibuf_overruns++;
}
cy_events = 1;
} else {/* no exception, received data OK */
n_chars = cd_read_reg(sc, cy->cy_chip,
CD1400_RDCR);
/* If no tty or not open, discard data */
if (cy->cy_tty == NULL ||
!ISSET(cy->cy_tty->t_state, TS_ISOPEN)) {
while (n_chars--)
(void)cd_read_reg(sc,
cy->cy_chip, CD1400_RDSR);
goto end_rx_serv;
}
#ifdef CY_DEBUG
aprint_debug_dev(sc->sc_dev,
"port %d receive ok %d chars\n",
cy->cy_port_num, n_chars);
#endif
while (n_chars--) {
*buf_p++ = 0; /* status: OK */
/* data byte */
*buf_p++ = cd_read_reg(sc,
cy->cy_chip, CD1400_RDSR);
if (buf_p == cy->cy_ibuf_end)
buf_p = cy->cy_ibuf;
if (buf_p == cy->cy_ibuf_rd_ptr) {
if (buf_p == cy->cy_ibuf)
buf_p = cy->cy_ibuf_end;
buf_p -= 2;
cy->cy_ibuf_overruns++;
break;
}
}
cy_events = 1;
}
cy->cy_ibuf_wr_ptr = buf_p;
/* RTS handshaking for incoming data */
if (ISSET(cy->cy_tty->t_cflag, CRTSCTS)) {
int bf, msvr;
/* wait until cd1400 is ready to process a new command */
while (cd_read_reg(sc, cy->cy_chip, CD1400_CCR) != 0 && waitcnt-- > 0);
if (waitcnt == 0)
log(LOG_ERR, "%s: channel command timeout\n",
device_xname(sc->sc_dev));
cd_write_reg(sc, cy->cy_chip, CD1400_CCR, cmd);
}
/*
* Compute clock option register and baud rate register values
* for a given speed. Return 0 on success, -1 on failure.
*
* The error between requested and actual speed seems
* to be well within allowed limits (less than 3%)
* with every speed value between 50 and 150000 bps.
*/
int
cy_speed(speed_t speed, int *cor, int *bpr, int cy_clock)
{
int c, co, br;
if (speed < 50 || speed > 150000)
return -1;
for (c = 0, co = 8; co <= 2048; co <<= 2, c++) {
br = (cy_clock + (co * speed) / 2) / (co * speed);
if (br < 0x100) {
*bpr = br;
*cor = c;
return 0;
}
}