📄 sio_ioc4.c
字号:
/* * 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 + -