stallion.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:137k
- ioaddr += EREG_BANKSIZE;
- brdp->nrports += panelp->nrports;
- brdp->panels[panelnr++] = panelp;
- if ((brdp->brdtype != BRD_ECHPCI) &&
- (ioaddr >= (brdp->ioaddr2 + brdp->iosize2)))
- break;
- }
- brdp->nrpanels = panelnr;
- brdp->nrbnks = banknr;
- if (brdp->brdtype == BRD_ECH)
- outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
- brdp->state |= BRD_FOUND;
- i = stl_mapirq(brdp->irq, name);
- return(i);
- }
- /*****************************************************************************/
- /*
- * Initialize and configure the specified board.
- * Scan through all the boards in the configuration and see what we
- * can find. Handle EIO and the ECH boards a little differently here
- * since the initial search and setup is very different.
- */
- static int __init stl_brdinit(stlbrd_t *brdp)
- {
- int i;
- #if DEBUG
- printk("stl_brdinit(brdp=%x)n", (int) brdp);
- #endif
- switch (brdp->brdtype) {
- case BRD_EASYIO:
- case BRD_EASYIOPCI:
- stl_initeio(brdp);
- break;
- case BRD_ECH:
- case BRD_ECHMC:
- case BRD_ECHPCI:
- case BRD_ECH64PCI:
- stl_initech(brdp);
- break;
- default:
- printk("STALLION: board=%d is unknown board type=%dn",
- brdp->brdnr, brdp->brdtype);
- return(ENODEV);
- }
- stl_brds[brdp->brdnr] = brdp;
- if ((brdp->state & BRD_FOUND) == 0) {
- printk("STALLION: %s board not found, board=%d io=%x irq=%dn",
- stl_brdnames[brdp->brdtype], brdp->brdnr,
- brdp->ioaddr1, brdp->irq);
- return(ENODEV);
- }
- for (i = 0; (i < STL_MAXPANELS); i++)
- if (brdp->panels[i] != (stlpanel_t *) NULL)
- stl_initports(brdp, brdp->panels[i]);
- printk("STALLION: %s found, board=%d io=%x irq=%d "
- "nrpanels=%d nrports=%dn", stl_brdnames[brdp->brdtype],
- brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels,
- brdp->nrports);
- return(0);
- }
- /*****************************************************************************/
- /*
- * Find the next available board number that is free.
- */
- static inline int stl_getbrdnr()
- {
- int i;
- for (i = 0; (i < STL_MAXBRDS); i++) {
- if (stl_brds[i] == (stlbrd_t *) NULL) {
- if (i >= stl_nrbrds)
- stl_nrbrds = i + 1;
- return(i);
- }
- }
- return(-1);
- }
- /*****************************************************************************/
- #ifdef CONFIG_PCI
- /*
- * We have a Stallion board. Allocate a board structure and
- * initialize it. Read its IO and IRQ resources from PCI
- * configuration space.
- */
- static inline int stl_initpcibrd(int brdtype, struct pci_dev *devp)
- {
- stlbrd_t *brdp;
- #if DEBUG
- printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)n", brdtype,
- devp->bus->number, devp->devfn);
- #endif
- if (pci_enable_device(devp))
- return(-EIO);
- if ((brdp = stl_allocbrd()) == (stlbrd_t *) NULL)
- return(-ENOMEM);
- if ((brdp->brdnr = stl_getbrdnr()) < 0) {
- printk("STALLION: too many boards found, "
- "maximum supported %dn", STL_MAXBRDS);
- return(0);
- }
- brdp->brdtype = brdtype;
- /*
- * Different Stallion boards use the BAR registers in different ways,
- * so set up io addresses based on board type.
- */
- #if DEBUG
- printk("%s(%d): BAR[]=%x,%x,%x,%x IRQ=%xn", __FILE__, __LINE__,
- pci_resource_start(devp, 0), pci_resource_start(devp, 1),
- pci_resource_start(devp, 2), pci_resource_start(devp, 3), devp->irq);
- #endif
- /*
- * We have all resources from the board, so let's setup the actual
- * board structure now.
- */
- switch (brdtype) {
- case BRD_ECHPCI:
- brdp->ioaddr2 = pci_resource_start(devp, 0);
- brdp->ioaddr1 = pci_resource_start(devp, 1);
- break;
- case BRD_ECH64PCI:
- brdp->ioaddr2 = pci_resource_start(devp, 2);
- brdp->ioaddr1 = pci_resource_start(devp, 1);
- break;
- case BRD_EASYIOPCI:
- brdp->ioaddr1 = pci_resource_start(devp, 2);
- brdp->ioaddr2 = pci_resource_start(devp, 1);
- break;
- default:
- printk("STALLION: unknown PCI board type=%dn", brdtype);
- break;
- }
- brdp->irq = devp->irq;
- stl_brdinit(brdp);
- return(0);
- }
- /*****************************************************************************/
- /*
- * Find all Stallion PCI boards that might be installed. Initialize each
- * one as it is found.
- */
- static inline int stl_findpcibrds()
- {
- struct pci_dev *dev = NULL;
- int i, rc;
- #if DEBUG
- printk("stl_findpcibrds()n");
- #endif
- if (! pci_present())
- return(0);
- for (i = 0; (i < stl_nrpcibrds); i++)
- while ((dev = pci_find_device(stl_pcibrds[i].vendid,
- stl_pcibrds[i].devid, dev))) {
- /*
- * Found a device on the PCI bus that has our vendor and
- * device ID. Need to check now that it is really us.
- */
- if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE)
- continue;
- rc = stl_initpcibrd(stl_pcibrds[i].brdtype, dev);
- if (rc)
- return(rc);
- }
- return(0);
- }
- #endif
- /*****************************************************************************/
- /*
- * Scan through all the boards in the configuration and see what we
- * can find. Handle EIO and the ECH boards a little differently here
- * since the initial search and setup is too different.
- */
- static inline int stl_initbrds()
- {
- stlbrd_t *brdp;
- stlconf_t *confp;
- int i;
- #if DEBUG
- printk("stl_initbrds()n");
- #endif
- if (stl_nrbrds > STL_MAXBRDS) {
- printk("STALLION: too many boards in configuration table, "
- "truncating to %dn", STL_MAXBRDS);
- stl_nrbrds = STL_MAXBRDS;
- }
- /*
- * Firstly scan the list of static boards configured. Allocate
- * resources and initialize the boards as found.
- */
- for (i = 0; (i < stl_nrbrds); i++) {
- confp = &stl_brdconf[i];
- #ifdef MODULE
- stl_parsebrd(confp, stl_brdsp[i]);
- #endif
- if ((brdp = stl_allocbrd()) == (stlbrd_t *) NULL)
- return(-ENOMEM);
- brdp->brdnr = i;
- brdp->brdtype = confp->brdtype;
- brdp->ioaddr1 = confp->ioaddr1;
- brdp->ioaddr2 = confp->ioaddr2;
- brdp->irq = confp->irq;
- brdp->irqtype = confp->irqtype;
- stl_brdinit(brdp);
- }
- /*
- * Find any dynamically supported boards. That is via module load
- * line options or auto-detected on the PCI bus.
- */
- #ifdef MODULE
- stl_argbrds();
- #endif
- #ifdef CONFIG_PCI
- stl_findpcibrds();
- #endif
- return(0);
- }
- /*****************************************************************************/
- /*
- * Return the board stats structure to user app.
- */
- static int stl_getbrdstats(combrd_t *bp)
- {
- stlbrd_t *brdp;
- stlpanel_t *panelp;
- int i;
- copy_from_user(&stl_brdstats, bp, sizeof(combrd_t));
- if (stl_brdstats.brd >= STL_MAXBRDS)
- return(-ENODEV);
- brdp = stl_brds[stl_brdstats.brd];
- if (brdp == (stlbrd_t *) NULL)
- return(-ENODEV);
- memset(&stl_brdstats, 0, sizeof(combrd_t));
- stl_brdstats.brd = brdp->brdnr;
- stl_brdstats.type = brdp->brdtype;
- stl_brdstats.hwid = brdp->hwid;
- stl_brdstats.state = brdp->state;
- stl_brdstats.ioaddr = brdp->ioaddr1;
- stl_brdstats.ioaddr2 = brdp->ioaddr2;
- stl_brdstats.irq = brdp->irq;
- stl_brdstats.nrpanels = brdp->nrpanels;
- stl_brdstats.nrports = brdp->nrports;
- for (i = 0; (i < brdp->nrpanels); i++) {
- panelp = brdp->panels[i];
- stl_brdstats.panels[i].panel = i;
- stl_brdstats.panels[i].hwid = panelp->hwid;
- stl_brdstats.panels[i].nrports = panelp->nrports;
- }
- copy_to_user(bp, &stl_brdstats, sizeof(combrd_t));
- return(0);
- }
- /*****************************************************************************/
- /*
- * Resolve the referenced port number into a port struct pointer.
- */
- static stlport_t *stl_getport(int brdnr, int panelnr, int portnr)
- {
- stlbrd_t *brdp;
- stlpanel_t *panelp;
- if ((brdnr < 0) || (brdnr >= STL_MAXBRDS))
- return((stlport_t *) NULL);
- brdp = stl_brds[brdnr];
- if (brdp == (stlbrd_t *) NULL)
- return((stlport_t *) NULL);
- if ((panelnr < 0) || (panelnr >= brdp->nrpanels))
- return((stlport_t *) NULL);
- panelp = brdp->panels[panelnr];
- if (panelp == (stlpanel_t *) NULL)
- return((stlport_t *) NULL);
- if ((portnr < 0) || (portnr >= panelp->nrports))
- return((stlport_t *) NULL);
- return(panelp->ports[portnr]);
- }
- /*****************************************************************************/
- /*
- * Return the port stats structure to user app. A NULL port struct
- * pointer passed in means that we need to find out from the app
- * what port to get stats for (used through board control device).
- */
- static int stl_getportstats(stlport_t *portp, comstats_t *cp)
- {
- unsigned char *head, *tail;
- unsigned long flags;
- if (portp == (stlport_t *) NULL) {
- copy_from_user(&stl_comstats, cp, sizeof(comstats_t));
- portp = stl_getport(stl_comstats.brd, stl_comstats.panel,
- stl_comstats.port);
- if (portp == (stlport_t *) NULL)
- return(-ENODEV);
- }
- portp->stats.state = portp->istate;
- portp->stats.flags = portp->flags;
- portp->stats.hwid = portp->hwid;
- portp->stats.ttystate = 0;
- portp->stats.cflags = 0;
- portp->stats.iflags = 0;
- portp->stats.oflags = 0;
- portp->stats.lflags = 0;
- portp->stats.rxbuffered = 0;
- save_flags(flags);
- cli();
- if (portp->tty != (struct tty_struct *) NULL) {
- if (portp->tty->driver_data == portp) {
- portp->stats.ttystate = portp->tty->flags;
- portp->stats.rxbuffered = portp->tty->flip.count;
- if (portp->tty->termios != (struct termios *) NULL) {
- portp->stats.cflags = portp->tty->termios->c_cflag;
- portp->stats.iflags = portp->tty->termios->c_iflag;
- portp->stats.oflags = portp->tty->termios->c_oflag;
- portp->stats.lflags = portp->tty->termios->c_lflag;
- }
- }
- }
- restore_flags(flags);
- head = portp->tx.head;
- tail = portp->tx.tail;
- portp->stats.txbuffered = ((head >= tail) ? (head - tail) :
- (STL_TXBUFSIZE - (tail - head)));
- portp->stats.signals = (unsigned long) stl_getsignals(portp);
- copy_to_user(cp, &portp->stats, sizeof(comstats_t));
- return(0);
- }
- /*****************************************************************************/
- /*
- * Clear the port stats structure. We also return it zeroed out...
- */
- static int stl_clrportstats(stlport_t *portp, comstats_t *cp)
- {
- if (portp == (stlport_t *) NULL) {
- copy_from_user(&stl_comstats, cp, sizeof(comstats_t));
- portp = stl_getport(stl_comstats.brd, stl_comstats.panel,
- stl_comstats.port);
- if (portp == (stlport_t *) NULL)
- return(-ENODEV);
- }
- memset(&portp->stats, 0, sizeof(comstats_t));
- portp->stats.brd = portp->brdnr;
- portp->stats.panel = portp->panelnr;
- portp->stats.port = portp->portnr;
- copy_to_user(cp, &portp->stats, sizeof(comstats_t));
- return(0);
- }
- /*****************************************************************************/
- /*
- * Return the entire driver ports structure to a user app.
- */
- static int stl_getportstruct(unsigned long arg)
- {
- stlport_t *portp;
- copy_from_user(&stl_dummyport, (void *) arg, sizeof(stlport_t));
- portp = stl_getport(stl_dummyport.brdnr, stl_dummyport.panelnr,
- stl_dummyport.portnr);
- if (portp == (stlport_t *) NULL)
- return(-ENODEV);
- copy_to_user((void *) arg, portp, sizeof(stlport_t));
- return(0);
- }
- /*****************************************************************************/
- /*
- * Return the entire driver board structure to a user app.
- */
- static int stl_getbrdstruct(unsigned long arg)
- {
- stlbrd_t *brdp;
- copy_from_user(&stl_dummybrd, (void *) arg, sizeof(stlbrd_t));
- if ((stl_dummybrd.brdnr < 0) || (stl_dummybrd.brdnr >= STL_MAXBRDS))
- return(-ENODEV);
- brdp = stl_brds[stl_dummybrd.brdnr];
- if (brdp == (stlbrd_t *) NULL)
- return(-ENODEV);
- copy_to_user((void *) arg, brdp, sizeof(stlbrd_t));
- return(0);
- }
- /*****************************************************************************/
- /*
- * The "staliomem" device is also required to do some special operations
- * on the board and/or ports. In this driver it is mostly used for stats
- * collection.
- */
- static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
- {
- int brdnr, rc;
- #if DEBUG
- printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)n", (int) ip,
- (int) fp, cmd, (int) arg);
- #endif
- brdnr = MINOR(ip->i_rdev);
- if (brdnr >= STL_MAXBRDS)
- return(-ENODEV);
- rc = 0;
- switch (cmd) {
- case COM_GETPORTSTATS:
- if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(comstats_t))) == 0)
- rc = stl_getportstats((stlport_t *) NULL,
- (comstats_t *) arg);
- break;
- case COM_CLRPORTSTATS:
- if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(comstats_t))) == 0)
- rc = stl_clrportstats((stlport_t *) NULL,
- (comstats_t *) arg);
- break;
- case COM_GETBRDSTATS:
- if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(combrd_t))) == 0)
- rc = stl_getbrdstats((combrd_t *) arg);
- break;
- case COM_READPORT:
- if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(stlport_t))) == 0)
- rc = stl_getportstruct(arg);
- break;
- case COM_READBOARD:
- if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(stlbrd_t))) == 0)
- rc = stl_getbrdstruct(arg);
- break;
- default:
- rc = -ENOIOCTLCMD;
- break;
- }
- return(rc);
- }
- /*****************************************************************************/
- int __init stl_init(void)
- {
- printk(KERN_INFO "%s: version %sn", stl_drvtitle, stl_drvversion);
- stl_initbrds();
- /*
- * Allocate a temporary write buffer.
- */
- stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE);
- if (stl_tmpwritebuf == (char *) NULL)
- printk("STALLION: failed to allocate memory (size=%d)n",
- STL_TXBUFSIZE);
- /*
- * Set up a character driver for per board stuff. This is mainly used
- * to do stats ioctls on the ports.
- */
- if (devfs_register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem))
- printk("STALLION: failed to register serial board devicen");
- devfs_handle = devfs_mk_dir (NULL, "staliomem", NULL);
- devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT,
- STL_SIOMEMMAJOR, 0,
- S_IFCHR | S_IRUSR | S_IWUSR,
- &stl_fsiomem, NULL);
- /*
- * Set up the tty driver structure and register us as a driver.
- * Also setup the callout tty device.
- */
- memset(&stl_serial, 0, sizeof(struct tty_driver));
- stl_serial.magic = TTY_DRIVER_MAGIC;
- stl_serial.driver_name = stl_drvname;
- stl_serial.name = stl_serialname;
- stl_serial.major = STL_SERIALMAJOR;
- stl_serial.minor_start = 0;
- stl_serial.num = STL_MAXBRDS * STL_MAXPORTS;
- stl_serial.type = TTY_DRIVER_TYPE_SERIAL;
- stl_serial.subtype = STL_DRVTYPSERIAL;
- stl_serial.init_termios = stl_deftermios;
- stl_serial.flags = TTY_DRIVER_REAL_RAW;
- stl_serial.refcount = &stl_refcount;
- stl_serial.table = stl_ttys;
- stl_serial.termios = stl_termios;
- stl_serial.termios_locked = stl_termioslocked;
-
- stl_serial.open = stl_open;
- stl_serial.close = stl_close;
- stl_serial.write = stl_write;
- stl_serial.put_char = stl_putchar;
- stl_serial.flush_chars = stl_flushchars;
- stl_serial.write_room = stl_writeroom;
- stl_serial.chars_in_buffer = stl_charsinbuffer;
- stl_serial.ioctl = stl_ioctl;
- stl_serial.set_termios = stl_settermios;
- stl_serial.throttle = stl_throttle;
- stl_serial.unthrottle = stl_unthrottle;
- stl_serial.stop = stl_stop;
- stl_serial.start = stl_start;
- stl_serial.hangup = stl_hangup;
- stl_serial.flush_buffer = stl_flushbuffer;
- stl_serial.break_ctl = stl_breakctl;
- stl_serial.wait_until_sent = stl_waituntilsent;
- stl_serial.send_xchar = stl_sendxchar;
- stl_serial.read_proc = stl_readproc;
- stl_callout = stl_serial;
- stl_callout.name = stl_calloutname;
- stl_callout.major = STL_CALLOUTMAJOR;
- stl_callout.subtype = STL_DRVTYPCALLOUT;
- stl_callout.read_proc = 0;
- if (tty_register_driver(&stl_serial))
- printk("STALLION: failed to register serial drivern");
- if (tty_register_driver(&stl_callout))
- printk("STALLION: failed to register callout drivern");
- return(0);
- }
- /*****************************************************************************/
- /* CD1400 HARDWARE FUNCTIONS */
- /*****************************************************************************/
- /*
- * These functions get/set/update the registers of the cd1400 UARTs.
- * Access to the cd1400 registers is via an address/data io port pair.
- * (Maybe should make this inline...)
- */
- static int stl_cd1400getreg(stlport_t *portp, int regnr)
- {
- outb((regnr + portp->uartaddr), portp->ioaddr);
- return(inb(portp->ioaddr + EREG_DATA));
- }
- static void stl_cd1400setreg(stlport_t *portp, int regnr, int value)
- {
- outb((regnr + portp->uartaddr), portp->ioaddr);
- outb(value, portp->ioaddr + EREG_DATA);
- }
- static int stl_cd1400updatereg(stlport_t *portp, int regnr, int value)
- {
- outb((regnr + portp->uartaddr), portp->ioaddr);
- if (inb(portp->ioaddr + EREG_DATA) != value) {
- outb(value, portp->ioaddr + EREG_DATA);
- return(1);
- }
- return(0);
- }
- /*****************************************************************************/
- /*
- * Inbitialize the UARTs in a panel. We don't care what sort of board
- * these ports are on - since the port io registers are almost
- * identical when dealing with ports.
- */
- static int stl_cd1400panelinit(stlbrd_t *brdp, stlpanel_t *panelp)
- {
- unsigned int gfrcr;
- int chipmask, i, j;
- int nrchips, uartaddr, ioaddr;
- #if DEBUG
- printk("stl_panelinit(brdp=%x,panelp=%x)n", (int) brdp, (int) panelp);
- #endif
- BRDENABLE(panelp->brdnr, panelp->pagenr);
- /*
- * Check that each chip is present and started up OK.
- */
- chipmask = 0;
- nrchips = panelp->nrports / CD1400_PORTS;
- for (i = 0; (i < nrchips); i++) {
- if (brdp->brdtype == BRD_ECHPCI) {
- outb((panelp->pagenr + (i >> 1)), brdp->ioctrl);
- ioaddr = panelp->iobase;
- } else {
- ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1));
- }
- uartaddr = (i & 0x01) ? 0x080 : 0;
- outb((GFRCR + uartaddr), ioaddr);
- outb(0, (ioaddr + EREG_DATA));
- outb((CCR + uartaddr), ioaddr);
- outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
- outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
- outb((GFRCR + uartaddr), ioaddr);
- for (j = 0; (j < CCR_MAXWAIT); j++) {
- if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0)
- break;
- }
- if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) {
- printk("STALLION: cd1400 not responding, "
- "brd=%d panel=%d chip=%dn",
- panelp->brdnr, panelp->panelnr, i);
- continue;
- }
- chipmask |= (0x1 << i);
- outb((PPR + uartaddr), ioaddr);
- outb(PPR_SCALAR, (ioaddr + EREG_DATA));
- }
- BRDDISABLE(panelp->brdnr);
- return(chipmask);
- }
- /*****************************************************************************/
- /*
- * Initialize hardware specific port registers.
- */
- static void stl_cd1400portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp)
- {
- #if DEBUG
- printk("stl_cd1400portinit(brdp=%x,panelp=%x,portp=%x)n",
- (int) brdp, (int) panelp, (int) portp);
- #endif
- if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) ||
- (portp == (stlport_t *) NULL))
- return;
- portp->ioaddr = panelp->iobase + (((brdp->brdtype == BRD_ECHPCI) ||
- (portp->portnr < 8)) ? 0 : EREG_BANKSIZE);
- portp->uartaddr = (portp->portnr & 0x04) << 5;
- portp->pagenr = panelp->pagenr + (portp->portnr >> 3);
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
- stl_cd1400setreg(portp, LIVR, (portp->portnr << 3));
- portp->hwid = stl_cd1400getreg(portp, GFRCR);
- BRDDISABLE(portp->brdnr);
- }
- /*****************************************************************************/
- /*
- * Wait for the command register to be ready. We will poll this,
- * since it won't usually take too long to be ready.
- */
- static void stl_cd1400ccrwait(stlport_t *portp)
- {
- int i;
- for (i = 0; (i < CCR_MAXWAIT); i++) {
- if (stl_cd1400getreg(portp, CCR) == 0) {
- return;
- }
- }
- printk("STALLION: cd1400 not responding, port=%d panel=%d brd=%dn",
- portp->portnr, portp->panelnr, portp->brdnr);
- }
- /*****************************************************************************/
- /*
- * Set up the cd1400 registers for a port based on the termios port
- * settings.
- */
- static void stl_cd1400setport(stlport_t *portp, struct termios *tiosp)
- {
- stlbrd_t *brdp;
- unsigned long flags;
- unsigned int clkdiv, baudrate;
- unsigned char cor1, cor2, cor3;
- unsigned char cor4, cor5, ccr;
- unsigned char srer, sreron, sreroff;
- unsigned char mcor1, mcor2, rtpr;
- unsigned char clk, div;
- cor1 = 0;
- cor2 = 0;
- cor3 = 0;
- cor4 = 0;
- cor5 = 0;
- ccr = 0;
- rtpr = 0;
- clk = 0;
- div = 0;
- mcor1 = 0;
- mcor2 = 0;
- sreron = 0;
- sreroff = 0;
- brdp = stl_brds[portp->brdnr];
- if (brdp == (stlbrd_t *) NULL)
- return;
- /*
- * Set up the RX char ignore mask with those RX error types we
- * can ignore. We can get the cd1400 to help us out a little here,
- * it will ignore parity errors and breaks for us.
- */
- portp->rxignoremsk = 0;
- if (tiosp->c_iflag & IGNPAR) {
- portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN);
- cor1 |= COR1_PARIGNORE;
- }
- if (tiosp->c_iflag & IGNBRK) {
- portp->rxignoremsk |= ST_BREAK;
- cor4 |= COR4_IGNBRK;
- }
- portp->rxmarkmsk = ST_OVERRUN;
- if (tiosp->c_iflag & (INPCK | PARMRK))
- portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING);
- if (tiosp->c_iflag & BRKINT)
- portp->rxmarkmsk |= ST_BREAK;
- /*
- * Go through the char size, parity and stop bits and set all the
- * option register appropriately.
- */
- switch (tiosp->c_cflag & CSIZE) {
- case CS5:
- cor1 |= COR1_CHL5;
- break;
- case CS6:
- cor1 |= COR1_CHL6;
- break;
- case CS7:
- cor1 |= COR1_CHL7;
- break;
- default:
- cor1 |= COR1_CHL8;
- break;
- }
- if (tiosp->c_cflag & CSTOPB)
- cor1 |= COR1_STOP2;
- else
- cor1 |= COR1_STOP1;
- if (tiosp->c_cflag & PARENB) {
- if (tiosp->c_cflag & PARODD)
- cor1 |= (COR1_PARENB | COR1_PARODD);
- else
- cor1 |= (COR1_PARENB | COR1_PAREVEN);
- } else {
- cor1 |= COR1_PARNONE;
- }
- /*
- * Set the RX FIFO threshold at 6 chars. This gives a bit of breathing
- * space for hardware flow control and the like. This should be set to
- * VMIN. Also here we will set the RX data timeout to 10ms - this should
- * really be based on VTIME.
- */
- cor3 |= FIFO_RXTHRESHOLD;
- rtpr = 2;
- /*
- * Calculate the baud rate timers. For now we will just assume that
- * the input and output baud are the same. Could have used a baud
- * table here, but this way we can generate virtually any baud rate
- * we like!
- */
- baudrate = tiosp->c_cflag & CBAUD;
- if (baudrate & CBAUDEX) {
- baudrate &= ~CBAUDEX;
- if ((baudrate < 1) || (baudrate > 4))
- tiosp->c_cflag &= ~CBAUDEX;
- else
- baudrate += 15;
- }
- baudrate = stl_baudrates[baudrate];
- if ((tiosp->c_cflag & CBAUD) == B38400) {
- if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- baudrate = 57600;
- else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- baudrate = 115200;
- else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- baudrate = 230400;
- else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- baudrate = 460800;
- else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
- baudrate = (portp->baud_base / portp->custom_divisor);
- }
- if (baudrate > STL_CD1400MAXBAUD)
- baudrate = STL_CD1400MAXBAUD;
- if (baudrate > 0) {
- for (clk = 0; (clk < CD1400_NUMCLKS); clk++) {
- clkdiv = ((portp->clk / stl_cd1400clkdivs[clk]) / baudrate);
- if (clkdiv < 0x100)
- break;
- }
- div = (unsigned char) clkdiv;
- }
- /*
- * Check what form of modem signaling is required and set it up.
- */
- if ((tiosp->c_cflag & CLOCAL) == 0) {
- mcor1 |= MCOR1_DCD;
- mcor2 |= MCOR2_DCD;
- sreron |= SRER_MODEM;
- portp->flags |= ASYNC_CHECK_CD;
- } else {
- portp->flags &= ~ASYNC_CHECK_CD;
- }
- /*
- * Setup cd1400 enhanced modes if we can. In particular we want to
- * handle as much of the flow control as possible automatically. As
- * well as saving a few CPU cycles it will also greatly improve flow
- * control reliability.
- */
- if (tiosp->c_iflag & IXON) {
- cor2 |= COR2_TXIBE;
- cor3 |= COR3_SCD12;
- if (tiosp->c_iflag & IXANY)
- cor2 |= COR2_IXM;
- }
- if (tiosp->c_cflag & CRTSCTS) {
- cor2 |= COR2_CTSAE;
- mcor1 |= FIFO_RTSTHRESHOLD;
- }
- /*
- * All cd1400 register values calculated so go through and set
- * them all up.
- */
- #if DEBUG
- printk("SETPORT: portnr=%d panelnr=%d brdnr=%dn",
- portp->portnr, portp->panelnr, portp->brdnr);
- printk(" cor1=%x cor2=%x cor3=%x cor4=%x cor5=%xn",
- cor1, cor2, cor3, cor4, cor5);
- printk(" mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%xn",
- mcor1, mcor2, rtpr, sreron, sreroff);
- printk(" tcor=%x tbpr=%x rcor=%x rbpr=%xn", clk, div, clk, div);
- printk(" schr1=%x schr2=%x schr3=%x schr4=%xn",
- tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP],
- tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
- #endif
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_cd1400setreg(portp, CAR, (portp->portnr & 0x3));
- srer = stl_cd1400getreg(portp, SRER);
- stl_cd1400setreg(portp, SRER, 0);
- if (stl_cd1400updatereg(portp, COR1, cor1))
- ccr = 1;
- if (stl_cd1400updatereg(portp, COR2, cor2))
- ccr = 1;
- if (stl_cd1400updatereg(portp, COR3, cor3))
- ccr = 1;
- if (ccr) {
- stl_cd1400ccrwait(portp);
- stl_cd1400setreg(portp, CCR, CCR_CORCHANGE);
- }
- stl_cd1400setreg(portp, COR4, cor4);
- stl_cd1400setreg(portp, COR5, cor5);
- stl_cd1400setreg(portp, MCOR1, mcor1);
- stl_cd1400setreg(portp, MCOR2, mcor2);
- if (baudrate > 0) {
- stl_cd1400setreg(portp, TCOR, clk);
- stl_cd1400setreg(portp, TBPR, div);
- stl_cd1400setreg(portp, RCOR, clk);
- stl_cd1400setreg(portp, RBPR, div);
- }
- stl_cd1400setreg(portp, SCHR1, tiosp->c_cc[VSTART]);
- stl_cd1400setreg(portp, SCHR2, tiosp->c_cc[VSTOP]);
- stl_cd1400setreg(portp, SCHR3, tiosp->c_cc[VSTART]);
- stl_cd1400setreg(portp, SCHR4, tiosp->c_cc[VSTOP]);
- stl_cd1400setreg(portp, RTPR, rtpr);
- mcor1 = stl_cd1400getreg(portp, MSVR1);
- if (mcor1 & MSVR1_DCD)
- portp->sigs |= TIOCM_CD;
- else
- portp->sigs &= ~TIOCM_CD;
- stl_cd1400setreg(portp, SRER, ((srer & ~sreroff) | sreron));
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- }
- /*****************************************************************************/
- /*
- * Set the state of the DTR and RTS signals.
- */
- static void stl_cd1400setsignals(stlport_t *portp, int dtr, int rts)
- {
- unsigned char msvr1, msvr2;
- unsigned long flags;
- #if DEBUG
- printk("stl_cd1400setsignals(portp=%x,dtr=%d,rts=%d)n",
- (int) portp, dtr, rts);
- #endif
- msvr1 = 0;
- msvr2 = 0;
- if (dtr > 0)
- msvr1 = MSVR1_DTR;
- if (rts > 0)
- msvr2 = MSVR2_RTS;
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
- if (rts >= 0)
- stl_cd1400setreg(portp, MSVR2, msvr2);
- if (dtr >= 0)
- stl_cd1400setreg(portp, MSVR1, msvr1);
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- }
- /*****************************************************************************/
- /*
- * Return the state of the signals.
- */
- static int stl_cd1400getsignals(stlport_t *portp)
- {
- unsigned char msvr1, msvr2;
- unsigned long flags;
- int sigs;
- #if DEBUG
- printk("stl_cd1400getsignals(portp=%x)n", (int) portp);
- #endif
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
- msvr1 = stl_cd1400getreg(portp, MSVR1);
- msvr2 = stl_cd1400getreg(portp, MSVR2);
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- sigs = 0;
- sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0;
- sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0;
- sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0;
- sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0;
- #if 0
- sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0;
- sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0;
- #else
- sigs |= TIOCM_DSR;
- #endif
- return(sigs);
- }
- /*****************************************************************************/
- /*
- * Enable/Disable the Transmitter and/or Receiver.
- */
- static void stl_cd1400enablerxtx(stlport_t *portp, int rx, int tx)
- {
- unsigned char ccr;
- unsigned long flags;
- #if DEBUG
- printk("stl_cd1400enablerxtx(portp=%x,rx=%d,tx=%d)n",
- (int) portp, rx, tx);
- #endif
- ccr = 0;
- if (tx == 0)
- ccr |= CCR_TXDISABLE;
- else if (tx > 0)
- ccr |= CCR_TXENABLE;
- if (rx == 0)
- ccr |= CCR_RXDISABLE;
- else if (rx > 0)
- ccr |= CCR_RXENABLE;
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
- stl_cd1400ccrwait(portp);
- stl_cd1400setreg(portp, CCR, ccr);
- stl_cd1400ccrwait(portp);
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- }
- /*****************************************************************************/
- /*
- * Start/stop the Transmitter and/or Receiver.
- */
- static void stl_cd1400startrxtx(stlport_t *portp, int rx, int tx)
- {
- unsigned char sreron, sreroff;
- unsigned long flags;
- #if DEBUG
- printk("stl_cd1400startrxtx(portp=%x,rx=%d,tx=%d)n",
- (int) portp, rx, tx);
- #endif
- sreron = 0;
- sreroff = 0;
- if (tx == 0)
- sreroff |= (SRER_TXDATA | SRER_TXEMPTY);
- else if (tx == 1)
- sreron |= SRER_TXDATA;
- else if (tx >= 2)
- sreron |= SRER_TXEMPTY;
- if (rx == 0)
- sreroff |= SRER_RXDATA;
- else if (rx > 0)
- sreron |= SRER_RXDATA;
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
- stl_cd1400setreg(portp, SRER,
- ((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron));
- BRDDISABLE(portp->brdnr);
- if (tx > 0)
- set_bit(ASYI_TXBUSY, &portp->istate);
- restore_flags(flags);
- }
- /*****************************************************************************/
- /*
- * Disable all interrupts from this port.
- */
- static void stl_cd1400disableintrs(stlport_t *portp)
- {
- unsigned long flags;
- #if DEBUG
- printk("stl_cd1400disableintrs(portp=%x)n", (int) portp);
- #endif
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
- stl_cd1400setreg(portp, SRER, 0);
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- }
- /*****************************************************************************/
- static void stl_cd1400sendbreak(stlport_t *portp, int len)
- {
- unsigned long flags;
- #if DEBUG
- printk("stl_cd1400sendbreak(portp=%x,len=%d)n", (int) portp, len);
- #endif
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
- stl_cd1400setreg(portp, SRER,
- ((stl_cd1400getreg(portp, SRER) & ~SRER_TXDATA) |
- SRER_TXEMPTY));
- BRDDISABLE(portp->brdnr);
- portp->brklen = len;
- if (len == 1)
- portp->stats.txbreaks++;
- restore_flags(flags);
- }
- /*****************************************************************************/
- /*
- * Take flow control actions...
- */
- static void stl_cd1400flowctrl(stlport_t *portp, int state)
- {
- struct tty_struct *tty;
- unsigned long flags;
- #if DEBUG
- printk("stl_cd1400flowctrl(portp=%x,state=%x)n", (int) portp, state);
- #endif
- if (portp == (stlport_t *) NULL)
- return;
- tty = portp->tty;
- if (tty == (struct tty_struct *) NULL)
- return;
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
- if (state) {
- if (tty->termios->c_iflag & IXOFF) {
- stl_cd1400ccrwait(portp);
- stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1);
- portp->stats.rxxon++;
- stl_cd1400ccrwait(portp);
- }
- /*
- * Question: should we return RTS to what it was before? It may
- * have been set by an ioctl... Suppose not, since if you have
- * hardware flow control set then it is pretty silly to go and
- * set the RTS line by hand.
- */
- if (tty->termios->c_cflag & CRTSCTS) {
- stl_cd1400setreg(portp, MCOR1,
- (stl_cd1400getreg(portp, MCOR1) |
- FIFO_RTSTHRESHOLD));
- stl_cd1400setreg(portp, MSVR2, MSVR2_RTS);
- portp->stats.rxrtson++;
- }
- } else {
- if (tty->termios->c_iflag & IXOFF) {
- stl_cd1400ccrwait(portp);
- stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2);
- portp->stats.rxxoff++;
- stl_cd1400ccrwait(portp);
- }
- if (tty->termios->c_cflag & CRTSCTS) {
- stl_cd1400setreg(portp, MCOR1,
- (stl_cd1400getreg(portp, MCOR1) & 0xf0));
- stl_cd1400setreg(portp, MSVR2, 0);
- portp->stats.rxrtsoff++;
- }
- }
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- }
- /*****************************************************************************/
- /*
- * Send a flow control character...
- */
- static void stl_cd1400sendflow(stlport_t *portp, int state)
- {
- struct tty_struct *tty;
- unsigned long flags;
- #if DEBUG
- printk("stl_cd1400sendflow(portp=%x,state=%x)n", (int) portp, state);
- #endif
- if (portp == (stlport_t *) NULL)
- return;
- tty = portp->tty;
- if (tty == (struct tty_struct *) NULL)
- return;
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
- if (state) {
- stl_cd1400ccrwait(portp);
- stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1);
- portp->stats.rxxon++;
- stl_cd1400ccrwait(portp);
- } else {
- stl_cd1400ccrwait(portp);
- stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2);
- portp->stats.rxxoff++;
- stl_cd1400ccrwait(portp);
- }
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- }
- /*****************************************************************************/
- static void stl_cd1400flush(stlport_t *portp)
- {
- unsigned long flags;
- #if DEBUG
- printk("stl_cd1400flush(portp=%x)n", (int) portp);
- #endif
- if (portp == (stlport_t *) NULL)
- return;
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
- stl_cd1400ccrwait(portp);
- stl_cd1400setreg(portp, CCR, CCR_TXFLUSHFIFO);
- stl_cd1400ccrwait(portp);
- portp->tx.tail = portp->tx.head;
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- }
- /*****************************************************************************/
- /*
- * Return the current state of data flow on this port. This is only
- * really interresting when determining if data has fully completed
- * transmission or not... This is easy for the cd1400, it accurately
- * maintains the busy port flag.
- */
- static int stl_cd1400datastate(stlport_t *portp)
- {
- #if DEBUG
- printk("stl_cd1400datastate(portp=%x)n", (int) portp);
- #endif
- if (portp == (stlport_t *) NULL)
- return(0);
- return(test_bit(ASYI_TXBUSY, &portp->istate) ? 1 : 0);
- }
- /*****************************************************************************/
- /*
- * Interrupt service routine for cd1400 EasyIO boards.
- */
- static void stl_cd1400eiointr(stlpanel_t *panelp, unsigned int iobase)
- {
- unsigned char svrtype;
- #if DEBUG
- printk("stl_cd1400eiointr(panelp=%x,iobase=%x)n",
- (int) panelp, iobase);
- #endif
- outb(SVRR, iobase);
- svrtype = inb(iobase + EREG_DATA);
- if (panelp->nrports > 4) {
- outb((SVRR + 0x80), iobase);
- svrtype |= inb(iobase + EREG_DATA);
- }
- if (svrtype & SVRR_RX)
- stl_cd1400rxisr(panelp, iobase);
- else if (svrtype & SVRR_TX)
- stl_cd1400txisr(panelp, iobase);
- else if (svrtype & SVRR_MDM)
- stl_cd1400mdmisr(panelp, iobase);
- }
- /*****************************************************************************/
- /*
- * Interrupt service routine for cd1400 panels.
- */
- static void stl_cd1400echintr(stlpanel_t *panelp, unsigned int iobase)
- {
- unsigned char svrtype;
- #if DEBUG
- printk("stl_cd1400echintr(panelp=%x,iobase=%x)n", (int) panelp,
- iobase);
- #endif
- outb(SVRR, iobase);
- svrtype = inb(iobase + EREG_DATA);
- outb((SVRR + 0x80), iobase);
- svrtype |= inb(iobase + EREG_DATA);
- if (svrtype & SVRR_RX)
- stl_cd1400rxisr(panelp, iobase);
- else if (svrtype & SVRR_TX)
- stl_cd1400txisr(panelp, iobase);
- else if (svrtype & SVRR_MDM)
- stl_cd1400mdmisr(panelp, iobase);
- }
- /*****************************************************************************/
- /*
- * Unfortunately we need to handle breaks in the TX data stream, since
- * this is the only way to generate them on the cd1400.
- */
- static inline int stl_cd1400breakisr(stlport_t *portp, int ioaddr)
- {
- if (portp->brklen == 1) {
- outb((COR2 + portp->uartaddr), ioaddr);
- outb((inb(ioaddr + EREG_DATA) | COR2_ETC),
- (ioaddr + EREG_DATA));
- outb((TDR + portp->uartaddr), ioaddr);
- outb(ETC_CMD, (ioaddr + EREG_DATA));
- outb(ETC_STARTBREAK, (ioaddr + EREG_DATA));
- outb((SRER + portp->uartaddr), ioaddr);
- outb((inb(ioaddr + EREG_DATA) & ~(SRER_TXDATA | SRER_TXEMPTY)),
- (ioaddr + EREG_DATA));
- return(1);
- } else if (portp->brklen > 1) {
- outb((TDR + portp->uartaddr), ioaddr);
- outb(ETC_CMD, (ioaddr + EREG_DATA));
- outb(ETC_STOPBREAK, (ioaddr + EREG_DATA));
- portp->brklen = -1;
- return(1);
- } else {
- outb((COR2 + portp->uartaddr), ioaddr);
- outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC),
- (ioaddr + EREG_DATA));
- portp->brklen = 0;
- }
- return(0);
- }
- /*****************************************************************************/
- /*
- * Transmit interrupt handler. This has gotta be fast! Handling TX
- * chars is pretty simple, stuff as many as possible from the TX buffer
- * into the cd1400 FIFO. Must also handle TX breaks here, since they
- * are embedded as commands in the data stream. Oh no, had to use a goto!
- * This could be optimized more, will do when I get time...
- * In practice it is possible that interrupts are enabled but that the
- * port has been hung up. Need to handle not having any TX buffer here,
- * this is done by using the side effect that head and tail will also
- * be NULL if the buffer has been freed.
- */
- static void stl_cd1400txisr(stlpanel_t *panelp, int ioaddr)
- {
- stlport_t *portp;
- int len, stlen;
- char *head, *tail;
- unsigned char ioack, srer;
- #if DEBUG
- printk("stl_cd1400txisr(panelp=%x,ioaddr=%x)n", (int) panelp, ioaddr);
- #endif
- ioack = inb(ioaddr + EREG_TXACK);
- if (((ioack & panelp->ackmask) != 0) ||
- ((ioack & ACK_TYPMASK) != ACK_TYPTX)) {
- printk("STALLION: bad TX interrupt ack value=%xn", ioack);
- return;
- }
- portp = panelp->ports[(ioack >> 3)];
- /*
- * Unfortunately we need to handle breaks in the data stream, since
- * this is the only way to generate them on the cd1400. Do it now if
- * a break is to be sent.
- */
- if (portp->brklen != 0)
- if (stl_cd1400breakisr(portp, ioaddr))
- goto stl_txalldone;
- head = portp->tx.head;
- tail = portp->tx.tail;
- len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
- if ((len == 0) || ((len < STL_TXBUFLOW) &&
- (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
- set_bit(ASYI_TXLOW, &portp->istate);
- MOD_INC_USE_COUNT;
- if (schedule_task(&portp->tqueue) == 0)
- MOD_DEC_USE_COUNT;
- }
- if (len == 0) {
- outb((SRER + portp->uartaddr), ioaddr);
- srer = inb(ioaddr + EREG_DATA);
- if (srer & SRER_TXDATA) {
- srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY;
- } else {
- srer &= ~(SRER_TXDATA | SRER_TXEMPTY);
- clear_bit(ASYI_TXBUSY, &portp->istate);
- }
- outb(srer, (ioaddr + EREG_DATA));
- } else {
- len = MIN(len, CD1400_TXFIFOSIZE);
- portp->stats.txtotal += len;
- stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail));
- outb((TDR + portp->uartaddr), ioaddr);
- outsb((ioaddr + EREG_DATA), tail, stlen);
- len -= stlen;
- tail += stlen;
- if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
- tail = portp->tx.buf;
- if (len > 0) {
- outsb((ioaddr + EREG_DATA), tail, len);
- tail += len;
- }
- portp->tx.tail = tail;
- }
- stl_txalldone:
- outb((EOSRR + portp->uartaddr), ioaddr);
- outb(0, (ioaddr + EREG_DATA));
- }
- /*****************************************************************************/
- /*
- * Receive character interrupt handler. Determine if we have good chars
- * or bad chars and then process appropriately. Good chars are easy
- * just shove the lot into the RX buffer and set all status byte to 0.
- * If a bad RX char then process as required. This routine needs to be
- * fast! In practice it is possible that we get an interrupt on a port
- * that is closed. This can happen on hangups - since they completely
- * shutdown a port not in user context. Need to handle this case.
- */
- static void stl_cd1400rxisr(stlpanel_t *panelp, int ioaddr)
- {
- stlport_t *portp;
- struct tty_struct *tty;
- unsigned int ioack, len, buflen;
- unsigned char status;
- char ch;
- #if DEBUG
- printk("stl_cd1400rxisr(panelp=%x,ioaddr=%x)n", (int) panelp, ioaddr);
- #endif
- ioack = inb(ioaddr + EREG_RXACK);
- if ((ioack & panelp->ackmask) != 0) {
- printk("STALLION: bad RX interrupt ack value=%xn", ioack);
- return;
- }
- portp = panelp->ports[(ioack >> 3)];
- tty = portp->tty;
- if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) {
- outb((RDCR + portp->uartaddr), ioaddr);
- len = inb(ioaddr + EREG_DATA);
- if ((tty == (struct tty_struct *) NULL) ||
- (tty->flip.char_buf_ptr == (char *) NULL) ||
- ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
- len = MIN(len, sizeof(stl_unwanted));
- outb((RDSR + portp->uartaddr), ioaddr);
- insb((ioaddr + EREG_DATA), &stl_unwanted[0], len);
- portp->stats.rxlost += len;
- portp->stats.rxtotal += len;
- } else {
- len = MIN(len, buflen);
- if (len > 0) {
- outb((RDSR + portp->uartaddr), ioaddr);
- insb((ioaddr + EREG_DATA), tty->flip.char_buf_ptr, len);
- memset(tty->flip.flag_buf_ptr, 0, len);
- tty->flip.flag_buf_ptr += len;
- tty->flip.char_buf_ptr += len;
- tty->flip.count += len;
- tty_schedule_flip(tty);
- portp->stats.rxtotal += len;
- }
- }
- } else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) {
- outb((RDSR + portp->uartaddr), ioaddr);
- status = inb(ioaddr + EREG_DATA);
- ch = inb(ioaddr + EREG_DATA);
- if (status & ST_PARITY)
- portp->stats.rxparity++;
- if (status & ST_FRAMING)
- portp->stats.rxframing++;
- if (status & ST_OVERRUN)
- portp->stats.rxoverrun++;
- if (status & ST_BREAK)
- portp->stats.rxbreaks++;
- if (status & ST_SCHARMASK) {
- if ((status & ST_SCHARMASK) == ST_SCHAR1)
- portp->stats.txxon++;
- if ((status & ST_SCHARMASK) == ST_SCHAR2)
- portp->stats.txxoff++;
- goto stl_rxalldone;
- }
- if ((tty != (struct tty_struct *) NULL) &&
- ((portp->rxignoremsk & status) == 0)) {
- if (portp->rxmarkmsk & status) {
- if (status & ST_BREAK) {
- status = TTY_BREAK;
- if (portp->flags & ASYNC_SAK) {
- do_SAK(tty);
- BRDENABLE(portp->brdnr, portp->pagenr);
- }
- } else if (status & ST_PARITY) {
- status = TTY_PARITY;
- } else if (status & ST_FRAMING) {
- status = TTY_FRAME;
- } else if(status & ST_OVERRUN) {
- status = TTY_OVERRUN;
- } else {
- status = 0;
- }
- } else {
- status = 0;
- }
- if (tty->flip.char_buf_ptr != (char *) NULL) {
- if (tty->flip.count < TTY_FLIPBUF_SIZE) {
- *tty->flip.flag_buf_ptr++ = status;
- *tty->flip.char_buf_ptr++ = ch;
- tty->flip.count++;
- }
- tty_schedule_flip(tty);
- }
- }
- } else {
- printk("STALLION: bad RX interrupt ack value=%xn", ioack);
- return;
- }
- stl_rxalldone:
- outb((EOSRR + portp->uartaddr), ioaddr);
- outb(0, (ioaddr + EREG_DATA));
- }
- /*****************************************************************************/
- /*
- * Modem interrupt handler. The is called when the modem signal line
- * (DCD) has changed state. Leave most of the work to the off-level
- * processing routine.
- */
- static void stl_cd1400mdmisr(stlpanel_t *panelp, int ioaddr)
- {
- stlport_t *portp;
- unsigned int ioack;
- unsigned char misr;
- #if DEBUG
- printk("stl_cd1400mdmisr(panelp=%x)n", (int) panelp);
- #endif
- ioack = inb(ioaddr + EREG_MDACK);
- if (((ioack & panelp->ackmask) != 0) ||
- ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) {
- printk("STALLION: bad MODEM interrupt ack value=%xn", ioack);
- return;
- }
- portp = panelp->ports[(ioack >> 3)];
- outb((MISR + portp->uartaddr), ioaddr);
- misr = inb(ioaddr + EREG_DATA);
- if (misr & MISR_DCD) {
- set_bit(ASYI_DCDCHANGE, &portp->istate);
- MOD_INC_USE_COUNT;
- if (schedule_task(&portp->tqueue) == 0)
- MOD_DEC_USE_COUNT;
- portp->stats.modem++;
- }
- outb((EOSRR + portp->uartaddr), ioaddr);
- outb(0, (ioaddr + EREG_DATA));
- }
- /*****************************************************************************/
- /* SC26198 HARDWARE FUNCTIONS */
- /*****************************************************************************/
- /*
- * These functions get/set/update the registers of the sc26198 UARTs.
- * Access to the sc26198 registers is via an address/data io port pair.
- * (Maybe should make this inline...)
- */
- static int stl_sc26198getreg(stlport_t *portp, int regnr)
- {
- outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
- return(inb(portp->ioaddr + XP_DATA));
- }
- static void stl_sc26198setreg(stlport_t *portp, int regnr, int value)
- {
- outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
- outb(value, (portp->ioaddr + XP_DATA));
- }
- static int stl_sc26198updatereg(stlport_t *portp, int regnr, int value)
- {
- outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
- if (inb(portp->ioaddr + XP_DATA) != value) {
- outb(value, (portp->ioaddr + XP_DATA));
- return(1);
- }
- return(0);
- }
- /*****************************************************************************/
- /*
- * Functions to get and set the sc26198 global registers.
- */
- static int stl_sc26198getglobreg(stlport_t *portp, int regnr)
- {
- outb(regnr, (portp->ioaddr + XP_ADDR));
- return(inb(portp->ioaddr + XP_DATA));
- }
- #if 0
- static void stl_sc26198setglobreg(stlport_t *portp, int regnr, int value)
- {
- outb(regnr, (portp->ioaddr + XP_ADDR));
- outb(value, (portp->ioaddr + XP_DATA));
- }
- #endif
- /*****************************************************************************/
- /*
- * Inbitialize the UARTs in a panel. We don't care what sort of board
- * these ports are on - since the port io registers are almost
- * identical when dealing with ports.
- */
- static int stl_sc26198panelinit(stlbrd_t *brdp, stlpanel_t *panelp)
- {
- int chipmask, i;
- int nrchips, ioaddr;
- #if DEBUG
- printk("stl_sc26198panelinit(brdp=%x,panelp=%x)n",
- (int) brdp, (int) panelp);
- #endif
- BRDENABLE(panelp->brdnr, panelp->pagenr);
- /*
- * Check that each chip is present and started up OK.
- */
- chipmask = 0;
- nrchips = (panelp->nrports + 4) / SC26198_PORTS;
- if (brdp->brdtype == BRD_ECHPCI)
- outb(panelp->pagenr, brdp->ioctrl);
- for (i = 0; (i < nrchips); i++) {
- ioaddr = panelp->iobase + (i * 4);
- outb(SCCR, (ioaddr + XP_ADDR));
- outb(CR_RESETALL, (ioaddr + XP_DATA));
- outb(TSTR, (ioaddr + XP_ADDR));
- if (inb(ioaddr + XP_DATA) != 0) {
- printk("STALLION: sc26198 not responding, "
- "brd=%d panel=%d chip=%dn",
- panelp->brdnr, panelp->panelnr, i);
- continue;
- }
- chipmask |= (0x1 << i);
- outb(GCCR, (ioaddr + XP_ADDR));
- outb(GCCR_IVRTYPCHANACK, (ioaddr + XP_DATA));
- outb(WDTRCR, (ioaddr + XP_ADDR));
- outb(0xff, (ioaddr + XP_DATA));
- }
- BRDDISABLE(panelp->brdnr);
- return(chipmask);
- }
- /*****************************************************************************/
- /*
- * Initialize hardware specific port registers.
- */
- static void stl_sc26198portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp)
- {
- #if DEBUG
- printk("stl_sc26198portinit(brdp=%x,panelp=%x,portp=%x)n",
- (int) brdp, (int) panelp, (int) portp);
- #endif
- if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) ||
- (portp == (stlport_t *) NULL))
- return;
- portp->ioaddr = panelp->iobase + ((portp->portnr < 8) ? 0 : 4);
- portp->uartaddr = (portp->portnr & 0x07) << 4;
- portp->pagenr = panelp->pagenr;
- portp->hwid = 0x1;
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_sc26198setreg(portp, IOPCR, IOPCR_SETSIGS);
- BRDDISABLE(portp->brdnr);
- }
- /*****************************************************************************/
- /*
- * Set up the sc26198 registers for a port based on the termios port
- * settings.
- */
- static void stl_sc26198setport(stlport_t *portp, struct termios *tiosp)
- {
- stlbrd_t *brdp;
- unsigned long flags;
- unsigned int baudrate;
- unsigned char mr0, mr1, mr2, clk;
- unsigned char imron, imroff, iopr, ipr;
- mr0 = 0;
- mr1 = 0;
- mr2 = 0;
- clk = 0;
- iopr = 0;
- imron = 0;
- imroff = 0;
- brdp = stl_brds[portp->brdnr];
- if (brdp == (stlbrd_t *) NULL)
- return;
- /*
- * Set up the RX char ignore mask with those RX error types we
- * can ignore.
- */
- portp->rxignoremsk = 0;
- if (tiosp->c_iflag & IGNPAR)
- portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING |
- SR_RXOVERRUN);
- if (tiosp->c_iflag & IGNBRK)
- portp->rxignoremsk |= SR_RXBREAK;
- portp->rxmarkmsk = SR_RXOVERRUN;
- if (tiosp->c_iflag & (INPCK | PARMRK))
- portp->rxmarkmsk |= (SR_RXPARITY | SR_RXFRAMING);
- if (tiosp->c_iflag & BRKINT)
- portp->rxmarkmsk |= SR_RXBREAK;
- /*
- * Go through the char size, parity and stop bits and set all the
- * option register appropriately.
- */
- switch (tiosp->c_cflag & CSIZE) {
- case CS5:
- mr1 |= MR1_CS5;
- break;
- case CS6:
- mr1 |= MR1_CS6;
- break;
- case CS7:
- mr1 |= MR1_CS7;
- break;
- default:
- mr1 |= MR1_CS8;
- break;
- }
- if (tiosp->c_cflag & CSTOPB)
- mr2 |= MR2_STOP2;
- else
- mr2 |= MR2_STOP1;
- if (tiosp->c_cflag & PARENB) {
- if (tiosp->c_cflag & PARODD)
- mr1 |= (MR1_PARENB | MR1_PARODD);
- else
- mr1 |= (MR1_PARENB | MR1_PAREVEN);
- } else {
- mr1 |= MR1_PARNONE;
- }
- mr1 |= MR1_ERRBLOCK;
- /*
- * Set the RX FIFO threshold at 8 chars. This gives a bit of breathing
- * space for hardware flow control and the like. This should be set to
- * VMIN.
- */
- mr2 |= MR2_RXFIFOHALF;
- /*
- * Calculate the baud rate timers. For now we will just assume that
- * the input and output baud are the same. The sc26198 has a fixed
- * baud rate table, so only discrete baud rates possible.
- */
- baudrate = tiosp->c_cflag & CBAUD;
- if (baudrate & CBAUDEX) {
- baudrate &= ~CBAUDEX;
- if ((baudrate < 1) || (baudrate > 4))
- tiosp->c_cflag &= ~CBAUDEX;
- else
- baudrate += 15;
- }
- baudrate = stl_baudrates[baudrate];
- if ((tiosp->c_cflag & CBAUD) == B38400) {
- if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- baudrate = 57600;
- else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- baudrate = 115200;
- else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- baudrate = 230400;
- else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- baudrate = 460800;
- else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
- baudrate = (portp->baud_base / portp->custom_divisor);
- }
- if (baudrate > STL_SC26198MAXBAUD)
- baudrate = STL_SC26198MAXBAUD;
- if (baudrate > 0) {
- for (clk = 0; (clk < SC26198_NRBAUDS); clk++) {
- if (baudrate <= sc26198_baudtable[clk])
- break;
- }
- }
- /*
- * Check what form of modem signaling is required and set it up.
- */
- if (tiosp->c_cflag & CLOCAL) {
- portp->flags &= ~ASYNC_CHECK_CD;
- } else {
- iopr |= IOPR_DCDCOS;
- imron |= IR_IOPORT;
- portp->flags |= ASYNC_CHECK_CD;
- }
- /*
- * Setup sc26198 enhanced modes if we can. In particular we want to
- * handle as much of the flow control as possible automatically. As
- * well as saving a few CPU cycles it will also greatly improve flow
- * control reliability.
- */
- if (tiosp->c_iflag & IXON) {
- mr0 |= MR0_SWFTX | MR0_SWFT;
- imron |= IR_XONXOFF;
- } else {
- imroff |= IR_XONXOFF;
- }
- if (tiosp->c_iflag & IXOFF)
- mr0 |= MR0_SWFRX;
- if (tiosp->c_cflag & CRTSCTS) {
- mr2 |= MR2_AUTOCTS;
- mr1 |= MR1_AUTORTS;
- }
- /*
- * All sc26198 register values calculated so go through and set
- * them all up.
- */
- #if DEBUG
- printk("SETPORT: portnr=%d panelnr=%d brdnr=%dn",
- portp->portnr, portp->panelnr, portp->brdnr);
- printk(" mr0=%x mr1=%x mr2=%x clk=%xn", mr0, mr1, mr2, clk);
- printk(" iopr=%x imron=%x imroff=%xn", iopr, imron, imroff);
- printk(" schr1=%x schr2=%x schr3=%x schr4=%xn",
- tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP],
- tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
- #endif
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_sc26198setreg(portp, IMR, 0);
- stl_sc26198updatereg(portp, MR0, mr0);
- stl_sc26198updatereg(portp, MR1, mr1);
- stl_sc26198setreg(portp, SCCR, CR_RXERRBLOCK);
- stl_sc26198updatereg(portp, MR2, mr2);
- stl_sc26198updatereg(portp, IOPIOR,
- ((stl_sc26198getreg(portp, IOPIOR) & ~IPR_CHANGEMASK) | iopr));
- if (baudrate > 0) {
- stl_sc26198setreg(portp, TXCSR, clk);
- stl_sc26198setreg(portp, RXCSR, clk);
- }
- stl_sc26198setreg(portp, XONCR, tiosp->c_cc[VSTART]);
- stl_sc26198setreg(portp, XOFFCR, tiosp->c_cc[VSTOP]);
- ipr = stl_sc26198getreg(portp, IPR);
- if (ipr & IPR_DCD)
- portp->sigs &= ~TIOCM_CD;
- else
- portp->sigs |= TIOCM_CD;
- portp->imr = (portp->imr & ~imroff) | imron;
- stl_sc26198setreg(portp, IMR, portp->imr);
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- }
- /*****************************************************************************/
- /*
- * Set the state of the DTR and RTS signals.
- */
- static void stl_sc26198setsignals(stlport_t *portp, int dtr, int rts)
- {
- unsigned char iopioron, iopioroff;
- unsigned long flags;
- #if DEBUG
- printk("stl_sc26198setsignals(portp=%x,dtr=%d,rts=%d)n",
- (int) portp, dtr, rts);
- #endif
- iopioron = 0;
- iopioroff = 0;
- if (dtr == 0)
- iopioroff |= IPR_DTR;
- else if (dtr > 0)
- iopioron |= IPR_DTR;
- if (rts == 0)
- iopioroff |= IPR_RTS;
- else if (rts > 0)
- iopioron |= IPR_RTS;
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_sc26198setreg(portp, IOPIOR,
- ((stl_sc26198getreg(portp, IOPIOR) & ~iopioroff) | iopioron));
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- }
- /*****************************************************************************/
- /*
- * Return the state of the signals.
- */
- static int stl_sc26198getsignals(stlport_t *portp)
- {
- unsigned char ipr;
- unsigned long flags;
- int sigs;
- #if DEBUG
- printk("stl_sc26198getsignals(portp=%x)n", (int) portp);
- #endif
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- ipr = stl_sc26198getreg(portp, IPR);
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- sigs = 0;
- sigs |= (ipr & IPR_DCD) ? 0 : TIOCM_CD;
- sigs |= (ipr & IPR_CTS) ? 0 : TIOCM_CTS;
- sigs |= (ipr & IPR_DTR) ? 0: TIOCM_DTR;
- sigs |= (ipr & IPR_RTS) ? 0: TIOCM_RTS;
- sigs |= TIOCM_DSR;
- return(sigs);
- }
- /*****************************************************************************/
- /*
- * Enable/Disable the Transmitter and/or Receiver.
- */
- static void stl_sc26198enablerxtx(stlport_t *portp, int rx, int tx)
- {
- unsigned char ccr;
- unsigned long flags;
- #if DEBUG
- printk("stl_sc26198enablerxtx(portp=%x,rx=%d,tx=%d)n",
- (int) portp, rx, tx);
- #endif
- ccr = portp->crenable;
- if (tx == 0)
- ccr &= ~CR_TXENABLE;
- else if (tx > 0)
- ccr |= CR_TXENABLE;
- if (rx == 0)
- ccr &= ~CR_RXENABLE;
- else if (rx > 0)
- ccr |= CR_RXENABLE;
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_sc26198setreg(portp, SCCR, ccr);
- BRDDISABLE(portp->brdnr);
- portp->crenable = ccr;
- restore_flags(flags);
- }
- /*****************************************************************************/
- /*
- * Start/stop the Transmitter and/or Receiver.
- */
- static void stl_sc26198startrxtx(stlport_t *portp, int rx, int tx)
- {
- unsigned char imr;
- unsigned long flags;
- #if DEBUG
- printk("stl_sc26198startrxtx(portp=%x,rx=%d,tx=%d)n",
- (int) portp, rx, tx);
- #endif
- imr = portp->imr;
- if (tx == 0)
- imr &= ~IR_TXRDY;
- else if (tx == 1)
- imr |= IR_TXRDY;
- if (rx == 0)
- imr &= ~(IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG);
- else if (rx > 0)
- imr |= IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG;
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_sc26198setreg(portp, IMR, imr);
- BRDDISABLE(portp->brdnr);
- portp->imr = imr;
- if (tx > 0)
- set_bit(ASYI_TXBUSY, &portp->istate);
- restore_flags(flags);
- }
- /*****************************************************************************/
- /*
- * Disable all interrupts from this port.
- */
- static void stl_sc26198disableintrs(stlport_t *portp)
- {
- unsigned long flags;
- #if DEBUG
- printk("stl_sc26198disableintrs(portp=%x)n", (int) portp);
- #endif
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- portp->imr = 0;
- stl_sc26198setreg(portp, IMR, 0);
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- }
- /*****************************************************************************/
- static void stl_sc26198sendbreak(stlport_t *portp, int len)
- {
- unsigned long flags;
- #if DEBUG
- printk("stl_sc26198sendbreak(portp=%x,len=%d)n", (int) portp, len);
- #endif
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- if (len == 1) {
- stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK);
- portp->stats.txbreaks++;
- } else {
- stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK);
- }
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- }
- /*****************************************************************************/
- /*
- * Take flow control actions...
- */
- static void stl_sc26198flowctrl(stlport_t *portp, int state)
- {
- struct tty_struct *tty;
- unsigned long flags;
- unsigned char mr0;
- #if DEBUG
- printk("stl_sc26198flowctrl(portp=%x,state=%x)n", (int) portp, state);
- #endif
- if (portp == (stlport_t *) NULL)
- return;
- tty = portp->tty;
- if (tty == (struct tty_struct *) NULL)
- return;
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- if (state) {
- if (tty->termios->c_iflag & IXOFF) {
- mr0 = stl_sc26198getreg(portp, MR0);
- stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
- stl_sc26198setreg(portp, SCCR, CR_TXSENDXON);
- mr0 |= MR0_SWFRX;
- portp->stats.rxxon++;
- stl_sc26198wait(portp);
- stl_sc26198setreg(portp, MR0, mr0);
- }
- /*
- * Question: should we return RTS to what it was before? It may
- * have been set by an ioctl... Suppose not, since if you have
- * hardware flow control set then it is pretty silly to go and
- * set the RTS line by hand.
- */
- if (tty->termios->c_cflag & CRTSCTS) {
- stl_sc26198setreg(portp, MR1,
- (stl_sc26198getreg(portp, MR1) | MR1_AUTORTS));
- stl_sc26198setreg(portp, IOPIOR,
- (stl_sc26198getreg(portp, IOPIOR) | IOPR_RTS));
- portp->stats.rxrtson++;
- }
- } else {
- if (tty->termios->c_iflag & IXOFF) {
- mr0 = stl_sc26198getreg(portp, MR0);
- stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
- stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF);
- mr0 &= ~MR0_SWFRX;
- portp->stats.rxxoff++;
- stl_sc26198wait(portp);
- stl_sc26198setreg(portp, MR0, mr0);
- }
- if (tty->termios->c_cflag & CRTSCTS) {
- stl_sc26198setreg(portp, MR1,
- (stl_sc26198getreg(portp, MR1) & ~MR1_AUTORTS));
- stl_sc26198setreg(portp, IOPIOR,
- (stl_sc26198getreg(portp, IOPIOR) & ~IOPR_RTS));
- portp->stats.rxrtsoff++;
- }
- }
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- }
- /*****************************************************************************/
- /*
- * Send a flow control character.
- */
- static void stl_sc26198sendflow(stlport_t *portp, int state)
- {
- struct tty_struct *tty;
- unsigned long flags;
- unsigned char mr0;
- #if DEBUG
- printk("stl_sc26198sendflow(portp=%x,state=%x)n", (int) portp, state);
- #endif
- if (portp == (stlport_t *) NULL)
- return;
- tty = portp->tty;
- if (tty == (struct tty_struct *) NULL)
- return;
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- if (state) {
- mr0 = stl_sc26198getreg(portp, MR0);
- stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
- stl_sc26198setreg(portp, SCCR, CR_TXSENDXON);
- mr0 |= MR0_SWFRX;
- portp->stats.rxxon++;
- stl_sc26198wait(portp);
- stl_sc26198setreg(portp, MR0, mr0);
- } else {
- mr0 = stl_sc26198getreg(portp, MR0);
- stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
- stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF);
- mr0 &= ~MR0_SWFRX;
- portp->stats.rxxoff++;
- stl_sc26198wait(portp);
- stl_sc26198setreg(portp, MR0, mr0);
- }
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- }
- /*****************************************************************************/
- static void stl_sc26198flush(stlport_t *portp)
- {
- unsigned long flags;
- #if DEBUG
- printk("stl_sc26198flush(portp=%x)n", (int) portp);
- #endif
- if (portp == (stlport_t *) NULL)
- return;
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- stl_sc26198setreg(portp, SCCR, CR_TXRESET);
- stl_sc26198setreg(portp, SCCR, portp->crenable);
- BRDDISABLE(portp->brdnr);
- portp->tx.tail = portp->tx.head;
- restore_flags(flags);
- }
- /*****************************************************************************/
- /*
- * Return the current state of data flow on this port. This is only
- * really interresting when determining if data has fully completed
- * transmission or not... The sc26198 interrupt scheme cannot
- * determine when all data has actually drained, so we need to
- * check the port statusy register to be sure.
- */
- static int stl_sc26198datastate(stlport_t *portp)
- {
- unsigned long flags;
- unsigned char sr;
- #if DEBUG
- printk("stl_sc26198datastate(portp=%x)n", (int) portp);
- #endif
- if (portp == (stlport_t *) NULL)
- return(0);
- if (test_bit(ASYI_TXBUSY, &portp->istate))
- return(1);
- save_flags(flags);
- cli();
- BRDENABLE(portp->brdnr, portp->pagenr);
- sr = stl_sc26198getreg(portp, SR);
- BRDDISABLE(portp->brdnr);
- restore_flags(flags);
- return((sr & SR_TXEMPTY) ? 0 : 1);
- }
- /*****************************************************************************/
- /*
- * Delay for a small amount of time, to give the sc26198 a chance
- * to process a command...
- */
- static void stl_sc26198wait(stlport_t *portp)
- {
- int i;
- #if DEBUG
- printk("stl_sc26198wait(portp=%x)n", (int) portp);
- #endif
- if (portp == (stlport_t *) NULL)
- return;
- for (i = 0; (i < 20); i++)
- stl_sc26198getglobreg(portp, TSTR);
- }
- /*****************************************************************************/
- /*
- * If we are TX flow controlled and in IXANY mode then we may
- * need to unflow control here. We gotta do this because of the
- * automatic flow control modes of the sc26198.
- */
- static inline void stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty)
- {
- unsigned char mr0;
- mr0 = stl_sc26198getreg(portp, MR0);
- stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
- stl_sc26198setreg(portp, SCCR, CR_HOSTXON);
- stl_sc26198wait(portp);
- stl_sc26198setreg(portp, MR0, mr0);
- clear_bit(ASYI_TXFLOWED, &portp->istate);
- }
- /*****************************************************************************/
- /*
- * Interrupt service routine for sc26198 panels.
- */
- static void stl_sc26198intr(stlpanel_t *panelp, unsigned int iobase)
- {
- stlport_t *portp;
- unsigned int iack;
- /*
- * Work around bug in sc26198 chip... Cannot have A6 address
- * line of UART high, else iack will be returned as 0.
- */
- outb(0, (iobase + 1));
- iack = inb(iobase + XP_IACK);
- portp = panelp->ports[(iack & IVR_CHANMASK) + ((iobase & 0x4) << 1)];
- if (iack & IVR_RXDATA)
- stl_sc26198rxisr(portp, iack);
- else if (iack & IVR_TXDATA)
- stl_sc26198txisr(portp);
- else
- stl_sc26198otherisr(portp, iack);
- }
- /*****************************************************************************/
- /*
- * Transmit interrupt handler. This has gotta be fast! Handling TX
- * chars is pretty simple, stuff as many as possible from the TX buffer
- * into the sc26198 FIFO.
- * In practice it is possible that interrupts are enabled but that the
- * port has been hung up. Need to handle not having any TX buffer here,
- * this is done by using the side effect that head and tail will also
- * be NULL if the buffer has been freed.
- */
- static void stl_sc26198txisr(stlport_t *portp)
- {
- unsigned int ioaddr;
- unsigned char mr0;
- int len, stlen;
- char *head, *tail;
- #if DEBUG
- printk("stl_sc26198txisr(portp=%x)n", (int) portp);
- #endif
- ioaddr = portp->ioaddr;
- head = portp->tx.head;
- tail = portp->tx.tail;
- len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
- if ((len == 0) || ((len < STL_TXBUFLOW) &&
- (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
- set_bit(ASYI_TXLOW, &portp->istate);
- MOD_INC_USE_COUNT;
- if (schedule_task(&portp->tqueue) == 0)
- MOD_DEC_USE_COUNT;
- }
- if (len == 0) {
- outb((MR0 | portp->uartaddr), (ioaddr + XP_ADDR));
- mr0 = inb(ioaddr + XP_DATA);
- if ((mr0 & MR0_TXMASK) == MR0_TXEMPTY) {
- portp->imr &= ~IR_TXRDY;
- outb((IMR | portp->uartaddr), (ioaddr + XP_ADDR));
- outb(portp->imr, (ioaddr + XP_DATA));
- clear_bit(ASYI_TXBUSY, &portp->istate);
- } else {
- mr0 |= ((mr0 & ~MR0_TXMASK) | MR0_TXEMPTY);
- outb(mr0, (ioaddr + XP_DATA));
- }
- } else {
- len = MIN(len, SC26198_TXFIFOSIZE);
- portp->stats.txtotal += len;
- stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail));
- outb(GTXFIFO, (ioaddr + XP_ADDR));
- outsb((ioaddr + XP_DATA), tail, stlen);
- len -= stlen;
- tail += stlen;
- if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
- tail = portp->tx.buf;
- if (len > 0) {
- outsb((ioaddr + XP_DATA), tail, len);
- tail += len;
- }
- portp->tx.tail = tail;
- }
- }
- /*****************************************************************************/
- /*
- * Receive character interrupt handler. Determine if we have good chars
- * or bad chars and then process appropriately. Good chars are easy
- * just shove the lot into the RX buffer and set all status byte to 0.
- * If a bad RX char then process as required. This routine needs to be
- * fast! In practice it is possible that we get an interrupt on a port
- * that is closed. This can happen on hangups - since they completely
- * shutdown a port not in user context. Need to handle this case.
- */
- static void stl_sc26198rxisr(stlport_t *portp, unsigned int iack)
- {
- struct tty_struct *tty;
- unsigned int len, buflen, ioaddr;
- #if DEBUG
- printk("stl_sc26198rxisr(portp=%x,iack=%x)n", (int) portp, iack);
- #endif
- tty = portp->tty;
- ioaddr = portp->ioaddr;
- outb(GIBCR, (ioaddr + XP_ADDR));
- len = inb(ioaddr + XP_DATA) + 1;
- if ((iack & IVR_TYPEMASK) == IVR_RXDATA) {
- if ((tty == (struct tty_struct *) NULL) ||
- (tty->flip.char_buf_ptr == (char *) NULL) ||
- ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
- len = MIN(len, sizeof(stl_unwanted));
- outb(GRXFIFO, (ioaddr + XP_ADDR));
- insb((ioaddr + XP_DATA), &stl_unwanted[0], len);
- portp->stats.rxlost += len;
- portp->stats.rxtotal += len;
- } else {
- len = MIN(len, buflen);
- if (len > 0) {
- outb(GRXFIFO, (ioaddr + XP_ADDR));
- insb((ioaddr + XP_DATA), tty->flip.char_buf_ptr, len);
- memset(tty->flip.flag_buf_ptr, 0, len);
- tty->flip.flag_buf_ptr += len;
- tty->flip.char_buf_ptr += len;
- tty->flip.count += len;
- tty_schedule_flip(tty);
- portp->stats.rxtotal += len;
- }
- }
- } else {
- stl_sc26198rxbadchars(portp);
- }
- /*
- * If we are TX flow controlled and in IXANY mode then we may need
- * to unflow control here. We gotta do this because of the automatic
- * flow control modes of the sc26198.
- */
- if (test_bit(ASYI_TXFLOWED, &portp->istate)) {
- if ((tty != (struct tty_struct *) NULL) &&
- (tty->termios != (struct termios *) NULL) &&
- (tty->termios->c_iflag & IXANY)) {
- stl_sc26198txunflow(portp, tty);
- }
- }
- }
- /*****************************************************************************/
- /*
- * Process an RX bad character.
- */
- static void inline stl_sc26198rxbadch(stlport_t *portp, unsigned char status, char ch)
- {
- struct tty_struct *tty;
- unsigned int ioaddr;
- tty = portp->tty;
- ioaddr = portp->ioaddr;
- if (status & SR_RXPARITY)
- portp->stats.rxparity++;
- if (status & SR_RXFRAMING)
- portp->stats.rxframing++;
- if (status & SR_RXOVERRUN)
- portp->stats.rxoverrun++;
- if (status & SR_RXBREAK)
- portp->stats.rxbreaks++;
- if ((tty != (struct tty_struct *) NULL) &&
- ((portp->rxignoremsk & status) == 0)) {
- if (portp->rxmarkmsk & status) {
- if (status & SR_RXBREAK) {
- status = TTY_BREAK;
- if (portp->flags & ASYNC_SAK) {
- do_SAK(tty);
- BRDENABLE(portp->brdnr, portp->pagenr);
- }
- } else if (status & SR_RXPARITY) {
- status = TTY_PARITY;
- } else if (status & SR_RXFRAMING) {
- status = TTY_FRAME;
- } else if(status & SR_RXOVERRUN) {
- status = TTY_OVERRUN;
- } else {
- status = 0;
- }
- } else {
- status = 0;
- }
- if (tty->flip.char_buf_ptr != (char *) NULL) {
- if (tty->flip.count < TTY_FLIPBUF_SIZE) {
- *tty->flip.flag_buf_ptr++ = status;
- *tty->flip.char_buf_ptr++ = ch;
- tty->flip.count++;
- }
- tty_schedule_flip(tty);
- }
- if (status == 0)
- portp->stats.rxtotal++;
- }
- }
- /*****************************************************************************/
- /*
- * Process all characters in the RX FIFO of the UART. Check all char
- * status bytes as well, and process as required. We need to check
- * all bytes in the FIFO, in case some more enter the FIFO while we
- * are here. To get the exact character error type we need to switch
- * into CHAR error mode (that is why we need to make sure we empty
- * the FIFO).
- */
- static void stl_sc26198rxbadchars(stlport_t *portp)
- {
- unsigned char status, mr1;
- char ch;
- /*
- * To get the precise error type for each character we must switch
- * back into CHAR error mode.
- */
- mr1 = stl_sc26198getreg(portp, MR1);
- stl_sc26198setreg(portp, MR1, (mr1 & ~MR1_ERRBLOCK));
- while ((status = stl_sc26198getreg(portp, SR)) & SR_RXRDY) {
- stl_sc26198setreg(portp, SCCR, CR_CLEARRXERR);
- ch = stl_sc26198getreg(portp, RXFIFO);
- stl_sc26198rxbadch(portp, status, ch);
- }
- /*
- * To get correct interrupt class we must switch back into BLOCK
- * error mode.
- */
- stl_sc26198setreg(portp, MR1, mr1);
- }
- /*****************************************************************************/
- /*
- * Other interrupt handler. This includes modem signals, flow
- * control actions, etc. Most stuff is left to off-level interrupt
- * processing time.
- */
- static void stl_sc26198otherisr(stlport_t *portp, unsigned int iack)
- {
- unsigned char cir, ipr, xisr;
- #if DEBUG
- printk("stl_sc26198otherisr(portp=%x,iack=%x)n", (int) portp, iack);
- #endif
- cir = stl_sc26198getglobreg(portp, CIR);
- switch (cir & CIR_SUBTYPEMASK) {
- case CIR_SUBCOS:
- ipr = stl_sc26198getreg(portp, IPR);
- if (ipr & IPR_DCDCHANGE) {
- set_bit(ASYI_DCDCHANGE, &portp->istate);
- MOD_INC_USE_COUNT;
- if (schedule_task(&portp->tqueue) == 0)
- MOD_DEC_USE_COUNT;
- portp->stats.modem++;
- }
- break;
- case CIR_SUBXONXOFF:
- xisr = stl_sc26198getreg(portp, XISR);
- if (xisr & XISR_RXXONGOT) {
- set_bit(ASYI_TXFLOWED, &portp->istate);
- portp->stats.txxoff++;
- }
- if (xisr & XISR_RXXOFFGOT) {
- clear_bit(ASYI_TXFLOWED, &portp->istate);
- portp->stats.txxon++;
- }
- break;
- case CIR_SUBBREAK:
- stl_sc26198setreg(portp, SCCR, CR_BREAKRESET);
- stl_sc26198rxbadchars(portp);
- break;
- default:
- break;
- }
- }
- /*****************************************************************************/