Files
linux-nv-oot/drivers/tty/serial/wch_35x/wch_main.c
Ankit patel a9ae1a62f2 tty: wch: update copyright headers
Update copyright header to GPLv2, removed proprietary copyright headers

Bug 5065840

Change-Id: Ib0b8be01f3c0a4fc963244d7b49489960420dc27
Signed-off-by: Ankit patel <anpatel@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3289171
Reviewed-by: Bibek Basu <bbasu@nvidia.com>
Tested-by: Bibek Basu <bbasu@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-01-23 05:23:59 -08:00

890 lines
34 KiB
C

/*
* SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*/
#include "wch_common.h"
extern struct wch_board wch_board_table[WCH_BOARDS_MAX];
extern struct wch_ser_port wch_ser_table[WCH_SER_TOTAL_MAX + 1];
extern unsigned char ch365_32s;
struct wch_board wch_board_table[WCH_BOARDS_MAX];
struct wch_ser_port wch_ser_table[WCH_SER_TOTAL_MAX + 1];
int wch_ser_port_total_cnt;
unsigned char ch365_32s = 0;
struct pci_device_id wch_pci_board_id[] = {
{VENDOR_ID_WCH_CH351, DEVICE_ID_WCH_CH351_2S, SUB_VENDOR_ID_WCH_CH351, SUB_DEVICE_ID_WCH_CH351_2S, 0, 0,
WCH_BOARD_CH351_2S},
{VENDOR_ID_WCH_PCI, DEVICE_ID_WCH_CH352_2S, SUB_VENDOR_ID_WCH_PCI, SUB_DEVICE_ID_WCH_CH352_2S, 0, 0,
WCH_BOARD_CH352_2S},
{VENDOR_ID_WCH_PCI, DEVICE_ID_WCH_CH352_1S1P, SUB_VENDOR_ID_WCH_PCI, SUB_DEVICE_ID_WCH_CH352_1S1P, 0, 0,
WCH_BOARD_CH352_1S1P},
{VENDOR_ID_WCH_PCI, DEVICE_ID_WCH_CH353_4S, SUB_VENDOR_ID_WCH_PCI, SUB_DEVICE_ID_WCH_CH353_4S, 0, 0,
WCH_BOARD_CH353_4S},
{VENDOR_ID_WCH_PCI, DEVICE_ID_WCH_CH353_2S1P, SUB_VENDOR_ID_WCH_PCI, SUB_DEVICE_ID_WCH_CH353_2S1P, 0, 0,
WCH_BOARD_CH353_2S1P},
{VENDOR_ID_WCH_PCI, DEVICE_ID_WCH_CH353_2S1PAR, SUB_VENDOR_ID_WCH_PCI, SUB_DEVICE_ID_WCH_CH353_2S1PAR, 0, 0,
WCH_BOARD_CH353_2S1PAR},
{VENDOR_ID_WCH_PCI, DEVICE_ID_WCH_CH355_4S, SUB_VENDOR_ID_WCH_PCI, SUB_DEVICE_ID_WCH_CH355_4S, 0, 0,
WCH_BOARD_CH355_4S},
{VENDOR_ID_WCH_PCI, DEVICE_ID_WCH_CH356_4S1P, SUB_VENDOR_ID_WCH_PCI, SUB_DEVICE_ID_WCH_CH356_4S1P, 0, 0,
WCH_BOARD_CH356_4S1P},
{VENDOR_ID_WCH_PCI, DEVICE_ID_WCH_CH356_6S, SUB_VENDOR_ID_WCH_PCI, SUB_DEVICE_ID_WCH_CH356_6S, 0, 0,
WCH_BOARD_CH356_6S},
{VENDOR_ID_WCH_PCI, DEVICE_ID_WCH_CH356_8S, SUB_VENDOR_ID_WCH_PCI, SUB_DEVICE_ID_WCH_CH356_8S, 0, 0,
WCH_BOARD_CH356_8S},
{VENDOR_ID_WCH_PCI, DEVICE_ID_WCH_CH357_4S, SUB_VENDOR_ID_WCH_PCI, SUB_DEVICE_ID_WCH_CH357_4S, 0, 0,
WCH_BOARD_CH357_4S},
{VENDOR_ID_WCH_PCI, DEVICE_ID_WCH_CH358_4S1P, SUB_VENDOR_ID_WCH_PCI, SUB_DEVICE_ID_WCH_CH358_4S1P, 0, 0,
WCH_BOARD_CH358_4S1P},
{VENDOR_ID_WCH_PCI, DEVICE_ID_WCH_CH358_8S, SUB_VENDOR_ID_WCH_PCI, SUB_DEVICE_ID_WCH_CH358_8S, 0, 0,
WCH_BOARD_CH358_8S},
{VENDOR_ID_WCH_PCI, DEVICE_ID_WCH_CH359_16S, SUB_VENDOR_ID_WCH_PCI, SUB_DEVICE_ID_WCH_CH359_16S, 0, 0,
WCH_BOARD_CH359_16S},
{VENDOR_ID_WCH_PCIE, DEVICE_ID_WCH_CH382_2S, SUB_VENDOR_ID_WCH_PCIE, SUB_DEVICE_ID_WCH_CH382_2S, 0, 0,
WCH_BOARD_CH382_2S},
{VENDOR_ID_WCH_PCIE, DEVICE_ID_WCH_CH382_2S1P, SUB_VENDOR_ID_WCH_PCIE, SUB_DEVICE_ID_WCH_CH382_2S1P, 0, 0,
WCH_BOARD_CH382_2S1P},
{VENDOR_ID_WCH_PCIE, DEVICE_ID_WCH_CH384_4S, SUB_VENDOR_ID_WCH_PCIE, SUB_DEVICE_ID_WCH_CH384_4S, 0, 0,
WCH_BOARD_CH384_4S},
{VENDOR_ID_WCH_PCIE, DEVICE_ID_WCH_CH384_4S1P, SUB_VENDOR_ID_WCH_PCIE, SUB_DEVICE_ID_WCH_CH384_4S1P, 0, 0,
WCH_BOARD_CH384_4S1P},
{VENDOR_ID_WCH_PCIE, DEVICE_ID_WCH_CH384_8S, SUB_VENDOR_ID_WCH_PCIE, SUB_DEVICE_ID_WCH_CH384_8S, 0, 0,
WCH_BOARD_CH384_8S},
{VENDOR_ID_WCH_PCIE, DEVICE_ID_WCH_CH384_28S, SUB_VENDOR_ID_WCH_PCIE, SUB_DEVICE_ID_WCH_CH384_28S, 0, 0,
WCH_BOARD_CH384_28S},
{VENDOR_ID_WCH_PCI, DEVICE_ID_WCH_CH365_32S, SUB_VENDOR_ID_WCH_PCI, SUB_DEVICE_ID_WCH_CH365_32S, 0, 0,
WCH_BOARD_CH365_32S},
{0}
};
MODULE_DEVICE_TABLE(pci, wch_pci_board_id);
static irqreturn_t wch_interrupt(int irq, void *dev_id)
{
struct wch_ser_port *sp = NULL;
struct wch_board *sb = NULL;
int i;
int status = 0;
int handled = IRQ_NONE;
for (i = 0; i < WCH_BOARDS_MAX; i++) {
if (dev_id == &(wch_board_table[i])) {
sb = dev_id;
break;
}
}
if (i == WCH_BOARDS_MAX) {
status = 1;
}
if (!sb) {
status = 1;
}
if (sb->board_enum <= 0) {
status = 1;
}
if (status != 0) {
return handled;
}
if ((sb->ser_ports > 0) && (sb->ser_isr != NULL)) {
sp = &wch_ser_table[sb->ser_port_index];
if (!sp) {
status = 1;
}
status = sb->ser_isr(sb, sp);
}
if (status != 0) {
return handled;
}
handled = IRQ_HANDLED;
return handled;
}
static int wch_pci_board_probe(void)
{
struct wch_board *sb;
struct pci_dev *pdev = NULL;
struct pci_dev *pdev_array[4] = {NULL, NULL, NULL, NULL};
int wch_pci_board_id_cnt;
int table_cnt;
int board_cnt;
int i;
unsigned short int sub_device_id;
int status;
#if WCH_DBG
printk("%s : %s\n", __FILE__, __FUNCTION__);
#endif
// clear and init some variable
memset(wch_board_table, 0, WCH_BOARDS_MAX * sizeof(struct wch_board));
for (i = 0; i < WCH_BOARDS_MAX; i++) {
wch_board_table[i].board_enum = -1;
wch_board_table[i].board_number = -1;
}
wch_pci_board_id_cnt = (sizeof(wch_pci_board_id) / sizeof(wch_pci_board_id[0])) - 1;
// search wch serial and multi-I/O board
pdev = NULL;
table_cnt = 0;
board_cnt = 0;
status = 0;
while (table_cnt < wch_pci_board_id_cnt) {
pdev = pci_get_device(wch_pci_board_id[table_cnt].vendor, wch_pci_board_id[table_cnt].device, pdev);
if (pdev == NULL) {
table_cnt++;
continue;
}
if ((table_cnt > 0) && ((pdev == pdev_array[0]) || (pdev == pdev_array[1]) || (pdev == pdev_array[2]) ||
(pdev == pdev_array[3]))) {
continue;
}
if (wch_pci_board_id[table_cnt].driver_data == WCH_BOARD_CH365_32S) {
ch365_32s = 0x01;
}
pci_read_config_word(pdev, 0x2e, &sub_device_id);
if (ch365_32s) {
} else {
if (sub_device_id == 0) {
printk("WCH Error: WCH Board (bus:%d device:%d), in configuration space,\n", pdev->bus->number,
PCI_SLOT(pdev->devfn));
printk(" subdevice id isn't vaild.\n\n");
status = -EIO;
return status;
}
if (sub_device_id != wch_pci_board_id[table_cnt].subdevice) {
continue;
}
}
if (pdev == NULL) {
printk("WCH Error: PCI device object is an NULL pointer !\n\n");
status = -EIO;
return status;
} else {
status = pci_enable_device(pdev);
if (status != 0) {
printk("WCH Error: WCH Board Enable Fail !\n\n");
status = -ENXIO;
return status;
}
}
board_cnt++;
if (board_cnt > WCH_BOARDS_MAX) {
printk("\n");
printk("WCH Error: WCH Driver Module Support Four Boards In Maximum !\n\n");
status = -ENOSPC;
return status;
}
sb = &wch_board_table[board_cnt - 1];
pdev_array[board_cnt - 1] = pdev;
sb->pdev = pdev;
sb->bus_number = pdev->bus->number;
sb->dev_number = PCI_SLOT(pdev->devfn);
sb->board_enum = (int)wch_pci_board_id[table_cnt].driver_data;
sb->pb_info = wch_pci_board_conf[sb->board_enum];
sb->board_flag = sb->pb_info.board_flag;
sb->board_number = board_cnt - 1;
}
if (board_cnt == 0) {
printk("WCH Info : No WCH Multi-I/O Board Found !\n\n");
status = -ENXIO;
return status;
} else {
for (i = 0; i < WCH_BOARDS_MAX; i++) {
sb = &wch_board_table[i];
if (sb->board_enum > 0) {
printk("\n");
if ((sb->pb_info.num_serport) > 0) {
printk("WCH Info : Found WCH %s Series Board (%dS),\n", sb->pb_info.board_name,
sb->pb_info.num_serport);
}
printk(" bus number:%d, device number:%d\n\n", sb->bus_number, sb->dev_number);
}
}
}
return status;
}
static int wch_get_pci_board_conf(void)
{
struct wch_board *sb = NULL;
struct pci_dev *pdev = NULL;
int status = 0;
int i;
int j;
#if WCH_DBG
printk("%s : %s\n", __FILE__, __FUNCTION__);
#endif
for (i = 0; i < WCH_BOARDS_MAX; i++) {
sb = &wch_board_table[i];
if (sb->board_enum > 0) {
pdev = sb->pdev;
sb->ser_ports = sb->pb_info.num_serport;
wch_ser_port_total_cnt = wch_ser_port_total_cnt + sb->ser_ports;
if (wch_ser_port_total_cnt > WCH_SER_TOTAL_MAX) {
printk("WCH Error: Too much serial port, maximum %d ports can be supported !\n\n", WCH_SER_TOTAL_MAX);
status = -EIO;
return status;
}
for (j = 0; j < WCH_PCICFG_BAR_TOTAL; j++) {
sb->bar_addr[j] = pci_resource_start(pdev, j);
}
if ((sb->board_flag & BOARDFLAG_CH365_32_PORTS) == BOARDFLAG_CH365_32_PORTS) {
sb->board_membase = ioremap(sb->bar_addr[1], 4096);
if (!sb->board_membase) {
status = -EIO;
printk("WCH Error: ioremap failed !\n");
return status;
}
}
sb->irq = sb->pdev->irq;
if (sb->irq <= 0) {
printk(
"WCH Error: WCH 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 wch_assign_resource(void)
{
struct wch_board *sb = NULL;
struct wch_ser_port *sp = NULL;
int status = 0;
int i;
int j;
int ser_n;
int ser_port_index = 0;
#if WCH_DBG
printk("%s : %s\n", __FILE__, __FUNCTION__);
#endif
memset(wch_ser_table, 0, (WCH_SER_TOTAL_MAX + 1) * sizeof(struct wch_ser_port));
for (i = 0; i < WCH_BOARDS_MAX; i++) {
sb = &wch_board_table[i];
if (sb->board_enum > 0) {
if (sb->ser_ports > 0) {
sb->vector_mask = 0;
// assign serial port resource
ser_n = sb->ser_port_index = ser_port_index;
sp = &wch_ser_table[ser_n];
if (sp == NULL) {
status = -ENXIO;
printk("WCH Error: Serial port table address error !\n");
return status;
}
for (j = 0; j < sb->ser_ports; 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;
/* use scr reg to test io space */
outb(0x55, sp->port.iobase + UART_SCR);
if (inb(sp->port.iobase + UART_SCR) != 0x55) {
status = -ENXIO;
if (j == 0)
printk("WCH Error: pci/pcie address error !\n");
else
printk("WCH Error: ch432/ch438 communication error !\n");
return status;
}
if ((sb->board_flag & BOARDFLAG_REMAP) == BOARDFLAG_REMAP) {
sp->port.vector = 0;
} else if ((sb->board_flag & BOARDFLAG_CH384_8_PORTS) == BOARDFLAG_CH384_8_PORTS) {
sp->port.chip_iobase = sb->bar_addr[sb->pb_info.port[j].bar1];
sp->port.vector = sb->bar_addr[sb->pb_info.intr_vector_bar] + sb->pb_info.intr_vector_offset;
} else if ((sb->board_flag & BOARDFLAG_CH384_28_PORTS) == BOARDFLAG_CH384_28_PORTS) {
sp->port.chip_iobase = sb->bar_addr[sb->pb_info.port[j].bar1];
if (j >= 0 && j < 0x04) {
sp->port.vector =
sb->bar_addr[sb->pb_info.intr_vector_bar] + sb->pb_info.intr_vector_offset;
} else if (j >= 0x04 && j < 0x0C) {
sp->port.vector =
sb->bar_addr[sb->pb_info.intr_vector_bar] + sb->pb_info.intr_vector_offset_1;
} else if (j >= 0x0C && j < 0x14) {
sp->port.vector =
sb->bar_addr[sb->pb_info.intr_vector_bar] + sb->pb_info.intr_vector_offset_2;
} else if (j >= 0x14 && j < 0x1C) {
sp->port.vector =
sb->bar_addr[sb->pb_info.intr_vector_bar] + sb->pb_info.intr_vector_offset_3;
} else {
}
} else if ((sb->board_flag & BOARDFLAG_CH365_32_PORTS) == BOARDFLAG_CH365_32_PORTS) {
sp->port.chip_iobase = sb->bar_addr[sb->pb_info.port[j].bar1];
sp->port.board_membase = sb->board_membase;
if (j >= 0 && j < 0x08) {
if (j >= 0 && j < 0x04) {
sp->port.port_membase = sb->board_membase + 0x100 + j * 0x10;
}
if (j >= 4 && j < 0x08) {
sp->port.port_membase = sb->board_membase + 0x100 + 0x08 + (j - 4) * 0x10;
}
}
if (j >= 0x08 && j < 0x10) {
if (j >= 0x08 && j < 0x0C) {
sp->port.port_membase = sb->board_membase + 0x100 + 0x80 + (j - 0x08) * 0x10;
}
if (j >= 0x0C && j < 0x10) {
sp->port.port_membase = sb->board_membase + 0x100 + 0x80 + 0x08 + (j - 0x0C) * 0x10;
}
}
if (j >= 0x10 && j < 0x18) {
if (j >= 0x10 && j < 0x14) {
sp->port.port_membase = sb->board_membase + 0x100 + 0x100 + (j - 0x10) * 0x10;
}
if (j >= 0x14 && j < 0x18) {
sp->port.port_membase = sb->board_membase + 0x100 + 0x100 + 0x08 + (j - 0x14) * 0x10;
}
}
if (j >= 0x18 && j < 0x20) {
if (j >= 0x18 && j < 0x1C) {
sp->port.port_membase = sb->board_membase + 0x100 + 0x180 + (j - 0x18) * 0x10;
}
if (j >= 0x1C && j < 0x20) {
sp->port.port_membase = sb->board_membase + 0x100 + 0x180 + 0x08 + (j - 0x1C) * 0x10;
}
}
} else {
sp->port.vector = sb->bar_addr[sb->pb_info.intr_vector_bar] + sb->pb_info.intr_vector_offset;
}
}
sb->vector_mask = 0xffffffff;
ser_port_index = ser_port_index + sb->ser_ports;
}
}
}
return status;
}
static int wch_ser_port_table_init(void)
{
struct wch_board *sb = NULL;
struct wch_ser_port *sp = NULL;
int status = 0;
int i;
int j;
int n;
#if WCH_DBG
printk("%s : %s\n", __FILE__, __FUNCTION__);
#endif
for (i = 0; i < WCH_BOARDS_MAX; i++) {
sb = &wch_board_table[i];
if (sb == NULL) {
status = -ENXIO;
printk("WCH Error: Board table pointer error !\n");
return status;
}
if ((sb->board_enum > 0) && (sb->ser_ports > 0)) {
n = sb->ser_port_index;
sp = &wch_ser_table[n];
if (sp == NULL) {
status = -ENXIO;
printk("WCH Error: Serial port table pointer error !\n");
return status;
}
for (j = 0; j < sb->ser_ports; j++, n++, sp++) {
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 = CRYSTAL_FREQ * 2 / 16;
sp->port.pb_info = sb->pb_info;
if (sp->port.chip_flag == WCH_BOARD_CH384_8S) {
if (n == sb->ser_port_index)
sp->port.bext1stport = true;
else
sp->port.bext1stport = false;
} else if (sp->port.chip_flag == WCH_BOARD_CH384_28S) {
if ((n == sb->ser_port_index + 4) || (n == sb->ser_port_index + 12) ||
(n == sb->ser_port_index + 20))
sp->port.bext1stport = true;
else
sp->port.bext1stport = false;
} else if (sp->port.chip_flag == WCH_BOARD_CH355_4S || sp->port.chip_flag == WCH_BOARD_CH356_4S1P ||
sp->port.chip_flag == WCH_BOARD_CH356_6S || sp->port.chip_flag == WCH_BOARD_CH356_8S ||
sp->port.chip_flag == WCH_BOARD_CH358_4S1P || sp->port.chip_flag == WCH_BOARD_CH358_8S) {
if (n == sb->ser_port_index)
sp->port.bext1stport = true;
else
sp->port.bext1stport = false;
} else if (sp->port.chip_flag == WCH_BOARD_CH359_16S) {
if ((n == sb->ser_port_index) || (n == sb->ser_port_index + 8))
sp->port.bext1stport = true;
else
sp->port.bext1stport = false;
}
if (sp->port.chip_flag == WCH_BOARD_CH351_2S || sp->port.chip_flag == WCH_BOARD_CH352_1S1P ||
sp->port.chip_flag == WCH_BOARD_CH352_2S || sp->port.chip_flag == WCH_BOARD_CH353_2S1P ||
sp->port.chip_flag == WCH_BOARD_CH353_2S1PAR || sp->port.chip_flag == WCH_BOARD_CH353_4S) {
if (n == sb->ser_port_index)
sp->port.bspe1stport = true;
else
sp->port.bspe1stport = false;
}
sp->port.irq = sb->irq;
sp->port.line = n;
sp->port.uartclk = CRYSTAL_FREQ * 2;
if (ch365_32s) {
sp->port.iotype = WCH_UPIO_MEM;
} else {
sp->port.iotype = WCH_UPIO_PORT;
}
sp->port.ldisc_stop_rx = 0;
spin_lock_init(&sp->port.lock);
if (sp->port.chip_flag == WCH_BOARD_CH351_2S) {
sp->port.type = PORT_SER_16550A;
sp->port.fifosize = CH351_FIFOSIZE_SET;
sp->port.rx_trigger = CH351_TRIGGER_LEVEL_SET;
} else if (sp->port.chip_flag == WCH_BOARD_CH352_2S || sp->port.chip_flag == WCH_BOARD_CH352_1S1P) {
sp->port.type = PORT_SER_16550A;
sp->port.fifosize = CH352_FIFOSIZE_SET;
sp->port.rx_trigger = CH352_TRIGGER_LEVEL_SET;
} else if (sp->port.chip_flag == WCH_BOARD_CH353_4S || sp->port.chip_flag == WCH_BOARD_CH353_2S1P ||
sp->port.chip_flag == WCH_BOARD_CH353_2S1PAR) {
sp->port.type = PORT_SER_16550A;
sp->port.fifosize = CH353_FIFOSIZE_SET;
sp->port.rx_trigger = CH353_TRIGGER_LEVEL_SET;
} else if (sp->port.chip_flag == WCH_BOARD_CH355_4S) {
sp->port.type = PORT_SER_16550A;
sp->port.fifosize = CH355_FIFOSIZE_SET;
sp->port.rx_trigger = CH355_TRIGGER_LEVEL_SET;
} else if (sp->port.chip_flag == WCH_BOARD_CH356_4S1P || sp->port.chip_flag == WCH_BOARD_CH356_6S ||
sp->port.chip_flag == WCH_BOARD_CH356_8S) {
sp->port.type = PORT_SER_16550A;
sp->port.fifosize = CH356_FIFOSIZE_SET;
sp->port.rx_trigger = CH356_TRIGGER_LEVEL_SET;
} else if (sp->port.chip_flag == WCH_BOARD_CH357_4S) {
sp->port.type = PORT_SER_16750;
sp->port.fifosize = CH357_FIFOSIZE_SET;
sp->port.rx_trigger = CH357_TRIGGER_LEVEL_SET;
} else if (sp->port.chip_flag == WCH_BOARD_CH358_4S1P || sp->port.chip_flag == WCH_BOARD_CH358_8S) {
sp->port.type = PORT_SER_16750;
sp->port.fifosize = CH358_FIFOSIZE_SET;
sp->port.rx_trigger = CH358_TRIGGER_LEVEL_SET;
} else if (sp->port.chip_flag == WCH_BOARD_CH359_16S) {
sp->port.type = PORT_SER_16750;
sp->port.fifosize = CH359_FIFOSIZE_SET;
sp->port.rx_trigger = CH359_TRIGGER_LEVEL_SET;
} else if (sp->port.chip_flag == WCH_BOARD_CH382_2S || sp->port.chip_flag == WCH_BOARD_CH382_2S1P) {
sp->port.type = PORT_SER_16750;
sp->port.fifosize = CH382_FIFOSIZE_SET;
sp->port.rx_trigger = CH382_TRIGGER_LEVEL_SET;
} else if (sp->port.chip_flag == WCH_BOARD_CH384_4S || sp->port.chip_flag == WCH_BOARD_CH384_4S1P) {
sp->port.type = PORT_SER_16750;
sp->port.fifosize = CH384_FIFOSIZE_SET;
sp->port.rx_trigger = CH384_TRIGGER_LEVEL_SET;
} else if (sp->port.chip_flag == WCH_BOARD_CH384_8S) {
sp->port.type = PORT_SER_16750;
sp->port.fifosize = CH358_FIFOSIZE_SET;
sp->port.rx_trigger = CH358_TRIGGER_LEVEL_SET;
} else if (sp->port.chip_flag == WCH_BOARD_CH384_28S) {
sp->port.type = PORT_SER_16750;
if (j >= 0 && j < 0x04) {
sp->port.fifosize = CH384_FIFOSIZE_SET;
sp->port.rx_trigger = CH384_TRIGGER_LEVEL_SET;
} else {
sp->port.fifosize = CH358_FIFOSIZE_SET;
sp->port.rx_trigger = CH358_TRIGGER_LEVEL_SET;
}
} else if (sp->port.chip_flag == WCH_BOARD_CH365_32S) {
sp->port.type = PORT_SER_16750;
sp->port.fifosize = CH438_FIFOSIZE_SET;
sp->port.rx_trigger = CH438_TRIGGER_LEVEL_SET;
} else {
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;
}
sp->port.setserial_flag = WCH_SER_BAUD_NOTSETSER;
}
sb->ser_isr = wch_ser_interrupt;
} else {
sb->ser_isr = NULL;
}
}
return status;
}
#if WCH_DBG
void wch_debug(void)
{
#if WCH_DBG_BOARD
struct wch_board *sb = NULL;
int i;
#endif
#if WCH_DBG_SERPORT
struct wch_ser_port *sp = NULL;
int j;
#endif
#if WCH_DBG_BOARD
printk("\n");
printk("======== board info ========\n");
for (i = 0; i < WCH_BOARDS_MAX; i++) {
sb = &wch_board_table[i];
if (sb->board_enum != -1) {
printk(" name : %s\n", sb->pb_info.board_name);
printk(" board_enum : %d\n", sb->board_enum);
printk(" board_number : %d\n", sb->board_number);
printk(" irq : %d\n", sb->irq);
printk(" vector_mask : 0x%x\n", sb->vector_mask);
printk(" bar[0] : 0x%lx\n", sb->bar_addr[0]);
printk(" bar[1] : 0x%lx\n", sb->bar_addr[1]);
printk(" bar[2] : 0x%lx\n", sb->bar_addr[2]);
printk(" bar[3] : 0x%lx\n", sb->bar_addr[3]);
printk(" bar[4] : 0x%lx\n", sb->bar_addr[4]);
printk(" bar[5] : 0x%lx\n", sb->bar_addr[5]);
printk("----------------------------\n");
}
}
printk("============================\n");
printk("\n");
#endif
#if WCH_DBG_SERPORT
printk("\n");
printk("======== serial info ========\n");
for (j = 0; j < wch_ser_port_total_cnt; j++) {
sp = &wch_ser_table[j];
if (sp->port.iobase) {
printk(" number : %d\n", j);
printk(" name : %s\n", sp->port.pb_info.board_name);
printk(" iobase : 0x%lx\n", sp->port.iobase);
printk(" chip_iobase : 0x%x\n", sp->port.chip_iobase);
printk(" irq : %d\n", sp->port.irq);
printk(" vector : 0x%lx\n", sp->port.vector);
printk(" vector_mask : 0x%x\n", sp->port.vector_mask);
printk(" chip_flag : 0x%x\n", sp->port.chip_flag);
printk(" port_flag : 0x%x\n", sp->port.port_flag);
printk("----------------------------\n");
}
}
printk("============================\n");
printk("\n");
#endif
}
#endif
#if WCH_DBG_SERIAL
void ch365_32s_test(void)
{
struct wch_board *sb = NULL;
int i;
dbg_serial("\n");
dbg_serial("======== board info ========\n");
for (i = 0; i < WCH_BOARDS_MAX; i++) {
sb = &wch_board_table[i];
if (sb->board_enum != -1) {
dbg_serial(" ch438_1_uart0 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05));
dbg_serial(" ch438_1_uart1 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x08));
dbg_serial(" ch438_1_uart2 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x10));
dbg_serial(" ch438_1_uart3 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x18));
dbg_serial(" ch438_1_uart4 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x20));
dbg_serial(" ch438_1_uart5 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x28));
dbg_serial(" ch438_1_uart6 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x30));
dbg_serial(" ch438_1_uart7 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x38));
dbg_serial("----------------------------\n");
dbg_serial(" ch438_2_uart0 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x80));
dbg_serial(" ch438_2_uart1 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x08 + 0x80));
dbg_serial(" ch438_2_uart2 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x10 + 0x80));
dbg_serial(" ch438_2_uart3 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x18 + 0x80));
dbg_serial(" ch438_2_uart4 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x20 + 0x80));
dbg_serial(" ch438_2_uart5 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x28 + 0x80));
dbg_serial(" ch438_2_uart6 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x30 + 0x80));
dbg_serial(" ch438_2_uart7 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x38 + 0x80));
dbg_serial("----------------------------\n");
dbg_serial(" ch438_3_uart0 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x100));
dbg_serial(" ch438_3_uart1 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x08 + 0x100));
dbg_serial(" ch438_3_uart2 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x10 + 0x100));
dbg_serial(" ch438_3_uart3 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x18 + 0x100));
dbg_serial(" ch438_3_uart4 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x20 + 0x100));
dbg_serial(" ch438_3_uart5 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x28 + 0x100));
dbg_serial(" ch438_3_uart6 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x30 + 0x100));
dbg_serial(" ch438_3_uart7 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x38 + 0x100));
dbg_serial("----------------------------\n");
dbg_serial(" ch438_4_uart0 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x180));
dbg_serial(" ch438_4_uart1 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x08 + 0x180));
dbg_serial(" ch438_4_uart2 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x10 + 0x180));
dbg_serial(" ch438_4_uart3 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x18 + 0x180));
dbg_serial(" ch438_4_uart4 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x20 + 0x180));
dbg_serial(" ch438_4_uart5 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x28 + 0x180));
dbg_serial(" ch438_4_uart6 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x30 + 0x180));
dbg_serial(" ch438_4_uart7 LSR = %x\n", readb(sb->board_membase + 0x100 + 0x05 + 0x38 + 0x180));
dbg_serial("----------------------------\n");
}
}
dbg_serial("============================\n");
dbg_serial("\n");
}
#endif
static int wch_register_irq(void)
{
struct wch_board *sb = NULL;
int status = 0;
int i;
unsigned long chip_iobase;
#if WCH_DBG
printk("%s : %s\n", __FILE__, __FUNCTION__);
#endif
for (i = 0; i < WCH_BOARDS_MAX; i++) {
sb = &wch_board_table[i];
if (sb == NULL) {
status = -ENXIO;
printk("WCH Error: Board table pointer error !\n");
return status;
}
if (sb->board_enum > 0) {
status = request_irq(sb->irq, wch_interrupt, IRQF_SHARED, "wch", sb);
if (status) {
printk("WCH Error: WCH Multi-I/O %s Board(bus:%d device:%d), request\n", sb->pb_info.board_name,
sb->bus_number, sb->dev_number);
printk(" IRQ %d fail, IRQ %d may be conflit with another device.\n", sb->irq, sb->irq);
return status;
}
}
if (ch365_32s) {
outb(inb(sb->bar_addr[0] + 0xF8) & 0xFE, sb->bar_addr[0] + 0xF8);
outb(((inb(sb->bar_addr[0] + 0xFA) & 0xFB) | 0x03),
sb->bar_addr[0] + 0xFA); // set read/write plus width 240ns->120ns
}
if (((sb->board_flag & BOARDFLAG_CH384_8_PORTS) == BOARDFLAG_CH384_8_PORTS) ||
((sb->board_flag & BOARDFLAG_CH384_28_PORTS) == BOARDFLAG_CH384_28_PORTS)) {
chip_iobase = sb->bar_addr[0];
if (chip_iobase) {
outb(inb(chip_iobase + 0xEB) | 0x02, chip_iobase + 0xEB);
/* set read/write plus width 120ns->210ns */
outb(inb(chip_iobase + 0xFA) | 0x10, chip_iobase + 0xFA);
}
}
}
return status;
}
static void wch_iounmap(void)
{
struct wch_board *sb = NULL;
int i;
#if WCH_DBG
printk("%s : %s\n", __FILE__, __FUNCTION__);
#endif
for (i = 0; i < WCH_BOARDS_MAX; i++) {
sb = &wch_board_table[i];
if (sb->board_enum > 0) {
iounmap(sb->board_membase);
}
}
}
static void wch_release_irq(void)
{
struct wch_board *sb = NULL;
int i;
unsigned long chip_iobase;
#if WCH_DBG
printk("%s : %s\n", __FILE__, __FUNCTION__);
#endif
for (i = 0; i < WCH_BOARDS_MAX; i++) {
sb = &wch_board_table[i];
if (sb->board_enum > 0) {
free_irq(sb->irq, sb);
}
if (ch365_32s) {
outb(inb(sb->bar_addr[0] + 0xF8) | 0x01, sb->bar_addr[0] + 0xF8);
}
if (((sb->board_flag & BOARDFLAG_CH384_8_PORTS) == BOARDFLAG_CH384_8_PORTS) ||
((sb->board_flag & BOARDFLAG_CH384_28_PORTS) == BOARDFLAG_CH384_28_PORTS)) {
chip_iobase = sb->bar_addr[0];
if (chip_iobase)
outb(inb(chip_iobase + 0xEB) & 0xFD, chip_iobase + 0xEB);
}
}
}
static struct ser_driver wch_ser_reg = {
.dev_name = "ttyWCH",
.major = WCH_TTY_MAJOR,
.minor = 0,
};
int wch_35x_init(void)
{
int status = 0;
printk("\n\n");
printk("===================== WCH Device Driver Module Install =====================\n");
printk("\n");
printk("WCH Info : Loading WCH Multi-I/O Board Driver Module\n");
printk(" -- Date : %s\n", WCH_DRIVER_DATE);
printk(" -- Version : %s\n\n", WCH_DRIVER_VERSION);
wch_ser_port_total_cnt = 0;
status = wch_pci_board_probe();
if (status != 0) {
goto step1_fail;
}
printk("------------------->pci board probe success\n");
status = wch_get_pci_board_conf();
if (status != 0) {
goto step1_fail;
}
printk("------------------->pci board conf success\n");
status = wch_assign_resource();
if (status != 0) {
goto step1_fail;
}
printk("------------------->pci assign success\n");
status = wch_ser_port_table_init();
if (status != 0) {
goto step1_fail;
}
printk("------------------->ser port table init success\n");
status = wch_register_irq();
if (status != 0) {
goto step1_fail;
}
printk("------------------->pci register irq success\n");
status = wch_ser_register_driver(&wch_ser_reg);
if (status != 0) {
goto step2_fail;
}
printk("------------------->ser register driver success\n");
status = wch_ser_register_ports(&wch_ser_reg);
if (status != 0) {
goto step3_fail;
}
#if WCH_DBG
wch_debug();
// ch365_32s_test();
#endif
printk("================================================================================\n");
return status;
step3_fail:
wch_ser_unregister_driver(&wch_ser_reg);
step2_fail:
wch_release_irq();
step1_fail:
printk("WCH Error: Couldn't Loading WCH Multi-I/O Board Driver Module correctly,\n");
printk(" please reboot system and try again. If still can't loading driver,\n");
printk(" contact support.\n\n");
printk("================================================================================\n");
return status;
}
void wch_35x_exit(void)
{
printk("\n\n");
printk("==================== WCH Device Driver Module Uninstall ====================\n");
printk("\n");
wch_ser_unregister_ports(&wch_ser_reg);
printk("***********wch_ser_unregister_ports***************\n");
wch_ser_unregister_driver(&wch_ser_reg);
printk("***********wch_ser_unregister_driver_success***********\n");
wch_iounmap();
wch_release_irq();
printk("WCH Info : Unload WCH Multi-I/O Board Driver Module Done.\n");
printk("================================================================================\n");
}