linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] serial/efm32: add new driver
@ 2011-12-21 15:05 Uwe Kleine-König
  2011-12-21 20:28 ` Alan Cox
  0 siblings, 1 reply; 17+ messages in thread
From: Uwe Kleine-König @ 2011-12-21 15:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: devicetree-discuss, Greg Kroah-Hartman, Alan Cox, linux-serial, kernel

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
Hello,

note that ARCH_EFM32 isn't in mainline yet, so to be actually usable
some arch patches are needed.

Best regards
Uwe

 .../devicetree/bindings/tty/serial/efm32-usart.txt |   14 +
 drivers/tty/serial/Kconfig                         |   10 +
 drivers/tty/serial/Makefile                        |    1 +
 drivers/tty/serial/efm32-usart.c                   |  714 ++++++++++++++++++++
 include/linux/serial_core.h                        |    2 +
 5 files changed, 741 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/tty/serial/efm32-usart.txt
 create mode 100644 drivers/tty/serial/efm32-usart.c

diff --git a/Documentation/devicetree/bindings/tty/serial/efm32-usart.txt b/Documentation/devicetree/bindings/tty/serial/efm32-usart.txt
new file mode 100644
index 0000000..eef2721
--- /dev/null
+++ b/Documentation/devicetree/bindings/tty/serial/efm32-usart.txt
@@ -0,0 +1,14 @@
+* Energymicro efm32 UART
+
+Required properties:
+- compatible : Should be "efm32,usart"
+- reg : Address and length of the register set
+- interrupts : Should contain uart interrupt
+
+Example:
+
+uart@0x4000c400 {
+	compatible = "efm32,usart";
+	reg = <0x4000c400 0x400>;
+	interrupts = <15>;
+};
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 925a1e5..cfeb0f3 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1610,4 +1610,14 @@ config SERIAL_XILINX_PS_UART_CONSOLE
 	help
 	  Enable a Xilinx PS UART port to be the system console.
 
+config SERIAL_EFM32_USART
+	bool "EFM32 USART port."
+	depends on ARCH_EFM32
+	select SERIAL_CORE
+
+config SERIAL_EFM32_USART_CONSOLE
+	bool "EFM32 USART console support"
+	depends on SERIAL_EFM32_USART=y
+	select SERIAL_CORE_CONSOLE
+
 endmenu
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index e10cf5b..d3ab42b 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -94,3 +94,4 @@ obj-$(CONFIG_SERIAL_MSM_SMD)	+= msm_smd_tty.o
 obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o
 obj-$(CONFIG_SERIAL_LANTIQ)	+= lantiq.o
 obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o
+obj-$(CONFIG_SERIAL_EFM32_USART) += efm32-usart.o
diff --git a/drivers/tty/serial/efm32-usart.c b/drivers/tty/serial/efm32-usart.c
new file mode 100644
index 0000000..4d0b636
--- /dev/null
+++ b/drivers/tty/serial/efm32-usart.c
@@ -0,0 +1,714 @@
+#include <linux/kernel.h>
+#include <linux/serial_core.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/console.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#define DRIVER_NAME "efm32-usart"
+#define DEV_NAME "ttyefm"
+
+#define USARTn_CTRL		0x00
+#define USARTn_CTRL_SYNC		0x0001
+
+#define USARTn_FRAME		0x04
+#define USARTn_FRAME_DATABITS(n)	((n) - 3)
+#define USARTn_FRAME_PARITY_NONE	0x0000
+#define USARTn_FRAME_STOPBITS_HALF	0x0000
+#define USARTn_FRAME_STOPBITS_ONE	0x1000
+#define USARTn_FRAME_STOPBITS_TWO	0x3000
+
+#define USARTn_CMD		0x0c
+#define USARTn_CMD_RXEN			0x0001
+#define USARTn_CMD_RXDIS		0x0002
+#define USARTn_CMD_TXEN			0x0004
+#define USARTn_CMD_TXDIS		0x0008
+
+#define USARTn_STATUS		0x10
+#define USARTn_STATUS_TXENS		0x0002
+#define USARTn_STATUS_TXC		0x0020
+#define USARTn_STATUS_TXBL		0x0040
+#define USARTn_STATUS_RXDATAV		0x0080
+
+#define USARTn_CLKDIV		0x14
+
+#define USARTn_RXDATAX		0x18
+#define USARTn_RXDATAX_PERR		0x4000
+#define USARTn_RXDATAX_FERR		0x8000
+
+#define USARTn_TXDATA		0x34
+
+#define USARTn_IF		0x40
+#define USARTn_IF_TXBL			0x0002
+#define USARTn_IF_RXDATAV		0x0004
+
+#define USARTn_IFS		0x44
+#define USARTn_IFC		0x48
+#define USARTn_IEN		0x4c
+
+#define USARTn_ROUTE		0x54
+#define USARTn_ROUTE_RXPEN		0x0001
+#define USARTn_ROUTE_TXPEN		0x0002
+
+struct efm32_usart_port {
+	struct uart_port port;
+	unsigned int txirq;
+	struct clk *clk;
+};
+#define to_efm_port(_port) container_of(_port, struct efm32_usart_port, port)
+#define efm_debug(efm_port, format, arg...)			\
+	dev_dbg(efm_port->port.dev, format, ##arg)
+
+static void efm32_usart_write32(struct efm32_usart_port *efm_port,
+		u32 value, unsigned offset)
+{
+	__raw_writel(value, efm_port->port.membase + offset);
+}
+
+static u32 efm32_usart_read32(struct efm32_usart_port *efm_port,
+		unsigned offset)
+{
+	return __raw_readl(efm_port->port.membase + offset);
+}
+
+static unsigned int efm32_usart_tx_empty(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+	u32 status = efm32_usart_read32(efm_port, USARTn_STATUS);
+
+	/* XXX: does TXBL also mean that the shifter is done? */
+	if (status & USARTn_STATUS_TXBL)
+		return TIOCSER_TEMT;
+	else
+		return 0;
+}
+
+static void efm32_usart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	/* sorry, neither handshaking lines nor loop functionallity */
+}
+
+static unsigned int efm32_usart_get_mctrl(struct uart_port *port)
+{
+	/* sorry, no handshaking lines available */
+	return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
+}
+
+static void efm32_usart_stop_tx(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+	u32 ien = efm32_usart_read32(efm_port,  USARTn_IEN);
+
+	//efm32_usart_write32(efm_port, USARTn_CMD_TXDIS, USARTn_CMD);
+	efm32_usart_write32(efm_port, ien & ~USARTn_IF_TXBL, USARTn_IEN);
+}
+
+static void efm32_usart_tx_chars(struct efm32_usart_port *efm_port)
+{
+	struct uart_port *port = &efm_port->port;
+	struct circ_buf *xmit = &port->state->xmit;
+
+	while (efm32_usart_read32(efm_port, USARTn_STATUS) &
+			USARTn_STATUS_TXBL) {
+		if (port->x_char) {
+			port->icount.tx++;
+			efm32_usart_write32(efm_port, port->x_char,
+					USARTn_TXDATA);
+			port->x_char = 0;
+			continue;
+		}
+		if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) {
+			port->icount.tx++;
+			efm32_usart_write32(efm_port, xmit->buf[xmit->tail],
+					USARTn_TXDATA);
+			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		} else
+			break;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (!port->x_char && uart_circ_empty(xmit))
+		efm32_usart_stop_tx(port);
+}
+
+static void efm32_usart_start_tx(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+	u32 ien;
+
+	efm32_usart_write32(efm_port, USARTn_IF_TXBL, USARTn_IFC);
+	ien = efm32_usart_read32(efm_port,  USARTn_IEN);
+	efm32_usart_write32(efm_port, ien | USARTn_IF_TXBL, USARTn_IEN);
+	efm32_usart_write32(efm_port, USARTn_CMD_TXEN, USARTn_CMD);
+
+	efm32_usart_tx_chars(efm_port);
+}
+
+static void efm32_usart_stop_rx(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+
+	efm32_usart_write32(efm_port, USARTn_CMD_RXDIS, USARTn_CMD);
+}
+
+static void efm32_usart_enable_ms(struct uart_port *port)
+{
+	/* no handshake lines, no modem status interrupts */
+}
+
+static void efm32_usart_break_ctl(struct uart_port *port, int ctl)
+{
+	/* not possible without fiddling with gpios */
+}
+
+static void efm32_usart_rx_chars(struct efm32_usart_port *efm_port)
+{
+	struct uart_port *port = &efm_port->port;
+	struct tty_struct *tty = port->state->port.tty;
+
+	spin_lock(&port->lock);
+
+	while (efm32_usart_read32(efm_port, USARTn_STATUS) &
+			USARTn_STATUS_RXDATAV) {
+		u32 rxdata = efm32_usart_read32(efm_port, USARTn_RXDATAX);
+		int flag = 0;
+
+		port->icount.rx++;
+
+		/* XXX detect BREAK and overrun */
+
+		if (rxdata & USARTn_RXDATAX_PERR) {
+			port->icount.parity++;
+			if (port->read_status_mask & USARTn_RXDATAX_PERR)
+				flag = TTY_PARITY;
+		} else if (rxdata & USARTn_RXDATAX_FERR) {
+			port->icount.frame++;
+			if (port->read_status_mask & USARTn_RXDATAX_FERR)
+				flag = TTY_FRAME;
+		}
+
+		if (rxdata & port->ignore_status_mask)
+			continue;
+
+		tty_insert_flip_char(tty, rxdata, flag);
+	}
+	spin_unlock(&port->lock);
+
+	tty_flip_buffer_push(tty);
+}
+
+static irqreturn_t efm32_usart_rxirq(int irq, void *data)
+{
+	struct efm32_usart_port *efm_port = data;
+	u32 irqflag = efm32_usart_read32(efm_port, USARTn_IF);
+
+	if (irqflag & USARTn_IF_RXDATAV) {
+		efm32_usart_write32(efm_port, USARTn_IF_RXDATAV, USARTn_IFC);
+		efm32_usart_rx_chars(efm_port);
+
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+static irqreturn_t efm32_usart_txirq(int irq, void *data)
+{
+	struct efm32_usart_port *efm_port = data;
+	u32 irqflag = efm32_usart_read32(efm_port, USARTn_IF);
+
+	if (irqflag & USARTn_IF_TXBL) {
+		efm32_usart_write32(efm_port, USARTn_IF_TXBL, USARTn_IFC);
+		efm32_usart_tx_chars(efm_port);
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+static int efm32_usart_startup(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+	int ret;
+
+	ret = clk_enable(efm_port->clk);
+	if (ret) {
+		efm_debug(efm_port, "failed to enable clk\n");
+		goto err_clk_enable;
+	}
+	port->uartclk = clk_get_rate(efm_port->clk);
+
+	/* Enable pins at default location */
+	efm32_usart_write32(efm_port, USARTn_ROUTE_RXPEN | USARTn_ROUTE_TXPEN,
+			USARTn_ROUTE);
+
+	ret = request_irq(port->irq, efm32_usart_rxirq, 0,
+			DRIVER_NAME, efm_port);
+	if (ret) {
+		efm_debug(efm_port, "failed to register rxirq\n");
+		goto err_request_irq_rx;
+	}
+
+	/* disable all irqs */
+	efm32_usart_write32(efm_port, 0, USARTn_IEN);
+
+	ret = request_irq(efm_port->txirq, efm32_usart_txirq, 0,
+			DRIVER_NAME, efm_port);
+	if (ret) {
+		efm_debug(efm_port, "failed to register txirq\n");
+		free_irq(port->irq, efm_port);
+err_request_irq_rx:
+
+		clk_disable(efm_port->clk);
+	} else {
+		efm32_usart_write32(efm_port, USARTn_IF_RXDATAV, USARTn_IEN);
+		efm32_usart_write32(efm_port, USARTn_CMD_RXEN, USARTn_CMD);
+	}
+
+err_clk_enable:
+	return ret;
+}
+
+static void efm32_usart_shutdown(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+
+	efm32_usart_write32(efm_port, 0, USARTn_IEN);
+	free_irq(port->irq, efm_port);
+
+	clk_disable(efm_port->clk);
+}
+
+static void efm32_usart_set_termios(struct uart_port *port,
+		struct ktermios *new, struct ktermios *old)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+	unsigned long flags;
+	unsigned baud;
+	u32 clkdiv;
+
+	/* no modem control lines */
+	new->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);
+
+	/* currently only some features are implemented */
+	new->c_cflag &= ~CSIZE;
+	new->c_cflag |= CS8;
+	new->c_cflag |= CSTOPB;
+	new->c_cflag &= ~PARENB;
+	new->c_iflag = 0;
+
+	baud = uart_get_baud_rate(port, new, old,
+			DIV_ROUND_CLOSEST(port->uartclk, 16 * 8192),
+			DIV_ROUND_CLOSEST(port->uartclk, 16));
+
+	/*
+	 * the 6 lowest bits of CLKDIV are dc, bit 6 has value 0.25.
+	 * port->uartclk <= 14e6, so 4 * port->uartclk doesn't overflow.
+	 */
+	clkdiv = (DIV_ROUND_CLOSEST(4 * port->uartclk, 16 * baud) - 4) << 6;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	efm32_usart_write32(efm_port,
+			USARTn_CMD_TXDIS | USARTn_CMD_RXDIS, USARTn_CMD);
+
+	port->read_status_mask = 0;
+	port->ignore_status_mask = 0;
+
+	uart_update_timeout(port, new->c_cflag, baud);
+
+	efm32_usart_write32(efm_port, 0, USARTn_CTRL);
+	efm32_usart_write32(efm_port, USARTn_FRAME_STOPBITS_ONE |
+			USARTn_FRAME_PARITY_NONE | USARTn_FRAME_DATABITS(8),
+			USARTn_FRAME);
+	efm32_usart_write32(efm_port, clkdiv, USARTn_CLKDIV);
+
+	efm32_usart_write32(efm_port, USARTn_CMD_TXEN | USARTn_CMD_RXEN, USARTn_CMD);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *efm32_usart_type(struct uart_port *port)
+{
+	return port->type == PORT_EFMUSART ? "efm32-usart" : NULL;
+}
+
+static void efm32_usart_release_port(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+
+	clk_unprepare(efm_port->clk);
+	clk_put(efm_port->clk);
+	iounmap(port->membase);
+}
+
+static int efm32_usart_request_port(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+	int ret;
+
+	port->membase = ioremap(port->mapbase, 60);
+	if (!efm_port->port.membase) {
+		ret = -ENOMEM;
+		efm_debug(efm_port, "failed to remap\n");
+		goto err_ioremap;
+	}
+
+	efm_port->clk = clk_get(port->dev, NULL);
+	if (IS_ERR(efm_port->clk)) {
+		ret = PTR_ERR(efm_port->clk);
+		efm_debug(efm_port, "failed to get clock\n");
+		goto err_clk_get;
+	}
+
+	ret = clk_prepare(efm_port->clk);
+	if (ret) {
+		clk_put(efm_port->clk);
+err_clk_get:
+
+		iounmap(port->membase);
+err_ioremap:
+		return ret;
+	}
+	return 0;
+}
+
+static void efm32_usart_config_port(struct uart_port *port, int type)
+{
+	if (type & UART_CONFIG_TYPE &&
+			!efm32_usart_request_port(port))
+		port->type = PORT_EFMUSART;
+}
+
+static int efm32_usart_verify_port(struct uart_port *port,
+		struct serial_struct *serinfo)
+{
+	int ret = 0;
+
+	if (serinfo->type != PORT_UNKNOWN && serinfo->type != PORT_EFMUSART)
+		ret = -EINVAL;
+
+	return ret;
+}
+
+static struct uart_ops efm32_usart_pops = {
+	.tx_empty = efm32_usart_tx_empty,
+	.set_mctrl = efm32_usart_set_mctrl,
+	.get_mctrl = efm32_usart_get_mctrl,
+	.stop_tx = efm32_usart_stop_tx,
+	.start_tx = efm32_usart_start_tx,
+	.stop_rx = efm32_usart_stop_rx,
+	.enable_ms = efm32_usart_enable_ms,
+	.break_ctl = efm32_usart_break_ctl,
+	.startup = efm32_usart_startup,
+	.shutdown = efm32_usart_shutdown,
+	.set_termios = efm32_usart_set_termios,
+	.type = efm32_usart_type,
+	.release_port = efm32_usart_release_port,
+	.request_port = efm32_usart_request_port,
+	.config_port = efm32_usart_config_port,
+	.verify_port = efm32_usart_verify_port,
+};
+
+static struct efm32_usart_port *efm32_usart_ports[3];
+
+#ifdef CONFIG_SERIAL_EFM32_USART_CONSOLE
+static void efm32_usart_console_putchar(struct uart_port *port, int ch)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+	unsigned int timeout = 0x400;
+	u32 status;
+
+	while (1) {
+		status = efm32_usart_read32(efm_port, USARTn_STATUS);
+
+		if (status & USARTn_STATUS_TXBL)
+			break;
+		if (!timeout--)
+			return;
+	}
+	efm32_usart_write32(efm_port, ch, USARTn_TXDATA);
+}
+
+static void efm32_usart_console_write(struct console *co, const char *s,
+		unsigned int count)
+{
+	struct efm32_usart_port *efm_port = efm32_usart_ports[co->index];
+	u32 status = efm32_usart_read32(efm_port, USARTn_STATUS);
+	unsigned int timeout = 0x400;
+
+	if (!(status & USARTn_STATUS_TXENS))
+		efm32_usart_write32(efm_port, USARTn_CMD_TXEN, USARTn_CMD);
+
+	uart_console_write(&efm_port->port, s, count,
+			efm32_usart_console_putchar);
+
+	/* Wait for the transmitter to become empty */
+	while (1) {
+		u32 status = efm32_usart_read32(efm_port, USARTn_STATUS);
+		if (status & USARTn_STATUS_TXC)
+			break;
+		if (!timeout--)
+			break;
+	}
+
+	if (!(status & USARTn_STATUS_TXENS))
+		efm32_usart_write32(efm_port, USARTn_CMD_TXDIS, USARTn_CMD);
+}
+
+static void efm32_usart_console_get_options(struct efm32_usart_port *efm_port,
+		int *baud, int *parity, int *bits)
+{
+	u32 ctrl = efm32_usart_read32(efm_port, USARTn_CTRL);
+	u32 route, clkdiv;
+
+	if (ctrl & USARTn_CTRL_SYNC)
+		/* not operating in async mode */
+		return;
+
+	route = efm32_usart_read32(efm_port, USARTn_ROUTE);
+	if (!(route & USARTn_ROUTE_TXPEN))
+		/* tx pin not routed */
+		return;
+
+	clkdiv = efm32_usart_read32(efm_port, USARTn_CLKDIV);
+
+	*baud = DIV_ROUND_CLOSEST(4 * efm_port->port.uartclk,
+			16 * (4 + (clkdiv >> 6)));
+	*parity = 'n';
+	*bits = 8;
+
+	efm_debug(efm_port, "get_opts: baud=%d\n", *baud);
+}
+
+static int efm32_usart_console_setup(struct console *co, char *options)
+{
+	struct efm32_usart_port *efm_port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret;
+
+	if (co->index < 0 || co->index >= ARRAY_SIZE(efm32_usart_ports)) {
+		unsigned i;
+		for (i = 0; i < ARRAY_SIZE(efm32_usart_ports); ++i) {
+			if (efm32_usart_ports[i]) {
+				pr_warn("efm32-console: fall back to console index %u (from %hhi)\n",
+						i, co->index);
+				co->index = i;
+				break;
+			}
+		}
+	}
+
+	efm_port = efm32_usart_ports[co->index];
+	if (!efm_port) {
+		pr_warn("efm32-console: No port at %d\n", co->index);
+		return -ENODEV;
+	}
+
+	ret = clk_prepare(efm_port->clk);
+	if (ret) {
+		dev_warn(efm_port->port.dev,
+				"console: clk_prepare failed: %d\n", ret);
+		return ret;
+	}
+
+	efm_port->port.uartclk = clk_get_rate(efm_port->clk);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		efm32_usart_console_get_options(efm_port,
+				&baud, &parity, &bits);
+
+	return uart_set_options(&efm_port->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver efm32_usart_reg;
+
+static struct console efm32_usart_console = {
+	.name = DEV_NAME,
+	.write = efm32_usart_console_write,
+	.device = uart_console_device,
+	.setup = efm32_usart_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &efm32_usart_reg,
+};
+
+#else
+#define efm32_usart_console (*(struct console *)NULL)
+#endif /* ifdef CONFIG_SERIAL_EFM32_USART_CONSOLE / else */
+
+static struct uart_driver efm32_usart_reg = {
+	.owner = THIS_MODULE,
+	.driver_name = DRIVER_NAME,
+	.dev_name = DEV_NAME,
+	.nr = ARRAY_SIZE(efm32_usart_ports),
+	.cons = &efm32_usart_console,
+};
+
+static int efm32_usart_probe_dt(struct platform_device *pdev,
+		struct efm32_usart_port *efm_port)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int ret;
+
+	if (!np)
+		return 1;
+
+	ret = of_alias_get_id(np, "serial");
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to get alias id: %d\n", ret);
+		return ret;
+	} else {
+		efm_port->port.line = ret;
+		return 0;
+	}
+
+}
+
+static int __devinit efm32_usart_probe(struct platform_device *pdev)
+{
+	struct efm32_usart_port *efm_port;
+	struct resource *res;
+	int ret;
+
+	efm_port = kzalloc(sizeof(*efm_port), GFP_KERNEL);
+	if (!efm_port) {
+		dev_dbg(&pdev->dev, "failed to allocate private data\n");
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENODEV;
+		dev_dbg(&pdev->dev, "failed to determine base address\n");
+		goto err_get_base;
+	}
+
+	if (resource_size(res) < 60) {
+		ret = -EINVAL;
+		dev_dbg(&pdev->dev, "memory resource too small\n");
+		goto err_too_small;
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret <= 0) {
+		dev_dbg(&pdev->dev, "failed to get rx irq\n");
+		goto err_get_rxirq;
+	}
+
+	efm_port->port.irq = ret;
+
+	ret = platform_get_irq(pdev, 1);
+	if (ret <= 0)
+		ret = efm_port->port.irq + 1;
+
+	efm_port->txirq = ret;
+
+	efm_port->port.dev = &pdev->dev;
+	efm_port->port.mapbase = res->start;
+	efm_port->port.type = PORT_EFMUSART;
+	efm_port->port.iotype = UPIO_MEM32;
+	efm_port->port.fifosize = 2;
+	efm_port->port.ops = &efm32_usart_pops;
+	efm_port->port.flags = UPF_BOOT_AUTOCONF;
+
+	ret = efm32_usart_probe_dt(pdev, efm_port);
+	if (ret > 0)
+		/* not created by device tree */
+		efm_port->port.line = pdev->id;
+
+	if (efm_port->port.line >= 0 &&
+			efm_port->port.line < ARRAY_SIZE(efm32_usart_ports))
+		efm32_usart_ports[efm_port->port.line] = efm_port;
+
+	ret = uart_add_one_port(&efm32_usart_reg, &efm_port->port);
+	if (ret) {
+		dev_dbg(&pdev->dev, "failed to add port: %d\n", ret);
+
+		if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_usart_ports))
+			efm32_usart_ports[pdev->id] = NULL;
+err_get_rxirq:
+err_too_small:
+err_get_base:
+		kfree(efm_port);
+	} else {
+		platform_set_drvdata(pdev, efm_port);
+		dev_dbg(&pdev->dev, "\\o/\n");
+	}
+
+	return ret;
+}
+
+static int __devexit efm32_usart_remove(struct platform_device *pdev)
+{
+	struct efm32_usart_port *efm_port = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	uart_remove_one_port(&efm32_usart_reg, &efm_port->port);
+
+	if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_usart_ports))
+		efm32_usart_ports[pdev->id] = NULL;
+
+	kfree(efm_port);
+
+	return 0;
+}
+
+static struct of_device_id efm32_usart_dt_ids[] = {
+	{
+		.compatible = "efm32,usart",
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, efm32_usart_dt_ids);
+
+static struct platform_driver efm32_usart_driver = {
+	.probe = efm32_usart_probe,
+	.remove = __devexit_p(efm32_usart_remove),
+
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = efm32_usart_dt_ids,
+	},
+};
+
+static int __init efm32_usart_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&efm32_usart_reg);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&efm32_usart_driver);
+	if (ret)
+		uart_unregister_driver(&efm32_usart_reg);
+
+	pr_info("EFM32 USART driver\n");
+
+	return ret;
+}
+module_init(efm32_usart_init);
+
+static void __exit efm32_usart_exit(void)
+{
+	platform_driver_unregister(&efm32_usart_driver);
+	uart_unregister_driver(&efm32_usart_reg);
+}
+
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_DESCRIPTION("EFM32 USART driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index eadf33d..eb45d4d 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -207,6 +207,8 @@
 /* Xilinx PSS UART */
 #define PORT_XUARTPS	98
 
+#define PORT_EFMUSART   99
+
 #ifdef __KERNEL__
 
 #include <linux/compiler.h>
-- 
1.7.7.3


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH] serial/efm32: add new driver
  2011-12-21 15:05 [PATCH] serial/efm32: add new driver Uwe Kleine-König
@ 2011-12-21 20:28 ` Alan Cox
  2011-12-22  8:57   ` Uwe Kleine-König
  2011-12-22 13:38   ` Uwe Kleine-König
  0 siblings, 2 replies; 17+ messages in thread
From: Alan Cox @ 2011-12-21 20:28 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-kernel, devicetree-discuss, Greg Kroah-Hartman,
	linux-serial, kernel

On Wed, 21 Dec 2011 16:05:59 +0100
Uwe Kleine-König <u.kleine-koenig@pengutronix.de> wrote:

> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> ---
> Hello,
> 
> note that ARCH_EFM32 isn't in mainline yet, so to be actually usable
> some arch patches are needed.

Start by running it through the CodingStyle scripts as I see a //
comment in there 8)


> +static void efm32_usart_rx_chars(struct efm32_usart_port *efm_port)
> +{
> +	struct uart_port *port = &efm_port->port;
> +	struct tty_struct *tty = port->state->port.tty;

Needs to be using krefs and checking the tty is not NULL
(tty_port_tty_get)

> +static void efm32_usart_set_termios(struct uart_port *port,
> +		struct ktermios *new, struct ktermios *old)
> +{
> +	struct efm32_usart_port *efm_port = to_efm_port(port);
> +	unsigned long flags;
> +	unsigned baud;
> +	u32 clkdiv;
> +
> +	/* no modem control lines */
> +	new->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);

Minor item - HUPCL shouldn't get cleared - its a request for hangup
behaviour not a port feature.

> +	/* currently only some features are implemented */
> +	new->c_cflag &= ~CSIZE;
> +	new->c_cflag |= CS8;
> +	new->c_cflag |= CSTOPB;
> +	new->c_cflag &= ~PARENB;

If you can do CS8 without parity you can do CS7 with parity.

> +	new->c_iflag = 0;

This seems broken. Lots of the iflgs are user requests and stack
properties not port features. For example you can do xon/xoff as its
pure software. As far as I can see you should leave c_iflag alone.

Alan

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] serial/efm32: add new driver
  2011-12-21 20:28 ` Alan Cox
@ 2011-12-22  8:57   ` Uwe Kleine-König
  2011-12-22 13:38   ` Uwe Kleine-König
  1 sibling, 0 replies; 17+ messages in thread
From: Uwe Kleine-König @ 2011-12-22  8:57 UTC (permalink / raw)
  To: Alan Cox
  Cc: linux-kernel, devicetree-discuss, Greg Kroah-Hartman,
	linux-serial, kernel

Hello,

On Wed, Dec 21, 2011 at 08:28:47PM +0000, Alan Cox wrote:
> On Wed, 21 Dec 2011 16:05:59 +0100
> Uwe Kleine-König <u.kleine-koenig@pengutronix.de> wrote:
> 
> > Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > ---
> > Hello,
> > 
> > note that ARCH_EFM32 isn't in mainline yet, so to be actually usable
> > some arch patches are needed.
> 
> Start by running it through the CodingStyle scripts as I see a //
> comment in there 8)
oops

> > +static void efm32_usart_rx_chars(struct efm32_usart_port *efm_port)
> > +{
> > +	struct uart_port *port = &efm_port->port;
> > +	struct tty_struct *tty = port->state->port.tty;
> 
> Needs to be using krefs and checking the tty is not NULL
> (tty_port_tty_get)
ah, I copied from mxs-auart. Will try to find a better example. 
 
> > +static void efm32_usart_set_termios(struct uart_port *port,
> > +		struct ktermios *new, struct ktermios *old)
> > +{
> > +	struct efm32_usart_port *efm_port = to_efm_port(port);
> > +	unsigned long flags;
> > +	unsigned baud;
> > +	u32 clkdiv;
> > +
> > +	/* no modem control lines */
> > +	new->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);
> 
> Minor item - HUPCL shouldn't get cleared - its a request for hangup
> behaviour not a port feature.
ah, I copied that from imx (in an if(0) though).
 
> > +	/* currently only some features are implemented */
> > +	new->c_cflag &= ~CSIZE;
> > +	new->c_cflag |= CS8;
> > +	new->c_cflag |= CSTOPB;
> > +	new->c_cflag &= ~PARENB;
> 
> If you can do CS8 without parity you can do CS7 with parity.
I sticked to the things I use and tested. According to the manual the
hardware can do 4 up to 16 data bits with and without parity :-)
 
> > +	new->c_iflag = 0;
> 
> This seems broken. Lots of the iflgs are user requests and stack
> properties not port features. For example you can do xon/xoff as its
> pure software. As far as I can see you should leave c_iflag alone.
ok.

I will send an update and fix the drivers I copied from.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH] serial/efm32: add new driver
  2011-12-21 20:28 ` Alan Cox
  2011-12-22  8:57   ` Uwe Kleine-König
@ 2011-12-22 13:38   ` Uwe Kleine-König
  2011-12-23 10:35     ` Arnd Bergmann
  2012-01-09 16:44     ` [PATCH v3] " Uwe Kleine-König
  1 sibling, 2 replies; 17+ messages in thread
From: Uwe Kleine-König @ 2011-12-22 13:38 UTC (permalink / raw)
  To: linux-kernel, Alan Cox
  Cc: devicetree-discuss, Greg Kroah-Hartman, kernel, linux-serial

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
changes since (implicit) v1:
- fix checkpatch issues
- use tty_port_tty_get/tty_kref_put in efm32_usart_rx_chars
- handle parity and csize in .set_termios and
  efm32_usart_console_get_options
- drop clearing HUPCL from c_cflag and everything from c_iflag in
  .set_termios

These were all spotted by Alan.

 .../devicetree/bindings/tty/serial/efm32-usart.txt |   14 +
 drivers/tty/serial/Kconfig                         |   10 +
 drivers/tty/serial/Makefile                        |    1 +
 drivers/tty/serial/efm32-usart.c                   |  750 ++++++++++++++++++++
 include/linux/serial_core.h                        |    2 +
 5 files changed, 777 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/tty/serial/efm32-usart.txt
 create mode 100644 drivers/tty/serial/efm32-usart.c

diff --git a/Documentation/devicetree/bindings/tty/serial/efm32-usart.txt b/Documentation/devicetree/bindings/tty/serial/efm32-usart.txt
new file mode 100644
index 0000000..eef2721
--- /dev/null
+++ b/Documentation/devicetree/bindings/tty/serial/efm32-usart.txt
@@ -0,0 +1,14 @@
+* Energymicro efm32 UART
+
+Required properties:
+- compatible : Should be "efm32,usart"
+- reg : Address and length of the register set
+- interrupts : Should contain uart interrupt
+
+Example:
+
+uart@0x4000c400 {
+	compatible = "efm32,usart";
+	reg = <0x4000c400 0x400>;
+	interrupts = <15>;
+};
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 925a1e5..cfeb0f3 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1610,4 +1610,14 @@ config SERIAL_XILINX_PS_UART_CONSOLE
 	help
 	  Enable a Xilinx PS UART port to be the system console.
 
+config SERIAL_EFM32_USART
+	bool "EFM32 USART port."
+	depends on ARCH_EFM32
+	select SERIAL_CORE
+
+config SERIAL_EFM32_USART_CONSOLE
+	bool "EFM32 USART console support"
+	depends on SERIAL_EFM32_USART=y
+	select SERIAL_CORE_CONSOLE
+
 endmenu
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index e10cf5b..d3ab42b 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -94,3 +94,4 @@ obj-$(CONFIG_SERIAL_MSM_SMD)	+= msm_smd_tty.o
 obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o
 obj-$(CONFIG_SERIAL_LANTIQ)	+= lantiq.o
 obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o
