2006-08-22 15:13:23

by Peter Korsgaard

[permalink] [raw]
Subject: Re: [RFC][PATCH] Xilinx uartlite serial driver

>>>>> "Peter" == Peter Korsgaard <[email protected]> writes:

Hi,

Peter> The following patch adds a driver for the Xilinx uartlite serial
Peter> controller used in boards with the PPC405 core in the Xilinx V2P/V4
Peter> fpgas.

Peter> The hardware is very simple (baudrate/start/stopbits fixed and
Peter> no break support). See the datasheet for details:

Peter> http://www.xilinx.com/bvdocs/ipcenter/data_sheet/opb_uartlite.pdf

Peter> Comments and suggestions are welcome. I'm especially wondering about
Peter> the fact that I'm hijacking the device nodes used by the mpc52xx_uart
Peter> driver ..

Ok, I now got a chunk of the 204 major range from LANANA. That afaik
solves the last remaining issue with this..

Signed-off-by: Peter Korsgaard <[email protected]>

diff -urpN linux-2.6.18-rc4.orig/drivers/serial/Kconfig linux-2.6.18-rc4/drivers/serial/Kconfig
--- linux-2.6.18-rc4.orig/drivers/serial/Kconfig 2006-08-22 16:19:15.000000000 +0200
+++ linux-2.6.18-rc4/drivers/serial/Kconfig 2006-08-22 16:24:25.000000000 +0200
@@ -511,6 +511,33 @@ config SERIAL_IMX_CONSOLE
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)

