[PATCH] Add driver for SUNIX serial board

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch add support for SUNIX serial board.

Signed-off-by: morrisku <saumah@xxxxxxxxx>
---
 serial/Kconfig      |  11 ++
 serial/Makefile     |   1 +
 serial/sunix_uart.c | 357 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 369 insertions(+)
 create mode 100644 serial/sunix_uart.c

diff --git a/serial/Kconfig b/serial/Kconfig
index 0d31251..f9ae108 100644
--- a/serial/Kconfig
+++ b/serial/Kconfig
@@ -1618,6 +1618,17 @@ config SERIAL_MILBEAUT_USIO_PORTS
 	depends on SERIAL_MILBEAUT_USIO
 	default "4"
 
+config SERIAL_SUNIX
+	tristate "SUNIX pci serial port support"
+	depends on SERIAL_8250
+	select SERIAL_CORE
+	help
+	  Say Y here if you have a SUNIX serial card.
+	  If unsure, say N.
+
+	  This driver can also be built as a module. The module will be called
+	  sunix_pci_serial. If you want to do that, say M here.
+
 config SERIAL_MILBEAUT_USIO_CONSOLE
 	bool "Support for console on MILBEAUT USIO/UART serial port"
 	depends on SERIAL_MILBEAUT_USIO=y
diff --git a/serial/Makefile b/serial/Makefile
index 79c3d51..cecccc6 100644
--- a/serial/Makefile
+++ b/serial/Makefile
@@ -94,6 +94,7 @@ obj-$(CONFIG_SERIAL_OWL)	+= owl-uart.o
 obj-$(CONFIG_SERIAL_RDA)	+= rda-uart.o
 obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += milbeaut_usio.o
 obj-$(CONFIG_SERIAL_SIFIVE)	+= sifive.o
+obj-$(CONFIG_SERIAL_SUNIX)	+= sunix_uart.o
 
 # GPIOLIB helpers for modem control lines
 obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
