The patch adds support for 3 additional LP-8x4x built-in serial
ports.

The device can also host up to 8 extension cards with 4 serial ports
on each card for a total of 35 ports. However, I don't have
the hardware to test extension cards, so they are not supported, yet.

Signed-off-by: Sergei Ianovich <ynv...@gmail.com>
---
 arch/arm/configs/lp8x4x_defconfig       |   1 +
 arch/arm/mach-pxa/include/mach/lp8x4x.h |   6 ++
 drivers/tty/serial/8250/8250_lp8x4x.c   | 181 ++++++++++++++++++++++++++++++++
 drivers/tty/serial/8250/Kconfig         |  11 ++
 drivers/tty/serial/8250/Makefile        |   1 +
 5 files changed, 200 insertions(+)
 create mode 100644 drivers/tty/serial/8250/8250_lp8x4x.c

diff --git a/arch/arm/configs/lp8x4x_defconfig 
b/arch/arm/configs/lp8x4x_defconfig
index 03c85bc..d297a67 100644
--- a/arch/arm/configs/lp8x4x_defconfig
+++ b/arch/arm/configs/lp8x4x_defconfig
@@ -1141,6 +1141,7 @@ CONFIG_SERIAL_8250_NR_UARTS=36
 CONFIG_SERIAL_8250_RUNTIME_UARTS=36
 CONFIG_SERIAL_8250_EXTENDED=y
 CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_LP8X4X=m
 CONFIG_SERIAL_8250_SHARE_IRQ=y
 # CONFIG_SERIAL_8250_DETECT_IRQ is not set
 # CONFIG_SERIAL_8250_RSA is not set
diff --git a/arch/arm/mach-pxa/include/mach/lp8x4x.h 
b/arch/arm/mach-pxa/include/mach/lp8x4x.h
index a49df22..4d5474e 100644
--- a/arch/arm/mach-pxa/include/mach/lp8x4x.h
+++ b/arch/arm/mach-pxa/include/mach/lp8x4x.h
@@ -50,6 +50,12 @@
 #define LP8X4X_CLRFALLINT      LP8X4X_P2V(0x1700901a)
 #define LP8X4X_RWRTC           LP8X4X_P2V(0x1700901c)
 #define LP8X4X_SRAMBANK                0x1700901e
+#define LP8X4X_TTYS0_QUIRK     0x17009030
+#define LP8X4X_TTYS1_QUIRK     0x17009032
+#define LP8X4X_TTYS2_QUIRK     0x17009034
+#define LP8X4X_TTYS0_IOMEM     0x17009050
+#define LP8X4X_TTYS1_IOMEM     0x17009060
+#define LP8X4X_TTYS2_IOMEM     0x17009070
 #define LP8X4X_SRAM            0x1700a000
 
 /* board specific IRQs */