+config SERIAL_UARTLITE
+ tristate "Xilinx uartlite serial port support"
+ depends on PPC32
+ select SERIAL_CORE
+ help
+ Say Y here if you want to use the Xilinx uartlite serial controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called uartlite.ko.
+
+config SERIAL_UARTLITE_NR_UARTS
+ int "Maximum number of Xilinx uartlite serial ports"
+ depends on SERIAL_UARTLITE
+ default "4"
+ help
+ Set this to the number of serial ports you want the driver
+ to support.
+
+config SERIAL_UARTLITE_CONSOLE
+ bool "Support for console on Xilinx uartlite serial port"
+ depends on SERIAL_UARTLITE=y
+ select SERIAL_CORE_CONSOLE
+ help
+ Say Y here if you wish to use a Xilinx uartlite as the system
+ console (the system console is the device which receives all kernel
+ messages and warnings and which allows logins in single user mode).
+
config SERIAL_SUNCORE
bool
depends on SPARC
diff -urpN linux-2.6.18-rc4.orig/drivers/serial/Makefile linux-2.6.18-rc4/drivers/serial/Makefile
--- linux-2.6.18-rc4.orig/drivers/serial/Makefile 2006-08-22 16:19:15.000000000 +0200
+++ linux-2.6.18-rc4/drivers/serial/Makefile 2006-08-22 16:24:25.000000000 +0200
@@ -55,4 +55,5 @@ obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_si
obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o
obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
obj-$(CONFIG_SERIAL_AT91) += at91_serial.o
+obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
diff -urpN linux-2.6.18-rc4.orig/drivers/serial/uartlite.c linux-2.6.18-rc4/drivers/serial/uartlite.c
--- linux-2.6.18-rc4.orig/drivers/serial/uartlite.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.18-rc4/drivers/serial/uartlite.c 2006-08-22 16:48:08.000000000 +0200
@@ -0,0 +1,431 @@
+/*
+ * uartlite.c: Serial driver for Xilinx uartlite serial controller
+ *
+ * Peter Korsgaard <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/config.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#define SERIAL_ULITE_MAJOR 204
+#define SERIAL_ULITE_MINOR 187
+
+/* For register details see datasheet:
+ http://www.xilinx.com/bvdocs/ipcenter/data_sheet/opb_uartlite.pdf
+*/
+#define ULITE_RX 0x00
+#define ULITE_TX 0x04
+#define ULITE_STATUS 0x08
+#define ULITE_CONTROL 0x0c
+
+#define ULITE_STATUS_RXVALID 0x01
+#define ULITE_STATUS_RXFULL 0x02
+#define ULITE_STATUS_TXEMPTY 0x04
+#define ULITE_STATUS_TXFULL 0x08
+#define ULITE_STATUS_IE 0x10
+#define ULITE_STATUS_OVERRUN 0x20
+#define ULITE_STATUS_FRAME 0x40
+#define ULITE_STATUS_PARITY 0x80
+
+#define ULITE_CONTROL_RST_TX 0x01
+#define ULITE_CONTROL_RST_RX 0x02
+#define ULITE_CONTROL_IE 0x10
+
+
+static struct uart_port ports[CONFIG_SERIAL_UARTLITE_NR_UARTS];
+
+
+static inline unsigned int serial_in(struct uart_port *port, int offset)
+{
+ return readb(port->membase + offset);
+}
+
+static inline void serial_out(struct uart_port *port, int offset, int value)
+{
+ writeb(value, port->membase + offset);
+}
+
+static unsigned int ulite_tx_empty(struct uart_port *port)
+{
+ unsigned long flags;
+ unsigned int ret;
+
+ spin_lock_irqsave(&port->lock, flags);
+ ret = serial_in(port, ULITE_STATUS);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int ulite_get_mctrl(struct uart_port *port)
+{
+ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void ulite_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /* N/A */
+}
+
+static void ulite_stop_tx(struct uart_port *port)
+{
+ /* N/A */
+}
+
+static void ulite_start_tx(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->info->xmit;
+ int stat;
+
+ stat = serial_in(port, ULITE_STATUS);
+ if (!(stat & ULITE_STATUS_TXFULL)) {
+ serial_out(port, ULITE_TX, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ }
+}
+
+static void ulite_stop_rx(struct uart_port *port)
+{
+ /* N/A */
+}
+
+static void ulite_enable_ms(struct uart_port *port)
+{
+ /* N/A */
+}
+
+static void ulite_break_ctl(struct uart_port *port, int ctl)
+{
+ /* N/A */
+}
+
+static irqreturn_t ulite_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct uart_port *port = (struct uart_port *)dev_id;
+ struct tty_struct *tty = port->info->tty;
+ struct circ_buf *xmit = &port->info->xmit;
+ int busy;
+
+ do {
+ int stat = serial_in(port, ULITE_STATUS);
+
+ busy = 0;
+
+ if (stat & (ULITE_STATUS_OVERRUN | ULITE_STATUS_FRAME
+ | ULITE_STATUS_RXVALID)) {
+ busy = 1;
+
+ if (stat & ULITE_STATUS_OVERRUN) {
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ port->icount.overrun++;
+ }
+
+ if (stat & ULITE_STATUS_FRAME) {
+ tty_insert_flip_char(tty, 0, TTY_FRAME);
+ port->icount.frame++;
+ }
+
+ if (stat & ULITE_STATUS_RXVALID) {
+ unsigned char flag = TTY_NORMAL;
+
+ if (stat & ULITE_STATUS_PARITY) {
+ flag = TTY_PARITY; /* still received */
+ port->icount.parity++;
+ }
+
+ tty_insert_flip_char(
+ tty, serial_in(port, ULITE_RX), flag);
+ port->icount.rx++;
+ }
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+ continue;
+
+ if (!(stat & ULITE_STATUS_TXFULL)) {
+ busy = 1;
+ serial_out(port, ULITE_TX, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ }
+
+ /* wake up */
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+ } while (busy);
+
+ tty_flip_buffer_push(tty);
+
+ return IRQ_HANDLED;
+}
+
+static int ulite_startup(struct uart_port *port)
+{
+ int ret;
+
+ ret = request_irq(port->irq, ulite_isr,
+ SA_INTERRUPT | SA_SAMPLE_RANDOM, "uartlite", port);
+ if (ret)
+ return ret;
+
+ serial_out(port, ULITE_CONTROL,
+ ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX);
+ serial_out(port, ULITE_CONTROL, ULITE_CONTROL_IE);
+
+ return 0;
+}
+
+static void ulite_shutdown(struct uart_port *port)
+{
+ serial_out(port, ULITE_CONTROL, 0);
+ free_irq(port->irq, port);
+}
+
+static void ulite_set_termios(struct uart_port *port, struct termios *termios,
+ struct termios *old)
+{
+ /* N/A */
+}
+
+static const char *ulite_type(struct uart_port *port)
+{
+ return port->type == PORT_UARTLITE ? "uartlite" : NULL;
+}
+
+static void ulite_release_port(struct uart_port *port)
+{
+ /* N/A */
+}
+
+static int ulite_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+static void ulite_config_port(struct uart_port *port, int flags)
+{
+ port->type = PORT_UARTLITE;
+}
+
+static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ /* we don't want the core code to modify any port params */
+ return -EINVAL;
+}
+
+static struct uart_ops ulite_ops = {
+ .tx_empty = ulite_tx_empty,
+ .set_mctrl = ulite_set_mctrl,
+ .get_mctrl = ulite_get_mctrl,
+ .stop_tx = ulite_stop_tx,
+ .start_tx = ulite_start_tx,
+ .stop_rx = ulite_stop_rx,
+ .enable_ms = ulite_enable_ms,
+ .break_ctl = ulite_break_ctl,
+ .startup = ulite_startup,
+ .shutdown = ulite_shutdown,
+ .set_termios = ulite_set_termios,
+ .type = ulite_type,
+ .release_port = ulite_release_port,
+ .request_port = ulite_request_port,
+ .config_port = ulite_config_port,
+ .verify_port = ulite_verify_port
+};
+
+#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
+
+static void ulite_console_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ struct uart_port *port = &ports[co->index];
+ int i;
+
+ /* disable interrupts */
+ serial_out(port, ULITE_CONTROL, 0);
+
+ for (i = 0; i < count; i++) {
+ /* line return handling */
+ if (*s == '\n')
+ serial_out(port, ULITE_TX, '\r');
+
+ serial_out(port, ULITE_TX, *s++);
+
+ /* wait until it's sent */
+ while (!(serial_in(port, ULITE_STATUS)
+ & ULITE_STATUS_TXEMPTY)) ;
+ }
+
+ /* restore interrupt state */
+ serial_out(port, ULITE_CONTROL, ULITE_CONTROL_IE);
+}
+
+static int __init ulite_console_setup(struct console *co, char *options)
+{
+ struct uart_port *port;
+
+ if (co->index < 0 || co->index >= CONFIG_SERIAL_UARTLITE_NR_UARTS)
+ return -EINVAL;
+
+ port = &ports[co->index];
+
+ /* not initialized yet? */
+ if (!port->type)
+ return -ENODEV;
+
+ return 0;
+}
+
+static struct uart_driver ulite_uart_driver;
+
+static struct console ulite_console = {
+ .name = "ttyUL",
+ .write = ulite_console_write,
+ .device = uart_console_device,
+ .setup = ulite_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1, /* Specified on the cmdline (e.g. console=ttyUL0 ) */
+ .data = &ulite_uart_driver,
+};
+
+static int __init ulite_console_init(void)
+{
+ register_console(&ulite_console);
+ return 0;
+}
+
+console_initcall(ulite_console_init);
+
+#endif /* CONFIG_SERIAL_UARTLITE_CONSOLE */
+
+static struct uart_driver ulite_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "uartlite",
+ .dev_name = "ttyUL",
+ .devfs_name = "ttyUL",
+ .major = SERIAL_ULITE_MAJOR,
+ .minor = SERIAL_ULITE_MINOR,
+ .nr = CONFIG_SERIAL_UARTLITE_NR_UARTS,
+#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
+ .cons = &ulite_console,
+#endif
+};
+
+static int __devinit ulite_probe(struct platform_device *pdev)
+{
+ struct resource *res, *res2;
+ struct uart_port *port;
+ int ret;
+
+ if (pdev->id < 0 || pdev->id >= CONFIG_SERIAL_UARTLITE_NR_UARTS)
+ return -EINVAL;
+
+ if (ports[pdev->id].iobase)
+ return -EBUSY;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res2)
+ return -ENODEV;
+
+ port = &ports[pdev->id];
+
+ port->fifosize = 16;
+ port->regshift = 2;
+ port->iotype = UPIO_MEM;
+ port->iobase = 1; /* mark port in use */
+ port->mapbase = res->start;
+ port->ops = &ulite_ops;
+ port->irq = res2->start;
+ port->flags = UPF_BOOT_AUTOCONF;
+ port->dev = &pdev->dev;
+
+ if (!request_mem_region(res->start, res->end - res->start + 1,
+ pdev->name)) {
+ dev_err(&pdev->dev, "Memory region busy\n");
+ ret = -EBUSY;
+ goto request_mem_failed;
+ }
+
+ port->membase = ioremap(res->start, res->end - res->start + 1);
+ if (!port->membase) {
+ dev_err(&pdev->dev, "Unable to map registers\n");
+ ret = -EIO;
+ goto map_failed;
+ }
+
+ uart_add_one_port(&ulite_uart_driver, port);
+ platform_set_drvdata(pdev, port);
+ return 0;
+
+ map_failed:
+ release_mem_region(res->start, res->end - res->start + 1);
+ request_mem_failed:
+
+ return ret;
+}
+
+static int ulite_remove(struct platform_device *pdev)
+{
+ struct uart_port *port = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (port)
+ uart_remove_one_port(&ulite_uart_driver, port);
+
+ port->iobase = 0;
+
+ return 0;
+}
+
+static struct platform_driver ulite_platform_driver = {
+ .probe = ulite_probe,
+ .remove = ulite_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "uartlite",
+ },
+};
+
+int __init ulite_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&ulite_uart_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&ulite_platform_driver);
+ if (ret)
+ uart_unregister_driver(&ulite_uart_driver);
+
+ return ret;
+}
+
+void __exit ulite_exit(void)
+{
+ platform_driver_unregister(&ulite_platform_driver);
+ uart_unregister_driver(&ulite_uart_driver);
+}
+
+module_init(ulite_init);
+module_exit(ulite_exit);
+
+MODULE_AUTHOR("Peter Korsgaard <[email protected]>");
+MODULE_DESCRIPTION("Xilinx uartlite serial driver");
+MODULE_LICENSE("GPL");
diff -urpN linux-2.6.18-rc4.orig/include/linux/serial.h linux-2.6.18-rc4/include/linux/serial.h
--- linux-2.6.18-rc4.orig/include/linux/serial.h 2006-06-18 03:49:35.000000000 +0200
+++ linux-2.6.18-rc4/include/linux/serial.h 2006-08-22 16:24:25.000000000 +0200
@@ -76,7 +76,8 @@ struct serial_struct {
#define PORT_16654 11
#define PORT_16850 12
#define PORT_RSA 13 /* RSA-DV II/S card */
-#define PORT_MAX 13
+#define PORT_UARTLITE 14
+#define PORT_MAX 14

#define SERIAL_IO_PORT 0
#define SERIAL_IO_HUB6 1
diff -urpN linux-2.6.18-rc4.orig/MAINTAINERS linux-2.6.18-rc4/MAINTAINERS
--- linux-2.6.18-rc4.orig/MAINTAINERS 2006-08-22 16:19:12.000000000 +0200
+++ linux-2.6.18-rc4/MAINTAINERS 2006-08-22 16:41:08.000000000 +0200
@@ -3288,6 +3288,12 @@ L: [email protected]
W: http://oss.sgi.com/projects/xfs
S: Supported

+XILINX UARTLITE SERIAL DRIVER
+P: Peter Korsgaard
+M: [email protected]
+L: [email protected]
+S: Maintained
+
X86 3-LEVEL PAGING (PAE) SUPPORT
P: Ingo Molnar
M: [email protected]

--
Bye, Peter Korsgaard


2006-09-12 14:34:42

by Olof Johansson

[permalink] [raw]
Subject: Re: [RFC][PATCH] Xilinx uartlite serial driver

On Tue, 22 Aug 2006 17:13:13 +0200 Peter Korsgaard <[email protected]> wrote:

> >>>>> "Peter" == Peter Korsgaard <[email protected]> writes:
>
> Hi,
>
> Peter> The following patch adds a driver for the Xilinx uartlite serial
> Peter> controller used in boards with the PPC405 core in the Xilinx V2P/V4
> Peter> fpgas.
>
> Peter> The hardware is very simple (baudrate/start/stopbits fixed and
> Peter> no break support). See the datasheet for details:
>
> Peter> http://www.xilinx.com/bvdocs/ipcenter/data_sheet/opb_uartlite.pdf
>
> Peter> Comments and suggestions are welcome. I'm especially wondering about
> Peter> the fact that I'm hijacking the device nodes used by the mpc52xx_uart
> Peter> driver ..
>
> Ok, I now got a chunk of the 204 major range from LANANA. That afaik
> solves the last remaining issue with this..

Hi,

I've been working on getting this running smoothly over the last few days.
In my opinion there's a few pieces missing:

1. There's no early boot console support for PPC. There's another
uartlite patch that's floating around that has this, very useful for
early bringup tasks. I'd like to see this expanded to include that as
well (the regular part of the other driver seems more or less broken
though, so this is a better base driver). That shouldn't stop this part
of the driver to go in though, just possible future work.

2. There seems to be some timeout issues with tx_empty. If I boot a
regular distro with getty, etc, I get veeeery slow console output right
when init starts up. Adding a small default timeout in the init of the
uart code seems to help -- the hanging thread seems to be sleeping in
uart_wait_until_sent(). I picked a value of '5', seems ok. (Also,
without this fix, getty won't start on the port for some reason, it
sits in the same timeout forever, or at least for a very long time).

3. It would be useful to demonstrate how to hook up the device all the
way through to the board port (i.e. add a config option and include it
in the ml403 platform code or similar). It's not hard, but it'd make it
easier for whomever comes next. Of course, with 4xx hopefully soon
moving over to arch/powerpc, this would be taken care of through the
device tree instead.

So, (2) is really the only thing that's needed (IMHO) before this goes
in, but the rest would be useful as well.


-Olof

2006-09-13 05:56:19

by Peter Korsgaard

[permalink] [raw]
Subject: Re: [RFC][PATCH] Xilinx uartlite serial driver

>>>>> "Olof" == Olof Johansson <[email protected]> writes:

Hi Olof, thanks for looking into the driver..

Olof> In my opinion there's a few pieces missing:

Olof> 1. There's no early boot console support for PPC. There's another
Olof> uartlite patch that's floating around that has this, very useful for
Olof> early bringup tasks. I'd like to see this expanded to include that as
Olof> well (the regular part of the other driver seems more or less broken
Olof> though, so this is a better base driver). That shouldn't stop this part
Olof> of the driver to go in though, just possible future work.

True. The board I have an uartlite on also has a 8250 so it hasn't
been an issue for me so far - But it would indeed be a nice
addition. Adding new stuff to arch/ppc at this moment isn't that
sensible, so perhaps it makes most sense to wait til 4xx is supported
under arch/powerpc?

Olof> 2. There seems to be some timeout issues with tx_empty. If I boot a
Olof> regular distro with getty, etc, I get veeeery slow console output right
Olof> when init starts up. Adding a small default timeout in the init of the
Olof> uart code seems to help -- the hanging thread seems to be sleeping in
Olof> uart_wait_until_sent(). I picked a value of '5', seems ok. (Also,
Olof> without this fix, getty won't start on the port for some reason, it
Olof> sits in the same timeout forever, or at least for a very long time).

Huh, that's odd - I haven't noticed that. I'll test it again and get
back to you.

Olof> 3. It would be useful to demonstrate how to hook up the device all the
Olof> way through to the board port (i.e. add a config option and include it
Olof> in the ml403 platform code or similar). It's not hard, but it'd make it
Olof> easier for whomever comes next. Of course, with 4xx hopefully soon
Olof> moving over to arch/powerpc, this would be taken care of through the
Olof> device tree instead.

Yes - Afaik none of the Xilinx boards comes with uartlites
configured so it's not usable out-of-the-box, but you can ofcause do a
setup with the proper #ifdefs like the 8250 stuff in virtex.c.

--
Bye, Peter Korsgaard

2006-09-13 13:39:20

by Peter Korsgaard

[permalink] [raw]
Subject: Re: [RFC][PATCH] Xilinx uartlite serial driver

>>>>> "Peter" == Peter Korsgaard <[email protected]> writes:

Olof> 2. There seems to be some timeout issues with tx_empty.

Peter> Huh, that's odd - I haven't noticed that. I'll test it again and get
Peter> back to you.

Ahh, found it - I didn't update the uart timeout in set_termios so it
used a very long timeout. Please give the updated patch a try.

Additional changes:
- Actually add the fixes I did in May on request of Russell
- Update for 2.6.18 (devfs and new IRQF defines)
- Removed the SERIAL_UARTLITE_NR_UARTS config option as we only have 4
minors reserved with lanana anyway.

Signed-off-by: Peter Korsgaard <[email protected]>

diff -urpN linux-2.6.18-rc7.orig/drivers/serial/Kconfig linux-2.6.18-rc7/drivers/serial/Kconfig
--- linux-2.6.18-rc7.orig/drivers/serial/Kconfig 2006-09-13 15:01:24.000000000 +0200
+++ linux-2.6.18-rc7/drivers/serial/Kconfig 2006-09-13 15:01:51.000000000 +0200
@@ -511,6 +511,25 @@ config SERIAL_IMX_CONSOLE
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)

