Driver for SUNIX Multi-I/O card.
Based on driver/char/serial.c by Linus Torvalds, Theodore Ts'o.

SUNIX serial card designed with SUNIX UART controller and
compatible with 16C950 UART specification.

Signed-off-by: Morris Ku <sau...@gmail.com>
---
 mfd/sunix/snx_main.c   | 1671 +++++++++++++++++++
 mfd/sunix/snx_serial.c | 3513 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 5184 insertions(+)
 create mode 100644 mfd/sunix/snx_main.c
 create mode 100644 mfd/sunix/snx_serial.c

diff --git a/mfd/sunix/snx_main.c b/mfd/sunix/snx_main.c
new file mode 100644
index 00000000..e606e018
--- /dev/null
+++ b/mfd/sunix/snx_main.c
@@ -0,0 +1,1671 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "snx_common.h"
+#include "driver_extd.h"
+
+#define SNX_DRIVER_AUTHOR      "SUNIX Co., Ltd.<i...@sunix.com.tw>"
+#define SNX_DRIVER_DESC                "SUNIX Multi-I/O Board Driver Module"
+
+MODULE_AUTHOR(SNX_DRIVER_AUTHOR);
+MODULE_DESCRIPTION(SNX_DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static const pci_board snx_pci_board_conf[] = {
+       {
+               VENID_GOLDEN,   DEVID_G_SERIAL, SUBVENID_GOLDEN,
+               SUBDEVID_TEST, 0, 0, 0, 0x00, "none",
+               BOARDFLAG_NONE, PART_NUMBER_NONE,
+               CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'n', -1, 0, 0, -1, 0, 0, 0x0000, SUNNONE_HWID},
+               },
+       },
+
+       // mode P1002
+       {
+               VENID_MATRIX, DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P1002, 2, 0, 1, 0x00, "1002", BOARDFLAG_NONE,
+               PART_NUMBER_NONE, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUNMATX_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUNMATX_HWID},
+               },
+       },
+
+       // mode P1004
+       {
+               VENID_MATRIX, DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P1004, 4, 0, 1, 0x00, "1004", BOARDFLAG_NONE,
+               PART_NUMBER_NONE, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUNMATX_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUNMATX_HWID},
+                       {'s', 0, 16, 8, -1,     0, 0, 0x0004, SUNMATX_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUNMATX_HWID},
+               },
+       },
+
+       // mode P1008
+       {
+               VENID_MATRIX, DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P1008, 8, 0, 1, 0x00, "1008", BOARDFLAG_NONE,
+               PART_NUMBER_NONE, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUNMATX_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUNMATX_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUNMATX_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUNMATX_HWID},
+                       {'s', 0, 32, 8, -1, 0, 0, 0x0010, SUNMATX_HWID},
+                       {'s', 0, 40, 8, -1,     0, 0, 0x0020, SUNMATX_HWID},
+                       {'s', 0, 48, 8, -1,     0, 0, 0x0040, SUNMATX_HWID},
+                       {'s', 0, 56, 8, -1,     0, 0, 0x0080, SUNMATX_HWID},
+               },
+       },
+
+       // mode P1016
+       {
+               VENID_MATRIX, DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P1016, 16, 0, 1, 0x00, "1016",
+               BOARDFLAG_NONE | BOARDFLAG_16PORTS,
+               PART_NUMBER_NONE, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUNMATX_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUNMATX_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUNMATX_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUNMATX_HWID},
+                       {'s', 0, 32, 8, -1,     0, 0, 0x0010, SUNMATX_HWID},
+                       {'s', 0, 40, 8, -1, 0, 0, 0x0020, SUNMATX_HWID},
+                       {'s', 0, 48, 8, -1, 0, 0, 0x0040, SUNMATX_HWID},
+                       {'s', 0, 56, 8, -1, 0, 0, 0x0080, SUNMATX_HWID},
+                       {'s', 0, 64, 8, -1, 0, 0, 0x0100, SUNMATX_HWID},
+                       {'s', 0, 72, 8, -1, 0, 0, 0x0200, SUNMATX_HWID},
+                       {'s', 0, 80, 8, -1, 0, 0, 0x0400, SUNMATX_HWID},
+                       {'s', 0, 88, 8, -1, 0, 0, 0x0800, SUNMATX_HWID},
+                       {'s', 0, 96, 8, -1, 0, 0, 0x1000, SUNMATX_HWID},
+                       {'s', 0, 104, 8, -1, 0, 0, 0x2000, SUNMATX_HWID},
+                       {'s', 0, 112, 8, -1, 0, 0, 0x4000, SUNMATX_HWID},
+                       {'s', 0, 120, 8, -1, 0, 0, 0x8000, SUNMATX_HWID},
+               },
+       },
+
+       // mode P2002
+       {
+               VENID_MATRIX, DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P2002, 2, 0, 1, 0x00, "2002", BOARDFLAG_NONE,
+               PART_NUMBER_NONE, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUNMATX_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUNMATX_HWID},
+               },
+       },
+
+       // mode P2004
+       {
+               VENID_MATRIX, DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P2004, 4, 0, 1, 0x00, "2004", BOARDFLAG_NONE,
+               PART_NUMBER_NONE, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUNMATX_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUNMATX_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUNMATX_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUNMATX_HWID},
+               },
+       },
+
+       // mode P2008
+       {
+               VENID_MATRIX, DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P2008, 8, 0, 1, 0x00, "2008", BOARDFLAG_NONE,
+               PART_NUMBER_NONE, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUNMATX_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUNMATX_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUNMATX_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUNMATX_HWID},
+                       {'s', 0, 32, 8, -1, 0, 0, 0x0010, SUNMATX_HWID},
+                       {'s', 0, 40, 8, -1, 0, 0, 0x0020, SUNMATX_HWID},
+                       {'s', 0, 48, 8, -1, 0, 0, 0x0040, SUNMATX_HWID},
+                       {'s', 0, 56, 8, -1, 0, 0, 0x0080, SUNMATX_HWID},
+               },
+       },
+
+       // mode P3002
+       {
+               VENID_MATRIX, DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P3002, 2, 0, 1, 0x00, "3002", BOARDFLAG_NONE,
+               PART_NUMBER_NONE, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUNMATX_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUNMATX_HWID},
+               },
+       },
+
+       // mode P3004
+       {
+               VENID_MATRIX, DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P3004, 4, 0, 1, 0x00, "3004", BOARDFLAG_NONE,
+               PART_NUMBER_NONE, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUNMATX_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUNMATX_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUNMATX_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUNMATX_HWID},
+               },
+       },
+
+       // mode P3008
+       {
+               VENID_MATRIX, DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P3008, 8, 0, 1, 0x00, "3008", BOARDFLAG_NONE,
+               PART_NUMBER_NONE, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUNMATX_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUNMATX_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUNMATX_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUNMATX_HWID},
+                       {'s', 0, 32, 8, -1, 0, 0, 0x0010, SUNMATX_HWID},
+                       {'s', 0, 40, 8, -1, 0, 0, 0x0020, SUNMATX_HWID},
+                       {'s', 0, 48, 8, -1, 0, 0, 0x0040, SUNMATX_HWID},
+                       {'s', 0, 56, 8, -1, 0, 0, 0x0080, SUNMATX_HWID},
+               },
+       },
+
+
+       // mode 5027A
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5027A, 1, 0, 3, 0x00, "5027", BOARDFLAG_NONE,
+               0x01, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       { 's', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+               },
+       },
+
+       // mode 5037A
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5037A, 2, 0, 3, 0x00, "5037", BOARDFLAG_NONE,
+               0x02, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUN1999_HWID},
+               },
+       },
+
+       // mode 5056A
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5056A, 4, 0, 3, 0x00, "5056", BOARDFLAG_NONE,
+               0x04, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUN1999_HWID},
+               },
+       },
+
+       // mode 5066A
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5066A, 8, 0, 3, 0x00, "5066",
+               BOARDFLAG_NONE, 0x08, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8,     8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUN1999_HWID},
+                       {'s', 1, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 1, 8,     8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 1, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 1, 24, 8, -1,     0, 0, 0x0008, SUN1999_HWID},
+               },
+       },
+
+       // mode 5016
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5016A, 16, 0, 3, 0x00, "5016",
+               BOARDFLAG_NONE | BOARDFLAG_16PORTS, 0x10,
+               CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUN1999_HWID},
+                       {'s', 1, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 1, 8, 8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 1, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 1, 24, 8, -1, 0, 0, 0x0008, SUN1999_HWID},
+                       {'s', 1, 64, 8, -1, 0, 0, 0x0010, SUN1999_HWID},
+                       {'s', 1, 72, 8, -1, 0, 0, 0x0020, SUN1999_HWID},
+                       {'s', 1, 80, 8, -1, 0, 0, 0x0040, SUN1999_HWID},
+                       {'s', 1, 88, 8, -1, 0, 0, 0x0080, SUN1999_HWID},
+                       {'s', 1, 128, 8, -1, 0, 0, 0x0100, SUN1999_HWID},
+                       {'s', 1, 136, 8, -1, 0, 0, 0x0200, SUN1999_HWID},
+                       {'s', 1, 144, 8, -1, 0, 0, 0x0400, SUN1999_HWID},
+                       {'s', 1, 152, 8, -1, 0, 0, 0x0800, SUN1999_HWID},
+               },
+       },
+
+       // mode 5069A 5069H
+       {
+               VENID_SUN1999,  DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5069A, 1, 1, 3, 0x00, "5069", BOARDFLAG_NONE,
+               0x01, 0x00, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'p', 1, 0, 8, 2, 0, 0, 0x0000, SUN1999_HWID},
+               },
+       },
+
+
+       // mode 5079A
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5079A, 2, 1, 3, 0x00, "5079", BOARDFLAG_NONE,
+               0x02, 0x00, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'p', 1, 0, 8, 2, 0, 0, 0x0000, SUN1999_HWID},
+               },
+       },
+
+       // mode 5099A
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5099A, 4, 1, 3, 0x00, "5099", BOARDFLAG_NONE,
+               0x04, 0x00, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUN1999_HWID},
+                       {'p', 1, 0, 8, 2, 0, 0, 0x0000, SUN1999_HWID},
+               },
+       },
+
+       // mode 5008A
+       {
+               VENID_SUN1999, DEVID_S_PARALL, SUBVENID_SUN1999,
+               SUBDEVID_5008A, 0, 1, 0, 0x00, "5008", BOARDFLAG_NONE,
+               0x00, 0x00, GPIO_NONE,
+               {
+                       {'p', 1, 0, 8, 2, 0, 0, 0x0000, SUN1999_HWID},
+               },
+       },
+
+       // mode P2102
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999, SUBDEVID_P2102,
+               2, 0, 3, 0x00, "P2102", BOARDFLAG_NONE, 0x42,
+               CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUN1999_HWID},
+               },
+       },
+
+       // mode P2104
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_P2104, 4, 0, 3, 0x00, "P2104", BOARDFLAG_NONE,
+               0x44, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUN1999_HWID},
+               },
+       },
+
+       // mode P2108
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_P2108, 8, 0, 3, 0x00, "P2108", BOARDFLAG_NONE,
+               0x48, CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8,     8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUN1999_HWID},
+                       {'s', 1, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 1, 8, 8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 1, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 1, 24, 8, -1, 0, 0, 0x0008, SUN1999_HWID},
+               },
+       },
+
+       // mode P2116
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_P2116, 16, 0, 3, 0x00, "P2116",
+               BOARDFLAG_NONE | BOARDFLAG_16PORTS, 0x50,
+               CARD_TYPE_UART_ONLY, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8,     8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUN1999_HWID},
+                       {'s', 1, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 1, 8,     8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 1, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 1, 24, 8, -1, 0, 0, 0x0008, SUN1999_HWID},
+                       {'s', 1, 64, 8, -1, 0, 0, 0x0010, SUN1999_HWID},
+                       {'s', 1, 72, 8, -1, 0, 0, 0x0020, SUN1999_HWID},
+                       {'s', 1, 80, 8, -1, 0, 0, 0x0040, SUN1999_HWID},
+                       {'s', 1, 88, 8, -1, 0, 0, 0x0080, SUN1999_HWID},
+                       {'s', 1, 128, 8, -1, 0, 0, 0x0100, SUN1999_HWID},
+                       {'s', 1, 136, 8, -1, 0, 0, 0x0200, SUN1999_HWID},
+                       {'s', 1, 144, 8, -1, 0, 0, 0x0400, SUN1999_HWID},
+                       {'s', 1, 152, 8, -1, 0, 0, 0x0800, SUN1999_HWID},
+               },
+       },
+
+       // mode IPC-P3104
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_P3104, 4, 0, 3, 0x00, "P3104", BOARDFLAG_NONE,
+               0x84, 0x00, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8,     8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 0, 16, 8, -1,     0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 0, 24, 8, -1,     0, 0, 0x0008, SUN1999_HWID},
+               },
+       },
+
+       // mode IPC-P3108
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_P3108, 8, 0, 3, 0x00, "P3108", BOARDFLAG_NONE,
+               0x88, 0x00, GPIO_NONE,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUN1999_HWID},
+                       {'s', 1, 0,     8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 1, 8,     8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 1, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 1, 24, 8, -1, 0, 0, 0x0008, SUN1999_HWID},
+               },
+       },
+
+       // CDK1037
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_CASH_2S, 2, 0, 3, 0x00, "CDK1037",
+               BOARDFLAG_NONE, 0x02, CARD_TYPE_UART_GINTR, INTR_GPIO_6PORT,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUN1999_HWID},
+               },
+       },
+
+       // CDK1056
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_CASH_4S, 4, 0, 3, 0x00, "CDK1056", BOARDFLAG_NONE,
+               0x04, CARD_TYPE_UART_GINTR, INTR_GPIO_6PORT,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8,     8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUN1999_HWID},
+               },
+       },
+
+       // mode DIO-0802
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_DIO0802, 2, 0, 3, 0x00, "DIO0802", BOARDFLAG_NONE,
+               0x02, CARD_TYPE_UART_GEXTR, EXTR_GPIO_8PORT,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8,     8, -1, 0, 0, 0x0002, SUN1999_HWID},
+               },
+       },
+
+       // mode DIO-1604
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_DIO1604, 4, 0, 3, 0x00, "DIO1604",
+               BOARDFLAG_NONE, 0x04, CARD_TYPE_UART_GEXTR, EXTR_GPIO_16PORT,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 0, 24, 8, -1, 0, 0, 0x0008, SUN1999_HWID},
+               },
+       },
+
+       // mode DIO-3204
+       {
+               VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_DIO3204, 4, 0, 3, 0x00, "DIO3204", BOARDFLAG_NONE,
+               0x04, CARD_TYPE_UART_GEXTR, EXTR_GPIO_32PORT,
+               {
+                       {'s', 0, 0, 8, -1, 0, 0, 0x0001, SUN1999_HWID},
+                       {'s', 0, 8, 8, -1, 0, 0, 0x0002, SUN1999_HWID},
+                       {'s', 0, 16, 8, -1, 0, 0, 0x0004, SUN1999_HWID},
+                       {'s', 0, 24, 8, -1,     0, 0, 0x0008, SUN1999_HWID},
+               },
+       },
+};
+
+static const char snx_ser_ic_table[SNX_SER_PORT_MAX_UART][10] = {
+       {"UNKNOWN"},
+       {"SUN1889"},
+       {"SUN1699"},
+       {"SUNMATX"},
+       {"SUN1999"}
+};
+
+static const char snx_par_ic_table[SNX_PAR_PORT_MAX_UART][10] = {
+       {"UNKNOWN"},
+       {"SUN1888"},
+       {"SUN1689"},
+       {"SUNMATX"},
+       {"SUN1999"}
+};
+
+static const char snx_port_remap[2][10] = {
+       {"NON-REMAP"},
+       {"REMAP"}
+};
+
+enum{
+// golden-serial
+       GOLDEN_BOARD_TEST = 0,
+
+// matrix-serial
+       MATRIX_BOARD_P1002,
+       MATRIX_BOARD_P1004,
+       MATRIX_BOARD_P1008,
+       MATRIX_BOARD_P1016,
+       MATRIX_BOARD_P2002,
+       MATRIX_BOARD_P2004,
+       MATRIX_BOARD_P2008,
+       MATRIX_BOARD_P3002,
+       MATRIX_BOARD_P3004,
+       MATRIX_BOARD_P3008,
+
+// sun1999-serial RS232
+       SUN1999_BOARD_5027A,
+       SUN1999_BOARD_5037A,
+       SUN1999_BOARD_5056A,
+       SUN1999_BOARD_5066A,
+       SUN1999_BOARD_5016A,
+
+       //sun1999-multi I/O
+       SUN1999_BOARD_5069A,
+       SUN1999_BOARD_5079A,
+       SUN1999_BOARD_5099A,
+
+       //sun1999-parallel
+
+       SUN1999_BOARD_5008A,
+
+       //sun1999-serial RS422/485
+       SUN1999_BOARD_P2102,
+       SUN1999_BOARD_P2104,
+       SUN1999_BOARD_P2108,
+       SUN1999_BOARD_P2116,
+
+       //sun1999 3_in_1
+       SUN1999_BOARD_P3104,
+       SUN1999_BOARD_P3108,
+
+       //cash drawer card
+       SUN1999_BOARD_CASH_2S,
+       SUN1999_BOARD_CASH_4S,
+
+       //DIO
+       SUN1999_BOARD_DIO0802,
+       SUN1999_BOARD_DIO1604,
+       SUN1999_BOARD_DIO3204,
+
+};
+
+static const struct pci_device_id      sunix_pci_board_id[] = {
+// golden-serial
+       {VENID_GOLDEN,  DEVID_G_SERIAL, SUBVENID_GOLDEN,
+               SUBDEVID_TEST,  0,      0,      GOLDEN_BOARD_TEST},
+
+// matrix-serial
+       {VENID_MATRIX,  DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P1002, 0,      0,      MATRIX_BOARD_P1002},
+       {VENID_MATRIX,  DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P1004, 0,      0,      MATRIX_BOARD_P1004},
+       {VENID_MATRIX,  DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P1008, 0,      0,      MATRIX_BOARD_P1008},
+       {VENID_MATRIX,  DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P1016, 0,      0,      MATRIX_BOARD_P1016},
+       {VENID_MATRIX,  DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P2002, 0,      0,      MATRIX_BOARD_P2002},
+       {VENID_MATRIX,  DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P2004, 0,      0,      MATRIX_BOARD_P2004},
+       {VENID_MATRIX,  DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P2008, 0,      0,      MATRIX_BOARD_P2008},
+       {VENID_MATRIX,  DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P3002, 0,      0,      MATRIX_BOARD_P3002},
+       {VENID_MATRIX,  DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P3004, 0,      0,      MATRIX_BOARD_P3004},
+       {VENID_MATRIX,  DEVID_M_SERIAL, SUBVENID_MATRIX,
+               SUBDEVID_P3008, 0,      0,      MATRIX_BOARD_P3008},
+
+       // sun1999-serial RS232
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5027A, 0,      0,      SUN1999_BOARD_5027A},
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5037A, 0,      0,      SUN1999_BOARD_5037A},
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5056A, 0,      0,      SUN1999_BOARD_5056A},
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5066A, 0,      0,      SUN1999_BOARD_5066A},
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5016A, 0,      0,      SUN1999_BOARD_5016A},
+
+       // sun1999-multi I/O
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5069A, 0,      0,      SUN1999_BOARD_5069A},
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5079A, 0,      0,      SUN1999_BOARD_5079A},
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_5099A, 0,      0,      SUN1999_BOARD_5099A},
+
+       // sun1999-parallel
+       {VENID_SUN1999, DEVID_S_PARALL, SUBVENID_SUN1999,
+               SUBDEVID_5008A, 0,      0,      SUN1999_BOARD_5008A},
+
+       // sun1999-serial RS422/485
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_P2102, 0,      0,      SUN1999_BOARD_P2102},
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_P2104, 0,      0,      SUN1999_BOARD_P2104},
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_P2108, 0,      0,      SUN1999_BOARD_P2108},
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_P2116, 0,      0,      SUN1999_BOARD_P2116},
+
+       // sun1999 3_in_1
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_P3104, 0,      0,      SUN1999_BOARD_P3104},
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_P3108, 0,      0,      SUN1999_BOARD_P3108},
+
+       //cash drawer card  2S
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_CASH_2S, 0,    0,      SUN1999_BOARD_CASH_2S},
+
+       //cash drawer card  4S
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_CASH_4S, 0,    0,      SUN1999_BOARD_CASH_4S},
+
+       //DIO
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_DIO0802, 0,    0,      SUN1999_BOARD_DIO0802},
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_DIO1604, 0,    0,      SUN1999_BOARD_DIO1604},
+       {VENID_SUN1999, DEVID_S_SERIAL, SUBVENID_SUN1999,
+               SUBDEVID_DIO3204, 0,    0,      SUN1999_BOARD_DIO3204},
+
+       {0}
+};
+MODULE_DEVICE_TABLE(pci, sunix_pci_board_id);
+
+struct sunix_board                     sunix_board_table[SNX_BOARDS_MAX];
+struct sunix_ser_port      sunix_ser_table[SNX_SER_TOTAL_MAX + 1];
+struct sunix_par_port      sunix_par_table[SNX_PAR_TOTAL_MAX];
+
+static int snx_ser_port_total_cnt;
+static int snx_par_port_total_cnt;
+
+int snx_board_count;
+
+static struct snx_ser_driver   sunix_ser_reg = {
+       .dev_name = "ttySNX",
+       .major = 0,
+       .minor = 0,
+       .nr = (SNX_SER_TOTAL_MAX + 1),
+};
+
+static irqreturn_t sunix_interrupt(int irq, void *dev_id)
+{
+       struct sunix_ser_port *sp = NULL;
+       struct sunix_par_port *pp = NULL;
+       struct sunix_board *sb = NULL;
+       int i;
+       int status = 0;
+
+       int handled = IRQ_NONE;
+
+       for (i = 0; i < SNX_BOARDS_MAX; i++) {
+
+               if (dev_id == &(sunix_board_table[i])) {
+                       sb = dev_id;
+                       break;
+               }
+       }
+
+       if (i == SNX_BOARDS_MAX)
+               status = 1;
+
+       if (!sb)
+               status = 1;
+
+       if (sb->board_enum <= 0)
+               status = 1;
+
+       if (status != 0)
+               return handled;
+
+       if ((sb->ser_port > 0) && (sb->ser_isr != NULL)) {
+               sp = &sunix_ser_table[sb->ser_port_index];
+
+               if (!sp)
+                       status = 1;
+
+               status = sb->ser_isr(sb, sp);
+       }
+
+       if ((sb->par_port > 0) && (sb->par_isr != NULL)) {
+               pp = &sunix_par_table[sb->par_port_index];
+
+               if (!pp)
+                       status = 1;
+
+               status = sb->par_isr(sb, pp);
+       }
+
+       if (status != 0)
+               return handled;
+
+       handled = IRQ_HANDLED;
+       return handled;
+}
+
+static int snx_suspend_one(struct pci_dev *pdev, pm_message_t state)
+{
+       return  0;
+}
+
+static int snx_set_port_termios(struct snx_ser_state *state)
+{
+       struct tty_struct *tty = state->info->tty;
+       struct SNXTERMIOS *termios;
+
+       int retval = 0;
+
+       termios = &tty->termios;
+
+       retval = snx_ser_startup(state, 0);
+
+       if (retval == 0)
+               snx_ser_update_termios(state);
+
+       return 0;
+}
+
+static int snx_resume_port_termios(struct snx_ser_info *info)
+{
+       struct snx_ser_state *state = NULL;
+       struct tty_struct *tty = info->tty;
+
+       state = tty->driver_data;
+       snx_set_port_termios(state);
+
+       return 0;
+}
+
+
+static int snx_resume_port(struct sunix_ser_port *sp)
+{
+       struct snx_ser_port *port = &sp->port;
+       struct snx_ser_info *info = port->info;
+
+       if (info)
+               snx_resume_port_termios(info);
+
+       return 0;
+}
+
+static int snx_resume_one(struct pci_dev *pdev)
+{
+       struct sunix_board *sb = pci_get_drvdata(pdev);
+       struct sunix_ser_port *sp = NULL;
+       int j;
+
+       if (sb == NULL)
+               return 0;
+
+       for (j = 0; j < sb->ser_port; j++) {
+               sp = &sunix_ser_table[j];
+
+               if (sp == NULL)
+                       return 0;
+
+               if (sp->port.suspended == 1)
+                       snx_resume_port(sp);
+       }
+
+       return 0;
+}
+
+
+static int sunix_pci_board_probe(void)
+{
+       struct sunix_board *sb;
+       struct pci_dev *pdev = NULL;
+       struct pci_dev *pdev_array[4] = {NULL, NULL, NULL, NULL};
+
+       int sunix_pci_board_id_cnt;
+       int tablecnt;
+       int boardcnt;
+       int i;
+       unsigned short int sub_device_id;
+       unsigned short int device_part_number;
+       unsigned int bar3_base_add;
+
+       int status;
+       unsigned int bar3_Byte5;
+       unsigned int bar3_Byte6;
+       unsigned int bar3_Byte7;
+       unsigned int oem_id;
+       unsigned char uart_type;
+       unsigned char gpio_type;
+       unsigned char gpio_card_type;
+       int gpio_ch_cnt;
+
+       // clear and init some variable
+       memset(sunix_board_table, 0, SNX_BOARDS_MAX *
+       sizeof(struct sunix_board));
+
+       for (i = 0; i < SNX_BOARDS_MAX; i++) {
+               sunix_board_table[i].board_enum = -1;
+               sunix_board_table[i].board_number = -1;
+       }
+
+       sunix_pci_board_id_cnt =
+       (sizeof(sunix_pci_board_id) / sizeof(sunix_pci_board_id[0])) - 1;
+
+       // search matrix serial board
+       pdev = NULL;
+       tablecnt = 0;
+       boardcnt = 0;
+       sub_device_id = 0;
+       status = 0;
+
+       while (tablecnt < sunix_pci_board_id_cnt) {
+
+               pdev = pci_get_device(VENID_MATRIX, DEVID_M_SERIAL, pdev);
+
+               if (pdev == NULL) {
+                       tablecnt++;
+                       continue;
+               }
+
+               if ((tablecnt > 0) && ((pdev == pdev_array[0]) ||
+               (pdev == pdev_array[1]) ||
+               (pdev == pdev_array[2]) ||
+               (pdev == pdev_array[3]))) {
+                       continue;
+               }
+
+               pci_read_config_word(pdev, 0x2e, &sub_device_id);
+
+               if (sub_device_id == 0) {
+                       status = -EIO;
+                       return status;
+               }
+
+               if (sub_device_id != sunix_pci_board_id[tablecnt].subdevice)
+                       continue;
+               if (pdev == NULL) {
+                       pr_err("SNX Error: PCI device object is NULL !\n");
+                       status = -EIO;
+                       return status;
+
+               } else {
+
+                       status = pci_enable_device(pdev);
+
+                       if (status != 0) {
+                               pr_err("SNX Error: SUNIX Board Enable Fail 
!\n\n");
+                               status = -ENXIO;
+                               return status;
+                       }
+               }
+
+               boardcnt++;
+               if (boardcnt > SNX_BOARDS_MAX) {
+                       pr_err("SNX Error: Support Four Boards In Maximum !\n");
+                       status = -ENOSPC;
+                       return status;
+               }
+
+               sb = &sunix_board_table[boardcnt-1];
+               pdev_array[boardcnt-1] = pdev;
+               sb->pdev = pdev;
+               sb->bus_number = pdev->bus->number;
+               sb->dev_number = PCI_SLOT(pdev->devfn);
+               sb->board_enum = (int)sunix_pci_board_id[tablecnt].driver_data;
+               sb->pb_info = snx_pci_board_conf[sb->board_enum];
+               sb->board_flag = sb->pb_info.board_flag;
+               sb->board_number = boardcnt - 1;
+       }
+
+       // search sun1999 muti I/O board
+       pdev = NULL;
+       tablecnt = 0;
+       sub_device_id = 0;
+       status = 0;
+       device_part_number = 0;
+       bar3_base_add = 0;
+       bar3_Byte5 = 0;
+       bar3_Byte6 = 0;
+       bar3_Byte7 = 0;
+       oem_id = 0;
+       uart_type = 0;
+       gpio_type = 0;
+       gpio_card_type = 0;
+       gpio_ch_cnt = 0;
+
+       while (tablecnt < sunix_pci_board_id_cnt) {
+
+               pdev = pci_get_device(VENID_SUN1999, DEVID_S_SERIAL, pdev);
+
+               if (pdev == NULL) {
+                       tablecnt++;
+                       continue;
+               }
+
+               if ((tablecnt > 0) &&
+               ((pdev == pdev_array[0]) ||
+               (pdev == pdev_array[1]) ||
+               (pdev == pdev_array[2]) ||
+               (pdev == pdev_array[3]))) {
+                       continue;
+               }
+
+               pci_read_config_word(pdev, 0x2e, &sub_device_id);
+
+               if (sub_device_id == 0) {
+                       status = -EIO;
+                       return status;
+               }
+
+               if (sub_device_id != sunix_pci_board_id[tablecnt].subdevice)
+                       continue;
+
+               if (pdev == NULL) {
+                       pr_err("SNX Error: PCI device object is an NULL pointer 
!\n\n");
+                       status = -EIO;
+                       return status;
+
+               } else {
+
+                       status = pci_enable_device(pdev);
+
+                       if (status != 0) {
+                               pr_err("SNX Error: SUNIX Board Enable Fail 
!\n\n");
+                               status = -ENXIO;
+                               return status;
+                       }
+               }
+
+               bar3_base_add = pci_resource_start(pdev, 3);
+               device_part_number = inb(bar3_base_add + 5);
+               bar3_Byte5 = device_part_number;
+               bar3_Byte6 = inb(bar3_base_add + 0x06);
+               bar3_Byte7 = inb(bar3_base_add + 0x07);
+               gpio_card_type = ((bar3_Byte7 & 0x18)>>3);
+               oem_id = (bar3_Byte5 | (bar3_Byte6 << 8) | (bar3_Byte7 << 16));
+               uart_type = ((bar3_Byte5 & 0xc0)>>6);
+               gpio_ch_cnt = ((bar3_Byte7 & 0x60)>>5);
+               gpio_type = ((bar3_Byte7 & 0x80)>>7);
+
+               if ((gpio_ch_cnt == 0x00) && (gpio_card_type == 0x01))
+                       gpio_ch_cnt = 6;
+               else if ((gpio_ch_cnt == 0x00) && (gpio_card_type == 0x02))
+                       gpio_ch_cnt = 8;
+               else if (gpio_ch_cnt == 0x01)
+                       gpio_ch_cnt = 16;
+               else if (gpio_ch_cnt == 0x02)
+                       gpio_ch_cnt = 32;
+
+       if (device_part_number != snx_pci_board_conf[tablecnt].part_number)
+               continue;
+       else if (gpio_card_type != snx_pci_board_conf[tablecnt].card_type)
+               continue;
+       else if (gpio_ch_cnt != snx_pci_board_conf[tablecnt].gpio_ch_cnt)
+               continue;
+       else
+               pr_err("\n");
+
+               boardcnt++;
+               if (boardcnt > SNX_BOARDS_MAX) {
+                       pr_err("SNX Error: Support Four Boards In Maximum !\n");
+                       status = -ENOSPC;
+                       return status;
+               }
+
+               sb = &sunix_board_table[boardcnt-1];
+               pdev_array[boardcnt-1] = pdev;
+               sb->pdev = pdev;
+               sb->bus_number = pdev->bus->number;
+               sb->dev_number = PCI_SLOT(pdev->devfn);
+               sb->board_enum = (int)sunix_pci_board_id[tablecnt].driver_data;
+               sb->pb_info = snx_pci_board_conf[sb->board_enum];
+               sb->board_flag = sb->pb_info.board_flag;
+               sb->board_number = boardcnt - 1;
+               sb->oem_id = oem_id;
+               sb->uart_cnt = sb->pb_info.num_serport;
+               sb->gpio_chl_cnt = gpio_ch_cnt;
+               sb->board_uart_type = uart_type;
+               sb->board_gpio_card_type = gpio_card_type;
+               sb->board_gpio_type = gpio_type;
+
+       }
+
+       // search SUN1999 parallel board
+       pdev = NULL;
+       tablecnt = 0;
+       sub_device_id = 0;
+       status = 0;
+
+       while (tablecnt < sunix_pci_board_id_cnt) {
+
+               pdev = pci_get_device(VENID_SUN1999, DEVID_S_PARALL, pdev);
+
+               if (pdev == NULL) {
+                       tablecnt++;
+                       continue;
+               }
+
+               if ((tablecnt > 0) &&
+               ((pdev == pdev_array[0]) ||
+               (pdev == pdev_array[1]) ||
+               (pdev == pdev_array[2]) ||
+               (pdev == pdev_array[3]))) {
+                       continue;
+               }
+
+               pci_read_config_word(pdev, 0x2e, &sub_device_id);
+
+               if (sub_device_id == 0) {
+                       status = -EIO;
+                       return status;
+               }
+
+               if (sub_device_id != sunix_pci_board_id[tablecnt].subdevice)
+                       continue;
+
+               if (pdev == NULL) {
+                       pr_err("SNX Error: PCI device object is an NULL pointer 
!\n\n");
+                       status = -EIO;
+                       return status;
+
+               } else {
+
+                       status = pci_enable_device(pdev);
+
+                       if (status != 0) {
+                               pr_err("SNX Error: SUNIX Board Enable Fail 
!\n\n");
+                               status = -ENXIO;
+                               return status;
+                       }
+               }
+
+               boardcnt++;
+               if (boardcnt > SNX_BOARDS_MAX) {
+                       pr_err("\n");
+                       pr_err("SNX Error: SUNIX Driver Module Support Four 
Boards In Maximum !\n\n");
+                       status = -ENOSPC;
+                       return status;
+               }
+
+               sb = &sunix_board_table[boardcnt-1];
+               pdev_array[boardcnt-1] = pdev;
+               sb->pdev = pdev;
+               sb->bus_number = pdev->bus->number;
+               sb->dev_number = PCI_SLOT(pdev->devfn);
+               sb->board_enum = (int)sunix_pci_board_id[tablecnt].driver_data;
+               sb->pb_info = snx_pci_board_conf[sb->board_enum];
+               sb->board_flag = sb->pb_info.board_flag;
+               sb->board_number = boardcnt - 1;
+       }
+
+       // print info
+       if (boardcnt == 0) {
+               pr_err("SNX Info : No SUNIX Multi-I/O Board Found !\n\n");
+               status = -ENXIO;
+               return status;
+
+       } else {
+
+               for (i = 0; i < SNX_BOARDS_MAX; i++) {
+                       sb = &sunix_board_table[i];
+
+                       if (sb->board_enum > 0) {
+                               pr_err("\n");
+
+                       if ((sb->pb_info.num_serport > 0) &&
+                               (sb->pb_info.num_parport > 0)) {
+                               pr_err("SNX Info : Found SUNIX %s Series Board 
(%dS%dP),\n",
+                               sb->pb_info.board_name,
+                               sb->pb_info.num_serport,
+                               sb->pb_info.num_parport);
+                       } else if ((sb->pb_info.num_serport) > 0) {
+                               pr_err("SNX Info : Found SUNIX %s Series Board 
(%dS),\n",
+                               sb->pb_info.board_name,
+                               sb->pb_info.num_serport);
+                       } else {
+                               pr_err("SNX Info : Found SUNIX %s Series Board 
(%dP),\n",
+                               sb->pb_info.board_name,
+                               sb->pb_info.num_parport);
+                       }
+                               pr_err(" bus number:%d, device number:%d\n\n",
+                               sb->bus_number, sb->dev_number);
+                       }
+               }
+               snx_board_count = boardcnt;
+       }
+
+       return status;
+}
+
+
+static int sunix_get_pci_board_conf(void)
+{
+       struct sunix_board *sb = NULL;
+       struct pci_dev *pdev = NULL;
+       int status = 0;
+       int i;
+       int j;
+
+       for (i = 0; i < SNX_BOARDS_MAX; i++) {
+               sb = &sunix_board_table[i];
+
+               if (sb->board_enum > 0) {
+                       pdev = sb->pdev;
+                       sb->ports = sb->pb_info.num_serport +
+                               sb->pb_info.num_parport;
+                       sb->ser_port = sb->pb_info.num_serport;
+                       sb->par_port = sb->pb_info.num_parport;
+                       snx_ser_port_total_cnt = snx_ser_port_total_cnt +
+                               sb->ser_port;
+                       snx_par_port_total_cnt = snx_par_port_total_cnt +
+                               sb->par_port;
+
+                       if (snx_ser_port_total_cnt > SNX_SER_TOTAL_MAX) {
+                               pr_err("SNX Error: Too much serial port\n");
+                               status = -EIO;
+                               return status;
+                       }
+
+                       if (snx_par_port_total_cnt > SNX_PAR_SUPPORT_MAX) {
+                               pr_err("SNX Error: Too much parallel port\n");
+                               status = -EIO;
+                               return status;
+                       }
+
+                       for (j = 0; j < SNX_PCICFG_BAR_TOTAL; j++)
+                               sb->bar_addr[j] = pci_resource_start(pdev, j);
+
+
+                       sb->irq = sb->pdev->irq;
+
+                       if (sb->irq <= 0) {
+                               pr_err("SNX Error: SUNIX Board %s Series 
(bus:%d device:%d), in configuartion space, irq isn't valid !\n\n",
+                               sb->pb_info.board_name,
+                               sb->bus_number,
+                               sb->dev_number);
+
+                               status = -EIO;
+                               return status;
+                       }
+               }
+       }
+
+       return status;
+}
+
+
+static int sunix_assign_resource(void)
+{
+       struct sunix_board *sb = NULL;
+       struct sunix_ser_port *sp = NULL;
+       struct sunix_par_port *pp = NULL;
+
+       int status = 0;
+       int i;
+       int j;
+       int k;
+       int ser_n;
+       int ser_port_index = 0;
+
+       memset(sunix_ser_table, 0, (SNX_SER_TOTAL_MAX + 1) *
+       sizeof(struct sunix_ser_port));
+
+       memset(sunix_par_table, 0, (SNX_PAR_TOTAL_MAX) *
+       sizeof(struct sunix_par_port));
+
+       for (i = 0; i < SNX_BOARDS_MAX; i++) {
+               sb = &sunix_board_table[i];
+
+               if (sb->board_enum > 0) {
+                       if (sb->ser_port > 0) {
+                               sb->vector_mask = 0;
+
+               // assign serial port resource
+               ser_n = sb->ser_port_index = ser_port_index;
+
+               sp = &sunix_ser_table[ser_n];
+
+               if (sp == NULL) {
+                       status = -ENXIO;
+                       pr_err("SNX Error: Serial port table address error 
!\n");
+                       return status;
+               }
+
+               for (j = 0; j < sb->ser_port; j++, ser_n++, sp++) {
+                       sp->port.chip_flag = sb->pb_info.port[j].chip_flag;
+                       sp->port.iobase = sb->bar_addr[
+                       sb->pb_info.port[j].bar1] +
+                       sb->pb_info.port[j].offset1;
+
+                       if ((sb->board_flag & BOARDFLAG_REMAP) ==
+                       BOARDFLAG_REMAP) {
+                               sp->port.vector = 0;
+                               sb->vector_mask = 0x00;
+                       } else {
+                               sp->port.vector = sb->bar_addr[
+                               sb->pb_info.intr_vector_bar] +
+                               sb->pb_info.intr_vector_offset;
+                               sb->vector_mask |= (1 << j);
+                       }
+               }
+
+                       ser_port_index = ser_port_index + sb->ser_port;
+               }
+
+
+               // assign parallel port resource
+               if (sb->par_port > 0) {
+                       k = 0;
+
+                       for (j = 0; j < SNX_PAR_TOTAL_MAX; j++) {
+                               if ((k + 1) > sb->par_port)
+                                       break;
+
+                               if (j >= SNX_PAR_TOTAL_MAX) {
+                                       status = -EACCES;
+                                       pr_err("SNX Error: Too much parallel 
port!\n");
+                                       return status;
+                               }
+
+                               pp = &sunix_par_table[j];
+
+                               if (pp == NULL) {
+                                       status = -ENXIO;
+                                       pr_err("SNX Error: Parallel port table 
address error !\n");
+                                       return status;
+                               }
+
+                               if (pp->chip_flag != SUNNONE_HWID) {
+                                       continue;
+                               } else {
+                                       pp->chip_flag = sb->pb_info.port[k +
+                                       sb->ser_port].chip_flag;
+
+                                       pp->base = sb->bar_addr[
+                                       sb->pb_info.port[k +
+                                       sb->ser_port].bar1] +
+                                       sb->pb_info.port[k +
+                                       sb->ser_port].offset1;
+
+                                       pp->base_hi = sb->bar_addr[
+                                       sb->pb_info.port[k +
+                                       sb->ser_port].bar2] +
+                                       sb->pb_info.port[k +
+                                       sb->ser_port].offset2;
+
+                                       pp->bus_number = sb->bus_number;
+                                       pp->dev_number = sb->dev_number;
+                                       pp->board_enum = sb->board_enum;
+                                       k++;
+                               }
+                       }
+               }
+               }
+       }
+
+       return status;
+}
+
+
+static int sunix_ser_port_table_init(void)
+{
+       struct sunix_board *sb = NULL;
+       struct sunix_ser_port *sp = NULL;
+       int status = 0;
+       int i;
+       int j;
+       int n;
+       int AHDC_State = 0;
+       int RS422_State = 0;
+
+       for (i = 0; i < SNX_BOARDS_MAX; i++) {
+               sb = &sunix_board_table[i];
+
+       if (sb == NULL) {
+               status = -ENXIO;
+               pr_err("SNX Error: Board table pointer error !\n");
+               return status;
+       }
+
+       if ((sb->board_enum > 0) && (sb->ser_port > 0)) {
+               n = sb->ser_port_index;
+               sp = &sunix_ser_table[n];
+
+               if (sp == NULL) {
+                       status = -ENXIO;
+                       pr_err("SNX Error: Serial port table pointer error 
!\n");
+                       return status;
+               }
+
+               for (j = 0; j < sb->ser_port; j++, n++, sp++) {
+                       if (j < 4) {
+                               AHDC_State = inb(sb->bar_addr[3]+2) & 0x0F &
+                                               (0x01 << (((j+1)-1) % 4));
+                               RS422_State = inb(sb->bar_addr[3]+3) &
+                               0xF0 & (0x10 << (((j+1)-1) % 4));
+                       } else if (j < 8) {
+                               AHDC_State = inb(sb->bar_addr[1] + 0x32) &
+                               0x0F & (0x01 << (((j+1)-1) % 4));
+                               RS422_State = inb(sb->bar_addr[1] + 0x33) &
+                               0xF0 & (0x10 << (((j+1)-1) % 4));
+                       }
+
+                       RS422_State = ((RS422_State & 0xF0) >> 4);
+                       sp->port.AHDC_State = AHDC_State >> (((j+1)-1)%4);
+                       sp->port.RS422_State = RS422_State >> (((j+1)-1)%4);
+
+                       sp->port.board_enum     = sb->board_enum;
+                       sp->port.bus_number     = sb->bus_number;
+                       sp->port.dev_number     = sb->dev_number;
+                       sp->port.baud_base = 921600;
+                       sp->port.pb_info = sb->pb_info;
+                       sp->port.irq = sb->irq;
+                       sp->port.line = n;
+                       sp->port.uartclk = sp->port.baud_base * 16;
+                       sp->port.iotype = SNX_UPIO_PORT;
+                       sp->port.flags = ASYNC_SHARE_IRQ;
+                       sp->port.ldisc_stop_rx = 0;
+                       spin_lock_init(&sp->port.lock);
+
+                       if (sp->port.chip_flag == SUN1889_HWID) {
+                               sp->port.snx_type = SNX_SER_PORT_SUN1889;
+                               sp->port.type = PORT_SER_16650V2;
+                               sp->port.fifosize = SUN1889_FIFOSIZE_SET;
+                               sp->port.rx_trigger = SUN1889_TRIGGER_LEVEL_SET;
+                       } else if (sp->port.chip_flag == SUN1699_HWID) {
+                               sp->port.snx_type = SNX_SER_PORT_SUN1699;
+                               sp->port.type = PORT_SER_16650V2;
+                               sp->port.fifosize = SUN1699_FIFOSIZE_SET;
+                               sp->port.rx_trigger = SUN1699_TRIGGER_LEVEL_SET;
+                       } else if (sp->port.chip_flag == SUNMATX_HWID) {
+                               sp->port.snx_type = SNX_SER_PORT_SUNMATX;
+                               sp->port.type = PORT_SER_16750;
+                               sp->port.fifosize = SUNMATX_FIFOSIZE_SET;
+                               sp->port.rx_trigger = SUNMATX_TRIGGER_LEVEL_SET;
+                       } else if (sp->port.chip_flag == SUN1999_HWID) {
+                               sp->port.snx_type = SNX_SER_PORT_SUN1999;
+                               sp->port.type = PORT_SER_16750;
+                               sp->port.fifosize = SUN1999_FIFOSIZE_SET;
+                               sp->port.rx_trigger = SUN1999_TRIGGER_LEVEL_SET;
+                       } else {
+                               sp->port.snx_type = SNX_SER_PORT_UNKNOWN;
+                               sp->port.type = PORT_SER_16450;
+                               sp->port.fifosize = DEFAULT_FIFOSIZE;
+                               sp->port.rx_trigger = DEFAULT_TRIGGER_LEVEL;
+                       }
+
+
+                       if ((sb->pb_info.board_flag &
+                               BOARDFLAG_REMAP) == BOARDFLAG_REMAP) {
+                               sp->port.vector_mask = 0;
+                               sp->port.port_flag = PORTFLAG_REMAP;
+                       } else {
+                               sp->port.vector_mask = sb->vector_mask;
+                               sp->port.port_flag = PORTFLAG_NONE;
+                       }
+
+                       if ((sb->pb_info.board_flag &
+                               BOARDFLAG_16PORTS) ==   BOARDFLAG_16PORTS)
+                               sp->port.port_flag |= PORTFLAG_16PORTS;
+
+
+                       sp->port.setserial_flag = SNX_SER_BAUD_NOTSETSER;
+                       }
+
+                       sb->ser_isr = sunix_ser_interrupt;
+               } else {
+                       sb->ser_isr = NULL;
+               }
+       }
+
+
+       // release io resource
+       for (i = 0; i < SNX_SER_TOTAL_MAX; i++) {
+               sp = &sunix_ser_table[i];
+
+               if (sp->port.iobase > 0)
+                       release_region(sp->port.iobase, SNX_SER_ADDRESS_LENGTH);
+
+       }
+       return status;
+}
+
+
+static int sunix_par_port_table_init(void)
+{
+       struct sunix_board *sb = NULL;
+       struct sunix_par_port *pp = NULL;
+       int status = 0;
+       int i;
+       int j;
+       int k;
+
+       for (i = 0; i < SNX_BOARDS_MAX; i++) {
+               sb = &sunix_board_table[i];
+
+               if (sb == NULL) {
+                       status = -ENXIO;
+                       pr_err("SNX Error: Board table pointer error !\n");
+                       return status;
+               }
+
+               if ((sb->board_enum > 0) && (sb->par_port > 0)) {
+                       k = 0;
+                       for (j = 0; j < SNX_PAR_TOTAL_MAX; j++) {
+                               pp = &sunix_par_table[j];
+
+                               if (pp == NULL) {
+                                       status = -ENXIO;
+               pr_err("SNX Error: Parallel port tablepointer error\n");
+                                       return status;
+                               }
+
+                               if ((k + 1) > sb->par_port)
+                                       break;
+
+                               if ((pp->bus_number == sb->bus_number) &&
+                                       (pp->dev_number == sb->dev_number) &&
+                                       (pp->board_enum == sb->board_enum)) {
+
+                                       pp->pb_info = sb->pb_info;
+                                       pp->irq = PARPORT_IRQ_NONE;
+                                       pp->portnum = j;
+
+                                       if (pp->chip_flag == SUN1888_HWID) {
+                                               pp->snx_type =
+                                               SNX_PAR_PORT_SUN1888;
+                                       } else if (pp->chip_flag ==
+                                               SUN1689_HWID) {
+                                               pp->snx_type =
+                                               SNX_PAR_PORT_SUN1689;
+                                       } else if (pp->chip_flag ==
+                                               SUNMATX_HWID) {
+                                               pp->snx_type =
+                                               SNX_PAR_PORT_SUNMATX;
+                                       } else if (pp->chip_flag ==
+                                               SUN1999_HWID) {
+                                               pp->snx_type =
+                                               SNX_PAR_PORT_SUN1999;
+                                       } else {
+                                               pp->snx_type =
+                                               SNX_PAR_PORT_UNKNOWN;
+                                       }
+
+                                       if ((sb->pb_info.board_flag &
+                                               BOARDFLAG_REMAP) ==
+                                               BOARDFLAG_REMAP) {
+                                               pp->port_flag = PORTFLAG_REMAP;
+                                       } else {
+                                               pp->port_flag = PORTFLAG_NONE;
+                                       }
+                                       sb->par_isr = NULL;
+                                       k++;
+                               }
+                       }
+               }
+       }
+
+
+       // release io resource
+       for (i = 0; i < SNX_PAR_TOTAL_MAX; i++) {
+               pp = &sunix_par_table[i];
+
+               if (pp->base > 0) {
+                       release_region(pp->base, SNX_PAR_ADDRESS_LENGTH);
+
+
+                       release_region(pp->base, SNX_PAR_STD_ADDR_LENGTH);
+
+                       release_region(pp->base +
+                       SNX_PAR_STD_ADDR_LENGTH,
+                       SNX_PAR_ETD_ADDR_LENGTH);
+
+                       if (pp->base_hi > 0) {
+                               release_region(pp->base_hi,
+                               SNX_PAR_ADDRESS_LENGTH);
+
+                               release_region(pp->base_hi,
+                               SNX_PAR_STD_ADDR_LENGTH);
+
+                               release_region(pp->base_hi +
+                               SNX_PAR_STD_ADDR_LENGTH,
+                               SNX_PAR_ETD_ADDR_LENGTH);
+                       }
+               }
+       }
+
+       return status;
+}
+
+int sunix_register_irq(void)
+{
+       struct sunix_board *sb = NULL;
+       int status = 0;
+       int i;
+
+       for (i = 0; i < SNX_BOARDS_MAX; i++) {
+               sb = &sunix_board_table[i];
+
+               if (sb == NULL) {
+                       status = -ENXIO;
+                       pr_err("SNX Error: Board table pointer error !\n");
+                       return status;
+               }
+
+               if (sb->board_enum > 0) {
+                       status = request_irq(sb->irq,
+                       sunix_interrupt,
+                       IRQF_SHARED,
+                       "snx", sb);
+
+                       if (status) {
+                               pr_err("SNX Error: SUNIX Multi-I/O %s 
Board(bus:%d device:%d), request\n",
+                               sb->pb_info.board_name,
+                               sb->bus_number,
+                               sb->dev_number);
+                               pr_err("           IRQ %d fail, IRQ %d may be 
conflit with another device.\n",
+                               sb->irq, sb->irq);
+                               return status;
+                       }
+               }
+       }
+
+       return status;
+}
+
+
+void sunix_release_irq(void)
+{
+       struct sunix_board *sb = NULL;
+       int i;
+
+       for (i = 0; i < SNX_BOARDS_MAX; i++) {
+               sb = &sunix_board_table[i];
+
+               if (sb->board_enum > 0)
+                       free_irq(sb->irq, sb);
+       }
+}
+
+
+static int snx_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       return 0;
+}
+
+static struct pci_driver snx_pci_driver = {
+       .name                   = "snx",
+       .probe                  = snx_pci_probe,
+       .suspend                = snx_suspend_one,
+       .resume                 = snx_resume_one,
+       .id_table               = sunix_pci_board_id,
+};
+
+static int __init snx_init(void)
+{
+       int status = 0;
+
+       snx_ser_port_total_cnt = snx_par_port_total_cnt = 0;
+
+       status = sunix_pci_board_probe();
+       if (status != 0)
+               goto step1_fail;
+
+       status = sunix_get_pci_board_conf();
+       if (status != 0)
+               goto step1_fail;
+
+
+       status = sunix_assign_resource();
+       if (status != 0)
+               goto step1_fail;
+
+
+       status = sunix_ser_port_table_init();
+       if (status != 0)
+               goto step1_fail;
+
+
+       status = sunix_par_port_table_init();
+       if (status != 0)
+               goto step1_fail;
+
+
+       status = sunix_register_irq();
+       if (status != 0)
+               goto step1_fail;
+
+
+       status = sunix_ser_register_driver(&sunix_ser_reg);
+       if (status != 0)
+               goto step2_fail;
+
+       status = sunix_ser_register_ports(&sunix_ser_reg);
+       if (status != 0)
+               goto step3_fail;
+
+       status = pci_register_driver(&snx_pci_driver);
+       if (status != 0)
+               goto step7_fail;
+
+       if (snx_par_port_total_cnt > 0) {
+               status = sunix_par_parport_init();
+               if (status != 0)
+                       goto step4_fail;
+
+               status = sunix_par_ppdev_init();
+               if (status != 0)
+                       goto step5_fail;
+
+               status = sunix_par_lp_init();
+               if (status != 0)
+                       goto step6_fail;
+
+       }
+
+#if SNX_DBG
+       sunix_debug();
+#endif
+
+       return status;
+
+
+       if (snx_par_port_total_cnt > 0) {
+step7_fail:
+
+               pci_unregister_driver(&snx_pci_driver);
+
+step6_fail:
+
+               sunix_par_ppdev_exit();
+
+step5_fail:
+
+               sunix_par_parport_exit();
+
+step4_fail:
+
+               sunix_ser_unregister_ports(&sunix_ser_reg);
+       }
+
+step3_fail:
+
+       sunix_ser_unregister_driver(&sunix_ser_reg);
+
+
+step2_fail:
+
+       sunix_release_irq();
+
+
+step1_fail:
+
+       pr_err("SNX Error: Couldn't Loading SUNIX Multi-I/O Board Driver Module 
correctly,\n");
+       pr_err("           please reboot system and try again. If still can't 
loading driver,\n");
+       pr_err("           contact support.\n\n");
+
+       return status;
+}
+
+
+static void __exit snx_exit(void)
+{
+       if (snx_par_port_total_cnt > 0) {
+               sunix_par_lp_exit();
+
+               sunix_par_ppdev_exit();
+
+               sunix_par_parport_exit();
+       }
+
+       sunix_ser_unregister_ports(&sunix_ser_reg);
+
+       sunix_ser_unregister_driver(&sunix_ser_reg);
+
+       sunix_release_irq();
+       pci_unregister_driver(&snx_pci_driver);
+       pr_err("SNX Info : Unload SUNIX Multi-I/O Board Driver Module Done.\n");
+}
+
+module_init(snx_init);
+module_exit(snx_exit);
+
diff --git a/mfd/sunix/snx_serial.c b/mfd/sunix/snx_serial.c
new file mode 100644
index 00000000..74319d8f
--- /dev/null
+++ b/mfd/sunix/snx_serial.c
@@ -0,0 +1,3513 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "snx_common.h"
+#include "driver_extd.h"
+
+#define        EEPROM_ACCESS_DELAY_COUNT                       100000
+#define SNX_DRIVER_VERSION     "V2.0.4.5"
+
+struct tty_driver              *snx_driver;
+
+static DEFINE_SEMAPHORE(ser_port_sem);
+
+#define SNX_HIGH_BITS_OFFSET   ((sizeof(long)-sizeof(int))*8)
+#define sunix_ser_users(state) \
+((state)->count + ((state)->info ? (state)->info->blocked_open : 0))
+
+#define RELEVANT_IFLAG(iflag)  ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+#define snx_set_mctrl(port, set)       snx_ser_update_mctrl(port, set, 0)
+#define snx_clear_mctrl(port, clear)   snx_ser_update_mctrl(port, 0, clear)
+
+static struct tty_port snx_service_port;
+
+struct serial_uart_config {
+       char    *name;
+       int             dfl_xmit_fifo_size;
+       int             flags;
+};
+
+static const struct serial_uart_config
+snx_uart_config[PORT_SER_MAX_UART + 1] = {
+       { "unknown",    1,              0 },
+       { "8250",               1,              0 },
+       { "16450",              1,              0 },
+       { "16550",              1,              0 },
+       { "16550A",     16,     UART_CLEAR_FIFO | UART_USE_FIFO },
+       { "Cirrus",             1,              0 },
+       { "ST16650",    1,              0 },
+       { "ST16650V2",  32,             UART_CLEAR_FIFO | UART_USE_FIFO },
+       { "TI16750",    64,             UART_CLEAR_FIFO | UART_USE_FIFO },
+};
+
+
+static void snx_ser_handle_cts_change(
+       struct snx_ser_port *, unsigned int);
+static void snx_ser_update_mctrl(
+       struct snx_ser_port *, unsigned int, unsigned int);
+static void     snx_ser_write_wakeup(struct snx_ser_port *);
+static void     snx_ser_stop(struct tty_struct *);
+static void     __snx_ser_start(struct tty_struct *);
+static void     snx_ser_start(struct tty_struct *);
+static void     snx_ser_shutdown(struct snx_ser_state *);
+static void __snx_ser_put_char(
+       struct snx_ser_port *, struct circ_buf *, unsigned char);
+static int      snx_ser_put_char(
+       struct tty_struct *, unsigned char);
+static void     snx_ser_flush_chars(struct tty_struct *);
+static int      snx_ser_chars_in_buffer(struct tty_struct *);
+static void     snx_ser_flush_buffer(struct tty_struct *);
+static void     snx_ser_send_xchar(struct tty_struct *, char);
+static void     snx_ser_throttle(struct tty_struct *);
+static void     snx_ser_unthrottle(struct tty_struct *);
+static int      snx_ser_get_info(
+       struct snx_ser_state *, struct serial_struct *);
+static int      snx_ser_set_info(
+       struct snx_ser_state *, struct serial_struct *);
+static int      snx_ser_write_room(struct tty_struct *);
+static int      snx_ser_write(
+       struct tty_struct *, const unsigned char *, int);
+static int      snx_ser_get_lsr_info(
+       struct snx_ser_state *, unsigned int *);
+static int      snx_ser_tiocmget(struct tty_struct *);
+static int      snx_ser_tiocmset(
+       struct tty_struct *, unsigned int, unsigned int);
+static int      snx_ser_break_ctl(struct tty_struct *, int);
+static int      snx_ser_wait_modem_status(
+       struct snx_ser_state *, unsigned long);
+static int      snx_ser_get_count(struct snx_ser_state *,
+       struct serial_icounter_struct *);
+static int      snx_ser_ioctl(struct tty_struct *, unsigned int, unsigned 
long);
+static void            snx_ser_hangup(struct tty_struct *tty);
+static unsigned int    snx_ser_get_divisor(
+       struct snx_ser_port *port, unsigned int baud);
+
+static void            snx_ser_set_termios(
+       struct tty_struct *, struct SNXTERMIOS *);
+static void            snx_ser_update_timeout(
+       struct snx_ser_port *, unsigned int, unsigned int);
+static struct  snx_ser_state *snx_ser_get(struct snx_ser_driver *, int);
+static int             snx_ser_block_til_ready(
+       struct file *, struct snx_ser_state *);
+static void            snx_ser_wait_until_sent(struct tty_struct *, int);
+static int             snx_ser_open(struct tty_struct *, struct file *);
+static void            snx_ser_close(struct tty_struct *, struct file *);
+
+
+static void            sunix_ser_set_mctrl(
+       struct snx_ser_port *, unsigned int);
+static unsigned int sunix_ser_tx_empty(struct snx_ser_port *);
+static unsigned int sunix_ser_get_mctrl(struct snx_ser_port *);
+static void            sunix_ser_stop_tx(struct snx_ser_port *, unsigned int);
+static void            sunix_ser_start_tx(struct snx_ser_port *, unsigned int);
+static void            sunix_ser_stop_rx(struct snx_ser_port *);
+static void            sunix_ser_enable_ms(struct snx_ser_port *);
+static void            sunix_ser_break_ctl(struct snx_ser_port *, int);
+static int             sunix_ser_startup(struct snx_ser_port *);
+static void            sunix_ser_shutdown(struct snx_ser_port *);
+static unsigned int sunix_ser_get_divisor(
+       struct snx_ser_port *, unsigned int);
+static void            sunix_ser_set_termios(
+       struct snx_ser_port *, struct SNXTERMIOS *, struct SNXTERMIOS *);
+
+static void sunix_ser_timeout(struct timer_list *t);
+
+static void sunix_ser_receive_chars(
+       struct sunix_ser_port *, unsigned char *);
+static void sunix_ser_transmit_chars(
+       struct sunix_ser_port *);
+static void sunix_ser_check_modem_status(
+       struct sunix_ser_port *, unsigned char);
+static void sunix_ser_handle_port(
+       struct sunix_ser_port *, unsigned char);
+
+static void            sunix_ser_release_io(struct snx_ser_port *port);
+static void            sunix_ser_request_io(struct snx_ser_port *port);
+static void            sunix_ser_configure_port(
+       struct snx_ser_driver *drv,
+       struct snx_ser_state *state,
+       struct snx_ser_port *port);
+static void            sunix_ser_unconfigure_port(
+       struct snx_ser_driver *drv,
+       struct snx_ser_state *state);
+static int             sunix_ser_add_one_port(
+       struct snx_ser_driver *drv, struct snx_ser_port *port);
+static int             sunix_ser_remove_one_port(
+       struct snx_ser_driver *drv, struct snx_ser_port *port);
+
+static unsigned char read_interrupt_vector_byte(
+       struct sunix_ser_port *);
+static unsigned int  read_interrupt_vector_word(
+       struct sunix_ser_port *);
+static unsigned int  read_1999_interrupt_vector_word(
+       struct sunix_board *, struct sunix_ser_port *);
+static unsigned char read_uart_rx(struct sunix_ser_port *);
+static unsigned char read_uart_iir(struct sunix_ser_port *);
+static unsigned char read_uart_lcr(struct sunix_ser_port *);
+static unsigned char read_uart_lsr(struct sunix_ser_port *);
+static unsigned char read_uart_msr(struct sunix_ser_port *);
+static void write_uart_tx(struct sunix_ser_port *, unsigned char);
+static void write_uart_ier(struct sunix_ser_port *, unsigned char);
+static void write_uart_fcr(struct sunix_ser_port *, unsigned char);
+static void write_uart_lcr(struct sunix_ser_port *, unsigned char);
+static void write_uart_mcr(struct sunix_ser_port *, unsigned char);
+static void write_uart_dll(struct sunix_ser_port *, int);
+static void write_uart_dlm(struct sunix_ser_port *, int);
+
+static int EEPROMWriteData(int, int, int);
+
+static void snx_ser_insert_char
+(
+               struct snx_ser_port *port,
+               unsigned int status,
+               unsigned int overrun,
+               unsigned int ch,
+               unsigned int flag
+)
+{
+       struct snx_ser_info *info = port->info;
+       struct tty_struct *tty = info->tty;
+       struct snx_ser_state *state = NULL;
+       struct tty_port *tport = NULL;
+
+       state = tty->driver_data;
+
+       tport = &state->tport;
+
+       if ((status & port->ignore_status_mask & ~overrun) == 0) {
+
+               if (tty_insert_flip_char(tport, ch, flag) == 0)
+                       ++port->icount.buf_overrun;
+       }
+
+       if (status & ~port->ignore_status_mask & overrun) {
+
+               if (tty_insert_flip_char(tport, 0, TTY_OVERRUN) == 0)
+                       ++port->icount.buf_overrun;
+       }
+}
+
+static unsigned char read_interrupt_vector_byte(struct sunix_ser_port *sp)
+{
+       unsigned char data;
+
+       if (sp->port.vector) {
+               data = inb(sp->port.vector);
+               return data;
+       }
+       return 0;
+}
+
+
+static unsigned int read_interrupt_vector_word(struct sunix_ser_port *sp)
+{
+       unsigned int data;
+       unsigned int vet1;
+       unsigned int vet2;
+
+       if (sp->port.vector) {
+               vet1 = inb(sp->port.vector);
+               vet2 = inb(sp->port.vector + 1);
+
+               vet2 <<= 8;
+               data = (vet1 | vet2);
+       return data;
+       }
+       return 0;
+}
+
+static unsigned int read_1999_interrupt_vector_word(
+struct sunix_board *sb, struct sunix_ser_port *sp)
+{
+       unsigned int data;
+       unsigned int vet1 = 0;
+       unsigned int vet2 = 0;
+       unsigned int vet3 = 0;
+       unsigned int vet4 = 0;
+       unsigned int var;
+       unsigned int local_vector;
+
+       if (sp->port.vector) {
+               vet1 = inb(sp->port.vector);
+               var = inb(sp->port.vector + 1);
+               local_vector = sb->bar_addr[1];
+
+               if (var == 0x01) {
+                       vet2 = inb(local_vector + 0x30);
+                       vet2 <<= 4;
+               }
+
+               if (var == 0x02) {
+                       vet3 = inb(local_vector + 0x70);
+                       vet3 <<= 8;
+               }
+
+               if (var == 0x04) {
+                       vet4 = inb(local_vector + 0xb0);
+                       vet4 <<= 12;
+               }
+
+               data = (vet1 | vet2 | vet3 | vet4);
+
+               return data;
+       }
+       return 0;
+}
+
+
+static unsigned char read_uart_rx(struct sunix_ser_port *sp)
+{
+       unsigned char data;
+
+       if (sp->port.iobase) {
+               data = inb(sp->port.iobase + UART_RX);
+
+               return data;
+       }
+       return 0;
+}
+
+
+static void write_uart_tx(struct sunix_ser_port *sp, unsigned char data)
+{
+       if (sp->port.iobase)
+               outb(data, sp->port.iobase + UART_TX);
+
+}
+
+
+static void write_uart_ier(struct sunix_ser_port *sp, unsigned char data)
+{
+       if (sp->port.iobase)
+               outb(data, sp->port.iobase + UART_IER);
+
+}
+
+
+static unsigned char read_uart_iir(struct sunix_ser_port *sp)
+{
+       unsigned char data;
+
+       if (sp->port.iobase) {
+               data = inb(sp->port.iobase + UART_IIR);
+               return data;
+       }
+       return 0;
+}
+
+
+static void write_uart_fcr(struct sunix_ser_port *sp, unsigned char data)
+{
+       if (sp->port.iobase)
+               outb(data, sp->port.iobase + UART_FCR);
+
+}
+
+
+static unsigned char read_uart_lcr(struct sunix_ser_port *sp)
+{
+       unsigned char data;
+
+       if (sp->port.iobase) {
+               data = inb(sp->port.iobase + UART_LCR);
+               return data;
+       }
+       return 0;
+}
+
+
+static void write_uart_lcr(struct sunix_ser_port *sp, unsigned char data)
+{
+       if (sp->port.iobase)
+               outb(data, sp->port.iobase + UART_LCR);
+
+}
+
+
+static void write_uart_mcr(struct sunix_ser_port *sp, unsigned char data)
+{
+       if (sp->port.iobase)
+               outb(data, sp->port.iobase + UART_MCR);
+
+}
+
+
+static unsigned char read_uart_lsr(struct sunix_ser_port *sp)
+{
+       unsigned char data;
+
+       if (sp->port.iobase) {
+               data = inb(sp->port.iobase + UART_LSR);
+               return data;
+       }
+       return 0;
+}
+
+
+static unsigned char read_uart_msr(struct sunix_ser_port *sp)
+{
+       unsigned char data;
+
+       if (sp->port.iobase) {
+               data = inb(sp->port.iobase + UART_MSR);
+               return data;
+       }
+       return 0;
+}
+
+
+static void write_uart_dll(struct sunix_ser_port *sp, int data)
+{
+       if (sp->port.iobase)
+               outb(data, sp->port.iobase + UART_DLL);
+
+}
+
+
+static void write_uart_dlm(struct sunix_ser_port *sp, int data)
+{
+       if (sp->port.iobase)
+               outb(data, sp->port.iobase + UART_DLM);
+
+}
+
+static int EEPROMWriteData(int targetConfigAddress, int address, int data)
+{
+       int Busy = -1;
+       int Error = -1;
+       int delayCount = 0;
+
+       do {
+               do {
+                       Busy = inb(targetConfigAddress + 0x08) & 0x01;
+
+                       if (delayCount++ > EEPROM_ACCESS_DELAY_COUNT)
+                               return -1;
+
+               } while (Busy);
+
+               outb(address, targetConfigAddress + 0x09);
+               outb(data, targetConfigAddress + 0x0A);
+               outb(0x03, targetConfigAddress + 0x08);
+
+               delayCount      = 0;
+
+               do {
+                       Busy = inb(targetConfigAddress + 0x08) & 0x01;
+
+                       if (delayCount++ > EEPROM_ACCESS_DELAY_COUNT)
+                               return -1;
+
+               } while (Busy);
+
+               Error = inb(targetConfigAddress + 0x08) & 0x04;
+
+       } while (Error);
+
+       return 0;
+
+}
+
+
+static void snx_ser_handle_cts_change(
+struct snx_ser_port *port, unsigned int status)
+{
+       struct snx_ser_info *info = port->info;
+       struct tty_struct *tty = info->tty;
+
+       port->icount.cts++;
+
+       if (info->flags & SNX_UIF_CTS_FLOW) {
+               if (tty->hw_stopped) {
+                       if (status) {
+                               tty->hw_stopped = 0;
+                               sunix_ser_start_tx(port, 0);
+                               snx_ser_write_wakeup(port);
+                       }
+               } else {
+
+                       if (!status) {
+                               tty->hw_stopped = 1;
+                               sunix_ser_stop_tx(port, 0);
+                       }
+               }
+       }
+}
+
+
+static void snx_ser_update_mctrl(
+struct snx_ser_port *port, unsigned int set, unsigned int clear)
+{
+       unsigned long flags;
+       unsigned int old;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       old = port->mctrl;
+       port->mctrl = (old & ~clear) | set;
+
+       if (old != port->mctrl)
+               sunix_ser_set_mctrl(port, port->mctrl);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void snx_ser_write_wakeup(struct snx_ser_port *port)
+{
+       struct snx_ser_info *info = port->info;
+       struct tty_struct *tty = info->tty;
+
+       tty_wakeup(tty);
+}
+
+
+static void snx_ser_stop(struct tty_struct *tty)
+{
+       struct snx_ser_state *state = NULL;
+       struct snx_ser_port *port = NULL;
+       unsigned long flags;
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return;
+
+       state = tty->driver_data;
+       port = state->port;
+
+       spin_lock_irqsave(&port->lock, flags);
+       sunix_ser_stop_tx(port, 1);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+
+static void __snx_ser_start(struct tty_struct *tty)
+{
+       struct snx_ser_state *state = tty->driver_data;
+       struct snx_ser_port *port = state->port;
+
+       if (!snx_ser_circ_empty(&state->info->xmit) &&
+       state->info->xmit.buf && !tty->stopped && !tty->hw_stopped) {
+               sunix_ser_start_tx(port, 1);
+       }
+}
+
+
+static void snx_ser_start(struct tty_struct *tty)
+{
+       struct snx_ser_state *state = NULL;
+       struct snx_ser_port *port = NULL;
+       unsigned long flags;
+
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return;
+
+       state = tty->driver_data;
+       port = state->port;
+
+       spin_lock_irqsave(&port->lock, flags);
+       __snx_ser_start(tty);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+
+extern int snx_ser_startup(struct snx_ser_state *state, int init_hw)
+{
+       struct snx_ser_info *info = state->info;
+       struct snx_ser_port *port = state->port;
+       unsigned long page;
+       int retval = 0;
+
+       if (info->flags & SNX_UIF_INITIALIZED)
+               return 0;
+
+       if (info->tty)
+               set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       if (port->type == PORT_UNKNOWN)
+               return 0;
+
+       if (!info->xmit.buf) {
+               page = get_zeroed_page(GFP_KERNEL);
+
+               if (!page)
+                       return -ENOMEM;
+
+               info->xmit.buf = (unsigned char *) page;
+
+               info->tmpbuf = info->xmit.buf + SNX_UART_XMIT_SIZE;
+
+               sema_init(&info->tmpbuf_sem, 1);
+
+               snx_ser_circ_clear(&info->xmit);
+       }
+
+       retval = sunix_ser_startup(port);
+
+       if (retval == 0) {
+               if (init_hw) {
+                       snx_ser_change_speed(state, NULL);
+
+                       if (info->tty->termios.c_cflag & CBAUD)
+                               snx_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
+
+               }
+
+               info->flags |= SNX_UIF_INITIALIZED;
+
+               clear_bit(TTY_IO_ERROR, &info->tty->flags);
+       }
+
+       if (retval && capable(CAP_SYS_ADMIN))
+               retval = 0;
+
+       return retval;
+}
+
+
+static void snx_ser_shutdown(struct snx_ser_state *state)
+{
+       struct snx_ser_info *info = state->info;
+       struct snx_ser_port *port = state->port;
+
+       if (!(info->flags & SNX_UIF_INITIALIZED))
+               return;
+
+       if (!info->tty || (info->tty->termios.c_cflag & HUPCL))
+               snx_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+
+       wake_up_interruptible(&info->delta_msr_wait);
+
+       sunix_ser_shutdown(port);
+
+       synchronize_irq(port->irq);
+
+       if (info->xmit.buf) {
+               free_page((unsigned long)info->xmit.buf);
+               info->xmit.buf = NULL;
+               info->tmpbuf = NULL;
+       }
+
+       if (info->tty)
+               set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       info->flags &= ~SNX_UIF_INITIALIZED;
+}
+
+
+static void __snx_ser_put_char(
+struct snx_ser_port *port, struct circ_buf *circ, unsigned char c)
+{
+       unsigned long flags;
+
+       if (!circ->buf)
+               return;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       if (snx_ser_circ_chars_free(circ) != 0) {
+               circ->buf[circ->head] = c;
+               circ->head = (circ->head + 1) & (SNX_UART_XMIT_SIZE - 1);
+       }
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int  snx_ser_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct snx_ser_state *state = NULL;
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return 0;
+
+       state = tty->driver_data;
+       __snx_ser_put_char(state->port, &state->info->xmit, ch);
+
+       return 0;
+}
+
+
+static void snx_ser_flush_chars(struct tty_struct *tty)
+{
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return;
+
+       snx_ser_start(tty);
+}
+
+
+static int snx_ser_chars_in_buffer(struct tty_struct *tty)
+{
+       struct snx_ser_state *state = NULL;
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return 0;
+
+
+       state = tty->driver_data;
+
+       return snx_ser_circ_chars_pending(&state->info->xmit);
+}
+
+
+static void snx_ser_flush_buffer(struct tty_struct *tty)
+{
+       struct snx_ser_state *state = NULL;
+       struct snx_ser_port *port = NULL;
+       unsigned long flags;
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return;
+
+
+       state = tty->driver_data;
+       port = state->port;
+
+       if (!state || !state->info)
+               return;
+
+
+       spin_lock_irqsave(&port->lock, flags);
+       snx_ser_circ_clear(&state->info->xmit);
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       wake_up_interruptible(&tty->write_wait);
+
+       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+       tty->ldisc->ops->write_wakeup) {
+               (tty->ldisc->ops->write_wakeup)(tty);
+       }
+}
+
+
+static void snx_ser_send_xchar(struct tty_struct *tty, char ch)
+{
+       struct snx_ser_state *state = NULL;
+       struct snx_ser_port *port = NULL;
+       unsigned long flags;
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return;
+
+       state = tty->driver_data;
+       port = state->port;
+       port->x_char = ch;
+
+       if (ch) {
+               spin_lock_irqsave(&port->lock, flags);
+               sunix_ser_start_tx(port, 0);
+               spin_unlock_irqrestore(&port->lock, flags);
+       }
+}
+
+
+static void snx_ser_throttle(struct tty_struct *tty)
+{
+       struct snx_ser_state *state = NULL;
+       struct snx_ser_port *port = NULL;
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return;
+
+       state = tty->driver_data;
+       port = state->port;
+
+       port->ldisc_stop_rx = 1;
+
+       if (I_IXOFF(tty))
+               snx_ser_send_xchar(tty, STOP_CHAR(tty));
+
+       if (tty->termios.c_cflag & CRTSCTS)
+               snx_clear_mctrl(state->port, TIOCM_RTS);
+}
+
+
+static void snx_ser_unthrottle(struct tty_struct *tty)
+{
+       struct snx_ser_state *state = NULL;
+       struct snx_ser_port *port = NULL;
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return;
+
+       state = tty->driver_data;
+       port = state->port;
+
+       port->ldisc_stop_rx = 0;
+
+       if (I_IXOFF(tty)) {
+               if (port->x_char)
+                       port->x_char = 0;
+               else
+                       snx_ser_send_xchar(tty, START_CHAR(tty));
+
+       }
+
+       if (tty->termios.c_cflag & CRTSCTS)
+               snx_set_mctrl(port, TIOCM_RTS);
+
+}
+
+static int snx_ser_get_info(struct snx_ser_state *state,
+struct serial_struct *retinfo)
+{
+       struct snx_ser_port *port = state->port;
+
+       struct serial_struct tmp;
+
+       memset(&tmp, 0, sizeof(tmp));
+       tmp.type            = port->type;
+       tmp.line            = port->line;
+       tmp.port            = port->iobase;
+
+       if (SNX_HIGH_BITS_OFFSET)
+               tmp.port_high = (long) port->iobase >> SNX_HIGH_BITS_OFFSET;
+
+       tmp.irq             = port->irq;
+       tmp.flags           = port->flags;
+       tmp.xmit_fifo_size  = port->fifosize;
+       tmp.baud_base       = port->uartclk / 16;
+       tmp.close_delay     = state->close_delay;
+       tmp.closing_wait    = state->closing_wait;
+
+       tmp.custom_divisor  = port->custom_divisor;
+       tmp.io_type         = port->iotype;
+
+       if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+               return -EFAULT;
+
+       return 0;
+}
+
+
+static int snx_ser_set_info(struct snx_ser_state *state,
+struct serial_struct *newinfo)
+{
+       struct serial_struct new_serial;
+       struct snx_ser_port *port = state->port;
+       struct tty_port         *tport = &state->tport;
+       unsigned long new_port;
+       unsigned int change_irq;
+       unsigned int change_port;
+       unsigned int old_custom_divisor;
+       unsigned int closing_wait;
+       unsigned int close_delay;
+       unsigned int old_flags;
+       unsigned int new_flags;
+       int retval = 0;
+
+       if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
+               return -EFAULT;
+
+       new_port = new_serial.port;
+
+       if (SNX_HIGH_BITS_OFFSET)
+               new_port += (unsigned long)
+                       new_serial.port_high << SNX_HIGH_BITS_OFFSET;
+
+       new_serial.irq = irq_canonicalize(new_serial.irq);
+
+       close_delay = new_serial.close_delay;
+       closing_wait = new_serial.closing_wait ==
+               ASYNC_CLOSING_WAIT_NONE ?
+               SNX_USF_CLOSING_WAIT_NONE : new_serial.closing_wait;
+
+       down(&state->sem);
+
+       change_irq  = new_serial.irq != port->irq;
+
+       change_port =   new_port != port->iobase ||
+                                       new_serial.io_type != port->iotype ||
+                                       new_serial.type != port->type;
+
+       old_flags = port->flags;
+       new_flags = new_serial.flags;
+       old_custom_divisor = port->custom_divisor;
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               retval = -EPERM;
+                       if (change_irq ||
+                               change_port ||
+                               (new_serial.baud_base != port->uartclk / 16) ||
+                               (close_delay != state->close_delay) ||
+                               (closing_wait != state->closing_wait) ||
+                               (new_serial.xmit_fifo_size != port->fifosize) ||
+                               (((new_flags ^ old_flags)
+                               & ~SNX_UPF_USR_MASK) != 0)) {
+                               goto exit;
+                       }
+
+               port->flags = ((port->flags & ~SNX_UPF_USR_MASK) |
+                       (new_flags & SNX_UPF_USR_MASK));
+               port->custom_divisor = new_serial.custom_divisor;
+               goto check_and_exit;
+       }
+
+       if (change_port || change_irq) {
+               retval = -EBUSY;
+
+               if (sunix_ser_users(state) > 1)
+                       goto exit;
+
+               snx_ser_shutdown(state);
+       }
+
+       if (change_port) {
+               unsigned int old_type;
+
+               old_type = port->type;
+
+               if (old_type != PORT_UNKNOWN)
+                       sunix_ser_release_io(port);
+
+               port->iobase = new_port;
+               port->type = new_serial.type;
+               port->iotype = new_serial.io_type;
+
+               retval = 0;
+       }
+
+       port->irq       = new_serial.irq;
+       port->uartclk   = new_serial.baud_base * 16;
+       port->flags     = ((port->flags & ~SNX_UPF_CHANGE_MASK) |
+               (new_flags & SNX_UPF_CHANGE_MASK));
+       port->custom_divisor    = new_serial.custom_divisor;
+       state->close_delay      = close_delay;
+       state->closing_wait     = closing_wait;
+       port->fifosize  = new_serial.xmit_fifo_size;
+
+       tport->low_latency = (port->flags & SNX_UPF_LOW_LATENCY) ? 1 : 0;
+
+check_and_exit:
+       retval = 0;
+       if (port->type == PORT_UNKNOWN)
+               goto exit;
+
+
+       if (state->info->flags & SNX_UIF_INITIALIZED) {
+               if (((old_flags ^ port->flags) & SNX_UPF_SPD_MASK) ||
+               old_custom_divisor != port->custom_divisor) {
+
+                       if (port->flags & SNX_UPF_SPD_MASK) {
+                               pr_info("SNX Info : %s sets custom speed ",
+                                       current->comm);
+                               pr_info("on ttySNX%d. This is deprecated.\n",
+                                       port->line);
+                       }
+                       snx_ser_change_speed(state, NULL);
+               }
+       } else {
+               retval = snx_ser_startup(state, 1);
+       }
+exit:
+
+       up(&state->sem);
+
+       return retval;
+}
+
+
+static int snx_ser_write_room(struct tty_struct *tty)
+{
+       struct snx_ser_state *state = NULL;
+       int line = SNX_SER_DEVNUM(tty);
+       int status = 0;
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return 0;
+
+       state = tty->driver_data;
+
+       status = snx_ser_circ_chars_free(&state->info->xmit);
+
+       return status;
+}
+
+
+static int snx_ser_write(struct tty_struct *tty,
+const unsigned char *buf, int count)
+{
+       struct snx_ser_state *state = tty->driver_data;
+       struct circ_buf *circ = NULL;
+       struct snx_ser_port *port = state->port;
+       unsigned long flags;
+
+       int c;
+       int ret = 0;
+
+       if (!state || !state->info)
+               return -EL3HLT;
+
+       circ = &state->info->xmit;
+
+       if (!circ->buf)
+               return 0;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       while (1) {
+               c = CIRC_SPACE_TO_END(circ->head, circ->tail,
+                       SNX_UART_XMIT_SIZE);
+               if (count < c)
+                       c = count;
+
+               if (c <= 0)
+                       break;
+
+               memcpy(circ->buf + circ->head, buf, c);
+
+               circ->head = (circ->head + c) & (SNX_UART_XMIT_SIZE - 1);
+               buf += c;
+               count -= c;
+               ret += c;
+       }
+
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       snx_ser_start(tty);
+
+       return ret;
+}
+
+
+static int snx_ser_get_lsr_info(struct snx_ser_state *state,
+unsigned int *value)
+{
+       struct snx_ser_port *port = state->port;
+       unsigned int result = 0;
+
+       result = sunix_ser_tx_empty(port);
+
+
+       if ((port->x_char) ||
+               ((snx_ser_circ_chars_pending(&state->info->xmit) > 0) &&
+               !state->info->tty->stopped && !state->info->tty->hw_stopped)) {
+               result &= ~TIOCSER_TEMT;
+       }
+
+       return put_user(result, value);
+
+}
+
+
+static int snx_ser_tiocmget(struct tty_struct *tty)
+{
+       struct snx_ser_state *state = NULL;
+       struct snx_ser_port *port = NULL;
+       int result = -EIO;
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return 0;
+
+       state = tty->driver_data;
+       port = state->port;
+
+       down(&state->sem);
+
+       if (!(tty->flags & (1 << TTY_IO_ERROR))) {
+               result = port->mctrl;
+               result |= sunix_ser_get_mctrl(port);
+       }
+
+       up(&state->sem);
+
+       return result;
+}
+
+static int snx_ser_tiocmset(struct tty_struct *tty,
+unsigned int set, unsigned int clear)
+{
+       struct snx_ser_state *state = NULL;
+       struct snx_ser_port *port = NULL;
+       int ret = -EIO;
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return 0;
+
+       state = tty->driver_data;
+       port = state->port;
+
+       down(&state->sem);
+
+       if (!(tty->flags & (1 << TTY_IO_ERROR))) {
+               snx_ser_update_mctrl(port, set, clear);
+               ret = 0;
+       }
+
+       up(&state->sem);
+
+       return ret;
+}
+
+
+static int  snx_ser_break_ctl(struct tty_struct *tty, int break_state)
+{
+       struct snx_ser_state *state = NULL;
+       struct snx_ser_port *port = NULL;
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return 0;
+
+       state = tty->driver_data;
+       port = state->port;
+
+       down(&state->sem);
+
+       if (port->type != PORT_UNKNOWN)
+               sunix_ser_break_ctl(port, break_state);
+
+
+       up(&state->sem);
+
+       return 0;
+}
+
+
+static int snx_ser_wait_modem_status(
+struct snx_ser_state *state, unsigned long arg)
+{
+       struct snx_ser_port *port = state->port;
+       DECLARE_WAITQUEUE(wait, current);
+       struct snx_ser_icount cprev;
+       struct snx_ser_icount cnow;
+       int ret = 0;
+
+       spin_lock_irq(&port->lock);
+       memcpy(&cprev, &port->icount, sizeof(struct snx_ser_icount));
+
+       sunix_ser_enable_ms(port);
+
+       spin_unlock_irq(&port->lock);
+
+       add_wait_queue(&state->info->delta_msr_wait, &wait);
+
+       for (;;) {
+               spin_lock_irq(&port->lock);
+               memcpy(&cnow, &port->icount, sizeof(struct snx_ser_icount));
+               spin_unlock_irq(&port->lock);
+
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+                       ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+                       ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+                       ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
+                       ret = 0;
+                       break;
+               }
+
+               schedule();
+
+
+               if (signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+
+               cprev = cnow;
+       }
+
+       current->state = TASK_RUNNING;
+       remove_wait_queue(&state->info->delta_msr_wait, &wait);
+
+       return ret;
+}
+
+
+static int snx_ser_get_count(struct snx_ser_state *state,
+struct serial_icounter_struct *icnt)
+{
+       struct serial_icounter_struct icount;
+       struct snx_ser_icount cnow;
+       struct snx_ser_port *port = state->port;
+
+       spin_lock_irq(&port->lock);
+       memcpy(&cnow, &port->icount, sizeof(struct snx_ser_icount));
+       spin_unlock_irq(&port->lock);
+
+       icount.cts         = cnow.cts;
+       icount.dsr         = cnow.dsr;
+       icount.rng         = cnow.rng;
+       icount.dcd         = cnow.dcd;
+       icount.rx          = cnow.rx;
+       icount.tx          = cnow.tx;
+       icount.frame       = cnow.frame;
+       icount.overrun     = cnow.overrun;
+       icount.parity      = cnow.parity;
+       icount.brk         = cnow.brk;
+       icount.buf_overrun = cnow.buf_overrun;
+
+       return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0;
+}
+
+static int snx_ser_ioctl(struct tty_struct *tty,
+unsigned int cmd, unsigned long arg)
+{
+       struct snx_ser_state *state = NULL;
+       int ret = -ENOIOCTLCMD;
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line < SNX_SER_TOTAL_MAX)
+               state = tty->driver_data;
+
+
+       switch (cmd) {
+       case TIOCGSERIAL:
+       {
+               if (line < SNX_SER_TOTAL_MAX)
+                       ret = snx_ser_get_info(state,
+                               (struct serial_struct *)arg);
+
+               break;
+       }
+
+       case TIOCSSERIAL:
+       {
+               if (line < SNX_SER_TOTAL_MAX) {
+                       state->port->setserial_flag = SNX_SER_BAUD_SETSERIAL;
+                       ret = snx_ser_set_info(state,
+                               (struct serial_struct *)arg);
+               }
+               break;
+       }
+
+
+       case TCSETS:
+       {
+               if (line < SNX_SER_TOTAL_MAX) {
+                       state->port->flags &= ~(SNX_UPF_SPD_HI |
+                               SNX_UPF_SPD_VHI |
+                               SNX_UPF_SPD_SHI |
+                               SNX_UPF_SPD_WARP);
+                       state->port->setserial_flag = SNX_SER_BAUD_NOTSETSER;
+                       snx_ser_update_termios(state);
+               }
+               break;
+       }
+
+       case TIOCSERGWILD:
+       case TIOCSERSWILD:
+       {
+               if (line < SNX_SER_TOTAL_MAX)
+                       ret = 0;
+
+       break;
+       }
+
+       case SNX_SER_DUMP_PORT_INFO:
+       {
+               int i;
+               struct snx_ser_port_info snx_port_info[SNX_SER_TOTAL_MAX];
+               struct snx_ser_port *sdn = NULL;
+
+               memset(snx_port_info, 0,
+                       (sizeof(struct snx_ser_port_info) *
+                               SNX_SER_TOTAL_MAX));
+
+               if (line == 0) {
+                       for (i = 0; i < SNX_SER_TOTAL_MAX; i++) {
+                               sdn = (struct snx_ser_port *)
+                                       &sunix_ser_table[i];
+
+                               memcpy(&snx_port_info[i].board_name_info[0],
+                                       &sdn->pb_info.board_name[0],
+                                       SNX_BOARDNAME_LENGTH);
+
+                               snx_port_info[i].bus_number_info =
+                                       sdn->bus_number;
+                               snx_port_info[i].dev_number_info =
+                                       sdn->dev_number;
+                               snx_port_info[i].port_info       = sdn->line;
+                               snx_port_info[i].base_info       = sdn->iobase;
+                               snx_port_info[i].irq_info        = sdn->irq;
+                       }
+
+                       if (copy_to_user((void *)arg, snx_port_info,
+                       SNX_SER_TOTAL_MAX * sizeof(struct snx_ser_port_info)))
+                               ret = -EFAULT;
+                       else
+                               ret = 0;
+
+               } else {
+                       ret = 0;
+               }
+
+               ret = 0;
+               break;
+       }
+
+       case SNX_SER_DUMP_DRIVER_VER:
+       {
+               char driver_ver[SNX_DRIVERVERSION_LENGTH];
+
+               memset(driver_ver, 0,
+                       (sizeof(char) * SNX_DRIVERVERSION_LENGTH));
+
+               if (line == 0) {
+
+                       memcpy(&driver_ver[0], SNX_DRIVER_VERSION,
+                               sizeof(SNX_DRIVER_VERSION));
+
+                       if (copy_to_user((void *)arg, &driver_ver,
+                       (sizeof(char) * SNX_DRIVERVERSION_LENGTH)))
+                               ret = -EFAULT;
+                       else
+                               ret = 0;
+
+               } else {
+                       ret = 0;
+               }
+
+               break;
+       }
+
+       case SNX_COMM_GET_BOARD_CNT:
+       {
+               SNX_DRVR_BOARD_CNT gb;
+               int snx_board_count = 0;
+
+               memset(&gb, 0, (sizeof(SNX_DRVR_BOARD_CNT)));
+
+               gb.cnt = snx_board_count;
+
+               if (copy_to_user((void *)arg, &gb,
+               (sizeof(SNX_DRVR_BOARD_CNT))))
+                       ret = -EFAULT;
+               else
+                       ret = 0;
+
+
+               break;
+       }
+
+       case SNX_COMM_GET_BOARD_INFO:
+       {
+               struct sunix_board *sb = NULL;
+               SNX_DRVR_BOARD_INFO             board_info;
+
+               memset(&board_info, 0, (sizeof(SNX_DRVR_BOARD_INFO)));
+
+               if (copy_from_user(&board_info, (void *)arg,
+               (sizeof(SNX_DRVR_BOARD_INFO))))
+                       ret = -EFAULT;
+               else
+                       ret = 0;
+
+               sb = &sunix_board_table[board_info.board_id - 1];
+
+               board_info.subvender_id = sb->pb_info.sub_vendor_id;
+               board_info.subsystem_id = sb->pb_info.sub_device_id;
+               board_info.oem_id = sb->oem_id;
+               board_info.uart_cnt = sb->uart_cnt;
+               board_info.gpio_chl_cnt = sb->gpio_chl_cnt;
+               board_info.board_uart_type = sb->board_uart_type;
+               board_info.board_gpio_type = sb->board_gpio_type;
+
+               if (copy_to_user((void *)arg, &board_info,
+               (sizeof(SNX_DRVR_BOARD_INFO))))
+                       ret = -EFAULT;
+               else
+                       ret = 0;
+
+               break;
+       }
+
+       case SNX_UART_GET_TYPE:
+       {
+               struct sunix_board      *sb = NULL;
+               SNX_DRVR_UART_GET_TYPE gb;
+
+               int bar3_base_Address;
+               int bar1_base_Address;
+
+               int bar3_byte5;
+               int uart_type;
+               int     RS422state;
+               int     AHDCstate;
+
+               memset(&gb, 0, (sizeof(SNX_DRVR_UART_GET_TYPE)));
+
+               if (copy_from_user(&gb, (void *)arg,
+                       (sizeof(SNX_DRVR_UART_GET_TYPE))))
+                       ret = -EFAULT;
+               else
+                       ret = 0;
+
+               sb = &sunix_board_table[gb.board_id - 1];
+
+               bar3_base_Address = pci_resource_start(sb->pdev, 3);
+               bar1_base_Address = pci_resource_start(sb->pdev, 1);
+
+               bar3_byte5 = inb(bar3_base_Address + 5);
+               uart_type = (bar3_byte5 & 0xC0) >> 6;
+
+               if (gb.uart_num <= 4) {
+                       AHDCstate = inb(bar3_base_Address + 2)
+                       & 0x0F & (0x01 << ((gb.uart_num - 1) % 4));
+                       RS422state = inb(bar3_base_Address + 3)
+                       & 0xF0 & (0x10 << ((gb.uart_num - 1) % 4));
+               } else if (gb.uart_num <= 8) {
+                       AHDCstate = inb(bar1_base_Address + 0x32)
+                       & 0x0F & (0x01 << ((gb.uart_num - 1) % 4));
+                       RS422state = inb(bar1_base_Address + 0x33) &
+                       0xF0 & (0x10 << ((gb.uart_num - 1) % 4));
+               } else if (gb.uart_num <= 12) {
+                       AHDCstate = inb(bar1_base_Address
+                       + 0x32 + 0x40) & 0x0F &
+                       (0x01 << ((gb.uart_num - 1) % 4));
+                       RS422state = inb(bar1_base_Address +
+                       0x33 + 0x40) & 0xF0 &
+                       (0x10 << ((gb.uart_num - 1) % 4));
+               } else if (gb.uart_num <= 16) {
+                       AHDCstate = inb(bar1_base_Address +
+                       0x32 + 0x80) & 0x0F &
+                       (0x01 << ((gb.uart_num - 1) % 4));
+                       RS422state = inb(bar1_base_Address +
+                       0x33 + 0x80) & 0xF0 &
+                       (0x10 << ((gb.uart_num - 1) % 4));
+               } else {
+                       break;
+               }
+
+                       switch (uart_type) {
+                       case 0: // RS-232
+                       {
+                               gb.uart_type = 0;
+                               break;
+                       }
+                       case 1:
+                       {
+                               if (AHDCstate && RS422state)
+                                       gb.uart_type = 3;
+                               else if (AHDCstate && !RS422state)
+                                       gb.uart_type = 2;
+                               else if (!AHDCstate && RS422state)
+                                       gb.uart_type = 1;
+                               else
+                                       gb.uart_type = -1;
+
+                               break;
+                       }
+                       case 2:
+                       {
+                               if (AHDCstate && RS422state)
+                                       gb.uart_type = 3;
+                               else if (AHDCstate && !RS422state)
+                                       gb.uart_type = 2;
+                               else if (!AHDCstate && RS422state)
+                                       gb.uart_type = 1;
+                               else if (!AHDCstate && !RS422state)
+                                       gb.uart_type = 0;
+                               else
+                                       gb.uart_type = -1;
+
+                               break;
+                       }
+               }
+
+               if (copy_to_user((void *)arg, &gb,
+                       (sizeof(SNX_DRVR_UART_GET_TYPE)))) {
+                       ret = -EFAULT;
+               } else {
+                       ret = 0;
+               }
+
+       break;
+       }
+
+       case SNX_UART_SET_TYPE: {
+               struct sunix_board      *sb = NULL;
+               struct sunix_ser_port *sp = NULL;
+
+               SNX_DRVR_UART_SET_TYPE gb;
+
+               int targetConfigAddress = 0;
+               int bar3_byte5;
+               int uart_type;
+               int AHDCstate;
+               int iobase;
+               int ModemControl;
+               int GPIOcontrol;
+               unsigned char   RS422state = 0;
+               unsigned char   GPIOstate = 0;
+
+               int n = 0;
+
+               memset(&gb, 0, (sizeof(SNX_DRVR_UART_SET_TYPE)));
+
+               if (copy_from_user(&gb, (void *)arg,
+                       (sizeof(SNX_DRVR_UART_SET_TYPE)))) {
+                       ret = -EFAULT;
+               } else {
+                       ret = 0;
+               }
+
+               sb = &sunix_board_table[gb.board_id - 1];
+
+               n = (sb->ser_port_index - 1) + gb.uart_num;
+               sp = &sunix_ser_table[n];
+
+               bar3_byte5 = inb(sb->bar_addr[3] + 5);
+               uart_type  = (bar3_byte5 & 0xC0) >> 6;
+
+               if (gb.uart_num <= 4) {
+                       targetConfigAddress     = sb->bar_addr[3];
+               } else if (gb.uart_num <= 8) {
+                       targetConfigAddress     =
+                               (sb->bar_addr[1] + 0x30);
+               } else if (gb.uart_num <= 12) {
+                       targetConfigAddress     =
+                               (sb->bar_addr[1] + 0x30 + 0x40);
+               } else if (gb.uart_num <= 16) {
+                       targetConfigAddress     =
+                               (sb->bar_addr[1] + 0x30 + 0x80);
+               }
+
+               AHDCstate = inb(targetConfigAddress + 0x02);
+               RS422state = inb(targetConfigAddress + 0x03);
+               GPIOstate = inb(targetConfigAddress + 0x0C);
+               GPIOcontrol = inb(targetConfigAddress + 0x0D);
+
+               iobase = sp->port.iobase;
+
+               ModemControl = inb(iobase + UART_MCR);
+
+               AHDCstate &= ~(0x01 <<
+                       ((gb.uart_num - 1) % 4));
+               RS422state &= ~(0x10 <<
+                       ((gb.uart_num - 1) % 4));
+               GPIOstate &= ~(0x01 <<
+                       ((gb.uart_num - 1) % 4));
+               GPIOstate &= ~(0x10 <<
+                       ((gb.uart_num - 1) % 4));
+               GPIOcontrol &= ~(0x01 <<
+                       ((gb.uart_num - 1) % 4));
+
+               if (uart_type == 0) {
+                       if (gb.uart_type == 0) {
+                               AHDCstate &= ~(0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               RS422state &= ~(0x10 <<
+                                       ((gb.uart_num - 1) % 4));
+                               RS422state &= ~(0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOstate |= (0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOcontrol |= (0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+
+                               sp->port.AHDC_State = 0x00;
+                               sp->port.RS422_State = 0x00;
+                       }
+               } else if (uart_type == 1) {
+                       if (gb.uart_type == 3) {
+                               AHDCstate |= (0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               AHDCstate |= (0x10 <<
+                                       ((gb.uart_num - 1) % 4));
+                               RS422state |= (0x10 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOstate &= ~(0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOcontrol |= (0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+
+                               sp->port.AHDC_State = 0x01;
+                               sp->port.RS422_State = 0x01;
+                       } else if (gb.uart_type == 2) {
+                               AHDCstate |= (0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               AHDCstate |= (0x10 <<
+                                       ((gb.uart_num - 1) % 4));
+                               RS422state &= ~(0x10 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOstate &= ~(0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOcontrol |= (0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+
+                               // close DTR, close RTS
+                               outb((ModemControl & 0xFC) |
+                               0x03, iobase + UART_MCR);
+
+                               sp->port.AHDC_State = 0x01;
+                               sp->port.RS422_State = 0x00;
+                       } else if (gb.uart_type == 1) {
+                               AHDCstate |= (0x10 <<
+                                       ((gb.uart_num - 1) % 4));
+                               AHDCstate &= ~(0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               RS422state |= (0x10 <<
+                                       ((gb.uart_num - 1) % 4));
+                               RS422state &= ~(0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOstate &= ~(0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOcontrol |= (0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+
+                               outb((ModemControl & 0xFC) |
+                                       0x03, iobase + UART_MCR);
+
+                               sp->port.AHDC_State = 0x00;
+                               sp->port.RS422_State = 0x01;
+                       }
+               } else if (uart_type == 2) {
+                       if (gb.uart_type == 3) {
+                               AHDCstate |= (0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               RS422state |= (0x10 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOstate &= ~(0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOcontrol |= (0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+
+                               sp->port.AHDC_State = 0x01;
+                               sp->port.RS422_State = 0x01;
+                       } else if (gb.uart_type == 2) {
+                               AHDCstate |= (0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               RS422state &= ~(0x10 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOstate &= ~(0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOcontrol |= (0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+
+                               outb((ModemControl & 0xFC) |
+                               0x03, iobase + UART_MCR);
+
+                               sp->port.AHDC_State = 0x01;
+                               sp->port.RS422_State = 0x00;
+                       } else if (gb.uart_type == 1) {
+                               AHDCstate &= ~(0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               RS422state |= (0x10 <<
+                                       ((gb.uart_num - 1) % 4));
+                               RS422state &= ~(0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOstate &= ~(0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOcontrol |= (0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+
+
+                               outb((ModemControl & 0xFC) |
+                               0x03, iobase + UART_MCR);
+
+                               sp->port.AHDC_State = 0x00;
+                               sp->port.RS422_State = 0x01;
+                       } else if (gb.uart_type == 0) {
+                               AHDCstate &= ~(0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               RS422state &= ~(0x10 <<
+                                       ((gb.uart_num - 1) % 4));
+                               RS422state &= ~(0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOstate |= (0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+                               GPIOcontrol |= (0x01 <<
+                                       ((gb.uart_num - 1) % 4));
+
+                               sp->port.AHDC_State = 0x00;
+                               sp->port.RS422_State = 0x00;
+                       }
+               }
+
+               outb(AHDCstate,
+                       targetConfigAddress + 0x02);
+               outb(RS422state,
+                       targetConfigAddress + 0x03);
+               outb(GPIOstate,
+                       targetConfigAddress + 0x0C);
+               outb(GPIOcontrol,
+                       targetConfigAddress + 0x0D);
+
+               EEPROMWriteData(targetConfigAddress,
+               0x13, AHDCstate);
+               EEPROMWriteData(targetConfigAddress,
+               0x14, RS422state);
+               EEPROMWriteData(targetConfigAddress,
+               0x19, ~GPIOcontrol);
+               EEPROMWriteData(targetConfigAddress,
+               0x1A, GPIOstate);
+
+               break;
+       }
+
+       case SNX_UART_GET_ACS:
+       {
+               SNX_DRVR_UART_GET_ACS gb;
+               struct sunix_board      *sb = NULL;
+               int     ACSstate = 0;
+
+               memset(&gb, 0, sizeof(SNX_DRVR_UART_GET_ACS));
+
+               if (copy_from_user(&gb, (void *)arg,
+                       (sizeof(SNX_DRVR_UART_GET_ACS))))
+                       ret = -EFAULT;
+               else
+                       ret = 0;
+
+               sb = &sunix_board_table[gb.board_id - 1];
+
+               if (gb.uart_num <= 4) {
+                       ACSstate = inb(sb->bar_addr[3] +
+                       3) & 0x0F &
+                       (0x01 << ((gb.uart_num - 1) % 4));
+               } else if (gb.uart_num <= 8) {
+                       ACSstate = inb(sb->bar_addr[1] +
+                       0x33) & 0x0F &
+                       (0x01 << ((gb.uart_num - 1) % 4));
+               } else if (gb.uart_num <= 12) {
+                       ACSstate = inb(sb->bar_addr[1] +
+                       0x33 + 0x40) & 0x0F &
+                       (0x01 << ((gb.uart_num - 1) % 4));
+               } else if (gb.uart_num <= 16) {
+                       ACSstate = inb(sb->bar_addr[1] +
+                       0x33 + 0x80) & 0x0F &
+                       (0x01 << ((gb.uart_num - 1) % 4));
+               }
+
+               if (ACSstate)
+                       gb.uart_acs = 1;
+               else
+                       gb.uart_acs = 0;
+
+               if (copy_to_user((void *)arg, &gb,
+                       (sizeof(SNX_DRVR_UART_GET_ACS))))
+                       ret = -EFAULT;
+               else
+                       ret = 0;
+
+               break;
+       }
+
+       case SNX_UART_SET_ACS:
+       {
+               SNX_DRVR_UART_SET_ACS gb;
+               struct sunix_board      *sb = NULL;
+               int ACSstate = 0;
+               int targetConfigAddress = 0;
+
+               memset(&gb, 0, sizeof(SNX_DRVR_UART_SET_ACS));
+
+               if (copy_from_user(&gb, (void *)arg,
+                       (sizeof(SNX_DRVR_UART_SET_ACS))))
+                       ret = -EFAULT;
+               else
+                       ret = 0;
+
+               sb = &sunix_board_table[gb.board_id - 1];
+
+               if (gb.uart_num <= 4) {
+                       targetConfigAddress     =
+                       sb->bar_addr[3];
+               } else if (gb.uart_num <= 8) {
+                       targetConfigAddress     =
+                       (sb->bar_addr[1] + 0x30);
+               } else if (gb.uart_num <= 12) {
+                       targetConfigAddress     =
+                       (sb->bar_addr[1] + 0x30 + 0x40);
+               } else if (gb.uart_num <= 16) {
+                       targetConfigAddress     =
+                       (sb->bar_addr[1] + 0x30 + 0x80);
+               }
+                       ACSstate = inb(targetConfigAddress + 0x03);
+
+                       ACSstate &= ~(0x01 << ((gb.uart_num - 1) % 4));
+
+                       if (gb.uart_acs == 1) {
+                               ACSstate |=     (0x01 <<
+                               ((gb.uart_num - 1) % 4));
+                       }
+
+                       outb(ACSstate, targetConfigAddress + 0x03);
+
+                       EEPROMWriteData(targetConfigAddress,
+                       0x14, ACSstate);
+
+               break;
+       }
+       }
+
+       if (ret != -ENOIOCTLCMD)
+               goto out;
+
+
+       if (tty->flags & (1 << TTY_IO_ERROR)) {
+               ret = -EIO;
+               goto out;
+       }
+
+       switch (cmd) {
+       case TIOCMIWAIT:
+               if (line < SNX_SER_TOTAL_MAX)
+                       ret = snx_ser_wait_modem_status(state, arg);
+
+       break;
+
+       case TIOCGICOUNT:
+               if (line < SNX_SER_TOTAL_MAX) {
+                       ret = snx_ser_get_count(state,
+                               (struct serial_icounter_struct *)arg);
+               }
+               break;
+               }
+
+       if (ret != -ENOIOCTLCMD)
+               goto out;
+
+       if (line < SNX_SER_TOTAL_MAX) {
+               down(&state->sem);
+
+               switch (cmd) {
+               case TIOCSERGETLSR:
+                       ret = snx_ser_get_lsr_info(state, (unsigned int *)arg);
+                       break;
+
+               default:
+               {
+                       break;
+               }
+               }
+
+               up(&state->sem);
+       }
+
+out:
+       return ret;
+}
+
+
+static void snx_ser_hangup(struct tty_struct *tty)
+{
+       struct snx_ser_state *state = NULL;
+       struct tty_port *tport = &state->tport;
+
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return;
+
+       state = tty->driver_data;
+
+       tport = &state->tport;
+
+       down(&state->sem);
+
+       if (state->info && state->info->flags & SNX_UIF_NORMAL_ACTIVE) {
+               snx_ser_flush_buffer(tty);
+               snx_ser_shutdown(state);
+               state->count = 0;
+               state->info->flags &= ~SNX_UIF_NORMAL_ACTIVE;
+
+               tty_port_tty_set(tport, NULL);
+
+               state->info->tty = NULL;
+               wake_up_interruptible(&state->info->open_wait);
+               wake_up_interruptible(&state->info->delta_msr_wait);
+       }
+
+       up(&state->sem);
+}
+
+
+static unsigned int snx_ser_get_divisor(struct snx_ser_port *port,
+unsigned int baud)
+{
+       unsigned int quot;
+
+       if (baud == 38400 && (port->flags & SNX_UPF_SPD_MASK) ==
+               SNX_UPF_SPD_CUST)
+               quot = port->custom_divisor;
+       else
+               quot = port->uartclk / (16 * baud);
+
+       return quot;
+}
+
+
+unsigned int snx_ser_get_baud_rate(struct snx_ser_port *port,
+struct SNXTERMIOS *termios, struct SNXTERMIOS *old,
+unsigned int min, unsigned int max)
+{
+       unsigned int try;
+       unsigned int baud;
+       unsigned int altbaud = 0;
+       unsigned int flags = port->flags & SNX_UPF_SPD_MASK;
+
+       for (try = 0; try < 2; try++) {
+               if ((port->setserial_flag == SNX_SER_BAUD_SETSERIAL) ||
+                       (port->flags & SNX_UPF_SPD_MASK)) {
+                       altbaud = 38400;
+
+                       if (flags == SNX_UPF_SPD_HI)
+                               altbaud = 57600;
+
+                       if (flags == SNX_UPF_SPD_VHI)
+                               altbaud = 115200;
+
+                       if (flags == SNX_UPF_SPD_SHI)
+                               altbaud = 230400;
+
+                       if (flags == SNX_UPF_SPD_WARP)
+                               altbaud = 460800;
+
+                       baud = altbaud;
+               } else {
+                       switch (termios->c_cflag & (CBAUD | CBAUDEX)) {
+                       case B921600:
+                       baud = 921600;
+                       break;
+
+                       case B460800:
+                       baud = 460800;
+                       break;
+
+                       case B230400:
+                       baud = 230400;
+                       break;
+
+                       case B115200:
+                       baud = 115200;
+                       break;
+
+                       case B57600:
+                       baud = 57600;
+                       break;
+
+                       case B38400:
+                       baud = 38400;
+                       break;
+
+                       case B19200:
+                       baud = 19200;
+                       break;
+
+                       case B9600:
+                       baud = 9600;
+                       break;
+
+                       case B4800:
+                       baud = 4800;
+                       break;
+
+                       case B2400:
+                       baud = 2400;
+                       break;
+
+                       case B1800:
+                       baud = 1800;
+                       break;
+
+                       case B1200:
+                       baud = 1200;
+                       break;
+
+                       case B600:
+                       baud = 600;
+                       break;
+
+                       case B300:
+                       baud = 300;
+                       break;
+
+                       case B200:
+                       baud = 200;
+                       break;
+
+                       case B150:
+                       baud = 150;
+                       break;
+
+                       case B134:
+                       baud = 134;
+                       break;
+
+                       case B110:
+                       baud = 110;
+                       break;
+
+                       case B75:
+                       baud = 75;
+                       break;
+
+                       case B50:
+                       baud = 50;
+                       break;
+
+                       default:
+                       baud = 9600;
+                       break;
+                       }
+               }
+
+               if (baud == 0)
+                       baud = 9600;
+
+               if (baud >= min && baud <= max)
+                       return baud;
+
+               termios->c_cflag &= ~CBAUD;
+
+               if (old) {
+                       termios->c_cflag |= old->c_cflag & CBAUD;
+                       old = NULL;
+                       continue;
+               }
+
+               termios->c_cflag |= B9600;
+       }
+
+       return 0;
+}
+
+
+extern void snx_ser_change_speed(struct snx_ser_state *state,
+struct SNXTERMIOS *old_termios)
+{
+       struct tty_struct *tty = state->info->tty;
+       struct snx_ser_port *port = state->port;
+       struct SNXTERMIOS *termios;
+
+       if (!tty || port->type == PORT_UNKNOWN)
+               return;
+
+       termios = &tty->termios;
+
+       if (termios->c_cflag & CRTSCTS)
+               state->info->flags |= SNX_UIF_CTS_FLOW;
+       else
+               state->info->flags &= ~SNX_UIF_CTS_FLOW;
+
+       if (termios->c_cflag & CLOCAL)
+               state->info->flags &= ~SNX_UIF_CHECK_CD;
+       else
+               state->info->flags |= SNX_UIF_CHECK_CD;
+
+       sunix_ser_set_termios(port, termios, old_termios);
+}
+
+
+static void snx_ser_set_termios(struct tty_struct *tty,
+struct SNXTERMIOS *old_termios)
+{
+       struct snx_ser_state *state = NULL;
+       unsigned long flags;
+       unsigned int cflag = tty->termios.c_cflag;
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return;
+
+       state = tty->driver_data;
+
+       if ((cflag ^ old_termios->c_cflag) == 0 && RELEVANT_IFLAG(
+               tty->termios.c_iflag ^ old_termios->c_iflag) == 0) {
+               return;
+       }
+
+       snx_ser_change_speed(state, old_termios);
+
+       if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
+               snx_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR);
+
+       if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
+               unsigned int mask = TIOCM_DTR;
+
+               if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags))
+                       mask |= TIOCM_RTS;
+
+               snx_set_mctrl(state->port, mask);
+       }
+
+       if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
+               spin_lock_irqsave(&state->port->lock, flags);
+               tty->hw_stopped = 0;
+               __snx_ser_start(tty);
+               spin_unlock_irqrestore(&state->port->lock, flags);
+       }
+}
+
+extern void snx_ser_update_termios(struct snx_ser_state *state)
+{
+       struct tty_struct *tty = state->info->tty;
+       struct snx_ser_port *port = state->port;
+
+       if (!(tty->flags & (1 << TTY_IO_ERROR))) {
+               snx_ser_change_speed(state, NULL);
+
+               if (tty->termios.c_cflag & CBAUD)
+                       snx_set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+       }
+}
+
+
+static void snx_ser_update_timeout(struct snx_ser_port *port,
+unsigned int cflag, unsigned int baud)
+{
+       unsigned int bits;
+
+       switch (cflag & CSIZE) {
+       case CS5:
+               bits = 7;
+               break;
+
+       case CS6:
+               bits = 8;
+               break;
+
+       case CS7:
+               bits = 9;
+               break;
+
+       default:
+               bits = 10;
+               break;
+       }
+
+       if (cflag & CSTOPB)
+               bits++;
+
+       if (cflag & PARENB)
+               bits++;
+
+       bits = bits * port->fifosize;
+
+       port->timeout = (HZ * bits) / baud + HZ/50;
+}
+
+
+static struct snx_ser_state *snx_ser_get(struct snx_ser_driver *drv, int line)
+{
+       struct snx_ser_state *state = NULL;
+
+       down(&ser_port_sem);
+
+       state = drv->state + line;
+
+       if (down_interruptible(&state->sem)) {
+               state = ERR_PTR(-ERESTARTSYS);
+               goto out;
+       }
+
+       state->count++;
+
+       if (!state->port) {
+               state->count--;
+               up(&state->sem);
+               state = ERR_PTR(-ENXIO);
+               goto out;
+       }
+
+       if (!state->port->iobase) {
+               state->count--;
+               up(&state->sem);
+               state = ERR_PTR(-ENXIO);
+               goto out;
+       }
+
+       if (!state->info) {
+               state->info = kmalloc(sizeof(struct snx_ser_info), GFP_KERNEL);
+
+               if (state->info) {
+                       memset(state->info, 0, sizeof(struct snx_ser_info));
+                       init_waitqueue_head(&state->info->open_wait);
+                       init_waitqueue_head(&state->info->delta_msr_wait);
+
+                       state->port->info = state->info;
+
+                       //tasklet_init(&state->info->tlet,
+                       //snx_ser_tasklet_action, (unsigned long)state);
+               } else {
+                       state->count--;
+                       up(&state->sem);
+                       state = ERR_PTR(-ENOMEM);
+               }
+       }
+
+out:
+       up(&ser_port_sem);
+
+       return state;
+}
+
+
+static int snx_ser_block_til_ready(struct file *filp,
+struct snx_ser_state *state)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       struct snx_ser_info *info = state->info;
+       struct snx_ser_port *port = state->port;
+
+       info->blocked_open++;
+       state->count--;
+
+       add_wait_queue(&info->open_wait, &wait);
+
+       while (1) {
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               if (tty_hung_up_p(filp) || info->tty == NULL)
+                       break;
+
+               if (!(info->flags & SNX_UIF_INITIALIZED))
+                       break;
+
+               if ((filp->f_flags & O_NONBLOCK) ||
+                       (info->tty->termios.c_cflag & CLOCAL) ||
+                       (info->tty->flags & (1 << TTY_IO_ERROR))) {
+                       break;
+               }
+
+               if (info->tty->termios.c_cflag & CBAUD)
+                       snx_set_mctrl(port, TIOCM_DTR);
+
+               if (sunix_ser_get_mctrl(port) & TIOCM_CAR)
+                       break;
+
+               up(&state->sem);
+               schedule();
+               down(&state->sem);
+
+               if (signal_pending(current))
+                       break;
+       }
+
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&info->open_wait, &wait);
+
+       state->count++;
+       info->blocked_open--;
+
+       if (signal_pending(current))
+               return -ERESTARTSYS;
+
+       if (!info->tty || tty_hung_up_p(filp))
+               return -EAGAIN;
+
+       return 0;
+}
+
+
+static void snx_ser_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+       struct snx_ser_state *state = NULL;
+       struct snx_ser_port *port = NULL;
+       unsigned long char_time;
+       unsigned long expire;
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line >= SNX_SER_TOTAL_MAX)
+               return;
+
+       state = tty->driver_data;
+       port = state->port;
+
+       if (port->type == PORT_UNKNOWN || port->fifosize == 0)
+               return;
+
+       char_time = (port->timeout - HZ/50) / port->fifosize;
+
+       char_time = char_time / 5;
+
+       if (char_time == 0)
+               char_time = 1;
+
+       if (timeout && timeout < char_time)
+               char_time = timeout;
+
+       if (timeout == 0 || timeout > 2 * port->timeout)
+               timeout = 2 * port->timeout;
+
+       expire = jiffies + timeout;
+
+       while (!sunix_ser_tx_empty(port)) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(char_time);
+
+               if (signal_pending(current))
+                       break;
+
+               if (time_after(jiffies, expire))
+                       break;
+       }
+       set_current_state(TASK_RUNNING);
+}
+
+
+static int snx_ser_open(struct tty_struct *tty, struct file *filp)
+{
+       struct snx_ser_driver *drv =
+       (struct snx_ser_driver *)tty->driver->driver_state;
+
+       struct snx_ser_state *state = NULL;
+       struct tty_port *tport = NULL;
+
+       int retval = 0;
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line < SNX_SER_TOTAL_MAX) {
+               retval = -ENODEV;
+
+               if (line >= SNX_SER_TOTAL_MAX)
+                       goto fail;
+
+               state = snx_ser_get(drv, line);
+
+               tport = &state->tport;
+
+               if (IS_ERR(state)) {
+                       retval = PTR_ERR(state);
+                       goto fail;
+               }
+
+               if (!state)
+                       goto fail;
+
+
+               state->port->suspended = 1;
+               tty->driver_data = state;
+
+               tport->low_latency = (state->port->flags &
+               SNX_UPF_LOW_LATENCY) ? 1 : 0;
+
+               state->info->tty = tty;
+
+               tty_port_tty_set(tport, tty);
+
+               if (tty_hung_up_p(filp)) {
+                       retval = -EAGAIN;
+                       state->count--;
+                       up(&state->sem);
+                       goto fail;
+               }
+
+               retval = snx_ser_startup(state, 0);
+
+               if (retval == 0)
+                       retval = snx_ser_block_til_ready(filp, state);
+
+               up(&state->sem);
+
+               if (retval == 0 && !(state->info->flags &
+                       SNX_UIF_NORMAL_ACTIVE)) {
+                       state->info->flags |= SNX_UIF_NORMAL_ACTIVE;
+
+                       snx_ser_update_termios(state);
+               }
+
+       } else {
+       }
+
+fail:
+
+       return retval;
+}
+
+
+static void snx_ser_close(struct tty_struct *tty, struct file *filp)
+{
+       struct snx_ser_state *state = tty->driver_data;
+       struct tty_port *tport;
+       struct snx_ser_port *port = NULL;
+       int line = SNX_SER_DEVNUM(tty);
+
+       if (line < SNX_SER_TOTAL_MAX) {
+               if (!state || !state->port)
+                       return;
+
+               port = state->port;
+
+               tport = &state->tport;
+
+               down(&state->sem);
+
+               if (tty_hung_up_p(filp))
+                       goto done;
+
+               if ((tty->count == 1) && (state->count != 1)) {
+                       pr_info("SNX Info : tty->count is 1, state->count is 
%d\n",
+                       state->count);
+                       state->count = 1;
+               }
+
+               if (--state->count < 0) {
+                       pr_info("SNX Info : bad serial port count : ttySNX%d: 
%d\n",
+                       port->line, state->count);
+                       state->count = 0;
+               }
+
+               if (state->count)
+                       goto done;
+
+               tty->closing = 1;
+
+               port->suspended = 0;
+               if (state->closing_wait != SNX_USF_CLOSING_WAIT_NONE)
+                       tty_wait_until_sent(tty, state->closing_wait);
+
+               if (state->info->flags & SNX_UIF_INITIALIZED) {
+                       unsigned long flags;
+
+                       spin_lock_irqsave(&port->lock, flags);
+                       sunix_ser_stop_rx(port);
+                       spin_unlock_irqrestore(&port->lock, flags);
+
+                       snx_ser_wait_until_sent(tty, port->timeout);
+               }
+
+               snx_ser_shutdown(state);
+               snx_ser_flush_buffer(tty);
+
+               if (tty->ldisc->ops->flush_buffer)
+                       tty->ldisc->ops->flush_buffer(tty);
+
+               tty_port_tty_set(tport, NULL);
+
+               tty->closing = 0;
+               state->info->tty = NULL;
+
+               if (state->info->blocked_open) {
+                       if (state->close_delay) {
+                               set_current_state(TASK_INTERRUPTIBLE);
+                               schedule_timeout(state->close_delay);
+                       }
+               }
+
+               state->info->flags &= ~SNX_UIF_NORMAL_ACTIVE;
+               wake_up_interruptible(&state->info->open_wait);
+
+done:
+               up(&state->sem);
+
+
+       } else {
+       }
+}
+
+
+static void sunix_ser_set_mctrl(struct snx_ser_port *port,
+unsigned int mctrl)
+{
+       struct sunix_ser_port *sp = (struct sunix_ser_port *)port;
+       unsigned char mcr = 0;
+
+       if (sp->port.pb_info.sub_vendor_id == SUBVENID_SUN1999) {
+               if ((sp->port.AHDC_State  == 0) &&
+                       (sp->port.RS422_State == 1)) {
+                       if (mctrl & TIOCM_OUT1)
+                               mcr |= UART_MCR_OUT1;
+
+                       if (mctrl & TIOCM_OUT2)
+                               mcr |= UART_MCR_OUT2;
+
+                       if (mctrl & TIOCM_LOOP)
+                               mcr |= UART_MCR_LOOP;
+
+                       mcr = (mcr & sp->mcr_mask) | sp->mcr_force | sp->mcr;
+               } else if ((sp->port.AHDC_State == 1) &&
+                       (sp->port.RS422_State == 0)) {
+
+                       if (mctrl & TIOCM_OUT1)
+                               mcr |= UART_MCR_OUT1;
+
+                       if (mctrl & TIOCM_OUT2)
+                               mcr |= UART_MCR_OUT2;
+
+                       if (mctrl & TIOCM_LOOP)
+                               mcr |= UART_MCR_LOOP;
+
+                       mcr = (mcr & sp->mcr_mask) | sp->mcr_force | sp->mcr;
+
+               } else {
+                       if (mctrl & TIOCM_RTS)
+                               mcr |= UART_MCR_RTS;
+
+                       if (mctrl & TIOCM_DTR)
+                               mcr |= UART_MCR_DTR;
+
+                       if (mctrl & TIOCM_OUT1)
+                               mcr |= UART_MCR_OUT1;
+
+                       if (mctrl & TIOCM_OUT2)
+                               mcr |= UART_MCR_OUT2;
+
+                       if (mctrl & TIOCM_LOOP)
+                               mcr |= UART_MCR_LOOP;
+
+                       mcr = (mcr & sp->mcr_mask) | sp->mcr_force | sp->mcr;
+               }
+       } else {
+               if (mctrl & TIOCM_RTS)
+                       mcr |= UART_MCR_RTS;
+
+               if (mctrl & TIOCM_DTR)
+                       mcr |= UART_MCR_DTR;
+
+               if (mctrl & TIOCM_OUT1)
+                       mcr |= UART_MCR_OUT1;
+
+               if (mctrl & TIOCM_OUT2)
+                       mcr |= UART_MCR_OUT2;
+
+               if (mctrl & TIOCM_LOOP)
+                       mcr |= UART_MCR_LOOP;
+
+               mcr = (mcr & sp->mcr_mask) | sp->mcr_force | sp->mcr;
+       }
+
+       write_uart_mcr(sp, mcr);
+}
+
+
+static unsigned int sunix_ser_tx_empty(struct snx_ser_port *port)
+{
+       struct sunix_ser_port *sp = (struct sunix_ser_port *)port;
+       unsigned long flags;
+       unsigned int ret;
+
+       spin_lock_irqsave(&sp->port.lock, flags);
+       ret = read_uart_lsr(sp) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+       spin_unlock_irqrestore(&sp->port.lock, flags);
+
+       return ret;
+}
+
+
+static unsigned int sunix_ser_get_mctrl(struct snx_ser_port *port)
+{
+       struct sunix_ser_port *sp = (struct sunix_ser_port *)port;
+       unsigned long flags;
+       unsigned char status;
+       unsigned int ret;
+
+       ret = 0;
+
+       spin_lock_irqsave(&sp->port.lock, flags);
+       status = read_uart_msr(sp);
+       spin_unlock_irqrestore(&sp->port.lock, flags);
+
+       if (sp->port.pb_info.sub_vendor_id == SUBVENID_SUN1999) {
+               if ((sp->port.AHDC_State  == 0) &&
+                       (sp->port.RS422_State == 1)) {
+                       //RS422 mode : bypass DCD RI DSR CTS
+               } else if ((sp->port.AHDC_State == 1) &&
+                       (sp->port.RS422_State == 0)) {
+                       //RS485 mode : bypass DCD RI DSR CTS
+               } else {
+                       if (status & UART_MSR_DCD)
+                               ret |= TIOCM_CAR;
+
+                       if (status & UART_MSR_RI)
+                               ret |= TIOCM_RNG;
+
+                       if (status & UART_MSR_DSR)
+                               ret |= TIOCM_DSR;
+
+                       if (status & UART_MSR_CTS)
+                               ret |= TIOCM_CTS;
+               }
+
+       } else {
+               ret = 0;
+               if (status & UART_MSR_DCD)
+                       ret |= TIOCM_CAR;
+
+               if (status & UART_MSR_RI)
+                       ret |= TIOCM_RNG;
+
+               if (status & UART_MSR_DSR)
+                       ret |= TIOCM_DSR;
+
+               if (status & UART_MSR_CTS)
+                       ret |= TIOCM_CTS;
+       }
+       return ret;
+}
+
+
+static void sunix_ser_stop_tx(struct snx_ser_port *port, unsigned int tty_stop)
+{
+       struct sunix_ser_port *sp = (struct sunix_ser_port *)port;
+
+
+       if (sp->ier & UART_IER_THRI) {
+               sp->ier &= ~UART_IER_THRI;
+               write_uart_ier(sp, sp->ier);
+       }
+}
+
+
+static void sunix_ser_start_tx(struct snx_ser_port *port,
+unsigned int tty_start)
+{
+       struct sunix_ser_port *sp = (struct sunix_ser_port *)port;
+
+       if (!(sp->ier & UART_IER_THRI)) {
+               sp->ier |= UART_IER_THRI;
+               write_uart_ier(sp, sp->ier);
+       }
+}
+
+
+static void sunix_ser_stop_rx(struct snx_ser_port *port)
+{
+       struct sunix_ser_port *sp = (struct sunix_ser_port *)port;
+
+       sp->ier &= ~UART_IER_RLSI;
+       sp->port.read_status_mask &= ~UART_LSR_DR;
+       write_uart_ier(sp, sp->ier);
+}
+
+
+static void sunix_ser_enable_ms(struct snx_ser_port *port)
+{
+       struct sunix_ser_port *sp = (struct sunix_ser_port *)port;
+
+       sp->ier |= UART_IER_MSI;
+       write_uart_ier(sp, sp->ier);
+}
+
+
+static void sunix_ser_break_ctl(struct snx_ser_port *port, int break_state)
+{
+       struct sunix_ser_port *sp = (struct sunix_ser_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sp->port.lock, flags);
+
+       if (break_state == -1)
+               sp->lcr |= UART_LCR_SBC;
+       else
+               sp->lcr &= ~UART_LCR_SBC;
+
+       write_uart_lcr(sp, sp->lcr);
+       spin_unlock_irqrestore(&sp->port.lock, flags);
+}
+
+
+static int sunix_ser_startup(struct snx_ser_port *port)
+{
+       struct sunix_ser_port *sp = (struct sunix_ser_port *)port;
+
+       sp->capabilities = snx_uart_config[sp->port.type].flags;
+       sp->mcr = 0;
+
+       if (sp->capabilities & UART_CLEAR_FIFO) {
+               write_uart_fcr(sp, UART_FCR_ENABLE_FIFO);
+               write_uart_fcr(sp, UART_FCR_ENABLE_FIFO |
+               UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+               write_uart_fcr(sp, 0);
+       }
+
+       (void) read_uart_lsr(sp);
+       (void) read_uart_rx(sp);
+       (void) read_uart_iir(sp);
+       (void) read_uart_msr(sp);
+
+       if (!(sp->port.flags & SNX_UPF_BUGGY_UART) &&
+               (read_uart_lsr(sp) == 0xff)) {
+               pr_err("SNX Info : ttySNX%d: LSR safety check engaged!\n",
+               sp->port.line);
+               return -ENODEV;
+       }
+
+       write_uart_lcr(sp, UART_LCR_WLEN8);
+
+       if (sp->port.snx_type == SNX_SER_PORT_SUN1699)
+               sp->ier = UART_IER_RLSI | UART_IER_RDI |
+               SUN1699_CLK_DIVIDER_DISABLE;
+       else
+               sp->ier = UART_IER_RLSI | UART_IER_RDI;
+
+
+       write_uart_ier(sp, sp->ier);
+
+       (void) read_uart_lsr(sp);
+       (void) read_uart_rx(sp);
+       (void) read_uart_iir(sp);
+       (void) read_uart_msr(sp);
+
+       return 0;
+}
+
+
+static void sunix_ser_shutdown(struct snx_ser_port *port)
+{
+       struct sunix_ser_port *sp = (struct sunix_ser_port *)port;
+
+       sp->ier = 0;
+       write_uart_ier(sp, 0);
+
+       write_uart_lcr(sp, read_uart_lcr(sp) & ~UART_LCR_SBC);
+
+       write_uart_fcr(sp, UART_FCR_ENABLE_FIFO |
+       UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+       write_uart_fcr(sp, 0);
+
+       (void) read_uart_rx(sp);
+}
+
+
+static unsigned int sunix_ser_get_divisor(struct snx_ser_port *port,
+unsigned int baud)
+{
+       unsigned int quot;
+
+       if ((port->flags & SNX_UPF_MAGIC_MULTIPLIER) &&
+               baud == (port->uartclk/4)) {
+               quot = 0x8001;
+       } else if ((port->flags & SNX_UPF_MAGIC_MULTIPLIER) &&
+                               baud == (port->uartclk/8)) {
+               quot = 0x8002;
+       } else {
+               quot = snx_ser_get_divisor(port, baud);
+       }
+
+       return quot;
+}
+
+
+static void sunix_ser_set_termios(struct snx_ser_port *port,
+struct SNXTERMIOS *termios, struct SNXTERMIOS *old)
+{
+       struct sunix_ser_port *sp = (struct sunix_ser_port *)port;
+       unsigned char cval;
+       unsigned char fcr = 0;
+       unsigned long flags;
+       unsigned int baud;
+       unsigned int quot;
+
+               switch (termios->c_cflag & CSIZE) {
+               case CS5:
+                       cval = 0x00;
+                       break;
+
+               case CS6:
+                       cval = 0x01;
+                       break;
+
+               case CS7:
+                       cval = 0x02;
+                       break;
+
+               default:
+               case CS8:
+                       cval = 0x03;
+                       break;
+               }
+
+       if (termios->c_cflag & CSTOPB)
+               cval |= 0x04;
+
+       if (termios->c_cflag & PARENB)
+               cval |= UART_LCR_PARITY;
+
+       if (!(termios->c_cflag & PARODD))
+               cval |= UART_LCR_EPAR;
+
+#ifdef CMSPAR
+       if (termios->c_cflag & CMSPAR)
+               cval |= UART_LCR_SPAR;
+
+#endif
+
+       baud = snx_ser_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+       quot = sunix_ser_get_divisor(port, baud);
+
+       if (sp->capabilities & UART_USE_FIFO) {
+               if (baud < 2400)
+                       fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+               else
+                       fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+
+       }
+
+
+       if (sp->port.snx_type == SNX_SER_PORT_SUN1889) {
+               sp->mcr &= ~UART_MCR_AFE;
+
+               if (termios->c_cflag & CRTSCTS)
+                       sp->mcr |= UART_MCR_AFE;
+
+               fcr |= UART_SUN1889_FCR_32BYTE;
+       } else if (sp->port.snx_type == SNX_SER_PORT_SUN1699) {
+               sp->mcr &= ~UART_MCR_AFE;
+
+               if (termios->c_cflag & CRTSCTS)
+                       sp->mcr |= UART_MCR_AFE;
+
+               fcr |= UART_SUN1699_FCR_32BYTE;
+       } else if (sp->port.snx_type == SNX_SER_PORT_SUNMATX) {
+               sp->mcr &= ~UART_MCR_AFE;
+
+               if (termios->c_cflag & CRTSCTS)
+                       sp->mcr |= UART_MCR_AFE;
+
+               fcr |= UART_SUNMATX_FCR_64BYTE;
+       } else if (sp->port.snx_type == SNX_SER_PORT_SUN1999) {
+               sp->mcr &= ~UART_MCR_AFE;
+
+               if (termios->c_cflag & CRTSCTS)
+                       sp->mcr |= UART_MCR_AFE;
+
+               fcr |= UART_SUN1999_FCR_128BYTE;
+       } else {
+               sp->mcr &= ~UART_MCR_AFE;
+
+               if (termios->c_cflag & CRTSCTS)
+                       sp->mcr |= UART_MCR_AFE;
+
+               fcr |= UART_DEFAULT_FCR;
+       }
+
+       spin_lock_irqsave(&sp->port.lock, flags);
+
+
+       snx_ser_update_timeout(port, termios->c_cflag, baud);
+
+
+       sp->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+
+       if (termios->c_iflag & INPCK)
+               sp->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+
+       if (termios->c_iflag & (BRKINT | PARMRK))
+               sp->port.read_status_mask |= UART_LSR_BI;
+
+       sp->port.ignore_status_mask = 0;
+
+       if (termios->c_iflag & IGNPAR)
+               sp->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+
+       if (termios->c_iflag & IGNBRK) {
+               sp->port.ignore_status_mask |= UART_LSR_BI;
+
+               if (termios->c_iflag & IGNPAR)
+                       sp->port.ignore_status_mask |= UART_LSR_OE;
+       }
+
+       if ((termios->c_cflag & CREAD) == 0)
+               sp->port.ignore_status_mask |= UART_LSR_DR;
+
+       sp->ier &= ~UART_IER_MSI;
+       if (SNX_ENABLE_MS(&sp->port, termios->c_cflag))
+               sp->ier |= UART_IER_MSI;
+
+
+       write_uart_lcr(sp, cval | UART_LCR_DLAB);
+
+       write_uart_dll(sp, quot & 0xff);
+       write_uart_dlm(sp, quot >> 8);
+
+       write_uart_fcr(sp, fcr);
+
+       write_uart_lcr(sp, cval);
+
+       sp->lcr = cval;
+
+       sunix_ser_set_mctrl(&sp->port, sp->port.mctrl);
+
+       write_uart_ier(sp, sp->ier);
+
+       spin_unlock_irqrestore(&sp->port.lock, flags);
+}
+
+
+static void sunix_ser_timeout(struct timer_list *t)
+{
+       struct sunix_ser_port *sp = from_timer(sp, t, timer);
+
+       unsigned int timeout;
+       unsigned int iir;
+
+       iir = read_uart_iir(sp);
+
+       if (!(iir & UART_IIR_NO_INT)) {
+               spin_lock(&sp->port.lock);
+               sunix_ser_handle_port(sp, iir);
+               spin_unlock(&sp->port.lock);
+       }
+
+       timeout = sp->port.timeout;
+       timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
+
+       mod_timer(&sp->timer, jiffies + timeout);
+}
+
+
+static void sunix_ser_receive_chars(
+struct sunix_ser_port *sp, unsigned char *status)
+{
+       struct tty_struct *tty = sp->port.info->tty;
+       unsigned char ch;
+       int max_count = 256;
+       unsigned char lsr = *status;
+       char flag;
+
+       struct snx_ser_state *state = NULL;
+       struct tty_port *tport = NULL;
+
+       state = tty->driver_data;
+       tport = &state->tport;
+
+       do {
+               ch = read_uart_rx(sp);
+               flag = TTY_NORMAL;
+               sp->port.icount.rx++;
+
+               if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE |
+               UART_LSR_FE | UART_LSR_OE)))
+
+               {
+                       if (lsr & UART_LSR_BI) {
+                               lsr &= ~(UART_LSR_FE | UART_LSR_PE);
+                               sp->port.icount.brk++;
+
+                               if (snx_ser_handle_break(&sp->port))
+                                       goto ignore_char;
+
+                       } else if (lsr & UART_LSR_PE) {
+                               sp->port.icount.parity++;
+                       } else if (lsr & UART_LSR_FE) {
+                               sp->port.icount.frame++;
+                       }
+
+                       if (lsr & UART_LSR_OE)
+                               sp->port.icount.overrun++;
+
+                       lsr &= sp->port.read_status_mask;
+
+                       if (lsr & UART_LSR_BI)
+                               flag = TTY_BREAK;
+                       else if (lsr & UART_LSR_PE)
+                               flag = TTY_PARITY;
+                       else if (lsr & UART_LSR_FE)
+                               flag = TTY_FRAME;
+               }
+
+
+               if ((I_IXOFF(tty)) || I_IXON(tty)) {
+                       if (ch == START_CHAR(tty)) {
+                               tty->stopped = 0;
+                               sunix_ser_start_tx(&sp->port, 1);
+                               goto ignore_char;
+                       } else if (ch == STOP_CHAR(tty)) {
+                               tty->stopped = 1;
+                               sunix_ser_stop_tx(&sp->port, 1);
+                               goto ignore_char;
+                       }
+               }
+
+       snx_ser_insert_char(&sp->port, lsr, UART_LSR_OE, ch, flag);
+
+ignore_char:
+               lsr = read_uart_lsr(sp);
+
+               if (lsr == 0xff)
+                       lsr = 0x01;
+
+       } while ((lsr & UART_LSR_DR) && (max_count-- > 0));
+
+       spin_unlock(&sp->port.lock);
+
+       tty_flip_buffer_push(tport);
+
+       spin_lock(&sp->port.lock);
+       *status = lsr;
+}
+
+
+static void sunix_ser_transmit_chars(struct sunix_ser_port *sp)
+{
+       struct circ_buf *xmit = &sp->port.info->xmit;
+       int count;
+
+       if ((!sp) || (!sp->port.iobase))
+               return;
+
+
+       if (!sp->port.info)
+               return;
+
+
+       if (!xmit)
+               return;
+
+
+       if (sp->port.x_char) {
+               write_uart_tx(sp, sp->port.x_char);
+               sp->port.icount.tx++;
+               sp->port.x_char = 0;
+               return;
+       }
+
+       if (snx_ser_circ_empty(xmit) || snx_ser_tx_stopped(&sp->port)) {
+               sunix_ser_stop_tx(&sp->port, 0);
+               return;
+       }
+
+       count = sp->port.fifosize;
+
+       do {
+               write_uart_tx(sp, xmit->buf[xmit->tail]);
+               xmit->tail = (xmit->tail + 1) & (SNX_UART_XMIT_SIZE - 1);
+               sp->port.icount.tx++;
+
+               if (snx_ser_circ_empty(xmit))
+                       break;
+
+
+       } while (--count > 0);
+
+       if (snx_ser_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               snx_ser_write_wakeup(&sp->port);
+
+}
+
+
+static void sunix_ser_check_modem_status(
+struct sunix_ser_port *sp, unsigned char status)
+{
+       if ((status & UART_MSR_ANY_DELTA) == 0)
+               return;
+
+       if (!sp->port.info)
+               return;
+
+       if (status & UART_MSR_TERI)
+               sp->port.icount.rng++;
+
+       if (status & UART_MSR_DDSR)
+               sp->port.icount.dsr++;
+
+       if (status & UART_MSR_DDCD)
+               snx_ser_handle_dcd_change(&sp->port, status & UART_MSR_DCD);
+
+       if (status & UART_MSR_DCTS)
+               snx_ser_handle_cts_change(&sp->port, status & UART_MSR_CTS);
+
+       wake_up_interruptible(&sp->port.info->delta_msr_wait);
+}
+
+
+static void sunix_ser_handle_port(struct sunix_ser_port *sp,
+unsigned char iir)
+{
+       unsigned char lsr = read_uart_lsr(sp);
+       unsigned char msr = 0;
+
+       if (lsr == 0xff)
+               lsr = 0x01;
+
+
+       if ((iir == UART_IIR_RLSI) || (iir == UART_IIR_CTO) ||
+               (iir == UART_IIR_RDI))
+               sunix_ser_receive_chars(sp, &lsr);
+
+
+       if ((iir == UART_IIR_THRI) && (lsr & UART_LSR_THRE))
+               sunix_ser_transmit_chars(sp);
+
+
+       msr = read_uart_msr(sp);
+
+       if (msr & UART_MSR_ANY_DELTA) {
+               sunix_ser_check_modem_status(sp, msr);
+       } else {
+               if ((iir == 0x00) && (sp->port.chip_flag == SUN1699_HWID)) {
+                       if (!(sp->ier & UART_IER_THRI)) {
+                               sp->ier |= UART_IER_THRI;
+                               write_uart_ier(sp, sp->ier);
+                       }
+               }
+       }
+}
+
+static const struct tty_operations sunix_tty_ops = {
+       .open               = snx_ser_open,
+       .close              = snx_ser_close,
+       .write              = snx_ser_write,
+       .put_char           = snx_ser_put_char,
+       .flush_chars        = snx_ser_flush_chars,
+       .write_room         = snx_ser_write_room,
+       .chars_in_buffer    = snx_ser_chars_in_buffer,
+       .flush_buffer       = snx_ser_flush_buffer,
+       .ioctl              = snx_ser_ioctl,
+       .throttle           = snx_ser_throttle,
+       .unthrottle         = snx_ser_unthrottle,
+       .send_xchar         = snx_ser_send_xchar,
+       .set_termios        = snx_ser_set_termios,
+       .stop               = snx_ser_stop,
+       .start              = snx_ser_start,
+       .hangup             = snx_ser_hangup,
+       .break_ctl          = snx_ser_break_ctl,
+       .wait_until_sent    = snx_ser_wait_until_sent,
+       .tiocmget           = snx_ser_tiocmget,
+       .tiocmset           = snx_ser_tiocmset,
+};
+
+extern int sunix_ser_register_driver(struct snx_ser_driver *drv)
+{
+       struct tty_driver *normal = NULL;
+       int i;
+       int ret = 0;
+
+       drv->state = kmalloc(sizeof(struct snx_ser_state) * drv->nr,
+               GFP_KERNEL);
+
+       ret = -ENOMEM;
+
+       if (!drv->state) {
+               pr_err("SNX Error: Allocate memory fail !\n\n");
+               goto out;
+       }
+
+       memset(drv->state, 0, sizeof(struct snx_ser_state) * drv->nr);
+
+       for (i = 0; i < drv->nr; i++) {
+               struct snx_ser_state *state = drv->state + i;
+               struct tty_port *tport = &state->tport;
+
+               tty_port_init(tport);
+
+               if (!state) {
+                       ret = -1;
+                       pr_err("SNX Error: Memory error !\n\n");
+                       goto out;
+               }
+
+               state->close_delay     = 5 * HZ / 100;
+               state->closing_wait    = 3 * HZ;
+
+               sema_init(&state->sem, 1);
+       }
+
+       normal = tty_alloc_driver(SNX_SER_TOTAL_MAX + 1,
+       TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
+
+       if (!normal) {
+               pr_err("SNX Error: Allocate tty driver fail !\n\n");
+               goto out;
+       }
+
+       snx_driver = normal;
+
+       normal->magic                   = TTY_DRIVER_MAGIC;
+       normal->name                    = drv->dev_name;
+       normal->major                   = drv->major;
+       normal->minor_start             = drv->minor;
+       normal->num                     = (SNX_SER_TOTAL_MAX + 1);
+       normal->type                    = TTY_DRIVER_TYPE_SERIAL;
+       normal->subtype                 = SERIAL_TYPE_NORMAL;
+
+       normal->init_termios            = tty_std_termios;
+       normal->init_termios.c_cflag    = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       normal->init_termios.c_iflag    = 0;
+
+       normal->driver_state            = drv;
+
+       tty_set_operations(normal, &sunix_tty_ops);
+
+       tty_port_link_device(&snx_service_port, normal, SNX_SER_TOTAL_MAX);
+
+       kref_init(&normal->kref);
+
+       ret = tty_register_driver(normal);
+
+       if (ret < 0) {
+               pr_err("SNX Error: Register tty driver fail !\n\n");
+               goto out;
+       }
+
+       for (i = 0; i < drv->nr; i++) {
+               struct snx_ser_state *state = drv->state + i;
+               struct tty_port *tport = &state->tport;
+
+               tty_port_destroy(tport);
+       }
+
+out:
+       if (ret < 0) {
+               put_tty_driver(normal);
+               kfree(drv->state);
+       }
+
+       return (ret);
+}
+
+extern void sunix_ser_unregister_driver(struct snx_ser_driver *drv)
+{
+       struct tty_driver *normal = NULL;
+
+       unsigned int i;
+
+       normal = snx_driver;
+
+       if (!normal)
+               return;
+
+
+       tty_unregister_driver(normal);
+       put_tty_driver(normal);
+
+       for (i = 0; i < drv->nr; i++) {
+               struct snx_ser_state *state = drv->state + i;
+               struct tty_port *tport = &state->tport;
+
+               tty_port_destroy(tport);
+       }
+
+       snx_driver = NULL;
+
+       if (drv->state)
+               kfree(drv->state);
+}
+
+
+static void sunix_ser_request_io(struct snx_ser_port *port)
+{
+       struct sunix_ser_port *sp = (struct sunix_ser_port *)port;
+
+       switch (sp->port.iotype) {
+       case SNX_UPIO_PORT:
+       request_region(sp->port.iobase, SNX_SER_ADDRESS_LENGTH, "snx_ser");
+       break;
+
+       default:
+       break;
+       }
+}
+
+
+static void sunix_ser_configure_port(struct snx_ser_driver *drv,
+struct snx_ser_state *state, struct snx_ser_port *port)
+{
+       unsigned long flags;
+
+       if (!port->iobase)
+               return;
+
+       flags = SNX_UART_CONFIG_TYPE;
+
+       if (port->type != PORT_UNKNOWN) {
+               sunix_ser_request_io(port);
+
+               spin_lock_irqsave(&port->lock, flags);
+
+               sunix_ser_set_mctrl(port, 0);
+               spin_unlock_irqrestore(&port->lock, flags);
+       }
+}
+
+
+static int sunix_ser_add_one_port(struct snx_ser_driver *drv,
+struct snx_ser_port *port)
+{
+       struct snx_ser_state *state = NULL;
+       struct tty_port *tport;
+       struct device *tty_dev;
+
+       int ret = 0;
+
+       if (port->line >= drv->nr)
+               return -EINVAL;
+
+       state = drv->state + port->line;
+
+       tport = &state->tport;
+
+       down(&ser_port_sem);
+
+       if (state->port) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       state->port = port;
+
+       port->info = state->info;
+
+       sunix_ser_configure_port(drv, state, port);
+
+       tty_dev = tty_port_register_device(
+               tport, snx_driver, port->line, port->dev);
+
+       if (likely(!IS_ERR(tty_dev)))
+               device_set_wakeup_capable(tty_dev, 1);
+       else
+               pr_info("Cannot register tty device on line %d\n",
+               port->line);
+
+out:
+       up(&ser_port_sem);
+
+       return ret;
+}
+
+
+extern int sunix_ser_register_ports(struct snx_ser_driver *drv)
+{
+       struct sunix_board *sb = NULL;
+
+       int i;
+       int ret;
+
+       sb = &sunix_board_table[0];
+
+       if (sb == NULL)
+               return 0;
+
+       pci_set_drvdata(sb->pdev, sb);
+
+       for (i = 0; i < SNX_SER_TOTAL_MAX + 1; i++) {
+               struct sunix_ser_port *sp = &sunix_ser_table[i];
+
+               if (!sp)
+                       return -1;
+
+               sp->port.line = i;
+
+               if (sp->port.iobase) {
+
+                       timer_setup(&sp->timer, sunix_ser_timeout, 0);
+
+                       sp->mcr_mask = ~0;
+                       sp->mcr_force = 0;
+
+                       ret = sunix_ser_add_one_port(drv, &sp->port);
+
+                       if (ret != 0)
+                               return ret;
+
+               }
+       }
+
+       return 0;
+}
+
+
+static void sunix_ser_release_io(struct snx_ser_port *port)
+{
+       struct sunix_ser_port *sp = (struct sunix_ser_port *)port;
+
+       switch (sp->port.iotype) {
+       case SNX_UPIO_PORT:
+       release_region(sp->port.iobase, SNX_SER_ADDRESS_LENGTH);
+       break;
+
+       default:
+       break;
+       }
+}
+
+
+static void sunix_ser_unconfigure_port(struct snx_ser_driver *drv,
+struct snx_ser_state *state)
+{
+       struct snx_ser_port *port = state->port;
+       struct snx_ser_info *info = state->info;
+
+       if (info && info->tty)
+               tty_hangup(info->tty);
+
+       down(&state->sem);
+
+       tty_unregister_device(snx_driver, port->line);
+
+       state->info = NULL;
+
+       if (port->type != PORT_UNKNOWN)
+               sunix_ser_release_io(port);
+
+       port->type = PORT_UNKNOWN;
+
+       up(&state->sem);
+}
+
+
+static int sunix_ser_remove_one_port(struct snx_ser_driver *drv,
+struct snx_ser_port *port)
+{
+       struct snx_ser_state *state = drv->state + port->line;
+
+       if (state->port != port) {
+               pr_info("SNX Info : Removing wrong port: %p != %p\n\n",
+               state->port, port);
+       }
+
+       down(&ser_port_sem);
+
+       sunix_ser_unconfigure_port(drv, state);
+
+       state->port = NULL;
+
+       up(&ser_port_sem);
+
+       return 0;
+}
+
+
+extern void sunix_ser_unregister_ports(struct snx_ser_driver *drv)
+{
+       int i;
+
+       for (i = 0; i < SNX_SER_TOTAL_MAX + 1; i++) {
+               struct sunix_ser_port *sp = &sunix_ser_table[i];
+
+               if (sp->port.iobase)
+                       sunix_ser_remove_one_port(drv, &sp->port);
+
+       }
+}
+
+extern int sunix_ser_interrupt(struct sunix_board *sb,
+struct sunix_ser_port *first_sp)
+{
+       struct sunix_ser_port *sp = NULL;
+       int i;
+       int max;
+       int irqbits;
+       int bits;
+       int pass_counter = 0;
+       unsigned char iir;
+
+       max = sb->ser_port;
+
+       if ((first_sp->port.port_flag & PORTFLAG_REMAP) == PORTFLAG_REMAP) {
+               while (1) {
+                       for (i = 0; i < max; i++) {
+                               sp = first_sp + i;
+
+                               if (!sp->port.iobase)
+                                       continue;
+
+
+                               iir = read_uart_iir(sp) & 0x0f;
+
+                               if (iir & UART_IIR_NO_INT) {
+                                       continue;
+                               } else {
+                                       spin_lock(&sp->port.lock);
+                                       sunix_ser_handle_port(sp, iir);
+                                       spin_unlock(&sp->port.lock);
+                               }
+                       }
+
+                       if (pass_counter++ > INTERRUPT_COUNT)
+                               break;
+
+               }
+       } else if (first_sp->port.snx_type == SNX_SER_PORT_SUN1999) {
+               while (1) {
+                       irqbits = read_1999_interrupt_vector_word
+                       (sb, first_sp) & first_sp->port.vector_mask;
+
+                       if (irqbits == 0x0000)
+                               break;
+
+
+                       for (i = 0, bits = 1; i < max; i++, bits <<= 1) {
+                               if (!(bits & irqbits))
+                                       continue;
+
+
+                       sp = first_sp + i;
+
+                       iir = read_uart_iir(sp) & 0x0f;
+
+                       if (iir & UART_IIR_NO_INT) {
+                               continue;
+                       } else {
+                               spin_lock(&sp->port.lock);
+                               sunix_ser_handle_port(sp, iir);
+                               spin_unlock(&sp->port.lock);
+                       }
+                       }
+
+                       if (pass_counter++ > INTERRUPT_COUNT)
+                               break;
+
+               }
+       } else {
+               if (first_sp->port.snx_type == SNX_SER_PORT_SUNMATX) {
+                       while (1) {
+                               irqbits = read_interrupt_vector_word(first_sp) &
+                               first_sp->port.vector_mask;
+
+                               if (irqbits == 0x0000)
+                                       break;
+
+
+                               for (i = 0, bits = 1; i < max; i++,
+                               bits <<= 1) {
+                                       if (!(bits & irqbits))
+                                               continue;
+
+
+                                       sp = first_sp + i;
+
+                                       iir = read_uart_iir(sp) & 0x0f;
+
+                                       if (iir & UART_IIR_NO_INT) {
+                                               continue;
+                                       } else {
+                                               spin_lock(&sp->port.lock);
+                                               sunix_ser_handle_port(sp, iir);
+                                               spin_unlock(&sp->port.lock);
+                                       }
+                               }
+
+                               if (pass_counter++ > INTERRUPT_COUNT)
+                                       break;
+
+                       }
+               } else {
+                       while (1) {
+                               irqbits = read_interrupt_vector_byte(first_sp) &
+                               first_sp->port.vector_mask;
+
+                               if (irqbits == 0x0000)
+                                       break;
+
+                               for (i = 0, bits = 1; i < max; i++,
+                               bits <<= 1) {
+                                       if (!(bits & irqbits))
+                                               continue;
+
+                                       sp = first_sp + i;
+
+                                       iir = read_uart_iir(sp) & 0x0f;
+
+                                       if (iir & UART_IIR_NO_INT) {
+                                               continue;
+                                       } else {
+                                               spin_lock(&sp->port.lock);
+                                               sunix_ser_handle_port(sp, iir);
+                                               spin_unlock(&sp->port.lock);
+                                       }
+                               }
+
+                               if (pass_counter++ > INTERRUPT_COUNT)
+                                       break;
+
+                       }
+               }
+       }
+
+       return 0;
+}
+
+
-- 
2.17.1

Reply via email to