* Add i.MX23/28 auart support @ 2011-01-11 15:09 Sascha Hauer 2011-01-11 15:09 ` [PATCH 1/2] serial: Add auart driver for i.MX23/28 Sascha Hauer ` (2 more replies) 0 siblings, 3 replies; 14+ messages in thread From: Sascha Hauer @ 2011-01-11 15:09 UTC (permalink / raw) To: linux-arm-kernel The following series adds support for the auart found on i.MX23/28 Sascha Hauer (2): serial: Add auart driver for i.MX23/28 ARM i.MXS: Add auart platform support for i.MX28 arch/arm/mach-mxs/clock-mx28.c | 5 + arch/arm/mach-mxs/devices-mx28.h | 8 + arch/arm/mach-mxs/devices/Kconfig | 3 + arch/arm/mach-mxs/devices/Makefile | 1 + arch/arm/mach-mxs/devices/platform-auart.c | 54 ++ arch/arm/mach-mxs/include/mach/devices-common.h | 10 + drivers/serial/Kconfig | 15 + drivers/serial/Makefile | 1 + drivers/serial/mxs-auart.c | 763 +++++++++++++++++++++++ 9 files changed, 860 insertions(+), 0 deletions(-) create mode 100644 arch/arm/mach-mxs/devices/platform-auart.c create mode 100644 drivers/serial/mxs-auart.c ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 1/2] serial: Add auart driver for i.MX23/28 2011-01-11 15:09 Add i.MX23/28 auart support Sascha Hauer @ 2011-01-11 15:09 ` Sascha Hauer 2011-01-11 16:05 ` Russell King - ARM Linux ` (2 more replies) 2011-01-11 15:09 ` [PATCH 2/2] ARM i.MXS: Add auart platform support for i.MX28 Sascha Hauer 2011-01-12 5:31 ` Add i.MX23/28 auart support Shawn Guo 2 siblings, 3 replies; 14+ messages in thread From: Sascha Hauer @ 2011-01-11 15:09 UTC (permalink / raw) To: linux-arm-kernel Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/serial/Kconfig | 15 + drivers/serial/Makefile | 1 + drivers/serial/mxs-auart.c | 763 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 779 insertions(+), 0 deletions(-) create mode 100644 drivers/serial/mxs-auart.c diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index ec3c214..de37fe5 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1657,4 +1657,19 @@ config SERIAL_PCH_UART This driver is for PCH(Platform controller Hub) UART of Intel EG20T which is an IOH(Input/Output Hub) for x86 embedded processor. Enabling PCH_DMA, this PCH UART works as DMA mode. + +config SERIAL_MXS_AUART + depends on ARCH_MXS + tristate "i.MXS AUART support" + select SERIAL_CORE + help + This driver supports the i.MX AUART port. + +config SERIAL_MXS_AUART_CONSOLE + bool "i.MXS AUART console support" + depends on SERIAL_MXS_AUART=y + select SERIAL_CORE_CONSOLE + help + Enable a i.MXS AUART port to be the system console. + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 8ea92e9..c855071 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -92,3 +92,4 @@ obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o +obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o diff --git a/drivers/serial/mxs-auart.c b/drivers/serial/mxs-auart.c new file mode 100644 index 0000000..dd437ea --- /dev/null +++ b/drivers/serial/mxs-auart.c @@ -0,0 +1,763 @@ +/* + * Application UART driver for hardware found on + * Sigmatel STMP37XX/STMP378X and + * Freescale i.MX23/28 + * + * Author: dmitry pervushin <dimka@embeddedalley.com> + * + * Copyright 2010 Sascha Hauer <s.hauer@pengutronix.de> + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/clk.h> +#include <linux/delay.h> + +#include <asm/cacheflush.h> +#include <mach/mx28.h> + +#define MXS_AUART_PORTS 5 + +#define UARTAPP_CTRL0 0x00000000 +#define UARTAPP_CTRL0_SET 0x00000004 +#define UARTAPP_CTRL0_CLR 0x00000008 +#define UARTAPP_CTRL0_TOG 0x0000000c +#define UARTAPP_CTRL1 0x00000010 +#define UARTAPP_CTRL1_SET 0x00000014 +#define UARTAPP_CTRL1_CLR 0x00000018 +#define UARTAPP_CTRL1_TOG 0x0000001c +#define UARTAPP_CTRL2 0x00000020 +#define UARTAPP_CTRL2_SET 0x00000024 +#define UARTAPP_CTRL2_CLR 0x00000028 +#define UARTAPP_CTRL2_TOG 0x0000002c +#define UARTAPP_LINECTRL 0x00000030 +#define UARTAPP_LINECTRL_SET 0x00000034 +#define UARTAPP_LINECTRL_CLR 0x00000038 +#define UARTAPP_LINECTRL_TOG 0x0000003c +#define UARTAPP_LINECTRL2 0x00000040 +#define UARTAPP_LINECTRL2_SET 0x00000044 +#define UARTAPP_LINECTRL2_CLR 0x00000048 +#define UARTAPP_LINECTRL2_TOG 0x0000004c +#define UARTAPP_INTR 0x00000050 +#define UARTAPP_INTR_SET 0x00000054 +#define UARTAPP_INTR_CLR 0x00000058 +#define UARTAPP_INTR_TOG 0x0000005c +#define UARTAPP_DATA 0x00000060 +#define UARTAPP_STAT 0x00000070 +#define UARTAPP_DEBUG 0x00000080 +#define UARTAPP_VERSION 0x00000090 +#define UARTAPP_AUTOBAUD 0x000000a0 + +#define BM_UARTAPP_CTRL0_SFTRST (1 << 31) +#define BM_UARTAPP_CTRL0_CLKGATE (1 << 30) + + +#define BM_UARTAPP_CTRL2_CTSEN (1 << 15) +#define BM_UARTAPP_CTRL2_RTS (1 << 11) +#define BM_UARTAPP_CTRL2_RXE (1 << 9) +#define BM_UARTAPP_CTRL2_TXE (1 << 8) +#define BM_UARTAPP_CTRL2_UARTEN (1 << 0) + +#define BP_UARTAPP_LINECTRL_BAUD_DIVINT 16 +#define BM_UARTAPP_LINECTRL_BAUD_DIVINT 0xffff0000 +#define BF_UARTAPP_LINECTRL_BAUD_DIVINT(v) (((v) & 0xffff) << 16) +#define BP_UARTAPP_LINECTRL_BAUD_DIVFRAC 8 +#define BM_UARTAPP_LINECTRL_BAUD_DIVFRAC 0x00003f00 +#define BF_UARTAPP_LINECTRL_BAUD_DIVFRAC(v) (((v) & 0x3f) << 8) +#define BP_UARTAPP_LINECTRL_WLEN 5 +#define BM_UARTAPP_LINECTRL_WLEN 0x00000060 +#define BF_UARTAPP_LINECTRL_WLEN(v) (((v) & 0x3) << 5) +#define BM_UARTAPP_LINECTRL_FEN (1 << 4) +#define BM_UARTAPP_LINECTRL_STP2 (1 << 3) +#define BM_UARTAPP_LINECTRL_EPS (1 << 2) +#define BM_UARTAPP_LINECTRL_PEN (1 << 1) +#define BM_UARTAPP_LINECTRL_BRK (1 << 0) + +#define BM_UARTAPP_INTR_RTIEN (1 << 22) +#define BM_UARTAPP_INTR_TXIEN (1 << 21) +#define BM_UARTAPP_INTR_RXIEN (1 << 20) +#define BM_UARTAPP_INTR_CTSMIEN (1 << 17) +#define BM_UARTAPP_INTR_RTIS (1 << 6) +#define BM_UARTAPP_INTR_TXIS (1 << 5) +#define BM_UARTAPP_INTR_RXIS (1 << 4) +#define BM_UARTAPP_INTR_CTSMIS (1 << 1) + +#define BM_UARTAPP_STAT_BUSY (1 << 29) +#define BM_UARTAPP_STAT_CTS (1 << 28) +#define BM_UARTAPP_STAT_TXFE (1 << 27) +#define BM_UARTAPP_STAT_TXFF (1 << 25) +#define BM_UARTAPP_STAT_RXFE (1 << 24) +#define BM_UARTAPP_STAT_OERR (1 << 19) +#define BM_UARTAPP_STAT_BERR (1 << 18) +#define BM_UARTAPP_STAT_PERR (1 << 17) +#define BM_UARTAPP_STAT_FERR (1 << 16) + +#define MXS_AUART_MAJOR 242 +#define MXS_AUART_RX_THRESHOLD 16 + +static struct uart_driver auart_driver; + +struct mxs_auart_port { + struct uart_port port; + + unsigned int flags; + unsigned int ctrl; + + unsigned int irq; + + struct clk *clk; + struct device *dev; +}; + +static void mxs_auart_stop_tx(struct uart_port *u); + +#define to_auart_port(u) container_of(u, struct mxs_auart_port, port) + +static inline void mxs_auart_tx_chars(struct mxs_auart_port *s) +{ + struct circ_buf *xmit = &s->port.state->xmit; + + while (!(readl(s->port.membase + UARTAPP_STAT) & + BM_UARTAPP_STAT_TXFF)) { + if (s->port.x_char) { + writel(s->port.x_char, + s->port.membase + UARTAPP_DATA); + s->port.x_char = 0; + continue; + } + if (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) { + writel(xmit->buf[xmit->tail], + s->port.membase + UARTAPP_DATA); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); + } else + break; + } + if (uart_circ_empty(&(s->port.state->xmit))) + writel(BM_UARTAPP_INTR_TXIEN, + s->port.membase + UARTAPP_INTR_CLR); + else + writel(BM_UARTAPP_INTR_TXIEN, + s->port.membase + UARTAPP_INTR_SET); + + if (uart_tx_stopped(&s->port)) + mxs_auart_stop_tx(&s->port); +} + +static inline unsigned int +mxs_auart_rx_char(struct mxs_auart_port *s, unsigned int stat, u8 c) +{ + int flag; + + flag = TTY_NORMAL; + if (stat & BM_UARTAPP_STAT_BERR) { + stat &= ~BM_UARTAPP_STAT_BERR; + s->port.icount.brk++; + if (uart_handle_break(&s->port)) + return stat; + flag = TTY_BREAK; + } else if (stat & BM_UARTAPP_STAT_PERR) { + stat &= ~BM_UARTAPP_STAT_PERR; + s->port.icount.parity++; + flag = TTY_PARITY; + } else if (stat & BM_UARTAPP_STAT_FERR) { + stat &= ~BM_UARTAPP_STAT_FERR; + s->port.icount.frame++; + flag = TTY_FRAME; + } + + if (stat & BM_UARTAPP_STAT_OERR) + s->port.icount.overrun++; + + if (uart_handle_sysrq_char(&s->port, c)) + return stat; + + uart_insert_char(&s->port, stat, BM_UARTAPP_STAT_OERR, c, flag); + + return stat; +} + +static void mxs_auart_rx_chars(struct mxs_auart_port *s) +{ + u8 c; + struct tty_struct *tty = s->port.state->port.tty; + u32 stat = 0; + + for (;;) { + stat = readl(s->port.membase + UARTAPP_STAT); + if (stat & BM_UARTAPP_STAT_RXFE) + break; + c = readl(s->port.membase + UARTAPP_DATA); + stat = mxs_auart_rx_char(s, stat, c); + writel(stat, s->port.membase + UARTAPP_STAT); + } + + writel(stat, s->port.membase + UARTAPP_STAT); + tty_flip_buffer_push(tty); +} + +static int mxs_auart_request_port(struct uart_port *u) +{ + return 0; +} + +static int mxs_auart_verify_port(struct uart_port *u, + struct serial_struct *ser) +{ + if (u->type != PORT_UNKNOWN && u->type != PORT_IMX) + return -EINVAL; + return 0; +} + +static void mxs_auart_config_port(struct uart_port *u, int flags) +{ +} + +static const char *mxs_auart_type(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + return dev_name(s->dev); +} + +static void mxs_auart_release_port(struct uart_port *u) +{ +} + +static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) +{ + struct mxs_auart_port *s = to_auart_port(u); + + u32 ctrl = readl(u->membase + UARTAPP_CTRL2); + + ctrl &= ~BM_UARTAPP_CTRL2_RTS; + if (mctrl & TIOCM_RTS) + ctrl |= BM_UARTAPP_CTRL2_RTS; + s->ctrl = mctrl; + writel(ctrl, u->membase + UARTAPP_CTRL2); +} + +static u32 mxs_auart_get_mctrl(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + u32 stat = readl(u->membase + UARTAPP_STAT); + int ctrl2 = readl(u->membase + UARTAPP_CTRL2); + u32 mctrl = s->ctrl; + + mctrl &= ~TIOCM_CTS; + if (stat & BM_UARTAPP_STAT_CTS) + mctrl |= TIOCM_CTS; + + if (ctrl2 & BM_UARTAPP_CTRL2_RTS) + mctrl |= TIOCM_RTS; + + return mctrl; +} + +static void mxs_auart_settermios(struct uart_port *u, + struct ktermios *termios, + struct ktermios *old) +{ + u32 bm, ctrl, ctrl2, div; + unsigned int cflag, baud; + + cflag = termios->c_cflag; + + ctrl = BM_UARTAPP_LINECTRL_FEN; + ctrl2 = readl(u->membase + UARTAPP_CTRL2); + + /* byte size */ + switch (cflag & CSIZE) { + case CS5: + bm = 0; + break; + case CS6: + bm = 1; + break; + case CS7: + bm = 2; + break; + case CS8: + bm = 3; + break; + default: + return; + } + + ctrl |= BF_UARTAPP_LINECTRL_WLEN(bm); + + /* parity */ + if (cflag & PARENB) { + ctrl |= BM_UARTAPP_LINECTRL_PEN; + if ((cflag & PARODD) == 0) + ctrl |= BM_UARTAPP_LINECTRL_EPS; + } + + /* figure out the stop bits requested */ + if (cflag & CSTOPB) + ctrl |= BM_UARTAPP_LINECTRL_STP2; + + /* figure out the hardware flow control settings */ + if (cflag & CRTSCTS) + ctrl2 |= BM_UARTAPP_CTRL2_CTSEN; + else + ctrl2 &= ~BM_UARTAPP_CTRL2_CTSEN; + + /* set baud rate */ + baud = uart_get_baud_rate(u, termios, old, 0, u->uartclk); + div = u->uartclk * 32 / baud; + ctrl |= BF_UARTAPP_LINECTRL_BAUD_DIVFRAC(div & 0x3F); + ctrl |= BF_UARTAPP_LINECTRL_BAUD_DIVINT(div >> 6); + + if ((cflag & CREAD) != 0) + ctrl2 |= BM_UARTAPP_CTRL2_RXE; + + writel(ctrl, u->membase + UARTAPP_LINECTRL); + writel(ctrl2, u->membase + UARTAPP_CTRL2); +} + +static irqreturn_t mxs_auart_irq_handle(int irq, void *context) +{ + u32 istatus, istat; + struct mxs_auart_port *s = context; + u32 stat = readl(s->port.membase + UARTAPP_STAT); + + istatus = istat = readl(s->port.membase + UARTAPP_INTR); + + if (istat & BM_UARTAPP_INTR_CTSMIS) { + uart_handle_cts_change(&s->port, stat & BM_UARTAPP_STAT_CTS); + writel(BM_UARTAPP_INTR_CTSMIS, + s->port.membase + UARTAPP_INTR_CLR); + istat &= ~BM_UARTAPP_INTR_CTSMIS; + } + + if (istat & (BM_UARTAPP_INTR_RTIS | BM_UARTAPP_INTR_RXIS)) { + mxs_auart_rx_chars(s); + istat &= ~(BM_UARTAPP_INTR_RTIS | BM_UARTAPP_INTR_RXIS); + } + + if (istat & BM_UARTAPP_INTR_TXIS) { + mxs_auart_tx_chars(s); + istat &= ~BM_UARTAPP_INTR_TXIS; + } + + writel(istatus & (BM_UARTAPP_INTR_RTIS + | BM_UARTAPP_INTR_TXIS + | BM_UARTAPP_INTR_RXIS + | BM_UARTAPP_INTR_CTSMIS), + s->port.membase + UARTAPP_INTR_CLR); + + return IRQ_HANDLED; +} + +static void mxs_auart_reset(struct uart_port *u) +{ + int i; + unsigned int reg; + + writel(BM_UARTAPP_CTRL0_SFTRST, + u->membase + UARTAPP_CTRL0_CLR); + + for (i = 0; i < 10000; i++) { + reg = readl(u->membase + UARTAPP_CTRL0); + if (!(reg & BM_UARTAPP_CTRL0_SFTRST)) + break; + udelay(3); + } + + writel(BM_UARTAPP_CTRL0_CLKGATE, + u->membase + UARTAPP_CTRL0_CLR); +} + +static int mxs_auart_startup(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + clk_enable(s->clk); + + writel(BM_UARTAPP_CTRL2_UARTEN, + s->port.membase + UARTAPP_CTRL2_SET); + + writel(BM_UARTAPP_INTR_RXIEN | BM_UARTAPP_INTR_RTIEN, + s->port.membase + UARTAPP_INTR); + + writel(BM_UARTAPP_INTR_CTSMIEN, + s->port.membase + UARTAPP_INTR_SET); + + /* + * Enable fifo so all four bytes of a DMA word are written to + * output (otherwise, only the LSB is written, ie. 1 in 4 bytes) + */ + writel(BM_UARTAPP_LINECTRL_FEN, + s->port.membase + UARTAPP_LINECTRL_SET); + + return 0; +} + +static void mxs_auart_shutdown(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + writel(BM_UARTAPP_CTRL0_SFTRST, + s->port.membase + UARTAPP_CTRL0_SET); + + writel(BM_UARTAPP_INTR_RXIEN | BM_UARTAPP_INTR_RTIEN | + BM_UARTAPP_INTR_CTSMIEN, + s->port.membase + UARTAPP_INTR_CLR); + + clk_disable(s->clk); +} + +static unsigned int mxs_auart_tx_empty(struct uart_port *u) +{ + if (readl(u->membase + UARTAPP_STAT) & BM_UARTAPP_STAT_TXFE) + return TIOCSER_TEMT; + else + return 0; +} + +static void mxs_auart_start_tx(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + /* enable transmitter */ + writel(BM_UARTAPP_CTRL2_TXE, u->membase + UARTAPP_CTRL2_SET); + + mxs_auart_tx_chars(s); +} + +static void mxs_auart_stop_tx(struct uart_port *u) +{ + writel(BM_UARTAPP_CTRL2_TXE, u->membase + UARTAPP_CTRL2_CLR); +} + +static void mxs_auart_stop_rx(struct uart_port *u) +{ + writel(BM_UARTAPP_CTRL2_RXE, u->membase + UARTAPP_CTRL2_CLR); +} + +static void mxs_auart_break_ctl(struct uart_port *u, int ctl) +{ + if (ctl) + writel(BM_UARTAPP_LINECTRL_BRK, + u->membase + UARTAPP_LINECTRL_SET); + else + writel(BM_UARTAPP_LINECTRL_BRK, + u->membase + UARTAPP_LINECTRL_CLR); +} + +static void mxs_auart_enable_ms(struct uart_port *port) +{ + /* just empty */ +} + +static struct uart_ops mxs_auart_ops = { + .tx_empty = mxs_auart_tx_empty, + .start_tx = mxs_auart_start_tx, + .stop_tx = mxs_auart_stop_tx, + .stop_rx = mxs_auart_stop_rx, + .enable_ms = mxs_auart_enable_ms, + .break_ctl = mxs_auart_break_ctl, + .set_mctrl = mxs_auart_set_mctrl, + .get_mctrl = mxs_auart_get_mctrl, + .startup = mxs_auart_startup, + .shutdown = mxs_auart_shutdown, + .set_termios = mxs_auart_settermios, + .type = mxs_auart_type, + .release_port = mxs_auart_release_port, + .request_port = mxs_auart_request_port, + .config_port = mxs_auart_config_port, + .verify_port = mxs_auart_verify_port, +}; + +static struct mxs_auart_port *auart_port[MXS_AUART_PORTS]; + +#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE +static void mxs_auart_console_putchar(struct uart_port *port, int ch) +{ + unsigned int status; + + do { + status = readl(port->membase + UARTAPP_STAT); + } while (status & BM_UARTAPP_STAT_TXFF); + writel(ch, port->membase + UARTAPP_DATA); +} + +static void +auart_console_write(struct console *co, const char *str, unsigned int count) +{ + struct mxs_auart_port *s; + struct uart_port *port; + unsigned int status, old_cr; + + if (co->index > MXS_AUART_PORTS || co->index < 0) + return; + + s = auart_port[co->index]; + port = &s->port; + + clk_enable(s->clk); + + /* First save the CR then disable the interrupts */ + old_cr = readl(port->membase + UARTAPP_CTRL2); + writel(BM_UARTAPP_CTRL2_UARTEN | BM_UARTAPP_CTRL2_TXE, + port->membase + UARTAPP_CTRL2_SET); + + uart_console_write(port, str, count, mxs_auart_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the TCR + */ + do { + status = readl(port->membase + UARTAPP_STAT); + } while (status & BM_UARTAPP_STAT_BUSY); + + writel(old_cr, port->membase + UARTAPP_CTRL2); + + clk_disable(s->clk); +} + +static void __init +auart_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + if (readl(port->membase + UARTAPP_CTRL2) + & BM_UARTAPP_CTRL2_UARTEN) { + unsigned int lcr_h, quot; + lcr_h = readl(port->membase + UARTAPP_LINECTRL); + + *parity = 'n'; + if (lcr_h & BM_UARTAPP_LINECTRL_PEN) { + if (lcr_h & BM_UARTAPP_LINECTRL_EPS) + *parity = 'e'; + else + *parity = 'o'; + } + + if ((lcr_h & BM_UARTAPP_LINECTRL_WLEN) + == BF_UARTAPP_LINECTRL_WLEN(2)) + *bits = 7; + else + *bits = 8; + + quot = (((readl(port->membase + UARTAPP_LINECTRL) + & BM_UARTAPP_LINECTRL_BAUD_DIVINT)) + >> (BP_UARTAPP_LINECTRL_BAUD_DIVINT - 6)) + | (((readl(port->membase + UARTAPP_LINECTRL) + & BM_UARTAPP_LINECTRL_BAUD_DIVFRAC)) + >> BP_UARTAPP_LINECTRL_BAUD_DIVFRAC); + if (quot == 0) + quot = 1; + *baud = (port->uartclk << 2) / quot; + } +} + +static int __init +auart_console_setup(struct console *co, char *options) +{ + struct mxs_auart_port *s; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index == -1 || co->index >= ARRAY_SIZE(auart_port)) + co->index = 0; + s = auart_port[co->index]; + if (!s) + return -ENODEV; + + clk_enable(s->clk); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + auart_console_get_options(&s->port, &baud, &parity, &bits); + + ret = uart_set_options(&s->port, co, baud, parity, bits, flow); + + clk_disable(s->clk); + + return ret; +} + +static struct console auart_console = { + .name = "ttyAPP", + .write = auart_console_write, + .device = uart_console_device, + .setup = auart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &auart_driver, +}; +#endif + +static struct uart_driver auart_driver = { + .owner = THIS_MODULE, + .driver_name = "ttyAPP", + .dev_name = "ttyAPP", + .major = 0, + .minor = 0, + .nr = MXS_AUART_PORTS, +#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE + .cons = &auart_console, +#endif +}; + +static int __devinit mxs_auart_probe(struct platform_device *pdev) +{ + struct mxs_auart_port *s; + u32 version; + int ret = 0; + struct resource *r; + + s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL); + if (!s) { + ret = -ENOMEM; + goto out; + } + + s->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(s->clk)) { + ret = PTR_ERR(s->clk); + goto out_free; + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + ret = -ENXIO; + goto out_free_clk; + } + + s->port.mapbase = r->start; + s->port.membase = ioremap(r->start, resource_size(r)); + s->port.ops = &mxs_auart_ops; + s->port.iotype = UPIO_MEM; + s->port.line = pdev->id < 0 ? 0 : pdev->id; + s->port.fifosize = 16; + s->port.uartclk = clk_get_rate(s->clk); + s->port.type = PORT_IMX; + s->port.dev = s->dev = get_device(&pdev->dev); + + s->flags = 0; + s->ctrl = 0; + + s->irq = platform_get_irq(pdev, 0); + s->port.irq = s->irq; + ret = request_irq(s->irq, mxs_auart_irq_handle, 0, dev_name(&pdev->dev), s); + if (ret) + goto out_free_clk; + + platform_set_drvdata(pdev, s); + + auart_port[pdev->id] = s; + + mxs_auart_reset(&s->port); + + ret = uart_add_one_port(&auart_driver, &s->port); + if (ret) + goto out_free_irq; + + version = readl(s->port.membase + UARTAPP_VERSION); + dev_info(&pdev->dev, "Found APPUART %d.%d.%d\n", + (version >> 24) & 0xff, + (version >> 16) & 0xff, version & 0xffff); + + return 0; + +out_free_irq: + auart_port[pdev->id] = NULL; + free_irq(s->irq, s); +out_free_clk: + clk_put(s->clk); +out_free: + kfree(s); +out: + return ret; +} + +static int __devexit mxs_auart_remove(struct platform_device *pdev) +{ + struct mxs_auart_port *s = platform_get_drvdata(pdev); + + uart_remove_one_port(&auart_driver, &s->port); + + auart_port[pdev->id] = NULL; + + clk_put(s->clk); + free_irq(s->irq, s); + kfree(s); + + return 0; +} + +static struct platform_driver mxs_auart_driver = { + .probe = mxs_auart_probe, + .remove = __devexit_p(mxs_auart_remove), + .driver = { + .name = "mxs-auart", + .owner = THIS_MODULE, + }, +}; + +static int __init mxs_auart_init(void) +{ + int r; + + r = uart_register_driver(&auart_driver); + if (r) + goto out; + + r = platform_driver_register(&mxs_auart_driver); + if (r) + goto out_err; + + return 0; +out_err: + uart_unregister_driver(&auart_driver); +out: + return r; +} + +static void __exit mxs_auart_exit(void) +{ + platform_driver_unregister(&mxs_auart_driver); + uart_unregister_driver(&auart_driver); +} + +module_init(mxs_auart_init); +module_exit(mxs_auart_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Freescale MXS application uart driver"); +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); -- 1.7.2.3 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 1/2] serial: Add auart driver for i.MX23/28 2011-01-11 15:09 ` [PATCH 1/2] serial: Add auart driver for i.MX23/28 Sascha Hauer @ 2011-01-11 16:05 ` Russell King - ARM Linux 2011-01-11 17:10 ` Wolfram Sang 2011-01-12 5:39 ` Shawn Guo 2 siblings, 0 replies; 14+ messages in thread From: Russell King - ARM Linux @ 2011-01-11 16:05 UTC (permalink / raw) To: linux-arm-kernel On Tue, Jan 11, 2011 at 04:09:04PM +0100, Sascha Hauer wrote: > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> So you don't handle any of the termios settings at all in this driver such as ignoring parity, reading parity, framing errors, etc. There's a reason that you're provided with read_status_mask and ignore_status_mask. If you don't intend to support those termios modes, ensure that you force the termios structure to reflect what you actually do implement so that userspace can detect the capabilities of your driver. IOW, iflags INPCK, BRKINT, PARMRK, IGNPAR, IGNBRK and cflag CREAD. ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 1/2] serial: Add auart driver for i.MX23/28 2011-01-11 15:09 ` [PATCH 1/2] serial: Add auart driver for i.MX23/28 Sascha Hauer 2011-01-11 16:05 ` Russell King - ARM Linux @ 2011-01-11 17:10 ` Wolfram Sang 2011-01-11 17:57 ` Sascha Hauer 2011-01-12 5:39 ` Shawn Guo 2 siblings, 1 reply; 14+ messages in thread From: Wolfram Sang @ 2011-01-11 17:10 UTC (permalink / raw) To: linux-arm-kernel On Tue, Jan 11, 2011 at 04:09:04PM +0100, Sascha Hauer wrote: [...] > +#include <asm/cacheflush.h> > +#include <mach/mx28.h> What about MX23 and STMP3xxx? > + > +#define MXS_AUART_PORTS 5 MX23 has just 2. We should better adapt to that depending on the type? Bye, Wolfram -- Pengutronix e.K. | Wolfram Sang | Industrial Linux Solutions | http://www.pengutronix.de/ | -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 198 bytes Desc: Digital signature URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20110111/e2e6b8c3/attachment.sig> ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 1/2] serial: Add auart driver for i.MX23/28 2011-01-11 17:10 ` Wolfram Sang @ 2011-01-11 17:57 ` Sascha Hauer 0 siblings, 0 replies; 14+ messages in thread From: Sascha Hauer @ 2011-01-11 17:57 UTC (permalink / raw) To: linux-arm-kernel On Tue, Jan 11, 2011 at 06:10:50PM +0100, Wolfram Sang wrote: > On Tue, Jan 11, 2011 at 04:09:04PM +0100, Sascha Hauer wrote: > [...] > > +#include <asm/cacheflush.h> > > +#include <mach/mx28.h> > > What about MX23 and STMP3xxx? This include is not needed, will remove. Instead we need linux/io.h. > > > + > > +#define MXS_AUART_PORTS 5 > > MX23 has just 2. We should better adapt to that depending on the type? This is just the maximum number the driver supports, it's safe to keep it as is. Sascha -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 1/2] serial: Add auart driver for i.MX23/28 2011-01-11 15:09 ` [PATCH 1/2] serial: Add auart driver for i.MX23/28 Sascha Hauer 2011-01-11 16:05 ` Russell King - ARM Linux 2011-01-11 17:10 ` Wolfram Sang @ 2011-01-12 5:39 ` Shawn Guo 2 siblings, 0 replies; 14+ messages in thread From: Shawn Guo @ 2011-01-12 5:39 UTC (permalink / raw) To: linux-arm-kernel On Tue, Jan 11, 2011 at 04:09:04PM +0100, Sascha Hauer wrote: > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> > --- > drivers/serial/Kconfig | 15 + > drivers/serial/Makefile | 1 + > drivers/serial/mxs-auart.c | 763 ++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 779 insertions(+), 0 deletions(-) > create mode 100644 drivers/serial/mxs-auart.c > > diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig > index ec3c214..de37fe5 100644 > --- a/drivers/serial/Kconfig > +++ b/drivers/serial/Kconfig > @@ -1657,4 +1657,19 @@ config SERIAL_PCH_UART > This driver is for PCH(Platform controller Hub) UART of Intel EG20T > which is an IOH(Input/Output Hub) for x86 embedded processor. > Enabling PCH_DMA, this PCH UART works as DMA mode. > + > +config SERIAL_MXS_AUART > + depends on ARCH_MXS > + tristate "i.MXS AUART support" Can we not use i.MXS? We use i.MX23 and i.MX28 for SoC, MXS for the arch, but never i.MXS. > + select SERIAL_CORE > + help > + This driver supports the i.MX AUART port. i.MX is being used to break the naming consistency here. > + > +config SERIAL_MXS_AUART_CONSOLE > + bool "i.MXS AUART console support" > + depends on SERIAL_MXS_AUART=y > + select SERIAL_CORE_CONSOLE > + help > + Enable a i.MXS AUART port to be the system console. > + > endmenu > diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile > index 8ea92e9..c855071 100644 > --- a/drivers/serial/Makefile > +++ b/drivers/serial/Makefile > @@ -92,3 +92,4 @@ obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o > obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o > obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o > obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o > +obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o > diff --git a/drivers/serial/mxs-auart.c b/drivers/serial/mxs-auart.c > new file mode 100644 > index 0000000..dd437ea > --- /dev/null > +++ b/drivers/serial/mxs-auart.c > @@ -0,0 +1,763 @@ > +/* > + * Application UART driver for hardware found on > + * Sigmatel STMP37XX/STMP378X and > + * Freescale i.MX23/28 > + * > + * Author: dmitry pervushin <dimka@embeddedalley.com> > + * > + * Copyright 2010 Sascha Hauer <s.hauer@pengutronix.de> 2011? > + * Copyright 2008-2010 Freescale Semiconductor, Inc. > + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. > + * > + * The code contained herein is licensed under the GNU General Public > + * License. You may obtain a copy of the GNU General Public License > + * Version 2 or later at the following locations: > + * > + * http://www.opensource.org/licenses/gpl-license.html > + * http://www.gnu.org/copyleft/gpl.html > + */ > + > +#include <linux/kernel.h> > +#include <linux/device.h> > +#include <linux/errno.h> > +#include <linux/init.h> > +#include <linux/console.h> > +#include <linux/interrupt.h> > +#include <linux/module.h> > +#include <linux/slab.h> > +#include <linux/wait.h> > +#include <linux/tty.h> > +#include <linux/tty_driver.h> > +#include <linux/tty_flip.h> > +#include <linux/serial.h> > +#include <linux/serial_core.h> > +#include <linux/platform_device.h> > +#include <linux/device.h> > +#include <linux/clk.h> > +#include <linux/delay.h> > + > +#include <asm/cacheflush.h> > +#include <mach/mx28.h> > + > +#define MXS_AUART_PORTS 5 > + > +#define UARTAPP_CTRL0 0x00000000 > +#define UARTAPP_CTRL0_SET 0x00000004 > +#define UARTAPP_CTRL0_CLR 0x00000008 > +#define UARTAPP_CTRL0_TOG 0x0000000c > +#define UARTAPP_CTRL1 0x00000010 > +#define UARTAPP_CTRL1_SET 0x00000014 > +#define UARTAPP_CTRL1_CLR 0x00000018 > +#define UARTAPP_CTRL1_TOG 0x0000001c > +#define UARTAPP_CTRL2 0x00000020 > +#define UARTAPP_CTRL2_SET 0x00000024 > +#define UARTAPP_CTRL2_CLR 0x00000028 > +#define UARTAPP_CTRL2_TOG 0x0000002c > +#define UARTAPP_LINECTRL 0x00000030 > +#define UARTAPP_LINECTRL_SET 0x00000034 > +#define UARTAPP_LINECTRL_CLR 0x00000038 > +#define UARTAPP_LINECTRL_TOG 0x0000003c > +#define UARTAPP_LINECTRL2 0x00000040 > +#define UARTAPP_LINECTRL2_SET 0x00000044 > +#define UARTAPP_LINECTRL2_CLR 0x00000048 > +#define UARTAPP_LINECTRL2_TOG 0x0000004c > +#define UARTAPP_INTR 0x00000050 > +#define UARTAPP_INTR_SET 0x00000054 > +#define UARTAPP_INTR_CLR 0x00000058 > +#define UARTAPP_INTR_TOG 0x0000005c > +#define UARTAPP_DATA 0x00000060 > +#define UARTAPP_STAT 0x00000070 > +#define UARTAPP_DEBUG 0x00000080 > +#define UARTAPP_VERSION 0x00000090 > +#define UARTAPP_AUTOBAUD 0x000000a0 > + > +#define BM_UARTAPP_CTRL0_SFTRST (1 << 31) > +#define BM_UARTAPP_CTRL0_CLKGATE (1 << 30) > + > + > +#define BM_UARTAPP_CTRL2_CTSEN (1 << 15) > +#define BM_UARTAPP_CTRL2_RTS (1 << 11) > +#define BM_UARTAPP_CTRL2_RXE (1 << 9) > +#define BM_UARTAPP_CTRL2_TXE (1 << 8) > +#define BM_UARTAPP_CTRL2_UARTEN (1 << 0) > + > +#define BP_UARTAPP_LINECTRL_BAUD_DIVINT 16 > +#define BM_UARTAPP_LINECTRL_BAUD_DIVINT 0xffff0000 > +#define BF_UARTAPP_LINECTRL_BAUD_DIVINT(v) (((v) & 0xffff) << 16) > +#define BP_UARTAPP_LINECTRL_BAUD_DIVFRAC 8 > +#define BM_UARTAPP_LINECTRL_BAUD_DIVFRAC 0x00003f00 > +#define BF_UARTAPP_LINECTRL_BAUD_DIVFRAC(v) (((v) & 0x3f) << 8) > +#define BP_UARTAPP_LINECTRL_WLEN 5 > +#define BM_UARTAPP_LINECTRL_WLEN 0x00000060 > +#define BF_UARTAPP_LINECTRL_WLEN(v) (((v) & 0x3) << 5) > +#define BM_UARTAPP_LINECTRL_FEN (1 << 4) > +#define BM_UARTAPP_LINECTRL_STP2 (1 << 3) > +#define BM_UARTAPP_LINECTRL_EPS (1 << 2) > +#define BM_UARTAPP_LINECTRL_PEN (1 << 1) > +#define BM_UARTAPP_LINECTRL_BRK (1 << 0) > + > +#define BM_UARTAPP_INTR_RTIEN (1 << 22) > +#define BM_UARTAPP_INTR_TXIEN (1 << 21) > +#define BM_UARTAPP_INTR_RXIEN (1 << 20) > +#define BM_UARTAPP_INTR_CTSMIEN (1 << 17) > +#define BM_UARTAPP_INTR_RTIS (1 << 6) > +#define BM_UARTAPP_INTR_TXIS (1 << 5) > +#define BM_UARTAPP_INTR_RXIS (1 << 4) > +#define BM_UARTAPP_INTR_CTSMIS (1 << 1) > + > +#define BM_UARTAPP_STAT_BUSY (1 << 29) > +#define BM_UARTAPP_STAT_CTS (1 << 28) > +#define BM_UARTAPP_STAT_TXFE (1 << 27) > +#define BM_UARTAPP_STAT_TXFF (1 << 25) > +#define BM_UARTAPP_STAT_RXFE (1 << 24) > +#define BM_UARTAPP_STAT_OERR (1 << 19) > +#define BM_UARTAPP_STAT_BERR (1 << 18) > +#define BM_UARTAPP_STAT_PERR (1 << 17) > +#define BM_UARTAPP_STAT_FERR (1 << 16) > + > +#define MXS_AUART_MAJOR 242 > +#define MXS_AUART_RX_THRESHOLD 16 > + > +static struct uart_driver auart_driver; > + > +struct mxs_auart_port { > + struct uart_port port; > + > + unsigned int flags; > + unsigned int ctrl; > + > + unsigned int irq; > + > + struct clk *clk; > + struct device *dev; > +}; > + > +static void mxs_auart_stop_tx(struct uart_port *u); > + > +#define to_auart_port(u) container_of(u, struct mxs_auart_port, port) > + > +static inline void mxs_auart_tx_chars(struct mxs_auart_port *s) > +{ > + struct circ_buf *xmit = &s->port.state->xmit; > + > + while (!(readl(s->port.membase + UARTAPP_STAT) & > + BM_UARTAPP_STAT_TXFF)) { > + if (s->port.x_char) { > + writel(s->port.x_char, > + s->port.membase + UARTAPP_DATA); > + s->port.x_char = 0; > + continue; > + } > + if (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) { > + writel(xmit->buf[xmit->tail], > + s->port.membase + UARTAPP_DATA); > + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); > + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) > + uart_write_wakeup(&s->port); > + } else > + break; > + } > + if (uart_circ_empty(&(s->port.state->xmit))) > + writel(BM_UARTAPP_INTR_TXIEN, > + s->port.membase + UARTAPP_INTR_CLR); > + else > + writel(BM_UARTAPP_INTR_TXIEN, > + s->port.membase + UARTAPP_INTR_SET); > + > + if (uart_tx_stopped(&s->port)) > + mxs_auart_stop_tx(&s->port); > +} > + > +static inline unsigned int > +mxs_auart_rx_char(struct mxs_auart_port *s, unsigned int stat, u8 c) > +{ > + int flag; > + > + flag = TTY_NORMAL; > + if (stat & BM_UARTAPP_STAT_BERR) { > + stat &= ~BM_UARTAPP_STAT_BERR; > + s->port.icount.brk++; > + if (uart_handle_break(&s->port)) > + return stat; > + flag = TTY_BREAK; > + } else if (stat & BM_UARTAPP_STAT_PERR) { > + stat &= ~BM_UARTAPP_STAT_PERR; > + s->port.icount.parity++; > + flag = TTY_PARITY; > + } else if (stat & BM_UARTAPP_STAT_FERR) { > + stat &= ~BM_UARTAPP_STAT_FERR; > + s->port.icount.frame++; > + flag = TTY_FRAME; > + } > + > + if (stat & BM_UARTAPP_STAT_OERR) > + s->port.icount.overrun++; > + > + if (uart_handle_sysrq_char(&s->port, c)) > + return stat; > + > + uart_insert_char(&s->port, stat, BM_UARTAPP_STAT_OERR, c, flag); > + > + return stat; > +} > + > +static void mxs_auart_rx_chars(struct mxs_auart_port *s) > +{ > + u8 c; > + struct tty_struct *tty = s->port.state->port.tty; > + u32 stat = 0; > + > + for (;;) { > + stat = readl(s->port.membase + UARTAPP_STAT); > + if (stat & BM_UARTAPP_STAT_RXFE) > + break; > + c = readl(s->port.membase + UARTAPP_DATA); > + stat = mxs_auart_rx_char(s, stat, c); > + writel(stat, s->port.membase + UARTAPP_STAT); > + } > + > + writel(stat, s->port.membase + UARTAPP_STAT); > + tty_flip_buffer_push(tty); > +} > + > +static int mxs_auart_request_port(struct uart_port *u) > +{ > + return 0; > +} > + > +static int mxs_auart_verify_port(struct uart_port *u, > + struct serial_struct *ser) > +{ > + if (u->type != PORT_UNKNOWN && u->type != PORT_IMX) > + return -EINVAL; > + return 0; > +} > + > +static void mxs_auart_config_port(struct uart_port *u, int flags) > +{ > +} > + > +static const char *mxs_auart_type(struct uart_port *u) > +{ > + struct mxs_auart_port *s = to_auart_port(u); > + > + return dev_name(s->dev); > +} > + > +static void mxs_auart_release_port(struct uart_port *u) > +{ > +} > + > +static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) > +{ > + struct mxs_auart_port *s = to_auart_port(u); > + > + u32 ctrl = readl(u->membase + UARTAPP_CTRL2); > + > + ctrl &= ~BM_UARTAPP_CTRL2_RTS; > + if (mctrl & TIOCM_RTS) > + ctrl |= BM_UARTAPP_CTRL2_RTS; > + s->ctrl = mctrl; > + writel(ctrl, u->membase + UARTAPP_CTRL2); > +} > + > +static u32 mxs_auart_get_mctrl(struct uart_port *u) > +{ > + struct mxs_auart_port *s = to_auart_port(u); > + u32 stat = readl(u->membase + UARTAPP_STAT); > + int ctrl2 = readl(u->membase + UARTAPP_CTRL2); > + u32 mctrl = s->ctrl; > + > + mctrl &= ~TIOCM_CTS; > + if (stat & BM_UARTAPP_STAT_CTS) > + mctrl |= TIOCM_CTS; > + > + if (ctrl2 & BM_UARTAPP_CTRL2_RTS) > + mctrl |= TIOCM_RTS; > + > + return mctrl; > +} > + > +static void mxs_auart_settermios(struct uart_port *u, > + struct ktermios *termios, > + struct ktermios *old) > +{ > + u32 bm, ctrl, ctrl2, div; > + unsigned int cflag, baud; > + > + cflag = termios->c_cflag; > + > + ctrl = BM_UARTAPP_LINECTRL_FEN; > + ctrl2 = readl(u->membase + UARTAPP_CTRL2); > + > + /* byte size */ > + switch (cflag & CSIZE) { > + case CS5: > + bm = 0; > + break; > + case CS6: > + bm = 1; > + break; > + case CS7: > + bm = 2; > + break; > + case CS8: > + bm = 3; > + break; > + default: > + return; > + } > + > + ctrl |= BF_UARTAPP_LINECTRL_WLEN(bm); > + > + /* parity */ > + if (cflag & PARENB) { > + ctrl |= BM_UARTAPP_LINECTRL_PEN; > + if ((cflag & PARODD) == 0) > + ctrl |= BM_UARTAPP_LINECTRL_EPS; > + } > + > + /* figure out the stop bits requested */ > + if (cflag & CSTOPB) > + ctrl |= BM_UARTAPP_LINECTRL_STP2; > + > + /* figure out the hardware flow control settings */ > + if (cflag & CRTSCTS) > + ctrl2 |= BM_UARTAPP_CTRL2_CTSEN; > + else > + ctrl2 &= ~BM_UARTAPP_CTRL2_CTSEN; > + > + /* set baud rate */ > + baud = uart_get_baud_rate(u, termios, old, 0, u->uartclk); > + div = u->uartclk * 32 / baud; > + ctrl |= BF_UARTAPP_LINECTRL_BAUD_DIVFRAC(div & 0x3F); > + ctrl |= BF_UARTAPP_LINECTRL_BAUD_DIVINT(div >> 6); > + > + if ((cflag & CREAD) != 0) > + ctrl2 |= BM_UARTAPP_CTRL2_RXE; > + > + writel(ctrl, u->membase + UARTAPP_LINECTRL); > + writel(ctrl2, u->membase + UARTAPP_CTRL2); > +} > + > +static irqreturn_t mxs_auart_irq_handle(int irq, void *context) > +{ > + u32 istatus, istat; > + struct mxs_auart_port *s = context; > + u32 stat = readl(s->port.membase + UARTAPP_STAT); > + > + istatus = istat = readl(s->port.membase + UARTAPP_INTR); > + > + if (istat & BM_UARTAPP_INTR_CTSMIS) { > + uart_handle_cts_change(&s->port, stat & BM_UARTAPP_STAT_CTS); > + writel(BM_UARTAPP_INTR_CTSMIS, > + s->port.membase + UARTAPP_INTR_CLR); > + istat &= ~BM_UARTAPP_INTR_CTSMIS; > + } > + > + if (istat & (BM_UARTAPP_INTR_RTIS | BM_UARTAPP_INTR_RXIS)) { > + mxs_auart_rx_chars(s); > + istat &= ~(BM_UARTAPP_INTR_RTIS | BM_UARTAPP_INTR_RXIS); > + } > + > + if (istat & BM_UARTAPP_INTR_TXIS) { > + mxs_auart_tx_chars(s); > + istat &= ~BM_UARTAPP_INTR_TXIS; > + } > + > + writel(istatus & (BM_UARTAPP_INTR_RTIS > + | BM_UARTAPP_INTR_TXIS > + | BM_UARTAPP_INTR_RXIS > + | BM_UARTAPP_INTR_CTSMIS), > + s->port.membase + UARTAPP_INTR_CLR); > + > + return IRQ_HANDLED; > +} > + > +static void mxs_auart_reset(struct uart_port *u) > +{ > + int i; > + unsigned int reg; > + > + writel(BM_UARTAPP_CTRL0_SFTRST, > + u->membase + UARTAPP_CTRL0_CLR); > + > + for (i = 0; i < 10000; i++) { > + reg = readl(u->membase + UARTAPP_CTRL0); > + if (!(reg & BM_UARTAPP_CTRL0_SFTRST)) > + break; > + udelay(3); > + } > + > + writel(BM_UARTAPP_CTRL0_CLKGATE, > + u->membase + UARTAPP_CTRL0_CLR); > +} > + > +static int mxs_auart_startup(struct uart_port *u) > +{ > + struct mxs_auart_port *s = to_auart_port(u); > + > + clk_enable(s->clk); > + > + writel(BM_UARTAPP_CTRL2_UARTEN, > + s->port.membase + UARTAPP_CTRL2_SET); > + > + writel(BM_UARTAPP_INTR_RXIEN | BM_UARTAPP_INTR_RTIEN, > + s->port.membase + UARTAPP_INTR); > + > + writel(BM_UARTAPP_INTR_CTSMIEN, > + s->port.membase + UARTAPP_INTR_SET); > + > + /* > + * Enable fifo so all four bytes of a DMA word are written to > + * output (otherwise, only the LSB is written, ie. 1 in 4 bytes) > + */ > + writel(BM_UARTAPP_LINECTRL_FEN, > + s->port.membase + UARTAPP_LINECTRL_SET); > + > + return 0; > +} > + > +static void mxs_auart_shutdown(struct uart_port *u) > +{ > + struct mxs_auart_port *s = to_auart_port(u); > + > + writel(BM_UARTAPP_CTRL0_SFTRST, > + s->port.membase + UARTAPP_CTRL0_SET); > + > + writel(BM_UARTAPP_INTR_RXIEN | BM_UARTAPP_INTR_RTIEN | > + BM_UARTAPP_INTR_CTSMIEN, > + s->port.membase + UARTAPP_INTR_CLR); > + > + clk_disable(s->clk); > +} > + > +static unsigned int mxs_auart_tx_empty(struct uart_port *u) > +{ > + if (readl(u->membase + UARTAPP_STAT) & BM_UARTAPP_STAT_TXFE) > + return TIOCSER_TEMT; > + else > + return 0; > +} > + > +static void mxs_auart_start_tx(struct uart_port *u) > +{ > + struct mxs_auart_port *s = to_auart_port(u); > + > + /* enable transmitter */ > + writel(BM_UARTAPP_CTRL2_TXE, u->membase + UARTAPP_CTRL2_SET); > + > + mxs_auart_tx_chars(s); > +} > + > +static void mxs_auart_stop_tx(struct uart_port *u) > +{ > + writel(BM_UARTAPP_CTRL2_TXE, u->membase + UARTAPP_CTRL2_CLR); > +} > + > +static void mxs_auart_stop_rx(struct uart_port *u) > +{ > + writel(BM_UARTAPP_CTRL2_RXE, u->membase + UARTAPP_CTRL2_CLR); > +} > + > +static void mxs_auart_break_ctl(struct uart_port *u, int ctl) > +{ > + if (ctl) > + writel(BM_UARTAPP_LINECTRL_BRK, > + u->membase + UARTAPP_LINECTRL_SET); > + else > + writel(BM_UARTAPP_LINECTRL_BRK, > + u->membase + UARTAPP_LINECTRL_CLR); > +} > + > +static void mxs_auart_enable_ms(struct uart_port *port) > +{ > + /* just empty */ > +} > + > +static struct uart_ops mxs_auart_ops = { > + .tx_empty = mxs_auart_tx_empty, > + .start_tx = mxs_auart_start_tx, > + .stop_tx = mxs_auart_stop_tx, > + .stop_rx = mxs_auart_stop_rx, > + .enable_ms = mxs_auart_enable_ms, > + .break_ctl = mxs_auart_break_ctl, > + .set_mctrl = mxs_auart_set_mctrl, > + .get_mctrl = mxs_auart_get_mctrl, > + .startup = mxs_auart_startup, > + .shutdown = mxs_auart_shutdown, > + .set_termios = mxs_auart_settermios, > + .type = mxs_auart_type, > + .release_port = mxs_auart_release_port, > + .request_port = mxs_auart_request_port, > + .config_port = mxs_auart_config_port, > + .verify_port = mxs_auart_verify_port, > +}; > + > +static struct mxs_auart_port *auart_port[MXS_AUART_PORTS]; > + > +#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE > +static void mxs_auart_console_putchar(struct uart_port *port, int ch) > +{ > + unsigned int status; > + > + do { > + status = readl(port->membase + UARTAPP_STAT); > + } while (status & BM_UARTAPP_STAT_TXFF); > + writel(ch, port->membase + UARTAPP_DATA); > +} > + > +static void > +auart_console_write(struct console *co, const char *str, unsigned int count) > +{ > + struct mxs_auart_port *s; > + struct uart_port *port; > + unsigned int status, old_cr; > + > + if (co->index > MXS_AUART_PORTS || co->index < 0) > + return; > + > + s = auart_port[co->index]; > + port = &s->port; > + > + clk_enable(s->clk); > + > + /* First save the CR then disable the interrupts */ > + old_cr = readl(port->membase + UARTAPP_CTRL2); > + writel(BM_UARTAPP_CTRL2_UARTEN | BM_UARTAPP_CTRL2_TXE, > + port->membase + UARTAPP_CTRL2_SET); > + > + uart_console_write(port, str, count, mxs_auart_console_putchar); > + > + /* > + * Finally, wait for transmitter to become empty > + * and restore the TCR > + */ > + do { > + status = readl(port->membase + UARTAPP_STAT); > + } while (status & BM_UARTAPP_STAT_BUSY); > + > + writel(old_cr, port->membase + UARTAPP_CTRL2); > + > + clk_disable(s->clk); > +} > + > +static void __init > +auart_console_get_options(struct uart_port *port, int *baud, > + int *parity, int *bits) > +{ > + if (readl(port->membase + UARTAPP_CTRL2) > + & BM_UARTAPP_CTRL2_UARTEN) { > + unsigned int lcr_h, quot; > + lcr_h = readl(port->membase + UARTAPP_LINECTRL); > + > + *parity = 'n'; > + if (lcr_h & BM_UARTAPP_LINECTRL_PEN) { > + if (lcr_h & BM_UARTAPP_LINECTRL_EPS) > + *parity = 'e'; > + else > + *parity = 'o'; > + } > + > + if ((lcr_h & BM_UARTAPP_LINECTRL_WLEN) > + == BF_UARTAPP_LINECTRL_WLEN(2)) > + *bits = 7; > + else > + *bits = 8; > + > + quot = (((readl(port->membase + UARTAPP_LINECTRL) > + & BM_UARTAPP_LINECTRL_BAUD_DIVINT)) > + >> (BP_UARTAPP_LINECTRL_BAUD_DIVINT - 6)) > + | (((readl(port->membase + UARTAPP_LINECTRL) > + & BM_UARTAPP_LINECTRL_BAUD_DIVFRAC)) > + >> BP_UARTAPP_LINECTRL_BAUD_DIVFRAC); > + if (quot == 0) > + quot = 1; > + *baud = (port->uartclk << 2) / quot; > + } > +} > + > +static int __init > +auart_console_setup(struct console *co, char *options) > +{ > + struct mxs_auart_port *s; > + int baud = 9600; > + int bits = 8; > + int parity = 'n'; > + int flow = 'n'; > + int ret; > + > + /* > + * Check whether an invalid uart number has been specified, and > + * if so, search for the first available port that does have > + * console support. > + */ > + if (co->index == -1 || co->index >= ARRAY_SIZE(auart_port)) > + co->index = 0; > + s = auart_port[co->index]; > + if (!s) > + return -ENODEV; > + > + clk_enable(s->clk); > + > + if (options) > + uart_parse_options(options, &baud, &parity, &bits, &flow); > + else > + auart_console_get_options(&s->port, &baud, &parity, &bits); > + > + ret = uart_set_options(&s->port, co, baud, parity, bits, flow); > + > + clk_disable(s->clk); > + > + return ret; > +} > + > +static struct console auart_console = { > + .name = "ttyAPP", > + .write = auart_console_write, > + .device = uart_console_device, > + .setup = auart_console_setup, > + .flags = CON_PRINTBUFFER, > + .index = -1, > + .data = &auart_driver, > +}; > +#endif > + > +static struct uart_driver auart_driver = { > + .owner = THIS_MODULE, > + .driver_name = "ttyAPP", > + .dev_name = "ttyAPP", > + .major = 0, > + .minor = 0, > + .nr = MXS_AUART_PORTS, > +#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE > + .cons = &auart_console, > +#endif > +}; > + > +static int __devinit mxs_auart_probe(struct platform_device *pdev) > +{ > + struct mxs_auart_port *s; > + u32 version; > + int ret = 0; > + struct resource *r; > + > + s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL); > + if (!s) { > + ret = -ENOMEM; > + goto out; > + } > + > + s->clk = clk_get(&pdev->dev, NULL); > + if (IS_ERR(s->clk)) { > + ret = PTR_ERR(s->clk); > + goto out_free; > + } > + > + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!r) { > + ret = -ENXIO; > + goto out_free_clk; > + } > + > + s->port.mapbase = r->start; > + s->port.membase = ioremap(r->start, resource_size(r)); > + s->port.ops = &mxs_auart_ops; > + s->port.iotype = UPIO_MEM; > + s->port.line = pdev->id < 0 ? 0 : pdev->id; > + s->port.fifosize = 16; > + s->port.uartclk = clk_get_rate(s->clk); > + s->port.type = PORT_IMX; > + s->port.dev = s->dev = get_device(&pdev->dev); > + > + s->flags = 0; > + s->ctrl = 0; > + > + s->irq = platform_get_irq(pdev, 0); > + s->port.irq = s->irq; > + ret = request_irq(s->irq, mxs_auart_irq_handle, 0, dev_name(&pdev->dev), s); > + if (ret) > + goto out_free_clk; > + > + platform_set_drvdata(pdev, s); > + > + auart_port[pdev->id] = s; > + > + mxs_auart_reset(&s->port); > + > + ret = uart_add_one_port(&auart_driver, &s->port); > + if (ret) > + goto out_free_irq; > + > + version = readl(s->port.membase + UARTAPP_VERSION); > + dev_info(&pdev->dev, "Found APPUART %d.%d.%d\n", > + (version >> 24) & 0xff, > + (version >> 16) & 0xff, version & 0xffff); > + > + return 0; > + > +out_free_irq: > + auart_port[pdev->id] = NULL; > + free_irq(s->irq, s); > +out_free_clk: > + clk_put(s->clk); > +out_free: > + kfree(s); > +out: > + return ret; > +} > + > +static int __devexit mxs_auart_remove(struct platform_device *pdev) > +{ > + struct mxs_auart_port *s = platform_get_drvdata(pdev); > + > + uart_remove_one_port(&auart_driver, &s->port); > + > + auart_port[pdev->id] = NULL; > + > + clk_put(s->clk); > + free_irq(s->irq, s); > + kfree(s); > + > + return 0; > +} > + > +static struct platform_driver mxs_auart_driver = { > + .probe = mxs_auart_probe, > + .remove = __devexit_p(mxs_auart_remove), > + .driver = { > + .name = "mxs-auart", > + .owner = THIS_MODULE, > + }, > +}; > + > +static int __init mxs_auart_init(void) > +{ > + int r; > + > + r = uart_register_driver(&auart_driver); > + if (r) > + goto out; > + > + r = platform_driver_register(&mxs_auart_driver); > + if (r) > + goto out_err; > + > + return 0; > +out_err: > + uart_unregister_driver(&auart_driver); > +out: > + return r; > +} > + > +static void __exit mxs_auart_exit(void) > +{ > + platform_driver_unregister(&mxs_auart_driver); > + uart_unregister_driver(&auart_driver); > +} > + > +module_init(mxs_auart_init); > +module_exit(mxs_auart_exit); > +MODULE_LICENSE("GPL"); > +MODULE_DESCRIPTION("Freescale MXS application uart driver"); > +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); > -- > 1.7.2.3 > > -- Regards, Shawn ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 2/2] ARM i.MXS: Add auart platform support for i.MX28 2011-01-11 15:09 Add i.MX23/28 auart support Sascha Hauer 2011-01-11 15:09 ` [PATCH 1/2] serial: Add auart driver for i.MX23/28 Sascha Hauer @ 2011-01-11 15:09 ` Sascha Hauer 2011-01-11 15:17 ` Uwe Kleine-König 2011-01-12 6:17 ` Shawn Guo 2011-01-12 5:31 ` Add i.MX23/28 auart support Shawn Guo 2 siblings, 2 replies; 14+ messages in thread From: Sascha Hauer @ 2011-01-11 15:09 UTC (permalink / raw) To: linux-arm-kernel Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- arch/arm/mach-mxs/clock-mx28.c | 5 ++ arch/arm/mach-mxs/devices-mx28.h | 8 +++ arch/arm/mach-mxs/devices/Kconfig | 3 + arch/arm/mach-mxs/devices/Makefile | 1 + arch/arm/mach-mxs/devices/platform-auart.c | 54 +++++++++++++++++++++++ arch/arm/mach-mxs/include/mach/devices-common.h | 10 ++++ 6 files changed, 81 insertions(+), 0 deletions(-) create mode 100644 arch/arm/mach-mxs/devices/platform-auart.c diff --git a/arch/arm/mach-mxs/clock-mx28.c b/arch/arm/mach-mxs/clock-mx28.c index 74e2103..9f65934 100644 --- a/arch/arm/mach-mxs/clock-mx28.c +++ b/arch/arm/mach-mxs/clock-mx28.c @@ -603,6 +603,11 @@ _DEFINE_CLOCK(fec_clk, ENET, DISABLE, &hbus_clk); static struct clk_lookup lookups[] = { _REGISTER_CLOCK("mxs-duart.0", NULL, uart_clk) + _REGISTER_CLOCK("mxs-auart.0", NULL, uart_clk) + _REGISTER_CLOCK("mxs-auart.1", NULL, uart_clk) + _REGISTER_CLOCK("mxs-auart.2", NULL, uart_clk) + _REGISTER_CLOCK("mxs-auart.3", NULL, uart_clk) + _REGISTER_CLOCK("mxs-auart.4", NULL, uart_clk) _REGISTER_CLOCK("fec.0", NULL, fec_clk) _REGISTER_CLOCK("rtc", NULL, rtc_clk) _REGISTER_CLOCK("pll2", NULL, pll2_clk) diff --git a/arch/arm/mach-mxs/devices-mx28.h b/arch/arm/mach-mxs/devices-mx28.h index 00b736c..a0b70df 100644 --- a/arch/arm/mach-mxs/devices-mx28.h +++ b/arch/arm/mach-mxs/devices-mx28.h @@ -15,6 +15,14 @@ extern const struct mxs_duart_data mx28_duart_data __initconst; #define mx28_add_duart() \ mxs_add_duart(&mx28_duart_data) +extern const struct mxs_auart_data mx28_auart_data[] __initconst; +#define mx28_add_auart(id) mxs_add_auart(&mx28_auart_data[id]) +#define mx28_add_auart0() mx28_add_auart(0) +#define mx28_add_auart1() mx28_add_auart(1) +#define mx28_add_auart2() mx28_add_auart(2) +#define mx28_add_auart3() mx28_add_auart(3) +#define mx28_add_auart4() mx28_add_auart(4) + extern const struct mxs_fec_data mx28_fec_data[] __initconst; #define mx28_add_fec(id, pdata) \ mxs_add_fec(&mx28_fec_data[id], pdata) diff --git a/arch/arm/mach-mxs/devices/Kconfig b/arch/arm/mach-mxs/devices/Kconfig index a35a2dc..9d16540 100644 --- a/arch/arm/mach-mxs/devices/Kconfig +++ b/arch/arm/mach-mxs/devices/Kconfig @@ -1,5 +1,8 @@ config MXS_HAVE_PLATFORM_DUART bool +config MXS_HAVE_PLATFORM_AUART + bool + config MXS_HAVE_PLATFORM_FEC bool diff --git a/arch/arm/mach-mxs/devices/Makefile b/arch/arm/mach-mxs/devices/Makefile index 4b5266a..b46d18d 100644 --- a/arch/arm/mach-mxs/devices/Makefile +++ b/arch/arm/mach-mxs/devices/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_MXS_HAVE_PLATFORM_DUART) += platform-duart.o +obj-$(CONFIG_MXS_HAVE_PLATFORM_AUART) += platform-auart.o obj-$(CONFIG_MXS_HAVE_PLATFORM_FEC) += platform-fec.o diff --git a/arch/arm/mach-mxs/devices/platform-auart.c b/arch/arm/mach-mxs/devices/platform-auart.c new file mode 100644 index 0000000..d5e6cb2 --- /dev/null +++ b/arch/arm/mach-mxs/devices/platform-auart.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 Pengutronix + * Sascha Hauer <s.hauer@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + */ +#include <asm/sizes.h> +#include <mach/mx28.h> +#include <mach/devices-common.h> + +#define mxs_auart_data_entry_single(soc, _id) \ + { \ + .id = _id, \ + .iobase = soc ## _AUART ## _id ## _BASE_ADDR, \ + .irq = soc ## _INT_AUART ## _id, \ + } + +#define mxs_auart_data_entry(soc, _id) \ + [_id] = mxs_auart_data_entry_single(soc, _id) + +#ifdef CONFIG_SOC_IMX28 +const struct mxs_auart_data mx28_auart_data[] __initconst = { +#define mx28_auart_data_entry(_id) \ + mxs_auart_data_entry(MX28, _id) + mx28_auart_data_entry(0), + mx28_auart_data_entry(1), + mx28_auart_data_entry(2), + mx28_auart_data_entry(3), + mx28_auart_data_entry(4), +}; +#endif + +struct platform_device *__init mxs_add_auart( + const struct mxs_auart_data *data) +{ + struct resource res[] = { + { + .start = data->iobase, + .end = data->iobase + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, { + .start = data->irq, + .end = data->irq, + .flags = IORESOURCE_IRQ, + }, + }; + + return mxs_add_platform_device_dmamask("mxs-auart", data->id, + res, ARRAY_SIZE(res), NULL, 0, + DMA_BIT_MASK(32)); +} + diff --git a/arch/arm/mach-mxs/include/mach/devices-common.h b/arch/arm/mach-mxs/include/mach/devices-common.h index 3da48d4..b8ab332 100644 --- a/arch/arm/mach-mxs/include/mach/devices-common.h +++ b/arch/arm/mach-mxs/include/mach/devices-common.h @@ -33,6 +33,16 @@ struct mxs_duart_data { struct platform_device *__init mxs_add_duart( const struct mxs_duart_data *data); +/* auart */ +struct mxs_auart_data { + int id; + resource_size_t iobase; + resource_size_t iosize; + resource_size_t irq; +}; +struct platform_device *__init mxs_add_auart( + const struct mxs_auart_data *data); + /* fec */ #include <linux/fec.h> struct mxs_fec_data { -- 1.7.2.3 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 2/2] ARM i.MXS: Add auart platform support for i.MX28 2011-01-11 15:09 ` [PATCH 2/2] ARM i.MXS: Add auart platform support for i.MX28 Sascha Hauer @ 2011-01-11 15:17 ` Uwe Kleine-König 2011-01-11 15:26 ` Sascha Hauer 2011-01-12 5:06 ` Shawn Guo 2011-01-12 6:17 ` Shawn Guo 1 sibling, 2 replies; 14+ messages in thread From: Uwe Kleine-König @ 2011-01-11 15:17 UTC (permalink / raw) To: linux-arm-kernel Hello Sascha, On Tue, Jan 11, 2011 at 04:09:05PM +0100, Sascha Hauer wrote: > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> > --- > arch/arm/mach-mxs/clock-mx28.c | 5 ++ > arch/arm/mach-mxs/devices-mx28.h | 8 +++ > arch/arm/mach-mxs/devices/Kconfig | 3 + > arch/arm/mach-mxs/devices/Makefile | 1 + > arch/arm/mach-mxs/devices/platform-auart.c | 54 +++++++++++++++++++++++ > arch/arm/mach-mxs/include/mach/devices-common.h | 10 ++++ > 6 files changed, 81 insertions(+), 0 deletions(-) > create mode 100644 arch/arm/mach-mxs/devices/platform-auart.c > > diff --git a/arch/arm/mach-mxs/clock-mx28.c b/arch/arm/mach-mxs/clock-mx28.c > index 74e2103..9f65934 100644 > --- a/arch/arm/mach-mxs/clock-mx28.c > +++ b/arch/arm/mach-mxs/clock-mx28.c > @@ -603,6 +603,11 @@ _DEFINE_CLOCK(fec_clk, ENET, DISABLE, &hbus_clk); > > static struct clk_lookup lookups[] = { > _REGISTER_CLOCK("mxs-duart.0", NULL, uart_clk) > + _REGISTER_CLOCK("mxs-auart.0", NULL, uart_clk) > + _REGISTER_CLOCK("mxs-auart.1", NULL, uart_clk) > + _REGISTER_CLOCK("mxs-auart.2", NULL, uart_clk) > + _REGISTER_CLOCK("mxs-auart.3", NULL, uart_clk) > + _REGISTER_CLOCK("mxs-auart.4", NULL, uart_clk) > _REGISTER_CLOCK("fec.0", NULL, fec_clk) > _REGISTER_CLOCK("rtc", NULL, rtc_clk) > _REGISTER_CLOCK("pll2", NULL, pll2_clk) > diff --git a/arch/arm/mach-mxs/devices-mx28.h b/arch/arm/mach-mxs/devices-mx28.h > index 00b736c..a0b70df 100644 > --- a/arch/arm/mach-mxs/devices-mx28.h > +++ b/arch/arm/mach-mxs/devices-mx28.h > @@ -15,6 +15,14 @@ extern const struct mxs_duart_data mx28_duart_data __initconst; > #define mx28_add_duart() \ > mxs_add_duart(&mx28_duart_data) > > +extern const struct mxs_auart_data mx28_auart_data[] __initconst; > +#define mx28_add_auart(id) mxs_add_auart(&mx28_auart_data[id]) > +#define mx28_add_auart0() mx28_add_auart(0) > +#define mx28_add_auart1() mx28_add_auart(1) > +#define mx28_add_auart2() mx28_add_auart(2) > +#define mx28_add_auart3() mx28_add_auart(3) > +#define mx28_add_auart4() mx28_add_auart(4) I wouldn't add the mx28_add_auartX macros, only mx28_add_auart. The former are a relict of the time when we didn't use an array to get the data from. > + > extern const struct mxs_fec_data mx28_fec_data[] __initconst; > #define mx28_add_fec(id, pdata) \ > mxs_add_fec(&mx28_fec_data[id], pdata) > diff --git a/arch/arm/mach-mxs/devices/Kconfig b/arch/arm/mach-mxs/devices/Kconfig > index a35a2dc..9d16540 100644 > --- a/arch/arm/mach-mxs/devices/Kconfig > +++ b/arch/arm/mach-mxs/devices/Kconfig > @@ -1,5 +1,8 @@ > config MXS_HAVE_PLATFORM_DUART > bool > > +config MXS_HAVE_PLATFORM_AUART > + bool > + This doesn't happen to be an amba device, too? > config MXS_HAVE_PLATFORM_FEC > bool > diff --git a/arch/arm/mach-mxs/devices/Makefile b/arch/arm/mach-mxs/devices/Makefile > index 4b5266a..b46d18d 100644 > --- a/arch/arm/mach-mxs/devices/Makefile > +++ b/arch/arm/mach-mxs/devices/Makefile > @@ -1,2 +1,3 @@ > obj-$(CONFIG_MXS_HAVE_PLATFORM_DUART) += platform-duart.o > +obj-$(CONFIG_MXS_HAVE_PLATFORM_AUART) += platform-auart.o > obj-$(CONFIG_MXS_HAVE_PLATFORM_FEC) += platform-fec.o this conflicts with the "make duart use amba-pl011 driver" patch. Also can you please keep the file sorted alphabetically? > diff --git a/arch/arm/mach-mxs/devices/platform-auart.c b/arch/arm/mach-mxs/devices/platform-auart.c > new file mode 100644 > index 0000000..d5e6cb2 > --- /dev/null > +++ b/arch/arm/mach-mxs/devices/platform-auart.c > @@ -0,0 +1,54 @@ > +/* > + * Copyright (C) 2010 Pengutronix > + * Sascha Hauer <s.hauer@pengutronix.de> > + * > + * This program is free software; you can redistribute it and/or modify it under > + * the terms of the GNU General Public License version 2 as published by the > + * Free Software Foundation. > + */ > +#include <asm/sizes.h> > +#include <mach/mx28.h> > +#include <mach/devices-common.h> > + > +#define mxs_auart_data_entry_single(soc, _id) \ > + { \ > + .id = _id, \ > + .iobase = soc ## _AUART ## _id ## _BASE_ADDR, \ > + .irq = soc ## _INT_AUART ## _id, \ > + } > + > +#define mxs_auart_data_entry(soc, _id) \ > + [_id] = mxs_auart_data_entry_single(soc, _id) > + > +#ifdef CONFIG_SOC_IMX28 > +const struct mxs_auart_data mx28_auart_data[] __initconst = { > +#define mx28_auart_data_entry(_id) \ > + mxs_auart_data_entry(MX28, _id) > + mx28_auart_data_entry(0), > + mx28_auart_data_entry(1), > + mx28_auart_data_entry(2), > + mx28_auart_data_entry(3), > + mx28_auart_data_entry(4), > +}; > +#endif > + > +struct platform_device *__init mxs_add_auart( > + const struct mxs_auart_data *data) > +{ > + struct resource res[] = { > + { > + .start = data->iobase, > + .end = data->iobase + SZ_4K - 1, > + .flags = IORESOURCE_MEM, > + }, { > + .start = data->irq, > + .end = data->irq, > + .flags = IORESOURCE_IRQ, > + }, > + }; > + > + return mxs_add_platform_device_dmamask("mxs-auart", data->id, > + res, ARRAY_SIZE(res), NULL, 0, > + DMA_BIT_MASK(32)); > +} > + > diff --git a/arch/arm/mach-mxs/include/mach/devices-common.h b/arch/arm/mach-mxs/include/mach/devices-common.h > index 3da48d4..b8ab332 100644 > --- a/arch/arm/mach-mxs/include/mach/devices-common.h > +++ b/arch/arm/mach-mxs/include/mach/devices-common.h > @@ -33,6 +33,16 @@ struct mxs_duart_data { > struct platform_device *__init mxs_add_duart( > const struct mxs_duart_data *data); > > +/* auart */ > +struct mxs_auart_data { > + int id; > + resource_size_t iobase; > + resource_size_t iosize; > + resource_size_t irq; > +}; > +struct platform_device *__init mxs_add_auart( > + const struct mxs_auart_data *data); > + > /* fec */ > #include <linux/fec.h> > struct mxs_fec_data { Best regards Uwe -- Pengutronix e.K. | Uwe Kleine-K?nig | Industrial Linux Solutions | http://www.pengutronix.de/ | ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 2/2] ARM i.MXS: Add auart platform support for i.MX28 2011-01-11 15:17 ` Uwe Kleine-König @ 2011-01-11 15:26 ` Sascha Hauer 2011-01-12 5:06 ` Shawn Guo 1 sibling, 0 replies; 14+ messages in thread From: Sascha Hauer @ 2011-01-11 15:26 UTC (permalink / raw) To: linux-arm-kernel On Tue, Jan 11, 2011 at 04:17:25PM +0100, Uwe Kleine-K?nig wrote: > > > > +extern const struct mxs_auart_data mx28_auart_data[] __initconst; > > +#define mx28_add_auart(id) mxs_add_auart(&mx28_auart_data[id]) > > +#define mx28_add_auart0() mx28_add_auart(0) > > +#define mx28_add_auart1() mx28_add_auart(1) > > +#define mx28_add_auart2() mx28_add_auart(2) > > +#define mx28_add_auart3() mx28_add_auart(3) > > +#define mx28_add_auart4() mx28_add_auart(4) > I wouldn't add the mx28_add_auartX macros, only mx28_add_auart. The > former are a relict of the time when we didn't use an array to get the > data from. With pleasure. > > > + > > extern const struct mxs_fec_data mx28_fec_data[] __initconst; > > #define mx28_add_fec(id, pdata) \ > > mxs_add_fec(&mx28_fec_data[id], pdata) > > diff --git a/arch/arm/mach-mxs/devices/Kconfig b/arch/arm/mach-mxs/devices/Kconfig > > index a35a2dc..9d16540 100644 > > --- a/arch/arm/mach-mxs/devices/Kconfig > > +++ b/arch/arm/mach-mxs/devices/Kconfig > > @@ -1,5 +1,8 @@ > > config MXS_HAVE_PLATFORM_DUART > > bool > > > > +config MXS_HAVE_PLATFORM_AUART > > + bool > > + > This doesn't happen to be an amba device, too? no > > > config MXS_HAVE_PLATFORM_FEC > > bool > > diff --git a/arch/arm/mach-mxs/devices/Makefile b/arch/arm/mach-mxs/devices/Makefile > > index 4b5266a..b46d18d 100644 > > --- a/arch/arm/mach-mxs/devices/Makefile > > +++ b/arch/arm/mach-mxs/devices/Makefile > > @@ -1,2 +1,3 @@ > > obj-$(CONFIG_MXS_HAVE_PLATFORM_DUART) += platform-duart.o > > +obj-$(CONFIG_MXS_HAVE_PLATFORM_AUART) += platform-auart.o > > obj-$(CONFIG_MXS_HAVE_PLATFORM_FEC) += platform-fec.o > this conflicts with the "make duart use amba-pl011 driver" patch. Nah, let the maintainer deal with it... > Also can you please keep the file sorted alphabetically? ok Sascha -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 2/2] ARM i.MXS: Add auart platform support for i.MX28 2011-01-11 15:17 ` Uwe Kleine-König 2011-01-11 15:26 ` Sascha Hauer @ 2011-01-12 5:06 ` Shawn Guo 1 sibling, 0 replies; 14+ messages in thread From: Shawn Guo @ 2011-01-12 5:06 UTC (permalink / raw) To: linux-arm-kernel On Tue, Jan 11, 2011 at 04:17:25PM +0100, Uwe Kleine-K?nig wrote: > Hello Sascha, > > On Tue, Jan 11, 2011 at 04:09:05PM +0100, Sascha Hauer wrote: > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> > > --- > > arch/arm/mach-mxs/clock-mx28.c | 5 ++ > > arch/arm/mach-mxs/devices-mx28.h | 8 +++ > > arch/arm/mach-mxs/devices/Kconfig | 3 + > > arch/arm/mach-mxs/devices/Makefile | 1 + > > arch/arm/mach-mxs/devices/platform-auart.c | 54 +++++++++++++++++++++++ > > arch/arm/mach-mxs/include/mach/devices-common.h | 10 ++++ > > 6 files changed, 81 insertions(+), 0 deletions(-) > > create mode 100644 arch/arm/mach-mxs/devices/platform-auart.c > > > > diff --git a/arch/arm/mach-mxs/clock-mx28.c b/arch/arm/mach-mxs/clock-mx28.c > > index 74e2103..9f65934 100644 > > --- a/arch/arm/mach-mxs/clock-mx28.c > > +++ b/arch/arm/mach-mxs/clock-mx28.c > > @@ -603,6 +603,11 @@ _DEFINE_CLOCK(fec_clk, ENET, DISABLE, &hbus_clk); > > > > static struct clk_lookup lookups[] = { > > _REGISTER_CLOCK("mxs-duart.0", NULL, uart_clk) > > + _REGISTER_CLOCK("mxs-auart.0", NULL, uart_clk) > > + _REGISTER_CLOCK("mxs-auart.1", NULL, uart_clk) > > + _REGISTER_CLOCK("mxs-auart.2", NULL, uart_clk) > > + _REGISTER_CLOCK("mxs-auart.3", NULL, uart_clk) > > + _REGISTER_CLOCK("mxs-auart.4", NULL, uart_clk) > > _REGISTER_CLOCK("fec.0", NULL, fec_clk) > > _REGISTER_CLOCK("rtc", NULL, rtc_clk) > > _REGISTER_CLOCK("pll2", NULL, pll2_clk) > > diff --git a/arch/arm/mach-mxs/devices-mx28.h b/arch/arm/mach-mxs/devices-mx28.h > > index 00b736c..a0b70df 100644 > > --- a/arch/arm/mach-mxs/devices-mx28.h > > +++ b/arch/arm/mach-mxs/devices-mx28.h > > @@ -15,6 +15,14 @@ extern const struct mxs_duart_data mx28_duart_data __initconst; > > #define mx28_add_duart() \ > > mxs_add_duart(&mx28_duart_data) > > > > +extern const struct mxs_auart_data mx28_auart_data[] __initconst; > > +#define mx28_add_auart(id) mxs_add_auart(&mx28_auart_data[id]) > > +#define mx28_add_auart0() mx28_add_auart(0) > > +#define mx28_add_auart1() mx28_add_auart(1) > > +#define mx28_add_auart2() mx28_add_auart(2) > > +#define mx28_add_auart3() mx28_add_auart(3) > > +#define mx28_add_auart4() mx28_add_auart(4) > I wouldn't add the mx28_add_auartX macros, only mx28_add_auart. The > former are a relict of the time when we didn't use an array to get the > data from. > > > + > > extern const struct mxs_fec_data mx28_fec_data[] __initconst; > > #define mx28_add_fec(id, pdata) \ > > mxs_add_fec(&mx28_fec_data[id], pdata) > > diff --git a/arch/arm/mach-mxs/devices/Kconfig b/arch/arm/mach-mxs/devices/Kconfig > > index a35a2dc..9d16540 100644 > > --- a/arch/arm/mach-mxs/devices/Kconfig > > +++ b/arch/arm/mach-mxs/devices/Kconfig > > @@ -1,5 +1,8 @@ > > config MXS_HAVE_PLATFORM_DUART > > bool > > > > +config MXS_HAVE_PLATFORM_AUART > > + bool > > + > This doesn't happen to be an amba device, too? > > > config MXS_HAVE_PLATFORM_FEC > > bool > > diff --git a/arch/arm/mach-mxs/devices/Makefile b/arch/arm/mach-mxs/devices/Makefile > > index 4b5266a..b46d18d 100644 > > --- a/arch/arm/mach-mxs/devices/Makefile > > +++ b/arch/arm/mach-mxs/devices/Makefile > > @@ -1,2 +1,3 @@ > > obj-$(CONFIG_MXS_HAVE_PLATFORM_DUART) += platform-duart.o > > +obj-$(CONFIG_MXS_HAVE_PLATFORM_AUART) += platform-auart.o > > obj-$(CONFIG_MXS_HAVE_PLATFORM_FEC) += platform-fec.o > this conflicts with the "make duart use amba-pl011 driver" patch. May I know the reason that the patch is still pending for being merged? DEBUG_SPINLOCK_SLEEP warning? [...] -- Regards, Shawn ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 2/2] ARM i.MXS: Add auart platform support for i.MX28 2011-01-11 15:09 ` [PATCH 2/2] ARM i.MXS: Add auart platform support for i.MX28 Sascha Hauer 2011-01-11 15:17 ` Uwe Kleine-König @ 2011-01-12 6:17 ` Shawn Guo 1 sibling, 0 replies; 14+ messages in thread From: Shawn Guo @ 2011-01-12 6:17 UTC (permalink / raw) To: linux-arm-kernel Hi Sascha, On Tue, Jan 11, 2011 at 04:09:05PM +0100, Sascha Hauer wrote: > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> > --- > arch/arm/mach-mxs/clock-mx28.c | 5 ++ > arch/arm/mach-mxs/devices-mx28.h | 8 +++ > arch/arm/mach-mxs/devices/Kconfig | 3 + > arch/arm/mach-mxs/devices/Makefile | 1 + > arch/arm/mach-mxs/devices/platform-auart.c | 54 +++++++++++++++++++++++ > arch/arm/mach-mxs/include/mach/devices-common.h | 10 ++++ > 6 files changed, 81 insertions(+), 0 deletions(-) > create mode 100644 arch/arm/mach-mxs/devices/platform-auart.c > [...] > +struct platform_device *__init mxs_add_auart( > + const struct mxs_auart_data *data) > +{ > + struct resource res[] = { > + { > + .start = data->iobase, > + .end = data->iobase + SZ_4K - 1, The io size here is supposed to mean the space for holding the current defined registers or the memory map size for this block given by SoC design? If it's the latter, the size should be SZ_8K. > + .flags = IORESOURCE_MEM, > + }, { > + .start = data->irq, > + .end = data->irq, > + .flags = IORESOURCE_IRQ, > + }, > + }; > + > + return mxs_add_platform_device_dmamask("mxs-auart", data->id, > + res, ARRAY_SIZE(res), NULL, 0, > + DMA_BIT_MASK(32)); > +} > + -- Regards, Shawn ^ permalink raw reply [flat|nested] 14+ messages in thread
* Add i.MX23/28 auart support 2011-01-11 15:09 Add i.MX23/28 auart support Sascha Hauer 2011-01-11 15:09 ` [PATCH 1/2] serial: Add auart driver for i.MX23/28 Sascha Hauer 2011-01-11 15:09 ` [PATCH 2/2] ARM i.MXS: Add auart platform support for i.MX28 Sascha Hauer @ 2011-01-12 5:31 ` Shawn Guo 2 siblings, 0 replies; 14+ messages in thread From: Shawn Guo @ 2011-01-12 5:31 UTC (permalink / raw) To: linux-arm-kernel Hi Sascha, On Tue, Jan 11, 2011 at 04:09:03PM +0100, Sascha Hauer wrote: > The following series adds support for the auart found on i.MX23/28 > > Sascha Hauer (2): > serial: Add auart driver for i.MX23/28 > ARM i.MXS: Add auart platform support for i.MX28 > > arch/arm/mach-mxs/clock-mx28.c | 5 + > arch/arm/mach-mxs/devices-mx28.h | 8 + > arch/arm/mach-mxs/devices/Kconfig | 3 + > arch/arm/mach-mxs/devices/Makefile | 1 + > arch/arm/mach-mxs/devices/platform-auart.c | 54 ++ > arch/arm/mach-mxs/include/mach/devices-common.h | 10 + > drivers/serial/Kconfig | 15 + > drivers/serial/Makefile | 1 + > drivers/serial/mxs-auart.c | 763 +++++++++++++++++++++++ > 9 files changed, 860 insertions(+), 0 deletions(-) > create mode 100644 arch/arm/mach-mxs/devices/platform-auart.c > create mode 100644 drivers/serial/mxs-auart.c > > I just fetched the branch imx-for-2.6.38 and tried to apply the patch set on it, but failed. What commit should I apply against? -- Regards, Shawn ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2] Add i.MX23/28 auart support @ 2011-01-13 13:14 Sascha Hauer 2011-01-13 13:14 ` [PATCH 1/2] serial: Add auart driver for i.MX23/28 Sascha Hauer 0 siblings, 1 reply; 14+ messages in thread From: Sascha Hauer @ 2011-01-13 13:14 UTC (permalink / raw) To: linux-arm-kernel This is the second version of these patches. I have reworked the driver to support the NPCK, BRKINT, PARMRK, IGNPAR, IGNBRK and cflag CREAD flags. I have tested these flags to the best of my knowledge, but it's sometimes hard to judge if the behaviour is expected. Sascha Sascha Hauer (2): serial: Add auart driver for i.MX23/28 ARM MXS: Add auart platform support for i.MX28 arch/arm/mach-mxs/clock-mx28.c | 5 + arch/arm/mach-mxs/devices-mx28.h | 8 + arch/arm/mach-mxs/devices/Kconfig | 3 + arch/arm/mach-mxs/devices/Makefile | 1 + arch/arm/mach-mxs/devices/platform-auart.c | 54 ++ arch/arm/mach-mxs/include/mach/devices-common.h | 10 + drivers/serial/Kconfig | 15 + drivers/serial/Makefile | 1 + drivers/serial/mxs-auart.c | 806 +++++++++++++++++++++++ 9 files changed, 903 insertions(+), 0 deletions(-) create mode 100644 arch/arm/mach-mxs/devices/platform-auart.c create mode 100644 drivers/serial/mxs-auart.c ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 1/2] serial: Add auart driver for i.MX23/28 2011-01-13 13:14 [PATCH v2] " Sascha Hauer @ 2011-01-13 13:14 ` Sascha Hauer 0 siblings, 0 replies; 14+ messages in thread From: Sascha Hauer @ 2011-01-13 13:14 UTC (permalink / raw) To: linux-arm-kernel Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/serial/Kconfig | 15 + drivers/serial/Makefile | 1 + drivers/serial/mxs-auart.c | 806 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 822 insertions(+), 0 deletions(-) create mode 100644 drivers/serial/mxs-auart.c diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index ec3c214..6a2a39c 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1657,4 +1657,19 @@ config SERIAL_PCH_UART This driver is for PCH(Platform controller Hub) UART of Intel EG20T which is an IOH(Input/Output Hub) for x86 embedded processor. Enabling PCH_DMA, this PCH UART works as DMA mode. + +config SERIAL_MXS_AUART + depends on ARCH_MXS + tristate "MXS AUART support" + select SERIAL_CORE + help + This driver supports the MXS Application UART (AUART) port. + +config SERIAL_MXS_AUART_CONSOLE + bool "MXS AUART console support" + depends on SERIAL_MXS_AUART=y + select SERIAL_CORE_CONSOLE + help + Enable a MXS AUART port to be the system console. + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 8ea92e9..c855071 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -92,3 +92,4 @@ obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o +obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o diff --git a/drivers/serial/mxs-auart.c b/drivers/serial/mxs-auart.c new file mode 100644 index 0000000..6529fd7 --- /dev/null +++ b/drivers/serial/mxs-auart.c @@ -0,0 +1,806 @@ +/* + * Freescale STMP37XX/STMP378X Application UART driver + * + * Author: dmitry pervushin <dimka@embeddedalley.com> + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include <asm/cacheflush.h> + +#define MXS_AUART_PORTS 5 + +#define AUART_CTRL0 0x00000000 +#define AUART_CTRL0_SET 0x00000004 +#define AUART_CTRL0_CLR 0x00000008 +#define AUART_CTRL0_TOG 0x0000000c +#define AUART_CTRL1 0x00000010 +#define AUART_CTRL1_SET 0x00000014 +#define AUART_CTRL1_CLR 0x00000018 +#define AUART_CTRL1_TOG 0x0000001c +#define AUART_CTRL2 0x00000020 +#define AUART_CTRL2_SET 0x00000024 +#define AUART_CTRL2_CLR 0x00000028 +#define AUART_CTRL2_TOG 0x0000002c +#define AUART_LINECTRL 0x00000030 +#define AUART_LINECTRL_SET 0x00000034 +#define AUART_LINECTRL_CLR 0x00000038 +#define AUART_LINECTRL_TOG 0x0000003c +#define AUART_LINECTRL2 0x00000040 +#define AUART_LINECTRL2_SET 0x00000044 +#define AUART_LINECTRL2_CLR 0x00000048 +#define AUART_LINECTRL2_TOG 0x0000004c +#define AUART_INTR 0x00000050 +#define AUART_INTR_SET 0x00000054 +#define AUART_INTR_CLR 0x00000058 +#define AUART_INTR_TOG 0x0000005c +#define AUART_DATA 0x00000060 +#define AUART_STAT 0x00000070 +#define AUART_DEBUG 0x00000080 +#define AUART_VERSION 0x00000090 +#define AUART_AUTOBAUD 0x000000a0 + +#define AUART_CTRL0_SFTRST (1 << 31) +#define AUART_CTRL0_CLKGATE (1 << 30) + + +#define AUART_CTRL2_CTSEN (1 << 15) +#define AUART_CTRL2_RTS (1 << 11) +#define AUART_CTRL2_RXE (1 << 9) +#define AUART_CTRL2_TXE (1 << 8) +#define AUART_CTRL2_UARTEN (1 << 0) + +#define AUART_LINECTRL_BAUD_DIVINT_SHIFT 16 +#define AUART_LINECTRL_BAUD_DIVINT_MASK 0xffff0000 +#define AUART_LINECTRL_BAUD_DIVINT(v) (((v) & 0xffff) << 16) +#define AUART_LINECTRL_BAUD_DIVFRAC_SHIFT 8 +#define AUART_LINECTRL_BAUD_DIVFRAC_MASK 0x00003f00 +#define AUART_LINECTRL_BAUD_DIVFRAC(v) (((v) & 0x3f) << 8) +#define AUART_LINECTRL_WLEN_MASK 0x00000060 +#define AUART_LINECTRL_WLEN(v) (((v) & 0x3) << 5) +#define AUART_LINECTRL_FEN (1 << 4) +#define AUART_LINECTRL_STP2 (1 << 3) +#define AUART_LINECTRL_EPS (1 << 2) +#define AUART_LINECTRL_PEN (1 << 1) +#define AUART_LINECTRL_BRK (1 << 0) + +#define AUART_INTR_RTIEN (1 << 22) +#define AUART_INTR_TXIEN (1 << 21) +#define AUART_INTR_RXIEN (1 << 20) +#define AUART_INTR_CTSMIEN (1 << 17) +#define AUART_INTR_RTIS (1 << 6) +#define AUART_INTR_TXIS (1 << 5) +#define AUART_INTR_RXIS (1 << 4) +#define AUART_INTR_CTSMIS (1 << 1) + +#define AUART_STAT_BUSY (1 << 29) +#define AUART_STAT_CTS (1 << 28) +#define AUART_STAT_TXFE (1 << 27) +#define AUART_STAT_TXFF (1 << 25) +#define AUART_STAT_RXFE (1 << 24) +#define AUART_STAT_OERR (1 << 19) +#define AUART_STAT_BERR (1 << 18) +#define AUART_STAT_PERR (1 << 17) +#define AUART_STAT_FERR (1 << 16) + +#define UART_DUMMY_STAT_RX (1 << 31) + +static struct uart_driver auart_driver; + +struct mxs_auart_port { + struct uart_port port; + + unsigned int flags; + unsigned int ctrl; + + unsigned int irq; + + struct clk *clk; + struct device *dev; +}; + +static void mxs_auart_stop_tx(struct uart_port *u); + +#define to_auart_port(u) container_of(u, struct mxs_auart_port, port) + +static inline void mxs_auart_tx_chars(struct mxs_auart_port *s) +{ + struct circ_buf *xmit = &s->port.state->xmit; + + while (!(readl(s->port.membase + AUART_STAT) & + AUART_STAT_TXFF)) { + if (s->port.x_char) { + s->port.icount.tx++; + writel(s->port.x_char, + s->port.membase + AUART_DATA); + s->port.x_char = 0; + continue; + } + if (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) { + s->port.icount.tx++; + writel(xmit->buf[xmit->tail], + s->port.membase + AUART_DATA); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); + } else + break; + } + if (uart_circ_empty(&(s->port.state->xmit))) + writel(AUART_INTR_TXIEN, + s->port.membase + AUART_INTR_CLR); + else + writel(AUART_INTR_TXIEN, + s->port.membase + AUART_INTR_SET); + + if (uart_tx_stopped(&s->port)) + mxs_auart_stop_tx(&s->port); +} + +static void mxs_auart_rx_char(struct mxs_auart_port *s) +{ + int flag; + u32 stat; + u8 c; + + c = readl(s->port.membase + AUART_DATA); + stat = readl(s->port.membase + AUART_STAT) | UART_DUMMY_STAT_RX; + + flag = TTY_NORMAL; + s->port.icount.rx++; + + if (stat & AUART_STAT_BERR) { + s->port.icount.brk++; + if (uart_handle_break(&s->port)) + goto out; + } else if (stat & AUART_STAT_PERR) { + s->port.icount.parity++; + } else if (stat & AUART_STAT_FERR) { + s->port.icount.frame++; + } + + /* + * Mask off conditions which should be ingored. + */ + stat &= s->port.read_status_mask; + + if (stat & AUART_STAT_BERR) { + flag = TTY_BREAK; + } else if (stat & AUART_STAT_PERR) + flag = TTY_PARITY; + else if (stat & AUART_STAT_FERR) + flag = TTY_FRAME; + + if (stat & AUART_STAT_OERR) + s->port.icount.overrun++; + + if (uart_handle_sysrq_char(&s->port, c)) + goto out; + + uart_insert_char(&s->port, stat, AUART_STAT_OERR, c, flag); +out: + writel(stat, s->port.membase + AUART_STAT); +} + +static void mxs_auart_rx_chars(struct mxs_auart_port *s) +{ + struct tty_struct *tty = s->port.state->port.tty; + u32 stat = 0; + + for (;;) { + stat = readl(s->port.membase + AUART_STAT); + if (stat & AUART_STAT_RXFE) + break; + mxs_auart_rx_char(s); + } + + writel(stat, s->port.membase + AUART_STAT); + tty_flip_buffer_push(tty); +} + +static int mxs_auart_request_port(struct uart_port *u) +{ + return 0; +} + +static int mxs_auart_verify_port(struct uart_port *u, + struct serial_struct *ser) +{ + if (u->type != PORT_UNKNOWN && u->type != PORT_IMX) + return -EINVAL; + return 0; +} + +static void mxs_auart_config_port(struct uart_port *u, int flags) +{ +} + +static const char *mxs_auart_type(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + return dev_name(s->dev); +} + +static void mxs_auart_release_port(struct uart_port *u) +{ +} + +static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) +{ + struct mxs_auart_port *s = to_auart_port(u); + + u32 ctrl = readl(u->membase + AUART_CTRL2); + + ctrl &= ~AUART_CTRL2_RTS; + if (mctrl & TIOCM_RTS) + ctrl |= AUART_CTRL2_RTS; + s->ctrl = mctrl; + writel(ctrl, u->membase + AUART_CTRL2); +} + +static u32 mxs_auart_get_mctrl(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + u32 stat = readl(u->membase + AUART_STAT); + int ctrl2 = readl(u->membase + AUART_CTRL2); + u32 mctrl = s->ctrl; + + mctrl &= ~TIOCM_CTS; + if (stat & AUART_STAT_CTS) + mctrl |= TIOCM_CTS; + + if (ctrl2 & AUART_CTRL2_RTS) + mctrl |= TIOCM_RTS; + + return mctrl; +} + +static void mxs_auart_settermios(struct uart_port *u, + struct ktermios *termios, + struct ktermios *old) +{ + u32 bm, ctrl, ctrl2, div; + unsigned int cflag, baud; + + cflag = termios->c_cflag; + + ctrl = AUART_LINECTRL_FEN; + ctrl2 = readl(u->membase + AUART_CTRL2); + + /* byte size */ + switch (cflag & CSIZE) { + case CS5: + bm = 0; + break; + case CS6: + bm = 1; + break; + case CS7: + bm = 2; + break; + case CS8: + bm = 3; + break; + default: + return; + } + + ctrl |= AUART_LINECTRL_WLEN(bm); + + /* parity */ + if (cflag & PARENB) { + ctrl |= AUART_LINECTRL_PEN; + if ((cflag & PARODD) == 0) + ctrl |= AUART_LINECTRL_EPS; + } + + u->read_status_mask = 0; + + if (termios->c_iflag & INPCK) + u->read_status_mask |= AUART_STAT_PERR; + if (termios->c_iflag & (BRKINT | PARMRK)) + u->read_status_mask |= AUART_STAT_BERR; + + /* + * Characters to ignore + */ + u->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + u->ignore_status_mask |= AUART_STAT_PERR; + if (termios->c_iflag & IGNBRK) { + u->ignore_status_mask |= AUART_STAT_BERR; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + u->ignore_status_mask |= AUART_STAT_OERR; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + u->ignore_status_mask |= UART_DUMMY_STAT_RX; + + /* figure out the stop bits requested */ + if (cflag & CSTOPB) + ctrl |= AUART_LINECTRL_STP2; + + /* figure out the hardware flow control settings */ + if (cflag & CRTSCTS) + ctrl2 |= AUART_CTRL2_CTSEN; + else + ctrl2 &= ~AUART_CTRL2_CTSEN; + + /* set baud rate */ + baud = uart_get_baud_rate(u, termios, old, 0, u->uartclk); + div = u->uartclk * 32 / baud; + ctrl |= AUART_LINECTRL_BAUD_DIVFRAC(div & 0x3F); + ctrl |= AUART_LINECTRL_BAUD_DIVINT(div >> 6); + + if ((cflag & CREAD) != 0) + ctrl2 |= AUART_CTRL2_RXE; + + writel(ctrl, u->membase + AUART_LINECTRL); + writel(ctrl2, u->membase + AUART_CTRL2); +} + +static irqreturn_t mxs_auart_irq_handle(int irq, void *context) +{ + u32 istatus, istat; + struct mxs_auart_port *s = context; + u32 stat = readl(s->port.membase + AUART_STAT); + + istatus = istat = readl(s->port.membase + AUART_INTR); + + if (istat & AUART_INTR_CTSMIS) { + uart_handle_cts_change(&s->port, stat & AUART_STAT_CTS); + writel(AUART_INTR_CTSMIS, + s->port.membase + AUART_INTR_CLR); + istat &= ~AUART_INTR_CTSMIS; + } + + if (istat & (AUART_INTR_RTIS | AUART_INTR_RXIS)) { + mxs_auart_rx_chars(s); + istat &= ~(AUART_INTR_RTIS | AUART_INTR_RXIS); + } + + if (istat & AUART_INTR_TXIS) { + mxs_auart_tx_chars(s); + istat &= ~AUART_INTR_TXIS; + } + + writel(istatus & (AUART_INTR_RTIS + | AUART_INTR_TXIS + | AUART_INTR_RXIS + | AUART_INTR_CTSMIS), + s->port.membase + AUART_INTR_CLR); + + return IRQ_HANDLED; +} + +static void mxs_auart_reset(struct uart_port *u) +{ + int i; + unsigned int reg; + + writel(AUART_CTRL0_SFTRST, + u->membase + AUART_CTRL0_CLR); + + for (i = 0; i < 10000; i++) { + reg = readl(u->membase + AUART_CTRL0); + if (!(reg & AUART_CTRL0_SFTRST)) + break; + udelay(3); + } +} + +static int mxs_auart_startup(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + clk_enable(s->clk); + + writel(AUART_CTRL0_CLKGATE, + u->membase + AUART_CTRL0_CLR); + + writel(AUART_CTRL2_UARTEN, + s->port.membase + AUART_CTRL2_SET); + + writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN, + s->port.membase + AUART_INTR); + + writel(AUART_INTR_CTSMIEN, + s->port.membase + AUART_INTR_SET); + + /* + * Enable fifo so all four bytes of a DMA word are written to + * output (otherwise, only the LSB is written, ie. 1 in 4 bytes) + */ + writel(AUART_LINECTRL_FEN, + s->port.membase + AUART_LINECTRL_SET); + + return 0; +} + +static void mxs_auart_shutdown(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + writel(AUART_CTRL2_UARTEN, + s->port.membase + AUART_CTRL2_CLR); + + writel(AUART_CTRL0_CLKGATE, + u->membase + AUART_CTRL0_SET); + + writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | + AUART_INTR_CTSMIEN, + s->port.membase + AUART_INTR_CLR); + + clk_disable(s->clk); +} + +static unsigned int mxs_auart_tx_empty(struct uart_port *u) +{ + if (readl(u->membase + AUART_STAT) & AUART_STAT_TXFE) + return TIOCSER_TEMT; + else + return 0; +} + +static void mxs_auart_start_tx(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + /* enable transmitter */ + writel(AUART_CTRL2_TXE, u->membase + AUART_CTRL2_SET); + + mxs_auart_tx_chars(s); +} + +static void mxs_auart_stop_tx(struct uart_port *u) +{ + writel(AUART_CTRL2_TXE, u->membase + AUART_CTRL2_CLR); +} + +static void mxs_auart_stop_rx(struct uart_port *u) +{ + writel(AUART_CTRL2_RXE, u->membase + AUART_CTRL2_CLR); +} + +static void mxs_auart_break_ctl(struct uart_port *u, int ctl) +{ + if (ctl) + writel(AUART_LINECTRL_BRK, + u->membase + AUART_LINECTRL_SET); + else + writel(AUART_LINECTRL_BRK, + u->membase + AUART_LINECTRL_CLR); +} + +static void mxs_auart_enable_ms(struct uart_port *port) +{ + /* just empty */ +} + +static struct uart_ops mxs_auart_ops = { + .tx_empty = mxs_auart_tx_empty, + .start_tx = mxs_auart_start_tx, + .stop_tx = mxs_auart_stop_tx, + .stop_rx = mxs_auart_stop_rx, + .enable_ms = mxs_auart_enable_ms, + .break_ctl = mxs_auart_break_ctl, + .set_mctrl = mxs_auart_set_mctrl, + .get_mctrl = mxs_auart_get_mctrl, + .startup = mxs_auart_startup, + .shutdown = mxs_auart_shutdown, + .set_termios = mxs_auart_settermios, + .type = mxs_auart_type, + .release_port = mxs_auart_release_port, + .request_port = mxs_auart_request_port, + .config_port = mxs_auart_config_port, + .verify_port = mxs_auart_verify_port, +}; + +static struct mxs_auart_port *auart_port[MXS_AUART_PORTS]; + +#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE +static void mxs_auart_console_putchar(struct uart_port *port, int ch) +{ + unsigned int status; + + do { + status = readl(port->membase + AUART_STAT); + } while (status & AUART_STAT_TXFF); + writel(ch, port->membase + AUART_DATA); +} + +static void +auart_console_write(struct console *co, const char *str, unsigned int count) +{ + struct mxs_auart_port *s; + struct uart_port *port; + unsigned int status, old_ctrl0, old_ctrl2; + + if (co->index > MXS_AUART_PORTS || co->index < 0) + return; + + s = auart_port[co->index]; + port = &s->port; + + clk_enable(s->clk); + + /* First save the CR then disable the interrupts */ + old_ctrl2 = readl(port->membase + AUART_CTRL2); + old_ctrl0 = readl(port->membase + AUART_CTRL0); + + writel(AUART_CTRL0_CLKGATE, + port->membase + AUART_CTRL0_CLR); + writel(AUART_CTRL2_UARTEN | AUART_CTRL2_TXE, + port->membase + AUART_CTRL2_SET); + + uart_console_write(port, str, count, mxs_auart_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the TCR + */ + do { + status = readl(port->membase + AUART_STAT); + } while (status & AUART_STAT_BUSY); + + writel(old_ctrl0, port->membase + AUART_CTRL0); + writel(old_ctrl2, port->membase + AUART_CTRL2); + + clk_disable(s->clk); +} + +static void __init +auart_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + unsigned int lcr_h, quot; + + if (!(readl(port->membase + AUART_CTRL2) & AUART_CTRL2_UARTEN)) + return; + + lcr_h = readl(port->membase + AUART_LINECTRL); + + *parity = 'n'; + if (lcr_h & AUART_LINECTRL_PEN) { + if (lcr_h & AUART_LINECTRL_EPS) + *parity = 'e'; + else + *parity = 'o'; + } + + if ((lcr_h & AUART_LINECTRL_WLEN_MASK) == AUART_LINECTRL_WLEN(2)) + *bits = 7; + else + *bits = 8; + + quot = ((readl(port->membase + AUART_LINECTRL) + & AUART_LINECTRL_BAUD_DIVINT_MASK)) + >> (AUART_LINECTRL_BAUD_DIVINT_SHIFT - 6); + quot |= ((readl(port->membase + AUART_LINECTRL) + & AUART_LINECTRL_BAUD_DIVFRAC_MASK)) + >> AUART_LINECTRL_BAUD_DIVFRAC_SHIFT; + if (quot == 0) + quot = 1; + + *baud = (port->uartclk << 2) / quot; +} + +static int __init +auart_console_setup(struct console *co, char *options) +{ + struct mxs_auart_port *s; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index == -1 || co->index >= ARRAY_SIZE(auart_port)) + co->index = 0; + s = auart_port[co->index]; + if (!s) + return -ENODEV; + + clk_enable(s->clk); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + auart_console_get_options(&s->port, &baud, &parity, &bits); + + ret = uart_set_options(&s->port, co, baud, parity, bits, flow); + + clk_disable(s->clk); + + return ret; +} + +static struct console auart_console = { + .name = "ttyAPP", + .write = auart_console_write, + .device = uart_console_device, + .setup = auart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &auart_driver, +}; +#endif + +static struct uart_driver auart_driver = { + .owner = THIS_MODULE, + .driver_name = "ttyAPP", + .dev_name = "ttyAPP", + .major = 0, + .minor = 0, + .nr = MXS_AUART_PORTS, +#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE + .cons = &auart_console, +#endif +}; + +static int __devinit mxs_auart_probe(struct platform_device *pdev) +{ + struct mxs_auart_port *s; + u32 version; + int ret = 0; + struct resource *r; + + s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL); + if (!s) { + ret = -ENOMEM; + goto out; + } + + s->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(s->clk)) { + ret = PTR_ERR(s->clk); + goto out_free; + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + ret = -ENXIO; + goto out_free_clk; + } + + s->port.mapbase = r->start; + s->port.membase = ioremap(r->start, resource_size(r)); + s->port.ops = &mxs_auart_ops; + s->port.iotype = UPIO_MEM; + s->port.line = pdev->id < 0 ? 0 : pdev->id; + s->port.fifosize = 16; + s->port.uartclk = clk_get_rate(s->clk); + s->port.type = PORT_IMX; + s->port.dev = s->dev = get_device(&pdev->dev); + + s->flags = 0; + s->ctrl = 0; + + s->irq = platform_get_irq(pdev, 0); + s->port.irq = s->irq; + ret = request_irq(s->irq, mxs_auart_irq_handle, 0, dev_name(&pdev->dev), s); + if (ret) + goto out_free_clk; + + platform_set_drvdata(pdev, s); + + auart_port[pdev->id] = s; + + mxs_auart_reset(&s->port); + + ret = uart_add_one_port(&auart_driver, &s->port); + if (ret) + goto out_free_irq; + + version = readl(s->port.membase + AUART_VERSION); + dev_info(&pdev->dev, "Found APPUART %d.%d.%d\n", + (version >> 24) & 0xff, + (version >> 16) & 0xff, version & 0xffff); + + return 0; + +out_free_irq: + auart_port[pdev->id] = NULL; + free_irq(s->irq, s); +out_free_clk: + clk_put(s->clk); +out_free: + kfree(s); +out: + return ret; +} + +static int __devexit mxs_auart_remove(struct platform_device *pdev) +{ + struct mxs_auart_port *s = platform_get_drvdata(pdev); + + uart_remove_one_port(&auart_driver, &s->port); + + auart_port[pdev->id] = NULL; + + clk_put(s->clk); + free_irq(s->irq, s); + kfree(s); + + return 0; +} + +static struct platform_driver mxs_auart_driver = { + .probe = mxs_auart_probe, + .remove = __devexit_p(mxs_auart_remove), + .driver = { + .name = "mxs-auart", + .owner = THIS_MODULE, + }, +}; + +static int __init mxs_auart_init(void) +{ + int r; + + r = uart_register_driver(&auart_driver); + if (r) + goto out; + + r = platform_driver_register(&mxs_auart_driver); + if (r) + goto out_err; + + return 0; +out_err: + uart_unregister_driver(&auart_driver); +out: + return r; +} + +static void __exit mxs_auart_exit(void) +{ + platform_driver_unregister(&mxs_auart_driver); + uart_unregister_driver(&auart_driver); +} + +module_init(mxs_auart_init); +module_exit(mxs_auart_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Freescale MXS application uart driver"); -- 1.7.2.3 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v3] Add i.MX23/28 auart support @ 2011-01-24 10:03 Sascha Hauer 2011-01-24 10:03 ` [PATCH 1/2] serial: Add auart driver for i.MX23/28 Sascha Hauer 0 siblings, 1 reply; 14+ messages in thread From: Sascha Hauer @ 2011-01-24 10:03 UTC (permalink / raw) To: linux-arm-kernel changes since v2: - Fix console support - For handling the CREAD flag just disable the receiver instead of ignoring the characters in the receive interrupt - Add timeout to loops in console support The following changes since commit 0c21e3aaf6ae85bee804a325aa29c325209180fd: Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/hch/hfsplus (2011-01-07 17:16:27 -0800) are available in the git repository at: nowhere ..BRANCH.NOT.VERIFIED.. Sascha Hauer (2): serial: Add auart driver for i.MX23/28 ARM MXS: Add auart platform support for i.MX28 arch/arm/mach-mxs/clock-mx28.c | 5 + arch/arm/mach-mxs/devices-mx28.h | 8 + arch/arm/mach-mxs/devices/Kconfig | 3 + arch/arm/mach-mxs/devices/Makefile | 1 + arch/arm/mach-mxs/devices/platform-auart.c | 54 ++ arch/arm/mach-mxs/include/mach/devices-common.h | 10 + drivers/serial/Kconfig | 15 + drivers/serial/Makefile | 1 + drivers/serial/mxs-auart.c | 799 +++++++++++++++++++++++ 9 files changed, 896 insertions(+), 0 deletions(-) create mode 100644 arch/arm/mach-mxs/devices/platform-auart.c create mode 100644 drivers/serial/mxs-auart.c ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 1/2] serial: Add auart driver for i.MX23/28 2011-01-24 10:03 [PATCH v3] Add i.MX23/28 auart support Sascha Hauer @ 2011-01-24 10:03 ` Sascha Hauer 0 siblings, 0 replies; 14+ messages in thread From: Sascha Hauer @ 2011-01-24 10:03 UTC (permalink / raw) To: linux-arm-kernel Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/serial/Kconfig | 15 + drivers/serial/Makefile | 1 + drivers/serial/mxs-auart.c | 799 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 815 insertions(+), 0 deletions(-) create mode 100644 drivers/serial/mxs-auart.c diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index ec3c214..6a2a39c 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1657,4 +1657,19 @@ config SERIAL_PCH_UART This driver is for PCH(Platform controller Hub) UART of Intel EG20T which is an IOH(Input/Output Hub) for x86 embedded processor. Enabling PCH_DMA, this PCH UART works as DMA mode. + +config SERIAL_MXS_AUART + depends on ARCH_MXS + tristate "MXS AUART support" + select SERIAL_CORE + help + This driver supports the MXS Application UART (AUART) port. + +config SERIAL_MXS_AUART_CONSOLE + bool "MXS AUART console support" + depends on SERIAL_MXS_AUART=y + select SERIAL_CORE_CONSOLE + help + Enable a MXS AUART port to be the system console. + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 8ea92e9..c855071 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -92,3 +92,4 @@ obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o +obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o diff --git a/drivers/serial/mxs-auart.c b/drivers/serial/mxs-auart.c new file mode 100644 index 0000000..6d01ac9 --- /dev/null +++ b/drivers/serial/mxs-auart.c @@ -0,0 +1,799 @@ +/* + * Freescale STMP37XX/STMP378X Application UART driver + * + * Author: dmitry pervushin <dimka@embeddedalley.com> + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include <asm/cacheflush.h> + +#define MXS_AUART_PORTS 5 + +#define AUART_CTRL0 0x00000000 +#define AUART_CTRL0_SET 0x00000004 +#define AUART_CTRL0_CLR 0x00000008 +#define AUART_CTRL0_TOG 0x0000000c +#define AUART_CTRL1 0x00000010 +#define AUART_CTRL1_SET 0x00000014 +#define AUART_CTRL1_CLR 0x00000018 +#define AUART_CTRL1_TOG 0x0000001c +#define AUART_CTRL2 0x00000020 +#define AUART_CTRL2_SET 0x00000024 +#define AUART_CTRL2_CLR 0x00000028 +#define AUART_CTRL2_TOG 0x0000002c +#define AUART_LINECTRL 0x00000030 +#define AUART_LINECTRL_SET 0x00000034 +#define AUART_LINECTRL_CLR 0x00000038 +#define AUART_LINECTRL_TOG 0x0000003c +#define AUART_LINECTRL2 0x00000040 +#define AUART_LINECTRL2_SET 0x00000044 +#define AUART_LINECTRL2_CLR 0x00000048 +#define AUART_LINECTRL2_TOG 0x0000004c +#define AUART_INTR 0x00000050 +#define AUART_INTR_SET 0x00000054 +#define AUART_INTR_CLR 0x00000058 +#define AUART_INTR_TOG 0x0000005c +#define AUART_DATA 0x00000060 +#define AUART_STAT 0x00000070 +#define AUART_DEBUG 0x00000080 +#define AUART_VERSION 0x00000090 +#define AUART_AUTOBAUD 0x000000a0 + +#define AUART_CTRL0_SFTRST (1 << 31) +#define AUART_CTRL0_CLKGATE (1 << 30) + +#define AUART_CTRL2_CTSEN (1 << 15) +#define AUART_CTRL2_RTS (1 << 11) +#define AUART_CTRL2_RXE (1 << 9) +#define AUART_CTRL2_TXE (1 << 8) +#define AUART_CTRL2_UARTEN (1 << 0) + +#define AUART_LINECTRL_BAUD_DIVINT_SHIFT 16 +#define AUART_LINECTRL_BAUD_DIVINT_MASK 0xffff0000 +#define AUART_LINECTRL_BAUD_DIVINT(v) (((v) & 0xffff) << 16) +#define AUART_LINECTRL_BAUD_DIVFRAC_SHIFT 8 +#define AUART_LINECTRL_BAUD_DIVFRAC_MASK 0x00003f00 +#define AUART_LINECTRL_BAUD_DIVFRAC(v) (((v) & 0x3f) << 8) +#define AUART_LINECTRL_WLEN_MASK 0x00000060 +#define AUART_LINECTRL_WLEN(v) (((v) & 0x3) << 5) +#define AUART_LINECTRL_FEN (1 << 4) +#define AUART_LINECTRL_STP2 (1 << 3) +#define AUART_LINECTRL_EPS (1 << 2) +#define AUART_LINECTRL_PEN (1 << 1) +#define AUART_LINECTRL_BRK (1 << 0) + +#define AUART_INTR_RTIEN (1 << 22) +#define AUART_INTR_TXIEN (1 << 21) +#define AUART_INTR_RXIEN (1 << 20) +#define AUART_INTR_CTSMIEN (1 << 17) +#define AUART_INTR_RTIS (1 << 6) +#define AUART_INTR_TXIS (1 << 5) +#define AUART_INTR_RXIS (1 << 4) +#define AUART_INTR_CTSMIS (1 << 1) + +#define AUART_STAT_BUSY (1 << 29) +#define AUART_STAT_CTS (1 << 28) +#define AUART_STAT_TXFE (1 << 27) +#define AUART_STAT_TXFF (1 << 25) +#define AUART_STAT_RXFE (1 << 24) +#define AUART_STAT_OERR (1 << 19) +#define AUART_STAT_BERR (1 << 18) +#define AUART_STAT_PERR (1 << 17) +#define AUART_STAT_FERR (1 << 16) + +static struct uart_driver auart_driver; + +struct mxs_auart_port { + struct uart_port port; + + unsigned int flags; + unsigned int ctrl; + + unsigned int irq; + + struct clk *clk; + struct device *dev; +}; + +static void mxs_auart_stop_tx(struct uart_port *u); + +#define to_auart_port(u) container_of(u, struct mxs_auart_port, port) + +static inline void mxs_auart_tx_chars(struct mxs_auart_port *s) +{ + struct circ_buf *xmit = &s->port.state->xmit; + + while (!(readl(s->port.membase + AUART_STAT) & + AUART_STAT_TXFF)) { + if (s->port.x_char) { + s->port.icount.tx++; + writel(s->port.x_char, + s->port.membase + AUART_DATA); + s->port.x_char = 0; + continue; + } + if (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) { + s->port.icount.tx++; + writel(xmit->buf[xmit->tail], + s->port.membase + AUART_DATA); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); + } else + break; + } + if (uart_circ_empty(&(s->port.state->xmit))) + writel(AUART_INTR_TXIEN, + s->port.membase + AUART_INTR_CLR); + else + writel(AUART_INTR_TXIEN, + s->port.membase + AUART_INTR_SET); + + if (uart_tx_stopped(&s->port)) + mxs_auart_stop_tx(&s->port); +} + +static void mxs_auart_rx_char(struct mxs_auart_port *s) +{ + int flag; + u32 stat; + u8 c; + + c = readl(s->port.membase + AUART_DATA); + stat = readl(s->port.membase + AUART_STAT); + + flag = TTY_NORMAL; + s->port.icount.rx++; + + if (stat & AUART_STAT_BERR) { + s->port.icount.brk++; + if (uart_handle_break(&s->port)) + goto out; + } else if (stat & AUART_STAT_PERR) { + s->port.icount.parity++; + } else if (stat & AUART_STAT_FERR) { + s->port.icount.frame++; + } + + /* + * Mask off conditions which should be ingored. + */ + stat &= s->port.read_status_mask; + + if (stat & AUART_STAT_BERR) { + flag = TTY_BREAK; + } else if (stat & AUART_STAT_PERR) + flag = TTY_PARITY; + else if (stat & AUART_STAT_FERR) + flag = TTY_FRAME; + + if (stat & AUART_STAT_OERR) + s->port.icount.overrun++; + + if (uart_handle_sysrq_char(&s->port, c)) + goto out; + + uart_insert_char(&s->port, stat, AUART_STAT_OERR, c, flag); +out: + writel(stat, s->port.membase + AUART_STAT); +} + +static void mxs_auart_rx_chars(struct mxs_auart_port *s) +{ + struct tty_struct *tty = s->port.state->port.tty; + u32 stat = 0; + + for (;;) { + stat = readl(s->port.membase + AUART_STAT); + if (stat & AUART_STAT_RXFE) + break; + mxs_auart_rx_char(s); + } + + writel(stat, s->port.membase + AUART_STAT); + tty_flip_buffer_push(tty); +} + +static int mxs_auart_request_port(struct uart_port *u) +{ + return 0; +} + +static int mxs_auart_verify_port(struct uart_port *u, + struct serial_struct *ser) +{ + if (u->type != PORT_UNKNOWN && u->type != PORT_IMX) + return -EINVAL; + return 0; +} + +static void mxs_auart_config_port(struct uart_port *u, int flags) +{ +} + +static const char *mxs_auart_type(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + return dev_name(s->dev); +} + +static void mxs_auart_release_port(struct uart_port *u) +{ +} + +static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) +{ + struct mxs_auart_port *s = to_auart_port(u); + + u32 ctrl = readl(u->membase + AUART_CTRL2); + + ctrl &= ~AUART_CTRL2_RTS; + if (mctrl & TIOCM_RTS) + ctrl |= AUART_CTRL2_RTS; + s->ctrl = mctrl; + writel(ctrl, u->membase + AUART_CTRL2); +} + +static u32 mxs_auart_get_mctrl(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + u32 stat = readl(u->membase + AUART_STAT); + int ctrl2 = readl(u->membase + AUART_CTRL2); + u32 mctrl = s->ctrl; + + mctrl &= ~TIOCM_CTS; + if (stat & AUART_STAT_CTS) + mctrl |= TIOCM_CTS; + + if (ctrl2 & AUART_CTRL2_RTS) + mctrl |= TIOCM_RTS; + + return mctrl; +} + +static void mxs_auart_settermios(struct uart_port *u, + struct ktermios *termios, + struct ktermios *old) +{ + u32 bm, ctrl, ctrl2, div; + unsigned int cflag, baud; + + cflag = termios->c_cflag; + + ctrl = AUART_LINECTRL_FEN; + ctrl2 = readl(u->membase + AUART_CTRL2); + + /* byte size */ + switch (cflag & CSIZE) { + case CS5: + bm = 0; + break; + case CS6: + bm = 1; + break; + case CS7: + bm = 2; + break; + case CS8: + bm = 3; + break; + default: + return; + } + + ctrl |= AUART_LINECTRL_WLEN(bm); + + /* parity */ + if (cflag & PARENB) { + ctrl |= AUART_LINECTRL_PEN; + if ((cflag & PARODD) == 0) + ctrl |= AUART_LINECTRL_EPS; + } + + u->read_status_mask = 0; + + if (termios->c_iflag & INPCK) + u->read_status_mask |= AUART_STAT_PERR; + if (termios->c_iflag & (BRKINT | PARMRK)) + u->read_status_mask |= AUART_STAT_BERR; + + /* + * Characters to ignore + */ + u->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + u->ignore_status_mask |= AUART_STAT_PERR; + if (termios->c_iflag & IGNBRK) { + u->ignore_status_mask |= AUART_STAT_BERR; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + u->ignore_status_mask |= AUART_STAT_OERR; + } + + /* + * ignore all characters if CREAD is not set + */ + if (cflag & CREAD) + ctrl2 |= AUART_CTRL2_RXE; + else + ctrl2 &= ~AUART_CTRL2_RXE; + + /* figure out the stop bits requested */ + if (cflag & CSTOPB) + ctrl |= AUART_LINECTRL_STP2; + + /* figure out the hardware flow control settings */ + if (cflag & CRTSCTS) + ctrl2 |= AUART_CTRL2_CTSEN; + else + ctrl2 &= ~AUART_CTRL2_CTSEN; + + /* set baud rate */ + baud = uart_get_baud_rate(u, termios, old, 0, u->uartclk); + div = u->uartclk * 32 / baud; + ctrl |= AUART_LINECTRL_BAUD_DIVFRAC(div & 0x3F); + ctrl |= AUART_LINECTRL_BAUD_DIVINT(div >> 6); + + writel(ctrl, u->membase + AUART_LINECTRL); + writel(ctrl2, u->membase + AUART_CTRL2); +} + +static irqreturn_t mxs_auart_irq_handle(int irq, void *context) +{ + u32 istatus, istat; + struct mxs_auart_port *s = context; + u32 stat = readl(s->port.membase + AUART_STAT); + + istatus = istat = readl(s->port.membase + AUART_INTR); + + if (istat & AUART_INTR_CTSMIS) { + uart_handle_cts_change(&s->port, stat & AUART_STAT_CTS); + writel(AUART_INTR_CTSMIS, + s->port.membase + AUART_INTR_CLR); + istat &= ~AUART_INTR_CTSMIS; + } + + if (istat & (AUART_INTR_RTIS | AUART_INTR_RXIS)) { + mxs_auart_rx_chars(s); + istat &= ~(AUART_INTR_RTIS | AUART_INTR_RXIS); + } + + if (istat & AUART_INTR_TXIS) { + mxs_auart_tx_chars(s); + istat &= ~AUART_INTR_TXIS; + } + + writel(istatus & (AUART_INTR_RTIS + | AUART_INTR_TXIS + | AUART_INTR_RXIS + | AUART_INTR_CTSMIS), + s->port.membase + AUART_INTR_CLR); + + return IRQ_HANDLED; +} + +static void mxs_auart_reset(struct uart_port *u) +{ + int i; + unsigned int reg; + + writel(AUART_CTRL0_SFTRST, u->membase + AUART_CTRL0_CLR); + + for (i = 0; i < 10000; i++) { + reg = readl(u->membase + AUART_CTRL0); + if (!(reg & AUART_CTRL0_SFTRST)) + break; + udelay(3); + } + writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_CLR); +} + +static int mxs_auart_startup(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + clk_enable(s->clk); + + writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_CLR); + + writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_SET); + + writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN, + u->membase + AUART_INTR); + + /* + * Enable fifo so all four bytes of a DMA word are written to + * output (otherwise, only the LSB is written, ie. 1 in 4 bytes) + */ + writel(AUART_LINECTRL_FEN, u->membase + AUART_LINECTRL_SET); + + return 0; +} + +static void mxs_auart_shutdown(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_CLR); + + writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_SET); + + writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN, + u->membase + AUART_INTR_CLR); + + clk_disable(s->clk); +} + +static unsigned int mxs_auart_tx_empty(struct uart_port *u) +{ + if (readl(u->membase + AUART_STAT) & AUART_STAT_TXFE) + return TIOCSER_TEMT; + else + return 0; +} + +static void mxs_auart_start_tx(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + /* enable transmitter */ + writel(AUART_CTRL2_TXE, u->membase + AUART_CTRL2_SET); + + mxs_auart_tx_chars(s); +} + +static void mxs_auart_stop_tx(struct uart_port *u) +{ + writel(AUART_CTRL2_TXE, u->membase + AUART_CTRL2_CLR); +} + +static void mxs_auart_stop_rx(struct uart_port *u) +{ + writel(AUART_CTRL2_RXE, u->membase + AUART_CTRL2_CLR); +} + +static void mxs_auart_break_ctl(struct uart_port *u, int ctl) +{ + if (ctl) + writel(AUART_LINECTRL_BRK, + u->membase + AUART_LINECTRL_SET); + else + writel(AUART_LINECTRL_BRK, + u->membase + AUART_LINECTRL_CLR); +} + +static void mxs_auart_enable_ms(struct uart_port *port) +{ + /* just empty */ +} + +static struct uart_ops mxs_auart_ops = { + .tx_empty = mxs_auart_tx_empty, + .start_tx = mxs_auart_start_tx, + .stop_tx = mxs_auart_stop_tx, + .stop_rx = mxs_auart_stop_rx, + .enable_ms = mxs_auart_enable_ms, + .break_ctl = mxs_auart_break_ctl, + .set_mctrl = mxs_auart_set_mctrl, + .get_mctrl = mxs_auart_get_mctrl, + .startup = mxs_auart_startup, + .shutdown = mxs_auart_shutdown, + .set_termios = mxs_auart_settermios, + .type = mxs_auart_type, + .release_port = mxs_auart_release_port, + .request_port = mxs_auart_request_port, + .config_port = mxs_auart_config_port, + .verify_port = mxs_auart_verify_port, +}; + +static struct mxs_auart_port *auart_port[MXS_AUART_PORTS]; + +#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE +static void mxs_auart_console_putchar(struct uart_port *port, int ch) +{ + unsigned int to = 1000; + + while (readl(port->membase + AUART_STAT) & AUART_STAT_TXFF) { + if (!to--) + break; + udelay(1); + } + + writel(ch, port->membase + AUART_DATA); +} + +static void +auart_console_write(struct console *co, const char *str, unsigned int count) +{ + struct mxs_auart_port *s; + struct uart_port *port; + unsigned int old_ctrl0, old_ctrl2; + unsigned int to = 1000; + + if (co->index > MXS_AUART_PORTS || co->index < 0) + return; + + s = auart_port[co->index]; + port = &s->port; + + clk_enable(s->clk); + + /* First save the CR then disable the interrupts */ + old_ctrl2 = readl(port->membase + AUART_CTRL2); + old_ctrl0 = readl(port->membase + AUART_CTRL0); + + writel(AUART_CTRL0_CLKGATE, + port->membase + AUART_CTRL0_CLR); + writel(AUART_CTRL2_UARTEN | AUART_CTRL2_TXE, + port->membase + AUART_CTRL2_SET); + + uart_console_write(port, str, count, mxs_auart_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the TCR + */ + while (readl(port->membase + AUART_STAT) & AUART_STAT_BUSY) { + if (!to--) + break; + udelay(1); + } + + writel(old_ctrl0, port->membase + AUART_CTRL0); + writel(old_ctrl2, port->membase + AUART_CTRL2); + + clk_disable(s->clk); +} + +static void __init +auart_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + unsigned int lcr_h, quot; + + if (!(readl(port->membase + AUART_CTRL2) & AUART_CTRL2_UARTEN)) + return; + + lcr_h = readl(port->membase + AUART_LINECTRL); + + *parity = 'n'; + if (lcr_h & AUART_LINECTRL_PEN) { + if (lcr_h & AUART_LINECTRL_EPS) + *parity = 'e'; + else + *parity = 'o'; + } + + if ((lcr_h & AUART_LINECTRL_WLEN_MASK) == AUART_LINECTRL_WLEN(2)) + *bits = 7; + else + *bits = 8; + + quot = ((readl(port->membase + AUART_LINECTRL) + & AUART_LINECTRL_BAUD_DIVINT_MASK)) + >> (AUART_LINECTRL_BAUD_DIVINT_SHIFT - 6); + quot |= ((readl(port->membase + AUART_LINECTRL) + & AUART_LINECTRL_BAUD_DIVFRAC_MASK)) + >> AUART_LINECTRL_BAUD_DIVFRAC_SHIFT; + if (quot == 0) + quot = 1; + + *baud = (port->uartclk << 2) / quot; +} + +static int __init +auart_console_setup(struct console *co, char *options) +{ + struct mxs_auart_port *s; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index == -1 || co->index >= ARRAY_SIZE(auart_port)) + co->index = 0; + s = auart_port[co->index]; + if (!s) + return -ENODEV; + + clk_enable(s->clk); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + auart_console_get_options(&s->port, &baud, &parity, &bits); + + ret = uart_set_options(&s->port, co, baud, parity, bits, flow); + + clk_disable(s->clk); + + return ret; +} + +static struct console auart_console = { + .name = "ttyAPP", + .write = auart_console_write, + .device = uart_console_device, + .setup = auart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &auart_driver, +}; +#endif + +static struct uart_driver auart_driver = { + .owner = THIS_MODULE, + .driver_name = "ttyAPP", + .dev_name = "ttyAPP", + .major = 0, + .minor = 0, + .nr = MXS_AUART_PORTS, +#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE + .cons = &auart_console, +#endif +}; + +static int __devinit mxs_auart_probe(struct platform_device *pdev) +{ + struct mxs_auart_port *s; + u32 version; + int ret = 0; + struct resource *r; + + s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL); + if (!s) { + ret = -ENOMEM; + goto out; + } + + s->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(s->clk)) { + ret = PTR_ERR(s->clk); + goto out_free; + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + ret = -ENXIO; + goto out_free_clk; + } + + s->port.mapbase = r->start; + s->port.membase = ioremap(r->start, resource_size(r)); + s->port.ops = &mxs_auart_ops; + s->port.iotype = UPIO_MEM; + s->port.line = pdev->id < 0 ? 0 : pdev->id; + s->port.fifosize = 16; + s->port.uartclk = clk_get_rate(s->clk); + s->port.type = PORT_IMX; + s->port.dev = s->dev = get_device(&pdev->dev); + + s->flags = 0; + s->ctrl = 0; + + s->irq = platform_get_irq(pdev, 0); + s->port.irq = s->irq; + ret = request_irq(s->irq, mxs_auart_irq_handle, 0, dev_name(&pdev->dev), s); + if (ret) + goto out_free_clk; + + platform_set_drvdata(pdev, s); + + auart_port[pdev->id] = s; + + mxs_auart_reset(&s->port); + + ret = uart_add_one_port(&auart_driver, &s->port); + if (ret) + goto out_free_irq; + + version = readl(s->port.membase + AUART_VERSION); + dev_info(&pdev->dev, "Found APPUART %d.%d.%d\n", + (version >> 24) & 0xff, + (version >> 16) & 0xff, version & 0xffff); + + return 0; + +out_free_irq: + auart_port[pdev->id] = NULL; + free_irq(s->irq, s); +out_free_clk: + clk_put(s->clk); +out_free: + kfree(s); +out: + return ret; +} + +static int __devexit mxs_auart_remove(struct platform_device *pdev) +{ + struct mxs_auart_port *s = platform_get_drvdata(pdev); + + uart_remove_one_port(&auart_driver, &s->port); + + auart_port[pdev->id] = NULL; + + clk_put(s->clk); + free_irq(s->irq, s); + kfree(s); + + return 0; +} + +static struct platform_driver mxs_auart_driver = { + .probe = mxs_auart_probe, + .remove = __devexit_p(mxs_auart_remove), + .driver = { + .name = "mxs-auart", + .owner = THIS_MODULE, + }, +}; + +static int __init mxs_auart_init(void) +{ + int r; + + r = uart_register_driver(&auart_driver); + if (r) + goto out; + + r = platform_driver_register(&mxs_auart_driver); + if (r) + goto out_err; + + return 0; +out_err: + uart_unregister_driver(&auart_driver); +out: + return r; +} + +static void __exit mxs_auart_exit(void) +{ + platform_driver_unregister(&mxs_auart_driver); + uart_unregister_driver(&auart_driver); +} + +module_init(mxs_auart_init); +module_exit(mxs_auart_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Freescale MXS application uart driver"); -- 1.7.2.3 ^ permalink raw reply related [flat|nested] 14+ messages in thread
end of thread, other threads:[~2011-01-24 10:03 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2011-01-11 15:09 Add i.MX23/28 auart support Sascha Hauer 2011-01-11 15:09 ` [PATCH 1/2] serial: Add auart driver for i.MX23/28 Sascha Hauer 2011-01-11 16:05 ` Russell King - ARM Linux 2011-01-11 17:10 ` Wolfram Sang 2011-01-11 17:57 ` Sascha Hauer 2011-01-12 5:39 ` Shawn Guo 2011-01-11 15:09 ` [PATCH 2/2] ARM i.MXS: Add auart platform support for i.MX28 Sascha Hauer 2011-01-11 15:17 ` Uwe Kleine-König 2011-01-11 15:26 ` Sascha Hauer 2011-01-12 5:06 ` Shawn Guo 2011-01-12 6:17 ` Shawn Guo 2011-01-12 5:31 ` Add i.MX23/28 auart support Shawn Guo -- strict thread matches above, loose matches on Subject: below -- 2011-01-13 13:14 [PATCH v2] " Sascha Hauer 2011-01-13 13:14 ` [PATCH 1/2] serial: Add auart driver for i.MX23/28 Sascha Hauer 2011-01-24 10:03 [PATCH v3] Add i.MX23/28 auart support Sascha Hauer 2011-01-24 10:03 ` [PATCH 1/2] serial: Add auart driver for i.MX23/28 Sascha Hauer
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).