+config SERIAL_UARTLITE
+ tristate "Xilinx uartlite serial port support"
+ depends on PPC32
+ select SERIAL_CORE
+ help
+ Say Y here if you want to use the Xilinx uartlite serial controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called uartlite.ko.
+
+config SERIAL_UARTLITE_CONSOLE
+ bool "Support for console on Xilinx uartlite serial port"
+ depends on SERIAL_UARTLITE=y
+ select SERIAL_CORE_CONSOLE
+ help
+ Say Y here if you wish to use a Xilinx uartlite as the system
+ console (the system console is the device which receives all kernel
+ messages and warnings and which allows logins in single user mode).
+
config SERIAL_SUNCORE
bool
depends on SPARC
diff -urpN linux-2.6.18-rc7.orig/drivers/serial/Makefile linux-2.6.18-rc7/drivers/serial/Makefile
--- linux-2.6.18-rc7.orig/drivers/serial/Makefile 2006-09-13 15:01:24.000000000 +0200
+++ linux-2.6.18-rc7/drivers/serial/Makefile 2006-09-13 15:01:51.000000000 +0200
@@ -55,4 +55,5 @@ obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_si
obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o
obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
obj-$(CONFIG_SERIAL_AT91) += at91_serial.o
+obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
diff -urpN linux-2.6.18-rc7.orig/drivers/serial/uartlite.c linux-2.6.18-rc7/drivers/serial/uartlite.c
--- linux-2.6.18-rc7.orig/drivers/serial/uartlite.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.18-rc7/drivers/serial/uartlite.c 2006-09-13 15:01:51.000000000 +0200
@@ -0,0 +1,496 @@
+/*
+ * uartlite.c: Serial driver for Xilinx uartlite serial controller
+ *
+ * Peter Korsgaard <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#define ULITE_MAJOR 204
+#define ULITE_MINOR 187
+#define ULITE_NR_UARTS 4
+
+/* For register details see datasheet:
+ http://www.xilinx.com/bvdocs/ipcenter/data_sheet/opb_uartlite.pdf
+*/
+#define ULITE_RX 0x00
+#define ULITE_TX 0x04
+#define ULITE_STATUS 0x08
+#define ULITE_CONTROL 0x0c
+
+#define ULITE_REGION 16
+
+#define ULITE_STATUS_RXVALID 0x01
+#define ULITE_STATUS_RXFULL 0x02
+#define ULITE_STATUS_TXEMPTY 0x04
+#define ULITE_STATUS_TXFULL 0x08
+#define ULITE_STATUS_IE 0x10
+#define ULITE_STATUS_OVERRUN 0x20
+#define ULITE_STATUS_FRAME 0x40
+#define ULITE_STATUS_PARITY 0x80
+
+#define ULITE_CONTROL_RST_TX 0x01
+#define ULITE_CONTROL_RST_RX 0x02
+#define ULITE_CONTROL_IE 0x10
+
+
+static struct uart_port ports[ULITE_NR_UARTS];
+
+static int ulite_receive(struct uart_port *port, int stat)
+{
+ struct tty_struct *tty = port->info->tty;
+ unsigned char ch = 0;
+ char flag = TTY_NORMAL;
+
+ if ((stat & (ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN
+ | ULITE_STATUS_FRAME)) == 0)
+ return 0;
+
+ /* stats */
+ if (stat & ULITE_STATUS_RXVALID) {
+ port->icount.rx++;
+ ch = readb(port->membase + ULITE_RX);
+
+ if (stat & ULITE_STATUS_PARITY)
+ port->icount.parity++;
+ }
+
+ if (stat & ULITE_STATUS_OVERRUN)
+ port->icount.overrun++;
+
+ if (stat & ULITE_STATUS_FRAME)
+ port->icount.frame++;
+
+
+ /* drop byte with parity error if IGNPAR specificed */
+ if (stat & port->ignore_status_mask & ULITE_STATUS_PARITY)
+ stat &= ~ULITE_STATUS_RXVALID;
+
+ stat &= port->read_status_mask;
+
+ if (stat & ULITE_STATUS_PARITY)
+ flag = TTY_PARITY;
+
+
+ stat &= ~port->ignore_status_mask;
+
+ if (stat & ULITE_STATUS_RXVALID)
+ tty_insert_flip_char(tty, ch, flag);
+
+ if (stat & ULITE_STATUS_FRAME)
+ tty_insert_flip_char(tty, 0, TTY_FRAME);
+
+ if (stat & ULITE_STATUS_OVERRUN)
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+
+ return 1;
+}
+
+static int ulite_transmit(struct uart_port *port, int stat)
+{
+ struct circ_buf *xmit = &port->info->xmit;
+
+ if (stat & ULITE_STATUS_TXFULL)
+ return 0;
+
+ if (port->x_char) {
+ writeb(port->x_char, port->membase + ULITE_TX);
+ port->x_char = 0;
+ port->icount.tx++;
+ return 1;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+ return 0;
+
+ writeb(xmit->buf[xmit->tail], port->membase + ULITE_TX);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
+ port->icount.tx++;
+
+ /* wake up */
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ return 1;
+}
+
+static irqreturn_t ulite_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct uart_port *port = (struct uart_port *)dev_id;
+ int busy;
+
+ do {
+ int stat = readb(port->membase + ULITE_STATUS);
+ busy = ulite_receive(port, stat);
+ busy |= ulite_transmit(port, stat);
+ } while (busy);
+
+ tty_flip_buffer_push(port->info->tty);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned int ulite_tx_empty(struct uart_port *port)
+{
+ unsigned long flags;
+ unsigned int ret;
+
+ spin_lock_irqsave(&port->lock, flags);
+ ret = readb(port->membase + ULITE_STATUS);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int ulite_get_mctrl(struct uart_port *port)
+{
+ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void ulite_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /* N/A */
+}
+
+static void ulite_stop_tx(struct uart_port *port)
+{
+ /* N/A */
+}
+
+static void ulite_start_tx(struct uart_port *port)
+{
+ ulite_transmit(port, readb(port->membase + ULITE_STATUS));
+}
+
+static void ulite_stop_rx(struct uart_port *port)
+{
+ /* don't forward any more data (like !CREAD) */
+ port->ignore_status_mask = ULITE_STATUS_RXVALID | ULITE_STATUS_PARITY
+ | ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN;
+}
+
+static void ulite_enable_ms(struct uart_port *port)
+{
+ /* N/A */
+}
+
+static void ulite_break_ctl(struct uart_port *port, int ctl)
+{
+ /* N/A */
+}
+
+static int ulite_startup(struct uart_port *port)
+{
+ int ret;
+
+ ret = request_irq(port->irq, ulite_isr,
+ IRQF_DISABLED | IRQF_SAMPLE_RANDOM, "uartlite", port);
+ if (ret)
+ return ret;
+
+ writeb(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX,
+ port->membase + ULITE_CONTROL);
+ writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL);
+
+ return 0;
+}
+
+static void ulite_shutdown(struct uart_port *port)
+{
+ writeb(0, port->membase + ULITE_CONTROL);
+ free_irq(port->irq, port);
+}
+
+static void ulite_set_termios(struct uart_port *port, struct termios *termios,
+ struct termios *old)
+{
+ unsigned long flags;
+ unsigned int baud;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ port->read_status_mask = ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN
+ | ULITE_STATUS_TXFULL;
+
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |=
+ ULITE_STATUS_PARITY | ULITE_STATUS_FRAME;
+
+ port->ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= ULITE_STATUS_PARITY
+ | ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN;
+
+ /* ignore all characters if CREAD is not set */
+ if ((termios->c_cflag & CREAD) == 0)
+ port->ignore_status_mask |=
+ ULITE_STATUS_RXVALID | ULITE_STATUS_PARITY
+ | ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN;
+
+ /* update timeout */
+ baud = uart_get_baud_rate(port, termios, old, 0, 460800);
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *ulite_type(struct uart_port *port)
+{
+ return port->type == PORT_UARTLITE ? "uartlite" : NULL;
+}
+
+static void ulite_release_port(struct uart_port *port)
+{
+ release_mem_region(port->mapbase, ULITE_REGION);
+ iounmap(port->membase);
+ port->membase = 0;
+}
+
+static int ulite_request_port(struct uart_port *port)
+{
+ if (!request_mem_region(port->mapbase, ULITE_REGION, "uartlite")) {
+ dev_err(port->dev, "Memory region busy\n");
+ return -EBUSY;
+ }
+
+ port->membase = ioremap(port->mapbase, ULITE_REGION);
+ if (!port->membase) {
+ dev_err(port->dev, "Unable to map registers\n");
+ release_mem_region(port->mapbase, ULITE_REGION);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void ulite_config_port(struct uart_port *port, int flags)
+{
+ ulite_request_port(port);
+ port->type = PORT_UARTLITE;
+}
+
+static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ /* we don't want the core code to modify any port params */
+ return -EINVAL;
+}
+
+static struct uart_ops ulite_ops = {
+ .tx_empty = ulite_tx_empty,
+ .set_mctrl = ulite_set_mctrl,
+ .get_mctrl = ulite_get_mctrl,
+ .stop_tx = ulite_stop_tx,
+ .start_tx = ulite_start_tx,
+ .stop_rx = ulite_stop_rx,
+ .enable_ms = ulite_enable_ms,
+ .break_ctl = ulite_break_ctl,
+ .startup = ulite_startup,
+ .shutdown = ulite_shutdown,
+ .set_termios = ulite_set_termios,
+ .type = ulite_type,
+ .release_port = ulite_release_port,
+ .request_port = ulite_request_port,
+ .config_port = ulite_config_port,
+ .verify_port = ulite_verify_port
+};
+
+#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
+static void ulite_console_wait_tx(struct uart_port *port)
+{
+ int i;
+
+ /* wait up to 10ms for the character(s) to be sent */
+ for (i=0; i<10000; i++) {
+ if (readb(port->membase + ULITE_STATUS) & ULITE_STATUS_TXEMPTY)
+ break;
+ udelay(1);
+ }
+}
+
+static void ulite_console_putchar(struct uart_port *port, int ch)
+{
+ ulite_console_wait_tx(port);
+ writeb(ch, port->membase + ULITE_TX);
+}
+
+static void ulite_console_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ struct uart_port *port = &ports[co->index];
+ unsigned long flags;
+ unsigned int ier;
+ int locked = 1;
+
+ if (oops_in_progress) {
+ locked = spin_trylock_irqsave(&port->lock, flags);
+ } else
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* save and disable interrupt */
+ ier = readb(port->membase + ULITE_STATUS) & ULITE_STATUS_IE;
+ writeb(0, port->membase + ULITE_CONTROL);
+
+ uart_console_write(port, s, count, ulite_console_putchar);
+
+ ulite_console_wait_tx(port);
+
+ /* restore interrupt state */
+ if (ier)
+ writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL);
+
+ if (locked)
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int __init ulite_console_setup(struct console *co, char *options)
+{
+ struct uart_port *port;
+
+ if (co->index < 0 || co->index >= ULITE_NR_UARTS)
+ return -EINVAL;
+
+ port = &ports[co->index];
+
+ /* not initialized yet? */
+ if (!port->membase)
+ return -ENODEV;
+
+ return 0;
+}
+
+static struct uart_driver ulite_uart_driver;
+
+static struct console ulite_console = {
+ .name = "ttyUL",
+ .write = ulite_console_write,
+ .device = uart_console_device,
+ .setup = ulite_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1, /* Specified on the cmdline (e.g. console=ttyUL0 ) */
+ .data = &ulite_uart_driver,
+};
+
+static int __init ulite_console_init(void)
+{
+ register_console(&ulite_console);
+ return 0;
+}
+
+console_initcall(ulite_console_init);
+
+#endif /* CONFIG_SERIAL_UARTLITE_CONSOLE */
+
+static struct uart_driver ulite_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "uartlite",
+ .dev_name = "ttyUL",
+ .major = ULITE_MAJOR,
+ .minor = ULITE_MINOR,
+ .nr = ULITE_NR_UARTS,
+#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
+ .cons = &ulite_console,
+#endif
+};
+
+static int __devinit ulite_probe(struct platform_device *pdev)
+{
+ struct resource *res, *res2;
+ struct uart_port *port;
+
+ if (pdev->id < 0 || pdev->id >= ULITE_NR_UARTS)
+ return -EINVAL;
+
+ if (ports[pdev->id].membase)
+ return -EBUSY;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res2)
+ return -ENODEV;
+
+ port = &ports[pdev->id];
+
+ port->fifosize = 16;
+ port->regshift = 2;
+ port->iotype = UPIO_MEM;
+ port->iobase = 1; /* mark port in use */
+ port->mapbase = res->start;
+ port->membase = 0;
+ port->ops = &ulite_ops;
+ port->irq = res2->start;
+ port->flags = UPF_BOOT_AUTOCONF;
+ port->dev = &pdev->dev;
+ port->type = PORT_UNKNOWN;
+
+ uart_add_one_port(&ulite_uart_driver, port);
+ platform_set_drvdata(pdev, port);
+
+ return 0;
+}
+
+static int ulite_remove(struct platform_device *pdev)
+{
+ struct uart_port *port = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (port)
+ uart_remove_one_port(&ulite_uart_driver, port);
+
+ /* mark port as free */
+ port->membase = 0;
+
+ return 0;
+}
+
+static struct platform_driver ulite_platform_driver = {
+ .probe = ulite_probe,
+ .remove = ulite_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "uartlite",
+ },
+};
+
+int __init ulite_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&ulite_uart_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&ulite_platform_driver);
+ if (ret)
+ uart_unregister_driver(&ulite_uart_driver);
+
+ return ret;
+}
+
+void __exit ulite_exit(void)
+{
+ platform_driver_unregister(&ulite_platform_driver);
+ uart_unregister_driver(&ulite_uart_driver);
+}
+
+module_init(ulite_init);
+module_exit(ulite_exit);
+
+MODULE_AUTHOR("Peter Korsgaard <[email protected]>");
+MODULE_DESCRIPTION("Xilinx uartlite serial driver");
+MODULE_LICENSE("GPL");
diff -urpN linux-2.6.18-rc7.orig/include/linux/serial_core.h linux-2.6.18-rc7/include/linux/serial_core.h
--- linux-2.6.18-rc7.orig/include/linux/serial_core.h 2006-09-13 15:01:25.000000000 +0200
+++ linux-2.6.18-rc7/include/linux/serial_core.h 2006-09-13 15:01:51.000000000 +0200
@@ -132,6 +132,8 @@

