mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-24 10:11:26 +03:00
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>
890 lines
34 KiB
C
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");
|
|
}
|