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

📄 mpc52xx_uart.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Driver for the PSC of the Freescale MPC52xx PSCs configured as UARTs. * * FIXME According to the usermanual the status bits in the status register * are only updated when the peripherals access the FIFO and not when the * CPU access them. So since we use this bits to know when we stop writing * and reading, they may not be updated in-time and a race condition may * exists. But I haven't be able to prove this and I don't care. But if * any problem arises, it might worth checking. The TX/RX FIFO Stats * registers should be used in addition. * Update: Actually, they seem updated ... At least the bits we use. * * * Maintainer : Sylvain Munaut <tnt@246tNt.com> * * Some of the code has been inspired/copied from the 2.4 code written * by Dale Farnsworth <dfarnsworth@mvista.com>. * * Copyright (C) 2006 Secret Lab Technologies Ltd. *                    Grant Likely <grant.likely@secretlab.ca> * Copyright (C) 2004-2006 Sylvain Munaut <tnt@246tNt.com> * Copyright (C) 2003 MontaVista, Software, Inc. * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. *//* Platform device Usage : * * Since PSCs can have multiple function, the correct driver for each one * is selected by calling mpc52xx_match_psc_function(...). The function * handled by this driver is "uart". * * The driver init all necessary registers to place the PSC in uart mode without * DCD. However, the pin multiplexing aren't changed and should be set either * by the bootloader or in the platform init code. * * The idx field must be equal to the PSC index ( e.g. 0 for PSC1, 1 for PSC2, * and so on). So the PSC1 is mapped to /dev/ttyPSC0, PSC2 to /dev/ttyPSC1 and * so on. But be warned, it's an ABSOLUTE REQUIREMENT ! This is needed mainly * fpr the console code : without this 1:1 mapping, at early boot time, when we * are parsing the kernel args console=ttyPSC?, we wouldn't know which PSC it * will be mapped to. *//* OF Platform device Usage : * * This driver is only used for PSCs configured in uart mode.  The device * tree will have a node for each PSC in uart mode w/ device_type = "serial" * and "mpc52xx-psc-uart" in the compatible string * * By default, PSC devices are enumerated in the order they are found.  However * a particular PSC number can be forces by adding 'device_no = <port#>' * to the device node. * * The driver init all necessary registers to place the PSC in uart mode without * DCD. However, the pin multiplexing aren't changed and should be set either * by the bootloader or in the platform init code. */#undef DEBUG#include <linux/device.h>#include <linux/module.h>#include <linux/tty.h>#include <linux/serial.h>#include <linux/sysrq.h>#include <linux/console.h>#include <asm/delay.h>#include <asm/io.h>#if defined(CONFIG_PPC_MERGE)#include <asm/of_platform.h>#else#include <linux/platform_device.h>#endif#include <asm/mpc52xx.h>#include <asm/mpc52xx_psc.h>#if defined(CONFIG_SERIAL_MPC52xx_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/serial_core.h>/* We've been assigned a range on the "Low-density serial ports" major */#define SERIAL_PSC_MAJOR	204#define SERIAL_PSC_MINOR	148#define ISR_PASS_LIMIT 256	/* Max number of iteration in the interrupt */static struct uart_port mpc52xx_uart_ports[MPC52xx_PSC_MAXNUM];	/* Rem: - We use the read_status_mask as a shadow of	 *        psc->mpc52xx_psc_imr	 *      - It's important that is array is all zero on start as we	 *        use it to know if it's initialized or not ! If it's not sure	 *        it's cleared, then a memset(...,0,...) should be added to	 *        the console_init	 */#if defined(CONFIG_PPC_MERGE)/* lookup table for matching device nodes to index numbers */static struct device_node *mpc52xx_uart_nodes[MPC52xx_PSC_MAXNUM];static void mpc52xx_uart_of_enumerate(void);#endif#define PSC(port) ((struct mpc52xx_psc __iomem *)((port)->membase))/* Forward declaration of the interruption handling routine */static irqreturn_t mpc52xx_uart_int(int irq,void *dev_id);/* Simple macro to test if a port is console or not. This one is taken * for serial_core.c and maybe should be moved to serial_core.h ? */#ifdef CONFIG_SERIAL_CORE_CONSOLE#define uart_console(port)	((port)->cons && (port)->cons->index == (port)->line)#else#define uart_console(port)	(0)#endif#if defined(CONFIG_PPC_MERGE)static struct of_device_id mpc52xx_uart_of_match[] = {	{ .type = "serial", .compatible = "mpc5200-psc-uart", },	{},};#endif/* ======================================================================== *//* UART operations                                                          *//* ======================================================================== */static unsigned intmpc52xx_uart_tx_empty(struct uart_port *port){	int status = in_be16(&PSC(port)->mpc52xx_psc_status);	return (status & MPC52xx_PSC_SR_TXEMP) ? TIOCSER_TEMT : 0;}static voidmpc52xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl){	/* Not implemented */}static unsigned intmpc52xx_uart_get_mctrl(struct uart_port *port){	/* Not implemented */	return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;}static voidmpc52xx_uart_stop_tx(struct uart_port *port){	/* port->lock taken by caller */	port->read_status_mask &= ~MPC52xx_PSC_IMR_TXRDY;	out_be16(&PSC(port)->mpc52xx_psc_imr,port->read_status_mask);}static voidmpc52xx_uart_start_tx(struct uart_port *port){	/* port->lock taken by caller */	port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY;	out_be16(&PSC(port)->mpc52xx_psc_imr,port->read_status_mask);}static voidmpc52xx_uart_send_xchar(struct uart_port *port, char ch){	unsigned long flags;	spin_lock_irqsave(&port->lock, flags);	port->x_char = ch;	if (ch) {		/* Make sure tx interrupts are on */		/* Truly necessary ??? They should be anyway */		port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY;		out_be16(&PSC(port)->mpc52xx_psc_imr,port->read_status_mask);	}	spin_unlock_irqrestore(&port->lock, flags);}static voidmpc52xx_uart_stop_rx(struct uart_port *port){	/* port->lock taken by caller */	port->read_status_mask &= ~MPC52xx_PSC_IMR_RXRDY;	out_be16(&PSC(port)->mpc52xx_psc_imr,port->read_status_mask);}static voidmpc52xx_uart_enable_ms(struct uart_port *port){	/* Not implemented */}static voidmpc52xx_uart_break_ctl(struct uart_port *port, int ctl){	unsigned long flags;	spin_lock_irqsave(&port->lock, flags);	if ( ctl == -1 )		out_8(&PSC(port)->command,MPC52xx_PSC_START_BRK);	else		out_8(&PSC(port)->command,MPC52xx_PSC_STOP_BRK);	spin_unlock_irqrestore(&port->lock, flags);}static intmpc52xx_uart_startup(struct uart_port *port){	struct mpc52xx_psc __iomem *psc = PSC(port);	int ret;	/* Request IRQ */	ret = request_irq(port->irq, mpc52xx_uart_int,		IRQF_DISABLED | IRQF_SAMPLE_RANDOM, "mpc52xx_psc_uart", port);	if (ret)		return ret;	/* Reset/activate the port, clear and enable interrupts */	out_8(&psc->command,MPC52xx_PSC_RST_RX);	out_8(&psc->command,MPC52xx_PSC_RST_TX);	out_be32(&psc->sicr,0);	/* UART mode DCD ignored */	out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); /* /16 prescaler on */	out_8(&psc->rfcntl, 0x00);	out_be16(&psc->rfalarm, 0x1ff);	out_8(&psc->tfcntl, 0x07);	out_be16(&psc->tfalarm, 0x80);	port->read_status_mask |= MPC52xx_PSC_IMR_RXRDY | MPC52xx_PSC_IMR_TXRDY;	out_be16(&psc->mpc52xx_psc_imr,port->read_status_mask);	out_8(&psc->command,MPC52xx_PSC_TX_ENABLE);	out_8(&psc->command,MPC52xx_PSC_RX_ENABLE);	return 0;}static voidmpc52xx_uart_shutdown(struct uart_port *port){	struct mpc52xx_psc __iomem *psc = PSC(port);	/* Shut down the port.  Leave TX active if on a console port */	out_8(&psc->command,MPC52xx_PSC_RST_RX);	if (!uart_console(port))		out_8(&psc->command,MPC52xx_PSC_RST_TX);	port->read_status_mask = 0;	out_be16(&psc->mpc52xx_psc_imr,port->read_status_mask);	/* Release interrupt */	free_irq(port->irq, port);}static voidmpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,                         struct ktermios *old){	struct mpc52xx_psc __iomem *psc = PSC(port);	unsigned long flags;	unsigned char mr1, mr2;	unsigned short ctr;	unsigned int j, baud, quot;	/* Prepare what we're gonna write */	mr1 = 0;	switch (new->c_cflag & CSIZE) {		case CS5:	mr1 |= MPC52xx_PSC_MODE_5_BITS;				break;		case CS6:	mr1 |= MPC52xx_PSC_MODE_6_BITS;				break;		case CS7:	mr1 |= MPC52xx_PSC_MODE_7_BITS;				break;		case CS8:		default:	mr1 |= MPC52xx_PSC_MODE_8_BITS;	}	if (new->c_cflag & PARENB) {		mr1 |= (new->c_cflag & PARODD) ?			MPC52xx_PSC_MODE_PARODD : MPC52xx_PSC_MODE_PAREVEN;	} else		mr1 |= MPC52xx_PSC_MODE_PARNONE;	mr2 = 0;	if (new->c_cflag & CSTOPB)		mr2 |= MPC52xx_PSC_MODE_TWO_STOP;	else		mr2 |= ((new->c_cflag & CSIZE) == CS5) ?			MPC52xx_PSC_MODE_ONE_STOP_5_BITS :			MPC52xx_PSC_MODE_ONE_STOP;	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk/16);	quot = uart_get_divisor(port, baud);	ctr = quot & 0xffff;	/* Get the lock */	spin_lock_irqsave(&port->lock, flags);	/* Update the per-port timeout */	uart_update_timeout(port, new->c_cflag, baud);	/* Do our best to flush TX & RX, so we don't loose anything */	/* But we don't wait indefinitly ! */	j = 5000000;	/* Maximum wait */	/* FIXME Can't receive chars since set_termios might be called at early	 * boot for the console, all stuff is not yet ready to receive at that	 * time and that just makes the kernel oops */	/* while (j-- && mpc52xx_uart_int_rx_chars(port)); */	while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP) &&	       --j)		udelay(1);	if (!j)		printk(	KERN_ERR "mpc52xx_uart.c: "			"Unable to flush RX & TX fifos in-time in set_termios."			"Some chars may have been lost.\n" );	/* Reset the TX & RX */	out_8(&psc->command,MPC52xx_PSC_RST_RX);	out_8(&psc->command,MPC52xx_PSC_RST_TX);	/* Send new mode settings */	out_8(&psc->command,MPC52xx_PSC_SEL_MODE_REG_1);	out_8(&psc->mode,mr1);	out_8(&psc->mode,mr2);	out_8(&psc->ctur,ctr >> 8);	out_8(&psc->ctlr,ctr & 0xff);	/* Reenable TX & RX */	out_8(&psc->command,MPC52xx_PSC_TX_ENABLE);	out_8(&psc->command,MPC52xx_PSC_RX_ENABLE);	/* We're all set, release the lock */	spin_unlock_irqrestore(&port->lock, flags);}static const char *mpc52xx_uart_type(struct uart_port *port){	return port->type == PORT_MPC52xx ? "MPC52xx PSC" : NULL;}static voidmpc52xx_uart_release_port(struct uart_port *port){	if (port->flags & UPF_IOREMAP) { /* remapped by us ? */		iounmap(port->membase);		port->membase = NULL;	}	release_mem_region(port->mapbase, sizeof(struct mpc52xx_psc));}static intmpc52xx_uart_request_port(struct uart_port *port){	int err;	if (port->flags & UPF_IOREMAP) /* Need to remap ? */		port->membase = ioremap(port->mapbase,		                        sizeof(struct mpc52xx_psc));	if (!port->membase)		return -EINVAL;	err = request_mem_region(port->mapbase, sizeof(struct mpc52xx_psc),			"mpc52xx_psc_uart") != NULL ? 0 : -EBUSY;

⌨️ 快捷键说明

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