specialix.c
来自「linux 内核源代码」· C语言 代码 · 共 2,562 行 · 第 1/5 页
C
2,562 行
/* * specialix.c -- specialix IO8+ multiport serial driver. * * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) * * Specialix pays for the development and support of this driver. * Please DO contact io8-linux@specialix.co.uk if you require * support. But please read the documentation (specialix.txt) * first. * * This driver was developped in the BitWizard linux device * driver service. If you require a linux device driver for your * product, please contact devices@BitWizard.nl for a quote. * * This code is firmly based on the riscom/8 serial driver, * written by Dmitry Gorodchanin. The specialix IO8+ card * programming information was obtained from the CL-CD1865 Data * Book, and Specialix document number 6200059: IO8+ Hardware * Functional Specification. * * 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. * * This program is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, * USA. * * Revision history: * * Revision 1.0: April 1st 1997. * Initial release for alpha testing. * Revision 1.1: April 14th 1997. * Incorporated Richard Hudsons suggestions, * removed some debugging printk's. * Revision 1.2: April 15th 1997. * Ported to 2.1.x kernels. * Revision 1.3: April 17th 1997 * Backported to 2.0. (Compatibility macros). * Revision 1.4: April 18th 1997 * Fixed DTR/RTS bug that caused the card to indicate * "don't send data" to a modem after the password prompt. * Fixed bug for premature (fake) interrupts. * Revision 1.5: April 19th 1997 * fixed a minor typo in the header file, cleanup a little. * performance warnings are now MAXed at once per minute. * Revision 1.6: May 23 1997 * Changed the specialix=... format to include interrupt. * Revision 1.7: May 27 1997 * Made many more debug printk's a compile time option. * Revision 1.8: Jul 1 1997 * port to linux-2.1.43 kernel. * Revision 1.9: Oct 9 1998 * Added stuff for the IO8+/PCI version. * Revision 1.10: Oct 22 1999 / Jan 21 2000. * Added stuff for setserial. * Nicolas Mailhot (Nicolas.Mailhot@email.enst.fr) * */#define VERSION "1.11"/* * There is a bunch of documentation about the card, jumpers, config * settings, restrictions, cables, device names and numbers in * Documentation/specialix.txt */#include <linux/module.h>#include <asm/io.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/ioport.h>#include <linux/interrupt.h>#include <linux/errno.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/mm.h>#include <linux/serial.h>#include <linux/fcntl.h>#include <linux/major.h>#include <linux/delay.h>#include <linux/pci.h>#include <linux/init.h>#include <asm/uaccess.h>#include "specialix_io8.h"#include "cd1865.h"/* This driver can spew a whole lot of debugging output at you. If you need maximum performance, you should disable the DEBUG define. To aid in debugging in the field, I'm leaving the compile-time debug features enabled, and disable them "runtime". That allows me to instruct people with problems to enable debugging without requiring them to recompile...*/#define DEBUGstatic int sx_debug;static int sx_rxfifo = SPECIALIX_RXFIFO;#ifdef DEBUG#define dprintk(f, str...) if (sx_debug & f) printk (str)#else#define dprintk(f, str...) /* nothing */#endif#define SX_DEBUG_FLOW 0x0001#define SX_DEBUG_DATA 0x0002#define SX_DEBUG_PROBE 0x0004#define SX_DEBUG_CHAN 0x0008#define SX_DEBUG_INIT 0x0010#define SX_DEBUG_RX 0x0020#define SX_DEBUG_TX 0x0040#define SX_DEBUG_IRQ 0x0080#define SX_DEBUG_OPEN 0x0100#define SX_DEBUG_TERMIOS 0x0200#define SX_DEBUG_SIGNALS 0x0400#define SX_DEBUG_FIFO 0x0800#define func_enter() dprintk (SX_DEBUG_FLOW, "io8: enter %s\n",__FUNCTION__)#define func_exit() dprintk (SX_DEBUG_FLOW, "io8: exit %s\n", __FUNCTION__)#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1)/* Configurable options: *//* Am I paranoid or not ? ;-) */#define SPECIALIX_PARANOIA_CHECK/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help) When the IRQ routine leaves the chip in a state that is keeps on requiring attention, the timer doesn't help either. */#undef SPECIALIX_TIMER#ifdef SPECIALIX_TIMERstatic int sx_poll = HZ;#endif/* * The following defines are mostly for testing purposes. But if you need * some nice reporting in your syslog, you can define them also. */#undef SX_REPORT_FIFO#undef SX_REPORT_OVERRUN#ifdef CONFIG_SPECIALIX_RTSCTS#define SX_CRTSCTS(bla) 1#else#define SX_CRTSCTS(tty) C_CRTSCTS(tty)#endif/* Used to be outb (0xff, 0x80); */#define short_pause() udelay (1)#define SPECIALIX_LEGAL_FLAGS \ (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \ ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \ ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)#undef RS_EVENT_WRITE_WAKEUP#define RS_EVENT_WRITE_WAKEUP 0static struct tty_driver *specialix_driver;static struct specialix_board sx_board[SX_NBOARD] = { { 0, SX_IOBASE1, 9, }, { 0, SX_IOBASE2, 11, }, { 0, SX_IOBASE3, 12, }, { 0, SX_IOBASE4, 15, },};static struct specialix_port sx_port[SX_NBOARD * SX_NPORT];#ifdef SPECIALIX_TIMERstatic struct timer_list missed_irq_timer;static irqreturn_t sx_interrupt(int irq, void * dev_id);#endifstatic inline int sx_paranoia_check(struct specialix_port const * port, char *name, const char *routine){#ifdef SPECIALIX_PARANOIA_CHECK static const char *badmagic = KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n"; static const char *badinfo = KERN_ERR "sx: Warning: null specialix port for device %s in %s\n"; if (!port) { printk(badinfo, name, routine); return 1; } if (port->magic != SPECIALIX_MAGIC) { printk(badmagic, name, routine); return 1; }#endif return 0;}/* * * Service functions for specialix IO8+ driver. * *//* Get board number from pointer */static inline int board_No (struct specialix_board * bp){ return bp - sx_board;}/* Get port number from pointer */static inline int port_No (struct specialix_port const * port){ return SX_PORT(port - sx_port);}/* Get pointer to board from pointer to port */static inline struct specialix_board * port_Board(struct specialix_port const * port){ return &sx_board[SX_BOARD(port - sx_port)];}/* Input Byte from CL CD186x register */static inline unsigned char sx_in(struct specialix_board * bp, unsigned short reg){ bp->reg = reg | 0x80; outb (reg | 0x80, bp->base + SX_ADDR_REG); return inb (bp->base + SX_DATA_REG);}/* Output Byte to CL CD186x register */static inline void sx_out(struct specialix_board * bp, unsigned short reg, unsigned char val){ bp->reg = reg | 0x80; outb (reg | 0x80, bp->base + SX_ADDR_REG); outb (val, bp->base + SX_DATA_REG);}/* Input Byte from CL CD186x register */static inline unsigned char sx_in_off(struct specialix_board * bp, unsigned short reg){ bp->reg = reg; outb (reg, bp->base + SX_ADDR_REG); return inb (bp->base + SX_DATA_REG);}/* Output Byte to CL CD186x register */static inline void sx_out_off(struct specialix_board * bp, unsigned short reg, unsigned char val){ bp->reg = reg; outb (reg, bp->base + SX_ADDR_REG); outb (val, bp->base + SX_DATA_REG);}/* Wait for Channel Command Register ready */static inline void sx_wait_CCR(struct specialix_board * bp){ unsigned long delay, flags; unsigned char ccr; for (delay = SX_CCR_TIMEOUT; delay; delay--) { spin_lock_irqsave(&bp->lock, flags); ccr = sx_in(bp, CD186x_CCR); spin_unlock_irqrestore(&bp->lock, flags); if (!ccr) return; udelay (1); } printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));}/* Wait for Channel Command Register ready */static inline void sx_wait_CCR_off(struct specialix_board * bp){ unsigned long delay; unsigned char crr; unsigned long flags; for (delay = SX_CCR_TIMEOUT; delay; delay--) { spin_lock_irqsave(&bp->lock, flags); crr = sx_in_off(bp, CD186x_CCR); spin_unlock_irqrestore(&bp->lock, flags); if (!crr) return; udelay (1); } printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));}/* * specialix IO8+ IO range functions. */static inline int sx_request_io_range(struct specialix_board * bp){ return request_region(bp->base, bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE, "specialix IO8+") == NULL;}static inline void sx_release_io_range(struct specialix_board * bp){ release_region(bp->base, bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE);}/* Set the IRQ using the RTS lines that run to the PAL on the board.... */static int sx_set_irq ( struct specialix_board *bp){ int virq; int i; unsigned long flags; if (bp->flags & SX_BOARD_IS_PCI) return 1; switch (bp->irq) { /* In the same order as in the docs... */ case 15: virq = 0;break; case 12: virq = 1;break; case 11: virq = 2;break; case 9: virq = 3;break; default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq); return 0; } spin_lock_irqsave(&bp->lock, flags); for (i=0;i<2;i++) { sx_out(bp, CD186x_CAR, i); sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0); } spin_unlock_irqrestore(&bp->lock, flags); return 1;}/* Reset and setup CD186x chip */static int sx_init_CD186x(struct specialix_board * bp){ unsigned long flags; int scaler; int rv = 1; func_enter(); sx_wait_CCR_off(bp); /* Wait for CCR ready */ spin_lock_irqsave(&bp->lock, flags); sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */ spin_unlock_irqrestore(&bp->lock, flags); msleep(50); /* Delay 0.05 sec */ spin_lock_irqsave(&bp->lock, flags); sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */ sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */ sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */ sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */ sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */ /* Set RegAckEn */ sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN); /* Setting up prescaler. We need 4 ticks per 1 ms */ scaler = SX_OSCFREQ/SPECIALIX_TPS; sx_out_off(bp, CD186x_PPRH, scaler >> 8); sx_out_off(bp, CD186x_PPRL, scaler & 0xff); spin_unlock_irqrestore(&bp->lock, flags); if (!sx_set_irq (bp)) { /* Figure out how to pass this along... */ printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq); rv = 0; } func_exit(); return rv;}static int read_cross_byte (struct specialix_board *bp, int reg, int bit){ int i; int t; unsigned long flags; spin_lock_irqsave(&bp->lock, flags); for (i=0, t=0;i<8;i++) { sx_out_off (bp, CD186x_CAR, i); if (sx_in_off (bp, reg) & bit) t |= 1 << i; } spin_unlock_irqrestore(&bp->lock, flags); return t;}#ifdef SPECIALIX_TIMERvoid missed_irq (unsigned long data){ unsigned char irq; unsigned long flags; struct specialix_board *bp = (struct specialix_board *)data; spin_lock_irqsave(&bp->lock, flags); irq = sx_in ((struct specialix_board *)data, CD186x_SRSR) & (SRSR_RREQint | SRSR_TREQint | SRSR_MREQint); spin_unlock_irqrestore(&bp->lock, flags); if (irq) { printk (KERN_INFO "Missed interrupt... Calling int from timer. \n"); sx_interrupt (((struct specialix_board *)data)->irq, (void*)data); } mod_timer(&missed_irq_timer, jiffies + sx_poll);}#endif/* Main probing routine, also sets irq. */static int sx_probe(struct specialix_board *bp){ unsigned char val1, val2;#if 0 int irqs = 0; int retries;#endif int rev; int chip; func_enter(); if (sx_request_io_range(bp)) { func_exit(); return 1; } /* Are the I/O ports here ? */ sx_out_off(bp, CD186x_PPRL, 0x5a); short_pause (); val1 = sx_in_off(bp, CD186x_PPRL); sx_out_off(bp, CD186x_PPRL, 0xa5); short_pause (); val2 = sx_in_off(bp, CD186x_PPRL); if ((val1 != 0x5a) || (val2 != 0xa5)) { printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n", board_No(bp), bp->base); sx_release_io_range(bp); func_exit(); return 1; } /* Check the DSR lines that Specialix uses as board identification */ val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR); val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS); dprintk (SX_DEBUG_INIT, "sx%d: DSR lines are: %02x, rts lines are: %02x\n", board_No(bp), val1, val2); /* They managed to switch the bit order between the docs and the IO8+ card. The new PCI card now conforms to old docs. They changed the PCI docs to reflect the situation on the old card. */ val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2; if (val1 != val2) { printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n", board_No(bp), val2, bp->base, val1); sx_release_io_range(bp); func_exit(); return 1; }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?