diff options
Diffstat (limited to 'drivers/serial')
36 files changed, 6682 insertions, 1213 deletions
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 7a411d456e..60b0e5f1dc 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only menu "serial drivers" depends on !CONSOLE_NONE @@ -30,7 +31,7 @@ config DRIVER_SERIAL_AR933X config DRIVER_SERIAL_EFI bool "EFI serial" - depends on EFI_BOOTUP + depends on EFI_PAYLOAD config DRIVER_SERIAL_IMX depends on ARCH_IMX @@ -38,8 +39,8 @@ config DRIVER_SERIAL_IMX bool "i.MX serial driver" config DRIVER_SERIAL_STM32 - depends on ARCH_STM32MP - bool "stm32mp serial driver" + depends on ARCH_STM32 + bool "stm32 serial driver" config DRIVER_SERIAL_STM378X depends on ARCH_MXS @@ -48,15 +49,16 @@ config DRIVER_SERIAL_STM378X config DRIVER_SERIAL_AUART depends on ARCH_MXS + select STMP_DEVICE bool "i.MX23/i.MX28 application UART serial driver" config DRIVER_SERIAL_LINUX_CONSOLE - depends on LINUX + depends on SANDBOX default y bool "linux console driver" config DRIVER_SERIAL_EFI_STDIO - depends on EFI_BOOTUP + depends on EFI_PAYLOAD bool "EFI stdio driver" config DRIVER_SERIAL_MPC5XXX @@ -69,16 +71,6 @@ config DRIVER_SERIAL_CLPS711X default y bool "CLPS711X serial driver" -config DRIVER_SERIAL_ALTERA - depends on NIOS2 - default y - bool "Altera serial driver" - -config DRIVER_SERIAL_ALTERA_JTAG - depends on NIOS2 - default n - bool "Altera JTAG serial driver" - config DRIVER_SERIAL_NS16550 default n bool "NS16550 serial driver" @@ -90,12 +82,13 @@ config DRIVER_SERIAL_ATMEL default y bool "Atmel serial driver" -config DRIVER_SERIAL_NS16550_OMAP_EXTENSIONS - bool "OMAP Extensions for NS16550" +config DRIVER_SERIAL_NS16550_PCI depends on DRIVER_SERIAL_NS16550 - depends on ARCH_OMAP + depends on PCI + default y + bool "NS16550 PCI serial driver" help - Say Y here if you are using OMAP extensions to NS16550 + Enable this to get support for NS16550 UARTs connected over PCI config DRIVER_SERIAL_PL010 depends on ARCH_EP93XX @@ -104,24 +97,6 @@ config DRIVER_SERIAL_PL010 help Enable this to get support for AMBA PL010 based serial devices -config DRIVER_SERIAL_S3C_IMPROVED - bool - -config DRIVER_SERIAL_S3C - bool "Samsung S3C serial driver" - depends on ARCH_SAMSUNG - select DRIVER_SERIAL_S3C_IMPROVED if (CPU_S5PC110 || CPU_S5PV210 || CPU_S3C6410) - default y - help - Say Y here if you want to use the CONS on a Samsung S3C CPU - -config DRIVER_SERIAL_S3C_AUTOSYNC - bool "Enable auto flow" - depends on DRIVER_SERIAL_S3C - help - Say Y here if you want to use the auto flow feature of this - UART. RTS and CTS will be handled by the hardware when enabled. - config DRIVER_SERIAL_PXA bool "PXA serial driver" depends on ARCH_PXA @@ -147,4 +122,33 @@ config DRIVER_SERIAL_LPUART default y bool "LPUART serial driver" +config DRIVER_SERIAL_LPUART32 + depends on ARCH_IMX + bool "LPUART32 serial driver" + +config VIRTIO_CONSOLE + tristate "Virtio console" + depends on VIRTIO + help + Virtio console for use with hypervisors. + + Also serves as a general-purpose serial device for data + transfer between the guest and host. + +config SERIAL_SIFIVE + tristate "SiFive UART support" + depends on OFDEVICE + help + Select this option if you are building barebox for a device that + contains a SiFive UART IP block. This type of UART is present on + SiFive FU540 SoCs, among others. + +config SERIAL_SBI + tristate "RISCV Serial support over SBI's HTIF" + depends on OFDEVICE + depends on RISCV_SBI + help + Select this option if you are building barebox for a RISCV platform + that implements a serial over SBI. + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 6f9e3b7835..4887e24ee1 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_DRIVER_SERIAL_ARM_DCC) += arm_dcc.o obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o obj-$(CONFIG_DRIVER_SERIAL_AR933X) += serial_ar933x.o @@ -9,11 +10,9 @@ obj-$(CONFIG_DRIVER_SERIAL_LINUX_CONSOLE) += linux_console.o obj-$(CONFIG_DRIVER_SERIAL_MPC5XXX) += serial_mpc5xxx.o obj-$(CONFIG_DRIVER_SERIAL_CLPS711X) += serial_clps711x.o obj-$(CONFIG_DRIVER_SERIAL_NS16550) += serial_ns16550.o +obj-$(CONFIG_DRIVER_SERIAL_NS16550_PCI) += serial_ns16550_pci.o obj-$(CONFIG_DRIVER_SERIAL_PL010) += serial_pl010.o -obj-$(CONFIG_DRIVER_SERIAL_S3C) += serial_s3c.o obj-$(CONFIG_DRIVER_SERIAL_STM32) += serial_stm32.o -obj-$(CONFIG_DRIVER_SERIAL_ALTERA) += serial_altera.o -obj-$(CONFIG_DRIVER_SERIAL_ALTERA_JTAG) += serial_altera_jtag.o obj-$(CONFIG_DRIVER_SERIAL_PXA) += serial_pxa.o obj-$(CONFIG_DRIVER_SERIAL_OMAP4_USBBOOT) += serial_omap4_usbboot.o obj-$(CONFIG_DRIVER_SERIAL_AUART) += serial_auart.o @@ -21,3 +20,8 @@ obj-$(CONFIG_DRIVER_SERIAL_CADENCE) += serial_cadence.o obj-$(CONFIG_DRIVER_SERIAL_EFI_STDIO) += efi-stdio.o obj-$(CONFIG_DRIVER_SERIAL_DIGIC) += serial_digic.o obj-$(CONFIG_DRIVER_SERIAL_LPUART) += serial_lpuart.o +obj-$(CONFIG_DRIVER_SERIAL_LPUART32) += serial_lpuart32.o +obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o +obj-$(CONFIG_SERIAL_SIFIVE) += serial_sifive.o +obj-$(CONFIG_SERIAL_SBI) += serial_sbi.o +obj-$(CONFIG_SOC_LITEX) += serial_litex.o diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index 9b567e3cd2..b53ff6877b 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * (C) Copyright 2000 * Rob Taylor, Flying Pig Systems. robt@flyingpig.com. @@ -5,20 +6,6 @@ * (C) Copyright 2004 * ARM Ltd. * Philippe Robin, <philippe.robin@arm.com> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ /* Simple U-Boot driver for the PrimeCell PL010/PL011 UARTs */ @@ -67,6 +54,23 @@ to_amba_uart_port(struct console_device *uart) return container_of(uart, struct amba_uart_port, uart); } +static void pl011_rlcr(struct amba_uart_port *uart, u32 lcr) +{ + struct vendor_data *vendor = uart->vendor; + + writew(lcr, uart->base + vendor->lcrh_rx); + if (vendor->lcrh_tx != vendor->lcrh_rx) { + int i; + /* + * Wait 10 PCLKs before writing LCRH_TX register, + * to get this delay write read only register 10 times + */ + for (i = 0; i < 10; ++i) + writew(0xff, uart->base + UART011_MIS); + writew(lcr, uart->base + vendor->lcrh_tx); + } +} + static int pl011_setbaudrate(struct console_device *cdev, int baudrate) { struct amba_uart_port *uart = to_amba_uart_port(cdev); @@ -74,6 +78,7 @@ static int pl011_setbaudrate(struct console_device *cdev, int baudrate) unsigned int divider; unsigned int remainder; unsigned int fraction; + uint32_t cr; /* ** Set baud rate @@ -87,9 +92,15 @@ static int pl011_setbaudrate(struct console_device *cdev, int baudrate) temp = (8 * remainder) / baudrate; fraction = (temp >> 1) + (temp & 1); + cr = readl(uart->base + UART011_CR); + writel(0x0, uart->base + UART011_CR); + writel(divider, uart->base + UART011_IBRD); writel(fraction, uart->base + UART011_FBRD); + pl011_rlcr(uart, UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN); + writel(cr, uart->base + UART011_CR); + return 0; } @@ -131,23 +142,6 @@ static int pl011_tstc(struct console_device *cdev) return !(readl(uart->base + UART01x_FR) & UART01x_FR_RXFE); } -static void pl011_rlcr(struct amba_uart_port *uart, u32 lcr) -{ - struct vendor_data *vendor = uart->vendor; - - writew(lcr, uart->base + vendor->lcrh_rx); - if (vendor->lcrh_tx != vendor->lcrh_rx) { - int i; - /* - * Wait 10 PCLKs before writing LCRH_TX register, - * to get this delay write read only register 10 times - */ - for (i = 0; i < 10; ++i) - writew(0xff, uart->base + UART011_MIS); - writew(lcr, uart->base + vendor->lcrh_tx); - } -} - static int pl011_init_port(struct console_device *cdev) { struct amba_uart_port *uart = to_amba_uart_port(cdev); @@ -207,6 +201,9 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) cdev->putc = pl011_putc; cdev->getc = pl011_getc; cdev->setbrg = pl011_setbaudrate; + cdev->linux_console_name = "ttyAMA"; + cdev->linux_earlycon_name = "pl011"; + cdev->phys_base = uart->base; pl011_init_port(cdev); @@ -239,10 +236,4 @@ struct amba_driver pl011_driver = { .id_table = pl011_ids, }; -static int pl011_init(void) -{ - amba_driver_register(&pl011_driver); - return 0; -} - -console_initcall(pl011_init); +console_amba_driver(pl011_driver); diff --git a/drivers/serial/arm_dcc.c b/drivers/serial/arm_dcc.c index e5f2bbe5e2..1e2b4e425c 100644 --- a/drivers/serial/arm_dcc.c +++ b/drivers/serial/arm_dcc.c @@ -1,26 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH eCos-exception-2.0 */ /* * Copyright (C) 2004-2007 ARM Limited. * Copyright (C) 2008 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * As a special exception, if other files instantiate templates or use macros - * or inline functions from this file, or you compile this file and link it - * with other works to produce a work based on this file, this file does not - * by itself cause the resulting work to be covered by the GNU General Public - * License. However the source code for this file must still be made available - * in accordance with section (3) of the GNU General Public License. - - * This exception does not invalidate any other reasons why a work based on - * this file might be covered by the GNU General Public License. */ #include <common.h> @@ -127,7 +108,7 @@ static int arm_dcc_tstc(struct console_device *cdev) static struct console_device arm_dcc_dev; -static int arm_dcc_probe(struct device_d *dev) +static int arm_dcc_probe(struct device *dev) { struct console_device *cdev; @@ -144,13 +125,13 @@ static int arm_dcc_probe(struct device_d *dev) return 0; } -static struct driver_d arm_dcc_driver = { +static struct driver arm_dcc_driver = { .name = "arm_dcc", .probe = arm_dcc_probe, }; console_platform_driver(arm_dcc_driver); -static struct device_d arm_dcc_device = { +static struct device arm_dcc_device = { .id = DEVICE_ID_DYNAMIC, .name = "arm_dcc", }; diff --git a/drivers/serial/atmel.c b/drivers/serial/atmel.c index 8394273f9f..b957b75284 100644 --- a/drivers/serial/atmel.c +++ b/drivers/serial/atmel.c @@ -1,17 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * (c) 2004 Sascha Hauer <sascha@saschahauer.de> - * - * 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, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ #include <common.h> @@ -395,7 +384,7 @@ static int atmel_serial_set_mode(struct console_device *cdev, enum console_mode */ static int atmel_serial_init_port(struct console_device *cdev) { - struct device_d *dev = cdev->dev; + struct device *dev = cdev->dev; struct atmel_uart_port *uart = to_atmel_uart_port(cdev); uart->base = dev_request_mem_region_err_null(dev, 0); @@ -423,10 +412,11 @@ static int atmel_serial_init_port(struct console_device *cdev) return 0; } -static int atmel_serial_probe(struct device_d *dev) +static int atmel_serial_probe(struct device *dev) { struct atmel_uart_port *uart; struct console_device *cdev; + int ret; uart = xzalloc(sizeof(struct atmel_uart_port)); cdev = &uart->uart; @@ -437,8 +427,13 @@ static int atmel_serial_probe(struct device_d *dev) cdev->setbrg = atmel_serial_setbaudrate; cdev->set_mode = atmel_serial_set_mode; cdev->linux_console_name = "ttyAT"; + cdev->linux_earlycon_name = "atmel_serial"; + + ret = atmel_serial_init_port(cdev); + if (ret) + return ret; - atmel_serial_init_port(cdev); + cdev->phys_base = uart->base; /* Enable UART */ @@ -452,8 +447,9 @@ static const struct of_device_id __maybe_unused atmel_serial_dt_ids[] = { { .compatible = "atmel,at91sam9260-usart" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, atmel_serial_dt_ids); -static struct driver_d atmel_serial_driver = { +static struct driver atmel_serial_driver = { .name = "atmel_usart", .probe = atmel_serial_probe, .of_compatible = DRV_OF_COMPAT(atmel_serial_dt_ids), diff --git a/drivers/serial/efi-stdio.c b/drivers/serial/efi-stdio.c index 9e825181e6..92133f8378 100644 --- a/drivers/serial/efi-stdio.c +++ b/drivers/serial/efi-stdio.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * efi_console.c - EFI console support * * Copyright (c) 2014 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> #include <driver.h> @@ -25,63 +13,32 @@ #include <efi.h> #include <readkey.h> #include <linux/ctype.h> -#include <efi/efi.h> +#include <efi/efi-payload.h> +#include <kfifo.h> #include <efi/efi-device.h> -#include "efi-stdio.h" - -#define EFI_SHIFT_STATE_VALID 0x80000000 -#define EFI_RIGHT_CONTROL_PRESSED 0x00000004 -#define EFI_LEFT_CONTROL_PRESSED 0x00000008 -#define EFI_RIGHT_ALT_PRESSED 0x00000010 -#define EFI_LEFT_ALT_PRESSED 0x00000020 - -#define EFI_CONTROL_PRESSED (EFI_RIGHT_CONTROL_PRESSED | EFI_LEFT_CONTROL_PRESSED) -#define EFI_ALT_PRESSED (EFI_RIGHT_ALT_PRESSED | EFI_LEFT_ALT_PRESSED) -#define KEYPRESS(keys, scan, uni) ((((uint64_t)keys) << 32) | ((scan) << 16) | (uni)) -#define KEYCHAR(k) ((k) & 0xffff) -#define CHAR_CTRL(c) ((c) - 'a' + 1) - -#define EFI_BLACK 0x00 -#define EFI_BLUE 0x01 -#define EFI_GREEN 0x02 -#define EFI_CYAN (EFI_BLUE | EFI_GREEN) -#define EFI_RED 0x04 -#define EFI_MAGENTA (EFI_BLUE | EFI_RED) -#define EFI_BROWN (EFI_GREEN | EFI_RED) -#define EFI_LIGHTGRAY (EFI_BLUE | EFI_GREEN | EFI_RED) -#define EFI_BRIGHT 0x08 -#define EFI_DARKGRAY (EFI_BRIGHT) -#define EFI_LIGHTBLUE (EFI_BLUE | EFI_BRIGHT) -#define EFI_LIGHTGREEN (EFI_GREEN | EFI_BRIGHT) -#define EFI_LIGHTCYAN (EFI_CYAN | EFI_BRIGHT) -#define EFI_LIGHTRED (EFI_RED | EFI_BRIGHT) -#define EFI_LIGHTMAGENTA (EFI_MAGENTA | EFI_BRIGHT) -#define EFI_YELLOW (EFI_BROWN | EFI_BRIGHT) -#define EFI_WHITE (EFI_BLUE | EFI_GREEN | EFI_RED | EFI_BRIGHT) - -#define EFI_TEXT_ATTR(f,b) ((f) | ((b) << 4)) - -#define EFI_BACKGROUND_BLACK 0x00 -#define EFI_BACKGROUND_BLUE 0x10 -#define EFI_BACKGROUND_GREEN 0x20 -#define EFI_BACKGROUND_CYAN (EFI_BACKGROUND_BLUE | EFI_BACKGROUND_GREEN) -#define EFI_BACKGROUND_RED 0x40 -#define EFI_BACKGROUND_MAGENTA (EFI_BACKGROUND_BLUE | EFI_BACKGROUND_RED) -#define EFI_BACKGROUND_BROWN (EFI_BACKGROUND_GREEN | EFI_BACKGROUND_RED) -#define EFI_BACKGROUND_LIGHTGRAY (EFI_BACKGROUND_BLUE | EFI_BACKGROUND_GREEN | EFI_BACKGROUND_RED) +#include <efi/efi-stdio.h> struct efi_console_priv { struct efi_simple_text_output_protocol *out; - struct efi_simple_input_interface *in; + struct efi_simple_text_input_protocol *in; struct efi_simple_text_input_ex_protocol *inex; struct console_device cdev; - int lastkey; - u16 efi_console_buffer[CONFIG_CBSIZE]; + u16 efi_console_buffer[CONFIG_CBSIZE + 1]; + int pos; + + struct kfifo *inputbuffer; unsigned long columns, rows; - int current_color; + int fg; + int bg; + bool inverse; s16 *blank_line; + + struct param_d *param_mode; + const char **mode_names; + int *mode_num; + unsigned int var_mode; }; static inline struct efi_console_priv *to_efi(struct console_device *cdev) @@ -122,17 +79,23 @@ static int xlate_keypress(struct efi_input_key *k) return k->unicode_char & 0xff; } +static void efi_wait_single_event(struct efi_event *event) +{ + size_t index; + + /* wait until key is pressed */ + BS->wait_for_event(1, &event, &index); +} + static int efi_read_key(struct efi_console_priv *priv, bool wait) { - unsigned long index; efi_status_t efiret; struct efi_key_data kd; - /* wait until key is pressed */ - if (wait) - BS->wait_for_event(1, priv->in->wait_for_key, &index); - if (priv->inex) { + if (wait) + efi_wait_single_event(priv->inex->wait_for_key_ex); + efiret = priv->inex->read_key_stroke_ex(priv->inex, &kd); if (efiret == EFI_NOT_READY) @@ -161,6 +124,9 @@ static int efi_read_key(struct efi_console_priv *priv, bool wait) } } + if (wait) + efi_wait_single_event(priv->in->wait_for_key); + efiret = priv->in->read_key_stroke(priv->in, &kd.key); if (EFI_ERROR(efiret)) @@ -169,148 +135,221 @@ static int efi_read_key(struct efi_console_priv *priv, bool wait) return xlate_keypress(&kd.key); } -static void efi_console_putc(struct console_device *cdev, char c) +static void clear_to_eol(struct efi_console_priv *priv) { - uint16_t str[2] = {}; - struct efi_console_priv *priv = to_efi(cdev); - struct efi_simple_text_output_protocol *con_out = priv->out; + int pos = priv->out->mode->cursor_column; - str[0] = c; + priv->out->output_string(priv->out, priv->blank_line + pos); +} - con_out->output_string(con_out, str); +static int ansi_to_efi_color(int ansi) +{ + switch (ansi) { + case 30: + return EFI_BLACK; + case 31: + return EFI_RED; + case 32: + return EFI_GREEN; + case 33: + return EFI_YELLOW; + case 34: + return EFI_BLUE; + case 35: + return EFI_MAGENTA; + case 36: + return EFI_CYAN; + case 37: + return EFI_WHITE; + case 39: + return EFI_LIGHTGRAY; + } + + return -1; } -static void clear_to_eol(struct efi_console_priv *priv) +static void set_fg_bg_colors(struct efi_console_priv *priv) { - int pos = priv->out->mode->cursor_column; + int fg = priv->inverse ? priv->bg : priv->fg; + int bg = priv->inverse ? priv->fg : priv->bg; - priv->out->output_string(priv->out, priv->blank_line + pos); + priv->out->set_attribute(priv->out, EFI_TEXT_ATTR(fg , bg)); } static int efi_process_square_bracket(struct efi_console_priv *priv, const char *inp) { - int x, y; char *endp; + int n, retlen; + int arg0 = -1, arg1 = -1, arg2 = -1; + char *buf; - inp++; - - switch (*inp) { - case 'A': - /* Cursor up */ - case 'B': - /* Cursor down */ - case 'C': - /* Cursor right */ - case 'D': - /* Cursor left */ - case 'H': - /* home */ - case 'F': - /* end */ - return 3; - case 'K': - clear_to_eol(priv); - return 3; - } - - if (*inp == '2' && *(inp + 1) == 'J') { - priv->out->clear_screen(priv->out); - return 4; - } + endp = strpbrk(inp, "ABCDEFGHJKmrnhl"); + if (!endp) + return 0; - if (*inp == '0' && *(inp + 1) == 'm') { - priv->out->set_attribute(priv->out, - EFI_TEXT_ATTR(EFI_WHITE, EFI_BLACK)); - return 4; - } + retlen = endp - inp + 1; - if (*inp == '7' && *(inp + 1) == 'm') { - priv->out->set_attribute(priv->out, - EFI_TEXT_ATTR(EFI_BLACK, priv->current_color)); - return 4; - } + inp++; - if (*inp == '1' && - *(inp + 1) == ';' && - *(inp + 2) == '3' && - *(inp + 3) && - *(inp + 4) == 'm') { - int color; - switch (*(inp + 3)) { - case '1': color = EFI_RED; break; - case '4': color = EFI_BLUE; break; - case '2': color = EFI_GREEN; break; - case '6': color = EFI_CYAN; break; - case '3': color = EFI_YELLOW; break; - case '5': color = EFI_MAGENTA; break; - case '7': color = EFI_WHITE; break; - default: color = EFI_WHITE; break; + if (isdigit(*inp)) { + char *e; + arg0 = simple_strtoul(inp, &e, 10); + if (*e == ';') { + arg1 = simple_strtoul(e + 1, &e, 10); + if (*e == ';') + arg2 = simple_strtoul(e + 1, &e, 10); } - - priv->current_color = color; - - priv->out->set_attribute(priv->out, - EFI_TEXT_ATTR(color, EFI_BLACK)); - return 7; + } else if (*inp == '?') { + arg0 = simple_strtoul(inp + 1, NULL, 10); } - y = simple_strtoul(inp, &endp, 10); - if (*endp == ';') { - x = simple_strtoul(endp + 1, &endp, 10); - if (*endp == 'H') { - priv->out->set_cursor_position(priv->out, x - 1, y - 1); - return endp - inp + 3; + switch (*endp) { + case 'K': + switch (arg0) { + case 0: + case -1: + clear_to_eol(priv); + break; + } + break; + case 'J': + switch (arg0) { + case 2: + priv->out->clear_screen(priv->out); + break; + } + break; + case 'H': + if (arg0 >= 0 && arg1 >= 0) { + int row = min_t(int, arg0, priv->rows); + int col = min_t(int, arg1, priv->columns); + priv->out->set_cursor_position(priv->out, col - 1, row - 1); + } + break; + case 'm': + switch (arg0) { + case 0: + priv->inverse = false; + priv->fg = EFI_LIGHTGRAY; + priv->bg = EFI_BLACK; + set_fg_bg_colors(priv); + break; + case 7: + priv->inverse = true; + set_fg_bg_colors(priv); + break; + case 1: + priv->fg = ansi_to_efi_color(arg1); + if (priv->fg < 0) + priv->fg = EFI_LIGHTGRAY; + priv->bg = ansi_to_efi_color(arg2); + if (priv->bg < 0) + priv->bg = EFI_BLACK; + set_fg_bg_colors(priv); + break; } + break; + case 'n': + switch (arg0) { + case 6: + n = asprintf(&buf, "\033[%d;%dR", priv->out->mode->cursor_row + 1, + priv->out->mode->cursor_column + 1); + kfifo_put(priv->inputbuffer, buf, n); + free(buf); + break; + } + break; + case 'h': + if (*inp == '?' && arg0 == 25) + priv->out->enable_cursor(priv->out, true); + break; + case 'l': + if (*inp == '?' && arg0 == 25) + priv->out->enable_cursor(priv->out, false); + break; } - return 8; + return retlen; } -static int efi_process_key(struct efi_console_priv *priv, const char *inp) +static int efi_process_escape(struct efi_console_priv *priv, const char *inp) { char c; c = *inp; - if (c != 27) - return 0; - inp++; if (*inp == '[') - return efi_process_square_bracket(priv, inp); + return efi_process_square_bracket(priv, inp) + 1; return 1; } +static void efi_console_add_char(struct efi_console_priv *priv, int c) +{ + if (priv->pos >= CONFIG_CBSIZE) + return; + + priv->efi_console_buffer[priv->pos] = c; + priv->pos++; +} + +static void efi_console_flush(struct efi_console_priv *priv) +{ + priv->efi_console_buffer[priv->pos] = 0; + + priv->out->output_string(priv->out, priv->efi_console_buffer); + + priv->pos = 0; +} + static int efi_console_puts(struct console_device *cdev, const char *s, size_t nbytes) { struct efi_console_priv *priv = to_efi(cdev); - int n = 0; - - while (nbytes--) { - if (*s == 27) { - priv->efi_console_buffer[n] = 0; - priv->out->output_string(priv->out, - priv->efi_console_buffer); - n = 0; - s += efi_process_key(priv, s); - continue; + int n, pos = 0; + + while (pos < nbytes) { + switch (s[pos]) { + case 27: + efi_console_flush(priv); + pos += efi_process_escape(priv, s + pos); + break; + case '\n': + efi_console_add_char(priv, '\r'); + efi_console_add_char(priv, '\n'); + pos++; + break; + case '\t': + efi_console_flush(priv); + n = 8 - priv->out->mode->cursor_column % 8; + while (n--) + efi_console_add_char(priv, ' '); + pos++; + break; + case '\b': + n = priv->out->mode->cursor_column; + if (n > 0) + priv->out->set_cursor_position(priv->out, + n - 1, priv->out->mode->cursor_row); + pos++; + break; + default: + efi_console_add_char(priv, s[pos]); + pos++; + break; } - - if (*s == '\n') - priv->efi_console_buffer[n++] = '\r'; - priv->efi_console_buffer[n] = *s; - s++; - n++; } - priv->efi_console_buffer[n] = 0; + efi_console_flush(priv); - priv->out->output_string(priv->out, priv->efi_console_buffer); + return nbytes; +} - return n; +static void efi_console_putc(struct console_device *cdev, char c) +{ + efi_console_puts(cdev, &c, 1); } static int efi_console_tstc(struct console_device *cdev) @@ -318,14 +357,14 @@ static int efi_console_tstc(struct console_device *cdev) struct efi_console_priv *priv = to_efi(cdev); int key; - if (priv->lastkey > 0) + if (kfifo_len(priv->inputbuffer)) return 1; key = efi_read_key(priv, 0); if (key < 0) return 0; - priv->lastkey = key; + kfifo_putc(priv->inputbuffer, key); return 1; } @@ -333,58 +372,74 @@ static int efi_console_tstc(struct console_device *cdev) static int efi_console_getc(struct console_device *cdev) { struct efi_console_priv *priv = to_efi(cdev); - int key; + unsigned char c; - if (priv->lastkey > 0) { - key = priv->lastkey; - priv->lastkey = -1; - return key; - } + if (!kfifo_getc(priv->inputbuffer, &c)) + return c; return efi_read_key(priv, 1); } +static int efi_console_set_mode(struct param_d *param, void *p) +{ + struct efi_console_priv *priv = p; + + priv->out->set_mode(priv->out, priv->mode_num[priv->var_mode]); + + priv->out->query_mode(priv->out, priv->out->mode->mode, + &priv->columns, &priv->rows); + return 0; +} + static void efi_set_mode(struct efi_console_priv *priv) { -#if 0 int i; - unsigned long rows, columns, best = 0, mode = 0; + unsigned long rows, columns; + int n = 0; efi_status_t efiret; + priv->mode_names = xzalloc(priv->out->mode->max_mode * sizeof(*priv->mode_names)); + priv->mode_num = xzalloc(priv->out->mode->max_mode * sizeof(*priv->mode_num)); + + priv->out->query_mode(priv->out, priv->out->mode->mode, &priv->columns, &priv->rows); + for (i = 0; i < priv->out->mode->max_mode; i++) { - priv->out->query_mode(priv->out, i, &columns, &rows); - printf("%d: %ld %ld\n", i, columns, rows); - if (rows * columns > best) { - best = rows * columns; - mode = i; - } + efiret = priv->out->query_mode(priv->out, i, &columns, &rows); + if (EFI_ERROR(efiret)) + continue; + + if (columns == priv->columns && rows == priv->rows) + priv->var_mode = n; + + priv->mode_names[n] = basprintf("%ldx%ld", columns, rows); + priv->mode_num[n] = i; + n++; } - /* - * Setting the mode doesn't work as expected. set_mode succeeds, but - * the graphics resolution is not changed. - */ - priv->out->set_mode(priv->out, mode); -#endif - priv->out->query_mode(priv->out, priv->out->mode->mode, &priv->columns, &priv->rows); + priv->param_mode = dev_add_param_enum(&priv->cdev.class_dev, "mode", + efi_console_set_mode, NULL, &priv->var_mode, + priv->mode_names, n, priv); } -static int efi_console_probe(struct device_d *dev) +static int efi_console_probe(struct device *dev) { efi_guid_t inex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; struct efi_simple_text_input_ex_protocol *inex; struct console_device *cdev; struct efi_console_priv *priv; efi_status_t efiret; - - int i; + int i, ret; priv = xzalloc(sizeof(*priv)); priv->out = efi_sys_table->con_out; priv->in = efi_sys_table->con_in; - efiret = BS->open_protocol((void *)efi_sys_table->con_in_handle, + priv->inputbuffer = kfifo_alloc(128); + if (!priv->inputbuffer) + return -ENOMEM; + + efiret = BS->open_protocol(efi_sys_table->con_in_handle, &inex_guid, (void **)&inex, efi_parent_image, @@ -396,9 +451,8 @@ static int efi_console_probe(struct device_d *dev) dev_dbg(dev, "Using simple_text_input_ex_protocol\n"); } - priv->current_color = EFI_WHITE; - - efi_set_mode(priv); + priv->fg = EFI_LIGHTGRAY; + priv->bg = EFI_BLACK; priv->out->enable_cursor(priv->out, 1); @@ -413,12 +467,16 @@ static int efi_console_probe(struct device_d *dev) cdev->putc = efi_console_putc; cdev->puts = efi_console_puts; - priv->lastkey = -1; + ret = console_register(cdev); + if (ret) + return ret; + + efi_set_mode(priv); - return console_register(cdev); + return 0; } -static struct driver_d efi_console_driver = { +static struct driver efi_console_driver = { .name = "efi-stdio", .probe = efi_console_probe, }; diff --git a/drivers/serial/efi-stdio.h b/drivers/serial/efi-stdio.h deleted file mode 100644 index 1fa417c706..0000000000 --- a/drivers/serial/efi-stdio.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef EFI_STDIO_H_ -#define EFI_STDIO_H_ - -#include <efi.h> - -struct efi_simple_text_input_ex_protocol; - -typedef efi_status_t (EFIAPI *efi_input_reset_ex)( - struct efi_simple_text_input_ex_protocol *this, - efi_bool_t extended_verification -); - -struct efi_key_state { - u32 shift_state; - u8 toggle_state; -}; - -struct efi_key_data { - struct efi_input_key key; - struct efi_key_state state; -}; - -typedef efi_status_t (EFIAPI *efi_input_read_key_ex)( - struct efi_simple_text_input_ex_protocol *this, - struct efi_key_data *keydata -); - -typedef efi_status_t (EFIAPI *efi_set_state)( - struct efi_simple_text_input_ex_protocol *this, - u8 *key_toggle_state -); - -typedef efi_status_t (EFIAPI *efi_key_notify_function)( - struct efi_key_data *keydata -); - -typedef efi_status_t (EFIAPI *efi_register_keystroke_notify)( - struct efi_simple_text_input_ex_protocol *this, - struct efi_key_data keydata, - efi_key_notify_function key_notification_function, - void **notify_handle -); - -typedef efi_status_t (EFIAPI *efi_unregister_keystroke_notify)( - struct efi_simple_text_input_ex_protocol *this, - void *notification_handle -); - -struct efi_simple_text_input_ex_protocol { - efi_input_reset_ex reset; - efi_input_read_key_ex read_key_stroke_ex; - void *wait_for_key_ex; - efi_set_state set_state; - efi_register_keystroke_notify register_key_notify; - efi_unregister_keystroke_notify unregister_key_notify; -}; - -#endif diff --git a/drivers/serial/linux_console.c b/drivers/serial/linux_console.c index 0d5da9d1b0..b8303dfd12 100644 --- a/drivers/serial/linux_console.c +++ b/drivers/serial/linux_console.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * linux_console.c - Use stdin/stdout as a console device * * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> @@ -27,7 +15,7 @@ static void linux_console_putc(struct console_device *cdev, char c) { - struct device_d *dev = cdev->dev; + struct device *dev = cdev->dev; struct linux_console_data *d = dev->platform_data; linux_write(d->stdoutfd, &c, 1); @@ -35,7 +23,7 @@ static void linux_console_putc(struct console_device *cdev, char c) static int linux_console_tstc(struct console_device *cdev) { - struct device_d *dev = cdev->dev; + struct device *dev = cdev->dev; struct linux_console_data *d = dev->platform_data; return linux_tstc(d->stdinfd); @@ -43,7 +31,7 @@ static int linux_console_tstc(struct console_device *cdev) static int linux_console_getc(struct console_device *cdev) { - struct device_d *dev = cdev->dev; + struct device *dev = cdev->dev; struct linux_console_data *d = dev->platform_data; static char old_c; char c; @@ -57,7 +45,7 @@ static int linux_console_getc(struct console_device *cdev) return c; } -static int linux_console_probe(struct device_d *dev) +static int linux_console_probe(struct device *dev) { struct console_device *cdev; struct linux_console_data *data = dev->platform_data; @@ -79,7 +67,7 @@ static int linux_console_probe(struct device_d *dev) return 0; } -static struct driver_d linux_console_driver = { +static struct driver linux_console_driver = { .name = "console", .probe = linux_console_probe, }; diff --git a/drivers/serial/serial_altera.c b/drivers/serial/serial_altera.c deleted file mode 100644 index 10d1506bca..0000000000 --- a/drivers/serial/serial_altera.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * (C) Copyright 2011, Franck JULLIEN, <elec4fun@gmail.com> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <common.h> -#include <driver.h> -#include <init.h> -#include <malloc.h> -#include <io.h> -#include <asm/nios2-io.h> - -struct altera_serial_priv { - struct console_device cdev; - void __iomem *regs; -}; - -static int altera_serial_setbaudrate(struct console_device *cdev, int baudrate) -{ - struct altera_serial_priv *priv = container_of(cdev, - struct altera_serial_priv, cdev); - - struct nios_uart *uart = priv->regs; - uint16_t div; - - div = (CPU_FREQ / baudrate) - 1; - writew(div, &uart->divisor); - - return 0; -} - -static void altera_serial_putc(struct console_device *cdev, char c) -{ - struct altera_serial_priv *priv = container_of(cdev, - struct altera_serial_priv, cdev); - - struct nios_uart *uart = priv->regs; - - while ((readw(&uart->status) & NIOS_UART_TRDY) == 0); - - writew(c, &uart->txdata); -} - -static int altera_serial_tstc(struct console_device *cdev) -{ - struct altera_serial_priv *priv = container_of(cdev, - struct altera_serial_priv, cdev); - - struct nios_uart *uart = priv->regs; - - return readw(&uart->status) & NIOS_UART_RRDY; -} - -static int altera_serial_getc(struct console_device *cdev) -{ - struct altera_serial_priv *priv = container_of(cdev, - struct altera_serial_priv, cdev); - - struct nios_uart *uart = priv->regs; - - while (altera_serial_tstc(cdev) == 0); - - return readw(&uart->rxdata) & 0x000000FF; -} - -static int altera_serial_probe(struct device_d *dev) -{ - struct resource *iores; - struct console_device *cdev; - struct altera_serial_priv *priv; - - priv = xzalloc(sizeof(*priv)); - cdev = &priv->cdev; - - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - priv->regs = IOMEM(iores->start); - cdev->dev = dev; - cdev->tstc = altera_serial_tstc; - cdev->putc = altera_serial_putc; - cdev->getc = altera_serial_getc; - cdev->setbrg = altera_serial_setbaudrate; - - console_register(cdev); - - return 0; -} - -static struct driver_d altera_serial_driver = { - .name = "altera_serial", - .probe = altera_serial_probe, -}; -console_platform_driver(altera_serial_driver); diff --git a/drivers/serial/serial_altera_jtag.c b/drivers/serial/serial_altera_jtag.c deleted file mode 100644 index 0164ea5eff..0000000000 --- a/drivers/serial/serial_altera_jtag.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * (C) Copyright 2004, Psyent Corporation <www.psyent.com> - * Scott McNutt <smcnutt@psyent.com> - * - * (C) Copyright 2011 - Franck JULLIEN <elec4fun@gmail.com> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <common.h> -#include <driver.h> -#include <init.h> -#include <malloc.h> -#include <io.h> -#include <asm/nios2-io.h> - -struct altera_serial_jtag_priv { - struct console_device cdev; - void __iomem *regs; -}; - - -static int altera_serial_jtag_setbaudrate(struct console_device *cdev, int baudrate) -{ - return 0; -} - -static void altera_serial_jtag_putc(struct console_device *cdev, char c) -{ - struct altera_serial_jtag_priv *priv = container_of(cdev, - struct altera_serial_jtag_priv, cdev); - - struct nios_jtag *jtag = priv->regs; - uint32_t st; - - while (1) { - st = readl(&jtag->control); - if (NIOS_JTAG_WSPACE(st)) - break; - } - - writel(c, &jtag->data); -} - -static int altera_serial_jtag_tstc(struct console_device *cdev) -{ - struct altera_serial_jtag_priv *priv = container_of(cdev, - struct altera_serial_jtag_priv, cdev); - - struct nios_jtag *jtag = priv->regs; - - return readl(&jtag->control) & NIOS_JTAG_RRDY; -} - -static int altera_serial_jtag_getc(struct console_device *cdev) -{ - struct altera_serial_jtag_priv *priv = container_of(cdev, - struct altera_serial_jtag_priv, cdev); - - struct nios_jtag *jtag = priv->regs; - uint32_t val; - - while (1) { - val = readl(&jtag->data); - if (val & NIOS_JTAG_RVALID) - break; - } - - return val & 0xFF; -} - -static int altera_serial_jtag_probe(struct device_d *dev) { - struct resource *iores; - - struct console_device *cdev; - struct altera_serial_jtag_priv *priv; - - priv = xzalloc(sizeof(*priv)); - cdev = &priv->cdev; - - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - priv->regs = IOMEM(iores->start); - cdev->dev = dev; - cdev->tstc = altera_serial_jtag_tstc; - cdev->putc = altera_serial_jtag_putc; - cdev->getc = altera_serial_jtag_getc; - cdev->setbrg = altera_serial_jtag_setbaudrate; - - console_register(cdev); - - return 0; -} - -static struct driver_d altera_serial_jtag_driver = { - .name = "altera_serial_jtag", - .probe = altera_serial_jtag_probe, -}; -console_platform_driver(altera_serial_jtag_driver); diff --git a/drivers/serial/serial_ar933x.c b/drivers/serial/serial_ar933x.c index dda604652b..f5595ec1ee 100644 --- a/drivers/serial/serial_ar933x.c +++ b/drivers/serial/serial_ar933x.c @@ -1,19 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * based on linux.git/drivers/tty/serial/serial_ar933x.c - * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> @@ -21,7 +8,7 @@ #include <init.h> #include <malloc.h> #include <io.h> -#include <asm-generic/div64.h> +#include <linux/math64.h> #include <linux/clk.h> #include <linux/err.h> @@ -154,7 +141,7 @@ static int ar933x_serial_getc(struct console_device *cdev) return rdata & AR933X_UART_DATA_TX_RX_MASK; } -static int ar933x_serial_probe(struct device_d *dev) +static int ar933x_serial_probe(struct device *dev) { struct resource *iores; struct console_device *cdev; @@ -201,8 +188,9 @@ static struct of_device_id ar933x_serial_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, ar933x_serial_dt_ids); -static struct driver_d ar933x_serial_driver = { +static struct driver ar933x_serial_driver = { .name = "ar933x_serial", .probe = ar933x_serial_probe, .of_compatible = DRV_OF_COMPAT(ar933x_serial_dt_ids), diff --git a/drivers/serial/serial_ar933x.h b/drivers/serial/serial_ar933x.h index f55f0fa3d7..d73e77a603 100644 --- a/drivers/serial/serial_ar933x.h +++ b/drivers/serial/serial_ar933x.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Atheros AR933X UART defines * * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. */ #ifndef __AR933X_UART_H diff --git a/drivers/serial/serial_auart.c b/drivers/serial/serial_auart.c index 05cc757970..217ac5c891 100644 --- a/drivers/serial/serial_auart.c +++ b/drivers/serial/serial_auart.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * (C) 2013 Marc Kleine-Budde <mkl@pengutronix.de> * @@ -23,17 +24,6 @@ * * Copyright 2008-2010 Freescale Semiconductor, Inc. * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. - * - * 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, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> @@ -46,8 +36,6 @@ #include <linux/clk.h> #include <linux/err.h> -#include <mach/clock.h> - #define HW_UARTAPP_CTRL0 (0x00000000) #define HW_UARTAPP_CTRL2 (0x00000020) @@ -178,7 +166,7 @@ static void auart_serial_init_port(struct auart_priv *priv) writel(0x0, priv->base + HW_UARTAPP_INTR); } -static int auart_serial_probe(struct device_d *dev) +static int auart_serial_probe(struct device *dev) { struct resource *iores; struct auart_priv *priv; @@ -193,6 +181,7 @@ static int auart_serial_probe(struct device_d *dev) cdev->flush = auart_serial_flush; cdev->setbrg = auart_serial_setbaudrate; cdev->dev = dev; + cdev->linux_console_name = "ttyAPP"; dev->priv = priv; iores = dev_request_mem_resource(dev, 0); @@ -227,8 +216,9 @@ static const __maybe_unused struct of_device_id auart_serial_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, auart_serial_dt_ids); -static struct driver_d auart_serial_driver = { +static struct driver auart_serial_driver = { .name = "auart_serial", .probe = auart_serial_probe, .of_compatible = DRV_OF_COMPAT(auart_serial_dt_ids), diff --git a/drivers/serial/serial_cadence.c b/drivers/serial/serial_cadence.c index 6454888e3c..ee162e541a 100644 --- a/drivers/serial/serial_cadence.c +++ b/drivers/serial/serial_cadence.c @@ -1,58 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de> - * - * 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, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ + #include <common.h> #include <driver.h> #include <init.h> -#include <malloc.h> -#include <notifier.h> #include <io.h> -#include <linux/err.h> #include <linux/clk.h> - -#define CADENCE_UART_CONTROL 0x00 -#define CADENCE_UART_MODE 0x04 -#define CADENCE_UART_BAUD_GEN 0x18 -#define CADENCE_UART_CHANNEL_STS 0x2C -#define CADENCE_UART_RXTXFIFO 0x30 -#define CADENCE_UART_BAUD_DIV 0x34 - -#define CADENCE_CTRL_RXRES (1 << 0) -#define CADENCE_CTRL_TXRES (1 << 1) -#define CADENCE_CTRL_RXEN (1 << 2) -#define CADENCE_CTRL_RXDIS (1 << 3) -#define CADENCE_CTRL_TXEN (1 << 4) -#define CADENCE_CTRL_TXDIS (1 << 5) -#define CADENCE_CTRL_RSTTO (1 << 6) -#define CADENCE_CTRL_STTBRK (1 << 7) -#define CADENCE_CTRL_STPBRK (1 << 8) - -#define CADENCE_MODE_CLK_REF (0 << 0) -#define CADENCE_MODE_CLK_REF_DIV (1 << 0) -#define CADENCE_MODE_CHRL_6 (3 << 1) -#define CADENCE_MODE_CHRL_7 (2 << 1) -#define CADENCE_MODE_CHRL_8 (0 << 1) -#define CADENCE_MODE_PAR_EVEN (0 << 3) -#define CADENCE_MODE_PAR_ODD (1 << 3) -#define CADENCE_MODE_PAR_SPACE (2 << 3) -#define CADENCE_MODE_PAR_MARK (3 << 3) -#define CADENCE_MODE_PAR_NONE (4 << 3) - -#define CADENCE_STS_REMPTY (1 << 1) -#define CADENCE_STS_RFUL (1 << 2) -#define CADENCE_STS_TEMPTY (1 << 3) -#define CADENCE_STS_TFUL (1 << 4) +#include <linux/err.h> +#include <malloc.h> +#include <notifier.h> +#include <serial/cadence.h> /* * create default values for different platforms @@ -214,7 +173,7 @@ static int cadence_clocksource_clock_change(struct notifier_block *nb, return 0; } -static int cadence_serial_probe(struct device_d *dev) +static int cadence_serial_probe(struct device *dev) { struct resource *iores; struct console_device *cdev; @@ -272,9 +231,16 @@ static __maybe_unused struct of_device_id cadence_serial_dt_ids[] = { .compatible = "xlnx,xuartps", .data = &cadence_r1p08_data, }, { + .compatible = "cdns,uart-r1p12", + .data = &cadence_r1p08_data, + }, { + .compatible = "xlnx,zynqmp-uart", + .data = &cadence_r1p08_data, + }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, cadence_serial_dt_ids); static struct platform_device_id cadence_serial_ids[] = { { @@ -285,15 +251,11 @@ static struct platform_device_id cadence_serial_ids[] = { }, }; -static struct driver_d cadence_serial_driver = { +static struct driver cadence_serial_driver = { .name = "cadence_serial", .probe = cadence_serial_probe, .of_compatible = DRV_OF_COMPAT(cadence_serial_dt_ids), .id_table = cadence_serial_ids, }; -static int cadence_serial_init(void) -{ - return platform_driver_register(&cadence_serial_driver); -} -console_initcall(cadence_serial_init); +console_platform_driver(cadence_serial_driver); diff --git a/drivers/serial/serial_clps711x.c b/drivers/serial/serial_clps711x.c index 7a7d595dff..2a284909bf 100644 --- a/drivers/serial/serial_clps711x.c +++ b/drivers/serial/serial_clps711x.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later /* Author: Alexander Shiyan <shc_work@mail.ru> */ #include <common.h> @@ -8,6 +8,7 @@ #include <linux/clk.h> #include <linux/err.h> #include <mfd/syscon.h> +#include <linux/regmap.h> #define UARTDR 0x00 # define UARTDR_FRMERR (1 << 8) @@ -35,7 +36,7 @@ struct clps711x_uart { void __iomem *base; - void __iomem *syscon; + struct regmap *regmap; struct clk *uart_clk; struct console_device cdev; }; @@ -61,8 +62,7 @@ static void clps711x_init_port(struct console_device *cdev) u32 tmp; /* Disable the UART */ - tmp = readl(s->syscon + SYSCON); - writel(tmp & ~SYSCON_UARTEN, s->syscon + SYSCON); + regmap_update_bits(s->regmap, SYSCON, SYSCON_UARTEN, 0); /* Setup Line Control Register */ tmp = readl(s->base + UBRLCR) & UBRLCR_BAUD_MASK; @@ -70,17 +70,19 @@ static void clps711x_init_port(struct console_device *cdev) writel(tmp, s->base + UBRLCR); /* Enable the UART */ - tmp = readl(s->syscon + SYSCON); - writel(tmp | SYSCON_UARTEN, s->syscon + SYSCON); + regmap_update_bits(s->regmap, SYSCON, SYSCON_UARTEN, SYSCON_UARTEN); } static void clps711x_putc(struct console_device *cdev, char c) { struct clps711x_uart *s = cdev->dev->priv; + u32 tmp; /* Wait until there is space in the FIFO */ do { - } while (readl(s->syscon + SYSFLG) & SYSFLG_UTXFF); + regmap_read(s->regmap, SYSFLG, &tmp); + + } while (tmp & SYSFLG_UTXFF); /* Send the character */ writew(c, s->base + UARTDR); @@ -90,10 +92,12 @@ static int clps711x_getc(struct console_device *cdev) { struct clps711x_uart *s = cdev->dev->priv; u16 data; + u32 tmp; /* Wait until there is data in the FIFO */ do { - } while (readl(s->syscon + SYSFLG) & SYSFLG_URXFE); + regmap_read(s->regmap, SYSFLG, &tmp); + } while (tmp & SYSFLG_URXFE); data = readw(s->base + UARTDR); @@ -107,32 +111,32 @@ static int clps711x_getc(struct console_device *cdev) static int clps711x_tstc(struct console_device *cdev) { struct clps711x_uart *s = cdev->dev->priv; + u32 tmp; + + regmap_read(s->regmap, SYSFLG, &tmp); - return !(readl(s->syscon + SYSFLG) & SYSFLG_URXFE); + return !(tmp & SYSFLG_URXFE); } static void clps711x_flush(struct console_device *cdev) { struct clps711x_uart *s = cdev->dev->priv; + u32 tmp; do { - } while (readl(s->syscon + SYSFLG) & SYSFLG_UBUSY); + regmap_read(s->regmap, SYSFLG, &tmp); + } while (tmp & SYSFLG_UBUSY); } -static int clps711x_probe(struct device_d *dev) +static int clps711x_probe(struct device *dev) { + struct device_node *syscon; struct clps711x_uart *s; - int err, id = dev->id; - char syscon_dev[8]; const char *devname; - - if (dev->device_node) - id = of_alias_get_id(dev->device_node, "serial"); - - if (id != 0 && id != 1) - return -EINVAL; + int err; s = xzalloc(sizeof(struct clps711x_uart)); + s->uart_clk = clk_get(dev, NULL); if (IS_ERR(s->uart_clk)) { err = PTR_ERR(s->uart_clk); @@ -140,19 +144,15 @@ static int clps711x_probe(struct device_d *dev) } s->base = dev_get_mem_region(dev, 0); - if (IS_ERR(s->base)) - return PTR_ERR(s->base); - - if (!dev->device_node) { - sprintf(syscon_dev, "syscon%i", id + 1); - s->syscon = syscon_base_lookup_by_pdevname(syscon_dev); - } else { - s->syscon = syscon_base_lookup_by_phandle(dev->device_node, - "syscon"); + if (IS_ERR(s->base)) { + err = PTR_ERR(s->base); + goto out_err; } - if (IS_ERR(s->syscon)) { - err = PTR_ERR(s->syscon); + syscon = of_parse_phandle(dev->of_node, "syscon", 0); + s->regmap = syscon_node_to_regmap(syscon); + if (IS_ERR(s->regmap)) { + err = PTR_ERR(s->regmap); goto out_err; } @@ -165,7 +165,7 @@ static int clps711x_probe(struct device_d *dev) s->cdev.setbrg = clps711x_setbaudrate; s->cdev.linux_console_name = "ttyCL"; - devname = of_alias_get(dev->device_node); + devname = of_alias_get(dev->of_node); if (devname) { s->cdev.devname = xstrdup(devname); s->cdev.devid = DEVICE_ID_SINGLE; @@ -182,11 +182,13 @@ out_err: return err; } -static struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = { +static const struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = { { .compatible = "cirrus,ep7209-uart", }, + { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, clps711x_uart_dt_ids); -static struct driver_d clps711x_driver = { +static struct driver clps711x_driver = { .name = "clps711x-uart", .probe = clps711x_probe, .of_compatible = DRV_OF_COMPAT(clps711x_uart_dt_ids), diff --git a/drivers/serial/serial_digic.c b/drivers/serial/serial_digic.c index 06b6e15e0c..48e9cd3248 100644 --- a/drivers/serial/serial_digic.c +++ b/drivers/serial/serial_digic.c @@ -1,18 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2013, 2014 Antony Pavlov <antonynpavlov@gmail.com> * * This file is part of barebox. - * See file CREDITS for list of people who contributed to this project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> @@ -20,7 +10,7 @@ #include <malloc.h> #include <io.h> -#include <mach/uart.h> +#include <mach/digic/uart.h> /* * This driver is based on the "Serial terminal" docs here: @@ -99,7 +89,7 @@ static int digic_serial_tstc(struct console_device *cdev) */ } -static int digic_serial_probe(struct device_d *dev) +static int digic_serial_probe(struct device *dev) { struct resource *iores; struct console_device *cdev; @@ -127,8 +117,9 @@ static __maybe_unused struct of_device_id digic_serial_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, digic_serial_dt_ids); -static struct driver_d digic_serial_driver = { +static struct driver digic_serial_driver = { .name = "digic-uart", .probe = digic_serial_probe, .of_compatible = DRV_OF_COMPAT(digic_serial_dt_ids), diff --git a/drivers/serial/serial_efi.c b/drivers/serial/serial_efi.c index 667d51f622..6ed068f159 100644 --- a/drivers/serial/serial_efi.c +++ b/drivers/serial/serial_efi.c @@ -1,7 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2017 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> - * - * Under GPLv2 Only */ #include <common.h> @@ -9,7 +8,7 @@ #include <init.h> #include <malloc.h> #include <efi.h> -#include <efi/efi.h> +#include <efi/efi-payload.h> #include <efi/efi-device.h> /* diff --git a/drivers/serial/serial_imx.c b/drivers/serial/serial_imx.c index 09341af874..0f91028605 100644 --- a/drivers/serial/serial_imx.c +++ b/drivers/serial/serial_imx.c @@ -1,17 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * (c) 2004 Sascha Hauer <sascha@saschahauer.de> - * - * 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, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ #include <common.h> @@ -60,6 +49,7 @@ struct imx_serial_priv { void __iomem *regs; struct clk *clk; struct imx_serial_devtype_data *devtype; + bool rs485_mode; }; static int imx_serial_reffreq(struct imx_serial_priv *priv) @@ -107,6 +97,9 @@ static int imx_serial_init_port(struct console_device *cdev) /* Enable FIFOs */ val = readl(regs + UCR2); + /* set CTS to not block RS485 bus */ + if (priv->rs485_mode) + val |= UCR2_CTS; val |= UCR2_SRST | UCR2_RXEN | UCR2_TXEN; writel(val, regs + UCR2); @@ -204,7 +197,7 @@ static int imx_clocksource_clock_change(struct notifier_block *nb, return 0; } -static int imx_serial_probe(struct device_d *dev) +static int imx_serial_probe(struct device *dev) { struct resource *iores; struct console_device *cdev; @@ -241,17 +234,23 @@ static int imx_serial_probe(struct device_d *dev) cdev->flush = imx_serial_flush; cdev->setbrg = imx_serial_setbaudrate; cdev->linux_console_name = "ttymxc"; - if (dev->device_node) { - devname = of_alias_get(dev->device_node); + cdev->linux_earlycon_name = "ec_imx6q"; + cdev->phys_base = priv->regs; + if (dev->of_node) { + devname = of_alias_get(dev->of_node); if (devname) { cdev->devname = xstrdup(devname); cdev->devid = DEVICE_ID_SINGLE; } } - if (of_property_read_bool(dev->device_node, "fsl,dte-mode")) + if (of_property_read_bool(dev->of_node, "fsl,dte-mode")) priv->dte_mode = 1; + if (of_property_read_bool(dev->of_node, "linux,rs485-enabled-at-boot-time") && + !of_property_read_bool(dev->of_node, "rs485-rts-active-low")) + priv->rs485_mode = 1; + imx_serial_init_port(cdev); /* Enable UART */ @@ -288,9 +287,19 @@ static __maybe_unused struct of_device_id imx_serial_dt_ids[] = { .compatible = "fsl,imx8mq-uart", .data = &imx21_data, }, { + .compatible = "fsl,imx8mm-uart", + .data = &imx21_data, + }, { + .compatible = "fsl,imx8mn-uart", + .data = &imx21_data, + }, { + .compatible = "fsl,imx8mp-uart", + .data = &imx21_data, + }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, imx_serial_dt_ids); static struct platform_device_id imx_serial_ids[] = { { @@ -304,7 +313,7 @@ static struct platform_device_id imx_serial_ids[] = { }, }; -static struct driver_d imx_serial_driver = { +static struct driver imx_serial_driver = { .name = "imx_serial", .probe = imx_serial_probe, .of_compatible = DRV_OF_COMPAT(imx_serial_dt_ids), diff --git a/drivers/serial/serial_litex.c b/drivers/serial/serial_litex.c new file mode 100644 index 0000000000..04da556f6a --- /dev/null +++ b/drivers/serial/serial_litex.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2019 Antony Pavlov <antonynpavlov@gmail.com> + * + */ + +#include <common.h> +#include <init.h> +#include <io.h> + +#define UART_RXTX 0x00 +#define UART_TXFULL 0x04 +#define UART_RXEMPTY 0x08 +#define UART_EV_PENDING 0x10 +#define UART_EV_RX (1 << 1) + +static inline uint32_t litex_serial_readb(struct console_device *cdev, + uint32_t offset) +{ + void __iomem *base = cdev->dev->priv; + + return readb(base + offset); +} + +static inline void litex_serial_writeb(struct console_device *cdev, + uint32_t value, uint32_t offset) +{ + void __iomem *base = cdev->dev->priv; + + writeb(value, base + offset); +} + +static void litex_serial_putc(struct console_device *cdev, char c) +{ + while (litex_serial_readb(cdev, UART_TXFULL)) + ; + + litex_serial_writeb(cdev, c, UART_RXTX); +} + +static int litex_serial_getc(struct console_device *cdev) +{ + int c; + + while (litex_serial_readb(cdev, UART_RXEMPTY)) + ; + + c = litex_serial_readb(cdev, UART_RXTX); + + /* refresh UART_RXEMPTY by writing UART_EV_RX to UART_EV_PENDING */ + litex_serial_writeb(cdev, UART_EV_RX, UART_EV_PENDING); + + return c; +} + +static int litex_serial_tstc(struct console_device *cdev) +{ + return !litex_serial_readb(cdev, UART_RXEMPTY); +} + +static int litex_serial_probe(struct device *dev) +{ + struct resource *iores; + struct console_device *cdev; + + cdev = xzalloc(sizeof(struct console_device)); + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + dev->priv = IOMEM(iores->start); + cdev->dev = dev; + cdev->tstc = &litex_serial_tstc; + cdev->putc = &litex_serial_putc; + cdev->getc = &litex_serial_getc; + cdev->setbrg = NULL; + cdev->linux_console_name = "ttyLXU"; + cdev->linux_earlycon_name = "liteuart"; + cdev->phys_base = IOMEM(iores->start); + + console_register(cdev); + + return 0; +} + +static __maybe_unused struct of_device_id litex_serial_dt_ids[] = { + { + .compatible = "litex,uart", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, litex_serial_dt_ids); + +static struct driver litex_serial_driver = { + .name = "litex-uart", + .probe = litex_serial_probe, + .of_compatible = DRV_OF_COMPAT(litex_serial_dt_ids), +}; +console_platform_driver(litex_serial_driver); diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c index 246fc3d3af..828a0dd0bb 100644 --- a/drivers/serial/serial_lpuart.c +++ b/drivers/serial/serial_lpuart.c @@ -1,21 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2016 Zodiac Inflight Innovation * Author: Andrey Smirnov <andrew.smirnov@gmail.com> * * Based on analogous driver from U-Boot - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> @@ -125,7 +113,7 @@ static int lpuart_clocksource_clock_change(struct notifier_block *nb, return lpuart_serial_setbaudrate(&lpuart->cdev, lpuart->baudrate); } -static int lpuart_serial_probe(struct device_d *dev) +static int lpuart_serial_probe(struct device *dev) { int ret; struct console_device *cdev; @@ -163,8 +151,8 @@ static int lpuart_serial_probe(struct device_d *dev) cdev->flush = lpuart_serial_flush; cdev->setbrg = lpuart_serial_setbaudrate; - if (dev->device_node) { - devname = of_alias_get(dev->device_node); + if (dev->of_node) { + devname = of_alias_get(dev->of_node); if (devname) { cdev->devname = xstrdup(devname); cdev->devid = DEVICE_ID_SINGLE; @@ -172,6 +160,8 @@ static int lpuart_serial_probe(struct device_d *dev) } cdev->linux_console_name = "ttyLP"; + cdev->linux_earlycon_name = "lpuart"; + cdev->phys_base = lpuart->base; lpuart_setup(lpuart->base, clk_get_rate(lpuart->clk)); @@ -196,8 +186,9 @@ static struct of_device_id lpuart_serial_dt_ids[] = { { .compatible = "fsl,vf610-lpuart" }, {} }; +MODULE_DEVICE_TABLE(of, lpuart_serial_dt_ids); -static struct driver_d lpuart_serial_driver = { +static struct driver lpuart_serial_driver = { .name = "lpuart-serial", .probe = lpuart_serial_probe, .of_compatible = DRV_OF_COMPAT(lpuart_serial_dt_ids), diff --git a/drivers/serial/serial_lpuart32.c b/drivers/serial/serial_lpuart32.c new file mode 100644 index 0000000000..09d4b620be --- /dev/null +++ b/drivers/serial/serial_lpuart32.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Pengutronix + */ + +#include <common.h> +#include <driver.h> +#include <init.h> +#include <malloc.h> +#include <notifier.h> +#include <io.h> +#include <of.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <serial/lpuart32.h> + +struct lpuart32_devtype_data { + unsigned int reg_offs; +}; + +struct lpuart32 { + struct console_device cdev; + int baudrate; + int dte_mode; + struct notifier_block notify; + struct resource *io; + void __iomem *base; + struct clk *clk; +}; + +static struct lpuart32 *cdev_to_lpuart32(struct console_device *cdev) +{ + return container_of(cdev, struct lpuart32, cdev); +} + +static void lpuart32_enable(struct lpuart32 *lpuart32) +{ + writel(LPUART32_UARTCTRL_TE | LPUART32_UARTCTRL_RE, + lpuart32->base + LPUART32_UARTCTRL); +} + +static void lpuart32_disable(struct lpuart32 *lpuart32) +{ + writel(0, lpuart32->base + LPUART32_UARTCTRL); +} + +/* Test whether a character is in the RX buffer */ +static int lpuart32_serial_tstc(struct console_device *cdev) +{ + struct lpuart32 *lpuart32 = cdev_to_lpuart32(cdev); + + if (readl(lpuart32->base + LPUART32_UARTSTAT) & LPUART32_UARTSTAT_OR) + writel(LPUART32_UARTSTAT_OR, lpuart32->base + LPUART32_UARTSTAT); + + return readl(lpuart32->base + LPUART32_UARTSTAT) & LPUART32_UARTSTAT_RDRF; +} + +static int lpuart32_serial_setbaudrate(struct console_device *cdev, + int baudrate) +{ + struct lpuart32 *lpuart32 = cdev_to_lpuart32(cdev); + + lpuart32_disable(lpuart32); + + /* + * We treat baudrate of 0 as a request to disable UART + */ + if (baudrate) { + lpuart32_setbrg(lpuart32->base, clk_get_rate(lpuart32->clk), + baudrate); + lpuart32_enable(lpuart32); + } + + lpuart32->baudrate = baudrate; + + return 0; +} + +static int lpuart32_serial_getc(struct console_device *cdev) +{ + struct lpuart32 *lpuart32 = cdev_to_lpuart32(cdev); + + while (!lpuart32_serial_tstc(cdev)); + + return readl(lpuart32->base + LPUART32_UARTDATA) & 0xff; +} + +static void lpuart32_serial_putc(struct console_device *cdev, char c) +{ + struct lpuart32 *lpuart32 = cdev_to_lpuart32(cdev); + + lpuart32_putc(lpuart32->base, c); +} + +static void lpuart32_serial_flush(struct console_device *cdev) +{ +} + +static int lpuart32_serial_probe(struct device *dev) +{ + int ret; + struct console_device *cdev; + struct lpuart32 *lpuart32; + const char *devname; + struct lpuart32_devtype_data *devtype; + + ret = dev_get_drvdata(dev, (const void **)&devtype); + if (ret) + return ret; + + lpuart32 = xzalloc(sizeof(*lpuart32)); + cdev = &lpuart32->cdev; + dev->priv = lpuart32; + + lpuart32->io = dev_request_mem_resource(dev, 0); + if (IS_ERR(lpuart32->io)) { + ret = PTR_ERR(lpuart32->io); + goto err_free; + } + lpuart32->base = IOMEM(lpuart32->io->start) + devtype->reg_offs; + + lpuart32->clk = clk_get(dev, NULL); + if (IS_ERR(lpuart32->clk)) { + ret = PTR_ERR(lpuart32->clk); + dev_err(dev, "Failed to get UART clock %d\n", ret); + goto io_release; + } + + ret = clk_enable(lpuart32->clk); + if (ret) { + dev_err(dev, "Failed to enable UART clock %d\n", ret); + goto io_release; + } + + cdev->dev = dev; + cdev->tstc = lpuart32_serial_tstc; + cdev->putc = lpuart32_serial_putc; + cdev->getc = lpuart32_serial_getc; + cdev->flush = lpuart32_serial_flush; + cdev->setbrg = lpuart32_serial_setbaudrate; + + if (dev->of_node) { + devname = of_alias_get(dev->of_node); + if (devname) { + cdev->devname = xstrdup(devname); + cdev->devid = DEVICE_ID_SINGLE; + } + } + + cdev->linux_console_name = "ttyLP"; + cdev->linux_earlycon_name = "lpuart"; + cdev->phys_base = lpuart32->base; + + lpuart32_setup(lpuart32->base, clk_get_rate(lpuart32->clk)); + + ret = console_register(cdev); + if (!ret) + return 0; + + clk_put(lpuart32->clk); +io_release: + release_region(lpuart32->io); +err_free: + free(lpuart32); + + return ret; +} + +static struct lpuart32_devtype_data imx7ulp_data = { + .reg_offs = 0x10, +}; + +static struct of_device_id lpuart32_serial_dt_ids[] = { + { + .compatible = "fsl,imx7ulp-lpuart", + .data = &imx7ulp_data, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, lpuart32_serial_dt_ids); + +static struct driver lpuart32_serial_driver = { + .name = "lpuart32-serial", + .probe = lpuart32_serial_probe, + .of_compatible = DRV_OF_COMPAT(lpuart32_serial_dt_ids), +}; +console_platform_driver(lpuart32_serial_driver); diff --git a/drivers/serial/serial_mpc5xxx.c b/drivers/serial/serial_mpc5xxx.c index cc63a84c68..4408de9e91 100644 --- a/drivers/serial/serial_mpc5xxx.c +++ b/drivers/serial/serial_mpc5xxx.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * (C) Copyright 2000 - 2003 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * * Hacked for MPC8260 by Murray.Jensen@cmst.csiro.au, 19-Oct-00, with * changes based on the file arch/ppc/mbxboot/m8260_tty.c from the * Linux/PPC sources (m8260_tty.c had no copyright info in it). @@ -59,7 +46,7 @@ static int __mpc5xxx_serial_setbaudrate(struct mpc5xxx_psc *psc, int baudrate) static int mpc5xxx_serial_setbaudrate(struct console_device *cdev, int baudrate) { - struct device_d *dev = cdev->dev; + struct device *dev = cdev->dev; struct mpc5xxx_psc *psc = dev->priv; __mpc5xxx_serial_setbaudrate(psc, baudrate); @@ -103,7 +90,7 @@ static int __mpc5xxx_serial_init(struct mpc5xxx_psc *psc) static int mpc5xxx_serial_init(struct console_device *cdev) { - struct device_d *dev = cdev->dev; + struct device *dev = cdev->dev; struct mpc5xxx_psc *psc = dev->priv; __mpc5xxx_serial_init(psc); @@ -113,7 +100,7 @@ static int mpc5xxx_serial_init(struct console_device *cdev) static void mpc5xxx_serial_putc (struct console_device *cdev, const char c) { - struct device_d *dev = cdev->dev; + struct device *dev = cdev->dev; struct mpc5xxx_psc *psc = dev->priv; /* Wait for last character to go. */ @@ -125,7 +112,7 @@ static void mpc5xxx_serial_putc (struct console_device *cdev, const char c) static int mpc5xxx_serial_getc (struct console_device *cdev) { - struct device_d *dev = cdev->dev; + struct device *dev = cdev->dev; struct mpc5xxx_psc *psc = dev->priv; /* Wait for a character to arrive. */ @@ -137,13 +124,13 @@ static int mpc5xxx_serial_getc (struct console_device *cdev) static int mpc5xxx_serial_tstc (struct console_device *cdev) { - struct device_d *dev = cdev->dev; + struct device *dev = cdev->dev; struct mpc5xxx_psc *psc = dev->priv; return (psc->psc_status & PSC_SR_RXRDY); } -static int mpc5xxx_serial_probe(struct device_d *dev) +static int mpc5xxx_serial_probe(struct device *dev) { struct resource *iores; struct console_device *cdev; @@ -166,7 +153,7 @@ static int mpc5xxx_serial_probe(struct device_d *dev) return 0; } -static struct driver_d mpc5xxx_serial_driver = { +static struct driver mpc5xxx_serial_driver = { .name = "mpc5xxx_serial", .probe = mpc5xxx_serial_probe, }; diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c index 3edeb0dcbe..1b1692658f 100644 --- a/drivers/serial/serial_ns16550.c +++ b/drivers/serial/serial_ns16550.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /** * @file * @brief NS16550 Driver implementation @@ -16,20 +17,6 @@ * * (C) Copyright 2000 * Rob Taylor, Flying Pig Systems. robt@flyingpig.com. - * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> @@ -52,11 +39,18 @@ struct ns16550_priv { unsigned iobase; void (*write_reg)(struct ns16550_priv *, uint8_t val, unsigned offset); uint8_t (*read_reg)(struct ns16550_priv *, unsigned offset); + const char *access_type; + + bool rs485_mode; + bool rs485_rts_active_low; + bool rs485_rx_during_tx; }; struct ns16550_drvdata { void (*init_port)(struct console_device *cdev); const char *linux_console_name; + const char *linux_earlycon_name; + unsigned int clk_default; }; static inline struct ns16550_priv *to_ns16550_priv(struct console_device *cdev) @@ -182,7 +176,6 @@ static inline unsigned int ns16550_calc_divisor(struct console_device *cdev, unsigned int clk = plat->clock; return (clk / MODE_X_DIV / baudrate); - } /** @@ -253,14 +246,10 @@ static void ns16550_jz_init_port(struct console_device *cdev) ns16550_serial_init_port(cdev); } -#define BCM2836_AUX_CLOCK_ENB 0x3f215004 /* BCM2835 AUX Clock enable register */ -#define BCM2836_AUX_CLOCK_EN_UART BIT(0) /* Bit 0 enables the Miniuart */ - static void rpi_init_port(struct console_device *cdev) { struct ns16550_priv *priv = to_ns16550_priv(cdev); - writeb(BCM2836_AUX_CLOCK_EN_UART, BCM2836_AUX_CLOCK_ENB); priv->plat.shift = 2; /* * We double the clock rate since the 16550 will divide by 16 @@ -280,9 +269,37 @@ static void rpi_init_port(struct console_device *cdev) */ static void ns16550_putc(struct console_device *cdev, char c) { - /* Loop Doing Nothing */ - while ((ns16550_read(cdev, lsr) & LSR_THRE) == 0) ; + struct ns16550_priv *priv = to_ns16550_priv(cdev); + + /* wait until FIFO can accept at least one byte */ + while ((ns16550_read(cdev, lsr) & (LSR_THRE)) != (LSR_THRE)) + ; + + if (priv->rs485_mode) { + if (priv->rs485_rts_active_low) + ns16550_write(cdev, MCR_RTS, mcr); + else + ns16550_write(cdev, 0, mcr); + + if (!priv->rs485_rx_during_tx) + ns16550_write(cdev, CNTL_TXEN, cntl); + } + ns16550_write(cdev, c, thr); + + if (priv->rs485_mode) { + /* wait until FIFO is cleared*/ + while ((ns16550_read(cdev, lsr) & (LSR_EMPTY)) != (LSR_EMPTY)) + ; + + if (priv->rs485_rts_active_low) + ns16550_write(cdev, 0, mcr); + else + ns16550_write(cdev, MCR_RTS, mcr); + + if (!priv->rs485_rx_during_tx) + ns16550_write(cdev, CNTL_TXEN | CNTL_RXEN, cntl); + } } /** @@ -311,13 +328,24 @@ static int ns16550_tstc(struct console_device *cdev) return ((ns16550_read(cdev, lsr) & LSR_DR) != 0); } -static void ns16550_probe_dt(struct device_d *dev, struct ns16550_priv *priv) +/** + * @brief Flush remaining characters in serial device + * + * @param[in] cdev pointer to console device + */ +static void ns16550_flush(struct console_device *cdev) +{ + /* Loop Doing Nothing */ + while ((ns16550_read(cdev, lsr) & LSR_TEMT) == 0) ; +} + +static void ns16550_probe_dt(struct device *dev, struct ns16550_priv *priv) { - struct device_node *np = dev->device_node; + struct device_node *np = dev_of_node(dev); u32 offset; u32 width = 1; - if (!IS_ENABLED(CONFIG_OFDEVICE)) + if (!np) return; of_property_read_u32(np, "clock-frequency", &priv->plat.clock); @@ -325,22 +353,33 @@ static void ns16550_probe_dt(struct device_d *dev, struct ns16550_priv *priv) priv->mmiobase += offset; of_property_read_u32(np, "reg-shift", &priv->plat.shift); of_property_read_u32(np, "reg-io-width", &width); + priv->rs485_rts_active_low = + of_property_read_bool(np, "rs485-rts-active-low"); + priv->rs485_mode = + of_property_read_bool(np, "linux,rs485-enabled-at-boot-time"); + priv->rs485_rx_during_tx = + of_property_read_bool(np, "rs485-rx-during-tx"); + switch (width) { case 1: priv->read_reg = ns16550_read_reg_mmio_8; priv->write_reg = ns16550_write_reg_mmio_8; + priv->access_type = "mmio"; break; case 2: priv->read_reg = ns16550_read_reg_mmio_16; priv->write_reg = ns16550_write_reg_mmio_16; + priv->access_type = "mmio16"; break; case 4: if (of_device_is_big_endian(np)) { priv->read_reg = ns16550_read_reg_mmio_32be; priv->write_reg = ns16550_write_reg_mmio_32be; + priv->access_type = "mmio32be"; } else { priv->read_reg = ns16550_read_reg_mmio_32; priv->write_reg = ns16550_write_reg_mmio_32; + priv->access_type = "mmio32"; } break; default: @@ -352,98 +391,101 @@ static void ns16550_probe_dt(struct device_d *dev, struct ns16550_priv *priv) static struct ns16550_drvdata ns16450_drvdata = { .init_port = ns16450_serial_init_port, .linux_console_name = "ttyS", + .linux_earlycon_name = "uart8250", }; static struct ns16550_drvdata ns16550_drvdata = { .init_port = ns16550_serial_init_port, .linux_console_name = "ttyS", + .linux_earlycon_name = "uart8250", }; static __maybe_unused struct ns16550_drvdata omap_drvdata = { .init_port = ns16550_omap_init_port, .linux_console_name = "ttyO", + .linux_earlycon_name = "omap8250", }; -static __maybe_unused struct ns16550_drvdata jz_drvdata = { - .init_port = ns16550_jz_init_port, +static __maybe_unused struct ns16550_drvdata omap_clk48m_drvdata = { + .init_port = ns16550_omap_init_port, + .linux_console_name = "ttyO", + .clk_default = 48000000, }; -static __maybe_unused struct ns16550_drvdata tegra_drvdata = { - .init_port = ns16550_serial_init_port, - .linux_console_name = "ttyS", +static __maybe_unused struct ns16550_drvdata jz_drvdata = { + .init_port = ns16550_jz_init_port, + .linux_earlycon_name = "jz4740_uart", }; static __maybe_unused struct ns16550_drvdata rpi_drvdata = { .init_port = rpi_init_port, .linux_console_name = "ttyS", + .linux_earlycon_name = "bcm2835aux", }; -static int ns16550_init_iomem(struct device_d *dev, struct ns16550_priv *priv) +/** + * @return the requested resource to be properly released in case probe fail + */ +static struct resource *ns16550_init_iores(struct device *dev, struct ns16550_priv *priv) { - struct resource *iores; struct resource *res; - int width; + struct resource *iores; + unsigned long flags; res = dev_get_resource(dev, IORESOURCE_MEM, 0); if (IS_ERR(res)) - return PTR_ERR(res); + res = dev_get_resource(dev, IORESOURCE_IO, 0); + if (IS_ERR(res)) + return res; + + flags = res->flags & (IORESOURCE_MEM_TYPE_MASK | IORESOURCE_IO); - iores = dev_request_mem_resource(dev, 0); + if (flags & IORESOURCE_IO) + iores = request_ioport_region(dev_name(dev), res->start, res->end); + else + iores = request_iomem_region(dev_name(dev), res->start, res->end); if (IS_ERR(iores)) - return PTR_ERR(iores); - priv->mmiobase = IOMEM(iores->start); + return iores; - width = res->flags & IORESOURCE_MEM_TYPE_MASK; - switch (width) { + if (flags & IORESOURCE_IO) + priv->iobase = iores->start; + else + priv->mmiobase = IOMEM(iores->start); + + switch (flags) { + case IORESOURCE_IO | IORESOURCE_MEM_8BIT: + priv->read_reg = ns16550_read_reg_ioport_8; + priv->write_reg = ns16550_write_reg_ioport_8; + priv->access_type = "io"; + break; + case IORESOURCE_IO | IORESOURCE_MEM_16BIT: + priv->read_reg = ns16550_read_reg_ioport_16; + priv->write_reg = ns16550_write_reg_ioport_16; + priv->access_type = "io"; + break; + case IORESOURCE_IO | IORESOURCE_MEM_32BIT: + priv->read_reg = ns16550_read_reg_ioport_32; + priv->write_reg = ns16550_write_reg_ioport_32; + priv->access_type = "io"; + break; case IORESOURCE_MEM_8BIT: priv->read_reg = ns16550_read_reg_mmio_8; priv->write_reg = ns16550_write_reg_mmio_8; + priv->access_type = "mmio"; break; case IORESOURCE_MEM_16BIT: priv->read_reg = ns16550_read_reg_mmio_16; priv->write_reg = ns16550_write_reg_mmio_16; + priv->access_type = "mmio16"; break; case IORESOURCE_MEM_32BIT: priv->read_reg = ns16550_read_reg_mmio_32; priv->write_reg = ns16550_write_reg_mmio_32; + priv->access_type = "mmio32"; break; } - return 0; -} - -static int ns16550_init_ioport(struct device_d *dev, struct ns16550_priv *priv) -{ - struct resource *res; - int width; - - res = dev_get_resource(dev, IORESOURCE_IO, 0); - if (IS_ERR(res)) - return PTR_ERR(res); - - res = request_ioport_region(dev_name(dev), res->start, res->end); - if (IS_ERR(res)) - return PTR_ERR(res); - - priv->iobase = res->start; - - width = res->flags & IORESOURCE_MEM_TYPE_MASK; - switch (width) { - case IORESOURCE_MEM_8BIT: - priv->read_reg = ns16550_read_reg_ioport_8; - priv->write_reg = ns16550_write_reg_ioport_8; - break; - case IORESOURCE_MEM_16BIT: - priv->read_reg = ns16550_read_reg_ioport_16; - priv->write_reg = ns16550_write_reg_ioport_16; - break; - case IORESOURCE_MEM_32BIT: - priv->read_reg = ns16550_read_reg_ioport_32; - priv->write_reg = ns16550_write_reg_ioport_32; - break; - } - - return 0; + return iores; } /** @@ -455,47 +497,50 @@ static int ns16550_init_ioport(struct device_d *dev, struct ns16550_priv *priv) * ENOMEM if calloc failed * else return result of console_register */ -static int ns16550_probe(struct device_d *dev) +static int ns16550_probe(struct device *dev) { struct ns16550_priv *priv; struct console_device *cdev; struct NS16550_plat *plat = (struct NS16550_plat *)dev->platform_data; - struct ns16550_drvdata *devtype; + const struct ns16550_drvdata *devtype; + struct resource *iores; int ret; - ret = dev_get_drvdata(dev, (const void **)&devtype); - if (ret) - devtype = &ns16550_drvdata; + devtype = device_get_match_data(dev) ?: &ns16550_drvdata; priv = xzalloc(sizeof(*priv)); - ret = ns16550_init_iomem(dev, priv); - if (ret) - ret = ns16550_init_ioport(dev, priv); - - if (ret) - return ret; + iores = ns16550_init_iores(dev, priv); + if (IS_ERR(iores)) { + ret = PTR_ERR(iores); + goto err; + } if (plat) priv->plat = *plat; else ns16550_probe_dt(dev, priv); + if (devtype->clk_default && !priv->plat.clock) + priv->plat.clock = devtype->clk_default; + if (!priv->plat.clock) { priv->clk = clk_get(dev, NULL); if (IS_ERR(priv->clk)) { ret = PTR_ERR(priv->clk); dev_err(dev, "failed to get clk (%d)\n", ret); - goto err; + goto release_region; } - clk_enable(priv->clk); + ret = clk_enable(priv->clk); + if (ret) + goto clk_put; priv->plat.clock = clk_get_rate(priv->clk); } if (priv->plat.clock == 0) { dev_err(dev, "no valid clockrate\n"); ret = -EINVAL; - goto err; + goto clk_disable; } cdev = &priv->cdev; @@ -504,14 +549,29 @@ static int ns16550_probe(struct device_d *dev) cdev->putc = ns16550_putc; cdev->getc = ns16550_getc; cdev->setbrg = ns16550_setbaudrate; + cdev->flush = ns16550_flush; cdev->linux_console_name = devtype->linux_console_name; + cdev->linux_earlycon_name = basprintf("%s,%s", devtype->linux_earlycon_name, + priv->access_type); + cdev->phys_base = !strcmp(priv->access_type, "io") ? + IOMEM((ulong)priv->iobase) : priv->mmiobase; priv->fcrval = FCRVAL; devtype->init_port(cdev); - return console_register(cdev); + ret = console_register(cdev); + if (ret) + goto clk_disable; + return 0; + +clk_disable: + clk_disable(priv->clk); +clk_put: + clk_put(priv->clk); +release_region: + release_region(iores); err: free(priv); @@ -524,14 +584,19 @@ static struct of_device_id ns16550_serial_dt_ids[] = { .data = &ns16450_drvdata, }, { .compatible = "ns16550a", - .data = &ns16550_drvdata, }, { .compatible = "snps,dw-apb-uart", - .data = &ns16550_drvdata, }, { .compatible = "marvell,armada-38x-uart", - .data = &ns16550_drvdata, + }, { + .compatible = "nvidia,tegra20-uart", + }, +#if IS_ENABLED(CONFIG_ARCH_K3) + { + .compatible = "ti,am654-uart", + .data = &omap_clk48m_drvdata, }, +#endif #if IS_ENABLED(CONFIG_ARCH_OMAP) { .compatible = "ti,omap2-uart", @@ -542,12 +607,9 @@ static struct of_device_id ns16550_serial_dt_ids[] = { }, { .compatible = "ti,omap4-uart", .data = &omap_drvdata, - }, -#endif -#if IS_ENABLED(CONFIG_ARCH_TEGRA) - { - .compatible = "nvidia,tegra20-uart", - .data = &tegra_drvdata, + }, { + .compatible = "ti,am4372-uart", + .data = &omap_clk48m_drvdata, }, #endif #if IS_ENABLED(CONFIG_MACH_MIPS_XBURST) @@ -566,11 +628,12 @@ static struct of_device_id ns16550_serial_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, ns16550_serial_dt_ids); static __maybe_unused struct platform_device_id ns16550_serial_ids[] = { { .name = "omap-uart", - .driver_data = (unsigned long)&omap_drvdata, + .driver_data = (unsigned long)&omap_clk48m_drvdata, }, { /* sentinel */ }, @@ -579,7 +642,7 @@ static __maybe_unused struct platform_device_id ns16550_serial_ids[] = { /** * @brief Driver registration structure */ -static struct driver_d ns16550_serial_driver = { +static struct driver ns16550_serial_driver = { .name = "ns16550_serial", .probe = ns16550_probe, .of_compatible = DRV_OF_COMPAT(ns16550_serial_dt_ids), diff --git a/drivers/serial/serial_ns16550.h b/drivers/serial/serial_ns16550.h index c37d63ca9e..2d941cb8d1 100644 --- a/drivers/serial/serial_ns16550.h +++ b/drivers/serial/serial_ns16550.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /** * @file * @brief Serial NS16550 header @@ -8,11 +9,6 @@ * Register definitions for NS16550 device */ /* - * This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - * * NS16550 Serial Port * originally from linux source (arch/ppc/boot/ns16550.h) * modified slightly to @@ -40,6 +36,7 @@ #define lsr 5 #define msr 6 #define scr 7 +#define cntl 8 #define thr rbr #define iir fcr @@ -77,6 +74,12 @@ #define LSR_TEMT 0x40 /* Xmitter empty */ #define LSR_ERR 0x80 /* Error */ +/* Transmitter FIFO completely empty */ +#define LSR_EMPTY (LSR_THRE | LSR_TEMT) + +#define CNTL_RXEN 0x01 +#define CNTL_TXEN 0x02 + /* useful defaults for LCR */ #define LCR_8N1 0x03 diff --git a/drivers/serial/serial_ns16550_pci.c b/drivers/serial/serial_ns16550_pci.c new file mode 100644 index 0000000000..f82b5797ce --- /dev/null +++ b/drivers/serial/serial_ns16550_pci.c @@ -0,0 +1,5305 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Probe module for 8250/16550-type PCI serial ports. + * + * Based on Linux drivers/tty/serial/8250_pci.c + * Itself based on Linux drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * Copyright (C) 2019 Ahmad Fatoum, Pengutronix + */ + +#include <common.h> +#include <init.h> +#include <linux/pci.h> +#include <driver.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/bitops.h> + +#include <asm/byteorder.h> +#include <asm/io.h> + +#include <platform_data/serial-ns16550.h> +#include "serial_ns16550.h" + +#define PCI_NUM_BAR_RESOURCES 6 + +#define FL_BASE_MASK 0x0007 +#define FL_BASE0 0x0000 +#define FL_BASE1 0x0001 +#define FL_BASE2 0x0002 +#define FL_BASE3 0x0003 +#define FL_BASE4 0x0004 +#define FL_GET_BASE(x) (x & FL_BASE_MASK) + +/* Use successive BARs (PCI base address registers), + else use offset into some specified BAR */ +#define FL_BASE_BARS 0x0008 + +/* do not assign an irq; no-op, we use no irqs */ +#define FL_NOIRQ 0x0080 + +/* Use the Base address register size to cap number of ports */ +#define FL_REGION_SZ_CAP 0x0100 + + +struct uart_8250_port { + struct NS16550_plat *pdata; + struct resource resource; + +}; + +struct pciserial_board { + unsigned int flags; + unsigned int num_ports; + unsigned int base_baud; + unsigned int uart_offset; + unsigned int reg_shift; + unsigned int first_offset; +}; + +struct serial_private; + +/* + * init function returns: + * > 0 - number of ports + * = 0 - use board->num_ports + * < 0 - error + */ +struct pci_serial_quirk { + u32 vendor; + u32 device; + u32 subvendor; + u32 subdevice; + int (*probe)(struct pci_dev *dev); + int (*init)(struct pci_dev *dev); + int (*setup)(struct serial_private *, + const struct pciserial_board *, + struct uart_8250_port *, int); + void (*exit)(struct pci_dev *dev); +}; + +#define PCI_NUM_BAR_RESOURCES 6 + +struct serial_private { + struct pci_dev *dev; + unsigned int nr; + struct pci_serial_quirk *quirk; + const struct pciserial_board *board; + void *private_data; /* for use by quirks */ +}; + +static int pci_default_setup(struct serial_private*, + const struct pciserial_board*, struct uart_8250_port *, int); + +static void moan_device(const char *str, struct pci_dev *dev) +{ + dev_err(&dev->dev, + "%s: %s\n" + "Please send the output of lspci -vv, this\n" + "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n" + "manufacturer and name of serial board or\n" + "modem board to <linux-serial@vger.kernel.org>.\n", + dev_name(&dev->dev), str, dev->vendor, dev->device, + dev->subsystem_vendor, dev->subsystem_device); +} + +static int +setup_port(struct serial_private *priv, struct uart_8250_port *port, + int bar, int offset, int regshift) +{ + struct pci_dev *dev = priv->dev; + + if (bar >= PCI_NUM_BAR_RESOURCES) + return -EINVAL; + + port->resource.flags = IORESOURCE_MEM_8BIT; + + if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { + if (!pci_iomap(dev, bar)) + return -ENOMEM; + + port->resource.flags |= IORESOURCE_MEM; + } else { + port->resource.flags |= IORESOURCE_IO; + } + + port->resource.start = pci_resource_start(dev, bar) + offset; + port->resource.end = port->resource.start + pci_resource_len(dev, bar) - 1; + + port->pdata->shift = regshift; + + return 0; +} + +/* + * ADDI-DATA GmbH communication cards <info@addi-data.com> + */ +static int addidata_apci7800_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + unsigned int bar = 0, offset = board->first_offset; + bar = FL_GET_BASE(board->flags); + + if (idx < 2) { + offset += idx * board->uart_offset; + } else if ((idx >= 2) && (idx < 4)) { + bar += 1; + offset += ((idx - 2) * board->uart_offset); + } else if ((idx >= 4) && (idx < 6)) { + bar += 2; + offset += ((idx - 4) * board->uart_offset); + } else if (idx >= 6) { + bar += 3; + offset += ((idx - 6) * board->uart_offset); + } + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +/* + * AFAVLAB uses a different mixture of BARs and offsets + * Not that ugly ;) -- HW + */ +static int +afavlab_setup(struct serial_private *priv, const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + unsigned int bar, offset = board->first_offset; + + bar = FL_GET_BASE(board->flags); + if (idx < 4) + bar += idx; + else { + bar = 4; + offset += (idx - 4) * board->uart_offset; + } + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +/* + * HP's Remote Management Console. The Diva chip came in several + * different versions. N-class, L2000 and A500 have two Diva chips, each + * with 3 UARTs (the third UART on the second chip is unused). Superdome + * and Keystone have one Diva chip with 3 UARTs. Some later machines have + * one Diva chip, but it has been expanded to 5 UARTs. + */ +static int pci_hp_diva_init(struct pci_dev *dev) +{ + int rc = 0; + + switch (dev->subsystem_device) { + case PCI_DEVICE_ID_HP_DIVA_TOSCA1: + case PCI_DEVICE_ID_HP_DIVA_HALFDOME: + case PCI_DEVICE_ID_HP_DIVA_KEYSTONE: + case PCI_DEVICE_ID_HP_DIVA_EVEREST: + rc = 3; + break; + case PCI_DEVICE_ID_HP_DIVA_TOSCA2: + rc = 2; + break; + case PCI_DEVICE_ID_HP_DIVA_MAESTRO: + rc = 4; + break; + case PCI_DEVICE_ID_HP_DIVA_POWERBAR: + case PCI_DEVICE_ID_HP_DIVA_HURRICANE: + rc = 1; + break; + } + + return rc; +} + +/* + * HP's Diva chip puts the 4th/5th serial port further out, and + * some serial ports are supposed to be hidden on certain models. + */ +static int +pci_hp_diva_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + unsigned int offset = board->first_offset; + unsigned int bar = FL_GET_BASE(board->flags); + + switch (priv->dev->subsystem_device) { + case PCI_DEVICE_ID_HP_DIVA_MAESTRO: + if (idx == 3) + idx++; + break; + case PCI_DEVICE_ID_HP_DIVA_EVEREST: + if (idx > 0) + idx++; + if (idx > 2) + idx++; + break; + } + if (idx > 2) + offset = 0x18; + + offset += idx * board->uart_offset; + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +/* + * Added for EKF Intel i960 serial boards + */ +static int pci_inteli960ni_init(struct pci_dev *dev) +{ + u32 oldval; + + if (!(dev->subsystem_device & 0x1000)) + return -ENODEV; + + /* is firmware started? */ + pci_read_config_dword(dev, 0x44, &oldval); + if (oldval == 0x00001000L) { /* RESET value */ + dev_dbg(&dev->dev, "Local i960 firmware missing\n"); + return -ENODEV; + } + return 0; +} + +/* + * Some PCI serial cards using the PLX 9050 PCI interface chip require + * that the card interrupt be explicitly enabled or disabled. This + * seems to be mainly needed on card using the PLX which also use I/O + * mapped memory. + */ +static int pci_plx9050_init(struct pci_dev *dev) +{ + void __iomem *p; + + if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar 0", dev); + return 0; + } + + /* + * disable interrupts + */ + p = pci_iomap(dev, 0); + if (p == NULL) + return -ENOMEM; + + writel(0, p + 0x4c); + + /* + * Read the register back to ensure that it took effect. + */ + readl(p + 0x4c); + + return 0; +} + +#define NI8420_INT_ENABLE_REG 0x38 +#define NI8420_INT_ENABLE_BIT 0x2000 + +/* MITE registers */ +#define MITE_IOWBSR1 0xc4 +#define MITE_IOWCR1 0xf4 +#define MITE_LCIMR1 0x08 +#define MITE_LCIMR2 0x10 + +#define MITE_LCIMR2_CLR_CPU_IE (1 << 30) + +/* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */ +static int +sbs_setup(struct serial_private *priv, const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + unsigned int bar, offset = board->first_offset; + + bar = 0; + + if (idx < 4) { + /* first four channels map to 0, 0x100, 0x200, 0x300 */ + offset += idx * board->uart_offset; + } else if (idx < 8) { + /* last four channels map to 0x1000, 0x1100, 0x1200, 0x1300 */ + offset += idx * board->uart_offset + 0xC00; + } else /* we have only 8 ports on PMC-OCTALPRO */ + return 1; + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +/* +* This does initialization for PMC OCTALPRO cards: +* maps the device memory, resets the UARTs (needed, bc +* if the module is removed and inserted again, the card +* is in the sleep mode) and enables global interrupt. +*/ + +/* global control register offset for SBS PMC-OctalPro */ +#define OCT_REG_CR_OFF 0x500 + +static int sbs_init(struct pci_dev *dev) +{ + u8 __iomem *p; + + p = pci_iomap(dev, 0); + + if (p == NULL) + return -ENOMEM; + /* Set bit-4 Control Register (UART RESET) in to reset the uarts */ + writeb(0x10, p + OCT_REG_CR_OFF); + udelay(50); + writeb(0x0, p + OCT_REG_CR_OFF); + + /* Clear especially bit-2 (INTENABLE) of Control Register */ + writeb(0, p + OCT_REG_CR_OFF); + + return 0; +} + +/* + * SIIG serial cards have an PCI interface chip which also controls + * the UART clocking frequency. Each UART can be clocked independently + * (except cards equipped with 4 UARTs) and initial clocking settings + * are stored in the EEPROM chip. It can cause problems because this + * version of serial driver doesn't support differently clocked UART's + * on single PCI card. To prevent this, initialization functions set + * high frequency clocking for all UART's on given card. It is safe (I + * hope) because it doesn't touch EEPROM settings to prevent conflicts + * with other OSes (like M$ DOS). + * + * SIIG support added by Andrey Panin <pazke@donpac.ru>, 10/1999 + * + * There is two family of SIIG serial cards with different PCI + * interface chip and different configuration methods: + * - 10x cards have control registers in IO and/or memory space; + * - 20x cards have control registers in standard PCI configuration space. + * + * Note: all 10x cards have PCI device ids 0x10.. + * all 20x cards have PCI device ids 0x20.. + * + * There are also Quartet Serial cards which use Oxford Semiconductor + * 16954 quad UART PCI chip clocked by 18.432 MHz quartz. + * + * Note: some SIIG cards are probed by the parport_serial object. + */ + +#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc) +#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8) + +static int pci_siig10x_init(struct pci_dev *dev) +{ + u16 data; + void __iomem *p; + + switch (dev->device & 0xfff8) { + case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ + data = 0xffdf; + break; + case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ + data = 0xf7ff; + break; + default: /* 1S1P, 4S */ + data = 0xfffb; + break; + } + + p = pci_iomap(dev, 0); + if (p == NULL) + return -ENOMEM; + + writew(readw(p + 0x28) & data, p + 0x28); + readw(p + 0x28); + + + return 0; +} + +#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc) +#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc) + +static int pci_siig20x_init(struct pci_dev *dev) +{ + u8 data; + + /* Change clock frequency for the first UART. */ + pci_read_config_byte(dev, 0x6f, &data); + pci_write_config_byte(dev, 0x6f, data & 0xef); + + /* If this card has 2 UART, we have to do the same with second UART. */ + if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) || + ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) { + pci_read_config_byte(dev, 0x73, &data); + pci_write_config_byte(dev, 0x73, data & 0xef); + } + return 0; +} + +static int pci_siig_init(struct pci_dev *dev) +{ + unsigned int type = dev->device & 0xff00; + + if (type == 0x1000) + return pci_siig10x_init(dev); + else if (type == 0x2000) + return pci_siig20x_init(dev); + + moan_device("Unknown SIIG card", dev); + return -ENODEV; +} + +static int pci_siig_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + unsigned int bar = FL_GET_BASE(board->flags) + idx, offset = 0; + + if (idx > 3) { + bar = 4; + offset = (idx - 4) * 8; + } + + return setup_port(priv, port, bar, offset, 0); +} + +/* + * Timedia has an explosion of boards, and to avoid the PCI table from + * growing *huge*, we use this function to collapse some 70 entries + * in the PCI table into one, for sanity's and compactness's sake. + */ +static const unsigned short timedia_single_port[] = { + 0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0 +}; + +static const unsigned short timedia_dual_port[] = { + 0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085, + 0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, + 0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, + 0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079, + 0xD079, 0 +}; + +static const unsigned short timedia_quad_port[] = { + 0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, + 0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, + 0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056, + 0xB157, 0 +}; + +static const unsigned short timedia_eight_port[] = { + 0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, + 0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0 +}; + +static const struct timedia_struct { + int num; + const unsigned short *ids; +} timedia_data[] = { + { 1, timedia_single_port }, + { 2, timedia_dual_port }, + { 4, timedia_quad_port }, + { 8, timedia_eight_port } +}; + +/* + * There are nearly 70 different Timedia/SUNIX PCI serial devices. Instead of + * listing them individually, this driver merely grabs them all with + * PCI_ANY_ID. Some of these devices, however, also feature a parallel port, + * and should be left free to be claimed by parport_serial instead. + */ +static int pci_timedia_probe(struct pci_dev *dev) +{ + /* + * Check the third digit of the subdevice ID + * (0,2,3,5,6: serial only -- 7,8,9: serial + parallel) + */ + if ((dev->subsystem_device & 0x00f0) >= 0x70) { + dev_info(&dev->dev, + "ignoring Timedia subdevice %04x for parport_serial\n", + dev->subsystem_device); + return -ENODEV; + } + + return 0; +} + +static int pci_timedia_init(struct pci_dev *dev) +{ + const unsigned short *ids; + int i, j; + + for (i = 0; i < ARRAY_SIZE(timedia_data); i++) { + ids = timedia_data[i].ids; + for (j = 0; ids[j]; j++) + if (dev->subsystem_device == ids[j]) + return timedia_data[i].num; + } + return 0; +} + +/* + * Timedia/SUNIX uses a mixture of BARs and offsets + * Ugh, this is ugly as all hell --- TYT + */ +static int +pci_timedia_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + unsigned int bar = 0, offset = board->first_offset; + + switch (idx) { + case 0: + bar = 0; + break; + case 1: + offset = board->uart_offset; + bar = 0; + break; + case 2: + bar = 1; + break; + case 3: + offset = board->uart_offset; + /* FALLTHROUGH */ + case 4: /* BAR 2 */ + case 5: /* BAR 3 */ + case 6: /* BAR 4 */ + case 7: /* BAR 5 */ + bar = idx - 2; + } + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +/* + * Some Titan cards are also a little weird + */ +static int +titan_400l_800l_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + unsigned int bar, offset = board->first_offset; + + switch (idx) { + case 0: + bar = 1; + break; + case 1: + bar = 2; + break; + default: + bar = 4; + offset = (idx - 2) * board->uart_offset; + } + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +static int pci_xircom_init(struct pci_dev *dev) +{ + mdelay(100); + return 0; +} + +static int pci_ni8420_init(struct pci_dev *dev) +{ + void __iomem *p; + unsigned int bar = 0; + + if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar", dev); + return 0; + } + + p = pci_iomap(dev, bar); + if (p == NULL) + return -ENOMEM; + + /* Disable the CPU Interrupt */ + writel(readl(p + NI8420_INT_ENABLE_REG) & ~(NI8420_INT_ENABLE_BIT), + p + NI8420_INT_ENABLE_REG); + + return 0; +} + +static int pci_ni8430_init(struct pci_dev *dev) +{ + return -ENOSYS; +} + +/* UART Port Control Register */ +#define NI16550_PCR_OFFSET 0x0f +#define NI16550_PCR_RS422 0x00 +#define NI16550_PCR_ECHO_RS485 0x01 +#define NI16550_PCR_DTR_RS485 0x02 +#define NI16550_PCR_AUTO_RS485 0x03 +#define NI16550_PCR_WIRE_MODE_MASK 0x03 +#define NI16550_PCR_TXVR_ENABLE_BIT BIT(3) +#define NI16550_PCR_RS485_TERMINATION_BIT BIT(6) +#define NI16550_ACR_DTR_AUTO_DTR (0x2 << 3) +#define NI16550_ACR_DTR_MANUAL_DTR (0x0 << 3) + +static int +pci_ni8430_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + struct pci_dev *dev = priv->dev; + void __iomem *p; + unsigned int bar, offset = board->first_offset; + + if (idx >= board->num_ports) + return 1; + + bar = FL_GET_BASE(board->flags); + offset += idx * board->uart_offset; + + p = pci_iomap(dev, bar); + if (!p) + return -ENOMEM; + + /* enable the transceiver */ + writeb(readb(p + offset + NI16550_PCR_OFFSET) | NI16550_PCR_TXVR_ENABLE_BIT, + p + offset + NI16550_PCR_OFFSET); + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +static int pci_ni8431_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *uart, int idx) +{ + return -ENOSYS; +} + +static int pci_netmos_9900_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + unsigned int bar; + + if ((priv->dev->device != PCI_DEVICE_ID_NETMOS_9865) && + (priv->dev->subsystem_device & 0xff00) == 0x3000) { + /* netmos apparently orders BARs by datasheet layout, so serial + * ports get BARs 0 and 3 (or 1 and 4 for memmapped) + */ + bar = 3 * idx; + + return setup_port(priv, port, bar, 0, board->reg_shift); + } else { + return pci_default_setup(priv, board, port, idx); + } +} + +/* the 99xx series comes with a range of device IDs and a variety + * of capabilities: + * + * 9900 has varying capabilities and can cascade to sub-controllers + * (cascading should be purely internal) + * 9904 is hardwired with 4 serial ports + * 9912 and 9922 are hardwired with 2 serial ports + */ +static int pci_netmos_9900_numports(struct pci_dev *dev) +{ + unsigned int c = dev->class; + unsigned int pi; + unsigned short sub_serports; + + pi = c & 0xff; + + if (pi == 2) + return 1; + + if ((pi == 0) && (dev->device == PCI_DEVICE_ID_NETMOS_9900)) { + /* two possibilities: 0x30ps encodes number of parallel and + * serial ports, or 0x1000 indicates *something*. This is not + * immediately obvious, since the 2s1p+4s configuration seems + * to offer all functionality on functions 0..2, while still + * advertising the same function 3 as the 4s+2s1p config. + */ + sub_serports = dev->subsystem_device & 0xf; + if (sub_serports > 0) + return sub_serports; + + dev_err(&dev->dev, + "NetMos/Mostech serial driver ignoring port on ambiguous config.\n"); + return 0; + } + + moan_device("unknown NetMos/Mostech program interface", dev); + return 0; +} + +static int pci_netmos_init(struct pci_dev *dev) +{ + /* subdevice 0x00PS means <P> parallel, <S> serial */ + unsigned int num_serial = dev->subsystem_device & 0xf; + + if ((dev->device == PCI_DEVICE_ID_NETMOS_9901) || + (dev->device == PCI_DEVICE_ID_NETMOS_9865)) + return 0; + + if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM && + dev->subsystem_device == 0x0299) + return 0; + + switch (dev->device) { /* FALLTHROUGH on all */ + case PCI_DEVICE_ID_NETMOS_9904: + case PCI_DEVICE_ID_NETMOS_9912: + case PCI_DEVICE_ID_NETMOS_9922: + case PCI_DEVICE_ID_NETMOS_9900: + num_serial = pci_netmos_9900_numports(dev); + break; + + default: + break; + } + + if (num_serial == 0) { + moan_device("unknown NetMos/Mostech device", dev); + return -ENODEV; + } + + return num_serial; +} + +/* + * These chips are available with optionally one parallel port and up to + * two serial ports. Unfortunately they all have the same product id. + * + * Basic configuration is done over a region of 32 I/O ports. The base + * ioport is called INTA or INTC, depending on docs/other drivers. + * + * The region of the 32 I/O ports is configured in POSIO0R... + */ + +/* registers */ +#define ITE_887x_MISCR 0x9c +#define ITE_887x_INTCBAR 0x78 +#define ITE_887x_UARTBAR 0x7c +#define ITE_887x_PS0BAR 0x10 +#define ITE_887x_POSIO0 0x60 + +/* I/O space size */ +#define ITE_887x_IOSIZE 32 +/* I/O space size (bits 26-24; 8 bytes = 011b) */ +#define ITE_887x_POSIO_IOSIZE_8 (3 << 24) +/* I/O space size (bits 26-24; 32 bytes = 101b) */ +#define ITE_887x_POSIO_IOSIZE_32 (5 << 24) +/* Decoding speed (1 = slow, 2 = medium, 3 = fast) */ +#define ITE_887x_POSIO_SPEED (3 << 29) +/* enable IO_Space bit */ +#define ITE_887x_POSIO_ENABLE (1 << 31) + +static int pci_ite887x_init(struct pci_dev *dev) +{ + struct serial_private *priv; + /* inta_addr are the configuration addresses of the ITE */ + static const short inta_addr[] = { 0x2a0, 0x2c0, 0x220, 0x240, 0x1e0, + 0x200, 0x280, 0 }; + int ret, i, type; + struct resource *iobase = NULL; + u32 miscr, uartbar, ioport; + + /* search for the base-ioport */ + i = 0; + while (inta_addr[i] && iobase == NULL) { + iobase = request_ioport_region("ite887x", + inta_addr[i], + inta_addr[i] + ITE_887x_IOSIZE - 1); + if (!IS_ERR(iobase)) { + /* write POSIO0R - speed | size | ioport */ + pci_write_config_dword(dev, ITE_887x_POSIO0, + ITE_887x_POSIO_ENABLE | ITE_887x_POSIO_SPEED | + ITE_887x_POSIO_IOSIZE_32 | inta_addr[i]); + /* write INTCBAR - ioport */ + pci_write_config_dword(dev, ITE_887x_INTCBAR, + inta_addr[i]); + ret = inb(inta_addr[i]); + if (ret != 0xff) { + /* ioport connected */ + break; + } + release_region(iobase); + iobase = NULL; + } + i++; + } + + if (!inta_addr[i]) { + dev_err(&dev->dev, "ite887x: could not find iobase\n"); + return -ENODEV; + } + + /* start of undocumented type checking (see parport_pc.c) */ + type = inb(iobase->start + 0x18) & 0x0f; + + switch (type) { + case 0x2: /* ITE8871 (1P) */ + case 0xa: /* ITE8875 (1P) */ + ret = 0; + break; + case 0xe: /* ITE8872 (2S1P) */ + ret = 2; + break; + case 0x6: /* ITE8873 (1S) */ + ret = 1; + break; + case 0x8: /* ITE8874 (2S) */ + ret = 2; + break; + default: + moan_device("Unknown ITE887x", dev); + ret = -ENODEV; + } + + /* configure all serial ports */ + for (i = 0; i < ret; i++) { + /* read the I/O port from the device */ + pci_read_config_dword(dev, ITE_887x_PS0BAR + (0x4 * (i + 1)), + &ioport); + ioport &= 0x0000FF00; /* the actual base address */ + pci_write_config_dword(dev, ITE_887x_POSIO0 + (0x4 * (i + 1)), + ITE_887x_POSIO_ENABLE | ITE_887x_POSIO_SPEED | + ITE_887x_POSIO_IOSIZE_8 | ioport); + + /* write the ioport to the UARTBAR */ + pci_read_config_dword(dev, ITE_887x_UARTBAR, &uartbar); + uartbar &= ~(0xffff << (16 * i)); /* clear half the reg */ + uartbar |= (ioport << (16 * i)); /* set the ioport */ + pci_write_config_dword(dev, ITE_887x_UARTBAR, uartbar); + + /* get current config */ + pci_read_config_dword(dev, ITE_887x_MISCR, &miscr); + /* disable interrupts (UARTx_Routing[3:0]) */ + miscr &= ~(0xf << (12 - 4 * i)); + /* activate the UART (UARTx_En) */ + miscr |= 1 << (23 - i); + /* write new config with activated UART */ + pci_write_config_dword(dev, ITE_887x_MISCR, miscr); + } + + if (ret <= 0) { + /* the device has no UARTs if we get here */ + release_region(iobase); + } + + priv = dev->dev.priv; + priv->private_data = iobase; + + return ret; +} + +static void pci_ite887x_exit(struct pci_dev *dev) +{ + struct serial_private *priv; + u32 ioport; + /* the ioport is bit 0-15 in POSIO0R */ + pci_read_config_dword(dev, ITE_887x_POSIO0, &ioport); + ioport &= 0xffff; + + priv = dev->dev.priv; + if (priv->private_data) + release_region(priv->private_data); +} + +/* + * EndRun Technologies. + * Determine the number of ports available on the device. + */ +#define PCI_VENDOR_ID_ENDRUN 0x7401 +#define PCI_DEVICE_ID_ENDRUN_1588 0xe100 + +static int pci_endrun_init(struct pci_dev *dev) +{ + u8 __iomem *p; + unsigned long deviceID; + unsigned int number_uarts = 0; + + /* EndRun device is all 0xexxx */ + if (dev->vendor == PCI_VENDOR_ID_ENDRUN && + (dev->device & 0xf000) != 0xe000) + return 0; + + p = pci_iomap(dev, 0); + if (p == NULL) + return -ENOMEM; + + deviceID = ioread32(p); + /* EndRun device */ + if (deviceID == 0x07000200) { + number_uarts = ioread8(p + 4); + dev_dbg(&dev->dev, + "%d ports detected on EndRun PCI Express device\n", + number_uarts); + } + + return number_uarts; +} + +/* + * Oxford Semiconductor Inc. + * Check that device is part of the Tornado range of devices, then determine + * the number of ports available on the device. + */ +static int pci_oxsemi_tornado_init(struct pci_dev *dev) +{ + u8 __iomem *p; + unsigned long deviceID; + unsigned int number_uarts = 0; + + /* OxSemi Tornado devices are all 0xCxxx */ + if (dev->vendor == PCI_VENDOR_ID_OXSEMI && + (dev->device & 0xF000) != 0xC000) + return 0; + + p = pci_iomap(dev, 0); + if (p == NULL) + return -ENOMEM; + + deviceID = ioread32(p); + /* Tornado device */ + if (deviceID == 0x07000200) { + number_uarts = ioread8(p + 4); + dev_dbg(&dev->dev, + "%d ports detected on Oxford PCI Express device\n", + number_uarts); + } + + return number_uarts; +} + +static int pci_asix_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + return -ENOSYS; +} + +/* Quatech devices have their own extra interface features */ + +struct quatech_feature { + u16 devid; + bool amcc; +}; + +#define QPCR_TEST_FOR1 0x3F +#define QPCR_TEST_GET1 0x00 +#define QPCR_TEST_FOR2 0x40 +#define QPCR_TEST_GET2 0x40 +#define QPCR_TEST_FOR3 0x80 +#define QPCR_TEST_GET3 0x40 +#define QPCR_TEST_FOR4 0xC0 +#define QPCR_TEST_GET4 0x80 + +#define QOPR_CLOCK_X1 0x0000 +#define QOPR_CLOCK_X2 0x0001 +#define QOPR_CLOCK_X4 0x0002 +#define QOPR_CLOCK_X8 0x0003 +#define QOPR_CLOCK_RATE_MASK 0x0003 + + +static struct quatech_feature quatech_cards[] = { + { PCI_DEVICE_ID_QUATECH_QSC100, 1 }, + { PCI_DEVICE_ID_QUATECH_DSC100, 1 }, + { PCI_DEVICE_ID_QUATECH_DSC100E, 0 }, + { PCI_DEVICE_ID_QUATECH_DSC200, 1 }, + { PCI_DEVICE_ID_QUATECH_DSC200E, 0 }, + { PCI_DEVICE_ID_QUATECH_ESC100D, 1 }, + { PCI_DEVICE_ID_QUATECH_ESC100M, 1 }, + { PCI_DEVICE_ID_QUATECH_QSCP100, 1 }, + { PCI_DEVICE_ID_QUATECH_DSCP100, 1 }, + { PCI_DEVICE_ID_QUATECH_QSCP200, 1 }, + { PCI_DEVICE_ID_QUATECH_DSCP200, 1 }, + { PCI_DEVICE_ID_QUATECH_ESCLP100, 0 }, + { PCI_DEVICE_ID_QUATECH_QSCLP100, 0 }, + { PCI_DEVICE_ID_QUATECH_DSCLP100, 0 }, + { PCI_DEVICE_ID_QUATECH_SSCLP100, 0 }, + { PCI_DEVICE_ID_QUATECH_QSCLP200, 0 }, + { PCI_DEVICE_ID_QUATECH_DSCLP200, 0 }, + { PCI_DEVICE_ID_QUATECH_SSCLP200, 0 }, + { PCI_DEVICE_ID_QUATECH_SPPXP_100, 0 }, + { 0, } +}; + +static int pci_quatech_amcc(u16 devid) +{ + struct quatech_feature *qf = &quatech_cards[0]; + while (qf->devid) { + if (qf->devid == devid) + return qf->amcc; + qf++; + } + pr_err("quatech: unknown port type '0x%04X'.\n", devid); + return 0; +}; + +static int pci_quatech_rqopr(struct uart_8250_port *port) +{ + unsigned long base = port->resource.start; + u8 LCR, val; + + LCR = inb(base + lcr); + outb(0xBF, base + lcr); + val = inb(base + scr); + outb(LCR, base + lcr); + return val; +} + +static void pci_quatech_wqopr(struct uart_8250_port *port, u8 qopr) +{ + unsigned long base = port->resource.start; + u8 LCR; + + LCR = inb(base + lcr); + outb(0xBF, base + lcr); + inb(base + scr); + outb(qopr, base + scr); + outb(LCR, base + lcr); +} + +static int pci_quatech_rqmcr(struct uart_8250_port *port) +{ + unsigned long base = port->resource.start; + u8 LCR, val, qmcr; + + LCR = inb(base + lcr); + outb(0xBF, base + lcr); + val = inb(base + scr); + outb(val | 0x10, base + scr); + qmcr = inb(base + mcr); + outb(val, base + scr); + outb(LCR, base + lcr); + + return qmcr; +} + +static void pci_quatech_wqmcr(struct uart_8250_port *port, u8 qmcr) +{ + unsigned long base = port->resource.start; + u8 LCR, val; + + LCR = inb(base + lcr); + outb(0xBF, base + lcr); + val = inb(base + scr); + outb(val | 0x10, base + scr); + outb(qmcr, base + mcr); + outb(val, base + scr); + outb(LCR, base + lcr); +} + +static int pci_quatech_has_qmcr(struct uart_8250_port *port) +{ + unsigned long base = port->resource.start; + u8 LCR, val; + + LCR = inb(base + lcr); + outb(0xBF, base + lcr); + val = inb(base + scr); + if (val & 0x20) { + outb(0x80, lcr); + if (!(inb(scr) & 0x20)) { + outb(LCR, base + lcr); + return 1; + } + } + return 0; +} + +static int pci_quatech_test(struct uart_8250_port *port) +{ + u8 reg, qopr; + + qopr = pci_quatech_rqopr(port); + pci_quatech_wqopr(port, qopr & QPCR_TEST_FOR1); + reg = pci_quatech_rqopr(port) & 0xC0; + if (reg != QPCR_TEST_GET1) + return -EINVAL; + pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR2); + reg = pci_quatech_rqopr(port) & 0xC0; + if (reg != QPCR_TEST_GET2) + return -EINVAL; + pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR3); + reg = pci_quatech_rqopr(port) & 0xC0; + if (reg != QPCR_TEST_GET3) + return -EINVAL; + pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR4); + reg = pci_quatech_rqopr(port) & 0xC0; + if (reg != QPCR_TEST_GET4) + return -EINVAL; + + pci_quatech_wqopr(port, qopr); + return 0; +} + +static int pci_quatech_clock(struct uart_8250_port *port) +{ + u8 qopr, reg, set; + unsigned long clock; + + if (pci_quatech_test(port) < 0) + return 1843200; + + qopr = pci_quatech_rqopr(port); + + pci_quatech_wqopr(port, qopr & ~QOPR_CLOCK_X8); + reg = pci_quatech_rqopr(port); + if (reg & QOPR_CLOCK_X8) { + clock = 1843200; + goto out; + } + pci_quatech_wqopr(port, qopr | QOPR_CLOCK_X8); + reg = pci_quatech_rqopr(port); + if (!(reg & QOPR_CLOCK_X8)) { + clock = 1843200; + goto out; + } + reg &= QOPR_CLOCK_X8; + if (reg == QOPR_CLOCK_X2) { + clock = 3685400; + set = QOPR_CLOCK_X2; + } else if (reg == QOPR_CLOCK_X4) { + clock = 7372800; + set = QOPR_CLOCK_X4; + } else if (reg == QOPR_CLOCK_X8) { + clock = 14745600; + set = QOPR_CLOCK_X8; + } else { + clock = 1843200; + set = QOPR_CLOCK_X1; + } + qopr &= ~QOPR_CLOCK_RATE_MASK; + qopr |= set; + +out: + pci_quatech_wqopr(port, qopr); + return clock; +} + +static int pci_quatech_rs422(struct uart_8250_port *port) +{ + u8 qmcr; + int rs422 = 0; + + if (!pci_quatech_has_qmcr(port)) + return 0; + qmcr = pci_quatech_rqmcr(port); + pci_quatech_wqmcr(port, 0xFF); + if (pci_quatech_rqmcr(port)) + rs422 = 1; + pci_quatech_wqmcr(port, qmcr); + return rs422; +} + +static int pci_quatech_init(struct pci_dev *dev) +{ + if (pci_quatech_amcc(dev->device)) { + unsigned long base = pci_resource_start(dev, 0); + if (base) { + u32 tmp; + + outl(inl(base + 0x38) | 0x00002000, base + 0x38); + tmp = inl(base + 0x3c); + outl(tmp | 0x01000000, base + 0x3c); + outl(tmp & ~0x01000000, base + 0x3c); + } + } + return 0; +} + +static int pci_quatech_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + /* Needed by pci_quatech calls below */ + port->resource.start = pci_resource_start(priv->dev, FL_GET_BASE(board->flags)); + /* Set up the clocking */ + port->pdata->clock = pci_quatech_clock(port); + /* For now just warn about RS422 */ + if (pci_quatech_rs422(port)) + pr_warn("quatech: software control of RS422 features not currently supported.\n"); + return pci_default_setup(priv, board, port, idx); +} + +static int pci_default_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + unsigned int bar, offset = board->first_offset, maxnr; + + bar = FL_GET_BASE(board->flags); + if (board->flags & FL_BASE_BARS) + bar += idx; + else + offset += idx * board->uart_offset; + + maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >> + (board->reg_shift + 3); + + if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) + return 1; + + return setup_port(priv, port, bar, offset, board->reg_shift); +} +static int pci_pericom_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + return -ENOSYS; +} + +static int pci_pericom_setup_four_at_eight(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + return -ENOSYS; +} + +static int +ce4100_serial_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + return -ENOSYS; +} + +static int +pci_omegapci_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + return setup_port(priv, port, 2, idx * 8, 0); +} + +static int +pci_brcm_trumanage_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + return -ENOSYS; +} + +/* RTS will control by MCR if this bit is 0 */ +#define FINTEK_RTS_CONTROL_BY_HW BIT(4) +/* only worked with FINTEK_RTS_CONTROL_BY_HW on */ +#define FINTEK_RTS_INVERT BIT(5) + +static int pci_fintek_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + struct pci_dev *pdev = priv->dev; + u8 config_base; + u16 iobase; + + config_base = 0x40 + 0x08 * idx; + + /* Get the io address from configuration space */ + pci_read_config_word(pdev, config_base + 4, &iobase); + + dev_dbg(&pdev->dev, "%s: idx=%d iobase=0x%x", __func__, idx, iobase); + + port->resource.flags = IORESOURCE_IO; + port->resource.start = iobase; + port->resource.end = iobase + 8 - 1; + + return 0; +} + +static int pci_fintek_init(struct pci_dev *dev) +{ + unsigned long iobase; + u32 max_port, i; + resource_size_t bar_data[3]; + u8 config_base; + + if (!(pci_resource_flags(dev, 5) & IORESOURCE_IO) || + !(pci_resource_flags(dev, 4) & IORESOURCE_IO) || + !(pci_resource_flags(dev, 3) & IORESOURCE_IO)) + return -ENODEV; + + switch (dev->device) { + case 0x1104: /* 4 ports */ + case 0x1108: /* 8 ports */ + max_port = dev->device & 0xff; + break; + case 0x1112: /* 12 ports */ + max_port = 12; + break; + default: + return -EINVAL; + } + + /* Get the io address dispatch from the BIOS */ + bar_data[0] = pci_resource_start(dev, 5); + bar_data[1] = pci_resource_start(dev, 4); + bar_data[2] = pci_resource_start(dev, 3); + + for (i = 0; i < max_port; ++i) { + /* UART0 configuration offset start from 0x40 */ + config_base = 0x40 + 0x08 * i; + + /* Calculate Real IO Port */ + iobase = (bar_data[i / 4] & 0xffffffe0) + (i % 4) * 8; + + /* Enable UART I/O port */ + pci_write_config_byte(dev, config_base + 0x00, 0x01); + + /* Select 128-byte FIFO and 8x FIFO threshold */ + pci_write_config_byte(dev, config_base + 0x01, 0x33); + + /* LSB UART */ + pci_write_config_byte(dev, config_base + 0x04, + (u8)(iobase & 0xff)); + + /* MSB UART */ + pci_write_config_byte(dev, config_base + 0x05, + (u8)((iobase & 0xff00) >> 8)); + + /* First init without port data + * force init to RS232 Mode + */ + pci_write_config_byte(dev, config_base + 0x07, 0x01); + } + + return max_port; +} + +static int pci_fintek_f815xxa_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + return -ENOSYS; +} + +static int pci_fintek_f815xxa_init(struct pci_dev *dev) +{ + u32 max_port, i; + int config_base; + + if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM)) + return -ENODEV; + + switch (dev->device) { + case 0x1204: /* 4 ports */ + case 0x1208: /* 8 ports */ + max_port = dev->device & 0xff; + break; + case 0x1212: /* 12 ports */ + max_port = 12; + break; + default: + return -EINVAL; + } + + /* Set to mmio decode */ + pci_write_config_byte(dev, 0x209, 0x40); + + for (i = 0; i < max_port; ++i) { + /* UART0 configuration offset start from 0x2A0 */ + config_base = 0x2A0 + 0x08 * i; + + /* Select 128-byte FIFO and 8x FIFO threshold */ + pci_write_config_byte(dev, config_base + 0x01, 0x33); + + /* Enable UART I/O port */ + pci_write_config_byte(dev, config_base + 0, 0x01); + } + + return max_port; +} + +static int skip_tx_en_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + return -ENOSYS; +} + +static int kt_serial_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + return -ENOSYS; +} + +static int +pci_wch_ch353_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + return -ENOSYS; +} + +static int +pci_wch_ch355_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + return -ENOSYS; +} + +static int +pci_wch_ch38x_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + return -ENOSYS; +} + +static int +pci_sunix_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + return -ENOSYS; +} + +static int +pci_moxa_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + unsigned int bar = FL_GET_BASE(board->flags); + int offset; + + if (board->num_ports == 4 && idx == 3) + offset = 7 * board->uart_offset; + else + offset = idx * board->uart_offset; + + return setup_port(priv, port, bar, offset, 0); +} + +#define PCI_VENDOR_ID_SBSMODULARIO 0x124B +#define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B +#define PCI_DEVICE_ID_OCTPRO 0x0001 +#define PCI_SUBDEVICE_ID_OCTPRO232 0x0108 +#define PCI_SUBDEVICE_ID_OCTPRO422 0x0208 +#define PCI_SUBDEVICE_ID_POCTAL232 0x0308 +#define PCI_SUBDEVICE_ID_POCTAL422 0x0408 +#define PCI_SUBDEVICE_ID_SIIG_DUAL_00 0x2500 +#define PCI_SUBDEVICE_ID_SIIG_DUAL_30 0x2530 +#define PCI_VENDOR_ID_ADVANTECH 0x13fe +#define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66 +#define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620 +#define PCI_DEVICE_ID_ADVANTECH_PCI3618 0x3618 +#define PCI_DEVICE_ID_ADVANTECH_PCIf618 0xf618 +#define PCI_DEVICE_ID_TITAN_200I 0x8028 +#define PCI_DEVICE_ID_TITAN_400I 0x8048 +#define PCI_DEVICE_ID_TITAN_800I 0x8088 +#define PCI_DEVICE_ID_TITAN_800EH 0xA007 +#define PCI_DEVICE_ID_TITAN_800EHB 0xA008 +#define PCI_DEVICE_ID_TITAN_400EH 0xA009 +#define PCI_DEVICE_ID_TITAN_100E 0xA010 +#define PCI_DEVICE_ID_TITAN_200E 0xA012 +#define PCI_DEVICE_ID_TITAN_400E 0xA013 +#define PCI_DEVICE_ID_TITAN_800E 0xA014 +#define PCI_DEVICE_ID_TITAN_200EI 0xA016 +#define PCI_DEVICE_ID_TITAN_200EISI 0xA017 +#define PCI_DEVICE_ID_TITAN_200V3 0xA306 +#define PCI_DEVICE_ID_TITAN_400V3 0xA310 +#define PCI_DEVICE_ID_TITAN_410V3 0xA312 +#define PCI_DEVICE_ID_TITAN_800V3 0xA314 +#define PCI_DEVICE_ID_TITAN_800V3B 0xA315 +#define PCI_DEVICE_ID_OXSEMI_16PCI958 0x9538 +#define PCIE_DEVICE_ID_NEO_2_OX_IBM 0x00F6 +#define PCI_DEVICE_ID_PLX_CRONYX_OMEGA 0xc001 +#define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d +#define PCI_VENDOR_ID_WCH 0x4348 +#define PCI_DEVICE_ID_WCH_CH352_2S 0x3253 +#define PCI_DEVICE_ID_WCH_CH353_4S 0x3453 +#define PCI_DEVICE_ID_WCH_CH353_2S1PF 0x5046 +#define PCI_DEVICE_ID_WCH_CH353_1S1P 0x5053 +#define PCI_DEVICE_ID_WCH_CH353_2S1P 0x7053 +#define PCI_DEVICE_ID_WCH_CH355_4S 0x7173 +#define PCI_VENDOR_ID_AGESTAR 0x5372 +#define PCI_DEVICE_ID_AGESTAR_9375 0x6872 +#define PCI_VENDOR_ID_ASIX 0x9710 +#define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a +#define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e + +#define PCIE_VENDOR_ID_WCH 0x1c00 +#define PCIE_DEVICE_ID_WCH_CH382_2S1P 0x3250 +#define PCIE_DEVICE_ID_WCH_CH384_4S 0x3470 +#define PCIE_DEVICE_ID_WCH_CH382_2S 0x3253 + +#define PCI_VENDOR_ID_PERICOM 0x12D8 +#define PCI_DEVICE_ID_PERICOM_PI7C9X7951 0x7951 +#define PCI_DEVICE_ID_PERICOM_PI7C9X7952 0x7952 +#define PCI_DEVICE_ID_PERICOM_PI7C9X7954 0x7954 +#define PCI_DEVICE_ID_PERICOM_PI7C9X7958 0x7958 + +#define PCI_VENDOR_ID_ACCESIO 0x494f +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SDB 0x1051 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2S 0x1053 +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB 0x105C +#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4S 0x105E +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_2DB 0x1091 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_2 0x1093 +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB 0x1099 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_4 0x109B +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SMDB 0x10D1 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2SM 0x10D3 +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB 0x10DA +#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4SM 0x10DC +#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_1 0x1108 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_2 0x1110 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_2 0x1111 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4 0x1118 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_4 0x1119 +#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2S 0x1152 +#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S 0x115A +#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_2 0x1190 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_2 0x1191 +#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4 0x1198 +#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_4 0x1199 +#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2SM 0x11D0 +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4 0x105A +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4 0x105B +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM422_8 0x106A +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM485_8 0x106B +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4 0x1098 +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_8 0x10A9 +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM 0x10D9 +#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_8SM 0x10E9 +#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM 0x11D8 + +#define PCIE_DEVICE_ID_NI_PXIE8430_2328 0x74C2 +#define PCIE_DEVICE_ID_NI_PXIE8430_23216 0x74C1 +#define PCI_DEVICE_ID_NI_PXI8431_4852 0x7081 +#define PCI_DEVICE_ID_NI_PXI8431_4854 0x70DE +#define PCI_DEVICE_ID_NI_PXI8431_4858 0x70E3 +#define PCI_DEVICE_ID_NI_PXI8433_4852 0x70E9 +#define PCI_DEVICE_ID_NI_PXI8433_4854 0x70ED +#define PCIE_DEVICE_ID_NI_PXIE8431_4858 0x74C4 +#define PCIE_DEVICE_ID_NI_PXIE8431_48516 0x74C3 + +#define PCI_DEVICE_ID_MOXA_CP102E 0x1024 +#define PCI_DEVICE_ID_MOXA_CP102EL 0x1025 +#define PCI_DEVICE_ID_MOXA_CP104EL_A 0x1045 +#define PCI_DEVICE_ID_MOXA_CP114EL 0x1144 +#define PCI_DEVICE_ID_MOXA_CP116E_A_A 0x1160 +#define PCI_DEVICE_ID_MOXA_CP116E_A_B 0x1161 +#define PCI_DEVICE_ID_MOXA_CP118EL_A 0x1182 +#define PCI_DEVICE_ID_MOXA_CP118E_A_I 0x1183 +#define PCI_DEVICE_ID_MOXA_CP132EL 0x1322 +#define PCI_DEVICE_ID_MOXA_CP134EL_A 0x1342 +#define PCI_DEVICE_ID_MOXA_CP138E_A 0x1381 +#define PCI_DEVICE_ID_MOXA_CP168EL_A 0x1683 + +/* Unknown vendors/cards - this should not be in linux/pci_ids.h */ +#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 +#define PCI_SUBDEVICE_ID_UNKNOWN_0x1588 0x1588 + +/* + * Master list of serial port init/setup/exit quirks. + * This does not describe the general nature of the port. + * (ie, baud base, number and location of ports, etc) + * + * This list is ordered alphabetically by vendor then device. + * Specific entries must come before more generic entries. + */ +static struct pci_serial_quirk pci_serial_quirks[] = { + /* + * ADDI-DATA GmbH communication cards <info@addi-data.com> + */ + { + .vendor = PCI_VENDOR_ID_AMCC, + .device = PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = addidata_apci7800_setup, + }, + /* + * AFAVLAB cards - these may be called via parport_serial + * It is not clear whether this applies to all products. + */ + { + .vendor = PCI_VENDOR_ID_AFAVLAB, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = afavlab_setup, + }, + /* + * HP Diva + */ + { + .vendor = PCI_VENDOR_ID_HP, + .device = PCI_DEVICE_ID_HP_DIVA, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_hp_diva_init, + .setup = pci_hp_diva_setup, + }, + /* + * Intel + */ + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_80960_RP, + .subvendor = 0xe4bf, + .subdevice = PCI_ANY_ID, + .init = pci_inteli960ni_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_8257X_SOL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = skip_tx_en_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82573L_SOL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = skip_tx_en_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82573E_SOL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = skip_tx_en_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CE4100_UART, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = ce4100_serial_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_PATSBURG_KT, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = kt_serial_setup, + }, + /* + * ITE + */ + { + .vendor = PCI_VENDOR_ID_ITE, + .device = PCI_DEVICE_ID_ITE_8872, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ite887x_init, + .setup = pci_default_setup, + .exit = pci_ite887x_exit, + }, + /* + * National Instruments + */ + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI23216, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI2328, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI2324, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI2322, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI2324I, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI2322I, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8420_23216, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8420_2328, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8420_2324, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8420_2322, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8422_2324, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8422_2322, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8430_init, + .setup = pci_ni8430_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCIE_DEVICE_ID_NI_PXIE8430_2328, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8430_init, + .setup = pci_ni8430_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCIE_DEVICE_ID_NI_PXIE8430_23216, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8430_init, + .setup = pci_ni8430_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8431_4852, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8430_init, + .setup = pci_ni8431_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8431_4854, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8430_init, + .setup = pci_ni8431_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8431_4858, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8430_init, + .setup = pci_ni8431_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8433_4852, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8430_init, + .setup = pci_ni8431_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8433_4854, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8430_init, + .setup = pci_ni8431_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCIE_DEVICE_ID_NI_PXIE8431_4858, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8430_init, + .setup = pci_ni8431_setup, + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCIE_DEVICE_ID_NI_PXIE8431_48516, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8430_init, + .setup = pci_ni8431_setup, + }, + /* Quatech */ + { + .vendor = PCI_VENDOR_ID_QUATECH, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_quatech_init, + .setup = pci_quatech_setup, + }, + /* + * Panacom + */ + { + .vendor = PCI_VENDOR_ID_PANACOM, + .device = PCI_DEVICE_ID_PANACOM_QUADMODEM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_plx9050_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_PANACOM, + .device = PCI_DEVICE_ID_PANACOM_DUALMODEM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_plx9050_init, + .setup = pci_default_setup, + }, + /* + * Pericom (Only 7954 - It have a offset jump for port 4) + */ + { + .vendor = PCI_VENDOR_ID_PERICOM, + .device = PCI_DEVICE_ID_PERICOM_PI7C9X7954, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + /* + * PLX + */ + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9050, + .subvendor = PCI_SUBVENDOR_ID_EXSYS, + .subdevice = PCI_SUBDEVICE_ID_EXSYS_4055, + .init = pci_plx9050_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9050, + .subvendor = PCI_SUBVENDOR_ID_KEYSPAN, + .subdevice = PCI_SUBDEVICE_ID_KEYSPAN_SX2, + .init = pci_plx9050_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_ROMULUS, + .subvendor = PCI_VENDOR_ID_PLX, + .subdevice = PCI_DEVICE_ID_PLX_ROMULUS, + .init = pci_plx9050_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_4, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4SM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_4, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + { + .vendor = PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S, + .device = PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_4, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup_four_at_eight, + }, + { + .vendor = PCI_VENDOR_ID_ACCESIO, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup, + }, /* + * SBS Technologies, Inc., PMC-OCTALPRO 232 + */ + { + .vendor = PCI_VENDOR_ID_SBSMODULARIO, + .device = PCI_DEVICE_ID_OCTPRO, + .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, + .subdevice = PCI_SUBDEVICE_ID_OCTPRO232, + .init = sbs_init, + .setup = sbs_setup, + }, + /* + * SBS Technologies, Inc., PMC-OCTALPRO 422 + */ + { + .vendor = PCI_VENDOR_ID_SBSMODULARIO, + .device = PCI_DEVICE_ID_OCTPRO, + .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, + .subdevice = PCI_SUBDEVICE_ID_OCTPRO422, + .init = sbs_init, + .setup = sbs_setup, + }, + /* + * SBS Technologies, Inc., P-Octal 232 + */ + { + .vendor = PCI_VENDOR_ID_SBSMODULARIO, + .device = PCI_DEVICE_ID_OCTPRO, + .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, + .subdevice = PCI_SUBDEVICE_ID_POCTAL232, + .init = sbs_init, + .setup = sbs_setup, + }, + /* + * SBS Technologies, Inc., P-Octal 422 + */ + { + .vendor = PCI_VENDOR_ID_SBSMODULARIO, + .device = PCI_DEVICE_ID_OCTPRO, + .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, + .subdevice = PCI_SUBDEVICE_ID_POCTAL422, + .init = sbs_init, + .setup = sbs_setup, + }, + /* + * SIIG cards - these may be called via parport_serial + */ + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig_init, + .setup = pci_siig_setup, + }, + /* + * Titan cards + */ + { + .vendor = PCI_VENDOR_ID_TITAN, + .device = PCI_DEVICE_ID_TITAN_400L, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = titan_400l_800l_setup, + }, + { + .vendor = PCI_VENDOR_ID_TITAN, + .device = PCI_DEVICE_ID_TITAN_800L, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = titan_400l_800l_setup, + }, + /* + * Timedia cards + */ + { + .vendor = PCI_VENDOR_ID_TIMEDIA, + .device = PCI_DEVICE_ID_TIMEDIA_1889, + .subvendor = PCI_VENDOR_ID_TIMEDIA, + .subdevice = PCI_ANY_ID, + .probe = pci_timedia_probe, + .init = pci_timedia_init, + .setup = pci_timedia_setup, + }, + { + .vendor = PCI_VENDOR_ID_TIMEDIA, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_timedia_setup, + }, + /* + * Sunix PCI serial boards + */ + { + .vendor = PCI_VENDOR_ID_SUNIX, + .device = PCI_DEVICE_ID_SUNIX_1999, + .subvendor = PCI_VENDOR_ID_SUNIX, + .subdevice = PCI_ANY_ID, + .setup = pci_sunix_setup, + }, + /* + * Xircom cards + */ + { + .vendor = PCI_VENDOR_ID_XIRCOM, + .device = PCI_DEVICE_ID_XIRCOM_X3201_MDM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_xircom_init, + .setup = pci_default_setup, + }, + /* + * Netmos cards - these may be called via parport_serial + */ + { + .vendor = PCI_VENDOR_ID_NETMOS, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_netmos_init, + .setup = pci_netmos_9900_setup, + }, + /* + * EndRun Technologies + */ + { + .vendor = PCI_VENDOR_ID_ENDRUN, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_endrun_init, + .setup = pci_default_setup, + }, + /* + * For Oxford Semiconductor Tornado based devices + */ + { + .vendor = PCI_VENDOR_ID_OXSEMI, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_MAINPINE, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_DIGI, + .device = PCIE_DEVICE_ID_NEO_2_OX_IBM, + .subvendor = PCI_SUBVENDOR_ID_IBM, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8811, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8812, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8813, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8814, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + }, + { + .vendor = 0x10DB, + .device = 0x8027, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + }, + { + .vendor = 0x10DB, + .device = 0x8028, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + }, + { + .vendor = 0x10DB, + .device = 0x8029, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + }, + { + .vendor = 0x10DB, + .device = 0x800C, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + }, + { + .vendor = 0x10DB, + .device = 0x800D, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + }, + /* + * Cronyx Omega PCI (PLX-chip based) + */ + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_CRONYX_OMEGA, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_omegapci_setup, + }, + /* WCH CH353 1S1P card (16550 clone) */ + { + .vendor = PCI_VENDOR_ID_WCH, + .device = PCI_DEVICE_ID_WCH_CH353_1S1P, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch353_setup, + }, + /* WCH CH353 2S1P card (16550 clone) */ + { + .vendor = PCI_VENDOR_ID_WCH, + .device = PCI_DEVICE_ID_WCH_CH353_2S1P, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch353_setup, + }, + /* WCH CH353 4S card (16550 clone) */ + { + .vendor = PCI_VENDOR_ID_WCH, + .device = PCI_DEVICE_ID_WCH_CH353_4S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch353_setup, + }, + /* WCH CH353 2S1PF card (16550 clone) */ + { + .vendor = PCI_VENDOR_ID_WCH, + .device = PCI_DEVICE_ID_WCH_CH353_2S1PF, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch353_setup, + }, + /* WCH CH352 2S card (16550 clone) */ + { + .vendor = PCI_VENDOR_ID_WCH, + .device = PCI_DEVICE_ID_WCH_CH352_2S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch353_setup, + }, + /* WCH CH355 4S card (16550 clone) */ + { + .vendor = PCI_VENDOR_ID_WCH, + .device = PCI_DEVICE_ID_WCH_CH355_4S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch355_setup, + }, + /* WCH CH382 2S card (16850 clone) */ + { + .vendor = PCIE_VENDOR_ID_WCH, + .device = PCIE_DEVICE_ID_WCH_CH382_2S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch38x_setup, + }, + /* WCH CH382 2S1P card (16850 clone) */ + { + .vendor = PCIE_VENDOR_ID_WCH, + .device = PCIE_DEVICE_ID_WCH_CH382_2S1P, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch38x_setup, + }, + /* WCH CH384 4S card (16850 clone) */ + { + .vendor = PCIE_VENDOR_ID_WCH, + .device = PCIE_DEVICE_ID_WCH_CH384_4S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch38x_setup, + }, + /* + * ASIX devices with FIFO bug + */ + { + .vendor = PCI_VENDOR_ID_ASIX, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_asix_setup, + }, + /* + * Broadcom TruManage (NetXtreme) + */ + { + .vendor = PCI_VENDOR_ID_BROADCOM, + .device = PCI_DEVICE_ID_BROADCOM_TRUMANAGE, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_brcm_trumanage_setup, + }, + { + .vendor = 0x1c29, + .device = 0x1104, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fintek_setup, + .init = pci_fintek_init, + }, + { + .vendor = 0x1c29, + .device = 0x1108, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fintek_setup, + .init = pci_fintek_init, + }, + { + .vendor = 0x1c29, + .device = 0x1112, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fintek_setup, + .init = pci_fintek_init, + }, + /* + * MOXA + */ + { + .vendor = PCI_VENDOR_ID_MOXA, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_moxa_setup, + }, + { + .vendor = 0x1c29, + .device = 0x1204, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fintek_f815xxa_setup, + .init = pci_fintek_f815xxa_init, + }, + { + .vendor = 0x1c29, + .device = 0x1208, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fintek_f815xxa_setup, + .init = pci_fintek_f815xxa_init, + }, + { + .vendor = 0x1c29, + .device = 0x1212, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fintek_f815xxa_setup, + .init = pci_fintek_f815xxa_init, + }, + + /* + * Default "match everything" terminator entry + */ + { + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + } +}; + +static inline int quirk_id_matches(u32 quirk_id, u32 dev_id) +{ + return quirk_id == PCI_ANY_ID || quirk_id == dev_id; +} + +static struct pci_serial_quirk *find_quirk(struct pci_dev *dev) +{ + struct pci_serial_quirk *quirk; + + for (quirk = pci_serial_quirks; ; quirk++) + if (quirk_id_matches(quirk->vendor, dev->vendor) && + quirk_id_matches(quirk->device, dev->device) && + quirk_id_matches(quirk->subvendor, dev->subsystem_vendor) && + quirk_id_matches(quirk->subdevice, dev->subsystem_device)) + break; + return quirk; +} + +/* + * This is the configuration table for all of the PCI serial boards + * which we support. It is directly indexed by the pci_board_num_t enum + * value, which is encoded in the pci_device_id PCI probe table's + * driver_data member. + * + * The makeup of these names are: + * pbn_bn{_bt}_n_baud{_offsetinhex} + * + * bn = PCI BAR number + * bt = Index using PCI BARs + * n = number of serial ports + * baud = baud rate + * offsetinhex = offset for each sequential port (in hex) + * + * This table is sorted by (in order): bn, bt, baud, offsetindex, n. + * + * Please note: in theory if n = 1, _bt infix should make no difference. + * ie, pbn_b0_1_115200 is the same as pbn_b0_bt_1_115200 + */ +enum pci_board_num_t { + pbn_default = 0, + + pbn_b0_1_115200, + pbn_b0_2_115200, + pbn_b0_4_115200, + pbn_b0_5_115200, + pbn_b0_8_115200, + + pbn_b0_1_921600, + pbn_b0_2_921600, + pbn_b0_4_921600, + + pbn_b0_2_1130000, + + pbn_b0_4_1152000, + + pbn_b0_4_1250000, + + pbn_b0_2_1843200, + pbn_b0_4_1843200, + + pbn_b0_1_4000000, + + pbn_b0_bt_1_115200, + pbn_b0_bt_2_115200, + pbn_b0_bt_4_115200, + pbn_b0_bt_8_115200, + + pbn_b0_bt_1_460800, + pbn_b0_bt_2_460800, + pbn_b0_bt_4_460800, + + pbn_b0_bt_1_921600, + pbn_b0_bt_2_921600, + pbn_b0_bt_4_921600, + pbn_b0_bt_8_921600, + + pbn_b1_1_115200, + pbn_b1_2_115200, + pbn_b1_4_115200, + pbn_b1_8_115200, + pbn_b1_16_115200, + + pbn_b1_1_921600, + pbn_b1_2_921600, + pbn_b1_4_921600, + pbn_b1_8_921600, + + pbn_b1_2_1250000, + + pbn_b1_bt_1_115200, + pbn_b1_bt_2_115200, + pbn_b1_bt_4_115200, + + pbn_b1_bt_2_921600, + + pbn_b1_1_1382400, + pbn_b1_2_1382400, + pbn_b1_4_1382400, + pbn_b1_8_1382400, + + pbn_b2_1_115200, + pbn_b2_2_115200, + pbn_b2_4_115200, + pbn_b2_8_115200, + + pbn_b2_1_460800, + pbn_b2_4_460800, + pbn_b2_8_460800, + pbn_b2_16_460800, + + pbn_b2_1_921600, + pbn_b2_4_921600, + pbn_b2_8_921600, + + pbn_b2_8_1152000, + + pbn_b2_bt_1_115200, + pbn_b2_bt_2_115200, + pbn_b2_bt_4_115200, + + pbn_b2_bt_2_921600, + pbn_b2_bt_4_921600, + + pbn_b3_2_115200, + pbn_b3_4_115200, + pbn_b3_8_115200, + + pbn_b4_bt_2_921600, + pbn_b4_bt_4_921600, + pbn_b4_bt_8_921600, + + /* + * Board-specific versions. + */ + pbn_panacom, + pbn_panacom2, + pbn_panacom4, + pbn_plx_romulus, + pbn_endrun_2_4000000, + pbn_oxsemi, + pbn_oxsemi_1_4000000, + pbn_oxsemi_2_4000000, + pbn_oxsemi_4_4000000, + pbn_oxsemi_8_4000000, + pbn_intel_i960, + pbn_sgi_ioc3, + pbn_computone_4, + pbn_computone_6, + pbn_computone_8, + pbn_sbsxrsio, + pbn_pasemi_1682M, + pbn_ni8430_2, + pbn_ni8430_4, + pbn_ni8430_8, + pbn_ni8430_16, + pbn_ni8430_pxie_8, + pbn_ni8430_pxie_16, + pbn_ni8431_2, + pbn_ni8431_4, + pbn_ni8431_8, + pbn_ni8431_pxie_8, + pbn_ni8431_pxie_16, + pbn_ADDIDATA_PCIe_1_3906250, + pbn_ADDIDATA_PCIe_2_3906250, + pbn_ADDIDATA_PCIe_4_3906250, + pbn_ADDIDATA_PCIe_8_3906250, + pbn_ce4100_1_115200, + pbn_omegapci, + pbn_NETMOS9900_2s_115200, + pbn_brcm_trumanage, + pbn_fintek_4, + pbn_fintek_8, + pbn_fintek_12, + pbn_fintek_F81504A, + pbn_fintek_F81508A, + pbn_fintek_F81512A, + pbn_wch382_2, + pbn_wch384_4, + pbn_pericom_PI7C9X7951, + pbn_pericom_PI7C9X7952, + pbn_pericom_PI7C9X7954, + pbn_pericom_PI7C9X7958, + pbn_sunix_pci_1s, + pbn_sunix_pci_2s, + pbn_sunix_pci_4s, + pbn_sunix_pci_8s, + pbn_sunix_pci_16s, + pbn_moxa8250_2p, + pbn_moxa8250_4p, + pbn_moxa8250_8p, +}; + +/* + * uart_offset - the space between channels + * reg_shift - describes how the UART registers are mapped + * to PCI memory by the card. + * For example IER register on SBS, Inc. PMC-OctPro is located at + * offset 0x10 from the UART base, while UART_IER is defined as 1 + * in include/linux/serial_reg.h, + * see first lines of serial_in() and serial_out() in 8250.c +*/ + +static struct pciserial_board pci_boards[] = { + [pbn_default] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_1_115200] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_2_115200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_4_115200] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_5_115200] = { + .flags = FL_BASE0, + .num_ports = 5, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_8_115200] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_1_921600] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_2_921600] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_4_921600] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b0_2_1130000] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 1130000, + .uart_offset = 8, + }, + + [pbn_b0_4_1152000] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 1152000, + .uart_offset = 8, + }, + + [pbn_b0_4_1250000] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 1250000, + .uart_offset = 8, + }, + + [pbn_b0_2_1843200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 1843200, + .uart_offset = 8, + }, + [pbn_b0_4_1843200] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 1843200, + .uart_offset = 8, + }, + + [pbn_b0_1_4000000] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 4000000, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_bt_2_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_bt_4_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_bt_8_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_460800] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b0_bt_2_460800] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b0_bt_4_460800] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 460800, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_2_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_4_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_8_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b1_1_115200] = { + .flags = FL_BASE1, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_2_115200] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_4_115200] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_8_115200] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_16_115200] = { + .flags = FL_BASE1, + .num_ports = 16, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b1_1_921600] = { + .flags = FL_BASE1, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_2_921600] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_4_921600] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_8_921600] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_2_1250000] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 1250000, + .uart_offset = 8, + }, + + [pbn_b1_bt_1_115200] = { + .flags = FL_BASE1|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_bt_2_115200] = { + .flags = FL_BASE1|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_bt_4_115200] = { + .flags = FL_BASE1|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b1_bt_2_921600] = { + .flags = FL_BASE1|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b1_1_1382400] = { + .flags = FL_BASE1, + .num_ports = 1, + .base_baud = 1382400, + .uart_offset = 8, + }, + [pbn_b1_2_1382400] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 1382400, + .uart_offset = 8, + }, + [pbn_b1_4_1382400] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 1382400, + .uart_offset = 8, + }, + [pbn_b1_8_1382400] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 1382400, + .uart_offset = 8, + }, + + [pbn_b2_1_115200] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_2_115200] = { + .flags = FL_BASE2, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_4_115200] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_8_115200] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b2_1_460800] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_4_460800] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_8_460800] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_16_460800] = { + .flags = FL_BASE2, + .num_ports = 16, + .base_baud = 460800, + .uart_offset = 8, + }, + + [pbn_b2_1_921600] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_4_921600] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_8_921600] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b2_8_1152000] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 1152000, + .uart_offset = 8, + }, + + [pbn_b2_bt_1_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_bt_2_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_bt_4_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b2_bt_2_921600] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_bt_4_921600] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b3_2_115200] = { + .flags = FL_BASE3, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b3_4_115200] = { + .flags = FL_BASE3, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b3_8_115200] = { + .flags = FL_BASE3, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b4_bt_2_921600] = { + .flags = FL_BASE4, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b4_bt_4_921600] = { + .flags = FL_BASE4, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b4_bt_8_921600] = { + .flags = FL_BASE4, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + /* + * Entries following this are board-specific. + */ + + /* + * Panacom - IOMEM + */ + [pbn_panacom] = { + .flags = FL_BASE2, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + [pbn_panacom2] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + [pbn_panacom4] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + + /* I think this entry is broken - the first_offset looks wrong --rmk */ + [pbn_plx_romulus] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8 << 2, + .reg_shift = 2, + .first_offset = 0x03, + }, + + /* + * EndRun Technologies + * Uses the size of PCI Base region 0 to + * signal now many ports are available + * 2 port 952 Uart support + */ + [pbn_endrun_2_4000000] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + + /* + * This board uses the size of PCI Base region 0 to + * signal now many ports are available + */ + [pbn_oxsemi] = { + .flags = FL_BASE0|FL_REGION_SZ_CAP, + .num_ports = 32, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_oxsemi_1_4000000] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_oxsemi_2_4000000] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_oxsemi_4_4000000] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_oxsemi_8_4000000] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + + + /* + * EKF addition for i960 Boards form EKF with serial port. + * Max 256 ports. + */ + [pbn_intel_i960] = { + .flags = FL_BASE0, + .num_ports = 32, + .base_baud = 921600, + .uart_offset = 8 << 2, + .reg_shift = 2, + .first_offset = 0x10000, + }, + [pbn_sgi_ioc3] = { + .flags = FL_BASE0|FL_NOIRQ, + .num_ports = 1, + .base_baud = 458333, + .uart_offset = 8, + .reg_shift = 0, + .first_offset = 0x20178, + }, + + /* + * Computone - uses IOMEM. + */ + [pbn_computone_4] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, + [pbn_computone_6] = { + .flags = FL_BASE0, + .num_ports = 6, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, + [pbn_computone_8] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, + [pbn_sbsxrsio] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 460800, + .uart_offset = 256, + .reg_shift = 4, + }, + /* + * PA Semi PWRficient PA6T-1682M on-chip UART + */ + [pbn_pasemi_1682M] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 8333333, + }, + /* + * National Instruments 843x + */ + [pbn_ni8430_16] = { + .flags = FL_BASE0, + .num_ports = 16, + .base_baud = 3686400, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8430_8] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 3686400, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8430_4] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 3686400, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8430_2] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 3686400, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8430_pxie_16] = { + .flags = FL_BASE0, + .num_ports = 16, + .base_baud = 3125000, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8430_pxie_8] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 3125000, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8431_8] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 3686400, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8431_4] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 3686400, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8431_2] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 3686400, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8431_pxie_16] = { + .flags = FL_BASE0, + .num_ports = 16, + .base_baud = 3125000, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8431_pxie_8] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 3125000, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + /* + * ADDI-DATA GmbH PCI-Express communication cards <info@addi-data.com> + */ + [pbn_ADDIDATA_PCIe_1_3906250] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ADDIDATA_PCIe_2_3906250] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ADDIDATA_PCIe_4_3906250] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ADDIDATA_PCIe_8_3906250] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ce4100_1_115200] = { + .flags = FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .reg_shift = 2, + }, + [pbn_omegapci] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 0x200, + }, + [pbn_NETMOS9900_2s_115200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 115200, + }, + [pbn_brcm_trumanage] = { + .flags = FL_BASE0, + .num_ports = 1, + .reg_shift = 2, + .base_baud = 115200, + }, + [pbn_fintek_4] = { + .num_ports = 4, + .uart_offset = 8, + .base_baud = 115200, + .first_offset = 0x40, + }, + [pbn_fintek_8] = { + .num_ports = 8, + .uart_offset = 8, + .base_baud = 115200, + .first_offset = 0x40, + }, + [pbn_fintek_12] = { + .num_ports = 12, + .uart_offset = 8, + .base_baud = 115200, + .first_offset = 0x40, + }, + [pbn_fintek_F81504A] = { + .num_ports = 4, + .uart_offset = 8, + .base_baud = 115200, + }, + [pbn_fintek_F81508A] = { + .num_ports = 8, + .uart_offset = 8, + .base_baud = 115200, + }, + [pbn_fintek_F81512A] = { + .num_ports = 12, + .uart_offset = 8, + .base_baud = 115200, + }, + [pbn_wch382_2] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + .first_offset = 0xC0, + }, + [pbn_wch384_4] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + .first_offset = 0xC0, + }, + /* + * Pericom PI7C9X795[1248] Uno/Dual/Quad/Octal UART + */ + [pbn_pericom_PI7C9X7951] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 0x8, + }, + [pbn_pericom_PI7C9X7952] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x8, + }, + [pbn_pericom_PI7C9X7954] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x8, + }, + [pbn_pericom_PI7C9X7958] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 0x8, + }, + [pbn_sunix_pci_1s] = { + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 0x8, + }, + [pbn_sunix_pci_2s] = { + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x8, + }, + [pbn_sunix_pci_4s] = { + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x8, + }, + [pbn_sunix_pci_8s] = { + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 0x8, + }, + [pbn_sunix_pci_16s] = { + .num_ports = 16, + .base_baud = 921600, + .uart_offset = 0x8, + }, + [pbn_moxa8250_2p] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x200, + }, + [pbn_moxa8250_4p] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x200, + }, + [pbn_moxa8250_8p] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 0x200, + }, +}; + +static const struct pci_device_id blacklist[] = { + /* softmodems */ + { PCI_VDEVICE(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */ + { PCI_VDEVICE(MOTOROLA, 0x3052), }, /* Motorola Si3052-based modem */ + { PCI_DEVICE(0x1543, 0x3052), }, /* Si3052-based modem, default IDs */ + + /* multi-io cards handled by parport_serial */ + { PCI_DEVICE(0x4348, 0x7053), }, /* WCH CH353 2S1P */ + { PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */ + { PCI_DEVICE(0x1c00, 0x3250), }, /* WCH CH382 2S1P */ + + /* Intel platforms with MID UART */ + { PCI_VDEVICE(INTEL, 0x081b), }, + { PCI_VDEVICE(INTEL, 0x081c), }, + { PCI_VDEVICE(INTEL, 0x081d), }, + { PCI_VDEVICE(INTEL, 0x1191), }, + { PCI_VDEVICE(INTEL, 0x18d8), }, + { PCI_VDEVICE(INTEL, 0x19d8), }, + + /* Intel platforms with DesignWare UART */ + { PCI_VDEVICE(INTEL, 0x0936), }, + { PCI_VDEVICE(INTEL, 0x0f0a), }, + { PCI_VDEVICE(INTEL, 0x0f0c), }, + { PCI_VDEVICE(INTEL, 0x228a), }, + { PCI_VDEVICE(INTEL, 0x228c), }, + { PCI_VDEVICE(INTEL, 0x9ce3), }, + { PCI_VDEVICE(INTEL, 0x9ce4), }, + + /* Exar devices */ + { PCI_VDEVICE(EXAR, PCI_ANY_ID), }, + { PCI_VDEVICE(COMMTECH, PCI_ANY_ID), }, + + /* End of the black list */ + { } +}; + +static int serial_pci_is_class_communication(struct pci_dev *dev) +{ + /* + * If it is not a communications device or the programming + * interface is greater than 6, give up. + */ + if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) && + ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MULTISERIAL) && + ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) || + (dev->class & 0xff) > 6) + return -ENODEV; + + return 0; +} + +/* + * Given a complete unknown PCI device, try to use some heuristics to + * guess what the configuration might be, based on the pitiful PCI + * serial specs. Returns 0 on success, -ENODEV on failure. + */ +static int +serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board) +{ + int num_iomem, num_port, first_port = -1, i; + int rc; + + rc = serial_pci_is_class_communication(dev); + if (rc) + return rc; + + /* + * Should we try to make guesses for multiport serial devices later? + */ + if ((dev->class >> 8) == PCI_CLASS_COMMUNICATION_MULTISERIAL) + return -ENODEV; + + num_iomem = num_port = 0; + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (pci_resource_flags(dev, i) & IORESOURCE_IO) { + num_port++; + if (first_port == -1) + first_port = i; + } + if (pci_resource_flags(dev, i) & IORESOURCE_MEM) + num_iomem++; + } + + /* + * If there is 1 or 0 iomem regions, and exactly one port, + * use it. We guess the number of ports based on the IO + * region size. + */ + if (num_iomem <= 1 && num_port == 1) { + board->flags = first_port; + board->num_ports = pci_resource_len(dev, first_port) / 8; + return 0; + } + + /* + * Now guess if we've got a board which indexes by BARs. + * Each IO BAR should be 8 bytes, and they should follow + * consecutively. + */ + first_port = -1; + num_port = 0; + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (pci_resource_flags(dev, i) & IORESOURCE_IO && + pci_resource_len(dev, i) == 8 && + (first_port == -1 || (first_port + num_port) == i)) { + num_port++; + if (first_port == -1) + first_port = i; + } + } + + if (num_port > 1) { + board->flags = first_port | FL_BASE_BARS; + board->num_ports = num_port; + return 0; + } + + return -ENODEV; +} + +static inline int +serial_pci_matches(const struct pciserial_board *board, + const struct pciserial_board *guessed) +{ + return + board->num_ports == guessed->num_ports && + board->base_baud == guessed->base_baud && + board->uart_offset == guessed->uart_offset && + board->reg_shift == guessed->reg_shift && + board->first_offset == guessed->first_offset; +} + +static struct serial_private * +pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) +{ + struct uart_8250_port uart; + struct serial_private *priv; + struct pci_serial_quirk *quirk; + int rc, nr_ports, i; + + nr_ports = board->num_ports; + + /* + * Find an init and setup quirks. + */ + quirk = find_quirk(dev); + + /* + * Run the new-style initialization function. + * The initialization function returns: + * <0 - error + * 0 - use board->num_ports + * >0 - number of ports + */ + if (quirk->init) { + rc = quirk->init(dev); + if (rc < 0) { + priv = ERR_PTR(rc); + goto err_out; + } + if (rc) + nr_ports = rc; + } + + priv = xzalloc(sizeof(struct serial_private) + + sizeof(unsigned int) * nr_ports); + + priv->dev = dev; + priv->quirk = quirk; + + memset(&uart, 0, sizeof(uart)); + uart.pdata = xzalloc(sizeof(*uart.pdata)); + uart.pdata->clock = board->base_baud * 16; + + for (i = 0; i < nr_ports; i++) { + struct device *ns16550_dev; + struct resource *res; + + rc = quirk->setup(priv, board, &uart, i); + if (rc == -ENOSYS) { + priv = ERR_PTR(-ENOSYS); + goto err_deinit; + } + + if (rc) + break; + + res = &uart.resource; + + dev_dbg(&dev->dev, "setup PCI %s console @ 0x%pa-0x%pa\n", + res->flags & IORESOURCE_MEM ? "MMIO" : "IO port", + &res->start, &res->end); + + ns16550_dev = device_alloc("ns16550_serial", DEVICE_ID_DYNAMIC); + ns16550_dev->platform_data = uart.pdata; + ns16550_dev->parent = &dev->dev; + device_add_resource(ns16550_dev, NULL, + res->start, res->end - res->start + 1, + res->flags); + + rc = platform_device_register(ns16550_dev); + if (rc < 0) { + dev_err(&dev->dev, "couldn't register PCI %s console @0x%pa: %s\n", + res->flags & IORESOURCE_MEM ? "MMIO" : "IO port", + &res->start, strerror(-rc)); + + break; + } + } + priv->nr = i; + priv->board = board; + return priv; + +err_deinit: + if (quirk->exit) + quirk->exit(dev); +err_out: + return priv; +} + +/* + * Probe one serial board. Unfortunately, there is no rhyme nor reason + * to the arrangement of serial ports on a PCI card. + */ +static int +pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) +{ + struct pci_serial_quirk *quirk; + struct serial_private *priv; + const struct pciserial_board *board; + const struct pci_device_id *exclude; + struct pciserial_board tmp; + int rc; + + quirk = find_quirk(dev); + if (quirk->probe) { + rc = quirk->probe(dev); + if (rc) + return rc; + } + + if (ent->driver_data >= ARRAY_SIZE(pci_boards)) { + dev_err(&dev->dev, "invalid driver_data: %ld\n", + ent->driver_data); + return -EINVAL; + } + + board = &pci_boards[ent->driver_data]; + + exclude = pci_match_id(blacklist, dev); + if (exclude) + return -ENODEV; + + rc = pci_enable_device(dev); + if (rc) + return rc; + + if (ent->driver_data == pbn_default) { + /* + * Use a copy of the pci_board entry for this; + * avoid changing entries in the table. + */ + memcpy(&tmp, board, sizeof(struct pciserial_board)); + board = &tmp; + + /* + * We matched one of our class entries. Try to + * determine the parameters of this board. + */ + rc = serial_pci_guess_board(dev, &tmp); + if (rc) + return rc; + } else { + /* + * We matched an explicit entry. If we are able to + * detect this boards settings with our heuristic, + * then we no longer need this entry. + */ + memcpy(&tmp, &pci_boards[pbn_default], + sizeof(struct pciserial_board)); + rc = serial_pci_guess_board(dev, &tmp); + if (rc == 0 && serial_pci_matches(board, &tmp)) + moan_device("Redundant entry in serial pci_table.", + dev); + } + + priv = pciserial_init_ports(dev, board); + if (IS_ERR(priv)) { + if (PTR_ERR(priv) == -ENOSYS) { + dev_err(&dev->dev, + "serial port requires not-yet-supported quirky behavior.\n" + "Consider porting it over from the Linux 8250_pci driver\n"); + } + + return PTR_ERR(priv); + } + + dev->dev.priv = priv; + return 0; +} + +static const struct pci_device_id serial_pci_tbl[] = { + /* Advantech use PCI_DEVICE_ID_ADVANTECH_PCI3620 (0x3620) as 'PCI_SUBVENDOR_ID' */ + { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620, + PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001, 0, 0, + pbn_b2_8_921600 }, + /* Advantech also use 0x3618 and 0xf618 */ + { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3618, + PCI_DEVICE_ID_ADVANTECH_PCI3618, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCIf618, + PCI_DEVICE_ID_ADVANTECH_PCI3618, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, + pbn_b1_8_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, + pbn_b1_4_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, + pbn_b1_2_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, + pbn_b1_8_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, + pbn_b1_4_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, + pbn_b1_2_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0, + pbn_b1_2_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ, 0, 0, + pbn_b1_2_1250000 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2, 0, 0, + pbn_b0_2_1843200 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4, 0, 0, + pbn_b0_4_1843200 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_VENDOR_ID_AFAVLAB, + PCI_SUBDEVICE_ID_AFAVLAB_P061, 0, 0, + pbn_b0_4_1152000 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_1_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_7803, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_460800 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_115200 }, + + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, + /* + * VScom SPCOM800, from sl@s.pl + */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_921600 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_4_921600 }, + /* Unknown card - subdevice 0x1584 */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, + PCI_SUBDEVICE_ID_UNKNOWN_0x1584, 0, 0, + pbn_b2_4_115200 }, + /* Unknown card - subdevice 0x1588 */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, + PCI_SUBDEVICE_ID_UNKNOWN_0x1588, 0, 0, + pbn_b2_8_115200 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_KEYSPAN, + PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, + pbn_panacom }, + { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_panacom4 }, + { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_panacom2 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, + PCI_VENDOR_ID_ESDGMBH, + PCI_DEVICE_ID_ESDGMBH_CPCIASIO4, 0, 0, + pbn_b2_4_115200 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, + pbn_b2_4_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, + pbn_b2_8_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, + pbn_b2_16_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, + pbn_b2_16_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, + PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, + pbn_b2_4_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, + PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, + pbn_b2_8_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_EXSYS, + PCI_SUBDEVICE_ID_EXSYS_4055, 0, 0, + pbn_b2_4_115200 }, + /* + * Megawolf Romulus PCI Serial Card, from Mike Hudson + * (Exoray@isys.ca) + */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, + 0x10b5, 0x106a, 0, 0, + pbn_plx_romulus }, + /* + * EndRun Technologies. PCI express device range. + * EndRun PTP/1588 has 2 Native UARTs. + */ + { PCI_VENDOR_ID_ENDRUN, PCI_DEVICE_ID_ENDRUN_1588, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_endrun_2_4000000 }, + /* + * Quatech cards. These actually have configurable clocks but for + * now we just use the default. + * + * 100 series are RS232, 200 series RS422, + */ + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_4_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_4_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_4_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_4_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_4_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_4_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESCLP100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_8_115200 }, + + { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, + 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL, + 0, 0, + pbn_b0_4_1152000 }, + { PCI_VENDOR_ID_OXSEMI, 0x9505, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + + /* + * The below card is a little controversial since it is the + * subject of a PCI vendor/device ID clash. (See + * www.ussg.iu.edu/hypermail/linux/kernel/0303.1/0516.html). + * For now just used the hex ID 0x950a. + */ + { PCI_VENDOR_ID_OXSEMI, 0x950a, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_00, + 0, 0, pbn_b0_2_115200 }, + { PCI_VENDOR_ID_OXSEMI, 0x950a, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_30, + 0, 0, pbn_b0_2_115200 }, + { PCI_VENDOR_ID_OXSEMI, 0x950a, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_2_1130000 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_C950, + PCI_VENDOR_ID_OXSEMI, PCI_SUBDEVICE_ID_OXSEMI_C950, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_115200 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI958, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_1152000 }, + + /* + * Oxford Semiconductor Inc. Tornado PCI express device range. + */ + { PCI_VENDOR_ID_OXSEMI, 0xc101, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc105, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc11b, /* OXPCIe952 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc11f, /* OXPCIe952 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc120, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc124, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc138, /* OXPCIe952 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc13d, /* OXPCIe952 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc140, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc141, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc144, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc145, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc158, /* OXPCIe952 2 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc15d, /* OXPCIe952 2 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc208, /* OXPCIe954 4 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_4_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc20d, /* OXPCIe954 4 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_4_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc308, /* OXPCIe958 8 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_8_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc30d, /* OXPCIe958 8 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_8_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc40b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc40f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc41b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc41f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc42b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc42f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc43b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc43f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc44b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc44f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc45b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc45f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc46b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc46f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc47b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc47f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc48b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc48f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc49b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc49f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4ab, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4af, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4bb, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4bf, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4cb, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4cf, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + /* + * Mainpine Inc. IQ Express "Rev3" utilizing OxSemi Tornado + */ + { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 1 Port V.34 Super-G3 Fax */ + PCI_VENDOR_ID_MAINPINE, 0x4001, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 2 Port V.34 Super-G3 Fax */ + PCI_VENDOR_ID_MAINPINE, 0x4002, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 4 Port V.34 Super-G3 Fax */ + PCI_VENDOR_ID_MAINPINE, 0x4004, 0, 0, + pbn_oxsemi_4_4000000 }, + { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 8 Port V.34 Super-G3 Fax */ + PCI_VENDOR_ID_MAINPINE, 0x4008, 0, 0, + pbn_oxsemi_8_4000000 }, + + /* + * Digi/IBM PCIe 2-port Async EIA-232 Adapter utilizing OxSemi Tornado + */ + { PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_2_OX_IBM, + PCI_SUBVENDOR_ID_IBM, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + + /* + * SBS Technologies, Inc. P-Octal and PMC-OCTPRO cards, + * from skokodyn@yahoo.com + */ + { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO232, 0, 0, + pbn_sbsxrsio }, + { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO422, 0, 0, + pbn_sbsxrsio }, + { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL232, 0, 0, + pbn_sbsxrsio }, + { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL422, 0, 0, + pbn_sbsxrsio }, + + /* + * Digitan DS560-558, from jimd@esoft.com + */ + { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_115200 }, + + /* + * Titan Electronic cards + * The 400L and 800L have a custom setup quirk. + */ + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_2_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b4_bt_2_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b4_bt_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b4_bt_8_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400EH, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EH, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EHB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_4_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_8_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EI, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EISI, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200V3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400V3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_410V3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800V3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800V3B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_460800 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_460800 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_460800 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_921600 }, + + /* + * Computone devices submitted by Doug McNash dmcnash@computone.com + */ + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, + 0, 0, pbn_computone_4 }, + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8, + 0, 0, pbn_computone_8 }, + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6, + 0, 0, pbn_computone_6 }, + + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi }, + { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, + PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_921600 }, + + /* + * Sunix PCI serial boards + */ + { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0001, 0, 0, + pbn_sunix_pci_1s }, + { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0002, 0, 0, + pbn_sunix_pci_2s }, + { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0004, 0, 0, + pbn_sunix_pci_4s }, + { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0084, 0, 0, + pbn_sunix_pci_4s }, + { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0008, 0, 0, + pbn_sunix_pci_8s }, + { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0088, 0, 0, + pbn_sunix_pci_8s }, + { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0010, 0, 0, + pbn_sunix_pci_16s }, + + /* + * AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org> + */ + { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_115200 }, + { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P030, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_115200 }, + + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_460800 }, + + /* + * Korenix Jetcard F0/F1 cards (JC1204, JC1208, JC1404, JC1408). + * Cards are identified by their subsystem vendor IDs, which + * (in hex) match the model number. + * + * Note that JC140x are RS422/485 cards which require ox950 + * ACR = 0x10, and as such are not currently fully supported. + */ + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1204, 0x0004, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, +/* { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1402, 0x0002, 0, 0, + pbn_b0_2_921600 }, */ +/* { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1404, 0x0004, 0, 0, + pbn_b0_4_921600 }, */ + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF1, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, + + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, + 0x1204, 0x0004, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF3, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, + /* + * Dell Remote Access Card 4 - Tim_T_Murphy@Dell.com + */ + { PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RAC4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_1382400 }, + + /* + * Dell Remote Access Card III - Tim_T_Murphy@Dell.com + */ + { PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RACIII, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_1382400 }, + + /* + * RAStel 2 port modem, gerg@moreton.com.au + */ + { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + + /* + * EKF addition for i960 Boards form EKF with serial port + */ + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80960_RP, + 0xE4BF, PCI_ANY_ID, 0, 0, + pbn_intel_i960 }, + + /* + * Xircom Cardbus/Ethernet combos + */ + { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_115200 }, + /* + * Xircom RBM56G cardbus modem - Dirk Arnold (temp entry) + */ + { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_RBM56G, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_115200 }, + + /* + * Untested PCI modems, sent in from various folks... + */ + + /* + * Elsa Model 56K PCI Modem, from Andreas Rath <arh@01019freenet.de> + */ + { PCI_VENDOR_ID_ROCKWELL, 0x1004, + 0x1048, 0x1500, 0, 0, + pbn_b1_1_115200 }, + + { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, + 0xFF00, 0, 0, 0, + pbn_sgi_ioc3 }, + + /* + * HP Diva card + */ + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, + PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_RMP3, 0, 0, + pbn_b1_1_115200 }, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_5_115200 }, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_AUX, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_115200 }, + + { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b3_2_115200 }, + { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b3_4_115200 }, + { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b3_8_115200 }, + /* + * Pericom PI7C9X795[1248] Uno/Dual/Quad/Octal UART + */ + { PCI_VENDOR_ID_PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7951, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_pericom_PI7C9X7951 }, + { PCI_VENDOR_ID_PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7952, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_pericom_PI7C9X7952 }, + { PCI_VENDOR_ID_PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7954, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7958, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_pericom_PI7C9X7958 }, + /* + * ACCES I/O Products quad + */ + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SDB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7952 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2S, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7952 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4S, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_2DB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7952 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7952 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SMDB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7952 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2SM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7952 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4SM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_1, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7951 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7952 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7952 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2S, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7952 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7952 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7952 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2SM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7952 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM422_8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7958 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM485_8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7958 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7958 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_8SM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7958 }, + { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pericom_PI7C9X7954 }, + /* + * Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke) + */ + { PCI_VENDOR_ID_TOPIC, PCI_DEVICE_ID_TOPIC_TP560, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_115200 }, + /* + * ITE + */ + { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + pbn_b1_bt_1_115200 }, + + /* + * IntaShield IS-200 + */ + { PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* 135a.0811 */ + pbn_b2_2_115200 }, + /* + * IntaShield IS-400 + */ + { PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS400, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* 135a.0dc0 */ + pbn_b2_4_115200 }, + /* + * BrainBoxes UC-260 + */ + { PCI_VENDOR_ID_INTASHIELD, 0x0D21, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00, + pbn_b2_4_115200 }, + { PCI_VENDOR_ID_INTASHIELD, 0x0E34, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00, + pbn_b2_4_115200 }, + /* + * Perle PCI-RAS cards + */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, + PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS4, + 0, 0, pbn_b2_4_921600 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, + PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS8, + 0, 0, pbn_b2_8_921600 }, + + /* + * Mainpine series cards: Fairly standard layout but fools + * parts of the autodetect in some cases and uses otherwise + * unmatched communications subclasses in the PCI Express case + */ + + { /* RockForceDUO */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0200, + 0, 0, pbn_b0_2_115200 }, + { /* RockForceQUATRO */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0300, + 0, 0, pbn_b0_4_115200 }, + { /* RockForceDUO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0400, + 0, 0, pbn_b0_2_115200 }, + { /* RockForceQUATRO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0500, + 0, 0, pbn_b0_4_115200 }, + { /* RockForce+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0600, + 0, 0, pbn_b0_2_115200 }, + { /* RockForce+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0700, + 0, 0, pbn_b0_4_115200 }, + { /* RockForceOCTO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0800, + 0, 0, pbn_b0_8_115200 }, + { /* RockForceDUO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0C00, + 0, 0, pbn_b0_2_115200 }, + { /* RockForceQUARTRO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0D00, + 0, 0, pbn_b0_4_115200 }, + { /* RockForceOCTO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x1D00, + 0, 0, pbn_b0_8_115200 }, + { /* RockForceD1 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2000, + 0, 0, pbn_b0_1_115200 }, + { /* RockForceF1 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2100, + 0, 0, pbn_b0_1_115200 }, + { /* RockForceD2 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2200, + 0, 0, pbn_b0_2_115200 }, + { /* RockForceF2 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2300, + 0, 0, pbn_b0_2_115200 }, + { /* RockForceD4 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2400, + 0, 0, pbn_b0_4_115200 }, + { /* RockForceF4 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2500, + 0, 0, pbn_b0_4_115200 }, + { /* RockForceD8 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2600, + 0, 0, pbn_b0_8_115200 }, + { /* RockForceF8 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2700, + 0, 0, pbn_b0_8_115200 }, + { /* IQ Express D1 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3000, + 0, 0, pbn_b0_1_115200 }, + { /* IQ Express F1 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3100, + 0, 0, pbn_b0_1_115200 }, + { /* IQ Express D2 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3200, + 0, 0, pbn_b0_2_115200 }, + { /* IQ Express F2 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3300, + 0, 0, pbn_b0_2_115200 }, + { /* IQ Express D4 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3400, + 0, 0, pbn_b0_4_115200 }, + { /* IQ Express F4 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3500, + 0, 0, pbn_b0_4_115200 }, + { /* IQ Express D8 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3C00, + 0, 0, pbn_b0_8_115200 }, + { /* IQ Express F8 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3D00, + 0, 0, pbn_b0_8_115200 }, + + + /* + * PA Semi PA6T-1682M on-chip UART + */ + { PCI_VENDOR_ID_PASEMI, 0xa004, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pasemi_1682M }, + + /* + * National Instruments + */ + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI23216, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_16_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2328, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_4_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_4_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_23216, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_16_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2328, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_4_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_4_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_2 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_2 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_4 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_4 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2328, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_8 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2328, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_8 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_23216, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_16 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_23216, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_16 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_2 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_2 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_4 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_4 }, + { PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8430_2328, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_pxie_8 }, + { PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8430_23216, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_pxie_16 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8431_4852, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8431_2 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8431_4854, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8431_4 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8431_4858, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8431_8 }, + { PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8431_4858, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8431_pxie_8 }, + { PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8431_48516, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8431_pxie_16 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8433_4852, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8431_2 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8433_4854, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8431_4 }, + + /* + * MOXA + */ + { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP102E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_moxa8250_2p }, + { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP102EL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_moxa8250_2p }, + { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP104EL_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_moxa8250_4p }, + { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP114EL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_moxa8250_4p }, + { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_moxa8250_8p }, + { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_moxa8250_8p }, + { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP118EL_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_moxa8250_8p }, + { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP118E_A_I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_moxa8250_8p }, + { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP132EL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_moxa8250_2p }, + { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP134EL_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_moxa8250_4p }, + { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP138E_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_moxa8250_8p }, + { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP168EL_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_moxa8250_8p }, + + /* + * ADDI-DATA GmbH communication cards <info@addi-data.com> + */ + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7500, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_4_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7420, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_2_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7300, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_AMCC, + PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b1_8_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7500_2, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_4_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7420_2, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_2_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7300_2, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7500_3, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_4_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7420_3, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_2_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7300_3, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7800_3, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_8_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7500, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_4_3906250 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7420, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_2_3906250 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7300, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_1_3906250 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7800, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_8_3906250 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, + PCI_VENDOR_ID_IBM, 0x0299, + 0, 0, pbn_b0_bt_2_115200 }, + + /* + * other NetMos 9835 devices are most likely handled by the + * parport_serial driver, check drivers/parport/parport_serial.c + * before adding them here. + */ + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + /* the 9901 is a rebranded 9912 */ + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9922, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9904, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x3002, + 0, 0, pbn_NETMOS9900_2s_115200 }, + + /* + * Best Connectivity and Rosewill PCI Multi I/O cards + */ + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x3002, + 0, 0, pbn_b0_bt_2_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x3004, + 0, 0, pbn_b0_bt_4_115200 }, + /* Intel CE4100 */ + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ce4100_1_115200 }, + + /* + * Cronyx Omega PCI + */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_CRONYX_OMEGA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_omegapci }, + + /* + * Broadcom TruManage + */ + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BROADCOM_TRUMANAGE, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_brcm_trumanage }, + + /* + * AgeStar as-prs2-009 + */ + { PCI_VENDOR_ID_AGESTAR, PCI_DEVICE_ID_AGESTAR_9375, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, pbn_b0_bt_2_115200 }, + + /* + * WCH CH353 series devices: The 2S1P is handled by parport_serial + * so not listed here. + */ + { PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH353_4S, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, pbn_b0_bt_4_115200 }, + + { PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH353_2S1PF, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, pbn_b0_bt_2_115200 }, + + { PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH355_4S, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, pbn_b0_bt_4_115200 }, + + { PCIE_VENDOR_ID_WCH, PCIE_DEVICE_ID_WCH_CH382_2S, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, pbn_wch382_2 }, + + { PCIE_VENDOR_ID_WCH, PCIE_DEVICE_ID_WCH_CH384_4S, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, pbn_wch384_4 }, + + /* Fintek PCI serial cards */ + { PCI_DEVICE(0x1c29, 0x1104), .driver_data = pbn_fintek_4 }, + { PCI_DEVICE(0x1c29, 0x1108), .driver_data = pbn_fintek_8 }, + { PCI_DEVICE(0x1c29, 0x1112), .driver_data = pbn_fintek_12 }, + { PCI_DEVICE(0x1c29, 0x1204), .driver_data = pbn_fintek_F81504A }, + { PCI_DEVICE(0x1c29, 0x1208), .driver_data = pbn_fintek_F81508A }, + { PCI_DEVICE(0x1c29, 0x1212), .driver_data = pbn_fintek_F81512A }, + + /* MKS Tenta SCOM-080x serial cards */ + { PCI_DEVICE(0x1601, 0x0800), .driver_data = pbn_b0_4_1250000 }, + { PCI_DEVICE(0x1601, 0xa801), .driver_data = pbn_b0_4_1250000 }, + + /* Amazon PCI serial device */ + { PCI_DEVICE(0x1d0f, 0x8250), .driver_data = pbn_b0_1_115200 }, + + /* + * These entries match devices with class COMMUNICATION_SERIAL, + * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL + */ + { PCI_ANY_ID, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_SERIAL << 8, + 0xffff00, pbn_default }, + { PCI_ANY_ID, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_MODEM << 8, + 0xffff00, pbn_default }, + { PCI_ANY_ID, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, + 0xffff00, pbn_default }, + { 0, } +}; + +static struct pci_driver serial_pci_driver = { + .name = "ns16550_serial_pci", + .probe = pciserial_init_one, + .id_table = serial_pci_tbl, +}; + +static int __init serial_pci_register(void) +{ + return pci_driver_register(&serial_pci_driver); +} +console_initcall(serial_pci_register); diff --git a/drivers/serial/serial_omap4_usbboot.c b/drivers/serial/serial_omap4_usbboot.c index 2ef026c24d..6592be4f35 100644 --- a/drivers/serial/serial_omap4_usbboot.c +++ b/drivers/serial/serial_omap4_usbboot.c @@ -1,20 +1,10 @@ -/* - * 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, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-or-later #include <common.h> #include <init.h> #include <malloc.h> #include <errno.h> -#include <mach/omap4_rom_usb.h> +#include <mach/omap/omap4_rom_usb.h> struct serial_omap4_usbboot_priv { struct console_device cdev; @@ -56,9 +46,15 @@ static int serial_omap4_usbboot_getc(struct console_device *cdev) return priv->val; } -static int serial_omap4_usbboot_probe(struct device_d *dev) +static int serial_omap4_usbboot_probe(struct device *dev) { struct serial_omap4_usbboot_priv *priv; + int ret; + + ret = omap4_usbboot_open(); + if (ret) + return ret; + priv = xzalloc(sizeof(*priv)); priv->cdev.dev = dev; @@ -70,7 +66,7 @@ static int serial_omap4_usbboot_probe(struct device_d *dev) return console_register(&priv->cdev); } -static struct driver_d serial_omap4_usbboot_driver = { +static struct driver serial_omap4_usbboot_driver = { .name = "serial_omap4_usbboot", .probe = serial_omap4_usbboot_probe, }; diff --git a/drivers/serial/serial_pl010.c b/drivers/serial/serial_pl010.c index 06f9d2dcdc..56b9e67610 100644 --- a/drivers/serial/serial_pl010.c +++ b/drivers/serial/serial_pl010.c @@ -1,27 +1,9 @@ -/* - * Copyright (C) 2010 Matthias Kaehlcke <matthias@kaehlcke.net> - * - * (C) Copyright 2000 - * Rob Taylor, Flying Pig Systems. robt@flyingpig.com. - * - * (C) Copyright 2004 - * ARM Ltd. - * Philippe Robin, <philippe.robin@arm.com> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2000 Rob Taylor <robt@flyingpig.com>, Flying Pig Systems +// SPDX-FileCopyrightText: 2004 ARM Ltd. +// SPDX-FileCopyrightText: 2010 Matthias Kaehlcke <matthias@kaehlcke.net> + +/* Contributor: Philippe Robin <philippe.robin@arm.com> */ /* Simple U-Boot driver for the PrimeCell PL010/PL011 UARTs */ @@ -133,7 +115,7 @@ static int pl010_tstc(struct console_device *cdev) return !(readl(&pl010->flag) & UART_PL010_FR_RXFE); } -static int pl010_probe(struct device_d *dev) +static int pl010_probe(struct device *dev) { struct resource *iores; struct console_device *cdev; @@ -156,7 +138,7 @@ static int pl010_probe(struct device_d *dev) return 0; } -static struct driver_d pl010_driver = { +static struct driver pl010_driver = { .name = "pl010_serial", .probe = pl010_probe, }; diff --git a/drivers/serial/serial_pl010.h b/drivers/serial/serial_pl010.h index ff3d2f9974..ba81a66da0 100644 --- a/drivers/serial/serial_pl010.h +++ b/drivers/serial/serial_pl010.h @@ -1,24 +1,8 @@ -/* - * Copyright (C) 2010 Matthias Kaehlcke <matthias@kaehlcke.net> - * - * (C) Copyright 2003, 2004 - * ARM Ltd. - * Philippe Robin, <philippe.robin@arm.com> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-FileCopyrightText: 2003, 2004 ARM Ltd. */ +/* SPDX-FileCopyrightText: 2010 Matthias Kaehlcke <matthias@kaehlcke.net> */ + +/* Contributor: Philippe Robin <philippe.robin@arm.com> */ struct hldc_struct { uint32_t ctrl; diff --git a/drivers/serial/serial_pxa.c b/drivers/serial/serial_pxa.c index a427437b5c..97342f923e 100644 --- a/drivers/serial/serial_pxa.c +++ b/drivers/serial/serial_pxa.c @@ -1,17 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * (c) 2009 Sascha Hauer <s.hauer@pengutronix.de> * 2010 by Marc Kleine-Budde <kernel@pengutronix.de> - * - * 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, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> @@ -19,7 +9,7 @@ #include <init.h> #include <malloc.h> -#include <mach/clock.h> +#include <mach/pxa/clock.h> #include <asm/io.h> #define RBR 0x00 /* Receive Buffer Register (read only) */ @@ -159,7 +149,7 @@ static int pxa_serial_setbaudrate(struct console_device *cdev, int baudrate) return 0; } -static int pxa_serial_probe(struct device_d *dev) +static int pxa_serial_probe(struct device *dev) { struct resource *iores; struct console_device *cdev; @@ -185,7 +175,7 @@ static int pxa_serial_probe(struct device_d *dev) return 0; } -static struct driver_d pxa_serial_driver = { +static struct driver pxa_serial_driver = { .name = "pxa_serial", .probe = pxa_serial_probe, }; diff --git a/drivers/serial/serial_s3c.c b/drivers/serial/serial_s3c.c deleted file mode 100644 index 1945560723..0000000000 --- a/drivers/serial/serial_s3c.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * (c) 2009...2011 Juergen Beisert <j.beisert@pengutronix.de> - * - * Based on code from: - * (c) 2004 Sascha Hauer <sascha@saschahauer.de> - * - * 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, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - */ - -#include <common.h> -#include <driver.h> -#include <init.h> -#include <malloc.h> -#include <io.h> -#include <mach/s3c-generic.h> -#include <mach/s3c-iomap.h> - -/* Note: Offsets are for little endian access */ -#define ULCON 0x00 /* line control */ -#define UCON 0x04 /* UART control */ -# define UCON_SET_CLK_SRC(x) (((x) & 0x03) << 10) -# define UCON_GET_CLK_SRC(x) (((x) >> 10) & 0x03) -#define UFCON 0x08 /* FIFO control */ -#define UMCON 0x0c /* modem control */ -#define UTRSTAT 0x10 /* Rx/Tx status */ -#define UERSTAT 0x14 /* error status */ -#define UFSTAT 0x18 /* FIFO status */ -#define UMSTAT 0x1c /* modem status */ -#define UTXH 0x20 /* transmitt */ -#define URXH 0x24 /* receive */ -#define UBRDIV 0x28 /* baudrate generator */ -#define UBRDIVSLOT 0x2c /* baudrate slot generator */ -#define UINTM 0x38 /* interrupt mask register */ - -struct s3c_uart { - void __iomem *regs; - struct console_device cdev; -}; - -#define to_s3c_uart(c) container_of(c, struct s3c_uart, cdev) - -/* each architecture has a preferred reference clock for its UARTs */ -static unsigned s3c_select_arch_input_clock(void) -{ - /* S3C24xx: 0=2=PCLK, 1=UEXTCLK, 3=FCLK/n */ - if (IS_ENABLED(CONFIG_ARCH_S3C24xx)) - return 0; /* use the internal PCLK */ - /* S3C64xx: 0=2=PCLK, 1=UCLK0, 3=UCLK1 */ - if (IS_ENABLED(CONFIG_ARCH_S3C64xx)) - return 3; /* use the internal UCLK1 */ - /* S5PCxx: 0=PCLK, 1=SCLK_UART */ - if (IS_ENABLED(CONFIG_ARCH_S5PCxx)) - return 0; /* use the internal PCLK */ -} - -static unsigned s3c_get_arch_uart_input_clock(void __iomem *base) -{ - unsigned reg = readw(base + UCON); - return s3c_get_uart_clk(UCON_GET_CLK_SRC(reg)); -} - -/* - * This table takes the fractional value of the baud divisor and gives - * the recommended setting for the UDIVSLOT register. Refer the datasheet - * for further details - */ -static const uint16_t udivslot_table[] __maybe_unused = { - 0x0000, 0x0080, 0x0808, 0x0888, 0x2222, 0x4924, 0x4A52, 0x54AA, - 0x5555, 0xD555, 0xD5D5, 0xDDD5, 0xDDDD, 0xDFDD, 0xDFDF, 0xFFDF, -}; - -static int s3c_serial_setbaudrate(struct console_device *cdev, int baudrate) -{ - struct s3c_uart *priv = to_s3c_uart(cdev); - void __iomem *base = priv->regs; - unsigned val; - - if (IS_ENABLED(CONFIG_DRIVER_SERIAL_S3C_IMPROVED)) { - val = s3c_get_arch_uart_input_clock(base) / baudrate; - writew(udivslot_table[val & 15], base + UBRDIVSLOT); - } - - val = s3c_get_arch_uart_input_clock(base) / (16 * baudrate) - 1; - writew(val, base + UBRDIV); - - return 0; -} - -static int s3c_serial_init_port(struct console_device *cdev) -{ - struct s3c_uart *priv = to_s3c_uart(cdev); - void __iomem *base = priv->regs; - - /* FIFO enable, Tx/Rx FIFO clear */ - writeb(0x07, base + UFCON); - writeb(0x00, base + UMCON); - - /* Normal,No parity,1 stop,8 bit */ - writeb(0x03, base + ULCON); - - /* - * S3C2440 SoC: - * - no clock divider - * all SoCs: - * - enable receive and transmit mode - */ - writew(0x0005 | UCON_SET_CLK_SRC(s3c_select_arch_input_clock()), - base + UCON); - - if (IS_ENABLED(CONFIG_DRIVER_SERIAL_S3C_IMPROVED)) - /* 'interrupt or polling mode' for both directions */ - writeb(0xf, base + UINTM); - - if (IS_ENABLED(CONFIG_DRIVER_SERIAL_S3C_AUTOSYNC)) - writeb(0x10, base + UMCON); /* enable auto flow control */ - else - writeb(0x01, base + UMCON); /* RTS up */ - - return 0; -} - -static void s3c_serial_putc(struct console_device *cdev, char c) -{ - struct s3c_uart *priv = to_s3c_uart(cdev); - void __iomem *base = priv->regs; - - /* Wait for Tx FIFO not full */ - while (!(readb(base + UTRSTAT) & 0x2)) - ; - - writeb(c, base + UTXH); -} - -static int s3c_serial_tstc(struct console_device *cdev) -{ - struct s3c_uart *priv = to_s3c_uart(cdev); - void __iomem *base = priv->regs; - - /* If receive fifo is empty, return false */ - if (readb(base + UTRSTAT) & 0x1) - return 1; - - return 0; -} - -static int s3c_serial_getc(struct console_device *cdev) -{ - struct s3c_uart *priv = to_s3c_uart(cdev); - void __iomem *base = priv->regs; - - /* wait for a character */ - while (!(readb(base + UTRSTAT) & 0x1)) - ; - - return readb(base + URXH); -} - -static void s3c_serial_flush(struct console_device *cdev) -{ - struct s3c_uart *priv = to_s3c_uart(cdev); - void __iomem *base = priv->regs; - - while (!(readb(base + UTRSTAT) & 0x4)) - ; -} - -static int s3c_serial_probe(struct device_d *dev) -{ - struct resource *iores; - struct s3c_uart *priv; - struct console_device *cdev; - - priv = xzalloc(sizeof(struct s3c_uart)); - cdev = &priv->cdev; - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - priv->regs = IOMEM(iores->start); - dev->priv = priv; - cdev->dev = dev; - cdev->tstc = s3c_serial_tstc; - cdev->putc = s3c_serial_putc; - cdev->getc = s3c_serial_getc; - cdev->flush = s3c_serial_flush; - cdev->setbrg = s3c_serial_setbaudrate; - - s3c_serial_init_port(cdev); - - /* Enable UART */ - console_register(cdev); - - return 0; -} - -static struct driver_d s3c_serial_driver = { - .name = "s3c_serial", - .probe = s3c_serial_probe, -}; -console_platform_driver(s3c_serial_driver); diff --git a/drivers/serial/serial_sbi.c b/drivers/serial/serial_sbi.c new file mode 100644 index 0000000000..dd3eef41d1 --- /dev/null +++ b/drivers/serial/serial_sbi.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Marcelo Politzer <marcelo.politzer@cartesi.io> + */ + +#include <common.h> +#include <driver.h> +#include <malloc.h> +#include <asm/sbi.h> + +struct sbi_serial_priv { + struct console_device cdev; + uint8_t b[2], head, tail; +}; + +static int sbi_serial_getc(struct console_device *cdev) +{ + struct sbi_serial_priv *priv = cdev->dev->priv; + if (priv->head == priv->tail) + return -1; + return priv->b[priv->head++ & 0x1]; +} + +static void sbi_serial_putc(struct console_device *cdev, const char ch) +{ + sbi_console_putchar(ch); +} + +static int sbi_serial_tstc(struct console_device *cdev) +{ + struct sbi_serial_priv *priv = cdev->dev->priv; + int c = sbi_console_getchar(); + + if (c != -1) + priv->b[priv->tail++ & 0x1] = c; + return priv->head != priv->tail; +} + +static int sbi_serial_probe(struct device *dev) +{ + struct sbi_serial_priv *priv; + + priv = dev->priv = xzalloc(sizeof(*priv)); + priv->cdev.dev = dev; + priv->cdev.putc = sbi_serial_putc; + priv->cdev.getc = sbi_serial_getc; + priv->cdev.tstc = sbi_serial_tstc; + priv->cdev.flush = 0; + priv->cdev.setbrg = 0; + + return console_register(&priv->cdev); +} + +static struct driver serial_sbi_driver = { + .name = "riscv-serial-sbi", + .probe = sbi_serial_probe, +}; +postcore_platform_driver(serial_sbi_driver); diff --git a/drivers/serial/serial_sifive.c b/drivers/serial/serial_sifive.c new file mode 100644 index 0000000000..f056233b4e --- /dev/null +++ b/drivers/serial/serial_sifive.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Anup Patel <anup@brainfault.org> + */ + +#include <common.h> +#include <driver.h> +#include <malloc.h> +#include <io.h> +#include <of.h> +#include <linux/err.h> +#include <linux/clk.h> + +#define UART_TXFIFO_FULL 0x80000000 +#define UART_RXFIFO_EMPTY 0x80000000 +#define UART_RXFIFO_DATA 0x000000ff +#define UART_TXCTRL_TXEN 0x1 +#define UART_RXCTRL_RXEN 0x1 + +/* IP register */ +#define UART_IP_RXWM 0x2 + +struct sifive_serial_regs { + u32 txfifo; + u32 rxfifo; + u32 txctrl; + u32 rxctrl; + u32 ie; + u32 ip; + u32 div; +}; + +struct sifive_serial_priv { + unsigned long freq; + struct sifive_serial_regs __iomem *regs; + struct console_device cdev; +}; + +#define to_priv(cdev) container_of(cdev, struct sifive_serial_priv, cdev) + +/** + * Find minimum divisor divides in_freq to max_target_hz; + * Based on uart driver n SiFive FSBL. + * + * f_baud = f_in / (div + 1) => div = (f_in / f_baud) - 1 + * The nearest integer solution requires rounding up as to not exceed + * max_target_hz. + * div = ceil(f_in / f_baud) - 1 + * = floor((f_in - 1 + f_baud) / f_baud) - 1 + * This should not overflow as long as (f_in - 1 + f_baud) does not exceed + * 2^32 - 1, which is unlikely since we represent frequencies in kHz. + */ +static inline unsigned int uart_min_clk_divisor(unsigned long in_freq, + unsigned long max_target_hz) +{ + unsigned long quotient = + (in_freq + max_target_hz - 1) / (max_target_hz); + /* Avoid underflow */ + if (quotient == 0) + return 0; + else + return quotient - 1; +} + +static void sifive_serial_init(struct sifive_serial_regs __iomem *regs) +{ + writel(UART_TXCTRL_TXEN, ®s->txctrl); + writel(UART_RXCTRL_RXEN, ®s->rxctrl); + writel(0, ®s->ie); +} + +static int sifive_serial_setbrg(struct console_device *cdev, int baudrate) +{ + struct sifive_serial_priv *priv = to_priv(cdev); + + writel((uart_min_clk_divisor(priv->freq, baudrate)), &priv->regs->div); + + return 0; +} + +static int sifive_serial_getc(struct console_device *cdev) +{ + struct sifive_serial_regs __iomem *regs = to_priv(cdev)->regs; + u32 ch; + + do { + ch = readl(®s->rxfifo); + } while (ch & UART_RXFIFO_EMPTY); + + return ch & UART_RXFIFO_DATA; +} + +static void sifive_serial_putc(struct console_device *cdev, const char ch) +{ + struct sifive_serial_regs __iomem *regs = to_priv(cdev)->regs; + + // TODO: how to check for !empty to utilize fifo? + while (readl(®s->txfifo) & UART_TXFIFO_FULL) + ; + + writel(ch, ®s->txfifo); +} + +static int sifive_serial_tstc(struct console_device *cdev) +{ + struct sifive_serial_regs __iomem *regs = to_priv(cdev)->regs; + + return readl(®s->ip) & UART_IP_RXWM; +} + +static void sifive_serial_flush(struct console_device *cdev) +{ + struct sifive_serial_regs __iomem *regs = to_priv(cdev)->regs; + + while (readl(®s->txfifo) & UART_TXFIFO_FULL) + ; +} + +static int sifive_serial_probe(struct device *dev) +{ + struct sifive_serial_priv *priv; + struct resource *iores; + struct clk *clk; + u32 freq; + int ret; + + clk = clk_get(dev, NULL); + if (!IS_ERR(clk)) { + freq = clk_get_rate(clk); + } else { + dev_dbg(dev, "failed to get clock. Fallback to device tree.\n"); + + ret = of_property_read_u32(dev->of_node, "clock-frequency", + &freq); + if (ret) { + dev_warn(dev, "unknown clock frequency\n"); + return ret; + } + } + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + priv = xzalloc(sizeof(*priv)); + + priv->freq = freq; + priv->regs = IOMEM(iores->start); + + priv->cdev.dev = dev; + priv->cdev.putc = sifive_serial_putc; + priv->cdev.getc = sifive_serial_getc; + priv->cdev.tstc = sifive_serial_tstc; + priv->cdev.flush = sifive_serial_flush; + priv->cdev.setbrg = sifive_serial_setbrg, + + sifive_serial_init(priv->regs); + + return console_register(&priv->cdev); +} + +static __maybe_unused struct of_device_id sifive_serial_dt_ids[] = { + { .compatible = "sifive,uart0" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sifive_serial_dt_ids); + +static struct driver serial_sifive_driver = { + .name = "serial_sifive", + .probe = sifive_serial_probe, + .of_compatible = sifive_serial_dt_ids, +}; +console_platform_driver(serial_sifive_driver); diff --git a/drivers/serial/serial_stm32.c b/drivers/serial/serial_stm32.c index 4bbfb1eef7..3e18a2c152 100644 --- a/drivers/serial/serial_stm32.c +++ b/drivers/serial/serial_stm32.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2016, STMicroelectronics - All Rights Reserved * Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics. @@ -41,6 +41,8 @@ static int stm32_serial_setbaudrate(struct console_device *cdev, int baudrate) unsigned long clock_rate; clock_rate = clk_get_rate(stm32->clk); + if (!clock_rate) + return -EINVAL; int_div = DIV_ROUND_CLOSEST(clock_rate, baudrate); @@ -136,7 +138,7 @@ static void stm32_serial_init(struct console_device *cdev) writel(cr1, base + CR1_OFFSET(stm32f4)); } -static int stm32_serial_probe(struct device_d *dev) +static int stm32_serial_probe(struct device *dev) { int ret; struct console_device *cdev; @@ -163,7 +165,7 @@ static int stm32_serial_probe(struct device_d *dev) stm32->stm32f4 = info->stm32f4; stm32->uart_enable_bit = info->uart_enable_bit; - stm32->clk = clk_get(dev, NULL); + stm32->clk = clk_get_for_console(dev, NULL); if (IS_ERR(stm32->clk)) { ret = PTR_ERR(stm32->clk); dev_err(dev, "Failed to get UART clock %d\n", ret); @@ -181,11 +183,11 @@ static int stm32_serial_probe(struct device_d *dev) cdev->putc = stm32_serial_putc; cdev->getc = stm32_serial_getc; cdev->flush = stm32_serial_flush; - cdev->setbrg = stm32_serial_setbaudrate; + cdev->setbrg = stm32->clk ? stm32_serial_setbaudrate : NULL; cdev->linux_console_name = "ttySTM"; - if (dev->device_node) { - devname = of_alias_get(dev->device_node); + if (dev->of_node) { + devname = of_alias_get(dev->of_node); if (devname) { cdev->devname = xstrdup(devname); cdev->devid = DEVICE_ID_SINGLE; @@ -238,8 +240,9 @@ static struct of_device_id stm32_serial_dt_ids[] = { }, { } }; +MODULE_DEVICE_TABLE(of, stm32_serial_dt_ids); -static struct driver_d stm32_serial_driver = { +static struct driver stm32_serial_driver = { .name = "stm32-serial", .probe = stm32_serial_probe, .of_compatible = DRV_OF_COMPAT(stm32_serial_dt_ids), diff --git a/drivers/serial/serial_stm32.h b/drivers/serial/serial_stm32.h index dd3e930c93..f519f2abac 100644 --- a/drivers/serial/serial_stm32.h +++ b/drivers/serial/serial_stm32.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2016, STMicroelectronics - All Rights Reserved * Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics. diff --git a/drivers/serial/stm-serial.c b/drivers/serial/stm-serial.c index ea482415ce..af30bfa71d 100644 --- a/drivers/serial/stm-serial.c +++ b/drivers/serial/stm-serial.c @@ -1,21 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * (C) Copyright 2010 Juergen Beisert - Pengutronix * * This code was inspired by some patches made for u-boot covered by: * (c) 2007 Sascha Hauer <s.hauer@pengutronix.de> * (C) Copyright 2009 Freescale Semiconductor, Inc. - * - * 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, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ /* @@ -31,7 +20,6 @@ #include <malloc.h> #include <linux/clk.h> #include <linux/err.h> -#include <mach/clock.h> #define UARTDBGDR 0x00 #define UARTDBGFR 0x18 @@ -143,7 +131,7 @@ static int stm_serial_init_port(struct stm_priv *priv) return 0; } -static int stm_serial_probe(struct device_d *dev) +static int stm_serial_probe(struct device *dev) { struct resource *iores; struct stm_priv *priv; @@ -159,12 +147,15 @@ static int stm_serial_probe(struct device_d *dev) cdev->flush = stm_serial_flush; cdev->setbrg = stm_serial_setbaudrate; cdev->dev = dev; + cdev->linux_console_name = "ttyAMA"; + cdev->linux_earlycon_name = "pl011"; dev->priv = priv; iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); priv->base = IOMEM(iores->start); + cdev->phys_base = priv->base; priv->clk = clk_get(dev, NULL); if (IS_ERR(priv->clk)) return PTR_ERR(priv->clk); @@ -189,8 +180,9 @@ static __maybe_unused struct of_device_id stm_serial_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, stm_serial_dt_ids); -static struct driver_d stm_serial_driver = { +static struct driver stm_serial_driver = { .name = "stm_serial", .probe = stm_serial_probe, .of_compatible = DRV_OF_COMPAT(stm_serial_dt_ids), diff --git a/drivers/serial/virtio_console.c b/drivers/serial/virtio_console.c new file mode 100644 index 0000000000..a4adb77610 --- /dev/null +++ b/drivers/serial/virtio_console.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation + * Copyright (C) 2009, 2010, 2011 Red Hat, Inc. + * Copyright (C) 2009, 2010, 2011 Amit Shah <amit.shah@redhat.com> + * Copyright (C) 2021 Ahmad Fatoum + * + * This ridiculously simple implementation does a DMA transfer for + * every single character. On the plus side, we neither need to + * buffer RX or to wade through TX to turn LFs to CRLFs. + */ +#include <common.h> +#include <driver.h> +#include <init.h> +#include <linux/list.h> +#include <malloc.h> +#include <console.h> +#include <xfuncs.h> +#include <linux/spinlock.h> +#include <linux/virtio.h> +#include <linux/virtio_ring.h> +#include <linux/virtio_console.h> + +struct virtio_console { + struct console_device cdev; + struct virtqueue *in_vq, *out_vq; + char inbuf[1]; +}; + +static bool have_one; + +/* + * The put_chars() callback is pretty straightforward. + * + * We turn the characters into a scatter-gather list, add it to the + * output queue and then kick the Host. Then we sit here waiting for + * it to finish: inefficient in theory, but in practice + * implementations will do it immediately (lguest's Launcher does). + */ +static void put_chars(struct virtio_console *virtcons, const char *buf, int count) +{ + struct virtqueue *out_vq = virtcons->out_vq; + unsigned int len; + struct virtio_sg *sgs[1] = { + &(struct virtio_sg) { .addr = (void *)buf, .length = count } + }; + + /* + * add_buf wants a token to identify this buffer: we hand it + * any non-NULL pointer, since there's only ever one buffer. + */ + if (virtqueue_add(out_vq, sgs, 1, 0) >= 0) { + /* Tell Host to go! */ + virtqueue_kick(out_vq); + /* Chill out until it's done with the buffer. */ + while (!virtqueue_get_buf(out_vq, &len)) + cpu_relax(); + } +} + +static void virtcons_putc(struct console_device *cdev, char c) +{ + struct virtio_console *virtcons = container_of(cdev, struct virtio_console, cdev); + + return put_chars(virtcons, &c, 1); +} + +/* + * Create a scatter-gather list representing our input buffer and put + * it in the queue. + */ +static void add_inbuf(struct virtio_console *virtcons) +{ + struct virtio_sg *sgs[1] = { &(struct virtio_sg) { + .addr = virtcons->inbuf, .length = sizeof(virtcons->inbuf) } + }; + + /* We should always be able to add one buffer to an empty queue. */ + if (virtqueue_add(virtcons->in_vq, sgs, 0, 1) < 0) + BUG(); + virtqueue_kick(virtcons->in_vq); +} + +static int virtcons_tstc(struct console_device *cdev) +{ + struct virtio_console *virtcons = container_of(cdev, struct virtio_console, cdev); + + return virtqueue_poll(virtcons->in_vq, virtcons->in_vq->last_used_idx); +} + +static int virtcons_getc(struct console_device *cdev) +{ + struct virtio_console *virtcons = container_of(cdev, struct virtio_console, cdev); + char *in; + int ch; + + in = virtqueue_get_buf(virtcons->in_vq, NULL); + if (!in) + BUG(); + + ch = *in; + + add_inbuf(virtcons); + + return ch; +} + +static int virtcons_probe(struct virtio_device *vdev) +{ + struct virtqueue *vqs[2]; + struct virtio_console *virtcons; + int err; + + if (have_one) { + /* Neither multiport consoles (one virtio_device for multiple consoles) + * nor multiple consoles (one virtio_device per each console + * is supported. I would've expected: + * -chardev socket,path=/tmp/bar,server,nowait,id=bar \ + * -device virtconsole,chardev=bar,name=console.bar \ + * -device virtio-serial-device \ + * -chardev socket,path=/tmp/baz,server,nowait,id=baz \ + * -device virtconsole,chardev=baz,name=console.baz \ + * to just work, but it doesn't + */ + dev_warn(&vdev->dev, + "Multiple virtio-console devices not supported yet\n"); + return -EEXIST; + } + + /* Find the queues. */ + err = virtio_find_vqs(vdev, 2, vqs); + if (err) + return err; + + virtcons = xzalloc(sizeof(*virtcons)); + + vdev->priv = virtcons; + + virtcons->in_vq = vqs[0]; + virtcons->out_vq = vqs[1]; + + /* Register the input buffer the first time. */ + add_inbuf(virtcons); + + virtcons->cdev.dev = &vdev->dev; + virtcons->cdev.tstc = virtcons_tstc; + virtcons->cdev.getc = virtcons_getc; + virtcons->cdev.putc = virtcons_putc; + + have_one = true; + + return console_register(&virtcons->cdev); +} + +static void virtcons_remove(struct virtio_device *vdev) +{ + struct virtio_console *virtcons = vdev->priv; + + vdev->config->reset(vdev); + console_unregister(&virtcons->cdev); + vdev->config->del_vqs(vdev); + + free(virtcons); +} + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_CONSOLE, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static struct virtio_driver virtio_console = { + .driver.name = "virtio_console", + .id_table = id_table, + .probe = virtcons_probe, + .remove = virtcons_remove, +}; +device_virtio_driver(virtio_console); + +MODULE_DESCRIPTION("Virtio console driver"); +MODULE_LICENSE("GPL"); |