662 lines · 17.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/****************************************************************************/ /* *	mcf.c -- Freescale ColdFire UART driver * *	(C) Copyright 2003-2007, Greg Ungerer <gerg@uclinux.org> */ /****************************************************************************/ #include <linux/kernel.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/module.h>#include <linux/console.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/serial.h>#include <linux/serial_core.h>#include <linux/io.h>#include <linux/uaccess.h>#include <linux/platform_device.h>#include <asm/coldfire.h>#include <asm/mcfsim.h>#include <asm/mcfuart.h>#include <asm/nettel.h> /****************************************************************************/ /* *	Some boards implement the DTR/DCD lines using GPIO lines, most *	don't. Dummy out the access macros for those that don't. Those *	that do should define these macros somewhere in there board *	specific inlude files. */#if !defined(mcf_getppdcd)#define	mcf_getppdcd(p)		(1)#endif#if !defined(mcf_getppdtr)#define	mcf_getppdtr(p)		(1)#endif#if !defined(mcf_setppdtr)#define	mcf_setppdtr(p, v)	do { } while (0)#endif /****************************************************************************/ /* *	Local per-uart structure. */struct mcf_uart {	struct uart_port	port;	unsigned int		sigs;		/* Local copy of line sigs */	unsigned char		imr;		/* Local IMR mirror */}; /****************************************************************************/ static unsigned int mcf_tx_empty(struct uart_port *port){	return (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXEMPTY) ?		TIOCSER_TEMT : 0;} /****************************************************************************/ static unsigned int mcf_get_mctrl(struct uart_port *port){	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);	unsigned int sigs; 	sigs = (readb(port->membase + MCFUART_UIPR) & MCFUART_UIPR_CTS) ?		0 : TIOCM_CTS;	sigs |= (pp->sigs & TIOCM_RTS);	sigs |= (mcf_getppdcd(port->line) ? TIOCM_CD : 0);	sigs |= (mcf_getppdtr(port->line) ? TIOCM_DTR : 0); 	return sigs;} /****************************************************************************/ static void mcf_set_mctrl(struct uart_port *port, unsigned int sigs){	struct mcf_uart *pp = container_of(port, struct mcf_uart, port); 	pp->sigs = sigs;	mcf_setppdtr(port->line, (sigs & TIOCM_DTR));	if (sigs & TIOCM_RTS)		writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1);	else		writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP0);} /****************************************************************************/ static void mcf_start_tx(struct uart_port *port){	struct mcf_uart *pp = container_of(port, struct mcf_uart, port); 	if (port->rs485.flags & SER_RS485_ENABLED) {		/* Enable Transmitter */		writeb(MCFUART_UCR_TXENABLE, port->membase + MCFUART_UCR);		/* Manually assert RTS */		writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1);	}	pp->imr |= MCFUART_UIR_TXREADY;	writeb(pp->imr, port->membase + MCFUART_UIMR);} /****************************************************************************/ static void mcf_stop_tx(struct uart_port *port){	struct mcf_uart *pp = container_of(port, struct mcf_uart, port); 	pp->imr &= ~MCFUART_UIR_TXREADY;	writeb(pp->imr, port->membase + MCFUART_UIMR);} /****************************************************************************/ static void mcf_stop_rx(struct uart_port *port){	struct mcf_uart *pp = container_of(port, struct mcf_uart, port); 	pp->imr &= ~MCFUART_UIR_RXREADY;	writeb(pp->imr, port->membase + MCFUART_UIMR);} /****************************************************************************/ static void mcf_break_ctl(struct uart_port *port, int break_state){	unsigned long flags; 	uart_port_lock_irqsave(port, &flags);	if (break_state == -1)		writeb(MCFUART_UCR_CMDBREAKSTART, port->membase + MCFUART_UCR);	else		writeb(MCFUART_UCR_CMDBREAKSTOP, port->membase + MCFUART_UCR);	uart_port_unlock_irqrestore(port, flags);} /****************************************************************************/ static int mcf_startup(struct uart_port *port){	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);	unsigned long flags; 	uart_port_lock_irqsave(port, &flags); 	/* Reset UART, get it into known state... */	writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR);	writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); 	/* Enable the UART transmitter and receiver */	writeb(MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE,		port->membase + MCFUART_UCR); 	/* Enable RX interrupts now */	pp->imr = MCFUART_UIR_RXREADY;	writeb(pp->imr, port->membase + MCFUART_UIMR); 	uart_port_unlock_irqrestore(port, flags); 	return 0;} /****************************************************************************/ static void mcf_shutdown(struct uart_port *port){	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);	unsigned long flags; 	uart_port_lock_irqsave(port, &flags); 	/* Disable all interrupts now */	pp->imr = 0;	writeb(pp->imr, port->membase + MCFUART_UIMR); 	/* Disable UART transmitter and receiver */	writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR);	writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); 	uart_port_unlock_irqrestore(port, flags);} /****************************************************************************/ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios,			    const struct ktermios *old){	unsigned long flags;	unsigned int baud, baudclk;#if defined(CONFIG_M5272)	unsigned int baudfr;#endif	unsigned char mr1, mr2; 	baud = uart_get_baud_rate(port, termios, old, 0, 230400);#if defined(CONFIG_M5272)	baudclk = (MCF_BUSCLK / baud) / 32;	baudfr = (((MCF_BUSCLK / baud) + 1) / 2) % 16;#else	baudclk = ((MCF_BUSCLK / baud) + 16) / 32;#endif 	mr1 = MCFUART_MR1_RXIRQRDY | MCFUART_MR1_RXERRCHAR;	mr2 = 0; 	switch (termios->c_cflag & CSIZE) {	case CS5: mr1 |= MCFUART_MR1_CS5; break;	case CS6: mr1 |= MCFUART_MR1_CS6; break;	case CS7: mr1 |= MCFUART_MR1_CS7; break;	case CS8:	default:  mr1 |= MCFUART_MR1_CS8; break;	} 	if (termios->c_cflag & PARENB) {		if (termios->c_cflag & CMSPAR) {			if (termios->c_cflag & PARODD)				mr1 |= MCFUART_MR1_PARITYMARK;			else				mr1 |= MCFUART_MR1_PARITYSPACE;		} else {			if (termios->c_cflag & PARODD)				mr1 |= MCFUART_MR1_PARITYODD;			else				mr1 |= MCFUART_MR1_PARITYEVEN;		}	} else {		mr1 |= MCFUART_MR1_PARITYNONE;	} 	/*	 * FIXME: port->read_status_mask and port->ignore_status_mask	 * need to be initialized based on termios settings for	 * INPCK, IGNBRK, IGNPAR, PARMRK, BRKINT	 */ 	if (termios->c_cflag & CSTOPB)		mr2 |= MCFUART_MR2_STOP2;	else		mr2 |= MCFUART_MR2_STOP1; 	if (termios->c_cflag & CRTSCTS) {		mr1 |= MCFUART_MR1_RXRTS;		mr2 |= MCFUART_MR2_TXCTS;	} 	uart_port_lock_irqsave(port, &flags);	if (port->rs485.flags & SER_RS485_ENABLED) {		dev_dbg(port->dev, "Setting UART to RS485\n");		mr2 |= MCFUART_MR2_TXRTS;	} 	uart_update_timeout(port, termios->c_cflag, baud);	writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR);	writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR);	writeb(MCFUART_UCR_CMDRESETMRPTR, port->membase + MCFUART_UCR);	writeb(mr1, port->membase + MCFUART_UMR);	writeb(mr2, port->membase + MCFUART_UMR);	writeb((baudclk & 0xff00) >> 8, port->membase + MCFUART_UBG1);	writeb((baudclk & 0xff), port->membase + MCFUART_UBG2);#if defined(CONFIG_M5272)	writeb((baudfr & 0x0f), port->membase + MCFUART_UFPD);#endif	writeb(MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER,		port->membase + MCFUART_UCSR);	writeb(MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE,		port->membase + MCFUART_UCR);	uart_port_unlock_irqrestore(port, flags);} /****************************************************************************/ static void mcf_rx_chars(struct mcf_uart *pp){	struct uart_port *port = &pp->port;	u8 status, ch, flag; 	while ((status = readb(port->membase + MCFUART_USR)) & MCFUART_USR_RXREADY) {		ch = readb(port->membase + MCFUART_URB);		flag = TTY_NORMAL;		port->icount.rx++; 		if (status & MCFUART_USR_RXERR) {			writeb(MCFUART_UCR_CMDRESETERR,				port->membase + MCFUART_UCR); 			if (status & MCFUART_USR_RXBREAK) {				port->icount.brk++;				if (uart_handle_break(port))					continue;			} else if (status & MCFUART_USR_RXPARITY) {				port->icount.parity++;			} else if (status & MCFUART_USR_RXOVERRUN) {				port->icount.overrun++;			} else if (status & MCFUART_USR_RXFRAMING) {				port->icount.frame++;			} 			status &= port->read_status_mask; 			if (status & MCFUART_USR_RXBREAK)				flag = TTY_BREAK;			else if (status & MCFUART_USR_RXPARITY)				flag = TTY_PARITY;			else if (status & MCFUART_USR_RXFRAMING)				flag = TTY_FRAME;		} 		if (uart_handle_sysrq_char(port, ch))			continue;		uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag);	} 	tty_flip_buffer_push(&port->state->port);} /****************************************************************************/ static void mcf_tx_chars(struct mcf_uart *pp){	struct uart_port *port = &pp->port;	bool pending;	u8 ch; 	pending = uart_port_tx(port, ch,		readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY,		writeb(ch, port->membase + MCFUART_UTB)); 	/* Disable TX to negate RTS automatically */	if (!pending && (port->rs485.flags & SER_RS485_ENABLED))		writeb(MCFUART_UCR_TXDISABLE, port->membase + MCFUART_UCR);} /****************************************************************************/ static irqreturn_t mcf_interrupt(int irq, void *data){	struct uart_port *port = data;	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);	unsigned int isr;	irqreturn_t ret = IRQ_NONE; 	isr = readb(port->membase + MCFUART_UISR) & pp->imr; 	uart_port_lock(port);	if (isr & MCFUART_UIR_RXREADY) {		mcf_rx_chars(pp);		ret = IRQ_HANDLED;	}	if (isr & MCFUART_UIR_TXREADY) {		mcf_tx_chars(pp);		ret = IRQ_HANDLED;	}	uart_port_unlock(port); 	return ret;} /****************************************************************************/ static void mcf_config_port(struct uart_port *port, int flags){	port->type = PORT_MCF;	port->fifosize = MCFUART_TXFIFOSIZE; 	/* Clear mask, so no surprise interrupts. */	writeb(0, port->membase + MCFUART_UIMR); 	if (request_irq(port->irq, mcf_interrupt, 0, "UART", port))		printk(KERN_ERR "MCF: unable to attach ColdFire UART %d "			"interrupt vector=%d\n", port->line, port->irq);} /****************************************************************************/ static const char *mcf_type(struct uart_port *port){	return (port->type == PORT_MCF) ? "ColdFire UART" : NULL;} /****************************************************************************/ static int mcf_request_port(struct uart_port *port){	/* UARTs always present */	return 0;} /****************************************************************************/ static void mcf_release_port(struct uart_port *port){	/* Nothing to release... */} /****************************************************************************/ static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser){	if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_MCF))		return -EINVAL;	return 0;} /****************************************************************************/ /* Enable or disable the RS485 support */static int mcf_config_rs485(struct uart_port *port, struct ktermios *termios,			    struct serial_rs485 *rs485){	unsigned char mr1, mr2; 	/* Get mode registers */	mr1 = readb(port->membase + MCFUART_UMR);	mr2 = readb(port->membase + MCFUART_UMR);	if (rs485->flags & SER_RS485_ENABLED) {		dev_dbg(port->dev, "Setting UART to RS485\n");		/* Automatically negate RTS after TX completes */		mr2 |= MCFUART_MR2_TXRTS;	} else {		dev_dbg(port->dev, "Setting UART to RS232\n");		mr2 &= ~MCFUART_MR2_TXRTS;	}	writeb(mr1, port->membase + MCFUART_UMR);	writeb(mr2, port->membase + MCFUART_UMR); 	return 0;} static const struct serial_rs485 mcf_rs485_supported = {	.flags = SER_RS485_ENABLED | SER_RS485_RTS_AFTER_SEND,}; /****************************************************************************/ /* *	Define the basic serial functions we support. */static const struct uart_ops mcf_uart_ops = {	.tx_empty	= mcf_tx_empty,	.get_mctrl	= mcf_get_mctrl,	.set_mctrl	= mcf_set_mctrl,	.start_tx	= mcf_start_tx,	.stop_tx	= mcf_stop_tx,	.stop_rx	= mcf_stop_rx,	.break_ctl	= mcf_break_ctl,	.startup	= mcf_startup,	.shutdown	= mcf_shutdown,	.set_termios	= mcf_set_termios,	.type		= mcf_type,	.request_port	= mcf_request_port,	.release_port	= mcf_release_port,	.config_port	= mcf_config_port,	.verify_port	= mcf_verify_port,}; static struct mcf_uart mcf_ports[10]; #define	MCF_MAXPORTS	ARRAY_SIZE(mcf_ports) /****************************************************************************/#if defined(CONFIG_SERIAL_MCF_CONSOLE)/****************************************************************************/ static void mcf_console_putc(struct console *co, const char c){	struct uart_port *port = &(mcf_ports + co->index)->port;	int i; 	for (i = 0; (i < 0x10000); i++) {		if (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY)			break;	}	writeb(c, port->membase + MCFUART_UTB);	for (i = 0; (i < 0x10000); i++) {		if (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY)			break;	}} /****************************************************************************/ static void mcf_console_write(struct console *co, const char *s, unsigned int count){	for (; (count); count--, s++) {		mcf_console_putc(co, *s);		if (*s == '\n')			mcf_console_putc(co, '\r');	}} /****************************************************************************/

Lines 1–500 of 662.