patch-2.4.19 linux-2.4.19/drivers/net/wan/8253x/8253xutl.c
Next file: linux-2.4.19/drivers/net/wan/8253x/Makefile
Previous file: linux-2.4.19/drivers/net/wan/8253x/8253xtty.c
Back to the patch index
Back to the overall index
- Lines: 1423
- Date:
Fri Aug 2 17:39:44 2002
- Orig file:
linux-2.4.18/drivers/net/wan/8253x/8253xutl.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -urN linux-2.4.18/drivers/net/wan/8253x/8253xutl.c linux-2.4.19/drivers/net/wan/8253x/8253xutl.c
@@ -0,0 +1,1422 @@
+/* -*- linux-c -*- */
+/* $Id: 8253xutl.c,v 1.3 2002/02/10 22:17:26 martillo Exp $
+ * 8253xutl.c: SYNC TTY Driver for the SIEMENS SAB8253X DUSCC.
+ *
+ * Implementation, modifications and extensions
+ * Copyright (C) 2001 By Joachim Martillo, Telford Tools, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/* Standard in kernel modules */
+#define DEFINE_VARIABLE
+#include <linux/module.h> /* Specifically, a module */
+#include <asm/io.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/mm.h>
+#include <linux/version.h>
+#include <asm/uaccess.h>
+#include "8253xctl.h"
+#include "8253x.h"
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include "sp502.h"
+
+#ifdef MODULE
+#undef XCONFIG_SERIAL_CONSOLE
+#endif
+
+void sab8253x_start_txS(struct sab_port *port)
+{
+ unsigned long flags;
+ register int count;
+ register int total;
+ register int offset;
+ char temporary[32];
+ register unsigned int slopspace;
+ register int sendsize;
+ unsigned int totaltransmit;
+ unsigned fifospace;
+ unsigned loadedcount;
+ struct tty_struct *tty = port->tty; /* a little gross tty flags whether
+ invoked from a tty or the network */
+
+ fifospace = port->xmit_fifo_size; /* This code can handle fragmented frames
+ although currently none are generated*/
+ loadedcount = 0;
+
+ if(port->sabnext2.transmit == NULL)
+ {
+ return;
+ }
+
+ save_flags(flags);
+ cli();
+
+
+ if(count = port->sabnext2.transmit->Count, (count & OWNER) == OWN_SAB)
+ {
+ count &= ~OWN_SAB; /* OWN_SAB is really 0 but cannot guarantee in the future */
+
+ if(port->sabnext2.transmit->HostVaddr)
+ {
+ total = (port->sabnext2.transmit->HostVaddr->tail -
+ port->sabnext2.transmit->HostVaddr->data); /* packet size */
+ }
+ else
+ {
+ total = 0; /* the data is only the crc/trailer */
+ }
+
+ if(tty && (tty->stopped || tty->hw_stopped) && (count == total))
+ { /* works for frame that only has a trailer (crc) */
+ port->interrupt_mask1 |= SAB82532_IMR1_XPR;
+ WRITEB(port, imr1, port->interrupt_mask1);
+ restore_flags(flags); /* can't send */
+ return;
+ }
+
+ offset = (total - count); /* offset to data still to send */
+
+ port->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
+ WRITEB(port, imr1, port->interrupt_mask1);
+ port->all_sent = 0;
+
+ if(READB(port,star) & SAB82532_STAR_XFW)
+ {
+ if(count <= fifospace)
+ {
+ port->xmit_cnt = count;
+ slopspace = 0;
+ sendsize = 0;
+ if(port->sabnext2.transmit->sendcrc)
+ /* obviously should not happen for async but might use for
+ priority transmission */
+ {
+ slopspace = fifospace - count;
+ }
+ if(slopspace)
+ {
+ if(count)
+ {
+ memcpy(temporary, &port->sabnext2.transmit->HostVaddr->data[offset],
+ count);
+ }
+ sendsize = MIN(slopspace, (4 - port->sabnext2.transmit->crcindex));
+ /* how many bytes to send */
+ memcpy(&temporary[count],
+ &((unsigned char*)(&port->sabnext2.transmit->crc))
+ [port->sabnext2.transmit->crcindex],
+ sendsize);
+ port->sabnext2.transmit->crcindex += sendsize;
+ if(port->sabnext2.transmit->crcindex >= 4)
+ {
+ port->sabnext2.transmit->sendcrc = 0;
+ }
+ port->xmit_buf = temporary;
+ }
+ else
+ {
+ port->xmit_buf = /* set up wrifefifo variables */
+ &port->sabnext2.transmit->HostVaddr->data[offset];
+ }
+ port->xmit_cnt += sendsize;
+ count = 0;
+ }
+ else
+ {
+ count -= fifospace;
+ port->xmit_cnt = fifospace;
+ port->xmit_buf = /* set up wrifefifo variables */
+ &port->sabnext2.transmit->HostVaddr->data[offset];
+
+ }
+ port->xmit_tail= 0;
+ loadedcount = port->xmit_cnt;
+ (*port->writefifo)(port);
+ totaltransmit = Sab8253xCountTransmitDescriptors(port);
+ if(tty && (totaltransmit < (sab8253xs_listsize/2))) /* only makes sense on a TTY */
+ {
+ sab8253x_sched_event(port, SAB8253X_EVENT_WRITE_WAKEUP);
+ }
+
+ if((sab8253xt_listsize - totaltransmit) > (sab8253xt_listsize/2))
+ {
+ port->buffergreedy = 0;
+ }
+ else
+ {
+ port->buffergreedy = 1;
+ }
+
+ port->xmit_buf = NULL; /* this var is used to indicate whether to call kfree */
+
+ /* fifospace -= loadedcount;*/
+ /* Here to make mods to handle arbitrarily fragmented frames look to 8253xtty.c for help */
+
+ if ((count <= 0) && (port->sabnext2.transmit->sendcrc == 0))
+ {
+ port->sabnext2.transmit->Count = OWN_DRIVER;
+ if(!tty)
+ { /* called by network driver */
+ ++(port->Counters.transmitpacket);
+ }
+#ifdef FREEININTERRUPT /* treat this routine as if taking place in interrupt */
+ if(port->sabnext2.transmit->HostVaddr)
+ {
+ skb_unlink(port->sabnext2.transmit->HostVaddr);
+ dev_kfree_skb_any(port->sabnext2.transmit->HostVaddr);
+ port->sabnext2.transmit->HostVaddr = 0; /* no skb */
+ }
+ port->sabnext2.transmit->crcindex = 0; /* no single byte */
+#endif
+ sab8253x_cec_wait(port);
+ WRITEB(port, cmdr, SAB82532_CMDR_XME|SAB82532_CMDR_XTF); /* Terminate the frame */
+
+ port->sabnext2.transmit = port->sabnext2.transmit->VNext;
+
+ if(!tty && port->tx_full) /* invoked from the network driver */
+ {
+ port->tx_full = 0; /* there is a free slot */
+ switch(port->open_type)
+ {
+ case OPEN_SYNC_NET:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
+ port->dev->start = 1;
+ port->dev->tbusy = 0; /* maybe need mark_bh here */
+#else
+ netif_start_queue(port->dev);
+#endif
+ break;
+
+ case OPEN_SYNC_CHAR:
+ wake_up_interruptible(&port->write_wait);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if((port->sabnext2.transmit->Count & OWNER) == OWN_SAB)
+ { /* new frame to send */
+ port->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+ WRITEB(port, imr1, port->interrupt_mask1);
+ }
+ else
+ {
+ port->interrupt_mask1 |= SAB82532_IMR1_XPR;
+ WRITEB(port, imr1, port->interrupt_mask1);
+ if((port->open_type == OPEN_SYNC_CHAR) && port->async_queue)
+ { /* if indication of transmission is needed by the */
+ /* application on a per-frame basis kill_fasync */
+ /* can provide it */
+ kill_fasync(&port->async_queue, SIGIO, POLL_OUT);
+ }
+ }
+ restore_flags(flags);
+ return;
+ }
+ /* Issue a Transmit FIFO command. */
+ sab8253x_cec_wait(port);
+ WRITEB(port, cmdr, SAB82532_CMDR_XTF);
+ port->sabnext2.transmit->Count = (count|OWN_SAB);
+ }
+ port->interrupt_mask1 &= ~(SAB82532_IMR1_XPR); /* more to send */
+ WRITEB(port, imr1, port->interrupt_mask1);
+ }
+ else
+ { /* nothing to send */
+ port->interrupt_mask1 |= SAB82532_IMR1_XPR;
+ WRITEB(port, imr1, port->interrupt_mask1);
+ }
+ restore_flags(flags);
+ return;
+}
+
+void sab8253x_transmit_charsS(struct sab_port *port,
+ union sab8253x_irq_status *stat)
+{
+ if (stat->sreg.isr1 & SAB82532_ISR1_ALLS)
+ {
+ port->interrupt_mask1 |= SAB82532_IMR1_ALLS;
+ WRITEB(port, imr1, port->interrupt_mask1);
+ port->all_sent = 1;
+ }
+ sab8253x_start_txS(port);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+
+/***************************************************************************
+ * sab_baudenh: Function to compute the "enhanced" baudrate.
+ *
+ *
+ * Parameters :
+ * encbaud 2* the baudrate. We use the
+ * double value so as to support 134.5 (in only)
+ * clkspeed The board clock speed in Hz.
+ * bgr Value of reg BGR for baudrate(output)
+ * ccr2 Value of reg // CCR2 for baudrate (output)
+ * ccr4 Value of reg CCR4 for baudrate (output)
+ * truebaud The actual baudrate achieved (output).
+ *
+ *
+ * Return value : Return FALSE the parameters could not be computed,
+ *
+ * Prerequisite : The various ports must have been initialized
+ *
+ * Remark : Stolen from the Aurora ase driver.
+ *
+ * Author : fw
+ *
+ * Revision : Oct 9 2000, creation
+ ***************************************************************************/
+/*
+ * Macro to check to see if the high n bits of the given unsigned long
+ * are zero.
+ */
+#define HIZERO(x, n) ( ((unsigned long) ((x) << (n)) >> (n)) == (x))
+/* form an n-bit bitmask */
+#define NBM(n) (~(((~(unsigned long) 0) >> (n)) << (n)))
+/* shift x by y bits to right, rounded */
+#define ROUND_SHIFT(x, y) (((unsigned long) (x) + (NBM(y - 1) + 1)) >> (y))
+/* perform rounded division */
+#define ROUND_DIV(x, y) (((x) + ((y) >> 1)) / (y))
+#define ABSDIF(x, y) ((x) > (y) ? ((x) - (y)) : ((y) - (x)))
+static unsigned int
+sab8253x_baudenh(unsigned long encbaud, unsigned long clk_speed,
+ unsigned char *bgr, unsigned char *ccr2,
+ unsigned long *truebaudp)
+{
+ register unsigned short tmp;
+ register unsigned char ccr2tmp;
+ unsigned long power2, mant;
+ unsigned int fastclock;
+
+ if (encbaud == 0) {
+ return FALSE;
+ }
+
+ /*
+ * Keep dividing quotien by two until it is between the value of 1 and 64,
+ * inclusive.
+ */
+
+ fastclock = (clk_speed >= 10000000); /* >= 10 MHz */
+
+ for (power2 = 0; power2 < 16; power2++)
+ {
+ /* divisor = baud * 2^M * 16 */
+ if (!HIZERO(encbaud, power2 + 3))
+ {
+ if (!HIZERO(encbaud, power2))
+ { /* baud rate still too big? */
+ mant = ROUND_DIV(ROUND_SHIFT(clk_speed, power2 + 3), encbaud);
+
+ /* mant = (clk_speed / (8 * 2^M)) / (baud * 2) */
+ /* = clk_speed / (baud * 16 * 2^M) */
+ }
+ else
+ {
+ mant = ROUND_DIV(ROUND_SHIFT(clk_speed, 3), encbaud << power2);
+ /* mant = (clk_speed / 8) / (baud * 2 * 2^M) */
+ /* = clk_speed / (baud * 16 * 2^M) */
+ }
+ }
+ else
+ {
+ mant = ROUND_DIV(clk_speed, encbaud << (power2 + 3));
+ /* mant = clk_speed / (baud * 2 * 8 * 2^M) */
+ /* = clk_speed / (baud * 16 * 2^M) */
+ }
+
+ /* mant = clk_speed / (baud * 2^M * 16) */
+
+ if (mant < 2
+ || (mant <= 64 && (!fastclock || power2 != 0)))
+ {
+ break;
+ }
+ }
+
+ /*
+ * Did we not succeed? (Baud rate is too small)
+ */
+ if (mant > 64)
+ {
+ return FALSE;
+ }
+
+ /*
+ * Now, calculate the true baud rate.
+ */
+
+ if (mant < 1 || (mant == 1 && power2 == 0))
+ {
+ /* bgr and ccr2 should be initialized to 0 */
+ *truebaudp = ROUND_SHIFT(clk_speed, 4);
+ }
+ else
+ {
+ *truebaudp = ROUND_DIV(clk_speed, mant << (4 + power2));
+ /* divisor is not zero because mant is [1, 64] */
+ mant--; /* now [0, 63] */
+
+ /*
+ * Encode the N and M values into the bgr and ccr2 registers.
+ */
+
+ tmp = ((unsigned short) mant) | ((unsigned short) power2 << 6);
+
+ ccr2tmp = SAB82532_CCR2_BDF;
+ if ((tmp & 0x200) != 0)
+ {
+ ccr2tmp |= SAB82532_CCR2_BR9;
+ }
+ if ((tmp & 0x100) != 0)
+ {
+ ccr2tmp |= SAB82532_CCR2_BR8;
+ }
+
+ *ccr2 = ccr2tmp | (*ccr2 & ~(SAB82532_CCR2_BDF|SAB82532_CCR2_BR8|SAB82532_CCR2_BR9));
+ *bgr = (unsigned char) tmp;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Calculate the standard mode baud divisor using an integral algorithm.
+ */
+/***************************************************************************
+ * sab_baudstd: Function to compute the "standard " baudrate.
+ *
+ *
+ * Parameters :
+ * encbaud 2* the baudrate. We use the
+ * double value so as to support 134.5 (in only)
+ * clkspeed The board clock speed in Hz.
+ * bgr Value of reg BGR for baudrate(output)
+ * ccr2 Value of reg CCR2 for baudrate (output)
+ * ccr4 Value of reg CCR4 for baudrate (output)
+ * truebaud The actual baudrate achieved (output).
+ *
+ *
+ * Return value : Return FALSE the parameters could not be computed,
+ *
+ * Prerequisite : The various ports must have been initialized
+ *
+ * Remark : Stolen from the Aurora ase driver.
+ *
+ * Author : fw
+ *
+ * Revision : Oct 9 2000, creation
+ ***************************************************************************/
+static unsigned int
+sab8253x_baudstd(unsigned long encbaud, unsigned long clk_speed,
+ unsigned char *bgr, unsigned char *ccr2,
+ unsigned long *truebaudp)
+{
+ register unsigned short quot;
+ register unsigned char ccr2tmp;
+
+ if (encbaud == 0)
+ {
+ return FALSE;
+ }
+
+ /*
+ * This divisor algorithm is a little strange. The
+ * divisors are all multiples of 2, except for the
+ * magic value of 1.
+ *
+ * What we do is do most of the algorithm for multiples
+ * of 1, and then switch at the last minute to multiples
+ * of 2.
+ */
+
+ /*
+ * Will we lose any information by left shifting encbaud?
+ * If so, then right shift clk_speed instead.
+ */
+ if (!HIZERO(encbaud, 3))
+ {
+ quot = (unsigned short) ROUND_DIV(ROUND_SHIFT(clk_speed, 3),
+ encbaud);
+ /* quot = (clk_speed / 8) / (baud * 2) = clk_speed / (16 * baud) */
+ }
+ else
+ {
+ /* encbaud isn't a multiple of 2^29 (baud not mult. of 2^28) */
+ quot = (unsigned short) ROUND_DIV(clk_speed, encbaud << 3);
+ }
+
+ /* quot = clk_speed / (baud * 16) */
+ if (quot < 2)
+ {
+ /* bgr and ccr2 should be initialized to 0 */
+ *truebaudp = ROUND_SHIFT(clk_speed, 4);
+ return TRUE;
+ }
+
+ /*
+ * Divide the quotient by two.
+ */
+ quot = ROUND_SHIFT(quot, 1);
+
+ if (quot <= 0x400)
+ {
+ /* quot = [1, 0x400] -> (quot << 5) != 0 */
+ *truebaudp = ROUND_DIV(clk_speed, ((unsigned long) quot << 5));
+ quot--;
+
+ ccr2tmp = SAB82532_CCR2_BDF;
+ if ((quot & 0x200) != 0)
+ {
+ ccr2tmp |= SAB82532_CCR2_BR9;
+ }
+ if ((quot & 0x100) != 0)
+ {
+ ccr2tmp |=SAB82532_CCR2_BR8;
+ }
+
+ *ccr2 = ccr2tmp | (*ccr2 & ~(SAB82532_CCR2_BDF|SAB82532_CCR2_BR8|SAB82532_CCR2_BR9));
+ *bgr = (unsigned char) quot;
+ }
+ else
+ { /* the baud rate is too small. */
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/***************************************************************************
+ * sab_baud: Function to compute the best register value to achieve
+ * a given baudrate.
+ *
+ *
+ * Parameters :
+ * port: The port being used (in only)
+ * encbaud: 2* the baudrate. We use the
+ * double value so as to support 134.5 (in only)
+ * bgr Value of reg BGR for baudrate(output)
+ * ccr2 Value of reg CCR2 for baudrate (output)
+ * ccr4 Value of reg CCR4 for baudrate (output)
+ * truebaud The actual baudrate achieved (output).
+ *
+ *
+ * Return value : Return TRUE if the vaudrate can be set, FALSE otherwise
+ *
+ * Prerequisite : The various ports must have been initialized
+ *
+ * Remark : Stolen from the Aurora ase driver.
+ *
+ * Author : fw
+ *
+ * Revision : Oct 9 2000, creation
+ ***************************************************************************/
+unsigned int
+sab8253x_baud(sab_port_t *port, unsigned long encbaud,
+ unsigned char *bgr, unsigned char *ccr2,
+ unsigned char *ccr4, unsigned long *truebaudp)
+{
+ unsigned char bgr_std, bgr_enh, ccr2_std, ccr2_enh, ccr4_enh;
+ unsigned int ok_std, ok_enh;
+ unsigned long truebaud_std, truebaud_enh, truebaud,clkspeed;
+
+ bgr_std = bgr_enh = 0;
+ ccr2_std = ccr2_enh = 0;
+ ccr4_enh = 0;
+
+ /*
+ * the port/chip/board structure will tell us:
+ * 1) clock speed
+ * 2) chip revision (to figure out if the enhanced method is
+ * available.
+ */
+
+ clkspeed = port->chip->c_cim ? port->chip->c_cim->ci_clkspeed : port->board->b_clkspeed;
+
+#ifdef NODEBUGGING
+ printk("With clk speed %ld, baud rate = %ld\n",clkspeed, encbaud);
+#endif
+
+ ok_std = sab8253x_baudstd(encbaud, clkspeed, &bgr_std,
+ &ccr2_std, &truebaud_std);
+#ifdef NODEBUGGING
+ printk("Std gives bgr = 0x%x, ccr2=0x%x for speed %ld\n",bgr_std,ccr2_std,truebaud_std);
+#endif
+ if(port->chip->c_revision >= SAB82532_VSTR_VN_3_2)
+ {
+ ok_enh = sab8253x_baudenh(encbaud, clkspeed,
+ &bgr_enh, &ccr2_enh, &truebaud_enh);
+#ifdef NODEBUGGING
+ printk("Enh gives bgr = 0x%x, ccr2=0x%x for speed %ld\n",bgr_enh,ccr2_enh,truebaud_enh);
+#endif
+ }
+ else
+ ok_enh = FALSE;
+
+ /*
+ * Did both methods return values?
+ */
+ if (ok_std && ok_enh)
+ {
+ /*
+ * Find the closest of the two.
+ */
+ if (ABSDIF((truebaud_enh<<1), encbaud) <
+ ABSDIF((truebaud_std<<1), encbaud))
+ {
+ ok_std = FALSE;
+ }
+ else
+ {
+ ok_enh = FALSE;
+ }
+ }
+
+ /*
+ * Now return the values.
+ */
+
+ if (ok_std || ok_enh)
+ {
+ truebaud = ok_std ? truebaud_std : truebaud_enh;
+
+ /*
+ * If the true baud rate is off by more than 5%, then
+ * we don't support it.
+ */
+ if (ROUND_DIV(ABSDIF((truebaud<<1), encbaud), encbaud) != 0)
+ {
+ /*
+ * We're not even in the right ballpark. This
+ * test is here to deal with overflow conditions.
+ */
+ return FALSE;
+ }
+ else if (ROUND_DIV(ABSDIF((truebaud<<1), encbaud) * 100,
+ encbaud) >= 5)
+ {
+ return FALSE;
+ }
+
+ *truebaudp = truebaud;
+
+ if (ok_enh)
+ {
+ *ccr4 |= SAB82532_CCR4_EBRG;
+ *ccr2 = ccr2_enh;
+ *bgr = bgr_enh;
+#ifdef DEBUGGING
+ printk("Enhanced Baud at %ld, ccr4 = 0x%x, ccr2 = 9x%x, bgr = 0x%x\n",
+ truebaud,*ccr4,*ccr2,*bgr);
+#endif
+ }
+ else
+ {
+ *ccr4 &= ~SAB82532_CCR4_EBRG;
+ *ccr2 = ccr2_std;
+ *bgr = bgr_std;
+#ifdef DEBUGGING
+ printk("Standard Baud at %ld, ccr4 = 0x%x, ccr2 = 9x%x, bgr = 0x%x\n",
+ truebaud,*ccr4,*ccr2,*bgr);
+#endif
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+int Sab8253xCountTransmit(SAB_PORT *port)
+{
+ register RING_DESCRIPTOR *rd;
+ register int total;
+ register int count;
+ unsigned long flags;
+ RING_DESCRIPTOR *start;
+
+ if(port->sabnext2.transmit == NULL)
+ {
+ return 0;
+ }
+
+ save_flags(flags);
+ cli();
+ rd = port->sabnext2.transmit;
+ start = rd;
+ total = 0;
+ while(1)
+ {
+ count = rd->Count;
+ if((count & OWNER) == OWN_DRIVER)
+ {
+ break;
+ }
+ total += (count & ~OWNER);
+ if(rd->sendcrc)
+ {
+ total += (4 - rd->crcindex);
+ }
+ rd = rd->VNext;
+ if(rd == start)
+ {
+ break;
+ }
+ }
+ restore_flags(flags);
+ return total;
+}
+
+int Sab8253xCountTransmitDescriptors(SAB_PORT *port)
+{
+ register RING_DESCRIPTOR *rd;
+ register int total;
+ register int count;
+ unsigned long flags;
+ RING_DESCRIPTOR *start;
+
+ if(port->sabnext2.transmit == NULL)
+ {
+ return 0;
+ }
+
+ save_flags(flags);
+ cli();
+ rd = port->sabnext2.transmit;
+ start = rd;
+ total = 0;
+ while(1)
+ {
+ count = rd->Count;
+ if((count & OWNER) == OWN_DRIVER)
+ {
+ break;
+ }
+ ++total;
+ rd = rd->VNext;
+ if(rd == start)
+ {
+ break;
+ }
+ }
+ restore_flags(flags);
+ return total;
+}
+
+int getccr0configS(struct sab_port *port)
+{
+ return port->ccontrol.ccr0;
+}
+
+int getccr1configS(struct sab_port *port)
+{
+ return port->ccontrol.ccr1;
+}
+
+int getccr2configS(struct sab_port *port)
+{
+ return port->ccontrol.ccr2;
+}
+
+int getccr3configS(struct sab_port *port)
+{
+ return port->ccontrol.ccr3;
+}
+
+int getccr4configS(struct sab_port *port)
+{
+ return port->ccontrol.ccr4;
+}
+
+int getrlcrconfigS(struct sab_port *port)
+{
+ return port->ccontrol.rlcr;
+}
+
+int getmodeS(struct sab_port *port)
+{
+ return port->ccontrol.mode;
+}
+
+void sab8253x_init_lineS(struct sab_port *port)
+{
+ unsigned char stat;
+
+ if(port->chip->c_cim)
+ {
+ if(port->chip->c_cim->ci_type == CIM_SP502)
+ {
+ aura_sp502_program(port, SP502_OFF_MODE);
+ }
+ }
+
+ /*
+ * Wait for any commands or immediate characters
+ */
+ sab8253x_cec_wait(port);
+#if 0
+ sab8253x_tec_wait(port); /* I have to think about this one
+ * should I assume the line was
+ * previously in async mode*/
+#endif
+
+ /*
+ * Clear the FIFO buffers.
+ */
+
+ WRITEB(port, cmdr, SAB82532_CMDR_RHR);
+ sab8253x_cec_wait(port);
+ WRITEB(port,cmdr,SAB82532_CMDR_XRES);
+
+
+ /*
+ * Clear the interrupt registers.
+ */
+ stat = READB(port, isr0); /* acks ints */
+ stat = READB(port, isr1);
+
+ /*
+ * Now, initialize the UART
+ */
+ WRITEB(port, ccr0, 0); /* power-down */
+ WRITEB(port, ccr0, getccr0configS(port));
+ WRITEB(port, ccr1, getccr1configS(port));
+ WRITEB(port, ccr2, getccr2configS(port));
+ WRITEB(port, ccr3, getccr3configS(port));
+ WRITEB(port, ccr4, getccr4configS(port)); /* 32 byte receive fifo */
+ WRITEB(port, mode, getmodeS(port));
+ WRITEB(port, tic /* really rlcr */, getrlcrconfigS(port));
+ /* power-up */
+
+ switch(port->ccontrol.ccr4 & SAB82532_CCR4_RF02)
+ {
+ case SAB82532_CCR4_RF32:
+ port->recv_fifo_size = 32;
+ break;
+ case SAB82532_CCR4_RF16:
+ port->recv_fifo_size = 16;
+ break;
+ case SAB82532_CCR4_RF04:
+ port->recv_fifo_size = 4;
+ break;
+ case SAB82532_CCR4_RF02:
+ port->recv_fifo_size = 2;
+ break;
+ default:
+ port->recv_fifo_size = 32;
+ port->ccontrol.ccr4 &= ~SAB82532_CCR4_RF02;
+ break;
+ }
+
+ if(port->ccontrol.ccr2 & SAB82532_CCR2_TOE)
+ {
+ RAISE(port, txclkdir);
+ }
+ else
+ {
+ LOWER(port, txclkdir);
+ }
+
+ SET_REG_BIT(port,ccr0,SAB82532_CCR0_PU);
+
+ if(port->chip->c_cim)
+ {
+ if(port->chip->c_cim->ci_type == CIM_SP502)
+ {
+ aura_sp502_program(port, port->sigmode);
+ }
+ }
+}
+
+/* frees up all skbuffs currently */
+/* held by driver */
+void Sab8253xFreeAllFreeListSKBUFFS(SAB_PORT* priv) /* empty the skbuffer list */
+/* either on failed open */
+/* or on close*/
+{
+ struct sk_buff* skb;
+
+ if(priv->sab8253xbuflist == NULL)
+ {
+ return;
+ }
+
+ DEBUGPRINT((KERN_ALERT "sab8253x: freeing %i skbuffs.\n",
+ skb_queue_len(priv->sab8253xbuflist)));
+
+ while(skb_queue_len(priv->sab8253xbuflist) > 0)
+ {
+ skb = skb_dequeue(priv->sab8253xbuflist);
+ dev_kfree_skb_any(skb);
+ }
+ kfree(priv->sab8253xbuflist);
+ priv->sab8253xbuflist = NULL;
+}
+
+int Sab8253xSetUpLists(SAB_PORT *priv)
+{
+ if(priv->sab8253xbuflist)
+ {
+ if(priv->sab8253xc_rcvbuflist)
+ {
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+ return 0;
+ }
+ else if(priv->sab8253xc_rcvbuflist)
+ {
+ return -1;
+ }
+
+ priv->sab8253xbuflist = (struct sk_buff_head*) kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
+ if(priv->sab8253xbuflist == NULL)
+ {
+ return -1;
+ }
+ priv->sab8253xc_rcvbuflist = (struct sk_buff_head*) kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
+ if(priv->sab8253xc_rcvbuflist == NULL)
+ {
+ kfree(priv->sab8253xbuflist);
+ return -1;
+ }
+ skb_queue_head_init(priv->sab8253xbuflist);
+ skb_queue_head_init(priv->sab8253xc_rcvbuflist);
+ return 0;
+}
+
+/* sets up transmit ring and one receive sk_buff */
+
+/* set up transmit and receive
+ sk_buff control structures */
+int Sab8253xInitDescriptors2(SAB_PORT *priv, int listsize, int rbufsize)
+{
+ RING_DESCRIPTOR *desc;
+ RING_DESCRIPTOR *xdesc;
+
+ if(priv->dcontrol2.transmit != NULL)
+
+ {
+ if(priv->dcontrol2.receive != NULL)
+ {
+ return 0;
+ }
+ return -1;
+ }
+ else if(priv->dcontrol2.receive != NULL)
+ {
+ return -1;
+ }
+
+ priv->dcontrol2.transmit = (RING_DESCRIPTOR*)
+ kmalloc(sizeof(RING_DESCRIPTOR) * listsize, GFP_KERNEL);
+ /* dcontrol2 is an historical
+ artifact from when the code
+ talked to an intelligent controller */
+ if(priv->dcontrol2.transmit == NULL)
+ {
+ return -1;
+ }
+
+ priv->dcontrol2.receive = (RING_DESCRIPTOR*)
+ kmalloc(sizeof(RING_DESCRIPTOR), GFP_KERNEL); /* only one receive sk_buffer */
+ if(priv->dcontrol2.receive == NULL)
+ {
+ kfree(priv->dcontrol2.transmit);
+ priv->dcontrol2.transmit = NULL;
+ return -1;
+ }
+
+ for(xdesc = priv->dcontrol2.transmit;
+ xdesc < &priv->dcontrol2.transmit[listsize - 1];
+ xdesc = &xdesc[1]) /* set up transmit descriptors */
+ {
+ xdesc->HostVaddr = NULL;
+ xdesc->VNext = &xdesc[1];
+ xdesc->Count = 0 | OWN_DRIVER;
+ xdesc->crc = 0;
+ xdesc->sendcrc = 0;
+ xdesc->crcindex = 0;
+ }
+ xdesc->HostVaddr = NULL;
+ xdesc->VNext = priv->dcontrol2.transmit; /* circular list */
+ xdesc->Count = 0 | OWN_DRIVER;
+ xdesc->crc = 0;
+ xdesc->sendcrc = 0;
+ xdesc->crcindex = 0;
+
+ desc = priv->dcontrol2.receive; /* only need one descriptor for receive */
+ desc->HostVaddr = NULL;
+ desc->VNext = &desc[0];
+
+ desc = priv->dcontrol2.receive;
+ desc->HostVaddr = dev_alloc_skb(rbufsize);
+ if(desc->HostVaddr == NULL)
+ {
+ printk(KERN_ALERT "Unable to allocate skb_buffers (rx 0).\n");
+ printk(KERN_ALERT "Driver initialization failed.\n");
+ kfree(priv->dcontrol2.transmit);
+ kfree(priv->dcontrol2.receive);
+ priv->dcontrol2.transmit = NULL; /* get rid of descriptor ring */
+ priv->dcontrol2.receive = NULL; /* get rid of descriptor */
+ /* probably should do some deallocation of sk_buffs*/
+ /* but will take place in the open */
+ return -1;
+ }
+ skb_queue_head(priv->sab8253xbuflist, (struct sk_buff*) desc->HostVaddr);
+ desc->Count = rbufsize|OWN_SAB; /* belongs to int handler */
+ desc->crc = 0;
+ desc->sendcrc = 0;
+ desc->crcindex = 0;
+
+ /* setup the various pointers */
+ priv->active2 = priv->dcontrol2; /* insert new skbuff */
+ priv->sabnext2 = priv->dcontrol2; /* transmit from here */
+
+ return 0;
+}
+
+/* loads program, waits for PPC */
+/* and completes initialization*/
+
+void Sab8253xCleanUpTransceiveN(SAB_PORT* priv)
+{
+ Sab8253xFreeAllFreeListSKBUFFS(priv);
+ Sab8253xFreeAllReceiveListSKBUFFS(priv);
+
+ /* these are also cleaned up in the module cleanup routine */
+ /* should probably only be done here */
+ if(priv->dcontrol2.receive)
+ {
+ kfree(priv->dcontrol2.receive);
+ priv->dcontrol2.receive = NULL;
+ }
+ if(priv->dcontrol2.transmit)
+ {
+ kfree(priv->dcontrol2.transmit);
+ priv->dcontrol2.transmit = NULL;
+ }
+ priv->active2 = priv->dcontrol2;
+ priv->sabnext2 = priv->dcontrol2;
+}
+
+void Sab8253xFreeAllReceiveListSKBUFFS(SAB_PORT* priv) /* empty the skbuffer list */
+/* either on failed open */
+/* or on close*/
+{
+ struct sk_buff* skb;
+
+ if(priv->sab8253xc_rcvbuflist == NULL)
+ {
+ return;
+ }
+
+ DEBUGPRINT((KERN_ALERT "sab8253x: freeing %i skbuffs.\n",
+ skb_queue_len(priv->sab8253xc_rcvbuflist)));
+
+ while(skb_queue_len(priv->sab8253xc_rcvbuflist) > 0)
+ {
+ skb = skb_dequeue(priv->sab8253xc_rcvbuflist);
+ dev_kfree_skb_any(skb);
+ }
+ kfree(priv->sab8253xc_rcvbuflist);
+ priv->sab8253xc_rcvbuflist = NULL;
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+
+void sab8253x_change_speedN(struct sab_port *port)
+{
+ unsigned long flags;
+ unsigned char ccr2=0, ccr4=0, ebrg=0;
+ int bits = 8;
+
+#ifdef DEBUGGING
+ printk("Change speed! ");
+#endif
+
+ if(!sab8253x_baud(port, (port->baud)*2, &ebrg, &ccr2, &ccr4, &(port->baud)))
+ {
+ printk("Aurora Warning. baudrate %ld could not be set! Using 115200", port->baud);
+ port->baud = 115200;
+ sab8253x_baud(port, (port->baud*2), &ebrg, &ccr2, &ccr4, &(port->baud));
+ }
+
+ if (port->baud)
+ {
+ port->timeout = (port->xmit_fifo_size * HZ * bits) / port->baud;
+ port->cec_timeout = port->tec_timeout >> 2;
+ }
+ else
+ {
+ port->timeout = 0;
+ port->cec_timeout = SAB8253X_MAX_CEC_DELAY;
+ }
+ port->timeout += HZ / 50; /* Add .02 seconds of slop */
+
+ save_flags(flags);
+ cli();
+ sab8253x_cec_wait(port);
+
+ WRITEB(port, bgr, ebrg);
+ WRITEB(port, ccr2, READB(port, ccr2) & ~(0xc0)); /* clear out current baud rage */
+ WRITEB(port, ccr2, READB(port, ccr2) | ccr2);
+ WRITEB(port, ccr4, (READB(port,ccr4) & ~SAB82532_CCR4_EBRG) | ccr4);
+
+ if (port->flags & FLAG8253X_CTS_FLOW)
+ {
+ WRITEB(port, mode, READB(port,mode) & ~(SAB82532_MODE_RTS));
+ port->interrupt_mask1 &= ~(SAB82532_IMR1_CSC);
+ WRITEB(port, imr1, port->interrupt_mask1);
+ }
+ else
+ {
+ WRITEB(port, mode, READB(port,mode) | SAB82532_MODE_RTS);
+ port->interrupt_mask1 |= SAB82532_IMR1_CSC;
+ WRITEB(port, imr1, port->interrupt_mask1);
+ }
+ WRITEB(port, mode, READB(port, mode) | SAB82532_MODE_RAC);
+ restore_flags(flags);
+}
+
+void sab8253x_shutdownN(struct sab_port *port)
+{
+ unsigned long flags;
+
+ if (!(port->flags & FLAG8253X_INITIALIZED))
+ {
+ return;
+ }
+
+ save_flags(flags); cli(); /* Disable interrupts */
+
+ /*
+ * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+ * here so the queue might never be waken up
+ */
+ wake_up_interruptible(&port->delta_msr_wait);
+
+ /* Disable Interrupts */
+
+ port->interrupt_mask0 = 0xff;
+ WRITEB(port, imr0, port->interrupt_mask0);
+ port->interrupt_mask1 = 0xff;
+ WRITEB(port, imr1, port->interrupt_mask1);
+
+ LOWER(port,rts);
+ LOWER(port,dtr);
+
+ /* Disable Receiver */
+ CLEAR_REG_BIT(port,mode,SAB82532_MODE_RAC);
+
+ /* Power Down */
+ CLEAR_REG_BIT(port,ccr0,SAB82532_CCR0_PU);
+
+ port->flags &= ~FLAG8253X_INITIALIZED;
+ restore_flags(flags);
+}
+
+int sab8253x_block_til_ready(struct tty_struct *tty, struct file * filp,
+ struct sab_port *port)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int retval;
+ int do_clocal = 0;
+ unsigned long flags;
+
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (tty_hung_up_p(filp) ||
+ (port->flags & FLAG8253X_CLOSING))
+ {
+ if (port->flags & FLAG8253X_CLOSING)
+ {
+ interruptible_sleep_on(&port->close_wait); /* finish up previous close */
+ }
+#ifdef SERIAL_DO_RESTART
+ if (port->flags & FLAG8253X_HUP_NOTIFY)
+ {
+ return -EAGAIN;
+ }
+ else
+ {
+ return -ERESTARTSYS;
+ }
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT)
+ {
+ if (port->flags & FLAG8253X_NORMAL_ACTIVE)
+ {
+ return -EBUSY; /* async, sync tty or network driver active */
+ }
+ if ((port->flags & FLAG8253X_CALLOUT_ACTIVE) &&
+ (port->flags & FLAG8253X_SESSION_LOCKOUT) &&
+ (port->session != current->session))
+ {
+ return -EBUSY;
+ }
+ if ((port->flags & FLAG8253X_CALLOUT_ACTIVE) &&
+ (port->flags & FLAG8253X_PGRP_LOCKOUT) &&
+ (port->pgrp != current->pgrp))
+ {
+ return -EBUSY;
+ }
+ port->flags |= FLAG8253X_CALLOUT_ACTIVE; /* doing a callout */
+ return 0;
+ }
+
+ /* sort out async vs sync tty, not call out */
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ */
+
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR)))
+ {
+ if (port->flags & FLAG8253X_CALLOUT_ACTIVE)
+ {
+ return -EBUSY;
+ }
+ port->flags |= FLAG8253X_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ if (port->flags & FLAG8253X_CALLOUT_ACTIVE)
+ {
+ if (port->normal_termios.c_cflag & CLOCAL)
+ {
+ do_clocal = 1;
+ }
+ }
+ else if (tty->termios->c_cflag & CLOCAL)
+ {
+ do_clocal = 1;
+ }
+
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, port->count is dropped by one, so that
+ * sab8253x_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+
+ /* The port decrement logic is probably */
+ /* broken -- hence if def'd out -- it does*/
+ retval = 0;
+ add_wait_queue(&port->open_wait, &wait); /* starts the wait but does not block here */
+ port->blocked_open++;
+ while (1)
+ {
+ save_flags(flags);
+ cli();
+ if (!(port->flags & FLAG8253X_CALLOUT_ACTIVE) &&
+ (tty->termios->c_cflag & CBAUD))
+ {
+ RAISE(port, dtr);
+ RAISE(port, rts); /* maybe not correct for sync */
+ /*
+ * ??? Why changing the mode here?
+ * port->regs->rw.mode |= SAB82532_MODE_FRTS;
+ * port->regs->rw.mode &= ~(SAB82532_MODE_RTS);
+ */
+ }
+ restore_flags(flags);
+ current->state = TASK_INTERRUPTIBLE;
+ if (tty_hung_up_p(filp) ||
+ !(port->flags & FLAG8253X_INITIALIZED))
+ {
+#ifdef SERIAL_DO_RESTART
+ if (port->flags & FLAG8253X_HUP_NOTIFY)
+ {
+ retval = -EAGAIN;
+ }
+ else
+ {
+ retval = -ERESTARTSYS;
+ }
+#else
+ retval = -EAGAIN;
+#endif
+ break;
+ }
+ if (!(port->flags & FLAG8253X_CALLOUT_ACTIVE) &&
+ !(port->flags & FLAG8253X_CLOSING) &&
+ (do_clocal || ISON(port,dcd)))
+ {
+ break;
+ }
+#ifdef DEBUG_OPEN
+ printk("sab8253x_block_til_ready:2 flags = 0x%x\n",port->flags);
+#endif
+ if (signal_pending(current))
+ {
+ retval = -ERESTARTSYS;
+ break;
+ }
+#ifdef DEBUG_OPEN
+ printk("sab8253x_block_til_ready blocking: ttyS%d, count = %d, flags = %x, clocal = %d, vstr = %02x\n",
+ port->line, port->count, port->flags, do_clocal, READB(port,vstr));
+#endif
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&port->open_wait, &wait);
+ port->blocked_open--;
+#ifdef DEBUG_OPEN
+ printk("sab8253x_block_til_ready after blocking: ttys%d, count = %d\n",
+ port->line, port->count);
+#endif
+ if (retval)
+ {
+ return retval;
+ }
+ port->flags |= FLAG8253X_NORMAL_ACTIVE;
+ return 0;
+}
+
+/*
+ * sab8253x_wait_until_sent() --- wait until the transmitter is empty
+ */
+void sab8253x_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+ unsigned long orig_jiffies, char_time;
+
+ if (sab8253x_serial_paranoia_check(port,tty->device,"sab8253x_wait_until_sent"))
+ {
+ return;
+ }
+
+ orig_jiffies = jiffies;
+ /*
+ * Set the check interval to be 1/5 of the estimated time to
+ * send a single character, and make it at least 1. The check
+ * interval should also be less than the timeout.
+ *
+ * Note: we have to use pretty tight timings here to satisfy
+ * the NIST-PCTS.
+ */
+ char_time = (port->timeout - HZ/50) / port->xmit_fifo_size;
+ char_time = char_time / 5;
+ if (char_time == 0)
+ {
+ char_time = 1;
+ }
+ if (timeout)
+ {
+ char_time = MIN(char_time, timeout);
+ }
+ while ((Sab8253xCountTransmit(port) > 0) || !port->all_sent)
+ {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(char_time);
+ if (signal_pending(current))
+ {
+ break;
+ }
+ if (timeout && time_after(jiffies, orig_jiffies + timeout))
+ {
+ break;
+ }
+ }
+}
+
+void sab8253x_flush_buffer(struct tty_struct *tty)
+{
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+ unsigned long flags;
+ register RING_DESCRIPTOR *freeme;
+
+ if(sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_flush_buffer"))
+ {
+ return;
+ }
+
+ if(port->sabnext2.transmit == NULL)
+ {
+ return;
+ }
+
+ save_flags(flags);
+ cli(); /* need to turn off ints because mucking
+ with sabnext2 */
+#ifndef FREEININTERRUPT
+ freeme = port->active2.transmit;
+ do /* just go all around */
+ {
+ if(freeme->HostVaddr)
+ {
+ skb_unlink((struct sk_buff*)freeme->HostVaddr);
+ dev_kfree_skb_any((struct sk_buff*)freeme->HostVaddr);
+ freeme->HostVaddr = NULL;
+ }
+ freeme->sendcrc = 0;
+ freeme->crcindex = 0;
+ freeme->Count = OWN_DRIVER;
+ freeme = (RING_DESCRIPTOR*) freeme->VNext;
+ }
+ while(freeme != port->active2.transmit);
+#else /* buffers only from sabnext2.transmit to active2.transmit */
+ while((port->sabnext2.transmit->Count & OWNER) == OWN_SAB) /* clear out stuff waiting to be transmitted */
+ {
+ freeme = port->sabnext2.transmit;
+ if(freeme->HostVaddr)
+ {
+ skb_unlink((struct sk_buff*)freeme->HostVaddr);
+ dev_kfree_skb_any((struct sk_buff*)freeme->HostVaddr);
+ freeme->HostVaddr = NULL;
+ }
+ freeme->sendcrc = 0;
+ freeme->crcindex = 0;
+ freeme->Count = OWN_DRIVER;
+ port->sabnext2.transmit = freeme->VNext;
+ }
+#endif
+ port->sabnext2.transmit = port->active2.transmit; /* should already be equal to be sure */
+ sab8253x_cec_wait(port);
+ WRITEB(port,cmdr,SAB82532_CMDR_XRES);
+ restore_flags(flags);
+
+ wake_up_interruptible(&tty->write_wait); /* wake up tty driver */
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ {
+ (*tty->ldisc.write_wakeup)(tty);
+ }
+}
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)