diff --git a/drivers/tty/serial/8250/8250_lp8x4x.c 
b/drivers/tty/serial/8250/8250_lp8x4x.c
new file mode 100644
index 0000000..27b01f0b
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_lp8x4x.c
@@ -0,0 +1,181 @@
+/*  linux/drivers/tty/serial/8250/8250_lp8x4x.c
+ *
+ *  Support for 16550 serial ports on ICP DAS LP-8x4x
+ *
+ *  Copyright (C) 2013 Sergei Ianovich <ynv...@gmail.com>
+ *  Framework taken from linux/drivers/tty/serial/8250/8250_accent.c
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/serial_8250.h>
+#include <mach/lp8x4x.h>
+#include <linux/io.h>
+
+#define QUIRK_PORT(_base, _irq)                                        \
+       {                                                       \
+               .iobase         = _base,                        \
+               .membase        = (void *) _base,               \
+               .mapbase        = _base,                        \
+               .irq            = _irq,                         \
+               .uartclk        = 14745600,                     \
+               .regshift       = 1,                            \
+               .iotype         = UPIO_MEM,                     \
+               .flags          = UPF_IOREMAP,                  \
+               .set_termios    = lp8x4x_set_termios,           \
+               .serial_in      = lp8x4x_serial_in,             \
+               .serial_out     = lp8x4x_serial_out,            \
+       }
+
+static void lp8x4x_set_termios(struct uart_port *port,
+               struct ktermios *termios, struct ktermios *old)
+{
+       unsigned int len;
+       unsigned int baud;
+
+       switch (termios->c_cflag & CSIZE) {
+       case CS5:
+               len = 7;
+               break;
+       case CS6:
+               len = 8;
+               break;
+       case CS7:
+               len = 9;
+               break;
+       default:
+       case CS8:
+               len = 10;
+               break;
+       }
+
+       if (termios->c_cflag & CSTOPB)
+               len++;
+       if (termios->c_cflag & PARENB)
+               len++;
+       if (!(termios->c_cflag & PARODD))
+               len++;
+#ifdef CMSPAR
+       if (termios->c_cflag & CMSPAR)
+               len++;
+#endif
+
+       len -= 9;
+       len &= 3;
+       len <<= 3;
+       /*
+        * Ask the core to calculate the divisor for us.
+        */
+       baud = uart_get_baud_rate(port, termios, old,
+                                 port->uartclk / 16 / 0xffff,
+                                 port->uartclk / 16);
+       switch (baud) {
+       case 2400:
+               len |= 1;
+       case 4800:
+               len |= 2;
+       case 19200:
+               len |= 4;
+       case 38400:
+               len |= 5;
+       case 57600:
+               len |= 6;
+       case 115200:
+               len |= 7;
+       case 9600:
+       default:
+               len |= 3;
+       };
+       iowrite8(len, port->private_data);
+
+       serial8250_do_set_termios(port, termios, old);
+}
+
+static unsigned int lp8x4x_serial_in(struct uart_port *p, int offset)
+{
+       unsigned int b;
+       udelay(30);
+       offset = offset << p->regshift;
+       b = readb(p->membase + offset);
+       return b;
+}
+
+static void lp8x4x_serial_out(struct uart_port *p, int offset, int value)
+{
+       offset = offset << p->regshift;
+       writeb(value, p->membase + offset);
+}
+
+static struct plat_serial8250_port lp8x4x_data[] = {
+       QUIRK_PORT(LP8X4X_TTYS0_IOMEM, LP8X4X_TTYS0_IRQ),
+       QUIRK_PORT(LP8X4X_TTYS1_IOMEM, LP8X4X_TTYS1_IRQ),
+       QUIRK_PORT(LP8X4X_TTYS2_IOMEM, LP8X4X_TTYS2_IRQ),
+       { },
+};
+
+/* Total number of ports can be 35. The first 3 ports are on
+ * the device, the rest are on extension slots. Only the first 3
+ * require termios quirk */
+#define LP8X4X_QUIRK_PORTS     3
+
+static unsigned int extra_mem[LP8X4X_QUIRK_PORTS] = {
+       LP8X4X_TTYS0_QUIRK,
+       LP8X4X_TTYS1_QUIRK,
+       LP8X4X_TTYS2_QUIRK
+};
+
+static int request_and_remap(int i)
+{
+       if (!request_mem_region(extra_mem[i], 1, "serial"))
+               return -EBUSY;
+
+       lp8x4x_data[i].private_data = ioremap(extra_mem[i], 1);
+       if (lp8x4x_data[i].private_data)
+               return 0;
+
+       release_mem_region(extra_mem[i], 1);
+       return -ENODEV;
+}
+
+static void release_and_unmap(int i)
+{
+       iounmap((void *) lp8x4x_data[i].private_data);
+       release_mem_region(extra_mem[i], 1);
+}
+
+static struct platform_device lp8x4x_device = {
+       .name                   = "serial8250",
+       .id                     = PLAT8250_DEV_ACCENT,
+       .dev                    = {
+               .platform_data  = lp8x4x_data,
+       },
+};
+
+static int __init lp8x4x_init(void)
+{
+       int i = 0;
+       int err = 0;
+
+       for (i = 0; i < LP8X4X_QUIRK_PORTS; i++) {
+               err = request_and_remap(i);
+               if (err == 0)
+                       continue;
+
+               for (; i >= 0; i--)
+                       release_and_unmap(i);
+               lp8x4x_device.dev.platform_data = NULL;
+               return err;
+       }
+       return platform_device_register(&lp8x4x_device);
+}
+
+module_init(lp8x4x_init);
+
+MODULE_AUTHOR("Sergei Ianovich");
+MODULE_DESCRIPTION("8250 serial port module for LP-8x4x");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index f3b306e..30b9af4 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -237,6 +237,17 @@ config SERIAL_8250_HUB6
          To compile this driver as a module, choose M here: the module
          will be called 8250_hub6.
 
+config SERIAL_8250_LP8X4X
+       tristate "Support 8250 ports on ICP DAS LP-8x4x"
+       depends on SERIAL_8250 != n && SERIAL_8250_MANY_PORTS && MACH_LP8X4X
+       help
+         In addition on serial ports on PXA270 SoC, LP-8x4x has 1 dual
+         RS232/RS485 port, 1 RS485 port and 1 RS232 port serial ports.
+
+         Say N here, unless you plan to run this kernel on a LP-8x4x system.
+
+         If you choose M, the module will be called 8250_lp8x4x.
+
 #
 # Misc. options/drivers.
 #
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 36d68d0..451c558 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_SERIAL_8250_ACCENT)      += 8250_accent.o
 obj-$(CONFIG_SERIAL_8250_BOCA)         += 8250_boca.o
 obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554)        += 8250_exar_st16c554.o
 obj-$(CONFIG_SERIAL_8250_HUB6)         += 8250_hub6.o
+obj-$(CONFIG_SERIAL_8250_LP8X4X)       += 8250_lp8x4x.o
 obj-$(CONFIG_SERIAL_8250_FSL)          += 8250_fsl.o
 obj-$(CONFIG_SERIAL_8250_DW)           += 8250_dw.o
 obj-$(CONFIG_SERIAL_8250_EM)           += 8250_em.o
-- 
1.8.4.2

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to