#define PORT_S3C2412 73

+/* Xilinx uartlite */
+#define PORT_UARTLITE 74

#ifdef __KERNEL__

diff -urpN linux-2.6.18-rc7.orig/MAINTAINERS linux-2.6.18-rc7/MAINTAINERS
--- linux-2.6.18-rc7.orig/MAINTAINERS 2006-09-13 15:01:20.000000000 +0200
+++ linux-2.6.18-rc7/MAINTAINERS 2006-09-13 15:01:51.000000000 +0200
@@ -3303,6 +3303,12 @@ W: http://oss.sgi.com/projects/xfs
T: git git://oss.sgi.com:8090/xfs/xfs-2.6
S: Supported

+XILINX UARTLITE SERIAL DRIVER
+P: Peter Korsgaard
+M: [email protected]
+L: [email protected]
+S: Maintained
+
X86 3-LEVEL PAGING (PAE) SUPPORT
P: Ingo Molnar
M: [email protected]

--
Bye, Peter Korsgaard

2006-09-13 15:02:55

by Olof Johansson

[permalink] [raw]
Subject: Re: [RFC][PATCH] Xilinx uartlite serial driver

On Wed, 13 Sep 2006 15:39:09 +0200 Peter Korsgaard <[email protected]> wrote:

> >>>>> "Peter" == Peter Korsgaard <[email protected]> writes:
>
> Olof> 2. There seems to be some timeout issues with tx_empty.
>
> Peter> Huh, that's odd - I haven't noticed that. I'll test it again and get
> Peter> back to you.
>
> Ahh, found it - I didn't update the uart timeout in set_termios so it
> used a very long timeout. Please give the updated patch a try.

