ioc3_serial.c

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

C
2,194
字号
	/* Timeout is in ticks.  Let's figure out how many chars we	 * can receive at the current baud rate in that interval	 * and set the rx threshold to that amount.  There are 4 chars	 * per ring entry, so we'll divide the number of chars that will	 * arrive in timeout by 4.	 * So .... timeout * baud / 10 / HZ / 4, with HZ = 100.	 */	threshold = timeout * port->ip_baud / 4000;	if (threshold == 0)		threshold = 1;	/* otherwise we'll intr all the time! */	if ((unsigned)threshold > (unsigned)SSCR_RX_THRESHOLD)		return 1;	port->ip_sscr &= ~SSCR_RX_THRESHOLD;	port->ip_sscr |= threshold;	writel(port->ip_sscr, &port->ip_serial_regs->sscr);	/* Now set the rx timeout to the given value	 * again timeout * SRTR_HZ / HZ	 */	timeout = timeout * SRTR_HZ / 100;	if (timeout > SRTR_CNT)		timeout = SRTR_CNT;	writel(timeout, &port->ip_serial_regs->srtr);	return 0;}/** * config_port - config the hardware * @port: port to config * @baud: baud rate for the port * @byte_size: data size * @stop_bits: number of stop bits * @parenb: parity enable ? * @parodd: odd parity ? */static inline intconfig_port(struct ioc3_port *port,	    int baud, int byte_size, int stop_bits, int parenb, int parodd){	char lcr, sizebits;	int spiniter = 0;	DPRINT_CONFIG(("%s: line %d baud %d byte_size %d stop %d parenb %d "			"parodd %d\n",		       __FUNCTION__, ((struct uart_port *)port->ip_port)->line,			baud, byte_size, stop_bits, parenb, parodd));	if (set_baud(port, baud))		return 1;	switch (byte_size) {	case 5:		sizebits = UART_LCR_WLEN5;		break;	case 6:		sizebits = UART_LCR_WLEN6;		break;	case 7:		sizebits = UART_LCR_WLEN7;		break;	case 8:		sizebits = UART_LCR_WLEN8;		break;	default:		return 1;	}	/* 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)				return -1;		}	}	/* Clear relevant fields in lcr */	lcr = readb(&port->ip_uart_regs->iu_lcr);	lcr &= ~(LCR_MASK_BITS_CHAR | UART_LCR_EPAR |		 UART_LCR_PARITY | LCR_MASK_STOP_BITS);	/* Set byte size in lcr */	lcr |= sizebits;	/* Set parity */	if (parenb) {		lcr |= UART_LCR_PARITY;		if (!parodd)			lcr |= UART_LCR_EPAR;	}	/* Set stop bits */	if (stop_bits)		lcr |= UART_LCR_STOP /* 2 stop bits */ ;	writeb(lcr, &port->ip_uart_regs->iu_lcr);	/* Re-enable the DMA interface if necessary */	if (port->ip_sscr & SSCR_DMA_EN) {		writel(port->ip_sscr, &port->ip_serial_regs->sscr);	}	port->ip_baud = baud;	/* When we get within this number of ring entries of filling the	 * entire ring on tx, place an EXPLICIT intr to generate a lowat	 * notification when output has drained.	 */	port->ip_tx_lowat = (TX_LOWAT_CHARS(baud) + 3) / 4;	if (port->ip_tx_lowat == 0)		port->ip_tx_lowat = 1;	set_rx_timeout(port, 2);	return 0;}/** * do_write - Write bytes to the port.  Returns the number of bytes *			actually written. Called from transmit_chars * @port: port to use * @buf: the stuff to write * @len: how many bytes in 'buf' */static inline int do_write(struct ioc3_port *port, char *buf, int len){	int prod_ptr, cons_ptr, total = 0;	struct ring *outring;	struct ring_entry *entry;	struct port_hooks *hooks = port->ip_hooks;	BUG_ON(!(len >= 0));	prod_ptr = port->ip_tx_prod;	cons_ptr = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK;	outring = port->ip_outring;	/* Maintain a 1-entry red-zone.  The ring buffer is full when	 * (cons - prod) % ring_size is 1.  Rather than do this subtraction	 * in the body of the loop, I'll do it now.	 */	cons_ptr = (cons_ptr - (int)sizeof(struct ring_entry)) & PROD_CONS_MASK;	/* Stuff the bytes into the output */	while ((prod_ptr != cons_ptr) && (len > 0)) {		int xx;		/* Get 4 bytes (one ring entry) at a time */		entry = (struct ring_entry *)((caddr_t) outring + prod_ptr);		/* Invalidate all entries */		entry->ring_allsc = 0;		/* Copy in some bytes */		for (xx = 0; (xx < 4) && (len > 0); xx++) {			entry->ring_data[xx] = *buf++;			entry->ring_sc[xx] = TXCB_VALID;			len--;			total++;		}		/* If we are within some small threshold of filling up the		 * entire ring buffer, we must place an EXPLICIT intr here		 * to generate a lowat interrupt in case we subsequently		 * really do fill up the ring and the caller goes to sleep.		 * No need to place more than one though.		 */		if (!(port->ip_flags & LOWAT_WRITTEN) &&		    ((cons_ptr - prod_ptr) & PROD_CONS_MASK)		    <= port->ip_tx_lowat * (int)sizeof(struct ring_entry)) {			port->ip_flags |= LOWAT_WRITTEN;			entry->ring_sc[0] |= TXCB_INT_WHEN_DONE;		}		/* Go on to next entry */		prod_ptr += sizeof(struct ring_entry);		prod_ptr &= PROD_CONS_MASK;	}	/* If we sent something, start DMA if necessary */	if (total > 0 && !(port->ip_sscr & SSCR_DMA_EN)) {		port->ip_sscr |= SSCR_DMA_EN;		writel(port->ip_sscr, &port->ip_serial_regs->sscr);	}	/* Store the new producer pointer.  If tx is disabled, we stuff the	 * data into the ring buffer, but we don't actually start tx.	 */	if (!uart_tx_stopped(port->ip_port)) {		writel(prod_ptr, &port->ip_serial_regs->stpir);		/* If we are now transmitting, enable tx_mt interrupt so we		 * can disable DMA if necessary when the tx finishes.		 */		if (total > 0)			enable_intrs(port, hooks->intr_tx_mt);	}	port->ip_tx_prod = prod_ptr;	return total;}/** * disable_intrs - disable interrupts * @port: port to enable * @mask: mask to use */static inline void disable_intrs(struct ioc3_port *port, uint32_t mask){	if (port->ip_card->ic_enable & mask) {		ioc3_disable(port->ip_is, port->ip_idd, mask);		port->ip_card->ic_enable &= ~mask;	}}/** * set_notification - Modify event notification * @port: port to use * @mask: events mask * @set_on: set ? */static int set_notification(struct ioc3_port *port, int mask, int set_on){	struct port_hooks *hooks = port->ip_hooks;	uint32_t intrbits, sscrbits;	BUG_ON(!mask);	intrbits = sscrbits = 0;	if (mask & N_DATA_READY)		intrbits |= (hooks->intr_rx_timer | hooks->intr_rx_high);	if (mask & N_OUTPUT_LOWAT)		intrbits |= hooks->intr_tx_explicit;	if (mask & N_DDCD) {		intrbits |= hooks->intr_delta_dcd;		sscrbits |= SSCR_RX_RING_DCD;	}	if (mask & N_DCTS)		intrbits |= hooks->intr_delta_cts;	if (set_on) {		enable_intrs(port, intrbits);		port->ip_notify |= mask;		port->ip_sscr |= sscrbits;	} else {		disable_intrs(port, intrbits);		port->ip_notify &= ~mask;		port->ip_sscr &= ~sscrbits;	}	/* We require DMA if either DATA_READY or DDCD notification is	 * currently requested. If neither of these is requested and	 * there is currently no tx in progress, DMA may be disabled.	 */	if (port->ip_notify & (N_DATA_READY | N_DDCD))		port->ip_sscr |= SSCR_DMA_EN;	else if (!(port->ip_card->ic_enable & hooks->intr_tx_mt))		port->ip_sscr &= ~SSCR_DMA_EN;	writel(port->ip_sscr, &port->ip_serial_regs->sscr);	return 0;}/** * set_mcr - set the master control reg * @the_port: port to use * @mask1: mcr mask * @mask2: shadow mask */static inline int set_mcr(struct uart_port *the_port,			  int mask1, int mask2){	struct ioc3_port *port = get_ioc3_port(the_port);	uint32_t shadow;	int spiniter = 0;	char mcr;	if (!port)		return -1;	/* 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)				return -1;		}	}	shadow = readl(&port->ip_serial_regs->shadow);	mcr = (shadow & 0xff000000) >> 24;	/* Set new value */	mcr |= mask1;	shadow |= mask2;	writeb(mcr, &port->ip_uart_regs->iu_mcr);	writel(shadow, &port->ip_serial_regs->shadow);	/* Re-enable the DMA interface if necessary */	if (port->ip_sscr & SSCR_DMA_EN) {		writel(port->ip_sscr, &port->ip_serial_regs->sscr);	}	return 0;}/** * ioc3_set_proto - set the protocol for the port * @port: port to use * @proto: protocol to use */static int ioc3_set_proto(struct ioc3_port *port, int proto){	struct port_hooks *hooks = port->ip_hooks;	switch (proto) {	default:	case PROTO_RS232:		/* Clear the appropriate GIO pin */		DPRINT_CONFIG(("%s: rs232\n", __FUNCTION__));		writel(0, (&port->ip_idd->vma->gppr[0]					+ hooks->rs422_select_pin));		break;	case PROTO_RS422:		/* Set the appropriate GIO pin */		DPRINT_CONFIG(("%s: rs422\n", __FUNCTION__));		writel(1, (&port->ip_idd->vma->gppr[0]					+ hooks->rs422_select_pin));		break;	}	return 0;}/** * transmit_chars - upper level write, called with the_port->lock * @the_port: port to write */static void transmit_chars(struct uart_port *the_port){	int xmit_count, tail, head;	int result;	char *start;	struct tty_struct *tty;	struct ioc3_port *port = get_ioc3_port(the_port);	struct uart_info *info;	if (!the_port)		return;	if (!port)		return;	info = the_port->info;	tty = info->tty;	if (uart_circ_empty(&info->xmit) || uart_tx_stopped(the_port)) {		/* Nothing to do or hw stopped */		set_notification(port, N_ALL_OUTPUT, 0);		return;	}	head = info->xmit.head;	tail = info->xmit.tail;	start = (char *)&info->xmit.buf[tail];	/* write out all the data or until the end of the buffer */	xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail);	if (xmit_count > 0) {		result = do_write(port, start, xmit_count);		if (result > 0) {			/* booking */			xmit_count -= result;			the_port->icount.tx += result;			/* advance the pointers */			tail += result;			tail &= UART_XMIT_SIZE - 1;			info->xmit.tail = tail;			start = (char *)&info->xmit.buf[tail];		}	}	if (uart_circ_chars_pending(&info->xmit) < WAKEUP_CHARS)		uart_write_wakeup(the_port);	if (uart_circ_empty(&info->xmit)) {		set_notification(port, N_OUTPUT_LOWAT, 0);	} else {		set_notification(port, N_OUTPUT_LOWAT, 1);	}}/** * ioc3_change_speed - change the speed of the port * @the_port: port to change * @new_termios: new termios settings * @old_termios: old termios settings */static voidioc3_change_speed(struct uart_port *the_port,		  struct ktermios *new_termios, struct ktermios *old_termios){	struct ioc3_port *port = get_ioc3_port(the_port);	unsigned int cflag;	int baud;	int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;	struct uart_info *info = the_port->info;	cflag = new_termios->c_cflag;	switch (cflag & CSIZE) {	case CS5:		new_data = 5;		break;	case CS6:		new_data = 6;		break;	case CS7:		new_data = 7;		break;	case CS8:		new_data = 8;		break;	default:		/* cuz we always need a default ... */		new_data = 5;		break;	}	if (cflag & CSTOPB) {		new_stop = 1;	}	if (cflag & PARENB) {		new_parity_enable = 1;		if (cflag & PARODD)			new_parity = 1;	}	baud = uart_get_baud_rate(the_port, new_termios, old_termios,				  MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED);	DPRINT_CONFIG(("%s: returned baud %d for line %d\n", __FUNCTION__, baud,				the_port->line));	if (!the_port->fifosize)		the_port->fifosize = FIFO_SIZE;	uart_update_timeout(the_port, cflag, baud);	the_port->ignore_status_mask = N_ALL_INPUT;	info->tty->low_latency = 1;	if (I_IGNPAR(info->tty))		the_port->ignore_status_mask &= ~(N_PARITY_ERROR						  | N_FRAMING_ERROR);	if (I_IGNBRK(info->tty)) {		the_port->ignore_status_mask &= ~N_BREAK;		if (I_IGNPAR(info->tty))			the_port->ignore_status_mask &= ~N_OVERRUN_ERROR;	}	if (!(cflag & CREAD)) {		/* ignore everything */		the_port->ignore_status_mask &= ~N_DATA_READY;	}	if (cflag & CRTSCTS) {		/* enable hardware flow control */		port->ip_sscr |= SSCR_HFC_EN;	}	else {		/* disable hardware flow control */		port->ip_sscr &= ~SSCR_HFC_EN;	}	writel(port->ip_sscr, &port->ip_serial_regs->sscr);	/* Set the configuration and proper notification call */	DPRINT_CONFIG(("%s : port 0x%p line %d cflag 0%o "		       "config_port(baud %d data %d stop %d penable %d "			" parity %d), notification 0x%x\n",		       __FUNCTION__, (void *)port, the_port->line, cflag, baud,		       new_data, new_stop, new_parity_enable, new_parity,		       the_port->ignore_status_mask));	if ((config_port(port, baud,	/* baud */			 new_data,	/* byte size */			 new_stop,	/* stop bits */			 new_parity_enable,	/* set parity */			 new_parity)) >= 0) {	/* parity 1==odd */		set_notification(port, the_port->ignore_status_mask, 1);	}}/** * ic3_startup_local - Start up the serial port - returns >= 0 if no errors * @the_port: Port to operate on */static inline int ic3_startup_local(struct uart_port *the_port){	struct ioc3_port *port;	if (!the_port) {		NOT_PROGRESS();		return -1;	}	port = get_ioc3_port(the_port);	if (!port) {		NOT_PROGRESS();		return -1;	}	local_open(port);	/* set the protocol */	ioc3_set_proto(port, IS_RS232(the_port->line) ? PROTO_RS232 :							PROTO_RS422);	return 0;}/* * ioc3_cb_output_lowat - called when the output low water mark is hit * @port: port to output */static void ioc3_cb_output_lowat(struct ioc3_port *port){	unsigned long pflags;	/* the_port->lock is set on the call here */	if (port->ip_port) {		spin_lock_irqsave(&port->ip_port->lock, pflags);		transmit_chars(port->ip_port);		spin_unlock_irqrestore(&port->ip_port->lock, pflags);	}}/* * ioc3_cb_post_ncs - called for some basic errors * @port: port to use * @ncs: event */static void ioc3_cb_post_ncs(struct uart_port *the_port, int ncs){	struct uart_icount *icount;	icount = &the_port->icount;	if (ncs & NCS_BREAK)		icount->brk++;	if (ncs & NCS_FRAMING)

⌨️ 快捷键说明

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