diff options
| author | Niklas Cassel <cassel@kernel.org> | 2026-09-16 15:00:32 +0200 |
|---|---|---|
| committer | Niklas Cassel <cassel@kernel.org> | 2026-09-16 16:18:00 +0200 |
| commit | 8d836581f9b1f57ceaa2b47654754ef1260b410b (patch) | |
| tree | 1dbd4d0dfb99c2e238356cbe947e5b0be3e3a571 /drivers/tty/serial/8250 | |
| download | linux-stable-8d836581f9b1f57ceaa2b47654754ef1260b410b.tar.gz linux-stable-8d836581f9b1f57ceaa2b47654754ef1260b410b.zip | |
ata: libata-scsi: fix ata_dsm_trim_pages() kernel-docgrafted
make htmldocs fails with:
Documentation/driver-api/libata:604: ./drivers/ata/libata-scsi.c:2301:
ERROR: Unexpected indentation. [docutils]
The Return: section of ata_dsm_trim_pages() ends its first sentence with
a colon and continues with an indented bullet list. reStructuredText
requires a blank line before an indented block, so docutils chokes on
the list.
Simply adding the missing blank line does not work either: Return: is a
kernel-doc "special section", which is terminated by the first blank
line, so the bullet list would end up in the Description section,
detached from the sentence introducing it.
Spell the two bounds out as prose instead, so that the Return: section
stays self-contained. Documentation-only change.
Fixes: e64e6b5dc867 ("ata: libata-scsi: scale DSM TRIM payload by MAX PAGES PER DSM COMMAND")
Reported-by: Thomas Huth <thuth@redhat.com>
Closes: https://lore.kernel.org/linux-ide/b0a0b8e8-cc4f-4c7b-8bc5-fee0712d405a@redhat.com/
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Link: https://lore.kernel.org/r/20260916130031.29990-2-cassel@kernel.org
Signed-off-by: Niklas Cassel <cassel@kernel.org>
Diffstat (limited to 'drivers/tty/serial/8250')
53 files changed, 29907 insertions, 0 deletions
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h new file mode 100644 index 000000000..9337fec93 --- /dev/null +++ b/drivers/tty/serial/8250/8250.h @@ -0,0 +1,480 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Driver for 8250/16550-type serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King. + */ + +#include <linux/bits.h> +#include <linux/serial_8250.h> +#include <linux/serial_core.h> +#include <linux/dmaengine.h> + +#include "../serial_mctrl_gpio.h" + +struct uart_8250_dma { + int (*tx_dma)(struct uart_8250_port *p); + int (*rx_dma)(struct uart_8250_port *p); + void (*prepare_tx_dma)(struct uart_8250_port *p); + void (*prepare_rx_dma)(struct uart_8250_port *p); + + /* Filter function */ + dma_filter_fn fn; + /* Parameter to the filter function */ + void *rx_param; + void *tx_param; + + struct dma_slave_config rxconf; + struct dma_slave_config txconf; + + struct dma_chan *rxchan; + struct dma_chan *txchan; + + /* Device address base for DMA operations */ + phys_addr_t rx_dma_addr; + phys_addr_t tx_dma_addr; + + /* DMA address of the buffer in memory */ + dma_addr_t rx_addr; + dma_addr_t tx_addr; + + dma_cookie_t rx_cookie; + dma_cookie_t tx_cookie; + + void *rx_buf; + + size_t rx_size; + size_t tx_size; + + unsigned char tx_running; + unsigned char tx_err; + unsigned char rx_running; +}; + +struct old_serial_port { + unsigned int uart; + unsigned int baud_base; + unsigned int port; + unsigned int irq; + upf_t flags; + unsigned char io_type; + unsigned char __iomem *iomem_base; + unsigned short iomem_reg_shift; +}; + +struct serial8250_config { + const char *name; + unsigned short fifo_size; + unsigned short tx_loadsz; + unsigned char fcr; + unsigned char rxtrig_bytes[UART_FCR_R_TRIG_MAX_STATE]; + unsigned int flags; +}; + +#define UART_CAP_FIFO BIT(8) /* UART has FIFO */ +#define UART_CAP_EFR BIT(9) /* UART has EFR */ +#define UART_CAP_SLEEP BIT(10) /* UART has IER sleep */ +#define UART_CAP_AFE BIT(11) /* MCR-based hw flow control */ +#define UART_CAP_UUE BIT(12) /* UART needs IER bit 6 set (Xscale) */ +#define UART_CAP_RTOIE BIT(13) /* UART needs IER bit 4 set (Xscale, Tegra) */ +#define UART_CAP_HFIFO BIT(14) /* UART has a "hidden" FIFO */ +#define UART_CAP_RPM BIT(15) /* Runtime PM is active while idle */ +#define UART_CAP_IRDA BIT(16) /* UART supports IrDA line discipline */ +#define UART_CAP_MINI BIT(17) /* Mini UART on BCM283X family lacks: + * STOP PARITY EPAR SPAR WLEN5 WLEN6 + */ +#define UART_CAP_NOTEMT BIT(18) /* UART without interrupt on TEMT available */ + +#define UART_BUG_QUOT BIT(0) /* UART has buggy quot LSB */ +#define UART_BUG_TXEN BIT(1) /* UART has buggy TX IIR status */ +#define UART_BUG_NOMSR BIT(2) /* UART has buggy MSR status bits (Au1x00) */ +#define UART_BUG_THRE BIT(3) /* UART has buggy THRE reassertion */ +#define UART_BUG_TXRACE BIT(5) /* UART Tx fails to set remote DR */ + +/* Module parameters */ +#define UART_NR CONFIG_SERIAL_8250_NR_UARTS + +extern unsigned int nr_uarts; + +#define SERIAL8250_PORT_FLAGS(_base, _irq, _flags) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = UPF_BOOT_AUTOCONF | (_flags), \ + } + +#define SERIAL8250_PORT(_base, _irq) SERIAL8250_PORT_FLAGS(_base, _irq, 0) + +extern struct uart_driver serial8250_reg; +void serial8250_register_ports(struct uart_driver *drv, struct device *dev); + +/* Legacy ISA bus related APIs */ +typedef void (*serial8250_isa_config_fn)(int, struct uart_port *, u32 *); +extern serial8250_isa_config_fn serial8250_isa_config; + +void serial8250_isa_init_ports(void); + +extern struct platform_device *serial8250_isa_devs; + +extern const struct uart_ops *univ8250_port_base_ops; +extern struct uart_ops univ8250_port_ops; + +static inline int serial_in(struct uart_8250_port *up, int offset) +{ + return up->port.serial_in(&up->port, offset); +} + +static inline void serial_out(struct uart_8250_port *up, int offset, int value) +{ + up->port.serial_out(&up->port, offset, value); +} + +/** + * serial_lsr_in - Read LSR register and preserve flags across reads + * @up: uart 8250 port + * + * Read LSR register and handle saving non-preserved flags across reads. + * The flags that are not preserved across reads are stored into + * up->lsr_saved_flags. + * + * Returns LSR value or'ed with the preserved flags (if any). + */ +static inline u16 serial_lsr_in(struct uart_8250_port *up) +{ + u16 lsr = up->lsr_saved_flags; + + lsr |= serial_in(up, UART_LSR); + up->lsr_saved_flags = lsr & up->lsr_save_mask; + + return lsr; +} + +/* + * For the 16C950 + */ +static void serial_icr_write(struct uart_8250_port *up, int offset, int value) +{ + serial_out(up, UART_SCR, offset); + serial_out(up, UART_ICR, value); +} + +static unsigned int __maybe_unused serial_icr_read(struct uart_8250_port *up, + int offset) +{ + unsigned int value; + + serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD); + serial_out(up, UART_SCR, offset); + value = serial_in(up, UART_ICR); + serial_icr_write(up, UART_ACR, up->acr); + + return value; +} + +void serial8250_clear_fifos(struct uart_8250_port *p); +void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p); +void serial8250_fifo_wait_for_lsr_thre(struct uart_8250_port *up, + struct nbcon_write_context *wctxt, + unsigned int count); + +void serial8250_rpm_get(struct uart_8250_port *p); +void serial8250_rpm_put(struct uart_8250_port *p); +DEFINE_GUARD(serial8250_rpm, struct uart_8250_port *, + serial8250_rpm_get(_T), serial8250_rpm_put(_T)); + +static inline u32 serial_dl_read(struct uart_8250_port *up) +{ + return up->dl_read(up); +} + +static inline void serial_dl_write(struct uart_8250_port *up, u32 value) +{ + up->dl_write(up, value); +} + +static inline bool serial8250_set_THRI(struct uart_8250_port *up) +{ + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&up->port.lock); + + if (up->ier & UART_IER_THRI) + return false; + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + return true; +} + +static inline bool serial8250_clear_THRI(struct uart_8250_port *up) +{ + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&up->port.lock); + + if (!(up->ier & UART_IER_THRI)) + return false; + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + return true; +} + +struct uart_8250_port *serial8250_setup_port(int index); +struct uart_8250_port *serial8250_get_port(int line); + +int serial8250_em485_config(struct uart_port *port, struct ktermios *termios, + struct serial_rs485 *rs485); +void serial8250_em485_start_tx(struct uart_8250_port *p, bool toggle_ier); +void serial8250_em485_stop_tx(struct uart_8250_port *p, bool toggle_ier); +void serial8250_em485_destroy(struct uart_8250_port *p); +extern struct serial_rs485 serial8250_em485_supported; + +/* MCR <-> TIOCM conversion */ +static inline int serial8250_TIOCM_to_MCR(int tiocm) +{ + int mcr = 0; + + if (tiocm & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (tiocm & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (tiocm & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (tiocm & TIOCM_OUT2) + mcr |= UART_MCR_OUT2; + if (tiocm & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + return mcr; +} + +static inline int serial8250_MCR_to_TIOCM(int mcr) +{ + int tiocm = 0; + + if (mcr & UART_MCR_RTS) + tiocm |= TIOCM_RTS; + if (mcr & UART_MCR_DTR) + tiocm |= TIOCM_DTR; + if (mcr & UART_MCR_OUT1) + tiocm |= TIOCM_OUT1; + if (mcr & UART_MCR_OUT2) + tiocm |= TIOCM_OUT2; + if (mcr & UART_MCR_LOOP) + tiocm |= TIOCM_LOOP; + + return tiocm; +} + +/* MSR <-> TIOCM conversion */ +static inline int serial8250_MSR_to_TIOCM(int msr) +{ + int tiocm = 0; + + if (msr & UART_MSR_DCD) + tiocm |= TIOCM_CAR; + if (msr & UART_MSR_RI) + tiocm |= TIOCM_RNG; + if (msr & UART_MSR_DSR) + tiocm |= TIOCM_DSR; + if (msr & UART_MSR_CTS) + tiocm |= TIOCM_CTS; + + return tiocm; +} + +static inline void serial8250_out_MCR(struct uart_8250_port *up, int value) +{ + serial_out(up, UART_MCR, value); + + if (up->gpios) + mctrl_gpio_set(up->gpios, serial8250_MCR_to_TIOCM(value)); +} + +static inline int serial8250_in_MCR(struct uart_8250_port *up) +{ + int mctrl; + + mctrl = serial_in(up, UART_MCR); + + if (up->gpios) { + unsigned int mctrl_gpio = 0; + + mctrl_gpio = mctrl_gpio_get_outputs(up->gpios, &mctrl_gpio); + mctrl |= serial8250_TIOCM_to_MCR(mctrl_gpio); + } + + return mctrl; +} + +#ifdef CONFIG_SERIAL_8250_PNP +int serial8250_pnp_init(void); +void serial8250_pnp_exit(void); +#else +static inline int serial8250_pnp_init(void) { return 0; } +static inline void serial8250_pnp_exit(void) { } +#endif + +#ifdef CONFIG_SERIAL_8250_RSA +void univ8250_rsa_support(struct uart_ops *ops, const struct uart_ops *core_ops); +void rsa_enable(struct uart_8250_port *up); +void rsa_disable(struct uart_8250_port *up); +void rsa_autoconfig(struct uart_8250_port *up); +void rsa_reset(struct uart_8250_port *up); +#else +static inline void univ8250_rsa_support(struct uart_ops *ops, const struct uart_ops *core_ops) { } +static inline void rsa_enable(struct uart_8250_port *up) {} +static inline void rsa_disable(struct uart_8250_port *up) {} +static inline void rsa_autoconfig(struct uart_8250_port *up) {} +static inline void rsa_reset(struct uart_8250_port *up) {} +#endif + +#ifdef CONFIG_SERIAL_8250_FINTEK +int fintek_8250_probe(struct uart_8250_port *uart); +#else +static inline int fintek_8250_probe(struct uart_8250_port *uart) { return 0; } +#endif + +#if IS_REACHABLE(CONFIG_SERIAL_8250_HUB6) +bool hub6_match_port(const struct uart_port *port1, const struct uart_port *port2); +#else +static inline bool hub6_match_port(const struct uart_port *port1, const struct uart_port *port2) +{ return false; } +#endif + +#ifdef CONFIG_ARCH_OMAP1 +#include <linux/soc/ti/omap1-soc.h> +static inline int is_omap1_8250(struct uart_8250_port *pt) +{ + int res; + + switch (pt->port.mapbase) { + case OMAP1_UART1_BASE: + case OMAP1_UART2_BASE: + case OMAP1_UART3_BASE: + res = 1; + break; + default: + res = 0; + break; + } + + return res; +} + +static inline int is_omap1510_8250(struct uart_8250_port *pt) +{ + if (!cpu_is_omap1510()) + return 0; + + return is_omap1_8250(pt); +} +#else +static inline int is_omap1_8250(struct uart_8250_port *pt) +{ + return 0; +} +static inline int is_omap1510_8250(struct uart_8250_port *pt) +{ + return 0; +} +#endif + +#ifdef CONFIG_SERIAL_8250_DMA +extern int serial8250_tx_dma(struct uart_8250_port *); +extern void serial8250_tx_dma_flush(struct uart_8250_port *); +extern int serial8250_rx_dma(struct uart_8250_port *); +extern void serial8250_rx_dma_flush(struct uart_8250_port *); +extern int serial8250_request_dma(struct uart_8250_port *); +extern void serial8250_release_dma(struct uart_8250_port *); + +static inline void serial8250_do_prepare_tx_dma(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + + if (dma->prepare_tx_dma) + dma->prepare_tx_dma(p); +} + +static inline void serial8250_do_prepare_rx_dma(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + + if (dma->prepare_rx_dma) + dma->prepare_rx_dma(p); +} + +static inline bool serial8250_tx_dma_running(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + + return dma && dma->tx_running; +} + +static inline void serial8250_tx_dma_pause(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + + if (!dma->tx_running) + return; + + dmaengine_pause(dma->txchan); +} + +static inline void serial8250_tx_dma_resume(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + + if (!dma->tx_running) + return; + + dmaengine_resume(dma->txchan); +} +#else +static inline int serial8250_tx_dma(struct uart_8250_port *p) +{ + return -1; +} +static inline void serial8250_tx_dma_flush(struct uart_8250_port *p) { } +static inline int serial8250_rx_dma(struct uart_8250_port *p) +{ + return -1; +} +static inline void serial8250_rx_dma_flush(struct uart_8250_port *p) { } +static inline int serial8250_request_dma(struct uart_8250_port *p) +{ + return -1; +} +static inline void serial8250_release_dma(struct uart_8250_port *p) { } + +static inline bool serial8250_tx_dma_running(struct uart_8250_port *p) +{ + return false; +} + +static inline void serial8250_tx_dma_pause(struct uart_8250_port *p) { } +static inline void serial8250_tx_dma_resume(struct uart_8250_port *p) { } +#endif + +static inline int ns16550a_goto_highspeed(struct uart_8250_port *up) +{ + unsigned char status; + + status = serial_in(up, 0x04); /* EXCR2 */ +#define PRESL(x) ((x) & 0x30) + if (PRESL(status) == 0x10) { + /* already in high speed mode */ + return 0; + } else { + status &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */ + status |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ + serial_out(up, 0x04, status); + } + return 1; +} + +static inline int serial_index(struct uart_port *port) +{ + return port->minor - 64; +} diff --git a/drivers/tty/serial/8250/8250_accent.c b/drivers/tty/serial/8250/8250_accent.c new file mode 100644 index 000000000..1691f1a57 --- /dev/null +++ b/drivers/tty/serial/8250/8250_accent.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/serial_8250.h> + +#include "8250.h" + +static struct plat_serial8250_port accent_data[] = { + SERIAL8250_PORT(0x330, 4), + SERIAL8250_PORT(0x338, 4), + { }, +}; + +static struct platform_device accent_device = { + .name = "serial8250", + .id = PLAT8250_DEV_ACCENT, + .dev = { + .platform_data = accent_data, + }, +}; + +static int __init accent_init(void) +{ + return platform_device_register(&accent_device); +} + +module_init(accent_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for Accent Async cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_acorn.c b/drivers/tty/serial/8250/8250_acorn.c new file mode 100644 index 000000000..fb596262b --- /dev/null +++ b/drivers/tty/serial/8250/8250_acorn.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/drivers/serial/acorn.c + * + * Copyright (C) 1996-2003 Russell King. + */ +#include <linux/module.h> +#include <linux/types.h> +#include <linux/tty.h> +#include <linux/serial_core.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/init.h> + +#include <asm/io.h> +#include <asm/ecard.h> +#include <asm/string.h> + +#include "8250.h" + +#define MAX_PORTS 3 + +struct serial_card_type { + unsigned int num_ports; + unsigned int uartclk; + unsigned int type; + unsigned int offset[MAX_PORTS]; +}; + +struct serial_card_info { + unsigned int num_ports; + int ports[MAX_PORTS]; + void __iomem *vaddr; +}; + +static int +serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct serial_card_info *info; + struct serial_card_type *type = id->data; + struct uart_8250_port uart; + unsigned long bus_addr; + unsigned int i; + + info = kzalloc_obj(struct serial_card_info); + if (!info) + return -ENOMEM; + + info->num_ports = type->num_ports; + + bus_addr = ecard_resource_start(ec, type->type); + info->vaddr = ecardm_iomap(ec, type->type, 0, 0); + if (!info->vaddr) { + kfree(info); + return -ENOMEM; + } + + ecard_set_drvdata(ec, info); + + memset(&uart, 0, sizeof(struct uart_8250_port)); + uart.port.irq = ec->irq; + uart.port.flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; + uart.port.uartclk = type->uartclk; + uart.port.iotype = UPIO_MEM; + uart.port.regshift = 2; + uart.port.dev = &ec->dev; + + for (i = 0; i < info->num_ports; i++) { + uart.port.membase = info->vaddr + type->offset[i]; + uart.port.mapbase = bus_addr + type->offset[i]; + + info->ports[i] = serial8250_register_8250_port(&uart); + } + + return 0; +} + +static void serial_card_remove(struct expansion_card *ec) +{ + struct serial_card_info *info = ecard_get_drvdata(ec); + int i; + + ecard_set_drvdata(ec, NULL); + + for (i = 0; i < info->num_ports; i++) + if (info->ports[i] > 0) + serial8250_unregister_port(info->ports[i]); + + kfree(info); +} + +static struct serial_card_type atomwide_type = { + .num_ports = 3, + .uartclk = 7372800, + .type = ECARD_RES_IOCSLOW, + .offset = { 0x2800, 0x2400, 0x2000 }, +}; + +static struct serial_card_type serport_type = { + .num_ports = 2, + .uartclk = 3686400, + .type = ECARD_RES_IOCSLOW, + .offset = { 0x2000, 0x2020 }, +}; + +static const struct ecard_id serial_cids[] = { + { MANU_ATOMWIDE, PROD_ATOMWIDE_3PSERIAL, &atomwide_type }, + { MANU_SERPORT, PROD_SERPORT_DSPORT, &serport_type }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver serial_card_driver = { + .probe = serial_card_probe, + .remove = serial_card_remove, + .id_table = serial_cids, + .drv = { + .name = "8250_acorn", + }, +}; + +static int __init serial_card_init(void) +{ + return ecard_register_driver(&serial_card_driver); +} + +static void __exit serial_card_exit(void) +{ + ecard_remove_driver(&serial_card_driver); +} + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Acorn 8250-compatible serial port expansion card driver"); +MODULE_LICENSE("GPL"); + +module_init(serial_card_init); +module_exit(serial_card_exit); diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c new file mode 100644 index 000000000..26fc0464f --- /dev/null +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -0,0 +1,580 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Serial Port driver for Aspeed VUART device + * + * Copyright (C) 2016 Jeremy Kerr <jk@ozlabs.org>, IBM Corp. + * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp. + */ +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/clk.h> + +#include "8250.h" + +#define ASPEED_VUART_GCRA 0x20 +#define ASPEED_VUART_GCRA_VUART_EN BIT(0) +#define ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY BIT(1) +#define ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5) +#define ASPEED_VUART_GCRB 0x24 +#define ASPEED_VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4) +#define ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT 4 +#define ASPEED_VUART_ADDRL 0x28 +#define ASPEED_VUART_ADDRH 0x2c + +#define ASPEED_VUART_DEFAULT_LPC_ADDR 0x3f8 +#define ASPEED_VUART_DEFAULT_SIRQ 4 +#define ASPEED_VUART_DEFAULT_SIRQ_POLARITY IRQ_TYPE_LEVEL_LOW + +struct aspeed_vuart { + struct device *dev; + int line; + struct timer_list unthrottle_timer; + struct uart_8250_port *port; +}; + +/* + * If we fill the tty flip buffers, we throttle the data ready interrupt + * to prevent dropped characters. This timeout defines how long we wait + * to (conditionally, depending on buffer state) unthrottle. + */ +static const int unthrottle_timeout = HZ/10; + +/* + * The VUART is basically two UART 'front ends' connected by their FIFO + * (no actual serial line in between). One is on the BMC side (management + * controller) and one is on the host CPU side. + * + * It allows the BMC to provide to the host a "UART" that pipes into + * the BMC itself and can then be turned by the BMC into a network console + * of some sort for example. + * + * This driver is for the BMC side. The sysfs files allow the BMC + * userspace which owns the system configuration policy, to specify + * at what IO port and interrupt number the host side will appear + * to the host on the Host <-> BMC LPC bus. It could be different on a + * different system (though most of them use 3f8/4). + */ + +static inline u8 aspeed_vuart_readb(struct aspeed_vuart *vuart, u8 reg) +{ + return readb(vuart->port->port.membase + reg); +} + +static inline void aspeed_vuart_writeb(struct aspeed_vuart *vuart, u8 val, u8 reg) +{ + writeb(val, vuart->port->port.membase + reg); +} + +static ssize_t lpc_address_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aspeed_vuart *vuart = dev_get_drvdata(dev); + u16 addr; + + addr = (aspeed_vuart_readb(vuart, ASPEED_VUART_ADDRH) << 8) | + (aspeed_vuart_readb(vuart, ASPEED_VUART_ADDRL)); + + return sysfs_emit(buf, "0x%x\n", addr); +} + +static int aspeed_vuart_set_lpc_address(struct aspeed_vuart *vuart, u32 addr) +{ + if (addr > U16_MAX) + return -EINVAL; + + aspeed_vuart_writeb(vuart, addr >> 8, ASPEED_VUART_ADDRH); + aspeed_vuart_writeb(vuart, addr >> 0, ASPEED_VUART_ADDRL); + + return 0; +} + +static ssize_t lpc_address_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aspeed_vuart *vuart = dev_get_drvdata(dev); + u32 val; + int err; + + err = kstrtou32(buf, 0, &val); + if (err) + return err; + + err = aspeed_vuart_set_lpc_address(vuart, val); + return err ? : count; +} + +static DEVICE_ATTR_RW(lpc_address); + +static ssize_t sirq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aspeed_vuart *vuart = dev_get_drvdata(dev); + u8 reg; + + reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRB); + reg &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; + reg >>= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; + + return sysfs_emit(buf, "%u\n", reg); +} + +static int aspeed_vuart_set_sirq(struct aspeed_vuart *vuart, u32 sirq) +{ + u8 reg; + + if (sirq > (ASPEED_VUART_GCRB_HOST_SIRQ_MASK >> ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT)) + return -EINVAL; + + sirq <<= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; + sirq &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; + + reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRB); + reg &= ~ASPEED_VUART_GCRB_HOST_SIRQ_MASK; + reg |= sirq; + aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRB); + + return 0; +} + +static ssize_t sirq_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aspeed_vuart *vuart = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 0, &val); + if (err) + return err; + + err = aspeed_vuart_set_sirq(vuart, val); + return err ? : count; +} + +static DEVICE_ATTR_RW(sirq); + +static ssize_t sirq_polarity_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aspeed_vuart *vuart = dev_get_drvdata(dev); + u8 reg; + + reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA); + reg &= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY; + + return sysfs_emit(buf, "%u\n", reg ? 1 : 0); +} + +static void aspeed_vuart_set_sirq_polarity(struct aspeed_vuart *vuart, + bool polarity) +{ + u8 reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA); + + if (polarity) + reg |= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY; + else + reg &= ~ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY; + + aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRA); +} + +static ssize_t sirq_polarity_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aspeed_vuart *vuart = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 0, &val); + if (err) + return err; + + aspeed_vuart_set_sirq_polarity(vuart, val != 0); + + return count; +} + +static DEVICE_ATTR_RW(sirq_polarity); + +static struct attribute *aspeed_vuart_attrs[] = { + &dev_attr_sirq.attr, + &dev_attr_sirq_polarity.attr, + &dev_attr_lpc_address.attr, + NULL, +}; + +static const struct attribute_group aspeed_vuart_attr_group = { + .attrs = aspeed_vuart_attrs, +}; + +static void aspeed_vuart_set_enabled(struct aspeed_vuart *vuart, bool enabled) +{ + u8 reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA); + + if (enabled) + reg |= ASPEED_VUART_GCRA_VUART_EN; + else + reg &= ~ASPEED_VUART_GCRA_VUART_EN; + + aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRA); +} + +static void aspeed_vuart_set_host_tx_discard(struct aspeed_vuart *vuart, + bool discard) +{ + u8 reg; + + reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA); + + /* If the DISABLE_HOST_TX_DISCARD bit is set, discard is disabled */ + if (!discard) + reg |= ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD; + else + reg &= ~ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD; + + aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRA); +} + +static int aspeed_vuart_startup(struct uart_port *uart_port) +{ + struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port); + struct aspeed_vuart *vuart = uart_8250_port->port.private_data; + int rc; + + rc = serial8250_do_startup(uart_port); + if (rc) + return rc; + + aspeed_vuart_set_host_tx_discard(vuart, false); + + return 0; +} + +static void aspeed_vuart_shutdown(struct uart_port *uart_port) +{ + struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port); + struct aspeed_vuart *vuart = uart_8250_port->port.private_data; + + aspeed_vuart_set_host_tx_discard(vuart, true); + + serial8250_do_shutdown(uart_port); +} + +static void __aspeed_vuart_set_throttle(struct uart_8250_port *up, + bool throttle) +{ + unsigned char irqs = UART_IER_RLSI | UART_IER_RDI; + + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&up->port.lock); + + up->ier &= ~irqs; + if (!throttle) + up->ier |= irqs; + serial_out(up, UART_IER, up->ier); +} +static void aspeed_vuart_set_throttle(struct uart_port *port, bool throttle) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned long flags; + + uart_port_lock_irqsave(port, &flags); + __aspeed_vuart_set_throttle(up, throttle); + uart_port_unlock_irqrestore(port, flags); +} + +static void aspeed_vuart_throttle(struct uart_port *port) +{ + aspeed_vuart_set_throttle(port, true); +} + +static void aspeed_vuart_unthrottle(struct uart_port *port) +{ + aspeed_vuart_set_throttle(port, false); +} + +static void aspeed_vuart_unthrottle_exp(struct timer_list *timer) +{ + struct aspeed_vuart *vuart = timer_container_of(vuart, timer, + unthrottle_timer); + struct uart_8250_port *up = vuart->port; + + if (!tty_buffer_space_avail(&up->port.state->port)) { + mod_timer(&vuart->unthrottle_timer, + jiffies + unthrottle_timeout); + return; + } + + aspeed_vuart_unthrottle(&up->port); +} + +/* + * Custom interrupt handler to manage finer-grained flow control. Although we + * have throttle/unthrottle callbacks, we've seen that the VUART device can + * deliver characters faster than the ldisc has a chance to check buffer space + * against the throttle threshold. This results in dropped characters before + * the throttle. + * + * We do this by checking for flip buffer space before RX. If we have no space, + * throttle now and schedule an unthrottle for later, once the ldisc has had + * a chance to drain the buffers. + */ +static int aspeed_vuart_handle_irq(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int iir, lsr; + unsigned long flags; + unsigned int space, count; + + iir = serial_port_in(port, UART_IIR); + + if (iir & UART_IIR_NO_INT) + return 0; + + uart_port_lock_irqsave(port, &flags); + + lsr = serial_port_in(port, UART_LSR); + + if (lsr & (UART_LSR_DR | UART_LSR_BI)) { + space = tty_buffer_space_avail(&port->state->port); + + if (!space) { + /* throttle and schedule an unthrottle later */ + struct aspeed_vuart *vuart = port->private_data; + __aspeed_vuart_set_throttle(up, true); + + if (!timer_pending(&vuart->unthrottle_timer)) + mod_timer(&vuart->unthrottle_timer, + jiffies + unthrottle_timeout); + + } else { + count = min(space, 256U); + + do { + serial8250_read_char(up, lsr); + lsr = serial_in(up, UART_LSR); + if (--count == 0) + break; + } while (lsr & (UART_LSR_DR | UART_LSR_BI)); + + tty_flip_buffer_push(&port->state->port); + } + } + + serial8250_modem_status(up); + if (lsr & UART_LSR_THRE) + serial8250_tx_chars(up); + + uart_unlock_and_check_sysrq_irqrestore(port, flags); + + return 1; +} + +static void aspeed_vuart_auto_configure_sirq_polarity( + struct aspeed_vuart *vuart, struct device_node *syscon_np, + u32 reg_offset, u32 reg_mask) +{ + struct regmap *regmap; + u32 value; + + regmap = syscon_node_to_regmap(syscon_np); + if (IS_ERR(regmap)) { + dev_warn(vuart->dev, + "could not get regmap for aspeed,sirq-polarity-sense\n"); + return; + } + if (regmap_read(regmap, reg_offset, &value)) { + dev_warn(vuart->dev, "could not read hw strap table\n"); + return; + } + + aspeed_vuart_set_sirq_polarity(vuart, (value & reg_mask) == 0); +} + +static int aspeed_vuart_map_irq_polarity(u32 dt) +{ + switch (dt) { + case IRQ_TYPE_LEVEL_LOW: + return 0; + case IRQ_TYPE_LEVEL_HIGH: + return 1; + default: + return -EINVAL; + } +} + +static int aspeed_vuart_probe(struct platform_device *pdev) +{ + struct of_phandle_args sirq_polarity_sense_args; + struct device *dev = &pdev->dev; + struct uart_8250_port port; + struct aspeed_vuart *vuart; + struct device_node *np; + struct resource *res; + int rc, sirq_polarity; + u32 prop, sirq[2]; + struct clk *vclk; + + np = pdev->dev.of_node; + + vuart = devm_kzalloc(&pdev->dev, sizeof(*vuart), GFP_KERNEL); + if (!vuart) + return -ENOMEM; + + vuart->dev = &pdev->dev; + timer_setup(&vuart->unthrottle_timer, aspeed_vuart_unthrottle_exp, 0); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + memset(&port, 0, sizeof(port)); + port.port.private_data = vuart; + port.port.mapbase = res->start; + port.port.mapsize = resource_size(res); + port.port.startup = aspeed_vuart_startup; + port.port.shutdown = aspeed_vuart_shutdown; + port.port.throttle = aspeed_vuart_throttle; + port.port.unthrottle = aspeed_vuart_unthrottle; + port.port.status = UPSTAT_SYNC_FIFO; + port.port.dev = &pdev->dev; + port.port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE); + port.port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_FIXED_PORT | UPF_FIXED_TYPE | + UPF_NO_THRE_TEST; + port.bugs |= UART_BUG_TXRACE; + + rc = sysfs_create_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); + if (rc < 0) + return rc; + + rc = uart_read_port_properties(&port.port); + if (rc) + goto err_sysfs_remove; + + /* Get clk rate through clk driver if present */ + if (!port.port.uartclk) { + vclk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(vclk)) { + rc = dev_err_probe(dev, PTR_ERR(vclk), "clk or clock-frequency not defined\n"); + goto err_sysfs_remove; + } + + port.port.uartclk = clk_get_rate(vclk); + } + + /* If current-speed was set, then try not to change it. */ + if (of_property_read_u32(np, "current-speed", &prop) == 0) + port.port.custom_divisor = port.port.uartclk / (16 * prop); + + port.port.handle_irq = aspeed_vuart_handle_irq; + port.port.type = PORT_ASPEED_VUART; + + if (port.port.fifosize) + port.capabilities = UART_CAP_FIFO; + + if (of_property_read_bool(np, "auto-flow-control")) + port.capabilities |= UART_CAP_AFE; + + rc = serial8250_register_8250_port(&port); + if (rc < 0) + goto err_sysfs_remove; + + vuart->line = rc; + vuart->port = serial8250_get_port(vuart->line); + + rc = of_parse_phandle_with_fixed_args( + np, "aspeed,sirq-polarity-sense", 2, 0, + &sirq_polarity_sense_args); + if (rc < 0) { + dev_dbg(&pdev->dev, + "aspeed,sirq-polarity-sense property not found\n"); + } else { + aspeed_vuart_auto_configure_sirq_polarity( + vuart, sirq_polarity_sense_args.np, + sirq_polarity_sense_args.args[0], + BIT(sirq_polarity_sense_args.args[1])); + of_node_put(sirq_polarity_sense_args.np); + } + + rc = of_property_read_u32(np, "aspeed,lpc-io-reg", &prop); + if (rc < 0) + prop = ASPEED_VUART_DEFAULT_LPC_ADDR; + + rc = aspeed_vuart_set_lpc_address(vuart, prop); + if (rc < 0) { + dev_err_probe(dev, rc, "invalid value in aspeed,lpc-io-reg property\n"); + goto err_sysfs_remove; + } + + rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts", sirq, 2); + if (rc < 0) { + sirq[0] = ASPEED_VUART_DEFAULT_SIRQ; + sirq[1] = ASPEED_VUART_DEFAULT_SIRQ_POLARITY; + } + + rc = aspeed_vuart_set_sirq(vuart, sirq[0]); + if (rc < 0) { + dev_err_probe(dev, rc, "invalid sirq number in aspeed,lpc-interrupts property\n"); + goto err_sysfs_remove; + } + + sirq_polarity = aspeed_vuart_map_irq_polarity(sirq[1]); + if (sirq_polarity < 0) { + rc = dev_err_probe(dev, sirq_polarity, + "invalid sirq polarity in aspeed,lpc-interrupts property\n"); + goto err_sysfs_remove; + } + + aspeed_vuart_set_sirq_polarity(vuart, sirq_polarity); + + aspeed_vuart_set_enabled(vuart, true); + aspeed_vuart_set_host_tx_discard(vuart, true); + platform_set_drvdata(pdev, vuart); + + return 0; + +err_sysfs_remove: + sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); + return rc; +} + +static void aspeed_vuart_remove(struct platform_device *pdev) +{ + struct aspeed_vuart *vuart = platform_get_drvdata(pdev); + + timer_delete_sync(&vuart->unthrottle_timer); + aspeed_vuart_set_enabled(vuart, false); + serial8250_unregister_port(vuart->line); + sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); +} + +static const struct of_device_id aspeed_vuart_table[] = { + { .compatible = "aspeed,ast2400-vuart" }, + { .compatible = "aspeed,ast2500-vuart" }, + { }, +}; +MODULE_DEVICE_TABLE(of, aspeed_vuart_table); + +static struct platform_driver aspeed_vuart_driver = { + .driver = { + .name = "aspeed-vuart", + .of_match_table = aspeed_vuart_table, + }, + .probe = aspeed_vuart_probe, + .remove = aspeed_vuart_remove, +}; + +module_platform_driver(aspeed_vuart_driver); + +MODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Driver for Aspeed VUART device"); diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c new file mode 100644 index 000000000..0609582a6 --- /dev/null +++ b/drivers/tty/serial/8250/8250_bcm2835aux.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Serial port driver for BCM2835AUX UART + * + * Copyright (C) 2016 Martin Sperl <kernel@martin.sperl.org> + * + * Based on 8250_lpc18xx.c: + * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> + * + * The bcm2835aux is capable of RTS auto flow-control, but this driver doesn't + * take advantage of it yet. When adding support, be sure not to enable it + * simultaneously to rs485. + */ + +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/property.h> + +#include "8250.h" + +#define BCM2835_AUX_UART_CNTL 8 +#define BCM2835_AUX_UART_CNTL_RXEN 0x01 /* Receiver enable */ +#define BCM2835_AUX_UART_CNTL_TXEN 0x02 /* Transmitter enable */ +#define BCM2835_AUX_UART_CNTL_AUTORTS 0x04 /* RTS set by RX fill level */ +#define BCM2835_AUX_UART_CNTL_AUTOCTS 0x08 /* CTS stops transmitter */ +#define BCM2835_AUX_UART_CNTL_RTS3 0x00 /* RTS set until 3 chars left */ +#define BCM2835_AUX_UART_CNTL_RTS2 0x10 /* RTS set until 2 chars left */ +#define BCM2835_AUX_UART_CNTL_RTS1 0x20 /* RTS set until 1 chars left */ +#define BCM2835_AUX_UART_CNTL_RTS4 0x30 /* RTS set until 4 chars left */ +#define BCM2835_AUX_UART_CNTL_RTSINV 0x40 /* Invert auto RTS polarity */ +#define BCM2835_AUX_UART_CNTL_CTSINV 0x80 /* Invert auto CTS polarity */ + +/** + * struct bcm2835aux_data - driver private data of BCM2835 auxiliary UART + * @clk: clock producer of the port's uartclk + * @line: index of the port's serial8250_ports[] entry + * @cntl: cached copy of CNTL register + */ +struct bcm2835aux_data { + struct clk *clk; + int line; + u32 cntl; +}; + +static void bcm2835aux_rs485_start_tx(struct uart_8250_port *up, bool toggle_ier) +{ + if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) { + struct bcm2835aux_data *data = dev_get_drvdata(up->port.dev); + + data->cntl &= ~BCM2835_AUX_UART_CNTL_RXEN; + serial_out(up, BCM2835_AUX_UART_CNTL, data->cntl); + } + + /* + * On the bcm2835aux, the MCR register contains no other + * flags besides RTS. So no need for a read-modify-write. + */ + if (up->port.rs485.flags & SER_RS485_RTS_ON_SEND) + serial8250_out_MCR(up, 0); + else + serial8250_out_MCR(up, UART_MCR_RTS); +} + +static void bcm2835aux_rs485_stop_tx(struct uart_8250_port *up, bool toggle_ier) +{ + if (up->port.rs485.flags & SER_RS485_RTS_AFTER_SEND) + serial8250_out_MCR(up, 0); + else + serial8250_out_MCR(up, UART_MCR_RTS); + + if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) { + struct bcm2835aux_data *data = dev_get_drvdata(up->port.dev); + + data->cntl |= BCM2835_AUX_UART_CNTL_RXEN; + serial_out(up, BCM2835_AUX_UART_CNTL, data->cntl); + } +} + +static int bcm2835aux_serial_probe(struct platform_device *pdev) +{ + const struct software_node *bcm2835_swnode; + struct uart_8250_port up = { }; + struct bcm2835aux_data *data; + struct resource *res; + unsigned int uartclk; + int ret; + + /* allocate the custom structure */ + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* initialize data */ + up.capabilities = UART_CAP_FIFO | UART_CAP_MINI; + up.port.dev = &pdev->dev; + up.port.type = PORT_16550; + up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SKIP_TEST | UPF_IOREMAP; + up.port.rs485_config = serial8250_em485_config; + up.port.rs485_supported = serial8250_em485_supported; + up.rs485_start_tx = bcm2835aux_rs485_start_tx; + up.rs485_stop_tx = bcm2835aux_rs485_stop_tx; + + /* initialize cached copy with power-on reset value */ + data->cntl = BCM2835_AUX_UART_CNTL_RXEN | BCM2835_AUX_UART_CNTL_TXEN; + + platform_set_drvdata(pdev, data); + + /* get the clock - this also enables the HW */ + data->clk = devm_clk_get_optional(&pdev->dev, NULL); + if (IS_ERR(data->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->clk), "could not get clk\n"); + + /* map the main registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "memory resource not found"); + return -EINVAL; + } + + up.port.mapbase = res->start; + up.port.mapsize = resource_size(res); + + bcm2835_swnode = device_get_match_data(&pdev->dev); + if (bcm2835_swnode) { + ret = device_add_software_node(&pdev->dev, bcm2835_swnode); + if (ret) + return ret; + } + + ret = uart_read_port_properties(&up.port); + if (ret) + goto rm_swnode; + + up.port.regshift = 2; + up.port.fifosize = 8; + + /* enable the clock as a last step */ + ret = clk_prepare_enable(data->clk); + if (ret) { + dev_err_probe(&pdev->dev, ret, "unable to enable uart clock\n"); + goto rm_swnode; + } + + uartclk = clk_get_rate(data->clk); + if (uartclk) + up.port.uartclk = uartclk; + + /* the HW-clock divider for bcm2835aux is 8, + * but 8250 expects a divider of 16, + * so we have to multiply the actual clock by 2 + * to get identical baudrates. + */ + up.port.uartclk *= 2; + + /* register the port */ + ret = serial8250_register_8250_port(&up); + if (ret < 0) { + dev_err_probe(&pdev->dev, ret, "unable to register 8250 port\n"); + goto dis_clk; + } + data->line = ret; + + return 0; + +dis_clk: + clk_disable_unprepare(data->clk); +rm_swnode: + device_remove_software_node(&pdev->dev); + return ret; +} + +static void bcm2835aux_serial_remove(struct platform_device *pdev) +{ + struct bcm2835aux_data *data = platform_get_drvdata(pdev); + + serial8250_unregister_port(data->line); + clk_disable_unprepare(data->clk); + device_remove_software_node(&pdev->dev); +} + +/* + * Some UEFI implementations (e.g. tianocore/edk2 for the Raspberry Pi) + * describe the miniuart with a base address that encompasses the auxiliary + * registers shared between the miniuart and spi. + * + * This is due to historical reasons, see discussion here: + * https://edk2.groups.io/g/devel/topic/87501357#84349 + * + * We need to add the offset between the miniuart and auxiliary registers + * to get the real miniuart base address. + */ +static const struct property_entry bcm2835_acpi_properties[] = { + PROPERTY_ENTRY_U32("reg-offset", 0x40), + { } +}; + +static const struct software_node bcm2835_acpi_node = { + .properties = bcm2835_acpi_properties, +}; + +static const struct of_device_id bcm2835aux_serial_match[] = { + { .compatible = "brcm,bcm2835-aux-uart" }, + { }, +}; +MODULE_DEVICE_TABLE(of, bcm2835aux_serial_match); + +static const struct acpi_device_id bcm2835aux_serial_acpi_match[] = { + { "BCM2836", (kernel_ulong_t)&bcm2835_acpi_node }, + { } +}; +MODULE_DEVICE_TABLE(acpi, bcm2835aux_serial_acpi_match); + +static bool bcm2835aux_can_disable_clock(struct device *dev) +{ + struct bcm2835aux_data *data = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(data->line); + + if (device_may_wakeup(dev)) + return false; + + if (uart_console(&up->port) && !console_suspend_enabled) + return false; + + return true; +} + +static int bcm2835aux_suspend(struct device *dev) +{ + struct bcm2835aux_data *data = dev_get_drvdata(dev); + + serial8250_suspend_port(data->line); + + if (!bcm2835aux_can_disable_clock(dev)) + return 0; + + clk_disable_unprepare(data->clk); + return 0; +} + +static int bcm2835aux_resume(struct device *dev) +{ + struct bcm2835aux_data *data = dev_get_drvdata(dev); + int ret; + + if (bcm2835aux_can_disable_clock(dev)) { + ret = clk_prepare_enable(data->clk); + if (ret) + return ret; + } + + serial8250_resume_port(data->line); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(bcm2835aux_dev_pm_ops, bcm2835aux_suspend, bcm2835aux_resume); + +static struct platform_driver bcm2835aux_serial_driver = { + .driver = { + .name = "bcm2835-aux-uart", + .of_match_table = bcm2835aux_serial_match, + .acpi_match_table = bcm2835aux_serial_acpi_match, + .pm = pm_ptr(&bcm2835aux_dev_pm_ops), + }, + .probe = bcm2835aux_serial_probe, + .remove = bcm2835aux_serial_remove, +}; +module_platform_driver(bcm2835aux_serial_driver); + +#ifdef CONFIG_SERIAL_8250_CONSOLE + +static int __init early_bcm2835aux_setup(struct earlycon_device *device, + const char *options) +{ + if (!device->port.membase) + return -ENODEV; + + device->port.iotype = UPIO_MEM32; + device->port.regshift = 2; + + return early_serial8250_setup(device, NULL); +} + +OF_EARLYCON_DECLARE(bcm2835aux, "brcm,bcm2835-aux-uart", + early_bcm2835aux_setup); +#endif + +MODULE_DESCRIPTION("BCM2835 auxiliar UART driver"); +MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c new file mode 100644 index 000000000..cb7d594d2 --- /dev/null +++ b/drivers/tty/serial/8250/8250_bcm7271.c @@ -0,0 +1,1232 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2020, Broadcom */ +/* + * 8250-core based driver for Broadcom ns16550a UARTs + * + * This driver uses the standard 8250 driver core but adds additional + * optional features including the ability to use a baud rate clock + * mux for more accurate high speed baud rate selection and also + * an optional DMA engine. + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/tty.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/dma-mapping.h> +#include <linux/tty_flip.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/debugfs.h> +#include <linux/units.h> + +#include "8250.h" + +/* Register definitions for UART DMA block. Version 1.1 or later. */ +#define UDMA_ARB_RX 0x00 +#define UDMA_ARB_TX 0x04 +#define UDMA_ARB_REQ 0x00000001 +#define UDMA_ARB_GRANT 0x00000002 + +#define UDMA_RX_REVISION 0x00 +#define UDMA_RX_REVISION_REQUIRED 0x00000101 +#define UDMA_RX_CTRL 0x04 +#define UDMA_RX_CTRL_BUF_CLOSE_MODE 0x00010000 +#define UDMA_RX_CTRL_MASK_WR_DONE 0x00008000 +#define UDMA_RX_CTRL_ENDIAN_OVERRIDE 0x00004000 +#define UDMA_RX_CTRL_ENDIAN 0x00002000 +#define UDMA_RX_CTRL_OE_IS_ERR 0x00001000 +#define UDMA_RX_CTRL_PE_IS_ERR 0x00000800 +#define UDMA_RX_CTRL_FE_IS_ERR 0x00000400 +#define UDMA_RX_CTRL_NUM_BUF_USED_MASK 0x000003c0 +#define UDMA_RX_CTRL_NUM_BUF_USED_SHIFT 6 +#define UDMA_RX_CTRL_BUF_CLOSE_CLK_SEL_SYS 0x00000020 +#define UDMA_RX_CTRL_BUF_CLOSE_ENA 0x00000010 +#define UDMA_RX_CTRL_TIMEOUT_CLK_SEL_SYS 0x00000008 +#define UDMA_RX_CTRL_TIMEOUT_ENA 0x00000004 +#define UDMA_RX_CTRL_ABORT 0x00000002 +#define UDMA_RX_CTRL_ENA 0x00000001 +#define UDMA_RX_STATUS 0x08 +#define UDMA_RX_STATUS_ACTIVE_BUF_MASK 0x0000000f +#define UDMA_RX_TRANSFER_LEN 0x0c +#define UDMA_RX_TRANSFER_TOTAL 0x10 +#define UDMA_RX_BUFFER_SIZE 0x14 +#define UDMA_RX_SRC_ADDR 0x18 +#define UDMA_RX_TIMEOUT 0x1c +#define UDMA_RX_BUFFER_CLOSE 0x20 +#define UDMA_RX_BLOCKOUT_COUNTER 0x24 +#define UDMA_RX_BUF0_PTR_LO 0x28 +#define UDMA_RX_BUF0_PTR_HI 0x2c +#define UDMA_RX_BUF0_STATUS 0x30 +#define UDMA_RX_BUFX_STATUS_OVERRUN_ERR 0x00000010 +#define UDMA_RX_BUFX_STATUS_FRAME_ERR 0x00000008 +#define UDMA_RX_BUFX_STATUS_PARITY_ERR 0x00000004 +#define UDMA_RX_BUFX_STATUS_CLOSE_EXPIRED 0x00000002 +#define UDMA_RX_BUFX_STATUS_DATA_RDY 0x00000001 +#define UDMA_RX_BUF0_DATA_LEN 0x34 +#define UDMA_RX_BUF1_PTR_LO 0x38 +#define UDMA_RX_BUF1_PTR_HI 0x3c +#define UDMA_RX_BUF1_STATUS 0x40 +#define UDMA_RX_BUF1_DATA_LEN 0x44 + +#define UDMA_TX_REVISION 0x00 +#define UDMA_TX_REVISION_REQUIRED 0x00000101 +#define UDMA_TX_CTRL 0x04 +#define UDMA_TX_CTRL_ENDIAN_OVERRIDE 0x00000080 +#define UDMA_TX_CTRL_ENDIAN 0x00000040 +#define UDMA_TX_CTRL_NUM_BUF_USED_MASK 0x00000030 +#define UDMA_TX_CTRL_NUM_BUF_USED_1 0x00000010 +#define UDMA_TX_CTRL_ABORT 0x00000002 +#define UDMA_TX_CTRL_ENA 0x00000001 +#define UDMA_TX_DST_ADDR 0x08 +#define UDMA_TX_BLOCKOUT_COUNTER 0x10 +#define UDMA_TX_TRANSFER_LEN 0x14 +#define UDMA_TX_TRANSFER_TOTAL 0x18 +#define UDMA_TX_STATUS 0x20 +#define UDMA_TX_BUF0_PTR_LO 0x24 +#define UDMA_TX_BUF0_PTR_HI 0x28 +#define UDMA_TX_BUF0_STATUS 0x2c +#define UDMA_TX_BUFX_LAST 0x00000002 +#define UDMA_TX_BUFX_EMPTY 0x00000001 +#define UDMA_TX_BUF0_DATA_LEN 0x30 +#define UDMA_TX_BUF0_DATA_SENT 0x34 +#define UDMA_TX_BUF1_PTR_LO 0x38 + +#define UDMA_INTR_STATUS 0x00 +#define UDMA_INTR_ARB_TX_GRANT 0x00040000 +#define UDMA_INTR_ARB_RX_GRANT 0x00020000 +#define UDMA_INTR_TX_ALL_EMPTY 0x00010000 +#define UDMA_INTR_TX_EMPTY_BUF1 0x00008000 +#define UDMA_INTR_TX_EMPTY_BUF0 0x00004000 +#define UDMA_INTR_TX_ABORT 0x00002000 +#define UDMA_INTR_TX_DONE 0x00001000 +#define UDMA_INTR_RX_ERROR 0x00000800 +#define UDMA_INTR_RX_TIMEOUT 0x00000400 +#define UDMA_INTR_RX_READY_BUF7 0x00000200 +#define UDMA_INTR_RX_READY_BUF6 0x00000100 +#define UDMA_INTR_RX_READY_BUF5 0x00000080 +#define UDMA_INTR_RX_READY_BUF4 0x00000040 +#define UDMA_INTR_RX_READY_BUF3 0x00000020 +#define UDMA_INTR_RX_READY_BUF2 0x00000010 +#define UDMA_INTR_RX_READY_BUF1 0x00000008 +#define UDMA_INTR_RX_READY_BUF0 0x00000004 +#define UDMA_INTR_RX_READY_MASK 0x000003fc +#define UDMA_INTR_RX_READY_SHIFT 2 +#define UDMA_INTR_RX_ABORT 0x00000002 +#define UDMA_INTR_RX_DONE 0x00000001 +#define UDMA_INTR_SET 0x04 +#define UDMA_INTR_CLEAR 0x08 +#define UDMA_INTR_MASK_STATUS 0x0c +#define UDMA_INTR_MASK_SET 0x10 +#define UDMA_INTR_MASK_CLEAR 0x14 + + +#define UDMA_RX_INTERRUPTS ( \ + UDMA_INTR_RX_ERROR | \ + UDMA_INTR_RX_TIMEOUT | \ + UDMA_INTR_RX_READY_BUF0 | \ + UDMA_INTR_RX_READY_BUF1 | \ + UDMA_INTR_RX_READY_BUF2 | \ + UDMA_INTR_RX_READY_BUF3 | \ + UDMA_INTR_RX_READY_BUF4 | \ + UDMA_INTR_RX_READY_BUF5 | \ + UDMA_INTR_RX_READY_BUF6 | \ + UDMA_INTR_RX_READY_BUF7 | \ + UDMA_INTR_RX_ABORT | \ + UDMA_INTR_RX_DONE) + +#define UDMA_RX_ERR_INTERRUPTS ( \ + UDMA_INTR_RX_ERROR | \ + UDMA_INTR_RX_TIMEOUT | \ + UDMA_INTR_RX_ABORT | \ + UDMA_INTR_RX_DONE) + +#define UDMA_TX_INTERRUPTS ( \ + UDMA_INTR_TX_ABORT | \ + UDMA_INTR_TX_DONE) + +#define UDMA_IS_RX_INTERRUPT(status) ((status) & UDMA_RX_INTERRUPTS) +#define UDMA_IS_TX_INTERRUPT(status) ((status) & UDMA_TX_INTERRUPTS) + + +/* Current devices have 8 sets of RX buffer registers */ +#define UDMA_RX_BUFS_COUNT 8 +#define UDMA_RX_BUFS_REG_OFFSET (UDMA_RX_BUF1_PTR_LO - UDMA_RX_BUF0_PTR_LO) +#define UDMA_RX_BUFx_PTR_LO(x) (UDMA_RX_BUF0_PTR_LO + \ + ((x) * UDMA_RX_BUFS_REG_OFFSET)) +#define UDMA_RX_BUFx_PTR_HI(x) (UDMA_RX_BUF0_PTR_HI + \ + ((x) * UDMA_RX_BUFS_REG_OFFSET)) +#define UDMA_RX_BUFx_STATUS(x) (UDMA_RX_BUF0_STATUS + \ + ((x) * UDMA_RX_BUFS_REG_OFFSET)) +#define UDMA_RX_BUFx_DATA_LEN(x) (UDMA_RX_BUF0_DATA_LEN + \ + ((x) * UDMA_RX_BUFS_REG_OFFSET)) + +/* Current devices have 2 sets of TX buffer registers */ +#define UDMA_TX_BUFS_COUNT 2 +#define UDMA_TX_BUFS_REG_OFFSET (UDMA_TX_BUF1_PTR_LO - UDMA_TX_BUF0_PTR_LO) +#define UDMA_TX_BUFx_PTR_LO(x) (UDMA_TX_BUF0_PTR_LO + \ + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_PTR_HI(x) (UDMA_TX_BUF0_PTR_HI + \ + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_STATUS(x) (UDMA_TX_BUF0_STATUS + \ + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_DATA_LEN(x) (UDMA_TX_BUF0_DATA_LEN + \ + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define UDMA_TX_BUFx_DATA_SENT(x) (UDMA_TX_BUF0_DATA_SENT + \ + ((x) * UDMA_TX_BUFS_REG_OFFSET)) +#define REGS_8250 0 +#define REGS_DMA_RX 1 +#define REGS_DMA_TX 2 +#define REGS_DMA_ISR 3 +#define REGS_DMA_ARB 4 +#define REGS_MAX 5 + +#define TX_BUF_SIZE 4096 +#define RX_BUF_SIZE 4096 +#define RX_BUFS_COUNT 2 + +static const u32 brcmstb_rate_table[] = { + 81 * HZ_PER_MHZ, + 108 * HZ_PER_MHZ, + 64 * HZ_PER_MHZ, /* Actually 64285715 for some chips */ + 48 * HZ_PER_MHZ, +}; + +static const u32 brcmstb_rate_table_7278[] = { + 81 * HZ_PER_MHZ, + 108 * HZ_PER_MHZ, + 0, + 48 * HZ_PER_MHZ, +}; + +struct brcmuart_priv { + int line; + struct clk *baud_mux_clk; + unsigned long default_mux_rate; + u32 real_rates[ARRAY_SIZE(brcmstb_rate_table)]; + const u32 *rate_table; + ktime_t char_wait; + struct uart_port *up; + struct hrtimer hrt; + bool shutdown; + bool dma_enabled; + struct uart_8250_dma dma; + void __iomem *regs[REGS_MAX]; + dma_addr_t rx_addr; + void *rx_bufs; + size_t rx_size; + int rx_next_buf; + dma_addr_t tx_addr; + void *tx_buf; + size_t tx_size; + bool tx_running; + bool rx_running; + struct dentry *debugfs_dir; + + /* stats exposed through debugfs */ + u64 dma_rx_partial_buf; + u64 dma_rx_full_buf; + u32 rx_bad_timeout_late_char; + u32 rx_bad_timeout_no_char; + u32 rx_missing_close_timeout; + u32 rx_err; + u32 rx_timeout; + u32 rx_abort; + u32 saved_mctrl; +}; + +static struct dentry *brcmuart_debugfs_root; + +/* + * Register access routines + */ +static u32 udma_readl(struct brcmuart_priv *priv, + int reg_type, int offset) +{ + return readl(priv->regs[reg_type] + offset); +} + +static void udma_writel(struct brcmuart_priv *priv, + int reg_type, int offset, u32 value) +{ + writel(value, priv->regs[reg_type] + offset); +} + +static void udma_set(struct brcmuart_priv *priv, + int reg_type, int offset, u32 bits) +{ + void __iomem *reg = priv->regs[reg_type] + offset; + u32 value; + + value = readl(reg); + value |= bits; + writel(value, reg); +} + +static void udma_unset(struct brcmuart_priv *priv, + int reg_type, int offset, u32 bits) +{ + void __iomem *reg = priv->regs[reg_type] + offset; + u32 value; + + value = readl(reg); + value &= ~bits; + writel(value, reg); +} + +/* + * The UART DMA engine hardware can be used by multiple UARTS, but + * only one at a time. Sharing is not currently supported so + * the first UART to request the DMA engine will get it and any + * subsequent requests by other UARTS will fail. + */ +static int brcmuart_arbitration(struct brcmuart_priv *priv, bool acquire) +{ + u32 rx_grant; + u32 tx_grant; + int waits; + int ret = 0; + + if (acquire) { + udma_set(priv, REGS_DMA_ARB, UDMA_ARB_RX, UDMA_ARB_REQ); + udma_set(priv, REGS_DMA_ARB, UDMA_ARB_TX, UDMA_ARB_REQ); + + waits = 1; + while (1) { + rx_grant = udma_readl(priv, REGS_DMA_ARB, UDMA_ARB_RX); + tx_grant = udma_readl(priv, REGS_DMA_ARB, UDMA_ARB_TX); + if (rx_grant & tx_grant & UDMA_ARB_GRANT) + return 0; + if (waits-- == 0) + break; + msleep(1); + } + ret = 1; + } + + udma_unset(priv, REGS_DMA_ARB, UDMA_ARB_RX, UDMA_ARB_REQ); + udma_unset(priv, REGS_DMA_ARB, UDMA_ARB_TX, UDMA_ARB_REQ); + return ret; +} + +static void brcmuart_init_dma_hardware(struct brcmuart_priv *priv) +{ + u32 daddr; + u32 value; + int x; + + /* Start with all interrupts disabled */ + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_MASK_SET, 0xffffffff); + + udma_writel(priv, REGS_DMA_RX, UDMA_RX_BUFFER_SIZE, RX_BUF_SIZE); + + /* + * Setup buffer close to happen when 32 character times have + * elapsed since the last character was received. + */ + udma_writel(priv, REGS_DMA_RX, UDMA_RX_BUFFER_CLOSE, 16*10*32); + value = (RX_BUFS_COUNT << UDMA_RX_CTRL_NUM_BUF_USED_SHIFT) + | UDMA_RX_CTRL_BUF_CLOSE_MODE + | UDMA_RX_CTRL_BUF_CLOSE_ENA; + udma_writel(priv, REGS_DMA_RX, UDMA_RX_CTRL, value); + + udma_writel(priv, REGS_DMA_RX, UDMA_RX_BLOCKOUT_COUNTER, 0); + daddr = priv->rx_addr; + for (x = 0; x < RX_BUFS_COUNT; x++) { + + /* Set RX transfer length to 0 for unknown */ + udma_writel(priv, REGS_DMA_RX, UDMA_RX_TRANSFER_LEN, 0); + + udma_writel(priv, REGS_DMA_RX, UDMA_RX_BUFx_PTR_LO(x), + lower_32_bits(daddr)); + udma_writel(priv, REGS_DMA_RX, UDMA_RX_BUFx_PTR_HI(x), + upper_32_bits(daddr)); + daddr += RX_BUF_SIZE; + } + + daddr = priv->tx_addr; + udma_writel(priv, REGS_DMA_TX, UDMA_TX_BUFx_PTR_LO(0), + lower_32_bits(daddr)); + udma_writel(priv, REGS_DMA_TX, UDMA_TX_BUFx_PTR_HI(0), + upper_32_bits(daddr)); + udma_writel(priv, REGS_DMA_TX, UDMA_TX_CTRL, + UDMA_TX_CTRL_NUM_BUF_USED_1); + + /* clear all interrupts then enable them */ + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_CLEAR, 0xffffffff); + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_MASK_CLEAR, + UDMA_RX_INTERRUPTS | UDMA_TX_INTERRUPTS); + +} + +static void start_rx_dma(struct uart_8250_port *p) +{ + struct brcmuart_priv *priv = p->port.private_data; + int x; + + udma_unset(priv, REGS_DMA_RX, UDMA_RX_CTRL, UDMA_RX_CTRL_ENA); + + /* Clear the RX ready bit for all buffers */ + for (x = 0; x < RX_BUFS_COUNT; x++) + udma_unset(priv, REGS_DMA_RX, UDMA_RX_BUFx_STATUS(x), + UDMA_RX_BUFX_STATUS_DATA_RDY); + + /* always start with buffer 0 */ + udma_unset(priv, REGS_DMA_RX, UDMA_RX_STATUS, + UDMA_RX_STATUS_ACTIVE_BUF_MASK); + priv->rx_next_buf = 0; + + udma_set(priv, REGS_DMA_RX, UDMA_RX_CTRL, UDMA_RX_CTRL_ENA); + priv->rx_running = true; +} + +static void stop_rx_dma(struct uart_8250_port *p) +{ + struct brcmuart_priv *priv = p->port.private_data; + + /* If RX is running, set the RX ABORT */ + if (priv->rx_running) + udma_set(priv, REGS_DMA_RX, UDMA_RX_CTRL, UDMA_RX_CTRL_ABORT); +} + +static int stop_tx_dma(struct uart_8250_port *p) +{ + struct brcmuart_priv *priv = p->port.private_data; + u32 value; + + /* If TX is running, set the TX ABORT */ + value = udma_readl(priv, REGS_DMA_TX, UDMA_TX_CTRL); + if (value & UDMA_TX_CTRL_ENA) + udma_set(priv, REGS_DMA_TX, UDMA_TX_CTRL, UDMA_TX_CTRL_ABORT); + priv->tx_running = false; + return 0; +} + +/* + * NOTE: printk's in this routine will hang the system if this is + * the console tty + */ +static int brcmuart_tx_dma(struct uart_8250_port *p) +{ + struct brcmuart_priv *priv = p->port.private_data; + struct tty_port *tport = &p->port.state->port; + u32 tx_size; + + if (uart_tx_stopped(&p->port) || priv->tx_running || + kfifo_is_empty(&tport->xmit_fifo)) { + return 0; + } + + priv->dma.tx_err = 0; + tx_size = uart_fifo_out(&p->port, priv->tx_buf, UART_XMIT_SIZE); + + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) + uart_write_wakeup(&p->port); + + udma_writel(priv, REGS_DMA_TX, UDMA_TX_TRANSFER_LEN, tx_size); + udma_writel(priv, REGS_DMA_TX, UDMA_TX_BUF0_DATA_LEN, tx_size); + udma_unset(priv, REGS_DMA_TX, UDMA_TX_BUF0_STATUS, UDMA_TX_BUFX_EMPTY); + udma_set(priv, REGS_DMA_TX, UDMA_TX_CTRL, UDMA_TX_CTRL_ENA); + priv->tx_running = true; + + return 0; +} + +static void brcmuart_rx_buf_done_isr(struct uart_port *up, int index) +{ + struct brcmuart_priv *priv = up->private_data; + struct tty_port *tty_port = &up->state->port; + u32 status; + u32 length; + u32 copied; + + /* Make sure we're still in sync with the hardware */ + status = udma_readl(priv, REGS_DMA_RX, UDMA_RX_BUFx_STATUS(index)); + length = udma_readl(priv, REGS_DMA_RX, UDMA_RX_BUFx_DATA_LEN(index)); + + if ((status & UDMA_RX_BUFX_STATUS_DATA_RDY) == 0) { + dev_err(up->dev, "RX done interrupt but DATA_RDY not found\n"); + return; + } + if (status & (UDMA_RX_BUFX_STATUS_OVERRUN_ERR | + UDMA_RX_BUFX_STATUS_FRAME_ERR | + UDMA_RX_BUFX_STATUS_PARITY_ERR)) { + if (status & UDMA_RX_BUFX_STATUS_OVERRUN_ERR) { + up->icount.overrun++; + dev_warn(up->dev, "RX OVERRUN Error\n"); + } + if (status & UDMA_RX_BUFX_STATUS_FRAME_ERR) { + up->icount.frame++; + dev_warn(up->dev, "RX FRAMING Error\n"); + } + if (status & UDMA_RX_BUFX_STATUS_PARITY_ERR) { + up->icount.parity++; + dev_warn(up->dev, "RX PARITY Error\n"); + } + } + copied = (u32)tty_insert_flip_string( + tty_port, + priv->rx_bufs + (index * RX_BUF_SIZE), + length); + if (copied != length) { + dev_warn(up->dev, "Flip buffer overrun of %d bytes\n", + length - copied); + up->icount.overrun += length - copied; + } + up->icount.rx += length; + if (status & UDMA_RX_BUFX_STATUS_CLOSE_EXPIRED) + priv->dma_rx_partial_buf++; + else if (length != RX_BUF_SIZE) + /* + * This is a bug in the controller that doesn't cause + * any problems but will be fixed in the future. + */ + priv->rx_missing_close_timeout++; + else + priv->dma_rx_full_buf++; + + tty_flip_buffer_push(tty_port); +} + +static void brcmuart_rx_isr(struct uart_port *up, u32 rx_isr) +{ + struct brcmuart_priv *priv = up->private_data; + struct device *dev = up->dev; + u32 rx_done_isr; + u32 check_isr; + + rx_done_isr = (rx_isr & UDMA_INTR_RX_READY_MASK); + while (rx_done_isr) { + check_isr = UDMA_INTR_RX_READY_BUF0 << priv->rx_next_buf; + if (check_isr & rx_done_isr) { + brcmuart_rx_buf_done_isr(up, priv->rx_next_buf); + } else { + dev_err(dev, + "RX buffer ready out of sequence, restarting RX DMA\n"); + start_rx_dma(up_to_u8250p(up)); + break; + } + if (rx_isr & UDMA_RX_ERR_INTERRUPTS) { + if (rx_isr & UDMA_INTR_RX_ERROR) + priv->rx_err++; + if (rx_isr & UDMA_INTR_RX_TIMEOUT) { + priv->rx_timeout++; + dev_err(dev, "RX TIMEOUT Error\n"); + } + if (rx_isr & UDMA_INTR_RX_ABORT) + priv->rx_abort++; + priv->rx_running = false; + } + /* If not ABORT, re-enable RX buffer */ + if (!(rx_isr & UDMA_INTR_RX_ABORT)) + udma_unset(priv, REGS_DMA_RX, + UDMA_RX_BUFx_STATUS(priv->rx_next_buf), + UDMA_RX_BUFX_STATUS_DATA_RDY); + rx_done_isr &= ~check_isr; + priv->rx_next_buf++; + if (priv->rx_next_buf == RX_BUFS_COUNT) + priv->rx_next_buf = 0; + } +} + +static void brcmuart_tx_isr(struct uart_port *up, u32 isr) +{ + struct brcmuart_priv *priv = up->private_data; + struct device *dev = up->dev; + struct uart_8250_port *port_8250 = up_to_u8250p(up); + struct tty_port *tport = &port_8250->port.state->port; + + if (isr & UDMA_INTR_TX_ABORT) { + if (priv->tx_running) + dev_err(dev, "Unexpected TX_ABORT interrupt\n"); + return; + } + priv->tx_running = false; + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(up)) + brcmuart_tx_dma(port_8250); +} + +static irqreturn_t brcmuart_isr(int irq, void *dev_id) +{ + struct uart_port *up = dev_id; + struct device *dev = up->dev; + struct brcmuart_priv *priv = up->private_data; + unsigned long flags; + u32 interrupts; + u32 rval; + u32 tval; + + interrupts = udma_readl(priv, REGS_DMA_ISR, UDMA_INTR_STATUS); + if (interrupts == 0) + return IRQ_NONE; + + uart_port_lock_irqsave(up, &flags); + + /* Clear all interrupts */ + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_CLEAR, interrupts); + + rval = UDMA_IS_RX_INTERRUPT(interrupts); + if (rval) + brcmuart_rx_isr(up, rval); + tval = UDMA_IS_TX_INTERRUPT(interrupts); + if (tval) + brcmuart_tx_isr(up, tval); + if ((rval | tval) == 0) + dev_warn(dev, "Spurious interrupt: 0x%x\n", interrupts); + + uart_port_unlock_irqrestore(up, flags); + return IRQ_HANDLED; +} + +static int brcmuart_startup(struct uart_port *port) +{ + int res; + struct uart_8250_port *up = up_to_u8250p(port); + struct brcmuart_priv *priv = up->port.private_data; + + priv->shutdown = false; + + /* + * prevent serial8250_do_startup() from allocating non-existent + * DMA resources + */ + up->dma = NULL; + + res = serial8250_do_startup(port); + if (!priv->dma_enabled) + return res; + /* + * Disable the Receive Data Interrupt because the DMA engine + * will handle this. + * + * Synchronize UART_IER access against the console. + */ + uart_port_lock_irq(port); + up->ier &= ~UART_IER_RDI; + serial_port_out(port, UART_IER, up->ier); + uart_port_unlock_irq(port); + + priv->tx_running = false; + priv->dma.rx_dma = NULL; + priv->dma.tx_dma = brcmuart_tx_dma; + up->dma = &priv->dma; + + brcmuart_init_dma_hardware(priv); + start_rx_dma(up); + return res; +} + +static void brcmuart_shutdown(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct brcmuart_priv *priv = up->port.private_data; + unsigned long flags; + + uart_port_lock_irqsave(port, &flags); + priv->shutdown = true; + if (priv->dma_enabled) { + stop_rx_dma(up); + stop_tx_dma(up); + /* disable all interrupts */ + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_MASK_SET, + UDMA_RX_INTERRUPTS | UDMA_TX_INTERRUPTS); + } + + /* + * prevent serial8250_do_shutdown() from trying to free + * DMA resources that we never alloc'd for this driver. + */ + up->dma = NULL; + + uart_port_unlock_irqrestore(port, flags); + serial8250_do_shutdown(port); +} + +/* + * Not all clocks run at the exact specified rate, so set each requested + * rate and then get the actual rate. + */ +static void init_real_clk_rates(struct device *dev, struct brcmuart_priv *priv) +{ + int x; + int rc; + + priv->default_mux_rate = clk_get_rate(priv->baud_mux_clk); + for (x = 0; x < ARRAY_SIZE(priv->real_rates); x++) { + if (priv->rate_table[x] == 0) { + priv->real_rates[x] = 0; + continue; + } + rc = clk_set_rate(priv->baud_mux_clk, priv->rate_table[x]); + if (rc) { + dev_err(dev, "Error selecting BAUD MUX clock for %u\n", + priv->rate_table[x]); + priv->real_rates[x] = priv->rate_table[x]; + } else { + priv->real_rates[x] = clk_get_rate(priv->baud_mux_clk); + } + } + clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate); +} + +static u32 find_quot(struct device *dev, u32 freq, u32 baud, u32 *percent) +{ + u32 quot; + u32 rate; + u64 hires_rate; + u64 hires_baud; + u64 hires_err; + + rate = freq / 16; + quot = DIV_ROUND_CLOSEST(rate, baud); + if (!quot) + return 0; + + /* increase resolution to get xx.xx percent */ + hires_rate = div_u64((u64)rate * 10000, (u64)quot); + hires_baud = (u64)baud * 10000; + + /* get the delta */ + if (hires_rate > hires_baud) + hires_err = (hires_rate - hires_baud); + else + hires_err = (hires_baud - hires_rate); + + *percent = (unsigned long)DIV_ROUND_CLOSEST_ULL(hires_err, baud); + + dev_dbg(dev, "Baud rate: %u, MUX Clk: %u, Error: %u.%u%%\n", + baud, freq, *percent / 100, *percent % 100); + + return quot; +} + +static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv, + u32 baud) +{ + u32 percent; + u32 best_percent = UINT_MAX; + u32 quot; + u32 freq; + u32 best_quot = 1; + u32 best_freq = 0; + int rc; + int i; + int real_baud; + + /* If the Baud Mux Clock was not specified, just return */ + if (priv->baud_mux_clk == NULL) + return; + + /* Try default_mux_rate first */ + quot = find_quot(up->dev, priv->default_mux_rate, baud, &percent); + if (quot) { + best_percent = percent; + best_freq = priv->default_mux_rate; + best_quot = quot; + } + /* If more than 1% error, find the closest match for specified baud */ + if (best_percent > 100) { + for (i = 0; i < ARRAY_SIZE(priv->real_rates); i++) { + freq = priv->real_rates[i]; + if (freq == 0 || freq == priv->default_mux_rate) + continue; + quot = find_quot(up->dev, freq, baud, &percent); + if (!quot) + continue; + + if (percent < best_percent) { + best_percent = percent; + best_freq = freq; + best_quot = quot; + } + } + } + if (!best_freq) { + dev_err(up->dev, "Error, %d BAUD rate is too fast.\n", baud); + return; + } + rc = clk_set_rate(priv->baud_mux_clk, best_freq); + if (rc) + dev_err(up->dev, "Error selecting BAUD MUX clock\n"); + + /* Error over 3 percent will cause data errors */ + if (best_percent > 300) + dev_err(up->dev, "Error, baud: %d has %u.%u%% error\n", + baud, percent / 100, percent % 100); + + real_baud = best_freq / 16 / best_quot; + dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", best_freq); + dev_dbg(up->dev, "Requested baud: %u, Actual baud: %u\n", + baud, real_baud); + + /* calc nanoseconds for 1.5 characters time at the given baud rate */ + i = NSEC_PER_SEC / real_baud / 10; + i += (i / 2); + priv->char_wait = ns_to_ktime(i); + + up->uartclk = best_freq; +} + +static void brcmstb_set_termios(struct uart_port *up, + struct ktermios *termios, + const struct ktermios *old) +{ + struct uart_8250_port *p8250 = up_to_u8250p(up); + struct brcmuart_priv *priv = up->private_data; + + if (priv->dma_enabled) + stop_rx_dma(p8250); + set_clock_mux(up, priv, tty_termios_baud_rate(termios)); + serial8250_do_set_termios(up, termios, old); + if (p8250->mcr & UART_MCR_AFE) + p8250->port.status |= UPSTAT_AUTOCTS; + if (priv->dma_enabled) + start_rx_dma(p8250); +} + +static int brcmuart_handle_irq(struct uart_port *p) +{ + unsigned int iir = serial_port_in(p, UART_IIR); + struct brcmuart_priv *priv = p->private_data; + struct uart_8250_port *up = up_to_u8250p(p); + unsigned int status; + unsigned long flags; + unsigned int ier; + unsigned int mcr; + int handled = 0; + + /* + * There's a bug in some 8250 cores where we get a timeout + * interrupt but there is no data ready. + */ + if (((iir & UART_IIR_ID) == UART_IIR_RX_TIMEOUT) && !(priv->shutdown)) { + uart_port_lock_irqsave(p, &flags); + status = serial_port_in(p, UART_LSR); + if ((status & UART_LSR_DR) == 0) { + + ier = serial_port_in(p, UART_IER); + /* + * if Receive Data Interrupt is enabled and + * we're uing hardware flow control, deassert + * RTS and wait for any chars in the pipeline to + * arrive and then check for DR again. + */ + if ((ier & UART_IER_RDI) && (up->mcr & UART_MCR_AFE)) { + ier &= ~(UART_IER_RLSI | UART_IER_RDI); + serial_port_out(p, UART_IER, ier); + mcr = serial_port_in(p, UART_MCR); + mcr &= ~UART_MCR_RTS; + serial_port_out(p, UART_MCR, mcr); + hrtimer_start(&priv->hrt, priv->char_wait, + HRTIMER_MODE_REL); + } else { + serial_port_in(p, UART_RX); + } + + handled = 1; + } + uart_port_unlock_irqrestore(p, flags); + if (handled) + return 1; + } + return serial8250_handle_irq(p, iir); +} + +static enum hrtimer_restart brcmuart_hrtimer_func(struct hrtimer *t) +{ + struct brcmuart_priv *priv = container_of(t, struct brcmuart_priv, hrt); + struct uart_port *p = priv->up; + struct uart_8250_port *up = up_to_u8250p(p); + unsigned int status; + unsigned long flags; + + if (priv->shutdown) + return HRTIMER_NORESTART; + + uart_port_lock_irqsave(p, &flags); + status = serial_port_in(p, UART_LSR); + + /* + * If a character did not arrive after the timeout, clear the false + * receive timeout. + */ + if ((status & UART_LSR_DR) == 0) { + serial_port_in(p, UART_RX); + priv->rx_bad_timeout_no_char++; + } else { + priv->rx_bad_timeout_late_char++; + } + + /* re-enable receive unless upper layer has disabled it */ + if ((up->ier & (UART_IER_RLSI | UART_IER_RDI)) == + (UART_IER_RLSI | UART_IER_RDI)) { + status = serial_port_in(p, UART_IER); + status |= (UART_IER_RLSI | UART_IER_RDI); + serial_port_out(p, UART_IER, status); + status = serial_port_in(p, UART_MCR); + status |= UART_MCR_RTS; + serial_port_out(p, UART_MCR, status); + } + uart_port_unlock_irqrestore(p, flags); + return HRTIMER_NORESTART; +} + +static const struct of_device_id brcmuart_dt_ids[] = { + { + .compatible = "brcm,bcm7278-uart", + .data = brcmstb_rate_table_7278, + }, + { + .compatible = "brcm,bcm7271-uart", + .data = brcmstb_rate_table, + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, brcmuart_dt_ids); + +static void brcmuart_free_bufs(struct device *dev, struct brcmuart_priv *priv) +{ + if (priv->rx_bufs) + dma_free_coherent(dev, priv->rx_size, priv->rx_bufs, + priv->rx_addr); + if (priv->tx_buf) + dma_free_coherent(dev, priv->tx_size, priv->tx_buf, + priv->tx_addr); +} + +static void brcmuart_throttle(struct uart_port *port) +{ + struct brcmuart_priv *priv = port->private_data; + + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_MASK_SET, UDMA_RX_INTERRUPTS); +} + +static void brcmuart_unthrottle(struct uart_port *port) +{ + struct brcmuart_priv *priv = port->private_data; + + udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_MASK_CLEAR, + UDMA_RX_INTERRUPTS); +} + +static int debugfs_stats_show(struct seq_file *s, void *unused) +{ + struct brcmuart_priv *priv = s->private; + + seq_printf(s, "rx_err:\t\t\t\t%u\n", + priv->rx_err); + seq_printf(s, "rx_timeout:\t\t\t%u\n", + priv->rx_timeout); + seq_printf(s, "rx_abort:\t\t\t%u\n", + priv->rx_abort); + seq_printf(s, "rx_bad_timeout_late_char:\t%u\n", + priv->rx_bad_timeout_late_char); + seq_printf(s, "rx_bad_timeout_no_char:\t\t%u\n", + priv->rx_bad_timeout_no_char); + seq_printf(s, "rx_missing_close_timeout:\t%u\n", + priv->rx_missing_close_timeout); + if (priv->dma_enabled) { + seq_printf(s, "dma_rx_partial_buf:\t\t%llu\n", + priv->dma_rx_partial_buf); + seq_printf(s, "dma_rx_full_buf:\t\t%llu\n", + priv->dma_rx_full_buf); + } + return 0; +} +DEFINE_SHOW_ATTRIBUTE(debugfs_stats); + +static void brcmuart_init_debugfs(struct brcmuart_priv *priv, + const char *device) +{ + priv->debugfs_dir = debugfs_create_dir(device, brcmuart_debugfs_root); + debugfs_create_file("stats", 0444, priv->debugfs_dir, priv, + &debugfs_stats_fops); +} + + +static int brcmuart_probe(struct platform_device *pdev) +{ + struct resource *regs; + const struct of_device_id *of_id = NULL; + struct uart_8250_port *new_port; + struct device *dev = &pdev->dev; + struct brcmuart_priv *priv; + struct clk *baud_mux_clk; + struct uart_8250_port up; + void __iomem *membase = NULL; + resource_size_t mapbase = 0; + int ret; + int x; + int dma_irq; + static const char * const reg_names[REGS_MAX] = { + "uart", "dma_rx", "dma_tx", "dma_intr2", "dma_arb" + }; + + priv = devm_kzalloc(dev, sizeof(struct brcmuart_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + of_id = of_match_node(brcmuart_dt_ids, dev->of_node); + if (!of_id || !of_id->data) + priv->rate_table = brcmstb_rate_table; + else + priv->rate_table = of_id->data; + + for (x = 0; x < REGS_MAX; x++) { + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, + reg_names[x]); + if (!regs) + break; + priv->regs[x] = devm_ioremap(dev, regs->start, + resource_size(regs)); + if (!priv->regs[x]) + return -ENOMEM; + if (x == REGS_8250) { + mapbase = regs->start; + membase = priv->regs[x]; + } + } + + /* We should have just the uart base registers or all the registers */ + if (x != 1 && x != REGS_MAX) + return dev_err_probe(dev, -EINVAL, "%s registers not specified\n", + reg_names[x]); + + /* if the DMA registers were specified, try to enable DMA */ + if (x > REGS_DMA_RX) { + if (brcmuart_arbitration(priv, 1) == 0) { + u32 txrev = 0; + u32 rxrev = 0; + + txrev = udma_readl(priv, REGS_DMA_RX, UDMA_RX_REVISION); + rxrev = udma_readl(priv, REGS_DMA_TX, UDMA_TX_REVISION); + if ((txrev >= UDMA_TX_REVISION_REQUIRED) && + (rxrev >= UDMA_RX_REVISION_REQUIRED)) { + + /* Enable the use of the DMA hardware */ + priv->dma_enabled = true; + } else { + brcmuart_arbitration(priv, 0); + dev_err(dev, + "Unsupported DMA Hardware Revision\n"); + } + } else { + dev_err(dev, + "Timeout arbitrating for UART DMA hardware\n"); + } + } + + dev_dbg(dev, "DMA is %senabled\n", priv->dma_enabled ? "" : "not "); + + memset(&up, 0, sizeof(up)); + up.port.type = PORT_BCM7271; + up.port.dev = dev; + up.port.mapbase = mapbase; + up.port.membase = membase; + up.port.handle_irq = brcmuart_handle_irq; + up.port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_FIXED_TYPE; + up.port.private_data = priv; + + ret = uart_read_port_properties(&up.port); + if (ret) + goto release_dma; + + up.port.regshift = 2; + up.port.iotype = device_is_big_endian(dev) ? UPIO_MEM32BE : UPIO_MEM32; + + /* See if a Baud clock has been specified */ + baud_mux_clk = devm_clk_get_optional_enabled(dev, "sw_baud"); + ret = PTR_ERR_OR_ZERO(baud_mux_clk); + if (ret) + goto release_dma; + if (baud_mux_clk) { + dev_dbg(dev, "BAUD MUX clock found\n"); + + priv->baud_mux_clk = baud_mux_clk; + init_real_clk_rates(dev, priv); + up.port.uartclk = priv->default_mux_rate; + } else { + dev_dbg(dev, "BAUD MUX clock not specified\n"); + } + + /* setup HR timer */ + hrtimer_setup(&priv->hrt, brcmuart_hrtimer_func, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + + up.port.shutdown = brcmuart_shutdown; + up.port.startup = brcmuart_startup; + up.port.throttle = brcmuart_throttle; + up.port.unthrottle = brcmuart_unthrottle; + up.port.set_termios = brcmstb_set_termios; + + if (priv->dma_enabled) { + priv->rx_size = RX_BUF_SIZE * RX_BUFS_COUNT; + priv->rx_bufs = dma_alloc_coherent(dev, + priv->rx_size, + &priv->rx_addr, GFP_KERNEL); + if (!priv->rx_bufs) { + ret = -ENOMEM; + goto err; + } + priv->tx_size = UART_XMIT_SIZE; + priv->tx_buf = dma_alloc_coherent(dev, + priv->tx_size, + &priv->tx_addr, GFP_KERNEL); + if (!priv->tx_buf) { + ret = -ENOMEM; + goto err; + } + } + + ret = serial8250_register_8250_port(&up); + if (ret < 0) { + dev_err_probe(dev, ret, "unable to register 8250 port\n"); + goto err; + } + priv->line = ret; + new_port = serial8250_get_port(ret); + priv->up = &new_port->port; + if (priv->dma_enabled) { + dma_irq = platform_get_irq_byname(pdev, "dma"); + if (dma_irq < 0) { + ret = dev_err_probe(dev, dma_irq, "no IRQ resource info\n"); + goto err1; + } + ret = devm_request_irq(dev, dma_irq, brcmuart_isr, + IRQF_SHARED, "uart DMA irq", &new_port->port); + if (ret) + goto err1; + } + platform_set_drvdata(pdev, priv); + brcmuart_init_debugfs(priv, dev_name(&pdev->dev)); + return 0; + +err1: + serial8250_unregister_port(priv->line); +err: + brcmuart_free_bufs(dev, priv); +release_dma: + if (priv->dma_enabled) + brcmuart_arbitration(priv, 0); + return ret; +} + +static void brcmuart_remove(struct platform_device *pdev) +{ + struct brcmuart_priv *priv = platform_get_drvdata(pdev); + + debugfs_remove_recursive(priv->debugfs_dir); + hrtimer_cancel(&priv->hrt); + serial8250_unregister_port(priv->line); + brcmuart_free_bufs(&pdev->dev, priv); + if (priv->dma_enabled) + brcmuart_arbitration(priv, 0); +} + +static int __maybe_unused brcmuart_suspend(struct device *dev) +{ + struct brcmuart_priv *priv = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(priv->line); + struct uart_port *port = &up->port; + unsigned long flags; + + /* + * This will prevent resume from enabling RTS before the + * baud rate has been restored. + */ + uart_port_lock_irqsave(port, &flags); + priv->saved_mctrl = port->mctrl; + port->mctrl &= ~TIOCM_RTS; + uart_port_unlock_irqrestore(port, flags); + + serial8250_suspend_port(priv->line); + clk_disable_unprepare(priv->baud_mux_clk); + + return 0; +} + +static int __maybe_unused brcmuart_resume(struct device *dev) +{ + struct brcmuart_priv *priv = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(priv->line); + struct uart_port *port = &up->port; + unsigned long flags; + int ret; + + ret = clk_prepare_enable(priv->baud_mux_clk); + if (ret) + dev_err(dev, "Error enabling BAUD MUX clock\n"); + + /* + * The hardware goes back to it's default after suspend + * so get the "clk" back in sync. + */ + ret = clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate); + if (ret) + dev_err(dev, "Error restoring default BAUD MUX clock\n"); + if (priv->dma_enabled) { + if (brcmuart_arbitration(priv, 1)) { + dev_err(dev, "Timeout arbitrating for DMA hardware on resume\n"); + return(-EBUSY); + } + brcmuart_init_dma_hardware(priv); + start_rx_dma(serial8250_get_port(priv->line)); + } + serial8250_resume_port(priv->line); + + if (priv->saved_mctrl & TIOCM_RTS) { + /* Restore RTS */ + uart_port_lock_irqsave(port, &flags); + port->mctrl |= TIOCM_RTS; + port->ops->set_mctrl(port, port->mctrl); + uart_port_unlock_irqrestore(port, flags); + } + + return 0; +} + +static const struct dev_pm_ops brcmuart_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(brcmuart_suspend, brcmuart_resume) +}; + +static struct platform_driver brcmuart_platform_driver = { + .driver = { + .name = "bcm7271-uart", + .pm = &brcmuart_dev_pm_ops, + .of_match_table = brcmuart_dt_ids, + }, + .probe = brcmuart_probe, + .remove = brcmuart_remove, +}; + +static int __init brcmuart_init(void) +{ + int ret; + + brcmuart_debugfs_root = debugfs_create_dir( + brcmuart_platform_driver.driver.name, NULL); + ret = platform_driver_register(&brcmuart_platform_driver); + if (ret) { + debugfs_remove_recursive(brcmuart_debugfs_root); + return ret; + } + + return 0; +} +module_init(brcmuart_init); + +static void __exit brcmuart_deinit(void) +{ + platform_driver_unregister(&brcmuart_platform_driver); + debugfs_remove_recursive(brcmuart_debugfs_root); +} +module_exit(brcmuart_deinit); + +MODULE_AUTHOR("Al Cooper"); +MODULE_DESCRIPTION("Broadcom NS16550A compatible serial port driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/8250/8250_boca.c b/drivers/tty/serial/8250/8250_boca.c new file mode 100644 index 000000000..a9b97c034 --- /dev/null +++ b/drivers/tty/serial/8250/8250_boca.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/serial_8250.h> + +#include "8250.h" + +static struct plat_serial8250_port boca_data[] = { + SERIAL8250_PORT(0x100, 12), + SERIAL8250_PORT(0x108, 12), + SERIAL8250_PORT(0x110, 12), + SERIAL8250_PORT(0x118, 12), + SERIAL8250_PORT(0x120, 12), + SERIAL8250_PORT(0x128, 12), + SERIAL8250_PORT(0x130, 12), + SERIAL8250_PORT(0x138, 12), + SERIAL8250_PORT(0x140, 12), + SERIAL8250_PORT(0x148, 12), + SERIAL8250_PORT(0x150, 12), + SERIAL8250_PORT(0x158, 12), + SERIAL8250_PORT(0x160, 12), + SERIAL8250_PORT(0x168, 12), + SERIAL8250_PORT(0x170, 12), + SERIAL8250_PORT(0x178, 12), + { }, +}; + +static struct platform_device boca_device = { + .name = "serial8250", + .id = PLAT8250_DEV_BOCA, + .dev = { + .platform_data = boca_data, + }, +}; + +static int __init boca_init(void) +{ + return platform_device_register(&boca_device); +} + +module_init(boca_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for Boca cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_ce4100.c b/drivers/tty/serial/8250/8250_ce4100.c new file mode 100644 index 000000000..81dfb2adb --- /dev/null +++ b/drivers/tty/serial/8250/8250_ce4100.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel CE4100 platform specific setup code + * + * (C) Copyright 2010 Intel Corporation + */ + +#include <linux/init.h> +#include <linux/io.h> +#include <linux/types.h> + +#include <asm/ce4100.h> +#include <asm/fixmap.h> +#include <asm/page.h> + +#include <linux/serial_reg.h> +#include <linux/serial_8250.h> + +static unsigned int mem_serial_in(struct uart_port *p, int offset) +{ + offset = offset << p->regshift; + return readl(p->membase + offset); +} + +/* + * The UART Tx interrupts are not set under some conditions and therefore serial + * transmission hangs. This is a silicon issue and has not been root caused. The + * workaround for this silicon issue checks UART_LSR_THRE bit and UART_LSR_TEMT + * bit of LSR register in interrupt handler to see whether at least one of these + * two bits is set, if so then process the transmit request. If this workaround + * is not applied, then the serial transmission may hang. This workaround is for + * errata number 9 in Errata - B step. +*/ +static u32 ce4100_mem_serial_in(struct uart_port *p, unsigned int offset) +{ + u32 ret, ier, lsr; + + ret = mem_serial_in(p, offset); + if (offset != UART_IIR || !(ret & UART_IIR_NO_INT)) + return ret; + + /* see if the TX interrupt should have really set */ + ier = mem_serial_in(p, UART_IER); + /* see if the UART's XMIT interrupt is enabled */ + if (!(ier & UART_IER_THRI)) + return ret; + + lsr = mem_serial_in(p, UART_LSR); + /* now check to see if the UART should be generating an interrupt (but isn't) */ + if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) + ret &= ~UART_IIR_NO_INT; + + return ret; +} + +static void ce4100_mem_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ + offset <<= p->regshift; + writel(value, p->membase + offset); +} + +static void ce4100_serial_fixup(int port, struct uart_port *up, u32 *capabilities) +{ +#ifdef CONFIG_EARLY_PRINTK + /* + * Override the legacy port configuration that comes from + * asm/serial.h. Using the ioport driver then switching to the + * PCI memmaped driver hangs the IOAPIC. + */ + if (up->iotype != UPIO_MEM32) { + up->uartclk = 14745600; + up->mapbase = 0xdffe0200; + set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, up->mapbase & PAGE_MASK); + up->membase = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); + up->membase += up->mapbase & ~PAGE_MASK; + up->mapbase += port * 0x100; + up->membase += port * 0x100; + up->iotype = UPIO_MEM32; + up->regshift = 2; + up->irq = 4; + } +#endif + up->iobase = 0; + up->serial_in = ce4100_mem_serial_in; + up->serial_out = ce4100_mem_serial_out; + + *capabilities |= (1 << 12); +} + +void __init sdv_serial_fixup(void) +{ + serial8250_set_isa_configurator(ce4100_serial_fixup); +} diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c new file mode 100644 index 000000000..b875d3947 --- /dev/null +++ b/drivers/tty/serial/8250/8250_core.c @@ -0,0 +1,949 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Universal/legacy driver for 8250/16550-type serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King. + * + * Supports: + * early_serial_setup() ports + * userspace-configurable "phantom" ports + * serial8250_register_8250_port() ports + */ + +#include <linux/acpi.h> +#include <linux/hashtable.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/tty.h> +#include <linux/ratelimit.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serial_8250.h> +#include <linux/nmi.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/string_helpers.h> +#include <linux/uaccess.h> +#include <linux/io.h> + +#include <asm/irq.h> + +#include "8250.h" + +#define PASS_LIMIT 512 + +struct irq_info { + struct hlist_node node; + int irq; + spinlock_t lock; /* Protects list not the hash */ + struct list_head *head; +}; + +#define IRQ_HASH_BITS 5 /* Can be adjusted later */ +static DEFINE_HASHTABLE(irq_lists, IRQ_HASH_BITS); +static DEFINE_MUTEX(hash_mutex); /* Used to walk the hash */ + +static bool skip_txen_test; +module_param(skip_txen_test, bool, 0644); +MODULE_PARM_DESC(skip_txen_test, "Skip checking for the TXEN bug at init time"); + +/* + * This is the serial driver's interrupt routine. + * + * Arjan thinks the old way was overly complex, so it got simplified. + * Alan disagrees, saying that need the complexity to handle the weird + * nature of ISA shared interrupts. (This is a special exception.) + * + * In order to handle ISA shared interrupts properly, we need to check + * that all ports have been serviced, and therefore the ISA interrupt + * line has been de-asserted. + * + * This means we need to loop through all ports. checking that they + * don't have an interrupt pending. + */ +static irqreturn_t serial8250_interrupt(int irq, void *dev_id) +{ + struct irq_info *i = dev_id; + struct list_head *l, *end = NULL; + int pass_counter = 0, handled = 0; + + guard(spinlock)(&i->lock); + + l = i->head; + do { + struct uart_8250_port *up = list_entry(l, struct uart_8250_port, list); + struct uart_port *port = &up->port; + + if (port->handle_irq(port)) { + handled = 1; + end = NULL; + } else if (end == NULL) + end = l; + + l = l->next; + + if (l == i->head && pass_counter++ > PASS_LIMIT) + break; + } while (l != end); + + return IRQ_RETVAL(handled); +} + +/* + * To support ISA shared interrupts, we need to have one interrupt + * handler that ensures that the IRQ line has been deasserted + * before returning. Failing to do this will result in the IRQ + * line being stuck active, and, since ISA irqs are edge triggered, + * no more IRQs will be seen. + */ +static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up) +{ + spin_lock_irq(&i->lock); + + if (!list_empty(i->head)) { + if (i->head == &up->list) + i->head = i->head->next; + list_del(&up->list); + } else { + BUG_ON(i->head != &up->list); + i->head = NULL; + } + spin_unlock_irq(&i->lock); + /* List empty so throw away the hash node */ + if (i->head == NULL) { + hlist_del(&i->node); + kfree(i); + } +} + +/* + * Either: + * - find the corresponding info in the hashtable and return it, or + * - allocate a new one, add it to the hashtable and return it. + */ +static struct irq_info *serial_get_or_create_irq_info(const struct uart_8250_port *up) +{ + struct irq_info *i; + + guard(mutex)(&hash_mutex); + + hash_for_each_possible(irq_lists, i, node, up->port.irq) + if (i->irq == up->port.irq) + return i; + + i = kzalloc_obj(*i); + if (i == NULL) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&i->lock); + i->irq = up->port.irq; + hash_add(irq_lists, &i->node, i->irq); + + return i; +} + +static int serial_link_irq_chain(struct uart_8250_port *up) +{ + struct irq_info *i; + int ret; + + i = serial_get_or_create_irq_info(up); + if (IS_ERR(i)) + return PTR_ERR(i); + + scoped_guard(spinlock_irq, &i->lock) { + if (i->head) { + list_add(&up->list, i->head); + + return 0; + } + + INIT_LIST_HEAD(&up->list); + i->head = &up->list; + } + + ret = request_irq(up->port.irq, serial8250_interrupt, up->port.irqflags, up->port.name, i); + if (ret < 0) + serial_do_unlink(i, up); + + return ret; +} + +static void serial_unlink_irq_chain(struct uart_8250_port *up) +{ + struct irq_info *i; + + guard(mutex)(&hash_mutex); + + hash_for_each_possible(irq_lists, i, node, up->port.irq) + if (i->irq == up->port.irq) { + if (WARN_ON(i->head == NULL)) + return; + + if (list_empty(i->head)) + free_irq(up->port.irq, i); + + serial_do_unlink(i, up); + + return; + } + + WARN_ON(1); +} + +/* + * This function is used to handle ports that do not have an + * interrupt. This doesn't work very well for 16450's, but gives + * barely passable results for a 16550A. (Although at the expense + * of much CPU overhead). + */ +static void serial8250_timeout(struct timer_list *t) +{ + struct uart_8250_port *up = timer_container_of(up, t, timer); + + up->port.handle_irq(&up->port); + mod_timer(&up->timer, jiffies + uart_poll_timeout(&up->port)); +} + +static void serial8250_backup_timeout(struct timer_list *t) +{ + struct uart_8250_port *up = timer_container_of(up, t, timer); + unsigned int iir, ier = 0, lsr; + unsigned long flags; + + uart_port_lock_irqsave(&up->port, &flags); + + /* + * Must disable interrupts or else we risk racing with the interrupt + * based handler. + */ + if (up->port.irq) { + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); + } + + iir = serial_in(up, UART_IIR); + + /* + * This should be a safe test for anyone who doesn't trust the + * IIR bits on their UART, but it's specifically designed for + * the "Diva" UART used on the management processor on many HP + * ia64 and parisc boxes. + */ + lsr = serial_lsr_in(up); + if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) && + (!kfifo_is_empty(&up->port.state->port.xmit_fifo) || + up->port.x_char) && + (lsr & UART_LSR_THRE)) { + iir &= ~(UART_IIR_ID | UART_IIR_NO_INT); + iir |= UART_IIR_THRI; + } + + if (!(iir & UART_IIR_NO_INT)) + serial8250_tx_chars(up); + + if (up->port.irq) + serial_out(up, UART_IER, ier); + + uart_port_unlock_irqrestore(&up->port, flags); + + /* Standard timer interval plus 0.2s to keep the port running */ + mod_timer(&up->timer, + jiffies + uart_poll_timeout(&up->port) + HZ / 5); +} + +static void univ8250_setup_timer(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + + /* + * The above check will only give an accurate result the first time + * the port is opened so this value needs to be preserved. + */ + if (up->bugs & UART_BUG_THRE) { + pr_debug("%s - using backup timer\n", port->name); + + up->timer.function = serial8250_backup_timeout; + mod_timer(&up->timer, jiffies + + uart_poll_timeout(port) + HZ / 5); + } + + /* + * If the "interrupt" for this port doesn't correspond with any + * hardware interrupt, we use a timer-based system. The original + * driver used to do this with IRQ0. + */ + if (!port->irq) + mod_timer(&up->timer, jiffies + uart_poll_timeout(port)); +} + +static int univ8250_setup_irq(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + + if (port->irq) + return serial_link_irq_chain(up); + + return 0; +} + +static void univ8250_release_irq(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + + timer_delete_sync(&up->timer); + up->timer.function = serial8250_timeout; + if (port->irq) + serial_unlink_irq_chain(up); +} + +const struct uart_ops *univ8250_port_base_ops; +struct uart_ops univ8250_port_ops; + +static const struct uart_8250_ops univ8250_driver_ops = { + .setup_irq = univ8250_setup_irq, + .release_irq = univ8250_release_irq, + .setup_timer = univ8250_setup_timer, +}; + +static struct uart_8250_port serial8250_ports[UART_NR]; + +/** + * serial8250_get_port - retrieve struct uart_8250_port + * @line: serial line number + * + * This function retrieves struct uart_8250_port for the specific line. + * This struct *must* *not* be used to perform a 8250 or serial core operation + * which is not accessible otherwise. Its only purpose is to make the struct + * accessible to the runtime-pm callbacks for context suspend/restore. + * The lock assumption made here is none because runtime-pm suspend/resume + * callbacks should not be invoked if there is any operation performed on the + * port. + */ +struct uart_8250_port *serial8250_get_port(int line) +{ + return &serial8250_ports[line]; +} +EXPORT_SYMBOL_GPL(serial8250_get_port); + +static inline void serial8250_apply_quirks(struct uart_8250_port *up) +{ + up->port.quirks |= skip_txen_test ? UPQ_NO_TXEN_TEST : 0; +} + +struct uart_8250_port *serial8250_setup_port(int index) +{ + struct uart_8250_port *up; + + if (index >= UART_NR) + return NULL; + + up = &serial8250_ports[index]; + up->port.line = index; + up->port.port_id = index; + + serial8250_init_port(up); + if (!univ8250_port_base_ops) + univ8250_port_base_ops = up->port.ops; + up->port.ops = &univ8250_port_ops; + + timer_setup(&up->timer, serial8250_timeout, 0); + + up->ops = &univ8250_driver_ops; + + serial8250_set_defaults(up); + + return up; +} + +void __init serial8250_register_ports(struct uart_driver *drv, struct device *dev) +{ + int i; + + for (i = 0; i < nr_uarts; i++) { + struct uart_8250_port *up = &serial8250_ports[i]; + + if (up->port.type == PORT_8250_CIR) + continue; + + if (up->port.dev) + continue; + + up->port.dev = dev; + + if (uart_console_registered(&up->port)) + pm_runtime_get_sync(up->port.dev); + + serial8250_apply_quirks(up); + uart_add_one_port(drv, &up->port); + } +} + +#ifdef CONFIG_SERIAL_8250_CONSOLE + +static void univ8250_console_write_atomic(struct console *co, + struct nbcon_write_context *wctxt) +{ + struct uart_8250_port *up = &serial8250_ports[co->index]; + + serial8250_console_write(up, wctxt, true); +} + +static void univ8250_console_write_thread(struct console *co, + struct nbcon_write_context *wctxt) +{ + struct uart_8250_port *up = &serial8250_ports[co->index]; + + serial8250_console_write(up, wctxt, false); +} + +static void univ8250_console_device_lock(struct console *co, unsigned long *flags) +{ + struct uart_port *up = &serial8250_ports[co->index].port; + + __uart_port_lock_irqsave(up, flags); +} + +static void univ8250_console_device_unlock(struct console *co, unsigned long flags) +{ + struct uart_port *up = &serial8250_ports[co->index].port; + + __uart_port_unlock_irqrestore(up, flags); +} + +static int univ8250_console_setup(struct console *co, char *options) +{ + struct uart_8250_port *up; + struct uart_port *port; + int retval, i; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index < 0 || co->index >= UART_NR) + co->index = 0; + + /* + * If the console is past the initial isa ports, init more ports up to + * co->index as needed and increment nr_uarts accordingly. + */ + for (i = nr_uarts; i <= co->index; i++) { + up = serial8250_setup_port(i); + if (!up) + return -ENODEV; + nr_uarts++; + } + + port = &serial8250_ports[co->index].port; + /* link port to console */ + uart_port_set_cons(port, co); + + retval = serial8250_console_setup(port, options, false); + if (retval != 0) + uart_port_set_cons(port, NULL); + return retval; +} + +static int univ8250_console_exit(struct console *co) +{ + struct uart_port *port; + + port = &serial8250_ports[co->index].port; + return serial8250_console_exit(port); +} + +/** + * univ8250_console_match - non-standard console matching + * @co: registering console + * @name: name from console command line + * @idx: index from console command line + * @options: ptr to option string from console command line + * + * Only attempts to match console command lines of the form: + * console=uart[8250],io|mmio|mmio16|mmio32,<addr>[,<options>] + * console=uart[8250],0x<addr>[,<options>] + * This form is used to register an initial earlycon boot console and + * replace it with the serial8250_console at 8250 driver init. + * + * Performs console setup for a match (as required by interface) + * If no <options> are specified, then assume the h/w is already setup. + * + * Returns 0 if console matches; otherwise non-zero to use default matching + */ +static int univ8250_console_match(struct console *co, char *name, int idx, + char *options) +{ + char match[] = "uart"; /* 8250-specific earlycon name */ + enum uart_iotype iotype; + resource_size_t addr; + int i; + + if (strncmp(name, match, 4) != 0) + return -ENODEV; + + if (uart_parse_earlycon(options, &iotype, &addr, &options)) + return -ENODEV; + + /* try to match the port specified on the command line */ + for (i = 0; i < nr_uarts; i++) { + struct uart_port *port = &serial8250_ports[i].port; + + if (port->iotype != iotype) + continue; + if ((iotype == UPIO_MEM || iotype == UPIO_MEM16 || + iotype == UPIO_MEM32 || iotype == UPIO_MEM32BE) + && (port->mapbase != addr)) + continue; + if (iotype == UPIO_PORT && port->iobase != addr) + continue; + + co->index = i; + uart_port_set_cons(port, co); + return serial8250_console_setup(port, options, true); + } + + return -ENODEV; +} + +static struct console univ8250_console = { + .name = "ttyS", + .write_atomic = univ8250_console_write_atomic, + .write_thread = univ8250_console_write_thread, + .device_lock = univ8250_console_device_lock, + .device_unlock = univ8250_console_device_unlock, + .device = uart_console_device, + .setup = univ8250_console_setup, + .exit = univ8250_console_exit, + .match = univ8250_console_match, + .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_NBCON, + .index = -1, + .data = &serial8250_reg, +}; + +static int __init univ8250_console_init(void) +{ + if (nr_uarts == 0) + return -ENODEV; + + serial8250_isa_init_ports(); + register_console(&univ8250_console); + return 0; +} +console_initcall(univ8250_console_init); + +#define SERIAL8250_CONSOLE (&univ8250_console) +#else +#define SERIAL8250_CONSOLE NULL +#endif + +struct uart_driver serial8250_reg = { + .owner = THIS_MODULE, + .driver_name = "serial_8250", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .cons = SERIAL8250_CONSOLE, +}; + +/* + * early_serial_setup - early registration for 8250 ports + * + * Setup an 8250 port structure prior to console initialisation. Use + * after console initialisation will cause undefined behaviour. + */ +int __init early_serial_setup(struct uart_port *port) +{ + struct uart_port *p; + + if (port->line >= ARRAY_SIZE(serial8250_ports) || nr_uarts == 0) + return -ENODEV; + + serial8250_isa_init_ports(); + p = &serial8250_ports[port->line].port; + p->iobase = port->iobase; + p->membase = port->membase; + p->irq = port->irq; + p->irqflags = port->irqflags; + p->uartclk = port->uartclk; + p->fifosize = port->fifosize; + p->regshift = port->regshift; + p->iotype = port->iotype; + p->flags = port->flags; + p->mapbase = port->mapbase; + p->mapsize = port->mapsize; + p->private_data = port->private_data; + p->type = port->type; + p->line = port->line; + + serial8250_set_defaults(up_to_u8250p(p)); + + if (port->serial_in) + p->serial_in = port->serial_in; + if (port->serial_out) + p->serial_out = port->serial_out; + if (port->handle_irq) + p->handle_irq = port->handle_irq; + + return 0; +} + +/** + * serial8250_suspend_port - suspend one serial port + * @line: serial line number + * + * Suspend one serial port. + */ +void serial8250_suspend_port(int line) +{ + struct uart_8250_port *up = &serial8250_ports[line]; + struct uart_port *port = &up->port; + + if (uart_console(port)) { + /* No irq_work may be queued when suspending */ + scoped_guard(uart_port_lock_irq, port) + up->console_msr_work_allow = false; + irq_work_sync(&up->console_msr_work); + + if (!console_suspend_enabled && port->type != PORT_8250) { + unsigned char canary = 0xa5; + + serial_out(up, UART_SCR, canary); + if (serial_in(up, UART_SCR) == canary) + up->canary = canary; + } + } + + uart_suspend_port(&serial8250_reg, port); +} +EXPORT_SYMBOL(serial8250_suspend_port); + +/** + * serial8250_resume_port - resume one serial port + * @line: serial line number + * + * Resume one serial port. + */ +void serial8250_resume_port(int line) +{ + struct uart_8250_port *up = &serial8250_ports[line]; + struct uart_port *port = &up->port; + + up->canary = 0; + + if (up->capabilities & UART_NATSEMI) { + /* Ensure it's still in high speed mode */ + serial_port_out(port, UART_LCR, 0xE0); + + ns16550a_goto_highspeed(up); + + serial_port_out(port, UART_LCR, 0); + port->uartclk = 921600*16; + } + uart_resume_port(&serial8250_reg, port); + + if (uart_console(port)) { + + guard(uart_port_lock_irq)(port); + + /* irq_work allowed again */ + up->console_msr_work_allow = true; + + /* Handle any pending MSR changes */ + if (up->msr_saved_flags) + serial8250_modem_status(up); + } +} +EXPORT_SYMBOL(serial8250_resume_port); + +/* + * serial8250_register_8250_port and serial8250_unregister_port allows for + * 16x50 serial ports to be configured at run-time, to support PCMCIA + * modems and PCI multiport cards. + */ +static DEFINE_MUTEX(serial_mutex); + +static struct uart_8250_port *serial8250_find_match_or_unused(const struct uart_port *port) +{ + int i; + + /* + * First, find a port entry which matches. + */ + for (i = 0; i < nr_uarts; i++) + if (uart_match_port(&serial8250_ports[i].port, port)) + return &serial8250_ports[i]; + + /* try line number first if still available */ + i = port->line; + if (i < nr_uarts && serial8250_ports[i].port.type == PORT_UNKNOWN && + serial8250_ports[i].port.iobase == 0) + return &serial8250_ports[i]; + /* + * We didn't find a matching entry, so look for the first + * free entry. We look for one which hasn't been previously + * used (indicated by zero iobase). + */ + for (i = 0; i < nr_uarts; i++) + if (serial8250_ports[i].port.type == PORT_UNKNOWN && + serial8250_ports[i].port.iobase == 0) + return &serial8250_ports[i]; + + /* + * That also failed. Last resort is to find any entry which + * doesn't have a real port associated with it. + */ + for (i = 0; i < nr_uarts; i++) + if (serial8250_ports[i].port.type == PORT_UNKNOWN) + return &serial8250_ports[i]; + + return NULL; +} + +static void serial_8250_overrun_backoff_work(struct work_struct *work) +{ + struct uart_8250_port *up = container_of(to_delayed_work(work), struct uart_8250_port, + overrun_backoff); + + guard(uart_port_lock_irqsave)(&up->port); + up->ier |= UART_IER_RLSI | UART_IER_RDI; + serial_out(up, UART_IER, up->ier); +} + +/** + * serial8250_register_8250_port - register a serial port + * @up: serial port template + * + * Configure the serial port specified by the request. If the + * port exists and is in use, it is hung up and unregistered + * first. + * + * The port is then probed and if necessary the IRQ is autodetected + * If this fails an error is returned. + * + * On success the port is ready to use and the line number is returned. + */ +int serial8250_register_8250_port(const struct uart_8250_port *up) +{ + struct uart_8250_port *uart; + bool cons_flow; + int ret; + + if (up->port.uartclk == 0) + return -EINVAL; + + guard(mutex)(&serial_mutex); + + uart = serial8250_find_match_or_unused(&up->port); + if (!uart) { + /* + * If the port is past the initial isa ports, initialize a new + * port and increment nr_uarts accordingly. + */ + uart = serial8250_setup_port(nr_uarts); + if (!uart) + return -ENOSPC; + nr_uarts++; + } + + /* Check if it is CIR already. We check this below again, see there why. */ + if (uart->port.type == PORT_8250_CIR) + return -ENODEV; + + /* Preserve specified console flow control. */ + cons_flow = uart_cons_flow_enabled(&uart->port); + + if (uart->port.dev) + uart_remove_one_port(&serial8250_reg, &uart->port); + + uart->port.ctrl_id = up->port.ctrl_id; + uart->port.port_id = up->port.port_id; + uart->port.iobase = up->port.iobase; + uart->port.membase = up->port.membase; + uart->port.irq = up->port.irq; + uart->port.irqflags = up->port.irqflags; + uart->port.uartclk = up->port.uartclk; + uart->port.fifosize = up->port.fifosize; + uart->port.regshift = up->port.regshift; + uart->port.iotype = up->port.iotype; + uart->port.flags = up->port.flags | UPF_BOOT_AUTOCONF; + uart->bugs = up->bugs; + uart->port.mapbase = up->port.mapbase; + uart->port.mapsize = up->port.mapsize; + uart->port.private_data = up->port.private_data; + uart->tx_loadsz = up->tx_loadsz; + uart->capabilities = up->capabilities; + uart->port.throttle = up->port.throttle; + uart->port.unthrottle = up->port.unthrottle; + uart->port.rs485_config = up->port.rs485_config; + uart->port.rs485_supported = up->port.rs485_supported; + uart->port.rs485 = up->port.rs485; + uart->rs485_start_tx = up->rs485_start_tx; + uart->rs485_stop_tx = up->rs485_stop_tx; + uart->lsr_save_mask = up->lsr_save_mask; + uart->dma = up->dma; + + uart_set_cons_flow_enabled(&uart->port, uart_cons_flow_enabled(&up->port) | cons_flow); + + /* Take tx_loadsz from fifosize if it wasn't set separately */ + if (uart->port.fifosize && !uart->tx_loadsz) + uart->tx_loadsz = uart->port.fifosize; + + if (up->port.dev) { + uart->port.dev = up->port.dev; + ret = uart_get_rs485_mode(&uart->port); + if (ret) + goto err; + } + + if (up->port.flags & UPF_FIXED_TYPE) + uart->port.type = up->port.type; + + /* + * Only call mctrl_gpio_init(), if the device has no ACPI + * companion device + */ + if (uart->port.dev && !has_acpi_companion(uart->port.dev)) { + struct mctrl_gpios *gpios = mctrl_gpio_init(&uart->port, 0); + if (IS_ERR(gpios)) { + ret = PTR_ERR(gpios); + goto err; + } else { + uart->gpios = gpios; + } + } + + serial8250_set_defaults(uart); + + /* Possibly override default I/O functions. */ + if (up->port.serial_in) + uart->port.serial_in = up->port.serial_in; + if (up->port.serial_out) + uart->port.serial_out = up->port.serial_out; + if (up->port.handle_irq) + uart->port.handle_irq = up->port.handle_irq; + /* Possibly override set_termios call */ + if (up->port.set_termios) + uart->port.set_termios = up->port.set_termios; + if (up->port.set_ldisc) + uart->port.set_ldisc = up->port.set_ldisc; + if (up->port.get_mctrl) + uart->port.get_mctrl = up->port.get_mctrl; + if (up->port.set_mctrl) + uart->port.set_mctrl = up->port.set_mctrl; + if (up->port.get_divisor) + uart->port.get_divisor = up->port.get_divisor; + if (up->port.set_divisor) + uart->port.set_divisor = up->port.set_divisor; + if (up->port.get_rxtrig) + uart->port.get_rxtrig = up->port.get_rxtrig; + if (up->port.set_rxtrig) + uart->port.set_rxtrig = up->port.set_rxtrig; + if (up->port.startup) + uart->port.startup = up->port.startup; + if (up->port.shutdown) + uart->port.shutdown = up->port.shutdown; + if (up->port.break_ctl) + uart->port.break_ctl = up->port.break_ctl; + if (up->port.pm) + uart->port.pm = up->port.pm; + if (up->port.handle_break) + uart->port.handle_break = up->port.handle_break; + if (up->dl_read) + uart->dl_read = up->dl_read; + if (up->dl_write) + uart->dl_write = up->dl_write; + + /* Check the type (again)! It might have changed by the port.type assignment above. */ + if (uart->port.type != PORT_8250_CIR) { + if (uart_console_registered(&uart->port)) + pm_runtime_get_sync(uart->port.dev); + + if (serial8250_isa_config != NULL) + serial8250_isa_config(0, &uart->port, + &uart->capabilities); + + serial8250_apply_quirks(uart); + ret = uart_add_one_port(&serial8250_reg, + &uart->port); + if (ret) + goto err; + + ret = uart->port.line; + } else { + dev_info(uart->port.dev, + "skipping CIR port at 0x%lx / 0x%llx, IRQ %d\n", + uart->port.iobase, + (unsigned long long)uart->port.mapbase, + uart->port.irq); + + ret = 0; + } + + if (!uart->lsr_save_mask) + uart->lsr_save_mask = LSR_SAVE_FLAGS; /* Use default LSR mask */ + + /* Initialise interrupt backoff work if required */ + if (up->overrun_backoff_time_ms > 0) { + uart->overrun_backoff_time_ms = + up->overrun_backoff_time_ms; + INIT_DELAYED_WORK(&uart->overrun_backoff, + serial_8250_overrun_backoff_work); + } else { + uart->overrun_backoff_time_ms = 0; + } + + return ret; + +err: + uart->port.dev = NULL; + return ret; +} +EXPORT_SYMBOL(serial8250_register_8250_port); + +/** + * serial8250_unregister_port - remove a 16x50 serial port at runtime + * @line: serial line number + * + * Remove one serial port. This may not be called from interrupt + * context. We hand the port back to the our control. + */ +void serial8250_unregister_port(int line) +{ + struct uart_8250_port *uart = &serial8250_ports[line]; + + guard(mutex)(&serial_mutex); + + if (uart->em485) { + guard(uart_port_lock_irqsave)(&uart->port); + serial8250_em485_destroy(uart); + } + + uart_remove_one_port(&serial8250_reg, &uart->port); + if (serial8250_isa_devs) { + uart->port.flags &= ~UPF_BOOT_AUTOCONF; + uart->port.type = PORT_UNKNOWN; + uart->port.dev = &serial8250_isa_devs->dev; + uart->port.port_id = line; + uart->capabilities = 0; + serial8250_init_port(uart); + serial8250_apply_quirks(uart); + uart_add_one_port(&serial8250_reg, &uart->port); + } else { + uart->port.dev = NULL; + } +} +EXPORT_SYMBOL(serial8250_unregister_port); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 serial driver"); diff --git a/drivers/tty/serial/8250/8250_dfl.c b/drivers/tty/serial/8250/8250_dfl.c new file mode 100644 index 000000000..475ca3409 --- /dev/null +++ b/drivers/tty/serial/8250/8250_dfl.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for FPGA UART + * + * Copyright (C) 2022 Intel Corporation. + * + * Authors: + * Ananda Ravuri <ananda.ravuri@intel.com> + * Matthew Gerlach <matthew.gerlach@linux.intel.com> + */ + +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/dfl.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/types.h> + +#include <linux/serial.h> +#include <linux/serial_8250.h> + +#define DFHv1_PARAM_ID_CLK_FRQ 0x2 +#define DFHv1_PARAM_ID_FIFO_LEN 0x3 + +#define DFHv1_PARAM_ID_REG_LAYOUT 0x4 +#define DFHv1_PARAM_REG_LAYOUT_WIDTH GENMASK_ULL(63, 32) +#define DFHv1_PARAM_REG_LAYOUT_SHIFT GENMASK_ULL(31, 0) + +struct dfl_uart { + int line; +}; + +static int dfh_get_u64_param_val(struct dfl_device *dfl_dev, int param_id, u64 *pval) +{ + size_t psize; + u64 *p; + + p = dfh_find_param(dfl_dev, param_id, &psize); + if (IS_ERR(p)) + return PTR_ERR(p); + + if (psize != sizeof(*pval)) + return -EINVAL; + + *pval = *p; + + return 0; +} + +static int dfl_uart_get_params(struct dfl_device *dfl_dev, struct uart_8250_port *uart) +{ + struct device *dev = &dfl_dev->dev; + u64 fifo_len, clk_freq, reg_layout; + u32 reg_width; + int ret; + + ret = dfh_get_u64_param_val(dfl_dev, DFHv1_PARAM_ID_CLK_FRQ, &clk_freq); + if (ret) + return dev_err_probe(dev, ret, "missing CLK_FRQ param\n"); + + uart->port.uartclk = clk_freq; + + ret = dfh_get_u64_param_val(dfl_dev, DFHv1_PARAM_ID_FIFO_LEN, &fifo_len); + if (ret) + return dev_err_probe(dev, ret, "missing FIFO_LEN param\n"); + + switch (fifo_len) { + case 32: + uart->port.type = PORT_ALTR_16550_F32; + break; + + case 64: + uart->port.type = PORT_ALTR_16550_F64; + break; + + case 128: + uart->port.type = PORT_ALTR_16550_F128; + break; + + default: + return dev_err_probe(dev, -EINVAL, "unsupported FIFO_LEN %llu\n", fifo_len); + } + + ret = dfh_get_u64_param_val(dfl_dev, DFHv1_PARAM_ID_REG_LAYOUT, ®_layout); + if (ret) + return dev_err_probe(dev, ret, "missing REG_LAYOUT param\n"); + + uart->port.regshift = FIELD_GET(DFHv1_PARAM_REG_LAYOUT_SHIFT, reg_layout); + reg_width = FIELD_GET(DFHv1_PARAM_REG_LAYOUT_WIDTH, reg_layout); + switch (reg_width) { + case 4: + uart->port.iotype = UPIO_MEM32; + break; + + case 2: + uart->port.iotype = UPIO_MEM16; + break; + + default: + return dev_err_probe(dev, -EINVAL, "unsupported reg-width %u\n", reg_width); + + } + + return 0; +} + +static int dfl_uart_probe(struct dfl_device *dfl_dev) +{ + struct device *dev = &dfl_dev->dev; + struct uart_8250_port uart = { }; + struct dfl_uart *dfluart; + int ret; + + uart.port.flags = UPF_IOREMAP; + uart.port.mapbase = dfl_dev->mmio_res.start; + uart.port.mapsize = resource_size(&dfl_dev->mmio_res); + + ret = dfl_uart_get_params(dfl_dev, &uart); + if (ret < 0) + return dev_err_probe(dev, ret, "failed uart feature walk\n"); + + if (dfl_dev->num_irqs == 1) + uart.port.irq = dfl_dev->irqs[0]; + + dfluart = devm_kzalloc(dev, sizeof(*dfluart), GFP_KERNEL); + if (!dfluart) + return -ENOMEM; + + dfluart->line = serial8250_register_8250_port(&uart); + if (dfluart->line < 0) + return dev_err_probe(dev, dfluart->line, "unable to register 8250 port.\n"); + + dev_set_drvdata(dev, dfluart); + + return 0; +} + +static void dfl_uart_remove(struct dfl_device *dfl_dev) +{ + struct dfl_uart *dfluart = dev_get_drvdata(&dfl_dev->dev); + + serial8250_unregister_port(dfluart->line); +} + +#define FME_FEATURE_ID_UART 0x24 + +static const struct dfl_device_id dfl_uart_ids[] = { + { FME_ID, FME_FEATURE_ID_UART }, + { } +}; +MODULE_DEVICE_TABLE(dfl, dfl_uart_ids); + +static struct dfl_driver dfl_uart_driver = { + .drv = { + .name = "dfl-uart", + }, + .id_table = dfl_uart_ids, + .probe = dfl_uart_probe, + .remove = dfl_uart_remove, +}; +module_dfl_driver(dfl_uart_driver); + +MODULE_DESCRIPTION("DFL Intel UART driver"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c new file mode 100644 index 000000000..5a83e5269 --- /dev/null +++ b/drivers/tty/serial/8250/8250_dma.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * 8250_dma.c - DMA Engine API support for 8250.c + * + * Copyright (C) 2013 Intel Corporation + */ +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_reg.h> +#include <linux/dma-mapping.h> + +#include "8250.h" + +static void __dma_tx_complete(void *param) +{ + struct uart_8250_port *p = param; + struct uart_8250_dma *dma = p->dma; + struct tty_port *tport = &p->port.state->port; + unsigned long flags; + int ret; + + dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + uart_port_lock_irqsave(&p->port, &flags); + + dma->tx_running = 0; + + uart_xmit_advance(&p->port, dma->tx_size); + + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) + uart_write_wakeup(&p->port); + + ret = serial8250_tx_dma(p); + if (ret || !dma->tx_running) + serial8250_set_THRI(p); + + uart_port_unlock_irqrestore(&p->port, flags); +} + +static void __dma_rx_complete(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + struct tty_port *tty_port = &p->port.state->port; + struct dma_tx_state state; + enum dma_status dma_status; + int count; + + /* + * New DMA Rx can be started during the completion handler before it + * could acquire port's lock and it might still be ongoing. Don't to + * anything in such case. + */ + dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + if (dma_status == DMA_IN_PROGRESS) + return; + + count = dma->rx_size - state.residue; + + tty_insert_flip_string(tty_port, dma->rx_buf, count); + p->port.icount.rx += count; + dma->rx_running = 0; + + tty_flip_buffer_push(tty_port); +} + +static void dma_rx_complete(void *param) +{ + struct uart_8250_port *p = param; + struct uart_8250_dma *dma = p->dma; + unsigned long flags; + + uart_port_lock_irqsave(&p->port, &flags); + if (dma->rx_running) + __dma_rx_complete(p); + + /* + * Cannot be combined with the previous check because __dma_rx_complete() + * changes dma->rx_running. + */ + if (!dma->rx_running && (serial_lsr_in(p) & UART_LSR_DR)) + p->dma->rx_dma(p); + uart_port_unlock_irqrestore(&p->port, flags); +} + +int serial8250_tx_dma(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + struct tty_port *tport = &p->port.state->port; + struct dma_async_tx_descriptor *desc; + struct uart_port *up = &p->port; + struct scatterlist *sg; + struct scatterlist sgl[2]; + int i; + int ret; + + if (dma->tx_running) { + if (up->x_char) { + dmaengine_pause(dma->txchan); + uart_xchar_out(up, UART_TX); + dmaengine_resume(dma->txchan); + } + return 0; + } else if (up->x_char) { + uart_xchar_out(up, UART_TX); + } + + if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) { + /* We have been called from __dma_tx_complete() */ + return 0; + } + + serial8250_do_prepare_tx_dma(p); + + sg_init_table(sgl, ARRAY_SIZE(sgl)); + + ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, sgl, ARRAY_SIZE(sgl), + UART_XMIT_SIZE, dma->tx_addr); + + dma->tx_size = 0; + + for_each_sg(sgl, sg, ret, i) + dma->tx_size += sg_dma_len(sg); + + desc = dmaengine_prep_slave_sg(dma->txchan, sgl, ret, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + ret = -EBUSY; + goto err; + } + + dma->tx_running = 1; + desc->callback = __dma_tx_complete; + desc->callback_param = p; + + dma->tx_cookie = dmaengine_submit(desc); + + dma_sync_single_for_device(dma->txchan->device->dev, dma->tx_addr, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + dma_async_issue_pending(dma->txchan); + serial8250_clear_THRI(p); + dma->tx_err = 0; + + return 0; +err: + dma->tx_err = 1; + return ret; +} + +void serial8250_tx_dma_flush(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + + if (!dma->tx_running) + return; + + /* + * kfifo_reset() has been called by the serial core, avoid + * advancing and underflowing in __dma_tx_complete(). + */ + dma->tx_size = 0; + + /* + * We can't use `dmaengine_terminate_sync` because `uart_flush_buffer` is + * holding the uart port spinlock. + */ + dmaengine_terminate_async(dma->txchan); + + /* + * The callback might or might not run. If it doesn't run, we need to ensure + * that `tx_running` is cleared so that we can schedule new transactions. + * If it does run, then the zombie callback will clear `tx_running` again + * and perform a no-op since `tx_size` was cleared above. + * + * In either case, we ASSUME the DMA transaction will terminate before we + * issue a new `serial8250_tx_dma`. + */ + dma->tx_running = 0; +} + +int serial8250_rx_dma(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + struct dma_async_tx_descriptor *desc; + + if (dma->rx_running) + return 0; + + serial8250_do_prepare_rx_dma(p); + + desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, + dma->rx_size, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -EBUSY; + + dma->rx_running = 1; + desc->callback = dma_rx_complete; + desc->callback_param = p; + + dma->rx_cookie = dmaengine_submit(desc); + + dma_async_issue_pending(dma->rxchan); + + return 0; +} + +void serial8250_rx_dma_flush(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + + if (!dma || !dma->rxchan || !dma->rx_running) + return; + + dmaengine_pause(dma->rxchan); + __dma_rx_complete(p); + dmaengine_terminate_async(dma->rxchan); +} +EXPORT_SYMBOL_GPL(serial8250_rx_dma_flush); + +int serial8250_request_dma(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + phys_addr_t rx_dma_addr = dma->rx_dma_addr ? + dma->rx_dma_addr : p->port.mapbase; + phys_addr_t tx_dma_addr = dma->tx_dma_addr ? + dma->tx_dma_addr : p->port.mapbase; + dma_cap_mask_t mask; + struct dma_slave_caps caps; + int ret; + + /* Default slave configuration parameters */ + dma->rxconf.direction = DMA_DEV_TO_MEM; + dma->rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma->rxconf.src_addr = rx_dma_addr + UART_RX; + + dma->txconf.direction = DMA_MEM_TO_DEV; + dma->txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma->txconf.dst_addr = tx_dma_addr + UART_TX; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + /* Get a channel for RX */ + dma->rxchan = dma_request_slave_channel_compat(mask, + dma->fn, dma->rx_param, + p->port.dev, "rx"); + if (!dma->rxchan) + return -ENODEV; + + /* 8250 rx dma requires dmaengine driver to support pause/terminate */ + ret = dma_get_slave_caps(dma->rxchan, &caps); + if (ret) + goto release_rx; + if (!caps.cmd_pause || !caps.cmd_terminate || + caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR) { + ret = -EINVAL; + goto release_rx; + } + + dmaengine_slave_config(dma->rxchan, &dma->rxconf); + + /* Get a channel for TX */ + dma->txchan = dma_request_slave_channel_compat(mask, + dma->fn, dma->tx_param, + p->port.dev, "tx"); + if (!dma->txchan) { + ret = -ENODEV; + goto release_rx; + } + + /* 8250 tx dma requires dmaengine driver to support terminate */ + ret = dma_get_slave_caps(dma->txchan, &caps); + if (ret) + goto err; + if (!caps.cmd_terminate) { + ret = -EINVAL; + goto err; + } + + dmaengine_slave_config(dma->txchan, &dma->txconf); + + /* RX buffer */ + if (!dma->rx_size) + dma->rx_size = PAGE_SIZE; + + dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size, + &dma->rx_addr, GFP_KERNEL); + if (!dma->rx_buf) { + ret = -ENOMEM; + goto err; + } + + /* TX buffer */ + dma->tx_addr = dma_map_single(dma->txchan->device->dev, + p->port.state->port.xmit_buf, + UART_XMIT_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) { + dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, + dma->rx_buf, dma->rx_addr); + ret = -ENOMEM; + goto err; + } + + dev_dbg_ratelimited(p->port.dev, "got both dma channels\n"); + + return 0; +err: + dma_release_channel(dma->txchan); +release_rx: + dma_release_channel(dma->rxchan); + return ret; +} +EXPORT_SYMBOL_GPL(serial8250_request_dma); + +void serial8250_release_dma(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + + if (!dma) + return; + + /* Release RX resources */ + dmaengine_terminate_sync(dma->rxchan); + dma->rx_running = 0; + dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, dma->rx_buf, + dma->rx_addr); + dma_release_channel(dma->rxchan); + dma->rxchan = NULL; + + /* Release TX resources */ + dmaengine_terminate_sync(dma->txchan); + dma_unmap_single(dma->txchan->device->dev, dma->tx_addr, + UART_XMIT_SIZE, DMA_TO_DEVICE); + dma_release_channel(dma->txchan); + dma->txchan = NULL; + dma->tx_running = 0; + + dev_dbg_ratelimited(p->port.dev, "dma channels released\n"); +} +EXPORT_SYMBOL_GPL(serial8250_release_dma); diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c new file mode 100644 index 000000000..51d026f20 --- /dev/null +++ b/drivers/tty/serial/8250/8250_dw.c @@ -0,0 +1,943 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Synopsys DesignWare 8250 driver. + * + * Copyright 2011 Picochip, Jamie Iles. + * Copyright 2013 Intel Corporation + * + * The Synopsys DesignWare 8250 has an extra feature whereby it detects if the + * LCR is written whilst busy. If it is, then a busy detect interrupt is + * raised, the LCR needs to be rewritten and the uart status register read. + */ +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/lockdep.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include <asm/byteorder.h> + +#include <linux/serial_8250.h> +#include <linux/serial_reg.h> + +#include "8250_dwlib.h" + +#define OCTEON_UART_USR 0x27 /* UART Status Register */ + +#define RZN1_UART_TDMACR 0x10c /* DMA Control Register Transmit Mode */ +#define RZN1_UART_RDMACR 0x110 /* DMA Control Register Receive Mode */ + +/* Renesas specific register fields */ +#define RZN1_UART_xDMACR_DMA_EN BIT(0) +#define RZN1_UART_xDMACR_1_WORD_BURST (0 << 1) +#define RZN1_UART_xDMACR_4_WORD_BURST (1 << 1) +#define RZN1_UART_xDMACR_8_WORD_BURST (2 << 1) +#define RZN1_UART_xDMACR_BLK_SZ(x) ((x) << 3) + +/* Quirks */ +#define DW_UART_QUIRK_OCTEON BIT(0) +#define DW_UART_QUIRK_ARMADA_38X BIT(1) +#define DW_UART_QUIRK_SKIP_SET_RATE BIT(2) +#define DW_UART_QUIRK_IS_DMA_FC BIT(3) +#define DW_UART_QUIRK_APMC0D08 BIT(4) +#define DW_UART_QUIRK_CPR_VALUE BIT(5) +#define DW_UART_QUIRK_IER_KICK BIT(6) + +/* + * Number of consecutive IIR_NO_INT interrupts required to trigger interrupt + * storm prevention code. + */ +#define DW_UART_QUIRK_IER_KICK_THRES 4 + +struct dw8250_platform_data { + u8 usr_reg; + u32 cpr_value; + unsigned int quirks; +}; + +struct dw8250_data { + struct dw8250_port_data data; + const struct dw8250_platform_data *pdata; + + u32 msr_mask_on; + u32 msr_mask_off; + struct clk *clk; + struct clk *pclk; + struct reset_control *rst; + + unsigned int skip_autocfg:1; + unsigned int uart_16550_compatible:1; + unsigned int in_idle:1; + + u8 no_int_count; +}; + +static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data) +{ + return container_of(data, struct dw8250_data, data); +} + +static inline u32 dw8250_modify_msr(struct uart_port *p, unsigned int offset, u32 value) +{ + struct dw8250_data *d = to_dw8250_data(p->private_data); + + /* Override any modem control signals if needed */ + if (offset == UART_MSR) { + value |= d->msr_mask_on; + value &= ~d->msr_mask_off; + } + + return value; +} + +static void dw8250_idle_exit(struct uart_port *p) +{ + struct dw8250_data *d = to_dw8250_data(p->private_data); + struct uart_8250_port *up = up_to_u8250p(p); + + if (d->uart_16550_compatible) + return; + + if (up->capabilities & UART_CAP_FIFO) + serial_port_out(p, UART_FCR, up->fcr); + serial_port_out(p, UART_MCR, up->mcr); + serial_port_out(p, UART_IER, up->ier); + + /* DMA Rx is restarted by IRQ handler as needed. */ + if (up->dma) + serial8250_tx_dma_resume(up); + + d->in_idle = 0; +} + +/* + * Ensure BUSY is not asserted. If DW UART is configured with + * !uart_16550_compatible, the writes to LCR, DLL, and DLH fail while + * BUSY is asserted. + * + * Context: port's lock must be held + */ +static int dw8250_idle_enter(struct uart_port *p) +{ + struct dw8250_data *d = to_dw8250_data(p->private_data); + unsigned int usr_reg = d->pdata ? d->pdata->usr_reg : DW_UART_USR; + struct uart_8250_port *up = up_to_u8250p(p); + int retries; + u32 lsr; + + lockdep_assert_held_once(&p->lock); + + if (d->uart_16550_compatible) + return 0; + + d->in_idle = 1; + + /* Prevent triggering interrupt from RBR filling */ + serial_port_out(p, UART_IER, 0); + + if (up->dma) { + serial8250_rx_dma_flush(up); + if (serial8250_tx_dma_running(up)) + serial8250_tx_dma_pause(up); + } + + /* + * Wait until Tx becomes empty + one extra frame time to ensure all bits + * have been sent on the wire. + * + * FIXME: frame_time delay is too long with very low baudrates. + */ + serial8250_fifo_wait_for_lsr_thre(up, NULL, p->fifosize); + ndelay(p->frame_time); + + serial_port_out(p, UART_MCR, up->mcr | UART_MCR_LOOP); + + retries = 4; /* Arbitrary limit, 2 was always enough in tests */ + do { + serial8250_clear_fifos(up); + if (!(serial_port_in(p, usr_reg) & DW_UART_USR_BUSY)) + break; + /* FIXME: frame_time delay is too long with very low baudrates. */ + ndelay(p->frame_time); + } while (--retries); + + lsr = serial_lsr_in(up); + if (lsr & UART_LSR_DR) { + serial_port_in(p, UART_RX); + up->lsr_saved_flags = 0; + } + + /* Now guaranteed to have BUSY deasserted? Just sanity check */ + if (serial_port_in(p, usr_reg) & DW_UART_USR_BUSY) { + dw8250_idle_exit(p); + return -EBUSY; + } + + return 0; +} + +static void dw8250_set_divisor(struct uart_port *p, unsigned int baud, + unsigned int quot, unsigned int quot_frac) +{ + struct uart_8250_port *up = up_to_u8250p(p); + int ret; + + ret = dw8250_idle_enter(p); + if (ret < 0) + return; + + serial_port_out(p, UART_LCR, up->lcr | UART_LCR_DLAB); + if (!(serial_port_in(p, UART_LCR) & UART_LCR_DLAB)) + goto idle_failed; + + serial_dl_write(up, quot); + serial_port_out(p, UART_LCR, up->lcr); + +idle_failed: + dw8250_idle_exit(p); +} + +/* + * This function is being called as part of the uart_port::serial_out() + * routine. Hence, special care must be taken when serial_port_out() or + * serial_out() against the modified registers here, i.e. LCR (d->in_idle is + * used to break recursion loop). + */ +static void dw8250_check_lcr(struct uart_port *p, unsigned int offset, u32 value) +{ + struct dw8250_data *d = to_dw8250_data(p->private_data); + u32 lcr; + int ret; + + if (offset != UART_LCR || d->uart_16550_compatible) + return; + + lcr = serial_port_in(p, UART_LCR); + + /* Make sure LCR write wasn't ignored */ + if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) + return; + + if (d->in_idle) + goto write_err; + + ret = dw8250_idle_enter(p); + if (ret < 0) + goto write_err; + + serial_port_out(p, UART_LCR, value); + dw8250_idle_exit(p); + return; + +write_err: + /* + * FIXME: this deadlocks if port->lock is already held + * dev_err(p->dev, "Couldn't set LCR to %d\n", value); + */ + return; /* Silences "label at the end of compound statement" */ +} + +/* + * With BUSY, LCR writes can be very expensive (IRQ + complex retry logic). + * If the write does not change the value of the LCR register, skip it entirely. + */ +static bool dw8250_can_skip_reg_write(struct uart_port *p, unsigned int offset, u32 value) +{ + struct dw8250_data *d = to_dw8250_data(p->private_data); + u32 lcr; + + if (offset != UART_LCR || d->uart_16550_compatible) + return false; + + lcr = serial_port_in(p, offset); + return lcr == value; +} + +/* Returns once the transmitter is empty or we run out of retries */ +static void dw8250_tx_wait_empty(struct uart_port *p) +{ + struct uart_8250_port *up = up_to_u8250p(p); + unsigned int tries = 20000; + unsigned int delay_threshold = tries - 1000; + unsigned int lsr; + + while (tries--) { + lsr = readb (p->membase + (UART_LSR << p->regshift)); + up->lsr_saved_flags |= lsr & up->lsr_save_mask; + + if (lsr & UART_LSR_TEMT) + break; + + /* The device is first given a chance to empty without delay, + * to avoid slowdowns at high bitrates. If after 1000 tries + * the buffer has still not emptied, allow more time for low- + * speed links. */ + if (tries < delay_threshold) + udelay (1); + } +} + +static void dw8250_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ + if (dw8250_can_skip_reg_write(p, offset, value)) + return; + + writeb(value, p->membase + (offset << p->regshift)); + dw8250_check_lcr(p, offset, value); +} + +static void dw8250_serial_out38x(struct uart_port *p, unsigned int offset, u32 value) +{ + if (dw8250_can_skip_reg_write(p, offset, value)) + return; + + /* Allow the TX to drain before we reconfigure */ + if (offset == UART_LCR) + dw8250_tx_wait_empty(p); + + dw8250_serial_out(p, offset, value); +} + +static u32 dw8250_serial_in(struct uart_port *p, unsigned int offset) +{ + u32 value = readb(p->membase + (offset << p->regshift)); + + return dw8250_modify_msr(p, offset, value); +} + +#ifdef CONFIG_64BIT +static u32 dw8250_serial_inq(struct uart_port *p, unsigned int offset) +{ + u8 value = __raw_readq(p->membase + (offset << p->regshift)); + + return dw8250_modify_msr(p, offset, value); +} + +static void dw8250_serial_outq(struct uart_port *p, unsigned int offset, u32 value) +{ + if (dw8250_can_skip_reg_write(p, offset, value)) + return; + + value &= 0xff; + __raw_writeq(value, p->membase + (offset << p->regshift)); + /* Read back to ensure register write ordering. */ + __raw_readq(p->membase + (UART_LCR << p->regshift)); + + dw8250_check_lcr(p, offset, value); +} +#endif /* CONFIG_64BIT */ + +static void dw8250_serial_out32(struct uart_port *p, unsigned int offset, u32 value) +{ + if (dw8250_can_skip_reg_write(p, offset, value)) + return; + + writel(value, p->membase + (offset << p->regshift)); + dw8250_check_lcr(p, offset, value); +} + +static u32 dw8250_serial_in32(struct uart_port *p, unsigned int offset) +{ + u32 value = readl(p->membase + (offset << p->regshift)); + + return dw8250_modify_msr(p, offset, value); +} + +static void dw8250_serial_out32be(struct uart_port *p, unsigned int offset, u32 value) +{ + if (dw8250_can_skip_reg_write(p, offset, value)) + return; + + iowrite32be(value, p->membase + (offset << p->regshift)); + dw8250_check_lcr(p, offset, value); +} + +static u32 dw8250_serial_in32be(struct uart_port *p, unsigned int offset) +{ + u32 value = ioread32be(p->membase + (offset << p->regshift)); + + return dw8250_modify_msr(p, offset, value); +} + +/* + * INTC10EE UART can IRQ storm while reporting IIR_NO_INT. Inducing IIR value + * change has been observed to break the storm. + * + * If Tx is empty (THRE asserted), we use here IER_THRI to cause IIR_NO_INT -> + * IIR_THRI transition. + */ +static void dw8250_quirk_ier_kick(struct uart_port *p) +{ + struct uart_8250_port *up = up_to_u8250p(p); + u32 lsr; + + if (up->ier & UART_IER_THRI) + return; + + lsr = serial_lsr_in(up); + if (!(lsr & UART_LSR_THRE)) + return; + + serial_port_out(p, UART_IER, up->ier | UART_IER_THRI); + serial_port_in(p, UART_LCR); /* safe, no side-effects */ + serial_port_out(p, UART_IER, up->ier); +} + +static int dw8250_handle_irq(struct uart_port *p) +{ + struct uart_8250_port *up = up_to_u8250p(p); + struct dw8250_data *d = to_dw8250_data(p->private_data); + unsigned int iir = serial_port_in(p, UART_IIR); + bool rx_timeout = (iir & 0x3f) == UART_IIR_RX_TIMEOUT; + unsigned int quirks = d->pdata->quirks; + unsigned int status; + + guard(uart_port_lock_check_sysrq_irqsave)(p); + + switch (FIELD_GET(DW_UART_IIR_IID, iir)) { + case UART_IIR_NO_INT: + if (d->uart_16550_compatible || up->dma) + return 0; + + if (quirks & DW_UART_QUIRK_IER_KICK && + d->no_int_count == (DW_UART_QUIRK_IER_KICK_THRES - 1)) + dw8250_quirk_ier_kick(p); + d->no_int_count = (d->no_int_count + 1) % DW_UART_QUIRK_IER_KICK_THRES; + + return 0; + + case UART_IIR_BUSY: + /* Clear the USR */ + serial_port_in(p, d->pdata->usr_reg); + + d->no_int_count = 0; + + return 1; + } + + d->no_int_count = 0; + + /* + * There are ways to get Designware-based UARTs into a state where + * they are asserting UART_IIR_RX_TIMEOUT but there is no actual + * data available. If we see such a case then we'll do a bogus + * read. If we don't do this then the "RX TIMEOUT" interrupt will + * fire forever. + * + * This problem has only been observed so far when not in DMA mode + * so we limit the workaround only to non-DMA mode. + */ + if (!up->dma && rx_timeout) { + status = serial_lsr_in(up); + + if (!(status & (UART_LSR_DR | UART_LSR_BI))) + serial_port_in(p, UART_RX); + } + + /* Manually stop the Rx DMA transfer when acting as flow controller */ + if (quirks & DW_UART_QUIRK_IS_DMA_FC && up->dma && up->dma->rx_running && rx_timeout) { + status = serial_lsr_in(up); + + if (status & (UART_LSR_DR | UART_LSR_BI)) { + dw8250_writel_ext(p, RZN1_UART_RDMACR, 0); + dw8250_writel_ext(p, DW_UART_DMASA, 1); + } + } + + serial8250_handle_irq_locked(p, iir); + + return 1; +} + +static void +dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) +{ + if (!state) + pm_runtime_get_sync(port->dev); + + serial8250_do_pm(port, state, old); + + if (state) + pm_runtime_put_sync_suspend(port->dev); +} + +static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, + const struct ktermios *old) +{ + unsigned long newrate = tty_termios_baud_rate(termios) * 16; + struct dw8250_data *d = to_dw8250_data(p->private_data); + long rate; + int ret; + + clk_disable_unprepare(d->clk); + rate = clk_round_rate(d->clk, newrate); + if (rate > 0) { + ret = clk_set_rate(d->clk, newrate); + if (!ret) + p->uartclk = rate; + } + clk_prepare_enable(d->clk); + + dw8250_do_set_termios(p, termios, old); +} + +static void dw8250_set_ldisc(struct uart_port *p, struct ktermios *termios) +{ + struct uart_8250_port *up = up_to_u8250p(p); + unsigned int mcr = serial_port_in(p, UART_MCR); + + if (up->capabilities & UART_CAP_IRDA) { + if (termios->c_line == N_IRDA) + mcr |= DW_UART_MCR_SIRE; + else + mcr &= ~DW_UART_MCR_SIRE; + + serial_port_out(p, UART_MCR, mcr); + } + serial8250_do_set_ldisc(p, termios); +} + +/* + * dw8250_fallback_dma_filter will prevent the UART from getting just any free + * channel on platforms that have DMA engines, but don't have any channels + * assigned to the UART. + * + * REVISIT: This is a work around for limitation in the DMA Engine API. Once the + * core problem is fixed, this function is no longer needed. + */ +static bool dw8250_fallback_dma_filter(struct dma_chan *chan, void *param) +{ + return false; +} + +static bool dw8250_idma_filter(struct dma_chan *chan, void *param) +{ + return param == chan->device->dev; +} + +static void dw8250_setup_dma_filter(struct uart_port *p, struct dw8250_data *data) +{ + /* Platforms with iDMA 64-bit */ + if (platform_get_resource_byname(to_platform_device(p->dev), IORESOURCE_MEM, "lpss_priv")) { + data->data.dma.rx_param = p->dev->parent; + data->data.dma.tx_param = p->dev->parent; + data->data.dma.fn = dw8250_idma_filter; + } else { + data->data.dma.fn = dw8250_fallback_dma_filter; + } +} + +static u32 dw8250_rzn1_get_dmacr_burst(int max_burst) +{ + if (max_burst >= 8) + return RZN1_UART_xDMACR_8_WORD_BURST; + else if (max_burst >= 4) + return RZN1_UART_xDMACR_4_WORD_BURST; + else + return RZN1_UART_xDMACR_1_WORD_BURST; +} + +static void dw8250_prepare_tx_dma(struct uart_8250_port *p) +{ + struct uart_port *up = &p->port; + struct uart_8250_dma *dma = p->dma; + u32 val; + + dw8250_writel_ext(up, RZN1_UART_TDMACR, 0); + val = dw8250_rzn1_get_dmacr_burst(dma->txconf.dst_maxburst) | + RZN1_UART_xDMACR_BLK_SZ(dma->tx_size) | + RZN1_UART_xDMACR_DMA_EN; + dw8250_writel_ext(up, RZN1_UART_TDMACR, val); +} + +static void dw8250_prepare_rx_dma(struct uart_8250_port *p) +{ + struct uart_port *up = &p->port; + struct uart_8250_dma *dma = p->dma; + u32 val; + + dw8250_writel_ext(up, RZN1_UART_RDMACR, 0); + val = dw8250_rzn1_get_dmacr_burst(dma->rxconf.src_maxburst) | + RZN1_UART_xDMACR_BLK_SZ(dma->rx_size) | + RZN1_UART_xDMACR_DMA_EN; + dw8250_writel_ext(up, RZN1_UART_RDMACR, val); +} + +static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) +{ + unsigned int quirks = data->pdata->quirks; + u32 cpr_value = data->pdata->cpr_value; + + if (quirks & DW_UART_QUIRK_CPR_VALUE) + data->data.cpr_value = cpr_value; + +#ifdef CONFIG_64BIT + if (quirks & DW_UART_QUIRK_OCTEON) { + p->serial_in = dw8250_serial_inq; + p->serial_out = dw8250_serial_outq; + p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; + p->type = PORT_OCTEON; + data->skip_autocfg = true; + } +#endif + + if (quirks & DW_UART_QUIRK_ARMADA_38X) + p->serial_out = dw8250_serial_out38x; + if (quirks & DW_UART_QUIRK_SKIP_SET_RATE) + p->set_termios = dw8250_do_set_termios; + if (quirks & DW_UART_QUIRK_IS_DMA_FC) { + data->data.dma.txconf.device_fc = 1; + data->data.dma.rxconf.device_fc = 1; + data->data.dma.prepare_tx_dma = dw8250_prepare_tx_dma; + data->data.dma.prepare_rx_dma = dw8250_prepare_rx_dma; + } + if (quirks & DW_UART_QUIRK_APMC0D08) { + p->iotype = UPIO_MEM32; + p->regshift = 2; + p->serial_in = dw8250_serial_in32; + data->uart_16550_compatible = true; + } +} + +static void dw8250_reset_control_assert(void *data) +{ + reset_control_assert(data); +} + +static void dw8250_shutdown(struct uart_port *port) +{ + struct dw8250_data *d = to_dw8250_data(port->private_data); + + serial8250_do_shutdown(port); + d->no_int_count = 0; +} + +static int dw8250_probe(struct platform_device *pdev) +{ + struct uart_8250_port uart = {}, *up = &uart; + struct uart_port *p = &up->port; + struct device *dev = &pdev->dev; + struct dw8250_data *data; + struct resource *regs; + int err; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return dev_err_probe(dev, -EINVAL, "no registers defined\n"); + + spin_lock_init(&p->lock); + p->pm = dw8250_do_pm; + p->type = PORT_8250; + p->flags = UPF_FIXED_PORT; + p->dev = dev; + + p->set_ldisc = dw8250_set_ldisc; + p->set_termios = dw8250_set_termios; + p->set_divisor = dw8250_set_divisor; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + p->private_data = &data->data; + + p->mapbase = regs->start; + p->mapsize = resource_size(regs); + + p->membase = devm_ioremap(dev, p->mapbase, p->mapsize); + if (!p->membase) + return -ENOMEM; + + err = uart_read_port_properties(p); + /* no interrupt -> fall back to polling */ + if (err == -ENXIO) + err = 0; + if (err) + return err; + + switch (p->iotype) { + case UPIO_MEM: + p->serial_in = dw8250_serial_in; + p->serial_out = dw8250_serial_out; + break; + case UPIO_MEM32: + p->serial_in = dw8250_serial_in32; + p->serial_out = dw8250_serial_out32; + break; + case UPIO_MEM32BE: + p->serial_in = dw8250_serial_in32be; + p->serial_out = dw8250_serial_out32be; + break; + default: + return -ENODEV; + } + + if (device_property_read_bool(dev, "dcd-override")) { + /* Always report DCD as active */ + data->msr_mask_on |= UART_MSR_DCD; + data->msr_mask_off |= UART_MSR_DDCD; + } + + if (device_property_read_bool(dev, "dsr-override")) { + /* Always report DSR as active */ + data->msr_mask_on |= UART_MSR_DSR; + data->msr_mask_off |= UART_MSR_DDSR; + } + + if (device_property_read_bool(dev, "cts-override")) { + /* Always report CTS as active */ + data->msr_mask_on |= UART_MSR_CTS; + data->msr_mask_off |= UART_MSR_DCTS; + } + + if (device_property_read_bool(dev, "ri-override")) { + /* Always report Ring indicator as inactive */ + data->msr_mask_off |= UART_MSR_RI; + data->msr_mask_off |= UART_MSR_TERI; + } + + /* If there is separate baudclk, get the rate from it. */ + data->clk = devm_clk_get_optional_enabled(dev, "baudclk"); + if (data->clk == NULL) + data->clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(data->clk)) + return dev_err_probe(dev, PTR_ERR(data->clk), + "failed to get baudclk\n"); + + if (data->clk) + p->uartclk = clk_get_rate(data->clk); + + /* If no clock rate is defined, fail. */ + if (!p->uartclk) + return dev_err_probe(dev, -EINVAL, "clock rate not defined\n"); + + data->pclk = devm_clk_get_optional_enabled(dev, "apb_pclk"); + if (IS_ERR(data->pclk)) + return PTR_ERR(data->pclk); + + data->rst = devm_reset_control_array_get_optional_exclusive(dev); + if (IS_ERR(data->rst)) + return PTR_ERR(data->rst); + + err = reset_control_deassert(data->rst); + if (err) + return dev_err_probe(dev, err, "failed to deassert resets\n"); + + err = devm_add_action_or_reset(dev, dw8250_reset_control_assert, data->rst); + if (err) + return err; + + err = pm_runtime_set_active(dev); + if (err) + return dev_err_probe(dev, err, "Failed to set the runtime suspend as active\n"); + + data->uart_16550_compatible = device_property_read_bool(dev, "snps,uart-16550-compatible"); + + data->pdata = device_get_match_data(p->dev); + if (data->pdata) + dw8250_quirks(p, data); + + /* If the Busy Functionality is not implemented, don't handle it */ + if (data->uart_16550_compatible) { + p->handle_irq = NULL; + } else if (data->pdata) { + p->handle_irq = dw8250_handle_irq; + p->shutdown = dw8250_shutdown; + } + + dw8250_setup_dma_filter(p, data); + + if (!data->skip_autocfg) + dw8250_setup_port(p); + + /* If we have a valid fifosize, try hooking up DMA */ + if (p->fifosize) { + data->data.dma.rxconf.src_maxburst = p->fifosize / 4; + data->data.dma.txconf.dst_maxburst = p->fifosize / 4; + up->dma = &data->data.dma; + } + + data->data.line = serial8250_register_8250_port(up); + if (data->data.line < 0) + return data->data.line; + + platform_set_drvdata(pdev, data); + + pm_runtime_enable(dev); + + return 0; +} + +static void dw8250_remove(struct platform_device *pdev) +{ + struct dw8250_data *data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + pm_runtime_get_sync(dev); + + serial8250_unregister_port(data->data.line); + + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); +} + +static int dw8250_suspend(struct device *dev) +{ + struct dw8250_data *data = dev_get_drvdata(dev); + + serial8250_suspend_port(data->data.line); + + return 0; +} + +static int dw8250_resume(struct device *dev) +{ + struct dw8250_data *data = dev_get_drvdata(dev); + + serial8250_resume_port(data->data.line); + + return 0; +} + +static int dw8250_runtime_suspend(struct device *dev) +{ + struct dw8250_data *data = dev_get_drvdata(dev); + + clk_disable_unprepare(data->clk); + + clk_disable_unprepare(data->pclk); + + return 0; +} + +static int dw8250_runtime_resume(struct device *dev) +{ + int ret; + struct dw8250_data *data = dev_get_drvdata(dev); + + ret = clk_prepare_enable(data->pclk); + if (ret) + return ret; + + ret = clk_prepare_enable(data->clk); + if (ret) { + clk_disable_unprepare(data->pclk); + return ret; + } + + return 0; +} + +static _DEFINE_DEV_PM_OPS(dw8250_pm_ops, dw8250_suspend, dw8250_resume, + dw8250_runtime_suspend, dw8250_runtime_resume, + NULL); + +static const struct dw8250_platform_data dw8250_dw_apb = { + .usr_reg = DW_UART_USR, +}; + +static const struct dw8250_platform_data dw8250_octeon_3860_data = { + .usr_reg = OCTEON_UART_USR, + .quirks = DW_UART_QUIRK_OCTEON, +}; + +static const struct dw8250_platform_data dw8250_armada_38x_data = { + .usr_reg = DW_UART_USR, + .quirks = DW_UART_QUIRK_ARMADA_38X, +}; + +static const struct dw8250_platform_data dw8250_renesas_rzn1_data = { + .usr_reg = DW_UART_USR, + .cpr_value = FIELD_PREP_CONST(DW_UART_CPR_ABP_DATA_WIDTH, 2) | + DW_UART_CPR_AFCE_MODE | + DW_UART_CPR_THRE_MODE | + DW_UART_CPR_ADDITIONAL_FEATURES | + DW_UART_CPR_FIFO_ACCESS | + DW_UART_CPR_FIFO_STAT | + DW_UART_CPR_SHADOW | + DW_UART_CPR_DMA_EXTRA | + DW_UART_CPR_FIFO_MODE_FROM_SIZE(16), + .quirks = DW_UART_QUIRK_CPR_VALUE | DW_UART_QUIRK_IS_DMA_FC, +}; + +static const struct dw8250_platform_data dw8250_skip_set_rate_data = { + .usr_reg = DW_UART_USR, + .quirks = DW_UART_QUIRK_SKIP_SET_RATE, +}; + +static const struct dw8250_platform_data dw8250_intc10ee = { + .usr_reg = DW_UART_USR, + .quirks = DW_UART_QUIRK_IER_KICK, +}; + +static const struct dw8250_platform_data dw8250_ultrarisc_dp1000_data = { + .usr_reg = DW_UART_USR, + .cpr_value = FIELD_PREP_CONST(DW_UART_CPR_ABP_DATA_WIDTH, 2) | + DW_UART_CPR_THRE_MODE | + DW_UART_CPR_DMA_EXTRA | + DW_UART_CPR_FIFO_MODE_FROM_SIZE(32), + .quirks = DW_UART_QUIRK_CPR_VALUE, +}; + +static const struct of_device_id dw8250_of_match[] = { + { .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb }, + { .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data }, + { .compatible = "marvell,armada-38x-uart", .data = &dw8250_armada_38x_data }, + { .compatible = "renesas,rzn1-uart", .data = &dw8250_renesas_rzn1_data }, + { .compatible = "sophgo,sg2044-uart", .data = &dw8250_skip_set_rate_data }, + { .compatible = "starfive,jh7100-uart", .data = &dw8250_skip_set_rate_data }, + { .compatible = "ultrarisc,dp1000-uart", .data = &dw8250_ultrarisc_dp1000_data }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dw8250_of_match); + +static const struct dw8250_platform_data dw8250_apmc0d08 = { + .usr_reg = DW_UART_USR, + .quirks = DW_UART_QUIRK_APMC0D08, +}; + +static const struct acpi_device_id dw8250_acpi_match[] = { + { "80860F0A", (kernel_ulong_t)&dw8250_dw_apb }, + { "8086228A", (kernel_ulong_t)&dw8250_dw_apb }, + { "AMD0020", (kernel_ulong_t)&dw8250_dw_apb }, + { "AMDI0020", (kernel_ulong_t)&dw8250_dw_apb }, + { "AMDI0022", (kernel_ulong_t)&dw8250_dw_apb }, + { "APMC0D08", (kernel_ulong_t)&dw8250_apmc0d08 }, + { "BRCM2032", (kernel_ulong_t)&dw8250_dw_apb }, + { "HISI0031", (kernel_ulong_t)&dw8250_dw_apb }, + { "INT33C4", (kernel_ulong_t)&dw8250_dw_apb }, + { "INT33C5", (kernel_ulong_t)&dw8250_dw_apb }, + { "INT3434", (kernel_ulong_t)&dw8250_dw_apb }, + { "INT3435", (kernel_ulong_t)&dw8250_dw_apb }, + { "INTC10EE", (kernel_ulong_t)&dw8250_intc10ee }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match); + +static struct platform_driver dw8250_platform_driver = { + .driver = { + .name = "dw-apb-uart", + .pm = pm_ptr(&dw8250_pm_ops), + .of_match_table = dw8250_of_match, + .acpi_match_table = dw8250_acpi_match, + }, + .probe = dw8250_probe, + .remove = dw8250_remove, +}; + +module_platform_driver(dw8250_platform_driver); + +MODULE_IMPORT_NS("SERIAL_8250"); +MODULE_AUTHOR("Jamie Iles"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Synopsys DesignWare 8250 serial port driver"); +MODULE_ALIAS("platform:dw-apb-uart"); diff --git a/drivers/tty/serial/8250/8250_dwlib.c b/drivers/tty/serial/8250/8250_dwlib.c new file mode 100644 index 000000000..8859e66d2 --- /dev/null +++ b/drivers/tty/serial/8250/8250_dwlib.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Synopsys DesignWare 8250 library. */ + +#include <linux/bitops.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/math.h> +#include <linux/property.h> +#include <linux/serial_8250.h> +#include <linux/serial_core.h> + +#include "8250_dwlib.h" + +/* + * divisor = div(I) + div(F) + * "I" means integer, "F" means fractional + * quot = div(I) = clk / (16 * baud) + * frac = div(F) * 2^dlf_size + * + * let rem = clk % (16 * baud) + * we have: div(F) * (16 * baud) = rem + * so frac = 2^dlf_size * rem / (16 * baud) = (rem << dlf_size) / (16 * baud) + */ +static unsigned int dw8250_get_divisor(struct uart_port *p, unsigned int baud, + unsigned int *frac) +{ + unsigned int quot, rem, base_baud = baud * 16; + struct dw8250_port_data *d = p->private_data; + + quot = p->uartclk / base_baud; + rem = p->uartclk % base_baud; + *frac = DIV_ROUND_CLOSEST(rem << d->dlf_size, base_baud); + + return quot; +} + +static void dw8250_set_divisor(struct uart_port *p, unsigned int baud, + unsigned int quot, unsigned int quot_frac) +{ + dw8250_writel_ext(p, DW_UART_DLF, quot_frac); + serial8250_do_set_divisor(p, baud, quot); +} + +void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, + const struct ktermios *old) +{ + p->status &= ~UPSTAT_AUTOCTS; + if (termios->c_cflag & CRTSCTS) + p->status |= UPSTAT_AUTOCTS; + + serial8250_do_set_termios(p, termios, old); + + /* Filter addresses which have 9th bit set */ + p->ignore_status_mask |= DW_UART_LSR_ADDR_RCVD; + p->read_status_mask |= DW_UART_LSR_ADDR_RCVD; +} +EXPORT_SYMBOL_GPL(dw8250_do_set_termios); + +/* + * Wait until re is de-asserted for sure. An ongoing receive will keep + * re asserted until end of frame. Without BUSY indication available, + * only available course of action is to wait for the time it takes to + * receive one frame (there might nothing to receive but w/o BUSY the + * driver cannot know). + */ +static void dw8250_wait_re_deassert(struct uart_port *p) +{ + ndelay(p->frame_time); +} + +static void dw8250_update_rar(struct uart_port *p, u32 addr) +{ + u32 re_en = dw8250_readl_ext(p, DW_UART_RE_EN); + + /* + * RAR shouldn't be changed while receiving. Thus, de-assert RE_EN + * if asserted and wait. + */ + if (re_en) + dw8250_writel_ext(p, DW_UART_RE_EN, 0); + dw8250_wait_re_deassert(p); + dw8250_writel_ext(p, DW_UART_RAR, addr); + if (re_en) + dw8250_writel_ext(p, DW_UART_RE_EN, re_en); +} + +static void dw8250_rs485_set_addr(struct uart_port *p, struct serial_rs485 *rs485, + struct ktermios *termios) +{ + u32 lcr = dw8250_readl_ext(p, DW_UART_LCR_EXT); + + if (rs485->flags & SER_RS485_ADDRB) { + lcr |= DW_UART_LCR_EXT_DLS_E; + if (termios) + termios->c_cflag |= ADDRB; + + if (rs485->flags & SER_RS485_ADDR_RECV) { + u32 delta = p->rs485.flags ^ rs485->flags; + + /* + * rs485 (param) is equal to uart_port's rs485 only during init + * (during init, delta is not yet applicable). + */ + if (unlikely(&p->rs485 == rs485)) + delta = rs485->flags; + + if ((delta & SER_RS485_ADDR_RECV) || + (p->rs485.addr_recv != rs485->addr_recv)) + dw8250_update_rar(p, rs485->addr_recv); + lcr |= DW_UART_LCR_EXT_ADDR_MATCH; + } else { + lcr &= ~DW_UART_LCR_EXT_ADDR_MATCH; + } + if (rs485->flags & SER_RS485_ADDR_DEST) { + /* + * Don't skip writes here as another endpoint could + * have changed communication line's destination + * address in between. + */ + dw8250_writel_ext(p, DW_UART_TAR, rs485->addr_dest); + lcr |= DW_UART_LCR_EXT_SEND_ADDR; + } + } else { + lcr = 0; + } + dw8250_writel_ext(p, DW_UART_LCR_EXT, lcr); +} + +static int dw8250_rs485_config(struct uart_port *p, struct ktermios *termios, + struct serial_rs485 *rs485) +{ + u32 tcr; + + tcr = dw8250_readl_ext(p, DW_UART_TCR); + tcr &= ~DW_UART_TCR_XFER_MODE; + + if (rs485->flags & SER_RS485_ENABLED) { + tcr |= DW_UART_TCR_RS485_EN; + + if (rs485->flags & SER_RS485_RX_DURING_TX) + tcr |= DW_UART_TCR_XFER_MODE_DE_DURING_RE; + else + tcr |= DW_UART_TCR_XFER_MODE_DE_OR_RE; + dw8250_writel_ext(p, DW_UART_DE_EN, 1); + dw8250_writel_ext(p, DW_UART_RE_EN, 1); + } else { + if (termios) + termios->c_cflag &= ~ADDRB; + + tcr &= ~DW_UART_TCR_RS485_EN; + } + + /* Reset to default polarity */ + tcr |= DW_UART_TCR_DE_POL; + tcr &= ~DW_UART_TCR_RE_POL; + + if (!(rs485->flags & SER_RS485_RTS_ON_SEND)) + tcr &= ~DW_UART_TCR_DE_POL; + if (device_property_read_bool(p->dev, "rs485-rx-active-high")) + tcr |= DW_UART_TCR_RE_POL; + + dw8250_writel_ext(p, DW_UART_TCR, tcr); + + /* Addressing mode can only be set up after TCR */ + if (rs485->flags & SER_RS485_ENABLED) + dw8250_rs485_set_addr(p, rs485, termios); + + return 0; +} + +/* + * Tests if RE_EN register can have non-zero value to see if RS-485 HW support + * is present. + */ +static bool dw8250_detect_rs485_hw(struct uart_port *p) +{ + u32 reg; + + dw8250_writel_ext(p, DW_UART_RE_EN, 1); + reg = dw8250_readl_ext(p, DW_UART_RE_EN); + dw8250_writel_ext(p, DW_UART_RE_EN, 0); + return reg; +} + +static const struct serial_rs485 dw8250_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RX_DURING_TX | SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND | SER_RS485_ADDRB | SER_RS485_ADDR_RECV | + SER_RS485_ADDR_DEST, +}; + +void dw8250_setup_port(struct uart_port *p) +{ + struct dw8250_port_data *pd = p->private_data; + struct uart_8250_port *up = up_to_u8250p(p); + u32 reg, old_dlf; + + pd->hw_rs485_support = dw8250_detect_rs485_hw(p); + if (pd->hw_rs485_support) { + p->rs485_config = dw8250_rs485_config; + up->lsr_save_mask = LSR_SAVE_FLAGS | DW_UART_LSR_ADDR_RCVD; + p->rs485_supported = dw8250_rs485_supported; + } else { + p->rs485_config = serial8250_em485_config; + p->rs485_supported = serial8250_em485_supported; + up->rs485_start_tx = serial8250_em485_start_tx; + up->rs485_stop_tx = serial8250_em485_stop_tx; + } + up->capabilities |= UART_CAP_NOTEMT; + + /* Preserve value written by firmware or bootloader */ + old_dlf = dw8250_readl_ext(p, DW_UART_DLF); + dw8250_writel_ext(p, DW_UART_DLF, ~0U); + reg = dw8250_readl_ext(p, DW_UART_DLF); + dw8250_writel_ext(p, DW_UART_DLF, old_dlf); + + if (reg) { + pd->dlf_size = fls(reg); + p->get_divisor = dw8250_get_divisor; + p->set_divisor = dw8250_set_divisor; + } + + reg = dw8250_readl_ext(p, DW_UART_UCV); + if (reg) + dev_dbg(p->dev, "Designware UART version %c.%c%c\n", + (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); + + reg = dw8250_readl_ext(p, DW_UART_CPR); + if (!reg) { + reg = pd->cpr_value; + dev_dbg(p->dev, "CPR is not available, using 0x%08x instead\n", reg); + } + if (!reg) + return; + + /* Select the type based on FIFO */ + if (reg & DW_UART_CPR_FIFO_MODE) { + p->type = PORT_16550A; + p->flags |= UPF_FIXED_TYPE; + p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); + up->capabilities = UART_CAP_FIFO | UART_CAP_NOTEMT; + } + + if (reg & DW_UART_CPR_AFCE_MODE) + up->capabilities |= UART_CAP_AFE; + + if (reg & DW_UART_CPR_SIR_MODE) + up->capabilities |= UART_CAP_IRDA; +} +EXPORT_SYMBOL_GPL(dw8250_setup_port); diff --git a/drivers/tty/serial/8250/8250_dwlib.h b/drivers/tty/serial/8250/8250_dwlib.h new file mode 100644 index 000000000..1fe52332e --- /dev/null +++ b/drivers/tty/serial/8250/8250_dwlib.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Synopsys DesignWare 8250 library header file. */ + +#ifndef _SERIAL_8250_DWLIB_H_ +#define _SERIAL_8250_DWLIB_H_ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/build_bug.h> +#include <linux/io.h> +#include <linux/types.h> + +#include "8250.h" + +/* Offsets for the DesignWare specific registers */ +#define DW_UART_USR 0x1f /* UART Status Register */ +#define DW_UART_DMASA 0xa8 /* DMA Software Ack */ +#define DW_UART_TCR 0xac /* Transceiver Control Register (RS485) */ +#define DW_UART_DE_EN 0xb0 /* Driver Output Enable Register */ +#define DW_UART_RE_EN 0xb4 /* Receiver Output Enable Register */ +#define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */ +#define DW_UART_RAR 0xc4 /* Receive Address Register */ +#define DW_UART_TAR 0xc8 /* Transmit Address Register */ +#define DW_UART_LCR_EXT 0xcc /* Line Extended Control Register */ +#define DW_UART_CPR 0xf4 /* Component Parameter Register */ +#define DW_UART_UCV 0xf8 /* UART Component Version */ + +/* Interrupt ID Register bits */ +#define DW_UART_IIR_IID GENMASK(3, 0) + +/* Modem Control Register bits */ +#define DW_UART_MCR_SIRE BIT(6) + +/* Line Status Register bits */ +#define DW_UART_LSR_ADDR_RCVD BIT(8) + +/* UART Status Register bits */ +#define DW_UART_USR_BUSY BIT(0) + +/* Transceiver Control Register bits */ +#define DW_UART_TCR_RS485_EN BIT(0) +#define DW_UART_TCR_RE_POL BIT(1) +#define DW_UART_TCR_DE_POL BIT(2) +#define DW_UART_TCR_XFER_MODE GENMASK(4, 3) +#define DW_UART_TCR_XFER_MODE_DE_DURING_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 0) +#define DW_UART_TCR_XFER_MODE_SW_DE_OR_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 1) +#define DW_UART_TCR_XFER_MODE_DE_OR_RE FIELD_PREP(DW_UART_TCR_XFER_MODE, 2) + +/* Receive / Transmit Address Register bits */ +#define DW_UART_ADDR_MASK GENMASK(7, 0) + +/* Line Extended Control Register bits */ +#define DW_UART_LCR_EXT_DLS_E BIT(0) +#define DW_UART_LCR_EXT_ADDR_MATCH BIT(1) +#define DW_UART_LCR_EXT_SEND_ADDR BIT(2) +#define DW_UART_LCR_EXT_TRANSMIT_MODE BIT(3) + +/* Component Parameter Register bits */ +#define DW_UART_CPR_ABP_DATA_WIDTH GENMASK(1, 0) +#define DW_UART_CPR_AFCE_MODE BIT(4) +#define DW_UART_CPR_THRE_MODE BIT(5) +#define DW_UART_CPR_SIR_MODE BIT(6) +#define DW_UART_CPR_SIR_LP_MODE BIT(7) +#define DW_UART_CPR_ADDITIONAL_FEATURES BIT(8) +#define DW_UART_CPR_FIFO_ACCESS BIT(9) +#define DW_UART_CPR_FIFO_STAT BIT(10) +#define DW_UART_CPR_SHADOW BIT(11) +#define DW_UART_CPR_ENCODED_PARMS BIT(12) +#define DW_UART_CPR_DMA_EXTRA BIT(13) +#define DW_UART_CPR_FIFO_MODE GENMASK(23, 16) + +/* Helpers for FIFO size calculation */ +#define DW_UART_CPR_FIFO_SIZE(a) (FIELD_GET(DW_UART_CPR_FIFO_MODE, (a)) * 16) +#define DW_UART_CPR_FIFO_MODE_FROM_SIZE(size) \ + (FIELD_PREP_CONST(DW_UART_CPR_FIFO_MODE, \ + BUILD_BUG_ON_ZERO((size) > 2048) + \ + BUILD_BUG_ON_ZERO((size) % 16) + \ + ((size) / 16))) + +struct dw8250_port_data { + /* Port properties */ + int line; + + /* DMA operations */ + struct uart_8250_dma dma; + + /* Hardware configuration */ + u32 cpr_value; + u8 dlf_size; + + /* RS485 variables */ + bool hw_rs485_support; +}; + +void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, const struct ktermios *old); +void dw8250_setup_port(struct uart_port *p); + +static inline u32 dw8250_readl_ext(struct uart_port *p, int offset) +{ + if (p->iotype == UPIO_MEM32BE) + return ioread32be(p->membase + offset); + return readl(p->membase + offset); +} + +static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg) +{ + if (p->iotype == UPIO_MEM32BE) + iowrite32be(reg, p->membase + offset); + else + writel(reg, p->membase + offset); +} + +#endif /* _SERIAL_8250_DWLIB_H_ */ diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c new file mode 100644 index 000000000..44ec209f3 --- /dev/null +++ b/drivers/tty/serial/8250/8250_early.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Early serial console for 8250/16550 devices + * + * (c) Copyright 2004 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas <bjorn.helgaas@hp.com> + * + * Based on the 8250.c serial driver, Copyright (C) 2001 Russell King, + * and on early_printk.c by Andi Kleen. + * + * This is for use before the serial driver has initialized, in + * particular, before the UARTs have been discovered and named. + * Instead of specifying the console device as, e.g., "ttyS0", + * we locate the device directly by its MMIO or I/O port address. + * + * The user can specify the device directly, e.g., + * earlycon=uart8250,io,0x3f8,9600n8 + * earlycon=uart8250,mmio,0xff5e0000,115200n8 + * earlycon=uart8250,mmio32,0xff5e0000,115200n8 + * or + * console=uart8250,io,0x3f8,9600n8 + * console=uart8250,mmio,0xff5e0000,115200n8 + * console=uart8250,mmio32,0xff5e0000,115200n8 + */ + +#include <linux/align.h> +#include <linux/tty.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/of.h> +#include <linux/serial_reg.h> +#include <linux/serial.h> +#include <linux/serial_8250.h> +#include <asm/io.h> +#include <asm/serial.h> + +static unsigned int serial8250_early_in(struct uart_port *port, int offset) +{ + offset <<= port->regshift; + + switch (port->iotype) { + case UPIO_MEM: + return readb(port->membase + offset); + case UPIO_MEM16: + return readw(port->membase + offset); + case UPIO_MEM32: + return readl(port->membase + offset); + case UPIO_MEM32BE: + return ioread32be(port->membase + offset); +#ifdef CONFIG_HAS_IOPORT + case UPIO_PORT: + return inb(port->iobase + offset); +#endif + default: + return 0; + } +} + +static void serial8250_early_out(struct uart_port *port, int offset, int value) +{ + offset <<= port->regshift; + + switch (port->iotype) { + case UPIO_MEM: + writeb(value, port->membase + offset); + break; + case UPIO_MEM16: + writew(value, port->membase + offset); + break; + case UPIO_MEM32: + writel(value, port->membase + offset); + break; + case UPIO_MEM32BE: + iowrite32be(value, port->membase + offset); + break; +#ifdef CONFIG_HAS_IOPORT + case UPIO_PORT: + outb(value, port->iobase + offset); + break; +#endif + default: + break; + } +} + +static void serial_putc(struct uart_port *port, unsigned char c) +{ + unsigned int status; + + serial8250_early_out(port, UART_TX, c); + + for (;;) { + status = serial8250_early_in(port, UART_LSR); + if (uart_lsr_tx_empty(status)) + break; + cpu_relax(); + } +} + +static void early_serial8250_write(struct console *console, + const char *s, unsigned int count) +{ + struct earlycon_device *device = console->data; + struct uart_port *port = &device->port; + + uart_console_write(port, s, count, serial_putc); +} + +#ifdef CONFIG_CONSOLE_POLL +static int early_serial8250_read(struct console *console, + char *s, unsigned int count) +{ + struct earlycon_device *device = console->data; + struct uart_port *port = &device->port; + unsigned int status; + int num_read = 0; + + while (num_read < count) { + status = serial8250_early_in(port, UART_LSR); + if (!(status & UART_LSR_DR)) + break; + s[num_read++] = serial8250_early_in(port, UART_RX); + } + + return num_read; +} +#else +#define early_serial8250_read NULL +#endif + +static void __init init_port(struct earlycon_device *device) +{ + struct uart_port *port = &device->port; + unsigned int divisor; + unsigned char c; + unsigned int ier; + + serial8250_early_out(port, UART_LCR, UART_LCR_WLEN8); /* 8n1 */ + ier = serial8250_early_in(port, UART_IER); + serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); /* no interrupt */ + serial8250_early_out(port, UART_FCR, 0); /* no fifo */ + serial8250_early_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); + + if (port->uartclk) { + divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud); + c = serial8250_early_in(port, UART_LCR); + serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB); + serial8250_early_out(port, UART_DLL, divisor & 0xff); + serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff); + serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB); + } +} + +int __init early_serial8250_setup(struct earlycon_device *device, + const char *options) +{ + if (!(device->port.membase || device->port.iobase)) + return -ENODEV; + + if (!device->baud) { + struct uart_port *port = &device->port; + unsigned int ier; + + /* assume the device was initialized, only mask interrupts */ + ier = serial8250_early_in(port, UART_IER); + serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); + } else + init_port(device); + + device->con->write = early_serial8250_write; + device->con->read = early_serial8250_read; + return 0; +} +EARLYCON_DECLARE(uart8250, early_serial8250_setup); +EARLYCON_DECLARE(uart, early_serial8250_setup); +OF_EARLYCON_DECLARE(ns16550, "ns16550", early_serial8250_setup); +OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup); +OF_EARLYCON_DECLARE(uart, "nvidia,tegra20-uart", early_serial8250_setup); +OF_EARLYCON_DECLARE(uart, "snps,dw-apb-uart", early_serial8250_setup); + +static int __init early_serial8250_xscale_setup(struct earlycon_device *device, + const char *options) +{ + /* + * Adjust for BE32 register accesses: drop any hardcoded + * address for the big endian byte target, add it explicitly + * if running on BE32. + */ + device->port.membase = PTR_ALIGN_DOWN(device->port.membase, 4); + if (IS_ENABLED(CONFIG_CPU_ENDIAN_BE32)) + device->port.membase += 3; + device->port.regshift = 2; + + return early_serial8250_setup(device, options); +} +OF_EARLYCON_DECLARE(uart, "intel,xscale-uart", early_serial8250_xscale_setup); + +static int __init early_serial8250_rs2_setup(struct earlycon_device *device, + const char *options) +{ + device->port.regshift = 2; + + return early_serial8250_setup(device, options); +} +OF_EARLYCON_DECLARE(uart, "mrvl,mmp-uart", early_serial8250_rs2_setup); +OF_EARLYCON_DECLARE(uart, "mrvl,pxa-uart", early_serial8250_rs2_setup); + +#ifdef CONFIG_SERIAL_8250_OMAP + +static int __init early_omap8250_setup(struct earlycon_device *device, + const char *options) +{ + struct uart_port *port = &device->port; + + if (!(device->port.membase || device->port.iobase)) + return -ENODEV; + + port->regshift = 2; + device->con->write = early_serial8250_write; + return 0; +} + +OF_EARLYCON_DECLARE(omap8250, "ti,omap2-uart", early_omap8250_setup); +OF_EARLYCON_DECLARE(omap8250, "ti,omap3-uart", early_omap8250_setup); +OF_EARLYCON_DECLARE(omap8250, "ti,omap4-uart", early_omap8250_setup); +OF_EARLYCON_DECLARE(omap8250, "ti,am654-uart", early_omap8250_setup); + +#endif diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c new file mode 100644 index 000000000..32d077da0 --- /dev/null +++ b/drivers/tty/serial/8250/8250_em.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas Emma Mobile 8250 driver + * + * Copyright (C) 2012 Magnus Damm + */ + +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/serial_8250.h> +#include <linux/serial_reg.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include "8250.h" + +#define UART_DLL_EM 9 +#define UART_DLM_EM 10 +#define UART_HCR0_EM 11 + +/* + * A high value for UART_FCR_EM avoids overlapping with existing UART_* + * register defines. UART_FCR_EM_HW is the real HW register offset. + */ +#define UART_FCR_EM 0x10003 +#define UART_FCR_EM_HW 3 + +#define UART_HCR0_EM_SW_RESET BIT(7) /* SW Reset */ + +struct serial8250_em_priv { + int line; +}; + +static void serial8250_em_serial_out_helper(struct uart_port *p, int offset, + int value) +{ + switch (offset) { + case UART_TX: /* TX @ 0x00 */ + writeb(value, p->membase); + break; + case UART_LCR: /* LCR @ 0x10 (+1) */ + case UART_MCR: /* MCR @ 0x14 (+1) */ + case UART_SCR: /* SCR @ 0x20 (+1) */ + writel(value, p->membase + ((offset + 1) << 2)); + break; + case UART_FCR_EM: + writel(value, p->membase + (UART_FCR_EM_HW << 2)); + break; + case UART_IER: /* IER @ 0x04 */ + value &= 0x0f; /* only 4 valid bits - not Xscale */ + fallthrough; + case UART_DLL_EM: /* DLL @ 0x24 (+9) */ + case UART_DLM_EM: /* DLM @ 0x28 (+9) */ + case UART_HCR0_EM: /* HCR0 @ 0x2c */ + writel(value, p->membase + (offset << 2)); + break; + } +} + +static u32 serial8250_em_serial_in(struct uart_port *p, unsigned int offset) +{ + switch (offset) { + case UART_RX: /* RX @ 0x00 */ + return readb(p->membase); + case UART_LCR: /* LCR @ 0x10 (+1) */ + case UART_MCR: /* MCR @ 0x14 (+1) */ + case UART_LSR: /* LSR @ 0x18 (+1) */ + case UART_MSR: /* MSR @ 0x1c (+1) */ + case UART_SCR: /* SCR @ 0x20 (+1) */ + return readl(p->membase + ((offset + 1) << 2)); + case UART_FCR_EM: + return readl(p->membase + (UART_FCR_EM_HW << 2)); + case UART_IER: /* IER @ 0x04 */ + case UART_IIR: /* IIR @ 0x08 */ + case UART_DLL_EM: /* DLL @ 0x24 (+9) */ + case UART_DLM_EM: /* DLM @ 0x28 (+9) */ + case UART_HCR0_EM: /* HCR0 @ 0x2c */ + return readl(p->membase + (offset << 2)); + } + return 0; +} + +static void serial8250_em_reg_update(struct uart_port *p, int off, int value) +{ + unsigned int ier, fcr, lcr, mcr, hcr0; + + ier = serial8250_em_serial_in(p, UART_IER); + fcr = serial8250_em_serial_in(p, UART_FCR_EM); + lcr = serial8250_em_serial_in(p, UART_LCR); + mcr = serial8250_em_serial_in(p, UART_MCR); + hcr0 = serial8250_em_serial_in(p, UART_HCR0_EM); + + serial8250_em_serial_out_helper(p, UART_FCR_EM, fcr | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial8250_em_serial_out_helper(p, UART_HCR0_EM, hcr0 | + UART_HCR0_EM_SW_RESET); + serial8250_em_serial_out_helper(p, UART_HCR0_EM, hcr0 & + ~UART_HCR0_EM_SW_RESET); + + switch (off) { + case UART_FCR_EM: + fcr = value; + break; + case UART_LCR: + lcr = value; + break; + case UART_MCR: + mcr = value; + break; + } + + serial8250_em_serial_out_helper(p, UART_IER, ier); + serial8250_em_serial_out_helper(p, UART_FCR_EM, fcr); + serial8250_em_serial_out_helper(p, UART_MCR, mcr); + serial8250_em_serial_out_helper(p, UART_LCR, lcr); + serial8250_em_serial_out_helper(p, UART_HCR0_EM, hcr0); +} + +static void serial8250_em_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ + switch (offset) { + case UART_TX: + case UART_SCR: + case UART_IER: + case UART_DLL_EM: + case UART_DLM_EM: + serial8250_em_serial_out_helper(p, offset, value); + break; + case UART_FCR: + serial8250_em_reg_update(p, UART_FCR_EM, value); + break; + case UART_LCR: + case UART_MCR: + serial8250_em_reg_update(p, offset, value); + break; + } +} + +static u32 serial8250_em_serial_dl_read(struct uart_8250_port *up) +{ + return serial_in(up, UART_DLL_EM) | serial_in(up, UART_DLM_EM) << 8; +} + +static void serial8250_em_serial_dl_write(struct uart_8250_port *up, u32 value) +{ + serial_out(up, UART_DLL_EM, value & 0xff); + serial_out(up, UART_DLM_EM, value >> 8 & 0xff); +} + +static int serial8250_em_probe(struct platform_device *pdev) +{ + struct serial8250_em_priv *priv; + struct device *dev = &pdev->dev; + struct uart_8250_port up; + struct resource *regs; + struct clk *sclk; + int irq, ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return dev_err_probe(dev, -EINVAL, "missing registers\n"); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + sclk = devm_clk_get_enabled(dev, "sclk"); + if (IS_ERR(sclk)) + return dev_err_probe(dev, PTR_ERR(sclk), "unable to get clock\n"); + + memset(&up, 0, sizeof(up)); + up.port.mapbase = regs->start; + up.port.irq = irq; + up.port.type = PORT_16750; + up.port.flags = UPF_FIXED_PORT | UPF_IOREMAP | UPF_FIXED_TYPE; + up.port.dev = dev; + up.port.private_data = priv; + + up.port.uartclk = clk_get_rate(sclk); + + up.port.iotype = UPIO_MEM32; + up.port.serial_in = serial8250_em_serial_in; + up.port.serial_out = serial8250_em_serial_out; + up.dl_read = serial8250_em_serial_dl_read; + up.dl_write = serial8250_em_serial_dl_write; + + ret = serial8250_register_8250_port(&up); + if (ret < 0) + return dev_err_probe(dev, ret, "unable to register 8250 port\n"); + + priv->line = ret; + platform_set_drvdata(pdev, priv); + return 0; +} + +static void serial8250_em_remove(struct platform_device *pdev) +{ + struct serial8250_em_priv *priv = platform_get_drvdata(pdev); + + serial8250_unregister_port(priv->line); +} + +static const struct of_device_id serial8250_em_dt_ids[] = { + { .compatible = "renesas,em-uart", }, + {}, +}; +MODULE_DEVICE_TABLE(of, serial8250_em_dt_ids); + +static struct platform_driver serial8250_em_platform_driver = { + .driver = { + .name = "serial8250-em", + .of_match_table = serial8250_em_dt_ids, + }, + .probe = serial8250_em_probe, + .remove = serial8250_em_remove, +}; + +module_platform_driver(serial8250_em_platform_driver); + +MODULE_AUTHOR("Magnus Damm"); +MODULE_DESCRIPTION("Renesas Emma Mobile 8250 Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c new file mode 100644 index 000000000..836792e86 --- /dev/null +++ b/drivers/tty/serial/8250/8250_exar.c @@ -0,0 +1,1740 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Probe module for 8250/16550-type Exar chips PCI serial ports. + * + * Based on drivers/tty/serial/8250/8250_pci.c, + * + * Copyright (C) 2017 Sudip Mukherjee, All Rights Reserved. + */ +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dmi.h> +#include <linux/eeprom_93cx6.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/math.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/property.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <linux/serial_8250.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> + +#include <asm/byteorder.h> + +#include "8250.h" +#include "8250_pcilib.h" + +#define PCI_DEVICE_ID_ACCESSIO_COM_2S 0x1052 +#define PCI_DEVICE_ID_ACCESSIO_COM_4S 0x105d +#define PCI_DEVICE_ID_ACCESSIO_COM_8S 0x106c +#define PCI_DEVICE_ID_ACCESSIO_COM232_8 0x10a8 +#define PCI_DEVICE_ID_ACCESSIO_COM_2SM 0x10d2 +#define PCI_DEVICE_ID_ACCESSIO_COM_4SM 0x10db +#define PCI_DEVICE_ID_ACCESSIO_COM_8SM 0x10ea + +#define PCI_DEVICE_ID_ADVANTECH_XR17V352 0x0018 + +#define PCI_DEVICE_ID_COMMTECH_4224PCI335 0x0002 +#define PCI_DEVICE_ID_COMMTECH_4222PCI335 0x0004 +#define PCI_DEVICE_ID_COMMTECH_2324PCI335 0x000a +#define PCI_DEVICE_ID_COMMTECH_2328PCI335 0x000b +#define PCI_DEVICE_ID_COMMTECH_4224PCIE 0x0020 +#define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021 +#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 + +#define PCI_VENDOR_ID_CONNECT_TECH 0x12c4 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_SP_OPTO 0x0340 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_A 0x0341 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_B 0x0342 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS 0x0350 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A 0x0351 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B 0x0352 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS 0x0353 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A 0x0354 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B 0x0355 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO 0x0360 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A 0x0361 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B 0x0362 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP 0x0370 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232 0x0371 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485 0x0372 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP 0x0373 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP 0x0374 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP 0x0375 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232_NS 0x0376 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT 0x0380 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT 0x0381 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO 0x0382 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO 0x0392 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP 0x03A0 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232 0x03A1 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485 0x03A2 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232_NS 0x03A3 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XEG001 0x0602 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_BASE 0x1000 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_2 0x1002 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_4 0x1004 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_8 0x1008 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_12 0x100C +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_16 0x1010 +#define PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X 0x110c +#define PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X 0x110d +#define PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16 0x1110 + +#define PCI_DEVICE_ID_EXAR_XR17V4358 0x4358 +#define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358 +#define PCI_DEVICE_ID_EXAR_XR17V252 0x0252 +#define PCI_DEVICE_ID_EXAR_XR17V254 0x0254 +#define PCI_DEVICE_ID_EXAR_XR17V258 0x0258 + +#define PCI_SUBDEVICE_ID_USR_2980 0x0128 +#define PCI_SUBDEVICE_ID_USR_2981 0x0129 + +#define UART_EXAR_INT0 0x80 +#define UART_EXAR_8XMODE 0x88 /* 8X sampling rate select */ +#define UART_EXAR_SLEEP 0x8b /* Sleep mode */ +#define UART_EXAR_DVID 0x8d /* Device identification */ + +#define UART_EXAR_FCTR 0x08 /* Feature Control Register */ +#define UART_FCTR_EXAR_IRDA 0x10 /* IrDa data encode select */ +#define UART_FCTR_EXAR_485 0x20 /* Auto 485 half duplex dir ctl */ +#define UART_FCTR_EXAR_TRGA 0x00 /* FIFO trigger table A */ +#define UART_FCTR_EXAR_TRGB 0x60 /* FIFO trigger table B */ +#define UART_FCTR_EXAR_TRGC 0x80 /* FIFO trigger table C */ +#define UART_FCTR_EXAR_TRGD 0xc0 /* FIFO trigger table D programmable */ + +#define UART_EXAR_TXTRG 0x0a /* Tx FIFO trigger level write-only */ +#define UART_EXAR_RXTRG 0x0b /* Rx FIFO trigger level write-only */ + +#define UART_EXAR_MPIOINT_7_0 0x8f /* MPIOINT[7:0] */ +#define UART_EXAR_MPIOLVL_7_0 0x90 /* MPIOLVL[7:0] */ +#define UART_EXAR_MPIO3T_7_0 0x91 /* MPIO3T[7:0] */ +#define UART_EXAR_MPIOINV_7_0 0x92 /* MPIOINV[7:0] */ +#define UART_EXAR_MPIOSEL_7_0 0x93 /* MPIOSEL[7:0] */ +#define UART_EXAR_MPIOOD_7_0 0x94 /* MPIOOD[7:0] */ +#define UART_EXAR_MPIOINT_15_8 0x95 /* MPIOINT[15:8] */ +#define UART_EXAR_MPIOLVL_15_8 0x96 /* MPIOLVL[15:8] */ +#define UART_EXAR_MPIO3T_15_8 0x97 /* MPIO3T[15:8] */ +#define UART_EXAR_MPIOINV_15_8 0x98 /* MPIOINV[15:8] */ +#define UART_EXAR_MPIOSEL_15_8 0x99 /* MPIOSEL[15:8] */ +#define UART_EXAR_MPIOOD_15_8 0x9a /* MPIOOD[15:8] */ + +#define UART_EXAR_RS485_DLY(x) ((x) << 4) + +#define UART_EXAR_DLD 0x02 /* Divisor Fractional */ +#define UART_EXAR_DLD_485_POLARITY 0x80 /* RS-485 Enable Signal Polarity */ + +/* EEPROM registers */ +#define UART_EXAR_REGB 0x8e +#define UART_EXAR_REGB_EECK BIT(4) +#define UART_EXAR_REGB_EECS BIT(5) +#define UART_EXAR_REGB_EEDI BIT(6) +#define UART_EXAR_REGB_EEDO BIT(7) + +#define UART_EXAR_XR17C15X_PORT_OFFSET 0x200 +#define UART_EXAR_XR17V25X_PORT_OFFSET 0x200 +#define UART_EXAR_XR17V35X_PORT_OFFSET 0x400 + +/* + * IOT2040 MPIO wiring semantics: + * + * MPIO Port Function + * ---- ---- -------- + * 0 2 Mode bit 0 + * 1 2 Mode bit 1 + * 2 2 Terminate bus + * 3 - <reserved> + * 4 3 Mode bit 0 + * 5 3 Mode bit 1 + * 6 3 Terminate bus + * 7 - <reserved> + * 8 2 Enable + * 9 3 Enable + * 10 - Red LED + * 11..15 - <unused> + */ + +/* IOT2040 MPIOs 0..7 */ +#define IOT2040_UART_MODE_RS232 0x01 +#define IOT2040_UART_MODE_RS485 0x02 +#define IOT2040_UART_MODE_RS422 0x03 +#define IOT2040_UART_TERMINATE_BUS 0x04 + +#define IOT2040_UART1_MASK 0x0f +#define IOT2040_UART2_SHIFT 4 + +#define IOT2040_UARTS_DEFAULT_MODE 0x11 /* both RS232 */ +#define IOT2040_UARTS_GPIO_LO_MODE 0x88 /* reserved pins as input */ + +/* IOT2040 MPIOs 8..15 */ +#define IOT2040_UARTS_ENABLE 0x03 +#define IOT2040_UARTS_GPIO_HI_MODE 0xF8 /* enable & LED as outputs */ + +/* CTI EEPROM offsets */ +#define CTI_EE_OFF_XR17C15X_OSC_FREQ 0x04 /* 2 words */ +#define CTI_EE_OFF_XR17C15X_PART_NUM 0x0A /* 4 words */ +#define CTI_EE_OFF_XR17C15X_SERIAL_NUM 0x0E /* 1 word */ + +#define CTI_EE_OFF_XR17V25X_OSC_FREQ 0x08 /* 2 words */ +#define CTI_EE_OFF_XR17V25X_PART_NUM 0x0E /* 4 words */ +#define CTI_EE_OFF_XR17V25X_SERIAL_NUM 0x12 /* 1 word */ + +#define CTI_EE_OFF_XR17V35X_SERIAL_NUM 0x11 /* 2 word */ +#define CTI_EE_OFF_XR17V35X_BRD_FLAGS 0x13 /* 1 word */ +#define CTI_EE_OFF_XR17V35X_PORT_FLAGS 0x14 /* 1 word */ + +#define CTI_EE_MASK_PORT_FLAGS_TYPE GENMASK(7, 0) +#define CTI_EE_MASK_OSC_FREQ GENMASK(31, 0) + +#define CTI_FPGA_RS485_IO_REG 0x2008 +#define CTI_FPGA_CFG_INT_EN_REG 0x48 +#define CTI_FPGA_CFG_INT_EN_EXT_BIT BIT(15) /* External int enable bit */ + +#define CTI_DEFAULT_PCI_OSC_FREQ 29491200 +#define CTI_DEFAULT_PCIE_OSC_FREQ 125000000 +#define CTI_DEFAULT_FPGA_OSC_FREQ 33333333 + +/* + * CTI Serial port line types. These match the values stored in the first + * nibble of the CTI EEPROM port_flags word. + */ +enum cti_port_type { + CTI_PORT_TYPE_NONE = 0, + CTI_PORT_TYPE_RS232, // RS232 ONLY + CTI_PORT_TYPE_RS422_485, // RS422/RS485 ONLY + CTI_PORT_TYPE_RS232_422_485_HW, // RS232/422/485 HW ONLY Switchable + CTI_PORT_TYPE_RS232_422_485_SW, // RS232/422/485 SW ONLY Switchable + CTI_PORT_TYPE_RS232_422_485_4B, // RS232/422/485 HW/SW (4bit ex. BCG004) + CTI_PORT_TYPE_RS232_422_485_2B, // RS232/422/485 HW/SW (2bit ex. BBG008) + CTI_PORT_TYPE_MAX, +}; + +#define CTI_PORT_TYPE_VALID(_port_type) \ + (((_port_type) > CTI_PORT_TYPE_NONE) && \ + ((_port_type) < CTI_PORT_TYPE_MAX)) + +#define CTI_PORT_TYPE_RS485(_port_type) \ + (((_port_type) > CTI_PORT_TYPE_RS232) && \ + ((_port_type) < CTI_PORT_TYPE_MAX)) + +struct exar8250; + +struct exar8250_platform { + int (*rs485_config)(struct uart_port *port, struct ktermios *termios, + struct serial_rs485 *rs485); + const struct serial_rs485 *rs485_supported; + int (*register_gpio)(struct pci_dev *pcidev, struct uart_8250_port *port); + void (*unregister_gpio)(struct uart_8250_port *port); +}; + +/** + * struct exar8250_board - board information + * @num_ports: number of serial ports + * @reg_shift: describes UART register mapping in PCI memory + * @setup: quirk run at ->probe() stage for each port + * @exit: quirk run at ->remove() stage + */ +struct exar8250_board { + unsigned int num_ports; + unsigned int reg_shift; + int (*setup)(struct exar8250 *priv, struct pci_dev *pcidev, + struct uart_8250_port *port, int idx); + void (*exit)(struct pci_dev *pcidev); +}; + +struct exar8250 { + unsigned int nr; + unsigned int osc_freq; + struct exar8250_board *board; + struct eeprom_93cx6 eeprom; + void __iomem *virt; + int line[]; +}; + +static inline void exar_write_reg(struct exar8250 *priv, + unsigned int reg, u8 value) +{ + writeb(value, priv->virt + reg); +} + +static inline u8 exar_read_reg(struct exar8250 *priv, unsigned int reg) +{ + return readb(priv->virt + reg); +} + +static void exar_eeprom_93cx6_reg_read(struct eeprom_93cx6 *eeprom) +{ + struct exar8250 *priv = eeprom->data; + u8 regb = exar_read_reg(priv, UART_EXAR_REGB); + + /* EECK and EECS always read 0 from REGB so only set EEDO */ + eeprom->reg_data_out = regb & UART_EXAR_REGB_EEDO; +} + +static void exar_eeprom_93cx6_reg_write(struct eeprom_93cx6 *eeprom) +{ + struct exar8250 *priv = eeprom->data; + u8 regb = 0; + + if (eeprom->reg_data_in) + regb |= UART_EXAR_REGB_EEDI; + if (eeprom->reg_data_clock) + regb |= UART_EXAR_REGB_EECK; + if (eeprom->reg_chip_select) + regb |= UART_EXAR_REGB_EECS; + + exar_write_reg(priv, UART_EXAR_REGB, regb); +} + +static void exar_eeprom_init(struct exar8250 *priv) +{ + priv->eeprom.data = priv; + priv->eeprom.register_read = exar_eeprom_93cx6_reg_read; + priv->eeprom.register_write = exar_eeprom_93cx6_reg_write; + priv->eeprom.width = PCI_EEPROM_WIDTH_93C46; + priv->eeprom.quirks |= PCI_EEPROM_QUIRK_EXTRA_READ_CYCLE; +} + +/** + * exar_mpio_config_output() - Configure an Exar MPIO as an output + * @priv: Device's private structure + * @mpio_num: MPIO number/offset to configure + * + * Configure a single MPIO as an output and disable tristate. It is recommended + * to set the level with exar_mpio_set_high()/exar_mpio_set_low() prior to + * calling this function to ensure default MPIO pin state. + * + * Return: 0 on success, negative error code on failure + */ +static int exar_mpio_config_output(struct exar8250 *priv, + unsigned int mpio_num) +{ + unsigned int mpio_offset; + u8 sel_reg; // MPIO Select register (input/output) + u8 tri_reg; // MPIO Tristate register + u8 value; + + if (mpio_num < 8) { + sel_reg = UART_EXAR_MPIOSEL_7_0; + tri_reg = UART_EXAR_MPIO3T_7_0; + mpio_offset = mpio_num; + } else if (mpio_num >= 8 && mpio_num < 16) { + sel_reg = UART_EXAR_MPIOSEL_15_8; + tri_reg = UART_EXAR_MPIO3T_15_8; + mpio_offset = mpio_num - 8; + } else { + return -EINVAL; + } + + // Disable MPIO pin tri-state + value = exar_read_reg(priv, tri_reg); + value &= ~BIT(mpio_offset); + exar_write_reg(priv, tri_reg, value); + + value = exar_read_reg(priv, sel_reg); + value &= ~BIT(mpio_offset); + exar_write_reg(priv, sel_reg, value); + + return 0; +} + +/** + * _exar_mpio_set() - Set an Exar MPIO output high or low + * @priv: Device's private structure + * @mpio_num: MPIO number/offset to set + * @high: Set MPIO high if true, low if false + * + * Set a single MPIO high or low. exar_mpio_config_output() must also be called + * to configure the pin as an output. + * + * Return: 0 on success, negative error code on failure + */ +static int _exar_mpio_set(struct exar8250 *priv, + unsigned int mpio_num, bool high) +{ + unsigned int mpio_offset; + u8 lvl_reg; + u8 value; + + if (mpio_num < 8) { + lvl_reg = UART_EXAR_MPIOLVL_7_0; + mpio_offset = mpio_num; + } else if (mpio_num >= 8 && mpio_num < 16) { + lvl_reg = UART_EXAR_MPIOLVL_15_8; + mpio_offset = mpio_num - 8; + } else { + return -EINVAL; + } + + value = exar_read_reg(priv, lvl_reg); + if (high) + value |= BIT(mpio_offset); + else + value &= ~BIT(mpio_offset); + exar_write_reg(priv, lvl_reg, value); + + return 0; +} + +static int exar_mpio_set_low(struct exar8250 *priv, unsigned int mpio_num) +{ + return _exar_mpio_set(priv, mpio_num, false); +} + +static int exar_mpio_set_high(struct exar8250 *priv, unsigned int mpio_num) +{ + return _exar_mpio_set(priv, mpio_num, true); +} + +static int generic_rs485_config(struct uart_port *port, struct ktermios *termios, + struct serial_rs485 *rs485) +{ + bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED); + u8 __iomem *p = port->membase; + u8 value; + + value = readb(p + UART_EXAR_FCTR); + if (is_rs485) + value |= UART_FCTR_EXAR_485; + else + value &= ~UART_FCTR_EXAR_485; + + writeb(value, p + UART_EXAR_FCTR); + + if (is_rs485) + writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR); + + return 0; +} + +static const struct serial_rs485 generic_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND, +}; + +static void exar_pm(struct uart_port *port, unsigned int state, unsigned int old) +{ + /* + * Exar UARTs have a SLEEP register that enables or disables each UART + * to enter sleep mode separately. On the XR17V35x the register + * is accessible to each UART at the UART_EXAR_SLEEP offset, but + * the UART channel may only write to the corresponding bit. + */ + serial_port_out(port, UART_EXAR_SLEEP, state ? 0xff : 0); +} + +/* + * XR17V35x UARTs have an extra fractional divisor register (DLD) + * Calculate divisor with extra 4-bit fractional portion + */ +static unsigned int xr17v35x_get_divisor(struct uart_port *p, unsigned int baud, + unsigned int *frac) +{ + unsigned int quot_16; + + quot_16 = DIV_ROUND_CLOSEST(p->uartclk, baud); + *frac = quot_16 & 0x0f; + + return quot_16 >> 4; +} + +static void xr17v35x_set_divisor(struct uart_port *p, unsigned int baud, + unsigned int quot, unsigned int quot_frac) +{ + serial8250_do_set_divisor(p, baud, quot); + + /* Preserve bits not related to baudrate; DLD[7:4]. */ + quot_frac |= serial_port_in(p, 0x2) & 0xf0; + serial_port_out(p, 0x2, quot_frac); +} + +static int xr17v35x_startup(struct uart_port *port) +{ + /* + * First enable access to IER [7:5], ISR [5:4], FCR [5:4], + * MCR [7:5] and MSR [7:0] + */ + serial_port_out(port, UART_XR_EFR, UART_EFR_ECB); + + /* + * Make sure all interrupts are masked until initialization is + * complete and the FIFOs are cleared + * + * Synchronize UART_IER access against the console. + */ + uart_port_lock_irq(port); + serial_port_out(port, UART_IER, 0); + uart_port_unlock_irq(port); + + return serial8250_do_startup(port); +} + +static void exar_shutdown(struct uart_port *port) +{ + bool tx_complete = false; + struct uart_8250_port *up = up_to_u8250p(port); + struct tty_port *tport = &port->state->port; + int i = 0; + u16 lsr; + + do { + lsr = serial_in(up, UART_LSR); + if (lsr & (UART_LSR_TEMT | UART_LSR_THRE)) + tx_complete = true; + else + tx_complete = false; + usleep_range(1000, 1100); + } while (!kfifo_is_empty(&tport->xmit_fifo) && + !tx_complete && i++ < 1000); + + serial8250_do_shutdown(port); +} + +static int default_setup(struct exar8250 *priv, struct pci_dev *pcidev, + int idx, unsigned int offset, + struct uart_8250_port *port) +{ + const struct exar8250_board *board = priv->board; + unsigned char status; + int err; + + err = serial8250_pci_setup_port(pcidev, port, 0, offset, board->reg_shift, priv->virt); + if (err) + return err; + + /* + * XR17V35x UARTs have an extra divisor register, DLD that gets enabled + * with when DLAB is set which will cause the device to incorrectly match + * and assign port type to PORT_16650. The EFR for this UART is found + * at offset 0x09. Instead check the Deice ID (DVID) register + * for a 2, 4 or 8 port UART. + */ + status = readb(port->port.membase + UART_EXAR_DVID); + if (status == 0x82 || status == 0x84 || status == 0x88) { + port->port.type = PORT_XR17V35X; + + port->port.get_divisor = xr17v35x_get_divisor; + port->port.set_divisor = xr17v35x_set_divisor; + + port->port.startup = xr17v35x_startup; + } else { + port->port.type = PORT_XR17D15X; + } + + port->port.pm = exar_pm; + port->port.shutdown = exar_shutdown; + + return 0; +} + +static int +pci_fastcom335_setup(struct exar8250 *priv, struct pci_dev *pcidev, + struct uart_8250_port *port, int idx) +{ + unsigned int offset = idx * 0x200; + unsigned int baud = 1843200; + u8 __iomem *p; + int err; + + port->port.uartclk = baud * 16; + + err = default_setup(priv, pcidev, idx, offset, port); + if (err) + return err; + + p = port->port.membase; + + writeb(0x00, p + UART_EXAR_8XMODE); + writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR); + writeb(32, p + UART_EXAR_TXTRG); + writeb(32, p + UART_EXAR_RXTRG); + + /* Skip the initial (per device) setup */ + if (idx) + return 0; + + /* + * Setup Multipurpose Input/Output pins. + */ + switch (pcidev->device) { + case PCI_DEVICE_ID_COMMTECH_4222PCI335: + case PCI_DEVICE_ID_COMMTECH_4224PCI335: + writeb(0x78, p + UART_EXAR_MPIOLVL_7_0); + writeb(0x00, p + UART_EXAR_MPIOINV_7_0); + writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); + break; + case PCI_DEVICE_ID_COMMTECH_2324PCI335: + case PCI_DEVICE_ID_COMMTECH_2328PCI335: + writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); + writeb(0xc0, p + UART_EXAR_MPIOINV_7_0); + writeb(0xc0, p + UART_EXAR_MPIOSEL_7_0); + break; + default: + break; + } + writeb(0x00, p + UART_EXAR_MPIOINT_7_0); + writeb(0x00, p + UART_EXAR_MPIO3T_7_0); + writeb(0x00, p + UART_EXAR_MPIOOD_7_0); + + return 0; +} + +/** + * cti_tristate_disable() - Disable RS485 transciever tristate + * @priv: Device's private structure + * @port_num: Port number to set tristate off + * + * Most RS485 capable cards have a power on tristate jumper/switch that ensures + * the RS422/RS485 transceiver does not drive a multi-drop RS485 bus when it is + * not the master. When this jumper is installed the user must set the RS485 + * mode to Full or Half duplex to disable tristate prior to using the port. + * + * Some Exar UARTs have an auto-tristate feature while others require setting + * an MPIO to disable the tristate. + * + * Return: 0 on success, negative error code on failure + */ +static int cti_tristate_disable(struct exar8250 *priv, unsigned int port_num) +{ + int ret; + + ret = exar_mpio_set_high(priv, port_num); + if (ret) + return ret; + + return exar_mpio_config_output(priv, port_num); +} + +/** + * cti_plx_int_enable() - Enable UART interrupts to PLX bridge + * @priv: Device's private structure + * + * Some older CTI cards require MPIO_0 to be set low to enable the + * interrupts from the UART to the PLX PCI->PCIe bridge. + * + * Return: 0 on success, negative error code on failure + */ +static int cti_plx_int_enable(struct exar8250 *priv) +{ + int ret; + + ret = exar_mpio_set_low(priv, 0); + if (ret) + return ret; + + return exar_mpio_config_output(priv, 0); +} + +/** + * cti_read_osc_freq() - Read the UART oscillator frequency from EEPROM + * @priv: Device's private structure + * @eeprom_offset: Offset where the oscillator frequency is stored + * + * CTI XR17x15X and XR17V25X cards have the serial boards oscillator frequency + * stored in the EEPROM. FPGA and XR17V35X based cards use the PCI/PCIe clock. + * + * Return: frequency on success, negative error code on failure + */ +static int cti_read_osc_freq(struct exar8250 *priv, u8 eeprom_offset) +{ + __le16 ee_words[2]; + u32 osc_freq; + + eeprom_93cx6_multiread(&priv->eeprom, eeprom_offset, ee_words, ARRAY_SIZE(ee_words)); + + osc_freq = le16_to_cpu(ee_words[0]) | (le16_to_cpu(ee_words[1]) << 16); + if (osc_freq == CTI_EE_MASK_OSC_FREQ) + return -EIO; + + return osc_freq; +} + +/** + * cti_get_port_type_xr17c15x_xr17v25x() - Get port type of xr17c15x/xr17v25x + * @priv: Device's private structure + * @pcidev: Pointer to the PCI device for this port + * @port_num: Port to get type of + * + * CTI xr17c15x and xr17v25x based cards port types are based on PCI IDs. + * + * Return: port type on success, CTI_PORT_TYPE_NONE on failure + */ +static enum cti_port_type cti_get_port_type_xr17c15x_xr17v25x(struct exar8250 *priv, + struct pci_dev *pcidev, + unsigned int port_num) +{ + switch (pcidev->subsystem_device) { + // RS232 only cards + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232_NS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232_NS: + return CTI_PORT_TYPE_RS232; + // 1x RS232, 1x RS422/RS485 + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1: + return (port_num == 0) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; + // 2x RS232, 2x RS422/RS485 + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2: + return (port_num < 2) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; + // 4x RS232, 4x RS422/RS485 + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP: + return (port_num < 4) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; + // RS232/RS422/RS485 HW (jumper) selectable + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_SP_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP: + return CTI_PORT_TYPE_RS232_422_485_HW; + // RS422/RS485 HW (jumper) selectable + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485: + return CTI_PORT_TYPE_RS422_485; + // 6x RS232, 2x RS422/RS485 + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP: + return (port_num < 6) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; + // 2x RS232, 6x RS422/RS485 + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP: + return (port_num < 2) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; + default: + dev_err(&pcidev->dev, "unknown/unsupported device\n"); + return CTI_PORT_TYPE_NONE; + } +} + +/** + * cti_get_port_type_fpga() - Get the port type of a CTI FPGA card + * @priv: Device's private structure + * @pcidev: Pointer to the PCI device for this port + * @port_num: Port to get type of + * + * FPGA based cards port types are based on PCI IDs. + * + * Return: port type on success, CTI_PORT_TYPE_NONE on failure + */ +static enum cti_port_type cti_get_port_type_fpga(struct exar8250 *priv, + struct pci_dev *pcidev, + unsigned int port_num) +{ + switch (pcidev->device) { + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X: + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X: + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16: + return CTI_PORT_TYPE_RS232_422_485_HW; + default: + dev_err(&pcidev->dev, "unknown/unsupported device\n"); + return CTI_PORT_TYPE_NONE; + } +} + +/** + * cti_get_port_type_xr17v35x() - Read port type from the EEPROM + * @priv: Device's private structure + * @pcidev: Pointer to the PCI device for this port + * @port_num: port offset + * + * CTI XR17V35X based cards have the port types stored in the EEPROM. + * This function reads the port type for a single port. + * + * Return: port type on success, CTI_PORT_TYPE_NONE on failure + */ +static enum cti_port_type cti_get_port_type_xr17v35x(struct exar8250 *priv, + struct pci_dev *pcidev, + unsigned int port_num) +{ + enum cti_port_type port_type; + u16 port_flags; + u8 offset; + + offset = CTI_EE_OFF_XR17V35X_PORT_FLAGS + port_num; + eeprom_93cx6_read(&priv->eeprom, offset, &port_flags); + + port_type = FIELD_GET(CTI_EE_MASK_PORT_FLAGS_TYPE, port_flags); + if (CTI_PORT_TYPE_VALID(port_type)) + return port_type; + + /* + * If the port type is missing the card assume it is a + * RS232/RS422/RS485 card to be safe. + * + * There is one known board (BEG013) that only has 3 of 4 port types + * written to the EEPROM so this acts as a work around. + */ + dev_warn(&pcidev->dev, "failed to get port %d type from EEPROM\n", port_num); + + return CTI_PORT_TYPE_RS232_422_485_HW; +} + +static int cti_rs485_config_mpio_tristate(struct uart_port *port, + struct ktermios *termios, + struct serial_rs485 *rs485) +{ + struct exar8250 *priv = (struct exar8250 *)port->private_data; + int ret; + + ret = generic_rs485_config(port, termios, rs485); + if (ret) + return ret; + + // Disable power-on RS485 tri-state via MPIO + return cti_tristate_disable(priv, port->port_id); +} + +static void cti_board_init_osc_freq(struct exar8250 *priv, struct pci_dev *pcidev, u8 eeprom_offset) +{ + int osc_freq; + + osc_freq = cti_read_osc_freq(priv, eeprom_offset); + if (osc_freq <= 0) { + dev_warn(&pcidev->dev, "failed to read OSC freq from EEPROM, using default\n"); + osc_freq = CTI_DEFAULT_PCI_OSC_FREQ; + } + + priv->osc_freq = osc_freq; +} + +static int cti_port_setup_common(struct exar8250 *priv, + struct pci_dev *pcidev, + int idx, unsigned int offset, + struct uart_8250_port *port) +{ + int ret; + + port->port.port_id = idx; + port->port.uartclk = priv->osc_freq; + + ret = serial8250_pci_setup_port(pcidev, port, 0, offset, 0, priv->virt); + if (ret) + return ret; + + port->port.private_data = (void *)priv; + port->port.pm = exar_pm; + port->port.shutdown = exar_shutdown; + + return 0; +} + +static int cti_board_init_fpga(struct exar8250 *priv, struct pci_dev *pcidev) +{ + int ret; + u16 cfg_val; + + // FPGA OSC is fixed to the 33MHz PCI clock + priv->osc_freq = CTI_DEFAULT_FPGA_OSC_FREQ; + + // Enable external interrupts in special cfg space register + ret = pci_read_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, &cfg_val); + if (ret) + return pcibios_err_to_errno(ret); + + cfg_val |= CTI_FPGA_CFG_INT_EN_EXT_BIT; + ret = pci_write_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, cfg_val); + if (ret) + return pcibios_err_to_errno(ret); + + // RS485 gate needs to be enabled; otherwise RTS/CTS will not work + exar_write_reg(priv, CTI_FPGA_RS485_IO_REG, 0x01); + + return 0; +} + +static int cti_port_setup_fpga(struct exar8250 *priv, + struct pci_dev *pcidev, + struct uart_8250_port *port, + int idx) +{ + enum cti_port_type port_type; + unsigned int offset; + int ret; + + if (idx == 0) { + ret = cti_board_init_fpga(priv, pcidev); + if (ret) + return ret; + } + + port_type = cti_get_port_type_fpga(priv, pcidev, idx); + + // FPGA shares port offsets with XR17C15X + offset = idx * UART_EXAR_XR17C15X_PORT_OFFSET; + port->port.type = PORT_XR17D15X; + + port->port.get_divisor = xr17v35x_get_divisor; + port->port.set_divisor = xr17v35x_set_divisor; + port->port.startup = xr17v35x_startup; + + if (CTI_PORT_TYPE_RS485(port_type)) { + port->port.rs485_config = generic_rs485_config; + port->port.rs485_supported = generic_rs485_supported; + } + + return cti_port_setup_common(priv, pcidev, idx, offset, port); +} + +static void cti_board_init_xr17v35x(struct exar8250 *priv, struct pci_dev *pcidev) +{ + // XR17V35X uses the PCIe clock rather than an oscillator + priv->osc_freq = CTI_DEFAULT_PCIE_OSC_FREQ; +} + +static int cti_port_setup_xr17v35x(struct exar8250 *priv, + struct pci_dev *pcidev, + struct uart_8250_port *port, + int idx) +{ + enum cti_port_type port_type; + unsigned int offset; + int ret; + + if (idx == 0) + cti_board_init_xr17v35x(priv, pcidev); + + port_type = cti_get_port_type_xr17v35x(priv, pcidev, idx); + + offset = idx * UART_EXAR_XR17V35X_PORT_OFFSET; + port->port.type = PORT_XR17V35X; + + port->port.get_divisor = xr17v35x_get_divisor; + port->port.set_divisor = xr17v35x_set_divisor; + port->port.startup = xr17v35x_startup; + + switch (port_type) { + case CTI_PORT_TYPE_RS422_485: + case CTI_PORT_TYPE_RS232_422_485_HW: + port->port.rs485_config = cti_rs485_config_mpio_tristate; + port->port.rs485_supported = generic_rs485_supported; + break; + case CTI_PORT_TYPE_RS232_422_485_SW: + case CTI_PORT_TYPE_RS232_422_485_4B: + case CTI_PORT_TYPE_RS232_422_485_2B: + port->port.rs485_config = generic_rs485_config; + port->port.rs485_supported = generic_rs485_supported; + break; + default: + break; + } + + ret = cti_port_setup_common(priv, pcidev, idx, offset, port); + if (ret) + return ret; + + exar_write_reg(priv, (offset + UART_EXAR_8XMODE), 0x00); + exar_write_reg(priv, (offset + UART_EXAR_FCTR), UART_FCTR_EXAR_TRGD); + exar_write_reg(priv, (offset + UART_EXAR_TXTRG), 128); + exar_write_reg(priv, (offset + UART_EXAR_RXTRG), 128); + + return 0; +} + +static void cti_board_init_xr17v25x(struct exar8250 *priv, struct pci_dev *pcidev) +{ + cti_board_init_osc_freq(priv, pcidev, CTI_EE_OFF_XR17V25X_OSC_FREQ); + + /* enable interrupts on cards that need the "PLX fix" */ + switch (pcidev->subsystem_device) { + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B: + cti_plx_int_enable(priv); + break; + default: + break; + } +} + +static int cti_port_setup_xr17v25x(struct exar8250 *priv, + struct pci_dev *pcidev, + struct uart_8250_port *port, + int idx) +{ + enum cti_port_type port_type; + unsigned int offset; + int ret; + + if (idx == 0) + cti_board_init_xr17v25x(priv, pcidev); + + port_type = cti_get_port_type_xr17c15x_xr17v25x(priv, pcidev, idx); + + offset = idx * UART_EXAR_XR17V25X_PORT_OFFSET; + port->port.type = PORT_XR17D15X; + + // XR17V25X supports fractional baudrates + port->port.get_divisor = xr17v35x_get_divisor; + port->port.set_divisor = xr17v35x_set_divisor; + port->port.startup = xr17v35x_startup; + + if (CTI_PORT_TYPE_RS485(port_type)) { + switch (pcidev->subsystem_device) { + // These cards support power on 485 tri-state via MPIO + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485: + port->port.rs485_config = cti_rs485_config_mpio_tristate; + break; + // Otherwise auto or no power on 485 tri-state support + default: + port->port.rs485_config = generic_rs485_config; + break; + } + + port->port.rs485_supported = generic_rs485_supported; + } + + ret = cti_port_setup_common(priv, pcidev, idx, offset, port); + if (ret) + return ret; + + exar_write_reg(priv, (offset + UART_EXAR_8XMODE), 0x00); + exar_write_reg(priv, (offset + UART_EXAR_FCTR), UART_FCTR_EXAR_TRGD); + exar_write_reg(priv, (offset + UART_EXAR_TXTRG), 32); + exar_write_reg(priv, (offset + UART_EXAR_RXTRG), 32); + + return 0; +} + +static void cti_board_init_xr17c15x(struct exar8250 *priv, struct pci_dev *pcidev) +{ + cti_board_init_osc_freq(priv, pcidev, CTI_EE_OFF_XR17C15X_OSC_FREQ); + + /* enable interrupts on cards that need the "PLX fix" */ + switch (pcidev->subsystem_device) { + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B: + cti_plx_int_enable(priv); + break; + default: + break; + } +} + +static int cti_port_setup_xr17c15x(struct exar8250 *priv, + struct pci_dev *pcidev, + struct uart_8250_port *port, + int idx) +{ + enum cti_port_type port_type; + unsigned int offset; + + if (idx == 0) + cti_board_init_xr17c15x(priv, pcidev); + + port_type = cti_get_port_type_xr17c15x_xr17v25x(priv, pcidev, idx); + + offset = idx * UART_EXAR_XR17C15X_PORT_OFFSET; + port->port.type = PORT_XR17D15X; + + if (CTI_PORT_TYPE_RS485(port_type)) { + switch (pcidev->subsystem_device) { + // These cards support power on 485 tri-state via MPIO + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485: + port->port.rs485_config = cti_rs485_config_mpio_tristate; + break; + // Otherwise auto or no power on 485 tri-state support + default: + port->port.rs485_config = generic_rs485_config; + break; + } + + port->port.rs485_supported = generic_rs485_supported; + } + + return cti_port_setup_common(priv, pcidev, idx, offset, port); +} + +static int +pci_xr17c154_setup(struct exar8250 *priv, struct pci_dev *pcidev, + struct uart_8250_port *port, int idx) +{ + unsigned int offset = idx * 0x200; + unsigned int baud = 921600; + + port->port.uartclk = baud * 16; + return default_setup(priv, pcidev, idx, offset, port); +} + +static void setup_gpio(struct pci_dev *pcidev, u8 __iomem *p) +{ + /* + * The Commtech adapters required the MPIOs to be driven low. The Exar + * devices will export them as GPIOs, so we pre-configure them safely + * as inputs. + */ + u8 dir = 0x00; + + if ((pcidev->vendor == PCI_VENDOR_ID_EXAR) && + (pcidev->subsystem_vendor != PCI_VENDOR_ID_SEALEVEL)) { + // Configure GPIO as inputs for Commtech adapters + dir = 0xff; + } else { + // Configure GPIO as outputs for SeaLevel adapters + dir = 0x00; + } + + writeb(0x00, p + UART_EXAR_MPIOINT_7_0); + writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); + writeb(0x00, p + UART_EXAR_MPIO3T_7_0); + writeb(0x00, p + UART_EXAR_MPIOINV_7_0); + writeb(dir, p + UART_EXAR_MPIOSEL_7_0); + writeb(0x00, p + UART_EXAR_MPIOOD_7_0); + writeb(0x00, p + UART_EXAR_MPIOINT_15_8); + writeb(0x00, p + UART_EXAR_MPIOLVL_15_8); + writeb(0x00, p + UART_EXAR_MPIO3T_15_8); + writeb(0x00, p + UART_EXAR_MPIOINV_15_8); + writeb(dir, p + UART_EXAR_MPIOSEL_15_8); + writeb(0x00, p + UART_EXAR_MPIOOD_15_8); +} + +static struct platform_device *__xr17v35x_register_gpio(struct pci_dev *pcidev, + const struct software_node *node) +{ + struct platform_device_info pdevinfo = { + .name = "gpio_exar", + .id = PLATFORM_DEVID_AUTO, + .parent = &pcidev->dev, + .fwnode = dev_fwnode(&pcidev->dev), + .swnode = node, + }; + + return platform_device_register_full(&pdevinfo); +} + +static void __xr17v35x_unregister_gpio(struct platform_device *pdev) +{ + device_remove_software_node(&pdev->dev); + platform_device_unregister(pdev); +} + +static const struct property_entry exar_gpio_properties[] = { + PROPERTY_ENTRY_U32("exar,first-pin", 0), + PROPERTY_ENTRY_U32("ngpios", 16), + { } +}; + +static const struct software_node exar_gpio_node = { + .properties = exar_gpio_properties, +}; + +static int xr17v35x_register_gpio(struct pci_dev *pcidev, struct uart_8250_port *port) +{ + if (pcidev->vendor == PCI_VENDOR_ID_EXAR) + port->port.private_data = + __xr17v35x_register_gpio(pcidev, &exar_gpio_node); + + return 0; +} + +static void xr17v35x_unregister_gpio(struct uart_8250_port *port) +{ + if (!port->port.private_data) + return; + + __xr17v35x_unregister_gpio(port->port.private_data); + port->port.private_data = NULL; +} + +static int sealevel_rs485_config(struct uart_port *port, struct ktermios *termios, + struct serial_rs485 *rs485) +{ + u8 __iomem *p = port->membase; + u8 old_lcr; + u8 efr; + u8 dld; + int ret; + + ret = generic_rs485_config(port, termios, rs485); + if (ret) + return ret; + + if (!(rs485->flags & SER_RS485_ENABLED)) + return 0; + + old_lcr = readb(p + UART_LCR); + + /* Set EFR[4]=1 to enable enhanced feature registers */ + efr = readb(p + UART_XR_EFR); + efr |= UART_EFR_ECB; + writeb(efr, p + UART_XR_EFR); + + /* Set MCR to use DTR as Auto-RS485 Enable signal */ + writeb(UART_MCR_OUT1, p + UART_MCR); + + /* Set LCR[7]=1 to enable access to DLD register */ + writeb(old_lcr | UART_LCR_DLAB, p + UART_LCR); + + /* Set DLD[7]=1 for inverted RS485 Enable logic */ + dld = readb(p + UART_EXAR_DLD); + dld |= UART_EXAR_DLD_485_POLARITY; + writeb(dld, p + UART_EXAR_DLD); + + writeb(old_lcr, p + UART_LCR); + + return 0; +} + +static const struct exar8250_platform exar8250_default_platform = { + .register_gpio = xr17v35x_register_gpio, + .unregister_gpio = xr17v35x_unregister_gpio, + .rs485_config = generic_rs485_config, + .rs485_supported = &generic_rs485_supported, +}; + +static int iot2040_rs485_config(struct uart_port *port, struct ktermios *termios, + struct serial_rs485 *rs485) +{ + bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED); + u8 __iomem *p = port->membase; + u8 mask = IOT2040_UART1_MASK; + u8 mode, value; + + if (is_rs485) { + if (rs485->flags & SER_RS485_RX_DURING_TX) + mode = IOT2040_UART_MODE_RS422; + else + mode = IOT2040_UART_MODE_RS485; + + if (rs485->flags & SER_RS485_TERMINATE_BUS) + mode |= IOT2040_UART_TERMINATE_BUS; + } else { + mode = IOT2040_UART_MODE_RS232; + } + + if (port->line == 3) { + mask <<= IOT2040_UART2_SHIFT; + mode <<= IOT2040_UART2_SHIFT; + } + + value = readb(p + UART_EXAR_MPIOLVL_7_0); + value &= ~mask; + value |= mode; + writeb(value, p + UART_EXAR_MPIOLVL_7_0); + + return generic_rs485_config(port, termios, rs485); +} + +static const struct serial_rs485 iot2040_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | + SER_RS485_RX_DURING_TX | SER_RS485_TERMINATE_BUS, +}; + +static const struct property_entry iot2040_gpio_properties[] = { + PROPERTY_ENTRY_U32("exar,first-pin", 10), + PROPERTY_ENTRY_U32("ngpios", 1), + { } +}; + +static const struct software_node iot2040_gpio_node = { + .properties = iot2040_gpio_properties, +}; + +static int iot2040_register_gpio(struct pci_dev *pcidev, + struct uart_8250_port *port) +{ + u8 __iomem *p = port->port.membase; + + writeb(IOT2040_UARTS_DEFAULT_MODE, p + UART_EXAR_MPIOLVL_7_0); + writeb(IOT2040_UARTS_GPIO_LO_MODE, p + UART_EXAR_MPIOSEL_7_0); + writeb(IOT2040_UARTS_ENABLE, p + UART_EXAR_MPIOLVL_15_8); + writeb(IOT2040_UARTS_GPIO_HI_MODE, p + UART_EXAR_MPIOSEL_15_8); + + port->port.private_data = + __xr17v35x_register_gpio(pcidev, &iot2040_gpio_node); + + return 0; +} + +static const struct exar8250_platform iot2040_platform = { + .rs485_config = iot2040_rs485_config, + .rs485_supported = &iot2040_rs485_supported, + .register_gpio = iot2040_register_gpio, + .unregister_gpio = xr17v35x_unregister_gpio, +}; + +/* + * For SIMATIC IOT2000, only IOT2040 and its variants have the Exar device, + * IOT2020 doesn't have. Therefore it is sufficient to match on the common + * board name after the device was found. + */ +static const struct dmi_system_id exar_platforms[] = { + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"), + }, + .driver_data = (void *)&iot2040_platform, + }, + {} +}; + +static const struct exar8250_platform *exar_get_platform(void) +{ + const struct dmi_system_id *dmi_match; + + dmi_match = dmi_first_match(exar_platforms); + if (dmi_match) + return dmi_match->driver_data; + + return &exar8250_default_platform; +} + +static int +pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, + struct uart_8250_port *port, int idx) +{ + const struct exar8250_platform *platform = exar_get_platform(); + unsigned int offset = idx * 0x400; + unsigned int baud = 7812500; + u8 __iomem *p; + int ret; + + port->port.uartclk = baud * 16; + port->port.rs485_config = platform->rs485_config; + port->port.rs485_supported = *(platform->rs485_supported); + + if (pcidev->subsystem_vendor == PCI_VENDOR_ID_SEALEVEL) + port->port.rs485_config = sealevel_rs485_config; + + /* + * Setup the UART clock for the devices on expansion slot to + * half the clock speed of the main chip (which is 125MHz) + */ + if (idx >= 8) + port->port.uartclk /= 2; + + ret = default_setup(priv, pcidev, idx, offset, port); + if (ret) + return ret; + + p = port->port.membase; + + writeb(0x00, p + UART_EXAR_8XMODE); + writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR); + writeb(128, p + UART_EXAR_TXTRG); + writeb(128, p + UART_EXAR_RXTRG); + + if (idx == 0) { + /* Setup Multipurpose Input/Output pins. */ + setup_gpio(pcidev, p); + + ret = platform->register_gpio(pcidev, port); + } + + return ret; +} + +static void pci_xr17v35x_exit(struct pci_dev *pcidev) +{ + const struct exar8250_platform *platform = exar_get_platform(); + struct exar8250 *priv = pci_get_drvdata(pcidev); + struct uart_8250_port *port = serial8250_get_port(priv->line[0]); + + platform->unregister_gpio(port); +} + +static inline void exar_misc_clear(struct exar8250 *priv) +{ + /* Clear all PCI interrupts by reading INT0. No effect on IIR */ + readb(priv->virt + UART_EXAR_INT0); + + /* Clear INT0 for Expansion Interface slave ports, too */ + if (priv->board->num_ports > 8) + readb(priv->virt + 0x2000 + UART_EXAR_INT0); +} + +/* + * These Exar UARTs have an extra interrupt indicator that could fire for a + * few interrupts that are not presented/cleared through IIR. One of which is + * a wakeup interrupt when coming out of sleep. These interrupts are only + * cleared by reading global INT0 or INT1 registers as interrupts are + * associated with channel 0. The INT[3:0] registers _are_ accessible from each + * channel's address space, but for the sake of bus efficiency we register a + * dedicated handler at the PCI device level to handle them. + */ +static irqreturn_t exar_misc_handler(int irq, void *data) +{ + exar_misc_clear(data); + + return IRQ_HANDLED; +} + +static unsigned int exar_get_nr_ports(struct exar8250_board *board, struct pci_dev *pcidev) +{ + if (pcidev->vendor == PCI_VENDOR_ID_ACCESSIO) + return BIT(((pcidev->device & 0x38) >> 3) - 1); + + // Check if board struct overrides number of ports + if (board->num_ports > 0) + return board->num_ports; + + // Exar encodes # ports in last nibble of PCI Device ID ex. 0358 + if (pcidev->vendor == PCI_VENDOR_ID_EXAR) + return pcidev->device & 0x0f; + + // Handle CTI FPGA cards + if (pcidev->vendor == PCI_VENDOR_ID_CONNECT_TECH) { + switch (pcidev->device) { + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X: + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X: + return 12; + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16: + return 16; + default: + return 0; + } + } + + return 0; +} + +static int +exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) +{ + unsigned int nr_ports, i, bar = 0, maxnr; + struct exar8250_board *board; + struct uart_8250_port uart; + struct exar8250 *priv; + int rc; + + board = (struct exar8250_board *)ent->driver_data; + if (!board) + return -EINVAL; + + rc = pcim_enable_device(pcidev); + if (rc) + return rc; + + maxnr = pci_resource_len(pcidev, bar) >> (board->reg_shift + 3); + + nr_ports = exar_get_nr_ports(board, pcidev); + if (nr_ports == 0) + return dev_err_probe(&pcidev->dev, -ENODEV, "failed to get number of ports\n"); + + priv = devm_kzalloc(&pcidev->dev, struct_size(priv, line, nr_ports), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->board = board; + priv->virt = pcim_iomap(pcidev, bar, 0); + if (!priv->virt) + return -ENOMEM; + + pci_set_master(pcidev); + + rc = pci_alloc_irq_vectors(pcidev, 1, 1, PCI_IRQ_ALL_TYPES); + if (rc < 0) + return rc; + + memset(&uart, 0, sizeof(uart)); + uart.port.flags = UPF_SHARE_IRQ | UPF_EXAR_EFR | UPF_FIXED_TYPE | UPF_FIXED_PORT; + uart.port.irq = pci_irq_vector(pcidev, 0); + uart.port.dev = &pcidev->dev; + + /* Clear interrupts */ + exar_misc_clear(priv); + + rc = devm_request_irq(&pcidev->dev, uart.port.irq, exar_misc_handler, + IRQF_SHARED, "exar_uart", priv); + if (rc) + return rc; + + exar_eeprom_init(priv); + + for (i = 0; i < nr_ports && i < maxnr; i++) { + rc = board->setup(priv, pcidev, &uart, i); + if (rc) { + dev_err_probe(&pcidev->dev, rc, "Failed to setup port %u\n", i); + break; + } + + dev_dbg(&pcidev->dev, "Setup PCI port: port %lx, irq %d, type %d\n", + uart.port.iobase, uart.port.irq, uart.port.iotype); + + priv->line[i] = serial8250_register_8250_port(&uart); + if (priv->line[i] < 0) { + dev_err_probe(&pcidev->dev, priv->line[i], + "Couldn't register serial port %lx, type %d, irq %d\n", + uart.port.iobase, uart.port.iotype, uart.port.irq); + break; + } + } + priv->nr = i; + pci_set_drvdata(pcidev, priv); + return 0; +} + +static void exar_pci_remove(struct pci_dev *pcidev) +{ + struct exar8250 *priv = pci_get_drvdata(pcidev); + unsigned int i; + + for (i = 0; i < priv->nr; i++) + serial8250_unregister_port(priv->line[i]); + + /* Ensure that every init quirk is properly torn down */ + if (priv->board->exit) + priv->board->exit(pcidev); +} + +static int exar_suspend(struct device *dev) +{ + struct exar8250 *priv = dev_get_drvdata(dev); + unsigned int i; + + for (i = 0; i < priv->nr; i++) + if (priv->line[i] >= 0) + serial8250_suspend_port(priv->line[i]); + + return 0; +} + +static int exar_resume(struct device *dev) +{ + struct exar8250 *priv = dev_get_drvdata(dev); + unsigned int i; + + exar_misc_clear(priv); + + for (i = 0; i < priv->nr; i++) + if (priv->line[i] >= 0) + serial8250_resume_port(priv->line[i]); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(exar_pci_pm, exar_suspend, exar_resume); + +static const struct exar8250_board pbn_fastcom335_2 = { + .num_ports = 2, + .setup = pci_fastcom335_setup, +}; + +static const struct exar8250_board pbn_fastcom335_4 = { + .num_ports = 4, + .setup = pci_fastcom335_setup, +}; + +static const struct exar8250_board pbn_fastcom335_8 = { + .num_ports = 8, + .setup = pci_fastcom335_setup, +}; + +static const struct exar8250_board pbn_cti_xr17c15x = { + .setup = cti_port_setup_xr17c15x, +}; + +static const struct exar8250_board pbn_cti_xr17v25x = { + .setup = cti_port_setup_xr17v25x, +}; + +static const struct exar8250_board pbn_cti_xr17v35x = { + .setup = cti_port_setup_xr17v35x, +}; + +static const struct exar8250_board pbn_cti_fpga = { + .setup = cti_port_setup_fpga, +}; + +static const struct exar8250_board pbn_exar_ibm_saturn = { + .num_ports = 1, + .setup = pci_xr17c154_setup, +}; + +static const struct exar8250_board pbn_exar_XR17C15x = { + .setup = pci_xr17c154_setup, +}; + +static const struct exar8250_board pbn_exar_XR17V35x = { + .setup = pci_xr17v35x_setup, + .exit = pci_xr17v35x_exit, +}; + +static const struct exar8250_board pbn_fastcom35x_2 = { + .num_ports = 2, + .setup = pci_xr17v35x_setup, + .exit = pci_xr17v35x_exit, +}; + +static const struct exar8250_board pbn_fastcom35x_4 = { + .num_ports = 4, + .setup = pci_xr17v35x_setup, + .exit = pci_xr17v35x_exit, +}; + +static const struct exar8250_board pbn_fastcom35x_8 = { + .num_ports = 8, + .setup = pci_xr17v35x_setup, + .exit = pci_xr17v35x_exit, +}; + +static const struct exar8250_board pbn_adv_XR17V352 = { + .num_ports = 2, + .setup = pci_xr17v35x_setup, + .exit = pci_xr17v35x_exit, +}; + +static const struct exar8250_board pbn_exar_XR17V4358 = { + .num_ports = 12, + .setup = pci_xr17v35x_setup, + .exit = pci_xr17v35x_exit, +}; + +static const struct exar8250_board pbn_exar_XR17V8358 = { + .num_ports = 16, + .setup = pci_xr17v35x_setup, + .exit = pci_xr17v35x_exit, +}; + +#define CTI_EXAR_DEVICE(devid, bd) { \ + PCI_DEVICE_SUB( \ + PCI_VENDOR_ID_EXAR, \ + PCI_DEVICE_ID_EXAR_##devid, \ + PCI_SUBVENDOR_ID_CONNECT_TECH, \ + PCI_ANY_ID), \ + .driver_data = (kernel_ulong_t)&bd \ +} + +#define EXAR_DEVICE(vend, devid, bd) { PCI_DEVICE_DATA(vend, devid, &bd) } + +#define IBM_DEVICE(devid, sdevid, bd) { \ + PCI_DEVICE_SUB( \ + PCI_VENDOR_ID_EXAR, \ + PCI_DEVICE_ID_EXAR_##devid, \ + PCI_SUBVENDOR_ID_IBM, \ + PCI_SUBDEVICE_ID_IBM_##sdevid), \ + .driver_data = (kernel_ulong_t)&bd \ +} + +#define USR_DEVICE(devid, sdevid, bd) { \ + PCI_DEVICE_SUB( \ + PCI_VENDOR_ID_USR, \ + PCI_DEVICE_ID_EXAR_##devid, \ + PCI_VENDOR_ID_EXAR, \ + PCI_SUBDEVICE_ID_USR_##sdevid), \ + .driver_data = (kernel_ulong_t)&bd \ +} + +static const struct pci_device_id exar_pci_tbl[] = { + EXAR_DEVICE(ACCESSIO, COM_2S, pbn_exar_XR17C15x), + EXAR_DEVICE(ACCESSIO, COM_4S, pbn_exar_XR17C15x), + EXAR_DEVICE(ACCESSIO, COM_8S, pbn_exar_XR17C15x), + EXAR_DEVICE(ACCESSIO, COM232_8, pbn_exar_XR17C15x), + EXAR_DEVICE(ACCESSIO, COM_2SM, pbn_exar_XR17C15x), + EXAR_DEVICE(ACCESSIO, COM_4SM, pbn_exar_XR17C15x), + EXAR_DEVICE(ACCESSIO, COM_8SM, pbn_exar_XR17C15x), + + /* Connect Tech cards with Exar vendor/device PCI IDs */ + CTI_EXAR_DEVICE(XR17C152, pbn_cti_xr17c15x), + CTI_EXAR_DEVICE(XR17C154, pbn_cti_xr17c15x), + CTI_EXAR_DEVICE(XR17C158, pbn_cti_xr17c15x), + + CTI_EXAR_DEVICE(XR17V252, pbn_cti_xr17v25x), + CTI_EXAR_DEVICE(XR17V254, pbn_cti_xr17v25x), + CTI_EXAR_DEVICE(XR17V258, pbn_cti_xr17v25x), + + CTI_EXAR_DEVICE(XR17V352, pbn_cti_xr17v35x), + CTI_EXAR_DEVICE(XR17V354, pbn_cti_xr17v35x), + CTI_EXAR_DEVICE(XR17V358, pbn_cti_xr17v35x), + + /* Connect Tech cards with Connect Tech vendor/device PCI IDs (FPGA based) */ + EXAR_DEVICE(CONNECT_TECH, PCI_XR79X_12_XIG00X, pbn_cti_fpga), + EXAR_DEVICE(CONNECT_TECH, PCI_XR79X_12_XIG01X, pbn_cti_fpga), + EXAR_DEVICE(CONNECT_TECH, PCI_XR79X_16, pbn_cti_fpga), + + IBM_DEVICE(XR17C152, SATURN_SERIAL_ONE_PORT, pbn_exar_ibm_saturn), + + /* USRobotics USR298x-OEM PCI Modems */ + USR_DEVICE(XR17C152, 2980, pbn_exar_XR17C15x), + USR_DEVICE(XR17C152, 2981, pbn_exar_XR17C15x), + + /* ADVANTECH devices */ + EXAR_DEVICE(ADVANTECH, XR17V352, pbn_adv_XR17V352), + + /* Exar Corp. XR17C15[248] Dual/Quad/Octal UART */ + EXAR_DEVICE(EXAR, XR17C152, pbn_exar_XR17C15x), + EXAR_DEVICE(EXAR, XR17C154, pbn_exar_XR17C15x), + EXAR_DEVICE(EXAR, XR17C158, pbn_exar_XR17C15x), + + /* Exar Corp. XR17V[48]35[248] Dual/Quad/Octal/Hexa PCIe UARTs */ + EXAR_DEVICE(EXAR, XR17V352, pbn_exar_XR17V35x), + EXAR_DEVICE(EXAR, XR17V354, pbn_exar_XR17V35x), + EXAR_DEVICE(EXAR, XR17V358, pbn_exar_XR17V35x), + EXAR_DEVICE(EXAR, XR17V4358, pbn_exar_XR17V4358), + EXAR_DEVICE(EXAR, XR17V8358, pbn_exar_XR17V8358), + EXAR_DEVICE(COMMTECH, 4222PCIE, pbn_fastcom35x_2), + EXAR_DEVICE(COMMTECH, 4224PCIE, pbn_fastcom35x_4), + EXAR_DEVICE(COMMTECH, 4228PCIE, pbn_fastcom35x_8), + + EXAR_DEVICE(COMMTECH, 4222PCI335, pbn_fastcom335_2), + EXAR_DEVICE(COMMTECH, 4224PCI335, pbn_fastcom335_4), + EXAR_DEVICE(COMMTECH, 2324PCI335, pbn_fastcom335_4), + EXAR_DEVICE(COMMTECH, 2328PCI335, pbn_fastcom335_8), + { } +}; +MODULE_DEVICE_TABLE(pci, exar_pci_tbl); + +static struct pci_driver exar_pci_driver = { + .name = "exar_serial", + .probe = exar_pci_probe, + .remove = exar_pci_remove, + .driver = { + .pm = pm_sleep_ptr(&exar_pci_pm), + }, + .id_table = exar_pci_tbl, +}; +module_pci_driver(exar_pci_driver); + +MODULE_IMPORT_NS("SERIAL_8250_PCI"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Exar Serial Driver"); +MODULE_AUTHOR("Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>"); diff --git a/drivers/tty/serial/8250/8250_exar_st16c554.c b/drivers/tty/serial/8250/8250_exar_st16c554.c new file mode 100644 index 000000000..933811ebf --- /dev/null +++ b/drivers/tty/serial/8250/8250_exar_st16c554.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Written by Paul B Schroeder < pschroeder "at" uplogix "dot" com > + * Based on 8250_boca. + * + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/serial_8250.h> + +#include "8250.h" + +static struct plat_serial8250_port exar_data[] = { + SERIAL8250_PORT(0x100, 5), + SERIAL8250_PORT(0x108, 5), + SERIAL8250_PORT(0x110, 5), + SERIAL8250_PORT(0x118, 5), + { }, +}; + +static struct platform_device exar_device = { + .name = "serial8250", + .id = PLAT8250_DEV_EXAR_ST16C554, + .dev = { + .platform_data = exar_data, + }, +}; + +static int __init exar_init(void) +{ + return platform_device_register(&exar_device); +} + +module_init(exar_init); + +MODULE_AUTHOR("Paul B Schroeder"); +MODULE_DESCRIPTION("8250 serial probe module for Exar cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c new file mode 100644 index 000000000..976c57489 --- /dev/null +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Probe for F81216A LPC to 4 UART and F81214E LPC/eSPI to 2 UART + * + * Copyright (C) 2014-2016 Ricardo Ribalda, Qtechnology A/S + */ +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/pnp.h> +#include <linux/kernel.h> +#include <linux/serial_core.h> +#include <linux/irq.h> +#include "8250.h" + +#define ADDR_PORT 0 +#define DATA_PORT 1 +#define EXIT_KEY 0xAA +#define CHIP_ID1 0x20 +#define CHIP_ID2 0x21 +#define CHIP_ID_F81865 0x0407 +#define CHIP_ID_F81866 0x1010 +#define CHIP_ID_F81966 0x0215 +#define CHIP_ID_F81216AD 0x1602 +#define CHIP_ID_F81216E 0x1617 +#define CHIP_ID_F81216H 0x0501 +#define CHIP_ID_F81214E 0x1417 +#define CHIP_ID_F81216 0x0802 +#define VENDOR_ID1 0x23 +#define VENDOR_ID1_VAL 0x19 +#define VENDOR_ID2 0x24 +#define VENDOR_ID2_VAL 0x34 +#define IO_ADDR1 0x61 +#define IO_ADDR2 0x60 +#define LDN 0x7 + +#define FINTEK_IRQ_MODE 0x70 +#define IRQ_SHARE BIT(4) +#define IRQ_MODE_MASK (BIT(6) | BIT(5)) +#define IRQ_LEVEL_LOW 0 +#define IRQ_EDGE_HIGH BIT(5) + +/* + * F81216H clock source register, the value and mask is the same with F81866, + * but it's on F0h. + * + * Clock speeds for UART (register F0h) + * 00: 1.8432MHz. + * 01: 18.432MHz. + * 10: 24MHz. + * 11: 14.769MHz. + */ +#define RS485 0xF0 +#define RTS_INVERT BIT(5) +#define RS485_URA BIT(4) +#define RXW4C_IRA BIT(3) +#define TXW4C_IRA BIT(2) + +#define FIFO_CTRL 0xF6 +#define FIFO_MODE_MASK (BIT(1) | BIT(0)) +#define FIFO_MODE_128 (BIT(1) | BIT(0)) +#define RXFTHR_MODE_MASK (BIT(5) | BIT(4)) +#define RXFTHR_MODE_4X BIT(5) + +#define F81216_LDN_LOW 0x0 +#define F81216_LDN_HIGH 0x4 + +/* + * F81866/966 registers + * + * The IRQ setting mode of F81866/966 is not the same with F81216 series. + * Level/Low: IRQ_MODE0:0, IRQ_MODE1:0 + * Edge/High: IRQ_MODE0:1, IRQ_MODE1:0 + * + * Clock speeds for UART (register F2h) + * 00: 1.8432MHz. + * 01: 18.432MHz. + * 10: 24MHz. + * 11: 14.769MHz. + */ +#define F81866_IRQ_MODE 0xf0 +#define F81866_IRQ_SHARE BIT(0) +#define F81866_IRQ_MODE0 BIT(1) + +#define F81866_FIFO_CTRL FIFO_CTRL +#define F81866_IRQ_MODE1 BIT(3) + +#define F81866_LDN_LOW 0x10 +#define F81866_LDN_HIGH 0x16 + +#define F81866_UART_CLK 0xF2 +#define F81866_UART_CLK_MASK (BIT(1) | BIT(0)) +#define F81866_UART_CLK_1_8432MHZ 0 +#define F81866_UART_CLK_14_769MHZ (BIT(1) | BIT(0)) +#define F81866_UART_CLK_18_432MHZ BIT(0) +#define F81866_UART_CLK_24MHZ BIT(1) + +struct fintek_8250 { + u16 pid; + u16 base_port; + u8 index; + u8 key; +}; + +static u8 sio_read_reg(struct fintek_8250 *pdata, u8 reg) +{ + outb(reg, pdata->base_port + ADDR_PORT); + return inb(pdata->base_port + DATA_PORT); +} + +static void sio_write_reg(struct fintek_8250 *pdata, u8 reg, u8 data) +{ + outb(reg, pdata->base_port + ADDR_PORT); + outb(data, pdata->base_port + DATA_PORT); +} + +static void sio_write_mask_reg(struct fintek_8250 *pdata, u8 reg, u8 mask, + u8 data) +{ + u8 tmp; + + tmp = (sio_read_reg(pdata, reg) & ~mask) | (mask & data); + sio_write_reg(pdata, reg, tmp); +} + +static int fintek_8250_enter_key(u16 base_port, u8 key) +{ + if (!request_muxed_region(base_port, 2, "8250_fintek")) + return -EBUSY; + + /* Force to deactivate all SuperIO in this base_port */ + outb(EXIT_KEY, base_port + ADDR_PORT); + + outb(key, base_port + ADDR_PORT); + outb(key, base_port + ADDR_PORT); + return 0; +} + +static void fintek_8250_exit_key(u16 base_port) +{ + + outb(EXIT_KEY, base_port + ADDR_PORT); + release_region(base_port + ADDR_PORT, 2); +} + +static int fintek_8250_check_id(struct fintek_8250 *pdata) +{ + u16 chip; + + if (sio_read_reg(pdata, VENDOR_ID1) != VENDOR_ID1_VAL) + return -ENODEV; + + if (sio_read_reg(pdata, VENDOR_ID2) != VENDOR_ID2_VAL) + return -ENODEV; + + chip = sio_read_reg(pdata, CHIP_ID1); + chip |= sio_read_reg(pdata, CHIP_ID2) << 8; + + switch (chip) { + case CHIP_ID_F81865: + case CHIP_ID_F81866: + case CHIP_ID_F81966: + case CHIP_ID_F81216AD: + case CHIP_ID_F81216E: + case CHIP_ID_F81216H: + case CHIP_ID_F81214E: + case CHIP_ID_F81216: + break; + default: + return -ENODEV; + } + + pdata->pid = chip; + return 0; +} + +static int fintek_8250_get_ldn_range(struct fintek_8250 *pdata, int *min, + int *max) +{ + switch (pdata->pid) { + case CHIP_ID_F81966: + case CHIP_ID_F81865: + case CHIP_ID_F81866: + *min = F81866_LDN_LOW; + *max = F81866_LDN_HIGH; + return 0; + + case CHIP_ID_F81216AD: + case CHIP_ID_F81216E: + case CHIP_ID_F81216H: + case CHIP_ID_F81214E: + case CHIP_ID_F81216: + *min = F81216_LDN_LOW; + *max = F81216_LDN_HIGH; + return 0; + } + + return -ENODEV; +} + +static int fintek_8250_rs485_config(struct uart_port *port, struct ktermios *termios, + struct serial_rs485 *rs485) +{ + uint8_t config = 0; + struct fintek_8250 *pdata = port->private_data; + + if (!pdata) + return -EINVAL; + + + if (rs485->flags & SER_RS485_ENABLED) { + /* Hardware do not support same RTS level on send and receive */ + if (!(rs485->flags & SER_RS485_RTS_ON_SEND) == + !(rs485->flags & SER_RS485_RTS_AFTER_SEND)) + return -EINVAL; + config |= RS485_URA; + } + + if (rs485->delay_rts_before_send) { + rs485->delay_rts_before_send = 1; + config |= TXW4C_IRA; + } + + if (rs485->delay_rts_after_send) { + rs485->delay_rts_after_send = 1; + config |= RXW4C_IRA; + } + + if (rs485->flags & SER_RS485_RTS_ON_SEND) + config |= RTS_INVERT; + + if (fintek_8250_enter_key(pdata->base_port, pdata->key)) + return -EBUSY; + + sio_write_reg(pdata, LDN, pdata->index); + sio_write_reg(pdata, RS485, config); + fintek_8250_exit_key(pdata->base_port); + + return 0; +} + +static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) +{ + sio_write_reg(pdata, LDN, pdata->index); + + switch (pdata->pid) { + case CHIP_ID_F81966: + case CHIP_ID_F81866: + sio_write_mask_reg(pdata, F81866_FIFO_CTRL, F81866_IRQ_MODE1, + 0); + fallthrough; + case CHIP_ID_F81865: + sio_write_mask_reg(pdata, F81866_IRQ_MODE, F81866_IRQ_SHARE, + F81866_IRQ_SHARE); + sio_write_mask_reg(pdata, F81866_IRQ_MODE, F81866_IRQ_MODE0, + is_level ? 0 : F81866_IRQ_MODE0); + break; + + case CHIP_ID_F81216AD: + case CHIP_ID_F81216E: + case CHIP_ID_F81216H: + case CHIP_ID_F81214E: + case CHIP_ID_F81216: + sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_SHARE, + IRQ_SHARE); + sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_MODE_MASK, + is_level ? IRQ_LEVEL_LOW : IRQ_EDGE_HIGH); + break; + } +} + +static void fintek_8250_set_max_fifo(struct fintek_8250 *pdata) +{ + switch (pdata->pid) { + case CHIP_ID_F81216E: /* 128Bytes FIFO */ + case CHIP_ID_F81216H: + case CHIP_ID_F81214E: + case CHIP_ID_F81966: + case CHIP_ID_F81866: + sio_write_mask_reg(pdata, FIFO_CTRL, + FIFO_MODE_MASK | RXFTHR_MODE_MASK, + FIFO_MODE_128 | RXFTHR_MODE_4X); + break; + + default: /* Default 16Bytes FIFO */ + break; + } +} + +static void fintek_8250_set_termios(struct uart_port *port, + struct ktermios *termios, + const struct ktermios *old) +{ + struct fintek_8250 *pdata = port->private_data; + unsigned int baud = tty_termios_baud_rate(termios); + int i; + u8 reg; + static u32 baudrate_table[] = {115200, 921600, 1152000, 1500000}; + static u8 clock_table[] = { F81866_UART_CLK_1_8432MHZ, + F81866_UART_CLK_14_769MHZ, F81866_UART_CLK_18_432MHZ, + F81866_UART_CLK_24MHZ }; + + /* + * We'll use serial8250_do_set_termios() for baud = 0, otherwise It'll + * crash on baudrate_table[i] % baud with "division by zero". + */ + if (!baud) + goto exit; + + switch (pdata->pid) { + case CHIP_ID_F81216E: + case CHIP_ID_F81216H: + case CHIP_ID_F81214E: + reg = RS485; + break; + case CHIP_ID_F81966: + case CHIP_ID_F81866: + reg = F81866_UART_CLK; + break; + default: + /* Don't change clocksource with unknown PID */ + dev_warn(port->dev, + "%s: pid: %x Not support. use default set_termios.\n", + __func__, pdata->pid); + goto exit; + } + + for (i = 0; i < ARRAY_SIZE(baudrate_table); ++i) { + if (baud > baudrate_table[i] || baudrate_table[i] % baud != 0) + continue; + + if (port->uartclk == baudrate_table[i] * 16) + break; + + if (fintek_8250_enter_key(pdata->base_port, pdata->key)) + continue; + + port->uartclk = baudrate_table[i] * 16; + + sio_write_reg(pdata, LDN, pdata->index); + sio_write_mask_reg(pdata, reg, F81866_UART_CLK_MASK, + clock_table[i]); + + fintek_8250_exit_key(pdata->base_port); + break; + } + + if (i == ARRAY_SIZE(baudrate_table)) { + baud = tty_termios_baud_rate(old); + tty_termios_encode_baud_rate(termios, baud, baud); + } + +exit: + serial8250_do_set_termios(port, termios, old); +} + +static void fintek_8250_set_termios_handler(struct uart_8250_port *uart) +{ + struct fintek_8250 *pdata = uart->port.private_data; + + switch (pdata->pid) { + case CHIP_ID_F81216E: + case CHIP_ID_F81216H: + case CHIP_ID_F81214E: + case CHIP_ID_F81966: + case CHIP_ID_F81866: + uart->port.set_termios = fintek_8250_set_termios; + break; + + default: + break; + } +} + +static int probe_setup_port(struct fintek_8250 *pdata, + struct uart_8250_port *uart) +{ + static const u16 addr[] = {0x4e, 0x2e}; + static const u8 keys[] = {0x77, 0xa0, 0x87, 0x67}; + struct irq_data *irq_data; + bool level_mode = false; + int i, j, k, min, max; + + for (i = 0; i < ARRAY_SIZE(addr); i++) { + for (j = 0; j < ARRAY_SIZE(keys); j++) { + pdata->base_port = addr[i]; + pdata->key = keys[j]; + + if (fintek_8250_enter_key(addr[i], keys[j])) + continue; + if (fintek_8250_check_id(pdata) || + fintek_8250_get_ldn_range(pdata, &min, &max)) { + fintek_8250_exit_key(addr[i]); + continue; + } + + for (k = min; k < max; k++) { + u16 aux; + + sio_write_reg(pdata, LDN, k); + aux = sio_read_reg(pdata, IO_ADDR1); + aux |= sio_read_reg(pdata, IO_ADDR2) << 8; + if (aux != uart->port.iobase) + continue; + + pdata->index = k; + + irq_data = irq_get_irq_data(uart->port.irq); + if (irq_data) + level_mode = + irqd_is_level_type(irq_data); + + fintek_8250_set_irq_mode(pdata, level_mode); + fintek_8250_set_max_fifo(pdata); + + fintek_8250_exit_key(addr[i]); + + return 0; + } + + fintek_8250_exit_key(addr[i]); + } + } + + return -ENODEV; +} + +/* Only the first port supports delays */ +static const struct serial_rs485 fintek_8250_rs485_supported_port0 = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, +}; + +static const struct serial_rs485 fintek_8250_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, +}; + +static void fintek_8250_set_rs485_handler(struct uart_8250_port *uart) +{ + struct fintek_8250 *pdata = uart->port.private_data; + + switch (pdata->pid) { + case CHIP_ID_F81216AD: + case CHIP_ID_F81216H: + case CHIP_ID_F81966: + case CHIP_ID_F81866: + case CHIP_ID_F81865: + uart->port.rs485_config = fintek_8250_rs485_config; + if (!pdata->index) + uart->port.rs485_supported = fintek_8250_rs485_supported_port0; + else + uart->port.rs485_supported = fintek_8250_rs485_supported; + break; + + case CHIP_ID_F81216E: /* F81216E does not support RS485 delays */ + case CHIP_ID_F81214E: /* F81214E does not support RS485 delays */ + uart->port.rs485_config = fintek_8250_rs485_config; + uart->port.rs485_supported = fintek_8250_rs485_supported; + break; + + default: /* No RS485 Auto direction functional */ + break; + } +} + +int fintek_8250_probe(struct uart_8250_port *uart) +{ + struct fintek_8250 *pdata; + struct fintek_8250 probe_data; + + if (probe_setup_port(&probe_data, uart)) + return -ENODEV; + + pdata = devm_kzalloc(uart->port.dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + memcpy(pdata, &probe_data, sizeof(probe_data)); + uart->port.private_data = pdata; + fintek_8250_set_rs485_handler(uart); + fintek_8250_set_termios_handler(uart); + + return 0; +} diff --git a/drivers/tty/serial/8250/8250_fourport.c b/drivers/tty/serial/8250/8250_fourport.c new file mode 100644 index 000000000..3215b9b7a --- /dev/null +++ b/drivers/tty/serial/8250/8250_fourport.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/serial_8250.h> + +#include "8250.h" + +#define SERIAL8250_FOURPORT(_base, _irq) \ + SERIAL8250_PORT_FLAGS(_base, _irq, UPF_FOURPORT) + +static struct plat_serial8250_port fourport_data[] = { + SERIAL8250_FOURPORT(0x1a0, 9), + SERIAL8250_FOURPORT(0x1a8, 9), + SERIAL8250_FOURPORT(0x1b0, 9), + SERIAL8250_FOURPORT(0x1b8, 9), + SERIAL8250_FOURPORT(0x2a0, 5), + SERIAL8250_FOURPORT(0x2a8, 5), + SERIAL8250_FOURPORT(0x2b0, 5), + SERIAL8250_FOURPORT(0x2b8, 5), + { }, +}; + +static struct platform_device fourport_device = { + .name = "serial8250", + .id = PLAT8250_DEV_FOURPORT, + .dev = { + .platform_data = fourport_data, + }, +}; + +static int __init fourport_init(void) +{ + return platform_device_register(&fourport_device); +} + +module_init(fourport_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for AST Fourport cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c new file mode 100644 index 000000000..649ae5c83 --- /dev/null +++ b/drivers/tty/serial/8250/8250_fsl.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Freescale 16550 UART "driver", Copyright (C) 2011 Paul Gortmaker. + * Copyright 2020 NXP + * Copyright 2020 Puresoftware Ltd. + * + * This isn't a full driver; it just provides an alternate IRQ + * handler to deal with an errata and provide ACPI wrapper. + * Everything else is just using the bog standard 8250 support. + * + * We follow code flow of serial8250_default_handle_irq() but add + * a check for a break and insert a dummy read on the Rx for the + * immediately following IRQ event. + * + * We re-use the already existing "bug handling" lsr_saved_flags + * field to carry the "what we just did" information from the one + * IRQ event to the next one. + */ + +#include <linux/acpi.h> +#include <linux/serial_reg.h> +#include <linux/serial_8250.h> + +#include "8250.h" + +int fsl8250_handle_irq(struct uart_port *port) +{ + unsigned long flags; + u16 lsr, orig_lsr; + unsigned int iir; + struct uart_8250_port *up = up_to_u8250p(port); + + uart_port_lock_irqsave(&up->port, &flags); + + iir = serial_port_in(port, UART_IIR); + if (iir & UART_IIR_NO_INT) { + uart_port_unlock_irqrestore(&up->port, flags); + return 0; + } + + /* + * For a single break the hardware reports LSR.BI for each character + * time. This is described in the MPC8313E chip errata as "General17". + * A typical break has a duration of 0.3s, with a 115200n8 configuration + * that (theoretically) corresponds to ~3500 interrupts in these 0.3s. + * In practise it's less (around 500) because of hardware + * and software latencies. The workaround recommended by the vendor is + * to read the RX register (to clear LSR.DR and thus prevent a FIFO + * aging interrupt). To prevent the irq from retriggering LSR must not be + * read. (This would clear LSR.BI, hardware would reassert the BI event + * immediately and interrupt the CPU again. The hardware clears LSR.BI + * when the next valid char is read.) + */ + if (unlikely((iir & UART_IIR_ID) == UART_IIR_RLSI && + (up->lsr_saved_flags & UART_LSR_BI))) { + up->lsr_saved_flags &= ~UART_LSR_BI; + serial_port_in(port, UART_RX); + uart_port_unlock_irqrestore(&up->port, flags); + return 1; + } + + lsr = orig_lsr = serial_port_in(port, UART_LSR); + + /* Process incoming characters first */ + if ((lsr & (UART_LSR_DR | UART_LSR_BI)) && + (up->ier & (UART_IER_RLSI | UART_IER_RDI))) { + lsr = serial8250_rx_chars(up, lsr); + } + + /* Stop processing interrupts on input overrun */ + if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) { + unsigned long delay; + + up->ier = serial_port_in(port, UART_IER); + if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { + port->ops->stop_rx(port); + } else { + /* Keep restarting the timer until + * the input overrun subsides. + */ + cancel_delayed_work(&up->overrun_backoff); + } + + delay = msecs_to_jiffies(up->overrun_backoff_time_ms); + schedule_delayed_work(&up->overrun_backoff, delay); + } + + serial8250_modem_status(up); + + if ((lsr & UART_LSR_THRE) && (up->ier & UART_IER_THRI)) + serial8250_tx_chars(up); + + up->lsr_saved_flags |= orig_lsr & UART_LSR_BI; + + uart_unlock_and_check_sysrq_irqrestore(&up->port, flags); + + return 1; +} +EXPORT_SYMBOL_GPL(fsl8250_handle_irq); + +#ifdef CONFIG_ACPI +struct fsl8250_data { + int line; +}; + +static int fsl8250_acpi_probe(struct platform_device *pdev) +{ + struct fsl8250_data *data; + struct uart_8250_port port8250; + struct device *dev = &pdev->dev; + struct resource *regs; + + int ret, irq; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(dev, "no registers defined\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + memset(&port8250, 0, sizeof(port8250)); + + ret = device_property_read_u32(dev, "clock-frequency", + &port8250.port.uartclk); + if (ret) + return ret; + + spin_lock_init(&port8250.port.lock); + + port8250.port.mapbase = regs->start; + port8250.port.irq = irq; + port8250.port.handle_irq = fsl8250_handle_irq; + port8250.port.type = PORT_16550A; + port8250.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF + | UPF_FIXED_PORT | UPF_IOREMAP + | UPF_FIXED_TYPE; + port8250.port.dev = dev; + port8250.port.mapsize = resource_size(regs); + port8250.port.iotype = UPIO_MEM; + port8250.port.irqflags = IRQF_SHARED; + + port8250.port.membase = devm_ioremap(dev, port8250.port.mapbase, + port8250.port.mapsize); + if (!port8250.port.membase) + return -ENOMEM; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->line = serial8250_register_8250_port(&port8250); + if (data->line < 0) + return data->line; + + platform_set_drvdata(pdev, data); + return 0; +} + +static void fsl8250_acpi_remove(struct platform_device *pdev) +{ + struct fsl8250_data *data = platform_get_drvdata(pdev); + + serial8250_unregister_port(data->line); +} + +static const struct acpi_device_id fsl_8250_acpi_id[] = { + { "NXP0018", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, fsl_8250_acpi_id); + +static struct platform_driver fsl8250_platform_driver = { + .driver = { + .name = "fsl-16550-uart", + .acpi_match_table = ACPI_PTR(fsl_8250_acpi_id), + }, + .probe = fsl8250_acpi_probe, + .remove = fsl8250_acpi_remove, +}; + +module_platform_driver(fsl8250_platform_driver); +#endif + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Handling of Freescale specific 8250 variants"); diff --git a/drivers/tty/serial/8250/8250_hp300.c b/drivers/tty/serial/8250/8250_hp300.c new file mode 100644 index 000000000..59ec0c737 --- /dev/null +++ b/drivers/tty/serial/8250/8250_hp300.c @@ -0,0 +1,324 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the 98626/98644/internal serial interface on hp300/hp400 + * (based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs) + * + * Ported from 2.2 and modified to use the normal 8250 driver + * by Kars de Jong <jongk@linux-m68k.org>, May 2004. + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/serial.h> +#include <linux/serial_8250.h> +#include <linux/delay.h> +#include <linux/dio.h> +#include <linux/console.h> +#include <linux/slab.h> +#include <asm/io.h> + +#include "8250.h" + +#if !defined(CONFIG_HPDCA) && !defined(CONFIG_HPAPCI) && !defined(CONFIG_COMPILE_TEST) +#warning CONFIG_SERIAL_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure? +#endif + +#ifdef CONFIG_HPAPCI +struct hp300_port { + struct hp300_port *next; /* next port */ + int line; /* line (tty) number */ +}; + +static struct hp300_port *hp300_ports; +#endif + +#ifdef CONFIG_HPDCA + +static int hpdca_init_one(struct dio_dev *d, + const struct dio_device_id *ent); +static void hpdca_remove_one(struct dio_dev *d); + +static struct dio_device_id hpdca_dio_tbl[] = { + { DIO_ID_DCA0 }, + { DIO_ID_DCA0REM }, + { DIO_ID_DCA1 }, + { DIO_ID_DCA1REM }, + { 0 } +}; + +static struct dio_driver hpdca_driver = { + .name = "hpdca", + .id_table = hpdca_dio_tbl, + .probe = hpdca_init_one, + .remove = hpdca_remove_one, +}; + +#endif + +static unsigned int num_ports; + +extern int hp300_uart_scode; + +/* Offset to UART registers from base of DCA */ +#define UART_OFFSET 17 + +#define DCA_ID 0x01 /* ID (read), reset (write) */ +#define DCA_IC 0x03 /* Interrupt control */ + +/* Interrupt control */ +#define DCA_IC_IE 0x80 /* Master interrupt enable */ + +#define HPDCA_BAUD_BASE 153600 + +/* Base address of the Frodo part */ +#define FRODO_BASE (0x41c000) + +/* + * Where we find the 8250-like APCI ports, and how far apart they are. + */ +#define FRODO_APCIBASE 0x0 +#define FRODO_APCISPACE 0x20 +#define FRODO_APCI_OFFSET(x) (FRODO_APCIBASE + ((x) * FRODO_APCISPACE)) + +#define HPAPCI_BAUD_BASE 500400 + +#ifdef CONFIG_SERIAL_8250_CONSOLE +/* + * Parse the bootinfo to find descriptions for headless console and + * debug serial ports and register them with the 8250 driver. + */ +int __init hp300_setup_serial_console(void) +{ + int scode; + struct uart_port port; + + memset(&port, 0, sizeof(port)); + + if (hp300_uart_scode < 0 || hp300_uart_scode > DIO_SCMAX) + return 0; + + if (DIO_SCINHOLE(hp300_uart_scode)) + return 0; + + scode = hp300_uart_scode; + + /* Memory mapped I/O */ + port.iotype = UPIO_MEM; + port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF; + port.type = PORT_UNKNOWN; + + /* Check for APCI console */ + if (scode == 256) { +#ifdef CONFIG_HPAPCI + pr_info("Serial console is HP APCI 1\n"); + + port.uartclk = HPAPCI_BAUD_BASE * 16; + port.mapbase = (FRODO_BASE + FRODO_APCI_OFFSET(1)); + port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); + port.regshift = 2; + add_preferred_console("ttyS", port.line, "9600n8"); +#else + pr_warn("Serial console is APCI but support is disabled (CONFIG_HPAPCI)!\n"); + return 0; +#endif + } else { +#ifdef CONFIG_HPDCA + unsigned long pa = dio_scodetophysaddr(scode); + if (!pa) + return 0; + + pr_info("Serial console is HP DCA at select code %d\n", scode); + + port.uartclk = HPDCA_BAUD_BASE * 16; + port.mapbase = (pa + UART_OFFSET); + port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); + port.regshift = 1; + port.irq = DIO_IPL(pa + DIO_VIRADDRBASE); + + /* Enable board-interrupts */ + out_8(pa + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE); + + if (DIO_ID(pa + DIO_VIRADDRBASE) & 0x80) + add_preferred_console("ttyS", port.line, "9600n8"); +#else + pr_warn("Serial console is DCA but support is disabled (CONFIG_HPDCA)!\n"); + return 0; +#endif + } + + if (early_serial_setup(&port) < 0) + pr_warn("%s: early_serial_setup() failed.\n", __func__); + return 0; +} +#endif /* CONFIG_SERIAL_8250_CONSOLE */ + +#ifdef CONFIG_HPDCA +static int hpdca_init_one(struct dio_dev *d, + const struct dio_device_id *ent) +{ + struct uart_8250_port uart; + int line; + +#ifdef CONFIG_SERIAL_8250_CONSOLE + if (hp300_uart_scode == d->scode) { + /* Already got it. */ + return 0; + } +#endif + memset(&uart, 0, sizeof(uart)); + + /* Memory mapped I/O */ + uart.port.iotype = UPIO_MEM; + uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF; + uart.port.irq = d->ipl; + uart.port.uartclk = HPDCA_BAUD_BASE * 16; + uart.port.mapbase = (d->resource.start + UART_OFFSET); + uart.port.membase = (char *)(uart.port.mapbase + DIO_VIRADDRBASE); + uart.port.regshift = 1; + uart.port.dev = &d->dev; + line = serial8250_register_8250_port(&uart); + + if (line < 0) { + dev_notice(&d->dev, + "8250_hp300: register_serial() DCA scode %d irq %d failed\n", + d->scode, uart.port.irq); + return -ENOMEM; + } + + /* Enable board-interrupts */ + out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE); + dio_set_drvdata(d, (void *)line); + + /* Reset the DCA */ + out_8(d->resource.start + DIO_VIRADDRBASE + DCA_ID, 0xff); + udelay(100); + + num_ports++; + + return 0; +} +#endif + +static int __init hp300_8250_init(void) +{ + static int called; +#ifdef CONFIG_HPAPCI + int line; + unsigned long base; + struct uart_8250_port uart; + struct hp300_port *port; + int i; +#endif + if (called) + return -ENODEV; + called = 1; + + if (!MACH_IS_HP300) + return -ENODEV; + +#ifdef CONFIG_HPDCA + dio_register_driver(&hpdca_driver); +#endif +#ifdef CONFIG_HPAPCI + if (hp300_model < HP_400) { + if (!num_ports) + return -ENODEV; + return 0; + } + /* These models have the Frodo chip. + * Port 0 is reserved for the Apollo Domain keyboard. + * Port 1 is either the console or the DCA. + */ + for (i = 1; i < 4; i++) { + /* Port 1 is the console on a 425e, on other machines it's + * mapped to DCA. + */ +#ifdef CONFIG_SERIAL_8250_CONSOLE + if (i == 1) + continue; +#endif + + /* Create new serial device */ + port = kmalloc_obj(struct hp300_port); + if (!port) + return -ENOMEM; + + memset(&uart, 0, sizeof(uart)); + + base = (FRODO_BASE + FRODO_APCI_OFFSET(i)); + + /* Memory mapped I/O */ + uart.port.iotype = UPIO_MEM; + uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ + | UPF_BOOT_AUTOCONF; + /* XXX - no interrupt support yet */ + uart.port.irq = 0; + uart.port.uartclk = HPAPCI_BAUD_BASE * 16; + uart.port.mapbase = base; + uart.port.membase = (char *)(base + DIO_VIRADDRBASE); + uart.port.regshift = 2; + + line = serial8250_register_8250_port(&uart); + + if (line < 0) { + dev_notice(uart.port.dev, + "8250_hp300: register_serial() APCI %d irq %d failed\n", + i, uart.port.irq); + kfree(port); + continue; + } + + port->line = line; + port->next = hp300_ports; + hp300_ports = port; + + num_ports++; + } +#endif + + /* Any boards found? */ + if (!num_ports) + return -ENODEV; + + return 0; +} + +#ifdef CONFIG_HPDCA +static void hpdca_remove_one(struct dio_dev *d) +{ + int line; + + line = (int) dio_get_drvdata(d); + if (d->resource.start) { + /* Disable board-interrupts */ + out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, 0); + } + serial8250_unregister_port(line); +} +#endif + +static void __exit hp300_8250_exit(void) +{ +#ifdef CONFIG_HPAPCI + struct hp300_port *port, *to_free; + + for (port = hp300_ports; port; ) { + serial8250_unregister_port(port->line); + to_free = port; + port = port->next; + kfree(to_free); + } + + hp300_ports = NULL; +#endif +#ifdef CONFIG_HPDCA + dio_unregister_driver(&hpdca_driver); +#endif +} + +module_init(hp300_8250_init); +module_exit(hp300_8250_exit); +MODULE_DESCRIPTION("HP DCA/APCI serial driver"); +MODULE_AUTHOR("Kars de Jong <jongk@linux-m68k.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_hub6.c b/drivers/tty/serial/8250/8250_hub6.c new file mode 100644 index 000000000..b6767633c --- /dev/null +++ b/drivers/tty/serial/8250/8250_hub6.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/serial_8250.h> + +#include "8250.h" + +#define HUB6(card, port) \ + { \ + .iobase = 0x302, \ + .irq = 3, \ + .uartclk = 1843200, \ + .iotype = UPIO_HUB6, \ + .flags = UPF_BOOT_AUTOCONF, \ + .hub6 = (card) << 6 | (port) << 3 | 1, \ + } + +static struct plat_serial8250_port hub6_data[] = { + HUB6(0, 0), + HUB6(0, 1), + HUB6(0, 2), + HUB6(0, 3), + HUB6(0, 4), + HUB6(0, 5), + HUB6(1, 0), + HUB6(1, 1), + HUB6(1, 2), + HUB6(1, 3), + HUB6(1, 4), + HUB6(1, 5), + { }, +}; + +static struct platform_device hub6_device = { + .name = "serial8250", + .id = PLAT8250_DEV_HUB6, + .dev = { + .platform_data = hub6_data, + }, +}; + +bool hub6_match_port(const struct uart_port *port1, const struct uart_port *port2) +{ + return port1->iobase == port2->iobase && port1->hub6 == port2->hub6; +} +EXPORT_SYMBOL_GPL(hub6_match_port); + +static int __init hub6_init(void) +{ + return platform_device_register(&hub6_device); +} + +module_init(hub6_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for Hub6 cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c new file mode 100644 index 000000000..94542fc14 --- /dev/null +++ b/drivers/tty/serial/8250/8250_ingenic.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2010 Lars-Peter Clausen <lars@metafoo.de> + * Copyright (C) 2015 Imagination Technologies + * + * Ingenic SoC UART support + */ + +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/io.h> +#include <linux/libfdt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/platform_device.h> +#include <linux/serial_8250.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> + +#include "8250.h" + +/** ingenic_uart_config: SOC specific config data. */ +struct ingenic_uart_config { + int tx_loadsz; + int fifosize; +}; + +struct ingenic_uart_data { + struct clk *clk_module; + struct clk *clk_baud; + int line; +}; + +static const struct of_device_id of_match[]; + +#define UART_FCR_UME BIT(4) + +#define UART_MCR_MDCE BIT(7) +#define UART_MCR_FCM BIT(6) + +static struct earlycon_device *early_device; + +static uint8_t early_in(struct uart_port *port, int offset) +{ + return readl(port->membase + (offset << 2)); +} + +static void early_out(struct uart_port *port, int offset, uint8_t value) +{ + writel(value, port->membase + (offset << 2)); +} + +static void ingenic_early_console_putc(struct uart_port *port, unsigned char c) +{ + u16 lsr; + + do { + lsr = early_in(port, UART_LSR); + } while ((lsr & UART_LSR_TEMT) == 0); + + early_out(port, UART_TX, c); +} + +static void ingenic_early_console_write(struct console *console, + const char *s, unsigned int count) +{ + uart_console_write(&early_device->port, s, count, + ingenic_early_console_putc); +} + +static void __init ingenic_early_console_setup_clock(struct earlycon_device *dev) +{ + void *fdt = initial_boot_params; + const __be32 *prop; + int offset; + + offset = fdt_path_offset(fdt, "/ext"); + if (offset < 0) + return; + + prop = fdt_getprop(fdt, offset, "clock-frequency", NULL); + if (!prop) + return; + + dev->port.uartclk = be32_to_cpup(prop); +} + +static int __init ingenic_earlycon_setup_tail(struct earlycon_device *dev, + const char *opt) +{ + struct uart_port *port = &dev->port; + unsigned int divisor; + int baud = 115200; + + if (!dev->port.membase) + return -ENODEV; + + if (opt) { + unsigned int parity, bits, flow; /* unused for now */ + + uart_parse_options(opt, &baud, &parity, &bits, &flow); + } + + if (dev->baud) + baud = dev->baud; + divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * baud); + + early_out(port, UART_IER, 0); + early_out(port, UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8); + early_out(port, UART_DLL, 0); + early_out(port, UART_DLM, 0); + early_out(port, UART_LCR, UART_LCR_WLEN8); + early_out(port, UART_FCR, UART_FCR_UME | UART_FCR_CLEAR_XMIT | + UART_FCR_CLEAR_RCVR | UART_FCR_ENABLE_FIFO); + early_out(port, UART_MCR, UART_MCR_RTS | UART_MCR_DTR); + + early_out(port, UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8); + early_out(port, UART_DLL, divisor & 0xff); + early_out(port, UART_DLM, (divisor >> 8) & 0xff); + early_out(port, UART_LCR, UART_LCR_WLEN8); + + early_device = dev; + dev->con->write = ingenic_early_console_write; + + return 0; +} + +static int __init ingenic_early_console_setup(struct earlycon_device *dev, + const char *opt) +{ + ingenic_early_console_setup_clock(dev); + + return ingenic_earlycon_setup_tail(dev, opt); +} + +static int __init jz4750_early_console_setup(struct earlycon_device *dev, + const char *opt) +{ + /* + * JZ4750/55/60 have an optional /2 divider between the EXT + * oscillator and some peripherals including UART, which will + * be enabled if using a 24 MHz oscillator, and disabled when + * using a 12 MHz oscillator. + */ + ingenic_early_console_setup_clock(dev); + if (dev->port.uartclk >= 16000000) + dev->port.uartclk /= 2; + + return ingenic_earlycon_setup_tail(dev, opt); +} + +OF_EARLYCON_DECLARE(jz4740_uart, "ingenic,jz4740-uart", + ingenic_early_console_setup); + +OF_EARLYCON_DECLARE(jz4750_uart, "ingenic,jz4750-uart", + jz4750_early_console_setup); + +OF_EARLYCON_DECLARE(jz4770_uart, "ingenic,jz4770-uart", + ingenic_early_console_setup); + +OF_EARLYCON_DECLARE(jz4775_uart, "ingenic,jz4775-uart", + ingenic_early_console_setup); + +OF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart", + ingenic_early_console_setup); + +OF_EARLYCON_DECLARE(x1000_uart, "ingenic,x1000-uart", + ingenic_early_console_setup); + +static void ingenic_uart_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ + u32 ier; + + switch (offset) { + case UART_FCR: + /* UART module enable */ + value |= UART_FCR_UME; + break; + + case UART_IER: + /* + * Enable receive timeout interrupt with the receive line + * status interrupt. + */ + value |= (value & 0x4) << 2; + break; + + case UART_MCR: + /* + * If we have enabled modem status IRQs we should enable + * modem mode. + */ + ier = p->serial_in(p, UART_IER); + + if (ier & UART_IER_MSI) + value |= UART_MCR_MDCE | UART_MCR_FCM; + else + value &= ~(UART_MCR_MDCE | UART_MCR_FCM); + break; + + default: + break; + } + + writeb(value, p->membase + (offset << p->regshift)); +} + +static u32 ingenic_uart_serial_in(struct uart_port *p, unsigned int offset) +{ + u8 value; + + value = readb(p->membase + (offset << p->regshift)); + + /* Hide non-16550 compliant bits from higher levels */ + switch (offset) { + case UART_FCR: + value &= ~UART_FCR_UME; + break; + + case UART_MCR: + value &= ~(UART_MCR_MDCE | UART_MCR_FCM); + break; + + default: + break; + } + return value; +} + +static int ingenic_uart_probe(struct platform_device *pdev) +{ + struct uart_8250_port uart = {}; + struct ingenic_uart_data *data; + const struct ingenic_uart_config *cdata; + struct resource *regs; + int err; + + cdata = of_device_get_match_data(&pdev->dev); + if (!cdata) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(&pdev->dev, "no registers defined\n"); + return -EINVAL; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + spin_lock_init(&uart.port.lock); + uart.port.type = PORT_16550A; + uart.port.flags = UPF_SKIP_TEST | UPF_IOREMAP | UPF_FIXED_TYPE; + uart.port.mapbase = regs->start; + uart.port.serial_out = ingenic_uart_serial_out; + uart.port.serial_in = ingenic_uart_serial_in; + uart.port.dev = &pdev->dev; + uart.tx_loadsz = cdata->tx_loadsz; + uart.capabilities = UART_CAP_FIFO | UART_CAP_RTOIE; + + err = uart_read_port_properties(&uart.port); + if (err) + return err; + + uart.port.regshift = 2; + uart.port.fifosize = cdata->fifosize; + + uart.port.membase = devm_ioremap(&pdev->dev, regs->start, + resource_size(regs)); + if (!uart.port.membase) + return -ENOMEM; + + data->clk_module = devm_clk_get(&pdev->dev, "module"); + if (IS_ERR(data->clk_module)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->clk_module), + "unable to get module clock\n"); + + data->clk_baud = devm_clk_get(&pdev->dev, "baud"); + if (IS_ERR(data->clk_baud)) + return dev_err_probe(&pdev->dev, PTR_ERR(data->clk_baud), + "unable to get baud clock\n"); + + err = clk_prepare_enable(data->clk_module); + if (err) { + dev_err(&pdev->dev, "could not enable module clock: %d\n", err); + goto out; + } + + err = clk_prepare_enable(data->clk_baud); + if (err) { + dev_err(&pdev->dev, "could not enable baud clock: %d\n", err); + goto out_disable_moduleclk; + } + uart.port.uartclk = clk_get_rate(data->clk_baud); + + data->line = serial8250_register_8250_port(&uart); + if (data->line < 0) { + err = data->line; + goto out_disable_baudclk; + } + + platform_set_drvdata(pdev, data); + return 0; + +out_disable_baudclk: + clk_disable_unprepare(data->clk_baud); +out_disable_moduleclk: + clk_disable_unprepare(data->clk_module); +out: + return err; +} + +static void ingenic_uart_remove(struct platform_device *pdev) +{ + struct ingenic_uart_data *data = platform_get_drvdata(pdev); + + serial8250_unregister_port(data->line); + clk_disable_unprepare(data->clk_module); + clk_disable_unprepare(data->clk_baud); +} + +static const struct ingenic_uart_config jz4740_uart_config = { + .tx_loadsz = 8, + .fifosize = 16, +}; + +static const struct ingenic_uart_config jz4760_uart_config = { + .tx_loadsz = 16, + .fifosize = 32, +}; + +static const struct ingenic_uart_config jz4780_uart_config = { + .tx_loadsz = 32, + .fifosize = 64, +}; + +static const struct ingenic_uart_config x1000_uart_config = { + .tx_loadsz = 32, + .fifosize = 64, +}; + +static const struct of_device_id of_match[] = { + { .compatible = "ingenic,jz4740-uart", .data = &jz4740_uart_config }, + { .compatible = "ingenic,jz4750-uart", .data = &jz4760_uart_config }, + { .compatible = "ingenic,jz4760-uart", .data = &jz4760_uart_config }, + { .compatible = "ingenic,jz4770-uart", .data = &jz4760_uart_config }, + { .compatible = "ingenic,jz4775-uart", .data = &jz4760_uart_config }, + { .compatible = "ingenic,jz4780-uart", .data = &jz4780_uart_config }, + { .compatible = "ingenic,x1000-uart", .data = &x1000_uart_config }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, of_match); + +static struct platform_driver ingenic_uart_platform_driver = { + .driver = { + .name = "ingenic-uart", + .of_match_table = of_match, + }, + .probe = ingenic_uart_probe, + .remove = ingenic_uart_remove, +}; + +module_platform_driver(ingenic_uart_platform_driver); + +MODULE_AUTHOR("Paul Burton"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Ingenic SoC UART driver"); diff --git a/drivers/tty/serial/8250/8250_ioc3.c b/drivers/tty/serial/8250/8250_ioc3.c new file mode 100644 index 000000000..28e280767 --- /dev/null +++ b/drivers/tty/serial/8250/8250_ioc3.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SGI IOC3 8250 UART driver + * + * Copyright (C) 2019 Thomas Bogendoerfer <tbogendoerfer@suse.de> + * + * based on code Copyright (C) 2005 Stanislaw Skowronek <skylark@unaligned.org> + * Copyright (C) 2014 Joshua Kinard <linux@kumba.dev> + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/platform_device.h> + +#include "8250.h" + +#define IOC3_UARTCLK (22000000 / 3) + +struct ioc3_8250_data { + int line; +}; + +static u32 ioc3_serial_in(struct uart_port *p, unsigned int offset) +{ + return readb(p->membase + (offset ^ 3)); +} + +static void ioc3_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ + writeb(value, p->membase + (offset ^ 3)); +} + +static int serial8250_ioc3_probe(struct platform_device *pdev) +{ + struct ioc3_8250_data *data; + struct uart_8250_port up; + struct resource *r; + void __iomem *membase; + int irq, line; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) + return -ENODEV; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + membase = devm_ioremap(&pdev->dev, r->start, resource_size(r)); + if (!membase) + return -ENOMEM; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + irq = 0; /* no interrupt -> use polling */ + + /* Register serial ports with 8250.c */ + memset(&up, 0, sizeof(struct uart_8250_port)); + up.port.iotype = UPIO_MEM; + up.port.uartclk = IOC3_UARTCLK; + up.port.type = PORT_16550A; + up.port.irq = irq; + up.port.flags = (UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ); + up.port.dev = &pdev->dev; + up.port.membase = membase; + up.port.mapbase = r->start; + up.port.serial_in = ioc3_serial_in; + up.port.serial_out = ioc3_serial_out; + line = serial8250_register_8250_port(&up); + if (line < 0) + return line; + + platform_set_drvdata(pdev, data); + return 0; +} + +static void serial8250_ioc3_remove(struct platform_device *pdev) +{ + struct ioc3_8250_data *data = platform_get_drvdata(pdev); + + serial8250_unregister_port(data->line); +} + +static struct platform_driver serial8250_ioc3_driver = { + .probe = serial8250_ioc3_probe, + .remove = serial8250_ioc3_remove, + .driver = { + .name = "ioc3-serial8250", + } +}; + +module_platform_driver(serial8250_ioc3_driver); + +MODULE_AUTHOR("Thomas Bogendoerfer <tbogendoerfer@suse.de>"); +MODULE_DESCRIPTION("SGI IOC3 8250 UART driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_keba.c b/drivers/tty/serial/8250/8250_keba.c new file mode 100644 index 000000000..5b791b6ee --- /dev/null +++ b/drivers/tty/serial/8250/8250_keba.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 KEBA Industrial Automation GmbH + * + * Driver for KEBA UART FPGA IP core + */ + +#include <linux/auxiliary_bus.h> +#include <linux/bits.h> +#include <linux/container_of.h> +#include <linux/dev_printk.h> +#include <linux/device/devres.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/misc/keba.h> +#include <linux/module.h> +#include <linux/serial_core.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include "8250.h" + +#define KUART "kuart" + +/* flags */ +#define KUART_RS485 BIT(0) +#define KUART_USE_CAPABILITY BIT(1) + +/* registers */ +#define KUART_VERSION 0x0000 +#define KUART_REVISION 0x0001 +#define KUART_CAPABILITY 0x0002 +#define KUART_CONTROL 0x0004 +#define KUART_BASE 0x000C +#define KUART_REGSHIFT 2 +#define KUART_CLK 1843200 + +/* mode flags */ +enum kuart_mode { + KUART_MODE_NONE = 0, + KUART_MODE_RS485, + KUART_MODE_RS422, + KUART_MODE_RS232 +}; + +/* capability flags */ +#define KUART_CAPABILITY_NONE BIT(KUART_MODE_NONE) +#define KUART_CAPABILITY_RS485 BIT(KUART_MODE_RS485) +#define KUART_CAPABILITY_RS422 BIT(KUART_MODE_RS422) +#define KUART_CAPABILITY_RS232 BIT(KUART_MODE_RS232) +#define KUART_CAPABILITY_MASK GENMASK(3, 0) + +/* registers for Indexed Control Register access in enhanced mode */ +#define KUART_EMODE_ICR_OFFSET UART_SCR +#define KUART_EMODE_ICR_VALUE UART_LSR + +/* Additional Control Register DTR line configuration */ +#define UART_ACR_DTRLC_MASK 0x18 +#define UART_ACR_DTRLC_COMPAT 0x00 +#define UART_ACR_DTRLC_ENABLE_LOW 0x10 + +struct kuart { + struct keba_uart_auxdev *auxdev; + void __iomem *base; + unsigned int line; + + unsigned int flags; + u8 capability; + enum kuart_mode mode; +}; + +static void kuart_set_phy_mode(struct kuart *kuart, enum kuart_mode mode) +{ + iowrite8(mode, kuart->base + KUART_CONTROL); +} + +static void kuart_enhanced_mode(struct uart_8250_port *up, bool enable) +{ + u8 lcr, efr; + + /* backup LCR register */ + lcr = serial_in(up, UART_LCR); + + /* enable 650 compatible register set (EFR, ...) */ + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + /* enable/disable enhanced mode with indexed control registers */ + efr = serial_in(up, UART_EFR); + if (enable) + efr |= UART_EFR_ECB; + else + efr &= ~UART_EFR_ECB; + serial_out(up, UART_EFR, efr); + + /* disable 650 compatible register set, restore LCR */ + serial_out(up, UART_LCR, lcr); +} + +static void kuart_dtr_line_config(struct uart_8250_port *up, u8 dtrlc) +{ + u8 acr; + + /* set index register to 0 to access ACR register */ + serial_out(up, KUART_EMODE_ICR_OFFSET, UART_ACR); + + /* set value register to 0x10 writing DTR mode (1,0) */ + acr = serial_in(up, KUART_EMODE_ICR_VALUE); + acr &= ~UART_ACR_DTRLC_MASK; + acr |= dtrlc; + serial_out(up, KUART_EMODE_ICR_VALUE, acr); +} + +static int kuart_rs485_config(struct uart_port *port, struct ktermios *termios, + struct serial_rs485 *rs485) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct kuart *kuart = port->private_data; + enum kuart_mode mode; + u8 dtrlc; + + if (rs485->flags & SER_RS485_ENABLED) { + if (rs485->flags & SER_RS485_MODE_RS422) + mode = KUART_MODE_RS422; + else + mode = KUART_MODE_RS485; + } else { + mode = KUART_MODE_RS232; + } + + if (mode == kuart->mode) + return 0; + + if (kuart->flags & KUART_USE_CAPABILITY) { + /* deactivate physical interface, break before make */ + kuart_set_phy_mode(kuart, KUART_MODE_NONE); + } + + if (mode == KUART_MODE_RS485) { + /* + * Set DTR line configuration of 95x UART to DTR mode (1,0). + * In this mode the DTR pin drives the active-low enable pin of + * an external RS485 buffer. The DTR pin will be forced low + * whenever the transmitter is not empty, otherwise DTR pin is + * high. + */ + dtrlc = UART_ACR_DTRLC_ENABLE_LOW; + } else { + /* + * Set DTR line configuration of 95x UART to DTR mode (0,0). + * In this mode the DTR pin is compatible with 16C450, 16C550, + * 16C650 and 16c670 (i.e. normal). + */ + dtrlc = UART_ACR_DTRLC_COMPAT; + } + + kuart_enhanced_mode(up, true); + kuart_dtr_line_config(up, dtrlc); + kuart_enhanced_mode(up, false); + + if (kuart->flags & KUART_USE_CAPABILITY) { + /* activate selected physical interface */ + kuart_set_phy_mode(kuart, mode); + } + + kuart->mode = mode; + + return 0; +} + +static int kuart_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &auxdev->dev; + struct uart_8250_port uart = {}; + struct resource res; + struct kuart *kuart; + int retval; + + kuart = devm_kzalloc(dev, sizeof(*kuart), GFP_KERNEL); + if (!kuart) + return -ENOMEM; + kuart->auxdev = container_of(auxdev, struct keba_uart_auxdev, auxdev); + kuart->flags = id->driver_data; + auxiliary_set_drvdata(auxdev, kuart); + + /* + * map only memory in front of UART registers, UART registers will be + * mapped by serial port + */ + res = kuart->auxdev->io; + res.end = res.start + KUART_BASE - 1; + kuart->base = devm_ioremap_resource(dev, &res); + if (IS_ERR(kuart->base)) + return PTR_ERR(kuart->base); + + if (kuart->flags & KUART_USE_CAPABILITY) { + /* + * supported modes are read from capability register, at least + * one mode other than none must be supported + */ + kuart->capability = ioread8(kuart->base + KUART_CAPABILITY) & + KUART_CAPABILITY_MASK; + if ((kuart->capability & ~KUART_CAPABILITY_NONE) == 0) + return -EIO; + } + + spin_lock_init(&uart.port.lock); + uart.port.dev = dev; + uart.port.mapbase = kuart->auxdev->io.start + KUART_BASE; + uart.port.irq = kuart->auxdev->irq; + uart.port.uartclk = KUART_CLK; + uart.port.private_data = kuart; + + /* 8 bit registers are 32 bit aligned => shift register offset */ + uart.port.iotype = UPIO_MEM32; + uart.port.regshift = KUART_REGSHIFT; + + /* + * UART mixes 16550, 16750 and 16C950 (for RS485) standard => auto + * configuration works best + */ + uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_IOREMAP; + + /* + * UART supports RS485, RS422 and RS232 with switching of physical + * interface + */ + uart.port.rs485_config = kuart_rs485_config; + if (kuart->flags & KUART_RS485) { + uart.port.rs485_supported.flags = SER_RS485_ENABLED | + SER_RS485_RTS_ON_SEND; + uart.port.rs485.flags = SER_RS485_ENABLED | + SER_RS485_RTS_ON_SEND; + } + if (kuart->flags & KUART_USE_CAPABILITY) { + /* default mode priority is RS485 > RS422 > RS232 */ + if (kuart->capability & KUART_CAPABILITY_RS422) { + uart.port.rs485_supported.flags |= SER_RS485_ENABLED | + SER_RS485_RTS_ON_SEND | + SER_RS485_MODE_RS422; + uart.port.rs485.flags = SER_RS485_ENABLED | + SER_RS485_RTS_ON_SEND | + SER_RS485_MODE_RS422; + } + if (kuart->capability & KUART_CAPABILITY_RS485) { + uart.port.rs485_supported.flags |= SER_RS485_ENABLED | + SER_RS485_RTS_ON_SEND; + uart.port.rs485.flags = SER_RS485_ENABLED | + SER_RS485_RTS_ON_SEND; + } + } + + retval = serial8250_register_8250_port(&uart); + if (retval < 0) + return dev_err_probe(&auxdev->dev, retval, + "UART registration failed!\n"); + kuart->line = retval; + + return 0; +} + +static void kuart_remove(struct auxiliary_device *auxdev) +{ + struct kuart *kuart = auxiliary_get_drvdata(auxdev); + + if (kuart->flags & KUART_USE_CAPABILITY) + kuart_set_phy_mode(kuart, KUART_MODE_NONE); + + serial8250_unregister_port(kuart->line); +} + +static const struct auxiliary_device_id kuart_devtype_aux[] = { + { .name = "keba.rs485-uart", .driver_data = KUART_RS485 }, + { .name = "keba.rs232-uart", .driver_data = 0 }, + { .name = "keba.uart", .driver_data = KUART_USE_CAPABILITY }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, kuart_devtype_aux); + +static struct auxiliary_driver kuart_driver_aux = { + .name = KUART, + .id_table = kuart_devtype_aux, + .probe = kuart_probe, + .remove = kuart_remove, +}; +module_auxiliary_driver(kuart_driver_aux); + +MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>"); +MODULE_DESCRIPTION("KEBA 8250 serial port driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_loongson.c b/drivers/tty/serial/8250/8250_loongson.c new file mode 100644 index 000000000..19acb2c6b --- /dev/null +++ b/drivers/tty/serial/8250/8250_loongson.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Serial Port driver for Loongson family chips + * + * Copyright (C) 2020-2025 Loongson Technology Corporation Limited + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/property.h> +#include <linux/math.h> +#include <linux/pm.h> +#include <linux/reset.h> + +#include "8250.h" + +/* Divisor Latch Fraction Register */ +#define LOONGSON_UART_DLF 0x2 + +#define LOONGSON_QUOT_FRAC_MASK GENMASK(7, 0) +#define LOONGSON_QUOT_DIV_MASK GENMASK(15, 8) + +struct loongson_uart_ddata { + bool has_frac; + u8 mcr_invert; + u8 msr_invert; +}; + +static const struct loongson_uart_ddata ls2k0500_uart_data = { + .has_frac = false, + .mcr_invert = UART_MCR_RTS | UART_MCR_DTR, + .msr_invert = UART_MSR_CTS | UART_MSR_DSR, +}; + +static const struct loongson_uart_ddata ls2k1500_uart_data = { + .has_frac = true, + .mcr_invert = UART_MCR_RTS | UART_MCR_DTR, + .msr_invert = 0, +}; + +struct loongson_uart_priv { + int line; + struct clk *clk; + struct resource *res; + struct reset_control *rst; + const struct loongson_uart_ddata *ddata; +}; + +static u8 serial_fixup(struct uart_port *p, unsigned int offset, u8 val) +{ + struct loongson_uart_priv *priv = p->private_data; + + switch (offset) { + case UART_MCR: + return val ^ priv->ddata->mcr_invert; + case UART_MSR: + return val ^ priv->ddata->msr_invert; + default: + return val; + } +} + +static u32 loongson_serial_in(struct uart_port *p, unsigned int offset) +{ + u8 val; + + val = readb(p->membase + (offset << p->regshift)); + + return serial_fixup(p, offset, val); +} + +static void loongson_serial_out(struct uart_port *p, unsigned int offset, unsigned int value) +{ + u8 val; + + offset <<= p->regshift; + val = serial_fixup(p, offset, value); + writeb(val, p->membase + offset); +} + +static unsigned int loongson_frac_get_divisor(struct uart_port *port, unsigned int baud, + unsigned int *frac) +{ + unsigned int quot; + + quot = DIV_ROUND_CLOSEST((port->uartclk << 4), baud); + *frac = FIELD_GET(LOONGSON_QUOT_FRAC_MASK, quot); + + return FIELD_GET(LOONGSON_QUOT_DIV_MASK, quot); +} + +static void loongson_frac_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot, unsigned int quot_frac) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB); + serial_dl_write(up, quot); + serial_port_out(port, LOONGSON_UART_DLF, quot_frac); +} + +static int loongson_uart_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct uart_8250_port uart = {}; + struct loongson_uart_priv *priv; + struct uart_port *port; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->ddata = device_get_match_data(dev); + + port = &uart.port; + spin_lock_init(&port->lock); + port->flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_IOREMAP; + port->iotype = UPIO_MEM; + port->regshift = 0; + port->dev = dev; + port->type = PORT_16550A; + port->private_data = priv; + + port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &priv->res); + if (IS_ERR(port->membase)) + return PTR_ERR(port->membase); + + port->mapbase = priv->res->start; + port->mapsize = resource_size(priv->res); + port->serial_in = loongson_serial_in; + port->serial_out = loongson_serial_out; + + if (priv->ddata->has_frac) { + port->get_divisor = loongson_frac_get_divisor; + port->set_divisor = loongson_frac_set_divisor; + } + + ret = uart_read_port_properties(port); + if (ret) + return ret; + + if (!port->uartclk) { + priv->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), + "Unable to determine clock frequency!\n"); + port->uartclk = clk_get_rate(priv->clk); + } + + priv->rst = devm_reset_control_get_optional_shared(dev, NULL); + if (IS_ERR(priv->rst)) + return PTR_ERR(priv->rst); + + ret = reset_control_deassert(priv->rst); + if (ret) + return ret; + + ret = serial8250_register_8250_port(&uart); + if (ret < 0) { + reset_control_assert(priv->rst); + return ret; + } + + priv->line = ret; + platform_set_drvdata(pdev, priv); + + return 0; +} + +static void loongson_uart_remove(struct platform_device *pdev) +{ + struct loongson_uart_priv *priv = platform_get_drvdata(pdev); + + serial8250_unregister_port(priv->line); + reset_control_assert(priv->rst); +} + +static int loongson_uart_suspend(struct device *dev) +{ + struct loongson_uart_priv *priv = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(priv->line); + + serial8250_suspend_port(priv->line); + + if (!uart_console(&up->port) || console_suspend_enabled) + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int loongson_uart_resume(struct device *dev) +{ + struct loongson_uart_priv *priv = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(priv->line); + int ret; + + if (!uart_console(&up->port) || console_suspend_enabled) { + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + } + + serial8250_resume_port(priv->line); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(loongson_uart_pm_ops, loongson_uart_suspend, + loongson_uart_resume); + +static const struct of_device_id loongson_uart_of_ids[] = { + { .compatible = "loongson,ls2k0500-uart", .data = &ls2k0500_uart_data }, + { .compatible = "loongson,ls2k1500-uart", .data = &ls2k1500_uart_data }, + { }, +}; +MODULE_DEVICE_TABLE(of, loongson_uart_of_ids); + +static struct platform_driver loongson_uart_driver = { + .probe = loongson_uart_probe, + .remove = loongson_uart_remove, + .driver = { + .name = "loongson-uart", + .pm = pm_ptr(&loongson_uart_pm_ops), + .of_match_table = loongson_uart_of_ids, + }, +}; + +module_platform_driver(loongson_uart_driver); + +MODULE_DESCRIPTION("Loongson UART driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited."); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_lpc18xx.c b/drivers/tty/serial/8250/8250_lpc18xx.c new file mode 100644 index 000000000..6c0489c9c --- /dev/null +++ b/drivers/tty/serial/8250/8250_lpc18xx.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Serial port driver for NXP LPC18xx/43xx UART + * + * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> + * + * Based on 8250_mtk.c: + * Copyright (c) 2014 MundoReader S.L. + * Matthias Brugger <matthias.bgg@gmail.com> + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include "8250.h" + +/* Additional LPC18xx/43xx 8250 registers and bits */ +#define LPC18XX_UART_RS485CTRL (0x04c / sizeof(u32)) +#define LPC18XX_UART_RS485CTRL_NMMEN BIT(0) +#define LPC18XX_UART_RS485CTRL_DCTRL BIT(4) +#define LPC18XX_UART_RS485CTRL_OINV BIT(5) +#define LPC18XX_UART_RS485DLY (0x054 / sizeof(u32)) +#define LPC18XX_UART_RS485DLY_MAX 255 + +struct lpc18xx_uart_data { + struct uart_8250_dma dma; + struct clk *clk_uart; + struct clk *clk_reg; + int line; +}; + +static int lpc18xx_rs485_config(struct uart_port *port, struct ktermios *termios, + struct serial_rs485 *rs485) +{ + struct uart_8250_port *up = up_to_u8250p(port); + u32 rs485_ctrl_reg = 0; + u32 rs485_dly_reg = 0; + unsigned baud_clk; + + if (rs485->flags & SER_RS485_ENABLED) { + rs485_ctrl_reg |= LPC18XX_UART_RS485CTRL_NMMEN | + LPC18XX_UART_RS485CTRL_DCTRL; + + if (rs485->flags & SER_RS485_RTS_ON_SEND) + rs485_ctrl_reg |= LPC18XX_UART_RS485CTRL_OINV; + } + + if (rs485->delay_rts_after_send) { + baud_clk = port->uartclk / up->dl_read(up); + rs485_dly_reg = DIV_ROUND_UP(rs485->delay_rts_after_send + * baud_clk, MSEC_PER_SEC); + + if (rs485_dly_reg > LPC18XX_UART_RS485DLY_MAX) + rs485_dly_reg = LPC18XX_UART_RS485DLY_MAX; + + /* Calculate the resulting delay in ms */ + rs485->delay_rts_after_send = (rs485_dly_reg * MSEC_PER_SEC) + / baud_clk; + } + + serial_out(up, LPC18XX_UART_RS485CTRL, rs485_ctrl_reg); + serial_out(up, LPC18XX_UART_RS485DLY, rs485_dly_reg); + + return 0; +} + +static void lpc18xx_uart_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ + /* + * For DMA mode one must ensure that the UART_FCR_DMA_SELECT + * bit is set when FIFO is enabled. Even if DMA is not used + * setting this bit doesn't seem to affect anything. + */ + if (offset == UART_FCR && (value & UART_FCR_ENABLE_FIFO)) + value |= UART_FCR_DMA_SELECT; + + offset = offset << p->regshift; + writel(value, p->membase + offset); +} + +static const struct serial_rs485 lpc18xx_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, + .delay_rts_after_send = 1, + /* Delay RTS before send is not supported */ +}; + +static int lpc18xx_serial_probe(struct platform_device *pdev) +{ + struct lpc18xx_uart_data *data; + struct uart_8250_port uart; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "memory resource not found"); + return -EINVAL; + } + + memset(&uart, 0, sizeof(uart)); + + uart.port.membase = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!uart.port.membase) + return -ENOMEM; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->clk_uart = devm_clk_get(&pdev->dev, "uartclk"); + if (IS_ERR(data->clk_uart)) { + dev_err(&pdev->dev, "uart clock not found\n"); + return PTR_ERR(data->clk_uart); + } + + data->clk_reg = devm_clk_get(&pdev->dev, "reg"); + if (IS_ERR(data->clk_reg)) { + dev_err(&pdev->dev, "reg clock not found\n"); + return PTR_ERR(data->clk_reg); + } + + ret = clk_prepare_enable(data->clk_reg); + if (ret) { + dev_err(&pdev->dev, "unable to enable reg clock\n"); + return ret; + } + + ret = clk_prepare_enable(data->clk_uart); + if (ret) { + dev_err(&pdev->dev, "unable to enable uart clock\n"); + goto dis_clk_reg; + } + + data->dma.rx_param = data; + data->dma.tx_param = data; + + spin_lock_init(&uart.port.lock); + uart.port.dev = &pdev->dev; + uart.port.mapbase = res->start; + uart.port.type = PORT_16550A; + uart.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SKIP_TEST; + uart.port.uartclk = clk_get_rate(data->clk_uart); + uart.port.private_data = data; + uart.port.rs485_config = lpc18xx_rs485_config; + uart.port.rs485_supported = lpc18xx_rs485_supported; + uart.port.serial_out = lpc18xx_uart_serial_out; + + ret = uart_read_port_properties(&uart.port); + if (ret) + goto dis_uart_clk; + + uart.port.iotype = UPIO_MEM32; + uart.port.regshift = 2; + + uart.dma = &data->dma; + uart.dma->rxconf.src_maxburst = 1; + uart.dma->txconf.dst_maxburst = 1; + + ret = serial8250_register_8250_port(&uart); + if (ret < 0) { + dev_err(&pdev->dev, "unable to register 8250 port\n"); + goto dis_uart_clk; + } + + data->line = ret; + platform_set_drvdata(pdev, data); + + return 0; + +dis_uart_clk: + clk_disable_unprepare(data->clk_uart); +dis_clk_reg: + clk_disable_unprepare(data->clk_reg); + return ret; +} + +static void lpc18xx_serial_remove(struct platform_device *pdev) +{ + struct lpc18xx_uart_data *data = platform_get_drvdata(pdev); + + serial8250_unregister_port(data->line); + clk_disable_unprepare(data->clk_uart); + clk_disable_unprepare(data->clk_reg); +} + +static const struct of_device_id lpc18xx_serial_match[] = { + { .compatible = "nxp,lpc1850-uart" }, + { }, +}; +MODULE_DEVICE_TABLE(of, lpc18xx_serial_match); + +static struct platform_driver lpc18xx_serial_driver = { + .probe = lpc18xx_serial_probe, + .remove = lpc18xx_serial_remove, + .driver = { + .name = "lpc18xx-uart", + .of_match_table = lpc18xx_serial_match, + }, +}; +module_platform_driver(lpc18xx_serial_driver); + +MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); +MODULE_DESCRIPTION("Serial port driver NXP LPC18xx/43xx devices"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c new file mode 100644 index 000000000..776ec1ef2 --- /dev/null +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 8250_lpss.c - Driver for UART on Intel Braswell and various other Intel SoCs + * + * Copyright (C) 2016 Intel Corporation + * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> + */ + +#include <linux/bitops.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/rational.h> + +#include <linux/dmaengine.h> +#include <linux/dma/dw.h> + +#include "8250_dwlib.h" + +#define PCI_DEVICE_ID_INTEL_QRK_UARTx 0x0936 + +#define PCI_DEVICE_ID_INTEL_BYT_UART1 0x0f0a +#define PCI_DEVICE_ID_INTEL_BYT_UART2 0x0f0c + +#define PCI_DEVICE_ID_INTEL_BSW_UART1 0x228a +#define PCI_DEVICE_ID_INTEL_BSW_UART2 0x228c + +#define PCI_DEVICE_ID_INTEL_EHL_UART0 0x4b96 +#define PCI_DEVICE_ID_INTEL_EHL_UART1 0x4b97 +#define PCI_DEVICE_ID_INTEL_EHL_UART2 0x4b98 +#define PCI_DEVICE_ID_INTEL_EHL_UART3 0x4b99 +#define PCI_DEVICE_ID_INTEL_EHL_UART4 0x4b9a +#define PCI_DEVICE_ID_INTEL_EHL_UART5 0x4b9b + +#define PCI_DEVICE_ID_INTEL_BDW_UART1 0x9ce3 +#define PCI_DEVICE_ID_INTEL_BDW_UART2 0x9ce4 + +/* Intel LPSS specific registers */ + +#define BYT_PRV_CLK 0x800 +#define BYT_PRV_CLK_EN BIT(0) +#define BYT_PRV_CLK_M_VAL_SHIFT 1 +#define BYT_PRV_CLK_N_VAL_SHIFT 16 +#define BYT_PRV_CLK_UPDATE BIT(31) + +#define BYT_TX_OVF_INT 0x820 +#define BYT_TX_OVF_INT_MASK BIT(1) + +struct lpss8250; + +struct lpss8250_board { + unsigned long freq; + unsigned int base_baud; + int (*setup)(struct lpss8250 *, struct uart_port *p); + void (*exit)(struct lpss8250 *); +}; + +struct lpss8250 { + struct dw8250_port_data data; + struct lpss8250_board *board; + + /* DMA parameters */ + struct dw_dma_chip dma_chip; + struct dw_dma_slave dma_param; + u8 dma_maxburst; +}; + +static inline struct lpss8250 *to_lpss8250(struct dw8250_port_data *data) +{ + return container_of(data, struct lpss8250, data); +} + +static void byt_set_termios(struct uart_port *p, struct ktermios *termios, + const struct ktermios *old) +{ + unsigned int baud = tty_termios_baud_rate(termios); + struct lpss8250 *lpss = to_lpss8250(p->private_data); + unsigned long fref = lpss->board->freq, fuart = baud * 16; + unsigned long w = BIT(15) - 1; + unsigned long m, n; + u32 reg; + + /* Gracefully handle the B0 case: fall back to B9600 */ + fuart = fuart ? fuart : 9600 * 16; + + /* Get Fuart closer to Fref */ + fuart *= rounddown_pow_of_two(fref / fuart); + + /* + * For baud rates 0.5M, 1M, 1.5M, 2M, 2.5M, 3M, 3.5M and 4M the + * dividers must be adjusted. + * + * uartclk = (m / n) * 100 MHz, where m <= n + */ + rational_best_approximation(fuart, fref, w, w, &m, &n); + p->uartclk = fuart; + + /* Reset the clock */ + reg = (m << BYT_PRV_CLK_M_VAL_SHIFT) | (n << BYT_PRV_CLK_N_VAL_SHIFT); + writel(reg, p->membase + BYT_PRV_CLK); + reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE; + writel(reg, p->membase + BYT_PRV_CLK); + + dw8250_do_set_termios(p, termios, old); +} + +static unsigned int byt_get_mctrl(struct uart_port *port) +{ + unsigned int ret = serial8250_do_get_mctrl(port); + + /* Force DCD and DSR signals to permanently be reported as active */ + ret |= TIOCM_CAR | TIOCM_DSR; + + return ret; +} + +static int byt_serial_setup(struct lpss8250 *lpss, struct uart_port *port) +{ + struct dw_dma_slave *param = &lpss->dma_param; + struct pci_dev *pdev = to_pci_dev(port->dev); + struct pci_dev *dma_dev; + + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_BYT_UART1: + case PCI_DEVICE_ID_INTEL_BSW_UART1: + case PCI_DEVICE_ID_INTEL_BDW_UART1: + param->src_id = 3; + param->dst_id = 2; + break; + case PCI_DEVICE_ID_INTEL_BYT_UART2: + case PCI_DEVICE_ID_INTEL_BSW_UART2: + case PCI_DEVICE_ID_INTEL_BDW_UART2: + param->src_id = 5; + param->dst_id = 4; + break; + default: + return -EINVAL; + } + + dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), 0)); + + param->dma_dev = &dma_dev->dev; + param->m_master = 0; + param->p_master = 1; + + lpss->dma_maxburst = 16; + + port->set_termios = byt_set_termios; + port->get_mctrl = byt_get_mctrl; + + /* Disable TX counter interrupts */ + writel(BYT_TX_OVF_INT_MASK, port->membase + BYT_TX_OVF_INT); + + return 0; +} + +static void byt_serial_exit(struct lpss8250 *lpss) +{ + struct dw_dma_slave *param = &lpss->dma_param; + + /* Paired with pci_get_slot() in the byt_serial_setup() above */ + put_device(param->dma_dev); +} + +static int ehl_serial_setup(struct lpss8250 *lpss, struct uart_port *port) +{ + struct uart_8250_dma *dma = &lpss->data.dma; + struct uart_8250_port *up = up_to_u8250p(port); + + /* + * This simply makes the checks in the 8250_port to try the DMA + * channel request which in turn uses the magic of ACPI tables + * parsing (see drivers/dma/acpi-dma.c for the details) and + * matching with the registered General Purpose DMA controllers. + */ + up->dma = dma; + + lpss->dma_maxburst = 16; + + port->set_termios = dw8250_do_set_termios; + + return 0; +} + +static void ehl_serial_exit(struct lpss8250 *lpss) +{ + struct uart_8250_port *up = serial8250_get_port(lpss->data.line); + + up->dma = NULL; +} + +#ifdef CONFIG_SERIAL_8250_DMA +static const struct dw_dma_platform_data qrk_serial_dma_pdata = { + .nr_channels = 2, + .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, + .chan_priority = CHAN_PRIORITY_ASCENDING, + .block_size = 4095, + .nr_masters = 1, + .data_width = {4}, + .multi_block = {0}, +}; + +static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port) +{ + struct uart_8250_dma *dma = &lpss->data.dma; + struct dw_dma_chip *chip = &lpss->dma_chip; + struct dw_dma_slave *param = &lpss->dma_param; + struct pci_dev *pdev = to_pci_dev(port->dev); + int ret; + + chip->pdata = &qrk_serial_dma_pdata; + chip->dev = &pdev->dev; + chip->id = pdev->devfn; + chip->irq = pci_irq_vector(pdev, 0); + chip->regs = pci_ioremap_bar(pdev, 1); + if (!chip->regs) + return; + + /* Falling back to PIO mode if DMA probing fails */ + ret = dw_dma_probe(chip); + if (ret) + return; + + pci_try_set_mwi(pdev); + + /* Special DMA address for UART */ + dma->rx_dma_addr = 0xfffff000; + dma->tx_dma_addr = 0xfffff000; + + param->dma_dev = &pdev->dev; + param->src_id = 0; + param->dst_id = 1; + param->hs_polarity = true; + + lpss->dma_maxburst = 8; +} + +static void qrk_serial_exit_dma(struct lpss8250 *lpss) +{ + struct dw_dma_chip *chip = &lpss->dma_chip; + struct dw_dma_slave *param = &lpss->dma_param; + + if (!param->dma_dev) + return; + + dw_dma_remove(chip); + + pci_iounmap(to_pci_dev(chip->dev), chip->regs); +} +#else /* CONFIG_SERIAL_8250_DMA */ +static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port) {} +static void qrk_serial_exit_dma(struct lpss8250 *lpss) {} +#endif /* !CONFIG_SERIAL_8250_DMA */ + +static int qrk_serial_setup(struct lpss8250 *lpss, struct uart_port *port) +{ + qrk_serial_setup_dma(lpss, port); + return 0; +} + +static void qrk_serial_exit(struct lpss8250 *lpss) +{ + qrk_serial_exit_dma(lpss); +} + +static bool lpss8250_dma_filter(struct dma_chan *chan, void *param) +{ + struct dw_dma_slave *dws = param; + + if (dws->dma_dev != chan->device->dev) + return false; + + chan->private = dws; + return true; +} + +static int lpss8250_dma_setup(struct lpss8250 *lpss, struct uart_8250_port *port) +{ + struct uart_8250_dma *dma = &lpss->data.dma; + struct dw_dma_slave *rx_param, *tx_param; + struct device *dev = port->port.dev; + + if (!lpss->dma_param.dma_dev) { + dma = port->dma; + if (dma) + goto out_configuration_only; + + return 0; + } + + rx_param = devm_kmemdup(dev, &lpss->dma_param, sizeof(*rx_param), GFP_KERNEL); + if (!rx_param) + return -ENOMEM; + + tx_param = devm_kmemdup(dev, &lpss->dma_param, sizeof(*tx_param), GFP_KERNEL); + if (!tx_param) + return -ENOMEM; + + dma->fn = lpss8250_dma_filter; + dma->rx_param = rx_param; + dma->tx_param = tx_param; + + port->dma = dma; + +out_configuration_only: + dma->rxconf.src_maxburst = lpss->dma_maxburst; + dma->txconf.dst_maxburst = lpss->dma_maxburst; + + return 0; +} + +static int lpss8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct uart_8250_port uart; + struct lpss8250 *lpss; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + pci_set_master(pdev); + + lpss = devm_kzalloc(&pdev->dev, sizeof(*lpss), GFP_KERNEL); + if (!lpss) + return -ENOMEM; + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) + return ret; + + lpss->board = (struct lpss8250_board *)id->driver_data; + + memset(&uart, 0, sizeof(struct uart_8250_port)); + + uart.port.dev = &pdev->dev; + uart.port.irq = pci_irq_vector(pdev, 0); + uart.port.private_data = &lpss->data; + uart.port.type = PORT_16550A; + uart.port.iotype = UPIO_MEM32; + uart.port.regshift = 2; + uart.port.uartclk = lpss->board->base_baud * 16; + uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE; + uart.capabilities = UART_CAP_FIFO | UART_CAP_AFE; + uart.port.mapbase = pci_resource_start(pdev, 0); + uart.port.membase = pcim_iomap(pdev, 0, 0); + if (!uart.port.membase) + return -ENOMEM; + + ret = lpss->board->setup(lpss, &uart.port); + if (ret) + return ret; + + dw8250_setup_port(&uart.port); + + ret = lpss8250_dma_setup(lpss, &uart); + if (ret) + goto err_exit; + + ret = serial8250_register_8250_port(&uart); + if (ret < 0) + goto err_exit; + + lpss->data.line = ret; + + pci_set_drvdata(pdev, lpss); + return 0; + +err_exit: + lpss->board->exit(lpss); + pci_free_irq_vectors(pdev); + return ret; +} + +static void lpss8250_remove(struct pci_dev *pdev) +{ + struct lpss8250 *lpss = pci_get_drvdata(pdev); + + serial8250_unregister_port(lpss->data.line); + + lpss->board->exit(lpss); + pci_free_irq_vectors(pdev); +} + +static const struct lpss8250_board byt_board = { + .freq = 100000000, + .base_baud = 2764800, + .setup = byt_serial_setup, + .exit = byt_serial_exit, +}; + +static const struct lpss8250_board ehl_board = { + .freq = 200000000, + .base_baud = 12500000, + .setup = ehl_serial_setup, + .exit = ehl_serial_exit, +}; + +static const struct lpss8250_board qrk_board = { + .freq = 44236800, + .base_baud = 2764800, + .setup = qrk_serial_setup, + .exit = qrk_serial_exit, +}; + +static const struct pci_device_id pci_ids[] = { + { PCI_DEVICE_DATA(INTEL, QRK_UARTx, &qrk_board) }, + { PCI_DEVICE_DATA(INTEL, EHL_UART0, &ehl_board) }, + { PCI_DEVICE_DATA(INTEL, EHL_UART1, &ehl_board) }, + { PCI_DEVICE_DATA(INTEL, EHL_UART2, &ehl_board) }, + { PCI_DEVICE_DATA(INTEL, EHL_UART3, &ehl_board) }, + { PCI_DEVICE_DATA(INTEL, EHL_UART4, &ehl_board) }, + { PCI_DEVICE_DATA(INTEL, EHL_UART5, &ehl_board) }, + { PCI_DEVICE_DATA(INTEL, BYT_UART1, &byt_board) }, + { PCI_DEVICE_DATA(INTEL, BYT_UART2, &byt_board) }, + { PCI_DEVICE_DATA(INTEL, BSW_UART1, &byt_board) }, + { PCI_DEVICE_DATA(INTEL, BSW_UART2, &byt_board) }, + { PCI_DEVICE_DATA(INTEL, BDW_UART1, &byt_board) }, + { PCI_DEVICE_DATA(INTEL, BDW_UART2, &byt_board) }, + { } +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver lpss8250_pci_driver = { + .name = "8250_lpss", + .id_table = pci_ids, + .probe = lpss8250_probe, + .remove = lpss8250_remove, +}; + +module_pci_driver(lpss8250_pci_driver); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel LPSS UART driver"); diff --git a/drivers/tty/serial/8250/8250_men_mcb.c b/drivers/tty/serial/8250/8250_men_mcb.c new file mode 100644 index 000000000..6373234da --- /dev/null +++ b/drivers/tty/serial/8250/8250_men_mcb.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/mcb.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/serial_8250.h> + +#define MEN_UART_ID_Z025 0x19 +#define MEN_UART_ID_Z057 0x39 +#define MEN_UART_ID_Z125 0x7d + +/* + * IP Cores Z025 and Z057 can have up to 4 UART + * The UARTs available are stored in a global + * register saved in physical address + 0x40 + * Is saved as follows: + * + * 7 0 + * +------+-------+-------+-------+-------+-------+-------+-------+ + * |UART4 | UART3 | UART2 | UART1 | U4irq | U3irq | U2irq | U1irq | + * +------+-------+-------+-------+-------+-------+-------+-------+ + */ +#define MEN_UART1_MASK 0x01 +#define MEN_UART2_MASK 0x02 +#define MEN_UART3_MASK 0x04 +#define MEN_UART4_MASK 0x08 + +#define MEN_Z025_MAX_UARTS 4 +#define MEN_UART_MEM_SIZE 0x10 +#define MEM_UART_REGISTER_SIZE 0x01 +#define MEN_Z025_REGISTER_OFFSET 0x40 + +#define MEN_UART1_OFFSET 0 +#define MEN_UART2_OFFSET (MEN_UART1_OFFSET + MEN_UART_MEM_SIZE) +#define MEN_UART3_OFFSET (MEN_UART2_OFFSET + MEN_UART_MEM_SIZE) +#define MEN_UART4_OFFSET (MEN_UART3_OFFSET + MEN_UART_MEM_SIZE) + +#define MEN_READ_REGISTER(addr) readb(addr) + +struct serial_8250_men_mcb_data { + int num_ports; + int line[MEN_Z025_MAX_UARTS]; + unsigned int offset[MEN_Z025_MAX_UARTS]; +}; + +/* + * The Z125 16550-compatible UART has no fixed base clock assigned + * So, depending on the board we're on, we need to adjust the + * parameter in order to really set the correct baudrate, and + * do so if possible without user interaction + */ +static u32 men_lookup_uartclk(struct mcb_device *mdev) +{ + /* use default value if board is not available below */ + u32 clkval = 1041666; + + dev_info(&mdev->dev, "%s on board %s\n", + dev_name(&mdev->dev), + mdev->bus->name); + if (strncmp(mdev->bus->name, "F075", 4) == 0) + clkval = 1041666; + else if (strncmp(mdev->bus->name, "F216", 4) == 0) + clkval = 1843200; + else if (strncmp(mdev->bus->name, "F210", 4) == 0) + clkval = 115200; + else if (strstr(mdev->bus->name, "215")) + clkval = 1843200; + else + dev_info(&mdev->dev, + "board not detected, using default uartclk\n"); + + clkval = clkval << 4; + + return clkval; +} + +static int read_uarts_available_from_register(struct resource *mem_res, + u8 *uarts_available) +{ + void __iomem *mem; + int reg_value; + + if (!request_mem_region(mem_res->start + MEN_Z025_REGISTER_OFFSET, + MEM_UART_REGISTER_SIZE, KBUILD_MODNAME)) { + return -EBUSY; + } + + mem = ioremap(mem_res->start + MEN_Z025_REGISTER_OFFSET, + MEM_UART_REGISTER_SIZE); + if (!mem) { + release_mem_region(mem_res->start + MEN_Z025_REGISTER_OFFSET, + MEM_UART_REGISTER_SIZE); + return -ENOMEM; + } + + reg_value = MEN_READ_REGISTER(mem); + + iounmap(mem); + + release_mem_region(mem_res->start + MEN_Z025_REGISTER_OFFSET, + MEM_UART_REGISTER_SIZE); + + *uarts_available = reg_value >> 4; + + return 0; +} + +static int read_serial_data(struct mcb_device *mdev, + struct resource *mem_res, + struct serial_8250_men_mcb_data *serial_data) +{ + u8 uarts_available; + int count = 0; + int mask; + int res; + int i; + + res = read_uarts_available_from_register(mem_res, &uarts_available); + if (res < 0) + return res; + + for (i = 0; i < MEN_Z025_MAX_UARTS; i++) { + mask = 0x1 << i; + switch (uarts_available & mask) { + case MEN_UART1_MASK: + serial_data->offset[count] = MEN_UART1_OFFSET; + count++; + break; + case MEN_UART2_MASK: + serial_data->offset[count] = MEN_UART2_OFFSET; + count++; + break; + case MEN_UART3_MASK: + serial_data->offset[count] = MEN_UART3_OFFSET; + count++; + break; + case MEN_UART4_MASK: + serial_data->offset[count] = MEN_UART4_OFFSET; + count++; + break; + default: + return -EINVAL; + } + } + + if (count <= 0 || count > MEN_Z025_MAX_UARTS) { + dev_err(&mdev->dev, "unexpected number of ports: %u\n", + count); + return -ENODEV; + } + + serial_data->num_ports = count; + + return 0; +} + +static int init_serial_data(struct mcb_device *mdev, + struct resource *mem_res, + struct serial_8250_men_mcb_data *serial_data) +{ + switch (mdev->id) { + case MEN_UART_ID_Z125: + serial_data->num_ports = 1; + serial_data->offset[0] = 0; + return 0; + case MEN_UART_ID_Z025: + case MEN_UART_ID_Z057: + return read_serial_data(mdev, mem_res, serial_data); + default: + dev_err(&mdev->dev, "no supported device!\n"); + return -ENODEV; + } +} + +static int serial_8250_men_mcb_probe(struct mcb_device *mdev, + const struct mcb_device_id *id) +{ + struct uart_8250_port uart; + struct serial_8250_men_mcb_data *data; + struct resource *mem; + int i; + int res; + + mem = mcb_get_resource(mdev, IORESOURCE_MEM); + if (mem == NULL) + return -ENXIO; + + data = devm_kzalloc(&mdev->dev, + sizeof(struct serial_8250_men_mcb_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = init_serial_data(mdev, mem, data); + if (res < 0) + return res; + + dev_dbg(&mdev->dev, "found a 16z%03u with %u ports\n", + mdev->id, data->num_ports); + + mcb_set_drvdata(mdev, data); + + for (i = 0; i < data->num_ports; i++) { + memset(&uart, 0, sizeof(struct uart_8250_port)); + spin_lock_init(&uart.port.lock); + + uart.port.flags = UPF_SKIP_TEST | + UPF_SHARE_IRQ | + UPF_BOOT_AUTOCONF | + UPF_IOREMAP; + uart.port.iotype = UPIO_MEM; + uart.port.uartclk = men_lookup_uartclk(mdev); + uart.port.irq = mcb_get_irq(mdev); + uart.port.mapbase = (unsigned long) mem->start + + data->offset[i]; + + /* ok, register the port */ + res = serial8250_register_8250_port(&uart); + if (res < 0) { + dev_err(&mdev->dev, "unable to register UART port\n"); + return res; + } + + data->line[i] = res; + dev_info(&mdev->dev, "found MCB UART: ttyS%d\n", data->line[i]); + } + + return 0; +} + +static void serial_8250_men_mcb_remove(struct mcb_device *mdev) +{ + int i; + struct serial_8250_men_mcb_data *data = mcb_get_drvdata(mdev); + + if (!data) + return; + + for (i = 0; i < data->num_ports; i++) + serial8250_unregister_port(data->line[i]); +} + +static const struct mcb_device_id serial_8250_men_mcb_ids[] = { + { .device = MEN_UART_ID_Z025 }, + { .device = MEN_UART_ID_Z057 }, + { .device = MEN_UART_ID_Z125 }, + { } +}; +MODULE_DEVICE_TABLE(mcb, serial_8250_men_mcb_ids); + +static struct mcb_driver mcb_driver = { + .driver = { + .name = "8250_men_mcb", + }, + .probe = serial_8250_men_mcb_probe, + .remove = serial_8250_men_mcb_remove, + .id_table = serial_8250_men_mcb_ids, +}; +module_mcb_driver(mcb_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MEN 8250 UART driver"); +MODULE_AUTHOR("Michael Moese <michael.moese@men.de"); +MODULE_IMPORT_NS("MCB"); diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c new file mode 100644 index 000000000..82656645b --- /dev/null +++ b/drivers/tty/serial/8250/8250_mid.c @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 8250_mid.c - Driver for UART on Intel Penwell and various other Intel SOCs + * + * Copyright (C) 2015 Intel Corporation + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + */ + +#include <linux/bitops.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/rational.h> +#include <linux/util_macros.h> + +#include <linux/dma/hsu.h> + +#include "8250.h" + +#define PCI_DEVICE_ID_INTEL_PNW_UART1 0x081b +#define PCI_DEVICE_ID_INTEL_PNW_UART2 0x081c +#define PCI_DEVICE_ID_INTEL_PNW_UART3 0x081d +#define PCI_DEVICE_ID_INTEL_TNG_UART 0x1191 +#define PCI_DEVICE_ID_INTEL_CDF_UART 0x18d8 +#define PCI_DEVICE_ID_INTEL_DNV_UART 0x19d8 + +/* Intel MID Specific registers */ +#define INTEL_MID_UART_FISR 0x08 +#define INTEL_MID_UART_PS 0x30 +#define INTEL_MID_UART_MUL 0x34 +#define INTEL_MID_UART_DIV 0x38 + +struct mid8250; + +struct mid8250_board { + unsigned long freq; + unsigned int base_baud; + unsigned int bar; + int (*setup)(struct mid8250 *, struct uart_port *p); + void (*exit)(struct mid8250 *); +}; + +struct mid8250 { + int line; + int dma_index; + struct pci_dev *dma_dev; + struct uart_8250_dma dma; + struct mid8250_board *board; + struct hsu_dma_chip dma_chip; +}; + +/*****************************************************************************/ + +static int pnw_setup(struct mid8250 *mid, struct uart_port *p) +{ + struct pci_dev *pdev = to_pci_dev(p->dev); + + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_PNW_UART1: + mid->dma_index = 0; + break; + case PCI_DEVICE_ID_INTEL_PNW_UART2: + mid->dma_index = 1; + break; + case PCI_DEVICE_ID_INTEL_PNW_UART3: + mid->dma_index = 2; + break; + default: + return -EINVAL; + } + + mid->dma_dev = pci_get_slot(pdev->bus, + PCI_DEVFN(PCI_SLOT(pdev->devfn), 3)); + return 0; +} + +static void pnw_exit(struct mid8250 *mid) +{ + pci_dev_put(mid->dma_dev); +} + +static int tng_handle_irq(struct uart_port *p) +{ + struct mid8250 *mid = p->private_data; + struct uart_8250_port *up = up_to_u8250p(p); + struct hsu_dma_chip *chip; + u32 status; + int ret = 0; + int err; + + chip = pci_get_drvdata(mid->dma_dev); + + /* Rx DMA */ + err = hsu_dma_get_status(chip, mid->dma_index * 2 + 1, &status); + if (err > 0) { + serial8250_rx_dma_flush(up); + ret |= 1; + } else if (err == 0) + ret |= hsu_dma_do_irq(chip, mid->dma_index * 2 + 1, status); + + /* Tx DMA */ + err = hsu_dma_get_status(chip, mid->dma_index * 2, &status); + if (err > 0) + ret |= 1; + else if (err == 0) + ret |= hsu_dma_do_irq(chip, mid->dma_index * 2, status); + + /* UART */ + ret |= serial8250_handle_irq(p, serial_port_in(p, UART_IIR)); + return IRQ_RETVAL(ret); +} + +static int tng_setup(struct mid8250 *mid, struct uart_port *p) +{ + struct pci_dev *pdev = to_pci_dev(p->dev); + int index = PCI_FUNC(pdev->devfn); + + /* + * Device 0000:00:04.0 is not a real HSU port. It provides a global + * register set for all HSU ports, although it has the same PCI ID. + * Skip it here. + */ + if (index-- == 0) + return -ENODEV; + + mid->dma_index = index; + mid->dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(5, 0)); + + p->handle_irq = tng_handle_irq; + return 0; +} + +static void tng_exit(struct mid8250 *mid) +{ + pci_dev_put(mid->dma_dev); +} + +static int dnv_handle_irq(struct uart_port *p) +{ + struct mid8250 *mid = p->private_data; + struct uart_8250_port *up = up_to_u8250p(p); + unsigned int fisr = serial_port_in(p, INTEL_MID_UART_FISR); + u32 status; + int ret = 0; + int err; + + if (fisr & BIT(2)) { + err = hsu_dma_get_status(&mid->dma_chip, 1, &status); + if (err > 0) { + serial8250_rx_dma_flush(up); + ret |= 1; + } else if (err == 0) + ret |= hsu_dma_do_irq(&mid->dma_chip, 1, status); + } + if (fisr & BIT(1)) { + err = hsu_dma_get_status(&mid->dma_chip, 0, &status); + if (err > 0) + ret |= 1; + else if (err == 0) + ret |= hsu_dma_do_irq(&mid->dma_chip, 0, status); + } + if (fisr & BIT(0)) + ret |= serial8250_handle_irq(p, serial_port_in(p, UART_IIR)); + return IRQ_RETVAL(ret); +} + +#define DNV_DMA_CHAN_OFFSET 0x80 + +static int dnv_setup(struct mid8250 *mid, struct uart_port *p) +{ + struct hsu_dma_chip *chip = &mid->dma_chip; + struct pci_dev *pdev = to_pci_dev(p->dev); + int ret; + + pci_set_master(pdev); + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) + return ret; + + p->irq = pci_irq_vector(pdev, 0); + + chip->dev = &pdev->dev; + chip->irq = pci_irq_vector(pdev, 0); + chip->regs = p->membase; + chip->length = pci_resource_len(pdev, mid->board->bar); + chip->offset = DNV_DMA_CHAN_OFFSET; + + /* Falling back to PIO mode if DMA probing fails */ + ret = hsu_dma_probe(chip); + if (ret) + return 0; + + mid->dma_dev = pdev; + + p->handle_irq = dnv_handle_irq; + return 0; +} + +static void dnv_exit(struct mid8250 *mid) +{ + if (!mid->dma_dev) + return; + hsu_dma_remove(&mid->dma_chip); +} + +/*****************************************************************************/ + +static void mid8250_set_termios(struct uart_port *p, struct ktermios *termios, + const struct ktermios *old) +{ + unsigned int baud = tty_termios_baud_rate(termios); + struct mid8250 *mid = p->private_data; + unsigned short ps = 16; + unsigned long fuart = baud * ps; + unsigned long w = BIT(24) - 1; + unsigned long mul, div; + + /* Gracefully handle the B0 case: fall back to B9600 */ + fuart = fuart ? fuart : 9600 * 16; + + if (mid->board->freq < fuart) { + /* Find prescaler value that satisfies Fuart < Fref */ + if (mid->board->freq > baud) + ps = mid->board->freq / baud; /* baud rate too high */ + else + ps = 1; /* PLL case */ + fuart = baud * ps; + } else { + /* Get Fuart closer to Fref */ + fuart *= rounddown_pow_of_two(mid->board->freq / fuart); + } + + rational_best_approximation(fuart, mid->board->freq, w, w, &mul, &div); + p->uartclk = fuart * 16 / ps; /* core uses ps = 16 always */ + + writel(ps, p->membase + INTEL_MID_UART_PS); /* set PS */ + writel(mul, p->membase + INTEL_MID_UART_MUL); /* set MUL */ + writel(div, p->membase + INTEL_MID_UART_DIV); + + serial8250_do_set_termios(p, termios, old); +} + +static bool mid8250_dma_filter(struct dma_chan *chan, void *param) +{ + struct hsu_dma_slave *s = param; + + if (s->dma_dev != chan->device->dev || s->chan_id != chan->chan_id) + return false; + + chan->private = s; + return true; +} + +static int mid8250_dma_setup(struct mid8250 *mid, struct uart_8250_port *port) +{ + struct uart_8250_dma *dma = &mid->dma; + struct device *dev = port->port.dev; + struct hsu_dma_slave *rx_param; + struct hsu_dma_slave *tx_param; + + if (!mid->dma_dev) + return 0; + + rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL); + if (!rx_param) + return -ENOMEM; + + tx_param = devm_kzalloc(dev, sizeof(*tx_param), GFP_KERNEL); + if (!tx_param) + return -ENOMEM; + + rx_param->chan_id = mid->dma_index * 2 + 1; + tx_param->chan_id = mid->dma_index * 2; + + dma->rxconf.src_maxburst = 64; + dma->txconf.dst_maxburst = 64; + + rx_param->dma_dev = &mid->dma_dev->dev; + tx_param->dma_dev = &mid->dma_dev->dev; + + dma->fn = mid8250_dma_filter; + dma->rx_param = rx_param; + dma->tx_param = tx_param; + + port->dma = dma; + return 0; +} + +static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct uart_8250_port uart; + struct mid8250 *mid; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + mid = devm_kzalloc(&pdev->dev, sizeof(*mid), GFP_KERNEL); + if (!mid) + return -ENOMEM; + + mid->board = (struct mid8250_board *)id->driver_data; + + memset(&uart, 0, sizeof(struct uart_8250_port)); + + uart.port.dev = &pdev->dev; + uart.port.irq = pdev->irq; + uart.port.private_data = mid; + uart.port.type = PORT_16750; + uart.port.iotype = UPIO_MEM; + uart.port.uartclk = mid->board->base_baud * 16; + uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE; + uart.port.set_termios = mid8250_set_termios; + + uart.port.mapbase = pci_resource_start(pdev, mid->board->bar); + uart.port.membase = pcim_iomap(pdev, mid->board->bar, 0); + if (!uart.port.membase) + return -ENOMEM; + + if (mid->board->setup) { + ret = mid->board->setup(mid, &uart.port); + if (ret) + return ret; + } + + ret = mid8250_dma_setup(mid, &uart); + if (ret) + goto err; + + ret = serial8250_register_8250_port(&uart); + if (ret < 0) + goto err; + + mid->line = ret; + + pci_set_drvdata(pdev, mid); + return 0; + +err: + if (mid->board->exit) + mid->board->exit(mid); + return ret; +} + +static void mid8250_remove(struct pci_dev *pdev) +{ + struct mid8250 *mid = pci_get_drvdata(pdev); + + serial8250_unregister_port(mid->line); + + if (mid->board->exit) + mid->board->exit(mid); +} + +static const struct mid8250_board pnw_board = { + .freq = 50000000, + .base_baud = 115200, + .bar = 0, + .setup = pnw_setup, + .exit = pnw_exit, +}; + +static const struct mid8250_board tng_board = { + .freq = 38400000, + .base_baud = 1843200, + .bar = 0, + .setup = tng_setup, + .exit = tng_exit, +}; + +static const struct mid8250_board dnv_board = { + .freq = 133333333, + .base_baud = 115200, + .bar = 1, + /* + * Errata: + * HSUART May Stop Functioning when DMA is Active. + * + * - Denverton document #572409, rev 3.4, DNV60 + * - Ice Lake Xeon D document #714070, ICXD65 + * - Snowridge document #731931, SNR44 + */ + .setup = PTR_IF(false, dnv_setup), + .exit = PTR_IF(false, dnv_exit), +}; + +static const struct pci_device_id pci_ids[] = { + { PCI_DEVICE_DATA(INTEL, PNW_UART1, &pnw_board) }, + { PCI_DEVICE_DATA(INTEL, PNW_UART2, &pnw_board) }, + { PCI_DEVICE_DATA(INTEL, PNW_UART3, &pnw_board) }, + { PCI_DEVICE_DATA(INTEL, TNG_UART, &tng_board) }, + { PCI_DEVICE_DATA(INTEL, CDF_UART, &dnv_board) }, + { PCI_DEVICE_DATA(INTEL, DNV_UART, &dnv_board) }, + { } +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver mid8250_pci_driver = { + .name = "8250_mid", + .id_table = pci_ids, + .probe = mid8250_probe, + .remove = mid8250_remove, +}; + +module_pci_driver(mid8250_pci_driver); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel MID UART driver"); diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c new file mode 100644 index 000000000..841d64f6a --- /dev/null +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -0,0 +1,701 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Mediatek 8250 driver. + * + * Copyright (c) 2014 MundoReader S.L. + * Author: Matthias Brugger <matthias.bgg@gmail.com> + */ +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/serial_8250.h> +#include <linux/serial_reg.h> +#include <linux/console.h> +#include <linux/dma-mapping.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/units.h> + +#include "8250.h" + +#define MTK_UART_HIGHS 0x09 /* Highspeed register */ +#define MTK_UART_SAMPLE_COUNT 0x0a /* Sample count register */ +#define MTK_UART_SAMPLE_POINT 0x0b /* Sample point register */ +#define MTK_UART_RATE_FIX 0x0d /* UART Rate Fix Register */ +#define MTK_UART_ESCAPE_DAT 0x10 /* Escape Character register */ +#define MTK_UART_ESCAPE_EN 0x11 /* Escape Enable register */ +#define MTK_UART_DMA_EN 0x13 /* DMA Enable register */ +#define MTK_UART_RXTRI_AD 0x14 /* RX Trigger address */ +#define MTK_UART_FRACDIV_L 0x15 /* Fractional divider LSB address */ +#define MTK_UART_FRACDIV_M 0x16 /* Fractional divider MSB address */ +#define MTK_UART_DEBUG0 0x18 +#define MTK_UART_IER_XOFFI 0x20 /* Enable XOFF character interrupt */ +#define MTK_UART_IER_RTSI 0x40 /* Enable RTS Modem status interrupt */ +#define MTK_UART_IER_CTSI 0x80 /* Enable CTS Modem status interrupt */ + +#define MTK_UART_EFR 38 /* I/O: Extended Features Register */ +#define MTK_UART_EFR_EN 0x10 /* Enable enhancement feature */ +#define MTK_UART_EFR_RTS 0x40 /* Enable hardware rx flow control */ +#define MTK_UART_EFR_CTS 0x80 /* Enable hardware tx flow control */ +#define MTK_UART_EFR_NO_SW_FC 0x0 /* no sw flow control */ +#define MTK_UART_EFR_XON1_XOFF1 0xa /* XON1/XOFF1 as sw flow control */ +#define MTK_UART_EFR_XON2_XOFF2 0x5 /* XON2/XOFF2 as sw flow control */ +#define MTK_UART_EFR_SW_FC_MASK 0xf /* Enable CTS Modem status interrupt */ +#define MTK_UART_EFR_HW_FC (MTK_UART_EFR_RTS | MTK_UART_EFR_CTS) +#define MTK_UART_DMA_EN_TX 0x2 +#define MTK_UART_DMA_EN_RX 0x5 + +#define MTK_UART_ESCAPE_CHAR 0x77 /* Escape char added under sw fc */ +#define MTK_UART_RX_SIZE 0x8000 +#define MTK_UART_TX_TRIGGER 1 +#define MTK_UART_RX_TRIGGER MTK_UART_RX_SIZE + +#define MTK_UART_XON1 40 /* I/O: Xon character 1 */ +#define MTK_UART_XOFF1 42 /* I/O: Xoff character 1 */ + +#ifdef CONFIG_SERIAL_8250_DMA +enum dma_rx_status { + DMA_RX_START = 0, + DMA_RX_RUNNING = 1, + DMA_RX_SHUTDOWN = 2, +}; +#endif + +struct mtk8250_data { + int line; + unsigned int rx_pos; + unsigned int clk_count; + struct clk *uart_clk; + struct clk *bus_clk; + struct uart_8250_dma *dma; +#ifdef CONFIG_SERIAL_8250_DMA + enum dma_rx_status rx_status; +#endif + int rx_wakeup_irq; +}; + +/* flow control mode */ +enum { + MTK_UART_FC_NONE, + MTK_UART_FC_SW, + MTK_UART_FC_HW, +}; + +#ifdef CONFIG_SERIAL_8250_DMA +static void mtk8250_rx_dma(struct uart_8250_port *up); + +static void mtk8250_dma_rx_complete(void *param) +{ + struct uart_8250_port *up = param; + struct uart_8250_dma *dma = up->dma; + struct mtk8250_data *data = up->port.private_data; + struct tty_port *tty_port = &up->port.state->port; + struct dma_tx_state state; + int copied, total, cnt; + unsigned char *ptr; + unsigned long flags; + + if (data->rx_status == DMA_RX_SHUTDOWN) + return; + + uart_port_lock_irqsave(&up->port, &flags); + + dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + total = dma->rx_size - state.residue; + cnt = total; + + if ((data->rx_pos + cnt) > dma->rx_size) + cnt = dma->rx_size - data->rx_pos; + + ptr = (unsigned char *)(data->rx_pos + dma->rx_buf); + copied = tty_insert_flip_string(tty_port, ptr, cnt); + data->rx_pos += cnt; + + if (total > cnt) { + ptr = (unsigned char *)(dma->rx_buf); + cnt = total - cnt; + copied += tty_insert_flip_string(tty_port, ptr, cnt); + data->rx_pos = cnt; + } + + up->port.icount.rx += copied; + + tty_flip_buffer_push(tty_port); + + mtk8250_rx_dma(up); + + uart_port_unlock_irqrestore(&up->port, flags); +} + +static void mtk8250_rx_dma(struct uart_8250_port *up) +{ + struct uart_8250_dma *dma = up->dma; + struct dma_async_tx_descriptor *desc; + + desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, + dma->rx_size, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + pr_err("failed to prepare rx slave single\n"); + return; + } + + desc->callback = mtk8250_dma_rx_complete; + desc->callback_param = up; + + dma->rx_cookie = dmaengine_submit(desc); + + dma_async_issue_pending(dma->rxchan); +} + +static void mtk8250_dma_enable(struct uart_8250_port *up) +{ + struct uart_8250_dma *dma = up->dma; + struct mtk8250_data *data = up->port.private_data; + int lcr = serial_in(up, UART_LCR); + + if (data->rx_status != DMA_RX_START) + return; + + dma->rxconf.src_port_window_size = dma->rx_size; + dma->rxconf.src_addr = dma->rx_addr; + + dma->txconf.dst_port_window_size = UART_XMIT_SIZE; + dma->txconf.dst_addr = dma->tx_addr; + + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial_out(up, MTK_UART_DMA_EN, + MTK_UART_DMA_EN_RX | MTK_UART_DMA_EN_TX); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, MTK_UART_EFR, UART_EFR_ECB); + serial_out(up, UART_LCR, lcr); + + if (dmaengine_slave_config(dma->rxchan, &dma->rxconf) != 0) + pr_err("failed to configure rx dma channel\n"); + if (dmaengine_slave_config(dma->txchan, &dma->txconf) != 0) + pr_err("failed to configure tx dma channel\n"); + + data->rx_status = DMA_RX_RUNNING; + data->rx_pos = 0; + mtk8250_rx_dma(up); +} +#endif + +static int mtk8250_startup(struct uart_port *port) +{ +#ifdef CONFIG_SERIAL_8250_DMA + struct uart_8250_port *up = up_to_u8250p(port); + struct mtk8250_data *data = port->private_data; + + /* disable DMA for console */ + if (uart_console(port)) + up->dma = NULL; + + if (up->dma) { + data->rx_status = DMA_RX_START; + kfifo_reset(&port->state->port.xmit_fifo); + } +#endif + memset(&port->icount, 0, sizeof(port->icount)); + + return serial8250_do_startup(port); +} + +static void mtk8250_shutdown(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct mtk8250_data *data = port->private_data; + int irq = data->rx_wakeup_irq; + +#ifdef CONFIG_SERIAL_8250_DMA + if (up->dma) + data->rx_status = DMA_RX_SHUTDOWN; +#endif + + serial8250_do_shutdown(port); + + if (irq >= 0) + serial8250_do_set_mctrl(&up->port, TIOCM_RTS); +} + +static void mtk8250_disable_intrs(struct uart_8250_port *up, int mask) +{ + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&up->port.lock); + + serial_out(up, UART_IER, serial_in(up, UART_IER) & (~mask)); +} + +static void mtk8250_enable_intrs(struct uart_8250_port *up, int mask) +{ + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&up->port.lock); + + serial_out(up, UART_IER, serial_in(up, UART_IER) | mask); +} + +static void mtk8250_set_flow_ctrl(struct uart_8250_port *up, int mode) +{ + struct uart_port *port = &up->port; + int lcr = serial_in(up, UART_LCR); + + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&port->lock); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, MTK_UART_EFR, UART_EFR_ECB); + serial_out(up, UART_LCR, lcr); + lcr = serial_in(up, UART_LCR); + + switch (mode) { + case MTK_UART_FC_NONE: + serial_out(up, MTK_UART_ESCAPE_DAT, MTK_UART_ESCAPE_CHAR); + serial_out(up, MTK_UART_ESCAPE_EN, 0x00); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, MTK_UART_EFR, serial_in(up, MTK_UART_EFR) & + (~(MTK_UART_EFR_HW_FC | MTK_UART_EFR_SW_FC_MASK))); + serial_out(up, UART_LCR, lcr); + mtk8250_disable_intrs(up, MTK_UART_IER_XOFFI | + MTK_UART_IER_RTSI | MTK_UART_IER_CTSI); + break; + + case MTK_UART_FC_HW: + serial_out(up, MTK_UART_ESCAPE_DAT, MTK_UART_ESCAPE_CHAR); + serial_out(up, MTK_UART_ESCAPE_EN, 0x00); + serial_out(up, UART_MCR, UART_MCR_RTS); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + /*enable hw flow control*/ + serial_out(up, MTK_UART_EFR, MTK_UART_EFR_HW_FC | + (serial_in(up, MTK_UART_EFR) & + (~(MTK_UART_EFR_HW_FC | MTK_UART_EFR_SW_FC_MASK)))); + + serial_out(up, UART_LCR, lcr); + mtk8250_disable_intrs(up, MTK_UART_IER_XOFFI); + mtk8250_enable_intrs(up, MTK_UART_IER_CTSI | MTK_UART_IER_RTSI); + break; + + case MTK_UART_FC_SW: /*MTK software flow control */ + serial_out(up, MTK_UART_ESCAPE_DAT, MTK_UART_ESCAPE_CHAR); + serial_out(up, MTK_UART_ESCAPE_EN, 0x01); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + /*enable sw flow control */ + serial_out(up, MTK_UART_EFR, MTK_UART_EFR_XON1_XOFF1 | + (serial_in(up, MTK_UART_EFR) & + (~(MTK_UART_EFR_HW_FC | MTK_UART_EFR_SW_FC_MASK)))); + + serial_out(up, MTK_UART_XON1, START_CHAR(port->state->port.tty)); + serial_out(up, MTK_UART_XOFF1, STOP_CHAR(port->state->port.tty)); + serial_out(up, UART_LCR, lcr); + mtk8250_disable_intrs(up, MTK_UART_IER_CTSI|MTK_UART_IER_RTSI); + mtk8250_enable_intrs(up, MTK_UART_IER_XOFFI); + break; + default: + break; + } +} + +static void +mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, + const struct ktermios *old) +{ + static const unsigned short fraction_L_mapping[] = { + 0, 1, 0x5, 0x15, 0x55, 0x57, 0x57, 0x77, 0x7F, 0xFF, 0xFF + }; + static const unsigned short fraction_M_mapping[] = { + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 3 + }; + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int baud, quot, fraction; + unsigned long flags; + int mode; + +#ifdef CONFIG_SERIAL_8250_DMA + if (up->dma) { + if (uart_console(port)) { + devm_kfree(up->port.dev, up->dma); + up->dma = NULL; + } else { + mtk8250_dma_enable(up); + } + } +#endif + + /* + * Store the requested baud rate before calling the generic 8250 + * set_termios method. Standard 8250 port expects bauds to be + * no higher than (uartclk / 16) so the baud will be clamped if it + * gets out of that bound. Mediatek 8250 port supports speed + * higher than that, therefore we'll get original baud rate back + * after calling the generic set_termios method and recalculate + * the speed later in this method. + */ + baud = tty_termios_baud_rate(termios); + + serial8250_do_set_termios(port, termios, NULL); + + tty_termios_encode_baud_rate(termios, baud, baud); + + /* + * Mediatek UARTs use an extra highspeed register (MTK_UART_HIGHS) + * + * We need to recalculate the quot register, as the calculation depends + * on the value in the highspeed register. + * + * Some baudrates are not supported by the chip, so we use the next + * lower rate supported and update termios c_flag. + * + * If highspeed register is set to 3, we need to specify sample count + * and sample point to increase accuracy. If not, we reset the + * registers to their default values. + */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / UART_DIV_MAX, + port->uartclk); + + if (baud < 115200) { + serial_port_out(port, MTK_UART_HIGHS, 0x0); + quot = uart_get_divisor(port, baud); + } else { + serial_port_out(port, MTK_UART_HIGHS, 0x3); + quot = DIV_ROUND_UP(port->uartclk, 256 * baud); + } + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + uart_port_lock_irqsave(port, &flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* set DLAB we have cval saved in up->lcr from the call to the core */ + serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB); + serial_dl_write(up, quot); + + /* reset DLAB */ + serial_port_out(port, UART_LCR, up->lcr); + + if (baud >= 115200) { + unsigned int tmp; + + tmp = (port->uartclk / (baud * quot)) - 1; + serial_port_out(port, MTK_UART_SAMPLE_COUNT, tmp); + serial_port_out(port, MTK_UART_SAMPLE_POINT, + (tmp >> 1) - 1); + + /*count fraction to set fractoin register */ + fraction = ((port->uartclk * 100) / baud / quot) % 100; + fraction = DIV_ROUND_CLOSEST(fraction, 10); + serial_port_out(port, MTK_UART_FRACDIV_L, + fraction_L_mapping[fraction]); + serial_port_out(port, MTK_UART_FRACDIV_M, + fraction_M_mapping[fraction]); + } else { + serial_port_out(port, MTK_UART_SAMPLE_COUNT, 0x00); + serial_port_out(port, MTK_UART_SAMPLE_POINT, 0xff); + serial_port_out(port, MTK_UART_FRACDIV_L, 0x00); + serial_port_out(port, MTK_UART_FRACDIV_M, 0x00); + } + + if ((termios->c_cflag & CRTSCTS) && (!(termios->c_iflag & CRTSCTS))) + mode = MTK_UART_FC_HW; + else if (termios->c_iflag & CRTSCTS) + mode = MTK_UART_FC_SW; + else + mode = MTK_UART_FC_NONE; + + mtk8250_set_flow_ctrl(up, mode); + + if (uart_console(port)) + up->port.cons->cflag = termios->c_cflag; + + uart_port_unlock_irqrestore(port, flags); + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +static int __maybe_unused mtk8250_runtime_suspend(struct device *dev) +{ + struct mtk8250_data *data = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(data->line); + + /* wait until UART in idle status */ + while + (serial_in(up, MTK_UART_DEBUG0)); + + clk_disable_unprepare(data->uart_clk); + clk_disable_unprepare(data->bus_clk); + + return 0; +} + +static int __maybe_unused mtk8250_runtime_resume(struct device *dev) +{ + struct mtk8250_data *data = dev_get_drvdata(dev); + + clk_prepare_enable(data->bus_clk); + clk_prepare_enable(data->uart_clk); + + return 0; +} + +static void +mtk8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) +{ + if (!state) + pm_runtime_get_sync(port->dev); + + serial8250_do_pm(port, state, old); + + if (state) + pm_runtime_put_sync_suspend(port->dev); +} + +#ifdef CONFIG_SERIAL_8250_DMA +static bool mtk8250_dma_filter(struct dma_chan *chan, void *param) +{ + return false; +} +#endif + +static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p, + struct mtk8250_data *data) +{ +#ifdef CONFIG_SERIAL_8250_DMA + int dmacnt; +#endif + + data->uart_clk = devm_clk_get_enabled(&pdev->dev, "baud"); + if (IS_ERR(data->uart_clk)) { + /* + * For compatibility with older device trees try unnamed + * clk when no baud clk can be found. + */ + data->uart_clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(data->uart_clk)) { + dev_warn(&pdev->dev, "Can't get uart clock\n"); + return PTR_ERR(data->uart_clk); + } + + return 0; + } + + data->bus_clk = devm_clk_get_enabled(&pdev->dev, "bus"); + if (IS_ERR(data->bus_clk)) + return PTR_ERR(data->bus_clk); + + data->dma = NULL; +#ifdef CONFIG_SERIAL_8250_DMA + dmacnt = of_property_count_strings(pdev->dev.of_node, "dma-names"); + if (dmacnt == 2) { + data->dma = devm_kzalloc(&pdev->dev, sizeof(*data->dma), + GFP_KERNEL); + if (!data->dma) + return -ENOMEM; + + data->dma->fn = mtk8250_dma_filter; + data->dma->rx_size = MTK_UART_RX_SIZE; + data->dma->rxconf.src_maxburst = MTK_UART_RX_TRIGGER; + data->dma->txconf.dst_maxburst = MTK_UART_TX_TRIGGER; + } +#endif + + return 0; +} + +static int mtk8250_probe(struct platform_device *pdev) +{ + struct uart_8250_port uart = {}; + struct mtk8250_data *data; + struct resource *regs; + int irq, err, line; + struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(&pdev->dev, "no registers defined\n"); + return -EINVAL; + } + + uart.port.membase = devm_ioremap(&pdev->dev, regs->start, + resource_size(regs)); + if (!uart.port.membase) + return -ENOMEM; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->clk_count = 0; + + if (is_of_node(fwnode)) { + err = mtk8250_probe_of(pdev, &uart.port, data); + if (err) + return err; + } else if (!fwnode) { + return -ENODEV; + } + + spin_lock_init(&uart.port.lock); + uart.port.mapbase = regs->start; + uart.port.irq = irq; + uart.port.pm = mtk8250_do_pm; + uart.port.type = PORT_16550; + uart.port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; + uart.port.dev = &pdev->dev; + uart.port.iotype = UPIO_MEM32; + uart.port.regshift = 2; + uart.port.private_data = data; + uart.port.shutdown = mtk8250_shutdown; + uart.port.startup = mtk8250_startup; + uart.port.set_termios = mtk8250_set_termios; + uart.port.uartclk = clk_get_rate(data->uart_clk); + if (!uart.port.uartclk) + uart.port.uartclk = 26 * HZ_PER_MHZ; +#ifdef CONFIG_SERIAL_8250_DMA + if (data->dma) + uart.dma = data->dma; +#endif + + if (is_of_node(fwnode)) { + line = of_alias_get_id(pdev->dev.of_node, "serial"); + if (line >= 0) + uart.port.line = line; + + /* Disable Rate Fix function */ + writel(0x0, uart.port.membase + + (MTK_UART_RATE_FIX << uart.port.regshift)); + } + + platform_set_drvdata(pdev, data); + + data->line = serial8250_register_8250_port(&uart); + if (data->line < 0) + return data->line; + + data->rx_wakeup_irq = platform_get_irq_optional(pdev, 1); + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + return 0; +} + +static void mtk8250_remove(struct platform_device *pdev) +{ + struct mtk8250_data *data = platform_get_drvdata(pdev); + + pm_runtime_get_sync(&pdev->dev); + + serial8250_unregister_port(data->line); + + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); +} + +static int __maybe_unused mtk8250_suspend(struct device *dev) +{ + struct mtk8250_data *data = dev_get_drvdata(dev); + int irq = data->rx_wakeup_irq; + int err; + + serial8250_suspend_port(data->line); + + pinctrl_pm_select_sleep_state(dev); + if (irq >= 0) { + err = enable_irq_wake(irq); + if (err) { + dev_err(dev, + "failed to enable irq wake on IRQ %d: %d\n", + irq, err); + pinctrl_pm_select_default_state(dev); + serial8250_resume_port(data->line); + return err; + } + } + + return 0; +} + +static int __maybe_unused mtk8250_resume(struct device *dev) +{ + struct mtk8250_data *data = dev_get_drvdata(dev); + int irq = data->rx_wakeup_irq; + + if (irq >= 0) + disable_irq_wake(irq); + pinctrl_pm_select_default_state(dev); + + serial8250_resume_port(data->line); + + return 0; +} + +static const struct dev_pm_ops mtk8250_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mtk8250_suspend, mtk8250_resume) + SET_RUNTIME_PM_OPS(mtk8250_runtime_suspend, mtk8250_runtime_resume, + NULL) +}; + +static const struct of_device_id mtk8250_of_match[] = { + { .compatible = "mediatek,mt6577-uart" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mtk8250_of_match); + +static const struct acpi_device_id mtk8250_acpi_match[] = { + { "MTKI0511" }, + { "NVDA0240" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, mtk8250_acpi_match); + +static struct platform_driver mtk8250_platform_driver = { + .driver = { + .name = "mt6577-uart", + .pm = &mtk8250_pm_ops, + .of_match_table = mtk8250_of_match, + .acpi_match_table = mtk8250_acpi_match, + }, + .probe = mtk8250_probe, + .remove = mtk8250_remove, +}; +module_platform_driver(mtk8250_platform_driver); + +#ifdef CONFIG_SERIAL_8250_CONSOLE +static int __init early_mtk8250_setup(struct earlycon_device *device, + const char *options) +{ + if (!device->port.membase) + return -ENODEV; + + device->port.iotype = UPIO_MEM32; + device->port.regshift = 2; + + return early_serial8250_setup(device, NULL); +} + +OF_EARLYCON_DECLARE(mtk8250, "mediatek,mt6577-uart", early_mtk8250_setup); +#endif + +MODULE_AUTHOR("Matthias Brugger"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Mediatek 8250 serial port driver"); diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c new file mode 100644 index 000000000..ce873fbd6 --- /dev/null +++ b/drivers/tty/serial/8250/8250_mxpcie.c @@ -0,0 +1,627 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Moxa PCIe multiport serial device driver + * + * Copyright (C) 2025 Moxa Inc. (support@moxa.com) + * Author: Crescent Hsieh <crescentcy.hsieh@moxa.com> + */ +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/dev_printk.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/tty_flip.h> +#include <linux/types.h> + +#include <linux/8250_pci.h> +#include <linux/serial_8250.h> + +#include "8250.h" + +#define PCI_DEVICE_ID_MOXA_CP102E 0x1024 +#define PCI_DEVICE_ID_MOXA_CP102EL 0x1025 +#define PCI_DEVICE_ID_MOXA_CP102N 0x1027 +#define PCI_DEVICE_ID_MOXA_CP104EL_A 0x1045 +#define PCI_DEVICE_ID_MOXA_CP104N 0x1046 +#define PCI_DEVICE_ID_MOXA_CP112N 0x1121 +#define PCI_DEVICE_ID_MOXA_CP114EL 0x1144 +#define PCI_DEVICE_ID_MOXA_CP114N 0x1145 +#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_CP132N 0x1323 +#define PCI_DEVICE_ID_MOXA_CP134EL_A 0x1342 +#define PCI_DEVICE_ID_MOXA_CP134N 0x1343 +#define PCI_DEVICE_ID_MOXA_CP138E_A 0x1381 +#define PCI_DEVICE_ID_MOXA_CP168EL_A 0x1683 + +/* Bits in PCI device ID encoding board capabilities */ +#define MOXA_DEV_ID_IFACE_MASK GENMASK(11, 8) /* Supported serial interface */ +#define MOXA_DEV_ID_NPORTS_MASK GENMASK(7, 4) /* Number of UART ports */ + +/* UART */ +#define MOXA_PUART_BASE_BAUD 921600 +#define MOXA_PUART_OFFSET 0x200 +#define MOXA_PUART_TX_TRIG_DEFAULT 0 +#define MOXA_PUART_RX_TRIG_DEFAULT 96 +#define MOXA_PUART_RX_FLOW_LOW_DEFAULT 16 +#define MOXA_PUART_RX_FLOW_HIGH_DEFAULT 110 + +/* Special Function Register (SFR) */ +#define MOXA_PUART_SFR 0x07 +#define MOXA_PUART_SFR_FORCE_TX BIT(0) +#define MOXA_PUART_SFR_950 BIT(5) + +/* Enhanced Function Register (EFR) */ +#define MOXA_PUART_EFR 0x0A +#define MOXA_PUART_EFR_ENHANCED BIT(4) +#define MOXA_PUART_EFR_AUTO_RTS BIT(6) +#define MOXA_PUART_EFR_AUTO_CTS BIT(7) +#define MOXA_PUART_EFR_RX_FLOW_MASK GENMASK(1, 0) +#define MOXA_PUART_EFR_RX_FLOW_DISABLED 0x0 +#define MOXA_PUART_EFR_RX_FLOW_XON2_XOFF2 0x1 +#define MOXA_PUART_EFR_RX_FLOW_XON1_XOFF1 0x2 +#define MOXA_PUART_EFR_RX_FLOW_COPY_TX 0x3 +#define MOXA_PUART_EFR_TX_FLOW_MASK GENMASK(3, 2) +#define MOXA_PUART_EFR_TX_FLOW_DISABLED 0x0 +#define MOXA_PUART_EFR_TX_FLOW_XON2_XOFF2 0x1 +#define MOXA_PUART_EFR_TX_FLOW_XON1_XOFF1 0x2 +#define MOXA_PUART_EFR_TX_FLOW_RESERVED 0x3 + +#define MOXA_PUART_XON1 0x0B +#define MOXA_PUART_XON2 0x0C +#define MOXA_PUART_XOFF1 0x0D +#define MOXA_PUART_XOFF2 0x0E +#define MOXA_PUART_TTL 0x10 /* Tx Interrupt Trigger Level */ +#define MOXA_PUART_RTL 0x11 /* Rx Interrupt Trigger Level */ +#define MOXA_PUART_FCL 0x12 /* Flow Control Low Trigger Level */ +#define MOXA_PUART_FCH 0x13 /* Flow Control High Trigger Level */ +#define MOXA_PUART_RX_FIFO_CNT 0x15 /* Rx FIFO Data Counter */ +#define MOXA_PUART_TX_FIFO_CNT 0x16 /* Tx FIFO Data Counter */ + +#define MOXA_PUART_RX_FIFO_MEM 0x100 /* Memory Space to Rx FIFO Data Register */ +#define MOXA_PUART_TX_FIFO_MEM 0x100 /* Memory Space to Tx FIFO Data Register */ + +#define MOXA_GPIO_DIRECTION 0x09 +#define MOXA_GPIO_OUTPUT 0x0A + +#define MOXA_GPIO_PIN2 BIT(2) + +#define MOXA_UIR_OFFSET 0x04 +#define MOXA_UIR_RS232 0x00 +#define MOXA_UIR_RS422 0x01 +#define MOXA_UIR_RS485_4W 0x0B +#define MOXA_UIR_RS485_2W 0x0F + +#define MOXA_EVEN_RS_MASK GENMASK(3, 0) +#define MOXA_ODD_RS_MASK GENMASK(7, 4) + +struct mxpcie8250_port { + int line; + u8 rx_trig_level; +}; + +struct mxpcie8250 { + unsigned int supp_rs; + unsigned int num_ports; + void __iomem *bar1_base; /* UART registers (MMIO) */ + void __iomem *bar2_base; /* UIR / GPIO / CPLD (IO) */ + struct mxpcie8250_port port[] __counted_by(num_ports); +}; + +enum { + MOXA_SUPP_RS232 = BIT(0), + MOXA_SUPP_RS422 = BIT(1), + MOXA_SUPP_RS485 = BIT(2), +}; + +static const struct serial_rs485 mxpcie8250_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX | SER_RS485_MODE_RS422, +}; + +static bool mxpcie8250_is_mini_pcie(unsigned short device) +{ + if (device == PCI_DEVICE_ID_MOXA_CP102N || + device == PCI_DEVICE_ID_MOXA_CP104N || + device == PCI_DEVICE_ID_MOXA_CP112N || + device == PCI_DEVICE_ID_MOXA_CP114N || + device == PCI_DEVICE_ID_MOXA_CP132N || + device == PCI_DEVICE_ID_MOXA_CP134N) + return true; + + return false; +} + +static unsigned int mxpcie8250_get_supp_rs(unsigned short device) +{ + switch (device & MOXA_DEV_ID_IFACE_MASK) { + case 0x0000: + case 0x0600: + return MOXA_SUPP_RS232; + case 0x0100: + return MOXA_SUPP_RS232 | MOXA_SUPP_RS422 | MOXA_SUPP_RS485; + case 0x0300: + return MOXA_SUPP_RS422 | MOXA_SUPP_RS485; + default: + return 0; + } +} + +static unsigned short mxpcie8250_get_nports(unsigned short device) +{ + switch (device) { + case PCI_DEVICE_ID_MOXA_CP116E_A_A: + case PCI_DEVICE_ID_MOXA_CP116E_A_B: + return 8; + default: + return FIELD_GET(MOXA_DEV_ID_NPORTS_MASK, device); + } +} + +static void mxpcie8250_set_interface(struct mxpcie8250 *priv, + unsigned int port_idx, + u8 mode) +{ + void __iomem *uir_addr = priv->bar2_base + MOXA_UIR_OFFSET + port_idx / 2; + u8 cval; + + cval = ioread8(uir_addr); + + if (port_idx % 2) + FIELD_MODIFY(MOXA_ODD_RS_MASK, &cval, mode); + else + FIELD_MODIFY(MOXA_EVEN_RS_MASK, &cval, mode); + + iowrite8(cval, uir_addr); +} + +/* + * Moxa PCIe multiport serial boards support switching serial interfaces + * via the ioctl() command "TIOCSRS485". Supported modes and corresponding + * flags in "serial_rs485": + * + * RS232 = (no flags set) + * RS422 = SER_RS485_ENABLED | SER_RS485_MODE_RS422 + * RS485_2W (half-duplex) = SER_RS485_ENABLED + * RS485_4W (full-duplex) = SER_RS485_ENABLED | SER_RS485_RX_DURING_TX + */ +static int mxpcie8250_rs485_config(struct uart_port *port, + struct ktermios *termios, + struct serial_rs485 *rs485) +{ + struct mxpcie8250 *priv = dev_get_drvdata(port->dev); + u8 mode = MOXA_UIR_RS232; + + if (rs485->flags & SER_RS485_ENABLED) { + if (rs485->flags & SER_RS485_MODE_RS422) + mode = MOXA_UIR_RS422; + else if (rs485->flags & SER_RS485_RX_DURING_TX) + mode = MOXA_UIR_RS485_4W; + else + mode = MOXA_UIR_RS485_2W; + } else if (!(priv->supp_rs & MOXA_SUPP_RS232)) { + return -ENODEV; + } + mxpcie8250_set_interface(priv, port->port_id, mode); + + return 0; +} + +static void mxpcie8250_set_termios(struct uart_port *port, + struct ktermios *new, + const struct ktermios *old) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct tty_struct *tty = port->state->port.tty; + unsigned int cflag = tty->termios.c_cflag; + u8 efr, val; + + serial8250_do_set_termios(port, new, old); + + up->port.status &= ~(UPSTAT_AUTORTS | UPSTAT_AUTOCTS | UPSTAT_AUTOXOFF); + + efr = serial_in(up, MOXA_PUART_EFR); + efr &= ~(MOXA_PUART_EFR_AUTO_RTS | MOXA_PUART_EFR_AUTO_CTS); + + if (cflag & CRTSCTS) { + efr |= (MOXA_PUART_EFR_AUTO_RTS | MOXA_PUART_EFR_AUTO_CTS); + up->port.status |= (UPSTAT_AUTORTS | UPSTAT_AUTOCTS); + } + /* Set on-chip software flow control character */ + serial_out(up, MOXA_PUART_XON1, START_CHAR(tty)); + serial_out(up, MOXA_PUART_XON2, START_CHAR(tty)); + serial_out(up, MOXA_PUART_XOFF1, STOP_CHAR(tty)); + serial_out(up, MOXA_PUART_XOFF2, STOP_CHAR(tty)); + + val = I_IXON(tty) ? MOXA_PUART_EFR_RX_FLOW_XON1_XOFF1 : MOXA_PUART_EFR_RX_FLOW_DISABLED; + FIELD_MODIFY(MOXA_PUART_EFR_RX_FLOW_MASK, &efr, val); + + val = I_IXOFF(tty) ? MOXA_PUART_EFR_TX_FLOW_XON1_XOFF1 : MOXA_PUART_EFR_TX_FLOW_DISABLED; + FIELD_MODIFY(MOXA_PUART_EFR_TX_FLOW_MASK, &efr, val); + + if (I_IXOFF(tty)) + up->port.status |= UPSTAT_AUTOXOFF; + + serial_out(up, MOXA_PUART_EFR, efr); +} + +static int mxpcie8250_get_rxtrig(struct uart_port *port) +{ + return serial_port_in(port, MOXA_PUART_RTL); +} + +static int mxpcie8250_set_rxtrig(struct uart_port *port, unsigned char bytes) +{ + struct mxpcie8250 *priv = dev_get_drvdata(port->dev); + + if (bytes > port->fifosize) + return -EINVAL; + + serial_port_out(port, MOXA_PUART_RTL, bytes); + priv->port[port->port_id].rx_trig_level = bytes; + + return 0; +} + +static int mxpcie8250_startup(struct uart_port *port) +{ + struct mxpcie8250 *priv = dev_get_drvdata(port->dev); + struct uart_8250_port *up = up_to_u8250p(port); + int ret; + + ret = serial8250_do_startup(port); + if (ret) + return ret; + + /* + * The TX FIFO write pointer (w_ptr) and read pointer (r_ptr) + * are driven by different clocks: w_ptr uses the PCIe clock + * and r_ptr uses the UART clock. When TX FIFO flush is requested, + * w_ptr may be cleared before r_ptr, so the UART can still observe + * pending TX data. + * + * It is recommended to clear the FIFOs at least 5 times to ensure + * both pointers are reset. + */ + for (unsigned int i = 0; i < 5; ++i) + serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + + serial_out(up, MOXA_PUART_EFR, MOXA_PUART_EFR_ENHANCED); + serial_out(up, MOXA_PUART_SFR, MOXA_PUART_SFR_950); + + serial_out(up, MOXA_PUART_TTL, MOXA_PUART_TX_TRIG_DEFAULT); + serial_out(up, MOXA_PUART_RTL, priv->port[port->port_id].rx_trig_level); + serial_out(up, MOXA_PUART_FCL, MOXA_PUART_RX_FLOW_LOW_DEFAULT); + serial_out(up, MOXA_PUART_FCH, MOXA_PUART_RX_FLOW_HIGH_DEFAULT); + + return 0; +} + +static void mxpcie8250_shutdown(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + serial_out(up, MOXA_PUART_EFR, 0); + serial_out(up, MOXA_PUART_SFR, 0); + + serial8250_do_shutdown(port); +} + +static void mxpcie8250_throttle(struct uart_port *port) +{ + guard(uart_port_lock_irqsave)(port); + + port->ops->stop_rx(port); +} + +static void mxpcie8250_unthrottle(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + guard(uart_port_lock_irqsave)(port); + + up->ier |= UART_IER_RLSI | UART_IER_RDI; + port->read_status_mask |= UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + +static void mxpcie8250_rx_chars(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + struct tty_port *tport = &port->state->port; + unsigned int count; + u8 *buf; + + count = serial_in(up, MOXA_PUART_RX_FIFO_CNT); + count = min(count, port->fifosize); + count = tty_prepare_flip_string(tport, &buf, count); + if (!count) + return; + + for (unsigned int i = 0; i < count; ++i) + buf[i] = serial_in(up, MOXA_PUART_RX_FIFO_MEM + i); + + port->icount.rx += count; + tty_flip_buffer_push(tport); +} + +static bool mxpcie8250_should_rx(struct uart_8250_port *up, u16 lsr) +{ + struct uart_port *port = &up->port; + + if (!(lsr & (UART_LSR_DR | UART_LSR_BI))) + return false; + + if (!(port->status & (UPSTAT_AUTOCTS | UPSTAT_AUTORTS))) + return true; + if (lsr & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) + return true; + if (port->read_status_mask & UART_LSR_DR) + return true; + + return false; +} + +static void mxpcie8250_tx_chars(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + unsigned int offset = 0; + unsigned char c; + + uart_port_tx_limited(port, c, port->fifosize - serial_in(up, MOXA_PUART_TX_FIFO_CNT), + true, + serial_out(up, MOXA_PUART_TX_FIFO_MEM + offset++, c), + ({})); +} + +static int mxpcie8250_handle_irq(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + u16 lsr; + u8 iir; + + iir = serial_in(up, UART_IIR); + if (iir & UART_IIR_NO_INT) + return 0; + + guard(uart_port_lock_check_sysrq_irqsave)(port); + + lsr = serial_lsr_in(up); + if (mxpcie8250_should_rx(up, lsr)) { + if (!(lsr & UART_LSR_BRK_ERROR_BITS)) + mxpcie8250_rx_chars(up); + else + lsr = serial8250_rx_chars(up, lsr); + } + + serial8250_modem_status(up); + + if ((lsr & UART_LSR_THRE) && (up->ier & UART_IER_THRI)) + mxpcie8250_tx_chars(up); + + return 1; +} + +static void mxpcie8250_software_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct tty_struct *tty = port->state->port.tty; + unsigned int baud, quot; + u8 sfr, tx_byte = 0x01; + + guard(uart_port_lock_irqsave)(port); + + if (break_state == -1) { + serial_out(up, UART_LCR, up->lcr | UART_LCR_DLAB); + serial_dl_write(up, 0); + serial_out(up, UART_LCR, up->lcr); + + serial_out(up, MOXA_PUART_TX_FIFO_MEM, tx_byte); + + sfr = serial_in(up, MOXA_PUART_SFR); + serial_out(up, MOXA_PUART_SFR, sfr | MOXA_PUART_SFR_FORCE_TX); + + up->lcr |= UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + } else { + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + + sfr = serial_in(up, MOXA_PUART_SFR); + serial_out(up, MOXA_PUART_SFR, sfr & ~MOXA_PUART_SFR_FORCE_TX); + + serial_out(up, UART_FCR, UART_FCR_CLEAR_XMIT); + + baud = tty_get_baud_rate(tty); + quot = uart_get_divisor(port, baud); + serial8250_do_set_divisor(port, baud, quot); + serial_out(up, UART_LCR, up->lcr); + } +} + +static void mxpcie8250_break_ctl(struct uart_port *port, int break_state) +{ + if (port->rs485.flags & SER_RS485_ENABLED && + !(port->rs485.flags & SER_RS485_MODE_RS422)) + mxpcie8250_software_break_ctl(port, break_state); + else + serial8250_do_break_ctl(port, break_state); +} + +static void mxpcie8250_init_board(struct pci_dev *pdev, struct mxpcie8250 *priv) +{ + void __iomem *bar2_base = priv->bar2_base; + unsigned short device = pdev->device; + u8 cval; + + /* Initial terminator */ + if (device == PCI_DEVICE_ID_MOXA_CP114EL || + device == PCI_DEVICE_ID_MOXA_CP118EL_A) { + iowrite8(0xff, bar2_base + MOXA_GPIO_DIRECTION); + iowrite8(0x00, bar2_base + MOXA_GPIO_OUTPUT); + } + /* + * Enable hardware buffer to prevent break signal output when system boots up. + * This hardware buffer is only supported on Mini PCIe series. + */ + if (mxpcie8250_is_mini_pcie(device)) { + /* Set GPIO direction */ + cval = ioread8(bar2_base + MOXA_GPIO_DIRECTION); + cval |= MOXA_GPIO_PIN2; + iowrite8(cval, bar2_base + MOXA_GPIO_DIRECTION); + /* Enable low GPIO */ + cval = ioread8(bar2_base + MOXA_GPIO_OUTPUT); + cval &= ~MOXA_GPIO_PIN2; + iowrite8(cval, bar2_base + MOXA_GPIO_OUTPUT); + } +} + +static void mxpcie8250_setup_port(struct pci_dev *pdev, + struct mxpcie8250 *priv, + struct uart_8250_port *up, + int idx) +{ + unsigned short device = pdev->device; + int offset = idx * MOXA_PUART_OFFSET; + u8 init_mode = MOXA_UIR_RS232; + + if (priv->supp_rs & MOXA_SUPP_RS485) { + up->port.rs485_config = mxpcie8250_rs485_config; + up->port.rs485_supported = mxpcie8250_rs485_supported; + } + if (!(priv->supp_rs & MOXA_SUPP_RS232)) { + init_mode = MOXA_UIR_RS422; + up->port.rs485.flags = SER_RS485_ENABLED | SER_RS485_MODE_RS422; + } + mxpcie8250_set_interface(priv, idx, init_mode); + + if (idx == 3 && + (device == PCI_DEVICE_ID_MOXA_CP104EL_A || + device == PCI_DEVICE_ID_MOXA_CP114EL || + device == PCI_DEVICE_ID_MOXA_CP134EL_A)) + offset = 7 * MOXA_PUART_OFFSET; + + up->port.mapbase = pci_resource_start(pdev, FL_BASE1) + offset; + up->port.membase = pcim_iomap_table(pdev)[FL_BASE1] + offset; +} + +static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct uart_8250_port up = {}; + struct mxpcie8250 *priv; + unsigned short device = pdev->device; + unsigned int num_ports; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + num_ports = mxpcie8250_get_nports(device); + + priv = devm_kzalloc(dev, struct_size(priv, port, num_ports), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->num_ports = num_ports; + priv->supp_rs = mxpcie8250_get_supp_rs(device); + + priv->bar1_base = pcim_iomap(pdev, FL_BASE1, 0); + if (!priv->bar1_base) + return -ENOMEM; + + priv->bar2_base = pcim_iomap(pdev, FL_BASE2, 0); + if (!priv->bar2_base) + return -ENOMEM; + + mxpcie8250_init_board(pdev, priv); + + up.port.dev = dev; + up.port.irq = pdev->irq; + up.port.uartclk = MOXA_PUART_BASE_BAUD * 16; + up.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ | UPF_FIXED_TYPE; + up.port.type = PORT_MUEX50; + + up.port.iotype = UPIO_MEM; + up.port.iobase = 0; + up.port.regshift = 0; + + up.port.set_termios = mxpcie8250_set_termios; + up.port.get_rxtrig = mxpcie8250_get_rxtrig; + up.port.set_rxtrig = mxpcie8250_set_rxtrig; + up.port.startup = mxpcie8250_startup; + up.port.shutdown = mxpcie8250_shutdown; + up.port.throttle = mxpcie8250_throttle; + up.port.unthrottle = mxpcie8250_unthrottle; + up.port.handle_irq = mxpcie8250_handle_irq; + up.port.break_ctl = mxpcie8250_break_ctl; + + for (unsigned int i = 0; i < num_ports; i++) { + mxpcie8250_setup_port(pdev, priv, &up, i); + + dev_dbg(dev, "Setup PCI port: port %lx, irq %d, type %d\n", + up.port.iobase, up.port.irq, up.port.iotype); + + priv->port[i].line = serial8250_register_8250_port(&up); + if (priv->port[i].line < 0) { + dev_err(dev, + "Couldn't register serial port %lx, irq %d, type %d, error %d\n", + up.port.iobase, up.port.irq, + up.port.iotype, priv->port[i].line); + break; + } + priv->port[i].rx_trig_level = MOXA_PUART_RX_TRIG_DEFAULT; + } + pci_set_drvdata(pdev, priv); + + return 0; +} + +static void mxpcie8250_remove(struct pci_dev *pdev) +{ + struct mxpcie8250 *priv = pci_get_drvdata(pdev); + + for (unsigned int i = 0; i < priv->num_ports; i++) + serial8250_unregister_port(priv->port[i].line); +} + +static const struct pci_device_id mxpcie8250_pci_ids[] = { + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102E) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102EL) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102N) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL_A) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104N) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP112N) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114EL) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114N) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_A) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_B) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL_A) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118E_A_I) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132EL) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132N) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134EL_A) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134N) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP138E_A) }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL_A) }, + { } +}; +MODULE_DEVICE_TABLE(pci, mxpcie8250_pci_ids); + +static struct pci_driver mxpcie8250_pci_driver = { + .name = "8250_mxpcie", + .id_table = mxpcie8250_pci_ids, + .probe = mxpcie8250_probe, + .remove = mxpcie8250_remove, +}; +module_pci_driver(mxpcie8250_pci_driver); + +MODULE_AUTHOR("Moxa Inc."); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Moxa PCIe Multiport Serial Device Driver"); diff --git a/drivers/tty/serial/8250/8250_ni.c b/drivers/tty/serial/8250/8250_ni.c new file mode 100644 index 000000000..9f945f8ed --- /dev/null +++ b/drivers/tty/serial/8250/8250_ni.c @@ -0,0 +1,449 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NI 16550 UART Driver + * + * The National Instruments (NI) 16550 is a UART that is compatible with the + * TL16C550C and OX16C950B register interfaces, but has additional functions + * for RS-485 transceiver control. This driver implements support for the + * additional functionality on top of the standard serial8250 core. + * + * Copyright 2012-2023 National Instruments Corporation + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/serial_core.h> +#include <linux/types.h> + +#include "8250.h" + +/* Extra bits in UART_ACR */ +#define NI16550_ACR_AUTO_DTR_EN BIT(4) + +/* TFS - TX FIFO Size */ +#define NI16550_TFS_OFFSET 0x0C +/* RFS - RX FIFO Size */ +#define NI16550_RFS_OFFSET 0x0D + +/* PMR - Port Mode Register */ +#define NI16550_PMR_OFFSET 0x0E +/* PMR[1:0] - Port Capabilities */ +#define NI16550_PMR_CAP_MASK GENMASK(1, 0) +#define NI16550_PMR_NOT_IMPL FIELD_PREP(NI16550_PMR_CAP_MASK, 0) /* not implemented */ +#define NI16550_PMR_CAP_RS232 FIELD_PREP(NI16550_PMR_CAP_MASK, 1) /* RS-232 capable */ +#define NI16550_PMR_CAP_RS485 FIELD_PREP(NI16550_PMR_CAP_MASK, 2) /* RS-485 capable */ +#define NI16550_PMR_CAP_DUAL FIELD_PREP(NI16550_PMR_CAP_MASK, 3) /* dual-port */ +/* PMR[4] - Interface Mode */ +#define NI16550_PMR_MODE_MASK GENMASK(4, 4) +#define NI16550_PMR_MODE_RS232 FIELD_PREP(NI16550_PMR_MODE_MASK, 0) /* currently 232 */ +#define NI16550_PMR_MODE_RS485 FIELD_PREP(NI16550_PMR_MODE_MASK, 1) /* currently 485 */ + +/* PCR - Port Control Register */ +/* + * Wire Mode | Tx enabled? | Rx enabled? + * ---------------|----------------------|-------------------------- + * PCR_RS422 | Always | Always + * PCR_ECHO_RS485 | When DTR asserted | Always + * PCR_DTR_RS485 | When DTR asserted | Disabled when TX enabled + * PCR_AUTO_RS485 | When data in TX FIFO | Disabled when TX enabled + */ +#define NI16550_PCR_OFFSET 0x0F +#define NI16550_PCR_WIRE_MODE_MASK GENMASK(1, 0) +#define NI16550_PCR_RS422 FIELD_PREP(NI16550_PCR_WIRE_MODE_MASK, 0) +#define NI16550_PCR_ECHO_RS485 FIELD_PREP(NI16550_PCR_WIRE_MODE_MASK, 1) +#define NI16550_PCR_DTR_RS485 FIELD_PREP(NI16550_PCR_WIRE_MODE_MASK, 2) +#define NI16550_PCR_AUTO_RS485 FIELD_PREP(NI16550_PCR_WIRE_MODE_MASK, 3) +#define NI16550_PCR_TXVR_ENABLE_BIT BIT(3) +#define NI16550_PCR_RS485_TERMINATION_BIT BIT(6) + +/* flags for ni16550_device_info */ +#define NI_HAS_PMR BIT(0) + +struct ni16550_device_info { + u32 uartclk; + u8 prescaler; + u8 flags; +}; + +struct ni16550_data { + int line; + struct clk *clk; +}; + +static int ni16550_enable_transceivers(struct uart_port *port) +{ + u8 pcr; + + pcr = port->serial_in(port, NI16550_PCR_OFFSET); + pcr |= NI16550_PCR_TXVR_ENABLE_BIT; + dev_dbg(port->dev, "enable transceivers: write pcr: 0x%02x\n", pcr); + port->serial_out(port, NI16550_PCR_OFFSET, pcr); + + return 0; +} + +static int ni16550_disable_transceivers(struct uart_port *port) +{ + u8 pcr; + + pcr = serial_port_in(port, NI16550_PCR_OFFSET); + pcr &= ~NI16550_PCR_TXVR_ENABLE_BIT; + dev_dbg(port->dev, "disable transceivers: write pcr: 0x%02x\n", pcr); + serial_port_out(port, NI16550_PCR_OFFSET, pcr); + + return 0; +} + +static int ni16550_rs485_config(struct uart_port *port, + struct ktermios *termios, + struct serial_rs485 *rs485) +{ + struct uart_8250_port *up = container_of(port, struct uart_8250_port, port); + u8 pcr; + + pcr = serial_port_in(port, NI16550_PCR_OFFSET); + pcr &= ~NI16550_PCR_WIRE_MODE_MASK; + + if ((rs485->flags & SER_RS485_MODE_RS422) || + !(rs485->flags & SER_RS485_ENABLED)) { + /* RS-422 */ + pcr |= NI16550_PCR_RS422; + up->acr &= ~NI16550_ACR_AUTO_DTR_EN; + } else { + /* RS-485 2-wire Auto */ + pcr |= NI16550_PCR_AUTO_RS485; + up->acr |= NI16550_ACR_AUTO_DTR_EN; + } + + dev_dbg(port->dev, "config rs485: write pcr: 0x%02x, acr: %02x\n", pcr, up->acr); + serial_port_out(port, NI16550_PCR_OFFSET, pcr); + serial_icr_write(up, UART_ACR, up->acr); + + return 0; +} + +static bool is_pmr_rs232_mode(struct uart_8250_port *up) +{ + u8 pmr = serial_in(up, NI16550_PMR_OFFSET); + u8 pmr_mode = pmr & NI16550_PMR_MODE_MASK; + u8 pmr_cap = pmr & NI16550_PMR_CAP_MASK; + + /* + * If the PMR is not implemented, then by default NI UARTs are + * connected to RS-485 transceivers + */ + if (pmr_cap == NI16550_PMR_NOT_IMPL) + return false; + + if (pmr_cap == NI16550_PMR_CAP_DUAL) + /* + * If the port is dual-mode capable, then read the mode bit + * to know the current mode + */ + return pmr_mode == NI16550_PMR_MODE_RS232; + /* + * If it is not dual-mode capable, then decide based on the + * capability + */ + return pmr_cap == NI16550_PMR_CAP_RS232; +} + +static void ni16550_config_prescaler(struct uart_8250_port *up, + u8 prescaler) +{ + /* + * Page in the Enhanced Mode Registers + * Sets EFR[4] for Enhanced Mode. + */ + u8 lcr_value; + u8 efr_value; + + lcr_value = serial_in(up, UART_LCR); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + efr_value = serial_in(up, UART_EFR); + efr_value |= UART_EFR_ECB; + + serial_out(up, UART_EFR, efr_value); + + /* Page out the Enhanced Mode Registers */ + serial_out(up, UART_LCR, lcr_value); + + /* Set prescaler to CPR register. */ + serial_out(up, UART_SCR, UART_CPR); + serial_out(up, UART_ICR, prescaler); +} + +static const struct serial_rs485 ni16550_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_MODE_RS422 | SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND, + /* + * delay_rts_* and RX_DURING_TX are not supported. + * + * RTS_{ON,AFTER}_SEND are supported, but ignored; the transceiver + * is connected in only one way and we don't need userspace to tell + * us, but want to retain compatibility with applications that do. + */ +}; + +static void ni16550_rs485_setup(struct uart_port *port) +{ + port->rs485_config = ni16550_rs485_config; + port->rs485_supported = ni16550_rs485_supported; + /* + * The hardware comes up by default in 2-wire auto mode and we + * set the flags to represent that + */ + port->rs485.flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND; +} + +static int ni16550_port_startup(struct uart_port *port) +{ + int ret; + + ret = serial8250_do_startup(port); + if (ret) + return ret; + + return ni16550_enable_transceivers(port); +} + +static void ni16550_port_shutdown(struct uart_port *port) +{ + ni16550_disable_transceivers(port); + + serial8250_do_shutdown(port); +} + +static int ni16550_get_regs(struct platform_device *pdev, + struct uart_port *port) +{ + struct resource *regs; + + regs = platform_get_mem_or_io(pdev, 0); + if (!regs) + return dev_err_probe(&pdev->dev, -EINVAL, "no registers defined\n"); + + switch (resource_type(regs)) { + case IORESOURCE_IO: + port->iotype = UPIO_PORT; + port->iobase = regs->start; + + return 0; + case IORESOURCE_MEM: + port->iotype = UPIO_MEM; + port->mapbase = regs->start; + port->mapsize = resource_size(regs); + port->flags |= UPF_IOREMAP; + + return 0; + default: + return -EINVAL; + } +} + +/* + * Very old implementations don't have the TFS or RFS registers + * defined, so we may read all-0s or all-1s. For such devices, + * assume a FIFO size of 128. + */ +static u8 ni16550_read_fifo_size(struct uart_8250_port *uart, int reg) +{ + u8 value = serial_in(uart, reg); + + if (value == 0x00 || value == 0xFF) + return 128; + + return value; +} + +static void ni16550_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + up->mcr |= UART_MCR_CLKSEL; + serial8250_do_set_mctrl(port, mctrl); +} + +static int ni16550_probe(struct platform_device *pdev) +{ + struct uart_8250_port *uart __free(kfree) = NULL; + const struct ni16550_device_info *info; + struct device *dev = &pdev->dev; + unsigned int txfifosz, rxfifosz; + struct ni16550_data *data; + unsigned int prescaler; + const char *portmode; + bool rs232_property; + int ret; + + uart = kzalloc_obj(*uart); + if (!uart) + return -ENOMEM; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + spin_lock_init(&uart->port.lock); + + ret = ni16550_get_regs(pdev, &uart->port); + if (ret < 0) + return ret; + + /* early setup so that serial_in()/serial_out() work */ + serial8250_set_defaults(uart); + + info = device_get_match_data(dev); + + uart->port.dev = dev; + uart->port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_FIXED_TYPE; + uart->port.startup = ni16550_port_startup; + uart->port.shutdown = ni16550_port_shutdown; + + /* + * Hardware instantiation of FIFO sizes are held in registers. + */ + txfifosz = ni16550_read_fifo_size(uart, NI16550_TFS_OFFSET); + rxfifosz = ni16550_read_fifo_size(uart, NI16550_RFS_OFFSET); + + dev_dbg(dev, "NI 16550 has TX FIFO size %u, RX FIFO size %u\n", + txfifosz, rxfifosz); + + uart->port.type = PORT_16550A; + uart->port.fifosize = txfifosz; + uart->tx_loadsz = txfifosz; + uart->fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10; + uart->capabilities = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR; + + /* + * Declaration of the base clock frequency can come from one of: + * - static declaration in this driver (for older ACPI IDs) + * - a "clock-frequency" ACPI + */ + uart->port.uartclk = info->uartclk; + + ret = uart_read_port_properties(&uart->port); + if (ret) + return ret; + + if (!uart->port.uartclk) { + data->clk = devm_clk_get_enabled(dev, NULL); + if (!IS_ERR(data->clk)) + uart->port.uartclk = clk_get_rate(data->clk); + } + + if (!uart->port.uartclk) + return dev_err_probe(dev, -ENODEV, "unable to determine clock frequency!\n"); + + prescaler = info->prescaler; + device_property_read_u32(dev, "clock-prescaler", &prescaler); + if (prescaler) { + uart->port.set_mctrl = ni16550_set_mctrl; + ni16550_config_prescaler(uart, (u8)prescaler); + } + + /* + * The determination of whether or not this is an RS-485 or RS-232 port + * can come from the PMR (if present), otherwise we're solely an RS-485 + * port. + * + * This is a device-specific property, and there are old devices in the + * field using "transceiver" as an ACPI property, so we have to check + * for that as well. + */ + if (!device_property_read_string(dev, "transceiver", &portmode)) { + rs232_property = strncmp(portmode, "RS-232", 6) == 0; + + dev_dbg(dev, "port is in %s mode (via device property)\n", + rs232_property ? "RS-232" : "RS-485"); + } else if (info->flags & NI_HAS_PMR) { + rs232_property = is_pmr_rs232_mode(uart); + + dev_dbg(dev, "port is in %s mode (via PMR)\n", + rs232_property ? "RS-232" : "RS-485"); + } else { + rs232_property = 0; + + dev_dbg(dev, "port is fixed as RS-485\n"); + } + + if (!rs232_property) { + /* + * Neither the 'transceiver' property nor the PMR indicate + * that this is an RS-232 port, so it must be an RS-485 one. + */ + ni16550_rs485_setup(&uart->port); + } + + ret = serial8250_register_8250_port(uart); + if (ret < 0) + return ret; + data->line = ret; + + platform_set_drvdata(pdev, data); + return 0; +} + +static void ni16550_remove(struct platform_device *pdev) +{ + struct ni16550_data *data = platform_get_drvdata(pdev); + + serial8250_unregister_port(data->line); +} + +/* NI 16550 RS-485 Interface */ +static const struct ni16550_device_info nic7750 = { + .uartclk = 33333333, +}; + +/* NI CVS-145x RS-485 Interface */ +static const struct ni16550_device_info nic7772 = { + .uartclk = 1843200, + .flags = NI_HAS_PMR, +}; + +/* NI cRIO-904x RS-485 Interface */ +static const struct ni16550_device_info nic792b = { + /* Sets UART clock rate to 22.222 MHz with 1.125 prescale */ + .uartclk = 22222222, + .prescaler = 0x09, +}; + +/* NI sbRIO 96x8 RS-232/485 Interfaces */ +static const struct ni16550_device_info nic7a69 = { + /* Set UART clock rate to 29.629 MHz with 1.125 prescale */ + .uartclk = 29629629, + .prescaler = 0x09, +}; + +static const struct acpi_device_id ni16550_acpi_match[] = { + { "NIC7750", (kernel_ulong_t)&nic7750 }, + { "NIC7772", (kernel_ulong_t)&nic7772 }, + { "NIC792B", (kernel_ulong_t)&nic792b }, + { "NIC7A69", (kernel_ulong_t)&nic7a69 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, ni16550_acpi_match); + +static struct platform_driver ni16550_driver = { + .driver = { + .name = "ni16550", + .acpi_match_table = ni16550_acpi_match, + }, + .probe = ni16550_probe, + .remove = ni16550_remove, +}; + +module_platform_driver(ni16550_driver); + +MODULE_AUTHOR("Emerson Electric Co."); +MODULE_DESCRIPTION("NI 16550 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c new file mode 100644 index 000000000..b1561c499 --- /dev/null +++ b/drivers/tty/serial/8250/8250_of.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Serial Port driver for Open Firmware platform devices + * + * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp. + */ + +#include <linux/align.h> +#include <linux/bits.h> +#include <linux/console.h> +#include <linux/math.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/pm_runtime.h> +#include <linux/clk.h> +#include <linux/reset.h> +#include <linux/notifier.h> + +#include "8250.h" + +struct of_serial_info { + struct clk *clk; + struct clk *bus_clk; + struct reset_control *rst; + int type; + int line; + struct notifier_block clk_notifier; +}; + +/* Nuvoton NPCM timeout register */ +#define UART_NPCM_TOR 7 +#define UART_NPCM_TOIE BIT(7) /* Timeout Interrupt Enable */ + +static int npcm_startup(struct uart_port *port) +{ + /* + * Nuvoton calls the scratch register 'UART_TOR' (timeout + * register). Enable it, and set TIOC (timeout interrupt + * comparator) to be 0x20 for correct operation. + */ + serial_port_out(port, UART_NPCM_TOR, UART_NPCM_TOIE | 0x20); + + return serial8250_do_startup(port); +} + +/* Nuvoton NPCM UARTs have a custom divisor calculation */ +static unsigned int npcm_get_divisor(struct uart_port *port, unsigned int baud, + unsigned int *frac) +{ + return DIV_ROUND_CLOSEST(port->uartclk, 16 * baud + 2) - 2; +} + +static int npcm_setup(struct uart_port *port) +{ + port->get_divisor = npcm_get_divisor; + port->startup = npcm_startup; + return 0; +} + +static inline struct of_serial_info *clk_nb_to_info(struct notifier_block *nb) +{ + return container_of(nb, struct of_serial_info, clk_notifier); +} + +static int of_platform_serial_clk_notifier_cb(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct of_serial_info *info = clk_nb_to_info(nb); + struct uart_8250_port *port8250 = serial8250_get_port(info->line); + struct clk_notifier_data *ndata = data; + + if (event == POST_RATE_CHANGE) { + serial8250_update_uartclk(&port8250->port, ndata->new_rate); + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + +static int lpc32xx_handle_irq(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int iir; + u16 status; + + guard(serial8250_rpm)(up); + + iir = serial_port_in(port, UART_IIR); + if (iir & UART_IIR_NO_INT) + return 0; + + guard(uart_port_lock_check_sysrq_irqsave)(port); + + /* + * The LPC32xx UART can assert an RX character-timeout interrupt while + * the RX FIFO is empty: IIR reports UART_IIR_RX_TIMEOUT but LSR.DR is + * clear. The timeout is only cleared by reading RHR, but the core RX + * path skips that read when the FIFO is empty, so the level-triggered + * IRQ re-fires forever and livelocks this single-core SoC. Do one + * throwaway RHR read to clear it; a healthy UART never reports a + * timeout with DR/BI clear, so no received data is ever discarded. + */ + if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) { + status = serial_lsr_in(up); + if (!(status & (UART_LSR_DR | UART_LSR_BI))) + serial_port_in(port, UART_RX); + } + + serial8250_handle_irq_locked(port, iir); + + return 1; +} + +/* + * Fill a struct uart_port for a given device node + */ +static int of_platform_serial_setup(struct platform_device *ofdev, + int type, struct uart_8250_port *up, + struct of_serial_info *info) +{ + struct resource resource; + struct device *dev = &ofdev->dev; + struct device_node *np = dev->of_node; + struct uart_port *port = &up->port; + u32 spd; + int ret; + + memset(port, 0, sizeof(*port)); + + pm_runtime_enable(&ofdev->dev); + pm_runtime_get_sync(&ofdev->dev); + + ret = of_address_to_resource(np, 0, &resource); + if (ret) { + dev_err_probe(dev, ret, "invalid address\n"); + goto err_pmruntime; + } + + port->dev = &ofdev->dev; + port->flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_FIXED_TYPE; + spin_lock_init(&port->lock); + + if (resource_type(&resource) == IORESOURCE_IO) { + port->iobase = resource.start; + } else { + port->mapbase = resource.start; + port->mapsize = resource_size(&resource); + port->flags |= UPF_IOREMAP; + } + + ret = uart_read_and_validate_port_properties(port); + if (ret) + goto err_pmruntime; + + if (IS_ENABLED(CONFIG_CPU_XSCALE) && type == PORT_XSCALE) { + /* + * Adjust for BE32 register accesses: drop any hardcoded + * address for the big endian byte target, add it explicitly + * if running on BE32. + */ + port->mapbase = PTR_ALIGN_DOWN(port->mapbase, 4); + if (IS_ENABLED(CONFIG_CPU_ENDIAN_BE32)) + port->mapbase += 3; + } + + /* Get clk rate through clk driver if present */ + if (!port->uartclk) { + struct clk *bus_clk; + + bus_clk = devm_clk_get_optional_enabled(dev, "bus"); + if (IS_ERR(bus_clk)) { + ret = dev_err_probe(dev, PTR_ERR(bus_clk), "failed to get bus clock\n"); + goto err_pmruntime; + } + + /* If the bus clock is required, core clock must be named */ + info->clk = devm_clk_get_enabled(dev, bus_clk ? "core" : NULL); + if (IS_ERR(info->clk)) { + ret = dev_err_probe(dev, PTR_ERR(info->clk), "failed to get clock\n"); + goto err_pmruntime; + } + + info->bus_clk = bus_clk; + port->uartclk = clk_get_rate(info->clk); + } + /* If current-speed was set, then try not to change it. */ + if (of_property_read_u32(np, "current-speed", &spd) == 0) + port->custom_divisor = port->uartclk / (16 * spd); + + /* Compatibility with the deprecated pxa driver and 8250_pxa drivers. */ + if (of_device_is_compatible(np, "mrvl,mmp-uart")) + port->regshift = 2; + + info->rst = devm_reset_control_get_optional_shared(&ofdev->dev, NULL); + if (IS_ERR(info->rst)) { + ret = PTR_ERR(info->rst); + goto err_pmruntime; + } + + ret = reset_control_deassert(info->rst); + if (ret) + goto err_pmruntime; + + port->type = type; + port->rs485_config = serial8250_em485_config; + port->rs485_supported = serial8250_em485_supported; + up->rs485_start_tx = serial8250_em485_start_tx; + up->rs485_stop_tx = serial8250_em485_stop_tx; + + switch (type) { + case PORT_RT2880: + ret = rt288x_setup(port); + break; + case PORT_NPCM: + ret = npcm_setup(port); + break; + case PORT_LPC3220: + port->handle_irq = lpc32xx_handle_irq; + break; + default: + /* Nothing to do */ + ret = 0; + break; + } + if (ret) + goto err_pmruntime; + + if (IS_REACHABLE(CONFIG_SERIAL_8250_FSL) && + (of_device_is_compatible(np, "fsl,ns16550") || + of_device_is_compatible(np, "fsl,16550-FIFO64"))) { + port->handle_irq = fsl8250_handle_irq; + port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE); + } + + return 0; +err_pmruntime: + pm_runtime_put_sync(&ofdev->dev); + pm_runtime_disable(&ofdev->dev); + return ret; +} + +/* + * Try to register a serial port + */ +static int of_platform_serial_probe(struct platform_device *ofdev) +{ + struct of_serial_info *info; + struct uart_8250_port port8250; + unsigned int port_type; + u32 tx_threshold; + int ret; + + if (IS_ENABLED(CONFIG_SERIAL_8250_BCM7271) && + of_device_is_compatible(ofdev->dev.of_node, "brcm,bcm7271-uart")) + return -ENODEV; + + port_type = (unsigned long)of_device_get_match_data(&ofdev->dev); + if (port_type == PORT_UNKNOWN) + return -EINVAL; + + if (of_property_read_bool(ofdev->dev.of_node, "used-by-rtas")) + return -EBUSY; + + info = kzalloc_obj(*info); + if (info == NULL) + return -ENOMEM; + + memset(&port8250, 0, sizeof(port8250)); + ret = of_platform_serial_setup(ofdev, port_type, &port8250, info); + if (ret) + goto err_free; + + if (port8250.port.fifosize) + port8250.capabilities = UART_CAP_FIFO; + + /* Check for TX FIFO threshold & set tx_loadsz */ + if ((of_property_read_u32(ofdev->dev.of_node, "tx-threshold", + &tx_threshold) == 0) && + (tx_threshold < port8250.port.fifosize)) + port8250.tx_loadsz = port8250.port.fifosize - tx_threshold; + + if (of_property_read_bool(ofdev->dev.of_node, "auto-flow-control")) + port8250.capabilities |= UART_CAP_AFE; + + if (of_property_read_u32(ofdev->dev.of_node, + "overrun-throttle-ms", + &port8250.overrun_backoff_time_ms) != 0) + port8250.overrun_backoff_time_ms = 0; + + ret = serial8250_register_8250_port(&port8250); + if (ret < 0) + goto err_dispose; + + info->type = port_type; + info->line = ret; + platform_set_drvdata(ofdev, info); + + if (info->clk) { + info->clk_notifier.notifier_call = of_platform_serial_clk_notifier_cb; + ret = clk_notifier_register(info->clk, &info->clk_notifier); + if (ret) { + dev_err_probe(port8250.port.dev, ret, "Failed to set the clock notifier\n"); + goto err_unregister; + } + } + + return 0; +err_unregister: + serial8250_unregister_port(info->line); +err_dispose: + pm_runtime_put_sync(&ofdev->dev); + pm_runtime_disable(&ofdev->dev); +err_free: + kfree(info); + return ret; +} + +/* + * Release a line + */ +static void of_platform_serial_remove(struct platform_device *ofdev) +{ + struct of_serial_info *info = platform_get_drvdata(ofdev); + + if (info->clk) + clk_notifier_unregister(info->clk, &info->clk_notifier); + + serial8250_unregister_port(info->line); + + reset_control_assert(info->rst); + pm_runtime_put_sync(&ofdev->dev); + pm_runtime_disable(&ofdev->dev); + kfree(info); +} + +#ifdef CONFIG_PM_SLEEP +static int of_serial_suspend(struct device *dev) +{ + struct of_serial_info *info = dev_get_drvdata(dev); + struct uart_8250_port *port8250 = serial8250_get_port(info->line); + struct uart_port *port = &port8250->port; + + serial8250_suspend_port(info->line); + + if (!uart_console(port) || console_suspend_enabled) { + pm_runtime_put_sync(dev); + clk_disable_unprepare(info->clk); + clk_disable_unprepare(info->bus_clk); + } + return 0; +} + +static int of_serial_resume(struct device *dev) +{ + struct of_serial_info *info = dev_get_drvdata(dev); + struct uart_8250_port *port8250 = serial8250_get_port(info->line); + struct uart_port *port = &port8250->port; + + if (!uart_console(port) || console_suspend_enabled) { + pm_runtime_get_sync(dev); + clk_prepare_enable(info->bus_clk); + clk_prepare_enable(info->clk); + } + + serial8250_resume_port(info->line); + + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(of_serial_pm_ops, of_serial_suspend, of_serial_resume); + +/* + * A few common types, add more as needed. + */ +static const struct of_device_id of_platform_serial_table[] = { + { .compatible = "ns8250", .data = (void *)PORT_8250, }, + { .compatible = "ns16450", .data = (void *)PORT_16450, }, + { .compatible = "ns16550a", .data = (void *)PORT_16550A, }, + { .compatible = "ns16550", .data = (void *)PORT_16550, }, + { .compatible = "ns16750", .data = (void *)PORT_16750, }, + { .compatible = "ns16850", .data = (void *)PORT_16850, }, + { .compatible = "nxp,lpc3220-uart", .data = (void *)PORT_LPC3220, }, + { .compatible = "ralink,rt2880-uart", .data = (void *)PORT_RT2880, }, + { .compatible = "intel,xscale-uart", .data = (void *)PORT_XSCALE, }, + { .compatible = "altr,16550-FIFO32", + .data = (void *)PORT_ALTR_16550_F32, }, + { .compatible = "altr,16550-FIFO64", + .data = (void *)PORT_ALTR_16550_F64, }, + { .compatible = "altr,16550-FIFO128", + .data = (void *)PORT_ALTR_16550_F128, }, + { .compatible = "fsl,16550-FIFO64", + .data = (void *)PORT_16550A_FSL64, }, + { .compatible = "mediatek,mtk-btif", + .data = (void *)PORT_MTK_BTIF, }, + { .compatible = "mrvl,mmp-uart", + .data = (void *)PORT_XSCALE, }, + { .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, }, + { .compatible = "nuvoton,wpcm450-uart", .data = (void *)PORT_NPCM, }, + { .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, of_platform_serial_table); + +static struct platform_driver of_platform_serial_driver = { + .driver = { + .name = "of_serial", + .of_match_table = of_platform_serial_table, + .pm = &of_serial_pm_ops, + }, + .probe = of_platform_serial_probe, + .remove = of_platform_serial_remove, +}; + +module_platform_driver(of_platform_serial_driver); + +MODULE_IMPORT_NS("SERIAL_8250"); +MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Serial Port driver for Open Firmware platform devices"); diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c new file mode 100644 index 000000000..3c7775df2 --- /dev/null +++ b/drivers/tty/serial/8250/8250_omap.c @@ -0,0 +1,1894 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 8250-core based driver for the OMAP internal UART + * + * based on omap-serial.c, Copyright (C) 2010 Texas Instruments. + * + * Copyright (C) 2014 Sebastian Andrzej Siewior + * + */ + +#include <linux/atomic.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/serial_8250.h> +#include <linux/serial_reg.h> +#include <linux/tty_flip.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/delay.h> +#include <linux/pm_runtime.h> +#include <linux/console.h> +#include <linux/pm_qos.h> +#include <linux/pm_wakeirq.h> +#include <linux/dma-mapping.h> +#include <linux/sys_soc.h> +#include <linux/reboot.h> +#include <linux/pinctrl/consumer.h> + +#include "8250.h" + +#define DEFAULT_CLK_SPEED 48000000 +#define OMAP_UART_REGSHIFT 2 + +#define UART_ERRATA_i202_MDR1_ACCESS (1 << 0) +#define OMAP_UART_WER_HAS_TX_WAKEUP (1 << 1) +#define OMAP_DMA_TX_KICK (1 << 2) +/* + * See Advisory 21 in AM437x errata SPRZ408B, updated April 2015. + * The same errata is applicable to AM335x and DRA7x processors too. + */ +#define UART_ERRATA_CLOCK_DISABLE (1 << 3) +#define UART_HAS_EFR2 BIT(4) +#define UART_HAS_RHR_IT_DIS BIT(5) +#define UART_RX_TIMEOUT_QUIRK BIT(6) +#define UART_HAS_NATIVE_RS485 BIT(7) + +#define OMAP_UART_FCR_RX_TRIG 6 +#define OMAP_UART_FCR_TX_TRIG 4 + +/* SCR register bitmasks */ +#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7) +#define OMAP_UART_SCR_TX_TRIG_GRANU1_MASK (1 << 6) +#define OMAP_UART_SCR_TX_EMPTY (1 << 3) +#define OMAP_UART_SCR_DMAMODE_MASK (3 << 1) +#define OMAP_UART_SCR_DMAMODE_1 (1 << 1) +#define OMAP_UART_SCR_DMAMODE_CTL (1 << 0) + +/* MVR register bitmasks */ +#define OMAP_UART_MVR_SCHEME_SHIFT 30 +#define OMAP_UART_LEGACY_MVR_MAJ_MASK 0xf0 +#define OMAP_UART_LEGACY_MVR_MAJ_SHIFT 4 +#define OMAP_UART_LEGACY_MVR_MIN_MASK 0x0f +#define OMAP_UART_MVR_MAJ_MASK 0x700 +#define OMAP_UART_MVR_MAJ_SHIFT 8 +#define OMAP_UART_MVR_MIN_MASK 0x3f + +/* SYSC register bitmasks */ +#define OMAP_UART_SYSC_SOFTRESET (1 << 1) + +/* SYSS register bitmasks */ +#define OMAP_UART_SYSS_RESETDONE (1 << 0) + +#define UART_TI752_TLR_TX 0 +#define UART_TI752_TLR_RX 4 + +#define TRIGGER_TLR_MASK(x) ((x & 0x3c) >> 2) +#define TRIGGER_FCR_MASK(x) (x & 3) + +/* Enable XON/XOFF flow control on output */ +#define OMAP_UART_SW_TX 0x08 +/* Enable XON/XOFF flow control on input */ +#define OMAP_UART_SW_RX 0x02 + +#define OMAP_UART_WER_MOD_WKUP 0x7f +#define OMAP_UART_TX_WAKEUP_EN (1 << 7) + +#define TX_TRIGGER 1 +#define RX_TRIGGER 48 + +#define OMAP_UART_TCR_RESTORE(x) ((x / 4) << 4) +#define OMAP_UART_TCR_HALT(x) ((x / 4) << 0) + +#define UART_BUILD_REVISION(x, y) (((x) << 8) | (y)) + +#define OMAP_UART_REV_46 0x0406 +#define OMAP_UART_REV_52 0x0502 +#define OMAP_UART_REV_63 0x0603 + +/* Resume register */ +#define UART_OMAP_RESUME 0x0B + +/* Interrupt Enable Register 2 */ +#define UART_OMAP_IER2 0x1B +#define UART_OMAP_IER2_RHR_IT_DIS BIT(2) + +/* Mode Definition Register 3 */ +#define UART_OMAP_MDR3 0x20 +#define UART_OMAP_MDR3_DIR_POL BIT(3) +#define UART_OMAP_MDR3_DIR_EN BIT(4) + +/* Enhanced features register 2 */ +#define UART_OMAP_EFR2 0x23 +#define UART_OMAP_EFR2_TIMEOUT_BEHAVE BIT(6) + +/* RX FIFO occupancy indicator */ +#define UART_OMAP_RX_LVL 0x19 + +/* Timeout low and High */ +#define UART_OMAP_TO_L 0x26 +#define UART_OMAP_TO_H 0x27 +struct omap8250_priv { + void __iomem *membase; + int line; + u8 habit; + u8 mdr1; + u8 mdr3; + u8 efr; + u8 scr; + u8 wer; + u8 xon; + u8 xoff; + u8 delayed_restore; + u16 quot; + + u8 tx_trigger; + u8 rx_trigger; + atomic_t active; + bool is_suspending; + int wakeirq; + u32 latency; + u32 calc_latency; + struct pm_qos_request pm_qos_request; + struct work_struct qos_work; + struct uart_8250_dma omap8250_dma; + spinlock_t rx_dma_lock; + bool rx_dma_broken; + bool throttled; + + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_wakeup; +}; + +struct omap8250_dma_params { + u32 rx_size; + u8 rx_trigger; + u8 tx_trigger; +}; + +struct omap8250_platdata { + struct omap8250_dma_params *dma_params; + u8 habit; +}; + +#ifdef CONFIG_SERIAL_8250_DMA +static void omap_8250_rx_dma_flush(struct uart_8250_port *p); +#else +static inline void omap_8250_rx_dma_flush(struct uart_8250_port *p) { } +#endif + +static u32 uart_read(struct omap8250_priv *priv, u32 reg) +{ + return readl(priv->membase + (reg << OMAP_UART_REGSHIFT)); +} + +/* + * Called on runtime PM resume path from omap8250_restore_regs(), and + * omap8250_set_mctrl(). + */ +static void __omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct omap8250_priv *priv = port->private_data; + u8 lcr; + + serial8250_do_set_mctrl(port, mctrl); + + if (!mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS)) { + /* + * Turn off autoRTS if RTS is lowered and restore autoRTS + * setting if RTS is raised + */ + lcr = serial_in(up, UART_LCR); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS)) + priv->efr |= UART_EFR_RTS; + else + priv->efr &= ~UART_EFR_RTS; + serial_out(up, UART_EFR, priv->efr); + serial_out(up, UART_LCR, lcr); + } +} + +static void omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + int err; + + err = pm_runtime_resume_and_get(port->dev); + if (err) + return; + + __omap8250_set_mctrl(port, mctrl); + + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_autosuspend(port->dev); +} + +/* + * Work Around for Errata i202 (2430, 3430, 3630, 4430 and 4460) + * The access to uart register after MDR1 Access + * causes UART to corrupt data. + * + * Need a delay = + * 5 L4 clock cycles + 5 UART functional clock cycle (@48MHz = ~0.2uS) + * give 10 times as much + */ +static void omap_8250_mdr1_errataset(struct uart_8250_port *up, + struct omap8250_priv *priv) +{ + serial_out(up, UART_OMAP_MDR1, priv->mdr1); + udelay(2); + serial_out(up, UART_FCR, up->fcr | UART_FCR_CLEAR_XMIT | + UART_FCR_CLEAR_RCVR); +} + +static void omap_8250_get_divisor(struct uart_port *port, unsigned int baud, + struct omap8250_priv *priv) +{ + unsigned int uartclk = port->uartclk; + unsigned int div_13, div_16; + unsigned int abs_d13, abs_d16; + + div_13 = DIV_ROUND_CLOSEST(uartclk, 13 * baud); + div_16 = DIV_ROUND_CLOSEST(uartclk, 16 * baud); + + if (!div_13) + div_13 = 1; + if (!div_16) + div_16 = 1; + + abs_d13 = abs(baud - uartclk / 13 / div_13); + abs_d16 = abs(baud - uartclk / 16 / div_16); + + if (abs_d13 >= abs_d16) { + priv->mdr1 = UART_OMAP_MDR1_16X_MODE; + priv->quot = div_16; + } else { + priv->mdr1 = UART_OMAP_MDR1_13X_MODE; + priv->quot = div_13; + } +} + +static void omap8250_update_scr(struct uart_8250_port *up, + struct omap8250_priv *priv) +{ + u8 old_scr; + + old_scr = serial_in(up, UART_OMAP_SCR); + if (old_scr == priv->scr) + return; + + /* + * The manual recommends not to enable the DMA mode selector in the SCR + * (instead of the FCR) register _and_ selecting the DMA mode as one + * register write because this may lead to malfunction. + */ + if (priv->scr & OMAP_UART_SCR_DMAMODE_MASK) + serial_out(up, UART_OMAP_SCR, + priv->scr & ~OMAP_UART_SCR_DMAMODE_MASK); + serial_out(up, UART_OMAP_SCR, priv->scr); +} + +static void omap8250_update_mdr1(struct uart_8250_port *up, + struct omap8250_priv *priv) +{ + if (priv->habit & UART_ERRATA_i202_MDR1_ACCESS) + omap_8250_mdr1_errataset(up, priv); + else + serial_out(up, UART_OMAP_MDR1, priv->mdr1); +} + +static void omap8250_restore_regs(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + struct omap8250_priv *priv = port->private_data; + struct uart_8250_dma *dma = up->dma; + u8 mcr = serial8250_in_MCR(up); + + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&port->lock); + + if (dma && dma->tx_running) { + /* + * TCSANOW requests the change to occur immediately however if + * we have a TX-DMA operation in progress then it has been + * observed that it might stall and never complete. Therefore we + * delay DMA completes to prevent this hang from happen. + */ + priv->delayed_restore = 1; + return; + } + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, UART_EFR_ECB); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial8250_out_MCR(up, mcr | UART_MCR_TCRTLR); + serial_out(up, UART_FCR, up->fcr); + + omap8250_update_scr(up, priv); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_RESTORE(16) | + OMAP_UART_TCR_HALT(52)); + serial_out(up, UART_TI752_TLR, + TRIGGER_TLR_MASK(priv->tx_trigger) << UART_TI752_TLR_TX | + TRIGGER_TLR_MASK(priv->rx_trigger) << UART_TI752_TLR_RX); + + serial_out(up, UART_LCR, 0); + + /* drop TCR + TLR access, we setup XON/XOFF later */ + serial8250_out_MCR(up, mcr); + + serial_out(up, UART_IER, up->ier); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_dl_write(up, priv->quot); + + serial_out(up, UART_EFR, priv->efr); + + /* Configure flow control */ + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_XON1, priv->xon); + serial_out(up, UART_XOFF1, priv->xoff); + + serial_out(up, UART_LCR, up->lcr); + + omap8250_update_mdr1(up, priv); + + __omap8250_set_mctrl(port, port->mctrl); + + serial_out(up, UART_OMAP_MDR3, priv->mdr3); + + if (port->rs485.flags & SER_RS485_ENABLED && + port->rs485_config == serial8250_em485_config) + serial8250_em485_stop_tx(up, true); +} + +static void omap_8250_set_termios_atomic(struct uart_port *port, struct ktermios *termios, + const struct ktermios *old, unsigned int baud) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct omap8250_priv *priv = port->private_data; + u8 cval; + + cval = UART_LCR_WLEN(tty_get_char_size(termios->c_cflag)); + + if (termios->c_cflag & CSTOPB) + cval |= UART_LCR_STOP; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; + if (termios->c_cflag & CMSPAR) + cval |= UART_LCR_SPAR; + + omap_8250_get_divisor(port, baud, priv); + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + guard(serial8250_rpm)(up); + guard(uart_port_lock_irq)(port); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* + * Specify which conditions may be considered for error + * handling and the ignoring of characters. The actual + * ignoring of characters only occurs if the bit is set + * in @ignore_status_mask as well. + */ + port->read_status_mask = UART_LSR_OE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + port->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (IGNBRK | PARMRK)) + port->read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= UART_LSR_DR; + + /* + * Modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + + up->lcr = cval; + /* Up to here it was mostly serial8250_do_set_termios() */ + + /* + * We enable TRIG_GRANU for RX and TX and additionally we set + * SCR_TX_EMPTY bit. The result is the following: + * - RX_TRIGGER amount of bytes in the FIFO will cause an interrupt. + * - less than RX_TRIGGER number of bytes will also cause an interrupt + * once the UART decides that there no new bytes arriving. + * - Once THRE is enabled, the interrupt will be fired once the FIFO is + * empty - the trigger level is ignored here. + * + * Once DMA is enabled: + * - UART will assert the TX DMA line once there is room for TX_TRIGGER + * bytes in the TX FIFO. On each assert the DMA engine will move + * TX_TRIGGER bytes into the FIFO. + * - UART will assert the RX DMA line once there are RX_TRIGGER bytes in + * the FIFO and move RX_TRIGGER bytes. + * This is because threshold and trigger values are the same. + */ + up->fcr = UART_FCR_ENABLE_FIFO; + up->fcr |= TRIGGER_FCR_MASK(priv->tx_trigger) << OMAP_UART_FCR_TX_TRIG; + up->fcr |= TRIGGER_FCR_MASK(priv->rx_trigger) << OMAP_UART_FCR_RX_TRIG; + + priv->scr = OMAP_UART_SCR_RX_TRIG_GRANU1_MASK | OMAP_UART_SCR_TX_EMPTY | + OMAP_UART_SCR_TX_TRIG_GRANU1_MASK; + + if (up->dma) + priv->scr |= OMAP_UART_SCR_DMAMODE_1 | + OMAP_UART_SCR_DMAMODE_CTL; + + priv->xon = termios->c_cc[VSTART]; + priv->xoff = termios->c_cc[VSTOP]; + + priv->efr = 0; + port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF); + + if (termios->c_cflag & CRTSCTS && port->flags & UPF_HARD_FLOW && + !mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS) && + !mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_CTS)) { + /* Enable AUTOCTS (autoRTS is enabled when RTS is raised) */ + port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS; + priv->efr |= UART_EFR_CTS; + } else if (port->flags & UPF_SOFT_FLOW) { + /* + * OMAP rx s/w flow control is borked; the transmitter remains + * stuck off even if rx flow control is subsequently disabled + */ + + /* + * IXOFF Flag: + * Enable XON/XOFF flow control on output. + * Transmit XON1, XOFF1 + */ + if (termios->c_iflag & IXOFF) { + port->status |= UPSTAT_AUTOXOFF; + priv->efr |= OMAP_UART_SW_TX; + } + } + omap8250_restore_regs(up); +} + +/* + * OMAP can use "CLK / (16 or 13) / div" for baud rate. And then we have have + * some differences in how we want to handle flow control. + */ +static void omap_8250_set_termios(struct uart_port *port, + struct ktermios *termios, + const struct ktermios *old) +{ + struct omap8250_priv *priv = port->private_data; + unsigned int baud; + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / UART_DIV_MAX, + port->uartclk / 13); + + omap_8250_set_termios_atomic(port, termios, old, baud); + + /* calculate wakeup latency constraint */ + priv->calc_latency = USEC_PER_SEC * 64 * 8 / baud; + priv->latency = priv->calc_latency; + + schedule_work(&priv->qos_work); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +/* same as 8250 except that we may have extra flow bits set in EFR */ +static void omap_8250_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_8250_port *up = up_to_u8250p(port); + u8 efr; + + guard(serial8250_rpm)(up); + /* Synchronize UART_IER access against the console. */ + guard(uart_port_lock_irq)(port); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, efr | UART_EFR_ECB); + serial_out(up, UART_LCR, 0); + + serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, efr); + serial_out(up, UART_LCR, 0); +} + +static void omap_serial_fill_features_erratas(struct uart_8250_port *up, + struct omap8250_priv *priv) +{ + static const struct soc_device_attribute k3_soc_devices[] = { + { .family = "AM65X", }, + { .family = "J721E", .revision = "SR1.0" }, + { /* sentinel */ } + }; + u32 mvr, scheme; + u16 revision, major, minor; + + mvr = uart_read(priv, UART_OMAP_MVER); + + /* Check revision register scheme */ + scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT; + + switch (scheme) { + case 0: /* Legacy Scheme: OMAP2/3 */ + /* MINOR_REV[0:4], MAJOR_REV[4:7] */ + major = (mvr & OMAP_UART_LEGACY_MVR_MAJ_MASK) >> + OMAP_UART_LEGACY_MVR_MAJ_SHIFT; + minor = (mvr & OMAP_UART_LEGACY_MVR_MIN_MASK); + break; + case 1: + /* New Scheme: OMAP4+ */ + /* MINOR_REV[0:5], MAJOR_REV[8:10] */ + major = (mvr & OMAP_UART_MVR_MAJ_MASK) >> + OMAP_UART_MVR_MAJ_SHIFT; + minor = (mvr & OMAP_UART_MVR_MIN_MASK); + break; + default: + dev_warn(up->port.dev, + "Unknown revision, defaulting to highest\n"); + /* highest possible revision */ + major = 0xff; + minor = 0xff; + } + /* normalize revision for the driver */ + revision = UART_BUILD_REVISION(major, minor); + + switch (revision) { + case OMAP_UART_REV_46: + priv->habit |= UART_ERRATA_i202_MDR1_ACCESS; + break; + case OMAP_UART_REV_52: + priv->habit |= UART_ERRATA_i202_MDR1_ACCESS | + OMAP_UART_WER_HAS_TX_WAKEUP; + break; + case OMAP_UART_REV_63: + priv->habit |= UART_ERRATA_i202_MDR1_ACCESS | + OMAP_UART_WER_HAS_TX_WAKEUP; + break; + default: + break; + } + + /* + * AM65x SR1.0, AM65x SR2.0 and J721e SR1.0 don't + * don't have RHR_IT_DIS bit in IER2 register. So drop to flag + * to enable errata workaround. + */ + if (soc_device_match(k3_soc_devices)) + priv->habit &= ~UART_HAS_RHR_IT_DIS; +} + +static void omap8250_uart_qos_work(struct work_struct *work) +{ + struct omap8250_priv *priv; + + priv = container_of(work, struct omap8250_priv, qos_work); + cpu_latency_qos_update_request(&priv->pm_qos_request, priv->latency); +} + +#ifdef CONFIG_SERIAL_8250_DMA +static int omap_8250_dma_handle_irq(struct uart_port *port); +#endif + +static irqreturn_t omap8250_irq(int irq, void *dev_id) +{ + struct omap8250_priv *priv = dev_id; + struct uart_8250_port *up = serial8250_get_port(priv->line); + struct uart_port *port = &up->port; + unsigned int iir, lsr; + int ret; + + pm_runtime_get_noresume(port->dev); + + /* Shallow idle state wake-up to an IO interrupt? */ + if (atomic_add_unless(&priv->active, 1, 1)) { + priv->latency = priv->calc_latency; + schedule_work(&priv->qos_work); + } + +#ifdef CONFIG_SERIAL_8250_DMA + if (up->dma) { + ret = omap_8250_dma_handle_irq(port); + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put(port->dev); + return IRQ_RETVAL(ret); + } +#endif + + lsr = serial_port_in(port, UART_LSR); + iir = serial_port_in(port, UART_IIR); + ret = serial8250_handle_irq(port, iir); + + /* + * On K3 SoCs, it is observed that RX TIMEOUT is signalled after + * FIFO has been drained or erroneously. + * So apply solution of Errata i2310 as mentioned in + * https://www.ti.com/lit/pdf/sprz536 + */ + if (priv->habit & UART_RX_TIMEOUT_QUIRK && + (iir & UART_IIR_RX_TIMEOUT) == UART_IIR_RX_TIMEOUT && + serial_port_in(port, UART_OMAP_RX_LVL) == 0) { + unsigned char efr2, timeout_h, timeout_l; + + efr2 = serial_in(up, UART_OMAP_EFR2); + timeout_h = serial_in(up, UART_OMAP_TO_H); + timeout_l = serial_in(up, UART_OMAP_TO_L); + serial_out(up, UART_OMAP_TO_H, 0xFF); + serial_out(up, UART_OMAP_TO_L, 0xFF); + serial_out(up, UART_OMAP_EFR2, UART_OMAP_EFR2_TIMEOUT_BEHAVE); + serial_in(up, UART_IIR); + serial_out(up, UART_OMAP_EFR2, efr2); + serial_out(up, UART_OMAP_TO_H, timeout_h); + serial_out(up, UART_OMAP_TO_L, timeout_l); + } + + /* Stop processing interrupts on input overrun */ + if ((lsr & UART_LSR_OE) && up->overrun_backoff_time_ms > 0) { + unsigned long delay; + + /* Synchronize UART_IER access against the console. */ + uart_port_lock(port); + up->ier = serial_port_in(port, UART_IER); + if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { + port->ops->stop_rx(port); + } else { + /* Keep restarting the timer until + * the input overrun subsides. + */ + cancel_delayed_work(&up->overrun_backoff); + } + uart_port_unlock(port); + + delay = msecs_to_jiffies(up->overrun_backoff_time_ms); + schedule_delayed_work(&up->overrun_backoff, delay); + } + + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put(port->dev); + + return IRQ_RETVAL(ret); +} + +static int omap_8250_startup(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct omap8250_priv *priv = port->private_data; + struct uart_8250_dma *dma = &priv->omap8250_dma; + int ret; + + if (priv->wakeirq) { + ret = dev_pm_set_dedicated_wake_irq(port->dev, priv->wakeirq); + if (ret) + return ret; + } + +#ifdef CONFIG_PM + up->capabilities |= UART_CAP_RPM; +#endif + + guard(serial8250_rpm)(up); + + serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + + serial_out(up, UART_LCR, UART_LCR_WLEN8); + + up->lsr_saved_flags = 0; + up->msr_saved_flags = 0; + + /* Disable DMA for console UART */ + if (dma->fn && !uart_console(port)) { + up->dma = &priv->omap8250_dma; + ret = serial8250_request_dma(up); + if (ret) { + dev_warn_ratelimited(port->dev, + "failed to request DMA\n"); + up->dma = NULL; + } + } else { + up->dma = NULL; + } + + /* Synchronize UART_IER access against the console. */ + scoped_guard(uart_port_lock_irq, port) { + up->ier = UART_IER_RLSI | UART_IER_RDI; + serial_out(up, UART_IER, up->ier); + } + + /* Enable module level wake up */ + priv->wer = OMAP_UART_WER_MOD_WKUP; + if (priv->habit & OMAP_UART_WER_HAS_TX_WAKEUP) + priv->wer |= OMAP_UART_TX_WAKEUP_EN; + serial_out(up, UART_OMAP_WER, priv->wer); + + if (up->dma && !(priv->habit & UART_HAS_EFR2)) { + guard(uart_port_lock_irq)(port); + up->dma->rx_dma(up); + } + + enable_irq(port->irq); + + return 0; +} + +static void omap_8250_shutdown(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct omap8250_priv *priv = port->private_data; + + guard(serial8250_rpm)(up); + + flush_work(&priv->qos_work); + if (up->dma) + omap_8250_rx_dma_flush(up); + + serial_out(up, UART_OMAP_WER, 0); + if (priv->habit & UART_HAS_EFR2) + serial_out(up, UART_OMAP_EFR2, 0x0); + + /* Synchronize UART_IER access against the console. */ + scoped_guard(uart_port_lock_irq, port) { + up->ier = 0; + serial_out(up, UART_IER, 0); + } + + disable_irq_nosync(port->irq); + dev_pm_clear_wake_irq(port->dev); + + serial8250_release_dma(up); + up->dma = NULL; + + /* + * Disable break condition and FIFOs + */ + if (up->lcr & UART_LCR_SBC) + serial_out(up, UART_LCR, up->lcr & ~UART_LCR_SBC); + serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); +} + +static void omap_8250_throttle(struct uart_port *port) +{ + struct omap8250_priv *priv = port->private_data; + + guard(serial8250_rpm)(up_to_u8250p(port)); + guard(uart_port_lock_irqsave)(port); + + port->ops->stop_rx(port); + priv->throttled = true; +} + +static void omap_8250_unthrottle(struct uart_port *port) +{ + struct omap8250_priv *priv = port->private_data; + struct uart_8250_port *up = up_to_u8250p(port); + + guard(serial8250_rpm)(up); + /* Synchronize UART_IER access against the console. */ + guard(uart_port_lock_irqsave)(port); + + priv->throttled = false; + if (up->dma) + up->dma->rx_dma(up); + up->ier |= UART_IER_RLSI | UART_IER_RDI; + serial_out(up, UART_IER, up->ier); +} + +static int omap8250_rs485_config(struct uart_port *port, + struct ktermios *termios, + struct serial_rs485 *rs485) +{ + struct omap8250_priv *priv = port->private_data; + struct uart_8250_port *up = up_to_u8250p(port); + u32 fixed_delay_rts_before_send = 0; + u32 fixed_delay_rts_after_send = 0; + unsigned int baud; + + /* + * There is a fixed delay of 3 bit clock cycles after the TX shift + * register is going empty to allow time for the stop bit to transition + * through the transceiver before direction is changed to receive. + * + * Additionally there appears to be a 1 bit clock delay between writing + * to the THR register and transmission of the start bit, per page 8783 + * of the AM65 TRM: https://www.ti.com/lit/ug/spruid7e/spruid7e.pdf + */ + if (priv->quot) { + if (priv->mdr1 == UART_OMAP_MDR1_16X_MODE) + baud = port->uartclk / (16 * priv->quot); + else + baud = port->uartclk / (13 * priv->quot); + + fixed_delay_rts_after_send = 3 * MSEC_PER_SEC / baud; + fixed_delay_rts_before_send = 1 * MSEC_PER_SEC / baud; + } + + /* + * Fall back to RS485 software emulation if the UART is missing + * hardware support, if the device tree specifies an mctrl_gpio + * (indicates that RTS is unavailable due to a pinmux conflict) + * or if the requested delays exceed the fixed hardware delays. + */ + if (!(priv->habit & UART_HAS_NATIVE_RS485) || + mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS) || + rs485->delay_rts_after_send > fixed_delay_rts_after_send || + rs485->delay_rts_before_send > fixed_delay_rts_before_send) { + priv->mdr3 &= ~UART_OMAP_MDR3_DIR_EN; + serial_out(up, UART_OMAP_MDR3, priv->mdr3); + + port->rs485_config = serial8250_em485_config; + return serial8250_em485_config(port, termios, rs485); + } + + rs485->delay_rts_after_send = fixed_delay_rts_after_send; + rs485->delay_rts_before_send = fixed_delay_rts_before_send; + + if (rs485->flags & SER_RS485_ENABLED) + priv->mdr3 |= UART_OMAP_MDR3_DIR_EN; + else + priv->mdr3 &= ~UART_OMAP_MDR3_DIR_EN; + + /* + * Retain same polarity semantics as RS485 software emulation, + * i.e. SER_RS485_RTS_ON_SEND means driving RTS low on send. + */ + if (rs485->flags & SER_RS485_RTS_ON_SEND) + priv->mdr3 &= ~UART_OMAP_MDR3_DIR_POL; + else + priv->mdr3 |= UART_OMAP_MDR3_DIR_POL; + + serial_out(up, UART_OMAP_MDR3, priv->mdr3); + + return 0; +} + +#ifdef CONFIG_SERIAL_8250_DMA +static int omap_8250_rx_dma(struct uart_8250_port *p); + +/* Must be called while priv->rx_dma_lock is held */ +static void __dma_rx_do_complete(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + struct tty_port *tty_port = &p->port.state->port; + struct omap8250_priv *priv = p->port.private_data; + struct dma_chan *rxchan = dma->rxchan; + dma_cookie_t cookie; + struct dma_tx_state state; + int count; + int ret; + u32 reg; + + if (!dma->rx_running) + goto out; + + cookie = dma->rx_cookie; + + /* Re-enable RX FIFO interrupt now that transfer is complete */ + if (priv->habit & UART_HAS_RHR_IT_DIS) { + reg = serial_in(p, UART_OMAP_IER2); + reg &= ~UART_OMAP_IER2_RHR_IT_DIS; + serial_out(p, UART_OMAP_IER2, reg); + } + + dmaengine_tx_status(rxchan, cookie, &state); + + count = dma->rx_size - state.residue + state.in_flight_bytes; + if (count < dma->rx_size) { + dmaengine_terminate_async(rxchan); + + /* + * Poll for teardown to complete which guarantees in + * flight data is drained. + */ + if (state.in_flight_bytes) { + int poll_count = 25; + + while (dmaengine_tx_status(rxchan, cookie, NULL) && + poll_count--) + cpu_relax(); + + if (poll_count == -1) + dev_err(p->port.dev, "teardown incomplete\n"); + } + } + + dma->rx_running = 0; + if (!count) + goto out; + ret = tty_insert_flip_string(tty_port, dma->rx_buf, count); + + p->port.icount.rx += ret; + p->port.icount.buf_overrun += count - ret; +out: + + tty_flip_buffer_push(tty_port); +} + +static void __dma_rx_complete(void *param) +{ + struct uart_8250_port *p = param; + struct omap8250_priv *priv = p->port.private_data; + struct uart_8250_dma *dma = p->dma; + struct dma_tx_state state; + + /* Synchronize UART_IER access against the console. */ + guard(uart_port_lock_irqsave)(&p->port); + + /* + * If the tx status is not DMA_COMPLETE, then this is a delayed + * completion callback. A previous RX timeout flush would have + * already pushed the data, so exit. + */ + if (dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state) != DMA_COMPLETE) + return; + + __dma_rx_do_complete(p); + if (priv->throttled) + return; + + p->ier |= UART_IER_RLSI | UART_IER_RDI; + serial_out(p, UART_IER, p->ier); + if (!(priv->habit & UART_HAS_EFR2)) + omap_8250_rx_dma(p); +} + +static void omap_8250_rx_dma_flush(struct uart_8250_port *p) +{ + struct omap8250_priv *priv = p->port.private_data; + struct uart_8250_dma *dma = p->dma; + struct dma_tx_state state; + unsigned long flags; + int ret; + + spin_lock_irqsave(&priv->rx_dma_lock, flags); + + if (!dma->rx_running) { + spin_unlock_irqrestore(&priv->rx_dma_lock, flags); + return; + } + + ret = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + if (ret == DMA_IN_PROGRESS) { + ret = dmaengine_pause(dma->rxchan); + if (WARN_ON_ONCE(ret)) + priv->rx_dma_broken = true; + } + __dma_rx_do_complete(p); + spin_unlock_irqrestore(&priv->rx_dma_lock, flags); +} + +static int omap_8250_rx_dma(struct uart_8250_port *p) +{ + struct omap8250_priv *priv = p->port.private_data; + struct uart_8250_dma *dma = p->dma; + int err = 0; + struct dma_async_tx_descriptor *desc; + unsigned long flags; + u32 reg; + + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&p->port.lock); + + if (priv->rx_dma_broken) + return -EINVAL; + + spin_lock_irqsave(&priv->rx_dma_lock, flags); + + if (dma->rx_running) { + enum dma_status state; + + state = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, NULL); + if (state == DMA_COMPLETE) { + /* + * Disable RX interrupts to allow RX DMA completion + * callback to run. + */ + p->ier &= ~(UART_IER_RLSI | UART_IER_RDI); + serial_out(p, UART_IER, p->ier); + } + goto out; + } + + desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, + dma->rx_size, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + err = -EBUSY; + goto out; + } + + dma->rx_running = 1; + desc->callback = __dma_rx_complete; + desc->callback_param = p; + + dma->rx_cookie = dmaengine_submit(desc); + + /* + * Disable RX FIFO interrupt while RX DMA is enabled, else + * spurious interrupt may be raised when data is in the RX FIFO + * but is yet to be drained by DMA. + */ + if (priv->habit & UART_HAS_RHR_IT_DIS) { + reg = serial_in(p, UART_OMAP_IER2); + reg |= UART_OMAP_IER2_RHR_IT_DIS; + serial_out(p, UART_OMAP_IER2, reg); + } + + dma_async_issue_pending(dma->rxchan); +out: + spin_unlock_irqrestore(&priv->rx_dma_lock, flags); + return err; +} + +static int omap_8250_tx_dma(struct uart_8250_port *p); + +static void omap_8250_dma_tx_complete(void *param) +{ + struct uart_8250_port *p = param; + struct uart_8250_dma *dma = p->dma; + struct tty_port *tport = &p->port.state->port; + bool en_thri = false; + struct omap8250_priv *priv = p->port.private_data; + + dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + guard(uart_port_lock_irqsave)(&p->port); + + dma->tx_running = 0; + + uart_xmit_advance(&p->port, dma->tx_size); + + if (priv->delayed_restore) { + priv->delayed_restore = 0; + omap8250_restore_regs(p); + } + + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) + uart_write_wakeup(&p->port); + + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(&p->port)) { + int ret; + + ret = omap_8250_tx_dma(p); + if (ret) + en_thri = true; + } else if (p->capabilities & UART_CAP_RPM) { + en_thri = true; + } + + if (en_thri) { + dma->tx_err = 1; + serial8250_set_THRI(p); + } +} + +static int omap_8250_tx_dma(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + struct omap8250_priv *priv = p->port.private_data; + struct tty_port *tport = &p->port.state->port; + struct dma_async_tx_descriptor *desc; + struct scatterlist sg; + int skip_byte = -1; + int ret; + + if (dma->tx_running) + return 0; + if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) { + + /* + * Even if no data, we need to return an error for the two cases + * below so serial8250_tx_chars() is invoked and properly clears + * THRI and/or runtime suspend. + */ + if (dma->tx_err || p->capabilities & UART_CAP_RPM) { + ret = -EBUSY; + goto err; + } + serial8250_clear_THRI(p); + return 0; + } + + if (priv->habit & OMAP_DMA_TX_KICK) { + unsigned char c; + u8 tx_lvl; + + /* + * We need to put the first byte into the FIFO in order to start + * the DMA transfer. For transfers smaller than four bytes we + * don't bother doing DMA at all. It seem not matter if there + * are still bytes in the FIFO from the last transfer (in case + * we got here directly from omap_8250_dma_tx_complete()). Bytes + * leaving the FIFO seem not to trigger the DMA transfer. It is + * really the byte that we put into the FIFO. + * If the FIFO is already full then we most likely got here from + * omap_8250_dma_tx_complete(). And this means the DMA engine + * just completed its work. We don't have to wait the complete + * 86us at 115200,8n1 but around 60us (not to mention lower + * baudrates). So in that case we take the interrupt and try + * again with an empty FIFO. + */ + tx_lvl = serial_in(p, UART_OMAP_TX_LVL); + if (tx_lvl == p->tx_loadsz) { + ret = -EBUSY; + goto err; + } + if (kfifo_len(&tport->xmit_fifo) < 4) { + ret = -EINVAL; + goto err; + } + if (!uart_fifo_out(&p->port, &c, 1)) { + ret = -EINVAL; + goto err; + } + skip_byte = c; + } + + sg_init_table(&sg, 1); + ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1, UART_XMIT_SIZE, dma->tx_addr); + if (ret != 1) { + ret = -EINVAL; + goto err; + } + + desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + ret = -EBUSY; + goto err; + } + + dma->tx_size = sg_dma_len(&sg); + dma->tx_running = 1; + + desc->callback = omap_8250_dma_tx_complete; + desc->callback_param = p; + + dma->tx_cookie = dmaengine_submit(desc); + + dma_sync_single_for_device(dma->txchan->device->dev, dma->tx_addr, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + dma_async_issue_pending(dma->txchan); + if (dma->tx_err) + dma->tx_err = 0; + + serial8250_clear_THRI(p); + ret = 0; + goto out_skip; +err: + dma->tx_err = 1; +out_skip: + if (skip_byte >= 0) + serial_out(p, UART_TX, skip_byte); + return ret; +} + +static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) +{ + switch (iir & 0x3f) { + case UART_IIR_RLSI: + case UART_IIR_RX_TIMEOUT: + case UART_IIR_RDI: + omap_8250_rx_dma_flush(up); + return true; + } + return omap_8250_rx_dma(up); +} + +static u16 omap_8250_handle_rx_dma(struct uart_8250_port *up, u8 iir, u16 status) +{ + if ((status & (UART_LSR_DR | UART_LSR_BI)) && + (iir & UART_IIR_RDI)) { + if (handle_rx_dma(up, iir)) { + status = serial8250_rx_chars(up, status); + omap_8250_rx_dma(up); + } + } + + return status; +} + +static void am654_8250_handle_uart_errors(struct uart_8250_port *up, u8 iir, u16 status) +{ + if (status & UART_LSR_OE) { + serial8250_clear_and_reinit_fifos(up); + serial_in(up, UART_LSR); + serial_in(up, UART_OMAP_RESUME); + } else { + if (status & (UART_LSR_FE | UART_LSR_PE | UART_LSR_BI)) + serial_in(up, UART_RX); + if (iir & UART_IIR_XOFF) + serial_in(up, UART_IIR); + } +} + +static void am654_8250_handle_rx_dma(struct uart_8250_port *up, u8 iir, + u16 status) +{ + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&up->port.lock); + + /* + * Queue a new transfer if FIFO has data. + */ + if ((status & (UART_LSR_DR | UART_LSR_BI)) && + (up->ier & UART_IER_RDI) && !(status & UART_LSR_OE)) { + am654_8250_handle_uart_errors(up, iir, status); + omap_8250_rx_dma(up); + serial_out(up, UART_OMAP_EFR2, UART_OMAP_EFR2_TIMEOUT_BEHAVE); + } else if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) { + /* + * Disable RX timeout, read IIR to clear + * current timeout condition, clear EFR2 to + * periodic timeouts, re-enable interrupts. + */ + up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); + serial_out(up, UART_IER, up->ier); + omap_8250_rx_dma_flush(up); + serial_in(up, UART_IIR); + serial_out(up, UART_OMAP_EFR2, 0x0); + up->ier |= UART_IER_RLSI | UART_IER_RDI; + serial_out(up, UART_IER, up->ier); + } else { + am654_8250_handle_uart_errors(up, iir, status); + } +} + +/* + * This is mostly serial8250_handle_irq(). We have a slightly different DMA + * hook for RX/TX and need different logic for them in the ISR. Therefore we + * use the default routine in the non-DMA case and this one for with DMA. + */ +static int omap_8250_dma_handle_irq(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct omap8250_priv *priv = port->private_data; + u16 status; + u8 iir; + + iir = serial_port_in(port, UART_IIR); + if (iir & UART_IIR_NO_INT) { + return IRQ_HANDLED; + } + + uart_port_lock(port); + + status = serial_port_in(port, UART_LSR); + + if ((iir & 0x3f) != UART_IIR_THRI) { + if (priv->habit & UART_HAS_EFR2) + am654_8250_handle_rx_dma(up, iir, status); + else + status = omap_8250_handle_rx_dma(up, iir, status); + } + + serial8250_modem_status(up); + if (status & UART_LSR_THRE && up->dma->tx_err) { + if (uart_tx_stopped(port) || + kfifo_is_empty(&port->state->port.xmit_fifo)) { + up->dma->tx_err = 0; + serial8250_tx_chars(up); + } else { + /* + * try again due to an earlier failure which + * might have been resolved by now. + */ + if (omap_8250_tx_dma(up)) + serial8250_tx_chars(up); + } + } + + uart_unlock_and_check_sysrq(port); + + return 1; +} + +static bool the_no_dma_filter_fn(struct dma_chan *chan, void *param) +{ + return false; +} + +#else + +static inline int omap_8250_rx_dma(struct uart_8250_port *p) +{ + return -EINVAL; +} +#endif + +static int omap8250_no_handle_irq(struct uart_port *port) +{ + /* IRQ has not been requested but handling irq? */ + WARN_ONCE(1, "Unexpected irq handling before port startup\n"); + return 0; +} + +static int omap8250_select_wakeup_pinctrl(struct device *dev, + struct omap8250_priv *priv) +{ + if (IS_ERR_OR_NULL(priv->pinctrl_wakeup)) + return 0; + + if (!device_may_wakeup(dev)) + return 0; + + device_set_out_band_wakeup(dev); + + return pinctrl_select_state(priv->pinctrl, priv->pinctrl_wakeup); +} + +static struct omap8250_dma_params am654_dma = { + .rx_size = SZ_2K, + .rx_trigger = 1, + .tx_trigger = TX_TRIGGER, +}; + +static struct omap8250_dma_params am33xx_dma = { + .rx_size = RX_TRIGGER, + .rx_trigger = RX_TRIGGER, + .tx_trigger = TX_TRIGGER, +}; + +static struct omap8250_platdata am654_platdata = { + .dma_params = &am654_dma, + .habit = UART_HAS_EFR2 | UART_HAS_RHR_IT_DIS | + UART_RX_TIMEOUT_QUIRK | UART_HAS_NATIVE_RS485, +}; + +static struct omap8250_platdata am33xx_platdata = { + .dma_params = &am33xx_dma, + .habit = OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE, +}; + +static struct omap8250_platdata omap4_platdata = { + .dma_params = &am33xx_dma, + .habit = UART_ERRATA_CLOCK_DISABLE, +}; + +static const struct of_device_id omap8250_dt_ids[] = { + { .compatible = "ti,am654-uart", .data = &am654_platdata, }, + { .compatible = "ti,omap2-uart" }, + { .compatible = "ti,omap3-uart" }, + { .compatible = "ti,omap4-uart", .data = &omap4_platdata, }, + { .compatible = "ti,am3352-uart", .data = &am33xx_platdata, }, + { .compatible = "ti,am4372-uart", .data = &am33xx_platdata, }, + { .compatible = "ti,dra742-uart", .data = &omap4_platdata, }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap8250_dt_ids); + +static int omap8250_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct omap8250_priv *priv; + const struct omap8250_platdata *pdata; + struct uart_8250_port up; + struct resource *regs; + void __iomem *membase; + int ret; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(&pdev->dev, "missing registers\n"); + return -EINVAL; + } + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + membase = devm_ioremap(&pdev->dev, regs->start, + resource_size(regs)); + if (!membase) + return -ENODEV; + + memset(&up, 0, sizeof(up)); + up.port.dev = &pdev->dev; + up.port.mapbase = regs->start; + up.port.membase = membase; + /* + * It claims to be 16C750 compatible however it is a little different. + * It has EFR and has no FCR7_64byte bit. The AFE (which it claims to + * have) is enabled via EFR instead of MCR. The type is set here 8250 + * just to get things going. UNKNOWN does not work for a few reasons and + * we don't need our own type since we don't use 8250's set_termios() + * or pm callback. + */ + up.port.type = PORT_8250; + up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SOFT_FLOW | UPF_HARD_FLOW; + up.port.private_data = priv; + + up.tx_loadsz = 64; + up.capabilities = UART_CAP_FIFO; +#ifdef CONFIG_PM + /* + * Runtime PM is mostly transparent. However to do it right we need to a + * TX empty interrupt before we can put the device to auto idle. So if + * PM is not enabled we don't add that flag and can spare that one extra + * interrupt in the TX path. + */ + up.capabilities |= UART_CAP_RPM; +#endif + up.port.set_termios = omap_8250_set_termios; + up.port.set_mctrl = omap8250_set_mctrl; + up.port.pm = omap_8250_pm; + up.port.startup = omap_8250_startup; + up.port.shutdown = omap_8250_shutdown; + up.port.throttle = omap_8250_throttle; + up.port.unthrottle = omap_8250_unthrottle; + up.port.rs485_config = omap8250_rs485_config; + /* same rs485_supported for software emulation and native RS485 */ + up.port.rs485_supported = serial8250_em485_supported; + up.rs485_start_tx = serial8250_em485_start_tx; + up.rs485_stop_tx = serial8250_em485_stop_tx; + up.port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE); + + ret = uart_read_port_properties(&up.port); + if (ret) + return ret; + + up.port.regshift = OMAP_UART_REGSHIFT; + up.port.fifosize = 64; + + if (!up.port.uartclk) { + struct clk *clk; + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } else { + up.port.uartclk = clk_get_rate(clk); + } + } + + if (of_property_read_u32(np, "overrun-throttle-ms", + &up.overrun_backoff_time_ms) != 0) + up.overrun_backoff_time_ms = 0; + + pdata = of_device_get_match_data(&pdev->dev); + if (pdata) + priv->habit |= pdata->habit; + + if (!up.port.uartclk) { + up.port.uartclk = DEFAULT_CLK_SPEED; + dev_warn(&pdev->dev, + "No clock speed specified: using default: %d\n", + DEFAULT_CLK_SPEED); + } + + priv->membase = membase; + priv->line = -ENODEV; + priv->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; + priv->calc_latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; + cpu_latency_qos_add_request(&priv->pm_qos_request, priv->latency); + INIT_WORK(&priv->qos_work, omap8250_uart_qos_work); + + spin_lock_init(&priv->rx_dma_lock); + + platform_set_drvdata(pdev, priv); + + device_set_wakeup_capable(&pdev->dev, true); + if (of_property_read_bool(np, "wakeup-source")) + device_set_wakeup_enable(&pdev->dev, true); + + pm_runtime_enable(&pdev->dev); + pm_runtime_use_autosuspend(&pdev->dev); + + /* + * Disable runtime PM until autosuspend delay unless specifically + * enabled by the user via sysfs. This is the historic way to + * prevent an unsafe default policy with lossy characters on wake-up. + * For serdev devices this is not needed, the policy can be managed by + * the serdev driver. + */ + if (!of_get_available_child_count(pdev->dev.of_node)) + pm_runtime_set_autosuspend_delay(&pdev->dev, -1); + + pm_runtime_get_sync(&pdev->dev); + + omap_serial_fill_features_erratas(&up, priv); + up.port.handle_irq = omap8250_no_handle_irq; + priv->rx_trigger = RX_TRIGGER; + priv->tx_trigger = TX_TRIGGER; +#ifdef CONFIG_SERIAL_8250_DMA + /* + * Oh DMA support. If there are no DMA properties in the DT then + * we will fall back to a generic DMA channel which does not + * really work here. To ensure that we do not get a generic DMA + * channel assigned, we have the the_no_dma_filter_fn() here. + * To avoid "failed to request DMA" messages we check for DMA + * properties in DT. + */ + ret = of_property_count_strings(np, "dma-names"); + if (ret == 2) { + struct omap8250_dma_params *dma_params = NULL; + struct uart_8250_dma *dma = &priv->omap8250_dma; + + dma->fn = the_no_dma_filter_fn; + dma->tx_dma = omap_8250_tx_dma; + dma->rx_dma = omap_8250_rx_dma; + if (pdata) + dma_params = pdata->dma_params; + + if (dma_params) { + dma->rx_size = dma_params->rx_size; + dma->rxconf.src_maxburst = dma_params->rx_trigger; + dma->txconf.dst_maxburst = dma_params->tx_trigger; + priv->rx_trigger = dma_params->rx_trigger; + priv->tx_trigger = dma_params->tx_trigger; + } else { + dma->rx_size = RX_TRIGGER; + dma->rxconf.src_maxburst = RX_TRIGGER; + dma->txconf.dst_maxburst = TX_TRIGGER; + } + } +#endif + + irq_set_status_flags(up.port.irq, IRQ_NOAUTOEN); + ret = devm_request_irq(&pdev->dev, up.port.irq, omap8250_irq, 0, + dev_name(&pdev->dev), priv); + if (ret < 0) + goto err; + + priv->wakeirq = irq_of_parse_and_map(np, 1); + + ret = serial8250_register_8250_port(&up); + if (ret < 0) { + dev_err(&pdev->dev, "unable to register 8250 port\n"); + goto err; + } + priv->line = ret; + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + + priv->pinctrl = devm_pinctrl_get(&pdev->dev); + if (!IS_ERR_OR_NULL(priv->pinctrl)) + priv->pinctrl_wakeup = pinctrl_lookup_state(priv->pinctrl, "wakeup"); + + return 0; +err: + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); + flush_work(&priv->qos_work); + pm_runtime_disable(&pdev->dev); + cpu_latency_qos_remove_request(&priv->pm_qos_request); + return ret; +} + +static void omap8250_remove(struct platform_device *pdev) +{ + struct omap8250_priv *priv = platform_get_drvdata(pdev); + struct uart_8250_port *up; + int err; + + err = pm_runtime_resume_and_get(&pdev->dev); + if (err) + dev_err(&pdev->dev, "Failed to resume hardware\n"); + + up = serial8250_get_port(priv->line); + omap_8250_shutdown(&up->port); + serial8250_unregister_port(priv->line); + priv->line = -ENODEV; + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); + flush_work(&priv->qos_work); + pm_runtime_disable(&pdev->dev); + cpu_latency_qos_remove_request(&priv->pm_qos_request); + device_set_wakeup_capable(&pdev->dev, false); +} + +static int omap8250_prepare(struct device *dev) +{ + struct omap8250_priv *priv = dev_get_drvdata(dev); + + if (!priv) + return 0; + priv->is_suspending = true; + return 0; +} + +static void omap8250_complete(struct device *dev) +{ + struct omap8250_priv *priv = dev_get_drvdata(dev); + + if (!priv) + return; + priv->is_suspending = false; +} + +static int omap8250_suspend(struct device *dev) +{ + struct omap8250_priv *priv = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(priv->line); + int err = 0; + + err = omap8250_select_wakeup_pinctrl(dev, priv); + if (err) { + dev_err(dev, "Failed to select wakeup pinctrl, aborting suspend %pe\n", + ERR_PTR(err)); + return err; + } + + serial8250_suspend_port(priv->line); + + err = pm_runtime_resume_and_get(dev); + if (err) + return err; + if (!device_may_wakeup(dev)) + priv->wer = 0; + serial_out(up, UART_OMAP_WER, priv->wer); + if (uart_console(&up->port) && console_suspend_enabled) + err = pm_runtime_force_suspend(dev); + flush_work(&priv->qos_work); + + return err; +} + +static int omap8250_resume(struct device *dev) +{ + struct omap8250_priv *priv = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(priv->line); + int err; + + err = pinctrl_select_default_state(dev); + if (err) { + dev_err(dev, "Failed to select default pinctrl state on resume: %pe\n", + ERR_PTR(err)); + return err; + } + + if (uart_console(&up->port) && console_suspend_enabled) { + err = pm_runtime_force_resume(dev); + if (err) + return err; + } + + serial8250_resume_port(priv->line); + /* Paired with pm_runtime_resume_and_get() in omap8250_suspend() */ + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; +} + +static int omap8250_lost_context(struct uart_8250_port *up) +{ + u32 val; + + val = serial_in(up, UART_OMAP_SCR); + /* + * If we lose context, then SCR is set to its reset value of zero. + * After set_termios() we set bit 3 of SCR (TX_EMPTY_CTL_IT) to 1, + * among other bits, to never set the register back to zero again. + */ + if (!val) + return 1; + return 0; +} + +static void uart_write(struct omap8250_priv *priv, u32 reg, u32 val) +{ + writel(val, priv->membase + (reg << OMAP_UART_REGSHIFT)); +} + +/* TODO: in future, this should happen via API in drivers/reset/ */ +static int omap8250_soft_reset(struct device *dev) +{ + struct omap8250_priv *priv = dev_get_drvdata(dev); + int timeout = 100; + int sysc; + int syss; + + /* + * At least on omap4, unused uarts may not idle after reset without + * a basic scr dma configuration even with no dma in use. The + * module clkctrl status bits will be 1 instead of 3 blocking idle + * for the whole clockdomain. The softreset below will clear scr, + * and we restore it on resume so this is safe to do on all SoCs + * needing omap8250_soft_reset() quirk. Do it in two writes as + * recommended in the comment for omap8250_update_scr(). + */ + uart_write(priv, UART_OMAP_SCR, OMAP_UART_SCR_DMAMODE_1); + uart_write(priv, UART_OMAP_SCR, + OMAP_UART_SCR_DMAMODE_1 | OMAP_UART_SCR_DMAMODE_CTL); + + sysc = uart_read(priv, UART_OMAP_SYSC); + + /* softreset the UART */ + sysc |= OMAP_UART_SYSC_SOFTRESET; + uart_write(priv, UART_OMAP_SYSC, sysc); + + /* By experiments, 1us enough for reset complete on AM335x */ + do { + udelay(1); + syss = uart_read(priv, UART_OMAP_SYSS); + } while (--timeout && !(syss & OMAP_UART_SYSS_RESETDONE)); + + if (!timeout) { + dev_err(dev, "timed out waiting for reset done\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int omap8250_runtime_suspend(struct device *dev) +{ + struct omap8250_priv *priv = dev_get_drvdata(dev); + struct uart_8250_port *up = NULL; + + if (priv->line >= 0) + up = serial8250_get_port(priv->line); + + if (priv->habit & UART_ERRATA_CLOCK_DISABLE) { + int ret; + + ret = omap8250_soft_reset(dev); + if (ret) + return ret; + + if (up) { + /* Restore to UART mode after reset (for wakeup) */ + omap8250_update_mdr1(up, priv); + /* Restore wakeup enable register */ + serial_out(up, UART_OMAP_WER, priv->wer); + } + } + + if (up && up->dma && up->dma->rxchan) + omap_8250_rx_dma_flush(up); + + priv->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; + schedule_work(&priv->qos_work); + atomic_set(&priv->active, 0); + + return 0; +} + +static int omap8250_runtime_resume(struct device *dev) +{ + struct omap8250_priv *priv = dev_get_drvdata(dev); + struct uart_8250_port *up = NULL; + + /* Did the hardware wake to a device IO interrupt before a wakeirq? */ + if (atomic_read(&priv->active)) + return 0; + + if (priv->line >= 0) + up = serial8250_get_port(priv->line); + + if (up && omap8250_lost_context(up)) { + guard(uart_port_lock_irq)(&up->port); + omap8250_restore_regs(up); + } + + if (up && up->dma && up->dma->rxchan && !(priv->habit & UART_HAS_EFR2)) { + guard(uart_port_lock_irq)(&up->port); + omap_8250_rx_dma(up); + } + + atomic_set(&priv->active, 1); + priv->latency = priv->calc_latency; + schedule_work(&priv->qos_work); + + return 0; +} + +#ifdef CONFIG_SERIAL_8250_OMAP_TTYO_FIXUP +static int __init omap8250_console_fixup(void) +{ + char *omap_str; + char *options; + u8 idx; + + if (strstr(boot_command_line, "console=ttyS")) + /* user set a ttyS based name for the console */ + return 0; + + omap_str = strstr(boot_command_line, "console=ttyO"); + if (!omap_str) + /* user did not set ttyO based console, so we don't care */ + return 0; + + omap_str += 12; + if ('0' <= *omap_str && *omap_str <= '9') + idx = *omap_str - '0'; + else + return 0; + + omap_str++; + if (omap_str[0] == ',') { + omap_str++; + options = omap_str; + } else { + options = NULL; + } + + add_preferred_console("ttyS", idx, options); + pr_err("WARNING: Your 'console=ttyO%d' has been replaced by 'ttyS%d'\n", + idx, idx); + pr_err("This ensures that you still see kernel messages. Please\n"); + pr_err("update your kernel commandline.\n"); + return 0; +} +console_initcall(omap8250_console_fixup); +#endif + +static const struct dev_pm_ops omap8250_dev_pm_ops = { + SYSTEM_SLEEP_PM_OPS(omap8250_suspend, omap8250_resume) + RUNTIME_PM_OPS(omap8250_runtime_suspend, + omap8250_runtime_resume, NULL) + .prepare = pm_sleep_ptr(omap8250_prepare), + .complete = pm_sleep_ptr(omap8250_complete), +}; + +static struct platform_driver omap8250_platform_driver = { + .driver = { + .name = "omap8250", + .pm = pm_ptr(&omap8250_dev_pm_ops), + .of_match_table = omap8250_dt_ids, + }, + .probe = omap8250_probe, + .remove = omap8250_remove, +}; +module_platform_driver(omap8250_platform_driver); + +MODULE_AUTHOR("Sebastian Andrzej Siewior"); +MODULE_DESCRIPTION("OMAP 8250 Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/8250/8250_parisc.c b/drivers/tty/serial/8250/8250_parisc.c new file mode 100644 index 000000000..4ba05a987 --- /dev/null +++ b/drivers/tty/serial/8250/8250_parisc.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Serial Device Initialisation for Lasi/Asp/Wax/Dino + * + * (c) Copyright Matthew Wilcox <willy@debian.org> 2001-2002 + */ + +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/serial_core.h> +#include <linux/signal.h> +#include <linux/types.h> + +#include <asm/hardware.h> +#include <asm/parisc-device.h> +#include <asm/io.h> + +#include "8250.h" + +static int __init serial_init_chip(struct parisc_device *dev) +{ + struct uart_8250_port uart; + unsigned long address; + int err; + +#if defined(CONFIG_64BIT) && defined(CONFIG_IOSAPIC) + if (!dev->irq && (dev->id.sversion == 0xad)) + dev->irq = iosapic_serial_irq(dev); +#endif + + if (!dev->irq) { + /* We find some unattached serial ports by walking native + * busses. These should be silently ignored. Otherwise, + * what we have here is a missing parent device, so tell + * the user what they're missing. + */ + if (parisc_parent(dev)->id.hw_type != HPHW_IOA) + dev_info(&dev->dev, + "Serial: device 0x%llx not configured.\n" + "Enable support for Wax, Lasi, Asp or Dino.\n", + (unsigned long long)dev->hpa.start); + return -ENODEV; + } + + address = dev->hpa.start; + if (dev->id.sversion != 0x8d) + address += 0x800; + + memset(&uart, 0, sizeof(uart)); + uart.port.iotype = UPIO_MEM; + /* 7.272727MHz on Lasi. Assumed the same for Dino, Wax and Timi. */ + uart.port.uartclk = (dev->id.sversion != 0xad) ? + 7272727 : 1843200; + uart.port.mapbase = address; + uart.port.membase = ioremap(address, 16); + if (!uart.port.membase) { + dev_warn(&dev->dev, "Failed to map memory\n"); + return -ENOMEM; + } + uart.port.irq = dev->irq; + uart.port.flags = UPF_BOOT_AUTOCONF; + uart.port.dev = &dev->dev; + + err = serial8250_register_8250_port(&uart); + if (err < 0) { + dev_warn(&dev->dev, + "serial8250_register_8250_port returned error %d\n", + err); + iounmap(uart.port.membase); + return err; + } + + return 0; +} + +static const struct parisc_device_id serial_tbl[] __initconst = { + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00075 }, + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008c }, + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008d }, + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x000ad }, + { 0 } +}; + +/* Hack. Some machines have SERIAL_0 attached to Lasi and SERIAL_1 + * attached to Dino. Unfortunately, Dino appears before Lasi in the device + * tree. To ensure that ttyS0 == SERIAL_0, we register two drivers; one + * which only knows about Lasi and then a second which will find all the + * other serial ports. HPUX ignores this problem. + */ +static const struct parisc_device_id lasi_tbl[] __initconst = { + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03B, 0x0008C }, /* C1xx/C1xxL */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03C, 0x0008C }, /* B132L */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03D, 0x0008C }, /* B160L */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03E, 0x0008C }, /* B132L+ */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03F, 0x0008C }, /* B180L+ */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x046, 0x0008C }, /* Rocky2 120 */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x047, 0x0008C }, /* Rocky2 150 */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x04E, 0x0008C }, /* Kiji L2 132 */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x056, 0x0008C }, /* Raven+ */ + { 0 } +}; + + +MODULE_DEVICE_TABLE(parisc, serial_tbl); + +static struct parisc_driver lasi_driver __refdata = { + .name = "serial_1", + .id_table = lasi_tbl, + .probe = serial_init_chip, +}; + +static struct parisc_driver serial_driver __refdata = { + .name = "serial", + .id_table = serial_tbl, + .probe = serial_init_chip, +}; + +static int __init probe_serial_gsc(void) +{ + register_parisc_driver(&lasi_driver); + register_parisc_driver(&serial_driver); + return 0; +} + +module_init(probe_serial_gsc); + +MODULE_DESCRIPTION("Serial Device Initialisation for Lasi/Asp/Wax/Dino"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c new file mode 100644 index 000000000..c24a59896 --- /dev/null +++ b/drivers/tty/serial/8250/8250_pci.c @@ -0,0 +1,6091 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Probe module for 8250/16550-type PCI serial ports. + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + */ +#undef DEBUG +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/math.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/serial_reg.h> +#include <linux/serial_core.h> +#include <linux/8250_pci.h> +#include <linux/bitops.h> +#include <linux/bitfield.h> + +#include <asm/byteorder.h> +#include <asm/io.h> + +#include "8250.h" +#include "8250_pcilib.h" + +#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_PCI1600 0x1600 +#define PCI_DEVICE_ID_ADVANTECH_PCI1600_1611 0x1611 +#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_DEVICE_ID_WCHCN_CH352_2S 0x3253 +#define PCI_DEVICE_ID_WCHCN_CH355_4S 0x7173 + +#define PCI_VENDOR_ID_AGESTAR 0x5372 +#define PCI_DEVICE_ID_AGESTAR_9375 0x6872 +#define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a +#define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e + +#define PCI_DEVICE_ID_WCHIC_CH384_4S 0x3470 +#define PCI_DEVICE_ID_WCHIC_CH384_8S 0x3853 + +#define PCI_DEVICE_ID_ADDIDATA_CPCI7500 0x7003 +#define PCI_DEVICE_ID_ADDIDATA_CPCI7500_NG 0x7024 +#define PCI_DEVICE_ID_ADDIDATA_CPCI7420_NG 0x7025 +#define PCI_DEVICE_ID_ADDIDATA_CPCI7300_NG 0x7026 + +#define PCI_VENDOR_ID_SYSTEMBASE 0x14a1 + +/* 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 + +/* + * 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); +}; + +struct f815xxa_data { + spinlock_t lock; + int idx; +}; + +struct serial_private { + struct pci_dev *dev; + unsigned int nr; + struct pci_serial_quirk *quirk; + const struct pciserial_board *board; + int line[]; +}; + +#define PCI_DEVICE_ID_HPE_PCI_SERIAL 0x37e +#define PCIE_VENDOR_ID_ASIX 0x125B +#define PCIE_DEVICE_ID_AX99100 0x9100 + +static const struct pci_device_id pci_use_msi[] = { + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x1000) }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9912, + 0xA000, 0x1000) }, + { PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9922, + 0xA000, 0x1000) }, + { PCI_VDEVICE_SUB(ASIX, PCI_DEVICE_ID_ASIX_AX99100, + 0xA000, 0x1000) }, + { PCI_VDEVICE(HP_3PAR, PCI_DEVICE_ID_HPE_PCI_SERIAL) }, + { PCI_DEVICE_SUB(PCIE_VENDOR_ID_ASIX, PCIE_DEVICE_ID_AX99100, + 0xA000, 0x1000) }, + { } +}; + +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) +{ + pci_err(dev, "%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", + str, dev->vendor, dev->device, + dev->subsystem_vendor, dev->subsystem_device); +} + +static int +setup_port(struct serial_private *priv, struct uart_8250_port *port, + u8 bar, unsigned int offset, int regshift) +{ + void __iomem *iomem = NULL; + + if (pci_resource_flags(priv->dev, bar) & IORESOURCE_MEM) { + iomem = pcim_iomap(priv->dev, bar, 0); + if (!iomem) + return -ENOMEM; + } + + return serial8250_pci_setup_port(priv->dev, port, bar, offset, regshift, iomem); +} + +/* + * 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 */ + pci_dbg(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) +{ + u8 irq_config; + void __iomem *p; + + if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar 0", dev); + return 0; + } + + irq_config = 0x41; + if (dev->vendor == PCI_VENDOR_ID_PANACOM || + dev->subsystem_vendor == PCI_SUBVENDOR_ID_EXSYS) + irq_config = 0x43; + + if ((dev->vendor == PCI_VENDOR_ID_PLX) && + (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) + /* + * As the megawolf cards have the int pins active + * high, and have 2 UART chips, both ints must be + * enabled on the 9050. Also, the UARTS are set in + * 16450 mode by default, so we have to enable the + * 16C950 'enhanced' mode so that we can use the + * deep FIFOs + */ + irq_config = 0x5b; + /* + * enable/disable interrupts + */ + p = ioremap(pci_resource_start(dev, 0), 0x80); + if (p == NULL) + return -ENOMEM; + writel(irq_config, p + 0x4c); + + /* + * Read the register back to ensure that it took effect. + */ + readl(p + 0x4c); + iounmap(p); + + return 0; +} + +static void pci_plx9050_exit(struct pci_dev *dev) +{ + u8 __iomem *p; + + if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) + return; + + /* + * disable interrupts + */ + p = ioremap(pci_resource_start(dev, 0), 0x80); + if (p != NULL) { + writel(0, p + 0x4c); + + /* + * Read the register back to ensure that it took effect. + */ + readl(p + 0x4c); + iounmap(p); + } +} + +#define NI8420_INT_ENABLE_REG 0x38 +#define NI8420_INT_ENABLE_BIT 0x2000 + +static void pci_ni8420_exit(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; + } + + p = pci_ioremap_bar(dev, bar); + if (p == NULL) + return; + + /* Disable the CPU Interrupt */ + writel(readl(p + NI8420_INT_ENABLE_REG) & ~(NI8420_INT_ENABLE_BIT), + p + NI8420_INT_ENABLE_REG); + iounmap(p); +} + + +/* 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) + +static void pci_ni8430_exit(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; + } + + p = pci_ioremap_bar(dev, bar); + if (p == NULL) + return; + + /* Disable the CPU Interrupt */ + writel(MITE_LCIMR2_CLR_CPU_IE, p + MITE_LCIMR2); + iounmap(p); +} + +/* 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_ioremap_bar(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); + + /* Set bit-2 (INTENABLE) of Control Register */ + writeb(0x4, p + OCT_REG_CR_OFF); + iounmap(p); + + return 0; +} + +/* + * Disables the global interrupt of PMC-OctalPro + */ + +static void sbs_exit(struct pci_dev *dev) +{ + u8 __iomem *p; + + p = pci_ioremap_bar(dev, 0); + /* FIXME: What if resource_len < OCT_REG_CR_OFF */ + if (p != NULL) + writeb(0, p + OCT_REG_CR_OFF); + iounmap(p); +} + +/* + * 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 = ioremap(pci_resource_start(dev, 0), 0x80); + if (p == NULL) + return -ENOMEM; + + writew(readw(p + 0x28) & data, p + 0x28); + readw(p + 0x28); + iounmap(p); + 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); + 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) { + pci_info(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) +{ + msleep(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_ioremap_bar(dev, bar); + if (p == NULL) + return -ENOMEM; + + /* Enable CPU Interrupt */ + writel(readl(p + NI8420_INT_ENABLE_REG) | NI8420_INT_ENABLE_BIT, + p + NI8420_INT_ENABLE_REG); + + iounmap(p); + return 0; +} + +#define MITE_IOWBSR1_WSIZE 0xa +#define MITE_IOWBSR1_WIN_OFFSET 0x800 +#define MITE_IOWBSR1_WENAB (1 << 7) +#define MITE_LCIMR1_IO_IE_0 (1 << 24) +#define MITE_LCIMR2_SET_CPU_IE (1 << 31) +#define MITE_IOWCR1_RAMSEL_MASK 0xfffffffe + +static int pci_ni8430_init(struct pci_dev *dev) +{ + void __iomem *p; + struct pci_bus_region region; + u32 device_window; + unsigned int bar = 0; + + if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar", dev); + return 0; + } + + p = pci_ioremap_bar(dev, bar); + if (p == NULL) + return -ENOMEM; + + /* + * Set device window address and size in BAR0, while acknowledging that + * the resource structure may contain a translated address that differs + * from the address the device responds to. + */ + pcibios_resource_to_bus(dev->bus, ®ion, &dev->resource[bar]); + device_window = ((region.start + MITE_IOWBSR1_WIN_OFFSET) & 0xffffff00) + | MITE_IOWBSR1_WENAB | MITE_IOWBSR1_WSIZE; + writel(device_window, p + MITE_IOWBSR1); + + /* Set window access to go to RAMSEL IO address space */ + writel((readl(p + MITE_IOWCR1) & MITE_IOWCR1_RAMSEL_MASK), + p + MITE_IOWCR1); + + /* Enable IO Bus Interrupt 0 */ + writel(MITE_LCIMR1_IO_IE_0, p + MITE_LCIMR1); + + /* Enable CPU Interrupt */ + writel(MITE_LCIMR2_SET_CPU_IE, p + MITE_LCIMR2); + + iounmap(p); + return 0; +} + +/* UART Port Control Register */ +#define NI8430_PORTCON 0x0f +#define NI8430_PORTCON_TXVR_ENABLE (1 << 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_ioremap_bar(dev, bar); + if (!p) + return -ENOMEM; + + /* enable the transceiver */ + writeb(readb(p + offset + NI8430_PORTCON) | NI8430_PORTCON_TXVR_ENABLE, + p + offset + NI8430_PORTCON); + + iounmap(p); + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +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); + } + + 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; + + pci_err(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: + case PCIE_DEVICE_ID_AX99100: + 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) + +/* inta_addr are the configuration addresses of the ITE */ +static const short inta_addr[] = { 0x2a0, 0x2c0, 0x220, 0x240, 0x1e0, 0x200, 0x280 }; +static int pci_ite887x_init(struct pci_dev *dev) +{ + int ret, i, type; + struct resource *iobase = NULL; + u32 miscr, uartbar, ioport; + + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(dev); + + /* search for the base-ioport */ + for (i = 0; i < ARRAY_SIZE(inta_addr); i++) { + iobase = request_region(inta_addr[i], ITE_887x_IOSIZE, + "ite887x"); + if (iobase != NULL) { + /* 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->start, ITE_887x_IOSIZE); + } + } + + if (i == ARRAY_SIZE(inta_addr)) { + pci_err(dev, "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->start, ITE_887x_IOSIZE); + } + + return ret; +} + +static void pci_ite887x_exit(struct pci_dev *dev) +{ + u32 ioport; + /* the ioport is bit 0-15 in POSIO0R */ + pci_read_config_dword(dev, ITE_887x_POSIO0, &ioport); + ioport &= 0xffff; + release_region(ioport, ITE_887x_IOSIZE); +} + +/* + * Oxford Semiconductor Inc. + * Check if an OxSemi device is part of the Tornado range of devices. + */ +#define PCI_VENDOR_ID_ENDRUN 0x7401 +#define PCI_DEVICE_ID_ENDRUN_1588 0xe100 + +static bool pci_oxsemi_tornado_p(struct pci_dev *dev) +{ + /* OxSemi Tornado devices are all 0xCxxx */ + if (dev->vendor == PCI_VENDOR_ID_OXSEMI && + (dev->device & 0xf000) != 0xc000) + return false; + + /* EndRun devices are all 0xExxx */ + if (dev->vendor == PCI_VENDOR_ID_ENDRUN && + (dev->device & 0xf000) != 0xe000) + return false; + + return true; +} + +/* + * Determine the number of ports available on a Tornado device. + */ +static int pci_oxsemi_tornado_init(struct pci_dev *dev) +{ + u8 __iomem *p; + unsigned long deviceID; + unsigned int number_uarts = 0; + + if (!pci_oxsemi_tornado_p(dev)) + return 0; + + p = pci_iomap(dev, 0, 5); + if (p == NULL) + return -ENOMEM; + + deviceID = ioread32(p); + /* Tornado device */ + if (deviceID == 0x07000200) { + number_uarts = ioread8(p + 4); + pci_dbg(dev, "%d ports detected on %s PCI Express device\n", + number_uarts, + dev->vendor == PCI_VENDOR_ID_ENDRUN ? + "EndRun" : "Oxford"); + } + pci_iounmap(dev, p); + return number_uarts; +} + +/* Tornado-specific constants for the TCR and CPR registers; see below. */ +#define OXSEMI_TORNADO_TCR_MASK 0xf +#define OXSEMI_TORNADO_CPR_MASK 0x1ff +#define OXSEMI_TORNADO_CPR_MIN 0x008 +#define OXSEMI_TORNADO_CPR_DEF 0x10f + +/* + * Determine the oversampling rate, the clock prescaler, and the clock + * divisor for the requested baud rate. The clock rate is 62.5 MHz, + * which is four times the baud base, and the prescaler increments in + * steps of 1/8. Therefore to make calculations on integers we need + * to use a scaled clock rate, which is the baud base multiplied by 32 + * (or our assumed UART clock rate multiplied by 2). + * + * The allowed oversampling rates are from 4 up to 16 inclusive (values + * from 0 to 3 inclusive map to 16). Likewise the clock prescaler allows + * values between 1.000 and 63.875 inclusive (operation for values from + * 0.000 to 0.875 has not been specified). The clock divisor is the usual + * unsigned 16-bit integer. + * + * For the most accurate baud rate we use a table of predetermined + * oversampling rates and clock prescalers that records all possible + * products of the two parameters in the range from 4 up to 255 inclusive, + * and additionally 335 for the 1500000bps rate, with the prescaler scaled + * by 8. The table is sorted by the decreasing value of the oversampling + * rate and ties are resolved by sorting by the decreasing value of the + * product. This way preference is given to higher oversampling rates. + * + * We iterate over the table and choose the product of an oversampling + * rate and a clock prescaler that gives the lowest integer division + * result deviation, or if an exact integer divider is found we stop + * looking for it right away. We do some fixup if the resulting clock + * divisor required would be out of its unsigned 16-bit integer range. + * + * Finally we abuse the supposed fractional part returned to encode the + * 4-bit value of the oversampling rate and the 9-bit value of the clock + * prescaler which will end up in the TCR and CPR/CPR2 registers. + */ +static unsigned int pci_oxsemi_tornado_get_divisor(struct uart_port *port, + unsigned int baud, + unsigned int *frac) +{ + static u8 p[][2] = { + { 16, 14, }, { 16, 13, }, { 16, 12, }, { 16, 11, }, + { 16, 10, }, { 16, 9, }, { 16, 8, }, { 15, 17, }, + { 15, 16, }, { 15, 15, }, { 15, 14, }, { 15, 13, }, + { 15, 12, }, { 15, 11, }, { 15, 10, }, { 15, 9, }, + { 15, 8, }, { 14, 18, }, { 14, 17, }, { 14, 14, }, + { 14, 13, }, { 14, 12, }, { 14, 11, }, { 14, 10, }, + { 14, 9, }, { 14, 8, }, { 13, 19, }, { 13, 18, }, + { 13, 17, }, { 13, 13, }, { 13, 12, }, { 13, 11, }, + { 13, 10, }, { 13, 9, }, { 13, 8, }, { 12, 19, }, + { 12, 18, }, { 12, 17, }, { 12, 11, }, { 12, 9, }, + { 12, 8, }, { 11, 23, }, { 11, 22, }, { 11, 21, }, + { 11, 20, }, { 11, 19, }, { 11, 18, }, { 11, 17, }, + { 11, 11, }, { 11, 10, }, { 11, 9, }, { 11, 8, }, + { 10, 25, }, { 10, 23, }, { 10, 20, }, { 10, 19, }, + { 10, 17, }, { 10, 10, }, { 10, 9, }, { 10, 8, }, + { 9, 27, }, { 9, 23, }, { 9, 21, }, { 9, 19, }, + { 9, 18, }, { 9, 17, }, { 9, 9, }, { 9, 8, }, + { 8, 31, }, { 8, 29, }, { 8, 23, }, { 8, 19, }, + { 8, 17, }, { 8, 8, }, { 7, 35, }, { 7, 31, }, + { 7, 29, }, { 7, 25, }, { 7, 23, }, { 7, 21, }, + { 7, 19, }, { 7, 17, }, { 7, 15, }, { 7, 14, }, + { 7, 13, }, { 7, 12, }, { 7, 11, }, { 7, 10, }, + { 7, 9, }, { 7, 8, }, { 6, 41, }, { 6, 37, }, + { 6, 31, }, { 6, 29, }, { 6, 23, }, { 6, 19, }, + { 6, 17, }, { 6, 13, }, { 6, 11, }, { 6, 10, }, + { 6, 9, }, { 6, 8, }, { 5, 67, }, { 5, 47, }, + { 5, 43, }, { 5, 41, }, { 5, 37, }, { 5, 31, }, + { 5, 29, }, { 5, 25, }, { 5, 23, }, { 5, 19, }, + { 5, 17, }, { 5, 15, }, { 5, 13, }, { 5, 11, }, + { 5, 10, }, { 5, 9, }, { 5, 8, }, { 4, 61, }, + { 4, 59, }, { 4, 53, }, { 4, 47, }, { 4, 43, }, + { 4, 41, }, { 4, 37, }, { 4, 31, }, { 4, 29, }, + { 4, 23, }, { 4, 19, }, { 4, 17, }, { 4, 13, }, + { 4, 9, }, { 4, 8, }, + }; + /* Scale the quotient for comparison to get the fractional part. */ + const unsigned int quot_scale = 65536; + unsigned int sclk = port->uartclk * 2; + unsigned int sdiv = DIV_ROUND_CLOSEST(sclk, baud); + unsigned int best_squot; + unsigned int squot; + unsigned int quot; + u16 cpr; + u8 tcr; + int i; + + best_squot = quot_scale; + for (i = 0; i < ARRAY_SIZE(p); i++) { + unsigned int spre; + unsigned int srem; + u8 cp; + u8 tc; + + tc = p[i][0]; + cp = p[i][1]; + spre = tc * cp; + + srem = sdiv % spre; + if (srem > spre / 2) + srem = spre - srem; + squot = DIV_ROUND_CLOSEST(srem * quot_scale, spre); + + if (srem == 0) { + tcr = tc; + cpr = cp; + quot = sdiv / spre; + break; + } else if (squot < best_squot) { + best_squot = squot; + tcr = tc; + cpr = cp; + quot = DIV_ROUND_CLOSEST(sdiv, spre); + } + } + while (tcr <= (OXSEMI_TORNADO_TCR_MASK + 1) >> 1 && + quot % 2 == 0) { + quot >>= 1; + tcr <<= 1; + } + while (quot > UART_DIV_MAX) { + if (tcr <= (OXSEMI_TORNADO_TCR_MASK + 1) >> 1) { + quot >>= 1; + tcr <<= 1; + } else if (cpr <= OXSEMI_TORNADO_CPR_MASK >> 1) { + quot >>= 1; + cpr <<= 1; + } else { + quot = quot * cpr / OXSEMI_TORNADO_CPR_MASK; + cpr = OXSEMI_TORNADO_CPR_MASK; + } + } + + *frac = (cpr << 8) | (tcr & OXSEMI_TORNADO_TCR_MASK); + return quot; +} + +/* + * Set the oversampling rate in the transmitter clock cycle register (TCR), + * the clock prescaler in the clock prescaler register (CPR and CPR2), and + * the clock divisor in the divisor latch (DLL and DLM). Note that for + * backwards compatibility any write to CPR clears CPR2 and therefore CPR + * has to be written first, followed by CPR2, which occupies the location + * of CKS used with earlier UART designs. + */ +static void pci_oxsemi_tornado_set_divisor(struct uart_port *port, + unsigned int baud, + unsigned int quot, + unsigned int quot_frac) +{ + struct uart_8250_port *up = up_to_u8250p(port); + u8 cpr2 = quot_frac >> 16; + u8 cpr = quot_frac >> 8; + u8 tcr = quot_frac; + + serial_icr_write(up, UART_TCR, tcr); + serial_icr_write(up, UART_CPR, cpr); + serial_icr_write(up, UART_CKS, cpr2); + serial8250_do_set_divisor(port, baud, quot); +} + +/* + * For Tornado devices we force MCR[7] set for the Divide-by-M N/8 baud rate + * generator prescaler (CPR and CPR2). Otherwise no prescaler would be used. + */ +static void pci_oxsemi_tornado_set_mctrl(struct uart_port *port, + unsigned int mctrl) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + up->mcr |= UART_MCR_CLKSEL; + serial8250_do_set_mctrl(port, mctrl); +} + +/* + * We require EFR features for clock programming, so set UPF_FULL_PROBE + * for full probing regardless of CONFIG_SERIAL_8250_16550A_VARIANTS setting. + */ +static int pci_oxsemi_tornado_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *up, int idx) +{ + struct pci_dev *dev = priv->dev; + + if (pci_oxsemi_tornado_p(dev)) { + up->port.flags |= UPF_FULL_PROBE; + up->port.get_divisor = pci_oxsemi_tornado_get_divisor; + up->port.set_divisor = pci_oxsemi_tornado_set_divisor; + up->port.set_mctrl = pci_oxsemi_tornado_set_mctrl; + } + + return pci_default_setup(priv, board, up, idx); +} + +#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 + +/* Quatech devices have their own extra interface features */ +static struct pci_device_id quatech_cards[] = { + { PCI_DEVICE_DATA(QUATECH, QSC100, 1) }, + { PCI_DEVICE_DATA(QUATECH, DSC100, 1) }, + { PCI_DEVICE_DATA(QUATECH, DSC100E, 0) }, + { PCI_DEVICE_DATA(QUATECH, DSC200, 1) }, + { PCI_DEVICE_DATA(QUATECH, DSC200E, 0) }, + { PCI_DEVICE_DATA(QUATECH, ESC100D, 1) }, + { PCI_DEVICE_DATA(QUATECH, ESC100M, 1) }, + { PCI_DEVICE_DATA(QUATECH, QSCP100, 1) }, + { PCI_DEVICE_DATA(QUATECH, DSCP100, 1) }, + { PCI_DEVICE_DATA(QUATECH, QSCP200, 1) }, + { PCI_DEVICE_DATA(QUATECH, DSCP200, 1) }, + { PCI_DEVICE_DATA(QUATECH, ESCLP100, 0) }, + { PCI_DEVICE_DATA(QUATECH, QSCLP100, 0) }, + { PCI_DEVICE_DATA(QUATECH, DSCLP100, 0) }, + { PCI_DEVICE_DATA(QUATECH, SSCLP100, 0) }, + { PCI_DEVICE_DATA(QUATECH, QSCLP200, 0) }, + { PCI_DEVICE_DATA(QUATECH, DSCLP200, 0) }, + { PCI_DEVICE_DATA(QUATECH, SSCLP200, 0) }, + { PCI_DEVICE_DATA(QUATECH, SPPXP_100, 0) }, + { 0, } +}; + +static int pci_quatech_rqopr(struct uart_8250_port *port) +{ + unsigned long base = port->port.iobase; + u8 LCR, val; + + LCR = inb(base + UART_LCR); + outb(0xBF, base + UART_LCR); + val = inb(base + UART_SCR); + outb(LCR, base + UART_LCR); + return val; +} + +static void pci_quatech_wqopr(struct uart_8250_port *port, u8 qopr) +{ + unsigned long base = port->port.iobase; + u8 LCR; + + LCR = inb(base + UART_LCR); + outb(0xBF, base + UART_LCR); + inb(base + UART_SCR); + outb(qopr, base + UART_SCR); + outb(LCR, base + UART_LCR); +} + +static int pci_quatech_rqmcr(struct uart_8250_port *port) +{ + unsigned long base = port->port.iobase; + u8 LCR, val, qmcr; + + LCR = inb(base + UART_LCR); + outb(0xBF, base + UART_LCR); + val = inb(base + UART_SCR); + outb(val | 0x10, base + UART_SCR); + qmcr = inb(base + UART_MCR); + outb(val, base + UART_SCR); + outb(LCR, base + UART_LCR); + + return qmcr; +} + +static void pci_quatech_wqmcr(struct uart_8250_port *port, u8 qmcr) +{ + unsigned long base = port->port.iobase; + u8 LCR, val; + + LCR = inb(base + UART_LCR); + outb(0xBF, base + UART_LCR); + val = inb(base + UART_SCR); + outb(val | 0x10, base + UART_SCR); + outb(qmcr, base + UART_MCR); + outb(val, base + UART_SCR); + outb(LCR, base + UART_LCR); +} + +static int pci_quatech_has_qmcr(struct uart_8250_port *port) +{ + unsigned long base = port->port.iobase; + u8 LCR, val; + + LCR = inb(base + UART_LCR); + outb(0xBF, base + UART_LCR); + val = inb(base + UART_SCR); + if (val & 0x20) { + outb(0x80, UART_LCR); + if (!(inb(UART_SCR) & 0x20)) { + outb(LCR, base + UART_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) +{ + const struct pci_device_id *match; + bool amcc = false; + + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(dev); + + match = pci_match_id(quatech_cards, dev); + if (match) + amcc = match->driver_data; + else + pci_err(dev, "unknown port type '0x%04X'.\n", dev->device); + + if (amcc) { + 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) +{ + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(priv->dev); + + /* Needed by pci_quatech calls below */ + port->port.iobase = pci_resource_start(priv->dev, FL_GET_BASE(board->flags)); + /* Set up the clocking */ + port->port.uartclk = pci_quatech_clock(port); + /* For now just warn about RS422 */ + if (pci_quatech_rs422(port)) + pci_warn(priv->dev, "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 +ce4100_serial_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + int ret; + + ret = setup_port(priv, port, idx, 0, board->reg_shift); + port->port.iotype = UPIO_MEM32; + port->port.type = PORT_XSCALE; + port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); + port->port.regshift = 2; + + return ret; +} + +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) +{ + int ret = pci_default_setup(priv, board, port, idx); + + port->port.type = PORT_BRCM_TRUMANAGE; + port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); + return ret; +} + +/* 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) + +/* We should do proper H/W transceiver setting before change to RS485 mode */ +static int pci_fintek_rs485_config(struct uart_port *port, struct ktermios *termios, + struct serial_rs485 *rs485) +{ + struct pci_dev *pci_dev = to_pci_dev(port->dev); + u8 setting; + u8 *index = (u8 *) port->private_data; + + pci_read_config_byte(pci_dev, 0x40 + 8 * *index + 7, &setting); + + if (rs485->flags & SER_RS485_ENABLED) { + /* Enable RTS H/W control mode */ + setting |= FINTEK_RTS_CONTROL_BY_HW; + + if (rs485->flags & SER_RS485_RTS_ON_SEND) { + /* RTS driving high on TX */ + setting &= ~FINTEK_RTS_INVERT; + } else { + /* RTS driving low on TX */ + setting |= FINTEK_RTS_INVERT; + } + } else { + /* Disable RTS H/W control mode */ + setting &= ~(FINTEK_RTS_CONTROL_BY_HW | FINTEK_RTS_INVERT); + } + + pci_write_config_byte(pci_dev, 0x40 + 8 * *index + 7, setting); + + return 0; +} + +static const struct serial_rs485 pci_fintek_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, + /* F81504/508/512 does not support RTS delay before or after send */ +}; + +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 *data; + u8 config_base; + u16 iobase; + + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(pdev); + + config_base = 0x40 + 0x08 * idx; + + /* Get the io address from configuration space */ + pci_read_config_word(pdev, config_base + 4, &iobase); + + pci_dbg(pdev, "idx=%d iobase=0x%x", idx, iobase); + + port->port.iotype = UPIO_PORT; + port->port.iobase = iobase; + port->port.rs485_config = pci_fintek_rs485_config; + port->port.rs485_supported = pci_fintek_rs485_supported; + + data = devm_kzalloc(&pdev->dev, sizeof(u8), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* preserve index in PCI configuration space */ + *data = idx; + port->port.private_data = data; + + 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; + struct serial_private *priv = pci_get_drvdata(dev); + + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(dev); + + 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)); + + pci_write_config_byte(dev, config_base + 0x06, dev->irq); + + if (!priv) { + /* First init without port data + * force init to RS232 Mode + */ + pci_write_config_byte(dev, config_base + 0x07, 0x01); + } + } + + return max_port; +} + +static void f815xxa_mem_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ + struct f815xxa_data *data = p->private_data; + unsigned long flags; + + spin_lock_irqsave(&data->lock, flags); + writeb(value, p->membase + offset); + readb(p->membase + UART_SCR); /* Dummy read for flush pcie tx queue */ + spin_unlock_irqrestore(&data->lock, flags); +} + +static int pci_fintek_f815xxa_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + struct pci_dev *pdev = priv->dev; + struct f815xxa_data *data; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->idx = idx; + spin_lock_init(&data->lock); + + port->port.private_data = data; + port->port.iotype = UPIO_MEM; + port->port.flags |= UPF_IOREMAP; + port->port.mapbase = pci_resource_start(pdev, 0) + 8 * idx; + port->port.serial_out = f815xxa_mem_serial_out; + + return 0; +} + +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) +{ + port->port.quirks |= UPQ_NO_TXEN_TEST; + pci_dbg(priv->dev, + "serial8250: skipping TxEn test for device [%04x:%04x] subsystem [%04x:%04x]\n", + priv->dev->vendor, priv->dev->device, + priv->dev->subsystem_vendor, priv->dev->subsystem_device); + + return pci_default_setup(priv, board, port, idx); +} + +static void kt_handle_break(struct uart_port *p) +{ + struct uart_8250_port *up = up_to_u8250p(p); + /* + * On receipt of a BI, serial device in Intel ME (Intel + * management engine) needs to have its fifos cleared for sane + * SOL (Serial Over Lan) output. + */ + serial8250_clear_and_reinit_fifos(up); +} + +static u32 kt_serial_in(struct uart_port *p, unsigned int offset) +{ + struct uart_8250_port *up = up_to_u8250p(p); + u32 val; + + /* + * When the Intel ME (management engine) gets reset its serial + * port registers could return 0 momentarily. Functions like + * serial8250_console_write, read and save the IER, perform + * some operation and then restore it. In order to avoid + * setting IER register inadvertently to 0, if the value read + * is 0, double check with ier value in uart_8250_port and use + * that instead. up->ier should be the same value as what is + * currently configured. + */ + val = inb(p->iobase + offset); + if (offset == UART_IER) { + if (val == 0) + val = up->ier; + } + return val; +} + +static int kt_serial_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(priv->dev); + + port->port.flags |= UPF_BUG_THRE; + port->port.serial_in = kt_serial_in; + port->port.handle_break = kt_handle_break; + return skip_tx_en_setup(priv, board, port, idx); +} + +static int pci_eg20t_init(struct pci_dev *dev) +{ +#if defined(CONFIG_SERIAL_PCH_UART) || defined(CONFIG_SERIAL_PCH_UART_MODULE) + return -ENODEV; +#else + return 0; +#endif +} + +static int +pci_wch_ch353_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(priv->dev); + + port->port.flags |= UPF_FIXED_TYPE; + port->port.type = PORT_16550A; + return pci_default_setup(priv, board, port, idx); +} + +static int +pci_wch_ch355_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(priv->dev); + + port->port.flags |= UPF_FIXED_TYPE; + port->port.type = PORT_16550A; + return pci_default_setup(priv, board, port, idx); +} + +static int +pci_wch_ch38x_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(priv->dev); + + port->port.flags |= UPF_FIXED_TYPE; + port->port.type = PORT_16850; + return pci_default_setup(priv, board, port, idx); +} + + +#define CH384_XINT_ENABLE_REG 0xEB +#define CH384_XINT_ENABLE_BIT 0x02 + +static int pci_wch_ch38x_init(struct pci_dev *dev) +{ + int max_port; + unsigned long iobase; + + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(dev); + + switch (dev->device) { + case 0x3853: /* 8 ports */ + max_port = 8; + break; + default: + return -EINVAL; + } + + iobase = pci_resource_start(dev, 0); + outb(CH384_XINT_ENABLE_BIT, iobase + CH384_XINT_ENABLE_REG); + + return max_port; +} + +static void pci_wch_ch38x_exit(struct pci_dev *dev) +{ + unsigned long iobase; + + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) { + serial_8250_warn_need_ioport(dev); + return; + } + + iobase = pci_resource_start(dev, 0); + outb(0x0, iobase + CH384_XINT_ENABLE_REG); +} + + +static int +pci_sunix_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + int bar; + int offset; + + port->port.flags |= UPF_FIXED_TYPE; + port->port.type = PORT_SUNIX; + + if (idx < 4) { + bar = 0; + offset = idx * board->uart_offset; + } else { + bar = 1; + idx -= 4; + idx = div_s64_rem(idx, 4, &offset); + offset = idx * 64 + offset * board->uart_offset; + } + + return setup_port(priv, port, bar, offset, 0); +} + +#define SB_OPTR_IMR0 0x0c /* Interrupt mask register, p0 to p7 */ +static int pci_systembase_init(struct pci_dev *dev) +{ + resource_size_t iobase; + + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(dev); + + iobase = pci_resource_start(dev, 1); + + /* This will support up to 8 ports */ + outb(0xff, iobase + SB_OPTR_IMR0); + + return 0; +} + +static void pci_systembase_exit(struct pci_dev *dev) +{ + resource_size_t iobase; + + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) { + serial_8250_warn_need_ioport(dev); + return; + } + + iobase = pci_resource_start(dev, 0); + outb(0x00, iobase + SB_OPTR_IMR0); +} + +/* + * 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, + }, + /* + * HPE PCI serial device + */ + { + .vendor = PCI_VENDOR_ID_HP_3PAR, + .device = PCI_DEVICE_ID_HPE_PCI_SERIAL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .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, + .exit = pci_ni8420_exit, + }, + { + .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, + .exit = pci_ni8420_exit, + }, + { + .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, + .exit = pci_ni8420_exit, + }, + { + .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, + .exit = pci_ni8420_exit, + }, + { + .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, + .exit = pci_ni8420_exit, + }, + { + .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, + .exit = pci_ni8420_exit, + }, + { + .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, + .exit = pci_ni8420_exit, + }, + { + .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, + .exit = pci_ni8420_exit, + }, + { + .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, + .exit = pci_ni8420_exit, + }, + { + .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, + .exit = pci_ni8420_exit, + }, + { + .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, + .exit = pci_ni8420_exit, + }, + { + .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, + .exit = pci_ni8420_exit, + }, + { + .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, + .exit = pci_ni8430_exit, + }, + /* 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, + .exit = pci_plx9050_exit, + }, + { + .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, + .exit = pci_plx9050_exit, + }, + /* + * 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, + .exit = pci_plx9050_exit, + }, + { + .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, + .exit = pci_plx9050_exit, + }, + { + .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, + .exit = pci_plx9050_exit, + }, + /* + * 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, + .exit = sbs_exit, + }, + /* + * 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, + .exit = sbs_exit, + }, + /* + * 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, + .exit = sbs_exit, + }, + /* + * 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, + .exit = sbs_exit, + }, + /* + * 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, + }, + /* Systembase */ + { + .vendor = PCI_VENDOR_ID_SYSTEMBASE, + .device = 0x0008, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_systembase_init, + .setup = pci_default_setup, + .exit = pci_systembase_exit, + }, + /* + * 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, + }, + { + .vendor = PCIE_VENDOR_ID_ASIX, + .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_oxsemi_tornado_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_oxsemi_tornado_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_oxsemi_tornado_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_oxsemi_tornado_setup, + }, + /* + * Brainboxes devices - all Oxsemi based + */ + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x4027, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x4028, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x4029, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x4019, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x4016, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x4015, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x400A, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x400E, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x400C, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x400B, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x400F, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x4010, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x4011, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x401D, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x401E, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x4013, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x4017, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x4018, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x4026, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x4021, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8811, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8812, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8813, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x8814, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = 0x10DB, + .device = 0x8027, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = 0x10DB, + .device = 0x8028, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = 0x10DB, + .device = 0x8029, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = 0x10DB, + .device = 0x800C, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_eg20t_init, + .setup = pci_default_setup, + }, + { + .vendor = 0x10DB, + .device = 0x800D, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_eg20t_init, + .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_WCHCN, + .device = PCI_DEVICE_ID_WCHCN_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_WCHCN, + .device = PCI_DEVICE_ID_WCHCN_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_WCHCN, + .device = PCI_DEVICE_ID_WCHCN_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_WCHCN, + .device = PCI_DEVICE_ID_WCHCN_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_WCHCN, + .device = PCI_DEVICE_ID_WCHCN_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_WCHCN, + .device = PCI_DEVICE_ID_WCHCN_CH355_4S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch355_setup, + }, + /* WCH CH382 2S card (16850 clone) */ + { + .vendor = PCI_VENDOR_ID_WCHIC, + .device = PCI_DEVICE_ID_WCHIC_CH382_2S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch38x_setup, + }, + /* WCH CH382 2S1P card (16850 clone) */ + { + .vendor = PCI_VENDOR_ID_WCHIC, + .device = PCI_DEVICE_ID_WCHIC_CH382_2S1P, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch38x_setup, + }, + /* WCH CH384 4S card (16850 clone) */ + { + .vendor = PCI_VENDOR_ID_WCHIC, + .device = PCI_DEVICE_ID_WCHIC_CH384_4S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch38x_setup, + }, + /* WCH CH384 8S card (16850 clone) */ + { + .vendor = PCI_VENDOR_ID_WCHIC, + .device = PCI_DEVICE_ID_WCHIC_CH384_8S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_wch_ch38x_init, + .exit = pci_wch_ch38x_exit, + .setup = pci_wch_ch38x_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, + }, + { + .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_8_921600, + + pbn_b0_2_1130000, + + pbn_b0_4_1152000, + + pbn_b0_4_1250000, + + pbn_b0_2_1843200, + pbn_b0_4_1843200, + + pbn_b0_1_15625000, + + 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_oxsemi, + pbn_oxsemi_1_15625000, + pbn_oxsemi_2_15625000, + pbn_oxsemi_4_15625000, + pbn_oxsemi_8_15625000, + 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_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_wch384_8, + pbn_sunix_pci_1s, + pbn_sunix_pci_2s, + pbn_sunix_pci_4s, + pbn_sunix_pci_8s, + pbn_sunix_pci_16s, + pbn_titan_1_4000000, + pbn_titan_2_4000000, + pbn_titan_4_4000000, + pbn_titan_8_4000000, +}; + +/* + * 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_8_921600] = { + .flags = FL_BASE0, + .num_ports = 8, + .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_15625000] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 15625000, + .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, + }, + + /* + * 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_15625000] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 15625000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_oxsemi_2_15625000] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 15625000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_oxsemi_4_15625000] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 15625000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_oxsemi_8_15625000] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 15625000, + .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, + }, + /* + * 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, + }, + [pbn_wch384_8] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + .first_offset = 0x00, + }, + [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_titan_1_4000000] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_titan_2_4000000] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_titan_4_4000000] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_titan_8_4000000] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, +}; + +#define REPORT_CONFIG(option) \ + (IS_ENABLED(CONFIG_##option) ? 0 : (kernel_ulong_t)&#option) +#define REPORT_8250_CONFIG(option) \ + (IS_ENABLED(CONFIG_SERIAL_8250_##option) ? \ + 0 : (kernel_ulong_t)&"SERIAL_8250_"#option) + +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 */ + /* WCH CH353 2S1P */ + { PCI_VDEVICE(WCHCN, 0x7053), .driver_data = REPORT_CONFIG(PARPORT_SERIAL), }, + /* WCH CH353 1S1P */ + { PCI_VDEVICE(WCHCN, 0x5053), .driver_data = REPORT_CONFIG(PARPORT_SERIAL), }, + /* WCH CH382 2S1P */ + { PCI_VDEVICE(WCHIC, 0x3250), .driver_data = REPORT_CONFIG(PARPORT_SERIAL), }, + + /* Intel platforms with MID UART */ + { PCI_VDEVICE(INTEL, 0x081b), .driver_data = REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x081c), .driver_data = REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x081d), .driver_data = REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x1191), .driver_data = REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x18d8), .driver_data = REPORT_8250_CONFIG(MID), }, + { PCI_VDEVICE(INTEL, 0x19d8), .driver_data = REPORT_8250_CONFIG(MID), }, + + /* Intel platforms with DesignWare UART */ + { PCI_VDEVICE(INTEL, 0x0936), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x0f0a), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x0f0c), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x228a), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x228c), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b96), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b97), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b98), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b99), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b9a), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x4b9b), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x9ce3), .driver_data = REPORT_8250_CONFIG(LPSS), }, + { PCI_VDEVICE(INTEL, 0x9ce4), .driver_data = REPORT_8250_CONFIG(LPSS), }, + + /* Exar devices */ + { PCI_VDEVICE(EXAR, PCI_ANY_ID), .driver_data = REPORT_8250_CONFIG(EXAR), }, + { PCI_VDEVICE(COMMTECH, PCI_ANY_ID), .driver_data = REPORT_8250_CONFIG(EXAR), }, + + /* Pericom devices */ + { PCI_VDEVICE(PERICOM, PCI_ANY_ID), .driver_data = REPORT_8250_CONFIG(PERICOM), }, + { PCI_VDEVICE(ACCESSIO, PCI_ANY_ID), .driver_data = REPORT_8250_CONFIG(PERICOM), }, + + /* Moxa devices */ + { PCI_VDEVICE(MOXA, PCI_ANY_ID), REPORT_8250_CONFIG(MOXA), }, + + /* 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_STD_NUM_BARS; 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_STD_NUM_BARS; 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; +} + +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 = kzalloc_flex(*priv, line, nr_ports); + if (!priv) { + priv = ERR_PTR(-ENOMEM); + goto err_deinit; + } + + priv->dev = dev; + priv->quirk = quirk; + + memset(&uart, 0, sizeof(uart)); + uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; + uart.port.uartclk = board->base_baud * 16; + + if (board->flags & FL_NOIRQ) { + uart.port.irq = 0; + } else { + if (pci_match_id(pci_use_msi, dev)) { + pci_dbg(dev, "Using MSI(-X) interrupts\n"); + pci_set_master(dev); + uart.port.flags &= ~UPF_SHARE_IRQ; + rc = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_ALL_TYPES); + } else { + pci_dbg(dev, "Using legacy interrupts\n"); + rc = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_INTX); + } + if (rc < 0) { + kfree(priv); + priv = ERR_PTR(rc); + goto err_deinit; + } + + uart.port.irq = pci_irq_vector(dev, 0); + } + + uart.port.dev = &dev->dev; + + for (i = 0; i < nr_ports; i++) { + if (quirk->setup(priv, board, &uart, i)) + break; + + pci_dbg(dev, "Setup PCI port: port %lx, irq %d, type %d\n", + uart.port.iobase, uart.port.irq, uart.port.iotype); + + priv->line[i] = serial8250_register_8250_port(&uart); + if (priv->line[i] < 0) { + pci_err(dev, + "Couldn't register serial port %lx, irq %d, type %d, error %d\n", + uart.port.iobase, uart.port.irq, + uart.port.iotype, priv->line[i]); + break; + } + } + priv->nr = i; + priv->board = board; + return priv; + +err_deinit: + if (quirk->exit) + quirk->exit(dev); +err_out: + return priv; +} +EXPORT_SYMBOL_GPL(pciserial_init_ports); + +static void pciserial_detach_ports(struct serial_private *priv) +{ + struct pci_serial_quirk *quirk; + int i; + + for (i = 0; i < priv->nr; i++) + serial8250_unregister_port(priv->line[i]); + + /* + * Find the exit quirks. + */ + quirk = find_quirk(priv->dev); + if (quirk->exit) + quirk->exit(priv->dev); +} + +void pciserial_remove_ports(struct serial_private *priv) +{ + pciserial_detach_ports(priv); + kfree(priv); +} +EXPORT_SYMBOL_GPL(pciserial_remove_ports); + +void pciserial_suspend_ports(struct serial_private *priv) +{ + int i; + + for (i = 0; i < priv->nr; i++) + if (priv->line[i] >= 0) + serial8250_suspend_port(priv->line[i]); + + /* + * Ensure that every init quirk is properly torn down + */ + if (priv->quirk->exit) + priv->quirk->exit(priv->dev); +} +EXPORT_SYMBOL_GPL(pciserial_suspend_ports); + +void pciserial_resume_ports(struct serial_private *priv) +{ + int i; + + /* + * Ensure that the board is correctly configured. + */ + if (priv->quirk->init) + priv->quirk->init(priv->dev); + + for (i = 0; i < priv->nr; i++) + if (priv->line[i] >= 0) + serial8250_resume_port(priv->line[i]); +} +EXPORT_SYMBOL_GPL(pciserial_resume_ports); + +/* + * 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)) { + pci_err(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) { + if (exclude->driver_data) + pci_warn(dev, "ignoring port, enable %s to handle\n", + (const char *)exclude->driver_data); + return -ENODEV; + } + + rc = pcim_enable_device(dev); + pci_save_state(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)) + return PTR_ERR(priv); + + pci_set_drvdata(dev, priv); + return 0; +} + +static void pciserial_remove_one(struct pci_dev *dev) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + pciserial_remove_ports(priv); +} + +#ifdef CONFIG_PM_SLEEP +static int pciserial_suspend_one(struct device *dev) +{ + struct serial_private *priv = dev_get_drvdata(dev); + + if (priv) + pciserial_suspend_ports(priv); + + return 0; +} + +static int pciserial_resume_one(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct serial_private *priv = pci_get_drvdata(pdev); + int err; + + if (priv) { + /* + * The device may have been disabled. Re-enable it. + */ + err = pci_enable_device(pdev); + /* FIXME: We cannot simply error out here */ + if (err) + pci_err(pdev, "Unable to re-enable ports, trying to continue.\n"); + pciserial_resume_ports(priv); + } + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pciserial_pm_ops, pciserial_suspend_one, + pciserial_resume_one); + +static const struct pci_device_id serial_pci_tbl[] = { + { + PCI_VDEVICE_SUB(ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI1600, + PCI_DEVICE_ID_ADVANTECH_PCI1600_1611, PCI_ANY_ID), + .driver_data = pbn_b0_4_921600, + }, { + /* Advantech use PCI_DEVICE_ID_ADVANTECH_PCI3620 (0x3620) as 'PCI_SUBVENDOR_ID' */ + PCI_VDEVICE_SUB(ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620, + PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001), + .driver_data = pbn_b2_8_921600, + }, { + /* Advantech also use 0x3618 and 0xf618 */ + PCI_VDEVICE_SUB(ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3618, + PCI_DEVICE_ID_ADVANTECH_PCI3618, PCI_ANY_ID), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE_SUB(ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCIf618, + PCI_DEVICE_ID_ADVANTECH_PCI3618, PCI_ANY_ID), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232), + .driver_data = pbn_b1_8_1382400, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232), + .driver_data = pbn_b1_4_1382400, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232), + .driver_data = pbn_b1_2_1382400, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232), + .driver_data = pbn_b1_8_1382400, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232), + .driver_data = pbn_b1_4_1382400, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232), + .driver_data = pbn_b1_2_1382400, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485), + .driver_data = pbn_b1_8_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4), + .driver_data = pbn_b1_8_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485), + .driver_data = pbn_b1_4_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2), + .driver_data = pbn_b1_4_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485), + .driver_data = pbn_b1_2_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6), + .driver_data = pbn_b1_8_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1), + .driver_data = pbn_b1_8_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1), + .driver_data = pbn_b1_4_921600, + }, { + PCI_VDEVICE_SUB(V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ), + .driver_data = pbn_b1_2_1250000, + }, { + PCI_VDEVICE_SUB(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2), + .driver_data = pbn_b0_2_1843200, + }, { + PCI_VDEVICE_SUB(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4), + .driver_data = pbn_b0_4_1843200, + }, { + PCI_VDEVICE_SUB(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_VENDOR_ID_AFAVLAB, PCI_SUBDEVICE_ID_AFAVLAB_P061), + .driver_data = pbn_b0_4_1152000, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530), + .driver_data = pbn_b2_bt_1_115200, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2), + .driver_data = pbn_b2_bt_2_115200, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422), + .driver_data = pbn_b2_bt_4_115200, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232), + .driver_data = pbn_b2_bt_2_115200, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4), + .driver_data = pbn_b2_bt_4_115200, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8), + .driver_data = pbn_b2_8_115200, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_7803), + .driver_data = pbn_b2_8_460800, + }, { + PCI_VDEVICE(SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM8), + .driver_data = pbn_b2_8_115200, + }, { + PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2), + .driver_data = pbn_b2_bt_2_115200, + }, { + PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_SPCOM200), + .driver_data = pbn_b2_bt_2_921600, + }, { + /* VScom SPCOM800, from sl@s.pl */ + PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_SPCOM800), + .driver_data = pbn_b2_8_921600, + }, { + PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_1077), + .driver_data = pbn_b2_4_921600, + }, { + /* Unknown card - subdevice 0x1584 */ + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, PCI_SUBDEVICE_ID_UNKNOWN_0x1584), + .driver_data = pbn_b2_4_115200, + }, { + /* Unknown card - subdevice 0x1588 */ + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, PCI_SUBDEVICE_ID_UNKNOWN_0x1588), + .driver_data = pbn_b2_8_115200, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_KEYSPAN, PCI_SUBDEVICE_ID_KEYSPAN_SX2), + .driver_data = pbn_panacom, + }, { + PCI_VDEVICE(PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM), + .driver_data = pbn_panacom4, + }, { + PCI_VDEVICE(PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM), + .driver_data = pbn_panacom2, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9030, + PCI_VENDOR_ID_ESDGMBH, PCI_DEVICE_ID_ESDGMBH_CPCIASIO4), + .driver_data = pbn_b2_4_115200, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, PCI_SUBDEVICE_ID_CHASE_PCIFAST4), + .driver_data = pbn_b2_4_460800, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, PCI_SUBDEVICE_ID_CHASE_PCIFAST8), + .driver_data = pbn_b2_8_460800, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, PCI_SUBDEVICE_ID_CHASE_PCIFAST16), + .driver_data = pbn_b2_16_460800, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC), + .driver_data = pbn_b2_16_460800, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, PCI_SUBDEVICE_ID_CHASE_PCIRAS4), + .driver_data = pbn_b2_4_460800, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, PCI_SUBDEVICE_ID_CHASE_PCIRAS8), + .driver_data = pbn_b2_8_460800, + }, { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4055), + .driver_data = pbn_b2_4_115200, + }, { + /* + * Megawolf Romulus PCI Serial Card, from Mike Hudson + * (Exoray@isys.ca) + */ + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_ROMULUS, + 0x10b5, 0x106a), + .driver_data = pbn_plx_romulus, + }, + + /* + * Quatech cards. These actually have configurable clocks but for + * now we just use the default. + * + * 100 series are RS232, 200 series RS422, + */ + { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_QSC100), + .driver_data = pbn_b1_4_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSC100), + .driver_data = pbn_b1_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSC100E), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSC200), + .driver_data = pbn_b1_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSC200E), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_QSC200), + .driver_data = pbn_b1_4_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D), + .driver_data = pbn_b1_8_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M), + .driver_data = pbn_b1_8_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_QSCP100), + .driver_data = pbn_b1_4_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSCP100), + .driver_data = pbn_b1_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_QSCP200), + .driver_data = pbn_b1_4_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSCP200), + .driver_data = pbn_b1_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP100), + .driver_data = pbn_b2_4_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP100), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP100), + .driver_data = pbn_b2_1_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP200), + .driver_data = pbn_b2_4_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP200), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP200), + .driver_data = pbn_b2_1_115200, + }, { + PCI_VDEVICE(QUATECH, PCI_DEVICE_ID_QUATECH_ESCLP100), + .driver_data = pbn_b0_8_115200, + }, { + PCI_VDEVICE_SUB(SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE_SUB(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL), + .driver_data = pbn_b0_4_1152000, + }, { + PCI_VDEVICE(OXSEMI, 0x9505), + .driver_data = 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_VDEVICE_SUB(OXSEMI, 0x950a, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_00), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE_SUB(OXSEMI, 0x950a, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_30), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(OXSEMI, 0x950a), + .driver_data = pbn_b0_2_1130000, + }, { + PCI_VDEVICE_SUB(OXSEMI, PCI_DEVICE_ID_OXSEMI_C950, + PCI_VENDOR_ID_OXSEMI, PCI_SUBDEVICE_ID_OXSEMI_C950), + .driver_data = pbn_b0_1_921600, + }, { + PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952), + .driver_data = pbn_b0_bt_2_921600, + }, { + PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI958), + .driver_data = pbn_b2_8_1152000, + }, + + /* + * Oxford Semiconductor Inc. Tornado PCI express device range. + */ + { + PCI_VDEVICE(OXSEMI, 0xc101), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc105), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc11b), /* OXPCIe952 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc11f), /* OXPCIe952 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc120), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc124), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc138), /* OXPCIe952 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc13d), /* OXPCIe952 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc140), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc141), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc144), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc145), /* OXPCIe952 1 Legacy UART */ + .driver_data = pbn_b0_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc158), /* OXPCIe952 2 Native UART */ + .driver_data = pbn_oxsemi_2_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc15d), /* OXPCIe952 2 Native UART */ + .driver_data = pbn_oxsemi_2_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc208), /* OXPCIe954 4 Native UART */ + .driver_data = pbn_oxsemi_4_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc20d), /* OXPCIe954 4 Native UART */ + .driver_data = pbn_oxsemi_4_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc308), /* OXPCIe958 8 Native UART */ + .driver_data = pbn_oxsemi_8_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc30d), /* OXPCIe958 8 Native UART */ + .driver_data = pbn_oxsemi_8_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc40b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc40f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc41b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc41f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc42b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc42f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc43b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc43f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc44b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc44f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc45b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc45f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc46b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc46f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc47b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc47f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc48b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc48f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc49b), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc49f), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc4ab), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc4af), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc4bb), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc4bf), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc4cb), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, { + PCI_VDEVICE(OXSEMI, 0xc4cf), /* OXPCIe200 1 Native UART */ + .driver_data = pbn_oxsemi_1_15625000, + }, + /* + * Mainpine Inc. IQ Express "Rev3" utilizing OxSemi Tornado + */ + { + /* IQ Express 1 Port V.34 Super-G3 Fax */ + PCI_VDEVICE_SUB(MAINPINE, 0x4000, + PCI_VENDOR_ID_MAINPINE, 0x4001), + .driver_data = pbn_oxsemi_1_15625000, + }, { + /* IQ Express 2 Port V.34 Super-G3 Fax */ + PCI_VDEVICE_SUB(MAINPINE, 0x4000, + PCI_VENDOR_ID_MAINPINE, 0x4002), + .driver_data = pbn_oxsemi_2_15625000, + }, { + /* IQ Express 4 Port V.34 Super-G3 Fax */ + PCI_VDEVICE_SUB(MAINPINE, 0x4000, + PCI_VENDOR_ID_MAINPINE, 0x4004), + .driver_data = pbn_oxsemi_4_15625000, + }, { + /* IQ Express 8 Port V.34 Super-G3 Fax */ + PCI_VDEVICE_SUB(MAINPINE, 0x4000, + PCI_VENDOR_ID_MAINPINE, 0x4008), + .driver_data = pbn_oxsemi_8_15625000, + }, + + /* + * Digi/IBM PCIe 2-port Async EIA-232 Adapter utilizing OxSemi Tornado + */ + { + PCI_VDEVICE_SUB(DIGI, PCIE_DEVICE_ID_NEO_2_OX_IBM, + PCI_SUBVENDOR_ID_IBM, PCI_ANY_ID), + .driver_data = pbn_oxsemi_2_15625000, + }, + + /* + * EndRun Technologies. PCI express device range. + * EndRun PTP/1588 has 2 Native UARTs utilizing OxSemi 952. + */ + { + PCI_VDEVICE(ENDRUN, PCI_DEVICE_ID_ENDRUN_1588), + .driver_data = pbn_oxsemi_2_15625000, + }, + + /* + * SBS Technologies, Inc. P-Octal and PMC-OCTPRO cards, + * from skokodyn@yahoo.com + */ + { + PCI_VDEVICE_SUB(SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO232), + .driver_data = pbn_sbsxrsio, + }, { + PCI_VDEVICE_SUB(SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO422), + .driver_data = pbn_sbsxrsio, + }, { + PCI_VDEVICE_SUB(SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL232), + .driver_data = pbn_sbsxrsio, + }, { + PCI_VDEVICE_SUB(SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL422), + .driver_data = pbn_sbsxrsio, + }, + + /* + * Digitan DS560-558, from jimd@esoft.com + */ + { + PCI_VDEVICE(ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM), + .driver_data = pbn_b1_1_115200, + }, + + /* + * Titan Electronic cards + * The 400L and 800L have a custom setup quirk. + */ + { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_100), + .driver_data = pbn_b0_1_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_200), + .driver_data = pbn_b0_2_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_400), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800B), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_100L), + .driver_data = pbn_b1_1_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_200L), + .driver_data = pbn_b1_bt_2_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_400L), + .driver_data = pbn_b0_bt_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800L), + .driver_data = pbn_b0_bt_8_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_200I), + .driver_data = pbn_b4_bt_2_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_400I), + .driver_data = pbn_b4_bt_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800I), + .driver_data = pbn_b4_bt_8_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_400EH), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800EH), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800EHB), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_100E), + .driver_data = pbn_titan_1_4000000, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_200E), + .driver_data = pbn_titan_2_4000000, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_400E), + .driver_data = pbn_titan_4_4000000, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800E), + .driver_data = pbn_titan_8_4000000, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_200EI), + .driver_data = pbn_titan_2_4000000, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_200EISI), + .driver_data = pbn_titan_2_4000000, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_200V3), + .driver_data = pbn_b0_bt_2_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_400V3), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_410V3), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800V3), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(TITAN, PCI_DEVICE_ID_TITAN_800V3B), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550), + .driver_data = pbn_b2_1_460800, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650), + .driver_data = pbn_b2_1_460800, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850), + .driver_data = pbn_b2_1_460800, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550), + .driver_data = pbn_b2_bt_2_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650), + .driver_data = pbn_b2_bt_2_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850), + .driver_data = pbn_b2_bt_2_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550), + .driver_data = pbn_b2_bt_4_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650), + .driver_data = pbn_b2_bt_4_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850), + .driver_data = pbn_b2_bt_4_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550), + .driver_data = pbn_b0_1_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650), + .driver_data = pbn_b0_1_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850), + .driver_data = pbn_b0_1_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550), + .driver_data = pbn_b0_bt_2_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650), + .driver_data = pbn_b0_bt_2_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850), + .driver_data = pbn_b0_bt_2_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550), + .driver_data = pbn_b0_bt_4_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650), + .driver_data = pbn_b0_bt_4_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850), + .driver_data = pbn_b0_bt_4_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_8S_20x_550), + .driver_data = pbn_b0_bt_8_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_8S_20x_650), + .driver_data = pbn_b0_bt_8_921600, + }, { + PCI_VDEVICE(SIIG, PCI_DEVICE_ID_SIIG_8S_20x_850), + .driver_data = pbn_b0_bt_8_921600, + }, + + /* + * Computone devices submitted by Doug McNash dmcnash@computone.com + */ + { + PCI_VDEVICE_SUB(COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4), + .driver_data = pbn_computone_4, + }, { + PCI_VDEVICE_SUB(COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8), + .driver_data = pbn_computone_8, + }, { + PCI_VDEVICE_SUB(COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6), + .driver_data = pbn_computone_6, + }, + + { + PCI_VDEVICE(OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N), + .driver_data = pbn_oxsemi, + }, { + PCI_VDEVICE_SUB(TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, + PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID), + .driver_data = pbn_b0_bt_1_921600, + }, + + /* + * Sunix PCI serial boards + */ + { + PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0001), + .driver_data = pbn_sunix_pci_1s, + }, { + PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0002), + .driver_data = pbn_sunix_pci_2s, + }, { + PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0004), + .driver_data = pbn_sunix_pci_4s, + }, { + PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0084), + .driver_data = pbn_sunix_pci_4s, + }, { + PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0008), + .driver_data = pbn_sunix_pci_8s, + }, { + PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0088), + .driver_data = pbn_sunix_pci_8s, + }, { + PCI_VDEVICE_SUB(SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, 0x0010), + .driver_data = pbn_sunix_pci_16s, + }, + + /* + * AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org> + */ + { + PCI_VDEVICE(AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028), + .driver_data = pbn_b0_bt_8_115200, + }, { + PCI_VDEVICE(AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P030), + .driver_data = pbn_b0_bt_8_115200, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_DSERIAL), + .driver_data = pbn_b0_bt_2_115200, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A), + .driver_data = pbn_b0_bt_2_115200, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B), + .driver_data = pbn_b0_bt_2_115200, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_OCTO_A), + .driver_data = pbn_b0_bt_4_460800, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_OCTO_B), + .driver_data = pbn_b0_bt_4_460800, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS), + .driver_data = pbn_b0_bt_2_460800, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_QUAD_A), + .driver_data = pbn_b0_bt_2_460800, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_QUAD_B), + .driver_data = pbn_b0_bt_2_460800, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_SSERIAL), + .driver_data = pbn_b0_bt_1_115200, + }, { + PCI_VDEVICE(LAVA, PCI_DEVICE_ID_LAVA_PORT_650), + .driver_data = 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_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1204, 0x0004), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1208, 0x0004), + .driver_data = pbn_b0_4_921600, + }, +/* { + PCI_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1402, 0x0002), + .driver_data = pbn_b0_2_921600, + }, */ +/* { + PCI_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1404, 0x0004), + .driver_data = pbn_b0_4_921600, + }, */ + { + PCI_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF1, + 0x1208, 0x0004), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, + 0x1204, 0x0004), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, + 0x1208, 0x0004), + .driver_data = pbn_b0_4_921600, + }, { + PCI_VDEVICE_SUB(KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF3, + 0x1208, 0x0004), + .driver_data = pbn_b0_4_921600, + }, + /* + * Dell Remote Access Card 4 - Tim_T_Murphy@Dell.com + */ + { + PCI_VDEVICE(DELL, PCI_DEVICE_ID_DELL_RAC4), + .driver_data = pbn_b1_1_1382400, + }, + + /* + * Dell Remote Access Card III - Tim_T_Murphy@Dell.com + */ + { + PCI_VDEVICE(DELL, PCI_DEVICE_ID_DELL_RACIII), + .driver_data = pbn_b1_1_1382400, + }, + + /* + * RAStel 2 port modem, gerg@moreton.com.au + */ + { + PCI_VDEVICE(MORETON, PCI_DEVICE_ID_RASTEL_2PORT), + .driver_data = pbn_b2_bt_2_115200, + }, + + /* + * EKF addition for i960 Boards form EKF with serial port + */ + { + PCI_VDEVICE_SUB(INTEL, PCI_DEVICE_ID_INTEL_80960_RP, + 0xE4BF, PCI_ANY_ID), + .driver_data = pbn_intel_i960, + }, + + /* + * Xircom Cardbus/Ethernet combos + */ + { + PCI_VDEVICE(XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM), + .driver_data = pbn_b0_1_115200, + }, + /* + * Xircom RBM56G cardbus modem - Dirk Arnold (temp entry) + */ + { + PCI_VDEVICE(XIRCOM, PCI_DEVICE_ID_XIRCOM_RBM56G), + .driver_data = pbn_b0_1_115200, + }, + + /* + * Untested PCI modems, sent in from various folks... + */ + + { + /* Elsa Model 56K PCI Modem, from Andreas Rath <arh@01019freenet.de> */ + PCI_VDEVICE_SUB(ROCKWELL, 0x1004, 0x1048, 0x1500), + .driver_data = pbn_b1_1_115200, + }, { + PCI_VDEVICE_SUB(SGI, PCI_DEVICE_ID_SGI_IOC3, 0xFF00, 0), + .driver_data = pbn_sgi_ioc3, + }, + + /* + * HP Diva card + */ + { + PCI_VDEVICE_SUB(HP, PCI_DEVICE_ID_HP_DIVA, + PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_RMP3), + .driver_data = pbn_b1_1_115200, + }, { + PCI_VDEVICE(HP, PCI_DEVICE_ID_HP_DIVA), + .driver_data = pbn_b0_5_115200, + }, { + PCI_VDEVICE(HP, PCI_DEVICE_ID_HP_DIVA_AUX), + .driver_data = pbn_b2_1_115200, + }, { + /* HPE PCI serial device */ + PCI_VDEVICE(HP_3PAR, PCI_DEVICE_ID_HPE_PCI_SERIAL), + .driver_data = pbn_b1_1_115200, + }, { + PCI_VDEVICE(DCI, PCI_DEVICE_ID_DCI_PCCOM2), + .driver_data = pbn_b3_2_115200, + }, { + PCI_VDEVICE(DCI, PCI_DEVICE_ID_DCI_PCCOM4), + .driver_data = pbn_b3_4_115200, + }, { + PCI_VDEVICE(DCI, PCI_DEVICE_ID_DCI_PCCOM8), + .driver_data = pbn_b3_8_115200, + }, { + /* Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke) */ + PCI_VDEVICE(TOPIC, PCI_DEVICE_ID_TOPIC_TP560), + .driver_data = pbn_b0_1_115200, + }, { + /* ITE */ + PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8872), + .driver_data = pbn_b1_bt_1_115200, + }, { + /* IntaShield IS-100 */ + PCI_VDEVICE(INTASHIELD, 0x0D60), + .driver_data = pbn_b2_1_115200, + }, { + /* IntaShield IS-200; 135a.0d80 */ + PCI_VDEVICE(INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS200), + .driver_data = pbn_b2_2_115200, + }, { + /* IntaShield IS-400; 135a.0dc0 */ + PCI_VDEVICE(INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS400), + .driver_data = pbn_b2_4_115200, + }, { + /* IntaShield IX-100 */ + PCI_VDEVICE(INTASHIELD, 0x4027), + .driver_data = pbn_oxsemi_1_15625000, + }, { + /* IntaShield IX-200 */ + PCI_VDEVICE(INTASHIELD, 0x4028), + .driver_data = pbn_oxsemi_2_15625000, + }, { + /* IntaShield IX-400 */ + PCI_VDEVICE(INTASHIELD, 0x4029), + .driver_data = pbn_oxsemi_4_15625000, + }, + /* Brainboxes Devices */ + /* + * Brainboxes UC-101 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0BA1), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0BA2), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0BA3), + .driver_data = pbn_b2_2_115200, + }, + /* + * Brainboxes UC-235/246 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0AA1), + .driver_data = pbn_b2_1_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0AA2), + .driver_data = pbn_b2_1_115200, + }, + /* + * Brainboxes UC-253/UC-734 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0CA1), + .driver_data = pbn_b2_2_115200, + }, + /* + * Brainboxes UC-260/271/701/756 + */ + { + PCI_DEVICE(PCI_VENDOR_ID_INTASHIELD, 0x0D21), + .class = PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, + .class_mask = 0xffff00, + .driver_data = pbn_b2_4_115200, + }, { + PCI_DEVICE(PCI_VENDOR_ID_INTASHIELD, 0x0E34), + .class = PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, + .class_mask = 0xffff00, + .driver_data = pbn_b2_4_115200, + }, + /* + * Brainboxes UC-268 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0841), + .driver_data = pbn_b2_4_115200, + }, + /* + * Brainboxes UC-275/279 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0881), + .driver_data = pbn_b2_8_115200, + }, + /* + * Brainboxes UC-302 + */ + { + PCI_VDEVICE(INTASHIELD, 0x08E1), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x08E2), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x08E3), + .driver_data = pbn_b2_2_115200, + }, + /* + * Brainboxes UC-310 + */ + { + PCI_VDEVICE(INTASHIELD, 0x08C1), + .driver_data = pbn_b2_2_115200, + }, + /* + * Brainboxes UC-313 + */ + { + PCI_VDEVICE(INTASHIELD, 0x08A1), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x08A2), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x08A3), + .driver_data = pbn_b2_2_115200, + }, + /* + * Brainboxes UC-320/324 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0A61), + .driver_data = pbn_b2_1_115200, + }, + /* + * Brainboxes UC-346 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0B01), + .driver_data = pbn_b2_4_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0B02), + .driver_data = pbn_b2_4_115200, + }, + /* + * Brainboxes UC-357 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0A81), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0A82), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0A83), + .driver_data = pbn_b2_2_115200, + }, + /* + * Brainboxes UC-368 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0C41), + .driver_data = pbn_b2_4_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0C42), + .driver_data = pbn_b2_4_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0C43), + .driver_data = pbn_b2_4_115200, + }, + /* + * Brainboxes UC-420 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0921), + .driver_data = pbn_b2_4_115200, + }, + /* + * Brainboxes UC-607 + */ + { + PCI_VDEVICE(INTASHIELD, 0x09A1), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x09A2), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x09A3), + .driver_data = pbn_b2_2_115200, + }, + /* + * Brainboxes UC-836 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0D41), + .driver_data = pbn_b2_4_115200, + }, + /* + * Brainboxes UP-189 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0AC1), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0AC2), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0AC3), + .driver_data = pbn_b2_2_115200, + }, + /* + * Brainboxes UP-200 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0B21), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0B22), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0B23), + .driver_data = pbn_b2_2_115200, + }, + /* + * Brainboxes UP-869 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0C01), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0C02), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0C03), + .driver_data = pbn_b2_2_115200, + }, + /* + * Brainboxes UP-880 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0C21), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0C22), + .driver_data = pbn_b2_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x0C23), + .driver_data = pbn_b2_2_115200, + }, + /* + * Brainboxes PX-101 + */ + { + PCI_VDEVICE(INTASHIELD, 0x4005), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x4019), + .driver_data = pbn_oxsemi_2_15625000, + }, + /* + * Brainboxes PX-235/246 + */ + { + PCI_VDEVICE(INTASHIELD, 0x4004), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x4016), + .driver_data = pbn_oxsemi_1_15625000, + }, + /* + * Brainboxes PX-203/PX-257 + */ + { + PCI_VDEVICE(INTASHIELD, 0x4006), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x4015), + .driver_data = pbn_oxsemi_2_15625000, + }, + /* + * Brainboxes PX-260/PX-701 + */ + { + PCI_VDEVICE(INTASHIELD, 0x400A), + .driver_data = pbn_oxsemi_4_15625000, + }, + /* + * Brainboxes PX-275/279 + */ + { + PCI_VDEVICE(INTASHIELD, 0x0E41), + .driver_data = pbn_b2_8_115200, + }, + /* + * Brainboxes PX-310 + */ + { + PCI_VDEVICE(INTASHIELD, 0x400E), + .driver_data = pbn_oxsemi_2_15625000, + }, + /* + * Brainboxes PX-313 + */ + { + PCI_VDEVICE(INTASHIELD, 0x400C), + .driver_data = pbn_oxsemi_2_15625000, + }, + /* + * Brainboxes PX-320/324/PX-376/PX-387 + */ + { + PCI_VDEVICE(INTASHIELD, 0x400B), + .driver_data = pbn_oxsemi_1_15625000, + }, + /* + * Brainboxes PX-335/346 + */ + { + PCI_VDEVICE(INTASHIELD, 0x400F), + .driver_data = pbn_oxsemi_4_15625000, + }, + /* + * Brainboxes PX-368 + */ + { + PCI_VDEVICE(INTASHIELD, 0x4010), + .driver_data = pbn_oxsemi_4_15625000, + }, + /* + * Brainboxes PX-420 + */ + { + PCI_VDEVICE(INTASHIELD, 0x4000), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x4011), + .driver_data = pbn_oxsemi_4_15625000, + }, + /* + * Brainboxes PX-475 + */ + { + PCI_VDEVICE(INTASHIELD, 0x401D), + .driver_data = pbn_oxsemi_1_15625000, + }, + /* + * Brainboxes PX-803/PX-857 + */ + { + PCI_VDEVICE(INTASHIELD, 0x4009), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x4018), + .driver_data = pbn_oxsemi_2_15625000, + }, { + PCI_VDEVICE(INTASHIELD, 0x401E), + .driver_data = pbn_oxsemi_2_15625000, + }, + /* + * Brainboxes PX-820 + */ + { + PCI_VDEVICE(INTASHIELD, 0x4002), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x4013), + .driver_data = pbn_oxsemi_4_15625000, + }, + /* + * Brainboxes PX-835/PX-846 + */ + { + PCI_VDEVICE(INTASHIELD, 0x4008), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE(INTASHIELD, 0x4017), + .driver_data = pbn_oxsemi_1_15625000, + }, + /* + * Brainboxes XC-235 + */ + { + PCI_VDEVICE(INTASHIELD, 0x4026), + .driver_data = pbn_oxsemi_1_15625000, + }, + /* + * Brainboxes XC-475 + */ + { + PCI_VDEVICE(INTASHIELD, 0x4021), + .driver_data = pbn_oxsemi_1_15625000, + }, + + /* + * Perle PCI-RAS cards + */ + { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9030, + PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS4), + .driver_data = pbn_b2_4_921600, + }, + { + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9030, + PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS8), + .driver_data = 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_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0200), + .driver_data = pbn_b0_2_115200, + }, { + /* RockForceQUATRO */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0300), + .driver_data = pbn_b0_4_115200, + }, { + /* RockForceDUO+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0400), + .driver_data = pbn_b0_2_115200, + }, { + /* RockForceQUATRO+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0500), + .driver_data = pbn_b0_4_115200, + }, { + /* RockForce+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0600), + .driver_data = pbn_b0_2_115200, + }, { + /* RockForce+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0700), + .driver_data = pbn_b0_4_115200, + }, { + /* RockForceOCTO+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0800), + .driver_data = pbn_b0_8_115200, + }, { + /* RockForceDUO+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0C00), + .driver_data = pbn_b0_2_115200, + }, { + /* RockForceQUARTRO+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0D00), + .driver_data = pbn_b0_4_115200, + }, { + /* RockForceOCTO+ */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x1D00), + .driver_data = pbn_b0_8_115200, + }, { + /* RockForceD1 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2000), + .driver_data = pbn_b0_1_115200, + }, { + /* RockForceF1 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2100), + .driver_data = pbn_b0_1_115200, + }, { /* RockForceD2 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2200), + .driver_data = pbn_b0_2_115200, + }, { /* RockForceF2 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2300), + .driver_data = pbn_b0_2_115200, + }, { + /* RockForceD4 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2400), + .driver_data = pbn_b0_4_115200, + }, { + /* RockForceF4 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2500), + .driver_data = pbn_b0_4_115200, + }, { + /* RockForceD8 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2600), + .driver_data = pbn_b0_8_115200, + }, { + /* RockForceF8 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2700), + .driver_data = pbn_b0_8_115200, + }, { + /* IQ Express D1 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3000), + .driver_data = pbn_b0_1_115200, + }, { + /* IQ Express F1 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3100), + .driver_data = pbn_b0_1_115200, + }, { + /* IQ Express D2 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3200), + .driver_data = pbn_b0_2_115200, + }, { + /* IQ Express F2 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3300), + .driver_data = pbn_b0_2_115200, + }, { + /* IQ Express D4 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3400), + .driver_data = pbn_b0_4_115200, + }, { + /* IQ Express F4 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3500), + .driver_data = pbn_b0_4_115200, + }, { + /* IQ Express D8 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3C00), + .driver_data = pbn_b0_8_115200, + }, { + /* IQ Express F8 */ + PCI_VDEVICE_SUB(MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3D00), + .driver_data = pbn_b0_8_115200, + }, + + { + /* PA Semi PA6T-1682M on-chip UART */ + PCI_VDEVICE(PASEMI, 0xa004), + .driver_data = pbn_pasemi_1682M, + }, + + /* + * National Instruments + */ + { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI23216), + .driver_data = pbn_b1_16_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI2328), + .driver_data = pbn_b1_8_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI2324), + .driver_data = pbn_b1_bt_4_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI2322), + .driver_data = pbn_b1_bt_2_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI2324I), + .driver_data = pbn_b1_bt_4_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI2322I), + .driver_data = pbn_b1_bt_2_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8420_23216), + .driver_data = pbn_b1_16_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8420_2328), + .driver_data = pbn_b1_8_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8420_2324), + .driver_data = pbn_b1_bt_4_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8420_2322), + .driver_data = pbn_b1_bt_2_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8422_2324), + .driver_data = pbn_b1_bt_4_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8422_2322), + .driver_data = pbn_b1_bt_2_115200, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8430_2322), + .driver_data = pbn_ni8430_2, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI8430_2322), + .driver_data = pbn_ni8430_2, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8430_2324), + .driver_data = pbn_ni8430_4, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI8430_2324), + .driver_data = pbn_ni8430_4, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8430_2328), + .driver_data = pbn_ni8430_8, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI8430_2328), + .driver_data = pbn_ni8430_8, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8430_23216), + .driver_data = pbn_ni8430_16, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI8430_23216), + .driver_data = pbn_ni8430_16, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8432_2322), + .driver_data = pbn_ni8430_2, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI8432_2322), + .driver_data = pbn_ni8430_2, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PXI8432_2324), + .driver_data = pbn_ni8430_4, + }, { + PCI_VDEVICE(NI, PCI_DEVICE_ID_NI_PCI8432_2324), + .driver_data = pbn_ni8430_4, + }, + + /* + * ADDI-DATA GmbH communication cards <info@addi-data.com> + */ + { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7500), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7420), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7300), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE(AMCC, PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800), + .driver_data = pbn_b1_8_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7500_2), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7420_2), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7300_2), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7500_3), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7420_3), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7300_3), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCI7800_3), + .driver_data = pbn_b0_8_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCIe7500), + .driver_data = pbn_ADDIDATA_PCIe_4_3906250, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCIe7420), + .driver_data = pbn_ADDIDATA_PCIe_2_3906250, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCIe7300), + .driver_data = pbn_ADDIDATA_PCIe_1_3906250, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_APCIe7800), + .driver_data = pbn_ADDIDATA_PCIe_8_3906250, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_CPCI7500), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_CPCI7500_NG), + .driver_data = pbn_b0_4_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_CPCI7420_NG), + .driver_data = pbn_b0_2_115200, + }, { + PCI_VDEVICE(ADDIDATA, PCI_DEVICE_ID_ADDIDATA_CPCI7300_NG), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9835, + PCI_VENDOR_ID_IBM, 0x0299), + .driver_data = 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_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9901, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, { + /* the 9901 is a rebranded 9912 */ + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9912, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9922, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9904, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x3002), + .driver_data = pbn_NETMOS9900_2s_115200, + }, { + PCI_DEVICE_SUB(PCIE_VENDOR_ID_ASIX, PCIE_DEVICE_ID_AX99100, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, + + /* + * Best Connectivity and Rosewill PCI Multi I/O cards + */ + + { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x3002), + .driver_data = pbn_b0_bt_2_115200, + }, { + PCI_VDEVICE_SUB(NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x3004), + .driver_data = pbn_b0_bt_4_115200, + }, + + /* + * ASIX AX99100 PCIe to Multi I/O Controller + */ + { + PCI_VDEVICE_SUB(ASIX, PCI_DEVICE_ID_ASIX_AX99100, + 0xA000, 0x1000), + .driver_data = pbn_b0_1_115200, + }, + + /* Intel CE4100 */ + { + PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART), + .driver_data = pbn_ce4100_1_115200, + }, + + /* + * Cronyx Omega PCI + */ + { + PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_CRONYX_OMEGA), + .driver_data = pbn_omegapci, + }, + + /* + * Broadcom TruManage + */ + { + PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_BROADCOM_TRUMANAGE), + .driver_data = pbn_brcm_trumanage, + }, + + /* + * AgeStar as-prs2-009 + */ + { + PCI_VDEVICE(AGESTAR, PCI_DEVICE_ID_AGESTAR_9375), + .driver_data = pbn_b0_bt_2_115200, + }, + + /* + * WCH CH353 series devices: The 2S1P is handled by parport_serial + * so not listed here. + */ + { + PCI_VDEVICE(WCHCN, PCI_DEVICE_ID_WCHCN_CH353_4S), + .driver_data = pbn_b0_bt_4_115200, + }, { + PCI_VDEVICE(WCHCN, PCI_DEVICE_ID_WCHCN_CH353_2S1PF), + .driver_data = pbn_b0_bt_2_115200, + }, { + PCI_VDEVICE(WCHCN, PCI_DEVICE_ID_WCHCN_CH355_4S), + .driver_data = pbn_b0_bt_4_115200, + }, { + PCI_VDEVICE(WCHIC, PCI_DEVICE_ID_WCHIC_CH382_2S), + .driver_data = pbn_wch382_2, + }, { + PCI_VDEVICE(WCHIC, PCI_DEVICE_ID_WCHIC_CH384_4S), + .driver_data = pbn_wch384_4, + }, { + PCI_VDEVICE(WCHIC, PCI_DEVICE_ID_WCHIC_CH384_8S), + .driver_data = pbn_wch384_8, + }, + + /* + * Realtek RealManage + */ + { + PCI_VDEVICE(REALTEK, 0x816a), + .driver_data = pbn_b0_1_115200, + }, { + PCI_VDEVICE(REALTEK, 0x816b), + .driver_data = pbn_b0_1_115200, + }, + + /* Systembase Multi I/O cards */ + { + PCI_VDEVICE(SYSTEMBASE, 0x0008), + .driver_data = pbn_b0_8_921600, + }, + + /* 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_DEVICE_CLASS(PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00), + .driver_data = pbn_default, + }, { + PCI_DEVICE_CLASS(PCI_CLASS_COMMUNICATION_MODEM << 8, 0xffff00), + .driver_data = pbn_default, + }, { + PCI_DEVICE_CLASS(PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00), + .driver_data = pbn_default, + }, + { } +}; + +static pci_ers_result_t serial8250_io_error_detected(struct pci_dev *dev, + pci_channel_state_t state) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (state == pci_channel_io_perm_failure) + return PCI_ERS_RESULT_DISCONNECT; + + if (priv) + pciserial_detach_ports(priv); + + pci_disable_device(dev); + + return PCI_ERS_RESULT_NEED_RESET; +} + +static pci_ers_result_t serial8250_io_slot_reset(struct pci_dev *dev) +{ + int rc; + + rc = pci_enable_device(dev); + + if (rc) + return PCI_ERS_RESULT_DISCONNECT; + + pci_restore_state(dev); + + return PCI_ERS_RESULT_RECOVERED; +} + +static void serial8250_io_resume(struct pci_dev *dev) +{ + struct serial_private *priv = pci_get_drvdata(dev); + struct serial_private *new; + + if (!priv) + return; + + new = pciserial_init_ports(dev, priv->board); + if (!IS_ERR(new)) { + pci_set_drvdata(dev, new); + kfree(priv); + } +} + +static const struct pci_error_handlers serial8250_err_handler = { + .error_detected = serial8250_io_error_detected, + .slot_reset = serial8250_io_slot_reset, + .resume = serial8250_io_resume, +}; + +static struct pci_driver serial_pci_driver = { + .name = "serial", + .probe = pciserial_init_one, + .remove = pciserial_remove_one, + .driver = { + .pm = &pciserial_pm_ops, + }, + .id_table = serial_pci_tbl, + .err_handler = &serial8250_err_handler, +}; + +module_pci_driver(serial_pci_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module"); +MODULE_DEVICE_TABLE(pci, serial_pci_tbl); +MODULE_IMPORT_NS("SERIAL_8250_PCI"); diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c new file mode 100644 index 000000000..feeede164 --- /dev/null +++ b/drivers/tty/serial/8250/8250_pci1xxxx.c @@ -0,0 +1,887 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Probe module for 8250/16550-type MCHP PCI serial ports. + * + * Based on drivers/tty/serial/8250/8250_pci.c, + * + * Copyright (C) 2022 Microchip Technology Inc., All Rights Reserved. + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/circ_buf.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gfp_types.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/overflow.h> +#include <linux/pci.h> +#include <linux/pm.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> +#include <linux/serial_8250.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/time.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <asm/byteorder.h> + +#include "8250.h" +#include "8250_pcilib.h" + +#define PCI_DEVICE_ID_EFAR_PCI12000 0xa002 +#define PCI_DEVICE_ID_EFAR_PCI11010 0xa012 +#define PCI_DEVICE_ID_EFAR_PCI11101 0xa022 +#define PCI_DEVICE_ID_EFAR_PCI11400 0xa032 +#define PCI_DEVICE_ID_EFAR_PCI11414 0xa042 + +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_4p 0x0001 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p012 0x0002 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p013 0x0003 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p023 0x0004 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p123 0x0005 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p01 0x0006 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p02 0x0007 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p03 0x0008 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p12 0x0009 +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p13 0x000a +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p23 0x000b +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p0 0x000c +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p1 0x000d +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p2 0x000e +#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p3 0x000f + +#define PCI_SUBDEVICE_ID_EFAR_PCI12000 PCI_DEVICE_ID_EFAR_PCI12000 +#define PCI_SUBDEVICE_ID_EFAR_PCI11010 PCI_DEVICE_ID_EFAR_PCI11010 +#define PCI_SUBDEVICE_ID_EFAR_PCI11101 PCI_DEVICE_ID_EFAR_PCI11101 +#define PCI_SUBDEVICE_ID_EFAR_PCI11400 PCI_DEVICE_ID_EFAR_PCI11400 +#define PCI_SUBDEVICE_ID_EFAR_PCI11414 PCI_DEVICE_ID_EFAR_PCI11414 + +#define UART_SYSTEM_ADDR_BASE 0x1000 +#define UART_DEV_REV_REG (UART_SYSTEM_ADDR_BASE + 0x00) +#define UART_DEV_REV_MASK GENMASK(7, 0) +#define UART_SYSLOCK_REG (UART_SYSTEM_ADDR_BASE + 0xA0) +#define UART_SYSLOCK BIT(2) +#define SYSLOCK_SLEEP_TIMEOUT 100 +#define SYSLOCK_RETRY_CNT 1000 + +#define UART_RX_BYTE_FIFO 0x00 +#define UART_TX_BYTE_FIFO 0x00 +#define UART_FIFO_CTL 0x02 + +#define UART_MODEM_CTL_REG 0x04 +#define UART_MODEM_CTL_RTS_SET BIT(1) + +#define UART_LINE_STAT_REG 0x05 +#define UART_LINE_XMIT_CHECK_MASK GENMASK(6, 5) + +#define UART_ACTV_REG 0x11 +#define UART_BLOCK_SET_ACTIVE BIT(0) + +#define UART_PCI_CTRL_REG 0x80 +#define UART_PCI_CTRL_SET_MULTIPLE_MSI BIT(4) +#define UART_PCI_CTRL_D3_CLK_ENABLE BIT(0) + +#define ADCL_CFG_REG 0x40 +#define ADCL_CFG_POL_SEL BIT(2) +#define ADCL_CFG_PIN_SEL BIT(1) +#define ADCL_CFG_EN BIT(0) + +#define UART_BIT_SAMPLE_CNT_8 8 +#define UART_BIT_SAMPLE_CNT_16 16 +#define BAUD_CLOCK_DIV_INT_MSK GENMASK(31, 8) +#define ADCL_CFG_RTS_DELAY_MASK GENMASK(11, 8) +#define FRAC_DIV_TX_END_POINT_MASK GENMASK(23, 20) + +#define UART_WAKE_REG 0x8C +#define UART_WAKE_MASK_REG 0x90 +#define UART_WAKE_N_PIN BIT(2) +#define UART_WAKE_NCTS BIT(1) +#define UART_WAKE_INT BIT(0) +#define UART_WAKE_SRCS \ + (UART_WAKE_N_PIN | UART_WAKE_NCTS | UART_WAKE_INT) + +#define UART_BAUD_CLK_DIVISOR_REG 0x54 +#define FRAC_DIV_CFG_REG 0x58 + +#define UART_RESET_REG 0x94 +#define UART_RESET_D3_RESET_DISABLE BIT(16) +#define UART_RESET_HOT_RESET_DISABLE BIT(17) + +#define UART_BURST_STATUS_REG 0x9C +#define UART_TX_BURST_FIFO 0xA0 +#define UART_RX_BURST_FIFO 0xA4 + +#define UART_BIT_DIVISOR_8 0x26731000 +#define UART_BIT_DIVISOR_16 0x6ef71000 +#define UART_BAUD_4MBPS 4000000 + +#define MAX_PORTS 4 +#define PORT_OFFSET 0x100 +#define RX_BUF_SIZE 512 +#define UART_BYTE_SIZE 1 +#define UART_BURST_SIZE 4 + +#define UART_BST_STAT_RX_COUNT_MASK 0x00FF +#define UART_BST_STAT_TX_COUNT_MASK 0xFF00 +#define UART_BST_STAT_IIR_INT_PEND 0x100000 +#define UART_LSR_OVERRUN_ERR_CLR 0x43 +#define UART_BST_STAT_LSR_RX_MASK 0x9F000000 +#define UART_BST_STAT_LSR_RX_ERR_MASK 0x9E000000 +#define UART_BST_STAT_LSR_OVERRUN_ERR 0x2000000 +#define UART_BST_STAT_LSR_PARITY_ERR 0x4000000 +#define UART_BST_STAT_LSR_FRAME_ERR 0x8000000 +#define UART_BST_STAT_LSR_THRE 0x20000000 + +#define GET_MODEM_CTL_RTS_STATUS(reg) ((reg) & UART_MODEM_CTL_RTS_SET) +#define GET_RTS_PIN_STATUS(val) (((val) & TIOCM_RTS) >> 1) +#define RTS_TOGGLE_STATUS_MASK(val, reg) (GET_MODEM_CTL_RTS_STATUS(reg) \ + != GET_RTS_PIN_STATUS(val)) + +struct pci1xxxx_8250 { + unsigned int nr; + u8 dev_rev; + u8 pad[3]; + void __iomem *membase; + int line[] __counted_by(nr); +}; + +static const struct serial_rs485 pci1xxxx_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND, + .delay_rts_after_send = 1, + /* Delay RTS before send is not supported */ +}; + +static int pci1xxxx_set_sys_lock(struct pci1xxxx_8250 *port) +{ + writel(UART_SYSLOCK, port->membase + UART_SYSLOCK_REG); + return readl(port->membase + UART_SYSLOCK_REG); +} + +static int pci1xxxx_acquire_sys_lock(struct pci1xxxx_8250 *port) +{ + u32 regval; + + return readx_poll_timeout(pci1xxxx_set_sys_lock, port, regval, + (regval & UART_SYSLOCK), + SYSLOCK_SLEEP_TIMEOUT, + SYSLOCK_RETRY_CNT * SYSLOCK_SLEEP_TIMEOUT); +} + +static void pci1xxxx_release_sys_lock(struct pci1xxxx_8250 *port) +{ + writel(0x0, port->membase + UART_SYSLOCK_REG); +} + +static const int logical_to_physical_port_idx[][MAX_PORTS] = { + {0, 1, 2, 3}, /* PCI12000, PCI11010, PCI11101, PCI11400, PCI11414 */ + {0, 1, 2, 3}, /* PCI4p */ + {0, 1, 2, -1}, /* PCI3p012 */ + {0, 1, 3, -1}, /* PCI3p013 */ + {0, 2, 3, -1}, /* PCI3p023 */ + {1, 2, 3, -1}, /* PCI3p123 */ + {0, 1, -1, -1}, /* PCI2p01 */ + {0, 2, -1, -1}, /* PCI2p02 */ + {0, 3, -1, -1}, /* PCI2p03 */ + {1, 2, -1, -1}, /* PCI2p12 */ + {1, 3, -1, -1}, /* PCI2p13 */ + {2, 3, -1, -1}, /* PCI2p23 */ + {0, -1, -1, -1}, /* PCI1p0 */ + {1, -1, -1, -1}, /* PCI1p1 */ + {2, -1, -1, -1}, /* PCI1p2 */ + {3, -1, -1, -1}, /* PCI1p3 */ +}; + +static int pci1xxxx_get_num_ports(struct pci_dev *dev) +{ + switch (dev->subsystem_device) { + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p0: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p1: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p2: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p3: + case PCI_SUBDEVICE_ID_EFAR_PCI12000: + case PCI_SUBDEVICE_ID_EFAR_PCI11010: + case PCI_SUBDEVICE_ID_EFAR_PCI11101: + case PCI_SUBDEVICE_ID_EFAR_PCI11400: + default: + return 1; + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p01: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p02: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p03: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p12: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p13: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p23: + return 2; + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p012: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p123: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p013: + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p023: + return 3; + case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_4p: + case PCI_SUBDEVICE_ID_EFAR_PCI11414: + return 4; + } +} + +static unsigned int pci1xxxx_get_divisor(struct uart_port *port, + unsigned int baud, unsigned int *frac) +{ + unsigned int uart_sample_cnt; + unsigned int quot; + + if (baud >= UART_BAUD_4MBPS) + uart_sample_cnt = UART_BIT_SAMPLE_CNT_8; + else + uart_sample_cnt = UART_BIT_SAMPLE_CNT_16; + + /* + * Calculate baud rate sampling period in nanoseconds. + * Fractional part x denotes x/255 parts of a nanosecond. + */ + quot = NSEC_PER_SEC / (baud * uart_sample_cnt); + *frac = (NSEC_PER_SEC - quot * baud * uart_sample_cnt) * + 255 / uart_sample_cnt / baud; + + return quot; +} + +static void pci1xxxx_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot, unsigned int frac) +{ + if (baud >= UART_BAUD_4MBPS) + writel(UART_BIT_DIVISOR_8, port->membase + FRAC_DIV_CFG_REG); + else + writel(UART_BIT_DIVISOR_16, port->membase + FRAC_DIV_CFG_REG); + + writel(FIELD_PREP(BAUD_CLOCK_DIV_INT_MSK, quot) | frac, + port->membase + UART_BAUD_CLK_DIVISOR_REG); +} + +static void pci1xxxx_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + u32 fract_div_cfg_reg; + u32 line_stat_reg; + u32 modem_ctl_reg; + u32 adcl_cfg_reg; + + adcl_cfg_reg = readl(port->membase + ADCL_CFG_REG); + + /* HW is responsible in ADCL_EN case */ + if ((adcl_cfg_reg & (ADCL_CFG_EN | ADCL_CFG_PIN_SEL))) + return; + + modem_ctl_reg = readl(port->membase + UART_MODEM_CTL_REG); + + serial8250_do_set_mctrl(port, mctrl); + + if (RTS_TOGGLE_STATUS_MASK(mctrl, modem_ctl_reg)) { + line_stat_reg = readl(port->membase + UART_LINE_STAT_REG); + if (line_stat_reg & UART_LINE_XMIT_CHECK_MASK) { + fract_div_cfg_reg = readl(port->membase + + FRAC_DIV_CFG_REG); + + writel((fract_div_cfg_reg & + ~(FRAC_DIV_TX_END_POINT_MASK)), + port->membase + FRAC_DIV_CFG_REG); + + /* Enable ADC and set the nRTS pin */ + writel((adcl_cfg_reg | (ADCL_CFG_EN | + ADCL_CFG_PIN_SEL)), + port->membase + ADCL_CFG_REG); + + /* Revert to the original settings */ + writel(adcl_cfg_reg, port->membase + ADCL_CFG_REG); + + writel(fract_div_cfg_reg, port->membase + + FRAC_DIV_CFG_REG); + } + } +} + +static int pci1xxxx_rs485_config(struct uart_port *port, + struct ktermios *termios, + struct serial_rs485 *rs485) +{ + u32 delay_in_baud_periods; + u32 baud_period_in_ns; + u32 mode_cfg = 0; + u32 sample_cnt; + u32 clock_div; + u32 frac_div; + + frac_div = readl(port->membase + FRAC_DIV_CFG_REG); + + if (frac_div == UART_BIT_DIVISOR_16) + sample_cnt = UART_BIT_SAMPLE_CNT_16; + else + sample_cnt = UART_BIT_SAMPLE_CNT_8; + + /* + * pci1xxxx's uart hardware supports only RTS delay after + * Tx and in units of bit times to a maximum of 15 + */ + if (rs485->flags & SER_RS485_ENABLED) { + mode_cfg = ADCL_CFG_EN | ADCL_CFG_PIN_SEL; + + if (!(rs485->flags & SER_RS485_RTS_ON_SEND)) + mode_cfg |= ADCL_CFG_POL_SEL; + + if (rs485->delay_rts_after_send) { + clock_div = readl(port->membase + UART_BAUD_CLK_DIVISOR_REG); + baud_period_in_ns = + FIELD_GET(BAUD_CLOCK_DIV_INT_MSK, clock_div) * + sample_cnt; + delay_in_baud_periods = + rs485->delay_rts_after_send * NSEC_PER_MSEC / + baud_period_in_ns; + delay_in_baud_periods = + min_t(u32, delay_in_baud_periods, + FIELD_MAX(ADCL_CFG_RTS_DELAY_MASK)); + mode_cfg |= FIELD_PREP(ADCL_CFG_RTS_DELAY_MASK, + delay_in_baud_periods); + rs485->delay_rts_after_send = + baud_period_in_ns * delay_in_baud_periods / + NSEC_PER_MSEC; + } + } + writel(mode_cfg, port->membase + ADCL_CFG_REG); + return 0; +} + +static u32 pci1xxxx_read_burst_status(struct uart_port *port) +{ + u32 status; + + status = readl(port->membase + UART_BURST_STATUS_REG); + if (status & UART_BST_STAT_LSR_RX_ERR_MASK) { + if (status & UART_BST_STAT_LSR_OVERRUN_ERR) { + writeb(UART_LSR_OVERRUN_ERR_CLR, + port->membase + UART_FIFO_CTL); + port->icount.overrun++; + } + + if (status & UART_BST_STAT_LSR_FRAME_ERR) + port->icount.frame++; + + if (status & UART_BST_STAT_LSR_PARITY_ERR) + port->icount.parity++; + } + return status; +} + +static void pci1xxxx_process_read_data(struct uart_port *port, + unsigned char *rx_buff, u32 *buff_index, + u32 *valid_byte_count) +{ + u32 valid_burst_count = *valid_byte_count / UART_BURST_SIZE; + u32 *burst_buf; + + /* + * Depending on the RX Trigger Level the number of bytes that can be + * stored in RX FIFO at a time varies. Each transaction reads data + * in DWORDs. If there are less than four remaining valid_byte_count + * to read, the data is received one byte at a time. + */ + while (valid_burst_count--) { + if (*buff_index > (RX_BUF_SIZE - UART_BURST_SIZE)) + break; + burst_buf = (u32 *)&rx_buff[*buff_index]; + *burst_buf = readl(port->membase + UART_RX_BURST_FIFO); + *buff_index += UART_BURST_SIZE; + *valid_byte_count -= UART_BURST_SIZE; + } + + while (*valid_byte_count) { + if (*buff_index >= RX_BUF_SIZE) + break; + rx_buff[*buff_index] = readb(port->membase + + UART_RX_BYTE_FIFO); + *buff_index += UART_BYTE_SIZE; + *valid_byte_count -= UART_BYTE_SIZE; + } +} + +static void pci1xxxx_rx_burst(struct uart_port *port, u32 uart_status) +{ + u32 valid_byte_count = uart_status & UART_BST_STAT_RX_COUNT_MASK; + struct tty_port *tty_port = &port->state->port; + unsigned char rx_buff[RX_BUF_SIZE]; + u32 buff_index = 0; + u32 copied_len; + + if (valid_byte_count != 0 && + valid_byte_count < RX_BUF_SIZE) { + pci1xxxx_process_read_data(port, rx_buff, &buff_index, + &valid_byte_count); + + copied_len = (u32)tty_insert_flip_string(tty_port, rx_buff, + buff_index); + + if (copied_len != buff_index) + port->icount.overrun += buff_index - copied_len; + + port->icount.rx += buff_index; + tty_flip_buffer_push(tty_port); + } +} + +static void pci1xxxx_process_write_data(struct uart_port *port, + int *data_empty_count, + u32 *valid_byte_count) +{ + struct tty_port *tport = &port->state->port; + u32 valid_burst_count = *valid_byte_count / UART_BURST_SIZE; + + /* + * Each transaction transfers data in DWORDs. If there are less than + * four remaining valid_byte_count to transfer or if the circular + * buffer has insufficient space for a DWORD, the data is transferred + * one byte at a time. + */ + while (valid_burst_count) { + u32 c; + + if (*data_empty_count - UART_BURST_SIZE < 0) + break; + if (kfifo_len(&tport->xmit_fifo) < UART_BURST_SIZE) + break; + if (WARN_ON(kfifo_out(&tport->xmit_fifo, (u8 *)&c, sizeof(c)) != + sizeof(c))) + break; + writel(c, port->membase + UART_TX_BURST_FIFO); + *valid_byte_count -= UART_BURST_SIZE; + *data_empty_count -= UART_BURST_SIZE; + valid_burst_count -= UART_BYTE_SIZE; + } + + while (*valid_byte_count) { + u8 c; + + if (!kfifo_get(&tport->xmit_fifo, &c)) + break; + writeb(c, port->membase + UART_TX_BYTE_FIFO); + *data_empty_count -= UART_BYTE_SIZE; + *valid_byte_count -= UART_BYTE_SIZE; + + /* + * If there are any pending burst count, data is handled by + * transmitting DWORDs at a time. + */ + if (valid_burst_count && + kfifo_len(&tport->xmit_fifo) >= UART_BURST_SIZE) + break; + } +} + +static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct tty_port *tport = &port->state->port; + u32 valid_byte_count; + int data_empty_count; + + if (port->x_char) { + writeb(port->x_char, port->membase + UART_TX); + port->icount.tx++; + port->x_char = 0; + return; + } + + if ((uart_tx_stopped(port)) || kfifo_is_empty(&tport->xmit_fifo)) { + port->ops->stop_tx(port); + } else { + data_empty_count = (pci1xxxx_read_burst_status(port) & + UART_BST_STAT_TX_COUNT_MASK) >> 8; + do { + valid_byte_count = kfifo_len(&tport->xmit_fifo); + + pci1xxxx_process_write_data(port, + &data_empty_count, + &valid_byte_count); + + port->icount.tx++; + if (kfifo_is_empty(&tport->xmit_fifo)) + break; + } while (data_empty_count && valid_byte_count); + } + + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) + uart_write_wakeup(port); + + /* + * With RPM enabled, we have to wait until the FIFO is empty before + * the HW can go idle. So we get here once again with empty FIFO and + * disable the interrupt and RPM in __stop_tx() + */ + if (kfifo_is_empty(&tport->xmit_fifo) && + !(up->capabilities & UART_CAP_RPM)) + port->ops->stop_tx(port); +} + +static int pci1xxxx_handle_irq(struct uart_port *port) +{ + unsigned long flags; + u32 status; + + status = pci1xxxx_read_burst_status(port); + + if (status & UART_BST_STAT_IIR_INT_PEND) + return 0; + + spin_lock_irqsave(&port->lock, flags); + + if (status & UART_BST_STAT_LSR_RX_MASK) + pci1xxxx_rx_burst(port, status); + + if (status & UART_BST_STAT_LSR_THRE) + pci1xxxx_tx_burst(port, status); + + spin_unlock_irqrestore(&port->lock, flags); + + return 1; +} + +static bool pci1xxxx_port_suspend(int line) +{ + struct uart_8250_port *up = serial8250_get_port(line); + struct uart_port *port = &up->port; + struct tty_port *tport = &port->state->port; + unsigned long flags; + bool ret = false; + u8 wakeup_mask; + + mutex_lock(&tport->mutex); + if (port->suspended == 0 && port->dev) { + wakeup_mask = readb(up->port.membase + UART_WAKE_MASK_REG); + + uart_port_lock_irqsave(port, &flags); + port->mctrl &= ~TIOCM_OUT2; + port->ops->set_mctrl(port, port->mctrl); + uart_port_unlock_irqrestore(port, flags); + + ret = (wakeup_mask & UART_WAKE_SRCS) != UART_WAKE_SRCS; + } + + writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG); + mutex_unlock(&tport->mutex); + + return ret; +} + +static void pci1xxxx_port_resume(int line) +{ + struct uart_8250_port *up = serial8250_get_port(line); + struct uart_port *port = &up->port; + struct tty_port *tport = &port->state->port; + unsigned long flags; + + mutex_lock(&tport->mutex); + writeb(UART_BLOCK_SET_ACTIVE, port->membase + UART_ACTV_REG); + writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG); + + if (port->suspended == 0) { + uart_port_lock_irqsave(port, &flags); + port->mctrl |= TIOCM_OUT2; + port->ops->set_mctrl(port, port->mctrl); + uart_port_unlock_irqrestore(port, flags); + } + mutex_unlock(&tport->mutex); +} + +static int pci1xxxx_suspend(struct device *dev) +{ + struct pci1xxxx_8250 *priv = dev_get_drvdata(dev); + struct pci_dev *pcidev = to_pci_dev(dev); + bool wakeup = false; + unsigned int data; + void __iomem *p; + int i; + + for (i = 0; i < priv->nr; i++) { + if (priv->line[i] >= 0) { + serial8250_suspend_port(priv->line[i]); + wakeup |= pci1xxxx_port_suspend(priv->line[i]); + } + } + + p = pci_ioremap_bar(pcidev, 0); + if (!p) { + dev_err(dev, "remapping of bar 0 memory failed"); + return -ENOMEM; + } + + data = readl(p + UART_RESET_REG); + + if (priv->dev_rev >= 0xC0) + data |= UART_RESET_HOT_RESET_DISABLE; + + writel(data | UART_RESET_D3_RESET_DISABLE, p + UART_RESET_REG); + + if (wakeup) + writeb(UART_PCI_CTRL_D3_CLK_ENABLE, p + UART_PCI_CTRL_REG); + + iounmap(p); + device_set_wakeup_enable(dev, true); + pci_wake_from_d3(pcidev, true); + + return 0; +} + +static int pci1xxxx_resume(struct device *dev) +{ + struct pci1xxxx_8250 *priv = dev_get_drvdata(dev); + struct pci_dev *pcidev = to_pci_dev(dev); + unsigned int data; + void __iomem *p; + int i; + + p = pci_ioremap_bar(pcidev, 0); + if (!p) { + dev_err(dev, "remapping of bar 0 memory failed"); + return -ENOMEM; + } + + data = readl(p + UART_RESET_REG); + + if (priv->dev_rev >= 0xC0) + data &= ~UART_RESET_HOT_RESET_DISABLE; + + writel(data & ~UART_RESET_D3_RESET_DISABLE, p + UART_RESET_REG); + + iounmap(p); + + for (i = 0; i < priv->nr; i++) { + if (priv->line[i] >= 0) { + pci1xxxx_port_resume(priv->line[i]); + serial8250_resume_port(priv->line[i]); + } + } + + return 0; +} + +static int pci1xxxx_setup(struct pci_dev *pdev, + struct uart_8250_port *port, int port_idx, struct pci1xxxx_8250 *priv) +{ + int ret; + + port->port.flags |= UPF_FIXED_TYPE | UPF_SKIP_TEST; + port->port.type = PORT_MCHP16550A; + /* + * 8250 core considers prescaller value to be always 16. + * The MCHP ports support downscaled mode and hence the + * functional UART clock can be lower, i.e. 62.5MHz, than + * software expects in order to support higher baud rates. + * Assign here 64MHz to support 4Mbps. + * + * The value itself is not really used anywhere except baud + * rate calculations, so we can mangle it as we wish. + */ + port->port.uartclk = 64 * HZ_PER_MHZ; + port->port.set_termios = serial8250_do_set_termios; + port->port.get_divisor = pci1xxxx_get_divisor; + port->port.set_divisor = pci1xxxx_set_divisor; + port->port.rs485_config = pci1xxxx_rs485_config; + port->port.rs485_supported = pci1xxxx_rs485_supported; + + /* + * C0 and later revisions support Burst operation. + * RTS workaround in mctrl is applicable only to B0. + */ + if (priv->dev_rev >= 0xC0) + port->port.handle_irq = pci1xxxx_handle_irq; + else if (priv->dev_rev == 0xB0) + port->port.set_mctrl = pci1xxxx_set_mctrl; + + ret = serial8250_pci_setup_port(pdev, port, 0, PORT_OFFSET * port_idx, 0, priv->membase); + if (ret < 0) + return ret; + + writeb(UART_BLOCK_SET_ACTIVE, port->port.membase + UART_ACTV_REG); + writeb(UART_WAKE_SRCS, port->port.membase + UART_WAKE_REG); + writeb(UART_WAKE_N_PIN, port->port.membase + UART_WAKE_MASK_REG); + + return 0; +} + +static unsigned int pci1xxxx_get_max_port(int subsys_dev) +{ + unsigned int i = MAX_PORTS; + + if (subsys_dev < ARRAY_SIZE(logical_to_physical_port_idx)) + while (i--) { + if (logical_to_physical_port_idx[subsys_dev][i] != -1) + return logical_to_physical_port_idx[subsys_dev][i] + 1; + } + + if (subsys_dev == PCI_SUBDEVICE_ID_EFAR_PCI11414) + return 4; + + return 1; +} + +static int pci1xxxx_logical_to_physical_port_translate(int subsys_dev, int port) +{ + if (subsys_dev < ARRAY_SIZE(logical_to_physical_port_idx)) + return logical_to_physical_port_idx[subsys_dev][port]; + + return logical_to_physical_port_idx[0][port]; +} + +static int pci1xxxx_get_device_revision(struct pci1xxxx_8250 *priv) +{ + u32 regval; + int ret; + + /* + * DEV REV is a system register, HW Syslock bit + * should be acquired before accessing the register + */ + ret = pci1xxxx_acquire_sys_lock(priv); + if (ret) + return ret; + + regval = readl(priv->membase + UART_DEV_REV_REG); + priv->dev_rev = regval & UART_DEV_REV_MASK; + + pci1xxxx_release_sys_lock(priv); + + return 0; +} + +static int pci1xxxx_serial_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct pci1xxxx_8250 *priv; + struct uart_8250_port uart; + unsigned int max_vec_reqd; + unsigned int nr_ports, i; + int num_vectors; + int subsys_dev; + int port_idx; + int ret; + int rc; + + rc = pcim_enable_device(pdev); + if (rc) + return rc; + + nr_ports = pci1xxxx_get_num_ports(pdev); + + priv = devm_kzalloc(dev, struct_size(priv, line, nr_ports), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->membase = pci_ioremap_bar(pdev, 0); + if (!priv->membase) + return -ENOMEM; + + ret = pci1xxxx_get_device_revision(priv); + if (ret) + return ret; + + pci_set_master(pdev); + + priv->nr = nr_ports; + + subsys_dev = pdev->subsystem_device; + max_vec_reqd = pci1xxxx_get_max_port(subsys_dev); + + num_vectors = pci_alloc_irq_vectors(pdev, 1, max_vec_reqd, PCI_IRQ_ALL_TYPES); + if (num_vectors < 0) { + pci_iounmap(pdev, priv->membase); + return num_vectors; + } + + memset(&uart, 0, sizeof(uart)); + uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT; + uart.port.dev = dev; + + if (num_vectors == max_vec_reqd) + writeb(UART_PCI_CTRL_SET_MULTIPLE_MSI, priv->membase + UART_PCI_CTRL_REG); + + for (i = 0; i < nr_ports; i++) { + priv->line[i] = -ENODEV; + + port_idx = pci1xxxx_logical_to_physical_port_translate(subsys_dev, i); + + if (num_vectors == max_vec_reqd) + uart.port.irq = pci_irq_vector(pdev, port_idx); + else + uart.port.irq = pci_irq_vector(pdev, 0); + + rc = pci1xxxx_setup(pdev, &uart, port_idx, priv); + if (rc) { + dev_warn(dev, "Failed to setup port %u\n", i); + continue; + } + + priv->line[i] = serial8250_register_8250_port(&uart); + if (priv->line[i] < 0) { + dev_warn(dev, + "Couldn't register serial port %lx, irq %d, type %d, error %d\n", + uart.port.iobase, uart.port.irq, uart.port.iotype, + priv->line[i]); + } + } + + pci_set_drvdata(pdev, priv); + + return 0; +} + +static void pci1xxxx_serial_remove(struct pci_dev *dev) +{ + struct pci1xxxx_8250 *priv = pci_get_drvdata(dev); + unsigned int i; + + for (i = 0; i < priv->nr; i++) { + if (priv->line[i] >= 0) + serial8250_unregister_port(priv->line[i]); + } + + pci_free_irq_vectors(dev); + pci_iounmap(dev, priv->membase); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(pci1xxxx_pm_ops, pci1xxxx_suspend, pci1xxxx_resume); + +static const struct pci_device_id pci1xxxx_pci_tbl[] = { + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11010) }, + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11101) }, + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11400) }, + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11414) }, + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI12000) }, + {} +}; +MODULE_DEVICE_TABLE(pci, pci1xxxx_pci_tbl); + +static struct pci_driver pci1xxxx_pci_driver = { + .name = "pci1xxxx serial", + .probe = pci1xxxx_serial_probe, + .remove = pci1xxxx_serial_remove, + .driver = { + .pm = pm_sleep_ptr(&pci1xxxx_pm_ops), + }, + .id_table = pci1xxxx_pci_tbl, +}; +module_pci_driver(pci1xxxx_pci_driver); + +static_assert((ARRAY_SIZE(logical_to_physical_port_idx) == PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p3 + 1)); + +MODULE_IMPORT_NS("SERIAL_8250_PCI"); +MODULE_DESCRIPTION("Microchip Technology Inc. PCIe to UART module"); +MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>"); +MODULE_AUTHOR("Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_pcilib.c b/drivers/tty/serial/8250/8250_pcilib.c new file mode 100644 index 000000000..9d5d2531a --- /dev/null +++ b/drivers/tty/serial/8250/8250_pcilib.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 8250 PCI library. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + */ +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/types.h> + +#include "8250.h" +#include "8250_pcilib.h" + +int serial_8250_warn_need_ioport(struct pci_dev *dev) +{ + dev_warn(&dev->dev, + "Serial port not supported because of missing I/O resource\n"); + + return -ENXIO; +} +EXPORT_SYMBOL_NS_GPL(serial_8250_warn_need_ioport, "SERIAL_8250_PCI"); + +int serial8250_pci_setup_port(struct pci_dev *dev, struct uart_8250_port *port, + u8 bar, unsigned int offset, int regshift, void __iomem *iomem) +{ + if (bar >= PCI_STD_NUM_BARS) + return -EINVAL; + + if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { + port->port.iotype = UPIO_MEM; + port->port.iobase = 0; + port->port.mapbase = pci_resource_start(dev, bar) + offset; + port->port.membase = iomem + offset; + port->port.regshift = regshift; + } else if (IS_ENABLED(CONFIG_HAS_IOPORT)) { + port->port.iotype = UPIO_PORT; + port->port.iobase = pci_resource_start(dev, bar) + offset; + port->port.mapbase = 0; + port->port.membase = NULL; + port->port.regshift = 0; + } else { + return serial_8250_warn_need_ioport(dev); + } + return 0; +} +EXPORT_SYMBOL_NS_GPL(serial8250_pci_setup_port, "SERIAL_8250_PCI"); +MODULE_DESCRIPTION("8250 PCI library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_pcilib.h b/drivers/tty/serial/8250/8250_pcilib.h new file mode 100644 index 000000000..ab18de8d1 --- /dev/null +++ b/drivers/tty/serial/8250/8250_pcilib.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * 8250 PCI library header file. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + */ + +#include <linux/types.h> + +struct pci_dev; + +struct uart_8250_port; + +int serial8250_pci_setup_port(struct pci_dev *dev, struct uart_8250_port *port, u8 bar, + unsigned int offset, int regshift, void __iomem *iomem); + +int serial_8250_warn_need_ioport(struct pci_dev *dev); diff --git a/drivers/tty/serial/8250/8250_pericom.c b/drivers/tty/serial/8250/8250_pericom.c new file mode 100644 index 000000000..b8d5b7714 --- /dev/null +++ b/drivers/tty/serial/8250/8250_pericom.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Driver for Pericom UART */ + +#include <linux/bits.h> +#include <linux/module.h> +#include <linux/overflow.h> +#include <linux/pci.h> + +#include "8250.h" + +#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_2SDB 0x1051 +#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_2S 0x1053 +#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM422_4 0x105a +#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM485_4 0x105b +#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SDB 0x105c +#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_4S 0x105e +#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM422_8 0x106a +#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM485_8 0x106b +#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_2DB 0x1091 +#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM232_2 0x1093 +#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_4 0x1098 +#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_4DB 0x1099 +#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM232_4 0x109b +#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_8 0x10a9 +#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_2SMDB 0x10d1 +#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_2SM 0x10d3 +#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SM 0x10d9 +#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SMDB 0x10da +#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_4SM 0x10dc +#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_8SM 0x10e9 +#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_1 0x1108 +#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM422_2 0x1110 +#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_2 0x1111 +#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM422_4 0x1118 +#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_4 0x1119 +#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_2S 0x1152 +#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_4S 0x115a +#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM232_2 0x1190 +#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM232_2 0x1191 +#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM232_4 0x1198 +#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM232_4 0x1199 +#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_2SM 0x11d0 +#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_4SM 0x11d8 + +struct pericom8250 { + void __iomem *virt; + unsigned int nr; + int line[]; +}; + +static void pericom_do_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot, unsigned int quot_frac) +{ + int scr; + + for (scr = 16; scr > 4; scr--) { + unsigned int maxrate = port->uartclk / scr; + unsigned int divisor = max(maxrate / baud, 1U); + int delta = maxrate / divisor - baud; + + if (baud > maxrate + baud / 50) + continue; + + if (delta > baud / 50) + divisor++; + + if (divisor > 0xffff) + continue; + + /* Update delta due to possible divisor change */ + delta = maxrate / divisor - baud; + if (abs(delta) < baud / 50) { + struct uart_8250_port *up = up_to_u8250p(port); + int lcr = serial_port_in(port, UART_LCR); + + serial_port_out(port, UART_LCR, lcr | UART_LCR_DLAB); + serial_dl_write(up, divisor); + serial_port_out(port, 2, 16 - scr); + serial_port_out(port, UART_LCR, lcr); + return; + } + } +} + +static int pericom8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + unsigned int nr, i, bar = 0, maxnr; + struct pericom8250 *pericom; + struct uart_8250_port uart; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + maxnr = pci_resource_len(pdev, bar) >> 3; + + if (pdev->vendor == PCI_VENDOR_ID_PERICOM) + nr = pdev->device & 0x0f; + else if (pdev->vendor == PCI_VENDOR_ID_ACCESSIO) + nr = BIT(((pdev->device & 0x38) >> 3) - 1); + else + nr = 1; + + pericom = devm_kzalloc(&pdev->dev, struct_size(pericom, line, nr), GFP_KERNEL); + if (!pericom) + return -ENOMEM; + + pericom->virt = pcim_iomap(pdev, bar, 0); + if (!pericom->virt) + return -ENOMEM; + + memset(&uart, 0, sizeof(uart)); + + uart.port.dev = &pdev->dev; + uart.port.irq = pdev->irq; + uart.port.private_data = pericom; + uart.port.iotype = UPIO_PORT; + uart.port.uartclk = 921600 * 16; + uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; + uart.port.set_divisor = pericom_do_set_divisor; + for (i = 0; i < nr && i < maxnr; i++) { + unsigned int offset = (i == 3 && nr == 4) ? 0x38 : i * 0x8; + + uart.port.iobase = pci_resource_start(pdev, bar) + offset; + + dev_dbg(&pdev->dev, "Setup PCI port: port %lx, irq %d, type %d\n", + uart.port.iobase, uart.port.irq, uart.port.iotype); + + pericom->line[i] = serial8250_register_8250_port(&uart); + if (pericom->line[i] < 0) { + dev_err(&pdev->dev, + "Couldn't register serial port %lx, irq %d, type %d, error %d\n", + uart.port.iobase, uart.port.irq, + uart.port.iotype, pericom->line[i]); + break; + } + } + pericom->nr = i; + + pci_set_drvdata(pdev, pericom); + return 0; +} + +static void pericom8250_remove(struct pci_dev *pdev) +{ + struct pericom8250 *pericom = pci_get_drvdata(pdev); + unsigned int i; + + for (i = 0; i < pericom->nr; i++) + serial8250_unregister_port(pericom->line[i]); +} + +static const struct pci_device_id pericom8250_pci_ids[] = { + /* + * Pericom PI7C9X795[1248] Uno/Dual/Quad/Octal UART + * (Only 7954 has an offset jump for port 4) + */ + { PCI_VDEVICE(PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7951) }, + { PCI_VDEVICE(PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7952) }, + { PCI_VDEVICE(PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7954) }, + { PCI_VDEVICE(PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7958) }, + + /* + * ACCES I/O Products quad + * (Only 7954 has an offset jump for port 4) + */ + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_2SDB) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_2S) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM422_4) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM485_4) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SDB) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_4S) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM422_8) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM485_8) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_2DB) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM232_2) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_4) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_4DB) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM232_4) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_8) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_2SMDB) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_2SM) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SM) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SMDB) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_4SM) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_8SM) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_1) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM422_2) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_2) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM422_4) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_4) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_2S) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_4S) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM232_2) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM232_2) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM232_4) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM232_4) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_2SM) }, + { PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_4SM) }, + { } +}; +MODULE_DEVICE_TABLE(pci, pericom8250_pci_ids); + +static struct pci_driver pericom8250_pci_driver = { + .name = "8250_pericom", + .id_table = pericom8250_pci_ids, + .probe = pericom8250_probe, + .remove = pericom8250_remove, +}; +module_pci_driver(pericom8250_pci_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Pericom UART driver"); diff --git a/drivers/tty/serial/8250/8250_platform.c b/drivers/tty/serial/8250/8250_platform.c new file mode 100644 index 000000000..af946d12e --- /dev/null +++ b/drivers/tty/serial/8250/8250_platform.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Universal/legacy platform driver for 8250/16550-type serial ports + * + * Supports: + * ISA-compatible 8250/16550 ports + * ACPI 8250/16550 ports + * PNP 8250/16550 ports + * "serial8250" platform devices + */ +#include <linux/acpi.h> +#include <linux/array_size.h> +#include <linux/cleanup.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/once.h> +#include <linux/platform_device.h> + +#include <linux/serial_8250.h> + +#ifdef CONFIG_SPARC +#include <linux/sunserialcore.h> +#endif + +#include "8250.h" + +/* + * Configuration: + * share_irqs: Whether we pass IRQF_SHARED to request_irq(). + * This option is unsafe when used on edge-triggered interrupts. + */ +static bool share_irqs = IS_ENABLED(CONFIG_SERIAL_8250_SHARE_IRQ); + +unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS; + +#include <asm/serial.h> + +/* + * SERIAL_PORT_DFNS tells us about built-in ports that have no + * standard enumeration mechanism. Platforms that can find all + * serial ports via mechanisms like ACPI or PCI need not supply it. + */ +#ifndef SERIAL_PORT_DFNS +#define SERIAL_PORT_DFNS +#endif + +static const struct old_serial_port old_serial_port[] = { + SERIAL_PORT_DFNS /* defined in asm/serial.h */ +}; + +serial8250_isa_config_fn serial8250_isa_config; +void serial8250_set_isa_configurator(serial8250_isa_config_fn v) +{ + serial8250_isa_config = v; +} +EXPORT_SYMBOL(serial8250_set_isa_configurator); + +static void __init __serial8250_isa_init_ports(void) +{ + int i; + + if (nr_uarts > UART_NR) + nr_uarts = UART_NR; + + /* + * Set up initial ISA ports based on nr_uart module param, or else + * default to CONFIG_SERIAL_8250_RUNTIME_UARTS. Note that we do not + * need to increase nr_uarts when setting up the initial ISA ports. + */ + for (i = 0; i < nr_uarts; i++) + serial8250_setup_port(i); + + /* chain base port ops to support Remote Supervisor Adapter */ + univ8250_port_ops = *univ8250_port_base_ops; + univ8250_rsa_support(&univ8250_port_ops, univ8250_port_base_ops); + + for (i = 0; i < ARRAY_SIZE(old_serial_port) && i < nr_uarts; i++) { + struct uart_8250_port *up = serial8250_get_port(i); + struct uart_port *port = &up->port; + + port->iobase = old_serial_port[i].port; + port->irq = irq_canonicalize(old_serial_port[i].irq); + port->irqflags = 0; + port->uartclk = old_serial_port[i].baud_base * 16; + port->flags = old_serial_port[i].flags; + port->hub6 = 0; + port->membase = old_serial_port[i].iomem_base; + port->iotype = old_serial_port[i].io_type; + port->regshift = old_serial_port[i].iomem_reg_shift; + + if (share_irqs) + port->irqflags |= IRQF_SHARED; + + if (serial8250_isa_config != NULL) + serial8250_isa_config(i, &up->port, &up->capabilities); + } +} + +void __init serial8250_isa_init_ports(void) +{ + DO_ONCE(__serial8250_isa_init_ports); +} + +/* + * Generic 16550A platform devices + */ +static int serial8250_probe_acpi(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *regs; + int ret, line; + + struct uart_8250_port *uart __free(kfree) = kzalloc_obj(*uart); + if (!uart) + return -ENOMEM; + + regs = platform_get_mem_or_io(pdev, 0); + if (!regs) + return dev_err_probe(dev, -EINVAL, "no registers defined\n"); + + switch (resource_type(regs)) { + case IORESOURCE_IO: + uart->port.iobase = regs->start; + break; + case IORESOURCE_MEM: + uart->port.mapbase = regs->start; + uart->port.mapsize = resource_size(regs); + uart->port.flags = UPF_IOREMAP; + break; + default: + return -EINVAL; + } + + /* default clock frequency */ + uart->port.uartclk = 1843200; + uart->port.type = PORT_16550A; + uart->port.dev = &pdev->dev; + uart->port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + + ret = uart_read_and_validate_port_properties(&uart->port); + /* no interrupt -> fall back to polling */ + if (ret == -ENXIO) + ret = 0; + if (ret) + return ret; + + line = serial8250_register_8250_port(uart); + if (line < 0) + return line; + + return 0; +} + +static int serial8250_probe_platform(struct platform_device *dev, struct plat_serial8250_port *p) +{ + int ret, i; + + struct uart_8250_port *uart __free(kfree) = kzalloc_obj(*uart); + if (!uart) + return -ENOMEM; + + for (i = 0; p && p->flags != 0; p++, i++) { + uart->port.iobase = p->iobase; + uart->port.membase = p->membase; + uart->port.irq = p->irq; + uart->port.irqflags = p->irqflags; + uart->port.uartclk = p->uartclk; + uart->port.regshift = p->regshift; + uart->port.iotype = p->iotype; + uart->port.flags = p->flags; + uart->port.mapbase = p->mapbase; + uart->port.mapsize = p->mapsize; + uart->port.hub6 = p->hub6; + uart->port.has_sysrq = p->has_sysrq; + uart->port.private_data = p->private_data; + uart->port.type = p->type; + uart->bugs = p->bugs; + uart->port.serial_in = p->serial_in; + uart->port.serial_out = p->serial_out; + uart->dl_read = p->dl_read; + uart->dl_write = p->dl_write; + uart->port.handle_irq = p->handle_irq; + uart->port.handle_break = p->handle_break; + uart->port.set_termios = p->set_termios; + uart->port.set_ldisc = p->set_ldisc; + uart->port.get_mctrl = p->get_mctrl; + uart->port.pm = p->pm; + uart->port.dev = &dev->dev; + + if (share_irqs) + uart->port.irqflags |= IRQF_SHARED; + + ret = serial8250_register_8250_port(uart); + if (ret < 0) { + dev_err(&dev->dev, "unable to register port at index %d " + "(IO%lx MEM%llx IRQ%d): %d\n", i, + p->iobase, (unsigned long long)p->mapbase, + p->irq, ret); + } + } + return 0; +} + +/* + * Register a set of serial devices attached to a platform device. + * The list is terminated with a zero flags entry, which means we expect + * all entries to have at least UPF_BOOT_AUTOCONF set. + */ +static int serial8250_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct plat_serial8250_port *p; + + p = dev_get_platdata(dev); + if (p) + return serial8250_probe_platform(pdev, p); + + /* + * Probe platform UART devices defined using standard hardware + * discovery mechanism like ACPI or DT. Support only ACPI based + * serial device for now. + */ + if (has_acpi_companion(dev)) + return serial8250_probe_acpi(pdev); + + return 0; +} + +/* + * Remove serial ports registered against a platform device. + */ +static void serial8250_remove(struct platform_device *dev) +{ + int i; + + for (i = 0; i < nr_uarts; i++) { + struct uart_8250_port *up = serial8250_get_port(i); + + if (up->port.dev == &dev->dev) + serial8250_unregister_port(i); + } +} + +static int serial8250_suspend(struct platform_device *dev, pm_message_t state) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_8250_port *up = serial8250_get_port(i); + + if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) + uart_suspend_port(&serial8250_reg, &up->port); + } + + return 0; +} + +static int serial8250_resume(struct platform_device *dev) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_8250_port *up = serial8250_get_port(i); + + if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) + serial8250_resume_port(i); + } + + return 0; +} + +static const struct acpi_device_id acpi_platform_serial_table[] = { + { "RSCV0003" }, /* RISC-V Generic 16550A UART */ + { } +}; +MODULE_DEVICE_TABLE(acpi, acpi_platform_serial_table); + +static struct platform_driver serial8250_isa_driver = { + .probe = serial8250_probe, + .remove = serial8250_remove, + .suspend = serial8250_suspend, + .resume = serial8250_resume, + .driver = { + .name = "serial8250", + .acpi_match_table = acpi_platform_serial_table, + .probe_type = PROBE_FORCE_SYNCHRONOUS, + }, +}; + +/* + * This "device" covers _all_ ISA 8250-compatible serial devices listed + * in the table in include/asm/serial.h. + */ +struct platform_device *serial8250_isa_devs; + +static int __init serial8250_init(void) +{ + int ret; + + if (nr_uarts == 0) + return -ENODEV; + + serial8250_isa_init_ports(); + + pr_info("Serial: 8250/16550 driver, %d ports, IRQ sharing %s\n", + nr_uarts, str_enabled_disabled(share_irqs)); + +#ifdef CONFIG_SPARC + ret = sunserial_register_minors(&serial8250_reg, UART_NR); +#else + serial8250_reg.nr = UART_NR; + ret = uart_register_driver(&serial8250_reg); +#endif + if (ret) + goto out; + + ret = serial8250_pnp_init(); + if (ret) + goto unreg_uart_drv; + + serial8250_isa_devs = platform_device_alloc("serial8250", PLAT8250_DEV_LEGACY); + if (!serial8250_isa_devs) { + ret = -ENOMEM; + goto unreg_pnp; + } + + ret = platform_device_add(serial8250_isa_devs); + if (ret) + goto put_dev; + + serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev); + + ret = platform_driver_register(&serial8250_isa_driver); + if (ret == 0) + goto out; + + platform_device_del(serial8250_isa_devs); +put_dev: + platform_device_put(serial8250_isa_devs); +unreg_pnp: + serial8250_pnp_exit(); +unreg_uart_drv: +#ifdef CONFIG_SPARC + sunserial_unregister_minors(&serial8250_reg, UART_NR); +#else + uart_unregister_driver(&serial8250_reg); +#endif +out: + return ret; +} +module_init(serial8250_init); + +static void __exit serial8250_exit(void) +{ + struct platform_device *isa_dev = serial8250_isa_devs; + + /* + * This tells serial8250_unregister_port() not to re-register + * the ports (thereby making serial8250_isa_driver permanently + * in use). + */ + serial8250_isa_devs = NULL; + + platform_driver_unregister(&serial8250_isa_driver); + platform_device_unregister(isa_dev); + + serial8250_pnp_exit(); + +#ifdef CONFIG_SPARC + sunserial_unregister_minors(&serial8250_reg, UART_NR); +#else + uart_unregister_driver(&serial8250_reg); +#endif +} +module_exit(serial8250_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 serial platform driver"); + +module_param_hw(share_irqs, bool, other, 0644); +MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices (unsafe)"); + +module_param(nr_uarts, uint, 0644); +MODULE_PARM_DESC(nr_uarts, "Maximum number of UARTs supported. (1-" __MODULE_STRING(CONFIG_SERIAL_8250_NR_UARTS) ")"); + +MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR); diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c new file mode 100644 index 000000000..6bfdeff5f --- /dev/null +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -0,0 +1,538 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Probe for 8250/16550-type ISAPNP serial ports. + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * Ported to the Linux PnP Layer - (C) Adam Belay. + */ +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/pm.h> +#include <linux/pnp.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/property.h> +#include <linux/serial_core.h> +#include <linux/bitops.h> + +#include <asm/byteorder.h> + +#include "8250.h" + +#define UNKNOWN_DEV 0x3000 +#define CIR_PORT 0x0800 + +static const struct pnp_device_id pnp_dev_table[] = { + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { .id = "AAC000F", .driver_data = 0 }, + /* Anchor Datacomm BV */ + /* SXPro 144 External Data Fax Modem Plug & Play */ + { .id = "ADC0001", .driver_data = 0 }, + /* SXPro 288 External Data Fax Modem Plug & Play */ + { .id = "ADC0002", .driver_data = 0 }, + /* PROLiNK 1456VH ISA PnP K56flex Fax Modem */ + { .id = "AEI0250", .driver_data = 0 }, + /* Actiontec ISA PNP 56K X2 Fax Modem */ + { .id = "AEI1240", .driver_data = 0 }, + /* Rockwell 56K ACF II Fax+Data+Voice Modem */ + { .id ="AKY1021", .driver_data = 0 /*SPCI_FL_NO_SHIRQ*/ }, + /* + * ALi Fast Infrared Controller + * Native driver (ali-ircc) is broken so at least + * it can be used with irtty-sir. + */ + { .id = "ALI5123", .driver_data = 0 }, + /* AZT3005 PnP SOUND DEVICE */ + { .id = "AZT4001", .driver_data = 0 }, + /* Best Data Products Inc. Smart One 336F PnP Modem */ + { .id = "BDP3336", .driver_data = 0 }, + /* Boca Research */ + /* Boca Complete Ofc Communicator 14.4 Data-FAX */ + { .id = "BRI0A49", .driver_data = 0 }, + /* Boca Research 33,600 ACF Modem */ + { .id = "BRI1400", .driver_data = 0 }, + /* Boca 33.6 Kbps Internal FD34FSVD */ + { .id = "BRI3400", .driver_data = 0 }, + /* Computer Peripherals Inc */ + /* EuroViVa CommCenter-33.6 SP PnP */ + { .id = "CPI4050", .driver_data = 0 }, + /* Creative Labs */ + /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ + { .id = "CTL3001", .driver_data = 0 }, + /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ + { .id = "CTL3011", .driver_data = 0 }, + /* Davicom ISA 33.6K Modem */ + { .id = "DAV0336", .driver_data = 0 }, + /* Creative */ + /* Creative Modem Blaster Flash56 DI5601-1 */ + { .id = "DMB1032", .driver_data = 0 }, + /* Creative Modem Blaster V.90 DI5660 */ + { .id = "DMB2001", .driver_data = 0 }, + /* E-Tech */ + /* E-Tech CyberBULLET PC56RVP */ + { .id = "ETT0002", .driver_data = 0 }, + /* FUJITSU */ + /* Fujitsu 33600 PnP-I2 R Plug & Play */ + { .id = "FUJ0202", .driver_data = 0 }, + /* Fujitsu FMV-FX431 Plug & Play */ + { .id = "FUJ0205", .driver_data = 0 }, + /* Fujitsu 33600 PnP-I4 R Plug & Play */ + { .id = "FUJ0206", .driver_data = 0 }, + /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ + { .id = "FUJ0209", .driver_data = 0 }, + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { .id = "GVC000F", .driver_data = 0 }, + /* Archtek SmartLink Modem 3334BRV 33.6K Data Fax Voice */ + { .id = "GVC0303", .driver_data = 0 }, + /* Hayes */ + /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ + { .id = "HAY0001", .driver_data = 0 }, + /* Hayes Optima 336 V.34 + FAX + Voice PnP */ + { .id = "HAY000C", .driver_data = 0 }, + /* Hayes Optima 336B V.34 + FAX + Voice PnP */ + { .id = "HAY000D", .driver_data = 0 }, + /* Hayes Accura 56K Ext Fax Modem PnP */ + { .id = "HAY5670", .driver_data = 0 }, + /* Hayes Accura 56K Ext Fax Modem PnP */ + { .id = "HAY5674", .driver_data = 0 }, + /* Hayes Accura 56K Fax Modem PnP */ + { .id = "HAY5675", .driver_data = 0 }, + /* Hayes 288, V.34 + FAX */ + { .id = "HAYF000", .driver_data = 0 }, + /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ + { .id = "HAYF001", .driver_data = 0 }, + /* IBM */ + /* IBM Thinkpad 701 Internal Modem Voice */ + { .id = "IBM0033", .driver_data = 0 }, + /* Intermec */ + /* Intermec CV60 touchscreen port */ + { .id = "PNP4972", .driver_data = 0 }, + /* Intertex */ + /* Intertex 28k8 33k6 Voice EXT PnP */ + { .id = "IXDC801", .driver_data = 0 }, + /* Intertex 33k6 56k Voice EXT PnP */ + { .id = "IXDC901", .driver_data = 0 }, + /* Intertex 28k8 33k6 Voice SP EXT PnP */ + { .id = "IXDD801", .driver_data = 0 }, + /* Intertex 33k6 56k Voice SP EXT PnP */ + { .id = "IXDD901", .driver_data = 0 }, + /* Intertex 28k8 33k6 Voice SP INT PnP */ + { .id = "IXDF401", .driver_data = 0 }, + /* Intertex 28k8 33k6 Voice SP EXT PnP */ + { .id = "IXDF801", .driver_data = 0 }, + /* Intertex 33k6 56k Voice SP EXT PnP */ + { .id = "IXDF901", .driver_data = 0 }, + /* Kortex International */ + /* KORTEX 28800 Externe PnP */ + { .id = "KOR4522", .driver_data = 0 }, + /* KXPro 33.6 Vocal ASVD PnP */ + { .id = "KORF661", .driver_data = 0 }, + /* Lasat */ + /* LASAT Internet 33600 PnP */ + { .id = "LAS4040", .driver_data = 0 }, + /* Lasat Safire 560 PnP */ + { .id = "LAS4540", .driver_data = 0 }, + /* Lasat Safire 336 PnP */ + { .id = "LAS5440", .driver_data = 0 }, + /* Microcom, Inc. */ + /* Microcom TravelPorte FAST V.34 Plug & Play */ + { .id = "MNP0281", .driver_data = 0 }, + /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ + { .id = "MNP0336", .driver_data = 0 }, + /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ + { .id = "MNP0339", .driver_data = 0 }, + /* Microcom DeskPorte 28.8P Plug & Play */ + { .id = "MNP0342", .driver_data = 0 }, + /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + { .id = "MNP0500", .driver_data = 0 }, + /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + { .id = "MNP0501", .driver_data = 0 }, + /* Microcom DeskPorte 28.8S Internal Plug & Play */ + { .id = "MNP0502", .driver_data = 0 }, + /* Motorola */ + /* Motorola BitSURFR Plug & Play */ + { .id = "MOT1105", .driver_data = 0 }, + /* Motorola TA210 Plug & Play */ + { .id = "MOT1111", .driver_data = 0 }, + /* Motorola HMTA 200 (ISDN) Plug & Play */ + { .id = "MOT1114", .driver_data = 0 }, + /* Motorola BitSURFR Plug & Play */ + { .id = "MOT1115", .driver_data = 0 }, + /* Motorola Lifestyle 28.8 Internal */ + { .id = "MOT1190", .driver_data = 0 }, + /* Motorola V.3400 Plug & Play */ + { .id = "MOT1501", .driver_data = 0 }, + /* Motorola Lifestyle 28.8 V.34 Plug & Play */ + { .id = "MOT1502", .driver_data = 0 }, + /* Motorola Power 28.8 V.34 Plug & Play */ + { .id = "MOT1505", .driver_data = 0 }, + /* Motorola ModemSURFR External 28.8 Plug & Play */ + { .id = "MOT1509", .driver_data = 0 }, + /* Motorola Premier 33.6 Desktop Plug & Play */ + { .id = "MOT150A", .driver_data = 0 }, + /* Motorola VoiceSURFR 56K External PnP */ + { .id = "MOT150F", .driver_data = 0 }, + /* Motorola ModemSURFR 56K External PnP */ + { .id = "MOT1510", .driver_data = 0 }, + /* Motorola ModemSURFR 56K Internal PnP */ + { .id = "MOT1550", .driver_data = 0 }, + /* Motorola ModemSURFR Internal 28.8 Plug & Play */ + { .id = "MOT1560", .driver_data = 0 }, + /* Motorola Premier 33.6 Internal Plug & Play */ + { .id = "MOT1580", .driver_data = 0 }, + /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ + { .id = "MOT15B0", .driver_data = 0 }, + /* Motorola VoiceSURFR 56K Internal PnP */ + { .id = "MOT15F0", .driver_data = 0 }, + /* Com 1 */ + /* Deskline K56 Phone System PnP */ + { .id = "MVX00A1", .driver_data = 0 }, + /* PC Rider K56 Phone System PnP */ + { .id = "MVX00F2", .driver_data = 0 }, + /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */ + { .id = "nEC8241", .driver_data = 0 }, + /* Pace 56 Voice Internal Plug & Play Modem */ + { .id = "PMC2430", .driver_data = 0 }, + /* Generic */ + /* Generic standard PC COM port */ + { .id = "PNP0500", .driver_data = 0 }, + /* Generic 16550A-compatible COM port */ + { .id = "PNP0501", .driver_data = 0 }, + /* Compaq 14400 Modem */ + { .id = "PNPC000", .driver_data = 0 }, + /* Compaq 2400/9600 Modem */ + { .id = "PNPC001", .driver_data = 0 }, + /* Dial-Up Networking Serial Cable between 2 PCs */ + { .id = "PNPC031", .driver_data = 0 }, + /* Dial-Up Networking Parallel Cable between 2 PCs */ + { .id = "PNPC032", .driver_data = 0 }, + /* Standard 9600 bps Modem */ + { .id = "PNPC100", .driver_data = 0 }, + /* Standard 14400 bps Modem */ + { .id = "PNPC101", .driver_data = 0 }, + /* Standard 28800 bps Modem*/ + { .id = "PNPC102", .driver_data = 0 }, + /* Standard Modem*/ + { .id = "PNPC103", .driver_data = 0 }, + /* Standard 9600 bps Modem*/ + { .id = "PNPC104", .driver_data = 0 }, + /* Standard 14400 bps Modem*/ + { .id = "PNPC105", .driver_data = 0 }, + /* Standard 28800 bps Modem*/ + { .id = "PNPC106", .driver_data = 0 }, + /* Standard Modem */ + { .id = "PNPC107", .driver_data = 0 }, + /* Standard 9600 bps Modem */ + { .id = "PNPC108", .driver_data = 0 }, + /* Standard 14400 bps Modem */ + { .id = "PNPC109", .driver_data = 0 }, + /* Standard 28800 bps Modem */ + { .id = "PNPC10A", .driver_data = 0 }, + /* Standard Modem */ + { .id = "PNPC10B", .driver_data = 0 }, + /* Standard 9600 bps Modem */ + { .id = "PNPC10C", .driver_data = 0 }, + /* Standard 14400 bps Modem */ + { .id = "PNPC10D", .driver_data = 0 }, + /* Standard 28800 bps Modem */ + { .id = "PNPC10E", .driver_data = 0 }, + /* Standard Modem */ + { .id = "PNPC10F", .driver_data = 0 }, + /* Standard PCMCIA Card Modem */ + { .id = "PNP2000", .driver_data = 0 }, + /* Rockwell */ + /* Modular Technology */ + /* Rockwell 33.6 DPF Internal PnP */ + /* Modular Technology 33.6 Internal PnP */ + { .id = "ROK0030", .driver_data = 0 }, + /* Kortex International */ + /* KORTEX 14400 Externe PnP */ + { .id = "ROK0100", .driver_data = 0 }, + /* Rockwell 28.8 */ + { .id = "ROK4120", .driver_data = 0 }, + /* Viking Components, Inc */ + /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ + { .id = "ROK4920", .driver_data = 0 }, + /* Rockwell */ + /* British Telecom */ + /* Modular Technology */ + /* Rockwell 33.6 DPF External PnP */ + /* BT Prologue 33.6 External PnP */ + /* Modular Technology 33.6 External PnP */ + { .id = "RSS00A0", .driver_data = 0 }, + /* Viking 56K FAX INT */ + { .id = "RSS0262", .driver_data = 0 }, + /* K56 par,VV,Voice,Speakphone,AudioSpan,PnP */ + { .id = "RSS0250", .driver_data = 0 }, + /* SupraExpress 28.8 Data/Fax PnP modem */ + { .id = "SUP1310", .driver_data = 0 }, + /* SupraExpress 336i PnP Voice Modem */ + { .id = "SUP1381", .driver_data = 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { .id = "SUP1421", .driver_data = 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { .id = "SUP1590", .driver_data = 0 }, + /* SupraExpress 336i Sp ASVD */ + { .id = "SUP1620", .driver_data = 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { .id = "SUP1760", .driver_data = 0 }, + /* SupraExpress 56i Sp Intl */ + { .id = "SUP2171", .driver_data = 0 }, + /* Phoebe Micro */ + /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ + { .id = "TEX0011", .driver_data = 0 }, + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { .id = "UAC000F", .driver_data = 0 }, + /* 3Com Corp. */ + /* Gateway Telepath IIvi 33.6 */ + { .id = "USR0000", .driver_data = 0 }, + /* U.S. Robotics Sporster 33.6K Fax INT PnP */ + { .id = "USR0002", .driver_data = 0 }, + /* Sportster Vi 14.4 PnP FAX Voicemail */ + { .id = "USR0004", .driver_data = 0 }, + /* U.S. Robotics 33.6K Voice INT PnP */ + { .id = "USR0006", .driver_data = 0 }, + /* U.S. Robotics 33.6K Voice EXT PnP */ + { .id = "USR0007", .driver_data = 0 }, + /* U.S. Robotics Courier V.Everything INT PnP */ + { .id = "USR0009", .driver_data = 0 }, + /* U.S. Robotics 33.6K Voice INT PnP */ + { .id = "USR2002", .driver_data = 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { .id = "USR2070", .driver_data = 0 }, + /* U.S. Robotics 56K Voice EXT PnP */ + { .id = "USR2080", .driver_data = 0 }, + /* U.S. Robotics 56K FAX INT */ + { .id = "USR3031", .driver_data = 0 }, + /* U.S. Robotics 56K FAX INT */ + { .id = "USR3050", .driver_data = 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { .id = "USR3070", .driver_data = 0 }, + /* U.S. Robotics 56K Voice EXT PnP */ + { .id = "USR3080", .driver_data = 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { .id = "USR3090", .driver_data = 0 }, + /* U.S. Robotics 56K Message */ + { .id = "USR9100", .driver_data = 0 }, + /* U.S. Robotics 56K FAX EXT PnP*/ + { .id = "USR9160", .driver_data = 0 }, + /* U.S. Robotics 56K FAX INT PnP*/ + { .id = "USR9170", .driver_data = 0 }, + /* U.S. Robotics 56K Voice EXT PnP*/ + { .id = "USR9180", .driver_data = 0 }, + /* U.S. Robotics 56K Voice INT PnP*/ + { .id = "USR9190", .driver_data = 0 }, + /* Wacom tablets */ + { .id = "WACFXXX", .driver_data = 0 }, + /* Compaq touchscreen */ + { .id = "FPI2002", .driver_data = 0 }, + /* Fujitsu Stylistic touchscreens */ + { .id = "FUJ02B2", .driver_data = 0 }, + { .id = "FUJ02B3", .driver_data = 0 }, + /* Fujitsu Stylistic LT touchscreens */ + { .id = "FUJ02B4", .driver_data = 0 }, + /* Passive Fujitsu Stylistic touchscreens */ + { .id = "FUJ02B6", .driver_data = 0 }, + { .id = "FUJ02B7", .driver_data = 0 }, + { .id = "FUJ02B8", .driver_data = 0 }, + { .id = "FUJ02B9", .driver_data = 0 }, + { .id = "FUJ02BC", .driver_data = 0 }, + /* Fujitsu Wacom Tablet PC device */ + { .id = "FUJ02E5", .driver_data = 0 }, + /* Fujitsu P-series tablet PC device */ + { .id = "FUJ02E6", .driver_data = 0 }, + /* Fujitsu Wacom 2FGT Tablet PC device */ + { .id = "FUJ02E7", .driver_data = 0 }, + /* Fujitsu Wacom 1FGT Tablet PC device */ + { .id = "FUJ02E9", .driver_data = 0 }, + /* + * LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 + * in disguise). + */ + { .id = "LTS0001", .driver_data = 0 }, + /* Rockwell's (PORALiNK) 33600 INT PNP */ + { .id = "WCI0003", .driver_data = 0 }, + /* Unknown PnP modems */ + { .id = "PNPCXXX", .driver_data = UNKNOWN_DEV }, + /* More unknown PnP modems */ + { .id = "PNPDXXX", .driver_data = UNKNOWN_DEV }, + /* + * Winbond CIR port, should not be probed. We should keep track of + * it to prevent the legacy serial driver from probing it. + */ + { .id = "WEC1022", .driver_data = CIR_PORT }, + /* + * SMSC IrCC SIR/FIR port, should not be probed by serial driver as + * well so its own driver can bind to it. + */ + { .id = "SMCF010", .driver_data = CIR_PORT }, + { } +}; + +MODULE_DEVICE_TABLE(pnp, pnp_dev_table); + +static const char *modem_names[] = { + "MODEM", "Modem", "modem", "FAX", "Fax", "fax", + "56K", "56k", "K56", "33.6", "28.8", "14.4", + "33,600", "28,800", "14,400", "33.600", "28.800", "14.400", + "33600", "28800", "14400", "V.90", "V.34", "V.32", NULL +}; + +static bool check_name(const char *name) +{ + const char **tmp; + + for (tmp = modem_names; *tmp; tmp++) + if (strstr(name, *tmp)) + return true; + + return false; +} + +static bool check_resources(struct pnp_dev *dev) +{ + static const resource_size_t base[] = {0x2f8, 0x3f8, 0x2e8, 0x3e8}; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(base); i++) { + if (pnp_possible_config(dev, IORESOURCE_IO, base[i], 8)) + return true; + } + + return false; +} + +/* + * Given a complete unknown PnP device, try to use some heuristics to + * detect modems. Currently use such heuristic set: + * - dev->name or dev->bus->name must contain "modem" substring; + * - device must have only one IO region (8 byte long) with base address + * 0x2e8, 0x3e8, 0x2f8 or 0x3f8. + * + * Such detection looks very ugly, but can detect at least some of numerous + * PnP modems, alternatively we must hardcode all modems in pnp_devices[] + * table. + */ +static int serial_pnp_guess_board(struct pnp_dev *dev) +{ + if (!(check_name(pnp_dev_name(dev)) || + (dev->card && check_name(dev->card->name)))) + return -ENODEV; + + if (check_resources(dev)) + return 0; + + return -ENODEV; +} + +static int +serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) +{ + struct uart_8250_port uart, *port; + int ret, flags = dev_id->driver_data; + long line; + + if (flags & UNKNOWN_DEV) { + ret = serial_pnp_guess_board(dev); + if (ret < 0) + return ret; + } + + memset(&uart, 0, sizeof(uart)); + if ((flags & CIR_PORT) && pnp_port_valid(dev, 2)) { + uart.port.iobase = pnp_port_start(dev, 2); + } else if (pnp_port_valid(dev, 0)) { + uart.port.iobase = pnp_port_start(dev, 0); + } else if (pnp_mem_valid(dev, 0)) { + uart.port.mapbase = pnp_mem_start(dev, 0); + uart.port.mapsize = pnp_mem_len(dev, 0); + uart.port.flags = UPF_IOREMAP; + } else + return -ENODEV; + + uart.port.uartclk = 1843200; + uart.port.dev = &dev->dev; + uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + + ret = uart_read_port_properties(&uart.port); + /* no interrupt -> fall back to polling */ + if (ret == -ENXIO) + ret = 0; + if (ret) + return ret; + + if (flags & CIR_PORT) { + uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE; + uart.port.type = PORT_8250_CIR; + } + + dev_dbg(&dev->dev, + "Setup PNP port: port %#lx, mem %#llx, size %#llx, irq %u, type %u\n", + uart.port.iobase, (unsigned long long)uart.port.mapbase, + (unsigned long long)uart.port.mapsize, uart.port.irq, uart.port.iotype); + + line = serial8250_register_8250_port(&uart); + if (line < 0 || (flags & CIR_PORT)) + return -ENODEV; + + port = serial8250_get_port(line); + if (uart_console(&port->port)) + dev->capabilities |= PNP_CONSOLE; + + pnp_set_drvdata(dev, (void *)line); + return 0; +} + +static void serial_pnp_remove(struct pnp_dev *dev) +{ + long line = (long)pnp_get_drvdata(dev); + + dev->capabilities &= ~PNP_CONSOLE; + serial8250_unregister_port(line); +} + +static int serial_pnp_suspend(struct device *dev) +{ + long line = (long)dev_get_drvdata(dev); + + serial8250_suspend_port(line); + return 0; +} + +static int serial_pnp_resume(struct device *dev) +{ + long line = (long)dev_get_drvdata(dev); + + serial8250_resume_port(line); + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(serial_pnp_pm_ops, serial_pnp_suspend, serial_pnp_resume); + +static struct pnp_driver serial_pnp_driver = { + .name = "serial", + .probe = serial_pnp_probe, + .remove = serial_pnp_remove, + .driver = { + .pm = pm_sleep_ptr(&serial_pnp_pm_ops), + .probe_type = PROBE_FORCE_SYNCHRONOUS, + }, + .id_table = pnp_dev_table, +}; + +int serial8250_pnp_init(void) +{ + return pnp_register_driver(&serial_pnp_driver); +} + +void serial8250_pnp_exit(void) +{ + pnp_unregister_driver(&serial_pnp_driver); +} + diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c new file mode 100644 index 000000000..38fa45e74 --- /dev/null +++ b/drivers/tty/serial/8250/8250_port.c @@ -0,0 +1,3635 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Base port operations for 8250/16550-type serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * Split from 8250_core.c, Copyright (C) 2001 Russell King. + * + * A note about mapbase / membase + * + * mapbase is the physical address of the IO port. + * membase is an 'ioremapped' cookie. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/console.h> +#include <linux/gpio/consumer.h> +#include <linux/lockdep.h> +#include <linux/sysrq.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/tty.h> +#include <linux/ratelimit.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serial_8250.h> +#include <linux/nmi.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/pm_runtime.h> +#include <linux/ktime.h> + +#include <asm/io.h> +#include <asm/irq.h> + +#include "8250.h" + +/* + * Here we define the default xmit fifo size used for each type of UART. + */ +static const struct serial8250_config uart_config[] = { + [PORT_UNKNOWN] = { + .name = "unknown", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_8250] = { + .name = "8250", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_16450] = { + .name = "16450", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_16550] = { + .name = "16550", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_16550A] = { + .name = "16550A", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .rxtrig_bytes = {1, 4, 8, 14}, + .flags = UART_CAP_FIFO, + }, + [PORT_CIRRUS] = { + .name = "Cirrus", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_16650] = { + .name = "ST16650", + .fifo_size = 1, + .tx_loadsz = 1, + .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, + }, + [PORT_16650V2] = { + .name = "ST16650V2", + .fifo_size = 32, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | + UART_FCR_T_TRIG_00, + .rxtrig_bytes = {8, 16, 24, 28}, + .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, + }, + [PORT_16750] = { + .name = "TI16750", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | + UART_FCR7_64BYTE, + .rxtrig_bytes = {1, 16, 32, 56}, + .flags = UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_AFE, + }, + [PORT_STARTECH] = { + .name = "Startech", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_16C950] = { + .name = "16C950/954", + .fifo_size = 128, + .tx_loadsz = 128, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01, + .rxtrig_bytes = {16, 32, 112, 120}, + /* UART_CAP_EFR breaks billionon CF bluetooth card. */ + .flags = UART_CAP_FIFO | UART_CAP_SLEEP, + }, + [PORT_16654] = { + .name = "ST16654", + .fifo_size = 64, + .tx_loadsz = 32, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | + UART_FCR_T_TRIG_10, + .rxtrig_bytes = {8, 16, 56, 60}, + .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, + }, + [PORT_16850] = { + .name = "XR16850", + .fifo_size = 128, + .tx_loadsz = 128, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, + }, + [PORT_RSA] = { + .name = "RSA", + .fifo_size = 2048, + .tx_loadsz = 2048, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11, + .flags = UART_CAP_FIFO, + }, + [PORT_NS16550A] = { + .name = "NS16550A", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_NATSEMI, + }, + [PORT_XSCALE] = { + .name = "XScale", + .fifo_size = 32, + .tx_loadsz = 32, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_UUE | UART_CAP_RTOIE, + }, + [PORT_OCTEON] = { + .name = "OCTEON", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO, + }, + [PORT_U6_16550A] = { + .name = "U6_16550A", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, + [PORT_TEGRA] = { + .name = "Tegra", + .fifo_size = 32, + .tx_loadsz = 8, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | + UART_FCR_T_TRIG_01, + .rxtrig_bytes = {1, 4, 8, 14}, + .flags = UART_CAP_FIFO | UART_CAP_RTOIE, + }, + [PORT_XR17D15X] = { + .name = "XR17D15X", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR | + UART_CAP_SLEEP, + }, + [PORT_XR17V35X] = { + .name = "XR17V35X", + .fifo_size = 256, + .tx_loadsz = 256, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11 | + UART_FCR_T_TRIG_11, + .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR | + UART_CAP_SLEEP, + }, + [PORT_LPC3220] = { + .name = "LPC3220", + .fifo_size = 64, + .tx_loadsz = 32, + .fcr = UART_FCR_DMA_SELECT | UART_FCR_ENABLE_FIFO | + UART_FCR_R_TRIG_00 | UART_FCR_T_TRIG_00, + .flags = UART_CAP_FIFO, + }, + [PORT_BRCM_TRUMANAGE] = { + .name = "TruManage", + .fifo_size = 1, + .tx_loadsz = 1024, + .flags = UART_CAP_HFIFO, + }, + [PORT_8250_CIR] = { + .name = "CIR port" + }, + [PORT_ALTR_16550_F32] = { + .name = "Altera 16550 FIFO32", + .fifo_size = 32, + .tx_loadsz = 32, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .rxtrig_bytes = {1, 8, 16, 30}, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, + [PORT_ALTR_16550_F64] = { + .name = "Altera 16550 FIFO64", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .rxtrig_bytes = {1, 16, 32, 62}, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, + [PORT_ALTR_16550_F128] = { + .name = "Altera 16550 FIFO128", + .fifo_size = 128, + .tx_loadsz = 128, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .rxtrig_bytes = {1, 32, 64, 126}, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, + /* + * tx_loadsz is set to 63-bytes instead of 64-bytes to implement + * workaround of errata A-008006 which states that tx_loadsz should + * be configured less than Maximum supported fifo bytes. + */ + [PORT_16550A_FSL64] = { + .name = "16550A_FSL64", + .fifo_size = 64, + .tx_loadsz = 63, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | + UART_FCR7_64BYTE, + .flags = UART_CAP_FIFO | UART_CAP_NOTEMT, + }, + [PORT_RT2880] = { + .name = "Palmchip BK-3103", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .rxtrig_bytes = {1, 4, 8, 14}, + .flags = UART_CAP_FIFO, + }, + [PORT_DA830] = { + .name = "TI DA8xx/66AK2x", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_DMA_SELECT | UART_FCR_ENABLE_FIFO | + UART_FCR_R_TRIG_10, + .rxtrig_bytes = {1, 4, 8, 14}, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, + [PORT_MTK_BTIF] = { + .name = "MediaTek BTIF", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, + .flags = UART_CAP_FIFO, + }, + [PORT_NPCM] = { + .name = "Nuvoton 16550", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, + .rxtrig_bytes = {1, 4, 8, 14}, + .flags = UART_CAP_FIFO, + }, + [PORT_SUNIX] = { + .name = "Sunix", + .fifo_size = 128, + .tx_loadsz = 128, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .rxtrig_bytes = {1, 32, 64, 112}, + .flags = UART_CAP_FIFO | UART_CAP_SLEEP, + }, + [PORT_ASPEED_VUART] = { + .name = "ASPEED VUART", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00, + .rxtrig_bytes = {1, 4, 8, 14}, + .flags = UART_CAP_FIFO, + }, + [PORT_MCHP16550A] = { + .name = "MCHP16550A", + .fifo_size = 256, + .tx_loadsz = 256, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01, + .rxtrig_bytes = {2, 66, 130, 194}, + .flags = UART_CAP_FIFO, + }, + [PORT_BCM7271] = { + .name = "Broadcom BCM7271 UART", + .fifo_size = 32, + .tx_loadsz = 32, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01, + .rxtrig_bytes = {1, 8, 16, 30}, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, + [PORT_MUEX50] = { + .name = "Moxa MUEx50 UART", + .fifo_size = 128, + .tx_loadsz = 128, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .rxtrig_bytes = {15, 31, 63, 111}, + .flags = UART_CAP_FIFO, + }, +}; + +/* Uart divisor latch read */ +static u32 default_serial_dl_read(struct uart_8250_port *up) +{ + /* Assign these in pieces to truncate any bits above 7. */ + unsigned char dll = serial_in(up, UART_DLL); + unsigned char dlm = serial_in(up, UART_DLM); + + return dll | dlm << 8; +} + +/* Uart divisor latch write */ +static void default_serial_dl_write(struct uart_8250_port *up, u32 value) +{ + serial_out(up, UART_DLL, value & 0xff); + serial_out(up, UART_DLM, value >> 8 & 0xff); +} + +#ifdef CONFIG_HAS_IOPORT +static u32 hub6_serial_in(struct uart_port *p, unsigned int offset) +{ + offset = offset << p->regshift; + outb(p->hub6 - 1 + offset, p->iobase); + return inb(p->iobase + 1); +} + +static void hub6_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ + offset = offset << p->regshift; + outb(p->hub6 - 1 + offset, p->iobase); + outb(value, p->iobase + 1); +} +#endif /* CONFIG_HAS_IOPORT */ + +static u32 mem_serial_in(struct uart_port *p, unsigned int offset) +{ + offset = offset << p->regshift; + return readb(p->membase + offset); +} + +static void mem_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ + offset = offset << p->regshift; + writeb(value, p->membase + offset); +} + +static void mem16_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ + offset = offset << p->regshift; + writew(value, p->membase + offset); +} + +static u32 mem16_serial_in(struct uart_port *p, unsigned int offset) +{ + offset = offset << p->regshift; + return readw(p->membase + offset); +} + +static void mem32_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ + offset = offset << p->regshift; + writel(value, p->membase + offset); +} + +static u32 mem32_serial_in(struct uart_port *p, unsigned int offset) +{ + offset = offset << p->regshift; + return readl(p->membase + offset); +} + +static void mem32be_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ + offset = offset << p->regshift; + iowrite32be(value, p->membase + offset); +} + +static u32 mem32be_serial_in(struct uart_port *p, unsigned int offset) +{ + offset = offset << p->regshift; + return ioread32be(p->membase + offset); +} + +#ifdef CONFIG_HAS_IOPORT +static u32 io_serial_in(struct uart_port *p, unsigned int offset) +{ + offset = offset << p->regshift; + return inb(p->iobase + offset); +} + +static void io_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ + offset = offset << p->regshift; + outb(value, p->iobase + offset); +} +#endif +static u32 no_serial_in(struct uart_port *p, unsigned int offset) +{ + return ~0U; +} + +static void no_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ +} + +static int serial8250_default_handle_irq(struct uart_port *port); + +static void set_io_from_upio(struct uart_port *p) +{ + struct uart_8250_port *up = up_to_u8250p(p); + + up->dl_read = default_serial_dl_read; + up->dl_write = default_serial_dl_write; + + switch (p->iotype) { +#ifdef CONFIG_HAS_IOPORT + case UPIO_HUB6: + p->serial_in = hub6_serial_in; + p->serial_out = hub6_serial_out; + break; +#endif + + case UPIO_MEM: + p->serial_in = mem_serial_in; + p->serial_out = mem_serial_out; + break; + + case UPIO_MEM16: + p->serial_in = mem16_serial_in; + p->serial_out = mem16_serial_out; + break; + + case UPIO_MEM32: + p->serial_in = mem32_serial_in; + p->serial_out = mem32_serial_out; + break; + + case UPIO_MEM32BE: + p->serial_in = mem32be_serial_in; + p->serial_out = mem32be_serial_out; + break; +#ifdef CONFIG_HAS_IOPORT + case UPIO_PORT: + p->serial_in = io_serial_in; + p->serial_out = io_serial_out; + break; +#endif + case UPIO_AU: + break; + default: + WARN(p->iotype != UPIO_PORT || p->iobase, + "Unsupported UART type %x\n", p->iotype); + p->serial_in = no_serial_in; + p->serial_out = no_serial_out; + } + /* Remember loaded iotype */ + up->cur_iotype = p->iotype; + p->handle_irq = serial8250_default_handle_irq; +} + +static void +serial_port_out_sync(struct uart_port *p, int offset, int value) +{ + if (uart_iotype_mmio(p->iotype)) { + p->serial_out(p, offset, value); + p->serial_in(p, UART_LCR); /* safe, no side-effects */ + } else { + p->serial_out(p, offset, value); + } +} + +/* + * FIFO support. + */ +void serial8250_clear_fifos(struct uart_8250_port *p) +{ + if (p->capabilities & UART_CAP_FIFO) { + serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_out(p, UART_FCR, 0); + } +} +EXPORT_SYMBOL_NS_GPL(serial8250_clear_fifos, "SERIAL_8250"); + +static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t); +static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t); + +void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p) +{ + serial8250_clear_fifos(p); + serial_out(p, UART_FCR, p->fcr); +} +EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos); + +void serial8250_rpm_get(struct uart_8250_port *p) +{ + if (!(p->capabilities & UART_CAP_RPM)) + return; + pm_runtime_get_sync(p->port.dev); +} +EXPORT_SYMBOL_GPL(serial8250_rpm_get); + +void serial8250_rpm_put(struct uart_8250_port *p) +{ + if (!(p->capabilities & UART_CAP_RPM)) + return; + pm_runtime_mark_last_busy(p->port.dev); + pm_runtime_put_autosuspend(p->port.dev); +} +EXPORT_SYMBOL_GPL(serial8250_rpm_put); + +/** + * serial8250_em485_init() - put uart_8250_port into rs485 emulating + * @p: uart_8250_port port instance + * + * The function is used to start rs485 software emulating on the + * &struct uart_8250_port* @p. Namely, RTS is switched before/after + * transmission. The function is idempotent, so it is safe to call it + * multiple times. + * + * The caller MUST enable interrupt on empty shift register before + * calling serial8250_em485_init(). This interrupt is not a part of + * 8250 standard, but implementation defined. + * + * The function is supposed to be called from .rs485_config callback + * or from any other callback protected with p->port.lock spinlock. + * + * See also serial8250_em485_destroy() + * + * Return 0 - success, -errno - otherwise + */ +static int serial8250_em485_init(struct uart_8250_port *p) +{ + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&p->port.lock); + + if (p->em485) + goto deassert_rts; + + p->em485 = kmalloc_obj(struct uart_8250_em485, GFP_ATOMIC); + if (!p->em485) + return -ENOMEM; + + hrtimer_setup(&p->em485->stop_tx_timer, &serial8250_em485_handle_stop_tx, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + hrtimer_setup(&p->em485->start_tx_timer, &serial8250_em485_handle_start_tx, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + p->em485->port = p; + p->em485->active_timer = NULL; + p->em485->tx_stopped = true; + +deassert_rts: + if (p->em485->tx_stopped) + p->rs485_stop_tx(p, true); + + return 0; +} + +/** + * serial8250_em485_destroy() - put uart_8250_port into normal state + * @p: uart_8250_port port instance + * + * The function is used to stop rs485 software emulating on the + * &struct uart_8250_port* @p. The function is idempotent, so it is safe to + * call it multiple times. + * + * The function is supposed to be called from .rs485_config callback + * or from any other callback protected with p->port.lock spinlock. + * + * See also serial8250_em485_init() + */ +void serial8250_em485_destroy(struct uart_8250_port *p) +{ + if (!p->em485) + return; + + hrtimer_cancel(&p->em485->start_tx_timer); + hrtimer_cancel(&p->em485->stop_tx_timer); + + kfree(p->em485); + p->em485 = NULL; +} +EXPORT_SYMBOL_GPL(serial8250_em485_destroy); + +struct serial_rs485 serial8250_em485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND | + SER_RS485_TERMINATE_BUS | SER_RS485_RX_DURING_TX, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, +}; +EXPORT_SYMBOL_GPL(serial8250_em485_supported); + +/** + * serial8250_em485_config() - generic ->rs485_config() callback + * @port: uart port + * @termios: termios structure + * @rs485: rs485 settings + * + * Generic callback usable by 8250 uart drivers to activate rs485 settings + * if the uart is incapable of driving RTS as a Transmit Enable signal in + * hardware, relying on software emulation instead. + */ +int serial8250_em485_config(struct uart_port *port, struct ktermios *termios, + struct serial_rs485 *rs485) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + /* + * Both serial8250_em485_init() and serial8250_em485_destroy() + * are idempotent. + */ + if (rs485->flags & SER_RS485_ENABLED) + return serial8250_em485_init(up); + + serial8250_em485_destroy(up); + return 0; +} +EXPORT_SYMBOL_GPL(serial8250_em485_config); + +/* + * These two wrappers ensure that enable_runtime_pm_tx() can be called more than + * once and disable_runtime_pm_tx() will still disable RPM because the fifo is + * empty and the HW can idle again. + */ +static void serial8250_rpm_get_tx(struct uart_8250_port *p) +{ + unsigned char rpm_active; + + if (!(p->capabilities & UART_CAP_RPM)) + return; + + rpm_active = xchg(&p->rpm_tx_active, 1); + if (rpm_active) + return; + pm_runtime_get_sync(p->port.dev); +} + +static void serial8250_rpm_put_tx(struct uart_8250_port *p) +{ + unsigned char rpm_active; + + if (!(p->capabilities & UART_CAP_RPM)) + return; + + rpm_active = xchg(&p->rpm_tx_active, 0); + if (!rpm_active) + return; + pm_runtime_mark_last_busy(p->port.dev); + pm_runtime_put_autosuspend(p->port.dev); +} + +/* + * IER sleep support. UARTs which have EFRs need the "extended + * capability" bit enabled. Note that on XR16C850s, we need to + * reset LCR to write to IER. + */ +static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) +{ + unsigned char lcr = 0, efr = 0; + + guard(serial8250_rpm)(p); + + if (!(p->capabilities & UART_CAP_SLEEP)) + return; + + /* Synchronize UART_IER access against the console. */ + guard(uart_port_lock_irq)(&p->port); + + if (p->capabilities & UART_CAP_EFR) { + lcr = serial_in(p, UART_LCR); + efr = serial_in(p, UART_EFR); + serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(p, UART_EFR, UART_EFR_ECB); + serial_out(p, UART_LCR, 0); + } + serial_out(p, UART_IER, sleep ? UART_IERX_SLEEP : 0); + if (p->capabilities & UART_CAP_EFR) { + serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(p, UART_EFR, efr); + serial_out(p, UART_LCR, lcr); + } +} + +/* Clear the interrupt registers. */ +static void serial8250_clear_interrupts(struct uart_port *port) +{ + serial_port_in(port, UART_LSR); + serial_port_in(port, UART_RX); + serial_port_in(port, UART_IIR); + serial_port_in(port, UART_MSR); +} + +/* + * Only to be directly used by serial8250_console_write() and + * serial8250_put_poll_char(), which do not require the port lock. + * Use serial8250_clear_IER() instead for all other cases. + */ +static void __serial8250_clear_IER(struct uart_8250_port *up) +{ + if (up->capabilities & UART_CAP_UUE) + serial_out(up, UART_IER, UART_IER_UUE); + else + serial_out(up, UART_IER, 0); +} + +static inline void serial8250_clear_IER(struct uart_8250_port *up) +{ + /* Port locked to synchronize UART_IER access against the console */ + lockdep_assert_held_once(&up->port.lock); + + __serial8250_clear_IER(up); +} + +/* + * This is a quickie test to see how big the FIFO is. + * It doesn't work at all the time, more's the pity. + */ +static int size_fifo(struct uart_8250_port *up) +{ + unsigned char old_fcr, old_mcr, old_lcr; + u32 old_dl; + int count; + + old_lcr = serial_in(up, UART_LCR); + serial_out(up, UART_LCR, 0); + old_fcr = serial_in(up, UART_FCR); + old_mcr = serial8250_in_MCR(up); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial8250_out_MCR(up, UART_MCR_LOOP); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + old_dl = serial_dl_read(up); + serial_dl_write(up, 0x0001); + serial_out(up, UART_LCR, UART_LCR_WLEN8); + for (count = 0; count < 256; count++) + serial_out(up, UART_TX, count); + mdelay(20);/* FIXME - schedule_timeout */ + for (count = 0; (serial_in(up, UART_LSR) & UART_LSR_DR) && + (count < 256); count++) + serial_in(up, UART_RX); + serial_out(up, UART_FCR, old_fcr); + serial8250_out_MCR(up, old_mcr); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_dl_write(up, old_dl); + serial_out(up, UART_LCR, old_lcr); + + return count; +} + +/* + * Read UART ID using the divisor method - set DLL and DLM to zero + * and the revision will be in DLL and device type in DLM. We + * preserve the device state across this. + */ +static unsigned int autoconfig_read_divisor_id(struct uart_8250_port *p) +{ + unsigned char old_lcr; + unsigned int id, old_dl; + + old_lcr = serial_in(p, UART_LCR); + serial_out(p, UART_LCR, UART_LCR_CONF_MODE_A); + old_dl = serial_dl_read(p); + serial_dl_write(p, 0); + id = serial_dl_read(p); + serial_dl_write(p, old_dl); + + serial_out(p, UART_LCR, old_lcr); + + return id; +} + +/* + * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. + * When this function is called we know it is at least a StarTech + * 16650 V2, but it might be one of several StarTech UARTs, or one of + * its clones. (We treat the broken original StarTech 16650 V1 as a + * 16550, and why not? Startech doesn't seem to even acknowledge its + * existence.) + * + * What evil have men's minds wrought... + */ +static void autoconfig_has_efr(struct uart_8250_port *up) +{ + unsigned int id1, id2, id3, rev; + + /* + * Everything with an EFR has SLEEP + */ + up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP; + + /* + * First we check to see if it's an Oxford Semiconductor UART. + * + * If we have to do this here because some non-National + * Semiconductor clone chips lock up if you try writing to the + * LSR register (which serial_icr_read does) + */ + + /* + * Check for Oxford Semiconductor 16C950. + * + * EFR [4] must be set else this test fails. + * + * This shouldn't be necessary, but Mike Hudson (Exoray@isys.ca) + * claims that it's needed for 952 dual UART's (which are not + * recommended for new designs). + */ + up->acr = 0; + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, UART_EFR_ECB); + serial_out(up, UART_LCR, 0x00); + id1 = serial_icr_read(up, UART_ID1); + id2 = serial_icr_read(up, UART_ID2); + id3 = serial_icr_read(up, UART_ID3); + rev = serial_icr_read(up, UART_REV); + + if (id1 == 0x16 && id2 == 0xC9 && + (id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) { + up->port.type = PORT_16C950; + + /* + * Enable work around for the Oxford Semiconductor 952 rev B + * chip which causes it to seriously miscalculate baud rates + * when DLL is 0. + */ + if (id3 == 0x52 && rev == 0x01) + up->bugs |= UART_BUG_QUOT; + return; + } + + /* + * We check for a XR16C850 by setting DLL and DLM to 0, and then + * reading back DLL and DLM. The chip type depends on the DLM + * value read back: + * 0x10 - XR16C850 and the DLL contains the chip revision. + * 0x12 - XR16C2850. + * 0x14 - XR16C854. + */ + id1 = autoconfig_read_divisor_id(up); + + id2 = id1 >> 8; + if (id2 == 0x10 || id2 == 0x12 || id2 == 0x14) { + up->port.type = PORT_16850; + return; + } + + /* + * It wasn't an XR16C850. + * + * We distinguish between the '654 and the '650 by counting + * how many bytes are in the FIFO. I'm using this for now, + * since that's the technique that was sent to me in the + * serial driver update, but I'm not convinced this works. + * I've had problems doing this in the past. -TYT + */ + if (size_fifo(up) == 64) + up->port.type = PORT_16654; + else + up->port.type = PORT_16650V2; +} + +/* + * We detected a chip without a FIFO. Only two fall into + * this category - the original 8250 and the 16450. The + * 16450 has a scratch register (accessible with LCR=0) + */ +static void autoconfig_8250(struct uart_8250_port *up) +{ + unsigned char scratch, status1, status2; + + up->port.type = PORT_8250; + + scratch = serial_in(up, UART_SCR); + serial_out(up, UART_SCR, 0xa5); + status1 = serial_in(up, UART_SCR); + serial_out(up, UART_SCR, 0x5a); + status2 = serial_in(up, UART_SCR); + serial_out(up, UART_SCR, scratch); + + if (status1 == 0xa5 && status2 == 0x5a) + up->port.type = PORT_16450; +} + +static int broken_efr(struct uart_8250_port *up) +{ + /* + * Exar ST16C2550 "A2" devices incorrectly detect as + * having an EFR, and report an ID of 0x0201. See + * http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-11/4812.html + */ + if (autoconfig_read_divisor_id(up) == 0x0201 && size_fifo(up) == 16) + return 1; + + return 0; +} + +/* + * We know that the chip has FIFOs. Does it have an EFR? The + * EFR is located in the same register position as the IIR and + * we know the top two bits of the IIR are currently set. The + * EFR should contain zero. Try to read the EFR. + */ +static void autoconfig_16550a(struct uart_8250_port *up) +{ + unsigned char status1, status2; + unsigned int iersave; + + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&up->port.lock); + + up->port.type = PORT_16550A; + up->capabilities |= UART_CAP_FIFO; + + if (!IS_ENABLED(CONFIG_SERIAL_8250_16550A_VARIANTS) && + !(up->port.flags & UPF_FULL_PROBE)) + return; + + /* + * Check for presence of the EFR when DLAB is set. + * Only ST16C650V1 UARTs pass this test. + */ + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + if (serial_in(up, UART_EFR) == 0) { + serial_out(up, UART_EFR, 0xA8); + if (serial_in(up, UART_EFR) != 0) { + up->port.type = PORT_16650; + up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP; + } else { + serial_out(up, UART_LCR, 0); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR7_64BYTE); + status1 = serial_in(up, UART_IIR) & UART_IIR_FIFO_ENABLED_16750; + serial_out(up, UART_FCR, 0); + serial_out(up, UART_LCR, 0); + + if (status1 == UART_IIR_FIFO_ENABLED_16750) + up->port.type = PORT_16550A_FSL64; + } + serial_out(up, UART_EFR, 0); + return; + } + + /* + * Maybe it requires 0xbf to be written to the LCR. + * (other ST16C650V2 UARTs, TI16C752A, etc) + */ + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + if (serial_in(up, UART_EFR) == 0 && !broken_efr(up)) { + autoconfig_has_efr(up); + return; + } + + /* + * Check for a National Semiconductor SuperIO chip. + * Attempt to switch to bank 2, read the value of the LOOP bit + * from EXCR1. Switch back to bank 0, change it in MCR. Then + * switch back to bank 2, read it from EXCR1 again and check + * it's changed. If so, set baud_base in EXCR2 to 921600. -- dwmw2 + */ + serial_out(up, UART_LCR, 0); + status1 = serial8250_in_MCR(up); + serial_out(up, UART_LCR, 0xE0); + status2 = serial_in(up, 0x02); /* EXCR1 */ + + if (!((status2 ^ status1) & UART_MCR_LOOP)) { + serial_out(up, UART_LCR, 0); + serial8250_out_MCR(up, status1 ^ UART_MCR_LOOP); + serial_out(up, UART_LCR, 0xE0); + status2 = serial_in(up, 0x02); /* EXCR1 */ + serial_out(up, UART_LCR, 0); + serial8250_out_MCR(up, status1); + + if ((status2 ^ status1) & UART_MCR_LOOP) { + unsigned short quot; + + serial_out(up, UART_LCR, 0xE0); + + quot = serial_dl_read(up); + quot <<= 3; + + if (ns16550a_goto_highspeed(up)) + serial_dl_write(up, quot); + + serial_out(up, UART_LCR, 0); + + up->port.uartclk = 921600*16; + up->port.type = PORT_NS16550A; + up->capabilities |= UART_NATSEMI; + return; + } + } + + /* + * No EFR. Try to detect a TI16750, which only sets bit 5 of + * the IIR when 64 byte FIFO mode is enabled when DLAB is set. + * Try setting it with and without DLAB set. Cheap clones + * set bit 5 without DLAB set. + */ + serial_out(up, UART_LCR, 0); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + status1 = serial_in(up, UART_IIR) & UART_IIR_FIFO_ENABLED_16750; + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + status2 = serial_in(up, UART_IIR) & UART_IIR_FIFO_ENABLED_16750; + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + + serial_out(up, UART_LCR, 0); + + if (status1 == UART_IIR_FIFO_ENABLED_16550A && + status2 == UART_IIR_FIFO_ENABLED_16750) { + up->port.type = PORT_16750; + up->capabilities |= UART_CAP_AFE | UART_CAP_SLEEP; + return; + } + + /* + * Try writing and reading the UART_IER_UUE bit (b6). + * If it works, this is probably one of the Xscale platform's + * internal UARTs. + * We're going to explicitly set the UUE bit to 0 before + * trying to write and read a 1 just to make sure it's not + * already a 1 and maybe locked there before we even start. + */ + iersave = serial_in(up, UART_IER); + serial_out(up, UART_IER, iersave & ~UART_IER_UUE); + if (!(serial_in(up, UART_IER) & UART_IER_UUE)) { + /* + * OK it's in a known zero state, try writing and reading + * without disturbing the current state of the other bits. + */ + serial_out(up, UART_IER, iersave | UART_IER_UUE); + if (serial_in(up, UART_IER) & UART_IER_UUE) { + /* + * It's an Xscale. + * We'll leave the UART_IER_UUE bit set to 1 (enabled). + */ + up->port.type = PORT_XSCALE; + up->capabilities |= UART_CAP_UUE | UART_CAP_RTOIE; + return; + } + } + serial_out(up, UART_IER, iersave); + + /* + * We distinguish between 16550A and U6 16550A by counting + * how many bytes are in the FIFO. + */ + if (up->port.type == PORT_16550A && size_fifo(up) == 64) { + up->port.type = PORT_U6_16550A; + up->capabilities |= UART_CAP_AFE; + } +} + +/* + * This routine is called by rs_init() to initialize a specific serial + * port. It determines what type of UART chip this serial port is + * using: 8250, 16450, 16550, 16550A. The important question is + * whether or not this UART is a 16550A or not, since this will + * determine whether or not we can use its FIFO features or not. + */ +static void autoconfig(struct uart_8250_port *up) +{ + unsigned char status1, scratch, scratch2, scratch3; + unsigned char save_lcr, save_mcr; + struct uart_port *port = &up->port; + unsigned long flags; + unsigned int old_capabilities; + + if (!port->iobase && !port->mapbase && !port->membase) + return; + + /* + * We really do need global IRQs disabled here - we're going to + * be frobbing the chips IRQ enable register to see if it exists. + * + * Synchronize UART_IER access against the console. + */ + uart_port_lock_irqsave(port, &flags); + + up->capabilities = 0; + up->bugs = 0; + + if (!(port->flags & UPF_BUGGY_UART)) { + /* + * Do a simple existence test first; if we fail this, + * there's no point trying anything else. + * + * 0x80 is used as a nonsense port to prevent against + * false positives due to ISA bus float. The + * assumption is that 0x80 is a non-existent port; + * which should be safe since include/asm/io.h also + * makes this assumption. + * + * Note: this is safe as long as MCR bit 4 is clear + * and the device is in "PC" mode. + */ + scratch = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); +#if defined(__i386__) && defined(CONFIG_HAS_IOPORT) + outb(0xff, 0x080); +#endif + /* + * Mask out IER[7:4] bits for test as some UARTs (e.g. TL + * 16C754B) allow only to modify them if an EFR bit is set. + */ + scratch2 = serial_in(up, UART_IER) & UART_IER_ALL_INTR; + serial_out(up, UART_IER, UART_IER_ALL_INTR); +#if defined(__i386__) && defined(CONFIG_HAS_IOPORT) + outb(0, 0x080); +#endif + scratch3 = serial_in(up, UART_IER) & UART_IER_ALL_INTR; + serial_out(up, UART_IER, scratch); + if (scratch2 != 0 || scratch3 != UART_IER_ALL_INTR) { + /* + * We failed; there's nothing here + */ + uart_port_unlock_irqrestore(port, flags); + return; + } + } + + save_mcr = serial8250_in_MCR(up); + save_lcr = serial_in(up, UART_LCR); + + /* + * Check to see if a UART is really there. Certain broken + * internal modems based on the Rockwell chipset fail this + * test, because they apparently don't implement the loopback + * test mode. So this test is skipped on the COM 1 through + * COM 4 ports. This *should* be safe, since no board + * manufacturer would be stupid enough to design a board + * that conflicts with COM 1-4 --- we hope! + */ + if (!(port->flags & UPF_SKIP_TEST)) { + serial8250_out_MCR(up, UART_MCR_LOOP | UART_MCR_OUT2 | UART_MCR_RTS); + status1 = serial_in(up, UART_MSR) & UART_MSR_STATUS_BITS; + serial8250_out_MCR(up, save_mcr); + if (status1 != (UART_MSR_DCD | UART_MSR_CTS)) { + uart_port_unlock_irqrestore(port, flags); + return; + } + } + + /* + * We're pretty sure there's a port here. Lets find out what + * type of port it is. The IIR top two bits allows us to find + * out if it's 8250 or 16450, 16550, 16550A or later. This + * determines what we test for next. + * + * We also initialise the EFR (if any) to zero for later. The + * EFR occupies the same register location as the FCR and IIR. + */ + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, 0); + serial_out(up, UART_LCR, 0); + + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + + switch (serial_in(up, UART_IIR) & UART_IIR_FIFO_ENABLED) { + case UART_IIR_FIFO_ENABLED_8250: + autoconfig_8250(up); + break; + case UART_IIR_FIFO_ENABLED_16550: + port->type = PORT_16550; + break; + case UART_IIR_FIFO_ENABLED_16550A: + autoconfig_16550a(up); + break; + default: + port->type = PORT_UNKNOWN; + break; + } + + rsa_autoconfig(up); + + serial_out(up, UART_LCR, save_lcr); + + port->fifosize = uart_config[up->port.type].fifo_size; + old_capabilities = up->capabilities; + up->capabilities = uart_config[port->type].flags; + up->tx_loadsz = uart_config[port->type].tx_loadsz; + + if (port->type != PORT_UNKNOWN) { + /* + * Reset the UART. + */ + rsa_reset(up); + serial8250_out_MCR(up, save_mcr); + serial8250_clear_fifos(up); + serial_in(up, UART_RX); + serial8250_clear_IER(up); + } + + uart_port_unlock_irqrestore(port, flags); + + /* + * Check if the device is a Fintek F81216A + */ + if (port->type == PORT_16550A && port->iotype == UPIO_PORT) + fintek_8250_probe(up); + + if (up->capabilities != old_capabilities) { + dev_warn(port->dev, "detected caps %08x should be %08x\n", + old_capabilities, up->capabilities); + } +} + +static void autoconfig_irq(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + unsigned char save_mcr, save_ier; + unsigned char save_ICP = 0; + unsigned int ICP = 0; + unsigned long irqs; + int irq; + + if (port->flags & UPF_FOURPORT) { + ICP = (port->iobase & 0xfe0) | 0x1f; + save_ICP = inb_p(ICP); + outb_p(0x80, ICP); + inb_p(ICP); + } + + /* forget possible initially masked and pending IRQ */ + probe_irq_off(probe_irq_on()); + save_mcr = serial8250_in_MCR(up); + /* Synchronize UART_IER access against the console. */ + scoped_guard(uart_port_lock_irq, port) + save_ier = serial_in(up, UART_IER); + serial8250_out_MCR(up, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); + serial8250_out_MCR(up, 0); + udelay(10); + if (port->flags & UPF_FOURPORT) { + serial8250_out_MCR(up, UART_MCR_DTR | UART_MCR_RTS); + } else { + serial8250_out_MCR(up, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + } + /* Synchronize UART_IER access against the console. */ + scoped_guard(uart_port_lock_irq, port) + serial_out(up, UART_IER, UART_IER_ALL_INTR); + serial8250_clear_interrupts(port); + serial_out(up, UART_TX, 0xFF); + udelay(20); + irq = probe_irq_off(irqs); + + serial8250_out_MCR(up, save_mcr); + /* Synchronize UART_IER access against the console. */ + scoped_guard(uart_port_lock_irq, port) + serial_out(up, UART_IER, save_ier); + + if (port->flags & UPF_FOURPORT) + outb_p(save_ICP, ICP); + + port->irq = (irq > 0) ? irq : 0; +} + +static void serial8250_stop_rx(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&port->lock); + + guard(serial8250_rpm)(up); + + up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); + serial_port_out(port, UART_IER, up->ier); +} + +/** + * serial8250_em485_stop_tx() - generic ->rs485_stop_tx() callback + * @p: uart 8250 port + * @toggle_ier: true to allow enabling receive interrupts + * + * Generic callback usable by 8250 uart drivers to stop rs485 transmission. + */ +void serial8250_em485_stop_tx(struct uart_8250_port *p, bool toggle_ier) +{ + unsigned char mcr = serial8250_in_MCR(p); + + if (p->port.rs485.flags & SER_RS485_RTS_AFTER_SEND) + mcr |= UART_MCR_RTS; + else + mcr &= ~UART_MCR_RTS; + serial8250_out_MCR(p, mcr); + + /* + * Empty the RX FIFO, we are not interested in anything + * received during the half-duplex transmission. + * Enable previously disabled RX interrupts. + */ + if (!(p->port.rs485.flags & SER_RS485_RX_DURING_TX)) { + serial8250_clear_and_reinit_fifos(p); + + if (toggle_ier) { + /* + * Port locked to synchronize UART_IER access against + * the console. The lockdep_assert must be restricted + * to this condition because only here is it + * guaranteed that the port lock is held. The other + * hardware access in this function is synchronized + * by console ownership. + */ + lockdep_assert_held_once(&p->port.lock); + + p->ier |= UART_IER_RLSI | UART_IER_RDI; + serial_port_out(&p->port, UART_IER, p->ier); + } + } +} +EXPORT_SYMBOL_GPL(serial8250_em485_stop_tx); + +static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t) +{ + struct uart_8250_em485 *em485 = container_of(t, struct uart_8250_em485, + stop_tx_timer); + struct uart_8250_port *p = em485->port; + + guard(serial8250_rpm)(p); + guard(uart_port_lock_irqsave)(&p->port); + + if (em485->active_timer == &em485->stop_tx_timer) { + p->rs485_stop_tx(p, true); + em485->active_timer = NULL; + em485->tx_stopped = true; + } + + return HRTIMER_NORESTART; +} + +static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec) +{ + hrtimer_start(hrt, ms_to_ktime(msec), HRTIMER_MODE_REL); +} + +static void __stop_tx_rs485(struct uart_8250_port *p, u64 stop_delay) +{ + struct uart_8250_em485 *em485 = p->em485; + + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&p->port.lock); + + stop_delay += (u64)p->port.rs485.delay_rts_after_send * NSEC_PER_MSEC; + + /* + * rs485_stop_tx() is going to set RTS according to config + * AND flush RX FIFO if required. + */ + if (stop_delay > 0) { + em485->active_timer = &em485->stop_tx_timer; + hrtimer_start(&em485->stop_tx_timer, ns_to_ktime(stop_delay), HRTIMER_MODE_REL); + } else { + p->rs485_stop_tx(p, true); + em485->active_timer = NULL; + em485->tx_stopped = true; + } +} + +static inline void __stop_tx(struct uart_8250_port *p) +{ + struct uart_8250_em485 *em485 = p->em485; + + if (em485) { + u16 lsr = serial_lsr_in(p); + u64 stop_delay = 0; + + if (!(lsr & UART_LSR_THRE)) + return; + /* + * To provide required timing and allow FIFO transfer, + * __stop_tx_rs485() must be called only when both FIFO and + * shift register are empty. The device driver should either + * enable interrupt on TEMT or set UART_CAP_NOTEMT that will + * enlarge stop_tx_timer by the tx time of one frame to cover + * for emptying of the shift register. + */ + if (!(lsr & UART_LSR_TEMT)) { + if (!(p->capabilities & UART_CAP_NOTEMT)) + return; + /* + * RTS might get deasserted too early with the normal + * frame timing formula. It seems to suggest THRE might + * get asserted already during tx of the stop bit + * rather than after it is fully sent. + * Roughly estimate 1 extra bit here with / 7. + */ + stop_delay = p->port.frame_time + DIV_ROUND_UP(p->port.frame_time, 7); + } + + __stop_tx_rs485(p, stop_delay); + } + + if (serial8250_clear_THRI(p)) + serial8250_rpm_put_tx(p); +} + +static void serial8250_stop_tx(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + guard(serial8250_rpm)(up); + __stop_tx(up); + + /* + * We really want to stop the transmitter from sending. + */ + if (port->type == PORT_16C950) { + up->acr |= UART_ACR_TXDIS; + serial_icr_write(up, UART_ACR, up->acr); + } +} + +static inline void __start_tx(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + if (up->dma && !up->dma->tx_dma(up)) + return; + + if (serial8250_set_THRI(up)) { + if (up->bugs & UART_BUG_TXEN) { + u16 lsr = serial_lsr_in(up); + + if (lsr & UART_LSR_THRE) + serial8250_tx_chars(up); + } + } + + /* + * Re-enable the transmitter if we disabled it. + */ + if (port->type == PORT_16C950 && up->acr & UART_ACR_TXDIS) { + up->acr &= ~UART_ACR_TXDIS; + serial_icr_write(up, UART_ACR, up->acr); + } +} + +/** + * serial8250_em485_start_tx() - generic ->rs485_start_tx() callback + * @up: uart 8250 port + * @toggle_ier: true to allow disabling receive interrupts + * + * Generic callback usable by 8250 uart drivers to start rs485 transmission. + * Assumes that setting the RTS bit in the MCR register means RTS is high. + * (Some chips use inverse semantics.) Further assumes that reception is + * stoppable by disabling the UART_IER_RDI interrupt. (Some chips set the + * UART_LSR_DR bit even when UART_IER_RDI is disabled, foiling this approach.) + */ +void serial8250_em485_start_tx(struct uart_8250_port *up, bool toggle_ier) +{ + unsigned char mcr = serial8250_in_MCR(up); + + if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX) && toggle_ier) + serial8250_stop_rx(&up->port); + + if (up->port.rs485.flags & SER_RS485_RTS_ON_SEND) + mcr |= UART_MCR_RTS; + else + mcr &= ~UART_MCR_RTS; + serial8250_out_MCR(up, mcr); +} +EXPORT_SYMBOL_GPL(serial8250_em485_start_tx); + +/* Returns false, if start_tx_timer was setup to defer TX start */ +static bool start_tx_rs485(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct uart_8250_em485 *em485 = up->em485; + + /* + * While serial8250_em485_handle_stop_tx() is a noop if + * em485->active_timer != &em485->stop_tx_timer, it might happen that + * the timer is still armed and triggers only after the current bunch of + * chars is send and em485->active_timer == &em485->stop_tx_timer again. + * So cancel the timer. There is still a theoretical race condition if + * the timer is already running and only comes around to check for + * em485->active_timer when &em485->stop_tx_timer is armed again. + */ + if (em485->active_timer == &em485->stop_tx_timer) + hrtimer_try_to_cancel(&em485->stop_tx_timer); + + em485->active_timer = NULL; + + if (em485->tx_stopped) { + em485->tx_stopped = false; + + up->rs485_start_tx(up, true); + + if (up->port.rs485.delay_rts_before_send > 0) { + em485->active_timer = &em485->start_tx_timer; + start_hrtimer_ms(&em485->start_tx_timer, + up->port.rs485.delay_rts_before_send); + return false; + } + } + + return true; +} + +static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t) +{ + struct uart_8250_em485 *em485 = container_of(t, struct uart_8250_em485, + start_tx_timer); + struct uart_8250_port *p = em485->port; + + guard(uart_port_lock_irqsave)(&p->port); + + if (em485->active_timer == &em485->start_tx_timer) { + __start_tx(&p->port); + em485->active_timer = NULL; + } + + return HRTIMER_NORESTART; +} + +static void serial8250_start_tx(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct uart_8250_em485 *em485 = up->em485; + + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&port->lock); + + if (!port->x_char && kfifo_is_empty(&port->state->port.xmit_fifo)) + return; + + serial8250_rpm_get_tx(up); + + if (em485) { + if ((em485->active_timer == &em485->start_tx_timer) || + !start_tx_rs485(port)) + return; + } + __start_tx(port); +} + +static void serial8250_throttle(struct uart_port *port) +{ + port->throttle(port); +} + +static void serial8250_unthrottle(struct uart_port *port) +{ + port->unthrottle(port); +} + +static void serial8250_disable_ms(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&port->lock); + + /* no MSR capabilities */ + if (up->bugs & UART_BUG_NOMSR) + return; + + mctrl_gpio_disable_ms_no_sync(up->gpios); + + up->ier &= ~UART_IER_MSI; + serial_port_out(port, UART_IER, up->ier); +} + +static void serial8250_enable_ms(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&port->lock); + + /* no MSR capabilities */ + if (up->bugs & UART_BUG_NOMSR) + return; + + mctrl_gpio_enable_ms(up->gpios); + + up->ier |= UART_IER_MSI; + + guard(serial8250_rpm)(up); + serial_port_out(port, UART_IER, up->ier); +} + +void serial8250_read_char(struct uart_8250_port *up, u16 lsr) +{ + struct uart_port *port = &up->port; + u8 ch, flag = TTY_NORMAL; + + if (likely(lsr & UART_LSR_DR)) + ch = serial_in(up, UART_RX); + else + /* + * Intel 82571 has a Serial Over Lan device that will + * set UART_LSR_BI without setting UART_LSR_DR when + * it receives a break. To avoid reading from the + * receive buffer without UART_LSR_DR bit set, we + * just force the read character to be 0 + */ + ch = 0; + + port->icount.rx++; + + lsr |= up->lsr_saved_flags; + up->lsr_saved_flags = 0; + + if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { + if (lsr & UART_LSR_BI) { + lsr &= ~(UART_LSR_FE | UART_LSR_PE); + port->icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(port)) + return; + } else if (lsr & UART_LSR_PE) + port->icount.parity++; + else if (lsr & UART_LSR_FE) + port->icount.frame++; + if (lsr & UART_LSR_OE) + port->icount.overrun++; + + /* + * Mask off conditions which should be ignored. + */ + lsr &= port->read_status_mask; + + if (lsr & UART_LSR_BI) { + dev_dbg(port->dev, "handling break\n"); + flag = TTY_BREAK; + } else if (lsr & UART_LSR_PE) + flag = TTY_PARITY; + else if (lsr & UART_LSR_FE) + flag = TTY_FRAME; + } + if (uart_prepare_sysrq_char(port, ch)) + return; + + uart_insert_char(port, lsr, UART_LSR_OE, ch, flag); +} +EXPORT_SYMBOL_GPL(serial8250_read_char); + +/* + * serial8250_rx_chars - Read characters. The first LSR value must be passed in. + * + * Returns LSR bits. The caller should rely only on non-Rx related LSR bits + * (such as THRE) because the LSR value might come from an already consumed + * character. + */ +u16 serial8250_rx_chars(struct uart_8250_port *up, u16 lsr) +{ + struct uart_port *port = &up->port; + int max_count = 256; + + do { + serial8250_read_char(up, lsr); + if (--max_count == 0) + break; + lsr = serial_in(up, UART_LSR); + } while (lsr & (UART_LSR_DR | UART_LSR_BI)); + + tty_flip_buffer_push(&port->state->port); + return lsr; +} +EXPORT_SYMBOL_GPL(serial8250_rx_chars); + +void serial8250_tx_chars(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + struct tty_port *tport = &port->state->port; + int count; + + if (port->x_char) { + uart_xchar_out(port, UART_TX); + return; + } + if (uart_tx_stopped(port)) { + serial8250_stop_tx(port); + return; + } + if (kfifo_is_empty(&tport->xmit_fifo)) { + __stop_tx(up); + return; + } + + count = up->tx_loadsz; + do { + unsigned char c; + + if (!uart_fifo_get(port, &c)) + break; + + serial_out(up, UART_TX, c); + if (up->bugs & UART_BUG_TXRACE) { + /* + * The Aspeed BMC virtual UARTs have a bug where data + * may get stuck in the BMC's Tx FIFO from bursts of + * writes on the APB interface. + * + * Delay back-to-back writes by a read cycle to avoid + * stalling the VUART. Read a register that won't have + * side-effects and discard the result. + */ + serial_in(up, UART_SCR); + } + + if ((up->capabilities & UART_CAP_HFIFO) && + !uart_lsr_tx_empty(serial_in(up, UART_LSR))) + break; + /* The BCM2835 MINI UART THRE bit is really a not-full bit. */ + if ((up->capabilities & UART_CAP_MINI) && + !(serial_in(up, UART_LSR) & UART_LSR_THRE)) + break; + } while (--count > 0); + + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) + uart_write_wakeup(port); + + /* + * With RPM enabled, we have to wait until the FIFO is empty before the + * HW can go idle. So we get here once again with empty FIFO and disable + * the interrupt and RPM in __stop_tx() + */ + if (kfifo_is_empty(&tport->xmit_fifo) && + !(up->capabilities & UART_CAP_RPM)) + __stop_tx(up); +} +EXPORT_SYMBOL_GPL(serial8250_tx_chars); + +/* Caller holds uart port lock */ +unsigned int serial8250_modem_status(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + unsigned int status = serial_in(up, UART_MSR); + + status |= up->msr_saved_flags; + up->msr_saved_flags = 0; + if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI && + port->state != NULL) { + if (status & UART_MSR_TERI) + port->icount.rng++; + if (status & UART_MSR_DDSR) + port->icount.dsr++; + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + uart_handle_cts_change(port, status & UART_MSR_CTS); + + wake_up_interruptible(&port->state->port.delta_msr_wait); + } + + return status; +} +EXPORT_SYMBOL_GPL(serial8250_modem_status); + +static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) +{ + switch (iir & 0x3f) { + case UART_IIR_THRI: + /* + * Postpone DMA or not decision to IIR_RDI or IIR_RX_TIMEOUT + * because it's impossible to do an informed decision about + * that with IIR_THRI. + * + * This also fixes one known DMA Rx corruption issue where + * DR is asserted but DMA Rx only gets a corrupted zero byte + * (too early DR?). + */ + return false; + case UART_IIR_RDI: + if (!up->dma->rx_running) + break; + fallthrough; + case UART_IIR_RLSI: + case UART_IIR_RX_TIMEOUT: + serial8250_rx_dma_flush(up); + return true; + } + return up->dma->rx_dma(up); +} + +/* + * Context: port's lock must be held by the caller. The caller must + * release it via guard(uart_port_lock_check_sysrq_irqsave) or + * uart_unlock_and_check_sysrq_irqrestore(), which captures SysRq + * character on unlock. + */ +void serial8250_handle_irq_locked(struct uart_port *port, unsigned int iir) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct tty_port *tport = &port->state->port; + bool skip_rx = false; + u16 status; + + lockdep_assert_held_once(&port->lock); + + status = serial_lsr_in(up); + + /* + * Recover from no-data-ready and FIFO error condition to avoid getting + * stuck in the ISR. + */ + if (!(status & UART_LSR_DR) && (status & UART_LSR_FIFOE)) + serial8250_clear_and_reinit_fifos(up); + + /* + * If port is stopped and there are no error conditions in the + * FIFO, then don't drain the FIFO, as this may lead to TTY buffer + * overflow. Not servicing, RX FIFO would trigger auto HW flow + * control when FIFO occupancy reaches preset threshold, thus + * halting RX. This only works when auto HW flow control is + * available. + */ + if (!(status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) && + (port->status & (UPSTAT_AUTOCTS | UPSTAT_AUTORTS)) && + !(up->ier & (UART_IER_RLSI | UART_IER_RDI))) + skip_rx = true; + + if (status & (UART_LSR_DR | UART_LSR_BI) && !skip_rx) { + struct irq_data *d; + + d = irq_get_irq_data(port->irq); + if (d && irqd_is_wakeup_set(d)) + pm_wakeup_event(tport->tty->dev, 0); + if (!up->dma || handle_rx_dma(up, iir)) + status = serial8250_rx_chars(up, status); + } + serial8250_modem_status(up); + if ((status & UART_LSR_THRE) && (up->ier & UART_IER_THRI)) { + if (!up->dma || up->dma->tx_err) + serial8250_tx_chars(up); + else if (!up->dma->tx_running) + __stop_tx(up); + } +} +EXPORT_SYMBOL_NS_GPL(serial8250_handle_irq_locked, "SERIAL_8250"); + +/* + * This handles the interrupt from one port. + */ +int serial8250_handle_irq(struct uart_port *port, unsigned int iir) +{ + if (iir & UART_IIR_NO_INT) + return 0; + + guard(uart_port_lock_check_sysrq_irqsave)(port); + serial8250_handle_irq_locked(port, iir); + + return 1; +} +EXPORT_SYMBOL_GPL(serial8250_handle_irq); + +static int serial8250_default_handle_irq(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int iir; + + guard(serial8250_rpm)(up); + + iir = serial_port_in(port, UART_IIR); + return serial8250_handle_irq(port, iir); +} + +/* + * Newer 16550 compatible parts such as the SC16C650 & Altera 16550 Soft IP + * have a programmable TX threshold that triggers the THRE interrupt in + * the IIR register. In this case, the THRE interrupt indicates the FIFO + * has space available. Load it up with tx_loadsz bytes. + */ +static int serial8250_tx_threshold_handle_irq(struct uart_port *port) +{ + unsigned int iir = serial_port_in(port, UART_IIR); + + /* TX Threshold IRQ triggered so load up FIFO */ + if ((iir & UART_IIR_ID) == UART_IIR_THRI) { + struct uart_8250_port *up = up_to_u8250p(port); + + guard(uart_port_lock_irqsave)(port); + serial8250_tx_chars(up); + } + + iir = serial_port_in(port, UART_IIR); + return serial8250_handle_irq(port, iir); +} + +static unsigned int serial8250_tx_empty(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + guard(serial8250_rpm)(up); + guard(uart_port_lock_irqsave)(port); + + if (!serial8250_tx_dma_running(up) && uart_lsr_tx_empty(serial_lsr_in(up))) + return TIOCSER_TEMT; + + return 0; +} + +unsigned int serial8250_do_get_mctrl(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int status; + unsigned int val; + + scoped_guard(serial8250_rpm, up) + status = serial8250_modem_status(up); + + val = serial8250_MSR_to_TIOCM(status); + if (up->gpios) + return mctrl_gpio_get(up->gpios, &val); + + return val; +} +EXPORT_SYMBOL_GPL(serial8250_do_get_mctrl); + +static unsigned int serial8250_get_mctrl(struct uart_port *port) +{ + if (port->get_mctrl) + return port->get_mctrl(port); + return serial8250_do_get_mctrl(port); +} + +void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned char mcr; + + mcr = serial8250_TIOCM_to_MCR(mctrl); + + mcr |= up->mcr; + + serial8250_out_MCR(up, mcr); +} +EXPORT_SYMBOL_GPL(serial8250_do_set_mctrl); + +static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + if (port->rs485.flags & SER_RS485_ENABLED) + return; + + if (port->set_mctrl) + port->set_mctrl(port, mctrl); + else + serial8250_do_set_mctrl(port, mctrl); +} + +void serial8250_do_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + guard(serial8250_rpm)(up); + guard(uart_port_lock_irqsave)(port); + + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_port_out(port, UART_LCR, up->lcr); +} +EXPORT_SYMBOL_GPL(serial8250_do_break_ctl); + +static void serial8250_break_ctl(struct uart_port *port, int break_state) +{ + if (port->break_ctl) + port->break_ctl(port, break_state); + else + serial8250_do_break_ctl(port, break_state); +} + +/* Returns true if @bits were set, false on timeout */ +static bool wait_for_lsr(struct uart_8250_port *up, int bits) +{ + unsigned int status, tmout; + + /* + * Wait for a character to be sent. Fallback to a safe default + * timeout value if @frame_time is not available. + */ + if (up->port.frame_time) + tmout = up->port.frame_time * 2 / NSEC_PER_USEC; + else + tmout = 10000; + + for (;;) { + status = serial_lsr_in(up); + + if ((status & bits) == bits) + break; + if (--tmout == 0) + break; + udelay(1); + touch_nmi_watchdog(); + } + + return (tmout != 0); +} + +/* Wait for transmitter and holding register to empty with timeout */ +static void wait_for_xmitr(struct uart_8250_port *up, int bits) +{ + unsigned int tmout; + bool tx_ready; + + tx_ready = wait_for_lsr(up, bits); + + /* + * Wait up to 1s for flow control if necessary. + * When 'no_console_suspend' is active (in the window between + * suspend() and resume()), flow control is temporarily ignored + * because the canary workaround is not reliable in all situations, + * leading to flow control timeouts for every character. + */ + if (uart_console_hwflow_active(&up->port) && !up->canary) { + for (tmout = 1000000; tmout; tmout--) { + unsigned int msr = serial_in(up, UART_MSR); + up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; + if (msr & UART_MSR_CTS) { + if (!tx_ready) + wait_for_lsr(up, bits); + break; + } + udelay(1); + touch_nmi_watchdog(); + } + } +} + +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +static int serial8250_get_poll_char(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + u16 lsr; + + guard(serial8250_rpm)(up); + + lsr = serial_port_in(port, UART_LSR); + if (!(lsr & UART_LSR_DR)) + return NO_POLL_CHAR; + + return serial_port_in(port, UART_RX); +} + + +static void serial8250_put_poll_char(struct uart_port *port, + unsigned char c) +{ + unsigned int ier; + struct uart_8250_port *up = up_to_u8250p(port); + + /* + * Normally the port is locked to synchronize UART_IER access + * against the console. However, this function is only used by + * KDB/KGDB, where it may not be possible to acquire the port + * lock because all other CPUs are quiesced. The quiescence + * should allow safe lockless usage here. + */ + + guard(serial8250_rpm)(up); + /* + * First, save the IER, then disable the interrupts. The special + * variant to clear the IER is used because KDB/KGDB printing + * is synchronized via CPU quiescence without holding the port + * lock. + */ + ier = serial_port_in(port, UART_IER); + __serial8250_clear_IER(up); + + wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); + /* + * Send the character out. + */ + serial_port_out(port, UART_TX, c); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); + serial_port_out(port, UART_IER, ier); +} + +#endif /* CONFIG_CONSOLE_POLL */ + +static void serial8250_startup_special(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + switch (port->type) { + case PORT_16C950: { + /* + * Wake up and initialize UART + * + * Synchronize UART_IER access against the console. + */ + guard(uart_port_lock_irqsave)(port); + up->acr = 0; + serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B); + serial_port_out(port, UART_EFR, UART_EFR_ECB); + serial_port_out(port, UART_IER, 0); + serial_port_out(port, UART_LCR, 0); + serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ + serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B); + serial_port_out(port, UART_EFR, UART_EFR_ECB); + serial_port_out(port, UART_LCR, 0); + break; + } + case PORT_DA830: + /* + * Reset the port + * + * Synchronize UART_IER access against the console. + */ + scoped_guard(uart_port_lock_irqsave, port) { + serial_port_out(port, UART_IER, 0); + serial_port_out(port, UART_DA830_PWREMU_MGMT, 0); + } + mdelay(10); + + /* Enable Tx, Rx and free run mode */ + serial_port_out(port, UART_DA830_PWREMU_MGMT, + UART_DA830_PWREMU_MGMT_UTRST | + UART_DA830_PWREMU_MGMT_URRST | + UART_DA830_PWREMU_MGMT_FREE); + break; + case PORT_RSA: + rsa_enable(up); + break; + } +} + +static void serial8250_set_TRG_levels(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + switch (port->type) { + /* For a XR16C850, we need to set the trigger levels */ + case PORT_16850: { + u8 fctr; + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + fctr = serial_in(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX); + fctr |= UART_FCTR_TRGD; + serial_port_out(port, UART_FCTR, fctr | UART_FCTR_RX); + serial_port_out(port, UART_TRG, UART_TRG_96); + serial_port_out(port, UART_FCTR, fctr | UART_FCTR_TX); + serial_port_out(port, UART_TRG, UART_TRG_96); + + serial_port_out(port, UART_LCR, 0); + break; + } + /* For the Altera 16550 variants, set TX threshold trigger level. */ + case PORT_ALTR_16550_F32: + case PORT_ALTR_16550_F64: + case PORT_ALTR_16550_F128: + if (port->fifosize <= 1) + return; + + /* Bounds checking of TX threshold (valid 0 to fifosize-2) */ + if (up->tx_loadsz < 2 || up->tx_loadsz > port->fifosize) { + dev_err(port->dev, "TX FIFO Threshold errors, skipping\n"); + return; + } + serial_port_out(port, UART_ALTR_AFR, UART_ALTR_EN_TXFIFO_LW); + serial_port_out(port, UART_ALTR_TX_LOW, port->fifosize - up->tx_loadsz); + port->handle_irq = serial8250_tx_threshold_handle_irq; + break; + } +} + +static void serial8250_THRE_test(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + bool iir_noint1, iir_noint2; + + if (!port->irq) + return; + + if (up->port.flags & UPF_NO_THRE_TEST) + return; + + disable_irq(port->irq); + + /* + * Test for UARTs that do not reassert THRE when the transmitter is idle and the interrupt + * has already been cleared. Real 16550s should always reassert this interrupt whenever the + * transmitter is idle and the interrupt is enabled. Delays are necessary to allow register + * changes to become visible. + * + * Synchronize UART_IER access against the console. + */ + scoped_guard(uart_port_lock_irqsave, port) { + wait_for_xmitr(up, UART_LSR_THRE); + serial_port_out_sync(port, UART_IER, UART_IER_THRI); + udelay(1); /* allow THRE to set */ + iir_noint1 = serial_port_in(port, UART_IIR) & UART_IIR_NO_INT; + serial_port_out(port, UART_IER, 0); + serial_port_out_sync(port, UART_IER, UART_IER_THRI); + udelay(1); /* allow a working UART time to re-assert THRE */ + iir_noint2 = serial_port_in(port, UART_IIR) & UART_IIR_NO_INT; + serial_port_out(port, UART_IER, 0); + } + + enable_irq(port->irq); + + /* + * If the interrupt is not reasserted, or we otherwise don't trust the iir, setup a timer to + * kick the UART on a regular basis. + */ + if ((!iir_noint1 && iir_noint2) || up->port.flags & UPF_BUG_THRE) + up->bugs |= UART_BUG_THRE; +} + +static void serial8250_init_mctrl(struct uart_port *port) +{ + if (port->flags & UPF_FOURPORT) { + if (!port->irq) + port->mctrl |= TIOCM_OUT1; + } else { + /* Most PC uarts need OUT2 raised to enable interrupts. */ + if (port->irq) + port->mctrl |= TIOCM_OUT2; + } + + serial8250_set_mctrl(port, port->mctrl); +} + +static void serial8250_iir_txen_test(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + bool lsr_temt, iir_noint; + + if (port->quirks & UPQ_NO_TXEN_TEST) + return; + + /* Do a quick test to see if we receive an interrupt when we enable the TX irq. */ + serial_port_out(port, UART_IER, UART_IER_THRI); + lsr_temt = serial_port_in(port, UART_LSR) & UART_LSR_TEMT; + iir_noint = serial_port_in(port, UART_IIR) & UART_IIR_NO_INT; + serial_port_out(port, UART_IER, 0); + + /* + * Serial over Lan (SoL) hack: + * Intel 8257x Gigabit ethernet chips have a 16550 emulation, to be used for Serial Over + * Lan. Those chips take a longer time than a normal serial device to signalize that a + * transmission data was queued. Due to that, the above test generally fails. One solution + * would be to delay the reading of iir. However, this is not reliable, since the timeout is + * variable. So, in case of UPQ_NO_TXEN_TEST, let's just don't test if we receive TX irq. + * This way, we'll never enable UART_BUG_TXEN. + */ + if (lsr_temt && iir_noint) { + if (!(up->bugs & UART_BUG_TXEN)) { + up->bugs |= UART_BUG_TXEN; + dev_dbg(port->dev, "enabling bad tx status workarounds\n"); + } + return; + } + + /* FIXME: why is this needed? */ + up->bugs &= ~UART_BUG_TXEN; +} + +static void serial8250_initialize(struct uart_port *port) +{ + guard(uart_port_lock_irqsave)(port); + serial_port_out(port, UART_LCR, UART_LCR_WLEN8); + + serial8250_init_mctrl(port); + serial8250_iir_txen_test(port); +} + +int serial8250_do_startup(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + int retval; + + if (!port->fifosize) + port->fifosize = uart_config[port->type].fifo_size; + if (!up->tx_loadsz) + up->tx_loadsz = uart_config[port->type].tx_loadsz; + if (!up->capabilities) + up->capabilities = uart_config[port->type].flags; + up->mcr = 0; + + if (port->iotype != up->cur_iotype) + set_io_from_upio(port); + + guard(serial8250_rpm)(up); + + serial8250_startup_special(port); + + /* + * Clear the FIFO buffers and disable them. + * (they will be reenabled in set_termios()) + */ + serial8250_clear_fifos(up); + + serial8250_clear_interrupts(port); + + /* + * At this point, there's no way the LSR could still be 0xff; + * if it is, then bail out, because there's likely no UART + * here. + */ + if (!(port->flags & UPF_BUGGY_UART) && + (serial_port_in(port, UART_LSR) == 0xff)) { + dev_info_ratelimited(port->dev, "LSR safety check engaged!\n"); + return -ENODEV; + } + + serial8250_set_TRG_levels(port); + + /* Check if we need to have shared IRQs */ + if (port->irq && (up->port.flags & UPF_SHARE_IRQ)) + up->port.irqflags |= IRQF_SHARED; + + retval = up->ops->setup_irq(up); + if (retval) + return retval; + + serial8250_THRE_test(port); + + up->ops->setup_timer(up); + + serial8250_initialize(port); + + /* + * Clear the interrupt registers again for luck, and clear the + * saved flags to avoid getting false values from polling + * routines or the previous session. + */ + serial8250_clear_interrupts(port); + up->lsr_saved_flags = 0; + up->msr_saved_flags = 0; + + /* + * Request DMA channels for both RX and TX. + */ + if (up->dma) { + const char *msg = NULL; + + if (uart_console(port)) + msg = "forbid DMA for kernel console"; + else if (serial8250_request_dma(up)) + msg = "failed to request DMA"; + if (msg) { + dev_warn_ratelimited(port->dev, "%s\n", msg); + up->dma = NULL; + } + } + + /* + * Set the IER shadow for rx interrupts but defer actual interrupt + * enable until after the FIFOs are enabled; otherwise, an already- + * active sender can swamp the interrupt handler with "too much work". + */ + up->ier = UART_IER_RLSI | UART_IER_RDI; + + if (port->flags & UPF_FOURPORT) { + unsigned int icp; + /* + * Enable interrupts on the AST Fourport board + */ + icp = (port->iobase & 0xfe0) | 0x01f; + outb_p(0x80, icp); + inb_p(icp); + } + + return 0; +} +EXPORT_SYMBOL_GPL(serial8250_do_startup); + +static int serial8250_startup(struct uart_port *port) +{ + if (port->startup) + return port->startup(port); + return serial8250_do_startup(port); +} + +void serial8250_do_shutdown(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + u32 lcr; + + serial8250_rpm_get(up); + /* + * Disable interrupts from this port + * + * Synchronize UART_IER access against the console. + */ + scoped_guard(uart_port_lock_irqsave, port) { + up->ier = 0; + serial_port_out(port, UART_IER, 0); + } + + synchronize_irq(port->irq); + + serial8250_release_dma(up); + up->dma = NULL; + + scoped_guard(uart_port_lock_irqsave, port) { + if (port->flags & UPF_FOURPORT) { + /* reset interrupts on the AST Fourport board */ + inb((port->iobase & 0xfe0) | 0x1f); + port->mctrl |= TIOCM_OUT1; + } else + port->mctrl &= ~TIOCM_OUT2; + + serial8250_set_mctrl(port, port->mctrl); + + /* Disable break condition */ + lcr = serial_port_in(port, UART_LCR); + lcr &= ~UART_LCR_SBC; + serial_port_out(port, UART_LCR, lcr); + } + + serial8250_clear_fifos(up); + + rsa_disable(up); + + /* + * Read data port to reset things, and then unlink from + * the IRQ chain. + */ + serial_port_in(port, UART_RX); + /* + * LCR writes on DW UART can trigger late (unmaskable) IRQs. + * Handle them before releasing the handler. + */ + synchronize_irq(port->irq); + + serial8250_rpm_put(up); + + up->ops->release_irq(up); +} +EXPORT_SYMBOL_GPL(serial8250_do_shutdown); + +static void serial8250_shutdown(struct uart_port *port) +{ + if (port->shutdown) + port->shutdown(port); + else + serial8250_do_shutdown(port); +} + +static void serial8250_flush_buffer(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + if (up->dma) + serial8250_tx_dma_flush(up); +} + +static unsigned int serial8250_do_get_divisor(struct uart_port *port, unsigned int baud) +{ + upf_t magic_multiplier = port->flags & UPF_MAGIC_MULTIPLIER; + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int quot; + + /* + * Handle magic divisors for baud rates above baud_base on SMSC + * Super I/O chips. We clamp custom rates from clk/6 and clk/12 + * up to clk/4 (0x8001) and clk/8 (0x8002) respectively. These + * magic divisors actually reprogram the baud rate generator's + * reference clock derived from chips's 14.318MHz clock input. + * + * Documentation claims that with these magic divisors the base + * frequencies of 7.3728MHz and 3.6864MHz are used respectively + * for the extra baud rates of 460800bps and 230400bps rather + * than the usual base frequency of 1.8462MHz. However empirical + * evidence contradicts that. + * + * Instead bit 7 of the DLM register (bit 15 of the divisor) is + * effectively used as a clock prescaler selection bit for the + * base frequency of 7.3728MHz, always used. If set to 0, then + * the base frequency is divided by 4 for use by the Baud Rate + * Generator, for the usual arrangement where the value of 1 of + * the divisor produces the baud rate of 115200bps. Conversely, + * if set to 1 and high-speed operation has been enabled with the + * Serial Port Mode Register in the Device Configuration Space, + * then the base frequency is supplied directly to the Baud Rate + * Generator, so for the divisor values of 0x8001, 0x8002, 0x8003, + * 0x8004, etc. the respective baud rates produced are 460800bps, + * 230400bps, 153600bps, 115200bps, etc. + * + * In all cases only low 15 bits of the divisor are used to divide + * the baud base and therefore 32767 is the maximum divisor value + * possible, even though documentation says that the programmable + * Baud Rate Generator is capable of dividing the internal PLL + * clock by any divisor from 1 to 65535. + */ + if (magic_multiplier && baud >= port->uartclk / 6) + quot = 0x8001; + else if (magic_multiplier && baud >= port->uartclk / 12) + quot = 0x8002; + else + quot = uart_get_divisor(port, baud); + + /* + * Oxford Semi 952 rev B workaround + */ + if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0) + quot++; + + return quot; +} + +static unsigned int serial8250_get_divisor(struct uart_port *port, + unsigned int baud, + unsigned int *frac) +{ + if (port->get_divisor) + return port->get_divisor(port, baud, frac); + + return serial8250_do_get_divisor(port, baud); +} + +static unsigned char serial8250_compute_lcr(struct uart_8250_port *up, tcflag_t c_cflag) +{ + u8 lcr = UART_LCR_WLEN(tty_get_char_size(c_cflag)); + + if (c_cflag & CSTOPB) + lcr |= UART_LCR_STOP; + if (c_cflag & PARENB) + lcr |= UART_LCR_PARITY; + if (!(c_cflag & PARODD)) + lcr |= UART_LCR_EPAR; + if (c_cflag & CMSPAR) + lcr |= UART_LCR_SPAR; + + return lcr; +} + +void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + /* Workaround to enable 115200 baud on OMAP1510 internal ports */ + if (is_omap1510_8250(up)) { + if (baud == 115200) { + quot = 1; + serial_port_out(port, UART_OMAP_OSC_12M_SEL, 1); + } else + serial_port_out(port, UART_OMAP_OSC_12M_SEL, 0); + } + + /* + * For NatSemi, switch to bank 2 not bank 1, to avoid resetting EXCR2, + * otherwise just set DLAB + */ + if (up->capabilities & UART_NATSEMI) + serial_port_out(port, UART_LCR, 0xe0); + else + serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB); + + serial_dl_write(up, quot); +} +EXPORT_SYMBOL_GPL(serial8250_do_set_divisor); + +static void serial8250_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot, unsigned int quot_frac) +{ + if (port->set_divisor) + port->set_divisor(port, baud, quot, quot_frac); + else + serial8250_do_set_divisor(port, baud, quot); +} + +static unsigned int serial8250_get_baud_rate(struct uart_port *port, + struct ktermios *termios, + const struct ktermios *old) +{ + unsigned int tolerance = port->uartclk / 100; + unsigned int min; + unsigned int max; + + /* + * Handle magic divisors for baud rates above baud_base on SMSC + * Super I/O chips. Enable custom rates of clk/4 and clk/8, but + * disable divisor values beyond 32767, which are unavailable. + */ + if (port->flags & UPF_MAGIC_MULTIPLIER) { + min = port->uartclk / 16 / UART_DIV_MAX >> 1; + max = (port->uartclk + tolerance) / 4; + } else { + min = port->uartclk / 16 / UART_DIV_MAX; + max = (port->uartclk + tolerance) / 16; + } + + /* + * Ask the core to calculate the divisor for us. + * Allow 1% tolerance at the upper limit so uart clks marginally + * slower than nominal still match standard baud rates without + * causing transmission errors. + */ + return uart_get_baud_rate(port, termios, old, min, max); +} + +/* + * Note in order to avoid the tty port mutex deadlock don't use the next method + * within the uart port callbacks. Primarily it's supposed to be utilized to + * handle a sudden reference clock rate change. + */ +void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk) +{ + struct tty_port *tport = &port->state->port; + + scoped_guard(tty_port_tty, tport) { + struct tty_struct *tty = scoped_tty(); + + guard(rwsem_write)(&tty->termios_rwsem); + guard(mutex)(&tport->mutex); + + if (port->uartclk == uartclk) + return; + + port->uartclk = uartclk; + + if (!tty_port_initialized(tport)) + return; + + serial8250_do_set_termios(port, &tty->termios, NULL); + + return; + } + guard(mutex)(&tport->mutex); + port->uartclk = uartclk; +} +EXPORT_SYMBOL_GPL(serial8250_update_uartclk); + +static void serial8250_set_mini(struct uart_port *port, struct ktermios *termios) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + if (!(up->capabilities & UART_CAP_MINI)) + return; + + termios->c_cflag &= ~(CSTOPB | PARENB | PARODD | CMSPAR); + + tcflag_t csize = termios->c_cflag & CSIZE; + if (csize == CS5 || csize == CS6) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS7; + } +} + +static void serial8250_set_trigger_for_slow_speed(struct uart_port *port, struct ktermios *termios, + unsigned int baud) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + if (!(up->capabilities & UART_CAP_FIFO)) + return; + if (port->fifosize <= 1) + return; + if (baud >= 2400) + return; + if (up->dma) + return; + + up->fcr &= ~UART_FCR_TRIGGER_MASK; + up->fcr |= UART_FCR_TRIGGER_1; +} + +/* + * MCR-based auto flow control. When AFE is enabled, RTS will be deasserted when the receive FIFO + * contains more characters than the trigger, or the MCR RTS bit is cleared. + */ +static void serial8250_set_afe(struct uart_port *port, struct ktermios *termios) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + if (!(up->capabilities & UART_CAP_AFE)) + return; + + up->mcr &= ~UART_MCR_AFE; + if (termios->c_cflag & CRTSCTS) + up->mcr |= UART_MCR_AFE; +} + +static void serial8250_set_errors_and_ignores(struct uart_port *port, struct ktermios *termios) +{ + /* + * Specify which conditions may be considered for error handling and the ignoring of + * characters. The actual ignoring of characters only occurs if the bit is set in + * @ignore_status_mask as well. + */ + port->read_status_mask = UART_LSR_OE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + port->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) + port->read_status_mask |= UART_LSR_BI; + + /* Characters to ignore */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, ignore overruns too (for real raw + * support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= UART_LSR_OE; + } + + /* ignore all characters if CREAD is not set */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= UART_LSR_DR; +} + +static void serial8250_set_ier(struct uart_port *port, struct ktermios *termios) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + /* CTS flow control flag and modem status interrupts */ + up->ier &= ~UART_IER_MSI; + if (!(up->bugs & UART_BUG_NOMSR) && UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + if (up->capabilities & UART_CAP_UUE) + up->ier |= UART_IER_UUE; + if (up->capabilities & UART_CAP_RTOIE) + up->ier |= UART_IER_RTOIE; + + serial_port_out(port, UART_IER, up->ier); +} + +static void serial8250_set_efr(struct uart_port *port, struct ktermios *termios) +{ + struct uart_8250_port *up = up_to_u8250p(port); + u8 efr_reg = UART_EFR; + u8 efr = 0; + + if (!(up->capabilities & UART_CAP_EFR)) + return; + + /* + * TI16C752/Startech hardware flow control. FIXME: + * - TI16C752 requires control thresholds to be set. + * - UART_MCR_RTS is ineffective if auto-RTS mode is enabled. + */ + if (termios->c_cflag & CRTSCTS) + efr |= UART_EFR_CTS; + + if (port->flags & UPF_EXAR_EFR) + efr_reg = UART_XR_EFR; + + serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B); + serial_port_out(port, efr_reg, efr); +} + +static void serial8250_set_fcr(struct uart_port *port, struct ktermios *termios) +{ + struct uart_8250_port *up = up_to_u8250p(port); + bool is_16750 = port->type == PORT_16750; + + if (is_16750) + serial_port_out(port, UART_FCR, up->fcr); + + /* + * LCR DLAB must be reset to enable 64-byte FIFO mode. If the FCR is written without DLAB + * set, this mode will be disabled. + */ + serial_port_out(port, UART_LCR, up->lcr); + + if (is_16750) + return; + + /* emulated UARTs (Lucent Venus 167x) need two steps */ + if (up->fcr & UART_FCR_ENABLE_FIFO) + serial_port_out(port, UART_FCR, UART_FCR_ENABLE_FIFO); + + serial_port_out(port, UART_FCR, up->fcr); +} + +void +serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, + const struct ktermios *old) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int baud, quot, frac = 0; + u8 lcr; + + serial8250_set_mini(port, termios); + lcr = serial8250_compute_lcr(up, termios->c_cflag); + baud = serial8250_get_baud_rate(port, termios, old); + quot = serial8250_get_divisor(port, baud, &frac); + + /* + * Ok, we're now changing the port state. Do it with interrupts disabled. + * + * Synchronize UART_IER access against the console. + */ + scoped_guard(serial8250_rpm, up) { + guard(uart_port_lock_irqsave)(port); + + up->lcr = lcr; + serial8250_set_trigger_for_slow_speed(port, termios, baud); + serial8250_set_afe(port, termios); + uart_update_timeout(port, termios->c_cflag, baud); + serial8250_set_errors_and_ignores(port, termios); + serial8250_set_ier(port, termios); + serial8250_set_efr(port, termios); + serial8250_set_divisor(port, baud, quot, frac); + serial8250_set_fcr(port, termios); + /* Consoles manually poll CTS for hardware flow control. */ + if (uart_console(port) && + !(port->rs485.flags & SER_RS485_ENABLED) + && termios->c_cflag & CRTSCTS) { + port->mctrl |= TIOCM_RTS; + } + serial8250_set_mctrl(port, port->mctrl); + } + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} +EXPORT_SYMBOL(serial8250_do_set_termios); + +static void +serial8250_set_termios(struct uart_port *port, struct ktermios *termios, + const struct ktermios *old) +{ + if (port->set_termios) + port->set_termios(port, termios, old); + else + serial8250_do_set_termios(port, termios, old); +} + +void serial8250_do_set_ldisc(struct uart_port *port, struct ktermios *termios) +{ + if (termios->c_line == N_PPS) { + port->flags |= UPF_HARDPPS_CD; + guard(uart_port_lock_irq)(port); + serial8250_enable_ms(port); + } else { + port->flags &= ~UPF_HARDPPS_CD; + if (!UART_ENABLE_MS(port, termios->c_cflag)) { + guard(uart_port_lock_irq)(port); + serial8250_disable_ms(port); + } + } +} +EXPORT_SYMBOL_GPL(serial8250_do_set_ldisc); + +static void +serial8250_set_ldisc(struct uart_port *port, struct ktermios *termios) +{ + if (port->set_ldisc) + port->set_ldisc(port, termios); + else + serial8250_do_set_ldisc(port, termios); +} + +void serial8250_do_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_8250_port *p = up_to_u8250p(port); + + serial8250_set_sleep(p, state != 0); +} +EXPORT_SYMBOL(serial8250_do_pm); + +static void +serial8250_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + if (port->pm) + port->pm(port, state, oldstate); + else + serial8250_do_pm(port, state, oldstate); +} + +static unsigned int serial8250_port_size(struct uart_8250_port *pt) +{ + if (pt->port.mapsize) + return pt->port.mapsize; + if (is_omap1_8250(pt)) + return 0x16 << pt->port.regshift; + + return 8 << pt->port.regshift; +} + +/* + * Resource handling. + */ +static int serial8250_request_std_resource(struct uart_8250_port *up) +{ + unsigned int size = serial8250_port_size(up); + struct uart_port *port = &up->port; + + if (uart_iotype_mmio(port->iotype)) { + if (!port->mapbase) + return -EINVAL; + + if (!request_mem_region(port->mapbase, size, "serial")) + return -EBUSY; + + if (port->flags & UPF_IOREMAP) { + port->membase = ioremap(port->mapbase, size); + if (!port->membase) { + release_mem_region(port->mapbase, size); + return -ENOMEM; + } + } + } else if (uart_iotype_io(port->iotype)) { + if (!request_region(port->iobase, size, "serial")) + return -EBUSY; + } + + return 0; +} + +static void serial8250_release_std_resource(struct uart_8250_port *up) +{ + unsigned int size = serial8250_port_size(up); + struct uart_port *port = &up->port; + + if (uart_iotype_mmio(port->iotype)) { + if (!port->mapbase) + return; + + if (port->flags & UPF_IOREMAP) { + iounmap(port->membase); + port->membase = NULL; + } + + release_mem_region(port->mapbase, size); + } else if (uart_iotype_io(port->iotype)) { + release_region(port->iobase, size); + } +} + +static void serial8250_release_port(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + serial8250_release_std_resource(up); +} + +static int serial8250_request_port(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + return serial8250_request_std_resource(up); +} + +static int fcr_get_rxtrig_bytes(struct uart_8250_port *up) +{ + const struct serial8250_config *conf_type = &uart_config[up->port.type]; + unsigned char bytes; + + bytes = conf_type->rxtrig_bytes[UART_FCR_R_TRIG_BITS(up->fcr)]; + + return bytes ? bytes : -EOPNOTSUPP; +} + +static int bytes_to_fcr_rxtrig(struct uart_8250_port *up, unsigned char bytes) +{ + const struct serial8250_config *conf_type = &uart_config[up->port.type]; + int i; + + if (!conf_type->rxtrig_bytes[UART_FCR_R_TRIG_BITS(UART_FCR_R_TRIG_00)]) + return -EOPNOTSUPP; + + for (i = 1; i < UART_FCR_R_TRIG_MAX_STATE; i++) { + if (bytes < conf_type->rxtrig_bytes[i]) + /* Use the nearest lower value */ + return (--i) << UART_FCR_R_TRIG_SHIFT; + } + + return UART_FCR_R_TRIG_11; +} + +static int do_get_rxtrig(struct tty_port *port) +{ + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; + struct uart_8250_port *up = up_to_u8250p(uport); + + if (!(up->capabilities & UART_CAP_FIFO) || uport->fifosize <= 1) + return -EINVAL; + + return fcr_get_rxtrig_bytes(up); +} + +static int do_serial8250_get_rxtrig(struct tty_port *port) +{ + int rxtrig_bytes; + + mutex_lock(&port->mutex); + rxtrig_bytes = do_get_rxtrig(port); + mutex_unlock(&port->mutex); + + return rxtrig_bytes; +} + +static ssize_t rx_trig_bytes_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tty_port *port = dev_get_drvdata(dev); + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; + int rxtrig_bytes; + + if (uport->get_rxtrig) + rxtrig_bytes = uport->get_rxtrig(uport); + else + rxtrig_bytes = do_serial8250_get_rxtrig(port); + if (rxtrig_bytes < 0) + return rxtrig_bytes; + + return sysfs_emit(buf, "%d\n", rxtrig_bytes); +} + +static int do_set_rxtrig(struct tty_port *port, unsigned char bytes) +{ + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; + struct uart_8250_port *up = up_to_u8250p(uport); + int rxtrig; + + if (!(up->capabilities & UART_CAP_FIFO) || uport->fifosize <= 1) + return -EINVAL; + + rxtrig = bytes_to_fcr_rxtrig(up, bytes); + if (rxtrig < 0) + return rxtrig; + + serial8250_clear_fifos(up); + up->fcr &= ~UART_FCR_TRIGGER_MASK; + up->fcr |= (unsigned char)rxtrig; + serial_out(up, UART_FCR, up->fcr); + return 0; +} + +static int do_serial8250_set_rxtrig(struct tty_port *port, unsigned char bytes) +{ + int ret; + + mutex_lock(&port->mutex); + ret = do_set_rxtrig(port, bytes); + mutex_unlock(&port->mutex); + + return ret; +} + +static ssize_t rx_trig_bytes_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct tty_port *port = dev_get_drvdata(dev); + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; + unsigned char bytes; + int ret; + + if (!count) + return -EINVAL; + + ret = kstrtou8(buf, 10, &bytes); + if (ret < 0) + return ret; + + if (uport->set_rxtrig) + ret = uport->set_rxtrig(uport, bytes); + else + ret = do_serial8250_set_rxtrig(port, bytes); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR_RW(rx_trig_bytes); + +static struct attribute *serial8250_dev_attrs[] = { + &dev_attr_rx_trig_bytes.attr, + NULL +}; + +static struct attribute_group serial8250_dev_attr_group = { + .attrs = serial8250_dev_attrs, +}; + +static void register_dev_spec_attr_grp(struct uart_8250_port *up) +{ + const struct serial8250_config *conf_type = &uart_config[up->port.type]; + + if (conf_type->rxtrig_bytes[0]) + up->port.attr_group = &serial8250_dev_attr_group; +} + +static void serial8250_config_port(struct uart_port *port, int flags) +{ + struct uart_8250_port *up = up_to_u8250p(port); + int ret; + + /* + * Find the region that we can probe for. This in turn + * tells us whether we can probe for the type of port. + */ + ret = serial8250_request_std_resource(up); + if (ret < 0) + return; + + if (port->iotype != up->cur_iotype) + set_io_from_upio(port); + + if (flags & UART_CONFIG_TYPE) + autoconfig(up); + + /* HW bugs may trigger IRQ while IIR == NO_INT */ + if (port->type == PORT_TEGRA) + up->bugs |= UART_BUG_NOMSR; + + if (port->type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) + autoconfig_irq(up); + + if (port->type == PORT_UNKNOWN) + serial8250_release_std_resource(up); + + register_dev_spec_attr_grp(up); + up->fcr = uart_config[up->port.type].fcr; +} + +static int +serial8250_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if (ser->irq >= irq_get_nr_irqs() || ser->irq < 0 || + ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || + ser->type >= ARRAY_SIZE(uart_config) || ser->type == PORT_CIRRUS || + ser->type == PORT_STARTECH) + return -EINVAL; + return 0; +} + +static const char *serial8250_type(struct uart_port *port) +{ + int type = port->type; + + if (type >= ARRAY_SIZE(uart_config)) + type = 0; + return uart_config[type].name; +} + +static const struct uart_ops serial8250_pops = { + .tx_empty = serial8250_tx_empty, + .set_mctrl = serial8250_set_mctrl, + .get_mctrl = serial8250_get_mctrl, + .stop_tx = serial8250_stop_tx, + .start_tx = serial8250_start_tx, + .throttle = serial8250_throttle, + .unthrottle = serial8250_unthrottle, + .stop_rx = serial8250_stop_rx, + .enable_ms = serial8250_enable_ms, + .break_ctl = serial8250_break_ctl, + .startup = serial8250_startup, + .shutdown = serial8250_shutdown, + .flush_buffer = serial8250_flush_buffer, + .set_termios = serial8250_set_termios, + .set_ldisc = serial8250_set_ldisc, + .pm = serial8250_pm, + .type = serial8250_type, + .release_port = serial8250_release_port, + .request_port = serial8250_request_port, + .config_port = serial8250_config_port, + .verify_port = serial8250_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = serial8250_get_poll_char, + .poll_put_char = serial8250_put_poll_char, +#endif +}; + +void serial8250_init_port(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + + spin_lock_init(&port->lock); + port->ctrl_id = 0; + port->pm = NULL; + port->ops = &serial8250_pops; + port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE); + + up->cur_iotype = UPIO_UNKNOWN; +} +EXPORT_SYMBOL_GPL(serial8250_init_port); + +void serial8250_set_defaults(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + + if (up->port.flags & UPF_FIXED_TYPE) { + unsigned int type = up->port.type; + + if (!up->port.fifosize) + up->port.fifosize = uart_config[type].fifo_size; + if (!up->tx_loadsz) + up->tx_loadsz = uart_config[type].tx_loadsz; + if (!up->capabilities) + up->capabilities = uart_config[type].flags; + } + + set_io_from_upio(port); + + /* default dma handlers */ + if (up->dma) { + if (!up->dma->tx_dma) + up->dma->tx_dma = serial8250_tx_dma; + if (!up->dma->rx_dma) + up->dma->rx_dma = serial8250_rx_dma; + } +} +EXPORT_SYMBOL_GPL(serial8250_set_defaults); + +void serial8250_fifo_wait_for_lsr_thre(struct uart_8250_port *up, + struct nbcon_write_context *wctxt, + unsigned int count) +{ + unsigned int i; + + /* + * For console writing, enter/exit an unsafe section for each byte + * in order to pass the ownership as quickly as possible if a higher + * priority context wants ownership. Otherwise, an attempt to take + * over the ownership might timeout. The new owner will wait for + * UART_LSR_THRE before reusing the fifo. + */ + for (i = 0; i < count; i++) { + bool tx_ready; + + if (wctxt && !nbcon_enter_unsafe(wctxt)) + return; + + tx_ready = wait_for_lsr(up, UART_LSR_THRE); + + if (wctxt) + nbcon_exit_unsafe(wctxt); + + if (tx_ready) + break; + } +} +EXPORT_SYMBOL_NS_GPL(serial8250_fifo_wait_for_lsr_thre, "SERIAL_8250"); + +#ifdef CONFIG_SERIAL_8250_CONSOLE + +static void serial8250_console_putchar(struct uart_port *port, unsigned char ch) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + serial_port_out(port, UART_TX, ch); + + up->console_line_ended = (ch == '\n'); +} + +static void serial8250_console_wait_putchar(struct uart_port *port, unsigned char ch) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + wait_for_xmitr(up, UART_LSR_THRE); + serial8250_console_putchar(port, ch); +} + +/* + * Restore serial console when h/w power-off detected + */ +static void serial8250_console_restore(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + struct ktermios termios; + unsigned int baud, quot, frac = 0; + + termios.c_cflag = port->cons->cflag; + termios.c_ispeed = port->cons->ispeed; + termios.c_ospeed = port->cons->ospeed; + if (port->state->port.tty && termios.c_cflag == 0) { + termios.c_cflag = port->state->port.tty->termios.c_cflag; + termios.c_ispeed = port->state->port.tty->termios.c_ispeed; + termios.c_ospeed = port->state->port.tty->termios.c_ospeed; + } + + baud = serial8250_get_baud_rate(port, &termios, NULL); + quot = serial8250_get_divisor(port, baud, &frac); + + serial8250_set_divisor(port, baud, quot, frac); + serial_port_out(port, UART_LCR, up->lcr); + serial8250_out_MCR(up, up->mcr | UART_MCR_DTR | UART_MCR_RTS); +} + +/* + * Print a string to the serial port using the device FIFO + * + * It sends fifosize bytes and then waits for the fifo + * to get empty. + */ +static void __serial8250_console_fifo_write(struct uart_8250_port *up, + struct nbcon_write_context *wctxt, + const char *s, unsigned int count) +{ + const char *end = s + count; + unsigned int fifosize = up->tx_loadsz; + struct uart_port *port = &up->port; + unsigned int tx_count = 0; + bool cr_sent = false; + unsigned int i; + + while (s != end) { + /* Allow timeout for each byte of a possibly full FIFO */ + serial8250_fifo_wait_for_lsr_thre(up, wctxt, fifosize); + + /* + * Fill the FIFO. If a handover or takeover occurs, writing + * must be aborted since the string data is no longer valid. + */ + for (i = 0; i < fifosize && s != end; ++i) { + if (!nbcon_enter_unsafe(wctxt)) + return; + + if (*s == '\n' && !cr_sent) { + serial8250_console_putchar(port, '\r'); + cr_sent = true; + } else { + serial8250_console_putchar(port, *s++); + cr_sent = false; + } + + nbcon_exit_unsafe(wctxt); + } + tx_count = i; + } + + /* + * Allow timeout for each byte written since the caller will only wait + * for UART_LSR_BOTH_EMPTY using the timeout of a single character + */ + serial8250_fifo_wait_for_lsr_thre(up, wctxt, tx_count); +} + +static void serial8250_console_fifo_write(struct uart_8250_port *up, + struct nbcon_write_context *wctxt) +{ + __serial8250_console_fifo_write(up, wctxt, wctxt->outbuf, wctxt->len); +} + +static void __serial8250_console_byte_write(struct uart_8250_port *up, + struct nbcon_write_context *wctxt, + const char *s, unsigned int count) +{ + struct uart_port *port = &up->port; + const char *end = s + count; + + /* + * Write out the message. If a handover or takeover occurs, writing + * must be aborted since the string data is no longer valid. + */ + while (s != end) { + if (!nbcon_enter_unsafe(wctxt)) + return; + + uart_console_write(port, s++, 1, serial8250_console_wait_putchar); + + nbcon_exit_unsafe(wctxt); + } +} + +static void serial8250_console_byte_write(struct uart_8250_port *up, + struct nbcon_write_context *wctxt) +{ + __serial8250_console_byte_write(up, wctxt, wctxt->outbuf, wctxt->len); +} + +/* + * Print the console line using the appropriate variant. If ownership is lost + * at any time during printing, the printing is aborted. + */ +static void __serial8250_console_write(struct uart_8250_port *up, + struct nbcon_write_context *wctxt, + bool use_fifo) +{ + /* + * If the console printer did not fully output the previous line, it + * must have been handed or taken over. Insert a newline in order to + * maintain clean output. + */ + if (!up->console_line_ended) { + if (use_fifo) + __serial8250_console_fifo_write(up, wctxt, "\n", 1); + else + __serial8250_console_byte_write(up, wctxt, "\n", 1); + } + + if (use_fifo) + serial8250_console_fifo_write(up, wctxt); + else + serial8250_console_byte_write(up, wctxt); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + */ +void serial8250_console_write(struct uart_8250_port *up, + struct nbcon_write_context *wctxt, + bool is_atomic) +{ + struct uart_8250_em485 *em485 = up->em485; + struct uart_port *port = &up->port; + unsigned int ier; + bool use_fifo; + + if (!nbcon_enter_unsafe(wctxt)) + return; + + /* + * First, save the IER, then disable the interrupts. The special + * variant to clear the IER is used because emergency and panic + * printing is synchronized only by nbcon ownership without + * holding the port lock. + */ + ier = serial_port_in(port, UART_IER); + __serial8250_clear_IER(up); + + /* check scratch reg to see if port powered off during system sleep */ + if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) { + serial8250_console_restore(up); + up->canary = 0; + } + + if (em485) { + if (em485->tx_stopped) + up->rs485_start_tx(up, false); + mdelay(port->rs485.delay_rts_before_send); + } + + use_fifo = (up->capabilities & UART_CAP_FIFO) && + /* + * BCM283x requires to check the fifo + * after each byte. + */ + !(up->capabilities & UART_CAP_MINI) && + /* + * tx_loadsz contains the transmit fifo size + */ + up->tx_loadsz > 1 && + (up->fcr & UART_FCR_ENABLE_FIFO) && + port->state && + test_bit(TTY_PORT_INITIALIZED, &port->state->port.iflags) && + /* + * After we put a data in the fifo, the controller will send + * it regardless of the CTS state. Therefore, only use fifo + * if we don't use control flow. + */ + !uart_console_hwflow_active(&up->port); + + nbcon_exit_unsafe(wctxt); + + __serial8250_console_write(up, wctxt, use_fifo); + + /* + * Re-enter an unsafe section in order to perform final actions + * (such as re-enabling interrupts). If ownership was lost, this + * context must reacquire ownership. + */ + while (!nbcon_enter_unsafe(wctxt)) + nbcon_reacquire_nobuf(wctxt); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); + + if (em485) { + mdelay(port->rs485.delay_rts_after_send); + + /* Toggle unsafe after possibly long delay */ + nbcon_exit_unsafe(wctxt); + while (!nbcon_enter_unsafe(wctxt)) + nbcon_reacquire_nobuf(wctxt); + + if (em485->tx_stopped) + up->rs485_stop_tx(up, false); + } + + /* Toggle unsafe after possibly long delay */ + nbcon_exit_unsafe(wctxt); + while (!nbcon_enter_unsafe(wctxt)) + nbcon_reacquire_nobuf(wctxt); + + serial_port_out(port, UART_IER, ier); + + /* + * The receive handling will happen properly because the + * receive ready bit will still be set; it is not cleared + * on read. However, modem control will not, we must + * call it if we have saved something in the saved flags + * while processing with interrupts off. + */ + if (up->msr_saved_flags) { + if (is_atomic) { + /* + * For atomic, MSR handling must be deferred to + * irq_work because this may be a context that does + * not permit waking up tasks. + * + * But no irq_work may be queued when suspending. + * In that case, the MSR handling will occur during + * resume in serial8250_resume_port(). + */ + if (up->console_msr_work_allow) + irq_work_queue(&up->console_msr_work); + } else { + serial8250_modem_status(up); + } + } + + nbcon_exit_unsafe(wctxt); +} + +static unsigned int probe_baud(struct uart_port *port) +{ + unsigned char lcr, dll, dlm; + unsigned int quot; + + lcr = serial_port_in(port, UART_LCR); + serial_port_out(port, UART_LCR, lcr | UART_LCR_DLAB); + dll = serial_port_in(port, UART_DLL); + dlm = serial_port_in(port, UART_DLM); + serial_port_out(port, UART_LCR, lcr); + + quot = (dlm << 8) | dll; + return (port->uartclk / 16) / quot; +} + +/* + * irq_work handler to perform modem control during console output. + * Only triggered via ->write_atomic() callback because it may be + * in a scheduler or NMI context, unable to wake tasks. + */ +static void console_msr_handler(struct irq_work *iwp) +{ + struct uart_8250_port *up = container_of(iwp, struct uart_8250_port, console_msr_work); + struct uart_port *port = &up->port; + + guard(uart_port_lock)(port); + + serial8250_modem_status(up); +} + +int serial8250_console_setup(struct uart_port *port, char *options, bool probe) +{ + struct uart_8250_port *up = up_to_u8250p(port); + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + + if (!port->iobase && !port->membase) + return -ENODEV; + + up->console_line_ended = true; + up->console_msr_work_allow = true; + init_irq_work(&up->console_msr_work, console_msr_handler); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else if (probe) + baud = probe_baud(port); + + ret = uart_set_options(port, port->cons, baud, parity, bits, flow); + if (ret) + return ret; + + /* Track user-specified console flow control. */ + uart_set_cons_flow_enabled(port, flow == 'r'); + + if (port->dev) + pm_runtime_get_sync(port->dev); + + return 0; +} + +int serial8250_console_exit(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + irq_work_sync(&up->console_msr_work); + + if (port->dev) + pm_runtime_put_sync(port->dev); + + return 0; +} + +#endif /* CONFIG_SERIAL_8250_CONSOLE */ + +MODULE_DESCRIPTION("Base port operations for 8250/16550-type serial ports"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c new file mode 100644 index 000000000..6dd0190b4 --- /dev/null +++ b/drivers/tty/serial/8250/8250_pxa.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * drivers/tty/serial/8250/8250_pxa.c -- driver for PXA on-board UARTS + * Copyright: (C) 2013 Sergei Ianovich <ynvich@gmail.com> + * + * replaces drivers/serial/pxa.c by Nicolas Pitre + * Created: Feb 20, 2003 + * Copyright: (C) 2003 Monta Vista Software, Inc. + * + * Based on drivers/serial/8250.c by Russell King. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/serial_8250.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/clk.h> + +#include "8250.h" + +struct pxa8250_data { + int line; + struct clk *clk; +}; + +static int __maybe_unused serial_pxa_suspend(struct device *dev) +{ + struct pxa8250_data *data = dev_get_drvdata(dev); + + serial8250_suspend_port(data->line); + + return 0; +} + +static int __maybe_unused serial_pxa_resume(struct device *dev) +{ + struct pxa8250_data *data = dev_get_drvdata(dev); + + serial8250_resume_port(data->line); + + return 0; +} + +static const struct dev_pm_ops serial_pxa_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(serial_pxa_suspend, serial_pxa_resume) +}; + +static const struct of_device_id serial_pxa_dt_ids[] = { + { .compatible = "mrvl,pxa-uart", }, + { .compatible = "mrvl,mmp-uart", }, + {} +}; +MODULE_DEVICE_TABLE(of, serial_pxa_dt_ids); + +/* Uart divisor latch write */ +static void serial_pxa_dl_write(struct uart_8250_port *up, u32 value) +{ + unsigned int dll; + + serial_out(up, UART_DLL, value & 0xff); + /* + * work around Erratum #74 according to Marvel(R) PXA270M Processor + * Specification Update (April 19, 2010) + */ + dll = serial_in(up, UART_DLL); + WARN_ON(dll != (value & 0xff)); + + serial_out(up, UART_DLM, value >> 8 & 0xff); +} + + +static void serial_pxa_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct pxa8250_data *data = port->private_data; + + if (!state) + clk_prepare_enable(data->clk); + else + clk_disable_unprepare(data->clk); +} + +static int serial_pxa_probe(struct platform_device *pdev) +{ + struct uart_8250_port uart = {}; + struct pxa8250_data *data; + struct resource *mmres; + int ret; + + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mmres) + return -ENODEV; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(data->clk)) + return PTR_ERR(data->clk); + + ret = clk_prepare(data->clk); + if (ret) + return ret; + + uart.port.type = PORT_XSCALE; + uart.port.mapbase = mmres->start; + uart.port.flags = UPF_IOREMAP | UPF_SKIP_TEST | UPF_FIXED_TYPE; + uart.port.dev = &pdev->dev; + uart.port.uartclk = clk_get_rate(data->clk); + uart.port.pm = serial_pxa_pm; + uart.port.private_data = data; + + ret = uart_read_port_properties(&uart.port); + if (ret) + return ret; + + uart.port.iotype = UPIO_MEM32; + uart.port.regshift = 2; + uart.port.fifosize = 64; + uart.tx_loadsz = 32; + uart.dl_write = serial_pxa_dl_write; + + ret = serial8250_register_8250_port(&uart); + if (ret < 0) + goto err_clk; + + data->line = ret; + + platform_set_drvdata(pdev, data); + + return 0; + + err_clk: + clk_unprepare(data->clk); + return ret; +} + +static void serial_pxa_remove(struct platform_device *pdev) +{ + struct pxa8250_data *data = platform_get_drvdata(pdev); + + serial8250_unregister_port(data->line); + + clk_unprepare(data->clk); +} + +static struct platform_driver serial_pxa_driver = { + .probe = serial_pxa_probe, + .remove = serial_pxa_remove, + + .driver = { + .name = "pxa2xx-uart", + .pm = &serial_pxa_pm_ops, + .of_match_table = serial_pxa_dt_ids, + }, +}; + +module_platform_driver(serial_pxa_driver); + +MODULE_AUTHOR("Sergei Ianovich"); +MODULE_DESCRIPTION("driver for PXA on-board UARTS"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pxa2xx-uart"); diff --git a/drivers/tty/serial/8250/8250_rsa.c b/drivers/tty/serial/8250/8250_rsa.c new file mode 100644 index 000000000..da971437e --- /dev/null +++ b/drivers/tty/serial/8250/8250_rsa.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/moduleparam.h> + +#include <linux/serial.h> +#include <linux/serial_8250.h> + +#include "8250.h" + +#define PORT_RSA_MAX 4 +static unsigned long probe_rsa[PORT_RSA_MAX]; +static unsigned int probe_rsa_count; + +static const struct uart_ops *core_port_base_ops; + +static int rsa8250_request_resource(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + unsigned long start; + unsigned int size; + + if (!uart_iotype_io(port->iotype)) + return -EINVAL; + + start = UART_RSA_BASE << port->regshift; + start += port->iobase; + size = 8 << port->regshift; + + if (!request_region(start, size, "serial-rsa")) + return -EBUSY; + return 0; +} + +static void rsa8250_release_resource(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + unsigned long offset; + unsigned int size; + + if (!uart_iotype_io(port->iotype)) + return; + + offset = UART_RSA_BASE << port->regshift; + size = 8 << port->regshift; + release_region(port->iobase + offset, size); +} + +static void univ8250_config_port(struct uart_port *port, int flags) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int i; + + up->probe &= ~UART_PROBE_RSA; + if (port->type == PORT_RSA) { + if (rsa8250_request_resource(up) == 0) + up->probe |= UART_PROBE_RSA; + } else if (flags & UART_CONFIG_TYPE) { + for (i = 0; i < probe_rsa_count; i++) { + if (probe_rsa[i] == up->port.iobase) { + if (rsa8250_request_resource(up) == 0) + up->probe |= UART_PROBE_RSA; + break; + } + } + } + + core_port_base_ops->config_port(port, flags); + + if (port->type != PORT_RSA && up->probe & UART_PROBE_RSA) + rsa8250_release_resource(up); +} + +static int univ8250_request_port(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + int ret; + + ret = core_port_base_ops->request_port(port); + if (ret == 0 && port->type == PORT_RSA) { + ret = rsa8250_request_resource(up); + if (ret < 0) + core_port_base_ops->release_port(port); + } + + return ret; +} + +static void univ8250_release_port(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + if (port->type == PORT_RSA) + rsa8250_release_resource(up); + core_port_base_ops->release_port(port); +} + +/* + * It is not allowed to directly reference any symbols from 8250.ko here as + * that would result in a dependency loop between the 8250.ko and + * 8250_base.ko modules. This function is called from 8250.ko and is used to + * break the symbolic dependency cycle. Anything that is needed from 8250.ko + * has to be passed as pointers to this function which then can adjust those + * variables on 8250.ko side or store them locally as needed. + */ +void univ8250_rsa_support(struct uart_ops *ops, const struct uart_ops *core_ops) +{ + core_port_base_ops = core_ops; + ops->config_port = univ8250_config_port; + ops->request_port = univ8250_request_port; + ops->release_port = univ8250_release_port; +} +EXPORT_SYMBOL_FOR_MODULES(univ8250_rsa_support, "8250"); + +module_param_hw_array(probe_rsa, ulong, ioport, &probe_rsa_count, 0444); +MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); + +/* + * Attempts to turn on the RSA FIFO. Returns zero on failure. + * We set the port uart clock rate if we succeed. + */ +static int __rsa_enable(struct uart_8250_port *up) +{ + unsigned char mode; + int result; + + mode = serial_in(up, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + + if (!result) { + serial_out(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); + mode = serial_in(up, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + } + + if (result) + up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16; + + return result; +} + +/* + * If this is an RSA port, see if we can kick it up to the higher speed clock. + */ +void rsa_enable(struct uart_8250_port *up) +{ + if (up->port.type != PORT_RSA) + return; + + if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) { + guard(uart_port_lock_irq)(&up->port); + __rsa_enable(up); + } + if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) + serial_out(up, UART_RSA_FRR, 0); +} + +/* + * Attempts to turn off the RSA FIFO and resets the RSA board back to 115kbps compat mode. It is + * unknown why interrupts were disabled in here. However, the caller is expected to preserve this + * behaviour by grabbing the spinlock before calling this function. + */ +void rsa_disable(struct uart_8250_port *up) +{ + unsigned char mode; + int result; + + if (up->port.type != PORT_RSA) + return; + + if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) + return; + + guard(uart_port_lock_irq)(&up->port); + + mode = serial_in(up, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + + if (!result) { + serial_out(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); + mode = serial_in(up, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + } + + if (result) + up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; +} + +void rsa_autoconfig(struct uart_8250_port *up) +{ + /* Only probe for RSA ports if we got the region. */ + if (up->port.type != PORT_16550A) + return; + if (!(up->probe & UART_PROBE_RSA)) + return; + + if (__rsa_enable(up)) + up->port.type = PORT_RSA; +} + +void rsa_reset(struct uart_8250_port *up) +{ + if (up->port.type != PORT_RSA) + return; + + serial_out(up, UART_RSA_FRR, 0); +} diff --git a/drivers/tty/serial/8250/8250_rt288x.c b/drivers/tty/serial/8250/8250_rt288x.c new file mode 100644 index 000000000..bf28b8a9a --- /dev/null +++ b/drivers/tty/serial/8250/8250_rt288x.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RT288x/Au1xxx driver + */ + +#include <linux/module.h> +#include <linux/io.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/serial.h> +#include <linux/serial_8250.h> + +#include "8250.h" + +#define RT288X_DL 0x28 + +/* Au1x00/RT288x UART hardware has a weird register layout */ +static const u8 au_io_in_map[7] = { + [UART_RX] = 0, + [UART_IER] = 2, + [UART_IIR] = 3, + [UART_LCR] = 5, + [UART_MCR] = 6, + [UART_LSR] = 7, + [UART_MSR] = 8, +}; + +static const u8 au_io_out_map[5] = { + [UART_TX] = 1, + [UART_IER] = 2, + [UART_FCR] = 4, + [UART_LCR] = 5, + [UART_MCR] = 6, +}; + +static u32 au_serial_in(struct uart_port *p, unsigned int offset) +{ + if (offset >= ARRAY_SIZE(au_io_in_map)) + return UINT_MAX; + offset = au_io_in_map[offset]; + + return __raw_readl(p->membase + (offset << p->regshift)); +} + +static void au_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ + if (offset >= ARRAY_SIZE(au_io_out_map)) + return; + offset = au_io_out_map[offset]; + + __raw_writel(value, p->membase + (offset << p->regshift)); +} + +/* Au1x00 haven't got a standard divisor latch */ +static u32 au_serial_dl_read(struct uart_8250_port *up) +{ + return __raw_readl(up->port.membase + RT288X_DL); +} + +static void au_serial_dl_write(struct uart_8250_port *up, u32 value) +{ + __raw_writel(value, up->port.membase + RT288X_DL); +} + +int au_platform_setup(struct plat_serial8250_port *p) +{ + p->iotype = UPIO_AU; + + p->serial_in = au_serial_in; + p->serial_out = au_serial_out; + p->dl_read = au_serial_dl_read; + p->dl_write = au_serial_dl_write; + + p->mapsize = 0x1000; + + p->bugs |= UART_BUG_NOMSR; + + return 0; +} +EXPORT_SYMBOL_GPL(au_platform_setup); + +int rt288x_setup(struct uart_port *p) +{ + struct uart_8250_port *up = up_to_u8250p(p); + + p->iotype = UPIO_AU; + + p->serial_in = au_serial_in; + p->serial_out = au_serial_out; + up->dl_read = au_serial_dl_read; + up->dl_write = au_serial_dl_write; + + p->mapsize = 0x100; + + up->bugs |= UART_BUG_NOMSR; + + return 0; +} +EXPORT_SYMBOL_GPL(rt288x_setup); + +#ifdef CONFIG_SERIAL_8250_CONSOLE +static void au_putc(struct uart_port *port, unsigned char c) +{ + unsigned int status; + + au_serial_out(port, UART_TX, c); + + for (;;) { + status = au_serial_in(port, UART_LSR); + if (uart_lsr_tx_empty(status)) + break; + cpu_relax(); + } +} + +static void au_early_serial8250_write(struct console *console, + const char *s, unsigned int count) +{ + struct earlycon_device *device = console->data; + struct uart_port *port = &device->port; + + uart_console_write(port, s, count, au_putc); +} + +static int __init early_au_setup(struct earlycon_device *dev, const char *opt) +{ + rt288x_setup(&dev->port); + dev->con->write = au_early_serial8250_write; + + return 0; +} +OF_EARLYCON_DECLARE(palmchip, "ralink,rt2880-uart", early_au_setup); +#endif + +MODULE_DESCRIPTION("RT288x/Au1xxx UART driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_tegra.c b/drivers/tty/serial/8250/8250_tegra.c new file mode 100644 index 000000000..2f3b00757 --- /dev/null +++ b/drivers/tty/serial/8250/8250_tegra.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Serial Port driver for Tegra devices + * + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + */ + +#include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include "8250.h" + +struct tegra_uart { + struct clk *clk; + struct reset_control *rst; + int line; +}; + +static void tegra_uart_handle_break(struct uart_port *p) +{ + unsigned int status, tmout = 10000; + + while (1) { + status = p->serial_in(p, UART_LSR); + if (!(status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS))) + break; + + p->serial_in(p, UART_RX); + + if (--tmout == 0) + break; + udelay(1); + } +} + +static int tegra_uart_probe(struct platform_device *pdev) +{ + struct uart_8250_port port8250; + struct tegra_uart *uart; + struct uart_port *port; + struct resource *res; + int ret; + + uart = devm_kzalloc(&pdev->dev, sizeof(*uart), GFP_KERNEL); + if (!uart) + return -ENOMEM; + + memset(&port8250, 0, sizeof(port8250)); + + port = &port8250.port; + spin_lock_init(&port->lock); + + port->flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_FIXED_TYPE; + port->type = PORT_TEGRA; + port->dev = &pdev->dev; + port->handle_break = tegra_uart_handle_break; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + port->membase = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!port->membase) + return -ENOMEM; + + port->mapbase = res->start; + port->mapsize = resource_size(res); + + ret = uart_read_port_properties(port); + if (ret) + return ret; + + port->iotype = UPIO_MEM32; + port->regshift = 2; + + uart->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); + if (IS_ERR(uart->rst)) + return PTR_ERR(uart->rst); + + if (!port->uartclk) { + uart->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(uart->clk)) { + dev_err(&pdev->dev, "failed to get clock!\n"); + return -ENODEV; + } + + ret = clk_prepare_enable(uart->clk); + if (ret < 0) + return ret; + + port->uartclk = clk_get_rate(uart->clk); + } + + ret = reset_control_deassert(uart->rst); + if (ret) + goto err_clkdisable; + + ret = serial8250_register_8250_port(&port8250); + if (ret < 0) + goto err_ctrl_assert; + + platform_set_drvdata(pdev, uart); + uart->line = ret; + + return 0; + +err_ctrl_assert: + reset_control_assert(uart->rst); +err_clkdisable: + clk_disable_unprepare(uart->clk); + + return ret; +} + +static void tegra_uart_remove(struct platform_device *pdev) +{ + struct tegra_uart *uart = platform_get_drvdata(pdev); + + serial8250_unregister_port(uart->line); + reset_control_assert(uart->rst); + clk_disable_unprepare(uart->clk); +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_uart_suspend(struct device *dev) +{ + struct tegra_uart *uart = dev_get_drvdata(dev); + struct uart_8250_port *port8250 = serial8250_get_port(uart->line); + struct uart_port *port = &port8250->port; + + serial8250_suspend_port(uart->line); + + if (!uart_console(port) || console_suspend_enabled) + clk_disable_unprepare(uart->clk); + + return 0; +} + +static int tegra_uart_resume(struct device *dev) +{ + struct tegra_uart *uart = dev_get_drvdata(dev); + struct uart_8250_port *port8250 = serial8250_get_port(uart->line); + struct uart_port *port = &port8250->port; + + if (!uart_console(port) || console_suspend_enabled) + clk_prepare_enable(uart->clk); + + serial8250_resume_port(uart->line); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(tegra_uart_pm_ops, tegra_uart_suspend, + tegra_uart_resume); + +static const struct of_device_id tegra_uart_of_match[] = { + { .compatible = "nvidia,tegra20-uart", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_uart_of_match); + +static const struct acpi_device_id tegra_uart_acpi_match[] __maybe_unused = { + { "NVDA0100", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, tegra_uart_acpi_match); + +static struct platform_driver tegra_uart_driver = { + .driver = { + .name = "tegra-uart", + .pm = &tegra_uart_pm_ops, + .of_match_table = tegra_uart_of_match, + .acpi_match_table = ACPI_PTR(tegra_uart_acpi_match), + }, + .probe = tegra_uart_probe, + .remove = tegra_uart_remove, +}; + +module_platform_driver(tegra_uart_driver); + +MODULE_AUTHOR("Jeff Brasen <jbrasen@nvidia.com>"); +MODULE_DESCRIPTION("NVIDIA Tegra 8250 Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c new file mode 100644 index 000000000..e3db60bf5 --- /dev/null +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com> + */ + +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include "8250.h" + +/* + * This hardware is similar to 8250, but its register map is a bit different: + * - MMIO32 (regshift = 2) + * - FCR is not at 2, but 3 + * - LCR and MCR are not at 3 and 4, they share 4 + * - No SCR (Instead, CHAR can be used as a scratch register) + * - Divisor latch at 9, no divisor latch access bit + */ + +#define UNIPHIER_UART_REGSHIFT 2 + +/* bit[15:8] = CHAR, bit[7:0] = FCR */ +#define UNIPHIER_UART_CHAR_FCR (3 << (UNIPHIER_UART_REGSHIFT)) +/* bit[15:8] = LCR, bit[7:0] = MCR */ +#define UNIPHIER_UART_LCR_MCR (4 << (UNIPHIER_UART_REGSHIFT)) +/* Divisor Latch Register */ +#define UNIPHIER_UART_DLR (9 << (UNIPHIER_UART_REGSHIFT)) + +struct uniphier8250_priv { + int line; + struct clk *clk; + spinlock_t atomic_write_lock; +}; + +#ifdef CONFIG_SERIAL_8250_CONSOLE +static int __init uniphier_early_console_setup(struct earlycon_device *device, + const char *options) +{ + if (!device->port.membase) + return -ENODEV; + + /* This hardware always expects MMIO32 register interface. */ + device->port.iotype = UPIO_MEM32; + device->port.regshift = UNIPHIER_UART_REGSHIFT; + + /* + * Do not touch the divisor register in early_serial8250_setup(); + * we assume it has been initialized by a boot loader. + */ + device->baud = 0; + + return early_serial8250_setup(device, options); +} +OF_EARLYCON_DECLARE(uniphier, "socionext,uniphier-uart", + uniphier_early_console_setup); +#endif + +/* + * The register map is slightly different from that of 8250. + * IO callbacks must be overridden for correct access to FCR, LCR, MCR and SCR. + */ +static u32 uniphier_serial_in(struct uart_port *p, unsigned int offset) +{ + unsigned int valshift = 0; + + switch (offset) { + case UART_SCR: + /* No SCR for this hardware. Use CHAR as a scratch register */ + valshift = 8; + offset = UNIPHIER_UART_CHAR_FCR; + break; + case UART_LCR: + valshift = 8; + fallthrough; + case UART_MCR: + offset = UNIPHIER_UART_LCR_MCR; + break; + default: + offset <<= UNIPHIER_UART_REGSHIFT; + break; + } + + /* + * The return value must be masked with 0xff because some registers + * share the same offset that must be accessed by 32-bit write/read. + * 8 or 16 bit access to this hardware result in unexpected behavior. + */ + return (readl(p->membase + offset) >> valshift) & 0xff; +} + +static void uniphier_serial_out(struct uart_port *p, unsigned int offset, u32 value) +{ + unsigned int valshift = 0; + bool normal = false; + + switch (offset) { + case UART_SCR: + /* No SCR for this hardware. Use CHAR as a scratch register */ + valshift = 8; + fallthrough; + case UART_FCR: + offset = UNIPHIER_UART_CHAR_FCR; + break; + case UART_LCR: + valshift = 8; + /* Divisor latch access bit does not exist. */ + value &= ~UART_LCR_DLAB; + fallthrough; + case UART_MCR: + offset = UNIPHIER_UART_LCR_MCR; + break; + default: + offset <<= UNIPHIER_UART_REGSHIFT; + normal = true; + break; + } + + if (normal) { + writel(value, p->membase + offset); + } else { + /* + * Special case: two registers share the same address that + * must be 32-bit accessed. As this is not longer atomic safe, + * take a lock just in case. + */ + struct uniphier8250_priv *priv = p->private_data; + unsigned long flags; + u32 tmp; + + spin_lock_irqsave(&priv->atomic_write_lock, flags); + tmp = readl(p->membase + offset); + tmp &= ~(0xff << valshift); + tmp |= value << valshift; + writel(tmp, p->membase + offset); + spin_unlock_irqrestore(&priv->atomic_write_lock, flags); + } +} + +/* + * This hardware does not have the divisor latch access bit. + * The divisor latch register exists at different address. + * Override dl_read/write callbacks. + */ +static u32 uniphier_serial_dl_read(struct uart_8250_port *up) +{ + return readl(up->port.membase + UNIPHIER_UART_DLR); +} + +static void uniphier_serial_dl_write(struct uart_8250_port *up, u32 value) +{ + writel(value, up->port.membase + UNIPHIER_UART_DLR); +} + +static int uniphier_uart_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct uart_8250_port up; + struct uniphier8250_priv *priv; + struct resource *regs; + void __iomem *membase; + int ret; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(dev, "failed to get memory resource\n"); + return -EINVAL; + } + + membase = devm_ioremap(dev, regs->start, resource_size(regs)); + if (!membase) + return -ENOMEM; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + memset(&up, 0, sizeof(up)); + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(dev, "failed to get clock\n"); + return PTR_ERR(priv->clk); + } + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + up.port.uartclk = clk_get_rate(priv->clk); + + spin_lock_init(&priv->atomic_write_lock); + + up.port.dev = dev; + up.port.private_data = priv; + up.port.mapbase = regs->start; + up.port.mapsize = resource_size(regs); + up.port.membase = membase; + + ret = uart_read_port_properties(&up.port); + if (ret) + return ret; + + up.port.type = PORT_16550A; + up.port.iotype = UPIO_MEM32; + up.port.fifosize = 64; + up.port.regshift = UNIPHIER_UART_REGSHIFT; + up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE; + up.capabilities = UART_CAP_FIFO; + + if (of_property_read_bool(dev->of_node, "auto-flow-control")) + up.capabilities |= UART_CAP_AFE; + + up.port.serial_in = uniphier_serial_in; + up.port.serial_out = uniphier_serial_out; + up.dl_read = uniphier_serial_dl_read; + up.dl_write = uniphier_serial_dl_write; + + ret = serial8250_register_8250_port(&up); + if (ret < 0) { + dev_err(dev, "failed to register 8250 port\n"); + clk_disable_unprepare(priv->clk); + return ret; + } + priv->line = ret; + + platform_set_drvdata(pdev, priv); + + return 0; +} + +static void uniphier_uart_remove(struct platform_device *pdev) +{ + struct uniphier8250_priv *priv = platform_get_drvdata(pdev); + + serial8250_unregister_port(priv->line); + clk_disable_unprepare(priv->clk); +} + +static int __maybe_unused uniphier_uart_suspend(struct device *dev) +{ + struct uniphier8250_priv *priv = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(priv->line); + + serial8250_suspend_port(priv->line); + + if (!uart_console(&up->port) || console_suspend_enabled) + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int __maybe_unused uniphier_uart_resume(struct device *dev) +{ + struct uniphier8250_priv *priv = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(priv->line); + int ret; + + if (!uart_console(&up->port) || console_suspend_enabled) { + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + } + + serial8250_resume_port(priv->line); + + return 0; +} + +static const struct dev_pm_ops uniphier_uart_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(uniphier_uart_suspend, uniphier_uart_resume) +}; + +static const struct of_device_id uniphier_uart_match[] = { + { .compatible = "socionext,uniphier-uart" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, uniphier_uart_match); + +static struct platform_driver uniphier_uart_platform_driver = { + .probe = uniphier_uart_probe, + .remove = uniphier_uart_remove, + .driver = { + .name = "uniphier-uart", + .of_match_table = uniphier_uart_match, + .pm = &uniphier_uart_pm_ops, + }, +}; +module_platform_driver(uniphier_uart_platform_driver); + +MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>"); +MODULE_DESCRIPTION("UniPhier UART driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig new file mode 100644 index 000000000..fa6f33303 --- /dev/null +++ b/drivers/tty/serial/8250/Kconfig @@ -0,0 +1,610 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# The 8250/16550 serial drivers. You shouldn't be in this list unless +# you somehow have an implicit or explicit dependency on SERIAL_8250. +# + +config SERIAL_8250 + tristate "8250/16550 and compatible serial support" + depends on !S390 + select SERIAL_CORE + select SERIAL_MCTRL_GPIO if GPIOLIB + help + This selects whether you want to include the driver for the standard + serial ports. The standard answer is Y. People who might say N + here are those that are setting up dedicated Ethernet WWW/FTP + servers, or users that have one of the various bus mice instead of a + serial mouse and don't intend to use their machine's standard serial + port for anything. + + To compile this driver as a module, choose M here: the + module will be called 8250. + [WARNING: Do not compile this driver as a module if you are using + non-standard serial ports, since the configuration information will + be lost when the driver is unloaded. This limitation may be lifted + in the future.] + + BTW1: If you have a mouseman serial mouse which is not recognized by + the X window system, try running gpm first. + + BTW2: If you intend to use a software modem (also called Winmodem) + under Linux, forget it. These modems are crippled and require + proprietary drivers which are only available under Windows. + + Most people will say Y or M here, so that they can use serial mice, + modems and similar devices connecting to the standard serial ports. + +config SERIAL_8250_PNP + bool "8250/16550 PNP device support" if EXPERT + depends on SERIAL_8250 && PNP + default y + help + This builds standard PNP serial support. You may be able to + disable this feature if you only need legacy serial support. + +config SERIAL_8250_16550A_VARIANTS + bool "Support for variants of the 16550A serial port" + depends on SERIAL_8250 + default !X86 + help + The 8250 driver can probe for many variants of the venerable 16550A + serial port. Doing so takes additional time at boot. + + On modern systems, especially those using serial only for a simple + console, you can say N here. + +config SERIAL_8250_FINTEK + bool "Support for Fintek variants" + depends on SERIAL_8250 && HAS_IOPORT + help + Selecting this option will add support for the RS232 and RS485 + capabilities of the Fintek F81216A LPC to 4 UART as well similar + variants. + + If this option is not selected the device will be configured as a + standard 16550A serial port, however the device may not function + correctly without this option enabled. + + If unsure, say N. + +config SERIAL_8250_CONSOLE + bool "Console on 8250/16550 and compatible serial port" + depends on SERIAL_8250=y + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + help + If you say Y here, it will be possible to use a serial port as the + system console (the system console is the device which receives all + kernel messages and warnings and which allows logins in single user + mode). This could be useful if some terminal or printer is connected + to that serial port. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyS1". (Try "man bootparam" or see the documentation of + your boot loader (grub or lilo or loadlin) about how to pass options + to the kernel at boot time.) + + If you don't have a VGA card installed and you say Y here, the + kernel will automatically use the first serial line, /dev/ttyS0, as + system console. + + You can set that using a kernel command line option such as + "console=uart8250,io,0x3f8,9600n8" + "console=uart8250,mmio,0xff5e0000,115200n8". + and it will switch to normal serial console when the corresponding + port is ready. + "earlycon=uart8250,io,0x3f8,9600n8" + "earlycon=uart8250,mmio,0xff5e0000,115200n8". + it will not only setup early console. + + If unsure, say N. + +config SERIAL_8250_PARISC + tristate + depends on SERIAL_8250 && PARISC + default SERIAL_8250 + +config SERIAL_8250_DMA + bool "DMA support for 16550 compatible UART controllers" if EXPERT + depends on SERIAL_8250 && DMADEVICES=y + default SERIAL_8250 + help + This builds DMA support that can be used with 8250/16650 + compatible UART controllers that support DMA signaling. + +config SERIAL_8250_PCILIB + bool + depends on SERIAL_8250 && PCI + +config SERIAL_8250_PCI + tristate "8250/16550 PCI device support" + depends on SERIAL_8250 && PCI + select SERIAL_8250_PCILIB + default SERIAL_8250 + help + This builds standard PCI serial support. You may be able to + disable this feature if you only need legacy serial support. + Saves about 9K. + Note that serial ports on NetMos 9835 Multi-I/O cards are handled + by the parport_serial driver, enabled with CONFIG_PARPORT_SERIAL. + +config SERIAL_8250_EXAR + tristate "8250/16550 Exar/Commtech PCI/PCIe device support" + depends on SERIAL_8250 && PCI + select SERIAL_8250_PCILIB + select EEPROM_93CX6 + default SERIAL_8250 + help + This builds support for XR17C1xx, XR17V3xx and some Commtech + 422x PCIe serial cards that are not covered by the more generic + SERIAL_8250_PCI option. + +config SERIAL_8250_MOXA_PCIE + tristate "8250/16550 Moxa PCIe device support" + depends on SERIAL_8250 && PCI + default SERIAL_8250 + help + Say Y here if you have a Moxa PCIe serial card. + + To compile this driver as a module, choose M here: the + module will be called 8250_mxpcie. + +config SERIAL_8250_HP300 + tristate + depends on SERIAL_8250 && HP300 + default SERIAL_8250 + +config SERIAL_8250_CS + tristate "8250/16550 PCMCIA device support" + depends on PCMCIA && SERIAL_8250 && HAS_IOPORT + help + Say Y here to enable support for 16-bit PCMCIA serial devices, + including serial port cards, modems, and the modem functions of + multi-function Ethernet/modem cards. (PCMCIA- or PC-cards are + credit-card size devices often used with laptops.) + + To compile this driver as a module, choose M here: the + module will be called serial_cs. + + If unsure, say N. + +config SERIAL_8250_MEN_MCB + tristate "MEN MCB UART device support" + depends on MCB && SERIAL_8250 + help + This enables support for FPGA based UARTs found on many MEN + boards. This driver enables support for the 16z025, 16z057 + and 16z125 UARTs. + + To compile this driver as a module, chose M here: the + module will be called 8250_men_mcb. + + +config SERIAL_8250_NR_UARTS + int "Maximum number of 8250/16550 serial ports" + depends on SERIAL_8250 + default "4" + help + Set this to the number of serial ports you want the driver + to support. This includes any ports discovered via ACPI or + PCI enumeration and any ports that may be added at run-time + via hot-plug, or any ISA multi-port serial cards. + +config SERIAL_8250_RUNTIME_UARTS + int "Number of 8250/16550 serial ports to register at runtime" + depends on SERIAL_8250 + range 0 SERIAL_8250_NR_UARTS + default "4" + help + Set this to the maximum number of serial ports you want + the kernel to register at boot time. This can be overridden + with the module parameter "nr_uarts", or boot-time parameter + 8250.nr_uarts + +config SERIAL_8250_EXTENDED + bool "Extended 8250/16550 serial driver options" + depends on SERIAL_8250 + help + If you wish to use any non-standard features of the standard "dumb" + driver, say Y here. This includes HUB6 support, shared serial + interrupts, special multiport support, support for more than the + four COM 1/2/3/4 boards, etc. + + Note that the answer to this question won't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about serial driver options. If unsure, say N. + +config SERIAL_8250_SHARE_IRQ + bool "Support for sharing serial interrupts" + depends on SERIAL_8250_EXTENDED + help + Some serial boards have hardware support which allows multiple dumb + serial ports on the same board to share a single IRQ. To enable + support for this in the serial driver, say Y here. + +config SERIAL_8250_DETECT_IRQ + bool "Autodetect IRQ on standard ports (unsafe)" + depends on SERIAL_8250_EXTENDED + help + Say Y here if you want the kernel to try to guess which IRQ + to use for your serial port. + + This is considered unsafe; it is far better to configure the IRQ in + a boot script using the setserial command. + + If unsure, say N. + +config SERIAL_8250_RSA + bool "Support RSA serial ports" + depends on SERIAL_8250_EXTENDED + help + Say Y here if you have a IODATA RSA-DV II/S ISA card and + would like to use its >115kbps speeds. + You will need to provide module parameter "probe_rsa", or boot-time + parameter 8250.probe_rsa with I/O addresses of this card then. + + If you don't have such card, or if unsure, say N. + +config SERIAL_8250_MANY_PORTS + bool "Support more than 4 legacy serial ports" + depends on SERIAL_8250_EXTENDED + help + Say Y here if you have dumb serial boards other than the four + standard COM 1/2/3/4 ports. This may happen if you have an AST + FourPort, Accent Async, Boca (read the Boca mini-HOWTO, available + from <https://www.tldp.org/docs.html#howto>), or other custom + serial port hardware which acts similar to standard serial port + hardware. If you only use the standard COM 1/2/3/4 ports, you can + say N here to save some memory. You can also say Y if you have an + "intelligent" multiport card such as Digiboards, etc. + +# +# Multi-port serial cards +# + +config SERIAL_8250_FOURPORT + tristate "Support Fourport cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have an AST FourPort serial board. + + To compile this driver as a module, choose M here: the module + will be called 8250_fourport. + +config SERIAL_8250_ACCENT + tristate "Support Accent cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have an Accent Async serial board. + + To compile this driver as a module, choose M here: the module + will be called 8250_accent. + +config SERIAL_8250_BOCA + tristate "Support Boca cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have a Boca serial board. Please read the Boca + mini-HOWTO, available from <https://www.tldp.org/docs.html#howto> + + To compile this driver as a module, choose M here: the module + will be called 8250_boca. + +config SERIAL_8250_EXAR_ST16C554 + tristate "Support Exar ST16C554/554D Quad UART" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + The Uplogix Envoy TU301 uses this Exar Quad UART. If you are + tinkering with your Envoy TU301, or have a machine with this UART, + say Y here. + + To compile this driver as a module, choose M here: the module + will be called 8250_exar_st16c554. + +config SERIAL_8250_HUB6 + tristate "Support Hub6 cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have a HUB6 serial board. + + To compile this driver as a module, choose M here: the module + will be called 8250_hub6. + +config SERIAL_8250_PCI1XXXX + tristate "Microchip 8250 based serial port" + depends on SERIAL_8250 && PCI + select SERIAL_8250_PCILIB + help + Select this option if you have a setup with Microchip PCIe + Switch with serial port enabled and wish to enable 8250 + serial driver for the serial interface. This driver support + will ensure to support baud rates upto 1.5Mpbs. + +config SERIAL_8250_ASPEED_VUART + tristate "Aspeed Virtual UART" + depends on SERIAL_8250 + depends on OF + depends on MFD_SYSCON + depends on ARCH_ASPEED || COMPILE_TEST + select REGMAP + help + If you want to use the virtual UART (VUART) device on Aspeed + BMC platforms, enable this option. This enables the 16550A- + compatible device on the local LPC bus, giving a UART device + with no physical RS232 connections. + +# +# Misc. options/drivers. +# + +config SERIAL_8250_ACORN + tristate "Acorn expansion card serial port support" + depends on ARCH_ACORN && SERIAL_8250 + help + If you have an Atomwide Serial card or Serial Port card for an Acorn + system, say Y to this option. The driver can handle 1, 2, or 3 port + cards. If unsure, say N. + +config SERIAL_8250_BCM2835AUX + tristate "BCM2835 auxiliar mini UART support" + depends on ARCH_BCM2835 || COMPILE_TEST + depends on SERIAL_8250 && SERIAL_8250_SHARE_IRQ + help + Support for the BCM2835 auxiliar mini UART. + + Features and limitations of the UART are + Registers are similar to 16650 registers, + set bits in the control registers that are unsupported + are ignored and read back as 0 + 7/8 bit operation with 1 start and 1 stop bit + 8 symbols deep fifo for rx and tx + SW controlled RTS and SW readable CTS + Clock rate derived from system clock + Uses 8 times oversampling (compared to 16 times for 16650) + Missing break detection (but break generation) + Missing framing error detection + Missing parity bit + Missing receive time-out interrupt + Missing DCD, DSR, DTR and RI signals + + If unsure, say N. + +config SERIAL_8250_FSL + tristate "Freescale 16550 UART support" if COMPILE_TEST && !(PPC || ARM || ARM64) + depends on SERIAL_8250 + default SERIAL_8250 if PPC || ARM || ARM64 + help + Selecting this option enables a workaround for a break-detection + erratum for Freescale 16550 UARTs in the 8250 driver. It also + enables support for ACPI enumeration. + +config SERIAL_8250_DFL + tristate "DFL bus driver for Altera 16550 UART" + depends on SERIAL_8250 && FPGA_DFL + help + This option enables support for a Device Feature List (DFL) bus + driver for the Altera 16550 UART. One or more Altera 16550 UARTs + can be instantiated in a FPGA and then be discovered during + enumeration of the DFL bus. + + To compile this driver as a module, chose M here: the + module will be called 8250_dfl. + +config SERIAL_8250_DW + tristate "Support for Synopsys DesignWare 8250 quirks" + depends on SERIAL_8250 + select SERIAL_8250_DWLIB + help + Selecting this option will enable handling of the extra features + present in the Synopsys DesignWare APB UART. + +config SERIAL_8250_EM + tristate "Support for Emma Mobile integrated serial port" + depends on SERIAL_8250 && HAVE_CLK + depends on ARCH_RENESAS || COMPILE_TEST + help + Selecting this option will add support for the integrated serial + port hardware found on the Emma Mobile line of processors. + If unsure, say N. + +config SERIAL_8250_IOC3 + tristate "SGI IOC3 8250 UART support" + depends on SERIAL_8250 + depends on SGI_MFD_IOC3 || COMPILE_TEST + select SERIAL_8250_EXTENDED + select SERIAL_8250_SHARE_IRQ + help + Enable this if you have a SGI Origin or Octane machine. This module + provides basic serial support by directly driving the UART chip + behind the IOC3 device on those systems. Maximum baud speed is + 38400bps using this driver. + +config SERIAL_8250_KEBA + tristate "Support for KEBA 8250 UART" + depends on SERIAL_8250 + depends on KEBA_CP500 + help + Selecting this option will add support for KEBA UARTs. These UARTs + are used for the serial interfaces of KEBA PLCs. + + This driver can also be built as a module. If so, the module will + be called 8250_keba. + + If unsure, say N. + +config SERIAL_8250_RT288X + bool "Ralink RT288x/RT305x/RT3662/RT3883 serial port support" + depends on SERIAL_8250 + default y if MIPS_ALCHEMY || SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620 + help + Selecting this option will add support for the alternate register + layout used by Ralink RT288x/RT305x, Alchemy Au1xxx, and some others. + If unsure, say N. + +config SERIAL_8250_OMAP + tristate "Support for OMAP internal UART (8250 based driver)" + depends on SERIAL_8250 + depends on ARCH_OMAP2PLUS || ARCH_K3 || COMPILE_TEST + help + If you have a machine based on an Texas Instruments OMAP CPU you + can enable its onboard serial ports by enabling this option. + + This driver uses ttyS instead of ttyO. + +config SERIAL_8250_OMAP_TTYO_FIXUP + bool "Replace ttyO with ttyS" + depends on SERIAL_8250_OMAP=y && SERIAL_8250_CONSOLE + default y + help + This option replaces the "console=ttyO" argument with the matching + ttyS argument if the user did not specified it on the command line. + This ensures that the user can see the kernel output during boot + which he wouldn't see otherwise. The getty has still to be configured + for ttyS instead of ttyO regardless of this option. + This option is intended for people who "automatically" enable this + driver without knowing that this driver requires a different console= + argument. If you read this, please keep this option disabled and + instead update your kernel command line. If you prepare a kernel for a + distribution or other kind of larger user base then you probably want + to keep this option enabled. Otherwise people might complain about a + not booting kernel because the serial console remains silent in case + they forgot to update the command line. + +config SERIAL_8250_LOONGSON + tristate "Loongson 8250 based serial port" + depends on SERIAL_8250 + depends on LOONGARCH || MACH_LOONGSON64 || COMPILE_TEST + help + If you have a machine based on LoongArch CPU or MIPS-based Loongson + 3A4000 CPU you can enable its onboard serial ports by enabling this + option. The option is applicable to both devicetree and ACPI, say Y + to enable this option. + If unsure, say N. + +config SERIAL_8250_LPC18XX + tristate "NXP LPC18xx/43xx serial port support" + depends on SERIAL_8250 && OF && (ARCH_LPC18XX || COMPILE_TEST) + default ARCH_LPC18XX + help + If you have a LPC18xx/43xx based board and want to use the + serial port, say Y to this option. If unsure, say Y. + +config SERIAL_8250_MT6577 + tristate "Mediatek serial port support" + depends on SERIAL_8250 + depends on ARCH_MEDIATEK || COMPILE_TEST + help + If you have a Mediatek based board and want to use the + serial port, say Y to this option. If unsure, say N. + +config SERIAL_8250_UNIPHIER + tristate "Support for UniPhier on-chip UART" + depends on SERIAL_8250 + depends on ARCH_UNIPHIER || COMPILE_TEST + help + If you have a UniPhier based board and want to use the on-chip + serial ports, say Y to this option. If unsure, say N. + +config SERIAL_8250_INGENIC + tristate "Support for Ingenic SoC serial ports" + depends on SERIAL_8250 + depends on OF_FLATTREE + depends on MIPS || COMPILE_TEST + help + If you have a system using an Ingenic SoC and wish to make use of + its UARTs, say Y to this option. If unsure, say N. + +config SERIAL_8250_LPSS + tristate "Support for serial ports on Intel LPSS platforms" + default SERIAL_8250 + depends on SERIAL_8250 && PCI + depends on X86 || COMPILE_TEST + select SERIAL_8250_DWLIB + select DW_DMAC_CORE if SERIAL_8250_DMA + select DW_DMAC_PCI if (SERIAL_8250_DMA && X86_INTEL_LPSS) + select RATIONAL + help + Selecting this option will enable handling of the UART found on + various Intel platforms such as: + - Intel Baytrail SoC + - Intel Braswell SoC + - Intel Quark X1000 SoC + that are not covered by the more generic SERIAL_8250_PCI option. + +config SERIAL_8250_MID + tristate "Support for serial ports on Intel MID platforms" + default SERIAL_8250 + depends on SERIAL_8250 && PCI + depends on X86 || COMPILE_TEST + select HSU_DMA if SERIAL_8250_DMA + select HSU_DMA_PCI if (HSU_DMA && X86_INTEL_MID) + select RATIONAL + help + Selecting this option will enable handling of the UART found on + Intel Medfield SOC and various other Intel platforms that is not + covered by the more generic SERIAL_8250_PCI option. + +config SERIAL_8250_PERICOM + tristate "Support for Pericom and Acces I/O serial ports" + default SERIAL_8250 + depends on SERIAL_8250 && PCI + help + Selecting this option will enable handling of the Pericom and Acces + I/O UARTs that are not covered by the more generic SERIAL_8250_PCI + option. + +config SERIAL_8250_PXA + tristate "PXA serial port support" + depends on SERIAL_8250 + depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST + help + If you have a machine based on an Intel XScale PXA2xx CPU you can + enable its onboard serial ports by enabling this option. The option is + applicable to both devicetree and legacy boards, and early console is + part of its support. + +config SERIAL_8250_TEGRA + tristate "8250 support for Tegra serial ports" + default SERIAL_8250 + depends on SERIAL_8250 + depends on ARCH_TEGRA || COMPILE_TEST + help + Select this option if you have machine with an NVIDIA Tegra SoC and + wish to enable 8250 serial driver for the Tegra serial interfaces. + +config SERIAL_8250_BCM7271 + tristate "Broadcom 8250 based serial port" + depends on SERIAL_8250 && (ARCH_BRCMSTB || COMPILE_TEST) + default ARCH_BRCMSTB + help + If you have a Broadcom STB based board and want to use the + enhanced features of the Broadcom 8250 based serial port, + including DMA support and high accuracy BAUD rates, say + Y to this option. If unsure, say N. + +config SERIAL_8250_NI + tristate "NI 16550 based serial port" + depends on SERIAL_8250 + depends on X86 || COMPILE_TEST + help + This driver supports the integrated serial ports on National + Instruments (NI) controller hardware. This is required for all NI + controller models with onboard RS-485 or dual-mode RS-485/RS-232 + ports. + + To compile this driver as a module, choose M here: the module + will be called 8250_ni. + +config SERIAL_OF_PLATFORM + tristate "Devicetree based probing for 8250 ports" + depends on SERIAL_8250 && OF + help + This option is used for all 8250 compatible serial ports that + are probed through devicetree, including Open Firmware based + PowerPC systems and embedded systems on architectures using the + flattened device tree format. + +config SERIAL_8250_DWLIB + bool diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile new file mode 100644 index 000000000..46adadcfe --- /dev/null +++ b/drivers/tty/serial/8250/Makefile @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the 8250 serial device drivers. +# + +obj-$(CONFIG_SERIAL_8250) += 8250.o +8250-y := 8250_core.o +8250-y += 8250_platform.o +8250-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o + +obj-$(CONFIG_SERIAL_8250) += 8250_base.o +8250_base-y := 8250_port.o +8250_base-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o +8250_base-$(CONFIG_SERIAL_8250_DWLIB) += 8250_dwlib.o +8250_base-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o +8250_base-$(CONFIG_SERIAL_8250_PCILIB) += 8250_pcilib.o +8250_base-$(CONFIG_SERIAL_8250_RSA) += 8250_rsa.o + +obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o + +obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o +obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o +obj-$(CONFIG_SERIAL_8250_ASPEED_VUART) += 8250_aspeed_vuart.o +obj-$(CONFIG_SERIAL_8250_BCM2835AUX) += 8250_bcm2835aux.o +obj-$(CONFIG_SERIAL_8250_BCM7271) += 8250_bcm7271.o +obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o +ifeq ($(CONFIG_SERIAL_8250),y) +obj-$(CONFIG_X86_INTEL_CE) += 8250_ce4100.o +endif +obj-$(CONFIG_SERIAL_8250_DFL) += 8250_dfl.o +obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o +obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o +obj-$(CONFIG_SERIAL_8250_EXAR) += 8250_exar.o +obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o +obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o +obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o +obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o +obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o +obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o +obj-$(CONFIG_SERIAL_8250_IOC3) += 8250_ioc3.o +obj-$(CONFIG_SERIAL_8250_KEBA) += 8250_keba.o +obj-$(CONFIG_SERIAL_8250_LOONGSON) += 8250_loongson.o +obj-$(CONFIG_SERIAL_8250_LPC18XX) += 8250_lpc18xx.o +obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o +obj-$(CONFIG_SERIAL_8250_MEN_MCB) += 8250_men_mcb.o +obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o +obj-$(CONFIG_SERIAL_8250_MOXA_PCIE) += 8250_mxpcie.o +obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o +obj-$(CONFIG_SERIAL_8250_NI) += 8250_ni.o +obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o +obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o +obj-$(CONFIG_SERIAL_8250_PARISC) += 8250_parisc.o +obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o +obj-$(CONFIG_SERIAL_8250_PCI1XXXX) += 8250_pci1xxxx.o +obj-$(CONFIG_SERIAL_8250_PERICOM) += 8250_pericom.o +obj-$(CONFIG_SERIAL_8250_PXA) += 8250_pxa.o +obj-$(CONFIG_SERIAL_8250_RT288X) += 8250_rt288x.o +obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o +obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o +obj-$(CONFIG_SERIAL_8250_TEGRA) += 8250_tegra.o + +CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c new file mode 100644 index 000000000..80483fc73 --- /dev/null +++ b/drivers/tty/serial/8250/serial_cs.c @@ -0,0 +1,868 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) +/*====================================================================== + + A driver for PCMCIA serial devices + + serial_cs.c 1.134 2002/05/04 05:48:53 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + <dahinds@users.sourceforge.net>. Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU General Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/serial_core.h> +#include <linux/delay.h> +#include <linux/major.h> +#include <asm/io.h> + +#include <pcmcia/cistpl.h> +#include <pcmcia/ciscode.h> +#include <pcmcia/ds.h> +#include <pcmcia/cisreg.h> + +#include "8250.h" + + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Enable the speaker? */ +static int do_sound = 1; +/* Skip strict UART tests? */ +static int buggy_uart; + +module_param(do_sound, int, 0444); +module_param(buggy_uart, int, 0444); + +/*====================================================================*/ + +/* Table of multi-port card ID's */ + +struct serial_quirk { + unsigned int manfid; + unsigned int prodid; + int multi; /* 1 = multifunction, > 1 = # ports */ + void (*config)(struct pcmcia_device *); + void (*setup)(struct pcmcia_device *, struct uart_8250_port *); + void (*wakeup)(struct pcmcia_device *); + int (*post)(struct pcmcia_device *); +}; + +struct serial_info { + struct pcmcia_device *p_dev; + int ndev; + int multi; + int slave; + int manfid; + int prodid; + int c950ctrl; + int line[4]; + const struct serial_quirk *quirk; +}; + +/* + * vers_1 5.0, "Brain Boxes", "2-Port RS232 card", "r6" + * manfid 0x0160, 0x0104 + * This card appears to have a 14.7456MHz clock. + */ +/* Generic Modem: MD55x (GPRS/EDGE) have + * Elan VPU16551 UART with 14.7456MHz oscillator + * manfid 0x015D, 0x4C45 + */ +static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_8250_port *uart) +{ + uart->port.uartclk = 14745600; +} + +static int quirk_post_ibm(struct pcmcia_device *link) +{ + u8 val; + int ret; + + ret = pcmcia_read_config_byte(link, 0x800, &val); + if (ret) + goto failed; + + ret = pcmcia_write_config_byte(link, 0x800, val | 1); + if (ret) + goto failed; + return 0; + + failed: + return -ENODEV; +} + +/* + * Nokia cards are not really multiport cards. Shouldn't this + * be handled by setting the quirk entry .multi = 0 | 1 ? + */ +static void quirk_config_nokia(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + + if (info->multi > 1) + info->multi = 1; +} + +static void quirk_wakeup_oxsemi(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + + if (info->c950ctrl) + outb(12, info->c950ctrl + 1); +} + +/* request_region? oxsemi branch does no request_region too... */ +/* + * This sequence is needed to properly initialize MC45 attached to OXCF950. + * I tried decreasing these msleep()s, but it worked properly (survived + * 1000 stop/start operations) with these timeouts (or bigger). + */ +static void quirk_wakeup_possio_gcc(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + unsigned int ctrl = info->c950ctrl; + + outb(0xA, ctrl + 1); + msleep(100); + outb(0xE, ctrl + 1); + msleep(300); + outb(0xC, ctrl + 1); + msleep(100); + outb(0xE, ctrl + 1); + msleep(200); + outb(0xF, ctrl + 1); + msleep(100); + outb(0xE, ctrl + 1); + msleep(100); + outb(0xC, ctrl + 1); +} + +/* + * Socket Dual IO: this enables irq's for second port + */ +static void quirk_config_socket(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + + if (info->multi) + link->config_flags |= CONF_ENABLE_ESR; +} + +static const struct serial_quirk quirks[] = { + { + .manfid = 0x0160, + .prodid = 0x0104, + .multi = -1, + .setup = quirk_setup_brainboxes_0104, + }, { + .manfid = 0x015D, + .prodid = 0x4C45, + .multi = -1, + .setup = quirk_setup_brainboxes_0104, + }, { + .manfid = MANFID_IBM, + .prodid = ~0, + .multi = -1, + .post = quirk_post_ibm, + }, { + .manfid = MANFID_INTEL, + .prodid = PRODID_INTEL_DUAL_RS232, + .multi = 2, + }, { + .manfid = MANFID_NATINST, + .prodid = PRODID_NATINST_QUAD_RS232, + .multi = 4, + }, { + .manfid = MANFID_NOKIA, + .prodid = ~0, + .multi = -1, + .config = quirk_config_nokia, + }, { + .manfid = MANFID_OMEGA, + .prodid = PRODID_OMEGA_QSP_100, + .multi = 4, + }, { + .manfid = MANFID_OXSEMI, + .prodid = ~0, + .multi = -1, + .wakeup = quirk_wakeup_oxsemi, + }, { + .manfid = MANFID_POSSIO, + .prodid = PRODID_POSSIO_GCC, + .multi = -1, + .wakeup = quirk_wakeup_possio_gcc, + }, { + .manfid = MANFID_QUATECH, + .prodid = PRODID_QUATECH_DUAL_RS232, + .multi = 2, + }, { + .manfid = MANFID_QUATECH, + .prodid = PRODID_QUATECH_DUAL_RS232_D1, + .multi = 2, + }, { + .manfid = MANFID_QUATECH, + .prodid = PRODID_QUATECH_DUAL_RS232_G, + .multi = 2, + }, { + .manfid = MANFID_QUATECH, + .prodid = PRODID_QUATECH_QUAD_RS232, + .multi = 4, + }, { + .manfid = MANFID_SOCKET, + .prodid = PRODID_SOCKET_DUAL_RS232, + .multi = 2, + .config = quirk_config_socket, + }, { + .manfid = MANFID_SOCKET, + .prodid = ~0, + .multi = -1, + .config = quirk_config_socket, + } +}; + + +static int serial_config(struct pcmcia_device *link); + + +static void serial_remove(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int i; + + dev_dbg(&link->dev, "serial_release\n"); + + /* + * Recheck to see if the device is still configured. + */ + for (i = 0; i < info->ndev; i++) + serial8250_unregister_port(info->line[i]); + + if (!info->slave) + pcmcia_disable_device(link); +} + +static int serial_suspend(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int i; + + for (i = 0; i < info->ndev; i++) + serial8250_suspend_port(info->line[i]); + + return 0; +} + +static int serial_resume(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int i; + + for (i = 0; i < info->ndev; i++) + serial8250_resume_port(info->line[i]); + + if (info->quirk && info->quirk->wakeup) + info->quirk->wakeup(link); + + return 0; +} + +static int serial_probe(struct pcmcia_device *link) +{ + struct serial_info *info; + int ret; + + dev_dbg(&link->dev, "serial_attach()\n"); + + /* Create new serial device */ + info = kzalloc_obj(*info); + if (!info) + return -ENOMEM; + info->p_dev = link; + link->priv = info; + + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + if (do_sound) + link->config_flags |= CONF_ENABLE_SPKR; + + ret = serial_config(link); + if (ret) + goto free_info; + + return 0; + +free_info: + kfree(info); + return ret; +} + +static void serial_detach(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + + dev_dbg(&link->dev, "serial_detach\n"); + + /* + * Ensure that the ports have been released. + */ + serial_remove(link); + + /* free bits */ + kfree(info); +} + +/*====================================================================*/ + +static int setup_serial(struct pcmcia_device *handle, struct serial_info *info, + unsigned int iobase, int irq) +{ + struct uart_8250_port uart; + int line; + + memset(&uart, 0, sizeof(uart)); + uart.port.iobase = iobase; + uart.port.irq = irq; + uart.port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ; + uart.port.uartclk = 1843200; + uart.port.dev = &handle->dev; + if (buggy_uart) + uart.port.flags |= UPF_BUGGY_UART; + + if (info->quirk && info->quirk->setup) + info->quirk->setup(handle, &uart); + + line = serial8250_register_8250_port(&uart); + if (line < 0) { + pr_err("serial_cs: serial8250_register_8250_port() at 0x%04lx, irq %d failed\n", + (unsigned long)iobase, irq); + return -EINVAL; + } + + info->line[info->ndev] = line; + info->ndev++; + + return 0; +} + +/*====================================================================*/ + +static int pfc_config(struct pcmcia_device *p_dev) +{ + unsigned int port = 0; + struct serial_info *info = p_dev->priv; + + if ((p_dev->resource[1]->end != 0) && + (resource_size(p_dev->resource[1]) == 8)) { + port = p_dev->resource[1]->start; + info->slave = 1; + } else if ((info->manfid == MANFID_OSITECH) && + (resource_size(p_dev->resource[0]) == 0x40)) { + port = p_dev->resource[0]->start + 0x28; + info->slave = 1; + } + if (info->slave) + return setup_serial(p_dev, info, port, p_dev->irq); + + dev_warn(&p_dev->dev, "no usable port range found, giving up\n"); + return -ENODEV; +} + +static int simple_config_check(struct pcmcia_device *p_dev, void *priv_data) +{ + static const int size_table[2] = { 8, 16 }; + int *try = priv_data; + + if (p_dev->resource[0]->start == 0) + return -ENODEV; + + if ((*try & 0x1) == 0) + p_dev->io_lines = 16; + + if (p_dev->resource[0]->end != size_table[(*try >> 1)]) + return -ENODEV; + + p_dev->resource[0]->end = 8; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + + return pcmcia_request_io(p_dev); +} + +static int simple_config_check_notpicky(struct pcmcia_device *p_dev, + void *priv_data) +{ + static const unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; + int j; + + if (p_dev->io_lines > 3) + return -ENODEV; + + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = 8; + + for (j = 0; j < 5; j++) { + p_dev->resource[0]->start = base[j]; + p_dev->io_lines = base[j] ? 16 : 3; + if (!pcmcia_request_io(p_dev)) + return 0; + } + return -ENODEV; +} + +static int simple_config(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int ret, try; + + /* + * First pass: look for a config entry that looks normal. + * Two tries: without IO aliases, then with aliases. + */ + link->config_flags |= CONF_AUTO_SET_VPP; + for (try = 0; try < 4; try++) + if (!pcmcia_loop_config(link, simple_config_check, &try)) + goto found_port; + + /* + * Second pass: try to find an entry that isn't picky about + * its base address, then try to grab any standard serial port + * address, and finally try to get any free port. + */ + ret = pcmcia_loop_config(link, simple_config_check_notpicky, NULL); + if (ret) { + dev_warn(&link->dev, "no usable port range found, giving up\n"); + return ret; + } + +found_port: + if (info->multi && (info->manfid == MANFID_3COM)) + link->config_index &= ~(0x08); + + /* + * Apply any configuration quirks. + */ + if (info->quirk && info->quirk->config) + info->quirk->config(link); + + ret = pcmcia_enable_device(link); + if (ret != 0) + return ret; + return setup_serial(link, info, link->resource[0]->start, link->irq); +} + +static int multi_config_check(struct pcmcia_device *p_dev, void *priv_data) +{ + int *multi = priv_data; + + if (p_dev->resource[1]->end) + return -EINVAL; + + /* + * The quad port cards have bad CIS's, so just look for a + * window larger than 8 ports and assume it will be right. + */ + if (p_dev->resource[0]->end <= 8) + return -EINVAL; + + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = *multi * 8; + + if (pcmcia_request_io(p_dev)) + return -ENODEV; + return 0; +} + +static int multi_config_check_notpicky(struct pcmcia_device *p_dev, + void *priv_data) +{ + int *base2 = priv_data; + + if (!p_dev->resource[0]->end || !p_dev->resource[1]->end || + p_dev->resource[0]->start + 8 != p_dev->resource[1]->start) + return -ENODEV; + + p_dev->resource[0]->end = p_dev->resource[1]->end = 8; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + + if (pcmcia_request_io(p_dev)) + return -ENODEV; + + *base2 = p_dev->resource[0]->start + 8; + return 0; +} + +static int multi_config(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int i, base2 = 0; + + /* First, look for a generic full-sized window */ + if (!pcmcia_loop_config(link, multi_config_check, &info->multi)) + base2 = link->resource[0]->start + 8; + else { + /* If that didn't work, look for two windows */ + info->multi = 2; + if (pcmcia_loop_config(link, multi_config_check_notpicky, + &base2)) { + dev_warn(&link->dev, + "no usable port range found, giving up\n"); + return -ENODEV; + } + } + + if (!link->irq) + dev_warn(&link->dev, "no usable IRQ found, continuing...\n"); + + /* + * Apply any configuration quirks. + */ + if (info->quirk && info->quirk->config) + info->quirk->config(link); + + i = pcmcia_enable_device(link); + if (i != 0) + return -ENODEV; + + /* The Oxford Semiconductor OXCF950 cards are in fact single-port: + * 8 registers are for the UART, the others are extra registers. + * Siemen's MC45 PCMCIA (Possio's GCC) is OXCF950 based too. + */ + if (info->manfid == MANFID_OXSEMI || (info->manfid == MANFID_POSSIO && + info->prodid == PRODID_POSSIO_GCC)) { + if (link->config_index == 1 || + link->config_index == 3) { + setup_serial(link, info, base2, link->irq); + base2 = link->resource[0]->start; + } else { + setup_serial(link, info, link->resource[0]->start, + link->irq); + } + info->c950ctrl = base2; + + /* + * FIXME: We really should wake up the port prior to + * handing it over to the serial layer. + */ + if (info->quirk && info->quirk->wakeup) + info->quirk->wakeup(link); + + return 0; + } + + setup_serial(link, info, link->resource[0]->start, link->irq); + for (i = 0; i < info->multi - 1; i++) + setup_serial(link, info, base2 + (8 * i), + link->irq); + return 0; +} + +static int serial_check_for_multi(struct pcmcia_device *p_dev, void *priv_data) +{ + struct serial_info *info = p_dev->priv; + + if (!p_dev->resource[0]->end) + return -EINVAL; + + if ((!p_dev->resource[1]->end) && (p_dev->resource[0]->end % 8 == 0)) + info->multi = p_dev->resource[0]->end >> 3; + + if ((p_dev->resource[1]->end) && (p_dev->resource[0]->end == 8) + && (p_dev->resource[1]->end == 8)) + info->multi = 2; + + return 0; /* break */ +} + + +static int serial_config(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int i; + + dev_dbg(&link->dev, "serial_config\n"); + + /* Is this a compliant multifunction card? */ + info->multi = (link->socket->functions > 1); + + /* Is this a multiport card? */ + info->manfid = link->manf_id; + info->prodid = link->card_id; + + for (i = 0; i < ARRAY_SIZE(quirks); i++) + if ((quirks[i].manfid == ~0 || + quirks[i].manfid == info->manfid) && + (quirks[i].prodid == ~0 || + quirks[i].prodid == info->prodid)) { + info->quirk = &quirks[i]; + break; + } + + /* + * Another check for dual-serial cards: look for either serial or + * multifunction cards that ask for appropriate IO port ranges. + */ + if ((info->multi == 0) && + (link->has_func_id) && + (link->socket->pcmcia_pfc == 0) && + ((link->func_id == CISTPL_FUNCID_MULTI) || + (link->func_id == CISTPL_FUNCID_SERIAL))) { + if (pcmcia_loop_config(link, serial_check_for_multi, info)) + goto failed; + } + + /* + * Apply any multi-port quirk. + */ + if (info->quirk && info->quirk->multi != -1) + info->multi = info->quirk->multi; + + dev_info(&link->dev, + "trying to set up [0x%04x:0x%04x] (pfc: %d, multi: %d, quirk: %p)\n", + link->manf_id, link->card_id, + link->socket->pcmcia_pfc, info->multi, info->quirk); + if (link->socket->pcmcia_pfc) + i = pfc_config(link); + else if (info->multi > 1) + i = multi_config(link); + else + i = simple_config(link); + + if (i || info->ndev == 0) + goto failed; + + /* + * Apply any post-init quirk. FIXME: This should really happen + * before we register the port, since it might already be in use. + */ + if (info->quirk && info->quirk->post) + if (info->quirk->post(link)) + goto failed; + + return 0; + +failed: + dev_warn(&link->dev, "failed to initialize\n"); + serial_remove(link); + return -ENODEV; +} + +static const struct pcmcia_device_id serial_ids[] = { + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0057, 0x0021), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0089, 0x110a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0104, 0x000a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0d0a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0e0a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0xea15), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0109, 0x0501), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0138, 0x110a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0140, 0x000a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0x3341), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0xc0ab), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x016c, 0x0081), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x021b, 0x0101), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x08a1, 0xc0ab), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM28", 0x2e3ee845, 0x0ea978ea), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM33", 0x2e3ee845, 0x80609023), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM56", 0x2e3ee845, 0xa650c32a), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "REM10", 0x2e3ee845, 0x76df1d29), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "XEM5600", 0x2e3ee845, 0xf1403719), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "AnyCom", "Fast Ethernet + 56K COMBO", 0x578ba6e7, 0xb0ac62c4), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "ATKK", "LM33-PCM-T", 0xba9eb7e2, 0x077c174e), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Linksys", "EtherFast 10&100 + 56K PC Card (PCMLM56)", 0x0733cc81, 0xb3765033), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "LINKSYS", "PCMLM336", 0xf7cb0b07, 0x7a821b58), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "MICRO RESEARCH", "COMBO-L/M-336", 0xb2ced065, 0x3ced0555), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "NEC", "PK-UG-J001", 0x18df0ba0, 0x831b1064), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "LanModem", 0xdcfe12d3, 0xc67c648f), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Xircom", "CreditCard Ethernet+Modem II", 0x2e3ee845, 0xeca401bf), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0e01), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0a05), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0b05), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x1101), + PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0104, 0x0070), + PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0101, 0x0562), + PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0104, 0x0070), + PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x016c, 0x0020), + PCMCIA_MFC_DEVICE_PROD_ID123(1, "APEX DATA", "MULTICARD", "ETHERNET-MODEM", 0x11c2da09, 0x7289dc5d, 0xaad95e1f), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away 28.8 PC Card ", 0xb569a6e5, 0x5bd4ff2c), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away Credit Card Adapter", 0xb569a6e5, 0x4bdf15c3), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "w95 Home and Away Credit Card ", 0xb569a6e5, 0xae911c15), + PCMCIA_MFC_DEVICE_PROD_ID1(1, "Motorola MARQUIS", 0xf03e4e77), + PCMCIA_MFC_DEVICE_PROD_ID2(1, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302), + PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0301), + PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x0276), + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0039), + PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0006), + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x0101), /* TDK DF2814 */ + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x100a), /* Xircom CM-56G */ + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x3e0a), /* TDK DF5660 */ + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x410a), + PCMCIA_DEVICE_MANF_CARD(0x0107, 0x0002), /* USRobotics 14,400 */ + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d50), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d51), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d52), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d53), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0xd180), + PCMCIA_DEVICE_MANF_CARD(0x0115, 0x3330), /* USRobotics/SUN 14,400 */ + PCMCIA_DEVICE_MANF_CARD(0x0124, 0x0100), /* Nokia DTP-2 ver II */ + PCMCIA_DEVICE_MANF_CARD(0x0134, 0x5600), /* LASAT COMMUNICATIONS A/S */ + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x000e), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x001b), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052), + PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0006), /* Psion 56K+Fax */ + PCMCIA_DEVICE_MANF_CARD(0x0200, 0x0001), /* MultiMobile */ + PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae), + PCMCIA_DEVICE_PROD_ID124("GATEWAY2000", "CC3144", "PCMCIA MODEM", 0x506bccae, 0xcb3685f1, 0xbd6c43ef), + PCMCIA_DEVICE_PROD_ID14("MEGAHERTZ", "PCMCIA MODEM", 0xf510db04, 0xbd6c43ef), + PCMCIA_DEVICE_PROD_ID124("TOSHIBA", "T144PF", "PCMCIA MODEM", 0xb4585a1a, 0x7271409c, 0xbd6c43ef), + PCMCIA_DEVICE_PROD_ID123("FUJITSU", "FC14F ", "MBH10213", 0x6ee5a3d8, 0x30ead12b, 0xb00f05a0), + PCMCIA_DEVICE_PROD_ID123("Novatel Wireless", "Merlin UMTS Modem", "U630", 0x32607776, 0xd9e73b13, 0xe87332e), + PCMCIA_DEVICE_PROD_ID13("MEGAHERTZ", "V.34 PCMCIA MODEM", 0xf510db04, 0xbb2cce4a), + PCMCIA_DEVICE_PROD_ID12("Brain Boxes", "Bluetooth PC Card", 0xee138382, 0xd4ce9b02), + PCMCIA_DEVICE_PROD_ID12("CIRRUS LOGIC", "FAX MODEM", 0xe625f451, 0xcecd6dfa), + PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 28800 FAX/DATA MODEM", 0xa3a3062c, 0x8cbd7c76), + PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 33600 FAX/DATA MODEM", 0xa3a3062c, 0x5a00ce95), + PCMCIA_DEVICE_PROD_ID12("Computerboards, Inc.", "PCM-COM422", 0xd0b78f51, 0x7e2d49ed), + PCMCIA_DEVICE_PROD_ID12("Dr. Neuhaus", "FURY CARD 14K4", 0x76942813, 0x8b96ce65), + PCMCIA_DEVICE_PROD_ID12("IBM", "ISDN/56K/GSM", 0xb569a6e5, 0xfee5297b), + PCMCIA_DEVICE_PROD_ID12("Intelligent", "ANGIA FAX/MODEM", 0xb496e65e, 0xf31602a6), + PCMCIA_DEVICE_PROD_ID12("Intel", "MODEM 2400+", 0x816cc815, 0x412729fb), + PCMCIA_DEVICE_PROD_ID12("Intertex", "IX34-PCMCIA", 0xf8a097e3, 0x97880447), + PCMCIA_DEVICE_PROD_ID12("IOTech Inc ", "PCMCIA Dual RS-232 Serial Port Card", 0x3bd2d898, 0x92abc92f), + PCMCIA_DEVICE_PROD_ID12("MACRONIX", "FAX/MODEM", 0x668388b3, 0x3f9bdf2f), + PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT1432LT", 0x5f73be51, 0x0b3e2383), + PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT2834LT", 0x5f73be51, 0x4cd7c09e), + PCMCIA_DEVICE_PROD_ID12("OEM ", "C288MX ", 0xb572d360, 0xd2385b7a), + PCMCIA_DEVICE_PROD_ID12("Option International", "V34bis GSM/PSTN Data/Fax Modem", 0x9d7cd6f5, 0x5cb8bf41), + PCMCIA_DEVICE_PROD_ID12("Option International", "GSM-Ready 56K/ISDN", 0x9d7cd6f5, 0xb23844aa), + PCMCIA_DEVICE_PROD_ID12("PCMCIA ", "C336MX ", 0x99bcafe9, 0xaa25bcab), + PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "PCMCIA Dual RS-232 Serial Port Card", 0xc4420b35, 0x92abc92f), + PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "Dual RS-232 Serial Port PC Card", 0xc4420b35, 0x031a380d), + PCMCIA_DEVICE_PROD_ID12("Telia", "SurfinBird 560P/A+", 0xe2cdd5e, 0xc9314b38), + PCMCIA_DEVICE_PROD_ID1("Smart Serial Port", 0x2d8ce292), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "TOSHIBA", "Modem/LAN Card", 0xb4585a1a, 0x53f922f8, "cis/PCMLM28.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID12(1, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "cis/DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID4(1, "NSC MF LAN/Modem", 0x58fc6056, "cis/DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0556, "cis/3CCFEM556.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0175, 0x0000, "cis/DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0035, "cis/3CXEM556.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x003d, "cis/3CXEM556.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC850", 0xd85f6206, 0x42a2c018, "cis/SW_8xx_SER.cis"), /* Sierra Wireless AC850 3G Network Adapter R1 */ + PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC860", 0xd85f6206, 0x698f93db, "cis/SW_8xx_SER.cis"), /* Sierra Wireless AC860 3G Network Adapter R1 */ + PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC710/AC750", 0xd85f6206, 0x761b11e0, "cis/SW_7xx_SER.cis"), /* Sierra Wireless AC710/AC750 GPRS Network Adapter R1 */ + PCMCIA_DEVICE_CIS_MANF_CARD(0x0192, 0xa555, "cis/SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- pre update */ + PCMCIA_DEVICE_CIS_MANF_CARD(0x013f, 0xa555, "cis/SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- post update */ + PCMCIA_DEVICE_CIS_PROD_ID12("MultiTech", "PCMCIA 56K DataFax", 0x842047ee, 0xc2efcf03, "cis/MT5634ZLX.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-2", 0x96913a85, 0x27ab5437, "cis/COMpad2.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-4", 0x96913a85, 0xcec8f102, "cis/COMpad4.cis"), + PCMCIA_DEVICE_CIS_PROD_ID123("ADVANTECH", "COMpad-32/85", "1.0", 0x96913a85, 0x8fbe92ae, 0x0877b627, "cis/COMpad2.cis"), + PCMCIA_DEVICE_CIS_PROD_ID2("RS-COM 2P", 0xad20b156, "cis/RS-COM-2P.cis"), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.", "SERIAL CARD: SL100 1.00.", 0x19ca78af, 0xf964f42b), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.", "SERIAL CARD: SL100", 0x19ca78af, 0x71d98e83), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.", "SERIAL CARD: SL232 1.00.", 0x19ca78af, 0x69fb7490), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.", "SERIAL CARD: SL232", 0x19ca78af, 0xb6bc0235), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.", "SERIAL CARD: CF232", 0x63f2e0bd, 0xb9e175d3), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.", "SERIAL CARD: CF232-5", 0x63f2e0bd, 0xfce33442), + PCMCIA_DEVICE_PROD_ID12("Elan", "Serial Port: CF232", 0x3beb8cf2, 0x171e7190), + PCMCIA_DEVICE_PROD_ID12("Elan", "Serial Port: CF232-5", 0x3beb8cf2, 0x20da4262), + PCMCIA_DEVICE_PROD_ID12("Elan", "Serial Port: CF428", 0x3beb8cf2, 0xea5dd57d), + PCMCIA_DEVICE_PROD_ID12("Elan", "Serial Port: CF500", 0x3beb8cf2, 0xd77255fa), + PCMCIA_DEVICE_PROD_ID12("Elan", "Serial Port: IC232", 0x3beb8cf2, 0x6a709903), + PCMCIA_DEVICE_PROD_ID12("Elan", "Serial Port: SL232", 0x3beb8cf2, 0x18430676), + PCMCIA_DEVICE_PROD_ID12("Elan", "Serial Port: XL232", 0x3beb8cf2, 0x6f933767), + PCMCIA_MFC_DEVICE_PROD_ID12(0, "Elan", "Serial Port: CF332", 0x3beb8cf2, 0x16dc1ba7), + PCMCIA_MFC_DEVICE_PROD_ID12(0, "Elan", "Serial Port: SL332", 0x3beb8cf2, 0x19816c41), + PCMCIA_MFC_DEVICE_PROD_ID12(0, "Elan", "Serial Port: SL385", 0x3beb8cf2, 0x64112029), + PCMCIA_MFC_DEVICE_PROD_ID12(0, "Elan", "Serial Port: SL432", 0x3beb8cf2, 0x1cce7ac4), + PCMCIA_MFC_DEVICE_PROD_ID12(0, "Elan", "Serial+Parallel Port: SP230", 0x3beb8cf2, 0xdb9e58bc), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "Elan", "Serial Port: CF332", 0x3beb8cf2, 0x16dc1ba7), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "Elan", "Serial Port: SL332", 0x3beb8cf2, 0x19816c41), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "Elan", "Serial Port: SL385", 0x3beb8cf2, 0x64112029), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "Elan", "Serial Port: SL432", 0x3beb8cf2, 0x1cce7ac4), + PCMCIA_MFC_DEVICE_PROD_ID12(2, "Elan", "Serial Port: SL432", 0x3beb8cf2, 0x1cce7ac4), + PCMCIA_MFC_DEVICE_PROD_ID12(3, "Elan", "Serial Port: SL432", 0x3beb8cf2, 0x1cce7ac4), + PCMCIA_DEVICE_MANF_CARD(0x0279, 0x950b), + /* too generic */ + /* PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0160, 0x0002), */ + /* PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0160, 0x0002), */ + PCMCIA_DEVICE_FUNC_ID(2), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, serial_ids); + +MODULE_FIRMWARE("cis/PCMLM28.cis"); +MODULE_FIRMWARE("cis/DP83903.cis"); +MODULE_FIRMWARE("cis/3CCFEM556.cis"); +MODULE_FIRMWARE("cis/3CXEM556.cis"); +MODULE_FIRMWARE("cis/SW_8xx_SER.cis"); +MODULE_FIRMWARE("cis/SW_7xx_SER.cis"); +MODULE_FIRMWARE("cis/SW_555_SER.cis"); +MODULE_FIRMWARE("cis/MT5634ZLX.cis"); +MODULE_FIRMWARE("cis/COMpad2.cis"); +MODULE_FIRMWARE("cis/COMpad4.cis"); +MODULE_FIRMWARE("cis/RS-COM-2P.cis"); + +static struct pcmcia_driver serial_cs_driver = { + .owner = THIS_MODULE, + .name = "serial_cs", + .probe = serial_probe, + .remove = serial_detach, + .id_table = serial_ids, + .suspend = serial_suspend, + .resume = serial_resume, +}; +module_pcmcia_driver(serial_cs_driver); + +MODULE_DESCRIPTION("driver for PCMCIA serial devices"); +MODULE_LICENSE("GPL"); |
