diff options
author | Nathan Binkert <nate@cvs.openbsd.org> | 2001-06-21 13:21:51 +0000 |
---|---|---|
committer | Nathan Binkert <nate@cvs.openbsd.org> | 2001-06-21 13:21:51 +0000 |
commit | 1563926162fa9306a6a8c642cfd658595f08870f (patch) | |
tree | 4082e652ba2ac8338869b9a43ee8f37937852bc8 /sys/dev/pci | |
parent | 2dbfd090db438eaf05ca66a1de9a0434ccf2063d (diff) |
Add support for the Cyclades-Z multiport serial cards.
This has not been thoroughly tested yet, so it's not going into GENERIC now.
From NetBSD.
Diffstat (limited to 'sys/dev/pci')
-rw-r--r-- | sys/dev/pci/cz.c | 1670 | ||||
-rw-r--r-- | sys/dev/pci/czreg.h | 419 | ||||
-rw-r--r-- | sys/dev/pci/files.pci | 7 | ||||
-rw-r--r-- | sys/dev/pci/plx9060reg.h | 196 | ||||
-rw-r--r-- | sys/dev/pci/plx9060var.h | 57 |
5 files changed, 2348 insertions, 1 deletions
diff --git a/sys/dev/pci/cz.c b/sys/dev/pci/cz.c new file mode 100644 index 00000000000..d3978231f12 --- /dev/null +++ b/sys/dev/pci/cz.c @@ -0,0 +1,1670 @@ +/* $OpenBSD: cz.c,v 1.1 2001/06/21 13:21:50 nate Exp $ */ +/* $NetBSD: cz.c,v 1.15 2001/01/20 19:10:36 thorpej Exp $ */ + +/*- + * Copyright (c) 2000 Zembu Labs, Inc. + * All rights reserved. + * + * Authors: Jason R. Thorpe <thorpej@zembu.com> + * Bill Studenmund <wrstuden@zembu.com> + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Zembu Labs, Inc. + * 4. Neither the name of Zembu Labs nor the names of its employees may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR- + * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS- + * CLAIMED. IN NO EVENT SHALL ZEMBU LABS 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. + */ + +/* + * Cyclades-Z series multi-port serial adapter driver for NetBSD. + * + * Some notes: + * + * - The Cyclades-Z has fully automatic hardware (and software!) + * flow control. We only utilize RTS/CTS flow control here, + * and it is implemented in a very simplistic manner. This + * may be an area of future work. + * + * - The PLX can map the either the board's RAM or host RAM + * into the MIPS's memory window. This would enable us to + * use less expensive (for us) memory reads/writes to host + * RAM, rather than time-consuming reads/writes to PCI + * memory space. However, the PLX can only map a 0-128M + * window, so we would have to ensure that the DMA address + * of the host RAM fits there. This is kind of a pain, + * so we just don't bother right now. + * + * - In a perfect world, we would use the autoconfiguration + * mechanism to attach the TTYs that we find. However, + * that leads to somewhat icky looking autoconfiguration + * messages (one for every TTY, up to 64 per board!). So + * we don't do it that way, but assign minors as if there + * were the max of 64 ports per board. + * + * - We don't bother with PPS support here. There are so many + * ports, each with a large amount of buffer space, that the + * normal mode of operation is to poll the boards regularly + * (generally, every 20ms or so). This makes this driver + * unsuitable for PPS, as the latency will be generally too + * high. + */ +/* + * This driver inspired by the FreeBSD driver written by Brian J. McGovern + * for FreeBSD 3.2. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/tty.h> +#include <sys/conf.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/fcntl.h> +#include <sys/syslog.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> +#include <dev/pci/czreg.h> + +#include <dev/pci/plx9060reg.h> +#include <dev/pci/plx9060var.h> + +#include <dev/microcode/cyclades/cyzfirm.h> + +#define CZ_DRIVER_VERSION 0x20000411 + +#define CZ_POLL_MS 20 + +/* These are the interrupts we always use. */ +#define CZ_INTERRUPTS \ + (C_IN_MDSR | C_IN_MRI | C_IN_MRTS | C_IN_MCTS | C_IN_TXBEMPTY | \ + C_IN_TXFEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM | C_IN_RXNNDT | \ + C_IN_MDCD | C_IN_PR_ERROR | C_IN_FR_ERROR | C_IN_OVR_ERROR | \ + C_IN_RXOFL | C_IN_IOCTLW | C_IN_RXBRK) + +/* + * cztty_softc: + * + * Per-channel (TTY) state. + */ +struct cztty_softc { + struct cz_softc *sc_parent; + struct tty *sc_tty; + + struct timeout sc_diag_to; + + int sc_channel; /* Also used to flag unattached chan */ +#define CZTTY_CHANNEL_DEAD -1 + + bus_space_tag_t sc_chan_st; /* channel space tag */ + bus_space_handle_t sc_chan_sh; /* channel space handle */ + bus_space_handle_t sc_buf_sh; /* buffer space handle */ + + u_int sc_overflows, + sc_parity_errors, + sc_framing_errors, + sc_errors; + + int sc_swflags; + + u_int32_t sc_rs_control_dtr, + sc_chanctl_hw_flow, + sc_chanctl_comm_baud, + sc_chanctl_rs_control, + sc_chanctl_comm_data_l, + sc_chanctl_comm_parity; +}; + +/* + * cz_softc: + * + * Per-board state. + */ +struct cz_softc { + struct device cz_dev; /* generic device info */ + struct plx9060_config cz_plx; /* PLX 9060 config info */ + bus_space_tag_t cz_win_st; /* window space tag */ + bus_space_handle_t cz_win_sh; /* window space handle */ + struct timeout cz_timeout; /* timeout for polling-mode */ + + void *cz_ih; /* interrupt handle */ + + u_int32_t cz_mailbox0; /* our MAILBOX0 value */ + int cz_nchannels; /* number of channels */ + int cz_nopenchan; /* number of open channels */ + struct cztty_softc *cz_ports; /* our array of ports */ + + bus_addr_t cz_fwctl; /* offset of firmware control */ +}; + +int cz_match(struct device *, void *, void *); +void cz_attach(struct device *, struct device *, void *); +int cz_wait_pci_doorbell(struct cz_softc *, char *); + +struct cfattach cz_ca = { + sizeof(struct cz_softc), cz_match, cz_attach +}; + +void cz_reset_board(struct cz_softc *); +int cz_load_firmware(struct cz_softc *); + +int cz_intr(void *); +void cz_poll(void *); +int cztty_transmit(struct cztty_softc *, struct tty *); +int cztty_receive(struct cztty_softc *, struct tty *); + +struct cztty_softc * cztty_getttysoftc(dev_t dev); +int cztty_findmajor(void); +int cztty_major; +int cztty_attached_ttys; +int cz_timeout_ticks; + +cdev_decl(cztty); + +void czttystart(struct tty *tp); +int czttyparam(struct tty *tp, struct termios *t); +void cztty_shutdown(struct cztty_softc *sc); +void cztty_modem(struct cztty_softc *sc, int onoff); +void cztty_break(struct cztty_softc *sc, int onoff); +void tiocm_to_cztty(struct cztty_softc *sc, u_long how, int ttybits); +int cztty_to_tiocm(struct cztty_softc *sc); +void cztty_diag(void *arg); + +struct cfdriver cz_cd = { + 0, "cz", DV_TTY +}; + +/* Macros to clear/set/test flags. */ +#define SET(t, f) (t) |= (f) +#define CLR(t, f) (t) &= ~(f) +#define ISSET(t, f) ((t) & (f)) + +/* + * Macros to read and write the PLX. + */ +#define CZ_PLX_READ(cz, reg) \ + bus_space_read_4((cz)->cz_plx.plx_st, (cz)->cz_plx.plx_sh, (reg)) +#define CZ_PLX_WRITE(cz, reg, val) \ + bus_space_write_4((cz)->cz_plx.plx_st, (cz)->cz_plx.plx_sh, \ + (reg), (val)) + +/* + * Macros to read and write the FPGA. We must already be in the FPGA + * window for this. + */ +#define CZ_FPGA_READ(cz, reg) \ + bus_space_read_4((cz)->cz_win_st, (cz)->cz_win_sh, (reg)) +#define CZ_FPGA_WRITE(cz, reg, val) \ + bus_space_write_4((cz)->cz_win_st, (cz)->cz_win_sh, (reg), (val)) + +/* + * Macros to read and write the firmware control structures in board RAM. + */ +#define CZ_FWCTL_READ(cz, off) \ + bus_space_read_4((cz)->cz_win_st, (cz)->cz_win_sh, \ + (cz)->cz_fwctl + (off)) + +#define CZ_FWCTL_WRITE(cz, off, val) \ + bus_space_write_4((cz)->cz_win_st, (cz)->cz_win_sh, \ + (cz)->cz_fwctl + (off), (val)) + +/* + * Convenience macros for cztty routines. PLX window MUST be to RAM. + */ +#define CZTTY_CHAN_READ(sc, off) \ + bus_space_read_4((sc)->sc_chan_st, (sc)->sc_chan_sh, (off)) + +#define CZTTY_CHAN_WRITE(sc, off, val) \ + bus_space_write_4((sc)->sc_chan_st, (sc)->sc_chan_sh, \ + (off), (val)) + +#define CZTTY_BUF_READ(sc, off) \ + bus_space_read_4((sc)->sc_chan_st, (sc)->sc_buf_sh, (off)) + +#define CZTTY_BUF_WRITE(sc, off, val) \ + bus_space_write_4((sc)->sc_chan_st, (sc)->sc_buf_sh, \ + (off), (val)) + +/* + * Convenience macros. + */ +#define CZ_WIN_RAM(cz) \ +do { \ + CZ_PLX_WRITE((cz), PLX_LAS0BA, LOCAL_ADDR0_RAM); \ + delay(100); \ +} while (0) + +#define CZ_WIN_FPGA(cz) \ +do { \ + CZ_PLX_WRITE((cz), PLX_LAS0BA, LOCAL_ADDR0_FPGA); \ + delay(100); \ +} while (0) + +/***************************************************************************** + * Cyclades-Z controller code starts here... + *****************************************************************************/ + +/* + * cz_match: + * + * Determine if the given PCI device is a Cyclades-Z board. + */ +int +cz_match(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct pci_attach_args *pa = aux; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_CYCLADES) { + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_CYCLADES_CYCLOMZ_2: + return (1); + } + } + + return (0); +} + +/* + * cz_attach: + * + * A Cyclades-Z board was found; attach it. + */ +void +cz_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct cz_softc *cz = (void *) self; + struct pci_attach_args *pa = aux; + pci_chipset_tag_t pc = pa->pa_pc; + pci_intr_handle_t ih; + const char *intrstr = NULL; + struct cztty_softc *sc; + struct tty *tp; + int i; + + printf(": Cyclades-Z multiport serial\n"); + + cz->cz_plx.plx_pc = pa->pa_pc; + cz->cz_plx.plx_tag = pa->pa_tag; + + if (pci_mapreg_map(pa, PLX_PCI_RUNTIME_MEMADDR, + PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT, 0, + &cz->cz_plx.plx_st, &cz->cz_plx.plx_sh, NULL, NULL) != 0) { + printf("%s: unable to map PLX registers\n", + cz->cz_dev.dv_xname); + return; + } + if (pci_mapreg_map(pa, PLX_PCI_LOCAL_ADDR0, + PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT, 0, + &cz->cz_win_st, &cz->cz_win_sh, NULL, NULL) != 0) { + printf("%s: unable to map device window\n", + cz->cz_dev.dv_xname); + return; + } + + cz->cz_mailbox0 = CZ_PLX_READ(cz, PLX_MAILBOX0); + cz->cz_nopenchan = 0; + + /* + * Make sure that the board is completely stopped. + */ + CZ_WIN_FPGA(cz); + CZ_FPGA_WRITE(cz, FPGA_CPU_STOP, 0); + + /* + * Load the board's firmware. + */ + if (cz_load_firmware(cz) != 0) + return; + + /* + * Now that we're ready to roll, map and establish the interrupt + * handler. + */ + if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, + pa->pa_intrline, &ih) != 0) { + /* + * The common case is for Cyclades-Z boards to run + * in polling mode, and thus not have an interrupt + * mapped for them. Don't bother reporting that + * the interrupt is not mappable, since this isn't + * really an error. + */ + cz->cz_ih = NULL; + goto polling_mode; + } else { + intrstr = pci_intr_string(pa->pa_pc, ih); + cz->cz_ih = pci_intr_establish(pc, ih, IPL_TTY, + cz_intr, cz, cz->cz_dev.dv_xname); + } + if (cz->cz_ih == NULL) { + printf("%s: unable to establish interrupt", + cz->cz_dev.dv_xname); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + /* We will fall-back on polling mode. */ + } else + printf("%s: interrupting at %s\n", + cz->cz_dev.dv_xname, intrstr); + + polling_mode: + if (cz->cz_ih == NULL) { + timeout_set(&cz->cz_timeout, cz_poll, cz); + if (cz_timeout_ticks == 0) + cz_timeout_ticks = max(1, hz * CZ_POLL_MS / 1000); + printf("%s: polling mode, %d ms interval (%d tick%s)\n", + cz->cz_dev.dv_xname, CZ_POLL_MS, cz_timeout_ticks, + cz_timeout_ticks == 1 ? "" : "s"); + } + + if (cztty_major == 0) + cztty_major = cztty_findmajor(); + /* + * Allocate sufficient pointers for the children and + * attach them. Set all ports to a reasonable initial + * configuration while we're at it: + * + * disabled + * 8N1 + * default baud rate + * hardware flow control. + */ + CZ_WIN_RAM(cz); + + if (cz->cz_nchannels == 0) { + /* No channels? No more work to do! */ + return; + } + + cz->cz_ports = malloc(sizeof(struct cztty_softc) * cz->cz_nchannels, + M_DEVBUF, M_WAITOK); + cztty_attached_ttys += cz->cz_nchannels; + memset(cz->cz_ports, 0, + sizeof(struct cztty_softc) * cz->cz_nchannels); + + for (i = 0; i < cz->cz_nchannels; i++) { + sc = &cz->cz_ports[i]; + + sc->sc_channel = i; + sc->sc_chan_st = cz->cz_win_st; + sc->sc_parent = cz; + + if (bus_space_subregion(cz->cz_win_st, cz->cz_win_sh, + cz->cz_fwctl + ZFIRM_CHNCTL_OFF(i, 0), + ZFIRM_CHNCTL_SIZE, &sc->sc_chan_sh)) { + printf("%s: unable to subregion channel %d control\n", + cz->cz_dev.dv_xname, i); + sc->sc_channel = CZTTY_CHANNEL_DEAD; + continue; + } + if (bus_space_subregion(cz->cz_win_st, cz->cz_win_sh, + cz->cz_fwctl + ZFIRM_BUFCTL_OFF(i, 0), + ZFIRM_BUFCTL_SIZE, &sc->sc_buf_sh)) { + printf("%s: unable to subregion channel %d buffer\n", + cz->cz_dev.dv_xname, i); + sc->sc_channel = CZTTY_CHANNEL_DEAD; + continue; + } + + timeout_set(&sc->sc_diag_to, cztty_diag, sc); + + tp = ttymalloc(); + tp->t_dev = makedev(cztty_major, + (cz->cz_dev.dv_unit * ZFIRM_MAX_CHANNELS) + i); + tp->t_oproc = czttystart; + tp->t_param = czttyparam; + tty_attach(tp); + + sc->sc_tty = tp; + + CZTTY_CHAN_WRITE(sc, CHNCTL_OP_MODE, C_CH_DISABLE); + CZTTY_CHAN_WRITE(sc, CHNCTL_INTR_ENABLE, CZ_INTERRUPTS); + CZTTY_CHAN_WRITE(sc, CHNCTL_SW_FLOW, 0); + CZTTY_CHAN_WRITE(sc, CHNCTL_FLOW_XON, 0x11); + CZTTY_CHAN_WRITE(sc, CHNCTL_FLOW_XOFF, 0x13); + CZTTY_CHAN_WRITE(sc, CHNCTL_COMM_BAUD, TTYDEF_SPEED); + CZTTY_CHAN_WRITE(sc, CHNCTL_COMM_PARITY, C_PR_NONE); + CZTTY_CHAN_WRITE(sc, CHNCTL_COMM_DATA_L, C_DL_CS8 | C_DL_1STOP); + CZTTY_CHAN_WRITE(sc, CHNCTL_COMM_FLAGS, 0); + CZTTY_CHAN_WRITE(sc, CHNCTL_HW_FLOW, C_RS_CTS | C_RS_RTS); + CZTTY_CHAN_WRITE(sc, CHNCTL_RS_CONTROL, 0); + } +} + +/* + * cz_reset_board: + * + * Reset the board via the PLX. + */ +void +cz_reset_board(struct cz_softc *cz) +{ + u_int32_t reg; + + reg = CZ_PLX_READ(cz, PLX_CONTROL); + CZ_PLX_WRITE(cz, PLX_CONTROL, reg | CONTROL_SWR); + delay(1000); + + CZ_PLX_WRITE(cz, PLX_CONTROL, reg); + delay(1000); + + /* Now reload the PLX from its EEPROM. */ + reg = CZ_PLX_READ(cz, PLX_CONTROL); + CZ_PLX_WRITE(cz, PLX_CONTROL, reg | CONTROL_RELOADCFG); + delay(1000); + CZ_PLX_WRITE(cz, PLX_CONTROL, reg); +} + +/* + * cz_load_firmware: + * + * Load the ZFIRM firmware into the board's RAM and start it + * running. + */ +int +cz_load_firmware(struct cz_softc *cz) +{ + struct zfirm_header *zfh; + struct zfirm_config *zfc; + struct zfirm_block *zfb, *zblocks; + const u_int8_t *cp; + const char *board; + u_int32_t fid; + int i, j, nconfigs, nblocks, nbytes; + + zfh = (struct zfirm_header *) cycladesz_firmware; + + /* Find the config header. */ + if (letoh32(zfh->zfh_configoff) & (sizeof(u_int32_t) - 1)) { + printf("%s: bad ZFIRM config offset: 0x%x\n", + cz->cz_dev.dv_xname, letoh32(zfh->zfh_configoff)); + return (EIO); + } + zfc = (struct zfirm_config *)(cycladesz_firmware + + letoh32(zfh->zfh_configoff)); + nconfigs = letoh32(zfh->zfh_nconfig); + + /* Locate the correct configuration for our board. */ + for (i = 0; i < nconfigs; i++, zfc++) { + if (letoh32(zfc->zfc_mailbox) == cz->cz_mailbox0 && + letoh32(zfc->zfc_function) == ZFC_FUNCTION_NORMAL) + break; + } + if (i == nconfigs) { + printf("%s: unable to locate config header\n", + cz->cz_dev.dv_xname); + return (EIO); + } + + nblocks = letoh32(zfc->zfc_nblocks); + zblocks = (struct zfirm_block *)(cycladesz_firmware + + letoh32(zfh->zfh_blockoff)); + + /* + * 8Zo ver. 1 doesn't have an FPGA. Load it on all others if + * necessary. + */ + if (cz->cz_mailbox0 != MAILBOX0_8Zo_V1 +#if 0 + && ((CZ_PLX_READ(cz, PLX_CONTROL) & CONTROL_FPGA_LOADED) == 0) +#endif + ) { +#ifdef CZ_DEBUG + printf("%s: Loading FPGA...", cz->cz_dev.dv_xname); +#endif + CZ_WIN_FPGA(cz); + for (i = 0; i < nblocks; i++) { + /* zfb = zblocks + letoh32(zfc->zfc_blocklist[i]) ?? */ + zfb = &zblocks[letoh32(zfc->zfc_blocklist[i])]; + if (letoh32(zfb->zfb_type) == ZFB_TYPE_FPGA) { + nbytes = letoh32(zfb->zfb_size); + cp = &cycladesz_firmware[ + letoh32(zfb->zfb_fileoff)]; + for (j = 0; j < nbytes; j++, cp++) { + bus_space_write_1(cz->cz_win_st, + cz->cz_win_sh, 0, *cp); + /* FPGA needs 30-100us to settle. */ + delay(10); + } + } + } +#ifdef CZ_DEBUG + printf("done\n"); +#endif + } + + /* Now load the firmware. */ + CZ_WIN_RAM(cz); + + for (i = 0; i < nblocks; i++) { + /* zfb = zblocks + letoh32(zfc->zfc_blocklist[i]) ?? */ + zfb = &zblocks[letoh32(zfc->zfc_blocklist[i])]; + if (letoh32(zfb->zfb_type) == ZFB_TYPE_FIRMWARE) { + const u_int32_t *lp; + u_int32_t ro = letoh32(zfb->zfb_ramoff); + nbytes = letoh32(zfb->zfb_size); + lp = (const u_int32_t *) + &cycladesz_firmware[letoh32(zfb->zfb_fileoff)]; + for (j = 0; j < nbytes; j += 4, lp++) { + bus_space_write_4(cz->cz_win_st, cz->cz_win_sh, + ro + j, letoh32(*lp)); + delay(10); + } + } + } + + /* Now restart the MIPS. */ + CZ_WIN_FPGA(cz); + CZ_FPGA_WRITE(cz, FPGA_CPU_START, 0); + + /* Wait for the MIPS to start, then report the results. */ + CZ_WIN_RAM(cz); + +#ifdef CZ_DEBUG + printf("%s: waiting for MIPS to start", cz->cz_dev.dv_xname); +#endif + for (i = 0; i < 100; i++) { + fid = bus_space_read_4(cz->cz_win_st, cz->cz_win_sh, + ZFIRM_SIG_OFF); + if (fid == ZFIRM_SIG) { + /* MIPS has booted. */ + break; + } else if (fid == ZFIRM_HLT) { + /* + * The MIPS has halted, usually due to a power + * shortage on the expansion module. + */ + printf("%s: MIPS halted; possible power supply " + "problem\n", cz->cz_dev.dv_xname); + return (EIO); + } else { +#ifdef CZ_DEBUG + if ((i % 8) == 0) + printf("."); +#endif + delay(250000); + } + } +#ifdef CZ_DEBUG + printf("\n"); +#endif + if (i == 100) { + CZ_WIN_FPGA(cz); + printf("%s: MIPS failed to start; wanted 0x%08x got 0x%08x\n", + cz->cz_dev.dv_xname, ZFIRM_SIG, fid); + printf("%s: FPGA ID 0x%08x, FPGA version 0x%08x\n", + cz->cz_dev.dv_xname, CZ_FPGA_READ(cz, FPGA_ID), + CZ_FPGA_READ(cz, FPGA_VERSION)); + return (EIO); + } + + /* + * Locate the firmware control structures. + */ + cz->cz_fwctl = bus_space_read_4(cz->cz_win_st, cz->cz_win_sh, + ZFIRM_CTRLADDR_OFF); +#ifdef CZ_DEBUG + printf("%s: FWCTL structure at offset 0x%08lx\n", + cz->cz_dev.dv_xname, cz->cz_fwctl); +#endif + + CZ_FWCTL_WRITE(cz, BRDCTL_C_OS, C_OS_BSD); + CZ_FWCTL_WRITE(cz, BRDCTL_DRVERSION, CZ_DRIVER_VERSION); + + cz->cz_nchannels = CZ_FWCTL_READ(cz, BRDCTL_NCHANNEL); + + switch (cz->cz_mailbox0) { + case MAILBOX0_8Zo_V1: + board = "Cyclades-8Zo ver. 1"; + break; + + case MAILBOX0_8Zo_V2: + board = "Cyclades-8Zo ver. 2"; + break; + + case MAILBOX0_Ze_V1: + board = "Cyclades-Ze"; + break; + + default: + board = "unknown Cyclades Z-series"; + break; + } + + fid = CZ_FWCTL_READ(cz, BRDCTL_FWVERSION); + printf("%s: %s, ", cz->cz_dev.dv_xname, board); + if (cz->cz_nchannels == 0) + printf("no channels attached, "); + else + printf("%d channels (ttyCZ%04d..ttyCZ%04d), ", + cz->cz_nchannels, cztty_attached_ttys, + cztty_attached_ttys + (cz->cz_nchannels - 1)); + printf("firmware %x.%x.%x\n", + (fid >> 8) & 0xf, (fid >> 4) & 0xf, fid & 0xf); + + return (0); +} + +/* + * cz_poll: + * + * This card doesn't do interrupts, so scan it for activity every CZ_POLL_MS + * ms. + */ +void +cz_poll(void *arg) +{ + int s = spltty(); + struct cz_softc *cz = arg; + + cz_intr(cz); + timeout_add(&cz->cz_timeout, cz_timeout_ticks); + + splx(s); +} + +/* + * cz_intr: + * + * Interrupt service routine. + * + * We either are receiving an interrupt directly from the board, or we are + * in polling mode and it's time to poll. + */ +int +cz_intr(void *arg) +{ + int rval = 0; + u_int command, channel, param; + struct cz_softc *cz = arg; + struct cztty_softc *sc; + struct tty *tp; + + while ((command = (CZ_PLX_READ(cz, PLX_LOCAL_PCI_DOORBELL) & 0xff))) { + rval = 1; + channel = CZ_FWCTL_READ(cz, BRDCTL_FWCMD_CHANNEL); + param = CZ_FWCTL_READ(cz, BRDCTL_FWCMD_PARAM); + + /* now clear this interrupt, posslibly enabling another */ + CZ_PLX_WRITE(cz, PLX_LOCAL_PCI_DOORBELL, command); + + if (cz->cz_ports == NULL) { +#ifdef CZ_DEBUG + printf("%s: interrupt on channel %d, but no channels\n", + cz->cz_dev.dv_xname, channel); +#endif + continue; + } + + sc = &cz->cz_ports[channel]; + + if (sc->sc_channel == CZTTY_CHANNEL_DEAD) + break; + + tp = sc->sc_tty; + + switch (command) { + case C_CM_TXFEMPTY: /* transmit cases */ + case C_CM_TXBEMPTY: + case C_CM_TXLOWWM: + case C_CM_INTBACK: + if (!ISSET(tp->t_state, TS_ISOPEN)) { +#ifdef CZ_DEBUG + printf("%s: tx intr on closed channel %d\n", + cz->cz_dev.dv_xname, channel); +#endif + break; + } + + if (cztty_transmit(sc, tp)) { + /* + * Do wakeup stuff here. + */ + ttwakeup(tp); + wakeup(tp); + } + break; + + case C_CM_RXNNDT: /* receive cases */ + case C_CM_RXHIWM: + case C_CM_INTBACK2: /* from restart ?? */ +#if 0 + case C_CM_ICHAR: +#endif + if (!ISSET(tp->t_state, TS_ISOPEN)) { + CZTTY_BUF_WRITE(sc, BUFCTL_RX_GET, + CZTTY_BUF_READ(sc, BUFCTL_RX_PUT)); + break; + } + + if (cztty_receive(sc, tp)) { + /* + * Do wakeup stuff here. + */ + ttwakeup(tp); + wakeup(tp); + } + break; + + case C_CM_MDCD: + if (!ISSET(tp->t_state, TS_ISOPEN)) + break; + + (void) (*linesw[tp->t_line].l_modem)(tp, + ISSET(C_RS_DCD, CZTTY_CHAN_READ(sc, + CHNCTL_RS_STATUS))); + break; + + case C_CM_MDSR: + case C_CM_MRI: + case C_CM_MCTS: + case C_CM_MRTS: + break; + + case C_CM_IOCTLW: + break; + + case C_CM_PR_ERROR: + sc->sc_parity_errors++; + goto error_common; + + case C_CM_FR_ERROR: + sc->sc_framing_errors++; + goto error_common; + + case C_CM_OVR_ERROR: + sc->sc_overflows++; + error_common: + if (sc->sc_errors++ == 0) + timeout_add(&sc->sc_diag_to, 60 * hz); + break; + + case C_CM_RXBRK: + if (!ISSET(tp->t_state, TS_ISOPEN)) + break; + + /* + * A break is a \000 character with TTY_FE error + * flags set. So TTY_FE by itself works. + */ + (*linesw[tp->t_line].l_rint)(TTY_FE, tp); + ttwakeup(tp); + wakeup(tp); + break; + + default: +#ifdef CZ_DEBUG + printf("%s: channel %d: Unknown interrupt 0x%x\n", + cz->cz_dev.dv_xname, sc->sc_channel, command); +#endif + break; + } + } + + return (rval); +} + +/* + * cz_wait_pci_doorbell: + * + * Wait for the pci doorbell to be clear - wait for pending + * activity to drain. + */ +int +cz_wait_pci_doorbell(struct cz_softc *cz, char *wstring) +{ + int error; + + while (CZ_PLX_READ(cz, PLX_PCI_LOCAL_DOORBELL)) { + error = tsleep(cz, TTIPRI | PCATCH, wstring, max(1, hz/100)); + if ((error != 0) && (error != EWOULDBLOCK)) + return (error); + } + return (0); +} + +/***************************************************************************** + * Cyclades-Z TTY code starts here... + *****************************************************************************/ + +#define CZTTYDIALOUT_MASK 0x80000 + +#define CZTTY_DIALOUT(dev) (minor((dev)) & CZTTYDIALOUT_MASK) +#define CZTTY_CZ(sc) ((sc)->sc_parent) + +#define CZTTY_SOFTC(dev) cztty_getttysoftc(dev) + +struct cztty_softc * +cztty_getttysoftc(dev_t dev) +{ + int i, j, k, u = minor(dev) & ~CZTTYDIALOUT_MASK; + struct cz_softc *cz; + + for (i = 0, j = 0; i < cz_cd.cd_ndevs; i++) { + k = j; + cz = (struct cz_softc *)device_lookup(&cz_cd, i); + if (cz == NULL) + continue; + if (cz->cz_ports == NULL) + continue; + j += cz->cz_nchannels; + if (j > u) + break; + } + + if (i >= cz_cd.cd_ndevs) + return (NULL); + else + return (&cz->cz_ports[u - k]); +} + +int +cztty_findmajor(void) +{ + int maj; + + for (maj = 0; maj < nchrdev; maj++) { + if (cdevsw[maj].d_open == czttyopen) + break; + } + + return (maj == nchrdev) ? 0 : maj; +} + +/* + * czttytty: + * + * Return a pointer to our tty. + */ +struct tty * +czttytty(dev_t dev) +{ + struct cztty_softc *sc = CZTTY_SOFTC(dev); + +#ifdef DIAGNOSTIC + if (sc == NULL) + panic("czttytty"); +#endif + + return (sc->sc_tty); +} + +/* + * cztty_shutdown: + * + * Shut down a port. + */ +void +cztty_shutdown(struct cztty_softc *sc) +{ + struct cz_softc *cz = CZTTY_CZ(sc); + struct tty *tp = sc->sc_tty; + int s; + + s = spltty(); + + /* Clear any break condition set with TIOCSBRK. */ + cztty_break(sc, 0); + + /* + * Hang up if necessary. Wait a bit, so the other side has time to + * notice even if we immediately open the port again. + */ + if (ISSET(tp->t_cflag, HUPCL)) { + cztty_modem(sc, 0); + (void) tsleep(tp, TTIPRI, ttclos, hz); + } + + /* Disable the channel. */ + cz_wait_pci_doorbell(cz, "czdis"); + CZTTY_CHAN_WRITE(sc, CHNCTL_OP_MODE, C_CH_DISABLE); + CZ_FWCTL_WRITE(cz, BRDCTL_HCMD_CHANNEL, sc->sc_channel); + CZ_PLX_WRITE(cz, PLX_PCI_LOCAL_DOORBELL, C_CM_IOCTL); + + if ((--cz->cz_nopenchan == 0) && (cz->cz_ih == NULL)) { +#ifdef CZ_DEBUG + printf("%s: Disabling polling\n", cz->cz_dev.dv_xname); +#endif + timeout_del(&cz->cz_timeout); + } + + splx(s); +} + +/* + * czttyopen: + * + * Open a Cyclades-Z serial port. + */ +int +czttyopen(dev_t dev, int flags, int mode, struct proc *p) +{ + struct cztty_softc *sc = CZTTY_SOFTC(dev); + struct cz_softc *cz; + struct tty *tp; + int s, error; + + if (sc == NULL) + return (ENXIO); + + if (sc->sc_channel == CZTTY_CHANNEL_DEAD) + return (ENXIO); + + cz = CZTTY_CZ(sc); + tp = sc->sc_tty; + + if (ISSET(tp->t_state, TS_ISOPEN) && + ISSET(tp->t_state, TS_XCLUDE) && + p->p_ucred->cr_uid != 0) + return (EBUSY); + + s = spltty(); + + /* + * Do the following iff this is a first open. + */ + if (!ISSET(tp->t_state, TS_ISOPEN)) { + struct termios t; + + tp->t_dev = dev; + + /* If we're turning things on, enable interrupts */ + if ((cz->cz_nopenchan++ == 0) && (cz->cz_ih == NULL)) { +#ifdef CZ_DEBUG + printf("%s: Enabling polling.\n", + cz->cz_dev.dv_xname); +#endif + timeout_add(&cz->cz_timeout, cz_timeout_ticks); + } + + /* + * Enable the channel. Don't actually ring the + * doorbell here; czttyparam() will do it for us. + */ + cz_wait_pci_doorbell(cz, "czopen"); + + CZTTY_CHAN_WRITE(sc, CHNCTL_OP_MODE, C_CH_ENABLE); + + /* + * Initialize the termios status to the defaults. Add in the + * sticky bits from TIOCSFLAGS. + */ + t.c_ispeed = 0; + t.c_ospeed = TTYDEF_SPEED; + t.c_cflag = TTYDEF_CFLAG; + if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL)) + SET(t.c_cflag, CLOCAL); + if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS)) + SET(t.c_cflag, CRTSCTS); + + /* + * Reset the input and output rings. Do this before + * we call czttyparam(), as that function enables + * the channel. + */ + CZTTY_BUF_WRITE(sc, BUFCTL_RX_GET, + CZTTY_BUF_READ(sc, BUFCTL_RX_PUT)); + CZTTY_BUF_WRITE(sc, BUFCTL_TX_PUT, + CZTTY_BUF_READ(sc, BUFCTL_TX_GET)); + + /* Make sure czttyparam() will see changes. */ + tp->t_ospeed = 0; + (void) czttyparam(tp, &t); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_lflag = TTYDEF_LFLAG; + ttychars(tp); + ttsetwater(tp); + + /* + * Turn on DTR. We must always do this, even if carrier is not + * present, because otherwise we'd have to use TIOCSDTR + * immediately after setting CLOCAL, which applications do not + * expect. We always assert DTR while the device is open + * unless explicitly requested to deassert it. + */ + cztty_modem(sc, 1); + } + + splx(s); + + error = ttyopen(CZTTY_DIALOUT(dev), tp); + if (error) + goto bad; + + error = (*linesw[tp->t_line].l_open)(dev, tp); + if (error) + goto bad; + + return (0); + + bad: + if (!ISSET(tp->t_state, TS_ISOPEN)) { + /* + * We failed to open the device, and nobody else had it opened. + * Clean up the state as appropriate. + */ + cztty_shutdown(sc); + } + + return (error); +} + +/* + * czttyclose: + * + * Close a Cyclades-Z serial port. + */ +int +czttyclose(dev_t dev, int flags, int mode, struct proc *p) +{ + struct cztty_softc *sc = CZTTY_SOFTC(dev); + struct tty *tp = sc->sc_tty; + + /* XXX This is for cons.c. */ + if (!ISSET(tp->t_state, TS_ISOPEN)) + return (0); + + (*linesw[tp->t_line].l_close)(tp, flags); + ttyclose(tp); + + if (!ISSET(tp->t_state, TS_ISOPEN)) { + /* + * Although we got a last close, the device may still be in + * use; e.g. if this was the dialout node, and there are still + * processes waiting for carrier on the non-dialout node. + */ + cztty_shutdown(sc); + } + + return (0); +} + +/* + * czttyread: + * + * Read from a Cyclades-Z serial port. + */ +int +czttyread(dev_t dev, struct uio *uio, int flags) +{ + struct cztty_softc *sc = CZTTY_SOFTC(dev); + struct tty *tp = sc->sc_tty; + + return ((*linesw[tp->t_line].l_read)(tp, uio, flags)); +} + +/* + * czttywrite: + * + * Write to a Cyclades-Z serial port. + */ +int +czttywrite(dev_t dev, struct uio *uio, int flags) +{ + struct cztty_softc *sc = CZTTY_SOFTC(dev); + struct tty *tp = sc->sc_tty; + + return ((*linesw[tp->t_line].l_write)(tp, uio, flags)); +} + +#if 0 +/* + * czttypoll: + * + * Poll a Cyclades-Z serial port. + */ +int +czttypoll(dev_t dev, int events, struct proc p) +{ + struct cztty_softc *sc = CZTTY_SOFTC(dev); + struct tty *tp = sc->sc_tty; + + return ((*linesw[tp->t_line].l_poll)(tp, events, p)); +} +#endif + +/* + * czttyioctl: + * + * Perform a control operation on a Cyclades-Z serial port. + */ +int +czttyioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct cztty_softc *sc = CZTTY_SOFTC(dev); + struct tty *tp = sc->sc_tty; + int s, error; + + 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); + + error = 0; + + s = spltty(); + + switch (cmd) { + case TIOCSBRK: + cztty_break(sc, 1); + break; + + case TIOCCBRK: + cztty_break(sc, 0); + break; + + case TIOCGFLAGS: + *(int *)data = sc->sc_swflags; + break; + + case TIOCSFLAGS: + error = suser(p->p_ucred, &p->p_acflag); + if (error) + break; + sc->sc_swflags = *(int *)data; + break; + + case TIOCSDTR: + cztty_modem(sc, 1); + break; + + case TIOCCDTR: + cztty_modem(sc, 0); + break; + + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + tiocm_to_cztty(sc, cmd, *(int *)data); + break; + + case TIOCMGET: + *(int *)data = cztty_to_tiocm(sc); + break; + + default: + error = ENOTTY; + break; + } + + splx(s); + + return (error); +} + +/* + * cztty_break: + * + * Set or clear BREAK on a port. + */ +void +cztty_break(struct cztty_softc *sc, int onoff) +{ + struct cz_softc *cz = CZTTY_CZ(sc); + + cz_wait_pci_doorbell(cz, "czbreak"); + + CZ_FWCTL_WRITE(cz, BRDCTL_HCMD_CHANNEL, sc->sc_channel); + CZ_PLX_WRITE(cz, PLX_PCI_LOCAL_DOORBELL, + onoff ? C_CM_SET_BREAK : C_CM_CLR_BREAK); +} + +/* + * cztty_modem: + * + * Set or clear DTR on a port. + */ +void +cztty_modem(struct cztty_softc *sc, int onoff) +{ + struct cz_softc *cz = CZTTY_CZ(sc); + + if (sc->sc_rs_control_dtr == 0) + return; + + cz_wait_pci_doorbell(cz, "czmod"); + + if (onoff) + sc->sc_chanctl_rs_control |= sc->sc_rs_control_dtr; + else + sc->sc_chanctl_rs_control &= ~sc->sc_rs_control_dtr; + CZTTY_CHAN_WRITE(sc, CHNCTL_RS_CONTROL, sc->sc_chanctl_rs_control); + + CZ_FWCTL_WRITE(cz, BRDCTL_HCMD_CHANNEL, sc->sc_channel); + CZ_PLX_WRITE(cz, PLX_PCI_LOCAL_DOORBELL, C_CM_IOCTLM); +} + +/* + * tiocm_to_cztty: + * + * Process TIOCM* ioctls. + */ +void +tiocm_to_cztty(struct cztty_softc *sc, u_long how, int ttybits) +{ + struct cz_softc *cz = CZTTY_CZ(sc); + u_int32_t czttybits; + + czttybits = 0; + if (ISSET(ttybits, TIOCM_DTR)) + SET(czttybits, C_RS_DTR); + if (ISSET(ttybits, TIOCM_RTS)) + SET(czttybits, C_RS_RTS); + + cz_wait_pci_doorbell(cz, "cztiocm"); + + switch (how) { + case TIOCMBIC: + CLR(sc->sc_chanctl_rs_control, czttybits); + break; + + case TIOCMBIS: + SET(sc->sc_chanctl_rs_control, czttybits); + break; + + case TIOCMSET: + CLR(sc->sc_chanctl_rs_control, C_RS_DTR | C_RS_RTS); + SET(sc->sc_chanctl_rs_control, czttybits); + break; + } + + CZTTY_CHAN_WRITE(sc, CHNCTL_RS_CONTROL, sc->sc_chanctl_rs_control); + + CZ_FWCTL_WRITE(cz, BRDCTL_HCMD_CHANNEL, sc->sc_channel); + CZ_PLX_WRITE(cz, PLX_PCI_LOCAL_DOORBELL, C_CM_IOCTLM); +} + +/* + * cztty_to_tiocm: + * + * Process the TIOCMGET ioctl. + */ +int +cztty_to_tiocm(struct cztty_softc *sc) +{ + struct cz_softc *cz = CZTTY_CZ(sc); + u_int32_t rs_status, op_mode; + int ttybits = 0; + + cz_wait_pci_doorbell(cz, "cztty"); + + rs_status = CZTTY_CHAN_READ(sc, CHNCTL_RS_STATUS); + op_mode = CZTTY_CHAN_READ(sc, CHNCTL_OP_MODE); + + if (ISSET(rs_status, C_RS_RTS)) + SET(ttybits, TIOCM_RTS); + if (ISSET(rs_status, C_RS_CTS)) + SET(ttybits, TIOCM_CTS); + if (ISSET(rs_status, C_RS_DCD)) + SET(ttybits, TIOCM_CAR); + if (ISSET(rs_status, C_RS_DTR)) + SET(ttybits, TIOCM_DTR); + if (ISSET(rs_status, C_RS_RI)) + SET(ttybits, TIOCM_RNG); + if (ISSET(rs_status, C_RS_DSR)) + SET(ttybits, TIOCM_DSR); + + if (ISSET(op_mode, C_CH_ENABLE)) + SET(ttybits, TIOCM_LE); + + return (ttybits); +} + +/* + * czttyparam: + * + * Set Cyclades-Z serial port parameters from termios. + * + * XXX Should just copy the whole termios after making + * XXX sure all the changes could be done. + */ +int +czttyparam(struct tty *tp, struct termios *t) +{ + struct cztty_softc *sc = CZTTY_SOFTC(tp->t_dev); + struct cz_softc *cz = CZTTY_CZ(sc); + u_int32_t rs_status; + int ospeed, cflag; + + ospeed = t->c_ospeed; + cflag = t->c_cflag; + + /* Check requested parameters. */ + if (ospeed < 0) + return (EINVAL); + if (t->c_ispeed && t->c_ispeed != ospeed) + return (EINVAL); + + if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR)) { + SET(cflag, CLOCAL); + CLR(cflag, HUPCL); + } + + /* + * If there were no changes, don't do anything. This avoids dropping + * input and improves performance when all we did was frob things like + * VMIN and VTIME. + */ + if (tp->t_ospeed == ospeed && + tp->t_cflag == cflag) + return (0); + + /* Data bits. */ + sc->sc_chanctl_comm_data_l = 0; + switch (t->c_cflag & CSIZE) { + case CS5: + sc->sc_chanctl_comm_data_l |= C_DL_CS5; + break; + + case CS6: + sc->sc_chanctl_comm_data_l |= C_DL_CS6; + break; + + case CS7: + sc->sc_chanctl_comm_data_l |= C_DL_CS7; + break; + + case CS8: + sc->sc_chanctl_comm_data_l |= C_DL_CS8; + break; + } + + /* Stop bits. */ + if (t->c_cflag & CSTOPB) { + if ((sc->sc_chanctl_comm_data_l & C_DL_CS) == C_DL_CS5) + sc->sc_chanctl_comm_data_l |= C_DL_15STOP; + else + sc->sc_chanctl_comm_data_l |= C_DL_2STOP; + } else + sc->sc_chanctl_comm_data_l |= C_DL_1STOP; + + /* Parity. */ + if (t->c_cflag & PARENB) { + if (t->c_cflag & PARODD) + sc->sc_chanctl_comm_parity = C_PR_ODD; + else + sc->sc_chanctl_comm_parity = C_PR_EVEN; + } else + sc->sc_chanctl_comm_parity = C_PR_NONE; + + /* + * Initialize flow control pins depending on the current flow control + * mode. + */ + if (ISSET(t->c_cflag, CRTSCTS)) { + sc->sc_rs_control_dtr = C_RS_DTR; + sc->sc_chanctl_hw_flow = C_RS_CTS | C_RS_RTS; + } else if (ISSET(t->c_cflag, MDMBUF)) { + sc->sc_rs_control_dtr = 0; + sc->sc_chanctl_hw_flow = C_RS_DCD | C_RS_DTR; + } else { + /* + * If no flow control, then always set RTS. This will make + * the other side happy if it mistakenly thinks we're doing + * RTS/CTS flow control. + */ + sc->sc_rs_control_dtr = C_RS_DTR | C_RS_RTS; + sc->sc_chanctl_hw_flow = 0; + if (ISSET(sc->sc_chanctl_rs_control, C_RS_DTR)) + SET(sc->sc_chanctl_rs_control, C_RS_RTS); + else + CLR(sc->sc_chanctl_rs_control, C_RS_RTS); + } + + /* Baud rate. */ + sc->sc_chanctl_comm_baud = ospeed; + + /* Copy to tty. */ + tp->t_ispeed = 0; + tp->t_ospeed = t->c_ospeed; + tp->t_cflag = t->c_cflag; + + /* + * Now load the channel control structure. + */ + + cz_wait_pci_doorbell(cz, "czparam"); + + CZTTY_CHAN_WRITE(sc, CHNCTL_COMM_BAUD, sc->sc_chanctl_comm_baud); + CZTTY_CHAN_WRITE(sc, CHNCTL_COMM_DATA_L, sc->sc_chanctl_comm_data_l); + CZTTY_CHAN_WRITE(sc, CHNCTL_COMM_PARITY, sc->sc_chanctl_comm_parity); + CZTTY_CHAN_WRITE(sc, CHNCTL_HW_FLOW, sc->sc_chanctl_hw_flow); + CZTTY_CHAN_WRITE(sc, CHNCTL_RS_CONTROL, sc->sc_chanctl_rs_control); + + CZ_FWCTL_WRITE(cz, BRDCTL_HCMD_CHANNEL, sc->sc_channel); + CZ_PLX_WRITE(cz, PLX_PCI_LOCAL_DOORBELL, C_CM_IOCTLW); + + cz_wait_pci_doorbell(cz, "czparam"); + + CZ_FWCTL_WRITE(cz, BRDCTL_HCMD_CHANNEL, sc->sc_channel); + CZ_PLX_WRITE(cz, PLX_PCI_LOCAL_DOORBELL, C_CM_IOCTLM); + + cz_wait_pci_doorbell(cz, "czparam"); + + /* + * Update the tty layer's idea of the carrier bit, in case we changed + * CLOCAL. We don't hang up here; we only do that by explicit + * request. + */ + rs_status = CZTTY_CHAN_READ(sc, CHNCTL_RS_STATUS); + (void) (*linesw[tp->t_line].l_modem)(tp, ISSET(rs_status, C_RS_DCD)); + + return (0); +} + +/* + * czttystart: + * + * Start or restart transmission. + */ +void +czttystart(struct tty *tp) +{ + struct cztty_softc *sc = CZTTY_SOFTC(tp->t_dev); + int s; + + s = spltty(); + if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) + goto out; + + if (tp->t_outq.c_cc <= tp->t_lowat) { + if (ISSET(tp->t_state, TS_ASLEEP)) { + CLR(tp->t_state, TS_ASLEEP); + wakeup(&tp->t_outq); + } + selwakeup(&tp->t_wsel); + if (tp->t_outq.c_cc == 0) + goto out; + } + + cztty_transmit(sc, tp); + out: + splx(s); +} + +/* + * czttystop: + * + * Stop output, e.g., for ^S or output flush. + */ +int +czttystop(struct tty *tp, int flag) +{ + + /* + * XXX We don't do anything here, yet. Mostly, I don't know + * XXX exactly how this should be implemented on this device. + * XXX We've given a big chunk of data to the MIPS already, + * XXX and I don't know how we request the MIPS to stop sending + * XXX the data. So, punt for now. --thorpej + */ + return (0); +} + +/* + * cztty_diag: + * + * Issue a scheduled diagnostic message. + */ +void +cztty_diag(void *arg) +{ + struct cztty_softc *sc = arg; + struct cz_softc *cz = CZTTY_CZ(sc); + u_int overflows, parity_errors, framing_errors; + int s; + + s = spltty(); + + overflows = sc->sc_overflows; + sc->sc_overflows = 0; + + parity_errors = sc->sc_parity_errors; + sc->sc_parity_errors = 0; + + framing_errors = sc->sc_framing_errors; + sc->sc_framing_errors = 0; + + sc->sc_errors = 0; + + splx(s); + + log(LOG_WARNING, + "%s: channel %d: %u overflow%s, %u parity, %u framing error%s\n", + cz->cz_dev.dv_xname, sc->sc_channel, + overflows, overflows == 1 ? "" : "s", + parity_errors, + framing_errors, framing_errors == 1 ? "" : "s"); +} + +/* + * tx and rx ring buffer size macros: + * + * The transmitter and receiver both use ring buffers. For each one, there + * is a get (consumer) and a put (producer) offset. The get value is the + * next byte to be read from the ring, and the put is the next one to be + * put into the ring. get == put means the ring is empty. + * + * For each ring, the firmware controls one of (get, put) and this driver + * controls the other. For transmission, this driver updates put to point + * past the valid data, and the firmware moves get as bytes are sent. Likewise + * for receive, the driver controls put, and this driver controls get. + */ +#define TX_MOVEABLE(g, p, s) (((g) > (p)) ? ((g) - (p) - 1) : ((s) - (p))) +#define RX_MOVEABLE(g, p, s) (((g) > (p)) ? ((s) - (g)) : ((p) - (g))) + +/* + * cztty_transmit() + * + * Look at the tty for this port and start sending. + */ +int +cztty_transmit(struct cztty_softc *sc, struct tty *tp) +{ + struct cz_softc *cz = CZTTY_CZ(sc); + u_int move, get, put, size, address; +#ifdef HOSTRAMCODE + int error, done = 0; +#else + int done = 0; +#endif + + size = CZTTY_BUF_READ(sc, BUFCTL_TX_BUFSIZE); + get = CZTTY_BUF_READ(sc, BUFCTL_TX_GET); + put = CZTTY_BUF_READ(sc, BUFCTL_TX_PUT); + address = CZTTY_BUF_READ(sc, BUFCTL_TX_BUFADDR); + + while ((tp->t_outq.c_cc > 0) && ((move = TX_MOVEABLE(get, put, size)))){ +#ifdef HOSTRAMCODE + if (0) { + move = min(tp->t_outq.c_cc, move); + error = q_to_b(&tp->t_outq, 0, move); + if (error != move) { + printf("%s: channel %d: error moving to " + "transmit buf\n", cz->cz_dev.dv_xname, + sc->sc_channel); + move = error; + } + } else { +#endif + move = min(ndqb(&tp->t_outq, 0), move); + bus_space_write_region_1(cz->cz_win_st, cz->cz_win_sh, + address + put, tp->t_outq.c_cf, move); + ndflush(&tp->t_outq, move); +#ifdef HOSTRAMCODE + } +#endif + + put = ((put + move) % size); + done = 1; + } + if (done) { + CZTTY_BUF_WRITE(sc, BUFCTL_TX_PUT, put); + } + return (done); +} + +int +cztty_receive(struct cztty_softc *sc, struct tty *tp) +{ + struct cz_softc *cz = CZTTY_CZ(sc); + u_int get, put, size, address; + int done = 0, ch; + + size = CZTTY_BUF_READ(sc, BUFCTL_RX_BUFSIZE); + get = CZTTY_BUF_READ(sc, BUFCTL_RX_GET); + put = CZTTY_BUF_READ(sc, BUFCTL_RX_PUT); + address = CZTTY_BUF_READ(sc, BUFCTL_RX_BUFADDR); + + while ((get != put) && ((tp->t_canq.c_cc + tp->t_rawq.c_cc) < tp->t_hiwat)) { +#ifdef HOSTRAMCODE + if (hostram) + ch = ((char *)fifoaddr)[get]; + } else { +#endif + ch = bus_space_read_1(cz->cz_win_st, cz->cz_win_sh, + address + get); +#ifdef HOSTRAMCODE + } +#endif + (*linesw[tp->t_line].l_rint)(ch, tp); + get = (get + 1) % size; + done = 1; + } + if (done) { + CZTTY_BUF_WRITE(sc, BUFCTL_RX_GET, get); + } + return (done); +} diff --git a/sys/dev/pci/czreg.h b/sys/dev/pci/czreg.h new file mode 100644 index 00000000000..4567d567b90 --- /dev/null +++ b/sys/dev/pci/czreg.h @@ -0,0 +1,419 @@ +/* $OpenBSD: czreg.h,v 1.1 2001/06/21 13:21:50 nate Exp $ */ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2000 Zembu Labs, Inc. + * All rights reserved. + * + * Author: Jason R. Thorpe <thorpej@zembu.com> + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Zembu Labs, Inc. + * 4. Neither the name of Zembu Labs nor the names of its employees may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR- + * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS- + * CLAIMED. IN NO EVENT SHALL ZEMBU LABS 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. + */ + +/* + * Register and firmware communication definitions for the Cyclades + * Z series of multi-port serial adapters. + */ + +/* + * The Cyclades-Z series is an intelligent multi-port serial controller + * comprised of: + * + * - PLX PCI9060ES PCI bus interface + * - Xilinx XC5204 FPGA + * - IDT R3052 MIPS CPU + * + * Communication is performed by modifying structures in board local + * RAM or in host RAM. We define offsets into these structures so + * that either access method may be used. + * + * The Cyclades-Z comes in three basic flavors: + * + * - Cyclades-8Zo rev 1 -- This is an older 8-port board with no + * FPGA. + * + * - Cyclades-8Zo rev 2 -- This is the newer 8-port board, which + * uses an octopus cable. + * + * - Cyclades-Ze -- This is the top-of-the-line of the Cyclades + * multiport serial controllers. It uses a SCSI-2 cable to + * connect the card to a rack-mountable serial expansion box + * (1U high). Each box has 16 RJ45 serial ports, and up to + * 4 boxes can be chained together, for a total of 64 ports. + * Up to 2 boxes can be used without an extra power supply. + * Boxes 3 and 4 require their own external power supply, + * otherwise the firmware will refuse to start (as it cannot + * communicate with the UARTs in the boxes). + * + * The 8Zo flavors have not been tested, tho the programming interface + * is identical (except for the firmware load phase of the 8Zo rev 1; + * no FPGA load is done in that case), so they should work. + */ + +/* + * PLX Local Address Base values for the board RAM and FPGA regsiters. + * + * These values are specific to the Cyclades-Z. + */ +#define LOCAL_ADDR0_RAM (0x00000000 | LASBA_ENABLE) +#define LOCAL_ADDR0_FPGA (0x14000000 | LASBA_ENABLE) + +/* + * PLX Mailbox0 values. + * + * These values are specific to the Cyclades-Z. + */ +#define MAILBOX0_8Zo_V1 0 /* Cyclades-8Zo ver. 1 */ +#define MAILBOX0_8Zo_V2 1 /* Cyclades-8Zo ver. 2 */ +#define MAILBOX0_Ze_V1 2 /* Cyclades-Ze ver. 1 */ + +/* + * Bits in the PLX INIT_CTRL register. + * + * These values are specific to the Cyclades-Z. + */ +#define CONTROL_FPGA_LOADED CONTROL_GPI + +/* + * FPGA registers on the 8Zo boards. + */ +#define FPGA_ID 0x00 /* FPGA ID */ +#define FPGA_VERSION 0x04 /* FPGA version */ +#define FPGA_CPU_START 0x08 /* CPU start */ +#define FPGA_CPU_STOP 0x0c /* CPU stop */ +#define FPGA_MISC 0x10 /* Misc. register */ +#define FPGA_IDT_MODE 0x14 /* IDT MIPS R3000 mode */ +#define FPGA_UART_IRQ_STAT 0x18 /* UART interrupt status */ +#define FPGA_CLEAR_TIMER0_IRQ 0x1c /* clear timer 0 interrupt */ +#define FPGA_CLEAR_TIMER1_IRQ 0x20 /* clear timer 1 interrupt */ +#define FPGA_CLEAR_TIMER2_IRQ 0x24 /* clear timer 3 interrupt */ +#define FPGA_TEST 0x28 /* test register */ +#define FPGA_TEST_COUNT 0x2c /* test count register */ +#define FPGA_TIMER_SELECT 0x30 /* timer select */ +#define FPGA_PR_UART_IRQ_STAT 0x34 /* prioritized UART interrupt status */ +#define FPGA_RAM_WAIT_STATE 0x38 /* RAM wait state */ +#define FPGA_UART_WAIT_STATE 0x3c /* UART wait state */ +#define FPGA_TIMER_WAIT_STATE 0x40 /* timer wait state */ +#define FPGA_ACK_WAIT_STATE 0x44 /* ACK wait state */ + +/* + * FPGA registers on the Ze boards. Note that the important registers + * (FPGA_ID, FPGA_VERSION, FPGA_CPU_START, FPGA_CPU_STOP) are all in the + * same place as on the 8Zo boards, and have the same meanings. + */ +#define FPGA_ZE_ID 0x00 /* FPGA ID */ +#define FPGA_ZE_VERSION 0x04 /* FPGA version */ +#define FPGA_ZE_CPU_START 0x08 /* CPU start */ +#define FPGA_ZE_CPU_STOP 0x0c /* CPU stop */ +#define FPGA_ZE_CTRL 0x10 /* CPU control */ +#define FPGA_ZE_ZBUS_WAIT 0x14 /* Z-Bus wait state */ +#define FPGA_ZE_TIMER_DIV 0x18 /* timer divisor */ +#define FPGA_ZE_TIMER_IRQ_ACK 0x1c /* timer interrupt ACK */ + +/* + * Values for FPGA ID. + */ +#define FPGA_ID_8Zo_V1 0x95 /* Cyclades-8Zo ver. 1 */ +#define FPGA_ID_8Zo_V2 0x84 /* Cyclades-8Zo ver. 2 */ +#define FPGA_ID_Ze_V1 0x89 /* Cyclades-Ze ver. 1 */ + +/* + * Values for Cyclades-Ze timer divisor. + */ +#define ZE_TIMER_DIV_1M 0x00 +#define ZE_TIMER_DIV_256K 0x01 +#define ZE_TIMER_DIV_128K 0x02 +#define ZE_TIMER_DIV_32K 0x03 + +/* + * Firmware interface starts here. + * + * These values are valid for the following Cyclades-Z firmware: + * + * @(#) Copyright (c) Cyclades Corporation, 1996, 1999 + * @(#) ZFIRM Cyclades-Z/PCI Firmware V_3.3.1 09/24/99 + */ + +/* + * Structure of the firmware header. + */ +#define ZFIRM_MAX_BLOCKS 16 /* max. # of firmware/FPGA blocks */ +struct zfirm_header { + u_int8_t zfh_name[64]; + u_int8_t zfh_date[32]; + u_int8_t zfh_aux[32]; + u_int32_t zfh_nconfig; + u_int32_t zfh_configoff; + u_int32_t zfh_nblocks; + u_int32_t zfh_blockoff; + u_int32_t zfh_reserved[9]; +} __attribute__((__packed__)); + +struct zfirm_config { + u_int8_t zfc_name[64]; + u_int32_t zfc_mailbox; + u_int32_t zfc_function; + u_int32_t zfc_nblocks; + u_int32_t zfc_blocklist[ZFIRM_MAX_BLOCKS]; +} __attribute__((__packed__)); + +#define ZFC_FUNCTION_NORMAL 0 /* normal operation */ +#define ZFC_FUNCTION_TEST 1 /* test mode operation */ + +struct zfirm_block { + u_int32_t zfb_type; + u_int32_t zfb_fileoff; + u_int32_t zfb_ramoff; + u_int32_t zfb_size; +} __attribute__((__packed__)); + +#define ZFB_TYPE_FIRMWARE 0 /* MIPS firmware */ +#define ZFB_TYPE_FPGA 1 /* FPGA code */ + +#define ZFIRM_MAX_CHANNELS 64 /* max. # channels per board */ + +/* + * Firmware ID structure, which the firmware sets up after it boots. + */ +#define ZFIRM_SIG_OFF 0x00000180 /* offset of signature in board RAM */ +#define ZFIRM_CTRLADDR_OFF 0x00000184 /* offset of offset of control + structure */ +#define ZFIRM_SIG 0x5557465A /* ZFIRM signature */ +#define ZFIRM_HLT 0x59505B5C /* Halt due to power problem */ +#define ZFIRM_RST 0x56040674 /* Firmware reset */ + +/* + * The firmware control structures are made up of the following: + * + * BOARD CONTROL (64 bytes) + * CHANNEL CONTROL (96 bytes * ZFIRM_MAX_CHANNELS) + * BUFFER CONTROL (64 bytes * ZFIRM_MAX_CHANNELS) + */ + +#define ZFIRM_BRDCTL_SIZE 64 +#define ZFIRM_CHNCTL_SIZE 96 +#define ZFIRM_BUFCTL_SIZE 64 + +#define ZFIRM_CHNCTL_OFF(chan, reg) \ + (ZFIRM_BRDCTL_SIZE + ((chan) * ZFIRM_CHNCTL_SIZE) + (reg)) +#define ZFIRM_BUFCTL_OFF(chan, reg) \ + (ZFIRM_CHNCTL_OFF(ZFIRM_MAX_CHANNELS, 0) + \ + ((chan) * ZFIRM_BUFCTL_SIZE) + (reg)) + +/* + * Offsets in the BOARD CONTROL structure. + */ + /* static info provided by MIPS */ +#define BRDCTL_NCHANNEL 0x00 /* number of channels */ +#define BRDCTL_FWVERSION 0x04 /* firmware version */ + /* static info provided by driver */ +#define BRDCTL_C_OS 0x08 /* operating system ID */ +#define BRDCTL_DRVERSION 0x0c /* driver version */ + /* board control area */ +#define BRDCTL_INACTIVITY 0x10 /* inactivity control */ + /* host to firmware commands */ +#define BRDCTL_HCMD_CHANNEL 0x14 /* channel number */ +#define BRDCTL_HCMD_PARAM 0x18 /* parameter */ + /* firmware to host commands */ +#define BRDCTL_FWCMD_CHANNEL 0x1c /* channel number */ +#define BRDCTL_FWCMD_PARAM 0x20 /* parameter */ +#define BRDCTL_INT_QUEUE_OFF 0x24 /* offset to INT_QUEUE structure */ + +/* + * Offsets in the CHANNEL CONTROL structure. + */ +#define CHNCTL_OP_MODE 0x00 /* operation mode */ +#define CHNCTL_INTR_ENABLE 0x04 /* interrupt making for UART */ +#define CHNCTL_SW_FLOW 0x08 /* SW flow control */ +#define CHNCTL_FLOW_STATUS 0x0c /* output flow status */ +#define CHNCTL_COMM_BAUD 0x10 /* baud rate -- numerically specified */ +#define CHNCTL_COMM_PARITY 0x14 /* parity */ +#define CHNCTL_COMM_DATA_L 0x18 /* data length/stop */ +#define CHNCTL_COMM_FLAGS 0x1c /* other flags */ +#define CHNCTL_HW_FLOW 0x20 /* HW flow control */ +#define CHNCTL_RS_CONTROL 0x24 /* RS-232 outputs */ +#define CHNCTL_RS_STATUS 0x28 /* RS-232 inputs */ +#define CHNCTL_FLOW_XON 0x2c /* XON character */ +#define CHNCTL_FLOW_XOFF 0x30 /* XOFF character */ +#define CHNCTL_HW_OVERFLOW 0x34 /* HW overflow counter */ +#define CHNCTL_SW_OVERFLOW 0x38 /* SW overflow counter */ +#define CHNCTL_COMM_ERROR 0x3c /* frame/parity error counter */ +#define CHNCTL_ICHAR 0x40 /* special interrupt character */ + +/* + * Offsets in the BUFFER CONTROL structure. + */ +#define BUFCTL_FLAG_DMA 0x00 /* buffers are in Host memory */ +#define BUFCTL_TX_BUFADDR 0x04 /* address of Tx buffer */ +#define BUFCTL_TX_BUFSIZE 0x08 /* size of Tx buffer */ +#define BUFCTL_TX_THRESHOLD 0x0c /* Tx low water mark */ +#define BUFCTL_TX_GET 0x10 /* tail index Tx buf */ +#define BUFCTL_TX_PUT 0x14 /* head index Tx buf */ +#define BUFCTL_RX_BUFADDR 0x18 /* address of Rx buffer */ +#define BUFCTL_RX_BUFSIZE 0x1c /* size of Rx buffer */ +#define BUFCTL_RX_THRESHOLD 0x20 /* Rx high water mark */ +#define BUFCTL_RX_GET 0x24 /* tail index Rx buf */ +#define BUFCTL_RX_PUT 0x28 /* head index Rx buf */ + +/* Values for operating system ID (BOARD CONTROL) */ +#define C_OS_SVR3 0x00000010 /* generic SVR3 */ +#define C_OS_XENIX 0x00000011 /* SCO XENIX */ +#define C_OS_SCO 0x00000012 /* SCO SVR3 */ +#define C_OS_SVR4 0x00000020 /* generic SVR4 */ +#define C_OS_UXWARE 0x00000021 /* UnixWare */ +#define C_OS_LINUX 0x00000030 /* Linux */ +#define C_OS_SOLARIS 0x00000040 /* Solaris */ +#define C_OS_BSD 0x00000050 /* generic BSD */ +#define C_OS_DOS 0x00000070 /* generic DOS */ +#define C_OS_NT 0x00000080 /* Windows NT */ +#define C_OS_OS2 0x00000090 /* IBM OS/2 */ +#define C_OS_MACOS 0x000000a0 /* MacOS */ +#define C_OS_AIX 0x000000b0 /* IBM AIX */ + +/* Values for op_mode (CHANNEL CONTROL) */ +#define C_CH_DISABLE 0x00000000 /* channel is disabled */ +#define C_CH_TXENABLE 0x00000001 /* channel Tx enabled */ +#define C_CH_RXENABLE 0x00000002 /* channel Rx enabled */ +#define C_CH_ENABLE 0x00000003 /* channel Tx/Rx enabled */ +#define C_CH_LOOPBACK 0x00000004 /* Loopback mode */ + +/* Values for comm_parity (CHANNEL CONTROL) */ +#define C_PR_NONE 0x00000000 /* None */ +#define C_PR_ODD 0x00000001 /* Odd */ +#define C_PR_EVEN 0x00000002 /* Even */ +#define C_PR_MARK 0x00000004 /* Mark */ +#define C_PR_SPACE 0x00000008 /* Space */ +#define C_PR_PARITY 0x000000ff +#define C_PR_DISCARD 0x00000100 /* discard char with + frame/parity error */ +#define C_PR_IGNORE 0x00000200 /* ignore frame/par error */ + +/* Values for comm_data_l (CHANNEL CONTROL) */ +#define C_DL_CS5 0x00000001 +#define C_DL_CS6 0x00000002 +#define C_DL_CS7 0x00000004 +#define C_DL_CS8 0x00000008 +#define C_DL_CS 0x0000000f +#define C_DL_1STOP 0x00000010 +#define C_DL_15STOP 0x00000020 +#define C_DL_2STOP 0x00000040 +#define C_DL_STOP 0x000000f0 + +/* Values for intr_enable (CHANNEL CONTROL) */ +#define C_IN_DISABLE 0x00000000 /* zero, disable interrupts */ +#define C_IN_TXBEMPTY 0x00000001 /* tx buffer empty */ +#define C_IN_TXLOWWM 0x00000002 /* tx buffer below LWM */ +#define C_IN_TXFEMPTY 0x00000004 /* tx buffer + FIFO + + shift reg. empty */ +#define C_IN_RXHIWM 0x00000010 /* rx buffer above HWM */ +#define C_IN_RXNNDT 0x00000020 /* rx no new data timeout */ +#define C_IN_MDCD 0x00000100 /* modem DCD change */ +#define C_IN_MDSR 0x00000200 /* modem DSR change */ +#define C_IN_MRI 0x00000400 /* modem RI change */ +#define C_IN_MCTS 0x00000800 /* modem CTS change */ +#define C_IN_RXBRK 0x00001000 /* Break received */ +#define C_IN_PR_ERROR 0x00002000 /* parity error */ +#define C_IN_FR_ERROR 0x00004000 /* frame error */ +#define C_IN_OVR_ERROR 0x00008000 /* overrun error */ +#define C_IN_RXOFL 0x00010000 /* RX buffer overflow */ +#define C_IN_IOCTLW 0x00020000 /* I/O control w/ wait */ +#define C_IN_MRTS 0x00040000 /* modem RTS drop */ +#define C_IN_ICHAR 0x00080000 /* special intr. char + received */ + +/* Values for flow control (CHANNEL CONTROL) */ +#define C_FL_OXX 0x00000001 /* output Xon/Xoff flow + control */ +#define C_FL_IXX 0x00000002 /* input Xon/Xoff flow + control */ +#define C_FL_OIXANY 0x00000004 /* output Xon/Xoff (any xon) */ +#define C_FL_SWFLOW 0x0000000f + +/* Values for flow status (CHANNEL CONTROL) */ +#define C_FS_TXIDLE 0x00000000 /* no Tx data in the buffer + or UART */ +#define C_FS_SENDING 0x00000001 /* UART is sending data */ +#define C_FS_SWFLOW 0x00000002 /* Tx is stopped by received + Xoff */ + +/* Values for RS-232 signals (CHANNEL CONTROL) */ +#define C_RS_PARAM 0x80000000 /* indicates presence of + parameter in IOCTL command */ +#define C_RS_RTS 0x00000001 /* RTS */ +#define C_RS_DTR 0x00000004 /* DTR */ +#define C_RS_DCD 0x00000100 /* CD */ +#define C_RS_DSR 0x00000200 /* DSR */ +#define C_RS_RI 0x00000400 /* RI */ +#define C_RS_CTS 0x00000800 /* CTS */ + +/* Commands Host <--> Board */ +#define C_CM_RESET 0x01 /* resets/flushes buffers */ +#define C_CM_IOCTL 0x02 /* re-reads CH_CTRL */ +#define C_CM_IOCTLW 0x03 /* re-reads CH_CTRL, intr when done */ +#define C_CM_IOCTLM 0x04 /* RS-232 outputs change */ +#define C_CM_SENDXOFF 0x10 /* sends Xoff */ +#define C_CM_SENDXON 0x11 /* sends Xon */ +#define C_CM_CLFLOW 0x12 /* Clears flow control (resume) */ +#define C_CM_SENDBRK 0x41 /* sends break */ +#define C_CM_INTBACK 0x42 /* Interrupt back */ +#define C_CM_SET_BREAK 0x43 /* Tx break on */ +#define C_CM_CLR_BREAK 0x44 /* Tx break off */ +#define C_CM_CMD_DONE 0x45 /* Previous command done */ +#define C_CM_INTBACK2 0x46 /* Alternate Interrupt back */ +#define C_CM_TINACT 0x51 /* sets inactivity detection */ +#define C_CM_IRQ_ENBL 0x52 /* enables generation of interrupts */ +#define C_CM_IRQ_DSBL 0x53 /* disables generation of interrupts */ +#define C_CM_ACK_ENBL 0x54 /* enables acknolowdged interrupt + mode */ +#define C_CM_ACK_DSBL 0x55 /* disables acknolowdged intr mode */ +#define C_CM_FLUSH_RX 0x56 /* flushes Rx buffer */ +#define C_CM_FLUSH_TX 0x57 /* flushes Tx buffer */ +#define C_CM_Q_ENABLE 0x58 /* enables queue access from the + driver */ +#define C_CM_Q_DISABLE 0x59 /* disables queue access from the + driver */ +#define C_CM_TXBEMPTY 0x60 /* Tx buffer is empty */ +#define C_CM_TXLOWWM 0x61 /* Tx buffer low water mark */ +#define C_CM_RXHIWM 0x62 /* Rx buffer high water mark */ +#define C_CM_RXNNDT 0x63 /* rx no new data timeout */ +#define C_CM_TXFEMPTY 0x64 /* Tx buffer, FIFO and shift reg. + are empty */ +#define C_CM_ICHAR 0x65 /* Special Interrupt Character + received */ +#define C_CM_MDCD 0x70 /* modem DCD change */ +#define C_CM_MDSR 0x71 /* modem DSR change */ +#define C_CM_MRI 0x72 /* modem RI change */ +#define C_CM_MCTS 0x73 /* modem CTS change */ +#define C_CM_MRTS 0x74 /* modem RTS drop */ +#define C_CM_RXBRK 0x84 /* Break received */ +#define C_CM_PR_ERROR 0x85 /* Parity error */ +#define C_CM_FR_ERROR 0x86 /* Frame error */ +#define C_CM_OVR_ERROR 0x87 /* Overrun error */ +#define C_CM_RXOFL 0x88 /* RX buffer overflow */ +#define C_CM_CMDERROR 0x90 /* command error */ +#define C_CM_FATAL 0x91 /* fatal error */ +#define C_CM_HW_RESET 0x92 /* reset board */ diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 582dd9001a7..a43626a7b40 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.107 2001/06/08 02:26:13 nate Exp $ +# $OpenBSD: files.pci,v 1.108 2001/06/21 13:21:49 nate Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -374,3 +374,8 @@ file dev/pci/pcscp.c pcscp device nge: ether, ifnet, mii, ifmedia, mii_phy attach nge at pci file dev/pci/if_nge.c nge + +# Cyclades-Z series of intelligent multi-port serial adapters +device cz +attach cz at pci +file dev/pci/cz.c cz needs-flag diff --git a/sys/dev/pci/plx9060reg.h b/sys/dev/pci/plx9060reg.h new file mode 100644 index 00000000000..031893a42de --- /dev/null +++ b/sys/dev/pci/plx9060reg.h @@ -0,0 +1,196 @@ +/* $OpenBSD: plx9060reg.h,v 1.1 2001/06/21 13:21:50 nate Exp $ */ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2000 Zembu Labs, Inc. + * All rights reserved. + * + * Author: Jason R. Thorpe <thorpej@zembu.com> + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Zembu Labs, Inc. + * 4. Neither the name of Zembu Labs nor the names of its employees may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR- + * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS- + * CLAIMED. IN NO EVENT SHALL ZEMBU LABS 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. + */ + +/* + * Register description for the PLX 9060-family of PCI bus + * controllers. + * + * In order for this file to be really useful to you, you'll want to + * have the PLX 9060 datasheet in front of you. + */ + +#ifndef _DEV_PCI_PLX9060REG_H_ +#define _DEV_PCI_PLX9060REG_H_ + +/* + * PLX 9060 PCI configuration space registers. + */ + +#define PLX_PCI_RUNTIME_MEMADDR 0x10 /* memory mapped 9060 */ +#define PLX_PCI_RUNTIME_IOADDR 0x14 /* i/o mapped 9060 */ +#define PLX_PCI_LOCAL_ADDR0 0x18 /* PCI address of 9060 local bus */ + +/* + * PLX 9060 Runtime registers, in PCI space. + */ + +/* Local Address Space 0 Range Register */ +#define PLX_LAS0RR 0x00 +#define LASRR_IO 0x00000001 +#define LASRR_MEM_1M 0x00000002 +#define LASRR_MEM_64BIT 0x00000004 +#define LASRR_MEM_PREFETCH 0x00000008 +#define LASRR_MEM_MASK 0xfffffff0 +#define LASRR_IO_MASK 0xfffffffc + + +/* Local Address Space 0 Local Base Address (remap) Register */ +#define PLX_LAS0BA 0x04 +#define LASBA_ENABLE 0x00000001 +#define LASBA_MEM_MASK 0xfffffff0 +#define LASBA_IO_MASK 0xfffffffc + + +/* Local Arbitration Register */ +#define PLX_LAR 0x08 +#define LAR_LATTMR 0x000000ff +#define LAR_PAUSETMR 0x0000ff00 +#define LAR_LATTMR_EN 0x00010000 +#define LAR_BREQ_EN 0x00040000 +#define LAR_DSGIVEUP 0x00200000 +#define LAR_DSLOCK_EN 0x00400000 +#define LAR_PCI21_MODE 0x01000000 + + +/* Big/Little Endian Register */ +#define PLX_ENDIAN 0x0c +#define ENDIAN_CRBE 0x00000001 +#define ENDIAN_DMBE 0x00000002 +#define ENDIAN_DSAS0BE 0x00000004 +#define ENDIAN_DSAERBE 0x00000008 +#define ENDIAN_BEBL 0x00000010 + + +/* Expansion ROM Range Register */ +#define PLX_EROMRR 0x10 +#define EROMRR_MASK 0xffffffc0 + + +/* Expansion ROM Base Address (remap) Register */ +#define PLX_EROMBA 0x14 +#define EROMBA_BREQ_DC 0x0000000f +#define EROMBA_BREQ_EN 0x00000010 +#define EROMBA_MASK 0xffffffc0 + + +/* Local Bus Region Descriptor for PCI to Local Access Register */ +#define PLX_LBRD 0x18 + + +/* Local Range for Direct Master to PCI */ +#define PLX_DMRR 0x1c + + +/* Local Bus Base Address for Direct Master to PCI Memory */ +#define PLX_DMLBAM 0x20 + + +/* Local Bus Base Address for Direct Master to PCI IO/Config */ +#define PLX_DMLBAI 0x24 + + +/* PCI Base Address (remap) for Direct Master to PCI Memory */ +#define PLX_DMBPAM 0x28 + + +/* PCI Base Address (remap) for Direct Master to PCI IO/Config */ +#define PLX_DMPBAI 0x2c + + +#define PLX_MAILBOX0 0x40 /* Mailbox register 0 */ +#define PLX_MAILBOX1 0x44 /* Mailbox register 1 */ +#define PLX_MAILBOX2 0x48 /* Mailbox register 2 */ +#define PLX_MAILBOX3 0x4c /* Mailbox register 3 */ +#define PLX_MAILBOX4 0x50 /* Mailbox register 4 (not 9060ES) */ +#define PLX_MAILBOX5 0x54 /* Mailbox register 5 (not 9060ES) */ +#define PLX_MAILBOX6 0x58 /* Mailbox register 6 (not 9060ES) */ +#define PLX_MAILBOX7 0x5c /* Mailbox register 7 (not 9060ES) */ + + +#define PLX_PCI_LOCAL_DOORBELL 0x60 /* PCI -> local doorbell */ +#define PLX_LOCAL_PCI_DOORBELL 0x64 /* local -> PCI doorbell */ + + +/* Interrupt Control/Status */ +#define PLX_INTCSR 0x68 +#define INTCSR_LSERR_TAMA 0x00000001 +#define INTCSR_LSERR_PA 0x00000002 +#define INTCSR_SERR 0x00000004 +#define INTCSR_PCI_EN 0x00000100 +#define INTCSR_PCIDB_EN 0x00000200 +#define INTCSR_PCIAB_EN 0x00000400 +#define INTCSR_PCILOC_EN 0x00000800 +#define INTCSR_RETRYAB_EN 0x00001000 +#define INTCSR_PCIDB_INT 0x00002000 +#define INTCSR_PCIAB_INT 0x00004000 +#define INTCSR_PCILOC_INT 0x00008000 +#define INTCSR_LOCOE_EN 0x00010000 +#define INTCSR_LOCDB_EN 0x00020000 +#define INTCSR_LOCDB_INT 0x00100000 +#define INTCSR_BIST_INT 0x00800000 +#define INTCSR_DMAB_INT 0x01000000 +#define INTCSR_RETRYAB_INT 0x08000000 + + +/* EEPROM Control, PCI Command Codes, User I/O Control, Init Control */ +#define PLX_CONTROL 0x6c +#define CONTROL_PCIMRC 0x00000f00 +#define CONTROL_PCIMRC_SHIFT 8 +#define CONTROL_PCIMWC 0x0000f000 +#define CONTORL_PCIMWC_SHIFT 12 +#define CONTROL_GPO 0x00010000 +#define CONTROL_GPI 0x00020000 +#define CONTROL_EESK 0x01000000 +#define CONTROL_EECS 0x02000000 +#define CONTROL_EEDO 0x04000000 /* PLX -> EEPROM */ +#define CONTROL_EEDI 0x08000000 /* EEPROM -> PLX */ +#define CONTROL_EEPRESENT 0x10000000 +#define CONTROL_RELOADCFG 0x20000000 +#define CONTROL_SWR 0x40000000 +#define CONTROL_LOCALINIT 0x80000000 + + /* EEPROM opcodes */ +#define PLX_EEPROM_OPC_READ(x) (0x0080 | ((x) & 0x3f)) +#define PLX_EEPROM_OPC_WRITE(x) (0x0040 | ((x) & 0x3f)) +#define PLX_EEPROM_OPC_WREN 0x0030 +#define PLX_EEPROM_OPC_WRPR 0x0000 +#define PLX_EEPROM_COMMAND(y) (((y) & 0xff) | 0x100) + + +/* PCI Configuration ID */ +#define PLX_IDREG 0x70 + +#endif /* _DEV_PCI_PLX9060REG_H_ */ diff --git a/sys/dev/pci/plx9060var.h b/sys/dev/pci/plx9060var.h new file mode 100644 index 00000000000..d8dfc952456 --- /dev/null +++ b/sys/dev/pci/plx9060var.h @@ -0,0 +1,57 @@ +/* $OpenBSD: plx9060var.h,v 1.1 2001/06/21 13:21:50 nate Exp $ */ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2000 Zembu Labs, Inc. + * All rights reserved. + * + * Author: Jason R. Thorpe <thorpej@zembu.com> + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Zembu Labs, Inc. + * 4. Neither the name of Zembu Labs nor the names of its employees may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR- + * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS- + * CLAIMED. IN NO EVENT SHALL ZEMBU LABS 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. + */ + +/* + * API definition for the PLX 9060-family of PCI bus controllers. + */ + +#ifndef _DEV_PCI_PLX9060VAR_H_ +#define _DEV_PCI_PLX9060VAR_H_ + +struct plx9060_config { + bus_space_tag_t plx_st; /* bus space tag for runtime regs */ + bus_space_handle_t plx_sh; /* bus space handle for runtime regs */ + pci_chipset_tag_t plx_pc; /* PCI chipset we're on */ + pcitag_t plx_tag; /* PCI tag for our b/d/f */ +}; + +#ifdef notyet +void plx9060_read_eeprom __P((struct plx9060_config *, int, int, void *)); +void plx9060_write_eeprom __P((struct plx9060_config *, int, int, + const void *)); +#endif + +#endif /* _DEV_PCI_PLX9060VAR_H_ */ |