diff --git a/serial/sunix_uart.c b/serial/sunix_uart.c
new file mode 100644
index 0000000..d227d7a
--- /dev/null
+++ b/serial/sunix_uart.c
@@ -0,0 +1,357 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *	Driver for SUNIX PCI serial board
+ *	Based on drivers/tty/serial/8250/8250_pci.c
+ *	by Linus Torvalds, Theodore Ts'o.
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License.
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_reg.h>
+#include <linux/serial_core.h>
+#include <linux/8250_pci.h>
+#include <linux/bitops.h>
+#include <linux/dmaengine.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+
+struct sunix_pci_board {
+	kernel_ulong_t driver_data;
+	struct pci_dev *pdev;
+	unsigned int num_ports;
+	int line[0];
+};
+
+
+enum {
+	sunix_pci_1s = 0,
+	sunix_pci_2s,
+	sunix_pci_4s,
+	sunix_pci_8s,
+	sunix_pci_16s
+};
+
+
+static struct sunix_pci_board sunix_pci_boards[] = {
+	[sunix_pci_1s] = {.num_ports = 1},
+	[sunix_pci_2s] = {.num_ports = 2},
+	[sunix_pci_4s] = {.num_ports = 4},
+	[sunix_pci_8s] = {.num_ports = 8},
+	[sunix_pci_16s] = {.num_ports = 16},
+};
+
+
+struct sunix_pci_board *snx_init_ports(struct pci_dev *pdev,
+				kernel_ulong_t driver_data)
+{
+	struct uart_8250_port uart;
+	struct sunix_pci_board *board;
+	unsigned int num_ports;
+	int i, bar, offset;
+
+	num_ports = sunix_pci_boards[driver_data].num_ports;
+
+	board = kzalloc(sizeof(struct sunix_pci_board) +
+			sizeof(unsigned int) * num_ports, GFP_KERNEL);
+	if (!board) {
+		board = ERR_PTR(-ENOMEM);
+		goto err_out;
+	}
+
+	board->driver_data = driver_data;
+	board->pdev = pdev;
+	board->num_ports = num_ports;
+
+	memset(&uart, 0, sizeof(uart));
+	uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF |
+					UPF_SHARE_IRQ | UPF_FIXED_TYPE;
+	uart.port.uartclk = 921600 * 16;
+	uart.port.irq = pdev->irq;
+	uart.port.dev = &pdev->dev;
+	uart.port.type = PORT_16C950;
+	uart.port.fifosize = 128;
+
+	for (i = 0; i < num_ports; i++) {
+
+		switch (i) {
+		case 0:
+			bar = 0; offset = 0; break;
+		case 1:
+			bar = 0; offset = 8; break;
+		case 2:
+			bar = 0; offset = 16; break;
+		case 3:
+			bar = 0; offset = 24; break;
+		case 4:
+			bar = 1; offset = 0; break;
+		case 5:
+			bar = 1; offset = 8; break;
+		case 6:
+			bar = 1; offset = 16; break;
+		case 7:
+			bar = 1; offset = 24; break;
+		case 8:
+			bar = 1; offset = 64; break;
+		case 9:
+			bar = 1; offset = 72; break;
+		case 10:
+			bar = 1; offset = 80; break;
+		case 11:
+			bar = 1; offset = 88; break;
+		case 12:
+			bar = 1; offset = 128; break;
+		case 13:
+			bar = 1; offset = 136; break;
+		case 14:
+			bar = 1; offset = 144; break;
+		case 15:
+			bar = 1; offset = 152; break;
+		}
+
+		uart.port.iotype = UPIO_PORT;
+		uart.port.iobase = pci_resource_start(pdev, bar) + offset;
+		uart.port.mapbase = 0;
+		uart.port.membase = NULL;
+		uart.port.regshift = 0;
+
+		board->line[i] = serial8250_register_8250_port(&uart);
+		if (board->line[i] < 0) {
+			printk(KERN_INFO "sunix_pci_serial : Couldn't register serial port %s: %d\n",
+			pci_name(pdev), board->line[i]);
+			break;
+		}
+	}
+
+	return board;
+
+err_out:
+	return board;
+}
+
+
+void snx_detach_ports(struct sunix_pci_board *board)
+{
+
+	int i;
+
+	for (i = 0; i < board->num_ports; i++)
+		serial8250_unregister_port(board->line[i]);
+}
+
+
+void snx_remove_ports(struct sunix_pci_board *board)
+{
+	snx_detach_ports(board);
+	kfree(board);
+}
+
+
+void snx_suspend_ports(struct sunix_pci_board *board)
+{
+	int i;
+
+	for (i = 0; i < board->num_ports; i++) {
+		if (board->line[i] >= 0)
+			serial8250_suspend_port(board->line[i]);
+
+	}
+}
+
+
+void snx_resume_ports(struct sunix_pci_board *board)
+{
+
+	int i;
+
+	for (i = 0; i < board->num_ports; i++) {
+		if (board->line[i] >= 0)
+			serial8250_resume_port(board->line[i]);
+	}
+}
+
+
+#ifdef CONFIG_PM_SLEEP
+static int snx_suspend_one(struct device *dev)
+{
+
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct sunix_pci_board *board = pci_get_drvdata(pdev);
+
+	if (board)
+		snx_suspend_ports(board);
+
+	return  0;
+}
+
+
+static int snx_resume_one(struct device *dev)
+{
+
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct sunix_pci_board *board = pci_get_drvdata(pdev);
+	int err;
+
+	if (board) {
+		err = pci_enable_device(pdev);
+		if (err)
+			printk(KERN_INFO "sunix_pci_serial : Unable to re-enable ports\n");
+
+		snx_resume_ports(board);
+	}
+
+	return 0;
+}
+#endif
+
+
+static pci_ers_result_t snx_err_io_error_detected(struct pci_dev *pdev,
+		pci_channel_state_t state)
+{
+
+	struct sunix_pci_board *board = pci_get_drvdata(pdev);
+
+	if (state == pci_channel_io_perm_failure)
+		return PCI_ERS_RESULT_DISCONNECT;
+
+	if (board)
+		snx_detach_ports(board);
+
+	pci_disable_device(pdev);
+
+	return PCI_ERS_RESULT_NEED_RESET;
+}
+
+
+static pci_ers_result_t snx_err_io_slot_reset(struct pci_dev *pdev)
+{
+
+	int rc;
+
+	rc = pci_enable_device(pdev);
+	if (rc)
+		return PCI_ERS_RESULT_DISCONNECT;
+
+	pci_restore_state(pdev);
+	pci_save_state(pdev);
+
+	return PCI_ERS_RESULT_RECOVERED;
+}
+
+
+static void snx_err_io_resume(struct pci_dev *pdev)
+{
+	struct sunix_pci_board *board = pci_get_drvdata(pdev);
+	struct sunix_pci_board *new;
+
+	if (!board)
+		return;
+
+	new = snx_init_ports(pdev, board->driver_data);
+	if (!IS_ERR(new)) {
+		pci_set_drvdata(pdev, new);
+		kfree(board);
+	}
+}
+
+
+static int sunix_pci_serial_init_one(struct pci_dev *pdev,
+			const struct pci_device_id *ent)
+{
+
+	struct sunix_pci_board *board;
+	int rc;
+
+	if (ent->driver_data >= ARRAY_SIZE(sunix_pci_boards)) {
+		printk(KERN_INFO "sunix_pci_serial : Invalid driver_data:%ld\n",
+							ent->driver_data);
+		return -EINVAL;
+	}
+
+	rc = pci_enable_device(pdev);
+	pci_save_state(pdev);
+
+	if (rc)
+		return rc;
+
+	board = snx_init_ports(pdev, ent->driver_data);
+	if (IS_ERR(board)) {
+		rc = PTR_ERR(board);
+		return rc;
+	}
+
+	pci_set_drvdata(pdev, board);
+	return 0;
+}
+
+
+static void sunix_pci_serial_remove_one(struct pci_dev *pdev)
+{
+
+	struct sunix_pci_board *board = pci_get_drvdata(pdev);
+
+	snx_remove_ports(board);
+}
+
+
+static SIMPLE_DEV_PM_OPS(sunix_pci_serial_pm_ops,
+				snx_suspend_one, snx_resume_one);
+
+
+static struct pci_device_id sunix_pci_serial_id_tbl[] = {
+	// 5027A - 1S
+	{ 0x1fd4, 0x1999, 0x1fd4, 0x0001, 0, 0, sunix_pci_1s },
+	// 5037A,P2102,CDK1037,DIO0802 - 2S
+	{ 0x1fd4, 0x1999, 0x1fd4, 0x0002, 0, 0, sunix_pci_2s },
+	// 5056A,P2104,CDK1056,DIO1604,DIO3204 - 4S
+	{ 0x1fd4, 0x1999, 0x1fd4, 0x0004, 0, 0, sunix_pci_4s },
+	// P3104 - 4S
+	{ 0x1fd4, 0x1999, 0x1fd4, 0x0084, 0, 0, sunix_pci_4s },
+	// 5066A,P2108 - 8S
+	{ 0x1fd4, 0x1999, 0x1fd4, 0x0008, 0, 0, sunix_pci_8s },
+	// P3108 - 8S
+	{ 0x1fd4, 0x1999, 0x1fd4, 0x0088, 0, 0, sunix_pci_8s },
+	// 5016A,P2116 - 16S
+	{ 0x1fd4, 0x1999, 0x1fd4, 0x0010, 0, 0, sunix_pci_16s },
+	//
+	{0}
+};
+MODULE_DEVICE_TABLE(pci, sunix_pci_serial_id_tbl);
+
+
+static const struct pci_error_handlers sunix_pci_serial_err_handler = {
+	.error_detected = snx_err_io_error_detected,
+	.slot_reset		= snx_err_io_slot_reset,
+	.resume			= snx_err_io_resume,
+};
+
+
+static struct pci_driver sunix_pci_serial_driver = {
+	.name			= "sunix_pci_serial",
+	.probe			= sunix_pci_serial_init_one,
+	.remove			= sunix_pci_serial_remove_one,
+	.driver			= {
+		.pm			= &sunix_pci_serial_pm_ops,
+	},
+	.id_table		= sunix_pci_serial_id_tbl,
+	.err_handler	= &sunix_pci_serial_err_handler,
+};
+
+
+module_pci_driver(sunix_pci_serial_driver);
+
+MODULE_AUTHOR("SUNIX Co., Ltd.<info@xxxxxxxxxxxx>");
+MODULE_DESCRIPTION("SUNIX PCI serial board driver");
+MODULE_LICENSE("GPL");
+
+
-- 
2.17.1




[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux