2019-03-19 12:09:34

by Moriis Ku

[permalink] [raw]
Subject: [PATCH 2/4] Add support for SUNIX Multi-I/O board

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 <[email protected]>
---
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.<[email protected]>"
+#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



Subject: Re: [PATCH 2/4] Add support for SUNIX Multi-I/O board

On 19.03.19 13:07, Morris Ku wrote:
> 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.
>
<snip>

> +static const char snx_ser_ic_table[SNX_SER_PORT_MAX_UART][10] = {

Doesn't seem to be used anywhere.

> + {"UNKNOWN"},
> + {"SUN1889"},
> + {"SUN1699"},
> + {"SUNMATX"},
> + {"SUN1999"}
> +};
> +
> +static const char snx_par_ic_table[SNX_PAR_PORT_MAX_UART][10] = {

Doesn't seem to be used anywhere.

> + {"UNKNOWN"},
> + {"SUN1888"},
> + {"SUN1689"},
> + {"SUNMATX"},
> + {"SUN1999"}
> +};

> +
> +static const char snx_port_remap[2][10] = {

Doesn't seem to be used anywhere.

> + {"NON-REMAP"},
> + {"REMAP"}
> +};

<snip>

> +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];

Allocate these per-device (per card) in sunix_pci_board_probe() and let
the device's private_data point to it.

And better do static zero initialization instead of memset().

<snip>

> +static struct snx_ser_driver sunix_ser_reg = {
> + .dev_name = "ttySNX",
> + .major = 0,
> + .minor = 0,
> + .nr = (SNX_SER_TOTAL_MAX + 1),
> +};

Why this struct for something that exists only once and still allocating
more (struct snx_ser_state) in sunix_ser_register_driver() ?
Why not just putting everything into one struct ?

> +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])) {

Don't do unncessary loops in time sensitive functions. Instead pass the
pointer to the corresponding struct sunix_board via dev_id - it's the
second parameter of request_irq(). Would make the code much simpler and
smaller.

> +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;
> +}

Don't duplicate already existing functionality - use the serial
subsystem. See: drivers/tty/serial/

> +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;

use ARRAY_SIZE() here.

> +
> + // search matrix serial board
> + pdev = NULL;
> + tablecnt = 0;
> + boardcnt = 0;
> + sub_device_id = 0;
> + status = 0;

Do the initialization at declaration.

> + while (tablecnt < sunix_pci_board_id_cnt) {
> +
> + pdev = pci_get_device(VENID_MATRIX, DEVID_M_SERIAL, pdev);

Use separate per-board driver instances instead of one instance for all.
Using multiple cards won't work this way - probe() is called per card.

> + // 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;

Use separate driver instances for that.

> + while (tablecnt < sunix_pci_board_id_cnt) {
> +
> + pdev = pci_get_device(VENID_SUN1999, DEVID_S_SERIAL, pdev);

again: multiple cards won't work that way. you'll overwrite the
structures of the previous cards.

> + // search SUN1999 parallel board
> + pdev = NULL;
> + tablecnt = 0;
> + sub_device_id = 0;
> + status = 0;

use a separate driver for that.

> +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));

See above: use static zero initialization.

> + 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));
> + }

can't the register space be memory-mapped ?

> +int sunix_register_irq(void)
> +{
> + struct sunix_board *sb = NULL;
> + int status = 0;
> + int i;
> +
> + for (i = 0; i < SNX_BOARDS_MAX; i++) {

use separate per-board device instances.

> +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];

use separate per-board device instances.
otherwise hotplug won't work.

> +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;
> +

register the drivers first. the pci subsystem will then probe
automatically. (and call the driver's probe() function per board).

> + 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;
> + }

As already said: changing things like irqs or io areaas from userland
shouldn't be possible at all by such weird ways. Let the corresponding
bus subsystems (in that case: pci) handle it in generic ways, and care
for probing and hotplug. (anyways, it seems that this doesn't change the
irq the device is raising, just the one the driver expects - that's
even more wrong)

> + 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);

Why do you newly introduce things that are already deprecated ?

> +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;
> + }

Dont reimplement existing standard functionality in your own special
way. Use the serial subsystem instead.

> + 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;
> + }

This doesn't belong here. Don't invent your own private ioctl's for such
things. Use sysfs or debugfs.

> +
> + case SNX_SER_DUMP_DRIVER_VER:
> + {

Same here.

> + case SNX_COMM_GET_BOARD_CNT:
> + {

No, This information can already be retrieved just by the number of
serial/tty devices. Anyways this is just metadata for the admin, nothing
that client applications should have to care about.

> + case SNX_COMM_GET_BOARD_INFO:
> + {

See above: use debugfs or sysfs if you have to publish extra metadata.

> + case SNX_UART_GET_TYPE:
> + {

Don't reimplement existing standard functionality.

> + case SNX_UART_SET_TYPE: {
> + struct sunix_board *sb = NULL;
> + struct sunix_ser_port *sp = NULL;
> +

what things exactly can be set here, that aren't already
handled by the standard serial subsystem ?

> + GPIOstate = inb(targetConfigAddress + 0x0C);
> + GPIOcontrol = inb(targetConfigAddress + 0x0D);

GPIOs don't belong here. We have a separate GPIO subsystem.

> + case SNX_UART_GET_ACS:
> + {
> + SNX_DRVR_UART_GET_ACS gb;
> + struct sunix_board *sb = NULL;
> + int ACSstate = 0;

What kind of states are these exactly that aren't already
handled by the standard serial subsystem ?

> +extern void snx_ser_update_termios(struct snx_ser_state *state)

Why do you need "extern" here?

> +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,
> +};

Don't reimplement existing functionality - use the serial subsystem.

> +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);

Dont reimplement existing functionality - use the serial subsystem.


Your driver is at least 10 times bigger as it needs to be, reimplements
lots of standard functionality in own private ways and bypasses the
corresponding subsystems. I could never ack that.


--mtx

--
Enrico Weigelt, metux IT consult
Free software and Linux embedded engineering
[email protected] -- +49-151-27565287

2019-04-02 06:29:19

by Lee Jones

[permalink] [raw]
Subject: Re: [PATCH 2/4] Add support for SUNIX Multi-I/O board

On Tue, 19 Mar 2019, Morris Ku wrote:

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

Serial drivers should live in drivers/tty/serial.

Please move it there.

> Signed-off-by: Morris Ku <[email protected]>
> ---
> 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

--
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog