⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mx21_16c2552.c

📁 arm926e的mx21处理器的串口驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
		free_page(page);
	else
		info->xmit.buf = (unsigned char *) page;

	/*
	 * Allocate the IRQ
	 */
	retval = request_irq(info->port->irq, ambauart_int, SA_SHIRQ, "mx21_16c2552", info);
	if (retval) {
		if (capable(CAP_SYS_ADMIN)) {
			if (info->tty)
				set_bit(TTY_IO_ERROR, &info->tty->flags);
			retval = 0;
		}
		goto errout;
	}

	info->mctrl = 0;
	if (info->tty->termios->c_cflag & CBAUD)
		info->mctrl = TIOCM_RTS | TIOCM_DTR;
	info->port->set_mctrl(info->port, info->mctrl);

	/*
	 * initialise the old status of the modem signals
	 */
	info->old_status = UART_GET_FR(info->port) & AMBA_UARTFR_MODEM_ANY;

	/*
	 * Finally, enable interrupts
	 */
	UART_PUT_CR(info->port, AMBA_UARTCR_UARTEN | AMBA_UARTCR_RIE |
				AMBA_UARTCR_RTIE);

	if (info->tty)
		clear_bit(TTY_IO_ERROR, &info->tty->flags);
	info->xmit.head = info->xmit.tail = 0;

	/*
	 * Set up the tty->alt_speed kludge
	 */
	if (info->tty) {
		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
			info->tty->alt_speed = 57600;
		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
			info->tty->alt_speed = 115200;
		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
			info->tty->alt_speed = 230400;
		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
			info->tty->alt_speed = 460800;
	}

	/*
	 * and set the speed of the serial port
	 */
	ambauart_change_speed(info, NULL);

	info->flags |= ASYNC_INITIALIZED;
	restore_flags(flags);
	return 0;

errout:
	restore_flags(flags);
	return retval;
}

/*
 * This routine will shutdown a serial port; interrupts are disabled, and
 * DTR is dropped if the hangup on close termio flag is on.
 */
static void ambauart_shutdown(struct amba_info *info)
{
	unsigned long flags;

	if (!(info->flags & ASYNC_INITIALIZED))
		return;

	save_flags(flags); cli(); /* Disable interrupts */

	/*
	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
	 * here so the queue might never be woken up
	 */
	wake_up_interruptible(&info->delta_msr_wait);

	/*
	 * Free the IRQ
	 */
	free_irq(info->port->irq, info);

	if (info->xmit.buf) {
		unsigned long pg = (unsigned long) info->xmit.buf;
		info->xmit.buf = NULL;
		free_page(pg);
	}

	/*
	 * disable all interrupts, disable the port
	 */
	UART_PUT_CR(info->port, 0);

	/* disable break condition and fifos */
	UART_PUT_LCRH(info->port, UART_GET_LCRH(info->port) &
		~(AMBA_UARTLCR_H_BRK | AMBA_UARTLCR_H_FEN));

	if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
		info->mctrl &= ~(TIOCM_DTR|TIOCM_RTS);
	info->port->set_mctrl(info->port, info->mctrl);

	/* kill off our tasklet */
	tasklet_kill(&info->tlet);
	if (info->tty)
		set_bit(TTY_IO_ERROR, &info->tty->flags);

	info->flags &= ~ASYNC_INITIALIZED;
	restore_flags(flags);
}

static void ambauart_change_speed(struct amba_info *info, struct termios *old_termios)
{
	unsigned int lcr_h, baud, quot, cflag, old_cr, bits;
	unsigned long flags;

	if (!info->tty || !info->tty->termios)
		return;

	cflag = info->tty->termios->c_cflag;

#if DEBUG
	printk("ambauart_set_cflag(0x%x) called\n", cflag);
#endif
	/* byte size and parity */
	switch (cflag & CSIZE) {
	case CS5: lcr_h = AMBA_UARTLCR_H_WLEN_5; bits = 7;  break;
	case CS6: lcr_h = AMBA_UARTLCR_H_WLEN_6; bits = 8;  break;
	case CS7: lcr_h = AMBA_UARTLCR_H_WLEN_7; bits = 9;  break;
	default:  lcr_h = AMBA_UARTLCR_H_WLEN_8; bits = 10; break; // CS8
	}
	if (cflag & CSTOPB) {
		lcr_h |= AMBA_UARTLCR_H_STP2;
		bits ++;
	}
	if (cflag & PARENB) {
		lcr_h |= AMBA_UARTLCR_H_PEN;
		bits++;
		if (!(cflag & PARODD))
			lcr_h |= AMBA_UARTLCR_H_EPS;
	}
	if (info->port->fifosize > 1)
		lcr_h |= AMBA_UARTLCR_H_FEN;

	do {
		/* Determine divisor based on baud rate */
		baud = tty_get_baud_rate(info->tty);
		if (!baud)
			baud = 9600;

		if (baud == 38400 &&
		    ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
			quot = info->state->custom_divisor;
		else
			quot = (info->port->uartclk / (16 * baud)) - 1;

		if (!quot && old_termios) {
			info->tty->termios->c_cflag &= ~CBAUD;
			info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
			old_termios = NULL;
		}
	} while (quot == 0 && old_termios);

	/* As a last resort, if the quotient is zero, default to 9600 bps */
	if (!quot)
		quot = (info->port->uartclk / (16 * 9600)) - 1;
		
	info->timeout = (info->port->fifosize * HZ * bits * quot) /
			 (info->port->uartclk / 16);
	info->timeout += HZ/50;		/* Add .02 seconds of slop */

	if (cflag & CRTSCTS)
		info->flags |= ASYNC_CTS_FLOW;
	else
		info->flags &= ~ASYNC_CTS_FLOW;
	if (cflag & CLOCAL)
		info->flags &= ~ASYNC_CHECK_CD;
	else
		info->flags |= ASYNC_CHECK_CD;

	/*
	 * Set up parity check flag
	 */
#define RELEVENT_IFLAG(iflag)	((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))

	info->read_status_mask = AMBA_UARTRSR_OE;
	if (I_INPCK(info->tty))
		info->read_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE;
	if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
		info->read_status_mask |= AMBA_UARTRSR_BE;

	/*
	 * Characters to ignore
	 */
	info->ignore_status_mask = 0;
	if (I_IGNPAR(info->tty))
		info->ignore_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE;
	if (I_IGNBRK(info->tty)) {
		info->ignore_status_mask |= AMBA_UARTRSR_BE;
		/*
		 * If we're ignoring parity and break indicators,
		 * ignore overruns to (for real raw support).
		 */
		if (I_IGNPAR(info->tty))
			info->ignore_status_mask |= AMBA_UARTRSR_OE;
	}

	/* first, disable everything */
	save_flags(flags); cli();
//	old_cr = UART_GET_CR(info->port) &= ~AMBA_UARTCR_MSIE;
	old_cr = UART_GET_CR(info->port);
	UART_PUT_CR(info->port, old_cr & ~AMBA_UARTCR_MSIE);

	if ((info->flags & ASYNC_HARDPPS_CD) ||
	    (cflag & CRTSCTS) ||
	    !(cflag & CLOCAL))
		old_cr |= AMBA_UARTCR_MSIE;

	UART_PUT_CR(info->port, 0);

	/* Set baud rate */
	// MX1ADS, need to +1 for the division factor
//	UART_PUT_LCRM(info->port, ((quot & 0xf00) >> 8));
//	UART_PUT_LCRL(info->port, (quot & 0xff));
	UART_PUT_LCRM(info->port, (((quot+1) & 0xf00) >> 8));
	UART_PUT_LCRL(info->port, ((quot+1) & 0xff));

	/*
	 * ----------v----------v----------v----------v-----
	 * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
	 * ----------^----------^----------^----------^-----
	 */
	UART_PUT_LCRH(info->port, lcr_h);
	UART_PUT_CR(info->port, old_cr);
	restore_flags(flags);
}

static void ambauart_put_char(struct tty_struct *tty, u_char ch)
{
	struct amba_info *info = tty->driver_data;
	unsigned long flags;

// MX1ADS
//printk("(P)");

	if (!tty || !info->xmit.buf)
		return;

	save_flags(flags); cli();
	if (CIRC_SPACE(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE) != 0) {
		info->xmit.buf[info->xmit.head] = ch;
		info->xmit.head = (info->xmit.head + 1) & (AMBA_XMIT_SIZE - 1);
	}
	restore_flags(flags);
}

static void ambauart_flush_chars(struct tty_struct *tty)
{
	struct amba_info *info = tty->driver_data;
	unsigned long flags;

	if (info->xmit.head == info->xmit.tail
	    || tty->stopped
	    || tty->hw_stopped
	    || !info->xmit.buf)
		return;

	save_flags(flags); cli();
	ambauart_enable_tx_interrupt(info);
	restore_flags(flags);
}

static int ambauart_write(struct tty_struct *tty, int from_user,
			  const u_char * buf, int count)
{
	struct amba_info *info = tty->driver_data;
	unsigned long flags;
	int c, ret = 0;

// MX1ADS
//printk("(W)");

	if (!tty || !info->xmit.buf || !tmp_buf)
		return 0;

	save_flags(flags);
	if (from_user) {
		down(&tmp_buf_sem);
		while (1) {
			int c1;
			c = CIRC_SPACE_TO_END(info->xmit.head,
					      info->xmit.tail,
					      AMBA_XMIT_SIZE);
			if (count < c)
				c = count;
			if (c <= 0)
				break;

			c -= copy_from_user(tmp_buf, buf, c);
			if (!c) {
				if (!ret)
					ret = -EFAULT;
				break;
			}
			cli();
			c1 = CIRC_SPACE_TO_END(info->xmit.head,
					       info->xmit.tail,
					       AMBA_XMIT_SIZE);
			if (c1 < c)
				c = c1;
			memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
			info->xmit.head = (info->xmit.head + c) &
					  (AMBA_XMIT_SIZE - 1);
			restore_flags(flags);
			buf += c;
			count -= c;
			ret += c;
		}
		up(&tmp_buf_sem);
	} else {
		cli();
		while (1) {
			c = CIRC_SPACE_TO_END(info->xmit.head,
					      info->xmit.tail,
					      AMBA_XMIT_SIZE);
			if (count < c)
				c = count;
			if (c <= 0)
				break;
			memcpy(info->xmit.buf + info->xmit.head, buf, c);
			info->xmit.head = (info->xmit.head + c) &
					  (AMBA_XMIT_SIZE - 1);
			buf += c;
			count -= c;
			ret += c;
		}
		restore_flags(flags);
	}
	if (info->xmit.head != info->xmit.tail
	    && !tty->stopped
	    && !tty->hw_stopped)
		ambauart_enable_tx_interrupt(info);
	return ret;
}

static int ambauart_write_room(struct tty_struct *tty)
{
	struct amba_info *info = tty->driver_data;

	return CIRC_SPACE(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE);
}

static int ambauart_chars_in_buffer(struct tty_struct *tty)
{
	struct amba_info *info = tty->driver_data;

	return CIRC_CNT(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE);
}

static void ambauart_flush_buffer(struct tty_struct *tty)
{
	struct amba_info *info = tty->driver_data;
	unsigned long flags;

#if DEBUG
	printk("ambauart_flush_buffer(%d) called\n",
	       MINOR(tty->device) - tty->driver.minor_start);
#endif
	save_flags(flags); cli();
	info->xmit.head = info->xmit.tail = 0;
	restore_flags(flags);
	wake_up_interruptible(&tty->write_wait);
	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
	    tty->ldisc.write_wakeup)
		(tty->ldisc.write_wakeup)(tty);
}

/*
 * This function is used to send a high-priority XON/XOFF character to
 * the device
 */
static void ambauart_send_xchar(struct tty_struct *tty, char ch)
{
	struct amba_info *info = tty->driver_data;

	info->x_char = ch;
	if (ch)
		ambauart_enable_tx_interrupt(info);
}

static void ambauart_throttle(struct tty_struct *tty)
{
	struct amba_info *info = tty->driver_data;
	unsigned long flags;

	if (I_IXOFF(tty))
		ambauart_send_xchar(tty, STOP_CHAR(tty));

	if (tty->termios->c_cflag & CRTSCTS) {
		save_flags(flags); cli();
		info->mctrl &= ~TIOCM_RTS;
		info->port->set_mctrl(info->port, info->mctrl);
		restore_flags(flags);
	}
}

static void ambauart_unthrottle(struct tty_struct *tty)
{
	struct amba_info *info = (struct amba_info *) tty->driver_data;
	unsigned long flags;

	if (I_IXOFF(tty)) {
		if (info->x_char)
			info->x_char = 0;
		else
			ambauart_send_xchar(tty, START_CHAR(tty));
	}

	if (tty->termios->c_cflag & CRTSCTS) {
		save_flags(flags); cli();
		info->mctrl |= TIOCM_RTS;
		info->port->set_mctrl(info->port, info->mctrl);
		restore_flags(flags);
	}
}

static int get_serial_info(struct amba_info *info, struct serial_struct *retinfo)
{
	struct amba_state *state = info->state;
	struct amba_port *port = info->port;
	struct serial_struct tmp;

	memset(&tmp, 0, sizeof(tmp));
	tmp.type	   = 0;
	tmp.line	   = state->line;
	tmp.port	   = port->uart_base;
	if (HIGH_BITS_OFFSET)
		tmp.port_high = port->uart_base >> HIGH_BITS_OFFSET;
	tmp.irq		   = port->irq;
	tmp.flags	   = 0;
	tmp.xmit_fifo_size = port->fifosize;
	tmp.baud_base	   = port->uartclk / 16;
	tmp.close_delay	   = state->close_delay;
	tmp.closing_wait   = state->closing_wait;
	tmp.custom_divisor = state->custom_divisor;

⌨️ 快捷键说明

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