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 + -
显示快捷键?