+obj-$(CONFIG_SERIAL_EFM32_USART) += efm32-usart.o
diff --git a/drivers/tty/serial/efm32-usart.c b/drivers/tty/serial/efm32-usart.c
new file mode 100644
index 0000000..a46b43c
--- /dev/null
+++ b/drivers/tty/serial/efm32-usart.c
@@ -0,0 +1,750 @@
+#include <linux/kernel.h>
+#include <linux/serial_core.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/console.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#define DRIVER_NAME "efm32-usart"
+#define DEV_NAME "ttyefm"
+
+#define USARTn_CTRL		0x00
+#define USARTn_CTRL_SYNC		0x0001
+
+#define USARTn_FRAME		0x04
+#define USARTn_FRAME_DATABITS__MASK	0x000f
+#define USARTn_FRAME_DATABITS(n)	((n) - 3)
+#define USARTn_FRAME_PARITY_NONE	0x0000
+#define USARTn_FRAME_PARITY_EVEN	0x0200
+#define USARTn_FRAME_PARITY_ODD		0x0300
+#define USARTn_FRAME_STOPBITS_HALF	0x0000
+#define USARTn_FRAME_STOPBITS_ONE	0x1000
+#define USARTn_FRAME_STOPBITS_TWO	0x3000
+
+#define USARTn_CMD		0x0c
+#define USARTn_CMD_RXEN			0x0001
+#define USARTn_CMD_RXDIS		0x0002
+#define USARTn_CMD_TXEN			0x0004
+#define USARTn_CMD_TXDIS		0x0008
+
+#define USARTn_STATUS		0x10
+#define USARTn_STATUS_TXENS		0x0002
+#define USARTn_STATUS_TXC		0x0020
+#define USARTn_STATUS_TXBL		0x0040
+#define USARTn_STATUS_RXDATAV		0x0080
+
+#define USARTn_CLKDIV		0x14
+
+#define USARTn_RXDATAX		0x18
+#define USARTn_RXDATAX_PERR		0x4000
+#define USARTn_RXDATAX_FERR		0x8000
+
+#define USARTn_TXDATA		0x34
+
+#define USARTn_IF		0x40
+#define USARTn_IF_TXBL			0x0002
+#define USARTn_IF_RXDATAV		0x0004
+
+#define USARTn_IFS		0x44
+#define USARTn_IFC		0x48
+#define USARTn_IEN		0x4c
+
+#define USARTn_ROUTE		0x54
+#define USARTn_ROUTE_RXPEN		0x0001
+#define USARTn_ROUTE_TXPEN		0x0002
+
+struct efm32_usart_port {
+	struct uart_port port;
+	unsigned int txirq;
+	struct clk *clk;
+};
+#define to_efm_port(_port) container_of(_port, struct efm32_usart_port, port)
+#define efm_debug(efm_port, format, arg...)			\
+	dev_dbg(efm_port->port.dev, format, ##arg)
+
+static void efm32_usart_write32(struct efm32_usart_port *efm_port,
+		u32 value, unsigned offset)
+{
+	__raw_writel(value, efm_port->port.membase + offset);
+}
+
+static u32 efm32_usart_read32(struct efm32_usart_port *efm_port,
+		unsigned offset)
+{
+	return __raw_readl(efm_port->port.membase + offset);
+}
+
+static unsigned int efm32_usart_tx_empty(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+	u32 status = efm32_usart_read32(efm_port, USARTn_STATUS);
+
+	/* XXX: does TXBL also mean that the shifter is done? */
+	if (status & USARTn_STATUS_TXBL)
+		return TIOCSER_TEMT;
+	else
+		return 0;
+}
+
+static void efm32_usart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	/* sorry, neither handshaking lines nor loop functionallity */
+}
+
+static unsigned int efm32_usart_get_mctrl(struct uart_port *port)
+{
+	/* sorry, no handshaking lines available */
+	return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
+}
+
+static void efm32_usart_stop_tx(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+	u32 ien = efm32_usart_read32(efm_port,  USARTn_IEN);
+
+	efm32_usart_write32(efm_port, ien & ~USARTn_IF_TXBL, USARTn_IEN);
+}
+
+static void efm32_usart_tx_chars(struct efm32_usart_port *efm_port)
+{
+	struct uart_port *port = &efm_port->port;
+	struct circ_buf *xmit = &port->state->xmit;
+
+	while (efm32_usart_read32(efm_port, USARTn_STATUS) &
+			USARTn_STATUS_TXBL) {
+		if (port->x_char) {
+			port->icount.tx++;
+			efm32_usart_write32(efm_port, port->x_char,
+					USARTn_TXDATA);
+			port->x_char = 0;
+			continue;
+		}
+		if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) {
+			port->icount.tx++;
+			efm32_usart_write32(efm_port, xmit->buf[xmit->tail],
+					USARTn_TXDATA);
+			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		} else
+			break;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (!port->x_char && uart_circ_empty(xmit))
+		efm32_usart_stop_tx(port);
+}
+
+static void efm32_usart_start_tx(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+	u32 ien;
+
+	efm32_usart_write32(efm_port, USARTn_IF_TXBL, USARTn_IFC);
+	ien = efm32_usart_read32(efm_port,  USARTn_IEN);
+	efm32_usart_write32(efm_port, ien | USARTn_IF_TXBL, USARTn_IEN);
+	efm32_usart_write32(efm_port, USARTn_CMD_TXEN, USARTn_CMD);
+
+	efm32_usart_tx_chars(efm_port);
+}
+
+static void efm32_usart_stop_rx(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+
+	efm32_usart_write32(efm_port, USARTn_CMD_RXDIS, USARTn_CMD);
+}
+
+static void efm32_usart_enable_ms(struct uart_port *port)
+{
+	/* no handshake lines, no modem status interrupts */
+}
+
+static void efm32_usart_break_ctl(struct uart_port *port, int ctl)
+{
+	/* not possible without fiddling with gpios */
+}
+
+static void efm32_usart_rx_chars(struct efm32_usart_port *efm_port)
+{
+	struct uart_port *port = &efm_port->port;
+	struct tty_struct *tty = tty_port_tty_get(&port->state->port);
+
+	spin_lock(&port->lock);
+
+	while (efm32_usart_read32(efm_port, USARTn_STATUS) &
+			USARTn_STATUS_RXDATAV) {
+		u32 rxdata = efm32_usart_read32(efm_port, USARTn_RXDATAX);
+		int flag = 0;
+
+		port->icount.rx++;
+
+		/* XXX detect BREAK and overrun */
+
+		if (rxdata & USARTn_RXDATAX_PERR) {
+			port->icount.parity++;
+			if (port->read_status_mask & USARTn_RXDATAX_PERR)
+				flag = TTY_PARITY;
+		} else if (rxdata & USARTn_RXDATAX_FERR) {
+			port->icount.frame++;
+			if (port->read_status_mask & USARTn_RXDATAX_FERR)
+				flag = TTY_FRAME;
+		}
+
+		if (rxdata & port->ignore_status_mask)
+			continue;
+
+		if (tty)
+			tty_insert_flip_char(tty, rxdata, flag);
+	}
+	spin_unlock(&port->lock);
+
+	if (tty) {
+		tty_flip_buffer_push(tty);
+		tty_kref_put(tty);
+	}
+}
+
+static irqreturn_t efm32_usart_rxirq(int irq, void *data)
+{
+	struct efm32_usart_port *efm_port = data;
+	u32 irqflag = efm32_usart_read32(efm_port, USARTn_IF);
+
+	if (irqflag & USARTn_IF_RXDATAV) {
+		efm32_usart_write32(efm_port, USARTn_IF_RXDATAV, USARTn_IFC);
+		efm32_usart_rx_chars(efm_port);
+
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+static irqreturn_t efm32_usart_txirq(int irq, void *data)
+{
+	struct efm32_usart_port *efm_port = data;
+	u32 irqflag = efm32_usart_read32(efm_port, USARTn_IF);
+
+	if (irqflag & USARTn_IF_TXBL) {
+		efm32_usart_write32(efm_port, USARTn_IF_TXBL, USARTn_IFC);
+		efm32_usart_tx_chars(efm_port);
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+static int efm32_usart_startup(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+	int ret;
+
+	ret = clk_enable(efm_port->clk);
+	if (ret) {
+		efm_debug(efm_port, "failed to enable clk\n");
+		goto err_clk_enable;
+	}
+	port->uartclk = clk_get_rate(efm_port->clk);
+
+	/* Enable pins at default location */
+	efm32_usart_write32(efm_port, USARTn_ROUTE_RXPEN | USARTn_ROUTE_TXPEN,
+			USARTn_ROUTE);
+
+	ret = request_irq(port->irq, efm32_usart_rxirq, 0,
+			DRIVER_NAME, efm_port);
+	if (ret) {
+		efm_debug(efm_port, "failed to register rxirq\n");
+		goto err_request_irq_rx;
+	}
+
+	/* disable all irqs */
+	efm32_usart_write32(efm_port, 0, USARTn_IEN);
+
+	ret = request_irq(efm_port->txirq, efm32_usart_txirq, 0,
+			DRIVER_NAME, efm_port);
+	if (ret) {
+		efm_debug(efm_port, "failed to register txirq\n");
+		free_irq(port->irq, efm_port);
+err_request_irq_rx:
+
+		clk_disable(efm_port->clk);
+	} else {
+		efm32_usart_write32(efm_port, USARTn_IF_RXDATAV, USARTn_IEN);
+		efm32_usart_write32(efm_port, USARTn_CMD_RXEN, USARTn_CMD);
+	}
+
+err_clk_enable:
+	return ret;
+}
+
+static void efm32_usart_shutdown(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+
+	efm32_usart_write32(efm_port, 0, USARTn_IEN);
+	free_irq(port->irq, efm_port);
+
+	clk_disable(efm_port->clk);
+}
+
+static void efm32_usart_set_termios(struct uart_port *port,
+		struct ktermios *new, struct ktermios *old)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+	unsigned long flags;
+	unsigned baud;
+	u32 clkdiv;
+	u32 frame = 0;
+
+	/* no modem control lines */
+	new->c_cflag &= ~(CRTSCTS | CMSPAR);
+
+	baud = uart_get_baud_rate(port, new, old,
+			DIV_ROUND_CLOSEST(port->uartclk, 16 * 8192),
+			DIV_ROUND_CLOSEST(port->uartclk, 16));
+
+	switch (new->c_cflag & CSIZE) {
+	case CS5:
+		frame |= USARTn_FRAME_DATABITS(5);
+		break;
+	case CS6:
+		frame |= USARTn_FRAME_DATABITS(6);
+		break;
+	case CS7:
+		frame |= USARTn_FRAME_DATABITS(7);
+		break;
+	case CS8:
+		frame |= USARTn_FRAME_DATABITS(8);
+		break;
+	}
+
+	/* tx only supports a "half" stop bit */
+	new->c_cflag |= CSTOPB;
+	frame |= USARTn_FRAME_STOPBITS_ONE;
+
+	if (new->c_cflag & PARENB) {
+		if (new->c_cflag & PARODD)
+			frame |= USARTn_FRAME_PARITY_ODD;
+		else
+			frame |= USARTn_FRAME_PARITY_EVEN;
+	} else
+		frame |= USARTn_FRAME_PARITY_NONE;
+
+	/*
+	 * the 6 lowest bits of CLKDIV are dc, bit 6 has value 0.25.
+	 * port->uartclk <= 14e6, so 4 * port->uartclk doesn't overflow.
+	 */
+	clkdiv = (DIV_ROUND_CLOSEST(4 * port->uartclk, 16 * baud) - 4) << 6;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	efm32_usart_write32(efm_port,
+			USARTn_CMD_TXDIS | USARTn_CMD_RXDIS, USARTn_CMD);
+
+	port->read_status_mask = 0;
+	port->ignore_status_mask = 0;
+
+	uart_update_timeout(port, new->c_cflag, baud);
+
+	efm32_usart_write32(efm_port, 0, USARTn_CTRL);
+	efm32_usart_write32(efm_port, frame, USARTn_FRAME);
+	efm32_usart_write32(efm_port, clkdiv, USARTn_CLKDIV);
+
+	efm32_usart_write32(efm_port, USARTn_CMD_TXEN | USARTn_CMD_RXEN,
+			USARTn_CMD);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *efm32_usart_type(struct uart_port *port)
+{
+	return port->type == PORT_EFMUSART ? "efm32-usart" : NULL;
+}
+
+static void efm32_usart_release_port(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+
+	clk_unprepare(efm_port->clk);
+	clk_put(efm_port->clk);
+	iounmap(port->membase);
+}
+
+static int efm32_usart_request_port(struct uart_port *port)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+	int ret;
+
+	port->membase = ioremap(port->mapbase, 60);
+	if (!efm_port->port.membase) {
+		ret = -ENOMEM;
+		efm_debug(efm_port, "failed to remap\n");
+		goto err_ioremap;
+	}
+
+	efm_port->clk = clk_get(port->dev, NULL);
+	if (IS_ERR(efm_port->clk)) {
+		ret = PTR_ERR(efm_port->clk);
+		efm_debug(efm_port, "failed to get clock\n");
+		goto err_clk_get;
+	}
+
+	ret = clk_prepare(efm_port->clk);
+	if (ret) {
+		clk_put(efm_port->clk);
+err_clk_get:
+
+		iounmap(port->membase);
+err_ioremap:
+		return ret;
+	}
+	return 0;
+}
+
+static void efm32_usart_config_port(struct uart_port *port, int type)
+{
+	if (type & UART_CONFIG_TYPE &&
+			!efm32_usart_request_port(port))
+		port->type = PORT_EFMUSART;
+}
+
+static int efm32_usart_verify_port(struct uart_port *port,
+		struct serial_struct *serinfo)
+{
+	int ret = 0;
+
+	if (serinfo->type != PORT_UNKNOWN && serinfo->type != PORT_EFMUSART)
+		ret = -EINVAL;
+
+	return ret;
+}
+
+static struct uart_ops efm32_usart_pops = {
+	.tx_empty = efm32_usart_tx_empty,
+	.set_mctrl = efm32_usart_set_mctrl,
+	.get_mctrl = efm32_usart_get_mctrl,
+	.stop_tx = efm32_usart_stop_tx,
+	.start_tx = efm32_usart_start_tx,
+	.stop_rx = efm32_usart_stop_rx,
+	.enable_ms = efm32_usart_enable_ms,
+	.break_ctl = efm32_usart_break_ctl,
+	.startup = efm32_usart_startup,
+	.shutdown = efm32_usart_shutdown,
+	.set_termios = efm32_usart_set_termios,
+	.type = efm32_usart_type,
+	.release_port = efm32_usart_release_port,
+	.request_port = efm32_usart_request_port,
+	.config_port = efm32_usart_config_port,
+	.verify_port = efm32_usart_verify_port,
+};
+
+static struct efm32_usart_port *efm32_usart_ports[3];
+
+#ifdef CONFIG_SERIAL_EFM32_USART_CONSOLE
+static void efm32_usart_console_putchar(struct uart_port *port, int ch)
+{
+	struct efm32_usart_port *efm_port = to_efm_port(port);
+	unsigned int timeout = 0x400;
+	u32 status;
+
+	while (1) {
+		status = efm32_usart_read32(efm_port, USARTn_STATUS);
+
+		if (status & USARTn_STATUS_TXBL)
+			break;
+		if (!timeout--)
+			return;
+	}
+	efm32_usart_write32(efm_port, ch, USARTn_TXDATA);
+}
+
+static void efm32_usart_console_write(struct console *co, const char *s,
+		unsigned int count)
+{
+	struct efm32_usart_port *efm_port = efm32_usart_ports[co->index];
+	u32 status = efm32_usart_read32(efm_port, USARTn_STATUS);
+	unsigned int timeout = 0x400;
+
+	if (!(status & USARTn_STATUS_TXENS))
+		efm32_usart_write32(efm_port, USARTn_CMD_TXEN, USARTn_CMD);
+
+	uart_console_write(&efm_port->port, s, count,
+			efm32_usart_console_putchar);
+
+	/* Wait for the transmitter to become empty */
+	while (1) {
+		u32 status = efm32_usart_read32(efm_port, USARTn_STATUS);
+		if (status & USARTn_STATUS_TXC)
+			break;
+		if (!timeout--)
+			break;
+	}
+
+	if (!(status & USARTn_STATUS_TXENS))
+		efm32_usart_write32(efm_port, USARTn_CMD_TXDIS, USARTn_CMD);
+}
+
+static void efm32_usart_console_get_options(struct efm32_usart_port *efm_port,
+		int *baud, int *parity, int *bits)
+{
+	u32 ctrl = efm32_usart_read32(efm_port, USARTn_CTRL);
+	u32 route, clkdiv, frame;
+
+	if (ctrl & USARTn_CTRL_SYNC)
+		/* not operating in async mode */
+		return;
+
+	route = efm32_usart_read32(efm_port, USARTn_ROUTE);
+	if (!(route & USARTn_ROUTE_TXPEN))
+		/* tx pin not routed */
+		return;
+
+	clkdiv = efm32_usart_read32(efm_port, USARTn_CLKDIV);
+
+	*baud = DIV_ROUND_CLOSEST(4 * efm_port->port.uartclk,
+			16 * (4 + (clkdiv >> 6)));
+
+	frame = efm32_usart_read32(efm_port, USARTn_FRAME);
+	if (frame & USARTn_FRAME_PARITY_ODD)
+		*parity = 'o';
+	else if (frame & USARTn_FRAME_PARITY_EVEN)
+		*parity = 'e';
+	else
+		*parity = 'n';
+
+	*bits = (frame & USARTn_FRAME_DATABITS__MASK) -
+			USARTn_FRAME_DATABITS(4) + 4;
+
+	efm_debug(efm_port, "get_opts: options=%d%c%d\n",
+			*baud, *parity, *bits);
+}
+
+static int efm32_usart_console_setup(struct console *co, char *options)
+{
+	struct efm32_usart_port *efm_port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret;
+
+	if (co->index < 0 || co->index >= ARRAY_SIZE(efm32_usart_ports)) {
+		unsigned i;
+		for (i = 0; i < ARRAY_SIZE(efm32_usart_ports); ++i) {
+			if (efm32_usart_ports[i]) {
+				pr_warn("efm32-console: fall back to console index %u (from %hhi)\n",
+						i, co->index);
+				co->index = i;
+				break;
+			}
+		}
+	}
+
+	efm_port = efm32_usart_ports[co->index];
+	if (!efm_port) {
+		pr_warn("efm32-console: No port at %d\n", co->index);
+		return -ENODEV;
+	}
+
+	ret = clk_prepare(efm_port->clk);
+	if (ret) {
+		dev_warn(efm_port->port.dev,
+				"console: clk_prepare failed: %d\n", ret);
+		return ret;
+	}
+
+	efm_port->port.uartclk = clk_get_rate(efm_port->clk);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		efm32_usart_console_get_options(efm_port,
+				&baud, &parity, &bits);
+
+	return uart_set_options(&efm_port->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver efm32_usart_reg;
+
+static struct console efm32_usart_console = {
+	.name = DEV_NAME,
+	.write = efm32_usart_console_write,
+	.device = uart_console_device,
+	.setup = efm32_usart_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &efm32_usart_reg,
+};
+
+#else
+#define efm32_usart_console (*(struct console *)NULL)
+#endif /* ifdef CONFIG_SERIAL_EFM32_USART_CONSOLE / else */
+
+static struct uart_driver efm32_usart_reg = {
+	.owner = THIS_MODULE,
+	.driver_name = DRIVER_NAME,
+	.dev_name = DEV_NAME,
+	.nr = ARRAY_SIZE(efm32_usart_ports),
+	.cons = &efm32_usart_console,
+};
+
+static int efm32_usart_probe_dt(struct platform_device *pdev,
+		struct efm32_usart_port *efm_port)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int ret;
+
+	if (!np)
+		return 1;
+
+	ret = of_alias_get_id(np, "serial");
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to get alias id: %d\n", ret);
+		return ret;
+	} else {
+		efm_port->port.line = ret;
+		return 0;
+	}
+
+}
+
+static int __devinit efm32_usart_probe(struct platform_device *pdev)
+{
+	struct efm32_usart_port *efm_port;
+	struct resource *res;
+	int ret;
+
+	efm_port = kzalloc(sizeof(*efm_port), GFP_KERNEL);
+	if (!efm_port) {
+		dev_dbg(&pdev->dev, "failed to allocate private data\n");
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENODEV;
+		dev_dbg(&pdev->dev, "failed to determine base address\n");
+		goto err_get_base;
+	}
+
+	if (resource_size(res) < 60) {
+		ret = -EINVAL;
+		dev_dbg(&pdev->dev, "memory resource too small\n");
+		goto err_too_small;
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret <= 0) {
+		dev_dbg(&pdev->dev, "failed to get rx irq\n");
+		goto err_get_rxirq;
+	}
+
+	efm_port->port.irq = ret;
+
+	ret = platform_get_irq(pdev, 1);
+	if (ret <= 0)
+		ret = efm_port->port.irq + 1;
+
+	efm_port->txirq = ret;
+
+	efm_port->port.dev = &pdev->dev;
+	efm_port->port.mapbase = res->start;
+	efm_port->port.type = PORT_EFMUSART;
+	efm_port->port.iotype = UPIO_MEM32;
+	efm_port->port.fifosize = 2;
+	efm_port->port.ops = &efm32_usart_pops;
+	efm_port->port.flags = UPF_BOOT_AUTOCONF;
+
+	ret = efm32_usart_probe_dt(pdev, efm_port);
+	if (ret > 0)
+		/* not created by device tree */
+		efm_port->port.line = pdev->id;
+
+	if (efm_port->port.line >= 0 &&
+			efm_port->port.line < ARRAY_SIZE(efm32_usart_ports))
+		efm32_usart_ports[efm_port->port.line] = efm_port;
+
+	ret = uart_add_one_port(&efm32_usart_reg, &efm_port->port);
+	if (ret) {
+		dev_dbg(&pdev->dev, "failed to add port: %d\n", ret);
+
+		if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_usart_ports))
+			efm32_usart_ports[pdev->id] = NULL;
+err_get_rxirq:
+err_too_small:
+err_get_base:
+		kfree(efm_port);
+	} else {
+		platform_set_drvdata(pdev, efm_port);
+		dev_dbg(&pdev->dev, "\\o/\n");
+	}
+
+	return ret;
+}
+
+static int __devexit efm32_usart_remove(struct platform_device *pdev)
+{
+	struct efm32_usart_port *efm_port = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	uart_remove_one_port(&efm32_usart_reg, &efm_port->port);
+
+	if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_usart_ports))
+		efm32_usart_ports[pdev->id] = NULL;
+
+	kfree(efm_port);
+
+	return 0;
+}
+
+static struct of_device_id efm32_usart_dt_ids[] = {
+	{
+		.compatible = "efm32,usart",
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, efm32_usart_dt_ids);
+
+static struct platform_driver efm32_usart_driver = {
+	.probe = efm32_usart_probe,
+	.remove = __devexit_p(efm32_usart_remove),
+
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = efm32_usart_dt_ids,
+	},
+};
+
+static int __init efm32_usart_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&efm32_usart_reg);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&efm32_usart_driver);
+	if (ret)
+		uart_unregister_driver(&efm32_usart_reg);
+
+	pr_info("EFM32 USART driver\n");
+
+	return ret;
+}
+module_init(efm32_usart_init);
+
+static void __exit efm32_usart_exit(void)
+{
+	platform_driver_unregister(&efm32_usart_driver);
+	uart_unregister_driver(&efm32_usart_reg);
+}
+
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_DESCRIPTION("EFM32 USART driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index eadf33d..eb45d4d 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -207,6 +207,8 @@
 /* Xilinx PSS UART */
 #define PORT_XUARTPS	98
 
+#define PORT_EFMUSART   99
+
 #ifdef __KERNEL__
 
 #include <linux/compiler.h>
-- 
1.7.7.3


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH] serial/efm32: add new driver
  2011-12-22 13:38   ` Uwe Kleine-König
@ 2011-12-23 10:35     ` Arnd Bergmann
  2011-12-23 20:44       ` Uwe Kleine-König
  2012-01-09 16:44     ` [PATCH v3] " Uwe Kleine-König
  1 sibling, 1 reply; 17+ messages in thread
