ioc3_serial.c
来自「linux 内核源代码」· C语言 代码 · 共 2,194 行 · 第 1/4 页
C
2,194 行
/* * 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) 2005 Silicon Graphics, Inc. All Rights Reserved. *//* * This file contains a module version of the ioc3 serial driver. This * includes all the support functions needed (support functions, etc.) * and the serial driver itself. */#include <linux/errno.h>#include <linux/tty.h>#include <linux/serial.h>#include <linux/circ_buf.h>#include <linux/serial_reg.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/serial_core.h>#include <linux/ioc3.h>/* * Interesting things about the ioc3 */#define LOGICAL_PORTS 2 /* rs232(0) and rs422(1) */#define PORTS_PER_CARD 2#define LOGICAL_PORTS_PER_CARD (PORTS_PER_CARD * LOGICAL_PORTS)#define MAX_CARDS 8#define MAX_LOGICAL_PORTS (LOGICAL_PORTS_PER_CARD * MAX_CARDS)/* determine given the sio_ir what port it applies to */#define GET_PORT_FROM_SIO_IR(_x) (_x & SIO_IR_SA) ? 0 : 1/* * we have 2 logical ports (rs232, rs422) for each physical port * evens are rs232, odds are rs422 */#define GET_PHYSICAL_PORT(_x) ((_x) >> 1)#define GET_LOGICAL_PORT(_x) ((_x) & 1)#define IS_PHYSICAL_PORT(_x) !((_x) & 1)#define IS_RS232(_x) !((_x) & 1)static unsigned int Num_of_ioc3_cards;static unsigned int Submodule_slot;/* defining this will get you LOTS of great debug info *///#define DEBUG_INTERRUPTS#define DPRINT_CONFIG(_x...) ;//#define DPRINT_CONFIG(_x...) printk _x#define NOT_PROGRESS() ;//#define NOT_PROGRESS() printk("%s : fails %d\n", __FUNCTION__, __LINE__)/* number of characters we want to transmit to the lower level at a time */#define MAX_CHARS 256#define FIFO_SIZE (MAX_CHARS-1) /* it's a uchar *//* Device name we're using */#define DEVICE_NAME "ttySIOC"#define DEVICE_MAJOR 204#define DEVICE_MINOR 116/* flags for next_char_state */#define NCS_BREAK 0x1#define NCS_PARITY 0x2#define NCS_FRAMING 0x4#define NCS_OVERRUN 0x8/* cause we need SOME parameters ... */#define MIN_BAUD_SUPPORTED 1200#define MAX_BAUD_SUPPORTED 115200/* protocol types supported */#define PROTO_RS232 0#define PROTO_RS422 1/* Notification types */#define N_DATA_READY 0x01#define N_OUTPUT_LOWAT 0x02#define N_BREAK 0x04#define N_PARITY_ERROR 0x08#define N_FRAMING_ERROR 0x10#define N_OVERRUN_ERROR 0x20#define N_DDCD 0x40#define N_DCTS 0x80#define N_ALL_INPUT (N_DATA_READY | N_BREAK \ | N_PARITY_ERROR | N_FRAMING_ERROR \ | N_OVERRUN_ERROR | N_DDCD | N_DCTS)#define N_ALL_OUTPUT N_OUTPUT_LOWAT#define N_ALL_ERRORS (N_PARITY_ERROR | N_FRAMING_ERROR \ | N_OVERRUN_ERROR)#define N_ALL (N_DATA_READY | N_OUTPUT_LOWAT | N_BREAK \ | N_PARITY_ERROR | N_FRAMING_ERROR \ | N_OVERRUN_ERROR | N_DDCD | N_DCTS)#define SER_CLK_SPEED(prediv) ((22000000 << 1) / prediv)#define SER_DIVISOR(x, clk) (((clk) + (x) * 8) / ((x) * 16))#define DIVISOR_TO_BAUD(div, clk) ((clk) / 16 / (div))/* Some masks */#define LCR_MASK_BITS_CHAR (UART_LCR_WLEN5 | UART_LCR_WLEN6 \ | UART_LCR_WLEN7 | UART_LCR_WLEN8)#define LCR_MASK_STOP_BITS (UART_LCR_STOP)#define PENDING(_a, _p) (readl(&(_p)->vma->sio_ir) & (_a)->ic_enable)#define RING_BUF_SIZE 4096#define BUF_SIZE_BIT SBBR_L_SIZE#define PROD_CONS_MASK PROD_CONS_PTR_4K#define TOTAL_RING_BUF_SIZE (RING_BUF_SIZE * 4)/* driver specific - one per card */struct ioc3_card { struct { /* uart ports are allocated here */ struct uart_port icp_uart_port[LOGICAL_PORTS]; /* the ioc3_port used for this port */ struct ioc3_port *icp_port; } ic_port[PORTS_PER_CARD]; /* currently enabled interrupts */ uint32_t ic_enable;};/* Local port info for each IOC3 serial port */struct ioc3_port { /* handy reference material */ struct uart_port *ip_port; struct ioc3_card *ip_card; struct ioc3_driver_data *ip_idd; struct ioc3_submodule *ip_is; /* pci mem addresses for this port */ struct ioc3_serialregs __iomem *ip_serial_regs; struct ioc3_uartregs __iomem *ip_uart_regs; /* Ring buffer page for this port */ dma_addr_t ip_dma_ringbuf; /* vaddr of ring buffer */ struct ring_buffer *ip_cpu_ringbuf; /* Rings for this port */ struct ring *ip_inring; struct ring *ip_outring; /* Hook to port specific values */ struct port_hooks *ip_hooks; spinlock_t ip_lock; /* 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 */ uint32_t ip_sscr; uint32_t ip_tx_prod; uint32_t ip_rx_cons; unsigned char ip_flags;};/* 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 /* used to signify that we have turned off the rx_high * temporarily - we need to drain the fifo and don't * want to get blasted with interrupts. */#define DCD_ON 0x02 /* DCD state is on */#define LOWAT_WRITTEN 0x04#define READ_ABORTED 0x08 /* the read was aborted - used to avaoid infinate looping * in the interrupt handler */#define INPUT_ENABLE 0x10/* 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 port_hooks { uint32_t intr_delta_dcd; uint32_t intr_delta_cts; uint32_t intr_tx_mt; uint32_t intr_rx_timer; uint32_t intr_rx_high; uint32_t intr_tx_explicit; uint32_t intr_clear; uint32_t intr_all; char rs422_select_pin;};static struct port_hooks hooks_array[PORTS_PER_CARD] = { /* values for port A */ { .intr_delta_dcd = SIO_IR_SA_DELTA_DCD, .intr_delta_cts = SIO_IR_SA_DELTA_CTS, .intr_tx_mt = SIO_IR_SA_TX_MT, .intr_rx_timer = SIO_IR_SA_RX_TIMER, .intr_rx_high = SIO_IR_SA_RX_HIGH, .intr_tx_explicit = SIO_IR_SA_TX_EXPLICIT, .intr_clear = (SIO_IR_SA_TX_MT | SIO_IR_SA_RX_FULL | SIO_IR_SA_RX_HIGH | SIO_IR_SA_RX_TIMER | SIO_IR_SA_DELTA_DCD | SIO_IR_SA_DELTA_CTS | SIO_IR_SA_INT | SIO_IR_SA_TX_EXPLICIT | SIO_IR_SA_MEMERR), .intr_all = SIO_IR_SA, .rs422_select_pin = GPPR_UARTA_MODESEL_PIN, }, /* values for port B */ { .intr_delta_dcd = SIO_IR_SB_DELTA_DCD, .intr_delta_cts = SIO_IR_SB_DELTA_CTS, .intr_tx_mt = SIO_IR_SB_TX_MT, .intr_rx_timer = SIO_IR_SB_RX_TIMER, .intr_rx_high = SIO_IR_SB_RX_HIGH, .intr_tx_explicit = SIO_IR_SB_TX_EXPLICIT, .intr_clear = (SIO_IR_SB_TX_MT | SIO_IR_SB_RX_FULL | SIO_IR_SB_RX_HIGH | SIO_IR_SB_RX_TIMER | SIO_IR_SB_DELTA_DCD | SIO_IR_SB_DELTA_CTS | SIO_IR_SB_INT | SIO_IR_SB_TX_EXPLICIT | SIO_IR_SB_MEMERR), .intr_all = SIO_IR_SB, .rs422_select_pin = GPPR_UARTB_MODESEL_PIN, }};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)(RXSB_MODEM_VALID | 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_A; struct ring RX_A; struct ring TX_B; struct ring RX_B;};/* Get a ring from a port struct */#define RING(_p, _wh) &(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh)/* for Infinite loop detection */#define MAXITER 10000000/** * set_baud - Baud rate setting code * @port: port to set * @baud: baud rate to use */static int set_baud(struct ioc3_port *port, int baud){ int divisor; int actual_baud; int diff; int lcr, prediv; struct ioc3_uartregs __iomem *uart; for (prediv = 6; prediv < 64; prediv++) { divisor = SER_DIVISOR(baud, SER_CLK_SPEED(prediv)); if (!divisor) continue; /* invalid divisor */ actual_baud = DIVISOR_TO_BAUD(divisor, SER_CLK_SPEED(prediv)); diff = actual_baud - baud; if (diff < 0) diff = -diff; /* if we're within 1% we've found a match */ if (diff * 100 <= actual_baud) break; } /* if the above loop completed, we didn't match * the baud rate. give up. */ if (prediv == 64) { NOT_PROGRESS(); return 1; } uart = port->ip_uart_regs; lcr = readb(&uart->iu_lcr); writeb(lcr | UART_LCR_DLAB, &uart->iu_lcr); writeb((unsigned char)divisor, &uart->iu_dll); writeb((unsigned char)(divisor >> 8), &uart->iu_dlm); writeb((unsigned char)prediv, &uart->iu_scr); writeb((unsigned char)lcr, &uart->iu_lcr); return 0;}/** * get_ioc3_port - given a uart port, return the control structure * @the_port: uart port to find */static struct ioc3_port *get_ioc3_port(struct uart_port *the_port){ struct ioc3_driver_data *idd = dev_get_drvdata(the_port->dev); struct ioc3_card *card_ptr = idd->data[Submodule_slot]; int ii, jj; if (!card_ptr) { NOT_PROGRESS(); return NULL; } for (ii = 0; ii < PORTS_PER_CARD; ii++) { for (jj = 0; jj < LOGICAL_PORTS; jj++) { if (the_port == &card_ptr->ic_port[ii].icp_uart_port[jj]) return card_ptr->ic_port[ii].icp_port; } } NOT_PROGRESS(); return NULL;}/** * port_init - Initialize the sio and ioc3 hardware for a given port * called per port from attach... * @port: port to initialize */static int inline port_init(struct ioc3_port *port){ uint32_t sio_cr; struct port_hooks *hooks = port->ip_hooks; struct ioc3_uartregs __iomem *uart; int reset_loop_counter = 0xfffff; struct ioc3_driver_data *idd = port->ip_idd; /* Idle the IOC3 serial interface */ writel(SSCR_RESET, &port->ip_serial_regs->sscr); /* Wait until any pending bus activity for this port has ceased */ do { sio_cr = readl(&idd->vma->sio_cr); if (reset_loop_counter-- <= 0) { printk(KERN_WARNING "IOC3 unable to come out of reset" " scr 0x%x\n", sio_cr); return -1; } } while (!(sio_cr & SIO_CR_ARB_DIAG_IDLE) && (((sio_cr &= SIO_CR_ARB_DIAG) == SIO_CR_ARB_DIAG_TXA) || sio_cr == SIO_CR_ARB_DIAG_TXB || sio_cr == SIO_CR_ARB_DIAG_RXA || sio_cr == SIO_CR_ARB_DIAG_RXB)); /* Finish reset sequence */ writel(0, &port->ip_serial_regs->sscr); /* Once RESET is done, reload cached tx_prod and rx_cons values * and set rings to empty by making prod == cons */ port->ip_tx_prod = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; writel(port->ip_tx_prod, &port->ip_serial_regs->stpir); port->ip_rx_cons = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; writel(port->ip_rx_cons | SRCIR_ARM, &port->ip_serial_regs->srcir); /* Disable interrupts for this 16550 */ uart = port->ip_uart_regs; writeb(0, &uart->iu_lcr); writeb(0, &uart->iu_ier); /* Set the default baud */ set_baud(port, port->ip_baud); /* Set line control to 8 bits no parity */ writeb(UART_LCR_WLEN8 | 0, &uart->iu_lcr); /* UART_LCR_STOP == 1 stop */ /* Enable the FIFOs */ writeb(UART_FCR_ENABLE_FIFO, &uart->iu_fcr); /* then reset 16550 FIFOs */ writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, &uart->iu_fcr); /* Clear modem control register */ writeb(0, &uart->iu_mcr); /* Clear deltas in modem status register */ writel(0, &port->ip_serial_regs->shadow); /* Only do this once per port pair */ if (port->ip_hooks == &hooks_array[0]) { unsigned long ring_pci_addr; uint32_t __iomem *sbbr_l, *sbbr_h; sbbr_l = &idd->vma->sbbr_l; sbbr_h = &idd->vma->sbbr_h; ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf; DPRINT_CONFIG(("%s: ring_pci_addr 0x%p\n", __FUNCTION__, (void *)ring_pci_addr)); writel((unsigned int)((uint64_t) ring_pci_addr >> 32), sbbr_h); writel((unsigned int)ring_pci_addr | BUF_SIZE_BIT, sbbr_l); } /* Set the receive timeout value to 10 msec */ writel(SRTR_HZ / 100, &port->ip_serial_regs->srtr); /* Set rx threshold, enable DMA */ /* Set high water mark at 3/4 of full ring */ port->ip_sscr = (ENTRIES_PER_RING * 3 / 4); /* uart experiences pauses at high baud rate reducing actual * throughput by 10% or so unless we enable high speed polling * XXX when this hardware bug is resolved we should revert to * normal polling speed */ port->ip_sscr |= SSCR_HIGH_SPD; writel(port->ip_sscr, &port->ip_serial_regs->sscr); /* Disable and clear all serial related interrupt bits */ port->ip_card->ic_enable &= ~hooks->intr_clear; ioc3_disable(port->ip_is, idd, hooks->intr_clear); ioc3_ack(port->ip_is, idd, hooks->intr_clear); return 0;}/** * enable_intrs - enable interrupts * @port: port to enable * @mask: mask to use */static void enable_intrs(struct ioc3_port *port, uint32_t mask){ if ((port->ip_card->ic_enable & mask) != mask) { port->ip_card->ic_enable |= mask; ioc3_enable(port->ip_is, port->ip_idd, mask); }}/** * local_open - local open a port * @port: port to open */static inline int local_open(struct ioc3_port *port){ int spiniter = 0; port->ip_flags = INPUT_ENABLE; /* Pause the DMA interface if necessary */ if (port->ip_sscr & SSCR_DMA_EN) { writel(port->ip_sscr | SSCR_DMA_PAUSE, &port->ip_serial_regs->sscr); while ((readl(&port->ip_serial_regs->sscr) & SSCR_PAUSE_STATE) == 0) { spiniter++; if (spiniter > MAXITER) { NOT_PROGRESS(); return -1; } } } /* Reset the input fifo. If the uart received chars while the port * was closed and DMA is not enabled, the uart may have a bunch of * chars hanging around in its rx fifo which will not be discarded * by rclr in the upper layer. We must get rid of them here. */ writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR, &port->ip_uart_regs->iu_fcr); writeb(UART_LCR_WLEN8, &port->ip_uart_regs->iu_lcr); /* UART_LCR_STOP == 1 stop */ /* Re-enable DMA, set default threshold to intr whenever there is * data available. */ port->ip_sscr &= ~SSCR_RX_THRESHOLD; port->ip_sscr |= 1; /* default threshold */ /* Plug in the new sscr. This implicitly clears the DMA_PAUSE * flag if it was set above */ writel(port->ip_sscr, &port->ip_serial_regs->sscr); port->ip_tx_lowat = 1; return 0;}/** * set_rx_timeout - Set rx timeout and threshold values. * @port: port to use * @timeout: timeout value in ticks */static inline int set_rx_timeout(struct ioc3_port *port, int timeout){ int threshold; port->ip_rx_timeout = timeout;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?