diff options
author | Katsunaga Kinoshita <katsu@katsu-ubuntu.(none)> | 2012-05-31 17:51:00 +0900 |
---|---|---|
committer | Justin Waters <justin.waters@timesys.com> | 2012-07-03 17:01:11 -0400 |
commit | f553c19ab631bbefcd2f67cb0847a9cde8c351dd (patch) | |
tree | 7893c07a40b03009dfe2ece944dbf5b7bd2db7eb /drivers | |
parent | 162596a9af091775b330563f3f4aedd738c2b4f9 (diff) |
chane_20120531
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/tty/serial/Kconfig | 24 | ||||
-rw-r--r-- | drivers/tty/serial/Makefile | 2 | ||||
-rw-r--r-- | drivers/tty/serial/mvf_uart_early.c | 204 | ||||
-rw-r--r-- | drivers/tty/serial/serial_mvf.c | 1025 |
4 files changed, 1255 insertions, 0 deletions
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 9789293a8441..88d43a970ec6 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -838,6 +838,30 @@ config SERIAL_IMX_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) +config SERIAL_MVF + bool "MVF serial port support" + depends on ARM && (ARCH_MVF) + select SERIAL_CORE + select RATIONAL + help + If you have a machine based on a Freescale MVF CPU you + can enable its onboard serial port by enabling this option. + +config SERIAL_MVF_CONSOLE + bool "Console on MVF serial port" + depends on SERIAL_MVF + select SERIAL_CORE_CONSOLE + help + If you have enabled the serial port on the Freescale MVF + CPU you can make it the console by answering Y to this option. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttySA0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + config SERIAL_UARTLITE tristate "Xilinx uartlite serial port support" depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 5f9f9e69a4dc..17ce76a5e641 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o obj-$(CONFIG_SERIAL_SGI_L1_CONSOLE) += sn_console.o obj-$(CONFIG_SERIAL_CPM) += cpm_uart/ obj-$(CONFIG_SERIAL_IMX) += imx.o +obj-$(CONFIG_SERIAL_MVF) += serial_mvf.o obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o obj-$(CONFIG_SERIAL_ICOM) += icom.o obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o @@ -97,3 +98,4 @@ obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o obj-$(CONFIG_SERIAL_IMX_CONSOLE) += mxc_uart_early.o +obj-$(CONFIG_SERIAL_MVF_CONSOLE) += mvf_uart_early.o diff --git a/drivers/tty/serial/mvf_uart_early.c b/drivers/tty/serial/mvf_uart_early.c new file mode 100644 index 000000000000..2d6d4e9fb184 --- /dev/null +++ b/drivers/tty/serial/mvf_uart_early.c @@ -0,0 +1,204 @@ +/* + * based on drivers/tty/serial/mxc_uart_early.c + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/*! + * @file drivers/serial/mvf_uart_early.c + * + * @brief Driver for the Freescale Semiconductor MVF serial ports based on + * drivers/char/8250_early.c, + * Copyright 2004 Hewlett-Packard Development Company, + * L.P.by Bjorn Helgaasby. + * + * Early serial console for MVF UARTS. + * + * This is for use before the serial driver has initialized, in + * particular, before the UARTs have been discovered and named. + * Instead of specifying the console device as, e.g., "ttymvf0", + * we locate the device directly by its MMIO or I/O port address. + * + * The user can specify the device directly, e.g., + * console=mvfuart,0x43f90000,115200n8 + * or platform code can call early_uart_console_init() to set + * the early UART device. + * + * After the normal serial driver starts, we try to locate the + * matching ttymvf device and start a console there. + */ + +/* + * Include Files + */ + +#include <linux/tty.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/console.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> +#include <linux/clk.h> +#include <mach/mvf_uart.h> + +struct mvf_early_uart_device { + struct uart_port port; + char options[16]; /* e.g., 115200n8 */ + unsigned int baud; + struct clk *clk; +}; +static struct mvf_early_uart_device mvf_early_device __initdata; + +static inline int mvf_uart_enable(struct mvf_port *sport) +{ + unsinged char c2; + c2 = readb(sport->port.membase + MVF_UART_C2); + c2 |= (UART_C2_TE | UART_C2_RE); + writeb(c2, sport->port.membase + MVF_UART_C2); + +} +static inline int mvf_uart_disable(struct mvf_port *sport) +{ + c2 = readb(sport->port.membase + MVF_UART_C2); + c2 &= ~(UART_C2_TE | UART_C2_RE); + writeb(c2, sport->port.membase + MVF_UART_C2); + +} + +/* + * Write out a character once the UART is ready + */ +static void __init mvfuart_console_write_char(struct uart_port *port, int ch) +{ + unsigned int status; + + while (readb(sport->port.membase + S1) & UART_S1_TDRE) + barrier(); + + writeb(ch, sport->port.membase + UART_D); + +} + +/*! + * This function is called to write the console messages through the UART port. + * + * @param co the console structure + * @param s the log message to be written to the UART + * @param count length of the message + */ +void __init early_mvfuart_console_write(struct console *co, const char *s, + u_int count) +{ + struct uart_port *port = &mvf_early_device.port; + unsigned int status, oldc1, oldc2, oldc3, c1, c2, c3; + + /* + * First save the control registers and then disable the interrupts + */ + oldc1 = readb(port->membase + UART_C1); + oldc2 = readb(port->membase + UART_C2); + oldc3 = readb(port->membase + UART_C3); + + c2 = oldc2 & ~(UART_C2_TE | UART_C2_RE + | UART_C2_RIE | UART_C2_RIE); + + writeb(c2, port->membase + MVF_UARTUCR2); + + /* Transmit string */ + uart_console_write(port, s, count, mvfuart_console_write_char); + + /* + * Finally, wait for the transmitter to become empty + */ + do { + status = readb(port->membase + UART_S1); + } while (!(status & UART_S1_TDRE)); + + /* + * Restore the control registers + */ + writeb(oldc2, port->membase + UART_C2); + +} + +static unsigned int __init probe_baud(struct uart_port *port) +{ + /* FIXME Return Default Baud Rate */ + return 115200; +} + +static int __init mvf_early_uart_setup(struct console *console, char *options) +{ + struct mvf_early_uart_device *device = &mvf_early_device; + struct uart_port *port = &device->port; + int length; + + if (device->port.membase || device->port.iobase) + return -ENODEV; + + /* Enable Early MVF UART Clock */ + clk_enable(device->clk); + + port->uartclk = 50000000; + port->iotype = UPIO_MEM; + port->membase = ioremap(port->mapbase, SZ_4K); + + if (options) { + device->baud = simple_strtoul(options, NULL, 0); + length = min(strlen(options), sizeof(device->options)); + strncpy(device->options, options, length); + } else { + device->baud = probe_baud(port); + snprintf(device->options, sizeof(device->options), "%u", + device->baud); + } + printk(KERN_INFO + "MVF_Early serial console at MMIO 0x%x (options '%s')\n", + port->mapbase, device->options); + return 0; +} + +static struct console mvf_early_uart_console __initdata = { + .name = "ttymvf", + .write = early_mvfuart_console_write, + .setup = mvf_early_uart_setup, + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1, +}; + +int __init mvf_early_serial_console_init(unsigned long base, struct clk *clk) +{ + mvf_early_device.clk = clk; + mvf_early_device.port.mapbase = base; + + register_console(&mvf_early_uart_console); + return 0; +} + +int __init mvf_early_uart_console_disable(void) +{ + struct mvf_early_uart_device *device = &mvf_early_device; + struct uart_port *port = &device->port; + + if (mvf_early_uart_console.index >= 0) { + iounmap(port->membase); + clk_disable(device->clk); + clk_put(device->clk); + } + return 0; +} +late_initcall(mvf_early_uart_console_disable); diff --git a/drivers/tty/serial/serial_mvf.c b/drivers/tty/serial/serial_mvf.c new file mode 100644 index 000000000000..c0b5692856cb --- /dev/null +++ b/drivers/tty/serial/serial_mvf.c @@ -0,0 +1,1025 @@ +/* + * Driver for MVF serial ports + * + * Copyright 2012 + * + * Based on drivers/tty/serial/imx.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/*! + * @file drivers/serial/serial_mvf.c + * + */ + + +#include <mach/mvf_uart.h> + +#define DRIVER_NAME "MVF-uart" + +#define UART_NR 6 + +struct mvf_port { + struct uart_port port; + struct time_list timer; + unsigned int old_status; + int txirq,rxirqmrtsirq; + unsigned int +}; + +#ifdef CONFIG_IRDA +# Error IRDA not implemented yet. +#endif + + +static inline int mvf_set_bps(struct mvf_port *sport, + unsigned long base,unsigned long bps) +{ + unsigned char bdh,bdl; + unsigned long sbr; + sbr = base/((bps*10)+5)/160; + bdh = readb(sport->port.membase + MVF_UART_BDH) & 0xc0; + bdh = (sbr>>8) & 0x1f; + bdl = (sbr&0xff); + writeb(bdh, sport->port.membase + MVF_UART_BDH); + writeb(bdl, sport->port.membase + MVF_UART_BDL); + +} + +static inline mvf_set_paerity(struct mvf_port *sport, + unsigned long base,unsigned short ) + +static inline int mvf_uart_enable(struct mvf_port *sport) +{ + unsinged char c2; + c2 = readb(sport->port.membase + MVF_UART_C2); + c2 |= (UART_C2_TE | UART_C2_RE); + writeb(c2, sport->port.membase + MVF_UART_C2); + +} +static inline int mvf_uart_disable(struct mvf_port *sport) +{ + c2 = readb(sport->port.membase + MVF_UART_C2); + c2 &= ~(UART_C2_TE | UART_C2_RE); + writeb(c2, sport->port.membase + MVF_UART_C2); + +} + + +/* + * Handle any change of modem status signal since we were last called. + */ +static void mvf_mctrl_check(struct mvf_port *sport) +{ + /* + * TDB + */ + return ; + +} + +/* + * This is our per-port timeout handler, for checking the + * modem status signals. + */ +static void mvf_timeout(unsigned long data) +{ + /* + * TDB + */ + return; +} + +/* + * interrupts disabled on entry + */ +static void mvf_stop_tx(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned long temp; + + if (USE_IRDA(sport)) { + /* half duplex - wait for end of transmission */ + int n = 256; + while ((--n > 0) && + !(readb(sport->port.membase + USR2) & USR2_TXDC)) { + udelay(5); + barrier(); + } + /* + * irda transceiver - wait a bit more to avoid + * cutoff, hardware dependent + */ + udelay(sport->trcv_delay); + + /* + * half duplex - reactivate receive mode, + * flush receive pipe echo crap + */ + if (readb(sport->port.membase + USR2) & USR2_TXDC) { + temp = readb(sport->port.membase + UCR1); + temp &= ~(UCR1_TXMPTYEN | UCR1_TRDYEN); + writeb(temp, sport->port.membase + UCR1); + + temp = readb(sport->port.membase + UCR4); + temp &= ~(UCR4_TCEN); + writeb(temp, sport->port.membase + UCR4); + + while (readb(sport->port.membase + URXD0) & + URXD_CHARRDY) + barrier(); + + temp = readb(sport->port.membase + UCR1); + temp |= UCR1_RRDYEN; + writeb(temp, sport->port.membase + UCR1); + + temp = readb(sport->port.membase + UCR4); + temp |= UCR4_DREN; + writeb(temp, sport->port.membase + UCR4); + } + return; + } + + temp = readb(sport->port.membase + UCR1); + writeb(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1); +} + +/* + * interrupts disabled on entry + */ +static void mvf_stop_rx(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned long temp; + + temp = readb(sport->port.membase + UCR2); + writeb(temp &~ UCR2_RXEN, sport->port.membase + UCR2); +} + +/* + * Set the modem control timer to fire immediately. + */ +static void mvf_enable_ms(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + + mod_timer(&sport->timer, jiffies); +} + +static inline void mvf_transmit_buffer(struct mvf_port *sport) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + + while (!uart_circ_empty(xmit) && + !(readb(sport->port.membase + UTS) & UTS_TXFULL)) { + /* send xmit->buf[xmit->tail] + * out the port here */ + writeb(xmit->buf[xmit->tail], sport->port.membase + URTX0); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(xmit)) + mvf_stop_tx(&sport->port); +} + + +/* + * interrupts disabled on entry + */ +static void mvf_start_tx(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned long temp; + + if (USE_IRDA(sport)) { + /* half duplex in IrDA mode; have to disable receive mode */ + temp = readb(sport->port.membase + UCR4); + temp &= ~(UCR4_DREN); + writeb(temp, sport->port.membase + UCR4); + + temp = readb(sport->port.membase + UCR1); + temp &= ~(UCR1_RRDYEN); + writeb(temp, sport->port.membase + UCR1); + } + + + if (USE_IRDA(sport)) { + temp = readb(sport->port.membase + UCR1); + temp |= UCR1_TRDYEN; + writeb(temp, sport->port.membase + UCR1); + + temp = readb(sport->port.membase + UCR4); + temp |= UCR4_TCEN; + writeb(temp, sport->port.membase + UCR4); + } + + + if (readb(sport->port.membase + UTS) & UTS_TXEMPTY) + mvf_transmit_buffer(sport); +} + +static irqreturn_t mvf_rtsint(int irq, void *dev_id) +{ + struct mvf_port *sport = dev_id; + unsigned int val; + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock, flags); + + writeb(USR1_RTSD, sport->port.membase + USR1); + val = readb(sport->port.membase + USR1) & USR1_RTSS; + uart_handle_cts_change(&sport->port, !!val); + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); + + spin_unlock_irqrestore(&sport->port.lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t mvf_txint(int irq, void *dev_id) +{ + struct mvf_port *sport = dev_id; + struct circ_buf *xmit = &sport->port.state->xmit; + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock,flags); + if (sport->port.x_char) + { + /* Send next char */ + writeb(sport->port.x_char, sport->port.membase + URTX0); + goto out; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + mvf_stop_tx(&sport->port); + goto out; + } + + mvf_transmit_buffer(sport); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + +out: + spin_unlock_irqrestore(&sport->port.lock,flags); + return IRQ_HANDLED; +} + +static irqreturn_t mvf_rxint(int irq, void *dev_id) +{ + struct mvf_port *sport = dev_id; + unsigned int rx,flg,ignored = 0; + struct tty_struct *tty = sport->port.state->port.tty; + unsigned long flags, temp; + + spin_lock_irqsave(&sport->port.lock,flags); + + while (readb(sport->port.membase + USR2) & USR2_RDR) { + flg = TTY_NORMAL; + sport->port.icount.rx++; + + rx = readb(sport->port.membase + URXD0); + + temp = readb(sport->port.membase + USR2); + if (temp & USR2_BRCD) { + writeb(USR2_BRCD, sport->port.membase + USR2); + if (uart_handle_break(&sport->port)) + continue; + } + + if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) + continue; + + if (rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR) ) { + if (rx & URXD_PRERR) + sport->port.icount.parity++; + else if (rx & URXD_FRMERR) + sport->port.icount.frame++; + if (rx & URXD_OVRRUN) + sport->port.icount.overrun++; + + if (rx & sport->port.ignore_status_mask) { + if (++ignored > 100) + goto out; + continue; + } + + rx &= sport->port.read_status_mask; + + if (rx & URXD_PRERR) + flg = TTY_PARITY; + else if (rx & URXD_FRMERR) + flg = TTY_FRAME; + if (rx & URXD_OVRRUN) + flg = TTY_OVERRUN; + +#ifdef SUPPORT_SYSRQ + sport->port.sysrq = 0; +#endif + } + + tty_insert_flip_char(tty, rx, flg); + } + +out: + spin_unlock_irqrestore(&sport->port.lock,flags); + tty_flip_buffer_push(tty); + return IRQ_HANDLED; +} + + +static irqreturn_t mvf_int(int irq, void *dev_id) +{ + struct mvf_port *sport = dev_id; + unsigned int sts; + + sts = readb(sport->port.membase + USR1); + + if (sts & USR1_RRDY) { + mvf_rxint(irq, dev_id); + } + + if (sts & USR1_TRDY && + readb(sport->port.membase + UCR1) & UCR1_TXMPTYEN) + mvf_txint(irq, dev_id); + + if (sts & USR1_RTSD) + mvf_rtsint(irq, dev_id); + + if (sts & USR1_AWAKE) + writeb(USR1_AWAKE, sport->port.membase + USR1); + + return IRQ_HANDLED; +} + + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static unsigned int mvf_tx_empty(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + + return (readb(sport->port.membase + USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0; +} + +/* + * We have a modem side uart, so the meanings of RTS and CTS are inverted. + */ +static unsigned int mvf_get_mctrl(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned int tmp = TIOCM_DSR | TIOCM_CAR; + + if (readb(sport->port.membase + USR1) & USR1_RTSS) + tmp |= TIOCM_CTS; + + if (readb(sport->port.membase + UCR2) & UCR2_CTS) + tmp |= TIOCM_RTS; + + return tmp; +} + +static void mvf_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned long temp; + + temp = readb(sport->port.membase + UCR2) & ~UCR2_CTS; + + if (mctrl & TIOCM_RTS) + temp |= UCR2_CTS; + + writeb(temp, sport->port.membase + UCR2); +} + +/* + * Interrupts always disabled. + */ +static void mvf_break_ctl(struct uart_port *port, int break_state) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned long flags, temp; + + spin_lock_irqsave(&sport->port.lock, flags); + + temp = readb(sport->port.membase + UCR1) & ~UCR1_SNDBRK; + + if ( break_state != 0 ) + temp |= UCR1_SNDBRK; + + writeb(temp, sport->port.membase + UCR1); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +#define TXTL 2 /* reset default */ +#define RXTL 1 /* reset default */ + + +static int mvf_startup(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + int retval; + unsigned long flags, temp; + struct tty_struct *tty; + + + /* disable the DREN bit (Data Ready interrupt enable) before + * requesting IRQs + */ + temp = readb(sport->port.membase + UCR4); + + if (USE_IRDA(sport)) + temp |= UCR4_IRSC; + + /* set the trigger level for CTS */ + temp &= ~(UCR4_CTSTL_MASK<< UCR4_CTSTL_SHF); + temp |= CTSTL<< UCR4_CTSTL_SHF; + + writeb(temp & ~UCR4_DREN, sport->port.membase + UCR4); + + /* + * Allocate the IRQ(s) i.MX1 has three interrupts whereas later + * chips only have one interrupt. + */ + if (sport->txirq > 0) { + retval = request_irq(sport->rxirq, mvf_rxint, 0, + DRIVER_NAME, sport); + if (retval) + goto error_out1; + + retval = request_irq(sport->txirq, mvf_txint, 0, + DRIVER_NAME, sport); + if (retval) + goto error_out2; + + /* do not use RTS IRQ on IrDA */ + if (!USE_IRDA(sport)) { + retval = request_irq(sport->rtsirq, mvf_rtsint, + (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 : + IRQF_TRIGGER_FALLING | + IRQF_TRIGGER_RISING, + DRIVER_NAME, sport); + if (retval) + goto error_out3; + } + } else { + retval = request_irq(sport->port.irq, mvf_int, 0, + DRIVER_NAME, sport); + if (retval) { + free_irq(sport->port.irq, sport); + goto error_out1; + } + } + + spin_lock_irqsave(&sport->port.lock, flags); + /* + * Finally, clear and enable interrupts + */ + writeb(USR1_RTSD, sport->port.membase + USR1); + + temp = readb(sport->port.membase + UCR1); + temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN; + if (sport->enable_dma) { + temp |= UCR1_RDMAEN | UCR1_TDMAEN; + /* ICD, wait for more than 32 frames, but it still to short. */ + temp |= UCR1_ICD_REG(3); + } + + if (USE_IRDA(sport)) { + temp |= UCR1_IREN; + temp &= ~(UCR1_RTSDEN); + } + + writeb(temp, sport->port.membase + UCR1); + + temp = readb(sport->port.membase + UCR2); + temp |= (UCR2_RXEN | UCR2_TXEN); + writeb(temp, sport->port.membase + UCR2); + + if (USE_IRDA(sport)) { + /* clear RX-FIFO */ + int i = 64; + while ((--i > 0) && + (readb(sport->port.membase + URXD0) & URXD_CHARRDY)) { + barrier(); + } + } + + if (!cpu_is_mx1()) { + temp = readb(sport->port.membase + UCR3); + temp |= MX2_UCR3_RXDMUXSEL; + writeb(temp, sport->port.membase + UCR3); + } + + if (USE_IRDA(sport)) { + temp = readb(sport->port.membase + UCR4); + if (sport->irda_inv_rx) + temp |= UCR4_INVR; + else + temp &= ~(UCR4_INVR); + writeb(temp | UCR4_DREN, sport->port.membase + UCR4); + + temp = readb(sport->port.membase + UCR3); + if (sport->irda_inv_tx) + temp |= UCR3_INVT; + else + temp &= ~(UCR3_INVT); + writeb(temp, sport->port.membase + UCR3); + } + + if (sport->enable_dma) { + temp = readb(sport->port.membase + UCR4); + temp |= UCR4_IDDMAEN; + writeb(temp, sport->port.membase + UCR4); + } + + /* + * Enable modem status interrupts + */ + mvf_enable_ms(&sport->port); + spin_unlock_irqrestore(&sport->port.lock,flags); + + if (USE_IRDA(sport)) { + struct mvfuart_platform_data *pdata; + pdata = sport->port.dev->platform_data; + sport->irda_inv_rx = pdata->irda_inv_rx; + sport->irda_inv_tx = pdata->irda_inv_tx; + sport->trcv_delay = pdata->transceiver_delay; + if (pdata->irda_enable) + pdata->irda_enable(1); + } + + tty = sport->port.state->port.tty; + + return 0; + +error_out3: + if (sport->txirq) + free_irq(sport->txirq, sport); +error_out2: + if (sport->rxirq) + free_irq(sport->rxirq, sport); +error_out1: + return retval; +} + +static void mvf_shutdown(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned long temp; + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock, flags); + temp = readb(sport->port.membase + UCR2); + temp &= ~(UCR2_TXEN); + writeb(temp, sport->port.membase + UCR2); + spin_unlock_irqrestore(&sport->port.lock, flags); + + if (USE_IRDA(sport)) { + struct mvfuart_platform_data *pdata; + pdata = sport->port.dev->platform_data; + if (pdata->irda_enable) + pdata->irda_enable(0); + } + + /* + * Stop our timer. + */ + del_timer_sync(&sport->timer); + + /* + * Free the interrupts + */ + if (sport->txirq > 0) { + if (!USE_IRDA(sport)) + free_irq(sport->rtsirq, sport); + free_irq(sport->txirq, sport); + free_irq(sport->rxirq, sport); + } else + free_irq(sport->port.irq, sport); + + /* + * Disable all interrupts, port and break condition. + */ + + spin_lock_irqsave(&sport->port.lock, flags); + temp = readb(sport->port.membase + UCR1); + temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN); + if (USE_IRDA(sport)) + temp &= ~(UCR1_IREN); + writeb(temp, sport->port.membase + UCR1); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static void +mvf_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned long flags; + unsigned char c1,c2,c3,c4; + + /* + * We only support CS8. + */ + termios->c_cflag = CS8; + + c1 = UART_C1_M; + + if (termios->c_cflag & PARENB){ + c1 |= UART_C1_PE; + if (termios->c_cflag & PARODD) + c1 |= UART_C1_PT; + } + + /* + * We only supprt STOPB + */ + + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); + quot = uart_get_divisor(port, baud); + + spin_lock_irqsave(&sport->port.lock, flags); + + + writeb(c1,sport->port.membase + UART_C1); + writeb(c2,sport->port.membase + UART_C2); + writeb(c3,sport->port.membase + UART_C3); + + + + + spin_unlock_irqrestore(&sport->port.lock, flags); + +} + +static const char *mvf_type(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + + return sport->port.type == PORT_MVF ? "MVF" : NULL; +} + +/* + * Release the memory region(s) being used by 'port'. + */ +static void mvf_release_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *mmres; + + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mmres->start, mmres->end - mmres->start + 1); +} + +/* + * Request the memory region(s) being used by 'port'. + */ +static int mvf_request_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *mmres; + void *ret; + + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mmres) + return -ENODEV; + + ret = request_mem_region(mmres->start, mmres->end - mmres->start + 1, + "mvf-uart"); + + return ret ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void mvf_config_port(struct uart_port *port, int flags) +{ + struct mvf_port *sport = (struct mvf_port *)port; + + if (flags & UART_CONFIG_TYPE && + mvf_request_port(&sport->port) == 0) + sport->port.type = PORT_MVF; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + * The only change we allow are to the flags and type, and + * even then only between PORT_MVF and PORT_UNKNOWN + */ +static int +mvf_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + struct faradady_port *sport = (struct mvf_port *)port; + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_MVF) + ret = -EINVAL; + if (sport->port.irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != UPIO_MEM) + ret = -EINVAL; + if (sport->port.uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if ((void *)sport->port.mapbase != ser->iomem_base) + ret = -EINVAL; + if (sport->port.iobase != ser->port) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + return ret; +} + + +static struct uart_ops mvf_pops = { + .tx_empty = mvf_tx_empty, + .set_mctrl = mvf_set_mctrl, + .get_mctrl = mvf_get_mctrl, + .stop_tx = mvf_stop_tx, + .start_tx = mvf_start_tx, + .stop_rx = mvf_stop_rx, + .enable_ms = mvf_enable_ms, + .break_ctl = mvf_break_ctl, + .startup = mvf_startup, + .shutdown = mvf_shutdown, + .set_termios = mvf_set_termios, + .type = mvf_type, + .release_port = mvf_release_port, + .request_port = mvf_request_port, + .config_port = mvf_config_port, + .verify_port = mvf_verify_port, +}; + + +static struct mvf_ports[UART_NR]; + +static void mvf_console_putchar(struct uart_port *port, int ch) +{ + struct imx_port *sport = (struct imx_port *)port; + + while (readb(sport->port.membase + S1) & UART_S1_TDRE) + barrier(); + + writeb(ch, sport->port.membase + UART_D); +} + +static void +mvf_console_write(struct console *co, const char *s, unsigned int count) +{ + uart_console_write(&sport->port, s, count, mvf_console_putchar); + return; +} +/* + * If the port was already initialised (eg, by a boot loader), + * try to determine the current setup. + */ +static void __init +imx_console_get_options(struct imx_port *sport, int *baud, + int *parity, int *bits) +{ + /* UART Enable */ + if ( readb(soprt->port.membase + UART_C1) & (UART_C1_TE | UART_C1_RE)) { + unsigned char c1,c2,c3,c4; + unsigend short br; + c1 = readb(soprt->port.membase + UART_C1); + *parity = 'n'; + if ( c1 & UART_C1_PE) + *parity = (c1 & UART_C1_PT)?'o':'e'; + *bits = 7; + br = readb(soprt->port.membase + UART_BDH)<<8 + | readb(soprt->port.membase + UART_BDH); + br &= 0x1fff; + } +} +static int __init +mvf_console_setup(struct console *co,char *options) +{ + struct mvf_port *sport; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index == -1 || co->index >= ARRAY_SIZE(mvf_ports)) + co->index = 0; + sport = imx_ports[co->index]; + if(sport == NULL) + return -ENODEV; + return uart_set_options(&sport->port, co, baud, parity, bits, flow); +} + + +static struct console mvf_console = { + .name = DEV_NAME, + .write = mvf_console_write, + .device = uart_console_device, + .setup = mvf_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &mvf_reg, +}; + +static struct uart_drive mvf_reg = { + .onwer = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = DEV_NAME, + .major = SERIAL_MVF_MAJOR, + .minor = MINOR_START, + .nr = ARRAY_SIZE(mvf_ports), + .cons = MVF_CONSOLE, +}; + + + +static int serial_farada_suspend(struct platform_device *dev,pm_message_t state) +{ + /* TBD */ + return 0; +} + +static int serial_mvf_resume(struct platform_device *dev) +{ + /* TBD */ + return 0; +} + +#idef CONFIG_PM +static consit struct dev_pm_ops serial_mvf_pm_ops = { + .suspend = serial_mvf_suspend, + .resume = serial_mvf_resume, +}; +#endif + + +static int seryal_mvf_probe(struct platform_device *dev) +{ + struct mvf_port *sport; + struct mvf_platform_data *pdata; + void __iomem *base; + int ret = 0; + struct resource *res; + + sport = kzalloc(sizeof(*sport),GFP_KERNEL); + + if ( !sport ) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if ( !res ){ + ret = ENODEV; + goto free; + } + + base = ioremap(res->start, PAGE_SIZE); + + + sport->port.dev = &pdev->dev; + sport->port.mapbase = res->start; + sport->port.membase = base; + sport->port.type = PORT_MVF, + sport->port.iotype = UPIO_MEM; + sport->port.irq = platform_get_irq(pdev, 0); + sport->rxirq = platform_get_irq(pdev, 0); + sport->txirq = platform_get_irq(pdev, 1); + sport->rtsirq = platform_get_irq(pdev, 2); + sport->port.fifosize = 32; + sport->port.ops = &mvf_pops; + sport->port.flags = UPF_BOOT_AUTOCONF; + sport->port.line = pdev->id; + init_timer(&sport->timer); + sport->timer.function = mvf_timeout; + sport->timer.data = (unsigned long)sport; + + sport->clk = clk_get(&pdev->dev, "uart"); + if (IS_ERR(sport->clk)) { + ret = PTR_ERR(sport->clk); + goto unmap; + } + clk_enable(sport->clk); + + sport->port.uartclk = clk_get_rate(sport->clk); + + mvf_ports[pdev->id] = sport; + + pdata = pdev->dev.platform_data; + if (pdata && (pdata->flags & MVFUART_HAVE_RTSCTS)) + sport->have_rtscts = 1; + if (pdata && (pdata->flags & MVFUART_USE_DCEDTE)) + sport->use_dcedte = 1; + if (pdata && (pdata->flags & MVFUART_SDMA)) + sport->enable_dma = 1; + +#ifdef CONFIG_IRDA + if (pdata && (pdata->flags & MVFUART_IRDA)) + sport->use_irda = 1; +#endif + + if (pdata && pdata->init) { + ret = pdata->init(pdev); + if (ret) + goto clkput; + } + + ret = uart_add_one_port(&mvf_reg, &sport->port); + if (ret) + goto deinit; + platform_set_drvdata(pdev, &sport->port); + + return 0; +deinit: + if (pdata && pdata->exit) + pdata->exit(pdev); +clkput: + clk_put(sport->clk); + clk_disable(sport->clk); +unmap: + iounmap(sport->port.membase); +free: + kfree(sport); + + return ret; + + + +} + + +static void serial_mvf_remove(struct platform_device *dev) +{ + + return; + + +static struct platform_driver serial_mvf_driver = { + .probe = serial_mvf_probe, + .remove = serial_mvf_remove, + + .driver = { + .name = "mvf-uart", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &serial_mvf_pm_ops, + }, +}; + + +static int __init mvf_serial_init(void) +{ + int ret; + printk(KERN_INFO "Serial: Mvf driver\n"); + ret = uart_regiseter_driver(&mvf_reg); + if ( ret ) + return ret; + + ret = platform_driver_register(&serial_mvf_driver); + if ( ret != 0 ) + uart_unregister_driver(&mvf_reg); + + return 0; +} + + +static void __exit mvf_serial_exit(void) +{ + platform_driver_unregiseter(&serial_mvf_driver); + uart_unregister_driver(&mvf_reg); +} + + +module_init(mvf_serial_init); +module_exit(mvf_serial_exit); + +MODULES_AUTHER(""); +MODULES_DESCRIPTION("Faradeay serial port driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:"); |