From: Arnd Bergmann @ 2011-12-23 10:35 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-kernel, Alan Cox, devicetree-discuss, Greg Kroah-Hartman,
	kernel, linux-serial

On Thursday 22 December 2011, Uwe Kleine-König wrote:

> @@ -0,0 +1,14 @@
> +* Energymicro efm32 UART
> +
> +Required properties:
> +- compatible : Should be "efm32,usart"
> +- reg : Address and length of the register set
> +- interrupts : Should contain uart interrupt
> +
> +Example:
> +
> +uart@0x4000c400 {
> +	compatible = "efm32,usart";
> +	reg = <0x4000c400 0x400>;
> +	interrupts = <15>;
> +};

Do you know if the usart was actually designed by energymicro or licensed
from another party? If it is a licensed part, it would be better to
list the "compatible" value under the company name that made it.

I would suggest that you also support the "clock-frequency" and/or
"current-speed" properties that are defined for serial ports, see the
of_serial driver.

> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index 925a1e5..cfeb0f3 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1610,4 +1610,14 @@ config SERIAL_XILINX_PS_UART_CONSOLE
>  	help
>  	  Enable a Xilinx PS UART port to be the system console.
>  
> +config SERIAL_EFM32_USART
> +	bool "EFM32 USART port."
> +	depends on ARCH_EFM32
> +	select SERIAL_CORE

Why not tristate? If it's not a console, you should be able to use
it as a module.

I would generally prefer not to make the driver depend on the
platform, so we can get better compile time coverage. I think a better
set of dependencies would be

	depends on HAVE_CLK
	depends on OF
	default ARCH_EFM32

> +static void efm32_usart_write32(struct efm32_usart_port *efm_port,
> +		u32 value, unsigned offset)
> +{
> +	__raw_writel(value, efm_port->port.membase + offset);
> +}
> +
> +static u32 efm32_usart_read32(struct efm32_usart_port *efm_port,
> +		unsigned offset)
> +{
> +	return __raw_readl(efm_port->port.membase + offset);
> +}

Please use writel_relaxed() instead of __raw_writel().

> +static int __devinit efm32_usart_probe(struct platform_device *pdev)
> +{
> +	struct efm32_usart_port *efm_port;
> +	struct resource *res;
> +	int ret;
> +
> +	efm_port = kzalloc(sizeof(*efm_port), GFP_KERNEL);
> +	if (!efm_port) {
> +		dev_dbg(&pdev->dev, "failed to allocate private data\n");
> +		return -ENOMEM;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		ret = -ENODEV;
> +		dev_dbg(&pdev->dev, "failed to determine base address\n");
> +		goto err_get_base;
> +	}
> +
> +	if (resource_size(res) < 60) {
> +		ret = -EINVAL;
> +		dev_dbg(&pdev->dev, "memory resource too small\n");
> +		goto err_too_small;
> +	}

of_iomap() would be simpler here. I think you can leave out the assignment to 
mapbase, and go straight to membase here.

checking the resource size should not be necessary, because that would imply
having an invalid device tree, which you don't have to check at run time.

	Arnd

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] serial/efm32: add new driver
  2011-12-23 10:35     ` Arnd Bergmann
@ 2011-12-23 20:44       ` Uwe Kleine-König
  2012-01-09  9:59         ` Kconfig option for compile time build coverage (Was: Re: [PATCH] serial/efm32: add new driver) Uwe Kleine-König
  2012-01-09 10:34         ` [PATCH] serial/efm32: add new driver Uwe Kleine-König
  0 siblings, 2 replies; 17+ messages in thread
From: Uwe Kleine-König @ 2011-12-23 20:44 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-kernel, Alan Cox, devicetree-discuss, Greg Kroah-Hartman,
	kernel, linux-serial

Hello Arnd,

On Fri, Dec 23, 2011 at 10:35:22AM +0000, Arnd Bergmann wrote:
> On Thursday 22 December 2011, Uwe Kleine-König wrote:
> 
> > @@ -0,0 +1,14 @@
> > +* Energymicro efm32 UART
> > +
> > +Required properties:
> > +- compatible : Should be "efm32,usart"
> > +- reg : Address and length of the register set
> > +- interrupts : Should contain uart interrupt
> > +
> > +Example:
> > +
> > +uart@0x4000c400 {
> > +	compatible = "efm32,usart";
> > +	reg = <0x4000c400 0x400>;
> > +	interrupts = <15>;
> > +};
> 
> Do you know if the usart was actually designed by energymicro or licensed
> from another party? If it is a licensed part, it would be better to
> list the "compatible" value under the company name that made it.
I don't know so I passed the question to them.
 
> I would suggest that you also support the "clock-frequency" and/or
> "current-speed" properties that are defined for serial ports, see the
> of_serial driver.
I will have a look to find out what they mean and update the patch
accordingly.
 
> > diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> > index 925a1e5..cfeb0f3 100644
> > --- a/drivers/tty/serial/Kconfig
> > +++ b/drivers/tty/serial/Kconfig
> > @@ -1610,4 +1610,14 @@ config SERIAL_XILINX_PS_UART_CONSOLE
> >  	help
> >  	  Enable a Xilinx PS UART port to be the system console.
> >  
> > +config SERIAL_EFM32_USART
> > +	bool "EFM32 USART port."
> > +	depends on ARCH_EFM32
> > +	select SERIAL_CORE
> 
> Why not tristate? If it's not a console, you should be able to use
> it as a module.
Hmm, in theory yes. (In practice modules on mmu are not that trivial,
see the corresponding thread on lakml.) I will update but I guess I can
only build test that configuration.
 
> I would generally prefer not to make the driver depend on the
> platform, so we can get better compile time coverage. I think a better
> set of dependencies would be
> 
> 	depends on HAVE_CLK
> 	depends on OF
> 	default ARCH_EFM32
I'd prefer something like:

	depends on HAVE_CLK
	depends on ARCH_EFM32 || I_DO_BUILD_COVERAGE_TESTING

This would make it easier for Joe User to pick the right options for his
kernel (assuming he found out to better keep I_DO_BUILD_COVERAGE_TESTING
disabled). There is no hard dependency on OF in the driver and I guess
at least the in-production (and out-of-tree) usage will still stick to
non-OF because it saves some RAM. So I'd prefer not to add that.
(And it would help build coverage testing ;-)

> > +static void efm32_usart_write32(struct efm32_usart_port *efm_port,
> > +		u32 value, unsigned offset)
> > +{
> > +	__raw_writel(value, efm_port->port.membase + offset);
> > +}
> > +
> > +static u32 efm32_usart_read32(struct efm32_usart_port *efm_port,
> > +		unsigned offset)
> > +{
> > +	return __raw_readl(efm_port->port.membase + offset);
> > +}
> 
> Please use writel_relaxed() instead of __raw_writel().
ok

> > +static int __devinit efm32_usart_probe(struct platform_device *pdev)
> > +{
> > +	struct efm32_usart_port *efm_port;
> > +	struct resource *res;
> > +	int ret;
> > +
> > +	efm_port = kzalloc(sizeof(*efm_port), GFP_KERNEL);
> > +	if (!efm_port) {
> > +		dev_dbg(&pdev->dev, "failed to allocate private data\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	if (!res) {
> > +		ret = -ENODEV;
> > +		dev_dbg(&pdev->dev, "failed to determine base address\n");
> > +		goto err_get_base;
> > +	}
> > +
> > +	if (resource_size(res) < 60) {
> > +		ret = -EINVAL;
> > +		dev_dbg(&pdev->dev, "memory resource too small\n");
> > +		goto err_too_small;
> > +	}
> 
> of_iomap() would be simpler here. I think you can leave out the assignment to 
> mapbase, and go straight to membase here.
but ioremap is only done in .request_port, so it's necessary to have two
steps here, isn't it?
 
> checking the resource size should not be necessary, because that would imply
> having an invalid device tree, which you don't have to check at run time.
hmm, I prefer an error message over an access to unmapped memory even if
that can only happen when the device tree is broken.

Best regards and thanks for your comments,
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Kconfig option for compile time build coverage (Was: Re: [PATCH] serial/efm32: add new driver)
  2011-12-23 20:44       ` Uwe Kleine-König
@ 2012-01-09  9:59         ` Uwe Kleine-König
  2012-01-25 16:16           ` Arnd Bergmann
  2012-01-09 10:34         ` [PATCH] serial/efm32: add new driver Uwe Kleine-König
  1 sibling, 1 reply; 17+ messages in thread
From: Uwe Kleine-König @ 2012-01-09  9:59 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-kernel, Alan Cox, devicetree-discuss, Greg Kroah-Hartman,
	kernel, linux-serial, Andrew Morton

Hello,

On Fri, Dec 23, 2011 at 09:44:28PM +0100, Uwe Kleine-König wrote:
> On Fri, Dec 23, 2011 at 10:35:22AM +0000, Arnd Bergmann wrote:
> > On Thursday 22 December 2011, Uwe Kleine-König wrote:
> > > [...]
> > > +config SERIAL_EFM32_USART
> > > +	bool "EFM32 USART port."
> > > +	depends on ARCH_EFM32
> > > +	select SERIAL_CORE
> [...]
> > I would generally prefer not to make the driver depend on the
> > platform, so we can get better compile time coverage. I think a better
> > set of dependencies would be
> > 
> > 	depends on HAVE_CLK
> > 	depends on OF
> > 	default ARCH_EFM32
> I'd prefer something like:
> 
> 	depends on HAVE_CLK
> 	depends on ARCH_EFM32 || I_DO_BUILD_COVERAGE_TESTING
> 
> This would make it easier for Joe User to pick the right options for his
> kernel (assuming he found out to better keep I_DO_BUILD_COVERAGE_TESTING
> disabled). [...]
What do you think about this I_DO_BUILD_COVERAGE_TESTING option? It
would allow testers to get all possible options enabled (though there
will never be an EFM32 USART port on a non-EFM32 machine I guess) and
still users and distribution packagers would easily keep the option off
even without cluttering .config and {menu,n,x,whatever}config.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] serial/efm32: add new driver
  2011-12-23 20:44       ` Uwe Kleine-König
  2012-01-09  9:59         ` Kconfig option for compile time build coverage (Was: Re: [PATCH] serial/efm32: add new driver) Uwe Kleine-König
@ 2012-01-09 10:34         ` Uwe Kleine-König
  2012-01-25 16:56           ` Arnd Bergmann
  1 sibling, 1 reply; 17+ messages in thread
From: Uwe Kleine-König @ 2012-01-09 10:34 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-kernel, Alan Cox, devicetree-discuss, Greg Kroah-Hartman,
	kernel, linux-serial

Hello,

On Fri, Dec 23, 2011 at 09:44:28PM +0100, Uwe Kleine-König wrote:
> On Fri, Dec 23, 2011 at 10:35:22AM +0000, Arnd Bergmann wrote:
> > On Thursday 22 December 2011, Uwe Kleine-König wrote:
> > 
> > > @@ -0,0 +1,14 @@
> > > +* Energymicro efm32 UART
> > > +
> > > +Required properties:
> > > +- compatible : Should be "efm32,usart"
> > > +- reg : Address and length of the register set
> > > +- interrupts : Should contain uart interrupt
> > > +
> > > +Example:
> > > +
> > > +uart@0x4000c400 {
> > > +	compatible = "efm32,usart";
> > > +	reg = <0x4000c400 0x400>;
> > > +	interrupts = <15>;
> > > +};
> > 
> > Do you know if the usart was actually designed by energymicro or licensed
> > from another party? If it is a licensed part, it would be better to
> > list the "compatible" value under the company name that made it.
> I don't know so I passed the question to them.
I got an answer here over the holidays and Energy Micro designed the
part.
 
> > I would suggest that you also support the "clock-frequency" and/or
> > "current-speed" properties that are defined for serial ports, see the
> > of_serial driver.
> I will have a look to find out what they mean and update the patch
> accordingly.
I took that look and I don't understand what they are good for in my
case.
clock-frequency is used to initialize port->uartclk (and helps setting
up port->custom_divisor if current-speed is given). The driver I posted
uses the clk API to determine the clock frequency. So shouldn't the frequency
better be specified in the clk part of the dt? (I don't know yet how a
dt binding for clks should/can look like, so this is a bit speculative,
but I'd expect to have nothing more clk related in a device
specification but a reference to a clk definition.)

Independant of my driver I wonder if defining current-speed should also
result in

	port->flags |= UPF_SPD_CUST;

(in of_platform_serial_setup()).

Having said that and taking into account that my driver doesn't use
port->custom_divisor, do you keep suggesting that I should use
"clock-frequency" and/or "current-speed"?

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v3] serial/efm32: add new driver
  2011-12-22 13:38   ` Uwe Kleine-König
  2011-12-23 10:35     ` Arnd Bergmann
@ 2012-01-09 16:44     ` Uwe Kleine-König
  2012-01-24 22:05       ` Greg KH
  1 sibling, 1 reply; 17+ messages in thread
From: Uwe Kleine-König @ 2012-01-09 16:44 UTC (permalink / raw)
  To: linux-kernel, Alan Cox
  Cc: kernel, devicetree-discuss, Greg Kroah-Hartman, linux-serial

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
Hello,

changes since v2:
- use {read,write}l_relaxed
- rename driver to efm32-uart as USARTs and UARTs can be handled both
  with it
- disable TX in .stop_tx (which needs some changes related to
  USARTn_IF_TXC)

Best regards
Uwe

 .../devicetree/bindings/tty/serial/efm32-uart.txt  |   14 +
 arch/arm/boot/dts/efm32gg-dk3750.dts               |    2 +-
 arch/arm/mach-efm32/devices/pdev-efm32-usart.c     |    6 +-
 drivers/tty/serial/Kconfig                         |   13 +
 drivers/tty/serial/Makefile                        |    1 +
 drivers/tty/serial/efm32-uart.c                    |  761 ++++++++++++++++++++
 include/linux/serial_core.h                        |    2 +
 7 files changed, 795 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/tty/serial/efm32-uart.txt
 create mode 100644 drivers/tty/serial/efm32-uart.c

diff --git a/Documentation/devicetree/bindings/tty/serial/efm32-uart.txt b/Documentation/devicetree/bindings/tty/serial/efm32-uart.txt
new file mode 100644
index 0000000..6588b69
--- /dev/null
+++ b/Documentation/devicetree/bindings/tty/serial/efm32-uart.txt
@@ -0,0 +1,14 @@
+* Energymicro efm32 UART
+
+Required properties:
+- compatible : Should be "efm32,uart"
+- reg : Address and length of the register set
+- interrupts : Should contain uart interrupt
+
+Example:
+
+uart@0x4000c400 {
+	compatible = "efm32,uart";
+	reg = <0x4000c400 0x400>;
+	interrupts = <15>;
+};
diff --git a/arch/arm/boot/dts/efm32gg-dk3750.dts b/arch/arm/boot/dts/efm32gg-dk3750.dts
index a4aa4f3..2a28b0f 100644
--- a/arch/arm/boot/dts/efm32gg-dk3750.dts
+++ b/arch/arm/boot/dts/efm32gg-dk3750.dts
@@ -32,7 +32,7 @@
 		ranges;
 
 		uart1: uart@0x4000c400 { /* USART1 */
-			compatible = "efm32,usart";
+			compatible = "efm32,uart";
 			reg = <0x4000c400 0x400>;
 			interrupts = <15>;
 			status = "ok";
diff --git a/arch/arm/mach-efm32/devices/pdev-efm32-usart.c b/arch/arm/mach-efm32/devices/pdev-efm32-usart.c
index 03ff343..c7d91e1 100644
--- a/arch/arm/mach-efm32/devices/pdev-efm32-usart.c
+++ b/arch/arm/mach-efm32/devices/pdev-efm32-usart.c
@@ -20,19 +20,19 @@ static const struct resource efm32_usart2_resources[] __initconst = {
 
 const struct platform_device_info efm32_usart_devinfo[] __initconst = {
 	[0] = {
-		.name = "efm32-usart",
+		.name = "efm32-uart",
 		.id = 0,
 		.res = efm32_usart0_resources,
 		.num_res = ARRAY_SIZE(efm32_usart0_resources),
 	},
 	[1] = {
-		.name = "efm32-usart",
+		.name = "efm32-uart",
 		.id = 1,
 		.res = efm32_usart1_resources,
 		.num_res = ARRAY_SIZE(efm32_usart1_resources),
 	},
 	[2] = {
-		.name = "efm32-usart",
+		.name = "efm32-uart",
 		.id = 2,
 		.res = efm32_usart2_resources,
 		.num_res = ARRAY_SIZE(efm32_usart2_resources),
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 925a1e5..b91974a 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1610,4 +1610,17 @@ config SERIAL_XILINX_PS_UART_CONSOLE
 	help
 	  Enable a Xilinx PS UART port to be the system console.
 
+config SERIAL_EFM32_UART
+	tristate "EFM32 UART/USART port."
+	depends on ARCH_EFM32
+	select SERIAL_CORE
+	help
+	  This driver support the USART and UART ports on
+	  Energy Micro's efm32 SoCs.
+
+config SERIAL_EFM32_UART_CONSOLE
+	bool "EFM32 UART/USART console support"
+	depends on SERIAL_EFM32_UART=y
+	select SERIAL_CORE_CONSOLE
+
 endmenu
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index e10cf5b..7814122 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -94,3 +94,4 @@ obj-$(CONFIG_SERIAL_MSM_SMD)	+= msm_smd_tty.o
 obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o
 obj-$(CONFIG_SERIAL_LANTIQ)	+= lantiq.o
 obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o
+obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o
diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c
new file mode 100644
index 0000000..ed0bc0d
--- /dev/null
+++ b/drivers/tty/serial/efm32-uart.c
@@ -0,0 +1,761 @@
+#include <linux/kernel.h>
+#include <linux/serial_core.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/console.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#define DRIVER_NAME "efm32-uart"
+#define DEV_NAME "ttyefm"
+
+#define UARTn_CTRL		0x00
+#define UARTn_CTRL_SYNC		0x0001
+#define UARTn_CTRL_TXBIL		0x1000
+
+#define UARTn_FRAME		0x04
+#define UARTn_FRAME_DATABITS__MASK	0x000f
+#define UARTn_FRAME_DATABITS(n)	((n) - 3)
+#define UARTn_FRAME_PARITY_NONE	0x0000
+#define UARTn_FRAME_PARITY_EVEN	0x0200
+#define UARTn_FRAME_PARITY_ODD		0x0300
+#define UARTn_FRAME_STOPBITS_HALF	0x0000
+#define UARTn_FRAME_STOPBITS_ONE	0x1000
+#define UARTn_FRAME_STOPBITS_TWO	0x3000
+
+#define UARTn_CMD		0x0c
+#define UARTn_CMD_RXEN			0x0001
+#define UARTn_CMD_RXDIS		0x0002
+#define UARTn_CMD_TXEN			0x0004
+#define UARTn_CMD_TXDIS		0x0008
+
+#define UARTn_STATUS		0x10
+#define UARTn_STATUS_TXENS		0x0002
+#define UARTn_STATUS_TXC		0x0020
+#define UARTn_STATUS_TXBL		0x0040
+#define UARTn_STATUS_RXDATAV		0x0080
+
+#define UARTn_CLKDIV		0x14
+
+#define UARTn_RXDATAX		0x18
+#define UARTn_RXDATAX_PERR		0x4000
+#define UARTn_RXDATAX_FERR		0x8000
+
+#define UARTn_TXDATA		0x34
+
+#define UARTn_IF		0x40
+#define UARTn_IF_TXC			0x0001
+#define UARTn_IF_TXBL			0x0002
+#define UARTn_IF_RXDATAV		0x0004
+
+#define UARTn_IFS		0x44
+#define UARTn_IFC		0x48
+#define UARTn_IEN		0x4c
+
+#define UARTn_ROUTE		0x54
+#define UARTn_ROUTE_RXPEN		0x0001
+#define UARTn_ROUTE_TXPEN		0x0002
+
+struct efm32_uart_port {
+	struct uart_port port;
+	unsigned int txirq;
+	struct clk *clk;
+};
+#define to_efm_port(_port) container_of(_port, struct efm32_uart_port, port)
+#define efm_debug(efm_port, format, arg...)			\
+	dev_dbg(efm_port->port.dev, format, ##arg)
+
+static void efm32_uart_write32(struct efm32_uart_port *efm_port,
+		u32 value, unsigned offset)
+{
+	writel_relaxed(value, efm_port->port.membase + offset);
+}
+
+static u32 efm32_uart_read32(struct efm32_uart_port *efm_port,
+		unsigned offset)
+{
+	return readl_relaxed(efm_port->port.membase + offset);
+}
+
+static unsigned int efm32_uart_tx_empty(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	u32 status = efm32_uart_read32(efm_port, UARTn_STATUS);
+
+	/* XXX: does TXBL also mean that the shifter is done? */
+	if (status & UARTn_STATUS_TXBL)
+		return TIOCSER_TEMT;
+	else
+		return 0;
+}
+
+static void efm32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	/* sorry, neither handshaking lines nor loop functionallity */
+}
+
+static unsigned int efm32_uart_get_mctrl(struct uart_port *port)
+{
+	/* sorry, no handshaking lines available */
+	return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
+}
+
+static void efm32_uart_stop_tx(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	u32 ien = efm32_uart_read32(efm_port,  UARTn_IEN);
+
+	efm32_uart_write32(efm_port, UARTn_CMD_TXDIS, UARTn_CMD);
+	efm32_uart_write32(efm_port,
+			ien & ~(UARTn_IF_TXC | UARTn_IF_TXBL), UARTn_IEN);
+}
+
+static void efm32_uart_tx_chars(struct efm32_uart_port *efm_port)
+{
+	struct uart_port *port = &efm_port->port;
+	struct circ_buf *xmit = &port->state->xmit;
+
+	while (efm32_uart_read32(efm_port, UARTn_STATUS) &
+			UARTn_STATUS_TXBL) {
+		if (port->x_char) {
+			port->icount.tx++;
+			efm32_uart_write32(efm_port, port->x_char,
+					UARTn_TXDATA);
+			port->x_char = 0;
+			continue;
+		}
+		if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) {
+			port->icount.tx++;
+			efm32_uart_write32(efm_port, xmit->buf[xmit->tail],
+					UARTn_TXDATA);
+			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		} else
+			break;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (!port->x_char && uart_circ_empty(xmit) &&
+			efm32_uart_read32(efm_port, UARTn_STATUS) &
+				UARTn_STATUS_TXC)
+		efm32_uart_stop_tx(port);
+}
+
+static void efm32_uart_start_tx(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	u32 ien;
+
+	efm32_uart_write32(efm_port,
+			UARTn_IF_TXBL | UARTn_IF_TXC, UARTn_IFC);
+	ien = efm32_uart_read32(efm_port,  UARTn_IEN);
+	efm32_uart_write32(efm_port,
+			ien | UARTn_IF_TXBL | UARTn_IF_TXC, UARTn_IEN);
+	efm32_uart_write32(efm_port, UARTn_CMD_TXEN, UARTn_CMD);
+
+	efm32_uart_tx_chars(efm_port);
+}
+
+static void efm32_uart_stop_rx(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+
+	efm32_uart_write32(efm_port, UARTn_CMD_RXDIS, UARTn_CMD);
+}
+
+static void efm32_uart_enable_ms(struct uart_port *port)
+{
+	/* no handshake lines, no modem status interrupts */
+}
+
+static void efm32_uart_break_ctl(struct uart_port *port, int ctl)
+{
+	/* not possible without fiddling with gpios */
+}
+
+static void efm32_uart_rx_chars(struct efm32_uart_port *efm_port)
+{
+	struct uart_port *port = &efm_port->port;
+	struct tty_struct *tty = tty_port_tty_get(&port->state->port);
+
+	spin_lock(&port->lock);
+
+	while (efm32_uart_read32(efm_port, UARTn_STATUS) &
+			UARTn_STATUS_RXDATAV) {
+		u32 rxdata = efm32_uart_read32(efm_port, UARTn_RXDATAX);
+		int flag = 0;
+
+		port->icount.rx++;
+
+		/* XXX detect BREAK and overrun */
+
+		if (rxdata & UARTn_RXDATAX_PERR) {
+			port->icount.parity++;
+			if (port->read_status_mask & UARTn_RXDATAX_PERR)
+				flag = TTY_PARITY;
+		} else if (rxdata & UARTn_RXDATAX_FERR) {
+			port->icount.frame++;
+			if (port->read_status_mask & UARTn_RXDATAX_FERR)
+				flag = TTY_FRAME;
+		}
+
+		if (rxdata & port->ignore_status_mask)
+			continue;
+
+		if (tty)
+			tty_insert_flip_char(tty, rxdata, flag);
+	}
+	spin_unlock(&port->lock);
+
+	if (tty) {
+		tty_flip_buffer_push(tty);
+		tty_kref_put(tty);
+	}
+}
+
+static irqreturn_t efm32_uart_rxirq(int irq, void *data)
+{
+	struct efm32_uart_port *efm_port = data;
+	u32 irqflag = efm32_uart_read32(efm_port, UARTn_IF);
+
+	if (irqflag & UARTn_IF_RXDATAV) {
+		efm32_uart_write32(efm_port, UARTn_IF_RXDATAV, UARTn_IFC);
+		efm32_uart_rx_chars(efm_port);
+
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+static irqreturn_t efm32_uart_txirq(int irq, void *data)
+{
+	struct efm32_uart_port *efm_port = data;
+	u32 irqflag = efm32_uart_read32(efm_port, UARTn_IF);
+
+	/* TXBL doesn't need to be cleared */
+	if (irqflag & UARTn_IF_TXC)
+		efm32_uart_write32(efm_port, UARTn_IF_TXC, UARTn_IFC);
+
+	if (irqflag & (UARTn_IF_TXC | UARTn_IF_TXBL)) {
+		efm32_uart_tx_chars(efm_port);
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+static int efm32_uart_startup(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	int ret;
+
+	ret = clk_enable(efm_port->clk);
+	if (ret) {
+		efm_debug(efm_port, "failed to enable clk\n");
+		goto err_clk_enable;
+	}
+	port->uartclk = clk_get_rate(efm_port->clk);
+
+	/* Enable pins at default location */
+	efm32_uart_write32(efm_port, UARTn_ROUTE_RXPEN | UARTn_ROUTE_TXPEN,
+			UARTn_ROUTE);
+
+	ret = request_irq(port->irq, efm32_uart_rxirq, 0,
+			DRIVER_NAME, efm_port);
+	if (ret) {
+		efm_debug(efm_port, "failed to register rxirq\n");
+		goto err_request_irq_rx;
+	}
+
+	/* disable all irqs */
+	efm32_uart_write32(efm_port, 0, UARTn_IEN);
+
+	ret = request_irq(efm_port->txirq, efm32_uart_txirq, 0,
+			DRIVER_NAME, efm_port);
+	if (ret) {
+		efm_debug(efm_port, "failed to register txirq\n");
+		free_irq(port->irq, efm_port);
+err_request_irq_rx:
+
+		clk_disable(efm_port->clk);
+	} else {
+		efm32_uart_write32(efm_port, UARTn_IF_RXDATAV, UARTn_IEN);
+		efm32_uart_write32(efm_port, UARTn_CMD_RXEN, UARTn_CMD);
+	}
+
+err_clk_enable:
+	return ret;
+}
+
+static void efm32_uart_shutdown(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+
+	efm32_uart_write32(efm_port, 0, UARTn_IEN);
+	free_irq(port->irq, efm_port);
+
+	clk_disable(efm_port->clk);
+}
+
+static void efm32_uart_set_termios(struct uart_port *port,
+		struct ktermios *new, struct ktermios *old)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	unsigned long flags;
+	unsigned baud;
+	u32 clkdiv;
+	u32 frame = 0;
+
+	/* no modem control lines */
+	new->c_cflag &= ~(CRTSCTS | CMSPAR);
+
+	baud = uart_get_baud_rate(port, new, old,
+			DIV_ROUND_CLOSEST(port->uartclk, 16 * 8192),
+			DIV_ROUND_CLOSEST(port->uartclk, 16));
+
+	switch (new->c_cflag & CSIZE) {
+	case CS5:
+		frame |= UARTn_FRAME_DATABITS(5);
+		break;
+	case CS6:
+		frame |= UARTn_FRAME_DATABITS(6);
+		break;
+	case CS7:
+		frame |= UARTn_FRAME_DATABITS(7);
+		break;
+	case CS8:
+		frame |= UARTn_FRAME_DATABITS(8);
+		break;
+	}
+
+	/* tx only supports a "half" stop bit */
+	new->c_cflag |= CSTOPB;
+	frame |= UARTn_FRAME_STOPBITS_ONE;
+
+	if (new->c_cflag & PARENB) {
+		if (new->c_cflag & PARODD)
+			frame |= UARTn_FRAME_PARITY_ODD;
+		else
+			frame |= UARTn_FRAME_PARITY_EVEN;
+	} else
+		frame |= UARTn_FRAME_PARITY_NONE;
+
+	/*
+	 * the 6 lowest bits of CLKDIV are dc, bit 6 has value 0.25.
+	 * port->uartclk <= 14e6, so 4 * port->uartclk doesn't overflow.
+	 */
+	clkdiv = (DIV_ROUND_CLOSEST(4 * port->uartclk, 16 * baud) - 4) << 6;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	efm32_uart_write32(efm_port,
+			UARTn_CMD_TXDIS | UARTn_CMD_RXDIS, UARTn_CMD);
+
+	port->read_status_mask = 0;
+	port->ignore_status_mask = 0;
+
+	uart_update_timeout(port, new->c_cflag, baud);
+
+	efm32_uart_write32(efm_port, UARTn_CTRL_TXBIL, UARTn_CTRL);
+	efm32_uart_write32(efm_port, frame, UARTn_FRAME);
+	efm32_uart_write32(efm_port, clkdiv, UARTn_CLKDIV);
+
+	efm32_uart_write32(efm_port, UARTn_CMD_TXEN | UARTn_CMD_RXEN,
+			UARTn_CMD);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *efm32_uart_type(struct uart_port *port)
+{
+	return port->type == PORT_EFMUART ? "efm32-uart" : NULL;
+}
+
+static void efm32_uart_release_port(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+
+	clk_unprepare(efm_port->clk);
+	clk_put(efm_port->clk);
+	iounmap(port->membase);
+}
+
+static int efm32_uart_request_port(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	int ret;
+
+	port->membase = ioremap(port->mapbase, 60);
+	if (!efm_port->port.membase) {
+		ret = -ENOMEM;
+		efm_debug(efm_port, "failed to remap\n");
+		goto err_ioremap;
+	}
+
+	efm_port->clk = clk_get(port->dev, NULL);
+	if (IS_ERR(efm_port->clk)) {
+		ret = PTR_ERR(efm_port->clk);
+		efm_debug(efm_port, "failed to get clock\n");
+		goto err_clk_get;
+	}
+
+	ret = clk_prepare(efm_port->clk);
+	if (ret) {
+		clk_put(efm_port->clk);
+err_clk_get:
+
+		iounmap(port->membase);
+err_ioremap:
+		return ret;
+	}
+	return 0;
+}
+
+static void efm32_uart_config_port(struct uart_port *port, int type)
+{
+	if (type & UART_CONFIG_TYPE &&
+			!efm32_uart_request_port(port))
+		port->type = PORT_EFMUART;
+}
+
+static int efm32_uart_verify_port(struct uart_port *port,
+		struct serial_struct *serinfo)
+{
+	int ret = 0;
+
+	if (serinfo->type != PORT_UNKNOWN && serinfo->type != PORT_EFMUART)
+		ret = -EINVAL;
+
+	return ret;
+}
+
+static struct uart_ops efm32_uart_pops = {
+	.tx_empty = efm32_uart_tx_empty,
+	.set_mctrl = efm32_uart_set_mctrl,
+	.get_mctrl = efm32_uart_get_mctrl,
+	.stop_tx = efm32_uart_stop_tx,
+	.start_tx = efm32_uart_start_tx,
+	.stop_rx = efm32_uart_stop_rx,
+	.enable_ms = efm32_uart_enable_ms,
+	.break_ctl = efm32_uart_break_ctl,
+	.startup = efm32_uart_startup,
+	.shutdown = efm32_uart_shutdown,
+	.set_termios = efm32_uart_set_termios,
+	.type = efm32_uart_type,
+	.release_port = efm32_uart_release_port,
+	.request_port = efm32_uart_request_port,
+	.config_port = efm32_uart_config_port,
+	.verify_port = efm32_uart_verify_port,
+};
+
+static struct efm32_uart_port *efm32_uart_ports[3];
+
+#ifdef CONFIG_SERIAL_EFM32_UART_CONSOLE
+static void efm32_uart_console_putchar(struct uart_port *port, int ch)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	unsigned int timeout = 0x400;
+	u32 status;
+
+	while (1) {
+		status = efm32_uart_read32(efm_port, UARTn_STATUS);
+
+		if (status & UARTn_STATUS_TXBL)
+			break;
+		if (!timeout--)
+			return;
+	}
+	efm32_uart_write32(efm_port, ch, UARTn_TXDATA);
+}
+
+static void efm32_uart_console_write(struct console *co, const char *s,
+		unsigned int count)
+{
+	struct efm32_uart_port *efm_port = efm32_uart_ports[co->index];
+	u32 status = efm32_uart_read32(efm_port, UARTn_STATUS);
+	unsigned int timeout = 0x400;
+
+	if (!(status & UARTn_STATUS_TXENS))
+		efm32_uart_write32(efm_port, UARTn_CMD_TXEN, UARTn_CMD);
+
+	uart_console_write(&efm_port->port, s, count,
+			efm32_uart_console_putchar);
+
+	/* Wait for the transmitter to become empty */
+	while (1) {
+		u32 status = efm32_uart_read32(efm_port, UARTn_STATUS);
+		if (status & UARTn_STATUS_TXC)
+			break;
+		if (!timeout--)
+			break;
+	}
+
+	if (!(status & UARTn_STATUS_TXENS))
+		efm32_uart_write32(efm_port, UARTn_CMD_TXDIS, UARTn_CMD);
+}
+
+static void efm32_uart_console_get_options(struct efm32_uart_port *efm_port,
+		int *baud, int *parity, int *bits)
+{
+	u32 ctrl = efm32_uart_read32(efm_port, UARTn_CTRL);
+	u32 route, clkdiv, frame;
+
+	if (ctrl & UARTn_CTRL_SYNC)
+		/* not operating in async mode */
+		return;
+
+	route = efm32_uart_read32(efm_port, UARTn_ROUTE);
+	if (!(route & UARTn_ROUTE_TXPEN))
+		/* tx pin not routed */
+		return;
+
+	clkdiv = efm32_uart_read32(efm_port, UARTn_CLKDIV);
+
+	*baud = DIV_ROUND_CLOSEST(4 * efm_port->port.uartclk,
+			16 * (4 + (clkdiv >> 6)));
+
+	frame = efm32_uart_read32(efm_port, UARTn_FRAME);
+	if (frame & UARTn_FRAME_PARITY_ODD)
+		*parity = 'o';
+	else if (frame & UARTn_FRAME_PARITY_EVEN)
+		*parity = 'e';
+	else
+		*parity = 'n';
+
+	*bits = (frame & UARTn_FRAME_DATABITS__MASK) -
+			UARTn_FRAME_DATABITS(4) + 4;
+
+	efm_debug(efm_port, "get_opts: options=%d%c%d\n",
+			*baud, *parity, *bits);
+}
+
+static int efm32_uart_console_setup(struct console *co, char *options)
+{
+	struct efm32_uart_port *efm_port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret;
+
+	if (co->index < 0 || co->index >= ARRAY_SIZE(efm32_uart_ports)) {
+		unsigned i;
+		for (i = 0; i < ARRAY_SIZE(efm32_uart_ports); ++i) {
+			if (efm32_uart_ports[i]) {
+				pr_warn("efm32-console: fall back to console index %u (from %hhi)\n",
+						i, co->index);
+				co->index = i;
+				break;
+			}
+		}
+	}
+
+	efm_port = efm32_uart_ports[co->index];
+	if (!efm_port) {
+		pr_warn("efm32-console: No port at %d\n", co->index);
+		return -ENODEV;
+	}
+
+	ret = clk_prepare(efm_port->clk);
+	if (ret) {
+		dev_warn(efm_port->port.dev,
+				"console: clk_prepare failed: %d\n", ret);
+		return ret;
+	}
+
+	efm_port->port.uartclk = clk_get_rate(efm_port->clk);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		efm32_uart_console_get_options(efm_port,
+				&baud, &parity, &bits);
+
+	return uart_set_options(&efm_port->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver efm32_uart_reg;
+
+static struct console efm32_uart_console = {
+	.name = DEV_NAME,
+	.write = efm32_uart_console_write,
+	.device = uart_console_device,
+	.setup = efm32_uart_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &efm32_uart_reg,
+};
+
+#else
+#define efm32_uart_console (*(struct console *)NULL)
+#endif /* ifdef CONFIG_SERIAL_EFM32_UART_CONSOLE / else */
+
+static struct uart_driver efm32_uart_reg = {
+	.owner = THIS_MODULE,
+	.driver_name = DRIVER_NAME,
+	.dev_name = DEV_NAME,
+	.nr = ARRAY_SIZE(efm32_uart_ports),
+	.cons = &efm32_uart_console,
+};
+
+static int efm32_uart_probe_dt(struct platform_device *pdev,
+		struct efm32_uart_port *efm_port)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int ret;
+
+	if (!np)
+		return 1;
+
+	ret = of_alias_get_id(np, "serial");
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to get alias id: %d\n", ret);
+		return ret;
+	} else {
+		efm_port->port.line = ret;
+		return 0;
+	}
+
+}
+
+static int __devinit efm32_uart_probe(struct platform_device *pdev)
+{
+	struct efm32_uart_port *efm_port;
+	struct resource *res;
+	int ret;
+
+	efm_port = kzalloc(sizeof(*efm_port), GFP_KERNEL);
+	if (!efm_port) {
+		dev_dbg(&pdev->dev, "failed to allocate private data\n");
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENODEV;
+		dev_dbg(&pdev->dev, "failed to determine base address\n");
+		goto err_get_base;
+	}
+
+	if (resource_size(res) < 60) {
+		ret = -EINVAL;
+		dev_dbg(&pdev->dev, "memory resource too small\n");
+		goto err_too_small;
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret <= 0) {
+		dev_dbg(&pdev->dev, "failed to get rx irq\n");
+		goto err_get_rxirq;
+	}
+
+	efm_port->port.irq = ret;
+
+	ret = platform_get_irq(pdev, 1);
+	if (ret <= 0)
+		ret = efm_port->port.irq + 1;
+
+	efm_port->txirq = ret;
+
+	efm_port->port.dev = &pdev->dev;
+	efm_port->port.mapbase = res->start;
+	efm_port->port.type = PORT_EFMUART;
+	efm_port->port.iotype = UPIO_MEM32;
+	efm_port->port.fifosize = 2;
+	efm_port->port.ops = &efm32_uart_pops;
+	efm_port->port.flags = UPF_BOOT_AUTOCONF;
+
+	ret = efm32_uart_probe_dt(pdev, efm_port);
+	if (ret > 0)
+		/* not created by device tree */
+		efm_port->port.line = pdev->id;
+
+	if (efm_port->port.line >= 0 &&
+			efm_port->port.line < ARRAY_SIZE(efm32_uart_ports))
+		efm32_uart_ports[efm_port->port.line] = efm_port;
+
+	ret = uart_add_one_port(&efm32_uart_reg, &efm_port->port);
+	if (ret) {
+		dev_dbg(&pdev->dev, "failed to add port: %d\n", ret);
+
+		if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports))
+			efm32_uart_ports[pdev->id] = NULL;
+err_get_rxirq:
+err_too_small:
+err_get_base:
+		kfree(efm_port);
+	} else {
+		platform_set_drvdata(pdev, efm_port);
+		dev_dbg(&pdev->dev, "\\o/\n");
+	}
+
+	return ret;
+}
+
+static int __devexit efm32_uart_remove(struct platform_device *pdev)
+{
+	struct efm32_uart_port *efm_port = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	uart_remove_one_port(&efm32_uart_reg, &efm_port->port);
+
+	if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports))
+		efm32_uart_ports[pdev->id] = NULL;
+
+	kfree(efm_port);
+
+	return 0;
+}
+
+static struct of_device_id efm32_uart_dt_ids[] = {
+	{
+		.compatible = "efm32,uart",
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, efm32_uart_dt_ids);
+
+static struct platform_driver efm32_uart_driver = {
+	.probe = efm32_uart_probe,
+	.remove = __devexit_p(efm32_uart_remove),
+
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = efm32_uart_dt_ids,
+	},
+};
+
+static int __init efm32_uart_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&efm32_uart_reg);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&efm32_uart_driver);
+	if (ret)
+		uart_unregister_driver(&efm32_uart_reg);
+
+	pr_info("EFM32 UART/USART driver\n");
+
+	return ret;
+}
+module_init(efm32_uart_init);
+
+static void __exit efm32_uart_exit(void)
+{
+	platform_driver_unregister(&efm32_uart_driver);
+	uart_unregister_driver(&efm32_uart_reg);
+}
+
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_DESCRIPTION("EFM32 UART/USART driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index eadf33d..d3999ef 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -207,6 +207,8 @@
 /* Xilinx PSS UART */
 #define PORT_XUARTPS	98
 
+#define PORT_EFMUART   99
+
 #ifdef __KERNEL__
 
 #include <linux/compiler.h>
-- 
1.7.7.3


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH v3] serial/efm32: add new driver
  2012-01-09 16:44     ` [PATCH v3] " Uwe Kleine-König
@ 2012-01-24 22:05       ` Greg KH
  2012-01-25  8:05         ` [PATCH v4] " Uwe Kleine-König
  0 siblings, 1 reply; 17+ messages in thread
From: Greg KH @ 2012-01-24 22:05 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-kernel, Alan Cox, kernel, devicetree-discuss,
	Greg Kroah-Hartman, linux-serial

On Mon, Jan 09, 2012 at 05:44:07PM +0100, Uwe Kleine-König wrote:
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> ---
> Hello,
> 
> changes since v2:
> - use {read,write}l_relaxed
> - rename driver to efm32-uart as USARTs and UARTs can be handled both
>   with it
> - disable TX in .stop_tx (which needs some changes related to
>   USARTn_IF_TXC)
> 
> Best regards
> Uwe
> 
>  .../devicetree/bindings/tty/serial/efm32-uart.txt  |   14 +
>  arch/arm/boot/dts/efm32gg-dk3750.dts               |    2 +-
>  arch/arm/mach-efm32/devices/pdev-efm32-usart.c     |    6 +-

This file isn't in 3.3-rc1, so I can't apply this patch :(

What tree is it supposed to be against?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v4] serial/efm32: add new driver
  2012-01-24 22:05       ` Greg KH
@ 2012-01-25  8:05         ` Uwe Kleine-König
  2012-01-25  8:25           ` Joe Perches
  2012-01-25 15:52           ` Greg KH
  0 siblings, 2 replies; 17+ messages in thread
From: Uwe Kleine-König @ 2012-01-25  8:05 UTC (permalink / raw)
  To: Greg KH
  Cc: linux-kernel, Alan Cox, kernel, devicetree-discuss,
	Greg Kroah-Hartman, linux-serial

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
changes since v3 (id:1326127447-20284-1-git-send-email-u.kleine-koenig@pengutronix.de)
 - increase number of ports to support UART additionally to USART ports
 - implement choosing route location
 - implement setting stopbit config
   (before one stop bit was configured and CSTOPB was set unconditionally which
   is wrong. Spotted by Russell King.)
 - implement break, sysrq and overflow detection
 - drop unrelated changing to make patch apply on v3.3-rc1

Hello Greg,

On Tue, Jan 24, 2012 at 02:05:22PM -0800, Greg KH wrote:
> On Mon, Jan 09, 2012 at 05:44:07PM +0100, Uwe Kleine-König wrote:
> > Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > ---
> > Hello,
> > 
> > changes since v2:
> > - use {read,write}l_relaxed
> > - rename driver to efm32-uart as USARTs and UARTs can be handled both
> >   with it
> > - disable TX in .stop_tx (which needs some changes related to
> >   USARTn_IF_TXC)
> > 
> > Best regards
> > Uwe
> > 
> >  .../devicetree/bindings/tty/serial/efm32-uart.txt  |   14 +
> >  arch/arm/boot/dts/efm32gg-dk3750.dts               |    2 +-
> >  arch/arm/mach-efm32/devices/pdev-efm32-usart.c     |    6 +-
> 
> This file isn't in 3.3-rc1, so I can't apply this patch :(
> 
> What tree is it supposed to be against?
Sorry, I messed this up, it bases on my private development tree. These
two changes should go independant of the driver patch via an ARM tree
(if at all).

I added a few more features in the meantime, so the options are:
 - take v3 simply droping the hunks changing
   arch/arm/boot/dts/efm32gg-dk3750.dts and
   arch/arm/mach-efm32/devices/pdev-efm32-usart.c
 - take this for 3.3
 - take this for 3.4

My preference would be of course the second option, but for me the other two
would be OK if you don't like taking the update that late, too.

Thanks
Uwe

---
 .../devicetree/bindings/tty/serial/efm32-uart.txt  |   14 +
 drivers/tty/serial/Kconfig                         |   13 +
 drivers/tty/serial/Makefile                        |    1 +
 drivers/tty/serial/efm32-uart.c                    |  830 ++++++++++++++++++++
 include/linux/platform_data/efm32-uart.h           |   18 +
 include/linux/serial_core.h                        |    2 +
 6 files changed, 878 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/tty/serial/efm32-uart.txt
 create mode 100644 drivers/tty/serial/efm32-uart.c
 create mode 100644 include/linux/platform_data/efm32-uart.h

diff --git a/Documentation/devicetree/bindings/tty/serial/efm32-uart.txt b/Documentation/devicetree/bindings/tty/serial/efm32-uart.txt
new file mode 100644
index 0000000..6588b69
--- /dev/null
+++ b/Documentation/devicetree/bindings/tty/serial/efm32-uart.txt
@@ -0,0 +1,14 @@
+* Energymicro efm32 UART
+
+Required properties:
+- compatible : Should be "efm32,uart"
+- reg : Address and length of the register set
+- interrupts : Should contain uart interrupt
+
+Example:
+
+uart@0x4000c400 {
+	compatible = "efm32,uart";
+	reg = <0x4000c400 0x400>;
+	interrupts = <15>;
+};
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index aca2386..6e24a8f 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1628,4 +1628,17 @@ config SERIAL_AR933X_NR_UARTS
 	  Set this to the number of serial ports you want the driver
 	  to support.
 
+config SERIAL_EFM32_UART
+	tristate "EFM32 UART/USART port."
+	depends on ARCH_EFM32
+	select SERIAL_CORE
+	help
+	  This driver support the USART and UART ports on
+	  Energy Micro's efm32 SoCs.
+
+config SERIAL_EFM32_UART_CONSOLE
+	bool "EFM32 UART/USART console support"
+	depends on SERIAL_EFM32_UART=y
+	select SERIAL_CORE_CONSOLE
+
 endmenu
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index f5b01f2..1997ad4 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -92,3 +92,4 @@ obj-$(CONFIG_SERIAL_LANTIQ)	+= lantiq.o
 obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o
 obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o
 obj-$(CONFIG_SERIAL_AR933X)   += ar933x_uart.o
+obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o
diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c
new file mode 100644
index 0000000..615e464
--- /dev/null
+++ b/drivers/tty/serial/efm32-uart.c
@@ -0,0 +1,830 @@
+#if defined(CONFIG_SERIAL_EFM32_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial_core.h>
+#include <linux/tty_flip.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <linux/platform_data/efm32-uart.h>
+
+#define DRIVER_NAME "efm32-uart"
+#define DEV_NAME "ttyefm"
+
+#define UARTn_CTRL		0x00
+#define UARTn_CTRL_SYNC		0x0001
+#define UARTn_CTRL_TXBIL		0x1000
+
+#define UARTn_FRAME		0x04
+#define UARTn_FRAME_DATABITS__MASK	0x000f
+#define UARTn_FRAME_DATABITS(n)		((n) - 3)
+#define UARTn_FRAME_PARITY_NONE		0x0000
+#define UARTn_FRAME_PARITY_EVEN		0x0200
+#define UARTn_FRAME_PARITY_ODD		0x0300
+#define UARTn_FRAME_STOPBITS_HALF	0x0000
+#define UARTn_FRAME_STOPBITS_ONE	0x1000
+#define UARTn_FRAME_STOPBITS_TWO	0x3000
+
+#define UARTn_CMD		0x0c
+#define UARTn_CMD_RXEN			0x0001
+#define UARTn_CMD_RXDIS		0x0002
+#define UARTn_CMD_TXEN			0x0004
+#define UARTn_CMD_TXDIS		0x0008
+
+#define UARTn_STATUS		0x10
+#define UARTn_STATUS_TXENS		0x0002
+#define UARTn_STATUS_TXC		0x0020
+#define UARTn_STATUS_TXBL		0x0040
+#define UARTn_STATUS_RXDATAV		0x0080
+
+#define UARTn_CLKDIV		0x14
+
+#define UARTn_RXDATAX		0x18
+#define UARTn_RXDATAX_RXDATA__MASK	0x01ff
+#define UARTn_RXDATAX_PERR		0x4000
+#define UARTn_RXDATAX_FERR		0x8000
+/*
+ * This is a software only flag used for ignore_status_mask and
+ * read_status_mask! It's used for breaks that the hardware doesn't report
+ * explicitly.
+ */
+#define SW_UARTn_RXDATAX_BERR		0x2000
+
+#define UARTn_TXDATA		0x34
+
+#define UARTn_IF		0x40
+#define UARTn_IF_TXC			0x0001
+#define UARTn_IF_TXBL			0x0002
+#define UARTn_IF_RXDATAV		0x0004
+#define UARTn_IF_RXOF			0x0010
+
+#define UARTn_IFS		0x44
+#define UARTn_IFC		0x48
+#define UARTn_IEN		0x4c
+
+#define UARTn_ROUTE		0x54
+#define UARTn_ROUTE_LOCATION__MASK	0x0700
+#define UARTn_ROUTE_LOCATION(n)		(((n) << 8) & UARTn_ROUTE_LOCATION__MASK)
+#define UARTn_ROUTE_RXPEN		0x0001
+#define UARTn_ROUTE_TXPEN		0x0002
+
+struct efm32_uart_port {
+	struct uart_port port;
+	unsigned int txirq;
+	struct clk *clk;
+};
+#define to_efm_port(_port) container_of(_port, struct efm32_uart_port, port)
+#define efm_debug(efm_port, format, arg...)			\
+	dev_dbg(efm_port->port.dev, format, ##arg)
+
+static void efm32_uart_write32(struct efm32_uart_port *efm_port,
+		u32 value, unsigned offset)
+{
+	writel_relaxed(value, efm_port->port.membase + offset);
+}
+
+static u32 efm32_uart_read32(struct efm32_uart_port *efm_port,
+		unsigned offset)
+{
+	return readl_relaxed(efm_port->port.membase + offset);
+}
+
+static unsigned int efm32_uart_tx_empty(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	u32 status = efm32_uart_read32(efm_port, UARTn_STATUS);
+
+	if (status & UARTn_STATUS_TXC)
+		return TIOCSER_TEMT;
+	else
+		return 0;
+}
+
+static void efm32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	/* sorry, neither handshaking lines nor loop functionallity */
+}
+
+static unsigned int efm32_uart_get_mctrl(struct uart_port *port)
+{
+	/* sorry, no handshaking lines available */
+	return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
+}
+
+static void efm32_uart_stop_tx(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	u32 ien = efm32_uart_read32(efm_port,  UARTn_IEN);
+
+	efm32_uart_write32(efm_port, UARTn_CMD_TXDIS, UARTn_CMD);
+	ien &= ~(UARTn_IF_TXC | UARTn_IF_TXBL);
+	efm32_uart_write32(efm_port, ien, UARTn_IEN);
+}
+
+static void efm32_uart_tx_chars(struct efm32_uart_port *efm_port)
+{
+	struct uart_port *port = &efm_port->port;
+	struct circ_buf *xmit = &port->state->xmit;
+
+	while (efm32_uart_read32(efm_port, UARTn_STATUS) &
+			UARTn_STATUS_TXBL) {
+		if (port->x_char) {
+			port->icount.tx++;
+			efm32_uart_write32(efm_port, port->x_char,
+					UARTn_TXDATA);
+			port->x_char = 0;
+			continue;
+		}
+		if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) {
+			port->icount.tx++;
+			efm32_uart_write32(efm_port, xmit->buf[xmit->tail],
+					UARTn_TXDATA);
+			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		} else
+			break;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (!port->x_char && uart_circ_empty(xmit) &&
+			efm32_uart_read32(efm_port, UARTn_STATUS) &
+				UARTn_STATUS_TXC)
+		efm32_uart_stop_tx(port);
+}
+
+static void efm32_uart_start_tx(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	u32 ien;
+
+	efm32_uart_write32(efm_port,
+			UARTn_IF_TXBL | UARTn_IF_TXC, UARTn_IFC);
+	ien = efm32_uart_read32(efm_port, UARTn_IEN);
+	efm32_uart_write32(efm_port,
+			ien | UARTn_IF_TXBL | UARTn_IF_TXC, UARTn_IEN);
+	efm32_uart_write32(efm_port, UARTn_CMD_TXEN, UARTn_CMD);
+
+	efm32_uart_tx_chars(efm_port);
+}
+
+static void efm32_uart_stop_rx(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+
+	efm32_uart_write32(efm_port, UARTn_CMD_RXDIS, UARTn_CMD);
+}
+
+static void efm32_uart_enable_ms(struct uart_port *port)
+{
+	/* no handshake lines, no modem status interrupts */
+}
+
+static void efm32_uart_break_ctl(struct uart_port *port, int ctl)
+{
+	/* not possible without fiddling with gpios */
+}
+
+static void efm32_uart_rx_chars(struct efm32_uart_port *efm_port,
+		struct tty_struct *tty)
+{
+	struct uart_port *port = &efm_port->port;
+
+	while (efm32_uart_read32(efm_port, UARTn_STATUS) &
+			UARTn_STATUS_RXDATAV) {
+		u32 rxdata = efm32_uart_read32(efm_port, UARTn_RXDATAX);
+		int flag = 0;
+
+		/*
+		 * This is a reserved bit and I only saw it read as 0. But to be
+		 * sure not to be confused too much by new devices adhere to the
+		 * warning in the reference manual that reserverd bits might
+		 * read as 1 in the future.
+		 */
+		rxdata &= ~SW_UARTn_RXDATAX_BERR;
+
+		port->icount.rx++;
+
+		if ((rxdata & UARTn_RXDATAX_FERR) &&
+				!(rxdata & UARTn_RXDATAX_RXDATA__MASK)) {
+			rxdata |= SW_UARTn_RXDATAX_BERR;
+			port->icount.brk++;
+			if (uart_handle_break(port))
+				continue;
+		} else if (rxdata & UARTn_RXDATAX_PERR)
+			port->icount.parity++;
+		else if (rxdata & UARTn_RXDATAX_FERR)
+			port->icount.frame++;
+
+		rxdata &= port->read_status_mask;
+
+		if (rxdata & SW_UARTn_RXDATAX_BERR)
+			flag = TTY_BREAK;
+		else if (rxdata & UARTn_RXDATAX_PERR)
+			flag = TTY_PARITY;
+		else if (rxdata & UARTn_RXDATAX_FERR)
+			flag = TTY_FRAME;
+		else if (uart_handle_sysrq_char(port,
+					rxdata & UARTn_RXDATAX_RXDATA__MASK))
+			continue;
+
+		if (tty && (rxdata & port->ignore_status_mask) == 0)
+			tty_insert_flip_char(tty,
+					rxdata & UARTn_RXDATAX_RXDATA__MASK, flag);
+	}
+}
+
+static irqreturn_t efm32_uart_rxirq(int irq, void *data)
+{
+	struct efm32_uart_port *efm_port = data;
+	u32 irqflag = efm32_uart_read32(efm_port, UARTn_IF);
+	int handled = IRQ_NONE;
+	struct uart_port *port = &efm_port->port;
+	struct tty_struct *tty;
+
+	spin_lock(&port->lock);
+
+	tty = tty_kref_get(port->state->port.tty);
+
+	if (irqflag & UARTn_IF_RXDATAV) {
+		efm32_uart_write32(efm_port, UARTn_IF_RXDATAV, UARTn_IFC);
+		efm32_uart_rx_chars(efm_port, tty);
+
+		handled = IRQ_HANDLED;
+	}
+
+	if (irqflag & UARTn_IF_RXOF) {
+		efm32_uart_write32(efm_port, UARTn_IF_RXOF, UARTn_IFC);
+		port->icount.overrun++;
+		if (tty)
+			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+
+		handled = IRQ_HANDLED;
+	}
+
+	if (tty) {
+		tty_flip_buffer_push(tty);
+		tty_kref_put(tty);
+	}
+
+	spin_unlock(&port->lock);
+
+	return handled;
+}
+
+static irqreturn_t efm32_uart_txirq(int irq, void *data)
+{
+	struct efm32_uart_port *efm_port = data;
+	u32 irqflag = efm32_uart_read32(efm_port, UARTn_IF);
+
+	/* TXBL doesn't need to be cleared */
+	if (irqflag & UARTn_IF_TXC)
+		efm32_uart_write32(efm_port, UARTn_IF_TXC, UARTn_IFC);
+
+	if (irqflag & (UARTn_IF_TXC | UARTn_IF_TXBL)) {
+		efm32_uart_tx_chars(efm_port);
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+static int efm32_uart_startup(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	u32 location = 0;
+	struct efm32_uart_pdata *pdata = dev_get_platdata(port->dev);
+	int ret;
+
+	if (pdata)
+		location = UARTn_ROUTE_LOCATION(pdata->location);
+
+	ret = clk_enable(efm_port->clk);
+	if (ret) {
+		efm_debug(efm_port, "failed to enable clk\n");
+		goto err_clk_enable;
+	}
+	port->uartclk = clk_get_rate(efm_port->clk);
+
+	/* Enable pins at configured location */
+	efm32_uart_write32(efm_port, location | UARTn_ROUTE_RXPEN | UARTn_ROUTE_TXPEN,
+			UARTn_ROUTE);
+
+	ret = request_irq(port->irq, efm32_uart_rxirq, 0,
+			DRIVER_NAME, efm_port);
+	if (ret) {
+		efm_debug(efm_port, "failed to register rxirq\n");
+		goto err_request_irq_rx;
+	}
+
+	/* disable all irqs */
+	efm32_uart_write32(efm_port, 0, UARTn_IEN);
+
+	ret = request_irq(efm_port->txirq, efm32_uart_txirq, 0,
+			DRIVER_NAME, efm_port);
+	if (ret) {
+		efm_debug(efm_port, "failed to register txirq\n");
+		free_irq(port->irq, efm_port);
+err_request_irq_rx:
+
+		clk_disable(efm_port->clk);
+	} else {
+		efm32_uart_write32(efm_port,
+				UARTn_IF_RXDATAV | UARTn_IF_RXOF, UARTn_IEN);
+		efm32_uart_write32(efm_port, UARTn_CMD_RXEN, UARTn_CMD);
+	}
+
+err_clk_enable:
+	return ret;
+}
+
+static void efm32_uart_shutdown(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+
+	efm32_uart_write32(efm_port, 0, UARTn_IEN);
+	free_irq(port->irq, efm_port);
+
+	clk_disable(efm_port->clk);
+}
+
+static void efm32_uart_set_termios(struct uart_port *port,
+		struct ktermios *new, struct ktermios *old)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	unsigned long flags;
+	unsigned baud;
+	u32 clkdiv;
+	u32 frame = 0;
+
+	/* no modem control lines */
+	new->c_cflag &= ~(CRTSCTS | CMSPAR);
+
+	baud = uart_get_baud_rate(port, new, old,
+			DIV_ROUND_CLOSEST(port->uartclk, 16 * 8192),
+			DIV_ROUND_CLOSEST(port->uartclk, 16));
+
+	switch (new->c_cflag & CSIZE) {
+	case CS5:
+		frame |= UARTn_FRAME_DATABITS(5);
+		break;
+	case CS6:
+		frame |= UARTn_FRAME_DATABITS(6);
+		break;
+	case CS7:
+		frame |= UARTn_FRAME_DATABITS(7);
+		break;
+	case CS8:
+		frame |= UARTn_FRAME_DATABITS(8);
+		break;
+	}
+
+	if (new->c_cflag & CSTOPB)
+		/* the receiver only verifies the first stop bit */
+		frame |= UARTn_FRAME_STOPBITS_TWO;
+	else
+		frame |= UARTn_FRAME_STOPBITS_ONE;
+
+	if (new->c_cflag & PARENB) {
+		if (new->c_cflag & PARODD)
+			frame |= UARTn_FRAME_PARITY_ODD;
+		else
+			frame |= UARTn_FRAME_PARITY_EVEN;
+	} else
+		frame |= UARTn_FRAME_PARITY_NONE;
+
+	/*
+	 * the 6 lowest bits of CLKDIV are dc, bit 6 has value 0.25.
+	 * port->uartclk <= 14e6, so 4 * port->uartclk doesn't overflow.
+	 */
+	clkdiv = (DIV_ROUND_CLOSEST(4 * port->uartclk, 16 * baud) - 4) << 6;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	efm32_uart_write32(efm_port,
+			UARTn_CMD_TXDIS | UARTn_CMD_RXDIS, UARTn_CMD);
+
+	port->read_status_mask = UARTn_RXDATAX_RXDATA__MASK;
+	if (new->c_iflag & INPCK)
+		port->read_status_mask |=
+			UARTn_RXDATAX_FERR | UARTn_RXDATAX_PERR;
+	if (new->c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= SW_UARTn_RXDATAX_BERR;
+
+	port->ignore_status_mask = 0;
+	if (new->c_iflag & IGNPAR)
+		port->ignore_status_mask |=
+			UARTn_RXDATAX_FERR | UARTn_RXDATAX_PERR;
+	if (new->c_iflag & IGNBRK)
+		port->ignore_status_mask |= SW_UARTn_RXDATAX_BERR;
+
+	uart_update_timeout(port, new->c_cflag, baud);
+
+	efm32_uart_write32(efm_port, UARTn_CTRL_TXBIL, UARTn_CTRL);
+	efm32_uart_write32(efm_port, frame, UARTn_FRAME);
+	efm32_uart_write32(efm_port, clkdiv, UARTn_CLKDIV);
+
+	efm32_uart_write32(efm_port, UARTn_CMD_TXEN | UARTn_CMD_RXEN,
+			UARTn_CMD);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *efm32_uart_type(struct uart_port *port)
+{
+	return port->type == PORT_EFMUART ? "efm32-uart" : NULL;
+}
+
+static void efm32_uart_release_port(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+
+	clk_unprepare(efm_port->clk);
+	clk_put(efm_port->clk);
+	iounmap(port->membase);
+}
+
+static int efm32_uart_request_port(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	int ret;
+
+	port->membase = ioremap(port->mapbase, 60);
+	if (!efm_port->port.membase) {
+		ret = -ENOMEM;
+		efm_debug(efm_port, "failed to remap\n");
+		goto err_ioremap;
+	}
+
+	efm_port->clk = clk_get(port->dev, NULL);
+	if (IS_ERR(efm_port->clk)) {
+		ret = PTR_ERR(efm_port->clk);
+		efm_debug(efm_port, "failed to get clock\n");
+		goto err_clk_get;
+	}
+
+	ret = clk_prepare(efm_port->clk);
+	if (ret) {
+		clk_put(efm_port->clk);
+err_clk_get:
+
+		iounmap(port->membase);
+err_ioremap:
+		return ret;
+	}
+	return 0;
+}
+
+static void efm32_uart_config_port(struct uart_port *port, int type)
+{
+	if (type & UART_CONFIG_TYPE &&
+			!efm32_uart_request_port(port))
+		port->type = PORT_EFMUART;
+}
+
+static int efm32_uart_verify_port(struct uart_port *port,
+		struct serial_struct *serinfo)
+{
+	int ret = 0;
+
+	if (serinfo->type != PORT_UNKNOWN && serinfo->type != PORT_EFMUART)
+		ret = -EINVAL;
+
+	return ret;
+}
+
+static struct uart_ops efm32_uart_pops = {
+	.tx_empty = efm32_uart_tx_empty,
+	.set_mctrl = efm32_uart_set_mctrl,
+	.get_mctrl = efm32_uart_get_mctrl,
+	.stop_tx = efm32_uart_stop_tx,
+	.start_tx = efm32_uart_start_tx,
+	.stop_rx = efm32_uart_stop_rx,
+	.enable_ms = efm32_uart_enable_ms,
+	.break_ctl = efm32_uart_break_ctl,
+	.startup = efm32_uart_startup,
+	.shutdown = efm32_uart_shutdown,
+	.set_termios = efm32_uart_set_termios,
+	.type = efm32_uart_type,
+	.release_port = efm32_uart_release_port,
+	.request_port = efm32_uart_request_port,
+	.config_port = efm32_uart_config_port,
+	.verify_port = efm32_uart_verify_port,
+};
+
+static struct efm32_uart_port *efm32_uart_ports[5];
+
+#ifdef CONFIG_SERIAL_EFM32_UART_CONSOLE
+static void efm32_uart_console_putchar(struct uart_port *port, int ch)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	unsigned int timeout = 0x400;
+	u32 status;
+
+	while (1) {
+		status = efm32_uart_read32(efm_port, UARTn_STATUS);
+
+		if (status & UARTn_STATUS_TXBL)
+			break;
+		if (!timeout--)
+			return;
+	}
+	efm32_uart_write32(efm_port, ch, UARTn_TXDATA);
+}
+
+static void efm32_uart_console_write(struct console *co, const char *s,
+		unsigned int count)
+{
+	struct efm32_uart_port *efm_port = efm32_uart_ports[co->index];
+	u32 status = efm32_uart_read32(efm_port, UARTn_STATUS);
+	unsigned int timeout = 0x400;
+
+	if (!(status & UARTn_STATUS_TXENS))
+		efm32_uart_write32(efm_port, UARTn_CMD_TXEN, UARTn_CMD);
+
+	uart_console_write(&efm_port->port, s, count,
+			efm32_uart_console_putchar);
+
+	/* Wait for the transmitter to become empty */
+	while (1) {
+		u32 status = efm32_uart_read32(efm_port, UARTn_STATUS);
+		if (status & UARTn_STATUS_TXC)
+			break;
+		if (!timeout--)
+			break;
+	}
+
+	if (!(status & UARTn_STATUS_TXENS))
+		efm32_uart_write32(efm_port, UARTn_CMD_TXDIS, UARTn_CMD);
+}
+
+static void efm32_uart_console_get_options(struct efm32_uart_port *efm_port,
+		int *baud, int *parity, int *bits)
+{
+	u32 ctrl = efm32_uart_read32(efm_port, UARTn_CTRL);
+	u32 route, clkdiv, frame;
+
+	if (ctrl & UARTn_CTRL_SYNC)
+		/* not operating in async mode */
+		return;
+
+	route = efm32_uart_read32(efm_port, UARTn_ROUTE);
+	if (!(route & UARTn_ROUTE_TXPEN))
+		/* tx pin not routed */
+		return;
+
+	clkdiv = efm32_uart_read32(efm_port, UARTn_CLKDIV);
+
+	*baud = DIV_ROUND_CLOSEST(4 * efm_port->port.uartclk,
+			16 * (4 + (clkdiv >> 6)));
+
+	frame = efm32_uart_read32(efm_port, UARTn_FRAME);
+	if (frame & UARTn_FRAME_PARITY_ODD)
+		*parity = 'o';
+	else if (frame & UARTn_FRAME_PARITY_EVEN)
+		*parity = 'e';
+	else
+		*parity = 'n';
+
+	*bits = (frame & UARTn_FRAME_DATABITS__MASK) -
+			UARTn_FRAME_DATABITS(4) + 4;
+
+	efm_debug(efm_port, "get_opts: options=%d%c%d\n",
+			*baud, *parity, *bits);
+}
+
+static int efm32_uart_console_setup(struct console *co, char *options)
+{
+	struct efm32_uart_port *efm_port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret;
+
+	if (co->index < 0 || co->index >= ARRAY_SIZE(efm32_uart_ports)) {
+		unsigned i;
+		for (i = 0; i < ARRAY_SIZE(efm32_uart_ports); ++i) {
+			if (efm32_uart_ports[i]) {
+				pr_warn("efm32-console: fall back to console index %u (from %hhi)\n",
+						i, co->index);
+				co->index = i;
+				break;
+			}
+		}
+	}
+
+	efm_port = efm32_uart_ports[co->index];
+	if (!efm_port) {
+		pr_warn("efm32-console: No port at %d\n", co->index);
+		return -ENODEV;
+	}
+
+	ret = clk_prepare(efm_port->clk);
+	if (ret) {
+		dev_warn(efm_port->port.dev,
+				"console: clk_prepare failed: %d\n", ret);
+		return ret;
+	}
+
+	efm_port->port.uartclk = clk_get_rate(efm_port->clk);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		efm32_uart_console_get_options(efm_port,
+				&baud, &parity, &bits);
+
+	return uart_set_options(&efm_port->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver efm32_uart_reg;
+
+static struct console efm32_uart_console = {
+	.name = DEV_NAME,
+	.write = efm32_uart_console_write,
+	.device = uart_console_device,
+	.setup = efm32_uart_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &efm32_uart_reg,
+};
+
+#else
+#define efm32_uart_console (*(struct console *)NULL)
+#endif /* ifdef CONFIG_SERIAL_EFM32_UART_CONSOLE / else */
+
+static struct uart_driver efm32_uart_reg = {
+	.owner = THIS_MODULE,
+	.driver_name = DRIVER_NAME,
+	.dev_name = DEV_NAME,
+	.nr = ARRAY_SIZE(efm32_uart_ports),
+	.cons = &efm32_uart_console,
+};
+
+static int efm32_uart_probe_dt(struct platform_device *pdev,
+		struct efm32_uart_port *efm_port)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int ret;
+
+	if (!np)
+		return 1;
+
+	ret = of_alias_get_id(np, "serial");
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to get alias id: %d\n", ret);
+		return ret;
+	} else {
+		efm_port->port.line = ret;
+		return 0;
+	}
+
+}
+
+static int __devinit efm32_uart_probe(struct platform_device *pdev)
+{
+	struct efm32_uart_port *efm_port;
+	struct resource *res;
+	int ret;
+
+	efm_port = kzalloc(sizeof(*efm_port), GFP_KERNEL);
+	if (!efm_port) {
+		dev_dbg(&pdev->dev, "failed to allocate private data\n");
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENODEV;
+		dev_dbg(&pdev->dev, "failed to determine base address\n");
+		goto err_get_base;
+	}
+
+	if (resource_size(res) < 60) {
+		ret = -EINVAL;
+		dev_dbg(&pdev->dev, "memory resource too small\n");
+		goto err_too_small;
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret <= 0) {
+		dev_dbg(&pdev->dev, "failed to get rx irq\n");
+		goto err_get_rxirq;
+	}
+
+	efm_port->port.irq = ret;
+
+	ret = platform_get_irq(pdev, 1);
+	if (ret <= 0)
+		ret = efm_port->port.irq + 1;
+
+	efm_port->txirq = ret;
+
+	efm_port->port.dev = &pdev->dev;
+	efm_port->port.mapbase = res->start;
+	efm_port->port.type = PORT_EFMUART;
+	efm_port->port.iotype = UPIO_MEM32;
+	efm_port->port.fifosize = 2;
+	efm_port->port.ops = &efm32_uart_pops;
+	efm_port->port.flags = UPF_BOOT_AUTOCONF;
+
+	ret = efm32_uart_probe_dt(pdev, efm_port);
+	if (ret > 0)
+		/* not created by device tree */
+		efm_port->port.line = pdev->id;
+
+	if (efm_port->port.line >= 0 &&
+			efm_port->port.line < ARRAY_SIZE(efm32_uart_ports))
+		efm32_uart_ports[efm_port->port.line] = efm_port;
+
+	ret = uart_add_one_port(&efm32_uart_reg, &efm_port->port);
+	if (ret) {
+		dev_dbg(&pdev->dev, "failed to add port: %d\n", ret);
+
+		if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports))
+			efm32_uart_ports[pdev->id] = NULL;
+err_get_rxirq:
+err_too_small:
+err_get_base:
+		kfree(efm_port);
+	} else {
+		platform_set_drvdata(pdev, efm_port);
+		dev_dbg(&pdev->dev, "\\o/\n");
+	}
+
+	return ret;
+}
+
+static int __devexit efm32_uart_remove(struct platform_device *pdev)
+{
+	struct efm32_uart_port *efm_port = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	uart_remove_one_port(&efm32_uart_reg, &efm_port->port);
+
+	if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports))
+		efm32_uart_ports[pdev->id] = NULL;
+
+	kfree(efm_port);
+
+	return 0;
+}
+
+static struct of_device_id efm32_uart_dt_ids[] = {
+	{
+		.compatible = "efm32,uart",
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, efm32_uart_dt_ids);
+
+static struct platform_driver efm32_uart_driver = {
+	.probe = efm32_uart_probe,
+	.remove = __devexit_p(efm32_uart_remove),
+
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = efm32_uart_dt_ids,
+	},
+};
+
+static int __init efm32_uart_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&efm32_uart_reg);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&efm32_uart_driver);
+	if (ret)
+		uart_unregister_driver(&efm32_uart_reg);
+
+	pr_info("EFM32 UART/USART driver\n");
+
+	return ret;
+}
+module_init(efm32_uart_init);
+
+static void __exit efm32_uart_exit(void)
+{
+	platform_driver_unregister(&efm32_uart_driver);
+	uart_unregister_driver(&efm32_uart_reg);
+}
+
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_DESCRIPTION("EFM32 UART/USART driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/include/linux/platform_data/efm32-uart.h b/include/linux/platform_data/efm32-uart.h
new file mode 100644
index 0000000..ed0e975
--- /dev/null
+++ b/include/linux/platform_data/efm32-uart.h
@@ -0,0 +1,18 @@
+/*
+ *
+ *
+ */
+#ifndef __LINUX_PLATFORM_DATA_EFM32_UART_H__
+#define __LINUX_PLATFORM_DATA_EFM32_UART_H__
+
+#include <linux/types.h>
+
+/**
+ * struct efm32_uart_pdata
+ * @location: pinmux location for the I/O pins (to be written to the ROUTE
+ * 	register)
+ */
+struct efm32_uart_pdata {
+	u8 location;
+};
+#endif /* ifndef __LINUX_PLATFORM_DATA_EFM32_UART_H__ */
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index c91ace7..585bfd0 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -210,6 +210,8 @@
 /* Atheros AR933X SoC */
 #define PORT_AR933X	99
 
+/* Energy Micro efm32 SoC */
+#define PORT_EFMUART   100
 
 #ifdef __KERNEL__
 
-- 
1.7.8.3


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH v4] serial/efm32: add new driver
  2012-01-25  8:05         ` [PATCH v4] " Uwe Kleine-König
@ 2012-01-25  8:25           ` Joe Perches
  2012-01-25  8:41             ` Uwe Kleine-König
  2012-01-25 15:52           ` Greg KH
  1 sibling, 1 reply; 17+ messages in thread
From: Joe Perches @ 2012-01-25  8:25 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Greg KH, linux-kernel, Alan Cox, kernel, devicetree-discuss,
	Greg Kroah-Hartman, linux-serial

On Wed, 2012-01-25 at 09:05 +0100, Uwe Kleine-König wrote:
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>

trivial comments below:

> diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c
[]
> @@ -0,0 +1,830 @@
> +#if defined(CONFIG_SERIAL_EFM32_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
> +#define SUPPORT_SYSRQ
> +#endif

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

> +static void efm32_uart_rx_chars(struct efm32_uart_port *efm_port,
> +		struct tty_struct *tty)
> +{
[]
> +		if ((rxdata & UARTn_RXDATAX_FERR) &&
> +				!(rxdata & UARTn_RXDATAX_RXDATA__MASK)) {

Perhaps better as:

		if ((rxdata & UARTn_RXDATAX_FERR) &&
		    !(rxdata & UARTn_RXDATAX_RXDATA__MASK)) {

and RXDATA__MASK with 2 underscores?  perhaps just one _?

> +static int efm32_uart_console_setup(struct console *co, char *options)
[]
> +		for (i = 0; i < ARRAY_SIZE(efm32_uart_ports); ++i) {
> +			if (efm32_uart_ports[i]) {
> +				pr_warn("efm32-console: fall back to console index %u (from %hhi)\n",
> +						i, co->index);

				pr_warn("fall back to ..."
[]
> +	efm_port = efm32_uart_ports[co->index];
> +	if (!efm_port) {
> +		pr_warn("efm32-console: No port at %d\n", co->index);

		pr_warn("No port at..."



^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v4] serial/efm32: add new driver
  2012-01-25  8:25           ` Joe Perches
@ 2012-01-25  8:41             ` Uwe Kleine-König
  0 siblings, 0 replies; 17+ messages in thread
From: Uwe Kleine-König @ 2012-01-25  8:41 UTC (permalink / raw)
  To: Joe Perches
  Cc: Greg KH, linux-kernel, Alan Cox, kernel, devicetree-discuss,
	Greg Kroah-Hartman, linux-serial

On Wed, Jan 25, 2012 at 12:25:59AM -0800, Joe Perches wrote:
> On Wed, 2012-01-25 at 09:05 +0100, Uwe Kleine-König wrote:
> > Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> 
> trivial comments below:
> 
> > diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c
> []
> > @@ -0,0 +1,830 @@
> > +#if defined(CONFIG_SERIAL_EFM32_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
> > +#define SUPPORT_SYSRQ
> > +#endif
> 
> #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> 
> > +static void efm32_uart_rx_chars(struct efm32_uart_port *efm_port,
> > +		struct tty_struct *tty)
> > +{
> []
> > +		if ((rxdata & UARTn_RXDATAX_FERR) &&
> > +				!(rxdata & UARTn_RXDATAX_RXDATA__MASK)) {
> 
> Perhaps better as:
> 
> 		if ((rxdata & UARTn_RXDATAX_FERR) &&
> 		    !(rxdata & UARTn_RXDATAX_RXDATA__MASK)) {
This is how my editor does the indention. I'm sure this can be changed,
but I want the indention only to depend on the logical structure not on
where the opening parenthesis in the previous line is. This saves
context changes for future patches.
 
> and RXDATA__MASK with 2 underscores?  perhaps just one _?
Yeah, I like seperating the register bit field name from that fact that
the #define holds a mask.
 
> > +static int efm32_uart_console_setup(struct console *co, char *options)
> []
> > +		for (i = 0; i < ARRAY_SIZE(efm32_uart_ports); ++i) {
> > +			if (efm32_uart_ports[i]) {
> > +				pr_warn("efm32-console: fall back to console index %u (from %hhi)\n",
> > +						i, co->index);
> 
> 				pr_warn("fall back to ..."
> []
> > +	efm_port = efm32_uart_ports[co->index];
> > +	if (!efm_port) {
> > +		pr_warn("efm32-console: No port at %d\n", co->index);
> 
> 		pr_warn("No port at..."
I intentionally did that, as these two messages are related to the
console part of the serial driver while the rest of the messages are
about the serial/tty stuff. And I didn't like changing the definition of
pr_fmt in the middle of the file.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v4] serial/efm32: add new driver
  2012-01-25  8:05         ` [PATCH v4] " Uwe Kleine-König
  2012-01-25  8:25           ` Joe Perches
@ 2012-01-25 15:52           ` Greg KH
  2012-01-25 18:36             ` Uwe Kleine-König
  1 sibling, 1 reply; 17+ messages in thread
From: Greg KH @ 2012-01-25 15:52 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Greg KH, linux-kernel, Alan Cox, kernel, devicetree-discuss,
	linux-serial

On Wed, Jan 25, 2012 at 09:05:04AM +0100, Uwe Kleine-König wrote:
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> ---
> changes since v3 (id:1326127447-20284-1-git-send-email-u.kleine-koenig@pengutronix.de)
>  - increase number of ports to support UART additionally to USART ports
>  - implement choosing route location
>  - implement setting stopbit config
>    (before one stop bit was configured and CSTOPB was set unconditionally which
>    is wrong. Spotted by Russell King.)
>  - implement break, sysrq and overflow detection
>  - drop unrelated changing to make patch apply on v3.3-rc1
> 
> Hello Greg,
> 
> On Tue, Jan 24, 2012 at 02:05:22PM -0800, Greg KH wrote:
> > On Mon, Jan 09, 2012 at 05:44:07PM +0100, Uwe Kleine-König wrote:
> > > Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > > ---
> > > Hello,
> > > 
> > > changes since v2:
> > > - use {read,write}l_relaxed
> > > - rename driver to efm32-uart as USARTs and UARTs can be handled both
> > >   with it
> > > - disable TX in .stop_tx (which needs some changes related to
> > >   USARTn_IF_TXC)
> > > 
> > > Best regards
> > > Uwe
> > > 
> > >  .../devicetree/bindings/tty/serial/efm32-uart.txt  |   14 +
> > >  arch/arm/boot/dts/efm32gg-dk3750.dts               |    2 +-
> > >  arch/arm/mach-efm32/devices/pdev-efm32-usart.c     |    6 +-
> > 
> > This file isn't in 3.3-rc1, so I can't apply this patch :(
> > 
> > What tree is it supposed to be against?
> Sorry, I messed this up, it bases on my private development tree. These
> two changes should go independant of the driver patch via an ARM tree
> (if at all).
> 
> I added a few more features in the meantime, so the options are:
>  - take v3 simply droping the hunks changing
>    arch/arm/boot/dts/efm32gg-dk3750.dts and
>    arch/arm/mach-efm32/devices/pdev-efm32-usart.c
>  - take this for 3.3
>  - take this for 3.4
> 
> My preference would be of course the second option, but for me the other two
> would be OK if you don't like taking the update that late, too.

I was going to take this for the 3.4 merge, adding it to my tree now.
It should be ok to drop those hunks, right?

greg k-h

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Kconfig option for compile time build coverage (Was: Re: [PATCH] serial/efm32: add new driver)
  2012-01-09  9:59         ` Kconfig option for compile time build coverage (Was: Re: [PATCH] serial/efm32: add new driver) Uwe Kleine-König
@ 2012-01-25 16:16           ` Arnd Bergmann
  0 siblings, 0 replies; 17+ messages in thread
From: Arnd Bergmann @ 2012-01-25 16:16 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-kernel, Alan Cox, devicetree-discuss, Greg Kroah-Hartman,
	kernel, linux-serial, Andrew Morton

On Monday 09 January 2012, Uwe Kleine-König wrote:
> On Fri, Dec 23, 2011 at 09:44:28PM +0100, Uwe Kleine-König wrote:
> > On Fri, Dec 23, 2011 at 10:35:22AM +0000, Arnd Bergmann wrote:
> > > On Thursday 22 December 2011, Uwe Kleine-König wrote:
> > > > [...]
> > > > +config SERIAL_EFM32_USART
> > > > + bool "EFM32 USART port."
> > > > + depends on ARCH_EFM32
> > > > + select SERIAL_CORE
> > [...]
> > > I would generally prefer not to make the driver depend on the
> > > platform, so we can get better compile time coverage. I think a better
> > > set of dependencies would be
> > > 
> > >     depends on HAVE_CLK
> > >     depends on OF
> > >     default ARCH_EFM32
> > I'd prefer something like:
> > 
> >       depends on HAVE_CLK
> >       depends on ARCH_EFM32 || I_DO_BUILD_COVERAGE_TESTING
> > 
> > This would make it easier for Joe User to pick the right options for his
> > kernel (assuming he found out to better keep I_DO_BUILD_COVERAGE_TESTING
> > disabled). [...]
>
> What do you think about this I_DO_BUILD_COVERAGE_TESTING option? It
> would allow testers to get all possible options enabled (though there
> will never be an EFM32 USART port on a non-EFM32 machine I guess) and
> still users and distribution packagers would easily keep the option off
> even without cluttering .config and {menu,n,x,whatever}config.

Sorry for the late reply. The same topic has been discussed a lot of
times. We have different ways to handle this in the kernel, but the
way most common way is to use defconfig to decide which drivers to
use on a given machine but give the user the freedom to both enable
and disable any driver whereever possible, even if that would be
a silly thing to do. What we do require normally is that anything
that can be enabled is also able to be built without errors. A lot
of drivers have dependencies on platform specific header files
or other interfaces, so they require that platform to be enabled.

For all others I would not make such a restriction and I would always
recommend to be specific in the dependencies, e.g depending on HAVE_CLK
when you use the clock interfaces, instead of depending on other options
that implicitly enable HAVE_CLK.

	Arnd

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] serial/efm32: add new driver
  2012-01-09 10:34         ` [PATCH] serial/efm32: add new driver Uwe Kleine-König
@ 2012-01-25 16:56           ` Arnd Bergmann
  0 siblings, 0 replies; 17+ messages in thread
From: Arnd Bergmann @ 2012-01-25 16:56 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-kernel, Alan Cox, devicetree-discuss, Greg Kroah-Hartman,
	kernel, linux-serial

On Monday 09 January 2012, Uwe Kleine-König wrote:
> On Fri, Dec 23, 2011 at 09:44:28PM +0100, Uwe Kleine-König wrote:
>  
> > > I would suggest that you also support the "clock-frequency" and/or
> > > "current-speed" properties that are defined for serial ports, see the
> > > of_serial driver.
> > I will have a look to find out what they mean and update the patch
> > accordingly.
> I took that look and I don't understand what they are good for in my
> case.
> clock-frequency is used to initialize port->uartclk (and helps setting
> up port->custom_divisor if current-speed is given). The driver I posted
> uses the clk API to determine the clock frequency. So shouldn't the frequency
> better be specified in the clk part of the dt? (I don't know yet how a
> dt binding for clks should/can look like, so this is a bit speculative,
> but I'd expect to have nothing more clk related in a device
> specification but a reference to a clk definition.)

The binding for 8250 serial ports is documented at
http://www.openfirmware.org/1275/bindings/devices/html/serial.html

If you can always use the clk API to find out the base clock rate,
that's probably fine, I was mostly trying to make sure we don't
introduce another duplicate API for this.

The "current-speed" property is used to describe the baud rate that
should be used by the kernel for this port in order to talk to
devices connected to the port. This is very useful if you need to
connect the boot console to a fixed-rate device, and would get
used instead of the 115200 default you have when nothing else
is configured on the command line. I don't really know why
there is no respective option to set parity or flow control

> Independant of my driver I wonder if defining current-speed should also
> result in
> 
> 	port->flags |= UPF_SPD_CUST;
> 
> (in of_platform_serial_setup()).

I believe that is not needed because you don't call uart_get_divisor,
which would be the only place that looks at this flag.

> Having said that and taking into account that my driver doesn't use
> port->custom_divisor, do you keep suggesting that I should use
> "clock-frequency" and/or "current-speed"?

clock-frequency seems to be unnecessary, but current-speed would
still make sense. custom_divisor is simply the method that of_serial
uses to communicate the bit rate to the 8250 base driver, but you
could set the divisor directly.

	Arnd

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v4] serial/efm32: add new driver
  2012-01-25 15:52           ` Greg KH
@ 2012-01-25 18:36             ` Uwe Kleine-König
  0 siblings, 0 replies; 17+ messages in thread
From: Uwe Kleine-König @ 2012-01-25 18:36 UTC (permalink / raw)
  To: Greg KH
  Cc: Greg KH, linux-kernel, Alan Cox, kernel, devicetree-discuss,
	linux-serial

Hello Greg,

On Wed, Jan 25, 2012 at 07:52:58AM -0800, Greg KH wrote:
> On Wed, Jan 25, 2012 at 09:05:04AM +0100, Uwe Kleine-König wrote:
> > Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > ---
> > changes since v3 (id:1326127447-20284-1-git-send-email-u.kleine-koenig@pengutronix.de)
> >  - increase number of ports to support UART additionally to USART ports
> >  - implement choosing route location
> >  - implement setting stopbit config
> >    (before one stop bit was configured and CSTOPB was set unconditionally which
> >    is wrong. Spotted by Russell King.)
> >  - implement break, sysrq and overflow detection
> >  - drop unrelated changing to make patch apply on v3.3-rc1
> > 
> > Hello Greg,
> > 
> > On Tue, Jan 24, 2012 at 02:05:22PM -0800, Greg KH wrote:
> > > On Mon, Jan 09, 2012 at 05:44:07PM +0100, Uwe Kleine-König wrote:
> > > > Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > > > ---
> > > > Hello,
> > > > 
> > > > changes since v2:
> > > > - use {read,write}l_relaxed
> > > > - rename driver to efm32-uart as USARTs and UARTs can be handled both
> > > >   with it
> > > > - disable TX in .stop_tx (which needs some changes related to
> > > >   USARTn_IF_TXC)
> > > > 
> > > > Best regards
> > > > Uwe
> > > > 
> > > >  .../devicetree/bindings/tty/serial/efm32-uart.txt  |   14 +
> > > >  arch/arm/boot/dts/efm32gg-dk3750.dts               |    2 +-
> > > >  arch/arm/mach-efm32/devices/pdev-efm32-usart.c     |    6 +-
> > > 
> > > This file isn't in 3.3-rc1, so I can't apply this patch :(
> > > 
> > > What tree is it supposed to be against?
> > Sorry, I messed this up, it bases on my private development tree. These
> > two changes should go independant of the driver patch via an ARM tree
> > (if at all).
> > 
> > I added a few more features in the meantime, so the options are:
> >  - take v3 simply droping the hunks changing
> >    arch/arm/boot/dts/efm32gg-dk3750.dts and
> >    arch/arm/mach-efm32/devices/pdev-efm32-usart.c
> >  - take this for 3.3
> >  - take this for 3.4
> > 
> > My preference would be of course the second option, but for me the other two
> > would be OK if you don't like taking the update that late, too.
> 
> I was going to take this for the 3.4 merge, adding it to my tree now.
> It should be ok to drop those hunks, right?
Yeah, but if you were taking v4 then you wouldn't need to drop these hunks.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2012-01-25 18:36 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-12-21 15:05 [PATCH] serial/efm32: add new driver Uwe Kleine-König
2011-12-21 20:28 ` Alan Cox
2011-12-22  8:57   ` Uwe Kleine-König
2011-12-22 13:38   ` Uwe Kleine-König
2011-12-23 10:35     ` Arnd Bergmann
2011-12-23 20:44       ` Uwe Kleine-König
2012-01-09  9:59         ` Kconfig option for compile time build coverage (Was: Re: [PATCH] serial/efm32: add new driver) Uwe Kleine-König
2012-01-25 16:16           ` Arnd Bergmann
2012-01-09 10:34         ` [PATCH] serial/efm32: add new driver Uwe Kleine-König
2012-01-25 16:56           ` Arnd Bergmann
2012-01-09 16:44     ` [PATCH v3] " Uwe Kleine-König
2012-01-24 22:05       ` Greg KH
2012-01-25  8:05         ` [PATCH v4] " Uwe Kleine-König
2012-01-25  8:25           ` Joe Perches
2012-01-25  8:41             ` Uwe Kleine-König
2012-01-25 15:52           ` Greg KH
2012-01-25 18:36             ` Uwe Kleine-König

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).