Looks good. Thanks.

> Additional changes:
> - Actually add the fixes I did in May on request of Russell
> - Update for 2.6.18 (devfs and new IRQF defines)
> - Removed the SERIAL_UARTLITE_NR_UARTS config option as we only have 4
> minors reserved with lanana anyway.
>
> Signed-off-by: Peter Korsgaard <[email protected]>

Acked-by: Olof Johansson <[email protected]>

>
> diff -urpN linux-2.6.18-rc7.orig/drivers/serial/Kconfig linux-2.6.18-rc7/drivers/serial/Kconfig
> --- linux-2.6.18-rc7.orig/drivers/serial/Kconfig 2006-09-13 15:01:24.000000000 +0200
> +++ linux-2.6.18-rc7/drivers/serial/Kconfig 2006-09-13 15:01:51.000000000 +0200
> @@ -511,6 +511,25 @@ config SERIAL_IMX_CONSOLE
> your boot loader (lilo or loadlin) about how to pass options to the
> kernel at boot time.)
>
> +config SERIAL_UARTLITE
> + tristate "Xilinx uartlite serial port support"
> + depends on PPC32
> + select SERIAL_CORE
> + help
> + Say Y here if you want to use the Xilinx uartlite serial controller.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called uartlite.ko.
> +
> +config SERIAL_UARTLITE_CONSOLE
> + bool "Support for console on Xilinx uartlite serial port"
> + depends on SERIAL_UARTLITE=y
> + select SERIAL_CORE_CONSOLE
> + help
> + Say Y here if you wish to use a Xilinx uartlite as the system
> + console (the system console is the device which receives all kernel
> + messages and warnings and which allows logins in single user mode).
> +
> config SERIAL_SUNCORE
> bool
> depends on SPARC
> diff -urpN linux-2.6.18-rc7.orig/drivers/serial/Makefile linux-2.6.18-rc7/drivers/serial/Makefile
> --- linux-2.6.18-rc7.orig/drivers/serial/Makefile 2006-09-13 15:01:24.000000000 +0200
> +++ linux-2.6.18-rc7/drivers/serial/Makefile 2006-09-13 15:01:51.000000000 +0200
> @@ -55,4 +55,5 @@ obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_si
> obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o
> obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
> obj-$(CONFIG_SERIAL_AT91) += at91_serial.o
> +obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
> obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
> diff -urpN linux-2.6.18-rc7.orig/drivers/serial/uartlite.c linux-2.6.18-rc7/drivers/serial/uartlite.c
> --- linux-2.6.18-rc7.orig/drivers/serial/uartlite.c 1970-01-01 01:00:00.000000000 +0100
> +++ linux-2.6.18-rc7/drivers/serial/uartlite.c 2006-09-13 15:01:51.000000000 +0200
> @@ -0,0 +1,496 @@
> +/*
> + * uartlite.c: Serial driver for Xilinx uartlite serial controller
> + *
> + * Peter Korsgaard <[email protected]>
> + *
> + * This file is licensed under the terms of the GNU General Public License
> + * version 2. This program is licensed "as is" without any warranty of any
> + * kind, whether express or implied.
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/console.h>
> +#include <linux/serial.h>
> +#include <linux/serial_core.h>
> +#include <linux/tty.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <asm/io.h>
> +
> +#define ULITE_MAJOR 204
> +#define ULITE_MINOR 187
> +#define ULITE_NR_UARTS 4
> +
> +/* For register details see datasheet:
> + http://www.xilinx.com/bvdocs/ipcenter/data_sheet/opb_uartlite.pdf
> +*/
> +#define ULITE_RX 0x00
> +#define ULITE_TX 0x04
> +#define ULITE_STATUS 0x08
> +#define ULITE_CONTROL 0x0c
> +
> +#define ULITE_REGION 16
> +
> +#define ULITE_STATUS_RXVALID 0x01
> +#define ULITE_STATUS_RXFULL 0x02
> +#define ULITE_STATUS_TXEMPTY 0x04
> +#define ULITE_STATUS_TXFULL 0x08
> +#define ULITE_STATUS_IE 0x10
> +#define ULITE_STATUS_OVERRUN 0x20
> +#define ULITE_STATUS_FRAME 0x40
> +#define ULITE_STATUS_PARITY 0x80
> +
> +#define ULITE_CONTROL_RST_TX 0x01
> +#define ULITE_CONTROL_RST_RX 0x02
> +#define ULITE_CONTROL_IE 0x10
> +
> +
> +static struct uart_port ports[ULITE_NR_UARTS];
> +
> +static int ulite_receive(struct uart_port *port, int stat)
> +{
> + struct tty_struct *tty = port->info->tty;
> + unsigned char ch = 0;
> + char flag = TTY_NORMAL;
> +
> + if ((stat & (ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN
> + | ULITE_STATUS_FRAME)) == 0)
> + return 0;
> +
> + /* stats */
> + if (stat & ULITE_STATUS_RXVALID) {
> + port->icount.rx++;
> + ch = readb(port->membase + ULITE_RX);
> +
> + if (stat & ULITE_STATUS_PARITY)
> + port->icount.parity++;
> + }
> +
> + if (stat & ULITE_STATUS_OVERRUN)
> + port->icount.overrun++;
> +
> + if (stat & ULITE_STATUS_FRAME)
> + port->icount.frame++;
> +
> +
> + /* drop byte with parity error if IGNPAR specificed */
> + if (stat & port->ignore_status_mask & ULITE_STATUS_PARITY)
> + stat &= ~ULITE_STATUS_RXVALID;
> +
> + stat &= port->read_status_mask;
> +
> + if (stat & ULITE_STATUS_PARITY)
> + flag = TTY_PARITY;
> +
> +
> + stat &= ~port->ignore_status_mask;
> +
> + if (stat & ULITE_STATUS_RXVALID)
> + tty_insert_flip_char(tty, ch, flag);
> +
> + if (stat & ULITE_STATUS_FRAME)
> + tty_insert_flip_char(tty, 0, TTY_FRAME);
> +
> + if (stat & ULITE_STATUS_OVERRUN)
> + tty_insert_flip_char(tty, 0, TTY_OVERRUN);
> +
> + return 1;
> +}
> +
> +static int ulite_transmit(struct uart_port *port, int stat)
> +{
> + struct circ_buf *xmit = &port->info->xmit;
> +
> + if (stat & ULITE_STATUS_TXFULL)
> + return 0;
> +
> + if (port->x_char) {
> + writeb(port->x_char, port->membase + ULITE_TX);
> + port->x_char = 0;
> + port->icount.tx++;
> + return 1;
> + }
> +
> + if (uart_circ_empty(xmit) || uart_tx_stopped(port))
> + return 0;
> +
> + writeb(xmit->buf[xmit->tail], port->membase + ULITE_TX);
> + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
> + port->icount.tx++;
> +
> + /* wake up */
> + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + uart_write_wakeup(port);
> +
> + return 1;
> +}
> +
> +static irqreturn_t ulite_isr(int irq, void *dev_id, struct pt_regs *regs)
> +{
> + struct uart_port *port = (struct uart_port *)dev_id;
> + int busy;
> +
> + do {
> + int stat = readb(port->membase + ULITE_STATUS);
> + busy = ulite_receive(port, stat);
> + busy |= ulite_transmit(port, stat);
> + } while (busy);
> +
> + tty_flip_buffer_push(port->info->tty);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static unsigned int ulite_tx_empty(struct uart_port *port)
> +{
> + unsigned long flags;
> + unsigned int ret;
> +
> + spin_lock_irqsave(&port->lock, flags);
> + ret = readb(port->membase + ULITE_STATUS);
> + spin_unlock_irqrestore(&port->lock, flags);
> +
> + return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0;
> +}
> +
> +static unsigned int ulite_get_mctrl(struct uart_port *port)
> +{
> + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
> +}
> +
> +static void ulite_set_mctrl(struct uart_port *port, unsigned int mctrl)
> +{
> + /* N/A */
> +}
> +
> +static void ulite_stop_tx(struct uart_port *port)
> +{
> + /* N/A */
> +}
> +
> +static void ulite_start_tx(struct uart_port *port)
> +{
> + ulite_transmit(port, readb(port->membase + ULITE_STATUS));
> +}
> +
> +static void ulite_stop_rx(struct uart_port *port)
> +{
> + /* don't forward any more data (like !CREAD) */
> + port->ignore_status_mask = ULITE_STATUS_RXVALID | ULITE_STATUS_PARITY
> + | ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN;
> +}
> +
> +static void ulite_enable_ms(struct uart_port *port)
> +{
> + /* N/A */
> +}
> +
> +static void ulite_break_ctl(struct uart_port *port, int ctl)
> +{
> + /* N/A */
> +}
> +
> +static int ulite_startup(struct uart_port *port)
> +{
> + int ret;
> +
> + ret = request_irq(port->irq, ulite_isr,
> + IRQF_DISABLED | IRQF_SAMPLE_RANDOM, "uartlite", port);
> + if (ret)
> + return ret;
> +
> + writeb(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX,
> + port->membase + ULITE_CONTROL);
> + writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL);
> +
> + return 0;
> +}
> +
> +static void ulite_shutdown(struct uart_port *port)
> +{
> + writeb(0, port->membase + ULITE_CONTROL);
> + free_irq(port->irq, port);
> +}
> +
> +static void ulite_set_termios(struct uart_port *port, struct termios *termios,
> + struct termios *old)
> +{
> + unsigned long flags;
> + unsigned int baud;
> +
> + spin_lock_irqsave(&port->lock, flags);
> +
> + port->read_status_mask = ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN
> + | ULITE_STATUS_TXFULL;
> +
> + if (termios->c_iflag & INPCK)
> + port->read_status_mask |=
> + ULITE_STATUS_PARITY | ULITE_STATUS_FRAME;
> +
> + port->ignore_status_mask = 0;
> + if (termios->c_iflag & IGNPAR)
> + port->ignore_status_mask |= ULITE_STATUS_PARITY
> + | ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN;
> +
> + /* ignore all characters if CREAD is not set */
> + if ((termios->c_cflag & CREAD) == 0)
> + port->ignore_status_mask |=
> + ULITE_STATUS_RXVALID | ULITE_STATUS_PARITY
> + | ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN;
> +
> + /* update timeout */
> + baud = uart_get_baud_rate(port, termios, old, 0, 460800);
> + uart_update_timeout(port, termios->c_cflag, baud);
> +
> + spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +static const char *ulite_type(struct uart_port *port)
> +{
> + return port->type == PORT_UARTLITE ? "uartlite" : NULL;
> +}
> +
> +static void ulite_release_port(struct uart_port *port)
> +{
> + release_mem_region(port->mapbase, ULITE_REGION);
> + iounmap(port->membase);
> + port->membase = 0;
> +}
> +
> +static int ulite_request_port(struct uart_port *port)
> +{
> + if (!request_mem_region(port->mapbase, ULITE_REGION, "uartlite")) {
> + dev_err(port->dev, "Memory region busy\n");
> + return -EBUSY;
> + }
> +
> + port->membase = ioremap(port->mapbase, ULITE_REGION);
> + if (!port->membase) {
> + dev_err(port->dev, "Unable to map registers\n");
> + release_mem_region(port->mapbase, ULITE_REGION);
> + return -EBUSY;
> + }
> +
> + return 0;
> +}
> +
> +static void ulite_config_port(struct uart_port *port, int flags)
> +{
> + ulite_request_port(port);
> + port->type = PORT_UARTLITE;
> +}
> +
> +static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser)
> +{
> + /* we don't want the core code to modify any port params */
> + return -EINVAL;
> +}
> +
> +static struct uart_ops ulite_ops = {
> + .tx_empty = ulite_tx_empty,
> + .set_mctrl = ulite_set_mctrl,
> + .get_mctrl = ulite_get_mctrl,
> + .stop_tx = ulite_stop_tx,
> + .start_tx = ulite_start_tx,
> + .stop_rx = ulite_stop_rx,
> + .enable_ms = ulite_enable_ms,
> + .break_ctl = ulite_break_ctl,
> + .startup = ulite_startup,
> + .shutdown = ulite_shutdown,
> + .set_termios = ulite_set_termios,
> + .type = ulite_type,
> + .release_port = ulite_release_port,
> + .request_port = ulite_request_port,
> + .config_port = ulite_config_port,
> + .verify_port = ulite_verify_port
> +};
> +
> +#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
> +static void ulite_console_wait_tx(struct uart_port *port)
> +{
> + int i;
> +
> + /* wait up to 10ms for the character(s) to be sent */
> + for (i=0; i<10000; i++) {
> + if (readb(port->membase + ULITE_STATUS) & ULITE_STATUS_TXEMPTY)
> + break;
> + udelay(1);
> + }
> +}
> +
> +static void ulite_console_putchar(struct uart_port *port, int ch)
> +{
> + ulite_console_wait_tx(port);
> + writeb(ch, port->membase + ULITE_TX);
> +}
> +
> +static void ulite_console_write(struct console *co, const char *s,
> + unsigned int count)
> +{
> + struct uart_port *port = &ports[co->index];
> + unsigned long flags;
> + unsigned int ier;
> + int locked = 1;
> +
> + if (oops_in_progress) {
> + locked = spin_trylock_irqsave(&port->lock, flags);
> + } else
> + spin_lock_irqsave(&port->lock, flags);
> +
> + /* save and disable interrupt */
> + ier = readb(port->membase + ULITE_STATUS) & ULITE_STATUS_IE;
> + writeb(0, port->membase + ULITE_CONTROL);
> +
> + uart_console_write(port, s, count, ulite_console_putchar);
> +
> + ulite_console_wait_tx(port);
> +
> + /* restore interrupt state */
> + if (ier)
> + writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL);
> +
> + if (locked)
> + spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
> +static int __init ulite_console_setup(struct console *co, char *options)
> +{
> + struct uart_port *port;
> +
> + if (co->index < 0 || co->index >= ULITE_NR_UARTS)
> + return -EINVAL;
> +
> + port = &ports[co->index];
> +
> + /* not initialized yet? */
> + if (!port->membase)
> + return -ENODEV;
> +
> + return 0;
> +}
> +
> +static struct uart_driver ulite_uart_driver;
> +
> +static struct console ulite_console = {
> + .name = "ttyUL",
> + .write = ulite_console_write,
> + .device = uart_console_device,
> + .setup = ulite_console_setup,
> + .flags = CON_PRINTBUFFER,
> + .index = -1, /* Specified on the cmdline (e.g. console=ttyUL0 ) */
> + .data = &ulite_uart_driver,
> +};
> +
> +static int __init ulite_console_init(void)
> +{
> + register_console(&ulite_console);
> + return 0;
> +}
> +
> +console_initcall(ulite_console_init);
> +
> +#endif /* CONFIG_SERIAL_UARTLITE_CONSOLE */
> +
> +static struct uart_driver ulite_uart_driver = {
> + .owner = THIS_MODULE,
> + .driver_name = "uartlite",
> + .dev_name = "ttyUL",
> + .major = ULITE_MAJOR,
> + .minor = ULITE_MINOR,
> + .nr = ULITE_NR_UARTS,
> +#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
> + .cons = &ulite_console,
> +#endif
> +};
> +
> +static int __devinit ulite_probe(struct platform_device *pdev)
> +{
> + struct resource *res, *res2;
> + struct uart_port *port;
> +
> + if (pdev->id < 0 || pdev->id >= ULITE_NR_UARTS)
> + return -EINVAL;
> +
> + if (ports[pdev->id].membase)
> + return -EBUSY;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res)
> + return -ENODEV;
> +
> + res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + if (!res2)
> + return -ENODEV;
> +
> + port = &ports[pdev->id];
> +
> + port->fifosize = 16;
> + port->regshift = 2;
> + port->iotype = UPIO_MEM;
> + port->iobase = 1; /* mark port in use */
> + port->mapbase = res->start;
> + port->membase = 0;
> + port->ops = &ulite_ops;
> + port->irq = res2->start;
> + port->flags = UPF_BOOT_AUTOCONF;
> + port->dev = &pdev->dev;
> + port->type = PORT_UNKNOWN;
> +
> + uart_add_one_port(&ulite_uart_driver, port);
> + platform_set_drvdata(pdev, port);
> +
> + return 0;
> +}
> +
> +static int ulite_remove(struct platform_device *pdev)
> +{
> + struct uart_port *port = platform_get_drvdata(pdev);
> +
> + platform_set_drvdata(pdev, NULL);
> +
> + if (port)
> + uart_remove_one_port(&ulite_uart_driver, port);
> +
> + /* mark port as free */
> + port->membase = 0;
> +
> + return 0;
> +}
> +
> +static struct platform_driver ulite_platform_driver = {
> + .probe = ulite_probe,
> + .remove = ulite_remove,
> + .driver = {
> + .owner = THIS_MODULE,
> + .name = "uartlite",
> + },
> +};
> +
> +int __init ulite_init(void)
> +{
> + int ret;
> +
> + ret = uart_register_driver(&ulite_uart_driver);
> + if (ret)
> + return ret;
> +
> + ret = platform_driver_register(&ulite_platform_driver);
> + if (ret)
> + uart_unregister_driver(&ulite_uart_driver);
> +
> + return ret;
> +}
> +
> +void __exit ulite_exit(void)
> +{
> + platform_driver_unregister(&ulite_platform_driver);
> + uart_unregister_driver(&ulite_uart_driver);
> +}
> +
> +module_init(ulite_init);
> +module_exit(ulite_exit);
> +
> +MODULE_AUTHOR("Peter Korsgaard <[email protected]>");
> +MODULE_DESCRIPTION("Xilinx uartlite serial driver");
> +MODULE_LICENSE("GPL");
> diff -urpN linux-2.6.18-rc7.orig/include/linux/serial_core.h linux-2.6.18-rc7/include/linux/serial_core.h
> --- linux-2.6.18-rc7.orig/include/linux/serial_core.h 2006-09-13 15:01:25.000000000 +0200
> +++ linux-2.6.18-rc7/include/linux/serial_core.h 2006-09-13 15:01:51.000000000 +0200
> @@ -132,6 +132,8 @@
>
> #define PORT_S3C2412 73
>
> +/* Xilinx uartlite */
> +#define PORT_UARTLITE 74
>
> #ifdef __KERNEL__
>
> diff -urpN linux-2.6.18-rc7.orig/MAINTAINERS linux-2.6.18-rc7/MAINTAINERS
> --- linux-2.6.18-rc7.orig/MAINTAINERS 2006-09-13 15:01:20.000000000 +0200
> +++ linux-2.6.18-rc7/MAINTAINERS 2006-09-13 15:01:51.000000000 +0200
> @@ -3303,6 +3303,12 @@ W: http://oss.sgi.com/projects/xfs
> T: git git://oss.sgi.com:8090/xfs/xfs-2.6
> S: Supported
>
> +XILINX UARTLITE SERIAL DRIVER
> +P: Peter Korsgaard
> +M: [email protected]
> +L: [email protected]
> +S: Maintained
> +
> X86 3-LEVEL PAGING (PAE) SUPPORT
> P: Ingo Molnar
> M: [email protected]
>

