⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sio_ioc4.c

📁 linux-2.4.29操作系统的源码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * This file is subject to the terms and conditions of the GNU General Public * License.  See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 2003 Silicon Graphics, Inc.  All Rights Reserved. *//* * This is a lower level module for the modular serial I/O driver. This * module implements all hardware dependent functions for doing serial * I/O on the IOC4 serial ports. */#include <linux/config.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <asm/sn/types.h>#include <asm/sn/sgi.h>#include <asm/sn/invent.h>#include <asm/sn/driver.h>#include <asm/sn/iograph.h>#include <asm/param.h>#include <asm/atomic.h>#include <asm/delay.h>#include <asm/semaphore.h>#include <asm/sn/pio.h>#include <asm/sn/xtalk/xwidget.h>#include <asm/sn/io.h>#include <asm/sn/pci/pci_defs.h>#include <asm/sn/pci/pciio.h>#include <asm/sn/ioc4.h>#include <asm/sn/serialio.h>#include <asm/sn/uart16550.h>/* #define IOC4_SIO_DEBUG *//* define USE_64BIT_DMA */#define PENDING(port) (PCI_INW(&(port)->ip_ioc4->sio_ir) & port->ip_ienb)/* Default to 4k buffers */#ifdef IOC4_1K_BUFFERS#define RING_BUF_SIZE 1024#define IOC4_BUF_SIZE_BIT 0#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_1K#else#define RING_BUF_SIZE 4096#define IOC4_BUF_SIZE_BIT IOC4_SBBR_L_SIZE#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_4K#endif#define TOTAL_RING_BUF_SIZE (RING_BUF_SIZE * 4)#if PAGE_SIZE < TOTAL_RING_BUF_SIZE#include <sys/pfdat.h>#endif#ifdef DPRINTF#define dprintf(x) printk x#else#define dprintf(x)#endif#define	NEWA(ptr,n)	(ptr = snia_kmem_zalloc((n)*sizeof (*(ptr))))#define	contig_memalloc(a,b,c)	kmem_zalloc(PAGE_SIZE * (a))#define sio_port_islocked(a)	0	// FIXME: ?????#define KM_PHYSCONTIG   0x0008#define VM_DIRECT       KM_PHYSCONTIG#define VM_PHYSCONTIG   KM_PHYSCONTIG#ifdef DEBUG#define PROGRESS()	printk("%s : %d\n", __FUNCTION__, __LINE__)#define NOT_PROGRESS()	printk("%s : %d - Error\n", __FUNCTION__, __LINE__)#else#define PROGRESS()	;#define NOT_PROGRESS()	;#endifstatic __inline__ void *kvpalloc(size_t size, int flags, int colour){        if (flags & (VM_DIRECT|VM_PHYSCONTIG)) {                int order = 0;                while ((PAGE_SIZE << order) < (size << PAGE_SHIFT))                        order++;                return (void *) __get_free_pages(GFP_KERNEL, order);        } else                return vmalloc(size << PAGE_SHIFT);}/* Local port info for the IOC4 serial ports.  This contains as its * first member the global sio port private data. */typedef struct ioc4port {    sioport_t		ip_sioport;	/* Must be first struct entry! */    vertex_hdl_t	ip_conn_vhdl;	/* vhdl to use for pciio requests */    vertex_hdl_t	ip_port_vhdl;	/* vhdl for the serial port */    /* Base piomap addr of the ioc4 board this port is on     * and associated serial map;  serial map includes uart registers.     */    ioc4_mem_t	       *ip_ioc4;    ioc4_sregs_t       *ip_serial;    ioc4_uart_t        *ip_uart;    /* Ring buffer page for this port */    caddr_t		ip_ring_buf_k0;	/* Ring buffer location in K0 space */    /* Rings for this port */    struct ring	       *ip_inring;    struct ring	       *ip_outring;    /* Hook to port specific values for this port */    struct hooks       *ip_hooks;    int			ip_flags;    /* Cache of DCD/CTS bits last received */    char                ip_modem_bits;    /* Various rx/tx parameters */    int                 ip_baud;    int                 ip_tx_lowat;    int                 ip_rx_timeout;    /* Copy of notification bits */    int                 ip_notify;    /* Shadow copies of various registers so we don't need to PIO     * read them constantly     */    ioc4reg_t           ip_ienb;          /* Enabled interrupts */    ioc4reg_t           ip_sscr;    ioc4reg_t           ip_tx_prod;    ioc4reg_t           ip_rx_cons;    /* Back pointer to ioc4 soft area */    void               *ip_ioc4_soft;} ioc4port_t;#if DEBUG#define     MAXSAVEPORT 256static int         next_saveport = 0;static ioc4port_t *saveport[MAXSAVEPORT];#endif/* TX low water mark.  We need to notify the driver whenever TX is getting * close to empty so it can refill the TX buffer and keep things going. * Let's assume that if we interrupt 1 ms before the TX goes idle, we'll * have no trouble getting in more chars in time (I certainly hope so). */#define TX_LOWAT_LATENCY      1000#define TX_LOWAT_HZ          (1000000 / TX_LOWAT_LATENCY)#define TX_LOWAT_CHARS(baud) (baud / 10 / TX_LOWAT_HZ)/* Flags per port */#define INPUT_HIGH	0x01#define DCD_ON		0x02#define LOWAT_WRITTEN	0x04#define READ_ABORTED	0x08#define TX_DISABLED	0x10/* Get local port type from global sio port type */#define LPORT(port) ((ioc4port_t *) (port))/* Get global port from local port type */#define GPORT(port) ((sioport_t *) (port))/* Since each port has different register offsets and bitmasks * for everything, we'll store those that we need in tables so we * don't have to be constantly checking the port we are dealing with. */struct hooks {    ioc4reg_t intr_delta_dcd;    ioc4reg_t intr_delta_cts;    ioc4reg_t intr_tx_mt;    ioc4reg_t intr_rx_timer;    ioc4reg_t intr_rx_high;    ioc4reg_t intr_tx_explicit;    ioc4reg_t intr_dma_error;    ioc4reg_t intr_clear;    ioc4reg_t intr_all;    char      rs422_select_pin;};static struct hooks hooks_array[4] ={    /* Values for port 0 */    {	IOC4_SIO_IR_S0_DELTA_DCD,	IOC4_SIO_IR_S0_DELTA_CTS,	IOC4_SIO_IR_S0_TX_MT,	IOC4_SIO_IR_S0_RX_TIMER,	IOC4_SIO_IR_S0_RX_HIGH,	IOC4_SIO_IR_S0_TX_EXPLICIT,	IOC4_OTHER_IR_S0_MEMERR,	(IOC4_SIO_IR_S0_TX_MT | IOC4_SIO_IR_S0_RX_FULL |         IOC4_SIO_IR_S0_RX_HIGH | IOC4_SIO_IR_S0_RX_TIMER |         IOC4_SIO_IR_S0_DELTA_DCD | IOC4_SIO_IR_S0_DELTA_CTS |	 IOC4_SIO_IR_S0_INT | IOC4_SIO_IR_S0_TX_EXPLICIT),	IOC4_SIO_IR_S0,	IOC4_GPPR_UART0_MODESEL_PIN,    },    /* Values for port 1 */    {	IOC4_SIO_IR_S1_DELTA_DCD,	IOC4_SIO_IR_S1_DELTA_CTS,	IOC4_SIO_IR_S1_TX_MT,	IOC4_SIO_IR_S1_RX_TIMER,	IOC4_SIO_IR_S1_RX_HIGH,	IOC4_SIO_IR_S1_TX_EXPLICIT,	IOC4_OTHER_IR_S1_MEMERR,	(IOC4_SIO_IR_S1_TX_MT | IOC4_SIO_IR_S1_RX_FULL |         IOC4_SIO_IR_S1_RX_HIGH | IOC4_SIO_IR_S1_RX_TIMER |         IOC4_SIO_IR_S1_DELTA_DCD | IOC4_SIO_IR_S1_DELTA_CTS |	 IOC4_SIO_IR_S1_INT | IOC4_SIO_IR_S1_TX_EXPLICIT),	IOC4_SIO_IR_S1,	IOC4_GPPR_UART1_MODESEL_PIN,    },    /* Values for port 2 */    {	IOC4_SIO_IR_S2_DELTA_DCD,	IOC4_SIO_IR_S2_DELTA_CTS,	IOC4_SIO_IR_S2_TX_MT,	IOC4_SIO_IR_S2_RX_TIMER,	IOC4_SIO_IR_S2_RX_HIGH,	IOC4_SIO_IR_S2_TX_EXPLICIT,	IOC4_OTHER_IR_S2_MEMERR,	(IOC4_SIO_IR_S2_TX_MT | IOC4_SIO_IR_S2_RX_FULL |         IOC4_SIO_IR_S2_RX_HIGH | IOC4_SIO_IR_S2_RX_TIMER |         IOC4_SIO_IR_S2_DELTA_DCD | IOC4_SIO_IR_S2_DELTA_CTS |	 IOC4_SIO_IR_S2_INT | IOC4_SIO_IR_S2_TX_EXPLICIT),	IOC4_SIO_IR_S2,	IOC4_GPPR_UART2_MODESEL_PIN,    },    /* Values for port 3 */    {	IOC4_SIO_IR_S3_DELTA_DCD,	IOC4_SIO_IR_S3_DELTA_CTS,	IOC4_SIO_IR_S3_TX_MT,	IOC4_SIO_IR_S3_RX_TIMER,	IOC4_SIO_IR_S3_RX_HIGH,	IOC4_SIO_IR_S3_TX_EXPLICIT,	IOC4_OTHER_IR_S3_MEMERR,	(IOC4_SIO_IR_S3_TX_MT | IOC4_SIO_IR_S3_RX_FULL |         IOC4_SIO_IR_S3_RX_HIGH | IOC4_SIO_IR_S3_RX_TIMER |         IOC4_SIO_IR_S3_DELTA_DCD | IOC4_SIO_IR_S3_DELTA_CTS |	 IOC4_SIO_IR_S3_INT | IOC4_SIO_IR_S3_TX_EXPLICIT),	IOC4_SIO_IR_S3,	IOC4_GPPR_UART3_MODESEL_PIN,    }};/* Macros to get into the port hooks.  Require a variable called * hooks set to port->hooks */#define H_INTR_TX_MT	   hooks->intr_tx_mt#define H_INTR_RX_TIMER    hooks->intr_rx_timer#define H_INTR_RX_HIGH	   hooks->intr_rx_high#define H_INTR_TX_EXPLICIT hooks->intr_tx_explicit#define H_INTR_DMA_ERROR   hooks->intr_dma_error#define H_INTR_CLEAR	   hooks->intr_clear#define H_INTR_DELTA_DCD   hooks->intr_delta_dcd#define H_INTR_DELTA_CTS   hooks->intr_delta_cts#define H_INTR_ALL	   hooks->intr_all#define H_RS422		   hooks->rs422_select_pin/* A ring buffer entry */struct ring_entry {    union {	struct {	    uint32_t alldata;	    uint32_t allsc;	} all;	struct {	    char data[4];	/* data bytes */	    char sc[4];		/* status/control */	} s;    } u;};/* Test the valid bits in any of the 4 sc chars using "allsc" member */#define RING_ANY_VALID \	((uint32_t) (IOC4_RXSB_MODEM_VALID | IOC4_RXSB_DATA_VALID) * 0x01010101)#define ring_sc     u.s.sc#define ring_data   u.s.data#define ring_allsc  u.all.allsc/* Number of entries per ring buffer. */#define ENTRIES_PER_RING (RING_BUF_SIZE / (int) sizeof(struct ring_entry))/* An individual ring */struct ring {    struct ring_entry entries[ENTRIES_PER_RING];};/* The whole enchilada */struct ring_buffer {        struct ring TX_0_OR_2;        struct ring RX_0_OR_2;        struct ring TX_1_OR_3;        struct ring RX_1_OR_3;};/* Get a ring from a port struct */#define RING(port, which) \    &(((struct ring_buffer *) ((port)->ip_ring_buf_k0))->which)/* Local functions: */static int ioc4_open		(sioport_t *port);static int ioc4_config		(sioport_t *port, int baud, int byte_size,				 int stop_bits, int parenb, int parodd);static int ioc4_enable_hfc	(sioport_t *port, int enable);static int ioc4_set_extclk	(sioport_t *port, int clock_factor);/* Data transmission */static int do_ioc4_write	(sioport_t *port, char *buf, int len);static int ioc4_write		(sioport_t *port, char *buf, int len);static int ioc4_sync_write	(sioport_t *port, char *buf, int len);static void ioc4_wrflush	(sioport_t *port);static int ioc4_break		(sioport_t *port, int brk);static int ioc4_enable_tx	(sioport_t *port, int enb);/* Data reception */static int ioc4_read		(sioport_t *port, char *buf, int len);/* Event notification */static int ioc4_notification	(sioport_t *port, int mask, int on);static int ioc4_rx_timeout	(sioport_t *port, int timeout);/* Modem control */static int ioc4_set_DTR		(sioport_t *port, int dtr);static int ioc4_set_RTS		(sioport_t *port, int rts);static int ioc4_query_DCD	(sioport_t *port);static int ioc4_query_CTS	(sioport_t *port);/* Output mode */static int ioc4_set_proto	(sioport_t *port, enum sio_proto proto);/* User mapped driver support */static int ioc4_get_mapid	(sioport_t *port, void *arg);static int ioc4_set_sscr	(sioport_t *port, int arg, int flag);static struct serial_calldown ioc4_calldown = {    ioc4_open,    ioc4_config,    ioc4_enable_hfc,    ioc4_set_extclk,    ioc4_write,    ioc4_sync_write,    ioc4_wrflush,	/* du flush */    ioc4_break,    ioc4_enable_tx,    ioc4_read,    ioc4_notification,    ioc4_rx_timeout,    ioc4_set_DTR,    ioc4_set_RTS,    ioc4_query_DCD,    ioc4_query_CTS,    ioc4_set_proto,    ioc4_get_mapid,    0,    0,    ioc4_set_sscr};/* Baud rate stuff */#define SET_BAUD(p, b) set_baud_ti(p, b)static int set_baud_ti(ioc4port_t *, int);#ifdef DEBUG/* Performance characterization logging */#define DEBUGINC(x,i) stats.x += istatic struct {    /* Ports present */    uint ports;    /* Ports killed */    uint killed;    /* Interrupt counts */    uint total_intr;    uint port_0_intr;    uint port_1_intr;    uint ddcd_intr;    uint dcts_intr;    uint rx_timer_intr;    uint rx_high_intr;    uint explicit_intr;    uint mt_intr;    uint mt_lowat_intr;    /* Write characteristics */    uint write_bytes;    uint write_cnt;    uint wrote_bytes;    uint tx_buf_used;    uint tx_buf_cnt;    uint tx_pio_cnt;691    /* Read characteristics */    uint read_bytes;    uint read_cnt;    uint drain;    uint drainwait;    uint resetdma;    uint read_ddcd;    uint rx_overrun;    uint parity;    uint framing;    uint brk;    uint red_bytes;    uint rx_buf_used;    uint rx_buf_cnt;    /* Errors */    uint dma_lost;    uint read_aborted;    uint read_aborted_detected;} stats;#else#define DEBUGINC(x,i)#endif/* Infinite loop detection. */#define MAXITER 1000000#define SPIN(cond, success) \{ \	 int spiniter = 0; \	 success = 1; \	 while(cond) { \		 spiniter++; \		 if (spiniter > MAXITER) { \			 success = 0; \			 break; \		 } \	 } \}static iopaddr_tring_dmatrans(vertex_hdl_t conn_vhdl, caddr_t vaddr){    extern iopaddr_t pciio_dma_addr (vertex_hdl_t, device_desc_t, paddr_t,                                     size_t, pciio_dmamap_t *, unsigned);    iopaddr_t	paddr = (iopaddr_t)vaddr;    if (conn_vhdl != GRAPH_VERTEX_NONE)#ifdef	USE_64BIT_DMA        /* Use 64-bit DMA address when the IOC4 supports it */        return pciio_dmatrans_addr (conn_vhdl, 0, paddr, TOTAL_RING_BUF_SIZE, PCIIO_DMA_A64 | PCIIO_BYTE_STREAM);#else        /* Use 32-bit DMA address for current IOC4 */ 	return pciio_dma_addr (conn_vhdl, 0, paddr, TOTAL_RING_BUF_SIZE, NULL, PCIIO_BYTE_STREAM);#endif    return paddr;}/* If interrupt routine called enable_intrs, then would need to write * mask_enable_intrs() routine. */static inline voidmask_disable_intrs(ioc4port_t *port, ioc4reg_t mask){    port->ip_ienb &= ~mask;}static voidenable_intrs(ioc4port_t *port, ioc4reg_t mask){    struct hooks *hooks = port->ip_hooks;     if ((port->ip_ienb & mask) != mask) {	IOC4_WRITE_IES(port->ip_ioc4_soft, mask, ioc4_sio_intr_type);	port->ip_ienb |= mask;    }    if (port->ip_ienb)        IOC4_WRITE_IES(port->ip_ioc4_soft, H_INTR_DMA_ERROR, ioc4_other_intr_type);}static voiddisable_intrs(ioc4port_t *port, ioc4reg_t mask){    struct hooks *hooks = port->ip_hooks;    if (port->ip_ienb & mask) {	IOC4_WRITE_IEC(port->ip_ioc4_soft, mask, ioc4_sio_intr_type);	port->ip_ienb &= ~mask;    }    if (!port->ip_ienb)        IOC4_WRITE_IEC(port->ip_ioc4_soft, H_INTR_DMA_ERROR, ioc4_other_intr_type);}/* Service any pending interrupts on the given port */static voidioc4_serial_intr(intr_arg_t arg, ioc4reg_t sio_ir){    ioc4port_t   *port = (ioc4port_t *) arg;    sioport_t    *gp = GPORT(port);    struct hooks *hooks = port->ip_hooks;    unsigned      rx_high_rd_aborted = 0;    unsigned int  flags;    PROGRESS();#ifdef NOT_YET    ASSERT(sio_port_islocked(gp) == 0);#endif    /* Possible race condition here: The TX_MT interrupt bit may be     * cleared without the intervention of the interrupt handler,     * e.g. by a write.  If the top level interrupt handler reads a     * TX_MT, then some other processor does a write, starting up     * output, then we come in here, see the TX_MT and stop DMA, the     * output started by the other processor will hang.  Thus we can     * only rely on TX_MT being legitimate if it is read while the     * port lock is held.  Therefore this bit must be ignored in the     * passed in interrupt mask which was read by the top level     * interrupt handler since the port lock was not held at the time     * it was read.  We can only rely on this bit being accurate if it     * is read while the port lock is held.  So we'll clear it for now,     * and reload it later once we have the port lock.     */    sio_ir &= ~(H_INTR_TX_MT);    SIO_LOCK_PORT(gp, flags);    dprintf(("interrupt: sio_ir 0x%x\n", sio_ir));    do {	ioc4reg_t shadow;	/* Handle a DCD change */	if (sio_ir & H_INTR_DELTA_DCD) {	    DEBUGINC(ddcd_intr, 1);	    PROGRESS();	    /* ACK the interrupt */	    PCI_OUTW(&port->ip_ioc4->sio_ir, H_INTR_DELTA_DCD);	    /* If DCD has raised, notify upper layer.  Otherwise	     * wait for a record to be posted to notify of a dropped DCD.	     */	    shadow = PCI_INW(&port->ip_serial->shadow);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -