Writing a UART driver for Linux

Posted by Marcus Folkesson on Saturday, October 5, 2024

Writing a UART driver for Linux

Today we are going to write a UART driver for Linux!

UART (Universal asynchronous receiver-transmitter) is on of the most common device-to-device communication protocols that almost every SoC or microcontroller has hardware support for. Most SoC has several UART ports, but sometimes that is not enough as in this case.

Background

I'm working in a project where we have an ARM SoC connected to an FPGA on the AEMIF interface. This FPGA has implemented two UARTs that we intend to use from Linux. Unfortunately, I cannot share any more details about the platform nor the implementation in the FPGA, but that is not needed to understand the UART driver.

Since I can't reveal which client it is, I'll call the driver mfocserial.

Limitation

This driver comes with some limitations:

  • The UART implementation does not have interrupts, so the driver will be polling only.
  • No support for CONFIG_CONSOLE_POLL, so we will not be able to attach kgdb.

Overview of the driver

The FPGA is connected to the memory bus so driver will request_mem_region() and ioremap() the region to make it accessible for the device driver. The FPGA lack support for interrupts, so the device driver will setup a hrtimer for polling instead.

Here is an overview of the functions of the driver:

/media/uart-driver-flow.png

Lets go through the code.

Implementation

Global data

The driver initialize some global data structures:

1static struct uart_port mserial_ports[MSERIAL_NR_UARTS];
2static struct uart_driver mserial_uart_driver;

mserial_port is initialized as soon as a device is probed and mserial_uart_driver is statically defined further down:

1static struct uart_driver mserial_uart_driver = {
2	.owner		= THIS_MODULE,
3	.driver_name	= "mfocserial",
4	.dev_name	= MSERIAL_NAME,
5	.major		= MSERIAL_MAJOR,
6	.minor		= MSERIAL_MINOR,
7	.nr		= MSERIAL_NR_UARTS,
8};

The MSERIAL_NR_UARTS is set to 2 as we can see among the contants:

 1#define PORT_MFOCSERIAL		0x57
 2#define MSERIAL_NAME		"ttyMFOC"
 3#define MSERIAL_MAJOR		204
 4#define MSERIAL_MINOR		187
 5#define MSERIAL_NR_UARTS	2
 6
 7#define MSERIAL_POLLRATE	(50000000)
 8#define MSERIAL_CLOCK		(12000000)
 9#define MSERIAL_BR_MIN		(MSERIAL_CLOCK / (8 * 0x3ff))
10#define MSERIAL_BR_MAX		(MSERIAL_CLOCK / (8 * 0x001))
11
12#define UART_AUTOSUSPEND_TIMEOUT	3000	/* ms */

Initialization

Driver init

Since the FPGA is connected to the memory bus, we will register a platform driver:

 1static struct platform_driver mserial_platform_driver = {
 2	.probe = mserial_probe,
 3	.remove = mserial_remove,
 4	.driver = {
 5		.name  = "mfocserial",
 6		.of_match_table = of_match_ptr(mserial_of_match),
 7	},
 8};
 9
10static int __init mserial_init(void)
11{
12
13	pr_debug("mfocserial: calling platform_driver_register()\n");
14	return platform_driver_register(&mserial_platform_driver);
15}
16
17static void __exit mserial_exit(void)
18{
19	platform_driver_unregister(&mserial_platform_driver);
20	if (mserial_uart_driver.state)
21		uart_unregister_driver(&mserial_uart_driver);
22}
23
24module_init(mserial_init);
25module_exit(mserial_exit);

The mserial_probe will then be called for each registered device.

Device probe

The mserial_probe() function is invoked for each registred device. It is responsible for several things:

  • Allocate all per-device data structures
  • Fetch properties from the device tree (port-number and current-speed)
  • Request the memory region to map
  • Call mserial_assign() to map the device to the uart_port structure
 1static int mserial_probe(struct platform_device *pdev)
 2{
 3	struct resource *res;
 4	struct mserial_data *pdata;
 5	int ret;
 6	int id = pdev->id;
 7
 8	pdata = devm_kzalloc(&pdev->dev, sizeof(struct mserial_data),
 9			     GFP_KERNEL);
10	if (!pdata)
11		return -ENOMEM;
12
13	if (IS_ENABLED(CONFIG_OF)) {
14		const char *prop;
15		struct device_node *np = pdev->dev.of_node;
16
17		prop = "port-number";
18		ret = of_property_read_u32(np, prop, &id);
19		if (ret && ret != -EINVAL)
20of_err:
21			return dev_err_probe(&pdev->dev, ret,
22					     "could not read %s\n", prop);
23
24		prop = "current-speed";
25		ret = of_property_read_u32(np, prop, &pdata->baud);
26		if (ret)
27			goto of_err;
28	} else {
29		pdata->baud = 9600;
30		pdata->cflags = CS8;
31	}
32
33	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
34	if (!res)
35		return -ENODEV;
36
37	if (!mserial_uart_driver.state) {
38		dev_dbg(&pdev->dev, "mfocserial: calling uart_register_driver()\n");
39		ret = uart_register_driver(&mserial_uart_driver);
40		if (ret < 0) {
41			dev_err(&pdev->dev, "Failed to register driver\n");
42			return ret;
43		}
44	}
45
46	ret = mserial_assign(&pdev->dev, id, res->start, pdata);
47
48	return ret;
49}

The memory region we need to map is also provided by the device tree and is accessible from platform_get_resource().

mserial_assign() does a few things:

  • Map the probing device to one of the statically allocated uart_ports (mserial_ports):
  • Initialize the spinlock
  • Initialize the hrtimer
  • Initialize the uart_port
  • Finally call uart_add_one_port()
 1static int mserial_assign(struct device *dev, int id, phys_addr_t base,
 2			  struct mserial_data *pdata)
 3{
 4	struct uart_port *port;
 5	int rc;
 6
 7	/* if id = -1; then scan for a free id and use that */
 8	if (id < 0) {
 9		for (id = 0; id < MSERIAL_NR_UARTS; id++)
10			if (mserial_ports[id].mapbase == 0)
11				break;
12	}
13	if (id < 0 || id >= MSERIAL_NR_UARTS) {
14		dev_err(dev, "%s%i too large\n", MSERIAL_NAME, id);
15		return -EINVAL;
16	}
17
18	if ((mserial_ports[id].mapbase) && (mserial_ports[id].mapbase != base)) {
19		dev_err(dev, "cannot assign to %s%i; it is already in use\n",
20			MSERIAL_NAME, id);
21		return -EBUSY;
22	}
23
24	port = &mserial_ports[id];
25
26	spin_lock_init(&port->lock);
27	port->fifosize = 16;
28	port->regshift = 2;
29	port->iotype = UPIO_MEM;
30	port->iobase = 1; /* mark port in use */
31	port->mapbase = base;
32	port->membase = NULL;
33	port->ops = &mserial_ops;
34	port->flags = UPF_BOOT_AUTOCONF;
35	port->dev = dev;
36	port->type = PORT_UNKNOWN;
37	port->line = id;
38	port->private_data = pdata;
39	pdata->port = port;
40
41	dev_set_drvdata(dev, port);
42
43	pdata->pollrate = ktime_set(0, MSERIAL_POLLRATE);
44	hrtimer_init(&pdata->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
45	pdata->timer.function = mserial_poll;
46
47	/* Register the port */
48	rc = uart_add_one_port(&mserial_uart_driver, port);
49	if (rc) {
50		dev_err(dev, "uart_add_one_port() failed; err=%i\n", rc);
51		port->mapbase = 0;
52		dev_set_drvdata(dev, NULL);
53		return rc;
54	}
55
56	return 0;
57}

All uart_ports has a set of operations to meet the UART API. Those operations is abstracted as a set of callback functions declared in a uart_ops structure. The struct uart_ops we use in this driver is defined as follows:

 1static const struct uart_ops mserial_ops = {
 2	.tx_empty	= mserial_tx_empty,
 3	.set_mctrl	= mserial_set_mctrl,
 4	.get_mctrl	= mserial_get_mctrl,
 5	.stop_tx	= mserial_stop_tx,
 6	.start_tx	= mserial_start_tx,
 7	.stop_rx	= mserial_stop_rx,
 8	.break_ctl	= mserial_break_ctl,
 9	.startup	= mserial_startup,
10	.shutdown	= mserial_shutdown,
11	.set_termios	= mserial_set_termios,
12	.type		= mserial_type,
13	.release_port	= mserial_release_port,
14	.request_port	= mserial_request_port,
15	.config_port	= mserial_config_port,
16	.verify_port	= mserial_verify_port,
17};

uart_add_one_port() allows the driver to register its own uart_port structure within the core layer.

UART operations

Lets take another look at the uart_ops structure. It contains a bunch of callback functions:

 1static const struct uart_ops mserial_ops = {
 2	.tx_empty	= mserial_tx_empty,
 3	.set_mctrl	= mserial_set_mctrl,
 4	.get_mctrl	= mserial_get_mctrl,
 5	.stop_tx	= mserial_stop_tx,
 6	.start_tx	= mserial_start_tx,
 7	.stop_rx	= mserial_stop_rx,
 8	.break_ctl	= mserial_break_ctl,
 9	.startup	= mserial_startup,
10	.shutdown	= mserial_shutdown,
11	.set_termios	= mserial_set_termios,
12	.type		= mserial_type,
13	.release_port	= mserial_release_port,
14	.request_port	= mserial_request_port,
15	.config_port	= mserial_config_port,
16	.verify_port	= mserial_verify_port,
17};

You will find a description of each callback function in the linux tree [1].

tx_empty

This function tests whether the transmitter fifo and shifter for the port is empty. If it is empty, this function should return TIOCSER_TEMT, otherwise return 0. If the port does not support this operation, then it should return TIOCSER_TEMT.

 1static unsigned int mserial_tx_empty(struct uart_port *port)
 2{
 3	unsigned long flags;
 4	unsigned int ret;
 5
 6	spin_lock_irqsave(&port->lock, flags);
 7	ret = ioread32(port->membase + MSERIAL_CONSTAT);
 8	spin_unlock_irqrestore(&port->lock, flags);
 9
10	return (ret & MSERIAL_CONSTAT_TXC) ? TIOCSER_TEMT : 0;
11}
set_mctrl

This function sets the modem control lines for port to the state described by mctrl. The relevant bits of mctrl are:

  • TIOCM_RTS RTS signal.
  • TIOCM_DTR DTR signal.
  • TIOCM_OUT1 OUT1 signal.
  • TIOCM_OUT2 OUT2 signal.
  • TIOCM_LOOP Set the port into loopback mode.

If the appropriate bit is set, the signal should be driven active. If the bit is clear, the signal should be driven inactive.

This driver does not implement the feature to set these control lines, so the implementation is simply:

1static void mserial_set_mctrl(struct uart_port *port, unsigned int mctrl)
2{
3	/* N/A */
4}
get_mctrl

Returns the current state of modem control inputs of @port. The state of the outputs should not be returned, since the core keeps track of their state. The state information should include:

  • TIOCM_CAR state of DCD signal
  • TIOCM_CTS state of CTS signal
  • TIOCM_DSR state of DSR signal
  • TIOCM_RI state of RI signal

The bit is set if the signal is currently driven active. If the port does not support CTS, DCD or DSR, the driver should indicate that the signal is permanently active. If RI is not available, the signal should not be indicated as active.

1static unsigned int mserial_get_mctrl(struct uart_port *port)
2{
3	return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
4}
stop_tx

Stop transmitting characters. This might be due to the CTS line becoming inactive or the tty layer indicating we want to stop transmission due to an XOFF character.

The driver should stop transmitting characters as soon as possible.

We don't need to do any special for stop transmitting characters:

1static void mserial_stop_tx(struct uart_port *port)
2{
3}
start_tx

Start transmitting characters.

This function only calls mserial_transmit():

1static void mserial_start_tx(struct uart_port *port)
2{
3	mserial_transmit(port, ioread32(port->membase + MSERIAL_CONSTAT));
4}

For handling the transmitting of bytes from the circular buffer.

 1static int mserial_transmit(struct uart_port *port, int stat)
 2{
 3	struct circ_buf *xmit  = &port->state->xmit;
 4
 5	if (stat & MSERIAL_CONSTAT_TXFULL)
 6		return 0;
 7
 8
 9	if (port->x_char) {
10		iowrite8(port->x_char, port->membase + MSERIAL_TXDATA);
11		port->x_char = 0;
12		port->icount.tx++;
13		return 1;
14	}
15
16	if (uart_circ_empty(xmit) || uart_tx_stopped(port))
17		return 0;
18
19	iowrite8(xmit->buf[xmit->tail], port->membase + MSERIAL_TXDATA);
20	xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
21	port->icount.tx++;
22
23	/* wake up */
24	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
25		uart_write_wakeup(port);
26
27	return 1;
28}

The uart_write_wakeup() routine is used by the interrupt handler to schedule processing in the software interrupt portion of the driver. The driver should call the function when the number of charactes in the transmit buffer have dropped below a threshold.

stop_rx

Stop receiving characters; the port is in the process of being closed.

1static void mserial_stop_rx(struct uart_port *port)
2{
3	/* don't forward any more data (like !CREAD) */
4	port->ignore_status_mask = MSERIAL_CONSTAT_ERRMASK;
5}
break_ctl

Control the transmission of a break signal. If ctl is nonzero, the break signal should be transmitted. The signal should be terminated when another call is made with a zero ctl.

1static void mserial_break_ctl(struct uart_port *port, int ctl)
2{
3	/* N/A */
4}
startup

Grab any interrupt resources and initialise any low level driver state. Enable the port for reception. This method will only be called when the port is initially opened.

As we are using a hrtimer to poll for rx data, it is not time to start the timer.

 1static int mserial_startup(struct uart_port *port)
 2{
 3	struct mserial_data *pdata = port->private_data;
 4
 5	hrtimer_start(&pdata->timer, pdata->pollrate, HRTIMER_MODE_REL);
 6
 7	iowrite32(ioread32(port->membase + MSERIAL_CONSTAT) | MSERIAL_CONSTAT_ENUART,
 8			port->membase + MSERIAL_CONSTAT);
 9	return 0;
10}

Speaking of the timer. The timer function that kicks in is mserial_poll():

 1static enum hrtimer_restart mserial_poll(struct hrtimer *timer)
 2{
 3	struct mserial_data *ss = container_of(timer, struct mserial_data, timer);
 4	struct uart_port *port = ss->port;
 5	int rx_or_tx, n = 0;
 6	u16 cs;
 7
 8	do {
 9		cs = ioread32(port->membase + MSERIAL_CONSTAT);
10		iowrite32(cs & ~MSERIAL_CONSTAT_ERRMASK, port->membase + MSERIAL_CONSTAT);
11
12		rx_or_tx  = mserial_receive(port, cs);
13		rx_or_tx |= mserial_transmit(port, cs);
14		n++;
15	} while (rx_or_tx);
16
17	if (n > 1)
18		tty_flip_buffer_push(&port->state->port);
19
20	hrtimer_start(&ss->timer, ss->pollrate, HRTIMER_MODE_REL);
21	return HRTIMER_NORESTART;
22}

tty_flip_buffer_push() queues a push of the terminal flip buffer to the line current line discipline.

shutdown

Disable the port, disable any break condition that may be in effect, and free any interrupt resources.

Disable the UART and cancel the timer.

 1static int mserial_startup(struct uart_port *port)
 2{
 3	struct mserial_data *pdata = port->private_data;
 4
 5	hrtimer_start(&pdata->timer, pdata->pollrate, HRTIMER_MODE_REL);
 6
 7	iowrite32(ioread32(port->membase + MSERIAL_CONSTAT) | MSERIAL_CONSTAT_ENUART,
 8			port->membase + MSERIAL_CONSTAT);
 9	return 0;
10}

set_termios

Change the port parameters, including word length, parity, stop bits. Update port->read_status_mask and port->ignore_status_mask to indicate the types of events we are interested in receiving.

The UART implementation in the FPGA is rather simple and does not allow any port parameters except baudrate.

 1static void mserial_set_termios(struct uart_port *port,
 2			      struct ktermios *termios,
 3			      const struct ktermios *old)
 4{
 5	unsigned long flags;
 6	unsigned int baud = 0;
 7	u16 div;
 8
 9	spin_lock_irqsave(&port->lock, flags);
10
11	baud = uart_get_baud_rate(
12			port, termios, old, MSERIAL_BR_MIN, MSERIAL_BR_MAX);
13
14	if (baud)	{
15		div = MSERIAL_CLOCK / (8 * baud);
16		iowrite32(div, port->membase + MSERIAL_BR);
17		uart_update_timeout(port, termios->c_cflag, baud);
18	}
19	spin_unlock_irqrestore(&port->lock, flags);
20
21}

type

Return a pointer to a string constant describing the specified port, or return NULL, in which case the string 'unknown' is substituted.

1static const char *mserial_type(struct uart_port *port)
2{
3	return port->type == PORT_MFOCSERIAL ? "mfocserial" : NULL;
4}
release_port

Release any memory and IO region resources currently in use by the port.

In our case we only have the memory region to release.

1static void mserial_release_port(struct uart_port *port)
2{
3	release_mem_region(port->mapbase, MSERIAL_REGION);
4	iounmap(port->membase);
5	port->membase = NULL;
6}

request_port

Request any memory and IO region resources required by the port. If any fail, no resources should be registered when this function returns, and it should return -EBUSY on failure.

 1static int mserial_request_port(struct uart_port *port)
 2{
 3	pr_debug("mserial console: port=%p; port->mapbase=%llx\n",
 4		 port, (unsigned long long) port->mapbase);
 5
 6	if (!request_mem_region(port->mapbase, MSERIAL_REGION, "mserial")) {
 7		dev_err(port->dev, "Memory region busy\n");
 8		return -EBUSY;
 9	}
10
11	port->membase = ioremap(port->mapbase, MSERIAL_REGION);
12	if (!port->membase) {
13		dev_err(port->dev, "Unable to map registers\n");
14		release_mem_region(port->mapbase, MSERIAL_REGION);
15		return -EBUSY;
16	}
17	return 0;
18}
config_port

Perform any autoconfiguration steps required for the port. type contains a bit mask of the required configuration. port->type should be set to the type found, or PORT_UNKNOWN if no port was detected.

1static void mserial_config_port(struct uart_port *port, int flags)
2{
3	if (!mserial_request_port(port))
4		port->type = PORT_MFOCSERIAL;
5}

Complete driver

This is what the driver looks like when you put it all together:

  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * mfocserial.c: MFOC Serial port driver
  4 *
  5 * Copyright (C) 2024 Marcus Folkesson <marcus.folkesson@gmail.com>
  6 */
  7
  8#include <linux/platform_device.h>
  9#include <linux/module.h>
 10#include <linux/bitfield.h>
 11#include <linux/console.h>
 12#include <linux/serial.h>
 13#include <linux/serial_core.h>
 14#include <linux/tty.h>
 15#include <linux/tty_flip.h>
 16#include <linux/delay.h>
 17#include <linux/hrtimer.h>
 18#include <linux/interrupt.h>
 19#include <linux/init.h>
 20#include <linux/io.h>
 21#include <linux/iopoll.h>
 22#include <linux/of.h>
 23#include <linux/of_address.h>
 24#include <linux/of_device.h>
 25#include <linux/of_platform.h>
 26#include <linux/pm_runtime.h>
 27
 28#define PORT_MFOCSERIAL		0x57
 29#define MSERIAL_NAME		"ttyMFOC"
 30#define MSERIAL_MAJOR		204
 31#define MSERIAL_MINOR		187
 32#define MSERIAL_NR_UARTS	2
 33
 34#define MSERIAL_POLLRATE	(50000000)
 35#define MSERIAL_CLOCK		(12000000)
 36#define MSERIAL_BR_MIN		(MSERIAL_CLOCK / (8 * 0x3ff))
 37#define MSERIAL_BR_MAX		(MSERIAL_CLOCK / (8 * 0x001))
 38
 39#define UART_AUTOSUSPEND_TIMEOUT	3000	/* ms */
 40
 41/* ---------------------------------------------------------------------
 42 * Register definitions
 43 */
 44
 45#define MSERIAL_REGION		10
 46#define MSERIAL_CONSTAT		0x00
 47#define MSERIAL_TXDATA		0x04
 48#define MSERIAL_RXDATA		0x08
 49#define MSERIAL_BR			0x0c
 50
 51#define MSERIAL_CONSTAT_ENUART	0x0001
 52#define MSERIAL_CONSTAT_PAR		0x0002
 53#define MSERIAL_CONSTAT_ODD		0x0004
 54#define MSERIAL_CONSTAT_RXEMPTY	0x0100
 55#define MSERIAL_CONSTAT_RXFULL	0x0200
 56#define MSERIAL_CONSTAT_OVR		0x0800
 57#define MSERIAL_CONSTAT_FERR	0x1000
 58#define MSERIAL_CONSTAT_PARERR	0x2000
 59#define MSERIAL_CONSTAT_TXFULL	0x4000
 60#define MSERIAL_CONSTAT_TXC		0x8000
 61#define MSERIAL_CONSTAT_ERRMASK	\
 62	(MSERIAL_CONSTAT_OVR | MSERIAL_CONSTAT_FERR | MSERIAL_CONSTAT_PARERR)
 63
 64
 65struct mserial_data {
 66	struct uart_port	*port;
 67	unsigned int baud;
 68	tcflag_t cflags;
 69	struct hrtimer		timer;
 70	ktime_t				pollrate;
 71};
 72
 73
 74static struct uart_port mserial_ports[MSERIAL_NR_UARTS];
 75static struct uart_driver mserial_uart_driver;
 76
 77/* ---------------------------------------------------------------------
 78 * Core UART driver operations
 79 */
 80
 81
 82static int mserial_receive(struct uart_port *port, int stat)
 83{
 84	struct tty_port *tport = &port->state->port;
 85	unsigned char ch = 0;
 86	char flag = TTY_NORMAL;
 87
 88	if (!(stat & (MSERIAL_CONSTAT_OVR | MSERIAL_CONSTAT_FERR)) &&
 89		(stat & MSERIAL_CONSTAT_RXEMPTY))
 90		return 0;
 91
 92	if (!(stat & MSERIAL_CONSTAT_RXEMPTY)) {
 93		port->icount.rx++;
 94		ch = ioread8(port->membase + MSERIAL_RXDATA);
 95	}
 96
 97	port->icount.parity  += (stat & MSERIAL_CONSTAT_PARERR) ? 1 : 0;
 98	port->icount.overrun += (stat & MSERIAL_CONSTAT_OVR) ? 1 : 0;
 99	port->icount.frame   += (stat & MSERIAL_CONSTAT_FERR) ? 1 : 0;
100
101	if (!(stat & MSERIAL_CONSTAT_RXEMPTY))
102		tty_insert_flip_char(tport, ch, flag);
103	if (stat & MSERIAL_CONSTAT_OVR)
104		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
105	if (stat & MSERIAL_CONSTAT_FERR)
106		tty_insert_flip_char(tport, 0, TTY_FRAME);
107
108	return 1;
109}
110
111
112static int mserial_transmit(struct uart_port *port, int stat)
113{
114	struct circ_buf *xmit  = &port->state->xmit;
115
116	if (stat & MSERIAL_CONSTAT_TXFULL)
117		return 0;
118
119
120	if (port->x_char) {
121		iowrite8(port->x_char, port->membase + MSERIAL_TXDATA);
122		port->x_char = 0;
123		port->icount.tx++;
124		return 1;
125	}
126
127	if (uart_circ_empty(xmit) || uart_tx_stopped(port))
128		return 0;
129
130	iowrite8(xmit->buf[xmit->tail], port->membase + MSERIAL_TXDATA);
131	xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
132	port->icount.tx++;
133
134	/* wake up */
135	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
136		uart_write_wakeup(port);
137
138	return 1;
139}
140
141static enum hrtimer_restart mserial_poll(struct hrtimer *timer)
142{
143	struct mserial_data *ss = container_of(timer, struct mserial_data, timer);
144	struct uart_port *port = ss->port;
145	int rx_or_tx, n = 0;
146	u16 cs;
147
148	do {
149		cs = ioread32(port->membase + MSERIAL_CONSTAT);
150		iowrite32(cs & ~MSERIAL_CONSTAT_ERRMASK, port->membase + MSERIAL_CONSTAT);
151
152		rx_or_tx  = mserial_receive(port, cs);
153		rx_or_tx |= mserial_transmit(port, cs);
154		n++;
155	} while (rx_or_tx);
156
157	if (n > 1)
158		tty_flip_buffer_push(&port->state->port);
159
160	hrtimer_start(&ss->timer, ss->pollrate, HRTIMER_MODE_REL);
161	return HRTIMER_NORESTART;
162}
163
164static unsigned int mserial_tx_empty(struct uart_port *port)
165{
166	unsigned long flags;
167	unsigned int ret;
168
169	spin_lock_irqsave(&port->lock, flags);
170	ret = ioread32(port->membase + MSERIAL_CONSTAT);
171	spin_unlock_irqrestore(&port->lock, flags);
172
173	return (ret & MSERIAL_CONSTAT_TXC) ? TIOCSER_TEMT : 0;
174}
175
176static unsigned int mserial_get_mctrl(struct uart_port *port)
177{
178	return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
179}
180
181static void mserial_set_mctrl(struct uart_port *port, unsigned int mctrl)
182{
183	/* N/A */
184}
185
186static void mserial_stop_tx(struct uart_port *port)
187{
188}
189
190static void mserial_start_tx(struct uart_port *port)
191{
192	mserial_transmit(port, ioread32(port->membase + MSERIAL_CONSTAT));
193}
194
195static void mserial_stop_rx(struct uart_port *port)
196{
197	/* don't forward any more data (like !CREAD) */
198	port->ignore_status_mask = MSERIAL_CONSTAT_ERRMASK;
199}
200
201static void mserial_break_ctl(struct uart_port *port, int ctl)
202{
203	/* N/A */
204}
205
206static int mserial_startup(struct uart_port *port)
207{
208	struct mserial_data *pdata = port->private_data;
209
210	hrtimer_start(&pdata->timer, pdata->pollrate, HRTIMER_MODE_REL);
211
212	iowrite32(ioread32(port->membase + MSERIAL_CONSTAT) | MSERIAL_CONSTAT_ENUART,
213			port->membase + MSERIAL_CONSTAT);
214	return 0;
215}
216
217static void mserial_shutdown(struct uart_port *port)
218{
219	struct mserial_data *pdata = port->private_data;
220
221	iowrite32(ioread32(port->membase + MSERIAL_CONSTAT) & ~MSERIAL_CONSTAT_ENUART,
222			port->membase + MSERIAL_CONSTAT);
223
224	hrtimer_cancel(&pdata->timer);
225}
226
227static void mserial_set_termios(struct uart_port *port,
228			      struct ktermios *termios,
229			      const struct ktermios *old)
230{
231	unsigned long flags;
232	unsigned int baud = 0;
233	u16 div;
234
235	spin_lock_irqsave(&port->lock, flags);
236
237	baud = uart_get_baud_rate(
238			port, termios, old, MSERIAL_BR_MIN, MSERIAL_BR_MAX);
239
240	if (baud)	{
241		div = MSERIAL_CLOCK / (8 * baud);
242		iowrite32(div, port->membase + MSERIAL_BR);
243		uart_update_timeout(port, termios->c_cflag, baud);
244	}
245	spin_unlock_irqrestore(&port->lock, flags);
246
247}
248
249static const char *mserial_type(struct uart_port *port)
250{
251	return port->type == PORT_MFOCSERIAL ? "mfocserial" : NULL;
252}
253
254static void mserial_release_port(struct uart_port *port)
255{
256	release_mem_region(port->mapbase, MSERIAL_REGION);
257	iounmap(port->membase);
258	port->membase = NULL;
259}
260
261static int mserial_request_port(struct uart_port *port)
262{
263	pr_debug("mserial console: port=%p; port->mapbase=%llx\n",
264		 port, (unsigned long long) port->mapbase);
265
266	if (!request_mem_region(port->mapbase, MSERIAL_REGION, "mserial")) {
267		dev_err(port->dev, "Memory region busy\n");
268		return -EBUSY;
269	}
270
271	port->membase = ioremap(port->mapbase, MSERIAL_REGION);
272	if (!port->membase) {
273		dev_err(port->dev, "Unable to map registers\n");
274		release_mem_region(port->mapbase, MSERIAL_REGION);
275		return -EBUSY;
276	}
277	return 0;
278}
279
280static void mserial_config_port(struct uart_port *port, int flags)
281{
282	if (!mserial_request_port(port))
283		port->type = PORT_MFOCSERIAL;
284}
285
286static int mserial_verify_port(struct uart_port *port, struct serial_struct *ser)
287{
288	/* we don't want the core code to modify any port params */
289	return -EINVAL;
290}
291
292static const struct uart_ops mserial_ops = {
293	.tx_empty	= mserial_tx_empty,
294	.set_mctrl	= mserial_set_mctrl,
295	.get_mctrl	= mserial_get_mctrl,
296	.stop_tx	= mserial_stop_tx,
297	.start_tx	= mserial_start_tx,
298	.stop_rx	= mserial_stop_rx,
299	.break_ctl	= mserial_break_ctl,
300	.startup	= mserial_startup,
301	.shutdown	= mserial_shutdown,
302	.set_termios	= mserial_set_termios,
303	.type		= mserial_type,
304	.release_port	= mserial_release_port,
305	.request_port	= mserial_request_port,
306	.config_port	= mserial_config_port,
307	.verify_port	= mserial_verify_port,
308};
309
310static struct uart_driver mserial_uart_driver = {
311	.owner		= THIS_MODULE,
312	.driver_name	= "mfocserial",
313	.dev_name	= MSERIAL_NAME,
314	.major		= MSERIAL_MAJOR,
315	.minor		= MSERIAL_MINOR,
316	.nr		= MSERIAL_NR_UARTS,
317};
318
319/* ---------------------------------------------------------------------
320 * Port assignment functions (mapping devices to uart_port structures)
321 */
322
323static int mserial_assign(struct device *dev, int id, phys_addr_t base,
324			  struct mserial_data *pdata)
325{
326	struct uart_port *port;
327	int rc;
328
329	/* if id = -1; then scan for a free id and use that */
330	if (id < 0) {
331		for (id = 0; id < MSERIAL_NR_UARTS; id++)
332			if (mserial_ports[id].mapbase == 0)
333				break;
334	}
335	if (id < 0 || id >= MSERIAL_NR_UARTS) {
336		dev_err(dev, "%s%i too large\n", MSERIAL_NAME, id);
337		return -EINVAL;
338	}
339
340	if ((mserial_ports[id].mapbase) && (mserial_ports[id].mapbase != base)) {
341		dev_err(dev, "cannot assign to %s%i; it is already in use\n",
342			MSERIAL_NAME, id);
343		return -EBUSY;
344	}
345
346	port = &mserial_ports[id];
347
348	spin_lock_init(&port->lock);
349	port->fifosize = 16;
350	port->regshift = 2;
351	port->iotype = UPIO_MEM;
352	port->iobase = 1; /* mark port in use */
353	port->mapbase = base;
354	port->membase = NULL;
355	port->ops = &mserial_ops;
356	port->flags = UPF_BOOT_AUTOCONF;
357	port->dev = dev;
358	port->type = PORT_UNKNOWN;
359	port->line = id;
360	port->private_data = pdata;
361	pdata->port = port;
362
363	dev_set_drvdata(dev, port);
364
365	pdata->pollrate = ktime_set(0, MSERIAL_POLLRATE);
366	hrtimer_init(&pdata->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
367	pdata->timer.function = mserial_poll;
368
369	/* Register the port */
370	rc = uart_add_one_port(&mserial_uart_driver, port);
371	if (rc) {
372		dev_err(dev, "uart_add_one_port() failed; err=%i\n", rc);
373		port->mapbase = 0;
374		dev_set_drvdata(dev, NULL);
375		return rc;
376	}
377
378	return 0;
379}
380
381static int mserial_release(struct device *dev)
382{
383	struct uart_port *port = dev_get_drvdata(dev);
384	int rc = 0;
385
386	if (port) {
387		rc = uart_remove_one_port(&mserial_uart_driver, port);
388		dev_set_drvdata(dev, NULL);
389		port->mapbase = 0;
390	}
391
392	return rc;
393}
394
395
396/* ---------------------------------------------------------------------
397 * Platform bus binding
398 */
399
400
401#if defined(CONFIG_OF)
402/* Match table for of_platform binding */
403static const struct of_device_id mserial_of_match[] = {
404	{ .compatible = "mfoc,mfocserial", },
405	{}
406};
407MODULE_DEVICE_TABLE(of, mserial_of_match);
408#endif /* CONFIG_OF */
409
410static int mserial_probe(struct platform_device *pdev)
411{
412	struct resource *res;
413	struct mserial_data *pdata;
414	int ret;
415	int id = pdev->id;
416
417	pdata = devm_kzalloc(&pdev->dev, sizeof(struct mserial_data),
418			     GFP_KERNEL);
419	if (!pdata)
420		return -ENOMEM;
421
422	if (IS_ENABLED(CONFIG_OF)) {
423		const char *prop;
424		struct device_node *np = pdev->dev.of_node;
425
426		prop = "port-number";
427		ret = of_property_read_u32(np, prop, &id);
428		if (ret && ret != -EINVAL)
429of_err:
430			return dev_err_probe(&pdev->dev, ret,
431					     "could not read %s\n", prop);
432
433		prop = "current-speed";
434		ret = of_property_read_u32(np, prop, &pdata->baud);
435		if (ret)
436			goto of_err;
437	} else {
438		pdata->baud = 9600;
439		pdata->cflags = CS8;
440	}
441
442	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
443	if (!res)
444		return -ENODEV;
445
446	if (!mserial_uart_driver.state) {
447		dev_dbg(&pdev->dev, "mfocserial: calling uart_register_driver()\n");
448		ret = uart_register_driver(&mserial_uart_driver);
449		if (ret < 0) {
450			dev_err(&pdev->dev, "Failed to register driver\n");
451			return ret;
452		}
453	}
454
455	ret = mserial_assign(&pdev->dev, id, res->start, pdata);
456
457	return ret;
458}
459
460static int mserial_remove(struct platform_device *pdev)
461{
462	return mserial_release(&pdev->dev);
463}
464
465/* work with hotplug and coldplug */
466MODULE_ALIAS("platform:mfocserial");
467
468static struct platform_driver mserial_platform_driver = {
469	.probe = mserial_probe,
470	.remove = mserial_remove,
471	.driver = {
472		.name  = "mfocserial",
473		.of_match_table = of_match_ptr(mserial_of_match),
474	},
475};
476
477static int __init mserial_init(void)
478{
479
480	pr_debug("mfocserial: calling platform_driver_register()\n");
481	return platform_driver_register(&mserial_platform_driver);
482}
483
484static void __exit mserial_exit(void)
485{
486	platform_driver_unregister(&mserial_platform_driver);
487	if (mserial_uart_driver.state)
488		uart_unregister_driver(&mserial_uart_driver);
489}
490
491module_init(mserial_init);
492module_exit(mserial_exit);
493
494MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
495MODULE_DESCRIPTION("MFOC Serial Driver");
496MODULE_LICENSE("GPL");

Device tree node

The corresponding devicetree node:

 1	     mfocserial@64000040 {
 2		     compatible = "mfoc,mfocserial";
 3		     reg = <0x64000040 0xf>;
 4		     port-number = <0>;
 5		     current-speed = <115200>;
 6		     status = "okay";
 7	     };
 8	     mfocserial@64000050 {
 9		     compatible = "mfoc,mfocserial";
10		     reg = <0x64000050 0xf>;
11		     port-number = <1>;
12		     current-speed = <115200>;
13		     status = "okay";
14	     };

Summary

Writing a UART driver is not more complicated than anything else. The UART framework makes it a smooth ride. Line disciplines and such is handled on a overlying layer and the UART drivers only needs to handle the very basic I/O communication - as most device drivers do.