2006-10-19 23:08:08

by Olof Johansson

[permalink] [raw]
Subject: Re: [RFC][PATCH] Xilinx uartlite serial driver

On Wed, 13 Sep 2006 15:39:09 +0200 Peter Korsgaard <[email protected]> wrote:

> +static int __devinit ulite_probe(struct platform_device *pdev)
[...]
> + port = &ports[pdev->id];
> +
> + port->fifosize = 16;
> + port->regshift = 2;
> + port->iotype = UPIO_MEM;
> + port->iobase = 1; /* mark port in use */
> + port->mapbase = res->start;
> + port->membase = 0;
> + port->ops = &ulite_ops;
> + port->irq = res2->start;
> + port->flags = UPF_BOOT_AUTOCONF;
> + port->dev = &pdev->dev;
> + port->type = PORT_UNKNOWN;

Hi Peter,

You never fill in the 'line' member here, so probing of a second
uartlite port will fail.

Add:
port->line = pdev->id;


-Olof

2006-10-20 12:23:14

by Peter Korsgaard

[permalink] [raw]
Subject: Re: [RFC][PATCH] Xilinx uartlite serial driver

>>>>> "Olof" == Olof Johansson <[email protected]> writes:

>> +static int __devinit ulite_probe(struct platform_device *pdev)

Olof> You never fill in the 'line' member here, so probing of a second
Olof> uartlite port will fail.

Ah yes, sorry about that. I'll send a patch to Andrew.

--
Bye, Peter Korsgaard