📄 8253xutl.c
字号:
/* -*- 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#endifvoid 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 intsab8253x_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 intsab8253x_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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -