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

📄 serial_s3c44b0x.c

📁 S3C44B0x串行口驱动,支持两个UART口
💻 C
📖 第 1 页 / 共 4 页
字号:
		return;	}	for (count = TX_FIFO_DEPTH; count > 0; --count) {		status = inb(S3C44B0X_UFSTAT0 + info->uart_offset);		if (status & S3C44B0X_UFSTAT_TX_FIFO_FULL) {			handle_status (info, S3C44B0X_UFSTAT_TX_FIFO_FULL);			break;		} else {			xmit_char(info, info->xmit_buf[info->xmit_tail++]);			info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE - 1);			if (--info->xmit_cnt <= 0) 				break;		}	}	if (info->xmit_cnt < WAKEUP_CHARS)		rs_s3c44b0x_sched_event(info, RS_EVENT_WRITE_WAKEUP);	if (info->xmit_cnt <= 0) {		s3c44b0x_mask_ack_irq(info->irq);		tx_stop(info);		/* set the settings for receiving */		switch (info->ser_mode){		    	case SERIAL_MODE_RS422:		    	case SERIAL_MODE_RS422_LISTEN:				/* enable tx and rx */				tx_enable(info);				rx_enable(info);				break;				    			case SERIAL_MODE_RS485_ECHO:			case SERIAL_MODE_RS485_NO_ECHO:				/* disable tx and enable rx */				tx_disable(info);				rx_enable(info);				break;							case SERIAL_MODE_NONE:				/* disable tx and rx */				tx_disable(info);				rx_disable(info);				break;						default:				/* do nothing */				break;		}	}	return;}#define S3C44B0X_UERSTAT_ERROR  (	S3C44B0X_UERSTAT_OVERRUN_ERROR |\					S3C44B0X_UERSTAT_PARITY_ERROR |\					S3C44B0X_UERSTAT_FRAME_ERROR |\					S3C44B0X_UERSTAT_BREAK_DETECT	)static void rs_s3c44b0x_interruptRxa(int irq, void *dev_id, struct pt_regs *regs){    if (s3c44b0x_info[1].count)	    rs_s3c44b0x_interruptRx(irq, dev_id, regs, &s3c44b0x_info[1]);}static void rs_s3c44b0x_interruptRxb(int irq, void *dev_id, struct pt_regs *regs){    if (s3c44b0x_info[0].count)	    rs_s3c44b0x_interruptRx(irq, dev_id, regs, &s3c44b0x_info[0]);}static void rs_s3c44b0x_interruptRx(int irq, void *dev_id, 				    struct pt_regs *regs, 				    struct s3c44b0x_serial *serial_info){	struct s3c44b0x_serial *info = serial_info;	struct tty_struct *tty = info->tty;	unsigned int count;	volatile u_int8_t status, fifo_status;	if (!info || !tty || (!(info->flags & S_INITIALIZED)))		return;	if ((tty->flip.count + RX_FIFO_DEPTH) >= TTY_FLIPBUF_SIZE)		queue_task_irq_off(&tty->flip.tqueue, &tq_timer);	count = RX_FIFO_DEPTH;	do {		/* check all error flags and accept data if valid */		fifo_status = inb(S3C44B0X_UFSTAT0 + info->uart_offset);		if (!(fifo_status & S3C44B0X_UFSTAT_RX_FIFO_COUNT))			break;  /* if 0 bytes exit */		status = inb(S3C44B0X_UERSTAT0 + info->uart_offset);		if (!(status & S3C44B0X_UERSTAT_ERROR))			*tty->flip.flag_buf_ptr = TTY_NORMAL;		else {			if (fifo_status & S3C44B0X_UFSTAT_RX_FIFO_FULL) {				*tty->flip.flag_buf_ptr = TTY_NORMAL;				handle_status(info, 				   S3C44B0X_UFSTAT_RX_FIFO_FULL);			}			if (status & S3C44B0X_UERSTAT_OVERRUN_ERROR) {				*tty->flip.flag_buf_ptr = TTY_OVERRUN;				handle_status (info, S3C44B0X_UERSTAT_OVERRUN_ERROR);			}			if (status & S3C44B0X_UERSTAT_BREAK_DETECT) {				*tty->flip.flag_buf_ptr = TTY_BREAK;				handle_status (info, S3C44B0X_UERSTAT_BREAK_DETECT);			}			if (status & S3C44B0X_UERSTAT_PARITY_ERROR) {				*tty->flip.flag_buf_ptr = TTY_PARITY;				handle_status (info, S3C44B0X_UERSTAT_PARITY_ERROR);			}			if (status & S3C44B0X_UERSTAT_FRAME_ERROR) {				*tty->flip.flag_buf_ptr = TTY_FRAME;				handle_status (info, S3C44B0X_UERSTAT_FRAME_ERROR);			}		}		*tty->flip.char_buf_ptr++ = 			inb(S3C44B0X_URXH0 + info->uart_offset);				tty->flip.flag_buf_ptr++;		tty->flip.count++;	} while ((--count > 0) && !(status & S3C44B0X_UERSTAT_ERROR));#if 0	if (fifo_status & (U_RFOV | U_E_RxTO))		handle_status (info, (U_RFOV | U_E_RxTO));#endif		queue_task_irq_off(&tty->flip.tqueue, &tq_timer);	}static void rs_s3c44b0x_interruptErr(int irq, void *dev_id, struct pt_regs *regs){	rs_s3c44b0x_interruptRxa(irq, dev_id, regs);	rs_s3c44b0x_interruptRxb(irq, dev_id, regs);}static _INLINE_ void handle_status ( struct s3c44b0x_serial *info, unsigned int status ){#if 0	if (status & U_TFFUL) {		return;				/* FIXME - do something */	}	if (status & U_RFFUL) {		info->usart->cr |= U_SBR;		}	if (status & U_BSD) {		batten_down_hatches();	}	info->usart->csr |= status;#endif}/* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. * ------------------------------------------------------------------- *//* * This routine is used to handle the "bottom half" processing for the * serial driver, known also the "software interrupt" processing. * This processing is done at the kernel interrupt level, after the * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This * is where time-consuming activities which can not be done in the * interrupt driver proper are done; the interrupt driver schedules * them using rs_s3c44b0x_sched_event(), and they get done here. */static void do_serial_bh(void){	run_task_queue(&tq_s3c44b0x_serial);}static void do_softint(void *private_){	struct s3c44b0x_serial *info = (struct s3c44b0x_serial *) private_;	struct tty_struct *tty;	tty = info->tty;	if (!tty ) return;	if  (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {		wake_up_interruptible(&tty->write_wait);	}}/* * This routine is called from the scheduler tqueue when the interrupt * routine has signalled that a hangup has occurred.  The path of * hangup processing is: * * 	serial interrupt routine -> (scheduler tqueue) -> * 	do_serial_hangup() -> tty->hangup() -> rs_hangup() * */static void do_serial_hangup(void *private_){	struct s3c44b0x_serial *info = (struct s3c44b0x_serial *) private_;	struct tty_struct *tty;		tty = info->tty;	if (!tty)	return;	tty_hangup(tty);}static _INLINE_ u_int16_t calcCD(u_int32_t baudrate){	const u_int32_t brg_base = CONFIG_ARM_CLK >> 4;	u_int32_t divisor, rest;	/*	 * the S3C44B0X datasheet says that the baudrate has to be	 * calculated by ((CPU_CLOCK/16)/Baudrate)-1	 * As we do not want floating point in kernel, we have to	 * round manually by looking at the remaining rest of the	 * division through the baudrate.	 */	divisor = brg_base / baudrate;	rest    = brg_base % baudrate;	if (rest <= (baudrate >> 1))		divisor--;	return divisor;}static _INLINE_ void uart_speed(struct s3c44b0x_serial *info, unsigned int cflag){	unsigned baud = info->baud;	u_int16_t divisor = calcCD(baud);	outw(divisor, S3C44B0X_UBRDIV0 + info->uart_offset);}static int startup(struct s3c44b0x_serial *info){	unsigned long flags;	if (info->flags & S_INITIALIZED)		return 0;	if (!info->xmit_buf) {		info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);		if (!info->xmit_buf) return -ENOMEM;	}	save_flags(flags);    	cli();#ifdef SERIAL_DEBUG_OPEN	printk("starting up ttyS%d (irq %d)...\n", info->line, info->irq);#endif	fifo_init(info);	/*	 * Clear the FIFO buffers and disable them	 * (they will be reenabled in change_speed())	 */	fifo_reset(info);	/*	 * Now, initialize the UART 	 */ 	if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags);	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;		/*	 * and set the speed of the serial port	 */	change_speed(info);		info->flags |= S_INITIALIZED;	restore_flags(flags);	return 0;}/* * 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 shutdown(struct s3c44b0x_serial *info){	unsigned long flags;		tx_stop(info);	rx_stop(info);	/* All off! */	if (!(info->flags & S_INITIALIZED))		return;	#ifdef SERIAL_DEBUG_OPEN	printk("Shutting down serial port %d (irq %d)....\n", info->line,			info->irq);#endif		save_flags(flags);    cli();		/* Disable interrupts */		if (info->xmit_buf) {		free_page((unsigned long) info->xmit_buf);		info->xmit_buf = 0;	}	if  (info->tty)		set_bit(TTY_IO_ERROR, &info->tty->flags);	info->flags &= ~S_INITIALIZED;	restore_flags(flags);}static s3c_baud_table_t baud_table[] = {	{     50, B50 },	{     75, B75 },	{    110, B110 },	{    134, B134 },	{    150, B150 },	{    200, B200 },	{    300, B300 },	{    600, B600 },	{   1200, B1200 },	{   1800, B1800 },	{   2400, B2400 },	{   4800, B4800 },	{   9600, B9600 },	{  19200, B19200 },	{  38400, B38400 },	{  57600, B57600 },	{ 115200, B115200 }};/* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */static void change_speed(struct s3c44b0x_serial *info){	unsigned cflag;	int      table_index;	char  ulcon, ucon;	const unsigned int baudrates = sizeof(baud_table)/sizeof(baud_table[0]);		if (!info->tty || !info->tty->termios)		return;	cflag = info->tty->termios->c_cflag;		tx_stop(info);	rx_stop(info);		/* UART baudrate setup */	for (table_index = 0; table_index < baudrates; table_index++) {		if (baud_table[table_index].cflag == (cflag & CBAUD)) {#ifdef SERIAL_DEBUG_SPEED			printk("setting baudrate to %u\n", baud_table[table_index].rate);#endif			break;		}	}	if (table_index >= baudrates) {		/* baudrate not found */#ifdef SERIAL_DEBUG_SPEED		printk("baudrate not found, cflag=0x%08X\n", cflag);#endif		/* UART control register setup */		ucon = 0;		/* receive mode: IRQ or polling */		ucon |= S3C44B0X_UCON_RX_MODE_INT_POLL;   		/* transmit mode: IRQ or polling */		ucon |= S3C44B0X_UCON_TX_MODE_INT_POLL;   		/* Rx status interrupt enable */		ucon |= S3C44B0X_UCON_RX_ERR_INT_EN;      		/* Rx timeout enable (5 bits) */		ucon |= S3C44B0X_UCON_RX_TIMEOUT_EN;      		outb(ucon, S3C44B0X_UCON0 + info->uart_offset);				tx_start(info);		rx_start(info);		return;	}		info->baud = baud_table[table_index].rate;	uart_speed(info, cflag);	/* UART line control register setup */	ulcon = 0x00;	switch  (cflag & CSIZE) {		case CS5: ulcon |= S3C44B0X_ULCON_WORDLN_5; break;		case CS6: ulcon |= S3C44B0X_ULCON_WORDLN_6; break;		case CS7: ulcon |= S3C44B0X_ULCON_WORDLN_7; break;		case CS8: ulcon |= S3C44B0X_ULCON_WORDLN_8; break;	}	if (cflag & PARENB)		ulcon |= (cflag & PARODD) ? 			S3C44B0X_ULCON_PAR_ODD : S3C44B0X_ULCON_PAR_EVEN;	if (cflag & CSTOPB)		ulcon |= S3C44B0X_ULCON_STOPB_2;	outb(ulcon, S3C44B0X_ULCON0 + info->uart_offset);	/* UART control register setup */	ucon = 0;	/* receive mode: IRQ or polling */	ucon |= S3C44B0X_UCON_RX_MODE_INT_POLL;   	/* transmit mode: IRQ or polling */	ucon |= S3C44B0X_UCON_TX_MODE_INT_POLL;   	/* Rx status interrupt enable */	ucon |= S3C44B0X_UCON_RX_ERR_INT_EN;      	/* Rx timeout enable (5 bits) */	ucon |= S3C44B0X_UCON_RX_TIMEOUT_EN;  	outb(ucon, S3C44B0X_UCON0 + info->uart_offset);		tx_start(info);	rx_start(info);	return;}/* *	Wait for transmitter & holding register to empty */static inline void wait_for_xmitr(struct s3c44b0x_serial *info){	volatile unsigned int status, tmout = 100000;	do {		status = inb(S3C44B0X_UTRSTAT0 + info->uart_offset);		if (--tmout == 0)			break;	} while((status & (S3C44B0X_UTRSTAT_TBE | \			   S3C44B0X_UTRSTAT_TSE)) != \			   (S3C44B0X_UTRSTAT_TBE | \			    S3C44B0X_UTRSTAT_TSE));}static _INLINE_ void xmit_char(struct s3c44b0x_serial *info, char ch){	wait_for_xmitr(info);		tx_delay();	outb(ch, S3C44B0X_UTXH0 + info->uart_offset);}static void rs_flush_chars(struct tty_struct *tty){	struct s3c44b0x_serial *info = (struct s3c44b0x_serial *) tty->driver_data;	unsigned long flags;	if  (serial_paranoia_check(info, tty->device, "rs_flush_chars"))		return;	if  (!info->use_ints) {		for (;;) {			if (info->xmit_cnt <= 0 || tty->stopped || 			    tty->hw_stopped || !info->xmit_buf) 				return;			save_flags(flags);	    			cli();			tx_start(info);			xmit_char(info, info->xmit_buf[info->xmit_tail++]);			info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE - 1);			info->xmit_cnt--;		}	} else {		if (info->xmit_cnt <= 0 || tty->stopped || 		    tty->hw_stopped || !info->xmit_buf) 			return;		save_flags(flags);			cli();		tx_start(info);		xmit_char(info, info->xmit_buf[info->xmit_tail++]);		info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE - 1);		info->xmit_cnt--;	}	restore_flags(flags);}static int rs_write(struct tty_struct *tty, int from_user,					const unsigned char *buf, int count){	int c, total = 0;	struct s3c44b0x_serial *info = (struct s3c44b0x_serial *) tty->driver_data;	unsigned long flags;	if  (serial_paranoia_check(info, tty->device, "rs_write"))		return 0;	if  (!tty || !info->xmit_buf || !tmp_buf)		return 0;	save_flags(flags);	if (from_user) {		down(&tmp_buf_sem);		cli();		while (1) {			c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,				SERIAL_XMIT_SIZE - info->xmit_head));			if  (c <= 0)				break;						memcpy(info->xmit_buf + info->xmit_head, buf, c);			info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1);			info->xmit_cnt += c;			restore_flags(flags);			buf += c;			count -= c;			total += c;		}		restore_flags(flags);		up(&tmp_buf_sem);	} else {

⌨️ 快捷键说明

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