ioc4_serial.c

来自「linux 内核源代码」· C语言 代码 · 共 2,250 行 · 第 1/5 页

C
2,250
字号
/* Local port info for each IOC4 serial ports */struct ioc4_port {	struct uart_port *ip_port;	/* current active port ptr */	/* Ptrs for all ports */	struct uart_port *ip_all_ports[UART_PORT_COUNT];	/* Back ptrs for this port */	struct ioc4_control *ip_control;	struct pci_dev *ip_pdev;	struct ioc4_soft *ip_ioc4_soft;	/* pci mem addresses */	struct ioc4_misc_regs __iomem *ip_mem;	struct ioc4_serial __iomem *ip_serial;	struct ioc4_serialregs __iomem *ip_serial_regs;	struct ioc4_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 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_ienb;	/* Enabled interrupts */	uint32_t ip_sscr;	uint32_t ip_tx_prod;	uint32_t ip_rx_cons;	int ip_pci_bus_speed;	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#define DCD_ON		0x02#define LOWAT_WRITTEN	0x04#define READ_ABORTED	0x08#define PORT_ACTIVE	0x10#define PORT_INACTIVE	0	/* This is the value when "off" *//* 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 {	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_dma_error;	uint32_t intr_clear;	uint32_t intr_all;	int rs422_select_pin;};static struct hooks hooks_array[IOC4_NUM_SERIAL_PORTS] = {	/* 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,	 }};/* 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(_p, _wh)	&(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh)/* Infinite loop detection. */#define MAXITER 10000000/* Prototypes */static void receive_chars(struct uart_port *);static void handle_intr(void *arg, uint32_t sio_ir);/* * port_is_active - determines if this port is currently active * @port: ptr to soft struct for this port * @uart_port: uart port to test for */static inline int port_is_active(struct ioc4_port *port,		struct uart_port *uart_port){	if (port) {		if ((port->ip_flags & PORT_ACTIVE)					&& (port->ip_port == uart_port))			return 1;	}	return 0;}/** * write_ireg - write the interrupt regs * @ioc4_soft: ptr to soft struct for this port * @val: value to write * @which: which register * @type: which ireg set */static inline voidwrite_ireg(struct ioc4_soft *ioc4_soft, uint32_t val, int which, int type){	struct ioc4_misc_regs __iomem *mem = ioc4_soft->is_ioc4_misc_addr;	unsigned long flags;	spin_lock_irqsave(&ioc4_soft->is_ir_lock, flags);	switch (type) {	case IOC4_SIO_INTR_TYPE:		switch (which) {		case IOC4_W_IES:			writel(val, &mem->sio_ies.raw);			break;		case IOC4_W_IEC:			writel(val, &mem->sio_iec.raw);			break;		}		break;	case IOC4_OTHER_INTR_TYPE:		switch (which) {		case IOC4_W_IES:			writel(val, &mem->other_ies.raw);			break;		case IOC4_W_IEC:			writel(val, &mem->other_iec.raw);			break;		}		break;	default:		break;	}	spin_unlock_irqrestore(&ioc4_soft->is_ir_lock, flags);}/** * set_baud - Baud rate setting code * @port: port to set * @baud: baud rate to use */static int set_baud(struct ioc4_port *port, int baud){	int actual_baud;	int diff;	int lcr;	unsigned short divisor;	struct ioc4_uartregs __iomem *uart;	divisor = SER_DIVISOR(baud, port->ip_pci_bus_speed);	if (!divisor)		return 1;	actual_baud = DIVISOR_TO_BAUD(divisor, port->ip_pci_bus_speed);	diff = actual_baud - baud;	if (diff < 0)		diff = -diff;	/* If we're within 1%, we've found a match */	if (diff * 100 > actual_baud)		return 1;	uart = port->ip_uart_regs;	lcr = readb(&uart->i4u_lcr);	writeb(lcr | UART_LCR_DLAB, &uart->i4u_lcr);	writeb((unsigned char)divisor, &uart->i4u_dll);	writeb((unsigned char)(divisor >> 8), &uart->i4u_dlm);	writeb(lcr, &uart->i4u_lcr);	return 0;}/** * get_ioc4_port - given a uart port, return the control structure * @port: uart port * @set: set this port as current */static struct ioc4_port *get_ioc4_port(struct uart_port *the_port, int set){	struct ioc4_driver_data *idd = dev_get_drvdata(the_port->dev);	struct ioc4_control *control = idd->idd_serial_data;	struct ioc4_port *port;	int port_num, port_type;	if (control) {		for ( port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS;							port_num++ ) {			port = control->ic_port[port_num].icp_port;			if (!port)				continue;			for (port_type = UART_PORT_MIN;						port_type < UART_PORT_COUNT;						port_type++) {				if (the_port == port->ip_all_ports							[port_type]) {					/* set local copy */					if (set) {						port->ip_port = the_port;					}					return port;				}			}		}	}	return NULL;}/* The IOC4 hardware provides no atomic way to determine if interrupts * are pending since two reads are required to do so.  The handler must * read the SIO_IR and the SIO_IES, and take the logical and of the * two.  When this value is zero, all interrupts have been serviced and * the handler may return. * * This has the unfortunate "hole" that, if some other CPU or * some other thread or some higher level interrupt manages to * modify SIO_IE between our reads of SIO_IR and SIO_IE, we may * think we have observed SIO_IR&SIO_IE==0 when in fact this * condition never really occurred. * * To solve this, we use a simple spinlock that must be held * whenever modifying SIO_IE; holding this lock while observing * both SIO_IR and SIO_IE guarantees that we do not falsely * conclude that no enabled interrupts are pending. */static inline uint32_tpending_intrs(struct ioc4_soft *soft, int type){	struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr;	unsigned long flag;	uint32_t intrs = 0;	BUG_ON(!((type == IOC4_SIO_INTR_TYPE)	       || (type == IOC4_OTHER_INTR_TYPE)));	spin_lock_irqsave(&soft->is_ir_lock, flag);	switch (type) {	case IOC4_SIO_INTR_TYPE:		intrs = readl(&mem->sio_ir.raw) & readl(&mem->sio_ies.raw);		break;	case IOC4_OTHER_INTR_TYPE:		intrs = readl(&mem->other_ir.raw) & readl(&mem->other_ies.raw);		/* Don't process any ATA interrupte */		intrs &= ~(IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR);		break;	default:		break;	}	spin_unlock_irqrestore(&soft->is_ir_lock, flag);	return intrs;}/** * port_init - Initialize the sio and ioc4 hardware for a given port *			called per port from attach... * @port: port to initialize */static int inline port_init(struct ioc4_port *port){	uint32_t sio_cr;	struct hooks *hooks = port->ip_hooks;	struct ioc4_uartregs __iomem *uart;	/* Idle the IOC4 serial interface */	writel(IOC4_SSCR_RESET, &port->ip_serial_regs->sscr);	/* Wait until any pending bus activity for this port has ceased */	do		sio_cr = readl(&port->ip_mem->sio_cr.raw);	while (!(sio_cr & IOC4_SIO_CR_SIO_DIAG_IDLE));	/* 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 | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir);	/* Disable interrupts for this 16550 */	uart = port->ip_uart_regs;	writeb(0, &uart->i4u_lcr);	writeb(0, &uart->i4u_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->i4u_lcr);					/* UART_LCR_STOP == 1 stop */	/* Enable the FIFOs */	writeb(UART_FCR_ENABLE_FIFO, &uart->i4u_fcr);	/* then reset 16550 FIFOs */	writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,			&uart->i4u_fcr);	/* Clear modem control register */	writeb(0, &uart->i4u_mcr);	/* Clear deltas in modem status register */	readb(&uart->i4u_msr);	/* Only do this once per port pair */	if (port->ip_hooks == &hooks_array[0]			    || port->ip_hooks == &hooks_array[2]) {		unsigned long ring_pci_addr;		uint32_t __iomem *sbbr_l;		uint32_t __iomem *sbbr_h;		if (port->ip_hooks == &hooks_array[0]) {			sbbr_l = &port->ip_serial->sbbr01_l;			sbbr_h = &port->ip_serial->sbbr01_h;		} else {			sbbr_l = &port->ip_serial->sbbr23_l;			sbbr_h = &port->ip_serial->sbbr23_h;		}		ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf;		DPRINT_CONFIG(("%s: ring_pci_addr 0x%lx\n",					__FUNCTION__, ring_pci_addr));		writel((unsigned int)((uint64_t)ring_pci_addr >> 32), sbbr_h);		writel((unsigned int)ring_pci_addr | IOC4_BUF_SIZE_BIT, sbbr_l);	}	/* Set the receive timeout value to 10 msec */	writel(IOC4_SRTR_HZ / 100, &port->ip_serial_regs->srtr);

⌨️ 快捷键说明

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